1 /* -*- coding: utf-8 -*- */
2 /* Copyright (C) 2000-2012 by George Williams */
3 /*
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6 
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9 
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13 
14  * The name of the author may not be used to endorse or promote products
15  * derived from this software without specific prior written permission.
16 
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <fontforge-config.h>
30 
31 #include "autohint.h"
32 #include "cvundoes.h"
33 #include "fontforgeui.h"
34 #include "fvfonts.h"
35 #include "gkeysym.h"
36 #include "gwidget.h"
37 #include "namelist.h"
38 #include "splineorder2.h"
39 #include "splineoverlap.h"
40 #include "splinesaveafm.h"
41 #include "splineutil.h"
42 #include "splineutil2.h"
43 #include "tottf.h"
44 #include "tottfgpos.h"
45 #include "ttf.h"
46 #include "ustring.h"
47 
48 #include <math.h>
49 
50 /* ************************************************************************** */
51 /* ***************************** Problems Dialog **************************** */
52 /* ************************************************************************** */
53 
54 struct mgrpl {
55     char *search;
56     char *rpl;		/* a rpl of "" means delete (NULL means not found) */
57 };
58 
59 struct mlrpl {
60     uint32 search;
61     uint32 rpl;
62 };
63 
64 struct problems {
65     FontView *fv;
66     CharView *cv;
67     SplineChar *sc;
68     SplineChar *msc;
69     int layer;
70     unsigned int openpaths: 1;
71     unsigned int intersectingpaths: 1;
72     unsigned int nonintegral: 1;
73     unsigned int pointstooclose: 1;
74     unsigned int pointstoofar: 1;
75     unsigned int xnearval: 1;
76     unsigned int ynearval: 1;
77     unsigned int ynearstd: 1;		/* baseline, xheight, cap, ascent, descent, etc. */
78     unsigned int linenearstd: 1;	/* horizontal, vertical, italicangle */
79     unsigned int cpnearstd: 1;		/* control points near: horizontal, vertical, italicangle */
80     unsigned int cpodd: 1;		/* control points beyond points on spline */
81     unsigned int hintwithnopt: 1;
82     unsigned int ptnearhint: 1;
83     unsigned int hintwidthnearval: 1;
84     unsigned int missingextrema: 1;
85     unsigned int direction: 1;
86     unsigned int flippedrefs: 1;
87     unsigned int cidmultiple: 1;
88     unsigned int cidblank: 1;
89     unsigned int bitmaps: 1;
90     unsigned int bitmapwidths: 1;
91     unsigned int advancewidth: 1;
92     unsigned int vadvancewidth: 1;
93     unsigned int stem3: 1;
94     unsigned int showexactstem3: 1;
95     unsigned int irrelevantcontrolpoints: 1;
96     unsigned int multuni: 1;
97     unsigned int multname: 1;
98     unsigned int uninamemismatch: 1;
99     unsigned int missinganchor: 1;
100     unsigned int badsubs: 1;
101     unsigned int missingglyph: 1;
102     unsigned int missingscriptinfeature: 1;
103     unsigned int toomanypoints: 1;
104     unsigned int toomanyhints: 1;
105     unsigned int toodeeprefs: 1;
106     unsigned int ptmatchrefsoutofdate: 1;
107     unsigned int multusemymetrics: 1;
108     unsigned int refsbadtransformttf: 1;
109     unsigned int refsbadtransformps: 1;
110     unsigned int mixedcontoursrefs: 1;
111     unsigned int bbymax: 1;
112     unsigned int bbymin: 1;
113     unsigned int bbxmax: 1;
114     unsigned int bbxmin: 1;
115     unsigned int overlappedhints: 1;
116     unsigned int explain: 1;
117     unsigned int done: 1;
118     unsigned int doneexplain: 1;
119     unsigned int finish: 1;
120     unsigned int ignorethis: 1;
121     double near, xval, yval, widthval;
122     char *explaining;
123     double found, expected;
124     double xheight, caph, ascent, descent;
125     double irrelevantfactor;
126     int advancewidthval, vadvancewidthval;
127     int bbymax_val, bbymin_val, bbxmax_val, bbxmin_val;
128     int pointsmax, hintsmax, refdepthmax;
129     GWindow explainw;
130     GGadget *explaintext, *explainvals, *ignoregadg, *topbox;
131     SplineChar *lastcharopened;
132     CharView *cvopened;
133     char *badsubsname;
134     struct lookup_subtable *badsubs_lsubtable;
135     AnchorClass *missinganchor_class;
136     int rpl_cnt, rpl_max;
137     struct mgrpl *mg;
138     struct mlrpl *mlt;
139     char *glyphname;
140     int glyphenc;
141     EncMap *map;
142 };
143 
144 static int openpaths=0, pointstooclose=0/*, missing=0*/, doxnear=0, doynear=0;
145 static int nonintegral=0, pointstoofar=0;
146 static int intersectingpaths=0, missingextrema=0;
147 static int doynearstd=0, linestd=0, cpstd=0, cpodd=0, hintnopt=0, ptnearhint=0;
148 static int hintwidth=0, direction=0, flippedrefs=0, bitmaps=0, bitmapwidths=0;
149 static int cidblank=0, cidmultiple=0, advancewidth=0, vadvancewidth=0;
150 static int bbymax=0, bbymin=0, bbxmax=0, bbxmin=0;
151 static int irrelevantcp=0, missingglyph=0, missingscriptinfeature=0;
152 static int badsubs=0, missinganchor=0, toomanypoints=0, pointsmax = 1500;
153 static int multuni=0, multname=0, uninamemismatch=0, overlappedhints=0;
154 static int toomanyhints=0, hintsmax=96, toodeeprefs=0, refdepthmax=9;
155 static int ptmatchrefsoutofdate=0, refsbadtransformttf=0, refsbadtransformps=0;
156 static int mixedcontoursrefs=0, multusemymetrics=0;
157 static int stem3=0, showexactstem3=0;
158 static double near=3, xval=0, yval=0, widthval=50, advancewidthval=0, vadvancewidthval=0;
159 static double bbymax_val=0, bbymin_val=0, bbxmax_val=0, bbxmin_val=0;
160 static double irrelevantfactor = .005;
161 static SplineFont *lastsf=NULL;
162 
163 #define CID_Stop		2001
164 #define CID_Next		2002
165 #define CID_Fix			2003
166 #define CID_ClearAll		2004
167 #define CID_SetAll		2005
168 
169 #define CID_OpenPaths		1001
170 #define CID_IntersectingPaths	1002
171 #define CID_PointsTooClose	1003
172 #define CID_XNear		1004
173 #define CID_YNear		1005
174 #define CID_YNearStd		1006
175 #define CID_HintNoPt		1007
176 #define CID_PtNearHint		1008
177 #define CID_HintWidthNear	1009
178 #define CID_HintWidth		1010
179 #define CID_Near		1011
180 #define CID_XNearVal		1012
181 #define CID_YNearVal		1013
182 #define CID_LineStd		1014
183 #define CID_Direction		1015
184 #define CID_CpStd		1016
185 #define CID_CpOdd		1017
186 #define CID_CIDMultiple		1018
187 #define CID_CIDBlank		1019
188 #define CID_FlippedRefs		1020
189 #define CID_Bitmaps		1021
190 #define CID_AdvanceWidth	1022
191 #define CID_AdvanceWidthVal	1023
192 #define CID_VAdvanceWidth	1024
193 #define CID_VAdvanceWidthVal	1025
194 #define CID_Stem3		1026
195 #define CID_ShowExactStem3	1027
196 #define CID_IrrelevantCP	1028
197 #define CID_IrrelevantFactor	1029
198 #define CID_BadSubs		1030
199 #define CID_MissingGlyph	1031
200 #define CID_MissingScriptInFeature 1032
201 #define CID_TooManyPoints	1033
202 #define CID_PointsMax		1034
203 #define CID_TooManyHints	1035
204 #define CID_HintsMax		1036
205 #define CID_TooDeepRefs		1037
206 #define CID_RefDepthMax		1038
207 #define CID_MultUni		1040
208 #define CID_MultName		1041
209 #define CID_PtMatchRefsOutOfDate 1042
210 #define CID_RefBadTransformTTF	1043
211 #define CID_RefBadTransformPS	1044
212 #define CID_MixedContoursRefs	1045
213 #define CID_MissingExtrema	1046
214 #define CID_UniNameMisMatch	1047
215 #define CID_BBYMax		1048
216 #define CID_BBYMin		1049
217 #define CID_BBXMax		1050
218 #define CID_BBXMin		1051
219 #define CID_BBYMaxVal		1052
220 #define CID_BBYMinVal		1053
221 #define CID_BBXMaxVal		1054
222 #define CID_BBXMinVal		1055
223 #define CID_NonIntegral		1056
224 #define CID_PointsTooFar	1057
225 #define CID_BitmapWidths	1058
226 #define CID_MissingAnchor	1059
227 #define CID_MultUseMyMetrics	1060
228 #define CID_OverlappedHints	1061
229 
230 
FixIt(struct problems * p)231 static void FixIt(struct problems *p) {
232     SplinePointList *spl;
233     SplinePoint *sp;
234     /*StemInfo *h;*/
235     RefChar *r;
236     int ncp_changed, pcp_changed;
237 
238     if ( p->explaining==_("This reference has been flipped, so the paths in it are drawn backwards") ) {
239 	for ( r=p->sc->layers[p->layer].refs; r!=NULL && !r->selected; r = r->next );
240 	if ( r!=NULL ) {
241 	    SplineSet *ss, *spl;
242 	    SCPreserveLayer(p->sc,p->layer,false);
243 	    ss = p->sc->layers[p->layer].splines;
244 	    p->sc->layers[p->layer].splines = NULL;
245 	    SCRefToSplines(p->sc,r,p->layer);
246 	    for ( spl = p->sc->layers[p->layer].splines; spl!=NULL; spl=spl->next )
247 		SplineSetReverse(spl);
248 	    if ( p->sc->layers[p->layer].splines!=NULL ) {
249 		for ( spl = p->sc->layers[p->layer].splines; spl->next!=NULL; spl=spl->next );
250 		spl->next = ss;
251 	    } else
252 		p->sc->layers[p->layer].splines = ss;
253 	    SCCharChangedUpdate(p->sc,p->layer);
254 	} else
255 	    IError("Could not find referenc");
256 return;
257     } else if ( p->explaining==_("This glyph's advance width is different from the standard width") ) {
258 	SCSynchronizeWidth(p->sc,p->advancewidthval,p->sc->width,NULL);
259 return;
260     } else if ( p->explaining==_("This glyph's vertical advance is different from the standard width") ) {
261 	p->sc->vwidth=p->vadvancewidthval;
262 return;
263     }
264 
265     if ( p->explaining==_("This glyph is not mapped to any unicode code point, but its name should be.") ||
266 	    p->explaining==_("This glyph is mapped to a unicode code point which is different from its name.") ) {
267 	char buf[100]; const char *newname;
268 	SplineChar *foundsc;
269 	newname = StdGlyphName(buf,p->sc->unicodeenc,p->sc->parent->uni_interp,p->sc->parent->for_new_glyphs);
270 	foundsc = SFHashName(p->sc->parent,newname);
271 	if ( foundsc==NULL ) {
272 	    free(p->sc->name);
273 	    p->sc->name = copy(newname);
274 	} else {
275 	    ff_post_error(_("Can't fix"), _("The name FontForge would like to assign to this glyph, %.30s, is already used by a different glyph."),
276 		    newname );
277 	}
278 return;
279     }
280 
281     sp = NULL;
282     for ( spl=p->sc->layers[p->layer].splines; spl!=NULL; spl=spl->next ) {
283 	for ( sp = spl->first; ; ) {
284 	    if ( sp->selected )
285 	break;
286 	    if ( sp->next==NULL )
287 	break;
288 	    sp = sp->next->to;
289 	    if ( sp==spl->first )
290 	break;
291 	}
292 	if ( sp->selected )
293     break;
294     }
295     if ( sp==NULL ) {
296 	IError("Nothing selected");
297 return;
298     }
299 
300 /* I do not handle:
301     _("The two selected points are the endpoints of an open path")
302     _("The paths that make up this glyph intersect one another")
303     _("The selected points are too close to each other")
304     _("The selected line segment is near the italic angle"), _("The control point above the selected point is near the italic angle"), _("The control point below the selected point is near the italic angle"), _("The control point right of the selected point is near the italic angle"), _("The control point left of the selected point is near the italic angle")
305     _("The control point above the selected point is outside the spline segment"), _("The control point below the selected point is outside the spline segment"), _("The control point right of the selected point is outside the spline segment"), _("The control point left of the selected point is outside the spline segment")
306     _("This hint does not control any points")
307     _STR_ProbHint3*
308     _STR_ProbMultUni, STR_ProbMultName
309 */
310 
311     SCPreserveLayer(p->sc,p->layer,false);
312     ncp_changed = pcp_changed = false;
313     if ( p->explaining==_("The x coord of the selected point is near the specified value") || p->explaining==_("The selected point is near a vertical stem hint")) {
314 	sp->prevcp.x += p->expected-sp->me.x;
315 	sp->nextcp.x += p->expected-sp->me.x;
316 	sp->me.x = p->expected;
317 	ncp_changed = pcp_changed = true;
318     } else if ( p->explaining==_("The selected point is not at integral coordinates") ||
319 	    p->explaining==_("The selected point does not have integral control points")) {
320 	sp->me.x = rint(sp->me.x); sp->me.y = rint(sp->me.y);
321 	sp->nextcp.x = rint(sp->nextcp.x); sp->nextcp.y = rint(sp->nextcp.y);
322 	sp->prevcp.x = rint(sp->prevcp.x); sp->prevcp.y = rint(sp->prevcp.y);
323 	ncp_changed = pcp_changed = true;
324     } else if ( p->explaining==_("The y coord of the selected point is near the specified value") || p->explaining==_("The selected point is near a horizontal stem hint") ||
325 	    p->explaining==_("The y coord of the selected point is near the baseline") || p->explaining==_("The y coord of the selected point is near the xheight") ||
326 	    p->explaining==_("The y coord of the selected point is near the cap height") || p->explaining==_("The y coord of the selected point is near the ascender height") ||
327 	    p->explaining==_("The y coord of the selected point is near the descender height") ) {
328 	sp->prevcp.y += p->expected-sp->me.y;
329 	sp->nextcp.y += p->expected-sp->me.y;
330 	sp->me.y = p->expected;
331 	ncp_changed = pcp_changed = true;
332     } else if ( p->explaining==_("The selected spline attains its extrema somewhere other than its endpoints") ) {
333 	SplineCharAddExtrema(p->sc,p->sc->layers[p->layer].splines,
334 		ae_between_selected,p->sc->parent->ascent+p->sc->parent->descent);
335     } else if ( p->explaining==_("The selected line segment is nearly horizontal") ) {
336 	if ( sp->me.y!=p->found ) {
337 	    sp=sp->next->to;
338 	    if ( !sp->selected || sp->me.y!=p->found ) {
339 		IError("Couldn't find line");
340 return;
341 	    }
342 	}
343 	sp->prevcp.y += p->expected-sp->me.y;
344 	sp->nextcp.y += p->expected-sp->me.y;
345 	sp->me.y = p->expected;
346 	ncp_changed = pcp_changed = true;
347     } else if ( p->explaining==_("The control point above the selected point is nearly horizontal") || p->explaining==_("The control point below the selected point is nearly horizontal") ||
348 	    p->explaining==_("The control point right of the selected point is nearly horizontal") || p->explaining==_("The control point left of the selected point is nearly horizontal") ) {
349 	BasePoint *tofix, *other;
350 	if ( sp->nextcp.y==p->found ) {
351 	    tofix = &sp->nextcp;
352 	    other = &sp->prevcp;
353 	} else {
354 	    tofix = &sp->prevcp;
355 	    other = &sp->nextcp;
356 	}
357 	if ( tofix->y!=p->found ) {
358 	    IError("Couldn't find control point");
359 return;
360 	}
361 	tofix->y = p->expected;
362 	ncp_changed = pcp_changed = true;
363 	if ( sp->pointtype==pt_curve || sp->pointtype==pt_hvcurve )
364 	    other->y = p->expected;
365 	else {
366 	    sp->pointtype = pt_corner;
367 	    if ( other==&sp->nextcp )
368 		ncp_changed = false;
369 	    else
370 		pcp_changed = false;
371 	}
372     } else if ( p->explaining==_("The selected line segment is nearly vertical") ) {
373 	if ( sp->me.x!=p->found ) {
374 	    sp=sp->next->to;
375 	    if ( !sp->selected || sp->me.x!=p->found ) {
376 		IError("Couldn't find line");
377 return;
378 	    }
379 	}
380 	sp->prevcp.x += p->expected-sp->me.x;
381 	sp->nextcp.x += p->expected-sp->me.x;
382 	sp->me.x = p->expected;
383 	ncp_changed = pcp_changed = true;
384     } else if ( p->explaining==_("The control point above the selected point is nearly vertical") || p->explaining==_("The control point below the selected point is nearly vertical") ||
385 	    p->explaining==_("The control point right of the selected point is nearly vertical") || p->explaining==_("The control point left of the selected point is nearly vertical") ) {
386 	BasePoint *tofix, *other;
387 	if ( sp->nextcp.x==p->found ) {
388 	    tofix = &sp->nextcp;
389 	    other = &sp->prevcp;
390 	} else {
391 	    tofix = &sp->prevcp;
392 	    other = &sp->nextcp;
393 	}
394 	if ( tofix->x!=p->found ) {
395 	    IError("Couldn't find control point");
396 return;
397 	}
398 	tofix->x = p->expected;
399 	ncp_changed = pcp_changed = true;
400 	if ( sp->pointtype==pt_curve || sp->pointtype==pt_hvcurve )
401 	    other->x = p->expected;
402 	else {
403 	    sp->pointtype = pt_corner;
404 	    if ( other==&sp->nextcp )
405 		ncp_changed = false;
406 	    else
407 		pcp_changed = false;
408 	}
409     } else if ( p->explaining==_("This path should have been drawn in a counter-clockwise direction") || p->explaining==_("This path should have been drawn in a clockwise direction") ) {
410 	SplineSetReverse(spl);
411     } else if ( p->explaining==_("This glyph contains control points which are probably too close to the main points to alter the look of the spline") ) {
412 	if ( sp->next!=NULL ) {
413 	    double len = sqrt((sp->me.x-sp->next->to->me.x)*(sp->me.x-sp->next->to->me.x) +
414 		    (sp->me.y-sp->next->to->me.y)*(sp->me.y-sp->next->to->me.y));
415 	    double cplen = sqrt((sp->me.x-sp->nextcp.x)*(sp->me.x-sp->nextcp.x) +
416 		    (sp->me.y-sp->nextcp.y)*(sp->me.y-sp->nextcp.y));
417 	    if ( cplen!=0 && cplen<p->irrelevantfactor*len ) {
418 		sp->nextcp = sp->me;
419 		sp->nonextcp = true;
420 		ncp_changed = true;
421 	    }
422 	}
423 	if ( sp->prev!=NULL ) {
424 	    double len = sqrt((sp->me.x-sp->prev->from->me.x)*(sp->me.x-sp->prev->from->me.x) +
425 		    (sp->me.y-sp->prev->from->me.y)*(sp->me.y-sp->prev->from->me.y));
426 	    double cplen = sqrt((sp->me.x-sp->prevcp.x)*(sp->me.x-sp->prevcp.x) +
427 		    (sp->me.y-sp->prevcp.y)*(sp->me.y-sp->prevcp.y));
428 	    if ( cplen!=0 && cplen<p->irrelevantfactor*len ) {
429 		sp->prevcp = sp->me;
430 		sp->noprevcp = true;
431 		pcp_changed = true;
432 	    }
433 	}
434     } else
435 	IError("Did not fix: %d", p->explaining );
436     if ( p->sc->layers[p->layer].order2 ) {
437 	if ( ncp_changed )
438 	    SplinePointNextCPChanged2(sp);
439 	if ( pcp_changed )
440 	    SplinePointPrevCPChanged2(sp);
441     }
442     if ( sp->next!=NULL )
443 	SplineRefigure(sp->next);
444     if ( sp->prev!=NULL )
445 	SplineRefigure(sp->prev);
446     SCCharChangedUpdate(p->sc,p->layer);
447 }
448 
explain_e_h(GWindow gw,GEvent * event)449 static int explain_e_h(GWindow gw, GEvent *event) {
450     if ( event->type==et_close ) {
451 	struct problems *p = GDrawGetUserData(gw);
452 	p->doneexplain = true;
453     } else if ( event->type==et_controlevent &&
454 	    event->u.control.subtype == et_buttonactivate ) {
455 	struct problems *p = GDrawGetUserData(gw);
456 	if ( GGadgetGetCid(event->u.control.g)==CID_Stop )
457 	    p->finish = true;
458 	else if ( GGadgetGetCid(event->u.control.g)==CID_Fix )
459 	    FixIt(p);
460 	p->doneexplain = true;
461     } else if ( event->type==et_controlevent &&
462 	    event->u.control.subtype == et_radiochanged ) {
463 	struct problems *p = GDrawGetUserData(gw);
464 	p->ignorethis = GGadgetIsChecked(event->u.control.g);
465     } else if ( event->type==et_char ) {
466 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
467 	    help("ui/dialogs/problems.html", NULL);
468 return( true );
469 	}
470 return( false );
471     }
472 return( true );
473 }
474 
ExplainIt(struct problems * p,SplineChar * sc,char * explain,real found,real expected)475 static void ExplainIt(struct problems *p, SplineChar *sc, char *explain,
476 	real found, real expected ) {
477     GRect pos;
478     GWindowAttrs wattrs;
479     GGadgetCreateData gcd[9], boxes[3], *varray[10], *barray[14];
480     GTextInfo label[9];
481     char buf[200];
482     SplinePointList *spl; Spline *spline, *first;
483     int fixable;
484 
485     if ( !p->explain || p->finish )
486 return;
487     if ( p->explainw==NULL ) {
488 	memset(&wattrs,0,sizeof(wattrs));
489 	wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor;
490 	wattrs.event_masks = ~(1<<et_charup);
491 	wattrs.undercursor = 1;
492 	wattrs.cursor = ct_pointer;
493 	wattrs.utf8_window_title = _("Problem explanation");
494 	pos.x = pos.y = 0;
495 	pos.width = GGadgetScale(GDrawPointsToPixels(NULL,400));
496 	pos.height = GDrawPointsToPixels(NULL,86);
497 	p->explainw = GDrawCreateTopWindow(NULL,&pos,explain_e_h,p,&wattrs);
498 
499 	memset(&label,0,sizeof(label));
500 	memset(&gcd,0,sizeof(gcd));
501 	memset(&boxes,0,sizeof(boxes));
502 
503 	label[0].text = (unichar_t *) explain;
504 	label[0].text_is_1byte = true;
505 	gcd[0].gd.label = &label[0];
506 	gcd[0].gd.pos.x = 6; gcd[0].gd.pos.y = 6; gcd[0].gd.pos.width = 400-12;
507 	gcd[0].gd.flags = gg_visible | gg_enabled;
508 	gcd[0].creator = GLabelCreate;
509 	varray[0] = &gcd[0]; varray[1] = NULL;
510 
511 	label[4].text = (unichar_t *) "";
512 	label[4].text_is_1byte = true;
513 	gcd[4].gd.label = &label[4];
514 	gcd[4].gd.pos.x = 6; gcd[4].gd.pos.y = gcd[0].gd.pos.y+12; gcd[4].gd.pos.width = 400-12;
515 	gcd[4].gd.flags = gg_visible | gg_enabled;
516 	gcd[4].creator = GLabelCreate;
517 	varray[2] = &gcd[4]; varray[3] = NULL;
518 
519 	label[5].text = (unichar_t *) _("Ignore this problem in the future");
520 	label[5].text_is_1byte = true;
521 	gcd[5].gd.label = &label[5];
522 	gcd[5].gd.pos.x = 6; gcd[5].gd.pos.y = gcd[4].gd.pos.y+12;
523 	gcd[5].gd.flags = gg_visible | gg_enabled;
524 	gcd[5].creator = GCheckBoxCreate;
525 	varray[4] = &gcd[5]; varray[5] = NULL;
526 
527 	gcd[1].gd.pos.x = 15-3; gcd[1].gd.pos.y = gcd[5].gd.pos.y+20;
528 	gcd[1].gd.pos.width = -1; gcd[1].gd.pos.height = 0;
529 	gcd[1].gd.flags = gg_visible | gg_enabled | gg_but_default;
530 	label[1].text = (unichar_t *) _("_Next");
531 	label[1].text_is_1byte = true;
532 	label[1].text_in_resource = true;
533 	gcd[1].gd.mnemonic = 'N';
534 	gcd[1].gd.label = &label[1];
535 	gcd[1].gd.cid = CID_Next;
536 	gcd[1].creator = GButtonCreate;
537 	barray[0] = GCD_Glue; barray[1] = &gcd[1]; barray[2] = GCD_Glue; barray[3] = GCD_Glue;
538 
539 	gcd[6].gd.pos.x = 200-30; gcd[6].gd.pos.y = gcd[2].gd.pos.y;
540 	gcd[6].gd.pos.width = -1; gcd[6].gd.pos.height = 0;
541 	gcd[6].gd.flags = /*gg_visible |*/ gg_enabled;
542 	label[6].text = (unichar_t *) _("Fix");
543 	label[6].text_is_1byte = true;
544 	gcd[6].gd.mnemonic = 'F';
545 	gcd[6].gd.label = &label[6];
546 	gcd[6].gd.cid = CID_Fix;
547 	gcd[6].creator = GButtonCreate;
548 	barray[4] = GCD_Glue; barray[5] = GCD_Glue; barray[6] = &gcd[6]; barray[7] = GCD_Glue;
549 
550 	gcd[2].gd.pos.x = -15; gcd[2].gd.pos.y = gcd[1].gd.pos.y+3;
551 	gcd[2].gd.pos.width = -1; gcd[2].gd.pos.height = 0;
552 	gcd[2].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
553 	label[2].text = (unichar_t *) _("_Stop");
554 	label[2].text_is_1byte = true;
555 	label[2].text_in_resource = true;
556 	gcd[2].gd.label = &label[2];
557 	gcd[2].gd.mnemonic = 'S';
558 	gcd[2].gd.cid = CID_Stop;
559 	gcd[2].creator = GButtonCreate;
560 	barray[8] = GCD_Glue; barray[9] = GCD_Glue; barray[10] = GCD_Glue;
561 	barray[11] = &gcd[2]; barray[12] = GCD_Glue;
562 	barray[13] = NULL;
563 
564 	boxes[2].gd.flags = gg_enabled|gg_visible;
565 	boxes[2].gd.u.boxelements = barray;
566 	boxes[2].creator = GHBoxCreate;
567 	varray[6] = &boxes[2]; varray[7] = NULL; varray[8] = NULL;
568 
569 	boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
570 	boxes[0].gd.flags = gg_enabled|gg_visible;
571 	boxes[0].gd.u.boxelements = varray;
572 	boxes[0].creator = GHVGroupCreate;
573 
574 	GGadgetsCreate(p->explainw,boxes);
575 	GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame);
576 	p->explaintext = gcd[0].ret;
577 	p->explainvals = gcd[4].ret;
578 	p->ignoregadg = gcd[5].ret;
579 	p->topbox = boxes[0].ret;
580     } else
581 	GGadgetSetTitle8(p->explaintext,explain);
582     p->explaining = explain;
583     fixable = /*explain==_("This glyph contains a horizontal hint near the specified width") || explain==_("This glyph contains a vertical hint near the specified width") ||*/
584 	    explain==_("This reference has been flipped, so the paths in it are drawn backwards") ||
585 	    explain==_("The x coord of the selected point is near the specified value") || explain==_("The selected point is near a vertical stem hint") ||
586 	    explain==_("The y coord of the selected point is near the specified value") || explain==_("The selected point is near a horizontal stem hint") ||
587 	    explain==_("This glyph contains control points which are probably too close to the main points to alter the look of the spline") ||
588 	    explain==_("The y coord of the selected point is near the baseline") || explain==_("The y coord of the selected point is near the xheight") ||
589 	    explain==_("The y coord of the selected point is near the cap height") || explain==_("The y coord of the selected point is near the ascender height") ||
590 	    explain==_("The y coord of the selected point is near the descender height") ||
591 	    explain==_("The selected line segment is nearly horizontal") || explain==_("The selected line segment is nearly vertical") ||
592 	    explain==_("The control point above the selected point is nearly horizontal") || explain==_("The control point below the selected point is nearly horizontal") ||
593 	    explain==_("The control point right of the selected point is nearly horizontal") || explain==_("The control point left of the selected point is nearly horizontal") ||
594 	    explain==_("The control point above the selected point is nearly vertical") || explain==_("The control point below the selected point is nearly vertical") ||
595 	    explain==_("The control point right of the selected point is nearly vertical") || explain==_("The control point left of the selected point is nearly vertical") ||
596 	    explain==_("This path should have been drawn in a counter-clockwise direction") || explain==_("This path should have been drawn in a clockwise direction") ||
597 	    explain==_("The selected spline attains its extrema somewhere other than its endpoints") ||
598 	    explain==_("This glyph's advance width is different from the standard width") ||
599 	    explain==_("This glyph's vertical advance is different from the standard width") ||
600 	    explain==_("This glyph is not mapped to any unicode code point, but its name should be.") ||
601 	    explain==_("The selected point is not at integral coordinates") ||
602 	    explain==_("The selected point does not have integral control points") ||
603 	    explain==_("This glyph is mapped to a unicode code point which is different from its name.");
604 
605     GGadgetSetVisible(GWidgetGetControl(p->explainw,CID_Fix),fixable);
606 
607     if ( explain==_("This glyph contains a substitution or ligature entry which refers to an empty char") ) {
608 	snprintf(buf,sizeof(buf),
609 		_("%2$.20s refers to an empty character \"%1$.20s\""), p->badsubsname,
610 		p->badsubs_lsubtable->subtable_name );
611     } else if ( explain==_("This glyph contains anchor points from some, but not all anchor classes in a subtable") ) {
612 	snprintf(buf,sizeof(buf),
613 		_("There is no anchor for class %1$.30s in subtable %2$.30s"),
614 		p->missinganchor_class->name,
615 		p->missinganchor_class->subtable->subtable_name );
616     } else if ( explain==_("Two glyphs share the same unicode code point.\nChange the encoding to \"Glyph Order\" and use\nEdit->Select->Wildcard with the following code point") ) {
617 	snprintf(buf,sizeof(buf), _("U+%04x"), sc->unicodeenc );
618     } else if ( explain==_("Two glyphs have the same name.\nChange the encoding to \"Glyph Order\" and use\nEdit->Select->Wildcard with the following name") ) {
619 	snprintf(buf,sizeof(buf), _("%.40s"), sc->name );
620     } else if ( found==expected )
621 	buf[0]='\0';
622     else {
623 	sprintf(buf,_("Found %1$.4g, expected %2$.4g"), (double) found, (double) expected );
624     }
625     p->found = found; p->expected = expected;
626     GGadgetSetTitle8(p->explainvals,buf);
627     GGadgetSetChecked(p->ignoregadg,false);
628     GHVBoxFitWindow(p->topbox);
629 
630     p->doneexplain = false;
631     p->ignorethis = false;
632 
633     if ( sc!=p->lastcharopened || (CharView *) (sc->views)==NULL ) {
634 	if ( p->cvopened!=NULL && CVValid(p->fv->b.sf,p->lastcharopened,p->cvopened) )
635 	    GDrawDestroyWindow(p->cvopened->gw);
636 	p->cvopened = NULL;
637 	if ( (CharView *) (sc->views)!=NULL )
638 	    GDrawRaise(((CharView *) (sc->views))->gw);
639 	else
640 	    p->cvopened = CharViewCreate(sc,p->fv,-1);
641 	GDrawSync(NULL);
642 	GDrawProcessPendingEvents(NULL);
643 	GDrawProcessPendingEvents(NULL);
644 	p->lastcharopened = sc;
645     }
646     if ( explain==_("This glyph contains a substitution or ligature entry which refers to an empty char") ) {
647 	SCCharInfo(sc,p->layer,p->fv->b.map,-1);
648 	GDrawSync(NULL);
649 	GDrawProcessPendingEvents(NULL);
650 	GDrawProcessPendingEvents(NULL);
651     }
652 
653     SCUpdateAll(sc);		/* We almost certainly just selected something */
654 
655     GDrawSetVisible(p->explainw,true);
656     GDrawRaise(p->explainw);
657 
658     while ( !p->doneexplain )
659 	GDrawProcessOneEvent(NULL);
660     /*GDrawSetVisible(p->explainw,false);*/		/* KDE gets unhappy about this and refuses to show the window later. I don't know why */
661 
662     if ( p->cv!=NULL ) {
663 	CVClearSel(p->cv);
664     } else {
665 	for ( spl = p->sc->layers[p->layer].splines; spl!=NULL; spl = spl->next ) {
666 	    spl->first->selected = false;
667 	    first = NULL;
668 	    for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
669 		spline->to->selected = false;
670 		if ( first==NULL ) first = spline;
671 	    }
672 	}
673     }
674 }
675 
_ExplainIt(struct problems * p,int enc,char * explain,real found,real expected)676 static void _ExplainIt(struct problems *p, int enc, char *explain,
677 	real found, real expected ) {
678     ExplainIt(p,p->sc=SFMakeChar(p->fv->b.sf,p->fv->b.map,enc),explain,found,expected);
679 }
680 
681 /* if they deleted a point or a splineset while we were explaining then we */
682 /*  need to do some fix-ups. This routine detects a deletion and lets us know */
683 /*  that more processing is needed */
missing(struct problems * p,SplineSet * test,SplinePoint * sp)684 static int missing(struct problems *p,SplineSet *test, SplinePoint *sp) {
685     SplinePointList *spl, *check;
686     SplinePoint *tsp;
687 
688     if ( !p->explain )
689 return( false );
690 
691     if ( p->cv!=NULL )
692 	spl = p->cv->b.layerheads[p->cv->b.drawmode]->splines;
693     else
694 	spl = p->sc->layers[p->layer].splines;
695     for ( check = spl; check!=test && check!=NULL; check = check->next );
696     if ( check==NULL )
697 return( true );		/* Deleted splineset */
698 
699     if ( sp!=NULL ) {
700 	for ( tsp=test->first; tsp!=sp ; ) {
701 	    if ( tsp->next==NULL )
702 return( true );
703 	    tsp = tsp->next->to;
704 	    if ( tsp==test->first )
705 return( true );
706 	}
707     }
708 return( false );
709 }
710 
missingspline(struct problems * p,SplineSet * test,Spline * spline)711 static int missingspline(struct problems *p,SplineSet *test, Spline *spline) {
712     SplinePointList *spl, *check;
713     Spline *t, *first=NULL;
714 
715     if ( !p->explain )
716 return( false );
717 
718     if ( p->cv!=NULL )
719 	spl = p->cv->b.layerheads[p->cv->b.drawmode]->splines;
720     else
721 	spl = p->sc->layers[p->layer].splines;
722     for ( check = spl; check!=test && check!=NULL; check = check->next );
723     if ( check==NULL )
724 return( true );		/* Deleted splineset */
725 
726     for ( t=test->first->next; t!=NULL && t!=first && t!=spline; t = t->to->next )
727 	if ( first==NULL ) first = t;
728 return( t!=spline );
729 }
730 
missinghint(StemInfo * base,StemInfo * findme)731 static int missinghint(StemInfo *base, StemInfo *findme) {
732 
733     while ( base!=NULL && base!=findme )
734 	base = base->next;
735 return( base==NULL );
736 }
737 
missingschint(StemInfo * findme,SplineChar * sc)738 static int missingschint(StemInfo *findme, SplineChar *sc) {
739     StemInfo *base;
740 
741     for ( base = sc->hstem; base!=NULL; base=base->next )
742 	if ( base==findme )
743 return( false );		/* Hasn't been deleted */
744     for ( base = sc->vstem; base!=NULL; base=base->next )
745 	if ( base==findme )
746 return( false );
747 
748 return( true );
749 }
750 
HVITest(struct problems * p,BasePoint * to,BasePoint * from,Spline * spline,int hasia,real ia)751 static int HVITest(struct problems *p,BasePoint *to, BasePoint *from,
752 	Spline *spline, int hasia, real ia) {
753     real yoff, xoff, angle;
754     int ishor=false, isvert=false, isital=false;
755     int isto;
756     int type;
757     BasePoint *base, *other;
758     static char *hmsgs[5] = {
759 	N_("The selected line segment is nearly horizontal"),
760 	N_("The control point above the selected point is nearly horizontal"),
761 	N_("The control point below the selected point is nearly horizontal"),
762 	N_("The control point right of the selected point is nearly horizontal"),
763 	N_("The control point left of the selected point is nearly horizontal")
764     };
765     static char *vmsgs[5] = {
766 	N_("The selected line segment is nearly vertical"),
767 	N_("The control point above the selected point is nearly vertical"),
768 	N_("The control point below the selected point is nearly vertical"),
769 	N_("The control point right of the selected point is nearly vertical"),
770 	N_("The control point left of the selected point is nearly vertical")
771     };
772     static char *imsgs[5] = {
773 	N_("The selected line segment is near the italic angle"),
774 	N_("The control point above the selected point is near the italic angle"),
775 	N_("The control point below the selected point is near the italic angle"),
776 	N_("The control point right of the selected point is near the italic angle"),
777 	N_("The control point left of the selected point is near the italic angle")
778     };
779 
780     yoff = to->y-from->y;
781     xoff = to->x-from->x;
782     angle = atan2(yoff,xoff);
783     if ( angle<0 )
784 	angle += FF_PI;
785     if ( angle<.1 || angle>FF_PI-.1 ) {
786 	if ( yoff!=0 )
787 	    ishor = true;
788     } else if ( angle>1.5707963-.1 && angle<1.5707963+.1 ) {
789 	if ( xoff!=0 )
790 	    isvert = true;
791     } else if ( hasia && angle>ia-.1 && angle<ia+.1 ) {
792 	if ( angle<ia-.03 || angle>ia+.03 )
793 	    isital = true;
794     }
795     if ( ishor || isvert || isital ) {
796 	isto = false;
797 	if ( &spline->from->me==from || &spline->from->me==to )
798 	    spline->from->selected = true;
799 	if ( &spline->to->me==from || &spline->to->me==to )
800 	    spline->to->selected = isto = true;
801 	if ( from==&spline->from->me || from == &spline->to->me ) {
802 	    base = from; other = to;
803 	} else {
804 	    base = to; other = from;
805 	}
806 	if ( &spline->from->me==from && &spline->to->me==to ) {
807 	    type = 0;	/* Line */
808 	    if ( (ishor && xoff<0) || (isvert && yoff<0)) {
809 		base = from;
810 		other = to;
811 	    } else {
812 		base = to;
813 		other = from;
814 	    }
815 	} else if ( abs(yoff)>abs(xoff) )
816 	    type = ((yoff>0) ^ isto)?1:2;
817 	else
818 	    type = ((xoff>0) ^ isto)?3:4;
819 	if ( ishor )
820 	    ExplainIt(p,p->sc,_(hmsgs[type]), other->y,base->y);
821 	else if ( isvert )
822 	    ExplainIt(p,p->sc,_(vmsgs[type]), other->x,base->x);
823 	else
824 	    ExplainIt(p,p->sc,_(imsgs[type]),0,0);
825 return( true );
826     }
827 return( false );
828 }
829 
830 /* Is the control point outside of the spline segment when projected onto the */
831 /*  vector between the end points of the spline segment? */
OddCPCheck(BasePoint * cp,BasePoint * base,BasePoint * v,SplinePoint * sp,struct problems * p)832 static int OddCPCheck(BasePoint *cp,BasePoint *base,BasePoint *v,
833 	SplinePoint *sp, struct problems *p) {
834     real len = (cp->x-base->x)*v->x+ (cp->y-base->y)*v->y;
835     real xoff, yoff;
836     char *msg=NULL;
837 
838     if ( len<0 || len>1 || (len==0 && &sp->me!=base) || (len==1 && &sp->me==base)) {
839 	xoff = cp->x-sp->me.x; yoff = cp->y-sp->me.y;
840 	if ( fabs(yoff)>fabs(xoff) )
841 	    msg = yoff>0?_("The control point above the selected point is outside the spline segment"):_("The control point below the selected point is outside the spline segment");
842 	else
843 	    msg = xoff>0?_("The control point right of the selected point is outside the spline segment"):_("The control point left of the selected point is outside the spline segment");
844 	sp->selected = true;
845 	ExplainIt(p,p->sc,msg, 0,0);
846 return( true );
847     }
848 return( false );
849 }
850 
Hint3Check(struct problems * p,StemInfo * h)851 static int Hint3Check(struct problems *p,StemInfo *h) {
852     StemInfo *h2, *h3;
853 
854     /* Must be three hints to be interesting */
855     if ( h==NULL || (h2=h->next)==NULL || (h3=h2->next)==NULL )
856 return(false);
857     if ( h3->next!=NULL ) {
858 	StemInfo *bad, *goods[3];
859 	if ( h3->next->next!=NULL )	/* Don't try to find a subset with 5 */
860 return(false);
861 	if ( h->width==h2->width || h->width==h3->width ) {
862 	    goods[0] = h;
863 	    if ( h->width==h2->width ) {
864 		goods[1] = h2;
865 		if ( h->width==h3->width && h->width!=h3->next->width ) {
866 		    goods[2] = h3;
867 		    bad = h3->next;
868 		} else if ( h->width!=h3->width && h->width==h3->next->width ) {
869 		    goods[2] = h3->next;
870 		    bad = h3;
871 		} else
872 return(false);
873 	    } else if ( h->width==h3->width && h->width==h3->next->width ) {
874 		goods[1] = h3;
875 		goods[2] = h3->next;
876 		bad = h2;
877 	    } else
878 return(false);
879 	} else if ( h2->width == h3->width && h2->width==h3->next->width ) {
880 	    bad = h;
881 	    goods[0] = h2; goods[1] = h3; goods[2] = h3->next;
882 	} else
883 return(false);
884 	if ( goods[2]->start-goods[1]->start == goods[1]->start-goods[0]->start ) {
885 	    bad->active = true;
886 	    ExplainIt(p,p->sc,_("This glyph has four hints, but if this one were omitted it would fit a stem3 hint"),0,0);
887 	    if ( !missinghint(p->sc->hstem,bad) || !missinghint(p->sc->vstem,bad))
888 		bad->active = false;
889 	    if ( p->ignorethis )
890 		p->stem3 = false;
891 return( true );
892 	}
893 return(false);
894     }
895 
896     if ( h->width==h2->width && h->width==h3->width &&
897 	    h2->start-h->start == h3->start-h2->start ) {
898 	if ( p->showexactstem3 ) {
899 	    ExplainIt(p,p->sc,_("This glyph can use a stem3 hint"),0,0);
900 	    if ( p->ignorethis )
901 		p->showexactstem3 = false;
902 	}
903 return( false );		/* It IS a stem3, so don't complain */
904     }
905 
906     if ( h->width==h2->width && h->width==h3->width ) {
907 	if ( h2->start-h->start+p->near > h3->start-h2->start &&
908 		h2->start-h->start-p->near < h3->start-h2->start ) {
909 	    ExplainIt(p,p->sc,_("The counters between these hints are not the same size, bad for a stem3 hint"),0,0);
910 	    if ( p->ignorethis )
911 		p->stem3 = false;
912 return( true );
913 	}
914 return( false );
915     }
916 
917     if ( (h2->start-h->start+p->near > h3->start-h2->start &&
918 	     h2->start-h->start-p->near < h3->start-h2->start ) ||
919 	    (h2->start-h->start-h->width+p->near > h3->start-h2->start-h2->width &&
920 	     h2->start-h->start-h->width-p->near < h3->start-h2->start-h2->width )) {
921 	if ( h->width==h2->width ) {
922 	    if ( h->width+p->near > h3->width && h->width-p->near < h3->width ) {
923 		h3->active = true;
924 		ExplainIt(p,p->sc,_("This hint has the wrong width for a stem3 hint"),0,0);
925 		if ( !missinghint(p->sc->hstem,h3) || !missinghint(p->sc->vstem,h3))
926 		    h3->active = false;
927 		if ( p->ignorethis )
928 		    p->stem3 = false;
929 return( true );
930 	    } else
931 return( false );
932 	}
933 	if ( h->width==h3->width ) {
934 	    if ( h->width+p->near > h2->width && h->width-p->near < h2->width ) {
935 		h2->active = true;
936 		ExplainIt(p,p->sc,_("This hint has the wrong width for a stem3 hint"),0,0);
937 		if ( !missinghint(p->sc->hstem,h2) || !missinghint(p->sc->vstem,h2))
938 		    h2->active = false;
939 		if ( p->ignorethis )
940 		    p->stem3 = false;
941 return( true );
942 	    } else
943 return( false );
944 	}
945 	if ( h2->width==h3->width ) {
946 	    if ( h2->width+p->near > h->width && h2->width-p->near < h->width ) {
947 		h->active = true;
948 		ExplainIt(p,p->sc,_("This hint has the wrong width for a stem3 hint"),0,0);
949 		if ( !missinghint(p->sc->hstem,h) || !missinghint(p->sc->vstem,h))
950 		    h->active = false;
951 		if ( p->ignorethis )
952 		    p->stem3 = false;
953 return( true );
954 	    } else
955 return( false );
956 	}
957     }
958 return( false );
959 }
960 
probRefDepth(RefChar * r,int layer)961 static int probRefDepth(RefChar *r,int layer) {
962     RefChar *ref;
963     int cur, max=0;
964 
965     for ( ref= r->sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
966 	cur = probRefDepth(ref,layer);
967 	if ( cur>max ) max = cur;
968     }
969 return( max+1 );
970 }
971 
SCRefDepth(SplineChar * sc,int layer)972 static int SCRefDepth(SplineChar *sc,int layer) {
973     RefChar *ref;
974     int cur, max=0;
975 
976     for ( ref= sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
977 	cur = probRefDepth(ref,layer);
978 	if ( cur>max ) max = cur;
979     }
980 return( max );
981 }
982 
SPLPointCnt(SplinePointList * spl)983 static int SPLPointCnt(SplinePointList *spl) {
984     SplinePoint *sp;
985     int cnt=0;
986 
987     for ( ; spl!=NULL; spl = spl->next ) {
988 	for ( sp = spl->first; ; ) {
989 	    ++cnt;
990 	    if ( sp->prev!=NULL && !sp->prev->knownlinear ) {
991 		if ( sp->prev->order2 )
992 		    ++cnt;
993 		else
994 		    cnt += 2;
995 	    }
996 	    if ( sp->next==NULL )
997 	break;
998 	    sp = sp->next->to;
999 	    if ( sp==spl->first )
1000 	break;
1001 	}
1002     }
1003 return( cnt );
1004 }
1005 
FindRefOfSplineInLayer(Layer * layer,Spline * spline)1006 static RefChar *FindRefOfSplineInLayer(Layer *layer,Spline *spline) {
1007     RefChar *r;
1008     SplineSet *ss;
1009     Spline *s, *first;
1010 
1011     for ( r=layer->refs; r!=NULL; r=r->next ) {
1012 	for ( ss=r->layers[0].splines; ss!=NULL; ss=ss->next ) {
1013 	    first = NULL;
1014 	    for ( s=ss->first->next ; s!=NULL && s!=first; s=s->to->next ) {
1015 		if ( first==NULL ) first = s;
1016 		if ( s==spline )
1017 return( r );
1018 	    }
1019 	}
1020     }
1021 return( NULL );
1022 }
1023 
SCProblems(CharView * cv,SplineChar * sc,struct problems * p)1024 static int SCProblems(CharView *cv,SplineChar *sc,struct problems *p) {
1025     SplineSet *spl, *test;
1026     Spline *spline, *first;
1027     Layer *cur;
1028     SplinePoint *sp, *nsp;
1029     int needsupdate=false, changed=false;
1030     StemInfo *h;
1031     RefChar *r1, *r2;
1032     int uni;
1033     DBounds bb;
1034 
1035   restart:
1036     if ( cv!=NULL ) {
1037 	needsupdate = CVClearSel(cv);
1038 	cur = cv->b.layerheads[cv->b.drawmode];
1039 	spl = cur->splines;
1040 	sc = cv->b.sc;
1041     } else {
1042 	for ( spl = sc->layers[p->layer].splines; spl!=NULL; spl = spl->next ) {
1043 	    if ( spl->first->selected ) { needsupdate = true; spl->first->selected = false; }
1044 	    first = NULL;
1045 	    for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
1046 		if ( spline->to->selected )
1047 		    { needsupdate = true; spline->to->selected = false; }
1048 		if ( first==NULL ) first = spline;
1049 	    }
1050 	}
1051 	cur = &sc->layers[p->layer];
1052 	spl = cur->splines;
1053     }
1054     p->sc = sc;
1055     if (( p->ptnearhint || p->hintwidthnearval || p->hintwithnopt ) &&
1056 	    sc->changedsincelasthinted && !sc->manualhints )
1057 	SplineCharAutoHint(sc,p->layer,NULL);
1058 
1059     if ( p->openpaths ) {
1060 	for ( test=spl; test!=NULL && !p->finish; test=test->next ) {
1061 	    /* I'm also including in "open paths" the special case of a */
1062 	    /*  singleton point with connects to itself */
1063 	    if ( test->first!=NULL && ( test->first->prev==NULL ||
1064 		    ( test->first->prev == test->first->next &&
1065 			test->first->noprevcp && test->first->nonextcp))) {
1066 		changed = true;
1067 		test->first->selected = test->last->selected = true;
1068 		ExplainIt(p,sc,_("The two selected points are the endpoints of an open path"),0,0);
1069 		if ( p->ignorethis ) {
1070 		    p->openpaths = false;
1071 	break;
1072 		}
1073 		if ( missing(p,test,NULL))
1074       goto restart;
1075 	    }
1076 	}
1077     }
1078 
1079     if ( p->intersectingpaths && !p->finish ) {
1080 	Spline *s, *s2;
1081 	int found;
1082 	spl = LayerAllSplines(cur);
1083 	found = SplineSetIntersect(spl,&s,&s2);
1084 	spl = LayerUnAllSplines(cur);
1085 	if ( found ) {
1086 	    changed = true;
1087 	    if ( (r1 = FindRefOfSplineInLayer(cur,s))!=NULL )
1088 		r1->selected = true;
1089 	    else {
1090 		s->from->selected = true; s->to->selected = true;
1091 	    }
1092 	    if ( (r2 = FindRefOfSplineInLayer(cur,s2))!=NULL )
1093 		r2->selected = true;
1094 	    else {
1095 		s2->from->selected = true; s2->to->selected = true;
1096 	    }
1097 	    ExplainIt(p,sc,_("The paths that make up this glyph intersect one another"),0,0);
1098 	    if ( p->ignorethis ) {
1099 		p->intersectingpaths = false;
1100     /* break; */
1101 	    }
1102 	}
1103     }
1104 
1105     if ( p->nonintegral && !p->finish ) {
1106 	for ( test=spl; test!=NULL && !p->finish && p->nonintegral; test=test->next ) {
1107 	    sp = test->first;
1108 	    do {
1109 		int interp = SPInterpolate(sp);
1110 		int badme = interp
1111 			? (rint(2*sp->me.x)!=2*sp->me.x || rint(2*sp->me.y)!=2*sp->me.y)
1112 			: (rint(sp->me.x)!=sp->me.x || rint(sp->me.y)!=sp->me.y);
1113 		if ( badme ||
1114 			rint(sp->nextcp.x)!=sp->nextcp.x || rint(sp->nextcp.y)!=sp->nextcp.y ||
1115 			rint(sp->prevcp.x)!=sp->prevcp.x || rint(sp->prevcp.y)!=sp->prevcp.y ) {
1116 		    changed = true;
1117 		    sp->selected = true;
1118 		    if ( badme )
1119 			ExplainIt(p,sc,_("The selected point is not at integral coordinates"),0,0);
1120 		    else
1121 			ExplainIt(p,sc,_("The selected point does not have integral control points"),0,0);
1122 		    if ( p->ignorethis ) {
1123 			p->nonintegral = false;
1124 	    break;
1125 		    }
1126 		    if ( missing(p,test,nsp))
1127   goto restart;
1128 		}
1129 		if ( sp->next==NULL )
1130 	    break;
1131 		sp = sp->next->to;
1132 	    } while ( sp!=test->first && !p->finish );
1133 	    if ( !p->nonintegral )
1134 	break;
1135 	}
1136     }
1137 
1138     if ( p->pointstoofar && !p->finish ) {
1139 	SplinePoint *lastsp=NULL;
1140 	BasePoint lastpt;
1141 
1142 	memset(&lastpt,0,sizeof(lastpt));
1143 	for ( test=spl; test!=NULL && !p->finish && p->pointstoofar; test=test->next ) {
1144 	    sp = test->first;
1145 	    do {
1146 		if ( BPTooFar(&lastpt,&sp->prevcp) ||
1147 			BPTooFar(&sp->prevcp,&sp->me) ||
1148 			BPTooFar(&sp->me,&sp->nextcp)) {
1149 		    changed = true;
1150 		    sp->selected = true;
1151 		    if ( lastsp==NULL ) {
1152 			ExplainIt(p,sc,_("The selected point is too far from the origin"),0,0);
1153 		    } else {
1154 			lastsp->selected = true;
1155 			ExplainIt(p,sc,_("The selected points (or the intermediate control points) are too far apart"),0,0);
1156 		    }
1157 		    if ( p->ignorethis ) {
1158 			p->pointstoofar = false;
1159 	    break;
1160 		    }
1161 		    if ( missing(p,test,sp))
1162   goto restart;
1163 		}
1164 		memcpy(&lastpt,&sp->nextcp,sizeof(lastpt));
1165 		if ( sp->next==NULL )
1166 	    break;
1167 		sp = sp->next->to;
1168 		if ( sp==test->first ) {
1169 		    memcpy(&lastpt,&sp->me,sizeof(lastpt));
1170 	    break;
1171 		}
1172 	    } while ( !p->finish );
1173 	    if ( !p->pointstoofar )
1174 	break;
1175 	}
1176     }
1177 
1178     if ( p->pointstooclose && !p->finish ) {
1179 	for ( test=spl; test!=NULL && !p->finish && p->pointstooclose; test=test->next ) {
1180 	    sp = test->first;
1181 	    do {
1182 		if ( sp->next==NULL )
1183 	    break;
1184 		nsp = sp->next->to;
1185 		if ( (nsp->me.x-sp->me.x)*(nsp->me.x-sp->me.x) + (nsp->me.y-sp->me.y)*(nsp->me.y-sp->me.y) < 2*2 ) {
1186 		    changed = true;
1187 		    sp->selected = nsp->selected = true;
1188 		    ExplainIt(p,sc,_("The selected points are too close to each other"),0,0);
1189 		    if ( p->ignorethis ) {
1190 			p->pointstooclose = false;
1191 	    break;
1192 		    }
1193 		    if ( missing(p,test,nsp))
1194   goto restart;
1195 		}
1196 		sp = nsp;
1197 	    } while ( sp!=test->first && !p->finish );
1198 	    if ( !p->pointstooclose )
1199 	break;
1200 	}
1201     }
1202 
1203     if ( p->xnearval && !p->finish ) {
1204 	for ( test=spl; test!=NULL && !p->finish && p->xnearval; test=test->next ) {
1205 	    sp = test->first;
1206 	    do {
1207 		if ( sp->me.x-p->xval<p->near && p->xval-sp->me.x<p->near &&
1208 			sp->me.x!=p->xval ) {
1209 		    changed = true;
1210 		    sp->selected = true;
1211 		    ExplainIt(p,sc,_("The x coord of the selected point is near the specified value"),sp->me.x,p->xval);
1212 		    if ( p->ignorethis ) {
1213 			p->xnearval = false;
1214 	    break;
1215 		    }
1216 		    if ( missing(p,test,sp))
1217   goto restart;
1218 		}
1219 		if ( sp->next==NULL )
1220 	    break;
1221 		sp = sp->next->to;
1222 	    } while ( sp!=test->first && !p->finish );
1223 	    if ( !p->xnearval )
1224 	break;
1225 	}
1226     }
1227 
1228     if ( p->ynearval && !p->finish ) {
1229 	for ( test=spl; test!=NULL && !p->finish && p->ynearval; test=test->next ) {
1230 	    sp = test->first;
1231 	    do {
1232 		if ( sp->me.y-p->yval<p->near && p->yval-sp->me.y<p->near &&
1233 			sp->me.y != p->yval ) {
1234 		    changed = true;
1235 		    sp->selected = true;
1236 		    ExplainIt(p,sc,_("The y coord of the selected point is near the specified value"),sp->me.y,p->yval);
1237 		    if ( p->ignorethis ) {
1238 			p->ynearval = false;
1239 	    break;
1240 		    }
1241 		    if ( missing(p,test,sp))
1242   goto restart;
1243 		}
1244 		if ( sp->next==NULL )
1245 	    break;
1246 		sp = sp->next->to;
1247 	    } while ( sp!=test->first && !p->finish );
1248 	    if ( !p->ynearval )
1249 	break;
1250 	}
1251     }
1252 
1253     if ( p->ynearstd && !p->finish ) {
1254 	real expected;
1255 	char *msg;
1256 	for ( test=spl; test!=NULL && !p->finish && p->ynearstd; test=test->next ) {
1257 	    sp = test->first;
1258 	    do {
1259 		if (( sp->me.y-p->xheight<p->near && p->xheight-sp->me.y<p->near && sp->me.y!=p->xheight ) ||
1260 			( sp->me.y-p->caph<p->near && p->caph-sp->me.y<p->near && sp->me.y!=p->caph && sp->me.y!=p->ascent ) ||
1261 			( sp->me.y-p->ascent<p->near && p->ascent-sp->me.y<p->near && sp->me.y!=p->caph && sp->me.y!=p->ascent ) ||
1262 			( sp->me.y-p->descent<p->near && p->descent-sp->me.y<p->near && sp->me.y!=p->descent ) ||
1263 			( sp->me.y<p->near && -sp->me.y<p->near && sp->me.y!=0 ) ) {
1264 		    changed = true;
1265 		    sp->selected = true;
1266 		    if ( sp->me.y<p->near && -sp->me.y<p->near ) {
1267 			msg = _("The y coord of the selected point is near the baseline");
1268 			expected = 0;
1269 		    } else if ( sp->me.y-p->xheight<p->near && p->xheight-sp->me.y<p->near ) {
1270 			msg = _("The y coord of the selected point is near the xheight");
1271 			expected = p->xheight;
1272 		    } else if ( sp->me.y-p->caph<p->near && p->caph-sp->me.y<p->near ) {
1273 			msg = _("The y coord of the selected point is near the cap height");
1274 			expected = p->caph;
1275 		    } else if ( sp->me.y-p->ascent<p->near && p->ascent-sp->me.y<p->near ) {
1276 			msg = _("The y coord of the selected point is near the ascender height");
1277 			expected = p->ascent;
1278 		    } else {
1279 			msg = _("The y coord of the selected point is near the descender height");
1280 			expected = p->descent;
1281 		    }
1282 		    ExplainIt(p,sc,msg,sp->me.y,expected);
1283 		    if ( p->ignorethis ) {
1284 			p->ynearstd = false;
1285 	    break;
1286 		    }
1287 		    if ( missing(p,test,sp))
1288   goto restart;
1289 		}
1290 		if ( sp->next==NULL )
1291 	    break;
1292 		sp = sp->next->to;
1293 	    } while ( sp!=test->first && !p->finish );
1294 	    if ( !p->ynearstd )
1295 	break;
1296 	}
1297     }
1298 
1299     if ( p->linenearstd && !p->finish ) {
1300 	real ia = (90-p->fv->b.sf->italicangle)*(FF_PI/180);
1301 	int hasia = p->fv->b.sf->italicangle!=0;
1302 	for ( test=spl; test!=NULL && !p->finish && p->linenearstd; test = test->next ) {
1303 	    first = NULL;
1304 	    for ( spline = test->first->next; spline!=NULL && spline!=first && !p->finish; spline=spline->to->next ) {
1305 		if ( spline->knownlinear ) {
1306 		    if ( HVITest(p,&spline->to->me,&spline->from->me,spline,
1307 			    hasia, ia)) {
1308 			changed = true;
1309 			if ( p->ignorethis ) {
1310 			    p->linenearstd = false;
1311 	    break;
1312 			}
1313 			if ( missingspline(p,test,spline))
1314   goto restart;
1315 		    }
1316 		}
1317 		if ( first==NULL ) first = spline;
1318 	    }
1319 	    if ( !p->linenearstd )
1320 	break;
1321 	}
1322     }
1323 
1324     if ( p->cpnearstd && !p->finish ) {
1325 	real ia = (90-p->fv->b.sf->italicangle)*(FF_PI/180);
1326 	int hasia = p->fv->b.sf->italicangle!=0;
1327 	for ( test=spl; test!=NULL && !p->finish && p->linenearstd; test = test->next ) {
1328 	    first = NULL;
1329 	    for ( spline = test->first->next; spline!=NULL && spline!=first && !p->finish; spline=spline->to->next ) {
1330 		if ( !spline->knownlinear ) {
1331 		    if ( !spline->from->nonextcp &&
1332 			    HVITest(p,&spline->from->nextcp,&spline->from->me,spline,
1333 				hasia, ia)) {
1334 			changed = true;
1335 			if ( p->ignorethis ) {
1336 			    p->cpnearstd = false;
1337 	    break;
1338 			}
1339 			if ( missingspline(p,test,spline))
1340   goto restart;
1341 		    }
1342 		    if ( !spline->to->noprevcp &&
1343 			    HVITest(p,&spline->to->me,&spline->to->prevcp,spline,
1344 				hasia, ia)) {
1345 			changed = true;
1346 			if ( p->ignorethis ) {
1347 			    p->cpnearstd = false;
1348 	    break;
1349 			}
1350 			if ( missingspline(p,test,spline))
1351   goto restart;
1352 		    }
1353 		}
1354 		if ( first==NULL ) first = spline;
1355 	    }
1356 	    if ( !p->cpnearstd )
1357 	break;
1358 	}
1359     }
1360 
1361     if ( p->cpodd && !p->finish ) {
1362 	for ( test=spl; test!=NULL && !p->finish && p->linenearstd; test = test->next ) {
1363 	    first = NULL;
1364 	    for ( spline = test->first->next; spline!=NULL && spline!=first && !p->finish; spline=spline->to->next ) {
1365 		if ( !spline->knownlinear ) {
1366 		    BasePoint v; real len;
1367 		    v.x = spline->to->me.x-spline->from->me.x;
1368 		    v.y = spline->to->me.y-spline->from->me.y;
1369 		    len = /*sqrt*/(v.x*v.x+v.y*v.y);
1370 		    v.x /= len; v.y /= len;
1371 		    if ( !spline->from->nonextcp &&
1372 			    OddCPCheck(&spline->from->nextcp,&spline->from->me,&v,
1373 			     spline->from,p)) {
1374 			changed = true;
1375 			if ( p->ignorethis ) {
1376 			    p->cpodd = false;
1377 	    break;
1378 			}
1379 			if ( missingspline(p,test,spline))
1380   goto restart;
1381 		    }
1382 		    if ( !spline->to->noprevcp &&
1383 			    OddCPCheck(&spline->to->prevcp,&spline->from->me,&v,
1384 			     spline->to,p)) {
1385 			changed = true;
1386 			if ( p->ignorethis ) {
1387 			    p->cpodd = false;
1388 	    break;
1389 			}
1390 			if ( missingspline(p,test,spline))
1391   goto restart;
1392 		    }
1393 		}
1394 		if ( first==NULL ) first = spline;
1395 	    }
1396 	    if ( !p->cpodd )
1397 	break;
1398 	}
1399     }
1400 
1401     if ( p->irrelevantcontrolpoints && !p->finish ) {
1402 	for ( test=spl; test!=NULL && !p->finish && p->irrelevantcontrolpoints; test = test->next ) {
1403 	    for ( sp=test->first; !p->finish && p->irrelevantcontrolpoints; ) {
1404 		int either = false;
1405 		if ( sp->prev!=NULL ) {
1406 		    double len = sqrt((sp->me.x-sp->prev->from->me.x)*(sp->me.x-sp->prev->from->me.x) +
1407 			    (sp->me.y-sp->prev->from->me.y)*(sp->me.y-sp->prev->from->me.y));
1408 		    double cplen = sqrt((sp->me.x-sp->prevcp.x)*(sp->me.x-sp->prevcp.x) +
1409 			    (sp->me.y-sp->prevcp.y)*(sp->me.y-sp->prevcp.y));
1410 		    if ( cplen!=0 && cplen<p->irrelevantfactor*len )
1411 			either = true;
1412 		}
1413 		if ( sp->next!=NULL ) {
1414 		    double len = sqrt((sp->me.x-sp->next->to->me.x)*(sp->me.x-sp->next->to->me.x) +
1415 			    (sp->me.y-sp->next->to->me.y)*(sp->me.y-sp->next->to->me.y));
1416 		    double cplen = sqrt((sp->me.x-sp->nextcp.x)*(sp->me.x-sp->nextcp.x) +
1417 			    (sp->me.y-sp->nextcp.y)*(sp->me.y-sp->nextcp.y));
1418 		    if ( cplen!=0 && cplen<p->irrelevantfactor*len )
1419 			either = true;
1420 		}
1421 		if ( either ) {
1422 		    sp->selected = true;
1423 		    ExplainIt(p,sc,_("This glyph contains control points which are probably too close to the main points to alter the look of the spline"),0,0);
1424 		    if ( p->ignorethis ) {
1425 			p->irrelevantcontrolpoints = false;
1426 	    break;
1427 		    }
1428 		}
1429 		if ( sp->next==NULL )
1430 	    break;
1431 		sp = sp->next->to;
1432 		if ( sp==test->first )
1433 	    break;
1434 	    }
1435 	}
1436     }
1437 
1438     if ( p->hintwithnopt && !p->finish ) {
1439 	int anys, anye;
1440       restarthhint:
1441 	for ( h=sc->hstem; h!=NULL ; h=h->next ) {
1442 	    anys = anye = false;
1443 	    for ( test=spl; test!=NULL && !p->finish && (!anys || !anye); test=test->next ) {
1444 		sp = test->first;
1445 		do {
1446 		    if (sp->me.y==h->start )
1447 			anys = true;
1448 		    if (sp->me.y==h->start+h->width )
1449 			anye = true;
1450 		    if ( sp->next==NULL )
1451 		break;
1452 		    sp = sp->next->to;
1453 		} while ( sp!=test->first && !p->finish );
1454 	    }
1455 	    if ( h->ghost && ( anys || anye ))
1456 		/* Ghost hints only define one edge */;
1457 	    else if ( !anys || !anye ) {
1458 		h->active = true;
1459 		changed = true;
1460 		ExplainIt(p,sc,_("This hint does not control any points"),0,0);
1461 		if ( !missinghint(sc->hstem,h))
1462 		    h->active = false;
1463 		if ( p->ignorethis ) {
1464 		    p->hintwithnopt = false;
1465 	break;
1466 		}
1467 		if ( missinghint(sc->hstem,h))
1468       goto restarthhint;
1469 	    }
1470 	}
1471       restartvhint:
1472 	for ( h=sc->vstem; h!=NULL && p->hintwithnopt && !p->finish; h=h->next ) {
1473 	    anys = anye = false;
1474 	    for ( test=spl; test!=NULL && !p->finish && (!anys || !anye); test=test->next ) {
1475 		sp = test->first;
1476 		do {
1477 		    if (sp->me.x==h->start )
1478 			anys = true;
1479 		    if (sp->me.x==h->start+h->width )
1480 			anye = true;
1481 		    if ( sp->next==NULL )
1482 		break;
1483 		    sp = sp->next->to;
1484 		} while ( sp!=test->first && !p->finish );
1485 	    }
1486 	    if ( !anys || !anye ) {
1487 		h->active = true;
1488 		changed = true;
1489 		ExplainIt(p,sc,_("This hint does not control any points"),0,0);
1490 		if ( p->ignorethis ) {
1491 		    p->hintwithnopt = false;
1492 	break;
1493 		}
1494 		if ( missinghint(sc->vstem,h))
1495       goto restartvhint;
1496 		h->active = false;
1497 	    }
1498 	}
1499     }
1500 
1501     if ( p->ptnearhint && !p->finish ) {
1502 	real found, expected;
1503 	h = NULL;
1504 	for ( test=spl; test!=NULL && !p->finish && p->ptnearhint; test=test->next ) {
1505 	    sp = test->first;
1506 	    do {
1507 		int hs = false, vs = false;
1508 		for ( h=sc->hstem; h!=NULL; h=h->next ) {
1509 		    if (( sp->me.y-h->start<p->near && h->start-sp->me.y<p->near &&
1510 				sp->me.y!=h->start ) ||
1511 			    ( sp->me.y-h->start+h->width<p->near && h->start+h->width-sp->me.y<p->near &&
1512 				sp->me.y!=h->start+h->width )) {
1513 			found = sp->me.y;
1514 			if ( sp->me.y-h->start<p->near && h->start-sp->me.y<p->near )
1515 			    expected = h->start;
1516 			else
1517 			    expected = h->start+h->width;
1518 			h->active = true;
1519 			hs = true;
1520 		break;
1521 		    }
1522 		}
1523 		if ( !hs ) {
1524 		    for ( h=sc->vstem; h!=NULL; h=h->next ) {
1525 			if (( sp->me.x-h->start<p->near && h->start-sp->me.x<p->near &&
1526 				    sp->me.x!=h->start ) ||
1527 				( sp->me.x-h->start+h->width<p->near && h->start+h->width-sp->me.x<p->near &&
1528 				    sp->me.x!=h->start+h->width )) {
1529 			    found = sp->me.x;
1530 			    if ( sp->me.x-h->start<p->near && h->start-sp->me.x<p->near )
1531 				expected = h->start;
1532 			    else
1533 				expected = h->start+h->width;
1534 			    h->active = true;
1535 			    vs = true;
1536 		    break;
1537 			}
1538 		    }
1539 		}
1540 		if ( hs || vs ) {
1541 		    changed = true;
1542 		    sp->selected = true;
1543 		    ExplainIt(p,sc,hs?_("The selected point is near a horizontal stem hint"):_("The selected point is near a vertical stem hint"),found,expected);
1544 		    if ( h!=NULL )
1545 			h->active = false;
1546 		    if ( p->ignorethis ) {
1547 			p->ptnearhint = false;
1548 	    break;
1549 		    }
1550 		    if ( missing(p,test,sp))
1551   goto restart;
1552 		}
1553 		if ( sp->next==NULL )
1554 	    break;
1555 		sp = sp->next->to;
1556 	    } while ( sp!=test->first && !p->finish );
1557 	    if ( !p->ptnearhint )
1558 	break;
1559 	}
1560     }
1561 
1562     if ( p->overlappedhints && !p->finish && !cur->order2 && spl!=NULL ) {
1563 	int anyhm=0;
1564 	for ( test=spl; test!=NULL && !p->finish && p->overlappedhints; test=test->next ) {
1565 	    sp = test->first;
1566 	    do {
1567 		if ( sp->hintmask!=NULL ) {
1568 		    anyhm = true;
1569 		    h = SCHintOverlapInMask(sc,sp->hintmask);
1570 		    if ( h!=NULL ) {
1571 			sp->selected = true;
1572 			h->active = true;
1573 			changed = true;
1574 			ExplainIt(p,sc,_("The hint mask of the selected point contains overlapping hints"),0,0);
1575 			if ( p->ignorethis )
1576 			    p->overlappedhints = false;
1577 			if ( missing(p,test,sp))
1578   goto restart;
1579 			if ( missingschint(h,sc))
1580   goto restart;
1581 			h->active = false;
1582 			sp->selected = false;
1583 			if ( !p->overlappedhints )
1584 	    break;
1585 		    }
1586 		}
1587 		if ( sp->next==NULL )
1588 	    break;
1589 		sp = sp->next->to;
1590 	    } while ( sp!=test->first && !p->finish );
1591 	    if ( !p->overlappedhints )
1592 	break;
1593 	}
1594 	if ( p->overlappedhints && !anyhm ) {
1595 	    h = SCHintOverlapInMask(sc,NULL);
1596 	    if ( h!=NULL ) {
1597 		h->active = true;
1598 		changed = true;
1599 		ExplainIt(p,sc,_("There are no hint masks in this layer but there are overlapping hints."),0,0);
1600 		if ( missingschint(h,sc))
1601   goto restart;
1602 		h->active = false;
1603 	    }
1604 	}
1605     }
1606 
1607     if ( p->hintwidthnearval && !p->finish ) {
1608 	StemInfo *hs = NULL, *vs = NULL;
1609 	for ( h=sc->hstem; h!=NULL; h=h->next ) {
1610 	    if ( h->width-p->widthval<p->near && p->widthval-h->width<p->near &&
1611 		    h->width!=p->widthval ) {
1612 		h->active = true;
1613 		hs = h;
1614 	break;
1615 	    }
1616 	}
1617 	for ( h=sc->vstem; h!=NULL; h=h->next ) {
1618 	    if ( h->width-p->widthval<p->near && p->widthval-h->width<p->near &&
1619 		    h->width!=p->widthval ) {
1620 		h->active = true;
1621 		vs = h;
1622 	break;
1623 	    }
1624 	}
1625 	if ( hs || vs ) {
1626 	    changed = true;
1627 	    ExplainIt(p,sc,hs?_("This glyph contains a horizontal hint near the specified width"):_("This glyph contains a vertical hint near the specified width"),
1628 		    hs?hs->width:vs->width,p->widthval);
1629 	    if ( hs!=NULL && !missinghint(sc->hstem,hs)) hs->active = false;
1630 	    if ( vs!=NULL && !missinghint(sc->vstem,vs)) vs->active = false;
1631 	    if ( p->ignorethis )
1632 		p->hintwidthnearval = false;
1633 	    else if ( (hs!=NULL && missinghint(sc->hstem,hs)) &&
1634 		    ( vs!=NULL && missinghint(sc->vstem,vs)))
1635       goto restart;
1636 	}
1637     }
1638 
1639     if ( p->stem3 && !p->finish )
1640 	changed |= Hint3Check(p,sc->hstem);
1641     if ( p->stem3 && !p->finish )
1642 	changed |= Hint3Check(p,sc->vstem);
1643 
1644     if ( p->direction && !p->finish ) {
1645 	SplineSet **base, *ret, *ss;
1646 	Spline *s, *s2;
1647 	Layer *layer;
1648 	int lastscan= -1;
1649 	int self_intersects, dir;
1650 
1651 	if ( cv!=NULL )
1652 	    layer = cv->b.layerheads[cv->b.drawmode];
1653 	else
1654 	    layer = &sc->layers[p->layer];
1655 
1656 	ss = LayerAllSplines(layer);
1657 	self_intersects = SplineSetIntersect(ss,&s,&s2);
1658 	LayerUnAllSplines(layer);
1659 
1660 	if ( self_intersects )
1661 	    ff_post_error(_("This glyph self-intersects"),_("This glyph self-intersects. Checking for correct direction is meaningless until that is fixed"));
1662 	else {
1663 	    base = &layer->splines;
1664 
1665 	    while ( !p->finish && (ret=SplineSetsDetectDir(base,&lastscan))!=NULL ) {
1666 		sp = ret->first;
1667 		changed = true;
1668 		while ( 1 ) {
1669 		    sp->selected = true;
1670 		    if ( sp->next==NULL )
1671 		break;
1672 		    sp = sp->next->to;
1673 		    if ( sp==ret->first )
1674 		break;
1675 		}
1676 		dir = SplinePointListIsClockwise(ret);
1677 		if ( dir==-1 )
1678 		    ExplainIt(p,sc,_("This path probably intersects itself (though I could not find that when\n I checked for intersections), look closely at the corners"),0,0);
1679 		else if ( dir==1 )
1680 		    ExplainIt(p,sc,_("This path should have been drawn in a counter-clockwise direction"),0,0);
1681 		else
1682 		    ExplainIt(p,sc,_("This path should have been drawn in a clockwise direction"),0,0);
1683 		if ( p->ignorethis ) {
1684 		    p->direction = false;
1685 	    break;
1686 		}
1687 	    }
1688 	}
1689     }
1690 
1691     if ( p->missingextrema && !p->finish ) {
1692 	SplineSet *ss;
1693 	Spline *s, *first;
1694 	double len2, bound2 = p->sc->parent->extrema_bound;
1695 	double x,y;
1696 	extended extrema[4];
1697 
1698 	if ( bound2<=0 )
1699 	    bound2 = (p->sc->parent->ascent + p->sc->parent->descent)/32.0;
1700 
1701 	bound2 *= bound2;
1702       ae_restart:
1703 	for ( ss = sc->layers[p->layer].splines; ss!=NULL && !p->finish; ss=ss->next ) {
1704 	  ae2_restart:
1705 	    first = NULL;
1706 	    for ( s=ss->first->next ; s!=NULL && s!=first && !p->finish; s=s->to->next ) {
1707 		if ( first==NULL )
1708 		    first = s;
1709 		if ( s->acceptableextrema )
1710 	    continue;		/* If marked as good, don't check it */
1711 		/* rough appoximation to spline's length */
1712 		x = (s->to->me.x-s->from->me.x);
1713 		y = (s->to->me.y-s->from->me.y);
1714 		len2 = x*x + y*y;
1715 		/* short splines (serifs) need not to have points at their extrema */
1716 		/*  But how do we define "short"? */
1717 		if ( len2>bound2 && Spline2DFindExtrema(s,extrema)>0 ) {
1718 		    s->from->selected = true;
1719 		    s->to->selected = true;
1720 		    ExplainIt(p,sc,_("The selected spline attains its extrema somewhere other than its endpoints"),0,0);
1721 		    if ( !SSExistsInLayer(ss,sc->layers[p->layer].splines) )
1722       goto ae_restart;
1723 		    if ( !SplineExistsInSS(s,ss))
1724 	  goto ae2_restart;
1725 		    if ( !SplineExistsInSS(first,ss))
1726 			first = s;
1727 		    if ( p->ignorethis ) {
1728 			p->missingextrema = false;
1729 	    break;
1730 		    }
1731 		}
1732 	    }
1733 	    if ( !p->missingextrema )
1734 	break;
1735 	}
1736     }
1737 
1738     if ( p->flippedrefs && !p->finish && ( cv==NULL || cv->b.drawmode==dm_fore )) {
1739 	RefChar *ref;
1740 	for ( ref = sc->layers[p->layer].refs; ref!=NULL ; ref = ref->next )
1741 	    ref->selected = false;
1742 	for ( ref = sc->layers[p->layer].refs; !p->finish && ref!=NULL ; ref = ref->next ) {
1743 	    if ( ref->transform[0]*ref->transform[3]<0 ||
1744 		    (ref->transform[0]==0 && ref->transform[1]*ref->transform[2]>0)) {
1745 		changed = true;
1746 		ref->selected = true;
1747 		ExplainIt(p,sc,_("This reference has been flipped, so the paths in it are drawn backwards"),0,0);
1748 		ref->selected = false;
1749 		if ( p->ignorethis ) {
1750 		    p->flippedrefs = false;
1751 	break;
1752 		}
1753 	    }
1754 	}
1755     }
1756 
1757     if ( p->refsbadtransformttf && !p->finish ) {
1758 	RefChar *ref;
1759 	for ( ref = sc->layers[p->layer].refs; ref!=NULL ; ref = ref->next )
1760 	    ref->selected = false;
1761 	for ( ref = sc->layers[p->layer].refs; !p->finish && ref!=NULL ; ref = ref->next ) {
1762 	    if ( ref->transform[0]>=2 || ref->transform[0]<-2 ||
1763 		    ref->transform[1]>=2 || ref->transform[1]<-2 ||
1764 		    ref->transform[2]>=2 || ref->transform[2]<-2 ||
1765 		    ref->transform[3]>=2 || ref->transform[3]<-2 ||
1766 		    rint(ref->transform[4])!=ref->transform[4] ||
1767 		    rint(ref->transform[5])!=ref->transform[5]) {
1768 		changed = true;
1769 		ref->selected = true;
1770 		ExplainIt(p,sc,_("This reference has a transformation matrix which cannot be expressed in truetype.\nAll entries (except translation) must be between [-2.0,2.0).\nTranslation must be integral."),0,0);
1771 		ref->selected = false;
1772 		if ( p->ignorethis ) {
1773 		    p->refsbadtransformttf = false;
1774 	break;
1775 		}
1776 	    }
1777 	}
1778     }
1779 
1780     if ( p->mixedcontoursrefs && !p->finish ) {
1781 	RefChar *ref;
1782 	int hasref=0, hascontour = sc->layers[p->layer].splines!=NULL;
1783 	for ( ref = sc->layers[p->layer].refs; ref!=NULL ; ref = ref->next ) {
1784 	    ref->selected = false;
1785 	    if ( ref->transform[0]>=2 || ref->transform[0]<-2 ||
1786 		    ref->transform[1]>=2 || ref->transform[1]<-2 ||
1787 		    ref->transform[2]>=2 || ref->transform[2]<-2 ||
1788 		    ref->transform[3]>=2 || ref->transform[3]<-2 )
1789 		hascontour = true;
1790 	    else
1791 		hasref = true;
1792 	    if ( hascontour && hasref ) {
1793 		changed = true;
1794 		ref->selected = true;
1795 		ExplainIt(p,sc,_("This glyph contains both contours and references.\n(or contains a reference which has a bad transformation matrix and counts as a contour).\nThis cannot be expressed in the TrueType glyph format."),0,0);
1796 		ref->selected = false;
1797 		if ( p->ignorethis ) {
1798 		    p->mixedcontoursrefs = false;
1799 	break;
1800 		}
1801 	    }
1802 	}
1803     }
1804 
1805     if ( p->refsbadtransformps && !p->finish ) {
1806 	RefChar *ref;
1807 	for ( ref = sc->layers[p->layer].refs; ref!=NULL ; ref = ref->next )
1808 	    ref->selected = false;
1809 	for ( ref = sc->layers[p->layer].refs; !p->finish && ref!=NULL ; ref = ref->next ) {
1810 	    if ( ref->transform[0]!=1.0 ||
1811 		    ref->transform[1]!=0 ||
1812 		    ref->transform[2]!=0 ||
1813 		    ref->transform[3]!= 1.0 ) {
1814 		changed = true;
1815 		ref->selected = true;
1816 		ExplainIt(p,sc,_("This reference has a transformation matrix which cannot be expressed in Type1/2 fonts.\nNo scaling or rotation allowed."),0,0);
1817 		ref->selected = false;
1818 		if ( p->ignorethis ) {
1819 		    p->refsbadtransformps = false;
1820 	break;
1821 		}
1822 	    }
1823 	}
1824     }
1825 
1826     if ( p->multusemymetrics && !p->finish ) {
1827 	RefChar *ref, *found;
1828 	for ( ref = sc->layers[p->layer].refs; ref!=NULL ; ref = ref->next )
1829 	    ref->selected = false;
1830 	found = NULL;
1831 	for ( ref = sc->layers[p->layer].refs; !p->finish && ref!=NULL ; ref = ref->next ) {
1832 	    if ( ref->use_my_metrics ) {
1833 		if ( found==NULL )
1834 		    found = ref;
1835 		else {
1836 		    changed = true;
1837 		    ref->selected = true;
1838 		    found->selected = true;
1839 		    ExplainIt(p,sc,_("Both selected references have use-my-metrics set"),0,0);
1840 		    ref->selected = false;
1841 		    found->selected = false;
1842 		    if ( p->ignorethis ) {
1843 			p->multusemymetrics = false;
1844 	break;
1845 		    }
1846 		}
1847 	    }
1848 	}
1849     }
1850 
1851     if ( p->ptmatchrefsoutofdate && !p->finish ) {
1852 	RefChar *ref;
1853 	for ( ref = sc->layers[p->layer].refs; ref!=NULL ; ref = ref->next )
1854 	    ref->selected = false;
1855 	for ( ref = sc->layers[p->layer].refs; !p->finish && ref!=NULL ; ref = ref->next ) {
1856 	    if ( ref->point_match_out_of_date ) {
1857 		changed = true;
1858 		ref->selected = true;
1859 		ExplainIt(p,sc,_("This reference uses point-matching but it refers to a glyph\n(or a previous reference refers to a glyph)\nwhose points have been renumbered."),0,0);
1860 		ref->selected = false;
1861 		if ( p->ignorethis ) {
1862 		    p->ptmatchrefsoutofdate = false;
1863 	break;
1864 		}
1865 	    }
1866 	}
1867     }
1868 
1869     if ( p->toodeeprefs && !p->finish ) {
1870 	int cnt=SCRefDepth(sc,p->layer);
1871 	if ( cnt>p->refdepthmax ) {
1872 	    changed = true;
1873 	    ExplainIt(p,sc,_("References are nested more deeply in this glyph than the maximum allowed"),cnt,p->refdepthmax);
1874 	    if ( p->ignorethis )
1875 		p->toodeeprefs = false;
1876 	}
1877     }
1878 
1879     if ( p->toomanypoints && !p->finish ) {
1880 	int cnt=0;
1881 	RefChar *r;
1882 	cnt = SPLPointCnt(sc->layers[p->layer].splines);
1883 	for ( r=sc->layers[p->layer].refs; r!=NULL ; r=r->next )
1884 	    cnt += SPLPointCnt(r->layers[0].splines);
1885 	if ( cnt>p->pointsmax ) {
1886 	    changed = true;
1887 	    ExplainIt(p,sc,_("There are more points in this glyph than the maximum allowed"),cnt,p->pointsmax);
1888 	    if ( p->ignorethis )
1889 		p->toomanypoints = false;
1890 	}
1891     }
1892 
1893     if ( p->toomanyhints && !p->finish ) {
1894 	int cnt=0;
1895 	for ( h=sc->hstem; h!=NULL; h=h->next )
1896 	    ++cnt;
1897 	for ( h=sc->vstem; h!=NULL; h=h->next )
1898 	    ++cnt;
1899 	if ( cnt>p->hintsmax ) {
1900 	    changed = true;
1901 	    ExplainIt(p,sc,_("There are more hints in this glyph than the maximum allowed"),cnt,p->hintsmax);
1902 	    if ( p->ignorethis )
1903 		p->toomanyhints = false;
1904 	}
1905     }
1906 
1907     if ( p->bitmaps && !p->finish && SCWorthOutputting(sc)) {
1908 	BDFFont *bdf;
1909 
1910 	for ( bdf=sc->parent->bitmaps; bdf!=NULL; bdf=bdf->next ) {
1911 	    if ( sc->orig_pos>=bdf->glyphcnt || bdf->glyphs[sc->orig_pos]==NULL ) {
1912 		changed = true;
1913 		ExplainIt(p,sc,_("This outline glyph is missing a bitmap version"),0,0);
1914 		if ( p->ignorethis )
1915 		    p->bitmaps = false;
1916 	break;
1917 	    }
1918 	}
1919     }
1920 
1921     if ( p->bitmapwidths && !p->finish && SCWorthOutputting(sc)) {
1922 	BDFFont *bdf;
1923 	double em = (sc->parent->ascent+sc->parent->descent);
1924 
1925 	for ( bdf=sc->parent->bitmaps; bdf!=NULL; bdf=bdf->next ) {
1926 	    if ( sc->orig_pos<bdf->glyphcnt && bdf->glyphs[sc->orig_pos]!=NULL ) {
1927 		BDFChar *bc = bdf->glyphs[sc->orig_pos];
1928 		if ( bc->width!= (int) rint( (sc->width*bdf->pixelsize)/em ) ) {
1929 		    changed = true;
1930 		    ExplainIt(p,sc,_("This outline glyph's advance width is different from that of the bitmap's"),
1931 			    bc->width,rint( (sc->width*bdf->pixelsize)/em ));
1932 		    if ( p->ignorethis )
1933 			p->bitmapwidths = false;
1934 	break;
1935 		}
1936 	    }
1937 	}
1938     }
1939 
1940     if ( p->advancewidth && !p->finish && SCWorthOutputting(sc)) {
1941 	if ( sc->width!=p->advancewidthval ) {
1942 	    changed = true;
1943 	    ExplainIt(p,sc,_("This glyph's advance width is different from the standard width"),sc->width,p->advancewidthval);
1944 	    if ( p->ignorethis )
1945 		p->advancewidth = false;
1946 	}
1947     }
1948 
1949     if ( p->vadvancewidth && !p->finish && SCWorthOutputting(sc)) {
1950 	if ( sc->vwidth!=p->vadvancewidthval ) {
1951 	    changed = true;
1952 	    ExplainIt(p,sc,_("This glyph's vertical advance is different from the standard width"),sc->vwidth,p->vadvancewidthval);
1953 	    if ( p->ignorethis )
1954 		p->vadvancewidth = false;
1955 	}
1956     }
1957 
1958     if ( (p->bbymax || p->bbxmax || p->bbymin || p->bbxmin) && !p->finish &&
1959 	    SCWorthOutputting(sc)) {
1960 	SplineCharFindBounds(sc,&bb);
1961 	if ( p->bbymax && bb.maxy > p->bbymax_val ) {
1962 	    changed = true;
1963 	    ExplainIt(p,sc,_("This glyph is taller than desired"),bb.maxy,p->bbymax_val);
1964 	    if ( p->ignorethis )
1965 		p->bbymax = false;
1966 	}
1967 	if ( p->bbymin && bb.miny < p->bbymin_val ) {
1968 	    changed = true;
1969 	    ExplainIt(p,sc,_("This glyph extends further below the baseline than desired"),bb.miny,p->bbymin_val);
1970 	    if ( p->ignorethis )
1971 		p->bbymin = false;
1972 	}
1973 	if ( p->bbxmax && bb.maxx > p->bbxmax_val ) {
1974 	    changed = true;
1975 	    ExplainIt(p,sc,_("This glyph is wider than desired"),bb.maxx,p->bbxmax_val);
1976 	    if ( p->ignorethis )
1977 		p->bbxmax = false;
1978 	}
1979 	if ( p->bbxmin && bb.minx < p->bbxmin_val ) {
1980 	    changed = true;
1981 	    ExplainIt(p,sc,_("This glyph extends left further than desired"),bb.minx,p->bbxmin_val);
1982 	    if ( p->ignorethis )
1983 		p->bbxmin = false;
1984 	}
1985     }
1986 
1987     if ( p->badsubs && !p->finish ) {
1988 	PST *pst;
1989 	char *pt, *end; int ch;
1990 	for ( pst = sc->possub ; pst!=NULL; pst=pst->next ) {
1991 	    if ( pst->type==pst_substitution || pst->type==pst_alternate ||
1992 		    pst->type==pst_multiple || pst->type==pst_ligature ) {
1993 		for ( pt=pst->u.subs.variant; *pt!='\0' ; pt=end ) {
1994 		    end = strchr(pt,' ');
1995 		    if ( end==NULL ) end=pt+strlen(pt);
1996 		    ch = *end;
1997 		    *end = '\0';
1998 		    if ( !SCWorthOutputting(SFGetChar(sc->parent,-1,pt)) ) {
1999 			changed = true;
2000 			p->badsubsname = copy(pt);
2001 			*end = ch;
2002 			p->badsubs_lsubtable = pst->subtable;
2003 			ExplainIt(p,sc,_("This glyph contains a substitution or ligature entry which refers to an empty char"),0,0);
2004 			free(p->badsubsname);
2005 			if ( p->ignorethis )
2006 			    p->badsubs = false;
2007 		    } else
2008 			*end = ch;
2009 		    while ( *end==' ' ) ++end;
2010 		    if ( !p->badsubs )
2011 		break;
2012 		}
2013 		if ( !p->badsubs )
2014 	    break;
2015 	    }
2016 	}
2017     }
2018 
2019     if ( p->missinganchor && !p->finish ) {
2020 	for (;;) {
2021 	    p->missinganchor_class = SCValidateAnchors(sc);
2022 	    if ( p->missinganchor_class == NULL )
2023 	break;
2024 	    ExplainIt(p,sc,_("This glyph contains anchor points from some, but not all anchor classes in a subtable"),0,0);
2025 	    if ( p->ignorethis )
2026 		p->missinganchor = false;
2027 	break;
2028 	}
2029     }
2030 
2031     if ( p->multuni && !p->finish && sc->unicodeenc!=-1 ) {
2032 	SplineFont *sf = sc->parent;
2033 	int i;
2034 	for ( i=0; i<sf->glyphcnt; ++i )
2035 		if ( sf->glyphs[i]!=NULL && sf->glyphs[i]!=sc ) {
2036 	    if ( sf->glyphs[i]->unicodeenc == sc->unicodeenc ) {
2037 		changed = true;
2038 		p->glyphname = sf->glyphs[i]->name;
2039 		ExplainIt(p,sc,_("Two glyphs share the same unicode code point.\nChange the encoding to \"Glyph Order\" and use\nEdit->Select->Wildcard with the following code point"),0,0);
2040 		if ( p->ignorethis )
2041 		    p->multuni = false;
2042 	    }
2043 	}
2044     }
2045 
2046     if ( p->multname && !p->finish ) {
2047 	SplineFont *sf = sc->parent;
2048 	int i;
2049 	for ( i=0; i<sf->glyphcnt; ++i )
2050 		if ( sf->glyphs[i]!=NULL && sf->glyphs[i]!=sc ) {
2051 	    if ( strcmp(sf->glyphs[i]->name, sc->name)==0 ) {
2052 		changed = true;
2053 		p->glyphenc = i;
2054 		ExplainIt(p,sc,_("Two glyphs have the same name.\nChange the encoding to \"Glyph Order\" and use\nEdit->Select->Wildcard with the following name"),0,0);
2055 		if ( p->ignorethis )
2056 		    p->multname = false;
2057 	    }
2058 	}
2059     }
2060 
2061     if ( p->uninamemismatch && !p->finish &&
2062 		strcmp(sc->name,".notdef")!=0 &&
2063 		strcmp(sc->name,".null")!=0 &&
2064 		strcmp(sc->name,"nonmarkingreturn")!=0 &&
2065 		(uni = UniFromName(sc->name,sc->parent->uni_interp,p->fv->b.map->enc))!= -1 &&
2066 		sc->unicodeenc != uni ) {
2067 	changed = true;
2068 	p->glyphenc = sc->orig_pos;
2069 	if ( sc->unicodeenc==-1 )
2070 	    ExplainIt(p,sc,_("This glyph is not mapped to any unicode code point, but its name should be."),0,0);
2071 	else if ( strcmp(sc->name,"alefmaksurainitialarabic")==0 ||
2072                   strcmp(sc->name,"alefmaksuramedialarabic")==0 )
2073 	    ExplainIt(p,sc,_("The use of names 'alefmaksurainitialarabic' and 'alefmaksuramedialarabic' is discouraged."),0,0);
2074 	else
2075 	    ExplainIt(p,sc,_("This glyph is mapped to a unicode code point which is different from its name."),0,0);
2076 	if ( p->ignorethis )
2077 	    p->uninamemismatch = false;
2078     }
2079 
2080 
2081     if ( needsupdate || changed )
2082 	SCUpdateAll(sc);
2083 return( changed );
2084 }
2085 
CIDCheck(struct problems * p,int cid)2086 static int CIDCheck(struct problems *p,int cid) {
2087     int found = false;
2088 
2089     if ( (p->cidmultiple || p->cidblank) && !p->finish ) {
2090 	SplineFont *csf = p->fv->b.cidmaster;
2091 	int i, cnt;
2092 	for ( i=cnt=0; i<csf->subfontcnt; ++i )
2093 	    if ( cid<csf->subfonts[i]->glyphcnt &&
2094 		    SCWorthOutputting(csf->subfonts[i]->glyphs[cid]) )
2095 		++cnt;
2096 	if ( cnt>1 && p->cidmultiple ) {
2097 	    _ExplainIt(p,cid,_("This glyph is defined in more than one of the CID subfonts"),cnt,1);
2098 	    if ( p->ignorethis )
2099 		p->cidmultiple = false;
2100 	    found = true;
2101 	} else if ( cnt==0 && p->cidblank ) {
2102 	    _ExplainIt(p,cid,_("This glyph is not defined in any of the CID subfonts"),0,0);
2103 	    if ( p->ignorethis )
2104 		p->cidblank = false;
2105 	    found = true;
2106 	}
2107     }
2108 return( found );
2109 }
2110 
missinglookup(struct problems * p,char * str)2111 static char *missinglookup(struct problems *p,char *str) {
2112     int i;
2113 
2114     for ( i=0; i<p->rpl_cnt; ++i )
2115 	if ( strcmp(str,p->mg[i].search)==0 )
2116 return( p->mg[i].rpl );
2117 
2118 return( NULL );
2119 }
2120 
mgreplace(char ** base,char * str,char * end,char * new,SplineChar * sc,PST * pst)2121 static void mgreplace(char **base, char *str,char *end, char *new, SplineChar *sc, PST *pst) {
2122     PST *p, *ps;
2123 
2124     if ( new==NULL || *new=='\0' ) {
2125 	if ( *base==str && *end=='\0' && sc!=NULL ) {
2126 	    /* We deleted the last name from the pst, it is meaningless, remove it */
2127 	    if ( sc->possub==pst )
2128 		sc->possub = pst->next;
2129 	    else {
2130 		for ( p = sc->possub, ps=p->next; ps!=NULL && ps!=pst; p=ps, ps=ps->next );
2131 		if ( ps!=NULL )
2132 		    p->next = pst->next;
2133 	    }
2134 	    pst->next = NULL;
2135 	    PSTFree(pst);
2136 	} else if ( *end=='\0' )
2137 	    *str = '\0';
2138 	else
2139 	    strcpy(str,end+1);	/* Skip the space */
2140     } else {
2141 	char *res = malloc(strlen(*base)+strlen(new)-(end-str)+1);
2142 	strncpy(res,*base,str-*base);
2143 	strcpy(res+(str-*base),new);
2144 	strcat(res,end);
2145 	free(*base);
2146 	*base = res;
2147     }
2148 }
2149 
ClearMissingState(struct problems * p)2150 static void ClearMissingState(struct problems *p) {
2151     int i;
2152 
2153     if ( p->mg!=NULL ) {
2154 	for ( i=0; i<p->rpl_cnt; ++i ) {
2155 	    free(p->mg[i].search);
2156 	    free(p->mg[i].rpl);
2157 	}
2158 	free(p->mg);
2159     } else
2160 	free(p->mlt);
2161     p->mlt = NULL;
2162     p->mg = NULL;
2163     p->rpl_cnt = p->rpl_max = 0;
2164 }
2165 
2166 enum missingglyph_type { mg_pst, mg_fpst, mg_kern, mg_vkern, mg_asm };
2167 struct mgask_data {
2168     GWindow gw;
2169     uint8 done, skipped;
2170     uint32 tag;
2171     char **_str, *start, *end;
2172     SplineChar *sc;
2173     PST *pst;
2174     struct problems *p;
2175 };
2176 
mark_to_replace(struct problems * p,struct mgask_data * d,char * rpl)2177 static void mark_to_replace(struct problems *p,struct mgask_data *d, char *rpl) {
2178     int ch;
2179 
2180     if ( p->rpl_cnt >= p->rpl_max ) {
2181 	if ( p->rpl_max == 0 )
2182 	    p->mg = malloc((p->rpl_max = 30)*sizeof(struct mgrpl));
2183 	else
2184 	    p->mg = realloc(p->mg,(p->rpl_max += 30)*sizeof(struct mgrpl));
2185     }
2186     ch = *d->end; *d->end = '\0';
2187     p->mg[p->rpl_cnt].search = copy( d->start );
2188     p->mg[p->rpl_cnt++].rpl = copy( rpl );
2189     *d->end = ch;
2190 }
2191 
2192 #define CID_Always	1001
2193 #define CID_RplText	1002
2194 #define CID_Ignore	1003
2195 #define CID_Rpl		1004
2196 #define CID_Skip	1005
2197 #define CID_Delete	1006
2198 
MGA_RplChange(GGadget * g,GEvent * e)2199 static int MGA_RplChange(GGadget *g, GEvent *e) {
2200     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
2201 	struct mgask_data *d = GDrawGetUserData(GGadgetGetWindow(g));
2202 	const unichar_t *rpl = _GGadgetGetTitle(g);
2203 	GGadgetSetEnabled(GWidgetGetControl(d->gw,CID_Rpl),*rpl!=0);
2204     }
2205 return( true );
2206 }
2207 
MGA_Rpl(GGadget * g,GEvent * e)2208 static int MGA_Rpl(GGadget *g, GEvent *e) {
2209     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2210 	struct mgask_data *d = GDrawGetUserData(GGadgetGetWindow(g));
2211 	const unichar_t *_rpl = _GGadgetGetTitle(GWidgetGetControl(d->gw,CID_RplText));
2212 	char *rpl = cu_copy(_rpl);
2213 	if ( GGadgetIsChecked(GWidgetGetControl(d->gw,CID_Always)))
2214 	    mark_to_replace(d->p,d,rpl);
2215 	mgreplace(d->_str,d->start,d->end,rpl,d->sc,d->pst);
2216 	free(rpl);
2217 	d->done = true;
2218     }
2219 return( true );
2220 }
2221 
MGA_Delete(GGadget * g,GEvent * e)2222 static int MGA_Delete(GGadget *g, GEvent *e) {
2223     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2224 	struct mgask_data *d = GDrawGetUserData(GGadgetGetWindow(g));
2225 	if ( GGadgetIsChecked(GWidgetGetControl(d->gw,CID_Always)))
2226 	    mark_to_replace(d->p,d,"");
2227 	mgreplace(d->_str,d->start,d->end,"",d->sc,d->pst);
2228 	d->done = true;
2229     }
2230 return( true );
2231 }
2232 
MGA_Skip(GGadget * g,GEvent * e)2233 static int MGA_Skip(GGadget *g, GEvent *e) {
2234     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2235 	struct mgask_data *d = GDrawGetUserData(GGadgetGetWindow(g));
2236 	d->done = d->skipped = true;
2237     }
2238 return( true );
2239 }
2240 
mgask_e_h(GWindow gw,GEvent * event)2241 static int mgask_e_h(GWindow gw, GEvent *event) {
2242     if ( event->type==et_close ) {
2243 	struct mgask_data *d = GDrawGetUserData(gw);
2244 	d->done = d->skipped = true;
2245     } else if ( event->type==et_char ) {
2246 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
2247 	    help("ui/dialogs/problems.html", NULL);
2248 return( true );
2249 	}
2250 return( false );
2251     }
2252 return( true );
2253 }
2254 
mgAsk(struct problems * p,char ** _str,char * str,char * end,uint32 tag,SplineChar * sc,enum missingglyph_type which,void * data)2255 static int mgAsk(struct problems *p,char **_str,char *str, char *end,uint32 tag,
2256 	SplineChar *sc,enum missingglyph_type which,void *data) {
2257     char buffer[200];
2258     static char *pstnames[] = { "", N_("position"), N_("pair"), N_("substitution"),
2259 	N_("alternate subs"), N_("multiple subs"), N_("ligature"), NULL };
2260     static char *fpstnames[] = { N_("Contextual position"), N_("Contextual substitution"),
2261 	N_("Chaining position"), N_("Chaining substitution"), N_("Reverse chaining subs"), NULL };
2262     static char *asmnames[] = { N_("Indic reordering"), N_("Contextual substitution"),
2263 	N_("Lig"), NULL, N_("Simple"), N_("Contextual insertion"), NULL, NULL, NULL,
2264 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2265 	N_("Kerning"), NULL };
2266     PST *pst = data;
2267     FPST *fpst = data;
2268     ASM *sm = data;
2269     KernClass *kc = data;
2270     char end_ch;
2271     GRect pos;
2272     GWindow gw;
2273     GWindowAttrs wattrs;
2274     GGadgetCreateData gcd[12];
2275     GTextInfo label[12];
2276     struct mgask_data d;
2277     int blen = GIntGetResource(_NUM_Buttonsize), ptwidth;
2278     int k, rplpos;
2279 
2280     end_ch = *end; *end = '\0';
2281 
2282     if ( which == mg_pst ) {
2283 	snprintf(buffer,sizeof(buffer),
2284 		_("Glyph %1$.50s with a %2$s from lookup subtable %3$.50s"),
2285 		sc->name, _(pstnames[pst->type]),
2286 		pst->subtable->subtable_name );
2287     } else if ( which == mg_fpst )
2288 	snprintf(buffer,sizeof(buffer),
2289 		_("%1$s from lookup subtable %2$.50s"),
2290 		_(fpstnames[fpst->type-pst_contextpos]),
2291 		fpst->subtable->subtable_name );
2292     else if ( which == mg_asm )
2293 	snprintf(buffer,sizeof(buffer),
2294 		_("%1$s from lookup subtable %2$.50s"),
2295 		_(asmnames[sm->type]),
2296 		sm->subtable->subtable_name );
2297     else
2298 	snprintf(buffer,sizeof(buffer),
2299 		_("%1$s from lookup subtable %2$.50s"),
2300 		which==mg_kern ? _("Kerning Class"): _("Vertical Kerning Class"),
2301 		kc->subtable->subtable_name );
2302 
2303     memset(&d,'\0',sizeof(d));
2304     d._str = _str;
2305     d.start = str;
2306     d.end = end;
2307     d.sc = sc;
2308     d.pst = which==mg_pst ? data : NULL;
2309     d.p = p;
2310     d.tag = tag;
2311 
2312     memset(&wattrs,0,sizeof(wattrs));
2313     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_centered|wam_restrict|wam_isdlg;
2314     wattrs.event_masks = ~(1<<et_charup);
2315     wattrs.is_dlg = 1;
2316     wattrs.restrict_input_to_me = 1;
2317     wattrs.centered = 1;
2318     wattrs.cursor = ct_pointer;
2319     wattrs.utf8_window_title = _("Check for missing glyph names");
2320     pos.x = pos.y = 0;
2321     ptwidth = 3*blen+GGadgetScale(80);
2322     pos.width =GDrawPointsToPixels(NULL,ptwidth);
2323     pos.height = GDrawPointsToPixels(NULL,180);
2324     d.gw = gw = GDrawCreateTopWindow(NULL,&pos,mgask_e_h,&d,&wattrs);
2325 
2326     memset(&label,0,sizeof(label));
2327     memset(&gcd,0,sizeof(gcd));
2328 
2329     k=0;
2330     label[k].text = (unichar_t *) buffer;
2331     label[k].text_is_1byte = true;
2332     gcd[k].gd.label = &label[k];
2333     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 6;
2334     gcd[k].gd.flags = gg_visible | gg_enabled;
2335     gcd[k++].creator = GLabelCreate;
2336 
2337     label[k].text = (unichar_t *) _(" refers to a missing glyph");
2338     label[k].text_is_1byte = true;
2339     gcd[k].gd.label = &label[k];
2340     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+13;
2341     gcd[k].gd.flags = gg_visible | gg_enabled;
2342     gcd[k++].creator = GLabelCreate;
2343 
2344     label[k].text = (unichar_t *) str;
2345     label[k].text_is_1byte = true;
2346     gcd[k].gd.label = &label[k];
2347     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+13;
2348     gcd[k].gd.flags = gg_visible | gg_enabled;
2349     gcd[k++].creator = GLabelCreate;
2350 
2351     label[k].text = (unichar_t *) _("Replace With:");
2352     label[k].text_is_1byte = true;
2353     gcd[k].gd.label = &label[k];
2354     gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+16;
2355     gcd[k].gd.flags = gg_visible | gg_enabled;
2356     gcd[k++].creator = GLabelCreate;
2357 
2358     rplpos = k;
2359     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+13; gcd[k].gd.pos.width = ptwidth-20;
2360     gcd[k].gd.flags = gg_visible | gg_enabled;
2361     gcd[k].gd.cid = CID_RplText;
2362     gcd[k].gd.handle_controlevent = MGA_RplChange;
2363     gcd[k++].creator = GTextFieldCreate;
2364 
2365     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+30;
2366     gcd[k].gd.flags = gg_visible | gg_enabled;
2367     label[k].text = (unichar_t *) _("Always");
2368     label[k].text_is_1byte = true;
2369     gcd[k].gd.label = &label[k];
2370     gcd[k].gd.cid = CID_Always;
2371     gcd[k++].creator = GCheckBoxCreate;
2372 
2373     label[k].text = (unichar_t *) _("Ignore this problem in the future");
2374     label[k].text_is_1byte = true;
2375     gcd[k].gd.label = &label[k];
2376     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+20;
2377     gcd[k].gd.flags = gg_visible | gg_enabled;
2378     gcd[k].gd.cid = CID_Ignore;
2379     gcd[k++].creator = GCheckBoxCreate;
2380 
2381     gcd[k].gd.pos.x = 10-3; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+30 -3;
2382     gcd[k].gd.pos.width = -1;
2383     gcd[k].gd.flags = gg_visible | gg_but_default;
2384     label[k].text = (unichar_t *) _("Replace");
2385     label[k].text_is_1byte = true;
2386     gcd[k].gd.label = &label[k];
2387     gcd[k].gd.handle_controlevent = MGA_Rpl;
2388     gcd[k].gd.cid = CID_Rpl;
2389     gcd[k++].creator = GButtonCreate;
2390 
2391     gcd[k].gd.pos.x = 10+blen+(ptwidth-3*blen-GGadgetScale(20))/2;
2392     gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+3;
2393     gcd[k].gd.pos.width = -1;
2394     gcd[k].gd.flags = gg_visible | gg_enabled;
2395     label[k].text = (unichar_t *) _("Remove");
2396     label[k].text_is_1byte = true;
2397     gcd[k].gd.label = &label[k];
2398     gcd[k].gd.handle_controlevent = MGA_Delete;
2399     gcd[k].gd.cid = CID_Delete;
2400     gcd[k++].creator = GButtonCreate;
2401 
2402     gcd[k].gd.pos.x = -10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y;
2403     gcd[k].gd.pos.width = -1;
2404     gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
2405     label[k].text = (unichar_t *) _("Skip");
2406     label[k].text_is_1byte = true;
2407     gcd[k].gd.label = &label[k];
2408     gcd[k].gd.handle_controlevent = MGA_Skip;
2409     gcd[k].gd.cid = CID_Skip;
2410     gcd[k++].creator = GButtonCreate;
2411 
2412     gcd[k].gd.pos.x = 2; gcd[k].gd.pos.y = 2;
2413     gcd[k].gd.pos.width = pos.width-4; gcd[k].gd.pos.height = pos.height-4;
2414     gcd[k].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels;
2415     gcd[k++].creator = GGroupCreate;
2416 
2417     GGadgetsCreate(gw,gcd);
2418     *end = end_ch;
2419     GDrawSetVisible(gw,true);
2420 
2421     while ( !d.done )
2422 	GDrawProcessOneEvent(NULL);
2423     if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Ignore)))
2424 	p->missingglyph = false;
2425     GDrawDestroyWindow(gw);
2426 return( !d.skipped );
2427 }
2428 
StrMissingGlyph(struct problems * p,char ** _str,SplineChar * sc,int which,void * data)2429 static int StrMissingGlyph(struct problems *p,char **_str,SplineChar *sc,int which, void *data) {
2430     char *end, ch, *str = *_str, *new;
2431     int off;
2432     int found = false;
2433     SplineFont *sf = p->fv!=NULL ? p->fv->b.sf : p->cv!=NULL ? p->cv->b.sc->parent : p->msc->parent;
2434     SplineChar *ssc;
2435     int changed=false;
2436 
2437     if ( str==NULL )
2438 return( false );
2439 
2440     while ( *str ) {
2441 	if ( p->finish || !p->missingglyph )
2442     break;
2443 	while ( *str==' ' ) ++str;
2444 	for ( end=str; *end!='\0' && *end!=' '; ++end );
2445 	ch = *end; *end='\0';
2446 	if ( strcmp(str,MAC_DELETED_GLYPH_NAME)==0 )
2447 	    ssc = (SplineChar *) 1;
2448 	else
2449 	    ssc = SFGetChar(sf,-1,str);
2450 	*end = ch;
2451 	if ( ssc==NULL ) {
2452 	    off = end-*_str;
2453 	    if ( (new = missinglookup(p,str))!=NULL ) {
2454 		mgreplace(_str, str,end, new, sc, which==mg_pst ? data : NULL);
2455 		changed = true;
2456 		off += (strlen(new)-(end-str));
2457 	    } else {
2458 		if ( mgAsk(p,_str,str,end,0,sc,which,data)) {
2459 		    changed = true;
2460 		    off = 0;
2461 		}
2462 		found = true;
2463 	    }
2464 	    if ( changed ) {
2465 		PST *test;
2466 		if ( which==mg_pst ) {
2467 		    for ( test = sc->possub; test!=NULL && test!=data; test=test->next );
2468 		    if ( test==NULL )		/* Entire pst was removed */
2469 return( true );
2470 		    *_str = test->u.subs.variant;
2471 		}
2472 		end = *_str+off;
2473 	    }
2474 	}
2475 	str = end;
2476     }
2477 return( found );
2478 }
2479 
SCMissingGlyph(struct problems * p,SplineChar * sc)2480 static int SCMissingGlyph(struct problems *p,SplineChar *sc) {
2481     PST *pst, *next;
2482     int found = false;
2483 
2484     if ( !p->missingglyph || p->finish || sc==NULL )
2485 return( false );
2486 
2487     for ( pst=sc->possub; pst!=NULL; pst=next ) {
2488 	next = pst->next;
2489 	switch ( pst->type ) {
2490 	  case pst_pair:
2491 	    found |= StrMissingGlyph(p,&pst->u.pair.paired,sc,mg_pst,pst);
2492 	  break;
2493 	  case pst_substitution:
2494 	  case pst_alternate:
2495 	  case pst_multiple:
2496 	  case pst_ligature:
2497 	    found |= StrMissingGlyph(p,&pst->u.subs.variant,sc,mg_pst,pst);
2498 	  break;
2499 	}
2500     }
2501 return( found );
2502 }
2503 
KCMissingGlyph(struct problems * p,KernClass * kc,int isv)2504 static int KCMissingGlyph(struct problems *p,KernClass *kc,int isv) {
2505     int i;
2506     int found = false;
2507     int which = isv ? mg_vkern : mg_kern;
2508 
2509     for ( i=0; i<kc->first_cnt; ++i ) if ( kc->firsts[i]!=NULL )
2510 	found |= StrMissingGlyph(p,&kc->firsts[i],NULL,which,kc);
2511     for ( i=1; i<kc->second_cnt; ++i )
2512 	found |= StrMissingGlyph(p,&kc->seconds[i],NULL,which,kc);
2513 return( found );
2514 }
2515 
FPSTMissingGlyph(struct problems * p,FPST * fpst)2516 static int FPSTMissingGlyph(struct problems *p,FPST *fpst) {
2517     int i,j;
2518     int found = false;
2519 
2520     switch ( fpst->format ) {
2521       case pst_glyphs:
2522 	for ( i=0; i<fpst->rule_cnt; ++i )
2523 	    for ( j=0; j<3; ++j )
2524 		found |= StrMissingGlyph(p,&(&fpst->rules[i].u.glyph.names)[j],
2525 			NULL,mg_fpst,fpst);
2526       break;
2527       case pst_class:
2528 	for ( i=1; i<3; ++i )
2529 	    for ( j=0; j<(&fpst->nccnt)[i]; ++j )
2530 		found |= StrMissingGlyph(p,&(&fpst->nclass)[i][j],NULL,mg_fpst,fpst);
2531       break;
2532       case pst_reversecoverage:
2533 	found |= StrMissingGlyph(p,&fpst->rules[0].u.rcoverage.replacements,NULL,mg_fpst,fpst);
2534 	/* fall through */;
2535       case pst_coverage:
2536 	for ( i=1; i<3; ++i )
2537 	    for ( j=0; j<(&fpst->rules[0].u.coverage.ncnt)[i]; ++j )
2538 		found |= StrMissingGlyph(p,&(&fpst->rules[0].u.coverage.ncovers)[i][j],NULL,mg_fpst,fpst);
2539       break;
2540     }
2541 return( found );
2542 }
2543 
ASMMissingGlyph(struct problems * p,ASM * sm)2544 static int ASMMissingGlyph(struct problems *p,ASM *sm) {
2545     int j;
2546     int found = false;
2547 
2548     for ( j=4; j<sm->class_cnt; ++j )
2549 	found |= StrMissingGlyph(p,&sm->classes[j],NULL,mg_asm,sm);
2550 return( found );
2551 }
2552 
LookupFeaturesMissScript(struct problems * p,OTLookup * otl,OTLookup * nested,uint32 script,SplineFont * sf,char * glyph_name)2553 static int LookupFeaturesMissScript(struct problems *p,OTLookup *otl,OTLookup *nested,
2554 	uint32 script, SplineFont *sf, char *glyph_name) {
2555     OTLookup *invokers, *any;
2556     struct lookup_subtable *subs;
2557     int i,l, ret;
2558     int found = false;
2559     FeatureScriptLangList *fsl;
2560     struct scriptlanglist *sl;
2561     char buffer[400];
2562     char *buts[4];
2563 
2564     if ( script==DEFAULT_SCRIPT )
2565 return( false );
2566 
2567     if ( otl->features == NULL ) {
2568 	/* No features invoke us, so presume that we are to be invoked by a */
2569 	/*  contextual lookup, and check its scripts rather than ours */
2570 	if ( nested!=NULL ) {
2571 	    /* There is no need to have a nested contextual lookup */
2572 	    /*  so we don't support them */
2573 return(false);
2574 	}
2575 	any = NULL;
2576 	for ( invokers=otl->lookup_type>=gpos_start?sf->gpos_lookups:sf->gsub_lookups;
2577 		invokers!=NULL ; invokers = invokers->next ) {
2578 	    for ( subs=invokers->subtables; subs!=NULL; subs=subs->next ) {
2579 		if ( subs->fpst!=NULL ) {
2580 		    FPST *fpst = subs->fpst;
2581 		    for ( i=0; i<fpst->rule_cnt; ++i ) {
2582 			struct fpst_rule *r = &fpst->rules[i];
2583 			for ( l=0; l<r->lookup_cnt; ++l )
2584 			    if ( r->lookups[l].lookup == otl ) {
2585 				found |= LookupFeaturesMissScript(p,invokers,otl,script,sf,glyph_name);
2586 			        any = invokers;
2587 			    }
2588 		    }
2589 		}
2590 	    }
2591 	}
2592 	if ( any==NULL ) {
2593 	    /* No opentype contextual lookup uses this lookup with no features*/
2594 	    /*  so it appears totally useless. But a mac feature might I guess*/
2595 	    /*  so don't complain */
2596 	}
2597     } else {
2598 	for ( fsl = otl->features; fsl!=NULL; fsl=fsl->next ) {
2599 	    for ( sl=fsl->scripts; sl!=NULL; sl=sl->next ) {
2600 		if ( sl->script==script )
2601 	    break;
2602 	    }
2603 	    if ( sl!=NULL )
2604 	break;
2605 	}
2606 	if ( fsl==NULL ) {
2607 	    buffer[0]='\0';
2608 	    if ( nested!=NULL )
2609 		snprintf(buffer,sizeof(buffer),
2610 		  _("The lookup %.30s which invokes lookup %.30s is active "
2611 		    "for glyph %.30s which has script '%c%c%c%c', yet this script does not "
2612 		    "appear in any of the features which apply the lookup.\n"
2613 		    "Would you like to add this script to one of those features?"),
2614 			otl->lookup_name, nested->lookup_name,
2615 			glyph_name,
2616 			script>>24, script>>16, script>>8, script);
2617 	    else
2618 		snprintf(buffer,sizeof(buffer),
2619 		  _("The lookup %.30s is active for glyph %.30s which has script "
2620 		    "'%c%c%c%c', yet this script does not appear in any of the features which "
2621 		    "apply the lookup.\n\n"
2622 		    "Would you like to add this script to one of those features?"),
2623 			otl->lookup_name, glyph_name,
2624 			script>>24, script>>16, script>>8, script);
2625 	    buts[0] = _("_OK"); buts[1] = _("_Skip"); buts[2]="_Ignore"; buts[3] = NULL;
2626 	    ret = ff_ask(_("Missing Script"),(const char **) buts,0,1,buffer);
2627 	    if ( ret==0 ) {
2628 		sl = chunkalloc(sizeof(struct scriptlanglist));
2629 		sl->script = script;
2630 		sl->lang_cnt = 1;
2631 		sl->langs[0] = DEFAULT_LANG;
2632 		sl->next = otl->features->scripts;
2633 		otl->features->scripts = sl;
2634 		sf->changed = true;
2635 	    } else if ( ret==2 )
2636 		p->missingscriptinfeature = false;
2637 return( true );
2638 	}
2639     }
2640 return( found );
2641 }
2642 
SCMissingScriptFeat(struct problems * p,SplineFont * sf,SplineChar * sc)2643 static int SCMissingScriptFeat(struct problems *p,SplineFont *sf,SplineChar *sc) {
2644     PST *pst;
2645     int found = false;
2646     uint32 script;
2647     AnchorPoint *ap;
2648 
2649     if ( !p->missingscriptinfeature || p->finish || sc==NULL )
2650 return( false );
2651     script = SCScriptFromUnicode(sc);
2652 
2653     for ( pst=sc->possub; pst!=NULL; pst=pst->next ) if ( pst->subtable!=NULL )
2654 	found |= LookupFeaturesMissScript(p,pst->subtable->lookup,NULL,script,sf,sc->name);
2655     for ( ap=sc->anchor; ap!=NULL; ap=ap->next ) if ( ap->anchor->subtable!=NULL )
2656 	found |= LookupFeaturesMissScript(p,ap->anchor->subtable->lookup,NULL,script,sf,sc->name);
2657 
2658 return( found );
2659 }
2660 
StrMissingScript(struct problems * p,SplineFont * sf,OTLookup * otl,char * class)2661 static int StrMissingScript(struct problems *p,SplineFont *sf,OTLookup *otl,char *class) {
2662     char *pt, *start;
2663     int ch;
2664     SplineChar *sc;
2665     uint32 script;
2666     int found = 0;
2667 
2668     if ( class==NULL )
2669 return( false );
2670 
2671     for ( pt=class; *pt && p->missingscriptinfeature; ) {
2672 	while ( *pt==' ' ) ++pt;
2673 	if ( *pt=='\0' )
2674     break;
2675 	for ( start=pt; *pt && *pt!=' '; ++pt );
2676 	ch = *pt; *pt='\0';
2677 	sc = SFGetChar(sf,-1,start);
2678 	*pt = ch;
2679 	if ( sc!=NULL ) {
2680 	    script = SCScriptFromUnicode(sc);
2681 	    found |= LookupFeaturesMissScript(p,otl,NULL,script,sf,sc->name);
2682 	}
2683     }
2684 return( found );
2685 }
2686 
KCMissingScriptFeat(struct problems * p,SplineFont * sf,KernClass * kc,int isv)2687 static int KCMissingScriptFeat(struct problems *p,SplineFont *sf, KernClass *kc,int isv) {
2688     int i;
2689     int found = false;
2690     OTLookup *otl = kc->subtable->lookup;
2691 
2692     for ( i=0; i<kc->first_cnt; ++i )
2693 	found |= StrMissingScript(p,sf,otl,kc->firsts[i]);
2694 return( found );
2695 }
2696 
FPSTMissingScriptFeat(struct problems * p,SplineFont * sf,FPST * fpst)2697 static int FPSTMissingScriptFeat(struct problems *p,SplineFont *sf,FPST *fpst) {
2698     int i,j;
2699     int found = false;
2700     OTLookup *otl = fpst->subtable->lookup;
2701 
2702     switch ( fpst->format ) {
2703       case pst_glyphs:
2704 	for ( i=0; i<fpst->rule_cnt; ++i )
2705 	    for ( j=0; j<3; ++j )
2706 		found |= StrMissingScript(p,sf,otl,(&fpst->rules[i].u.glyph.names)[j]);
2707       break;
2708       case pst_class:
2709 	for ( i=1; i<3; ++i )
2710 	    for ( j=0; j<(&fpst->nccnt)[i]; ++j )
2711 		found |= StrMissingScript(p,sf,otl,(&fpst->nclass)[i][j]);
2712       break;
2713       case pst_reversecoverage:
2714 		found |= StrMissingScript(p,sf,otl,fpst->rules[0].u.rcoverage.replacements);
2715 	/* fall through */;
2716       case pst_coverage:
2717 	for ( i=1; i<3; ++i )
2718 	    for ( j=0; j<(&fpst->rules[0].u.coverage.ncnt)[i]; ++j )
2719 		found |= StrMissingScript(p,sf,otl,(&fpst->rules[0].u.coverage.ncovers)[i][j]);
2720       break;
2721     }
2722 return( found );
2723 }
2724 
CheckForATT(struct problems * p)2725 static int CheckForATT(struct problems *p) {
2726     int found = false;
2727     int i,k;
2728     FPST *fpst;
2729     ASM *sm;
2730     KernClass *kc;
2731     SplineFont *_sf, *sf;
2732     static char *buts[3];
2733     buts[0] = _("_Yes");
2734     buts[1] = _("_No");
2735     buts[2] = NULL;
2736 
2737     _sf = p->fv->b.sf;
2738     if ( _sf->cidmaster ) _sf = _sf->cidmaster;
2739 
2740     if ( p->missingglyph && !p->finish ) {
2741 	if ( p->cv!=NULL )
2742 	    found = SCMissingGlyph(p,p->cv->b.sc);
2743 	else if ( p->msc!=NULL )
2744 	    found = SCMissingGlyph(p,p->msc);
2745 	else {
2746 	    k=0;
2747 	    do {
2748 		if ( _sf->subfonts==NULL ) sf = _sf;
2749 		else sf = _sf->subfonts[k++];
2750 		for ( i=0; i<sf->glyphcnt && !p->finish; ++i ) if ( sf->glyphs[i]!=NULL )
2751 		    found |= SCMissingGlyph(p,sf->glyphs[i]);
2752 	    } while ( k<_sf->subfontcnt && !p->finish );
2753 	    for ( kc=_sf->kerns; kc!=NULL && !p->finish; kc=kc->next )
2754 		found |= KCMissingGlyph(p,kc,false);
2755 	    for ( kc=_sf->vkerns; kc!=NULL && !p->finish; kc=kc->next )
2756 		found |= KCMissingGlyph(p,kc,true);
2757 	    for ( fpst=_sf->possub; fpst!=NULL && !p->finish && p->missingglyph; fpst=fpst->next )
2758 		found |= FPSTMissingGlyph(p,fpst);
2759 	    for ( sm=_sf->sm; sm!=NULL && !p->finish && p->missingglyph; sm=sm->next )
2760 		found |= ASMMissingGlyph(p,sm);
2761 	}
2762 	ClearMissingState(p);
2763     }
2764 
2765     if ( p->missingscriptinfeature && !p->finish ) {
2766 	if ( p->cv!=NULL )
2767 	    found = SCMissingScriptFeat(p,_sf,p->cv->b.sc);
2768 	else if ( p->msc!=NULL )
2769 	    found = SCMissingScriptFeat(p,_sf,p->msc);
2770 	else {
2771 	    k=0;
2772 	    do {
2773 		if ( _sf->subfonts==NULL ) sf = _sf;
2774 		else sf = _sf->subfonts[k++];
2775 		for ( i=0; i<sf->glyphcnt && !p->finish; ++i ) if ( sf->glyphs[i]!=NULL )
2776 		    found |= SCMissingScriptFeat(p,_sf,sf->glyphs[i]);
2777 	    } while ( k<_sf->subfontcnt && !p->finish );
2778 	    for ( kc=_sf->kerns; kc!=NULL && !p->finish; kc=kc->next )
2779 		found |= KCMissingScriptFeat(p,_sf,kc,false);
2780 	    for ( kc=_sf->vkerns; kc!=NULL && !p->finish; kc=kc->next )
2781 		found |= KCMissingScriptFeat(p,_sf,kc,true);
2782 	    for ( fpst=_sf->possub; fpst!=NULL && !p->finish && p->missingglyph; fpst=fpst->next )
2783 		found |= FPSTMissingScriptFeat(p,_sf,fpst);
2784 	    /* Apple's state machines don't have the concept of "script" */
2785 	    /*  for their feature/settings */
2786 	}
2787     }
2788 
2789 return( found );
2790 }
2791 
DoProbs(struct problems * p)2792 static void DoProbs(struct problems *p) {
2793     int i, ret=false, gid;
2794     SplineChar *sc;
2795     BDFFont *bdf;
2796 
2797     ret = CheckForATT(p);
2798     if ( p->cv!=NULL ) {
2799 	ret |= SCProblems(p->cv,NULL,p);
2800 	ret |= CIDCheck(p,p->cv->b.sc->orig_pos);
2801     } else if ( p->msc!=NULL ) {
2802 	ret |= SCProblems(NULL,p->msc,p);
2803 	ret |= CIDCheck(p,p->msc->orig_pos);
2804     } else {
2805 	for ( i=0; i<p->fv->b.map->enccount && !p->finish; ++i )
2806 	    if ( p->fv->b.selected[i] ) {
2807 		sc = NULL;
2808 		if ( (gid=p->fv->b.map->map[i])!=-1 && (sc = p->fv->b.sf->glyphs[gid])!=NULL ) {
2809 		    if ( SCProblems(NULL,sc,p)) {
2810 			if ( sc!=p->lastcharopened ) {
2811 			    if ( (CharView *) (sc->views)!=NULL )
2812 				GDrawRaise(((CharView *) (sc->views))->gw);
2813 			    else
2814 				CharViewCreate(sc,p->fv,-1);
2815 			    p->lastcharopened = sc;
2816 			}
2817 			ret = true;
2818 		    }
2819 		}
2820 		if ( !p->finish && p->bitmaps && !SCWorthOutputting(sc)) {
2821 		    for ( bdf=p->fv->b.sf->bitmaps; bdf!=NULL; bdf=bdf->next )
2822 			if ( i<bdf->glyphcnt && bdf->glyphs[i]!=NULL ) {
2823 			    sc = SFMakeChar(p->fv->b.sf,p->fv->b.map,i);
2824 			    ExplainIt(p,sc,_("This blank outline glyph has an unexpected bitmap version"),0,0);
2825 			    ret = true;
2826 			}
2827 		}
2828 		ret |= CIDCheck(p,i);
2829 	    }
2830     }
2831     if ( !ret )
2832 	ff_post_error(_("No problems found"),_("No problems found"));
2833 }
2834 
FigureStandardHeights(struct problems * p)2835 static void FigureStandardHeights(struct problems *p) {
2836     BlueData bd;
2837 
2838     QuickBlues(p->fv->b.sf,p->layer,&bd);
2839     p->xheight = bd.xheight;
2840     p->caph = bd.caph;
2841     p->ascent = bd.ascent;
2842     p->descent = bd.descent;
2843 }
2844 
Prob_DoAll(GGadget * g,GEvent * e)2845 static int Prob_DoAll(GGadget *g, GEvent *e) {
2846     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2847 	struct problems *p = GDrawGetUserData(GGadgetGetWindow(g));
2848 	int set = GGadgetGetCid(g)==CID_SetAll;
2849 	GWindow gw = GGadgetGetWindow(g);
2850 	static int cbs[] = { CID_OpenPaths, CID_IntersectingPaths,
2851 	    CID_PointsTooClose, CID_XNear, CID_MissingExtrema,
2852 	    CID_PointsTooFar, CID_NonIntegral,
2853 	    CID_YNear, CID_YNearStd, CID_HintNoPt, CID_PtNearHint,
2854 	    CID_HintWidthNear, CID_LineStd, CID_Direction, CID_CpStd,
2855 	    CID_CpOdd, CID_FlippedRefs, CID_Bitmaps, CID_AdvanceWidth,
2856 	    CID_BadSubs, CID_MissingAnchor, CID_MissingGlyph,
2857 	    CID_MissingScriptInFeature,
2858 	    CID_Stem3, CID_IrrelevantCP, CID_TooManyPoints,
2859 	    CID_TooManyHints, CID_TooDeepRefs, CID_BitmapWidths,
2860 	    CID_MultUni, CID_MultName, CID_PtMatchRefsOutOfDate,
2861 	    CID_RefBadTransformTTF, CID_RefBadTransformPS, CID_MixedContoursRefs,
2862 	    CID_UniNameMisMatch, CID_BBYMax, CID_BBYMin, CID_BBXMax, CID_BBXMin,
2863 	    CID_MultUseMyMetrics, CID_OverlappedHints,
2864 	    0 };
2865 	int i;
2866 	if ( p->fv->b.cidmaster!=NULL ) {
2867 	    GGadgetSetChecked(GWidgetGetControl(gw,CID_CIDMultiple),set);
2868 	    GGadgetSetChecked(GWidgetGetControl(gw,CID_CIDBlank),set);
2869 	}
2870 	if ( p->fv->b.sf->hasvmetrics )
2871 	    GGadgetSetChecked(GWidgetGetControl(gw,CID_VAdvanceWidth),set);
2872 	for ( i=0; cbs[i]!=0; ++i )
2873 	    GGadgetSetChecked(GWidgetGetControl(gw,cbs[i]),set);
2874     }
2875 return( true );
2876 }
2877 
Prob_OK(GGadget * g,GEvent * e)2878 static int Prob_OK(GGadget *g, GEvent *e) {
2879     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2880 	GWindow gw = GGadgetGetWindow(g);
2881 	struct problems *p = GDrawGetUserData(gw);
2882 	int errs = false;
2883 
2884 	openpaths = p->openpaths = GGadgetIsChecked(GWidgetGetControl(gw,CID_OpenPaths));
2885 	intersectingpaths = p->intersectingpaths = GGadgetIsChecked(GWidgetGetControl(gw,CID_IntersectingPaths));
2886 	nonintegral = p->nonintegral = GGadgetIsChecked(GWidgetGetControl(gw,CID_NonIntegral));
2887 	pointstooclose = p->pointstooclose = GGadgetIsChecked(GWidgetGetControl(gw,CID_PointsTooClose));
2888 	pointstoofar = p->pointstoofar = GGadgetIsChecked(GWidgetGetControl(gw,CID_PointsTooFar));
2889 	/*missing = p->missingextrema = GGadgetIsChecked(GWidgetGetControl(gw,CID_MissingExtrema))*/;
2890 	doxnear = p->xnearval = GGadgetIsChecked(GWidgetGetControl(gw,CID_XNear));
2891 	doynear = p->ynearval = GGadgetIsChecked(GWidgetGetControl(gw,CID_YNear));
2892 	doynearstd = p->ynearstd = GGadgetIsChecked(GWidgetGetControl(gw,CID_YNearStd));
2893 	linestd = p->linenearstd = GGadgetIsChecked(GWidgetGetControl(gw,CID_LineStd));
2894 	cpstd = p->cpnearstd = GGadgetIsChecked(GWidgetGetControl(gw,CID_CpStd));
2895 	cpodd = p->cpodd = GGadgetIsChecked(GWidgetGetControl(gw,CID_CpOdd));
2896 	hintnopt = p->hintwithnopt = GGadgetIsChecked(GWidgetGetControl(gw,CID_HintNoPt));
2897 	ptnearhint = p->ptnearhint = GGadgetIsChecked(GWidgetGetControl(gw,CID_PtNearHint));
2898 	hintwidth = p->hintwidthnearval = GGadgetIsChecked(GWidgetGetControl(gw,CID_HintWidthNear));
2899 	missingextrema = p->missingextrema = GGadgetIsChecked(GWidgetGetControl(gw,CID_MissingExtrema));
2900 	direction = p->direction = GGadgetIsChecked(GWidgetGetControl(gw,CID_Direction));
2901 	flippedrefs = p->flippedrefs = GGadgetIsChecked(GWidgetGetControl(gw,CID_FlippedRefs));
2902 	bitmaps = p->bitmaps = GGadgetIsChecked(GWidgetGetControl(gw,CID_Bitmaps));
2903 	bitmapwidths = p->bitmapwidths = GGadgetIsChecked(GWidgetGetControl(gw,CID_BitmapWidths));
2904 	advancewidth = p->advancewidth = GGadgetIsChecked(GWidgetGetControl(gw,CID_AdvanceWidth));
2905 	bbymax = p->bbymax = GGadgetIsChecked(GWidgetGetControl(gw,CID_BBYMax));
2906 	bbymin = p->bbymin = GGadgetIsChecked(GWidgetGetControl(gw,CID_BBYMin));
2907 	bbxmax = p->bbxmax = GGadgetIsChecked(GWidgetGetControl(gw,CID_BBXMax));
2908 	bbxmin = p->bbxmin = GGadgetIsChecked(GWidgetGetControl(gw,CID_BBXMin));
2909 	irrelevantcp = p->irrelevantcontrolpoints = GGadgetIsChecked(GWidgetGetControl(gw,CID_IrrelevantCP));
2910 	multuni = p->multuni = GGadgetIsChecked(GWidgetGetControl(gw,CID_MultUni));
2911 	multname = p->multname = GGadgetIsChecked(GWidgetGetControl(gw,CID_MultName));
2912 	uninamemismatch = p->uninamemismatch = GGadgetIsChecked(GWidgetGetControl(gw,CID_UniNameMisMatch));
2913 	badsubs = p->badsubs = GGadgetIsChecked(GWidgetGetControl(gw,CID_BadSubs));
2914 	missinganchor = p->missinganchor = GGadgetIsChecked(GWidgetGetControl(gw,CID_MissingAnchor));
2915 	missingglyph = p->missingglyph = GGadgetIsChecked(GWidgetGetControl(gw,CID_MissingGlyph));
2916 	missingscriptinfeature = p->missingscriptinfeature = GGadgetIsChecked(GWidgetGetControl(gw,CID_MissingScriptInFeature));
2917 	toomanypoints = p->toomanypoints = GGadgetIsChecked(GWidgetGetControl(gw,CID_TooManyPoints));
2918 	toomanyhints = p->toomanyhints = GGadgetIsChecked(GWidgetGetControl(gw,CID_TooManyHints));
2919 	overlappedhints = p->overlappedhints = GGadgetIsChecked(GWidgetGetControl(gw,CID_OverlappedHints));
2920 	ptmatchrefsoutofdate = p->ptmatchrefsoutofdate = GGadgetIsChecked(GWidgetGetControl(gw,CID_PtMatchRefsOutOfDate));
2921 	multusemymetrics = p->multusemymetrics = GGadgetIsChecked(GWidgetGetControl(gw,CID_MultUseMyMetrics));
2922 	refsbadtransformttf = p->refsbadtransformttf = GGadgetIsChecked(GWidgetGetControl(gw,CID_RefBadTransformTTF));
2923 	refsbadtransformps = p->refsbadtransformps = GGadgetIsChecked(GWidgetGetControl(gw,CID_RefBadTransformPS));
2924 	mixedcontoursrefs = p->mixedcontoursrefs = GGadgetIsChecked(GWidgetGetControl(gw,CID_MixedContoursRefs));
2925 	toodeeprefs = p->toodeeprefs = GGadgetIsChecked(GWidgetGetControl(gw,CID_TooDeepRefs));
2926 	stem3 = p->stem3 = GGadgetIsChecked(GWidgetGetControl(gw,CID_Stem3));
2927 	if ( stem3 )
2928 	    showexactstem3 = p->showexactstem3 = GGadgetIsChecked(GWidgetGetControl(gw,CID_ShowExactStem3));
2929 	if ( p->fv->b.cidmaster!=NULL ) {
2930 	    cidmultiple = p->cidmultiple = GGadgetIsChecked(GWidgetGetControl(gw,CID_CIDMultiple));
2931 	    cidblank = p->cidblank = GGadgetIsChecked(GWidgetGetControl(gw,CID_CIDBlank));
2932 	}
2933 	if ( p->fv->b.sf->hasvmetrics ) {
2934 	    vadvancewidth = p->vadvancewidth = GGadgetIsChecked(GWidgetGetControl(gw,CID_VAdvanceWidth));
2935 	} else
2936 	    p->vadvancewidth = false;
2937 	p->explain = true;
2938 	if ( doxnear )
2939 	    p->xval = xval = GetReal8(gw,CID_XNearVal,U_("_X near¹"),&errs);
2940 	if ( doynear )
2941 	    p->yval = yval = GetReal8(gw,CID_YNearVal,U_("_Y near¹"),&errs);
2942 	if ( hintwidth )
2943 	    widthval = p->widthval = GetReal8(gw,CID_HintWidth,U_("Hint _Width Near¹"),&errs);
2944 	if ( p->advancewidth )
2945 	    advancewidthval = p->advancewidthval = GetInt8(gw,CID_AdvanceWidthVal,U_("Advance Width not"),&errs);
2946 	if ( p->vadvancewidth )
2947 	    vadvancewidthval = p->vadvancewidthval = GetInt8(gw,CID_VAdvanceWidthVal,U_("Vertical Advance not"),&errs);
2948 	if ( p->bbymax )
2949 	    bbymax_val = p->bbymax_val = GetInt8(gw,CID_BBYMaxVal,U_("Bounding box above"),&errs);
2950 	if ( p->bbymin )
2951 	    bbymin_val = p->bbymin_val = GetInt8(gw,CID_BBYMinVal,U_("Bounding box below"),&errs);
2952 	if ( p->bbxmax )
2953 	    bbxmax_val = p->bbxmax_val = GetInt8(gw,CID_BBXMaxVal,U_("Bounding box right of"),&errs);
2954 	if ( p->bbxmin )
2955 	    bbxmin_val = p->bbxmin_val = GetInt8(gw,CID_BBXMinVal,U_("Bounding box left of"),&errs);
2956 	if ( toomanypoints )
2957 	    p->pointsmax = pointsmax = GetInt8(gw,CID_PointsMax,_("_More points than:"),&errs);
2958 	if ( toomanyhints )
2959 	    p->hintsmax = hintsmax = GetInt8(gw,CID_HintsMax,_("_More hints than:"),&errs);
2960 	if ( toodeeprefs )
2961 /* GT: Refs is an abbreviation for References. Space is somewhat constrained here */
2962 	    p->refdepthmax = refdepthmax = GetInt8(gw,CID_RefDepthMax,_("Refs neste_d deeper than:"),&errs);
2963 	if ( irrelevantcp )
2964 	    p->irrelevantfactor = irrelevantfactor = GetReal8(gw,CID_IrrelevantFactor,_("Irrelevant _Factor:"),&errs)/100.0;
2965 	near = p->near = GetReal8(gw,CID_Near,_("Near"),&errs);
2966 	if ( errs )
2967 return( true );
2968 	lastsf = p->fv->b.sf;
2969 	if ( doynearstd )
2970 	    FigureStandardHeights(p);
2971 	GDrawSetVisible(gw,false);
2972 	if ( openpaths || intersectingpaths || pointstooclose  || doxnear || doynear ||
2973 		doynearstd || linestd || hintnopt || ptnearhint || hintwidth ||
2974 		direction || p->cidmultiple || p->cidblank || p->flippedrefs ||
2975 		p->bitmaps || p->advancewidth || p->vadvancewidth || p->stem3 ||
2976 		p->bitmapwidths || p->missinganchor ||
2977 		p->irrelevantcontrolpoints || p->badsubs || p->missingglyph ||
2978 		p->missingscriptinfeature || nonintegral || pointstoofar ||
2979 		p->toomanypoints || p->toomanyhints || p->missingextrema ||
2980 		p->toodeeprefs || multuni || multname || uninamemismatch ||
2981 		p->ptmatchrefsoutofdate || p->refsbadtransformttf ||
2982 		p->multusemymetrics || p->overlappedhints ||
2983 		p->mixedcontoursrefs || p->refsbadtransformps ||
2984 		p->bbymax || p->bbxmax || p->bbymin || p->bbxmin ) {
2985 	    DoProbs(p);
2986 	}
2987 	p->done = true;
2988     }
2989 return( true );
2990 }
2991 
DummyFindProblems(CharView * cv)2992 static void DummyFindProblems(CharView *cv) {
2993     struct problems p;
2994 
2995     memset(&p,0,sizeof(p));
2996     p.fv = (FontView *) (cv->b.fv);
2997     p.cv=cv;
2998     p.layer = CVLayer((CharViewBase *) cv);
2999     p.map = cv->b.fv->map;
3000     p.lastcharopened = cv->b.sc;
3001 
3002     p.openpaths = true;
3003     p.intersectingpaths = true;
3004     p.direction = true;
3005     p.flippedrefs = true;
3006     p.missingextrema = true;
3007     p.toomanypoints = true;
3008     p.toomanyhints = true;
3009     p.pointstoofar = true;
3010     p.nonintegral = true;
3011     p.missinganchor = true;
3012     p.overlappedhints = true;
3013 
3014     p.pointsmax = 1500;
3015     p.hintsmax = 96;
3016 
3017     p.explain = true;
3018 
3019     DoProbs(&p);
3020     if ( p.explainw!=NULL )
3021 	GDrawDestroyWindow(p.explainw);
3022 }
3023 
Prob_Cancel(GGadget * g,GEvent * e)3024 static int Prob_Cancel(GGadget *g, GEvent *e) {
3025     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3026 	struct problems *p = GDrawGetUserData(GGadgetGetWindow(g));
3027 	p->done = true;
3028     }
3029 return( true );
3030 }
3031 
Prob_TextChanged(GGadget * g,GEvent * e)3032 static int Prob_TextChanged(GGadget *g, GEvent *e) {
3033     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
3034 	GGadgetSetChecked(GWidgetGetControl(GGadgetGetWindow(g),(intpt) GGadgetGetUserData(g)),true);
3035     }
3036 return( true );
3037 }
3038 
Prob_EnableExact(GGadget * g,GEvent * e)3039 static int Prob_EnableExact(GGadget *g, GEvent *e) {
3040     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
3041 	GGadgetSetEnabled(GWidgetGetControl(GGadgetGetWindow(g),CID_ShowExactStem3),
3042 		GGadgetIsChecked(g));
3043     }
3044 return( true );
3045 }
3046 
e_h(GWindow gw,GEvent * event)3047 static int e_h(GWindow gw, GEvent *event) {
3048     if ( event->type==et_close ) {
3049 	struct problems *p = GDrawGetUserData(gw);
3050 	p->done = true;
3051     }
3052 return( event->type!=et_char );
3053 }
3054 
FindProblems(FontView * fv,CharView * cv,SplineChar * sc)3055 void FindProblems(FontView *fv,CharView *cv, SplineChar *sc) {
3056     GRect pos;
3057     GWindow gw;
3058     GWindowAttrs wattrs;
3059     GGadgetCreateData pgcd[15], pagcd[8], hgcd[10], rgcd[10], cgcd[5], mgcd[11], agcd[7], rfgcd[9];
3060     GGadgetCreateData bbgcd[14];
3061     GGadgetCreateData pboxes[6], paboxes[4], rfboxes[4], hboxes[5], aboxes[2],
3062 	    cboxes[2], bbboxes[2], rboxes[2], mboxes[5];
3063     GGadgetCreateData *parray[12], *pharray1[4], *pharray2[4], *pharray3[7],
3064 	    *paarray[8], *paharray[4], *rfarray[9], *rfharray[4],
3065 	    *harray[9], *hharray1[4], *hharray2[4], *hharray3[4], *aarray[6],
3066 	    *carray[5], *bbarray[8][4], *rarray[7], *marray[7][2],
3067 	    *mharray1[4], *mharray2[5], *barray[10];
3068     GTextInfo plabel[15], palabel[8], hlabel[9], rlabel[10], clabel[5], mlabel[10], alabel[7], rflabel[9];
3069     GTextInfo bblabel[14];
3070     GTabInfo aspects[9];
3071     struct problems p;
3072     char xnbuf[20], ynbuf[20], widthbuf[20], nearbuf[20], awidthbuf[20],
3073 	    vawidthbuf[20], irrel[20], pmax[20], hmax[20], rmax[20],
3074 	    xxmaxbuf[20], xxminbuf[20], yymaxbuf[20], yyminbuf[20];
3075     SplineChar *ssc;
3076     int i;
3077     SplineFont *sf;
3078     /*static GBox smallbox = { bt_raised, bs_rect, 2, 1, 0, 0, 0, 0, 0, 0, COLOR_DEFAULT, COLOR_DEFAULT, 0, 0, 0, 0, 0, 0, 0 };*/
3079 
3080     memset(&p,0,sizeof(p));
3081     if ( fv==NULL ) fv = (FontView *) (cv->b.fv);
3082     p.fv = fv; p.cv=cv; p.msc = sc;
3083     if ( cv!=NULL )
3084 	p.lastcharopened = cv->b.sc;
3085     if ( fv!=NULL ) {
3086 	p.map = fv->b.map;
3087 	p.layer = fv->b.active_layer;
3088     } else if ( cv!=NULL ) {
3089 	p.map = cv->b.fv->map;
3090 	p.layer = CVLayer((CharViewBase *) cv);
3091     } else {
3092 	p.map = sc->parent->fv->map;
3093 	p.layer = sc->parent->fv->active_layer;
3094     }
3095 
3096     memset(&wattrs,0,sizeof(wattrs));
3097     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_restrict|wam_isdlg;
3098     wattrs.event_masks = ~(1<<et_charup);
3099     wattrs.restrict_input_to_me = 1;
3100     wattrs.undercursor = 1;
3101     wattrs.cursor = ct_pointer;
3102     wattrs.utf8_window_title = _("Find Problems");
3103     pos.x = pos.y = 0;
3104     pos.width = GGadgetScale(GDrawPointsToPixels(NULL,218));
3105     pos.height = GDrawPointsToPixels(NULL,294);
3106     gw = GDrawCreateTopWindow(NULL,&pos,e_h,&p,&wattrs);
3107 
3108     memset(&plabel,0,sizeof(plabel));
3109     memset(&pgcd,0,sizeof(pgcd));
3110     memset(&pboxes,0,sizeof(pboxes));
3111 
3112     plabel[0].text = (unichar_t *) _("Non-_Integral coordinates");
3113     plabel[0].text_is_1byte = true;
3114     plabel[0].text_in_resource = true;
3115     pgcd[0].gd.label = &plabel[0];
3116     pgcd[0].gd.pos.x = 3; pgcd[0].gd.pos.y = 5;
3117     pgcd[0].gd.flags = gg_visible | gg_enabled;
3118     if ( nonintegral ) pgcd[0].gd.flags |= gg_cb_on;
3119     pgcd[0].gd.popup_msg = _(
3120 	    "The coordinates of all points and control points in truetype\n"
3121 	    "must be integers (if they are not integers then FontForge will\n"
3122 	    "round them when it outputs them, potentially causing havoc).\n"
3123 	    "Even in PostScript fonts it is generally a good idea to use\n"
3124 	    "integral values.");
3125     pgcd[0].gd.cid = CID_NonIntegral;
3126     pgcd[0].creator = GCheckBoxCreate;
3127     parray[0] = &pgcd[0];
3128 
3129     plabel[1].text = (unichar_t *) U_("_X near¹");
3130     plabel[1].text_is_1byte = true;
3131     plabel[1].text_in_resource = true;
3132     pgcd[1].gd.label = &plabel[1];
3133     pgcd[1].gd.mnemonic = 'X';
3134     pgcd[1].gd.pos.x = 3; pgcd[1].gd.pos.y = pgcd[0].gd.pos.y+17;
3135     pgcd[1].gd.flags = gg_visible | gg_enabled;
3136     if ( doxnear ) pgcd[1].gd.flags |= gg_cb_on;
3137     pgcd[1].gd.popup_msg = _("Allows you to check that vertical stems in several\ncharacters start at the same location.");
3138     pgcd[1].gd.cid = CID_XNear;
3139     pgcd[1].creator = GCheckBoxCreate;
3140     pharray1[0] = &pgcd[1];
3141 
3142     sprintf(xnbuf,"%g",xval);
3143     plabel[2].text = (unichar_t *) xnbuf;
3144     plabel[2].text_is_1byte = true;
3145     pgcd[2].gd.label = &plabel[2];
3146     pgcd[2].gd.pos.x = 60; pgcd[2].gd.pos.y = pgcd[1].gd.pos.y-5; pgcd[2].gd.pos.width = 40;
3147     pgcd[2].gd.flags = gg_visible | gg_enabled;
3148     pgcd[2].gd.cid = CID_XNearVal;
3149     pgcd[2].gd.handle_controlevent = Prob_TextChanged;
3150     pgcd[2].data = (void *) CID_XNear;
3151     pgcd[2].creator = GTextFieldCreate;
3152     pharray1[1] = &pgcd[2]; pharray1[2] = GCD_Glue; pharray1[3] = NULL;
3153 
3154     pboxes[2].gd.flags = gg_enabled|gg_visible;
3155     pboxes[2].gd.u.boxelements = pharray1;
3156     pboxes[2].creator = GHBoxCreate;
3157     parray[1] = &pboxes[2];
3158 
3159     plabel[3].text = (unichar_t *) U_("_Y near¹");
3160     plabel[3].text_is_1byte = true;
3161     plabel[3].text_in_resource = true;
3162     pgcd[3].gd.label = &plabel[3];
3163     pgcd[3].gd.mnemonic = 'Y';
3164     pgcd[3].gd.pos.x = 3; pgcd[3].gd.pos.y = pgcd[1].gd.pos.y+24;
3165     pgcd[3].gd.flags = gg_visible | gg_enabled;
3166     if ( doynear ) pgcd[3].gd.flags |= gg_cb_on;
3167     pgcd[3].gd.popup_msg = _("Allows you to check that horizontal stems in several\ncharacters start at the same location.");
3168     pgcd[3].gd.cid = CID_YNear;
3169     pgcd[3].creator = GCheckBoxCreate;
3170     pharray2[0] = &pgcd[3];
3171 
3172     sprintf(ynbuf,"%g",yval);
3173     plabel[4].text = (unichar_t *) ynbuf;
3174     plabel[4].text_is_1byte = true;
3175     pgcd[4].gd.label = &plabel[4];
3176     pgcd[4].gd.pos.x = 60; pgcd[4].gd.pos.y = pgcd[3].gd.pos.y-5; pgcd[4].gd.pos.width = 40;
3177     pgcd[4].gd.flags = gg_visible | gg_enabled;
3178     pgcd[4].gd.cid = CID_YNearVal;
3179     pgcd[4].gd.handle_controlevent = Prob_TextChanged;
3180     pgcd[4].data = (void *) CID_YNear;
3181     pgcd[4].creator = GTextFieldCreate;
3182     pharray2[1] = &pgcd[4]; pharray2[2] = GCD_Glue; pharray2[3] = NULL;
3183 
3184     pboxes[3].gd.flags = gg_enabled|gg_visible;
3185     pboxes[3].gd.u.boxelements = pharray2;
3186     pboxes[3].creator = GHBoxCreate;
3187     parray[2] = &pboxes[3];
3188 
3189     plabel[5].text = (unichar_t *) U_("Y near¹ _standard heights");
3190     plabel[5].text_is_1byte = true;
3191     plabel[5].text_in_resource = true;
3192     pgcd[5].gd.label = &plabel[5];
3193     pgcd[5].gd.mnemonic = 'S';
3194     pgcd[5].gd.pos.x = 3; pgcd[5].gd.pos.y = pgcd[3].gd.pos.y+18;
3195     pgcd[5].gd.flags = gg_visible | gg_enabled;
3196     if ( doynearstd ) pgcd[5].gd.flags |= gg_cb_on;
3197     pgcd[5].gd.popup_msg = _("Allows you to find points which are slightly\noff from the baseline, xheight, cap height,\nascender, descender heights.");
3198     pgcd[5].gd.cid = CID_YNearStd;
3199     pgcd[5].creator = GCheckBoxCreate;
3200     parray[3] = &pgcd[5];
3201 
3202     plabel[6].text = (unichar_t *) (fv->b.sf->italicangle==0?_("_Control Points near horizontal/vertical"):_("Control Points near horizontal/vertical/italic"));
3203     plabel[6].text_is_1byte = true;
3204     plabel[6].text_in_resource = true;
3205     pgcd[6].gd.label = &plabel[6];
3206     pgcd[6].gd.mnemonic = 'C';
3207     pgcd[6].gd.pos.x = 3; pgcd[6].gd.pos.y = pgcd[5].gd.pos.y+14;
3208     pgcd[6].gd.flags = gg_visible | gg_enabled;
3209     if ( cpstd ) pgcd[6].gd.flags |= gg_cb_on;
3210     pgcd[6].gd.popup_msg = _("Allows you to find control points which are almost,\nbut not quite horizontal or vertical\nfrom their base point\n(or at the italic angle).");
3211     pgcd[6].gd.cid = CID_CpStd;
3212     pgcd[6].creator = GCheckBoxCreate;
3213     parray[4] = &pgcd[6];
3214 
3215     plabel[7].text = (unichar_t *) _("Control Points _beyond spline");
3216     plabel[7].text_is_1byte = true;
3217     plabel[7].text_in_resource = true;
3218     pgcd[7].gd.label = &plabel[7];
3219     pgcd[7].gd.mnemonic = 'b';
3220     pgcd[7].gd.pos.x = 3; pgcd[7].gd.pos.y = pgcd[6].gd.pos.y+14;
3221     pgcd[7].gd.flags = gg_visible | gg_enabled;
3222     if ( cpodd ) pgcd[7].gd.flags |= gg_cb_on;
3223     pgcd[7].gd.popup_msg = _("Allows you to find control points which when projected\nonto the line segment between the two end points lie\noutside of those end points");
3224     pgcd[7].gd.cid = CID_CpOdd;
3225     pgcd[7].creator = GCheckBoxCreate;
3226     parray[5] = &pgcd[7];
3227 
3228     plabel[8].text = (unichar_t *) _("Check for _irrelevant control points");
3229     plabel[8].text_is_1byte = true;
3230     plabel[8].text_in_resource = true;
3231     pgcd[8].gd.label = &plabel[8];
3232     pgcd[8].gd.pos.x = 3; pgcd[8].gd.pos.y = pgcd[7].gd.pos.y+14;
3233     pgcd[8].gd.flags = gg_visible | gg_enabled;
3234     if ( irrelevantcp ) pgcd[8].gd.flags |= gg_cb_on;
3235     pgcd[8].gd.popup_msg = _("Control points are irrelevant if they are too close to the main\npoint to make a significant difference in the shape of the curve.");
3236     pgcd[8].gd.cid = CID_IrrelevantCP;
3237     pgcd[8].creator = GCheckBoxCreate;
3238     parray[6] = &pgcd[8];
3239 
3240     plabel[9].text = (unichar_t *) _("Irrelevant _Factor:");
3241     plabel[9].text_is_1byte = true;
3242     plabel[9].text_in_resource = true;
3243     pgcd[9].gd.label = &plabel[9];
3244     pgcd[9].gd.pos.x = 20; pgcd[9].gd.pos.y = pgcd[8].gd.pos.y+17;
3245     pgcd[9].gd.flags = gg_visible | gg_enabled;
3246     pgcd[9].gd.popup_msg = _("A control point is deemed irrelevant if the distance between it and the main\n(end) point is less than this times the distance between the two end points");
3247     pgcd[9].creator = GLabelCreate;
3248     pharray3[0] = GCD_HPad10; pharray3[1] = &pgcd[9];
3249 
3250     sprintf( irrel, "%g", irrelevantfactor*100 );
3251     plabel[10].text = (unichar_t *) irrel;
3252     plabel[10].text_is_1byte = true;
3253     pgcd[10].gd.label = &plabel[10];
3254     pgcd[10].gd.pos.x = 105; pgcd[10].gd.pos.y = pgcd[9].gd.pos.y-3;
3255     pgcd[10].gd.pos.width = 50;
3256     pgcd[10].gd.flags = gg_visible | gg_enabled;
3257     pgcd[10].gd.popup_msg = _("A control point is deemed irrelevant if the distance between it and the main\n(end) point is less than this times the distance between the two end points");
3258     pgcd[10].gd.cid = CID_IrrelevantFactor;
3259     pgcd[10].creator = GTextFieldCreate;
3260     pharray3[2] = &pgcd[10];
3261 
3262     plabel[11].text = (unichar_t *) "%";
3263     plabel[11].text_is_1byte = true;
3264     pgcd[11].gd.label = &plabel[11];
3265     pgcd[11].gd.pos.x = 163; pgcd[11].gd.pos.y = pgcd[9].gd.pos.y;
3266     pgcd[11].gd.flags = gg_visible | gg_enabled;
3267     pgcd[11].gd.popup_msg = _("A control point is deemed irrelevant if the distance between it and the main\n(end) point is less than this times the distance between the two end points");
3268     pgcd[11].creator = GLabelCreate;
3269     pharray3[3] = &pgcd[11]; pharray3[4] = GCD_Glue; pharray3[5] = NULL;
3270 
3271     pboxes[4].gd.flags = gg_enabled|gg_visible;
3272     pboxes[4].gd.u.boxelements = pharray3;
3273     pboxes[4].creator = GHBoxCreate;
3274     parray[7] = &pboxes[4];
3275 
3276     plabel[12].text = (unichar_t *) _("Poin_ts too close");
3277     plabel[12].text_is_1byte = true;
3278     plabel[12].text_in_resource = true;
3279     pgcd[12].gd.label = &plabel[12];
3280     pgcd[12].gd.pos.x = 3; pgcd[12].gd.pos.y = pgcd[11].gd.pos.y+14;
3281     pgcd[12].gd.flags = gg_visible | gg_enabled;
3282     if ( pointstooclose ) pgcd[12].gd.flags |= gg_cb_on;
3283     pgcd[12].gd.popup_msg = _("If two adjacent points on the same path are less than a few\nemunits apart they will cause problems for some of FontForge's\ncommands. PostScript shouldn't care though.");
3284     pgcd[12].gd.cid = CID_PointsTooClose;
3285     pgcd[12].creator = GCheckBoxCreate;
3286     parray[8] = &pgcd[12];
3287 
3288     plabel[13].text = (unichar_t *) _("_Points too far");
3289     plabel[13].text_is_1byte = true;
3290     plabel[13].text_in_resource = true;
3291     pgcd[13].gd.label = &plabel[13];
3292     pgcd[13].gd.pos.x = 3; pgcd[13].gd.pos.y = pgcd[12].gd.pos.y+14;
3293     pgcd[13].gd.flags = gg_visible | gg_enabled;
3294     if ( pointstoofar ) pgcd[13].gd.flags |= gg_cb_on;
3295     pgcd[13].gd.popup_msg = _("Most font formats cannot specify adjacent points (or control points)\nwhich are more than 32767 em-units apart in either the x or y direction");
3296     pgcd[13].gd.cid = CID_PointsTooFar;
3297     pgcd[13].creator = GCheckBoxCreate;
3298     parray[9] = &pgcd[13]; parray[10] = GCD_Glue; parray[11] = NULL;
3299 
3300     pboxes[0].gd.flags = gg_enabled|gg_visible;
3301     pboxes[0].gd.u.boxelements = parray;
3302     pboxes[0].creator = GVBoxCreate;
3303 
3304 /* ************************************************************************** */
3305 
3306     memset(&palabel,0,sizeof(palabel));
3307     memset(&pagcd,0,sizeof(pagcd));
3308     memset(&paboxes,0,sizeof(paboxes));
3309 
3310     palabel[0].text = (unichar_t *) _("O_pen Paths");
3311     palabel[0].text_is_1byte = true;
3312     palabel[0].text_in_resource = true;
3313     pagcd[0].gd.label = &palabel[0];
3314     pagcd[0].gd.mnemonic = 'P';
3315     pagcd[0].gd.pos.x = 3; pagcd[0].gd.pos.y = 6;
3316     pagcd[0].gd.flags = gg_visible | gg_enabled;
3317     if ( openpaths ) pagcd[0].gd.flags |= gg_cb_on;
3318     pagcd[0].gd.popup_msg = _("All paths should be closed loops, there should be no exposed endpoints");
3319     pagcd[0].gd.cid = CID_OpenPaths;
3320     pagcd[0].creator = GCheckBoxCreate;
3321     paarray[0] = &pagcd[0];
3322 
3323     palabel[1].text = (unichar_t *) _("Intersecting Paths");
3324     palabel[1].text_is_1byte = true;
3325     pagcd[1].gd.label = &palabel[1];
3326     pagcd[1].gd.mnemonic = 'E';
3327     pagcd[1].gd.pos.x = 3; pagcd[1].gd.pos.y = pagcd[0].gd.pos.y+17;
3328     pagcd[1].gd.flags = gg_visible | gg_enabled;
3329     if ( intersectingpaths ) pagcd[1].gd.flags |= gg_cb_on;
3330     pagcd[1].gd.popup_msg = _("No paths with within a glyph should intersect");
3331     pagcd[1].gd.cid = CID_IntersectingPaths;
3332     pagcd[1].creator = GCheckBoxCreate;
3333     paarray[1] = &pagcd[1];
3334 
3335     palabel[2].text = (unichar_t *) (fv->b.sf->italicangle==0?_("_Edges near horizontal/vertical"):_("Edges near horizontal/vertical/italic"));
3336     palabel[2].text_is_1byte = true;
3337     palabel[2].text_in_resource = true;
3338     pagcd[2].gd.label = &palabel[2];
3339     pagcd[2].gd.mnemonic = 'E';
3340     pagcd[2].gd.pos.x = 3; pagcd[2].gd.pos.y = pagcd[1].gd.pos.y+17;
3341     pagcd[2].gd.flags = gg_visible | gg_enabled;
3342     if ( linestd ) pagcd[2].gd.flags |= gg_cb_on;
3343     pagcd[2].gd.popup_msg = _("Allows you to find lines which are almost,\nbut not quite horizontal or vertical\n(or at the italic angle).");
3344     pagcd[2].gd.cid = CID_LineStd;
3345     pagcd[2].creator = GCheckBoxCreate;
3346     paarray[2] = &pagcd[2];
3347 
3348     palabel[3].text = (unichar_t *) _("Check _outermost paths clockwise");
3349     palabel[3].text_is_1byte = true;
3350     palabel[3].text_in_resource = true;
3351     pagcd[3].gd.label = &palabel[3];
3352     pagcd[3].gd.mnemonic = 'S';
3353     pagcd[3].gd.pos.x = 3; pagcd[3].gd.pos.y = pagcd[2].gd.pos.y+17;
3354     pagcd[3].gd.flags = gg_visible | gg_enabled;
3355     if ( direction ) pagcd[3].gd.flags |= gg_cb_on;
3356     pagcd[3].gd.popup_msg = _("FontForge internally uses paths drawn in a\nclockwise direction. This lets you check that they are.\nBefore doing this test insure that\nno paths self-intersect.");
3357     pagcd[3].gd.cid = CID_Direction;
3358     pagcd[3].creator = GCheckBoxCreate;
3359     paarray[3] = &pagcd[3];
3360 
3361     palabel[4].text = (unichar_t *) _("Check _missing extrema");
3362     palabel[4].text_is_1byte = true;
3363     palabel[4].text_in_resource = true;
3364     pagcd[4].gd.label = &palabel[4];
3365     pagcd[4].gd.pos.x = 3; pagcd[4].gd.pos.y = pagcd[3].gd.pos.y+17;
3366     pagcd[4].gd.flags = gg_visible | gg_enabled;
3367     if ( missingextrema ) pagcd[4].gd.flags |= gg_cb_on;
3368     pagcd[4].gd.popup_msg = _("PostScript and TrueType require that when a path\nreaches its maximum or minimum position\nthere must be a point at that location.");
3369     pagcd[4].gd.cid = CID_MissingExtrema;
3370     pagcd[4].creator = GCheckBoxCreate;
3371     paarray[4] = &pagcd[4];
3372 
3373     palabel[5].text = (unichar_t *) _("_More points than:");
3374     palabel[5].text_is_1byte = true;
3375     palabel[5].text_in_resource = true;
3376     pagcd[5].gd.label = &palabel[5];
3377     pagcd[5].gd.mnemonic = 'r';
3378     pagcd[5].gd.pos.x = 3; pagcd[5].gd.pos.y = pagcd[4].gd.pos.y+21;
3379     pagcd[5].gd.flags = gg_visible | gg_enabled;
3380     if ( toomanypoints ) pagcd[5].gd.flags |= gg_cb_on;
3381     pagcd[5].gd.popup_msg = _("The PostScript Language Reference Manual (Appendix B) says that\nan interpreter need not support paths with more than 1500 points.\nI think this count includes control points. From PostScript's point\nof view, all the contours in a character make up one path. Modern\ninterpreters tend to support paths with more points than this limit.\n(Note a truetype font after conversion to PS will contain\ntwice as many control points)");
3382     pagcd[5].gd.cid = CID_TooManyPoints;
3383     pagcd[5].creator = GCheckBoxCreate;
3384     paharray[0] = &pagcd[5];
3385 
3386     sprintf( pmax, "%d", pointsmax );
3387     palabel[6].text = (unichar_t *) pmax;
3388     palabel[6].text_is_1byte = true;
3389     pagcd[6].gd.label = &palabel[6];
3390     pagcd[6].gd.pos.x = 105; pagcd[6].gd.pos.y = pagcd[5].gd.pos.y-3;
3391     pagcd[6].gd.pos.width = 50;
3392     pagcd[6].gd.flags = gg_visible | gg_enabled;
3393     pagcd[6].gd.popup_msg = _("The PostScript Language Reference Manual (Appendix B) says that\nan interpreter need not support paths with more than 1500 points.\nI think this count includes control points. From PostScript's point\nof view, all the contours in a character make up one path. Modern\ninterpreters tend to support paths with more points than this limit.\n(Note a truetype font after conversion to PS will contain\ntwice as many control points)");
3394     pagcd[6].gd.cid = CID_PointsMax;
3395     pagcd[6].creator = GTextFieldCreate;
3396     paharray[1] = &pagcd[6]; paharray[2] = GCD_Glue; paharray[3] = NULL;
3397 
3398     paboxes[2].gd.flags = gg_enabled|gg_visible;
3399     paboxes[2].gd.u.boxelements = paharray;
3400     paboxes[2].creator = GHBoxCreate;
3401     paarray[5] = &paboxes[2]; paarray[6] = GCD_Glue; paarray[7] = NULL;
3402 
3403     paboxes[0].gd.flags = gg_enabled|gg_visible;
3404     paboxes[0].gd.u.boxelements = paarray;
3405     paboxes[0].creator = GVBoxCreate;
3406 
3407 /* ************************************************************************** */
3408 
3409     memset(&rflabel,0,sizeof(rflabel));
3410     memset(&rfgcd,0,sizeof(rfgcd));
3411     memset(&rfboxes,0,sizeof(rfboxes));
3412 
3413     rflabel[0].text = (unichar_t *) _("Check _flipped references");
3414     rflabel[0].text_is_1byte = true;
3415     rflabel[0].text_in_resource = true;
3416     rfgcd[0].gd.label = &rflabel[0];
3417     rfgcd[0].gd.mnemonic = 'r';
3418     rfgcd[0].gd.pos.x = 3; rfgcd[0].gd.pos.y = 6;
3419     rfgcd[0].gd.flags = gg_visible | gg_enabled;
3420     if ( flippedrefs ) rfgcd[0].gd.flags |= gg_cb_on;
3421     rfgcd[0].gd.popup_msg = _("PostScript and TrueType require that paths be drawn\nin a clockwise direction. If you have a reference\nthat has been flipped then the paths in that reference will\nprobably be counter-clockwise. You should unlink it and do\nElement->Correct direction on it.");
3422     rfgcd[0].gd.cid = CID_FlippedRefs;
3423     rfgcd[0].creator = GCheckBoxCreate;
3424     rfarray[0] = &rfgcd[0];
3425 
3426 /* GT: Refs is an abbreviation for References. Space is somewhat constrained here */
3427     rflabel[1].text = (unichar_t *) _("Refs with bad tt transformation matrices");
3428     rflabel[1].text_is_1byte = true;
3429     rflabel[1].text_in_resource = true;
3430     rfgcd[1].gd.label = &rflabel[1];
3431     rfgcd[1].gd.mnemonic = 'r';
3432     rfgcd[1].gd.pos.x = 3; rfgcd[1].gd.pos.y = rfgcd[0].gd.pos.y+17;
3433     rfgcd[1].gd.flags = gg_visible | gg_enabled;
3434     if ( refsbadtransformttf ) rfgcd[1].gd.flags |= gg_cb_on;
3435     rfgcd[1].gd.popup_msg = _("TrueType requires that all scaling and rotational\nentries in a transformation matrix be between -2 and 2");
3436     rfgcd[1].gd.cid = CID_RefBadTransformTTF;
3437     rfgcd[1].creator = GCheckBoxCreate;
3438     rfarray[1] = &rfgcd[1];
3439 
3440     rflabel[2].text = (unichar_t *) _("Mixed contours and references");
3441     rflabel[2].text_is_1byte = true;
3442     rflabel[2].text_in_resource = true;
3443     rfgcd[2].gd.label = &rflabel[2];
3444     rfgcd[2].gd.mnemonic = 'r';
3445     rfgcd[2].gd.pos.x = 3; rfgcd[2].gd.pos.y = rfgcd[1].gd.pos.y+17;
3446     rfgcd[2].gd.flags = gg_visible | gg_enabled;
3447     if ( mixedcontoursrefs ) rfgcd[2].gd.flags |= gg_cb_on;
3448     rfgcd[2].gd.popup_msg = _("TrueType glyphs can either contain references or contours.\nNot both.");
3449     rfgcd[2].gd.cid = CID_MixedContoursRefs;
3450     rfgcd[2].creator = GCheckBoxCreate;
3451     rfarray[2] = &rfgcd[2];
3452 
3453 /* GT: Refs is an abbreviation for References. Space is somewhat constrained here */
3454     rflabel[3].text = (unichar_t *) _("Refs with bad ps transformation matrices");
3455     rflabel[3].text_is_1byte = true;
3456     rflabel[3].text_in_resource = true;
3457     rfgcd[3].gd.label = &rflabel[3];
3458     rfgcd[3].gd.mnemonic = 'r';
3459     rfgcd[3].gd.pos.x = 3; rfgcd[3].gd.pos.y = rfgcd[2].gd.pos.y+17;
3460     rfgcd[3].gd.flags = gg_visible | gg_enabled;
3461     if ( refsbadtransformps ) rfgcd[3].gd.flags |= gg_cb_on;
3462     rfgcd[3].gd.popup_msg = _("Type1 and 2 fonts only support translation of references.\nThe first four entries of the transformation matrix should be\n[1 0 0 1].");
3463     rfgcd[3].gd.cid = CID_RefBadTransformPS;
3464     rfgcd[3].creator = GCheckBoxCreate;
3465     rfarray[3] = &rfgcd[3];
3466 
3467 /* GT: Refs is an abbreviation for References. Space is somewhat constrained here */
3468     rflabel[4].text = (unichar_t *) _("Refs neste_d deeper than:");
3469     rflabel[4].text_is_1byte = true;
3470     rflabel[4].text_in_resource = true;
3471     rfgcd[4].gd.label = &rflabel[4];
3472     rfgcd[4].gd.mnemonic = 'r';
3473     rfgcd[4].gd.pos.x = 3; rfgcd[4].gd.pos.y = rfgcd[3].gd.pos.y+21;
3474     rfgcd[4].gd.flags = gg_visible | gg_enabled;
3475     if ( toodeeprefs ) rfgcd[4].gd.flags |= gg_cb_on;
3476     rfgcd[4].gd.popup_msg = _("The Type 2 Charstring Reference (Appendix B) says that\nsubroutines may not be nested more than 10 deep. Each\nnesting level for references requires one subroutine\nlevel, and hints may require another level.");
3477     rfgcd[4].gd.cid = CID_TooDeepRefs;
3478     rfgcd[4].creator = GCheckBoxCreate;
3479     rfharray[0] = &rfgcd[4];
3480 
3481     sprintf( rmax, "%d", refdepthmax );
3482     rflabel[5].text = (unichar_t *) rmax;
3483     rflabel[5].text_is_1byte = true;
3484     rfgcd[5].gd.label = &rflabel[5];
3485     rfgcd[5].gd.pos.x = 140; rfgcd[5].gd.pos.y = rfgcd[4].gd.pos.y-3;
3486     rfgcd[5].gd.pos.width = 40;
3487     rfgcd[5].gd.flags = gg_visible | gg_enabled;
3488     rfgcd[5].gd.popup_msg = _("The Type 2 Charstring Reference (Appendix B) says that\nsubroutines may not be nested more than 10 deep. Each\nnesting level for references requires one subroutine\nlevel, and hints may require another level.");
3489     rfgcd[5].gd.cid = CID_RefDepthMax;
3490     rfgcd[5].creator = GTextFieldCreate;
3491     rfharray[1] = &rfgcd[5]; rfharray[2] = GCD_Glue; rfharray[3] = NULL;
3492 
3493     rfboxes[2].gd.flags = gg_enabled|gg_visible;
3494     rfboxes[2].gd.u.boxelements = rfharray;
3495     rfboxes[2].creator = GHBoxCreate;
3496     rfarray[4] = &rfboxes[2];
3497 
3498     rflabel[6].text = (unichar_t *) _("Refs with out of date point matching");
3499     rflabel[6].text_is_1byte = true;
3500     rflabel[6].text_in_resource = true;
3501     rfgcd[6].gd.label = &rflabel[6];
3502     rfgcd[6].gd.mnemonic = 'r';
3503     rfgcd[6].gd.pos.x = 3; rfgcd[6].gd.pos.y = rfgcd[5].gd.pos.y+24;
3504     rfgcd[6].gd.flags = gg_visible | gg_enabled;
3505     if ( ptmatchrefsoutofdate ) rfgcd[6].gd.flags |= gg_cb_on;
3506     rfgcd[6].gd.popup_msg = _("If a glyph has been edited so that it has a different\nnumber of points now, then any references\nwhich use point matching and depended on that glyph's\npoint count will be incorrect.");
3507     rfgcd[6].gd.cid = CID_PtMatchRefsOutOfDate;
3508     rfgcd[6].creator = GCheckBoxCreate;
3509     rfarray[5] = &rfgcd[6];
3510 
3511     rflabel[7].text = (unichar_t *) _("Multiple refs with use-my-metrics");
3512     rflabel[7].text_is_1byte = true;
3513     rflabel[7].text_in_resource = true;
3514     rfgcd[7].gd.label = &rflabel[7];
3515     rfgcd[7].gd.mnemonic = 'r';
3516     rfgcd[7].gd.flags = gg_visible | gg_enabled;
3517     if ( multusemymetrics ) rfgcd[7].gd.flags |= gg_cb_on;
3518     rfgcd[7].gd.popup_msg = _("There may be at most one reference with the use-my-metrics bit set");
3519     rfgcd[7].gd.cid = CID_MultUseMyMetrics;
3520     rfgcd[7].creator = GCheckBoxCreate;
3521     rfarray[6] = &rfgcd[7]; rfarray[7] = GCD_Glue; rfarray[8] = NULL;
3522 
3523     rfboxes[0].gd.flags = gg_enabled|gg_visible;
3524     rfboxes[0].gd.u.boxelements = rfarray;
3525     rfboxes[0].creator = GVBoxCreate;
3526 
3527 /* ************************************************************************** */
3528 
3529     memset(&hlabel,0,sizeof(hlabel));
3530     memset(&hgcd,0,sizeof(hgcd));
3531     memset(&hboxes,0,sizeof(hboxes));
3532 
3533     hlabel[0].text = (unichar_t *) _("_Hints controlling no points");
3534     hlabel[0].text_is_1byte = true;
3535     hlabel[0].text_in_resource = true;
3536     hgcd[0].gd.label = &hlabel[0];
3537     hgcd[0].gd.mnemonic = 'H';
3538     hgcd[0].gd.pos.x = 3; hgcd[0].gd.pos.y = 5;
3539     hgcd[0].gd.flags = gg_visible | gg_enabled;
3540     if ( hintnopt ) hgcd[0].gd.flags |= gg_cb_on;
3541     hgcd[0].gd.popup_msg = _("Ghostview (perhaps other interpreters) has a problem when a\nhint exists without any points that lie on it.");
3542     hgcd[0].gd.cid = CID_HintNoPt;
3543     hgcd[0].creator = GCheckBoxCreate;
3544     harray[0] = &hgcd[0];
3545 
3546     hlabel[1].text = (unichar_t *) U_("_Points near¹ hint edges");
3547     hlabel[1].text_is_1byte = true;
3548     hlabel[1].text_in_resource = true;
3549     hgcd[1].gd.label = &hlabel[1];
3550     hgcd[1].gd.mnemonic = 'H';
3551     hgcd[1].gd.pos.x = 3; hgcd[1].gd.pos.y = hgcd[0].gd.pos.y+17;
3552     hgcd[1].gd.flags = gg_visible | gg_enabled;
3553     if ( ptnearhint ) hgcd[1].gd.flags |= gg_cb_on;
3554     hgcd[1].gd.popup_msg = _("Often if a point is slightly off from a hint\nit is because a stem is made up\nof several segments, and one of them\nhas the wrong width.");
3555     hgcd[1].gd.cid = CID_PtNearHint;
3556     hgcd[1].creator = GCheckBoxCreate;
3557     harray[1] = &hgcd[1];
3558 
3559     hlabel[2].text = (unichar_t *) U_("Hint _Width Near¹");
3560     hlabel[2].text_is_1byte = true;
3561     hlabel[2].text_in_resource = true;
3562     hgcd[2].gd.label = &hlabel[2];
3563     hgcd[2].gd.mnemonic = 'W';
3564     hgcd[2].gd.pos.x = 3; hgcd[2].gd.pos.y = hgcd[1].gd.pos.y+21;
3565     hgcd[2].gd.flags = gg_visible | gg_enabled;
3566     if ( hintwidth ) hgcd[2].gd.flags |= gg_cb_on;
3567     hgcd[2].gd.popup_msg = _("Allows you to check that stems have consistent widths..");
3568     hgcd[2].gd.cid = CID_HintWidthNear;
3569     hgcd[2].creator = GCheckBoxCreate;
3570     hharray1[0] = &hgcd[2];
3571 
3572     sprintf(widthbuf,"%g",widthval);
3573     hlabel[3].text = (unichar_t *) widthbuf;
3574     hlabel[3].text_is_1byte = true;
3575     hgcd[3].gd.label = &hlabel[3];
3576     hgcd[3].gd.pos.x = 100+5; hgcd[3].gd.pos.y = hgcd[2].gd.pos.y-1; hgcd[3].gd.pos.width = 40;
3577     hgcd[3].gd.flags = gg_visible | gg_enabled;
3578     hgcd[3].gd.cid = CID_HintWidth;
3579     hgcd[3].gd.handle_controlevent = Prob_TextChanged;
3580     hgcd[3].data = (void *) CID_HintWidthNear;
3581     hgcd[3].creator = GTextFieldCreate;
3582     hharray1[1] = &hgcd[3]; hharray1[2] = GCD_Glue; hharray1[3] = NULL;
3583 
3584     hboxes[2].gd.flags = gg_enabled|gg_visible;
3585     hboxes[2].gd.u.boxelements = hharray1;
3586     hboxes[2].creator = GHBoxCreate;
3587     harray[2] = &hboxes[2];
3588 
3589 /* GT: The _3 is used to mark an accelerator */
3590     hlabel[4].text = (unichar_t *) _("Almost stem_3 hint");
3591     hlabel[4].text_is_1byte = true;
3592     hlabel[4].text_in_resource = true;
3593     hgcd[4].gd.label = &hlabel[4];
3594     hgcd[4].gd.mnemonic = '3';
3595     hgcd[4].gd.pos.x = 3; hgcd[4].gd.pos.y = hgcd[3].gd.pos.y+19;
3596     hgcd[4].gd.flags = gg_visible | gg_enabled;
3597     if ( stem3 ) hgcd[4].gd.flags |= gg_cb_on;
3598     hgcd[4].gd.popup_msg = _("This checks if the character almost, but not exactly,\nconforms to the requirements for a stem3 hint.\nThat is, either vertically or horizontally, there must\nbe exactly three hints, and they must have the same\nwidth and they must be evenly spaced.");
3599     hgcd[4].gd.cid = CID_Stem3;
3600     hgcd[4].gd.handle_controlevent = Prob_EnableExact;
3601     hgcd[4].creator = GCheckBoxCreate;
3602     harray[3] = &hgcd[4];
3603 
3604     hlabel[5].text = (unichar_t *) _("_Show Exact *stem3");
3605     hlabel[5].text_is_1byte = true;
3606     hlabel[5].text_in_resource = true;
3607     hgcd[5].gd.label = &hlabel[5];
3608     hgcd[5].gd.mnemonic = 'S';
3609     hgcd[5].gd.pos.x = hgcd[4].gd.pos.x+5; hgcd[5].gd.pos.y = hgcd[4].gd.pos.y+17;
3610     hgcd[5].gd.flags = gg_visible;
3611     if ( showexactstem3 ) hgcd[5].gd.flags |= gg_cb_on;
3612     if ( stem3 ) hgcd[5].gd.flags |= gg_enabled;
3613     hgcd[5].gd.popup_msg = _("Shows when this character is exactly a stem3 hint");
3614     hgcd[5].gd.cid = CID_ShowExactStem3;
3615     hgcd[5].creator = GCheckBoxCreate;
3616     hharray2[0] = GCD_HPad10; hharray2[1] = &hgcd[5]; hharray2[2] = GCD_Glue; hharray2[3] = NULL;
3617 
3618     hboxes[3].gd.flags = gg_enabled|gg_visible;
3619     hboxes[3].gd.u.boxelements = hharray2;
3620     hboxes[3].creator = GHBoxCreate;
3621     harray[4] = &hboxes[3];
3622 
3623     hlabel[6].text = (unichar_t *) _("_More hints than:");
3624     hlabel[6].text_is_1byte = true;
3625     hlabel[6].text_in_resource = true;
3626     hgcd[6].gd.label = &hlabel[6];
3627     hgcd[6].gd.pos.x = 3; hgcd[6].gd.pos.y = hgcd[5].gd.pos.y+21;
3628     hgcd[6].gd.flags = gg_visible | gg_enabled;
3629     if ( toomanyhints ) hgcd[6].gd.flags |= gg_cb_on;
3630     hgcd[6].gd.popup_msg = _("The Type 2 Charstring Reference (Appendix B) says that\nthere may be at most 96 horizontal and vertical stem hints\nin a character.");
3631     hgcd[6].gd.cid = CID_TooManyHints;
3632     hgcd[6].creator = GCheckBoxCreate;
3633     hharray3[0] = &hgcd[6];
3634 
3635     sprintf( hmax, "%d", hintsmax );
3636     hlabel[7].text = (unichar_t *) hmax;
3637     hlabel[7].text_is_1byte = true;
3638     hgcd[7].gd.label = &hlabel[7];
3639     hgcd[7].gd.pos.x = 105; hgcd[7].gd.pos.y = hgcd[6].gd.pos.y-3;
3640     hgcd[7].gd.pos.width = 50;
3641     hgcd[7].gd.flags = gg_visible | gg_enabled;
3642     hgcd[7].gd.popup_msg = _("The Type 2 Charstring Reference (Appendix B) says that\nthere may be at most 96 horizontal and vertical stem hints\nin a character.");
3643     hgcd[7].gd.cid = CID_HintsMax;
3644     hgcd[7].creator = GTextFieldCreate;
3645     hharray3[1] = &hgcd[7]; hharray3[2] = GCD_Glue; hharray3[3] = NULL;
3646 
3647     hboxes[4].gd.flags = gg_enabled|gg_visible;
3648     hboxes[4].gd.u.boxelements = hharray3;
3649     hboxes[4].creator = GHBoxCreate;
3650     harray[5] = &hboxes[4];
3651 
3652     hlabel[8].text = (unichar_t *) _("_Overlapped hints");
3653     hlabel[8].text_is_1byte = true;
3654     hlabel[8].text_in_resource = true;
3655     hgcd[8].gd.label = &hlabel[8];
3656     hgcd[8].gd.flags = gg_visible | gg_enabled;
3657     if ( overlappedhints ) hgcd[8].gd.flags |= gg_cb_on;
3658     hgcd[8].gd.popup_msg = _("Either a glyph should have no overlapping hints,\nor a glyph with hint masks should have no overlapping\nhints within a hint mask.");
3659     hgcd[8].gd.cid = CID_OverlappedHints;
3660     hgcd[8].creator = GCheckBoxCreate;
3661     harray[6] = &hgcd[8];
3662 
3663     harray[7] = GCD_Glue; harray[8] = NULL;
3664 
3665     hboxes[0].gd.flags = gg_enabled|gg_visible;
3666     hboxes[0].gd.u.boxelements = harray;
3667     hboxes[0].creator = GVBoxCreate;
3668 
3669 /* ************************************************************************** */
3670 
3671     memset(&rlabel,0,sizeof(rlabel));
3672     memset(&rgcd,0,sizeof(rgcd));
3673     memset(&rboxes,0,sizeof(rboxes));
3674 
3675     rlabel[0].text = (unichar_t *) _("Check missing _bitmaps");
3676     rlabel[0].text_is_1byte = true;
3677     rlabel[0].text_in_resource = true;
3678     rgcd[0].gd.label = &rlabel[0];
3679     rgcd[0].gd.mnemonic = 'r';
3680     rgcd[0].gd.pos.x = 3; rgcd[0].gd.pos.y = 6;
3681     rgcd[0].gd.flags = gg_visible | gg_enabled;
3682     if ( bitmaps ) rgcd[0].gd.flags |= gg_cb_on;
3683     rgcd[0].gd.popup_msg = _("Are there any outline characters which don't have a bitmap version in one of the bitmap fonts?\nConversely are there any bitmap characters without a corresponding outline character?");
3684     rgcd[0].gd.cid = CID_Bitmaps;
3685     rgcd[0].creator = GCheckBoxCreate;
3686 
3687     rlabel[1].text = (unichar_t *) _("Bitmap/outline _advance mismatch");
3688     rlabel[1].text_is_1byte = true;
3689     rlabel[1].text_in_resource = true;
3690     rgcd[1].gd.label = &rlabel[1];
3691     rgcd[1].gd.mnemonic = 'r';
3692     rgcd[1].gd.pos.x = 3; rgcd[1].gd.pos.y = 6;
3693     rgcd[1].gd.flags = gg_visible | gg_enabled;
3694     if ( bitmapwidths ) rgcd[1].gd.flags |= gg_cb_on;
3695     rgcd[1].gd.popup_msg = _("Are there any bitmap glyphs whose advance width\nis not is expected from scaling and rounding\nthe outline's advance width?");
3696     rgcd[1].gd.cid = CID_BitmapWidths;
3697     rgcd[1].creator = GCheckBoxCreate;
3698 
3699     rlabel[2].text = (unichar_t *) _("Check multiple Unicode");
3700     rlabel[2].text_is_1byte = true;
3701     rgcd[2].gd.label = &rlabel[2];
3702     rgcd[2].gd.pos.x = 3; rgcd[2].gd.pos.y = rgcd[1].gd.pos.y+15;
3703     rgcd[2].gd.flags = gg_visible | gg_enabled;
3704     if ( multuni ) rgcd[2].gd.flags |= gg_cb_on;
3705     rgcd[2].gd.popup_msg = _("Check multiple Unicode");
3706     rgcd[2].gd.cid = CID_MultUni;
3707     rgcd[2].creator = GCheckBoxCreate;
3708 
3709     rlabel[3].text = (unichar_t *) _("Check multiple Names");
3710     rlabel[3].text_is_1byte = true;
3711     rgcd[3].gd.label = &rlabel[3];
3712     rgcd[3].gd.pos.x = 3; rgcd[3].gd.pos.y = rgcd[2].gd.pos.y+15;
3713     rgcd[3].gd.flags = gg_visible | gg_enabled;
3714     if ( multname ) rgcd[3].gd.flags |= gg_cb_on;
3715     rgcd[3].gd.popup_msg = _("Check for multiple characters with the same name");
3716     rgcd[3].gd.cid = CID_MultName;
3717     rgcd[3].creator = GCheckBoxCreate;
3718 
3719     rlabel[4].text = (unichar_t *) _("Check Unicode/Name mismatch");
3720     rlabel[4].text_is_1byte = true;
3721     rgcd[4].gd.label = &rlabel[4];
3722     rgcd[4].gd.pos.x = 3; rgcd[4].gd.pos.y = rgcd[3].gd.pos.y+15;
3723     rgcd[4].gd.flags = gg_visible | gg_enabled;
3724     if ( uninamemismatch ) rgcd[4].gd.flags |= gg_cb_on;
3725     rgcd[4].gd.popup_msg = _("Check for characters whose name maps to a unicode code point\nwhich does not map the character's assigned code point.");
3726     rgcd[4].gd.cid = CID_UniNameMisMatch;
3727     rgcd[4].creator = GCheckBoxCreate;
3728 
3729     for ( i=0; i<=4; ++i )
3730 	rarray[i] = &rgcd[i];
3731     rarray[i++] = GCD_Glue; rarray[i++] = NULL;
3732 
3733     rboxes[0].gd.flags = gg_enabled|gg_visible;
3734     rboxes[0].gd.u.boxelements = rarray;
3735     rboxes[0].creator = GVBoxCreate;
3736 
3737 /* ************************************************************************** */
3738 
3739     memset(&bblabel,0,sizeof(bblabel));
3740     memset(&bbgcd,0,sizeof(bbgcd));
3741     memset(&bbboxes,0,sizeof(bbboxes));
3742 
3743     bblabel[0].text = (unichar_t *) _("Glyph BB Above");
3744     bblabel[0].text_is_1byte = true;
3745     bblabel[0].text_in_resource = true;
3746     bbgcd[0].gd.label = &bblabel[0];
3747     bbgcd[0].gd.mnemonic = 'r';
3748     bbgcd[0].gd.pos.x = 3; bbgcd[0].gd.pos.y = 6;
3749     bbgcd[0].gd.flags = gg_visible | gg_enabled;
3750     if ( bbymax ) bbgcd[0].gd.flags |= gg_cb_on;
3751     bbgcd[0].gd.popup_msg = _("Are there any glyph's whose bounding boxes extend above this number?");
3752     bbgcd[0].gd.cid = CID_BBYMax;
3753     bbgcd[0].creator = GCheckBoxCreate;
3754 
3755     sf = p.fv->b.sf;
3756     if ( lastsf!=sf ) {
3757 	bbymax_val = bbymin_val = bbxmax_val /* = bbxmin_val */= vadvancewidth = advancewidth = 0;
3758     }
3759 
3760     sprintf(yymaxbuf,"%g", bbymax_val!=0 ? bbymax_val : sf->ascent);
3761     bblabel[1].text = (unichar_t *) yymaxbuf;
3762     bblabel[1].text_is_1byte = true;
3763     bbgcd[1].gd.label = &bblabel[1];
3764     bbgcd[1].gd.pos.x = 100+15; bbgcd[1].gd.pos.y = bbgcd[0].gd.pos.y-1; bbgcd[1].gd.pos.width = 40;
3765     bbgcd[1].gd.flags = gg_visible | gg_enabled;
3766     bbgcd[1].gd.cid = CID_BBYMaxVal;
3767     bbgcd[1].gd.handle_controlevent = Prob_TextChanged;
3768     bbgcd[1].data = (void *) CID_BBYMax;
3769     bbgcd[1].creator = GTextFieldCreate;
3770     bbarray[0][0] = &bbgcd[0]; bbarray[0][1] = &bbgcd[1]; bbarray[0][2] = GCD_Glue; bbarray[0][3] = NULL;
3771 
3772     bblabel[2].text = (unichar_t *) _("Glyph BB Below");
3773     bblabel[2].text_is_1byte = true;
3774     bblabel[2].text_in_resource = true;
3775     bbgcd[2].gd.label = &bblabel[2];
3776     bbgcd[2].gd.mnemonic = 'r';
3777     bbgcd[2].gd.pos.x = 3; bbgcd[2].gd.pos.y = bbgcd[0].gd.pos.y+21;
3778     bbgcd[2].gd.flags = gg_visible | gg_enabled;
3779     if ( bbymin ) bbgcd[2].gd.flags |= gg_cb_on;
3780     bbgcd[2].gd.popup_msg = _("Are there any glyph's whose bounding boxes extend below this number?");
3781     bbgcd[2].gd.cid = CID_BBYMin;
3782     bbgcd[2].creator = GCheckBoxCreate;
3783 
3784     sprintf(yyminbuf,"%g", bbymin_val!=0 ? bbymin_val : -sf->descent);
3785     bblabel[3].text = (unichar_t *) yyminbuf;
3786     bblabel[3].text_is_1byte = true;
3787     bbgcd[3].gd.label = &bblabel[3];
3788     bbgcd[3].gd.pos.x = 100+15; bbgcd[3].gd.pos.y = bbgcd[2].gd.pos.y-1; bbgcd[3].gd.pos.width = 40;
3789     bbgcd[3].gd.flags = gg_visible | gg_enabled;
3790     bbgcd[3].gd.cid = CID_BBYMinVal;
3791     bbgcd[3].gd.handle_controlevent = Prob_TextChanged;
3792     bbgcd[3].data = (void *) CID_BBYMin;
3793     bbgcd[3].creator = GTextFieldCreate;
3794     bbarray[1][0] = &bbgcd[2]; bbarray[1][1] = &bbgcd[3]; bbarray[1][2] = GCD_Glue; bbarray[1][3] = NULL;
3795 
3796     bblabel[4].text = (unichar_t *) _("Glyph BB Right Of");
3797     bblabel[4].text_is_1byte = true;
3798     bblabel[4].text_in_resource = true;
3799     bbgcd[4].gd.label = &bblabel[4];
3800     bbgcd[4].gd.pos.x = 3; bbgcd[4].gd.pos.y = bbgcd[2].gd.pos.y+21;
3801     bbgcd[4].gd.flags = gg_visible | gg_enabled;
3802     if ( bbxmax ) bbgcd[4].gd.flags |= gg_cb_on;
3803     bbgcd[4].gd.popup_msg = _("Are there any glyphs whose bounding boxes extend to the right of this number?");
3804     bbgcd[4].gd.cid = CID_BBXMax;
3805     bbgcd[4].creator = GCheckBoxCreate;
3806 
3807     sprintf(xxmaxbuf,"%g", bbxmax_val!=0 ? bbxmax_val : (double) (sf->ascent+sf->descent));
3808     bblabel[5].text = (unichar_t *) xxmaxbuf;
3809     bblabel[5].text_is_1byte = true;
3810     bbgcd[5].gd.label = &bblabel[5];
3811     bbgcd[5].gd.pos.x = 100+15; bbgcd[5].gd.pos.y = bbgcd[4].gd.pos.y-1; bbgcd[5].gd.pos.width = 40;
3812     bbgcd[5].gd.flags = gg_visible | gg_enabled;
3813     bbgcd[5].gd.cid = CID_BBXMaxVal;
3814     bbgcd[5].gd.handle_controlevent = Prob_TextChanged;
3815     bbgcd[5].data = (void *) CID_BBXMax;
3816     bbgcd[5].creator = GTextFieldCreate;
3817     bbarray[2][0] = &bbgcd[4]; bbarray[2][1] = &bbgcd[5]; bbarray[2][2] = GCD_Glue; bbarray[2][3] = NULL;
3818 
3819     bblabel[6].text = (unichar_t *) _("Glyph BB Left Of");
3820     bblabel[6].text_is_1byte = true;
3821     bblabel[6].text_in_resource = true;
3822     bbgcd[6].gd.label = &bblabel[6];
3823     bbgcd[6].gd.pos.x = 3; bbgcd[6].gd.pos.y = bbgcd[4].gd.pos.y+21;
3824     bbgcd[6].gd.flags = gg_visible | gg_enabled;
3825     if ( bbxmin ) bbgcd[6].gd.flags |= gg_cb_on;
3826     bbgcd[6].gd.popup_msg = _("Are there any glyph's whose bounding boxes extend to the left of this number?");
3827     bbgcd[6].gd.cid = CID_BBXMin;
3828     bbgcd[6].creator = GCheckBoxCreate;
3829 
3830     sprintf(xxminbuf,"%g",bbxmin_val);
3831     bblabel[7].text = (unichar_t *) xxminbuf;
3832     bblabel[7].text_is_1byte = true;
3833     bbgcd[7].gd.label = &bblabel[7];
3834     bbgcd[7].gd.pos.x = 100+15; bbgcd[7].gd.pos.y = bbgcd[6].gd.pos.y-1; bbgcd[7].gd.pos.width = 40;
3835     bbgcd[7].gd.flags = gg_visible | gg_enabled;
3836     bbgcd[7].gd.cid = CID_BBXMinVal;
3837     bbgcd[7].gd.handle_controlevent = Prob_TextChanged;
3838     bbgcd[7].data = (void *) CID_BBXMin;
3839     bbgcd[7].creator = GTextFieldCreate;
3840     bbarray[3][0] = &bbgcd[6]; bbarray[3][1] = &bbgcd[7]; bbarray[3][2] = GCD_Glue; bbarray[3][3] = NULL;
3841 
3842     bblabel[8].text = (unichar_t *) _("Check Advance:");
3843     bblabel[8].text_is_1byte = true;
3844     bblabel[8].text_in_resource = true;
3845     bbgcd[8].gd.label = &bblabel[8];
3846     bbgcd[8].gd.mnemonic = 'W';
3847     bbgcd[8].gd.pos.x = 3; bbgcd[8].gd.pos.y = bbgcd[6].gd.pos.y+21;
3848     bbgcd[8].gd.flags = gg_visible | gg_enabled;
3849     if ( advancewidth ) bbgcd[8].gd.flags |= gg_cb_on;
3850     bbgcd[8].gd.popup_msg = _("Check for characters whose advance width is not the displayed value.");
3851     bbgcd[8].gd.cid = CID_AdvanceWidth;
3852     bbgcd[8].creator = GCheckBoxCreate;
3853 
3854     if ( ( ssc = SFGetChar(sf,' ',NULL))!=NULL )
3855 	advancewidthval = ssc->width;
3856     sprintf(awidthbuf,"%g",advancewidthval);
3857     bblabel[9].text = (unichar_t *) awidthbuf;
3858     bblabel[9].text_is_1byte = true;
3859     bbgcd[9].gd.label = &bblabel[9];
3860     bbgcd[9].gd.pos.x = 100+15; bbgcd[9].gd.pos.y = bbgcd[8].gd.pos.y-1; bbgcd[9].gd.pos.width = 40;
3861     bbgcd[9].gd.flags = gg_visible | gg_enabled;
3862     bbgcd[9].gd.cid = CID_AdvanceWidthVal;
3863     bbgcd[9].gd.handle_controlevent = Prob_TextChanged;
3864     bbgcd[9].data = (void *) CID_AdvanceWidth;
3865     bbgcd[9].creator = GTextFieldCreate;
3866     bbarray[4][0] = &bbgcd[8]; bbarray[4][1] = &bbgcd[9]; bbarray[4][2] = GCD_Glue; bbarray[4][3] = NULL;
3867 
3868     bblabel[10].text = (unichar_t *) _("Check VAdvance:\n");
3869     bblabel[10].text_is_1byte = true;
3870     bbgcd[10].gd.label = &bblabel[10];
3871     bbgcd[10].gd.mnemonic = 'W';
3872     bbgcd[10].gd.pos.x = 3; bbgcd[10].gd.pos.y = bbgcd[9].gd.pos.y+24;
3873     bbgcd[10].gd.flags = gg_visible | gg_enabled;
3874     if ( !sf->hasvmetrics ) bbgcd[10].gd.flags = gg_visible;
3875     else if ( vadvancewidth ) bbgcd[10].gd.flags |= gg_cb_on;
3876     bbgcd[10].gd.popup_msg = _("Check for characters whose vertical advance width is not the displayed value.");
3877     bbgcd[10].gd.cid = CID_VAdvanceWidth;
3878     bbgcd[10].creator = GCheckBoxCreate;
3879 
3880     if ( vadvancewidth==0 ) vadvancewidth = sf->ascent+sf->descent;
3881     sprintf(vawidthbuf,"%g",vadvancewidthval);
3882     bblabel[11].text = (unichar_t *) vawidthbuf;
3883     bblabel[11].text_is_1byte = true;
3884     bbgcd[11].gd.label = &bblabel[11];
3885     bbgcd[11].gd.pos.x = 100+15; bbgcd[11].gd.pos.y = bbgcd[10].gd.pos.y-1; bbgcd[11].gd.pos.width = 40;
3886     bbgcd[11].gd.flags = gg_visible | gg_enabled;
3887     if ( !sf->hasvmetrics ) bbgcd[11].gd.flags = gg_visible;
3888     bbgcd[11].gd.cid = CID_VAdvanceWidthVal;
3889     bbgcd[11].gd.handle_controlevent = Prob_TextChanged;
3890     bbgcd[11].data = (void *) CID_VAdvanceWidth;
3891     bbgcd[11].creator = GTextFieldCreate;
3892     bbarray[5][0] = &bbgcd[10]; bbarray[5][1] = &bbgcd[11]; bbarray[5][2] = GCD_Glue; bbarray[5][3] = NULL;
3893     bbarray[6][0] = GCD_Glue; bbarray[6][1] = GCD_Glue; bbarray[6][2] = GCD_Glue; bbarray[6][3] = NULL;
3894     bbarray[7][0] = NULL;
3895 
3896     bbboxes[0].gd.flags = gg_enabled|gg_visible;
3897     bbboxes[0].gd.u.boxelements = bbarray[0];
3898     bbboxes[0].creator = GHVBoxCreate;
3899 
3900 /* ************************************************************************** */
3901 
3902     memset(&clabel,0,sizeof(clabel));
3903     memset(&cgcd,0,sizeof(cgcd));
3904     memset(&cboxes,0,sizeof(cboxes));
3905 
3906     clabel[0].text = (unichar_t *) _("Check for CIDs defined _twice");
3907     clabel[0].text_is_1byte = true;
3908     clabel[0].text_in_resource = true;
3909     cgcd[0].gd.label = &clabel[0];
3910     cgcd[0].gd.mnemonic = 'S';
3911     cgcd[0].gd.pos.x = 3; cgcd[0].gd.pos.y = 6;
3912     cgcd[0].gd.flags = gg_visible | gg_enabled;
3913     if ( cidmultiple ) cgcd[0].gd.flags |= gg_cb_on;
3914     cgcd[0].gd.popup_msg = _("Check whether a CID is defined in more than one sub-font");
3915     cgcd[0].gd.cid = CID_CIDMultiple;
3916     cgcd[0].creator = GCheckBoxCreate;
3917     carray[0] = &cgcd[0];
3918 
3919     clabel[1].text = (unichar_t *) _("Check for _undefined CIDs");
3920     clabel[1].text_is_1byte = true;
3921     clabel[1].text_in_resource = true;
3922     cgcd[1].gd.label = &clabel[1];
3923     cgcd[1].gd.mnemonic = 'S';
3924     cgcd[1].gd.pos.x = 3; cgcd[1].gd.pos.y = cgcd[0].gd.pos.y+17;
3925     cgcd[1].gd.flags = gg_visible | gg_enabled;
3926     if ( cidblank ) cgcd[1].gd.flags |= gg_cb_on;
3927     cgcd[1].gd.popup_msg = _("Check whether a CID is undefined in all sub-fonts");
3928     cgcd[1].gd.cid = CID_CIDBlank;
3929     cgcd[1].creator = GCheckBoxCreate;
3930     carray[1] = &cgcd[1]; carray[2] = GCD_Glue; carray[3] = NULL;
3931 
3932     cboxes[0].gd.flags = gg_enabled|gg_visible;
3933     cboxes[0].gd.u.boxelements = carray;
3934     cboxes[0].creator = GVBoxCreate;
3935 
3936 /* ************************************************************************** */
3937 
3938     memset(&alabel,0,sizeof(alabel));
3939     memset(&agcd,0,sizeof(agcd));
3940     memset(&aboxes,0,sizeof(aboxes));
3941 
3942     alabel[0].text = (unichar_t *) _("Check for missing _glyph names");
3943     alabel[0].text_is_1byte = true;
3944     alabel[0].text_in_resource = true;
3945     agcd[0].gd.label = &alabel[0];
3946     agcd[0].gd.pos.x = 3; agcd[0].gd.pos.y = 6;
3947     agcd[0].gd.flags = gg_visible | gg_enabled;
3948     if ( missingglyph ) agcd[0].gd.flags |= gg_cb_on;
3949     agcd[0].gd.popup_msg = _("Check whether a substitution, kerning class, etc. uses a glyph name which does not match any glyph in the font");
3950     agcd[0].gd.cid = CID_MissingGlyph;
3951     agcd[0].creator = GCheckBoxCreate;
3952     aarray[0] = &agcd[0];
3953 
3954     alabel[1].text = (unichar_t *) _("Check for missing _scripts in features");
3955     alabel[1].text_is_1byte = true;
3956     alabel[1].text_in_resource = true;
3957     agcd[1].gd.label = &alabel[1];
3958     agcd[1].gd.pos.x = 3; agcd[1].gd.pos.y = agcd[0].gd.pos.y+14;
3959     agcd[1].gd.flags = gg_visible | gg_enabled;
3960     if ( missingscriptinfeature ) agcd[1].gd.flags |= gg_cb_on;
3961     agcd[1].gd.popup_msg = _(
3962 	    "In every lookup that uses a glyph, check that at\n"
3963 	    "least one feature is active for the glyph's script.");
3964     agcd[1].gd.cid = CID_MissingScriptInFeature;
3965     agcd[1].creator = GCheckBoxCreate;
3966     aarray[1] = &agcd[1];
3967 
3968     alabel[2].text = (unichar_t *) _("Check substitutions for empty chars");
3969     alabel[2].text_is_1byte = true;
3970     agcd[2].gd.label = &alabel[2];
3971     agcd[2].gd.pos.x = 3; agcd[2].gd.pos.y = agcd[1].gd.pos.y+15;
3972     agcd[2].gd.flags = gg_visible | gg_enabled;
3973     if ( badsubs ) agcd[2].gd.flags |= gg_cb_on;
3974     agcd[2].gd.popup_msg = _("Check for characters which contain 'GSUB' entries which refer to empty characters");
3975     agcd[2].gd.cid = CID_BadSubs;
3976     agcd[2].creator = GCheckBoxCreate;
3977     aarray[2] = &agcd[2];
3978 
3979     alabel[3].text = (unichar_t *) _("Check for incomplete mark to base subtables");
3980     alabel[3].text_is_1byte = true;
3981     agcd[3].gd.label = &alabel[3];
3982     agcd[3].gd.pos.x = 3; agcd[3].gd.pos.y = agcd[1].gd.pos.y+15;
3983     agcd[3].gd.flags = gg_visible | gg_enabled;
3984     if ( missinganchor ) agcd[3].gd.flags |= gg_cb_on;
3985     agcd[3].gd.popup_msg = _(
3986 	"The OpenType documentation suggests in a rather confusing way\n"
3987 	"that if a base glyph (or base mark) contains an anchor point\n"
3988 	"for one class in a lookup subtable, then it should contain\n"
3989 	"anchors for all classes in the subtable" );
3990     agcd[3].gd.cid = CID_MissingAnchor;
3991     agcd[3].creator = GCheckBoxCreate;
3992     aarray[3] = &agcd[3]; aarray[4] = GCD_Glue; aarray[5] = NULL;
3993 
3994     aboxes[0].gd.flags = gg_enabled|gg_visible;
3995     aboxes[0].gd.u.boxelements = aarray;
3996     aboxes[0].creator = GVBoxCreate;
3997 
3998 /* ************************************************************************** */
3999 
4000     memset(&mlabel,0,sizeof(mlabel));
4001     memset(&mgcd,0,sizeof(mgcd));
4002     memset(&mboxes,0,sizeof(mboxes));
4003     memset(aspects,0,sizeof(aspects));
4004     i = 0;
4005 
4006     aspects[i].text = (unichar_t *) _("Points");
4007     aspects[i].selected = true;
4008     aspects[i].text_is_1byte = true;
4009     aspects[i++].gcd = pboxes;
4010 
4011     aspects[i].text = (unichar_t *) _("Paths");
4012     aspects[i].text_is_1byte = true;
4013     aspects[i++].gcd = paboxes;
4014 
4015 /* GT: Refs is an abbreviation for References. Space is tight here */
4016     aspects[i].text = (unichar_t *) _("Refs");
4017     aspects[i].text_is_1byte = true;
4018     aspects[i++].gcd = rfboxes;
4019 
4020     aspects[i].text = (unichar_t *) _("Hints");
4021     aspects[i].text_is_1byte = true;
4022     aspects[i++].gcd = hboxes;
4023 
4024     aspects[i].text = (unichar_t *) _("ATT");
4025     aspects[i].text_is_1byte = true;
4026     aspects[i++].gcd = aboxes;
4027 
4028     aspects[i].text = (unichar_t *) _("CID");
4029     aspects[i].disabled = fv->b.cidmaster==NULL;
4030     aspects[i].text_is_1byte = true;
4031     aspects[i++].gcd = cboxes;
4032 
4033     aspects[i].text = (unichar_t *) _("BB");
4034     aspects[i].text_is_1byte = true;
4035     aspects[i++].gcd = bbboxes;
4036 
4037     aspects[i].text = (unichar_t *) _("Random");
4038     aspects[i].text_is_1byte = true;
4039     aspects[i++].gcd = rboxes;
4040 
4041     mgcd[0].gd.pos.x = 4; mgcd[0].gd.pos.y = 6;
4042     mgcd[0].gd.pos.width = 210;
4043     mgcd[0].gd.pos.height = 190;
4044     mgcd[0].gd.u.tabs = aspects;
4045     mgcd[0].gd.flags = gg_visible | gg_enabled;
4046     mgcd[0].creator = GTabSetCreate;
4047     marray[0][0] = &mgcd[0]; marray[0][1] = NULL;
4048 
4049     mgcd[1].gd.pos.x = 15; mgcd[1].gd.pos.y = 190+10;
4050     mgcd[1].gd.flags = gg_visible | gg_enabled | gg_dontcopybox;
4051     mlabel[1].text = (unichar_t *) _("Clear All");
4052     mlabel[1].text_is_1byte = true;
4053     mlabel[1].text_in_resource = true;
4054     mgcd[1].gd.label = &mlabel[1];
4055     /*mgcd[1].gd.box = &smallbox;*/
4056     mgcd[1].gd.handle_controlevent = Prob_DoAll;
4057     mgcd[2].gd.cid = CID_ClearAll;
4058     mgcd[1].creator = GButtonCreate;
4059     mharray1[0] = &mgcd[1];
4060 
4061     mgcd[2].gd.pos.x = mgcd[1].gd.pos.x+1.25*GIntGetResource(_NUM_Buttonsize);
4062     mgcd[2].gd.pos.y = mgcd[1].gd.pos.y;
4063     mgcd[2].gd.flags = gg_visible | gg_enabled | gg_dontcopybox;
4064     mlabel[2].text = (unichar_t *) _("Set All");
4065     mlabel[2].text_is_1byte = true;
4066     mlabel[2].text_in_resource = true;
4067     mgcd[2].gd.label = &mlabel[2];
4068     /*mgcd[2].gd.box = &smallbox;*/
4069     mgcd[2].gd.handle_controlevent = Prob_DoAll;
4070     mgcd[2].gd.cid = CID_SetAll;
4071     mgcd[2].creator = GButtonCreate;
4072     mharray1[1] = &mgcd[2]; mharray1[2] = GCD_Glue; mharray1[3] = NULL;
4073 
4074     mboxes[2].gd.flags = gg_enabled|gg_visible;
4075     mboxes[2].gd.u.boxelements = mharray1;
4076     mboxes[2].creator = GHBoxCreate;
4077     marray[1][0] = &mboxes[2]; marray[1][1] = NULL;
4078 
4079     mgcd[3].gd.pos.x = 6; mgcd[3].gd.pos.y = mgcd[1].gd.pos.y+27;
4080     mgcd[3].gd.pos.width = 218-12;
4081     mgcd[3].gd.flags = gg_visible | gg_enabled;
4082     mgcd[3].creator = GLineCreate;
4083     marray[2][0] = &mgcd[3]; marray[2][1] = NULL;
4084 
4085     mlabel[4].text = (unichar_t *) U_("¹ \"Near\" means within");
4086     mlabel[4].text_is_1byte = true;
4087     mgcd[4].gd.label = &mlabel[4];
4088     mgcd[4].gd.mnemonic = 'N';
4089     mgcd[4].gd.pos.x = 6; mgcd[4].gd.pos.y = mgcd[3].gd.pos.y+6+6;
4090     mgcd[4].gd.flags = gg_visible | gg_enabled;
4091     mgcd[4].creator = GLabelCreate;
4092     mharray2[0] = &mgcd[4];
4093 
4094     sprintf(nearbuf,"%g",near);
4095     mlabel[5].text = (unichar_t *) nearbuf;
4096     mlabel[5].text_is_1byte = true;
4097     mgcd[5].gd.label = &mlabel[5];
4098     mgcd[5].gd.pos.x = 130; mgcd[5].gd.pos.y = mgcd[4].gd.pos.y-6; mgcd[5].gd.pos.width = 40;
4099     mgcd[5].gd.flags = gg_visible | gg_enabled;
4100     mgcd[5].gd.cid = CID_Near;
4101     mgcd[5].creator = GTextFieldCreate;
4102     mharray2[1] = &mgcd[5];
4103 
4104     mlabel[6].text = (unichar_t *) _("em-units");
4105     mlabel[6].text_is_1byte = true;
4106     mgcd[6].gd.label = &mlabel[6];
4107     mgcd[6].gd.pos.x = mgcd[5].gd.pos.x+mgcd[5].gd.pos.width+4; mgcd[6].gd.pos.y = mgcd[4].gd.pos.y;
4108     mgcd[6].gd.flags = gg_visible | gg_enabled;
4109     mgcd[6].creator = GLabelCreate;
4110     mharray2[2] = &mgcd[6]; mharray2[3] = GCD_Glue; mharray2[4] = NULL;
4111 
4112     mboxes[3].gd.flags = gg_enabled|gg_visible;
4113     mboxes[3].gd.u.boxelements = mharray2;
4114     mboxes[3].creator = GHBoxCreate;
4115     marray[3][0] = &mboxes[3]; marray[3][1] = NULL;
4116 
4117     mgcd[7].gd.pos.x = 15-3; mgcd[7].gd.pos.y = mgcd[5].gd.pos.y+26;
4118     mgcd[7].gd.pos.width = -1; mgcd[7].gd.pos.height = 0;
4119     mgcd[7].gd.flags = gg_visible | gg_enabled | gg_but_default;
4120     mlabel[7].text = (unichar_t *) _("_OK");
4121     mlabel[7].text_is_1byte = true;
4122     mlabel[7].text_in_resource = true;
4123     mgcd[7].gd.mnemonic = 'O';
4124     mgcd[7].gd.label = &mlabel[7];
4125     mgcd[7].gd.handle_controlevent = Prob_OK;
4126     mgcd[7].creator = GButtonCreate;
4127     barray[0] = GCD_Glue; barray[1] = &mgcd[7]; barray[2] = GCD_Glue; barray[3] = GCD_Glue;
4128 
4129     mgcd[8].gd.pos.x = -15; mgcd[8].gd.pos.y = mgcd[7].gd.pos.y+3;
4130     mgcd[8].gd.pos.width = -1; mgcd[8].gd.pos.height = 0;
4131     mgcd[8].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
4132     mlabel[8].text = (unichar_t *) _("_Cancel");
4133     mlabel[8].text_is_1byte = true;
4134     mlabel[8].text_in_resource = true;
4135     mgcd[8].gd.label = &mlabel[8];
4136     mgcd[8].gd.mnemonic = 'C';
4137     mgcd[8].gd.handle_controlevent = Prob_Cancel;
4138     mgcd[8].creator = GButtonCreate;
4139     barray[4] = GCD_Glue; barray[5] = GCD_Glue; barray[6] = &mgcd[8]; barray[7] = GCD_Glue; barray[8] = NULL;
4140 
4141     mboxes[4].gd.flags = gg_enabled|gg_visible;
4142     mboxes[4].gd.u.boxelements = barray;
4143     mboxes[4].creator = GHBoxCreate;
4144     marray[4][0] = &mboxes[4]; marray[4][1] = NULL;
4145     marray[5][0] = NULL;
4146 
4147     mboxes[0].gd.pos.x = mboxes[0].gd.pos.y = 2;
4148     mboxes[0].gd.flags = gg_enabled|gg_visible;
4149     mboxes[0].gd.u.boxelements = marray[0];
4150     mboxes[0].creator = GHVGroupCreate;
4151 
4152     GGadgetsCreate(gw,mboxes);
4153 
4154     GHVBoxSetExpandableRow(mboxes[0].ret,0);
4155     GHVBoxSetExpandableCol(mboxes[2].ret,gb_expandglue);
4156     GHVBoxSetExpandableCol(mboxes[3].ret,gb_expandglue);
4157     GHVBoxSetExpandableCol(mboxes[4].ret,gb_expandgluesame);
4158     GHVBoxSetExpandableRow(pboxes[0].ret,gb_expandglue);
4159     GHVBoxSetExpandableCol(pboxes[2].ret,gb_expandglue);
4160     GHVBoxSetExpandableCol(pboxes[3].ret,gb_expandglue);
4161     GHVBoxSetExpandableCol(pboxes[4].ret,gb_expandglue);
4162     GHVBoxSetExpandableRow(paboxes[0].ret,gb_expandglue);
4163     GHVBoxSetExpandableCol(paboxes[2].ret,gb_expandglue);
4164     GHVBoxSetExpandableRow(rfboxes[0].ret,gb_expandglue);
4165     GHVBoxSetExpandableCol(rfboxes[2].ret,gb_expandglue);
4166     GHVBoxSetExpandableRow(hboxes[0].ret,gb_expandglue);
4167     GHVBoxSetExpandableCol(hboxes[2].ret,gb_expandglue);
4168     GHVBoxSetExpandableCol(hboxes[3].ret,gb_expandglue);
4169     GHVBoxSetExpandableRow(aboxes[0].ret,gb_expandglue);
4170     GHVBoxSetExpandableRow(cboxes[0].ret,gb_expandglue);
4171     GHVBoxSetExpandableRow(bbboxes[0].ret,gb_expandglue);
4172     GHVBoxSetExpandableCol(bbboxes[0].ret,gb_expandglue);
4173     GHVBoxSetExpandableRow(rboxes[0].ret,gb_expandglue);
4174 
4175     GHVBoxFitWindow(mboxes[0].ret);
4176 
4177     GDrawSetVisible(gw,true);
4178     while ( !p.done )
4179 	GDrawProcessOneEvent(NULL);
4180     GDrawDestroyWindow(gw);
4181     if ( p.explainw!=NULL )
4182 	GDrawDestroyWindow(p.explainw);
4183 }
4184 
4185 /* ************************************************************************** */
4186 /* ***************************** Validation code **************************** */
4187 /* ************************************************************************** */
4188 
4189 struct val_data {
4190     GWindow gw;
4191     GWindow v;
4192     GGadget *vsb;
4193     int lcnt;
4194     int loff_top;
4195     int vlcnt;
4196     SplineFont *sf;
4197     int cidmax;
4198     enum validation_state mask;
4199     int need_to_check_with_user_on_mask;
4200     int needs_blue;
4201     GTimer *recheck;
4202     int laststart;
4203     int finished_first_pass;
4204     int as,fh;
4205     GFont *font;
4206     SplineChar *sc;		/* used by popup menu */
4207     int lastgid;
4208     CharView *lastcv;
4209     int layer;
4210 };
4211 
4212 static char *vserrornames[] = {
4213     N_("Open Contour"),
4214     N_("Self Intersecting"),
4215     N_("Wrong Direction"),
4216     N_("Flipped References"),
4217     N_("Missing Points at Extrema"),
4218     N_("Unknown glyph referenced in GSUB/GPOS/MATH"),
4219     N_("Too Many Points"),
4220     N_("Too Many Hints"),
4221     N_("Bad Glyph Name"),
4222     NULL,		/* Maxp too many points */
4223     NULL,		/* Maxp too many paths */
4224     NULL,		/* Maxp too many component points */
4225     NULL,		/* Maxp too many component paths */
4226     NULL,		/* Maxp instructions too long */
4227     NULL,		/* Maxp too many references */
4228     NULL,		/* Maxp references too deep */
4229     NULL,		/* prep or fpgm too long */
4230     N_("Distance between adjacent points is too big"),
4231     N_("Non-integral coordinates"),
4232     N_("Contains anchor points for some, but not all, classes in a subtable"),
4233     N_("There is another glyph in the font with this name"),
4234     N_("There is another glyph in the font with this unicode code point"),
4235     N_("Glyph contains overlapped hints (in the same hintmask)")
4236 };
4237 
4238 static char *privateerrornames[] = {
4239     N_("Odd number of elements in BlueValues/OtherBlues array."),
4240     N_("Elements in BlueValues/OtherBlues array are disordered."),
4241     N_("Too many elements in BlueValues/OtherBlues array."),
4242     N_("Elements in BlueValues/OtherBlues array are too close (Change BlueFuzz)."),
4243     N_("Elements in BlueValues/OtherBlues array are not integers."),
4244     N_("Alignment zone height in BlueValues/OtherBlues array is too big for BlueScale."),
4245     NULL,
4246     NULL,
4247     N_("Odd number of elements in FamilyBlues/FamilyOtherBlues array."),
4248     N_("Elements in FamilyBlues/FamilyOtherBlues array are disordered."),
4249     N_("Too many elements in FamilyBlues/FamilyOtherBlues array."),
4250     N_("Elements in FamilyBlues/FamilyOtherBlues array are too close (Change BlueFuzz)."),
4251     N_("Elements in FamilyBlues/FamilyOtherBlues array are not integers."),
4252     N_("Alignment zone height in FamilyBlues/FamilyOtherBlues array is too big for BlueScale."),
4253     NULL,
4254     NULL,
4255     N_("Missing BlueValues entry."),
4256     N_("Bad BlueFuzz entry."),
4257     N_("Bad BlueScale entry."),
4258     N_("Bad StdHW entry."),
4259     N_("Bad StdVW entry."),
4260     N_("Bad StemSnapH entry."),
4261     N_("Bad StemSnapV entry."),
4262     N_("StemSnapH does not contain StdHW value."),
4263     N_("StemSnapV does not contain StdVW value."),
4264     N_("Bad BlueShift entry."),
4265     NULL
4266 };
4267 
VSErrorsFromMask(int mask,int private_mask)4268 char *VSErrorsFromMask(int mask, int private_mask) {
4269     int bit, m;
4270     int len;
4271     char *ret;
4272 
4273     len = 0;
4274     for ( m=0, bit=(vs_known<<1) ; bit<=vs_last; ++m, bit<<=1 )
4275 	if ( (mask&bit) && vserrornames[m]!=NULL )
4276 	    len += strlen( _(vserrornames[m]))+2;
4277     if ( private_mask != 0 )
4278 	len += strlen( _("Bad Private Dictionary")) +2;
4279     ret = malloc(len+1);
4280     len = 0;
4281     for ( m=0, bit=(vs_known<<1) ; bit<=vs_last; ++m, bit<<=1 )
4282 	if ( (mask&bit) && vserrornames[m]!=NULL ) {
4283 	    ret[len++] =' ';
4284 	    strcpy(ret+len,_(vserrornames[m]));
4285 	    len += strlen( ret+len );
4286 	    ret[len++] ='\n';
4287 	}
4288     if ( private_mask != 0 ) {
4289 	ret[len++] =' ';
4290 	strcpy(ret+len,_("Bad Private Dictionary"));
4291 	len += strlen( ret+len );
4292 	ret[len++] ='\n';
4293     }
4294     ret[len] = '\0';
4295 return( ret );
4296 }
4297 
VSModMask(SplineChar * sc,struct val_data * vw)4298 static int VSModMask(SplineChar *sc, struct val_data *vw) {
4299     int vs = 0;
4300     if ( sc!=NULL ) {
4301 	vs = sc->layers[vw->layer].validation_state;
4302 	if ( sc->unlink_rm_ovrlp_save_undo )
4303 	    vs &= ~vs_selfintersects;
4304 	/* if doing a truetype eval, then I'm told it's ok for references */
4305 	/*  to overlap. And if refs can overlap, then we can't figure out */
4306 	/*  direction properly */ /* I should really check that all the   */
4307 	/*  refs have legal ttf transform matrices */
4308 	if ( vw->mask==vs_maskttf &&
4309 		sc->layers[vw->layer].splines==NULL &&
4310 		sc->layers[vw->layer].refs!=NULL )
4311 	    vs &= ~(vs_selfintersects|vs_wrongdirection);
4312     }
4313 return( vs );
4314 }
4315 
VW_FindLine(struct val_data * vw,int line,int * skips)4316 static int VW_FindLine(struct val_data *vw,int line, int *skips) {
4317     int gid,k, cidmax = vw->cidmax;
4318     SplineFont *sf = vw->sf;
4319     SplineFont *sub;
4320     SplineChar *sc;
4321     int sofar, tot;
4322     int bit;
4323     int vs;
4324 
4325     sofar = 0;
4326     for ( gid=0; gid<cidmax ; ++gid ) {
4327 	if ( sf->subfontcnt==0 )
4328 	    sc = sf->glyphs[gid];
4329 	else {
4330 	    for ( k=0; k<sf->subfontcnt; ++k ) {
4331 		sub = sf->subfonts[k];
4332 		if ( gid<sub->glyphcnt && (sc = sub->glyphs[gid])!=NULL )
4333 	    break;
4334 	    }
4335 	}
4336 	/* Ignore it if it has not been validated */
4337 	/* Ignore it if it is good */
4338 	vs = VSModMask(sc,vw);
4339 	if ((vs&vs_known) && (vs&vw->mask)!=0 ) {
4340 	    tot = 1;
4341 	    if ( sc->vs_open )
4342 		for ( bit=(vs_known<<1) ; bit<=vs_last; bit<<=1 )
4343 		    if ( (bit&vw->mask) && (vs&bit) )
4344 			++tot;
4345 	    if ( sofar+tot>line ) {
4346 		*skips = line-sofar;
4347 return( gid );
4348 	    }
4349 	    sofar += tot;
4350 	}
4351     }
4352 
4353     vs = ValidatePrivate(sf);
4354     if ( !vw->needs_blue )
4355 	vs &= ~pds_missingblue;
4356     if ( vs!=0 ) {
4357 	tot = 1;
4358 	for ( bit=1 ; bit!=0; bit<<=1 )
4359 	    if ( vs&bit )
4360 		++tot;
4361 	if ( sofar+tot>line ) {
4362 	    *skips = line-sofar;
4363 return( -2 );
4364 	}
4365     }
4366 
4367     *skips = 0;
4368 return( -1 );
4369 }
4370 
VW_FindSC(struct val_data * vw,SplineChar * sought)4371 static int VW_FindSC(struct val_data *vw,SplineChar *sought) {
4372     int gid,k, cidmax = vw->cidmax;
4373     SplineFont *sf = vw->sf;
4374     SplineFont *sub;
4375     SplineChar *sc;
4376     int sofar;
4377     int bit, vs;
4378 
4379     sofar = 0;
4380     for ( gid=0; gid<cidmax ; ++gid ) {
4381 	if ( sf->subfontcnt==0 )
4382 	    sc = sf->glyphs[gid];
4383 	else {
4384 	    for ( k=0; k<sf->subfontcnt; ++k ) {
4385 		sub = sf->subfonts[k];
4386 		if ( gid<sub->glyphcnt && (sc = sub->glyphs[gid])!=NULL )
4387 	    break;
4388 	    }
4389 	}
4390 	/* Ignore it if it has not been validated */
4391 	/* Ignore it if it is good */
4392 	vs = VSModMask(sc,vw);
4393 	if ((vs&vs_known) && (vs&vw->mask)!=0 ) {
4394 	    if ( sc==sought )
4395 return( sofar );
4396 	    ++sofar;
4397 	    if ( sc->vs_open )
4398 		for ( bit=(vs_known<<1) ; bit<=vs_last; bit<<=1 )
4399 		    if ( (bit&vw->mask) && (vs&bit) )
4400 			++sofar;
4401 	} else if ( sc==sought )
4402 return( -1 );
4403     }
4404 return( -1 );
4405 }
4406 
VW_VScroll(GGadget * g,GEvent * e)4407 static int VW_VScroll(GGadget *g, GEvent *e) {
4408     struct val_data *vw = (struct val_data *) GDrawGetUserData(GGadgetGetWindow(g));
4409     int newpos = vw->loff_top;
4410 
4411     switch( e->u.control.u.sb.type ) {
4412       case et_sb_top:
4413         newpos = 0;
4414       break;
4415       case et_sb_uppage:
4416         newpos -= 9*vw->vlcnt/10;
4417       break;
4418       case et_sb_up:
4419         newpos -= vw->vlcnt/15;
4420       break;
4421       case et_sb_down:
4422         newpos += vw->vlcnt/15;
4423       break;
4424       case et_sb_downpage:
4425         newpos += 9*vw->vlcnt/10;
4426       break;
4427       case et_sb_bottom:
4428         newpos = 0;
4429       break;
4430       case et_sb_thumb:
4431       case et_sb_thumbrelease:
4432         newpos = e->u.control.u.sb.pos;
4433       break;
4434       case et_sb_halfup:
4435         newpos -= vw->vlcnt/30;
4436       break;
4437       case et_sb_halfdown:
4438         newpos += vw->vlcnt/30;
4439       break;
4440     }
4441     if ( newpos + vw->vlcnt > vw->lcnt )
4442 	newpos = vw->lcnt-vw->vlcnt;
4443     if ( newpos<0 )
4444 	newpos = 0;
4445     if ( vw->loff_top!=newpos ) {
4446 	vw->loff_top = newpos;
4447 	GScrollBarSetPos(vw->vsb,newpos);
4448 	GDrawRequestExpose(vw->v,NULL,false);
4449     }
4450 return( true );
4451 }
4452 
VW_SetSb(struct val_data * vw)4453 static void VW_SetSb(struct val_data *vw) {
4454     if ( vw->loff_top + vw->vlcnt > vw->lcnt )
4455 	vw->loff_top = vw->lcnt-vw->vlcnt;
4456     if ( vw->loff_top<0 )
4457 	vw->loff_top = 0;
4458     GScrollBarSetBounds(vw->vsb,0,vw->lcnt,vw->vlcnt);
4459     GScrollBarSetPos(vw->vsb,vw->loff_top);
4460 }
4461 
VW_Remetric(struct val_data * vw)4462 static void VW_Remetric(struct val_data *vw) {
4463     int gid,k, cidmax = vw->cidmax;
4464     SplineFont *sub, *sf = vw->sf;
4465     SplineChar *sc;
4466     int sofar, tot;
4467     int bit, vs;
4468 
4469     sofar = 0;
4470     for ( gid=0; gid<cidmax ; ++gid ) {
4471 	if ( sf->subfontcnt==0 )
4472 	    sc = sf->glyphs[gid];
4473 	else {
4474 	    for ( k=0; k<sf->subfontcnt; ++k ) {
4475 		sub = sf->subfonts[k];
4476 		if ( gid<sub->glyphcnt && (sc = sub->glyphs[gid])!=NULL )
4477 	    break;
4478 	    }
4479 	}
4480 	/* Ignore it if it has not been validated */
4481 	/* Ignore it if it is good */
4482 	vs = VSModMask(sc,vw);
4483 	if ((vs&vs_known) && (vs&vw->mask)!=0 ) {
4484 	    tot = 1;
4485 	    if ( sc->vs_open )
4486 		for ( bit=(vs_known<<1) ; bit<=vs_last; bit<<=1 )
4487 		    if ( (bit&vw->mask) && (vs&bit) )
4488 			++tot;
4489 	    sofar += tot;
4490 	}
4491     }
4492     vs = ValidatePrivate(sf);
4493     if ( !vw->needs_blue )
4494 	vs &= ~pds_missingblue;
4495     if ( vs!=0 ) {
4496 	tot = 1;
4497 	for ( bit=1 ; bit!=0; bit<<=1 )
4498 	    if ( vs&bit )
4499 		++tot;
4500 	sofar += tot;
4501     }
4502     if ( vw->lcnt!=sofar ) {
4503 	vw->lcnt = sofar;
4504 	VW_SetSb(vw);
4505     }
4506     GDrawRequestExpose(vw->v,NULL,false);
4507 }
4508 
VWMenuConnect(GWindow gw,struct gmenuitem * mi,GEvent * e)4509 static void VWMenuConnect(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4510     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4511     SplineChar *sc = vw->sc;
4512     int vs = sc->layers[vw->layer].validation_state;
4513     int changed = false;
4514     SplineSet *ss;
4515 
4516     for ( ss=sc->layers[vw->layer].splines; ss!=NULL; ss=ss->next ) {
4517 	if ( ss->first->prev==NULL && ss->first->next!=NULL ) {
4518 	    if ( !changed ) {
4519 		SCPreserveLayer(sc,vw->layer,false);
4520 		changed = true;
4521 	    }
4522 	    SplineMake(ss->last,ss->first,sc->layers[vw->layer].order2);
4523 	    ss->last = ss->first;
4524 	}
4525     }
4526     if ( changed ) {
4527 	SCCharChangedUpdate(sc,vw->layer);
4528 	SCValidate(vw->sc,vw->layer,true);
4529 	if ( vs != vw->sc->layers[vw->layer].validation_state )
4530 	    VW_Remetric(vw);
4531     }
4532 }
4533 
VWMenuInlineRefs(GWindow gw,struct gmenuitem * mi,GEvent * e)4534 static void VWMenuInlineRefs(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4535     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4536     SplineChar *sc = vw->sc;
4537     int vs = sc->layers[vw->layer].validation_state;
4538     int changed = false;
4539     RefChar *ref, *refnext;
4540 
4541     for ( ref= sc->layers[vw->layer].refs; ref!=NULL; ref=refnext ) {
4542 	refnext = ref->next;
4543 	if ( !changed )
4544 	    SCPreserveLayer(sc,vw->layer,false);
4545 	changed = true;
4546 	SCRefToSplines(sc,ref,vw->layer);
4547     }
4548     if ( changed ) {
4549 	SCCharChangedUpdate(sc,vw->layer);
4550 
4551 	SCValidate(vw->sc,vw->layer,true);
4552 	if ( vs != vw->sc->layers[vw->layer].validation_state )
4553 	    VW_Remetric(vw);
4554     }
4555 }
4556 
VWMenuOverlap(GWindow gw,struct gmenuitem * mi,GEvent * e)4557 static void VWMenuOverlap(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4558     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4559     SplineChar *sc = vw->sc;
4560     int vs = sc->layers[vw->layer].validation_state;
4561 
4562     if ( !SCRoundToCluster(sc,ly_all,false,.03,.12))
4563 	SCPreserveLayer(sc,vw->layer,false);
4564     sc->layers[vw->layer].splines = SplineSetRemoveOverlap(sc,sc->layers[vw->layer].splines,over_remove);
4565     SCCharChangedUpdate(sc,vw->layer);
4566 
4567     SCValidate(vw->sc,vw->layer,true);
4568     if ( vs != vw->sc->layers[vw->layer].validation_state )
4569 	VW_Remetric(vw);
4570 }
4571 
VWMenuMark(GWindow gw,struct gmenuitem * mi,GEvent * e)4572 static void VWMenuMark(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4573     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4574     SplineChar *sc = vw->sc;
4575 
4576     sc->unlink_rm_ovrlp_save_undo = true;
4577 
4578     VW_Remetric(vw);
4579 }
4580 
VWMenuInlineFlippedRefs(GWindow gw,struct gmenuitem * mi,GEvent * e)4581 static void VWMenuInlineFlippedRefs(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4582     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4583     SplineChar *sc = vw->sc;
4584     int vs = sc->layers[vw->layer].validation_state;
4585     int changed = false;
4586     RefChar *ref, *refnext;
4587 
4588     for ( ref= sc->layers[vw->layer].refs; ref!=NULL; ref=refnext ) {
4589 	refnext = ref->next;
4590 	if ( ref->transform[0]*ref->transform[3]<0 ||
4591 		(ref->transform[0]==0 && ref->transform[1]*ref->transform[2]>0)) {
4592 	    if ( !changed )
4593 		SCPreserveLayer(sc,vw->layer,false);
4594 	    changed = true;
4595 	    SCRefToSplines(sc,ref,vw->layer);
4596 	}
4597     }
4598     if ( changed ) {
4599 	SCCharChangedUpdate(sc,vw->layer);
4600 
4601 	SCValidate(vw->sc,vw->layer,true);
4602 	if ( vs != vw->sc->layers[vw->layer].validation_state )
4603 	    VW_Remetric(vw);
4604     }
4605 }
4606 
VWMenuCorrectDir(GWindow gw,struct gmenuitem * mi,GEvent * e)4607 static void VWMenuCorrectDir(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4608     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4609     SplineChar *sc = vw->sc;
4610     int vs = sc->layers[vw->layer].validation_state;
4611     int changed = false;
4612 
4613     SCPreserveLayer(sc,vw->layer,false);
4614     sc->layers[vw->layer].splines = SplineSetsCorrect(sc->layers[vw->layer].splines,&changed);
4615     SCCharChangedUpdate(sc,vw->layer);
4616 
4617     SCValidate(vw->sc,vw->layer,true);
4618     if ( vs != vw->sc->layers[vw->layer].validation_state )
4619 	VW_Remetric(vw);
4620 }
4621 
VWMenuGoodExtrema(GWindow gw,struct gmenuitem * mi,GEvent * e)4622 static void VWMenuGoodExtrema(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4623     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4624     SplineFont *sf = vw->sf;
4625     int emsize = sf->ascent+sf->descent;
4626     SplineChar *sc = vw->sc;
4627     int vs = sc->layers[vw->layer].validation_state;
4628 
4629     SCPreserveLayer(sc,vw->layer,false);
4630     SplineCharAddExtrema(sc,sc->layers[vw->layer].splines,ae_only_good,emsize);
4631     SCCharChangedUpdate(sc,vw->layer);
4632 
4633     SCValidate(vw->sc,vw->layer,true);
4634     if ( vs != vw->sc->layers[vw->layer].validation_state )
4635 	VW_Remetric(vw);
4636 }
4637 
VWMenuAllExtrema(GWindow gw,struct gmenuitem * mi,GEvent * e)4638 static void VWMenuAllExtrema(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4639     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4640     SplineFont *sf = vw->sf;
4641     int emsize = sf->ascent+sf->descent;
4642     SplineChar *sc = vw->sc;
4643     int vs = sc->layers[vw->layer].validation_state;
4644 
4645     SCPreserveLayer(sc,vw->layer,false);
4646     SplineCharAddExtrema(sc,sc->layers[vw->layer].splines,ae_all,emsize);
4647     SCCharChangedUpdate(sc,vw->layer);
4648 
4649     SCValidate(vw->sc,vw->layer,true);
4650     if ( vs != vw->sc->layers[vw->layer].validation_state )
4651 	VW_Remetric(vw);
4652 }
4653 
VWMenuSimplify(GWindow gw,struct gmenuitem * mi,GEvent * e)4654 static void VWMenuSimplify(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4655     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4656     SplineChar *sc = vw->sc;
4657     int vs = sc->layers[vw->layer].validation_state;
4658     static struct simplifyinfo smpl = { sf_normal, 0.75, 0.05, 0, -1, 0, 0 };
4659 
4660     SCPreserveLayer(sc,vw->layer,false);
4661     sc->layers[vw->layer].splines = SplineCharSimplify(sc,sc->layers[vw->layer].splines,&smpl);
4662     SCCharChangedUpdate(sc,vw->layer);
4663 
4664     SCValidate(vw->sc,vw->layer,true);
4665     if ( vs != vw->sc->layers[vw->layer].validation_state )
4666 	VW_Remetric(vw);
4667 }
4668 
VWMenuRevalidateAll(GWindow gw,struct gmenuitem * mi,GEvent * e)4669 static void VWMenuRevalidateAll(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4670     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4671     SplineChar *sc;
4672     int k, gid;
4673     SplineFont *sf;
4674 
4675     k=0;
4676     do {
4677 	sf = k<vw->sf->subfontcnt ? vw->sf->subfonts[k] : vw->sf;
4678 	for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
4679 	    sc->layers[vw->layer].validation_state = 0;
4680 	    sc->layers[vw->layer].old_vs = 2;
4681 	}
4682 	++k;
4683     } while ( k<vw->sf->subfontcnt );
4684     VW_Remetric(vw);
4685 }
4686 
VWMenuRevalidate(GWindow gw,struct gmenuitem * mi,GEvent * e)4687 static void VWMenuRevalidate(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4688     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4689     int vs = vw->sc->layers[vw->layer].validation_state;
4690     SCValidate(vw->sc,vw->layer,true);
4691     if ( vs != vw->sc->layers[vw->layer].validation_state )
4692 	VW_Remetric(vw);
4693 }
4694 
VWReuseCV(struct val_data * vw,SplineChar * sc)4695 static void VWReuseCV(struct val_data *vw, SplineChar *sc) {
4696     int k;
4697     SplineChar *sctest;
4698     SplineFont *sf = vw->sf;
4699     CharView *cv;
4700 
4701     /* See if the last cv we used is still open. This is a little complex as */
4702     /*  we must make sure that the splinechar is still in the font, and then */
4703     /*  that the cv is still attached to it */
4704     cv = NULL;
4705     if ( vw->lastgid!=-1 && vw->lastcv!=NULL ) {
4706 	sctest = NULL;
4707 	if ( sf->subfontcnt==0 ) {
4708 	    if ( vw->lastgid<sf->glyphcnt )
4709 		sctest = sf->glyphs[vw->lastgid];
4710 	} else {
4711 	    for ( k = 0; k<sf->subfontcnt; ++k )
4712 		if ( vw->lastgid<sf->subfonts[k]->glyphcnt )
4713 		    if ( (sctest = sf->subfonts[k]->glyphs[vw->lastgid])!=NULL )
4714 	    break;
4715 	}
4716 	if ( sctest!=NULL )
4717 	    for ( cv=(CharView *) (sctest->views); cv!=NULL && cv!=vw->lastcv; cv=(CharView *) (cv->b.next) );
4718     }
4719     if ( cv==NULL )
4720 	cv = CharViewCreate(sc,(FontView *) (vw->sf->fv),vw->sf->fv->map->backmap[sc->orig_pos]);
4721     else {
4722 	CVChangeSC(cv,sc);
4723 	GDrawSetVisible(cv->gw,true);
4724 	GDrawRaise(cv->gw);
4725     }
4726     if ( CVLayer((CharViewBase *) cv)!=vw->layer )
4727 	CVSetLayer(cv,vw->layer);
4728     vw->lastgid = sc->orig_pos;
4729     vw->lastcv = cv;
4730 
4731     if ( sc->layers[vw->layer].validation_state & vs_maskfindproblems & vw->mask )
4732 	DummyFindProblems(cv);
4733 }
4734 
VWMenuOpenGlyph(GWindow gw,struct gmenuitem * mi,GEvent * e)4735 static void VWMenuOpenGlyph(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4736     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4737     VWReuseCV(vw,vw->sc);
4738 }
4739 
VWMenuGotoGlyph(GWindow gw,struct gmenuitem * mi,GEvent * e)4740 static void VWMenuGotoGlyph(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4741     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4742     FontView *fv = (FontView *) (vw->sf->fv);
4743     int enc = GotoChar(vw->sf,fv->b.map,NULL);
4744     int gid, line;
4745     SplineChar *sc;
4746 
4747     if ( enc==-1 )
4748 return;
4749     gid = fv->b.map->map[enc];
4750     if ( gid==-1 || (sc=vw->sf->glyphs[gid])==NULL ) {
4751 	ff_post_error(_("Glyph not in font"), _("Glyph not in font"));
4752 return;
4753     } else if ( (SCValidate(sc,vw->layer,true)&vw->mask)==0 ) {
4754 	ff_post_notice(_("Glyph Valid"), _("No problems detected in %s"),
4755 		sc->name );
4756 return;
4757     }
4758 
4759     line = VW_FindSC(vw,sc);
4760     if ( line==-1 )
4761 	IError("Glyph doesn't exist?");
4762 
4763     if ( line + vw->vlcnt > vw->lcnt )
4764 	line = vw->lcnt-vw->vlcnt;
4765     if ( line<0 )
4766 	line = 0;
4767     if ( vw->loff_top!=line ) {
4768 	vw->loff_top = line;
4769 	GScrollBarSetPos(vw->vsb,line);
4770 	GDrawRequestExpose(vw->v,NULL,false);
4771     }
4772 }
4773 
4774 
4775 #define MID_SelectOpen	102
4776 #define MID_SelectRO	103
4777 #define MID_SelectDir	104
4778 #define MID_SelectExtr	105
4779 #define MID_SelectErrors	106
4780 
VWMenuSelect(GWindow gw,struct gmenuitem * mi,GEvent * e)4781 static void VWMenuSelect(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4782     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4783     FontView *fv = (FontView *) (vw->sf->fv);
4784     int mask = mi->mid == MID_SelectErrors ? vw->mask :
4785 	    mi->mid == MID_SelectOpen ? vs_opencontour :
4786 	    mi->mid == MID_SelectRO ? vs_selfintersects :
4787 	    mi->mid == MID_SelectDir ? vs_wrongdirection :
4788 	    mi->mid == MID_SelectExtr ? vs_missingextrema : 0;
4789     EncMap *map = fv->b.map;
4790     int i, gid;
4791     SplineChar *sc;
4792 
4793     for ( i=0; i<map->enccount; ++i ) {
4794 	fv->b.selected[i] = false;
4795 	gid = map->map[i];
4796 	if ( gid!=-1 && (sc=vw->sf->glyphs[gid])!=NULL &&
4797 		(SCValidate(sc,vw->layer,true) & mask) )
4798 	    fv->b.selected[i] = true;
4799     }
4800     GDrawSetVisible(fv->gw,true);
4801     GDrawRaise(fv->gw);
4802     GDrawRequestExpose(fv->v,NULL,false);
4803 }
4804 
4805 static GMenuItem vw_subselect[] = {
4806     { { (unichar_t *) N_("problselect|Errors"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, '\0', 0, NULL, NULL, VWMenuSelect, MID_SelectErrors },
4807     { { (unichar_t *) N_("problselect|Open Contours"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, '\0', 0, NULL, NULL, VWMenuSelect, MID_SelectOpen },
4808     { { (unichar_t *) N_("problselect|Bad Direction"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, '\0', 0, NULL, NULL, VWMenuSelect, MID_SelectDir },
4809     { { (unichar_t *) N_("problselect|Self Intersections"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, '\0', 0, NULL, NULL, VWMenuSelect, MID_SelectRO },
4810     { { (unichar_t *) N_("problselect|Missing Extrema"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, '\0', 0, NULL, NULL, VWMenuSelect, MID_SelectExtr },
4811     GMENUITEM_EMPTY
4812 };
4813 
VWMenuManyConnect(GWindow gw,struct gmenuitem * mi,GEvent * e)4814 static void VWMenuManyConnect(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4815     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4816     SplineChar *sc;
4817     int k, gid;
4818     SplineFont *sf;
4819 
4820     k=0;
4821     do {
4822 	sf = k<vw->sf->subfontcnt ? vw->sf->subfonts[k] : vw->sf;
4823 	for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL && (sc->layers[vw->layer].validation_state&vs_opencontour) ) {
4824 	    int vs = sc->layers[vw->layer].validation_state;
4825 	    int changed = false;
4826 	    SplineSet *ss;
4827 
4828 	    for ( ss=sc->layers[vw->layer].splines; ss!=NULL; ss=ss->next ) {
4829 		if ( ss->first->prev==NULL && ss->first->next!=NULL ) {
4830 		    if ( !changed ) {
4831 			SCPreserveLayer(sc,vw->layer,false);
4832 			changed = true;
4833 		    }
4834 		    SplineMake(ss->last,ss->first,sc->layers[vw->layer].order2);
4835 		    ss->last = ss->first;
4836 		}
4837 	    }
4838 	    if ( changed ) {
4839 		SCCharChangedUpdate(sc,vw->layer);
4840 		SCValidate(vw->sc,vw->layer,true);
4841 		if ( vs != vw->sc->layers[vw->layer].validation_state )
4842 		    VW_Remetric(vw);
4843 	    }
4844 	}
4845 	++k;
4846     } while ( k<vw->sf->subfontcnt );
4847 }
4848 
VWMenuManyOverlap(GWindow gw,struct gmenuitem * mi,GEvent * e)4849 static void VWMenuManyOverlap(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4850     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4851     SplineChar *sc;
4852     int k, gid;
4853     SplineFont *sf;
4854 
4855     k=0;
4856     do {
4857 	sf = k<vw->sf->subfontcnt ? vw->sf->subfonts[k] : vw->sf;
4858 	for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL && (sc->layers[vw->layer].validation_state&vs_selfintersects) ) {
4859 	    int vs = sc->layers[vw->layer].validation_state;
4860 
4861 	    /* If it's only got references, I could inline them, since the */
4862 	    /*  intersection would occur between two refs. But that seems */
4863 	    /*  to extreme to do to an unsuspecting user */
4864 	    if ( !SCRoundToCluster(sc,ly_all,false,.03,.12))
4865 		SCPreserveLayer(sc,vw->layer,false);
4866 	    sc->layers[vw->layer].splines = SplineSetRemoveOverlap(sc,sc->layers[vw->layer].splines,over_remove);
4867 	    SCCharChangedUpdate(sc,vw->layer);
4868 	    SCValidate(vw->sc,vw->layer,true);
4869 	    if ( vs != vw->sc->layers[vw->layer].validation_state )
4870 		VW_Remetric(vw);
4871 	}
4872 	++k;
4873     } while ( k<vw->sf->subfontcnt );
4874 }
4875 
VWMenuManyMark(GWindow gw,struct gmenuitem * mi,GEvent * e)4876 static void VWMenuManyMark(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4877     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4878     SplineChar *sc;
4879     int k, gid;
4880     SplineFont *sf;
4881 
4882     k=0;
4883     do {
4884 	sf = k<vw->sf->subfontcnt ? vw->sf->subfonts[k] : vw->sf;
4885 	for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL &&
4886 		(sc->layers[vw->layer].validation_state&vs_selfintersects) &&
4887 		sc->layers[vw->layer].refs!=NULL &&
4888 		sc->layers[vw->layer].refs->next!=NULL &&
4889 		sc->layers[vw->layer].splines==NULL ) {
4890 	    sc->unlink_rm_ovrlp_save_undo = true;
4891 	    VW_Remetric(vw);
4892 	}
4893 	++k;
4894     } while ( k<vw->sf->subfontcnt );
4895 }
4896 
VWMenuManyCorrectDir(GWindow gw,struct gmenuitem * mi,GEvent * e)4897 static void VWMenuManyCorrectDir(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4898     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4899     SplineChar *sc;
4900     int k, gid;
4901     SplineFont *sf;
4902     RefChar *ref, *refnext;
4903     int changed;
4904 
4905     k=0;
4906     do {
4907 	sf = k<vw->sf->subfontcnt ? vw->sf->subfonts[k] : vw->sf;
4908 	for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL && (sc->layers[vw->layer].validation_state&vs_wrongdirection) ) {
4909 	    int vs = sc->layers[vw->layer].validation_state;
4910 
4911 	    SCPreserveLayer(sc,vw->layer,false);
4912 	    /* But a flipped reference is just wrong so I have no compunctions*/
4913 	    /*  about inlining it and then correcting its direction */
4914 
4915 	    for ( ref= sc->layers[vw->layer].refs; ref!=NULL; ref=refnext ) {
4916 		refnext = ref->next;
4917 		if ( ref->transform[0]*ref->transform[3]<0 ||
4918 			(ref->transform[0]==0 && ref->transform[1]*ref->transform[2]>0)) {
4919 		    SCRefToSplines(sc,ref,vw->layer);
4920 		}
4921 	    }
4922 	    sc->layers[vw->layer].splines = SplineSetsCorrect(sc->layers[vw->layer].splines,&changed);
4923 	    SCCharChangedUpdate(sc,vw->layer);
4924 	    SCValidate(vw->sc,vw->layer,true);
4925 	    if ( vs != vw->sc->layers[vw->layer].validation_state )
4926 		VW_Remetric(vw);
4927 	}
4928 	++k;
4929     } while ( k<vw->sf->subfontcnt );
4930 }
4931 
VWMenuManyGoodExtrema(GWindow gw,struct gmenuitem * mi,GEvent * e)4932 static void VWMenuManyGoodExtrema(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4933     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4934     SplineChar *sc;
4935     int k, gid;
4936     SplineFont *sf = vw->sf;
4937     int emsize = sf->ascent+sf->descent;
4938 
4939     k=0;
4940     do {
4941 	sf = k<vw->sf->subfontcnt ? vw->sf->subfonts[k] : vw->sf;
4942 	for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL && (sc->layers[vw->layer].validation_state&vs_missingextrema) ) {
4943 	    int vs = sc->layers[vw->layer].validation_state;
4944 
4945 	    SCPreserveLayer(sc,vw->layer,false);
4946 	    SplineCharAddExtrema(sc,sc->layers[vw->layer].splines,ae_only_good,emsize);
4947 	    SCCharChangedUpdate(sc,vw->layer);
4948 	    SCValidate(vw->sc,vw->layer,true);
4949 	    if ( vs != vw->sc->layers[vw->layer].validation_state )
4950 		VW_Remetric(vw);
4951 	}
4952 	++k;
4953     } while ( k<vw->sf->subfontcnt );
4954 }
4955 
VWMenuManyAllExtrema(GWindow gw,struct gmenuitem * mi,GEvent * e)4956 static void VWMenuManyAllExtrema(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4957     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4958     SplineChar *sc;
4959     int k, gid;
4960     SplineFont *sf = vw->sf;
4961     int emsize = sf->ascent+sf->descent;
4962 
4963     k=0;
4964     do {
4965 	sf = k<vw->sf->subfontcnt ? vw->sf->subfonts[k] : vw->sf;
4966 	for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL && (sc->layers[vw->layer].validation_state&vs_missingextrema) ) {
4967 	    int vs = sc->layers[vw->layer].validation_state;
4968 
4969 	    SCPreserveLayer(sc,vw->layer,false);
4970 	    SplineCharAddExtrema(sc,sc->layers[vw->layer].splines,ae_all,emsize);
4971 	    SCCharChangedUpdate(sc,vw->layer);
4972 	    SCValidate(vw->sc,vw->layer,true);
4973 	    if ( vs != vw->sc->layers[vw->layer].validation_state )
4974 		VW_Remetric(vw);
4975 	}
4976 	++k;
4977     } while ( k<vw->sf->subfontcnt );
4978 }
4979 
VWMenuManySimplify(GWindow gw,struct gmenuitem * mi,GEvent * e)4980 static void VWMenuManySimplify(GWindow gw,struct gmenuitem *mi,GEvent *e) {
4981     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
4982     SplineChar *sc;
4983     int k, gid;
4984     SplineFont *sf;
4985     static struct simplifyinfo smpl = { sf_normal, 0.75, 0.05, 0, -1, 0, 0 };
4986 
4987     k=0;
4988     do {
4989 	sf = k<vw->sf->subfontcnt ? vw->sf->subfonts[k] : vw->sf;
4990 	for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL && (sc->layers[vw->layer].validation_state&vs_toomanypoints) ) {
4991 	    int vs = sc->layers[vw->layer].validation_state;
4992 
4993 	    SCPreserveLayer(sc,vw->layer,false);
4994 	    sc->layers[vw->layer].splines = SplineCharSimplify(sc,sc->layers[vw->layer].splines,&smpl);
4995 	    SCCharChangedUpdate(sc,vw->layer);
4996 	    SCValidate(vw->sc,vw->layer,true);
4997 	    if ( vs != vw->sc->layers[vw->layer].validation_state )
4998 		VW_Remetric(vw);
4999 	}
5000 	++k;
5001     } while ( k<vw->sf->subfontcnt );
5002 }
5003 
5004 static GMenuItem vw_subfixup[] = {
5005     { { (unichar_t *) N_("problfixup|Open Contours"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, '\0', 0, NULL, NULL, VWMenuManyConnect, 0 },
5006     { { (unichar_t *) N_("problfixup|Self Intersections"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0', 0, NULL, NULL, VWMenuManyOverlap, 0 },
5007     { { (unichar_t *) N_("problfixup|Mark for Overlap fix before Save"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0', 0, NULL, NULL, VWMenuManyMark, 0 },
5008     { { (unichar_t *) N_("problfixup|Bad Directions"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0', 0, NULL, NULL, VWMenuManyCorrectDir, 0 },
5009     { { (unichar_t *) N_("problfixup|Missing Extrema (cautiously)"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0', 0, NULL, NULL, VWMenuManyGoodExtrema, 0 },
5010     { { (unichar_t *) N_("problfixup|Missing Extrema"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0', 0, NULL, NULL, VWMenuManyAllExtrema, 0 },
5011     { { (unichar_t *) N_("problfixup|Too Many Points"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0', 0, NULL, NULL, VWMenuManySimplify, 0 },
5012     GMENUITEM_EMPTY
5013 };
5014 
5015 static GMenuItem vw_popuplist[] = {
5016     { { (unichar_t *) N_("Close Open Contours"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, '\0', 0, NULL, NULL, VWMenuConnect, 0 },
5017     { { (unichar_t *) N_("Inline All References"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0', 0, NULL, NULL, VWMenuInlineRefs, 0 },
5018     { { (unichar_t *) N_("Remove Overlap"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0', 0, NULL, NULL, VWMenuOverlap, 0 },
5019     { { (unichar_t *) N_("Mark for Overlap fix before Save"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0', 0, NULL, NULL, VWMenuMark, 0 },
5020     { { (unichar_t *) N_("Inline Flipped References"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0', 0, NULL, NULL, VWMenuInlineFlippedRefs, 0 },
5021     { { (unichar_t *) N_("Correct Direction"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0', 0, NULL, NULL, VWMenuCorrectDir, 0 },
5022     { { (unichar_t *) N_("Add Good Extrema"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0',0, NULL, NULL, VWMenuGoodExtrema, 0 },
5023     { { (unichar_t *) N_("Add All Extrema"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0', 0, NULL, NULL, VWMenuAllExtrema, 0 },
5024     { { (unichar_t *) N_("Simplify"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 0 }, '\0', 0, NULL, NULL, VWMenuSimplify, 0 },
5025     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0' }, '\0', 0, NULL, NULL, NULL, 0 }, /* line */
5026     { { (unichar_t *) N_("Revalidate All"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, '\0', 0, NULL, NULL, VWMenuRevalidateAll, 0 },
5027     { { (unichar_t *) N_("Revalidate"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, '\0', 0, NULL, NULL, VWMenuRevalidate, 0 },
5028     { { (unichar_t *) N_("Open Glyph"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, '\0', 0, NULL, NULL, VWMenuOpenGlyph, 0 },
5029     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0' }, '\0', 0, NULL, NULL, NULL, 0 }, /* line */
5030     { { (unichar_t *) N_("Scroll To Glyph"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, '\0', 0, NULL, NULL, VWMenuGotoGlyph, 0 },
5031     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0' }, '\0', 0, NULL, NULL, NULL, 0 }, /* line */
5032     { { (unichar_t *) N_("Select Glyphs With"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, '\0', 0, vw_subselect, NULL, NULL, 0 },
5033     { { (unichar_t *) N_("Try To Fix Glyphs With"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, '\0', 0, vw_subfixup, NULL, NULL, 0 },
5034     GMENUITEM_EMPTY
5035 };
5036 
VWMouse(struct val_data * vw,GEvent * e)5037 static void VWMouse(struct val_data *vw, GEvent *e) {
5038     int skips;
5039     int gid = VW_FindLine(vw,vw->loff_top + e->u.mouse.y/vw->fh, &skips);
5040     SplineChar *sc;
5041     int k=0;
5042 
5043     if ( gid==-2 && e->u.mouse.clicks==2 && e->type==et_mouseup ) {
5044 	FontInfo(vw->sf,vw->layer,4,false);	/* Bring up the Private Dict */
5045 return;
5046     }
5047     if ( gid<0 )
5048 return;
5049     if ( vw->sf->subfontcnt==0 ) {
5050 	if ( (sc = vw->sf->glyphs[gid])==NULL )
5051 return;
5052     } else {
5053 	sc = NULL;
5054 	for ( k=0; k<vw->sf->subfontcnt; ++k ) {
5055 	    if ( gid<vw->sf->subfonts[k]->glyphcnt &&
5056 		    (sc=vw->sf->subfonts[k]->glyphs[gid])!=NULL )
5057 	break;
5058 	}
5059 	if ( sc==NULL )
5060 return;
5061     }
5062 
5063     if ( e->u.mouse.clicks==2 && e->type==et_mouseup ) {
5064 	VWReuseCV(vw,sc);
5065     } else if ( e->type==et_mouseup && e->u.mouse.x<10+vw->as && skips==0 ) {
5066 	sc->vs_open = !sc->vs_open;
5067 	VW_Remetric(vw);
5068     } else if ( e->type==et_mousedown && e->u.mouse.button==3 ) {
5069 	static int initted = false;
5070 	if ( !initted ) {
5071 	    int i;
5072 	    initted = true;
5073 
5074 	    for ( i=0; vw_popuplist[i].ti.text!=NULL || vw_popuplist[i].ti.line; ++i )
5075 		if ( vw_popuplist[i].ti.text!=NULL )
5076 		    vw_popuplist[i].ti.text = (unichar_t *) _( (char *)vw_popuplist[i].ti.text);
5077 
5078 	    for (i=0; vw_subfixup[i].ti.text!=NULL || vw_subfixup[i].ti.line; ++i )
5079 		if ( vw_subfixup[i].ti.text!=NULL )
5080 		    vw_subfixup[i].ti.text = (unichar_t *) S_( (char *)vw_subfixup[i].ti.text);
5081 
5082 	    for (i=0; vw_subselect[i].ti.text!=NULL || vw_subselect[i].ti.line; ++i )
5083 		if ( vw_subselect[i].ti.text!=NULL )
5084 		    vw_subselect[i].ti.text = (unichar_t *) S_( (char *)vw_subselect[i].ti.text);
5085 	}
5086 	vw_popuplist[0].ti.disabled = (sc->layers[vw->layer].validation_state&vs_opencontour)?0:1;
5087 	vw_popuplist[1].ti.disabled = (SCValidate(sc,vw->layer,true)&vs_selfintersects)?0:1;
5088 	vw_popuplist[2].ti.disabled = (SCValidate(sc,vw->layer,true)&vs_selfintersects)?0:1;
5089 	vw_popuplist[3].ti.disabled = (SCValidate(sc,vw->layer,true)&vs_selfintersects) &&
5090 		    sc->layers[vw->layer].refs!=NULL?0:1;
5091 	vw_popuplist[4].ti.disabled = (sc->layers[vw->layer].validation_state&vs_flippedreferences)?0:1;
5092 	vw_popuplist[5].ti.disabled = (sc->layers[vw->layer].validation_state&vs_wrongdirection)?0:1;
5093 	vw_popuplist[6].ti.disabled = (sc->layers[vw->layer].validation_state&vs_missingextrema)?0:1;
5094 	vw_popuplist[7].ti.disabled = (sc->layers[vw->layer].validation_state&vs_missingextrema)?0:1;
5095 	vw_popuplist[8].ti.disabled = (sc->layers[vw->layer].validation_state&vs_toomanypoints)?0:1;
5096 	vw->sc = sc;
5097 	GMenuCreatePopupMenu(vw->v,e, vw_popuplist);
5098     }
5099 }
5100 
VWDrawWindow(GWindow pixmap,struct val_data * vw,GEvent * e)5101 static void VWDrawWindow(GWindow pixmap,struct val_data *vw, GEvent *e) {
5102     int gid,k, cidmax = vw->cidmax;
5103     SplineFont *sub, *sf=vw->sf;
5104     SplineChar *sc;
5105     int sofar;
5106     int bit, skips, vs, y, m;
5107     GRect old, r;
5108 
5109     GDrawPushClip(pixmap,&e->u.expose.rect,&old);
5110     GDrawSetFont(pixmap,vw->font);
5111     gid = VW_FindLine(vw,vw->loff_top, &skips);
5112     if ( gid==-1 ) {
5113 	GDrawDrawText8(pixmap,2,(vw->vlcnt-1)*vw->fh/2 + vw->as,
5114 		vw->finished_first_pass ? _("Passed Validation") : _("Thinking..."),
5115 		-1,0x000000 );
5116 	GDrawPopClip(pixmap,&old);
5117 return;
5118     }
5119 
5120     y = vw->as - skips*vw->fh;
5121     sofar = -skips;
5122     r.width = r.height = vw->as;
5123     if ( gid!=-2 ) {
5124 	for ( ; gid<cidmax && sofar<vw->vlcnt ; ++gid ) {
5125 	    if ( sf->subfontcnt==0 )
5126 		sc = sf->glyphs[gid];
5127 	    else {
5128 		for ( k=0; k<sf->subfontcnt; ++k ) {
5129 		    sub = sf->subfonts[k];
5130 		    if ( gid<sub->glyphcnt && (sc = sub->glyphs[gid])!=NULL )
5131 		break;
5132 		}
5133 	    }
5134 	    /* Ignore it if it has not been validated */
5135 	    /* Ignore it if it is good */
5136 	    vs = VSModMask(sc,vw);
5137 	    if ((vs&vs_known) && (vs&vw->mask)!=0 ) {
5138 		r.x = 2;   r.y = y-vw->as+1;
5139 		GDrawDrawRect(pixmap,&r,0x000000);
5140 		GDrawDrawLine(pixmap,r.x+2,r.y+vw->as/2,r.x+vw->as-2,r.y+vw->as/2,
5141 			0x000000);
5142 		if ( !sc->vs_open )
5143 		    GDrawDrawLine(pixmap,r.x+vw->as/2,r.y+2,r.x+vw->as/2,r.y+vw->as-2,
5144 			    0x000000);
5145 		GDrawDrawText8(pixmap,r.x+r.width+2,y,sc->name,-1,0x000000 );
5146 		y += vw->fh;
5147 		++sofar;
5148 		if ( sc->vs_open ) {
5149 		    for ( m=0, bit=(vs_known<<1) ; bit<=vs_last; ++m, bit<<=1 )
5150 			if ( (bit&vw->mask) && (vs&bit) && vserrornames[m]!=NULL ) {
5151 			    GDrawDrawText8(pixmap,10+r.width+r.x,y,_(vserrornames[m]),-1,0xff0000 );
5152 			    y += vw->fh;
5153 			    ++sofar;
5154 			}
5155 		}
5156 	    }
5157 	}
5158     }
5159     if ( sofar<vw->vlcnt ) {
5160 	vs = ValidatePrivate(sf);
5161 	if ( !vw->needs_blue )
5162 	    vs &= ~pds_missingblue;
5163 	if ( vs!=0 ) {
5164 	    /* GT: "Private" is a keyword (sort of) in PostScript. Perhaps it */
5165 	    /* GT: should remain untranslated? */
5166 	    GDrawDrawText8(pixmap,r.x+r.width+2,y,_("Private Dictionary"),-1,0x000000 );
5167 	    y += vw->fh;
5168 	    for ( m=0, bit=1 ; bit!=0; ++m, bit<<=1 ) {
5169 		if ( vs&bit ) {
5170 		    GDrawDrawText8(pixmap,10+r.width+r.x,y,_(privateerrornames[m]),-1,0xff0000 );
5171 		    y += vw->fh;
5172 		}
5173 	    }
5174 	}
5175     }
5176     GDrawPopClip(pixmap,&old);
5177 }
5178 
VWCheckup(struct val_data * vw)5179 static int VWCheckup(struct val_data *vw) {
5180     /* Check some glyphs to see what their validation state is or if they have*/
5181     /*  changed */
5182     int gid, k, cnt=0;
5183     int max;
5184     SplineFont *sf=vw->sf, *sub;
5185     int cntmax = vw->finished_first_pass ? 40 : 60;
5186     SplineChar *sc;
5187     int a_change = false;
5188     int firstv = true;
5189     char *buts[4];
5190 
5191     if ( sf->subfontcnt==0 )
5192 	max = vw->cidmax = sf->glyphcnt;
5193     else
5194 	max = vw->cidmax;
5195 
5196     for ( gid=vw->laststart; gid<max && cnt<cntmax && gid<vw->laststart+2000; ++gid ) {
5197 	if ( sf->subfontcnt==0 )
5198 	    sc = sf->glyphs[gid];
5199 	else {
5200 	    for ( k=0; k<sf->subfontcnt; ++k ) {
5201 		sub = sf->subfonts[k];
5202 		if ( gid<sub->glyphcnt && (sc = sub->glyphs[gid])!=NULL )
5203 	    break;
5204 	    }
5205 	}
5206 	if ( sc!=NULL && !(sc->layers[vw->layer].validation_state&vs_known)) {
5207 	    if ( firstv ) {
5208 		GDrawSetCursor(vw->v,ct_watch);
5209 		GDrawSync(NULL);
5210 		firstv = false;
5211 	    }
5212 	    SCValidate(sc,vw->layer,true);
5213 	    ++cnt;
5214 	}
5215 	if ( sc!=NULL && vw->need_to_check_with_user_on_mask &&
5216 		(sc->layers[vw->layer].validation_state&vs_nonintegral )) {
5217 	    vw->need_to_check_with_user_on_mask = false;
5218 	    buts[0] = _("Report as Error"); buts[1]=_("Ignore"); buts[2] = NULL;
5219 	    if ( ff_ask(_("Not sure if this is an error..."),(const char **) buts,0,1,
5220 		    _("This font contains non-integral coordinates. That's OK\n"
5221 			"in PostScript and SVG but causes problems in TrueType.\n"
5222 			"Should I consider that an error here?"))==0 ) {
5223 		a_change = true;
5224 		vw->mask |= vs_nonintegral;
5225 	    }
5226 	}
5227 	if ( sc!=NULL && sc->layers[vw->layer].validation_state!=sc->layers[vw->layer].old_vs ) {
5228 	    a_change = true;
5229 	    sc->layers[vw->layer].old_vs = sc->layers[vw->layer].validation_state;
5230 	}
5231     }
5232     if ( gid<max )
5233 	vw->laststart = gid;
5234     else {
5235 	vw->laststart = 0;
5236 	if ( !vw->finished_first_pass ) {
5237 	    vw->finished_first_pass = true;
5238 	    GDrawCancelTimer(vw->recheck);
5239 	    /* Check less frequently now we've completed a full scan */
5240 	    vw->recheck = GDrawRequestTimer(vw->v,3000,3000,NULL);
5241 	}
5242     }
5243     if ( a_change )
5244 	VW_Remetric(vw);
5245     if ( !firstv )
5246 	GDrawSetCursor(vw->v,ct_mypointer);
5247 return( a_change );
5248 }
5249 
VW_OK(GGadget * g,GEvent * e)5250 static int VW_OK(GGadget *g, GEvent *e) {
5251 
5252     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
5253 	struct val_data *vw = (struct val_data *) GDrawGetUserData(GGadgetGetWindow(g));
5254 	GDrawDestroyWindow(vw->gw);
5255     }
5256 return( true );
5257 }
5258 
vwv_e_h(GWindow gw,GEvent * event)5259 static int vwv_e_h(GWindow gw, GEvent *event) {
5260     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
5261 
5262     switch ( event->type ) {
5263       case et_expose:
5264 	if ( vw->recheck==NULL ) {
5265 	    vw->recheck = GDrawRequestTimer(vw->v,500,500,NULL);
5266 	    VWCheckup(vw);
5267 	}
5268 	VWDrawWindow(gw,vw,event);
5269       break;
5270       case et_mouseup:
5271       case et_mousedown:
5272       case et_mousemove:
5273 	if (( event->type==et_mouseup || event->type==et_mousedown ) &&
5274 		(event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
5275 return( GGadgetDispatchEvent(vw->vsb,event));
5276 	}
5277 	VWMouse(vw,event);
5278       break;
5279       case et_char:
5280 return( false );
5281       break;
5282       case et_resize: {
5283 	int vlcnt = event->u.resize.size.height/vw->fh;
5284 	vw->vlcnt = vlcnt;
5285 	VW_SetSb(vw);
5286 	GDrawRequestExpose(vw->v,NULL,false);
5287       } break;
5288       case et_timer:
5289 	VWCheckup(vw);
5290       break;
5291     }
5292 return( true );
5293 }
5294 
vw_e_h(GWindow gw,GEvent * event)5295 static int vw_e_h(GWindow gw, GEvent *event) {
5296     struct val_data *vw = (struct val_data *) GDrawGetUserData(gw);
5297 
5298     if ( event->type==et_close ) {
5299 	GDrawDestroyWindow(gw);
5300     } else if ( event->type == et_char ) {
5301 return( false );
5302     } else if ( event->type == et_destroy ) {
5303 	if ( vw->sf!=NULL )
5304 	    vw->sf->valwin = NULL;
5305 	chunkfree(vw,sizeof(*vw));
5306     }
5307 return( true );
5308 }
5309 
SFValidationWindow(SplineFont * sf,int layer,enum fontformat format)5310 void SFValidationWindow(SplineFont *sf,int layer,enum fontformat format) {
5311     GWindowAttrs wattrs;
5312     GRect pos;
5313     GWindow gw;
5314     GGadgetCreateData gcd[4], boxes[4], *harray[4], *butarray[8], *varray[3];
5315     GTextInfo label[4];
5316     struct val_data *valwin;
5317     char buffer[200];
5318     int k, gid;
5319     int cidmax;
5320     SplineFont *sub;
5321     SplineChar *sc;
5322     FontRequest rq;
5323     int as, ds, ld;
5324     int mask, needs_blue;
5325     static GFont *valfont=NULL;
5326 
5327     if ( sf->cidmaster )
5328 	sf = sf->cidmaster;
5329     mask = VSMaskFromFormat(sf,layer,format);
5330     needs_blue = (mask==vs_maskps || mask==vs_maskcid);
5331 
5332     if ( sf->valwin!=NULL ) {
5333 	/* Don't need to force a revalidation because if the window exists */
5334 	/*  it's been doing that all by itself */
5335 	if ( mask!=(sf->valwin->mask&~vs_nonintegral) ) {
5336 	    /* But if we go from postscript to truetype the types of errors */
5337 	    /*  change, so what we display might be different */
5338 	    sf->valwin->mask = mask;
5339 	    sf->valwin->needs_blue = needs_blue;
5340 	    sf->valwin->layer = layer;
5341 	    VW_Remetric(sf->valwin);
5342 	}
5343 	GDrawSetVisible(sf->valwin->gw,true);
5344 	GDrawRaise(sf->valwin->gw);
5345 return;
5346     }
5347 
5348     if ( sf->subfontcnt!=0 ) {
5349 	cidmax = 0;
5350 	for ( k=0; k<sf->subfontcnt; ++k )
5351 	    if ( sf->subfonts[k]->glyphcnt > cidmax )
5352 		cidmax = sf->subfonts[k]->glyphcnt;
5353     } else
5354 	cidmax = sf->glyphcnt;
5355 
5356     /* Init all glyphs as undrawn */
5357     for ( gid=0; gid<cidmax ; ++gid ) {
5358 	if ( sf->subfontcnt==0 )
5359 	    sc = sf->glyphs[gid];
5360 	else {
5361 	    for ( k=0; k<sf->subfontcnt; ++k ) {
5362 		sub = sf->subfonts[k];
5363 		if ( gid<sub->glyphcnt && (sc = sub->glyphs[gid])!=NULL )
5364 	    break;
5365 	    }
5366 	}
5367 	if ( sc!=NULL ) {
5368 	    sc->layers[layer].old_vs = 0;
5369 	    sc->vs_open = true;		/* should this default to false? */
5370 	}
5371     }
5372 
5373     valwin = chunkalloc(sizeof(struct val_data));
5374     valwin->sf = sf;
5375     valwin->mask = mask;
5376     valwin->needs_blue = needs_blue;
5377     valwin->cidmax = cidmax;
5378     valwin->lastgid = -1;
5379     valwin->layer = layer;
5380     valwin->need_to_check_with_user_on_mask = (format==ff_none && !sf->layers[layer].order2 );
5381 
5382     memset(&wattrs,0,sizeof(wattrs));
5383     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg;
5384     wattrs.event_masks = -1;
5385     wattrs.cursor = ct_mypointer;
5386     sprintf( buffer, _("Validation of %.100s"), sf->fontname );
5387     wattrs.utf8_window_title = buffer;
5388     wattrs.is_dlg = true;
5389     wattrs.undercursor = 1;
5390     pos.x = pos.y = 0;
5391     pos.width = GDrawPointsToPixels(NULL,200);
5392     pos.height = GDrawPointsToPixels(NULL,300);
5393     valwin->gw = gw = GDrawCreateTopWindow(NULL,&pos,vw_e_h,valwin,&wattrs);
5394 
5395     if ( valfont==NULL ) {
5396 	memset(&rq,0,sizeof(rq));
5397 	rq.utf8_family_name = "Helvetica";
5398 	rq.point_size = 11;
5399 	rq.weight = 400;
5400 	valfont = GDrawInstanciateFont(gw,&rq);
5401 	valfont = GResourceFindFont("Validate.Font",valfont);
5402     }
5403     valwin->font = valfont;
5404     GDrawWindowFontMetrics(valwin->gw,valwin->font,&as,&ds,&ld);
5405     valwin->fh = as+ds;
5406     valwin->as = as;
5407 
5408     memset(&label,0,sizeof(label));
5409     memset(&gcd,0,sizeof(gcd));
5410     memset(&boxes,0,sizeof(boxes));
5411 
5412     k = 0;
5413     gcd[k].gd.flags = gg_visible | gg_enabled;
5414     gcd[k].gd.u.drawable_e_h = vwv_e_h;
5415     gcd[k++].creator = GDrawableCreate;
5416 
5417     gcd[k].gd.flags = gg_visible | gg_enabled | gg_sb_vert;
5418     gcd[k].gd.handle_controlevent = VW_VScroll;
5419     gcd[k++].creator = GScrollBarCreate;
5420     harray[0] = &gcd[k-2]; harray[1] = &gcd[k-1]; harray[2] = NULL; harray[3] = NULL;
5421 
5422     gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
5423     label[k].text = (unichar_t *) _("_OK");
5424     label[k].text_is_1byte = true;
5425     label[k].text_in_resource = true;
5426     gcd[k].gd.label = &label[k];
5427     gcd[k].gd.handle_controlevent = VW_OK;
5428     gcd[k++].creator = GButtonCreate;
5429     butarray[0] = GCD_Glue; butarray[1] = &gcd[k-1]; butarray[2] = GCD_Glue; butarray[3] = NULL;
5430 
5431     boxes[2].gd.flags = gg_enabled|gg_visible;
5432     boxes[2].gd.u.boxelements = harray;
5433     boxes[2].creator = GHVGroupCreate;
5434 
5435     boxes[3].gd.flags = gg_enabled|gg_visible;
5436     boxes[3].gd.u.boxelements = butarray;
5437     boxes[3].creator = GHBoxCreate;
5438     varray[0] = &boxes[2]; varray[1] = &boxes[3]; varray[2] = NULL;
5439 
5440     boxes[0].gd.flags = gg_enabled|gg_visible;
5441     boxes[0].gd.u.boxelements = varray;
5442     boxes[0].creator = GVBoxCreate;
5443 
5444     GGadgetsCreate(gw,boxes);
5445     valwin->vsb = gcd[1].ret;
5446     valwin->v = GDrawableGetWindow(gcd[0].ret);
5447     GHVBoxSetExpandableRow(boxes[0].ret,0);
5448     GHVBoxSetExpandableCol(boxes[2].ret,0);
5449     GHVBoxSetPadding(boxes[2].ret,0,0);
5450     GHVBoxSetExpandableCol(boxes[3].ret,gb_expandglue);
5451     GHVBoxFitWindow(boxes[0].ret);
5452 
5453     GDrawSetVisible(gw,true);
5454 }
5455 
ValidationDestroy(SplineFont * sf)5456 void ValidationDestroy(SplineFont *sf) {
5457     if ( sf->valwin!=NULL ) {
5458 	sf->valwin->sf = NULL;
5459 	GDrawDestroyWindow(sf->valwin->gw);
5460 	sf->valwin = NULL;
5461     }
5462 }
5463