Code

Added initial transparency group and softmask support
authormiklosh <miklosh@users.sourceforge.net>
Fri, 17 Aug 2007 11:18:05 +0000 (11:18 +0000)
committermiklosh <miklosh@users.sourceforge.net>
Fri, 17 Aug 2007 11:18:05 +0000 (11:18 +0000)
src/extension/internal/pdfinput/pdf-parser.cpp
src/extension/internal/pdfinput/svg-builder.cpp
src/extension/internal/pdfinput/svg-builder.h

index bc6190b827a8282dfd539de380912ecb5fbb5398..d27d4dab42cf3acbf963ca35e3f9b264f219dc55 100644 (file)
@@ -794,7 +794,7 @@ void PdfParser::opSetExtGState(Object args[], int numArgs) {
   // soft mask
   if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
     if (obj2.isName("None")) {
-      //out->clearSoftMask(state);
+      builder->clearSoftMask(state);
     } else if (obj2.isDict()) {
       if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {
        alpha = gTrue;
@@ -1577,7 +1577,7 @@ void PdfParser::opShFill(Object args[], int numArgs) {
       // check proper operator sequence
       // first there should be one W(*) and then one 'cm' somewhere before 'sh'
       GBool seenClip, seenConcat;
-      seenClip = gFalse;
+      seenClip = (clipHistory->getClipPath() != NULL);
       seenConcat = gFalse;
       int i = 1;
       while (i <= maxOperatorHistoryDepth) {
@@ -1588,13 +1588,6 @@ void PdfParser::opShFill(Object args[], int numArgs) {
           } 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++;
       }
@@ -2813,6 +2806,12 @@ void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox
   // kill any pre-existing path
   state->clearPath();
 
+  if (softMask || transpGroup) {
+    builder->clearSoftMask(state);
+    builder->pushTransparencyGroup(state, bbox, blendingColorSpace,
+                                   isolated, knockout, softMask);
+  }
+
   // save current parser
   oldParser = parser;
 
@@ -2838,14 +2837,12 @@ void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox
       state->setBlendMode(gfxBlendNormal);
     }
     if (state->getFillOpacity() != 1) {
+      builder->setGroupOpacity(state->getFillOpacity());
       state->setFillOpacity(1);
     }
     if (state->getStrokeOpacity() != 1) {
       state->setStrokeOpacity(1);
     }
-    //out->clearSoftMask(state);
-    //out->beginTransparencyGroup(state, bbox, blendingColorSpace,
-    //                         isolated, knockout, softMask);
   }
 
   // set new base matrix
@@ -2857,10 +2854,6 @@ void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox
   // draw the form
   parse(str, gFalse);
 
-  if (softMask || transpGroup) {
-    //out->endTransparencyGroup(state);
-  }
-
   // restore base matrix
   for (i = 0; i < 6; ++i) {
     baseMatrix[i] = oldBaseMatrix[i];
@@ -2869,6 +2862,10 @@ void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox
   // restore parser
   parser = oldParser;
 
+  if (softMask || transpGroup) {
+      builder->popTransparencyGroup(state);
+  }
+
   // restore graphics state
   restoreState();
 
@@ -2876,9 +2873,9 @@ void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox
   popResources();
 
   if (softMask) {
-    //out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
+    builder->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
   } else if (transpGroup) {
-    //out->paintTransparencyGroup(state, bbox);
+    builder->paintTransparencyGroup(state, bbox);
   }
 
   return;
index b4aaefd235acf532f19a3d4cb35dc06d77cb6148..c2b467499d70c71031b32bd03cb2b69d6ef98c84 100644 (file)
@@ -55,6 +55,21 @@ namespace Internal {
 #define TRACE(_args) IFTRACE(g_print _args)
 
 
+/**
+ * \struct SvgTransparencyGroup
+ * \brief Holds information about a PDF transparency group
+ */
+struct SvgTransparencyGroup {
+    double bbox[6];
+    Inkscape::XML::Node *container;
+
+    bool isolated;
+    bool knockout;
+    bool for_softmask;
+
+    SvgTransparencyGroup *next;
+};
+
 /**
  * \class SvgBuilder
  * 
@@ -92,6 +107,11 @@ void SvgBuilder::_init() {
     _current_font = NULL;
     _current_state = NULL;
 
+    _transp_group_stack = NULL;
+    SvgGraphicsState initial_state;
+    initial_state.softmask = NULL;
+    initial_state.group_depth = 0;
+    _state_stack.push_back(initial_state);
     _node_stack.push_back(_container);
 }
 
@@ -112,16 +132,26 @@ void SvgBuilder::setAsLayer(char *layer_name) {
     }
 }
 
+/**
+ * \brief Sets the current container's opacity
+ */
+void SvgBuilder::setGroupOpacity(double opacity) {
+    sp_repr_set_svg_double(_container, "opacity", CLAMP(opacity, 0.0, 1.0));
+}
+
 void SvgBuilder::saveState() {
-    _group_depth.push_back(0);
+    SvgGraphicsState new_state;
+    new_state.group_depth = 0;
+    new_state.softmask = _state_stack.back().softmask;
+    _state_stack.push_back(new_state);
     pushGroup();
 }
 
 void SvgBuilder::restoreState() {
-    while (_group_depth.back() > 0) {
+    while( _state_stack.back().group_depth > 0 ) {
         popGroup();
     }
-    _group_depth.pop_back();
+    _state_stack.pop_back();
 }
 
 Inkscape::XML::Node *SvgBuilder::pushNode(const char *name) {
@@ -149,7 +179,7 @@ Inkscape::XML::Node *SvgBuilder::pushGroup() {
     Inkscape::XML::Node *node = pushNode("svg:g");
     saved_container->appendChild(node);
     Inkscape::GC::release(node);
-    _group_depth.back()++;
+    _state_stack.back().group_depth++;
     // Set as a layer if this is a top-level group
     if ( _container->parent() == _root && _is_top_level ) {
         static int layer_count = 1;
@@ -168,7 +198,7 @@ Inkscape::XML::Node *SvgBuilder::pushGroup() {
 Inkscape::XML::Node *SvgBuilder::popGroup() {
     if (_container != _root) {  // Pop if the current container isn't root
         popNode();
-        _group_depth[_group_depth.size()-1] = --_group_depth.back();
+        _state_stack.back().group_depth--;
     }
 
     return _container;
@@ -698,9 +728,17 @@ void SvgBuilder::_addStopToGradient(Inkscape::XML::Node *gradient, double offset
     Inkscape::XML::Node *stop = _xml_doc->createElement("svg:stop");
     SPCSSAttr *css = sp_repr_css_attr_new();
     Inkscape::CSSOStringStream os_opacity;
-    os_opacity << opacity;
+    gchar *color_text = NULL;
+    if ( _transp_group_stack != NULL && _transp_group_stack->for_softmask ) {
+        double gray = (double)color->r / 65535.0;
+        gray = CLAMP(gray, 0.0, 1.0);
+        os_opacity << gray;
+        color_text = "#ffffff";
+    } else {
+        os_opacity << opacity;
+        color_text = svgConvertGfxRGB(color);
+    }
     sp_repr_css_set_property(css, "stop-opacity", os_opacity.str().c_str());
-    gchar *color_text = svgConvertGfxRGB(color);
     sp_repr_css_set_property(css, "stop-color", color_text);
 
     sp_repr_css_change(stop, css, "style");
@@ -1602,6 +1640,77 @@ void SvgBuilder::addSoftMaskedImage(GfxState *state, Stream *str, int width, int
     }
 }
 
+/**
+ * \brief Starts building a new transparency group
+ */
+void SvgBuilder::pushTransparencyGroup(GfxState *state, double *bbox,
+                                       GfxColorSpace *blending_color_space,
+                                       bool isolated, bool knockout,
+                                       bool for_softmask) {
+
+    // Push node stack
+    pushNode("svg:g");
+
+    // Setup new transparency group
+    SvgTransparencyGroup *transpGroup = new SvgTransparencyGroup;
+    memcpy(&transpGroup->bbox, bbox, sizeof(bbox));
+    transpGroup->isolated = isolated;
+    transpGroup->knockout = knockout;
+    transpGroup->for_softmask = for_softmask;
+    transpGroup->container = _container;
+
+    // Push onto the stack
+    transpGroup->next = _transp_group_stack;
+    _transp_group_stack = transpGroup;
+}
+
+void SvgBuilder::popTransparencyGroup(GfxState *state) {
+    // Restore node stack
+    popNode();
+}
+
+/**
+ * \brief Places the current transparency group into the current container
+ */
+void SvgBuilder::paintTransparencyGroup(GfxState *state, double *bbox) {
+    SvgTransparencyGroup *transpGroup = _transp_group_stack;
+    _container->appendChild(transpGroup->container);
+    Inkscape::GC::release(transpGroup->container);
+    // Pop the stack
+    _transp_group_stack = transpGroup->next;
+    delete transpGroup;
+}
+
+/**
+ * \brief Creates a mask using the current transparency group as its content
+ */
+void SvgBuilder::setSoftMask(GfxState *state, double *bbox, bool alpha,
+                             Function *transfer_func, GfxColor *backdrop_color) {
+
+    // Create mask
+    Inkscape::XML::Node *mask_node = _createMask(1.0, 1.0);
+    // Add the softmask content to it
+    SvgTransparencyGroup *transpGroup = _transp_group_stack;
+    mask_node->appendChild(transpGroup->container);
+    Inkscape::GC::release(transpGroup->container);
+    // Apply the mask
+    _state_stack.back().softmask = mask_node;
+    pushGroup();
+    gchar *mask_url = g_strdup_printf("url(#%s)", mask_node->attribute("id"));
+    _container->setAttribute("mask", mask_url);
+    g_free(mask_url);
+    // Pop the stack
+    _transp_group_stack = transpGroup->next;
+    delete transpGroup;
+}
+
+void SvgBuilder::clearSoftMask(GfxState *state) {
+    if (_state_stack.back().softmask) {
+        _state_stack.back().softmask = NULL;
+        popGroup();
+    }
+}
+
 } } } /* namespace Inkscape, Extension, Internal */
 
 #endif /* HAVE_POPPLER */
index 8a3687e6d03dc74c7ed8fedce9e7598c0af18ee0..9298884a1898fddfddfb91d317be1269f345dabe 100644 (file)
@@ -33,6 +33,8 @@ namespace Inkscape {
 class GooString;
 class Function;
 struct GfxState;
+class GfxColor;
+class GfxColorSpace;
 class GfxRGB;
 class GfxPath;
 class GfxPattern;
@@ -52,6 +54,19 @@ namespace Inkscape {
 namespace Extension {
 namespace Internal {
 
+struct SvgTransparencyGroup;
+
+/**
+ * \struct SvgGraphicsState
+ * Holds information about the current softmask and group depth.
+ * Could be later used to store other graphics state parameters so that we could
+ * emit only the differences in style settings from the parent state.
+ */
+struct SvgGraphicsState {
+    Inkscape::XML::Node *softmask; // Points to current softmask node
+    int group_depth;    // Depth of nesting groups at this level
+};
+
 /**
  * \struct SvgGlyph
  * Holds information about glyphs added by PdfParser which haven't been added
@@ -87,6 +102,7 @@ public:
     // Property setting
     void setDocumentSize(double width, double height);  // Document size in px
     void setAsLayer(char *layer_name=NULL);
+    void setGroupOpacity(double opacity);
 
     // Handling the node stack
     Inkscape::XML::Node *pushGroup();
@@ -111,6 +127,17 @@ public:
                             Stream *mask_str, int mask_width, int mask_height,
                             GfxImageColorMap *mask_color_map);
 
+    // Transparency group and soft mask handling
+    void pushTransparencyGroup(GfxState *state, double *bbox,
+                               GfxColorSpace *blending_color_space,
+                               bool isolated, bool knockout,
+                               bool for_softmask);
+    void popTransparencyGroup(GfxState *state);
+    void paintTransparencyGroup(GfxState *state, double *bbox);
+    void setSoftMask(GfxState *state, double *bbox, bool alpha,
+                     Function *transfer_func, GfxColor *backdrop_color);
+    void clearSoftMask(GfxState *state);
+
     // Text handling
     void beginString(GfxState *state, GooString *s);
     void endString(GfxState *state);
@@ -171,6 +198,8 @@ private:
     Inkscape::XML::Node *popNode();
     std::vector<Inkscape::XML::Node *> _node_stack;
     std::vector<int> _group_depth;    // Depth of nesting groups
+    SvgTransparencyGroup *_transp_group_stack;  // Transparency group stack
+    std::vector<SvgGraphicsState> _state_stack;
 
     SPCSSAttr *_font_style;          // Current font style
     GfxFont *_current_font;