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