|
1 /* GRAPHITE2 LICENSING |
|
2 |
|
3 Copyright 2012, SIL International |
|
4 All rights reserved. |
|
5 |
|
6 This library is free software; you can redistribute it and/or modify |
|
7 it under the terms of the GNU Lesser General Public License as published |
|
8 by the Free Software Foundation; either version 2.1 of License, or |
|
9 (at your option) any later version. |
|
10 |
|
11 This program is distributed in the hope that it will be useful, |
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 Lesser General Public License for more details. |
|
15 |
|
16 You should also have received a copy of the GNU Lesser General Public |
|
17 License along with this library in the file named "LICENSE". |
|
18 If not, write to the Free Software Foundation, 51 Franklin Street, |
|
19 Suite 500, Boston, MA 02110-1335, USA or visit their web page on the |
|
20 internet at http://www.fsf.org/licenses/lgpl.html. |
|
21 |
|
22 Alternatively, the contents of this file may be used under the terms of the |
|
23 Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public |
|
24 License, as published by the Free Software Foundation, either version 2 |
|
25 of the License or (at your option) any later version. |
|
26 */ |
|
27 |
|
28 #include "inc/Segment.h" |
|
29 #include "graphite2/Font.h" |
|
30 #include "inc/debug.h" |
|
31 #include "inc/CharInfo.h" |
|
32 #include "inc/Slot.h" |
|
33 #include "inc/Main.h" |
|
34 #include <math.h> |
|
35 |
|
36 using namespace graphite2; |
|
37 |
|
38 class JustifyTotal { |
|
39 public: |
|
40 JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {} |
|
41 void accumulate(Slot *s, Segment *seg, int level); |
|
42 int weight() const { return m_tWeight; } |
|
43 |
|
44 CLASS_NEW_DELETE |
|
45 |
|
46 private: |
|
47 int m_numGlyphs; |
|
48 int m_tStretch; |
|
49 int m_tShrink; |
|
50 int m_tStep; |
|
51 int m_tWeight; |
|
52 }; |
|
53 |
|
54 void JustifyTotal::accumulate(Slot *s, Segment *seg, int level) |
|
55 { |
|
56 ++m_numGlyphs; |
|
57 m_tStretch += s->getJustify(seg, level, 0); |
|
58 m_tShrink += s->getJustify(seg, level, 1); |
|
59 m_tStep += s->getJustify(seg, level, 2); |
|
60 m_tWeight += s->getJustify(seg, level, 3); |
|
61 } |
|
62 |
|
63 float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags flags, Slot *pFirst, Slot *pLast) |
|
64 { |
|
65 Slot *s, *end; |
|
66 float currWidth = 0.0; |
|
67 const float scale = font ? font->scale() : 1.0f; |
|
68 Position res; |
|
69 |
|
70 if (width < 0 && !(silf()->flags())) |
|
71 return width; |
|
72 |
|
73 if (!pFirst) pFirst = pSlot; |
|
74 while (!pFirst->isBase()) pFirst = pFirst->attachedTo(); |
|
75 if (!pLast) pLast = last(); |
|
76 while (!pLast->isBase()) pLast = pLast->attachedTo(); |
|
77 const float base = pFirst->origin().x / scale; |
|
78 width = width / scale; |
|
79 if ((flags & gr_justEndInline) == 0) |
|
80 { |
|
81 do { |
|
82 Rect bbox = theGlyphBBoxTemporary(pLast->glyph()); |
|
83 if (bbox.bl.x != 0. || bbox.bl.y != 0. || bbox.tr.x != 0. || bbox.tr.y == 0.) |
|
84 break; |
|
85 pLast = pLast->prev(); |
|
86 } while (pLast != pFirst); |
|
87 } |
|
88 |
|
89 end = pLast->nextSibling(); |
|
90 pFirst = pFirst->nextSibling(); |
|
91 |
|
92 int icount = 0; |
|
93 int numLevels = silf()->numJustLevels(); |
|
94 if (!numLevels) |
|
95 { |
|
96 for (s = pSlot; s != end; s = s->next()) |
|
97 { |
|
98 CharInfo *c = charinfo(s->before()); |
|
99 if (isWhitespace(c->unicodeChar())) |
|
100 { |
|
101 s->setJustify(this, 0, 3, 1); |
|
102 s->setJustify(this, 0, 2, 1); |
|
103 s->setJustify(this, 0, 0, -1); |
|
104 ++icount; |
|
105 } |
|
106 } |
|
107 if (!icount) |
|
108 { |
|
109 for (s = pSlot; s != end; s = s->nextSibling()) |
|
110 { |
|
111 s->setJustify(this, 0, 3, 1); |
|
112 s->setJustify(this, 0, 2, 1); |
|
113 s->setJustify(this, 0, 0, -1); |
|
114 } |
|
115 } |
|
116 ++numLevels; |
|
117 } |
|
118 |
|
119 JustifyTotal *stats = new JustifyTotal[numLevels]; |
|
120 if (!stats) return -1.0; |
|
121 for (s = pFirst; s != end; s = s->nextSibling()) |
|
122 { |
|
123 float w = s->origin().x / scale + s->advance() - base; |
|
124 if (w > currWidth) currWidth = w; |
|
125 for (int j = 0; j < numLevels; ++j) |
|
126 stats[j].accumulate(s, this, j); |
|
127 s->just(0); |
|
128 } |
|
129 |
|
130 for (int i = (width < 0.0) ? -1 : numLevels - 1; i >= 0; --i) |
|
131 { |
|
132 float diff; |
|
133 float error = 0.; |
|
134 float diffpw; |
|
135 int tWeight = stats[i].weight(); |
|
136 |
|
137 do { |
|
138 error = 0.; |
|
139 diff = width - currWidth; |
|
140 diffpw = diff / tWeight; |
|
141 tWeight = 0; |
|
142 for (s = pFirst; s != end; s = s->nextSibling()) // don't include final glyph |
|
143 { |
|
144 int w = s->getJustify(this, i, 3); |
|
145 float pref = diffpw * w + error; |
|
146 int step = s->getJustify(this, i, 2); |
|
147 if (!step) step = 1; // handle lazy font developers |
|
148 if (pref > 0) |
|
149 { |
|
150 float max = uint16(s->getJustify(this, i, 0)); |
|
151 if (i == 0) max -= s->just(); |
|
152 if (pref > max) pref = max; |
|
153 else tWeight += w; |
|
154 } |
|
155 else |
|
156 { |
|
157 float max = uint16(s->getJustify(this, i, 1)); |
|
158 if (i == 0) max += s->just(); |
|
159 if (-pref > max) pref = -max; |
|
160 else tWeight += w; |
|
161 } |
|
162 int actual = step ? int(pref / step) * step : int(pref); |
|
163 |
|
164 if (actual) |
|
165 { |
|
166 error += diffpw * w - actual; |
|
167 if (i == 0) |
|
168 s->just(s->just() + actual); |
|
169 else |
|
170 s->setJustify(this, i, 4, actual); |
|
171 } |
|
172 } |
|
173 currWidth += diff - error; |
|
174 } while (i == 0 && int(abs(error)) > 0 && tWeight); |
|
175 } |
|
176 |
|
177 Slot *oldFirst = m_first; |
|
178 Slot *oldLast = m_last; |
|
179 if (silf()->flags() & 1) |
|
180 { |
|
181 m_first = pSlot = addLineEnd(pSlot); |
|
182 m_last = pLast = addLineEnd(end); |
|
183 if (!m_first || !m_last) return -1.0; |
|
184 } |
|
185 else |
|
186 { |
|
187 m_first = pSlot; |
|
188 m_last = pLast; |
|
189 } |
|
190 |
|
191 // run justification passes here |
|
192 #if !defined GRAPHITE2_NTRACING |
|
193 json * const dbgout = m_face->logger(); |
|
194 if (dbgout) |
|
195 *dbgout << json::object |
|
196 << "justifies" << objectid(this) |
|
197 << "passes" << json::array; |
|
198 #endif |
|
199 |
|
200 if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0. || (silf()->flags() & 1))) |
|
201 m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass()); |
|
202 |
|
203 #if !defined GRAPHITE2_NTRACING |
|
204 if (dbgout) |
|
205 { |
|
206 *dbgout << json::item << json::close; // Close up the passes array |
|
207 positionSlots(NULL, pSlot, pLast); |
|
208 Slot *lEnd = pLast->nextSibling(); |
|
209 *dbgout << "output" << json::array; |
|
210 for(Slot * t = pSlot; t != lEnd; t = t->next()) |
|
211 *dbgout << dslot(this, t); |
|
212 *dbgout << json::close << json::close; |
|
213 } |
|
214 #endif |
|
215 |
|
216 res = positionSlots(font, pSlot, pLast); |
|
217 |
|
218 if (silf()->flags() & 1) |
|
219 { |
|
220 delLineEnd(m_first); |
|
221 delLineEnd(m_last); |
|
222 } |
|
223 m_first = oldFirst; |
|
224 m_last = oldLast; |
|
225 return res.x; |
|
226 } |
|
227 |
|
228 Slot *Segment::addLineEnd(Slot *nSlot) |
|
229 { |
|
230 Slot *eSlot = newSlot(); |
|
231 if (!eSlot) return NULL; |
|
232 const uint16 gid = silf()->endLineGlyphid(); |
|
233 const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid); |
|
234 eSlot->setGlyph(this, gid, theGlyph); |
|
235 if (nSlot) |
|
236 { |
|
237 eSlot->next(nSlot); |
|
238 eSlot->prev(nSlot->prev()); |
|
239 nSlot->prev(eSlot); |
|
240 eSlot->before(nSlot->before()); |
|
241 if (eSlot->prev()) |
|
242 eSlot->after(eSlot->prev()->after()); |
|
243 else |
|
244 eSlot->after(nSlot->before()); |
|
245 } |
|
246 else |
|
247 { |
|
248 nSlot = m_last; |
|
249 eSlot->prev(nSlot); |
|
250 nSlot->next(eSlot); |
|
251 eSlot->after(eSlot->prev()->after()); |
|
252 eSlot->before(nSlot->after()); |
|
253 } |
|
254 return eSlot; |
|
255 } |
|
256 |
|
257 void Segment::delLineEnd(Slot *s) |
|
258 { |
|
259 Slot *nSlot = s->next(); |
|
260 if (nSlot) |
|
261 { |
|
262 nSlot->prev(s->prev()); |
|
263 if (s->prev()) |
|
264 s->prev()->next(nSlot); |
|
265 } |
|
266 else |
|
267 s->prev()->next(NULL); |
|
268 freeSlot(s); |
|
269 } |
|
270 |