1 /*
2 * Copyright 2014 Adobe Systems Incorporated (http://www.adobe.com/).
3 * All Rights Reserved.
4 *
5 * This software is licensed as OpenSource, under the Apache License, Version
6 * 2.0.
7 * This license is available at: http://opensource.org/licenses/Apache-2.0.
8 */
9
10 #include "ac.h"
11 #include "bbox.h"
12
13 static void DoHStems(HintVal* sLst1);
14 static void DoVStems(HintVal* sLst);
15
16 static bool CounterFailed;
17
18 void
InitAll(int32_t reason)19 InitAll(int32_t reason)
20 {
21 InitData(reason); /* must be first */
22 InitFix(reason);
23 InitGen(reason);
24 InitPick(reason);
25 }
26
27 static int32_t
PtLstLen(HintPoint * lst)28 PtLstLen(HintPoint* lst)
29 {
30 int32_t cnt = 0;
31 while (lst != NULL) {
32 cnt++;
33 lst = lst->next;
34 }
35 return cnt;
36 }
37
38 static int32_t
PointListCheck(HintPoint * new,HintPoint * lst)39 PointListCheck(HintPoint* new, HintPoint* lst)
40 {
41 /* -1 means not a member, 1 means already a member, 0 means conflicts */
42 Fixed l1 = 0, l2 = 0, n1 = 0, n2 = 0, tmp, halfMargin;
43 char ch = new->c;
44 halfMargin = FixHalfMul(gBandMargin);
45 halfMargin = FixHalfMul(halfMargin);
46 /* DEBUG 8 BIT. In the previous version, with 7 bit fraction coordinates
47 instead of the current
48 8 bit, bandMargin is declared as 30, but scaled by half to match the 7 bit
49 fraction coordinate -> a value of 15.
50 In the current version this scaling doesn't happen. However, in this part of
51 the code, the hint values are scaled up to 8 bits of fraction even in the
52 original version, but topBand is applied without correcting for the scaling
53 difference. In this version I need to divide by half again in order to get
54 to the same value. I think the original is a bug, but it has been working
55 for 30 years, so I am not going to change the test now.
56 */
57 switch (ch) {
58 case 'y':
59 case 'm': {
60 n1 = new->x0;
61 n2 = new->x1;
62 break;
63 }
64 case 'b':
65 case 'v': {
66 n1 = new->y0;
67 n2 = new->y1;
68 break;
69 }
70 default: {
71 LogMsg(LOGERROR, NONFATALERROR, "Illegal character in point list.");
72 }
73 }
74 if (n1 > n2) {
75 tmp = n1;
76 n1 = n2;
77 n2 = tmp;
78 }
79 while (true) {
80 if (lst == NULL) {
81 return -1;
82 }
83 if (lst->c == ch) { /* same kind of hint */
84 switch (ch) {
85 case 'y':
86 case 'm': {
87 l1 = lst->x0;
88 l2 = lst->x1;
89 break;
90 }
91 case 'b':
92 case 'v': {
93 l1 = lst->y0;
94 l2 = lst->y1;
95 break;
96 }
97 }
98 if (l1 > l2) {
99 tmp = l1;
100 l1 = l2;
101 l2 = tmp;
102 }
103 if (l1 == n1 && l2 == n2) {
104 return 1;
105 }
106 /* Add this extra margin to the band to fix a problem in
107 TimesEuropa/Italic/v,w,y where a main hstem hint was
108 being merged with newhints. This main hstem caused
109 problems in rasterization so it shouldn't be included. */
110 l1 -= halfMargin;
111 l2 += halfMargin;
112 if (l1 <= n2 && n1 <= l2) {
113 return 0;
114 }
115 }
116 lst = lst->next;
117 }
118 }
119
120 static bool
SameHintLists(HintPoint * lst1,HintPoint * lst2)121 SameHintLists(HintPoint* lst1, HintPoint* lst2)
122 {
123 if (PtLstLen(lst1) != PtLstLen(lst2)) {
124 return false;
125 }
126 while (lst1 != NULL) { /* go through lst1 */
127 if (PointListCheck(lst1, lst2) != 1) {
128 return false;
129 }
130 lst1 = lst1->next;
131 }
132 return true;
133 }
134
135 bool
SameHints(int32_t cn1,int32_t cn2)136 SameHints(int32_t cn1, int32_t cn2)
137 {
138 if (cn1 == cn2) {
139 return true;
140 }
141 return SameHintLists(gPtLstArray[cn1], gPtLstArray[cn2]);
142 }
143
144 void
MergeFromMainHints(char ch)145 MergeFromMainHints(char ch)
146 {
147 HintPoint* lst;
148 for (lst = gPtLstArray[0]; lst != NULL; lst = lst->next) {
149 if (lst->c != ch) {
150 continue;
151 }
152 if (PointListCheck(lst, gPointList) == -1) {
153 if (ch == 'b') {
154 AddHintPoint(0, lst->y0, 0, lst->y1, ch, lst->p0, lst->p1);
155 } else {
156 AddHintPoint(lst->x0, 0, lst->x1, 0, ch, lst->p0, lst->p1);
157 }
158 }
159 }
160 }
161
162 void
AddHintPoint(Fixed x0,Fixed y0,Fixed x1,Fixed y1,char ch,PathElt * p0,PathElt * p1)163 AddHintPoint(Fixed x0, Fixed y0, Fixed x1, Fixed y1, char ch, PathElt* p0,
164 PathElt* p1)
165 {
166 HintPoint* pt;
167 int32_t chk;
168 pt = (HintPoint*)Alloc(sizeof(HintPoint));
169 pt->x0 = x0;
170 pt->y0 = y0;
171 pt->x1 = x1;
172 pt->y1 = y1;
173 pt->c = ch;
174 pt->done = false;
175 pt->next = NULL;
176 pt->p0 = p0;
177 pt->p1 = p1;
178 chk = PointListCheck(pt, gPointList);
179 if (chk == 0) {
180 ReportHintConflict(x0, y0, x1, y1, ch);
181 }
182 if (chk == -1) {
183 pt->next = gPointList;
184 gPointList = pt;
185 LogHintInfo(gPointList);
186 }
187 }
188
189 static void
CopyHintFromLst(char hint,HintPoint * lst)190 CopyHintFromLst(char hint, HintPoint* lst)
191 {
192 bool bvflg = (hint == 'b' || hint == 'v');
193 while (lst != NULL) {
194 if (lst->c == hint) {
195 if (bvflg) {
196 AddHintPoint(0, lst->y0, 0, lst->y1, hint, lst->p0, lst->p1);
197 } else {
198 AddHintPoint(lst->x0, 0, lst->x1, 0, hint, lst->p0, lst->p1);
199 }
200 }
201 lst = lst->next;
202 }
203 }
204
205 void
CopyMainV(void)206 CopyMainV(void)
207 {
208 CopyHintFromLst('m', gPtLstArray[0]);
209 }
210
211 void
CopyMainH(void)212 CopyMainH(void)
213 {
214 CopyHintFromLst('v', gPtLstArray[0]);
215 }
216
217 void
AddHPair(HintVal * v,char ch)218 AddHPair(HintVal* v, char ch)
219 {
220 Fixed bot, top;
221 PathElt *p0, *p1, *p;
222 bot = -v->vLoc1;
223 top = -v->vLoc2;
224 p0 = v->vBst->vSeg1->sElt;
225 p1 = v->vBst->vSeg2->sElt;
226 if (top < bot) {
227 Fixed tmp = top;
228 top = bot;
229 bot = tmp;
230 p = p0;
231 p0 = p1;
232 p1 = p;
233 }
234 if (v->vGhst) {
235 if (v->vSeg1->sType == sGHOST) {
236 bot = top;
237 p0 = p1;
238 p1 = NULL;
239 top = bot - FixInt(20); /* width == -20 iff bottom seg is ghost */
240 } else {
241 top = bot;
242 p1 = p0;
243 p0 = NULL;
244 bot = top + FixInt(21); /* width == -21 iff top seg is ghost */
245 }
246 }
247 AddHintPoint(0, bot, 0, top, ch, p0, p1);
248 }
249
250 void
AddVPair(HintVal * v,char ch)251 AddVPair(HintVal* v, char ch)
252 {
253 Fixed lft, rght;
254 PathElt *p0, *p1, *p;
255 lft = v->vLoc1;
256 rght = v->vLoc2;
257 p0 = v->vBst->vSeg1->sElt;
258 p1 = v->vBst->vSeg2->sElt;
259 if (lft > rght) {
260 Fixed tmp = lft;
261 lft = rght;
262 rght = tmp;
263 p = p0;
264 p0 = p1;
265 p1 = p;
266 }
267 AddHintPoint(lft, 0, rght, 0, ch, p0, p1);
268 }
269
270 static bool
UseCounter(HintVal * sLst,bool mhint)271 UseCounter(HintVal* sLst, bool mhint)
272 {
273 int32_t cnt = 0;
274 Fixed minLoc, midLoc, maxLoc, prevBstVal, bestVal;
275 Fixed minDelta, midDelta, maxDelta, th;
276 HintVal *lst, *newLst;
277 minLoc = midLoc = maxLoc = FixInt(20000);
278 minDelta = midDelta = maxDelta = 0;
279 lst = sLst;
280 while (lst != NULL) {
281 cnt++;
282 lst = lst->vNxt;
283 }
284 if (cnt < 3) {
285 return false;
286 }
287 cnt -= 3;
288 prevBstVal = 0;
289 while (cnt > 0) {
290 cnt--;
291 if (cnt == 0) {
292 prevBstVal = sLst->vVal;
293 }
294 sLst = sLst->vNxt;
295 }
296 bestVal = sLst->vVal;
297 if (prevBstVal > FixInt(1000) || bestVal < prevBstVal * 10) {
298 return false;
299 }
300 newLst = sLst;
301 while (sLst != NULL) {
302 Fixed loc = sLst->vLoc1;
303 Fixed delta = sLst->vLoc2 - loc;
304 loc += FixHalfMul(delta);
305 if (loc < minLoc) {
306 maxLoc = midLoc;
307 maxDelta = midDelta;
308 midLoc = minLoc;
309 midDelta = minDelta;
310 minLoc = loc;
311 minDelta = delta;
312 } else if (loc < midLoc) {
313 maxLoc = midLoc;
314 maxDelta = midDelta;
315 midLoc = loc;
316 midDelta = delta;
317 } else {
318 maxLoc = loc;
319 maxDelta = delta;
320 }
321 sLst = sLst->vNxt;
322 }
323 th = FixInt(5) / 100;
324 if (abs(minDelta - maxDelta) < th &&
325 abs((maxLoc - midLoc) - (midLoc - minLoc)) < th) {
326 if (mhint) {
327 gVHinting = newLst;
328 } else {
329 gHHinting = newLst;
330 }
331 return true;
332 }
333 if (abs(minDelta - maxDelta) < FixInt(3) &&
334 abs((maxLoc - midLoc) - (midLoc - minLoc)) < FixInt(3)) {
335 LogMsg(INFO, OK,
336 mhint ? "Near miss for using V counter hinting."
337 : "Near miss for using H counter hinting.");
338 }
339 return false;
340 }
341
342 static void
GetNewPtLst(void)343 GetNewPtLst(void)
344 {
345 if (gNumPtLsts >= gMaxPtLsts) { /* increase size */
346 HintPoint** newArray;
347 int32_t i;
348 int32_t newSize = gMaxPtLsts * 2;
349 newArray = (HintPoint**)Alloc(newSize * sizeof(HintPoint*));
350 for (i = 0; i < gMaxPtLsts; i++) {
351 newArray[i] = gPtLstArray[i];
352 }
353 gPtLstArray = newArray;
354 gMaxPtLsts = newSize;
355 }
356 gPtLstIndex = gNumPtLsts;
357 gNumPtLsts++;
358 gPointList = NULL;
359 gPtLstArray[gPtLstIndex] = NULL;
360 }
361
362 void
XtraHints(PathElt * e)363 XtraHints(PathElt* e)
364 {
365 /* this can be simplified for standalone hinting */
366 gPtLstArray[gPtLstIndex] = gPointList;
367 if (e->newhints == 0) {
368 GetNewPtLst();
369 e->newhints = (int16_t)gPtLstIndex;
370 }
371 gPtLstIndex = e->newhints;
372 gPointList = gPtLstArray[gPtLstIndex];
373 }
374
375 static void
Blues(unsigned char * links)376 Blues(unsigned char* links)
377 {
378 Fixed pv = 0, pd = 0, pc = 0, pb = 0, pa = 0;
379 HintVal* sLst;
380
381 /*
382 * Top alignment zones are in the global 'gTopBands', bottom in
383 * 'gBotBands'.
384 *
385 * This function looks through the path, as defined by the linked list of
386 * PathElt's, starting at the global 'gPathStart', and adds to several
387 * lists. Coordinates are stored in the PathElt.(x,y) as (original
388 * value)/2.0, aka right shifted by 1 bit from the original 24.8 Fixed. I
389 * suspect that is to allow a larger integer portion - when this program
390 * was written, an int was 16 bits.
391 *
392 * 'gHStems' and 'gVStems' are global arrays of Fixed 24.8 numbers..
393 *
394 * 'gSegLists' is an array of 4 HintSeg linked lists. List 0 and 1 are
395 * respectively up and down vertical segments. Lists 2 and 3 are
396 * respectively left pointing and right pointing horizontal segments. On a
397 * counter-clockwise path, this is the same as selecting top and bottom
398 * stem locations.
399 *
400 * NoBlueGlyph() consults a hard-coded list of glyph names, if the glyph is
401 * in this list, set the alignment zones ('gTopBands' and 'gBotBands') to
402 * empty.
403 *
404 * 1) gen.c:GenHPts()
405 * Builds the raw list of stem segments in global
406 * 'topList' and 'botList'. It steps through the liked list of path
407 * segments, starting at 'gPathStart'. It decides if a path is mostly H,
408 * and if so, adds it to a linked list of hstem candidates in gSegLists,
409 * by calling gen.c:AddHSegment(). This calls ReportAddHSeg() (useful in
410 * debugging), and then gen.c:AddSegment().
411 *
412 * If the path segment is in fact entirely vertical and is followed by a
413 * sharp bend, gen.c:GenHPts() adds two new path segments just 1 unit
414 * long, after the segment end point, called H/VBends (segment type
415 * sBend=1). I have no idea what these are for.
416 *
417 * AddSegment() is pretty simple. It creates a new hint segment
418 * (HintSeg) for the parent PathElt, fills it in, adds it to appropriate
419 * list of the 4 gSegLists, and then sorts by hstem location. seg->sElt
420 * is the parent PathElt, seg->sType is the type, seg->sLoc is the
421 * location in Fixed 18.14: right shift 7 to get integer value.
422 *
423 * If the current PathElt is a Closepath, It also calls LinkSegment() to
424 * add the current stem segment to the list of stem segments referenced
425 * by this elt's e->Hs/Vs.
426 *
427 * Note that a hint segment is created for each nearly vertical or
428 * horizontal PathElt. This means that in an H, there will be two hint
429 * segments created for the bottom and top of the H, as there are two
430 * horizontal paths with the same Y at the top and bottom of the H.
431 *
432 * Assign the top and bottom Hstem location lists.
433 * topList = segLists[2]
434 * botList = segLists[3];
435 *
436 * 2) eval.c::EvalH()
437 * Evaluates every combination of botList and topList, and assign a
438 * priority value and a 'Q' value.
439 *
440 * For each bottom stem
441 * for each top stem
442 * 1) assign priority (spc) and weight (val) values with EvalHPair()
443 * 2) report stem near misses in the 'HStems' list with HStemMiss()
444 * 3) decide whether to add pair to 'HStems' list with AddHValue()
445 *
446 * Add ghost hints.
447 * For each bottom stem segment and then for each top stem segment:
448 * if it is in an alignment zone, make a ghost hint segment and add it
449 * with AddHValue().
450 *
451 * EvalHPair() sets priority (spc) and weight (val) values.
452 * Omit pair by setting value to 0 if:
453 * bottom is in bottom alignment zone, and top is in top alignment
454 * zone. (otherwise, these will override the ghost hints).
455 *
456 * Boost priority by +2 if either the bot or top segment is in an
457 * alignment zone.
458 *
459 * dy = stem width ( top - bot)
460 *
461 * Calculate dist. Dist is set to a fudge factor * dy.
462 * if bottom segment xo->x1 overlaps top x0->x1, the fudge factor is
463 * 1.0. The less the overlap, the larger the fduge factor.
464 * if bottom segment xo->x1 overlaps top x0->x1:.
465 * if top and bottom overlap exactly, dist = dy
466 * if they barely overlap, dist = 1.4*dy
467 * in between, interpolate.
468 * else, look at closest ends betwen bottom and top segments.
469 * dx = min X separation between top and bottom segments.
470 * dist = 1.4 *dy
471 * dist += dx*dx
472 * if dx > dy:
473 * dist *= dx / dy;
474 *
475 * Look through the gHStems global list. For each match to dy, boost
476 * priority by +1.
477 *
478 * Calculate weight with gen.c:AdjustVal()
479 * if dy is more than twice the 1.1.5* the largest hint in gHStems,
480 * set weight to 0.
481 * Calculate weight as related to length of the segments squared
482 * divided by the distance squared.
483 * Basically, the greater the ratio segment overlap to stem width,
484 * the higher the value.
485 * if dy is greater than the largest stem hint in gHStems, decrease
486 * the value scale weight by of * (largest stem hint in
487 * gHStems)/dy)**3.
488 *
489 * AddHValue() decides whether add a (bottom, top) pair of hint segments.
490 * Do not add the pair if:
491 * if weight (val) is 0,
492 * if both are sBEND segments
493 * if neither are a ghost hint, and weight <= pruneD and priority (spc)
494 * is <= 0:
495 * if either is an sBEND: skip
496 * if the BBox for one segment is the same or inside the BBox for the
497 * other: skip
498 *
499 * else add it with eval.c:InsertHValue()
500 * add new HintVal to global valList.
501 * item->vVal = val; # weight
502 * item->initVal = val; # originl weight from EvalHPair()
503 * item->vSpc = spc; # priority
504 * item->vLoc1 = bot; # bottom Y value in Fixed 18.14
505 * item->vLoc2 = top; # top Y value in Fixed 18.14
506 * item->vSeg1 = bSeg; # bottom hint segment
507 * item->vSeg2 = tSeg; # top hint segment
508 * item->vGhst = ghst; # if it is a ghost segment.
509 * The new item is inserted after the first element where vlist->vLoc2 >= top
510 * and vlist->vLoc1 >= bottom
511 *
512 * 3) merge.c:PruneHVals();
513 *
514 * item2 in the list knocks out item1 if:
515 * 1) (item2 val is more than 3* greater than item1 val) and
516 * (val 1 is less than FixedInt(100)) and
517 * (item2 top and bottom is within item 1 top and bottom) and
518 * (if val1 is more than 50* less than val2 and either top
519 * seg1 is close to top seg 2, or bottom seg1 is close to
520 * bottom seg 2) and
521 * (val 1 < FixInt(16)) or
522 * ((item1 top not in blue zone, or top1 = top2) and
523 * (item1 bottom not in blue zone, or top1 = bottom2))
524 * "Close to" for the bottom segment means you can get to the bottom elt for
525 * item 2 from bottom elt for 1 within the same path, by
526 * stepping either forward or back from item 1's elt, and without going
527 * outside the bounds between
528 * location 1 and location 2. Same for top segments.
529 *
530 * 4) pick.c:FindBestHVals();
531 * When a hint segment
532 */
533
534 LogMsg(LOGDEBUG, OK, "generate blues");
535 if (NoBlueGlyph()) {
536 gLenTopBands = gLenBotBands = 0;
537 }
538 GenHPts();
539 LogMsg(LOGDEBUG, OK, "evaluate");
540 if (!CounterFailed && HHintGlyph()) {
541 pv = gPruneValue;
542 gPruneValue = (Fixed)gMinVal;
543 pa = gPruneA;
544 gPruneA = (Fixed)gMinVal;
545 pd = gPruneD;
546 gPruneD = (Fixed)gMinVal;
547 pc = gPruneC;
548 gPruneC = (Fixed)gMaxVal;
549 pb = gPruneB;
550 gPruneB = (Fixed)gMinVal;
551 }
552 EvalH();
553 PruneHVals();
554 FindBestHVals();
555 MergeVals(false);
556
557 ShowHVals(gValList);
558 LogMsg(LOGDEBUG, OK, "pick best");
559 MarkLinks(gValList, true, links);
560 CheckVals(gValList, false);
561
562 /* Report stems and alignment zones, if this has been requested. */
563 if (gDoAligns || gDoStems)
564 DoHStems(gValList);
565
566 /* Moves best HintVal items from valList to Hhinting list.
567 * (? Choose from set of HintVals for the samte stem values.) */
568 PickHVals(gValList);
569
570 if (!CounterFailed && HHintGlyph()) {
571 gPruneValue = pv;
572 gPruneD = pd;
573 gPruneC = pc;
574 gPruneB = pb;
575 gPruneA = pa;
576 gUseH = UseCounter(gHHinting, false);
577 if (!gUseH) { /* try to fix */
578 AddBBoxHV(true, true);
579 gUseH = UseCounter(gHHinting, false);
580 if (!gUseH) { /* still bad news */
581 LogMsg(INFO, OK,
582 "Glyph is in list for using H counter hints, "
583 "but didn't find any candidates.");
584 CounterFailed = true;
585 }
586 }
587 } else {
588 gUseH = false;
589 }
590 if (gHHinting == NULL) {
591 AddBBoxHV(true, false);
592 }
593 LogMsg(LOGDEBUG, OK, "results");
594 LogMsg(LOGDEBUG, OK, gUseH ? "rv" : "rb");
595 ShowHVals(gHHinting);
596 if (gUseH) {
597 LogMsg(INFO, OK, "Using H counter hints.");
598 }
599 sLst = gHHinting;
600 while (sLst != NULL) {
601 AddHPair(sLst, gUseH ? 'v' : 'b'); /* actually adds hint */
602 sLst = sLst->vNxt;
603 }
604 }
605
606 static void
DoHStems(HintVal * sLst1)607 DoHStems(HintVal* sLst1)
608 {
609 Fixed glyphTop = INT32_MIN, glyphBot = INT32_MAX;
610 bool curved;
611
612 while (sLst1 != NULL) {
613 Fixed bot = -sLst1->vLoc1;
614 Fixed top = -sLst1->vLoc2;
615 if (top < bot) {
616 Fixed tmp = top;
617 top = bot;
618 bot = tmp;
619 }
620 if (top > glyphTop)
621 glyphTop = top;
622 if (bot < glyphBot)
623 glyphBot = bot;
624
625 /* skip if ghost or not a line on top or bottom */
626 if (!sLst1->vGhst) {
627 curved = !FindLineSeg(sLst1->vLoc1, botList) &&
628 !FindLineSeg(sLst1->vLoc2, topList);
629 AddHStem(top, bot, curved);
630 if (top != INT32_MIN || bot != INT32_MAX)
631 AddStemExtremes(bot, top);
632 }
633
634 sLst1 = sLst1->vNxt;
635 }
636
637 if (glyphTop != INT32_MIN || glyphBot != INT32_MAX)
638 AddGlyphExtremes(glyphBot, glyphTop);
639 }
640
641 static void
Yellows(unsigned char * links)642 Yellows(unsigned char* links)
643 {
644 Fixed pv = 0, pd = 0, pc = 0, pb = 0, pa = 0;
645 HintVal* sLst;
646 LogMsg(LOGDEBUG, OK, "generate yellows");
647 GenVPts(SpecialGlyphType());
648 LogMsg(LOGDEBUG, OK, "evaluate");
649 if (!CounterFailed && VHintGlyph()) {
650 pv = gPruneValue;
651 gPruneValue = (Fixed)gMinVal;
652 pa = gPruneA;
653 gPruneA = (Fixed)gMinVal;
654 pd = gPruneD;
655 gPruneD = (Fixed)gMinVal;
656 pc = gPruneC;
657 gPruneC = (Fixed)gMaxVal;
658 pb = gPruneB;
659 gPruneB = (Fixed)gMinVal;
660 }
661 EvalV();
662 PruneVVals();
663 FindBestVVals();
664 MergeVals(true);
665 ShowVVals(gValList);
666 LogMsg(LOGDEBUG, OK, "pick best");
667 MarkLinks(gValList, false, links);
668 CheckVals(gValList, true);
669
670 if (gDoAligns || gDoStems)
671 DoVStems(gValList);
672
673 PickVVals(gValList);
674 if (!CounterFailed && VHintGlyph()) {
675 gPruneValue = pv;
676 gPruneD = pd;
677 gPruneC = pc;
678 gPruneB = pb;
679 gPruneA = pa;
680 gUseV = UseCounter(gVHinting, true);
681 if (!gUseV) { /* try to fix */
682 AddBBoxHV(false, true);
683 gUseV = UseCounter(gVHinting, true);
684 if (!gUseV) { /* still bad news */
685 LogMsg(INFO, OK,
686 "Glyph is in list for using V counter hints, "
687 "but didn't find any candidates.");
688 CounterFailed = true;
689 }
690 }
691 } else {
692 gUseV = false;
693 }
694 if (gVHinting == NULL) {
695 AddBBoxHV(false, false);
696 }
697 LogMsg(LOGDEBUG, OK, "results");
698 LogMsg(LOGDEBUG, OK, gUseV ? "rm" : "ry");
699 ShowVVals(gVHinting);
700 if (gUseV) {
701 LogMsg(INFO, OK, "Using V counter hints.");
702 }
703 sLst = gVHinting;
704 while (sLst != NULL) {
705 AddVPair(sLst, gUseV ? 'm' : 'y');
706 sLst = sLst->vNxt;
707 }
708 }
709
710 static void
DoVStems(HintVal * sLst)711 DoVStems(HintVal* sLst)
712 {
713 while (sLst != NULL) {
714 Fixed lft, rght;
715 bool curved;
716 curved = !FindLineSeg(sLst->vLoc1, leftList) &&
717 !FindLineSeg(sLst->vLoc2, rightList);
718 lft = sLst->vLoc1;
719 rght = sLst->vLoc2;
720 if (lft > rght) {
721 Fixed tmp = lft;
722 lft = rght;
723 rght = tmp;
724 }
725 AddVStem(rght, lft, curved);
726 sLst = sLst->vNxt;
727 }
728 }
729
730 static void
RemoveRedundantFirstHints(void)731 RemoveRedundantFirstHints(void)
732 {
733 PathElt* e;
734 if (gNumPtLsts < 2 || !SameHints(0, 1)) {
735 return;
736 }
737 e = gPathStart;
738 while (e != NULL) {
739 if (e->newhints == 1) {
740 e->newhints = 0;
741 return;
742 }
743 e = e->next;
744 }
745 }
746
747 static void
AddHintsSetup(void)748 AddHintsSetup(void)
749 {
750 int i;
751 gVBigDist = 0;
752 for (i = 0; i < gNumVStems; i++) {
753 if (gVStems[i] > gVBigDist) {
754 gVBigDist = gVStems[i];
755 }
756 }
757 if (gVBigDist < gInitBigDist) {
758 gVBigDist = gInitBigDist;
759 }
760 gVBigDist = (gVBigDist * 23) / 20;
761 acfixtopflt(gVBigDist, &gVBigDistR);
762 gHBigDist = 0;
763 for (i = 0; i < gNumHStems; i++) {
764 if (gHStems[i] > gHBigDist) {
765 gHBigDist = gHStems[i];
766 }
767 }
768 gHBigDist = abs(gHBigDist);
769 if (gHBigDist < gInitBigDist) {
770 gHBigDist = gInitBigDist;
771 }
772 gHBigDist = (gHBigDist * 23) / 20;
773 acfixtopflt(gHBigDist, &gHBigDistR);
774 if (gRoundToInt) {
775 RoundPathCoords();
776 }
777 CheckForMultiMoveTo();
778 /* PreCheckForSolEol(); */
779 }
780
781 /* If extrahint is true then it is ok to have multi-level
782 hinting. */
783 static void
AddHintsInnerLoop(const char * srcglyph,bool extrahint)784 AddHintsInnerLoop(const char* srcglyph, bool extrahint)
785 {
786 int32_t retryHinting = 0;
787 unsigned char* links;
788
789 while (true) {
790 PreGenPts();
791 CheckSmooth();
792 links = InitShuffleSubpaths();
793 Blues(links);
794 if (!gDoAligns) {
795 Yellows(links);
796 }
797 if (gEditGlyph) {
798 DoShuffleSubpaths(links);
799 }
800 gHPrimary = CopyHints(gHHinting);
801 gVPrimary = CopyHints(gVHinting);
802 PruneElementHintSegs();
803 ListHintInfo();
804 if (extrahint) {
805 AutoExtraHints(MoveToNewHints());
806 }
807 gPtLstArray[gPtLstIndex] = gPointList;
808 retryHinting++;
809 /* we want to retry hinting if
810 `1) CounterFailed or
811 2) doFixes changed something, but in both cases, only on the first
812 pass.
813 */
814 if (CounterFailed && retryHinting == 1) {
815 goto retry;
816 }
817 if (retryHinting > 1) {
818 break;
819 }
820 retry:
821 /* if we are doing the stem and zones reporting, we need to discard the
822 * reported. */
823 if (gReportRetryCB != NULL) {
824 gReportRetryCB(gReportRetryUserData);
825 }
826 if (gPathStart == NULL || gPathStart == gPathEnd) {
827 LogMsg(LOGERROR, NONFATALERROR, "No glyph path.");
828 }
829
830 /* SaveFile(); SaveFile is always called in AddHintsCleanup, so this is
831 * a duplciate */
832 InitAll(RESTART);
833 if (gWriteHintedBez && !ReadGlyph(srcglyph, false, false)) {
834 break;
835 }
836 AddHintsSetup();
837 if (!PreCheckForHinting()) {
838 break;
839 }
840 if (gFlexOK) {
841 gHasFlex = false;
842 AutoAddFlex();
843 }
844 }
845 }
846
847 static void
AddHintsCleanup(void)848 AddHintsCleanup(void)
849 {
850 RemoveRedundantFirstHints();
851 if (gWriteHintedBez) {
852
853 if (gPathStart == NULL || gPathStart == gPathEnd) {
854 LogMsg(LOGERROR, NONFATALERROR,
855 "The glyph path vanished while adding hints.");
856 } else {
857 SaveFile();
858 }
859 }
860 InitAll(RESTART);
861 }
862
863 static void
AddHints(const char * srcglyph,bool extrahint)864 AddHints(const char* srcglyph, bool extrahint)
865 {
866 if (gPathStart == NULL || gPathStart == gPathEnd) {
867 LogMsg(INFO, OK, "No glyph path, so no hints.");
868 SaveFile(); /* make sure it gets saved with no hinting */
869 return;
870 }
871 CounterFailed = gBandError = false;
872 CheckPathBBox();
873 CheckForDups();
874 AddHintsSetup();
875 if (!PreCheckForHinting()) {
876 return;
877 }
878 if (gFlexOK) {
879 gHasFlex = false;
880 AutoAddFlex();
881 }
882 AddHintsInnerLoop(srcglyph, extrahint);
883 AddHintsCleanup();
884 }
885
886 bool
AutoHintGlyph(const char * srcglyph,bool extrahint)887 AutoHintGlyph(const char* srcglyph, bool extrahint)
888 {
889 int32_t lentop = gLenTopBands, lenbot = gLenBotBands;
890 if (!ReadGlyph(srcglyph, false, false)) {
891 LogMsg(LOGERROR, NONFATALERROR, "Cannot parse glyph.");
892 }
893 AddHints(srcglyph, extrahint);
894 gLenTopBands = lentop;
895 gLenBotBands = lenbot;
896 return true;
897 }
898