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,&current,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