73
73
#define PXPERMETER 2835
74
74
#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
76
enum drawmode {DRAW_PAINT, DRAW_PATTERN, DRAW_IMAGE, DRAW_LINEAR_GRADIENT, DRAW_RADIAL_GRADIENT};
79
char *fontname; //Font name
80
FFNEXUS *next; //link to next nexus, NULL if this is the last
81
double f1; //Vertical (rotating) offset factor (* font height)
82
double f2; //Vertical (nonrotating) offset factor (* font height)
83
double f3; //Horizontal (nonrotating) offset factor (* font height)
87
Geom::Point p1; // center or start
88
Geom::Point p2; // xhandle or end
89
Geom::Point p3; // yhandle or unused
90
double r; // radius or unused
91
void *grad; // to access the stops information
92
int mode; // DRAW_LINEAR_GRADIENT or DRAW_RADIAL_GRADIENT, if GRADVALUES is valid, else any value
93
U_COLORREF bgc; // document background color, this is as good a place as any to keep it
94
float rgb[3]; // also background color, but as 0-1 float.
98
static double PX2WORLD = 1200.0/90.0; // inkscape is 90 dpi, WMF file is 1200
78
static double PX2WORLD = 1200.0 / 90.0; // inkscape is 90 dpi, WMF file is 1200
99
79
static bool FixPPTCharPos, FixPPTDashLine, FixPPTGrad2Polys, FixPPTPatternAsHatch;
100
static FFNEXUS *wmf_short_fflist = NULL; //only those fonts so far encountered
101
static FFNEXUS *wmf_long_fflist = NULL; //all the fonts described in ...\share\extensions\fontfix.conf
102
80
static WMFTRACK *wt = NULL;
103
81
static WMFHANDLES *wht = NULL;
104
static GRADVALUES gv;
106
void PrintWmf::read_system_fflist(void){ //this is not called by any other source files
113
std::string path_to_ffconf;
115
if(wmf_long_fflist)return;
116
char *oldlocale = g_strdup(setlocale(LC_NUMERIC, NULL));
117
setlocale(LC_NUMERIC, "C");
119
path_to_ffconf=INKSCAPE_EXTENSIONDIR;
121
path_to_ffconf.append("\\fontfix.conf"); //Windows path syntax
123
path_to_ffconf.append("/fontfix.conf"); //Unix/linx path syntax
126
fffile.open(path_to_ffconf.c_str(), std::ios::in);
127
if(!fffile.is_open()){
128
g_error("Unable to open file: %s\n", path_to_ffconf.c_str());
130
while (std::getline(fffile,instr)){
131
if(instr[0]=='#')continue;
132
// not a comment, get the 4 values from the line
133
int elements=sscanf(instr.c_str(),"%lf %lf %lf %127[^\n]",&f1,&f2,&f3, &fontname[0]);
135
g_error("Expected \"f1 f2 f3 Fontname\" but did not find it in file: %s\n", path_to_ffconf.c_str());
137
temp=(FFNEXUS *) calloc(1,sizeof(FFNEXUS)); //This will never be freed
141
temp->fontname=strdup(fontname); //This will never be freed
142
temp->next=NULL; //just to be explicit, it is already 0
148
wmf_long_fflist=ptr=temp;
153
setlocale(LC_NUMERIC, oldlocale);
157
/* Looks for the fontname in the long list. If it does not find it, it adds the default values
158
to the short list with this fontname. If it does find it, then it adds the specified values.
160
void PrintWmf::search_long_fflist(const char *fontname, double *f1, double *f2, double *f3){ //this is not called by any other source files
162
FFNEXUS *tmp=wmf_long_fflist;
163
if(!wmf_long_fflist){
164
g_error("Programming error search_long_fflist called before read_system_fflist\n");
168
if(!strcmp(ptr->fontname,fontname)){ tmp=ptr; break; }
171
//tmp points at either the found name, or the default, the first entry in wmf_long_fflist
172
if(!wmf_short_fflist){
173
ptr=wmf_short_fflist=(FFNEXUS *) malloc(sizeof(FFNEXUS));
176
ptr=wmf_short_fflist;
177
while(ptr->next){ ptr=ptr->next; }
178
ptr->next=(FFNEXUS *) malloc(sizeof(FFNEXUS));
181
ptr->fontname=strdup(tmp->fontname);
182
*f1 = ptr->f1 = tmp->f1;
183
*f2 = ptr->f2 = tmp->f2;
184
*f3 = ptr->f3 = tmp->f3;
188
/* Looks for the fontname in the short list. If it does not find it, it looks in the wmf_long_fflist.
189
Either way it returns the f1, f2, f3 parameters for the font, even if these are for the default.
191
void PrintWmf::search_short_fflist(const char *fontname, double *f1, double *f2, double *f3){ //this is not called by any other source files
193
static FFNEXUS *last=NULL;
195
if(!wmf_long_fflist){
196
g_error("Programming error search_short_fflist called before read_system_fflist\n");
198
// This speeds things up a lot - if the same font is called twice in a row, pull it out immediately
199
if(last && !strcmp(last->fontname,fontname)){ ptr=last; }
200
else { ptr=wmf_short_fflist; } // wmf_short_fflist may still be NULL
202
if(!strcmp(ptr->fontname,fontname)){ *f1=ptr->f1; *f2=ptr->f2; *f3=ptr->f3; last=ptr; return; }
205
//reach this point only if there is no match
206
search_long_fflist(fontname, f1, f2, f3);
209
void PrintWmf::smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *rtl, int *ndx, float scale){
83
void PrintWmf::smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *rtl, int *ndx, float scale)
213
const char *cptr=&string[strlen(string)+1]; // this works because of the first fake terminator
88
const char *cptr = &string[strlen(string) + 1]; // this works because of the first fake terminator
216
91
*ky = 0.0; // set a default value
217
sscanf(cptr,"%7d",ndx);
218
if(!*ndx)return; // this could happen with an empty string
92
sscanf(cptr, "%7d", ndx);
94
return; // this could happen with an empty string
220
ladx = (int16_t *) malloc(*ndx * sizeof(int16_t) );
221
if(!ladx)g_error("Out of memory");
223
for(i=0; i<*ndx; i++,cptr+=7, ladx++){
224
sscanf(cptr,"%7f",&fdx);
225
*ladx=(int16_t) round(fdx * scale);
97
ladx = (int16_t *) malloc(*ndx * sizeof(int16_t));
99
g_error("Out of memory");
102
for (i = 0; i < *ndx; i++, cptr += 7, ladx++) {
103
sscanf(cptr, "%7f", &fdx);
104
*ladx = (int16_t) round(fdx * scale);
227
106
cptr++; // skip 2nd fake terminator
228
sscanf(cptr,"%7f",&fdx);
107
sscanf(cptr, "%7f", &fdx);
230
109
cptr += 7; // advance over ky and its space
231
sscanf(cptr,"%07d",rtl);
234
/* convert an 0RGB color to EMF U_COLORREF.
235
inverse of sethexcolor() in wmf-inout.cpp
237
U_COLORREF PrintWmf::gethexcolor(uint32_t color){
240
(color >> 16) & 0xFF,
248
/* Translate inkscape weights to WMF weights.
250
uint32_t PrintWmf::transweight(const unsigned int inkweight){
251
if(inkweight == SP_CSS_FONT_WEIGHT_400)return(U_FW_NORMAL);
252
if(inkweight == SP_CSS_FONT_WEIGHT_100)return(U_FW_THIN);
253
if(inkweight == SP_CSS_FONT_WEIGHT_200)return(U_FW_EXTRALIGHT);
254
if(inkweight == SP_CSS_FONT_WEIGHT_300)return(U_FW_LIGHT);
255
// 400 is tested first, as it is the most common case
256
if(inkweight == SP_CSS_FONT_WEIGHT_500)return(U_FW_MEDIUM);
257
if(inkweight == SP_CSS_FONT_WEIGHT_600)return(U_FW_SEMIBOLD);
258
if(inkweight == SP_CSS_FONT_WEIGHT_700)return(U_FW_BOLD);
259
if(inkweight == SP_CSS_FONT_WEIGHT_800)return(U_FW_EXTRABOLD);
260
if(inkweight == SP_CSS_FONT_WEIGHT_900)return(U_FW_HEAVY);
264
PrintWmf::PrintWmf (void)
110
sscanf(cptr, "%07d", rtl);
266
115
// all of the class variables are initialized elsewhere, many in PrintWmf::Begin,
270
PrintWmf::~PrintWmf (void)
273
/* restore default signal handling for SIGPIPE */
274
#if !defined(_WIN32) && !defined(__WIN32__)
275
(void) signal(SIGPIPE, SIG_DFL);
281
unsigned int PrintWmf::setup (Inkscape::Extension::Print * /*mod*/)
119
unsigned int PrintWmf::setup(Inkscape::Extension::Print * /*mod*/)
287
unsigned int PrintWmf::begin (Inkscape::Extension::Print *mod, SPDocument *doc)
125
unsigned int PrintWmf::begin(Inkscape::Extension::Print *mod, SPDocument *doc)
290
128
gchar const *utf8_fn = mod->get_param_string("destination");
326
166
if (pageBoundingBox) {
327
167
d = Geom::Rect::from_xywh(0, 0, _width, _height);
329
SPItem* doc_item = doc->getRoot();
169
SPItem *doc_item = doc->getRoot();
330
170
Geom::OptRect bbox = doc_item->desktopVisualBounds();
334
176
d *= Geom::Scale(Inkscape::Util::Quantity::convert(1, "px", "in")); // 90 dpi inside inkscape, wmf file will be 1200 dpi
336
178
/* -1/1200 in next two lines so that WMF read in will write out again at exactly the same size */
337
float dwInchesX = d.width() - 1.0/1200.0;
338
float dwInchesY = d.height() - 1.0/1200.0;
339
int dwPxX = round(dwInchesX * 1200.0);
340
int dwPxY = round(dwInchesY * 1200.0);
179
float dwInchesX = d.width() - 1.0 / 1200.0;
180
float dwInchesY = d.height() - 1.0 / 1200.0;
181
int dwPxX = round(dwInchesX * 1200.0);
182
int dwPxY = round(dwInchesY * 1200.0);
342
184
float dwInchesX = d.width();
343
185
float dwInchesY = d.height();
344
int dwPxX = round(d.width() * 1200.0);
345
int dwPxY = round(d.height() * 1200.0);
186
int dwPxX = round(d.width() * 1200.0);
187
int dwPxY = round(d.height() * 1200.0);
348
190
PU_PAIRF ps = U_PAIRF_set(dwInchesX, dwInchesY);
349
rec = U_WMRHEADER_set(ps,1200); // Example: drawing is A4 horizontal, 1200 dpi
191
rec = U_WMRHEADER_set(ps, 1200); // Example: drawing is A4 horizontal, 1200 dpi
351
193
g_error("Fatal programming error in PrintWmf::begin at WMRSETMAPMODE");
353
195
(void) wmf_header_append((PU_METARECORD)rec, wt, 1);
356
rec = U_WMRSETWINDOWEXT_set(point16_set( dwPxX, dwPxY));
357
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
198
rec = U_WMRSETWINDOWEXT_set(point16_set(dwPxX, dwPxY));
199
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
358
200
g_error("Fatal programming error in PrintWmf::begin at WMRSETWINDOWEXT");
361
rec = U_WMRSETWINDOWORG_set(point16_set(0,0));
362
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
203
rec = U_WMRSETWINDOWORG_set(point16_set(0, 0));
204
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
363
205
g_error("Fatal programming error in PrintWmf::begin at WMRSETWINDOWORG");
366
208
rec = U_WMRSETMAPMODE_set(U_MM_ANISOTROPIC);
367
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
209
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
368
210
g_error("Fatal programming error in PrintWmf::begin at WMRSETMAPMODE");
371
213
/* set some parameters, else the program that reads the WMF may default to other values */
373
215
rec = U_WMRSETBKMODE_set(U_TRANSPARENT);
374
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
216
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
375
217
g_error("Fatal programming error in PrintWmf::begin at U_WMRSETBKMODE");
378
hpolyfillmode=U_WINDING;
220
hpolyfillmode = U_WINDING;
379
221
rec = U_WMRSETPOLYFILLMODE_set(U_WINDING);
380
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
222
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
381
223
g_error("Fatal programming error in PrintWmf::begin at U_WMRSETPOLYFILLMODE");
386
228
// actually starts, and already takes into account the text object's alignment;
387
229
// - for this reason, the WMF text alignment must always be TA_BASELINE|TA_LEFT.
388
230
rec = U_WMRSETTEXTALIGN_set(U_TA_BASELINE | U_TA_LEFT);
389
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
231
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
390
232
g_error("Fatal programming error in PrintWmf::begin at U_WMRSETTEXTALIGN_set");
393
htextcolor_rgb[0] = htextcolor_rgb[1] = htextcolor_rgb[2] = 0.0;
394
rec = U_WMRSETTEXTCOLOR_set(U_RGB(0,0,0));
395
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
235
htextcolor_rgb[0] = htextcolor_rgb[1] = htextcolor_rgb[2] = 0.0;
236
rec = U_WMRSETTEXTCOLOR_set(U_RGB(0, 0, 0));
237
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
396
238
g_error("Fatal programming error in PrintWmf::begin at U_WMRSETTEXTCOLOR_set");
399
241
rec = U_WMRSETROP2_set(U_R2_COPYPEN);
400
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
242
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
401
243
g_error("Fatal programming error in PrintWmf::begin at U_WMRSETROP2");
405
247
rec = wmiterlimit_set(5);
406
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
248
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
407
249
g_error("Fatal programming error in PrintWmf::begin at wmiterlimit_set");
411
253
// 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
412
U_PEN up = U_PEN_set(U_PS_SOLID, 1, colorref_set(0,0,0));
254
U_PEN up = U_PEN_set(U_PS_SOLID, 1, colorref_set(0, 0, 0));
414
256
rec = wcreatepenindirect_set(&Pen, wht, up);
415
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
257
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
416
258
g_error("Fatal programming error in PrintWmf::begin at wcreatepenindirect_set");
419
261
// create a null pen. If no specific pen is set, this is used
420
up = U_PEN_set(U_PS_NULL, 1, colorref_set(0,0,0));
262
up = U_PEN_set(U_PS_NULL, 1, colorref_set(0, 0, 0));
421
263
rec = wcreatepenindirect_set(&hpen_null, wht, up);
422
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
264
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
423
265
g_error("Fatal programming error in PrintWmf::begin at wcreatepenindirect_set");
425
267
destroy_pen(); // make this pen active
475
unsigned int PrintWmf::comment ( Inkscape::Extension::Print * /*module*/, const char * /*comment*/)
319
unsigned int PrintWmf::comment(Inkscape::Extension::Print * /*module*/, const char * /*comment*/)
479
325
// earlier versions had flush of fill here, but it never executed and was removed
484
// Extract hatchType, hatchColor from a name like
485
// *MFhatch<hatchType>_<hatchColor>[_<bkcolor>] (WMF or EMF hatches are the same)
486
// Where the first one is a number and the second (and third) a color in hex.
487
// hatchType, hatchColor, bkColor have been set with defaults before this is called.
489
void PrintWmf::hatch_classify(char *name, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor){
494
// name should be EMFhatch or WMFhatch but *MFhatch will be accepted
495
if(0!=strncmp(&name[1],"MFhatch",7)){ return; } // not anything we can parse
496
name += 8; // WMFhatch already detected
498
while(*name && isdigit(*name)){
499
val = 10*val + *name - '0';
503
if(*name != '_' || val > U_HS_DITHEREDBKCLR){ // wrong syntax, cannot classify
508
if(2 != sscanf(name,"%X_%X", &hcolor, &bcolor)){ // not a pattern with background
509
if(1 != sscanf(name,"%X", &hcolor)){ *hatchType = -1; } // not a pattern, cannot classify
510
*hatchColor = gethexcolor(hcolor);
513
*hatchColor = gethexcolor(hcolor);
514
*bkColor = gethexcolor(bcolor);
518
/* Everything > U_HS_SOLIDCLR is solid, just specify the color in the brush rather than messing around with background or textcolor */
519
if(*hatchType > U_HS_SOLIDCLR)*hatchType = U_HS_SOLIDCLR;
523
// Recurse down from a brush pattern, try to figure out what it is.
524
// If an image is found set a pointer to the epixbuf, else set that to NULL
525
// If a pattern is found with a name like [EW]MFhatch3_3F7FFF return hatchType=3, hatchColor=3F7FFF (as a uint32_t),
526
// otherwise hatchType is set to -1 and hatchColor is not defined.
529
void PrintWmf::brush_classify(SPObject *parent, int depth, GdkPixbuf **epixbuf, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor){
533
*hatchColor = U_RGB(0,0,0);
534
*bkColor = U_RGB(255,255,255);
537
// first look along the pattern chain, if there is one
538
if(SP_IS_PATTERN(parent)){
539
for (SPPattern *pat_i = SP_PATTERN(parent); pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
540
if(SP_IS_IMAGE(pat_i)){
541
*epixbuf = ((SPImage *)pat_i)->pixbuf;
544
char temp[32]; // large enough
546
strncpy(temp,pat_i->getAttribute("id"),31); // Some names may be longer than [EW]MFhatch#_######
547
hatch_classify(temp, hatchType, hatchColor, bkColor);
548
if(*hatchType != -1)return;
550
// still looking? Look at this pattern's children, if there are any
551
SPObject *child = pat_i->firstChild();
552
while(child && !(*epixbuf) && (*hatchType == -1)){
553
brush_classify(child, depth, epixbuf, hatchType, hatchColor, bkColor);
554
child = child->getNext();
558
else if(SP_IS_IMAGE(parent)){
559
*epixbuf = ((SPImage *)parent)->pixbuf;
562
else { // some inkscape rearrangements pass through nodes between pattern and image which are not classified as either.
563
SPObject *child = parent->firstChild();
564
while(child && !(*epixbuf) && (*hatchType == -1)){
565
brush_classify(child, depth, epixbuf, hatchType, hatchColor, bkColor);
566
child = child->getNext();
571
//swap R/B in 4 byte pixel
572
void PrintWmf::swapRBinRGBA(char *px, int pixels){
574
for(int i=0;i<pixels*4;px+=4,i+=4){
581
/* opacity weighting of two colors as float. v1 is the color, op is its opacity, v2 is the background color */
582
inline float opweight(float v1, float v2, float op){
583
return v1*op + v2*(1.0-op);
586
U_COLORREF PrintWmf::avg_stop_color(SPGradient *gr){
588
int last = gr->vector.stops.size() -1;
594
ops = gr->vector.stops[0 ].opacity;
595
ope = gr->vector.stops[last].opacity;
596
sp_color_get_rgb_floatv(&gr->vector.stops[0 ].color, rgbs);
597
sp_color_get_rgb_floatv(&gr->vector.stops[last].color, rgbe);
599
/* Replace opacity at start & stop with that fraction background color, then average those two for final color. */
601
255*(( opweight(rgbs[0],gv.rgb[0],ops) + opweight(rgbe[0],gv.rgb[0],ope) )/2.0),
602
255*(( opweight(rgbs[1],gv.rgb[1],ops) + opweight(rgbe[1],gv.rgb[1],ope) )/2.0),
603
255*(( opweight(rgbs[2],gv.rgb[2],ops) + opweight(rgbe[2],gv.rgb[2],ope) )/2.0)
607
cr = U_RGB(0, 0, 0); // The default fill
612
int PrintWmf::hold_gradient(void *gr, int mode){
615
if(mode==DRAW_RADIAL_GRADIENT){
616
SPRadialGradient *rg = (SPRadialGradient *) gr;
617
gv.r = rg->r.computed; // radius, but of what???
618
gv.p1 = Geom::Point(rg->cx.computed, rg->cy.computed); // center
619
gv.p2 = Geom::Point(gv.r, 0) + gv.p1; // xhandle
620
gv.p3 = Geom::Point(0, -gv.r) + gv.p1; // yhandle
621
if (rg->gradientTransform_set) {
622
gv.p1 = gv.p1 * rg->gradientTransform;
623
gv.p2 = gv.p2 * rg->gradientTransform;
624
gv.p3 = gv.p3 * rg->gradientTransform;
627
else if(mode==DRAW_LINEAR_GRADIENT){
628
SPLinearGradient *lg = (SPLinearGradient *) gr;
630
gv.p1 = Geom::Point (lg->x1.computed, lg->y1.computed); // start
631
gv.p2 = Geom::Point (lg->x2.computed, lg->y2.computed); // end
632
gv.p3 = Geom::Point (0, 0); // unused
633
if (lg->gradientTransform_set) {
634
gv.p1 = gv.p1 * lg->gradientTransform;
635
gv.p2 = gv.p2 * lg->gradientTransform;
639
g_error("Fatal programming error, hold_gradient() in wmf-print.cpp called with invalid draw mode");
644
331
// fcolor is defined when gradients are being expanded, it is the color of one stripe or ring.
645
332
int PrintWmf::create_brush(SPStyle const *style, PU_COLORREF fcolor)
665
354
brushStyle = U_BS_SOLID;
666
355
hatchType = U_HS_SOLIDCLR;
667
356
bkColor = U_RGB(0, 0, 0);
668
if(fcolor){ hatchColor = *fcolor; }
669
else { hatchColor = U_RGB(0, 0, 0); }
358
hatchColor = *fcolor;
360
hatchColor = U_RGB(0, 0, 0);
671
363
if (!fcolor && style) {
672
if(style->fill.isColor()){
364
if (style->fill.isColor()) {
673
365
fill_mode = DRAW_PAINT;
674
366
float opacity = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
675
if (opacity <= 0.0)opacity = 0.0; // basically the same as no fill
367
if (opacity <= 0.0) {
368
opacity = 0.0; // basically the same as no fill
677
sp_color_get_rgb_floatv( &style->fill.value.color, rgb );
678
hatchColor = U_RGB(255*rgb[0], 255*rgb[1], 255*rgb[2]);
371
sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
372
hatchColor = U_RGB(255 * rgb[0], 255 * rgb[1], 255 * rgb[2]);
680
374
fmode = style->fill_rule.computed == 0 ? U_WINDING : (style->fill_rule.computed == 2 ? U_ALTERNATE : U_ALTERNATE);
682
else if(SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style))){ // must be paint-server
375
} else if (SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style))) { // must be paint-server
683
376
SPPaintServer *paintserver = style->fill.value.href->getObject();
684
SPPattern *pat = SP_PATTERN (paintserver);
377
SPPattern *pat = SP_PATTERN(paintserver);
685
378
double dwidth = pattern_width(pat);
686
379
double dheight = pattern_height(pat);
688
381
height = dheight;
689
brush_classify(pat,0,&pixbuf,&hatchType,&hatchColor,&bkColor);
690
if(pixbuf){ fill_mode = DRAW_IMAGE; }
382
brush_classify(pat, 0, &pixbuf, &hatchType, &hatchColor, &bkColor);
384
fill_mode = DRAW_IMAGE;
692
386
fill_mode = DRAW_PATTERN;
693
if(hatchType == -1){ // Not a standard hatch, so force it to something
387
if (hatchType == -1) { // Not a standard hatch, so force it to something
694
388
hatchType = U_HS_CROSS;
695
hatchColor = U_RGB(0xFF,0xC3,0xC3);
389
hatchColor = U_RGB(0xFF, 0xC3, 0xC3);
698
if(FixPPTPatternAsHatch){
699
if(hatchType == -1){ // image or unclassified
392
if (FixPPTPatternAsHatch) {
393
if (hatchType == -1) { // image or unclassified
700
394
fill_mode = DRAW_PATTERN;
701
395
hatchType = U_HS_DIAGCROSS;
702
hatchColor = U_RGB(0xFF,0xC3,0xC3);
396
hatchColor = U_RGB(0xFF, 0xC3, 0xC3);
705
399
brushStyle = U_BS_HATCHED;
707
else if(SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))){ // must be a gradient
400
} else if (SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))) { // must be a gradient
708
401
// currently we do not do anything with gradients, the code below just sets the color to the average of the stops
709
402
SPPaintServer *paintserver = style->fill.value.href->getObject();
710
403
SPLinearGradient *lg = NULL;
711
404
SPRadialGradient *rg = NULL;
713
if (SP_IS_LINEARGRADIENT (paintserver)) {
406
if (SP_IS_LINEARGRADIENT(paintserver)) {
714
407
lg = SP_LINEARGRADIENT(paintserver);
715
408
SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
716
409
fill_mode = DRAW_LINEAR_GRADIENT;
718
else if (SP_IS_RADIALGRADIENT (paintserver)) {
410
} else if (SP_IS_RADIALGRADIENT(paintserver)) {
719
411
rg = SP_RADIALGRADIENT(paintserver);
720
412
SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
721
413
fill_mode = DRAW_RADIAL_GRADIENT;
728
if(FixPPTGrad2Polys){ return hold_gradient(rg, fill_mode); }
729
else { hatchColor = avg_stop_color(rg); }
732
if(FixPPTGrad2Polys){ return hold_gradient(lg, fill_mode); }
733
else { hatchColor = avg_stop_color(lg); }
419
if (FixPPTGrad2Polys) {
420
return hold_gradient(rg, fill_mode);
422
hatchColor = avg_stop_color(rg);
425
if (FixPPTGrad2Polys) {
426
return hold_gradient(lg, fill_mode);
428
hatchColor = avg_stop_color(lg);
737
else { // if (!style)
432
} else { // if (!style)
742
case DRAW_LINEAR_GRADIENT: // fill with average color unless gradients are converted to slices
743
case DRAW_RADIAL_GRADIENT: // ditto
746
// SVG text has no background attribute, so OPAQUE mode ALWAYS cancels after the next draw, otherwise it would mess up future text output.
748
rec = U_WMRSETBKCOLOR_set(bkColor);
749
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
750
g_error("Fatal programming error in PrintWmf::create_brush at U_WMRSETBKCOLOR_set");
752
rec = U_WMRSETBKMODE_set(U_OPAQUE);
753
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
754
g_error("Fatal programming error in PrintWmf::create_brush at U_WMRSETBKMODE_set");
757
lb = U_WLOGBRUSH_set(brushStyle, hatchColor, hatchType);
758
rec = wcreatebrushindirect_set(&brush, wht, lb);
759
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
760
g_error("Fatal programming error in PrintWmf::create_brush at createbrushindirect_set");
770
U_BITMAPINFOHEADER Bmih;
772
rgba_px = (char *) gdk_pixbuf_get_pixels(pixbuf); // Do NOT free this!!!
773
colortype = U_BCBM_COLOR32;
774
(void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width*4, colortype, 0, 1);
775
// Not sure why the next swap is needed because the preceding does it, and the code is identical
776
// to that in stretchdibits_set, which does not need this.
777
swapRBinRGBA(px, width*height);
778
Bmih = bitmapinfoheader_set(width, height, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0);
779
Bmi = bitmapinfo_set(Bmih, ct);
780
rec = wcreatedibpatternbrush_srcdib_set(&brush, wht, U_DIB_RGB_COLORS, Bmi, cbPx, px);
781
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
782
g_error("Fatal programming error in PrintWmf::create_brush at createdibpatternbrushpt_set");
785
free(Bmi); // ct will be NULL because of colortype
437
case DRAW_LINEAR_GRADIENT: // fill with average color unless gradients are converted to slices
438
case DRAW_RADIAL_GRADIENT: // ditto
441
// SVG text has no background attribute, so OPAQUE mode ALWAYS cancels after the next draw, otherwise it would mess up future text output.
443
rec = U_WMRSETBKCOLOR_set(bkColor);
444
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
445
g_error("Fatal programming error in PrintWmf::create_brush at U_WMRSETBKCOLOR_set");
447
rec = U_WMRSETBKMODE_set(U_OPAQUE);
448
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
449
g_error("Fatal programming error in PrintWmf::create_brush at U_WMRSETBKMODE_set");
452
lb = U_WLOGBRUSH_set(brushStyle, hatchColor, hatchType);
453
rec = wcreatebrushindirect_set(&brush, wht, lb);
454
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
455
g_error("Fatal programming error in PrintWmf::create_brush at createbrushindirect_set");
465
U_BITMAPINFOHEADER Bmih;
467
rgba_px = (char *) gdk_pixbuf_get_pixels(pixbuf); // Do NOT free this!!!
468
colortype = U_BCBM_COLOR32;
469
(void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1);
470
// Not sure why the next swap is needed because the preceding does it, and the code is identical
471
// to that in stretchdibits_set, which does not need this.
472
swapRBinRGBA(px, width * height);
473
Bmih = bitmapinfoheader_set(width, height, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0);
474
Bmi = bitmapinfo_set(Bmih, ct);
475
rec = wcreatedibpatternbrush_srcdib_set(&brush, wht, U_DIB_RGB_COLORS, Bmi, cbPx, px);
476
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
477
g_error("Fatal programming error in PrintWmf::create_brush at createdibpatternbrushpt_set");
480
free(Bmi); // ct will be NULL because of colortype
789
484
hbrush = brush; // need this later for destroy_brush
790
485
rec = wselectobject_set(brush, wht);
791
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
486
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
792
487
g_error("Fatal programming error in PrintWmf::create_brush at wselectobject_set");
795
if(fmode != hpolyfillmode){
797
rec = U_WMRSETPOLYFILLMODE_set(fmode);
798
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
490
if (fmode != hpolyfillmode) {
491
hpolyfillmode = fmode;
492
rec = U_WMRSETPOLYFILLMODE_set(fmode);
493
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
799
494
g_error("Fatal programming error in PrintWmf::create_brush at U_WMRSETPOLYFILLMODE_set");
858
555
Geom::Point p1(one * transform);
859
556
Geom::Point p(p1 - p0);
861
double scale = sqrt( (p[X]*p[X]) + (p[Y]*p[Y]) ) / sqrt(2);
558
double scale = sqrt((p[X] * p[X]) + (p[Y] * p[Y])) / sqrt(2);
863
if(!style->stroke_width.computed){return 0;} //if width is 0 do not (reset) the pen, it should already be NULL_PEN
864
linewidth = MAX( 1, (uint32_t) round(scale * style->stroke_width.computed * PX2WORLD) );
560
if (!style->stroke_width.computed) {
561
return 0; //if width is 0 do not (reset) the pen, it should already be NULL_PEN
563
linewidth = MAX(1, (uint32_t) round(scale * style->stroke_width.computed * PX2WORLD));
866
565
// most WMF readers will ignore linecap and linejoin, but set them anyway. Inkscape itself can read them back in.
868
if ( style->stroke_linecap.computed == 0) { modstyle |= U_PS_ENDCAP_FLAT; }
869
else if (style->stroke_linecap.computed == 1) { modstyle |= U_PS_ENDCAP_ROUND; }
870
else { modstyle |= U_PS_ENDCAP_SQUARE; }
567
if (style->stroke_linecap.computed == 0) {
568
modstyle |= U_PS_ENDCAP_FLAT;
569
} else if (style->stroke_linecap.computed == 1) {
570
modstyle |= U_PS_ENDCAP_ROUND;
572
modstyle |= U_PS_ENDCAP_SQUARE;
872
575
if (style->stroke_linejoin.computed == 0) {
873
576
float miterlimit = style->stroke_miterlimit.value; // This is a ratio.
874
if (miterlimit < 1)miterlimit = 1;
577
if (miterlimit < 1) {
876
581
// most WMF readers will ignore miterlimit, but set it anyway. Inkscape itself can read it back in
877
if((uint32_t)miterlimit != hmiterlimit){
582
if ((uint32_t)miterlimit != hmiterlimit) {
878
583
hmiterlimit = (uint32_t)miterlimit;
879
584
rec = wmiterlimit_set((uint32_t) miterlimit);
880
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
585
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
881
586
g_error("Fatal programming error in PrintWmf::create_pen at wmiterlimit_set");
884
modstyle |= U_PS_JOIN_MITER;
589
modstyle |= U_PS_JOIN_MITER;
590
} else if (style->stroke_linejoin.computed == 1) {
591
modstyle |= U_PS_JOIN_ROUND;
593
modstyle |= U_PS_JOIN_BEVEL;
886
else if (style->stroke_linejoin.computed == 1) { modstyle |= U_PS_JOIN_ROUND; }
887
else { modstyle |= U_PS_JOIN_BEVEL; }
889
596
if (style->stroke_dash.n_dash &&
890
style->stroke_dash.dash )
892
if(!FixPPTDashLine){ // if this is set code elsewhere will break dots/dashes into many smaller lines.
597
style->stroke_dash.dash) {
598
if (!FixPPTDashLine) { // if this is set code elsewhere will break dots/dashes into many smaller lines.
893
599
penstyle = U_PS_DASH;// userstyle not supported apparently, for now map all Inkscape dot/dash to just dash
917
623
char *rec = NULL;
918
624
// WMF lets any object be deleted whenever, and the chips fall where they may...
920
626
rec = wdeleteobject_set(&hpen, wht);
921
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
627
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
922
628
g_error("Fatal programming error in PrintWmf::destroy_pen");
927
633
// (re)select the null pen
929
635
rec = wselectobject_set(hpen_null, wht);
930
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
636
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
931
637
g_error("Fatal programming error in PrintWmf::destroy_pen");
937
unsigned int PrintWmf::bind(Inkscape::Extension::Print * /*mod*/, Geom::Affine const &transform, float /*opacity*/)
939
if (!m_tr_stack.empty()) {
940
Geom::Affine tr_top = m_tr_stack.top();
941
m_tr_stack.push(transform * tr_top);
943
m_tr_stack.push(transform);
949
unsigned int PrintWmf::release(Inkscape::Extension::Print * /*mod*/)
955
#define clrweight(a,b,t) ((1-t)*((double) a) + (t)*((double) b))
956
inline U_COLORREF PrintWmf::weight_opacity(U_COLORREF c1){
957
float opa = c1.Reserved/255.0;
958
U_COLORREF result = U_RGB(
959
255*opweight((float)c1.Red /255.0, gv.rgb[0], opa),
960
255*opweight((float)c1.Green/255.0, gv.rgb[1], opa),
961
255*opweight((float)c1.Blue /255.0, gv.rgb[2], opa)
967
// return the color between c1 and c2, c1 for t=0, c2 for t=1.0
968
U_COLORREF PrintWmf::weight_colors(U_COLORREF c1, U_COLORREF c2, double t){
970
result.Red = clrweight(c1.Red, c2.Red, t);
971
result.Green = clrweight(c1.Green, c2.Green, t);
972
result.Blue = clrweight(c1.Blue, c2.Blue, t);
973
result.Reserved = clrweight(c1.Reserved, c2.Reserved, t);
975
// now handle the opacity, mix the RGB with background at the weighted opacity
977
if(result.Reserved != 255)result = weight_opacity(result);
982
/* convert from center ellipse to SVGEllipticalArc ellipse
985
http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
986
A point (x,y) on the arc can be found by:
988
{x,y} = {cx,cy} + {cosF,-sinF,sinF,cosF} x {rxcosT,rysinT}
991
{cx,cy} is the center of the ellipse
992
F is the rotation angle of the X axis of the ellipse from the true X axis
993
T is the rotation angle around the ellipse
994
{,,,} is the rotation matrix
995
rx,ry are the radii of the ellipse's axes
997
For SVG parameterization need two points.
998
Arbitrarily we can use T=0 and T=pi
999
Since the sweep is 180 the flags are always 0:
1001
F is in RADIANS, but the SVGEllipticalArc needs degrees!
1004
Geom::PathVector PrintWmf::center_ellipse_as_SVG_PathV(Geom::Point ctr, double rx, double ry, double F){
1010
x1 = ctr[X] + cos(F) * rx * cos(0) + sin(-F) * ry * sin(0);
1011
y1 = ctr[Y] + sin(F) * rx * cos(0) + cos(F) * ry * sin(0);
1012
x2 = ctr[X] + cos(F) * rx * cos(M_PI) + sin(-F) * ry * sin(M_PI);
1013
y2 = ctr[Y] + sin(F) * rx * cos(M_PI) + cos(F) * ry * sin(M_PI);
1016
sprintf(text," M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z",x1,y1, rx,ry,F*360./(2.*M_PI),x2,y2, rx,ry,F*360./(2.*M_PI),x1,y1);
1017
std::vector<Geom::Path> outres = Geom::parse_svg_path(text);
1022
/* rx2,ry2 must be larger than rx1,ry1!
1025
Geom::PathVector PrintWmf::center_elliptical_ring_as_SVG_PathV(Geom::Point ctr, double rx1, double ry1, double rx2, double ry2, double F){
1028
double x11,y11,x12,y12;
1029
double x21,y21,x22,y22;
1030
double degrot = F*360./(2.*M_PI);
1032
x11 = ctr[X] + cos(F) * rx1 * cos(0) + sin(-F) * ry1 * sin(0);
1033
y11 = ctr[Y] + sin(F) * rx1 * cos(0) + cos(F) * ry1 * sin(0);
1034
x12 = ctr[X] + cos(F) * rx1 * cos(M_PI) + sin(-F) * ry1 * sin(M_PI);
1035
y12 = ctr[Y] + sin(F) * rx1 * cos(M_PI) + cos(F) * ry1 * sin(M_PI);
1037
x21 = ctr[X] + cos(F) * rx2 * cos(0) + sin(-F) * ry2 * sin(0);
1038
y21 = ctr[Y] + sin(F) * rx2 * cos(0) + cos(F) * ry2 * sin(0);
1039
x22 = ctr[X] + cos(F) * rx2 * cos(M_PI) + sin(-F) * ry2 * sin(M_PI);
1040
y22 = ctr[Y] + sin(F) * rx2 * cos(M_PI) + cos(F) * ry2 * sin(M_PI);
1043
sprintf(text," M %f,%f A %f %f %f 0 1 %f %f A %f %f %f 0 1 %f %f z M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z",
1044
x11,y11, rx1,ry1,degrot,x12,y12, rx1,ry1,degrot,x11,y11,
1045
x21,y21, rx2,ry2,degrot,x22,y22, rx2,ry2,degrot,x21,y21
1047
std::vector<Geom::Path> outres = Geom::parse_svg_path(text);
1052
/* Elliptical hole in a large square extending from -50k to +50k */
1053
Geom::PathVector PrintWmf::center_elliptical_hole_as_SVG_PathV(Geom::Point ctr, double rx, double ry, double F){
1059
x1 = ctr[X] + cos(F) * rx * cos(0) + sin(-F) * ry * sin(0);
1060
y1 = ctr[Y] + sin(F) * rx * cos(0) + cos(F) * ry * sin(0);
1061
x2 = ctr[X] + cos(F) * rx * cos(M_PI) + sin(-F) * ry * sin(M_PI);
1062
y2 = ctr[Y] + sin(F) * rx * cos(M_PI) + cos(F) * ry * sin(M_PI);
1065
sprintf(text," M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z M 50000,50000 50000,-50000 -50000,-50000 -50000,50000 z",
1066
x1,y1, rx,ry,F*360./(2.*M_PI),x2,y2, rx,ry,F*360./(2.*M_PI),x1,y1);
1067
std::vector<Geom::Path> outres = Geom::parse_svg_path(text);
1071
/* rectangular cutter.
1072
ctr "center" of rectangle (might not actually be in the center with respect to leading/trailing edges
1073
pos vector from center to leading edge
1074
neg vector from center to trailing edge
1075
width vector to side edge
1077
Geom::PathVector PrintWmf::rect_cutter(Geom::Point ctr, Geom::Point pos, Geom::Point neg, Geom::Point width){
1078
std::vector<Geom::Path> outres;
1080
cutter.start( ctr + pos - width);
1081
cutter.appendNew<Geom::LineSegment>(ctr + pos + width);
1082
cutter.appendNew<Geom::LineSegment>(ctr + neg + width);
1083
cutter.appendNew<Geom::LineSegment>(ctr + neg - width);
1085
outres.push_back(cutter);
1089
/* Convert from SPWindRule to livarot's FillRule
1090
This is similar to what sp_selected_path_boolop() does
1092
FillRule PrintWmf::SPWR_to_LVFR(SPWindRule wr){
1094
if(wr == SP_WIND_RULE_EVENODD){ fr = fill_oddEven; }
1095
else { fr = fill_nonZero; }
1100
642
unsigned int PrintWmf::fill(
1101
643
Inkscape::Extension::Print * /*mod*/,
1102
644
Geom::PathVector const &pathv, Geom::Affine const & /*transform*/, SPStyle const *style,
1124
666
destroy_pen(); //this sets the NULL_PEN, otherwise gradient slices may display with boundaries, see longer explanation below
1125
667
Geom::Path cutter;
1127
U_COLORREF wc,c1,c2;
1128
FillRule frb = SPWR_to_LVFR( (SPWindRule) style->fill_rule.computed);
1129
double doff,doff_base,doff_range;
1130
double divisions= 128.0;
669
U_COLORREF wc, c1, c2;
670
FillRule frb = SPWR_to_LVFR((SPWindRule) style->fill_rule.computed);
671
double doff, doff_base, doff_range;
672
double divisions = 128.0;
1133
675
float opa; // opacity at stop
1135
SPRadialGradient *tg = (SPRadialGradient *) (gv.grad); // linear/radial are the same here
677
SPRadialGradient *tg = (SPRadialGradient *)(gv.grad); // linear/radial are the same here
1136
678
nstops = tg->vector.stops.size();
1137
679
sp_color_get_rgb_floatv(&tg->vector.stops[0].color, rgb);
1138
680
opa = tg->vector.stops[0].opacity;
1139
c1 = U_RGBA( 255*rgb[0], 255*rgb[1], 255*rgb[2], 255*opa );
1140
sp_color_get_rgb_floatv(&tg->vector.stops[nstops-1].color, rgb);
1141
opa = tg->vector.stops[nstops-1].opacity;
1142
c2 = U_RGBA( 255*rgb[0], 255*rgb[1], 255*rgb[2], 255*opa );
681
c1 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
682
sp_color_get_rgb_floatv(&tg->vector.stops[nstops - 1].color, rgb);
683
opa = tg->vector.stops[nstops - 1].opacity;
684
c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
1145
687
doff_base = 0.0;
1146
688
doff_range = tg->vector.stops[1].offset; // next or last stop
1148
if(gv.mode==DRAW_RADIAL_GRADIENT){
690
if (gv.mode == DRAW_RADIAL_GRADIENT) {
1149
691
Geom::Point xv = gv.p2 - gv.p1; // X' vector
1150
692
Geom::Point yv = gv.p3 - gv.p1; // Y' vector
1151
693
Geom::Point xuv = Geom::unit_vector(xv); // X' unit vector
1152
double rx = hypot(xv[X],xv[Y]);
1153
double ry = hypot(yv[X],yv[Y]);
1154
double range = fmax(rx,ry); // length along the gradient
1155
double step = range/divisions; // adequate approximation for gradient
1156
double overlap = step/4.0; // overlap slices slightly
694
double rx = hypot(xv[X], xv[Y]);
695
double ry = hypot(yv[X], yv[Y]);
696
double range = fmax(rx, ry); // length along the gradient
697
double step = range / divisions; // adequate approximation for gradient
698
double overlap = step / 4.0; // overlap slices slightly
1159
701
Geom::PathVector pathvc, pathvr;
1161
703
/* radial gradient might stop part way through the shape, fill with outer color from there to "infinity".
1162
704
Do this first so that outer colored ring will overlay it.
1164
pathvc = center_elliptical_hole_as_SVG_PathV(gv.p1, rx*(1.0 - overlap/range), ry*(1.0 - overlap/range), asin(xuv[Y]));
706
pathvc = center_elliptical_hole_as_SVG_PathV(gv.p1, rx * (1.0 - overlap / range), ry * (1.0 - overlap / range), asin(xuv[Y]));
1165
707
pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_oddEven, frb);
1166
wc = weight_opacity(c2);
708
wc = weight_opacity(c2);
1167
709
(void) create_brush(style, &wc);
1168
710
print_pathv(pathvr, fill_transform);
1170
712
sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb);
1171
713
opa = tg->vector.stops[istop].opacity;
1172
c2 = U_RGBA( 255*rgb[0], 255*rgb[1], 255*rgb[2], 255*opa );
714
c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
1174
for(start = 0.0; start < range; start += step, doff += 1./divisions){
716
for (start = 0.0; start < range; start += step, doff += 1. / divisions) {
1175
717
stop = start + step + overlap;
1176
if(stop > range)stop=range;
1177
wc = weight_colors(c1, c2, (doff - doff_base)/(doff_range-doff_base) );
721
wc = weight_colors(c1, c2, (doff - doff_base) / (doff_range - doff_base));
1178
722
(void) create_brush(style, &wc);
1180
pathvc = center_elliptical_ring_as_SVG_PathV(gv.p1, rx*start/range, ry*start/range, rx*stop/range, ry*stop/range, asin(xuv[Y]));
724
pathvc = center_elliptical_ring_as_SVG_PathV(gv.p1, rx * start / range, ry * start / range, rx * stop / range, ry * stop / range, asin(xuv[Y]));
1182
726
pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
1183
727
print_pathv(pathvr, fill_transform); // show the intersection
1185
if(doff >= doff_range - doff_base){
729
if (doff >= doff_range - doff_base) {
1187
if(istop >= nstops)continue; // could happen on a rounding error
731
if (istop >= nstops) {
732
continue; // could happen on a rounding error
1188
734
doff_base = doff_range;
1189
735
doff_range = tg->vector.stops[istop].offset; // next or last stop
1191
737
sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb);
1192
738
opa = tg->vector.stops[istop].opacity;
1193
c2 = U_RGBA( 255*rgb[0], 255*rgb[1], 255*rgb[2], 255*opa );
739
c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
1197
else if(gv.mode == DRAW_LINEAR_GRADIENT){
742
} else if (gv.mode == DRAW_LINEAR_GRADIENT) {
1198
743
Geom::Point uv = Geom::unit_vector(gv.p2 - gv.p1); // unit vector
1199
744
Geom::Point puv = uv.cw(); // perp. to unit vector
1200
double range = Geom::distance(gv.p1,gv.p2); // length along the gradient
1201
double step = range/divisions; // adequate approximation for gradient
1202
double overlap = step/4.0; // overlap slices slightly
745
double range = Geom::distance(gv.p1, gv.p2); // length along the gradient
746
double step = range / divisions; // adequate approximation for gradient
747
double overlap = step / 4.0; // overlap slices slightly
1205
750
Geom::PathVector pathvc, pathvr;
1207
752
/* before lower end of gradient, overlap first slice position */
1208
wc = weight_opacity(c1);
753
wc = weight_opacity(c1);
1209
754
(void) create_brush(style, &wc);
1210
pathvc = rect_cutter(gv.p1, uv*(overlap), uv*(-50000.0), puv*50000.0);
755
pathvc = rect_cutter(gv.p1, uv * (overlap), uv * (-50000.0), puv * 50000.0);
1211
756
pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
1212
757
print_pathv(pathvr, fill_transform);
1214
759
/* after high end of gradient, overlap last slice poosition */
1215
wc = weight_opacity(c2);
760
wc = weight_opacity(c2);
1216
761
(void) create_brush(style, &wc);
1217
pathvc = rect_cutter(gv.p2, uv*(-overlap), uv*(50000.0), puv*50000.0);
762
pathvc = rect_cutter(gv.p2, uv * (-overlap), uv * (50000.0), puv * 50000.0);
1218
763
pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
1219
764
print_pathv(pathvr, fill_transform);
1221
766
sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb);
1222
767
opa = tg->vector.stops[istop].opacity;
1223
c2 = U_RGBA( 255*rgb[0], 255*rgb[1], 255*rgb[2], 255*opa );
768
c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
1225
for(start = 0.0; start < range; start += step, doff += 1./divisions){
770
for (start = 0.0; start < range; start += step, doff += 1. / divisions) {
1226
771
stop = start + step + overlap;
1227
if(stop > range)stop=range;
1228
pathvc = rect_cutter(gv.p1, uv*start, uv*stop, puv*50000.0);
775
pathvc = rect_cutter(gv.p1, uv * start, uv * stop, puv * 50000.0);
1230
wc = weight_colors(c1, c2, (doff - doff_base)/(doff_range-doff_base) );
777
wc = weight_colors(c1, c2, (doff - doff_base) / (doff_range - doff_base));
1231
778
(void) create_brush(style, &wc);
1232
779
Geom::PathVector pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
1233
780
print_pathv(pathvr, fill_transform); // show the intersection
1235
if(doff >= doff_range - doff_base){
782
if (doff >= doff_range - doff_base) {
1237
if(istop >= nstops)continue; // could happen on a rounding error
784
if (istop >= nstops) {
785
continue; // could happen on a rounding error
1238
787
doff_base = doff_range;
1239
788
doff_range = tg->vector.stops[istop].offset; // next or last stop
1241
790
sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb);
1242
791
opa = tg->vector.stops[istop].opacity;
1243
c2 = U_RGBA( 255*rgb[0], 255*rgb[1], 255*rgb[2], 255*opa );
792
c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
1248
796
g_error("Fatal programming error in PrintWmf::fill, invalid gradient type detected");
1250
798
use_fill = false; // gradients handled, be sure stroke does not use stroke and fill
1254
Inkscape was not calling create_pen for objects with no border.
801
Inkscape was not calling create_pen for objects with no border.
1255
802
This was because it never called stroke() (next method).
1256
803
PPT, and presumably others, pick whatever they want for the border if it is not specified, so no border can
1257
804
become a visible border.
1258
805
To avoid this force the pen to NULL_PEN if we can determine that no pen will be needed after the fill.
1260
if (style->stroke.noneSet || style->stroke_width.computed == 0.0){
807
if (style->stroke.noneSet || style->stroke_width.computed == 0.0) {
1261
808
destroy_pen(); //this sets the NULL_PEN
1287
unsigned int PrintWmf::stroke (
836
unsigned int PrintWmf::stroke(
1288
837
Inkscape::Extension::Print * /*mod*/,
1289
838
Geom::PathVector const &pathv, const Geom::Affine &/*transform*/, const SPStyle *style,
1290
839
Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/)
1293
842
char *rec = NULL;
1294
843
Geom::Affine tf = m_tr_stack.top();
1296
845
use_stroke = true;
1297
846
// use_fill was set in ::fill, if it is needed, if not, the null brush is used, it should be already set
1299
if (create_pen(style, tf))return 0;
1301
if (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine ){
848
if (create_pen(style, tf)) {
852
if (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) {
1302
853
// convert the path, gets its complete length, and then make a new path with parameter length instead of t
1303
854
Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw; // pathv-> sbasis
1304
855
Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw2; // sbasis using arc length parameter
1305
856
Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw3; // new (discontinuous) path, composed of dots/dashes
1306
857
Geom::Piecewise<Geom::D2<Geom::SBasis> > first_frag; // first fragment, will be appended at end
1307
858
int n_dash = style->stroke_dash.n_dash;
1308
int i=0; //dash index
859
int i = 0; //dash index
1309
860
double tlength; // length of tmp_pathpw
1310
double slength=0.0; // start of gragment
861
double slength = 0.0; // start of gragment
1311
862
double elength; // end of gragment
1312
for (unsigned int i=0; i < pathv.size(); i++) {
863
for (unsigned int i = 0; i < pathv.size(); i++) {
1313
864
tmp_pathpw.concat(pathv[i].toPwSb());
1315
tlength = length(tmp_pathpw,0.1);
866
tlength = length(tmp_pathpw, 0.1);
1316
867
tmp_pathpw2 = arc_length_parametrization(tmp_pathpw);
1318
869
// go around the dash array repeatedly until the entire path is consumed (but not beyond).
1319
while(slength < tlength){
870
while (slength < tlength) {
1320
871
elength = slength + style->stroke_dash.dash[i++];
1321
if(elength > tlength)elength = tlength;
872
if (elength > tlength) {
1322
875
Geom::Piecewise<Geom::D2<Geom::SBasis> > fragment(portion(tmp_pathpw2, slength, elength));
1323
if(slength){ tmp_pathpw3.concat(fragment); }
1324
else { first_frag = fragment; }
877
tmp_pathpw3.concat(fragment);
879
first_frag = fragment;
1325
881
slength = elength;
1326
882
slength += style->stroke_dash.dash[i++]; // the gap
1329
887
tmp_pathpw3.concat(first_frag); // may merge line around start point
1330
Geom::PathVector out_pathv = Geom::path_from_piecewise(tmp_pathpw3, 0.01);
888
Geom::PathVector out_pathv = Geom::path_from_piecewise(tmp_pathpw3, 0.01);
1331
889
print_pathv(out_pathv, tf);
1334
891
print_pathv(pathv, tf);
1337
894
use_stroke = false;
1338
895
use_fill = false;
1340
if(usebk){ // OPAQUE was set, revert to TRANSPARENT
897
if (usebk) { // OPAQUE was set, revert to TRANSPARENT
1342
899
rec = U_WMRSETBKMODE_set(U_TRANSPARENT);
1343
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
900
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1344
901
g_error("Fatal programming error in PrintWmf::stroke at U_WMRSETBKMODE_set");
1454
1008
lpPoints[i].x = x1;
1455
1009
lpPoints[i].y = y1;
1456
lpPoints[i+1].x = x2;
1457
lpPoints[i+1].y = y2;
1458
lpPoints[i+2].x = x3;
1459
lpPoints[i+2].y = y3;
1010
lpPoints[i + 1].x = x2;
1011
lpPoints[i + 1].y = y2;
1012
lpPoints[i + 2].x = x3;
1013
lpPoints[i + 2].y = y3;
1465
1019
bool done = false;
1466
bool closed = (lpPoints[0].x == lpPoints[i-1].x) && (lpPoints[0].y == lpPoints[i-1].y);
1020
bool closed = (lpPoints[0].x == lpPoints[i - 1].x) && (lpPoints[0].y == lpPoints[i - 1].y);
1467
1021
bool polygon = false;
1468
1022
bool rectangle = false;
1469
1023
bool ellipse = false;
1471
if (moves == 1 && moves+lines == nodes && closed) {
1025
if (moves == 1 && moves + lines == nodes && closed) {
1472
1026
polygon = true;
1473
// if (nodes==5) { // disable due to LP Bug 407394
1474
// if (lpPoints[0].x == lpPoints[3].x && lpPoints[1].x == lpPoints[2].x &&
1475
// lpPoints[0].y == lpPoints[1].y && lpPoints[2].y == lpPoints[3].y)
1477
// rectangle = true;
1481
else if (moves == 1 && nodes == 5 && moves+curves == nodes && closed) {
1482
// if (lpPoints[0].x == lpPoints[1].x && lpPoints[1].x == lpPoints[11].x &&
1483
// lpPoints[5].x == lpPoints[6].x && lpPoints[6].x == lpPoints[7].x &&
1484
// lpPoints[2].x == lpPoints[10].x && lpPoints[3].x == lpPoints[9].x && lpPoints[4].x == lpPoints[8].x &&
1485
// lpPoints[2].y == lpPoints[3].y && lpPoints[3].y == lpPoints[4].y &&
1486
// lpPoints[8].y == lpPoints[9].y && lpPoints[9].y == lpPoints[10].y &&
1487
// lpPoints[5].y == lpPoints[1].y && lpPoints[6].y == lpPoints[0].y && lpPoints[7].y == lpPoints[11].y)
1488
// { // disable due to LP Bug 407394
1027
// if (nodes==5) { // disable due to LP Bug 407394
1028
// if (lpPoints[0].x == lpPoints[3].x && lpPoints[1].x == lpPoints[2].x &&
1029
// lpPoints[0].y == lpPoints[1].y && lpPoints[2].y == lpPoints[3].y)
1031
// rectangle = true;
1034
} else if (moves == 1 && nodes == 5 && moves + curves == nodes && closed) {
1035
// if (lpPoints[0].x == lpPoints[1].x && lpPoints[1].x == lpPoints[11].x &&
1036
// lpPoints[5].x == lpPoints[6].x && lpPoints[6].x == lpPoints[7].x &&
1037
// lpPoints[2].x == lpPoints[10].x && lpPoints[3].x == lpPoints[9].x && lpPoints[4].x == lpPoints[8].x &&
1038
// lpPoints[2].y == lpPoints[3].y && lpPoints[3].y == lpPoints[4].y &&
1039
// lpPoints[8].y == lpPoints[9].y && lpPoints[9].y == lpPoints[10].y &&
1040
// lpPoints[5].y == lpPoints[1].y && lpPoints[6].y == lpPoints[0].y && lpPoints[7].y == lpPoints[11].y)
1041
// { // disable due to LP Bug 407394
1493
1046
if (polygon || ellipse) {
1494
1047
// pens and brushes already set by caller, do not touch them
1498
U_RECT16 rcl = U_RECT16_set((U_POINT16) {lpPoints[0].x, lpPoints[0].y}, (U_POINT16) {lpPoints[2].x, lpPoints[2].y});
1051
U_RECT16 rcl = U_RECT16_set((U_POINT16) {
1052
lpPoints[0].x, lpPoints[0].y
1054
lpPoints[2].x, lpPoints[2].y
1499
1056
rec = U_WMRRECTANGLE_set(rcl);
1502
1058
rec = U_WMRPOLYGON_set(nodes, lpPoints);
1506
U_RECT16 rcl = U_RECT16_set((U_POINT16) {lpPoints[6].x, lpPoints[3].y}, (U_POINT16) {lpPoints[0].x, lpPoints[9].y});
1060
} else if (ellipse) {
1061
U_RECT16 rcl = U_RECT16_set((U_POINT16) {
1062
lpPoints[6].x, lpPoints[3].y
1064
lpPoints[0].x, lpPoints[9].y
1507
1066
rec = U_WMRELLIPSE_set(rcl);
1509
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
1068
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1510
1069
g_error("Fatal programming error in PrintWmf::print_simple_shape at retangle/ellipse/polygon");
1517
1076
delete[] lpPoints;
1522
1081
/** Some parts based on win32.cpp by Lauris Kaplinski <lauris@kaplinski.com>. Was a part of Inkscape
1523
1082
in the past (or will be in the future?) Not in current trunk. (4/19/2012)
1525
1084
Limitations of this code:
1526
1085
1. Images lose their rotation, one corner stays in the same place.
1527
1086
2. Transparency is lost on export. (A limitation of the WMF format.)
1568
1127
U_BITMAPINFOHEADER Bmih;
1569
1128
PU_BITMAPINFO Bmi;
1570
1129
colortype = U_BCBM_COLOR32;
1571
(void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, (char *) rgba_px, w, h, w*4, colortype, 0, 1);
1130
(void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, (char *) rgba_px, w, h, w * 4, colortype, 0, 1);
1572
1131
Bmih = bitmapinfoheader_set(w, h, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0);
1573
1132
Bmi = bitmapinfo_set(Bmih, ct);
1575
1134
U_POINT16 Dest = point16_set(round(pLL2[Geom::X] * PX2WORLD), round(pLL2[Geom::Y] * PX2WORLD));
1576
1135
U_POINT16 cDest = point16_set(round(dw * PX2WORLD), round(dh * PX2WORLD));
1577
U_POINT16 Src = point16_set(0,0);
1578
U_POINT16 cSrc = point16_set(w,h);
1136
U_POINT16 Src = point16_set(0, 0);
1137
U_POINT16 cSrc = point16_set(w, h);
1579
1138
rec = U_WMRSTRETCHDIB_set(
1580
Dest, //! Destination UL corner in logical units
1581
cDest, //! Destination W & H in logical units
1582
Src, //! Source UL corner in logical units
1583
cSrc, //! Source W & H in logical units
1584
U_DIB_RGB_COLORS, //! DIBColors Enumeration
1585
U_SRCCOPY, //! RasterOPeration Enumeration
1586
Bmi, //! (Optional) bitmapbuffer (U_BITMAPINFO section)
1587
h*rs, //! size in bytes of px
1588
px //! (Optional) bitmapbuffer (U_BITMAPINFO section)
1590
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
1139
Dest, //! Destination UL corner in logical units
1140
cDest, //! Destination W & H in logical units
1141
Src, //! Source UL corner in logical units
1142
cSrc, //! Source W & H in logical units
1143
U_DIB_RGB_COLORS, //! DIBColors Enumeration
1144
U_SRCCOPY, //! RasterOPeration Enumeration
1145
Bmi, //! (Optional) bitmapbuffer (U_BITMAPINFO section)
1146
h * rs, //! size in bytes of px
1147
px //! (Optional) bitmapbuffer (U_BITMAPINFO section)
1149
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1591
1150
g_error("Fatal programming error in PrintWmf::image at U_WMRSTRETCHDIB_set");
1650
1218
Geom::Point p1 = pit->initialPoint(); // This point is special, it isn't in the interator
1652
1220
p1[X] = (p1[X] * PX2WORLD);
1653
p1[Y] = (p1[Y] * PX2WORLD);
1221
p1[Y] = (p1[Y] * PX2WORLD);
1654
1222
*pt16ptr++ = point16_set((int32_t) round(p1[X]), (int32_t) round(p1[Y]));
1656
for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit)
1224
for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) {
1658
1225
Geom::Point p1 = cit->finalPoint();
1660
1227
p1[X] = (p1[X] * PX2WORLD);
1661
p1[Y] = (p1[Y] * PX2WORLD);
1228
p1[Y] = (p1[Y] * PX2WORLD);
1662
1229
*pt16ptr++ = point16_set((int32_t) round(p1[X]), (int32_t) round(p1[Y]));
1666
rec = U_WMRPOLYPOLYGON_set(nPolys, n16hold,pt16hold);
1667
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
1233
rec = U_WMRPOLYPOLYGON_set(nPolys, n16hold, pt16hold);
1234
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1668
1235
g_error("Fatal programming error in PrintWmf::print_pathv at U_WMRPOLYPOLYGON_set");
1670
1237
free(pt16hold);
1673
else { // one or more polyline or polygons (but not all polygons, that would be the preceding case)
1674
for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit)
1239
} else { // one or more polyline or polygons (but not all polygons, that would be the preceding case)
1240
for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) {
1679
/* Malformatted Polylines with a sequence like M L M M L have been seen, the 2nd M does nothing
1244
/* Malformatted Polylines with a sequence like M L M M L have been seen, the 2nd M does nothing
1680
1245
and that point must not go into the output. */
1681
if(!(pit->size_default())){ continue; }
1246
if (!(pit->size_default())) {
1682
1249
/* Figure out how many points there are, make an array big enough to hold them, and store
1683
1250
all the points. This is the same for open or closed path. This gives the upper bound for
1684
1251
the number of points. The actual number used is calculated on the fly.
1686
1253
int nPoints = 1 + pit->size_default();
1688
1255
pt16hold = pt16ptr = (PU_POINT16) malloc(nPoints * sizeof(U_POINT16));
1691
1260
/** For each segment in the subpath */
1693
1262
Geom::Point p1 = pit->initialPoint(); // This point is special, it isn't in the interator
1695
1264
p1[X] = (p1[X] * PX2WORLD);
1696
p1[Y] = (p1[Y] * PX2WORLD);
1265
p1[Y] = (p1[Y] * PX2WORLD);
1697
1266
*pt16ptr++ = point16_set((int32_t) round(p1[X]), (int32_t) round(p1[Y]));
1700
for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_default(); ++cit, nPoints++)
1269
for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_default(); ++cit, nPoints++) {
1702
1270
Geom::Point p1 = cit->finalPoint();
1704
1272
p1[X] = (p1[X] * PX2WORLD);
1705
p1[Y] = (p1[Y] * PX2WORLD);
1273
p1[Y] = (p1[Y] * PX2WORLD);
1706
1274
*pt16ptr++ = point16_set((int32_t) round(p1[X]), (int32_t) round(p1[Y]));
1709
1277
if (pit->end_default() == pit->end_closed()) {
1710
rec = U_WMRPOLYGON_set(nPoints, pt16hold);
1711
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
1278
rec = U_WMRPOLYGON_set(nPoints, pt16hold);
1279
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1712
1280
g_error("Fatal programming error in PrintWmf::print_pathv at U_WMRPOLYGON_set");
1715
else if(nPoints>2) {
1282
} else if (nPoints > 2) {
1716
1283
rec = U_WMRPOLYLINE_set(nPoints, pt16hold);
1717
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
1284
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1718
1285
g_error("Fatal programming error in PrintWmf::print_pathv at U_POLYLINE_set");
1721
else if(nPoints == 2) {
1287
} else if (nPoints == 2) {
1722
1288
rec = U_WMRMOVETO_set(pt16hold[0]);
1723
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
1289
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1724
1290
g_error("Fatal programming error in PrintWmf::print_pathv at U_WMRMOVETO_set");
1726
1292
rec = U_WMRLINETO_set(pt16hold[1]);
1727
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
1293
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1728
1294
g_error("Fatal programming error in PrintWmf::print_pathv at U_WMRLINETO_set");
1736
1302
// WMF has no fill or stroke commands, the draw does it with active pen/brush
1738
1304
// clean out brush and pen, but only after all parts of the draw complete
1739
if (use_fill){ destroy_brush(); }
1740
if (use_stroke){ destroy_pen(); }
1746
bool PrintWmf::textToPath(Inkscape::Extension::Print * ext)
1748
return ext->get_param_bool("textToPath");
1751
1316
unsigned int PrintWmf::text(Inkscape::Extension::Print * /*mod*/, char const *text, Geom::Point const &p,
1752
SPStyle const *const style)
1317
SPStyle const *const style)
1756
1323
char *rec = NULL;
1324
int ccount, newfont;
1759
1326
uint32_t hfont = 0;
1760
1327
Geom::Affine tf = m_tr_stack.top();
1761
double rot = -1800.0*std::atan2(tf[1], tf[0])/M_PI; // 0.1 degree rotation, - sign for MM_TEXT
1328
double rot = -1800.0 * std::atan2(tf[1], tf[0]) / M_PI; // 0.1 degree rotation, - sign for MM_TEXT
1762
1329
double rotb = -std::atan2(tf[1], tf[0]); // rotation for baseline offset for superscript/subscript, used below
1767
1333
// 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
1770
smuggle_adxky_out(text, &adx, &ky, &rtl, &ndx, PX2WORLD * std::min(tf.expansionX(),tf.expansionY())); // side effect: free() adx
1336
smuggle_adxky_out(text, &adx, &ky, &rtl, &ndx, PX2WORLD * std::min(tf.expansionX(), tf.expansionY())); // side effect: free() adx
1772
1338
uint32_t textalignment;
1773
if(rtl > 0){ textalignment = U_TA_BASELINE | U_TA_LEFT; }
1774
else { textalignment = U_TA_BASELINE | U_TA_RIGHT | U_TA_RTLREADING; }
1775
if(textalignment != htextalignment){
1340
textalignment = U_TA_BASELINE | U_TA_LEFT;
1342
textalignment = U_TA_BASELINE | U_TA_RIGHT | U_TA_RTLREADING;
1344
if (textalignment != htextalignment) {
1776
1345
htextalignment = textalignment;
1777
1346
rec = U_WMRSETTEXTALIGN_set(textalignment);
1778
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
1347
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1779
1348
g_error("Fatal programming error in PrintWmf::text at U_WMRSETTEXTALIGN_set");
1783
1352
char *text2 = strdup(text); // because U_Utf8ToUtf16le calls iconv which does not like a const char *
1784
uint16_t *unicode_text = U_Utf8ToUtf16le( text2, 0, NULL );
1353
uint16_t *unicode_text = U_Utf8ToUtf16le(text2, 0, NULL);
1786
1355
//translates Unicode as Utf16le to NonUnicode, if possible. If any translate, all will, and all to
1787
1356
//the same font, because of code in Layout::print
1788
1357
UnicodeToNon(unicode_text, &ccount, &newfont);
1789
1358
// The preceding hopefully handled conversions to symbol, wingdings or zapf dingbats. Now slam everything
1790
1359
// else down into latin1, which is all WMF can handle. If the language isn't English expect terrible results.
1791
char *latin1_text = U_Utf16leToLatin1( unicode_text, 0, NULL );
1360
char *latin1_text = U_Utf16leToLatin1(unicode_text, 0, NULL);
1792
1361
free(unicode_text);
1794
1363
//PPT gets funky with text within +-1 degree of a multiple of 90, but only for SOME fonts.Snap those to the central value
1795
1364
//Some funky ones: Arial, Times New Roman
1796
1365
//Some not funky ones: Symbol and Verdana.
1797
1366
//Without a huge table we cannot catch them all, so just the most common problem ones.
1801
search_short_fflist("Convert To Symbol", &f1, &f2, &f3);
1804
search_short_fflist("Convert To Zapf Dingbats", &f1, &f2, &f3);
1807
search_short_fflist("Convert To Wingdings", &f1, &f2, &f3);
1809
default: //also CVTNON
1810
search_short_fflist(style->text->font_family.value, &f1, &f2, &f3);
1367
FontfixParams params;
1369
if (FixPPTCharPos) {
1372
_lookup_ppt_fontfix("Convert To Symbol", params);
1375
_lookup_ppt_fontfix("Convert To Zapf Dingbats", params);
1378
_lookup_ppt_fontfix("Convert To Wingdings", params);
1380
default: //also CVTNON
1381
_lookup_ppt_fontfix(style->text->font_family.value, params);
1384
if (params.f2 != 0 || params.f3 != 0) {
1814
1385
int irem = ((int) round(rot)) % 900 ;
1815
if(irem <=9 && irem >= -9){
1816
fix90n=1; //assume vertical
1817
rot = (double) (((int) round(rot)) - irem);
1818
rotb = rot*M_PI/1800.0;
1819
if( abs(rot) == 900.0 ){ fix90n = 2; }
1386
if (irem <= 9 && irem >= -9) {
1387
fix90n = 1; //assume vertical
1388
rot = (double)(((int) round(rot)) - irem);
1389
rotb = rot * M_PI / 1800.0;
1390
if (abs(rot) == 900.0) {
1825
Note that text font sizes are stored into the WMF as fairly small integers and that limits their precision.
1398
Note that text font sizes are stored into the WMF as fairly small integers and that limits their precision.
1826
1399
The WMF output files produced here have been designed so that the integer valued pt sizes
1827
1400
land right on an integer value in the WMF file, so those are exact. However, something like 18.1 pt will be
1828
somewhat off, so that when it is read back in it becomes 18.11 pt. (For instance.)
1401
somewhat off, so that when it is read back in it becomes 18.11 pt. (For instance.)
1830
int textheight = round(-style->font_size.computed * PX2WORLD * std::min(tf.expansionX(),tf.expansionY()));
1403
int textheight = round(-style->font_size.computed * PX2WORLD * std::min(tf.expansionX(), tf.expansionY()));
1833
1406
// Get font face name. Use changed font name if unicode mapped to one
1834
1407
// of the special fonts.
1835
1408
char *facename;
1836
if(!newfont){ facename = U_Utf8ToLatin1(style->text->font_family.value, 0, NULL); }
1837
else { facename = U_Utf8ToLatin1(FontName(newfont), 0, NULL); }
1410
facename = U_Utf8ToLatin1(style->text->font_family.value, 0, NULL);
1412
facename = U_Utf8ToLatin1(FontName(newfont), 0, NULL);
1839
1415
// Scale the text to the minimum stretch. (It tends to stay within bounding rectangles even if
1840
1416
// it was streteched asymmetrically.) Few applications support text from WMF which is scaled
1841
// differently by height/width, so leave lfWidth alone.
1417
// differently by height/width, so leave lfWidth alone.
1843
1419
PU_FONT puf = U_FONT_set(
1848
transweight(style->font_weight.computed),
1849
(style->font_style.computed == SP_CSS_FONT_STYLE_ITALIC),
1850
style->text_decoration_line.underline,
1851
style->text_decoration_line.line_through,
1853
U_OUT_DEFAULT_PRECIS,
1854
U_CLIP_DEFAULT_PRECIS,
1856
U_DEFAULT_PITCH | U_FF_DONTCARE,
1860
rec = wcreatefontindirect_set( &hfont, wht, puf);
1861
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
1424
_translate_weight(style->font_weight.computed),
1425
(style->font_style.computed == SP_CSS_FONT_STYLE_ITALIC),
1426
style->text_decoration_line.underline,
1427
style->text_decoration_line.line_through,
1429
U_OUT_DEFAULT_PRECIS,
1430
U_CLIP_DEFAULT_PRECIS,
1432
U_DEFAULT_PITCH | U_FF_DONTCARE,
1436
rec = wcreatefontindirect_set(&hfont, wht, puf);
1437
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1862
1438
g_error("Fatal programming error in PrintWmf::text at wcreatefontindirect_set");
1867
1443
rec = wselectobject_set(hfont, wht);
1868
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
1444
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1869
1445
g_error("Fatal programming error in PrintWmf::text at wselectobject_set");
1873
sp_color_get_rgb_floatv( &style->fill.value.color, rgb );
1874
// only change the text color when it needs to be changed
1875
if(memcmp(htextcolor_rgb,rgb,3*sizeof(float))){
1876
memcpy(htextcolor_rgb,rgb,3*sizeof(float));
1877
rec = U_WMRSETTEXTCOLOR_set(U_RGB(255*rgb[0], 255*rgb[1], 255*rgb[2]));
1878
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
1449
sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1450
// only change the text color when it needs to be changed
1451
if (memcmp(htextcolor_rgb, rgb, 3 * sizeof(float))) {
1452
memcpy(htextcolor_rgb, rgb, 3 * sizeof(float));
1453
rec = U_WMRSETTEXTCOLOR_set(U_RGB(255 * rgb[0], 255 * rgb[1], 255 * rgb[2]));
1454
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1879
1455
g_error("Fatal programming error in PrintWmf::text at U_WMRSETTEXTCOLOR_set");
1884
1460
// Text alignment:
1885
1461
// - (x,y) coordinates received by this filter are those of the point where the text
1886
1462
// actually starts, and already takes into account the text object's alignment;
1923
1497
int32_t const xpos = (int32_t) round(p2[Geom::X]);
1924
1498
int32_t const ypos = (int32_t) round(p2[Geom::Y]);
1926
// The number of characters in the string is a bit fuzzy. ndx, the number of entries in adx is
1500
// The number of characters in the string is a bit fuzzy. ndx, the number of entries in adx is
1927
1501
// the number of VISIBLE characters, since some may combine from the UTF (8 originally,
1928
1502
// now 16) encoding. Conversely strlen() or wchar16len() would give the absolute number of
1929
1503
// encoding characters. Unclear if emrtext wants the former or the latter but for now assume the former.
1931
// This is currently being smuggled in from caller as part of text, works
1932
// MUCH better than the fallback hack below
1933
// uint32_t *adx = dx_set(textheight, U_FW_NORMAL, slen); // dx is needed, this makes one up
1935
rec = U_WMREXTTEXTOUT_set((U_POINT16) {xpos, ypos}, ndx, U_ETO_NONE, latin1_text, adx, U_RCL16_DEF);
1937
else { // RTL text, U_TA_RTLREADING should be enough, but set this one too just in case
1938
rec = U_WMREXTTEXTOUT_set((U_POINT16) {xpos, ypos}, ndx, U_ETO_RTLREADING, latin1_text, adx, U_RCL16_DEF);
1505
// This is currently being smuggled in from caller as part of text, works
1506
// MUCH better than the fallback hack below
1507
// uint32_t *adx = dx_set(textheight, U_FW_NORMAL, slen); // dx is needed, this makes one up
1509
rec = U_WMREXTTEXTOUT_set((U_POINT16) {
1510
(int16_t) xpos, (int16_t) ypos
1512
ndx, U_ETO_NONE, latin1_text, adx, U_RCL16_DEF);
1513
} else { // RTL text, U_TA_RTLREADING should be enough, but set this one too just in case
1514
rec = U_WMREXTTEXTOUT_set((U_POINT16) {
1515
(int16_t) xpos, (int16_t) ypos
1517
ndx, U_ETO_RTLREADING, latin1_text, adx, U_RCL16_DEF);
1940
1519
free(latin1_text);
1942
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
1521
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1943
1522
g_error("Fatal programming error in PrintWmf::text at U_WMREXTTEXTOUTW_set");
1946
1525
rec = wdeleteobject_set(&hfont, wht);
1947
if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){
1526
if (!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)) {
1948
1527
g_error("Fatal programming error in PrintWmf::text at wdeleteobject_set");
1954
void PrintWmf::init (void)
1533
void PrintWmf::init(void)
1956
read_system_fflist();
1535
_load_ppt_fontfix_data();
1958
1537
/* WMF print */
1959
1538
Inkscape::Extension::build_from_mem(