Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
emf-print.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
5 * Authors:
6 * Ulf Erikson <ulferikson@users.sf.net>
7 * Jon A. Cruz <jon@joncruz.org>
8 * Abhishek Sharma
9 * David Mathog
10 *
11 * Copyright (C) 2006-2009 Authors
12 *
13 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14 *
15 * References:
16 * - How to Create & Play Enhanced Metafiles in Win32
17 * http://support.microsoft.com/kb/q145999/
18 * - INFO: Windows Metafile Functions & Aldus Placeable Metafiles
19 * http://support.microsoft.com/kb/q66949/
20 * - Metafile Functions
21 * http://msdn.microsoft.com/library/en-us/gdi/metafile_0whf.asp
22 * - Metafile Structures
23 * http://msdn.microsoft.com/library/en-us/gdi/metafile_5hkj.asp
24 */
25
26#include "emf-print.h"
27
28#include <cstring>
29#include <glibmm/miscutils.h>
32#include <2geom/path.h>
33#include <2geom/pathvector.h>
34#include <2geom/rect.h>
35#include <2geom/curves.h>
36#include <2geom/svg-path-parser.h> // to get from SVG text to Geom::Path
37
38#include "inkscape-version.h"
39
40#include "document.h"
41#include "path-prefix.h"
42#include "style.h"
43#include "style-enums.h" // Fill rules
44
45#include "display/cairo-utils.h" // for Inkscape::Pixbuf::PF_CAIRO
46#include "display/curve.h"
47
48#include "extension/system.h"
49#include "extension/print.h"
50
51#include "helper/geom.h"
52#include "helper/geom-curves.h"
53
54#include "object/sp-pattern.h"
55#include "object/sp-image.h"
56#include "object/sp-gradient.h"
59#include "object/sp-item.h"
60#include "object/sp-root.h"
61#include "object/sp-shape.h"
62#include "object/sp-clippath.h"
63
64#include "path/path-boolop.h"
65
66#include "util/units.h"
67
68using namespace Inkscape::Colors;
69
70namespace Inkscape {
71namespace Extension {
72namespace Internal {
73
74#define PXPERMETER 2835
75
76/* globals */
77static double PX2WORLD;
79static EMFTRACK *et = nullptr;
80static EMFHANDLES *eht = nullptr;
81
82void PrintEmf::smuggle_adxkyrtl_out(const char *string, uint32_t **adx, double *ky, int *rtl, int *ndx, float scale)
83{
84 float fdx;
85 int i;
86 uint32_t *ladx;
87 const char *cptr = &string[strlen(string) + 1]; // this works because of the first fake terminator
88
89 *adx = nullptr;
90 *ky = 0.0; // set a default value
91 sscanf(cptr, "%7d", ndx);
92 if (!*ndx) {
93 return; // this could happen with an empty string
94 }
95 cptr += 7;
96 ladx = (uint32_t *) malloc(*ndx * sizeof(uint32_t));
97 if (!ladx) {
98 g_message("Out of memory");
99 }
100 *adx = ladx;
101 for (i = 0; i < *ndx; i++, cptr += 7, ladx++) {
102 sscanf(cptr, "%7f", &fdx);
103 *ladx = (uint32_t) round(fdx * scale);
104 }
105 cptr++; // skip 2nd fake terminator
106 sscanf(cptr, "%7f", &fdx);
107 *ky = fdx;
108 cptr += 7; // advance over ky and its space
109 sscanf(cptr, "%07d", rtl);
110}
111
113{
114 // all of the class variables are initialized elsewhere, many in PrintEmf::Begin,
115}
116
117
119{
120 return TRUE;
121}
122
123
125{
126 U_SIZEL szlDev, szlMm;
127 U_RECTL rclBounds, rclFrame;
128 char *rec;
129 gchar const *utf8_fn = mod->get_param_string("destination");
130
131 // Typically PX2WORLD is 1200/90, using inkscape's default dpi
132 PX2WORLD = 1200.0 / Inkscape::Util::Quantity::convert(1.0, "in", "px");
133 FixPPTCharPos = mod->get_param_bool("FixPPTCharPos");
134 FixPPTDashLine = mod->get_param_bool("FixPPTDashLine");
135 FixPPTGrad2Polys = mod->get_param_bool("FixPPTGrad2Polys");
136 FixPPTLinGrad = mod->get_param_bool("FixPPTLinGrad");
137 FixPPTPatternAsHatch = mod->get_param_bool("FixPPTPatternAsHatch");
138 FixImageRot = mod->get_param_bool("FixImageRot");
139
140 (void) emf_start(utf8_fn, 1000000, 250000, &et); // Initialize the et structure
141 (void) htable_create(128, 128, &eht); // Initialize the eht structure
142
143 char *ansi_uri = (char *) utf8_fn;
144
145 // width and height in px
147
148 // initialize a few global variables
149 hbrush = hbrushOld = hpen = 0;
150 htextalignment = U_TA_BASELINE | U_TA_LEFT;
152
154 if (nv) {
155 const char *p1 = nv->attribute("pagecolor");
156 char *p2;
157 uint32_t lc = strtoul(&p1[1], &p2, 16); // it looks like "#ABC123"
158 if (*p2) {
159 lc = 0;
160 }
161 gv.bgc = _gethexcolor(lc);
162 gv.rgb[0] = (float) U_RGBAGetR(gv.bgc) / 255.0;
163 gv.rgb[1] = (float) U_RGBAGetG(gv.bgc) / 255.0;
164 gv.rgb[2] = (float) U_RGBAGetB(gv.bgc) / 255.0;
165 }
166
167 bool pageBoundingBox;
168 pageBoundingBox = mod->get_param_bool("pageBoundingBox");
169
170 Geom::Rect d;
171 if (pageBoundingBox) {
172 d = *(doc->preferredBounds());
173 } else {
174 SPItem *doc_item = doc->getRoot();
175 Geom::OptRect bbox = doc_item->desktopVisualBounds();
176 if (bbox) {
177 d = *bbox;
178 }
179 }
180
182
183 float dwInchesX = d.width();
184 float dwInchesY = d.height();
185
186 // dwInchesX x dwInchesY in micrometer units, 1200 dpi/25.4 -> dpmm
187 (void) drawing_size((int) ceil(dwInchesX * 25.4), (int) ceil(dwInchesY * 25.4),1200.0/25.4, &rclBounds, &rclFrame);
188
189 // set up the reference device as 100 X A4 horizontal, (1200 dpi/25.4 -> dpmm). Extra digits maintain dpi better in EMF
190 int MMX = 216;
191 int MMY = 279;
192 (void) device_size(MMX, MMY, 1200.0 / 25.4, &szlDev, &szlMm);
193 int PixelsX = szlDev.cx;
194 int PixelsY = szlDev.cy;
195
196 // set up the description: (version string)0(file)00
197 char buff[1024];
198 memset(buff, 0, sizeof(buff));
199 char *p1 = strrchr(ansi_uri, '\\');
200 char *p2 = strrchr(ansi_uri, '/');
201 char *p = MAX(p1, p2);
202 if (p) {
203 p++;
204 } else {
205 p = ansi_uri;
206 }
207 snprintf(buff, sizeof(buff) - 1, "Inkscape %s \1%s\1", Inkscape::version_string, p);
208 uint16_t *Description = U_Utf8ToUtf16le(buff, 0, nullptr);
209 int cbDesc = 2 + wchar16len(Description); // also count the final terminator
210 (void) U_Utf16leEdit(Description, '\1', '\0'); // swap the temporary \1 characters for nulls
211
212 // construct the EMRHEADER record and append it to the EMF in memory
213 rec = U_EMRHEADER_set(rclBounds, rclFrame, nullptr, cbDesc, Description, szlDev, szlMm, 0);
214 free(Description);
215 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
216 g_error("Fatal programming error in PrintEmf::begin at EMRHEADER");
217 }
218
219
220 // Simplest mapping mode, supply all coordinates in pixels
221 rec = U_EMRSETMAPMODE_set(U_MM_TEXT);
222 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
223 g_error("Fatal programming error in PrintEmf::begin at EMRSETMAPMODE");
224 }
225
226
227 // In earlier versions this was used to scale from inkscape's dpi of 90 to
228 // the files 1200 dpi, taking into account PX2WORLD which was 20. Now PX2WORLD
229 // is set so that this matrix is unitary. The usual value of PX2WORLD is 1200/90,
230 // but might be different if the internal dpi is changed.
231
232 U_XFORM worldTransform;
233 worldTransform.eM11 = 1.0;
234 worldTransform.eM12 = 0.0;
235 worldTransform.eM21 = 0.0;
236 worldTransform.eM22 = 1.0;
237 worldTransform.eDx = 0;
238 worldTransform.eDy = 0;
239
240 rec = U_EMRMODIFYWORLDTRANSFORM_set(worldTransform, U_MWT_LEFTMULTIPLY);
241 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
242 g_error("Fatal programming error in PrintEmf::begin at EMRMODIFYWORLDTRANSFORM");
243 }
244
245
246 if (true) {
247 snprintf(buff, sizeof(buff) - 1, "Screen=%dx%dpx, %dx%dmm", PixelsX, PixelsY, MMX, MMY);
248 rec = textcomment_set(buff);
249 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
250 g_error("Fatal programming error in PrintEmf::begin at textcomment_set 1");
251 }
252
253 snprintf(buff, sizeof(buff) - 1, "Drawing=%.1fx%.1fpx, %.1fx%.1fmm", doc->preferredBounds()->width(),
254 doc->preferredBounds()->height(), Inkscape::Util::Quantity::convert(dwInchesX, "in", "mm"),
255 Inkscape::Util::Quantity::convert(dwInchesY, "in", "mm"));
256 rec = textcomment_set(buff);
257 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
258 g_error("Fatal programming error in PrintEmf::begin at textcomment_set 1");
259 }
260 }
261
262 /* set some parameters, else the program that reads the EMF may default to other values */
263
264 rec = U_EMRSETBKMODE_set(U_TRANSPARENT);
265 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
266 g_error("Fatal programming error in PrintEmf::begin at U_EMRSETBKMODE_set");
267 }
268
269 hpolyfillmode = U_WINDING;
270 rec = U_EMRSETPOLYFILLMODE_set(U_WINDING);
271 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
272 g_error("Fatal programming error in PrintEmf::begin at U_EMRSETPOLYFILLMODE_set");
273 }
274
275 // Text alignment: (only changed if RTL text is encountered )
276 // - (x,y) coordinates received by this filter are those of the point where the text
277 // actually starts, and already takes into account the text object's alignment;
278 // - for this reason, the EMF text alignment must always be TA_BASELINE|TA_LEFT.
279 htextalignment = U_TA_BASELINE | U_TA_LEFT;
280 rec = U_EMRSETTEXTALIGN_set(U_TA_BASELINE | U_TA_LEFT);
281 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
282 g_error("Fatal programming error in PrintEmf::begin at U_EMRSETTEXTALIGN_set");
283 }
284
285 htextcolor_rgb = Color(0x000000ff);
286 rec = U_EMRSETTEXTCOLOR_set(U_RGB(0, 0, 0));
287 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
288 g_error("Fatal programming error in PrintEmf::begin at U_EMRSETTEXTCOLOR_set");
289 }
290
291 rec = U_EMRSETROP2_set(U_R2_COPYPEN);
292 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
293 g_error("Fatal programming error in PrintEmf::begin at U_EMRSETROP2_set");
294 }
295
296 /* miterlimit is set with eah pen, so no need to check for it changes as in WMF */
297
298 return 0;
299}
300
301
303{
304 do_clip_if_present(nullptr); // Terminate any open clip.
305 char *rec;
306 if (!et) {
307 return 0;
308 }
309
310
311 // earlier versions had flush of fill here, but it never executed and was removed
312
313 rec = U_EMREOF_set(0, nullptr, et); // generate the EOF record
314 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
315 g_error("Fatal programming error in PrintEmf::finish");
316 }
317 (void) emf_finish(et, eht); // Finalize and write out the EMF
318 emf_free(&et); // clean up
319 htable_free(&eht); // clean up
320
321 return 0;
322}
323
324// fcolor is defined when gradients are being expanded, it is the color of one stripe or ring.
326{
327 char *rec;
328 U_LOGBRUSH lb;
329 uint32_t brush, fmode;
330 MFDrawMode fill_mode;
331 Inkscape::Pixbuf const *pixbuf;
332 uint32_t brushStyle;
333 int hatchType;
334 U_COLORREF hatchColor;
335 U_COLORREF bkColor;
336 uint32_t width = 0; // quiets a harmless compiler warning, initialization not otherwise required.
337 uint32_t height = 0;
338
339 if (!et) {
340 return 0;
341 }
342
343 // set a default fill in case we can't figure out a better way to do it
344 fmode = U_ALTERNATE;
345 fill_mode = DRAW_PAINT;
346 brushStyle = U_BS_SOLID;
347 hatchType = U_HS_SOLIDCLR;
348 bkColor = U_RGB(0, 0, 0);
349 if (fcolor) {
350 hatchColor = *fcolor;
351 } else {
352 hatchColor = U_RGB(0, 0, 0);
353 }
354
355 if (!fcolor && style) {
356 if (style->fill.isColor()) {
357 fill_mode = DRAW_PAINT;
358#if 0
359// opacity not supported by EMF
360 float opacity = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
361 if (opacity <= 0.0) {
362 opacity = 0.0; // basically the same as no fill
363 }
364#endif
365 auto rgb = *style->fill.getColor().converted(Space::Type::RGB);
366 hatchColor = U_RGB(255 * rgb[0], 255 * rgb[1], 255 * rgb[2]);
367
368 fmode = style->fill_rule.computed == 0 ? U_WINDING : (style->fill_rule.computed == 2 ? U_ALTERNATE : U_ALTERNATE);
369 } else if (is<SPPattern>(SP_STYLE_FILL_SERVER(style))) { // must be paint-server
370 SPPaintServer *paintserver = style->fill.href->getObject();
371 auto pat = cast<SPPattern>(paintserver);
372 double dwidth = pat->width();
373 double dheight = pat->height();
374 width = dwidth;
375 height = dheight;
376 brush_classify(pat, 0, &pixbuf, &hatchType, &hatchColor, &bkColor);
377 if (pixbuf) {
378 fill_mode = DRAW_IMAGE;
379 } else { // pattern
380 fill_mode = DRAW_PATTERN;
381 if (hatchType == -1) { // Not a standard hatch, so force it to something
382 hatchType = U_HS_CROSS;
383 hatchColor = U_RGB(0xFF, 0xC3, 0xC3);
384 }
385 }
387 if (hatchType == -1) { // image or unclassified
388 fill_mode = DRAW_PATTERN;
389 hatchType = U_HS_DIAGCROSS;
390 hatchColor = U_RGB(0xFF, 0xC3, 0xC3);
391 }
392 }
393 brushStyle = U_BS_HATCHED;
394 } else if (is<SPGradient>(SP_STYLE_FILL_SERVER(style))) { // must be a gradient
395 // currently we do not do anything with gradients, the code below just sets the color to the average of the stops
396 SPPaintServer *paintserver = style->fill.href->getObject();
397 SPLinearGradient *lg = nullptr;
398 SPRadialGradient *rg = nullptr;
399
400 if (is<SPLinearGradient>(paintserver)) {
401 lg = cast<SPLinearGradient>(paintserver);
402 lg->ensureVector(); // when exporting from commandline, vector is not built
403 fill_mode = DRAW_LINEAR_GRADIENT;
404 } else if (is<SPRadialGradient>(paintserver)) {
405 rg = cast<SPRadialGradient>(paintserver);
406 rg->ensureVector(); // when exporting from commandline, vector is not built
407 fill_mode = DRAW_RADIAL_GRADIENT;
408 } else {
409 // default fill
410 }
411
412 if (rg) {
413 if (FixPPTGrad2Polys) {
414 return hold_gradient(rg, fill_mode);
415 } else {
416 hatchColor = avg_stop_color(rg);
417 }
418 } else if (lg) {
420 return hold_gradient(lg, fill_mode);
421 } else {
422 hatchColor = avg_stop_color(lg);
423 }
424 }
425 }
426 } else { // if (!style)
427 // default fill
428 }
429
430 lb = logbrush_set(brushStyle, hatchColor, hatchType);
431
432 switch (fill_mode) {
433 case DRAW_LINEAR_GRADIENT: // fill with average color unless gradients are converted to slices
434 case DRAW_RADIAL_GRADIENT: // ditto
435 case DRAW_PAINT:
436 case DRAW_PATTERN:
437 // SVG text has no background attribute, so OPAQUE mode ALWAYS cancels after the next draw, otherwise it would mess up future text output.
438 if (usebk) {
439 rec = U_EMRSETBKCOLOR_set(bkColor);
440 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
441 g_error("Fatal programming error in PrintEmf::create_brush at U_EMRSETBKCOLOR_set");
442 }
443 rec = U_EMRSETBKMODE_set(U_OPAQUE);
444 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
445 g_error("Fatal programming error in PrintEmf::create_brush at U_EMRSETBKMODE_set");
446 }
447 }
448 rec = createbrushindirect_set(&brush, eht, lb);
449 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
450 g_error("Fatal programming error in PrintEmf::create_brush at createbrushindirect_set");
451 }
452 break;
453 case DRAW_IMAGE:
454 char *px;
455 char const *rgba_px;
456 uint32_t cbPx;
457 uint32_t colortype;
458 PU_RGBQUAD ct;
459 int numCt;
461 PU_BITMAPINFO Bmi;
462 rgba_px = (char const*) pixbuf->pixels(); // Do NOT free this!!!
463 colortype = U_BCBM_COLOR32;
464 (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1);
465 // pixbuf can be either PF_CAIRO or PF_GDK, and these have R and B bytes swapped
467 Bmih = bitmapinfoheader_set(width, height, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0);
468 Bmi = bitmapinfo_set(Bmih, ct);
469 rec = createdibpatternbrushpt_set(&brush, eht, U_DIB_RGB_COLORS, Bmi, cbPx, px);
470 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
471 g_error("Fatal programming error in PrintEmf::create_brush at createdibpatternbrushpt_set");
472 }
473 free(px);
474 free(Bmi); // ct will be NULL because of colortype
475 break;
476 }
477
478 hbrush = brush; // need this later for destroy_brush
479 rec = selectobject_set(brush, eht);
480 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
481 g_error("Fatal programming error in PrintEmf::create_brush at selectobject_set");
482 }
483
484 if (fmode != hpolyfillmode) {
485 hpolyfillmode = fmode;
486 rec = U_EMRSETPOLYFILLMODE_set(fmode);
487 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
488 g_error("Fatal programming error in PrintEmf::create_brush at U_EMRSETPOLYdrawmode_set");
489 }
490 }
491
492 return 0;
493}
494
495
497{
498 char *rec;
499 // before an object may be safely deleted it must no longer be selected
500 // select in a stock object to deselect this one, the stock object should
501 // never be used because we always select in a new one before drawing anythingrestore previous brush, necessary??? Would using a default stock object not work?
502 rec = selectobject_set(U_NULL_BRUSH, eht);
503 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
504 g_error("Fatal programming error in PrintEmf::destroy_brush at selectobject_set");
505 }
506 if (hbrush) {
507 rec = deleteobject_set(&hbrush, eht);
508 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
509 g_error("Fatal programming error in PrintEmf::destroy_brush");
510 }
511 hbrush = 0;
512 }
513}
514
515
516int PrintEmf::create_pen(SPStyle const *style, const Geom::Affine &transform)
517{
518 U_EXTLOGPEN *elp;
519 U_NUM_STYLEENTRY n_dash = 0;
520 U_STYLEENTRY *dash = nullptr;
521 char *rec = nullptr;
522 int linestyle = U_PS_SOLID;
523 int linecap = 0;
524 int linejoin = 0;
525 uint32_t pen;
526 uint32_t brushStyle;
527 Inkscape::Pixbuf const *pixbuf;
528 int hatchType;
529 U_COLORREF hatchColor;
530 U_COLORREF bkColor;
531 uint32_t width, height;
532 char *px = nullptr;
533 char *rgba_px;
534 uint32_t cbPx = 0;
535 uint32_t colortype;
536 PU_RGBQUAD ct = nullptr;
537 int numCt = 0;
539 PU_BITMAPINFO Bmi = nullptr;
540
541 if (!et) {
542 return 0;
543 }
544
545 // set a default stroke in case we can't figure out a better way to do it
546 brushStyle = U_BS_SOLID;
547 hatchColor = U_RGB(0, 0, 0);
548 hatchType = U_HS_HORIZONTAL;
549 bkColor = U_RGB(0, 0, 0);
550
551 if (style) {
552 if (is<SPPattern>(SP_STYLE_STROKE_SERVER(style))) { // must be paint-server
553 SPPaintServer *paintserver = style->stroke.href->getObject();
554 auto pat = cast<SPPattern>(paintserver);
555 double dwidth = pat->width();
556 double dheight = pat->height();
557 width = dwidth;
558 height = dheight;
559 brush_classify(pat, 0, &pixbuf, &hatchType, &hatchColor, &bkColor);
560 if (pixbuf) {
561 brushStyle = U_BS_DIBPATTERN;
562 rgba_px = (char *) pixbuf->pixels(); // Do NOT free this!!!
563 colortype = U_BCBM_COLOR32;
564 (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1);
565 // pixbuf can be either PF_CAIRO or PF_GDK, and these have R and B bytes swapped
567 Bmih = bitmapinfoheader_set(width, height, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0);
568 Bmi = bitmapinfo_set(Bmih, ct);
569 } else { // pattern
570 brushStyle = U_BS_HATCHED;
571 if (usebk) { // OPAQUE mode ALWAYS cancels after the next draw, otherwise it would mess up future text output.
572 rec = U_EMRSETBKCOLOR_set(bkColor);
573 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
574 g_error("Fatal programming error in PrintEmf::create_pen at U_EMRSETBKCOLOR_set");
575 }
576 rec = U_EMRSETBKMODE_set(U_OPAQUE);
577 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
578 g_error("Fatal programming error in PrintEmf::create_pen at U_EMRSETBKMODE_set");
579 }
580 }
581 if (hatchType == -1) { // Not a standard hatch, so force it to something
582 hatchType = U_HS_CROSS;
583 hatchColor = U_RGB(0xFF, 0xC3, 0xC3);
584 }
585 }
587 if (hatchType == -1) { // image or unclassified
588 brushStyle = U_BS_HATCHED;
589 hatchType = U_HS_DIAGCROSS;
590 hatchColor = U_RGB(0xFF, 0xC3, 0xC3);
591 }
592 }
593 } else if (is<SPGradient>(SP_STYLE_STROKE_SERVER(style))) { // must be a gradient
594 // currently we do not do anything with gradients, the code below has no net effect.
595
596 SPPaintServer *paintserver = style->stroke.href->getObject();
597 if (is<SPLinearGradient>(paintserver)) {
598 auto lg = cast<SPLinearGradient>(paintserver);
599
600 lg->ensureVector(); // when exporting from commandline, vector is not built
601
602 Geom::Point p1(lg->x1.computed, lg->y1.computed);
603 Geom::Point p2(lg->x2.computed, lg->y2.computed);
604
605 if (lg->gradientTransform_set) {
606 p1 = p1 * lg->gradientTransform;
607 p2 = p2 * lg->gradientTransform;
608 }
609 hatchColor = avg_stop_color(lg);
610 } else if (is<SPRadialGradient>(paintserver)) {
611 auto rg = cast<SPRadialGradient>(paintserver);
612
613 rg->ensureVector(); // when exporting from commandline, vector is not built
614 double r = rg->r.computed;
615
616 Geom::Point c(rg->cx.computed, rg->cy.computed);
617 Geom::Point xhandle_point(r, 0);
618 Geom::Point yhandle_point(0, -r);
619 yhandle_point += c;
620 xhandle_point += c;
621 if (rg->gradientTransform_set) {
622 c = c * rg->gradientTransform;
623 yhandle_point = yhandle_point * rg->gradientTransform;
624 xhandle_point = xhandle_point * rg->gradientTransform;
625 }
626 hatchColor = avg_stop_color(rg);
627 } else {
628 // default fill
629 }
630 } else if (style->stroke.isColor()) { // test last, always seems to be set, even for other types above
631 auto rgb = *style->stroke.getColor().converted(Space::Type::RGB);
632 brushStyle = U_BS_SOLID;
633 hatchColor = U_RGB(255 * rgb[0], 255 * rgb[1], 255 * rgb[2]);
634 hatchType = U_HS_SOLIDCLR;
635 } else {
636 // default fill
637 }
638
639
640
641 using Geom::X;
642 using Geom::Y;
643
644 Geom::Point zero(0, 0);
645 Geom::Point one(1, 1);
646 Geom::Point p0(zero * transform);
647 Geom::Point p1(one * transform);
648 Geom::Point p(p1 - p0);
649
650 double scale = sqrt((p[X] * p[X]) + (p[Y] * p[Y])) / sqrt(2);
651
652 if (!style->stroke_width.computed) {
653 return 0; //if width is 0 do not (reset) the pen, it should already be NULL_PEN
654 }
655 uint32_t linewidth = MAX(1, (uint32_t) round(scale * style->stroke_width.computed * PX2WORLD));
656
657 if (style->stroke_linecap.computed == 0) {
658 linecap = U_PS_ENDCAP_FLAT;
659 } else if (style->stroke_linecap.computed == 1) {
660 linecap = U_PS_ENDCAP_ROUND;
661 } else if (style->stroke_linecap.computed == 2) {
662 linecap = U_PS_ENDCAP_SQUARE;
663 }
664
665 if (style->stroke_linejoin.computed == 0) {
666 linejoin = U_PS_JOIN_MITER;
667 } else if (style->stroke_linejoin.computed == 1) {
668 linejoin = U_PS_JOIN_ROUND;
669 } else if (style->stroke_linejoin.computed == 2) {
670 linejoin = U_PS_JOIN_BEVEL;
671 }
672
673 if (!style->stroke_dasharray.values.empty()) {
674 if (FixPPTDashLine) { // will break up line into many smaller lines. Override gradient if that was set, cannot do both.
675 brushStyle = U_BS_SOLID;
676 hatchType = U_HS_HORIZONTAL;
677 } else {
678 unsigned i = 0;
679 while ((linestyle != U_PS_USERSTYLE) && (i < style->stroke_dasharray.values.size())) {
680 if (style->stroke_dasharray.values[i].value > 0.00000001) {
681 linestyle = U_PS_USERSTYLE;
682 }
683 i++;
684 }
685
686 if (linestyle == U_PS_USERSTYLE) {
687 n_dash = style->stroke_dasharray.values.size();
688 dash = new uint32_t[n_dash];
689 for (i = 0; i < n_dash; i++) {
690 dash[i] = MAX(1, (uint32_t)round(scale * style->stroke_dasharray.values[i].value * PX2WORLD));
691 }
692 }
693 }
694 }
695
696 elp = extlogpen_set(
697 U_PS_GEOMETRIC | linestyle | linecap | linejoin,
698 linewidth,
699 brushStyle,
700 hatchColor,
701 hatchType,
702 n_dash,
703 dash);
704
705 } else { // if (!style)
706 linejoin = 0;
707 elp = extlogpen_set(
708 linestyle,
709 1,
710 U_BS_SOLID,
711 U_RGB(0, 0, 0),
712 U_HS_HORIZONTAL,
713 0,
714 nullptr);
715 }
716
717 rec = extcreatepen_set(&pen, eht, Bmi, cbPx, px, elp);
718 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
719 g_error("Fatal programming error in PrintEmf::create_pen at extcreatepen_set");
720 }
721 free(elp);
722 if (Bmi) {
723 free(Bmi);
724 }
725 if (px) {
726 free(px); // ct will always be NULL
727 }
728
729 rec = selectobject_set(pen, eht);
730 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
731 g_error("Fatal programming error in PrintEmf::create_pen at selectobject_set");
732 }
733 hpen = pen; // need this later for destroy_pen
734
735 if (linejoin == U_PS_JOIN_MITER) {
736 float miterlimit = style->stroke_miterlimit.value; // This is a ratio.
737
738 if (miterlimit < 1) {
739 miterlimit = 1;
740 }
741
742 rec = U_EMRSETMITERLIMIT_set((uint32_t) miterlimit);
743 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
744 g_error("Fatal programming error in PrintEmf::create_pen at U_EMRSETMITERLIMIT_set");
745 }
746 }
747
748 if (n_dash) {
749 delete[] dash;
750 }
751 return 0;
752}
753
754// set the current pen to the stock object NULL_PEN and then delete the defined pen object, if there is one.
756{
757 char *rec = nullptr;
758 // before an object may be safely deleted it must no longer be selected
759 // select in a stock object to deselect this one, the stock object should
760 // never be used because we always select in a new one before drawing anythingrestore previous brush, necessary??? Would using a default stock object not work?
761 rec = selectobject_set(U_NULL_PEN, eht);
762 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
763 g_error("Fatal programming error in PrintEmf::destroy_pen at selectobject_set");
764 }
765 if (hpen) {
766 rec = deleteobject_set(&hpen, eht);
767 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
768 g_error("Fatal programming error in PrintEmf::destroy_pen");
769 }
770 hpen = 0;
771 }
772}
773
774/* Return a Path consisting of just the corner points of the single path in a PathVector. If the
775PathVector has more than one path, or that one path is open, or any of its segments are curved, then the
776returned PathVector is an empty path. If the input path is already just straight lines and vertices the output will be the
777same as the sole path in the input. */
778
780{
781 Geom::Point P1_trail;
782 Geom::Point P1;
783 Geom::Point P1_lead;
784 Geom::Point v1,v2;
785 Geom::Path output;
786 Geom::Path bad;
790 int first_seg=1;
791 ++pit2;
792 *vertices = 0;
793 if(pit->end_closed() != pit->end_default())return(bad); // path must be closed
794 if(pit2 != pv.end())return(bad); // there may only be one path
795 P1_trail = pit->finalPoint();
796 Geom::Path::const_iterator cit = pit->begin();
797 P1 = cit->initialPoint();
798 for(;cit != pit->end_closed();++cit) {
799 if (!is_straight_curve(*cit)) {
800 *vertices = 0;
801 return(bad);
802 }
803 P1_lead = cit->finalPoint();
804 if(Geom::are_near(P1_lead, P1, 1e-5))continue; // duplicate points at the same coordinate
805 v1 = unit_vector(P1 - P1_trail);
806 v2 = unit_vector(P1_lead - P1 );
807 if(Geom::are_near(dot(v1,v2), 1.0, 1e-5)){ // P1 is within a straight line
808 P1 = P1_lead;
809 continue;
810 }
811 // P1 is the center point of a turn of some angle
812 if(!*vertices){
813 output.start( P1 );
814 output.close( pit->closed() );
815 }
816 if(!Geom::are_near(P1, P1_trail, 1e-5)){ // possible for P1 to start on the end point
817 Geom::LineSegment ls(P1_trail, P1);
818 output.append(ls);
819 if(first_seg){
820 *vertices += 2;
821 first_seg=0;
822 }
823 else {
824 *vertices += 1;
825 }
826 }
827 P1_trail = P1;
828 P1 = P1_lead;
829 }
830 return(output);
831}
832
833/* Returns the simplified PathVector (no matter what).
834 Sets is_rect if it is a rectangle.
835 Sets angle that will rotate side closest to horizontal onto horizontal.
836*/
837Geom::Path PrintEmf::pathv_to_rect(Geom::PathVector const &pathv, bool *is_rect, double *angle)
838{
839 Geom::Point P1_trail;
840 Geom::Point P1;
841 Geom::Point v1,v2;
842 int vertices;
843 Geom::Path pR = pathv_to_simple_polygon(pathv, &vertices);
844 *is_rect = false;
845 if(vertices==4){ // or else it cannot be a rectangle
846 int vertex_count=0;
847 /* Get the ends of the LAST line segment.
848 Find minimum rotation to align rectangle with X,Y axes. (Very degenerate if it is rotated 45 degrees.) */
849 *angle = 10.0; /* must be > than the actual angle in radians. */
850 for(Geom::Path::iterator cit = pR.begin();; ++cit){
851 P1_trail = cit->initialPoint();
852 P1 = cit->finalPoint();
853 v1 = unit_vector(P1 - P1_trail);
854 if(v1[Geom::X] > 0){ // only check the 1 or 2 points on vectors aimed the same direction as unit X
855 double ang = asin(v1[Geom::Y]); // because component is rotation by ang of {1,0| vector
856 if(fabs(ang) < fabs(*angle))*angle = -ang; // y increases down, flips sign on angle
857 }
858 if(cit == pR.end_open())break;
859 }
860
861 /* For increased numerical stability, snap the angle to the nearest 1/100th of a degree. */
862 double convert = 36000.0/ (2.0 * M_PI);
863 *angle = round(*angle * convert)/convert;
864
865 // at this stage v1 holds the last vector in the path, whichever direction it points.
866 for(Geom::Path::iterator cit = pR.begin(); ;++cit) {
867 v2 = v1;
868 P1_trail = cit->initialPoint();
869 P1 = cit->finalPoint();
870 v1 = unit_vector(P1 - P1_trail);
871 // P1 is center of a turn that is not 90 degrees. Limit comes from cos(89.9) = .001745
872 if(!Geom::are_near(dot(v1,v2), 0.0, 2e-3))break;
873 vertex_count++;
874 if(cit == pR.end_open())break;
875 }
876 if(vertex_count == 4){
877 *is_rect=true;
878 }
879 }
880 return(pR);
881}
882
883/* Compare a vector with a rectangle's orientation (angle needed to rotate side(s)
884 closest to horizontal to exactly horizontal) and return:
885 0 none of the following
886 1 parallel to horizontal
887 2 parallel to vertical
888 3 antiparallel to horizontal
889 4 antiparallel to vertical
890*/
892 int stat = 0;
893 Geom::Point v1 = Geom::unit_vector(vtest); // unit vector to test alignment
894 Geom::Point v2 = Geom::Point(1,0) * Geom::Rotate(-angle); // unit horizontal side (sign change because Y increases DOWN)
895 Geom::Point v3 = Geom::Point(0,1) * Geom::Rotate(-angle); // unit horizontal side (sign change because Y increases DOWN)
896 if( Geom::are_near(dot(v1,v2), 1.0, 1e-5)){ stat = 1; }
897 else if(Geom::are_near(dot(v1,v2),-1.0, 1e-5)){ stat = 2; }
898 else if(Geom::are_near(dot(v1,v3), 1.0, 1e-5)){ stat = 3; }
899 else if(Geom::are_near(dot(v1,v3),-1.0, 1e-5)){ stat = 4; }
900 return(stat);
901}
902
903/* retrieve the point at the indicated corner:
904 0 UL (and default)
905 1 UR
906 2 LR
907 3 LL
908 Needed because the start can be any point, and the direction could run either
909 clockwise or counterclockwise. This should work even if the corners of the rectangle
910 are slightly displaced.
911*/
912Geom::Point PrintEmf::get_pathrect_corner(Geom::Path pathRect, double angle, int corner){
913 Geom::Point center(0,0);
914 for(Geom::Path::iterator cit = pathRect.begin(); ; ++cit) {
915 center += cit->initialPoint()/4.0;
916 if(cit == pathRect.end_open())break;
917 }
918
919 int LR; // 1 if Left, 0 if Right
920 int UL; // 1 if Lower, 0 if Upper (as viewed on screen, y coordinates increase downwards)
921 switch(corner){
922 case 1: //UR
923 LR = 0;
924 UL = 0;
925 break;
926 case 2: //LR
927 LR = 0;
928 UL = 1;
929 break;
930 case 3: //LL
931 LR = 1;
932 UL = 1;
933 break;
934 default: //UL
935 LR = 1;
936 UL = 0;
937 break;
938 }
939
940 Geom::Point v1 = Geom::Point(1,0) * Geom::Rotate(-angle); // unit horizontal side (sign change because Y increases DOWN)
941 Geom::Point v2 = Geom::Point(0,1) * Geom::Rotate(-angle); // unit vertical side (sign change because Y increases DOWN)
942 Geom::Point P1;
943 for(Geom::Path::iterator cit = pathRect.begin(); ; ++cit) {
944 P1 = cit->initialPoint();
945
946 if ( ( LR == (dot(P1 - center,v1) > 0 ? 0 : 1) )
947 && ( UL == (dot(P1 - center,v2) > 0 ? 1 : 0) ) ) break;
948 if(cit == pathRect.end_open())break;
949 }
950 return(P1);
951}
952
954 U_TRIVERTEX tv;
955 using Geom::X;
956 using Geom::Y;
957 tv.x = (int32_t) round(Pt[X]);
958 tv.y = (int32_t) round(Pt[Y]);
959 tv.Red = uc.Red << 8;
960 tv.Green = uc.Green << 8;
961 tv.Blue = uc.Blue << 8;
962 tv.Alpha = uc.Reserved << 8; // EMF will ignore this
963 return(tv);
964}
965
966/* Examine clip. If there is a (new) one then apply it. If there is one and it is the
967 same as the preceding one, leave the preceding one active. If style is NULL
968 terminate the current clip, if any, and return.
969*/
971 char *rec;
972 static SPClipPath *scpActive = nullptr;
973 if(!style){
974 if(scpActive){ // clear the existing clip
975 rec = U_EMRRESTOREDC_set(-1);
976 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
977 g_error("Fatal programming error in PrintEmf::fill at U_EMRRESTOREDC_set");
978 }
979 scpActive=nullptr;
980 }
981 } else {
982 /* The current implementation converts only one level of clipping. If there were more
983 clips further up the stack they should be combined with the pathvector using "and". Since this
984 comes up rarely, and would involve a lot of searching (all the way up the stack for every
985 draw operation), it has not yet been implemented.
986
987 Note, to debug this section of code use print statements on sp_svg_write_path(combined_pathvector).
988 */
989 /* find the first clip_ref at object or up the stack. There may not be one. */
990 SPClipPath *scp = nullptr;
991 auto item = cast<SPItem>(style->object);
992 while(true) {
993 scp = item->getClipObject();
994 if(scp)break;
995 item = cast<SPItem>(item->parent);
996 if(!item || is<SPRoot>(item))break; // this will never be a clipping path
997 }
998
999 if(scp != scpActive){ // change or remove the clipping
1000 if(scpActive){ // clear the existing clip
1001 rec = U_EMRRESTOREDC_set(-1);
1002 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1003 g_error("Fatal programming error in PrintEmf::fill at U_EMRRESTOREDC_set");
1004 }
1005 scpActive = nullptr;
1006 }
1007
1008 if (scp) { // set the new clip
1009 /* because of units and who knows what other transforms that might be applied above we
1010 need the full transform all the way to the root.
1011 */
1013 SPItem *scan_item = item;
1014 while(true) {
1015 scan_item = cast<SPItem>(scan_item->parent);
1016 if(!scan_item)break;
1017 tf *= scan_item->transform;
1018 }
1019 tf *= Geom::Scale(_doc_unit_scale);; // Transform must be in PIXELS, no matter what the document unit is.
1020
1021 /* find the clipping path */
1022 Geom::PathVector combined_pathvector;
1023 Geom::Affine tfc; // clipping transform, generally not the same as item transform
1024 for (auto& child: scp->children) {
1025 item = cast<SPItem>(&child);
1026 if (!item) {
1027 break;
1028 }
1029 if (is<SPGroup>(item)) { // not implemented
1030 // return sp_group_render(item);
1031 combined_pathvector = merge_PathVector_with_group(combined_pathvector, item, tfc);
1032 } else if (is<SPShape>(item)) {
1033 combined_pathvector = merge_PathVector_with_shape(combined_pathvector, item, tfc);
1034 } else { // not implemented
1035 }
1036 }
1037
1038 if (!combined_pathvector.empty()) { // if clipping path isn't empty, define EMF clipping record
1039 scpActive = scp; // remember for next time
1040 // the sole purpose of this SAVEDC is to let us clear the clipping region later.
1041 rec = U_EMRSAVEDC_set();
1042 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1043 g_error("Fatal programming error in PrintEmf::image at U_EMRSAVEDC_set");
1044 }
1045 (void) draw_pathv_to_EMF(combined_pathvector, tf);
1046 rec = U_EMRSELECTCLIPPATH_set(U_RGN_COPY);
1047 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1048 g_error("Fatal programming error in PrintEmf::do_clip_if_present at U_EMRSELECTCLIPPATH_set");
1049 }
1050 }
1051 else {
1052 scpActive = nullptr; // no valid path available to draw, so no DC was saved, so no signal to restore
1053 }
1054 } // change or remove clipping
1055 } // scp exists
1056 } // style exists
1057}
1058
1060{
1061 // sanity test, only a group should be passed in, return empty if something else happens
1062 auto group = cast<SPGroup>(item);
1063 if (!group)
1064 return {};
1065
1066 Geom::PathVector new_combined_pathvector = combined_pathvector;
1067 Geom::Affine tfc = item->transform * transform;
1068 for (auto& child: group->children) {
1069 item = cast<SPItem>(&child);
1070 if (!item) {
1071 break;
1072 }
1073 if (is<SPGroup>(item)) {
1074 new_combined_pathvector = merge_PathVector_with_group(new_combined_pathvector, item, tfc); // could be endlessly recursive on a badly formed SVG
1075 } else if (is<SPShape>(item)) {
1076 new_combined_pathvector = merge_PathVector_with_shape(new_combined_pathvector, item, tfc);
1077 } else { // not implemented
1078 }
1079 }
1080 return new_combined_pathvector;
1081}
1082
1084{
1085 Geom::PathVector new_combined_pathvector;
1086 auto shape = cast<SPShape>(item);
1087
1088 // sanity test, only a shape should be passed in, return empty if something else happens
1089 if (!shape)
1090 return new_combined_pathvector;
1091
1092 Geom::Affine tfc = item->transform * transform;
1093 if (shape->curve()) {
1094 Geom::PathVector const &new_vect = shape->curve()->get_pathvector();
1095 if(combined_pathvector.empty()){
1096 new_combined_pathvector = new_vect * tfc;
1097 }
1098 else {
1099 new_combined_pathvector = sp_pathvector_boolop(new_vect * tfc, combined_pathvector, bool_op_union , (FillRule) fill_oddEven, (FillRule) fill_oddEven);
1100 }
1101 }
1102 return new_combined_pathvector;
1103}
1104
1105unsigned int PrintEmf::fill(
1107 Geom::PathVector const &pathv, Geom::Affine const & /*transform*/, SPStyle const *style,
1108 Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/)
1109{
1110 char *rec;
1111 using Geom::X;
1112 using Geom::Y;
1113 Geom::Affine tf = m_tr_stack.top();
1114
1115 do_clip_if_present(style); // If clipping is needed set it up
1116
1117 use_fill = true;
1118 use_stroke = false;
1119
1120 fill_transform = tf;
1121
1122 int brush_stat = create_brush(style, nullptr);
1123
1124 /* native linear gradients are only used if the object is a rectangle AND the gradient is parallel to the sides of the object */
1125 bool is_Rect = false;
1126 double angle;
1127 int rectDir=0;
1128 Geom::Path pathRect;
1129 if(FixPPTLinGrad && brush_stat && gv.mode == DRAW_LINEAR_GRADIENT){
1130 Geom::PathVector pvr = pathv * fill_transform;
1131 pathRect = pathv_to_rect(pvr, &is_Rect, &angle);
1132 if(is_Rect){
1133 /* Gradientfill records can only be used if the gradient is parallel to the sides of the rectangle.
1134 That must be checked here so that we can fall back to another form of gradient fill if it is not
1135 the case. */
1136 rectDir = vector_rect_alignment(angle, (gv.p2 - gv.p1) * fill_transform);
1137 if(!rectDir)is_Rect = false;
1138 }
1139 if(!is_Rect && !FixPPTGrad2Polys)brush_stat=0; // fall all the way back to a solid fill
1140 }
1141
1142 if (brush_stat) { // only happens if the style is a gradient
1143 /*
1144 Handle gradients. Uses modified livarot as 2geom boolops is currently broken.
1145 Can handle gradients with multiple stops.
1146
1147 The overlap is needed to avoid antialiasing artifacts when edges are not strictly aligned on pixel boundaries.
1148 There is an inevitable loss of accuracy saving through an EMF file because of the integer coordinate system.
1149 Keep the overlap quite large so that loss of accuracy does not remove an overlap.
1150 */
1151 destroy_pen(); //this sets the NULL_PEN, otherwise gradient slices may display with boundaries, see longer explanation below
1152 Geom::Path cutter;
1153 U_COLORREF wc, c1, c2;
1154 FillRule frb = SPWR_to_LVFR((SPWindRule) style->fill_rule.computed);
1155 double doff, doff_base, doff_range;
1156 double divisions = 128.0;
1157 int nstops;
1158 int istop = 1;
1159
1160 SPRadialGradient *tg = (SPRadialGradient *)(gv.grad); // linear/radial are the same here
1161 nstops = tg->vector.stops.size();
1162 c1 = toColorRef(tg->vector.stops[0].color);
1163 c2 = toColorRef(tg->vector.stops[nstops - 1].color);
1164
1165 doff = 0.0;
1166 doff_base = 0.0;
1167 doff_range = tg->vector.stops[1].offset; // next or last stop
1168
1169 if (gv.mode == DRAW_RADIAL_GRADIENT) {
1170 Geom::Point xv = gv.p2 - gv.p1; // X' vector
1171 Geom::Point yv = gv.p3 - gv.p1; // Y' vector
1172 Geom::Point xuv = Geom::unit_vector(xv); // X' unit vector
1173 double rx = hypot(xv[X], xv[Y]);
1174 double ry = hypot(yv[X], yv[Y]);
1175 double range = fmax(rx, ry); // length along the gradient
1176 double step = range / divisions; // adequate approximation for gradient
1177 double overlap = step / 4.0; // overlap slices slightly
1178 double start;
1179 double stop;
1180 Geom::PathVector pathvc, pathvr;
1181
1182 /* radial gradient might stop part way through the shape, fill with outer color from there to "infinity".
1183 Do this first so that outer colored ring will overlay it.
1184 */
1185 pathvc = center_elliptical_hole_as_SVG_PathV(gv.p1, rx * (1.0 - overlap / range), ry * (1.0 - overlap / range), asin(xuv[Y]));
1186 pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_oddEven, frb);
1187 wc = weight_opacity(c2);
1188 (void) create_brush(style, &wc);
1189 print_pathv(pathvr, fill_transform);
1190
1191 c2 = toColorRef(tg->vector.stops[istop].color);
1192
1193 for (start = 0.0; start < range; start += step, doff += 1. / divisions) {
1194 stop = start + step + overlap;
1195 if (stop > range) {
1196 stop = range;
1197 }
1198 wc = weight_colors(c1, c2, (doff - doff_base) / (doff_range - doff_base));
1199 (void) create_brush(style, &wc);
1200
1201 pathvc = center_elliptical_ring_as_SVG_PathV(gv.p1, rx * start / range, ry * start / range, rx * stop / range, ry * stop / range, asin(xuv[Y]));
1202
1203 pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
1204 print_pathv(pathvr, fill_transform); // show the intersection
1205
1206 if (doff >= doff_range) {
1207 istop++;
1208 if (istop >= nstops) {
1209 istop = nstops - 1;
1210 continue; // could happen on a rounding error
1211 }
1212 doff_base = doff_range;
1213 doff_range = tg->vector.stops[istop].offset; // next or last stop
1214 c1 = c2;
1215 c2 = toColorRef(tg->vector.stops[istop].color);
1216 }
1217 }
1218 } else if (gv.mode == DRAW_LINEAR_GRADIENT) {
1219 if(is_Rect){
1220 int gMode;
1221 Geom::Point ul, ur, lr;
1222 Geom::Point outUL, outLR; // UL,LR corners of a stop rectangle, in OUTPUT coordinates
1223 U_TRIVERTEX ut[2];
1224 U_GRADIENT4 ug4;
1225 U_RECTL rcb;
1226 U_XFORM tmpTransform;
1227 double wRect, hRect;
1228
1229 /* coordinates: upper left, upper right, and lower right corners of the rectangle.
1230 inkscape transform already applied, but needs to be scaled to EMF coordinates. */
1231 ul = get_pathrect_corner(pathRect, angle, 0) * PX2WORLD;
1232 ur = get_pathrect_corner(pathRect, angle, 1) * PX2WORLD;
1233 lr = get_pathrect_corner(pathRect, angle, 2) * PX2WORLD;
1234 wRect = Geom::distance(ul,ur);
1235 hRect = Geom::distance(ur,lr);
1236
1237 /* The basic rectangle for all of these is placed with its UL corner at 0,0 with a size wRect,hRect.
1238 Apply a world transform to place/scale it into the appropriate position on the drawing.
1239 Actual gradientfill records are either this entire rectangle or slices of it as defined by the stops.
1240 This rectangle has already been transformed by tf (whatever rotation/scale) Inkscape had applied to it.
1241 */
1242
1243 Geom::Affine tf2 = Geom::Rotate(-angle); // the rectangle may be drawn skewed to the coordinate system
1244 tmpTransform.eM11 = tf2[0];
1245 tmpTransform.eM12 = tf2[1];
1246 tmpTransform.eM21 = tf2[2];
1247 tmpTransform.eM22 = tf2[3];
1248 tmpTransform.eDx = round((ul)[Geom::X]); // use explicit round for better stability
1249 tmpTransform.eDy = round((ul)[Geom::Y]);
1250
1251 rec = U_EMRSAVEDC_set();
1252 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1253 g_error("Fatal programming error in PrintEmf::image at U_EMRSAVEDC_set");
1254 }
1255
1256 rec = U_EMRMODIFYWORLDTRANSFORM_set(tmpTransform, U_MWT_LEFTMULTIPLY);
1257 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1258 g_error("Fatal programming error in PrintEmf::image at EMRMODIFYWORLDTRANSFORM");
1259 }
1260
1261 for(;istop<nstops;istop++){
1262 doff_range = tg->vector.stops[istop].offset; // next or last stop
1263 if(rectDir == 1 || rectDir == 2){
1264 outUL = Geom::Point(doff_base *wRect, 0 );
1265 outLR = Geom::Point(doff_range*wRect, hRect);
1266 gMode = U_GRADIENT_FILL_RECT_H;
1267 }
1268 else {
1269 outUL = Geom::Point(0, doff_base *hRect);
1270 outLR = Geom::Point(wRect,doff_range*hRect);
1271 gMode = U_GRADIENT_FILL_RECT_V;
1272 }
1273
1274 doff_base = doff_range;
1275 rcb.left = round(outUL[X]); // use explicit round for better stability
1276 rcb.top = round(outUL[Y]);
1277 rcb.right = round(outLR[X]);
1278 rcb.bottom = round(outLR[Y]);
1279 c2 = toColorRef(tg->vector.stops[istop].color);
1280
1281 if(rectDir == 2 || rectDir == 4){ // gradient is reversed, so swap colors
1282 ut[0] = make_trivertex(outUL, c2);
1283 ut[1] = make_trivertex(outLR, c1);
1284 }
1285 else {
1286 ut[0] = make_trivertex(outUL, c1);
1287 ut[1] = make_trivertex(outLR, c2);
1288 }
1289 c1 = c2; // for next stop
1290 ug4.UpperLeft = 0;
1291 ug4.LowerRight= 1;
1292 rec = U_EMRGRADIENTFILL_set(rcb, 2, 1, gMode, ut, (uint32_t *) &ug4 );
1293 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1294 g_error("Fatal programming error in PrintEmf::fill at U_EMRGRADIENTFILL_set");
1295 }
1296 }
1297
1298 rec = U_EMRRESTOREDC_set(-1);
1299 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1300 g_error("Fatal programming error in PrintEmf::fill at U_EMRRESTOREDC_set");
1301 }
1302 }
1303 else {
1304 Geom::Point uv = Geom::unit_vector(gv.p2 - gv.p1); // unit vector
1305 Geom::Point puv = uv.cw(); // perp. to unit vector
1306 double range = Geom::distance(gv.p1, gv.p2); // length along the gradient
1307 double step = range / divisions; // adequate approximation for gradient
1308 double overlap = step / 4.0; // overlap slices slightly
1309 double start;
1310 double stop;
1311 Geom::PathVector pathvc, pathvr;
1312
1313 /* before lower end of gradient, overlap first slice position */
1314 wc = weight_opacity(c1);
1315 (void) create_brush(style, &wc);
1316 pathvc = rect_cutter(gv.p1, uv * (overlap), uv * (-50000.0), puv * 50000.0);
1317 pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
1318 print_pathv(pathvr, fill_transform);
1319
1320 /* after high end of gradient, overlap last slice position */
1321 wc = weight_opacity(c2);
1322 (void) create_brush(style, &wc);
1323 pathvc = rect_cutter(gv.p2, uv * (-overlap), uv * (50000.0), puv * 50000.0);
1324 pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
1325 print_pathv(pathvr, fill_transform);
1326
1327 c2 = toColorRef(tg->vector.stops[istop].color);
1328
1329 for (start = 0.0; start < range; start += step, doff += 1. / divisions) {
1330 stop = start + step + overlap;
1331 if (stop > range) {
1332 stop = range;
1333 }
1334 pathvc = rect_cutter(gv.p1, uv * start, uv * stop, puv * 50000.0);
1335
1336 wc = weight_colors(c1, c2, (doff - doff_base) / (doff_range - doff_base));
1337 (void) create_brush(style, &wc);
1339 print_pathv(pathvr, fill_transform); // show the intersection
1340
1341 if (doff >= doff_range) {
1342 istop++;
1343 if (istop >= nstops) {
1344 istop = nstops - 1;
1345 continue; // could happen on a rounding error
1346 }
1347 doff_base = doff_range;
1348 doff_range = tg->vector.stops[istop].offset; // next or last stop
1349 c1 = c2;
1350 c2 = toColorRef(tg->vector.stops[istop].color);
1351 }
1352 }
1353 }
1354 } else {
1355 g_error("Fatal programming error in PrintEmf::fill, invalid gradient type detected");
1356 }
1357 use_fill = false; // gradients handled, be sure stroke does not use stroke and fill
1358 } else {
1359 /*
1360 Inkscape was not calling create_pen for objects with no border.
1361 This was because it never called stroke() (next method).
1362 PPT, and presumably others, pick whatever they want for the border if it is not specified, so no border can
1363 become a visible border.
1364 To avoid this force the pen to NULL_PEN if we can determine that no pen will be needed after the fill.
1365 */
1366 if (style->stroke.noneSet || style->stroke_width.computed == 0.0) {
1367 destroy_pen(); //this sets the NULL_PEN
1368 }
1369
1370 /* postpone fill in case stroke also required AND all stroke paths closed
1371 Dashes converted to line segments will "open" a closed path.
1372 */
1373 bool all_closed = true;
1374 for (const auto & pit : pathv) {
1375 for (Geom::Path::const_iterator cit = pit.begin(); cit != pit.end_open(); ++cit) {
1376 if (pit.end_default() != pit.end_closed()) {
1377 all_closed = false;
1378 }
1379 }
1380 }
1381 if (
1382 (style->stroke.isNone() || style->stroke.noneSet || style->stroke_width.computed == 0.0) ||
1383 (!style->stroke_dasharray.values.empty() && FixPPTDashLine) ||
1384 !all_closed
1385 ) {
1386 print_pathv(pathv, fill_transform); // do any fills. side effect: clears fill_pathv
1387 use_fill = false;
1388 }
1389 }
1390
1391 return 0;
1392}
1393
1394
1395unsigned int PrintEmf::stroke(
1397 Geom::PathVector const &pathv, const Geom::Affine &/*transform*/, const SPStyle *style,
1398 Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/)
1399{
1400
1401 char *rec = nullptr;
1402 Geom::Affine tf = m_tr_stack.top();
1403 do_clip_if_present(style); // If clipping is needed set it up
1404
1405 use_stroke = true;
1406 // use_fill was set in ::fill, if it is needed
1407
1408 if (create_pen(style, tf)) {
1409 return 0;
1410 }
1411
1412 if (!style->stroke_dasharray.values.empty() && FixPPTDashLine) {
1413 // convert the path, gets its complete length, and then make a new path with parameter length instead of t
1414 Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw; // pathv-> sbasis
1415 Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw2; // sbasis using arc length parameter
1416 Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw3; // new (discontinuous) path, composed of dots/dashes
1417 Geom::Piecewise<Geom::D2<Geom::SBasis> > first_frag; // first fragment, will be appended at end
1418 int n_dash = style->stroke_dasharray.values.size();
1419 int i = 0; //dash index
1420 double tlength; // length of tmp_pathpw
1421 double slength = 0.0; // start of gragment
1422 double elength; // end of gragment
1423 for (const auto & i : pathv) {
1424 tmp_pathpw.concat(i.toPwSb());
1425 }
1426 tlength = length(tmp_pathpw, 0.1);
1427 tmp_pathpw2 = arc_length_parametrization(tmp_pathpw);
1428
1429 // go around the dash array repeatedly until the entire path is consumed (but not beyond).
1430 while (slength < tlength) {
1431 elength = slength + style->stroke_dasharray.values[i++].value;
1432 if (elength > tlength) {
1433 elength = tlength;
1434 }
1435 Geom::Piecewise<Geom::D2<Geom::SBasis> > fragment(portion(tmp_pathpw2, slength, elength));
1436 if (slength) {
1437 tmp_pathpw3.concat(fragment);
1438 } else {
1439 first_frag = fragment;
1440 }
1441 slength = elength;
1442 slength += style->stroke_dasharray.values[i++].value; // the gap
1443 if (i >= n_dash) {
1444 i = 0;
1445 }
1446 }
1447 tmp_pathpw3.concat(first_frag); // may merge line around start point
1448 Geom::PathVector out_pathv = Geom::path_from_piecewise(tmp_pathpw3, 0.01);
1449 print_pathv(out_pathv, tf);
1450 } else {
1451 print_pathv(pathv, tf);
1452 }
1453
1454 use_stroke = false;
1455 use_fill = false;
1456
1457 if (usebk) { // OPAQUE was set, revert to TRANSPARENT
1458 usebk = false;
1459 rec = U_EMRSETBKMODE_set(U_TRANSPARENT);
1460 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1461 g_error("Fatal programming error in PrintEmf::stroke at U_EMRSETBKMODE_set");
1462 }
1463 }
1464
1465 return 0;
1466}
1467
1468
1469// Draws simple_shapes, those with closed EMR_* primitives, like polygons, rectangles and ellipses.
1470// These use whatever the current pen/brush are and need not be followed by a FILLPATH or STROKEPATH.
1471// For other paths it sets a few flags and returns.
1473{
1474
1476
1477 int nodes = 0;
1478 int moves = 0;
1479 int lines = 0;
1480 int curves = 0;
1481 char *rec = nullptr;
1482
1483 for (auto & pit : pv) {
1484 moves++;
1485 nodes++;
1486
1487 for (Geom::Path::iterator cit = pit.begin(); cit != pit.end_open(); ++cit) {
1488 nodes++;
1489
1490 if (is_straight_curve(*cit)) {
1491 lines++;
1492 } else if (dynamic_cast<Geom::CubicBezier const *>(&*cit)) {
1493 curves++;
1494 }
1495 }
1496 }
1497
1498 if (!nodes) {
1499 return false;
1500 }
1501
1502 U_POINT *lpPoints = new U_POINT[moves + lines + curves * 3];
1503 int i = 0;
1504
1508 for (auto & pit : pv) {
1509 using Geom::X;
1510 using Geom::Y;
1511
1512 Geom::Point p0 = pit.initialPoint();
1513
1514 p0[X] = (p0[X] * PX2WORLD);
1515 p0[Y] = (p0[Y] * PX2WORLD);
1516
1517 int32_t const x0 = (int32_t) round(p0[X]);
1518 int32_t const y0 = (int32_t) round(p0[Y]);
1519
1520 lpPoints[i].x = x0;
1521 lpPoints[i].y = y0;
1522 i = i + 1;
1523
1527 for (Geom::Path::iterator cit = pit.begin(); cit != pit.end_open(); ++cit) {
1528 if (is_straight_curve(*cit)) {
1529 //Geom::Point p0 = cit->initialPoint();
1530 Geom::Point p1 = cit->finalPoint();
1531
1532 //p0[X] = (p0[X] * PX2WORLD);
1533 p1[X] = (p1[X] * PX2WORLD);
1534 //p0[Y] = (p0[Y] * PX2WORLD);
1535 p1[Y] = (p1[Y] * PX2WORLD);
1536
1537 //int32_t const x0 = (int32_t) round(p0[X]);
1538 //int32_t const y0 = (int32_t) round(p0[Y]);
1539 int32_t const x1 = (int32_t) round(p1[X]);
1540 int32_t const y1 = (int32_t) round(p1[Y]);
1541
1542 lpPoints[i].x = x1;
1543 lpPoints[i].y = y1;
1544 i = i + 1;
1545 } else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*cit)) {
1546 std::vector<Geom::Point> points = cubic->controlPoints();
1547 //Geom::Point p0 = points[0];
1548 Geom::Point p1 = points[1];
1549 Geom::Point p2 = points[2];
1550 Geom::Point p3 = points[3];
1551
1552 //p0[X] = (p0[X] * PX2WORLD);
1553 p1[X] = (p1[X] * PX2WORLD);
1554 p2[X] = (p2[X] * PX2WORLD);
1555 p3[X] = (p3[X] * PX2WORLD);
1556 //p0[Y] = (p0[Y] * PX2WORLD);
1557 p1[Y] = (p1[Y] * PX2WORLD);
1558 p2[Y] = (p2[Y] * PX2WORLD);
1559 p3[Y] = (p3[Y] * PX2WORLD);
1560
1561 //int32_t const x0 = (int32_t) round(p0[X]);
1562 //int32_t const y0 = (int32_t) round(p0[Y]);
1563 int32_t const x1 = (int32_t) round(p1[X]);
1564 int32_t const y1 = (int32_t) round(p1[Y]);
1565 int32_t const x2 = (int32_t) round(p2[X]);
1566 int32_t const y2 = (int32_t) round(p2[Y]);
1567 int32_t const x3 = (int32_t) round(p3[X]);
1568 int32_t const y3 = (int32_t) round(p3[Y]);
1569
1570 lpPoints[i].x = x1;
1571 lpPoints[i].y = y1;
1572 lpPoints[i + 1].x = x2;
1573 lpPoints[i + 1].y = y2;
1574 lpPoints[i + 2].x = x3;
1575 lpPoints[i + 2].y = y3;
1576 i = i + 3;
1577 }
1578 }
1579 }
1580
1581 bool done = false;
1582 bool closed = (lpPoints[0].x == lpPoints[i - 1].x) && (lpPoints[0].y == lpPoints[i - 1].y);
1583 bool polygon = false;
1584 bool rectangle = false;
1585 bool ellipse = false;
1586
1587 if (moves == 1 && moves + lines == nodes && closed) {
1588 polygon = true;
1589 // if (nodes==5) { // disable due to LP Bug 407394
1590 // if (lpPoints[0].x == lpPoints[3].x && lpPoints[1].x == lpPoints[2].x &&
1591 // lpPoints[0].y == lpPoints[1].y && lpPoints[2].y == lpPoints[3].y)
1592 // {
1593 // rectangle = true;
1594 // }
1595 // }
1596 } else if (moves == 1 && nodes == 5 && moves + curves == nodes && closed) {
1597 // if (lpPoints[0].x == lpPoints[1].x && lpPoints[1].x == lpPoints[11].x &&
1598 // lpPoints[5].x == lpPoints[6].x && lpPoints[6].x == lpPoints[7].x &&
1599 // lpPoints[2].x == lpPoints[10].x && lpPoints[3].x == lpPoints[9].x && lpPoints[4].x == lpPoints[8].x &&
1600 // lpPoints[2].y == lpPoints[3].y && lpPoints[3].y == lpPoints[4].y &&
1601 // lpPoints[8].y == lpPoints[9].y && lpPoints[9].y == lpPoints[10].y &&
1602 // lpPoints[5].y == lpPoints[1].y && lpPoints[6].y == lpPoints[0].y && lpPoints[7].y == lpPoints[11].y)
1603 // { // disable due to LP Bug 407394
1604 // ellipse = true;
1605 // }
1606 }
1607
1608 if (polygon || ellipse) {
1609
1610 if (use_fill && !use_stroke) { // only fill
1611 rec = selectobject_set(U_NULL_PEN, eht);
1612 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1613 g_error("Fatal programming error in PrintEmf::print_simple_shape at selectobject_set pen");
1614 }
1615 } else if (!use_fill && use_stroke) { // only stroke
1616 rec = selectobject_set(U_NULL_BRUSH, eht);
1617 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1618 g_error("Fatal programming error in PrintEmf::print_simple_shape at selectobject_set brush");
1619 }
1620 }
1621
1622 if (polygon) {
1623 if (rectangle) {
1624 U_RECTL rcl = rectl_set((U_POINTL) {
1625 lpPoints[0].x, lpPoints[0].y
1626 }, (U_POINTL) {
1627 lpPoints[2].x, lpPoints[2].y
1628 });
1629 rec = U_EMRRECTANGLE_set(rcl);
1630 } else {
1631 rec = U_EMRPOLYGON_set(U_RCL_DEF, nodes, lpPoints);
1632 }
1633 } else if (ellipse) {
1634 U_RECTL rcl = rectl_set((U_POINTL) {
1635 lpPoints[6].x, lpPoints[3].y
1636 }, (U_POINTL) {
1637 lpPoints[0].x, lpPoints[9].y
1638 });
1639 rec = U_EMRELLIPSE_set(rcl);
1640 }
1641 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1642 g_error("Fatal programming error in PrintEmf::print_simple_shape at retangle/ellipse/polygon");
1643 }
1644
1645 done = true;
1646
1647 // replace the handle we moved above, assuming there was something set already
1648 if (use_fill && !use_stroke && hpen) { // only fill
1649 rec = selectobject_set(hpen, eht);
1650 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1651 g_error("Fatal programming error in PrintEmf::print_simple_shape at selectobject_set pen");
1652 }
1653 } else if (!use_fill && use_stroke && hbrush) { // only stroke
1654 rec = selectobject_set(hbrush, eht);
1655 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1656 g_error("Fatal programming error in PrintEmf::print_simple_shape at selectobject_set brush");
1657 }
1658 }
1659 }
1660
1661 delete[] lpPoints;
1662
1663 return done;
1664}
1665
1681unsigned int PrintEmf::image(
1682 Inkscape::Extension::Print * /* module */,
1683 unsigned char *rgba_px,
1684 unsigned int w,
1685 unsigned int h,
1686 unsigned int rs,
1687 Geom::Affine const &tf_rect,
1688 SPStyle const *style)
1689{
1690 double x1, y1, dw, dh;
1691 char *rec = nullptr;
1692 Geom::Affine tf = m_tr_stack.top();
1693
1694 do_clip_if_present(style); // If clipping is needed set it up
1695
1696 rec = U_EMRSETSTRETCHBLTMODE_set(U_COLORONCOLOR);
1697 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1698 g_error("Fatal programming error in PrintEmf::image at EMRHEADER");
1699 }
1700
1701 x1 = tf_rect[4];
1702 y1 = tf_rect[5];
1703 dw = ((double) w) * tf_rect[0];
1704 dh = ((double) h) * tf_rect[3];
1705 Geom::Point pLL(x1, y1);
1706 Geom::Point pLL2 = pLL * tf; //location of LL corner in Inkscape coordinates
1707
1708 char *px;
1709 uint32_t cbPx;
1710 uint32_t colortype;
1711 PU_RGBQUAD ct;
1712 int numCt;
1713 U_BITMAPINFOHEADER Bmih;
1714 PU_BITMAPINFO Bmi;
1715 colortype = U_BCBM_COLOR32;
1716 (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, (char *) rgba_px, w, h, w * 4, colortype, 0, 1);
1717 Bmih = bitmapinfoheader_set(w, h, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0);
1718 Bmi = bitmapinfo_set(Bmih, ct);
1719
1720 U_POINTL Dest = pointl_set(round(pLL2[Geom::X] * PX2WORLD), round(pLL2[Geom::Y] * PX2WORLD));
1721 U_POINTL cDest = pointl_set(round(dw * PX2WORLD), round(dh * PX2WORLD));
1722 U_POINTL Src = pointl_set(0, 0);
1723 U_POINTL cSrc = pointl_set(w, h);
1724 /* map the integer Dest coordinates back into pLL2, so that the rounded part does not destabilize the transform offset below */
1725 pLL2[Geom::X] = Dest.x;
1726 pLL2[Geom::Y] = Dest.y;
1727 pLL2 /= PX2WORLD;
1728 if (!FixImageRot) { /* Rotate images - some programs cannot read them in correctly if they are rotated */
1729 tf[4] = tf[5] = 0.0; // get rid of the offset in the transform
1730 Geom::Point pLL2prime = pLL2 * tf;
1731 U_XFORM tmpTransform;
1732 tmpTransform.eM11 = tf[0];
1733 tmpTransform.eM12 = tf[1];
1734 tmpTransform.eM21 = tf[2];
1735 tmpTransform.eM22 = tf[3];
1736 tmpTransform.eDx = (pLL2[Geom::X] - pLL2prime[Geom::X]) * PX2WORLD;
1737 tmpTransform.eDy = (pLL2[Geom::Y] - pLL2prime[Geom::Y]) * PX2WORLD;
1738
1739 rec = U_EMRSAVEDC_set();
1740 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1741 g_error("Fatal programming error in PrintEmf::image at U_EMRSAVEDC_set");
1742 }
1743
1744 rec = U_EMRMODIFYWORLDTRANSFORM_set(tmpTransform, U_MWT_LEFTMULTIPLY);
1745 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1746 g_error("Fatal programming error in PrintEmf::image at EMRMODIFYWORLDTRANSFORM");
1747 }
1748 }
1749 rec = U_EMRSTRETCHDIBITS_set(
1750 U_RCL_DEF,
1751 Dest,
1752 cDest,
1753 Src,
1754 cSrc,
1755 U_DIB_RGB_COLORS,
1756 U_SRCCOPY,
1757 Bmi,
1758 h * rs,
1759 px
1760 );
1761 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1762 g_error("Fatal programming error in PrintEmf::image at U_EMRSTRETCHDIBITS_set");
1763 }
1764 free(px);
1765 free(Bmi);
1766 if (numCt) {
1767 free(ct);
1768 }
1769
1770 if (!FixImageRot) {
1771 rec = U_EMRRESTOREDC_set(-1);
1772 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1773 g_error("Fatal programming error in PrintEmf::image at U_EMRRESTOREDC_set");
1774 }
1775 }
1776
1777 return 0;
1778}
1779
1780unsigned int PrintEmf::draw_pathv_to_EMF(Geom::PathVector const &pathv, const Geom::Affine &transform) {
1781 char *rec;
1782
1783 /* inkscape to EMF scaling is done below, but NOT the rotation/translation transform,
1784 that is handled by the EMF MODIFYWORLDTRANSFORM record
1785 */
1786
1788
1789 rec = U_EMRBEGINPATH_set();
1790 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1791 g_error("Fatal programming error in PrintEmf::print_pathv at U_EMRBEGINPATH_set");
1792 }
1793
1797 for (const auto & pit : pv) {
1798 using Geom::X;
1799 using Geom::Y;
1800
1801
1802 Geom::Point p0 = pit.initialPoint();
1803
1804 p0[X] = (p0[X] * PX2WORLD);
1805 p0[Y] = (p0[Y] * PX2WORLD);
1806
1807 U_POINTL ptl = pointl_set((int32_t) round(p0[X]), (int32_t) round(p0[Y]));
1808 rec = U_EMRMOVETOEX_set(ptl);
1809 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1810 g_error("Fatal programming error in PrintEmf::print_pathv at U_EMRMOVETOEX_set");
1811 }
1812
1816 for (Geom::Path::const_iterator cit = pit.begin(); cit != pit.end_open(); ++cit) {
1817 if (is_straight_curve(*cit)) {
1818 //Geom::Point p0 = cit->initialPoint();
1819 Geom::Point p1 = cit->finalPoint();
1820
1821 //p0[X] = (p0[X] * PX2WORLD);
1822 p1[X] = (p1[X] * PX2WORLD);
1823 //p0[Y] = (p0[Y] * PX2WORLD);
1824 p1[Y] = (p1[Y] * PX2WORLD);
1825
1826 //int32_t const x0 = (int32_t) round(p0[X]);
1827 //int32_t const y0 = (int32_t) round(p0[Y]);
1828
1829 ptl = pointl_set((int32_t) round(p1[X]), (int32_t) round(p1[Y]));
1830 rec = U_EMRLINETO_set(ptl);
1831 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1832 g_error("Fatal programming error in PrintEmf::print_pathv at U_EMRLINETO_set");
1833 }
1834 } else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*cit)) {
1835 std::vector<Geom::Point> points = cubic->controlPoints();
1836 //Geom::Point p0 = points[0];
1837 Geom::Point p1 = points[1];
1838 Geom::Point p2 = points[2];
1839 Geom::Point p3 = points[3];
1840
1841 //p0[X] = (p0[X] * PX2WORLD);
1842 p1[X] = (p1[X] * PX2WORLD);
1843 p2[X] = (p2[X] * PX2WORLD);
1844 p3[X] = (p3[X] * PX2WORLD);
1845 //p0[Y] = (p0[Y] * PX2WORLD);
1846 p1[Y] = (p1[Y] * PX2WORLD);
1847 p2[Y] = (p2[Y] * PX2WORLD);
1848 p3[Y] = (p3[Y] * PX2WORLD);
1849
1850 //int32_t const x0 = (int32_t) round(p0[X]);
1851 //int32_t const y0 = (int32_t) round(p0[Y]);
1852 int32_t const x1 = (int32_t) round(p1[X]);
1853 int32_t const y1 = (int32_t) round(p1[Y]);
1854 int32_t const x2 = (int32_t) round(p2[X]);
1855 int32_t const y2 = (int32_t) round(p2[Y]);
1856 int32_t const x3 = (int32_t) round(p3[X]);
1857 int32_t const y3 = (int32_t) round(p3[Y]);
1858
1859 U_POINTL pt[3];
1860 pt[0].x = x1;
1861 pt[0].y = y1;
1862 pt[1].x = x2;
1863 pt[1].y = y2;
1864 pt[2].x = x3;
1865 pt[2].y = y3;
1866
1867 rec = U_EMRPOLYBEZIERTO_set(U_RCL_DEF, 3, pt);
1868 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1869 g_error("Fatal programming error in PrintEmf::print_pathv at U_EMRPOLYBEZIERTO_set");
1870 }
1871 } else {
1872 g_warning("logical error, because pathv_to_linear_and_cubic_beziers was used");
1873 }
1874 }
1875
1876 if (pit.end_default() == pit.end_closed()) { // there may be multiples of this on a single path
1877 rec = U_EMRCLOSEFIGURE_set();
1878 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1879 g_error("Fatal programming error in PrintEmf::print_pathv at U_EMRCLOSEFIGURE_set");
1880 }
1881 }
1882
1883 }
1884
1885 rec = U_EMRENDPATH_set(); // there may be only be one of these on a single path
1886 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1887 g_error("Fatal programming error in PrintEmf::print_pathv at U_EMRENDPATH_set");
1888 }
1889 return(0);
1890}
1891
1892// may also be called with a simple_shape or an empty path, whereupon it just returns without doing anything
1893unsigned int PrintEmf::print_pathv(Geom::PathVector const &pathv, const Geom::Affine &transform)
1894{
1895 Geom::Affine tf = transform;
1896 char *rec = nullptr;
1897
1898 simple_shape = print_simple_shape(pathv, tf);
1899 if (simple_shape || pathv.empty()) {
1900 if (use_fill) {
1901 destroy_brush(); // these must be cleared even if nothing is drawn or hbrush,hpen fill up
1902 }
1903 if (use_stroke) {
1904 destroy_pen();
1905 }
1906 return TRUE;
1907 }
1908
1909 (void) draw_pathv_to_EMF(pathv, tf);
1910
1911 // explicit FILL/STROKE commands are needed for each sub section of the path
1912 if (use_fill && !use_stroke) {
1913 rec = U_EMRFILLPATH_set(U_RCL_DEF);
1914 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1915 g_error("Fatal programming error in PrintEmf::fill at U_EMRFILLPATH_set");
1916 }
1917 } else if (use_fill && use_stroke) {
1918 rec = U_EMRSTROKEANDFILLPATH_set(U_RCL_DEF);
1919 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1920 g_error("Fatal programming error in PrintEmf::stroke at U_EMRSTROKEANDFILLPATH_set");
1921 }
1922 } else if (!use_fill && use_stroke) {
1923 rec = U_EMRSTROKEPATH_set(U_RCL_DEF);
1924 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1925 g_error("Fatal programming error in PrintEmf::stroke at U_EMRSTROKEPATH_set");
1926 }
1927 }
1928
1929 // clean out brush and pen, but only after all parts of the draw complete
1930 if (use_fill) {
1931 destroy_brush();
1932 }
1933 if (use_stroke) {
1934 destroy_pen();
1935 }
1936
1937 return TRUE;
1938}
1939
1940
1941unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *text, Geom::Point const &p,
1942 SPStyle const *const style)
1943{
1944 if (!et || !text) {
1945 return 0;
1946 }
1947
1948 do_clip_if_present(style); // If clipping is needed set it up
1949 char *rec = nullptr;
1950 int ccount, newfont;
1951 int fix90n = 0;
1952 uint32_t hfont = 0;
1953 Geom::Affine tf = m_tr_stack.top();
1954 double rot = -1800.0 * std::atan2(tf[1], tf[0]) / M_PI; // 0.1 degree rotation, - sign for MM_TEXT
1955 double rotb = -std::atan2(tf[1], tf[0]); // rotation for baseline offset for superscript/subscript, used below
1956 double dx, dy;
1957 double ky;
1958
1959 // the dx array is smuggled in like: text<nul>w1 w2 w3 ...wn<nul><nul>, where the widths are floats 7 characters wide, including the space
1960 int ndx, rtl;
1961 uint32_t *adx;
1962 smuggle_adxkyrtl_out(text, &adx, &ky, &rtl, &ndx, PX2WORLD * std::min(tf.expansionX(), tf.expansionY())); // side effect: free() adx
1963
1964 uint32_t textalignment;
1965 if (rtl > 0) {
1966 textalignment = U_TA_BASELINE | U_TA_LEFT;
1967 } else {
1968 textalignment = U_TA_BASELINE | U_TA_RIGHT | U_TA_RTLREADING;
1969 }
1970 if (textalignment != htextalignment) {
1971 htextalignment = textalignment;
1972 rec = U_EMRSETTEXTALIGN_set(textalignment);
1973 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
1974 g_error("Fatal programming error in PrintEmf::text at U_EMRSETTEXTALIGN_set");
1975 }
1976 }
1977
1978 char *text2 = strdup(text); // because U_Utf8ToUtf16le calls iconv which does not like a const char *
1979 uint16_t *unicode_text = U_Utf8ToUtf16le(text2, 0, nullptr);
1980 free(text2);
1981 //translates Unicode to NonUnicode, if possible. If any translate, all will, and all to
1982 //the same font, because of code in Layout::print
1983 UnicodeToNon(unicode_text, &ccount, &newfont);
1984
1985 //PPT gets funky with text within +-1 degree of a multiple of 90, but only for SOME fonts.Snap those to the central value
1986 //Some funky ones: Arial, Times New Roman
1987 //Some not funky ones: Symbol and Verdana.
1988 //Without a huge table we cannot catch them all, so just the most common problem ones.
1989 FontfixParams params;
1990
1991 if (FixPPTCharPos) {
1992 switch (newfont) {
1993 case CVTSYM:
1994 _lookup_ppt_fontfix("Convert To Symbol", params);
1995 break;
1996 case CVTZDG:
1997 _lookup_ppt_fontfix("Convert To Zapf Dingbats", params);
1998 break;
1999 case CVTWDG:
2000 _lookup_ppt_fontfix("Convert To Wingdings", params);
2001 break;
2002 default: //also CVTNON
2003 _lookup_ppt_fontfix(style->font_family.value(), params);
2004 break;
2005 }
2006 if (params.f2 != 0 || params.f3 != 0) {
2007 int irem = ((int) round(rot)) % 900 ;
2008 if (irem <= 9 && irem >= -9) {
2009 fix90n = 1; //assume vertical
2010 rot = (double)(((int) round(rot)) - irem);
2011 rotb = rot * M_PI / 1800.0;
2012 if (std::abs(rot) == 900.0) {
2013 fix90n = 2;
2014 }
2015 }
2016 }
2017 }
2018
2019 /* Note that text font sizes are stored into the EMF as fairly small integers and that limits their precision.
2020 The EMF output files produced here have been designed so that the integer valued pt sizes
2021 land right on an integer value in the EMF file, so those are exact. However, something like 18.1 pt will be
2022 somewhat off, so that when it is read back in it becomes 18.11 pt. (For instance.)
2023 */
2024 int textheight = round(-style->font_size.computed * PX2WORLD * std::min(tf.expansionX(), tf.expansionY()));
2025
2026 if (!hfont) {
2027 // Get font face name. Use changed font name if unicode mapped to one
2028 // of the special fonts.
2029 uint16_t *wfacename;
2030 if (!newfont) {
2031 wfacename = U_Utf8ToUtf16le(style->font_family.value(), 0, nullptr);
2032 } else {
2033 wfacename = U_Utf8ToUtf16le(FontName(newfont), 0, nullptr);
2034 }
2035
2036 // Scale the text to the minimum stretch. (It tends to stay within bounding rectangles even if
2037 // it was streteched asymmetrically.) Few applications support text from EMF which is scaled
2038 // differently by height/width, so leave lfWidth alone.
2039
2040 U_LOGFONT lf = logfont_set(
2041 textheight,
2042 0,
2043 round(rot),
2044 round(rot),
2045 _translate_weight(style->font_weight.computed),
2046 (style->font_style.computed == SP_CSS_FONT_STYLE_ITALIC),
2047 style->text_decoration_line.underline,
2048 style->text_decoration_line.line_through,
2049 U_DEFAULT_CHARSET,
2050 U_OUT_DEFAULT_PRECIS,
2051 U_CLIP_DEFAULT_PRECIS,
2052 U_DEFAULT_QUALITY,
2053 U_DEFAULT_PITCH | U_FF_DONTCARE,
2054 wfacename);
2055 free(wfacename);
2056
2057 rec = extcreatefontindirectw_set(&hfont, eht, (char *) &lf, nullptr);
2058 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
2059 g_error("Fatal programming error in PrintEmf::text at extcreatefontindirectw_set");
2060 }
2061 }
2062
2063 rec = selectobject_set(hfont, eht);
2064 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
2065 g_error("Fatal programming error in PrintEmf::text at selectobject_set");
2066 }
2067
2068 auto rgb = style->fill.getColor();
2069 // only change the text color when it needs to be changed
2070 if (htextcolor_rgb != rgb) {
2072 rec = U_EMRSETTEXTCOLOR_set(toColorRef(rgb));
2073 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
2074 g_error("Fatal programming error in PrintEmf::text at U_EMRSETTEXTCOLOR_set");
2075 }
2076 }
2077
2078 Geom::Point p2 = p * tf;
2079
2080 //Handle super/subscripts and vertical kerning
2081 /* Previously used this, but vertical kerning was not supported
2082 p2[Geom::X] -= style->baseline_shift.computed * std::sin( rotb );
2083 p2[Geom::Y] -= style->baseline_shift.computed * std::cos( rotb );
2084 */
2085 p2[Geom::X] += ky * std::sin(rotb);
2086 p2[Geom::Y] += ky * std::cos(rotb);
2087
2088 //Conditionally handle compensation for PPT EMF import bug (affects PPT 2003-2010, at least)
2089 if (FixPPTCharPos) {
2090 if (fix90n == 1) { //vertical
2091 dx = 0.0;
2092 dy = params.f3 * style->font_size.computed * std::cos(rotb);
2093 } else if (fix90n == 2) { //horizontal
2094 dx = params.f2 * style->font_size.computed * std::sin(rotb);
2095 dy = 0.0;
2096 } else {
2097 dx = params.f1 * style->font_size.computed * std::sin(rotb);
2098 dy = params.f1 * style->font_size.computed * std::cos(rotb);
2099 }
2100 p2[Geom::X] += dx;
2101 p2[Geom::Y] += dy;
2102 }
2103
2104 p2[Geom::X] = (p2[Geom::X] * PX2WORLD);
2105 p2[Geom::Y] = (p2[Geom::Y] * PX2WORLD);
2106
2107 int32_t const xpos = (int32_t) round(p2[Geom::X]);
2108 int32_t const ypos = (int32_t) round(p2[Geom::Y]);
2109
2110
2111 // The number of characters in the string is a bit fuzzy. ndx, the number of entries in adx is
2112 // the number of VISIBLE characters, since some may combine from the UTF (8 originally,
2113 // now 16) encoding. Conversely strlen() or wchar16len() would give the absolute number of
2114 // encoding characters. Unclear if emrtext wants the former or the latter but for now assume the former.
2115
2116 // This is currently being smuggled in from caller as part of text, works
2117 // MUCH better than the fallback hack below
2118 // uint32_t *adx = dx_set(textheight, U_FW_NORMAL, slen); // dx is needed, this makes one up
2119 char *rec2;
2120 if (rtl > 0) {
2121 rec2 = emrtext_set((U_POINTL) {
2122 xpos, ypos
2123 }, ndx, 2, unicode_text, U_ETO_NONE, U_RCL_DEF, adx);
2124 } else { // RTL text, U_TA_RTLREADING should be enough, but set this one too just in case
2125 rec2 = emrtext_set((U_POINTL) {
2126 xpos, ypos
2127 }, ndx, 2, unicode_text, U_ETO_RTLREADING, U_RCL_DEF, adx);
2128 }
2129 free(unicode_text);
2130 free(adx);
2131 rec = U_EMREXTTEXTOUTW_set(U_RCL_DEF, U_GM_COMPATIBLE, 1.0, 1.0, (PU_EMRTEXT)rec2);
2132 free(rec2);
2133 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
2134 g_error("Fatal programming error in PrintEmf::text at U_EMREXTTEXTOUTW_set");
2135 }
2136
2137 // Must deselect an object before deleting it. Put the default font (back) in.
2138 rec = selectobject_set(U_DEVICE_DEFAULT_FONT, eht);
2139 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
2140 g_error("Fatal programming error in PrintEmf::text at selectobject_set");
2141 }
2142
2143 if (hfont) {
2144 rec = deleteobject_set(&hfont, eht);
2145 if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
2146 g_error("Fatal programming error in PrintEmf::text at deleteobject_set");
2147 }
2148 }
2149
2150 return 0;
2151}
2152
2154{
2155 /* EMF print */
2156 // clang-format off
2158 "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
2159 "<name>Enhanced Metafile Print</name>\n"
2160 "<id>org.inkscape.print.emf</id>\n"
2161 "<param gui-hidden=\"true\" name=\"destination\" type=\"string\"></param>\n"
2162 "<param gui-hidden=\"true\" name=\"textToPath\" type=\"bool\">true</param>\n"
2163 "<param gui-hidden=\"true\" name=\"pageBoundingBox\" type=\"bool\">true</param>\n"
2164 "<param gui-hidden=\"true\" name=\"FixPPTCharPos\" type=\"bool\">false</param>\n"
2165 "<param gui-hidden=\"true\" name=\"FixPPTDashLine\" type=\"bool\">false</param>\n"
2166 "<param gui-hidden=\"true\" name=\"FixPPTGrad2Polys\" type=\"bool\">false</param>\n"
2167 "<param gui-hidden=\"true\" name=\"FixPPTLinGrad\" type=\"bool\">false</param>\n"
2168 "<param gui-hidden=\"true\" name=\"FixPPTPatternAsHatch\" type=\"bool\">false</param>\n"
2169 "<param gui-hidden=\"true\" name=\"FixImageRot\" type=\"bool\">false</param>\n"
2170 "<print/>\n"
2171 "</inkscape-extension>", std::make_unique<PrintEmf>());
2172 // clang-format on
2173}
2174
2175} /* namespace Internal */
2176} /* namespace Extension */
2177} /* namespace Inkscape */
2178
2179
2180/*
2181 Local Variables:
2182 mode:c++
2183 c-file-style:"stroustrup"
2184 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2185 indent-tabs-mode:nil
2186 fill-column:99
2187 End:
2188*/
2189// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Path - a sequence of contiguous curves.
FillRule
Definition LivarotDefs.h:67
@ fill_oddEven
Definition LivarotDefs.h:68
@ fill_nonZero
Definition LivarotDefs.h:69
@ bool_op_inters
Definition LivarotDefs.h:78
@ bool_op_union
Definition LivarotDefs.h:77
double scale
Definition aa.cpp:228
Cairo integration helpers.
Fragment fragment
Definition canvas.cpp:136
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord expansionX() const
Calculates the amount of x-scaling imparted by the Affine.
Definition affine.cpp:64
Coord expansionY() const
Calculates the amount of y-scaling imparted by the Affine.
Definition affine.cpp:71
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent of the rectangle.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Sequence of subpaths.
Definition pathvector.h:122
Sequence::const_iterator const_iterator
Definition pathvector.h:127
bool empty() const
Check whether the vector contains any paths.
Definition pathvector.h:145
iterator begin()
Definition pathvector.h:151
iterator end()
Definition pathvector.h:152
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
const_iterator end_open() const
Definition path.h:467
void append(Curve *curve)
Add a new curve to the end of the path.
Definition path.h:750
const_iterator begin() const
Definition path.h:464
void start(Point const &p)
Definition path.cpp:426
Function defined as discrete pieces.
Definition piecewise.h:71
void concat(const Piecewise< T > &other)
Definition piecewise.h:235
Two-dimensional point that doubles as a vector.
Definition point.h:66
constexpr Point cw() const
Return a point like this point but rotated +90 degrees.
Definition point.h:137
Axis aligned, non-empty rectangle.
Definition rect.h:92
Rotation around the origin.
Definition transforms.h:187
Scaling from the origin.
Definition transforms.h:150
Geom::PathVector merge_PathVector_with_group(Geom::PathVector const &combined_pathvector, SPItem const *item, const Geom::Affine &transform)
unsigned int image(Inkscape::Extension::Print *module, unsigned char *px, unsigned int w, unsigned int h, unsigned int rs, Geom::Affine const &transform, SPStyle const *style) override
Some parts based on win32.cpp by Lauris Kaplinski lauris@kaplinski.com.
int create_brush(SPStyle const *style, PU_COLORREF fcolor) override
unsigned int fill(Inkscape::Extension::Print *module, Geom::PathVector const &pathv, Geom::Affine const &ctm, SPStyle const *style, Geom::OptRect const &pbox, Geom::OptRect const &dbox, Geom::OptRect const &bbox) override
unsigned int setup(Inkscape::Extension::Print *module) override
unsigned int draw_pathv_to_EMF(Geom::PathVector const &pathv, const Geom::Affine &transform)
Geom::PathVector merge_PathVector_with_shape(Geom::PathVector const &combined_pathvector, SPItem const *item, const Geom::Affine &transform)
Geom::Path pathv_to_rect(Geom::PathVector const &pathv, bool *is_rect, double *angle)
int vector_rect_alignment(double angle, Geom::Point vtest)
unsigned int text(Inkscape::Extension::Print *module, char const *text, Geom::Point const &p, SPStyle const *style) override
Geom::Path pathv_to_simple_polygon(Geom::PathVector const &pathv, int *vertices)
unsigned int stroke(Inkscape::Extension::Print *module, Geom::PathVector const &pathv, Geom::Affine const &ctm, SPStyle const *style, Geom::OptRect const &pbox, Geom::OptRect const &dbox, Geom::OptRect const &bbox) override
unsigned int print_pathv(Geom::PathVector const &pathv, const Geom::Affine &transform)
bool print_simple_shape(Geom::PathVector const &pathv, const Geom::Affine &transform)
int create_pen(SPStyle const *style, const Geom::Affine &transform) override
static void smuggle_adxkyrtl_out(const char *string, uint32_t **adx, double *ky, int *rtl, int *ndx, float scale)
Definition emf-print.cpp:82
void do_clip_if_present(SPStyle const *style)
U_TRIVERTEX make_trivertex(Geom::Point Pt, U_COLORREF uc)
unsigned int begin(Inkscape::Extension::Print *module, SPDocument *doc) override
Geom::Point get_pathrect_corner(Geom::Path pathRect, double angle, int corner)
unsigned int finish(Inkscape::Extension::Print *module) override
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)
static Geom::PathVector rect_cutter(Geom::Point ctr, Geom::Point pos, Geom::Point neg, Geom::Point width)
std::optional< Inkscape::Colors::Color > htextcolor_rgb
static U_COLORREF _gethexcolor(uint32_t color)
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)
static FillRule SPWR_to_LVFR(SPWindRule wr)
U_COLORREF weight_colors(U_COLORREF c1, U_COLORREF c2, double t)
static Geom::PathVector center_elliptical_hole_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
guchar const * pixels() const
PixelFormat pixelFormat() const
Definition cairo-utils.h:65
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition units.cpp:525
Interface for refcounted XML nodes.
Definition node.h:80
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
Typed SVG document implementation.
Definition document.h:103
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:202
Geom::OptRect preferredBounds() const
Definition document.cpp:978
Geom::Scale getDocumentScale(bool computed=true) const
Returns document scale as defined by width/height (in pixels) and viewBox (real world to user-units).
Definition document.cpp:773
Inkscape::XML::Node * getReprNamedView()
Definition document.cpp:223
SPGradientVector vector
Linear and Radial Gradients.
void ensureVector()
Forces vector to be built, if not present (i.e.
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine transform
Definition sp-item.h:138
Geom::OptRect desktopVisualBounds() const
Get item's visual bbox in desktop coordinate system.
Definition sp-item.cpp:1056
SPClipPath * getClipObject() const
Definition sp-item.cpp:102
Linear gradient.
SPObject * parent
Definition sp-object.h:189
ChildrenList children
Definition sp-object.h:907
Radial gradient.
An SVG style object.
Definition style.h:45
T< SPAttr::TEXT_DECORATION_LINE, SPITextDecorationLine > text_decoration_line
CSS 3 2.1, 2.2, 2.3.
Definition style.h:191
T< SPAttr::FONT_WEIGHT, SPIEnum< SPCSSFontWeight > > font_weight
Weight of the font.
Definition style.h:112
T< SPAttr::FILL, SPIPaint > fill
fill
Definition style.h:240
T< SPAttr::STROKE_DASHARRAY, SPIDashArray > stroke_dasharray
stroke-dasharray
Definition style.h:257
T< SPAttr::STROKE, SPIPaint > stroke
stroke
Definition style.h:247
SPObject * object
Object we are attached to.
Definition style.h:84
T< SPAttr::FONT_FAMILY, SPIString > font_family
Font family.
Definition style.h:120
T< SPAttr::STROKE_WIDTH, SPILength > stroke_width
stroke-width
Definition style.h:249
T< SPAttr::FILL_RULE, SPIEnum< SPWindRule > > fill_rule
fill-rule: 0 nonzero, 1 evenodd
Definition style.h:244
T< SPAttr::FILL_OPACITY, SPIScale24 > fill_opacity
fill-opacity
Definition style.h:242
T< SPAttr::FONT_STYLE, SPIEnum< SPCSSFontStyle > > font_style
Font style.
Definition style.h:108
T< SPAttr::STROKE_LINEJOIN, SPIEnum< SPStrokeJoinType > > stroke_linejoin
stroke-linejoin
Definition style.h:253
T< SPAttr::STROKE_MITERLIMIT, SPIFloat > stroke_miterlimit
stroke-miterlimit
Definition style.h:255
T< SPAttr::STROKE_LINECAP, SPIEnum< SPStrokeCapType > > stroke_linecap
stroke-linecap
Definition style.h:251
T< SPAttr::FONT_SIZE, SPIFontSize > font_size
Size of the font.
Definition style.h:116
const double w
Definition conic-4.cpp:19
vector< vpsc::Rectangle * > rs
Include all curve types.
double c[8][4]
Enhanced Metafile printing - implementation.
Specific curve type functions for Inkscape, not provided by lib2geom.
bool is_straight_curve(Geom::BezierCurve const &c)
Definition geom-curves.h:22
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Geom::PathVector pathv_to_linear_and_cubic_beziers(Geom::PathVector const &pathv)
Definition geom.cpp:588
Specific geometry functions for Inkscape, not provided my lib2geom.
SPItem * item
Mini static library that contains the version of Inkscape.
Geom::Point start
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
PathVector path_from_piecewise(Piecewise< D2< SBasis > > const &B, double tol, bool only_cubicbeziers=false)
Make a path from a d2 sbasis.
Point unit_vector(Point const &a)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
A set of useful color modifying functions which do not fit as generic methods on the color class itse...
Definition profile.cpp:24
U_COLORREF toColorRef(std::optional< Colors::Color > color)
static EMFHANDLES * eht
Definition emf-print.cpp:80
void build_from_mem(gchar const *buffer, std::unique_ptr< Implementation::Implementation > in_imp)
Create a module from a buffer holding an XML description.
Definition system.cpp:459
Helper class to stream background task notifications as a series of messages.
char const * version_string
full version string
Geom::PathVector sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, BooleanOp bop, FillRule fra, FillRule frb)
Perform a boolean operation on two pathvectors.
Boolean operations.
TODO: insert short description here.
PathVector - a sequence of subpaths.
Ocnode * child[8]
Definition quantize.cpp:33
RGB rgb
Definition quantize.cpp:36
Axis-aligned rectangle.
double ang(Point n1, Point n2)
Definition sanitize.cpp:89
Conversion between SBasis and Bezier basis polynomials.
SVG <image> implementation.
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
TODO: insert short description here.
SVG <pattern> implementation.
TODO: insert short description here.
SPRoot: SVG <svg> implementation.
The various create functions need a place to put their handles, these are stored in the table below.
Definition uemf.h:3327
Storage for keeping track of properties of the growing EMF file as records are added.
Definition uemf.h:3306
std::vector< SPGradientStop > stops
WMF manual 2.2.2.3.
Definition uemf.h:422
For U_EMR_* OffBmi* fields.
Definition uemf.h:2045
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
For U_EMR[POLY]EXTTEXTOUT[A|W] emrtext field.
Definition uemf.h:1729
General form of an EMF record.
Definition uemf.h:2071
For U_EMREXTCREATEPEN lopn field.
Definition uemf.h:2026
For U_EMRGRADIENTFILL GradObj field.
Definition uemf.h:1781
uint32_t UpperLeft
Index of UL corner in an array of U_TRIVERTEX objects.
Definition uemf.h:1782
uint32_t LowerRight
Index of LR corner in an array of U_TRIVERTEX objects.
Definition uemf.h:1783
For U_EMRCREATEBRUSHINDIRECT lb field.
Definition uemf.h:1813
For U_LOGFONT elfLogFont field.
Definition uemf.h:1827
WMF manual 2.2.2.15.
Definition uemf.h:546
int32_t x
X value.
Definition uemf.h:547
int32_t y
Y value.
Definition uemf.h:548
WMF manual 2.2.2.19.
Definition uemf.h:576
int32_t left
left coordinate
Definition uemf.h:577
int32_t right
right coordinate
Definition uemf.h:579
int32_t bottom
bottom coordinate
Definition uemf.h:580
int32_t top
top coordinate
Definition uemf.h:578
WMF manual 2.2.2.20.
Definition uemf.h:592
WMF manual 2.2.2.22.
Definition uemf.h:608
int32_t cy
Y size.
Definition uemf.h:610
int32_t cx
X size.
Definition uemf.h:609
For GRADIENT_[TRIANGLE|U_RECT].
Definition uemf.h:1766
uint16_t Green
Green component.
Definition uemf.h:1770
int32_t y
Y coord.
Definition uemf.h:1768
uint16_t Red
Red component.
Definition uemf.h:1769
int32_t x
X coord.
Definition uemf.h:1767
uint16_t Alpha
Alpha Transparency.
Definition uemf.h:1772
uint16_t Blue
Bule component.
Definition uemf.h:1771
For U_EMR[FILLRGN|STRETCHBLT|MASKBLT|PLGBLT] xformSrc field.
Definition uemf.h:2011
U_FLOAT eDx
X offset in logical units.
Definition uemf.h:2016
U_FLOAT eM12
Matrix element M12.
Definition uemf.h:2013
U_FLOAT eM21
Matrix element M21.
Definition uemf.h:2014
U_FLOAT eM11
Matrix element M11.
Definition uemf.h:2012
U_FLOAT eDy
Y offset in logical units.
Definition uemf.h:2017
U_FLOAT eM22
Matrix element M22.
Definition uemf.h:2015
SPStyle enums: named public enums that correspond to SVG property values.
SPWindRule
Definition style-enums.h:23
@ SP_CSS_FONT_STYLE_ITALIC
Definition style-enums.h:62
SPStyle - a style object for SPItem objects.
parse SVG path specifications
Enhanced Metafile Input/Output.
@ CVTZDG
@ CVTWDG
@ CVTSYM
void UnicodeToNon(uint16_t *text, int *ecount, int *edest)
char * FontName(int code)
void dot(Cairo::RefPtr< Cairo::Context > &cr, double x, double y)
double height
double width
struct U_PAIR U_POINTL
WMF manual 2.2.2.15.
uint32_t U_STYLEENTRY
StyleEntry For U_EXTLOGPEN.
Definition uemf.h:223
uint32_t U_NUM_STYLEENTRY
Number of U_STYLEENTRY.
Definition uemf.h:232
size_t wchar16len(const uint16_t *src)
uint16_t * U_Utf8ToUtf16le(const char *src, size_t max, size_t *len)
int U_Utf16leEdit(uint16_t *src, uint16_t find, uint16_t replace)