[libgxps] Initial implementation of ICC based colors
- From: Carlos Garcia Campos <carlosgc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgxps] Initial implementation of ICC based colors
- Date: Thu, 10 Nov 2011 20:12:19 +0000 (UTC)
commit 44034e1b2e095b6b46bc7d4fb425ff95181b3170
Author: Carlos Garcia Campos <carlosgc gnome org>
Date: Thu Nov 10 19:21:49 2011 +0100
Initial implementation of ICC based colors
Only CMYK colors are supported for now. Little CMS2 has been added as an
optional dependency.
configure.ac | 18 ++++
libgxps/Makefile.am | 4 +
libgxps/gxps-color.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++
libgxps/gxps-color.h | 45 +++++++++
libgxps/gxps-page.c | 186 +++++++++++++++++++++++++------------
5 files changed, 447 insertions(+), 58 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 8364b23..c8102be 100644
--- a/configure.ac
+++ b/configure.ac
@@ -142,6 +142,24 @@ fi
AC_SUBST(LIBTIFF)
+dnl libcms2
+AC_ARG_WITH(liblcms2,
+ [AC_HELP_STRING([--without-liblcms2],
+ [disable Little CMS 2])])
+if test x$with_liblcms2 != xno; then
+ PKG_CHECK_MODULES(LCMS2, lcms2, [enable_lcms2="yes"], [enable_lcms2="no"])
+ if test "x$enable_lcms2" = "xyes"; then
+ AC_DEFINE(HAVE_LIBLCMS2, [1], [Have Little CMS 2 library])
+ else
+ AC_MSG_ERROR([
+*** Checks for Little CMS 2 failed. You can build without it by passing
+*** --without-liblcms2 to configure])
+ fi
+fi
+
+AC_SUBST(LCMS2_CFLAGS)
+AC_SUBST(LCMS2_LIBS)
+
AC_DEFINE(_FILE_OFFSET_BITS, 64, [Enable LFS])
dnl Test
diff --git a/libgxps/Makefile.am b/libgxps/Makefile.am
index c833f82..d81780f 100644
--- a/libgxps/Makefile.am
+++ b/libgxps/Makefile.am
@@ -2,6 +2,7 @@ lib_LTLIBRARIES = libgxps.la
NOINST_H_FILES = \
gxps-archive.h \
+ gxps-color.h \
gxps-debug.h \
gxps-fonts.h \
gxps-images.h \
@@ -23,6 +24,7 @@ libgxpsinclude_HEADERS = $(INST_H_FILES)
libgxps_la_SOURCES = \
gxps-archive.c \
+ gxps-color.c \
gxps-debug.c \
gxps-document.c \
gxps-document-structure.c \
@@ -47,6 +49,7 @@ libgxps_la_CPPFLAGS = \
libgxps_la_CFLAGS = \
$(GXPS_CFLAGS) \
$(GXPS_DEBUG_CFLAGS) \
+ $(LCMS2_CFLAGS) \
$(WARN_CFLAGS) \
$(AM_CFLAGS)
@@ -58,6 +61,7 @@ libgxps_la_LDFLAGS = \
libgxps_la_LIBADD = \
$(GXPS_LIBS) \
+ $(LCMS2_LIBS) \
$(LIBJPEG) \
$(LIBTIFF)
diff --git a/libgxps/gxps-color.c b/libgxps/gxps-color.c
new file mode 100644
index 0000000..20a62a0
--- /dev/null
+++ b/libgxps/gxps-color.c
@@ -0,0 +1,252 @@
+/* GXPSColor
+ *
+ * Copyright (C) 2011 Carlos Garcia Campos <carlosgc gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#ifdef HAVE_LIBLCMS2
+#include <lcms2.h>
+#endif
+#include "gxps-color.h"
+#include "gxps-error.h"
+#include "gxps-debug.h"
+
+#define ICC_PROFILE_CACHE_KEY "gxps-icc-profile-cache"
+
+#ifdef HAVE_LIBLCMS2
+#ifdef GXPS_ENABLE_DEBUG
+static const gchar *
+get_color_space_string (cmsColorSpaceSignature color_space)
+{
+ switch (color_space) {
+ case cmsSigXYZData:
+ return "XYZ";
+ case cmsSigLabData:
+ return "Lab";
+ case cmsSigLuvData:
+ return "Luv";
+ case cmsSigYCbCrData:
+ return "YCbr";
+ case cmsSigYxyData:
+ return "Yxy";
+ case cmsSigRgbData:
+ return "RGB";
+ case cmsSigGrayData:
+ return "GRAY";
+ case cmsSigHsvData:
+ return "HSV";
+ case cmsSigHlsData:
+ return "HLS";
+ case cmsSigCmykData:
+ return "CMYK";
+ case cmsSigCmyData:
+ return "CMY";
+ case cmsSigMCH1Data:
+ return "MCH1";
+ case cmsSigMCH2Data:
+ return "MCH2";
+ case cmsSigMCH3Data:
+ return "MCH3";
+ case cmsSigMCH4Data:
+ return "MCH4";
+ case cmsSigMCH5Data:
+ return "MCH5";
+ case cmsSigMCH6Data:
+ return "MCH6";
+ case cmsSigMCH7Data:
+ return "MCH7";
+ case cmsSigMCH8Data:
+ return "MCH8";
+ case cmsSigMCH9Data:
+ return "MCH9";
+ case cmsSigMCHAData:
+ return "MCHA";
+ case cmsSigMCHBData:
+ return "MCHB";
+ case cmsSigMCHCData:
+ return "MCHC";
+ case cmsSigMCHDData:
+ return "MCHD";
+ case cmsSigMCHEData:
+ return "MCHE";
+ case cmsSigMCHFData:
+ return "MCHF";
+ case cmsSigNamedData:
+ return "nmcl";
+ case cmsSig1colorData:
+ return "1CLR";
+ case cmsSig2colorData:
+ return "2CLR";
+ case cmsSig3colorData:
+ return "3CLR";
+ case cmsSig4colorData:
+ return "4CLR";
+ case cmsSig5colorData:
+ return "5CLR";
+ case cmsSig6colorData:
+ return "6CLR";
+ case cmsSig7colorData:
+ return "7CLR";
+ case cmsSig8colorData:
+ return "8CLR";
+ case cmsSig9colorData:
+ return "9CLR";
+ case cmsSig10colorData:
+ return "ACLR";
+ case cmsSig11colorData:
+ return "BCLR";
+ case cmsSig12colorData:
+ return "CCLR";
+ case cmsSig13colorData:
+ return "DCLR";
+ case cmsSig14colorData:
+ return "ECLR";
+ case cmsSig15colorData:
+ return "FCLR";
+ case cmsSigLuvKData:
+ return "LuvK";
+ default:
+ g_assert_not_reached ();
+ }
+
+ return NULL;
+}
+#endif /* GXPS_ENABLE_DEBUG */
+
+static cmsHPROFILE
+create_rgb_profile (gpointer args)
+{
+ return cmsCreate_sRGBProfile ();
+}
+
+static cmsHPROFILE
+get_s_rgb_profile (void)
+{
+ static GOnce once_init = G_ONCE_INIT;
+ return g_once (&once_init, create_rgb_profile, NULL);
+}
+
+static gboolean
+gxps_color_new_for_icc_profile (cmsHPROFILE profile,
+ gdouble *values,
+ guint n_values,
+ GXPSColor *color)
+{
+ cmsHTRANSFORM transform;
+ gdouble cmyk[4];
+ gdouble rgb[3];
+
+ if (cmsChannelsOf (cmsGetColorSpace (profile)) != n_values)
+ return FALSE;
+
+ if (cmsGetColorSpace (profile) != cmsSigCmykData) {
+ GXPS_DEBUG (g_debug ("Unsupported color space %s", get_color_space_string (cmsGetColorSpace (profile))));
+
+ return FALSE;
+ }
+
+ cmyk[0] = CLAMP (values[0], 0., 1.) * 100.;
+ cmyk[1] = CLAMP (values[1], 0., 1.) * 100.;
+ cmyk[2] = CLAMP (values[2], 0., 1.) * 100.;
+ cmyk[3] = CLAMP (values[3], 0., 1.) * 100.;
+
+ transform = cmsCreateTransform (profile,
+ TYPE_CMYK_DBL,
+ get_s_rgb_profile (),
+ TYPE_RGB_DBL,
+ INTENT_PERCEPTUAL, 0);
+ cmsDoTransform (transform, cmyk, rgb, 1);
+ cmsDeleteTransform (transform);
+
+ color->red = rgb[0];
+ color->green = rgb[1];
+ color->blue = rgb[2];
+
+ return TRUE;
+}
+
+static cmsHPROFILE
+gxps_color_create_icc_profile (GXPSArchive *zip,
+ const gchar *icc_profile_uri)
+{
+ cmsHPROFILE profile;
+ guchar *profile_data;
+ gsize profile_data_len;
+ gboolean res;
+
+ res = gxps_archive_read_entry (zip, icc_profile_uri,
+ &profile_data, &profile_data_len,
+ NULL);
+ if (!res) {
+ GXPS_DEBUG (g_debug ("ICC profile source %s not found in archive", icc_profile_uri));
+ return NULL;
+ }
+
+ profile = cmsOpenProfileFromMem (profile_data, profile_data_len);
+ g_free (profile_data);
+
+ if (!profile) {
+ GXPS_DEBUG (g_debug ("Failed to load ICC profile %s", icc_profile_uri));
+
+ return NULL;
+ }
+
+ return profile;
+}
+#endif /* HAVE_LIBLCMS2 */
+
+gboolean
+gxps_color_new_for_icc (GXPSArchive *zip,
+ const gchar *icc_profile_uri,
+ gdouble *values,
+ guint n_values,
+ GXPSColor *color)
+{
+#ifdef HAVE_LIBLCMS2
+ GHashTable *icc_cache;
+ cmsHPROFILE profile;
+
+ icc_cache = g_object_get_data (G_OBJECT (zip), ICC_PROFILE_CACHE_KEY);
+ if (icc_cache) {
+ profile = g_hash_table_lookup (icc_cache, icc_profile_uri);
+ if (profile)
+ return gxps_color_new_for_icc_profile (profile, values, n_values, color);
+ }
+
+ profile = gxps_color_create_icc_profile (zip, icc_profile_uri);
+ if (!profile)
+ return FALSE;
+
+ if (!icc_cache) {
+ icc_cache = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)cmsCloseProfile);
+ g_object_set_data_full (G_OBJECT (zip), ICC_PROFILE_CACHE_KEY,
+ icc_cache,
+ (GDestroyNotify)g_hash_table_destroy);
+ }
+
+ g_hash_table_insert (icc_cache, g_strdup (icc_profile_uri), profile);
+
+ return gxps_color_new_for_icc_profile (profile, values, n_values, color);
+#else
+ return FALSE;
+#endif
+}
diff --git a/libgxps/gxps-color.h b/libgxps/gxps-color.h
new file mode 100644
index 0000000..232693f
--- /dev/null
+++ b/libgxps/gxps-color.h
@@ -0,0 +1,45 @@
+/* GXPSColor
+ *
+ * Copyright (C) 2011 Carlos Garcia Campos <carlosgc gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __GXPS_COLOR_H__
+#define __GXPS_COLOR_H__
+
+#include <glib.h>
+#include "gxps-archive.h"
+
+G_BEGIN_DECLS
+
+#define GXPS_COLOR_MAX_CHANNELS 8
+
+typedef struct _GXPSColor {
+ gdouble alpha;
+ gdouble red;
+ gdouble green;
+ gdouble blue;
+} GXPSColor;
+
+gboolean gxps_color_new_for_icc (GXPSArchive *zip,
+ const gchar *icc_profile_uri,
+ gdouble *values,
+ guint n_values,
+ GXPSColor *color);
+
+G_END_DECLS
+
+#endif /* __GXPS_COLOR_H__ */
diff --git a/libgxps/gxps-page.c b/libgxps/gxps-page.c
index 72f6c4f..dcde715 100644
--- a/libgxps/gxps-page.c
+++ b/libgxps/gxps-page.c
@@ -27,6 +27,7 @@
#include "gxps-fonts.h"
#include "gxps-links.h"
#include "gxps-images.h"
+#include "gxps-color.h"
#include "gxps-private.h"
#include "gxps-error.h"
#include "gxps-debug.h"
@@ -852,55 +853,49 @@ hex (const gchar *spec,
}
static gboolean
-gxps_color_rgb_parse (const gchar *color,
- gdouble *alpha,
- gdouble *red,
- gdouble *green,
- gdouble *blue)
+gxps_color_s_rgb_parse (const gchar *color_str,
+ GXPSColor *color)
{
- gsize len = strlen (color);
+ gsize len = strlen (color_str);
guint a = 255;
guint r, g, b;
switch (len) {
case 6:
- if (!hex (color, 2, &r) ||
- !hex (color + 2, 2, &g) ||
- !hex (color + 4, 2, &b))
+ if (!hex (color_str, 2, &r) ||
+ !hex (color_str + 2, 2, &g) ||
+ !hex (color_str + 4, 2, &b))
return FALSE;
break;
case 8:
- if (!hex (color, 2, &a) ||
- !hex (color + 2, 2, &r) ||
- !hex (color + 4, 2, &g) ||
- !hex (color + 6, 2, &b))
+ if (!hex (color_str, 2, &a) ||
+ !hex (color_str + 2, 2, &r) ||
+ !hex (color_str + 4, 2, &g) ||
+ !hex (color_str + 6, 2, &b))
return FALSE;
break;
default:
return FALSE;
}
- *alpha = a / 255.0;
- *red = r / 255.0;
- *green = g / 255.0;
- *blue = b / 255.0;
+ color->alpha = a / 255.;
+ color->red = r / 255.;
+ color->green = g / 255.;
+ color->blue = b / 255.;
- return TRUE;
+ return TRUE;
}
static gboolean
-gxps_color_scrgb_parse (const gchar *color,
- gdouble *alpha,
- gdouble *red,
- gdouble *green,
- gdouble *blue)
+gxps_color_sc_rgb_parse (const gchar *color_str,
+ GXPSColor *color)
{
gchar **tokens;
gsize len;
gdouble c[4];
guint i, start;
- tokens = g_strsplit (color, ",", 4);
+ tokens = g_strsplit (color_str, ",", 4);
len = g_strv_length (tokens);
switch (len) {
@@ -918,6 +913,7 @@ gxps_color_scrgb_parse (const gchar *color,
start = 0;
break;
default:
+ g_strfreev (tokens);
return FALSE;
}
@@ -931,52 +927,116 @@ gxps_color_scrgb_parse (const gchar *color,
g_strfreev (tokens);
- *alpha = CLAMP (c[0], 0., 1.);
- *red = CLAMP (c[1], 0., 1.);
- *green = CLAMP (c[2], 0., 1.);
- *blue = CLAMP (c[3], 0., 1.);
+ color->alpha = CLAMP (c[0], 0., 1.);
+ color->red = CLAMP (c[1], 0., 1.);
+ color->green = CLAMP (c[2], 0., 1.);
+ color->blue = CLAMP (c[3], 0., 1.);
return TRUE;
}
static gboolean
-gxps_color_parse (const gchar *color,
- gdouble *alpha,
- gdouble *red,
- gdouble *green,
- gdouble *blue)
+gxps_color_icc_parse (const gchar *color_str,
+ GXPSArchive *zip,
+ GXPSColor *color)
+{
+ const gchar *p;
+ gchar *icc_profile_uri;
+ gchar **tokens;
+ gsize len;
+ gdouble alpha;
+ gdouble values[GXPS_COLOR_MAX_CHANNELS];
+ guint i, j;
+ gboolean retval;
+
+ p = strstr (color_str, " ");
+ if (!p)
+ return FALSE;
+
+ icc_profile_uri = g_strndup (color_str, strlen (color_str) - strlen (p));
+
+ tokens = g_strsplit (++p, ",", -1);
+ len = g_strv_length (tokens);
+ if (len < 2) {
+ g_strfreev (tokens);
+ g_free (icc_profile_uri);
+
+ return FALSE;
+ }
+
+ if (!gxps_value_get_double (tokens[0], &alpha)) {
+ g_strfreev (tokens);
+ g_free (icc_profile_uri);
+
+ return FALSE;
+ }
+
+ for (i = 0, j = 1; i < GXPS_COLOR_MAX_CHANNELS && j < len; i++, j++) {
+ if (!gxps_value_get_double (tokens[j], &values[i])) {
+ g_strfreev (tokens);
+ g_free (icc_profile_uri);
+
+ return FALSE;
+ }
+ }
+
+ g_strfreev (tokens);
+
+ color->alpha = CLAMP (alpha, 0., 1.);
+ retval = gxps_color_new_for_icc (zip, icc_profile_uri, values, i, color);
+ g_free (icc_profile_uri);
+
+ return retval;
+}
+
+static gboolean
+gxps_color_parse (const gchar *color_str,
+ GXPSArchive *zip,
+ GXPSColor *color)
{
const gchar *p;
- p = strstr (color, "#");
+ p = strstr (color_str, "#");
if (!p) {
- GXPS_DEBUG (g_debug ("Unsupported color %s", color));
+ p = strstr (color_str, "ContextColor");
+ if (p == color_str) {
+ p += strlen ("ContextColor");
+ return gxps_color_icc_parse (++p, zip, color);
+ }
+ GXPS_DEBUG (g_debug ("Unsupported color %s", color_str));
+
return FALSE;
}
- if (p == color)
- return gxps_color_rgb_parse (++p, alpha, red, green, blue);
+ if (p == color_str)
+ return gxps_color_s_rgb_parse (++p, color);
- if (strncmp (color, "sc", 2) == 0 && p == color + 2)
- return gxps_color_scrgb_parse (++p, alpha, red, green, blue);
+ if (strncmp (color_str, "sc", 2) == 0 && p == color_str + 2)
+ return gxps_color_sc_rgb_parse (++p, color);
- GXPS_DEBUG (g_debug ("Unsupported color %s", color));
+ GXPS_DEBUG (g_debug ("Unsupported color %s", color_str));
return FALSE;
}
static cairo_pattern_t *
-gxps_create_solid_color_pattern_with_alpha (const gchar *color, gdouble alpha)
+gxps_create_solid_color_pattern_with_alpha (GXPSArchive *zip,
+ const gchar *color_str,
+ gdouble alpha)
{
cairo_pattern_t *pattern;
- gdouble a, r, g, b;
+ GXPSColor color;
- if (!gxps_color_parse (color, &a, &r, &g, &b))
+ if (!gxps_color_parse (color_str, zip, &color))
return NULL;
- pattern = cairo_pattern_create_rgba (r, g, b, a * alpha);
+ pattern = cairo_pattern_create_rgba (color.red,
+ color.green,
+ color.blue,
+ color.alpha * alpha);
if (cairo_pattern_status (pattern)) {
cairo_pattern_destroy (pattern);
+
return NULL;
}
@@ -984,9 +1044,10 @@ gxps_create_solid_color_pattern_with_alpha (const gchar *color, gdouble alpha)
}
static cairo_pattern_t *
-gxps_create_solid_color_pattern (const gchar *color)
+gxps_create_solid_color_pattern (GXPSArchive *zip,
+ const gchar *color_str)
{
- return gxps_create_solid_color_pattern_with_alpha (color, 1.0);
+ return gxps_create_solid_color_pattern_with_alpha (zip, color_str, 1.0);
}
static gboolean
@@ -1376,13 +1437,15 @@ brush_gradient_start_element (GMarkupParseContext *context,
if (strcmp (element_name, "LinearGradientBrush.GradientStops") == 0) {
} else if (strcmp (element_name, "RadialGradientBrush.GradientStops") == 0) {
} else if (strcmp (element_name, "GradientStop") == 0) {
- gint i;
- gdouble a = -1, r, g, b;
- gdouble offset = -1;
+ gint i;
+ GXPSColor color;
+ gboolean has_color = FALSE;
+ gdouble offset = -1;
for (i = 0; names[i] != NULL; i++) {
if (strcmp (names[i], "Color") == 0) {
- if (!gxps_color_parse (values[i], &a, &r, &g, &b)) {
+ has_color = TRUE;
+ if (!gxps_color_parse (values[i], brush->ctx->page->priv->zip, &color)) {
gxps_parse_error (context,
brush->ctx->page->priv->source,
G_MARKUP_ERROR_INVALID_CONTENT,
@@ -1402,18 +1465,21 @@ brush_gradient_start_element (GMarkupParseContext *context,
}
}
- if (a == -1 || offset == -1) {
+ if (!has_color || offset == -1) {
gxps_parse_error (context,
brush->ctx->page->priv->source,
G_MARKUP_ERROR_MISSING_ATTRIBUTE,
element_name,
- a == -1 ? "Color" : "Offset",
+ !has_color ? "Color" : "Offset",
NULL, error);
return;
}
- a *= brush->opacity;
- cairo_pattern_add_color_stop_rgba (brush->pattern, offset, r, g, b, a);
+ cairo_pattern_add_color_stop_rgba (brush->pattern, offset,
+ color.red,
+ color.green,
+ color.blue,
+ color.alpha * brush->opacity);
}
}
@@ -1464,7 +1530,8 @@ brush_start_element (GMarkupParseContext *context,
return;
}
- brush->pattern = gxps_create_solid_color_pattern_with_alpha (color_str, brush->opacity);
+ brush->pattern = gxps_create_solid_color_pattern_with_alpha (brush->ctx->page->priv->zip,
+ color_str, brush->opacity);
GXPS_DEBUG (g_message ("set_fill_pattern (solid)"));
if (!brush->pattern) {
gxps_parse_error (context,
@@ -2929,7 +2996,8 @@ render_start_element (GMarkupParseContext *context,
path->clip_data = g_strdup (values[i]);
} else if (strcmp (names[i], "Fill") == 0) {
GXPS_DEBUG (g_message ("set_fill_pattern (solid)"));
- path->fill_pattern = gxps_create_solid_color_pattern (values[i]);
+ path->fill_pattern = gxps_create_solid_color_pattern (ctx->page->priv->zip,
+ values[i]);
if (!path->fill_pattern) {
gxps_parse_error (context,
ctx->page->priv->source,
@@ -2939,7 +3007,8 @@ render_start_element (GMarkupParseContext *context,
}
} else if (strcmp (names[i], "Stroke") == 0) {
GXPS_DEBUG (g_message ("set_stroke_pattern (solid)"));
- path->stroke_pattern = gxps_create_solid_color_pattern (values[i]);
+ path->stroke_pattern = gxps_create_solid_color_pattern (ctx->page->priv->zip,
+ values[i]);
if (!path->stroke_pattern) {
gxps_parse_error (context,
ctx->page->priv->source,
@@ -3145,7 +3214,8 @@ render_start_element (GMarkupParseContext *context,
glyphs->opacity = opacity;
if (fill_color) {
GXPS_DEBUG (g_message ("set_fill_pattern (solid)"));
- glyphs->fill_pattern = gxps_create_solid_color_pattern (fill_color);
+ glyphs->fill_pattern = gxps_create_solid_color_pattern (ctx->page->priv->zip,
+ fill_color);
}
if (glyphs->opacity != 1.0)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]