svg-builder.cpp revision a02211fee45e7402a822d3f81bc0ed49233718e5
dbb33756bd786e9432e18ec7be93f8c416e1b492Jon A. Cruz * Native PDF import using libpoppler.
e9b6af083e34e2397a8ddbe9781920733d09d151Ted Gould * miklos erdelyi
e9b6af083e34e2397a8ddbe9781920733d09d151Ted Gould * Copyright (C) 2007 Authors
e9b6af083e34e2397a8ddbe9781920733d09d151Ted Gould * Released under GNU GPL, read the file 'COPYING' for more information
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz//#define IFTRACE(_code) _code
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz * \struct SvgTransparencyGroup
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz * \brief Holds information about a PDF transparency group
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \class SvgBuilder
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruzSvgBuilder::SvgBuilder(Document *document, gchar *docname, XRef *xref) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Set default preference settings
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz _preferences = _xml_doc->createElement("svgbuilder:prefs");
6c08c8c541e1537958c0ef1bdbab931af692b7f5joncruzSvgBuilder::SvgBuilder(SvgBuilder *parent, Inkscape::XML::Node *root) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Fill _availableFontNames (Bug LP #179589) (code cfr. FontLister)
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz font_factory::Default()->GetUIFamiliesAndStyles(&familyStyleMap);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz for (FamilyToStylesMap::iterator iter = familyStyleMap.begin();
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruzvoid SvgBuilder::setDocumentSize(double width, double height) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \brief Sets groupmode of the current container to 'layer' and sets its label if given
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz _container->setAttribute("inkscape:groupmode", "layer");
6c08c8c541e1537958c0ef1bdbab931af692b7f5joncruz _container->setAttribute("inkscape:label", layer_name);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \brief Sets the current container's opacity
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz sp_repr_set_svg_double(_container, "opacity", CLAMP(opacity, 0.0, 1.0));
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruzInkscape::XML::Node *SvgBuilder::pushNode(const char *name) {
6c08c8c541e1537958c0ef1bdbab931af692b7f5joncruz Inkscape::XML::Node *node = _xml_doc->createElement(name);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz _container = _node_stack.back(); // Re-set container
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Set as a layer if this is a top-level group
6c08c8c541e1537958c0ef1bdbab931af692b7f5joncruz if ( _container->parent() == _root && _is_top_level ) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz gchar *layer_name = g_strdup_printf("%s%d", _docname, layer_count);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz if (_container != _root) { // Pop if the current container isn't root
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruzstatic gchar *svgConvertRGBToText(double r, double g, double b) {
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz "#%02x%02x%02x",
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz return svgConvertRGBToText(r, g, b);
6c08c8c541e1537958c0ef1bdbab931af692b7f5joncruzstatic void svgSetTransform(Inkscape::XML::Node *node, double c0, double c1,
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz gchar *transform_text = sp_svg_transform_write(matrix);
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz * \brief Generates a SVG path string from poppler's data structure
6c08c8c541e1537958c0ef1bdbab931af692b7f5joncruz pathString.moveTo(subpath->getX(0), subpath->getY(0));
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz pathString.curveTo(subpath->getX(j), subpath->getY(j),
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz pathString.lineTo(subpath->getX(j), subpath->getY(j));
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz * \brief Sets stroke style from poppler's GfxState data structure
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz * Uses the given SPCSSAttr for storing the style properties
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruzvoid SvgBuilder::_setStrokeStyle(SPCSSAttr *css, GfxState *state) {
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Check line width
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Ignore stroke
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz if ( state->getStrokeColorSpace()->getMode() == csPattern ) {
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz gchar *urltext = _createPattern(state->getStrokePattern(), state, true);
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz sp_repr_css_set_property(css, "stroke", svgConvertGfxRGB(&stroke_color));
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz sp_repr_css_set_property(css, "stroke-opacity", os_opacity.str().c_str());
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Line width
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz sp_repr_css_set_property(css, "stroke-width", os_width.str().c_str());
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Line cap
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz sp_repr_css_set_property(css, "stroke-linecap", "butt");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz sp_repr_css_set_property(css, "stroke-linecap", "round");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz sp_repr_css_set_property(css, "stroke-linecap", "square");
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz // Line join
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz sp_repr_css_set_property(css, "stroke-linejoin", "miter");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz sp_repr_css_set_property(css, "stroke-linejoin", "round");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz sp_repr_css_set_property(css, "stroke-linejoin", "bevel");
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // Miterlimit
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz sp_repr_css_set_property(css, "stroke-miterlimit", os_ml.str().c_str());
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // Line dash
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz state->getLineDash(&dash_pattern, &dash_length, &dash_start);
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz for ( int i = 0 ; i < dash_length ; i++ ) {
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz sp_repr_css_set_property(css, "stroke-dasharray", os_array.str().c_str());
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz sp_repr_css_set_property(css, "stroke-dashoffset", os_offset.str().c_str());
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz sp_repr_css_set_property(css, "stroke-dasharray", "none");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz sp_repr_css_set_property(css, "stroke-dashoffset", NULL);
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz * \brief Sets fill style from poppler's GfxState data structure
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz * Uses the given SPCSSAttr for storing the style properties.
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruzvoid SvgBuilder::_setFillStyle(SPCSSAttr *css, GfxState *state, bool even_odd) {
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz if ( state->getFillColorSpace()->getMode() == csPattern ) {
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz gchar *urltext = _createPattern(state->getFillPattern(), state);
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz sp_repr_css_set_property(css, "fill", svgConvertGfxRGB(&fill_color));
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz sp_repr_css_set_property(css, "fill-opacity", os_opacity.str().c_str());
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // Fill rule
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz sp_repr_css_set_property(css, "fill-rule", even_odd ? "evenodd" : "nonzero");
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz * \brief Sets style properties from poppler's GfxState data structure
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz * \return SPCSSAttr with all the relevant properties set
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. CruzSPCSSAttr *SvgBuilder::_setStyle(GfxState *state, bool fill, bool stroke, bool even_odd) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \brief Emits the current path in poppler's GfxState data structure
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * Can be used to do filling and stroking at once.
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \param fill whether the path should be filled
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \param stroke whether the path should be stroked
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \param even_odd whether the even-odd rule should be used when filling the path
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruzvoid SvgBuilder::addPath(GfxState *state, bool fill, bool stroke, bool even_odd) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz Inkscape::XML::Node *path = _xml_doc->createElement("svg:path");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz gchar *pathtext = svgInterpretPath(state->getPath());
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz // Set style
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz SPCSSAttr *css = _setStyle(state, fill, stroke, even_odd);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \brief Emits the current path in poppler's GfxState data structure
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * The path is set to be filled with the given shading.
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruzvoid SvgBuilder::addShadedFill(GfxShading *shading, double *matrix, GfxPath *path,
9fbb9b436020e98c41eb85dbaadcc2e3226c53b2joncruz Inkscape::XML::Node *path_node = _xml_doc->createElement("svg:path");
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz // Set style
9e681e203a208a1994e83056bbba03f43361c6ffJon A. Cruz gchar *id = _createGradient(shading, matrix, true);
9e681e203a208a1994e83056bbba03f43361c6ffJon A. Cruz gchar *urltext = g_strdup_printf ("url(#%s)", id);
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz sp_repr_css_set_property(css, "fill-rule", "evenodd");
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz sp_repr_css_set_property(css, "stroke", "none");
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // Remove the clipping path emitted before the 'sh' operator
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz Inkscape::XML::Node *node = _container->parent();
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz while( node && node->childCount() == 1 && up_walk < 3 ) {
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz gchar const *clip_path_url = node->attribute("clip-path");
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // Obtain clipping path's id from the URL
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz strncpy(clip_path_id, clip_path_url + 5, strlen(clip_path_url) - 6);
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz SPObject *clip_obj = _doc->getObjectById(clip_path_id);
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz TRACE(("removed clipping path: %s\n", clip_path_id));
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz * \brief Clips to the current path set in GfxState
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz * \param state poppler's data structure
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz * \param even_odd whether the even-odd rule should be applied
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruzvoid SvgBuilder::clip(GfxState *state, bool even_odd) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruzvoid SvgBuilder::setClipPath(GfxState *state, bool even_odd) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Create the clipPath repr
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz Inkscape::XML::Node *clip_path = _xml_doc->createElement("svg:clipPath");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz clip_path->setAttribute("clipPathUnits", "userSpaceOnUse");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Create the path
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz Inkscape::XML::Node *path = _xml_doc->createElement("svg:path");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz gchar *pathtext = svgInterpretPath(state->getPath());
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // Append clipPath to defs and get id
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz SP_OBJECT_REPR (SP_DOCUMENT_DEFS (_doc))->appendChild(clip_path);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz gchar *urltext = g_strdup_printf ("url(#%s)", clip_path->attribute("id"));
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz * \brief Fills the given array with the current container's transform, if set
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz * \param transform array of doubles to be filled
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \return true on success; false on invalid transformation
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz gchar const *tr = _container->attribute("transform");
87ea779e2ba0cdee8c0945db55e0504e94fdb0b3JazzyNico for ( int i = 0 ; i < 6 ; i++ ) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz return true;
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz return false;
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz * \brief Sets the transformation matrix of the current container
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruzvoid SvgBuilder::setTransform(double c0, double c1, double c2, double c3,
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz // Avoid transforming a group with an already set clip-path
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz TRACE(("setTransform: %f %f %f %f %f %f\n", c0, c1, c2, c3, c4, c5));
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz svgSetTransform(_container, c0, c1, c2, c3, c4, c5);
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruzvoid SvgBuilder::setTransform(double const *transform) {
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz setTransform(transform[0], transform[1], transform[2], transform[3],
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz * \brief Checks whether the given pattern type can be represented in SVG
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz * Used by PdfParser to decide when to do fallback operations.
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruzbool SvgBuilder::isPatternTypeSupported(GfxPattern *pattern) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz GfxShading *shading = ((GfxShadingPattern *)pattern)->getShading();
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz return true;
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz return false;
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz } else if ( pattern->getType() == 1 ) { // tiling pattern
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz return true;
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz return false;
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \brief Creates a pattern from poppler's data structure
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * Handles linear and radial gradients. Creates a new PdfParser and uses it to
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * build a tiling pattern.
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \return an url pointing to the created pattern
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruzgchar *SvgBuilder::_createPattern(GfxPattern *pattern, GfxState *state, bool is_stroke) {
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz GfxShadingPattern *shading_pattern = (GfxShadingPattern*)pattern;
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz id = _createGradient(shading_pattern->getShading(),
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz } else if ( pattern->getType() == 1 ) { // Tiling pattern
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz id = _createTilingPattern((GfxTilingPattern*)pattern, state, is_stroke);
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz gchar *urltext = g_strdup_printf ("url(#%s)", id);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \brief Creates a tiling pattern from poppler's data structure
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * Creates a sub-page PdfParser and uses it to parse the pattern's content stream.
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \return id of the created pattern
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruzgchar *SvgBuilder::_createTilingPattern(GfxTilingPattern *tiling_pattern,
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz Inkscape::XML::Node *pattern_node = _xml_doc->createElement("svg:pattern");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Set pattern transform matrix
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz Geom::Matrix pat_matrix(p2u[0], p2u[1], p2u[2], p2u[3], p2u[4], p2u[5]);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz gchar *transform_text = sp_svg_transform_write(pat_matrix);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz pattern_node->setAttribute("patternTransform", transform_text);
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz pattern_node->setAttribute("patternUnits", "userSpaceOnUse");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Set pattern tiling
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // FIXME: don't ignore XStep and YStep
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz sp_repr_set_svg_double(pattern_node, "width", bbox[2] - bbox[0]);
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz sp_repr_set_svg_double(pattern_node, "height", bbox[3] - bbox[1]);
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Convert BBox for PdfParser
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // Create new SvgBuilder and sub-page PdfParser
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz SvgBuilder *pattern_builder = new SvgBuilder(this, pattern_node);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz PdfParser *pdf_parser = new PdfParser(_xref, pattern_builder, tiling_pattern->getResDict(),
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Get pattern color space
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz GfxPatternColorSpace *pat_cs = (GfxPatternColorSpace *)( is_stroke ? state->getStrokeColorSpace()
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Set fill/stroke colors if this is an uncolored tiling pattern
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz if ( tiling_pattern->getPaintType() == 2 && ( cs = pat_cs->getUnder() ) ) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz pattern_state->setStrokeColor(state->getFillColor());
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Generate the SVG pattern
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz pdf_parser->parse(tiling_pattern->getContentStream());
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Append the pattern to defs
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz SP_OBJECT_REPR (SP_DOCUMENT_DEFS (_doc))->appendChild(pattern_node);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz gchar *id = g_strdup(pattern_node->attribute("id"));
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \brief Creates a linear or radial gradient from poppler's data structure
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \param shading poppler's data structure for the shading
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \param matrix gradient transformation, can be null
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \param for_shading true if we're creating this for a shading operator; false otherwise
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \return id of the created object
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruzgchar *SvgBuilder::_createGradient(GfxShading *shading, double *matrix, bool for_shading) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz gradient = _xml_doc->createElement("svg:linearGradient");
87ea779e2ba0cdee8c0945db55e0504e94fdb0b3JazzyNico GfxAxialShading *axial_shading = (GfxAxialShading*)shading;
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz } else if (shading->getType() == 3) { // Radial shading
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz gradient = _xml_doc->createElement("svg:radialGradient");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz GfxRadialShading *radial_shading = (GfxRadialShading*)shading;
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz radial_shading->getCoords(&x1, &y1, &r1, &x2, &y2, &r2);
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz // FIXME: the inner circle's radius is ignored here
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz } else { // Unsupported shading type
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz gradient->setAttribute("gradientUnits", "userSpaceOnUse");
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz // If needed, flip the gradient transform around the y axis
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz Geom::Matrix pat_matrix(matrix[0], matrix[1], matrix[2], matrix[3],
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz Geom::Matrix flip(1.0, 0.0, 0.0, -1.0, 0.0, _height * PT_PER_PX);
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz gchar *transform_text = sp_svg_transform_write(pat_matrix);
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz gradient->setAttribute("gradientTransform", transform_text);
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz if ( num_funcs > 1 || !_addGradientStops(gradient, shading, func) ) {
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz Inkscape::XML::Node *defs = SP_OBJECT_REPR (SP_DOCUMENT_DEFS (_doc));
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz gchar *id = g_strdup(gradient->attribute("id"));
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz * \brief Adds a stop with the given properties to the gradient's representation
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruzvoid SvgBuilder::_addStopToGradient(Inkscape::XML::Node *gradient, double offset,
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz Inkscape::XML::Node *stop = _xml_doc->createElement("svg:stop");
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz if ( _transp_group_stack != NULL && _transp_group_stack->for_softmask ) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz sp_repr_css_set_property(css, "stop-opacity", os_opacity.str().c_str());
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz sp_repr_css_set_property(css, "stop-color", color_text);
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruzstatic bool svgGetShadingColorRGB(GfxShading *shading, double offset, GfxRGB *result) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz GfxColorSpace *color_space = shading->getColorSpace();
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz ((GfxAxialShading*)shading)->getColor(offset, &temp);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz } else if ( shading->getType() == 3 ) { // Radial shading
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz ((GfxRadialShading*)shading)->getColor(offset, &temp);
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz return false;
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz // Convert it to RGB
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz return true;
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruzbool SvgBuilder::_addGradientStops(Inkscape::XML::Node *gradient, GfxShading *shading,
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz if ( type == 0 || type == 2 ) { // Sampled or exponential function
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz if ( !svgGetShadingColorRGB(shading, 0.0, &stop1) ||
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz !svgGetShadingColorRGB(shading, 1.0, &stop2) ) {
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz return false;
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz StitchingFunction *stitchingFunc = (StitchingFunction*)func;
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz // Add stops from all the stitched functions
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz for ( int i = 0 ; i < num_funcs ; i++ ) {
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz svgGetShadingColorRGB(shading, bounds[i], &color);
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz if ( i > 0 ) { // Compare to previous stop
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz svgGetShadingColorRGB(shading, bounds[i-1], &prev_color);
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz if ( abs(color.r - prev_color.r) < INT_EPSILON &&
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz // Add stops
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz _addStopToGradient(gradient, bounds[i], &color, 1.0);
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz if ( is_continuation || ( i == num_funcs - 1 ) ) {
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz svgGetShadingColorRGB(shading, bounds[i+1], &next_color);
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz _addStopToGradient(gradient, bounds[i+1], &next_color, 1.0);
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz } else { // Unsupported function type
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz return false;
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz return true;
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz * \brief Sets _invalidated_style to true to indicate that styles have to be updated
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz * Used for text output when glyphs are buffered till a font change
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz MatchingChars
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz Count for how many characters s1 matches sp taking into account
73bd3f9ed5f8ae0bc838a2d064a1aab16a8c8dd2Jon A. Cruz that a space in sp may be removed or replaced by some other tokens
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz specified in the code. (Bug LP #179589)
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruzstatic int MatchingChars(std::string s1, std::string sp)
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz unsigned int is = 0;
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz unsigned int ip = 0;
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz if (s1[is] == '_') { // Valid matches to spaces in sp.
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz SvgBuilder::_BestMatchingFont
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz Scan the available fonts to find the font name that best matches PDFname.
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz (Bug LP #179589)
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruzstd::string SvgBuilder::_BestMatchingFont(std::string PDFname)
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz for (guint i = 0; i < _availableFontNames.size(); i++) {
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // At least the first word of the font name should match.
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz double relMatch = (float)Match / (fontname.length() + PDFname.length());
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz * This array holds info about translating font weight names to more or less CSS equivalents
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz * \brief Updates _font_style according to the font set in parameter state
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz updateTextMatrix(state); // Ensure that we have a text matrix built
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz //sp_repr_css_attr_unref(_font_style);
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // Store original name
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz _font_specification = font->getOrigName()->getCString();
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz _font_specification = font->getName()->getCString();
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // Prune the font name to get the correct font family name
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // In a PDF font names can look like this: IONIPB+MetaPlusBold-Italic
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz char *plus_sign = strstr(_font_specification, "+");
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz if ( ( style_delim = g_strrstr(font_family, "-") ) ||
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz ( style_delim = g_strrstr(font_family, ",") ) ) {
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz font_style_lowercase = g_ascii_strdown(font_style, -1);
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz // Font family
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz if (font->getFamily()) { // if font family is explicitly given use it.
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz sp_repr_css_set_property(_font_style, "font-family", font->getFamily()->getCString());
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz sp_repr_get_int(_preferences, "localFonts", &attr_value);
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz // Find the font that best matches the stripped down (orig)name (Bug LP #179589).
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz sp_repr_css_set_property(_font_style, "font-family", _BestMatchingFont(font_family).c_str());
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz sp_repr_css_set_property(_font_style, "font-family", font_family);
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz // Font style
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz sp_repr_css_set_property(_font_style, "font-style", "italic");
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz sp_repr_css_set_property(_font_style, "font-style", "italic");
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz } else if (strstr(font_style_lowercase, "oblique")) {
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz sp_repr_css_set_property(_font_style, "font-style", "oblique");
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz // Font variant -- default 'normal' value
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz sp_repr_css_set_property(_font_style, "font-variant", "normal");
dd20e16383a9d1bea048d58581e19a7adb5196ccJon A. Cruz // Font weight
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz GfxFont::Weight font_weight = font->getWeight();
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz if ( font_weight != GfxFont::WeightNotDefined ) {
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz weight_num[0] = (gchar)( '1' + (font_weight - GfxFont::W100) );
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz sp_repr_css_set_property(_font_style, "font-weight", (gchar *)&weight_num);
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // Apply the font weight translations
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz int num_translations = sizeof(font_weight_translator) / ( 2 * sizeof(char *) );
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz for ( int i = 0 ; i < num_translations ; i++ ) {
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz if (strstr(font_style_lowercase, font_weight_translator[i][0])) {
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz sp_repr_css_set_property(_font_style, "font-weight", css_font_weight);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Font stretch
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz GfxFont::Stretch font_stretch = font->getStretch();
5cb9a421ea3ff6d5fd2504f5e38f6014082dcb67joncruz sp_repr_css_set_property(_font_style, "font-stretch", stretch_value);
5cb9a421ea3ff6d5fd2504f5e38f6014082dcb67joncruz // Font size
5cb9a421ea3ff6d5fd2504f5e38f6014082dcb67joncruz double css_font_size = _font_scaling * state->getFontSize();
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz sp_repr_css_set_property(_font_style, "font-size", os_font_size.str().c_str());
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz // Writing mode
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz sp_repr_css_set_property(_font_style, "writing-mode", "lr");
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz sp_repr_css_set_property(_font_style, "writing-mode", "tb");
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz * \brief Shifts the current text position by the given amount (specified in text space)
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruzvoid SvgBuilder::updateTextShift(GfxState *state, double shift) {
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz double shift_value = -shift * 0.001 * fabs(state->getFontSize());
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz * \brief Updates current text position
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruzvoid SvgBuilder::updateTextPosition(double tx, double ty) {
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz * \brief Flushes the buffered characters
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz // Update text matrix
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz double w_scale = sqrt( text_matrix[0] * text_matrix[0] + text_matrix[2] * text_matrix[2] );
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz double h_scale = sqrt( text_matrix[1] * text_matrix[1] + text_matrix[3] * text_matrix[3] );
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz // Calculate new text matrix
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz Geom::Matrix new_text_matrix(text_matrix[0] * state->getHorizScaling(),
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz // Cancel out scaling by font size in text matrix
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz for ( int i = 0 ; i < 4 ; i++ ) {
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz * \brief Writes the buffered characters to the SVG document
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz // Ignore empty strings
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz std::vector<SvgGlyph>::iterator i = _glyphs.begin();
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz // Ignore invisible characters
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz Inkscape::XML::Node *text_node = _xml_doc->createElement("svg:text");
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz // Set text matrix
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz gchar *transform = sp_svg_transform_write(text_transform);
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz unsigned int glyphs_in_a_row = 0;
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz // Output all buffered glyphs
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz while (1) {
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz std::vector<SvgGlyph>::iterator prev_iterator = i - 1;
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz // Check if we need to make a new tspan
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz if ( !( ( glyph.dy == 0.0 && prev_glyph.dy == 0.0 &&
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz glyph.text_position[1] == prev_glyph.text_position[1] ) ||
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz glyph.text_position[0] == prev_glyph.text_position[0] ) ) ) {
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz // Create tspan node if needed
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz // Set the x and y coordinate arrays
0edc248c6edb75f5cbef095bf782eaf8273a6514joncruz sp_repr_set_svg_double(tspan_node, "x", last_delta_pos[0]);
9e681e203a208a1994e83056bbba03f43361c6ffJon A. Cruz sp_repr_set_svg_double(tspan_node, "y", last_delta_pos[1]);
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz TRACE(("tspan content: %s\n", text_buffer.c_str()));
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz // Add text content node to tspan
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz Inkscape::XML::Node *text_content = _xml_doc->createTextNode(text_buffer.c_str());
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz // Clear temporary buffers
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz // Create a font specification string and save the attribute in the style
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz PangoFontDescription *descr = pango_font_description_from_string(glyph.font_specification);
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz Glib::ustring properFontSpec = font_factory::Default()->ConstructFontSpecification(descr);
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz sp_repr_css_set_property(glyph.style, "-inkscape-font-specification", properFontSpec.c_str());
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Set style and unref SPCSSAttr if it won't be needed anymore
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz sp_repr_css_change(tspan_node, glyph.style, "style");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz if ( glyph.style_changed && i != _glyphs.begin() ) { // Free previous style
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // Check if we have the same coordinates
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz for ( int p = 0 ; p < 2 ; p++ ) {
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz if ( glyph.text_position[p] != prev_glyph.text_position[p] ) {
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz // Append the coordinates to their respective strings
d70ad7a28e89c69b96ec96e311aeef00b0f506f2joncruz Geom::Point delta_pos( glyph.text_position - first_glyph.text_position );
55919b10f5e27d2fa50c03ca269bce594f5c8d82joncruz // Append the character to the text buffer
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruzvoid SvgBuilder::beginString(GfxState *state, GooString *s) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz TRACE(("tm: %f %f %f %f %f %f\n",m[0], m[1],m[2], m[3], m[4], m[5]));
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz TRACE(("ctm: %f %f %f %f %f %f\n",m[0], m[1],m[2], m[3], m[4], m[5]));
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * \brief Adds the specified character to the text buffer
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * Takes care of converting it to UTF-8 and generates a new style repr if style
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz * has changed since the last call.
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruzvoid SvgBuilder::addChar(GfxState *state, double x, double y,
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz // Skip beginning space
9fbb9b436020e98c41eb85dbaadcc2e3226c53b2joncruz // Allow only one space in a row
9fbb9b436020e98c41eb85dbaadcc2e3226c53b2joncruz if ( is_space && _glyphs[_glyphs.size() - 1].code_size == 1 &&
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz new_glyph.position = Geom::Point( x - originX, y - originY );
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Convert the character to UTF-8 since that's our SVG document's encoding
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz for ( int i = 0 ; i < uLen ; i++ ) {
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz code_size += u_map->mapUnicode(u[i], (char *)&new_glyph.code[code_size], sizeof(new_glyph.code) - code_size);
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Copy current style if it has changed since the previous glyph
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz if (_invalidated_style || _glyphs.size() == 0 ) {
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Set style
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz bool has_stroke = ( render_mode & 3 ) == 1 || ( render_mode & 3 ) == 2;
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz new_glyph.style = _setStyle(state, has_fill, has_stroke);
d5edbcb9362e1d1e28bf53abade939e610bb3cd4joncruz sp_repr_css_merge(new_glyph.style, _font_style); // Merge with font style
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Point to previous glyph's style information
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz _invalidated_style = true; // Force copying of current state
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // TODO: clip if render_mode >= 4
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz * Helper functions for supporting direct PNG output into a base64 encoded stream
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruzvoid png_write_base64stream(png_structp png_ptr, png_bytep data, png_size_t length)
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz (Inkscape::IO::Base64OutputStream*)png_get_io_ptr(png_ptr); // Get pointer to stream
5cb9a421ea3ff6d5fd2504f5e38f6014082dcb67joncruz for ( unsigned i = 0 ; i < length ; i++ ) {
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz (Inkscape::IO::Base64OutputStream*)png_get_io_ptr(png_ptr); // Get pointer to stream
5cb9a421ea3ff6d5fd2504f5e38f6014082dcb67joncruz * \brief Creates an <image> element containing the given ImageStream as a PNG
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruzInkscape::XML::Node *SvgBuilder::_createImage(Stream *str, int width, int height,
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz GfxImageColorMap *color_map, int *mask_colors, bool alpha_only, bool invert_alpha) {
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Create PNG write struct
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Create PNG info struct
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz png_infop info_ptr = png_create_info_struct(png_ptr);
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Set error handler
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Decide whether we should embed this image
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz sp_repr_get_int(_preferences, "embedImages", &attr_value);
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Set read/write functions
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz Inkscape::IO::Base64OutputStream base64_stream(base64_string);
9fbb9b436020e98c41eb85dbaadcc2e3226c53b2joncruz base64_stream.setColumnWidth(0); // Disable line breaks
9fbb9b436020e98c41eb85dbaadcc2e3226c53b2joncruz png_set_write_fn(png_ptr, &base64_stream, png_write_base64stream, png_flush_base64stream);
9fbb9b436020e98c41eb85dbaadcc2e3226c53b2joncruz static int counter = 0;
9fbb9b436020e98c41eb85dbaadcc2e3226c53b2joncruz file_name = g_strdup_printf("%s_img%d.png", _docname, counter++);
9fbb9b436020e98c41eb85dbaadcc2e3226c53b2joncruz // Set header data
9fbb9b436020e98c41eb85dbaadcc2e3226c53b2joncruz // Write the file header
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Convert pixels
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz image_stream = new ImageStream(str, width, color_map->getNumPixelComps(),
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Convert grayscale values
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz for ( int y = 0 ; y < height ; y++ ) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz for ( int x = 0 ; x < width ; x++ ) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz } else if (color_map) {
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Convert RGB values
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz for ( int y = 0 ; y < height ; y++ ) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz for ( int x = 0 ; x < width ; x++ ) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Check each color component against the mask
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz for ( int i = 0; i < color_map->getNumPixelComps() ; i++) {
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Advance to the next pixel
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Write it to the PNG
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz for ( int i = 0 ; i < height ; i++ ) {
e27725d84d44011b7512665d18ffad32224e1cf1Jon A. Cruz } else { // A colormap must be provided, so quit
3095d406b8f21b2d15939d51c34ec4b0f3c2e558joncruz // Close PNG
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Create repr
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz Inkscape::XML::Node *image_node = _xml_doc->createElement("svg:image");
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Set transformation
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz svgSetTransform(image_node, 1.0, 0.0, 0.0, -1.0, 0.0, 1.0);
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Create href
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz // Append format specification to the URI
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz Glib::ustring& png_data = base64_string.getString();
b1fd0330076a954f02eb6a6a7879a90cb1886557joncruz image_node->setAttribute("xlink:href", png_data.c_str());
if (_is_top_level) {
static int mask_count = 0;
if (image_node) {
bool invert) {
if (mask_image_node) {
bool invert_mask) {
if (mask_image_node) {
if (image_node) {
if (mask_image_node) {
if (image_node) {
bool for_softmask) {
popNode();
delete transpGroup;
pushGroup();
delete transpGroup;
popGroup();