1 /* -*- coding: utf-8 -*- */
2 /* Copyright (C) 2003-2012 by George Williams */
3 /*
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6 
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9 
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13 
14  * The name of the author may not be used to endorse or promote products
15  * derived from this software without specific prior written permission.
16 
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <fontforge-config.h>
30 
31 #include "fontforgeui.h"
32 #include "fvfonts.h"
33 #include "gfile.h"
34 #include "gkeysym.h"
35 #include "glyphcomp.h"
36 #include "lookups.h"
37 #include "splinefill.h"
38 #include "splinesaveafm.h"
39 #include "splineutil.h"
40 #include "tottfaat.h"
41 #include "tottfgpos.h"
42 #include "ustring.h"
43 #include "utype.h"
44 
45 #include "ttf.h"
46 
47 extern int _GScrollBar_Width;
48 
49 /* This file contains routines to build a dialog showing GPOS/GSUB/morx */
50 /*  tables and their contents */
51 
52 struct att_dlg;
53 struct node {
54     unsigned int open: 1;
55     unsigned int children_checked: 1;
56     unsigned int used: 1;
57     unsigned int macfeat: 1;
58     unsigned int monospace: 1;
59     unsigned int horizontal: 1;
60     uint16 cnt;
61     struct node *children, *parent;
62     void (*build)(struct node *,struct att_dlg *);
63     char *label;		/* utf8 */
64     uint32 tag;
65     union sak {
66 	SplineChar *sc;
67 	int index;
68 	OTLookup *otl;
69 	struct lookup_subtable *sub;
70 	struct baselangextent *langs;
71 	Justify *jscript;
72 	struct jstf_lang *jlang;
73 	OTLookup **otll;
74     } u;
75     int lpos;
76 };
77 
78 enum dlg_type { dt_show_att, dt_font_comp };
79 
80 struct att_dlg {
81     unsigned int done: 1;
82     struct node *tables;
83     int open_cnt, lines_page, off_top, off_left, page_width, bmargin;
84     int maxl;
85     SplineFont *sf;
86     int def_layer;
87     GWindow gw,v;
88     GGadget *vsb, *hsb, *cancel;
89     int fh, as;
90     GFont *font, *monofont;
91     struct node *current;
92     enum dlg_type dlg_type;
93     FontView *fv1, *fv2;
94     struct node *popup_node;
95 };
96 
97 static void BuildGSUBlookups(struct node *node,struct att_dlg *att);
98 
nodesfree(struct node * node)99 static void nodesfree(struct node *node) {
100     int i;
101 
102     if ( node==NULL )
103 return;
104     for ( i=0; node[i].label!=NULL; ++i ) {
105 	nodesfree(node[i].children);
106 	free(node[i].label);
107     }
108     free(node);
109 }
110 
node_alphabetize(const void * _n1,const void * _n2)111 static int node_alphabetize(const void *_n1, const void *_n2) {
112     const struct node *n1 = _n1, *n2 = _n2;
113     int ret;
114 
115     ret = strcasecmp(n1->label,n2->label);
116     if ( ret!=0 )
117 return( ret );
118 
119 return( strcmp(n1->label,n2->label));
120 }
121 
BuildMarkedLigatures(struct node * node,struct att_dlg * att)122 static void BuildMarkedLigatures(struct node *node,struct att_dlg *att) {
123     SplineChar *sc = node->u.sc;
124     struct lookup_subtable *sub = node->parent->parent->u.sub;
125     AnchorClass *ac;
126     int classcnt, j, max, k;
127     AnchorPoint *ap;
128     char buf[90];
129     SplineFont *sf = att->sf;
130 
131     if ( sf->cidmaster!=NULL ) sf = sf->cidmaster;
132 
133     classcnt = 0;
134     for ( ap=sc->anchor; ap!=NULL; ap=ap->next )
135 	if ( ap->anchor->subtable == sub )
136 	    ++classcnt;
137     max=0;
138     for ( ap=sc->anchor; ap!=NULL ; ap=ap->next )
139 	if ( ap->lig_index>max )
140 	    max = ap->lig_index;
141     node->children = calloc(classcnt+1,sizeof(struct node));
142     for ( k=j=0; k<=max; ++k ) {
143 	for ( ac=sf->anchor; ac!=NULL; ac=ac->next ) if ( ac->subtable==sub ) {
144 	    for ( ap=sc->anchor; ap!=NULL && (ap->type!=at_baselig || ap->anchor!=ac || ap->lig_index!=k); ap=ap->next );
145 	    if ( ap!=NULL ) {
146 		sprintf(buf,_("Component %d %.30s (%d,%d)"),
147 			k, ac->name, (int) ap->me.x, (int) ap->me.y );
148 		node->children[j].label = copy(buf);
149 		node->children[j++].parent = node;
150 	    }
151 	}
152     }
153     node->cnt = j;
154 }
155 
BuildMarkedChars(struct node * node,struct att_dlg * att)156 static void BuildMarkedChars(struct node *node,struct att_dlg *att) {
157     SplineChar *sc = node->u.sc;
158     struct lookup_subtable *sub = node->parent->parent->u.sub;
159     AnchorClass *ac;
160     int classcnt, j;
161     AnchorPoint *ap;
162     char buf[80];
163     SplineFont *sf = att->sf;
164 
165     if ( sf->cidmaster!=NULL ) sf = sf->cidmaster;
166 
167     classcnt = 0;
168     for ( ap=sc->anchor; ap!=NULL; ap=ap->next )
169 	if ( ap->anchor->subtable == sub )
170 	    ++classcnt;
171     node->children = calloc(classcnt+1,sizeof(struct node));
172     for ( j=0, ac=sf->anchor; ac!=NULL; ac=ac->next ) if ( ac->subtable==sub ) {
173 	for ( ap=sc->anchor; ap!=NULL && (!(ap->type==at_basechar || ap->type==at_basemark) || ap->anchor!=ac); ap=ap->next );
174 	if ( ap!=NULL ) {
175 	    sprintf(buf,"%.30s (%d,%d)", ac->name,
176 		    (int) ap->me.x, (int) ap->me.y );
177 	    node->children[j].label = copy(buf);
178 	    node->children[j++].parent = node;
179 	}
180     }
181     qsort(node->children,j,sizeof(struct node), node_alphabetize);
182     node->cnt = j;
183 }
184 
BuildBase(struct node * node,SplineChar ** bases,enum anchor_type at,struct node * parent)185 static void BuildBase(struct node *node,SplineChar **bases,enum anchor_type at, struct node *parent) {
186     int i;
187 
188     node->parent = parent;
189     node->label = copy(at==at_basechar?_("Base Glyphs"):
190 			at==at_baselig?_("Base Ligatures"):
191 			_("Base Marks"));
192     for ( i=0; bases[i]!=NULL; ++i );
193     if ( i==0 ) {
194 	node->cnt = 1;
195 	node->children = calloc(2,sizeof(struct node));
196 	node->children[0].label = copy(_("Empty"));
197 	node->children[0].parent = node;
198     } else {
199 	node->cnt = i;
200 	node->children = calloc(i+1,sizeof(struct node));
201 	for ( i=0; bases[i]!=NULL; ++i ) {
202 	    node->children[i].label = copy(bases[i]->name);
203 	    node->children[i].parent = node;
204 	    node->children[i].u.sc = bases[i];
205 	    node->children[i].build = at==at_baselig?BuildMarkedLigatures:BuildMarkedChars;
206 	}
207 	qsort(node->children,node->cnt,sizeof(struct node), node_alphabetize);
208     }
209 }
210 
BuildMark(struct node * node,SplineChar ** marks,AnchorClass * ac,struct node * parent)211 static void BuildMark(struct node *node,SplineChar **marks,AnchorClass *ac, struct node *parent) {
212     int i;
213     char buf[80];
214     AnchorPoint *ap;
215 
216     node->parent = parent;
217     sprintf(buf,_("Mark Class %.20s"),ac->name);
218     node->label = copy(buf);
219     for ( i=0; marks[i]!=NULL; ++i );
220     if ( i==0 ) {
221 	node->cnt = 1;
222 	node->children = calloc(2,sizeof(struct node));
223 	node->children[0].label = copy(_("Empty"));
224 	node->children[0].parent = node;
225     } else {
226 	node->cnt = i;
227 	node->children = calloc(i+1,sizeof(struct node));
228 	for ( i=0; marks[i]!=NULL; ++i ) {
229 	    for ( ap=marks[i]->anchor; ap!=NULL && (ap->type!=at_mark || ap->anchor!=ac); ap=ap->next );
230 	    sprintf(buf,_("%.30s (%d,%d)"), marks[i]->name,
231 		    (int) ap->me.x, (int) ap->me.y );
232 	    node->children[i].label = copy(buf);
233 	    node->children[i].parent = node;
234 	}
235 	qsort(node->children,node->cnt,sizeof(struct node), node_alphabetize);
236     }
237 }
238 
BuildAnchorLists(struct node * node,struct att_dlg * att)239 static void BuildAnchorLists(struct node *node,struct att_dlg *att) {
240     struct lookup_subtable *sub = node->u.sub;
241     AnchorClass *ac, *ac2;
242     int cnt, i, j, classcnt;
243     AnchorPoint *ap, *ent, *ext;
244     SplineChar **base, **lig, **mkmk, **entryexit;
245     SplineChar ***marks;
246     int *subcnts;
247     SplineFont *sf = att->sf;
248     char buf[80];
249 
250     if ( sf->cidmaster!=NULL ) sf = sf->cidmaster;
251 
252     if ( sub->lookup->lookup_type==gpos_cursive ) {
253 	for ( ac = sf->anchor; ac!=NULL && ac->subtable!=sub; ac = ac->next );
254 	entryexit = ac==NULL ? NULL : EntryExitDecompose(sf,ac,NULL);
255 	if ( entryexit==NULL ) {
256 	    node->children = calloc(2,sizeof(struct node));
257 	    node->cnt = 1;
258 	    node->children[0].label = copy(_("Empty"));
259 	    node->children[0].parent = node;
260 	} else {
261 	    for ( cnt=0; entryexit[cnt]!=NULL; ++cnt );
262 	    node->children = calloc(cnt+1,sizeof(struct node));
263 	    node->cnt = cnt;
264 	    for ( cnt=0; entryexit[cnt]!=NULL; ++cnt ) {
265 		node->children[cnt].u.sc = entryexit[cnt];
266 		node->children[cnt].label = copy(entryexit[cnt]->name);
267 		node->children[cnt].parent = node;
268 	    }
269 	    qsort(node->children,node->cnt,sizeof(struct node), node_alphabetize);
270 	    for ( cnt=0; entryexit[cnt]!=NULL; ++cnt ) {
271 		for ( ent=ext=NULL, ap = node->children[cnt].u.sc->anchor; ap!=NULL; ap=ap->next ) {
272 		    if ( ap->anchor==ac ) {
273 			if ( ap->type == at_centry )
274 			    ent = ap;
275 			else if ( ap->type == at_cexit )
276 			    ent = ap;
277 		    }
278 		}
279 		node->children[cnt].cnt = (ent!=NULL)+(ext!=NULL);
280 		node->children[cnt].children = calloc((1+(ent!=NULL)+(ext!=NULL)),sizeof(struct node));
281 		i = 0;
282 		if ( ent!=NULL ) {
283 		    snprintf(buf,sizeof(buf), _("Entry (%d,%d)"),
284 			    (int) ent->me.x, (int) ent->me.y);
285 		    node->children[cnt].children[i].label = copy(buf);
286 		    node->children[cnt].children[i].parent = &node->children[cnt];
287 		    ++i;
288 		}
289 		if ( ext!=NULL ) {
290 		    snprintf(buf,sizeof(buf), _("Exit (%d,%d)"),
291 			    (int) ext->me.x, (int) ext->me.y);
292 		    node->children[cnt].children[i].label = copy(buf);
293 		    node->children[cnt].children[i].parent = &node->children[cnt];
294 		    ++i;
295 		}
296 	    }
297 	}
298 	free(entryexit);
299     } else {
300 	classcnt = 0;
301 	ac = NULL;
302 	for ( ac2=sf->anchor; ac2!=NULL; ac2=ac2->next ) {
303 	    if ( ac2->subtable == sub ) {
304 		if ( ac==NULL ) ac=ac2;
305 		ac2->matches = true;
306 		++classcnt;
307 	    } else
308 		ac2->matches = false;
309 	}
310 	if ( classcnt==0 )
311 return;
312 
313 	marks = malloc(classcnt*sizeof(SplineChar **));
314 	subcnts = malloc(classcnt*sizeof(int));
315 	AnchorClassDecompose(sf,ac,classcnt,subcnts,marks,&base,&lig,&mkmk,NULL);
316 	node->cnt = classcnt+(base!=NULL)+(lig!=NULL)+(mkmk!=NULL);
317 	node->children = calloc(node->cnt+1,sizeof(struct node));
318 	i=0;
319 	if ( base!=NULL )
320 	    BuildBase(&node->children[i++],base,at_basechar,node);
321 	if ( lig!=NULL )
322 	    BuildBase(&node->children[i++],lig,at_baselig,node);
323 	if ( mkmk!=NULL )
324 	    BuildBase(&node->children[i++],mkmk,at_basemark,node);
325 	for ( j=0, ac2=ac; j<classcnt; ac2=ac2->next ) if ( ac2->matches ) {
326 	    if ( marks[j]!=NULL )
327 		BuildMark(&node->children[i++],marks[j],ac2,node);
328 	    ++j;
329 	}
330 	node->cnt = i;
331 	for ( i=0; i<classcnt; ++i )
332 	    free(marks[i]);
333 	free(marks);
334 	free(subcnts);
335 	free(base);
336 	free(lig);
337 	free(mkmk);
338     }
339 }
340 
BuildKC2(struct node * node,struct att_dlg * att)341 static void BuildKC2(struct node *node,struct att_dlg *att) {
342     KernClass *kc = node->parent->u.sub->kc;
343     struct node *seconds;
344     int index=node->u.index,i,cnt, len;
345     char buf[32];
346     char *name;
347 
348     for ( i=1,cnt=0; i<kc->second_cnt; ++i )
349 	if ( kc->offsets[index*kc->second_cnt+i]!=0 && strlen(kc->seconds[i])!=0 )
350 	    ++cnt;
351 
352     node->children = seconds = calloc(cnt+1,sizeof(struct node));
353     node->cnt = cnt;
354     cnt = 0;
355     for ( i=1; i<kc->second_cnt; ++i ) if ( kc->offsets[index*kc->second_cnt+i]!=0 && strlen(kc->seconds[i])!=0 ) {
356 	sprintf( buf, "%d ", kc->offsets[index*kc->second_cnt+i]);
357 	len = strlen(buf)+strlen(kc->seconds[i])+1;
358 	name = malloc(len);
359 	strcpy(name,buf);
360 	strcat(name,kc->seconds[i]);
361 	seconds[cnt].label = name;
362 	seconds[cnt].parent = node;
363 	seconds[cnt].build = NULL;
364 	seconds[cnt++].u.index = i;
365     }
366 }
367 
BuildKC(struct node * node,struct att_dlg * att)368 static void BuildKC(struct node *node,struct att_dlg *att) {
369     KernClass *kc = node->u.sub->kc;
370     struct node *firsts;
371     int i,j,cnt,cnt2;
372 
373     for ( i=1,cnt=0; i<kc->first_cnt; ++i ) {
374 	for ( j=1,cnt2=0 ; j<kc->second_cnt; ++j ) {
375 	    if ( kc->offsets[i*kc->second_cnt+j]!=0 )
376 		++cnt2;
377 	}
378 	if ( cnt2 && strlen(kc->firsts[i])>0 )
379 	    ++cnt;
380     }
381 
382     node->children = firsts = calloc(cnt+1,sizeof(struct node));
383     node->cnt = cnt;
384     for ( i=1,cnt=0; i<kc->first_cnt; ++i ) {
385 	for ( j=1,cnt2=0 ; j<kc->second_cnt; ++j ) {
386 	    if ( kc->offsets[i*kc->second_cnt+j]!=0 )
387 		++cnt2;
388 	}
389 	if ( cnt2==0 || strlen(kc->firsts[i])==0 )
390     continue;
391 	firsts[cnt].label = copy(kc->firsts[i]);
392 	firsts[cnt].parent = node;
393 	firsts[cnt].build = BuildKC2;
394 	firsts[cnt++].u.index = i;
395     }
396 }
397 
PSTAllComponentsExist(SplineFont * sf,char * glyphnames)398 static int PSTAllComponentsExist(SplineFont *sf,char *glyphnames ) {
399     char *start, *end, ch;
400     int ret;
401 
402     if ( glyphnames==NULL )
403 return( false );
404     for ( start=glyphnames; *start; start = end ) {
405 	while ( *start==' ' ) ++start;
406 	for ( end = start; *end!=' ' && *end!='\0'; ++end );
407 	if ( end==start )
408     break;
409 	ch = *end; *end = '\0';
410 	ret = SCWorthOutputting(SFGetChar(sf,-1,start));
411 	*end = ch;
412 	if ( !ret )
413 return( false );
414     }
415 return( true );
416 }
417 
BuildFPSTRule(struct node * node,struct att_dlg * att)418 static void BuildFPSTRule(struct node *node,struct att_dlg *att) {
419     FPST *fpst = node->parent->u.sub->fpst;
420     int index = node->u.index;
421     struct fpst_rule *r = &fpst->rules[index];
422     int len, i, j;
423     struct node *lines;
424     char buf[200], *pt, *start, *spt;
425     char *upt;
426     GrowBuf gb;
427 
428     memset(&gb,0,sizeof(gb));
429 
430     for ( i=0; i<2; ++i ) {
431 	len = 0;
432 
433 	switch ( fpst->format ) {
434 	  case pst_glyphs:
435 	    if ( r->u.glyph.back!=NULL && *r->u.glyph.back!='\0' ) {
436 		if ( i ) {
437 		    strcpy(buf, _("Backtrack Match: ") );
438 		    lines[len].label = malloc((strlen(buf)+strlen(r->u.glyph.back)+1));
439 		    strcpy(lines[len].label,buf);
440 		    upt = lines[len].label+strlen(lines[len].label);
441 		    for ( pt=r->u.glyph.back+strlen(r->u.glyph.back); pt>r->u.glyph.back; pt=start ) {
442 			for ( start = pt-1; start>=r->u.glyph.back&& *start!=' '; --start );
443 			for ( spt=start+1; spt<pt; )
444 			    *upt++ = *spt++;
445 			*upt++ = ' ';
446 		    }
447 		    upt[-1] = '\0';
448 		    lines[len].parent = node;
449 		}
450 		++len;
451 	    }
452 	    if ( i ) {
453 		strcpy(buf, _("Match: ") );
454 		lines[len].label = malloc((strlen(buf)+strlen(r->u.glyph.names)+1));
455 		strcpy(lines[len].label,buf);
456 		strcat(lines[len].label,r->u.glyph.names);
457 		lines[len].parent = node;
458 	    }
459 	    ++len;
460 	    if ( r->u.glyph.fore!=NULL && *r->u.glyph.fore!='\0' ) {
461 		if ( i ) {
462 		    strcpy(buf, _("Lookahead Match: ") );
463 		    lines[len].label = malloc((strlen(buf)+strlen(r->u.glyph.fore)+1));
464 		    strcpy(lines[len].label,buf);
465 		    strcat(lines[len].label,r->u.glyph.fore);
466 		    lines[len].parent = node;
467 		}
468 		++len;
469 	    }
470 	  break;
471 	  case pst_class:
472 	    if ( r->u.class.bcnt!=0 ) {
473 		if ( i ) {
474 		    gb.pt = gb.base;
475 		    GrowBufferAddStr(&gb,P_("Backtrack class: ","Backtrack classes: ",r->u.class.bcnt));
476 		    for ( j=r->u.class.bcnt-1; j>=0; --j ) {
477 			if ( fpst->bclassnames==NULL || fpst->bclassnames[r->u.class.bclasses[j]]==NULL ) {
478 			    sprintf( buf, "%d ", r->u.class.bclasses[j] );
479 			    GrowBufferAddStr(&gb,buf);
480 			} else
481 			    GrowBufferAddStr(&gb,fpst->bclassnames[r->u.class.bclasses[j]]);
482 		    }
483 		    lines[len].label = copy(gb.base);
484 		    lines[len].parent = node;
485 		}
486 		++len;
487 	    }
488 	    if ( i ) {
489 		gb.pt = gb.base;
490 		GrowBufferAddStr(&gb, P_("Class","Classes",r->u.class.ncnt));
491 		for ( j=0; j<r->u.class.ncnt; ++j ) {
492 		    if ( fpst->nclassnames==NULL || fpst->nclassnames[r->u.class.nclasses[j]]==NULL ) {
493 			sprintf( buf, "%d ", r->u.class.nclasses[j] );
494 			GrowBufferAddStr(&gb,buf);
495 		    } else
496 			GrowBufferAddStr(&gb,fpst->nclassnames[r->u.class.nclasses[j]]);
497 		}
498 		lines[len].label = copy(gb.base);
499 		lines[len].parent = node;
500 	    }
501 	    ++len;
502 	    if ( r->u.class.fcnt!=0 ) {
503 		if ( i ) {
504 		    gb.pt = gb.base;
505 		    GrowBufferAddStr(&gb, P_("Lookahead Class","Lookahead Classes",r->u.class.fcnt));
506 		    for ( j=0; j<r->u.class.fcnt; ++j ) {
507 			if ( fpst->fclassnames==NULL || fpst->fclassnames[r->u.class.fclasses[j]]==NULL ) {
508 			    sprintf( buf, "%d ", r->u.class.fclasses[j] );
509 			    GrowBufferAddStr(&gb,buf);
510 			} else
511 			    GrowBufferAddStr(&gb,fpst->fclassnames[r->u.class.fclasses[j]]);
512 		    }
513 		    lines[len].label = copy(gb.base);
514 		    lines[len].parent = node;
515 		}
516 		++len;
517 	    }
518 	  break;
519 	  case pst_coverage:
520 	  case pst_reversecoverage:
521 	    for ( j=r->u.coverage.bcnt-1; j>=0; --j ) {
522 		if ( i ) {
523 		    sprintf(buf, _("Back coverage %d: "), -j-1);
524 		    lines[len].label = malloc((strlen(buf)+strlen(r->u.coverage.bcovers[j])+1));
525 		    strcpy(lines[len].label,buf);
526 		    strcat(lines[len].label,r->u.coverage.bcovers[j]);
527 		    lines[len].parent = node;
528 		}
529 		++len;
530 	    }
531 	    for ( j=0; j<r->u.coverage.ncnt; ++j ) {
532 		if ( i ) {
533 		    sprintf(buf, _("Coverage %d: "), j);
534 		    lines[len].label = malloc((strlen(buf)+strlen(r->u.coverage.ncovers[j])+1));
535 		    strcpy(lines[len].label,buf);
536 		    strcat(lines[len].label,r->u.coverage.ncovers[j]);
537 		    lines[len].parent = node;
538 		}
539 		++len;
540 	    }
541 	    for ( j=0; j<r->u.coverage.fcnt; ++j ) {
542 		if ( i ) {
543 		    sprintf(buf, _("Lookahead coverage %d: "), j+r->u.coverage.ncnt);
544 		    lines[len].label = malloc((strlen(buf)+strlen(r->u.coverage.fcovers[j])+1));
545 		    strcpy(lines[len].label,buf);
546 		    strcat(lines[len].label,r->u.coverage.fcovers[j]);
547 		    lines[len].parent = node;
548 		}
549 		++len;
550 	    }
551 	  break;
552 	}
553 	switch ( fpst->format ) {
554 	  case pst_glyphs:
555 	  case pst_class:
556 	  case pst_coverage:
557 	    for ( j=0; j<r->lookup_cnt; ++j ) {
558 		if ( i ) {
559 		    sprintf(buf, _("Apply at %d %.80s"), r->lookups[j].seq,
560 			    r->lookups[j].lookup->lookup_name );
561 		    lines[len].label = copy(buf);
562 		    lines[len].parent = node;
563 		    lines[len].u.otl = r->lookups[j].lookup;
564 		    lines[len].build = BuildGSUBlookups;
565 		}
566 		++len;
567 	    }
568 	  break;
569 	  case pst_reversecoverage:
570 	    if ( i ) {
571 		strcpy(buf, _("Replacement: ") );
572 		lines[len].label = malloc((strlen(buf)+strlen(r->u.rcoverage.replacements)+1));
573 		strcpy(lines[len].label,buf);
574 		strcat(lines[len].label,r->u.rcoverage.replacements);
575 		lines[len].parent = node;
576 	    }
577 	    ++len;
578 	  break;
579 	}
580 	if ( i==0 ) {
581 	    node->children = lines = calloc(len+1,sizeof(struct node));
582 	    node->cnt = len;
583 	}
584     }
585     free(gb.base);
586 }
587 
BuildFPST(struct node * node,struct att_dlg * att)588 static void BuildFPST(struct node *node,struct att_dlg *att) {
589     FPST *fpst = node->u.sub->fpst;
590     int len, i, j;
591     struct node *lines;
592     char buf[200];
593     static char *type[] = { N_("Contextual Positioning"), N_("Contextual Substitution"),
594 	    N_("Chaining Positioning"), N_("Chaining Substitution"),
595 	    N_("Reverse Chaining Subs") };
596     static char *format[] = { N_("glyphs"), N_("classes"), N_("coverage"), N_("coverage") };
597 
598     lines = NULL;
599     for ( i=0; i<2; ++i ) {
600 	len = 0;
601 
602 	if ( i ) {
603 /* GT: There are various broad classes of lookups here and the first string */
604 /* GT: describes those: "Contextual Positioning", Contextual Substitution", etc. */
605 /* GT: Each of those may be formated in 3 different ways: by (or perhaps using */
606 /* GT: would be a better word) glyphs, classes or coverage tables. */
607 /* GT: So this might look like: */
608 /* GT:  Contextual Positioning by classes */
609 	    sprintf(buf, _("%s by %s"), _(type[fpst->type-pst_contextpos]),
610 		    _(format[fpst->format]));
611 	    lines[len].label = copy(buf);
612 	    lines[len].parent = node;
613 	}
614 	++len;
615 	if ( fpst->format==pst_class ) {
616 	    for ( j=1; j<fpst->bccnt ; ++j ) {
617 		if ( i ) {
618 		    sprintf(buf, _("Backtrack class %d: "), j);
619 		    lines[len].label = malloc((strlen(buf)+strlen(fpst->bclass[j])+1));
620 		    strcpy(lines[len].label,buf);
621 		    strcat(lines[len].label,fpst->bclass[j]);
622 		    lines[len].parent = node;
623 		}
624 		++len;
625 	    }
626 	    for ( j=1; j<fpst->nccnt ; ++j ) {
627 		if ( i ) {
628 		    sprintf(buf, _("Class %d: "), j);
629 		    lines[len].label = malloc((strlen(buf)+strlen(fpst->nclass[j])+1));
630 		    strcpy(lines[len].label,buf);
631 		    strcat(lines[len].label,fpst->nclass[j]);
632 		    lines[len].parent = node;
633 		}
634 		++len;
635 	    }
636 	    for ( j=1; j<fpst->fccnt ; ++j ) {
637 		if ( i ) {
638 		    sprintf(buf, _("Lookahead class %d: "), j);
639 		    lines[len].label = malloc((strlen(buf)+strlen(fpst->fclass[j])+1));
640 		    strcpy(lines[len].label,buf);
641 		    strcat(lines[len].label,fpst->fclass[j]);
642 		    lines[len].parent = node;
643 		}
644 		++len;
645 	    }
646 	}
647 	for ( j=0; j<fpst->rule_cnt; ++j ) {
648 	    if ( i ) {
649 		sprintf(buf, _("Rule %d"), j);
650 		lines[len].label = copy(buf);
651 		lines[len].parent = node;
652 		lines[len].u.index = j;
653 		lines[len].build = BuildFPSTRule;
654 	    }
655 	    ++len;
656 	}
657 	if ( i==0 ) {
658 	    node->children = lines = calloc(len+1,sizeof(struct node));
659 	    node->cnt = len;
660 	}
661     }
662 }
663 
BuildASM(struct node * node,struct att_dlg * att)664 static void BuildASM(struct node *node,struct att_dlg *att) {
665     ASM *sm = node->u.sub->sm;
666     int len, i, j, k, scnt = 0;
667     struct node *lines;
668     char buf[200], *space;
669     static char *type[] = { N_("Indic Reordering"), N_("Contextual Substitution"),
670 	    N_("Ligatures"), N_("<undefined>"), N_("Simple Substitution"),
671 	    N_("Glyph Insertion"), N_("<undefined>"),  N_("<undefined>"), N_("<undefined>"),
672 	    N_("<undefined>"), N_("<undefined>"), N_("<undefined>"),N_("<undefined>"),
673 	    N_("<undefined>"), N_("<undefined>"), N_("<undefined>"), N_("<undefined>"),
674 	    N_("Kern by State") };
675     OTLookup **used;
676 
677     if ( sm->type == asm_context ) {
678 	used = malloc(sm->class_cnt*sm->state_cnt*2*sizeof(OTLookup *));
679 	for ( i=scnt=0; i<sm->class_cnt*sm->state_cnt; ++i ) {
680 	    OTLookup *otl;
681 	    otl = sm->state[i].u.context.mark_lookup;
682 	    if ( otl!=NULL ) {
683 		for ( k=0; k<scnt && used[k]!=otl; ++k );
684 		if ( k==scnt ) used[scnt++] = otl;
685 	    }
686 	    otl = sm->state[i].u.context.cur_lookup;
687 	    if ( otl!=NULL ) {
688 		for ( k=0; k<scnt && used[k]!=otl; ++k );
689 		if ( k==scnt ) used[scnt++] = otl;
690 	    }
691 	}
692     }
693 
694     lines = NULL;
695     space = malloc( 81*sm->class_cnt+40 );
696     for ( i=0; i<2; ++i ) {
697 	len = 0;
698 
699 	if ( i ) {
700 	    lines[len].label = copy(_(type[sm->type]));
701 	    lines[len].parent = node;
702 	}
703 	++len;
704 	for ( j=4; j<sm->class_cnt ; ++j ) {
705 	    if ( i ) {
706 		sprintf(buf, _("Class %d: "), j);
707 		lines[len].label = malloc((strlen(buf)+strlen(sm->classes[j])+1));
708 		strcpy(lines[len].label,buf);
709 		strcat(lines[len].label,sm->classes[j]);
710 		lines[len].parent = node;
711 	    }
712 	    ++len;
713 	}
714 	for ( j=0; j<sm->state_cnt; ++j ) {
715 	    if ( i ) {
716 /* GT: You're in a state machine, and this is describing the %4d'th state of */
717 /* GT: that machine. From the state the next state will be a list of */
718 /* GT: state-numbers which are appended to this string. */
719 		sprintf(space, _("State %4d Next: "), j );
720 		for ( k=0; k<sm->class_cnt; ++k )
721 		    sprintf( space+strlen(space), "%5d", sm->state[j*sm->class_cnt+k].next_state );
722 		lines[len].label = copy(space);
723 		lines[len].parent = node;
724 		lines[len].monospace = true;
725 	    }
726 	    ++len;
727 	    if ( i ) {
728 		sprintf(space, _("State %4d Flags:"), j );
729 		for ( k=0; k<sm->class_cnt; ++k )
730 		    sprintf( space+strlen(space), " %04x", sm->state[j*sm->class_cnt+k].flags );
731 		lines[len].label = copy(space);
732 		lines[len].parent = node;
733 		lines[len].monospace = true;
734 	    }
735 	    ++len;
736 	    if ( sm->type==asm_context ) {
737 		if ( i ) {
738 		    sprintf(space, _("State %4d Mark: "), j );
739 		    for ( k=0; k<sm->class_cnt; ++k )
740 			if ( sm->state[j*sm->class_cnt+k].u.context.mark_lookup==NULL )
741 			    strcat(space,"     ");
742 			else
743 			    sprintf( space+strlen(space), " %.80s", sm->state[j*sm->class_cnt+k].u.context.mark_lookup->lookup_name );
744 		    lines[len].label = copy(space);
745 		    lines[len].parent = node;
746 		    lines[len].monospace = true;
747 		}
748 		++len;
749 		if ( i ) {
750 		    sprintf(space, _("State %4d Cur:  "), j );
751 		    for ( k=0; k<sm->class_cnt; ++k )
752 			if ( sm->state[j*sm->class_cnt+k].u.context.cur_lookup==NULL )
753 			    strcat(space,"     ");
754 			else
755 			    sprintf( space+strlen(space), " %.80s", sm->state[j*sm->class_cnt+k].u.context.cur_lookup->lookup_name );
756 		    lines[len].label = copy(space);
757 		    lines[len].parent = node;
758 		    lines[len].monospace = true;
759 		}
760 		++len;
761 	    }
762 	}
763 	for ( j=0; j<scnt; ++j ) {
764 	    if ( i ) {
765 		sprintf(buf, _("Nested Substitution %.80s"), used[j]->lookup_name );
766 		lines[len].label = copy(buf);
767 		lines[len].parent = node;
768 		lines[len].u.otl = used[j];
769 		lines[len].build = BuildGSUBlookups;
770 	    }
771 	    ++len;
772 	}
773 	if ( i==0 ) {
774 	    node->children = lines = calloc(len+1,sizeof(struct node));
775 	    node->cnt = len;
776 	}
777     }
778     free(space);
779     free(used);
780 }
781 
BuildKern2(struct node * node,struct att_dlg * att)782 static void BuildKern2(struct node *node,struct att_dlg *att) {
783     struct lookup_subtable *sub = node->parent->u.sub;
784     SplineChar *base = node->u.sc;
785     SplineFont *_sf = att->sf;
786     int doit, cnt;
787     int isv;
788     PST *pst;
789     KernPair *kp;
790     char buffer[200];
791     struct node *lines;
792 
793     for ( doit = 0; doit<2; ++doit ) {
794 	cnt = 0;
795 	for ( pst=base->possub; pst!=NULL; pst = pst->next ) {
796 	    if ( pst->subtable==sub && pst->type==pst_pair &&
797 		    PSTAllComponentsExist(_sf,pst->u.pair.paired)) {
798 		if ( doit ) {
799 		    sprintf(buffer, "%.80s ", pst->u.pair.paired );
800 		    if ( pst->u.pair.vr[0].xoff!=0 ||
801 			    /* If everything is 0, we'll want to display something */
802 			    /* might as well be this */
803 			    ( pst->u.pair.vr[0].yoff == 0 &&
804 			      pst->u.pair.vr[0].h_adv_off ==0 &&
805 			      pst->u.pair.vr[0].v_adv_off ==0 &&
806 			      pst->u.pair.vr[1].xoff ==0 &&
807 			      pst->u.pair.vr[1].yoff ==0 &&
808 			      pst->u.pair.vr[1].h_adv_off ==0 &&
809 			      pst->u.pair.vr[1].v_adv_off == 0 ))
810 			sprintf( buffer+strlen(buffer), " ∆x¹=%d", pst->u.pair.vr[0].xoff );
811 		    if ( pst->u.pair.vr[0].yoff!=0 )
812 			sprintf( buffer+strlen(buffer), " ∆y¹=%d", pst->u.pair.vr[0].yoff );
813 		    if ( pst->u.pair.vr[0].h_adv_off!=0 )
814 			sprintf( buffer+strlen(buffer), " ∆x_adv¹=%d", pst->u.pair.vr[0].h_adv_off );
815 		    if ( pst->u.pair.vr[0].v_adv_off!=0 )
816 			sprintf( buffer+strlen(buffer), " ∆y_adv¹=%d", pst->u.pair.vr[0].v_adv_off );
817 		    if ( pst->u.pair.vr[1].xoff!=0 )
818 			sprintf( buffer+strlen(buffer), " ∆x²=%d", pst->u.pair.vr[1].xoff );
819 		    if ( pst->u.pair.vr[1].yoff!=0 )
820 			sprintf( buffer+strlen(buffer), " ∆y²=%d", pst->u.pair.vr[1].yoff );
821 		    if ( pst->u.pair.vr[1].h_adv_off!=0 )
822 			sprintf( buffer+strlen(buffer), " ∆x_adv²=%d", pst->u.pair.vr[1].h_adv_off );
823 		    if ( pst->u.pair.vr[1].v_adv_off!=0 )
824 			sprintf( buffer+strlen(buffer), " ∆y_adv²=%d", pst->u.pair.vr[1].v_adv_off );
825 		    lines[cnt].label = copy(buffer);
826 		    lines[cnt].parent = node;
827 		}
828 		++cnt;
829 	    }
830 	}
831 	for ( isv=0; isv<2 ; ++isv ) {
832 	    for ( kp= isv ? base->vkerns : base->kerns ; kp!=NULL; kp=kp->next ) {
833 		if ( kp->subtable==sub ) {
834 		    if ( doit ) {
835 			if ( isv )
836 			    sprintf( buffer, "%.80s  ∆y_adv¹=%d", kp->sc->name, kp->off );
837 			else if ( sub->lookup->lookup_flags&pst_r2l )
838 			    sprintf( buffer, "%.80s  ∆x_adv²=%d", kp->sc->name, kp->off );
839 			else
840 			    sprintf( buffer, "%.80s  ∆x_adv¹=%d", kp->sc->name, kp->off );
841 			lines[cnt].label = copy(buffer);
842 			lines[cnt].parent = node;
843 		    }
844 		    ++cnt;
845 		}
846 	    }
847 	}
848 	if ( !doit ) {
849 	    node->children = lines = calloc(cnt+1,sizeof(struct node));
850 	    node->cnt = cnt;
851 	} else
852 	    qsort(lines,cnt,sizeof(struct node), node_alphabetize);
853     }
854 }
855 
BuildKern(struct node * node,struct att_dlg * att)856 static void BuildKern(struct node *node,struct att_dlg *att) {
857     struct lookup_subtable *sub = node->u.sub;
858     SplineFont *_sf = att->sf, *sf;
859     int k, gid, doit, cnt, maxc, isv;
860     SplineChar *sc;
861     PST *pst;
862     KernPair *kp;
863     struct node *lines;
864 
865     if ( _sf->cidmaster!=NULL ) _sf = _sf->cidmaster;
866 
867     k=maxc=0;
868     do {
869 	sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[k];
870 	if ( sf->glyphcnt>maxc ) maxc = sf->glyphcnt;
871 	++k;
872     } while ( k<_sf->subfontcnt );
873 
874     for ( doit = 0; doit<2; ++doit ) {
875 	cnt = 0;
876 	for ( gid=0; gid<maxc; ++gid ) {
877 	    k=0;
878 	    sc = NULL;
879 	    do {
880 		sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[k];
881 		if ( gid<sf->glyphcnt && sf->glyphs[gid]!=NULL ) {
882 		    sc = sf->glyphs[gid];
883 	    break;
884 		}
885 		++k;
886 	    } while ( k<_sf->subfontcnt );
887 	    if ( sc!=NULL ) {
888 		int found = false;
889 		for ( pst=sc->possub; pst!=NULL; pst = pst->next ) {
890 		    if ( pst->subtable==sub ) {
891 			found = true;
892 		break;
893 		    }
894 		}
895 		for ( isv=0; isv<2 && !found; ++isv ) {
896 		    for ( kp= isv ? sc->vkerns : sc->kerns ; kp!=NULL; kp=kp->next ) {
897 			if ( kp->subtable==sub ) {
898 			    found = true;
899 		    break;
900 			}
901 		    }
902 		}
903 		if ( found ) {
904 		    if ( doit ) {
905 			lines[cnt].label = copy(sc->name);
906 			lines[cnt].parent = node;
907 			lines[cnt].build = BuildKern2;
908 			lines[cnt].u.sc = sc;
909 		    }
910 		    ++cnt;
911 		}
912 	    }
913 	}
914 	if ( !doit ) {
915 	    node->children = lines = calloc(cnt+1,sizeof(struct node));
916 	    node->cnt = cnt;
917 	} else
918 	    qsort(lines,cnt,sizeof(struct node), node_alphabetize);
919     }
920 }
921 
BuildPST(struct node * node,struct att_dlg * att)922 static void BuildPST(struct node *node,struct att_dlg *att) {
923     struct lookup_subtable *sub = node->u.sub;
924     SplineFont *_sf = att->sf, *sf;
925     int k, gid, doit, cnt, maxl, len, maxc;
926     SplineChar *sc;
927     PST *pst;
928     struct node *lines;
929     char *lbuf=NULL;
930 
931     if ( _sf->cidmaster!=NULL ) _sf = _sf->cidmaster;
932 
933     k=maxc=0;
934     do {
935 	sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[k];
936 	if ( sf->glyphcnt>maxc ) maxc = sf->glyphcnt;
937 	++k;
938     } while ( k<_sf->subfontcnt );
939 
940     for ( doit = 0; doit<2; ++doit ) {
941 	cnt = maxl = 0;
942 	for ( gid=0; gid<maxc; ++gid ) {
943 	    k=0;
944 	    sc = NULL;
945 	    do {
946 		sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[k];
947 		if ( gid<sf->glyphcnt && sf->glyphs[gid]!=NULL ) {
948 		    sc = sf->glyphs[gid];
949 	    break;
950 		}
951 		++k;
952 	    } while ( k<_sf->subfontcnt );
953 	    if ( sc!=NULL ) {
954 		for ( pst=sc->possub; pst!=NULL; pst = pst->next ) {
955 		    if ( pst->subtable==sub ) {
956 			if ( doit ) {
957 			    if ( pst->type==pst_position )
958 				sprintf(lbuf,"%s ∆x=%d ∆y=%d ∆x_adv=%d ∆y_adv=%d",
959 					sc->name,
960 					pst->u.pos.xoff, pst->u.pos.yoff,
961 					pst->u.pos.h_adv_off, pst->u.pos.v_adv_off );
962 			    else
963 				sprintf(lbuf, "%s %s %s", sc->name,
964 					pst->type==pst_ligature ? "<=" : "=>",
965 					pst->u.subs.variant );
966 			    lines[cnt].label = copy(lbuf);
967 			    lines[cnt].parent = node;
968 			} else {
969 			    if ( pst->type==pst_position )
970 				len = strlen(sc->name)+40;
971 			    else
972 				len = strlen(sc->name)+strlen(pst->u.subs.variant)+8;
973 			    if ( len>maxl ) maxl = len;
974 			}
975 			++cnt;
976 		    }
977 		}
978 	    }
979 	}
980 	if ( !doit ) {
981 	    lbuf = malloc(maxl*sizeof(unichar_t));
982 	    node->children = lines = calloc(cnt+1,sizeof(struct node));
983 	    node->cnt = cnt;
984 	} else
985 	    qsort(lines,cnt,sizeof(struct node), node_alphabetize);
986     }
987     free(lbuf);
988 }
989 
BuildSubtableDispatch(struct node * node,struct att_dlg * att)990 static void BuildSubtableDispatch(struct node *node,struct att_dlg *att) {
991     struct lookup_subtable *sub = node->u.sub;
992     int lookup_type = sub->lookup->lookup_type;
993 
994     switch ( lookup_type ) {
995       case gpos_context: case gpos_contextchain:
996       case gsub_context: case gsub_contextchain: case gsub_reversecchain:
997 	BuildFPST(node,att);
998 return;
999       case gpos_cursive: case gpos_mark2base: case gpos_mark2ligature: case gpos_mark2mark:
1000 	BuildAnchorLists(node,att);
1001 return;
1002       case gpos_pair:
1003 	if ( sub->kc!=NULL )
1004 	    BuildKC(node,att);
1005 	else
1006 	    BuildKern(node,att);
1007 return;
1008       case gpos_single:
1009       case gsub_single: case gsub_multiple: case gsub_alternate: case gsub_ligature:
1010 	BuildPST(node,att);
1011 return;
1012       case morx_indic: case morx_context: case morx_insert:
1013       case kern_statemachine:
1014 	BuildASM(node,att);
1015 return;
1016     }
1017     IError( "Unknown lookup type in BuildDispatch");
1018 }
1019 
BuildGSUBlookups(struct node * node,struct att_dlg * att)1020 static void BuildGSUBlookups(struct node *node,struct att_dlg *att) {
1021     OTLookup *otl = node->u.otl;
1022     struct lookup_subtable *sub;
1023     struct node *subslist;
1024     int cnt;
1025 
1026     for ( sub = otl->subtables, cnt=0; sub!=NULL; sub=sub->next, ++cnt );
1027     subslist = calloc(cnt+1,sizeof(struct node));
1028     for ( sub = otl->subtables, cnt=0; sub!=NULL; sub=sub->next, ++cnt ) {
1029 	subslist[cnt].parent = node;
1030 	subslist[cnt].u.sub = sub;
1031 	subslist[cnt].build = BuildSubtableDispatch;
1032 	subslist[cnt].label = copy(sub->subtable_name);
1033     }
1034 
1035     node->children = subslist;
1036     node->cnt = cnt;
1037 }
1038 
BuildGSUBfeatures(struct node * node,struct att_dlg * att)1039 static void BuildGSUBfeatures(struct node *node,struct att_dlg *att) {
1040     int isgsub = node->parent->parent->parent->tag==CHR('G','S','U','B');
1041     uint32 script = node->parent->parent->tag, lang = node->parent->tag, feat=node->tag;
1042     OTLookup *otl;
1043     SplineFont *sf = att->sf;
1044     int match;
1045     FeatureScriptLangList *fl;
1046     struct scriptlanglist *sl;
1047     int cnt, j,l;
1048     struct node *lookups = NULL;
1049 
1050     for ( j=0; j<2; ++j ) {
1051 	cnt = 0;
1052 	for ( otl = isgsub ? sf->gsub_lookups : sf->gpos_lookups ; otl!=NULL ; otl=otl->next ) {
1053 	    match = false;
1054 	    for ( fl = otl->features; fl!=NULL && !match; fl=fl->next ) {
1055 		if ( fl->featuretag == feat ) {
1056 		    for ( sl = fl->scripts; sl!=NULL && !match; sl=sl->next ) {
1057 			if ( sl->script == script ) {
1058 			    for ( l=0; l<sl->lang_cnt; ++l ) {
1059 				uint32 _lang = l<MAX_LANG ? sl->langs[l] : sl->morelangs[l-MAX_LANG];
1060 				if ( _lang == lang ) {
1061 				    match = true;
1062 			    break;
1063 				}
1064 			    }
1065 			}
1066 		    }
1067 		}
1068 	    }
1069 	    if ( match ) {
1070 		if ( lookups ) {
1071 		    lookups[cnt].parent = node;
1072 		    lookups[cnt].build = BuildGSUBlookups;
1073 		    lookups[cnt].label = copy(otl->lookup_name);
1074 		    lookups[cnt].u.otl = otl;
1075 		}
1076 		++cnt;
1077 	    }
1078 	}
1079 	if ( lookups == NULL )
1080 	    lookups = calloc(cnt+1,sizeof(struct node));
1081     }
1082 
1083     node->children = lookups;
1084     node->cnt = cnt;
1085 }
1086 
BuildGSUBlang(struct node * node,struct att_dlg * att)1087 static void BuildGSUBlang(struct node *node,struct att_dlg *att) {
1088     int isgsub = node->parent->parent->tag==CHR('G','S','U','B');
1089     uint32 script = node->parent->tag, lang = node->tag;
1090     int i,j;
1091     SplineFont *_sf = att->sf;
1092     struct node *featnodes;
1093     uint32 *featlist;
1094 
1095     /* Build up the list of features in this lang entry of this script in GSUB/GPOS */
1096 
1097     featlist = SFFeaturesInScriptLang(_sf,!isgsub,script,lang);
1098     for ( j=0; featlist[j]!=0; ++j );
1099     featnodes = calloc(j+1,sizeof(struct node));
1100     for ( i=0; featlist[i]!=0; ++i ) {
1101 	featnodes[i].tag = featlist[i];
1102 	featnodes[i].parent = node;
1103 	featnodes[i].build = BuildGSUBfeatures;
1104 	featnodes[i].label = TagFullName(_sf,featnodes[i].tag,false,false);
1105     }
1106     free( featlist );
1107 
1108     node->children = featnodes;
1109     node->cnt = j;
1110 }
1111 
BuildGSUBscript(struct node * node,struct att_dlg * att)1112 static void BuildGSUBscript(struct node *node,struct att_dlg *att) {
1113     SplineFont *sf = att->sf;
1114     int lang_max;
1115     int i,j;
1116     struct node *langnodes;
1117     uint32 *langlist;
1118     extern GTextInfo languages[];
1119     char buf[100];
1120     int isgpos = node->parent->tag == CHR('G','P','O','S');
1121 
1122     /* Build the list of languages that are used in this script */
1123     /* Don't bother to check whether they actually get used */
1124 
1125     langlist = SFLangsInScript(sf,isgpos,node->tag);
1126     for ( j=0; langlist[j]!=0; ++j );
1127     lang_max = j;
1128     langnodes = calloc(lang_max+1,sizeof(struct node));
1129     for ( i=0; langlist[i]!=0; ++i )
1130 	langnodes[i].tag = langlist[i];
1131     free( langlist );
1132 
1133     for ( i=0; i<lang_max; ++i ) {
1134 	for ( j=0; languages[j].text!=NULL && langnodes[i].tag!=(uint32) (intpt) languages[j].userdata; ++j );
1135 	buf[0] = '\'';
1136 	buf[1] = langnodes[i].tag>>24;
1137 	buf[2] = (langnodes[i].tag>>16)&0xff;
1138 	buf[3] = (langnodes[i].tag>>8)&0xff;
1139 	buf[4] = langnodes[i].tag&0xff;
1140 	buf[5] = '\'';
1141 	buf[6] = ' ';
1142 	if ( languages[j].text!=NULL ) {
1143 	    strcpy(buf+7,S_((char *) languages[j].text));
1144 	    strcat(buf," ");
1145 	} else
1146 	    buf[7]='\0';
1147 	strcat(buf,_("Language"));
1148 	langnodes[i].label = copy(buf);
1149 	langnodes[i].build = BuildGSUBlang;
1150 	langnodes[i].parent = node;
1151     }
1152     node->children = langnodes;
1153     node->cnt = i;
1154 }
1155 
BuildLookupList(struct node * node,struct att_dlg * att)1156 static void BuildLookupList(struct node *node,struct att_dlg *att) {
1157     OTLookup **otll = node->u.otll;
1158     int i;
1159     struct node *lookupnodes;
1160 
1161     for ( i=0; otll[i]!=NULL; ++i );
1162     lookupnodes = calloc(i+1,sizeof(struct node));
1163     for ( i=0; otll[i]!=NULL; ++i ) {
1164 	lookupnodes[i].label = copy(otll[i]->lookup_name);
1165 	lookupnodes[i].parent = node;
1166 	lookupnodes[i].build = BuildGSUBlookups;
1167 	lookupnodes[i].u.otl = otll[i];
1168     }
1169 
1170     node->children = lookupnodes;
1171     node->cnt = i;
1172 }
1173 
BuildJSTFPrio(struct node * node,struct node * parent,OTLookup ** otll,char * label_if_some,char * label_if_none)1174 static void BuildJSTFPrio(struct node *node, struct node *parent, OTLookup **otll,
1175 	char *label_if_some, char *label_if_none ) {
1176 
1177     node->parent = parent;
1178     if ( otll==NULL || otll[0]==NULL ) {
1179 	node->label = copy(label_if_none);
1180 	node->children_checked = true;
1181 	node->cnt = 0;
1182     } else {
1183 	node->label = copy(label_if_some);
1184 	node->build = BuildLookupList;
1185 	node->u.otll = otll;
1186     }
1187 }
1188 
BuildJSTFlang(struct node * node,struct att_dlg * att)1189 static void BuildJSTFlang(struct node *node,struct att_dlg *att) {
1190     struct jstf_lang *jlang = node->u.jlang;
1191     int i;
1192     struct node *prionodes, *kids;
1193     char buf[120];
1194 
1195     prionodes = calloc(jlang->cnt+1,sizeof(struct node));
1196     for ( i=0; i<jlang->cnt; ++i ) {
1197 	kids = calloc(7,sizeof(struct node));
1198 	BuildJSTFPrio(&kids[0],&prionodes[i],jlang->prios[i].enableExtend,_("Lookups Enabled for Expansion"), _("No Lookups Enabled for Expansion"));
1199 	BuildJSTFPrio(&kids[1],&prionodes[i],jlang->prios[i].disableExtend,_("Lookups Disabled for Expansion"), _("No Lookups Disabled for Expansion"));
1200 	BuildJSTFPrio(&kids[2],&prionodes[i],jlang->prios[i].maxExtend,_("Lookups Limiting Expansion"), _("No Lookups Limiting Expansion"));
1201 	BuildJSTFPrio(&kids[3],&prionodes[i],jlang->prios[i].enableShrink,_("Lookups Enabled for Shrinkage"), _("No Lookups Enabled for Shrinkage"));
1202 	BuildJSTFPrio(&kids[4],&prionodes[i],jlang->prios[i].disableShrink,_("Lookups Disabled for Shrinkage"), _("No Lookups Disabled for Shrinkage"));
1203 	BuildJSTFPrio(&kids[5],&prionodes[i],jlang->prios[i].maxShrink,_("Lookups Limiting Shrinkage"), _("No Lookups Limiting Shrinkage"));
1204 	sprintf( buf, _("Priority: %d"), i );
1205 	prionodes[i].label = copy(buf);
1206 	prionodes[i].parent = node;
1207 	prionodes[i].children_checked = true;
1208 	prionodes[i].children = kids;
1209 	prionodes[i].cnt = 6;
1210     }
1211 
1212     node->children = prionodes;
1213     node->cnt = i;
1214 }
1215 
BuildJSTFscript(struct node * node,struct att_dlg * att)1216 static void BuildJSTFscript(struct node *node,struct att_dlg *att) {
1217     SplineFont *sf = att->sf;
1218     Justify *jscript = node->u.jscript;
1219     int lang_max;
1220     int i,j, ch;
1221     struct node *langnodes, *extenders=NULL;
1222     extern GTextInfo languages[];
1223     char buf[100];
1224     struct jstf_lang *jlang;
1225     char *start, *end;
1226     int gc;
1227     SplineChar *sc;
1228 
1229     for ( jlang=jscript->langs, lang_max=0; jlang!=NULL; jlang=jlang->next, ++lang_max );
1230     langnodes = calloc(lang_max+2,sizeof(struct node));
1231     gc =0;
1232     if ( jscript->extenders!=NULL ) {
1233 	for ( start= jscript->extenders; ; ) {
1234 	    for ( ; *start==',' || *start==' '; ++start );
1235 	    for ( end=start ; *end!='\0' && *end!=',' && *end!=' '; ++end );
1236 	    if ( start==end )
1237 	break;
1238 	    ++gc;
1239 	    if ( *end=='\0' )
1240 	break;
1241 	    start = end;
1242 	}
1243 	extenders = calloc(gc+1,sizeof(struct node));
1244 	gc=0;
1245 	for ( start= jscript->extenders; ; ) {
1246 	    for ( ; *start==',' || *start==' '; ++start );
1247 	    for ( end=start ; *end!='\0' && *end!=',' && *end!=' '; ++end );
1248 	    if ( start==end )
1249 	break;
1250 	    ch = *end; *end='\0';
1251 	    sc = SFGetChar(sf,-1,start);
1252 	    *end = ch;
1253 	    if ( sc!=NULL ) {
1254 		extenders[gc].label = copy(sc->name);
1255 		extenders[gc].parent = &langnodes[0];
1256 		extenders[gc].children_checked = true;
1257 		extenders[gc].u.sc = sc;
1258 		++gc;
1259 	    }
1260 	    if ( *end=='\0' )
1261 	break;
1262 	    start = end;
1263 	}
1264     }
1265     if ( gc==0 ) {
1266 	free(extenders);
1267 	extenders=NULL;
1268 	langnodes[0].label = copy(_("No Extender Glyphs"));
1269 	langnodes[0].parent = node;
1270 	langnodes[0].children_checked = true;
1271     } else {
1272 	langnodes[0].label = copy(_("Extender Glyphs"));
1273 	langnodes[0].parent = node;
1274 	langnodes[0].children_checked = true;
1275 	langnodes[0].children = extenders;
1276 	langnodes[0].cnt = gc;
1277     }
1278 
1279     for ( jlang=jscript->langs, i=1; jlang!=NULL; jlang=jlang->next, ++i ) {
1280 	langnodes[i].tag = jlang->lang;
1281 	for ( j=0; languages[j].text!=NULL && langnodes[i].tag!=(uint32) (intpt) languages[j].userdata; ++j );
1282 	buf[0] = '\'';
1283 	buf[1] = langnodes[i].tag>>24;
1284 	buf[2] = (langnodes[i].tag>>16)&0xff;
1285 	buf[3] = (langnodes[i].tag>>8)&0xff;
1286 	buf[4] = langnodes[i].tag&0xff;
1287 	buf[5] = '\'';
1288 	buf[6] = ' ';
1289 	if ( languages[j].text!=NULL ) {
1290 	    strcpy(buf+7,S_((char *) languages[j].text));
1291 	    strcat(buf," ");
1292 	} else
1293 	    buf[7]='\0';
1294 	strcat(buf,_("Language"));
1295 	langnodes[i].label = copy(buf);
1296 	langnodes[i].build = BuildJSTFlang;
1297 	langnodes[i].parent = node;
1298 	langnodes[i].u.jlang = jlang;
1299     }
1300     node->children = langnodes;
1301     node->cnt = i;
1302 }
1303 
BuildMClass(struct node * node,struct att_dlg * att)1304 static void BuildMClass(struct node *node,struct att_dlg *att) {
1305     SplineFont *_sf = att->sf;
1306     struct node *glyphs;
1307     int i;
1308     char *temp;
1309 
1310     node->children = glyphs = calloc(_sf->mark_class_cnt,sizeof(struct node));
1311     node->cnt = _sf->mark_class_cnt-1;
1312     for ( i=1; i<_sf->mark_class_cnt; ++i ) {
1313 	glyphs[i-1].parent = node;
1314 	temp = malloc((strlen(_sf->mark_classes[i]) + strlen(_sf->mark_class_names[i]) + 4));
1315 	strcpy(temp,_sf->mark_class_names[i]);
1316 	strcat(temp,": ");
1317 	strcat(temp,_sf->mark_classes[i]);
1318 	glyphs[i-1].label = temp;
1319     }
1320 }
1321 
BuildLCarets(struct node * node,struct att_dlg * att)1322 static void BuildLCarets(struct node *node,struct att_dlg *att) {
1323     SplineChar *sc = node->u.sc;
1324     PST *pst;
1325     int i,j;
1326     char buffer[20];
1327     struct node *lcars;
1328 
1329     j = -1;
1330     for ( pst=sc->possub; pst!=NULL; pst=pst->next ) if ( pst->type==pst_lcaret ) {
1331 	for ( j=pst->u.lcaret.cnt-1; j>=0; --j )
1332 	    if ( pst->u.lcaret.carets[j]!=0 )
1333     goto break2;
1334     }
1335   break2:
1336     if ( j==-1 )
1337 return;
1338     ++j;
1339     node->children = lcars = calloc(j+1,sizeof(struct node));
1340     node->cnt = j;
1341     for ( j=i=0; j<pst->u.lcaret.cnt; ++j ) {
1342 	if ( pst->u.lcaret.carets[j]!=0 ) {
1343 	    sprintf( buffer,"%d", pst->u.lcaret.carets[j] );
1344 	    lcars[i].parent = node;
1345 	    lcars[i++].label = copy(buffer);
1346 	}
1347     }
1348 }
1349 
BuildLcar(struct node * node,struct att_dlg * att)1350 static void BuildLcar(struct node *node,struct att_dlg *att) {
1351     SplineFont *sf, *_sf = att->sf;
1352     struct node *glyphs;
1353     int i,j,k,l, lcnt;
1354     PST *pst;
1355 
1356     glyphs = NULL;
1357     for ( k=0; k<2; ++k ) {
1358 	lcnt = 0;
1359 	l = 0;
1360 	do {
1361 	    sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
1362 	    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL && sf->glyphs[i]->ttf_glyph!=-1 ) {
1363 		for ( pst=sf->glyphs[i]->possub; pst!=NULL; pst=pst->next ) {
1364 		    if ( pst->type == pst_lcaret ) {
1365 			for ( j=pst->u.lcaret.cnt-1; j>=0; --j )
1366 			    if ( pst->u.lcaret.carets[j]!=0 )
1367 			break;
1368 			if ( j!=-1 )
1369 		break;
1370 		    }
1371 		}
1372 		if ( pst!=NULL ) {
1373 		    if ( glyphs!=NULL ) {
1374 			glyphs[lcnt].parent = node;
1375 			glyphs[lcnt].build = BuildLCarets;
1376 			glyphs[lcnt].u.sc = sf->glyphs[i];
1377 			glyphs[lcnt].label = copy(sf->glyphs[i]->name);
1378 		    }
1379 		    ++lcnt;
1380 		}
1381 	    }
1382 	    ++l;
1383 	} while ( l<_sf->subfontcnt );
1384 	if ( lcnt==0 )
1385     break;
1386 	if ( glyphs!=NULL )
1387     break;
1388 	node->children = glyphs = calloc(lcnt+1,sizeof(struct node));
1389 	node->cnt = lcnt;
1390     }
1391     if ( glyphs!=NULL )
1392 	qsort(glyphs,lcnt,sizeof(struct node), node_alphabetize);
1393 }
1394 
BuildGdefs(struct node * node,struct att_dlg * att)1395 static void BuildGdefs(struct node *node,struct att_dlg *att) {
1396     SplineFont *sf, *_sf = att->sf;
1397     int i, cmax, l,j, ccnt;
1398     SplineChar *sc;
1399     struct node *chars;
1400     char buffer[100];
1401 
1402     cmax = 0;
1403     l = 0;
1404     do {
1405 	sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
1406 	if ( cmax<sf->glyphcnt ) cmax = sf->glyphcnt;
1407 	++l;
1408     } while ( l<_sf->subfontcnt );
1409 
1410     chars = NULL;
1411     for ( j=0; j<2; ++j ) {
1412 	ccnt = 0;
1413 	for ( i=0; i<cmax; ++i ) {
1414 	    l = 0;
1415 	    sc = NULL;
1416 	    do {
1417 		sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
1418 		if ( l<sf->glyphcnt && sf->glyphs[i]!=NULL ) {
1419 		    sc = sf->glyphs[i];
1420 	    break;
1421 		}
1422 		++l;
1423 	    } while ( l<_sf->subfontcnt );
1424 	    if ( sc!=NULL && SCWorthOutputting(sc) ) {
1425 		if ( chars!=NULL ) {
1426 		    int gdefc = gdefclass(sc);
1427 		    sprintf(buffer,"%.70s %s", sc->name,
1428 			gdefc==0 ? _("Not classified") :
1429 			gdefc==1 ? _("Base") :
1430 			gdefc==2 ? _("Ligature") :
1431 			gdefc==3 ? _("Mark") :
1432 			    _("Component") );
1433 		    chars[ccnt].parent = node;
1434 		    chars[ccnt].label = copy(buffer);;
1435 		}
1436 		++ccnt;
1437 	    }
1438 	}
1439 	if ( ccnt==0 )
1440     break;
1441 	if ( chars==NULL ) {
1442 	    node->cnt = ccnt;
1443 	    node->children = chars = calloc(ccnt+1,sizeof(struct node));
1444 	}
1445     }
1446 }
1447 
BuildGDEF(struct node * node,struct att_dlg * att)1448 static void BuildGDEF(struct node *node,struct att_dlg *att) {
1449     SplineFont *sf, *_sf = att->sf;
1450     AnchorClass *ac;
1451     PST *pst;
1452     int l,j,i;
1453     int gdef, lcar, mclass;
1454 
1455     for ( ac = _sf->anchor; ac!=NULL; ac=ac->next ) {
1456 	if ( ac->type==act_curs )
1457     break;
1458     }
1459     gdef = lcar = 0;
1460     if ( ac!=NULL )
1461 	gdef = 1;
1462     l = 0;
1463     pst = NULL;
1464     do {
1465 	sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
1466 	for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL && sf->glyphs[i]->ttf_glyph!=-1 ) {
1467 	    for ( pst=sf->glyphs[i]->possub; pst!=NULL; pst=pst->next ) {
1468 		if ( pst->type == pst_lcaret ) {
1469 		    for ( j=pst->u.lcaret.cnt-1; j>=0; --j )
1470 			if ( pst->u.lcaret.carets[j]!=0 ) {
1471 			    lcar = 1;
1472 		    break;
1473 			}
1474 		    if ( j!=-1 )
1475 	    break;
1476 		}
1477 	    }
1478 	    if ( sf->glyphs[i]->glyph_class!=0 )
1479 		gdef = 1;
1480 	}
1481 	++l;
1482     } while ( l<_sf->subfontcnt );
1483 
1484     mclass = _sf->mark_class_cnt!=0;
1485 
1486     if ( gdef+lcar+mclass!=0 ) {
1487 	node->children = calloc(gdef+lcar+mclass+1,sizeof(struct node));
1488 	node->cnt = gdef+lcar+mclass;
1489 	if ( gdef ) {
1490 	    node->children[0].label = copy(_("Glyph Definition Sub-Table"));
1491 	    node->children[0].build = BuildGdefs;
1492 	    node->children[0].parent = node;
1493 	}
1494 	if ( lcar ) {
1495 /* GT: Here caret means where to place the cursor inside a ligature. So OpenType */
1496 /* GT: allows there to be a typing cursor inside a ligature (for instance you */
1497 /* GT: can have a cursor between f and i in the "fi" ligature) */
1498 	    node->children[gdef].label = copy(_("Ligature Caret Sub-Table"));
1499 	    node->children[gdef].build = BuildLcar;
1500 	    node->children[gdef].parent = node;
1501 	}
1502 	if ( mclass ) {
1503 	    node->children[gdef+lcar].label = copy(_("Mark Attachment Classes"));
1504 	    node->children[gdef+lcar].build = BuildMClass;
1505 	    node->children[gdef+lcar].parent = node;
1506 	}
1507     }
1508 }
1509 
BuildBaseLangs(struct node * node,struct att_dlg * att)1510 static void BuildBaseLangs(struct node *node,struct att_dlg *att) {
1511     struct baselangextent *bl = node->u.langs, *lf;
1512     int cnt;
1513     struct node *langs;
1514     char buffer[300];
1515 
1516     for ( lf = bl, cnt=0; lf!=NULL; lf=lf->next, ++cnt );
1517 
1518     node->children = langs = calloc(cnt+1,sizeof(struct node));
1519     node->cnt = cnt;
1520 
1521     for ( lf = bl, cnt=0; lf!=NULL; lf=lf->next, ++cnt ) {
1522 	sprintf( buffer, _("%c%c%c%c  Min Extent=%d, Max Extent=%d"),
1523 		lf->lang>>24, lf->lang>>16, lf->lang>>8, lf->lang,
1524 		lf->descent, lf->ascent );
1525 	langs[cnt].label = copy(buffer);
1526 	langs[cnt].parent = node;
1527 	if ( lf->features!=NULL ) {
1528 	    langs[cnt].build = BuildBaseLangs;
1529 	    langs[cnt].u.langs = lf->features;
1530 	}
1531     }
1532 }
1533 
BuildBASE(struct node * node,struct att_dlg * att)1534 static void BuildBASE(struct node *node,struct att_dlg *att) {
1535     SplineFont *_sf = att->sf;
1536     struct Base *base = node->horizontal ? _sf->horiz_base : _sf->vert_base;
1537     struct basescript *bs;
1538     int cnt, i;
1539     struct node *scripts;
1540     char buffer[300];
1541 
1542     for ( bs=base->scripts, cnt=0; bs!=NULL; bs=bs->next, ++cnt );
1543 
1544     node->children = scripts = calloc(cnt+1,sizeof(struct node));
1545     node->cnt = cnt;
1546 
1547     for ( bs=base->scripts, cnt=0; bs!=NULL; bs=bs->next, ++cnt ) {
1548 	if ( base->baseline_cnt!=0 ) {
1549 	    i = bs->def_baseline;
1550 	    sprintf( buffer, _("Script '%c%c%c%c' on %c%c%c%c "),
1551 		    bs->script>>24, bs->script>>16, bs->script>>8, bs->script,
1552 		    base->baseline_tags[i]>>24, base->baseline_tags[i]>>16,
1553 		    base->baseline_tags[i]>>8, base->baseline_tags[i] );
1554 	    for ( i=0; i<base->baseline_cnt; ++i )
1555 		sprintf(buffer+strlen(buffer), " %c%c%c%c: %d ",
1556 			base->baseline_tags[i]>>24, base->baseline_tags[i]>>16,
1557 			base->baseline_tags[i]>>8, base->baseline_tags[i],
1558 			bs->baseline_pos[i]);
1559 	} else
1560 	    sprintf( buffer, _("Script '%c%c%c%c' "),
1561 		    bs->script>>24, bs->script>>16, bs->script>>8, bs->script );
1562 	scripts[cnt].label = copy(buffer);
1563 	scripts[cnt].parent = node;
1564 	if ( bs->langs!=NULL ) {
1565 	    scripts[cnt].build = BuildBaseLangs;
1566 	    scripts[cnt].u.langs = bs->langs;
1567 	}
1568     }
1569 }
1570 
BuildBsLnTable(struct node * node,struct att_dlg * att)1571 static void BuildBsLnTable(struct node *node,struct att_dlg *att) {
1572     SplineFont *_sf = att->sf;
1573     int def_baseline;
1574     int offsets[32];
1575     int16 *baselines;
1576     char buffer[300];
1577     struct node *glyphs;
1578     int gid,i;
1579     SplineChar *sc;
1580 
1581     baselines = PerGlyphDefBaseline(_sf,&def_baseline);
1582     FigureBaseOffsets(_sf,def_baseline&0x1f,offsets);
1583 
1584     node->children = calloc(3+1,sizeof(struct node));
1585     node->cnt = 3;
1586 
1587     sprintf( buffer, _("Default Baseline: '%s'"),
1588 	    (def_baseline&0x1f)==0 ? "romn" :
1589 	    (def_baseline&0x1f)==1 ? "idcn" :
1590 	    (def_baseline&0x1f)==2 ? "ideo" :
1591 	    (def_baseline&0x1f)==3 ? "hang" :
1592 	    (def_baseline&0x1f)==4 ? "math" : "????" );
1593     node->children[0].label = copy(buffer);
1594     node->children[0].parent = node;
1595     sprintf( buffer, _("Offsets from def. baseline:  romn: %d  idcn: %d  ideo: %d  hang: %d  math: %d"),
1596 	    offsets[0], offsets[1], offsets[2], offsets[3], offsets[4] );
1597     node->children[1].label = copy(buffer);
1598     node->children[1].parent = node;
1599     if ( def_baseline&0x100 ) {
1600 	node->children[2].label = copy(_("All glyphs have the same baseline"));
1601 	node->children[2].parent = node;
1602     } else {
1603 	node->children[2].label = copy(_("Per glyph baseline data"));
1604 	node->children[2].parent = node;
1605 	node->children[2].children_checked = true;
1606 	node->children[2].children = glyphs = calloc(_sf->glyphcnt+1,sizeof(struct node));
1607 	for ( gid=i=0; gid<_sf->glyphcnt; ++gid ) if ( (sc=_sf->glyphs[gid])!=NULL ) {
1608 	    sprintf( buffer, "%s: %s", sc->name,
1609 		    (baselines[gid])==0 ? "romn" :
1610 		    (baselines[gid])==1 ? "idcn" :
1611 		    (baselines[gid])==2 ? "ideo" :
1612 		    (baselines[gid])==3 ? "hang" :
1613 		    (baselines[gid])==4 ? "math" : "????" );
1614 	    glyphs[i].label = copy(buffer);
1615 	    glyphs[i++].parent = &node->children[2];
1616 	}
1617 	node->children[2].cnt = i;
1618     }
1619     free(baselines);
1620 }
1621 
BuildOpticalBounds(struct node * node,struct att_dlg * att)1622 static void BuildOpticalBounds(struct node *node,struct att_dlg *att) {
1623     SplineFont *sf, *_sf = att->sf;
1624     int i, cmax, l,j, ccnt;
1625     SplineChar *sc;
1626     struct node *chars;
1627     char buffer[200];
1628     PST *left, *right;
1629 
1630     cmax = 0;
1631     l = 0;
1632     do {
1633 	sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
1634 	if ( cmax<sf->glyphcnt ) cmax = sf->glyphcnt;
1635 	++l;
1636     } while ( l<_sf->subfontcnt );
1637 
1638     chars = NULL;
1639     for ( j=0; j<2; ++j ) {
1640 	ccnt = 0;
1641 	for ( i=0; i<cmax; ++i ) {
1642 	    l = 0;
1643 	    sc = NULL;
1644 	    do {
1645 		sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
1646 		if ( i<sf->glyphcnt && sf->glyphs[i]!=NULL ) {
1647 		    sc = sf->glyphs[i];
1648 	    break;
1649 		}
1650 		++l;
1651 	    } while ( l<_sf->subfontcnt );
1652 	    if ( sc!=NULL && SCWorthOutputting(sc) &&
1653 		    haslrbounds(sc,&left,&right)) {
1654 		if ( chars!=NULL ) {
1655 		    strncpy(buffer,sc->name,70);
1656 		    if ( left!=NULL )
1657 			sprintf(buffer+strlen(buffer), _("  Left Bound=%d"),
1658 				left->u.pos.xoff );
1659 		    if ( right!=NULL )
1660 			sprintf(buffer+strlen(buffer), _("  Right Bound=%d"),
1661 				-right->u.pos.h_adv_off );
1662 		    chars[ccnt].parent = node;
1663 		    chars[ccnt].label = copy(buffer);
1664 		}
1665 		++ccnt;
1666 	    }
1667 	}
1668 	if ( ccnt==0 )
1669 return;
1670 	if ( chars==NULL ) {
1671 	    node->children = chars = calloc(ccnt+1,sizeof(struct node));
1672 	    node->cnt = ccnt;
1673 	}
1674     }
1675 }
1676 
BuildProperties(struct node * node,struct att_dlg * att)1677 static void BuildProperties(struct node *node,struct att_dlg *att) {
1678     SplineFont *sf, *_sf = att->sf;
1679     int i, cmax, l,j,k, ccnt;
1680     SplineChar *sc;
1681     struct node *chars;
1682     uint16 *props;
1683     char buffer[200];
1684 
1685     cmax = 0;
1686     l = 0;
1687     do {
1688 	sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
1689 	if ( cmax<sf->glyphcnt ) cmax = sf->glyphcnt;
1690 	++l;
1691     } while ( l<_sf->subfontcnt );
1692 
1693     chars = NULL; props = NULL;
1694     for ( j=0; j<2; ++j ) {
1695 	ccnt = 0;
1696 	for ( i=0; i<cmax; ++i ) {
1697 	    l = 0;
1698 	    sc = NULL;
1699 	    do {
1700 		sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
1701 		if ( i<sf->glyphcnt && sf->glyphs[i]!=NULL ) {
1702 		    sc = sf->glyphs[i];
1703 	    break;
1704 		}
1705 		++l;
1706 	    } while ( l<_sf->subfontcnt );
1707 	    if ( sc!=NULL ) {
1708 		if ( chars==NULL ) {
1709 		    if ( SCWorthOutputting(sc))
1710 			sc->ttf_glyph = ccnt++;
1711 		    else
1712 			sc->ttf_glyph = -1;
1713 		} else if ( sc->ttf_glyph!=-1 ) {
1714 		    int prop = props[sc->ttf_glyph], offset;
1715 		    sprintf( buffer, "%.70s  dir=%s", sc->name,
1716 			(prop&0x7f)==0 ? _("Strong Left to Right"):
1717 			(prop&0x7f)==1 ? _("Strong Right to Left"):
1718 			(prop&0x7f)==2 ? _("Arabic Right to Left"):
1719 			(prop&0x7f)==3 ? _("European Number"):
1720 			(prop&0x7f)==4 ? _("European Number Separator"):
1721 			(prop&0x7f)==5 ? _("European Number Terminator"):
1722 			(prop&0x7f)==6 ? _("Arabic Number"):
1723 			(prop&0x7f)==7 ? _("Common Number Separator"):
1724 			(prop&0x7f)==8 ? _("Block Separator"):
1725 			(prop&0x7f)==9 ? _("Segment Separator"):
1726 			(prop&0x7f)==10 ? _("White Space"):
1727 			(prop&0x7f)==11 ? _("Neutral"):
1728 			    _("<Unknown direction>") );
1729 		    if ( prop&0x8000 )
1730 			strcat(buffer,_("  Floating accent"));
1731 		    if ( prop&0x4000 )
1732 			strcat(buffer,_("  Hang left"));
1733 		    if ( prop&0x2000 )
1734 			strcat(buffer,_("  Hang right"));
1735 		    if ( prop&0x80 )
1736 			strcat(buffer,_("  Attach right"));
1737 		    if ( prop&0x1000 ) {
1738 			offset = (prop&0xf00)>>8;
1739 			if ( offset&0x8 )
1740 			    offset |= 0xfffffff0;
1741 			if ( offset>0 ) {
1742 			    for ( k=i+offset; k<sf->glyphcnt; ++k )
1743 				if ( sf->glyphs[k]!=NULL && sf->glyphs[k]->ttf_glyph==sc->ttf_glyph+offset ) {
1744 				    sprintf( buffer+strlen(buffer), _("  Mirror=%.30s"), sf->glyphs[k]->name );
1745 			    break;
1746 				}
1747 			} else {
1748 			    for ( k=i+offset; k>=0; --k )
1749 				if ( sf->glyphs[k]!=NULL && sf->glyphs[k]->ttf_glyph==sc->ttf_glyph+offset ) {
1750 				    sprintf( buffer+strlen(buffer), _("  Mirror=%.30s"), sf->glyphs[k]->name );
1751 			    break;
1752 				}
1753 			}
1754 		    }
1755 		    chars[ccnt].parent = node;
1756 		    chars[ccnt++].label = copy(buffer);
1757 		}
1758 	    }
1759 	}
1760 	if ( chars==NULL ) {
1761 	    struct glyphinfo gi;
1762 	    memset(&gi,0,sizeof(gi)); gi.gcnt = _sf->glyphcnt;
1763 	    props = props_array(_sf,&gi);
1764 	    if ( props==NULL )
1765 return;
1766 	    node->children = chars = calloc(ccnt+1,sizeof(struct node));
1767 	}
1768 	node->cnt = ccnt;
1769     }
1770     free(props);
1771 }
1772 
BuildKernTable(struct node * node,struct att_dlg * att)1773 static void BuildKernTable(struct node *node,struct att_dlg *att) {
1774     SplineFont *_sf = att->sf;
1775     OTLookup *otl;
1776     FeatureScriptLangList *fl;
1777     int doit, cnt;
1778     struct node *kerns = NULL;
1779 
1780     if ( _sf->cidmaster ) _sf = _sf->cidmaster;
1781 
1782     for ( doit=0; doit<2; ++doit ) {
1783 	cnt = 0;
1784 	for ( otl = _sf->gpos_lookups; otl!=NULL ; otl=otl->next ) {
1785 	    for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
1786 		if ( (fl->featuretag==CHR('k','e','r','n') || fl->featuretag==CHR('v','k','r','n')) &&
1787 			scriptsHaveDefault(fl->scripts))
1788 	    break;
1789 	    }
1790 	    if ( otl->lookup_type == gpos_pair && fl!=NULL ) {
1791 		if ( doit ) {
1792 		    kerns[cnt].parent = node;
1793 		    kerns[cnt].build = BuildGSUBlookups;
1794 		    kerns[cnt].label = copy(otl->lookup_name);
1795 		    kerns[cnt].u.otl = otl;
1796 		}
1797 		++cnt;
1798 	    }
1799 	}
1800 	if ( !doit ) {
1801 	    node->children = kerns = calloc(cnt+1,sizeof(struct node));
1802 	    node->cnt = cnt;
1803 	}
1804     }
1805 }
1806 
BuildMorxTable(struct node * node,struct att_dlg * att)1807 static void BuildMorxTable(struct node *node,struct att_dlg *att) {
1808     SplineFont *_sf = att->sf;
1809     OTLookup *otl;
1810     FeatureScriptLangList *fl;
1811     int doit, cnt;
1812     struct node *lookups = NULL;
1813     int feat, set;
1814 
1815     if ( _sf->cidmaster ) _sf = _sf->cidmaster;
1816 
1817     for ( doit=0; doit<2; ++doit ) {
1818 	cnt = 0;
1819 	for ( otl = _sf->gsub_lookups; otl!=NULL ; otl=otl->next ) {
1820 	    for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
1821 		if ( fl->ismac ||
1822 			(OTTagToMacFeature(fl->featuretag,&feat,&set) &&
1823 			 scriptsHaveDefault(fl->scripts) &&
1824 			 (otl->lookup_type==gsub_single || otl->lookup_type==gsub_ligature)))
1825 		break;
1826 	    }
1827 	    if ( fl!=NULL ) {
1828 		if ( doit ) {
1829 		    lookups[cnt].parent = node;
1830 		    lookups[cnt].build = BuildGSUBlookups;
1831 		    lookups[cnt].label = copy(otl->lookup_name);
1832 		    lookups[cnt].u.otl = otl;
1833 		}
1834 		++cnt;
1835 	    }
1836 	}
1837 	if ( !doit ) {
1838 	    node->children = lookups = calloc(cnt+1,sizeof(struct node));
1839 	    node->cnt = cnt;
1840 	}
1841     }
1842 }
1843 
BuildTable(struct node * node,struct att_dlg * att)1844 static void BuildTable(struct node *node,struct att_dlg *att) {
1845     SplineFont *_sf = att->sf;
1846     int script_max;
1847     int i,j;
1848     uint32 *scriptlist;
1849     struct node *scriptnodes;
1850     extern GTextInfo scripts[];
1851     int isgsub = node->tag==CHR('G','S','U','B');
1852     char buf[120];
1853 
1854     /* Build the list of scripts that are mentioned in the font */
1855     scriptlist = SFScriptsInLookups(_sf,!isgsub);
1856     if ( scriptlist==NULL )
1857 return;
1858     for ( i=0; scriptlist[i]!=0; ++i );
1859     script_max = i;
1860     scriptnodes = calloc(script_max+1,sizeof(struct node));
1861     for ( i=0; scriptlist[i]!=0; ++i )
1862 	scriptnodes[i].tag = scriptlist[i];
1863     free( scriptlist );
1864 
1865     for ( i=0; i<script_max; ++i ) {
1866 	for ( j=0; scripts[j].text!=NULL && scriptnodes[i].tag!=(uint32) (intpt) scripts[j].userdata; ++j );
1867 	buf[0] = '\'';
1868 	buf[1] = scriptnodes[i].tag>>24;
1869 	buf[2] = (scriptnodes[i].tag>>16)&0xff;
1870 	buf[3] = (scriptnodes[i].tag>>8)&0xff;
1871 	buf[4] = scriptnodes[i].tag&0xff;
1872 	buf[5] = '\'';
1873 	buf[6] = ' ';
1874 	if ( scripts[j].text!=NULL ) {
1875 	    strcpy(buf+7,S_((char*) scripts[j].text));
1876 	    strcat(buf," ");
1877 	} else
1878 	    buf[7]='\0';
1879 /* GT: See the long comment at "Property|New" */
1880 /* GT: The msgstr should contain a translation of "Script", ignore "writing system|" */
1881 /* GT: English uses "script" to mean a general writing style (latin, greek, kanji) */
1882 /* GT: and the cursive handwriting style. Here we mean the general writing system. */
1883 	strcat(buf,S_("writing system|Script"));
1884 	scriptnodes[i].label = copy(buf);
1885 	scriptnodes[i].build = BuildGSUBscript;
1886 	scriptnodes[i].parent = node;
1887     }
1888     node->children = scriptnodes;
1889     node->cnt = i;
1890 }
1891 
BuildJSTFTable(struct node * node,struct att_dlg * att)1892 static void BuildJSTFTable(struct node *node,struct att_dlg *att) {
1893     SplineFont *_sf = att->sf;
1894     int sub_cnt,i,j;
1895     Justify *jscript;
1896     struct node *scriptnodes;
1897     char buf[120];
1898     extern GTextInfo scripts[];
1899 
1900     for ( sub_cnt=0, jscript=_sf->justify; jscript!=NULL; jscript=jscript->next, ++sub_cnt );
1901     scriptnodes = calloc(sub_cnt+1,sizeof(struct node));
1902     for ( i=0, jscript=_sf->justify; jscript!=NULL; jscript=jscript->next, ++i ) {
1903 	scriptnodes[i].tag = jscript->script;
1904 	for ( j=0; scripts[j].text!=NULL && scriptnodes[i].tag!=(uint32) (intpt) scripts[j].userdata; ++j );
1905 	buf[0] = '\'';
1906 	buf[1] = scriptnodes[i].tag>>24;
1907 	buf[2] = (scriptnodes[i].tag>>16)&0xff;
1908 	buf[3] = (scriptnodes[i].tag>>8)&0xff;
1909 	buf[4] = scriptnodes[i].tag&0xff;
1910 	buf[5] = '\'';
1911 	buf[6] = ' ';
1912 	if ( scripts[j].text!=NULL ) {
1913 	    strcpy(buf+7,S_((char*) scripts[j].text));
1914 	    strcat(buf," ");
1915 	} else
1916 	    buf[7]='\0';
1917 /* GT: See the long comment at "Property|New" */
1918 /* GT: The msgstr should contain a translation of "Script", ignore "writing system|" */
1919 /* GT: English uses "script" to me a general writing style (latin, greek, kanji) */
1920 /* GT: and the cursive handwriting style. Here we mean the general writing system. */
1921 	strcat(buf,S_("writing system|Script"));
1922 	scriptnodes[i].label = copy(buf);
1923 	scriptnodes[i].build = BuildJSTFscript;
1924 	scriptnodes[i].parent = node;
1925 	scriptnodes[i].u.jscript = jscript;
1926     }
1927     node->children = scriptnodes;
1928     node->cnt = i;
1929 }
1930 
BuildTop(struct att_dlg * att)1931 static void BuildTop(struct att_dlg *att) {
1932     SplineFont *sf, *_sf = att->sf;
1933     int hasgsub=0, hasgpos=0, hasgdef=0, hasbase=0, hasjstf=0;
1934     int hasmorx=0, haskern=0, hasvkern=0, haslcar=0, hasprop=0, hasopbd=0, hasbsln=0;
1935     int haskc=0, hasvkc=0;
1936     int feat, set;
1937     struct node *tables;
1938     PST *pst;
1939     SplineChar *sc;
1940     int i,k,j;
1941     AnchorClass *ac;
1942     OTLookup *otl;
1943     FeatureScriptLangList *fl;
1944     char buffer[200];
1945 
1946     SFFindClearUnusedLookupBits(_sf);
1947 
1948     for ( otl=_sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
1949 	for ( fl = otl->features; fl!=NULL ; fl=fl->next ) {
1950 	    if ( !fl->ismac )
1951 		hasgsub = true;
1952 	    if ( fl->ismac ||
1953 		    (OTTagToMacFeature(fl->featuretag,&feat,&set) &&
1954 		     scriptsHaveDefault(fl->scripts) &&
1955 		     (otl->lookup_type==gsub_single || otl->lookup_type==gsub_ligature)))
1956 		hasmorx = true;
1957 	}
1958     }
1959     for ( otl=_sf->gpos_lookups; otl!=NULL; otl=otl->next ) {
1960 	if ( otl->lookup_type==kern_statemachine )
1961 	    haskern = true;
1962 	else
1963 	    hasgpos = true;
1964 	if ( otl->lookup_type == gpos_single )
1965 	    for ( fl = otl->features; fl!=NULL ; fl=fl->next ) {
1966 		if ( fl->featuretag==CHR('l','f','b','d') || fl->featuretag==CHR('r','t','b','d') )
1967 		    hasopbd = true;
1968 	    }
1969     }
1970 
1971     k=0;
1972     do {
1973 	sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[k];
1974 	for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc=sf->glyphs[i])!=NULL ) {
1975 	    if (( sc->unicodeenc>=0x10800 && sc->unicodeenc<=0x10fff ) ||
1976 		    ( sc->unicodeenc!=-1 && sc->unicodeenc<0x10fff &&
1977 			isrighttoleft(sc->unicodeenc)) ||
1978 			ScriptIsRightToLeft(SCScriptFromUnicode(sc)) ) {
1979 		hasprop = true;
1980 	    }
1981 	    if ( sc->glyph_class!=0 )
1982 		hasgdef = true;
1983 	    for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
1984 		if ( pst->type == pst_lcaret ) {
1985 		    for ( j=pst->u.lcaret.cnt-1; j>=0; --j )
1986 			if ( pst->u.lcaret.carets[j]!=0 )
1987 		    break;
1988 		    if ( j!=-1 )
1989 			hasgdef = haslcar = true;
1990 		}
1991 	    }
1992 	    if ( sc->kerns!=NULL || sc->vkerns!=NULL )
1993 		haskern = hasgpos = true;
1994 	}
1995 	++k;
1996     } while ( k<_sf->subfontcnt );
1997     if ( _sf->vkerns!=NULL || _sf->kerns!=NULL )
1998 	haskern = hasgpos = true;
1999     if ( _sf->anchor!=NULL )
2000 	hasgpos = true;
2001     for ( ac = sf->anchor; ac!=NULL; ac=ac->next ) {
2002 	if ( ac->type==act_curs )
2003     break;
2004     }
2005     if ( ac!=NULL )
2006 	hasgdef = true;
2007     hasbase = ( _sf->horiz_base!=NULL || _sf->vert_base!=NULL );
2008     hasbsln = ( _sf->horiz_base!=NULL && _sf->horiz_base->baseline_cnt!=0 );
2009     hasjstf = ( _sf->justify!=NULL );
2010 
2011     if ( hasgsub+hasgpos+hasgdef+hasmorx+haskern+haslcar+hasopbd+hasprop+hasbase+hasjstf==0 ) {
2012 	tables = calloc(2,sizeof(struct node));
2013 	tables[0].label = copy(_("No Advanced Typography"));
2014     } else {
2015 	tables = calloc((hasgsub||hasgpos||hasgdef||hasbase||hasjstf)+
2016 	    (hasmorx||haskern||haslcar||hasopbd||hasprop||hasbsln)+1,sizeof(struct node));
2017 	i=0;
2018 	if ( hasgsub || hasgpos || hasgdef || hasbase || hasjstf ) {
2019 	    tables[i].label = copy(_("OpenType Tables"));
2020 	    tables[i].children_checked = true;
2021 	    tables[i].children = calloc(hasgsub+hasgpos+hasgdef+hasbase+hasjstf+1,sizeof(struct node));
2022 	    tables[i].cnt = hasgsub + hasgpos + hasgdef + hasbase + hasjstf;
2023 	    if ( hasbase ) {
2024 		int sub_cnt= (sf->horiz_base!=NULL) + (sf->vert_base!=NULL), j=0;
2025 		tables[i].children[0].label = copy(_("'BASE' Baseline Table"));
2026 		tables[i].children[0].tag = CHR('B','A','S','E');
2027 		tables[i].children[0].parent = &tables[i];
2028 		tables[i].children[0].children_checked = true;
2029 		tables[i].children[0].children = calloc(sub_cnt+1,sizeof(struct node));
2030 		tables[i].children[0].cnt = sub_cnt;
2031 		if ( _sf->horiz_base!=NULL ) {
2032 		    snprintf(buffer,sizeof(buffer),
2033 			    P_("Horizontal: %d baseline","Horizontal: %d baselines",_sf->horiz_base->baseline_cnt),
2034 			    _sf->horiz_base->baseline_cnt );
2035 		    tables[i].children[0].children[j].label = copy(buffer);
2036 		    tables[i].children[0].children[j].horizontal = true;
2037 		    tables[i].children[0].children[j].parent = &tables[i].children[0];
2038 		    tables[i].children[0].children[j].build = BuildBASE;
2039 		    ++j;
2040 		}
2041 		if ( _sf->vert_base!=NULL ) {
2042 		    snprintf(buffer,sizeof(buffer),
2043 			    P_("Vertical: %d baseline","Vertical: %d baselines",_sf->vert_base->baseline_cnt),
2044 			    _sf->vert_base->baseline_cnt );
2045 		    tables[i].children[0].children[j].label = copy(buffer);
2046 		    tables[i].children[0].children[j].horizontal = false;
2047 		    tables[i].children[0].children[j].parent = &tables[i].children[0];
2048 		    tables[i].children[0].children[j].build = BuildBASE;
2049 		    ++j;
2050 		}
2051 	    }
2052 	    if ( hasgdef ) {
2053 		tables[i].children[hasbase].label = copy(_("'GDEF' Glyph Definition Table"));
2054 		tables[i].children[hasbase].tag = CHR('G','D','E','F');
2055 		tables[i].children[hasbase].build = BuildGDEF;
2056 		tables[i].children[hasbase].parent = &tables[i];
2057 	    }
2058 	    if ( hasgpos ) {
2059 		tables[i].children[hasgdef+hasbase].label = copy(_("'GPOS' Glyph Positioning Table"));
2060 		tables[i].children[hasgdef+hasbase].tag = CHR('G','P','O','S');
2061 		tables[i].children[hasgdef+hasbase].build = BuildTable;
2062 		tables[i].children[hasgdef+hasbase].parent = &tables[i];
2063 	    }
2064 	    if ( hasgsub ) {
2065 		tables[i].children[hasgdef+hasgpos+hasbase].label = copy(_("'GSUB' Glyph Substitution Table"));
2066 		tables[i].children[hasgdef+hasgpos+hasbase].tag = CHR('G','S','U','B');
2067 		tables[i].children[hasgdef+hasgpos+hasbase].build = BuildTable;
2068 		tables[i].children[hasgdef+hasgpos+hasbase].parent = &tables[i];
2069 	    }
2070 	    if ( hasjstf ) {
2071 		int k = hasgdef+hasgpos+hasbase+hasgsub;
2072 		tables[i].children[k].label = copy(_("'JSTF' Justification Table"));
2073 		tables[i].children[k].tag = CHR('J','S','T','F');
2074 		tables[i].children[k].parent = &tables[i];
2075 		tables[i].children[k].build = BuildJSTFTable;
2076 	    }
2077 	    ++i;
2078 	}
2079 	if ( hasmorx || haskern || haslcar || hasopbd || hasprop || hasbsln ) {
2080 	    int j = 0;
2081 	    tables[i].label = copy(_("Apple Advanced Typography"));
2082 	    tables[i].children_checked = true;
2083 	    tables[i].children = calloc(hasmorx+haskern+haslcar+hasopbd+hasprop+hasvkern+haskc+hasvkc+hasbsln+1,sizeof(struct node));
2084 	    tables[i].cnt = hasmorx+haskern+hasopbd+hasprop+haslcar+hasvkern+haskc+hasvkc+hasbsln;
2085 	    if ( hasbsln ) {
2086 		tables[i].children[j].label = copy(_("'bsln' Horizontal Baseline Table"));
2087 		tables[i].children[j].tag = CHR('b','s','l','n');
2088 		tables[i].children[j].build = BuildBsLnTable;
2089 		tables[i].children[j++].parent = &tables[i];
2090 	    }
2091 	    if ( haskern ) {
2092 		tables[i].children[j].label = copy(_("'kern' Horizontal Kerning Table"));
2093 		tables[i].children[j].tag = CHR('k','e','r','n');
2094 		tables[i].children[j].build = BuildKernTable;
2095 		tables[i].children[j++].parent = &tables[i];
2096 	    }
2097 	    if ( haslcar ) {
2098 		tables[i].children[j].label = copy(_("'lcar' Ligature Caret Table"));
2099 		tables[i].children[j].tag = CHR('l','c','a','r');
2100 		tables[i].children[j].build = BuildLcar;
2101 		tables[i].children[j++].parent = &tables[i];
2102 	    }
2103 	    if ( hasmorx ) {
2104 		tables[i].children[j].label = copy(_("'morx' Glyph Extended Metamorphosis Table"));
2105 		tables[i].children[j].tag = CHR('m','o','r','x');
2106 		tables[i].children[j].build = BuildMorxTable;
2107 		tables[i].children[j++].parent = &tables[i];
2108 	    }
2109 	    if ( hasopbd ) {
2110 		tables[i].children[j].label = copy(_("'opbd' Optical Bounds Table"));
2111 		tables[i].children[j].tag = CHR('o','p','b','d');
2112 		tables[i].children[j].build = BuildOpticalBounds;
2113 		tables[i].children[j++].parent = &tables[i];
2114 	    }
2115 	    if ( hasprop ) {
2116 		tables[i].children[j].label = copy(_("'prop' Glyph Properties Table"));
2117 		tables[i].children[j].tag = CHR('p','r','o','p');
2118 		tables[i].children[j].build = BuildProperties;
2119 		tables[i].children[j++].parent = &tables[i];
2120 	    }
2121 	    ++i;
2122 	}
2123     }
2124 
2125     att->tables = tables;
2126     att->current = tables;
2127 }
2128 
_SizeCnt(struct att_dlg * att,struct node * node,int lpos,int depth)2129 static int _SizeCnt(struct att_dlg *att,struct node *node, int lpos,int depth) {
2130     int i, len;
2131 
2132     if ( node->monospace )
2133 	GDrawSetFont(att->v,att->monofont);
2134     node->lpos = lpos++;
2135     len = 5+8*depth+ att->as + 5 + GDrawGetText8Width(att->v,node->label,-1);
2136     if ( len>att->maxl ) att->maxl = len;
2137     if ( node->monospace )
2138 	GDrawSetFont(att->v,att->font);
2139 
2140     if ( node->open ) {
2141 	if ( !node->children_checked && node->build!=NULL ) {
2142 	    (node->build)(node,att);
2143 	    node->children_checked = true;
2144 	}
2145 	for ( i=0; i<node->cnt; ++i )
2146 	    lpos = _SizeCnt(att,&node->children[i],lpos,depth+1);
2147     }
2148 return( lpos );
2149 }
2150 
SizeCnt(struct att_dlg * att,struct node * node,int lpos)2151 static int SizeCnt(struct att_dlg *att,struct node *node, int lpos) {
2152     att->maxl = 0;
2153     GDrawSetFont(att->v,att->font);
2154     while ( node->label ) {
2155 	lpos = _SizeCnt(att,node,lpos,0);
2156 	++node;
2157     }
2158 
2159     GScrollBarSetBounds(att->vsb,0,lpos,att->lines_page);
2160     GScrollBarSetBounds(att->hsb,0,att->maxl,att->page_width);
2161     att->open_cnt = lpos;
2162 return( lpos );
2163 }
2164 
NodeFindLPos(struct node * node,int lpos,int * depth)2165 static struct node *NodeFindLPos(struct node *node,int lpos,int *depth) {
2166     for (;;) {
2167 	if ( node->lpos==lpos )
2168 return( node );
2169 	if ( node[1].label!=NULL && node[1].lpos<=lpos )
2170 	    ++node;
2171 	else if ( node->children==NULL || !node->open )
2172 return( NULL );
2173 	else {
2174 	    node = node->children;
2175 	    ++*depth;
2176 	}
2177     }
2178 }
2179 
NodeNext(struct node * node,int * depth)2180 static struct node *NodeNext(struct node *node,int *depth) {
2181     if ( node->open && node->children && node->children[0].label ) {
2182 	++*depth;
2183 return( node->children );
2184     }
2185     for (;;) {
2186 	if ( node[1].label )
2187 return( node+1 );
2188 	node = node->parent;
2189 	--*depth;
2190 	if ( node==NULL )
2191 return( NULL );
2192     }
2193 }
2194 
NodePrev(struct att_dlg * att,struct node * node,int * depth)2195 static struct node *NodePrev(struct att_dlg *att, struct node *node,int *depth) {
2196     while ( node->parent!=NULL && node==node->parent->children ) {
2197 	node = node->parent;
2198 	--*depth;
2199     }
2200     if ( node->parent==NULL && node==att->tables )
2201 return( NULL );
2202     --node;
2203     while ( node->open ) {
2204 	node = &node->children[node->cnt-1];
2205 	++*depth;
2206     }
2207 return( node );
2208 }
2209 
findstartquote(char * str)2210 static char *findstartquote(char *str) {
2211     static int quotes[] = { '"', 0x00ab, 0x2018, 0x201b, 0x201c, 0x201e, 0 };
2212     char *last, *cur;
2213     int i, ch;
2214 
2215     for ( cur=str; *cur!='\0'; ) {
2216 	last = cur;
2217 	ch = utf8_ildb((const char **) &cur);
2218 	for ( i=0; quotes[i]!=0; ++i )
2219 	    if ( ch==quotes[i] )
2220 return( last );
2221     }
2222 return( NULL );
2223 }
2224 
findendquote(char * str)2225 static char *findendquote(char *str) {
2226     static int endquotes[] = { '"', 0x00bb, 0x2019, 0x201b, 0x201d, 0x201e, 0 };
2227     char *last, *cur;
2228     int i, ch;
2229 
2230     /* startquote =*/ utf8_ildb((const char **) &str);
2231     for ( cur=str; *cur!='\0'; ) {
2232 	last = cur;
2233 	ch = utf8_ildb((const char **) &cur);
2234 	if ( ch==' ' )
2235 return( NULL );
2236 	for ( i=0; endquotes[i]!=0; ++i )
2237 	    if ( ch==endquotes[i] )
2238 return( last );
2239     }
2240 return( NULL );
2241 }
2242 
AttExpose(struct att_dlg * att,GWindow pixmap,GRect * rect)2243 static void AttExpose(struct att_dlg *att,GWindow pixmap,GRect *rect) {
2244     int depth, y;
2245     struct node *node;
2246     GRect r;
2247     Color fg;
2248     char *spt, *ept;
2249 
2250     GDrawFillRect(pixmap,rect,GDrawGetDefaultBackground(NULL));
2251     GDrawSetLineWidth(pixmap,0);
2252 
2253     r.height = r.width = att->as;
2254     y = (rect->y/att->fh) * att->fh + att->as;
2255     depth=0;
2256     node = NodeFindLPos(att->tables,rect->y/att->fh+att->off_top,&depth);
2257     GDrawSetFont(pixmap,att->font);
2258     while ( node!=NULL ) {
2259 	r.y = y-att->as+1;
2260 	r.x = 5+8*depth - att->off_left;
2261 	fg = node==att->current ? 0xff0000 : 0x000000;
2262 	if ( node->build || node->children ) {
2263 	    GDrawDrawRect(pixmap,&r,fg);
2264 	    GDrawDrawLine(pixmap,r.x+2,r.y+att->as/2,r.x+att->as-2,r.y+att->as/2,
2265 		    fg);
2266 	    if ( !node->open )
2267 		GDrawDrawLine(pixmap,r.x+att->as/2,r.y+2,r.x+att->as/2,r.y+att->as-2,
2268 			fg);
2269 	}
2270 	if ( node->monospace )
2271 	    GDrawSetFont(pixmap,att->monofont);
2272 	ept = NULL;
2273 	if ( att->dlg_type==dt_font_comp ) {
2274 	    if ( (spt = findstartquote(node->label))!=NULL )
2275 		ept = findendquote(spt);
2276 	}
2277 	if ( ept==NULL )
2278 	    GDrawDrawText8(pixmap,r.x+r.width+5,y,node->label,-1,fg);
2279 	else {
2280 	    int len;
2281 	    len = GDrawDrawText8(pixmap,r.x+r.width+5,y,node->label,spt-node->label,fg);
2282 	    len += GDrawDrawText8(pixmap,r.x+r.width+5+len,y,spt,ept-spt,0x0000ff);
2283 	    GDrawDrawText8(pixmap,r.x+r.width+5+len,y,ept,-1,fg);
2284 	}
2285 	if ( node->monospace )
2286 	    GDrawSetFont(pixmap,att->font);
2287 	node = NodeNext(node,&depth);
2288 	y += att->fh;
2289 	if ( y-att->fh>rect->y+rect->height )
2290     break;
2291     }
2292 }
2293 
ATTChangeCurrent(struct att_dlg * att,struct node * node)2294 static void ATTChangeCurrent(struct att_dlg *att,struct node *node) {
2295     int oldl = att->current->lpos, newl;
2296     GRect r;
2297     if ( node==NULL )
2298 return;
2299     newl = node->lpos;
2300     att->current = node;
2301     r.x =0; r.width = 3000;
2302     if ( newl<att->off_top || newl>=att->off_top+att->lines_page ) {
2303 	att->off_top = newl-att->lines_page/3;
2304 	if ( att->off_top<0 ) att->off_top = 0;
2305 	GScrollBarSetPos(att->vsb,att->off_top);
2306 	GDrawRequestExpose(att->v,NULL,false);
2307     } else if ( newl==oldl+1 ) {
2308 	r.y = (oldl-att->off_top)*att->fh; r.height = 2*att->fh;
2309 	GDrawRequestExpose(att->v,&r,false);
2310     } else if ( newl==oldl-1 ) {
2311 	r.y = (newl-att->off_top)*att->fh; r.height = 2*att->fh;
2312 	GDrawRequestExpose(att->v,&r,false);
2313     } else {
2314 	r.y = (newl-att->off_top)*att->fh; r.height = att->fh;
2315 	GDrawRequestExpose(att->v,&r,false);
2316 	r.y = (oldl-att->off_top)*att->fh; r.height = att->fh;
2317 	GDrawRequestExpose(att->v,&r,false);
2318     }
2319 }
2320 
pututf8(uint32 ch,FILE * file)2321 static void pututf8(uint32 ch,FILE *file) {
2322     if ( ch<0x80 )
2323 	putc(ch,file);
2324     else if ( ch<0x800 ) {
2325 	putc(0xc0 | (ch>>6), file);
2326 	putc(0x80 | (ch&0x3f), file);
2327     } else {
2328 	putc( 0xe0 | (ch>>12),file );
2329 	putc( 0x80 | ((ch>>6)&0x3f),file );
2330 	putc( 0x80 | (ch&0x3f),file );
2331     }
2332 }
2333 
AttSave(struct att_dlg * att)2334 static void AttSave(struct att_dlg *att) {
2335     char *ret = gwwv_save_filename(_("Save"),NULL,
2336 	    "*.txt");
2337     char *cret;
2338     FILE *file;
2339     char *pt;
2340     struct node *node;
2341     int depth=0, d;
2342 
2343     if ( ret==NULL )
2344 return;
2345     cret = utf82def_copy(ret);
2346     file = fopen(cret,"w");
2347     free(cret);
2348     if ( file==NULL ) {
2349 	ff_post_error(_("Save Failed"),_("Save Failed"),ret);
2350 	free(ret);
2351 return;
2352     }
2353     free(ret);
2354 
2355     pututf8(0xfeff,file);	/* Zero width something or other. Marks this as unicode, utf8 */
2356     node = NodeFindLPos(att->tables,0,&depth);
2357     while ( node!=NULL ) {
2358 	d = depth;
2359 	while ( d>=4 ) {
2360 	    pututf8('\t',file);
2361 	    d -= 4;
2362 	}
2363 	while ( d>0 ) {
2364 	    pututf8(' ',file);
2365 	    pututf8(' ',file);
2366 	    --d;
2367 	}
2368 	if ( !node->build && !node->children )
2369 	    pututf8(' ',file);
2370 	else if ( node->open )
2371 	    pututf8('-',file);
2372 	else
2373 	    pututf8('+',file);
2374 	for ( pt=node->label; *pt; ++pt )
2375 	    putc(*pt,file);
2376 	pututf8('\n',file);
2377 	node = NodeNext(node,&depth);
2378     }
2379     fclose(file);
2380 }
2381 
AttSaveM(GWindow gw,GMenuItem * mi,GEvent * e)2382 static void AttSaveM(GWindow gw, GMenuItem *mi,GEvent *e) {
2383     struct att_dlg *att = (struct att_dlg *) GDrawGetUserData(gw);
2384     AttSave(att);
2385 }
2386 
2387 static GMenuItem att_popuplist[] = {
2388     { { (unichar_t *) N_("Save"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 'S' }, 'S', ksm_control, NULL, NULL, AttSaveM, 0 },
2389     GMENUITEM_EMPTY
2390 };
2391 
FVVerify(FontView * fv)2392 static FontView *FVVerify(FontView *fv) {
2393     FontView *test;
2394 
2395     for ( test= fv_list; test!=NULL && test!=fv; test=(FontView *) test->b.next );
2396 return( test );
2397 }
2398 
FontCompActivate(struct att_dlg * att,struct node * node)2399 static void FontCompActivate(struct att_dlg *att,struct node *node) {
2400     char *pt, *pt2; int ch;
2401     SplineChar *sc1=NULL, *sc2=NULL;
2402     int size=0, depth=0;
2403     BDFFont *bdf1, *bdf2;
2404 
2405     att->fv1 = FVVerify(att->fv1);
2406     att->fv2 = FVVerify(att->fv2);
2407 
2408     pt = findstartquote(node->label);
2409     if ( pt==NULL )
2410 return;
2411     pt2 = findendquote(pt);
2412     if ( pt2==NULL )
2413 return;
2414     utf8_ildb((const char **) &pt);
2415     ch = *pt2; *pt2 = '\0';
2416     if ( att->fv1!=NULL )
2417 	sc1 = SFGetChar(att->fv1->b.sf,-1,pt);
2418     if ( att->fv2!=NULL )
2419 	sc2 = SFGetChar(att->fv2->b.sf,-1,pt);
2420     *pt2 = ch;
2421 
2422     pt = strchr(node->label,'@');
2423     if ( pt!=NULL ) {
2424 	for (pt2 = pt-1; pt2>=node->label && isdigit(*pt2); --pt2 );
2425 	size = strtol(pt2+1,NULL,10);
2426 	depth = strtol(pt+1,NULL,10);
2427     }
2428     if ( size!=0 && depth!=0 ) {
2429 	for ( bdf1 = att->fv1->b.sf->bitmaps; bdf1!=NULL &&
2430 		(bdf1->pixelsize!=size || BDFDepth(bdf1)!=depth); bdf1=bdf1->next );
2431 	for ( bdf2 = att->fv2->b.sf->bitmaps; bdf2!=NULL &&
2432 		(bdf2->pixelsize!=size || BDFDepth(bdf2)!=depth); bdf2=bdf2->next );
2433 	if ( bdf1!=NULL && sc1!=NULL && sc1->orig_pos<bdf1->glyphcnt &&
2434 		bdf1->glyphs[sc1->orig_pos]!=NULL )
2435 	    BitmapViewCreate(bdf1->glyphs[sc1->orig_pos],bdf1,att->fv1,
2436 		    att->fv1->b.map->backmap[sc1->orig_pos]);
2437 	if ( bdf2!=NULL && sc2!=NULL && sc2->orig_pos<bdf2->glyphcnt &&
2438 		bdf2->glyphs[sc2->orig_pos]!=NULL )
2439 	    BitmapViewCreate(bdf2->glyphs[sc2->orig_pos],bdf2,att->fv2,
2440 		    att->fv2->b.map->backmap[sc2->orig_pos]);
2441     } else {
2442 	if ( sc1!=NULL )
2443 	    CharViewCreate(sc1,att->fv1,att->fv1->b.map->backmap[sc1->orig_pos]);
2444 	if ( sc2!=NULL )
2445 	    CharViewCreate(sc2,att->fv2,att->fv2->b.map->backmap[sc2->orig_pos]);
2446     }
2447 }
2448 
_ATT_FreeImage(const void * _ci,GImage * img)2449 static void _ATT_FreeImage(const void *_ci, GImage *img) {
2450     GImageDestroy(img);
2451 }
2452 
_ATT_PopupImage(const void * _att)2453 static GImage *_ATT_PopupImage(const void *_att) {
2454     const struct att_dlg *att = _att;
2455     char *start, *pt;
2456     int ch;
2457     SplineChar *sc;
2458     int isliga;
2459 
2460     if ( att->popup_node==NULL || att->popup_node->label==NULL )
2461 return( NULL );
2462     for ( start=att->popup_node->label; *start==' ' || isdigit(*start); ++start );
2463     for ( pt=start; *pt!='\0' && *pt!=' '; ++pt );
2464     ch = *pt; *pt = '\0';
2465     sc = SFGetChar(att->sf,-1,start);
2466     *pt = ch;
2467     if ( sc==NULL )
2468 return( NULL );
2469 
2470     isliga = -1;
2471     while ( *pt==' ' || *pt=='=' || *pt=='>' || *pt=='<' ) {
2472 	if ( *pt=='<' ) isliga = true;
2473 	if ( *pt=='>' ) isliga = false;
2474 	++pt;
2475     }
2476     if ( !isalpha(*pt))		/* If alphabetic, then show the glyph names that follow too. Otherwise show nothing for gpos lookups */
2477 	pt = "";
2478 return( NameList_GetImage(att->sf,sc,att->def_layer,pt,isliga));
2479 }
2480 
ATTStartPopup(struct att_dlg * att,struct node * node)2481 static void ATTStartPopup(struct att_dlg *att,struct node *node) {
2482     att->popup_node = node;
2483     GGadgetPreparePopupImage(att->v,NULL,att,_ATT_PopupImage,_ATT_FreeImage);
2484 }
2485 
AttMouse(struct att_dlg * att,GEvent * event)2486 static void AttMouse(struct att_dlg *att,GEvent *event) {
2487     int l, depth;
2488     struct node *node;
2489     GRect r;
2490 
2491     if ( event->type==et_mousedown ) {
2492 	GGadgetEndPopup();
2493 	if ( event->u.mouse.button==3 ) {
2494 	    static int done=false;
2495 	    if ( !done ) {
2496 		done = true;
2497 		att_popuplist[0].ti.text = (unichar_t *) _( (char *)att_popuplist[0].ti.text);
2498 	    }
2499 	    GMenuCreatePopupMenu(att->v,event, att_popuplist);
2500 	}
2501 return;
2502     }
2503 
2504     l = (event->u.mouse.y/att->fh);
2505     depth=0;
2506     node = NodeFindLPos(att->tables,l+att->off_top,&depth);
2507     if ( event->type==et_mouseup ) {
2508 	ATTChangeCurrent(att,node);
2509 	if ( event->u.mouse.y > l*att->fh+att->as ||
2510 		event->u.mouse.x<5+8*depth ||
2511 		event->u.mouse.x>=5+8*depth+att->as || node==NULL ) {
2512 		/* Not in +/- rectangle */
2513 	    if ( event->u.mouse.x > 5+8*depth+att->as && node!=NULL &&
2514 		    att->dlg_type == dt_font_comp && event->u.mouse.clicks>1 )
2515 		FontCompActivate(att,node);
2516 return;
2517 	}
2518 	node->open = !node->open;
2519 
2520 	SizeCnt(att,att->tables,0);
2521 
2522 	r.x = 0; r.width = 3000;
2523 	r.y = l*att->fh; r.height = 3000;
2524 	GDrawRequestExpose(att->v,&r,false);
2525     } else if ( event->type == et_mousemove ) {
2526 	GGadgetEndPopup();
2527         ATTStartPopup(att,node);
2528     }
2529 }
2530 
AttScroll(struct att_dlg * att,struct sbevent * sb)2531 static void AttScroll(struct att_dlg *att,struct sbevent *sb) {
2532     int newpos = att->off_top;
2533 
2534     switch( sb->type ) {
2535       case et_sb_top:
2536         newpos = 0;
2537       break;
2538       case et_sb_uppage:
2539         newpos -= att->lines_page;
2540       break;
2541       case et_sb_up:
2542         --newpos;
2543       break;
2544       case et_sb_down:
2545         ++newpos;
2546       break;
2547       case et_sb_downpage:
2548         newpos += att->lines_page;
2549       break;
2550       case et_sb_bottom:
2551         newpos = att->open_cnt-att->lines_page;
2552       break;
2553       case et_sb_thumb:
2554       case et_sb_thumbrelease:
2555         newpos = sb->pos;
2556       break;
2557     }
2558     if ( newpos>att->open_cnt-att->lines_page )
2559         newpos = att->open_cnt-att->lines_page;
2560     if ( newpos<0 ) newpos =0;
2561     if ( newpos!=att->off_top ) {
2562 	int diff = newpos-att->off_top;
2563 	att->off_top = newpos;
2564 	GScrollBarSetPos(att->vsb,att->off_top);
2565 	GDrawScroll(att->v,NULL,0,diff*att->fh);
2566     }
2567 }
2568 
2569 
AttHScroll(struct att_dlg * att,struct sbevent * sb)2570 static void AttHScroll(struct att_dlg *att,struct sbevent *sb) {
2571     int newpos = att->off_left;
2572 
2573     switch( sb->type ) {
2574       case et_sb_top:
2575         newpos = 0;
2576       break;
2577       case et_sb_uppage:
2578         newpos -= att->page_width;
2579       break;
2580   /* I'd like to scroll by one character. .6*em is right for courier. */
2581       case et_sb_up:
2582         newpos -= (6*att->fh)/10;
2583       break;
2584       case et_sb_down:
2585         newpos += (6*att->fh)/10;
2586       break;
2587       case et_sb_downpage:
2588         newpos += att->page_width;
2589       break;
2590       case et_sb_bottom:
2591         newpos = att->maxl-att->page_width;
2592       break;
2593       case et_sb_thumb:
2594       case et_sb_thumbrelease:
2595         newpos = sb->pos;
2596       break;
2597     }
2598     if ( newpos>att->maxl-att->page_width )
2599         newpos = att->maxl-att->page_width;
2600     if ( newpos<0 ) newpos =0;
2601     if ( newpos!=att->off_left ) {
2602 	int diff = newpos-att->off_left;
2603 	att->off_left = newpos;
2604 	GScrollBarSetPos(att->hsb,att->off_left);
2605 	GDrawScroll(att->v,NULL,-diff,0);
2606     }
2607 }
2608 
AttResize(struct att_dlg * att,GEvent * event)2609 static void AttResize(struct att_dlg *att,GEvent *event) {
2610     GRect size, wsize;
2611     int lcnt;
2612     int sbsize = GDrawPointsToPixels(att->gw,_GScrollBar_Width);
2613 
2614     GDrawGetSize(att->gw,&size);
2615     lcnt = (size.height-att->bmargin)/att->fh;
2616     GGadgetResize(att->vsb,sbsize,lcnt*att->fh);
2617     GGadgetMove(att->vsb,size.width-sbsize,0);
2618     GGadgetResize(att->hsb,size.width-sbsize,sbsize);
2619     GGadgetMove(att->hsb,0,lcnt*att->fh);
2620     GDrawResize(att->v,size.width-sbsize,lcnt*att->fh);
2621     att->page_width = size.width-sbsize;
2622     att->lines_page = lcnt;
2623     GScrollBarSetBounds(att->vsb,0,att->open_cnt,att->lines_page);
2624     GScrollBarSetBounds(att->hsb,0,att->maxl,att->page_width);
2625 
2626     GGadgetGetSize(att->cancel,&wsize);
2627     GGadgetMove(att->cancel,(size.width-wsize.width)/2,lcnt*att->fh+sbsize+5);
2628     GDrawRequestExpose(att->v,NULL,true);
2629     GDrawRequestExpose(att->gw,NULL,true);
2630 }
2631 
AttChar(struct att_dlg * att,GEvent * event)2632 static int AttChar(struct att_dlg *att,GEvent *event) {
2633     int depth = 0;
2634     int pos;
2635 
2636     switch (event->u.chr.keysym) {
2637       case GK_F1: case GK_Help:
2638 	help("ui/dialogs/showatt.html", NULL);
2639 return( true );
2640       case GK_Return: case GK_KP_Enter:
2641 	att->current->open = !att->current->open;
2642 
2643 	att->open_cnt = SizeCnt(att,att->tables,0);
2644 	GScrollBarSetBounds(att->vsb,0,att->open_cnt,att->lines_page);
2645 
2646 	GDrawRequestExpose(att->v,NULL,false);
2647 return( true );
2648       case GK_Page_Down: case GK_KP_Page_Down:
2649 	pos = att->off_top+(att->lines_page<=1?1:att->lines_page-1);
2650 	if ( pos >= att->open_cnt-att->lines_page )
2651 	    pos = att->open_cnt-att->lines_page;
2652 	if ( pos<0 ) pos = 0;
2653 	att->off_top = pos;
2654 	GScrollBarSetPos(att->vsb,pos);
2655 	GDrawRequestExpose(att->v,NULL,false);
2656 return( true );
2657       case GK_Down: case GK_KP_Down:
2658 	ATTChangeCurrent(att,NodeNext(att->current,&depth));
2659 return( true );
2660       case GK_Up: case GK_KP_Up:
2661 	ATTChangeCurrent(att,NodePrev(att,att->current,&depth));
2662 return( true );
2663       case GK_Page_Up: case GK_KP_Page_Up:
2664 	pos = att->off_top-(att->lines_page<=1?1:att->lines_page-1);
2665 	if ( pos<0 ) pos = 0;
2666 	att->off_top = pos;
2667 	GScrollBarSetPos(att->vsb,pos);
2668 	GDrawRequestExpose(att->v,NULL,false);
2669 return( true );
2670       case GK_Left: case GK_KP_Left:
2671 	ATTChangeCurrent(att,att->current->parent);
2672 return( true );
2673       case GK_Right: case GK_KP_Right:
2674 	if ( !att->current->open ) {
2675 	    att->current->open = !att->current->open;
2676 
2677 	    att->open_cnt = SizeCnt(att,att->tables,0);
2678 	    GScrollBarSetBounds(att->vsb,0,att->open_cnt,att->lines_page);
2679 	    if ( att->current->children!=NULL )
2680 		att->current = att->current->children;
2681 
2682 	    GDrawRequestExpose(att->v,NULL,false);
2683 	} else
2684 	    ATTChangeCurrent(att,att->current->children);
2685 return( true );
2686       case GK_Home: case GK_KP_Home:
2687 	ATTChangeCurrent(att,att->tables);
2688 return( true );
2689       case GK_End: case GK_KP_End:
2690 	ATTChangeCurrent(att,NodeFindLPos(att->tables,att->open_cnt-1,&depth));
2691 return( true );
2692       case 'S': case 's':
2693 	if ( event->u.mouse.state&ksm_control )
2694 	    AttSave(att);
2695 return( true );
2696     }
2697 return( false );
2698 }
2699 
attv_e_h(GWindow gw,GEvent * event)2700 static int attv_e_h(GWindow gw, GEvent *event) {
2701     struct att_dlg *att = (struct att_dlg *) GDrawGetUserData(gw);
2702 
2703     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
2704 	    (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
2705 return( GGadgetDispatchEvent(att->vsb,event));
2706     }
2707 
2708     switch ( event->type ) {
2709       case et_expose:
2710 	AttExpose(att,gw,&event->u.expose.rect);
2711       break;
2712       case et_char:
2713 return( AttChar(att,event));
2714       case et_mousedown:
2715       case et_mousemove:
2716       case et_mouseup:
2717 	AttMouse(att,event);
2718       break;
2719     }
2720 return( true );
2721 }
2722 
att_e_h(GWindow gw,GEvent * event)2723 static int att_e_h(GWindow gw, GEvent *event) {
2724     struct att_dlg *att = (struct att_dlg *) GDrawGetUserData(gw);
2725 
2726     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
2727 	    (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
2728 return( GGadgetDispatchEvent(att->vsb,event));
2729     }
2730 
2731     switch ( event->type ) {
2732       case et_expose:
2733       break;
2734       case et_char:
2735 return( AttChar(att,event));
2736       break;
2737       case et_resize:
2738 	if ( event->u.resize.sized )
2739 	    AttResize(att,event);
2740       break;
2741       case et_controlevent:
2742 	switch ( event->u.control.subtype ) {
2743 	  case et_scrollbarchange:
2744 	    if ( event->u.control.g == att->vsb )
2745 		AttScroll(att,&event->u.control.u.sb);
2746 	    else
2747 		AttHScroll(att,&event->u.control.u.sb);
2748 	  break;
2749 	  case et_buttonactivate:
2750 	    att->done = true;
2751 	    if ( att->dlg_type==dt_font_comp )
2752 		GDrawDestroyWindow(gw);
2753 	  break;
2754 	}
2755       break;
2756       case et_close:
2757 	att->done = true;
2758 	if ( att->dlg_type==dt_font_comp )
2759 	    GDrawDestroyWindow(gw);
2760       break;
2761       case et_destroy:
2762 	if ( att!=NULL ) {
2763 	    nodesfree(att->tables);
2764 	    free(att);
2765 	}
2766     }
2767 return( true );
2768 }
2769 
ShowAttCreateDlg(struct att_dlg * att,SplineFont * sf,int which,char * win_title)2770 static void ShowAttCreateDlg(struct att_dlg *att, SplineFont *sf, int which,
2771 	char *win_title) {
2772     GRect pos;
2773     GWindowAttrs wattrs;
2774     FontRequest rq;
2775     int as, ds, ld;
2776     GGadgetCreateData gcd[5];
2777     GTextInfo label[4];
2778     int sbsize = GDrawPointsToPixels(NULL,_GScrollBar_Width);
2779     static GFont *monofont=NULL, *propfont=NULL;
2780 
2781     if ( sf->cidmaster ) sf = sf->cidmaster;
2782 
2783     memset( att,0,sizeof(*att));
2784     att->sf = sf;
2785     att->dlg_type = which;
2786 
2787     memset(&wattrs,0,sizeof(wattrs));
2788     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
2789     wattrs.event_masks = ~(1<<et_charup);
2790     wattrs.is_dlg = true;
2791     wattrs.restrict_input_to_me = which==dt_show_att;
2792     wattrs.undercursor = 1;
2793     wattrs.cursor = ct_pointer;
2794     wattrs.utf8_window_title = win_title;
2795     pos.x = pos.y = 0;
2796     pos.width = GDrawPointsToPixels(NULL,GGadgetScale(which==0?200:450));
2797     pos.height = GDrawPointsToPixels(NULL,300);
2798     att->gw = GDrawCreateTopWindow(NULL,&pos,att_e_h,att,&wattrs);
2799 
2800     if ( propfont==NULL ) {
2801 	memset(&rq,'\0',sizeof(rq));
2802 	rq.utf8_family_name = SANS_UI_FAMILIES;
2803 	rq.point_size = 12;
2804 	rq.weight = 400;
2805 	propfont = GDrawInstanciateFont(att->gw,&rq);
2806 	propfont = GResourceFindFont("ShowATT.Font",propfont);
2807 
2808 	GDrawDecomposeFont(propfont, &rq);
2809 	rq.utf8_family_name = MONO_UI_FAMILIES;	/* I want to show tabluar data sometimes */
2810 	monofont = GDrawInstanciateFont(att->gw,&rq);
2811 	monofont = GResourceFindFont("ShowATT.MonoFont",monofont);
2812     }
2813     att->font = propfont;
2814     att->monofont = monofont;
2815     GDrawWindowFontMetrics(att->gw,att->font,&as,&ds,&ld);
2816     att->fh = as+ds; att->as = as;
2817 
2818     att->bmargin = GDrawPointsToPixels(NULL,32)+sbsize;
2819 
2820     att->lines_page = (pos.height-att->bmargin)/att->fh;
2821     att->page_width = pos.width-sbsize;
2822     wattrs.mask = wam_events|wam_cursor/*|wam_bordwidth|wam_bordcol*/;
2823     wattrs.border_width = 1;
2824     wattrs.border_color = 0x000000;
2825     pos.x = 0; pos.y = 0;
2826     pos.width -= sbsize; pos.height = att->lines_page*att->fh;
2827     att->v = GWidgetCreateSubWindow(att->gw,&pos,attv_e_h,att,&wattrs);
2828     GDrawSetVisible(att->v,true);
2829 
2830     memset(&label,0,sizeof(label));
2831     memset(&gcd,0,sizeof(gcd));
2832 
2833     gcd[0].gd.pos.x = pos.width; gcd[0].gd.pos.y = 0;
2834     gcd[0].gd.pos.width = sbsize;
2835     gcd[0].gd.pos.height = pos.height;
2836     gcd[0].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels | gg_sb_vert;
2837     gcd[0].creator = GScrollBarCreate;
2838 
2839     gcd[1].gd.pos.x = 0; gcd[1].gd.pos.y = pos.height;
2840     gcd[1].gd.pos.height = sbsize;
2841     gcd[1].gd.pos.width = pos.width;
2842     gcd[1].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels;
2843     gcd[1].creator = GScrollBarCreate;
2844 
2845     gcd[2].gd.pos.width = -1;
2846     gcd[2].gd.pos.x = (pos.width-sbsize-GIntGetResource(_NUM_Buttonsize)*100/GIntGetResource(_NUM_ScaleFactor))/2;
2847     gcd[2].gd.pos.y = pos.height+sbsize+5;
2848     gcd[2].gd.flags = gg_visible | gg_enabled | gg_but_default | gg_but_cancel | gg_pos_in_pixels;
2849     label[2].text = (unichar_t *) _("_OK");
2850     label[2].text_is_1byte = true;
2851     label[2].text_in_resource = true;
2852     gcd[2].gd.label = &label[2];
2853     gcd[2].creator = GButtonCreate;
2854 
2855     GGadgetsCreate(att->gw,gcd);
2856     att->vsb = gcd[0].ret;
2857     att->hsb = gcd[1].ret;
2858     att->cancel = gcd[2].ret;
2859 }
2860 
ShowAtt(SplineFont * sf,int def_layer)2861 void ShowAtt(SplineFont *sf,int def_layer) {
2862     struct att_dlg att;
2863 
2864     ShowAttCreateDlg(&att, sf, 0, _("Show ATT"));
2865     att.def_layer = def_layer;
2866 
2867     BuildTop(&att);
2868     att.open_cnt = SizeCnt(&att,att.tables,0);
2869     GScrollBarSetBounds(att.vsb,0,att.open_cnt,att.lines_page);
2870 
2871     GDrawSetVisible(att.gw,true);
2872 
2873     while ( !att.done )
2874 	GDrawProcessOneEvent(NULL);
2875     GDrawSetUserData(att.gw,NULL);
2876     nodesfree(att.tables);
2877     GDrawDestroyWindow(att.gw);
2878 }
2879 
2880 /* ************************************************************************** */
2881 /* ****************************** Font Compare ****************************** */
2882 /* ************************ (reuse the show att dlg) ************************ */
2883 /* ************************************************************************** */
2884 struct nested_file {
2885     FILE *file;
2886     char *linebuf;
2887     int linemax;
2888     int read_nest;
2889 };
2890 
ReadNestedLine(struct nested_file * nf)2891 static int ReadNestedLine(struct nested_file *nf) {
2892     char *pt, *end;
2893     int ch;
2894     int nest = 0;
2895 
2896     pt = nf->linebuf; end = pt + nf->linemax-1;
2897     while ( (ch=getc(nf->file))==' ' )
2898 	++nest;
2899     if ( ch==EOF ) {
2900 	nf->read_nest = -1;
2901 return( -1 );
2902     }
2903     while ( ch!=EOF && ch!='\n' ) {
2904 	if ( pt>=end ) {
2905 	    char *old = nf->linebuf;
2906 	    nf->linemax += 200;
2907 	    nf->linebuf = realloc(nf->linebuf, nf->linemax);
2908 	    pt = nf->linebuf + (pt-old);
2909 	    end = nf->linebuf + nf->linemax - 1;
2910 	}
2911 	*pt++ = ch;
2912 	ch = getc(nf->file);
2913     }
2914     *pt = '\0';
2915     nf->read_nest = nest;
2916 return( nest );
2917 }
2918 
ReadKids(struct nested_file * nf,int desired_nest,struct node * parent)2919 static void ReadKids(struct nested_file *nf,int desired_nest,struct node *parent) {
2920     int i=0, max=0, j, k;
2921 
2922     ReadNestedLine(nf);
2923     for (;;) {
2924 	if ( nf->read_nest < desired_nest )
2925     break;
2926 	if ( i>=max-1 ) {
2927 	    parent->children = realloc(parent->children,(max+=10)*sizeof( struct node ));
2928 	    memset(parent->children+i,0,(max-i)*sizeof(struct node));
2929 	}
2930 	parent->children[i].label = copy(nf->linebuf);
2931 	parent->children[i].parent = parent;
2932 	ReadKids(nf,desired_nest+1,&parent->children[i]);
2933 	++i;
2934     }
2935     if ( i!=0 ) {
2936 	if ( i<max-1 )
2937 	    parent->children = realloc(parent->children,(i+1)*sizeof(struct node));
2938 	/* The reallocs can invalidate the parent field */
2939 	for ( j=0; j<i; ++j )
2940 	    for ( k=0; k<parent->children[j].cnt; ++k )
2941 		parent->children[j].children[k].parent = &parent->children[j];
2942 	parent->cnt = i;
2943     }
2944 }
2945 
BuildFCmpNodes(struct att_dlg * att,SplineFont * sf1,SplineFont * sf2,int flags)2946 static void BuildFCmpNodes(struct att_dlg *att, SplineFont *sf1, SplineFont *sf2,int flags) {
2947     FILE *tmp = GFileTmpfile();
2948     struct node *tables;
2949     struct nested_file nf;
2950 
2951     att->tables = tables = calloc(2,sizeof(struct node));
2952     att->current = tables;
2953     if ( !CompareFonts(sf1,att->fv1->b.map,sf2,tmp,flags) && ftell(tmp)==0 ) {
2954 	tables[0].label = copy(_("No differences found"));
2955     } else {
2956 	tables[0].label = copy(_("Differences..."));
2957 	rewind(tmp);
2958 	memset(&nf,0,sizeof(nf));
2959 	nf.file = tmp;
2960 	nf.linebuf = malloc( nf.linemax = 300 );
2961 	ReadKids(&nf,0,&tables[0]);
2962 	free(nf.linebuf);
2963     }
2964     fclose(tmp);
2965 }
2966 
FontCmpDlg(FontView * fv1,FontView * fv2,int flags)2967 static void FontCmpDlg(FontView *fv1, FontView *fv2,int flags) {
2968     struct att_dlg *att;
2969     char buffer[300];
2970     SplineFont *sf1 = fv1->b.sf, *sf2=fv2->b.sf;
2971 
2972     if ( strcmp(sf1->fontname,sf2->fontname)!=0 )
2973 	snprintf( buffer, sizeof(buffer), _("Compare %s to %s"), sf1->fontname, sf2->fontname);
2974     else if ( sf1->version!=NULL && sf2->version!=NULL &&
2975 	    strcmp(sf1->version,sf2->version)!=0 )
2976 	snprintf( buffer, sizeof(buffer), _("Compare version %s of %s to %s"),
2977 		sf1->version, sf1->fontname, sf2->version);
2978     else
2979 	strcpy( buffer, _("Font Compare"));
2980 
2981     att = malloc(sizeof(struct att_dlg));
2982     ShowAttCreateDlg(att, sf1, dt_font_comp, buffer);
2983     att->fv1 = fv1; att->fv2 = fv2;
2984 
2985     GDrawSetCursor(fv1->v,ct_watch);
2986     GDrawSetCursor(fv2->v,ct_watch);
2987     BuildFCmpNodes(att,sf1,sf2,flags);
2988     GDrawSetCursor(fv1->v,ct_pointer);
2989     GDrawSetCursor(fv2->v,ct_pointer);
2990 
2991     att->open_cnt = SizeCnt(att,att->tables,0);
2992     GScrollBarSetBounds(att->vsb,0,att->open_cnt,att->lines_page);
2993 
2994     GDrawSetVisible(att->gw,true);
2995 }
2996 
FCAskFilename(FontView * fv,int flags)2997 static void FCAskFilename(FontView *fv,int flags) {
2998     char *filename = GetPostScriptFontName(NULL,false,true);
2999     FontView *otherfv;
3000 
3001     if ( filename==NULL )
3002 return;
3003     otherfv = (FontView *) ViewPostScriptFont(filename,0);
3004     free(filename); filename = NULL;
3005     if ( otherfv==NULL )
3006 return;
3007     FontCmpDlg(fv,otherfv,flags);
3008 }
3009 
3010 struct mf_data {
3011     int done;
3012     FontView *fv;
3013     GGadget *other;
3014     GGadget *amount;
3015 };
3016 #define CID_Outlines	1
3017 #define	CID_Exact	2
3018 #define CID_Warn	3
3019 #define CID_Fuzzy	4
3020 #define CID_Hinting	5
3021 #define CID_Bitmaps	6
3022 #define CID_Names	7
3023 #define CID_GPos	8
3024 #define CID_GSub	9
3025 #define CID_HintMasks	10
3026 #define CID_HintMasksWConflicts	11
3027 #define CID_NoHintMasks	12
3028 #define CID_RefContourWarn	13
3029 #define CID_AddDiffs	14
3030 #define CID_AddMissing	15
3031 
3032 static int last_flags = fcf_outlines|fcf_hinting|fcf_bitmaps|fcf_names|fcf_gpos|fcf_gsub|fcf_adddiff2sf1;
3033 
FC_OK(GGadget * g,GEvent * e)3034 static int FC_OK(GGadget *g, GEvent *e) {
3035     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3036 	GWindow gw = GGadgetGetWindow(g);
3037 	struct mf_data *d = GDrawGetUserData(gw);
3038 	int i, index = GGadgetGetFirstListSelectedItem(d->other);
3039 	FontView *fv;
3040 	int flags = 0;
3041 	for ( i=0, fv=fv_list; fv!=NULL; fv=(FontView *) (fv->b.next) ) {
3042 	    if ( fv==d->fv )
3043 	continue;
3044 	    if ( i==index )
3045 	break;
3046 	    ++i;
3047 	}
3048 
3049 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Outlines)) )
3050 	    flags |= fcf_outlines;
3051 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Exact)) )
3052 	    flags |= fcf_exact;
3053 	else if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Warn)) )
3054 	    flags |= fcf_warn_not_exact;
3055 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_RefContourWarn)) )
3056 	    flags |= fcf_warn_not_ref_exact;
3057 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Hinting)) )
3058 	    flags |= fcf_hinting;
3059 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_HintMasks)) )
3060 	    flags |= fcf_hintmasks;
3061 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_HintMasksWConflicts)) )
3062 	    flags |= fcf_hmonlywithconflicts|fcf_hintmasks;
3063 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Bitmaps)) )
3064 	    flags |= fcf_bitmaps;
3065 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Names)) )
3066 	    flags |= fcf_names;
3067 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_GPos)) )
3068 	    flags |= fcf_gpos;
3069 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_GSub)) )
3070 	    flags |= fcf_gsub;
3071 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_AddDiffs)) )
3072 	    flags |= fcf_adddiff2sf1;
3073 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_AddMissing)) )
3074 	    flags |= fcf_addmissing;
3075 	last_flags = flags;
3076 
3077 	GDrawDestroyWindow(gw);
3078 	if ( fv==NULL )
3079 	    FCAskFilename(d->fv,flags);
3080 	else
3081 	    FontCmpDlg(d->fv,fv,flags);
3082 	d->done = true;
3083     }
3084 return( true );
3085 }
3086 
FC_Cancel(GGadget * g,GEvent * e)3087 static int FC_Cancel(GGadget *g, GEvent *e) {
3088     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3089 	GWindow gw = GGadgetGetWindow(g);
3090 	struct mf_data *d = GDrawGetUserData(gw);
3091 	d->done = true;
3092 	GDrawDestroyWindow(gw);
3093     }
3094 return( true );
3095 }
3096 
fc_e_h(GWindow gw,GEvent * event)3097 static int fc_e_h(GWindow gw, GEvent *event) {
3098     if ( event->type==et_close ) {
3099 	struct mf_data *d = GDrawGetUserData(gw);
3100 	d->done = true;
3101 	GDrawDestroyWindow(gw);
3102     } else if ( event->type == et_char ) {
3103 return( false );
3104     }
3105 return( true );
3106 }
3107 
FontCompareDlg(FontView * fv)3108 void FontCompareDlg(FontView *fv) {
3109     GRect pos;
3110     GWindow gw;
3111     GWindowAttrs wattrs;
3112     GGadgetCreateData gcd[25];
3113     GTextInfo label[25];
3114     struct mf_data d;
3115     char buffer[80];
3116     int k;
3117 
3118 	memset(&wattrs,0,sizeof(wattrs));
3119 	wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_restrict|wam_isdlg;
3120 	wattrs.event_masks = ~(1<<et_charup);
3121 	wattrs.restrict_input_to_me = 1;
3122 	wattrs.is_dlg = 1;
3123 	wattrs.undercursor = 1;
3124 	wattrs.cursor = ct_pointer;
3125 	wattrs.utf8_window_title = _("Font Compare");
3126 	pos.x = pos.y = 0;
3127 	pos.width = GGadgetScale(GDrawPointsToPixels(NULL,180));
3128 	pos.height = GDrawPointsToPixels(NULL,88+15*14+2);
3129 	gw = GDrawCreateTopWindow(NULL,&pos,fc_e_h,&d,&wattrs);
3130 
3131 	memset(&label,0,sizeof(label));
3132 	memset(&gcd,0,sizeof(gcd));
3133 
3134 	k=0;
3135 	sprintf( buffer, _("Font to compare with %.20s"), fv->b.sf->fontname );
3136 	label[k].text = (unichar_t *) buffer;
3137 	label[k].text_is_1byte = true;
3138 	gcd[k].gd.label = &label[k];
3139 	gcd[k].gd.pos.x = 12; gcd[k].gd.pos.y = 6;
3140 	gcd[k].gd.flags = gg_visible | gg_enabled;
3141 	gcd[k++].creator = GLabelCreate;
3142 
3143 	gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = 21;
3144 	gcd[k].gd.pos.width = 145;
3145 	gcd[k].gd.flags = gg_visible | gg_enabled;
3146 	gcd[k].gd.u.list = BuildFontList(fv);
3147 	gcd[k].gd.label = &gcd[k].gd.u.list[0];
3148 	gcd[k].gd.u.list[0].selected = true;
3149 	gcd[k++].creator = GListButtonCreate;
3150 
3151 	gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+24;
3152 	if ( fv->b.sf->onlybitmaps )
3153 	    gcd[k].gd.flags = gg_visible;
3154 	else
3155 	    gcd[k].gd.flags = gg_visible | gg_enabled | ((last_flags&fcf_outlines)?gg_cb_on:0);
3156 	label[k].text = (unichar_t *) _("Compare _Outlines");
3157 	label[k].text_is_1byte = true;
3158 	label[k].text_in_resource = true;
3159 	gcd[k].gd.label = &label[k];
3160 	gcd[k].gd.cid = CID_Outlines;
3161 	gcd[k++].creator = GCheckBoxCreate;
3162 
3163 	gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
3164 	if ( fv->b.sf->onlybitmaps )
3165 	    gcd[k].gd.flags = gg_visible;
3166 	else
3167 	    gcd[k].gd.flags = gg_visible | gg_enabled| ((last_flags&fcf_exact)?gg_cb_on:0);
3168 	label[k].text = (unichar_t *) _("_Exact");
3169 	label[k].text_is_1byte = true;
3170 	label[k].text_in_resource = true;
3171 	gcd[k].gd.label = &label[k];
3172 	gcd[k].gd.cid = CID_Exact;
3173 	gcd[k].gd.popup_msg = _("Accept outlines which exactly match the original");
3174 	gcd[k++].creator = GRadioCreate;
3175 
3176 	gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
3177 	if ( fv->b.sf->onlybitmaps )
3178 	    gcd[k].gd.flags = gg_visible;
3179 	else
3180 	    gcd[k].gd.flags = gg_visible | gg_enabled| ((last_flags&fcf_exact)?0:gg_cb_on);
3181 	label[k].text = (unichar_t *) _("_Accept inexact");
3182 	label[k].text_is_1byte = true;
3183 	label[k].text_in_resource = true;
3184 	gcd[k].gd.label = &label[k];
3185 	gcd[k].gd.cid = CID_Fuzzy;
3186 	gcd[k].gd.popup_msg = _("Accept an outline which is a close approximation to the original.\nIt may be off by an em-unit, or have a reference which matches a contour.");
3187 	gcd[k++].creator = GRadioCreate;
3188 
3189 	gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
3190 	if ( fv->b.sf->onlybitmaps )
3191 	    gcd[k].gd.flags = gg_visible;
3192 	else
3193 	    gcd[k].gd.flags = gg_visible | gg_enabled| ((last_flags&fcf_warn_not_exact)?gg_cb_on:0);
3194 	label[k].text = (unichar_t *) _("_Warn if inexact");
3195 	label[k].text_is_1byte = true;
3196 	label[k].text_in_resource = true;
3197 	gcd[k].gd.label = &label[k];
3198 	gcd[k].gd.cid = CID_Warn;
3199 	gcd[k].gd.popup_msg = _("Warn if the outlines are close but not exactly the same");
3200 	gcd[k++].creator = GCheckBoxCreate;
3201 
3202 	gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
3203 	if ( fv->b.sf->onlybitmaps )
3204 	    gcd[k].gd.flags = gg_visible;
3205 	else
3206 	    gcd[k].gd.flags = gg_visible | gg_enabled| ((last_flags&fcf_warn_not_ref_exact)?gg_cb_on:0);
3207 	label[k].text = (unichar_t *) _("Warn if _unlinked references");
3208 	label[k].text_is_1byte = true;
3209 	label[k].text_in_resource = true;
3210 	gcd[k].gd.label = &label[k];
3211 	gcd[k].gd.cid = CID_RefContourWarn;
3212 	gcd[k].gd.popup_msg = _("Warn if one glyph contains an outline while the other contains a reference (but the reference describes the same outline)");
3213 	gcd[k++].creator = GCheckBoxCreate;
3214 
3215 	gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
3216 	if ( fv->b.sf->onlybitmaps )
3217 	    gcd[k].gd.flags = gg_visible;
3218 	else
3219 	    gcd[k].gd.flags = gg_visible | gg_enabled| ((last_flags&fcf_hinting)?gg_cb_on:0);
3220 	label[k].text = (unichar_t *) _("Compare _Hints");
3221 	label[k].text_is_1byte = true;
3222 	label[k].text_in_resource = true;
3223 	gcd[k].gd.label = &label[k];
3224 	gcd[k].gd.cid = CID_Hinting;
3225 	gcd[k].gd.popup_msg = _("Compare postscript hints and hintmasks and truetype instructions");
3226 	gcd[k++].creator = GCheckBoxCreate;
3227 
3228 	gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
3229 	if ( fv->b.sf->onlybitmaps )
3230 	    gcd[k].gd.flags = gg_visible;
3231 	else
3232 	    gcd[k].gd.flags = gg_visible | gg_enabled| (((last_flags&fcf_hintmasks) && !(last_flags&fcf_hmonlywithconflicts))?gg_cb_on:0);
3233 	label[k].text = (unichar_t *) _("Compare Hint_Masks");
3234 	label[k].text_is_1byte = true;
3235 	label[k].text_in_resource = true;
3236 	gcd[k].gd.label = &label[k];
3237 	gcd[k].gd.cid = CID_HintMasks;
3238 	gcd[k].gd.popup_msg = _("Compare hintmasks");
3239 	gcd[k++].creator = GRadioCreate;
3240 
3241 	gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
3242 	if ( fv->b.sf->onlybitmaps )
3243 	    gcd[k].gd.flags = gg_visible;
3244 	else
3245 	    gcd[k].gd.flags = gg_visible | gg_enabled| ((last_flags&fcf_hmonlywithconflicts)?gg_cb_on:0);
3246 	label[k].text = (unichar_t *) _("HintMasks only if conflicts");
3247 	label[k].text_is_1byte = true;
3248 	label[k].text_in_resource = true;
3249 	gcd[k].gd.label = &label[k];
3250 	gcd[k].gd.cid = CID_HintMasksWConflicts;
3251 	gcd[k].gd.popup_msg = _("Don't compare hintmasks if the glyph has no hint conflicts");
3252 	gcd[k++].creator = GRadioCreate;
3253 
3254 	gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
3255 	if ( fv->b.sf->onlybitmaps )
3256 	    gcd[k].gd.flags = gg_visible ;
3257 	else
3258 	    gcd[k].gd.flags = gg_visible | gg_enabled | ((last_flags&fcf_hintmasks)?0:gg_cb_on);
3259 	label[k].text = (unichar_t *) _("Don't Compare HintMasks");
3260 	label[k].text_is_1byte = true;
3261 	label[k].text_in_resource = true;
3262 	gcd[k].gd.label = &label[k];
3263 	gcd[k].gd.cid = CID_NoHintMasks;
3264 	gcd[k++].creator = GRadioCreate;
3265 
3266 	gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
3267 	if ( fv->b.sf->onlybitmaps )
3268 	    gcd[k].gd.flags = gg_visible;
3269 	else
3270 	    gcd[k].gd.flags = gg_visible | gg_enabled| ((last_flags&fcf_adddiff2sf1)?gg_cb_on:0);
3271 	label[k].text = (unichar_t *) _("_Add Diff Outlines to Background");
3272 	label[k].text_is_1byte = true;
3273 	label[k].text_in_resource = true;
3274 	gcd[k].gd.label = &label[k];
3275 	gcd[k].gd.cid = CID_AddDiffs;
3276 	gcd[k].gd.popup_msg = _("If two glyphs differ, then add the outlines of the second glyph\nto the background layer of the first (So when opening the first\nthe differences will be visible).");
3277 	gcd[k++].creator = GCheckBoxCreate;
3278 
3279 	gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
3280 	if ( fv->b.sf->onlybitmaps )
3281 	    gcd[k].gd.flags = gg_visible;
3282 	else
3283 	    gcd[k].gd.flags = gg_visible | gg_enabled| ((last_flags&fcf_addmissing)?gg_cb_on:0);
3284 	label[k].text = (unichar_t *) _("Add _Missing Glyphs");
3285 	label[k].text_is_1byte = true;
3286 	label[k].text_in_resource = true;
3287 	gcd[k].gd.label = &label[k];
3288 	gcd[k].gd.cid = CID_AddMissing;
3289 	gcd[k].gd.popup_msg = _("If a glyph in the second font is missing from the first, then\nadd it to the first with the outlines of the second font in\nthe background");
3290 	gcd[k++].creator = GCheckBoxCreate;
3291 
3292 	gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+16;
3293 	if ( fv->b.sf->bitmaps==NULL )
3294 	    gcd[k].gd.flags = gg_visible;
3295 	else
3296 	    gcd[k].gd.flags = gg_visible | gg_enabled | ((last_flags&fcf_bitmaps)?gg_cb_on:0);
3297 	label[k].text = (unichar_t *) _("Compare _Bitmaps");
3298 	label[k].text_is_1byte = true;
3299 	label[k].text_in_resource = true;
3300 	gcd[k].gd.label = &label[k];
3301 	gcd[k].gd.cid = CID_Bitmaps;
3302 	gcd[k++].creator = GCheckBoxCreate;
3303 
3304 	gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
3305 	gcd[k].gd.flags = gg_visible | gg_enabled | ((last_flags&fcf_names)?gg_cb_on:0);
3306 	label[k].text = (unichar_t *) _("Compare _Names");
3307 	label[k].text_is_1byte = true;
3308 	label[k].text_in_resource = true;
3309 	gcd[k].gd.label = &label[k];
3310 	gcd[k].gd.cid = CID_Names;
3311 	gcd[k++].creator = GCheckBoxCreate;
3312 
3313 	gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
3314 	gcd[k].gd.flags = gg_visible | gg_enabled| ((last_flags&fcf_gpos)?gg_cb_on:0);
3315 	label[k].text = (unichar_t *) _("Compare Glyph _Positioning");
3316 	label[k].text_is_1byte = true;
3317 	label[k].text_in_resource = true;
3318 	gcd[k].gd.label = &label[k];
3319 	gcd[k].gd.cid = CID_GPos;
3320 	gcd[k].gd.popup_msg = _("Kerning & such");
3321 	gcd[k++].creator = GCheckBoxCreate;
3322 
3323 	gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
3324 	gcd[k].gd.flags = gg_visible | gg_enabled| ((last_flags&fcf_gsub)?gg_cb_on:0);
3325 	label[k].text = (unichar_t *) _("Compare Glyph _Substitution");
3326 	label[k].text_is_1byte = true;
3327 	label[k].text_in_resource = true;
3328 	gcd[k].gd.label = &label[k];
3329 	gcd[k].gd.cid = CID_GSub;
3330 	gcd[k].gd.popup_msg = _("Ligatures & such");
3331 	gcd[k++].creator = GCheckBoxCreate;
3332 
3333 	gcd[k].gd.pos.x = 15-3; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+23-3;
3334 	gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
3335 	gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
3336 	label[k].text = (unichar_t *) _("_OK");
3337 	label[k].text_is_1byte = true;
3338 	label[k].text_in_resource = true;
3339 	gcd[k].gd.label = &label[k];
3340 	gcd[k].gd.handle_controlevent = FC_OK;
3341 	gcd[k++].creator = GButtonCreate;
3342 
3343 	gcd[k].gd.pos.x = -15; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+3;
3344 	gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
3345 	gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
3346 	label[k].text = (unichar_t *) _("_Cancel");
3347 	label[k].text_is_1byte = true;
3348 	label[k].text_in_resource = true;
3349 	gcd[k].gd.label = &label[k];
3350 	gcd[k].gd.handle_controlevent = FC_Cancel;
3351 	gcd[k++].creator = GButtonCreate;
3352 
3353 	gcd[k].gd.pos.x = 2; gcd[k].gd.pos.y = 2;
3354 	gcd[k].gd.pos.width = pos.width-4; gcd[k].gd.pos.height = pos.height-2;
3355 	gcd[k].gd.flags = gg_enabled | gg_visible | gg_pos_in_pixels;
3356 	gcd[k++].creator = GGroupCreate;
3357 
3358 	GGadgetsCreate(gw,gcd);
3359 
3360 	memset(&d,'\0',sizeof(d));
3361 	d.other = gcd[1].ret;
3362 	d.fv = fv;
3363 
3364 	GWidgetHidePalettes();
3365 	GDrawSetVisible(gw,true);
3366 	while ( !d.done )
3367 	    GDrawProcessOneEvent(NULL);
3368 	TFFree(gcd[1].gd.u.list);
3369 }
3370