1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/graphite2/src/Justifier.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,270 @@ 1.4 +/* GRAPHITE2 LICENSING 1.5 + 1.6 + Copyright 2012, SIL International 1.7 + All rights reserved. 1.8 + 1.9 + This library is free software; you can redistribute it and/or modify 1.10 + it under the terms of the GNU Lesser General Public License as published 1.11 + by the Free Software Foundation; either version 2.1 of License, or 1.12 + (at your option) any later version. 1.13 + 1.14 + This program is distributed in the hope that it will be useful, 1.15 + but WITHOUT ANY WARRANTY; without even the implied warranty of 1.16 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1.17 + Lesser General Public License for more details. 1.18 + 1.19 + You should also have received a copy of the GNU Lesser General Public 1.20 + License along with this library in the file named "LICENSE". 1.21 + If not, write to the Free Software Foundation, 51 Franklin Street, 1.22 + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the 1.23 + internet at http://www.fsf.org/licenses/lgpl.html. 1.24 + 1.25 +Alternatively, the contents of this file may be used under the terms of the 1.26 +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public 1.27 +License, as published by the Free Software Foundation, either version 2 1.28 +of the License or (at your option) any later version. 1.29 +*/ 1.30 + 1.31 +#include "inc/Segment.h" 1.32 +#include "graphite2/Font.h" 1.33 +#include "inc/debug.h" 1.34 +#include "inc/CharInfo.h" 1.35 +#include "inc/Slot.h" 1.36 +#include "inc/Main.h" 1.37 +#include <math.h> 1.38 + 1.39 +using namespace graphite2; 1.40 + 1.41 +class JustifyTotal { 1.42 +public: 1.43 + JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {} 1.44 + void accumulate(Slot *s, Segment *seg, int level); 1.45 + int weight() const { return m_tWeight; } 1.46 + 1.47 + CLASS_NEW_DELETE 1.48 + 1.49 +private: 1.50 + int m_numGlyphs; 1.51 + int m_tStretch; 1.52 + int m_tShrink; 1.53 + int m_tStep; 1.54 + int m_tWeight; 1.55 +}; 1.56 + 1.57 +void JustifyTotal::accumulate(Slot *s, Segment *seg, int level) 1.58 +{ 1.59 + ++m_numGlyphs; 1.60 + m_tStretch += s->getJustify(seg, level, 0); 1.61 + m_tShrink += s->getJustify(seg, level, 1); 1.62 + m_tStep += s->getJustify(seg, level, 2); 1.63 + m_tWeight += s->getJustify(seg, level, 3); 1.64 +} 1.65 + 1.66 +float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags flags, Slot *pFirst, Slot *pLast) 1.67 +{ 1.68 + Slot *s, *end; 1.69 + float currWidth = 0.0; 1.70 + const float scale = font ? font->scale() : 1.0f; 1.71 + Position res; 1.72 + 1.73 + if (width < 0 && !(silf()->flags())) 1.74 + return width; 1.75 + 1.76 + if (!pFirst) pFirst = pSlot; 1.77 + while (!pFirst->isBase()) pFirst = pFirst->attachedTo(); 1.78 + if (!pLast) pLast = last(); 1.79 + while (!pLast->isBase()) pLast = pLast->attachedTo(); 1.80 + const float base = pFirst->origin().x / scale; 1.81 + width = width / scale; 1.82 + if ((flags & gr_justEndInline) == 0) 1.83 + { 1.84 + do { 1.85 + Rect bbox = theGlyphBBoxTemporary(pLast->glyph()); 1.86 + if (bbox.bl.x != 0. || bbox.bl.y != 0. || bbox.tr.x != 0. || bbox.tr.y == 0.) 1.87 + break; 1.88 + pLast = pLast->prev(); 1.89 + } while (pLast != pFirst); 1.90 + } 1.91 + 1.92 + end = pLast->nextSibling(); 1.93 + pFirst = pFirst->nextSibling(); 1.94 + 1.95 + int icount = 0; 1.96 + int numLevels = silf()->numJustLevels(); 1.97 + if (!numLevels) 1.98 + { 1.99 + for (s = pSlot; s != end; s = s->next()) 1.100 + { 1.101 + CharInfo *c = charinfo(s->before()); 1.102 + if (isWhitespace(c->unicodeChar())) 1.103 + { 1.104 + s->setJustify(this, 0, 3, 1); 1.105 + s->setJustify(this, 0, 2, 1); 1.106 + s->setJustify(this, 0, 0, -1); 1.107 + ++icount; 1.108 + } 1.109 + } 1.110 + if (!icount) 1.111 + { 1.112 + for (s = pSlot; s != end; s = s->nextSibling()) 1.113 + { 1.114 + s->setJustify(this, 0, 3, 1); 1.115 + s->setJustify(this, 0, 2, 1); 1.116 + s->setJustify(this, 0, 0, -1); 1.117 + } 1.118 + } 1.119 + ++numLevels; 1.120 + } 1.121 + 1.122 + JustifyTotal *stats = new JustifyTotal[numLevels]; 1.123 + if (!stats) return -1.0; 1.124 + for (s = pFirst; s != end; s = s->nextSibling()) 1.125 + { 1.126 + float w = s->origin().x / scale + s->advance() - base; 1.127 + if (w > currWidth) currWidth = w; 1.128 + for (int j = 0; j < numLevels; ++j) 1.129 + stats[j].accumulate(s, this, j); 1.130 + s->just(0); 1.131 + } 1.132 + 1.133 + for (int i = (width < 0.0) ? -1 : numLevels - 1; i >= 0; --i) 1.134 + { 1.135 + float diff; 1.136 + float error = 0.; 1.137 + float diffpw; 1.138 + int tWeight = stats[i].weight(); 1.139 + 1.140 + do { 1.141 + error = 0.; 1.142 + diff = width - currWidth; 1.143 + diffpw = diff / tWeight; 1.144 + tWeight = 0; 1.145 + for (s = pFirst; s != end; s = s->nextSibling()) // don't include final glyph 1.146 + { 1.147 + int w = s->getJustify(this, i, 3); 1.148 + float pref = diffpw * w + error; 1.149 + int step = s->getJustify(this, i, 2); 1.150 + if (!step) step = 1; // handle lazy font developers 1.151 + if (pref > 0) 1.152 + { 1.153 + float max = uint16(s->getJustify(this, i, 0)); 1.154 + if (i == 0) max -= s->just(); 1.155 + if (pref > max) pref = max; 1.156 + else tWeight += w; 1.157 + } 1.158 + else 1.159 + { 1.160 + float max = uint16(s->getJustify(this, i, 1)); 1.161 + if (i == 0) max += s->just(); 1.162 + if (-pref > max) pref = -max; 1.163 + else tWeight += w; 1.164 + } 1.165 + int actual = step ? int(pref / step) * step : int(pref); 1.166 + 1.167 + if (actual) 1.168 + { 1.169 + error += diffpw * w - actual; 1.170 + if (i == 0) 1.171 + s->just(s->just() + actual); 1.172 + else 1.173 + s->setJustify(this, i, 4, actual); 1.174 + } 1.175 + } 1.176 + currWidth += diff - error; 1.177 + } while (i == 0 && int(abs(error)) > 0 && tWeight); 1.178 + } 1.179 + 1.180 + Slot *oldFirst = m_first; 1.181 + Slot *oldLast = m_last; 1.182 + if (silf()->flags() & 1) 1.183 + { 1.184 + m_first = pSlot = addLineEnd(pSlot); 1.185 + m_last = pLast = addLineEnd(end); 1.186 + if (!m_first || !m_last) return -1.0; 1.187 + } 1.188 + else 1.189 + { 1.190 + m_first = pSlot; 1.191 + m_last = pLast; 1.192 + } 1.193 + 1.194 + // run justification passes here 1.195 +#if !defined GRAPHITE2_NTRACING 1.196 + json * const dbgout = m_face->logger(); 1.197 + if (dbgout) 1.198 + *dbgout << json::object 1.199 + << "justifies" << objectid(this) 1.200 + << "passes" << json::array; 1.201 +#endif 1.202 + 1.203 + if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0. || (silf()->flags() & 1))) 1.204 + m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass()); 1.205 + 1.206 +#if !defined GRAPHITE2_NTRACING 1.207 + if (dbgout) 1.208 + { 1.209 + *dbgout << json::item << json::close; // Close up the passes array 1.210 + positionSlots(NULL, pSlot, pLast); 1.211 + Slot *lEnd = pLast->nextSibling(); 1.212 + *dbgout << "output" << json::array; 1.213 + for(Slot * t = pSlot; t != lEnd; t = t->next()) 1.214 + *dbgout << dslot(this, t); 1.215 + *dbgout << json::close << json::close; 1.216 + } 1.217 +#endif 1.218 + 1.219 + res = positionSlots(font, pSlot, pLast); 1.220 + 1.221 + if (silf()->flags() & 1) 1.222 + { 1.223 + delLineEnd(m_first); 1.224 + delLineEnd(m_last); 1.225 + } 1.226 + m_first = oldFirst; 1.227 + m_last = oldLast; 1.228 + return res.x; 1.229 +} 1.230 + 1.231 +Slot *Segment::addLineEnd(Slot *nSlot) 1.232 +{ 1.233 + Slot *eSlot = newSlot(); 1.234 + if (!eSlot) return NULL; 1.235 + const uint16 gid = silf()->endLineGlyphid(); 1.236 + const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid); 1.237 + eSlot->setGlyph(this, gid, theGlyph); 1.238 + if (nSlot) 1.239 + { 1.240 + eSlot->next(nSlot); 1.241 + eSlot->prev(nSlot->prev()); 1.242 + nSlot->prev(eSlot); 1.243 + eSlot->before(nSlot->before()); 1.244 + if (eSlot->prev()) 1.245 + eSlot->after(eSlot->prev()->after()); 1.246 + else 1.247 + eSlot->after(nSlot->before()); 1.248 + } 1.249 + else 1.250 + { 1.251 + nSlot = m_last; 1.252 + eSlot->prev(nSlot); 1.253 + nSlot->next(eSlot); 1.254 + eSlot->after(eSlot->prev()->after()); 1.255 + eSlot->before(nSlot->after()); 1.256 + } 1.257 + return eSlot; 1.258 +} 1.259 + 1.260 +void Segment::delLineEnd(Slot *s) 1.261 +{ 1.262 + Slot *nSlot = s->next(); 1.263 + if (nSlot) 1.264 + { 1.265 + nSlot->prev(s->prev()); 1.266 + if (s->prev()) 1.267 + s->prev()->next(nSlot); 1.268 + } 1.269 + else 1.270 + s->prev()->next(NULL); 1.271 + freeSlot(s); 1.272 +} 1.273 +