michael@0: /* GRAPHITE2 LICENSING michael@0: michael@0: Copyright 2012, SIL International michael@0: All rights reserved. michael@0: michael@0: This library is free software; you can redistribute it and/or modify michael@0: it under the terms of the GNU Lesser General Public License as published michael@0: by the Free Software Foundation; either version 2.1 of License, or michael@0: (at your option) any later version. michael@0: michael@0: This program is distributed in the hope that it will be useful, michael@0: but WITHOUT ANY WARRANTY; without even the implied warranty of michael@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU michael@0: Lesser General Public License for more details. michael@0: michael@0: You should also have received a copy of the GNU Lesser General Public michael@0: License along with this library in the file named "LICENSE". michael@0: If not, write to the Free Software Foundation, 51 Franklin Street, michael@0: Suite 500, Boston, MA 02110-1335, USA or visit their web page on the michael@0: internet at http://www.fsf.org/licenses/lgpl.html. michael@0: michael@0: Alternatively, the contents of this file may be used under the terms of the michael@0: Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public michael@0: License, as published by the Free Software Foundation, either version 2 michael@0: of the License or (at your option) any later version. michael@0: */ michael@0: michael@0: #include "inc/Segment.h" michael@0: #include "graphite2/Font.h" michael@0: #include "inc/debug.h" michael@0: #include "inc/CharInfo.h" michael@0: #include "inc/Slot.h" michael@0: #include "inc/Main.h" michael@0: #include michael@0: michael@0: using namespace graphite2; michael@0: michael@0: class JustifyTotal { michael@0: public: michael@0: JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {} michael@0: void accumulate(Slot *s, Segment *seg, int level); michael@0: int weight() const { return m_tWeight; } michael@0: michael@0: CLASS_NEW_DELETE michael@0: michael@0: private: michael@0: int m_numGlyphs; michael@0: int m_tStretch; michael@0: int m_tShrink; michael@0: int m_tStep; michael@0: int m_tWeight; michael@0: }; michael@0: michael@0: void JustifyTotal::accumulate(Slot *s, Segment *seg, int level) michael@0: { michael@0: ++m_numGlyphs; michael@0: m_tStretch += s->getJustify(seg, level, 0); michael@0: m_tShrink += s->getJustify(seg, level, 1); michael@0: m_tStep += s->getJustify(seg, level, 2); michael@0: m_tWeight += s->getJustify(seg, level, 3); michael@0: } michael@0: michael@0: float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags flags, Slot *pFirst, Slot *pLast) michael@0: { michael@0: Slot *s, *end; michael@0: float currWidth = 0.0; michael@0: const float scale = font ? font->scale() : 1.0f; michael@0: Position res; michael@0: michael@0: if (width < 0 && !(silf()->flags())) michael@0: return width; michael@0: michael@0: if (!pFirst) pFirst = pSlot; michael@0: while (!pFirst->isBase()) pFirst = pFirst->attachedTo(); michael@0: if (!pLast) pLast = last(); michael@0: while (!pLast->isBase()) pLast = pLast->attachedTo(); michael@0: const float base = pFirst->origin().x / scale; michael@0: width = width / scale; michael@0: if ((flags & gr_justEndInline) == 0) michael@0: { michael@0: do { michael@0: Rect bbox = theGlyphBBoxTemporary(pLast->glyph()); michael@0: if (bbox.bl.x != 0. || bbox.bl.y != 0. || bbox.tr.x != 0. || bbox.tr.y == 0.) michael@0: break; michael@0: pLast = pLast->prev(); michael@0: } while (pLast != pFirst); michael@0: } michael@0: michael@0: end = pLast->nextSibling(); michael@0: pFirst = pFirst->nextSibling(); michael@0: michael@0: int icount = 0; michael@0: int numLevels = silf()->numJustLevels(); michael@0: if (!numLevels) michael@0: { michael@0: for (s = pSlot; s != end; s = s->next()) michael@0: { michael@0: CharInfo *c = charinfo(s->before()); michael@0: if (isWhitespace(c->unicodeChar())) michael@0: { michael@0: s->setJustify(this, 0, 3, 1); michael@0: s->setJustify(this, 0, 2, 1); michael@0: s->setJustify(this, 0, 0, -1); michael@0: ++icount; michael@0: } michael@0: } michael@0: if (!icount) michael@0: { michael@0: for (s = pSlot; s != end; s = s->nextSibling()) michael@0: { michael@0: s->setJustify(this, 0, 3, 1); michael@0: s->setJustify(this, 0, 2, 1); michael@0: s->setJustify(this, 0, 0, -1); michael@0: } michael@0: } michael@0: ++numLevels; michael@0: } michael@0: michael@0: JustifyTotal *stats = new JustifyTotal[numLevels]; michael@0: if (!stats) return -1.0; michael@0: for (s = pFirst; s != end; s = s->nextSibling()) michael@0: { michael@0: float w = s->origin().x / scale + s->advance() - base; michael@0: if (w > currWidth) currWidth = w; michael@0: for (int j = 0; j < numLevels; ++j) michael@0: stats[j].accumulate(s, this, j); michael@0: s->just(0); michael@0: } michael@0: michael@0: for (int i = (width < 0.0) ? -1 : numLevels - 1; i >= 0; --i) michael@0: { michael@0: float diff; michael@0: float error = 0.; michael@0: float diffpw; michael@0: int tWeight = stats[i].weight(); michael@0: michael@0: do { michael@0: error = 0.; michael@0: diff = width - currWidth; michael@0: diffpw = diff / tWeight; michael@0: tWeight = 0; michael@0: for (s = pFirst; s != end; s = s->nextSibling()) // don't include final glyph michael@0: { michael@0: int w = s->getJustify(this, i, 3); michael@0: float pref = diffpw * w + error; michael@0: int step = s->getJustify(this, i, 2); michael@0: if (!step) step = 1; // handle lazy font developers michael@0: if (pref > 0) michael@0: { michael@0: float max = uint16(s->getJustify(this, i, 0)); michael@0: if (i == 0) max -= s->just(); michael@0: if (pref > max) pref = max; michael@0: else tWeight += w; michael@0: } michael@0: else michael@0: { michael@0: float max = uint16(s->getJustify(this, i, 1)); michael@0: if (i == 0) max += s->just(); michael@0: if (-pref > max) pref = -max; michael@0: else tWeight += w; michael@0: } michael@0: int actual = step ? int(pref / step) * step : int(pref); michael@0: michael@0: if (actual) michael@0: { michael@0: error += diffpw * w - actual; michael@0: if (i == 0) michael@0: s->just(s->just() + actual); michael@0: else michael@0: s->setJustify(this, i, 4, actual); michael@0: } michael@0: } michael@0: currWidth += diff - error; michael@0: } while (i == 0 && int(abs(error)) > 0 && tWeight); michael@0: } michael@0: michael@0: Slot *oldFirst = m_first; michael@0: Slot *oldLast = m_last; michael@0: if (silf()->flags() & 1) michael@0: { michael@0: m_first = pSlot = addLineEnd(pSlot); michael@0: m_last = pLast = addLineEnd(end); michael@0: if (!m_first || !m_last) return -1.0; michael@0: } michael@0: else michael@0: { michael@0: m_first = pSlot; michael@0: m_last = pLast; michael@0: } michael@0: michael@0: // run justification passes here michael@0: #if !defined GRAPHITE2_NTRACING michael@0: json * const dbgout = m_face->logger(); michael@0: if (dbgout) michael@0: *dbgout << json::object michael@0: << "justifies" << objectid(this) michael@0: << "passes" << json::array; michael@0: #endif michael@0: michael@0: if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0. || (silf()->flags() & 1))) michael@0: m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass()); michael@0: michael@0: #if !defined GRAPHITE2_NTRACING michael@0: if (dbgout) michael@0: { michael@0: *dbgout << json::item << json::close; // Close up the passes array michael@0: positionSlots(NULL, pSlot, pLast); michael@0: Slot *lEnd = pLast->nextSibling(); michael@0: *dbgout << "output" << json::array; michael@0: for(Slot * t = pSlot; t != lEnd; t = t->next()) michael@0: *dbgout << dslot(this, t); michael@0: *dbgout << json::close << json::close; michael@0: } michael@0: #endif michael@0: michael@0: res = positionSlots(font, pSlot, pLast); michael@0: michael@0: if (silf()->flags() & 1) michael@0: { michael@0: delLineEnd(m_first); michael@0: delLineEnd(m_last); michael@0: } michael@0: m_first = oldFirst; michael@0: m_last = oldLast; michael@0: return res.x; michael@0: } michael@0: michael@0: Slot *Segment::addLineEnd(Slot *nSlot) michael@0: { michael@0: Slot *eSlot = newSlot(); michael@0: if (!eSlot) return NULL; michael@0: const uint16 gid = silf()->endLineGlyphid(); michael@0: const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid); michael@0: eSlot->setGlyph(this, gid, theGlyph); michael@0: if (nSlot) michael@0: { michael@0: eSlot->next(nSlot); michael@0: eSlot->prev(nSlot->prev()); michael@0: nSlot->prev(eSlot); michael@0: eSlot->before(nSlot->before()); michael@0: if (eSlot->prev()) michael@0: eSlot->after(eSlot->prev()->after()); michael@0: else michael@0: eSlot->after(nSlot->before()); michael@0: } michael@0: else michael@0: { michael@0: nSlot = m_last; michael@0: eSlot->prev(nSlot); michael@0: nSlot->next(eSlot); michael@0: eSlot->after(eSlot->prev()->after()); michael@0: eSlot->before(nSlot->after()); michael@0: } michael@0: return eSlot; michael@0: } michael@0: michael@0: void Segment::delLineEnd(Slot *s) michael@0: { michael@0: Slot *nSlot = s->next(); michael@0: if (nSlot) michael@0: { michael@0: nSlot->prev(s->prev()); michael@0: if (s->prev()) michael@0: s->prev()->next(nSlot); michael@0: } michael@0: else michael@0: s->prev()->next(NULL); michael@0: freeSlot(s); michael@0: } michael@0: