Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
wmf-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/*
16 * References:
17 * - How to Create & Play Enhanced Metafiles in Win32
18 * http://support.microsoft.com/kb/q145999/
19 * - INFO: Windows Metafile Functions & Aldus Placeable Metafiles
20 * http://support.microsoft.com/kb/q66949/
21 * - Metafile Functions
22 * http://msdn.microsoft.com/library/en-us/gdi/metafile_0whf.asp
23 * - Metafile Structures
24 * http://msdn.microsoft.com/library/en-us/gdi/metafile_5hkj.asp
25 */
26
29
30#include <2geom/path.h>
31#include <2geom/pathvector.h>
32#include <2geom/rect.h>
33#include <2geom/curves.h>
34#include "helper/geom.h"
35#include "helper/geom-curves.h"
36
37#include "inkscape-version.h"
38
39#include "util/units.h"
40
41#include "extension/system.h"
42#include "extension/print.h"
43#include "document.h"
44#include "path-prefix.h"
45
46#include "object/sp-pattern.h"
47#include "object/sp-image.h"
48#include "object/sp-gradient.h"
51#include "object/sp-root.h"
52#include "object/sp-item.h"
53
54#include "path/path-boolop.h"
55
56#include <2geom/svg-path-parser.h> // to get from SVG text to Geom::Path
57
58#include "display/cairo-utils.h" // for Inkscape::Pixbuf::PF_CAIRO
59
60#include "wmf-print.h"
61
62#include <cstring>
64
65namespace Inkscape {
66namespace Extension {
67namespace Internal {
68
69#define PXPERMETER 2835
70#define MAXDISP 2.0 // This should be set in the output dialog. This is ok for experimenting, no more than 2 pixel deviation. Not actually used at present
71
72
73/* globals */
74static double PX2WORLD; // value set in begin()
76static WMFTRACK *wt = nullptr;
77static WMFHANDLES *wht = nullptr;
78
79void PrintWmf::smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *rtl, int *ndx, float scale)
80{
81 float fdx;
82 int i;
83 int16_t *ladx;
84 const char *cptr = &string[strlen(string) + 1]; // this works because of the first fake terminator
85
86 *adx = nullptr;
87 *ky = 0.0; // set a default value
88 sscanf(cptr, "%7d", ndx);
89 if (!*ndx) {
90 return; // this could happen with an empty string
91 }
92 cptr += 7;
93 ladx = (int16_t *) malloc(*ndx * sizeof(int16_t));
94 if (!ladx) {
95 g_error("Out of memory");
96 }
97 *adx = ladx;
98 for (i = 0; i < *ndx; i++, cptr += 7, ladx++) {
99 sscanf(cptr, "%7f", &fdx);
100 *ladx = (int16_t) round(fdx * scale);
101 }
102 cptr++; // skip 2nd fake terminator
103 sscanf(cptr, "%7f", &fdx);
104 *ky = fdx;
105 cptr += 7; // advance over ky and its space
106 sscanf(cptr, "%07d", rtl);
107}
108
110{
111 // all of the class variables are initialized elsewhere, many in PrintWmf::Begin,
112}
113
114
116{
117 return TRUE;
118}
119
120
122{
123 char *rec;
124 gchar const *utf8_fn = mod->get_param_string("destination");
125
126 // Typically PX2WORLD is 1200/90, using inkscape's default dpi
127 PX2WORLD = 1200.0 / Inkscape::Util::Quantity::convert(1.0, "in", "px");
128 FixPPTCharPos = mod->get_param_bool("FixPPTCharPos");
129 FixPPTDashLine = mod->get_param_bool("FixPPTDashLine");
130 FixPPTGrad2Polys = mod->get_param_bool("FixPPTGrad2Polys");
131 FixPPTPatternAsHatch = mod->get_param_bool("FixPPTPatternAsHatch");
132
133 (void) wmf_start(utf8_fn, 1000000, 250000, &wt); // Initialize the wt structure
134 (void) wmf_htable_create(128, 128, &wht); // Initialize the wht structure
135
136 // WMF header the only things that can be set are the page size in inches (w,h) and the dpi
137 // width and height in px
138
139 // initialize a few global variables
140 hbrush = hpen = 0;
141 htextalignment = U_TA_BASELINE | U_TA_LEFT;
143
145 if (nv) {
146 const char *p1 = nv->attribute("pagecolor");
147 char *p2;
148 uint32_t lc = strtoul(&p1[1], &p2, 16); // it looks like "#ABC123"
149 if (*p2) {
150 lc = 0;
151 }
152 gv.bgc = _gethexcolor(lc);
153 gv.rgb[0] = (float) U_RGBAGetR(gv.bgc) / 255.0;
154 gv.rgb[1] = (float) U_RGBAGetG(gv.bgc) / 255.0;
155 gv.rgb[2] = (float) U_RGBAGetB(gv.bgc) / 255.0;
156 }
157
158 bool pageBoundingBox;
159 pageBoundingBox = mod->get_param_bool("pageBoundingBox");
160
161 Geom::Rect d;
162 if (pageBoundingBox) {
163 d = *(doc->preferredBounds());
164 } else {
165 SPItem *doc_item = doc->getRoot();
166 Geom::OptRect bbox = doc_item->desktopVisualBounds();
167 if (bbox) {
168 d = *bbox;
169 }
170 }
171
172 d *= Geom::Scale(Inkscape::Util::Quantity::convert(1, "px", "in")); // 90 dpi inside inkscape, wmf file will be 1200 dpi
173
174 /* -1/1200 in next two lines so that WMF read in will write out again at exactly the same size */
175 float dwInchesX = d.width() - 1.0 / 1200.0;
176 float dwInchesY = d.height() - 1.0 / 1200.0;
177 int dwPxX = round(dwInchesX * 1200.0);
178 int dwPxY = round(dwInchesY * 1200.0);
179#if 0
180 float dwInchesX = d.width();
181 float dwInchesY = d.height();
182 int dwPxX = round(d.width() * 1200.0);
183 int dwPxY = round(d.height() * 1200.0);
184#endif
185
186 U_PAIRF *ps = U_PAIRF_set(dwInchesX, dwInchesY);
187 rec = U_WMRHEADER_set(ps, 1200); // Example: drawing is A4 horizontal, 1200 dpi
188 free(ps);
189 if (!rec) {
190 g_warning("Failed in PrintWmf::begin at WMRHEADER");
191 return -1;
192 }
193 (void) wmf_header_append((U_METARECORD *)rec, wt, 1);
194
195 rec = U_WMRSETWINDOWEXT_set(point16_set(dwPxX, dwPxY));
196 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
197 g_warning("Failed in PrintWmf::begin at WMRSETWINDOWEXT");
198 return -1;
199 }
200
201 rec = U_WMRSETWINDOWORG_set(point16_set(0, 0));
202 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
203 g_warning("Failed in PrintWmf::begin at WMRSETWINDOWORG");
204 return -1;
205 }
206
207 rec = U_WMRSETMAPMODE_set(U_MM_ANISOTROPIC);
208 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
209 g_warning("Failed in PrintWmf::begin at WMRSETMAPMODE");
210 return -1;
211 }
212
213 /* set some parameters, else the program that reads the WMF may default to other values */
214
215 rec = U_WMRSETBKMODE_set(U_TRANSPARENT);
216 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
217 g_warning("Failed in PrintWmf::begin at U_WMRSETBKMODE");
218 return -1;
219 }
220
221 hpolyfillmode = U_WINDING;
222 rec = U_WMRSETPOLYFILLMODE_set(U_WINDING);
223 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
224 g_warning("Failed in PrintWmf::begin at U_WMRSETPOLYFILLMODE");
225 return -1;
226 }
227
228 // Text alignment: (only changed if RTL text is encountered )
229 // - (x,y) coordinates received by this filter are those of the point where the text
230 // actually starts, and already takes into account the text object's alignment;
231 // - for this reason, the WMF text alignment must always be TA_BASELINE|TA_LEFT.
232 rec = U_WMRSETTEXTALIGN_set(U_TA_BASELINE | U_TA_LEFT);
233 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
234 g_warning("Failed in PrintWmf::begin at U_WMRSETTEXTALIGN_set");
235 return -1;
236 }
237
238 htextcolor_rgb.reset();
239 rec = U_WMRSETTEXTCOLOR_set(U_RGB(0, 0, 0));
240 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
241 g_warning("Failed in PrintWmf::begin at U_WMRSETTEXTCOLOR_set");
242 return -1;
243 }
244
245 rec = U_WMRSETROP2_set(U_R2_COPYPEN);
246 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
247 g_warning("Failed in PrintWmf::begin at U_WMRSETROP2");
248 return -1;
249 }
250
251 hmiterlimit = 5;
252 rec = wmiterlimit_set(5);
253 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
254 g_warning("Failed in PrintWmf::begin at wmiterlimit_set");
255 return -1;
256 }
257
258
259 // create a pen as object 0. We never use it (except by mistake). Its purpose it to make all of the other object indices >=1
260 U_PEN up = U_PEN_set(U_PS_SOLID, 1, colorref_set(0, 0, 0));
261 uint32_t Pen;
262 rec = wcreatepenindirect_set(&Pen, wht, up);
263 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
264 g_warning("Failed in PrintWmf::begin at wcreatepenindirect_set");
265 return -1;
266 }
267
268 // create a null pen. If no specific pen is set, this is used
269 up = U_PEN_set(U_PS_NULL, 1, colorref_set(0, 0, 0));
270 rec = wcreatepenindirect_set(&hpen_null, wht, up);
271 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
272 g_warning("Failed in PrintWmf::begin at wcreatepenindirect_set");
273 return -1;
274 }
275 destroy_pen(); // make this pen active
276
277 // create a null brush. If no specific brush is set, this is used
278 U_WLOGBRUSH lb = U_WLOGBRUSH_set(U_BS_NULL, U_RGB(0, 0, 0), U_HS_HORIZONTAL);
279 rec = wcreatebrushindirect_set(&hbrush_null, wht, lb);
280 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
281 g_warning("Failed in PrintWmf::begin at wcreatebrushindirect_set");
282 return -1;
283 }
284 destroy_brush(); // make this brush active
285
286 return 0;
287}
288
289
291{
292 char *rec;
293 if (!wt) {
294 return 0;
295 }
296
297 // get rid of null brush
298 rec = wdeleteobject_set(&hbrush_null, wht);
299 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
300 g_error("Fatal programming error in PrintWmf::finish at wdeleteobject_set null brush");
301 }
302
303 // get rid of null pen
304 rec = wdeleteobject_set(&hpen_null, wht);
305 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
306 g_error("Fatal programming error in PrintWmf::finish at wdeleteobject_set null pen");
307 }
308
309 // get rid of object 0, which was a pen that was used to shift the other object indices to >=1.
310 hpen = 0;
311 rec = wdeleteobject_set(&hpen, wht);
312 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
313 g_error("Fatal programming error in PrintWmf::finish at wdeleteobject_set filler object");
314 }
315
316 rec = U_WMREOF_set(); // generate the EOF record
317 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
318 g_error("Fatal programming error in PrintWmf::finish");
319 }
320 (void) wmf_finish(wt); // Finalize and write out the WMF
321 uwmf_free(&wt); // clean up
322 wmf_htable_free(&wht); // clean up
323
324 return 0;
325}
326
327// fcolor is defined when gradients are being expanded, it is the color of one stripe or ring.
328int PrintWmf::create_brush(SPStyle const *style, U_COLORREF *fcolor)
329{
330 char *rec;
331 U_WLOGBRUSH lb;
332 uint32_t brush, fmode;
333 MFDrawMode fill_mode;
334 Inkscape::Pixbuf const *pixbuf;
335 uint32_t brushStyle;
336 int hatchType;
337 U_COLORREF hatchColor;
338 U_COLORREF bkColor;
339 uint32_t width = 0; // quiets a harmless compiler warning, initialization not otherwise required.
340 uint32_t height = 0;
341
342 if (!wt) {
343 return 0;
344 }
345
346 // set a default fill in case we can't figure out a better way to do it
347 fmode = U_ALTERNATE;
348 fill_mode = DRAW_PAINT;
349 brushStyle = U_BS_SOLID;
350 hatchType = U_HS_SOLIDCLR;
351 bkColor = U_RGB(0, 0, 0);
352 if (fcolor) {
353 hatchColor = *fcolor;
354 } else {
355 hatchColor = U_RGB(0, 0, 0);
356 }
357
358 if (!fcolor && style) {
359 if (style->fill.isColor()) {
360 fill_mode = DRAW_PAINT;
361 /* Dead assignment: Value stored to 'opacity' is never read
362 float opacity = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
363 if (opacity <= 0.0) {
364 opacity = 0.0; // basically the same as no fill
365 }
366 */
367 hatchColor = toColorRef(style->fill.getColor());
368
369 fmode = style->fill_rule.computed == 0 ? U_WINDING : (style->fill_rule.computed == 2 ? U_ALTERNATE : U_ALTERNATE);
370 } else if (is<SPPattern>(SP_STYLE_FILL_SERVER(style))) { // must be paint-server
371 SPPaintServer *paintserver = style->fill.href->getObject();
372 auto pat = cast<SPPattern>(paintserver);
373 double dwidth = pat->width();
374 double dheight = pat->height();
375 width = dwidth;
376 height = dheight;
377 brush_classify(pat, 0, &pixbuf, &hatchType, &hatchColor, &bkColor);
378 if (pixbuf) {
379 fill_mode = DRAW_IMAGE;
380 } else { // pattern
381 fill_mode = DRAW_PATTERN;
382 if (hatchType == -1) { // Not a standard hatch, so force it to something
383 hatchType = U_HS_CROSS;
384 hatchColor = U_RGB(0xFF, 0xC3, 0xC3);
385 }
386 }
388 if (hatchType == -1) { // image or unclassified
389 fill_mode = DRAW_PATTERN;
390 hatchType = U_HS_DIAGCROSS;
391 hatchColor = U_RGB(0xFF, 0xC3, 0xC3);
392 }
393 }
394 brushStyle = U_BS_HATCHED;
395 } else if (is<SPGradient>(SP_STYLE_FILL_SERVER(style))) { // must be a gradient
396 // currently we do not do anything with gradients, the code below just sets the color to the average of the stops
397 SPPaintServer *paintserver = style->fill.href->getObject();
398 SPLinearGradient *lg = nullptr;
399 SPRadialGradient *rg = nullptr;
400
401 if (is<SPLinearGradient>(paintserver)) {
402 lg = cast<SPLinearGradient>(paintserver);
403 lg->ensureVector(); // when exporting from commandline, vector is not built
404 fill_mode = DRAW_LINEAR_GRADIENT;
405 } else if (is<SPRadialGradient>(paintserver)) {
406 rg = cast<SPRadialGradient>(paintserver);
407 rg->ensureVector(); // when exporting from commandline, vector is not built
408 fill_mode = DRAW_RADIAL_GRADIENT;
409 } else {
410 // default fill
411 }
412
413 if (rg) {
414 if (FixPPTGrad2Polys) {
415 return hold_gradient(rg, fill_mode);
416 } else {
417 hatchColor = avg_stop_color(rg);
418 }
419 } else if (lg) {
420 if (FixPPTGrad2Polys) {
421 return hold_gradient(lg, fill_mode);
422 } else {
423 hatchColor = avg_stop_color(lg);
424 }
425 }
426 }
427 } else { // if (!style)
428 // default fill
429 }
430
431 switch (fill_mode) {
432 case DRAW_LINEAR_GRADIENT: // fill with average color unless gradients are converted to slices
433 case DRAW_RADIAL_GRADIENT: // ditto
434 case DRAW_PAINT:
435 case DRAW_PATTERN:
436 // SVG text has no background attribute, so OPAQUE mode ALWAYS cancels after the next draw, otherwise it would mess up future text output.
437 if (usebk) {
438 rec = U_WMRSETBKCOLOR_set(bkColor);
439 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
440 g_error("Fatal programming error in PrintWmf::create_brush at U_WMRSETBKCOLOR_set");
441 }
442 rec = U_WMRSETBKMODE_set(U_OPAQUE);
443 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
444 g_error("Fatal programming error in PrintWmf::create_brush at U_WMRSETBKMODE_set");
445 }
446 }
447 lb = U_WLOGBRUSH_set(brushStyle, hatchColor, hatchType);
448 rec = wcreatebrushindirect_set(&brush, wht, lb);
449 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
450 g_error("Fatal programming error in PrintWmf::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 U_RGBQUAD *ct;
459 int numCt;
461 U_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 = wcreatedibpatternbrush_srcdib_set(&brush, wht, U_DIB_RGB_COLORS, Bmi, cbPx, px);
470 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
471 g_error("Fatal programming error in PrintWmf::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 = wselectobject_set(brush, wht);
480 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
481 g_error("Fatal programming error in PrintWmf::create_brush at wselectobject_set");
482 }
483
484 if (fmode != hpolyfillmode) {
485 hpolyfillmode = fmode;
486 rec = U_WMRSETPOLYFILLMODE_set(fmode);
487 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
488 g_error("Fatal programming error in PrintWmf::create_brush at U_WMRSETPOLYFILLMODE_set");
489 }
490 }
491
492 return 0;
493}
494
495
497{
498 char *rec;
499 // WMF lets any object be deleted whenever, and the chips fall where they may...
500 if (hbrush) {
501 rec = wdeleteobject_set(&hbrush, wht);
502 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
503 g_error("Fatal programming error in PrintWmf::destroy_brush");
504 }
505 hbrush = 0;
506 }
507
508 // (re)select the null brush
509
510 rec = wselectobject_set(hbrush_null, wht);
511 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
512 g_error("Fatal programming error in PrintWmf::destroy_brush");
513 }
514}
515
516
517int PrintWmf::create_pen(SPStyle const *style, const Geom::Affine &transform)
518{
519 char *rec = nullptr;
520 uint32_t pen;
521 uint32_t penstyle;
522 U_COLORREF penColor;
523 U_PEN up;
524 int modstyle;
525
526 if (!wt) {
527 return 0;
528 }
529
530 // set a default stroke in case we can't figure out a better way to do it
531 penstyle = U_PS_SOLID;
532 modstyle = 0;
533 penColor = U_RGB(0, 0, 0);
534 uint32_t linewidth = 1;
535
536 if (style) { // override some or all of the preceding
537
538 // WMF does not support hatched, bitmap, or gradient pens, just set the color.
539 penColor = toColorRef(style->stroke.getColor());
540
541 using Geom::X;
542 using Geom::Y;
543
544 Geom::Point zero(0, 0);
545 Geom::Point one(1, 1);
546 Geom::Point p0(zero * transform);
547 Geom::Point p1(one * transform);
548 Geom::Point p(p1 - p0);
549
550 double scale = sqrt((p[X] * p[X]) + (p[Y] * p[Y])) / sqrt(2);
551
552 if (!style->stroke_width.computed) {
553 return 0; //if width is 0 do not (reset) the pen, it should already be NULL_PEN
554 }
555 linewidth = MAX(1, (uint32_t) round(scale * style->stroke_width.computed * PX2WORLD));
556
557 // most WMF readers will ignore linecap and linejoin, but set them anyway. Inkscape itself can read them back in.
558
559 if (style->stroke_linecap.computed == 0) {
560 modstyle |= U_PS_ENDCAP_FLAT;
561 } else if (style->stroke_linecap.computed == 1) {
562 modstyle |= U_PS_ENDCAP_ROUND;
563 } else {
564 modstyle |= U_PS_ENDCAP_SQUARE;
565 }
566
567 if (style->stroke_linejoin.computed == 0) {
568 float miterlimit = style->stroke_miterlimit.value; // This is a ratio.
569 if (miterlimit < 1) {
570 miterlimit = 1;
571 }
572
573 // most WMF readers will ignore miterlimit, but set it anyway. Inkscape itself can read it back in
574 if ((uint32_t)miterlimit != hmiterlimit) {
575 hmiterlimit = (uint32_t)miterlimit;
576 rec = wmiterlimit_set((uint32_t) miterlimit);
577 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
578 g_error("Fatal programming error in PrintWmf::create_pen at wmiterlimit_set");
579 }
580 }
581 modstyle |= U_PS_JOIN_MITER;
582 } else if (style->stroke_linejoin.computed == 1) {
583 modstyle |= U_PS_JOIN_ROUND;
584 } else {
585 modstyle |= U_PS_JOIN_BEVEL;
586 }
587
588 if (!style->stroke_dasharray.values.empty()) {
589 if (!FixPPTDashLine) { // if this is set code elsewhere will break dots/dashes into many smaller lines.
590 int n_dash = style->stroke_dasharray.values.size();
591 /* options are dash, dot, dashdot and dashdotdot. Try to pick the closest one. */
592 int mark_short=INT_MAX;
593 int mark_long =0;
594 int i;
595 for (i=0;i<n_dash;i++) {
596 int mark = style->stroke_dasharray.values[i].value;
597 if (mark > mark_long) {
598 mark_long = mark;
599 }
600 if (mark < mark_short) {
601 mark_short = mark;
602 }
603 }
604 if(mark_long == mark_short){ // only one mark size
605 penstyle = U_PS_DOT;
606 }
607 else if (n_dash==2) {
608 penstyle = U_PS_DASH;
609 }
610 else if (n_dash==4) {
611 penstyle = U_PS_DASHDOT;
612 }
613 else {
614 penstyle = U_PS_DASHDOTDOT;
615 }
616 }
617 }
618
619 }
620
621 up = U_PEN_set(penstyle | modstyle, linewidth, penColor);
622 rec = wcreatepenindirect_set(&pen, wht, up);
623 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
624 g_error("Fatal programming error in PrintWmf::create_pen at wcreatepenindirect_set");
625 }
626
627 rec = wselectobject_set(pen, wht);
628 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
629 g_error("Fatal programming error in PrintWmf::create_pen at wselectobject_set");
630 }
631 hpen = pen; // need this later for destroy_pen
632
633 return 0;
634}
635
636// delete the defined pen object
638{
639 char *rec = nullptr;
640 // WMF lets any object be deleted whenever, and the chips fall where they may...
641 if (hpen) {
642 rec = wdeleteobject_set(&hpen, wht);
643 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
644 g_error("Fatal programming error in PrintWmf::destroy_pen");
645 }
646 hpen = 0;
647 }
648
649 // (re)select the null pen
650
651 rec = wselectobject_set(hpen_null, wht);
652 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
653 g_error("Fatal programming error in PrintWmf::destroy_pen");
654 }
655}
656
657
658unsigned int PrintWmf::fill(
660 Geom::PathVector const &pathv, Geom::Affine const & /*transform*/, SPStyle const *style,
661 Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/)
662{
663 using Geom::X;
664 using Geom::Y;
665
666 Geom::Affine tf = m_tr_stack.top();
667
668 use_fill = true;
669 use_stroke = false;
670
671 fill_transform = tf;
672
673 if (create_brush(style, nullptr)) {
674 /*
675 Handle gradients. Uses modified livarot as 2geom boolops is currently broken.
676 Can handle gradients with multiple stops.
677
678 The overlap is needed to avoid antialiasing artifacts when edges are not strictly aligned on pixel boundaries.
679 There is an inevitable loss of accuracy saving through an WMF file because of the integer coordinate system.
680 Keep the overlap quite large so that loss of accuracy does not remove an overlap.
681 */
682 destroy_pen(); //this sets the NULL_PEN, otherwise gradient slices may display with boundaries, see longer explanation below
683 Geom::Path cutter;
684 U_COLORREF wc, c1, c2;
685 FillRule frb = SPWR_to_LVFR((SPWindRule) style->fill_rule.computed);
686 double doff, doff_base, doff_range;
687 double divisions = 128.0;
688 int nstops;
689 int istop = 1;
690
691 SPRadialGradient *tg = (SPRadialGradient *)(gv.grad); // linear/radial are the same here
692 nstops = tg->vector.stops.size();
693 c1 = toColorRef(tg->vector.stops[0].color);
694 c2 = toColorRef(tg->vector.stops[nstops - 1].color);
695
696 doff = 0.0;
697 doff_base = 0.0;
698 doff_range = tg->vector.stops[1].offset; // next or last stop
699
701 Geom::Point xv = gv.p2 - gv.p1; // X' vector
702 Geom::Point yv = gv.p3 - gv.p1; // Y' vector
703 Geom::Point xuv = Geom::unit_vector(xv); // X' unit vector
704 double rx = hypot(xv[X], xv[Y]);
705 double ry = hypot(yv[X], yv[Y]);
706 double range = fmax(rx, ry); // length along the gradient
707 double step = range / divisions; // adequate approximation for gradient
708 double overlap = step / 4.0; // overlap slices slightly
709 double start;
710 double stop;
711 Geom::PathVector pathvc, pathvr;
712
713 /* radial gradient might stop part way through the shape, fill with outer color from there to "infinity".
714 Do this first so that outer colored ring will overlay it.
715 */
716 pathvc = center_elliptical_hole_as_SVG_PathV(gv.p1, rx * (1.0 - overlap / range), ry * (1.0 - overlap / range), asin(xuv[Y]));
717 pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_oddEven, frb);
718 wc = weight_opacity(c2);
719 (void) create_brush(style, &wc);
721
722 c2 = toColorRef(tg->vector.stops[istop].color);
723
724 for (start = 0.0; start < range; start += step, doff += 1. / divisions) {
725 stop = start + step + overlap;
726 if (stop > range) {
727 stop = range;
728 }
729 wc = weight_colors(c1, c2, (doff - doff_base) / (doff_range - doff_base));
730 (void) create_brush(style, &wc);
731
732 pathvc = center_elliptical_ring_as_SVG_PathV(gv.p1, rx * start / range, ry * start / range, rx * stop / range, ry * stop / range, asin(xuv[Y]));
733
734 pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
735 print_pathv(pathvr, fill_transform); // show the intersection
736
737 if (doff >= doff_range - doff_base) {
738 istop++;
739 if (istop >= nstops) {
740 continue; // could happen on a rounding error
741 }
742 doff_base = doff_range;
743 doff_range = tg->vector.stops[istop].offset; // next or last stop
744 c1 = c2;
745 c2 = toColorRef(tg->vector.stops[istop].color);
746 }
747 }
748 } else if (gv.mode == DRAW_LINEAR_GRADIENT) {
749 Geom::Point uv = Geom::unit_vector(gv.p2 - gv.p1); // unit vector
750 Geom::Point puv = uv.cw(); // perp. to unit vector
751 double range = Geom::distance(gv.p1, gv.p2); // length along the gradient
752 double step = range / divisions; // adequate approximation for gradient
753 double overlap = step / 4.0; // overlap slices slightly
754 double start;
755 double stop;
756 Geom::PathVector pathvc, pathvr;
757
758 /* before lower end of gradient, overlap first slice position */
759 wc = weight_opacity(c1);
760 (void) create_brush(style, &wc);
761 pathvc = rect_cutter(gv.p1, uv * (overlap), uv * (-50000.0), puv * 50000.0);
762 pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
764
765 /* after high end of gradient, overlap last slice position */
766 wc = weight_opacity(c2);
767 (void) create_brush(style, &wc);
768 pathvc = rect_cutter(gv.p2, uv * (-overlap), uv * (50000.0), puv * 50000.0);
769 pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
771
772 c2 = toColorRef(tg->vector.stops[istop].color);
773
774 for (start = 0.0; start < range; start += step, doff += 1. / divisions) {
775 stop = start + step + overlap;
776 if (stop > range) {
777 stop = range;
778 }
779 pathvc = rect_cutter(gv.p1, uv * start, uv * stop, puv * 50000.0);
780
781 wc = weight_colors(c1, c2, (doff - doff_base) / (doff_range - doff_base));
782 (void) create_brush(style, &wc);
784 print_pathv(pathvr, fill_transform); // show the intersection
785
786 if (doff >= doff_range - doff_base) {
787 istop++;
788 if (istop >= nstops) {
789 continue; // could happen on a rounding error
790 }
791 doff_base = doff_range;
792 doff_range = tg->vector.stops[istop].offset; // next or last stop
793 c1 = c2;
794 c2 = toColorRef(tg->vector.stops[istop].color);
795 }
796 }
797 } else {
798 g_error("Fatal programming error in PrintWmf::fill, invalid gradient type detected");
799 }
800 use_fill = false; // gradients handled, be sure stroke does not use stroke and fill
801 } else {
802 /*
803 Inkscape was not calling create_pen for objects with no border.
804 This was because it never called stroke() (next method).
805 PPT, and presumably others, pick whatever they want for the border if it is not specified, so no border can
806 become a visible border.
807 To avoid this force the pen to NULL_PEN if we can determine that no pen will be needed after the fill.
808 */
809 if (style->stroke.noneSet || style->stroke_width.computed == 0.0) {
810 destroy_pen(); //this sets the NULL_PEN
811 }
812
813 /* postpone fill in case stroke also required AND all stroke paths closed
814 Dashes converted to line segments will "open" a closed path.
815 */
816 bool all_closed = true;
817 for (const auto & pit : pathv) {
818 for (Geom::Path::const_iterator cit = pit.begin(); cit != pit.end_open(); ++cit) {
819 if (pit.end_default() != pit.end_closed()) {
820 all_closed = false;
821 }
822 }
823 }
824 if (
825 (style->stroke.isNone() || style->stroke.noneSet || style->stroke_width.computed == 0.0) ||
826 (!style->stroke_dasharray.values.empty() && FixPPTDashLine) ||
827 !all_closed
828 ) {
829 print_pathv(pathv, fill_transform); // do any fills. side effect: clears fill_pathv
830 use_fill = false;
831 }
832 }
833
834 return 0;
835}
836
837
838unsigned int PrintWmf::stroke(
840 Geom::PathVector const &pathv, const Geom::Affine &/*transform*/, const SPStyle *style,
841 Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/)
842{
843
844 char *rec = nullptr;
845 Geom::Affine tf = m_tr_stack.top();
846
847 use_stroke = true;
848 // use_fill was set in ::fill, if it is needed, if not, the null brush is used, it should be already set
849
850 if (create_pen(style, tf)) {
851 return 0;
852 }
853
854 if (!style->stroke_dasharray.values.empty() && FixPPTDashLine) {
855 // convert the path, gets its complete length, and then make a new path with parameter length instead of t
856 Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw; // pathv-> sbasis
857 Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw2; // sbasis using arc length parameter
858 Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw3; // new (discontinuous) path, composed of dots/dashes
859 Geom::Piecewise<Geom::D2<Geom::SBasis> > first_frag; // first fragment, will be appended at end
860 int n_dash = style->stroke_dasharray.values.size();
861 int i = 0; //dash index
862 double tlength; // length of tmp_pathpw
863 double slength = 0.0; // start of gragment
864 double elength; // end of gragment
865 for (const auto & i : pathv) {
866 tmp_pathpw.concat(i.toPwSb());
867 }
868 tlength = length(tmp_pathpw, 0.1);
869 tmp_pathpw2 = arc_length_parametrization(tmp_pathpw);
870
871 // go around the dash array repeatedly until the entire path is consumed (but not beyond).
872 while (slength < tlength) {
873 elength = slength + style->stroke_dasharray.values[i++].value;
874 if (elength > tlength) {
875 elength = tlength;
876 }
877 Geom::Piecewise<Geom::D2<Geom::SBasis> > fragment(portion(tmp_pathpw2, slength, elength));
878 if (slength) {
879 tmp_pathpw3.concat(fragment);
880 } else {
881 first_frag = fragment;
882 }
883 slength = elength;
884 slength += style->stroke_dasharray.values[i++].value; // the gap
885 if (i >= n_dash) {
886 i = 0;
887 }
888 }
889 tmp_pathpw3.concat(first_frag); // may merge line around start point
890 Geom::PathVector out_pathv = Geom::path_from_piecewise(tmp_pathpw3, 0.01);
891 print_pathv(out_pathv, tf);
892 } else {
893 print_pathv(pathv, tf);
894 }
895
896 use_stroke = false;
897 use_fill = false;
898
899 if (usebk) { // OPAQUE was set, revert to TRANSPARENT
900 usebk = false;
901 rec = U_WMRSETBKMODE_set(U_TRANSPARENT);
902 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
903 g_error("Fatal programming error in PrintWmf::stroke at U_WMRSETBKMODE_set");
904 }
905 }
906
907 return 0;
908}
909
910
911// Draws simple_shapes, those with closed WMR_* primitives, like polygons, rectangles and ellipses.
912// These use whatever the current pen/brush are and need not be followed by a FILLPATH or STROKEPATH.
913// For other paths it sets a few flags and returns.
915{
916
917 Geom::PathVector pv = pathv_to_linear(pathv * transform, MAXDISP);
918
919 int nodes = 0;
920 int moves = 0;
921 int lines = 0;
922 int curves = 0;
923 char *rec = nullptr;
924
925 for (const auto & pit : pv) {
926 moves++;
927 nodes++;
928
929 for (Geom::Path::const_iterator cit = pit.begin(); cit != pit.end_open(); ++cit) {
930 nodes++;
931
932 if (is_straight_curve(*cit)) {
933 lines++;
934 } else if (dynamic_cast<Geom::CubicBezier const *>(&*cit)) {
935 curves++;
936 }
937 }
938 }
939
940 if (!nodes) {
941 return false;
942 }
943
944 U_POINT16 *lpPoints = new U_POINT16[moves + lines + curves * 3];
945 int i = 0;
946
949 for (const auto & pit : pv) {
950 using Geom::X;
951 using Geom::Y;
952
953 Geom::Point p0 = pit.initialPoint();
954
955 p0[X] = (p0[X] * PX2WORLD);
956 p0[Y] = (p0[Y] * PX2WORLD);
957
958 int32_t const x0 = (int32_t) round(p0[X]);
959 int32_t const y0 = (int32_t) round(p0[Y]);
960
961 lpPoints[i].x = x0;
962 lpPoints[i].y = y0;
963 i = i + 1;
964
967 for (Geom::Path::const_iterator cit = pit.begin(); cit != pit.end_open(); ++cit) {
968 if (is_straight_curve(*cit)) {
969 //Geom::Point p0 = cit->initialPoint();
970 Geom::Point p1 = cit->finalPoint();
971
972 //p0[X] = (p0[X] * PX2WORLD);
973 p1[X] = (p1[X] * PX2WORLD);
974 //p0[Y] = (p0[Y] * PX2WORLD);
975 p1[Y] = (p1[Y] * PX2WORLD);
976
977 //int32_t const x0 = (int32_t) round(p0[X]);
978 //int32_t const y0 = (int32_t) round(p0[Y]);
979 int32_t const x1 = (int32_t) round(p1[X]);
980 int32_t const y1 = (int32_t) round(p1[Y]);
981
982 lpPoints[i].x = x1;
983 lpPoints[i].y = y1;
984 i = i + 1;
985 } else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*cit)) {
986 std::vector<Geom::Point> points = cubic->controlPoints();
987 //Geom::Point p0 = points[0];
988 Geom::Point p1 = points[1];
989 Geom::Point p2 = points[2];
990 Geom::Point p3 = points[3];
991
992 //p0[X] = (p0[X] * PX2WORLD);
993 p1[X] = (p1[X] * PX2WORLD);
994 p2[X] = (p2[X] * PX2WORLD);
995 p3[X] = (p3[X] * PX2WORLD);
996 //p0[Y] = (p0[Y] * PX2WORLD);
997 p1[Y] = (p1[Y] * PX2WORLD);
998 p2[Y] = (p2[Y] * PX2WORLD);
999 p3[Y] = (p3[Y] * PX2WORLD);
1000
1001 //int32_t const x0 = (int32_t) round(p0[X]);
1002 //int32_t const y0 = (int32_t) round(p0[Y]);
1003 int32_t const x1 = (int32_t) round(p1[X]);
1004 int32_t const y1 = (int32_t) round(p1[Y]);
1005 int32_t const x2 = (int32_t) round(p2[X]);
1006 int32_t const y2 = (int32_t) round(p2[Y]);
1007 int32_t const x3 = (int32_t) round(p3[X]);
1008 int32_t const y3 = (int32_t) round(p3[Y]);
1009
1010 lpPoints[i].x = x1;
1011 lpPoints[i].y = y1;
1012 lpPoints[i + 1].x = x2;
1013 lpPoints[i + 1].y = y2;
1014 lpPoints[i + 2].x = x3;
1015 lpPoints[i + 2].y = y3;
1016 i = i + 3;
1017 }
1018 }
1019 }
1020
1021 bool done = false;
1022 bool closed = (lpPoints[0].x == lpPoints[i - 1].x) && (lpPoints[0].y == lpPoints[i - 1].y);
1023 bool polygon = false;
1024 bool rectangle = false;
1025 bool ellipse = false;
1026
1027 if (moves == 1 && moves + lines == nodes && closed) {
1028 polygon = true;
1029 // if (nodes==5) { // disable due to LP Bug 407394
1030 // if (lpPoints[0].x == lpPoints[3].x && lpPoints[1].x == lpPoints[2].x &&
1031 // lpPoints[0].y == lpPoints[1].y && lpPoints[2].y == lpPoints[3].y)
1032 // {
1033 // rectangle = true;
1034 // }
1035 // }
1036 } else if (moves == 1 && nodes == 5 && moves + curves == nodes && closed) {
1037 // if (lpPoints[0].x == lpPoints[1].x && lpPoints[1].x == lpPoints[11].x &&
1038 // lpPoints[5].x == lpPoints[6].x && lpPoints[6].x == lpPoints[7].x &&
1039 // lpPoints[2].x == lpPoints[10].x && lpPoints[3].x == lpPoints[9].x && lpPoints[4].x == lpPoints[8].x &&
1040 // lpPoints[2].y == lpPoints[3].y && lpPoints[3].y == lpPoints[4].y &&
1041 // lpPoints[8].y == lpPoints[9].y && lpPoints[9].y == lpPoints[10].y &&
1042 // lpPoints[5].y == lpPoints[1].y && lpPoints[6].y == lpPoints[0].y && lpPoints[7].y == lpPoints[11].y)
1043 // { // disable due to LP Bug 407394
1044 // ellipse = true;
1045 // }
1046 }
1047
1048 if (polygon || ellipse) {
1049 // pens and brushes already set by caller, do not touch them
1050
1051 if (polygon) {
1052 if (rectangle) {
1053 U_RECT16 rcl = U_RECT16_set((U_POINT16) {
1054 lpPoints[0].x, lpPoints[0].y
1055 }, (U_POINT16) {
1056 lpPoints[2].x, lpPoints[2].y
1057 });
1058 rec = U_WMRRECTANGLE_set(rcl);
1059 } else {
1060 rec = U_WMRPOLYGON_set(nodes, lpPoints);
1061 }
1062 } else if (ellipse) {
1063 U_RECT16 rcl = U_RECT16_set((U_POINT16) {
1064 lpPoints[6].x, lpPoints[3].y
1065 }, (U_POINT16) {
1066 lpPoints[0].x, lpPoints[9].y
1067 });
1068 rec = U_WMRELLIPSE_set(rcl);
1069 }
1070 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1071 g_error("Fatal programming error in PrintWmf::print_simple_shape at retangle/ellipse/polygon");
1072 }
1073
1074 done = true;
1075
1076 }
1077
1078 delete[] lpPoints;
1079
1080 return done;
1081}
1082
1099unsigned int PrintWmf::image(
1100 Inkscape::Extension::Print * /* module */,
1101 unsigned char *rgba_px,
1102 unsigned int w,
1103 unsigned int h,
1104 unsigned int rs,
1105 Geom::Affine const &tf_rect,
1106 SPStyle const * /*style*/)
1107{
1108 double x1, y1, dw, dh;
1109 char *rec = nullptr;
1110 Geom::Affine tf = m_tr_stack.top();
1111
1112 rec = U_WMRSETSTRETCHBLTMODE_set(U_COLORONCOLOR);
1113 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1114 g_error("Fatal programming error in PrintWmf::image at EMRHEADER");
1115 }
1116
1117 x1 = tf_rect[4];
1118 y1 = tf_rect[5];
1119 dw = ((double) w) * tf_rect[0];
1120 dh = ((double) h) * tf_rect[3];
1121 Geom::Point pLL(x1, y1);
1122 Geom::Point pLL2 = pLL * tf; //location of LL corner in Inkscape coordinates
1123
1124 /* adjust scale of w and h. This works properly when there is no rotation. The values are
1125 a bit strange when there is rotation, but since WMF cannot handle rotation in any case, all
1126 answers are equally wrong.
1127 */
1128 Geom::Point pWH(dw, dh);
1129 Geom::Point pWH2 = pWH * tf.withoutTranslation();
1130
1131 char *px;
1132 uint32_t cbPx;
1133 uint32_t colortype;
1134 U_RGBQUAD *ct;
1135 int numCt;
1136 U_BITMAPINFOHEADER Bmih;
1137 U_BITMAPINFO *Bmi;
1138 colortype = U_BCBM_COLOR32;
1139 (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, (char *) rgba_px, w, h, w * 4, colortype, 0, 1);
1140 Bmih = bitmapinfoheader_set(w, h, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0);
1141 Bmi = bitmapinfo_set(Bmih, ct);
1142
1143 U_POINT16 Dest = point16_set(round(pLL2[Geom::X] * PX2WORLD), round(pLL2[Geom::Y] * PX2WORLD));
1144 U_POINT16 cDest = point16_set(round(pWH2[Geom::X] * PX2WORLD), round(pWH2[Geom::Y] * PX2WORLD));
1145 U_POINT16 Src = point16_set(0, 0);
1146 U_POINT16 cSrc = point16_set(w, h);
1147 rec = U_WMRSTRETCHDIB_set(
1148 Dest,
1149 cDest,
1150 Src,
1151 cSrc,
1152 U_DIB_RGB_COLORS,
1153 U_SRCCOPY,
1154 Bmi,
1155 h * rs,
1156 px
1157 );
1158 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1159 g_error("Fatal programming error in PrintWmf::image at U_WMRSTRETCHDIB_set");
1160 }
1161 free(px);
1162 free(Bmi);
1163 if (numCt) {
1164 free(ct);
1165 }
1166 return 0;
1167}
1168
1169// may also be called with a simple_shape or an empty path, whereupon it just returns without doing anything
1170unsigned int PrintWmf::print_pathv(Geom::PathVector const &pathv, const Geom::Affine &transform)
1171{
1172 char *rec = nullptr;
1173 U_POINT16 *pt16hold, *pt16ptr;
1174 uint16_t *n16hold;
1175 uint16_t *n16ptr;
1176
1177 simple_shape = print_simple_shape(pathv, transform);
1178 if (!simple_shape && !pathv.empty()) {
1179 // WMF does not have beziers, need to convert to ONLY linears with something like this:
1180 Geom::PathVector pv = pathv_to_linear(pathv * transform, MAXDISP);
1181
1184 /* If the path consists entirely of closed subpaths use one polypolygon.
1185 Otherwise use a mix of polygon or polyline separately on each path.
1186 If the polyline turns out to be single line segments, use a series of MOVETO/LINETO instead,
1187 because WMF has no POLYPOLYLINE.
1188 The former allows path delimited donuts and the like, which
1189 cannot be represented in WMF with polygon or polyline because there is no external way to combine paths
1190 as there is in EMF or SVG.
1191 For polygons specify the last point the same as the first. The WMF/EMF manuals say that the
1192 reading program SHOULD close the path, which allows a conforming program not to, potentially rendering
1193 a closed path as an open one. */
1194 int nPolys = 0;
1195 int totPoints = 0;
1196 for (const auto & pit : pv) {
1197 totPoints += 1 + pit.size_default(); // big array, will hold all points, for all polygons. Size_default ignores first point in each path.
1198 if (pit.end_default() == pit.end_closed()) {
1199 nPolys++;
1200 } else {
1201 nPolys = 0;
1202 break;
1203 }
1204 }
1205
1206 if (nPolys > 1) { // a single polypolygon, a single polygon falls through to the else
1207 pt16hold = pt16ptr = (U_POINT16 *) malloc(totPoints * sizeof(U_POINT16));
1208 if (!pt16ptr) {
1209 return(false);
1210 }
1211
1212 n16hold = n16ptr = (uint16_t *) malloc(nPolys * sizeof(uint16_t));
1213 if (!n16ptr) {
1214 free(pt16hold);
1215 return(false);
1216 }
1217
1218 for (const auto & pit : pv) {
1219 using Geom::X;
1220 using Geom::Y;
1221
1222
1223 *n16ptr++ = pit.size_default(); // points in the subpath
1224
1227 Geom::Point p1 = pit.initialPoint(); // This point is special, it isn't in the iterator
1228
1229 p1[X] = (p1[X] * PX2WORLD);
1230 p1[Y] = (p1[Y] * PX2WORLD);
1231 *pt16ptr++ = point16_set((int32_t) round(p1[X]), (int32_t) round(p1[Y]));
1232
1233 for (Geom::Path::const_iterator cit = pit.begin(); cit != pit.end_open(); ++cit) {
1234 Geom::Point p1 = cit->finalPoint();
1235
1236 p1[X] = (p1[X] * PX2WORLD);
1237 p1[Y] = (p1[Y] * PX2WORLD);
1238 *pt16ptr++ = point16_set((int32_t) round(p1[X]), (int32_t) round(p1[Y]));
1239 }
1240
1241 }
1242 rec = U_WMRPOLYPOLYGON_set(nPolys, n16hold, pt16hold);
1243 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1244 g_error("Fatal programming error in PrintWmf::print_pathv at U_WMRPOLYPOLYGON_set");
1245 }
1246 free(pt16hold);
1247 free(n16hold);
1248 } else { // one or more polyline or polygons (but not all polygons, that would be the preceding case)
1249 for (const auto & pit : pv) {
1250 using Geom::X;
1251 using Geom::Y;
1252
1253 /* Malformatted Polylines with a sequence like M L M M L have been seen, the 2nd M does nothing
1254 and that point must not go into the output. */
1255 if (!(pit.size_default())) {
1256 continue;
1257 }
1258 /* Figure out how many points there are, make an array big enough to hold them, and store
1259 all the points. This is the same for open or closed path. This gives the upper bound for
1260 the number of points. The actual number used is calculated on the fly.
1261 */
1262 int nPoints = 1 + pit.size_default();
1263
1264 pt16hold = pt16ptr = (U_POINT16 *) malloc(nPoints * sizeof(U_POINT16));
1265 if (!pt16ptr) {
1266 break;
1267 }
1268
1271 Geom::Point p1 = pit.initialPoint(); // This point is special, it isn't in the iterator
1272
1273 p1[X] = (p1[X] * PX2WORLD);
1274 p1[Y] = (p1[Y] * PX2WORLD);
1275 *pt16ptr++ = point16_set((int32_t) round(p1[X]), (int32_t) round(p1[Y]));
1276 nPoints = 1;
1277
1278 for (Geom::Path::const_iterator cit = pit.begin(); cit != pit.end_default(); ++cit, nPoints++) {
1279 Geom::Point p1 = cit->finalPoint();
1280
1281 p1[X] = (p1[X] * PX2WORLD);
1282 p1[Y] = (p1[Y] * PX2WORLD);
1283 *pt16ptr++ = point16_set((int32_t) round(p1[X]), (int32_t) round(p1[Y]));
1284 }
1285
1286 if (pit.end_default() == pit.end_closed()) {
1287 rec = U_WMRPOLYGON_set(nPoints, pt16hold);
1288 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1289 g_error("Fatal programming error in PrintWmf::print_pathv at U_WMRPOLYGON_set");
1290 }
1291 } else if (nPoints > 2) {
1292 rec = U_WMRPOLYLINE_set(nPoints, pt16hold);
1293 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1294 g_error("Fatal programming error in PrintWmf::print_pathv at U_POLYLINE_set");
1295 }
1296 } else if (nPoints == 2) {
1297 rec = U_WMRMOVETO_set(pt16hold[0]);
1298 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1299 g_error("Fatal programming error in PrintWmf::print_pathv at U_WMRMOVETO_set");
1300 }
1301 rec = U_WMRLINETO_set(pt16hold[1]);
1302 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1303 g_error("Fatal programming error in PrintWmf::print_pathv at U_WMRLINETO_set");
1304 }
1305 }
1306 free(pt16hold);
1307 }
1308 }
1309 }
1310
1311 // WMF has no fill or stroke commands, the draw does it with active pen/brush
1312
1313 // clean out brush and pen, but only after all parts of the draw complete
1314 if (use_fill) {
1315 destroy_brush();
1316 }
1317 if (use_stroke) {
1318 destroy_pen();
1319 }
1320
1321 return TRUE;
1322}
1323
1324
1325unsigned int PrintWmf::text(Inkscape::Extension::Print * /*mod*/, char const *text, Geom::Point const &p,
1326 SPStyle const *const style)
1327{
1328 if (!wt || !text) {
1329 return 0;
1330 }
1331
1332 char *rec = nullptr;
1333 int ccount, newfont;
1334 int fix90n = 0;
1335 uint32_t hfont = 0;
1336 Geom::Affine tf = m_tr_stack.top();
1337 double rot = -1800.0 * std::atan2(tf[1], tf[0]) / M_PI; // 0.1 degree rotation, - sign for MM_TEXT
1338 double rotb = -std::atan2(tf[1], tf[0]); // rotation for baseline offset for superscript/subscript, used below
1339 double dx, dy;
1340 double ky;
1341
1342 // 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
1343 int ndx = 0;
1344 int rtl = 0;
1345 int16_t *adx;
1346 smuggle_adxky_out(text, &adx, &ky, &rtl, &ndx, PX2WORLD * std::min(tf.expansionX(), tf.expansionY())); // side effect: free() adx
1347
1348 uint32_t textalignment;
1349 if (rtl > 0) {
1350 textalignment = U_TA_BASELINE | U_TA_LEFT;
1351 } else {
1352 textalignment = U_TA_BASELINE | U_TA_RIGHT | U_TA_RTLREADING;
1353 }
1354 if (textalignment != htextalignment) {
1355 htextalignment = textalignment;
1356 rec = U_WMRSETTEXTALIGN_set(textalignment);
1357 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1358 g_error("Fatal programming error in PrintWmf::text at U_WMRSETTEXTALIGN_set");
1359 }
1360 }
1361
1362 char *text2 = strdup(text); // because U_Utf8ToUtf16le calls iconv which does not like a const char *
1363 uint16_t *unicode_text = U_Utf8ToUtf16le(text2, 0, nullptr);
1364 free(text2);
1365 //translates Unicode as Utf16le to NonUnicode, if possible. If any translate, all will, and all to
1366 //the same font, because of code in Layout::print
1367 UnicodeToNon(unicode_text, &ccount, &newfont);
1368 // The preceding hopefully handled conversions to symbol, wingdings or zapf dingbats. Now slam everything
1369 // else down into latin1, which is all WMF can handle. If the language isn't English expect terrible results.
1370 char *latin1_text = U_Utf16leToLatin1(unicode_text, 0, nullptr);
1371 free(unicode_text);
1372
1373 // in some cases a UTF string may reduce to NO latin1 characters, which returns NULL
1374 if(!latin1_text){free(adx); return 0; }
1375
1376 //PPT gets funky with text within +-1 degree of a multiple of 90, but only for SOME fonts.Snap those to the central value
1377 //Some funky ones: Arial, Times New Roman
1378 //Some not funky ones: Symbol and Verdana.
1379 //Without a huge table we cannot catch them all, so just the most common problem ones.
1380 FontfixParams params;
1381
1382 if (FixPPTCharPos) {
1383 switch (newfont) {
1384 case CVTSYM:
1385 _lookup_ppt_fontfix("Convert To Symbol", params);
1386 break;
1387 case CVTZDG:
1388 _lookup_ppt_fontfix("Convert To Zapf Dingbats", params);
1389 break;
1390 case CVTWDG:
1391 _lookup_ppt_fontfix("Convert To Wingdings", params);
1392 break;
1393 default: //also CVTNON
1394 _lookup_ppt_fontfix(style->font_family.value(), params);
1395 break;
1396 }
1397 if (params.f2 != 0 || params.f3 != 0) {
1398 int irem = ((int) round(rot)) % 900 ;
1399 if (irem <= 9 && irem >= -9) {
1400 fix90n = 1; //assume vertical
1401 rot = (double)(((int) round(rot)) - irem);
1402 rotb = rot * M_PI / 1800.0;
1403 if (std::abs(rot) == 900.0) {
1404 fix90n = 2;
1405 }
1406 }
1407 }
1408 }
1409
1410 /*
1411 Note that text font sizes are stored into the WMF as fairly small integers and that limits their precision.
1412 The WMF output files produced here have been designed so that the integer valued pt sizes
1413 land right on an integer value in the WMF file, so those are exact. However, something like 18.1 pt will be
1414 somewhat off, so that when it is read back in it becomes 18.11 pt. (For instance.)
1415 */
1416 int textheight = round(-style->font_size.computed * PX2WORLD * std::min(tf.expansionX(), tf.expansionY()));
1417 if (!hfont) {
1418
1419 // Get font face name. Use changed font name if unicode mapped to one
1420 // of the special fonts.
1421 char *facename;
1422 if (!newfont) {
1423 facename = U_Utf8ToLatin1(style->font_family.value(), 0, nullptr);
1424 } else {
1425 facename = U_Utf8ToLatin1(FontName(newfont), 0, nullptr);
1426 }
1427
1428 // Scale the text to the minimum stretch. (It tends to stay within bounding rectangles even if
1429 // it was streteched asymmetrically.) Few applications support text from WMF which is scaled
1430 // differently by height/width, so leave lfWidth alone.
1431
1432 U_FONT *puf = U_FONT_set(
1433 textheight,
1434 0,
1435 round(rot),
1436 round(rot),
1437 _translate_weight(style->font_weight.computed),
1438 (style->font_style.computed == SP_CSS_FONT_STYLE_ITALIC),
1439 style->text_decoration_line.underline,
1440 style->text_decoration_line.line_through,
1441 U_DEFAULT_CHARSET,
1442 U_OUT_DEFAULT_PRECIS,
1443 U_CLIP_DEFAULT_PRECIS,
1444 U_DEFAULT_QUALITY,
1445 U_DEFAULT_PITCH | U_FF_DONTCARE,
1446 facename);
1447 free(facename);
1448
1449 rec = wcreatefontindirect_set(&hfont, wht, puf);
1450 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1451 g_error("Fatal programming error in PrintWmf::text at wcreatefontindirect_set");
1452 }
1453 free(puf);
1454 }
1455
1456 rec = wselectobject_set(hfont, wht);
1457 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1458 g_error("Fatal programming error in PrintWmf::text at wselectobject_set");
1459 }
1460
1461 auto rgb = style->fill.getColor();
1462 // only change the text color when it needs to be changed
1463 if (htextcolor_rgb != rgb) {
1465 rec = U_WMRSETTEXTCOLOR_set(toColorRef(rgb));
1466 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1467 g_error("Fatal programming error in PrintWmf::text at U_WMRSETTEXTCOLOR_set");
1468 }
1469 }
1470
1471
1472 // Text alignment:
1473 // - (x,y) coordinates received by this filter are those of the point where the text
1474 // actually starts, and already takes into account the text object's alignment;
1475 // - for this reason, the WMF text alignment must always be TA_BASELINE|TA_LEFT.
1476 // this is set at the beginning of the file and never changed
1477
1478 // Transparent text background, never changes, set at the beginning of the file
1479
1480 Geom::Point p2 = p * tf;
1481
1482 //Handle super/subscripts and vertical kerning
1483 /* Previously used this, but vertical kerning was not supported
1484 p2[Geom::X] -= style->baseline_shift.computed * std::sin( rotb );
1485 p2[Geom::Y] -= style->baseline_shift.computed * std::cos( rotb );
1486 */
1487 p2[Geom::X] += ky * std::sin(rotb);
1488 p2[Geom::Y] += ky * std::cos(rotb);
1489
1490 //Conditionally handle compensation for PPT WMF import bug (affects PPT 2003-2010, at least)
1491 if (FixPPTCharPos) {
1492 if (fix90n == 1) { //vertical
1493 dx = 0.0;
1494 dy = params.f3 * style->font_size.computed * std::cos(rotb);
1495 } else if (fix90n == 2) { //horizontal
1496 dx = params.f2 * style->font_size.computed * std::sin(rotb);
1497 dy = 0.0;
1498 } else {
1499 dx = params.f1 * style->font_size.computed * std::sin(rotb);
1500 dy = params.f1 * style->font_size.computed * std::cos(rotb);
1501 }
1502 p2[Geom::X] += dx;
1503 p2[Geom::Y] += dy;
1504 }
1505
1506 p2[Geom::X] = (p2[Geom::X] * PX2WORLD);
1507 p2[Geom::Y] = (p2[Geom::Y] * PX2WORLD);
1508
1509 int32_t const xpos = (int32_t) round(p2[Geom::X]);
1510 int32_t const ypos = (int32_t) round(p2[Geom::Y]);
1511
1512 // The number of characters in the string is a bit fuzzy. ndx, the number of entries in adx is
1513 // the number of VISIBLE characters, since some may combine from the UTF (8 originally,
1514 // now 16) encoding. Conversely strlen() or wchar16len() would give the absolute number of
1515 // encoding characters. Unclear if emrtext wants the former or the latter but for now assume the former.
1516
1517 // This is currently being smuggled in from caller as part of text, works
1518 // MUCH better than the fallback hack below
1519 // uint32_t *adx = dx_set(textheight, U_FW_NORMAL, slen); // dx is needed, this makes one up
1520 if (rtl > 0) {
1521 rec = U_WMREXTTEXTOUT_set((U_POINT16) {
1522 (int16_t) xpos, (int16_t) ypos
1523 },
1524 ndx, U_ETO_NONE, latin1_text, adx, U_RCL16_DEF);
1525 } else { // RTL text, U_TA_RTLREADING should be enough, but set this one too just in case
1526 rec = U_WMREXTTEXTOUT_set((U_POINT16) {
1527 (int16_t) xpos, (int16_t) ypos
1528 },
1529 ndx, U_ETO_RTLREADING, latin1_text, adx, U_RCL16_DEF);
1530 }
1531 free(latin1_text);
1532 free(adx);
1533 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1534 g_error("Fatal programming error in PrintWmf::text at U_WMREXTTEXTOUTW_set");
1535 }
1536
1537 rec = wdeleteobject_set(&hfont, wht);
1538 if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
1539 g_error("Fatal programming error in PrintWmf::text at wdeleteobject_set");
1540 }
1541
1542 return 0;
1543}
1544
1546{
1547 /* WMF print */
1548 // clang-format off
1550 "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
1551 "<name>Windows Metafile Print</name>\n"
1552 "<id>org.inkscape.print.wmf</id>\n"
1553 "<param gui-hidden=\"true\" name=\"destination\" type=\"string\"></param>\n"
1554 "<param gui-hidden=\"true\" name=\"textToPath\" type=\"bool\">true</param>\n"
1555 "<param gui-hidden=\"true\" name=\"pageBoundingBox\" type=\"bool\">true</param>\n"
1556 "<param gui-hidden=\"true\" name=\"FixPPTCharPos\" type=\"bool\">false</param>\n"
1557 "<param gui-hidden=\"true\" name=\"FixPPTDashLine\" type=\"bool\">false</param>\n"
1558 "<param gui-hidden=\"true\" name=\"FixPPTGrad2Polys\" type=\"bool\">false</param>\n"
1559 "<param gui-hidden=\"true\" name=\"FixPPTPatternAsHatch\" type=\"bool\">false</param>\n"
1560 "<print/>\n"
1561 "</inkscape-extension>", std::make_unique<PrintWmf>());
1562 // clang-format on
1563
1564 return;
1565}
1566
1567} /* namespace Internal */
1568} /* namespace Extension */
1569} /* namespace Inkscape */
1570
1571
1572/*
1573 Local Variables:
1574 mode:c++
1575 c-file-style:"stroustrup"
1576 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1577 indent-tabs-mode:nil
1578 fill-column:99
1579 End:
1580*/
1581// 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
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
Affine withoutTranslation() const
Definition affine.h:169
Coord expansionY() const
Calculates the amount of y-scaling imparted by the Affine.
Definition affine.cpp:71
Bezier curve with compile-time specified order.
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
bool empty() const
Check whether the vector contains any paths.
Definition pathvector.h:145
Sequence of contiguous curves, aka spline.
Definition path.h:353
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
Scaling from the origin.
Definition transforms.h:150
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)
unsigned int finish(Inkscape::Extension::Print *module) 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_brush(SPStyle const *style, PU_COLORREF fcolor) override
unsigned int setup(Inkscape::Extension::Print *module) override
int create_pen(SPStyle const *style, const Geom::Affine &transform) override
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 begin(Inkscape::Extension::Print *module, SPDocument *doc) override
unsigned int text(Inkscape::Extension::Print *module, char const *text, Geom::Point const &p, SPStyle const *style) override
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.
static void smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *rtl, int *ndx, float scale)
Definition wmf-print.cpp:79
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
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
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::OptRect desktopVisualBounds() const
Get item's visual bbox in desktop coordinate system.
Definition sp-item.cpp:1049
Linear gradient.
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
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::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.
Elliptical arc curve.
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(Geom::PathVector const &pathv, double)
Definition geom.cpp:636
Specific geometry functions for Inkscape, not provided my lib2geom.
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)
U_COLORREF toColorRef(std::optional< Colors::Color > color)
static WMFHANDLES * wht
Definition wmf-print.cpp:77
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.
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.
RGB rgb
Definition quantize.cpp:36
Axis-aligned rectangle.
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.
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
Font Object WMF manual 2.2.1.2 Warning, only pass by pointer, passing by value will will truncate in ...
Definition uwmf.h:783
SizeL Object WMF manual 2.2.2.22 Same as "EMF SIZEL Object" in uemf.h.
Definition uwmf.h:1070
Any generic pair of floats.
Definition uemf.h:1637
Pen Object WMF manual 2.2.1.4.
Definition uwmf.h:826
WMF manual 2.2.2.16.
Definition uemf.h:563
int16_t y
Y size (16 bit)
Definition uemf.h:565
int16_t x
X size (16 bit)
Definition uemf.h:564
Rect Object WMF manual 2.2.2.18.
Definition uwmf.h:840
WMF manual 2.2.2.20.
Definition uemf.h:592
BitmapInfoHeader Object WMF manual 2.2.2.3 Same as "EMF BITMAPINFOHEADER Object" in uemf....
Definition uwmf.h:986
The various create functions need a place to put their handles, these are stored in the table below.
Definition uwmf.h:2067
Storage for keeping track of properties of the growing WMF file as records are added.
Definition uwmf.h:2045
SPWindRule
Definition style-enums.h:23
@ SP_CSS_FONT_STYLE_ITALIC
Definition style-enums.h:62
parse SVG path specifications
Enhanced Metafile Input/Output.
@ CVTZDG
@ CVTWDG
@ CVTSYM
void UnicodeToNon(uint16_t *text, int *ecount, int *edest)
char * FontName(int code)
double height
double width
char * U_Utf8ToLatin1(const char *src, size_t max, size_t *len)
uint16_t * U_Utf8ToUtf16le(const char *src, size_t max, size_t *len)
char * U_Utf16leToLatin1(const uint16_t *src, size_t max, size_t *len)
Windows Metafile printing - implementation.