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