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