svg-builder.cpp revision edf415bfa81ef3be799bb0d740a9ca54553ff091
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Native PDF import using libpoppler.
436b29506c99a8bfdb7aac4045f2b913bb0ac8efperrin * miklos erdelyi
436b29506c99a8bfdb7aac4045f2b913bb0ac8efperrin * Jon A. Cruz <jon@joncruz.org>
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Copyright (C) 2007 Authors
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Released under GNU GPL, read the file 'COPYING' for more information
fa9e4066f08beec538e775443c5be79dd423fcabahrens//#define IFTRACE(_code) _code
3589c4f01c20349ca65899d209cdc0c17a641433Neil Perrin * \struct SvgTransparencyGroup
b24ab6762772a3f6a89393947930c7fa61306783Jeff Bonwick * \brief Holds information about a PDF transparency group
6e1f5caa9321646aa4212d48e32a0d241866d85dNeil Perrin * \class SvgBuilder
6e1f5caa9321646aa4212d48e32a0d241866d85dNeil PerrinSvgBuilder::SvgBuilder(SPDocument *document, gchar *docname, XRef *xref)
fa9e4066f08beec538e775443c5be79dd423fcabahrens // Set default preference settings
b4d654b017879b2de918ec2b934d04fbea6c4e62perrin _preferences = _xml_doc->createElement("svgbuilder:prefs");
d80c45e0f58fa434ba37259ea2e2b12e0380c19abonwickSvgBuilder::SvgBuilder(SvgBuilder *parent, Inkscape::XML::Node *root) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw // Fill _availableFontNames (Bug LP #179589) (code cfr. FontLister)
569e6c63191416b7413c148fd5a6194a0b820b2cmarks font_factory::Default()->GetUIFamiliesAndStyles(&familyStyleMap);
569e6c63191416b7413c148fd5a6194a0b820b2cmarks for (FamilyToStylesMap::iterator iter = familyStyleMap.begin();
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwvoid SvgBuilder::setDocumentSize(double width, double height) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * \brief Sets groupmode of the current container to 'layer' and sets its label if given
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw _container->setAttribute("inkscape:groupmode", "layer");
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * \brief Sets the current container's opacity
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw sp_repr_set_svg_double(_container, "opacity", CLAMP(opacity, 0.0, 1.0));
b24ab6762772a3f6a89393947930c7fa61306783Jeff Bonwick new_state.softmask = _state_stack.back().softmask;
fa9e4066f08beec538e775443c5be79dd423fcabahrensInkscape::XML::Node *SvgBuilder::pushNode(const char *name) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens Inkscape::XML::Node *node = _xml_doc->createElement(name);
b24ab6762772a3f6a89393947930c7fa61306783Jeff Bonwick TRACE(("popNode() called when stack is empty\n"));
b24ab6762772a3f6a89393947930c7fa61306783Jeff Bonwick Inkscape::XML::Node *saved_container = _container;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw // Set as a layer if this is a top-level group
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw gchar *layer_name = g_strdup_printf("%s%d", _docname, layer_count);
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (_container != _root) { // Pop if the current container isn't root
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic gchar *svgConvertRGBToText(double r, double g, double b) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw "#%02x%02x%02x",
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return svgConvertRGBToText(r, g, b);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void svgSetTransform(Inkscape::XML::Node *node, double c0, double c1,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * \brief Generates a SVG path string from poppler's data structure
fa9e4066f08beec538e775443c5be79dd423fcabahrens pathString.moveTo(subpath->getX(0), subpath->getY(0));
fa9e4066f08beec538e775443c5be79dd423fcabahrens pathString.curveTo(subpath->getX(j), subpath->getY(j),
fa9e4066f08beec538e775443c5be79dd423fcabahrens pathString.lineTo(subpath->getX(j), subpath->getY(j));
fa9e4066f08beec538e775443c5be79dd423fcabahrens * \brief Sets stroke style from poppler's GfxState data structure
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Uses the given SPCSSAttr for storing the style properties
fa9e4066f08beec538e775443c5be79dd423fcabahrensvoid SvgBuilder::_setStrokeStyle(SPCSSAttr *css, GfxState *state) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens // Check line width
fa9e4066f08beec538e775443c5be79dd423fcabahrens // Ignore stroke
fa9e4066f08beec538e775443c5be79dd423fcabahrens if ( state->getStrokeColorSpace()->getMode() == csPattern ) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens gchar *urltext = _createPattern(state->getStrokePattern(), state, true);
fa9e4066f08beec538e775443c5be79dd423fcabahrens sp_repr_css_set_property(css, "stroke", svgConvertGfxRGB(&stroke_color));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw sp_repr_css_set_property(css, "stroke-opacity", os_opacity.str().c_str());
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw // Line width
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw sp_repr_css_set_property(css, "stroke-width", os_width.str().c_str());
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw // Line cap
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw sp_repr_css_set_property(css, "stroke-linecap", "butt");
fa9e4066f08beec538e775443c5be79dd423fcabahrens sp_repr_css_set_property(css, "stroke-linecap", "round");
fa9e4066f08beec538e775443c5be79dd423fcabahrens sp_repr_css_set_property(css, "stroke-linecap", "square");
510b6c0e09388dd3bd75e2d1afd0f1ef80a7f440Neil Perrin // Line join
510b6c0e09388dd3bd75e2d1afd0f1ef80a7f440Neil Perrin sp_repr_css_set_property(css, "stroke-linejoin", "miter");
510b6c0e09388dd3bd75e2d1afd0f1ef80a7f440Neil Perrin sp_repr_css_set_property(css, "stroke-linejoin", "round");
510b6c0e09388dd3bd75e2d1afd0f1ef80a7f440Neil Perrin sp_repr_css_set_property(css, "stroke-linejoin", "bevel");
510b6c0e09388dd3bd75e2d1afd0f1ef80a7f440Neil Perrin // Miterlimit
510b6c0e09388dd3bd75e2d1afd0f1ef80a7f440Neil Perrin sp_repr_css_set_property(css, "stroke-miterlimit", os_ml.str().c_str());
510b6c0e09388dd3bd75e2d1afd0f1ef80a7f440Neil Perrin // Line dash
104e2ed78d9ef0a0f89f320108b8ca29ca3850d5perrin state->getLineDash(&dash_pattern, &dash_length, &dash_start);
b24ab6762772a3f6a89393947930c7fa61306783Jeff Bonwick for ( int i = 0 ; i < dash_length ; i++ ) {
104e2ed78d9ef0a0f89f320108b8ca29ca3850d5perrin sp_repr_css_set_property(css, "stroke-dasharray", os_array.str().c_str());
fa9e4066f08beec538e775443c5be79dd423fcabahrens sp_repr_css_set_property(css, "stroke-dashoffset", os_offset.str().c_str());
fa9e4066f08beec538e775443c5be79dd423fcabahrens sp_repr_css_set_property(css, "stroke-dasharray", "none");
fa9e4066f08beec538e775443c5be79dd423fcabahrens sp_repr_css_set_property(css, "stroke-dashoffset", NULL);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * \brief Sets fill style from poppler's GfxState data structure
c5c6ffa0498b9c8555798756141b4a3061a138c1maybee * Uses the given SPCSSAttr for storing the style properties.
b24ab6762772a3f6a89393947930c7fa61306783Jeff Bonwickvoid SvgBuilder::_setFillStyle(SPCSSAttr *css, GfxState *state, bool even_odd) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens if ( state->getFillColorSpace()->getMode() == csPattern ) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens gchar *urltext = _createPattern(state->getFillPattern(), state);
1209a471b5681c43d839d4b890f708f500da7346Neil Perrin sp_repr_css_set_property(css, "fill", svgConvertGfxRGB(&fill_color));
fa9e4066f08beec538e775443c5be79dd423fcabahrens sp_repr_css_set_property(css, "fill-opacity", os_opacity.str().c_str());
b24ab6762772a3f6a89393947930c7fa61306783Jeff Bonwick // Fill rule
5002558f6bfef3915c7f3b4ecb7c19c7f044bf5bNeil Perrin sp_repr_css_set_property(css, "fill-rule", even_odd ? "evenodd" : "nonzero");
fd1368791be99c4a6354fa81f08408c2dbf4b444Matthew Ahrens * \brief Sets style properties from poppler's GfxState data structure
fd1368791be99c4a6354fa81f08408c2dbf4b444Matthew Ahrens * \return SPCSSAttr with all the relevant properties set
fa9e4066f08beec538e775443c5be79dd423fcabahrensSPCSSAttr *SvgBuilder::_setStyle(GfxState *state, bool fill, bool stroke, bool even_odd) {
e09fa4dacfb671e707d50a55ae9b5cc191e1b8cbNeil Perrin sp_repr_css_set_property(css, "stroke", "none");
fa9e4066f08beec538e775443c5be79dd423fcabahrens * \brief Emits the current path in poppler's GfxState data structure
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Can be used to do filling and stroking at once.
bool even_odd) {
if (id) {
if (even_odd) {
int up_walk = 0;
if (clip_path_url) {
if (clip_obj) {
up_walk++;
pushGroup();
if (even_odd) {
if (valid) {
pushGroup();
!is_stroke);
return NULL;
return urltext;
&box);
delete pdf_parser;
delete pattern_builder;
return id;
int num_funcs;
return NULL;
if (matrix) {
return NULL;
return id;
for ( int i = 0 ; i < num_funcs ; i++ ) {
bool is_continuation = false;
is_continuation = true;
if ( !is_continuation ) {
if (_in_text_object) {
_invalidated_style = true;
unsigned int is = 0;
unsigned int ip = 0;
ip++;
is++;
return(ip);
double bestMatch = 0;
if (bestMatch == 0)
return PDFname;
return bestFontname;
_need_font_update = false;
if (_font_style) {
if (plus_sign) {
style_delim[0] = 0;
if (attr_value != 0) {
} else if (font_style) {
} else if (font_style) {
for ( int i = 0 ; i < num_translations ; i++ ) {
if (css_font_weight) {
if (font_style_lowercase) {
switch (font_stretch) {
_invalidated_style = true;
_flushText();
double max_scale;
bool new_tspan = true;
unsigned int glyphs_in_a_row = 0;
new_tspan = true;
new_tspan = true;
if (tspan_node) {
if ( same_coords[0] ) {
glyphs_in_a_row = 0;
new_tspan = false;
if ( glyphs_in_a_row > 0 ) {
same_coords[p] = false;
if (_need_font_update) {
for (int i = 0; i < uLen; i++) {
uu[i] = u[i];
_invalidated_style = false;
_in_text_object = true;
_flushText();
_in_text_object = false;
for ( unsigned i = 0 ; i < length ; i++ ) {
return NULL;
return NULL;
return NULL;
if (embed_image) {
static int counter = 0;
return NULL;
if (alpha_only) {
if (alpha_only) {
if (color_map) {
for ( int y = 0 ; y < height ; y++ ) {
if (color_map) {
for ( int x = 0 ; x < width ; x++ ) {
*buf_ptr++ = 0;
delete [] buffer;
} else if (color_map) {
if (mask_colors) {
for ( int y = 0 ; y < height ; y++ ) {
for ( int x = 0 ; x < width ; x++ ) {
dest++;
for ( int i = 0 ; i < height ; i++ ) {
delete [] buffer;
if (!embed_image) {
return NULL;
delete image_stream;
if (_is_top_level) {
if (embed_image) {
return image_node;
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();