Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
metafile-print.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
5 * Authors:
6 * Krzysztof KosiƄski <tweenk.pl@gmail.com>
7 *
8 * Copyright (C) 2013 Authors
9 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
10 */
11
12#include <cstring>
13#include <fstream>
14#include <glib.h>
15#include <glibmm/miscutils.h>
16#include <2geom/rect.h>
17#include <2geom/curves.h>
19
21#include "extension/print.h"
22#include "path-prefix.h"
23#include "object/sp-gradient.h"
24#include "object/sp-image.h"
26#include "object/sp-pattern.h"
28#include "style.h"
29
30namespace Inkscape {
31namespace Extension {
32namespace Internal {
33
34U_COLORREF toColorRef(std::optional<Colors::Color> color)
35{
36 // Make sure it's RGB first, if wmf can support other color formats, this can be changed.
37 auto c = color ? *color->converted(Colors::Space::Type::RGB) : Colors::Color(0x000000ff);
38 return U_RGBA(255 * c[0], 255 * c[1], 255 * c[2], 255 * c[3]);
39}
40
41
43
44static std::map<Glib::ustring, FontfixParams> const &get_ppt_fixable_fonts()
45{
46 static std::map<Glib::ustring, FontfixParams> _ppt_fixable_fonts;
47
48 if (_ppt_fixable_fonts.empty()) {
49 _ppt_fixable_fonts = {
50 // clang-format off
51 {{"Arial"}, { 0.05, -0.055, -0.065}},
52 {{"Times New Roman"}, { 0.05, -0.055, -0.065}},
53 {{"Lucida Sans"}, {-0.025, -0.055, -0.065}},
54 {{"Sans"}, { 0.05, -0.055, -0.065}},
55 {{"Microsoft Sans Serif"}, {-0.05, -0.055, -0.065}},
56 {{"Serif"}, { 0.05, -0.055, -0.065}},
57 {{"Garamond"}, { 0.05, -0.055, -0.065}},
58 {{"Century Schoolbook"}, { 0.25, 0.025, 0.025}},
59 {{"Verdana"}, { 0.025, 0.0, 0.0}},
60 {{"Tahoma"}, { 0.045, 0.025, 0.025}},
61 {{"Symbol"}, { 0.025, 0.0, 0.0}},
62 {{"Wingdings"}, { 0.05, 0.0, 0.0}},
63 {{"Zapf Dingbats"}, { 0.025, 0.0, 0.0}},
64 {{"Convert To Symbol"}, { 0.025, 0.0, 0.0}},
65 {{"Convert To Wingdings"}, { 0.05, 0.0, 0.0}},
66 {{"Convert To Zapf Dingbats"}, { 0.025, 0.0, 0.0}},
67 {{"Sylfaen"}, { 0.1, 0.0, 0.0}},
68 {{"Palatino Linotype"}, { 0.175, 0.125, 0.125}},
69 {{"Segoe UI"}, { 0.1, 0.0, 0.0}},
70 // clang-format on
71 };
72 }
73 return _ppt_fixable_fonts;
74}
75
76
78{
79 return ext->get_param_bool("textToPath");
80}
81
82unsigned int PrintMetafile::bind(Inkscape::Extension::Print * /*mod*/, Geom::Affine const &transform, float /*opacity*/)
83{
84 if (!m_tr_stack.empty()) {
85 Geom::Affine tr_top = m_tr_stack.top();
86 m_tr_stack.push(transform * tr_top);
87 } else {
88 m_tr_stack.push(transform);
89 }
90
91 return 1;
92}
93
95{
96 m_tr_stack.pop();
97 return 1;
98}
99
100// Finds font fix parameters for the given fontname.
101void PrintMetafile::_lookup_ppt_fontfix(Glib::ustring const &fontname, FontfixParams &params)
102{
103 auto const &fixable_fonts = get_ppt_fixable_fonts();
104 auto it = fixable_fonts.find(fontname);
105 if (it != fixable_fonts.end()) {
106 params = it->second;
107 }
108}
109
111{
112 U_COLORREF out;
113 out = U_RGB(
114 (color >> 16) & 0xFF,
115 (color >> 8) & 0xFF,
116 (color >> 0) & 0xFF
117 );
118 return out;
119}
120
121// Translate Inkscape weights to EMF weights.
122uint32_t PrintMetafile::_translate_weight(unsigned inkweight)
123{
124 switch (inkweight) {
125 // 400 is tested first, as it is the most common case
126 case SP_CSS_FONT_WEIGHT_400: return U_FW_NORMAL;
127 case SP_CSS_FONT_WEIGHT_100: return U_FW_THIN;
128 case SP_CSS_FONT_WEIGHT_200: return U_FW_EXTRALIGHT;
129 case SP_CSS_FONT_WEIGHT_300: return U_FW_LIGHT;
130 case SP_CSS_FONT_WEIGHT_500: return U_FW_MEDIUM;
131 case SP_CSS_FONT_WEIGHT_600: return U_FW_SEMIBOLD;
132 case SP_CSS_FONT_WEIGHT_700: return U_FW_BOLD;
133 case SP_CSS_FONT_WEIGHT_800: return U_FW_EXTRABOLD;
134 case SP_CSS_FONT_WEIGHT_900: return U_FW_HEAVY;
135 default: return U_FW_NORMAL;
136 }
137}
138
139/* opacity weighting of two colors as float. v1 is the color, op is its opacity, v2 is the background color */
140inline float opweight(float v1, float v2, float op)
141{
142 return v1 * op + v2 * (1.0 - op);
143}
144
146{
147 U_COLORREF cr;
148 int last = gr->vector.stops.size() - 1;
149 if (last >= 1) {
150 auto rgbs = *gr->vector.stops[0 ].color->converted(Colors::Space::Type::RGB);
151 auto rgbe = *gr->vector.stops[last].color->converted(Colors::Space::Type::RGB);
152
153 /* Replace opacity at start & stop with that fraction background color, then average those two for final color. */
154 cr = U_RGB(
155 255 * ((opweight(rgbs[0], gv.rgb[0], rgbs[3]) + opweight(rgbe[0], gv.rgb[0], rgbe[3])) / 2.0),
156 255 * ((opweight(rgbs[1], gv.rgb[1], rgbs[3]) + opweight(rgbe[1], gv.rgb[1], rgbe[3])) / 2.0),
157 255 * ((opweight(rgbs[2], gv.rgb[2], rgbs[3]) + opweight(rgbe[2], gv.rgb[2], rgbe[3])) / 2.0)
158 );
159 } else {
160 cr = U_RGB(0, 0, 0); // The default fill
161 }
162 return cr;
163}
164
166{
167 float opa = c1.Reserved / 255.0;
168 U_COLORREF result = U_RGB(
169 255 * opweight((float)c1.Red / 255.0, gv.rgb[0], opa),
170 255 * opweight((float)c1.Green / 255.0, gv.rgb[1], opa),
171 255 * opweight((float)c1.Blue / 255.0, gv.rgb[2], opa)
172 );
173 return result;
174}
175
176/* t between 0 and 1, values outside that range use the nearest limit */
178{
179#define clrweight(a,b,t) ((1-t)*((double) a) + (t)*((double) b))
181 t = ( t > 1.0 ? 1.0 : ( t < 0.0 ? 0.0 : t));
182 // clang-format off
183 result.Red = clrweight(c1.Red, c2.Red, t);
184 result.Green = clrweight(c1.Green, c2.Green, t);
185 result.Blue = clrweight(c1.Blue, c2.Blue, t);
186 result.Reserved = clrweight(c1.Reserved, c2.Reserved, t);
187 // clang-format on
188
189 // now handle the opacity, mix the RGB with background at the weighted opacity
190
191 if (result.Reserved != 255) {
193 }
194
195 return result;
196}
197
198// Extract hatchType, hatchColor from a name like
199// EMFhatch<hatchType>_<hatchColor>
200// Where the first one is a number and the second a color in hex.
201// hatchType and hatchColor have been set with defaults before this is called.
202//
203void PrintMetafile::hatch_classify(char *name, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor)
204{
205 int val;
206 uint32_t hcolor = 0;
207 uint32_t bcolor = 0;
208
209 // name should be EMFhatch or WMFhatch but *MFhatch will be accepted
210 if (0 != strncmp(&name[1], "MFhatch", 7)) {
211 return; // not anything we can parse
212 }
213 name += 8; // EMFhatch already detected
214 val = 0;
215 while (*name && isdigit(*name)) {
216 val = 10 * val + *name - '0';
217 name++;
218 }
219 *hatchType = val;
220 if (*name != '_' || val > U_HS_DITHEREDBKCLR) { // wrong syntax, cannot classify
221 *hatchType = -1;
222 } else {
223 name++;
224 if (2 != sscanf(name, "%X_%X", &hcolor, &bcolor)) { // not a pattern with background
225 if (1 != sscanf(name, "%X", &hcolor)) {
226 *hatchType = -1; // not a pattern, cannot classify
227 }
228 *hatchColor = _gethexcolor(hcolor);
229 } else {
230 *hatchColor = _gethexcolor(hcolor);
231 *bkColor = _gethexcolor(bcolor);
232 usebk = true;
233 }
234 }
235 /* Everything > U_HS_SOLIDCLR is solid, just specify the color in the brush rather than messing around with background or textcolor */
236 if (*hatchType > U_HS_SOLIDCLR) {
237 *hatchType = U_HS_SOLIDCLR;
238 }
239}
240
241//
242// Recurse down from a brush pattern, try to figure out what it is.
243// If an image is found set a pointer to the epixbuf, else set that to NULL
244// If a pattern is found with a name like [EW]MFhatch3_3F7FFF return hatchType=3, hatchColor=3F7FFF (as a uint32_t),
245// otherwise hatchType is set to -1 and hatchColor is not defined.
246//
247
248void PrintMetafile::brush_classify(SPObject *parent, int depth, Inkscape::Pixbuf const **epixbuf, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor)
249{
250 if (depth == 0) {
251 *epixbuf = nullptr;
252 *hatchType = -1;
253 *hatchColor = U_RGB(0, 0, 0);
254 *bkColor = U_RGB(255, 255, 255);
255 }
256 depth++;
257 // first look along the pattern chain, if there is one
258 if (is<SPPattern>(parent)) {
259 for (auto pat_i = cast_unsafe<SPPattern>(parent); pat_i; pat_i = pat_i->ref.getObject()) {
260 char temp[32]; // large enough
261 strncpy(temp, pat_i->getAttribute("id"), sizeof(temp)-1); // Some names may be longer than [EW]MFhatch#_######
262 temp[sizeof(temp)-1] = '\0';
263 hatch_classify(temp, hatchType, hatchColor, bkColor);
264 if (*hatchType != -1) {
265 return;
266 }
267
268 // still looking? Look at this pattern's children, if there are any
269 for (auto& child: pat_i->children) {
270 if (*epixbuf || *hatchType != -1) {
271 break;
272 }
273 brush_classify(&child, depth, epixbuf, hatchType, hatchColor, bkColor);
274 }
275 }
276 } else if (auto img = cast<SPImage>(parent)) {
277 *epixbuf = img->pixbuf.get();
278 return;
279 } else { // some inkscape rearrangements pass through nodes between pattern and image which are not classified as either.
280 for (auto& child: parent->children) {
281 if (*epixbuf || *hatchType != -1) {
282 break;
283 }
284 brush_classify(&child, depth, epixbuf, hatchType, hatchColor, bkColor);
285 }
286 }
287}
288
289//swap R/B in 4 byte pixel
290void PrintMetafile::swapRBinRGBA(char *px, int pixels)
291{
292 char tmp;
293 for (int i = 0; i < pixels * 4; px += 4, i += 4) {
294 tmp = px[2];
295 px[2] = px[0];
296 px[0] = tmp;
297 }
298}
299
301{
302 gv.mode = mode;
303 gv.grad = gr;
304 if (mode == DRAW_RADIAL_GRADIENT) {
306 gv.r = rg->r.computed; // radius, but of what???
307 gv.p1 = Geom::Point(rg->cx.computed, rg->cy.computed); // center
308 gv.p2 = Geom::Point(gv.r, 0) + gv.p1; // xhandle
309 gv.p3 = Geom::Point(0, -gv.r) + gv.p1; // yhandle
310 if (rg->gradientTransform_set) {
311 gv.p1 = gv.p1 * rg->gradientTransform;
312 gv.p2 = gv.p2 * rg->gradientTransform;
313 gv.p3 = gv.p3 * rg->gradientTransform;
314 }
315 } else if (mode == DRAW_LINEAR_GRADIENT) {
317 gv.r = 0; // unused
318 gv.p1 = Geom::Point(lg->x1.computed, lg->y1.computed); // start
319 gv.p2 = Geom::Point(lg->x2.computed, lg->y2.computed); // end
320 gv.p3 = Geom::Point(0, 0); // unused
321 if (lg->gradientTransform_set) {
322 gv.p1 = gv.p1 * lg->gradientTransform;
323 gv.p2 = gv.p2 * lg->gradientTransform;
324 }
325 } else {
326 g_error("Fatal programming error, hold_gradient() in metafile-print.cpp called with invalid draw mode");
327 }
328 return 1;
329}
330
331/* convert from center ellipse to SVGEllipticalArc ellipse
332
333 From:
334 http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
335 A point (x,y) on the arc can be found by:
336
337 {x,y} = {cx,cy} + {cosF,-sinF,sinF,cosF} x {rxcosT,rysinT}
338
339 where
340 {cx,cy} is the center of the ellipse
341 F is the rotation angle of the X axis of the ellipse from the true X axis
342 T is the rotation angle around the ellipse
343 {,,,} is the rotation matrix
344 rx,ry are the radii of the ellipse's axes
345
346 For SVG parameterization need two points.
347 Arbitrarily we can use T=0 and T=pi
348 Since the sweep is 180 the flags are always 0:
349
350 F is in RADIANS, but the SVGEllipticalArc needs degrees!
351
352*/
354{
355 using Geom::X;
356 using Geom::Y;
357 double x1, y1, x2, y2;
358 Geom::Path SVGep;
359
360 x1 = ctr[X] + cos(F) * rx * cos(0) + sin(-F) * ry * sin(0);
361 y1 = ctr[Y] + sin(F) * rx * cos(0) + cos(F) * ry * sin(0);
362 x2 = ctr[X] + cos(F) * rx * cos(M_PI) + sin(-F) * ry * sin(M_PI);
363 y2 = ctr[Y] + sin(F) * rx * cos(M_PI) + cos(F) * ry * sin(M_PI);
364
365 char text[256];
366 snprintf(text, 256, " M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z",
367 x1, y1, rx, ry, F * 360. / (2.*M_PI), x2, y2, rx, ry, F * 360. / (2.*M_PI), x1, y1);
369 return outres;
370}
371
372
373/* rx2,ry2 must be larger than rx1,ry1!
374 angle is in RADIANS
375*/
376Geom::PathVector PrintMetafile::center_elliptical_ring_as_SVG_PathV(Geom::Point ctr, double rx1, double ry1, double rx2, double ry2, double F)
377{
378 using Geom::X;
379 using Geom::Y;
380 double x11, y11, x12, y12;
381 double x21, y21, x22, y22;
382 double degrot = F * 360. / (2.*M_PI);
383
384 x11 = ctr[X] + cos(F) * rx1 * cos(0) + sin(-F) * ry1 * sin(0);
385 y11 = ctr[Y] + sin(F) * rx1 * cos(0) + cos(F) * ry1 * sin(0);
386 x12 = ctr[X] + cos(F) * rx1 * cos(M_PI) + sin(-F) * ry1 * sin(M_PI);
387 y12 = ctr[Y] + sin(F) * rx1 * cos(M_PI) + cos(F) * ry1 * sin(M_PI);
388
389 x21 = ctr[X] + cos(F) * rx2 * cos(0) + sin(-F) * ry2 * sin(0);
390 y21 = ctr[Y] + sin(F) * rx2 * cos(0) + cos(F) * ry2 * sin(0);
391 x22 = ctr[X] + cos(F) * rx2 * cos(M_PI) + sin(-F) * ry2 * sin(M_PI);
392 y22 = ctr[Y] + sin(F) * rx2 * cos(M_PI) + cos(F) * ry2 * sin(M_PI);
393
394 char text[512];
395 snprintf(text, 512, " M %f,%f A %f %f %f 0 1 %f %f A %f %f %f 0 1 %f %f z M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z",
396 x11, y11, rx1, ry1, degrot, x12, y12, rx1, ry1, degrot, x11, y11,
397 x21, y21, rx2, ry2, degrot, x22, y22, rx2, ry2, degrot, x21, y21);
399
400 return outres;
401}
402
403/* Elliptical hole in a large square extending from -50k to +50k */
405{
406 using Geom::X;
407 using Geom::Y;
408 double x1, y1, x2, y2;
409 Geom::Path SVGep;
410
411 x1 = ctr[X] + cos(F) * rx * cos(0) + sin(-F) * ry * sin(0);
412 y1 = ctr[Y] + sin(F) * rx * cos(0) + cos(F) * ry * sin(0);
413 x2 = ctr[X] + cos(F) * rx * cos(M_PI) + sin(-F) * ry * sin(M_PI);
414 y2 = ctr[Y] + sin(F) * rx * cos(M_PI) + cos(F) * ry * sin(M_PI);
415
416 char text[256];
417 snprintf(text, 256, " M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z M 50000,50000 50000,-50000 -50000,-50000 -50000,50000 z",
418 x1, y1, rx, ry, F * 360. / (2.*M_PI), x2, y2, rx, ry, F * 360. / (2.*M_PI), x1, y1);
420 return outres;
421}
422
423/* rectangular cutter.
424ctr "center" of rectangle (might not actually be in the center with respect to leading/trailing edges
425pos vector from center to leading edge
426neg vector from center to trailing edge
427width vector to side edge
428*/
430{
431 Geom::PathVector outres;
432 Geom::Path cutter;
433 cutter.start(ctr + pos - width);
434 cutter.appendNew<Geom::LineSegment>(ctr + pos + width);
435 cutter.appendNew<Geom::LineSegment>(ctr + neg + width);
436 cutter.appendNew<Geom::LineSegment>(ctr + neg - width);
437 cutter.close();
438 outres.push_back(cutter);
439 return outres;
440}
441
442/* Convert from SPWindRule to livarot's FillRule
443 This is similar to what sp_selected_path_boolop() does
444*/
446{
447 FillRule fr;
448 if (wr == SP_WIND_RULE_EVENODD) {
449 fr = fill_oddEven;
450 } else {
451 fr = fill_nonZero;
452 }
453 return fr;
454}
455
456} // namespace Internal
457} // namespace Extension
458} // namespace Inkscape
459
460/*
461 Local Variables:
462 mode:c++
463 c-file-style:"stroustrup"
464 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
465 indent-tabs-mode:nil
466 fill-column:99
467 End:
468*/
469// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
FillRule
Definition LivarotDefs.h:67
@ fill_oddEven
Definition LivarotDefs.h:68
@ fill_nonZero
Definition LivarotDefs.h:69
3x3 matrix representing an affine transformation.
Definition affine.h:70
Sequence of subpaths.
Definition pathvector.h:122
void push_back(Path const &path)
Append a path at the end.
Definition pathvector.h:172
Sequence of contiguous curves, aka spline.
Definition path.h:353
void close(bool closed=true)
Set whether the path is closed.
Definition path.cpp:322
void appendNew(Args &&... args)
Append a new curve to the path.
Definition path.h:804
void start(Point const &p)
Definition path.cpp:426
Two-dimensional point that doubles as a vector.
Definition point.h:66
bool get_param_bool(char const *name) const
Gets a parameter identified by name with the bool placed in value.
virtual unsigned text(Inkscape::Extension::Print *, char const *, Geom::Point const &, SPStyle const *)
void brush_classify(SPObject *parent, int depth, Inkscape::Pixbuf const **epixbuf, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor)
static void swapRBinRGBA(char *px, int pixels)
bool textToPath(Inkscape::Extension::Print *ext) override
Tell the printing engine whether text should be text or path.
static Geom::PathVector rect_cutter(Geom::Point ctr, Geom::Point pos, Geom::Point neg, Geom::Point width)
static U_COLORREF _gethexcolor(uint32_t color)
unsigned int release(Inkscape::Extension::Print *module) override
static uint32_t _translate_weight(unsigned inkweight)
static void _lookup_ppt_fontfix(Glib::ustring const &fontname, FontfixParams &)
static Geom::PathVector center_elliptical_ring_as_SVG_PathV(Geom::Point ctr, double rx1, double ry1, double rx2, double ry2, double F)
unsigned int bind(Inkscape::Extension::Print *module, Geom::Affine const &transform, float opacity) override
static FillRule SPWR_to_LVFR(SPWindRule wr)
U_COLORREF weight_colors(U_COLORREF c1, U_COLORREF c2, double t)
void hatch_classify(char *name, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor)
static Geom::PathVector center_elliptical_hole_as_SVG_PathV(Geom::Point ctr, double rx, double ry, double F)
static Geom::PathVector center_ellipse_as_SVG_PathV(Geom::Point ctr, double rx, double ry, double F)
Class to hold image data for raster images.
Definition cairo-utils.h:31
Gradient.
Definition sp-gradient.h:86
SPGradientVector vector
Linear and Radial Gradients.
Geom::Affine gradientTransform
gradientTransform attribute
Definition sp-gradient.h:99
unsigned int gradientTransform_set
Linear gradient.
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
Radial gradient.
float computed
Definition svg-length.h:50
Css & result
Include all curve types.
double c[8][4]
static char const *const parent
Definition dir-util.cpp:70
void parse_svg_path(char const *str, PathSink &sink)
Feed SVG path data to the specified sink.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Metafile printing - common functions.
float opweight(float v1, float v2, float op)
U_COLORREF toColorRef(std::optional< Colors::Color > color)
static std::map< Glib::ustring, FontfixParams > const & get_ppt_fixable_fonts()
Helper class to stream background task notifications as a series of messages.
int mode
TODO: insert short description here.
Ocnode * child[8]
Definition quantize.cpp:33
Axis-aligned rectangle.
SVG <image> implementation.
TODO: insert short description here.
SVG <pattern> implementation.
TODO: insert short description here.
std::vector< SPGradientStop > stops
WMF manual 2.2.2.8.
Definition uemf.h:474
uint8_t Red
Red color (0-255)
Definition uemf.h:475
uint8_t Reserved
Not used.
Definition uemf.h:478
uint8_t Blue
Blue color (0-255)
Definition uemf.h:477
uint8_t Green
Green color (0-255)
Definition uemf.h:476
SPWindRule
Definition style-enums.h:23
@ SP_WIND_RULE_EVENODD
Definition style-enums.h:26
@ SP_CSS_FONT_WEIGHT_400
Definition style-enums.h:75
@ SP_CSS_FONT_WEIGHT_300
Definition style-enums.h:74
@ SP_CSS_FONT_WEIGHT_100
Definition style-enums.h:72
@ SP_CSS_FONT_WEIGHT_200
Definition style-enums.h:73
@ SP_CSS_FONT_WEIGHT_900
Definition style-enums.h:80
@ SP_CSS_FONT_WEIGHT_700
Definition style-enums.h:78
@ SP_CSS_FONT_WEIGHT_800
Definition style-enums.h:79
@ SP_CSS_FONT_WEIGHT_500
Definition style-enums.h:76
@ SP_CSS_FONT_WEIGHT_600
Definition style-enums.h:77
SPStyle - a style object for SPItem objects.
parse SVG path specifications
double width
Glib::ustring name
Definition toolbars.cpp:55