From c53f16f52840e8c0f2be9c1cc3af633c0ba1552e Mon Sep 17 00:00:00 2001 From: miklosh Date: Fri, 17 Aug 2007 11:18:05 +0000 Subject: [PATCH] Added initial transparency group and softmask support --- .../internal/pdfinput/pdf-parser.cpp | 33 +++-- .../internal/pdfinput/svg-builder.cpp | 123 +++++++++++++++++- src/extension/internal/pdfinput/svg-builder.h | 29 +++++ 3 files changed, 160 insertions(+), 25 deletions(-) diff --git a/src/extension/internal/pdfinput/pdf-parser.cpp b/src/extension/internal/pdfinput/pdf-parser.cpp index bc6190b82..d27d4dab4 100644 --- a/src/extension/internal/pdfinput/pdf-parser.cpp +++ b/src/extension/internal/pdfinput/pdf-parser.cpp @@ -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; diff --git a/src/extension/internal/pdfinput/svg-builder.cpp b/src/extension/internal/pdfinput/svg-builder.cpp index b4aaefd23..c2b467499 100644 --- a/src/extension/internal/pdfinput/svg-builder.cpp +++ b/src/extension/internal/pdfinput/svg-builder.cpp @@ -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 */ diff --git a/src/extension/internal/pdfinput/svg-builder.h b/src/extension/internal/pdfinput/svg-builder.h index 8a3687e6d..9298884a1 100644 --- a/src/extension/internal/pdfinput/svg-builder.h +++ b/src/extension/internal/pdfinput/svg-builder.h @@ -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 _node_stack; std::vector _group_depth; // Depth of nesting groups + SvgTransparencyGroup *_transp_group_stack; // Transparency group stack + std::vector _state_stack; SPCSSAttr *_font_style; // Current font style GfxFont *_current_font; -- 2.30.2