1 /* Copyright (C) 2000-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 "cvimages.h"
31 
32 #include "cvundoes.h"
33 #include "fontforgevw.h"
34 #include "fvfonts.h"
35 #include "parsepdf.h"
36 #include "psread.h"
37 #include "sd.h"
38 #include "spiro.h"
39 #include "splinefit.h"
40 #include "splineorder2.h"
41 #include "splineutil.h"
42 #include "splineutil2.h"
43 #include "svg.h"
44 #include "ustring.h"
45 #include "utype.h"
46 
47 #include <assert.h>
48 #include <dirent.h>
49 #include <math.h>
50 #include <sys/types.h>
51 
InitImportParams(ImportParams * ip)52 void InitImportParams(ImportParams *ip) {
53     assert( ip!=NULL );
54 
55     memset(ip, 0, sizeof(ImportParams));
56 
57     ip->initialized = true;
58 
59     ip->correct_direction = true;
60     ip->simplify = true;
61     ip->clip = true;
62     ip->scale = true;
63     ip->accuracy_target = 0.25;
64     ip->default_joinlimit = JLIMIT_INHERITED;
65 }
66 
ImportParamsState()67 ImportParams *ImportParamsState() {
68     static ImportParams ips;
69 
70     if ( !ips.initialized )
71 	InitImportParams(&ips);
72 
73     return &ips;
74 }
75 
SCAppendEntityLayers(SplineChar * sc,Entity * ent,ImportParams * ip)76 void SCAppendEntityLayers(SplineChar *sc, Entity *ent, ImportParams *ip) {
77     int cnt, pos;
78     Entity *e, *enext;
79     Layer *old = sc->layers;
80     SplineSet *ss;
81 
82     for ( e=ent, cnt=0; e!=NULL; e=e->next, ++cnt );
83     pos = sc->layer_cnt;
84     if ( cnt==0 )
85 return;
86     EntityDefaultStrokeFill(ent);
87 
88     sc->layers = realloc(sc->layers,(sc->layer_cnt+cnt)*sizeof(Layer));
89     for ( pos = sc->layer_cnt, e=ent; e!=NULL ; e=enext, ++pos ) {
90 	enext = e->next;
91 	LayerDefault(&sc->layers[pos]);
92 	sc->layers[pos].splines = NULL;
93 	sc->layers[pos].refs = NULL;
94 	sc->layers[pos].images = NULL;
95 	if ( e->type == et_splines ) {
96 	    sc->layers[pos].dofill = e->u.splines.fill.col != 0xffffffff;
97 	    sc->layers[pos].dostroke = e->u.splines.stroke.col != 0xffffffff;
98 	    if ( !sc->layers[pos].dofill && !sc->layers[pos].dostroke )
99 		sc->layers[pos].dofill = true;		/* If unspecified, assume an implied fill in BuildGlyph */
100 	    sc->layers[pos].fill_brush.col = e->u.splines.fill.col==0xffffffff ?
101 		    COLOR_INHERITED : e->u.splines.fill.col;
102 	    sc->layers[pos].fill_brush.gradient = e->u.splines.fill.grad;
103 	    /*!!!!!! pattern? */
104 	    sc->layers[pos].stroke_pen.brush.col = e->u.splines.stroke.col==0xffffffff ? COLOR_INHERITED : e->u.splines.stroke.col;
105 	    sc->layers[pos].stroke_pen.brush.gradient = e->u.splines.stroke.grad;
106 	    sc->layers[pos].stroke_pen.width = e->u.splines.stroke_width;
107 	    sc->layers[pos].stroke_pen.linejoin = e->u.splines.join;
108 	    sc->layers[pos].stroke_pen.linecap = e->u.splines.cap;
109 	    memcpy(sc->layers[pos].stroke_pen.trans, e->u.splines.transform,
110 		    4*sizeof(real));
111 	    sc->layers[pos].splines = e->u.splines.splines;
112 	} else if ( e->type == et_image ) {
113 	    ImageList *ilist = chunkalloc(sizeof(ImageList));
114 	    struct _GImage *base = e->u.image.image->list_len==0?
115 		    e->u.image.image->u.image:e->u.image.image->u.images[0];
116 	    sc->layers[pos].images = ilist;
117 	    sc->layers[pos].dofill = base->image_type==it_mono && base->trans!=(Color)-1;
118 	    sc->layers[pos].fill_brush.col = e->u.image.col==0xffffffff ?
119 		    COLOR_INHERITED : e->u.image.col;
120 	    ilist->image = e->u.image.image;
121 	    ilist->xscale = e->u.image.transform[0];
122 	    ilist->yscale = e->u.image.transform[3];
123 	    ilist->xoff = e->u.image.transform[4];
124 	    ilist->yoff = e->u.image.transform[5];
125 	    ilist->bb.minx = ilist->xoff;
126 	    ilist->bb.maxy = ilist->yoff;
127 	    ilist->bb.maxx = ilist->xoff + base->width*ilist->xscale;
128 	    ilist->bb.miny = ilist->yoff - base->height*ilist->yscale;
129 	}
130 	if ( e->clippath ) {
131 	    for ( ss=e->clippath; ss->next!=NULL; ss=ss->next )
132 		ss->is_clip_path = true;
133 	    ss->is_clip_path = true;
134 	    ss->next = sc->layers[pos].splines;
135 	    sc->layers[pos].splines = e->clippath;
136 	}
137 	free(e);
138     }
139     sc->layer_cnt += cnt;
140     SCMoreLayers(sc,old);
141 }
142 
SCImportPSFile(SplineChar * sc,int layer,FILE * ps,bool doclear,ImportParams * ip)143 void SCImportPSFile(SplineChar *sc,int layer,FILE *ps,bool doclear,
144                     ImportParams *ip) {
145     SplinePointList *spl, *espl;
146     SplineSet **head;
147     int empty, width;
148 
149     if ( ps==NULL )
150 return;
151     width = UNDEFINED_WIDTH;
152     empty = sc->layers[layer].splines==NULL && sc->layers[layer].refs==NULL;
153     if ( sc->parent->multilayer && layer>ly_back ) {
154 	SCAppendEntityLayers(sc, EntityInterpretPS(ps,&width), ip);
155     } else {
156 	spl = SplinePointListInterpretPS(ps,ip,sc->parent->strokedfont,&width);
157 	if ( spl==NULL ) {
158 	    ff_post_error( _("Too Complex or Bad"), _("I'm sorry this file is too complex for me to understand (or is erroneous, or is empty)") );
159 return;
160 	}
161 	if ( sc->layers[layer].order2 )
162 	    spl = SplineSetsConvertOrder(spl,true);
163 	for ( espl=spl; espl->next!=NULL; espl = espl->next );
164 	if ( layer==ly_grid )
165 	    head = &sc->parent->grid.splines;
166 	else {
167 	    SCPreserveLayer(sc,layer,false);
168 	    head = &sc->layers[layer].splines;
169 	}
170 	if ( doclear ) {
171 	    SplinePointListsFree(*head);
172 	    *head = NULL;
173 	}
174 	espl->next = *head;
175 	*head = spl;
176     }
177     if ( (empty || doclear) && width!=UNDEFINED_WIDTH )
178 	SCSynchronizeWidth(sc,width,sc->width,NULL);
179     SCCharChangedUpdate(sc,layer);
180 }
181 
SCImportPS(SplineChar * sc,int layer,char * path,bool doclear,ImportParams * ip)182 void SCImportPS(SplineChar *sc,int layer,char *path,bool doclear,
183                 ImportParams *ip) {
184     FILE *ps = fopen(path,"r");
185 
186     if ( ps==NULL )
187 return;
188     SCImportPSFile(sc,layer,ps,doclear,ip);
189     fclose(ps);
190 }
191 
SCImportPDFFile(SplineChar * sc,int layer,FILE * pdf,bool doclear,ImportParams * ip)192 void SCImportPDFFile(SplineChar *sc,int layer,FILE *pdf,bool doclear,
193                      ImportParams *ip) {
194     SplinePointList *spl, *espl;
195     SplineSet **head;
196     bigreal tmpjl = ip->default_joinlimit;
197 
198     if ( pdf==NULL )
199 return;
200 
201     if ( sc->parent->multilayer && layer>ly_back ) {
202 	SCAppendEntityLayers(sc, EntityInterpretPDFPage(pdf,-1), ip);
203     } else {
204 	if ( tmpjl==JLIMIT_INHERITED )
205 	    ip->default_joinlimit = 10.0; // PostScript default
206 	spl = SplinesFromEntities(EntityInterpretPDFPage(pdf,-1),ip,sc->parent->strokedfont);
207 	ip->default_joinlimit = tmpjl;
208 	if ( spl==NULL ) {
209 	    ff_post_error( _("Too Complex or Bad"), _("I'm sorry this file is too complex for me to understand (or is erroneous, or is empty)") );
210 return;
211 	}
212 	if ( sc->layers[layer].order2 )
213 	    spl = SplineSetsConvertOrder(spl,true);
214 	for ( espl=spl; espl->next!=NULL; espl = espl->next );
215 	if ( layer==ly_grid )
216 	    head = &sc->parent->grid.splines;
217 	else {
218 	    SCPreserveLayer(sc,layer,false);
219 	    head = &sc->layers[layer].splines;
220 	}
221 	if ( doclear ) {
222 	    SplinePointListsFree(*head);
223 	    *head = NULL;
224 	}
225 	espl->next = *head;
226 	*head = spl;
227     }
228     SCCharChangedUpdate(sc,layer);
229 }
230 
SCImportPDF(SplineChar * sc,int layer,char * path,bool doclear,ImportParams * ip)231 void SCImportPDF(SplineChar *sc,int layer,char *path,bool doclear,
232                  ImportParams *ip) {
233     FILE *pdf = fopen(path,"r");
234 
235     if ( pdf==NULL )
236 return;
237     SCImportPDFFile(sc,layer,pdf,doclear,ip);
238     fclose(pdf);
239 }
240 
SCImportPlateFile(SplineChar * sc,int layer,FILE * plate,bool doclear,ImportParams * ip)241 void SCImportPlateFile(SplineChar *sc,int layer,FILE *plate,bool doclear,
242                        ImportParams *ip) {
243     SplineSet **ly_head, *head, *cur, *last;
244     spiro_cp *spiros=NULL;
245     int cnt=0, max=0, ch;
246     char buffer[80];
247     real transform[6];
248 
249     if ( plate==NULL )
250 return;
251 
252     head = last = NULL;
253     fgets(buffer,sizeof(buffer),plate);
254     if ( strncmp(buffer,"(plate",strlen("plate("))!=0 ) {
255 	ff_post_error( _("Not a plate file"), _("This does not seem to be a plate file\nFirst line wrong"));
256 return;
257     }
258     while ( !feof(plate)) {
259 	while ( isspace( (ch=getc(plate)) ) );
260 	if ( ch==')' || ch==EOF )
261     break;
262 	if ( ch!='(' ) {
263 	    ff_post_error( _("Not a plate file"), _("This does not seem to be a plate file\nExpected left paren"));
264             free(spiros);
265 return;
266 	}
267 	ch = getc(plate);
268 	if ( ch!='v' && ch!='o' && ch!='c' && ch!='[' && ch!=']' && ch!='z' ) {
269 	    ff_post_error( _("Not a plate file"), _("This does not seem to be a plate file\nExpected one of 'voc[]z'"));
270             free(spiros);
271 return;
272 	}
273 	if ( cnt>=max )
274 	    spiros = realloc(spiros,(max+=30)*sizeof(spiro_cp));
275 	spiros[cnt].x = spiros[cnt].y = 0;
276 	spiros[cnt].ty = ch;
277 	if ( ch=='z' ) {
278 	    cur = SpiroCP2SplineSet(spiros);
279 	    cur->spiros = SpiroCPCopy(spiros,&cur->spiro_cnt);
280 	    cur->spiro_max = cur->spiro_cnt;
281 	    SplineSetAddExtrema(sc,cur,ae_only_good,sc->parent->ascent+sc->parent->descent);
282 	    if ( cur==NULL )
283 		/* Do Nothing */;
284 	    else if ( last!=NULL ) {
285 		last->next = cur;
286 		last = cur;
287 	    } else
288 		head = last = cur;
289 	    cnt = 0;
290 	    ch = getc(plate);		/* Must be ')' */
291 	} else {
292 	    if ( fscanf(plate,"%lg %lg )", &spiros[cnt].x, &spiros[cnt].y)!=2 ) {
293 		ff_post_error( _("Not a plate file"), _("This does not seem to be a plate file\nExpected two real numbers"));
294                 free(spiros);
295 return;
296 	    }
297 	    ++cnt;
298 	}
299     }
300     if ( cnt!=0 ) {
301 	/* This happens when we've got an open contour */
302 	if ( cnt>=max )
303 	    spiros = realloc(spiros,(max+=30)*sizeof(spiro_cp));
304 	spiros[cnt].x = spiros[cnt].y = 0;
305 	spiros[cnt].ty = 'z';
306 	spiros[0].ty = '{';		/* Open contour mark */
307 	cur = SpiroCP2SplineSet(spiros);
308 	cur->spiros = SpiroCPCopy(spiros,&cur->spiro_cnt);
309 	cur->spiro_max = cur->spiro_cnt;
310 	SplineSetAddExtrema(sc,cur,ae_only_good,sc->parent->ascent+sc->parent->descent);
311 	if ( cur==NULL )
312 	    /* Do Nothing */;
313 	else if ( last!=NULL ) {
314 	    last->next = cur;
315 	    last = cur;
316 	} else
317 	    head = last = cur;
318     }
319     free(spiros);
320 
321     /* Raph's plate files seem to have the base line at 800, and glyphs grow */
322     /*  downwards */ /* At least for Inconsola */
323     memset(transform,0,sizeof(transform));
324     transform[0] = 1; transform[3] = -1;
325     transform[5] = 800;
326     head = SplinePointListTransform(head,transform,tpt_AllPoints);
327     /* After doing the above flip, the contours appear oriented acording to my*/
328     /*  conventions */
329 
330     if ( sc->layers[layer].order2 ) {
331 	head = SplineSetsConvertOrder(head,true);
332 	for ( last=head; last->next!=NULL; last = last->next );
333     }
334     if ( layer==ly_grid )
335 	ly_head = &sc->parent->grid.splines;
336     else {
337 	SCPreserveLayer(sc,layer,false);
338 	ly_head = &sc->layers[layer].splines;
339     }
340     if ( doclear ) {
341 	SplinePointListsFree(*ly_head);
342 	*ly_head = NULL;
343     }
344     last->next = *ly_head;
345     *ly_head = head;
346     SCCharChangedUpdate(sc,layer);
347 }
348 
SCImportSVG(SplineChar * sc,int layer,char * path,char * memory,int memlen,bool doclear,ImportParams * ip)349 void SCImportSVG(SplineChar *sc,int layer,char *path,char *memory, int memlen,
350                  bool doclear, ImportParams *ip) {
351     SplinePointList *spl, *espl, **head;
352 
353     if ( sc->parent->multilayer && layer>ly_back ) {
354 	SCAppendEntityLayers(sc,
355 	       EntityInterpretSVG(path,memory,memlen,
356 	                          sc->parent->ascent+sc->parent->descent,
357 	                          sc->parent->ascent,ip->scale), ip);
358     } else {
359 	spl = SplinePointListInterpretSVG(path,memory,memlen,
360 	                                  sc->parent->ascent+sc->parent->descent,
361 	                                  sc->parent->ascent,
362 	                                  sc->parent->strokedfont, ip);
363 	for ( espl = spl; espl!=NULL && espl->first->next==NULL; espl=espl->next );
364 	if ( espl!=NULL )
365 	    if ( espl->first->next->order2!=sc->layers[layer].order2 )
366 		spl = SplineSetsConvertOrder(spl,sc->layers[layer].order2);
367 	if ( spl==NULL ) {
368 	    ff_post_error(_("Too Complex or Bad"),_("I'm sorry this file is too complex for me to understand (or is erroneous)"));
369 return;
370 	}
371 	for ( espl=spl; espl->next!=NULL; espl = espl->next );
372 	if ( layer==ly_grid )
373 	    head = &sc->parent->grid.splines;
374 	else {
375 	    SCPreserveLayer(sc,layer,false);
376 	    head = &sc->layers[layer].splines;
377 	}
378 	if ( doclear ) {
379 	    SplinePointListsFree(*head);
380 	    *head = NULL;
381 	}
382 	espl->next = *head;
383 	*head = spl;
384     }
385     SCCharChangedUpdate(sc,layer);
386 }
387 
SCImportGlif(SplineChar * sc,int layer,char * path,char * memory,int memlen,bool doclear,ImportParams * UNUSED (ip))388 void SCImportGlif(SplineChar *sc,int layer,char *path,char *memory, int memlen,
389                   bool doclear, ImportParams *UNUSED(ip)) {
390     SplinePointList *spl, *espl, **head;
391 
392     spl = SplinePointListInterpretGlif(sc->parent,path,memory,memlen,
393                                        sc->parent->ascent+sc->parent->descent,
394                                        sc->parent->ascent,
395                                        sc->parent->strokedfont);
396     for ( espl = spl; espl!=NULL && espl->first->next==NULL; espl=espl->next );
397     if ( espl!=NULL )
398 	if ( espl->first->next->order2!=sc->layers[layer].order2 )
399 	    spl = SplineSetsConvertOrder(spl,sc->layers[layer].order2);
400     if ( spl==NULL ) {
401 	ff_post_error(_("Too Complex or Bad"),_("I'm sorry this file is too complex for me to understand (or is erroneous)"));
402 return;
403     }
404     for ( espl=spl; espl->next!=NULL; espl = espl->next );
405     if ( layer==ly_grid )
406 	head = &sc->parent->grid.splines;
407     else {
408 	SCPreserveLayer(sc,layer,false);
409 	head = &sc->layers[layer].splines;
410     }
411     if ( doclear ) {
412 	SplinePointListsFree(*head);
413 	*head = NULL;
414     }
415     espl->next = *head;
416     *head = spl;
417 
418     SCCharChangedUpdate(sc,layer);
419 }
420 
421 /**************************** Fig File Import *********************************/
422 
slurppoints(FILE * fig,SplineFont * sf,int cnt)423 static BasePoint *slurppoints(FILE *fig,SplineFont *sf,int cnt ) {
424     BasePoint *bps = malloc((cnt+1)*sizeof(BasePoint));	/* spline code may want to add another point */
425     int x, y, i, ch;
426     real scale = sf->ascent/(8.5*1200.0);
427     real ascent = 11*1200*sf->ascent/(sf->ascent+sf->descent);
428 
429     for ( i = 0; i<cnt; ++i ) {
430 	fscanf(fig,"%d %d", &x, &y );
431 	bps[i].x = x*scale;
432 	bps[i].y = (ascent-y)*scale;
433     }
434     while ((ch=getc(fig))!='\n' && ch!=EOF);
435 return( bps );
436 }
437 
slurpcolor(FILE * fig,SplineSet * sofar)438 static SplineSet *slurpcolor(FILE *fig,SplineSet *sofar) {
439     int ch;
440     while ((ch=getc(fig))!='\n' && ch!=EOF);
441 return( sofar );
442 }
443 
444 static SplineSet *slurpcompoundguts(FILE *fig,SplineChar *sc, SplineSet *sofar);
445 
slurpcompound(FILE * fig,SplineChar * sc,SplineSet * sofar)446 static SplineSet * slurpcompound(FILE *fig,SplineChar *sc, SplineSet *sofar) {
447     int ch;
448 
449     fscanf(fig, "%*d %*d %*d %*d" );
450     while ((ch=getc(fig))!='\n' && ch!=EOF);
451     sofar = slurpcompoundguts(fig,sc,sofar);
452 return( sofar );
453 }
454 
ArcSpline(SplinePoint * sp,float sa,SplinePoint * ep,float ea,float cx,float cy,float r)455 static SplinePoint *ArcSpline(SplinePoint *sp,float sa,SplinePoint *ep,float ea,
456 	float cx, float cy, float r) {
457     double len;
458     double ss, sc, es, ec;
459 
460     ss = sin(sa); sc = cos(sa); es = sin(ea); ec = cos(ea);
461     if ( ep==NULL )
462 	ep = SplinePointCreate((double)(cx+r)*ec, (double)(cy+r)*es);
463     len = ((double)(ea-sa)/(FF_PI/2)) * (double)r * .552;
464 
465     sp->nextcp.x = sp->me.x - len*ss; sp->nextcp.y = sp->me.y + len*sc;
466     ep->prevcp.x = ep->me.x + len*es; ep->prevcp.y = ep->me.y - len*ec;
467     sp->nonextcp = ep->noprevcp = false;
468     SplineMake3(sp,ep);
469 return( ep );
470 }
471 
slurparc(FILE * fig,SplineChar * sc,SplineSet * sofar)472 static SplineSet * slurparc(FILE *fig,SplineChar *sc, SplineSet *sofar) {
473     int ch;
474     int sub, dir, fa, ba;	/* 0 clockwise, 1 counter */
475     float cx, cy, r, sa, ea, ma;
476     float sx,sy,ex,ey;
477     int _sx,_sy,_ex,_ey;
478     SplinePoint *sp, *ep;
479     SplinePointList *spl;
480     real scale = sc->parent->ascent/(8.5*1200.0);
481     real ascent = 11*1200*sc->parent->ascent/(sc->parent->ascent+sc->parent->descent);
482 
483     fscanf(fig, "%d %*d %*d %*d %*d %*d %*d %*d %*f %*d %d %d %d %f %f %d %d %*d %*d %d %d",
484 	    &sub, &dir, &fa, &ba, &cx, &cy, &_sx, &_sy, &_ex, &_ey );
485     while ((ch=getc(fig))!='\n' && ch!=EOF);
486     /* I ignore arrow lines */
487     if ( fa )
488 	while ((ch=getc(fig))!='\n' && ch!=EOF);
489     if ( ba )
490 	while ((ch=getc(fig))!='\n' && ch!=EOF);
491     sx = _sx*scale; sy = (ascent-_sy)*scale; ex = _ex*scale; ey=(ascent-_ey)*scale; cx=(double)cx*scale; cy=(ascent-(double)cy)*scale;
492     r = sqrt( (sx-cx)*(sx-cx) + (sy-cy)*(sy-cy) );
493     sa = atan2(sy-cy,sx-cx);
494     ea = atan2(ey-cy,ex-cx);
495 
496     spl = chunkalloc(sizeof(SplinePointList));
497     spl->next = sofar;
498     spl->first = sp = SplinePointCreate(sx,sy);
499     spl->last = ep = SplinePointCreate(ex,ey);
500 
501     if ( dir==0 ) {	/* clockwise */
502 	if ( ea>sa ) ea = (double)ea - 2*FF_PI;
503 	ma=ceil((double)sa/(FF_PI/2)-1)*(FF_PI/2);
504 	if ( RealNearish( sa,ma )) ma = (double)ma - (FF_PI/2);
505 	while ( ma > ea ) {
506 	    sp = ArcSpline(sp,sa,NULL,ma,cx,cy,r);
507 	    sa = ma;
508 	    ma = (double)ma - (FF_PI/2);
509 	}
510 	sp = ArcSpline(sp,sa,ep,ea,cx,cy,r);
511     } else {		/* counterclockwise */
512 	if ( ea<sa ) ea = (double)ea + 2*FF_PI;
513 	ma=floor((double)sa/(FF_PI/2)+1)*(FF_PI/2);
514 	if ( RealNearish( sa,ma )) ma = (double)ma + (FF_PI/2);
515 	while ( ma < ea ) {
516 	    sp = ArcSpline(sp,sa,NULL,ma,cx,cy,r);
517 	    sa = ma;
518 	    ma = (double)ma + (FF_PI/2);
519 	}
520 	sp = ArcSpline(sp,sa,ep,ea,cx,cy,r);
521     }
522 return( spl );
523 }
524 
slurpelipse(FILE * fig,SplineChar * sc,SplineSet * sofar)525 static SplineSet * slurpelipse(FILE *fig,SplineChar *sc, SplineSet *sofar) {
526     int ch;
527     int sub, dir, cx, cy, rx, ry;
528     float angle;
529     SplinePointList *spl;
530     SplinePoint *sp;
531     real dcx,dcy,drx,dry;
532     SplineFont *sf = sc->parent;
533     real scale = sf->ascent/(8.5*1200.0);
534     real ascent = 11*1200*sf->ascent/(sf->ascent+sf->descent);
535     /* I ignore the angle */
536 
537     fscanf(fig, "%d %*d %*d %*d %*d %*d %*d %*d %*f %d %f %d %d %d %d %*d %*d %*d %*d",
538 	    &sub, &dir, &angle, &cx, &cy, &rx, &ry );
539     while ((ch=getc(fig))!='\n' && ch!=EOF);
540 
541     dcx = cx*scale; dcy = (ascent-cy)*scale;
542     drx = rx*scale; dry = ry*scale;
543 
544     spl = chunkalloc(sizeof(SplinePointList));
545     spl->next = sofar;
546     spl->first = sp = chunkalloc(sizeof(SplinePoint));
547     sp->me.x = dcx; sp->me.y = dcy+dry;
548 	sp->nextcp.x = sp->me.x + .552*drx; sp->nextcp.y = sp->me.y;
549 	sp->prevcp.x = sp->me.x - .552*drx; sp->prevcp.y = sp->me.y;
550     spl->last = sp = chunkalloc(sizeof(SplinePoint));
551     sp->me.x = dcx+drx; sp->me.y = dcy;
552 	sp->nextcp.x = sp->me.x; sp->nextcp.y = sp->me.y - .552*dry;
553 	sp->prevcp.x = sp->me.x; sp->prevcp.y = sp->me.y + .552*dry;
554     SplineMake3(spl->first,sp);
555     sp = chunkalloc(sizeof(SplinePoint));
556     sp->me.x = dcx; sp->me.y = dcy-dry;
557 	sp->nextcp.x = sp->me.x - .552*drx; sp->nextcp.y = sp->me.y;
558 	sp->prevcp.x = sp->me.x + .552*drx; sp->prevcp.y = sp->me.y;
559     SplineMake3(spl->last,sp);
560     spl->last = sp;
561     sp = chunkalloc(sizeof(SplinePoint));
562     sp->me.x = dcx-drx; sp->me.y = dcy;
563 	sp->nextcp.x = sp->me.x; sp->nextcp.y = sp->me.y + .552*dry;
564 	sp->prevcp.x = sp->me.x; sp->prevcp.y = sp->me.y - .552*dry;
565     SplineMake3(spl->last,sp);
566     SplineMake3(sp,spl->first);
567     spl->last = spl->first;
568 return( spl );
569 }
570 
slurppolyline(FILE * fig,SplineChar * sc,SplineSet * sofar)571 static SplineSet * slurppolyline(FILE *fig,SplineChar *sc, SplineSet *sofar) {
572     int ch;
573     int sub, cnt, fa, ba, radius;	/* radius of roundrects (sub==4) */
574     BasePoint *bps;
575     BasePoint topleft, bottomright;
576     SplinePointList *spl=NULL;
577     SplinePoint *sp;
578     int i;
579 
580     fscanf(fig, "%d %*d %*d %*d %*d %*d %*d %*d %*f %*d %*d %d %d %d %d",
581 	    &sub, &radius, &fa, &ba, &cnt );
582     /* sub==1 => polyline, 2=>box, 3=>polygon, 4=>arc-box, 5=>imported eps bb */
583     while ((ch=getc(fig))!='\n' && ch!=EOF);
584     /* I ignore arrow lines */
585     if ( fa )
586 	while ((ch=getc(fig))!='\n' && ch!=EOF);
587     if ( ba )
588 	while ((ch=getc(fig))!='\n' && ch!=EOF);
589     bps = slurppoints(fig,sc->parent,cnt);
590     if ( sub==5 )		/* skip picture line */
591 	while ((ch=getc(fig))!='\n' && ch!=EOF);
592     else {
593 	if ( sub!=1 && bps[cnt-1].x==bps[0].x && bps[cnt-1].y==bps[0].y )
594 	    --cnt;
595 	spl = chunkalloc(sizeof(SplinePointList));
596 	if ( cnt==4 && sub==4/*arc-box*/ && radius!=0 ) {
597 	    SplineFont *sf = sc->parent;
598 	    real scale = sf->ascent/(8.5*80.0), r = radius*scale;	/* radii are scaled differently */
599 	    if ( bps[0].x>bps[2].x ) {
600 		topleft.x = bps[2].x;
601 		bottomright.x = bps[0].x;
602 	    } else {
603 		topleft.x = bps[0].x;
604 		bottomright.x = bps[2].x;
605 	    }
606 	    if ( bps[0].y<bps[2].y ) {
607 		topleft.y = bps[2].y;
608 		bottomright.y = bps[0].y;
609 	    } else {
610 		topleft.y = bps[0].y;
611 		bottomright.y = bps[2].y;
612 	    }
613 	    spl->first = SplinePointCreate(topleft.x,topleft.y-r); spl->first->pointtype = pt_tangent;
614 	    spl->first->nextcp.y += .552*r; spl->first->nonextcp = false;
615 	    spl->last = sp = SplinePointCreate(topleft.x+r,topleft.y); sp->pointtype = pt_tangent;
616 	    sp->prevcp.x -= .552*r; sp->noprevcp = false;
617 	    SplineMake3(spl->first,sp);
618 	    sp = SplinePointCreate(bottomright.x-r,topleft.y); sp->pointtype = pt_tangent;
619 	    sp->nextcp.x += .552*r; sp->nonextcp = false;
620 	    SplineMake3(spl->last,sp); spl->last = sp;
621 	    sp = SplinePointCreate(bottomright.x,topleft.y-r); sp->pointtype = pt_tangent;
622 	    sp->prevcp.y += .552*r; sp->noprevcp = false;
623 	    SplineMake3(spl->last,sp); spl->last = sp;
624 	    sp = SplinePointCreate(bottomright.x,bottomright.y+r); sp->pointtype = pt_tangent;
625 	    sp->nextcp.y -= .552*r; sp->nonextcp = false;
626 	    SplineMake3(spl->last,sp); spl->last = sp;
627 	    sp = SplinePointCreate(bottomright.x-r,bottomright.y); sp->pointtype = pt_tangent;
628 	    sp->prevcp.x += .552*r; sp->noprevcp = false;
629 	    SplineMake3(spl->last,sp); spl->last = sp;
630 	    sp = SplinePointCreate(topleft.x+r,bottomright.y); sp->pointtype = pt_tangent;
631 	    sp->nextcp.x -= .552*r; sp->nonextcp = false;
632 	    SplineMake3(spl->last,sp); spl->last = sp;
633 	    sp = SplinePointCreate(topleft.x,bottomright.y+r); sp->pointtype = pt_tangent;
634 	    sp->prevcp.y -= .552*r; sp->noprevcp = false;
635 	    SplineMake3(spl->last,sp); spl->last = sp;
636 	} else {
637 	    for ( i=0; i<cnt; ++i ) {
638 		sp = chunkalloc(sizeof(SplinePoint));
639 		sp->me = sp->nextcp = sp->prevcp = bps[i];
640 		sp->nonextcp = sp->noprevcp = true;
641 		sp->pointtype = pt_corner;
642 		if ( spl->first==NULL )
643 		    spl->first = sp;
644 		else
645 		    SplineMake3(spl->last,sp);
646 		spl->last = sp;
647 	    }
648 	}
649 	if ( sub!=1 ) {
650 	    SplineMake3(spl->last,spl->first);
651 	    spl->last = spl->first;
652 	}
653 	spl->next = sc->layers[ly_fore].splines;
654 	spl->next = sofar;
655     }
656     free(bps);
657 return( spl );
658 }
659 
660 /* http://dev.acm.org/pubs/citations/proceedings/graph/218380/p377-blanc/ */
661 /*  X-Splines: a spline model designed for the end-user */
662 /*		by Carole Blanc & Christophe Schlick */
663 /* Also based on the helpful code fragment by Andreas Baerentzen */
664 /*  http://lin1.gk.dtu.dk/home/jab/software.html */
665 
666 struct xspline {
667     int n;		/* total number of control points */
668     BasePoint *cp;	/* an array of n control points */
669     real *s;		/* an array of n tension values */
670     /* for a closed spline cp[0]==cp[n-1], but we may still need to wrap a bit*/
671     unsigned int closed: 1;
672 };
673 
g(real u,real q,real p)674 static real g(real u, real q, real p) {
675 return( u * (q + u * (2*q + u *( 10-12*q+10*p + u * ( 2*p+14*q-15 + u*(6-5*q-p))))) );
676 }
677 
h(real u,real q)678 static real h(real u, real q) {
679     /* The paper says that h(-1)==0, but the definition of h they give */
680     /*  doesn't do that. But if we negate the x^5 term it all works */
681     /*  (works for the higher derivatives too) */
682 return( q*u * (1 + u * (2 - u * u * (u+2))) );
683 }
684 
xsplineeval(BasePoint * ret,real t,struct xspline * xs)685 static void xsplineeval(BasePoint *ret,real t, struct xspline *xs) {
686     /* By choosing t to range between [0,n-1] we set delta in the article to 1*/
687     /*  and may therefore ignore it */
688 
689     /* For any value of t there are four possible points that might be */
690     /*  influencing things. These are cp[k], cp[k+1], cp[k+2], cp[k+3] */
691     /*  where k+1<=t<k+2 */
692     int k = floor(t-1);
693     int k0, k1, k2, k3;
694     /* now we need to find the points near us (on the + side of cp[k] & */
695     /*  cp[k-1] and the - side of cp[k+2] & cp[k+3]) where the blending */
696     /*  function becomes 0. This depends on the tension values */
697     /* For negative tension values it doesn't happen, the curve itself */
698     /*  is changed */
699     real Tk0 = k+1 + (xs->s[k+1]>0?xs->s[k+1]:0);
700     real Tk1 = k+2 + (xs->s[k+2]>0?xs->s[k+2]:0);
701     real Tk2 = k+1 - (xs->s[k+1]>0?xs->s[k+1]:0);
702     real Tk3 = k+2 - (xs->s[k+2]>0?xs->s[k+2]:0);
703     /* Now each blending function has a "p" value that describes its shape*/
704     real p0 = 2*(k-Tk0)*(k-Tk0);
705     real p1 = 2*(k+1-Tk1)*(k+1-Tk1);
706     real p2 = 2*(k+2-Tk2)*(k+2-Tk2);
707     real p3 = 2*(k+3-Tk3)*(k+3-Tk3);
708     /* and each negative tension blending function has a "q" value */
709     real q0 = xs->s[k+1]<0?-xs->s[k+1]/2:0;
710     real q1 = xs->s[k+2]<0?-xs->s[k+2]/2:0;
711     real q2 = q0;
712     real q3 = q1;
713     /* the function f for positive s is the same as g if q==0 */
714     real A0, A1, A2, A3;
715     if ( t<=Tk0 )
716         A0 = g( (t-Tk0)/(k-Tk0), q0, p0);
717     else if ( q0>0 )
718         A0 = h( (t-Tk0)/(k-Tk0), q0 );
719     else
720         A0 = 0;
721     A1 = g( (t-Tk1)/(k+1-Tk1), q1, p1);
722     A2 = g( (t-Tk2)/(k+2-Tk2), q2, p2);
723     if ( t>=Tk3 )
724         A3 = g( (t-Tk3)/(k+3-Tk3), q3, p3);
725     else if ( q3>0 )
726         A3 = h( (t-Tk3)/(k+3-Tk3), q3 );
727     else
728         A3 = 0;
729     k0 = k; k1=k+1; k2=k+2; k3=k+3;
730     if ( k<0 ) { k0=xs->n-2; if ( !xs->closed ) A0 = 0; }
731     if ( k3>=xs->n ) { k3 -= xs->n; if ( !xs->closed ) A3 = 0; }
732     if ( k2>=xs->n ) { k2 -= xs->n; if ( !xs->closed ) A2 = 0; }
733     ret->x = A0*xs->cp[k0].x + A1*xs->cp[k1].x + A2*xs->cp[k2].x + A3*xs->cp[k3].x;
734     ret->y = A0*xs->cp[k0].y + A1*xs->cp[k1].y + A2*xs->cp[k2].y + A3*xs->cp[k3].y;
735     ret->x /= (A0+A1+A2+A3);
736     ret->y /= (A0+A1+A2+A3);
737 }
738 
AdjustTs(FitPoint * mids,SplinePoint * from,SplinePoint * to)739 static void AdjustTs(FitPoint *mids,SplinePoint *from, SplinePoint *to) {
740     real len=0, sofar;
741     real lens[8];
742     int i;
743 
744     lens[0] = sqrt((mids[0].p.x-from->me.x)*(mids[0].p.x-from->me.x) +
745 		    (mids[0].p.y-from->me.y)*(mids[0].p.y-from->me.y));
746     lens[7] = sqrt((mids[6].p.x-to->me.x)*(mids[6].p.x-to->me.x) +
747 		    (mids[6].p.y-to->me.y)*(mids[6].p.y-to->me.y));
748     for ( i=1; i<7; ++i )
749 	lens[i] = sqrt((mids[i].p.x-mids[i-1].p.x)*(mids[i].p.x-mids[i-1].p.x) +
750 			(mids[i].p.y-mids[i-1].p.y)*(mids[i].p.y-mids[i-1].p.y));
751     for ( len=0, i=0; i<8; ++i )
752 	len += lens[i];
753     for ( sofar=0, i=0; i<7; ++i ) {
754 	sofar += lens[i];
755 	mids[i].t = sofar/len;
756     }
757 }
758 
ApproximateXSpline(struct xspline * xs,int order2)759 static SplineSet *ApproximateXSpline(struct xspline *xs,int order2) {
760     size_t i, j;
761     real t;
762     FitPoint mids[7];
763     SplineSet *spl = chunkalloc(sizeof(SplineSet));
764     SplinePoint *sp;
765 
766     spl->first = spl->last = chunkalloc(sizeof(SplinePoint));
767     xsplineeval(&spl->first->me,0,xs);
768     spl->first->pointtype = ( xs->s[0]==0 )?pt_corner:pt_curve;
769     for ( i=0; i<(size_t)(xs->n-1); ++i ) {
770 	if ( i==(size_t)(xs->n-2) && xs->closed )
771 	    sp = spl->first;
772 	else {
773 	    sp = chunkalloc(sizeof(SplinePoint));
774 	    sp->pointtype = ( xs->s[i+1]==0 )?pt_corner:pt_curve;
775 	    xsplineeval(&sp->me,i+1,xs);
776 	}
777 	for ( j=0, t=1./8; j<sizeof(mids)/sizeof(mids[0]); ++j, t+=1./8 ) {
778 	    xsplineeval((BasePoint *) &mids[j].p,i+t,xs);
779 	    mids[j].t = t;
780 	}
781 	AdjustTs(mids,spl->last,sp);
782 	ApproximateSplineFromPoints(spl->last,sp,mids,sizeof(mids)/sizeof(mids[0]),order2);
783 	SPAverageCps(spl->last);
784 	spl->last = sp;
785     }
786     if ( !xs->closed ) {
787 	spl->first->noprevcp = spl->last->nonextcp = true;
788 	spl->first->prevcp = spl->first->me;
789 	spl->last->nextcp = spl->last->me;
790     } else
791 	SPAverageCps(spl->first);
792 return( spl );
793 }
794 
slurpspline(FILE * fig,SplineChar * sc,SplineSet * sofar)795 static SplineSet * slurpspline(FILE *fig,SplineChar *sc, SplineSet *sofar) {
796     int ch;
797     int sub, cnt, fa, ba;
798     SplinePointList *spl;
799     struct xspline xs;
800     int i;
801 
802     fscanf(fig, "%d %*d %*d %*d %*d %*d %*d %*d %*f %*d %d %d %d",
803 	    &sub, &fa, &ba, &cnt );
804     while ((ch=getc(fig))!='\n' && ch!=EOF);
805     /* I ignore arrow lines */
806     if ( fa )
807 	while ((ch=getc(fig))!='\n' && ch!=EOF);
808     if ( ba )
809 	while ((ch=getc(fig))!='\n' && ch!=EOF);
810     xs.n = cnt;
811     xs.cp = slurppoints(fig,sc->parent,cnt);
812     xs.s = malloc((cnt+1)*sizeof(real));
813     xs.closed = (sub&1);
814     for ( i=0; i<cnt; ++i )
815 #ifdef FONTFORGE_CONFIG_USE_DOUBLE
816 	fscanf(fig,"%lf",&xs.s[i]);
817 #else
818 	fscanf(fig,"%f",&xs.s[i]);
819 #endif
820     /* the spec says that the last point of a closed path will duplicate the */
821     /* first, but it doesn't seem to */
822     if ( xs.closed && ( !RealNear(xs.cp[cnt-1].x,xs.cp[0].x) ||
823 			!RealNear(xs.cp[cnt-1].y,xs.cp[0].y) )) {
824 	xs.n = ++cnt;
825 	xs.cp[cnt-1] = xs.cp[0];
826 	xs.s[cnt-1] = xs.s[0];
827     }
828     spl = ApproximateXSpline(&xs,sc->layers[ly_fore].order2);
829 
830     free(xs.cp);
831     free(xs.s);
832 
833     spl->next = sofar;
834 return( spl );
835 }
836 
slurpcompoundguts(FILE * fig,SplineChar * sc,SplineSet * sofar)837 static SplineSet *slurpcompoundguts(FILE *fig,SplineChar *sc,SplineSet *sofar) {
838     int oc;
839     int ch;
840 
841     while ( 1 ) {
842 	fscanf(fig,"%d",&oc);
843 	if ( feof(fig) || oc==-6 )
844 return(sofar);
845 	switch ( oc ) {
846 	  case 6:
847 	    sofar = slurpcompound(fig,sc,sofar);
848 	  break;
849 	  case 0:
850 	    sofar = slurpcolor(fig,sofar);
851 	  break;
852 	  case 1:
853 	    sofar = slurpelipse(fig,sc,sofar);
854 	  break;
855 	  case 5:
856 	    sofar = slurparc(fig,sc,sofar);
857 	  break;
858 	  case 2:
859 	    sofar = slurppolyline(fig,sc,sofar);
860 	  break;
861 	  case 3:
862 	    sofar = slurpspline(fig,sc,sofar);
863 	  break;
864 	  case 4:
865 	  default:
866 	    /* Text is also only one line */
867 	    while ( (ch=getc(fig))!='\n' && ch!=EOF );
868 	  break;
869 	}
870     }
871 return( sofar );
872 }
873 
SCImportFig(SplineChar * sc,int layer,char * path,bool doclear,ImportParams * ip)874 void SCImportFig(SplineChar *sc,int layer,char *path,bool doclear,
875                  ImportParams *ip) {
876     FILE *fig;
877     char buffer[100];
878     SplineSet *spl, *espl, **head;
879     int i;
880 
881     fig = fopen(path,"r");
882     if ( fig==NULL ) {
883 	ff_post_error(_("Can't find the file"),_("Can't find the file"));
884 return;
885     }
886     if ( fgets(buffer,sizeof(buffer),fig)==NULL || strcmp(buffer,"#FIG 3.2\n")!=0 ) {
887 	ff_post_error(_("Bad xfig file"),_("Bad xfig file"));
888 	fclose(fig);
889 return;
890     }
891     /* skip the header, it isn't interesting */
892     for ( i=0; i<8; ++i )
893 	fgets(buffer,sizeof(buffer),fig);
894     spl = slurpcompoundguts(fig,sc,NULL);
895     if ( spl!=NULL ) {
896 	if ( layer==ly_grid )
897 	    head = &sc->parent->grid.splines;
898 	else {
899 	    SCPreserveLayer(sc,layer,false);
900 	    head = &sc->layers[layer].splines;
901 	}
902 	if ( doclear ) {
903 	    SplinePointListsFree(*head);
904 	    *head = NULL;
905 	}
906 	if ( sc->layers[ly_fore].order2 )
907 	    spl = SplineSetsConvertOrder(spl,true);
908 	for ( espl=spl; espl->next!=NULL; espl=espl->next );
909 	espl->next = *head;
910 	*head = spl;
911 	SCCharChangedUpdate(sc,layer);
912     }
913     fclose(fig);
914 }
915 
916 /************************** Normal Image Import *******************************/
917 
ImageAlterClut(GImage * image)918 GImage *ImageAlterClut(GImage *image) {
919     struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
920     GClut *clut;
921 
922     if ( base->image_type!=it_mono ) {
923 	/* png b&w images come through as indexed, not mono */
924 	if ( base->clut!=NULL && base->clut->clut_len==2 ) {
925 	    GImage *new = GImageCreate(it_mono,base->width,base->height);
926 	    struct _GImage *nbase = new->u.image;
927 	    int i,j;
928 	    memset(nbase->data,0,nbase->height*nbase->bytes_per_line);
929 	    for ( i=0; i<base->height; ++i ) for ( j=0; j<base->width; ++j )
930 		if ( base->data[i*base->bytes_per_line+j] )
931 		    nbase->data[i*nbase->bytes_per_line+(j>>3)] |= (0x80>>(j&7));
932 	    nbase->clut = base->clut;
933 	    base->clut = NULL;
934 	    nbase->trans = base->trans;
935 	    GImageDestroy(image);
936 	    image = new;
937 	    base = nbase;
938 	} else
939 return( image );
940     }
941 
942     clut = base->clut;
943     if ( clut==NULL ) {
944 	clut=base->clut = calloc(1,sizeof(GClut));
945 	clut->clut_len = 2;
946 	clut->clut[0] = 0x808080;
947 	if ( !no_windowing_ui )
948 	    clut->clut[1] = default_background;
949 	else
950 	    clut->clut[1] = 0xb0b0b0;
951 	clut->trans_index = 1;
952 	base->trans = 1;
953     } else if ( base->trans!=(Color)-1 ) {
954 	clut->clut[!base->trans] = 0x808080;
955     } else if ( clut->clut[0]<clut->clut[1] ) {
956 	clut->clut[0] = 0x808080;
957 	clut->trans_index = 1;
958 	base->trans = 1;
959     } else {
960 	clut->clut[1] = 0x808080;
961 	clut->trans_index = 0;
962 	base->trans = 0;
963     }
964 return( image );
965 }
966 
SCInsertImage(SplineChar * sc,GImage * image,real scale,real yoff,real xoff,int layer)967 void SCInsertImage(SplineChar *sc,GImage *image,real scale,real yoff,real xoff,
968 	int layer) {
969     ImageList *im;
970 
971     SCPreserveLayer(sc,layer,false);
972     im = malloc(sizeof(ImageList));
973     im->image = image;
974     im->xoff = xoff;
975     im->yoff = yoff;
976     im->xscale = im->yscale = scale;
977     im->selected = true;
978     im->next = sc->layers[layer].images;
979     im->bb.minx = im->xoff; im->bb.maxy = im->yoff;
980     im->bb.maxx = im->xoff + GImageGetWidth(im->image)*im->xscale;
981     im->bb.miny = im->yoff - GImageGetHeight(im->image)*im->yscale;
982     sc->layers[layer].images = im;
983     sc->parent->onlybitmaps = false;
984     SCOutOfDateBackground(sc);
985     SCCharChangedUpdate(sc,layer);
986 }
987 
SCAddScaleImage(SplineChar * sc,GImage * image,bool doclear,int layer,ImportParams * ip)988 void SCAddScaleImage(SplineChar *sc,GImage *image,bool doclear, int layer,
989                      ImportParams *ip) {
990     double scale = 1.0;
991 
992     image = ImageAlterClut(image);
993     if ( ip->scale )
994 	scale = (sc->parent->ascent+sc->parent->descent)/(real) GImageGetHeight(image);
995     if ( doclear ) {
996 	ImageListsFree(sc->layers[layer].images);
997 	sc->layers[layer].images = NULL;
998     }
999     SCInsertImage(sc,image,scale,sc->parent->ascent,0,layer);
1000 }
1001 
FVImportImages(FontViewBase * fv,char * path,int format,int toback,bool preclear,ImportParams * ip)1002 int FVImportImages(FontViewBase *fv,char *path,int format,int toback,
1003                    bool preclear, ImportParams *ip) {
1004     GImage *image;
1005     /*struct _GImage *base;*/
1006     int tot;
1007     char *start = path, *endpath=path;
1008     int i;
1009     SplineChar *sc;
1010 
1011     tot = 0;
1012     for ( i=0; i<fv->map->enccount; ++i ) if ( fv->selected[i]) {
1013 	sc = SFMakeChar(fv->sf,fv->map,i);
1014 	endpath = strchr(start,';');
1015 	if ( endpath!=NULL ) *endpath = '\0';
1016 	if ( format==fv_image ) {
1017 	    image = GImageRead(start);
1018 	    if ( image==NULL ) {
1019 		ff_post_error(_("Bad image file"),_("Bad image file: %.100s"),start);
1020 return(false);
1021 	    }
1022 	    ++tot;
1023 	    SCAddScaleImage(sc,image,true,toback?ly_back:ly_fore, ip);
1024 	} else if ( format==fv_svg ) {
1025 	    SCImportSVG(sc,toback?ly_back:fv->active_layer,start,NULL,0,preclear,ip);
1026 	    ++tot;
1027 	} else if ( format==fv_glif ) {
1028 	    SCImportGlif(sc,toback?ly_back:fv->active_layer,start,NULL,0,preclear,ip);
1029 	    ++tot;
1030 	} else if ( format==fv_eps ) {
1031 	    SCImportPS(sc,toback?ly_back:fv->active_layer,start,preclear,ip);
1032 	    ++tot;
1033 	} else if ( format==fv_pdf ) {
1034 	    SCImportPDF(sc,toback?ly_back:fv->active_layer,start,preclear,ip);
1035 	    ++tot;
1036 #ifndef _NO_PYTHON
1037 	} else if ( format>=fv_pythonbase ) {
1038 	    PyFF_SCImport(sc,format-fv_pythonbase,start, toback?ly_back:fv->active_layer,preclear);
1039 	    ++tot;
1040 #endif
1041 	}
1042 	if ( endpath==NULL )
1043     break;
1044 	start = endpath+1;
1045     }
1046     if ( tot==0 )
1047 	ff_post_error(_("Nothing Selected"),_("You must select a glyph before you can import an image into it"));
1048     else if ( endpath!=NULL )
1049 	ff_post_error(_("More Images Than Selected Glyphs"),_("More Images Than Selected Glyphs"));
1050 return( true );
1051 }
1052 
FVImportImageTemplate(FontViewBase * fv,char * path,int format,int toback,bool preclear,ImportParams * ip)1053 int FVImportImageTemplate(FontViewBase *fv,char *path,int format,int toback,
1054                           bool preclear, ImportParams *ip) {
1055     GImage *image;
1056     struct _GImage *base;
1057     int tot;
1058     char *ext, *name, *pt, *end;
1059     const char *dirname;
1060     int i, val;
1061     int isu=false, ise=false, isc=false;
1062     DIR *dir;
1063     struct dirent *entry;
1064     SplineChar *sc;
1065     char start [1025];
1066 
1067     ext = strrchr(path,'.');
1068     name = strrchr(path,'/');
1069     if ( ext==NULL ) {
1070 	ff_post_error(_("Bad Template"),_("Bad template, no extension"));
1071 return( false );
1072     }
1073     if ( name==NULL ) name=path-1;
1074     if ( name[1]=='u' ) isu = true;
1075     else if ( name[1]=='c' ) isc = true;
1076     else if ( name[1]=='e' ) ise = true;
1077     else {
1078 	ff_post_error(_("Bad Template"),_("Bad template, unrecognized format"));
1079 return( false );
1080     }
1081     if ( name<path )
1082 	dirname = ".";
1083     else {
1084 	dirname = path;
1085 	*name = '\0';
1086     }
1087 
1088     if ( (dir = opendir(dirname))==NULL ) {
1089 	    ff_post_error(_("Nothing Loaded"),_("Nothing Loaded"));
1090 return( false );
1091     }
1092 
1093     tot = 0;
1094     while ( (entry=readdir(dir))!=NULL ) {
1095 	pt = strrchr(entry->d_name,'.');
1096 	if ( pt==NULL )
1097     continue;
1098 	if ( strmatch(pt,ext)!=0 )
1099     continue;
1100 	if ( !(
1101 		(isu && entry->d_name[0]=='u' && entry->d_name[1]=='n' && entry->d_name[2]=='i' && (val=strtol(entry->d_name+3,&end,16), end==pt)) ||
1102 		(isu && entry->d_name[0]=='u' && (val=strtol(entry->d_name+1,&end,16), end==pt)) ||
1103 		(isc && entry->d_name[0]=='c' && entry->d_name[1]=='i' && entry->d_name[2]=='d' && (val=strtol(entry->d_name+3,&end,10), end==pt)) ||
1104 		(ise && entry->d_name[0]=='e' && entry->d_name[1]=='n' && entry->d_name[2]=='c' && (val=strtol(entry->d_name+3,&end,10), end==pt)) ))
1105     continue;
1106 	sprintf (start, "%s/%s", dirname, entry->d_name);
1107 	if ( isu ) {
1108 	    i = SFFindSlot(fv->sf,fv->map,val,NULL);
1109 	    if ( i==-1 ) {
1110 		ff_post_error(_("Unicode value not in font"),_("Unicode value (%x) not in font, ignored"),val);
1111     continue;
1112 	    }
1113 	    sc = SFMakeChar(fv->sf,fv->map,i);
1114 	} else {
1115 	    if ( val<fv->map->enccount ) {
1116 		/* It's there */;
1117 	    } else {
1118 		ff_post_error(_("Encoding value not in font"),_("Encoding value (%x) not in font, ignored"),val);
1119     continue;
1120 	    }
1121 	    sc = SFMakeChar(fv->sf,fv->map,val);
1122 	}
1123 	if ( format==fv_imgtemplate ) {
1124 	    image = GImageRead(start);
1125 	    if ( image==NULL ) {
1126 		ff_post_error(_("Bad image file"),_("Bad image file: %.100s"),start);
1127     continue;
1128 	    }
1129 	    base = image->list_len==0?image->u.image:image->u.images[0];
1130 	    if ( base->image_type!=it_mono ) {
1131 		ff_post_error(_("Bad image file"),_("Bad image file, not a bitmap: %.100s"),start);
1132 		GImageDestroy(image);
1133     continue;
1134 	    }
1135 	    ++tot;
1136 	    SCAddScaleImage(sc,image,true,toback?ly_back:ly_fore,ip);
1137 	} else if ( format==fv_svgtemplate ) {
1138 	    SCImportSVG(sc,toback?ly_back:fv->active_layer,start,NULL,0,preclear,ip);
1139 	    ++tot;
1140 	} else if ( format==fv_gliftemplate ) {
1141 	    SCImportGlif(sc,toback?ly_back:fv->active_layer,start,NULL,0,preclear,ip);
1142 	    ++tot;
1143 	} else if ( format==fv_pdftemplate ) {
1144 	    SCImportPDF(sc,toback?ly_back:fv->active_layer,start,preclear,ip);
1145 	    ++tot;
1146 	} else {
1147 	    SCImportPS(sc,toback?ly_back:fv->active_layer,start,preclear,ip);
1148 	    ++tot;
1149 	}
1150     }
1151     closedir(dir);
1152     if ( tot==0 )
1153 	ff_post_error(_("Nothing Loaded"),_("Nothing Loaded"));
1154 return( true );
1155 }
1156