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