1// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later
2// Copyright 2012, SIL International, All rights reserved.
3
4
5#include "inc/Segment.h"
6#include "graphite2/Font.h"
7#include "inc/debug.h"
8#include "inc/CharInfo.h"
9#include "inc/Slot.h"
10#include "inc/Main.h"
11#include <cmath>
12
13using namespace graphite2;
14
15class JustifyTotal {
16public:
17 JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {}
18 void accumulate(Slot *s, Segment *seg, int level);
19 int weight() const { return m_tWeight; }
20
21 CLASS_NEW_DELETE
22
23private:
24 int m_numGlyphs;
25 int m_tStretch;
26 int m_tShrink;
27 int m_tStep;
28 int m_tWeight;
29};
30
31void JustifyTotal::accumulate(Slot *s, Segment *seg, int level)
32{
33 ++m_numGlyphs;
34 m_tStretch += s->getJustify(seg, level, 0);
35 m_tShrink += s->getJustify(seg, level, 1);
36 m_tStep += s->getJustify(seg, level, 2);
37 m_tWeight += s->getJustify(seg, level, 3);
38}
39
40float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags jflags, Slot *pFirst, Slot *pLast)
41{
42 Slot *end = last();
43 float currWidth = 0.0;
44 const float scale = font ? font->scale() : 1.0f;
45 Position res;
46
47 if (width < 0 && !(silf()->flags()))
48 return width;
49
50 if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
51 {
52 reverseSlots();
53 std::swap(pFirst, pLast);
54 }
55 if (!pFirst) pFirst = pSlot;
56 while (!pFirst->isBase()) pFirst = pFirst->attachedTo();
57 if (!pLast) pLast = last();
58 while (!pLast->isBase()) pLast = pLast->attachedTo();
59 const float base = pFirst->origin().x / scale;
60 width = width / scale;
61 if ((jflags & gr_justEndInline) == 0)
62 {
63 while (pLast != pFirst && pLast)
64 {
65 Rect bbox = theGlyphBBoxTemporary(pLast->glyph());
66 if (bbox.bl.x != 0.f || bbox.bl.y != 0.f || bbox.tr.x != 0.f || bbox.tr.y == 0.f)
67 break;
68 pLast = pLast->prev();
69 }
70 }
71
72 if (pLast)
73 end = pLast->nextSibling();
74 if (pFirst)
75 pFirst = pFirst->nextSibling();
76
77 int icount = 0;
78 int numLevels = silf()->numJustLevels();
79 if (!numLevels)
80 {
81 for (Slot *s = pSlot; s && s != end; s = s->nextSibling())
82 {
83 CharInfo *c = charinfo(s->before());
84 if (isWhitespace(c->unicodeChar()))
85 {
86 s->setJustify(this, 0, 3, 1);
87 s->setJustify(this, 0, 2, 1);
88 s->setJustify(this, 0, 0, -1);
89 ++icount;
90 }
91 }
92 if (!icount)
93 {
94 for (Slot *s = pSlot; s && s != end; s = s->nextSibling())
95 {
96 s->setJustify(this, 0, 3, 1);
97 s->setJustify(this, 0, 2, 1);
98 s->setJustify(this, 0, 0, -1);
99 }
100 }
101 ++numLevels;
102 }
103
104 Vector<JustifyTotal> stats(numLevels);
105 for (Slot *s = pFirst; s && s != end; s = s->nextSibling())
106 {
107 float w = s->origin().x / scale + s->advance() - base;
108 if (w > currWidth) currWidth = w;
109 for (int j = 0; j < numLevels; ++j)
110 stats[j].accumulate(s, this, j);
111 s->just(0);
112 }
113
114 for (int i = (width < 0.0f) ? -1 : numLevels - 1; i >= 0; --i)
115 {
116 float diff;
117 float error = 0.;
118 float diffpw;
119 int tWeight = stats[i].weight();
120 if (tWeight == 0) continue;
121
122 do {
123 error = 0.;
124 diff = width - currWidth;
125 diffpw = diff / tWeight;
126 tWeight = 0;
127 for (Slot *s = pFirst; s && s != end; s = s->nextSibling()) // don't include final glyph
128 {
129 int w = s->getJustify(this, i, 3);
130 float pref = diffpw * w + error;
131 int step = s->getJustify(this, i, 2);
132 if (!step) step = 1; // handle lazy font developers
133 if (pref > 0)
134 {
135 float max = uint16(s->getJustify(this, i, 0));
136 if (i == 0) max -= s->just();
137 if (pref > max) pref = max;
138 else tWeight += w;
139 }
140 else
141 {
142 float max = uint16(s->getJustify(this, i, 1));
143 if (i == 0) max += s->just();
144 if (-pref > max) pref = -max;
145 else tWeight += w;
146 }
147 int actual = int(pref / step) * step;
148
149 if (actual)
150 {
151 error += diffpw * w - actual;
152 if (i == 0)
153 s->just(s->just() + actual);
154 else
155 s->setJustify(this, i, 4, actual);
156 }
157 }
158 currWidth += diff - error;
159 } while (i == 0 && int(std::abs(error)) > 0 && tWeight);
160 }
161
162 Slot *oldFirst = m_first;
163 Slot *oldLast = m_last;
164 if (silf()->flags() & 1)
165 {
166 m_first = pSlot = addLineEnd(pSlot);
167 m_last = pLast = addLineEnd(end);
168 if (!m_first || !m_last) return -1.0;
169 }
170 else
171 {
172 m_first = pSlot;
173 m_last = pLast;
174 }
175
176 // run justification passes here
177#if !defined GRAPHITE2_NTRACING
178 json * const dbgout = m_face->logger();
179 if (dbgout)
180 *dbgout << json::object
181 << "justifies" << objectid(this)
182 << "passes" << json::array;
183#endif
184
185 if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0.f || (silf()->flags() & 1)))
186 m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass());
187
188#if !defined GRAPHITE2_NTRACING
189 if (dbgout)
190 {
191 *dbgout << json::item << json::close; // Close up the passes array
192 positionSlots(NULL, pSlot, pLast, m_dir);
193 Slot *lEnd = pLast->nextSibling();
194 *dbgout << "output" << json::array;
195 for(Slot * t = pSlot; t != lEnd; t = t->next())
196 *dbgout << dslot(this, t);
197 *dbgout << json::close << json::close;
198 }
199#endif
200
201 res = positionSlots(font, pSlot, pLast, m_dir);
202
203 if (silf()->flags() & 1)
204 {
205 if (m_first)
206 delLineEnd(m_first);
207 if (m_last)
208 delLineEnd(m_last);
209 }
210 m_first = oldFirst;
211 m_last = oldLast;
212
213 if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
214 reverseSlots();
215 return res.x;
216}
217
218Slot *Segment::addLineEnd(Slot *nSlot)
219{
220 Slot *eSlot = newSlot();
221 if (!eSlot) return NULL;
222 const uint16 gid = silf()->endLineGlyphid();
223 const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
224 eSlot->setGlyph(this, gid, theGlyph);
225 if (nSlot)
226 {
227 eSlot->next(nSlot);
228 eSlot->prev(nSlot->prev());
229 nSlot->prev(eSlot);
230 eSlot->before(nSlot->before());
231 if (eSlot->prev())
232 eSlot->after(eSlot->prev()->after());
233 else
234 eSlot->after(nSlot->before());
235 }
236 else
237 {
238 nSlot = m_last;
239 eSlot->prev(nSlot);
240 nSlot->next(eSlot);
241 eSlot->after(eSlot->prev()->after());
242 eSlot->before(nSlot->after());
243 }
244 return eSlot;
245}
246
247void Segment::delLineEnd(Slot *s)
248{
249 Slot *nSlot = s->next();
250 if (nSlot)
251 {
252 nSlot->prev(s->prev());
253 if (s->prev())
254 s->prev()->next(nSlot);
255 }
256 else
257 s->prev()->next(NULL);
258 freeSlot(s);
259}
260