LCOV - code coverage report
Current view: top level - fontforgeexe - problems.c (source / functions) Hit Total Coverage
Test: FontForge coverage report 2017-08-04 01:21:11+02:00 (commit d35f7e4107a9e1db65cce47c468fcc914cecb8fd) Lines: 0 3715 0.0 %
Date: 2017-08-04 Functions: 0 85 0.0 %

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

Generated by: LCOV version 1.10