Path: blob/master/thirdparty/graphite/src/Justifier.cpp
9903 views
// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later1// Copyright 2012, SIL International, All rights reserved.234#include "inc/Segment.h"5#include "graphite2/Font.h"6#include "inc/debug.h"7#include "inc/CharInfo.h"8#include "inc/Slot.h"9#include "inc/Main.h"10#include <cmath>1112using namespace graphite2;1314class JustifyTotal {15public:16JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {}17void accumulate(Slot *s, Segment *seg, int level);18int weight() const { return m_tWeight; }1920CLASS_NEW_DELETE2122private:23int m_numGlyphs;24int m_tStretch;25int m_tShrink;26int m_tStep;27int m_tWeight;28};2930void JustifyTotal::accumulate(Slot *s, Segment *seg, int level)31{32++m_numGlyphs;33m_tStretch += s->getJustify(seg, level, 0);34m_tShrink += s->getJustify(seg, level, 1);35m_tStep += s->getJustify(seg, level, 2);36m_tWeight += s->getJustify(seg, level, 3);37}3839float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags jflags, Slot *pFirst, Slot *pLast)40{41Slot *end = last();42float currWidth = 0.0;43const float scale = font ? font->scale() : 1.0f;44Position res;4546if (width < 0 && !(silf()->flags()))47return width;4849if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())50{51reverseSlots();52std::swap(pFirst, pLast);53}54if (!pFirst) pFirst = pSlot;55while (!pFirst->isBase()) pFirst = pFirst->attachedTo();56if (!pLast) pLast = last();57while (!pLast->isBase()) pLast = pLast->attachedTo();58const float base = pFirst->origin().x / scale;59width = width / scale;60if ((jflags & gr_justEndInline) == 0)61{62while (pLast != pFirst && pLast)63{64Rect bbox = theGlyphBBoxTemporary(pLast->glyph());65if (bbox.bl.x != 0.f || bbox.bl.y != 0.f || bbox.tr.x != 0.f || bbox.tr.y == 0.f)66break;67pLast = pLast->prev();68}69}7071if (pLast)72end = pLast->nextSibling();73if (pFirst)74pFirst = pFirst->nextSibling();7576int icount = 0;77int numLevels = silf()->numJustLevels();78if (!numLevels)79{80for (Slot *s = pSlot; s && s != end; s = s->nextSibling())81{82CharInfo *c = charinfo(s->before());83if (isWhitespace(c->unicodeChar()))84{85s->setJustify(this, 0, 3, 1);86s->setJustify(this, 0, 2, 1);87s->setJustify(this, 0, 0, -1);88++icount;89}90}91if (!icount)92{93for (Slot *s = pSlot; s && s != end; s = s->nextSibling())94{95s->setJustify(this, 0, 3, 1);96s->setJustify(this, 0, 2, 1);97s->setJustify(this, 0, 0, -1);98}99}100++numLevels;101}102103Vector<JustifyTotal> stats(numLevels);104for (Slot *s = pFirst; s && s != end; s = s->nextSibling())105{106float w = s->origin().x / scale + s->advance() - base;107if (w > currWidth) currWidth = w;108for (int j = 0; j < numLevels; ++j)109stats[j].accumulate(s, this, j);110s->just(0);111}112113for (int i = (width < 0.0f) ? -1 : numLevels - 1; i >= 0; --i)114{115float diff;116float error = 0.;117float diffpw;118int tWeight = stats[i].weight();119if (tWeight == 0) continue;120121do {122error = 0.;123diff = width - currWidth;124diffpw = diff / tWeight;125tWeight = 0;126for (Slot *s = pFirst; s && s != end; s = s->nextSibling()) // don't include final glyph127{128int w = s->getJustify(this, i, 3);129float pref = diffpw * w + error;130int step = s->getJustify(this, i, 2);131if (!step) step = 1; // handle lazy font developers132if (pref > 0)133{134float max = uint16(s->getJustify(this, i, 0));135if (i == 0) max -= s->just();136if (pref > max) pref = max;137else tWeight += w;138}139else140{141float max = uint16(s->getJustify(this, i, 1));142if (i == 0) max += s->just();143if (-pref > max) pref = -max;144else tWeight += w;145}146int actual = int(pref / step) * step;147148if (actual)149{150error += diffpw * w - actual;151if (i == 0)152s->just(s->just() + actual);153else154s->setJustify(this, i, 4, actual);155}156}157currWidth += diff - error;158} while (i == 0 && int(std::abs(error)) > 0 && tWeight);159}160161Slot *oldFirst = m_first;162Slot *oldLast = m_last;163if (silf()->flags() & 1)164{165m_first = pSlot = addLineEnd(pSlot);166m_last = pLast = addLineEnd(end);167if (!m_first || !m_last) return -1.0;168}169else170{171m_first = pSlot;172m_last = pLast;173}174175// run justification passes here176#if !defined GRAPHITE2_NTRACING177json * const dbgout = m_face->logger();178if (dbgout)179*dbgout << json::object180<< "justifies" << objectid(this)181<< "passes" << json::array;182#endif183184if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0.f || (silf()->flags() & 1)))185m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass());186187#if !defined GRAPHITE2_NTRACING188if (dbgout)189{190*dbgout << json::item << json::close; // Close up the passes array191positionSlots(NULL, pSlot, pLast, m_dir);192Slot *lEnd = pLast->nextSibling();193*dbgout << "output" << json::array;194for(Slot * t = pSlot; t != lEnd; t = t->next())195*dbgout << dslot(this, t);196*dbgout << json::close << json::close;197}198#endif199200res = positionSlots(font, pSlot, pLast, m_dir);201202if (silf()->flags() & 1)203{204if (m_first)205delLineEnd(m_first);206if (m_last)207delLineEnd(m_last);208}209m_first = oldFirst;210m_last = oldLast;211212if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())213reverseSlots();214return res.x;215}216217Slot *Segment::addLineEnd(Slot *nSlot)218{219Slot *eSlot = newSlot();220if (!eSlot) return NULL;221const uint16 gid = silf()->endLineGlyphid();222const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);223eSlot->setGlyph(this, gid, theGlyph);224if (nSlot)225{226eSlot->next(nSlot);227eSlot->prev(nSlot->prev());228nSlot->prev(eSlot);229eSlot->before(nSlot->before());230if (eSlot->prev())231eSlot->after(eSlot->prev()->after());232else233eSlot->after(nSlot->before());234}235else236{237nSlot = m_last;238eSlot->prev(nSlot);239nSlot->next(eSlot);240eSlot->after(eSlot->prev()->after());241eSlot->before(nSlot->after());242}243return eSlot;244}245246void Segment::delLineEnd(Slot *s)247{248Slot *nSlot = s->next();249if (nSlot)250{251nSlot->prev(s->prev());252if (s->prev())253s->prev()->next(nSlot);254}255else256s->prev()->next(NULL);257freeSlot(s);258}259260261