Code

Added support for the shading operator
authormiklosh <miklosh@users.sourceforge.net>
Thu, 9 Aug 2007 13:04:00 +0000 (13:04 +0000)
committermiklosh <miklosh@users.sourceforge.net>
Thu, 9 Aug 2007 13:04:00 +0000 (13:04 +0000)
src/extension/internal/pdfinput/pdf-parser.cpp
src/extension/internal/pdfinput/pdf-parser.h
src/extension/internal/pdfinput/svg-builder.cpp
src/extension/internal/pdfinput/svg-builder.h

index 1d312c1165004da7c3d6e0b224ae61007afef9fe..465350b78edcc7775c7dbaabc58f85d2c3fd36a5 100644 (file)
@@ -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();
 }
 
 //------------------------------------------------------------------------
index 5747dcc9a0acd95b6974f726790096fd480d7c8f..40457453729ba14af273071ce0ff87ecb78f34b1 100644 (file)
@@ -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);
index 78426b85da3052db7a0afc5cfca0f853541cf6cb..08719afd023f500ca7201987b8b6e6abd87977cb 100644 (file)
@@ -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");
index 5004e90f9af2c358bc8798ae6b9baccaa8f73ac4..f2fc779710148f71038d892ec9ed2db84305d58c 100644 (file)
@@ -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);