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