27#include <poppler/Function.h>
28#include <poppler/GfxFont.h>
29#include <poppler/GfxState.h>
30#include <poppler/Page.h>
31#include <poppler/Stream.h>
64#define TRACE(_args) IFTRACE(g_print _args)
140 if (!
label.empty()) {
152 auto tr = st.translation();
153 if (st[0] < 0 || st[2] < 0) {
156 if (st[1] < 0 || st[3] < 0) {
204 val <<
"M" << box.left() <<
" " << box.top()
205 <<
"H" << box.right() <<
"V" << box.bottom()
206 <<
"H" << box.left() <<
"Z";
208 gchar *urltext = g_strdup_printf(
"url(#%s)", clip_path->attribute(
"id"));
225 g_warning(
"Can not store PDF margins in bare document.");
230 val << margins.
top() -
page.top() <<
" "
238 g_warning(
"Can not store PDF bleed in bare document.");
242 val <<
page.top() - bleed.
top() <<
" "
252 if (
name && !content.empty()) {
290 if (state == mask_state) {
324 TRACE((
"_popContainer() called when stack is empty\n"));
359 gchar *urltext = g_strdup_printf(
"url(#%s)", clip_path->attribute(
"id"));
385 static gchar tmp[1023] = {0};
391 return (gchar *)&tmp;
396 double r = (double)color->r / 65535.0;
397 double g = (double)color->g / 65535.0;
398 double b = (double)color->b / 65535.0;
404 std::string icc =
"";
405 switch (space->getMode()) {
412#if POPPLER_CHECK_VERSION(0, 90, 0)
413 auto icc_space =
dynamic_cast<GfxICCBasedColorSpace *
>(space);
416 g_warning(
"ICC profile ignored; libpoppler >= 0.90.0 required.");
422 space->getRGB(color, &
rgb);
427 icc_color << rgb_color <<
" icc-color(" << icc;
428 for (
int i = 0; i < space->getNComps(); ++i) {
429 icc_color <<
", " << colToDbl((*color).c[i]);
432 return icc_color.
str();
440 g_error(
"Adding transform AFTER clipping path.");
450 for (
int i = 0 ; i < path->getNumSubpaths() ; ++i ) {
451 _POPPLER_CONST_83 GfxSubpath *subpath = path->getSubpath(i);
452 if (subpath->getNumPoints() > 0) {
453 pathString.
moveTo(subpath->getX(0), subpath->getY(0));
455 while (j < subpath->getNumPoints()) {
456 if (subpath->getCurve(j)) {
457 pathString.
curveTo(subpath->getX(j), subpath->getY(j),
458 subpath->getX(j+1), subpath->getY(j+1),
459 subpath->getX(j+2), subpath->getY(j+2));
463 pathString.
lineTo(subpath->getX(j), subpath->getY(j));
467 if (subpath->isClosed()) {
473 return g_strdup(pathString.
c_str());
482 auto space = state->getStrokeColorSpace();
483 if (space->getMode() == csPattern) {
484 gchar *urltext =
_createPattern(state->getStrokePattern(), state,
true);
495 os_opacity << state->getStrokeOpacity();
500 double lw = state->getLineWidth();
502 os_width << (lw > 0.0 ? lw : 1.0);
506 switch (state->getLineCap()) {
519 switch (state->getLineJoin()) {
533 os_ml << state->getMiterLimit();
539#if POPPLER_CHECK_VERSION(22, 9, 0)
541 const std::vector<double> &dash = state->getLineDash(&dash_start);
543 dash_length = dash.size();
546 state->getLineDash(&
dash_pattern, &dash_length, &dash_start);
548 if ( dash_length > 0 ) {
550 for (
int i = 0 ; i < dash_length ; i++ ) {
552 if (i < (dash_length - 1)) {
559 os_offset << dash_start;
574 auto space = state->getFillColorSpace();
575 if (space->getMode() == csPattern) {
587 os_opacity << state->getFillOpacity();
601 GfxBlendMode blendmode = state->getBlendMode();
644 if (!prev || prev->attribute(
"mask"))
651 if (path != prev_d && path != std::string(prev_d) +
" Z")
657 return prev_val ==
"none";
699 if (!strlen(pathtext) || (fill != stroke &&
mergePath(state, fill, pathtext, even_odd))) {
741 gchar *urltext = g_strdup_printf (
"url(#%s)",
id);
788 if (
clip == clipNormal) {
812 if (
auto attr = clip_node->attribute(
"transform")) {
814 clip_node->removeAttribute(
"transform");
842 if (node_vec.
empty()) {
881 std::string prev_d = prev_path->
attribute(
"d");
882 std::string prev_tr = prev_path->attribute(
"transform") ? prev_path->attribute(
"transform")
885 prev_path->attribute(
"clip-rule") ? std::string(prev_path->attribute(
"clip-rule")) ==
"evenodd" :
false;
894 clip_path->
setAttribute(
"clipPathUnits",
"userSpaceOnUse");
919 if (
name && group && std::string(
name) ==
"OC") {
920 auto layer_id = std::string(
"layer-") +
sanitizeId(group);
922 if (existing->getRepr()->parent() ==
_container) {
926 g_warning(
"Unexpected marked content group in PDF!");
933 auto pair =
_ocgs[group];
965 return save_current_location;
981 cmsHPROFILE hp = cmsOpenProfileFromMem(profBuf, length);
983 g_warning(
"Failed to read ICCBased color space profile from PDF file.");
1013 auto icc_data = std::string(
"data:application/vnd.iccprofile;base64,") + profile->dumpBase64();
1027 if ( pattern !=
nullptr ) {
1028 if ( pattern->getType() == 2 ) {
1029 GfxShading *shading = (
static_cast<GfxShadingPattern *
>(pattern))->getShading();
1030 int shadingType = shading->getType();
1031 if ( shadingType == 2 ||
1032 shadingType == 3 ) {
1036 }
else if ( pattern->getType() == 1 ) {
1051 gchar *
id =
nullptr;
1052 if ( pattern !=
nullptr ) {
1053 if ( pattern->getType() == 2 ) {
1054 GfxShadingPattern *shading_pattern =
static_cast<GfxShadingPattern *
>(pattern);
1058 auto grad_affine =
ctmToAffine(shading_pattern->getMatrix());
1062 auto affine = (grad_affine * pt * flip) * obj_affine.inverse();
1063 id =
_createGradient(shading_pattern->getShading(), affine, !is_stroke);
1064 }
else if ( pattern->getType() == 1 ) {
1070 gchar *urltext = g_strdup_printf (
"url(#%s)",
id);
1081 GfxState *state,
bool is_stroke) {
1085 auto pat_matrix =
ctmToAffine(tiling_pattern->getMatrix());
1087 pattern_node->
setAttribute(
"patternUnits",
"userSpaceOnUse");
1090 const double *bbox = tiling_pattern->getBBox();
1107 GfxPatternColorSpace *pat_cs = (GfxPatternColorSpace *)( is_stroke ? state->getStrokeColorSpace()
1108 : state->getFillColorSpace() );
1110 GfxColorSpace *cs =
nullptr;
1111 if ( tiling_pattern->getPaintType() == 2 && ( cs = pat_cs->getUnder() ) ) {
1112 GfxState *pattern_state = pdf_parser->
getState();
1113 pattern_state->setFillColorSpace(cs->copy());
1114 pattern_state->setFillColor(state->getFillColor());
1115 pattern_state->setStrokeColorSpace(cs->copy());
1116 pattern_state->setStrokeColor(state->getFillColor());
1120 pdf_parser->
parse(tiling_pattern->getContentStream());
1124 delete pattern_builder;
1128 gchar *
id = g_strdup(pattern_node->
attribute(
"id"));
1144 _POPPLER_CONST Function *func;
1146 bool extend0, extend1;
1148 if ( shading->getType() == 2 ) {
1150 GfxAxialShading *axial_shading =
static_cast<GfxAxialShading*
>(shading);
1151 double x1, y1, x2, y2;
1152 axial_shading->getCoords(&x1, &y1, &x2, &y2);
1157 extend0 = axial_shading->getExtend0();
1158 extend1 = axial_shading->getExtend1();
1159 num_funcs = axial_shading->getNFuncs();
1160 func = axial_shading->getFunc(0);
1161 }
else if (shading->getType() == 3) {
1163 GfxRadialShading *radial_shading =
static_cast<GfxRadialShading*
>(shading);
1164 double x1, y1, r1, x2, y2, r2;
1165 radial_shading->getCoords(&x1, &y1, &r1, &x2, &y2, &r2);
1172 extend0 = radial_shading->getExtend0();
1173 extend1 = radial_shading->getExtend1();
1174 num_funcs = radial_shading->getNFuncs();
1175 func = radial_shading->getFunc(0);
1179 gradient->
setAttribute(
"gradientUnits",
"userSpaceOnUse");
1185 if ( extend0 && extend1 ) {
1195 gchar *
id = g_strdup(gradient->
attribute(
"id"));
1201#define EPSILON 0.0001
1211 std::string color_text =
"#ffffff";
1212 if (space->getMode() == csDeviceGray) {
1215 space->getRGB(color, &
rgb);
1216 double gray = (double)
rgb.r / 65535.0;
1217 gray = CLAMP(gray, 0.0, 1.0);
1220 os_opacity << opacity;
1236 if ( shading->getType() == 2 ) {
1237 (
static_cast<GfxAxialShading *
>(shading))->getColor(
offset,
result);
1238 }
else if ( shading->getType() == 3 ) {
1239 (
static_cast<GfxRadialShading *
>(shading))->getColor(
offset,
result);
1246#define INT_EPSILON 8
1248 _POPPLER_CONST Function *func) {
1249 auto type = func->getType();
1250 auto space = shading->getColorSpace();
1251 if (type == _POPPLER_FUNCTION_TYPE_SAMPLED || type == _POPPLER_FUNCTION_TYPE_EXPONENTIAL) {
1252 GfxColor stop1, stop2;
1259 }
else if (type == _POPPLER_FUNCTION_TYPE_STITCHING) {
1260 auto stitchingFunc =
static_cast<_POPPLER_CONST StitchingFunction*
>(func);
1261 const double *
bounds = stitchingFunc->getBounds();
1262 const double *encode = stitchingFunc->getEncode();
1263 int num_funcs = stitchingFunc->getNumFuncs();
1265 double max_bound = std::max({1.0,
bounds[num_funcs]});
1268 GfxColor prev_color, color;
1271 for (
int i = 0 ; i < num_funcs ; i++ ) {
1274 if (stitchingFunc->getFunc(i)->getType() == _POPPLER_FUNCTION_TYPE_EXPONENTIAL) {
1275 double expE = (
static_cast<_POPPLER_CONST ExponentialFunction*
>(stitchingFunc->getFunc(i)))->getE();
1278 if (encode[2*i] == 0) {
1312 TRACE((
"updateFont()\n"));
1315 auto font = state->getFont();
1316 auto font_id = font->getID()->num;
1318 auto new_font_size = state->getFontSize();
1319 if (font->getType() == fontType3) {
1320 const double *font_matrix = font->getFontMatrix();
1321 if (font_matrix[0] != 0.0) {
1322 new_font_size *= font_matrix[3] / font_matrix[0];
1358 auto new_font_specification = font_data.getSpecification();
1359 TRACE((
"FontSpecification: %s\n", new_font_specification.c_str()));
1371 if (font_data.found) {
1376 auto keep_name = font_data.family.size() ? font_data.family : font_data.name;
1387 if ( font->getWMode() == 0 ) {
1398 double shift_value = -
shift * 0.001 * fabs(state->getFontSize());
1399 if (state->getFont()->getWMode()) {
1449 text_node->setAttribute(
"xml:space",
"preserve");
1452 auto font = state->getFont();
1453 if (font->getWMode() == 1) {
1462 unsigned int glyphs_in_tspan = 0;
1463 Glib::ustring text_buffer;
1466 std::string x_coords;
1467 std::string y_coords;
1468 std::string dx_coords;
1469 std::string dy_coords;
1471 auto first_glyph =
_glyphs.front();
1472 auto prev_glyph =
_glyphs.front();
1479 if (glyphs_in_tspan == 0) {
1482 Geom::Point delta_pos(glyph.text_position - first_glyph.text_position);
1483 delta_pos[1] += glyph.rise;
1484 delta_pos[1] *= -1.0;
1486 delta_pos += glyph.origin;
1489 os_x << delta_pos[0];
1490 x_coords.append(os_x.
str());
1493 os_y << delta_pos[1];
1494 y_coords.append(os_y.
str());
1499 if (glyphs_in_tspan != 0) {
1501 delta_dpos = glyph.text_position - prev_glyph.text_position - prev_glyph.advance;
1505 if (std::abs(delta_dpos[0]) < 0.005) {
1506 delta_dpos[0] = 0.0;
1508 if (std::abs(delta_dpos[1]) < 0.005) {
1509 delta_dpos[1] = 0.0;
1512 delta_dpos[1] += glyph.rise;
1513 delta_dpos[1] *= -1.0;
1519 os_dx << delta_dpos[0] <<
" ";
1520 dx_coords.append(os_dx.
str());
1523 os_dy << delta_dpos[1] <<
" ";
1524 dy_coords.append(os_dy.
str());
1528 for (
int i = 0; i < glyph.code.size(); i++) {
1529 text_buffer.append(1, glyph.code[i]);
1531 dx_coords.append(
"0 ");
1532 dy_coords.append(
"0 ");
1542 auto writing_mode = state->getFont()->getWMode();
1543 auto next_it = it + 1;
1546 next_it->style_changed ||
1547 (writing_mode == 0 && std::abs(glyph.text_position[1] - next_it->text_position[1]) > 0.1) ||
1548 (writing_mode == 1 && std::abs(glyph.text_position[0] - next_it->text_position[0]) > 0.1);
1563 double text_size = text_scale * glyph.text_size;
1565 _setTextStyle(tspan_node, glyph.state, glyph.css_font, text_transform);
1570 if (next_it ==
_glyphs.end() ||
1571 next_it->style_changed ) {
1576 while (dx_coords.ends_with(
" 0 ")) {
1577 dx_coords.erase(dx_coords.length() - 2);
1580 while (dy_coords.ends_with(
" 0 ")) {
1581 dy_coords.erase(dy_coords.length() - 2);
1585 if (dx_coords ==
"0 ") {
1589 if (dy_coords ==
"0 ") {
1594 if (dx_coords.length() > 0) {
1595 dx_coords.pop_back();
1598 if (dy_coords.length() > 0) {
1599 dy_coords.pop_back();
1602 tspan_node->setAttributeOrRemoveIfEmpty(
"x", x_coords);
1603 tspan_node->setAttributeOrRemoveIfEmpty(
"dx", dx_coords);
1605 tspan_node->setAttributeOrRemoveIfEmpty(
"y", y_coords);
1606 tspan_node->setAttributeOrRemoveIfEmpty(
"dy", dy_coords);
1613 text_buffer.clear();
1614 glyphs_in_tspan = 0;
1616 TRACE((
"tspan content: %s\n", text_buffer.c_str()));
1631 auto cairo_glyphs = (cairo_glyph_t *)gmallocn(
_glyphs.size(),
sizeof(cairo_glyph_t));
1632 unsigned int cairo_glyph_count = 0;
1638 auto first_glyph =
_glyphs.front();
1644 Geom::Point delta_pos(glyph.text_position - first_glyph.text_position);
1645 delta_pos[1] += glyph.rise;
1646 delta_pos[1] *= -1.0;
1650 cairo_glyphs[cairo_glyph_count].index = glyph.cairo_index;
1651 cairo_glyphs[cairo_glyph_count].x = delta_pos[
Geom::X];
1652 cairo_glyphs[cairo_glyph_count].
y = delta_pos[
Geom::Y];
1653 cairo_glyph_count++;
1655 bool is_last_glyph = (it + 1) ==
_glyphs.end();
1656 bool flush_text = is_last_glyph ? true : (it+1)->style_changed;
1659 if (!is_last_glyph && !text_group) {
1663 double text_size = text_scale * glyph.text_size;
1667 node =
_renderText(glyph.cairo_font, text_size, text_transform, cairo_glyphs, cairo_glyph_count);
1669 g_warning(
"Empty or broken text in PDF file.");
1680 cairo_glyph_count = 0;
1682 if (is_last_glyph) {
1689 gfree(cairo_glyphs);
1690 cairo_glyphs =
nullptr;
1726 if (state->getRender() == 3) {
1727 std::cerr <<
"SVGBuilder::_flushText: Invisible pdf glyphs removed!" << std::endl;
1736 std::vector<SvgGlyph>::iterator i =
_glyphs.begin();
1737 const SvgGlyph& first_glyph = (*i);
1742 auto pos = first_glyph.
position * tr;
1773 int render_mode = state->getRender();
1774 bool has_fill = !(render_mode & 1);
1775 bool has_stroke = ( render_mode & 3 ) == 1 || ( render_mode & 3 ) == 2;
1777 state = state->save();
1778 state->setCTM(ta[0], ta[1], ta[2], ta[3], ta[4], ta[5]);
1781 state = state->restore();
1805 cairo_glyph_t *cairo_glyphs,
unsigned int count)
1810 if (!cairo_glyphs || !cairo_font ||
_aria_label.empty()) {
1811 std::cerr <<
"SvgBuilder::_renderText: Invalid argument!" << std::endl;
1818 cairo_set_font_face(cairo, cairo_font->getFontFace());
1819 cairo_set_font_size(cairo, font_size);
1821 cairo_glyph_path(cairo, cairo_glyphs, count);
1823 cairo_destroy(cairo);
1824 cairo_surface_destroy(
surface);
1828 std::cerr <<
"SvgBuilder::_renderText: Failed to render PDF text! " <<
_aria_label << std::endl;
1835 if (textpath.empty()) {
1836 std::cerr <<
"SvgBuilder::_renderText: Empty path! " <<
_aria_label << std::endl;
1858 IFTRACE(
double *m = state->getTextMat());
1859 TRACE((
"tm: %f %f %f %f %f %f\n",m[0], m[1],m[2], m[3], m[4], m[5]));
1860 IFTRACE(m = state->getCTM());
1861 TRACE((
"ctm: %f %f %f %f %f %f\n",m[0], m[1],m[2], m[3], m[4], m[5]));
1880 double dx,
double dy,
1881 double ax,
double ay,
1882 double originX,
double originY,
1883 CharCode code,
int , Unicode
const *u,
int uLen)
1896 std::string utf8_code;
1897 static std::wstring_convert<std::codecvt_utf8<char32_t>,
char32_t> conv1;
1901 utf8_code = conv1.to_bytes(*u);
1907 if (uLen > 0 && u[0] < 0x80 && g_ascii_iscntrl(u[0]) && !g_ascii_isspace(u[0])) {
1908 g_warning(
"Skipping ASCII control character %u", u[0]);
1921 bool is_space = ( uLen == 1 && u[0] == 32 );
1924 new_glyph.
code = utf8_code;
1932 new_glyph.
state = state;
1955 new_glyph.
rise = state->getRise();
1956 new_glyph.
char_space = state->getCharSpace();
1957 new_glyph.
word_space = state->getWordSpace();
1962 std::cout <<
"SVGBuilder::addChar: " << new_glyph.
code
1963 <<
" style changed: " << std::boolalpha << new_glyph.
style_changed
1964 << std::setprecision(4)
1965 <<
" position: " << new_glyph.
position
1966 <<
" delta: " << new_glyph.
delta
1967 <<
" x,y: (" << x <<
", " << y <<
") "
1968 <<
" origin: (" << originX <<
", " << originY <<
") "
1969 <<
" rise: " << new_glyph.
rise
1971 <<
" state: " << (
void*)new_glyph.
state
2002 auto *v_ptr =
reinterpret_cast<std::vector<guchar> *
>(png_get_io_ptr(png_ptr));
2003 for (
unsigned i = 0 ; i < length ; i++ ) {
2004 v_ptr->push_back(
data[i]);
2013 GfxImageColorMap *color_map,
bool interpolate,
2014 int *mask_colors,
bool alpha_only,
2015 bool invert_alpha) {
2018 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
2019 if ( png_ptr ==
nullptr ) {
2023 png_infop info_ptr = png_create_info_struct(png_ptr);
2024 if ( info_ptr ==
nullptr ) {
2025 png_destroy_write_struct(&png_ptr,
nullptr);
2029 if (setjmp(png_jmpbuf(png_ptr))) {
2030 png_destroy_write_struct(&png_ptr, &info_ptr);
2037 std::vector<guchar> png_buffer;
2039 gchar *file_name =
nullptr;
2045 fp = fopen(file_name,
"wb");
2046 if ( fp ==
nullptr ) {
2047 png_destroy_write_struct(&png_ptr, &info_ptr);
2051 png_init_io(png_ptr, fp);
2055 if ( !invert_alpha && !alpha_only ) {
2056 png_set_invert_alpha(png_ptr);
2058 png_color_8 sig_bit;
2060 png_set_IHDR(png_ptr, info_ptr,
2064 PNG_COLOR_TYPE_GRAY,
2066 PNG_COMPRESSION_TYPE_BASE,
2067 PNG_FILTER_TYPE_BASE);
2074 png_set_IHDR(png_ptr, info_ptr,
2078 PNG_COLOR_TYPE_RGB_ALPHA,
2080 PNG_COMPRESSION_TYPE_BASE,
2081 PNG_FILTER_TYPE_BASE);
2087 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
2088 png_set_bgr(png_ptr);
2090 png_write_info(png_ptr, info_ptr);
2093 ImageStream *image_stream;
2096 image_stream =
new ImageStream(str,
width, color_map->getNumPixelComps(),
2097 color_map->getBits());
2099 image_stream =
new ImageStream(str,
width, 1, 1);
2101 image_stream->reset();
2104 unsigned char *buffer =
new unsigned char[
width];
2105 int invert_bit = invert_alpha ? 1 : 0;
2106 for (
int y = 0 ; y <
height ; y++ ) {
2107 unsigned char *row = image_stream->getLine();
2109 color_map->getGrayLine(row, buffer,
width);
2111 unsigned char *buf_ptr = buffer;
2112 for (
int x = 0 ; x <
width ; x++ ) {
2113 if ( row[x] ^ invert_bit ) {
2120 png_write_row(png_ptr, (png_bytep)buffer);
2123 }
else if (color_map) {
2124 image_stream =
new ImageStream(str,
width,
2125 color_map->getNumPixelComps(),
2126 color_map->getBits());
2127 image_stream->reset();
2130 unsigned int *buffer =
new unsigned int[
width];
2132 for (
int y = 0 ; y <
height ; y++ ) {
2133 unsigned char *row = image_stream->getLine();
2134 color_map->getRGBLine(row, buffer,
width);
2136 unsigned int *dest = buffer;
2137 for (
int x = 0 ; x <
width ; x++ ) {
2139 for (
int i = 0; i < color_map->getNumPixelComps() ; i++) {
2140 if ( row[i] < mask_colors[2*i] * 255 ||
2141 row[i] > mask_colors[2*i + 1] * 255 ) {
2142 *dest = *dest | 0xff000000;
2147 row += color_map->getNumPixelComps();
2151 png_write_row(png_ptr, (png_bytep)buffer);
2154 for (
int i = 0 ; i <
height ; i++ ) {
2155 unsigned char *row = image_stream->getLine();
2156 memset((
void*)buffer, 0xff,
sizeof(
int) *
width);
2157 color_map->getRGBLine(row, buffer,
width);
2158 png_write_row(png_ptr, (png_bytep)buffer);
2164 png_destroy_write_struct(&png_ptr, &info_ptr);
2171 delete image_stream;
2174 png_write_end(png_ptr, info_ptr);
2175 png_destroy_write_struct(&png_ptr, &info_ptr);
2181 if( !interpolate ) {
2190 image_node->
setAttribute(
"preserveAspectRatio",
"none");
2195 auto *base64String = g_base64_encode(png_buffer.data(), png_buffer.size());
2196 auto png_data = std::string(
"data:image/png;base64,") + base64String;
2197 g_free(base64String);
2215 mask_node->
setAttribute(
"maskUnits",
"userSpaceOnUse");
2226 static int mask_count = 0;
2227 gchar *mask_id = g_strdup_printf(
"_mask%d", mask_count++);
2237 bool interpolate,
int *mask_colors)
2249 bool invert,
bool interpolate) {
2271 if (mask_image_node) {
2278 gchar *mask_url = g_strdup_printf(
"url(#%s)", mask_node->
attribute(
"id"));
2286 bool interpolate, Stream *mask_str,
int mask_width,
int mask_height,
bool invert_mask,
2287 bool mask_interpolate)
2290 nullptr, mask_interpolate,
nullptr,
true, invert_mask);
2292 if ( mask_image_node && image_node ) {
2302 gchar *mask_url = g_strdup_printf(
"url(#%s)", mask_node->
attribute(
"id"));
2309 }
else if (image_node) {
2312 if (mask_image_node) {
2318 bool interpolate, Stream *mask_str,
int mask_width,
int mask_height,
2319 GfxImageColorMap *mask_color_map,
bool mask_interpolate)
2322 mask_color_map, mask_interpolate,
nullptr,
true);
2324 if ( mask_image_node && image_node ) {
2331 gchar *mask_url = g_strdup_printf(
"url(#%s)", mask_node->
attribute(
"id"));
2338 }
else if (image_node) {
2341 if (mask_image_node) {
2354 return obj->getRepr();
2376 if (source_gr && target_gr && source_gr->childCount() == target_gr->childCount()) {
2377 bool same_pos =
_attrEqual(source_gr, target_gr,
"x1") &&
_attrEqual(source_gr, target_gr,
"x2")
2380 bool white_mask =
false;
2381 for (
auto source_st = source_gr->firstChild(); source_st !=
nullptr; source_st = source_st->next()) {
2383 white_mask = white_mask or source_css->getAttributeDouble(
"stop-opacity") != 1.0;
2384 if (std::string(source_css->attribute(
"stop-color")) !=
"#ffffff") {
2390 if (same_pos && white_mask) {
2392 auto target_st = target_gr->firstChild();
2393 for (
auto source_st = source_gr->firstChild(); source_st !=
nullptr; source_st = source_st->next()) {
2398 target_st = target_st->next();
2402 source_gr->parent()->removeChild(source_gr);
2407 gchar *mask_url = g_strdup_printf(
"url(#%s)", mask->
attribute(
"id"));
2417 bool knockout,
bool for_softmask)
2450 if (
parent->childCount() == 1 && !
parent->attribute(
"transform")) {
2454 if (will_clip &&
child->attribute(
"d")) {
2462 if (!will_clip && !
child->attribute(
"mask") && !
child->attribute(
"clip-path")) {
2463 auto orig =
child->getAttributeDouble(
"opacity", 1.0);
2464 auto grp =
parent->getAttributeDouble(
"opacity", 1.0);
2465 child->setAttributeSvgDouble(
"opacity",
orig * grp);
2472 if (
auto clip =
parent->attribute(
"clip-path")) {
2494 for (
auto font : *fonts.get()) {
2495 int id = font.first->getID()->num;
2496 bool found = font.second.found;
void ink_cairo_transform(cairo_t *ct, Geom::Affine const &m)
std::optional< Geom::PathVector > extract_pathvector_from_cairo(cairo_t *ct)
Cairo integration helpers.
Cairo::RefPtr< Cairo::ImageSurface > surface
ClipHistoryEntry * save(bool cleared=false)
Create a new clip-history, appending it to the stack.
const Geom::Affine & getAffine()
GfxClipType getClipType()
void setClip(GfxState *state, GfxClipType newClipType=clipNormal, bool bbox=false)
ClipHistoryEntry * restore()
3x3 matrix representing an affine transformation.
void setTranslation(Point const &loc)
Sets the translation imparted by the Affine.
Coord expansionX() const
Calculates the amount of x-scaling imparted by the Affine.
Affine inverse() const
Compute the inverse matrix.
C right() const
Return rightmost coordinate of the rectangle (+X is to the right).
C top() const
Return top coordinate of the rectangle (+Y is downwards).
C left() const
Return leftmost coordinate of the rectangle (+X is to the right).
C bottom() const
Return bottom coordinate of the rectangle (+Y is downwards).
Axis-aligned rectangle that can be empty.
void push_back(Path const &path)
Append a path at the end.
bool empty() const
Check whether the vector contains any paths.
Sequence of contiguous curves, aka spline.
Two-dimensional point that doubles as a vector.
constexpr Coord y() const noexcept
Axis aligned, non-empty rectangle.
Translate inverse() const
Get the inverse translation.
A thin wrapper around std::ostringstream, but writing floating point numbers in the format required b...
static std::shared_ptr< Profile > create(cmsHPROFILE handle, std::string path="", bool in_home=false)
Construct a color profile object from the lcms2 object.
std::shared_ptr< Space::CMS > getSpace(std::string const &name) const
Get the specific color space from the list of available spaces.
Builds the inner SVG representation using libpoppler from the calls of PdfParser.
Inkscape::XML::Node * _clip_text
void updateTextShift(GfxState *state, double shift)
Shifts the current text position by the given amount (specified in text space)
std::string convertGfxColor(const GfxColor *color, GfxColorSpace *space)
void updateTextMatrix(GfxState *state, bool flip)
Flushes the buffered characters.
Inkscape::XML::Document * _xml_doc
void endTextObject(GfxState *state)
ClipHistoryEntry * _clip_history
Inkscape::XML::Node * _pushGroup()
void applyOptionalMask(Inkscape::XML::Node *mask, Inkscape::XML::Node *target)
Take a constructed mask and decide how to apply it to the target.
void beginTextObject(GfxState *state)
These text object functions are the outer most calls for begining and ending text.
void updateTextPosition(double tx, double ty)
Updates current text position.
void setDocumentSize(double width, double height)
Inkscape::XML::Node * _getClip(const Inkscape::XML::Node *node)
Return the active clip as a new xml node.
void cropPage(const Geom::Rect &bbox)
Crop to this bounding box, do this before setMargins() but after setDocumentSize.
Inkscape::XML::Node * _preferences
void addImageMask(GfxState *state, Stream *str, int width, int height, bool invert, bool interpolate)
gchar * _createTilingPattern(GfxTilingPattern *tiling_pattern, GfxState *state, bool is_stroke=false)
Creates a tiling pattern from poppler's data structure Creates a sub-page PdfParser and uses it to pa...
void restoreState(GfxState *state)
void setClip(GfxState *state, GfxClipType clip, bool is_bbox=false)
Clips to the current path set in GfxState.
void setMetadata(char const *name, const std::string &content)
std::shared_ptr< CairoFont > _cairo_font
void _flushText(GfxState *state)
Writes the buffered characters to the SVG document.
std::string _getColorProfile(cmsHPROFILE hp)
Return the color profile name if it's already been added.
Inkscape::XML::Node * _popContainer()
Inkscape::XML::Node * _addToContainer(const char *name)
Create an svg element and append it to the current container object.
static FontStrategies autoFontStrategies(FontStrategy s, FontList fonts)
Decide what to do for each font in the font list, with the given strategy.
bool _shouldClip(const Inkscape::XML::Node *node) const
void _setClipPath(Inkscape::XML::Node *node)
std::string _font_specification
void popGroup(GfxState *state)
gchar * _createGradient(GfxShading *shading, const Geom::Affine pat_matrix, bool for_shading=false)
Creates a linear or radial gradient from poppler's data structure.
void addColorProfile(unsigned char *profBuf, int length)
void beginMarkedContent(const char *name=nullptr, const char *group=nullptr)
Geom::Affine _text_matrix
bool _addGradientStops(Inkscape::XML::Node *gradient, GfxShading *shading, _POPPLER_CONST Function *func)
void setGroupOpacity(double opacity)
Sets the current container's opacity.
void setAsLayer(const char *layer_name=nullptr, bool visible=true)
Sets groupmode of the current container to 'layer' and sets its label if given.
void saveState(GfxState *state)
Inkscape::XML::Node * _renderText(std::shared_ptr< CairoFont > cairo_font, double font_size, const Geom::Affine &transform, cairo_glyph_t *cairo_glyphs, unsigned int count)
Renders the text as a path object using cairo and returns the node object.
std::map< cmsHPROFILE, std::string > _icc_profiles
bool _invalidated_strategy
bool isPatternTypeSupported(GfxPattern *pattern)
Checks whether the given pattern type can be represented in SVG Used by PdfParser to decide when to d...
void endLayer(Inkscape::XML::Node *save)
Inkscape::XML::Node * _pushContainer(const char *name)
void _setTextStyle(Inkscape::XML::Node *node, GfxState *state, SPCSSAttr *font_style, Geom::Affine text_affine)
Sets the style for the text, rendered or un-rendered, preserving the text_transform for any gradients...
bool shouldMergePath(bool is_fill, const std::string &path)
Returns the CSSAttr of the previously added path if it's exactly the same path AND is missing the fil...
void addOptionalGroup(const std::string &oc, const std::string &label, bool visible=true)
void addSoftMaskedImage(GfxState *state, Stream *str, int width, int height, GfxImageColorMap *color_map, bool interpolate, Stream *mask_str, int mask_width, int mask_height, GfxImageColorMap *mask_color_map, bool mask_interpolate)
Geom::Point _text_position
std::vector< GfxState * > _mask_groups
void updateStyle(GfxState *state)
Sets _invalidated_style to true to indicate that styles have to be updated Used for text output when ...
void addPath(GfxState *state, bool fill, bool stroke, bool even_odd=false)
Emits the current path in poppler's GfxState data structure Can be used to do filling and stroking at...
Inkscape::XML::Node * _clip_text_group
bool mergePath(GfxState *state, bool is_fill, const std::string &path, bool even_odd=false)
Set the fill XOR stroke of the previously added path, if that path is missing the given attribute AND...
void addClippedFill(GfxShading *shading, const Geom::Affine shading_tr)
FontStrategies _font_strategies
Inkscape::XML::Node * _flushTextPath(GfxState *state, double text_scale, const Geom::Affine &text_transform)
Create path node(s) for text.
std::map< std::string, std::pair< std::string, bool > > _ocgs
static bool _attrEqual(Inkscape::XML::Node *a, Inkscape::XML::Node *b, char const *attr)
Geom::Affine _page_affine
void updateFont(GfxState *state, std::shared_ptr< CairoFont > cairo_font, bool flip)
Updates _css_font according to the font set in parameter state.
void setMargins(const Geom::Rect &page, const Geom::Rect &margins, const Geom::Rect &bleed)
Calculate the page margin size based on the pdf settings.
std::vector< Inkscape::XML::Node * > _node_stack
Inkscape::XML::Node * _getGradientNode(Inkscape::XML::Node *node, bool is_fill)
Find the fill or stroke gradient we previously set on this node.
void addImage(GfxState *state, Stream *str, int width, int height, GfxImageColorMap *color_map, bool interpolate, int *mask_colors)
std::vector< SvgGlyph > _glyphs
gchar * _createPattern(GfxPattern *pattern, GfxState *state, bool is_stroke=false)
Creates a pattern from poppler's data structure Handles linear and radial gradients.
void beforeStateChange(GfxState *old_state)
Notifies the svg builder the state will change.
void _setStrokeStyle(SPCSSAttr *css, GfxState *state)
Sets stroke style from poppler's GfxState data structure Uses the given SPCSSAttr for storing the sty...
void endString(GfxState *state)
Inkscape::XML::Node * _popGroup()
Inkscape::XML::Node * _container
Inkscape::XML::Node * _prev_clip
Inkscape::XML::Node * _root
void addMaskedImage(GfxState *state, Stream *str, int width, int height, GfxImageColorMap *color_map, bool interpolate, Stream *mask_str, int mask_width, int mask_height, bool invert_mask, bool mask_interpolate)
Inkscape::XML::Node * _page
void _addStopToGradient(Inkscape::XML::Node *gradient, double offset, GfxColor *color, GfxColorSpace *space, double opacity)
Adds a stop with the given properties to the gradient's representation.
Inkscape::XML::Node * _createMask(double width, double height)
Creates a <mask> with the specified width and height and adds to <defs> If we're not the top-level Sv...
Inkscape::XML::Node * _createClip(const std::string &d, const Geom::Affine tr, bool even_odd)
void addShadedFill(GfxShading *shading, const Geom::Affine shading_tr, GfxPath *path, const Geom::Affine tr, bool even_odd=false)
Emits the current path in poppler's GfxState data structure The path is set to be filled with the giv...
SvgBuilder(SPDocument *document, gchar *docname, XRef *xref)
void _setBlendMode(Inkscape::XML::Node *node, GfxState *state)
Sets blend style properties from poppler's GfxState data structure \update a SPCSSAttr with all mix-b...
SPCSSAttr * _setStyle(GfxState *state, bool fill, bool stroke, bool even_odd=false)
Sets style properties from poppler's GfxState data structure.
void pushPage(const std::string &label, GfxState *state)
We're creating a multi-page document, push page number.
Inkscape::XML::Node * _flushTextText(GfxState *state, double text_scale, const Geom::Affine &text_transform)
Create text node for text.
void _setTransform(Inkscape::XML::Node *node, GfxState *state, Geom::Affine extra=Geom::identity())
Inkscape::XML::Node * _createImage(Stream *str, int width, int height, GfxImageColorMap *color_map, bool interpolate, int *mask_colors, bool alpha_only=false, bool invert_alpha=false)
Creates an <image> element containing the given ImageStream as a PNG.
void addChar(GfxState *state, double x, double y, double dx, double dy, double ax, double ay, double originX, double originY, CharCode code, int nBytes, Unicode const *u, int uLen)
Adds the specified character to the text buffer Takes care of converting it to UTF-8 and generates a ...
Inkscape::XML::Node * beginLayer(const std::string &label, bool visible)
void beginString(GfxState *state, int len)
Begin and end string is the inner most text processing step which tells us we're about to have a cert...
void finishGroup(GfxState *state, bool for_softmask)
void _setFillStyle(SPCSSAttr *css, GfxState *state, bool even_odd)
Sets fill style from poppler's GfxState data structure Uses the given SPCSSAttr for storing the style...
void startGroup(GfxState *state, double *bbox, GfxColorSpace *blending_color_space, bool isolated, bool knockout, bool for_softmask)
Starts building a new transparency group.
Builder for SVG path strings.
PathString & curveTo(Geom::Coord x0, Geom::Coord y0, Geom::Coord x1, Geom::Coord y1, Geom::Coord x, Geom::Coord y)
PathString & moveTo(Geom::Coord x, Geom::Coord y)
PathString & lineTo(Geom::Coord x, Geom::Coord y)
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Interface for refcounted XML nodes.
virtual Node * parent()=0
Get the parent of this node.
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
virtual char const * name() const =0
Get the name of the element node.
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Change an attribute of this node.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
virtual Node * firstChild()=0
Get the first child of this node.
bool setAttributeCssDouble(Util::const_char_ptr key, double val)
Set a property attribute to val [slightly rounded], in the format required for CSS properties: in par...
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
void removeAttribute(Inkscape::Util::const_char_ptr key)
Remove an attribute of this node.
virtual void removeChild(Node *child)=0
Remove a child of this node.
virtual unsigned childCount() const =0
Get the number of children of this node.
bool setAttributeSvgDouble(Util::const_char_ptr key, double val)
For attributes where an exponent is allowed.
virtual Node * lastChild()=0
Get the last child of this node.
bool getAttributeBoolean(Util::const_char_ptr key, bool default_value=false) const
Parses the boolean value of an attribute "key" in repr and sets val accordingly, or to false if the a...
PDF parsing module using libpoppler's facilities.
void parse(Object *obj, GBool topLevel=gTrue)
Typed SVG document implementation.
SPObject * getObjectById(std::string const &id) const
Inkscape::XML::Node * getReprRoot()
SPDefs * getDefs()
Return the main defs object for the document.
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
int ensureUpToDate(unsigned int object_modified_tag=0)
Repeatedly works on getting the document updated, since sometimes it takes more than one pass to get ...
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
SPNamedView * getNamedView()
Get the namedview for this document, creates it if it's not found.
Inkscape::Colors::DocumentCMS & getDocumentCMS()
Geom::OptRect visualBounds(Geom::Affine const &transform=Geom::identity(), bool wfilter=true, bool wclip=true, bool wmask=true) const
Get item's visual bounding box in this item's coordinate system.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
constexpr uint32_t SP_COLOR_F_TO_U(double v)
TODO: insert short description here.
std::shared_ptr< Css const > css
std::vector< double > dash_pattern
static char const *const parent
struct _cairo_surface cairo_surface_t
static bool has_fill(SPObject *source)
static bool has_stroke(SPObject *source)
bool pathv_fully_contains(Geom::PathVector const &a, Geom::PathVector const &b)
Specific geometry functions for Inkscape, not provided my lib2geom.
Inkscape::XML::Node * node
void shift(T &a, T &b, T const &c)
Affine identity()
Create an identity matrix.
void png_write_vector(png_structp png_ptr, png_bytep data, png_size_t length)
Helper functions for supporting direct PNG output into a base64 encoded stream.
static gchar * svgConvertRGBToText(double r, double g, double b)
static std::string svgConvertGfxRGB(GfxRGB *color)
static void svgSetTransform(Inkscape::XML::Node *node, Geom::Affine matrix)
static bool svgGetShadingColor(GfxShading *shading, double offset, GfxColor *result)
static gchar * svgInterpretPath(_POPPLER_CONST_83 GfxPath *path)
Generates a SVG path string from poppler's data structure.
void save(Extension *key, SPDocument *doc, gchar const *filename, bool check_overwrite, bool official, Inkscape::Extension::FileSaveMethod save_method)
This is a generic function to use the save function of a module (including Autodetect)
int clamp(int const val)
Clamps an integer value to a value between 0 and 255.
static R & anchor(R &r)
Increments the reference count of a anchored object.
static R & release(R &r)
Decrements the reference count of a anchored object.
Helper class to stream background task notifications as a series of messages.
static T clip(T const &v, T const &a, T const &b)
Definition of functions needed by several filters.
static cairo_user_data_key_t key
Inkscape::SVG::PathString - builder for SVG path strings.
PDF parsing using libpoppler.
PDF Parsing utility functions and classes.
Geom::Affine stateToAffine(GfxState *state)
Get the default transformation state from the GfxState.
std::string sanitizeId(std::string const &in)
Convert arbitrary string (e.g.
std::string validateString(std::string const &in)
Ensure string is valid UTF8.
Geom::Affine ctmToAffine(const double *ctm)
Convert a transformation matrix to a lib2geom affine object.
std::shared_ptr< std::map< FontPtr, FontData > > FontList
Authors: see git history.
unsigned int rdf_set_work_entity(SPDocument *doc, struct rdf_work_entity_t *entity, const gchar *text)
struct rdf_work_entity_t * rdf_find_entity(gchar const *name)
Retrieves a known RDF/Work entity by name.
SPCSSAttr * sp_repr_css_attr_new()
Creates an empty SPCSSAttr (a class for manipulating CSS style properties).
void sp_repr_css_write_string(SPCSSAttr *css, Glib::ustring &str)
Write a style attribute string from a list of properties stored in an SPCSAttr object.
void sp_repr_css_merge(SPCSSAttr *dst, SPCSSAttr *src)
Merges two SPCSSAttr's.
void sp_repr_css_change(Node *repr, SPCSSAttr *css, gchar const *attr)
Creates a new SPCSAttr with the values filled from a repr, merges in properties from the given SPCSAt...
void sp_repr_css_set_property_double(SPCSSAttr *css, gchar const *name, double value)
Set a style property to a new float value (e.g.
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
SPCSSAttr * sp_repr_css_attr(Node const *repr, gchar const *attr)
Creates a new SPCSSAttr with one attribute (i.e.
char const * sp_repr_css_property(SPCSSAttr *css, gchar const *name, gchar const *defval)
Returns a character string of the value of a given style property or a default value if the attribute...
void sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
Set a style property to a new value (e.g.
C facade to Inkscape::XML::Node.
SPCSSAttr - interface for CSS Attributes.
void invert(const double v[16], double alpha[16])
Holds information about glyphs added by PdfParser which haven't been added to the document yet.
std::shared_ptr< CairoFont > cairo_font
std::string font_specification
Geom::Point text_position
virtual Node * createTextNode(char const *content)=0
virtual Node * createElement(char const *name)=0
static SPStyleEnum const enum_blend_mode[]
bool sp_svg_transform_read(gchar const *str, Geom::Affine *transform)
std::string sp_svg_transform_write(Geom::Affine const &transform)
Geom::PathVector sp_svg_read_pathv(char const *str)
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)
Interface for XML documents.