Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* GRAPHITE2 LICENSING
3 Copyright 2012, SIL International
4 All rights reserved.
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.
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.
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.
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 */
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>
36 using namespace graphite2;
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; }
44 CLASS_NEW_DELETE
46 private:
47 int m_numGlyphs;
48 int m_tStretch;
49 int m_tShrink;
50 int m_tStep;
51 int m_tWeight;
52 };
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 }
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;
70 if (width < 0 && !(silf()->flags()))
71 return width;
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 }
89 end = pLast->nextSibling();
90 pFirst = pFirst->nextSibling();
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 }
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 }
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();
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);
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 }
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 }
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
200 if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0. || (silf()->flags() & 1)))
201 m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass());
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
216 res = positionSlots(font, pSlot, pLast);
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 }
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 }
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 }