From 3686c32a570c3df738a09b34e85fc5d6bd50d020 Mon Sep 17 00:00:00 2001 From: miklosh Date: Thu, 9 Aug 2007 13:04:00 +0000 Subject: [PATCH] Added support for the shading operator --- .../internal/pdfinput/pdf-parser.cpp | 74 +++++++++++++++++-- src/extension/internal/pdfinput/pdf-parser.h | 2 + .../internal/pdfinput/svg-builder.cpp | 59 ++++++++++++--- src/extension/internal/pdfinput/svg-builder.h | 6 +- 4 files changed, 122 insertions(+), 19 deletions(-) diff --git a/src/extension/internal/pdfinput/pdf-parser.cpp b/src/extension/internal/pdfinput/pdf-parser.cpp index 1d312c116..465350b78 100644 --- a/src/extension/internal/pdfinput/pdf-parser.cpp +++ b/src/extension/internal/pdfinput/pdf-parser.cpp @@ -298,6 +298,7 @@ PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *bui state = new GfxState(72.0, 72.0, cropBox, rotate, gTrue); fontChanged = gFalse; clip = clipNone; + lastClipPath = NULL; ignoreUndef = 0; operatorHistory = NULL; builder = builderA; @@ -353,6 +354,7 @@ PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *bui state = new GfxState(72, 72, box, 0, gFalse); fontChanged = gFalse; clip = clipNone; + lastClipPath = NULL; ignoreUndef = 0; for (i = 0; i < 6; ++i) { baseMatrix[i] = state->getCTM()[i]; @@ -373,6 +375,9 @@ PdfParser::~PdfParser() { if (state) { delete state; } + if (lastClipPath) { + delete lastClipPath; + } } void PdfParser::parse(Object *obj, GBool topLevel) { @@ -1554,14 +1559,52 @@ void PdfParser::opShFill(Object args[], int numArgs) { GfxShading *shading; GfxPath *savedPath; double xMin, yMin, xMax, yMax; + double gradientTransform[6]; + double *matrix = NULL; + GBool savedState = gFalse; if (!(shading = res->lookupShading(args[0].getName()))) { return; } // save current graphics state - savedPath = state->getPath()->copy(); - saveState(); + if (shading->getType() != 2 && shading->getType() != 3) { + savedPath = state->getPath()->copy(); + saveState(); + savedState = gTrue; + } else { // get gradient transform if possible + // check proper operator sequence + // first there should be one W(*) and then one 'cm' somewhere before 'sh' + GBool seenClip, seenConcat; + seenClip = gFalse; + seenConcat = gFalse; + int i = 1; + while (i <= maxOperatorHistoryDepth) { + const char *opName = getPreviousOperator(i); + if (!strcmp(opName, "cm")) { + if (seenConcat) { // more than one 'cm' + break; + } else { + seenConcat = gTrue; + } + } else if (!strcmp(opName, "W") || !strcmp(opName, "W*")) { + if (seenConcat) { // good sequence + seenClip = gTrue; + break; + } else { // no 'cm' so bail out + break; + } + } + i++; + } + + if (seenConcat && seenClip) { + if (builder->getTransform((double*)&gradientTransform)) { + matrix = (double*)&gradientTransform; + builder->setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); // remove transform + } + } + } // clip to bbox if (shading->getHasBBox()) { @@ -1572,12 +1615,16 @@ void PdfParser::opShFill(Object args[], int numArgs) { state->lineTo(xMin, yMax); state->closePath(); state->clip(); - builder->clip(state); + if (savedState) + builder->setClipPath(state); + else + builder->clip(state); state->clearPath(); } // set the color space - state->setFillColorSpace(shading->getColorSpace()->copy()); + if (savedState) + state->setFillColorSpace(shading->getColorSpace()->copy()); // do shading type-specific operations switch (shading->getType()) { @@ -1586,7 +1633,10 @@ void PdfParser::opShFill(Object args[], int numArgs) { break; case 2: case 3: - // no need to implement these + if (lastClipPath) { + builder->addShadedFill(shading, matrix, lastClipPath, + lastClipType == clipEO ? true : false); + } break; case 4: case 5: @@ -1599,8 +1649,10 @@ void PdfParser::opShFill(Object args[], int numArgs) { } // restore graphics state - restoreState(); - state->setPath(savedPath); + if (savedState) { + restoreState(); + state->setPath(savedPath); + } delete shading; } @@ -1949,10 +2001,18 @@ void PdfParser::doEndPath() { void PdfParser::opClip(Object args[], int numArgs) { clip = clipNormal; + lastClipType = clipNormal; + if (lastClipPath) + delete lastClipPath; + lastClipPath = state->getPath()->copy(); } void PdfParser::opEOClip(Object args[], int numArgs) { clip = clipEO; + lastClipType = clipEO; + if (lastClipPath) + delete lastClipPath; + lastClipPath = state->getPath()->copy(); } //------------------------------------------------------------------------ diff --git a/src/extension/internal/pdfinput/pdf-parser.h b/src/extension/internal/pdfinput/pdf-parser.h index 5747dcc9a..404574537 100644 --- a/src/extension/internal/pdfinput/pdf-parser.h +++ b/src/extension/internal/pdfinput/pdf-parser.h @@ -154,6 +154,8 @@ private: void pushOperator(const char *name); OpHistoryEntry *popOperator(); const char *getPreviousOperator(unsigned int look_back=1); // returns the nth previous operator's name + GfxPath *lastClipPath; // Used as the path to be filled for an 'sh' operator + GfxClipType lastClipType; void go(GBool topLevel); void execOp(Object *cmd, Object args[], int numArgs); diff --git a/src/extension/internal/pdfinput/svg-builder.cpp b/src/extension/internal/pdfinput/svg-builder.cpp index 78426b85d..08719afd0 100644 --- a/src/extension/internal/pdfinput/svg-builder.cpp +++ b/src/extension/internal/pdfinput/svg-builder.cpp @@ -382,6 +382,42 @@ void SvgBuilder::addPath(GfxState *state, bool fill, bool stroke, bool even_odd) Inkscape::GC::release(path); } +/** + * \brief Emits the current path in poppler's GfxState data structure + * The path is set to be filled with the given shading. + */ +void SvgBuilder::addShadedFill(GfxShading *shading, double *matrix, GfxPath *path, + bool even_odd) { + + Inkscape::XML::Node *path_node = _xml_doc->createElement("svg:path"); + gchar *pathtext = svgInterpretPath(path); + path_node->setAttribute("d", pathtext); + g_free(pathtext); + + // Set style + SPCSSAttr *css = sp_repr_css_attr_new(); + gchar *id = _createGradient(shading, matrix); + if (id) { + gchar *urltext = g_strdup_printf ("url(#%s)", id); + sp_repr_css_set_property(css, "fill", urltext); + g_free(urltext); + g_free(id); + } else { + sp_repr_css_attr_unref(css); + Inkscape::GC::release(path_node); + return; + } + if (even_odd) { + sp_repr_css_set_property(css, "fill-rule", "evenodd"); + } + sp_repr_css_set_property(css, "stroke", "none"); + sp_repr_css_change(path_node, css, "style"); + sp_repr_css_attr_unref(css); + + _container->appendChild(path_node); + Inkscape::GC::release(path_node); +} + /** * \brief Clips to the current path set in GfxState * \param state poppler's data structure @@ -480,7 +516,9 @@ gchar *SvgBuilder::_createPattern(GfxPattern *pattern, GfxState *state, bool is_ gchar *id = NULL; if ( pattern != NULL ) { if ( pattern->getType() == 2 ) { // Shading pattern - id = _createGradient((GfxShadingPattern*)pattern); + GfxShadingPattern *shading_pattern = (GfxShadingPattern*)pattern; + id = _createGradient(shading_pattern->getShading(), + shading_pattern->getMatrix()); } else if ( pattern->getType() == 1 ) { // Tiling pattern id = _createTilingPattern((GfxTilingPattern*)pattern, state, is_stroke); } @@ -558,8 +596,7 @@ gchar *SvgBuilder::_createTilingPattern(GfxTilingPattern *tiling_pattern, * \brief Creates a linear or radial gradient from poppler's data structure * \return id of the created object */ -gchar *SvgBuilder::_createGradient(GfxShadingPattern *shading_pattern) { - GfxShading *shading = shading_pattern->getShading(); +gchar *SvgBuilder::_createGradient(GfxShading *shading, double *matrix) { Inkscape::XML::Node *gradient; Function *func; int num_funcs; @@ -598,13 +635,15 @@ gchar *SvgBuilder::_createGradient(GfxShadingPattern *shading_pattern) { } gradient->setAttribute("gradientUnits", "userSpaceOnUse"); // Flip the gradient transform around the y axis - double *p2u = shading_pattern->getMatrix(); - NR::Matrix pat_matrix(p2u[0], p2u[1], p2u[2], p2u[3], p2u[4], p2u[5]); - NR::Matrix flip(1.0, 0.0, 0.0, -1.0, 0.0, _height * PT_PER_PX); - pat_matrix *= flip; - gchar *transform_text = sp_svg_transform_write(pat_matrix); - gradient->setAttribute("gradientTransform", transform_text); - g_free(transform_text); + if (matrix) { + NR::Matrix pat_matrix(matrix[0], matrix[1], matrix[2], matrix[3], + matrix[4], matrix[5]); + NR::Matrix flip(1.0, 0.0, 0.0, -1.0, 0.0, _height * PT_PER_PX); + pat_matrix *= flip; + gchar *transform_text = sp_svg_transform_write(pat_matrix); + gradient->setAttribute("gradientTransform", transform_text); + g_free(transform_text); + } if ( extend0 && extend1 ) { gradient->setAttribute("spreadMethod", "pad"); diff --git a/src/extension/internal/pdfinput/svg-builder.h b/src/extension/internal/pdfinput/svg-builder.h index 5004e90f9..f2fc77971 100644 --- a/src/extension/internal/pdfinput/svg-builder.h +++ b/src/extension/internal/pdfinput/svg-builder.h @@ -33,9 +33,10 @@ namespace Inkscape { class GooString; class Function; struct GfxState; +class GfxPath; class GfxPattern; -class GfxShadingPattern; class GfxTilingPattern; +class GfxShading; class GfxFont; class GfxImageColorMap; class Stream; @@ -93,6 +94,7 @@ public: // Path adding void addPath(GfxState *state, bool fill, bool stroke, bool even_odd=false); + void addShadedFill(GfxShading *shading, double *matrix, GfxPath *path, bool even_odd=false); // Image handling void addImage(GfxState *state, Stream *str, int width, int height, @@ -144,7 +146,7 @@ private: // Pattern creation gchar *_createPattern(GfxPattern *pattern, GfxState *state, bool is_stroke=false); - gchar *_createGradient(GfxShadingPattern *shading_pattern); + gchar *_createGradient(GfxShading *shading, double *matrix); bool _addStopsToGradient(Inkscape::XML::Node *gradient, Function *func, double opacity); bool _addSamplesToGradient(Inkscape::XML::Node *gradient, Function *func, double offset0, double offset1, double opacity); -- 2.30.2