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