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 bool mergeMain;
14 
15 static PathElt*
GetSubPathNxt(PathElt * e)16 GetSubPathNxt(PathElt* e)
17 {
18     if (e->type == CLOSEPATH)
19         return GetDest(e);
20     return e->next;
21 }
22 
23 static PathElt*
GetSubPathPrv(PathElt * e)24 GetSubPathPrv(PathElt* e)
25 {
26     if (e->type == MOVETO)
27         e = GetClosedBy(e);
28     return e->prev;
29 }
30 
31 static HintVal*
FindClosestVal(HintVal * sLst,Fixed loc)32 FindClosestVal(HintVal* sLst, Fixed loc)
33 {
34     Fixed dist = FixInt(10000);
35     HintVal* best = NULL;
36     while (sLst != NULL) {
37         Fixed bot, top, d;
38         bot = sLst->vLoc1;
39         top = sLst->vLoc2;
40         if (bot > top) {
41             Fixed tmp = bot;
42             bot = top;
43             top = tmp;
44         }
45         if (loc >= bot && loc <= top) {
46             best = sLst;
47             break;
48         }
49         if (loc < bot)
50             d = bot - loc;
51         else
52             d = loc - top;
53         if (d < dist) {
54             dist = d;
55             best = sLst;
56         }
57         sLst = sLst->vNxt;
58     }
59     return best;
60 }
61 
62 static void
CpyHHint(PathElt * e)63 CpyHHint(PathElt* e)
64 {
65     Fixed x1, y1;
66     HintVal* best;
67     GetEndPoint(e, &x1, &y1);
68     best = FindClosestVal(gHPrimary, y1);
69     if (best != NULL)
70         AddHPair(best, 'b');
71 }
72 
73 static void
CpyVHint(PathElt * e)74 CpyVHint(PathElt* e)
75 {
76     Fixed x1, y1;
77     HintVal* best;
78     GetEndPoint(e, &x1, &y1);
79     best = FindClosestVal(gVPrimary, x1);
80     if (best != NULL)
81         AddVPair(best, 'y');
82 }
83 
84 static void
PruneHintSegs(PathElt * e,bool hFlg)85 PruneHintSegs(PathElt* e, bool hFlg)
86 {
87     SegLnkLst *lst, *nxt, *prv;
88     HintSeg* seg;
89     lst = hFlg ? e->Hs : e->Vs;
90     prv = NULL;
91     while (lst != NULL) {
92         HintVal* val = NULL;
93         SegLnk* lnk = lst->lnk;
94         if (lnk != NULL) {
95             seg = lnk->seg;
96             if (seg != NULL)
97                 val = seg->sLnk;
98         }
99         nxt = lst->next;
100         if (val == NULL) { /* prune this one */
101             if (prv == NULL) {
102                 if (hFlg)
103                     e->Hs = nxt;
104                 else
105                     e->Vs = nxt;
106             } else
107                 prv->next = nxt;
108             lst = nxt;
109         } else {
110             prv = lst;
111             lst = nxt;
112         }
113     }
114 }
115 
116 void
PruneElementHintSegs(void)117 PruneElementHintSegs(void)
118 {
119     PathElt* e;
120     e = gPathStart;
121     while (e != NULL) {
122         PruneHintSegs(e, true);
123         PruneHintSegs(e, false);
124         e = e->next;
125     }
126 }
127 
128 #define ElmntHintSegLst(e, hFlg) (hFlg) ? (e)->Hs : (e)->Vs
129 
130 static void
RemLnk(PathElt * e,bool hFlg,SegLnkLst * rm)131 RemLnk(PathElt* e, bool hFlg, SegLnkLst* rm)
132 {
133     SegLnkLst *lst, *prv, *nxt;
134     lst = hFlg ? e->Hs : e->Vs;
135     prv = NULL;
136     while (lst != NULL) {
137         nxt = lst->next;
138         if (lst == rm) {
139             if (prv == NULL) {
140                 if (hFlg)
141                     e->Hs = nxt;
142                 else
143                     e->Vs = nxt;
144             } else
145                 prv->next = nxt;
146             return;
147         }
148         prv = lst;
149         lst = nxt;
150     }
151     LogMsg(LOGERROR, NONFATALERROR, "Badly formatted segment list.");
152 }
153 
154 static bool
AlreadyOnList(HintVal * v,HintVal * lst)155 AlreadyOnList(HintVal* v, HintVal* lst)
156 {
157     while (lst != NULL) {
158         if (v == lst)
159             return true;
160         lst = lst->vNxt;
161     }
162     return false;
163 }
164 
165 static void
AutoVSeg(HintVal * sLst)166 AutoVSeg(HintVal* sLst)
167 {
168     AddVPair(sLst, 'y');
169 }
170 
171 static void
AutoHSeg(HintVal * sLst)172 AutoHSeg(HintVal* sLst)
173 {
174     AddHPair(sLst, 'b');
175 }
176 
177 static void
AddHHinting(HintVal * h)178 AddHHinting(HintVal* h)
179 {
180     if (gUseH || AlreadyOnList(h, gHHinting))
181         return;
182     h->vNxt = gHHinting;
183     gHHinting = h;
184     AutoHSeg(h);
185 }
186 
187 static void
AddVHinting(HintVal * v)188 AddVHinting(HintVal* v)
189 {
190     if (gUseV || AlreadyOnList(v, gVHinting))
191         return;
192     v->vNxt = gVHinting;
193     gVHinting = v;
194     AutoVSeg(v);
195 }
196 
197 static int32_t
TestHint(HintSeg * s,HintVal * hintList,bool flg,bool doLst)198 TestHint(HintSeg* s, HintVal* hintList, bool flg, bool doLst)
199 {
200     /* -1 means already in hintList; 0 means conflicts; 1 means ok to add */
201     HintVal *v, *clst;
202     Fixed top, bot, vT, vB, loc;
203     if (s == NULL)
204         return -1;
205     v = s->sLnk;
206     loc = s->sLoc;
207     if (v == NULL)
208         return -1;
209     vT = top = v->vLoc2;
210     vB = bot = v->vLoc1;
211     if (v->vGhst) { /* collapse width for conflict test */
212         if (v->vSeg1->sType == sGHOST)
213             bot = top;
214         else
215             top = bot;
216     }
217     {
218         int32_t cnt = 0;
219         clst = hintList;
220         while (clst != NULL) {
221             if (++cnt > 100) {
222                 LogMsg(LOGDEBUG, OK, "Loop in hintlist for TestHint.");
223                 return 0;
224             }
225             clst = clst->vNxt;
226         }
227     }
228     if (v->vGhst) {
229         bool loc1;
230         /* if best value for segment uses a ghost, and
231            segment loc is already in the hintList, then return -1 */
232         clst = hintList;
233         if (abs(loc - vT) < abs(loc - vB)) {
234             loc1 = false;
235             loc = vT;
236         } else {
237             loc1 = true;
238             loc = vB;
239         }
240         while (clst != NULL) {
241             if ((loc1 ? clst->vLoc1 : clst->vLoc2) == loc)
242                 return -1;
243             clst = clst->vNxt;
244             if (!doLst)
245                 break;
246         }
247     }
248     if (flg) {
249         top += gBandMargin;
250         bot -= gBandMargin;
251     } else {
252         top -= gBandMargin;
253         bot += gBandMargin;
254     }
255     while (hintList != NULL) { /* check for conflict */
256         Fixed cTop = hintList->vLoc2;
257         Fixed cBot = hintList->vLoc1;
258         if (vB == cBot && vT == cTop) {
259             return -1;
260         }
261         if (hintList->vGhst) { /* collapse width for conflict test */
262             if (hintList->vSeg1->sType == sGHOST)
263                 cBot = cTop;
264             else
265                 cTop = cBot;
266         }
267         if ((flg && (cBot <= top) && (cTop >= bot)) ||
268             (!flg && (cBot >= top) && (cTop <= bot))) {
269             return 0;
270         }
271         hintList = hintList->vNxt;
272         if (!doLst)
273             break;
274     }
275     return 1;
276 }
277 
278 #define TestHHintLst(h) TestHintLst(h, gHHinting, false, true)
279 #define TestVHintLst(v) TestHintLst(v, gVHinting, true, true)
280 
281 int
TestHintLst(SegLnkLst * lst,HintVal * hintList,bool flg,bool doLst)282 TestHintLst(SegLnkLst* lst, HintVal* hintList, bool flg, bool doLst)
283 {
284     /* -1 means already in hintList; 0 means conflicts; 1 means ok to add */
285     int result, cnt;
286     result = -1;
287     cnt = 0;
288     while (lst != NULL) {
289         int i = TestHint(lst->lnk->seg, hintList, flg, doLst);
290         if (i == 0) {
291             result = 0;
292             break;
293         }
294         if (i == 1)
295             result = 1;
296         lst = lst->next;
297         if (++cnt > 100) {
298             LogMsg(WARNING, OK, "Looping in TestHintLst.");
299             return 0;
300         }
301     }
302     return result;
303 }
304 
305 #define FixedMidPoint(m, a, b)                                                 \
306     (m).x = ((a).x + (b).x) >> 1;                                              \
307     (m).y = ((a).y + (b).y) >> 1
308 
309 #define FixedBezDiv(a0, a1, a2, a3, b0, b1, b2, b3)                            \
310     FixedMidPoint(b2, a2, a3);                                                 \
311     FixedMidPoint(a3, a1, a2);                                                 \
312     FixedMidPoint(a1, a0, a1);                                                 \
313     FixedMidPoint(a2, a1, a3);                                                 \
314     FixedMidPoint(b1, a3, b2);                                                 \
315     FixedMidPoint(a3, a2, b1);
316 
317 bool
ResolveConflictBySplit(PathElt * e,bool Hflg,SegLnkLst * lnk1,SegLnkLst * lnk2)318 ResolveConflictBySplit(PathElt* e, bool Hflg, SegLnkLst* lnk1, SegLnkLst* lnk2)
319 {
320     /* insert new pathelt immediately following e */
321     /* e gets first half of split; new gets second */
322     /* e gets lnk1 in Hs or Vs; new gets lnk2 */
323     PathElt* new;
324     Cd d0, d1, d2, d3, d4, d5, d6, d7;
325     if (e->type != CURVETO || e->isFlex)
326         return false;
327     ReportSplit(e);
328     new = (PathElt*)Alloc(sizeof(PathElt));
329     new->next = e->next;
330     e->next = new;
331     new->prev = e;
332     if (new->next == NULL)
333         gPathEnd = new;
334     else
335         new->next->prev = new;
336     if (Hflg) {
337         e->Hs = lnk1;
338         new->Hs = lnk2;
339     } else {
340         e->Vs = lnk1;
341         new->Vs = lnk2;
342     }
343     if (lnk1 != NULL)
344         lnk1->next = NULL;
345     if (lnk2 != NULL)
346         lnk2->next = NULL;
347     new->type = CURVETO;
348     GetEndPoint(e->prev, &d0.x, &d0.y);
349     d1.x = e->x1;
350     d1.y = e->y1;
351     d2.x = e->x2;
352     d2.y = e->y2;
353     d3.x = e->x3;
354     d3.y = e->y3;
355     d4 = d0;
356     d5 = d1;
357     d6 = d2;
358     d7 = d3;
359     new->x3 = d3.x;
360     new->y3 = d3.y;
361     FixedBezDiv(d4, d5, d6, d7, d0, d1, d2, d3);
362     e->x1 = d5.x;
363     e->y1 = d5.y;
364     e->x2 = d6.x;
365     e->y2 = d6.y;
366     e->x3 = d7.x;
367     e->y3 = d7.y;
368     new->x1 = d1.x;
369     new->y1 = d1.y;
370     new->x2 = d2.x;
371     new->y2 = d2.y;
372     return true;
373 }
374 
375 static void
RemDupLnks(PathElt * e,bool Hflg)376 RemDupLnks(PathElt* e, bool Hflg)
377 {
378     SegLnkLst *l1, *l2, *l2nxt;
379     l1 = Hflg ? e->Hs : e->Vs;
380     while (l1 != NULL) {
381         l2 = l1->next;
382         while (l2 != NULL) {
383             l2nxt = l2->next;
384             if (l1->lnk->seg == l2->lnk->seg)
385                 RemLnk(e, Hflg, l2);
386             l2 = l2nxt;
387         }
388         l1 = l1->next;
389     }
390 }
391 
392 #define OkToRemLnk(loc, Hflg, spc)                                             \
393     (!(Hflg) || (spc) == 0 ||                                                  \
394      (!InBlueBand((loc), gLenTopBands, gTopBands) &&                           \
395       !InBlueBand((loc), gLenBotBands, gBotBands)))
396 
397 /* The changes made here were to fix a problem in MinisterLight/E.
398    The top left point was not getting hinted. */
399 static bool
TryResolveConflict(PathElt * e,bool Hflg)400 TryResolveConflict(PathElt* e, bool Hflg)
401 {
402     int32_t typ;
403     SegLnkLst *lst, *lnk1, *lnk2;
404     HintSeg *seg, *seg1, *seg2;
405     HintVal *val1, *val2;
406     Fixed lc1, lc2, loc0, loc1, loc2, loc3, x0, y0, x1, y1;
407     RemDupLnks(e, Hflg);
408     typ = e->type;
409     if (typ == MOVETO)
410         GetEndPoints(GetClosedBy(e), &x0, &y0, &x1, &y1);
411     else if (typ == CURVETO) {
412         x0 = e->x1;
413         y0 = e->y1;
414         x1 = e->x3;
415         y1 = e->y3;
416     } else
417         GetEndPoints(e, &x0, &y0, &x1, &y1);
418     loc1 = Hflg ? y0 : x0;
419     loc2 = Hflg ? y1 : x1;
420     lst = Hflg ? e->Hs : e->Vs;
421     seg1 = lst->lnk->seg;
422     lc1 = seg1->sLoc;
423     lnk1 = lst;
424     lst = lst->next;
425     seg2 = lst->lnk->seg;
426     lc2 = seg2->sLoc;
427     lnk2 = lst;
428     if (lc1 == loc1 || lc2 == loc2) {
429         /* do nothing */
430     } else if (abs(lc1 - loc1) > abs(lc1 - loc2) ||
431                abs(lc2 - loc2) > abs(lc2 - loc1)) {
432         seg = seg1;
433         seg1 = seg2;
434         seg2 = seg;
435         lst = lnk1;
436         lnk1 = lnk2;
437         lnk2 = lst;
438     }
439     val1 = seg1->sLnk;
440     val2 = seg2->sLnk;
441     if (val1->vVal < FixInt(50) && OkToRemLnk(loc1, Hflg, val1->vSpc)) {
442         RemLnk(e, Hflg, lnk1);
443         ReportRemConflict(e);
444         return true;
445     }
446     if (val2->vVal < FixInt(50) && val1->vVal > val2->vVal * 20 &&
447         OkToRemLnk(loc2, Hflg, val2->vSpc)) {
448         RemLnk(e, Hflg, lnk2);
449         ReportRemConflict(e);
450         return true;
451     }
452     if (typ != CURVETO || ((((Hflg && IsHorizontal(x0, y0, x1, y1)) ||
453                              (!Hflg && IsVertical(x0, y0, x1, y1)))) &&
454                            OkToRemLnk(loc1, Hflg, val1->vSpc))) {
455         RemLnk(e, Hflg, lnk1);
456         ReportRemConflict(e);
457         return true;
458     }
459     GetEndPoints(GetSubPathPrv(e), &x0, &y0, &x1, &y1);
460     loc0 = Hflg ? y0 : x0;
461     if (ProdLt0(loc2 - loc1, loc0 - loc1)) {
462         RemLnk(e, Hflg, lnk1);
463         ReportRemConflict(e);
464         return true;
465     }
466     GetEndPoint(GetSubPathNxt(e), &x1, &y1);
467     loc3 = Hflg ? y1 : x1;
468     if (ProdLt0(loc3 - loc2, loc1 - loc2)) {
469         RemLnk(e, Hflg, lnk2);
470         ReportRemConflict(e);
471         return true;
472     }
473     if ((loc2 == val2->vLoc1 || loc2 == val2->vLoc2) && loc1 != val1->vLoc1 &&
474         loc1 != val1->vLoc2) {
475         RemLnk(e, Hflg, lnk1);
476         ReportRemConflict(e);
477         return true;
478     }
479     if ((loc1 == val1->vLoc1 || loc1 == val1->vLoc2) && loc2 != val2->vLoc1 &&
480         loc2 != val2->vLoc2) {
481         RemLnk(e, Hflg, lnk2);
482         ReportRemConflict(e);
483         return true;
484     }
485     if (gEditGlyph && ResolveConflictBySplit(e, Hflg, lnk1, lnk2))
486         return true;
487     else
488         return false;
489 }
490 
491 static bool
CheckHintSegs(PathElt * e,bool flg,bool Hflg)492 CheckHintSegs(PathElt* e, bool flg, bool Hflg)
493 {
494     SegLnkLst* lst;
495     SegLnkLst* lst2;
496     HintSeg* seg;
497     HintVal* val;
498     lst = Hflg ? e->Hs : e->Vs;
499     while (lst != NULL) {
500         lst2 = lst->next;
501         if (lst2 != NULL) {
502             seg = lst->lnk->seg;
503             val = seg->sLnk;
504             if (val != NULL && TestHintLst(lst2, val, flg, false) == 0) {
505                 if (TryResolveConflict(e, Hflg))
506                     return CheckHintSegs(e, flg, Hflg);
507                 if (Hflg)
508                     e->Hs = NULL;
509                 else
510                     e->Vs = NULL;
511                 return true;
512             }
513         }
514         lst = lst2;
515     }
516     return false;
517 }
518 
519 static void
CheckElmntHintSegs(void)520 CheckElmntHintSegs(void)
521 {
522     PathElt* e;
523     e = gPathStart;
524     while (e != NULL) {
525         if (!CheckHintSegs(e, false, true))
526             CheckHintSegs(e, true, false);
527         e = e->next;
528     }
529 }
530 static bool
HintLstsClash(SegLnkLst * lst1,SegLnkLst * lst2,bool flg)531 HintLstsClash(SegLnkLst* lst1, SegLnkLst* lst2, bool flg)
532 {
533     while (lst1 != NULL) {
534         HintSeg* seg = lst1->lnk->seg;
535         HintVal* val = seg->sLnk;
536         if (val != NULL) {
537             SegLnkLst* lst = lst2;
538             while (lst != NULL) {
539                 if (TestHintLst(lst, val, flg, false) == 0) {
540                     return true;
541                 }
542                 lst = lst->next;
543             }
544         }
545         lst1 = lst1->next;
546     }
547     return false;
548 }
549 
550 static SegLnkLst*
BestFromLsts(SegLnkLst * lst1,SegLnkLst * lst2)551 BestFromLsts(SegLnkLst* lst1, SegLnkLst* lst2)
552 {
553     SegLnkLst* bst = NULL;
554     Fixed bstval = 0;
555     int32_t i;
556     for (i = 0; i < 2; i++) {
557         SegLnkLst* lst = i ? lst1 : lst2;
558         while (lst != NULL) {
559             HintSeg* seg = lst->lnk->seg;
560             HintVal* val = seg->sLnk;
561             if (val != NULL && val->vVal > bstval) {
562                 bst = lst;
563                 bstval = val->vVal;
564             }
565             lst = lst->next;
566         }
567     }
568     return bst;
569 }
570 
571 static bool
HintsClash(PathElt * e,PathElt * p,SegLnkLst ** hLst,SegLnkLst ** vLst,SegLnkLst ** phLst,SegLnkLst ** pvLst)572 HintsClash(PathElt* e, PathElt* p, SegLnkLst** hLst, SegLnkLst** vLst,
573            SegLnkLst** phLst, SegLnkLst** pvLst)
574 {
575     bool clash = false;
576     SegLnkLst *bst, *new;
577     if (HintLstsClash(*hLst, *phLst, false)) {
578         clash = true;
579         bst = BestFromLsts(*hLst, *phLst);
580         if (bst) {
581             new = (SegLnkLst*)Alloc(sizeof(SegLnkLst));
582             new->next = NULL;
583             new->lnk = bst->lnk;
584         } else
585             new = NULL;
586         e->Hs = p->Hs = *hLst = *phLst = new;
587     }
588     if (HintLstsClash(*vLst, *pvLst, true)) {
589         clash = true;
590         bst = BestFromLsts(*vLst, *pvLst);
591         if (bst) {
592             new = (SegLnkLst*)Alloc(sizeof(SegLnkLst));
593             new->next = NULL;
594             new->lnk = bst->lnk;
595         } else
596             new = NULL;
597         e->Vs = p->Vs = *vLst = *pvLst = new;
598     }
599     return clash;
600 }
601 
602 static void
GetHintLsts(PathElt * e,SegLnkLst ** phLst,SegLnkLst ** pvLst,int32_t * ph,int32_t * pv)603 GetHintLsts(PathElt* e, SegLnkLst** phLst, SegLnkLst** pvLst, int32_t* ph,
604             int32_t* pv)
605 {
606     SegLnkLst *hLst, *vLst;
607     int32_t h, v;
608     if (gUseH) {
609         hLst = NULL;
610         h = -1;
611     } else {
612         hLst = e->Hs;
613         if (hLst == NULL)
614             h = -1;
615         else
616             h = TestHHintLst(hLst);
617     }
618     if (gUseV) {
619         vLst = NULL;
620         v = -1;
621     } else {
622         vLst = e->Vs;
623         if (vLst == NULL)
624             v = -1;
625         else
626             v = TestVHintLst(vLst);
627     }
628     *pvLst = vLst;
629     *phLst = hLst;
630     *ph = h;
631     *pv = v;
632 }
633 
634 static void
ReHintBounds(PathElt * e)635 ReHintBounds(PathElt* e)
636 {
637     if (!gUseH) {
638         if (gHHinting == NULL)
639             CpyHHint(e);
640         if (mergeMain)
641             MergeFromMainHints('b');
642     }
643     if (!gUseV) {
644         if (gVHinting == NULL)
645             CpyVHint(e);
646         if (mergeMain)
647             MergeFromMainHints('y');
648     }
649 }
650 
651 static void
AddHintLst(SegLnkLst * lst,bool vert)652 AddHintLst(SegLnkLst* lst, bool vert)
653 {
654     while (lst != NULL) {
655         HintSeg* seg = lst->lnk->seg;
656         HintVal* val = seg->sLnk;
657         if (vert)
658             AddVHinting(val);
659         else
660             AddHHinting(val);
661         lst = lst->next;
662     }
663 }
664 
665 static void
StartNewHinting(PathElt * e,SegLnkLst * hLst,SegLnkLst * vLst)666 StartNewHinting(PathElt* e, SegLnkLst* hLst, SegLnkLst* vLst)
667 {
668     ReHintBounds(e);
669     if (e->newhints != 0) {
670         LogMsg(LOGERROR, NONFATALERROR, "Uninitialized extra hints list.");
671     }
672     XtraHints(e);
673     if (gUseV)
674         CopyMainV();
675     if (gUseH)
676         CopyMainH();
677     gHHinting = gVHinting = NULL;
678     if (!gUseH)
679         AddHintLst(hLst, false);
680     if (!gUseV)
681         AddHintLst(vLst, true);
682 }
683 
684 static bool
IsIn(int32_t h,int32_t v)685 IsIn(int32_t h, int32_t v)
686 {
687     return (h == -1 && v == -1);
688 }
689 
690 static bool
IsOk(int32_t h,int32_t v)691 IsOk(int32_t h, int32_t v)
692 {
693     return (h != 0 && v != 0);
694 }
695 
696 #define AddIfNeedV(v, vLst)                                                    \
697     if (!gUseV && v == 1)                                                      \
698     AddHintLst(vLst, true)
699 #define AddIfNeedH(h, hLst)                                                    \
700     if (!gUseH && h == 1)                                                      \
701     AddHintLst(hLst, false)
702 
703 static void
SetHHints(HintVal * lst)704 SetHHints(HintVal* lst)
705 {
706     if (gUseH)
707         return;
708     gHHinting = lst;
709     while (lst != NULL) {
710         AutoHSeg(lst);
711         lst = lst->vNxt;
712     }
713 }
714 
715 static void
SetVHints(HintVal * lst)716 SetVHints(HintVal* lst)
717 {
718     if (gUseV)
719         return;
720     gVHinting = lst;
721     while (lst != NULL) {
722         AutoVSeg(lst);
723         lst = lst->vNxt;
724     }
725 }
726 
727 HintVal*
CopyHints(HintVal * lst)728 CopyHints(HintVal* lst)
729 {
730     HintVal* vlst;
731     int cnt;
732     vlst = NULL;
733     cnt = 0;
734     while (lst != NULL) {
735         HintVal* v = (HintVal*)Alloc(sizeof(HintVal));
736         *v = *lst;
737         v->vNxt = vlst;
738         vlst = v;
739         if (++cnt > 100) {
740             LogMsg(WARNING, OK, "Loop in CopyHints.");
741             return vlst;
742         }
743         lst = lst->vNxt;
744     }
745     return vlst;
746 }
747 
748 static bool
IsFlare(Fixed loc,PathElt * e,PathElt * n,bool Hflg)749 IsFlare(Fixed loc, PathElt* e, PathElt* n, bool Hflg)
750 {
751     Fixed x, y;
752     while (e != n) {
753         GetEndPoint(e, &x, &y);
754         if ((Hflg && abs(y - loc) > gMaxFlare) ||
755             (!Hflg && abs(x - loc) > gMaxFlare))
756             return false;
757         e = GetSubPathNxt(e);
758     }
759     return true;
760 }
761 
762 static bool
IsTopSegOfVal(Fixed loc,Fixed top,Fixed bot)763 IsTopSegOfVal(Fixed loc, Fixed top, Fixed bot)
764 {
765     Fixed d1, d2;
766     d1 = top - loc;
767     d2 = bot - loc;
768     return (abs(d1) <= abs(d2)) ? true : false;
769 }
770 
771 static void
RemFlareLnk(PathElt * e,bool hFlg,SegLnkLst * rm,PathElt * e2,int32_t i)772 RemFlareLnk(PathElt* e, bool hFlg, SegLnkLst* rm, PathElt* e2, int32_t i)
773 {
774     RemLnk(e, hFlg, rm);
775     ReportRemFlare(e, e2, hFlg, i);
776 }
777 
778 bool
CompareValues(HintVal * val1,HintVal * val2,int32_t factor,int32_t ghstshift)779 CompareValues(HintVal* val1, HintVal* val2, int32_t factor, int32_t ghstshift)
780 {
781     Fixed v1 = val1->vVal, v2 = val2->vVal, mx;
782     mx = NUMMAX(v1, v2);
783     while (mx < FIXED_MAX / 2) {
784         mx *= 2;
785         v1 *= 2;
786         v2 *= 2;
787     }
788     if (ghstshift > 0 && val1->vGhst != val2->vGhst) {
789         if (val1->vGhst)
790             v1 >>= ghstshift;
791         if (val2->vGhst)
792             v2 >>= ghstshift;
793     }
794     if ((val1->vSpc > 0 && val2->vSpc > 0) ||
795         (val1->vSpc == 0 && val2->vSpc == 0))
796         return v1 > v2;
797     if (val1->vSpc > 0)
798         return (v1 < FIXED_MAX / factor) ? (v1 * factor > v2)
799                                          : (v1 > v2 / factor);
800     return (v2 < FIXED_MAX / factor) ? (v1 > v2 * factor) : (v1 / factor > v2);
801 }
802 
803 static void
RemFlares(bool Hflg)804 RemFlares(bool Hflg)
805 {
806     SegLnkLst *lst1, *lst2, *nxt1, *nxt2;
807     PathElt *e, *n;
808     HintSeg *seg1, *seg2;
809     HintVal *val1, *val2;
810     Fixed diff;
811     bool nxtE;
812     bool Nm1, Nm2;
813     if (Hflg) {
814         Nm1 = true;
815         Nm2 = false;
816     } else {
817         Nm1 = false;
818         Nm2 = true;
819     }
820     e = gPathStart;
821     while (e != NULL) {
822         if (Nm1 ? e->Hs == NULL : e->Vs == NULL) {
823             e = e->next;
824             continue;
825         }
826         /* e now is an element with Nm1 prop */
827         n = GetSubPathNxt(e);
828         nxtE = false;
829         while (n != e && !nxtE) {
830             if (Nm1 ? n->Hs != NULL : n->Vs != NULL) {
831                 lst1 = ElmntHintSegLst(e, Nm1);
832                 while (lst1 != NULL) {
833                     seg1 = lst1->lnk->seg;
834                     nxt1 = lst1->next;
835                     lst2 = ElmntHintSegLst(n, Nm1);
836                     while (lst2 != NULL) {
837                         seg2 = lst2->lnk->seg;
838                         nxt2 = lst2->next;
839                         if (seg1 != NULL && seg2 != NULL) {
840                             diff = seg1->sLoc - seg2->sLoc;
841                             if (abs(diff) > gMaxFlare) {
842                                 nxtE = true;
843                                 goto Nxt2;
844                             }
845                             if (!IsFlare(seg1->sLoc, e, n, Hflg)) {
846                                 nxtE = true;
847                                 goto Nxt2;
848                             }
849                             val1 = seg1->sLnk;
850                             val2 = seg2->sLnk;
851                             if (diff != 0 &&
852                                 IsTopSegOfVal(seg1->sLoc, val1->vLoc2,
853                                               val1->vLoc1) ==
854                                   IsTopSegOfVal(seg2->sLoc, val2->vLoc2,
855                                                 val2->vLoc1)) {
856                                 if (CompareValues(val1, val2, spcBonus, 0)) {
857                                     /* This change was made to fix flares in
858                                      * Bodoni2. */
859                                     if (val2->vSpc == 0 &&
860                                         val2->vVal < FixInt(1000))
861                                         RemFlareLnk(n, Nm1, lst2, e, 1);
862                                 } else if (val1->vSpc == 0 &&
863                                            val1->vVal < FixInt(1000)) {
864                                     RemFlareLnk(e, Nm1, lst1, n, 2);
865                                     goto Nxt1;
866                                 }
867                             }
868                         }
869                     Nxt2:
870                         lst2 = nxt2;
871                     }
872                 Nxt1:
873                     lst1 = nxt1;
874                 }
875             }
876             if (Nm2 ? n->Hs != NULL : n->Vs != NULL)
877                 break;
878             n = GetSubPathNxt(n);
879         }
880         e = e->next;
881     }
882 }
883 
884 static void
CarryIfNeed(Fixed loc,bool vert,HintVal * hints)885 CarryIfNeed(Fixed loc, bool vert, HintVal* hints)
886 {
887     HintSeg* seg;
888     HintVal* seglnk;
889     Fixed l0, l1, tmp, halfMargin;
890     if ((vert && gUseV) || (!vert && gUseH))
891         return;
892     halfMargin = FixHalfMul(gBandMargin);
893     /* DEBUG 8 BIT. Needed to double test from 10 to 20 for change in coordinate
894      * system */
895     if (halfMargin > FixInt(20))
896         halfMargin = FixInt(20);
897     while (hints != NULL) {
898         seg = hints->vSeg1;
899         if (hints->vGhst && seg->sType == sGHOST)
900             seg = hints->vSeg2;
901         if (seg == NULL)
902             goto Nxt;
903         l0 = hints->vLoc2;
904         l1 = hints->vLoc1;
905         if (l0 > l1) {
906             tmp = l1;
907             l1 = l0;
908             l0 = tmp;
909         }
910         l0 -= halfMargin;
911         l1 += halfMargin;
912         if (loc > l0 && loc < l1) {
913             seglnk = seg->sLnk;
914             seg->sLnk = hints;
915             if (vert) {
916                 if (TestHint(seg, gVHinting, true, true) == 1) {
917                     ReportCarry(l0, l1, loc, hints, vert);
918                     AddVHinting(hints);
919                     seg->sLnk = seglnk;
920                     break;
921                 }
922             } else if (TestHint(seg, gHHinting, false, true) == 1) {
923                 ReportCarry(l0, l1, loc, hints, vert);
924                 AddHHinting(hints);
925                 seg->sLnk = seglnk;
926                 break;
927             }
928             seg->sLnk = seglnk;
929         }
930     Nxt:
931         hints = hints->vNxt;
932     }
933 }
934 
935 #define PRODIST                                                                \
936     (FixInt(100)) /* DEBUG 8 BIT. Needed to double test from 50 to 100 for     \
937                      change in coordinate system */
938 static void
ProHints(PathElt * e,bool hFlg,Fixed loc)939 ProHints(PathElt* e, bool hFlg, Fixed loc)
940 {
941     SegLnkLst* lst;
942     PathElt* prv;
943     lst = ElmntHintSegLst(e, hFlg);
944     if (lst == NULL)
945         return;
946     if (hFlg ? e->Hcopy : e->Vcopy)
947         return;
948     prv = e;
949     while (true) {
950         Fixed cx, cy, dst;
951         SegLnkLst* plst;
952         prv = GetSubPathPrv(prv);
953         plst = ElmntHintSegLst(prv, hFlg);
954         if (plst != NULL)
955             return;
956         GetEndPoint(prv, &cx, &cy);
957         dst = (hFlg ? cy : cx) - loc;
958         if (abs(dst) > PRODIST)
959             return;
960         if (hFlg) {
961             prv->Hs = lst;
962             prv->Hcopy = true;
963         } else {
964             prv->Vs = lst;
965             prv->Vcopy = true;
966         }
967     }
968 }
969 
970 static void
PromoteHints(void)971 PromoteHints(void)
972 {
973     PathElt* e;
974     e = gPathStart;
975     while (e != NULL) {
976         Fixed cx, cy;
977         GetEndPoint(e, &cx, &cy);
978         ProHints(e, true, cy);
979         ProHints(e, false, cx);
980         e = e->next;
981     }
982 }
983 
984 static void
RemPromotedHints(void)985 RemPromotedHints(void)
986 {
987     PathElt* e;
988     e = gPathStart;
989     while (e != NULL) {
990         if (e->Hcopy) {
991             e->Hs = NULL;
992             e->Hcopy = false;
993         }
994         if (e->Vcopy) {
995             e->Vs = NULL;
996             e->Vcopy = false;
997         }
998         e = e->next;
999     }
1000 }
1001 
1002 static void
RemShortHints(void)1003 RemShortHints(void)
1004 {
1005     /* Must not change hints at a short element. */
1006     PathElt* e;
1007     Fixed cx, cy, ex, ey;
1008     e = gPathStart;
1009     cx = 0;
1010     cy = 0;
1011     while (e != NULL) {
1012         GetEndPoint(e, &ex, &ey);
1013         if (abs(cx - ex) < gMinHintElementLength &&
1014             abs(cy - ey) < gMinHintElementLength) {
1015             ReportRemShortHints(ex, ey);
1016             e->Hs = NULL;
1017             e->Vs = NULL;
1018         }
1019         e = e->next;
1020         cx = ex;
1021         cy = ey;
1022     }
1023 }
1024 
1025 void
AutoExtraHints(bool movetoNewHints)1026 AutoExtraHints(bool movetoNewHints)
1027 {
1028     int32_t h, v, ph, pv;
1029     PathElt *e, *cp, *p;
1030     SegLnkLst *hLst, *vLst, *phLst, *pvLst;
1031     HintVal *mtVhints, *mtHhints, *prvHhints, *prvVhints;
1032 
1033     bool (*Tst)(int32_t, int32_t), newHints = true;
1034     Fixed x, y;
1035 
1036     mergeMain = (CountSubPaths() <= 5);
1037     e = gPathStart;
1038     LogMsg(LOGDEBUG, OK, "RemFlares");
1039     RemFlares(true);
1040     RemFlares(false);
1041     LogMsg(LOGDEBUG, OK, "CheckElmntHintSegs");
1042     CheckElmntHintSegs();
1043     LogMsg(LOGDEBUG, OK, "PromoteHints");
1044     PromoteHints();
1045     LogMsg(LOGDEBUG, OK, "RemShortHints");
1046     RemShortHints();
1047     p = NULL;
1048     Tst = IsOk; /* it is ok to add to primary hinting */
1049     LogMsg(LOGDEBUG, OK, "hint loop");
1050     mtVhints = mtHhints = NULL;
1051     while (e != NULL) {
1052         int32_t etype = e->type;
1053         if (movetoNewHints && etype == MOVETO) {
1054             StartNewHinting(e, NULL, NULL);
1055             Tst = IsOk;
1056         }
1057         if (newHints && e == p) {
1058             StartNewHinting(e, NULL, NULL);
1059             SetHHints(mtHhints);
1060             SetVHints(mtVhints);
1061             Tst = IsIn;
1062         }
1063         GetHintLsts(e, &hLst, &vLst, &h, &v);
1064         if (etype == MOVETO && IsShort(cp = GetClosedBy(e))) {
1065             GetHintLsts(p = cp->prev, &phLst, &pvLst, &ph, &pv);
1066             if (HintsClash(e, p, &hLst, &vLst, &phLst, &pvLst)) {
1067                 GetHintLsts(e, &hLst, &vLst, &h, &v);
1068                 GetHintLsts(p, &phLst, &pvLst, &ph, &pv);
1069             }
1070             if (!(*Tst)(ph, pv) || !(*Tst)(h, v)) {
1071                 StartNewHinting(e, hLst, vLst);
1072                 Tst = IsOk;
1073                 ph = pv = 1; /* force add of hints for p also */
1074             } else {
1075                 AddIfNeedH(h, hLst);
1076                 AddIfNeedV(v, vLst);
1077             }
1078             AddIfNeedH(ph, phLst);
1079             AddIfNeedV(pv, pvLst);
1080             newHints = false; /* so can tell if added new hints in subpath */
1081         } else if (!(*Tst)(h, v)) { /* e needs new hinting */
1082             if (etype ==
1083                 CLOSEPATH) { /* do not attach extra hints to closepath */
1084                 e = e->prev;
1085                 GetHintLsts(e, &hLst, &vLst, &h, &v);
1086             }
1087             prvHhints = CopyHints(gHHinting);
1088             prvVhints = CopyHints(gVHinting);
1089             if (!newHints) { /* this is the first extra since mt */
1090                 newHints = true;
1091                 mtVhints = CopyHints(prvVhints);
1092                 mtHhints = CopyHints(prvHhints);
1093             }
1094             StartNewHinting(e, hLst, vLst);
1095             Tst = IsOk;
1096             if (etype == CURVETO) {
1097                 x = e->x1;
1098                 y = e->y1;
1099             } else
1100                 GetEndPoint(e, &x, &y);
1101             CarryIfNeed(y, false, prvHhints);
1102             CarryIfNeed(x, true, prvVhints);
1103         } else { /* do not need to start new hinting */
1104             AddIfNeedH(h, hLst);
1105             AddIfNeedV(v, vLst);
1106         }
1107         e = e->next;
1108     }
1109     ReHintBounds(gPathEnd);
1110     LogMsg(LOGDEBUG, OK, "RemPromotedHints");
1111     RemPromotedHints();
1112     LogMsg(LOGDEBUG, OK, "done AutoExtraHints");
1113 }
1114