| 1 | // SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later |
| 2 | // Copyright 2012, SIL International, All rights reserved. |
| 3 | |
| 4 | |
| 5 | #include "inc/Segment.h" |
| 6 | #include "graphite2/Font.h" |
| 7 | #include "inc/debug.h" |
| 8 | #include "inc/CharInfo.h" |
| 9 | #include "inc/Slot.h" |
| 10 | #include "inc/Main.h" |
| 11 | #include <cmath> |
| 12 | |
| 13 | using namespace graphite2; |
| 14 | |
| 15 | class JustifyTotal { |
| 16 | public: |
| 17 | JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {} |
| 18 | void accumulate(Slot *s, Segment *seg, int level); |
| 19 | int weight() const { return m_tWeight; } |
| 20 | |
| 21 | CLASS_NEW_DELETE |
| 22 | |
| 23 | private: |
| 24 | int m_numGlyphs; |
| 25 | int m_tStretch; |
| 26 | int m_tShrink; |
| 27 | int m_tStep; |
| 28 | int m_tWeight; |
| 29 | }; |
| 30 | |
| 31 | void JustifyTotal::accumulate(Slot *s, Segment *seg, int level) |
| 32 | { |
| 33 | ++m_numGlyphs; |
| 34 | m_tStretch += s->getJustify(seg, level, 0); |
| 35 | m_tShrink += s->getJustify(seg, level, 1); |
| 36 | m_tStep += s->getJustify(seg, level, 2); |
| 37 | m_tWeight += s->getJustify(seg, level, 3); |
| 38 | } |
| 39 | |
| 40 | float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags jflags, Slot *pFirst, Slot *pLast) |
| 41 | { |
| 42 | Slot *end = last(); |
| 43 | float currWidth = 0.0; |
| 44 | const float scale = font ? font->scale() : 1.0f; |
| 45 | Position res; |
| 46 | |
| 47 | if (width < 0 && !(silf()->flags())) |
| 48 | return width; |
| 49 | |
| 50 | if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses()) |
| 51 | { |
| 52 | reverseSlots(); |
| 53 | std::swap(pFirst, pLast); |
| 54 | } |
| 55 | if (!pFirst) pFirst = pSlot; |
| 56 | while (!pFirst->isBase()) pFirst = pFirst->attachedTo(); |
| 57 | if (!pLast) pLast = last(); |
| 58 | while (!pLast->isBase()) pLast = pLast->attachedTo(); |
| 59 | const float base = pFirst->origin().x / scale; |
| 60 | width = width / scale; |
| 61 | if ((jflags & gr_justEndInline) == 0) |
| 62 | { |
| 63 | while (pLast != pFirst && pLast) |
| 64 | { |
| 65 | Rect bbox = theGlyphBBoxTemporary(pLast->glyph()); |
| 66 | if (bbox.bl.x != 0.f || bbox.bl.y != 0.f || bbox.tr.x != 0.f || bbox.tr.y == 0.f) |
| 67 | break; |
| 68 | pLast = pLast->prev(); |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | if (pLast) |
| 73 | end = pLast->nextSibling(); |
| 74 | if (pFirst) |
| 75 | pFirst = pFirst->nextSibling(); |
| 76 | |
| 77 | int icount = 0; |
| 78 | int numLevels = silf()->numJustLevels(); |
| 79 | if (!numLevels) |
| 80 | { |
| 81 | for (Slot *s = pSlot; s && s != end; s = s->nextSibling()) |
| 82 | { |
| 83 | CharInfo *c = charinfo(s->before()); |
| 84 | if (isWhitespace(c->unicodeChar())) |
| 85 | { |
| 86 | s->setJustify(this, 0, 3, 1); |
| 87 | s->setJustify(this, 0, 2, 1); |
| 88 | s->setJustify(this, 0, 0, -1); |
| 89 | ++icount; |
| 90 | } |
| 91 | } |
| 92 | if (!icount) |
| 93 | { |
| 94 | for (Slot *s = pSlot; s && s != end; s = s->nextSibling()) |
| 95 | { |
| 96 | s->setJustify(this, 0, 3, 1); |
| 97 | s->setJustify(this, 0, 2, 1); |
| 98 | s->setJustify(this, 0, 0, -1); |
| 99 | } |
| 100 | } |
| 101 | ++numLevels; |
| 102 | } |
| 103 | |
| 104 | Vector<JustifyTotal> stats(numLevels); |
| 105 | for (Slot *s = pFirst; s && s != end; s = s->nextSibling()) |
| 106 | { |
| 107 | float w = s->origin().x / scale + s->advance() - base; |
| 108 | if (w > currWidth) currWidth = w; |
| 109 | for (int j = 0; j < numLevels; ++j) |
| 110 | stats[j].accumulate(s, this, j); |
| 111 | s->just(0); |
| 112 | } |
| 113 | |
| 114 | for (int i = (width < 0.0f) ? -1 : numLevels - 1; i >= 0; --i) |
| 115 | { |
| 116 | float diff; |
| 117 | float error = 0.; |
| 118 | float diffpw; |
| 119 | int tWeight = stats[i].weight(); |
| 120 | if (tWeight == 0) continue; |
| 121 | |
| 122 | do { |
| 123 | error = 0.; |
| 124 | diff = width - currWidth; |
| 125 | diffpw = diff / tWeight; |
| 126 | tWeight = 0; |
| 127 | for (Slot *s = pFirst; s && s != end; s = s->nextSibling()) // don't include final glyph |
| 128 | { |
| 129 | int w = s->getJustify(this, i, 3); |
| 130 | float pref = diffpw * w + error; |
| 131 | int step = s->getJustify(this, i, 2); |
| 132 | if (!step) step = 1; // handle lazy font developers |
| 133 | if (pref > 0) |
| 134 | { |
| 135 | float max = uint16(s->getJustify(this, i, 0)); |
| 136 | if (i == 0) max -= s->just(); |
| 137 | if (pref > max) pref = max; |
| 138 | else tWeight += w; |
| 139 | } |
| 140 | else |
| 141 | { |
| 142 | float max = uint16(s->getJustify(this, i, 1)); |
| 143 | if (i == 0) max += s->just(); |
| 144 | if (-pref > max) pref = -max; |
| 145 | else tWeight += w; |
| 146 | } |
| 147 | int actual = int(pref / step) * step; |
| 148 | |
| 149 | if (actual) |
| 150 | { |
| 151 | error += diffpw * w - actual; |
| 152 | if (i == 0) |
| 153 | s->just(s->just() + actual); |
| 154 | else |
| 155 | s->setJustify(this, i, 4, actual); |
| 156 | } |
| 157 | } |
| 158 | currWidth += diff - error; |
| 159 | } while (i == 0 && int(std::abs(error)) > 0 && tWeight); |
| 160 | } |
| 161 | |
| 162 | Slot *oldFirst = m_first; |
| 163 | Slot *oldLast = m_last; |
| 164 | if (silf()->flags() & 1) |
| 165 | { |
| 166 | m_first = pSlot = addLineEnd(pSlot); |
| 167 | m_last = pLast = addLineEnd(end); |
| 168 | if (!m_first || !m_last) return -1.0; |
| 169 | } |
| 170 | else |
| 171 | { |
| 172 | m_first = pSlot; |
| 173 | m_last = pLast; |
| 174 | } |
| 175 | |
| 176 | // run justification passes here |
| 177 | #if !defined GRAPHITE2_NTRACING |
| 178 | json * const dbgout = m_face->logger(); |
| 179 | if (dbgout) |
| 180 | *dbgout << json::object |
| 181 | << "justifies" << objectid(this) |
| 182 | << "passes" << json::array; |
| 183 | #endif |
| 184 | |
| 185 | if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0.f || (silf()->flags() & 1))) |
| 186 | m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass()); |
| 187 | |
| 188 | #if !defined GRAPHITE2_NTRACING |
| 189 | if (dbgout) |
| 190 | { |
| 191 | *dbgout << json::item << json::close; // Close up the passes array |
| 192 | positionSlots(NULL, pSlot, pLast, m_dir); |
| 193 | Slot *lEnd = pLast->nextSibling(); |
| 194 | *dbgout << "output" << json::array; |
| 195 | for(Slot * t = pSlot; t != lEnd; t = t->next()) |
| 196 | *dbgout << dslot(this, t); |
| 197 | *dbgout << json::close << json::close; |
| 198 | } |
| 199 | #endif |
| 200 | |
| 201 | res = positionSlots(font, pSlot, pLast, m_dir); |
| 202 | |
| 203 | if (silf()->flags() & 1) |
| 204 | { |
| 205 | if (m_first) |
| 206 | delLineEnd(m_first); |
| 207 | if (m_last) |
| 208 | delLineEnd(m_last); |
| 209 | } |
| 210 | m_first = oldFirst; |
| 211 | m_last = oldLast; |
| 212 | |
| 213 | if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses()) |
| 214 | reverseSlots(); |
| 215 | return res.x; |
| 216 | } |
| 217 | |
| 218 | Slot *Segment::addLineEnd(Slot *nSlot) |
| 219 | { |
| 220 | Slot *eSlot = newSlot(); |
| 221 | if (!eSlot) return NULL; |
| 222 | const uint16 gid = silf()->endLineGlyphid(); |
| 223 | const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid); |
| 224 | eSlot->setGlyph(this, gid, theGlyph); |
| 225 | if (nSlot) |
| 226 | { |
| 227 | eSlot->next(nSlot); |
| 228 | eSlot->prev(nSlot->prev()); |
| 229 | nSlot->prev(eSlot); |
| 230 | eSlot->before(nSlot->before()); |
| 231 | if (eSlot->prev()) |
| 232 | eSlot->after(eSlot->prev()->after()); |
| 233 | else |
| 234 | eSlot->after(nSlot->before()); |
| 235 | } |
| 236 | else |
| 237 | { |
| 238 | nSlot = m_last; |
| 239 | eSlot->prev(nSlot); |
| 240 | nSlot->next(eSlot); |
| 241 | eSlot->after(eSlot->prev()->after()); |
| 242 | eSlot->before(nSlot->after()); |
| 243 | } |
| 244 | return eSlot; |
| 245 | } |
| 246 | |
| 247 | void Segment::delLineEnd(Slot *s) |
| 248 | { |
| 249 | Slot *nSlot = s->next(); |
| 250 | if (nSlot) |
| 251 | { |
| 252 | nSlot->prev(s->prev()); |
| 253 | if (s->prev()) |
| 254 | s->prev()->next(nSlot); |
| 255 | } |
| 256 | else |
| 257 | s->prev()->next(NULL); |
| 258 | freeSlot(s); |
| 259 | } |
| 260 | |