1 /* Copyright (C) 2003-2012 by George Williams */
2 /*
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5
6 * Redistributions of source code must retain the above copyright notice, this
7 * list of conditions and the following disclaimer.
8
9 * Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12
13 * The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <fontforge-config.h>
29
30 #include "svg.h"
31
32 #include "autohint.h"
33 #include "chardata.h"
34 #include "cvimages.h"
35 #include "dumppfa.h"
36 #include "encoding.h"
37 #include "fontforgevw.h"
38 #include "fvfonts.h"
39 #include "gfile.h"
40 #include "gutils.h"
41 #include "lookups.h"
42 #include "namelist.h"
43 #include "parsettf.h"
44 #include "psread.h"
45 #include "sd.h"
46 #include "splineorder2.h"
47 #include "splinesaveafm.h"
48 #include "splinestroke.h"
49 #include "splineutil.h"
50 #include "splineutil2.h"
51 #include "tottf.h"
52 #include "tottfgpos.h"
53 #include "ustring.h"
54 #include "utanvec.h"
55 #include "utype.h"
56
57 #include <locale.h>
58 #include <math.h>
59 #include <sys/stat.h>
60 #include <sys/types.h>
61 #include <time.h>
62 #include <unistd.h>
63
64 /* ************************************************************************** */
65 /* **************************** SVG Output **************************** */
66 /* ************************************************************************** */
67
latin1ToUtf8Out(FILE * file,char * str)68 static void latin1ToUtf8Out(FILE *file,char *str) {
69 /* beware of characters above 0x80, also &, <, > (things that are magic for xml) */
70 while ( *str ) {
71 if ( *str=='&' || *str=='<' || *str=='>' || (*str&0x80) )
72 fprintf( file, "&#%d;", (uint8) *str);
73 else
74 putc(*str,file);
75 ++str;
76 }
77 }
78
svg_outfontheader(FILE * file,SplineFont * sf,int layer)79 static int svg_outfontheader(FILE *file, SplineFont *sf,int layer) {
80 int defwid = SFFigureDefWidth(sf,NULL);
81 struct pfminfo info;
82 static const char *condexp[] = { "squinchy", "ultra-condensed", "extra-condensed",
83 "condensed", "semi-condensed", "normal", "semi-expanded", "expanded",
84 "extra-expanded", "ultra-expanded", "broad" };
85 DBounds bb;
86 BlueData bd;
87 char *hash, *hasv, ch;
88 int minu, maxu, i;
89 const char *author = GetAuthor();
90
91 memset(&info,0,sizeof(info));
92 SFDefaultOS2Info(&info,sf,sf->fontname);
93 SplineFontLayerFindBounds(sf,layer,&bb);
94 QuickBlues(sf,layer,&bd);
95
96 fprintf( file, "<?xml version=\"1.0\" standalone=\"no\"?>\n" );
97 fprintf( file, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\" >\n" );
98 if ( sf->comments!=NULL ) {
99 fprintf( file, "<!--\n" );
100 latin1ToUtf8Out(file,sf->comments);
101 fprintf( file, "\n-->\n" );
102 }
103 fprintf( file, "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\">\n" );
104 fprintf( file, "<metadata>\nCreated by FontForge %s at %s",
105 FONTFORGE_VERSION, ctime((time_t*)&sf->modificationtime) );
106 if ( author!=NULL )
107 fprintf(file," By %s\n", author);
108 else
109 fprintf(file,"\n" );
110 if ( sf->copyright!=NULL ) {
111 latin1ToUtf8Out(file,sf->copyright);
112 putc('\n',file);
113 }
114 fprintf( file, "</metadata>\n" );
115 fprintf( file, "<defs>\n" );
116 fprintf( file, "<font id=\"");
117 latin1ToUtf8Out(file, sf->fontname);
118 fprintf(file, "\" horiz-adv-x=\"%d\" ", defwid );
119 if ( sf->hasvmetrics )
120 fprintf( file, "vert-adv-y=\"%d\" ", sf->ascent+sf->descent );
121 putc('>',file); putc('\n',file);
122 fprintf( file, " <font-face \n" );
123 if (sf->familyname_with_timestamp != NULL || sf->familyname != NULL) {
124 fprintf( file, " font-family=\"");
125 latin1ToUtf8Out(file, sf->familyname_with_timestamp ? sf->familyname_with_timestamp : sf->familyname );
126 fprintf( file, "\"\n");
127 } else {
128 LogError(_("An SVG font without a familyname value might not be usable."));
129 }
130 fprintf( file, " font-weight=\"%d\"\n", info.weight );
131 if ( strstrmatch(sf->fontname,"obli") || strstrmatch(sf->fontname,"slanted") )
132 fprintf( file, " font-style=\"oblique\"\n" );
133 else if ( MacStyleCode(sf,NULL)&sf_italic )
134 fprintf( file, " font-style=\"italic\"\n" );
135 if ( strstrmatch(sf->fontname,"small") || strstrmatch(sf->fontname,"cap") )
136 fprintf( file, " font-variant=\"small-caps\"\n" );
137 fprintf( file, " font-stretch=\"%s\"\n", condexp[info.width]);
138 fprintf( file, " units-per-em=\"%d\"\n", sf->ascent+sf->descent );
139 fprintf( file, " panose-1=\"%d %d %d %d %d %d %d %d %d %d\"\n", info.panose[0],
140 info.panose[1], info.panose[2], info.panose[3], info.panose[4], info.panose[5],
141 info.panose[6], info.panose[7], info.panose[8], info.panose[9]);
142 fprintf( file, " ascent=\"%d\"\n", sf->ascent );
143 fprintf( file, " descent=\"%d\"\n", -sf->descent );
144 if ( bd.xheight>0 )
145 fprintf( file, " x-height=\"%g\"\n", (double) bd.xheight );
146 if ( bd.caph>0 )
147 fprintf( file, " cap-height=\"%g\"\n", (double) bd.caph );
148 fprintf( file, " bbox=\"%g %g %g %g\"\n", (double) bb.minx, (double) bb.miny, (double) bb.maxx, (double) bb.maxy );
149 fprintf( file, " underline-thickness=\"%g\"\n", (double) sf->uwidth );
150 fprintf( file, " underline-position=\"%g\"\n", (double) sf->upos );
151 if ( sf->italicangle!=0 )
152 fprintf(file, " slope=\"%g\"\n", (double) sf->italicangle );
153 hash = PSDictHasEntry(sf->private,"StdHW");
154 hasv = PSDictHasEntry(sf->private,"StdVW");
155 if ( hash!=NULL ) {
156 if ( *hash=='[' ) ++hash;
157 ch = hash[strlen(hash)-1];
158 if ( ch==']' ) hash[strlen(hash)-1] = '\0';
159 fprintf(file, " stemh=\"%s\"\n", hash );
160 if ( ch==']' ) hash[strlen(hash)] = ch;
161 }
162 if ( hasv!=NULL ) {
163 if ( *hasv=='[' ) ++hasv;
164 ch = hasv[strlen(hasv)-1];
165 if ( ch==']' ) hasv[strlen(hasv)-1] = '\0';
166 fprintf(file, " stemv=\"%s\"\n", hasv );
167 if ( ch==']' ) hasv[strlen(hasv)] = ch;
168 }
169 minu = 0x7fffff; maxu = 0;
170 for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL && sf->glyphs[i]->unicodeenc>0 ) {
171 if ( sf->glyphs[i]->unicodeenc<minu ) minu = sf->glyphs[i]->unicodeenc;
172 if ( sf->glyphs[i]->unicodeenc>maxu ) maxu = sf->glyphs[i]->unicodeenc;
173 }
174 if ( maxu!=0 )
175 fprintf(file, " unicode-range=\"U+%04X-%04X\"\n", minu, maxu );
176 fprintf( file, " />\n" );
177 return( defwid );
178 }
179
svg_pathdump(FILE * file,SplineSet * spl,int lineout,int forceclosed,int do_clips)180 static int svg_pathdump(FILE *file, SplineSet *spl, int lineout,
181 int forceclosed, int do_clips) {
182 BasePoint last;
183 char buffer[85];
184 int closed=false;
185 Spline *sp, *first;
186 /* as I see it there is nothing to be gained by optimizing out the */
187 /* command characters, since they just have to be replaced by spaces */
188 /* so I don't bother to */
189
190 last.x = last.y = 0;
191 while ( spl!=NULL ) {
192 if ( (do_clips && spl->is_clip_path) || (!do_clips && !spl->is_clip_path)) {
193 sprintf( buffer, "M%g %g", (double) spl->first->me.x, (double) spl->first->me.y );
194 if ( lineout+strlen(buffer)>=255 ) { putc('\n',file); lineout = 0; }
195 fputs( buffer,file );
196 lineout += strlen( buffer );
197 last = spl->first->me;
198 closed = false;
199
200 first = NULL;
201 for ( sp = spl->first->next; sp!=NULL && sp!=first; sp = sp->to->next ) {
202 if ( first==NULL ) first=sp;
203 if ( sp->knownlinear ) {
204 if ( sp->to->me.x==sp->from->me.x )
205 sprintf( buffer,"v%g", (double) (sp->to->me.y-last.y) );
206 else if ( sp->to->me.y==sp->from->me.y )
207 sprintf( buffer,"h%g", (double) (sp->to->me.x-last.x) );
208 else if ( sp->to->next==first ) {
209 strcpy( buffer, "z");
210 closed = true;
211 } else
212 sprintf( buffer,"l%g %g", (double) (sp->to->me.x-last.x), (double) (sp->to->me.y-last.y) );
213 } else if ( sp->order2 ) {
214 if ( sp->from->prev!=NULL && sp->from!=spl->first &&
215 sp->from->me.x-sp->from->prevcp.x == sp->from->nextcp.x-sp->from->me.x &&
216 sp->from->me.y-sp->from->prevcp.y == sp->from->nextcp.y-sp->from->me.y )
217 sprintf( buffer,"t%g %g", (double) (sp->to->me.x-last.x), (double) (sp->to->me.y-last.y) );
218 else
219 sprintf( buffer,"q%g %g %g %g",
220 (double) (sp->to->prevcp.x-last.x), (double) (sp->to->prevcp.y-last.y),
221 (double) (sp->to->me.x-last.x),(double) (sp->to->me.y-last.y));
222 } else {
223 if ( sp->from->prev!=NULL && sp->from!=spl->first &&
224 sp->from->me.x-sp->from->prevcp.x == sp->from->nextcp.x-sp->from->me.x &&
225 sp->from->me.y-sp->from->prevcp.y == sp->from->nextcp.y-sp->from->me.y )
226 sprintf( buffer,"s%g %g %g %g",
227 (double) (sp->to->prevcp.x-last.x), (double) (sp->to->prevcp.y-last.y),
228 (double) (sp->to->me.x-last.x),(double) (sp->to->me.y-last.y));
229 else
230 sprintf( buffer,"c%g %g %g %g %g %g",
231 (double) (sp->from->nextcp.x-last.x), (double) (sp->from->nextcp.y-last.y),
232 (double) (sp->to->prevcp.x-last.x), (double) (sp->to->prevcp.y-last.y),
233 (double) (sp->to->me.x-last.x),(double) (sp->to->me.y-last.y));
234 }
235 if ( lineout+strlen(buffer)>=255 ) { putc('\n',file); lineout = 0; }
236 fputs( buffer,file );
237 lineout += strlen( buffer );
238 last = sp->to->me;
239 }
240 if ( !closed && (forceclosed || spl->first->prev!=NULL) ) {
241 if ( lineout>=254 ) { putc('\n',file); lineout=0; }
242 putc('z',file);
243 ++lineout;
244 }
245 }
246 spl = spl->next;
247 }
248 return( lineout );
249 }
250
svg_dumpstroke(FILE * file,struct pen * cpen,struct pen * fallback,const char * scname,SplineChar * nested,int layer,int istop)251 static void svg_dumpstroke(FILE *file, struct pen *cpen, struct pen *fallback,
252 const char *scname, SplineChar *nested, int layer, int istop) {
253 static const char *joins[] = { "miter", "round", "bevel", "inherit", NULL };
254 static const char *caps[] = { "butt", "round", "square", "inherit", NULL };
255 struct pen pen;
256
257 pen = *cpen;
258 if ( fallback!=NULL ) {
259 if ( pen.brush.col == COLOR_INHERITED ) pen.brush.col = fallback->brush.col;
260 if ( pen.brush.opacity <0 ) pen.brush.opacity = fallback->brush.opacity;
261 if ( pen.width == WIDTH_INHERITED ) pen.width = fallback->width;
262 if ( pen.linecap == lc_inherited ) pen.linecap = fallback->linecap;
263 if ( pen.linejoin == lj_inherited ) pen.linejoin = fallback->linejoin;
264 if ( pen.dashes[0]==0 && pen.dashes[1]==DASH_INHERITED )
265 memcpy(pen.dashes,fallback->dashes,sizeof(pen.dashes));
266 }
267
268 if ( pen.brush.gradient!=NULL ) {
269 fprintf( file, "stroke=\"url(#%s", scname );
270 if ( nested!=NULL )
271 fprintf( file, "-%s", nested->name );
272 fprintf( file, "-ly%d-stroke-grad)\" ", layer );
273 } else if ( pen.brush.pattern!=NULL && istop ) {
274 fprintf( file, "stroke=\"url(#%s", scname );
275 if ( nested!=NULL )
276 fprintf( file, "-%s", nested->name );
277 fprintf( file, "-ly%d-stroke-pattern)\" ", layer );
278 } else {
279 if ( pen.brush.col!=COLOR_INHERITED )
280 fprintf( file, "stroke=\"#%02x%02x%02x\" ",
281 COLOR_RED(pen.brush.col), COLOR_GREEN(pen.brush.col), COLOR_BLUE(pen.brush.col));
282 else
283 fprintf( file, "stroke=\"currentColor\" " );
284 if ( pen.brush.opacity>=0 )
285 fprintf( file, "stroke-opacity=\"%g\" ", (double)pen.brush.opacity);
286 }
287 if ( pen.width!=WIDTH_INHERITED )
288 fprintf( file, "stroke-width=\"%g\" ", (double)pen.width );
289 if ( pen.linecap!=lc_inherited )
290 fprintf( file, "stroke-linecap=\"%s\" ", caps[pen.linecap] );
291 if ( pen.linejoin!=lc_inherited )
292 fprintf( file, "stroke-linejoin=\"%s\" ", joins[pen.linejoin] );
293 /* the current transformation matrix will not affect the fill, but it will */
294 /* affect the way stroke looks. So we must include it here. BUT the spline */
295 /* set has already been transformed, so we must apply the inverse transform */
296 /* to the splineset before outputting it, so that applying the transform */
297 /* will give us the splines we desire. */
298 if ( pen.trans[0]!=1.0 || pen.trans[3]!=1.0 || pen.trans[1]!=0 || pen.trans[2]!=0 )
299 fprintf( file, "transform=\"matrix(%g, %g, %g, %g, 0, 0)\" ",
300 (double) pen.trans[0], (double) pen.trans[1], (double) pen.trans[2], (double) pen.trans[3] );
301 if ( pen.dashes[0]==0 && pen.dashes[1]==DASH_INHERITED ) {
302 fprintf( file, "stroke-dasharray=\"inherit\" " );
303 } else if ( pen.dashes[0]!=0 ) {
304 int i;
305 fprintf( file, "stroke-dasharray=\"" );
306 for ( i=0; i<DASH_MAX && pen.dashes[i]!=0; ++i )
307 fprintf( file, "%d ", pen.dashes[i]);
308 fprintf( file,"\" ");
309 } else {
310 /* That's the default, don't need to say it */
311 /* fprintf( file, "stroke-dasharray=\"none\" " )*/
312 ;
313 }
314 }
315
svg_dumpfill(FILE * file,struct brush * cbrush,struct brush * fallback,int dofill,const char * scname,SplineChar * nested,int layer,int istop)316 static void svg_dumpfill(FILE *file, struct brush *cbrush, struct brush *fallback,
317 int dofill, const char *scname, SplineChar *nested, int layer, int istop ) {
318 struct brush brush;
319
320 if ( !dofill ) {
321 fprintf( file, "fill=\"none\" " );
322 return;
323 }
324
325 brush = *cbrush;
326 if ( fallback!=NULL ) {
327 if ( brush.col==COLOR_INHERITED ) brush.col = fallback->col;
328 if ( brush.opacity<0 ) brush.opacity = fallback->opacity;
329 }
330
331 if ( brush.gradient!=NULL ) {
332 fprintf( file, "fill=\"url(#%s", scname );
333 if ( nested!=NULL )
334 fprintf( file, "-%s", nested->name );
335 fprintf( file, "-ly%d-fill-grad)\" ", layer );
336 } else if ( brush.pattern!=NULL && istop ) {
337 fprintf( file, "fill=\"url(#%s", scname );
338 if ( nested!=NULL )
339 fprintf( file, "-%s", nested->name );
340 fprintf( file, "-ly%d-fill-pattern)\" ", layer );
341 } else {
342 if ( brush.col!=COLOR_INHERITED )
343 fprintf( file, "fill=\"#%02x%02x%02x\" ",
344 COLOR_RED(brush.col), COLOR_GREEN(brush.col), COLOR_BLUE(brush.col));
345 else
346 fprintf( file, "fill=\"currentColor\" " );
347 if ( brush.opacity>=0 )
348 fprintf( file, "fill-opacity=\"%g\" ", (double)brush.opacity);
349 }
350 }
351
TransBy(SplineSet * ss,real trans[4])352 static SplineSet *TransBy(SplineSet *ss, real trans[4] ) {
353 real inversetrans[6], transform[6];
354
355 if ( trans[0]==1.0 && trans[3]==1.0 && trans[1]==0 && trans[2]==0 )
356 return( ss );
357 memcpy(transform,trans,4*sizeof(real));
358 transform[4] = transform[5] = 0;
359 MatInverse(inversetrans,transform);
360 return( SplinePointListTransform(SplinePointListCopy(
361 ss),inversetrans,tpt_AllPoints));
362 }
363
svg_sc_any(SplineChar * sc,int layer)364 static int svg_sc_any(SplineChar *sc,int layer) {
365 int i,j;
366 int any;
367 RefChar *ref;
368 int first, last;
369
370 first = last = layer;
371 if ( sc->parent!=NULL && sc->parent->multilayer )
372 last = sc->layer_cnt-1;
373 any = false;
374 for ( i=first; i<=last && !any; ++i ) {
375 any = sc->layers[i].splines!=NULL || sc->layers[i].images!=NULL;
376 for ( ref=sc->layers[i].refs ; ref!=NULL && !any; ref = ref->next )
377 for ( j=0; j<ref->layer_cnt && !any; ++j )
378 any = ref->layers[j].splines!=NULL;
379 }
380 return( any );
381 }
382
383 static int base64tab[] = {
384 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
385 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
386 '0','1','2','3','4','5','6','7','8','9','+', '/'
387 };
388
DataURI_ImageDump(FILE * file,struct gimage * img)389 static void DataURI_ImageDump(FILE *file,struct gimage *img) {
390 const char *mimetype=NULL;
391 FILE *imgf;
392 int done = false;
393 int threechars[3], fourchars[4], i, ch, ch_on_line;
394 #if !defined( _NO_LIBJPEG)
395 struct _GImage *base = img->list_len==0 ? img->u.image : img->u.images[0];
396 #endif
397
398 /* Technically we can only put a file into an URI if the whole thing is */
399 /* less than 1024 bytes long. But I shall ignore that issue */
400 imgf = GFileTmpfile();
401 #if !defined(_NO_LIBJPEG)
402 if ( base->image_type == it_true ) {
403 done = GImageWrite_Jpeg(img,imgf,78,false);
404 mimetype = "image/jpeg";
405 }
406 #endif
407 #ifndef _NO_LIBPNG
408 if ( !done ) {
409 done = GImageWrite_Png(img,imgf,false);
410 mimetype = "image/png";
411 }
412 #endif
413 if ( !done ) {
414 GImageWrite_Bmp(img,imgf);
415 mimetype = "image/bmp";
416 }
417
418 fprintf( file,"%s;base64,", mimetype );
419 rewind(imgf);
420
421 /* Now do base64 output conversion */
422
423 rewind(imgf);
424 ch = getc(imgf);
425 ch_on_line = 0;
426 while ( ch!=EOF ) {
427 threechars[0] = threechars[1] = threechars[2] = 0;
428 for ( i=0; i<3 && ch!=EOF ; ++i ) {
429 threechars[i] = ch;
430 ch = getc(imgf);
431 }
432 if ( i>0 ) {
433 fourchars[0] = base64tab[threechars[0]>>2];
434 fourchars[1] = base64tab[((threechars[0]&0x3)<<4)|(threechars[1]>>4)];
435 fourchars[2] = base64tab[((threechars[1]&0xf)<<2)|(threechars[2]>>6)];
436 fourchars[3] = base64tab[threechars[2]&0x3f];
437 if ( i<3 )
438 fourchars[3] = '=';
439 if ( i<2 )
440 fourchars[2] = '=';
441 putc(fourchars[0],file);
442 putc(fourchars[1],file);
443 putc(fourchars[2],file);
444 putc(fourchars[3],file);
445 ch_on_line += 4;
446 if ( ch_on_line>=72 ) {
447 putc('\n',file);
448 ch_on_line = 0;
449 }
450 }
451 }
452 fclose(imgf);
453 }
454
svg_dumpgradient(FILE * file,struct gradient * gradient,const char * scname,SplineChar * nested,int layer,int is_fill)455 static void svg_dumpgradient(FILE *file,struct gradient *gradient,
456 const char *scname,SplineChar *nested,int layer,int is_fill) {
457 int i;
458 Color csame; float osame;
459
460 fprintf( file, " <%s ", gradient->radius==0 ? "linearGradient" : "radialGradient" );
461 if ( nested==NULL )
462 fprintf( file, " id=\"%s-ly%d-%s-grad\"", scname, layer, is_fill ? "fill" : "stroke" );
463 else
464 fprintf( file, " id=\"%s-%s-ly%d-%s-grad\"", scname, nested->name, layer, is_fill ? "fill" : "stroke" );
465 fprintf(file, "\n\tgradientUnits=\"userSpaceOnUse\"" );
466 if ( gradient->radius==0 ) {
467 fprintf( file, "\n\tx1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"",
468 (double) gradient->start.x, (double) gradient->start.y,
469 (double) gradient->stop.x, (double) gradient->stop.y );
470 } else {
471 if ( gradient->start.x==gradient->stop.x && gradient->start.y==gradient->stop.y )
472 fprintf( file, "\n\tcx=\"%g\" cy=\"%g\" r=\"%g\"",
473 (double) gradient->stop.x, (double) gradient->stop.y,
474 (double) gradient->radius );
475 else
476 fprintf( file, "\n\tfx=\"%g\" fy=\"%g\" cx=\"%g\" cy=\"%g\" r=\"%g\"",
477 (double) gradient->start.x, (double) gradient->start.y,
478 (double) gradient->stop.x, (double) gradient->stop.y,
479 (double) gradient->radius );
480 }
481 fprintf(file, "\n\tspreadMethod=\"%s\">\n",
482 gradient->sm == sm_pad ? "pad" :
483 gradient->sm == sm_reflect ? "reflect" :
484 "repeat" );
485
486 csame = (Color)-1; osame = -1;
487 for ( i=0; i<gradient->stop_cnt; ++i ) {
488 if ( csame==(Color)-1 )
489 csame = gradient->grad_stops[i].col;
490 else if ( csame!=gradient->grad_stops[i].col )
491 csame = (Color)-2;
492 if ( osame== -1 )
493 osame = gradient->grad_stops[i].opacity;
494 else if ( (double)osame!=gradient->grad_stops[i].opacity )
495 osame = -2;
496 }
497 for ( i=0; i<gradient->stop_cnt; ++i ) {
498 fprintf( file, " <stop offset=\"%g\"", (double) gradient->grad_stops[i].offset );
499 if ( csame==(Color)-2 ) {
500 if ( gradient->grad_stops[i].col==COLOR_INHERITED )
501 fprintf( file, " stop-color=\"inherit\"" );
502 else
503 fprintf( file, " stop-color=\"#%06x\"", gradient->grad_stops[i].col );
504 }
505 if ( osame<0 ) {
506 if ( gradient->grad_stops[i].opacity==COLOR_INHERITED )
507 fprintf( file, " stop-opacity=\"inherit\"" );
508 else
509 fprintf( file, " stop-opacity=\"%g\"", (double) gradient->grad_stops[i].opacity );
510 }
511 fprintf( file, "/>\n" );
512 }
513 fprintf( file, " </%s>\n", gradient->radius==0 ? "linearGradient" : "radialGradient" );
514 }
515
516 static void svg_dumpscdefs(FILE *file,SplineChar *sc,const char *name,int istop);
517 static void svg_dumptype3(FILE *file,SplineChar *sc,const char *name,int istop);
518
svg_dumppattern(FILE * file,struct pattern * pattern,const char * scname,SplineChar * base,SplineChar * nested,int layer,int is_fill)519 static void svg_dumppattern(FILE *file,struct pattern *pattern,
520 const char *scname, SplineChar *base,SplineChar *nested,int layer,int is_fill) {
521 SplineChar *pattern_sc = SFGetChar(base->parent,-1,pattern->pattern);
522 char *patsubname = NULL;
523
524 if ( pattern_sc!=NULL ) {
525 patsubname = strconcat3(scname,"-",pattern->pattern);
526 svg_dumpscdefs(file,pattern_sc,patsubname,false);
527 } else
528 LogError(_("No glyph named %s, used as a pattern in %s\n"), pattern->pattern, scname);
529
530 fprintf( file, " <pattern " );
531 if ( nested==NULL )
532 fprintf( file, " id=\"%s-ly%d-%s-pattern\"", scname, layer, is_fill ? "fill" : "stroke" );
533 else
534 fprintf( file, " id=\"%s-%s-ly%d-%s-pattern\"", scname, nested->name, layer, is_fill ? "fill" : "stroke" );
535 fprintf(file, "\n\tpatternUnits=\"userSpaceOnUse\"" );
536 if ( pattern_sc!=NULL ) {
537 DBounds b;
538 PatternSCBounds(pattern_sc,&b);
539 fprintf( file, "\n\tviewBox=\"%g %g %g %g\"",
540 (double) b.minx, (double) b.miny,
541 (double) (b.maxx-b.minx), (double) (b.maxy-b.miny) );
542 }
543 fprintf( file, "\n\twidth=\"%g\" height=\"%g\"",
544 (double) pattern->width, (double) pattern->height );
545 if ( pattern->transform[0]!=1 || pattern->transform[1]!=0 ||
546 pattern->transform[2]!=0 || pattern->transform[3]!=1 ||
547 pattern->transform[4]!=0 || pattern->transform[5]!=0 ) {
548 fprintf( file, "\n\tpatternTransform=\"matrix(%g %g %g %g %g %g)\"",
549 (double) pattern->transform[0], (double) pattern->transform[1],
550 (double) pattern->transform[2], (double) pattern->transform[3],
551 (double) pattern->transform[4], (double) pattern->transform[5] );
552 }
553 if ( pattern_sc!=NULL )
554 svg_dumpscdefs(file,pattern_sc,patsubname,false);
555 fprintf( file, " </pattern>\n" );
556 free(patsubname);
557 }
558
svg_layer_defs(FILE * file,SplineSet * splines,struct brush * fill_brush,struct pen * stroke_pen,SplineChar * sc,const char * scname,SplineChar * nested,int layer,int istop)559 static void svg_layer_defs(FILE *file, SplineSet *splines,struct brush *fill_brush,struct pen *stroke_pen,
560 SplineChar *sc, const char *scname, SplineChar *nested, int layer, int istop ) {
561 if ( SSHasClip(splines)) {
562 if ( nested==NULL )
563 fprintf( file, " <clipPath id=\"%s-ly%d-clip\">\n", scname, layer );
564 else
565 fprintf( file, " <clipPath id=\"%s-%s-ly%d-clip\">\n", scname, nested->name, layer );
566 fprintf(file, " <path d=\"\n");
567 svg_pathdump(file,sc->layers[layer].splines,16,true,true);
568 fprintf(file, "\"/>\n" );
569 fprintf( file, " </clipPath>\n" );
570 }
571 if ( fill_brush->gradient!=NULL )
572 svg_dumpgradient(file,fill_brush->gradient,scname,nested,layer,true);
573 else if ( fill_brush->pattern!=NULL && istop )
574 svg_dumppattern(file,fill_brush->pattern,scname,sc,nested,layer,true);
575 if ( stroke_pen->brush.gradient!=NULL )
576 svg_dumpgradient(file,stroke_pen->brush.gradient,scname,nested,layer,false);
577 else if ( stroke_pen->brush.pattern!=NULL && istop )
578 svg_dumppattern(file,stroke_pen->brush.pattern,scname,sc,nested,layer,false);
579 }
580
svg_dumpscdefs(FILE * file,SplineChar * sc,const char * name,int istop)581 static void svg_dumpscdefs(FILE *file,SplineChar *sc,const char *name,int istop) {
582 int i, j;
583 RefChar *ref;
584
585 for ( i=ly_fore; i<sc->layer_cnt ; ++i ) {
586 svg_layer_defs(file,sc->layers[i].splines,&sc->layers[i].fill_brush,&sc->layers[i].stroke_pen,
587 sc,name,NULL,i,istop);
588 for ( ref=sc->layers[i].refs ; ref!=NULL; ref = ref->next ) {
589 for ( j=0; j<ref->layer_cnt; ++j ) if ( ref->layers[j].splines!=NULL ) {
590 svg_layer_defs(file,ref->layers[j].splines,&ref->layers[j].fill_brush,&ref->layers[j].stroke_pen,
591 sc,name,ref->sc,j,istop);
592 }
593 }
594 }
595 }
596
svg_dumptype3(FILE * file,SplineChar * sc,const char * name,int istop)597 static void svg_dumptype3(FILE *file,SplineChar *sc,const char *name,int istop) {
598 int i, j;
599 RefChar *ref;
600 ImageList *images;
601 SplineSet *transed;
602
603 for ( i=ly_fore; i<sc->layer_cnt ; ++i ) {
604 if ( SSHasDrawn(sc->layers[i].splines) ) {
605 fprintf(file, " <g " );
606 if ( SSHasClip(sc->layers[i].splines))
607 fprintf( file, "clip-path=\"url(#%s-ly%d-clip)\" ", name, i );
608 transed = sc->layers[i].splines;
609 if ( sc->layers[i].dostroke ) {
610 svg_dumpstroke(file,&sc->layers[i].stroke_pen,NULL,name,NULL,i,istop);
611 transed = TransBy(transed,sc->layers[i].stroke_pen.trans);
612 }
613 svg_dumpfill(file,&sc->layers[i].fill_brush,NULL,sc->layers[i].dofill,name,NULL,i,istop);
614 fprintf( file, ">\n" );
615 fprintf(file, " <path d=\"\n");
616 svg_pathdump(file,transed,12,!sc->layers[i].dostroke,false);
617 fprintf(file, "\"/>\n" );
618 if ( transed!=sc->layers[i].splines )
619 SplinePointListsFree(transed);
620 fprintf(file, " </g>\n" );
621 }
622 for ( ref=sc->layers[i].refs ; ref!=NULL; ref = ref->next ) {
623 for ( j=0; j<ref->layer_cnt; ++j ) if ( ref->layers[j].splines!=NULL ) {
624 fprintf(file, " <g " );
625 transed = ref->layers[j].splines;
626 if ( SSHasClip(transed))
627 fprintf( file, "clip-path=\"url(#%s-%s-ly%d-clip)\" ", name, ref->sc->name, j );
628 if ( ref->layers[j].dostroke ) {
629 svg_dumpstroke(file,&ref->layers[j].stroke_pen,&sc->layers[i].stroke_pen,sc->name,ref->sc,j,istop);
630 transed = TransBy(transed,ref->layers[j].stroke_pen.trans);
631 }
632 svg_dumpfill(file,&ref->layers[j].fill_brush,&sc->layers[i].fill_brush,ref->layers[j].dofill,sc->name,ref->sc,j,istop);
633 fprintf( file, ">\n" );
634 fprintf(file, " <path d=\"\n");
635 svg_pathdump(file,transed,12,!ref->layers[j].dostroke,false);
636 fprintf(file, "\"/>\n" );
637 if ( transed!=ref->layers[j].splines )
638 SplinePointListsFree(transed);
639 fprintf(file, " </g>\n" );
640 }
641 }
642 for ( images=sc->layers[i].images ; images!=NULL; images = images->next ) {
643 struct _GImage *base;
644 fprintf(file, " <image\n" );
645 base = images->image->list_len==0 ? images->image->u.image :
646 images->image->u.images[0];
647 fprintf(file, "\twidth=\"%g\"\n\theight=\"%g\"\n",
648 (double) (base->width*images->xscale), (double) (base->height*images->yscale) );
649 fprintf(file, "\tx=\"%g\"\n\ty=\"%g\"\n", (double) images->xoff, (double) images->yoff );
650 fprintf(file, "\txlink:href=\"data:" );
651 DataURI_ImageDump(file,images->image);
652 fprintf(file, "\" />\n" );
653 }
654 }
655 }
656
svg_scpathdump(FILE * file,SplineChar * sc,const char * endpath,int layer)657 static void svg_scpathdump(FILE *file, SplineChar *sc,const char *endpath,int layer) {
658 RefChar *ref;
659 int lineout;
660 int i,j;
661 int needs_defs=0;
662
663 if ( !svg_sc_any(sc,layer) ) {
664 /* I think a space is represented by leaving out the d (path) entirely*/
665 /* rather than having d="" */
666 fputs(" />\n",file);
667 } else if ( sc->parent!=NULL && sc->parent->strokedfont ) {
668 /* Can't be done with a path, requires nested elements (I think) */
669 fprintf(file,">\n <g stroke=\"currentColor\" stroke-width=\"%g\" fill=\"none\">\n", (double) sc->parent->strokewidth );
670 fprintf( file," <path d=\"");
671 lineout = svg_pathdump(file,sc->layers[layer].splines,3,false,false);
672 for ( ref= sc->layers[layer].refs; ref!=NULL; ref=ref->next )
673 lineout = svg_pathdump(file,ref->layers[0].splines,lineout,false,false);
674 if ( lineout>=255-4 ) putc('\n',file );
675 putc('"',file);
676 fputs(" />\n </g>\n",file);
677 fputs(endpath,file);
678 } else if ( sc->parent==NULL || !sc->parent->multilayer ) {
679 fprintf( file,"d=\"");
680 lineout = svg_pathdump(file,sc->layers[layer].splines,3,true,false);
681 for ( ref= sc->layers[layer].refs; ref!=NULL; ref=ref->next )
682 lineout = svg_pathdump(file,ref->layers[0].splines,lineout,true,false);
683 if ( lineout>=255-4 ) putc('\n',file );
684 putc('"',file);
685 fputs(" />\n",file);
686 } else {
687 fputs(">\n",file);
688 for ( i=ly_fore; i<sc->layer_cnt && !needs_defs ; ++i ) {
689 if ( SSHasClip(sc->layers[i].splines))
690 needs_defs = true;
691 else if ( sc->layers[i].fill_brush.pattern!=NULL ||
692 sc->layers[i].fill_brush.gradient!=NULL ||
693 sc->layers[i].stroke_pen.brush.pattern!=NULL ||
694 sc->layers[i].stroke_pen.brush.gradient!=NULL )
695 needs_defs = true;
696 for ( ref=sc->layers[i].refs ; ref!=NULL; ref = ref->next ) {
697 for ( j=0; j<ref->layer_cnt; ++j ) if ( ref->layers[j].splines!=NULL ) {
698 if ( SSHasClip(ref->layers[j].splines))
699 needs_defs = true;
700 else if ( ref->layers[j].fill_brush.pattern!=NULL ||
701 ref->layers[j].fill_brush.gradient!=NULL ||
702 ref->layers[j].stroke_pen.brush.pattern!=NULL ||
703 ref->layers[j].stroke_pen.brush.gradient!=NULL )
704 needs_defs = true;
705 }
706 }
707 }
708 if ( needs_defs ) {
709 fprintf(file, " <defs>\n" );
710 svg_dumpscdefs(file,sc,sc->name,true);
711 fprintf(file, " </defs>\n" );
712 }
713 svg_dumptype3(file,sc,sc->name,true);
714 fputs(endpath,file);
715 }
716 }
717
LigCnt(SplineFont * sf,PST * lig,int32 * univals,int max)718 static int LigCnt(SplineFont *sf,PST *lig,int32 *univals,int max) {
719 char *pt, *end;
720 int c=0;
721 SplineChar *sc;
722
723 if ( lig->type!=pst_ligature )
724 return( 0 );
725 else if ( !lig->subtable->lookup->store_in_afm )
726 return( 0 );
727 pt = lig->u.lig.components;
728 for (;;) {
729 end = strchr(pt,' ');
730 if ( end!=NULL ) *end='\0';
731 sc = SFGetChar(sf,-1,pt);
732 if ( end!=NULL ) *end=' ';
733 if ( sc==NULL || sc->unicodeenc==-1 )
734 return( 0 );
735 if ( c>=max )
736 return( 0 );
737 univals[c++] = sc->unicodeenc;
738 if ( end==NULL )
739 return( c );
740 pt = end+1;
741 while ( *pt==' ' ) ++pt;
742 }
743 }
744
HasLigature(SplineChar * sc)745 static PST *HasLigature(SplineChar *sc) {
746 PST *pst, *best=NULL;
747 int bestc=0,c;
748 int32 univals[50];
749
750 for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
751 if ( pst->type==pst_ligature ) {
752 c = LigCnt(sc->parent,pst,univals,sizeof(univals)/sizeof(univals[0]));
753 if ( c>1 && c>bestc ) {
754 c = bestc;
755 best = pst;
756 }
757 }
758 }
759 return( best );
760 }
761
SCHasSubs(SplineChar * sc,uint32 tag)762 SplineChar *SCHasSubs(SplineChar *sc, uint32 tag) {
763 PST *pst;
764
765 for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
766 if ( pst->type==pst_substitution &&
767 FeatureTagInFeatureScriptList(tag,pst->subtable->lookup->features) )
768 return( SFGetChar(sc->parent,-1,pst->u.subs.variant));
769 }
770 return( NULL );
771 }
772
svg_scdump(FILE * file,SplineChar * sc,int defwid,int encuni,int vs,int layer)773 static void svg_scdump(FILE *file, SplineChar *sc,int defwid, int encuni, int vs,int layer) {
774 PST *best=NULL;
775 const unichar_t *alt;
776 int32 univals[50];
777 int i, c;
778
779 best = HasLigature(sc);
780 if ( sc->comment!=NULL ) {
781 fprintf( file, "\n<!--\n%s\n-->\n",sc->comment );
782 }
783 fprintf(file," <glyph glyph-name=\"%s\" ",sc->name );
784 if ( best!=NULL ) {
785 c = LigCnt(sc->parent,best,univals,sizeof(univals)/sizeof(univals[0]));
786 fputs("unicode=\"",file);
787 for ( i=0; i<c; ++i )
788 if ( univals[i]>='A' && univals[i]<='z' )
789 putc(univals[i],file);
790 else
791 fprintf(file,"&#x%x;", (unsigned int) univals[i]);
792 fputs("\" ",file);
793 } else if ( encuni!=-1 && encuni<0x110000 ) {
794 if ( encuni!=0x9 &&
795 encuni!=0xa &&
796 encuni!=0xd &&
797 !(encuni>=0x20 && encuni<=0xd7ff) &&
798 !(encuni>=0xe000 && encuni<=0xfffd) &&
799 !(encuni>=0x10000 && encuni<=0x10ffff) )
800 /* Not allowed in XML */;
801 else if ( (encuni>=0x7f && encuni<=0x84) ||
802 (encuni>=0x86 && encuni<=0x9f) ||
803 (encuni>=0xfdd0 && encuni<=0xfddf) ||
804 (encuni&0xffff)==0xfffe ||
805 (encuni&0xffff)==0xffff )
806 /* Not recommended in XML */;
807 else if ( encuni>=32 && encuni<127 &&
808 encuni!='"' && encuni!='&' &&
809 encuni!='<' && encuni!='>' )
810 fprintf( file, "unicode=\"%c\" ", encuni);
811 else if ( encuni<0x10000 &&
812 ( isarabisolated(encuni) || isarabinitial(encuni) || isarabmedial(encuni) || isarabfinal(encuni) ) &&
813 unicode_alternates[encuni>>8]!=NULL &&
814 (alt = unicode_alternates[encuni>>8][encuni&0xff])!=NULL &&
815 alt[1]=='\0' )
816 /* For arabic forms use the base representation in the 0600 block */
817 fprintf( file, "unicode=\"&#x%x;\" ", alt[0]);
818 else if ( vs!=-1 )
819 fprintf( file, "unicode=\"&#x%x;\" ", vs);
820 else
821 fprintf( file, "unicode=\"&#x%x;\" ", encuni);
822 }
823 if ( sc->width!=defwid )
824 fprintf( file, "horiz-adv-x=\"%d\" ", sc->width );
825 if ( sc->parent->hasvmetrics && sc->vwidth!=sc->parent->ascent+sc->parent->descent )
826 fprintf( file, "vert-adv-y=\"%d\" ", sc->vwidth );
827 if ( strstr(sc->name,".vert")!=NULL || strstr(sc->name,".vrt2")!=NULL )
828 fprintf( file, "orientation=\"v\" " );
829 if ( encuni!=-1 && encuni<0x10000 ) {
830 if ( isarabinitial(encuni))
831 fprintf( file,"arabic-form=\"initial\" " );
832 else if ( isarabmedial(encuni))
833 fprintf( file,"arabic-form=\"medial\" ");
834 else if ( isarabfinal(encuni))
835 fprintf( file,"arabic-form=\"final\" ");
836 else if ( isarabisolated(encuni))
837 fprintf( file,"arabic-form=\"isolated\" ");
838 }
839 putc('\n',file);
840 svg_scpathdump(file,sc," </glyph>\n",layer);
841 sc->ticked = true;
842 }
843
svg_notdefdump(FILE * file,SplineFont * sf,int defwid,int layer)844 static void svg_notdefdump(FILE *file, SplineFont *sf,int defwid,int layer) {
845 int notdefpos;
846
847 notdefpos = SFFindNotdef(sf,-2);
848
849 if ( notdefpos!=-1 ) {
850 SplineChar *sc = sf->glyphs[notdefpos];
851
852 fprintf(file, "<missing-glyph ");
853 if ( sc->width!=defwid )
854 fprintf( file, "horiz-adv-x=\"%d\" ", sc->width );
855 if ( sc->parent->hasvmetrics && sc->vwidth!=sc->parent->ascent+sc->parent->descent )
856 fprintf( file, "vert-adv-y=\"%d\" ", sc->vwidth );
857 putc('\n',file);
858 svg_scpathdump(file,sc," </glyph>\n",layer);
859 } else {
860 /* We'll let both the horiz and vert advances default to the values */
861 /* specified by the font, and I think a space is done by omitting */
862 /* d (the path) altogether */
863 fprintf(file," <missing-glyph />\n"); /* Is this a blank space? */
864 }
865 }
866
fputkerns(FILE * file,char * names)867 static void fputkerns( FILE *file, char *names) {
868 while ( *names ) {
869 if ( *names==' ' ) {
870 putc(',',file);
871 while ( names[1]==' ' ) ++names;
872 } else
873 putc(*names,file);
874 ++names;
875 }
876 }
877
svg_dumpkerns(FILE * file,SplineFont * sf,int isv)878 static void svg_dumpkerns(FILE *file,SplineFont *sf,int isv) {
879 int i,j;
880 KernPair *kp;
881 KernClass *kc;
882
883 for ( i=0; i<sf->glyphcnt; ++i ) if ( SCWorthOutputting(sf->glyphs[i]) ) {
884 for ( kp = isv ? sf->glyphs[i]->vkerns : sf->glyphs[i]->kerns;
885 kp!=NULL; kp = kp->next )
886 if ( kp->off!=0 && SCWorthOutputting(kp->sc)) {
887 fprintf( file, isv ? " <vkern " : " <hkern " );
888 if ( sf->glyphs[i]->unicodeenc==-1 || HasLigature(sf->glyphs[i]))
889 fprintf( file, "g1=\"%s\" ", sf->glyphs[i]->name );
890 else if ( sf->glyphs[i]->unicodeenc>='A' && sf->glyphs[i]->unicodeenc<='z' )
891 fprintf( file, "u1=\"%c\" ", sf->glyphs[i]->unicodeenc );
892 else
893 fprintf( file, "u1=\"&#x%x;\" ", sf->glyphs[i]->unicodeenc );
894 if ( kp->sc->unicodeenc==-1 || HasLigature(kp->sc))
895 fprintf( file, "g2=\"%s\" ", kp->sc->name );
896 else if ( kp->sc->unicodeenc>='A' && kp->sc->unicodeenc<='z' )
897 fprintf( file, "u2=\"%c\" ", kp->sc->unicodeenc );
898 else
899 fprintf( file, "u2=\"&#x%x;\" ", kp->sc->unicodeenc );
900 fprintf( file, "k=\"%d\" />\n", -kp->off );
901 }
902 }
903
904 for ( kc=isv ? sf->vkerns : sf->kerns; kc!=NULL; kc=kc->next ) {
905 for ( i=0; i<kc->first_cnt; ++i ) {
906 if ( kc->firsts[i] && *kc->firsts[i]!='\0' ) {
907 for ( j=0; j<kc->second_cnt; ++j ) {
908 if ( kc->seconds[j] && *kc->seconds[j]!='\0' &&
909 kc->offsets[i*kc->second_cnt+j]!=0 ) {
910 fprintf( file, isv ? " <vkern g1=\"" : " <hkern g1=\"" );
911 fputkerns( file, kc->firsts[i]);
912 fprintf( file, "\"\n\tg2=\"" );
913 fputkerns( file, kc->seconds[j]);
914 fprintf( file, "\"\n\tk=\"%d\" />\n",
915 -kc->offsets[i*kc->second_cnt+j]);
916 }
917 }
918 }
919 }
920 }
921 }
922
svg_outfonttrailer(FILE * file)923 static void svg_outfonttrailer(FILE *file) {
924 fprintf(file," </font>\n");
925 fprintf(file,"</defs></svg>\n" );
926 }
927
AnyArabicForm(SplineChar * sc)928 static int AnyArabicForm( SplineChar *sc ) {
929 struct altuni *altuni;
930
931 if ( sc->unicodeenc!=-1 && sc->unicodeenc<0x10000 &&
932 (isarabinitial(sc->unicodeenc) ||
933 isarabmedial(sc->unicodeenc) ||
934 isarabfinal(sc->unicodeenc) ||
935 isarabisolated(sc->unicodeenc)))
936 return( sc->unicodeenc );
937 for ( altuni = sc->altuni; altuni!=NULL; altuni = altuni->next )
938 if ( altuni->unienc!=-1 && altuni->unienc<0x10000 &&
939 altuni->vs==-1 && altuni->fid==0 &&
940 (isarabinitial(altuni->unienc) ||
941 isarabmedial(altuni->unienc) ||
942 isarabfinal(altuni->unienc) ||
943 isarabisolated(altuni->unienc)))
944 return( altuni->unienc );
945
946 return( -1 );
947 }
948
UnformedUni(int uni)949 static int UnformedUni(int uni) {
950 return( uni==-1 || uni>=0x10000 ||
951 !(isarabinitial(uni) ||
952 isarabmedial(uni) ||
953 isarabfinal(uni) ||
954 isarabisolated(uni)));
955 }
956
svg_sfdump(FILE * file,SplineFont * sf,int layer)957 static void svg_sfdump(FILE *file,SplineFont *sf,int layer) {
958 int defwid, i, formeduni;
959 struct altuni *altuni;
960
961 locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
962 switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
963
964 for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
965 sf->glyphs[i]->ticked = false;
966
967 defwid = svg_outfontheader(file,sf,layer);
968 svg_notdefdump(file,sf,defwid,layer);
969
970 /* Ligatures must be output before their initial components */
971 for ( i=0; i<sf->glyphcnt; ++i ) {
972 if ( SCWorthOutputting(sf->glyphs[i]) ) {
973 if ( HasLigature(sf->glyphs[i]))
974 svg_scdump(file, sf->glyphs[i],defwid,sf->glyphs[i]->unicodeenc,-1,layer);
975 /* Variation selectors should probably be treated as ligatures */
976 for ( altuni = sf->glyphs[i]->altuni; altuni!=NULL; altuni = altuni->next )
977 if ( altuni->vs!=-1 && altuni->fid==0 )
978 svg_scdump(file, sf->glyphs[i],defwid,altuni->unienc,altuni->vs,layer);
979 }
980 }
981 /* And formed arabic before unformed */
982 for ( i=0; i<sf->glyphcnt; ++i ) {
983 SplineChar *sc = sf->glyphs[i];
984 if ( SCWorthOutputting(sc) && !sc->ticked ) {
985 if ( (formeduni = AnyArabicForm(sc))!=-1 )
986 svg_scdump(file, sc,defwid,formeduni,-1,layer);
987 else if ( sc->unicodeenc>=0x0600 && sc->unicodeenc<=0x06ff ) {
988 /* The conventions now (as I understand them) suggest that */
989 /* fonts not use the unicode encodings for formed arabic */
990 /* but should use simple substitutions instead */
991 int arab_off = sc->unicodeenc-0x600;
992 SplineChar *formed;
993 formed = SCHasSubs(sc,CHR('i','n','i','t'));
994 if ( SCWorthOutputting(formed) && formed->unicodeenc==-1 &&
995 !formed->ticked && ArabicForms[arab_off].initial!=0 )
996 svg_scdump(file,formed,defwid,ArabicForms[arab_off].initial,-1,layer);
997 formed = SCHasSubs(sc,CHR('m','e','d','i'));
998 if ( SCWorthOutputting(formed) && formed->unicodeenc==-1 &&
999 !formed->ticked && ArabicForms[arab_off].medial!=0 )
1000 svg_scdump(file,formed,defwid,ArabicForms[arab_off].medial,-1,layer);
1001 formed = SCHasSubs(sc,CHR('f','i','n','a'));
1002 if ( SCWorthOutputting(formed) && formed->unicodeenc==-1 &&
1003 !formed->ticked && ArabicForms[arab_off].final!=0 )
1004 svg_scdump(file,formed,defwid,ArabicForms[arab_off].final,-1,layer);
1005 formed = SCHasSubs(sc,CHR('i','s','o','l'));
1006 if ( SCWorthOutputting(formed) && formed->unicodeenc==-1 &&
1007 !formed->ticked && ArabicForms[arab_off].isolated!=0 )
1008 svg_scdump(file,formed,defwid,ArabicForms[arab_off].isolated,-1,layer);
1009 }
1010 }
1011 }
1012 for ( i=0; i<sf->glyphcnt; ++i ) {
1013 if ( SCWorthOutputting(sf->glyphs[i]) && !sf->glyphs[i]->ticked ) {
1014 if ( UnformedUni(sf->glyphs[i]->unicodeenc) )
1015 svg_scdump(file, sf->glyphs[i],defwid,sf->glyphs[i]->unicodeenc,-1,layer);
1016 for ( altuni = sf->glyphs[i]->altuni; altuni!=NULL; altuni = altuni->next )
1017 if ( altuni->vs==-1 && altuni->fid==0 )
1018 svg_scdump(file, sf->glyphs[i],defwid,altuni->unienc,altuni->vs,layer);
1019 }
1020 }
1021 svg_dumpkerns(file,sf,false);
1022 svg_dumpkerns(file,sf,true);
1023 svg_outfonttrailer(file);
1024 switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
1025 }
1026
_WriteSVGFont(FILE * file,SplineFont * sf,int flags,EncMap * map,int layer)1027 int _WriteSVGFont(FILE *file,SplineFont *sf,int flags,
1028 EncMap *map,int layer) {
1029 int ret;
1030
1031 svg_sfdump(file,sf,layer);
1032 ret = true;
1033 if ( ferror(file))
1034 ret = false;
1035 return( ret );
1036 }
1037
WriteSVGFont(const char * fontname,SplineFont * sf,enum fontformat format,int flags,EncMap * map,int layer)1038 int WriteSVGFont(const char *fontname,SplineFont *sf,enum fontformat format,int flags,
1039 EncMap *map,int layer) {
1040 FILE *file;
1041 int ret;
1042
1043 if (( file=fopen(fontname,"w+"))==NULL )
1044 return( 0 );
1045 svg_sfdump(file,sf,layer);
1046 ret = true;
1047 if ( ferror(file))
1048 ret = false;
1049 if ( fclose(file)==-1 )
1050 return( 0 );
1051 return( ret );
1052 }
1053
1054 #define SVGMINLRPAD 10
1055
_ExportSVG(FILE * svg,SplineChar * sc,int layer,ExportParams * ep)1056 int _ExportSVG(FILE *svg,SplineChar *sc,int layer,ExportParams *ep) {
1057 char *end;
1058 int em_size, xstart, xend, ascent;
1059 real trans[6] = { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 };
1060 DBounds b;
1061 SplineChar *scc;
1062 SplineSet *orig;
1063
1064 SplineCharLayerFindBounds(sc,layer,&b);
1065 if ( sc->parent!=NULL ) {
1066 ascent = sc->parent->ascent;
1067 em_size = ascent+sc->parent->descent;
1068 if ( b.minx>0 ) b.minx=0;
1069 if ( b.miny>-sc->parent->descent ) b.miny = -sc->parent->descent;
1070 if ( b.maxy<em_size ) b.maxy = em_size;
1071 } else {
1072 ascent = b.maxy;
1073 em_size = b.maxy-b.miny;
1074 }
1075
1076 locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
1077 switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
1078 fprintf(svg, "<?xml version=\"1.0\" standalone=\"no\"?>\n" );
1079 fprintf(svg, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\" >\n" );
1080 // Adjust horizontal ViewBox to display entire glyph
1081 xstart = floor(b.minx);
1082 if (xstart > SVGMINLRPAD)
1083 xstart = 0; // Start from origin when sufficiently past it
1084 else
1085 xstart -= SVGMINLRPAD; // Give glyphs starting near or before the origin some extra space
1086 xend = ceil(b.maxx);
1087 if (xend < ceil(sc->width) - SVGMINLRPAD)
1088 xend = ceil(sc->width); // End at the advance width when sufficiently short of it
1089 else
1090 xend += SVGMINLRPAD; // Give glyphs ending near or past the advance width some extra space
1091 fprintf(svg, "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" viewBox=\"%d 0 %d %d\">\n",
1092 xstart, xend - xstart, (int) ceil(em_size));
1093 if ( ep->use_transform ) {
1094 fprintf(svg, " <g transform=\"matrix(1 0 0 -1 0 %d)\">\n", ascent );
1095 scc = sc;
1096 } else {
1097 trans[3] = -1;
1098 trans[5] = ascent;
1099 if (sc->parent!=NULL) {
1100 scc = SplineCharCopy(sc, sc->parent, NULL);
1101 FVTrans(scc->parent->fv,scc,trans,NULL,
1102 fvt_nopreserve|fvt_dontmovewidth|fvt_noupdate);
1103 } else {
1104 // This is probably an isolated layer, so just transform the splines
1105 // there and back
1106 orig = sc->layers[layer].splines;
1107 scc = sc;
1108 sc->layers[layer].splines = SplinePointListTransformExtended(
1109 SplinePointListCopy(scc->layers[layer].splines),
1110 trans, tpt_AllPoints, tpmask_dontTrimValues);
1111 }
1112 }
1113 if ( scc->parent!=NULL
1114 && ( scc->parent->multilayer
1115 || scc->parent->strokedfont
1116 || !svg_sc_any(scc,layer)) ) {
1117 fprintf(svg, " <g ");
1118 end = " </g>\n";
1119 } else {
1120 fprintf(svg, " <path fill=\"currentColor\"\n");
1121 end = " </path>\n";
1122 }
1123 svg_scpathdump(svg,scc,end,layer);
1124 if ( ep->use_transform )
1125 fprintf(svg, " </g>\n\n" );
1126 else {
1127 if ( sc->parent )
1128 SplineCharFree(scc);
1129 else {
1130 SplinePointListFree(sc->layers[layer].splines);
1131 sc->layers[layer].splines = orig;
1132 }
1133 }
1134 fprintf(svg, "</svg>\n" );
1135
1136 switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
1137 return( !ferror(svg));
1138 }
1139
1140 /* ************************************************************************** */
1141 /* ***************************** SVG Input **************************** */
1142 /* ************************************************************************** */
1143
1144 #ifndef HAVE_ICONV_H
1145 # undef iconv
1146 # undef iconv_t
1147 # undef iconv_open
1148 # undef iconv_close
1149 #endif
1150 #undef extended /* used in xlink.h */
1151 #include <libxml/parser.h>
1152
1153 /* Find a node with the given id */
XmlFindID(xmlNodePtr xml,char * name)1154 static xmlNodePtr XmlFindID(xmlNodePtr xml, char *name) {
1155 xmlChar *id;
1156 xmlNodePtr child, ret;
1157
1158 id = xmlGetProp(xml,(xmlChar *) "id");
1159 if ( id!=NULL && xmlStrcmp(id,(xmlChar *) name)==0 ) {
1160 xmlFree(id);
1161 return( xml );
1162 }
1163 if ( id!=NULL )
1164 xmlFree(id);
1165
1166 for ( child = xml->children; child!=NULL; child=child->next ) {
1167 ret = XmlFindID(child,name);
1168 if ( ret!=NULL )
1169 return( ret );
1170 }
1171 return( NULL );
1172 }
1173
XmlFindURI(xmlNodePtr xml,char * name)1174 static xmlNodePtr XmlFindURI(xmlNodePtr xml, char *name) {
1175 xmlNodePtr ret;
1176 char *pt, ch;
1177
1178 if ( strncmp(name,"url(#",5)!=0 )
1179 return( NULL );
1180 name += 5;
1181 for ( pt=name; *pt!=')' && *pt!='\0'; ++pt );
1182 ch = *pt; *pt = '\0';
1183 ret = XmlFindID(xml,name);
1184 *pt = ch;
1185 return( ret );
1186 }
1187
1188 /* We want to look for "font" nodes within "svg" nodes. Since "svg" nodes may */
1189 /* be embedded within another xml/html document there may be several of them */
1190 /* and there may be several fonts within each */
_FindSVGFontNodes(xmlNodePtr node,xmlNodePtr * fonts,int cnt,int max,char * nodename)1191 static int _FindSVGFontNodes(xmlNodePtr node,xmlNodePtr *fonts,int cnt, int max,
1192 char *nodename) {
1193 if ( xmlStrcmp(node->name,(const xmlChar *) nodename)==0 ) {
1194 if ( strcmp(nodename,"svg")==0 )
1195 nodename = "font";
1196 else {
1197 fonts[cnt++] = node;
1198 if ( cnt>=max )
1199 return( cnt );
1200 }
1201 }
1202
1203 for ( node=node->children; node!=NULL; node=node->next ) {
1204 cnt = _FindSVGFontNodes(node,fonts,cnt,max,nodename);
1205 if ( cnt>=max )
1206 return( cnt );
1207 }
1208 return( cnt );
1209 }
1210
FindSVGFontNodes(xmlDocPtr doc)1211 static xmlNodePtr *FindSVGFontNodes(xmlDocPtr doc) {
1212 xmlNodePtr *fonts=NULL;
1213 int cnt;
1214
1215 fonts = calloc(100,sizeof(xmlNodePtr)); /* If the file has more than 100 fonts in it then it's foolish to expect the user to pick out one, so let's limit ourselves to 100 */
1216 cnt = _FindSVGFontNodes(xmlDocGetRootElement(doc),fonts,0,100,"svg");
1217 if ( cnt==0 ) {
1218 free(fonts);
1219 return( NULL );
1220 }
1221 return( fonts );
1222 }
1223
SVGPickFont(xmlNodePtr * fonts,char * filename)1224 static xmlNodePtr SVGPickFont(xmlNodePtr *fonts,char *filename) {
1225 int cnt;
1226 char **names;
1227 xmlChar *name;
1228 char *pt, *lparen;
1229 int choice;
1230
1231 for ( cnt=0; fonts[cnt]!=NULL; ++cnt);
1232 names = malloc((cnt+1)*sizeof(char *));
1233 for ( cnt=0; fonts[cnt]!=NULL; ++cnt) {
1234 name = xmlGetProp(fonts[cnt],(xmlChar *) "id");
1235 if ( name==NULL ) {
1236 names[cnt] = copy("nameless-font");
1237 } else {
1238 names[cnt] = copy((char *) name);
1239 xmlFree(name);
1240 }
1241 }
1242 names[cnt] = NULL;
1243
1244 choice = -1;
1245 pt = NULL;
1246 if ( filename!=NULL )
1247 pt = strrchr(filename,'/');
1248 if ( pt==NULL ) pt = filename;
1249 if ( pt!=NULL && (lparen = strchr(pt,'('))!=NULL && strchr(lparen,')')!=NULL ) {
1250 char *find = copy(lparen+1);
1251 pt = strchr(find,')');
1252 if ( pt!=NULL ) *pt='\0';
1253 for ( choice=cnt-1; choice>=0; --choice )
1254 if ( strcmp(names[choice],find)==0 )
1255 break;
1256 if ( choice==-1 ) {
1257 char *fn = copy(filename);
1258 fn[lparen-filename] = '\0';
1259 ff_post_error(_("Not in Collection"),_("%s is not in %.100s"),find,fn);
1260 free(fn);
1261 }
1262 free(find);
1263 } else if ( no_windowing_ui )
1264 choice = 0;
1265 else
1266 choice = ff_choose(_("Pick a font, any font..."),(const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
1267 for ( cnt=0; names[cnt]!=NULL; ++cnt )
1268 free(names[cnt]);
1269 free(names);
1270 if ( choice!=-1 )
1271 return( fonts[choice] );
1272
1273 return( NULL );
1274 }
1275
1276 /* I don't see where the spec says that the seperator between numbers is */
1277 /* comma or whitespace (both is ok too) */
1278 /* But the style sheet spec says it, so I probably just missed it */
skipcomma(char * pt)1279 static char *skipcomma(char *pt) {
1280 while ( isspace(*pt))++pt;
1281 if ( *pt==',' ) ++pt;
1282 return( pt );
1283 }
1284
SVGTraceArc(SplineSet * cur,BasePoint * current,double x,double y,double rx,double ry,double axisrot,int large_arc,int sweep)1285 static void SVGTraceArc(SplineSet *cur,BasePoint *current,
1286 double x,double y,double rx,double ry,double axisrot,
1287 int large_arc,int sweep) {
1288 BasePoint ang, ut_fm, ut_to, c, foc1, foc2, tmp;
1289 double x1p, y1p, fd, sqdiff;
1290 double lambda, factor;
1291 double cxp, cyp, cx, cy;
1292 SplinePoint *sp;
1293
1294 if ( rx < 0 ) rx = -rx;
1295 if ( ry < 0 ) ry = -ry;
1296 if ( rx!=0 && ry!=0 ) {
1297 /* Page 647 in the SVG 1.1 spec describes how to do this */
1298 /* This is Appendix F (Implementation notes) section 6.5 */
1299 ang = (BasePoint) { cos(axisrot), sin(axisrot) };
1300 x1p = ang.x*(current->x-x)/2 + ang.y*(current->y-y)/2;
1301 y1p =-ang.y*(current->x-x)/2 + ang.x*(current->y-y)/2;
1302 /* Correct for bad radii */
1303 lambda = x1p*x1p/(rx*rx) + y1p*y1p/(ry*ry);
1304 if ( lambda>=1 ) {
1305 lambda = sqrt(lambda);
1306 rx *= lambda;
1307 ry *= lambda;
1308 cxp = cyp = 0;
1309 } else {
1310 factor = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p;
1311 factor = factor/(rx*rx*y1p*y1p+ry*ry*x1p*x1p);
1312 if (factor < 0) // Ideally the lambda check will prevent this, but ...
1313 factor = 0;
1314 else
1315 factor = sqrt(factor);
1316 // This part of the center calculation deals with large_arc
1317 // leaving sweep as a parameter to SSAppendArc
1318 if ( large_arc==sweep )
1319 factor = -factor;
1320 cxp = factor*(rx*y1p)/ry;
1321 cyp =-factor*(ry*x1p)/rx;
1322 }
1323 c = (BasePoint) { ang.x*cxp - ang.y*cyp + (current->x+x)/2,
1324 ang.y*cxp + ang.x*cyp + (current->y+y)/2 };
1325 // SSAppendArc is parameterized by start and end tangent angle
1326 // To convert start and end points to tangent angles it is easiest
1327 // to average the angles from the foci to the point. That average
1328 // is normal to the curve and therefore the tangent is 90 degrees
1329 // CW for a CW contour.
1330 sqdiff = rx*rx - ry*ry;
1331 if ( sqdiff > 0 )
1332 tmp = BPScale(ang, sqrt(sqdiff));
1333 else
1334 tmp = BPScale(BP90CW(ang), sqrt(-sqdiff));
1335 foc1 = BPAdd(c, tmp);
1336 foc2 = BPSub(c, tmp);
1337
1338 tmp = *current;
1339 ut_fm = BP90CW(NormVec(BPAdd(NormVec(BPSub(tmp, foc1)),
1340 NormVec(BPSub(tmp, foc2)))));
1341 tmp = (BasePoint) { x, y };
1342 ut_to = BP90CW(NormVec(BPAdd(NormVec(BPSub(tmp, foc1)),
1343 NormVec(BPSub(tmp, foc2)))));
1344 SSAppendArc(cur, rx, ry, ang, ut_fm, ut_to, sweep, false);
1345 SplineStrokeSimpleFixup(cur->last, tmp);
1346 } else {
1347 SplinePoint *sp = SplinePointCreate(x, y);
1348 SplineMake3(cur->last, sp);
1349 cur->last = sp;
1350 }
1351 *current = cur->last->me;
1352 }
1353
SVGParsePath(xmlChar * path)1354 static SplineSet *SVGParsePath(xmlChar *path) {
1355 BasePoint current;
1356 SplineSet *head=NULL, *last=NULL, *cur=NULL;
1357 SplinePoint *sp;
1358 int type = 'M';
1359 double x1,x2,x,y1,y2,y,rx,ry,axisrot;
1360 int large_arc,sweep;
1361 int order2 = 0;
1362 char *end;
1363
1364 current.x = current.y = 0;
1365
1366 while ( *path ) {
1367 while ( *path==' ' ) ++path;
1368 if ( isalpha(*path)) {
1369 type = *path++;
1370 }
1371 if ( *path=='\0' && type!='z' && type!='Z' )
1372 break;
1373 if ( type=='m' || type=='M' ) {
1374 if ( cur!=NULL && cur->last!=cur->first ) {
1375 // XXX Ugh -- who knows what scale we're at here
1376 // possibly buried in transforms
1377 if ( RealNear(cur->last->me.x,cur->first->me.x) &&
1378 RealNear(cur->last->me.y,cur->first->me.y) ) {
1379 cur->first->prevcp = cur->last->prevcp;
1380 cur->first->noprevcp = cur->last->noprevcp;
1381 cur->first->prev = cur->last->prev;
1382 cur->first->prev->to = cur->first;
1383 SplinePointFree(cur->last);
1384 cur->last = cur->first;
1385 }
1386 }
1387 x = strtod((char *) path,&end);
1388 end = skipcomma(end);
1389 y = strtod(end,&end);
1390 if ( type=='m' ) {
1391 x += current.x; y += current.y;
1392 }
1393 sp = SplinePointCreate(x,y);
1394 current = sp->me;
1395 cur = chunkalloc(sizeof(SplineSet));
1396 if ( head==NULL )
1397 head = cur;
1398 else
1399 last->next = cur;
1400 last = cur;
1401 cur->first = cur->last = sp;
1402 /* If you omit a command after a moveto then it defaults to lineto */
1403 if ( type=='m' ) type='l';
1404 else type = 'L';
1405 } else if ( type=='z' || type=='Z' ) {
1406 if ( cur!=NULL && cur->last!=cur->first ) {
1407 if ( RealNear(cur->last->me.x,cur->first->me.x) &&
1408 RealNear(cur->last->me.y,cur->first->me.y) ) {
1409 cur->first->prevcp = cur->last->prevcp;
1410 cur->first->noprevcp = cur->last->noprevcp;
1411 cur->first->prev = cur->last->prev;
1412 cur->first->prev->to = cur->first;
1413 SplinePointFree(cur->last);
1414 } else
1415 SplineMake(cur->last,cur->first,order2);
1416 cur->last = cur->first;
1417 current = cur->first->me;
1418 }
1419 type = ' ';
1420 end = (char *) path;
1421 } else {
1422 if ( cur==NULL ) {
1423 sp = SplinePointCreate(current.x,current.y);
1424 cur = chunkalloc(sizeof(SplineSet));
1425 if ( head==NULL )
1426 head = cur;
1427 else
1428 last->next = cur;
1429 last = cur;
1430 cur->first = cur->last = sp;
1431 }
1432 switch ( type ) {
1433 case 'l': case'L':
1434 x = strtod((char *) path,&end);
1435 end = skipcomma(end);
1436 y = strtod(end,&end);
1437 if ( type=='l' ) {
1438 x += current.x; y += current.y;
1439 }
1440 sp = SplinePointCreate(x,y);
1441 current = sp->me;
1442 SplineMake(cur->last,sp,order2);
1443 cur->last = sp;
1444 break;
1445 case 'h': case'H':
1446 x = strtod((char *) path,&end);
1447 y = current.y;
1448 if ( type=='h' ) {
1449 x += current.x;
1450 }
1451 sp = SplinePointCreate(x,y);
1452 current = sp->me;
1453 SplineMake(cur->last,sp,order2);
1454 cur->last = sp;
1455 break;
1456 case 'v': case 'V':
1457 x = current.x;
1458 y = strtod((char *) path,&end);
1459 if ( type=='v' ) {
1460 y += current.y;
1461 }
1462 sp = SplinePointCreate(x,y);
1463 current = sp->me;
1464 SplineMake(cur->last,sp,order2);
1465 cur->last = sp;
1466 break;
1467 case 'c': case 'C':
1468 x1 = strtod((char *) path,&end);
1469 end = skipcomma(end);
1470 y1 = strtod(end,&end);
1471 end = skipcomma(end);
1472 x2 = strtod(end,&end);
1473 end = skipcomma(end);
1474 y2 = strtod(end,&end);
1475 end = skipcomma(end);
1476 x = strtod(end,&end);
1477 end = skipcomma(end);
1478 y = strtod(end,&end);
1479 if ( type=='c' ) {
1480 x1 += current.x; y1 += current.y;
1481 x2 += current.x; y2 += current.y;
1482 x += current.x; y += current.y;
1483 }
1484 sp = SplinePointCreate(x,y);
1485 sp->prevcp.x = x2; sp->prevcp.y = y2; sp->noprevcp = false;
1486 cur->last->nextcp.x = x1; cur->last->nextcp.y = y1; cur->last->nonextcp = false;
1487 current = sp->me;
1488 SplineMake(cur->last,sp,false);
1489 cur->last = sp;
1490 break;
1491 case 's': case 'S':
1492 x1 = 2*cur->last->me.x - cur->last->prevcp.x;
1493 y1 = 2*cur->last->me.y - cur->last->prevcp.y;
1494 x2 = strtod((char *) path,&end);
1495 end = skipcomma(end);
1496 y2 = strtod(end,&end);
1497 end = skipcomma(end);
1498 x = strtod(end,&end);
1499 end = skipcomma(end);
1500 y = strtod(end,&end);
1501 if ( type=='s' ) {
1502 x2 += current.x; y2 += current.y;
1503 x += current.x; y += current.y;
1504 }
1505 sp = SplinePointCreate(x,y);
1506 sp->prevcp.x = x2; sp->prevcp.y = y2; sp->noprevcp = false;
1507 cur->last->nextcp.x = x1; cur->last->nextcp.y = y1; cur->last->nonextcp = false;
1508 current = sp->me;
1509 SplineMake(cur->last,sp,false);
1510 cur->last = sp;
1511 break;
1512 case 'Q': case 'q':
1513 x1 = strtod((char *) path,&end);
1514 end = skipcomma(end);
1515 y1 = strtod(end,&end);
1516 end = skipcomma(end);
1517 x = strtod(end,&end);
1518 end = skipcomma(end);
1519 y = strtod(end,&end);
1520 if ( type=='q' ) {
1521 x1 += current.x; y1 += current.y;
1522 x += current.x; y += current.y;
1523 }
1524 sp = SplinePointCreate(x,y);
1525 sp->prevcp.x = x1; sp->prevcp.y = y1; sp->noprevcp = false;
1526 cur->last->nextcp.x = x1; cur->last->nextcp.y = y1; cur->last->nonextcp = false;
1527 current = sp->me;
1528 SplineMake(cur->last,sp,true);
1529 cur->last = sp;
1530 order2 = true;
1531 break;
1532 case 'T': case 't':
1533 x = strtod((char *) path,&end);
1534 end = skipcomma(end);
1535 y = strtod(end,&end);
1536 if ( type=='t' ) {
1537 x += current.x; y += current.y;
1538 }
1539 x1 = 2*cur->last->me.x - cur->last->prevcp.x;
1540 y1 = 2*cur->last->me.y - cur->last->prevcp.y;
1541 sp = SplinePointCreate(x,y);
1542 sp->prevcp.x = x1; sp->prevcp.y = y1; sp->noprevcp = false;
1543 cur->last->nextcp.x = x1; cur->last->nextcp.y = y1; cur->last->nonextcp = false;
1544 current = sp->me;
1545 SplineMake(cur->last,sp,true);
1546 cur->last = sp;
1547 order2 = true;
1548 break;
1549 case 'A': case 'a':
1550 rx = strtod((char *) path,&end);
1551 end = skipcomma(end);
1552 ry = strtod(end,&end);
1553 end = skipcomma(end);
1554 axisrot = strtod(end,&end)*FF_PI/180;
1555 end = skipcomma(end);
1556 large_arc = strtol(end,&end,10);
1557 end = skipcomma(end);
1558 sweep = strtol(end,&end,10);
1559 end = skipcomma(end);
1560 x = strtod(end,&end);
1561 end = skipcomma(end);
1562 y = strtod(end,&end);
1563 if ( type=='a' ) {
1564 x += current.x; y += current.y;
1565 }
1566 if ( x!=current.x || y!=current.y )
1567 SVGTraceArc(cur,¤t,x,y,rx,ry,axisrot,large_arc,sweep);
1568 break;
1569 default:
1570 LogError( _("Unknown type '%c' found in path specification\n"), type );
1571 break;
1572 }
1573 }
1574 path = (xmlChar *) skipcomma(end);
1575 }
1576 return( head );
1577 }
1578
SVGParseExtendedPath(xmlNodePtr svg,xmlNodePtr top)1579 static SplineSet *SVGParseExtendedPath(xmlNodePtr svg, xmlNodePtr top) {
1580 /* Inkscape exends paths by allowing a sprio representation */
1581 /* But their representation looks nothing like spiros and I can't guess at it */
1582 xmlChar *outline/*, *effect, *spirooutline*/;
1583 SplineSet *head = NULL;
1584
1585 outline = xmlGetProp(svg,(xmlChar *) "d");
1586 if ( outline!=NULL ) {
1587 head = SVGParsePath(outline);
1588 xmlFree(outline);
1589 }
1590 return( head );
1591 }
1592
SVGParseRect(xmlNodePtr rect)1593 static SplineSet *SVGParseRect(xmlNodePtr rect) {
1594 /* x,y,width,height,rx,ry */
1595 double x,y,width,height,rx,ry;
1596 char *num;
1597 SplinePoint *sp;
1598 SplineSet *cur;
1599
1600 num = (char *) xmlGetProp(rect,(xmlChar *) "x");
1601 if ( num!=NULL ) {
1602 x = strtod((char *) num,NULL);
1603 xmlFree(num);
1604 } else
1605 x = 0;
1606 num = (char *) xmlGetProp(rect,(xmlChar *) "width");
1607 if ( num!=NULL ) {
1608 width = strtod((char *) num,NULL);
1609 xmlFree(num);
1610 } else
1611 return( NULL );
1612 num = (char *) xmlGetProp(rect,(xmlChar *) "y");
1613 if ( num!=NULL ) {
1614 y = strtod((char *) num,NULL);
1615 xmlFree(num);
1616 } else
1617 y = 0;
1618 num = (char *) xmlGetProp(rect,(xmlChar *) "height");
1619 if ( num!=NULL ) {
1620 height = strtod((char *) num,NULL);
1621 xmlFree(num);
1622 } else
1623 return( NULL );
1624
1625 rx = ry = 0;
1626 num = (char *) xmlGetProp(rect,(xmlChar *) "rx");
1627 if ( num!=NULL ) {
1628 ry = rx = strtod((char *) num,NULL);
1629 xmlFree(num);
1630 }
1631 num = (char *) xmlGetProp(rect,(xmlChar *) "ry");
1632 if ( num!=NULL ) {
1633 ry = strtod((char *) num,NULL);
1634 if ( rx==0 ) ry = rx;
1635 xmlFree(num);
1636 }
1637
1638 if ( 2*rx>width ) rx = width/2;
1639 if ( 2*ry>height ) ry = height/2;
1640
1641 cur = chunkalloc(sizeof(SplineSet));
1642 if ( rx==0 ) {
1643 cur->first = SplinePointCreate(x,y+height);
1644 cur->last = SplinePointCreate(x+width,y+height);
1645 SplineMake(cur->first,cur->last,true);
1646 sp = SplinePointCreate(x+width,y);
1647 SplineMake(cur->last,sp,true);
1648 cur->last = sp;
1649 sp = SplinePointCreate(x,y);
1650 SplineMake(cur->last,sp,true);
1651 SplineMake(sp,cur->first,true);
1652 cur->last = cur->first;
1653 return( cur );
1654 } else {
1655 cur->first = SplinePointCreate(x,y+height-ry);
1656 cur->last = SplinePointCreate(x+rx,y+height);
1657 cur->first->nextcp.x = x; cur->first->nextcp.y = y+height;
1658 cur->last->prevcp = cur->first->nextcp;
1659 cur->first->nonextcp = cur->last->noprevcp = false;
1660 SplineMake(cur->first,cur->last,false);
1661 if ( rx<2*width ) {
1662 sp = SplinePointCreate(x+width-rx,y+height);
1663 SplineMake(cur->last,sp,true);
1664 cur->last = sp;
1665 }
1666 sp = SplinePointCreate(x+width,y+height-ry);
1667 sp->prevcp.x = x+width; sp->prevcp.y = y+height;
1668 cur->last->nextcp = sp->prevcp;
1669 cur->last->nonextcp = sp->noprevcp = false;
1670 SplineMake(cur->last,sp,false);
1671 cur->last = sp;
1672 if ( ry<2*height ) {
1673 sp = SplinePointCreate(x+width,y+ry);
1674 SplineMake(cur->last,sp,false);
1675 cur->last = sp;
1676 }
1677 sp = SplinePointCreate(x+width-rx,y);
1678 sp->prevcp.x = x+width; sp->prevcp.y = y;
1679 cur->last->nextcp = sp->prevcp;
1680 cur->last->nonextcp = sp->noprevcp = false;
1681 SplineMake(cur->last,sp,false);
1682 cur->last = sp;
1683 if ( rx<2*width ) {
1684 sp = SplinePointCreate(x+rx,y);
1685 SplineMake(cur->last,sp,false);
1686 cur->last = sp;
1687 }
1688 cur->last->nextcp.x = x; cur->last->nextcp.y = y;
1689 cur->last->nonextcp = false;
1690 if ( ry>=2*height ) {
1691 cur->first->prevcp = cur->last->nextcp;
1692 cur->first->noprevcp = false;
1693 } else {
1694 sp = SplinePointCreate(x,y+ry);
1695 sp->prevcp = cur->last->nextcp;
1696 sp->noprevcp = false;
1697 SplineMake(cur->last,sp,false);
1698 cur->last = sp;
1699 }
1700 SplineMake(cur->last,cur->first,false);
1701 cur->first = cur->last;
1702 return( cur );
1703 }
1704 }
1705
SVGParseLine(xmlNodePtr line)1706 static SplineSet *SVGParseLine(xmlNodePtr line) {
1707 /* x1,y1, x2,y2 */
1708 double x,y, x2,y2;
1709 char *num;
1710 SplinePoint *sp1, *sp2;
1711 SplineSet *cur;
1712
1713 num = (char *) xmlGetProp(line,(xmlChar *) "x1");
1714 if ( num!=NULL ) {
1715 x = strtod((char *) num,NULL);
1716 xmlFree(num);
1717 } else
1718 x = 0;
1719 num = (char *) xmlGetProp(line,(xmlChar *) "x2");
1720 if ( num!=NULL ) {
1721 x2 = strtod((char *) num,NULL);
1722 xmlFree(num);
1723 } else
1724 x2 = 0;
1725 num = (char *) xmlGetProp(line,(xmlChar *) "y1");
1726 if ( num!=NULL ) {
1727 y = strtod((char *) num,NULL);
1728 xmlFree(num);
1729 } else
1730 y = 0;
1731 num = (char *) xmlGetProp(line,(xmlChar *) "y2");
1732 if ( num!=NULL ) {
1733 y2 = strtod((char *) num,NULL);
1734 xmlFree(num);
1735 } else
1736 y2 = 0;
1737
1738 sp1 = SplinePointCreate(x,y);
1739 sp2 = SplinePointCreate(x2,y2);
1740 SplineMake(sp1,sp2,false);
1741 cur = chunkalloc(sizeof(SplineSet));
1742 cur->first = sp1;
1743 cur->last = sp2;
1744 return( cur );
1745 }
1746
SVGParseEllipse(xmlNodePtr ellipse,int iscircle)1747 static SplineSet *SVGParseEllipse(xmlNodePtr ellipse, int iscircle) {
1748 /* cx,cy,rx,ry */
1749 /* cx,cy,r */
1750 double cx,cy,rx,ry;
1751 char *num;
1752 SplinePoint *sp;
1753 SplineSet *cur;
1754
1755 num = (char *) xmlGetProp(ellipse,(xmlChar *) "cx");
1756 if ( num!=NULL ) {
1757 cx = strtod((char *) num,NULL);
1758 xmlFree(num);
1759 } else
1760 cx = 0;
1761 num = (char *) xmlGetProp(ellipse,(xmlChar *) "cy");
1762 if ( num!=NULL ) {
1763 cy = strtod((char *) num,NULL);
1764 xmlFree(num);
1765 } else
1766 cy = 0;
1767 if ( iscircle ) {
1768 num = (char *) xmlGetProp(ellipse,(xmlChar *) "r");
1769 if ( num!=NULL ) {
1770 rx = ry = strtod((char *) num,NULL);
1771 xmlFree(num);
1772 } else
1773 return( NULL );
1774 } else {
1775 num = (char *) xmlGetProp(ellipse,(xmlChar *) "rx");
1776 if ( num!=NULL ) {
1777 rx = strtod((char *) num,NULL);
1778 xmlFree(num);
1779 } else
1780 return( NULL );
1781 num = (char *) xmlGetProp(ellipse,(xmlChar *) "ry");
1782 if ( num!=NULL ) {
1783 ry = strtod((char *) num,NULL);
1784 xmlFree(num);
1785 } else
1786 return( NULL );
1787 }
1788 if ( rx<0 ) rx = -rx;
1789 if ( ry<0 ) ry = -ry;
1790
1791 /* a magic number to make cubic beziers approximate ellipses */
1792 /* 4/3 * ( sqrt(2) - 1 ) = 0.55228... */
1793 /* also 4/3 * tan(t/4) where t = 90 b/c we do 4 curves */
1794 double magic = 0.5522847498307933984022516322796;
1795 /* offset from on-curve point to control points */
1796 double drx = rx * magic;
1797 double dry = ry * magic;
1798 cur = chunkalloc(sizeof(SplineSet));
1799 cur->first = SplinePointCreate(cx-rx,cy);
1800 cur->first->nextcp.x = cx-rx; cur->first->nextcp.y = cy+dry;
1801 cur->first->prevcp.x = cx-rx; cur->first->prevcp.y = cy-dry;
1802 cur->first->noprevcp = cur->first->nonextcp = false;
1803 cur->last = SplinePointCreate(cx,cy+ry);
1804 cur->last->prevcp.x = cx-drx; cur->last->prevcp.y = cy+ry;
1805 cur->last->nextcp.x = cx+drx; cur->last->nextcp.y = cy+ry;
1806 cur->last->noprevcp = cur->last->nonextcp = false;
1807 SplineMake(cur->first,cur->last,false);
1808 sp = SplinePointCreate(cx+rx,cy);
1809 sp->prevcp.x = cx+rx; sp->prevcp.y = cy+dry;
1810 sp->nextcp.x = cx+rx; sp->nextcp.y = cy-dry;
1811 sp->nonextcp = sp->noprevcp = false;
1812 SplineMake(cur->last,sp,false);
1813 cur->last = sp;
1814 sp = SplinePointCreate(cx,cy-ry);
1815 sp->prevcp.x = cx+drx; sp->prevcp.y = cy-ry;
1816 sp->nextcp.x = cx-drx; sp->nextcp.y = cy-ry;
1817 sp->nonextcp = sp->noprevcp = false;
1818 SplineMake(cur->last,sp,false);
1819 SplineMake(sp,cur->first,false);
1820 cur->last = cur->first;
1821 return( cur );
1822 }
1823
SVGParsePoly(xmlNodePtr poly,int isgon)1824 static SplineSet *SVGParsePoly(xmlNodePtr poly, int isgon) {
1825 /* points */
1826 double x,y;
1827 char *pts, *end;
1828 SplinePoint *sp;
1829 SplineSet *cur;
1830
1831 pts = (char *) xmlGetProp(poly,(xmlChar *) "points");
1832 if ( pts==NULL )
1833 return( NULL );
1834
1835 x = strtod(pts,&end);
1836 while ( isspace(*end) || *end==',' ) ++end;
1837 y = strtod(end,&end);
1838 while ( isspace(*end)) ++end;
1839
1840 cur = chunkalloc(sizeof(SplineSet));
1841 cur->first = cur->last = SplinePointCreate(x,y);
1842 while ( *end ) {
1843 x = strtod(end,&end);
1844 while ( isspace(*end) || *end==',' ) ++end;
1845 y = strtod(end,&end);
1846 while ( isspace(*end)) ++end;
1847 sp = SplinePointCreate(x,y);
1848 SplineMake(cur->last,sp,false);
1849 cur->last = sp;
1850 }
1851 if ( isgon ) {
1852 if ( RealNear(cur->last->me.x,cur->first->me.x) &&
1853 RealNear(cur->last->me.y,cur->first->me.y) ) {
1854 cur->first->prev = cur->last->prev;
1855 cur->first->prev->to = cur->first;
1856 SplinePointFree(cur->last);
1857 } else
1858 SplineMake(cur->last,cur->first,false);
1859 cur->last = cur->first;
1860 }
1861 return( cur );
1862 }
1863
1864 struct svg_state {
1865 double linewidth;
1866 double miterlimit;
1867 int dofill, dostroke;
1868 uint32 fillcol, strokecol;
1869 float fillopacity, strokeopacity;
1870 int isvisible;
1871 enum linecap lc;
1872 enum linejoin lj;
1873 real transform[6];
1874 DashType dashes[DASH_MAX];
1875 SplineSet *clippath;
1876 uint8 free_clip;
1877 uint32 currentColor;
1878 uint32 stopColor;
1879 float stopOpacity;
1880 };
1881
SVGFigureTransform(struct svg_state * st,char * name)1882 static void SVGFigureTransform(struct svg_state *st,char *name) {
1883 real trans[6], res[6];
1884 double a, cx,cy;
1885 char *pt, *paren, *end;
1886 /* matrix(a,b,c,d,e,f)
1887 rotate(theta[,cx,cy])
1888 scale(sx[,sy])
1889 translate(x,y)
1890 skewX(theta)
1891 skewY(theta)
1892 */
1893
1894 for ( pt = (char *)name; isspace(*pt); ++pt );
1895 while ( *pt ) {
1896 paren = strchr(pt,'(');
1897 if ( paren==NULL )
1898 break;
1899 if ( strncmp(pt,"matrix",paren-pt)==0 ) {
1900 trans[0] = strtod(paren+1,&end);
1901 trans[1] = strtod(skipcomma(end),&end);
1902 trans[2] = strtod(skipcomma(end),&end);
1903 trans[3] = strtod(skipcomma(end),&end);
1904 trans[4] = strtod(skipcomma(end),&end);
1905 trans[5] = strtod(skipcomma(end),&end);
1906 } else if ( strncmp(pt,"rotate",paren-pt)==0 ) {
1907 trans[4] = trans[5] = 0;
1908 a = strtod(paren+1,&end)*FF_PI/180;
1909 trans[0] = trans[3] = cos(a);
1910 trans[1] = sin(a);
1911 trans[2] = -trans[1];
1912 while ( isspace(*end)) ++end;
1913 if ( *end!=')' ) {
1914 cx = strtod(skipcomma(end),&end);
1915 cy = strtod(skipcomma(end),&end);
1916 res[0] = res[3] = 1;
1917 res[1] = res[2] = 0;
1918 res[4] = cx; res[5] = cy;
1919 MatMultiply(res,trans,res);
1920 trans[0] = trans[3] = 1;
1921 trans[1] = trans[2] = 0;
1922 trans[4] = -cx; trans[5] = -cy;
1923 MatMultiply(res,trans,res);
1924 memcpy(trans,res,sizeof(res));
1925 }
1926 } else if ( strncmp(pt,"scale",paren-pt)==0 ) {
1927 trans[1] = trans[2] = trans[4] = trans[5] = 0;
1928 trans[0] = trans[3] = strtod(paren+1,&end);
1929 while ( isspace(*end)) ++end;
1930 if ( *end!=')' )
1931 trans[3] = strtod(skipcomma(end),&end);
1932 } else if ( strncmp(pt,"translate",paren-pt)==0 ) {
1933 trans[0] = trans[3] = 1;
1934 trans[1] = trans[2] = trans[5] = 0;
1935 trans[4] = strtod(paren+1,&end);
1936 while ( isspace(*end)) ++end;
1937 if ( *end!=')' )
1938 trans[5] = strtod(skipcomma(end),&end);
1939 } else if ( strncmp(pt,"skewX",paren-pt)==0 ) {
1940 trans[0] = trans[3] = 1;
1941 trans[1] = trans[2] = trans[4] = trans[5] = 0;
1942 trans[2] = tan(strtod(paren+1,&end)*FF_PI/180);
1943 } else if ( strncmp(pt,"skewY",paren-pt)==0 ) {
1944 trans[0] = trans[3] = 1;
1945 trans[1] = trans[2] = trans[4] = trans[5] = 0;
1946 trans[1] = tan(strtod(paren+1,&end)*FF_PI/180);
1947 } else
1948 break;
1949 while ( isspace(*end)) ++end;
1950 if ( *end!=')')
1951 break;
1952 MatMultiply(trans,st->transform,st->transform);
1953 pt = end+1;
1954 while ( isspace(*pt)) ++pt;
1955 }
1956 }
1957
SVGuseTransform(struct svg_state * st,xmlNodePtr use,xmlNodePtr symbol)1958 static void SVGuseTransform(struct svg_state *st,xmlNodePtr use, xmlNodePtr symbol) {
1959 double x,y,uwid,uheight,swid,sheight;
1960 char *num, *end;
1961 real trans[6];
1962
1963 num = (char *) xmlGetProp(use,(xmlChar *) "x");
1964 if ( num!=NULL ) {
1965 x = strtod((char *) num,NULL);
1966 xmlFree(num);
1967 } else
1968 x = 0;
1969 num = (char *) xmlGetProp(use,(xmlChar *) "y");
1970 if ( num!=NULL ) {
1971 y = strtod((char *) num,NULL);
1972 xmlFree(num);
1973 } else
1974 y = 0;
1975 if ( x!=0 || y!=0 ) {
1976 trans[0] = trans[3] = 1;
1977 trans[1] = trans[2] = 0;
1978 trans[4] = x; trans[5] = y;
1979 MatMultiply(trans,st->transform,st->transform);
1980 }
1981 num = (char *) xmlGetProp(use,(xmlChar *) "width");
1982 if ( num!=NULL ) {
1983 uwid = strtod((char *) num,NULL);
1984 xmlFree(num);
1985 } else
1986 uwid = 0;
1987 num = (char *) xmlGetProp(use,(xmlChar *) "height");
1988 if ( num!=NULL ) {
1989 uheight = strtod((char *) num,NULL);
1990 xmlFree(num);
1991 } else
1992 uheight = 0;
1993 num = (char *) xmlGetProp(symbol,(xmlChar *) "viewBox");
1994 if ( num!=NULL ) {
1995 x = strtod((char *) num,&end);
1996 y = strtod((char *) end+1,&end);
1997 swid = strtod((char *) end+1,&end);
1998 sheight = strtod((char *) end+1,&end);
1999 xmlFree(num);
2000 } else
2001 return;
2002 if ( uwid != 0 || uheight != 0 ) {
2003 trans[0] = trans[3] = 1;
2004 trans[1] = trans[2] = trans[4] = trans[5] = 0;
2005 if ( uwid != 0 && swid!=0 ) trans[0] = uwid/swid;
2006 if ( uheight != 0 && sheight!=0 ) trans[3] = uheight/sheight;
2007 MatMultiply(trans,st->transform,st->transform);
2008 }
2009 }
2010
parseGCoord(xmlChar * prop,int bb_units,real bb_low,real bb_high)2011 static real parseGCoord(xmlChar *prop,int bb_units,real bb_low, real bb_high) {
2012 char *end;
2013 double val = strtod((char *) prop,&end);
2014
2015 if ( *end=='%' )
2016 val /= 100.0;
2017
2018 if ( bb_units )
2019 val = bb_low + val*(bb_high-bb_low);
2020 return( val );
2021 }
2022
2023 static int xmlParseColor(xmlChar *name,uint32 *color, char **url,struct svg_state *st);
2024
xmlParseColorSource(xmlNodePtr top,char * name,DBounds * bbox,struct svg_state * st,struct gradient ** _grad,struct epattern ** _epat)2025 static void xmlParseColorSource(xmlNodePtr top,char *name,DBounds *bbox,
2026 struct svg_state *st,struct gradient **_grad,struct epattern **_epat) {
2027 xmlNodePtr colour_source = XmlFindURI(top,name);
2028 int islinear;
2029 xmlChar *prop;
2030 xmlNodePtr kid;
2031 int scnt;
2032
2033 *_grad = NULL; *_epat = NULL;
2034 if ( colour_source==NULL )
2035 LogError(_("Could not find Color Source with id %s."), name );
2036 else if ( (islinear = (xmlStrcmp(colour_source->name,(xmlChar *) "linearGradient")==0)) ||
2037 xmlStrcmp(colour_source->name,(xmlChar *) "radialGradient")==0 ) {
2038 struct gradient *grad = chunkalloc(sizeof(struct gradient));
2039 int bbox_units;
2040 *_grad = grad;
2041
2042 prop = xmlGetProp(colour_source,(xmlChar *) "gradientUnits");
2043 if ( prop!=NULL ) {
2044 bbox_units = xmlStrcmp(prop,(xmlChar *) "userSpaceOnUse")!=0;
2045 xmlFree(prop);
2046 } else
2047 bbox_units = true;
2048
2049 prop = xmlGetProp(colour_source,(xmlChar *) "gradientTransform");
2050 /* I don't support this currently */
2051 if ( prop!=NULL )
2052 xmlFree(prop);
2053
2054 grad->sm = sm_pad;
2055 prop = xmlGetProp(colour_source,(xmlChar *) "spreadMethod");
2056 if ( prop!=NULL ) {
2057 if ( xmlStrcmp(prop,(xmlChar *) "reflect")==0 )
2058 grad->sm = sm_reflect;
2059 else if ( xmlStrcmp(prop,(xmlChar *) "repeat")==0 )
2060 grad->sm = sm_repeat;
2061 xmlFree(prop);
2062 }
2063
2064 if ( islinear ) {
2065 grad->start.x = bbox->minx; grad->start.y = bbox->miny;
2066 grad->stop.x = bbox->maxx; grad->stop.y = bbox->maxy;
2067
2068 prop = xmlGetProp(colour_source,(xmlChar *) "x1");
2069 if ( prop!=NULL ) {
2070 grad->start.x = parseGCoord( prop,bbox_units,bbox->minx,bbox->maxx);
2071 xmlFree(prop);
2072 }
2073
2074 prop = xmlGetProp(colour_source,(xmlChar *) "x2");
2075 if ( prop!=NULL ) {
2076 grad->stop.x = parseGCoord( prop,bbox_units,bbox->minx,bbox->maxx);
2077 xmlFree(prop);
2078 }
2079
2080 prop = xmlGetProp(colour_source,(xmlChar *) "y1");
2081 if ( prop!=NULL ) {
2082 grad->start.y = parseGCoord( prop,bbox_units,bbox->miny,bbox->maxy);
2083 xmlFree(prop);
2084 }
2085
2086 prop = xmlGetProp(colour_source,(xmlChar *) "y2");
2087 if ( prop!=NULL ) {
2088 grad->stop.y = parseGCoord( prop,bbox_units,bbox->miny,bbox->maxy);
2089 xmlFree(prop);
2090 }
2091
2092 grad->radius = 0;
2093 } else {
2094 double offx = (bbox->maxx-bbox->minx)/2;
2095 double offy = (bbox->maxy-bbox->miny)/2;
2096 grad->stop.x = (bbox->minx+bbox->maxx)/2;
2097 grad->stop.y = (bbox->minx+bbox->maxy)/2;
2098 grad->radius = sqrt(offx*offx + offy*offy);
2099
2100 prop = xmlGetProp(colour_source,(xmlChar *) "cx");
2101 if ( prop!=NULL ) {
2102 grad->stop.x = parseGCoord( prop,bbox_units,bbox->minx,bbox->maxx);
2103 xmlFree(prop);
2104 }
2105
2106 prop = xmlGetProp(colour_source,(xmlChar *) "cy");
2107 if ( prop!=NULL ) {
2108 grad->stop.y = parseGCoord( prop,bbox_units,bbox->miny,bbox->maxy);
2109 xmlFree(prop);
2110 }
2111
2112 prop = xmlGetProp(colour_source,(xmlChar *) "radius");
2113 if ( prop!=NULL ) {
2114 grad->radius = parseGCoord( prop,bbox_units,0,sqrt(4*(offx*offx + offy*offy)));
2115 xmlFree(prop);
2116 }
2117
2118 grad->start = grad->stop;
2119 prop = xmlGetProp(colour_source,(xmlChar *) "fx");
2120 if ( prop!=NULL ) {
2121 grad->start.x = parseGCoord( prop,bbox_units,bbox->minx,bbox->maxx);
2122 xmlFree(prop);
2123 }
2124
2125 prop = xmlGetProp(colour_source,(xmlChar *) "fy");
2126 if ( prop!=NULL ) {
2127 grad->start.y = parseGCoord( prop,bbox_units,bbox->miny,bbox->maxy);
2128 xmlFree(prop);
2129 }
2130 }
2131
2132 scnt = 0;
2133 for ( kid = colour_source->children; kid!=NULL; kid=kid->next ) if ( xmlStrcmp(kid->name,(xmlChar *) "stop")==0 )
2134 ++scnt;
2135
2136 if ( scnt==0 ) {
2137 /* I'm not sure how to use the style stop-color, but I'm guessing */
2138 /* this might be it */
2139 grad->stop_cnt = 1;
2140 grad->grad_stops = calloc(1,sizeof(struct grad_stops));
2141 grad->grad_stops[scnt].offset = 1;
2142 grad->grad_stops[scnt].col = st->stopColor;
2143 grad->grad_stops[scnt].opacity = st->stopOpacity;
2144 ++scnt;
2145 } else {
2146 grad->stop_cnt = scnt;
2147 grad->grad_stops = calloc(scnt,sizeof(struct grad_stops));
2148 scnt = 0;
2149 for ( kid = colour_source->children; kid!=NULL; kid=kid->next ) if ( xmlStrcmp(kid->name,(xmlChar *) "stop")==0 ) {
2150 grad->grad_stops[scnt].col = st->stopColor;
2151 grad->grad_stops[scnt].opacity = st->stopOpacity;
2152
2153 prop = xmlGetProp(kid,(xmlChar *) "offset");
2154 if ( prop!=NULL ) {
2155 grad->grad_stops[scnt].offset = parseGCoord( prop,false,0,1.0);
2156 xmlFree(prop);
2157 }
2158
2159 prop = xmlGetProp(kid,(xmlChar *) "stop-color");
2160 if ( prop!=NULL ) {
2161 xmlParseColor(prop, &grad->grad_stops[scnt].col, NULL, st);
2162 xmlFree(prop);
2163 }
2164
2165 prop = xmlGetProp(kid,(xmlChar *) "stop-opacity");
2166 if ( prop!=NULL ) {
2167 grad->grad_stops[scnt].opacity = strtod((char *) prop,NULL);
2168 xmlFree(prop);
2169 } else
2170 grad->grad_stops[scnt].opacity = 1.0;
2171
2172 ++scnt;
2173 }
2174 }
2175 } else if ( xmlStrcmp(colour_source->name,(xmlChar *) "pattern")==0 ) {
2176 LogError(_("FontForge does not currently parse pattern Color Sources (%s)."),
2177 name );
2178 } else {
2179 LogError(_("Color Source with id %s had an unexpected type %s."),
2180 name, (char *) colour_source->name );
2181 }
2182 }
2183
2184 /* Annoyingly we can't parse the colour source until we have parsed the contents */
2185 /* because the colour source needs to know the bounding box of the total item */
2186 /* This means that for something like <g> we must now go back and figure out */
2187 /* which components get this gradient/pattern, and which have their own source*/
2188 /* that overrides the inherited one */
xmlApplyColourSources(xmlNodePtr top,Entity * head,struct svg_state * st,char * fill_colour_source,char * stroke_colour_source)2189 static void xmlApplyColourSources(xmlNodePtr top,Entity *head,
2190 struct svg_state *st,
2191 char *fill_colour_source,char *stroke_colour_source) {
2192 DBounds b, ssb;
2193 Entity *e;
2194 struct gradient *grad;
2195 struct epattern *epat;
2196
2197 memset(&b,0,sizeof(b));
2198 for ( e=head; e!=NULL; e=e->next ) if ( e->type==et_splines ) {
2199 SplineSetFindBounds(e->u.splines.splines,&ssb);
2200 if ( b.minx==0 && b.miny==0 && b.maxx==0 && b.maxy==0 )
2201 b = ssb;
2202 else {
2203 if ( b.minx>ssb.minx ) b.minx = ssb.minx;
2204 if ( b.maxx>ssb.maxx ) b.maxx = ssb.maxx;
2205 if ( b.miny>ssb.miny ) b.miny = ssb.miny;
2206 if ( b.maxy>ssb.maxy ) b.maxy = ssb.maxy;
2207 }
2208 }
2209 if ( b.minx==b.maxx ) b.maxx = b.minx+1;
2210 if ( b.miny==b.maxy ) b.maxy = b.maxy+1;
2211
2212 if ( fill_colour_source!=NULL ) {
2213 xmlParseColorSource(top,fill_colour_source,&b,st,&grad,&epat);
2214 free(fill_colour_source);
2215 for ( e=head; e!=NULL; e=e->next ) if ( e->type==et_splines ) {
2216 if ( e->u.splines.fill.grad==NULL && e->u.splines.fill.tile==NULL &&
2217 e->u.splines.fill.col == COLOR_INHERITED ) {
2218 e->u.splines.fill.grad = GradientCopy(grad,NULL);
2219 /*e->u.splines.fill.tile = EPatternCopy(epat,NULL);*/
2220 }
2221 }
2222 GradientFree(grad);
2223 /*EPatternFree(epat);*/
2224 }
2225
2226 if ( stroke_colour_source!=NULL ) {
2227 xmlParseColorSource(top,stroke_colour_source,&b,st,&grad,&epat);
2228 free(stroke_colour_source);
2229 for ( e=head; e!=NULL; e=e->next ) if ( e->type==et_splines ) {
2230 if ( e->u.splines.stroke.grad==NULL && e->u.splines.stroke.tile==NULL &&
2231 e->u.splines.stroke.col == COLOR_INHERITED ) {
2232 e->u.splines.stroke.grad = GradientCopy(grad,NULL);
2233 /*e->u.splines.stroke.tile = EPatternCopy(epat,NULL);*/
2234 }
2235 }
2236 GradientFree(grad);
2237 /*EPatternFree(epat);*/
2238 }
2239 }
2240
xmlParseColor(xmlChar * name,uint32 * color,char ** url,struct svg_state * st)2241 static int xmlParseColor(xmlChar *name,uint32 *color, char **url,struct svg_state *st) {
2242 int doit, i;
2243 static struct { char *name; uint32 col; } stdcols[] = {
2244 { "red", 0xff0000 },
2245 { "green", 0x008000 },
2246 { "blue", 0x0000ff },
2247 { "cyan", 0x00ffff },
2248 { "magenta", 0xff00ff },
2249 { "yellow", 0xffff00 },
2250 { "black", 0x000000 },
2251 { "darkgray", 0x404040 },
2252 { "darkgrey", 0x404040 },
2253 { "gray", 0x808080 },
2254 { "grey", 0x808080 },
2255 { "lightgray", 0xc0c0c0 },
2256 { "lightgrey", 0xc0c0c0 },
2257 { "white", 0xffffff },
2258 { "maroon", 0x800000 },
2259 { "olive", 0x808000 },
2260 { "navy", 0x000080 },
2261 { "purple", 0x800080 },
2262 { "lime", 0x00ff00 },
2263 { "aqua", 0x00ffff },
2264 { "teal", 0x008080 },
2265 { "fuchsia", 0xff0080 },
2266 { "silver", 0xc0c0c0 },
2267 { NULL, 0 }
2268 };
2269
2270 doit = xmlStrcmp(name,(xmlChar *) "none")!=0;
2271 if ( doit ) {
2272 for ( i=0; stdcols[i].name!=NULL; ++i )
2273 if ( xmlStrcmp(name,(xmlChar *) stdcols[i].name)==0 )
2274 break;
2275 if ( stdcols[i].name!=NULL )
2276 *color = stdcols[i].col;
2277 else if ( xmlStrcmp(name,(xmlChar *) "currentColor")==0 )
2278 *color = st->currentColor;
2279 else if ( name[0]=='#' ) {
2280 unsigned int temp=0;
2281 if ( sscanf( (char *) name, "#%x", &temp )!=1 )
2282 LogError( _("Bad hex color spec: %s\n"), (char *) name );
2283 if ( strlen( (char *) name)==4 ) {
2284 *color = (((temp&0xf00)*0x11)<<8) |
2285 (((temp&0x0f0)*0x11)<<4) |
2286 (((temp&0x00f)*0x11) );
2287 } else if ( strlen( (char *) name)==7 ) {
2288 *color = temp;
2289 } else
2290 *color = COLOR_INHERITED;
2291 } else if ( strncmp( (char *) name, "rgb(",4)==0 ) {
2292 float r=0,g=0,b=0;
2293 if ( sscanf((char *)name + 4, "%g,%g,%g", &r, &g, &b )!=3 )
2294 LogError( _("Bad RGB color spec: %s\n"), (char *) name );
2295 if ( strchr((char *) name,'.')!=NULL ) {
2296 if ( r>=1 ) r = 1; else if ( r<0 ) r=0;
2297 if ( g>=1 ) g = 1; else if ( g<0 ) g=0;
2298 if ( b>=1 ) b = 1; else if ( b<0 ) b=0;
2299 *color = ( ((int) rint(r*255))<<16 ) |
2300 ( ((int) rint(g*255))<<8 ) |
2301 ( ((int) rint(b*255)) );
2302 } else {
2303 if ( r>=255 ) r = 255; else if ( r<0 ) r=0;
2304 if ( g>=255 ) g = 255; else if ( g<0 ) g=0;
2305 if ( b>=255 ) b = 255; else if ( b<0 ) b=0;
2306 *color = ( ((int) r)<<16 ) |
2307 ( ((int) g)<<8 ) |
2308 ( ((int) b) );
2309 }
2310 } else if ( url!=NULL && strncmp( (char *) name, "url(#",5)==0 ) {
2311 *url = copy( (char *) name);
2312 *color = COLOR_INHERITED;
2313 } else {
2314 LogError( _("Failed to parse color %s\n"), (char *) name );
2315 *color = COLOR_INHERITED;
2316 }
2317 }
2318 return( doit );
2319 }
2320
base64ch(int ch)2321 static int base64ch(int ch) {
2322 if ( ch>='A' && ch<='Z' )
2323 return( ch-'A' );
2324 if ( ch>='a' && ch<='z' )
2325 return( ch-'a'+26 );
2326 if ( ch>='0' && ch<='9' )
2327 return( ch-'0'+52 );
2328 if ( ch=='+' )
2329 return( 62 );
2330 if ( ch=='/' )
2331 return( 63 );
2332 if ( ch=='=' )
2333 return( 64 );
2334
2335 return( -1 );
2336 }
2337
DecodeBase64ToFile(FILE * tmp,char * str)2338 static void DecodeBase64ToFile(FILE *tmp,char *str) {
2339 char fourchars[4];
2340 int i;
2341
2342 while ( *str ) {
2343 fourchars[0] = fourchars[1] = fourchars[2] = fourchars[3] = 64;
2344 for ( i=0; i<4; ++i ) {
2345 while ( *str!='\0' && ( isspace(*str) || base64ch(*str)==-1 ) ) ++str;
2346 if ( *str=='\0' )
2347 break;
2348 fourchars[i] = base64ch(*str++);
2349 }
2350 if ( fourchars[0]<64 && fourchars[1]<64 ) {
2351 putc((fourchars[0]<<2)|(fourchars[1]>>4), tmp);
2352 if ( fourchars[2]<64 ) {
2353 putc((fourchars[1]<<4)|(fourchars[2]>>2), tmp);
2354 if ( fourchars[3]<64 )
2355 putc((fourchars[2]<<6)|fourchars[3], tmp);
2356 }
2357 }
2358 }
2359 }
2360
GImageFromDataURI(char * uri)2361 static GImage *GImageFromDataURI(char *uri) {
2362 char *mimetype;
2363 int is_base64=false, ch;
2364 FILE *tmp;
2365 GImage *img;
2366
2367 if ( uri==NULL )
2368 return( NULL );
2369 if ( strncmp(uri,"data:",5)!=0 )
2370 return( NULL );
2371 uri += 5;
2372
2373 mimetype = uri;
2374 while ( *uri!=',' && *uri!=';' && *uri!='\0' ) ++uri;
2375 if ( *uri=='\0' )
2376 return( NULL );
2377 ch = *uri;
2378 *uri='\0';
2379 if ( ch==';' && strncmp(uri+1,"base64,",7)==0 ) {
2380 is_base64=true;
2381 uri += 6;
2382 ch = ',';
2383 } else if ( ch==';' ) /* Can't deal with other encoding methods */
2384 return( NULL );
2385
2386 ++uri;
2387 if ( strcmp(mimetype,"image/png")==0 ||
2388 strcmp(mimetype,"image/jpeg")==0 ||
2389 strcmp(mimetype,"image/bmp")==0 )
2390 /* These we support (if we've got the libraries) */;
2391 else {
2392 LogError(_("Unsupported mime type in data URI: %s\n"), mimetype );
2393 return( NULL );
2394 }
2395 tmp = GFileTmpfile();
2396 if ( is_base64 )
2397 DecodeBase64ToFile(tmp,uri);
2398 else {
2399 while ( *uri ) {
2400 putc(*uri,tmp);
2401 ++uri;
2402 }
2403 }
2404 rewind(tmp);
2405 #ifndef _NO_LIBPNG
2406 if ( strcmp(mimetype,"image/png")==0 )
2407 img = GImageRead_Png(tmp);
2408 else
2409 #endif
2410 #ifndef _NO_LIBJPEG
2411 if ( strcmp(mimetype,"image/jpeg")==0 )
2412 img = GImageRead_Jpeg(tmp);
2413 else
2414 #endif
2415 if ( strcmp(mimetype,"image/bmp")==0 )
2416 img = GImageRead_Bmp(tmp);
2417 else
2418 img = NULL;
2419 fclose(tmp);
2420 return( img );
2421 }
2422
SVGParseImage(xmlNodePtr svg)2423 static Entity *SVGParseImage(xmlNodePtr svg) {
2424 double x=0,y=0,width=1,height=1;
2425 GImage *img;
2426 struct _GImage *base;
2427 Entity *ent;
2428 xmlChar *val;
2429
2430 val = xmlGetProp(svg,(xmlChar *) "x");
2431 if ( val!=NULL ) {
2432 x = strtod((char *) val,NULL);
2433 free(val);
2434 }
2435 val = xmlGetProp(svg,(xmlChar *) "y");
2436 if ( val!=NULL ) {
2437 y = strtod((char *) val,NULL);
2438 free(val);
2439 }
2440
2441 val = xmlGetProp(svg,(xmlChar *) "width");
2442 if ( val!=NULL ) {
2443 width = strtod((char *) val,NULL);
2444 free(val);
2445 }
2446 val = xmlGetProp(svg,(xmlChar *) "height");
2447 if ( val!=NULL ) {
2448 height = strtod((char *) val,NULL);
2449 free(val);
2450 }
2451
2452 val = xmlGetProp(svg,(xmlChar *) /*"xlink:href"*/ "href");
2453 if ( val==NULL )
2454 return( NULL );
2455 if ( strncmp((char *) val,"data:",5)!=0 ) {
2456 LogError(_("FontForge only supports embedded images in data: URIs\n"));
2457 free(val);
2458 return( NULL ); /* I can only handle data URIs */
2459 }
2460 img = GImageFromDataURI((char *) val);
2461 free(val);
2462 if ( img==NULL )
2463 return( NULL );
2464 base = img->list_len==0 ? img->u.image : img->u.images[0];
2465
2466 ent = chunkalloc(sizeof(Entity));
2467 ent->type = et_image;
2468 ent->u.image.image = img;
2469 ent->u.image.transform[1] = ent->u.image.transform[2] = 0;
2470 ent->u.image.transform[0] = width/base->width;
2471 ent->u.image.transform[3] = height/base->height;
2472 ent->u.image.transform[4] = x;
2473 ent->u.image.transform[5] = y;
2474 ent->u.image.col = 0xffffffff;
2475 return( ent );
2476 }
2477
EntityCreate(SplinePointList * head,struct svg_state * state)2478 static Entity *EntityCreate(SplinePointList *head,struct svg_state *state) {
2479 Entity *ent = calloc(1,sizeof(Entity));
2480 ent->type = et_splines;
2481 ent->u.splines.splines = head;
2482 ent->u.splines.cap = state->lc;
2483 ent->u.splines.join = state->lj;
2484 ent->u.splines.stroke_width = state->linewidth;
2485 ent->u.splines.miterlimit = state->miterlimit;
2486 ent->u.splines.fill.col = state->dofill ? state->fillcol : state->dostroke ? 0xffffffff : COLOR_INHERITED;
2487 ent->u.splines.stroke.col = state->dostroke ? state->strokecol : 0xffffffff;
2488 ent->u.splines.fill.opacity = state->fillopacity;
2489 ent->u.splines.stroke.opacity = state->strokeopacity;
2490 memcpy(ent->u.splines.transform,state->transform,6*sizeof(real));
2491 ent->clippath = SplinePointListCopy(state->clippath);
2492 return( ent );
2493 }
2494
SvgStateFree(struct svg_state * st)2495 static void SvgStateFree(struct svg_state *st) {
2496 if ( st->free_clip )
2497 SplinePointListFree(st->clippath);
2498 }
2499
SVGFigureStyle(struct svg_state * st,char * name,char ** fill_colour_source,char ** stroke_colour_source)2500 static void SVGFigureStyle(struct svg_state *st,char *name,
2501 char **fill_colour_source,char **stroke_colour_source) {
2502 char *pt;
2503 char namebuf[200], propbuf[400];
2504
2505 for (;;) {
2506 while ( isspace(*name)) ++name;
2507 if ( *name==':' ) {
2508 /* Missing prop name, skip the value */
2509 while ( *name!=';' && *name!='\0' ) ++name;
2510 if ( *name==';' ) ++name;
2511 } else if ( *name!='\0' && *name!=';' ) {
2512 pt = namebuf;
2513 while ( *name!='\0' && *name!=':' && *name!=';' && !isspace(*name) ) {
2514 if ( pt<namebuf+sizeof(namebuf)-1 )
2515 *pt++ = *name;
2516 ++name;
2517 }
2518 *pt = '\0';
2519 while ( *name!=':' && *name!=';' && *name!='\0' ) ++name;
2520 if ( *name==':' ) ++name;
2521
2522 /* fetch prop value */
2523 while ( isspace(*name) ) ++name;
2524 propbuf[0] = '\0';
2525 if ( *name!='\0' && *name!=';' ) {
2526 pt = propbuf;
2527 while ( *name!='\0' && *name!=';' && !isspace(*name) ) {
2528 if ( pt<propbuf+sizeof(propbuf)-1 )
2529 *pt++ = *name;
2530 ++name;
2531 }
2532 *pt = '\0';
2533 }
2534 while ( *name!=';' && *name!='\0' ) ++name;
2535 if ( *name==';' ) ++name;
2536
2537 if ( strcmp(namebuf,"color")==0 )
2538 xmlParseColor(propbuf,&st->currentColor,NULL,st);
2539 else if ( strcmp(namebuf,"fill")==0 )
2540 st->dofill = xmlParseColor(propbuf,&st->fillcol,fill_colour_source,st);
2541 else if ( strcmp(namebuf,"visibility")==0 )
2542 st->isvisible = strcmp(propbuf,"hidden")!=0 &&
2543 strcmp(propbuf,"colapse")!=0;
2544 else if ( strcmp(namebuf,"fill-opacity")==0 )
2545 st->fillopacity = strtod(propbuf,NULL);
2546 else if ( strcmp(namebuf,"stroke")==0 )
2547 st->dostroke = xmlParseColor(propbuf,&st->strokecol,stroke_colour_source,st);
2548 else if ( strcmp(namebuf,"stroke-opacity")==0 )
2549 st->strokeopacity = strtod((char *)propbuf,NULL);
2550 else if ( strcmp(namebuf,"stroke-width")==0 )
2551 st->linewidth = strtod((char *)propbuf,NULL);
2552 else if ( strcmp(namebuf,"stroke-linecap")==0 )
2553 st->lc = strcmp(propbuf,"butt")==0 ? lc_butt :
2554 strcmp(propbuf,"round")==0 ? lc_round :
2555 lc_square;
2556 else if ( strcmp(namebuf,"stroke-linejoin")==0 )
2557 st->lj = strcmp(propbuf,"miter-clip")==0 ? lj_miterclip :
2558 strcmp(propbuf,"miter")==0 ? lj_miter :
2559 strcmp(propbuf,"round")==0 ? lj_round :
2560 strcmp(propbuf,"arcs")==0 ? lj_arcs :
2561 lj_bevel;
2562 else if ( strcmp(namebuf,"stroke-miterlimit")==0 )
2563 st->miterlimit = strtod((char *)propbuf,NULL);
2564 else if ( strcmp(namebuf,"stroke-dasharray")==0 ) {
2565 if ( strcmp(propbuf,"inherit")==0 ) {
2566 st->dashes[0] = 0; st->dashes[1] = DASH_INHERITED;
2567 } else if ( strcmp(propbuf,"none")==0 ) {
2568 st->dashes[0] = 0; st->dashes[1] = 0;
2569 } else {
2570 int i;
2571 char *pt, *end;
2572 pt = propbuf;
2573 for ( i=0; i<DASH_MAX && *pt!='\0'; ++i ) {
2574 st->dashes[i] = strtol( pt, &end,10);
2575 pt = end;
2576 }
2577 if ( i<DASH_MAX ) st->dashes[i] = 0;
2578 }
2579 } else if ( strcmp(namebuf,"stop-color")==0 )
2580 xmlParseColor(propbuf,&st->stopColor,NULL,st);
2581 else if ( strcmp(namebuf,"stop-opacity")==0 )
2582 st->stopOpacity = strtod(propbuf,NULL);
2583 else {
2584 /* Lots of props we ignore */
2585 ;
2586 }
2587 } else if ( *name==';' )
2588 ++name;
2589 if ( *name=='\0' )
2590 break;
2591 }
2592 }
2593
_SVGParseSVG(xmlNodePtr svg,xmlNodePtr top,struct svg_state * inherit)2594 static Entity *_SVGParseSVG(xmlNodePtr svg, xmlNodePtr top,
2595 struct svg_state *inherit) {
2596 struct svg_state st;
2597 xmlChar *name;
2598 xmlNodePtr kid;
2599 Entity *ehead, *elast, *eret;
2600 SplineSet *head;
2601 int treat_symbol_as_g = false;
2602 char *fill_colour_source = NULL, *stroke_colour_source = NULL;
2603
2604 if ( svg==NULL )
2605 return( NULL );
2606
2607 st = *inherit;
2608 st.free_clip = false;
2609 tail_recurse:
2610 name = xmlGetProp(svg,(xmlChar *) "display");
2611 if ( name!=NULL ) {
2612 int hide = xmlStrcmp(name,(xmlChar *) "none")==0;
2613 xmlFree(name);
2614 if ( hide )
2615 return( NULL );
2616 }
2617 name = xmlGetProp(svg,(xmlChar *) "visibility");
2618 if ( name!=NULL ) {
2619 st.isvisible = xmlStrcmp(name,(xmlChar *) "hidden")!=0 &&
2620 xmlStrcmp(name,(xmlChar *) "colapse")!=0;
2621 xmlFree(name);
2622 }
2623 name = xmlGetProp(svg,(xmlChar *) "fill");
2624 if ( name!=NULL ) {
2625 st.dofill = xmlParseColor(name,&st.fillcol,&fill_colour_source,&st);
2626 xmlFree(name);
2627 }
2628 name = xmlGetProp(svg,(xmlChar *) "fill-opacity");
2629 if ( name!=NULL ) {
2630 st.fillopacity = strtod((char *)name,NULL);
2631 xmlFree(name);
2632 }
2633 name = xmlGetProp(svg,(xmlChar *) "stroke");
2634 if ( name!=NULL ) {
2635 st.dostroke = xmlParseColor(name,&st.strokecol,&stroke_colour_source,&st);
2636 xmlFree(name);
2637 }
2638 name = xmlGetProp(svg,(xmlChar *) "stroke-opacity");
2639 if ( name!=NULL ) {
2640 st.strokeopacity = strtod((char *)name,NULL);
2641 xmlFree(name);
2642 }
2643 name = xmlGetProp(svg,(xmlChar *) "stroke-width");
2644 if ( name!=NULL ) {
2645 st.linewidth = strtod((char *)name,NULL);
2646 xmlFree(name);
2647 }
2648 name = xmlGetProp(svg,(xmlChar *) "stroke-linecap");
2649 if ( name!=NULL ) {
2650 st.lc = xmlStrcmp(name,(xmlChar *) "butt")==0 ? lc_butt :
2651 xmlStrcmp(name,(xmlChar *) "round")==0 ? lc_round :
2652 lc_square;
2653 xmlFree(name);
2654 }
2655 name = xmlGetProp(svg,(xmlChar *) "stroke-linejoin");
2656 if ( name!=NULL ) {
2657 st.lj = xmlStrcmp(name,(xmlChar *) "miter-clip")==0 ? lj_miterclip :
2658 xmlStrcmp(name,(xmlChar *) "miter")==0 ? lj_miter :
2659 xmlStrcmp(name,(xmlChar *) "round")==0 ? lj_round :
2660 xmlStrcmp(name,(xmlChar *) "arcs")==0 ? lj_arcs :
2661 lj_bevel;
2662 xmlFree(name);
2663 }
2664 name = xmlGetProp(svg,(xmlChar *) "stroke-miterlimit");
2665 if ( name!=NULL ) {
2666 st.miterlimit = strtod((char *)name,NULL);
2667 xmlFree(name);
2668 }
2669 name = xmlGetProp(svg,(xmlChar *) "stroke-dasharray");
2670 if ( name!=NULL ) {
2671 if ( xmlStrcmp(name,(xmlChar *) "inherit")==0 ) {
2672 st.dashes[0] = 0; st.dashes[1] = DASH_INHERITED;
2673 } else if ( xmlStrcmp(name,(xmlChar *) "none")==0 ) {
2674 st.dashes[0] = 0; st.dashes[1] = 0;
2675 } else {
2676 int i;
2677 xmlChar *pt, *end;
2678 pt = name;
2679 for ( i=0; i<DASH_MAX && *pt!='\0'; ++i ) {
2680 st.dashes[i] = strtol((char *) pt,(char **) &end,10);
2681 pt = end;
2682 }
2683 if ( i<DASH_MAX ) st.dashes[i] = 0;
2684 }
2685 xmlFree(name);
2686 }
2687 name = xmlGetProp(svg,(xmlChar *) "style");
2688 if ( name!=NULL ) {
2689 SVGFigureStyle(&st,(char *) name, &fill_colour_source, &stroke_colour_source);
2690 xmlFree(name);
2691 }
2692 name = xmlGetProp(svg,(xmlChar *) "transform");
2693 if ( name!=NULL ) {
2694 SVGFigureTransform(&st,(char *) name);
2695 xmlFree(name);
2696 }
2697 name = xmlGetProp(svg,(xmlChar *) "clip-path");
2698 if ( name!=NULL ) {
2699 xmlNodePtr clip = XmlFindURI(top,(char *) name);
2700 if ( clip!=NULL && xmlStrcmp(clip->name,(xmlChar *) "clipPath")==0) {
2701 const xmlChar *temp = clip->name;
2702 struct svg_state null_state;
2703 memset(&null_state,0,sizeof(null_state));
2704 null_state.isvisible = true;
2705 null_state.transform[0] = null_state.transform[3] = 1;
2706 clip->name = (xmlChar *) "g";
2707 eret = _SVGParseSVG(clip,top,&null_state);
2708 clip->name = temp;
2709 if ( eret!=NULL && eret->type == et_splines ) {
2710 st.clippath = eret->u.splines.splines;
2711 eret->u.splines.splines = NULL;
2712 }
2713 free(eret);
2714 } else
2715 LogError(_("Could not find clippath named %s."), name );
2716 xmlFree(name);
2717 }
2718
2719 if ( (treat_symbol_as_g && xmlStrcmp(svg->name,(xmlChar *) "symbol")==0) ||
2720 xmlStrcmp(svg->name,(xmlChar *) "svg")==0 ||
2721 xmlStrcmp(svg->name,(xmlChar *) "glyph")==0 ||
2722 xmlStrcmp(svg->name,(xmlChar *) "pattern")==0 ||
2723 xmlStrcmp(svg->name,(xmlChar *) "g")==0 ) {
2724 ehead = elast = NULL;
2725 for ( kid = svg->children; kid!=NULL; kid=kid->next ) {
2726 eret = _SVGParseSVG(kid,top,&st);
2727 if ( eret!=NULL ) {
2728 if ( elast==NULL )
2729 ehead = eret;
2730 else
2731 elast->next = eret;
2732 while ( eret->next!=NULL ) eret = eret->next;
2733 elast = eret;
2734 }
2735 }
2736 SvgStateFree(&st);
2737 if ( fill_colour_source!=NULL || stroke_colour_source!=NULL )
2738 xmlApplyColourSources(top,ehead,&st,fill_colour_source,stroke_colour_source);
2739 return( ehead );
2740 } else if ( xmlStrcmp(svg->name,(xmlChar *) "use")==0 ) {
2741 name = xmlGetProp(svg,(xmlChar *) "href");
2742 kid = NULL;
2743 if ( name!=NULL && *name=='#' ) { /* Within this file */
2744 kid = XmlFindID(top,(char *) name+1);
2745 treat_symbol_as_g = true;
2746 }
2747 SVGuseTransform(&st,svg,kid);
2748 svg = kid;
2749 if ( name!=NULL )
2750 xmlFree(name);
2751 if ( svg!=NULL )
2752 goto tail_recurse;
2753 SvgStateFree(&st);
2754 return( NULL );
2755 }
2756
2757 if ( !st.isvisible ) {
2758 SvgStateFree(&st);
2759 return( NULL );
2760 }
2761
2762 /* basic shapes */
2763 head = NULL;
2764 if ( xmlStrcmp(svg->name,(xmlChar *) "path")==0 ) {
2765 head = SVGParseExtendedPath(svg,top);
2766 } else if ( xmlStrcmp(svg->name,(xmlChar *) "rect")==0 ) {
2767 head = SVGParseRect(svg); /* x,y,width,height,rx,ry */
2768 } else if ( xmlStrcmp(svg->name,(xmlChar *) "circle")==0 ) {
2769 head = SVGParseEllipse(svg,true); /* cx,cy, r */
2770 } else if ( xmlStrcmp(svg->name,(xmlChar *) "ellipse")==0 ) {
2771 head = SVGParseEllipse(svg,false); /* cx,cy, rx,ry */
2772 } else if ( xmlStrcmp(svg->name,(xmlChar *) "line")==0 ) {
2773 head = SVGParseLine(svg); /* x1,y1, x2,y2 */
2774 } else if ( xmlStrcmp(svg->name,(xmlChar *) "polyline")==0 ) {
2775 head = SVGParsePoly(svg,0); /* points */
2776 } else if ( xmlStrcmp(svg->name,(xmlChar *) "polygon")==0 ) {
2777 head = SVGParsePoly(svg,1); /* points */
2778 } else if ( xmlStrcmp(svg->name,(xmlChar *) "image")==0 ) {
2779 eret = SVGParseImage(svg);
2780 if (eret!=NULL)
2781 eret->clippath = st.clippath;
2782 return( eret );
2783 } else
2784 return( NULL );
2785 if ( head==NULL )
2786 return( NULL );
2787
2788 SPLCategorizePoints(head);
2789
2790 eret = EntityCreate(SplinePointListTransform(head,st.transform,tpt_AllPoints), &st);
2791 if ( fill_colour_source!=NULL || stroke_colour_source!=NULL )
2792 xmlApplyColourSources(top,eret,&st,fill_colour_source,stroke_colour_source);
2793 SvgStateFree(&st);
2794 return( eret );
2795 }
2796
SVGParseSVG(xmlNodePtr svg,int em_size,int ascent,bool scale)2797 static Entity *SVGParseSVG(xmlNodePtr svg,int em_size,int ascent,bool scale) {
2798 struct svg_state st;
2799 char *num, *end;
2800 double swidth,sheight,width=1,height=1;
2801
2802 memset(&st,0,sizeof(st));
2803 st.lc = lc_inherited;
2804 st.lj = lj_inherited;
2805 st.linewidth = WIDTH_INHERITED;
2806 st.miterlimit = JLIMIT_INHERITED;
2807 st.fillcol = COLOR_INHERITED;
2808 st.strokecol = COLOR_INHERITED;
2809 st.currentColor = COLOR_INHERITED;
2810 st.stopColor = COLOR_INHERITED;
2811 st.isvisible = true;
2812 st.transform[0] = 1;
2813 st.transform[3] = -1; /* The SVG coord system has y increasing down */
2814 /* Font coords have y increasing up */
2815 st.transform[5] = ascent;
2816 st.strokeopacity = st.fillopacity = 1.0;
2817
2818 num = (char *) xmlGetProp(svg,(xmlChar *) "width");
2819 if ( num!=NULL ) {
2820 width = strtod(num,NULL);
2821 xmlFree(num);
2822 }
2823 num = (char *) xmlGetProp(svg,(xmlChar *) "height");
2824 if ( num!=NULL ) {
2825 height = strtod(num,NULL);
2826 xmlFree(num);
2827 }
2828 if ( height<=0 ) height = 1;
2829 if ( width<=0 ) width = 1;
2830 num = (char *) xmlGetProp(svg,(xmlChar *) "viewBox");
2831 if ( num!=NULL ) {
2832 /* x = */strtod((char *) num,&end);
2833 /* y = */strtod((char *) end+1,&end);
2834 swidth = strtod((char *) end+1,&end);
2835 sheight = strtod((char *) end+1,&end);
2836 xmlFree(num);
2837 if ( width>height ) {
2838 if ( scale && swidth!=0 ) {
2839 st.transform[0] *= em_size/swidth;
2840 st.transform[3] *= em_size/swidth;
2841 }
2842 } else {
2843 if ( scale && sheight!=0 ) {
2844 st.transform[0] *= em_size/sheight;
2845 st.transform[3] *= em_size/sheight;
2846 }
2847 }
2848 }
2849 return( _SVGParseSVG(svg,svg,&st));
2850 }
2851
SVGParseGlyphBody(SplineChar * sc,xmlNodePtr glyph,ImportParams * ip)2852 static void SVGParseGlyphBody(SplineChar *sc, xmlNodePtr glyph,
2853 ImportParams *ip) {
2854 xmlChar *path;
2855
2856 path = xmlGetProp(glyph,(xmlChar *) "d");
2857 if ( path!=NULL ) {
2858 sc->layers[ly_fore].splines = SVGParseExtendedPath(glyph,glyph);
2859 xmlFree(path);
2860 } else {
2861 Entity *ent = SVGParseSVG(glyph,sc->parent->ascent+sc->parent->descent,
2862 sc->parent->ascent,ip->scale);
2863 sc->layer_cnt = 1;
2864 SCAppendEntityLayers(sc,ent,ip);
2865 if ( sc->layer_cnt==1 ) ++sc->layer_cnt;
2866 else sc->parent->multilayer = true;
2867 }
2868
2869 SCCategorizePoints(sc);
2870 }
2871
SVGParseGlyphArgs(xmlNodePtr glyph,int defh,int defv,SplineFont * sf)2872 static SplineChar *SVGParseGlyphArgs(xmlNodePtr glyph,int defh, int defv,
2873 SplineFont *sf) {
2874 SplineChar *sc = SFSplineCharCreate(sf);
2875 xmlChar *name, *form, *glyphname, *unicode, *orientation;
2876 uint32 *u;
2877 char buffer[100];
2878
2879 name = xmlGetProp(glyph,(xmlChar *) "horiz-adv-x");
2880 if ( name!=NULL ) {
2881 sc->width = strtod((char *) name,NULL);
2882 sc->widthset = true;
2883 xmlFree(name);
2884 } else
2885 sc->width = defh;
2886 name = xmlGetProp(glyph,(xmlChar *) "vert-adv-y");
2887 if ( name!=NULL ) {
2888 sc->vwidth = strtod((char *) name,NULL);
2889 xmlFree(name);
2890 } else
2891 sc->vwidth = defv;
2892 name = xmlGetProp(glyph,(xmlChar *) "vert-adv-y");
2893 if ( name!=NULL ) {
2894 sc->vwidth = strtod((char *) name,NULL);
2895 xmlFree(name);
2896 } else
2897 sc->vwidth = defv;
2898
2899 form = xmlGetProp(glyph,(xmlChar *) "arabic-form");
2900 unicode = xmlGetProp(glyph,(xmlChar *) "unicode");
2901 glyphname = xmlGetProp(glyph,(xmlChar *) "glyph-name");
2902 orientation = xmlGetProp(glyph,(xmlChar *) "orientation");
2903 if ( unicode!=NULL ) {
2904
2905 u = utf82u_copy((char *) unicode);
2906 xmlFree(unicode);
2907 if ( u[1]=='\0' ) {
2908 sc->unicodeenc = u[0];
2909 if ( form!=NULL && u[0]>=0x600 && u[0]<=0x6ff ) {
2910 if ( xmlStrcmp(form,(xmlChar *) "initial")==0 )
2911 sc->unicodeenc = ArabicForms[u[0]-0x600].initial;
2912 else if ( xmlStrcmp(form,(xmlChar *) "medial")==0 )
2913 sc->unicodeenc = ArabicForms[u[0]-0x600].medial;
2914 else if ( xmlStrcmp(form,(xmlChar *) "final")==0 )
2915 sc->unicodeenc = ArabicForms[u[0]-0x600].final;
2916 else if ( xmlStrcmp(form,(xmlChar *) "isolated")==0 )
2917 sc->unicodeenc = ArabicForms[u[0]-0x600].isolated;
2918 }
2919 }
2920 free(u);
2921 }
2922 if ( glyphname!=NULL ) {
2923 if ( sc->unicodeenc==-1 )
2924 sc->unicodeenc = UniFromName((char *) glyphname,ui_none,&custom);
2925 sc->name = copy((char *) glyphname);
2926 xmlFree(glyphname);
2927 } else if ( orientation!=NULL && *orientation=='v' && sc->unicodeenc!=-1 ) {
2928 if ( sc->unicodeenc<0x10000 )
2929 sprintf( buffer, "uni%04X.vert", sc->unicodeenc );
2930 else
2931 sprintf( buffer, "u%04X.vert", sc->unicodeenc );
2932 sc->name = copy( buffer );
2933 }
2934 /* we finish off defaulting the glyph name in the parseglyph routine */
2935 if ( form!=NULL )
2936 xmlFree(form);
2937 if ( orientation!=NULL )
2938 xmlFree(orientation);
2939 return( sc );
2940 }
2941
SVGParseMissing(SplineFont * sf,xmlNodePtr notdef,int defh,int defv,int enc,ImportParams * ip)2942 static SplineChar *SVGParseMissing(SplineFont *sf,xmlNodePtr notdef,int defh,
2943 int defv, int enc, ImportParams *ip) {
2944 SplineChar *sc = SVGParseGlyphArgs(notdef,defh,defv,sf);
2945 sc->parent = sf;
2946 sc->name = copy(".notdef");
2947 sc->unicodeenc = 0;
2948 SVGParseGlyphBody(sc,notdef,ip);
2949 return( sc );
2950 }
2951
SVGParseGlyph(SplineFont * sf,xmlNodePtr glyph,int defh,int defv,int enc,ImportParams * ip)2952 static SplineChar *SVGParseGlyph(SplineFont *sf,xmlNodePtr glyph,int defh,
2953 int defv, int enc, ImportParams *ip) {
2954 char buffer[400];
2955 SplineChar *sc = SVGParseGlyphArgs(glyph,defh,defv,sf);
2956 sc->parent = sf;
2957 if ( sc->name==NULL ) {
2958 if ( sc->unicodeenc==-1 ) {
2959 sprintf( buffer, "glyph%d", enc);
2960 sc->name = copy(buffer);
2961 } else
2962 sc->name = copy(StdGlyphName(buffer,sc->unicodeenc,ui_none,NULL));
2963 }
2964 SVGParseGlyphBody(sc,glyph,ip);
2965 return( sc );
2966 }
2967
SVGLigatureFixupCheck(SplineChar * sc,xmlNodePtr glyph)2968 static void SVGLigatureFixupCheck(SplineChar *sc,xmlNodePtr glyph) {
2969 xmlChar *unicode;
2970 uint32 *u;
2971 int len, len2;
2972 SplineChar **chars, *any = NULL;
2973 char *comp, *pt;
2974
2975 unicode = xmlGetProp(glyph,(xmlChar *) "unicode");
2976 if ( unicode!=NULL ) {
2977 u = utf82u_copy((char *) unicode);
2978 xmlFree(unicode);
2979 if ( u[1]!='\0' && u[2]=='\0' &&
2980 ((u[1]>=0x180B && u[1]<=0x180D) || /* Mongolian VS */
2981 (u[1]>=0xfe00 && u[1]<=0xfe0f) || /* First VS block */
2982 (u[1]>=0xE0100 && u[1]<=0xE01EF)) ) { /* Second VS block */
2983 /* Problably a variant glyph marked with a variation selector */
2984 /* ... not a true ligature at all */
2985 /* http://babelstone.blogspot.com/2007/06/secret-life-of-variation-selectors.html */
2986 struct altuni *altuni = chunkalloc(sizeof(struct altuni));
2987 altuni->unienc = u[0];
2988 altuni->vs = u[1];
2989 altuni->fid = 0;
2990 altuni->next = sc->altuni;
2991 sc->altuni = altuni;
2992 } else if ( u[1]!='\0' ) {
2993 /* Normal ligature */
2994 for ( len=0; u[len]!=0; ++len );
2995 chars = malloc(len*sizeof(SplineChar *));
2996 for ( len=len2=0; u[len]!=0; ++len ) {
2997 chars[len] = SFGetChar(sc->parent,u[len],NULL);
2998 if ( chars[len]==NULL )
2999 len2 += 9;
3000 else {
3001 len2 += strlen(chars[len]->name)+1;
3002 if ( any==NULL ) any = chars[len];
3003 }
3004 }
3005 if ( any==NULL ) any=sc;
3006 comp = pt = malloc(len2+1);
3007 *pt = '\0';
3008 for ( len=0; u[len]!=0; ++len ) {
3009 if ( chars[len]!=NULL )
3010 strcpy(pt,chars[len]->name);
3011 else if ( u[len]<0x10000 )
3012 sprintf(pt,"uni%04X", (unsigned int) u[len]);
3013 else
3014 sprintf(pt,"u%04X", (unsigned int) u[len]);
3015 pt += strlen(pt);
3016 if ( u[len+1]!='\0' )
3017 *pt++ = ' ';
3018 }
3019 SubsNew(sc,pst_ligature,CHR('l','i','g','a'),comp,any);
3020 /* Understand the unicode latin ligatures. There are too many */
3021 /* arabic ones */
3022 if ( u[0]=='f' ) {
3023 if ( u[1]=='f' && u[2]==0 )
3024 sc->unicodeenc = 0xfb00;
3025 else if ( u[1]=='i' && u[2]==0 )
3026 sc->unicodeenc = 0xfb01;
3027 else if ( u[1]=='l' && u[2]==0 )
3028 sc->unicodeenc = 0xfb02;
3029 else if ( u[1]=='f' && u[2]=='i' && u[3]==0 )
3030 sc->unicodeenc = 0xfb03;
3031 else if ( u[1]=='f' && u[2]=='l' && u[3]==0 )
3032 sc->unicodeenc = 0xfb04;
3033 else if ( u[1]=='t' && u[2]==0 )
3034 sc->unicodeenc = 0xfb05;
3035 } else if ( u[0]=='s' && u[1]=='t' && u[2]==0 )
3036 sc->unicodeenc = 0xfb06;
3037 if ( strncmp(sc->name,"glyph",5)==0 && isdigit(sc->name[5])) {
3038 /* It's a default name, we can do better */
3039 free(sc->name);
3040 sc->name = copy(comp);
3041 for ( pt = sc->name; *pt; ++pt )
3042 if ( *pt==' ' ) *pt = '_';
3043 }
3044 }
3045 free(u);
3046 }
3047 }
3048
SVGGetNames(SplineFont * sf,xmlChar * g,xmlChar * utf8,SplineChar ** sc)3049 static char *SVGGetNames(SplineFont *sf,xmlChar *g,xmlChar *utf8,SplineChar **sc) {
3050 uint32 *u=NULL;
3051 char *names;
3052 int len, i, ch;
3053 SplineChar *temp;
3054 char *pt, *gpt;
3055
3056 *sc = NULL;
3057 len = 0;
3058 if ( utf8!=NULL ) {
3059 u = utf82u_copy((char *) utf8);
3060 for ( i=0; u[i]!=0; ++i ) {
3061 temp = SFGetChar(sf,u[i],NULL);
3062 if ( temp!=NULL ) {
3063 if ( *sc==NULL ) *sc = temp;
3064 len += strlen(temp->name)+1;
3065 }
3066 }
3067 }
3068 names = pt = malloc(len+(g!=NULL?strlen((char *)g):0)+1);
3069 if ( utf8!=NULL ) {
3070 for ( i=0; u[i]!=0; ++i ) {
3071 temp = SFGetChar(sf,u[i],NULL);
3072 if ( temp!=NULL ) {
3073 strcpy(pt,temp->name);
3074 pt += strlen( pt );
3075 *pt++ = ' ';
3076 }
3077 }
3078 free(u);
3079 }
3080 if ( g!=NULL ) {
3081 for ( gpt=(char *) g; *gpt; ) {
3082 if ( *gpt==',' || isspace(*gpt)) {
3083 while ( *gpt==',' || isspace(*gpt)) ++gpt;
3084 *pt++ = ' ';
3085 } else {
3086 *pt++ = *gpt++;
3087 }
3088 }
3089 if ( *sc==NULL ) {
3090 for ( gpt = (char *) g; *gpt!='\0' && *gpt!=',' && !isspace(*gpt); ++gpt );
3091 ch = *gpt; *gpt = '\0';
3092 *sc = SFGetChar(sf,-1,(char *) g);
3093 *gpt = ch;
3094 }
3095 }
3096 if ( pt>names && pt[-1]==' ' ) --pt;
3097 *pt = '\0';
3098 return( names );
3099 }
3100
SVGParseKern(SplineFont * sf,xmlNodePtr kern,int isv)3101 static void SVGParseKern(SplineFont *sf,xmlNodePtr kern,int isv) {
3102 xmlChar *k, *g1, *u1, *g2, *u2;
3103 double off;
3104 char *c1, *c2;
3105 char *pt1, *pt2, *end1, *end2;
3106 SplineChar *sc1, *sc2;
3107 uint32 script;
3108 struct lookup_subtable *subtable;
3109
3110 k = xmlGetProp(kern,(xmlChar *) "k");
3111 if ( k==NULL )
3112 return;
3113 off = -strtod((char *)k, NULL);
3114 xmlFree(k);
3115 if ( off==0 )
3116 return;
3117
3118 g1 = xmlGetProp(kern,(xmlChar *) "g1");
3119 u1 = xmlGetProp(kern,(xmlChar *) "u1");
3120 if ( g1==NULL && u1==NULL )
3121 return;
3122 c1 = SVGGetNames(sf,g1,u1,&sc1);
3123 if ( g1!=NULL ) xmlFree(g1);
3124 if ( u1!=NULL ) xmlFree(u1);
3125
3126 g2 = xmlGetProp(kern,(xmlChar *) "g2");
3127 u2 = xmlGetProp(kern,(xmlChar *) "u2");
3128 if ( g2==NULL && u2==NULL ) {
3129 free(c1);
3130 return;
3131 }
3132 c2 = SVGGetNames(sf,g2,u2,&sc2);
3133 if ( g2!=NULL ) xmlFree(g2);
3134 if ( u2!=NULL ) xmlFree(u2);
3135
3136 script = DEFAULT_SCRIPT;
3137 if ( sc1!=NULL )
3138 script = SCScriptFromUnicode(sc1);
3139 if ( script==DEFAULT_SCRIPT && sc2!=NULL )
3140 script = SCScriptFromUnicode(sc2);
3141 /* It is tempting to use a kern class... but it doesn't work. No guarantees */
3142 /* that either of our two "classes" will ever show again. We could do the */
3143 /* complex shinanigans we do in parsing feature files, but it's a lot easier */
3144 /* just to make kernpairs. */
3145 subtable = SFSubTableFindOrMake(sf,
3146 isv?CHR('v','k','r','n'):CHR('k','e','r','n'),
3147 script, gpos_pair);
3148 subtable->lookup->lookup_flags = ((sc1!=NULL && SCRightToLeft(sc1)) ||
3149 (sc1==NULL && sc2!=NULL && SCRightToLeft(sc2)))? pst_r2l : 0;
3150 for ( pt1=c1 ; ; ) {
3151 while ( *pt1==' ' ) ++pt1;
3152 if ( *pt1=='\0' )
3153 break;
3154 end1 = strchr(pt1,' ');
3155 if ( end1!=NULL ) *end1 = '\0';
3156 sc1 = SFGetChar(sf,-1,pt1);
3157 if ( sc1!=NULL ) for ( pt2=c2 ; ; ) {
3158 while ( *pt2==' ' ) ++pt2;
3159 if ( *pt2=='\0' )
3160 break;
3161 end2 = strchr(pt2,' ');
3162 if ( end2!=NULL ) *end2 = '\0';
3163 sc2 = SFGetChar(sf,-1,pt2);
3164 if ( sc2!=NULL ) {
3165 KernPair *kp = chunkalloc(sizeof(KernPair));
3166 kp->sc = sc2;
3167 kp->off = off;
3168 if ( isv ) {
3169 kp->next = sc1->vkerns;
3170 sc1->vkerns = kp;
3171 } else {
3172 kp->next = sc1->kerns;
3173 sc1->kerns = kp;
3174 }
3175 kp->subtable = subtable;
3176 }
3177 if ( end2==NULL )
3178 break;
3179 *end2 = ' ';
3180 pt2 = end2+1;
3181 }
3182 if ( end1==NULL )
3183 break;
3184 *end1 = ' ';
3185 pt1 = end1+1;
3186 }
3187 free(c1); free(c2);
3188 }
3189
SVGParseFont(xmlNodePtr font)3190 static SplineFont *SVGParseFont(xmlNodePtr font) {
3191 int cnt;
3192 xmlNodePtr kids;
3193 int defh=0, defv=0;
3194 int has_font_face = false;
3195 xmlChar *name;
3196 SplineFont *sf;
3197 EncMap *map;
3198 ImportParams *ip = ImportParamsState();
3199 bigreal tmpjl = ip->default_joinlimit;
3200 int i;
3201
3202 if ( tmpjl==JLIMIT_INHERITED )
3203 ip->default_joinlimit = 4.0; // SVG default
3204
3205 sf = SplineFontEmpty();
3206 name = xmlGetProp(font,(xmlChar *) "horiz-adv-x");
3207 if ( name!=NULL ) {
3208 defh = strtod((char *) name,NULL);
3209 xmlFree(name);
3210 }
3211 name = xmlGetProp(font,(xmlChar *) "vert-adv-y");
3212 if ( name!=NULL ) {
3213 defv = strtod((char *) name,NULL);
3214 xmlFree(name);
3215 sf->hasvmetrics = true;
3216 }
3217 name = xmlGetProp(font,(xmlChar *) "id");
3218 if ( name!=NULL ) {
3219 sf->fontname = copy( (char *) name);
3220 xmlFree(name);
3221 }
3222
3223 cnt = 0;
3224 for ( kids = font->children; kids!=NULL; kids=kids->next ) {
3225 int ascent=0, descent=0;
3226 if ( xmlStrcmp(kids->name,(const xmlChar *) "font-face")==0 ) {
3227 has_font_face = true;
3228 name = xmlGetProp(kids,(xmlChar *) "units-per-em");
3229 if ( name!=NULL ) {
3230 int val = rint(strtod((char *) name,NULL));
3231 xmlFree(name);
3232 if ( val<0 ) val = 0;
3233 sf->ascent = val*800/1000;
3234 sf->descent = val - sf->ascent;
3235 if ( defv==0 ) defv = val;
3236 if ( defh==0 ) defh = val;
3237 SFDefaultOS2Simple(&sf->pfminfo,sf);
3238 } else {
3239 LogError( _("This font does not specify units-per-em\n") );
3240 SplineFontFree(sf);
3241 ip->default_joinlimit = tmpjl;
3242 return( NULL );
3243 }
3244 name = xmlGetProp(kids,(xmlChar *) "font-family");
3245 if ( name!=NULL ) {
3246 if ( strchr((char *) name,',')!=NULL )
3247 *strchr((char *) name,',') ='\0';
3248 sf->familyname = copy( (char *) name);
3249 xmlFree(name);
3250 }
3251 name = xmlGetProp(kids,(xmlChar *) "font-weight");
3252 if ( name!=NULL ) {
3253 if ( strnmatch((char *) name,"normal",6)==0 ) {
3254 sf->pfminfo.weight = 400;
3255 sf->weight = copy("Regular");
3256 sf->pfminfo.panose[2] = 5;
3257 } else if ( strnmatch((char *) name,"bold",4)==0 ) {
3258 sf->pfminfo.weight = 700;
3259 sf->weight = copy("Bold");
3260 sf->pfminfo.panose[2] = 8;
3261 } else {
3262 sf->pfminfo.weight = strtod((char *) name,NULL);
3263 if ( sf->pfminfo.weight <= 100 ) {
3264 sf->weight = copy("Thin");
3265 sf->pfminfo.panose[2] = 2;
3266 } else if ( sf->pfminfo.weight <= 200 ) {
3267 sf->weight = copy("Extra-Light");
3268 sf->pfminfo.panose[2] = 3;
3269 } else if ( sf->pfminfo.weight <= 300 ) {
3270 sf->weight = copy("Light");
3271 sf->pfminfo.panose[2] = 4;
3272 } else if ( sf->pfminfo.weight <= 400 ) {
3273 sf->weight = copy("Regular");
3274 sf->pfminfo.panose[2] = 5;
3275 } else if ( sf->pfminfo.weight <= 500 ) {
3276 sf->weight = copy("Medium");
3277 sf->pfminfo.panose[2] = 6;
3278 } else if ( sf->pfminfo.weight <= 600 ) {
3279 sf->weight = copy("DemiBold");
3280 sf->pfminfo.panose[2] = 7;
3281 } else if ( sf->pfminfo.weight <= 700 ) {
3282 sf->weight = copy("Bold");
3283 sf->pfminfo.panose[2] = 8;
3284 } else if ( sf->pfminfo.weight <= 800 ) {
3285 sf->weight = copy("Heavy");
3286 sf->pfminfo.panose[2] = 9;
3287 } else {
3288 sf->weight = copy("Black");
3289 sf->pfminfo.panose[2] = 10;
3290 }
3291 }
3292 sf->pfminfo.panose_set = true;
3293 sf->pfminfo.pfmset = true;
3294 xmlFree(name);
3295 }
3296 name = xmlGetProp(kids,(xmlChar *) "font-stretch");
3297 if ( name!=NULL ) {
3298 if ( strnmatch((char *) name,"normal",6)==0 ) {
3299 sf->pfminfo.panose[3] = 3;
3300 sf->pfminfo.width = 5;
3301 } else if ( strmatch((char *) name,"ultra-condensed")==0 ) {
3302 sf->pfminfo.panose[3] = 8;
3303 sf->pfminfo.width = 1;
3304 } else if ( strmatch((char *) name,"extra-condensed")==0 ) {
3305 sf->pfminfo.panose[3] = 8;
3306 sf->pfminfo.width = 2;
3307 } else if ( strmatch((char *) name,"condensed")==0 ) {
3308 sf->pfminfo.panose[3] = 6;
3309 sf->pfminfo.width = 3;
3310 } else if ( strmatch((char *) name,"semi-condensed")==0 ) {
3311 sf->pfminfo.panose[3] = 6;
3312 sf->pfminfo.width = 4;
3313 } else if ( strmatch((char *) name,"ultra-expanded")==0 ) {
3314 sf->pfminfo.panose[3] = 7;
3315 sf->pfminfo.width = 9;
3316 } else if ( strmatch((char *) name,"extra-expanded")==0 ) {
3317 sf->pfminfo.panose[3] = 7;
3318 sf->pfminfo.width = 8;
3319 } else if ( strmatch((char *) name,"expanded")==0 ) {
3320 sf->pfminfo.panose[3] = 5;
3321 sf->pfminfo.width = 7;
3322 } else if ( strmatch((char *) name,"semi-expanded")==0 ) {
3323 sf->pfminfo.panose[3] = 5;
3324 sf->pfminfo.width = 6;
3325 }
3326 sf->pfminfo.panose_set = true;
3327 sf->pfminfo.pfmset = true;
3328 xmlFree(name);
3329 }
3330 name = xmlGetProp(kids,(xmlChar *) "panose-1");
3331 if ( name!=NULL ) {
3332 char *pt, *end;
3333 int i;
3334 for ( i=0, pt=(char *) name; i<10 && *pt; pt = end, ++i ) {
3335 sf->pfminfo.panose[i] = strtol(pt,&end,10);
3336 }
3337 sf->pfminfo.panose_set = true;
3338 xmlFree(name);
3339 }
3340 name = xmlGetProp(kids,(xmlChar *) "slope");
3341 if ( name!=NULL ) {
3342 sf->italicangle = strtod((char *) name,NULL);
3343 xmlFree(name);
3344 }
3345 name = xmlGetProp(kids,(xmlChar *) "underline-position");
3346 if ( name!=NULL ) {
3347 sf->upos = strtod((char *) name,NULL);
3348 xmlFree(name);
3349 }
3350 name = xmlGetProp(kids,(xmlChar *) "underline-thickness");
3351 if ( name!=NULL ) {
3352 sf->uwidth = strtod((char *) name,NULL);
3353 xmlFree(name);
3354 }
3355 name = xmlGetProp(kids,(xmlChar *) "ascent");
3356 if ( name!=NULL ) {
3357 ascent = strtod((char *) name,NULL);
3358 xmlFree(name);
3359 }
3360 name = xmlGetProp(kids,(xmlChar *) "descent");
3361 if ( name!=NULL ) {
3362 descent = strtod((char *) name,NULL);
3363 xmlFree(name);
3364 }
3365 if ( ascent-descent==sf->ascent+sf->descent ) {
3366 sf->ascent = ascent;
3367 sf->descent = -descent;
3368 }
3369 sf->pfminfo.pfmset = true;
3370 } else if ( xmlStrcmp(kids->name,(const xmlChar *) "glyph")==0 ||
3371 xmlStrcmp(kids->name,(const xmlChar *) "missing-glyph")==0 )
3372 ++cnt;
3373 }
3374 if ( !has_font_face ) {
3375 LogError( _("This font does not specify font-face\n") );
3376 SplineFontFree(sf);
3377 ip->default_joinlimit = tmpjl;
3378 return( NULL );
3379 }
3380 if ( sf->weight==NULL )
3381 sf->weight = copy("Regular");
3382 if ( sf->fontname==NULL && sf->familyname==NULL )
3383 sf->fontname = GetNextUntitledName();
3384 if ( sf->familyname==NULL )
3385 sf->familyname = copy(sf->fontname);
3386 if ( sf->fontname==NULL )
3387 sf->fontname = EnforcePostScriptName(sf->familyname);
3388 sf->fullname = copy(sf->fontname);
3389
3390 /* Give ourselves an xuid, just in case they want to convert to PostScript*/
3391 if ( xuid!=NULL ) {
3392 sf->xuid = malloc(strlen(xuid)+20);
3393 sprintf(sf->xuid,"[%s %d]", xuid, (rand()&0xffffff));
3394 }
3395
3396 ff_progress_change_total(cnt);
3397 sf->glyphcnt = sf->glyphmax = cnt;
3398 sf->glyphs = malloc(cnt*sizeof(SplineChar *));
3399
3400 cnt = 0;
3401 for ( kids = font->children; kids!=NULL; kids=kids->next ) {
3402 if ( xmlStrcmp(kids->name,(const xmlChar *) "missing-glyph")==0 ) {
3403 sf->glyphs[cnt] = SVGParseMissing(sf,kids,defh,defv,cnt,ip);
3404 if ( sf->glyphs[cnt]!=NULL ) {
3405 sf->glyphs[cnt]->orig_pos = cnt;
3406 cnt++;
3407 }
3408 ff_progress_next();
3409 } else if ( xmlStrcmp(kids->name,(const xmlChar *) "glyph")==0 ) {
3410 sf->glyphs[cnt] = SVGParseGlyph(sf,kids,defh,defv,cnt,ip);
3411 if ( sf->glyphs[cnt]!=NULL ) {
3412 sf->glyphs[cnt]->orig_pos = cnt;
3413 cnt++;
3414 }
3415 ff_progress_next();
3416 }
3417 }
3418 cnt = 0;
3419 for ( kids = font->children; kids!=NULL; kids=kids->next ) {
3420 if ( xmlStrcmp(kids->name,(const xmlChar *) "hkern")==0 ) {
3421 SVGParseKern(sf,kids,false);
3422 } else if ( xmlStrcmp(kids->name,(const xmlChar *) "vkern")==0 ) {
3423 SVGParseKern(sf,kids,true);
3424 } else if ( xmlStrcmp(kids->name,(const xmlChar *) "glyph")==0 ) {
3425 SVGLigatureFixupCheck(sf->glyphs[cnt++],kids);
3426 } else if ( xmlStrcmp(kids->name,(const xmlChar *) "missing-glyph")==0 ) {
3427 ++cnt;
3428 }
3429 }
3430
3431 map = chunkalloc(sizeof(EncMap));
3432 map->enccount = map->encmax = map->backmax = sf->glyphcnt;
3433 map->enc = FindOrMakeEncoding("Original");
3434 map->map = malloc(sf->glyphcnt*sizeof(int));
3435 map->backmap = malloc(sf->glyphcnt*sizeof(int));
3436 for ( i=0; i<sf->glyphcnt; ++i )
3437 map->map[i] = map->backmap[i] = i;
3438 sf->map = map;
3439 ip->default_joinlimit = tmpjl;
3440 return( sf );
3441 }
3442
SPLFindOrder(SplineSet * ss)3443 static int SPLFindOrder(SplineSet *ss) {
3444 Spline *s, *first;
3445
3446 while ( ss!=NULL ) {
3447 first = NULL;
3448 for ( s = ss->first->next; s!=NULL && s!=first ; s = s->to->next ) {
3449 if ( first==NULL ) first = s;
3450 if ( !s->knownlinear )
3451 return( s->order2 );
3452 }
3453 ss = ss->next;
3454 }
3455 return( -1 );
3456 }
3457
EntFindOrder(Entity * ent)3458 static int EntFindOrder(Entity *ent) {
3459 int ret;
3460
3461 while ( ent!=NULL ) {
3462 if ( ent->type == et_splines ) {
3463 ret = SPLFindOrder(ent->u.splines.splines);
3464 if ( ret!=-1 )
3465 return( ret );
3466 }
3467 ent = ent->next;
3468 }
3469 return( -1 );
3470 }
3471
SFFindOrder(SplineFont * sf)3472 int SFFindOrder(SplineFont *sf) {
3473 int i, ret;
3474
3475 for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
3476 ret = SPLFindOrder(sf->glyphs[i]->layers[ly_fore].splines);
3477 if ( ret!=-1 )
3478 return( ret );
3479 }
3480 return( 0 );
3481 }
3482
SFLFindOrder(SplineFont * sf,int layerdest)3483 int SFLFindOrder(SplineFont *sf, int layerdest) {
3484 int i, ret;
3485
3486 for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
3487 if (layerdest >= sf->glyphs[i]->layer_cnt)
3488 continue;
3489 ret = SPLFindOrder(sf->glyphs[i]->layers[layerdest].splines);
3490 if ( ret!=-1 )
3491 return( ret );
3492 }
3493 return( 0 );
3494 }
3495
SPLSetOrder(SplineSet * ss,int order2)3496 static void SPLSetOrder(SplineSet *ss,int order2) {
3497 Spline *s, *first;
3498 SplinePoint *from, *to;
3499
3500 while ( ss!=NULL ) {
3501 first = NULL;
3502 for ( s = ss->first->next; s!=NULL && s!=first ; s = s->to->next ) {
3503 if ( first==NULL ) first = s;
3504 if ( s->order2!=order2 ) {
3505 if ( s->knownlinear ) {
3506 s->from->nextcp = s->from->me;
3507 s->to->prevcp = s->to->me;
3508 s->from->nonextcp = s->to->noprevcp = true;
3509 s->order2 = order2;
3510 } else if ( order2 ) {
3511 from = SplineTtfApprox(s);
3512 s->from->nextcp = from->nextcp;
3513 s->from->nonextcp = from->nonextcp;
3514 s->from->next = from->next;
3515 from->next->from = s->from;
3516 SplinePointFree(from);
3517 for ( to = s->from->next->to; to->next!=NULL; to=to->next->to );
3518 s->to->prevcp = to->prevcp;
3519 s->to->noprevcp = to->noprevcp;
3520 s->to->prev = to->prev;
3521 to->prev->to = s->to;
3522 SplinePointFree(to);
3523 to = s->to;
3524 from = s->from;
3525 SplineFree(s);
3526 if ( first==s ) first = from->next;
3527 s = to->prev;
3528 } else {
3529 s->from->nextcp.x = s->splines[0].c/3 + s->from->me.x;
3530 s->from->nextcp.y = s->splines[1].c/3 + s->from->me.y;
3531 s->to->prevcp.x = s->from->nextcp.x+ (s->splines[0].b+s->splines[0].c)/3;
3532 s->to->prevcp.y = s->from->nextcp.y+ (s->splines[1].b+s->splines[1].c)/3;
3533 s->order2 = false;
3534 SplineRefigure(s);
3535 }
3536 }
3537 }
3538 ss = ss->next;
3539 }
3540 }
3541
EntSetOrder(Entity * ent,int order2)3542 static void EntSetOrder(Entity *ent,int order2) {
3543 while ( ent!=NULL ) {
3544 if ( ent->type == et_splines )
3545 SPLSetOrder(ent->u.splines.splines,order2);
3546 SPLSetOrder(ent->clippath,order2);
3547 ent = ent->next;
3548 }
3549 }
3550
SFSetOrder(SplineFont * sf,int order2)3551 void SFSetOrder(SplineFont *sf,int order2) {
3552 int i,j;
3553
3554 for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
3555 for ( j=ly_fore; j<sf->glyphs[i]->layer_cnt; ++j ) {
3556 SPLSetOrder(sf->glyphs[i]->layers[j].splines,order2);
3557 sf->glyphs[i]->layers[j].order2 = order2;
3558 }
3559 }
3560 }
3561
SFLSetOrder(SplineFont * sf,int layerdest,int order2)3562 void SFLSetOrder(SplineFont *sf, int layerdest, int order2) {
3563 int i;
3564
3565 for ( i=0; i<sf->glyphcnt; ++i )
3566 if ( sf->glyphs[i]!=NULL && layerdest < sf->glyphs[i]->layer_cnt) {
3567 if (sf->glyphs[i]->layers[layerdest].splines != NULL)
3568 SPLSetOrder(sf->glyphs[i]->layers[layerdest].splines,order2);
3569 sf->glyphs[i]->layers[layerdest].order2 = order2;
3570 }
3571 }
3572
_SFReadSVG(xmlDocPtr doc,char * filename)3573 static SplineFont *_SFReadSVG(xmlDocPtr doc, char *filename) {
3574 xmlNodePtr *fonts, font;
3575 SplineFont *sf;
3576 char *chosenname = NULL;
3577
3578 fonts = FindSVGFontNodes(doc);
3579 if ( fonts==NULL || fonts[0]==NULL ) {
3580 LogError( _("This file contains no SVG fonts.\n") );
3581 xmlFreeDoc(doc);
3582 return( NULL );
3583 }
3584 font = fonts[0];
3585 if ( fonts[1]!=NULL ) {
3586 xmlChar *name;
3587 font = SVGPickFont(fonts,filename);
3588 name = xmlGetProp(font,(xmlChar *) "id");
3589 if ( name!=NULL ) {
3590 chosenname = cu_copy(utf82u_copy((char *) name));
3591 xmlFree(name);
3592 }
3593 }
3594 free(fonts);
3595 locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
3596 switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
3597 sf = SVGParseFont(font);
3598 switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
3599 xmlFreeDoc(doc);
3600
3601 if ( sf!=NULL ) {
3602 struct stat b;
3603 sf->layers[ly_fore].order2 = sf->layers[ly_back].order2 = sf->grid.order2 =
3604 SFFindOrder(sf);
3605 SFSetOrder(sf,sf->layers[ly_fore].order2);
3606 sf->chosenname = chosenname;
3607 if ( stat(filename,&b)!=-1 ) {
3608 sf->modificationtime = GetST_MTime(b);
3609 sf->creationtime = GetST_MTime(b);
3610 }
3611 }
3612 return( sf );
3613 }
3614
SFReadSVG(char * filename,int flags)3615 SplineFont *SFReadSVG(char *filename, int flags) {
3616 xmlDocPtr doc;
3617 char *temp=filename, *pt, *lparen;
3618
3619 pt = strrchr(filename,'/');
3620 if ( pt==NULL ) pt = filename;
3621 if ( (lparen=strchr(pt,'('))!=NULL && strchr(lparen,')')!=NULL ) {
3622 temp = copy(filename);
3623 pt = temp + (lparen-filename);
3624 *pt = '\0';
3625 }
3626
3627 doc = xmlParseFile(temp);
3628 if ( temp!=filename ) free(temp);
3629 if ( doc==NULL ) {
3630 /* Can I get an error message from libxml? */
3631 return( NULL );
3632 }
3633 return( _SFReadSVG(doc,filename));
3634 }
3635
SFReadSVGMem(char * data,int flags)3636 SplineFont *SFReadSVGMem(char *data, int flags) {
3637 xmlDocPtr doc;
3638
3639 doc = xmlParseMemory(data,strlen(data));
3640 if ( doc==NULL ) {
3641 /* Can I get an error message from libxml? */
3642 return( NULL );
3643 }
3644 return( _SFReadSVG(doc,NULL));
3645 }
3646
NamesReadSVG(char * filename)3647 char **NamesReadSVG(char *filename) {
3648 xmlNodePtr *fonts;
3649 xmlDocPtr doc;
3650 char **ret=NULL;
3651 int cnt;
3652 xmlChar *name;
3653
3654 doc = xmlParseFile(filename);
3655 if ( doc==NULL ) {
3656 /* Can I get an error message from libxml? */
3657 return( NULL );
3658 }
3659
3660 fonts = FindSVGFontNodes(doc);
3661 if ( fonts==NULL || fonts[0]==NULL ) {
3662 xmlFreeDoc(doc);
3663 return( NULL );
3664 }
3665
3666 for ( cnt=0; fonts[cnt]!=NULL; ++cnt);
3667 ret = malloc((cnt+1)*sizeof(char *));
3668 for ( cnt=0; fonts[cnt]!=NULL; ++cnt) {
3669 name = xmlGetProp(fonts[cnt],(xmlChar *) "id");
3670 if ( name==NULL ) {
3671 ret[cnt] = copy("nameless-font");
3672 } else {
3673 ret[cnt] = copy((char *) name);
3674 xmlFree(name);
3675 }
3676 }
3677 ret[cnt] = NULL;
3678
3679 free(fonts);
3680 xmlFreeDoc(doc);
3681
3682 return( ret );
3683 }
3684
EntityInterpretSVG(char * filename,char * memory,int memlen,int em_size,int ascent,bool scale)3685 Entity *EntityInterpretSVG(char *filename, char *memory, int memlen,
3686 int em_size, int ascent, bool scale) {
3687 xmlDocPtr doc;
3688 xmlNodePtr top;
3689 char oldloc[25];
3690 Entity *ret;
3691 int order2;
3692
3693 if ( filename!=NULL )
3694 doc = xmlParseFile(filename);
3695 else
3696 doc = xmlParseMemory(memory,memlen);
3697 if ( doc==NULL ) {
3698 /* Can I get an error message from libxml???? */
3699 return( NULL );
3700 }
3701
3702 top = xmlDocGetRootElement(doc);
3703 if ( xmlStrcmp(top->name,(xmlChar *) "svg")!=0 ) {
3704 LogError( _("%s does not contain an <svg> element at the top\n"), filename);
3705 xmlFreeDoc(doc);
3706 return( NULL );
3707 }
3708
3709 strncpy( oldloc,setlocale(LC_NUMERIC,NULL),24 );
3710 oldloc[24]=0;
3711 setlocale(LC_NUMERIC,"C");
3712 ret = SVGParseSVG(top,em_size,ascent,scale);
3713 setlocale(LC_NUMERIC,oldloc);
3714 xmlFreeDoc(doc);
3715
3716 if ( loaded_fonts_same_as_new )
3717 order2 = new_fonts_are_order2;
3718 else
3719 order2 = EntFindOrder(ret);
3720 if ( order2==-1 ) order2 = 0;
3721 EntSetOrder(ret,order2);
3722
3723 return( ret );
3724 }
3725
SplinePointListInterpretSVG(char * filename,char * memory,int memlen,int em_size,int ascent,int is_stroked,ImportParams * ip)3726 SplineSet *SplinePointListInterpretSVG(char *filename,char *memory, int memlen,int em_size,int ascent,int is_stroked, ImportParams *ip) {
3727 bigreal tmpjl = ip->default_joinlimit;
3728 if ( tmpjl==JLIMIT_INHERITED )
3729 ip->default_joinlimit = 4.0; // SVG default
3730 Entity *ret = EntityInterpretSVG(filename, memory, memlen, em_size, ascent,
3731 ip->scale);
3732 SplineSet *r = SplinesFromEntities(ret, ip, is_stroked);
3733 ip->default_joinlimit = tmpjl;
3734 return r;
3735 }
3736