/work/workdir/UnpackedTarball/graphite/src/Justifier.cpp
Line | Count | Source |
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 | | |
13 | | using namespace graphite2; |
14 | | |
15 | | class JustifyTotal { |
16 | | public: |
17 | 0 | 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 | 0 | int weight() const { return m_tWeight; } |
20 | | |
21 | | CLASS_NEW_DELETE |
22 | | |
23 | | private: |
24 | | int m_numGlyphs; |
25 | | int m_tStretch; |
26 | | int m_tShrink; |
27 | | int m_tStep; |
28 | | int m_tWeight; |
29 | | }; |
30 | | |
31 | | void JustifyTotal::accumulate(Slot *s, Segment *seg, int level) |
32 | 0 | { |
33 | 0 | ++m_numGlyphs; |
34 | 0 | m_tStretch += s->getJustify(seg, level, 0); |
35 | 0 | m_tShrink += s->getJustify(seg, level, 1); |
36 | 0 | m_tStep += s->getJustify(seg, level, 2); |
37 | 0 | m_tWeight += s->getJustify(seg, level, 3); |
38 | 0 | } |
39 | | |
40 | | float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags jflags, Slot *pFirst, Slot *pLast) |
41 | 0 | { |
42 | 0 | Slot *end = last(); |
43 | 0 | float currWidth = 0.0; |
44 | 0 | const float scale = font ? font->scale() : 1.0f; |
45 | 0 | Position res; |
46 | |
|
47 | 0 | if (width < 0 && !(silf()->flags())) |
48 | 0 | return width; |
49 | | |
50 | 0 | if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses()) |
51 | 0 | { |
52 | 0 | reverseSlots(); |
53 | 0 | std::swap(pFirst, pLast); |
54 | 0 | } |
55 | 0 | if (!pFirst) pFirst = pSlot; |
56 | 0 | while (!pFirst->isBase()) pFirst = pFirst->attachedTo(); |
57 | 0 | if (!pLast) pLast = last(); |
58 | 0 | while (!pLast->isBase()) pLast = pLast->attachedTo(); |
59 | 0 | const float base = pFirst->origin().x / scale; |
60 | 0 | width = width / scale; |
61 | 0 | if ((jflags & gr_justEndInline) == 0) |
62 | 0 | { |
63 | 0 | while (pLast != pFirst && pLast) |
64 | 0 | { |
65 | 0 | Rect bbox = theGlyphBBoxTemporary(pLast->glyph()); |
66 | 0 | if (bbox.bl.x != 0.f || bbox.bl.y != 0.f || bbox.tr.x != 0.f || bbox.tr.y == 0.f) |
67 | 0 | break; |
68 | 0 | pLast = pLast->prev(); |
69 | 0 | } |
70 | 0 | } |
71 | |
|
72 | 0 | if (pLast) |
73 | 0 | end = pLast->nextSibling(); |
74 | 0 | if (pFirst) |
75 | 0 | pFirst = pFirst->nextSibling(); |
76 | |
|
77 | 0 | int icount = 0; |
78 | 0 | int numLevels = silf()->numJustLevels(); |
79 | 0 | if (!numLevels) |
80 | 0 | { |
81 | 0 | for (Slot *s = pSlot; s && s != end; s = s->nextSibling()) |
82 | 0 | { |
83 | 0 | CharInfo *c = charinfo(s->before()); |
84 | 0 | if (isWhitespace(c->unicodeChar())) |
85 | 0 | { |
86 | 0 | s->setJustify(this, 0, 3, 1); |
87 | 0 | s->setJustify(this, 0, 2, 1); |
88 | 0 | s->setJustify(this, 0, 0, -1); |
89 | 0 | ++icount; |
90 | 0 | } |
91 | 0 | } |
92 | 0 | if (!icount) |
93 | 0 | { |
94 | 0 | for (Slot *s = pSlot; s && s != end; s = s->nextSibling()) |
95 | 0 | { |
96 | 0 | s->setJustify(this, 0, 3, 1); |
97 | 0 | s->setJustify(this, 0, 2, 1); |
98 | 0 | s->setJustify(this, 0, 0, -1); |
99 | 0 | } |
100 | 0 | } |
101 | 0 | ++numLevels; |
102 | 0 | } |
103 | |
|
104 | 0 | Vector<JustifyTotal> stats(numLevels); |
105 | 0 | for (Slot *s = pFirst; s && s != end; s = s->nextSibling()) |
106 | 0 | { |
107 | 0 | float w = s->origin().x / scale + s->advance() - base; |
108 | 0 | if (w > currWidth) currWidth = w; |
109 | 0 | for (int j = 0; j < numLevels; ++j) |
110 | 0 | stats[j].accumulate(s, this, j); |
111 | 0 | s->just(0); |
112 | 0 | } |
113 | |
|
114 | 0 | for (int i = (width < 0.0f) ? -1 : numLevels - 1; i >= 0; --i) |
115 | 0 | { |
116 | 0 | float diff; |
117 | 0 | float error = 0.; |
118 | 0 | float diffpw; |
119 | 0 | int tWeight = stats[i].weight(); |
120 | 0 | if (tWeight == 0) continue; |
121 | | |
122 | 0 | do { |
123 | 0 | error = 0.; |
124 | 0 | diff = width - currWidth; |
125 | 0 | diffpw = diff / tWeight; |
126 | 0 | tWeight = 0; |
127 | 0 | for (Slot *s = pFirst; s && s != end; s = s->nextSibling()) // don't include final glyph |
128 | 0 | { |
129 | 0 | int w = s->getJustify(this, i, 3); |
130 | 0 | float pref = diffpw * w + error; |
131 | 0 | int step = s->getJustify(this, i, 2); |
132 | 0 | if (!step) step = 1; // handle lazy font developers |
133 | 0 | if (pref > 0) |
134 | 0 | { |
135 | 0 | float max = uint16(s->getJustify(this, i, 0)); |
136 | 0 | if (i == 0) max -= s->just(); |
137 | 0 | if (pref > max) pref = max; |
138 | 0 | else tWeight += w; |
139 | 0 | } |
140 | 0 | else |
141 | 0 | { |
142 | 0 | float max = uint16(s->getJustify(this, i, 1)); |
143 | 0 | if (i == 0) max += s->just(); |
144 | 0 | if (-pref > max) pref = -max; |
145 | 0 | else tWeight += w; |
146 | 0 | } |
147 | 0 | int actual = int(pref / step) * step; |
148 | |
|
149 | 0 | if (actual) |
150 | 0 | { |
151 | 0 | error += diffpw * w - actual; |
152 | 0 | if (i == 0) |
153 | 0 | s->just(s->just() + actual); |
154 | 0 | else |
155 | 0 | s->setJustify(this, i, 4, actual); |
156 | 0 | } |
157 | 0 | } |
158 | 0 | currWidth += diff - error; |
159 | 0 | } while (i == 0 && int(std::abs(error)) > 0 && tWeight); |
160 | 0 | } |
161 | |
|
162 | 0 | Slot *oldFirst = m_first; |
163 | 0 | Slot *oldLast = m_last; |
164 | 0 | if (silf()->flags() & 1) |
165 | 0 | { |
166 | 0 | m_first = pSlot = addLineEnd(pSlot); |
167 | 0 | m_last = pLast = addLineEnd(end); |
168 | 0 | if (!m_first || !m_last) return -1.0; |
169 | 0 | } |
170 | 0 | else |
171 | 0 | { |
172 | 0 | m_first = pSlot; |
173 | 0 | m_last = pLast; |
174 | 0 | } |
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 | 0 | if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0.f || (silf()->flags() & 1))) |
186 | 0 | 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 | 0 | res = positionSlots(font, pSlot, pLast, m_dir); |
202 | |
|
203 | 0 | if (silf()->flags() & 1) |
204 | 0 | { |
205 | 0 | if (m_first) |
206 | 0 | delLineEnd(m_first); |
207 | 0 | if (m_last) |
208 | 0 | delLineEnd(m_last); |
209 | 0 | } |
210 | 0 | m_first = oldFirst; |
211 | 0 | m_last = oldLast; |
212 | |
|
213 | 0 | if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses()) |
214 | 0 | reverseSlots(); |
215 | 0 | return res.x; |
216 | 0 | } |
217 | | |
218 | | Slot *Segment::addLineEnd(Slot *nSlot) |
219 | 0 | { |
220 | 0 | Slot *eSlot = newSlot(); |
221 | 0 | if (!eSlot) return NULL; |
222 | 0 | const uint16 gid = silf()->endLineGlyphid(); |
223 | 0 | const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid); |
224 | 0 | eSlot->setGlyph(this, gid, theGlyph); |
225 | 0 | if (nSlot) |
226 | 0 | { |
227 | 0 | eSlot->next(nSlot); |
228 | 0 | eSlot->prev(nSlot->prev()); |
229 | 0 | nSlot->prev(eSlot); |
230 | 0 | eSlot->before(nSlot->before()); |
231 | 0 | if (eSlot->prev()) |
232 | 0 | eSlot->after(eSlot->prev()->after()); |
233 | 0 | else |
234 | 0 | eSlot->after(nSlot->before()); |
235 | 0 | } |
236 | 0 | else |
237 | 0 | { |
238 | 0 | nSlot = m_last; |
239 | 0 | eSlot->prev(nSlot); |
240 | 0 | nSlot->next(eSlot); |
241 | 0 | eSlot->after(eSlot->prev()->after()); |
242 | 0 | eSlot->before(nSlot->after()); |
243 | 0 | } |
244 | 0 | return eSlot; |
245 | 0 | } |
246 | | |
247 | | void Segment::delLineEnd(Slot *s) |
248 | 0 | { |
249 | 0 | Slot *nSlot = s->next(); |
250 | 0 | if (nSlot) |
251 | 0 | { |
252 | 0 | nSlot->prev(s->prev()); |
253 | 0 | if (s->prev()) |
254 | 0 | s->prev()->next(nSlot); |
255 | 0 | } |
256 | 0 | else |
257 | 0 | s->prev()->next(NULL); |
258 | 0 | freeSlot(s); |
259 | 0 | } |