From 0609341b3d2901bbc501210774cb426ac46f1316 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Fri, 5 Jun 2020 08:07:14 +0200 Subject: [PATCH 01/50] Move some flaky text tests into Lancelot There are some slight differences between normal drawText() and QGlyphRuns/QStaticText for decoration widths in certain fonts. We decided to accept this and tried working around it in the test by using ForceIntegerMetrics (since the difference is < 0.5 pixel). This enum has been deprecated, so we move the tests into Lancelot instead, since the idea here is to test for regressions, not to compare the two painter commands. Note that there is something off about decorations with drawGlyphRuns() and drawStaticText() which is exposed (not caused) by this, perhaps related to using a matrix for positioning, since that was untested before. This also takes the liberty of moving the emoji test string from text.qps, since this was not in the statictext.qps yet. Change-Id: Ib2d697095cbd11829cdd50b3c0268c85e9607c78 Reviewed-by: Eirik Aavitsland (cherry picked from commit 3279c8e7d72c5eee379fa8e4a2789acfe40445c0) --- .../auto/gui/text/qglyphrun/tst_qglyphrun.cpp | 132 ------------- .../gui/text/qstatictext/tst_qstatictext.cpp | 111 ----------- tests/auto/other/lancelot/paintcommands.cpp | 48 ++++- tests/auto/other/lancelot/paintcommands.h | 1 + .../auto/other/lancelot/scripts/glyphruns.qps | 175 ++++++++++++++++++ .../other/lancelot/scripts/statictext.qps | 35 ++++ tests/auto/other/lancelot/scripts/text.qps | 30 +++ 7 files changed, 287 insertions(+), 245 deletions(-) create mode 100644 tests/auto/other/lancelot/scripts/glyphruns.qps diff --git a/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp b/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp index 98e07919ef2..05f4a385947 100644 --- a/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp +++ b/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp @@ -56,9 +56,6 @@ private slots: void drawNonExistentGlyphs(); void drawMultiScriptText1(); void drawMultiScriptText2(); - void drawStruckOutText(); - void drawOverlinedText(); - void drawUnderlinedText(); void drawRightToLeft(); void detach(); void setRawData(); @@ -512,135 +509,6 @@ void tst_QGlyphRun::detach() QCOMPARE(glyphs.glyphIndexes(), QVector() << 1 << 2 << 3); } -void tst_QGlyphRun::drawStruckOutText() -{ - QPixmap textLayoutDraw(1000, 1000); - QPixmap drawGlyphs(1000, 1000); - - textLayoutDraw.fill(Qt::white); - drawGlyphs.fill(Qt::white); - - QString s = QString::fromLatin1("Foobar"); - - QFont font; - font.setStrikeOut(true); - font.setStyleStrategy(QFont::ForceIntegerMetrics); - - QTextLayout layout(s); - layout.setFont(font); - layout.setCacheEnabled(true); - layout.beginLayout(); - layout.createLine(); - layout.endLayout(); - - { - QPainter p(&textLayoutDraw); - layout.draw(&p, QPointF(50, 50)); - } - - QGlyphRun glyphs = layout.glyphRuns().size() > 0 - ? layout.glyphRuns().at(0) - : QGlyphRun(); - - { - QPainter p(&drawGlyphs); - p.drawGlyphRun(QPointF(50, 50), glyphs); - } - -#if defined(DEBUG_SAVE_IMAGE) - textLayoutDraw.save("drawStruckOutText_textLayoutDraw.png"); - drawGlyphs.save("drawStruckOutText_drawGlyphIndexes.png"); -#endif - - QCOMPARE(textLayoutDraw, drawGlyphs); -} - -void tst_QGlyphRun::drawOverlinedText() -{ - QPixmap textLayoutDraw(1000, 1000); - QPixmap drawGlyphs(1000, 1000); - - textLayoutDraw.fill(Qt::white); - drawGlyphs.fill(Qt::white); - - QString s = QString::fromLatin1("Foobar"); - - QFont font; - font.setOverline(true); - font.setStyleStrategy(QFont::ForceIntegerMetrics); - - QTextLayout layout(s); - layout.setFont(font); - layout.setCacheEnabled(true); - layout.beginLayout(); - layout.createLine(); - layout.endLayout(); - - { - QPainter p(&textLayoutDraw); - layout.draw(&p, QPointF(50, 50)); - } - - QGlyphRun glyphs = layout.glyphRuns().size() > 0 - ? layout.glyphRuns().at(0) - : QGlyphRun(); - - { - QPainter p(&drawGlyphs); - p.drawGlyphRun(QPointF(50, 50), glyphs); - } - -#if defined(DEBUG_SAVE_IMAGE) - textLayoutDraw.save("drawOverlineText_textLayoutDraw.png"); - drawGlyphs.save("drawOverlineText_drawGlyphIndexes.png"); -#endif - - QCOMPARE(textLayoutDraw, drawGlyphs); -} - -void tst_QGlyphRun::drawUnderlinedText() -{ - QPixmap textLayoutDraw(1000, 1000); - QPixmap drawGlyphs(1000, 1000); - - textLayoutDraw.fill(Qt::white); - drawGlyphs.fill(Qt::white); - - QString s = QString::fromLatin1("Foobar"); - - QFont font; - font.setUnderline(true); - font.setStyleStrategy(QFont::ForceIntegerMetrics); - - QTextLayout layout(s); - layout.setFont(font); - layout.setCacheEnabled(true); - layout.beginLayout(); - layout.createLine(); - layout.endLayout(); - - { - QPainter p(&textLayoutDraw); - layout.draw(&p, QPointF(50, 50)); - } - - QGlyphRun glyphs = layout.glyphRuns().size() > 0 - ? layout.glyphRuns().at(0) - : QGlyphRun(); - - { - QPainter p(&drawGlyphs); - p.drawGlyphRun(QPointF(50, 50), glyphs); - } - -#if defined(DEBUG_SAVE_IMAGE) - textLayoutDraw.save("drawUnderlineText_textLayoutDraw.png"); - drawGlyphs.save("drawUnderlineText_drawGlyphIndexes.png"); -#endif - - QCOMPARE(textLayoutDraw, drawGlyphs); -} - void tst_QGlyphRun::drawRightToLeft() { QString s; diff --git a/tests/auto/gui/text/qstatictext/tst_qstatictext.cpp b/tests/auto/gui/text/qstatictext/tst_qstatictext.cpp index b091edb64da..645ff39cbea 100644 --- a/tests/auto/gui/text/qstatictext/tst_qstatictext.cpp +++ b/tests/auto/gui/text/qstatictext/tst_qstatictext.cpp @@ -87,10 +87,6 @@ private slots: void setPenRichText(); void richTextOverridesPen(); - void drawStruckOutText(); - void drawOverlinedText(); - void drawUnderlinedText(); - void unprintableCharacter_qtbug12614(); #ifdef QT_BUILD_INTERNAL @@ -774,113 +770,6 @@ void tst_QStaticText::richTextOverridesPen() errorMessage.constData()); } -void tst_QStaticText::drawStruckOutText() -{ - QPixmap imageDrawText(1000, 1000); - QPixmap imageDrawStaticText(1000, 1000); - - imageDrawText.fill(Qt::white); - imageDrawStaticText.fill(Qt::white); - - QString s = QString::fromLatin1("Foobar"); - - QFont font; - font.setStrikeOut(true); - font.setStyleStrategy(QFont::ForceIntegerMetrics); - - { - QPainter p(&imageDrawText); - p.setFont(font); - p.drawText(QPointF(50, 50), s); - } - - { - QPainter p(&imageDrawStaticText); - QStaticText text = QStaticText(s); - p.setFont(font); - p.drawStaticText(QPointF(50, 50 - QFontMetricsF(p.font()).ascent()), text); - } - -#if defined(DEBUG_SAVE_IMAGE) - imageDrawText.save("drawStruckOutText_imageDrawText.png"); - imageDrawStaticText.save("drawStruckOutText_imageDrawStaticText.png"); -#endif - - QVERIFY(imageDrawText.toImage() != m_whiteSquare); - QCOMPARE(imageDrawText, imageDrawStaticText); -} - -void tst_QStaticText::drawOverlinedText() -{ - QPixmap imageDrawText(1000, 1000); - QPixmap imageDrawStaticText(1000, 1000); - - imageDrawText.fill(Qt::white); - imageDrawStaticText.fill(Qt::white); - - QString s = QString::fromLatin1("Foobar"); - - QFont font; - font.setOverline(true); - font.setStyleStrategy(QFont::ForceIntegerMetrics); - - { - QPainter p(&imageDrawText); - p.setFont(font); - p.drawText(QPointF(50, 50), s); - } - - { - QPainter p(&imageDrawStaticText); - QStaticText text = QStaticText(s); - p.setFont(font); - p.drawStaticText(QPointF(50, 50 - QFontMetricsF(p.font()).ascent()), text); - } - -#if defined(DEBUG_SAVE_IMAGE) - imageDrawText.save("drawOverlinedText_imageDrawText.png"); - imageDrawStaticText.save("drawOverlinedText_imageDrawStaticText.png"); -#endif - - QVERIFY(imageDrawText.toImage() != m_whiteSquare); - QCOMPARE(imageDrawText, imageDrawStaticText); -} - -void tst_QStaticText::drawUnderlinedText() -{ - QPixmap imageDrawText(1000, 1000); - QPixmap imageDrawStaticText(1000, 1000); - - imageDrawText.fill(Qt::white); - imageDrawStaticText.fill(Qt::white); - - QString s = QString::fromLatin1("Foobar"); - - QFont font; - font.setUnderline(true); - font.setStyleStrategy(QFont::ForceIntegerMetrics); - - { - QPainter p(&imageDrawText); - p.setFont(font); - p.drawText(QPointF(50, 50), s); - } - - { - QPainter p(&imageDrawStaticText); - QStaticText text = QStaticText(s); - p.setFont(font); - p.drawStaticText(QPointF(50, 50 - QFontMetricsF(p.font()).ascent()), text); - } - -#if defined(DEBUG_SAVE_IMAGE) - imageDrawText.save("drawUnderlinedText_imageDrawText.png"); - imageDrawStaticText.save("drawUnderlinedText_imageDrawStaticText.png"); -#endif - - QCOMPARE(imageDrawText, imageDrawStaticText); -} - void tst_QStaticText::unprintableCharacter_qtbug12614() { QString s(QChar(0x200B)); // U+200B, ZERO WIDTH SPACE diff --git a/tests/auto/other/lancelot/paintcommands.cpp b/tests/auto/other/lancelot/paintcommands.cpp index 0dab5376a07..ed1db8f9d49 100644 --- a/tests/auto/other/lancelot/paintcommands.cpp +++ b/tests/auto/other/lancelot/paintcommands.cpp @@ -297,8 +297,8 @@ void PaintCommands::staticInit() "setCompositionMode ", "setCompositionMode SourceOver"); DECL_PAINTCOMMAND("setFont", command_setFont, - "^setFont\\s+\"([\\w\\s]*)\"\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)$", - "setFont [size] [font weight|font weight enum] [italic] [hinting enum]\n - font weight is an integer between 0 and 99", + "^setFont\\s+\"([\\w\\s]*)\"\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)\\s*(\\w*)$", + "setFont [size] [font weight|font weight enum] [italic] [hinting enum] [underline] [strikeout] [overline]\n - font weight is an integer between 0 and 99", "setFont \"times\" 12"); DECL_PAINTCOMMAND("setPen", command_setPen, "^setPen\\s+#?(\\w*)$", @@ -448,6 +448,10 @@ void PaintCommands::staticInit() "^drawStaticText\\s+(-?\\w*)\\s+(-?\\w*)\\s+\"(.*)\"$", "drawStaticText ", "drawStaticText 10 10 \"my text\""); + DECL_PAINTCOMMAND("drawGlyphRun", command_drawGlyphRun, + "^drawGlyphRun\\s+(-?\\w*)\\s+(-?\\w*)\\s+\"(.*)\"$", + "drawGlyphRun - Will create glyph run using QTextLayout and draw this", + "drawGlyphRun 10 10 \"my text\""); DECL_PAINTCOMMAND("drawTextDocument", command_drawTextDocument, "^drawTextDocument\\s+(-?\\w*)\\s+(-?\\w*)\\s+\"(.*)\"$", "drawTextDocument ", @@ -1351,6 +1355,38 @@ void PaintCommands::command_drawStaticText(QRegularExpressionMatch re) m_painter->drawStaticText(x, y, QStaticText(txt)); } +void PaintCommands::command_drawGlyphRun(QRegularExpressionMatch re) +{ + if (!m_shouldDrawText) + return; + QStringList caps = re.capturedTexts(); + int x = convertToInt(caps.at(1)); + int y = convertToInt(caps.at(2)); + QString txt = caps.at(3); + + if (m_verboseMode) + printf(" -(lance) drawGlyphRun(%d, %d, %s)\n", x, y, qPrintable(txt)); + + QTextLayout layout; + layout.setFont(m_painter->font()); + layout.setText(txt); + layout.beginLayout(); + qreal lineY = 0.0; + forever { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; + line.setPosition(QPointF(0.0, lineY)); + lineY += line.height(); + } + layout.endLayout(); + + QList glyphRuns = layout.glyphRuns(); + + for (const QGlyphRun &glyphRun : glyphRuns) + m_painter->drawGlyphRun(QPointF(x, y), glyphRun); +} + void PaintCommands::command_drawTextDocument(QRegularExpressionMatch re) { if (!m_shouldDrawText) @@ -2093,6 +2129,14 @@ void PaintCommands::command_setFont(QRegularExpressionMatch re) hinting = 0; else font.setHintingPreference(QFont::HintingPreference(hinting)); + + bool underline = caps.at(6).toLower() == "true" || caps.at(6).toLower() == "underline"; + bool strikeOut = caps.at(7).toLower() == "true" || caps.at(7).toLower() == "strikeout"; + bool overline = caps.at(8).toLower() == "true" || caps.at(8).toLower() == "overline"; + font.setUnderline(underline); + font.setStrikeOut(strikeOut); + font.setOverline(overline); + if (m_verboseMode) printf(" -(lance) setFont(family=%s, size=%d, weight=%d, italic=%d hinting=%s\n", qPrintable(family), size, weight, italic, fontHintingTable[hinting]); diff --git a/tests/auto/other/lancelot/paintcommands.h b/tests/auto/other/lancelot/paintcommands.h index 4a1a89556d6..64e3a364668 100644 --- a/tests/auto/other/lancelot/paintcommands.h +++ b/tests/auto/other/lancelot/paintcommands.h @@ -199,6 +199,7 @@ private: void command_drawRoundRect(QRegularExpressionMatch re); void command_drawText(QRegularExpressionMatch re); void command_drawStaticText(QRegularExpressionMatch re); + void command_drawGlyphRun(QRegularExpressionMatch re); void command_drawTextDocument(QRegularExpressionMatch re); void command_drawTiledPixmap(QRegularExpressionMatch re); void command_fillRect(QRegularExpressionMatch re); diff --git a/tests/auto/other/lancelot/scripts/glyphruns.qps b/tests/auto/other/lancelot/scripts/glyphruns.qps new file mode 100644 index 00000000000..2b70c72610f --- /dev/null +++ b/tests/auto/other/lancelot/scripts/glyphruns.qps @@ -0,0 +1,175 @@ +drawGlyphRun -5 5 "Text that is drawn outside the bounds..." + +translate 20 20 +begin_block text_drawing +save + setFont "sansserif" 10 normal + drawGlyphRun 0 20 "sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawGlyphRun 0 40 "sansserif 12pt, normal" + + setFont "sansserif" 12 bold + drawGlyphRun 0 60 "sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawGlyphRun 0 80 "sansserif 10pt, bold italic" + + + translate 0 100 + setPen #7fff0000 + + setFont "sansserif" 10 normal + drawGlyphRun 0 20 "alpha sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawGlyphRun 0 40 "alpha sansserif 12pt, normal" + + setFont "sansserif" 12 bold + drawGlyphRun 0 60 "alpha sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawGlyphRun 0 80 "alpha sansserif 10pt, bold italic" + + + translate 0 100 + setPen black + save + scale 0.9 0.9 + + setFont "sansserif" 10 normal + drawGlyphRun 0 20 "scaled sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawGlyphRun 0 40 "scaled sansserif 12pt, normal" + + setFont "sansserif" 12 bold + drawGlyphRun 0 60 "scaled sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawGlyphRun 0 80 "scaled sansserif 10pt, bold italic" + restore + + translate 200 200 + setPen black + save + scale -1 -1 + + setFont "sansserif" 10 normal + drawGlyphRun 0 20 "flipped sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawGlyphRun 0 40 "flipped sansserif 12pt, normal" + + setFont "sansserif" 12 bold + drawGlyphRun 0 60 "flipped sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawGlyphRun 0 80 "flipped sansserif 10pt, bold italic" + restore + + translate -200 20 + setPen black + save + translate 200 90 + rotate 185 + + setFont "sansserif" 10 normal + drawGlyphRun 0 20 "rotated sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawGlyphRun 0 40 "rotated sansserif 12pt, normal" + + setFont "sansserif" 12 bold + drawGlyphRun 0 60 "rotated sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawGlyphRun 0 80 "rotated sansserif 10pt, bold italic" + restore + + translate 0 100 + gradient_appendStop 0 red + gradient_appendStop 0.5 #00ff00 + gradient_appendStop 1 blue + gradient_setLinear 0 0 200 0 + setPen brush + + setFont "sansserif" 10 normal + drawGlyphRun 0 0 "gradient sansserif 10pt, normal" + + setFont "sansserif" 12 normal + drawGlyphRun 0 20 "gradient sansserif 12pt, normal" + + setFont "sansserif" 12 bold + drawGlyphRun 0 40 "gradient sansserif 12pt, bold" + + setFont "sansserif" 10 bold italic + drawGlyphRun 0 60 "gradient sansserif 10pt, bold italic" +restore +end_block + +translate 250 0 +drawGlyphRun 25 640 "clipped to rectangle" +save + setPen #3f000000 + setBrush nobrush + drawRect 20 0 100 620 + setClipRect 20 0 100 620 + setPen black + repeat_block text_drawing +restore + +translate 150 0 +drawGlyphRun 25 640 "clipped to path" +save + path_moveTo clip 20 0 + path_cubicTo clip 0 200 40 400 20 400 + path_lineTo clip 30 620 + path_lineTo clip 30 0 + path_lineTo clip 40 0 + path_lineTo clip 40 620 + path_lineTo clip 120 620 + path_lineTo clip 120 0 + path_lineTo clip 20 0 + setPen #3f000000 + setBrush nobrush + drawPath clip + setClipPath clip + setPen black + repeat_block text_drawing +restore + +translate 150 0 +save + setPen black + setFont "sansserif" 16 normal + drawGlyphRun 0 40 "e๐Ÿ˜ƒm๐Ÿ˜‡o๐Ÿ˜j๐Ÿ˜œi๐Ÿ˜ธ!" +restore + +translate 0 55 +save + setPen black + setFont "sansserif" 12 normal normal default underline + drawGlyphRun 0 20 "Underlined text drawing" +restore + +translate 0 35 +save + setPen black + setFont "sansserif" 12 normal normal default normal strikeout + drawGlyphRun 0 20 "Struck out text drawing" +restore + +translate 0 35 +save + setPen black + setFont "sansserif" 12 normal normal default normal normal overline + drawGlyphRun 0 20 "Overlined text drawing" +restore + +translate 0 35 +save + setPen black + setFont "sansserif" 12 normal normal default underline strikeout overline + drawGlyphRun 0 20 "All the effects text drawing" +restore diff --git a/tests/auto/other/lancelot/scripts/statictext.qps b/tests/auto/other/lancelot/scripts/statictext.qps index 6b7b97d8faa..c2a30d08649 100644 --- a/tests/auto/other/lancelot/scripts/statictext.qps +++ b/tests/auto/other/lancelot/scripts/statictext.qps @@ -138,3 +138,38 @@ save setPen black repeat_block text_drawing restore + +translate 150 0 +save + setPen black + setFont "sansserif" 16 normal + drawStaticText 0 40 "e๐Ÿ˜ƒm๐Ÿ˜‡o๐Ÿ˜j๐Ÿ˜œi๐Ÿ˜ธ!" +restore + +translate 0 55 +save + setPen black + setFont "sansserif" 12 normal normal default underline + drawStaticText 0 20 "Underlined text drawing" +restore + +translate 0 35 +save + setPen black + setFont "sansserif" 12 normal normal default normal strikeout + drawStaticText 0 20 "Struck out text drawing" +restore + +translate 0 35 +save + setPen black + setFont "sansserif" 12 normal normal default normal normal overline + drawStaticText 0 20 "Overlined text drawing" +restore + +translate 0 35 +save + setPen black + setFont "sansserif" 12 normal normal default underline strikeout overline + drawStaticText 0 20 "All the effects text drawing" +restore diff --git a/tests/auto/other/lancelot/scripts/text.qps b/tests/auto/other/lancelot/scripts/text.qps index 1b4fe4f0644..4d81b3084c7 100644 --- a/tests/auto/other/lancelot/scripts/text.qps +++ b/tests/auto/other/lancelot/scripts/text.qps @@ -167,3 +167,33 @@ save setFont "sansserif" 16 normal drawText 0 40 "e๐Ÿ˜ƒm๐Ÿ˜‡o๐Ÿ˜j๐Ÿ˜œi๐Ÿ˜ธ!" restore + +translate 0 75 +save + setPen black + setFont "sansserif" 12 normal normal default underline + drawText 0 20 "Underlined text drawing" +restore + +translate 0 35 +save + setPen black + setFont "sansserif" 12 normal normal default normal strikeout + drawText 0 20 "Struck out text drawing" +restore + +translate 0 35 +save + setPen black + setFont "sansserif" 12 normal normal default normal normal overline + drawText 0 20 "Overlined text drawing" +restore + +translate 0 35 +save + setPen black + setFont "sansserif" 12 normal normal default underline strikeout overline + drawText 0 20 "All the effects text drawing" +restore + + From 6b92ef96e965a64b9a3e92f8ea3b2f3a57897ef7 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 4 May 2023 08:59:24 +0200 Subject: [PATCH 02/50] Update Harfbuzz to 7.2.0 Qt 5.15 has been lagging behind on Harfbuzz because of compile issues. This is an attempt at doing a full upgrade, duplicating most of the modifications we have done along the way. Two major differences from Qt 6: We still use hb-coretext for macOS shaping, since changing this is not really within scope for the current commit policy of the LTS. In addition, we enable deprecated API because we cannot change the minimum supported system-harfbuzz version, and thus we cannot rely on the replacement APIs. There are still older platforms which cannot compile the new Harfbuzz. Rather than including an outdated third party library for these platforms, we disable Harfbuzz-NG and use Qt 4's built-in shaper instead (equivalent to QT_HARFBUZZ=old). Users can still use -system-harfbuzz on these platforms if they have local versions of Harfbuzz that build. This adds a QSKIP to tst_qfontmetrics::zeroWidthMetrics. Looking at the code which introduced that, it primarily fixes it for freetype. Apparently it ends up passing with Harfbuzz-NG on Windows because we resolve it to a different glyph and this ends up with a large right bearing and negative width. But since it depends on the metrics of the corresponding glyph, it may not be very reliable in this case either. At least for the case where we use the old harfbuzz, the weakness is exposed and the test fails, so for now we just skip the test on this configuration. [ChangeLog][Important behavior change] The included version of the text shaping library Harfbuzz has been disabled on a selection of older platforms where the updated versions do not compile. There may be regressions in text appearance with certain fonts when rendering languages that depend on OpenType features due to this. A possible mitigation is to build an older Harfbuzz version manually and use -system-harfbuzz to use this with Qt. This affects the following platforms: QNX, Integrity, WinRT, Intel ICC compiler, MacOS 10.15 and lower, MSVC 2015 and lower. Task-number: QTBUG-113352 Task-number: QTBUG-111350 Change-Id: I0fad6841c41018586b21c6e2dcd4166c26ba5716 Reviewed-by: Volker Hilsheimer --- src/3rdparty/harfbuzz-ng/AUTHORS | 19 +- src/3rdparty/harfbuzz-ng/COPYING | 20 +- src/3rdparty/harfbuzz-ng/NEWS | 1452 ++++- src/3rdparty/harfbuzz-ng/THANKS | 2 +- src/3rdparty/harfbuzz-ng/config.h | 13 +- src/3rdparty/harfbuzz-ng/harfbuzz-ng.pro | 170 +- src/3rdparty/harfbuzz-ng/hb-dummy.cc | 8 + src/3rdparty/harfbuzz-ng/qt_attribution.json | 33 +- .../harfbuzz-ng/src/OT/Color/CBDT/CBDT.hh | 1030 +++ .../harfbuzz-ng/src/OT/Color/COLR/COLR.hh | 2436 +++++++ .../src/OT/Color/COLR/colrv1-closure.hh | 107 + .../harfbuzz-ng/src/OT/Color/CPAL/CPAL.hh | 350 + .../harfbuzz-ng/src/OT/Color/sbix/sbix.hh | 452 ++ .../harfbuzz-ng/src/OT/Color/svg/svg.hh | 151 + .../src/OT/Layout/Common/Coverage.hh | 337 + .../src/OT/Layout/Common/CoverageFormat1.hh | 133 + .../src/OT/Layout/Common/CoverageFormat2.hh | 232 + .../src/OT/Layout/Common/RangeRecord.hh | 85 + .../harfbuzz-ng/src/OT/Layout/GDEF/GDEF.hh | 918 +++ .../harfbuzz-ng/src/OT/Layout/GPOS/Anchor.hh | 83 + .../src/OT/Layout/GPOS/AnchorFormat1.hh | 46 + .../src/OT/Layout/GPOS/AnchorFormat2.hh | 58 + .../src/OT/Layout/GPOS/AnchorFormat3.hh | 100 + .../src/OT/Layout/GPOS/AnchorMatrix.hh | 77 + .../src/OT/Layout/GPOS/ChainContextPos.hh | 14 + .../harfbuzz-ng/src/OT/Layout/GPOS/Common.hh | 33 + .../src/OT/Layout/GPOS/ContextPos.hh | 14 + .../src/OT/Layout/GPOS/CursivePos.hh | 35 + .../src/OT/Layout/GPOS/CursivePosFormat1.hh | 301 + .../src/OT/Layout/GPOS/ExtensionPos.hh | 17 + .../harfbuzz-ng/src/OT/Layout/GPOS/GPOS.hh | 171 + .../src/OT/Layout/GPOS/LigatureArray.hh | 56 + .../src/OT/Layout/GPOS/MarkArray.hh | 128 + .../src/OT/Layout/GPOS/MarkBasePos.hh | 41 + .../src/OT/Layout/GPOS/MarkBasePosFormat1.hh | 244 + .../src/OT/Layout/GPOS/MarkLigPos.hh | 41 + .../src/OT/Layout/GPOS/MarkLigPosFormat1.hh | 223 + .../src/OT/Layout/GPOS/MarkMarkPos.hh | 42 + .../src/OT/Layout/GPOS/MarkMarkPosFormat1.hh | 228 + .../src/OT/Layout/GPOS/MarkRecord.hh | 52 + .../harfbuzz-ng/src/OT/Layout/GPOS/PairPos.hh | 46 + .../src/OT/Layout/GPOS/PairPosFormat1.hh | 217 + .../src/OT/Layout/GPOS/PairPosFormat2.hh | 351 + .../harfbuzz-ng/src/OT/Layout/GPOS/PairSet.hh | 207 + .../src/OT/Layout/GPOS/PairValueRecord.hh | 99 + .../src/OT/Layout/GPOS/PosLookup.hh | 79 + .../src/OT/Layout/GPOS/PosLookupSubTable.hh | 79 + .../src/OT/Layout/GPOS/SinglePos.hh | 100 + .../src/OT/Layout/GPOS/SinglePosFormat1.hh | 164 + .../src/OT/Layout/GPOS/SinglePosFormat2.hh | 176 + .../src/OT/Layout/GPOS/ValueFormat.hh | 394 ++ .../src/OT/Layout/GSUB/AlternateSet.hh | 126 + .../src/OT/Layout/GSUB/AlternateSubst.hh | 62 + .../OT/Layout/GSUB/AlternateSubstFormat1.hh | 128 + .../src/OT/Layout/GSUB/ChainContextSubst.hh | 18 + .../harfbuzz-ng/src/OT/Layout/GSUB/Common.hh | 21 + .../src/OT/Layout/GSUB/ContextSubst.hh | 18 + .../src/OT/Layout/GSUB/ExtensionSubst.hh | 22 + .../harfbuzz-ng/src/OT/Layout/GSUB/GSUB.hh | 61 + .../src/OT/Layout/GSUB/Ligature.hh | 190 + .../src/OT/Layout/GSUB/LigatureSet.hh | 131 + .../src/OT/Layout/GSUB/LigatureSubst.hh | 71 + .../OT/Layout/GSUB/LigatureSubstFormat1.hh | 166 + .../src/OT/Layout/GSUB/MultipleSubst.hh | 62 + .../OT/Layout/GSUB/MultipleSubstFormat1.hh | 130 + .../OT/Layout/GSUB/ReverseChainSingleSubst.hh | 36 + .../GSUB/ReverseChainSingleSubstFormat1.hh | 244 + .../src/OT/Layout/GSUB/Sequence.hh | 165 + .../src/OT/Layout/GSUB/SingleSubst.hh | 103 + .../src/OT/Layout/GSUB/SingleSubstFormat1.hh | 204 + .../src/OT/Layout/GSUB/SingleSubstFormat2.hh | 176 + .../src/OT/Layout/GSUB/SubstLookup.hh | 220 + .../src/OT/Layout/GSUB/SubstLookupSubTable.hh | 77 + .../harfbuzz-ng/src/OT/Layout/types.hh | 66 + .../harfbuzz-ng/src/OT/glyf/CompositeGlyph.hh | 399 ++ src/3rdparty/harfbuzz-ng/src/OT/glyf/Glyph.hh | 566 ++ .../harfbuzz-ng/src/OT/glyf/GlyphHeader.hh | 52 + .../harfbuzz-ng/src/OT/glyf/SimpleGlyph.hh | 345 + .../harfbuzz-ng/src/OT/glyf/SubsetGlyph.hh | 152 + .../src/OT/glyf/VarCompositeGlyph.hh | 371 ++ .../harfbuzz-ng/src/OT/glyf/composite-iter.hh | 68 + .../harfbuzz-ng/src/OT/glyf/coord-setter.hh | 34 + .../harfbuzz-ng/src/OT/glyf/glyf-helpers.hh | 104 + src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf.hh | 504 ++ src/3rdparty/harfbuzz-ng/src/OT/glyf/loca.hh | 43 + .../harfbuzz-ng/src/OT/glyf/path-builder.hh | 193 + src/3rdparty/harfbuzz-ng/src/OT/name/name.hh | 589 ++ .../harfbuzz-ng/src/graph/classdef-graph.hh | 216 + .../harfbuzz-ng/src/graph/coverage-graph.hh | 152 + src/3rdparty/harfbuzz-ng/src/graph/graph.hh | 1392 ++++ .../harfbuzz-ng/src/graph/gsubgpos-context.cc | 70 + .../harfbuzz-ng/src/graph/gsubgpos-context.hh | 61 + .../harfbuzz-ng/src/graph/gsubgpos-graph.hh | 414 ++ .../src/graph/markbasepos-graph.hh | 510 ++ .../harfbuzz-ng/src/graph/pairpos-graph.hh | 647 ++ .../harfbuzz-ng/src/graph/serialize.hh | 270 + .../harfbuzz-ng/src/graph/split-helpers.hh | 69 + .../src/graph/test-classdef-graph.cc | 119 + .../harfbuzz-ng/src/harfbuzz-subset.cc | 62 + src/3rdparty/harfbuzz-ng/src/harfbuzz.cc | 60 + .../src/hb-aat-layout-ankr-table.hh | 98 + .../src/hb-aat-layout-bsln-table.hh | 158 + .../harfbuzz-ng/src/hb-aat-layout-common.hh | 919 +++ .../src/hb-aat-layout-feat-table.hh | 222 + .../src/hb-aat-layout-just-table.hh | 417 ++ .../src/hb-aat-layout-kerx-table.hh | 1001 +++ .../src/hb-aat-layout-morx-table.hh | 1210 ++++ .../src/hb-aat-layout-opbd-table.hh | 173 + .../src/hb-aat-layout-trak-table.hh | 230 + src/3rdparty/harfbuzz-ng/src/hb-aat-layout.cc | 440 ++ src/3rdparty/harfbuzz-ng/src/hb-aat-layout.h | 795 +++ src/3rdparty/harfbuzz-ng/src/hb-aat-layout.hh | 77 + .../harfbuzz-ng/src/hb-aat-ltag-table.hh | 92 + src/3rdparty/harfbuzz-ng/src/hb-aat-map.cc | 172 + src/3rdparty/harfbuzz-ng/src/hb-aat-map.hh | 123 + src/3rdparty/harfbuzz-ng/src/hb-aat.h | 38 + src/3rdparty/harfbuzz-ng/src/hb-algs.hh | 1405 ++++ src/3rdparty/harfbuzz-ng/src/hb-array.hh | 494 ++ .../harfbuzz-ng/src/hb-atomic-private.hh | 189 - src/3rdparty/harfbuzz-ng/src/hb-atomic.hh | 221 + src/3rdparty/harfbuzz-ng/src/hb-bimap.hh | 164 + src/3rdparty/harfbuzz-ng/src/hb-bit-page.hh | 340 + .../harfbuzz-ng/src/hb-bit-set-invertible.hh | 378 ++ src/3rdparty/harfbuzz-ng/src/hb-bit-set.hh | 968 +++ src/3rdparty/harfbuzz-ng/src/hb-blob.cc | 558 +- src/3rdparty/harfbuzz-ng/src/hb-blob.h | 58 +- src/3rdparty/harfbuzz-ng/src/hb-blob.hh | 98 + .../src/hb-buffer-deserialize-json.hh | 604 +- .../src/hb-buffer-deserialize-text-glyphs.hh | 692 ++ .../src/hb-buffer-deserialize-text-unicode.hh | 332 + .../src/hb-buffer-deserialize-text.hh | 571 -- .../harfbuzz-ng/src/hb-buffer-private.hh | 388 -- .../harfbuzz-ng/src/hb-buffer-serialize.cc | 648 +- .../harfbuzz-ng/src/hb-buffer-verify.cc | 439 ++ src/3rdparty/harfbuzz-ng/src/hb-buffer.cc | 1172 ++-- src/3rdparty/harfbuzz-ng/src/hb-buffer.h | 330 +- src/3rdparty/harfbuzz-ng/src/hb-buffer.hh | 668 ++ .../harfbuzz-ng/src/hb-cache-private.hh | 74 - src/3rdparty/harfbuzz-ng/src/hb-cache.hh | 101 + .../harfbuzz-ng/src/hb-cairo-utils.cc | 874 +++ .../harfbuzz-ng/src/hb-cairo-utils.hh | 107 + src/3rdparty/harfbuzz-ng/src/hb-cairo.cc | 1010 +++ src/3rdparty/harfbuzz-ng/src/hb-cairo.h | 99 + .../harfbuzz-ng/src/hb-cff-interp-common.hh | 643 ++ .../src/hb-cff-interp-cs-common.hh | 907 +++ .../src/hb-cff-interp-dict-common.hh | 201 + .../harfbuzz-ng/src/hb-cff1-interp-cs.hh | 160 + .../harfbuzz-ng/src/hb-cff2-interp-cs.hh | 282 + src/3rdparty/harfbuzz-ng/src/hb-common.cc | 717 ++- src/3rdparty/harfbuzz-ng/src/hb-common.h | 841 ++- src/3rdparty/harfbuzz-ng/src/hb-config.hh | 195 + .../src/{hb-coretext.mm => hb-coretext.cc} | 721 +-- src/3rdparty/harfbuzz-ng/src/hb-coretext.h | 34 +- src/3rdparty/harfbuzz-ng/src/hb-cplusplus.hh | 223 + src/3rdparty/harfbuzz-ng/src/hb-debug.hh | 167 +- src/3rdparty/harfbuzz-ng/src/hb-deprecated.h | 202 +- .../harfbuzz-ng/src/hb-directwrite.cc | 884 +++ src/3rdparty/harfbuzz-ng/src/hb-directwrite.h | 40 + src/3rdparty/harfbuzz-ng/src/hb-dispatch.hh | 60 + src/3rdparty/harfbuzz-ng/src/hb-draw.cc | 458 ++ src/3rdparty/harfbuzz-ng/src/hb-draw.h | 340 + src/3rdparty/harfbuzz-ng/src/hb-draw.hh | 231 + src/3rdparty/harfbuzz-ng/src/hb-dsalgs.hh | 167 - .../harfbuzz-ng/src/hb-face-builder.cc | 246 + src/3rdparty/harfbuzz-ng/src/hb-face.cc | 412 +- src/3rdparty/harfbuzz-ng/src/hb-face.h | 85 +- .../src/{hb-face-private.hh => hb-face.hh} | 80 +- .../harfbuzz-ng/src/hb-fallback-shape.cc | 60 +- .../harfbuzz-ng/src/hb-font-private.hh | 555 -- src/3rdparty/harfbuzz-ng/src/hb-font.cc | 2548 ++++++-- src/3rdparty/harfbuzz-ng/src/hb-font.h | 785 ++- src/3rdparty/harfbuzz-ng/src/hb-font.hh | 720 +++ src/3rdparty/harfbuzz-ng/src/hb-ft-colr.hh | 567 ++ src/3rdparty/harfbuzz-ng/src/hb-ft.cc | 1500 +++++ src/3rdparty/harfbuzz-ng/src/hb-ft.h | 145 + src/3rdparty/harfbuzz-ng/src/hb-gdi.cc | 85 + src/3rdparty/harfbuzz-ng/src/hb-gdi.h | 39 + src/3rdparty/harfbuzz-ng/src/hb-glib.cc | 232 + .../src/{hb-ot-tag.h => hb-glib.h} | 37 +- ...an-private.hh => hb-gobject-enums.cc.tmpl} | 79 +- .../harfbuzz-ng/src/hb-gobject-enums.h.tmpl | 56 + .../harfbuzz-ng/src/hb-gobject-structs.cc | 116 + .../harfbuzz-ng/src/hb-gobject-structs.h | 136 + .../src/{hb-warning.cc => hb-gobject.h} | 23 +- src/3rdparty/harfbuzz-ng/src/hb-graphite2.cc | 449 ++ src/3rdparty/harfbuzz-ng/src/hb-graphite2.h | 61 + src/3rdparty/harfbuzz-ng/src/hb-icu.cc | 290 + src/3rdparty/harfbuzz-ng/src/hb-icu.h | 52 + src/3rdparty/harfbuzz-ng/src/hb-iter.hh | 1026 +++ src/3rdparty/harfbuzz-ng/src/hb-kern.hh | 145 + src/3rdparty/harfbuzz-ng/src/hb-limits.hh | 109 + src/3rdparty/harfbuzz-ng/src/hb-machinery.hh | 325 + src/3rdparty/harfbuzz-ng/src/hb-map.cc | 419 ++ src/3rdparty/harfbuzz-ng/src/hb-map.h | 143 + src/3rdparty/harfbuzz-ng/src/hb-map.hh | 490 ++ src/3rdparty/harfbuzz-ng/src/hb-meta.hh | 238 + .../harfbuzz-ng/src/hb-ms-feature-ranges.hh | 232 + src/3rdparty/harfbuzz-ng/src/hb-multimap.hh | 92 + .../src/{hb-mutex-private.hh => hb-mutex.hh} | 107 +- src/3rdparty/harfbuzz-ng/src/hb-null.hh | 226 + .../harfbuzz-ng/src/hb-number-parser.hh | 237 + src/3rdparty/harfbuzz-ng/src/hb-number.cc | 79 + src/3rdparty/harfbuzz-ng/src/hb-number.hh | 41 + .../harfbuzz-ng/src/hb-object-private.hh | 196 - src/3rdparty/harfbuzz-ng/src/hb-object.hh | 357 ++ .../harfbuzz-ng/src/hb-open-file-private.hh | 280 - src/3rdparty/harfbuzz-ng/src/hb-open-file.hh | 537 ++ .../harfbuzz-ng/src/hb-open-type-private.hh | 1184 ---- src/3rdparty/harfbuzz-ng/src/hb-open-type.hh | 1147 ++++ .../harfbuzz-ng/src/hb-ot-cbdt-table.hh | 471 -- .../harfbuzz-ng/src/hb-ot-cff-common.hh | 516 ++ .../harfbuzz-ng/src/hb-ot-cff1-std-str.hh | 425 ++ .../harfbuzz-ng/src/hb-ot-cff1-table.cc | 620 ++ .../harfbuzz-ng/src/hb-ot-cff1-table.hh | 1484 +++++ .../harfbuzz-ng/src/hb-ot-cff2-table.cc | 222 + .../harfbuzz-ng/src/hb-ot-cff2-table.hh | 540 ++ .../harfbuzz-ng/src/hb-ot-cmap-table.hh | 2039 +++++- src/3rdparty/harfbuzz-ng/src/hb-ot-color.cc | 363 ++ src/3rdparty/harfbuzz-ng/src/hb-ot-color.h | 155 + .../harfbuzz-ng/src/hb-ot-deprecated.h | 147 + .../harfbuzz-ng/src/hb-ot-face-table-list.hh | 152 + src/3rdparty/harfbuzz-ng/src/hb-ot-face.cc | 58 + src/3rdparty/harfbuzz-ng/src/hb-ot-face.hh | 77 + src/3rdparty/harfbuzz-ng/src/hb-ot-font.cc | 640 +- src/3rdparty/harfbuzz-ng/src/hb-ot-font.h | 2 +- .../harfbuzz-ng/src/hb-ot-gasp-table.hh | 84 + .../harfbuzz-ng/src/hb-ot-glyf-table.hh | 155 +- .../harfbuzz-ng/src/hb-ot-hdmx-table.hh | 170 + .../harfbuzz-ng/src/hb-ot-head-table.hh | 101 +- .../harfbuzz-ng/src/hb-ot-hhea-table.hh | 94 +- .../harfbuzz-ng/src/hb-ot-hmtx-table.hh | 510 +- .../harfbuzz-ng/src/hb-ot-kern-table.hh | 555 +- .../src/hb-ot-layout-base-table.hh | 525 ++ .../src/hb-ot-layout-common-private.hh | 1772 ------ .../harfbuzz-ng/src/hb-ot-layout-common.hh | 3715 +++++++++++ .../src/hb-ot-layout-gdef-table.hh | 427 +- .../src/hb-ot-layout-gpos-table.hh | 1619 +---- .../src/hb-ot-layout-gsub-table.hh | 1345 +--- .../src/hb-ot-layout-gsubgpos-private.hh | 2373 ------- .../harfbuzz-ng/src/hb-ot-layout-gsubgpos.hh | 4550 +++++++++++++ .../src/hb-ot-layout-jstf-table.hh | 85 +- .../src/hb-ot-layout-math-table.hh | 722 --- src/3rdparty/harfbuzz-ng/src/hb-ot-layout.cc | 2363 +++++-- src/3rdparty/harfbuzz-ng/src/hb-ot-layout.h | 341 +- ...b-ot-layout-private.hh => hb-ot-layout.hh} | 266 +- src/3rdparty/harfbuzz-ng/src/hb-ot-map.cc | 263 +- .../{hb-ot-map-private.hh => hb-ot-map.hh} | 187 +- .../harfbuzz-ng/src/hb-ot-math-table.hh | 839 ++- src/3rdparty/harfbuzz-ng/src/hb-ot-math.cc | 265 +- src/3rdparty/harfbuzz-ng/src/hb-ot-math.h | 132 +- .../harfbuzz-ng/src/hb-ot-maxp-table.hh | 121 +- .../harfbuzz-ng/src/hb-ot-meta-table.hh | 129 + src/3rdparty/harfbuzz-ng/src/hb-ot-meta.cc | 79 + src/3rdparty/harfbuzz-ng/src/hb-ot-meta.h | 72 + src/3rdparty/harfbuzz-ng/src/hb-ot-metrics.cc | 436 ++ src/3rdparty/harfbuzz-ng/src/hb-ot-metrics.h | 129 + src/3rdparty/harfbuzz-ng/src/hb-ot-metrics.hh | 35 + .../src/hb-ot-name-language-static.hh | 456 ++ .../harfbuzz-ng/src/hb-ot-name-language.hh | 40 + .../harfbuzz-ng/src/hb-ot-name-table.hh | 108 +- src/3rdparty/harfbuzz-ng/src/hb-ot-name.cc | 184 + src/3rdparty/harfbuzz-ng/src/hb-ot-name.h | 164 + .../harfbuzz-ng/src/hb-ot-os2-table.hh | 390 +- .../src/hb-ot-os2-unicode-ranges.hh | 231 + .../harfbuzz-ng/src/hb-ot-post-macroman.hh | 2 +- .../src/hb-ot-post-table-v2subset.hh | 136 + .../harfbuzz-ng/src/hb-ot-post-table.hh | 247 +- .../src/hb-ot-shape-complex-indic-machine.hh | 1566 ----- .../src/hb-ot-shape-complex-indic-private.hh | 190 - .../src/hb-ot-shape-complex-indic-table.cc | 486 -- .../hb-ot-shape-complex-myanmar-machine.hh | 404 -- .../src/hb-ot-shape-complex-myanmar.cc | 547 -- .../src/hb-ot-shape-complex-tibetan.cc | 63 - .../src/hb-ot-shape-complex-use-machine.hh | 459 -- .../src/hb-ot-shape-complex-use-private.hh | 97 - .../src/hb-ot-shape-complex-use-table.cc | 777 --- .../src/hb-ot-shape-complex-use.cc | 612 -- .../harfbuzz-ng/src/hb-ot-shape-fallback.cc | 267 +- ...ack-private.hh => hb-ot-shape-fallback.hh} | 23 +- .../harfbuzz-ng/src/hb-ot-shape-normalize.cc | 298 +- ...ze-private.hh => hb-ot-shape-normalize.hh} | 16 +- .../harfbuzz-ng/src/hb-ot-shape-private.hh | 108 - src/3rdparty/harfbuzz-ng/src/hb-ot-shape.cc | 1055 +-- src/3rdparty/harfbuzz-ng/src/hb-ot-shape.h | 2 +- src/3rdparty/harfbuzz-ng/src/hb-ot-shape.hh | 171 + ...ack.hh => hb-ot-shaper-arabic-fallback.hh} | 199 +- .../src/hb-ot-shaper-arabic-joining-list.hh | 47 + .../src/hb-ot-shaper-arabic-pua.hh | 118 + ...-table.hh => hb-ot-shaper-arabic-table.hh} | 238 +- ...1256.hh => hb-ot-shaper-arabic-win1256.hh} | 46 +- ...mplex-arabic.cc => hb-ot-shaper-arabic.cc} | 243 +- ...abic-private.hh => hb-ot-shaper-arabic.hh} | 10 +- ...lex-default.cc => hb-ot-shaper-default.cc} | 37 +- ...mplex-hangul.cc => hb-ot-shaper-hangul.cc} | 103 +- ...mplex-hebrew.cc => hb-ot-shaper-hebrew.cc} | 59 +- .../src/hb-ot-shaper-indic-machine.hh | 627 ++ .../src/hb-ot-shaper-indic-table.cc | 561 ++ ...complex-indic.cc => hb-ot-shaper-indic.cc} | 1223 ++-- .../harfbuzz-ng/src/hb-ot-shaper-indic.hh | 66 + .../src/hb-ot-shaper-khmer-machine.hh | 428 ++ .../harfbuzz-ng/src/hb-ot-shaper-khmer.cc | 387 ++ .../src/hb-ot-shaper-myanmar-machine.hh | 553 ++ .../harfbuzz-ng/src/hb-ot-shaper-myanmar.cc | 390 ++ .../harfbuzz-ng/src/hb-ot-shaper-syllabic.cc | 100 + .../harfbuzz-ng/src/hb-ot-shaper-syllabic.hh | 47 + ...e-complex-thai.cc => hb-ot-shaper-thai.cc} | 54 +- .../src/hb-ot-shaper-use-machine.hh | 1080 ++++ .../harfbuzz-ng/src/hb-ot-shaper-use-table.hh | 674 ++ .../harfbuzz-ng/src/hb-ot-shaper-use.cc | 511 ++ .../src/hb-ot-shaper-vowel-constraints.cc | 477 ++ .../src/hb-ot-shaper-vowel-constraints.hh | 39 + ...ape-complex-private.hh => hb-ot-shaper.hh} | 245 +- .../harfbuzz-ng/src/hb-ot-stat-table.hh | 620 ++ .../harfbuzz-ng/src/hb-ot-tag-table.hh | 2993 +++++++++ src/3rdparty/harfbuzz-ng/src/hb-ot-tag.cc | 1358 ++-- .../harfbuzz-ng/src/hb-ot-var-avar-table.hh | 177 +- .../harfbuzz-ng/src/hb-ot-var-common.hh | 594 ++ .../harfbuzz-ng/src/hb-ot-var-cvar-table.hh | 158 + .../harfbuzz-ng/src/hb-ot-var-fvar-table.hh | 426 +- .../harfbuzz-ng/src/hb-ot-var-gvar-table.hh | 516 ++ .../harfbuzz-ng/src/hb-ot-var-hvar-table.hh | 375 +- .../harfbuzz-ng/src/hb-ot-var-mvar-table.hh | 54 +- src/3rdparty/harfbuzz-ng/src/hb-ot-var.cc | 245 +- src/3rdparty/harfbuzz-ng/src/hb-ot-var.h | 142 +- .../harfbuzz-ng/src/hb-ot-vorg-table.hh | 136 + src/3rdparty/harfbuzz-ng/src/hb-ot.h | 6 +- src/3rdparty/harfbuzz-ng/src/hb-outline.cc | 321 + src/3rdparty/harfbuzz-ng/src/hb-outline.hh | 83 + .../harfbuzz-ng/src/hb-paint-extents.cc | 330 + .../harfbuzz-ng/src/hb-paint-extents.hh | 293 + src/3rdparty/harfbuzz-ng/src/hb-paint.cc | 703 ++ src/3rdparty/harfbuzz-ng/src/hb-paint.h | 987 +++ src/3rdparty/harfbuzz-ng/src/hb-paint.hh | 228 + src/3rdparty/harfbuzz-ng/src/hb-pool.hh | 107 + .../harfbuzz-ng/src/hb-priority-queue.hh | 159 + src/3rdparty/harfbuzz-ng/src/hb-private.hh | 899 --- src/3rdparty/harfbuzz-ng/src/hb-repacker.hh | 410 ++ src/3rdparty/harfbuzz-ng/src/hb-sanitize.hh | 442 ++ src/3rdparty/harfbuzz-ng/src/hb-serialize.hh | 768 +++ .../harfbuzz-ng/src/hb-set-digest-private.hh | 179 - src/3rdparty/harfbuzz-ng/src/hb-set-digest.hh | 207 + .../harfbuzz-ng/src/hb-set-private.hh | 577 -- src/3rdparty/harfbuzz-ng/src/hb-set.cc | 425 +- src/3rdparty/harfbuzz-ng/src/hb-set.h | 67 +- src/3rdparty/harfbuzz-ng/src/hb-set.hh | 184 + src/3rdparty/harfbuzz-ng/src/hb-shape-plan.cc | 669 +- src/3rdparty/harfbuzz-ng/src/hb-shape-plan.h | 20 +- src/3rdparty/harfbuzz-ng/src/hb-shape-plan.hh | 77 + src/3rdparty/harfbuzz-ng/src/hb-shape.cc | 392 +- src/3rdparty/harfbuzz-ng/src/hb-shape.h | 14 +- ...aper-impl-private.hh => hb-shaper-impl.hh} | 23 +- .../harfbuzz-ng/src/hb-shaper-list.hh | 16 +- .../harfbuzz-ng/src/hb-shaper-private.hh | 124 - src/3rdparty/harfbuzz-ng/src/hb-shaper.cc | 81 +- src/3rdparty/harfbuzz-ng/src/hb-shaper.hh | 134 + src/3rdparty/harfbuzz-ng/src/hb-static.cc | 135 + .../harfbuzz-ng/src/hb-string-array.hh | 16 +- src/3rdparty/harfbuzz-ng/src/hb-style.cc | 134 + src/3rdparty/harfbuzz-ng/src/hb-style.h | 81 + .../harfbuzz-ng/src/hb-subset-accelerator.hh | 132 + .../harfbuzz-ng/src/hb-subset-cff-common.cc | 220 + .../harfbuzz-ng/src/hb-subset-cff-common.hh | 1165 ++++ .../harfbuzz-ng/src/hb-subset-cff1.cc | 956 +++ .../harfbuzz-ng/src/hb-subset-cff1.hh | 37 + .../harfbuzz-ng/src/hb-subset-cff2.cc | 661 ++ .../harfbuzz-ng/src/hb-subset-cff2.hh | 37 + .../harfbuzz-ng/src/hb-subset-input.cc | 591 ++ .../harfbuzz-ng/src/hb-subset-input.hh | 153 + .../src/hb-subset-instancer-solver.cc | 464 ++ .../harfbuzz-ng/src/hb-subset-plan.cc | 1226 ++++ .../harfbuzz-ng/src/hb-subset-plan.hh | 327 + .../harfbuzz-ng/src/hb-subset-repacker.cc | 58 + .../harfbuzz-ng/src/hb-subset-repacker.h | 81 + src/3rdparty/harfbuzz-ng/src/hb-subset.cc | 639 ++ src/3rdparty/harfbuzz-ng/src/hb-subset.h | 230 + src/3rdparty/harfbuzz-ng/src/hb-subset.hh | 74 + src/3rdparty/harfbuzz-ng/src/hb-ucd-table.hh | 5633 +++++++++++++++++ src/3rdparty/harfbuzz-ng/src/hb-ucd.cc | 258 + .../harfbuzz-ng/src/hb-unicode-emoji-table.hh | 79 + src/3rdparty/harfbuzz-ng/src/hb-unicode.cc | 278 +- src/3rdparty/harfbuzz-ng/src/hb-unicode.h | 414 +- .../{hb-unicode-private.hh => hb-unicode.hh} | 160 +- src/3rdparty/harfbuzz-ng/src/hb-uniscribe.cc | 889 +++ src/3rdparty/harfbuzz-ng/src/hb-uniscribe.h | 46 + .../harfbuzz-ng/src/hb-utf-private.hh | 282 - src/3rdparty/harfbuzz-ng/src/hb-utf.hh | 481 ++ src/3rdparty/harfbuzz-ng/src/hb-vector.hh | 510 ++ src/3rdparty/harfbuzz-ng/src/hb-version.h | 39 +- src/3rdparty/harfbuzz-ng/src/hb.h | 8 +- src/3rdparty/harfbuzz-ng/src/hb.hh | 534 ++ src/gui/configure.json | 28 + .../text/qfontmetrics/tst_qfontmetrics.cpp | 4 + .../auto/gui/text/qglyphrun/tst_qglyphrun.cpp | 3 + .../tst_qtextscriptengine.cpp | 7 +- 394 files changed, 119839 insertions(+), 29392 deletions(-) create mode 100644 src/3rdparty/harfbuzz-ng/hb-dummy.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Color/CBDT/CBDT.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/COLR.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/colrv1-closure.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Color/CPAL/CPAL.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Color/sbix/sbix.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Color/svg/svg.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/Coverage.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat2.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/RangeRecord.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GDEF/GDEF.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Anchor.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat2.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat3.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorMatrix.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ChainContextPos.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Common.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ContextPos.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePos.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePosFormat1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ExtensionPos.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/GPOS.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/LigatureArray.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkArray.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePos.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePosFormat1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPos.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPosFormat1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPos.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkRecord.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPos.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat2.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairSet.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairValueRecord.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookup.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookupSubTable.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePos.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat2.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ValueFormat.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSet.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubst.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubstFormat1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ChainContextSubst.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Common.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ContextSubst.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ExtensionSubst.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/GSUB.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Ligature.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSet.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubst.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubstFormat1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubst.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubstFormat1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Sequence.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubst.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat2.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookup.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookupSubTable.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/Layout/types.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/glyf/CompositeGlyph.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/glyf/Glyph.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/glyf/GlyphHeader.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/glyf/SimpleGlyph.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/glyf/SubsetGlyph.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/glyf/VarCompositeGlyph.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/glyf/composite-iter.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/glyf/coord-setter.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf-helpers.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/glyf/loca.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/glyf/path-builder.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/OT/name/name.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/graph/classdef-graph.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/graph/coverage-graph.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/graph/graph.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-graph.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/graph/markbasepos-graph.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/graph/pairpos-graph.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/graph/serialize.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/graph/split-helpers.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/graph/test-classdef-graph.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/harfbuzz-subset.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/harfbuzz.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-layout-ankr-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-layout-bsln-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-layout-common.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-layout-feat-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-layout-just-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-layout-kerx-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-layout-morx-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-layout-opbd-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-layout-trak-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-layout.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-layout.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-layout.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-ltag-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-map.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat-map.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-aat.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-algs.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-array.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-atomic-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-atomic.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-bimap.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-bit-page.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-bit-set-invertible.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-bit-set.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-blob.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-glyphs.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-unicode.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-buffer-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-buffer-verify.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-buffer.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-cache-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-cache.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-cairo-utils.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-cairo-utils.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-cairo.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-cairo.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-cff-interp-common.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-cff-interp-cs-common.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-cff-interp-dict-common.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-cff1-interp-cs.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-cff2-interp-cs.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-config.hh rename src/3rdparty/harfbuzz-ng/src/{hb-coretext.mm => hb-coretext.cc} (58%) create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-cplusplus.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-directwrite.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-directwrite.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-dispatch.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-draw.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-draw.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-draw.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-dsalgs.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-face-builder.cc rename src/3rdparty/harfbuzz-ng/src/{hb-face-private.hh => hb-face.hh} (58%) delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-font-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-font.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ft-colr.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ft.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ft.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-gdi.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-gdi.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-glib.cc rename src/3rdparty/harfbuzz-ng/src/{hb-ot-tag.h => hb-glib.h} (69%) rename src/3rdparty/harfbuzz-ng/src/{hb-shape-plan-private.hh => hb-gobject-enums.cc.tmpl} (50%) create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-gobject-enums.h.tmpl create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.h rename src/3rdparty/harfbuzz-ng/src/{hb-warning.cc => hb-gobject.h} (68%) create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-graphite2.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-graphite2.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-icu.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-icu.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-iter.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-kern.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-limits.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-machinery.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-map.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-map.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-map.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-meta.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ms-feature-ranges.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-multimap.hh rename src/3rdparty/harfbuzz-ng/src/{hb-mutex-private.hh => hb-mutex.hh} (54%) create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-null.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-number-parser.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-number.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-number.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-object-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-object.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-open-file-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-open-file.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-open-type-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-open-type.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-cbdt-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-cff-common.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-std-str.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-color.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-color.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-deprecated.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-face-table-list.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-face.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-face.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-gasp-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-hdmx-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-layout-base-table.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-layout-common-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-layout-common.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-layout-gsubgpos-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-layout-gsubgpos.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-layout-math-table.hh rename src/3rdparty/harfbuzz-ng/src/{hb-ot-layout-private.hh => hb-ot-layout.hh} (77%) rename src/3rdparty/harfbuzz-ng/src/{hb-ot-map-private.hh => hb-ot-map.hh} (54%) create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-meta-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-meta.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-meta.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-metrics.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-metrics.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-metrics.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-name-language-static.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-name-language.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-name.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-name.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-os2-unicode-ranges.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-post-table-v2subset.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shape-complex-indic-machine.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shape-complex-indic-private.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shape-complex-indic-table.cc delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shape-complex-myanmar-machine.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shape-complex-myanmar.cc delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shape-complex-tibetan.cc delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shape-complex-use-machine.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shape-complex-use-private.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shape-complex-use-table.cc delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shape-complex-use.cc rename src/3rdparty/harfbuzz-ng/src/{hb-ot-shape-fallback-private.hh => hb-ot-shape-fallback.hh} (73%) rename src/3rdparty/harfbuzz-ng/src/{hb-ot-shape-normalize-private.hh => hb-ot-shape-normalize.hh} (84%) delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shape-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shape.hh rename src/3rdparty/harfbuzz-ng/src/{hb-ot-shape-complex-arabic-fallback.hh => hb-ot-shaper-arabic-fallback.hh} (60%) create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-arabic-joining-list.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-arabic-pua.hh rename src/3rdparty/harfbuzz-ng/src/{hb-ot-shape-complex-arabic-table.hh => hb-ot-shaper-arabic-table.hh} (66%) rename src/3rdparty/harfbuzz-ng/src/{hb-ot-shape-complex-arabic-win1256.hh => hb-ot-shaper-arabic-win1256.hh} (91%) rename src/3rdparty/harfbuzz-ng/src/{hb-ot-shape-complex-arabic.cc => hb-ot-shaper-arabic.cc} (75%) rename src/3rdparty/harfbuzz-ng/src/{hb-ot-shape-complex-arabic-private.hh => hb-ot-shaper-arabic.hh} (87%) rename src/3rdparty/harfbuzz-ng/src/{hb-ot-shape-complex-default.cc => hb-ot-shaper-default.cc} (66%) rename src/3rdparty/harfbuzz-ng/src/{hb-ot-shape-complex-hangul.cc => hb-ot-shaper-hangul.cc} (86%) rename src/3rdparty/harfbuzz-ng/src/{hb-ot-shape-complex-hebrew.cc => hb-ot-shaper-hebrew.cc} (78%) create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-indic-machine.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-indic-table.cc rename src/3rdparty/harfbuzz-ng/src/{hb-ot-shape-complex-indic.cc => hb-ot-shaper-indic.cc} (55%) create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-indic.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-khmer-machine.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-khmer.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-myanmar-machine.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-myanmar.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-syllabic.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-syllabic.hh rename src/3rdparty/harfbuzz-ng/src/{hb-ot-shape-complex-thai.cc => hb-ot-shaper-thai.cc} (91%) create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-use-machine.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-use-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-use.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-vowel-constraints.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-shaper-vowel-constraints.hh rename src/3rdparty/harfbuzz-ng/src/{hb-ot-shape-complex-private.hh => hb-ot-shaper.hh} (67%) create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-stat-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-tag-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-var-common.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-var-cvar-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-var-gvar-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ot-vorg-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-outline.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-outline.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-paint-extents.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-paint-extents.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-paint.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-paint.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-paint.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-pool.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-priority-queue.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-repacker.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-sanitize.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-serialize.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-set-digest-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-set-digest.hh delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-set-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-set.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-shape-plan.hh rename src/3rdparty/harfbuzz-ng/src/{hb-shaper-impl-private.hh => hb-shaper-impl.hh} (75%) delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-shaper-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-shaper.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-static.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-style.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-style.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-accelerator.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-cff-common.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-cff-common.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-cff1.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-cff1.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-cff2.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-cff2.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-input.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-input.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-instancer-solver.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-plan.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-plan.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-repacker.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset-repacker.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset.h create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-subset.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ucd-table.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-ucd.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-unicode-emoji-table.hh rename src/3rdparty/harfbuzz-ng/src/{hb-unicode-private.hh => hb-unicode.hh} (77%) create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-uniscribe.cc create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-uniscribe.h delete mode 100644 src/3rdparty/harfbuzz-ng/src/hb-utf-private.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-utf.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb-vector.hh create mode 100644 src/3rdparty/harfbuzz-ng/src/hb.hh diff --git a/src/3rdparty/harfbuzz-ng/AUTHORS b/src/3rdparty/harfbuzz-ng/AUTHORS index 81cdc4cf37f..83c0c66f99e 100644 --- a/src/3rdparty/harfbuzz-ng/AUTHORS +++ b/src/3rdparty/harfbuzz-ng/AUTHORS @@ -1,9 +1,14 @@ Behdad Esfahbod -Simon Hausmann -Martin Hosken -Jonathan Kew -Lars Knoll -Werner Lemberg -Roozbeh Pournader -Owen Taylor +David Corbett David Turner +Ebrahim Byagowi +Garret Rieger +Jonathan Kew +Khaled Hosny +Lars Knoll +Martin Hosken +Owen Taylor +Roderick Sheeter +Roozbeh Pournader +Simon Hausmann +Werner Lemberg diff --git a/src/3rdparty/harfbuzz-ng/COPYING b/src/3rdparty/harfbuzz-ng/COPYING index 9d1056f40b1..1dd917e9f2e 100644 --- a/src/3rdparty/harfbuzz-ng/COPYING +++ b/src/3rdparty/harfbuzz-ng/COPYING @@ -2,17 +2,23 @@ HarfBuzz is licensed under the so-called "Old MIT" license. Details follow. For parts of HarfBuzz that are licensed under different licenses see individual files names COPYING in subdirectories where applicable. -Copyright ยฉ 2010,2011,2012 Google, Inc. -Copyright ยฉ 2012 Mozilla Foundation +Copyright ยฉ 2010-2022 Google, Inc. +Copyright ยฉ 2015-2020 Ebrahim Byagowi +Copyright ยฉ 2019,2020 Facebook, Inc. +Copyright ยฉ 2012,2015 Mozilla Foundation Copyright ยฉ 2011 Codethink Limited Copyright ยฉ 2008,2010 Nokia Corporation and/or its subsidiary(-ies) Copyright ยฉ 2009 Keith Stribley -Copyright ยฉ 2009 Martin Hosken and SIL International +Copyright ยฉ 2011 Martin Hosken and SIL International Copyright ยฉ 2007 Chris Wilson -Copyright ยฉ 2006 Behdad Esfahbod -Copyright ยฉ 2005 David Turner -Copyright ยฉ 2004,2007,2008,2009,2010 Red Hat, Inc. -Copyright ยฉ 1998-2004 David Turner and Werner Lemberg +Copyright ยฉ 2005,2006,2020,2021,2022,2023 Behdad Esfahbod +Copyright ยฉ 2004,2007,2008,2009,2010,2013,2021,2022,2023 Red Hat, Inc. +Copyright ยฉ 1998-2005 David Turner and Werner Lemberg +Copyright ยฉ 2016 Igalia S.L. +Copyright ยฉ 2022 Matthias Clasen +Copyright ยฉ 2018,2021 Khaled Hosny +Copyright ยฉ 2018,2019,2020 Adobe, Inc +Copyright ยฉ 2013-2015 Alexei Podtelezhnikov For full copyright notices consult the individual files in the package. diff --git a/src/3rdparty/harfbuzz-ng/NEWS b/src/3rdparty/harfbuzz-ng/NEWS index 7434bcada03..e53a244f1b3 100644 --- a/src/3rdparty/harfbuzz-ng/NEWS +++ b/src/3rdparty/harfbuzz-ng/NEWS @@ -1,3 +1,1451 @@ +Overview of changes leading to 7.2.0 +Thursday, April 27, 2023 +==================================== +- Add Tifinagh to the list of scripts that can natively be either right-to-left + or left-to-right, to improve handling of its glyph positioning. + (Simon Cozens) +- Return also single substitution from hb_ot_layout_lookup_get_glyph_alternates() + (Behdad Esfahbod) +- Fix 4.2.0 regression in applying across syllables in syllabic scripts. + (Behdad Esfahbod) +- Add flag to avoid glyph substitution closure during subsetting, and the + corresponding โ€œ--no-layout-closureโ€ option to โ€œhb-subsetโ€ command line tool. + (Garret Rieger) +- Support instancing COLRv1 table. (Qunxin Liu) +- Donโ€™t drop used user-defined name table entries during subsetting. + (Qunxin Liu) +- Optimize handling of โ€œgvarโ€ table. (Behdad Esfahbod) +- Various subsetter bug fixes and improvements. (Garret Rieger, Qunxin Liu) +- Various documentation improvements. (Behdad Esfahbod, Josef Friedrich) + +- New API: ++HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE ++HB_UNICODE_COMBINING_CLASS_CCC132 + +- Deprecated API: ++HB_UNICODE_COMBINING_CLASS_CCC133 + + +Overview of changes leading to 7.1.0 +Friday, March 3, 2023 +==================================== +- New experimental hb_shape_justify() API that uses font variations to expand + or shrink the text to a given advance. (Behdad Esfahbod) +- Various build and bug fixes. (Behdad Esfahbod, Garret Rieger, Qunxin Liu) + +- New API: ++hb_font_set_variation() + + +Overview of changes leading to 7.0.1 +Monday, February 20, 2023 +==================================== +- Various build and bug fixes. + + +Overview of changes leading to 7.0.0 +Saturday, February 11, 2023 +==================================== +- New hb-paint API that is designed mainly to paint โ€œCOLRv1โ€ glyphs, but can be + also used as a unified API to paint any of the glyph representations + supported by HarfBuzz (B/W outlines, color layers, or color bitmaps). + (Behdad Esfahbod, Matthias Clasen) +- New hb-cairo API for integrating with cairo graphics library. This is provided + as a separate harfbuzz-cairo library. (Behdad Esfahbod, Matthias Clasen) +- Support for instancing โ€œCFF2โ€ table. (Behdad Esfahbod) +- Support font emboldening. (Behdad Esfahbod) +- Support feature ranges with AAT shaping. (Behdad Esfahbod) +- Experimental support to cubic curves in โ€œglyfโ€ table, see + https://github.com/harfbuzz/boring-expansion-spec/blob/main/glyf1-cubicOutlines.md + for spec. (Behdad Esfahbod) +- Various subsetter improvements. (Garret Rieger, Qunxin Liu, Behdad Esfahbod) +- Various documentation improvements. + (Behdad Esfahbod, Matthias Clasen, Khaled Hosny) +- Significantly reduced memory use during shaping. (Behdad Esfahbod) +- Greatly reduced memory use during subsetting โ€œCFFโ€ table. (Behdad Esfahbod) +- New command line utility, hb-info, for querying various font information. + (Behdad Esfahbod, Matthias Clasen) +- New hb-shape/hb-view options: --glyphs, --color-palette, --font-bold, + --font-grade, and --named-instance. (Behdad Esfahbod) +- Miscellaneous fixes and improvements. + (Amir Masoud Abdol, Andres Salomon, Behdad Esfahbod, Chun-wei Fan, + Garret Rieger, Jens Kutilek, Khaled Hosny, Konstantin Kaฬˆfer, Matthias Clasen, + Nirbheek Chauhan, Pedro J. Estรฉbanez, Qunxin Liu, Sergei Trofimovich) + +- New API: ++HB_FONT_NO_VAR_NAMED_INSTANCE ++HB_PAINT_IMAGE_FORMAT_BGRA ++HB_PAINT_IMAGE_FORMAT_PNG ++HB_PAINT_IMAGE_FORMAT_SVG ++hb_cairo_font_face_create_for_face ++hb_cairo_font_face_create_for_font ++hb_cairo_font_face_get_face ++hb_cairo_font_face_get_font ++hb_cairo_font_face_get_scale_factor ++hb_cairo_font_face_set_font_init_func ++hb_cairo_font_face_set_scale_factor ++hb_cairo_font_init_func_t ++hb_cairo_glyphs_from_buffer ++hb_cairo_scaled_font_get_font ++hb_color_line_get_color_stops ++hb_color_line_get_color_stops_func_t ++hb_color_line_get_extend ++hb_color_line_get_extend_func_t ++hb_color_line_t ++hb_color_stop_t ++hb_draw_funcs_get_empty ++hb_draw_funcs_get_user_data ++hb_draw_funcs_set_user_data ++hb_face_collect_nominal_glyph_mapping ++hb_font_draw_glyph ++hb_font_draw_glyph_func_t ++hb_font_funcs_set_draw_glyph_func ++hb_font_funcs_set_paint_glyph_func ++hb_font_get_synthetic_bold ++hb_font_get_var_named_instance ++hb_font_paint_glyph ++hb_font_paint_glyph_func_t ++hb_font_set_synthetic_bold ++hb_map_keys ++hb_map_next ++hb_map_update ++hb_map_values ++hb_ot_color_glyph_has_paint ++hb_ot_color_has_paint ++hb_ot_layout_script_select_language2 ++hb_ot_name_id_predefined_t ++hb_paint_color ++hb_paint_color_func_t ++hb_paint_composite_mode_t ++hb_paint_custom_palette_color ++hb_paint_custom_palette_color_func_t ++hb_paint_extend_t ++hb_paint_funcs_create ++hb_paint_funcs_destroy ++hb_paint_funcs_get_empty ++hb_paint_funcs_get_user_data ++hb_paint_funcs_is_immutable ++hb_paint_funcs_make_immutable ++hb_paint_funcs_reference ++hb_paint_funcs_set_color_func ++hb_paint_funcs_set_custom_palette_color_func ++hb_paint_funcs_set_image_func ++hb_paint_funcs_set_linear_gradient_func ++hb_paint_funcs_set_pop_clip_func ++hb_paint_funcs_set_pop_group_func ++hb_paint_funcs_set_pop_transform_func ++hb_paint_funcs_set_push_clip_glyph_func ++hb_paint_funcs_set_push_clip_rectangle_func ++hb_paint_funcs_set_push_group_func ++hb_paint_funcs_set_push_transform_func ++hb_paint_funcs_set_radial_gradient_func ++hb_paint_funcs_set_sweep_gradient_func ++hb_paint_funcs_set_user_data ++hb_paint_funcs_t ++hb_paint_image ++hb_paint_image_func_t ++hb_paint_linear_gradient ++hb_paint_linear_gradient_func_t ++hb_paint_pop_clip ++hb_paint_pop_clip_func_t ++hb_paint_pop_group ++hb_paint_pop_group_func_t ++hb_paint_pop_transform ++hb_paint_pop_transform_func_t ++hb_paint_push_clip_glyph ++hb_paint_push_clip_glyph_func_t ++hb_paint_push_clip_rectangle ++hb_paint_push_clip_rectangle_func_t ++hb_paint_push_group ++hb_paint_push_group_func_t ++hb_paint_push_transform ++hb_paint_push_transform_func_t ++hb_paint_radial_gradient ++hb_paint_radial_gradient_func_t ++hb_paint_sweep_gradient ++hb_paint_sweep_gradient_func_t ++hb_set_is_inverted ++hb_subset_input_keep_everything + +- Deprecated API: ++hb_font_funcs_set_glyph_shape_func ++hb_font_get_glyph_shape_func_t ++hb_font_get_glyph_shape + + +Overview of changes leading to 6.0.0 +Friday, December 16, 2022 +==================================== +- A new API have been added to pre-process the face and speed up future + subsetting operations on that face. Provides up to a 95% reduction in + subsetting times when the same face is subset more than once. + + For more details and benchmarks, see: + https://github.com/harfbuzz/harfbuzz/blob/main/docs/subset-preprocessing.md + + (Garret Rieger, Behdad Esfahbod) + +- Shaping have been speedup by skipping entire lookups when the buffer contents + don't intersect with the lookup. Shows up to a 10% speedup in shaping some + fonts. (Behdad Esfahbod) + +- A new experimental feature, โ€œVariable Compositesโ€ (enabled by passing + -Dexperimental_api=true to meson), is also featured in this release. + This technology enables drastic compression of fonts in the Chinese, + Japanese, Korean, and other writing systems, by reusing the OpenType Font + Variations technology for encoding โ€œsmart componentsโ€ into the font. + + The specification for these extensions to the font format can be found in: + https://github.com/harfbuzz/boring-expansion-spec/blob/glyf1/glyf1.md + + A test variable-font with ~7160 Hangul syllables derived from the + NotoSerifKR-VF font has been built, with existing OpenType technology, as + well as with the new Variable Composites (VarComposites) technology. The + VarComposites font is over 90% smaller than the OpenType version of the font! + Both fonts can be obtained from the โ€œsmartiesโ€ repository: + https://github.com/behdad/smarties/tree/3.0/fonts/hangul/serif + + When building HarfBuzz with experimental features enabled, you can test + the โ€œsmartiesโ€ font with a sample character like this: + + $ hb-view butchered-hangul-serif-smarties-variable.ttf -u AE01 --variations=wght=700 + + (Behdad Esfahbod) + +- The HarfBuzz subsetter can now drop axes by pinning them to specific values + (also referred to as instancing). There are a couple of restrictions + currently: + + - Only works with TrueType (โ€œglyfโ€) based fonts. โ€œCFF2โ€ fonts are not yet + supported. + - Only supports the case where all axes in a font are pinned. + + (Garret Rieger, Qunxin Liu) + +- Miscellaneous fixes and improvements. + + (Behdad Esfahbod, Christoph Reiter, David Corbett, Eli Schwartz, Garret + Rieger, Joel Auterson, Jordan Petridis, Khaled Hosny, Lorenz Wildberg, + Marco Rebhan, Martin Storsjรถ, Matthias Clasen, Qunxin Liu, Satadru Pramanik) + + +- New API ++hb_subset_input_pin_axis_location() ++hb_subset_input_pin_axis_to_default() ++hb_subset_preprocess() + + +Overview of changes leading to 5.3.1 +Wednesday, October 19, 2022 +==================================== +- Subsetter repacker fixes. (Garret Rieger) +- Adjust Grapheme clusters for Katakana voiced sound marks. (Behdad Esfahbod) +- New โ€œhb-subsetโ€ option โ€œ--preprocess-faceโ€. (Garret Rieger) + + +Overview of changes leading to 5.3.0 +Saturday, October 8, 2022 +"Women, Life, Freedom" #MahsaAmini +==================================== +- Donโ€™t add glyphs from dropped MATH or COLR tables to the subset glyphs. + (Khaled Hosny) +- Map โ€œrligโ€ to appropriate AAT feature selectors. (Jonathan Kew) +- Update USE data files to latest version. (David Corbett) +- Check โ€œCBDTโ€ extents first before outline tables, to help with fonts that + also include an empty โ€œglyfโ€ table. (Khaled Hosny) +- More work towards variable font instancing in the subsetter. (Qunxin Liu) +- Subsetter repacker improvements. (Garret Rieger) +- New API: ++hb_ot_layout_lookup_get_optical_bound() ++hb_face_builder_sort_tables() + + +Overview of changes leading to 5.2.0 +Saturday, September 17, 2022 +==================================== +- Fix regressions in hb-ft font functions for FT_Faceโ€™s with transformation + matrix. (Behdad Esfahbod) +- The experimental hb-repacker API now supports splitting several GPOS subtable + types when needed. (Garret Rieger) +- The HarfBuzz extensions to OpenType font format are now opt-in behind + build-time flags. (Behdad Esfahbod) +- The experimental hb-subset variable fonts instantiation API can now + instantiate more font tables and arbitrary axis locations. (Qunxin Liu) +- Unicode 15 support. (David Corbett) +- Various documentation improvements. (Behdad Esfahbod, Matthias Clasen) +- The hb-view command line tool now detects WezTerm inline images support. + (Wez Furlong) +- Fix FreeType and ICU dependency lookup with meson. (Xavier Claessens) + +- New API: ++HB_SCRIPT_KAWI ++HB_SCRIPT_NAG_MUNDARI + + +Overview of changes leading to 5.1.0 +Sunday, July 31, 2022 +==================================== +- More extensive buffer tracing messages. (Behdad Esfahbod) +- Fix hb-ft regression in bitmap fonts rendering. (Behdad Esfahbod) +- Support extension promotion of lookups in hb-subset-repacker. (Garret Rieger) +- A new HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL for scripts that use elongation + (e.g. Arabic) to signify where it is safe to insert tatweel glyph without + interrupting shaping. (Behdad Esfahbod) +- Add โ€œ--safe-to-insert-tatweelโ€ to โ€œhb-shapeโ€ tool. (Behdad Esfahbod) + +- New API ++HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL ++HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL + + +Overview of changes leading to 5.0.1 +Saturday, July 23, 2022 +==================================== +- Fix version 2 โ€œavarโ€ table with hb-ft. (Behdad Esfahbod) + + +Overview of changes leading to 5.0.0 +Saturday, July 23, 2022 +==================================== +- Support fonts with more than 65535 glyphs in โ€œGDEFโ€, โ€œGSUBโ€, and โ€œGPOSโ€ + tables. This is part of https://github.com/be-fonts/boring-expansion-spec to + extend OpenType in a backward-compatible way. + (Behdad Esfahbod, Garret Rieger) +- Complete support for more than 65535 glyphs in โ€œglyfโ€ table that started in + 4.0.0 release. Part of boring-expansion-spec. (Behdad Esfahbod) +- Support version 2 of โ€œavarโ€ table. Part of boring-expansion-spec. + (Behdad Esfahbod) +- Fix mark attachment on multiple substitutions in some cases. + (Behdad Esfahbod) +- Fix application of โ€œcaltโ€, โ€œrcltโ€, and โ€œccmpโ€ features to better match + Uniscribe behaviour with some Arabic fonts. (Behdad Esfahbod) +- Improvement to interaction between multiple cursive attachments. + (Behdad Esfahbod) +- Improve multiple mark interactions in Hebrew. (Behdad Esfahbod) +- Implement language-specific forms in AAT shaping. (Behdad Esfahbod) +- Fix variation of โ€œVORGโ€ table. (Behdad Esfahbod) +- Support for specific script tags to be retained in the subsetter, and add + โ€œ--layout-scriptsโ€ option to โ€œhb-subsetโ€ tool. (Garret Rieger) +- Accept space as delimiter for --features/--variations in command line tools. +- Improve subsetting of โ€œCOLRโ€ table. (Qunxin Liu) +- Improved fuzzing coverage for ot-math API. (Frรฉdรฉric Wang) +- Fix โ€œkernโ€ table version 2 (AAT) sanitization on 32-bit systems. + (Behdad Esfahbod) +- Allow negative glyph advances from โ€œgraphite2โ€ shaper. (Stephan Bergmann) +- Implement loading (color) bitmap fonts with hb-ft. (Behdad Esfahbod) +- Fix regression in hb-ft when changing font size. (Behdad Esfahbod) +- Fix build on GCC < 7. (Kleis Auke Wolthuizen) +- Dynamically load dwrite.dll on windows if โ€œdirectwriteโ€ shaper is enabled. + (Luca Bacci) +- Provide a single-file harfbuzz-subset.cc file for easier alternate building + of hb-subset library, similar to harfbuzz.cc. (Khaled Hosny) + +- New API ++HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG ++hb_language_matches() + + +Overview of changes leading to 4.4.1 +Wednesday, June 29, 2022 +==================================== +- Fix test failure with some compilers. +- Fix Telugu and Kannada kerning regression. + + +Overview of changes leading to 4.4.0 +Monday, June 27, 2022 +==================================== +- Caching of variable fonts shaping, in particular when using HarfBuzzโ€™s own + font loading functions (ot). Bringing performance of variable shaping in par + with non-variable fonts shaping. (Behdad Esfahbod) +- Caching of format 2 โ€œContextual Substitutionโ€ and โ€œChained Contexts + Substitutionโ€ lookups. Resulting in up to 20% speedup of lookup-heavy fonts + like Gulzar or Noto Nastaliq Urdu. (Behdad Esfahbod) +- Improved ANSI output from hb-view. (Behdad Esfahbod) +- Support for shaping legacy, pre-OpenType Windows 3.1-era, Arabic fonts that + relied on a fixed PUA encoding. (Khaled Hosny, Behdad Esfahbod) +- Sinhala script is now shaped by the USE shaper instead of โ€œindicโ€ one. + (Behdad Esfahbod, David Corbett) +- Thai shaper improvements. (David Corbett) +- hb-ot-name API supports approximate BCP-47 language matching, for example + asking for โ€œen_USโ€ in a font that has only โ€œenโ€ names will return them. + (Behdad Esfahbod) +- Optimized TrueType glyph shape loading. (Behdad Esfahbod) +- Fix subsetting of HarfBuzz faces created via hb_face_create_for_tables(). + (Garret Rieger) +- Add 32 bit var store support to the subsetter. (Garret Rieger) + +- New API ++HB_BUFFER_FLAG_DEFINED ++HB_BUFFER_SERIALIZE_FLAG_DEFINED ++hb_font_changed() ++hb_font_get_serial() ++hb_ft_hb_font_changed() ++hb_set_hash() ++hb_map_copy() ++hb_map_hash() + + +Overview of changes leading to 4.3.0 +Friday, May 20, 2022 +==================================== +- Major speed up in loading and subsetting fonts, especially in + handling CFF table. Subsetting some fonts is now 3 times faster. + (Behdad Esfahbod, Garret Rieger) +- Speed up blending CFF2 table. (Behdad Esfahbod) +- Speed up hb_ot_tags_from_language(). (Behdad Esfahbod, David Corbett) +- Fix USE classification of U+10A38 to fix multiple marks on single Kharoshthi + base. (David Corbett) +- Fix parsing of empty CFF Index. (Behdad Esfahbod) +- Fix subsetting CPAL table with partial palette overlaps. (Garret Rieger) + +- New API ++hb_map_is_equal() (Behdad Esfahbod) + + +Overview of changes leading to 4.2.1 +Sunday, April 24, 2022 +==================================== +- Make sure hb_blob_create_from_file_or_fail() always returns nullptr in case + of failure and not empty blob sometimes. (Khaled Hosny) +- Add --passthrough-tables option to hb-subset. (Cosimo Lupo) +- Reinstate a pause after basic features in Khmer shaper, fixing a regression + introduced in previous release. (Behdad Esfahbod) +- Better handling of Regional_Indicator when shaped with RTL-native scripts, + reverting earlier fix that caused regressions in AAT shaping. (Behdad Esfahbod) + + +Overview of changes leading to 4.2.0 +Wednesday, March 30, 2022 +==================================== +- Source code reorganization, splitting large hb-ot-layout files into smaller, + per-subtable ones under OT/Layout/*. Code for more tables will follow suit in + later releases. (Garret Rieger, Behdad Esfahbod) +- Revert Indic shaper change in previous release that broke some fonts and + instead make per-syllable restriction of โ€œGSUBโ€ application limited to + script-specific Indic features, while applying them and discretionary + features in one go. (Behdad Esfahbod) +- Fix decoding of private in gvar table. (Behdad Esfahbod) +- Fix handling of contextual lookups that delete too many glyphs. (Behdad Esfahbod) +- Make โ€œmorxโ€ deleted glyphs donโ€™t block โ€œGPOSโ€ application. (Behdad Esfahbod) +- Various build fixes. (Chun-wei Fan, Khaled Hosny) + +- New API ++hb_set_next_many() (Andrew John) + + +Overview of changes leading to 4.1.0 +Wednesday, March 23, 2022 +==================================== +- Various OSS-Fuzz fixes. (Behdad Esfahbod) +- Make fallback vertical-origin match FreeTypeโ€™s. (Behdad Esfahbod) +- Treat visible viramas like dependent vowels in USE shaper. (David Corbett) +- Apply presentation forms features and discretionary features in one go in + Indic shaper, which seems to match Uniscribe and CoreText behaviour. + (Behdad Esfahbod, David Corbett) +- Various bug fixes. + +- New API ++hb_set_add_sorted_array() (Andrew John) + + +Overview of changes leading to 4.0.1 +Friday, March 11, 2022 +==================================== +- Update OpenType to AAT mappings for โ€œhistโ€ and โ€œvrtrโ€ features. + (Florian Pircher) +- Update IANA Language Subtag Registry to 2022-03-02. (David Corbett) +- Update USE shaper to allow any non-numeric tail in a symbol cluster, and + remove obsolete data overrides. (David Corbett) +- Fix handling of baseline variations to return correctly scaled values. + (Matthias Clasen) +- A new experimental hb_subset_repack_or_fail() to repack an array of objects, + eliminating offset overflows. The API is not available unless HarfBuzz is + built with experimental APIs enabled. (Qunxin Liu) + +- New experimental API ++hb_link_t ++hb_object_t ++hb_subset_repack_or_fail() + + +Overview of changes leading to 4.0.0 +Tuesday, March 1, 2022 +==================================== +- New public API to create subset plan and gather information on things like + glyph mappings in the final subset. The plan can then be passed on to perform + the subsetting operation. (Garret Rieger) +- Draw API for extracting glyph shapes have been extended and finalized and is + no longer an experimental API. The draw API supports glyf, CFF and CFF2 + glyph outlines tables, and applies variation settings set on the font as well + as synthetic slant. The new public API is not backward compatible with the + previous, non-public, experimental API. (Behdad Esfahbod) +- The hb-view tool will use HarfBuzz draw API to render the glyphs instead of + cairo-ft when compiled with Cairo 1.17.5 or newer, setting HB_DRAW + environment variable to 1 or 0 will force using or not use the draw API, + respectively. (Behdad Esfahbod) +- The hb-shape and hb-view tools now default to using HarfBuzzโ€™s own font + loading functions (ot) instead of FreeType ones (ft). They also have a new + option, --font-slant, to apply synthetic slant to the font. (Behdad Esfahbod) +- HarfBuzz now supports more than 65535 (the OpenType limit) glyph shapes and + metrics. See https://github.com/be-fonts/boring-expansion-spec/issues/6 and + https://github.com/be-fonts/boring-expansion-spec/issues/7 for details. + (Behdad Esfahbod) +- New API to get the dominant horizontal baseline tag for a given script. + (Behdad Esfahbod) +- New API to get the baseline positions from the font, and synthesize missing + ones. As well as new API to get font metrics and synthesize missing ones. + (Matthias Clasen) +- Improvements to finding dependencies on Windows when building with Visual + Studio. (Chun-wei Fan) +- New buffer flag, HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT, that must be set + during shaping for HB_GLYPH_FLAG_UNSAFE_TO_CONCAT flag to be reliably + produced. This is to limit the performance hit of producing this flag to when + it is actually needed. (Behdad Esfahbod) +- Documentation improvements. (Matthias Clasen) + +- New API + - General: + +HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT + +hb_var_num_t + + - Draw: + +hb_draw_funcs_t + +hb_draw_funcs_create() + +hb_draw_funcs_reference() + +hb_draw_funcs_destroy() + +hb_draw_funcs_is_immutable() + +hb_draw_funcs_make_immutable() + +hb_draw_move_to_func_t + +hb_draw_funcs_set_move_to_func() + +hb_draw_line_to_func_t + +hb_draw_funcs_set_line_to_func() + +hb_draw_quadratic_to_func_t + +hb_draw_funcs_set_quadratic_to_func() + +hb_draw_cubic_to_func_t + +hb_draw_funcs_set_cubic_to_func() + +hb_draw_close_path_func_t + +hb_draw_funcs_set_close_path_func() + +hb_draw_state_t + +HB_DRAW_STATE_DEFAULT + +hb_draw_move_to() + +hb_draw_line_to() + +hb_draw_quadratic_to() + +hb_draw_cubic_to() + +hb_draw_close_path() + +hb_font_get_glyph_shape_func_t + +hb_font_funcs_set_glyph_shape_func() + +hb_font_get_glyph_shape() + + - OpenType layout + +HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_CENTRAL + +HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_CENTRAL + +hb_ot_layout_get_horizontal_baseline_tag_for_script() + +hb_ot_layout_get_baseline_with_fallback() + + - Metrics: + +hb_ot_metrics_get_position_with_fallback() + + - Subset: + +hb_subset_plan_t + +hb_subset_plan_create_or_fail() + +hb_subset_plan_reference() + +hb_subset_plan_destroy() + +hb_subset_plan_set_user_data() + +hb_subset_plan_get_user_data() + +hb_subset_plan_execute_or_fail() + +hb_subset_plan_unicode_to_old_glyph_mapping() + +hb_subset_plan_new_to_old_glyph_mapping() + +hb_subset_plan_old_to_new_glyph_mapping() + + +Overview of changes leading to 3.4.0 +Sunday, February 13, 2022 +==================================== +- Perform sanity checks on shaping results is now part of โ€œharfbuzzโ€ library + and can be enabled by setting the buffer flag HB_BUFFER_FLAG_VERIFY. + (Behdad Esfahbod) +- Arabic Mark Transient Reordering Algorithm have been updated to revision 6. + (Khaled Hosny) +- ISO 15924 code for mathematical notation, โ€˜Zmthโ€™, now maps to the OpenType + โ€˜mathโ€™ tag. (Alexis King) +- It is now possible to get at once all math kerning values for a given glyph + at a given corner. (Alexis King) +- Fix locale_t portability issues on systems the typedefโ€™s it to a void + pointer. (Behdad Esfahbod) + +- New API: ++HB_BUFFER_FLAG_VERIFY ++HB_OT_TAG_MATH_SCRIPT ++HB_SCRIPT_MATH ++hb_ot_math_kern_entry_t ++hb_ot_math_get_glyph_kernings() + +- Deprecated API ++HB_OT_MATH_SCRIPT + + +Overview of changes leading to 3.3.2 +Sunday, February 6, 2022 +==================================== +- Revert splitting of pair positioning values introduced in 3.3.0 as it proved + problematic. (Behdad Esfahbod) + + +Overview of changes leading to 3.3.1 +Monday, January 31, 2022 +==================================== +- Fix heap-use-after-free in harfbuzz-subset introduced in previous release. + (Garret Rieger) + + +Overview of changes leading to 3.3.0 +Monday, January 31, 2022 +==================================== +- Improved documentation. (Matthias Clasen) +- Internal code cleanup, using C++ standard library more. (Behdad Esfahbod) +- The low 16-bits of face index will be used by hb_face_create() to select a + face inside a font collection file format, while the high 16-bits will be + used by hb_font_create() to load the named instance. (Behdad Esfahbod) +- Glyph positions and other font metrics now apply synthetic slant set by + hb_font_set_synthetic_slant(), for improved positioning for synthetically + slanted fonts. (Behdad Esfahbod) +- Fixed unintentional locale dependency in hb_variation_to_string() for decimal + point representation. (Matthias Clasen) +- When applying pair positioning (kerning) the positioning value is split + between the two sides of the pair for improved cursor positioning between + such pairs. (Behdad Esfahbod) +- Introduced new HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, to be used in conjunction + with HB_GLYPH_FLAG_UNSAFE_TO_BREAK for optimizing re-shaping during line + breaking. Check the documentation for further details. (Behdad Esfahbod) +- Improved handling of macrolanguages when mapping BCP 47 codes to OpenType + tags. (David Corbett) + +- New API: ++HB_GLYPH_FLAG_UNSAFE_TO_CONCAT ++hb_segment_properties_overlay() ++hb_buffer_create_similar() ++hb_font_set_synthetic_slant() ++hb_font_get_synthetic_slant() ++hb_font_get_var_coords_design() + + +Overview of changes leading to 3.2.0 +Friday, November 26, 2021 +==================================== +โ€œharfbuzzโ€ library improvements: +- Fixed shaping of Apple Color Emoji flags in right-to-left context. (Behdad Esfahbod) +- Fixed positioning of CFF fonts in HB_TINY profile. (Behdad Esfahbod) +- OpenType 1.9 language tags update. (David Corbett) +- Add HB_NO_VERTICAL config option. +- Add HB_CONFIG_OVERRIDE_H for easier configuration. (Behdad Esfahbod) + +โ€œharfbuzz-subsetโ€ library improvements: +- Improved packing of cmap, loca, and Ligature tables. (Garret Rieger) +- Significantly improved overflow-resolution strategy in the repacker. (Garret Rieger) + + +Overview of changes leading to 3.1.2 +Friday, November 26, 2021 +==================================== +- hb-shape / hb-view: revert treating text on the commandline as single + paragraph (was introduced in 3.0.0); add new --single-par to do that. + (Behdad Esfahbod) +- Subsetter bug fixes. (Garret Rieger, Qunxin Liu, Behdad Esfahbod) + + +Overview of changes leading to 3.1.1 +Wednesday, November 8, 2021 +==================================== +- Work around GCC cast-align error/warning on some platforms. (Behdad Esfahbod) +- Documentation improvements. (Matthias Clasen) + + +Overview of changes leading to 3.1.0 +Wednesday, November 3, 2021 +==================================== +- Better offset-overflow handling in the subsetter library. (Garret Rieger) +- Improved Unicode 14 properties in the USE shaper, and various other USE + shaper fixes. (David Corbett) +- MATH and COLR v1 tables subsetting support, and various other subsetter fixes. + (Qunxin Liu) +- Support for Pwo Karen / Ason Chin medial la. (Simon Cozens) +- Apply GPOS positioning when substituting with morx table, if kerx is missing. + (Behdad Esfahbod) +- Apply calt and clig features across syllable boundaries in Indic shaper. + (Behdad Esfahbod) +- meson option for enabling Graphite 2 has been renamed to graphite2. +- Build and documentation fixes. + +- New API: ++hb_buffer_set_not_found_glyph() ++hb_buffer_get_not_found_glyph() + + +Overview of changes leading to 3.0.0 +Friday, September 17, 2021 +==================================== +- Unicode 14.0 support (David Corbett). +- The hb-subset API and the harfbuzz-subset library's ABI are now declared + stable. The harfbuzz-subset library would not have been possible without the + work of Garret Rieger and Qunxin Liu from Google Fonts, and the earlier work + of Michiharu Ariza from Adobe. +- The hb-style API is now stable and no longer experimental. + +- New API: ++hb_style_tag_t ++hb_style_get_value() ++hb_subset_input_t ++hb_subset_flags_t ++hb_subset_sets_t ++hb_subset_input_create_or_fail() ++hb_subset_input_reference() ++hb_subset_input_destroy() ++hb_subset_input_set_user_data() ++hb_subset_input_get_user_data() ++hb_subset_input_unicode_set() ++hb_subset_input_glyph_set() ++hb_subset_input_set() ++hb_subset_input_get_flags() ++hb_subset_input_set_flags() ++hb_subset_or_fail() + +- Removed old unstable harfbuzz-subset API: +-hb_subset_input_nameid_set() +-hb_subset_input_namelangid_set() +-hb_subset_input_layout_features_set() +-hb_subset_input_no_subset_tables_set() +-hb_subset_input_drop_tables_set() +-hb_subset_input_set_drop_hints() +-hb_subset_input_get_drop_hints() +-hb_subset_input_set_desubroutinize() +-hb_subset_input_get_desubroutinize() +-hb_subset_input_set_retain_gids() +-hb_subset_input_get_retain_gids() +-hb_subset_input_set_name_legacy() +-hb_subset_input_get_name_legacy() +-hb_subset_input_set_overlaps_flag() +-hb_subset_input_get_overlaps_flag() +-hb_subset_input_set_notdef_outline() +-hb_subset_input_get_notdef_outline() +-hb_subset_input_set_no_prune_unicode_ranges() +-hb_subset_input_get_no_prune_unicode_ranges() +-hb_subset() + + +Overview of changes leading to 2.9.1 +Tuesday, September 7, 2021 +==================================== +- Final subset API is in place and if no issues are discovered, it will be the + stable subset API of HarfBuzz 3.0.0. Old API is kept to ease transition, but + will be removed in 3.0.0. +- Various fuzzer-found bug fixes. +- hb_buffer_append() now handles the pre- and post-context which previously + were left unchanged in the destination buffer. +- hb-view / hb-shape now accept following new arguments: + o --unicodes-before/after: takes a list of hex numbers that represent Unicode + codepoints. +- Undeprecated API: + hb_set_invert() + + +Overview of changes leading to 2.9.0 +Wednesday, August 18, 2021 +History Repeats Itself (Afghanistan) +==================================== +- Subsetter API is being stabilized, with the first stable API to happen in + 3.0.0 release (https://github.com/harfbuzz/harfbuzz/issues/3078). +- Support multiple variation axes with same tag, aka HOI. +- The โ€œcoretextโ€ testing shaper now passes font variations to CoreText. +- hb-shape/hb-view does not break line at new lines unless text is read from + file. +- hb-view and hb-subset has a --batch now, similar to hb-shape. +- The --batch mode now uses ; as argument separator instead of : used previously. +- The --batch in hb-shape does not expect 0th argument anymore. That is, the + lines read are interpreted as argv[1:], instead of argv[0:]. +- The --batch option has been undocumented. We are ready to document it; send + feedback if you find it useful. +- hb-subset got arguments revamps. Added much-requested --gids-file, --glyphs, + --glyphs-file, --unicodes-file, supporting ranges in --unicodes. +- Various bug fixes. + + +Overview of changes leading to 2.8.2 +Tuesday, July 8, 2021 +==================================== +- Shaping LTR digits for RTL scripts now makes the native direction of the + digits LTR, applying shaping and positioning rules on the same glyph order as + Uniscribe. (Jonathan Kew, Khaled Hosny). +- Subsetting COLR v1 and CPAL tables is now supported. (Garret Rieger, Qunxin Liu) +- Various fixes and improvements to the subsetter. (Garret Rieger, Qunxin Liu, Behdad) +- When applying morx table, mark glyph widths should not be zeroed. (Jonathan Kew) +- GPOS is preferred over kerx, if GSUB was applied. (Behdad) +- Regional_Indicator pairs are grouped together when clustering. (Behdad) +- New API: ++hb_blob_create_or_fail() ++hb_blob_create_from_file_or_fail() ++hb_set_copy() + + +Overview of changes leading to 2.8.1 +Tuesday, May 4, 2021 +==================================== +- Subsetter now fully supports GSUB/GPOS/GDEF tables (including variations); as + such, layout tables are retained by subsetter by default. (Garret Rieger, Qunxin Liu) +- Build scripts no longer check for FontConfig as HarfBuzz does not use it. +- hb-view supports iTerm2 and kitty inline image protocols (Khaled Hosny), + it can also use Chafa for terminal graphics if available (Hans Petter Jansson). + +Overview of changes leading to 2.8.0 +Tuesday, March 16, 2021 +==================================== +- Shape joining scripts other than Arabic/Syriac using the Universal Shaping Engine. + Previously these were shaped using the generalized Arabic shaper. (David Corbett) +- Fix regression in shaping of U+0B55 ORIYA SIGN OVERLINE. (David Corbett) +- Update language tags. (David Corbett) +- Variations: reduce error: do not round each interpolated delta. (Just van Rossum) +- Documentation improvements. (Khaled Hosny, Nathan Willis) +- Subsetter improvements: subsets most, if not all, lookup types now. (Garret Rieger, Qunxin Liu) +- Fuzzer-found fixes and other improvements when memory failures happen. (Behdad) +- Removed most atomic implementations now that we have C++11 atomic impl. (Behdad) +- General codebase upkeep; using more C++11 features: constexpr constructors, etc. (Behdad) + + +Overview of changes leading to 2.7.4 +Sunday, December 27, 2020 +==================================== +- Fix missing --enable-introspection configure option from previous release + tarball. +- Documentation updates. + + +Overview of changes leading to 2.7.3 +Wednesday, December 23, 2020 +==================================== +- Update USE shaper to 2020-08-13 specification, and other improvements. +- Donโ€™t disable liga feature in myanmar shaper, to match Uniscribe. +- Improvements to language and script tags handling. +- Update language system tag registry to OpenType 1.8.4 +- Support for serializing and deserializing Unicode buffers. Serialized buffers + are now delimited with `<>` or `[]` based on whether it is a Unicode or + glyphs buffer. +- Increase buffer work limits to handle fonts with many complex lookups. +- Handle more shaping operations in trace output. +- Memory access fixes. +- More OOM fixes. +- Improved documentation. +- Build system improvements. +- New API: ++hb_buffer_has_positions() ++hb_buffer_serialize() ++hb_buffer_serialize_unicode() ++hb_buffer_deserialize_unicode() + + +Overview of changes leading to 2.7.2 +Saturday, August 29, 2020 +==================================== +- Fix a regression in the previous release that caused a crash with Kaithi. +- More OOM fixes. + + +Overview of changes leading to 2.7.1 +Thursday, August 13, 2020 +==================================== +- ot-funcs now handles variable empty glyphs better when hvar/vvar isn't present. +- Reverted a GDEF processing regression. +- A couple of fixes to handle OOM better. + + +Overview of changes leading to 2.7.0 +Saturday, July 25, 2020 +==================================== +- Use an implementation for round that always rounds up, some minor fluctuations + are expected on var font specially when hb-ot callback is used. +- Fix an AAT's `kerx` issue on broken rendering of Devanagari Sangam MN. +- Remove AAT's `lcar` table support from _get_ligature_carets API, not even much + use on macOS installed fonts (only two files). GDEF support is the recommended + one and expected to work properly after issues fixed two releases ago. +- Minor memory fixes to handle OOM better specially in hb-ft. +- Minor .so files versioning scheme change and remove stable/unstable scheme + differences, was never used in practice (always default to stable scheme). +- We are now suggesting careful packaging of the library using meson, + https://github.com/harfbuzz/harfbuzz/wiki/Notes-on-migration-to-meson + for more information. +- Distribution package URL is changed, either use GitHub generated tarballs, + `https://github.com/harfbuzz/harfbuzz/archive/$pkgver.tar.gz` + or, even more preferably use commit hash of the release and git checkouts like, + `git+https://github.com/harfbuzz/harfbuzz#commit=$commit` + + +Overview of changes leading to 2.6.8 +Monday, June 22, 2020 +==================================== +- New API to fetch glyph alternates from GSUB table. +- hb-coretext build fix for macOS < 10.10. +- Meson build fixes, cmake port removal is postponed but please prepare for + it and give us feedback. + Autotools is still our main build system however please consider + experimenting with meson also for packaging the library. +- New API: ++hb_ot_layout_lookup_get_glyph_alternates() + + +Overview of changes leading to 2.6.7 +Wednesday, June 3, 2020 +==================================== +- Update to Unicode 13.0.0. +- Fix hb_ot_layout_get_ligature_carets for fonts without lcar table, it was + completely broken for all the other fonts since 2.1.2. +- As a part of our migration to meson, this release will be the last one + to provide cmake port files but autotools still is our main build system. + There is a possibility that the next version or the after be released + using meson. + + +Overview of changes leading to 2.6.6 +Tuesday, May 12, 2020 +==================================== +- A fix in AAT kerning for Geeza Pro. +- Better support for resource fork fonts on macOS. + + +Overview of changes leading to 2.6.5 +Friday, April 17, 2020 +==================================== +- Add experimental meson build system. Autotools is still the primary + and supported build system. +- AAT is now always preferred for horizontal scripts when both AAT and OT + layout tables exist at the same time. +- Subsetter improvements. +- New API: ++hb_ft_font_lock_face() ++hb_ft_font_unlock_face() + + +Overview of changes leading to 2.6.4 +Monday, October 29, 2019 +==================================== +- Small bug fix. +- Build fixes. + + +Overview of changes leading to 2.6.3 +Monday, October 28, 2019 +==================================== +- Misc small fixes, mostly to build-related issues. +- New API: ++hb_font_get_nominal_glyphs() + + +Overview of changes leading to 2.6.2 +Monday, September 30, 2019 +==================================== +- Misc small fixes, mostly to build-related issues. + + +Overview of changes leading to 2.6.1 +Thursday, August 22, 2019 +==================================== +- Fix regression with hb_font_create_sub_font scaling introduced in 2.6.0. +- Change interpretation of font PTEM size / CoreText font size handling. + See https://github.com/harfbuzz/harfbuzz/pull/1484 +- hb-ot-font: Prefer symbol cmap subtable if present. +- Apply 'dist'/'abvm'/'blwm' features to all scripts. +- Drop experimental DirectWrite API. + + +Overview of changes leading to 2.6.0 +Tuesday, August 13, 2019 +==================================== +- New OpenType metrics, baseline, and metadata table access APIs. +- New API to set font variations to a named-instance. +- New hb-gdi.h header and API for creating hb_face_t from HFONT. +- Amalgam: Provide a single-file harfbuzz.cc file for easier alternate building. +- More size-reduction configurable options, enabled by HB_TINY. +- New API: ++hb_font_set_var_named_instance() ++hb_gdi_face_create() ++hb_ot_layout_baseline_tag_t ++hb_ot_layout_get_baseline() ++hb_ot_meta_tag_t ++hb_ot_meta_get_entry_tags() ++hb_ot_meta_reference_entry() ++hb_ot_metrics_tag_t ++hb_ot_metrics_get_position() ++hb_ot_metrics_get_variation() ++hb_ot_metrics_get_x_variation() ++hb_ot_metrics_get_y_variation() + + +Overview of changes leading to 2.5.3 +Wednesday, June 26, 2019 +==================================== +- Fix UCD script data for Unicode 10+ scripts. This was broken since 2.5.0. +- More optimizations for HB_TINY. + + +Overview of changes leading to 2.5.2 +Thursday, June 20, 2019 +==================================== +- More hb-config.hh facilities to shrink library size, namely when built as + HB_TINY. +- New documentation of custom configurations in CONFIG.md. +- Fix build on gcc 4.8. That's supported again. +- Universal Shaping Engine improvements thanks to David Corbett. +- API Changes: Undeprecate some horizontal-kerning API and re-enable in hb-ft, + such that Type1 fonts will continue kerning. + + +Overview of changes leading to 2.5.1 +Friday, May 31, 2019 +==================================== +- Fix build with various versions of Visual Studio. +- Improved documentation, thanks to Nathan Willis. +- Bugfix in subsetting glyf table. +- Improved scripts for cross-compiling for Windows using mingw. +- Rename HB_MATH_GLYPH_PART_FLAG_EXTENDER to HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER. + A deprecated macro is added for backwards-compatibility. + + +Overview of changes leading to 2.5.0 +Friday, May 24, 2019 +==================================== +- This release does not include much functional changes, but includes major internal + code-base changes. We now require C++11. Support for gcc 4.8 and earlier has been + dropped. +- New hb-config.hh facility for compiling smaller library for embedded and web usecases. +- New Unicode Character Database implementation that is half the size of previously-used + UCDN. +- Subsetter improvements. +- Improved documentation, thanks to Nathan Willis. +- Misc shaping fixes. + + +Overview of changes leading to 2.4.0 +Monday, March 25, 2019 +==================================== +- Unicode 12. +- Misc fixes. +- Subsetter improvements. +- New API: +HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE +hb_directwrite_face_create() + + +Overview of changes leading to 2.3.1 +Wednesday, January 30, 2019 +==================================== +- AAT bug fixes. +- Misc internal housekeeping cleanup. + + +Overview of changes leading to 2.3.0 +Thursday, December 20, 2018 +==================================== +- Fix regression on big-endian architectures. Ouch! +- Misc bug and build fixes. +- Fix subsetting of simple GSUB/GDEF. +- Merge CFF / CFF2 support contributed by Adobe. This mostly involves + the subsetter, but also get_glyph_extents on CFF fonts. + +New API in hb-aat.h: ++hb_aat_layout_has_substitution() ++hb_aat_layout_has_positioning() ++hb_aat_layout_has_tracking() + + +Overview of changes leading to 2.2.0 +Thursday, November 29, 2018 +==================================== +- Misc shaping bug fixes. +- Add font variations named-instance API. +- Deprecate font variations axis enumeration API and add replacement. +- AAT shaping improvements: + o Fixed 'kern' table Format 2 implementation. + o Implement 'feat' table API for feature detection. + o Blacklist 'GSUB' table of fonts from 'MUTF' foundry that also have 'morx'. + +New API: ++hb_aat_layout_feature_type_t ++hb_aat_layout_feature_selector_t ++hb_aat_layout_get_feature_types() ++hb_aat_layout_feature_type_get_name_id ++hb_aat_layout_feature_selector_info_t ++HB_AAT_LAYOUT_NO_SELECTOR_INDEX ++hb_aat_layout_feature_type_get_selector_infos() ++hb_ot_var_axis_flags_t ++hb_ot_var_axis_info_t ++hb_ot_var_get_axis_infos() ++hb_ot_var_find_axis_info() ++hb_ot_var_get_named_instance_count() ++hb_ot_var_named_instance_get_subfamily_name_id() ++hb_ot_var_named_instance_get_postscript_name_id() ++hb_ot_var_named_instance_get_design_coords() + +Deprecated API: ++HB_OT_VAR_NO_AXIS_INDEX ++hb_ot_var_axis_t ++hb_ot_var_get_axes() ++hb_ot_var_find_axis() + + +Overview of changes leading to 2.1.3 +Friday, November 16, 2018 +==================================== +- Fix AAT 'mort' shaping, which was broken in 2.1.2 + + +Overview of changes leading to 2.1.2 +Friday, November 16, 2018 +==================================== +- Various internal changes. +- AAT shaping improvements: + o Implement kern table Format 1 state-machine-based kerning. + o Implement cross-stream kerning (cursive positioning, etc). + o Ignore emptyish GSUB tables (zero scripts) if morx present. + o Don't apply GPOS if morx is being applied. Matches Apple. + + +-Overview of changes leading to 2.1.1 +Monday, November 5, 2018 +==================================== +- AAT improvements: + o Implement 'mort' table. + o Implement 'kern' subtables Format 1 and Format 3. + + +Overview of changes leading to 2.1.0 +Tuesday, October 30, 2018 +==================================== +- AAT shaping improvements: + o Allow user controlling AAT features, for whole buffer only currently. + o Several 'morx' fixes. + o Implement tuple-kerns in 'kerx'; Fixes kerning with Apple default + San Francisco fonts. +- Support for color fonts: + o COLR/CPAL API to fetch color layers. + o SVG table to fetch SVG documents. + o CBDT/sbix API to fetch PNG images. +- New 'name' table API. +- hb-ot-font now uses 'VORG' table to correctly position CFF glyphs + in vertical layout. +- Various fuzzer-found bug fixes. + +Changed API: + +A type and a macro added in 2.0.0 were renamed: + +hb_name_id_t -> hb_ot_name_id_t +HB_NAME_ID_INVALID -> HB_OT_NAME_ID_INVALID + +New API: + ++hb_color_t ++HB_COLOR ++hb_color_get_alpha() ++hb_color_get_red() ++hb_color_get_green() ++hb_color_get_blue() ++hb_ot_color_has_palettes() ++hb_ot_color_palette_get_count() ++hb_ot_color_palette_get_name_id() ++hb_ot_color_palette_color_get_name_id() ++hb_ot_color_palette_flags_t ++hb_ot_color_palette_get_flags() ++hb_ot_color_palette_get_colors() ++hb_ot_color_has_layers() ++hb_ot_color_layer_t ++hb_ot_color_glyph_get_layers() ++hb_ot_color_has_svg() ++hb_ot_color_glyph_reference_svg() ++hb_ot_color_has_png() ++hb_ot_color_glyph_reference_png() + ++hb_ot_name_id_t ++HB_OT_NAME_ID_INVALID ++HB_OT_NAME_ID_COPYRIGHT ++HB_OT_NAME_ID_FONT_FAMILY ++HB_OT_NAME_ID_FONT_SUBFAMILY ++HB_OT_NAME_ID_UNIQUE_ID ++HB_OT_NAME_ID_FULL_NAME ++HB_OT_NAME_ID_VERSION_STRING ++HB_OT_NAME_ID_POSTSCRIPT_NAME ++HB_OT_NAME_ID_TRADEMARK ++HB_OT_NAME_ID_MANUFACTURER ++HB_OT_NAME_ID_DESIGNER ++HB_OT_NAME_ID_DESCRIPTION ++HB_OT_NAME_ID_VENDOR_URL ++HB_OT_NAME_ID_DESIGNER_URL ++HB_OT_NAME_ID_LICENSE ++HB_OT_NAME_ID_LICENSE_URL ++HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY ++HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY ++HB_OT_NAME_ID_MAC_FULL_NAME ++HB_OT_NAME_ID_SAMPLE_TEXT ++HB_OT_NAME_ID_CID_FINDFONT_NAME ++HB_OT_NAME_ID_WWS_FAMILY ++HB_OT_NAME_ID_WWS_SUBFAMILY ++HB_OT_NAME_ID_LIGHT_BACKGROUND ++HB_OT_NAME_ID_DARK_BACKGROUND ++HB_OT_NAME_ID_VARIATIONS_PS_PREFIX ++hb_ot_name_entry_t ++hb_ot_name_list_names() ++hb_ot_name_get_utf8() ++hb_ot_name_get_utf16() ++hb_ot_name_get_utf32() + + +Overview of changes leading to 2.0.2 +Saturday, October 20, 2018 +==================================== +- Fix two minor memory access issues in AAT tables. + + +Overview of changes leading to 2.0.1 +Friday, October 19, 2018 +==================================== +- Fix hb-version.h reported release version that went wrong (1.8.0) + with previous release. +- Fix extrapolation in 'trak' table. +- Fix hb-font infinite-recursion issue with some font funcs and + subclassed fonts. +- Implement variation-kerning format in kerx table, although without + variation. +- Fix return value of hb_map_is_empty(). + + +Overview of changes leading to 2.0.0 +Thursday, October 18, 2018 +==================================== +- Added AAT shaping support (morx/kerx/trak). + Automatically used if GSUB/GPOS are not available respectively. + Set HB_OPTIONS=aat env var to have morx/kerx preferred over + GSUB/GPOS. +- Apply TrueType kern table internally, instead of relying on + hb_font_t callbacks. +- Khmer shaper significantly rewritten to better match Uniscribe. +- Indic3 tags ('dev3', etc) are passed to USE shaper. +- .dfont Mac font containers implemented. +- Script- and language-mapping revamped to better use BCP 47. +- Misc USE and Indic fixes. +- Misc everything fixes. +- Too many things to list. Biggest release since 0.9.1, with + over 500 commits in just over 5 weeks! Didn't intend it to + be a big release. Just happened to become. +- hb-ft now locks underlying FT_Face during use. + +API changes: + +- Newly-created hb_font_t's now have our internal "hb-ot-font" + callbacks set on them, so they should work out of the box + without any callbacks set. If callbacks are set, everything + is back to what it was before, the fallback callbacks are + null. If you to get the internal implementation modified, + sub_font it. + +- New hb_font_funcs_set_nominal_glyphs_func() allows speeding + up character to glyph mapping. + +New API: ++HB_FEATURE_GLOBAL_START ++HB_FEATURE_GLOBAL_END ++hb_buffer_set_invisible_glyph() ++hb_buffer_get_invisible_glyph() ++hb_font_funcs_set_nominal_glyphs_func() ++hb_ot_layout_table_select_script() ++hb_ot_layout_script_select_language() ++hb_ot_layout_feature_get_name_ids() ++hb_ot_layout_feature_get_characters() ++hb_name_id_t ++HB_NAME_ID_INVALID ++HB_OT_MAX_TAGS_PER_SCRIPT ++hb_ot_tags_from_script_and_language() ++hb_ot_tags_to_script_and_language() + +Deprecated API: +-hb_font_funcs_set_glyph_func() +-hb_unicode_eastasian_width_func_t +-hb_unicode_funcs_set_eastasian_width_func() +-hb_unicode_eastasian_width() +-hb_unicode_decompose_compatibility_func_t +-HB_UNICODE_MAX_DECOMPOSITION_LEN +-hb_unicode_funcs_set_decompose_compatibility_func() +-hb_unicode_decompose_compatibility() +-hb_font_funcs_set_glyph_h_kerning_func() +-hb_font_funcs_set_glyph_v_kerning_func() +-hb_font_get_glyph_h_kerning() +-hb_font_get_glyph_v_kerning() +-hb_font_get_glyph_kerning_for_direction() +-hb_ot_layout_table_choose_script() +-hb_ot_layout_script_find_language() +-hb_ot_tags_from_script() +-hb_ot_tag_from_language() + + +Overview of changes leading to 1.9.0 +Monday, September 10, 2018 +==================================== +- Added 'cmap' API to hb_face_t. +- Face-builder API. +- hb-ot-font re-creation should be much leaner now, as the + font tables it uses are cached on hb_face_t now. +- Internal source header file name changes: + hb-*-private.hh is renamed to hb-*.hh. + +New API: ++HB_UNICODE_MAX ++hb_face_collect_unicodes() ++hb_face_collect_variation_selectors() ++hb_face_collect_variation_unicodes() ++hb_face_builder_create() ++hb_face_builder_add_table() + + +Overview of changes leading to 1.8.8 +Tuesday, August 14, 2018 +==================================== +- Fix hb-icu crash on architectures where compare_exchange_weak() can + fail falsely. This bug was introduced in 1.8.4. + https://bugs.chromium.org/p/chromium/issues/detail?id=873568 +- More internal refactoring of atomic operations and singletons. +- API changes: + The following functions do NOT reference their return value before + returning: + * hb_unicode_funcs_get_default() + * hb_glib_get_unicode_funcs() + * hb_icu_get_unicode_funcs() + This is consistent with their naming ("get", instead of "reference") + as well as how they are used in the wild (ie. no one calls destroy() + on their return value.) + + +Overview of changes leading to 1.8.7 +Wednesday, August 8, 2018 +==================================== +- Fix assertion failure with GDEF-blacklisted fonts. + + +Overview of changes leading to 1.8.6 +Tuesday, August 7, 2018 +==================================== +- Internal code shuffling. +- New API to speed up getting advance widths for implementations + that have heavy overhead in get_h_advance callback: ++hb_font_funcs_set_glyph_h_advances_func ++hb_font_funcs_set_glyph_v_advances_func ++hb_font_get_glyph_advances_for_direction ++hb_font_get_glyph_h_advances ++hb_font_get_glyph_h_advances_func_t ++hb_font_get_glyph_v_advances ++hb_font_get_glyph_v_advances_func_t + + +Overview of changes leading to 1.8.5 +Wednesday, August 1, 2018 +==================================== +- Major Khmer shaper improvements to better match Microsoft. +- Indic bug fixes. +- Internal improvements to atomic operations. + + +Overview of changes leading to 1.8.4 +Tuesday, July 17, 2018 +==================================== +- Fix build on non-C++11. +- Use C++-style GCC atomics and C++11 atomics. + + +Overview of changes leading to 1.8.3 +Wednesday, July 11, 2018 +==================================== +- A couple of Indic / USE bug fixes. +- Disable vectorization, as it was causing unaligned access bus error on + certain 32bit architectures. + + +Overview of changes leading to 1.8.2 +Tuesday, July 3, 2018 +==================================== +- Fix infinite loop in Khmer shaper. +- Improve hb_blob_create_from_file() for streams. + + +Overview of changes leading to 1.8.1 +Tuesday, June 12, 2018 +==================================== +- Fix hb-version.h file generation; last two releases went out with wrong ones. +- Add correctness bug in hb_set_t operations, introduced in 1.7.7. +- Remove HB_SUBSET_BUILTIN build option. Not necessary. + + +Overview of changes leading to 1.8.0 +Tuesday, June 5, 2018 +==================================== +- Update to Unicode 11.0.0. + + +Overview of changes leading to 1.7.7 +Tuesday, June 5, 2018 +==================================== +- Lots of internal changes, but not yet exposed externally. +- All HarfBuzz objects are significantly smaller in size now. +- Sinhala: Position repha on top of post-consonant, not base. + This better matches Windows 10 behavior, which was changed + from previous Windows versions. +- New build options: + o New cpp macro HB_NO_ATEXIT + o New cpp macro HB_SUBSET_BUILTIN +- Significant libharfbuzz-subset changes. API subject to change. +- New API in libharfbuzz: + ++hb_blob_create_from_file() ++hb_face_count() + +A hashmap implementation: ++hb-map.h ++HB_MAP_VALUE_INVALID ++hb_map_t ++hb_map_create() ++hb_map_get_empty() ++hb_map_reference() ++hb_map_destroy() ++hb_map_set_user_data() ++hb_map_get_user_data() ++hb_map_allocation_successful() ++hb_map_clear() ++hb_map_is_empty() ++hb_map_get_population() ++hb_map_set() ++hb_map_get() ++hb_map_del() ++hb_map_has() + + +Overview of changes leading to 1.7.6 +Wednesday, March 7, 2018 +==================================== + +- Fix to hb_set_t binary operations. Ouch. +- New experimental harfbuzz-subset library. All of hb-subset.h + is experimental right now and API WILL change. + +- New API: +hb_blob_copy_writable_or_fail() +HB_OT_TAG_BASE +hb_set_previous() +hb_set_previous_range() + + +Overview of changes leading to 1.7.5 +Tuesday, January 30, 2018 +==================================== + +- Separate Khmer shaper from Indic. +- First stab at AAT morx. Not hooked up. +- Misc bug fixes. + + Overview of changes leading to 1.7.4 Wednesday, December 20, 2017 ==================================== @@ -415,7 +1863,7 @@ Thursday, February 25, 2016 due to bug in glyph class of ASCII double-quote character. This should address "regression" introduced in 1.2.0 when we switched mark zeroing in most shapers from BY_UNICODE_LATE to BY_GDEF_LATE. - This fourth release in a week should finally stablize things... + This fourth release in a week should finally stabilize things... - hb-ot-font's get_glyph() implementation saw some optimizations. Though, might be really hard to measure in real-world situations. @@ -1463,7 +2911,7 @@ o Changed API: - hb_buffer_create() takes zero arguments now. Use hb_buffer_pre_allocate() to pre-allocate. - - hb_buffer_add_utf*() now accept -1 for length parameteres, + - hb_buffer_add_utf*() now accept -1 for length parameters, meaning "nul-terminated". - hb_direction_t enum values changed. diff --git a/src/3rdparty/harfbuzz-ng/THANKS b/src/3rdparty/harfbuzz-ng/THANKS index 940cfde5c39..88cb7e9ea12 100644 --- a/src/3rdparty/harfbuzz-ng/THANKS +++ b/src/3rdparty/harfbuzz-ng/THANKS @@ -1,6 +1,6 @@ Bradley Grainger -Khaled Hosny Kenichi Ishibashi +Ivan Kuckir Ryan Lortie Jeff Muizelaar suzuki toshiya diff --git a/src/3rdparty/harfbuzz-ng/config.h b/src/3rdparty/harfbuzz-ng/config.h index b8b6b3c0fe3..38087a71ef4 100644 --- a/src/3rdparty/harfbuzz-ng/config.h +++ b/src/3rdparty/harfbuzz-ng/config.h @@ -22,6 +22,9 @@ * */ +#ifndef QHARFBUZZ_CONFIG_H +#define QHARFBUZZ_CONFIG_H + #include QT_USE_NAMESPACE @@ -37,11 +40,19 @@ inline QAtomicPointer *makeAtomicPointer(T * const &ptr) return reinterpret_cast *>(const_cast(&ptr)); } +static inline void _hb_memory_barrier () +{ + QAtomicInt a; + a.ref(); // Ordered memory semantics, so imposes a memory barrier at this point +} + } // namespace typedef int hb_atomic_int_impl_t; #define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) reinterpret_cast(AI).fetchAndAddOrdered(V) +#define hb_atomic_int_impl_add(AI, V) reinterpret_cast(AI)->fetchAndAddOrdered(V) #define hb_atomic_ptr_impl_get(P) makeAtomicPointer(*(P))->loadAcquire() #define hb_atomic_ptr_impl_cmpexch(P,O,N) makeAtomicPointer(*(P))->testAndSetOrdered((O), (N)) + +#endif // QHARFBUZZ_CONFIG_H diff --git a/src/3rdparty/harfbuzz-ng/harfbuzz-ng.pro b/src/3rdparty/harfbuzz-ng/harfbuzz-ng.pro index 19699389321..f55b31fff4b 100644 --- a/src/3rdparty/harfbuzz-ng/harfbuzz-ng.pro +++ b/src/3rdparty/harfbuzz-ng/harfbuzz-ng.pro @@ -14,11 +14,11 @@ SHAPERS += opentype # HB's main shaper; enabling it should be enough most # native shaper on Apple platforms; could be used alone to handle both OT and AAT fonts darwin: SHAPERS += coretext -# fallback shaper: not really useful with opentype or coretext shaper -#SHAPERS += fallback +# fallback shaper: not really useful with opentype or coretext shaper but needed for linking +SHAPERS += fallback DEFINES += HAVE_CONFIG_H -DEFINES += HB_NO_UNICODE_FUNCS HB_DISABLE_DEPRECATED +DEFINES += HB_NO_UNICODE_FUNCS DEFINES += HB_NDEBUG DEFINES += HB_EXTERN= @@ -35,35 +35,46 @@ INCLUDEPATH += $$QT.core.includes DEFINES += QT_NO_VERSION_TAGGING SOURCES += \ + $$PWD/hb-dummy.cc \ + $$PWD/src/hb-aat-layout.cc \ + $$PWD/src/hb-aat-map.cc \ $$PWD/src/hb-blob.cc \ $$PWD/src/hb-buffer.cc \ $$PWD/src/hb-buffer-serialize.cc \ $$PWD/src/hb-common.cc \ + $$PWD/src/hb-draw.cc \ $$PWD/src/hb-face.cc \ + $$PWD/src/hb-face-builder.cc \ $$PWD/src/hb-font.cc \ + $$PWD/src/hb-map.cc \ + $$PWD/src/hb-number.cc \ $$PWD/src/hb-ot-tag.cc \ + $$PWD/src/hb-outline.cc \ + $$PWD/src/hb-paint.cc \ + $$PWD/src/hb-paint-extents.cc \ $$PWD/src/hb-set.cc \ $$PWD/src/hb-shape.cc \ $$PWD/src/hb-shape-plan.cc \ $$PWD/src/hb-shaper.cc \ + $$PWD/src/hb-style.cc \ + $$PWD/src/hb-subset.cc \ + $$PWD/src/hb-subset-cff-common.cc \ + $$PWD/src/hb-subset-cff1.cc \ + $$PWD/src/hb-subset-cff2.cc \ + $$PWD/src/hb-subset-input.cc \ + $$PWD/src/hb-subset-instancer-solver.cc \ + $$PWD/src/hb-subset-plan.cc \ + $$PWD/src/hb-subset-repacker.cc \ $$PWD/src/hb-unicode.cc \ - $$PWD/src/hb-warning.cc + $$PWD/src/hb-buffer-verify.cc HEADERS += \ - $$PWD/src/hb-atomic-private.hh \ - $$PWD/src/hb-buffer-private.hh \ $$PWD/src/hb-buffer-deserialize-json.hh \ - $$PWD/src/hb-buffer-deserialize-text.hh \ - $$PWD/src/hb-cache-private.hh \ + $$PWD/src/hb-buffer-deserialize-text-glyphs.hh \ + $$PWD/src/hb-buffer-deserialize-text-unicode.hh \ $$PWD/src/hb-debug.hh \ - $$PWD/src/hb-dsalgs.hh \ - $$PWD/src/hb-face-private.hh \ - $$PWD/src/hb-font-private.hh \ - $$PWD/src/hb-mutex-private.hh \ - $$PWD/src/hb-object-private.hh \ - $$PWD/src/hb-open-file-private.hh \ - $$PWD/src/hb-open-type-private.hh \ - $$PWD/src/hb-ot-cbdt-table.hh \ + $$PWD/src/hb-face.hh \ + $$PWD/src/hb-mutex.hh \ $$PWD/src/hb-ot-cmap-table.hh \ $$PWD/src/hb-ot-glyf-table.hh \ $$PWD/src/hb-ot-head-table.hh \ @@ -73,83 +84,119 @@ HEADERS += \ $$PWD/src/hb-ot-name-table.hh \ $$PWD/src/hb-ot-os2-table.hh \ $$PWD/src/hb-ot-post-table.hh \ - $$PWD/src/hb-private.hh \ - $$PWD/src/hb-set-digest-private.hh \ - $$PWD/src/hb-set-private.hh \ - $$PWD/src/hb-shape-plan-private.hh \ - $$PWD/src/hb-shaper-impl-private.hh \ + $$PWD/src/hb-set.hh \ + $$PWD/src/hb-set-digest.hh \ + $$PWD/src/hb-shape-plan.hh \ + $$PWD/src/hb-shaper.hh \ + $$PWD/src/hb-shaper-impl.hh \ $$PWD/src/hb-shaper-list.hh \ - $$PWD/src/hb-shaper-private.hh \ $$PWD/src/hb-string-array.hh \ - $$PWD/src/hb-unicode-private.hh \ - $$PWD/src/hb-utf-private.hh + $$PWD/src/hb-subset-repacker.h \ + $$PWD/src/hb-unicode.hh HEADERS += \ $$PWD/src/hb.h \ + $$PWD/src/hb-algs.hh \ + $$PWD/src/hb-atomic.hh \ $$PWD/src/hb-blob.h \ $$PWD/src/hb-buffer.h \ + $$PWD/src/hb-buffer.hh \ + $$PWD/src/hb-cache.hh \ $$PWD/src/hb-common.h \ $$PWD/src/hb-deprecated.h \ + $$PWD/src/hb-draw.h \ + $$PWD/src/hb-draw.hh \ $$PWD/src/hb-face.h \ $$PWD/src/hb-font.h \ + $$PWD/src/hb-font.hh \ + $$PWD/src/hb-ft-colr.hh \ + $$PWD/src/hb-limits.hh \ + $$PWD/src/hb-map.h \ + $$PWD/src/hb-map.hh \ + $$PWD/src/hb-object.hh \ + $$PWD/src/hb-open-file.hh \ + $$PWD/src/hb-open-type.hh \ + $$PWD/src/hb-ot-cff1-std-str.hh \ + $$PWD/src/hb-outline.hh \ + $$PWD/src/hb-paint.h \ + $$PWD/src/hb-paint.hh \ + $$PWD/src/hb-paint-extents.hh \ + $$PWD/src/hb-priority-queue.hh \ + $$PWD/src/hb-repacker.hh \ $$PWD/src/hb-set.h \ $$PWD/src/hb-shape.h \ $$PWD/src/hb-shape-plan.h \ + $$PWD/src/hb-style.h \ $$PWD/src/hb-unicode.h \ + $$PWD/src/hb-utf.hh \ $$PWD/src/hb-version.h contains(SHAPERS, opentype) { DEFINES += HAVE_OT SOURCES += \ + $$PWD/src/hb-ot-cff1-table.cc \ + $$PWD/src/hb-ot-cff2-table.cc \ + $$PWD/src/hb-ot-color.cc \ + $$PWD/src/hb-ot-face.cc \ $$PWD/src/hb-ot-font.cc \ $$PWD/src/hb-ot-layout.cc \ $$PWD/src/hb-ot-map.cc \ $$PWD/src/hb-ot-math.cc \ + $$PWD/src/hb-ot-meta.cc \ + $$PWD/src/hb-ot-metrics.cc \ + $$PWD/src/hb-ot-name.cc \ $$PWD/src/hb-ot-shape.cc \ - $$PWD/src/hb-ot-shape-complex-arabic.cc \ - $$PWD/src/hb-ot-shape-complex-default.cc \ - $$PWD/src/hb-ot-shape-complex-hangul.cc \ - $$PWD/src/hb-ot-shape-complex-hebrew.cc \ - $$PWD/src/hb-ot-shape-complex-indic.cc \ - $$PWD/src/hb-ot-shape-complex-indic-table.cc \ - $$PWD/src/hb-ot-shape-complex-myanmar.cc \ - $$PWD/src/hb-ot-shape-complex-thai.cc \ - $$PWD/src/hb-ot-shape-complex-tibetan.cc \ - $$PWD/src/hb-ot-shape-complex-use.cc \ - $$PWD/src/hb-ot-shape-complex-use-table.cc \ + $$PWD/src/hb-ot-shaper-arabic.cc \ + $$PWD/src/hb-ot-shaper-default.cc \ + $$PWD/src/hb-ot-shaper-hangul.cc \ + $$PWD/src/hb-ot-shaper-hebrew.cc \ + $$PWD/src/hb-ot-shaper-indic.cc \ + $$PWD/src/hb-ot-shaper-thai.cc \ $$PWD/src/hb-ot-shape-fallback.cc \ $$PWD/src/hb-ot-shape-normalize.cc \ - $$PWD/src/hb-ot-var.cc + $$PWD/src/hb-ot-shaper-indic-table.cc \ + $$PWD/src/hb-ot-shaper-khmer.cc \ + $$PWD/src/hb-ot-shaper-myanmar.cc \ + $$PWD/src/hb-ot-shaper-syllabic.cc \ + $$PWD/src/hb-ot-shaper-use.cc \ + $$PWD/src/hb-ot-shaper-vowel-constraints.cc \ + $$PWD/src/hb-ot-var.cc HEADERS += \ $$PWD/src/hb-ot-kern-table.hh \ - $$PWD/src/hb-ot-layout-common-private.hh \ $$PWD/src/hb-ot-layout-gdef-table.hh \ $$PWD/src/hb-ot-layout-gpos-table.hh \ - $$PWD/src/hb-ot-layout-gsubgpos-private.hh \ $$PWD/src/hb-ot-layout-gsub-table.hh \ $$PWD/src/hb-ot-layout-jstf-table.hh \ - $$PWD/src/hb-ot-layout-math-table.hh \ - $$PWD/src/hb-ot-layout-private.hh \ - $$PWD/src/hb-ot-map-private.hh \ + $$PWD/src/hb-ot-layout.hh \ + $$PWD/src/hb-ot-map.hh \ $$PWD/src/hb-ot-math-table.hh \ $$PWD/src/hb-ot-post-macroman.hh \ - $$PWD/src/hb-ot-shape-complex-arabic-fallback.hh \ - $$PWD/src/hb-ot-shape-complex-arabic-private.hh \ - $$PWD/src/hb-ot-shape-complex-arabic-table.hh \ -# $$PWD/src/hb-ot-shape-complex-arabic-win1256.hh \ # disabled with HB_NO_WIN1256 - $$PWD/src/hb-ot-shape-complex-indic-machine.hh \ - $$PWD/src/hb-ot-shape-complex-indic-private.hh \ - $$PWD/src/hb-ot-shape-complex-myanmar-machine.hh \ - $$PWD/src/hb-ot-shape-complex-private.hh \ - $$PWD/src/hb-ot-shape-complex-use-machine.hh \ - $$PWD/src/hb-ot-shape-complex-use-private.hh \ - $$PWD/src/hb-ot-shape-fallback-private.hh \ - $$PWD/src/hb-ot-shape-normalize-private.hh \ - $$PWD/src/hb-ot-shape-private.hh \ + $$PWD/src/hb-ot-post-table-v2subset.hh \ + $$PWD/src/hb-ot-shaper-arabic-joining-list.hh \ + $$PWD/src/hb-ot-shaper-arabic-pua.hh \ + $$PWD/src/hb-ot-shaper-indic-machine.hh \ + $$PWD/src/hb-ot-shaper-arabic-fallback.hh \ + $$PWD/src/hb-ot-shaper-arabic.hh \ + $$PWD/src/hb-ot-shaper-arabic-table.hh \ +# $$PWD/src/hb-ot-shaper-arabic-win1256.hh \ # disabled with HB_NO_WIN1256 + $$PWD/src/hb-ot-shaper.hh \ + $$PWD/src/hb-ot-shape-fallback.hh \ + $$PWD/src/hb-ot-shape-normalize.hh \ + $$PWD/src/hb-ot-shaper-indic.hh \ + $$PWD/src/hb-ot-shaper-khmer-machine.hh \ + $$PWD/src/hb-ot-shaper-myanmar-machine.hh \ + $$PWD/src/hb-ot-shaper-syllabic.hh \ + $$PWD/src/hb-ot-shaper-use-machine.hh \ + $$PWD/src/hb-ot-shaper-use-table.hh \ + $$PWD/src/hb-ot-shaper-vowel-constraints.hh \ + $$PWD/src/hb-subset-accelerator.hh \ + $$PWD/src/hb-ot-var-common.hh \ $$PWD/src/hb-ot-var-avar-table.hh \ + $$PWD/src/hb-ot-var-cvar-table.hh \ $$PWD/src/hb-ot-var-fvar-table.hh \ + $$PWD/src/hb-ot-var-gvar-table.hh \ $$PWD/src/hb-ot-var-hvar-table.hh \ $$PWD/src/hb-ot-var-mvar-table.hh @@ -159,8 +206,17 @@ contains(SHAPERS, opentype) { $$PWD/src/hb-ot-layout.h \ $$PWD/src/hb-ot-math.h \ $$PWD/src/hb-ot-shape.h \ - $$PWD/src/hb-ot-tag.h \ - $$PWD/src/hb-ot-var.h + $$PWD/src/hb-ot-shape.hh \ + $$PWD/src/hb-ot-var.h \ + $$PWD/src/OT/Color/CBDT/CBDT.hh \ + $$PWD/src/OT/Color/COLR/COLR.hh \ + $$PWD/src/OT/Color/COLR/colrv1-closure.hh \ + $$PWD/src/OT/Color/CPAL/CPAL.hh \ + $$PWD/src/OT/Color/sbix/sbix.hh \ + $$PWD/src/OT/Color/svg/svg.hh \ + $$PWD/src/OT/Layout/GDEF/GDEF.hh \ + $$PWD/src/OT/name/name.hh + } MODULE_EXT_HEADERS = $$HEADERS @@ -169,7 +225,7 @@ contains(SHAPERS, coretext) { DEFINES += HAVE_CORETEXT SOURCES += \ - $$PWD/src/hb-coretext.mm + $$PWD/src/hb-coretext.cc HEADERS += \ $$PWD/src/hb-coretext.h diff --git a/src/3rdparty/harfbuzz-ng/hb-dummy.cc b/src/3rdparty/harfbuzz-ng/hb-dummy.cc new file mode 100644 index 00000000000..490b8ae35c8 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/hb-dummy.cc @@ -0,0 +1,8 @@ +// Work-around for issue with qmake: Since hb-common.cc has #include "hb-static.cc" in it, +// qmake will assume it is included and ignore it in the SOURCES list. But the #include +// is protected inside an #ifdef and will not be used, so hb-static.cc ends up not being +// linked at all and we get missing symbols. We work around this by including both in +// the same compilation unit. + +#include "src/hb-common.cc" +#include "src/hb-static.cc" diff --git a/src/3rdparty/harfbuzz-ng/qt_attribution.json b/src/3rdparty/harfbuzz-ng/qt_attribution.json index 2f1aed8a877..d6807118963 100644 --- a/src/3rdparty/harfbuzz-ng/qt_attribution.json +++ b/src/3rdparty/harfbuzz-ng/qt_attribution.json @@ -3,24 +3,31 @@ "Name": "HarfBuzz-NG", "QDocModule": "qtgui", "QtUsage": "Optionally used in Qt GUI. Configure with -system-harfbuzz to force the use of the system library, or -qt-harfbuzz to link statically to the library that is bundled with your Qt version.", + "SecurityCritical": true, "Description": "HarfBuzz is an OpenType text shaping engine.", "Homepage": "http://harfbuzz.org", - "Version": "1.7.4", + "Version": "7.2.0", + "DownloadLocation": "https://github.com/harfbuzz/harfbuzz/releases/tag/7.1.0", "License": "MIT License", "LicenseId": "MIT", "LicenseFile": "COPYING", - "Copyright": "Copyright ยฉ 2010,2011,2012 Google, Inc. -Copyright ยฉ 2012 Mozilla Foundation -Copyright ยฉ 2011 Codethink Limited -Copyright ยฉ 2008,2010 Nokia Corporation and/or its subsidiary(-ies) -Copyright ยฉ 2009 Keith Stribley -Copyright ยฉ 2009 Martin Hosken and SIL International -Copyright ยฉ 2007 Chris Wilson -Copyright ยฉ 2006 Behdad Esfahbod -Copyright ยฉ 2005 David Turner -Copyright ยฉ 2004,2007,2008,2009,2010 Red Hat, Inc. -Copyright ยฉ 1998-2004 David Turner and Werner Lemberg -" + "Copyright": "Copyright ยฉ 2010-2022 Google, Inc. + Copyright ยฉ 2015-2020 Ebrahim Byagowi + Copyright ยฉ 2019,2020 Facebook, Inc. + Copyright ยฉ 2012,2015 Mozilla Foundation + Copyright ยฉ 2011 Codethink Limited + Copyright ยฉ 2008,2010 Nokia Corporation and/or its subsidiary(-ies) + Copyright ยฉ 2009 Keith Stribley + Copyright ยฉ 2011 Martin Hosken and SIL International + Copyright ยฉ 2007 Chris Wilson + Copyright ยฉ 2005,2006,2020,2021,2022,2023 Behdad Esfahbod + Copyright ยฉ 2004,2007,2008,2009,2010,2013,2021,2022,2023 Red Hat, Inc. + Copyright ยฉ 1998-2005 David Turner and Werner Lemberg + Copyright ยฉ 2016 Igalia S.L. + Copyright ยฉ 2022 Matthias Clasen + Copyright ยฉ 2018,2021 Khaled Hosny + Copyright ยฉ 2018,2019,2020 Adobe, Inc + Copyright ยฉ 2013-2015 Alexei Podtelezhnikov" } diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Color/CBDT/CBDT.hh b/src/3rdparty/harfbuzz-ng/src/OT/Color/CBDT/CBDT.hh new file mode 100644 index 00000000000..b125052344f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Color/CBDT/CBDT.hh @@ -0,0 +1,1030 @@ +/* + * Copyright ยฉ 2016 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Seigo Nonaka, Calder Kitagawa + */ + +#ifndef OT_COLOR_CBDT_CBDT_HH +#define OT_COLOR_CBDT_CBDT_HH + +#include "../../../hb-open-type.hh" +#include "../../../hb-paint.hh" + +/* + * CBLC -- Color Bitmap Location + * https://docs.microsoft.com/en-us/typography/opentype/spec/cblc + * https://docs.microsoft.com/en-us/typography/opentype/spec/eblc + * CBDT -- Color Bitmap Data + * https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt + * https://docs.microsoft.com/en-us/typography/opentype/spec/ebdt + */ +#define HB_OT_TAG_CBLC HB_TAG('C','B','L','C') +#define HB_OT_TAG_CBDT HB_TAG('C','B','D','T') + + +namespace OT { + +struct cblc_bitmap_size_subset_context_t +{ + const char *cbdt; + unsigned int cbdt_length; + hb_vector_t *cbdt_prime; + unsigned int size; /* INOUT + * Input: old size of IndexSubtable + * Output: new size of IndexSubtable + */ + unsigned int num_tables; /* INOUT + * Input: old number of subtables. + * Output: new number of subtables. + */ + hb_codepoint_t start_glyph; /* OUT */ + hb_codepoint_t end_glyph; /* OUT */ +}; + +static inline bool +_copy_data_to_cbdt (hb_vector_t *cbdt_prime, + const void *data, + unsigned length) +{ + unsigned int new_len = cbdt_prime->length + length; + if (unlikely (!cbdt_prime->alloc (new_len))) return false; + hb_memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length); + cbdt_prime->length = new_len; + return true; +} + +struct SmallGlyphMetrics +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scale) const + { + extents->x_bearing = bearingX; + extents->y_bearing = bearingY; + extents->width = width; + extents->height = -static_cast (height); + + if (scale) + font->scale_glyph_extents (extents); + } + + HBUINT8 height; + HBUINT8 width; + HBINT8 bearingX; + HBINT8 bearingY; + HBUINT8 advance; + public: + DEFINE_SIZE_STATIC (5); +}; + +struct BigGlyphMetrics : SmallGlyphMetrics +{ + HBINT8 vertBearingX; + HBINT8 vertBearingY; + HBUINT8 vertAdvance; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct SBitLineMetrics +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBINT8 ascender; + HBINT8 decender; + HBUINT8 widthMax; + HBINT8 caretSlopeNumerator; + HBINT8 caretSlopeDenominator; + HBINT8 caretOffset; + HBINT8 minOriginSB; + HBINT8 minAdvanceSB; + HBINT8 maxBeforeBL; + HBINT8 minAfterBL; + HBINT8 padding1; + HBINT8 padding2; + public: + DEFINE_SIZE_STATIC (12); +}; + + +/* + * Index Subtables. + */ + +struct IndexSubtableHeader +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 indexFormat; + HBUINT16 imageFormat; + HBUINT32 imageDataOffset; + public: + DEFINE_SIZE_STATIC (8); +}; + +template +struct IndexSubtableFormat1Or3 +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + offsetArrayZ.sanitize (c, glyph_count + 1)); + } + + bool get_image_data (unsigned int idx, + unsigned int *offset, + unsigned int *length) const + { + if (unlikely (offsetArrayZ[idx + 1] <= offsetArrayZ[idx])) + return false; + + *offset = header.imageDataOffset + offsetArrayZ[idx]; + *length = offsetArrayZ[idx + 1] - offsetArrayZ[idx]; + return true; + } + + bool add_offset (hb_serialize_context_t *c, + unsigned int offset, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + Offset embedded_offset; + embedded_offset = offset; + *size += sizeof (OffsetType); + auto *o = c->embed (embedded_offset); + return_trace ((bool) o); + } + + IndexSubtableHeader header; + UnsizedArrayOf> + offsetArrayZ; + public: + DEFINE_SIZE_ARRAY (8, offsetArrayZ); +}; + +struct IndexSubtableFormat1 : IndexSubtableFormat1Or3 {}; +struct IndexSubtableFormat3 : IndexSubtableFormat1Or3 {}; + +struct IndexSubtable +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const + { + TRACE_SANITIZE (this); + if (!u.header.sanitize (c)) return_trace (false); + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.sanitize (c, glyph_count)); + case 3: return_trace (u.format3.sanitize (c, glyph_count)); + default:return_trace (true); + } + } + + bool + finish_subtable (hb_serialize_context_t *c, + unsigned int cbdt_prime_len, + unsigned int num_glyphs, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + + unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset; + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.add_offset (c, local_offset, size)); + case 3: { + if (!u.format3.add_offset (c, local_offset, size)) + return_trace (false); + if (!(num_glyphs & 0x01)) // Pad to 32-bit alignment if needed. + return_trace (u.format3.add_offset (c, 0, size)); + return_trace (true); + } + // TODO: implement 2, 4, 5. + case 2: case 4: // No-op. + case 5: // Pad to 32-bit aligned. + default: return_trace (false); + } + } + + bool + fill_missing_glyphs (hb_serialize_context_t *c, + unsigned int cbdt_prime_len, + unsigned int num_missing, + unsigned int *size /* OUT (accumulated) */, + unsigned int *num_glyphs /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + + unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset; + switch (u.header.indexFormat) + { + case 1: { + for (unsigned int i = 0; i < num_missing; i++) + { + if (unlikely (!u.format1.add_offset (c, local_offset, size))) + return_trace (false); + *num_glyphs += 1; + } + return_trace (true); + } + case 3: { + for (unsigned int i = 0; i < num_missing; i++) + { + if (unlikely (!u.format3.add_offset (c, local_offset, size))) + return_trace (false); + *num_glyphs += 1; + } + return_trace (true); + } + // TODO: implement 2, 4, 5. + case 2: // Add empty space in cbdt_prime?. + case 4: case 5: // No-op as sparse is supported. + default: return_trace (false); + } + } + + bool + copy_glyph_at_idx (hb_serialize_context_t *c, unsigned int idx, + const char *cbdt, unsigned int cbdt_length, + hb_vector_t *cbdt_prime /* INOUT */, + IndexSubtable *subtable_prime /* INOUT */, + unsigned int *size /* OUT (accumulated) */) const + { + TRACE_SERIALIZE (this); + + unsigned int offset, length, format; + if (unlikely (!get_image_data (idx, &offset, &length, &format))) return_trace (false); + if (unlikely (offset > cbdt_length || cbdt_length - offset < length)) return_trace (false); + + auto *header_prime = subtable_prime->get_header (); + unsigned int new_local_offset = cbdt_prime->length - (unsigned int) header_prime->imageDataOffset; + if (unlikely (!_copy_data_to_cbdt (cbdt_prime, cbdt + offset, length))) return_trace (false); + + return_trace (subtable_prime->add_offset (c, new_local_offset, size)); + } + + bool + add_offset (hb_serialize_context_t *c, unsigned int local_offset, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.add_offset (c, local_offset, size)); + case 3: return_trace (u.format3.add_offset (c, local_offset, size)); + // TODO: Implement tables 2, 4, 5 + case 2: // Should be a no-op. + case 4: case 5: // Handle sparse cases. + default: return_trace (false); + } + } + + bool get_extents (hb_glyph_extents_t *extents HB_UNUSED, bool scale HB_UNUSED) const + { + switch (u.header.indexFormat) + { + case 2: case 5: /* TODO */ + case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */ + default:return (false); + } + } + + bool + get_image_data (unsigned int idx, unsigned int *offset, + unsigned int *length, unsigned int *format) const + { + *format = u.header.imageFormat; + switch (u.header.indexFormat) + { + case 1: return u.format1.get_image_data (idx, offset, length); + case 3: return u.format3.get_image_data (idx, offset, length); + default: return false; + } + } + + const IndexSubtableHeader* get_header () const { return &u.header; } + + void populate_header (unsigned index_format, + unsigned image_format, + unsigned int image_data_offset, + unsigned int *size) + { + u.header.indexFormat = index_format; + u.header.imageFormat = image_format; + u.header.imageDataOffset = image_data_offset; + switch (u.header.indexFormat) + { + case 1: *size += IndexSubtableFormat1::min_size; break; + case 3: *size += IndexSubtableFormat3::min_size; break; + } + } + + protected: + union { + IndexSubtableHeader header; + IndexSubtableFormat1 format1; + IndexSubtableFormat3 format3; + /* TODO: Format 2, 4, 5. */ + } u; + public: + DEFINE_SIZE_UNION (8, header); +}; + +struct IndexSubtableRecord +{ + /* XXX Remove this and fix by not inserting it into vector. */ + IndexSubtableRecord& operator = (const IndexSubtableRecord &o) + { + firstGlyphIndex = o.firstGlyphIndex; + lastGlyphIndex = o.lastGlyphIndex; + offsetToSubtable = (unsigned) o.offsetToSubtable; + assert (offsetToSubtable.is_null ()); + return *this; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + firstGlyphIndex <= lastGlyphIndex && + offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1)); + } + + const IndexSubtable* get_subtable (const void *base) const + { + return &(base+offsetToSubtable); + } + + bool add_new_subtable (hb_subset_context_t* c, + cblc_bitmap_size_subset_context_t *bitmap_size_context, + IndexSubtableRecord *record, + const hb_vector_t> *lookup, /* IN */ + const void *base, + unsigned int *start /* INOUT */) const + { + TRACE_SERIALIZE (this); + + auto *subtable = c->serializer->start_embed (); + if (unlikely (!subtable)) return_trace (false); + if (unlikely (!c->serializer->extend_min (subtable))) return_trace (false); + + auto *old_subtable = get_subtable (base); + auto *old_header = old_subtable->get_header (); + + subtable->populate_header (old_header->indexFormat, + old_header->imageFormat, + bitmap_size_context->cbdt_prime->length, + &bitmap_size_context->size); + + unsigned int num_glyphs = 0; + bool early_exit = false; + for (unsigned int i = *start; i < lookup->length; i++) + { + hb_codepoint_t new_gid = (*lookup)[i].first; + const IndexSubtableRecord *next_record = (*lookup)[i].second; + const IndexSubtable *next_subtable = next_record->get_subtable (base); + auto *next_header = next_subtable->get_header (); + if (next_header != old_header) + { + *start = i; + early_exit = true; + break; + } + unsigned int num_missing = record->add_glyph_for_subset (new_gid); + if (unlikely (!subtable->fill_missing_glyphs (c->serializer, + bitmap_size_context->cbdt_prime->length, + num_missing, + &bitmap_size_context->size, + &num_glyphs))) + return_trace (false); + + hb_codepoint_t old_gid = 0; + c->plan->old_gid_for_new_gid (new_gid, &old_gid); + if (old_gid < next_record->firstGlyphIndex) + return_trace (false); + + unsigned int old_idx = (unsigned int) old_gid - next_record->firstGlyphIndex; + if (unlikely (!next_subtable->copy_glyph_at_idx (c->serializer, + old_idx, + bitmap_size_context->cbdt, + bitmap_size_context->cbdt_length, + bitmap_size_context->cbdt_prime, + subtable, + &bitmap_size_context->size))) + return_trace (false); + num_glyphs += 1; + } + if (!early_exit) + *start = lookup->length; + if (unlikely (!subtable->finish_subtable (c->serializer, + bitmap_size_context->cbdt_prime->length, + num_glyphs, + &bitmap_size_context->size))) + return_trace (false); + return_trace (true); + } + + bool add_new_record (hb_subset_context_t *c, + cblc_bitmap_size_subset_context_t *bitmap_size_context, + const hb_vector_t> *lookup, /* IN */ + const void *base, + unsigned int *start, /* INOUT */ + hb_vector_t* records /* INOUT */) const + { + TRACE_SERIALIZE (this); + auto snap = c->serializer->snapshot (); + unsigned int old_size = bitmap_size_context->size; + unsigned int old_cbdt_prime_length = bitmap_size_context->cbdt_prime->length; + + // Set to invalid state to indicate filling glyphs is not yet started. + if (unlikely (!c->serializer->check_success (records->resize (records->length + 1)))) + return_trace (false); + + records->tail ().firstGlyphIndex = 1; + records->tail ().lastGlyphIndex = 0; + bitmap_size_context->size += IndexSubtableRecord::min_size; + + c->serializer->push (); + + if (unlikely (!add_new_subtable (c, bitmap_size_context, &(records->tail ()), lookup, base, start))) + { + c->serializer->pop_discard (); + c->serializer->revert (snap); + bitmap_size_context->cbdt_prime->shrink (old_cbdt_prime_length); + bitmap_size_context->size = old_size; + records->resize (records->length - 1); + return_trace (false); + } + + bitmap_size_context->num_tables += 1; + return_trace (true); + } + + unsigned int add_glyph_for_subset (hb_codepoint_t gid) + { + if (firstGlyphIndex > lastGlyphIndex) + { + firstGlyphIndex = gid; + lastGlyphIndex = gid; + return 0; + } + // TODO maybe assert? this shouldn't occur. + if (lastGlyphIndex > gid) + return 0; + unsigned int num_missing = (unsigned int) (gid - lastGlyphIndex - 1); + lastGlyphIndex = gid; + return num_missing; + } + + bool get_extents (hb_glyph_extents_t *extents, const void *base, bool scale) const + { return (base+offsetToSubtable).get_extents (extents, scale); } + + bool get_image_data (unsigned int gid, + const void *base, + unsigned int *offset, + unsigned int *length, + unsigned int *format) const + { + if (gid < firstGlyphIndex || gid > lastGlyphIndex) return false; + return (base+offsetToSubtable).get_image_data (gid - firstGlyphIndex, + offset, length, format); + } + + HBGlyphID16 firstGlyphIndex; + HBGlyphID16 lastGlyphIndex; + Offset32To offsetToSubtable; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct IndexSubtableArray +{ + friend struct CBDT; + + bool sanitize (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + return_trace (indexSubtablesZ.sanitize (c, count, this)); + } + + void + build_lookup (hb_subset_context_t *c, cblc_bitmap_size_subset_context_t *bitmap_size_context, + hb_vector_t> *lookup /* OUT */) const + { + bool start_glyph_is_set = false; + for (hb_codepoint_t new_gid = 0; new_gid < c->plan->num_output_glyphs (); new_gid++) + { + hb_codepoint_t old_gid; + if (unlikely (!c->plan->old_gid_for_new_gid (new_gid, &old_gid))) continue; + + const IndexSubtableRecord* record = find_table (old_gid, bitmap_size_context->num_tables); + if (unlikely (!record)) continue; + + // Don't add gaps to the lookup. The best way to determine if a glyph is a + // gap is that it has no image data. + unsigned int offset, length, format; + if (unlikely (!record->get_image_data (old_gid, this, &offset, &length, &format))) continue; + + lookup->push (hb_pair_t (new_gid, record)); + + if (!start_glyph_is_set) + { + bitmap_size_context->start_glyph = new_gid; + start_glyph_is_set = true; + } + + bitmap_size_context->end_glyph = new_gid; + } + } + + bool + subset (hb_subset_context_t *c, + cblc_bitmap_size_subset_context_t *bitmap_size_context) const + { + TRACE_SUBSET (this); + + auto *dst = c->serializer->start_embed (); + if (unlikely (!dst)) return_trace (false); + + hb_vector_t> lookup; + build_lookup (c, bitmap_size_context, &lookup); + if (unlikely (!c->serializer->propagate_error (lookup))) + return false; + + bitmap_size_context->size = 0; + bitmap_size_context->num_tables = 0; + hb_vector_t records; + for (unsigned int start = 0; start < lookup.length;) + { + if (unlikely (!lookup[start].second->add_new_record (c, bitmap_size_context, &lookup, this, &start, &records))) + { + // Discard any leftover pushes to the serializer from successful records. + for (unsigned int i = 0; i < records.length; i++) + c->serializer->pop_discard (); + return_trace (false); + } + } + + /* Workaround to ensure offset ordering is from least to greatest when + * resolving links. */ + hb_vector_t objidxs; + for (unsigned int i = 0; i < records.length; i++) + objidxs.push (c->serializer->pop_pack ()); + for (unsigned int i = 0; i < records.length; i++) + { + IndexSubtableRecord* record = c->serializer->embed (records[i]); + if (unlikely (!record)) return_trace (false); + c->serializer->add_link (record->offsetToSubtable, objidxs[records.length - 1 - i]); + } + return_trace (true); + } + + public: + const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const + { + for (unsigned int i = 0; i < numTables; ++i) + { + unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex; + unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex; + if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex) + return &indexSubtablesZ[i]; + } + return nullptr; + } + + protected: + UnsizedArrayOf indexSubtablesZ; +}; + +struct BitmapSizeTable +{ + friend struct CBLC; + friend struct CBDT; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) && + horizontal.sanitize (c) && + vertical.sanitize (c)); + } + + const IndexSubtableRecord * + find_table (hb_codepoint_t glyph, const void *base, const void **out_base) const + { + *out_base = &(base+indexSubtableArrayOffset); + return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables); + } + + bool + subset (hb_subset_context_t *c, const void *base, + const char *cbdt, unsigned int cbdt_length, + hb_vector_t *cbdt_prime /* INOUT */) const + { + TRACE_SUBSET (this); + auto *out_table = c->serializer->embed (this); + if (unlikely (!out_table)) return_trace (false); + + cblc_bitmap_size_subset_context_t bitmap_size_context; + bitmap_size_context.cbdt = cbdt; + bitmap_size_context.cbdt_length = cbdt_length; + bitmap_size_context.cbdt_prime = cbdt_prime; + bitmap_size_context.size = indexTablesSize; + bitmap_size_context.num_tables = numberOfIndexSubtables; + bitmap_size_context.start_glyph = 1; + bitmap_size_context.end_glyph = 0; + + if (!out_table->indexSubtableArrayOffset.serialize_subset (c, + indexSubtableArrayOffset, + base, + &bitmap_size_context)) + return_trace (false); + if (!bitmap_size_context.size || + !bitmap_size_context.num_tables || + bitmap_size_context.start_glyph > bitmap_size_context.end_glyph) + return_trace (false); + + out_table->indexTablesSize = bitmap_size_context.size; + out_table->numberOfIndexSubtables = bitmap_size_context.num_tables; + out_table->startGlyphIndex = bitmap_size_context.start_glyph; + out_table->endGlyphIndex = bitmap_size_context.end_glyph; + return_trace (true); + } + + protected: + NNOffset32To + indexSubtableArrayOffset; + HBUINT32 indexTablesSize; + HBUINT32 numberOfIndexSubtables; + HBUINT32 colorRef; + SBitLineMetrics horizontal; + SBitLineMetrics vertical; + HBGlyphID16 startGlyphIndex; + HBGlyphID16 endGlyphIndex; + HBUINT8 ppemX; + HBUINT8 ppemY; + HBUINT8 bitDepth; + HBINT8 flags; + public: + DEFINE_SIZE_STATIC (48); +}; + + +/* + * Glyph Bitmap Data Formats. + */ + +struct GlyphBitmapDataFormat17 +{ + SmallGlyphMetrics glyphMetrics; + Array32Of data; + public: + DEFINE_SIZE_ARRAY (9, data); +}; + +struct GlyphBitmapDataFormat18 +{ + BigGlyphMetrics glyphMetrics; + Array32Of data; + public: + DEFINE_SIZE_ARRAY (12, data); +}; + +struct GlyphBitmapDataFormat19 +{ + Array32Of data; + public: + DEFINE_SIZE_ARRAY (4, data); +}; + +struct CBLC +{ + friend struct CBDT; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_CBLC; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2 || version.major == 3) && + sizeTables.sanitize (c, this)); + } + + static bool + sink_cbdt (hb_subset_context_t *c, hb_vector_t* cbdt_prime) + { + hb_blob_t *cbdt_prime_blob = hb_blob_create (cbdt_prime->arrayZ, + cbdt_prime->length, + HB_MEMORY_MODE_WRITABLE, + cbdt_prime->arrayZ, + hb_free); + cbdt_prime->init (); // Leak arrayZ to the blob. + bool ret = c->plan->add_table (HB_OT_TAG_CBDT, cbdt_prime_blob); + hb_blob_destroy (cbdt_prime_blob); + return ret; + } + + bool + subset_size_table (hb_subset_context_t *c, const BitmapSizeTable& table, + const char *cbdt /* IN */, unsigned int cbdt_length, + CBLC *cblc_prime /* INOUT */, hb_vector_t *cbdt_prime /* INOUT */) const + { + TRACE_SUBSET (this); + cblc_prime->sizeTables.len++; + + auto snap = c->serializer->snapshot (); + auto cbdt_prime_len = cbdt_prime->length; + + if (!table.subset (c, this, cbdt, cbdt_length, cbdt_prime)) + { + cblc_prime->sizeTables.len--; + c->serializer->revert (snap); + cbdt_prime->shrink (cbdt_prime_len); + return_trace (false); + } + return_trace (true); + } + + // Implemented in cc file as it depends on definition of CBDT. + HB_INTERNAL bool subset (hb_subset_context_t *c) const; + + protected: + const BitmapSizeTable &choose_strike (hb_font_t *font) const + { + unsigned count = sizeTables.len; + if (unlikely (!count)) + return Null (BitmapSizeTable); + + unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem); + if (!requested_ppem) + requested_ppem = 1<<30; /* Choose largest strike. */ + unsigned int best_i = 0; + unsigned int best_ppem = hb_max (sizeTables[0].ppemX, sizeTables[0].ppemY); + + for (unsigned int i = 1; i < count; i++) + { + unsigned int ppem = hb_max (sizeTables[i].ppemX, sizeTables[i].ppemY); + if ((requested_ppem <= ppem && ppem < best_ppem) || + (requested_ppem > best_ppem && ppem > best_ppem)) + { + best_i = i; + best_ppem = ppem; + } + } + + return sizeTables[best_i]; + } + + protected: + FixedVersion<> version; + Array32Of sizeTables; + public: + DEFINE_SIZE_ARRAY (8, sizeTables); +}; + +struct CBDT +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_CBDT; + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + this->cblc = hb_sanitize_context_t ().reference_table (face); + this->cbdt = hb_sanitize_context_t ().reference_table (face); + + upem = hb_face_get_upem (face); + } + ~accelerator_t () + { + this->cblc.destroy (); + this->cbdt.destroy (); + } + + bool + get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents, bool scale = true) const + { + const void *base; + const BitmapSizeTable &strike = this->cblc->choose_strike (font); + const IndexSubtableRecord *subtable_record = strike.find_table (glyph, cblc, &base); + if (!subtable_record || !strike.ppemX || !strike.ppemY) + return false; + + if (subtable_record->get_extents (extents, base, scale)) + return true; + + unsigned int image_offset = 0, image_length = 0, image_format = 0; + if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format)) + return false; + + unsigned int cbdt_len = cbdt.get_length (); + if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + return false; + + switch (image_format) + { + case 17: { + if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) + return false; + auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); + glyphFormat17.glyphMetrics.get_extents (font, extents, scale); + break; + } + case 18: { + if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) + return false; + auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); + glyphFormat18.glyphMetrics.get_extents (font, extents, scale); + break; + } + default: return false; /* TODO: Support other image formats. */ + } + + /* Convert to font units. */ + if (scale) + { + float x_scale = upem / (float) strike.ppemX; + float y_scale = upem / (float) strike.ppemY; + extents->x_bearing = roundf (extents->x_bearing * x_scale); + extents->y_bearing = roundf (extents->y_bearing * y_scale); + extents->width = roundf (extents->width * x_scale); + extents->height = roundf (extents->height * y_scale); + } + + return true; + } + + hb_blob_t* + reference_png (hb_font_t *font, hb_codepoint_t glyph) const + { + const void *base; + const BitmapSizeTable &strike = this->cblc->choose_strike (font); + const IndexSubtableRecord *subtable_record = strike.find_table (glyph, cblc, &base); + if (!subtable_record || !strike.ppemX || !strike.ppemY) + return hb_blob_get_empty (); + + unsigned int image_offset = 0, image_length = 0, image_format = 0; + if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format)) + return hb_blob_get_empty (); + + unsigned int cbdt_len = cbdt.get_length (); + if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + return hb_blob_get_empty (); + + switch (image_format) + { + case 17: + { + if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat17::min_size, + glyphFormat17.data.len); + } + case 18: + { + if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat18::min_size, + glyphFormat18.data.len); + } + case 19: + { + if (unlikely (image_length < GlyphBitmapDataFormat19::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat19 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat19::min_size, + glyphFormat19.data.len); + } + default: return hb_blob_get_empty (); /* TODO: Support other image formats. */ + } + } + + bool has_data () const { return cbdt.get_length (); } + + bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const + { + hb_glyph_extents_t extents; + hb_glyph_extents_t pixel_extents; + hb_blob_t *blob = reference_png (font, glyph); + + if (unlikely (blob == hb_blob_get_empty ())) + return false; + + if (unlikely (!hb_font_get_glyph_extents (font, glyph, &extents))) + return false; + + if (unlikely (!get_extents (font, glyph, &pixel_extents, false))) + return false; + + bool ret = funcs->image (data, + blob, + pixel_extents.width, -pixel_extents.height, + HB_PAINT_IMAGE_FORMAT_PNG, + font->slant_xy, + &extents); + + hb_blob_destroy (blob); + return ret; + } + + private: + hb_blob_ptr_t cblc; + hb_blob_ptr_t cbdt; + + unsigned int upem; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2 || version.major == 3)); + } + + protected: + FixedVersion<> version; + UnsizedArrayOf dataZ; + public: + DEFINE_SIZE_ARRAY (4, dataZ); +}; + +inline bool +CBLC::subset (hb_subset_context_t *c) const +{ + TRACE_SUBSET (this); + + auto *cblc_prime = c->serializer->start_embed (); + + // Use a vector as a secondary buffer as the tables need to be built in parallel. + hb_vector_t cbdt_prime; + + if (unlikely (!cblc_prime)) return_trace (false); + if (unlikely (!c->serializer->extend_min (cblc_prime))) return_trace (false); + cblc_prime->version = version; + + hb_blob_t* cbdt_blob = hb_sanitize_context_t ().reference_table (c->plan->source); + unsigned int cbdt_length; + CBDT* cbdt = (CBDT *) hb_blob_get_data (cbdt_blob, &cbdt_length); + if (unlikely (cbdt_length < CBDT::min_size)) + { + hb_blob_destroy (cbdt_blob); + return_trace (false); + } + _copy_data_to_cbdt (&cbdt_prime, cbdt, CBDT::min_size); + + for (const BitmapSizeTable& table : + sizeTables.iter ()) + subset_size_table (c, table, (const char *) cbdt, cbdt_length, cblc_prime, &cbdt_prime); + + hb_blob_destroy (cbdt_blob); + + return_trace (CBLC::sink_cbdt (c, &cbdt_prime)); +} + +struct CBDT_accelerator_t : CBDT::accelerator_t { + CBDT_accelerator_t (hb_face_t *face) : CBDT::accelerator_t (face) {} +}; + + +} /* namespace OT */ + +#endif /* OT_COLOR_CBDT_CBDT_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/COLR.hh b/src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/COLR.hh new file mode 100644 index 00000000000..191812f48ee --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/COLR.hh @@ -0,0 +1,2436 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * Copyright ยฉ 2020 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Calder Kitagawa + */ + +#ifndef OT_COLOR_COLR_COLR_HH +#define OT_COLOR_COLR_COLR_HH + +#include "../../../hb.hh" +#include "../../../hb-open-type.hh" +#include "../../../hb-ot-var-common.hh" +#include "../../../hb-paint.hh" +#include "../../../hb-paint-extents.hh" + +/* + * COLR -- Color + * https://docs.microsoft.com/en-us/typography/opentype/spec/colr + */ +#define HB_OT_TAG_COLR HB_TAG('C','O','L','R') + +namespace OT { +struct hb_paint_context_t; +} + +namespace OT { + +struct COLR; + +struct Paint; + +struct hb_paint_context_t : + hb_dispatch_context_t +{ + template + return_t dispatch (const T &obj) { obj.paint_glyph (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + + const COLR* get_colr_table () const + { return reinterpret_cast (base); } + +public: + const void *base; + hb_paint_funcs_t *funcs; + void *data; + hb_font_t *font; + unsigned int palette_index; + hb_color_t foreground; + VarStoreInstancer &instancer; + int depth_left = HB_MAX_NESTING_LEVEL; + int edge_count = HB_COLRV1_MAX_EDGE_COUNT; + + hb_paint_context_t (const void *base_, + hb_paint_funcs_t *funcs_, + void *data_, + hb_font_t *font_, + unsigned int palette_, + hb_color_t foreground_, + VarStoreInstancer &instancer_) : + base (base_), + funcs (funcs_), + data (data_), + font (font_), + palette_index (palette_), + foreground (foreground_), + instancer (instancer_) + { } + + hb_color_t get_color (unsigned int color_index, float alpha, hb_bool_t *is_foreground) + { + hb_color_t color = foreground; + + *is_foreground = true; + + if (color_index != 0xffff) + { + if (!funcs->custom_palette_color (data, color_index, &color)) + { + unsigned int clen = 1; + hb_face_t *face = hb_font_get_face (font); + + hb_ot_color_palette_get_colors (face, palette_index, color_index, &clen, &color); + } + + *is_foreground = false; + } + + return HB_COLOR (hb_color_get_blue (color), + hb_color_get_green (color), + hb_color_get_red (color), + hb_color_get_alpha (color) * alpha); + } + + inline void recurse (const Paint &paint); +}; + +struct hb_colrv1_closure_context_t : + hb_dispatch_context_t +{ + template + return_t dispatch (const T &obj) + { + if (unlikely (nesting_level_left == 0)) + return hb_empty_t (); + + if (paint_visited (&obj)) + return hb_empty_t (); + + nesting_level_left--; + obj.closurev1 (this); + nesting_level_left++; + return hb_empty_t (); + } + static return_t default_return_value () { return hb_empty_t (); } + + bool paint_visited (const void *paint) + { + hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) paint - (uintptr_t) base); + if (visited_paint.in_error() || visited_paint.has (delta)) + return true; + + visited_paint.add (delta); + return false; + } + + const COLR* get_colr_table () const + { return reinterpret_cast (base); } + + void add_glyph (unsigned glyph_id) + { glyphs->add (glyph_id); } + + void add_layer_indices (unsigned first_layer_index, unsigned num_of_layers) + { layer_indices->add_range (first_layer_index, first_layer_index + num_of_layers - 1); } + + void add_palette_index (unsigned palette_index) + { palette_indices->add (palette_index); } + + public: + const void *base; + hb_set_t visited_paint; + hb_set_t *glyphs; + hb_set_t *layer_indices; + hb_set_t *palette_indices; + unsigned nesting_level_left; + + hb_colrv1_closure_context_t (const void *base_, + hb_set_t *glyphs_, + hb_set_t *layer_indices_, + hb_set_t *palette_indices_, + unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) : + base (base_), + glyphs (glyphs_), + layer_indices (layer_indices_), + palette_indices (palette_indices_), + nesting_level_left (nesting_level_left_) + {} +}; + +struct LayerRecord +{ + operator hb_ot_color_layer_t () const { return {glyphId, colorIdx}; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBGlyphID16 glyphId; /* Glyph ID of layer glyph */ + Index colorIdx; /* Index value to use with a + * selected color palette. + * An index value of 0xFFFF + * is a special case indicating + * that the text foreground + * color (defined by a + * higher-level client) should + * be used and shall not be + * treated as actual index + * into CPAL ColorRecord array. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BaseGlyphRecord +{ + int cmp (hb_codepoint_t g) const + { return g < glyphId ? -1 : g > glyphId ? 1 : 0; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBGlyphID16 glyphId; /* Glyph ID of reference glyph */ + HBUINT16 firstLayerIdx; /* Index (from beginning of + * the Layer Records) to the + * layer record. There will be + * numLayers consecutive entries + * for this base glyph. */ + HBUINT16 numLayers; /* Number of color layers + * associated with this glyph */ + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct Variable +{ + static constexpr bool is_variable = true; + + Variable* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + + void closurev1 (hb_colrv1_closure_context_t* c) const + { value.closurev1 (c); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + if (!value.subset (c, instancer, varIdxBase)) return_trace (false); + if (c->plan->all_axes_pinned) + return_trace (true); + + //TODO: update varIdxBase for partial-instancing + return_trace (c->serializer->embed (varIdxBase)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + + void paint_glyph (hb_paint_context_t *c) const + { + value.paint_glyph (c, varIdxBase); + } + + void get_color_stop (hb_paint_context_t *c, + hb_color_stop_t *stop, + const VarStoreInstancer &instancer) const + { + value.get_color_stop (c, stop, varIdxBase, instancer); + } + + hb_paint_extend_t get_extend () const + { + return value.get_extend (); + } + + protected: + T value; + public: + VarIdx varIdxBase; + public: + DEFINE_SIZE_STATIC (4 + T::static_size); +}; + +template +struct NoVariable +{ + static constexpr bool is_variable = false; + + static constexpr uint32_t varIdxBase = VarIdx::NO_VARIATION; + + NoVariable* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + + void closurev1 (hb_colrv1_closure_context_t* c) const + { value.closurev1 (c); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + return_trace (value.subset (c, instancer, varIdxBase)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + + void paint_glyph (hb_paint_context_t *c) const + { + value.paint_glyph (c, varIdxBase); + } + + void get_color_stop (hb_paint_context_t *c, + hb_color_stop_t *stop, + const VarStoreInstancer &instancer) const + { + value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer); + } + + hb_paint_extend_t get_extend () const + { + return value.get_extend (); + } + + T value; + public: + DEFINE_SIZE_STATIC (T::static_size); +}; + +// Color structures + +struct ColorStop +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { c->add_palette_index (paletteIndex); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->stopOffset.set_float (stopOffset.to_float(instancer (varIdxBase, 0))); + out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 1))); + } + + return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_color_stop (hb_paint_context_t *c, + hb_color_stop_t *out, + uint32_t varIdx, + const VarStoreInstancer &instancer) const + { + out->offset = stopOffset.to_float(instancer (varIdx, 0)); + out->color = c->get_color (paletteIndex, + alpha.to_float (instancer (varIdx, 1)), + &out->is_foreground); + } + + F2DOT14 stopOffset; + HBUINT16 paletteIndex; + F2DOT14 alpha; + public: + DEFINE_SIZE_STATIC (2 + 2 * F2DOT14::static_size); +}; + +struct Extend : HBUINT8 +{ + enum { + EXTEND_PAD = 0, + EXTEND_REPEAT = 1, + EXTEND_REFLECT = 2, + }; + public: + DEFINE_SIZE_STATIC (1); +}; + +template class Var> +struct ColorLine +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { + for (const auto &stop : stops.iter ()) + stop.closurev1 (c); + } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + if (!c->serializer->check_assign (out->extend, extend, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); + if (!c->serializer->check_assign (out->stops.len, stops.len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW)) return_trace (false); + + for (const auto& stop : stops.iter ()) + { + if (!stop.subset (c, instancer)) return_trace (false); + } + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + stops.sanitize (c)); + } + + /* get up to count stops from start */ + unsigned int + get_color_stops (hb_paint_context_t *c, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops, + const VarStoreInstancer &instancer) const + { + unsigned int len = stops.len; + + if (count && color_stops) + { + unsigned int i; + for (i = 0; i < *count && start + i < len; i++) + stops[start + i].get_color_stop (c, &color_stops[i], instancer); + *count = i; + } + + return len; + } + + HB_INTERNAL static unsigned int static_get_color_stops (hb_color_line_t *color_line, + void *color_line_data, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops, + void *user_data) + { + const ColorLine *thiz = (const ColorLine *) color_line_data; + hb_paint_context_t *c = (hb_paint_context_t *) user_data; + return thiz->get_color_stops (c, start, count, color_stops, c->instancer); + } + + hb_paint_extend_t get_extend () const + { + return (hb_paint_extend_t) (unsigned int) extend; + } + + HB_INTERNAL static hb_paint_extend_t static_get_extend (hb_color_line_t *color_line, + void *color_line_data, + void *user_data) + { + const ColorLine *thiz = (const ColorLine *) color_line_data; + return thiz->get_extend (); + } + + Extend extend; + Array16Of> stops; + public: + DEFINE_SIZE_ARRAY_SIZED (3, stops); +}; + +// Composition modes + +// Compositing modes are taken from https://www.w3.org/TR/compositing-1/ +// NOTE: a brief audit of major implementations suggests most support most +// or all of the specified modes. +struct CompositeMode : HBUINT8 +{ + enum { + // Porter-Duff modes + // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators + COMPOSITE_CLEAR = 0, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_clear + COMPOSITE_SRC = 1, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_src + COMPOSITE_DEST = 2, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dst + COMPOSITE_SRC_OVER = 3, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcover + COMPOSITE_DEST_OVER = 4, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstover + COMPOSITE_SRC_IN = 5, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcin + COMPOSITE_DEST_IN = 6, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstin + COMPOSITE_SRC_OUT = 7, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcout + COMPOSITE_DEST_OUT = 8, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstout + COMPOSITE_SRC_ATOP = 9, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcatop + COMPOSITE_DEST_ATOP = 10, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstatop + COMPOSITE_XOR = 11, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_xor + COMPOSITE_PLUS = 12, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_plus + + // Blend modes + // https://www.w3.org/TR/compositing-1/#blending + COMPOSITE_SCREEN = 13, // https://www.w3.org/TR/compositing-1/#blendingscreen + COMPOSITE_OVERLAY = 14, // https://www.w3.org/TR/compositing-1/#blendingoverlay + COMPOSITE_DARKEN = 15, // https://www.w3.org/TR/compositing-1/#blendingdarken + COMPOSITE_LIGHTEN = 16, // https://www.w3.org/TR/compositing-1/#blendinglighten + COMPOSITE_COLOR_DODGE = 17, // https://www.w3.org/TR/compositing-1/#blendingcolordodge + COMPOSITE_COLOR_BURN = 18, // https://www.w3.org/TR/compositing-1/#blendingcolorburn + COMPOSITE_HARD_LIGHT = 19, // https://www.w3.org/TR/compositing-1/#blendinghardlight + COMPOSITE_SOFT_LIGHT = 20, // https://www.w3.org/TR/compositing-1/#blendingsoftlight + COMPOSITE_DIFFERENCE = 21, // https://www.w3.org/TR/compositing-1/#blendingdifference + COMPOSITE_EXCLUSION = 22, // https://www.w3.org/TR/compositing-1/#blendingexclusion + COMPOSITE_MULTIPLY = 23, // https://www.w3.org/TR/compositing-1/#blendingmultiply + + // Modes that, uniquely, do not operate on components + // https://www.w3.org/TR/compositing-1/#blendingnonseparable + COMPOSITE_HSL_HUE = 24, // https://www.w3.org/TR/compositing-1/#blendinghue + COMPOSITE_HSL_SATURATION = 25, // https://www.w3.org/TR/compositing-1/#blendingsaturation + COMPOSITE_HSL_COLOR = 26, // https://www.w3.org/TR/compositing-1/#blendingcolor + COMPOSITE_HSL_LUMINOSITY = 27, // https://www.w3.org/TR/compositing-1/#blendingluminosity + }; + public: + DEFINE_SIZE_STATIC (1); +}; + +struct Affine2x3 +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->xx.set_float (xx.to_float(instancer (varIdxBase, 0))); + out->yx.set_float (yx.to_float(instancer (varIdxBase, 1))); + out->xy.set_float (xy.to_float(instancer (varIdxBase, 2))); + out->yy.set_float (yy.to_float(instancer (varIdxBase, 3))); + out->dx.set_float (dx.to_float(instancer (varIdxBase, 4))); + out->dy.set_float (dy.to_float(instancer (varIdxBase, 5))); + } + return_trace (true); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + c->funcs->push_transform (c->data, + xx.to_float (c->instancer (varIdxBase, 0)), + yx.to_float (c->instancer (varIdxBase, 1)), + xy.to_float (c->instancer (varIdxBase, 2)), + yy.to_float (c->instancer (varIdxBase, 3)), + dx.to_float (c->instancer (varIdxBase, 4)), + dy.to_float (c->instancer (varIdxBase, 5))); + } + + F16DOT16 xx; + F16DOT16 yx; + F16DOT16 xy; + F16DOT16 yy; + F16DOT16 dx; + F16DOT16 dy; + public: + DEFINE_SIZE_STATIC (6 * F16DOT16::static_size); +}; + +struct PaintColrLayers +{ + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer HB_UNUSED) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (c->serializer->check_assign (out->firstLayerIndex, c->plan->colrv1_layers.get (firstLayerIndex), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + inline void paint_glyph (hb_paint_context_t *c) const; + + HBUINT8 format; /* format = 1 */ + HBUINT8 numLayers; + HBUINT32 firstLayerIndex; /* index into COLRv1::layerList */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct PaintSolid +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { c->add_palette_index (paletteIndex); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 0))); + + if (format == 3 && c->plan->all_axes_pinned) + out->format = 2; + + return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + hb_bool_t is_foreground; + hb_color_t color; + + color = c->get_color (paletteIndex, + alpha.to_float (c->instancer (varIdxBase, 0)), + &is_foreground); + c->funcs->color (c->data, is_foreground, color); + } + + HBUINT8 format; /* format = 2(noVar) or 3(Var)*/ + HBUINT16 paletteIndex; + F2DOT14 alpha; + public: + DEFINE_SIZE_STATIC (3 + F2DOT14::static_size); +}; + +template class Var> +struct PaintLinearGradient +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { (this+colorLine).closurev1 (c); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0)); + out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1)); + out->x1 = x1 + (int) roundf (instancer (varIdxBase, 2)); + out->y1 = y1 + (int) roundf (instancer (varIdxBase, 3)); + out->x2 = x2 + (int) roundf (instancer (varIdxBase, 4)); + out->y2 = y2 + (int) roundf (instancer (varIdxBase, 5)); + } + + if (format == 5 && c->plan->all_axes_pinned) + out->format = 4; + + return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + hb_color_line_t cl = { + (void *) &(this+colorLine), + (this+colorLine).static_get_color_stops, c, + (this+colorLine).static_get_extend, nullptr + }; + + c->funcs->linear_gradient (c->data, &cl, + x0 + c->instancer (varIdxBase, 0), + y0 + c->instancer (varIdxBase, 1), + x1 + c->instancer (varIdxBase, 2), + y1 + c->instancer (varIdxBase, 3), + x2 + c->instancer (varIdxBase, 4), + y2 + c->instancer (varIdxBase, 5)); + } + + HBUINT8 format; /* format = 4(noVar) or 5 (Var) */ + Offset24To> colorLine; /* Offset (from beginning of PaintLinearGradient + * table) to ColorLine subtable. */ + FWORD x0; + FWORD y0; + FWORD x1; + FWORD y1; + FWORD x2; + FWORD y2; + public: + DEFINE_SIZE_STATIC (4 + 6 * FWORD::static_size); +}; + +template class Var> +struct PaintRadialGradient +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { (this+colorLine).closurev1 (c); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0)); + out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1)); + out->radius0 = radius0 + (unsigned) roundf (instancer (varIdxBase, 2)); + out->x1 = x1 + (int) roundf (instancer (varIdxBase, 3)); + out->y1 = y1 + (int) roundf (instancer (varIdxBase, 4)); + out->radius1 = radius1 + (unsigned) roundf (instancer (varIdxBase, 5)); + } + + if (format == 7 && c->plan->all_axes_pinned) + out->format = 6; + + return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + hb_color_line_t cl = { + (void *) &(this+colorLine), + (this+colorLine).static_get_color_stops, c, + (this+colorLine).static_get_extend, nullptr + }; + + c->funcs->radial_gradient (c->data, &cl, + x0 + c->instancer (varIdxBase, 0), + y0 + c->instancer (varIdxBase, 1), + radius0 + c->instancer (varIdxBase, 2), + x1 + c->instancer (varIdxBase, 3), + y1 + c->instancer (varIdxBase, 4), + radius1 + c->instancer (varIdxBase, 5)); + } + + HBUINT8 format; /* format = 6(noVar) or 7 (Var) */ + Offset24To> colorLine; /* Offset (from beginning of PaintRadialGradient + * table) to ColorLine subtable. */ + FWORD x0; + FWORD y0; + UFWORD radius0; + FWORD x1; + FWORD y1; + UFWORD radius1; + public: + DEFINE_SIZE_STATIC (4 + 6 * FWORD::static_size); +}; + +template class Var> +struct PaintSweepGradient +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { (this+colorLine).closurev1 (c); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->centerX = centerX + (int) roundf (instancer (varIdxBase, 0)); + out->centerY = centerY + (int) roundf (instancer (varIdxBase, 1)); + out->startAngle.set_float (startAngle.to_float (instancer (varIdxBase, 2))); + out->endAngle.set_float (endAngle.to_float (instancer (varIdxBase, 3))); + } + + if (format == 9 && c->plan->all_axes_pinned) + out->format = 8; + + return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + hb_color_line_t cl = { + (void *) &(this+colorLine), + (this+colorLine).static_get_color_stops, c, + (this+colorLine).static_get_extend, nullptr + }; + + c->funcs->sweep_gradient (c->data, &cl, + centerX + c->instancer (varIdxBase, 0), + centerY + c->instancer (varIdxBase, 1), + (startAngle.to_float (c->instancer (varIdxBase, 2)) + 1) * HB_PI, + (endAngle.to_float (c->instancer (varIdxBase, 3)) + 1) * HB_PI); + } + + HBUINT8 format; /* format = 8(noVar) or 9 (Var) */ + Offset24To> colorLine; /* Offset (from beginning of PaintSweepGradient + * table) to ColorLine subtable. */ + FWORD centerX; + FWORD centerY; + F2DOT14 startAngle; + F2DOT14 endAngle; + public: + DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size + 2 * F2DOT14::static_size); +}; + +// Paint a non-COLR glyph, filled as indicated by paint. +struct PaintGlyph +{ + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (! c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid), + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + return_trace (out->paint.serialize_subset (c, paint, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && paint.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c) const + { + c->funcs->push_inverse_root_transform (c->data, c->font); + c->funcs->push_clip_glyph (c->data, gid, c->font); + c->funcs->push_root_transform (c->data, c->font); + c->recurse (this+paint); + c->funcs->pop_transform (c->data); + c->funcs->pop_clip (c->data); + c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 10 */ + Offset24To paint; /* Offset (from beginning of PaintGlyph table) to Paint subtable. */ + HBUINT16 gid; + public: + DEFINE_SIZE_STATIC (6); +}; + +struct PaintColrGlyph +{ + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer HB_UNUSED) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + inline void paint_glyph (hb_paint_context_t *c) const; + + HBUINT8 format; /* format = 11 */ + HBUINT16 gid; + public: + DEFINE_SIZE_STATIC (3); +}; + +template class Var> +struct PaintTransform +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + if (!out->transform.serialize_subset (c, transform, this, instancer)) return_trace (false); + if (format == 13 && c->plan->all_axes_pinned) + out->format = 12; + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + src.sanitize (c, this) && + transform.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c) const + { + (this+transform).paint_glyph (c); + c->recurse (this+src); + c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 12(noVar) or 13 (Var) */ + Offset24To src; /* Offset (from beginning of PaintTransform table) to Paint subtable. */ + Offset24To> transform; + public: + DEFINE_SIZE_STATIC (7); +}; + +struct PaintTranslate +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->dx = dx + (int) roundf (instancer (varIdxBase, 0)); + out->dy = dy + (int) roundf (instancer (varIdxBase, 1)); + } + + if (format == 15 && c->plan->all_axes_pinned) + out->format = 14; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float ddx = dx + c->instancer (varIdxBase, 0); + float ddy = dy + c->instancer (varIdxBase, 1); + + bool p1 = c->funcs->push_translate (c->data, ddx, ddy); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 14(noVar) or 15 (Var) */ + Offset24To src; /* Offset (from beginning of PaintTranslate table) to Paint subtable. */ + FWORD dx; + FWORD dy; + public: + DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size); +}; + +struct PaintScale +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0))); + out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1))); + } + + if (format == 17 && c->plan->all_axes_pinned) + out->format = 16; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float sx = scaleX.to_float (c->instancer (varIdxBase, 0)); + float sy = scaleY.to_float (c->instancer (varIdxBase, 1)); + + bool p1 = c->funcs->push_scale (c->data, sx, sy); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 16 (noVar) or 17(Var) */ + Offset24To src; /* Offset (from beginning of PaintScale table) to Paint subtable. */ + F2DOT14 scaleX; + F2DOT14 scaleY; + public: + DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size); +}; + +struct PaintScaleAroundCenter +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0))); + out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1))); + out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2)); + out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3)); + } + + if (format == 19 && c->plan->all_axes_pinned) + out->format = 18; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float sx = scaleX.to_float (c->instancer (varIdxBase, 0)); + float sy = scaleY.to_float (c->instancer (varIdxBase, 1)); + float tCenterX = centerX + c->instancer (varIdxBase, 2); + float tCenterY = centerY + c->instancer (varIdxBase, 3); + + bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); + bool p2 = c->funcs->push_scale (c->data, sx, sy); + bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->recurse (this+src); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 18 (noVar) or 19(Var) */ + Offset24To src; /* Offset (from beginning of PaintScaleAroundCenter table) to Paint subtable. */ + F2DOT14 scaleX; + F2DOT14 scaleY; + FWORD centerX; + FWORD centerY; + public: + DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size + 2 * FWORD::static_size); +}; + +struct PaintScaleUniform +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + out->scale.set_float (scale.to_float (instancer (varIdxBase, 0))); + + if (format == 21 && c->plan->all_axes_pinned) + out->format = 20; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float s = scale.to_float (c->instancer (varIdxBase, 0)); + + bool p1 = c->funcs->push_scale (c->data, s, s); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 20 (noVar) or 21(Var) */ + Offset24To src; /* Offset (from beginning of PaintScaleUniform table) to Paint subtable. */ + F2DOT14 scale; + public: + DEFINE_SIZE_STATIC (4 + F2DOT14::static_size); +}; + +struct PaintScaleUniformAroundCenter +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->scale.set_float (scale.to_float (instancer (varIdxBase, 0))); + out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1)); + out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2)); + } + + if (format == 23 && c->plan->all_axes_pinned) + out->format = 22; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float s = scale.to_float (c->instancer (varIdxBase, 0)); + float tCenterX = centerX + c->instancer (varIdxBase, 1); + float tCenterY = centerY + c->instancer (varIdxBase, 2); + + bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); + bool p2 = c->funcs->push_scale (c->data, s, s); + bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->recurse (this+src); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 22 (noVar) or 23(Var) */ + Offset24To src; /* Offset (from beginning of PaintScaleUniformAroundCenter table) to Paint subtable. */ + F2DOT14 scale; + FWORD centerX; + FWORD centerY; + public: + DEFINE_SIZE_STATIC (4 + F2DOT14::static_size + 2 * FWORD::static_size); +}; + +struct PaintRotate +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + out->angle.set_float (angle.to_float (instancer (varIdxBase, 0))); + + if (format == 25 && c->plan->all_axes_pinned) + out->format = 24; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float a = angle.to_float (c->instancer (varIdxBase, 0)); + + bool p1 = c->funcs->push_rotate (c->data, a); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 24 (noVar) or 25(Var) */ + Offset24To src; /* Offset (from beginning of PaintRotate table) to Paint subtable. */ + F2DOT14 angle; + public: + DEFINE_SIZE_STATIC (4 + F2DOT14::static_size); +}; + +struct PaintRotateAroundCenter +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->angle.set_float (angle.to_float (instancer (varIdxBase, 0))); + out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1)); + out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2)); + } + + if (format ==27 && c->plan->all_axes_pinned) + out->format = 26; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float a = angle.to_float (c->instancer (varIdxBase, 0)); + float tCenterX = centerX + c->instancer (varIdxBase, 1); + float tCenterY = centerY + c->instancer (varIdxBase, 2); + + bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); + bool p2 = c->funcs->push_rotate (c->data, a); + bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->recurse (this+src); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 26 (noVar) or 27(Var) */ + Offset24To src; /* Offset (from beginning of PaintRotateAroundCenter table) to Paint subtable. */ + F2DOT14 angle; + FWORD centerX; + FWORD centerY; + public: + DEFINE_SIZE_STATIC (4 + F2DOT14::static_size + 2 * FWORD::static_size); +}; + +struct PaintSkew +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0))); + out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1))); + } + + if (format == 29 && c->plan->all_axes_pinned) + out->format = 28; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0)); + float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1)); + + bool p1 = c->funcs->push_skew (c->data, sx, sy); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 28(noVar) or 29 (Var) */ + Offset24To src; /* Offset (from beginning of PaintSkew table) to Paint subtable. */ + F2DOT14 xSkewAngle; + F2DOT14 ySkewAngle; + public: + DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size); +}; + +struct PaintSkewAroundCenter +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0))); + out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1))); + out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2)); + out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3)); + } + + if (format == 31 && c->plan->all_axes_pinned) + out->format = 30; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0)); + float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1)); + float tCenterX = centerX + c->instancer (varIdxBase, 2); + float tCenterY = centerY + c->instancer (varIdxBase, 3); + + bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); + bool p2 = c->funcs->push_skew (c->data, sx, sy); + bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->recurse (this+src); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 30(noVar) or 31 (Var) */ + Offset24To src; /* Offset (from beginning of PaintSkewAroundCenter table) to Paint subtable. */ + F2DOT14 xSkewAngle; + F2DOT14 ySkewAngle; + FWORD centerX; + FWORD centerY; + public: + DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size + 2 * FWORD::static_size); +}; + +struct PaintComposite +{ + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (!out->src.serialize_subset (c, src, this, instancer)) return_trace (false); + return_trace (out->backdrop.serialize_subset (c, backdrop, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + src.sanitize (c, this) && + backdrop.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c) const + { + c->recurse (this+backdrop); + c->funcs->push_group (c->data); + c->recurse (this+src); + c->funcs->pop_group (c->data, (hb_paint_composite_mode_t) (int) mode); + } + + HBUINT8 format; /* format = 32 */ + Offset24To src; /* Offset (from beginning of PaintComposite table) to source Paint subtable. */ + CompositeMode mode; /* If mode is unrecognized use COMPOSITE_CLEAR */ + Offset24To backdrop; /* Offset (from beginning of PaintComposite table) to backdrop Paint subtable. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct ClipBoxData +{ + int xMin, yMin, xMax, yMax; +}; + +struct ClipBoxFormat1 +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer HB_UNUSED) const + { + clip_box.xMin = xMin; + clip_box.yMin = yMin; + clip_box.xMax = xMax; + clip_box.yMax = yMax; + } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->xMin = xMin + (int) roundf (instancer (varIdxBase, 0)); + out->yMin = yMin + (int) roundf (instancer (varIdxBase, 1)); + out->xMax = xMax + (int) roundf (instancer (varIdxBase, 2)); + out->yMax = yMax + (int) roundf (instancer (varIdxBase, 3)); + } + + if (format == 2 && c->plan->all_axes_pinned) + out->format = 1; + + return_trace (true); + } + + public: + HBUINT8 format; /* format = 1(noVar) or 2(Var)*/ + FWORD xMin; + FWORD yMin; + FWORD xMax; + FWORD yMax; + public: + DEFINE_SIZE_STATIC (1 + 4 * FWORD::static_size); +}; + +struct ClipBoxFormat2 : Variable +{ + void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer) const + { + value.get_clip_box(clip_box, instancer); + if (instancer) + { + clip_box.xMin += _hb_roundf (instancer (varIdxBase, 0)); + clip_box.yMin += _hb_roundf (instancer (varIdxBase, 1)); + clip_box.xMax += _hb_roundf (instancer (varIdxBase, 2)); + clip_box.yMax += _hb_roundf (instancer (varIdxBase, 3)); + } + } +}; + +struct ClipBox +{ + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + switch (u.format) { + case 1: return_trace (u.format1.subset (c, instancer, VarIdx::NO_VARIATION)); + case 2: return_trace (u.format2.subset (c, instancer)); + default:return_trace (c->default_return_value ()); + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + bool get_extents (hb_glyph_extents_t *extents, + const VarStoreInstancer &instancer) const + { + ClipBoxData clip_box; + switch (u.format) { + case 1: + u.format1.get_clip_box (clip_box, instancer); + break; + case 2: + u.format2.get_clip_box (clip_box, instancer); + break; + default: + return false; + } + + extents->x_bearing = clip_box.xMin; + extents->y_bearing = clip_box.yMax; + extents->width = clip_box.xMax - clip_box.xMin; + extents->height = clip_box.yMin - clip_box.yMax; + return true; + } + + protected: + union { + HBUINT8 format; /* Format identifier */ + ClipBoxFormat1 format1; + ClipBoxFormat2 format2; + } u; +}; + +struct ClipRecord +{ + int cmp (hb_codepoint_t g) const + { return g < startGlyphID ? -1 : g <= endGlyphID ? 0 : +1; } + + bool subset (hb_subset_context_t *c, + const void *base, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->clipBox.serialize_subset (c, clipBox, base, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && clipBox.sanitize (c, base)); + } + + bool get_extents (hb_glyph_extents_t *extents, + const void *base, + const VarStoreInstancer &instancer) const + { + return (base+clipBox).get_extents (extents, instancer); + } + + public: + HBUINT16 startGlyphID; // first gid clip applies to + HBUINT16 endGlyphID; // last gid clip applies to, inclusive + Offset24To clipBox; // Box or VarBox + public: + DEFINE_SIZE_STATIC (7); +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord); + +struct ClipList +{ + unsigned serialize_clip_records (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + const hb_set_t& gids, + const hb_map_t& gid_offset_map) const + { + TRACE_SERIALIZE (this); + if (gids.is_empty () || + gid_offset_map.get_population () != gids.get_population ()) + return_trace (0); + + unsigned count = 0; + + hb_codepoint_t start_gid= gids.get_min (); + hb_codepoint_t prev_gid = start_gid; + + unsigned offset = gid_offset_map.get (start_gid); + unsigned prev_offset = offset; + for (const hb_codepoint_t _ : gids.iter ()) + { + if (_ == start_gid) continue; + + offset = gid_offset_map.get (_); + if (_ == prev_gid + 1 && offset == prev_offset) + { + prev_gid = _; + continue; + } + + ClipRecord record; + record.startGlyphID = start_gid; + record.endGlyphID = prev_gid; + record.clipBox = prev_offset; + + if (!record.subset (c, this, instancer)) return_trace (0); + count++; + + start_gid = _; + prev_gid = _; + prev_offset = offset; + } + + //last one + { + ClipRecord record; + record.startGlyphID = start_gid; + record.endGlyphID = prev_gid; + record.clipBox = prev_offset; + if (!record.subset (c, this, instancer)) return_trace (0); + count++; + } + return_trace (count); + } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + if (!c->serializer->check_assign (out->format, format, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); + + const hb_set_t& glyphset = c->plan->_glyphset_colred; + const hb_map_t &glyph_map = *c->plan->glyph_map; + + hb_map_t new_gid_offset_map; + hb_set_t new_gids; + for (const ClipRecord& record : clips.iter ()) + { + unsigned start_gid = record.startGlyphID; + unsigned end_gid = record.endGlyphID; + for (unsigned gid = start_gid; gid <= end_gid; gid++) + { + if (!glyphset.has (gid) || !glyph_map.has (gid)) continue; + unsigned new_gid = glyph_map.get (gid); + new_gid_offset_map.set (new_gid, record.clipBox); + new_gids.add (new_gid); + } + } + + unsigned count = serialize_clip_records (c, instancer, new_gids, new_gid_offset_map); + if (!count) return_trace (false); + return_trace (c->serializer->check_assign (out->clips.len, count, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + // TODO Make a formatted struct! + return_trace (c->check_struct (this) && clips.sanitize (c, this)); + } + + bool + get_extents (hb_codepoint_t gid, + hb_glyph_extents_t *extents, + const VarStoreInstancer &instancer) const + { + auto *rec = clips.as_array ().bsearch (gid); + if (rec) + { + rec->get_extents (extents, this, instancer); + return true; + } + return false; + } + + HBUINT8 format; // Set to 1. + SortedArray32Of clips; // Clip records, sorted by startGlyphID + public: + DEFINE_SIZE_ARRAY_SIZED (5, clips); +}; + +struct Paint +{ + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + + if (unlikely (!c->check_start_recursion (HB_MAX_NESTING_LEVEL))) + return_trace (c->no_dispatch_return_value ()); + + return_trace (c->end_recursion (this->dispatch (c, std::forward (ds)...))); + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.paintformat1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.paintformat2, std::forward (ds)...)); + case 3: return_trace (c->dispatch (u.paintformat3, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.paintformat4, std::forward (ds)...)); + case 5: return_trace (c->dispatch (u.paintformat5, std::forward (ds)...)); + case 6: return_trace (c->dispatch (u.paintformat6, std::forward (ds)...)); + case 7: return_trace (c->dispatch (u.paintformat7, std::forward (ds)...)); + case 8: return_trace (c->dispatch (u.paintformat8, std::forward (ds)...)); + case 9: return_trace (c->dispatch (u.paintformat9, std::forward (ds)...)); + case 10: return_trace (c->dispatch (u.paintformat10, std::forward (ds)...)); + case 11: return_trace (c->dispatch (u.paintformat11, std::forward (ds)...)); + case 12: return_trace (c->dispatch (u.paintformat12, std::forward (ds)...)); + case 13: return_trace (c->dispatch (u.paintformat13, std::forward (ds)...)); + case 14: return_trace (c->dispatch (u.paintformat14, std::forward (ds)...)); + case 15: return_trace (c->dispatch (u.paintformat15, std::forward (ds)...)); + case 16: return_trace (c->dispatch (u.paintformat16, std::forward (ds)...)); + case 17: return_trace (c->dispatch (u.paintformat17, std::forward (ds)...)); + case 18: return_trace (c->dispatch (u.paintformat18, std::forward (ds)...)); + case 19: return_trace (c->dispatch (u.paintformat19, std::forward (ds)...)); + case 20: return_trace (c->dispatch (u.paintformat20, std::forward (ds)...)); + case 21: return_trace (c->dispatch (u.paintformat21, std::forward (ds)...)); + case 22: return_trace (c->dispatch (u.paintformat22, std::forward (ds)...)); + case 23: return_trace (c->dispatch (u.paintformat23, std::forward (ds)...)); + case 24: return_trace (c->dispatch (u.paintformat24, std::forward (ds)...)); + case 25: return_trace (c->dispatch (u.paintformat25, std::forward (ds)...)); + case 26: return_trace (c->dispatch (u.paintformat26, std::forward (ds)...)); + case 27: return_trace (c->dispatch (u.paintformat27, std::forward (ds)...)); + case 28: return_trace (c->dispatch (u.paintformat28, std::forward (ds)...)); + case 29: return_trace (c->dispatch (u.paintformat29, std::forward (ds)...)); + case 30: return_trace (c->dispatch (u.paintformat30, std::forward (ds)...)); + case 31: return_trace (c->dispatch (u.paintformat31, std::forward (ds)...)); + case 32: return_trace (c->dispatch (u.paintformat32, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + HBUINT8 format; + PaintColrLayers paintformat1; + NoVariable paintformat2; + Variable paintformat3; + NoVariable> paintformat4; + Variable> paintformat5; + NoVariable> paintformat6; + Variable> paintformat7; + NoVariable> paintformat8; + Variable> paintformat9; + PaintGlyph paintformat10; + PaintColrGlyph paintformat11; + PaintTransform paintformat12; + PaintTransform paintformat13; + NoVariable paintformat14; + Variable paintformat15; + NoVariable paintformat16; + Variable paintformat17; + NoVariable paintformat18; + Variable paintformat19; + NoVariable paintformat20; + Variable paintformat21; + NoVariable paintformat22; + Variable paintformat23; + NoVariable paintformat24; + Variable paintformat25; + NoVariable paintformat26; + Variable paintformat27; + NoVariable paintformat28; + Variable paintformat29; + NoVariable paintformat30; + Variable paintformat31; + PaintComposite paintformat32; + } u; + public: + DEFINE_SIZE_MIN (2); +}; + +struct BaseGlyphPaintRecord +{ + int cmp (hb_codepoint_t g) const + { return g < glyphId ? -1 : g > glyphId ? 1 : 0; } + + bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map, + const void* src_base, hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SERIALIZE (this); + auto *out = s->embed (this); + if (unlikely (!out)) return_trace (false); + if (!s->check_assign (out->glyphId, glyph_map->get (glyphId), + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + return_trace (out->paint.serialize_subset (c, paint, src_base, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && paint.sanitize (c, base))); + } + + public: + HBGlyphID16 glyphId; /* Glyph ID of reference glyph */ + Offset32To paint; /* Offset (from beginning of BaseGlyphPaintRecord array) to Paint, + * Typically PaintColrLayers */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseGlyphList : SortedArray32Of +{ + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + const hb_set_t* glyphset = &c->plan->_glyphset_colred; + + for (const auto& _ : as_array ()) + { + unsigned gid = _.glyphId; + if (!glyphset->has (gid)) continue; + + if (_.serialize (c->serializer, c->plan->glyph_map, this, c, instancer)) out->len++; + else return_trace (false); + } + + return_trace (out->len != 0); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (SortedArray32Of::sanitize (c, this)); + } +}; + +struct LayerList : Array32OfOffset32To +{ + const Paint& get_paint (unsigned i) const + { return this+(*this)[i]; } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + for (const auto& _ : + hb_enumerate (*this) + | hb_filter (c->plan->colrv1_layers, hb_first)) + + { + auto *o = out->serialize_append (c->serializer); + if (unlikely (!o) || !o->serialize_subset (c, _.second, this, instancer)) + return_trace (false); + } + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (Array32OfOffset32To::sanitize (c, this)); + } +}; + +struct COLR +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR; + + bool has_v0_data () const { return numBaseGlyphs; } + bool has_v1_data () const + { + if (version == 1) + return (this+baseGlyphList).len > 0; + + return false; + } + + unsigned int get_glyph_layers (hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *count, /* IN/OUT. May be NULL. */ + hb_ot_color_layer_t *layers /* OUT. May be NULL. */) const + { + const BaseGlyphRecord &record = (this+baseGlyphsZ).bsearch (numBaseGlyphs, glyph); + + hb_array_t all_layers = (this+layersZ).as_array (numLayers); + hb_array_t glyph_layers = all_layers.sub_array (record.firstLayerIdx, + record.numLayers); + if (count) + { + + glyph_layers.sub_array (start_offset, count) + | hb_sink (hb_array (layers, *count)) + ; + } + return glyph_layers.length; + } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { colr = hb_sanitize_context_t ().reference_table (face); } + ~accelerator_t () { this->colr.destroy (); } + + bool is_valid () { return colr.get_blob ()->length; } + + void closure_glyphs (hb_codepoint_t glyph, + hb_set_t *related_ids /* OUT */) const + { colr->closure_glyphs (glyph, related_ids); } + + void closure_V0palette_indices (const hb_set_t *glyphs, + hb_set_t *palettes /* OUT */) const + { colr->closure_V0palette_indices (glyphs, palettes); } + + void closure_forV1 (hb_set_t *glyphset, + hb_set_t *layer_indices, + hb_set_t *palette_indices) const + { colr->closure_forV1 (glyphset, layer_indices, palette_indices); } + + private: + hb_blob_ptr_t colr; + }; + + void closure_glyphs (hb_codepoint_t glyph, + hb_set_t *related_ids /* OUT */) const + { + const BaseGlyphRecord *record = get_base_glyph_record (glyph); + if (!record) return; + + auto glyph_layers = (this+layersZ).as_array (numLayers).sub_array (record->firstLayerIdx, + record->numLayers); + if (!glyph_layers.length) return; + related_ids->add_array (&glyph_layers[0].glyphId, glyph_layers.length, LayerRecord::min_size); + } + + void closure_V0palette_indices (const hb_set_t *glyphs, + hb_set_t *palettes /* OUT */) const + { + if (!numBaseGlyphs || !numLayers) return; + hb_array_t baseGlyphs = (this+baseGlyphsZ).as_array (numBaseGlyphs); + hb_array_t all_layers = (this+layersZ).as_array (numLayers); + + for (const BaseGlyphRecord record : baseGlyphs) + { + if (!glyphs->has (record.glyphId)) continue; + hb_array_t glyph_layers = all_layers.sub_array (record.firstLayerIdx, + record.numLayers); + for (const LayerRecord layer : glyph_layers) + palettes->add (layer.colorIdx); + } + } + + void closure_forV1 (hb_set_t *glyphset, + hb_set_t *layer_indices, + hb_set_t *palette_indices) const + { + if (version != 1) return; + hb_set_t visited_glyphs; + + hb_colrv1_closure_context_t c (this, &visited_glyphs, layer_indices, palette_indices); + const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList; + + for (const BaseGlyphPaintRecord &baseglyph_paintrecord: baseglyph_paintrecords.iter ()) + { + unsigned gid = baseglyph_paintrecord.glyphId; + if (!glyphset->has (gid)) continue; + + const Paint &paint = &baseglyph_paintrecords+baseglyph_paintrecord.paint; + paint.dispatch (&c); + } + hb_set_union (glyphset, &visited_glyphs); + } + + const LayerList& get_layerList () const + { return (this+layerList); } + + const BaseGlyphList& get_baseglyphList () const + { return (this+baseGlyphList); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) && + (this+layersZ).sanitize (c, numLayers) && + (version == 0 || + (version == 1 && + baseGlyphList.sanitize (c, this) && + layerList.sanitize (c, this) && + clipList.sanitize (c, this) && + varIdxMap.sanitize (c, this) && + varStore.sanitize (c, this)))); + } + + template + bool serialize_V0 (hb_serialize_context_t *c, + unsigned version, + BaseIterator base_it, + LayerIterator layer_it) + { + TRACE_SERIALIZE (this); + if (unlikely (base_it.len () != layer_it.len ())) + return_trace (false); + + this->version = version; + numLayers = 0; + numBaseGlyphs = base_it.len (); + if (numBaseGlyphs == 0) + { + baseGlyphsZ = 0; + layersZ = 0; + return_trace (true); + } + + c->push (); + for (const hb_item_type _ : + base_it.iter ()) + { + auto* record = c->embed (_); + if (unlikely (!record)) return_trace (false); + record->firstLayerIdx = numLayers; + numLayers += record->numLayers; + } + c->add_link (baseGlyphsZ, c->pop_pack ()); + + c->push (); + for (const hb_item_type& _ : + layer_it.iter ()) + _.as_array ().copy (c); + + c->add_link (layersZ, c->pop_pack ()); + + return_trace (true); + } + + const BaseGlyphRecord* get_base_glyph_record (hb_codepoint_t gid) const + { + const BaseGlyphRecord* record = &(this+baseGlyphsZ).bsearch (numBaseGlyphs, (unsigned int) gid); + if (record == &Null (BaseGlyphRecord) || + (record && (hb_codepoint_t) record->glyphId != gid)) + record = nullptr; + return record; + } + + const BaseGlyphPaintRecord* get_base_glyph_paintrecord (hb_codepoint_t gid) const + { + const BaseGlyphPaintRecord* record = &(this+baseGlyphList).bsearch ((unsigned) gid); + if ((record && (hb_codepoint_t) record->glyphId != gid)) + record = nullptr; + return record; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map; + const hb_set_t& glyphset = c->plan->_glyphset_colred; + + auto base_it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_filter ([&](hb_codepoint_t new_gid) + { + hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); + if (glyphset.has (old_gid)) return true; + return false; + }) + | hb_map_retains_sorting ([&](hb_codepoint_t new_gid) + { + hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); + + const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid); + if (unlikely (!old_record)) + return hb_pair_t (false, Null (BaseGlyphRecord)); + BaseGlyphRecord new_record = {}; + new_record.glyphId = new_gid; + new_record.numLayers = old_record->numLayers; + return hb_pair_t (true, new_record); + }) + | hb_filter (hb_first) + | hb_map_retains_sorting (hb_second) + ; + + auto layer_it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map (reverse_glyph_map) + | hb_filter (glyphset) + | hb_map_retains_sorting ([&](hb_codepoint_t old_gid) + { + const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid); + hb_vector_t out_layers; + + if (unlikely (!old_record || + old_record->firstLayerIdx >= numLayers || + old_record->firstLayerIdx + old_record->numLayers > numLayers)) + return hb_pair_t> (false, out_layers); + + auto layers = (this+layersZ).as_array (numLayers).sub_array (old_record->firstLayerIdx, + old_record->numLayers); + out_layers.resize (layers.length); + for (unsigned int i = 0; i < layers.length; i++) { + out_layers[i] = layers[i]; + hb_codepoint_t new_gid = 0; + if (unlikely (!c->plan->new_gid_for_old_gid (out_layers[i].glyphId, &new_gid))) + return hb_pair_t> (false, out_layers); + out_layers[i].glyphId = new_gid; + out_layers[i].colorIdx = c->plan->colr_palettes.get (layers[i].colorIdx); + } + + return hb_pair_t> (true, out_layers); + }) + | hb_filter (hb_first) + | hb_map_retains_sorting (hb_second) + ; + + if (version == 0 && (!base_it || !layer_it)) + return_trace (false); + + COLR *colr_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->extend_min (colr_prime))) return_trace (false); + + if (version == 0) + return_trace (colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)); + + auto snap = c->serializer->snapshot (); + if (!c->serializer->allocate_size (5 * HBUINT32::static_size)) return_trace (false); + + VarStoreInstancer instancer (varStore ? &(this+varStore) : nullptr, + varIdxMap ? &(this+varIdxMap) : nullptr, + c->plan->normalized_coords.as_array ()); + + if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer)) + { + if (c->serializer->in_error ()) return_trace (false); + //no more COLRv1 glyphs: downgrade to version 0 + c->serializer->revert (snap); + return_trace (colr_prime->serialize_V0 (c->serializer, 0, base_it, layer_it)); + } + + if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false); + + colr_prime->layerList.serialize_subset (c, layerList, this, instancer); + colr_prime->clipList.serialize_subset (c, clipList, this, instancer); + if (!varStore || c->plan->all_axes_pinned) + return_trace (true); + + colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this); + colr_prime->varStore.serialize_copy (c->serializer, varStore, this); + return_trace (true); + } + + const Paint *get_base_glyph_paint (hb_codepoint_t glyph) const + { + const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList; + const BaseGlyphPaintRecord* record = get_base_glyph_paintrecord (glyph); + if (record) + { + const Paint &paint = &baseglyph_paintrecords+record->paint; + return &paint; + } + else + return nullptr; + } + +#ifndef HB_NO_PAINT + bool + get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + { + if (version != 1) + return false; + + VarStoreInstancer instancer (&(this+varStore), + &(this+varIdxMap), + hb_array (font->coords, font->num_coords)); + + if (get_clip (glyph, extents, instancer)) + { + font->scale_glyph_extents (extents); + return true; + } + + auto *extents_funcs = hb_paint_extents_get_funcs (); + hb_paint_extents_context_t extents_data; + bool ret = paint_glyph (font, glyph, extents_funcs, &extents_data, 0, HB_COLOR(0,0,0,0)); + + hb_extents_t e = extents_data.get_extents (); + if (e.is_void ()) + { + extents->x_bearing = 0; + extents->y_bearing = 0; + extents->width = 0; + extents->height = 0; + } + else + { + extents->x_bearing = e.xmin; + extents->y_bearing = e.ymax; + extents->width = e.xmax - e.xmin; + extents->height = e.ymin - e.ymax; + } + + return ret; + } +#endif + + bool + has_paint_for_glyph (hb_codepoint_t glyph) const + { + if (version == 1) + { + const Paint *paint = get_base_glyph_paint (glyph); + + return paint != nullptr; + } + + return false; + } + + bool get_clip (hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + const VarStoreInstancer instancer) const + { + return (this+clipList).get_extents (glyph, + extents, + instancer); + } + +#ifndef HB_NO_PAINT + bool + paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const + { + VarStoreInstancer instancer (&(this+varStore), + &(this+varIdxMap), + hb_array (font->coords, font->num_coords)); + hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer); + + if (version == 1) + { + const Paint *paint = get_base_glyph_paint (glyph); + if (paint) + { + // COLRv1 glyph + + VarStoreInstancer instancer (&(this+varStore), + &(this+varIdxMap), + hb_array (font->coords, font->num_coords)); + + bool is_bounded = true; + if (clip) + { + hb_glyph_extents_t extents; + if (get_clip (glyph, &extents, instancer)) + { + font->scale_glyph_extents (&extents); + c.funcs->push_clip_rectangle (c.data, + extents.x_bearing, + extents.y_bearing + extents.height, + extents.x_bearing + extents.width, + extents.y_bearing); + } + else + { + auto *extents_funcs = hb_paint_extents_get_funcs (); + hb_paint_extents_context_t extents_data; + + paint_glyph (font, glyph, + extents_funcs, &extents_data, + palette_index, foreground, + false); + + hb_extents_t extents = extents_data.get_extents (); + is_bounded = extents_data.is_bounded (); + + c.funcs->push_clip_rectangle (c.data, + extents.xmin, + extents.ymin, + extents.xmax, + extents.ymax); + } + } + + c.funcs->push_root_transform (c.data, font); + + if (is_bounded) + c.recurse (*paint); + + c.funcs->pop_transform (c.data); + + if (clip) + c.funcs->pop_clip (c.data); + + return true; + } + } + + const BaseGlyphRecord *record = get_base_glyph_record (glyph); + if (record && ((hb_codepoint_t) record->glyphId == glyph)) + { + // COLRv0 glyph + for (const auto &r : (this+layersZ).as_array (numLayers) + .sub_array (record->firstLayerIdx, record->numLayers)) + { + hb_bool_t is_foreground; + hb_color_t color = c.get_color (r.colorIdx, 1., &is_foreground); + c.funcs->push_clip_glyph (c.data, r.glyphId, c.font); + c.funcs->color (c.data, is_foreground, color); + c.funcs->pop_clip (c.data); + } + + return true; + } + + return false; + } +#endif + + protected: + HBUINT16 version; /* Table version number (starts at 0). */ + HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */ + NNOffset32To> + baseGlyphsZ; /* Offset to Base Glyph records. */ + NNOffset32To> + layersZ; /* Offset to Layer Records. */ + HBUINT16 numLayers; /* Number of Layer Records. */ + // Version-1 additions + Offset32To baseGlyphList; + Offset32To layerList; + Offset32To clipList; // Offset to ClipList table (may be NULL) + Offset32To varIdxMap; // Offset to DeltaSetIndexMap table (may be NULL) + Offset32To varStore; + public: + DEFINE_SIZE_MIN (14); +}; + +struct COLR_accelerator_t : COLR::accelerator_t { + COLR_accelerator_t (hb_face_t *face) : COLR::accelerator_t (face) {} +}; + +void +hb_paint_context_t::recurse (const Paint &paint) +{ + if (unlikely (depth_left <= 0 || edge_count <= 0)) return; + depth_left--; + edge_count--; + paint.dispatch (this); + depth_left++; +} + +void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const +{ + const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList (); + for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++) + { + const Paint &paint = paint_offset_lists.get_paint (i); + c->funcs->push_group (c->data); + c->recurse (paint); + c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER); + } +} + +void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const +{ + const COLR *colr_table = c->get_colr_table (); + const Paint *paint = colr_table->get_base_glyph_paint (gid); + + hb_glyph_extents_t extents = {0}; + bool has_clip_box = colr_table->get_clip (gid, &extents, c->instancer); + + if (has_clip_box) + c->funcs->push_clip_rectangle (c->data, + extents.x_bearing, + extents.y_bearing + extents.height, + extents.x_bearing + extents.width, + extents.y_bearing); + + if (paint) + c->recurse (*paint); + + if (has_clip_box) + c->funcs->pop_clip (c->data); +} + +} /* namespace OT */ + +#endif /* OT_COLOR_COLR_COLR_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/colrv1-closure.hh b/src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/colrv1-closure.hh new file mode 100644 index 00000000000..705863d4ade --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/colrv1-closure.hh @@ -0,0 +1,107 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * Copyright ยฉ 2020 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#ifndef OT_COLOR_COLR_COLRV1_CLOSURE_HH +#define OT_COLOR_COLR_COLRV1_CLOSURE_HH + +#include "../../../hb-open-type.hh" +#include "COLR.hh" + +/* + * COLR -- Color + * https://docs.microsoft.com/en-us/typography/opentype/spec/colr + */ +namespace OT { + +HB_INTERNAL void PaintColrLayers::closurev1 (hb_colrv1_closure_context_t* c) const +{ + c->add_layer_indices (firstLayerIndex, numLayers); + const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList (); + for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++) + { + const Paint &paint = std::addressof (paint_offset_lists) + paint_offset_lists[i]; + paint.dispatch (c); + } +} + +HB_INTERNAL void PaintGlyph::closurev1 (hb_colrv1_closure_context_t* c) const +{ + c->add_glyph (gid); + (this+paint).dispatch (c); +} + +HB_INTERNAL void PaintColrGlyph::closurev1 (hb_colrv1_closure_context_t* c) const +{ + const COLR *colr_table = c->get_colr_table (); + const BaseGlyphPaintRecord* baseglyph_paintrecord = colr_table->get_base_glyph_paintrecord (gid); + if (!baseglyph_paintrecord) return; + c->add_glyph (gid); + + const BaseGlyphList &baseglyph_list = colr_table->get_baseglyphList (); + (&baseglyph_list+baseglyph_paintrecord->paint).dispatch (c); +} + +template class Var> +HB_INTERNAL void PaintTransform::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintTranslate::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintScale::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintScaleAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintScaleUniform::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintScaleUniformAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintRotate::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintRotateAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintSkew::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintSkewAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintComposite::closurev1 (hb_colrv1_closure_context_t* c) const +{ + (this+src).dispatch (c); + (this+backdrop).dispatch (c); +} + +} /* namespace OT */ + + +#endif /* OT_COLOR_COLR_COLRV1_CLOSURE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Color/CPAL/CPAL.hh b/src/3rdparty/harfbuzz-ng/src/OT/Color/CPAL/CPAL.hh new file mode 100644 index 00000000000..c07716c1c98 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Color/CPAL/CPAL.hh @@ -0,0 +1,350 @@ +/* + * Copyright ยฉ 2016 Google, Inc. + * Copyright ยฉ 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Sascha Brawer + */ + +#ifndef OT_COLOR_CPAL_CPAL_HH +#define OT_COLOR_CPAL_CPAL_HH + +#include "../../../hb-open-type.hh" +#include "../../../hb-ot-color.h" +#include "../../../hb-ot-name.h" + + +/* + * CPAL -- Color Palette + * https://docs.microsoft.com/en-us/typography/opentype/spec/cpal + */ +#define HB_OT_TAG_CPAL HB_TAG('C','P','A','L') + +namespace OT { + + +struct CPALV1Tail +{ + friend struct CPAL; + + private: + hb_ot_color_palette_flags_t get_palette_flags (const void *base, + unsigned int palette_index, + unsigned int palette_count) const + { + if (!paletteFlagsZ) return HB_OT_COLOR_PALETTE_FLAG_DEFAULT; + return (hb_ot_color_palette_flags_t) (uint32_t) + (base+paletteFlagsZ).as_array (palette_count)[palette_index]; + } + + hb_ot_name_id_t get_palette_name_id (const void *base, + unsigned int palette_index, + unsigned int palette_count) const + { + if (!paletteLabelsZ) return HB_OT_NAME_ID_INVALID; + return (base+paletteLabelsZ).as_array (palette_count)[palette_index]; + } + + hb_ot_name_id_t get_color_name_id (const void *base, + unsigned int color_index, + unsigned int color_count) const + { + if (!colorLabelsZ) return HB_OT_NAME_ID_INVALID; + return (base+colorLabelsZ).as_array (color_count)[color_index]; + } + + public: + void collect_name_ids (const void *base, + unsigned palette_count, + unsigned color_count, + const hb_map_t *color_index_map, + hb_set_t *nameids_to_retain /* OUT */) const + { + if (paletteLabelsZ) + { + + (base+paletteLabelsZ).as_array (palette_count) + | hb_sink (nameids_to_retain) + ; + } + + if (colorLabelsZ) + { + const hb_array_t colorLabels = (base+colorLabelsZ).as_array (color_count); + for (unsigned i = 0; i < color_count; i++) + { + if (!color_index_map->has (i)) continue; + nameids_to_retain->add (colorLabels[i]); + } + } + } + + bool serialize (hb_serialize_context_t *c, + unsigned palette_count, + unsigned color_count, + const void *base, + const hb_map_t *color_index_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->allocate_size (static_size); + if (unlikely (!out)) return_trace (false); + + out->paletteFlagsZ = 0; + if (paletteFlagsZ) + out->paletteFlagsZ.serialize_copy (c, paletteFlagsZ, base, 0, hb_serialize_context_t::Head, palette_count); + + out->paletteLabelsZ = 0; + if (paletteLabelsZ) + out->paletteLabelsZ.serialize_copy (c, paletteLabelsZ, base, 0, hb_serialize_context_t::Head, palette_count); + + const hb_array_t colorLabels = (base+colorLabelsZ).as_array (color_count); + if (colorLabelsZ) + { + c->push (); + for (unsigned i = 0; i < color_count; i++) + { + if (!color_index_map->has (i)) continue; + if (!c->copy (colorLabels[i])) + { + c->pop_discard (); + return_trace (false); + } + } + c->add_link (out->colorLabelsZ, c->pop_pack ()); + } + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c, + const void *base, + unsigned int palette_count, + unsigned int color_count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + (!paletteFlagsZ || (base+paletteFlagsZ).sanitize (c, palette_count)) && + (!paletteLabelsZ || (base+paletteLabelsZ).sanitize (c, palette_count)) && + (!colorLabelsZ || (base+colorLabelsZ).sanitize (c, color_count))); + } + + protected: + // TODO(garretrieger): these offsets can hold nulls so we should not be using non-null offsets + // here. Currently they are needed since UnsizedArrayOf doesn't define null_size + NNOffset32To> + paletteFlagsZ; /* Offset from the beginning of CPAL table to + * the Palette Type Array. Set to 0 if no array + * is provided. */ + NNOffset32To> + paletteLabelsZ; /* Offset from the beginning of CPAL table to + * the palette labels array. Set to 0 if no + * array is provided. */ + NNOffset32To> + colorLabelsZ; /* Offset from the beginning of CPAL table to + * the color labels array. Set to 0 + * if no array is provided. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +typedef HBUINT32 BGRAColor; + +struct CPAL +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_CPAL; + + bool has_data () const { return numPalettes; } + + unsigned int get_size () const + { return min_size + numPalettes * sizeof (colorRecordIndicesZ[0]); } + + unsigned int get_palette_count () const { return numPalettes; } + unsigned int get_color_count () const { return numColors; } + + hb_ot_color_palette_flags_t get_palette_flags (unsigned int palette_index) const + { return v1 ().get_palette_flags (this, palette_index, numPalettes); } + + hb_ot_name_id_t get_palette_name_id (unsigned int palette_index) const + { return v1 ().get_palette_name_id (this, palette_index, numPalettes); } + + hb_ot_name_id_t get_color_name_id (unsigned int color_index) const + { return v1 ().get_color_name_id (this, color_index, numColors); } + + unsigned int get_palette_colors (unsigned int palette_index, + unsigned int start_offset, + unsigned int *color_count, /* IN/OUT. May be NULL. */ + hb_color_t *colors /* OUT. May be NULL. */) const + { + if (unlikely (palette_index >= numPalettes)) + { + if (color_count) *color_count = 0; + return 0; + } + unsigned int start_index = colorRecordIndicesZ[palette_index]; + hb_array_t all_colors ((this+colorRecordsZ).arrayZ, numColorRecords); + hb_array_t palette_colors = all_colors.sub_array (start_index, + numColors); + if (color_count) + { + + palette_colors.sub_array (start_offset, color_count) + | hb_sink (hb_array (colors, *color_count)) + ; + } + return numColors; + } + + void collect_name_ids (const hb_map_t *color_index_map, + hb_set_t *nameids_to_retain /* OUT */) const + { + if (version == 1) + v1 ().collect_name_ids (this, numPalettes, numColors, color_index_map, nameids_to_retain); + } + + private: + const CPALV1Tail& v1 () const + { + if (version == 0) return Null (CPALV1Tail); + return StructAfter (*this); + } + + public: + bool serialize (hb_serialize_context_t *c, + const hb_array_t &color_record_indices, + const hb_array_t &color_records, + const hb_vector_t& first_color_index_for_layer, + const hb_map_t& first_color_to_layer_index, + const hb_set_t &retained_color_indices) const + { + TRACE_SERIALIZE (this); + + // TODO(grieger): limit total final size. + + for (const auto idx : color_record_indices) + { + hb_codepoint_t layer_index = first_color_to_layer_index[idx]; + + HBUINT16 new_idx; + new_idx = layer_index * retained_color_indices.get_population (); + if (!c->copy (new_idx)) return_trace (false); + } + + c->push (); + for (unsigned first_color_index : first_color_index_for_layer) + { + for (hb_codepoint_t color_index : retained_color_indices) + { + if (!c->copy (color_records[first_color_index + color_index])) + { + c->pop_discard (); + return_trace (false); + } + } + } + + c->add_link (colorRecordsZ, c->pop_pack ()); + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + if (!numPalettes) return_trace (false); + + const hb_map_t *color_index_map = &c->plan->colr_palettes; + if (color_index_map->is_empty ()) return_trace (false); + + hb_set_t retained_color_indices; + for (const auto _ : color_index_map->keys ()) + { + if (_ == 0xFFFF) continue; + retained_color_indices.add (_); + } + if (retained_color_indices.is_empty ()) return_trace (false); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + out->version = version; + out->numColors = retained_color_indices.get_population (); + out->numPalettes = numPalettes; + + hb_vector_t first_color_index_for_layer; + hb_map_t first_color_to_layer_index; + + const hb_array_t colorRecordIndices = colorRecordIndicesZ.as_array (numPalettes); + for (const auto first_color_record_idx : colorRecordIndices) + { + if (first_color_to_layer_index.has (first_color_record_idx)) continue; + + first_color_index_for_layer.push (first_color_record_idx); + first_color_to_layer_index.set (first_color_record_idx, + first_color_index_for_layer.length - 1); + } + + out->numColorRecords = first_color_index_for_layer.length + * retained_color_indices.get_population (); + + const hb_array_t color_records = (this+colorRecordsZ).as_array (numColorRecords); + if (!out->serialize (c->serializer, + colorRecordIndices, + color_records, + first_color_index_for_layer, + first_color_to_layer_index, + retained_color_indices)) + return_trace (false); + + if (version == 1) + return_trace (v1 ().serialize (c->serializer, numPalettes, numColors, this, color_index_map)); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + (this+colorRecordsZ).sanitize (c, numColorRecords) && + colorRecordIndicesZ.sanitize (c, numPalettes) && + (version == 0 || v1 ().sanitize (c, this, numPalettes, numColors))); + } + + protected: + HBUINT16 version; /* Table version number */ + /* Version 0 */ + HBUINT16 numColors; /* Number of colors in each palette. */ + HBUINT16 numPalettes; /* Number of palettes in the table. */ + HBUINT16 numColorRecords; /* Total number of color records, combined for + * all palettes. */ + NNOffset32To> + colorRecordsZ; /* Offset from the beginning of CPAL table to + * the first ColorRecord. */ + UnsizedArrayOf + colorRecordIndicesZ; /* Index of each paletteโ€™s first color record in + * the combined color record array. */ +/*CPALV1Tail v1;*/ + public: + DEFINE_SIZE_ARRAY (12, colorRecordIndicesZ); +}; + +} /* namespace OT */ + + +#endif /* OT_COLOR_CPAL_CPAL_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Color/sbix/sbix.hh b/src/3rdparty/harfbuzz-ng/src/OT/Color/sbix/sbix.hh new file mode 100644 index 00000000000..46ad3fd58e1 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Color/sbix/sbix.hh @@ -0,0 +1,452 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * Copyright ยฉ 2020 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Calder Kitagawa + */ + +#ifndef OT_COLOR_SBIX_SBIX_HH +#define OT_COLOR_SBIX_SBIX_HH + +#include "../../../hb-open-type.hh" +#include "../../../hb-paint.hh" + +/* + * sbix -- Standard Bitmap Graphics + * https://docs.microsoft.com/en-us/typography/opentype/spec/sbix + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6sbix.html + */ +#define HB_OT_TAG_sbix HB_TAG('s','b','i','x') + + +namespace OT { + + +struct SBIXGlyph +{ + SBIXGlyph* copy (hb_serialize_context_t *c, unsigned int data_length) const + { + TRACE_SERIALIZE (this); + SBIXGlyph* new_glyph = c->start_embed (); + if (unlikely (!new_glyph)) return_trace (nullptr); + if (unlikely (!c->extend_min (new_glyph))) return_trace (nullptr); + + new_glyph->xOffset = xOffset; + new_glyph->yOffset = yOffset; + new_glyph->graphicType = graphicType; + data.copy (c, data_length); + return_trace (new_glyph); + } + + HBINT16 xOffset; /* The horizontal (x-axis) offset from the left + * edge of the graphic to the glyphโ€™s origin. + * That is, the x-coordinate of the point on the + * baseline at the left edge of the glyph. */ + HBINT16 yOffset; /* The vertical (y-axis) offset from the bottom + * edge of the graphic to the glyphโ€™s origin. + * That is, the y-coordinate of the point on the + * baseline at the left edge of the glyph. */ + Tag graphicType; /* Indicates the format of the embedded graphic + * data: one of 'jpg ', 'png ' or 'tiff', or the + * special format 'dupe'. */ + UnsizedArrayOf + data; /* The actual embedded graphic data. The total + * length is inferred from sequential entries in + * the glyphDataOffsets array and the fixed size + * (8 bytes) of the preceding fields. */ + public: + DEFINE_SIZE_ARRAY (8, data); +}; + +struct SBIXStrike +{ + static unsigned int get_size (unsigned num_glyphs) + { return min_size + num_glyphs * HBUINT32::static_size; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + imageOffsetsZ.sanitize_shallow (c, c->get_num_glyphs () + 1)); + } + + hb_blob_t *get_glyph_blob (unsigned int glyph_id, + hb_blob_t *sbix_blob, + hb_tag_t file_type, + int *x_offset, + int *y_offset, + unsigned int num_glyphs, + unsigned int *strike_ppem) const + { + if (unlikely (!ppem)) return hb_blob_get_empty (); /* To get Null() object out of the way. */ + + unsigned int retry_count = 8; + unsigned int sbix_len = sbix_blob->length; + unsigned int strike_offset = (const char *) this - (const char *) sbix_blob->data; + assert (strike_offset < sbix_len); + + retry: + if (unlikely (glyph_id >= num_glyphs || + imageOffsetsZ[glyph_id + 1] <= imageOffsetsZ[glyph_id] || + imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] <= SBIXGlyph::min_size || + (unsigned int) imageOffsetsZ[glyph_id + 1] > sbix_len - strike_offset)) + return hb_blob_get_empty (); + + unsigned int glyph_offset = strike_offset + (unsigned int) imageOffsetsZ[glyph_id] + SBIXGlyph::min_size; + unsigned int glyph_length = imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] - SBIXGlyph::min_size; + + const SBIXGlyph *glyph = &(this+imageOffsetsZ[glyph_id]); + + if (glyph->graphicType == HB_TAG ('d','u','p','e')) + { + if (glyph_length >= 2) + { + glyph_id = *((HBUINT16 *) &glyph->data); + if (retry_count--) + goto retry; + } + return hb_blob_get_empty (); + } + + if (unlikely (file_type != glyph->graphicType)) + return hb_blob_get_empty (); + + if (strike_ppem) *strike_ppem = ppem; + if (x_offset) *x_offset = glyph->xOffset; + if (y_offset) *y_offset = glyph->yOffset; + return hb_blob_create_sub_blob (sbix_blob, glyph_offset, glyph_length); + } + + bool subset (hb_subset_context_t *c, unsigned int available_len) const + { + TRACE_SUBSET (this); + unsigned int num_output_glyphs = c->plan->num_output_glyphs (); + + auto* out = c->serializer->start_embed (); + if (unlikely (!out)) return_trace (false); + auto snap = c->serializer->snapshot (); + if (unlikely (!c->serializer->extend (out, num_output_glyphs + 1))) return_trace (false); + out->ppem = ppem; + out->resolution = resolution; + HBUINT32 head; + head = get_size (num_output_glyphs + 1); + + bool has_glyphs = false; + for (unsigned new_gid = 0; new_gid < num_output_glyphs; new_gid++) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (new_gid, &old_gid) || + unlikely (imageOffsetsZ[old_gid].is_null () || + imageOffsetsZ[old_gid + 1].is_null () || + imageOffsetsZ[old_gid + 1] <= imageOffsetsZ[old_gid] || + imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid] <= SBIXGlyph::min_size) || + (unsigned int) imageOffsetsZ[old_gid + 1] > available_len) + { + out->imageOffsetsZ[new_gid] = head; + continue; + } + has_glyphs = true; + unsigned int delta = imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid]; + unsigned int glyph_data_length = delta - SBIXGlyph::min_size; + if (!(this+imageOffsetsZ[old_gid]).copy (c->serializer, glyph_data_length)) + return_trace (false); + out->imageOffsetsZ[new_gid] = head; + head += delta; + } + if (has_glyphs) + out->imageOffsetsZ[num_output_glyphs] = head; + else + c->serializer->revert (snap); + return_trace (has_glyphs); + } + + public: + HBUINT16 ppem; /* The PPEM size for which this strike was designed. */ + HBUINT16 resolution; /* The device pixel density (in PPI) for which this + * strike was designed. (E.g., 96 PPI, 192 PPI.) */ + protected: + UnsizedArrayOf> + imageOffsetsZ; /* Offset from the beginning of the strike data header + * to bitmap data for an individual glyph ID. */ + public: + DEFINE_SIZE_ARRAY (4, imageOffsetsZ); +}; + +struct sbix +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_sbix; + + bool has_data () const { return version; } + + const SBIXStrike &get_strike (unsigned int i) const { return this+strikes[i]; } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + table = hb_sanitize_context_t ().reference_table (face); + num_glyphs = face->get_num_glyphs (); + } + ~accelerator_t () { table.destroy (); } + + bool has_data () const { return table->has_data (); } + + bool get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + bool scale = true) const + { + /* We only support PNG right now, and following function checks type. */ + return get_png_extents (font, glyph, extents, scale); + } + + hb_blob_t *reference_png (hb_font_t *font, + hb_codepoint_t glyph_id, + int *x_offset, + int *y_offset, + unsigned int *available_ppem) const + { + return choose_strike (font).get_glyph_blob (glyph_id, table.get_blob (), + HB_TAG ('p','n','g',' '), + x_offset, y_offset, + num_glyphs, available_ppem); + } + + bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const + { + if (!has_data ()) + return false; + + int x_offset = 0, y_offset = 0; + unsigned int strike_ppem = 0; + hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem); + hb_glyph_extents_t extents; + hb_glyph_extents_t pixel_extents; + + if (blob == hb_blob_get_empty ()) + return false; + + if (!hb_font_get_glyph_extents (font, glyph, &extents)) + return false; + + if (unlikely (!get_extents (font, glyph, &pixel_extents, false))) + return false; + + bool ret = funcs->image (data, + blob, + pixel_extents.width, -pixel_extents.height, + HB_PAINT_IMAGE_FORMAT_PNG, + font->slant_xy, + &extents); + + hb_blob_destroy (blob); + return ret; + } + + private: + + const SBIXStrike &choose_strike (hb_font_t *font) const + { + unsigned count = table->strikes.len; + if (unlikely (!count)) + return Null (SBIXStrike); + + unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem); + if (!requested_ppem) + requested_ppem = 1<<30; /* Choose largest strike. */ + /* TODO Add DPI sensitivity as well? */ + unsigned int best_i = 0; + unsigned int best_ppem = table->get_strike (0).ppem; + + for (unsigned int i = 1; i < count; i++) + { + unsigned int ppem = (table->get_strike (i)).ppem; + if ((requested_ppem <= ppem && ppem < best_ppem) || + (requested_ppem > best_ppem && ppem > best_ppem)) + { + best_i = i; + best_ppem = ppem; + } + } + + return table->get_strike (best_i); + } + + struct PNGHeader + { + HBUINT8 signature[8]; + struct + { + struct + { + HBUINT32 length; + Tag type; + } header; + HBUINT32 width; + HBUINT32 height; + HBUINT8 bitDepth; + HBUINT8 colorType; + HBUINT8 compressionMethod; + HBUINT8 filterMethod; + HBUINT8 interlaceMethod; + } IHDR; + + public: + DEFINE_SIZE_STATIC (29); + }; + + bool get_png_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + bool scale = true) const + { + /* Following code is safe to call even without data. + * But faster to short-circuit. */ + if (!has_data ()) + return false; + + int x_offset = 0, y_offset = 0; + unsigned int strike_ppem = 0; + hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem); + + const PNGHeader &png = *blob->as(); + + if (png.IHDR.height >= 65536 || png.IHDR.width >= 65536) + { + hb_blob_destroy (blob); + return false; + } + + extents->x_bearing = x_offset; + extents->y_bearing = png.IHDR.height + y_offset; + extents->width = png.IHDR.width; + extents->height = -1 * png.IHDR.height; + + /* Convert to font units. */ + if (strike_ppem && scale) + { + float scale = font->face->get_upem () / (float) strike_ppem; + extents->x_bearing = roundf (extents->x_bearing * scale); + extents->y_bearing = roundf (extents->y_bearing * scale); + extents->width = roundf (extents->width * scale); + extents->height = roundf (extents->height * scale); + } + + if (scale) + font->scale_glyph_extents (extents); + + hb_blob_destroy (blob); + + return strike_ppem; + } + + private: + hb_blob_ptr_t table; + + unsigned int num_glyphs; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version >= 1 && + strikes.sanitize (c, this))); + } + + bool + add_strike (hb_subset_context_t *c, unsigned i) const + { + if (strikes[i].is_null () || c->source_blob->length < (unsigned) strikes[i]) + return false; + + return (this+strikes[i]).subset (c, c->source_blob->length - (unsigned) strikes[i]); + } + + bool serialize_strike_offsets (hb_subset_context_t *c) const + { + TRACE_SERIALIZE (this); + + auto *out = c->serializer->start_embed> (); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_vector_t*> new_strikes; + hb_vector_t objidxs; + for (int i = strikes.len - 1; i >= 0; --i) + { + auto* o = out->serialize_append (c->serializer); + if (unlikely (!o)) return_trace (false); + *o = 0; + auto snap = c->serializer->snapshot (); + c->serializer->push (); + bool ret = add_strike (c, i); + if (!ret) + { + c->serializer->pop_discard (); + out->pop (); + c->serializer->revert (snap); + } + else + { + objidxs.push (c->serializer->pop_pack ()); + new_strikes.push (o); + } + } + for (unsigned int i = 0; i < new_strikes.length; ++i) + c->serializer->add_link (*new_strikes[i], objidxs[new_strikes.length - 1 - i]); + + return_trace (true); + } + + bool subset (hb_subset_context_t* c) const + { + TRACE_SUBSET (this); + + sbix *sbix_prime = c->serializer->start_embed (); + if (unlikely (!sbix_prime)) return_trace (false); + if (unlikely (!c->serializer->embed (this->version))) return_trace (false); + if (unlikely (!c->serializer->embed (this->flags))) return_trace (false); + + return_trace (serialize_strike_offsets (c)); + } + + protected: + HBUINT16 version; /* Table version number โ€” set to 1 */ + HBUINT16 flags; /* Bit 0: Set to 1. Bit 1: Draw outlines. + * Bits 2 to 15: reserved (set to 0). */ + Array32OfOffset32To + strikes; /* Offsets from the beginning of the 'sbix' + * table to data for each individual bitmap strike. */ + public: + DEFINE_SIZE_ARRAY (8, strikes); +}; + +struct sbix_accelerator_t : sbix::accelerator_t { + sbix_accelerator_t (hb_face_t *face) : sbix::accelerator_t (face) {} +}; + + +} /* namespace OT */ + +#endif /* OT_COLOR_SBIX_SBIX_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Color/svg/svg.hh b/src/3rdparty/harfbuzz-ng/src/OT/Color/svg/svg.hh new file mode 100644 index 00000000000..c7d91b88ee0 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Color/svg/svg.hh @@ -0,0 +1,151 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef OT_COLOR_SVG_SVG_HH +#define OT_COLOR_SVG_SVG_HH + +#include "../../../hb-open-type.hh" +#include "../../../hb-blob.hh" +#include "../../../hb-paint.hh" + +/* + * SVG -- SVG (Scalable Vector Graphics) + * https://docs.microsoft.com/en-us/typography/opentype/spec/svg + */ + +#define HB_OT_TAG_SVG HB_TAG('S','V','G',' ') + + +namespace OT { + + +struct SVGDocumentIndexEntry +{ + int cmp (hb_codepoint_t g) const + { return g < startGlyphID ? -1 : g > endGlyphID ? 1 : 0; } + + hb_blob_t *reference_blob (hb_blob_t *svg_blob, unsigned int index_offset) const + { + return hb_blob_create_sub_blob (svg_blob, + index_offset + (unsigned int) svgDoc, + svgDocLength); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + svgDoc.sanitize (c, base, svgDocLength)); + } + + protected: + HBUINT16 startGlyphID; /* The first glyph ID in the range described by + * this index entry. */ + HBUINT16 endGlyphID; /* The last glyph ID in the range described by + * this index entry. Must be >= startGlyphID. */ + NNOffset32To> + svgDoc; /* Offset from the beginning of the SVG Document Index + * to an SVG document. Must be non-zero. */ + HBUINT32 svgDocLength; /* Length of the SVG document. + * Must be non-zero. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct SVG +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_SVG; + + bool has_data () const { return svgDocEntries; } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { table = hb_sanitize_context_t ().reference_table (face); } + ~accelerator_t () { table.destroy (); } + + hb_blob_t *reference_blob_for_glyph (hb_codepoint_t glyph_id) const + { + return table->get_glyph_entry (glyph_id).reference_blob (table.get_blob (), + table->svgDocEntries); + } + + bool has_data () const { return table->has_data (); } + + bool paint_glyph (hb_font_t *font HB_UNUSED, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const + { + if (!has_data ()) + return false; + + hb_blob_t *blob = reference_blob_for_glyph (glyph); + + if (blob == hb_blob_get_empty ()) + return false; + + funcs->image (data, + blob, + 0, 0, + HB_PAINT_IMAGE_FORMAT_SVG, + font->slant_xy, + nullptr); + + hb_blob_destroy (blob); + return true; + } + + private: + hb_blob_ptr_t table; + public: + DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t)); + }; + + const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const + { return (this+svgDocEntries).bsearch (glyph_id); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (this+svgDocEntries).sanitize_shallow (c))); + } + + protected: + HBUINT16 version; /* Table version (starting at 0). */ + Offset32To> + svgDocEntries; /* Offset (relative to the start of the SVG table) to the + * SVG Documents Index. Must be non-zero. */ + /* Array of SVG Document Index Entries. */ + HBUINT32 reserved; /* Set to 0. */ + public: + DEFINE_SIZE_STATIC (10); +}; + +struct SVG_accelerator_t : SVG::accelerator_t { + SVG_accelerator_t (hb_face_t *face) : SVG::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* OT_COLOR_SVG_SVG_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/Coverage.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/Coverage.hh new file mode 100644 index 00000000000..d35654e2457 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/Coverage.hh @@ -0,0 +1,337 @@ +/* + * Copyright ยฉ 2007,2008,2009 Red Hat, Inc. + * Copyright ยฉ 2010,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_COMMON_COVERAGE_HH +#define OT_LAYOUT_COMMON_COVERAGE_HH + +#include "../types.hh" +#include "CoverageFormat1.hh" +#include "CoverageFormat2.hh" + +namespace OT { +namespace Layout { +namespace Common { + +template +static inline void Coverage_serialize (hb_serialize_context_t *c, + Iterator it); + +struct Coverage +{ + + protected: + union { + HBUINT16 format; /* Format identifier */ + CoverageFormat1_3 format1; + CoverageFormat2_4 format2; +#ifndef HB_NO_BEYOND_64K + CoverageFormat1_3format3; + CoverageFormat2_4format4; +#endif + } u; + public: + DEFINE_SIZE_UNION (2, format); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) + { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (u.format3.sanitize (c)); + case 4: return_trace (u.format4.sanitize (c)); +#endif + default:return_trace (true); + } + } + + /* Has interface. */ + unsigned operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != NOT_COVERED; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + unsigned int get (hb_codepoint_t k) const { return get_coverage (k); } + unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.get_coverage (glyph_id); + case 2: return u.format2.get_coverage (glyph_id); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.get_coverage (glyph_id); + case 4: return u.format4.get_coverage (glyph_id); +#endif + default:return NOT_COVERED; + } + } + + unsigned get_population () const + { + switch (u.format) { + case 1: return u.format1.get_population (); + case 2: return u.format2.get_population (); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.get_population (); + case 4: return u.format4.get_population (); +#endif + default:return NOT_COVERED; + } + } + + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + + unsigned count = 0; + unsigned num_ranges = 0; + hb_codepoint_t last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) + num_ranges++; + last = g; + count++; + } + u.format = count <= num_ranges * 3 ? 1 : 2; + +#ifndef HB_NO_BEYOND_64K + if (count && last > 0xFFFFu) + u.format += 2; +#endif + + switch (u.format) + { + case 1: return_trace (u.format1.serialize (c, glyphs)); + case 2: return_trace (u.format2.serialize (c, glyphs)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (u.format3.serialize (c, glyphs)); + case 4: return_trace (u.format4.serialize (c, glyphs)); +#endif + default:return_trace (false); + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto it = + + iter () + | hb_take (c->plan->source->get_num_glyphs ()) + | hb_filter (c->plan->glyph_map_gsub) + | hb_map_retains_sorting (c->plan->glyph_map_gsub) + ; + + // Cache the iterator result as it will be iterated multiple times + // by the serialize code below. + hb_sorted_vector_t glyphs (it); + Coverage_serialize (c->serializer, glyphs.iter ()); + return_trace (bool (glyphs)); + } + + bool intersects (const hb_set_t *glyphs) const + { + switch (u.format) + { + case 1: return u.format1.intersects (glyphs); + case 2: return u.format2.intersects (glyphs); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.intersects (glyphs); + case 4: return u.format4.intersects (glyphs); +#endif + default:return false; + } + } + bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const + { + switch (u.format) + { + case 1: return u.format1.intersects_coverage (glyphs, index); + case 2: return u.format2.intersects_coverage (glyphs, index); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.intersects_coverage (glyphs, index); + case 4: return u.format4.intersects_coverage (glyphs, index); +#endif + default:return false; + } + } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template + bool collect_coverage (set_t *glyphs) const + { + switch (u.format) + { + case 1: return u.format1.collect_coverage (glyphs); + case 2: return u.format2.collect_coverage (glyphs); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.collect_coverage (glyphs); + case 4: return u.format4.collect_coverage (glyphs); +#endif + default:return false; + } + } + + template + void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const + { + switch (u.format) + { + case 1: return u.format1.intersect_set (glyphs, intersect_glyphs); + case 2: return u.format2.intersect_set (glyphs, intersect_glyphs); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.intersect_set (glyphs, intersect_glyphs); + case 4: return u.format4.intersect_set (glyphs, intersect_glyphs); +#endif + default:return ; + } + } + + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + iter_t (const Coverage &c_ = Null (Coverage)) + { + hb_memset (this, 0, sizeof (*this)); + format = c_.u.format; + switch (format) + { + case 1: u.format1.init (c_.u.format1); return; + case 2: u.format2.init (c_.u.format2); return; +#ifndef HB_NO_BEYOND_64K + case 3: u.format3.init (c_.u.format3); return; + case 4: u.format4.init (c_.u.format4); return; +#endif + default: return; + } + } + bool __more__ () const + { + switch (format) + { + case 1: return u.format1.__more__ (); + case 2: return u.format2.__more__ (); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.__more__ (); + case 4: return u.format4.__more__ (); +#endif + default:return false; + } + } + void __next__ () + { + switch (format) + { + case 1: u.format1.__next__ (); break; + case 2: u.format2.__next__ (); break; +#ifndef HB_NO_BEYOND_64K + case 3: u.format3.__next__ (); break; + case 4: u.format4.__next__ (); break; +#endif + default: break; + } + } + typedef hb_codepoint_t __item_t__; + __item_t__ __item__ () const { return get_glyph (); } + + hb_codepoint_t get_glyph () const + { + switch (format) + { + case 1: return u.format1.get_glyph (); + case 2: return u.format2.get_glyph (); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.get_glyph (); + case 4: return u.format4.get_glyph (); +#endif + default:return 0; + } + } + bool operator != (const iter_t& o) const + { + if (unlikely (format != o.format)) return true; + switch (format) + { + case 1: return u.format1 != o.u.format1; + case 2: return u.format2 != o.u.format2; +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3 != o.u.format3; + case 4: return u.format4 != o.u.format4; +#endif + default:return false; + } + } + iter_t __end__ () const + { + iter_t it = {}; + it.format = format; + switch (format) + { + case 1: it.u.format1 = u.format1.__end__ (); break; + case 2: it.u.format2 = u.format2.__end__ (); break; +#ifndef HB_NO_BEYOND_64K + case 3: it.u.format3 = u.format3.__end__ (); break; + case 4: it.u.format4 = u.format4.__end__ (); break; +#endif + default: break; + } + return it; + } + + private: + unsigned int format; + union { +#ifndef HB_NO_BEYOND_64K + CoverageFormat2_4::iter_t format4; /* Put this one first since it's larger; helps shut up compiler. */ + CoverageFormat1_3::iter_t format3; +#endif + CoverageFormat2_4::iter_t format2; /* Put this one first since it's larger; helps shut up compiler. */ + CoverageFormat1_3::iter_t format1; + } u; + }; + iter_t iter () const { return iter_t (*this); } +}; + +template +static inline void +Coverage_serialize (hb_serialize_context_t *c, + Iterator it) +{ c->start_embed ()->serialize (c, it); } + +} +} +} + +#endif // #ifndef OT_LAYOUT_COMMON_COVERAGE_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat1.hh new file mode 100644 index 00000000000..5d68e3d15e7 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat1.hh @@ -0,0 +1,133 @@ +/* + * Copyright ยฉ 2007,2008,2009 Red Hat, Inc. + * Copyright ยฉ 2010,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + + +#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH +#define OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH + +namespace OT { +namespace Layout { +namespace Common { + +#define NOT_COVERED ((unsigned int) -1) + +template +struct CoverageFormat1_3 +{ + friend struct Coverage; + + protected: + HBUINT16 coverageFormat; /* Format identifier--format = 1 */ + SortedArray16Of + glyphArray; /* Array of GlyphIDs--in numerical order */ + public: + DEFINE_SIZE_ARRAY (4, glyphArray); + + private: + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (glyphArray.sanitize (c)); + } + + unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + unsigned int i; + glyphArray.bfind (glyph_id, &i, HB_NOT_FOUND_STORE, NOT_COVERED); + return i; + } + + unsigned get_population () const + { + return glyphArray.len; + } + + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) + { + TRACE_SERIALIZE (this); + return_trace (glyphArray.serialize (c, glyphs)); + } + + bool intersects (const hb_set_t *glyphs) const + { + if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len) / 2) + { + for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);) + if (get_coverage (g) != NOT_COVERED) + return true; + return false; + } + + for (const auto& g : glyphArray.as_array ()) + if (glyphs->has (g)) + return true; + return false; + } + bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const + { return glyphs->has (glyphArray[index]); } + + template + void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const + { + unsigned count = glyphArray.len; + for (unsigned i = 0; i < count; i++) + if (glyphs.has (glyphArray[i])) + intersect_glyphs << glyphArray[i]; + } + + template + bool collect_coverage (set_t *glyphs) const + { return glyphs->add_sorted_array (glyphArray.as_array ()); } + + public: + /* Older compilers need this to be public. */ + struct iter_t + { + void init (const struct CoverageFormat1_3 &c_) { c = &c_; i = 0; } + bool __more__ () const { return i < c->glyphArray.len; } + void __next__ () { i++; } + hb_codepoint_t get_glyph () const { return c->glyphArray[i]; } + bool operator != (const iter_t& o) const + { return i != o.i; } + iter_t __end__ () const { iter_t it; it.init (*c); it.i = c->glyphArray.len; return it; } + + private: + const struct CoverageFormat1_3 *c; + unsigned int i; + }; + private: +}; + +} +} +} + +#endif // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat2.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat2.hh new file mode 100644 index 00000000000..d7fcc352028 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat2.hh @@ -0,0 +1,232 @@ +/* + * Copyright ยฉ 2007,2008,2009 Red Hat, Inc. + * Copyright ยฉ 2010,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH +#define OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH + +#include "RangeRecord.hh" + +namespace OT { +namespace Layout { +namespace Common { + +template +struct CoverageFormat2_4 +{ + friend struct Coverage; + + protected: + HBUINT16 coverageFormat; /* Format identifier--format = 2 */ + SortedArray16Of> + rangeRecord; /* Array of glyph ranges--ordered by + * Start GlyphID. rangeCount entries + * long */ + public: + DEFINE_SIZE_ARRAY (4, rangeRecord); + + private: + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (rangeRecord.sanitize (c)); + } + + unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + const RangeRecord &range = rangeRecord.bsearch (glyph_id); + return likely (range.first <= range.last) + ? (unsigned int) range.value + (glyph_id - range.first) + : NOT_COVERED; + } + + unsigned get_population () const + { + typename Types::large_int ret = 0; + for (const auto &r : rangeRecord) + ret += r.get_population (); + return ret > UINT_MAX ? UINT_MAX : (unsigned) ret; + } + + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + + unsigned num_ranges = 0; + hb_codepoint_t last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) + num_ranges++; + last = g; + } + + if (unlikely (!rangeRecord.serialize (c, num_ranges))) return_trace (false); + if (!num_ranges) return_trace (true); + + unsigned count = 0; + unsigned range = (unsigned) -1; + last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) + { + range++; + rangeRecord[range].first = g; + rangeRecord[range].value = count; + } + rangeRecord[range].last = g; + last = g; + count++; + } + + return_trace (true); + } + + bool intersects (const hb_set_t *glyphs) const + { + if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2) + { + for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);) + if (get_coverage (g) != NOT_COVERED) + return true; + return false; + } + + return hb_any (+ hb_iter (rangeRecord) + | hb_map ([glyphs] (const RangeRecord &range) { return range.intersects (*glyphs); })); + } + bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const + { + auto *range = rangeRecord.as_array ().bsearch (index); + if (range) + return range->intersects (*glyphs); + return false; + } + + template + void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const + { + /* Break out of loop for overlapping, broken, tables, + * to avoid fuzzer timouts. */ + hb_codepoint_t last = 0; + for (const auto& range : rangeRecord) + { + if (unlikely (range.first < last)) + break; + last = range.last; + for (hb_codepoint_t g = range.first - 1; + glyphs.next (&g) && g <= last;) + intersect_glyphs << g; + } + } + + template + bool collect_coverage (set_t *glyphs) const + { + for (const auto& range: rangeRecord) + if (unlikely (!range.collect_coverage (glyphs))) + return false; + return true; + } + + public: + /* Older compilers need this to be public. */ + struct iter_t + { + void init (const CoverageFormat2_4 &c_) + { + c = &c_; + coverage = 0; + i = 0; + j = c->rangeRecord.len ? c->rangeRecord[0].first : 0; + if (unlikely (c->rangeRecord[0].first > c->rangeRecord[0].last)) + { + /* Broken table. Skip. */ + i = c->rangeRecord.len; + j = 0; + } + } + bool __more__ () const { return i < c->rangeRecord.len; } + void __next__ () + { + if (j >= c->rangeRecord[i].last) + { + i++; + if (__more__ ()) + { + unsigned int old = coverage; + j = c->rangeRecord[i].first; + coverage = c->rangeRecord[i].value; + if (unlikely (coverage != old + 1)) + { + /* Broken table. Skip. Important to avoid DoS. + * Also, our callers depend on coverage being + * consecutive and monotonically increasing, + * ie. iota(). */ + i = c->rangeRecord.len; + j = 0; + return; + } + } + else + j = 0; + return; + } + coverage++; + j++; + } + hb_codepoint_t get_glyph () const { return j; } + bool operator != (const iter_t& o) const + { return i != o.i || j != o.j; } + iter_t __end__ () const + { + iter_t it; + it.init (*c); + it.i = c->rangeRecord.len; + it.j = 0; + return it; + } + + private: + const struct CoverageFormat2_4 *c; + unsigned int i, coverage; + hb_codepoint_t j; + }; + private: +}; + +} +} +} + +#endif // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/RangeRecord.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/RangeRecord.hh new file mode 100644 index 00000000000..a62629fad34 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/RangeRecord.hh @@ -0,0 +1,85 @@ +/* + * Copyright ยฉ 2007,2008,2009 Red Hat, Inc. + * Copyright ยฉ 2010,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_COMMON_RANGERECORD_HH +#define OT_LAYOUT_COMMON_RANGERECORD_HH + +namespace OT { +namespace Layout { +namespace Common { + +template +struct RangeRecord +{ + typename Types::HBGlyphID first; /* First GlyphID in the range */ + typename Types::HBGlyphID last; /* Last GlyphID in the range */ + HBUINT16 value; /* Value */ + + DEFINE_SIZE_STATIC (2 + 2 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + int cmp (hb_codepoint_t g) const + { return g < first ? -1 : g <= last ? 0 : +1; } + + unsigned get_population () const + { + if (unlikely (last < first)) return 0; + return (last - first + 1); + } + + bool intersects (const hb_set_t &glyphs) const + { return glyphs.intersects (first, last); } + + template + bool collect_coverage (set_t *glyphs) const + { return glyphs->add_range (first, last); } +}; + +} +} +} + +// TODO(garretrieger): This was previously implemented using +// DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (OT, RangeRecord, 9); +// but that only works when there is only a single namespace level. +// The macro should probably be fixed so it can work in this situation. +extern HB_INTERNAL const unsigned char _hb_Null_OT_RangeRecord[9]; +template +struct Null> { + static OT::Layout::Common::RangeRecord const & get_null () { + return *reinterpret_cast *> (_hb_Null_OT_RangeRecord); + } +}; + + +#endif // #ifndef OT_LAYOUT_COMMON_RANGERECORD_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GDEF/GDEF.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GDEF/GDEF.hh new file mode 100644 index 00000000000..0551fcf812a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GDEF/GDEF.hh @@ -0,0 +1,918 @@ +/* + * Copyright ยฉ 2007,2008,2009 Red Hat, Inc. + * Copyright ยฉ 2010,2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef OT_LAYOUT_GDEF_GDEF_HH +#define OT_LAYOUT_GDEF_GDEF_HH + +#include "../../../hb-ot-layout-common.hh" + +#include "../../../hb-font.hh" + + +namespace OT { + + +/* + * Attachment List Table + */ + +/* Array of contour point indices--in increasing numerical order */ +struct AttachPoint : Array16Of +{ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->serialize (c->serializer, + iter ())); + } +}; + +struct AttachList +{ + unsigned int get_attach_points (hb_codepoint_t glyph_id, + unsigned int start_offset, + unsigned int *point_count /* IN/OUT */, + unsigned int *point_array /* OUT */) const + { + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (index == NOT_COVERED) + { + if (point_count) + *point_count = 0; + return 0; + } + + const AttachPoint &points = this+attachPoint[index]; + + if (point_count) + { + + points.as_array ().sub_array (start_offset, point_count) + | hb_sink (hb_array (point_array, *point_count)) + ; + } + + return points.len; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, attachPoint) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->attachPoint, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && attachPoint.sanitize (c, this)); + } + + protected: + Offset16To + coverage; /* Offset to Coverage table -- from + * beginning of AttachList table */ + Array16OfOffset16To + attachPoint; /* Array of AttachPoint tables + * in Coverage Index order */ + public: + DEFINE_SIZE_ARRAY (4, attachPoint); +}; + +/* + * Ligature Caret Table + */ + +struct CaretValueFormat1 +{ + friend struct CaretValue; + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (true); + } + + private: + hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction) const + { + return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_x (coordinate) : font->em_scale_y (coordinate); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 caretValueFormat; /* Format identifier--format = 1 */ + FWORD coordinate; /* X or Y value, in design units */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct CaretValueFormat2 +{ + friend struct CaretValue; + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (true); + } + + private: + hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id) const + { + hb_position_t x, y; + font->get_glyph_contour_point_for_origin (glyph_id, caretValuePoint, direction, &x, &y); + return HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 caretValueFormat; /* Format identifier--format = 2 */ + HBUINT16 caretValuePoint; /* Contour point index on glyph */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct CaretValueFormat3 +{ + friend struct CaretValue; + + hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, + const VariationStore &var_store) const + { + return HB_DIRECTION_IS_HORIZONTAL (direction) ? + font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font, var_store) : + font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font, var_store); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + if (!c->serializer->embed (caretValueFormat)) return_trace (false); + if (!c->serializer->embed (coordinate)) return_trace (false); + + unsigned varidx = (this+deviceTable).get_variation_index (); + if (c->plan->layout_variation_idx_delta_map.has (varidx)) + { + int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (varidx)); + if (delta != 0) + { + if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } + } + + if (c->plan->all_axes_pinned) + return_trace (c->serializer->check_assign (out->caretValueFormat, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + + if (!c->serializer->embed (deviceTable)) + return_trace (false); + + return_trace (out->deviceTable.serialize_copy (c->serializer, deviceTable, this, c->serializer->to_bias (out), + hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { (this+deviceTable).collect_variation_indices (c); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && deviceTable.sanitize (c, this)); + } + + protected: + HBUINT16 caretValueFormat; /* Format identifier--format = 3 */ + FWORD coordinate; /* X or Y value, in design units */ + Offset16To + deviceTable; /* Offset to Device table for X or Y + * value--from beginning of CaretValue + * table */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct CaretValue +{ + hb_position_t get_caret_value (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store) const + { + switch (u.format) { + case 1: return u.format1.get_caret_value (font, direction); + case 2: return u.format2.get_caret_value (font, direction, glyph_id); + case 3: return u.format3.get_caret_value (font, direction, var_store); + default:return 0; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); + case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + switch (u.format) { + case 1: + case 2: + return; + case 3: + u.format3.collect_variation_indices (c); + return; + default: return; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + CaretValueFormat1 format1; + CaretValueFormat2 format2; + CaretValueFormat3 format3; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + +struct LigGlyph +{ + unsigned get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store, + unsigned start_offset, + unsigned *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const + { + if (caret_count) + { + + carets.as_array ().sub_array (start_offset, caret_count) + | hb_map (hb_add (this)) + | hb_map ([&] (const CaretValue &value) { return value.get_caret_value (font, direction, glyph_id, var_store); }) + | hb_sink (hb_array (caret_array, *caret_count)) + ; + } + + return carets.len; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + hb_iter (carets) + | hb_apply (subset_offset_array (c, out->carets, this)) + ; + + return_trace (bool (out->carets)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + for (const Offset16To& offset : carets.iter ()) + (this+offset).collect_variation_indices (c); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (carets.sanitize (c, this)); + } + + protected: + Array16OfOffset16To + carets; /* Offset array of CaretValue tables + * --from beginning of LigGlyph table + * --in increasing coordinate order */ + public: + DEFINE_SIZE_ARRAY (2, carets); +}; + +struct LigCaretList +{ + unsigned int get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store, + unsigned int start_offset, + unsigned int *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const + { + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (index == NOT_COVERED) + { + if (caret_count) + *caret_count = 0; + return 0; + } + const LigGlyph &lig_glyph = this+ligGlyph[index]; + return lig_glyph.get_lig_carets (font, direction, glyph_id, var_store, start_offset, caret_count, caret_array); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, ligGlyph) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ligGlyph, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+coverage, ligGlyph) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigGlyph& _) { _.collect_variation_indices (c); }) + ; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this)); + } + + protected: + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of LigCaretList table */ + Array16OfOffset16To + ligGlyph; /* Array of LigGlyph tables + * in Coverage Index order */ + public: + DEFINE_SIZE_ARRAY (4, ligGlyph); +}; + + +struct MarkGlyphSetsFormat1 +{ + bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const + { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + bool ret = true; + for (const Offset32To& offset : coverage.iter ()) + { + auto *o = out->coverage.serialize_append (c->serializer); + if (unlikely (!o)) + { + ret = false; + break; + } + + //not using o->serialize_subset (c, offset, this, out) here because + //OTS doesn't allow null offset. + //See issue: https://github.com/khaledhosny/ots/issues/172 + c->serializer->push (); + c->dispatch (this+offset); + c->serializer->add_link (*o, c->serializer->pop_pack ()); + } + + return_trace (ret && out->coverage.len); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Array16Of> + coverage; /* Array of long offsets to mark set + * coverage tables */ + public: + DEFINE_SIZE_ARRAY (4, coverage); +}; + +struct MarkGlyphSets +{ + bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.covers (set_index, glyph_id); + default:return false; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + switch (u.format) { + case 1: return_trace (u.format1.subset (c)); + default:return_trace (false); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkGlyphSetsFormat1 format1; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + + +/* + * GDEF -- Glyph Definition + * https://docs.microsoft.com/en-us/typography/opentype/spec/gdef + */ + + +template +struct GDEFVersion1_2 +{ + friend struct GDEF; + + protected: + FixedVersion<>version; /* Version of the GDEF table--currently + * 0x00010003u */ + typename Types::template OffsetTo + glyphClassDef; /* Offset to class definition table + * for glyph type--from beginning of + * GDEF header (may be Null) */ + typename Types::template OffsetTo + attachList; /* Offset to list of glyphs with + * attachment points--from beginning + * of GDEF header (may be Null) */ + typename Types::template OffsetTo + ligCaretList; /* Offset to list of positioning points + * for ligature carets--from beginning + * of GDEF header (may be Null) */ + typename Types::template OffsetTo + markAttachClassDef; /* Offset to class definition table for + * mark attachment type--from beginning + * of GDEF header (may be Null) */ + typename Types::template OffsetTo + markGlyphSetsDef; /* Offset to the table of mark set + * definitions--from beginning of GDEF + * header (may be NULL). Introduced + * in version 0x00010002. */ + Offset32To + varStore; /* Offset to the table of Item Variation + * Store--from beginning of GDEF + * header (may be NULL). Introduced + * in version 0x00010003. */ + public: + DEFINE_SIZE_MIN (4 + 4 * Types::size); + + unsigned int get_size () const + { + return min_size + + (version.to_int () >= 0x00010002u ? markGlyphSetsDef.static_size : 0) + + (version.to_int () >= 0x00010003u ? varStore.static_size : 0); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + glyphClassDef.sanitize (c, this) && + attachList.sanitize (c, this) && + ligCaretList.sanitize (c, this) && + markAttachClassDef.sanitize (c, this) && + (version.to_int () < 0x00010002u || markGlyphSetsDef.sanitize (c, this)) && + (version.to_int () < 0x00010003u || varStore.sanitize (c, this))); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this, nullptr, false, true); + bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this); + bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this); + bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true); + + bool subset_markglyphsetsdef = false; + if (version.to_int () >= 0x00010002u) + { + subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this); + } + + bool subset_varstore = false; + if (version.to_int () >= 0x00010003u) + { + if (c->plan->all_axes_pinned) + out->varStore = 0; + else + subset_varstore = out->varStore.serialize_subset (c, varStore, this, c->plan->gdef_varstore_inner_maps.as_array ()); + } + + if (subset_varstore) + { + out->version.minor = 3; + } else if (subset_markglyphsetsdef) { + out->version.minor = 2; + } else { + out->version.minor = 0; + } + + return_trace (subset_glyphclassdef || subset_attachlist || + subset_ligcaretlist || subset_markattachclassdef || + (out->version.to_int () >= 0x00010002u && subset_markglyphsetsdef) || + (out->version.to_int () >= 0x00010003u && subset_varstore)); + } +}; + +struct GDEF +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_GDEF; + + enum GlyphClasses { + UnclassifiedGlyph = 0, + BaseGlyph = 1, + LigatureGlyph = 2, + MarkGlyph = 3, + ComponentGlyph = 4 + }; + + unsigned int get_size () const + { + switch (u.version.major) { + case 1: return u.version1.get_size (); +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.get_size (); +#endif + default: return u.version.static_size; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.version.sanitize (c))) return_trace (false); + switch (u.version.major) { + case 1: return_trace (u.version1.sanitize (c)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (u.version2.sanitize (c)); +#endif + default: return_trace (true); + } + } + + bool subset (hb_subset_context_t *c) const + { + switch (u.version.major) { + case 1: return u.version1.subset (c); +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.subset (c); +#endif + default: return false; + } + } + + bool has_glyph_classes () const + { + switch (u.version.major) { + case 1: return u.version1.glyphClassDef != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.glyphClassDef != 0; +#endif + default: return false; + } + } + const ClassDef &get_glyph_class_def () const + { + switch (u.version.major) { + case 1: return this+u.version1.glyphClassDef; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.glyphClassDef; +#endif + default: return Null(ClassDef); + } + } + bool has_attach_list () const + { + switch (u.version.major) { + case 1: return u.version1.attachList != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.attachList != 0; +#endif + default: return false; + } + } + const AttachList &get_attach_list () const + { + switch (u.version.major) { + case 1: return this+u.version1.attachList; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.attachList; +#endif + default: return Null(AttachList); + } + } + bool has_lig_carets () const + { + switch (u.version.major) { + case 1: return u.version1.ligCaretList != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.ligCaretList != 0; +#endif + default: return false; + } + } + const LigCaretList &get_lig_caret_list () const + { + switch (u.version.major) { + case 1: return this+u.version1.ligCaretList; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.ligCaretList; +#endif + default: return Null(LigCaretList); + } + } + bool has_mark_attachment_types () const + { + switch (u.version.major) { + case 1: return u.version1.markAttachClassDef != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.markAttachClassDef != 0; +#endif + default: return false; + } + } + const ClassDef &get_mark_attach_class_def () const + { + switch (u.version.major) { + case 1: return this+u.version1.markAttachClassDef; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.markAttachClassDef; +#endif + default: return Null(ClassDef); + } + } + bool has_mark_glyph_sets () const + { + switch (u.version.major) { + case 1: return u.version.to_int () >= 0x00010002u && u.version1.markGlyphSetsDef != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.markGlyphSetsDef != 0; +#endif + default: return false; + } + } + const MarkGlyphSets &get_mark_glyph_sets () const + { + switch (u.version.major) { + case 1: return u.version.to_int () >= 0x00010002u ? this+u.version1.markGlyphSetsDef : Null(MarkGlyphSets); +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.markGlyphSetsDef; +#endif + default: return Null(MarkGlyphSets); + } + } + bool has_var_store () const + { + switch (u.version.major) { + case 1: return u.version.to_int () >= 0x00010003u && u.version1.varStore != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.varStore != 0; +#endif + default: return false; + } + } + const VariationStore &get_var_store () const + { + switch (u.version.major) { + case 1: return u.version.to_int () >= 0x00010003u ? this+u.version1.varStore : Null(VariationStore); +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.varStore; +#endif + default: return Null(VariationStore); + } + } + + + bool has_data () const { return u.version.to_int (); } + unsigned int get_glyph_class (hb_codepoint_t glyph) const + { return get_glyph_class_def ().get_class (glyph); } + void get_glyphs_in_class (unsigned int klass, hb_set_t *glyphs) const + { get_glyph_class_def ().collect_class (glyphs, klass); } + + unsigned int get_mark_attachment_type (hb_codepoint_t glyph) const + { return get_mark_attach_class_def ().get_class (glyph); } + + unsigned int get_attach_points (hb_codepoint_t glyph_id, + unsigned int start_offset, + unsigned int *point_count /* IN/OUT */, + unsigned int *point_array /* OUT */) const + { return get_attach_list ().get_attach_points (glyph_id, start_offset, point_count, point_array); } + + unsigned int get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + unsigned int start_offset, + unsigned int *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const + { return get_lig_caret_list ().get_lig_carets (font, + direction, glyph_id, get_var_store(), + start_offset, caret_count, caret_array); } + + bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const + { return get_mark_glyph_sets ().covers (set_index, glyph_id); } + + /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing + * glyph class and other bits, and high 8-bit the mark attachment type (if any). + * Not to be confused with lookup_props which is very similar. */ + unsigned int get_glyph_props (hb_codepoint_t glyph) const + { + unsigned int klass = get_glyph_class (glyph); + + static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH == (unsigned int) LookupFlag::IgnoreBaseGlyphs), ""); + static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE == (unsigned int) LookupFlag::IgnoreLigatures), ""); + static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_MARK == (unsigned int) LookupFlag::IgnoreMarks), ""); + + switch (klass) { + default: return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED; + case BaseGlyph: return HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH; + case LigatureGlyph: return HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE; + case MarkGlyph: + klass = get_mark_attachment_type (glyph); + return HB_OT_LAYOUT_GLYPH_PROPS_MARK | (klass << 8); + } + } + + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, + hb_face_t *face) const; + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + table = hb_sanitize_context_t ().reference_table (face); + if (unlikely (table->is_blocklisted (table.get_blob (), face))) + { + hb_blob_destroy (table.get_blob ()); + table = hb_blob_get_empty (); + } + } + ~accelerator_t () { table.destroy (); } + + hb_blob_ptr_t table; + }; + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { get_lig_caret_list ().collect_variation_indices (c); } + + void remap_layout_variation_indices (const hb_set_t *layout_variation_indices, + hb_hashmap_t> *layout_variation_idx_delta_map /* OUT */) const + { + if (!has_var_store ()) return; + if (layout_variation_indices->is_empty ()) return; + + unsigned new_major = 0, new_minor = 0; + unsigned last_major = (layout_variation_indices->get_min ()) >> 16; + for (unsigned idx : layout_variation_indices->iter ()) + { + uint16_t major = idx >> 16; + if (major >= get_var_store ().get_sub_table_count ()) break; + if (major != last_major) + { + new_minor = 0; + ++new_major; + } + + unsigned new_idx = (new_major << 16) + new_minor; + if (!layout_variation_idx_delta_map->has (idx)) + continue; + int delta = hb_second (layout_variation_idx_delta_map->get (idx)); + + layout_variation_idx_delta_map->set (idx, hb_pair_t (new_idx, delta)); + ++new_minor; + last_major = major; + } + } + + protected: + union { + FixedVersion<> version; /* Version identifier */ + GDEFVersion1_2 version1; +#ifndef HB_NO_BEYOND_64K + GDEFVersion1_2 version2; +#endif + } u; + public: + DEFINE_SIZE_MIN (4); +}; + +struct GDEF_accelerator_t : GDEF::accelerator_t { + GDEF_accelerator_t (hb_face_t *face) : GDEF::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* OT_LAYOUT_GDEF_GDEF_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Anchor.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Anchor.hh new file mode 100644 index 00000000000..49e76e77509 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Anchor.hh @@ -0,0 +1,83 @@ +#ifndef OT_LAYOUT_GPOS_ANCHOR_HH +#define OT_LAYOUT_GPOS_ANCHOR_HH + +#include "AnchorFormat1.hh" +#include "AnchorFormat2.hh" +#include "AnchorFormat3.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct Anchor +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + AnchorFormat1 format1; + AnchorFormat2 format2; + AnchorFormat3 format3; + } u; + public: + DEFINE_SIZE_UNION (2, format); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + default:return_trace (true); + } + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id, + float *x, float *y) const + { + *x = *y = 0; + switch (u.format) { + case 1: u.format1.get_anchor (c, glyph_id, x, y); return; + case 2: u.format2.get_anchor (c, glyph_id, x, y); return; + case 3: u.format3.get_anchor (c, glyph_id, x, y); return; + default: return; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + switch (u.format) { + case 1: return_trace (bool (reinterpret_cast (u.format1.copy (c->serializer)))); + case 2: + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + { + // AnchorFormat 2 just containins extra hinting information, so + // if hints are being dropped convert to format 1. + return_trace (bool (reinterpret_cast (u.format1.copy (c->serializer)))); + } + return_trace (bool (reinterpret_cast (u.format2.copy (c->serializer)))); + case 3: return_trace (u.format3.subset (c)); + default:return_trace (false); + } + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + switch (u.format) { + case 1: case 2: + return; + case 3: + u.format3.collect_variation_indices (c); + return; + default: return; + } + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHOR_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat1.hh new file mode 100644 index 00000000000..738cc31bbfa --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat1.hh @@ -0,0 +1,46 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT1_HH +#define OT_LAYOUT_GPOS_ANCHORFORMAT1_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + FWORD xCoordinate; /* Horizontal value--in design units */ + FWORD yCoordinate; /* Vertical value--in design units */ + public: + DEFINE_SIZE_STATIC (6); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED, + float *x, float *y) const + { + hb_font_t *font = c->font; + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + } + + AnchorFormat1* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + AnchorFormat1* out = c->embed (this); + if (!out) return_trace (out); + out->format = 1; + return_trace (out); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT1_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat2.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat2.hh new file mode 100644 index 00000000000..70b4d19f53c --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat2.hh @@ -0,0 +1,58 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT2_HH +#define OT_LAYOUT_GPOS_ANCHORFORMAT2_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorFormat2 +{ + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + FWORD xCoordinate; /* Horizontal value--in design units */ + FWORD yCoordinate; /* Vertical value--in design units */ + HBUINT16 anchorPoint; /* Index to glyph contour point */ + public: + DEFINE_SIZE_STATIC (8); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id, + float *x, float *y) const + { + hb_font_t *font = c->font; + +#ifdef HB_NO_HINTING + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + return; +#endif + + unsigned int x_ppem = font->x_ppem; + unsigned int y_ppem = font->y_ppem; + hb_position_t cx = 0, cy = 0; + bool ret; + + ret = (x_ppem || y_ppem) && + font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy); + *x = ret && x_ppem ? cx : font->em_fscale_x (xCoordinate); + *y = ret && y_ppem ? cy : font->em_fscale_y (yCoordinate); + } + + AnchorFormat2* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT2_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat3.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat3.hh new file mode 100644 index 00000000000..e7e3c5c6d1e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat3.hh @@ -0,0 +1,100 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT3_HH +#define OT_LAYOUT_GPOS_ANCHORFORMAT3_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorFormat3 +{ + protected: + HBUINT16 format; /* Format identifier--format = 3 */ + FWORD xCoordinate; /* Horizontal value--in design units */ + FWORD yCoordinate; /* Vertical value--in design units */ + Offset16To + xDeviceTable; /* Offset to Device table for X + * coordinate-- from beginning of + * Anchor table (may be NULL) */ + Offset16To + yDeviceTable; /* Offset to Device table for Y + * coordinate-- from beginning of + * Anchor table (may be NULL) */ + public: + DEFINE_SIZE_STATIC (10); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this)); + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED, + float *x, float *y) const + { + hb_font_t *font = c->font; + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + + if (font->x_ppem || font->num_coords) + *x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache); + if (font->y_ppem || font->num_coords) + *y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->embed (format))) return_trace (false); + if (unlikely (!c->serializer->embed (xCoordinate))) return_trace (false); + if (unlikely (!c->serializer->embed (yCoordinate))) return_trace (false); + + unsigned x_varidx = xDeviceTable ? (this+xDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX; + if (c->plan->layout_variation_idx_delta_map.has (x_varidx)) + { + int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (x_varidx)); + if (delta != 0) + { + if (!c->serializer->check_assign (out->xCoordinate, xCoordinate + delta, + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } + } + + unsigned y_varidx = yDeviceTable ? (this+yDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX; + if (c->plan->layout_variation_idx_delta_map.has (y_varidx)) + { + int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (y_varidx)); + if (delta != 0) + { + if (!c->serializer->check_assign (out->yCoordinate, yCoordinate + delta, + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } + } + + if (c->plan->all_axes_pinned) + return_trace (c->serializer->check_assign (out->format, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + + if (!c->serializer->embed (xDeviceTable)) return_trace (false); + if (!c->serializer->embed (yDeviceTable)) return_trace (false); + + out->xDeviceTable.serialize_copy (c->serializer, xDeviceTable, this, 0, hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map); + out->yDeviceTable.serialize_copy (c->serializer, yDeviceTable, this, 0, hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map); + return_trace (out); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + (this+xDeviceTable).collect_variation_indices (c); + (this+yDeviceTable).collect_variation_indices (c); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT3_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorMatrix.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorMatrix.hh new file mode 100644 index 00000000000..c442efa1eaa --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorMatrix.hh @@ -0,0 +1,77 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORMATRIX_HH +#define OT_LAYOUT_GPOS_ANCHORMATRIX_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorMatrix +{ + HBUINT16 rows; /* Number of rows */ + UnsizedArrayOf> + matrixZ; /* Matrix of offsets to Anchor tables-- + * from beginning of AnchorMatrix table */ + public: + DEFINE_SIZE_ARRAY (2, matrixZ); + + bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const + { + TRACE_SANITIZE (this); + if (!c->check_struct (this)) return_trace (false); + if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false); + unsigned int count = rows * cols; + if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false); + for (unsigned int i = 0; i < count; i++) + if (!matrixZ[i].sanitize (c, this)) return_trace (false); + return_trace (true); + } + + const Anchor& get_anchor (unsigned int row, unsigned int col, + unsigned int cols, bool *found) const + { + *found = false; + if (unlikely (row >= rows || col >= cols)) return Null (Anchor); + *found = !matrixZ[row * cols + col].is_null (); + return this+matrixZ[row * cols + col]; + } + + template + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + Iterator index_iter) const + { + for (unsigned i : index_iter) + (this+matrixZ[i]).collect_variation_indices (c); + } + + template + bool subset (hb_subset_context_t *c, + unsigned num_rows, + Iterator index_iter) const + { + TRACE_SUBSET (this); + + auto *out = c->serializer->start_embed (this); + + if (!index_iter) return_trace (false); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + out->rows = num_rows; + for (const unsigned i : index_iter) + { + auto *offset = c->serializer->embed (matrixZ[i]); + if (!offset) return_trace (false); + offset->serialize_subset (c, matrixZ[i], this); + } + + return_trace (true); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_ANCHORMATRIX_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ChainContextPos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ChainContextPos.hh new file mode 100644 index 00000000000..d551ac2a2be --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ChainContextPos.hh @@ -0,0 +1,14 @@ +#ifndef OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH +#define OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct ChainContextPos : ChainContext {}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Common.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Common.hh new file mode 100644 index 00000000000..408197454f1 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Common.hh @@ -0,0 +1,33 @@ +#ifndef OT_LAYOUT_GPOS_COMMON_HH +#define OT_LAYOUT_GPOS_COMMON_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +enum attach_type_t { + ATTACH_TYPE_NONE = 0X00, + + /* Each attachment should be either a mark or a cursive; can't be both. */ + ATTACH_TYPE_MARK = 0X01, + ATTACH_TYPE_CURSIVE = 0X02, +}; + +/* buffer **position** var allocations */ +#define attach_chain() var.i16[0] /* glyph to which this attaches to, relative to current glyphs; negative for going back, positive for forward. */ +#define attach_type() var.u8[2] /* attachment type */ +/* Note! if attach_chain() is zero, the value of attach_type() is irrelevant. */ + +template +static void SinglePos_serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + const hb_hashmap_t> *layout_variation_idx_delta_map, + bool all_axes_pinned); + + +} +} +} + +#endif // OT_LAYOUT_GPOS_COMMON_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ContextPos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ContextPos.hh new file mode 100644 index 00000000000..2a01eaa3a68 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ContextPos.hh @@ -0,0 +1,14 @@ +#ifndef OT_LAYOUT_GPOS_CONTEXTPOS_HH +#define OT_LAYOUT_GPOS_CONTEXTPOS_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct ContextPos : Context {}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CONTEXTPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePos.hh new file mode 100644 index 00000000000..0105a9b8542 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePos.hh @@ -0,0 +1,35 @@ +#ifndef OT_LAYOUT_GPOS_CURSIVEPOS_HH +#define OT_LAYOUT_GPOS_CURSIVEPOS_HH + +#include "CursivePosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct CursivePos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + CursivePosFormat1 format1; + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CURSIVEPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePosFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePosFormat1.hh new file mode 100644 index 00000000000..ff255e090a2 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePosFormat1.hh @@ -0,0 +1,301 @@ +#ifndef OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH + +#include "Anchor.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct EntryExitRecord +{ + friend struct CursivePosFormat1; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *src_base) const + { + (src_base+entryAnchor).collect_variation_indices (c); + (src_base+exitAnchor).collect_variation_indices (c); + } + + EntryExitRecord* subset (hb_subset_context_t *c, + const void *src_base) const + { + TRACE_SERIALIZE (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + out->entryAnchor.serialize_subset (c, entryAnchor, src_base); + out->exitAnchor.serialize_subset (c, exitAnchor, src_base); + return_trace (out); + } + + protected: + Offset16To + entryAnchor; /* Offset to EntryAnchor table--from + * beginning of CursivePos + * subtable--may be NULL */ + Offset16To + exitAnchor; /* Offset to ExitAnchor table--from + * beginning of CursivePos + * subtable--may be NULL */ + public: + DEFINE_SIZE_STATIC (4); +}; + +static void +reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent) { + int chain = pos[i].attach_chain(), type = pos[i].attach_type(); + if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE))) + return; + + pos[i].attach_chain() = 0; + + unsigned int j = (int) i + chain; + + /* Stop if we see new parent in the chain. */ + if (j == new_parent) + return; + + reverse_cursive_minor_offset (pos, j, direction, new_parent); + + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + pos[j].y_offset = -pos[i].y_offset; + else + pos[j].x_offset = -pos[i].x_offset; + + pos[j].attach_chain() = -chain; + pos[j].attach_type() = type; +} + + +struct CursivePosFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + Array16Of + entryExitRecord; /* Array of EntryExit records--in + * Coverage Index order */ + public: + DEFINE_SIZE_ARRAY (6, entryExitRecord); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+coverage, entryExitRecord) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const EntryExitRecord& record) { record.collect_variation_indices (c, this); }) + ; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } + + const Coverage &get_coverage () const { return this+coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + + const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage (buffer->cur().codepoint)]; + if (!this_record.entryAnchor) return_trace (false); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + unsigned unsafe_from; + if (!skippy_iter.prev (&unsafe_from)) + { + buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); + return_trace (false); + } + + const EntryExitRecord &prev_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)]; + if (!prev_record.exitAnchor) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + unsigned int i = skippy_iter.idx; + unsigned int j = buffer->idx; + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "cursive attaching glyph at %u to glyph at %u", + i, j); + } + + buffer->unsafe_to_break (i, j + 1); + float entry_x, entry_y, exit_x, exit_y; + (this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y); + (this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y); + + hb_glyph_position_t *pos = buffer->pos; + + hb_position_t d; + /* Main-direction adjustment */ + switch (c->direction) { + case HB_DIRECTION_LTR: + pos[i].x_advance = roundf (exit_x) + pos[i].x_offset; + + d = roundf (entry_x) + pos[j].x_offset; + pos[j].x_advance -= d; + pos[j].x_offset -= d; + break; + case HB_DIRECTION_RTL: + d = roundf (exit_x) + pos[i].x_offset; + pos[i].x_advance -= d; + pos[i].x_offset -= d; + + pos[j].x_advance = roundf (entry_x) + pos[j].x_offset; + break; + case HB_DIRECTION_TTB: + pos[i].y_advance = roundf (exit_y) + pos[i].y_offset; + + d = roundf (entry_y) + pos[j].y_offset; + pos[j].y_advance -= d; + pos[j].y_offset -= d; + break; + case HB_DIRECTION_BTT: + d = roundf (exit_y) + pos[i].y_offset; + pos[i].y_advance -= d; + pos[i].y_offset -= d; + + pos[j].y_advance = roundf (entry_y); + break; + case HB_DIRECTION_INVALID: + default: + break; + } + + /* Cross-direction adjustment */ + + /* We attach child to parent (think graph theory and rooted trees whereas + * the root stays on baseline and each node aligns itself against its + * parent. + * + * Optimize things for the case of RightToLeft, as that's most common in + * Arabic. */ + unsigned int child = i; + unsigned int parent = j; + hb_position_t x_offset = entry_x - exit_x; + hb_position_t y_offset = entry_y - exit_y; + if (!(c->lookup_props & LookupFlag::RightToLeft)) + { + unsigned int k = child; + child = parent; + parent = k; + x_offset = -x_offset; + y_offset = -y_offset; + } + + /* If child was already connected to someone else, walk through its old + * chain and reverse the link direction, such that the whole tree of its + * previous connection now attaches to new parent. Watch out for case + * where new parent is on the path from old chain... + */ + reverse_cursive_minor_offset (pos, child, c->direction, parent); + + pos[child].attach_type() = ATTACH_TYPE_CURSIVE; + pos[child].attach_chain() = (int) parent - (int) child; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) + pos[child].y_offset = y_offset; + else + pos[child].x_offset = x_offset; + + /* If parent was attached to child, separate them. + * https://github.com/harfbuzz/harfbuzz/issues/2469 + */ + if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain())) + { + pos[parent].attach_chain() = 0; + if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) + pos[parent].y_offset = 0; + else + pos[parent].x_offset = 0; + } + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "cursive attached glyph at %u to glyph at %u", + i, j); + } + + buffer->idx++; + return_trace (true); + } + + template + void serialize (hb_subset_context_t *c, + Iterator it, + const void *src_base) + { + if (unlikely (!c->serializer->extend_min ((*this)))) return; + this->format = 1; + this->entryExitRecord.len = it.len (); + + for (const EntryExitRecord& entry_record : + it + | hb_map (hb_second)) + entry_record.subset (c, src_base); + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize_serialize (c->serializer, glyphs); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + + auto it = + + hb_zip (this+coverage, entryExitRecord) + | hb_filter (glyphset, hb_first) + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_pair_t + { return hb_pair (glyph_map[p.first], p.second);}) + ; + + bool ret = bool (it); + out->serialize (c, it, this); + return_trace (ret); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ExtensionPos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ExtensionPos.hh new file mode 100644 index 00000000000..d1808adab40 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ExtensionPos.hh @@ -0,0 +1,17 @@ +#ifndef OT_LAYOUT_GPOS_EXTENSIONPOS_HH +#define OT_LAYOUT_GPOS_EXTENSIONPOS_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct ExtensionPos : Extension +{ + typedef struct PosLookupSubTable SubTable; +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_EXTENSIONPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/GPOS.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/GPOS.hh new file mode 100644 index 00000000000..9493ec987e8 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/GPOS.hh @@ -0,0 +1,171 @@ +#ifndef OT_LAYOUT_GPOS_GPOS_HH +#define OT_LAYOUT_GPOS_GPOS_HH + +#include "../../../hb-ot-layout-common.hh" +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" +#include "PosLookup.hh" + +namespace OT { + +using Layout::GPOS_impl::PosLookup; + +namespace Layout { + +static void +propagate_attachment_offsets (hb_glyph_position_t *pos, + unsigned int len, + unsigned int i, + hb_direction_t direction, + unsigned nesting_level = HB_MAX_NESTING_LEVEL); + +/* + * GPOS -- Glyph Positioning + * https://docs.microsoft.com/en-us/typography/opentype/spec/gpos + */ + +struct GPOS : GSUBGPOS +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS; + + using Lookup = PosLookup; + + const PosLookup& get_lookup (unsigned int i) const + { return static_cast (GSUBGPOS::get_lookup (i)); } + + static inline void position_start (hb_font_t *font, hb_buffer_t *buffer); + static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer); + static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer); + + bool subset (hb_subset_context_t *c) const + { + hb_subset_layout_context_t l (c, tableTag); + return GSUBGPOS::subset (&l); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (GSUBGPOS::sanitize (c)); + } + + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, + hb_face_t *face) const; + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++) + { + if (!c->gpos_lookups->has (i)) continue; + const PosLookup &l = get_lookup (i); + l.dispatch (c); + } + } + + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const + { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); } + + typedef GSUBGPOS::accelerator_t accelerator_t; +}; + + +static void +propagate_attachment_offsets (hb_glyph_position_t *pos, + unsigned int len, + unsigned int i, + hb_direction_t direction, + unsigned nesting_level) +{ + /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate + * offset of glyph they are attached to. */ + int chain = pos[i].attach_chain(), type = pos[i].attach_type(); + if (likely (!chain)) + return; + + pos[i].attach_chain() = 0; + + unsigned int j = (int) i + chain; + + if (unlikely (j >= len)) + return; + + if (unlikely (!nesting_level)) + return; + + propagate_attachment_offsets (pos, len, j, direction, nesting_level - 1); + + assert (!!(type & GPOS_impl::ATTACH_TYPE_MARK) ^ !!(type & GPOS_impl::ATTACH_TYPE_CURSIVE)); + + if (type & GPOS_impl::ATTACH_TYPE_CURSIVE) + { + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + pos[i].y_offset += pos[j].y_offset; + else + pos[i].x_offset += pos[j].x_offset; + } + else /*if (type & GPOS_impl::ATTACH_TYPE_MARK)*/ + { + pos[i].x_offset += pos[j].x_offset; + pos[i].y_offset += pos[j].y_offset; + + assert (j < i); + if (HB_DIRECTION_IS_FORWARD (direction)) + for (unsigned int k = j; k < i; k++) { + pos[i].x_offset -= pos[k].x_advance; + pos[i].y_offset -= pos[k].y_advance; + } + else + for (unsigned int k = j + 1; k < i + 1; k++) { + pos[i].x_offset += pos[k].x_advance; + pos[i].y_offset += pos[k].y_advance; + } + } +} + +void +GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0; +} + +void +GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED) +{ + //_hb_buffer_assert_gsubgpos_vars (buffer); +} + +void +GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) +{ + _hb_buffer_assert_gsubgpos_vars (buffer); + + unsigned int len; + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len); + hb_direction_t direction = buffer->props.direction; + + /* Handle attachments */ + if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT) + for (unsigned i = 0; i < len; i++) + propagate_attachment_offsets (pos, len, i, direction); + + if (unlikely (font->slant)) + { + for (unsigned i = 0; i < len; i++) + if (unlikely (pos[i].y_offset)) + pos[i].x_offset += _hb_roundf (font->slant_xy * pos[i].y_offset); + } +} + +} + +struct GPOS_accelerator_t : Layout::GPOS::accelerator_t { + GPOS_accelerator_t (hb_face_t *face) : Layout::GPOS::accelerator_t (face) {} +}; + +} + +#endif /* OT_LAYOUT_GPOS_GPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/LigatureArray.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/LigatureArray.hh new file mode 100644 index 00000000000..a2d807cc320 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/LigatureArray.hh @@ -0,0 +1,56 @@ +#ifndef OT_LAYOUT_GPOS_LIGATUREARRAY_HH +#define OT_LAYOUT_GPOS_LIGATUREARRAY_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +typedef AnchorMatrix LigatureAttach; /* component-major-- + * in order of writing direction--, + * mark-minor-- + * ordered by class--zero-based. */ + +/* Array of LigatureAttach tables ordered by LigatureCoverage Index */ +struct LigatureArray : List16OfOffset16To +{ + template + bool subset (hb_subset_context_t *c, + Iterator coverage, + unsigned class_count, + const hb_map_t *klass_mapping) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + for (const auto _ : + hb_zip (coverage, *this) + | hb_filter (glyphset, hb_first)) + { + auto *matrix = out->serialize_append (c->serializer); + if (unlikely (!matrix)) return_trace (false); + + const LigatureAttach& src = (this + _.second); + auto indexes = + + hb_range (src.rows * class_count) + | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); }) + ; + matrix->serialize_subset (c, + _.second, + this, + src.rows, + indexes); + } + return_trace (this->len); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_LIGATUREARRAY_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkArray.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkArray.hh new file mode 100644 index 00000000000..ff43ffb8c57 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkArray.hh @@ -0,0 +1,128 @@ +#ifndef OT_LAYOUT_GPOS_MARKARRAY_HH +#define OT_LAYOUT_GPOS_MARKARRAY_HH + +#include "AnchorMatrix.hh" +#include "MarkRecord.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkArray : Array16Of /* Array of MarkRecords--in Coverage order */ +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (Array16Of::sanitize (c, this)); + } + + bool apply (hb_ot_apply_context_t *c, + unsigned int mark_index, unsigned int glyph_index, + const AnchorMatrix &anchors, unsigned int class_count, + unsigned int glyph_pos) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + const MarkRecord &record = Array16Of::operator[](mark_index); + unsigned int mark_class = record.klass; + + const Anchor& mark_anchor = this + record.markAnchor; + bool found; + const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found); + /* If this subtable doesn't have an anchor for this base and this class, + * return false such that the subsequent subtables have a chance at it. */ + if (unlikely (!found)) return_trace (false); + + float mark_x, mark_y, base_x, base_y; + + buffer->unsafe_to_break (glyph_pos, buffer->idx + 1); + mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y); + glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "attaching mark glyph at %u to glyph at %u", + c->buffer->idx, glyph_pos); + } + + hb_glyph_position_t &o = buffer->cur_pos(); + o.x_offset = roundf (base_x - mark_x); + o.y_offset = roundf (base_y - mark_y); + o.attach_type() = ATTACH_TYPE_MARK; + o.attach_chain() = (int) glyph_pos - (int) buffer->idx; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "attached mark glyph at %u to glyph at %u", + c->buffer->idx, glyph_pos); + } + + buffer->idx++; + return_trace (true); + } + + template + bool subset (hb_subset_context_t *c, + Iterator coverage, + const hb_map_t *klass_mapping) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + + auto* out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + auto mark_iter = + + hb_zip (coverage, this->iter ()) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + ; + + unsigned new_length = 0; + for (const auto& mark_record : mark_iter) { + if (unlikely (!mark_record.subset (c, this, klass_mapping))) + return_trace (false); + new_length++; + } + + if (unlikely (!c->serializer->check_assign (out->len, new_length, + HB_SERIALIZE_ERROR_ARRAY_OVERFLOW))) + return_trace (false); + + return_trace (true); + } +}; + +HB_INTERNAL inline +void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage, + const MarkArray &mark_array, + const hb_set_t &glyphset, + hb_map_t* klass_mapping /* INOUT */) +{ + hb_set_t orig_classes; + + + hb_zip (mark_coverage, mark_array) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + | hb_map (&MarkRecord::get_class) + | hb_sink (orig_classes) + ; + + unsigned idx = 0; + for (auto klass : orig_classes.iter ()) + { + if (klass_mapping->has (klass)) continue; + klass_mapping->set (klass, idx); + idx++; + } +} + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKARRAY_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePos.hh new file mode 100644 index 00000000000..cd2fc7ccfdd --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePos.hh @@ -0,0 +1,41 @@ +#ifndef OT_LAYOUT_GPOS_MARKBASEPOS_HH +#define OT_LAYOUT_GPOS_MARKBASEPOS_HH + +#include "MarkBasePosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkBasePos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkBasePosFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + MarkBasePosFormat1_2 format2; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKBASEPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePosFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePosFormat1.hh new file mode 100644 index 00000000000..eb4712049b1 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePosFormat1.hh @@ -0,0 +1,244 @@ +#ifndef OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH + +#include "MarkArray.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +typedef AnchorMatrix BaseArray; /* base-major-- + * in order of BaseCoverage Index--, + * mark-minor-- + * ordered by class--zero-based. */ + +template +struct MarkBasePosFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + markCoverage; /* Offset to MarkCoverage table--from + * beginning of MarkBasePos subtable */ + typename Types::template OffsetTo + baseCoverage; /* Offset to BaseCoverage table--from + * beginning of MarkBasePos subtable */ + HBUINT16 classCount; /* Number of classes defined for marks */ + typename Types::template OffsetTo + markArray; /* Offset to MarkArray table--from + * beginning of MarkBasePos subtable */ + typename Types::template OffsetTo + baseArray; /* Offset to BaseArray table--from + * beginning of MarkBasePos subtable */ + + public: + DEFINE_SIZE_STATIC (4 + 4 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + markCoverage.sanitize (c, this) && + baseCoverage.sanitize (c, this) && + markArray.sanitize (c, this) && + baseArray.sanitize (c, this, (unsigned int) classCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+markCoverage).intersects (glyphs) && + (this+baseCoverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping); + + unsigned basecount = (this+baseArray).rows; + auto base_iter = + + hb_zip (this+baseCoverage, hb_range (basecount)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + hb_sorted_vector_t base_indexes; + for (const unsigned row : base_iter) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (base_indexes) + ; + } + (this+baseArray).collect_variation_indices (c, base_indexes.iter ()); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return; + if (unlikely (!(this+baseCoverage).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+markCoverage; } + + static inline bool accept (hb_buffer_t *buffer, unsigned idx) + { + /* We only want to attach to the first of a MultipleSubst sequence. + * https://github.com/harfbuzz/harfbuzz/issues/740 + * Reject others... + * ...but stop if we find a mark in the MultipleSubst sequence: + * https://github.com/harfbuzz/harfbuzz/issues/1020 */ + return !_hb_glyph_info_multiplied (&buffer->info[idx]) || + 0 == _hb_glyph_info_get_lig_comp (&buffer->info[idx]) || + (idx == 0 || + _hb_glyph_info_is_mark (&buffer->info[idx - 1]) || + !_hb_glyph_info_multiplied (&buffer->info[idx - 1]) || + _hb_glyph_info_get_lig_id (&buffer->info[idx]) != + _hb_glyph_info_get_lig_id (&buffer->info[idx - 1]) || + _hb_glyph_info_get_lig_comp (&buffer->info[idx]) != + _hb_glyph_info_get_lig_comp (&buffer->info[idx - 1]) + 1 + ); + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); + if (likely (mark_index == NOT_COVERED)) return_trace (false); + + /* Now we search backwards for a non-mark glyph. + * We don't use skippy_iter.prev() to avoid O(n^2) behavior. */ + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); + + if (c->last_base_until > buffer->idx) + { + c->last_base_until = 0; + c->last_base = -1; + } + unsigned j; + for (j = buffer->idx; j > c->last_base_until; j--) + { + auto match = skippy_iter.match (buffer->info[j - 1]); + if (match == skippy_iter.MATCH) + { + // https://github.com/harfbuzz/harfbuzz/issues/4124 + if (!accept (buffer, j - 1) && + NOT_COVERED == (this+baseCoverage).get_coverage (buffer->info[j - 1].codepoint)) + match = skippy_iter.SKIP; + } + if (match == skippy_iter.MATCH) + { + c->last_base = (signed) j - 1; + break; + } + } + c->last_base_until = buffer->idx; + if (c->last_base == -1) + { + buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1); + return_trace (false); + } + + unsigned idx = (unsigned) c->last_base; + + /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */ + //if (!_hb_glyph_info_is_base_glyph (&buffer->info[idx])) { return_trace (false); } + + unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[idx].codepoint); + if (base_index == NOT_COVERED) + { + buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1); + return_trace (false); + } + + return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, idx)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark_iter = + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (glyphset, hb_first) + ; + + hb_sorted_vector_t new_coverage; + + mark_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->markCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + out->markArray.serialize_subset (c, markArray, this, + (this+markCoverage).iter (), + &klass_mapping); + + unsigned basecount = (this+baseArray).rows; + auto base_iter = + + hb_zip (this+baseCoverage, hb_range (basecount)) + | hb_filter (glyphset, hb_first) + ; + + new_coverage.reset (); + + base_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + hb_sorted_vector_t base_indexes; + for (const unsigned row : + base_iter + | hb_map (hb_second)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (base_indexes) + ; + } + + out->baseArray.serialize_subset (c, baseArray, this, + base_iter.len (), + base_indexes.iter ()); + + return_trace (true); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPos.hh new file mode 100644 index 00000000000..739c325411c --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPos.hh @@ -0,0 +1,41 @@ +#ifndef OT_LAYOUT_GPOS_MARKLIGPOS_HH +#define OT_LAYOUT_GPOS_MARKLIGPOS_HH + +#include "MarkLigPosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkLigPos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkLigPosFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + MarkLigPosFormat1_2 format2; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKLIGPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPosFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPosFormat1.hh new file mode 100644 index 00000000000..92e83a0e994 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPosFormat1.hh @@ -0,0 +1,223 @@ +#ifndef OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH + +#include "LigatureArray.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct MarkLigPosFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + markCoverage; /* Offset to Mark Coverage table--from + * beginning of MarkLigPos subtable */ + typename Types::template OffsetTo + ligatureCoverage; /* Offset to Ligature Coverage + * table--from beginning of MarkLigPos + * subtable */ + HBUINT16 classCount; /* Number of defined mark classes */ + typename Types::template OffsetTo + markArray; /* Offset to MarkArray table--from + * beginning of MarkLigPos subtable */ + typename Types::template OffsetTo + ligatureArray; /* Offset to LigatureArray table--from + * beginning of MarkLigPos subtable */ + public: + DEFINE_SIZE_STATIC (4 + 4 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + markCoverage.sanitize (c, this) && + ligatureCoverage.sanitize (c, this) && + markArray.sanitize (c, this) && + ligatureArray.sanitize (c, this, (unsigned int) classCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+markCoverage).intersects (glyphs) && + (this+ligatureCoverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping); + + unsigned ligcount = (this+ligatureArray).len; + auto lig_iter = + + hb_zip (this+ligatureCoverage, hb_range (ligcount)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + const LigatureArray& lig_array = this+ligatureArray; + for (const unsigned i : lig_iter) + { + hb_sorted_vector_t lig_indexes; + unsigned row_count = lig_array[i].rows; + for (unsigned row : + hb_range (row_count)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (lig_indexes) + ; + } + + lig_array[i].collect_variation_indices (c, lig_indexes.iter ()); + } + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return; + if (unlikely (!(this+ligatureCoverage).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+markCoverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); + if (likely (mark_index == NOT_COVERED)) return_trace (false); + + /* Now we search backwards for a non-mark glyph */ + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); + + if (c->last_base_until > buffer->idx) + { + c->last_base_until = 0; + c->last_base = -1; + } + unsigned j; + for (j = buffer->idx; j > c->last_base_until; j--) + { + auto match = skippy_iter.match (buffer->info[j - 1]); + if (match == skippy_iter.MATCH) + { + c->last_base = (signed) j - 1; + break; + } + } + c->last_base_until = buffer->idx; + if (c->last_base == -1) + { + buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1); + return_trace (false); + } + + unsigned idx = (unsigned) c->last_base; + + /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */ + //if (!_hb_glyph_info_is_ligature (&buffer->info[idx])) { return_trace (false); } + + unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[idx].codepoint); + if (lig_index == NOT_COVERED) + { + buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1); + return_trace (false); + } + + const LigatureArray& lig_array = this+ligatureArray; + const LigatureAttach& lig_attach = lig_array[lig_index]; + + /* Find component to attach to */ + unsigned int comp_count = lig_attach.rows; + if (unlikely (!comp_count)) + { + buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1); + return_trace (false); + } + + /* We must now check whether the ligature ID of the current mark glyph + * is identical to the ligature ID of the found ligature. If yes, we + * can directly use the component index. If not, we attach the mark + * glyph to the last component of the ligature. */ + unsigned int comp_index; + unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[idx]); + unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur()); + if (lig_id && lig_id == mark_id && mark_comp > 0) + comp_index = hb_min (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1; + else + comp_index = comp_count - 1; + + return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, idx)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark_iter = + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (glyphset, hb_first) + ; + + auto new_mark_coverage = + + mark_iter + | hb_map_retains_sorting (hb_first) + | hb_map_retains_sorting (glyph_map) + ; + + if (!out->markCoverage.serialize_serialize (c->serializer, new_mark_coverage)) + return_trace (false); + + out->markArray.serialize_subset (c, markArray, this, + (this+markCoverage).iter (), + &klass_mapping); + + auto new_ligature_coverage = + + hb_iter (this + ligatureCoverage) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + if (!out->ligatureCoverage.serialize_serialize (c->serializer, new_ligature_coverage)) + return_trace (false); + + out->ligatureArray.serialize_subset (c, ligatureArray, this, + hb_iter (this+ligatureCoverage), classCount, &klass_mapping); + + return_trace (true); + } + +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPos.hh new file mode 100644 index 00000000000..cddd2a3d507 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPos.hh @@ -0,0 +1,42 @@ +#ifndef OT_LAYOUT_GPOS_MARKMARKPOS_HH +#define OT_LAYOUT_GPOS_MARKMARKPOS_HH + +#include "MarkMarkPosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkMarkPos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkMarkPosFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + MarkMarkPosFormat1_2 format2; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKMARKPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh new file mode 100644 index 00000000000..fbcebb80441 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh @@ -0,0 +1,228 @@ +#ifndef OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH + +#include "MarkMarkPosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +typedef AnchorMatrix Mark2Array; /* mark2-major-- + * in order of Mark2Coverage Index--, + * mark1-minor-- + * ordered by class--zero-based. */ + +template +struct MarkMarkPosFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + mark1Coverage; /* Offset to Combining Mark1 Coverage + * table--from beginning of MarkMarkPos + * subtable */ + typename Types::template OffsetTo + mark2Coverage; /* Offset to Combining Mark2 Coverage + * table--from beginning of MarkMarkPos + * subtable */ + HBUINT16 classCount; /* Number of defined mark classes */ + typename Types::template OffsetTo + mark1Array; /* Offset to Mark1Array table--from + * beginning of MarkMarkPos subtable */ + typename Types::template OffsetTo + mark2Array; /* Offset to Mark2Array table--from + * beginning of MarkMarkPos subtable */ + public: + DEFINE_SIZE_STATIC (4 + 4 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + mark1Coverage.sanitize (c, this) && + mark2Coverage.sanitize (c, this) && + mark1Array.sanitize (c, this) && + mark2Array.sanitize (c, this, (unsigned int) classCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+mark1Coverage).intersects (glyphs) && + (this+mark2Coverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+mark1Coverage, this+mark1Array) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+mark1Array)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, *c->glyph_set, &klass_mapping); + + unsigned mark2_count = (this+mark2Array).rows; + auto mark2_iter = + + hb_zip (this+mark2Coverage, hb_range (mark2_count)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + hb_sorted_vector_t mark2_indexes; + for (const unsigned row : mark2_iter) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (mark2_indexes) + ; + } + (this+mark2Array).collect_variation_indices (c, mark2_indexes.iter ()); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+mark1Coverage).collect_coverage (c->input))) return; + if (unlikely (!(this+mark2Coverage).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+mark1Coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark1_index = (this+mark1Coverage).get_coverage (buffer->cur().codepoint); + if (likely (mark1_index == NOT_COVERED)) return_trace (false); + + /* now we search backwards for a suitable mark glyph until a non-mark glyph */ + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + skippy_iter.set_lookup_props (c->lookup_props & ~(uint32_t)LookupFlag::IgnoreFlags); + unsigned unsafe_from; + if (!skippy_iter.prev (&unsafe_from)) + { + buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); + return_trace (false); + } + + if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx])) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + unsigned int j = skippy_iter.idx; + + unsigned int id1 = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int id2 = _hb_glyph_info_get_lig_id (&buffer->info[j]); + unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur()); + unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]); + + if (likely (id1 == id2)) + { + if (id1 == 0) /* Marks belonging to the same base. */ + goto good; + else if (comp1 == comp2) /* Marks belonging to the same ligature component. */ + goto good; + } + else + { + /* If ligature ids don't match, it may be the case that one of the marks + * itself is a ligature. In which case match. */ + if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2)) + goto good; + } + + /* Didn't match. */ + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + + good: + unsigned int mark2_index = (this+mark2Coverage).get_coverage (buffer->info[j].codepoint); + if (mark2_index == NOT_COVERED) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark1_iter = + + hb_zip (this+mark1Coverage, this+mark1Array) + | hb_filter (glyphset, hb_first) + ; + + hb_sorted_vector_t new_coverage; + + mark1_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->mark1Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + out->mark1Array.serialize_subset (c, mark1Array, this, + (this+mark1Coverage).iter (), + &klass_mapping); + + unsigned mark2count = (this+mark2Array).rows; + auto mark2_iter = + + hb_zip (this+mark2Coverage, hb_range (mark2count)) + | hb_filter (glyphset, hb_first) + ; + + new_coverage.reset (); + + mark2_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + hb_sorted_vector_t mark2_indexes; + for (const unsigned row : + mark2_iter + | hb_map (hb_second)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (mark2_indexes) + ; + } + + out->mark2Array.serialize_subset (c, mark2Array, this, mark2_iter.len (), mark2_indexes.iter ()); + + return_trace (true); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkRecord.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkRecord.hh new file mode 100644 index 00000000000..a7d489d2a51 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkRecord.hh @@ -0,0 +1,52 @@ +#ifndef OT_LAYOUT_GPOS_MARKRECORD_HH +#define OT_LAYOUT_GPOS_MARKRECORD_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkRecord +{ + friend struct MarkArray; + + public: + HBUINT16 klass; /* Class defined for this mark */ + Offset16To + markAnchor; /* Offset to Anchor table--from + * beginning of MarkArray table */ + public: + DEFINE_SIZE_STATIC (4); + + unsigned get_class () const { return (unsigned) klass; } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && markAnchor.sanitize (c, base)); + } + + MarkRecord *subset (hb_subset_context_t *c, + const void *src_base, + const hb_map_t *klass_mapping) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + out->klass = klass_mapping->get (klass); + out->markAnchor.serialize_subset (c, markAnchor, src_base); + return_trace (out); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *src_base) const + { + (src_base+markAnchor).collect_variation_indices (c); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKRECORD_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPos.hh new file mode 100644 index 00000000000..c13d4f48941 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPos.hh @@ -0,0 +1,46 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOS_HH +#define OT_LAYOUT_GPOS_PAIRPOS_HH + +#include "PairPosFormat1.hh" +#include "PairPosFormat2.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PairPos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + PairPosFormat1_3 format1; + PairPosFormat2_4 format2; +#ifndef HB_NO_BEYOND_64K + PairPosFormat1_3 format3; + PairPosFormat2_4 format4; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOS_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat1.hh new file mode 100644 index 00000000000..4dada1c8301 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat1.hh @@ -0,0 +1,217 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH + +#include "PairSet.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct PairPosFormat1_3 +{ + using PairSet = GPOS_impl::PairSet; + using PairValueRecord = GPOS_impl::PairValueRecord; + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat[2]; /* [0] Defines the types of data in + * ValueRecord1--for the first glyph + * in the pair--may be zero (0) */ + /* [1] Defines the types of data in + * ValueRecord2--for the second glyph + * in the pair--may be zero (0) */ + Array16Of> + pairSet; /* Array of PairSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (8 + Types::size, pairSet); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + if (!c->check_struct (this)) return_trace (false); + + unsigned int len1 = valueFormat[0].get_len (); + unsigned int len2 = valueFormat[1].get_len (); + typename PairSet::sanitize_closure_t closure = + { + valueFormat, + len1, + PairSet::get_size (len1, len2) + }; + + return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure)); + } + + bool intersects (const hb_set_t *glyphs) const + { + auto &cov = this+coverage; + + if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len) / 4) + { + for (hb_codepoint_t g : glyphs->iter()) + { + unsigned i = cov.get_coverage (g); + if ((this+pairSet[i]).intersects (glyphs, valueFormat)) + return true; + } + return false; + } + + return + + hb_zip (cov, pairSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map ([glyphs, this] (const typename Types::template OffsetTo &_) + { return (this+_).intersects (glyphs, valueFormat); }) + | hb_any + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if ((!valueFormat[0].has_device ()) && (!valueFormat[1].has_device ())) return; + + auto it = + + hb_zip (this+coverage, pairSet) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + if (!it) return; + + it + | hb_map (hb_add (this)) + | hb_apply ([&] (const PairSet& _) { _.collect_variation_indices (c, valueFormat); }) + ; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + unsigned int count = pairSet.len; + for (unsigned int i = 0; i < count; i++) + (this+pairSet[i]).collect_glyphs (c, valueFormat); + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + unsigned unsafe_to; + if (!skippy_iter.next (&unsafe_to)) + { + buffer->unsafe_to_concat (buffer->idx, unsafe_to); + return_trace (false); + } + + return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + out->valueFormat[0] = valueFormat[0]; + out->valueFormat[1] = valueFormat[1]; + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + { + hb_pair_t newFormats = compute_effective_value_formats (glyphset); + out->valueFormat[0] = newFormats.first; + out->valueFormat[1] = newFormats.second; + } + + if (c->plan->all_axes_pinned) + { + out->valueFormat[0] = out->valueFormat[0].drop_device_table_flags (); + out->valueFormat[1] = out->valueFormat[1].drop_device_table_flags (); + } + + hb_sorted_vector_t new_coverage; + + + hb_zip (this+coverage, pairSet) + | hb_filter (glyphset, hb_first) + | hb_filter ([this, c, out] (const typename Types::template OffsetTo& _) + { + auto snap = c->serializer->snapshot (); + auto *o = out->pairSet.serialize_append (c->serializer); + if (unlikely (!o)) return false; + bool ret = o->serialize_subset (c, _, this, valueFormat, out->valueFormat); + if (!ret) + { + out->pairSet.pop (); + c->serializer->revert (snap); + } + return ret; + }, + hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + + return_trace (bool (new_coverage)); + } + + + hb_pair_t compute_effective_value_formats (const hb_set_t& glyphset) const + { + unsigned record_size = PairSet::get_size (valueFormat); + + unsigned format1 = 0; + unsigned format2 = 0; + for (const auto & _ : + + hb_zip (this+coverage, pairSet) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + ) + { + const PairSet& set = (this + _); + const PairValueRecord *record = &set.firstPairValueRecord; + + unsigned count = set.len; + for (unsigned i = 0; i < count; i++) + { + if (record->intersects (glyphset)) + { + format1 = format1 | valueFormat[0].get_effective_format (record->get_values_1 ()); + format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0])); + } + record = &StructAtOffset (record, record_size); + } + + if (format1 == valueFormat[0] && format2 == valueFormat[1]) + break; + } + + return hb_pair (format1, format2); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat2.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat2.hh new file mode 100644 index 00000000000..de15a29e3cf --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat2.hh @@ -0,0 +1,351 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH +#define OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH + +#include "ValueFormat.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +template +struct PairPosFormat2_4 +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat1; /* ValueRecord definition--for the + * first glyph of the pair--may be zero + * (0) */ + ValueFormat valueFormat2; /* ValueRecord definition--for the + * second glyph of the pair--may be + * zero (0) */ + typename Types::template OffsetTo + classDef1; /* Offset to ClassDef table--from + * beginning of PairPos subtable--for + * the first glyph of the pair */ + typename Types::template OffsetTo + classDef2; /* Offset to ClassDef table--from + * beginning of PairPos subtable--for + * the second glyph of the pair */ + HBUINT16 class1Count; /* Number of classes in ClassDef1 + * table--includes Class0 */ + HBUINT16 class2Count; /* Number of classes in ClassDef2 + * table--includes Class0 */ + ValueRecord values; /* Matrix of value pairs: + * class1-major, class2-minor, + * Each entry has value1 and value2 */ + public: + DEFINE_SIZE_ARRAY (10 + 3 * Types::size, values); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(c->check_struct (this) + && coverage.sanitize (c, this) + && classDef1.sanitize (c, this) + && classDef2.sanitize (c, this))) return_trace (false); + + unsigned int len1 = valueFormat1.get_len (); + unsigned int len2 = valueFormat2.get_len (); + unsigned int stride = HBUINT16::static_size * (len1 + len2); + unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size (); + unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count; + return_trace (c->check_range ((const void *) values, + count, + record_size) && + valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) && + valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+coverage).intersects (glyphs) && + (this+classDef2).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!intersects (c->glyph_set)) return; + if ((!valueFormat1.has_device ()) && (!valueFormat2.has_device ())) return; + + hb_set_t klass1_glyphs, klass2_glyphs; + if (!(this+classDef1).collect_coverage (&klass1_glyphs)) return; + if (!(this+classDef2).collect_coverage (&klass2_glyphs)) return; + + hb_set_t class1_set, class2_set; + for (const unsigned cp : + c->glyph_set->iter () | hb_filter (this + coverage)) + { + if (!klass1_glyphs.has (cp)) class1_set.add (0); + else + { + unsigned klass1 = (this+classDef1).get (cp); + class1_set.add (klass1); + } + } + + class2_set.add (0); + for (const unsigned cp : + c->glyph_set->iter () | hb_filter (klass2_glyphs)) + { + unsigned klass2 = (this+classDef2).get (cp); + class2_set.add (klass2); + } + + if (class1_set.is_empty () + || class2_set.is_empty () + || (class2_set.get_population() == 1 && class2_set.has(0))) + return; + + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + const hb_array_t values_array = values.as_array ((unsigned)class1Count * (unsigned) class2Count * (len1 + len2)); + for (const unsigned class1_idx : class1_set.iter ()) + { + for (const unsigned class2_idx : class2_set.iter ()) + { + unsigned start_offset = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + if (valueFormat1.has_device ()) + valueFormat1.collect_variation_indices (c, this, values_array.sub_array (start_offset, len1)); + + if (valueFormat2.has_device ()) + valueFormat2.collect_variation_indices (c, this, values_array.sub_array (start_offset+len1, len2)); + } + } + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + if (unlikely (!(this+classDef2).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + unsigned unsafe_to; + if (!skippy_iter.next (&unsafe_to)) + { + buffer->unsafe_to_concat (buffer->idx, unsafe_to); + return_trace (false); + } + + unsigned int len1 = valueFormat1.get_len (); + unsigned int len2 = valueFormat2.get_len (); + unsigned int record_len = len1 + len2; + + unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint); + unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint); + if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) + { + buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1); + return_trace (false); + } + + const Value *v = &values[record_len * (klass1 * class2Count + klass2)]; + + bool applied_first = false, applied_second = false; + + + /* Isolate simple kerning and apply it half to each side. + * Results in better cursor positinoing / underline drawing. + * + * Disabled, because causes issues... :-( + * https://github.com/harfbuzz/harfbuzz/issues/3408 + * https://github.com/harfbuzz/harfbuzz/pull/3235#issuecomment-1029814978 + */ +#ifndef HB_SPLIT_KERN + if (0) +#endif + { + if (!len2) + { + const hb_direction_t dir = buffer->props.direction; + const bool horizontal = HB_DIRECTION_IS_HORIZONTAL (dir); + const bool backward = HB_DIRECTION_IS_BACKWARD (dir); + unsigned mask = horizontal ? ValueFormat::xAdvance : ValueFormat::yAdvance; + if (backward) + mask |= mask >> 2; /* Add eg. xPlacement in RTL. */ + /* Add Devices. */ + mask |= mask << 4; + + if (valueFormat1 & ~mask) + goto bail; + + /* Is simple kern. Apply value on an empty position slot, + * then split it between sides. */ + + hb_glyph_position_t pos{}; + if (valueFormat1.apply_value (c, this, v, pos)) + { + hb_position_t *src = &pos.x_advance; + hb_position_t *dst1 = &buffer->cur_pos().x_advance; + hb_position_t *dst2 = &buffer->pos[skippy_iter.idx].x_advance; + unsigned i = horizontal ? 0 : 1; + + hb_position_t kern = src[i]; + hb_position_t kern1 = kern >> 1; + hb_position_t kern2 = kern - kern1; + + if (!backward) + { + dst1[i] += kern1; + dst2[i] += kern2; + dst2[i + 2] += kern2; + } + else + { + dst1[i] += kern1; + dst1[i + 2] += src[i + 2] - kern2; + dst2[i] += kern2; + } + + applied_first = applied_second = kern != 0; + goto success; + } + goto boring; + } + } + bail: + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "try kerning glyphs at %u,%u", + c->buffer->idx, skippy_iter.idx); + } + + applied_first = valueFormat1.apply_value (c, this, v, buffer->cur_pos()); + applied_second = valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]); + + if (applied_first || applied_second) + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "kerned glyphs at %u,%u", + c->buffer->idx, skippy_iter.idx); + } + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "tried kerning glyphs at %u,%u", + c->buffer->idx, skippy_iter.idx); + } + + success: + if (applied_first || applied_second) + buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1); + else + boring: + buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1); + + if (len2) + { + skippy_iter.idx++; + // https://github.com/harfbuzz/harfbuzz/issues/3824 + // https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116 + buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1); + } + + buffer->idx = skippy_iter.idx; + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass1_map; + out->classDef1.serialize_subset (c, classDef1, this, &klass1_map, true, true, &(this + coverage)); + out->class1Count = klass1_map.get_population (); + + hb_map_t klass2_map; + out->classDef2.serialize_subset (c, classDef2, this, &klass2_map, true, false); + out->class2Count = klass2_map.get_population (); + + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + + hb_pair_t newFormats = hb_pair (valueFormat1, valueFormat2); + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + newFormats = compute_effective_value_formats (klass1_map, klass2_map); + + out->valueFormat1 = newFormats.first; + out->valueFormat2 = newFormats.second; + + if (c->plan->all_axes_pinned) + { + out->valueFormat1 = out->valueFormat1.drop_device_table_flags (); + out->valueFormat2 = out->valueFormat2.drop_device_table_flags (); + } + + for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map)) + { + for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map)) + { + unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + valueFormat1.copy_values (c->serializer, out->valueFormat1, this, &values[idx], &c->plan->layout_variation_idx_delta_map); + valueFormat2.copy_values (c->serializer, out->valueFormat2, this, &values[idx + len1], &c->plan->layout_variation_idx_delta_map); + } + } + + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (this+coverage) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + out->coverage.serialize_serialize (c->serializer, it); + return_trace (out->class1Count && out->class2Count && bool (it)); + } + + + hb_pair_t compute_effective_value_formats (const hb_map_t& klass1_map, + const hb_map_t& klass2_map) const + { + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + unsigned record_size = len1 + len2; + + unsigned format1 = 0; + unsigned format2 = 0; + + for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map)) + { + for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map)) + { + unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * record_size; + format1 = format1 | valueFormat1.get_effective_format (&values[idx]); + format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1]); + } + + if (format1 == valueFormat1 && format2 == valueFormat2) + break; + } + + return hb_pair (format1, format2); + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairSet.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairSet.hh new file mode 100644 index 00000000000..147b8e00ea6 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairSet.hh @@ -0,0 +1,207 @@ +#ifndef OT_LAYOUT_GPOS_PAIRSET_HH +#define OT_LAYOUT_GPOS_PAIRSET_HH + +#include "PairValueRecord.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct PairSet +{ + template + friend struct PairPosFormat1_3; + + using PairValueRecord = GPOS_impl::PairValueRecord; + + protected: + HBUINT16 len; /* Number of PairValueRecords */ + PairValueRecord firstPairValueRecord; + /* Array of PairValueRecords--ordered + * by GlyphID of the second glyph */ + public: + DEFINE_SIZE_MIN (2); + + static unsigned get_size (unsigned len1, unsigned len2) + { + return Types::HBGlyphID::static_size + Value::static_size * (len1 + len2); + } + static unsigned get_size (const ValueFormat valueFormats[2]) + { + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + return get_size (len1, len2); + } + + struct sanitize_closure_t + { + const ValueFormat *valueFormats; + unsigned int len1; /* valueFormats[0].get_len() */ + unsigned int stride; /* bytes */ + }; + + bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const + { + TRACE_SANITIZE (this); + if (!(c->check_struct (this) + && c->check_range (&firstPairValueRecord, + len, + closure->stride))) return_trace (false); + + unsigned int count = len; + const PairValueRecord *record = &firstPairValueRecord; + return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) && + closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride)); + } + + bool intersects (const hb_set_t *glyphs, + const ValueFormat *valueFormats) const + { + unsigned record_size = get_size (valueFormats); + + const PairValueRecord *record = &firstPairValueRecord; + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + if (glyphs->has (record->secondGlyph)) + return true; + record = &StructAtOffset (record, record_size); + } + return false; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c, + const ValueFormat *valueFormats) const + { + unsigned record_size = get_size (valueFormats); + + const PairValueRecord *record = &firstPairValueRecord; + c->input->add_array (&record->secondGlyph, len, record_size); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats) const + { + unsigned record_size = get_size (valueFormats); + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len; + for (unsigned i = 0; i < count; i++) + { + if (c->glyph_set->has (record->secondGlyph)) + { record->collect_variation_indices (c, valueFormats, this); } + + record = &StructAtOffset (record, record_size); + } + } + + bool apply (hb_ot_apply_context_t *c, + const ValueFormat *valueFormats, + unsigned int pos) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int len1 = valueFormats[0].get_len (); + unsigned int len2 = valueFormats[1].get_len (); + unsigned record_size = get_size (len1, len2); + + const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint, + &firstPairValueRecord, + len, + record_size); + if (record) + { + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "try kerning glyphs at %u,%u", + c->buffer->idx, pos); + } + + bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()); + bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]); + + if (applied_first || applied_second) + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "kerned glyphs at %u,%u", + c->buffer->idx, pos); + } + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "tried kerning glyphs at %u,%u", + c->buffer->idx, pos); + } + + if (applied_first || applied_second) + buffer->unsafe_to_break (buffer->idx, pos + 1); + + if (len2) + { + pos++; + // https://github.com/harfbuzz/harfbuzz/issues/3824 + // https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116 + buffer->unsafe_to_break (buffer->idx, pos + 1); + } + + buffer->idx = pos; + return_trace (true); + } + buffer->unsafe_to_concat (buffer->idx, pos + 1); + return_trace (false); + } + + bool subset (hb_subset_context_t *c, + const ValueFormat valueFormats[2], + const ValueFormat newFormats[2]) const + { + TRACE_SUBSET (this); + auto snap = c->serializer->snapshot (); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->len = 0; + + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + unsigned record_size = get_size (len1, len2); + + typename PairValueRecord::context_t context = + { + this, + valueFormats, + newFormats, + len1, + &glyph_map, + &c->plan->layout_variation_idx_delta_map + }; + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len, num = 0; + for (unsigned i = 0; i < count; i++) + { + if (glyphset.has (record->secondGlyph) + && record->subset (c, &context)) num++; + record = &StructAtOffset (record, record_size); + } + + out->len = num; + if (!num) c->serializer->revert (snap); + return_trace (num); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRSET_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairValueRecord.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairValueRecord.hh new file mode 100644 index 00000000000..3222477764a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairValueRecord.hh @@ -0,0 +1,99 @@ +#ifndef OT_LAYOUT_GPOS_PAIRVALUERECORD_HH +#define OT_LAYOUT_GPOS_PAIRVALUERECORD_HH + +#include "ValueFormat.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct PairValueRecord +{ + template + friend struct PairSet; + + protected: + typename Types::HBGlyphID + secondGlyph; /* GlyphID of second glyph in the + * pair--first glyph is listed in the + * Coverage table */ + ValueRecord values; /* Positioning data for the first glyph + * followed by for second glyph */ + public: + DEFINE_SIZE_ARRAY (Types::size, values); + + int cmp (hb_codepoint_t k) const + { return secondGlyph.cmp (k); } + + struct context_t + { + const void *base; + const ValueFormat *valueFormats; + const ValueFormat *newFormats; + unsigned len1; /* valueFormats[0].get_len() */ + const hb_map_t *glyph_map; + const hb_hashmap_t> *layout_variation_idx_delta_map; + }; + + bool subset (hb_subset_context_t *c, + context_t *closure) const + { + TRACE_SERIALIZE (this); + auto *s = c->serializer; + auto *out = s->start_embed (*this); + if (unlikely (!s->extend_min (out))) return_trace (false); + + out->secondGlyph = (*closure->glyph_map)[secondGlyph]; + + closure->valueFormats[0].copy_values (s, + closure->newFormats[0], + closure->base, &values[0], + closure->layout_variation_idx_delta_map); + closure->valueFormats[1].copy_values (s, + closure->newFormats[1], + closure->base, + &values[closure->len1], + closure->layout_variation_idx_delta_map); + + return_trace (true); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats, + const void *base) const + { + unsigned record1_len = valueFormats[0].get_len (); + unsigned record2_len = valueFormats[1].get_len (); + const hb_array_t values_array = values.as_array (record1_len + record2_len); + + if (valueFormats[0].has_device ()) + valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len)); + + if (valueFormats[1].has_device ()) + valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len)); + } + + bool intersects (const hb_set_t& glyphset) const + { + return glyphset.has(secondGlyph); + } + + const Value* get_values_1 () const + { + return &values[0]; + } + + const Value* get_values_2 (ValueFormat format1) const + { + return &values[format1.get_len ()]; + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRVALUERECORD_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookup.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookup.hh new file mode 100644 index 00000000000..c4e57bb5430 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookup.hh @@ -0,0 +1,79 @@ +#ifndef OT_LAYOUT_GPOS_POSLOOKUP_HH +#define OT_LAYOUT_GPOS_POSLOOKUP_HH + +#include "PosLookupSubTable.hh" +#include "../../../hb-ot-layout-common.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PosLookup : Lookup +{ + using SubTable = PosLookupSubTable; + + const SubTable& get_subtable (unsigned int i) const + { return Lookup::get_subtable (i); } + + bool is_reverse () const + { + return false; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + return_trace (dispatch (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c); + } + + hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const + { return dispatch (c); } + + hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const + { + if (c->is_lookup_visited (this_index)) + return hb_closure_lookups_context_t::default_return_value (); + + c->set_lookup_visited (this_index); + if (!intersects (c->glyphs)) + { + c->set_lookup_inactive (this_index); + return hb_closure_lookups_context_t::default_return_value (); + } + + hb_closure_lookups_context_t::return_t ret = dispatch (c); + return ret; + } + + template + void collect_coverage (set_t *glyphs) const + { + hb_collect_coverage_context_t c (glyphs); + dispatch (&c); + } + + template + static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { return Lookup::dispatch (c, std::forward (ds)...); } + + bool subset (hb_subset_context_t *c) const + { return Lookup::subset (c); } + + bool sanitize (hb_sanitize_context_t *c) const + { return Lookup::sanitize (c); } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_POSLOOKUP_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookupSubTable.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookupSubTable.hh new file mode 100644 index 00000000000..c19fbc323ff --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookupSubTable.hh @@ -0,0 +1,79 @@ +#ifndef OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH +#define OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH + +#include "SinglePos.hh" +#include "PairPos.hh" +#include "CursivePos.hh" +#include "MarkBasePos.hh" +#include "MarkLigPos.hh" +#include "MarkMarkPos.hh" +#include "ContextPos.hh" +#include "ChainContextPos.hh" +#include "ExtensionPos.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PosLookupSubTable +{ + friend struct ::OT::Lookup; + friend struct PosLookup; + + enum Type { + Single = 1, + Pair = 2, + Cursive = 3, + MarkBase = 4, + MarkLig = 5, + MarkMark = 6, + Context = 7, + ChainContext = 8, + Extension = 9 + }; + + template + typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const + { + TRACE_DISPATCH (this, lookup_type); + switch (lookup_type) { + case Single: return_trace (u.single.dispatch (c, std::forward (ds)...)); + case Pair: return_trace (u.pair.dispatch (c, std::forward (ds)...)); + case Cursive: return_trace (u.cursive.dispatch (c, std::forward (ds)...)); + case MarkBase: return_trace (u.markBase.dispatch (c, std::forward (ds)...)); + case MarkLig: return_trace (u.markLig.dispatch (c, std::forward (ds)...)); + case MarkMark: return_trace (u.markMark.dispatch (c, std::forward (ds)...)); + case Context: return_trace (u.context.dispatch (c, std::forward (ds)...)); + case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward (ds)...)); + case Extension: return_trace (u.extension.dispatch (c, std::forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c, lookup_type); + } + + protected: + union { + SinglePos single; + PairPos pair; + CursivePos cursive; + MarkBasePos markBase; + MarkLigPos markLig; + MarkMarkPos markMark; + ContextPos context; + ChainContextPos chainContext; + ExtensionPos extension; + } u; + public: + DEFINE_SIZE_MIN (0); +}; + +} +} +} + +#endif /* HB_OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePos.hh new file mode 100644 index 00000000000..3af6c499659 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePos.hh @@ -0,0 +1,100 @@ +#ifndef OT_LAYOUT_GPOS_SINGLEPOS_HH +#define OT_LAYOUT_GPOS_SINGLEPOS_HH + +#include "SinglePosFormat1.hh" +#include "SinglePosFormat2.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct SinglePos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + SinglePosFormat1 format1; + SinglePosFormat2 format2; + } u; + + public: + template + unsigned get_format (Iterator glyph_val_iter_pairs) + { + hb_array_t first_val_iter = hb_second (*glyph_val_iter_pairs); + + for (const auto iter : glyph_val_iter_pairs) + for (const auto _ : hb_zip (iter.second, first_val_iter)) + if (_.first != _.second) + return 2; + + return 1; + } + + template + void serialize (hb_serialize_context_t *c, + const SrcLookup* src, + Iterator glyph_val_iter_pairs, + const hb_hashmap_t> *layout_variation_idx_delta_map, + bool all_axes_pinned) + { + if (unlikely (!c->extend_min (u.format))) return; + unsigned format = 2; + ValueFormat new_format = src->get_value_format (); + + if (all_axes_pinned) + new_format = new_format.drop_device_table_flags (); + + if (glyph_val_iter_pairs) + format = get_format (glyph_val_iter_pairs); + + u.format = format; + switch (u.format) { + case 1: u.format1.serialize (c, + src, + glyph_val_iter_pairs, + new_format, + layout_variation_idx_delta_map); + return; + case 2: u.format2.serialize (c, + src, + glyph_val_iter_pairs, + new_format, + layout_variation_idx_delta_map); + return; + default:return; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + + +template +static void +SinglePos_serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + const hb_hashmap_t> *layout_variation_idx_delta_map, + bool all_axes_pinned) +{ c->start_embed ()->serialize (c, src, it, layout_variation_idx_delta_map, all_axes_pinned); } + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat1.hh new file mode 100644 index 00000000000..623e4e66b20 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat1.hh @@ -0,0 +1,164 @@ +#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH + +#include "Common.hh" +#include "ValueFormat.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct SinglePosFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat; /* Defines the types of data in the + * ValueRecord */ + ValueRecord values; /* Defines positioning + * value(s)--applied to all glyphs in + * the Coverage table */ + public: + DEFINE_SIZE_ARRAY (6, values); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + /* The coverage table may use a range to represent a set + * of glyphs, which means a small number of bytes can + * generate a large glyph set. Manually modify the + * sanitizer max ops to take this into account. + * + * Note: This check *must* be right after coverage sanitize. */ + c->check_ops ((this + coverage).get_population () >> 1) && + valueFormat.sanitize_value (c, this, values)); + + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!valueFormat.has_device ()) return; + + hb_set_t intersection; + (this+coverage).intersect_set (*c->glyph_set, intersection); + if (!intersection) return; + + valueFormat.collect_variation_indices (c, this, values.as_array (valueFormat.get_len ())); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } + + const Coverage &get_coverage () const { return this+coverage; } + + ValueFormat get_value_format () const { return valueFormat; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioning glyph at %u", + c->buffer->idx); + } + + valueFormat.apply_value (c, this, values, buffer->cur_pos()); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioned glyph at %u", + c->buffer->idx); + } + + buffer->idx++; + return_trace (true); + } + + bool + position_single (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t gid, + hb_glyph_position_t &pos) const + { + unsigned int index = (this+coverage).get_coverage (gid); + if (likely (index == NOT_COVERED)) return false; + + /* This is ugly... */ + hb_buffer_t buffer; + buffer.props.direction = direction; + OT::hb_ot_apply_context_t c (1, font, &buffer); + + valueFormat.apply_value (&c, this, values, pos); + return true; + } + + template + void serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + ValueFormat newFormat, + const hb_hashmap_t> *layout_variation_idx_delta_map) + { + if (unlikely (!c->extend_min (this))) return; + if (unlikely (!c->check_assign (valueFormat, + newFormat, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) return; + + for (const hb_array_t& _ : + it | hb_map (hb_second)) + { + src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_delta_map); + // Only serialize the first entry in the iterator, the rest are assumed to + // be the same. + break; + } + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize_serialize (c, glyphs); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + hb_set_t intersection; + (this+coverage).intersect_set (glyphset, intersection); + + auto it = + + hb_iter (intersection) + | hb_map_retains_sorting (glyph_map) + | hb_zip (hb_repeat (values.as_array (valueFormat.get_len ()))) + ; + + bool ret = bool (it); + SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned); + return_trace (ret); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat2.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat2.hh new file mode 100644 index 00000000000..e8f2d7c2c6e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat2.hh @@ -0,0 +1,176 @@ +#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH +#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct SinglePosFormat2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat; /* Defines the types of data in the + * ValueRecord */ + HBUINT16 valueCount; /* Number of ValueRecords */ + ValueRecord values; /* Array of ValueRecords--positioning + * values applied to glyphs */ + public: + DEFINE_SIZE_ARRAY (8, values); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + valueFormat.sanitize_values (c, this, values, valueCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!valueFormat.has_device ()) return; + + auto it = + + hb_zip (this+coverage, hb_range ((unsigned) valueCount)) + | hb_filter (c->glyph_set, hb_first) + ; + + if (!it) return; + + unsigned sub_length = valueFormat.get_len (); + const hb_array_t values_array = values.as_array (valueCount * sub_length); + + for (unsigned i : + it + | hb_map (hb_second)) + valueFormat.collect_variation_indices (c, this, values_array.sub_array (i * sub_length, sub_length)); + + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } + + const Coverage &get_coverage () const { return this+coverage; } + + ValueFormat get_value_format () const { return valueFormat; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + if (unlikely (index >= valueCount)) return_trace (false); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioning glyph at %u", + c->buffer->idx); + } + + valueFormat.apply_value (c, this, + &values[index * valueFormat.get_len ()], + buffer->cur_pos()); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioned glyph at %u", + c->buffer->idx); + } + + buffer->idx++; + return_trace (true); + } + + bool + position_single (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t gid, + hb_glyph_position_t &pos) const + { + unsigned int index = (this+coverage).get_coverage (gid); + if (likely (index == NOT_COVERED)) return false; + if (unlikely (index >= valueCount)) return false; + + /* This is ugly... */ + hb_buffer_t buffer; + buffer.props.direction = direction; + OT::hb_ot_apply_context_t c (1, font, &buffer); + + valueFormat.apply_value (&c, this, + &values[index * valueFormat.get_len ()], + pos); + return true; + } + + + template + void serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + ValueFormat newFormat, + const hb_hashmap_t> *layout_variation_idx_delta_map) + { + auto out = c->extend_min (this); + if (unlikely (!out)) return; + if (unlikely (!c->check_assign (valueFormat, newFormat, HB_SERIALIZE_ERROR_INT_OVERFLOW))) return; + if (unlikely (!c->check_assign (valueCount, it.len (), HB_SERIALIZE_ERROR_ARRAY_OVERFLOW))) return; + + + it + | hb_map (hb_second) + | hb_apply ([&] (hb_array_t _) + { src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_delta_map); }) + ; + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize_serialize (c, glyphs); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + unsigned sub_length = valueFormat.get_len (); + auto values_array = values.as_array (valueCount * sub_length); + + auto it = + + hb_zip (this+coverage, hb_range ((unsigned) valueCount)) + | hb_filter (glyphset, hb_first) + | hb_map_retains_sorting ([&] (const hb_pair_t& _) + { + return hb_pair (glyph_map[_.first], + values_array.sub_array (_.second * sub_length, + sub_length)); + }) + ; + + bool ret = bool (it); + SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned); + return_trace (ret); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ValueFormat.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ValueFormat.hh new file mode 100644 index 00000000000..1aa451abcc8 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ValueFormat.hh @@ -0,0 +1,394 @@ +#ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH +#define OT_LAYOUT_GPOS_VALUEFORMAT_HH + +#include "../../../hb-ot-layout-gsubgpos.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +typedef HBUINT16 Value; + +typedef UnsizedArrayOf ValueRecord; + +struct ValueFormat : HBUINT16 +{ + enum Flags { + xPlacement = 0x0001u, /* Includes horizontal adjustment for placement */ + yPlacement = 0x0002u, /* Includes vertical adjustment for placement */ + xAdvance = 0x0004u, /* Includes horizontal adjustment for advance */ + yAdvance = 0x0008u, /* Includes vertical adjustment for advance */ + xPlaDevice = 0x0010u, /* Includes horizontal Device table for placement */ + yPlaDevice = 0x0020u, /* Includes vertical Device table for placement */ + xAdvDevice = 0x0040u, /* Includes horizontal Device table for advance */ + yAdvDevice = 0x0080u, /* Includes vertical Device table for advance */ + ignored = 0x0F00u, /* Was used in TrueType Open for MM fonts */ + reserved = 0xF000u, /* For future use */ + + devices = 0x00F0u /* Mask for having any Device table */ + }; + +/* All fields are options. Only those available advance the value pointer. */ +#if 0 + HBINT16 xPlacement; /* Horizontal adjustment for + * placement--in design units */ + HBINT16 yPlacement; /* Vertical adjustment for + * placement--in design units */ + HBINT16 xAdvance; /* Horizontal adjustment for + * advance--in design units (only used + * for horizontal writing) */ + HBINT16 yAdvance; /* Vertical adjustment for advance--in + * design units (only used for vertical + * writing) */ + Offset16To xPlaDevice; /* Offset to Device table for + * horizontal placement--measured from + * beginning of PosTable (may be NULL) */ + Offset16To yPlaDevice; /* Offset to Device table for vertical + * placement--measured from beginning + * of PosTable (may be NULL) */ + Offset16To xAdvDevice; /* Offset to Device table for + * horizontal advance--measured from + * beginning of PosTable (may be NULL) */ + Offset16To yAdvDevice; /* Offset to Device table for vertical + * advance--measured from beginning of + * PosTable (may be NULL) */ +#endif + + IntType& operator = (uint16_t i) { v = i; return *this; } + + unsigned int get_len () const { return hb_popcount ((unsigned int) *this); } + unsigned int get_size () const { return get_len () * Value::static_size; } + + hb_vector_t get_device_table_indices () const { + unsigned i = 0; + hb_vector_t result; + unsigned format = *this; + + if (format & xPlacement) i++; + if (format & yPlacement) i++; + if (format & xAdvance) i++; + if (format & yAdvance) i++; + + if (format & xPlaDevice) result.push (i++); + if (format & yPlaDevice) result.push (i++); + if (format & xAdvDevice) result.push (i++); + if (format & yAdvDevice) result.push (i++); + + return result; + } + + bool apply_value (hb_ot_apply_context_t *c, + const void *base, + const Value *values, + hb_glyph_position_t &glyph_pos) const + { + bool ret = false; + unsigned int format = *this; + if (!format) return ret; + + hb_font_t *font = c->font; + bool horizontal = +#ifndef HB_NO_VERTICAL + HB_DIRECTION_IS_HORIZONTAL (c->direction) +#else + true +#endif + ; + + if (format & xPlacement) glyph_pos.x_offset += font->em_scale_x (get_short (values++, &ret)); + if (format & yPlacement) glyph_pos.y_offset += font->em_scale_y (get_short (values++, &ret)); + if (format & xAdvance) { + if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values, &ret)); + values++; + } + /* y_advance values grow downward but font-space grows upward, hence negation */ + if (format & yAdvance) { + if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values, &ret)); + values++; + } + + if (!has_device ()) return ret; + + bool use_x_device = font->x_ppem || font->num_coords; + bool use_y_device = font->y_ppem || font->num_coords; + + if (!use_x_device && !use_y_device) return ret; + + const VariationStore &store = c->var_store; + auto *cache = c->var_store_cache; + + /* pixel -> fractional pixel */ + if (format & xPlaDevice) { + if (use_x_device) glyph_pos.x_offset += (base + get_device (values, &ret)).get_x_delta (font, store, cache); + values++; + } + if (format & yPlaDevice) { + if (use_y_device) glyph_pos.y_offset += (base + get_device (values, &ret)).get_y_delta (font, store, cache); + values++; + } + if (format & xAdvDevice) { + if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values, &ret)).get_x_delta (font, store, cache); + values++; + } + if (format & yAdvDevice) { + /* y_advance values grow downward but font-space grows upward, hence negation */ + if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values, &ret)).get_y_delta (font, store, cache); + values++; + } + return ret; + } + + unsigned int get_effective_format (const Value *values) const + { + unsigned int format = *this; + for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) { + if (format & flag) should_drop (*values++, (Flags) flag, &format); + } + + return format; + } + + template + unsigned int get_effective_format (Iterator it) const { + unsigned int new_format = 0; + + for (const hb_array_t& values : it) + new_format = new_format | get_effective_format (&values); + + return new_format; + } + + void copy_values (hb_serialize_context_t *c, + unsigned int new_format, + const void *base, + const Value *values, + const hb_hashmap_t> *layout_variation_idx_delta_map) const + { + unsigned int format = *this; + if (!format) return; + + HBINT16 *x_placement = nullptr, *y_placement = nullptr, *x_adv = nullptr, *y_adv = nullptr; + if (format & xPlacement) x_placement = copy_value (c, new_format, xPlacement, *values++); + if (format & yPlacement) y_placement = copy_value (c, new_format, yPlacement, *values++); + if (format & xAdvance) x_adv = copy_value (c, new_format, xAdvance, *values++); + if (format & yAdvance) y_adv = copy_value (c, new_format, yAdvance, *values++); + + if (format & xPlaDevice) + { + add_delta_to_value (x_placement, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xPlaDevice); + } + + if (format & yPlaDevice) + { + add_delta_to_value (y_placement, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yPlaDevice); + } + + if (format & xAdvDevice) + { + add_delta_to_value (x_adv, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xAdvDevice); + } + + if (format & yAdvDevice) + { + add_delta_to_value (y_adv, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yAdvDevice); + } + } + + HBINT16* copy_value (hb_serialize_context_t *c, + unsigned int new_format, + Flags flag, + Value value) const + { + // Filter by new format. + if (!(new_format & flag)) return nullptr; + return reinterpret_cast (c->copy (value)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *base, + const hb_array_t& values) const + { + unsigned format = *this; + unsigned i = 0; + if (format & xPlacement) i++; + if (format & yPlacement) i++; + if (format & xAdvance) i++; + if (format & yAdvance) i++; + if (format & xPlaDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c); + i++; + } + + if (format & ValueFormat::yPlaDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c); + i++; + } + + if (format & ValueFormat::xAdvDevice) + { + + (base + get_device (&(values[i]))).collect_variation_indices (c); + i++; + } + + if (format & ValueFormat::yAdvDevice) + { + + (base + get_device (&(values[i]))).collect_variation_indices (c); + i++; + } + } + + unsigned drop_device_table_flags () const + { + unsigned format = *this; + for (unsigned flag = xPlaDevice; flag <= yAdvDevice; flag = flag << 1) + format = format & ~flag; + + return format; + } + + private: + bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const + { + unsigned int format = *this; + + if (format & xPlacement) values++; + if (format & yPlacement) values++; + if (format & xAdvance) values++; + if (format & yAdvance) values++; + + if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false; + + return true; + } + + static inline Offset16To& get_device (Value* value) + { + return *static_cast *> (value); + } + static inline const Offset16To& get_device (const Value* value, bool *worked=nullptr) + { + if (worked) *worked |= bool (*value); + return *static_cast *> (value); + } + + void add_delta_to_value (HBINT16 *value, + const void *base, + const Value *src_value, + const hb_hashmap_t> *layout_variation_idx_delta_map) const + { + if (!value) return; + unsigned varidx = (base + get_device (src_value)).get_variation_index (); + hb_pair_t *varidx_delta; + if (!layout_variation_idx_delta_map->has (varidx, &varidx_delta)) return; + + *value += hb_second (*varidx_delta); + } + + bool copy_device (hb_serialize_context_t *c, const void *base, + const Value *src_value, + const hb_hashmap_t> *layout_variation_idx_delta_map, + unsigned int new_format, Flags flag) const + { + // Filter by new format. + if (!(new_format & flag)) return true; + + Value *dst_value = c->copy (*src_value); + + if (!dst_value) return false; + if (*dst_value == 0) return true; + + *dst_value = 0; + c->push (); + if ((base + get_device (src_value)).copy (c, layout_variation_idx_delta_map)) + { + c->add_link (*dst_value, c->pop_pack ()); + return true; + } + else + { + c->pop_discard (); + return false; + } + } + + static inline const HBINT16& get_short (const Value* value, bool *worked=nullptr) + { + if (worked) *worked |= bool (*value); + return *reinterpret_cast (value); + } + + public: + + bool has_device () const + { + unsigned int format = *this; + return (format & devices) != 0; + } + + bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const + { + TRACE_SANITIZE (this); + return_trace (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values))); + } + + bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const + { + TRACE_SANITIZE (this); + unsigned int len = get_len (); + + if (!c->check_range (values, count, get_size ())) return_trace (false); + + if (!has_device ()) return_trace (true); + + for (unsigned int i = 0; i < count; i++) { + if (!sanitize_value_devices (c, base, values)) + return_trace (false); + values += len; + } + + return_trace (true); + } + + /* Just sanitize referenced Device tables. Doesn't check the values themselves. */ + bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const + { + TRACE_SANITIZE (this); + + if (!has_device ()) return_trace (true); + + for (unsigned int i = 0; i < count; i++) { + if (!sanitize_value_devices (c, base, values)) + return_trace (false); + values = &StructAtOffset (values, stride); + } + + return_trace (true); + } + + private: + + void should_drop (Value value, Flags flag, unsigned int* format) const + { + if (value) return; + *format = *format & ~flag; + } + +}; + +} +} +} + +#endif // #ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSet.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSet.hh new file mode 100644 index 00000000000..b4466119be0 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSet.hh @@ -0,0 +1,126 @@ +#ifndef OT_LAYOUT_GSUB_ALTERNATESET_HH +#define OT_LAYOUT_GSUB_ALTERNATESET_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct AlternateSet +{ + protected: + Array16Of + alternates; /* Array of alternate GlyphIDs--in + * arbitrary order */ + public: + DEFINE_SIZE_ARRAY (2, alternates); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (alternates.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { return hb_any (alternates, glyphs); } + + void closure (hb_closure_context_t *c) const + { c->output->add_array (alternates.arrayZ, alternates.len); } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { c->output->add_array (alternates.arrayZ, alternates.len); } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int count = alternates.len; + + if (unlikely (!count)) return_trace (false); + + hb_mask_t glyph_mask = c->buffer->cur().mask; + hb_mask_t lookup_mask = c->lookup_mask; + + /* Note: This breaks badly if two features enabled this lookup together. */ + unsigned int shift = hb_ctz (lookup_mask); + unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift); + + /* If alt_index is MAX_VALUE, randomize feature if it is the rand feature. */ + if (alt_index == HB_OT_MAP_MAX_VALUE && c->random) + { + /* Maybe we can do better than unsafe-to-break all; but since we are + * changing random state, it would be hard to track that. Good 'nough. */ + c->buffer->unsafe_to_break (0, c->buffer->len); + alt_index = c->random_number () % count + 1; + } + + if (unlikely (alt_index > count || alt_index == 0)) return_trace (false); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (alternate substitution)", + c->buffer->idx); + } + + c->replace_glyph (alternates[alt_index - 1]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (alternate substitution)", + c->buffer->idx - 1u); + } + + return_trace (true); + } + + unsigned + get_alternates (unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { + if (alternates.len && alternate_count) + { + + alternates.as_array ().sub_array (start_offset, alternate_count) + | hb_sink (hb_array (alternate_glyphs, *alternate_count)) + ; + } + return alternates.len; + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator alts) + { + TRACE_SERIALIZE (this); + return_trace (alternates.serialize (c, alts)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (alternates) + | hb_filter (glyphset) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, it) && + out->alternates); + } +}; + +} +} +} + + +#endif /* OT_LAYOUT_GSUB_ALTERNATESET_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubst.hh new file mode 100644 index 00000000000..04a052a7836 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubst.hh @@ -0,0 +1,62 @@ +#ifndef OT_LAYOUT_GSUB_ALTERNATESUBST_HH +#define OT_LAYOUT_GSUB_ALTERNATESUBST_HH + +#include "AlternateSubstFormat1.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct AlternateSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + AlternateSubstFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + AlternateSubstFormat1_2 format2; +#endif + } u; + public: + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + /* TODO This function is unused and not updated to 24bit GIDs. Should be done by using + * iterators. While at it perhaps using iterator of arrays of hb_codepoint_t instead. */ + bool serialize (hb_serialize_context_t *c, + hb_sorted_array_t glyphs, + hb_array_t alternate_len_list, + hb_array_t alternate_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned int format = 1; + u.format = format; + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list)); + default:return_trace (false); + } + } + + /* TODO subset() should choose format. */ + +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_ALTERNATESUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubstFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubstFormat1.hh new file mode 100644 index 00000000000..adec65d5864 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubstFormat1.hh @@ -0,0 +1,128 @@ +#ifndef OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH + +#include "AlternateSet.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct AlternateSubstFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of>> + alternateSet; /* Array of AlternateSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (2 + 2 * Types::size, alternateSet); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + bool may_have_non_1to1 () const + { return false; } + + void closure (hb_closure_context_t *c) const + { + + hb_zip (this+coverage, alternateSet) + | hb_filter (c->parent_active_glyphs (), hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const AlternateSet &_) { _.closure (c); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, alternateSet) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); }) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + unsigned + get_glyph_alternates (hb_codepoint_t gid, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { return (this+alternateSet[(this+coverage).get_coverage (gid)]) + .get_alternates (start_offset, alternate_count, alternate_glyphs); } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + return_trace ((this+alternateSet[index]).apply (c)); + } + + bool serialize (hb_serialize_context_t *c, + hb_sorted_array_t glyphs, + hb_array_t alternate_len_list, + hb_array_t alternate_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!alternateSet.serialize (c, glyphs.length))) return_trace (false); + for (unsigned int i = 0; i < glyphs.length; i++) + { + unsigned int alternate_len = alternate_len_list[i]; + if (unlikely (!alternateSet[i] + .serialize_serialize (c, alternate_glyphs_list.sub_array (0, alternate_len)))) + return_trace (false); + alternate_glyphs_list += alternate_len; + } + return_trace (coverage.serialize_serialize (c, glyphs)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, alternateSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->alternateSet, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ChainContextSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ChainContextSubst.hh new file mode 100644 index 00000000000..08fd779f730 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ChainContextSubst.hh @@ -0,0 +1,18 @@ +#ifndef OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH +#define OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH + +// TODO(garretrieger): move to new layout. +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ChainContextSubst : ChainContext {}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Common.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Common.hh new file mode 100644 index 00000000000..968bba0481a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Common.hh @@ -0,0 +1,21 @@ +#ifndef OT_LAYOUT_GSUB_COMMON_HH +#define OT_LAYOUT_GSUB_COMMON_HH + +#include "../../../hb-serialize.hh" +#include "../../../hb-ot-layout-gsubgpos.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +typedef hb_pair_t hb_codepoint_pair_t; + +template +static void SingleSubst_serialize (hb_serialize_context_t *c, + Iterator it); + +} +} +} + +#endif /* OT_LAYOUT_GSUB_COMMON_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ContextSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ContextSubst.hh new file mode 100644 index 00000000000..9f8cb46b5e2 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ContextSubst.hh @@ -0,0 +1,18 @@ +#ifndef OT_LAYOUT_GSUB_CONTEXTSUBST_HH +#define OT_LAYOUT_GSUB_CONTEXTSUBST_HH + +// TODO(garretrieger): move to new layout. +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ContextSubst : Context {}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_CONTEXTSUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ExtensionSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ExtensionSubst.hh new file mode 100644 index 00000000000..831a7dfa2d1 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ExtensionSubst.hh @@ -0,0 +1,22 @@ +#ifndef OT_LAYOUT_GSUB_EXTENSIONSUBST_HH +#define OT_LAYOUT_GSUB_EXTENSIONSUBST_HH + +// TODO(garretrieger): move to new layout. +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ExtensionSubst : Extension +{ + typedef struct SubstLookupSubTable SubTable; + bool is_reverse () const; +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_EXTENSIONSUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/GSUB.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/GSUB.hh new file mode 100644 index 00000000000..900cf603e45 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/GSUB.hh @@ -0,0 +1,61 @@ +#ifndef OT_LAYOUT_GSUB_GSUB_HH +#define OT_LAYOUT_GSUB_GSUB_HH + +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" +#include "SubstLookup.hh" + +namespace OT { + +using Layout::GSUB_impl::SubstLookup; + +namespace Layout { + +/* + * GSUB -- Glyph Substitution + * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub + */ + +struct GSUB : GSUBGPOS +{ + using Lookup = SubstLookup; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB; + + const SubstLookup& get_lookup (unsigned int i) const + { return static_cast (GSUBGPOS::get_lookup (i)); } + + bool subset (hb_subset_context_t *c) const + { + hb_subset_layout_context_t l (c, tableTag); + return GSUBGPOS::subset (&l); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (GSUBGPOS::sanitize (c)); + } + + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, + hb_face_t *face) const; + + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const + { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); } + + typedef GSUBGPOS::accelerator_t accelerator_t; +}; + + +} + +struct GSUB_accelerator_t : Layout::GSUB::accelerator_t { + GSUB_accelerator_t (hb_face_t *face) : Layout::GSUB::accelerator_t (face) {} +}; + + +} + +#endif /* OT_LAYOUT_GSUB_GSUB_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Ligature.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Ligature.hh new file mode 100644 index 00000000000..38057cb60c9 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Ligature.hh @@ -0,0 +1,190 @@ +#ifndef OT_LAYOUT_GSUB_LIGATURE_HH +#define OT_LAYOUT_GSUB_LIGATURE_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct Ligature +{ + protected: + typename Types::HBGlyphID + ligGlyph; /* GlyphID of ligature to substitute */ + HeadlessArrayOf + component; /* Array of component GlyphIDs--start + * with the second component--ordered + * in writing direction */ + public: + DEFINE_SIZE_ARRAY (Types::size + 2, component); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (ligGlyph.sanitize (c) && component.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { return hb_all (component, glyphs); } + + bool intersects_lig_glyph (const hb_set_t *glyphs) const + { return glyphs->has(ligGlyph); } + + void closure (hb_closure_context_t *c) const + { + if (!intersects (c->glyphs)) return; + c->output->add (ligGlyph); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + c->input->add_array (component.arrayZ, component.get_length ()); + c->output->add (ligGlyph); + } + + bool would_apply (hb_would_apply_context_t *c) const + { + if (c->len != component.lenP1) + return false; + + for (unsigned int i = 1; i < c->len; i++) + if (likely (c->glyphs[i] != component[i])) + return false; + + return true; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int count = component.lenP1; + + if (unlikely (!count)) return_trace (false); + + /* Special-case to make it in-place and not consider this + * as a "ligated" substitution. */ + if (unlikely (count == 1)) + { + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (ligature substitution)", + c->buffer->idx); + } + + c->replace_glyph (ligGlyph); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (ligature substitution)", + c->buffer->idx - 1u); + } + + return_trace (true); + } + + unsigned int total_component_count = 0; + + unsigned int match_end = 0; + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; + + if (likely (!match_input (c, count, + &component[1], + match_glyph, + nullptr, + &match_end, + match_positions, + &total_component_count))) + { + c->buffer->unsafe_to_concat (c->buffer->idx, match_end); + return_trace (false); + } + + unsigned pos = 0; + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + unsigned delta = c->buffer->sync_so_far (); + + pos = c->buffer->idx; + + char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0}; + char *p = buf; + + match_end += delta; + for (unsigned i = 0; i < count; i++) + { + match_positions[i] += delta; + if (i) + *p++ = ','; + snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]); + p += strlen(p); + } + + c->buffer->message (c->font, + "ligating glyphs at %s", + buf); + } + + ligate_input (c, + count, + match_positions, + match_end, + ligGlyph, + total_component_count); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "ligated glyph at %u", + pos); + } + + return_trace (true); + } + + template + bool serialize (hb_serialize_context_t *c, + hb_codepoint_t ligature, + Iterator components /* Starting from second */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + ligGlyph = ligature; + if (unlikely (!component.serialize (c, components))) return_trace (false); + return_trace (true); + } + + bool subset (hb_subset_context_t *c, unsigned coverage_idx) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false); + // Ensure Coverage table is always packed after this. + c->serializer->add_virtual_link (coverage_idx); + + auto it = + + hb_iter (component) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, + glyph_map[ligGlyph], + it)); } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GSUB_LIGATURE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSet.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSet.hh new file mode 100644 index 00000000000..9db25cf567b --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSet.hh @@ -0,0 +1,131 @@ +#ifndef OT_LAYOUT_GSUB_LIGATURESET_HH +#define OT_LAYOUT_GSUB_LIGATURESET_HH + +#include "Common.hh" +#include "Ligature.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct LigatureSet +{ + protected: + Array16OfOffset16To> + ligature; /* Array LigatureSet tables + * ordered by preference */ + public: + DEFINE_SIZE_ARRAY (2, ligature); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (ligature.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); }) + | hb_any + ; + } + + bool intersects_lig_glyph (const hb_set_t *glyphs) const + { + return + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_map ([glyphs] (const Ligature &_) { + return _.intersects_lig_glyph (glyphs) && _.intersects (glyphs); + }) + | hb_any + ; + } + + void closure (hb_closure_context_t *c) const + { + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Ligature &_) { _.closure (c); }) + ; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); }) + ; + } + + bool would_apply (hb_would_apply_context_t *c) const + { + return + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_map ([c] (const Ligature &_) { return _.would_apply (c); }) + | hb_any + ; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int num_ligs = ligature.len; + for (unsigned int i = 0; i < num_ligs; i++) + { + const auto &lig = this+ligature[i]; + if (lig.apply (c)) return_trace (true); + } + + return_trace (false); + } + + bool serialize (hb_serialize_context_t *c, + hb_array_t ligatures, + hb_array_t component_count_list, + hb_array_t &component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false); + for (unsigned int i = 0; i < ligatures.length; i++) + { + unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0); + if (unlikely (!ligature[i].serialize_serialize (c, + ligatures[i], + component_list.sub_array (0, component_count)))) + return_trace (false); + component_list += component_count; + } + return_trace (true); + } + + bool subset (hb_subset_context_t *c, unsigned coverage_idx) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + hb_iter (ligature) + | hb_filter (subset_offset_array (c, out->ligature, this, coverage_idx)) + | hb_drain + ; + + if (bool (out->ligature)) + // Ensure Coverage table is always packed after this. + c->serializer->add_virtual_link (coverage_idx); + + return_trace (bool (out->ligature)); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_LIGATURESET_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubst.hh new file mode 100644 index 00000000000..18f6e355816 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubst.hh @@ -0,0 +1,71 @@ +#ifndef OT_LAYOUT_GSUB_LIGATURESUBST_HH +#define OT_LAYOUT_GSUB_LIGATURESUBST_HH + +#include "Common.hh" +#include "LigatureSubstFormat1.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct LigatureSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + LigatureSubstFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + LigatureSubstFormat1_2 format2; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + /* TODO This function is only used by small GIDs, and not updated to 24bit GIDs. Should + * be done by using iterators. While at it perhaps using iterator of arrays of hb_codepoint_t + * instead. */ + bool serialize (hb_serialize_context_t *c, + hb_sorted_array_t first_glyphs, + hb_array_t ligature_per_first_glyph_count_list, + hb_array_t ligatures_list, + hb_array_t component_count_list, + hb_array_t component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned int format = 1; + u.format = format; + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, + first_glyphs, + ligature_per_first_glyph_count_list, + ligatures_list, + component_count_list, + component_list)); + default:return_trace (false); + } + } + + /* TODO subset() should choose format. */ + +}; + + +} +} +} + +#endif /* OT_LAYOUT_GSUB_LIGATURESUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubstFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubstFormat1.hh new file mode 100644 index 00000000000..5c7df97d13a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubstFormat1.hh @@ -0,0 +1,166 @@ +#ifndef OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH + +#include "Common.hh" +#include "LigatureSet.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct LigatureSubstFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of>> + ligatureSet; /* Array LigatureSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (4 + Types::size, ligatureSet); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return + + hb_zip (this+coverage, ligatureSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map ([this, glyphs] (const typename Types::template OffsetTo> &_) + { return (this+_).intersects (glyphs); }) + | hb_any + ; + } + + bool may_have_non_1to1 () const + { return true; } + + void closure (hb_closure_context_t *c) const + { + + hb_zip (this+coverage, ligatureSet) + | hb_filter (c->parent_active_glyphs (), hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigatureSet &_) { _.closure (c); }) + ; + + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + + hb_zip (this+coverage, ligatureSet) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); }) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { + unsigned int index = (this+coverage).get_coverage (c->glyphs[0]); + if (likely (index == NOT_COVERED)) return false; + + const auto &lig_set = this+ligatureSet[index]; + return lig_set.would_apply (c); + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const auto &lig_set = this+ligatureSet[index]; + return_trace (lig_set.apply (c)); + } + + bool serialize (hb_serialize_context_t *c, + hb_sorted_array_t first_glyphs, + hb_array_t ligature_per_first_glyph_count_list, + hb_array_t ligatures_list, + hb_array_t component_count_list, + hb_array_t component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false); + for (unsigned int i = 0; i < first_glyphs.length; i++) + { + unsigned int ligature_count = ligature_per_first_glyph_count_list[i]; + if (unlikely (!ligatureSet[i] + .serialize_serialize (c, + ligatures_list.sub_array (0, ligature_count), + component_count_list.sub_array (0, ligature_count), + component_list))) return_trace (false); + ligatures_list += ligature_count; + component_count_list += ligature_count; + } + return_trace (coverage.serialize_serialize (c, first_glyphs)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + // Due to a bug in some older versions of windows 7 the Coverage table must be + // packed after the LigatureSet and Ligature tables, so serialize Coverage first + // which places it last in the packed order. + hb_set_t new_coverage; + + hb_zip (this+coverage, hb_iter (ligatureSet) | hb_map (hb_add (this))) + | hb_filter (glyphset, hb_first) + | hb_filter ([&] (const LigatureSet& _) { + return _.intersects_lig_glyph (&glyphset); + }, hb_second) + | hb_map (hb_first) + | hb_sink (new_coverage); + + if (!c->serializer->push () + ->serialize (c->serializer, + + new_coverage.iter () | hb_map_retains_sorting (glyph_map))) + { + c->serializer->pop_discard (); + return_trace (false); + } + + unsigned coverage_idx = c->serializer->pop_pack (); + c->serializer->add_link (out->coverage, coverage_idx); + + + hb_zip (this+coverage, ligatureSet) + | hb_filter (new_coverage, hb_first) + | hb_map (hb_second) + // to ensure that the repacker always orders the coverage table after the LigatureSet + // and LigatureSubtable's they will be linked to the Coverage table via a virtual link + // the coverage table object idx is passed down to facilitate this. + | hb_apply (subset_offset_array (c, out->ligatureSet, this, coverage_idx)) + ; + + return_trace (bool (new_coverage)); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubst.hh new file mode 100644 index 00000000000..742c8587ee3 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubst.hh @@ -0,0 +1,62 @@ +#ifndef OT_LAYOUT_GSUB_MULTIPLESUBST_HH +#define OT_LAYOUT_GSUB_MULTIPLESUBST_HH + +#include "Common.hh" +#include "MultipleSubstFormat1.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct MultipleSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MultipleSubstFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + MultipleSubstFormat1_2 format2; +#endif + } u; + + public: + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned int format = 1; + u.format = format; + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, it)); + default:return_trace (false); + } + } + + /* TODO subset() should choose format. */ + +}; + + +} +} +} + +#endif /* OT_LAYOUT_GSUB_MULTIPLESUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubstFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubstFormat1.hh new file mode 100644 index 00000000000..3b4bd116949 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubstFormat1.hh @@ -0,0 +1,130 @@ +#ifndef OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH + +#include "Common.hh" +#include "Sequence.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct MultipleSubstFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of>> + sequence; /* Array of Sequence tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (4 + Types::size, sequence); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + bool may_have_non_1to1 () const + { return true; } + + void closure (hb_closure_context_t *c) const + { + + hb_zip (this+coverage, sequence) + | hb_filter (c->parent_active_glyphs (), hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Sequence &_) { _.closure (c); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, sequence) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); }) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + return_trace ((this+sequence[index]).apply (c)); + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + auto sequences = + + it + | hb_map (hb_second) + ; + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + if (unlikely (!c->extend_min (this))) return_trace (false); + + if (unlikely (!sequence.serialize (c, sequences.length))) return_trace (false); + + for (auto& pair : hb_zip (sequences, sequence)) + { + if (unlikely (!pair.second + .serialize_serialize (c, pair.first))) + return_trace (false); + } + + return_trace (coverage.serialize_serialize (c, glyphs)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, sequence) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->sequence, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } +}; + +} +} +} + + +#endif /* OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh new file mode 100644 index 00000000000..5ad463fea79 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh @@ -0,0 +1,36 @@ +#ifndef OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH +#define OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH + +#include "Common.hh" +#include "ReverseChainSingleSubstFormat1.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ReverseChainSingleSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + ReverseChainSingleSubstFormat1 format1; + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* HB_OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh new file mode 100644 index 00000000000..2c2e1aa44fd --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh @@ -0,0 +1,244 @@ +#ifndef OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ReverseChainSingleSubstFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of table */ + Array16OfOffset16To + backtrack; /* Array of coverage tables + * in backtracking sequence, in glyph + * sequence order */ + Array16OfOffset16To + lookaheadX; /* Array of coverage tables + * in lookahead sequence, in glyph + * sequence order */ + Array16Of + substituteX; /* Array of substitute + * GlyphIDs--ordered by Coverage Index */ + public: + DEFINE_SIZE_MIN (10); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this))) + return_trace (false); + const auto &lookahead = StructAfter (backtrack); + if (!lookahead.sanitize (c, this)) + return_trace (false); + const auto &substitute = StructAfter (lookahead); + return_trace (substitute.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { + if (!(this+coverage).intersects (glyphs)) + return false; + + const auto &lookahead = StructAfter (backtrack); + + unsigned int count; + + count = backtrack.len; + for (unsigned int i = 0; i < count; i++) + if (!(this+backtrack[i]).intersects (glyphs)) + return false; + + count = lookahead.len; + for (unsigned int i = 0; i < count; i++) + if (!(this+lookahead[i]).intersects (glyphs)) + return false; + + return true; + } + + bool may_have_non_1to1 () const + { return false; } + + void closure (hb_closure_context_t *c) const + { + if (!intersects (c->glyphs)) return; + + const auto &lookahead = StructAfter (backtrack); + const auto &substitute = StructAfter (lookahead); + + + hb_zip (this+coverage, substitute) + | hb_filter (c->parent_active_glyphs (), hb_first) + | hb_map (hb_second) + | hb_sink (c->output) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + unsigned int count; + + count = backtrack.len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return; + + const auto &lookahead = StructAfter (backtrack); + count = lookahead.len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return; + + const auto &substitute = StructAfter (lookahead); + count = substitute.len; + c->output->add_array (substitute.arrayZ, substitute.len); + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL)) + return_trace (false); /* No chaining to this type */ + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const auto &lookahead = StructAfter (backtrack); + const auto &substitute = StructAfter (lookahead); + + if (unlikely (index >= substitute.len)) return_trace (false); + + unsigned int start_index = 0, end_index = 0; + if (match_backtrack (c, + backtrack.len, (HBUINT16 *) backtrack.arrayZ, + match_coverage, this, + &start_index) && + match_lookahead (c, + lookahead.len, (HBUINT16 *) lookahead.arrayZ, + match_coverage, this, + c->buffer->idx + 1, &end_index)) + { + c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replacing glyph at %u (reverse chaining substitution)", + c->buffer->idx); + } + + c->replace_glyph_inplace (substitute[index]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (reverse chaining substitution)", + c->buffer->idx); + } + + /* Note: We DON'T decrease buffer->idx. The main loop does it + * for us. This is useful for preventing surprises if someone + * calls us through a Context lookup. */ + return_trace (true); + } + else + { + c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index); + return_trace (false); + } + } + + template + bool serialize_coverage_offset_array (hb_subset_context_t *c, Iterator it) const + { + TRACE_SERIALIZE (this); + auto *out = c->serializer->start_embed> (); + + if (unlikely (!c->serializer->allocate_size (HBUINT16::static_size))) + return_trace (false); + + for (auto& offset : it) { + auto *o = out->serialize_append (c->serializer); + if (unlikely (!o) || !o->serialize_subset (c, offset, this)) + return_trace (false); + } + + return_trace (true); + } + + template + bool serialize (hb_subset_context_t *c, + Iterator coverage_subst_iter, + BacktrackIterator backtrack_iter, + LookaheadIterator lookahead_iter) const + { + TRACE_SERIALIZE (this); + + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->check_success (out))) return_trace (false); + if (unlikely (!c->serializer->embed (this->format))) return_trace (false); + if (unlikely (!c->serializer->embed (this->coverage))) return_trace (false); + + if (!serialize_coverage_offset_array (c, backtrack_iter)) return_trace (false); + if (!serialize_coverage_offset_array (c, lookahead_iter)) return_trace (false); + + auto *substitute_out = c->serializer->start_embed> (); + auto substitutes = + + coverage_subst_iter + | hb_map (hb_second) + ; + + auto glyphs = + + coverage_subst_iter + | hb_map_retains_sorting (hb_first) + ; + if (unlikely (! c->serializer->check_success (substitute_out->serialize (c->serializer, substitutes)))) + return_trace (false); + + if (unlikely (!out->coverage.serialize_serialize (c->serializer, glyphs))) + return_trace (false); + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + const auto &lookahead = StructAfter (backtrack); + const auto &substitute = StructAfter (lookahead); + + auto it = + + hb_zip (this+coverage, substitute) + | hb_filter (glyphset, hb_first) + | hb_filter (glyphset, hb_second) + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_codepoint_pair_t + { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) + ; + + return_trace (bool (it) && serialize (c, it, backtrack.iter (), lookahead.iter ())); + } +}; + +} +} +} + +#endif /* HB_OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Sequence.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Sequence.hh new file mode 100644 index 00000000000..ae3292f3295 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Sequence.hh @@ -0,0 +1,165 @@ +#ifndef OT_LAYOUT_GSUB_SEQUENCE_HH +#define OT_LAYOUT_GSUB_SEQUENCE_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct Sequence +{ + protected: + Array16Of + substitute; /* String of GlyphIDs to substitute */ + public: + DEFINE_SIZE_ARRAY (2, substitute); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (substitute.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { return hb_all (substitute, glyphs); } + + void closure (hb_closure_context_t *c) const + { c->output->add_array (substitute.arrayZ, substitute.len); } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { c->output->add_array (substitute.arrayZ, substitute.len); } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int count = substitute.len; + + /* Special-case to make it in-place and not consider this + * as a "multiplied" substitution. */ + if (unlikely (count == 1)) + { + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (multiple substitution)", + c->buffer->idx); + } + + c->replace_glyph (substitute.arrayZ[0]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (multiple subtitution)", + c->buffer->idx - 1u); + } + + return_trace (true); + } + /* Spec disallows this, but Uniscribe allows it. + * https://github.com/harfbuzz/harfbuzz/issues/253 */ + else if (unlikely (count == 0)) + { + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "deleting glyph at %u (multiple substitution)", + c->buffer->idx); + } + + c->buffer->delete_glyph (); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "deleted glyph at %u (multiple substitution)", + c->buffer->idx); + } + + return_trace (true); + } + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "multiplying glyph at %u", + c->buffer->idx); + } + + unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ? + HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0; + unsigned lig_id = _hb_glyph_info_get_lig_id (&c->buffer->cur()); + + for (unsigned int i = 0; i < count; i++) + { + /* If is attached to a ligature, don't disturb that. + * https://github.com/harfbuzz/harfbuzz/issues/3069 */ + if (!lig_id) + _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i); + c->output_glyph_for_component (substitute.arrayZ[i], klass); + } + c->buffer->skip_glyph (); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + + char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0}; + char *p = buf; + + for (unsigned i = c->buffer->idx - count; i < c->buffer->idx; i++) + { + if (buf < p) + *p++ = ','; + snprintf (p, sizeof(buf) - (p - buf), "%u", i); + p += strlen(p); + } + + c->buffer->message (c->font, + "multiplied glyphs at %s", + buf); + } + + return_trace (true); + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator subst) + { + TRACE_SERIALIZE (this); + return_trace (substitute.serialize (c, subst)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + if (!intersects (&glyphset)) return_trace (false); + + auto it = + + hb_iter (substitute) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, it)); + } +}; + + +} +} +} + + +#endif /* OT_LAYOUT_GSUB_SEQUENCE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubst.hh new file mode 100644 index 00000000000..4529927ba6d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubst.hh @@ -0,0 +1,103 @@ +#ifndef OT_LAYOUT_GSUB_SINGLESUBST_HH +#define OT_LAYOUT_GSUB_SINGLESUBST_HH + +#include "Common.hh" +#include "SingleSubstFormat1.hh" +#include "SingleSubstFormat2.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct SingleSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + SingleSubstFormat1_3 format1; + SingleSubstFormat2_4 format2; +#ifndef HB_NO_BEYOND_64K + SingleSubstFormat1_3 format3; + SingleSubstFormat2_4 format4; +#endif + } u; + + public: + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned format = 2; + unsigned delta = 0; + if (glyphs) + { + format = 1; + hb_codepoint_t mask = 0xFFFFu; + +#ifndef HB_NO_BEYOND_64K + if (+ glyphs + | hb_map_retains_sorting (hb_first) + | hb_filter ([] (hb_codepoint_t gid) { return gid > 0xFFFFu; })) + { + format += 2; + mask = 0xFFFFFFu; + } +#endif + + auto get_delta = [=] (hb_codepoint_pair_t _) + { return (unsigned) (_.second - _.first) & mask; }; + delta = get_delta (*glyphs); + if (!hb_all (++(+glyphs), delta, get_delta)) format += 1; + } + + u.format = format; + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, + + glyphs + | hb_map_retains_sorting (hb_first), + delta)); + case 2: return_trace (u.format2.serialize (c, glyphs)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (u.format3.serialize (c, + + glyphs + | hb_map_retains_sorting (hb_first), + delta)); + case 4: return_trace (u.format4.serialize (c, glyphs)); +#endif + default:return_trace (false); + } + } +}; + +template +static void +SingleSubst_serialize (hb_serialize_context_t *c, + Iterator it) +{ c->start_embed ()->serialize (c, it); } + +} +} +} + +#endif /* OT_LAYOUT_GSUB_SINGLESUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat1.hh new file mode 100644 index 00000000000..850be86c043 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat1.hh @@ -0,0 +1,204 @@ +#ifndef OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct SingleSubstFormat1_3 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + typename Types::HBUINT + deltaGlyphID; /* Add to original GlyphID to get + * substitute GlyphID, modulo 0x10000 */ + + public: + DEFINE_SIZE_STATIC (2 + 2 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + /* The coverage table may use a range to represent a set + * of glyphs, which means a small number of bytes can + * generate a large glyph set. Manually modify the + * sanitizer max ops to take this into account. + * + * Note: This check *must* be right after coverage sanitize. */ + c->check_ops ((this + coverage).get_population () >> 1)); + } + + hb_codepoint_t get_mask () const + { return (1 << (8 * Types::size)) - 1; } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + bool may_have_non_1to1 () const + { return false; } + + void closure (hb_closure_context_t *c) const + { + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + /* Help fuzzer avoid this function as much. */ + unsigned pop = (this+coverage).get_population (); + if (pop >= mask) + return; + + hb_set_t intersection; + (this+coverage).intersect_set (c->parent_active_glyphs (), intersection); + + /* In degenerate fuzzer-found fonts, but not real fonts, + * this table can keep adding new glyphs in each round of closure. + * Refuse to close-over, if it maps glyph range to overlapping range. */ + hb_codepoint_t min_before = intersection.get_min (); + hb_codepoint_t max_before = intersection.get_max (); + hb_codepoint_t min_after = (min_before + d) & mask; + hb_codepoint_t max_after = (max_before + d) & mask; + if (intersection.get_population () == max_before - min_before + 1 && + ((min_before <= min_after && min_after <= max_before) || + (min_before <= max_after && max_after <= max_before))) + return; + + + hb_iter (intersection) + | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; }) + | hb_sink (c->output) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + + hb_iter (this+coverage) + | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; }) + | hb_sink (c->output) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + unsigned + get_glyph_alternates (hb_codepoint_t glyph_id, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (likely (index == NOT_COVERED)) + { + if (alternate_count) + *alternate_count = 0; + return 0; + } + + if (alternate_count && *alternate_count) + { + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + glyph_id = (glyph_id + d) & mask; + + *alternate_glyphs = glyph_id; + *alternate_count = 1; + } + + return 1; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_codepoint_t glyph_id = c->buffer->cur().codepoint; + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (likely (index == NOT_COVERED)) return_trace (false); + + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + glyph_id = (glyph_id + d) & mask; + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (single substitution)", + c->buffer->idx); + } + + c->replace_glyph (glyph_id); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (single substitution)", + c->buffer->idx - 1u); + } + + return_trace (true); + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator glyphs, + unsigned delta) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false); + c->check_assign (deltaGlyphID, delta, HB_SERIALIZE_ERROR_INT_OVERFLOW); + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + hb_set_t intersection; + (this+coverage).intersect_set (glyphset, intersection); + + auto it = + + hb_iter (intersection) + | hb_map_retains_sorting ([d, mask] (hb_codepoint_t g) { + return hb_codepoint_pair_t (g, + (g + d) & mask); }) + | hb_filter (glyphset, hb_second) + | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t + { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) + ; + + bool ret = bool (it); + SingleSubst_serialize (c->serializer, it); + return_trace (ret); + } +}; + +} +} +} + + +#endif /* OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat2.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat2.hh new file mode 100644 index 00000000000..9c651abe71d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat2.hh @@ -0,0 +1,176 @@ +#ifndef OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH +#define OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct SingleSubstFormat2_4 +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of + substitute; /* Array of substitute + * GlyphIDs--ordered by Coverage Index */ + + public: + DEFINE_SIZE_ARRAY (4 + Types::size, substitute); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && substitute.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + bool may_have_non_1to1 () const + { return false; } + + void closure (hb_closure_context_t *c) const + { + auto &cov = this+coverage; + auto &glyph_set = c->parent_active_glyphs (); + + if (substitute.len > glyph_set.get_population () * 4) + { + for (auto g : glyph_set) + { + unsigned i = cov.get_coverage (g); + if (i == NOT_COVERED || i >= substitute.len) + continue; + c->output->add (substitute.arrayZ[i]); + } + + return; + } + + + hb_zip (cov, substitute) + | hb_filter (glyph_set, hb_first) + | hb_map (hb_second) + | hb_sink (c->output) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, substitute) + | hb_map (hb_second) + | hb_sink (c->output) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + unsigned + get_glyph_alternates (hb_codepoint_t glyph_id, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (likely (index == NOT_COVERED)) + { + if (alternate_count) + *alternate_count = 0; + return 0; + } + + if (alternate_count && *alternate_count) + { + glyph_id = substitute[index]; + + *alternate_glyphs = glyph_id; + *alternate_count = 1; + } + + return 1; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + if (unlikely (index >= substitute.len)) return_trace (false); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (single substitution)", + c->buffer->idx); + } + + c->replace_glyph (substitute[index]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (single substitution)", + c->buffer->idx - 1u); + } + + return_trace (true); + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + auto substitutes = + + it + | hb_map (hb_second) + ; + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false); + if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false); + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_zip (this+coverage, substitute) + | hb_filter (glyphset, hb_first) + | hb_filter (glyphset, hb_second) + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_codepoint_pair_t + { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) + ; + + bool ret = bool (it); + SingleSubst_serialize (c->serializer, it); + return_trace (ret); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookup.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookup.hh new file mode 100644 index 00000000000..d49dcc0e0fd --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookup.hh @@ -0,0 +1,220 @@ +#ifndef OT_LAYOUT_GSUB_SUBSTLOOKUP_HH +#define OT_LAYOUT_GSUB_SUBSTLOOKUP_HH + +#include "Common.hh" +#include "SubstLookupSubTable.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct SubstLookup : Lookup +{ + using SubTable = SubstLookupSubTable; + + bool sanitize (hb_sanitize_context_t *c) const + { return Lookup::sanitize (c); } + + const SubTable& get_subtable (unsigned int i) const + { return Lookup::get_subtable (i); } + + static inline bool lookup_type_is_reverse (unsigned int lookup_type) + { return lookup_type == SubTable::ReverseChainSingle; } + + bool is_reverse () const + { + unsigned int type = get_type (); + if (unlikely (type == SubTable::Extension)) + return get_subtable (0).u.extension.is_reverse (); + return lookup_type_is_reverse (type); + } + + bool may_have_non_1to1 () const + { + hb_have_non_1to1_context_t c; + return dispatch (&c); + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + return_trace (dispatch (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c); + } + + hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const + { + if (!c->should_visit_lookup (this_index)) + return hb_closure_context_t::default_return_value (); + + c->set_recurse_func (dispatch_closure_recurse_func); + + hb_closure_context_t::return_t ret = dispatch (c); + + c->flush (); + + return ret; + } + + hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const + { + if (c->is_lookup_visited (this_index)) + return hb_closure_lookups_context_t::default_return_value (); + + c->set_lookup_visited (this_index); + if (!intersects (c->glyphs)) + { + c->set_lookup_inactive (this_index); + return hb_closure_lookups_context_t::default_return_value (); + } + + hb_closure_lookups_context_t::return_t ret = dispatch (c); + return ret; + } + + hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const + { + c->set_recurse_func (dispatch_recurse_func); + return dispatch (c); + } + + template + void collect_coverage (set_t *glyphs) const + { + hb_collect_coverage_context_t c (glyphs); + dispatch (&c); + } + + bool would_apply (hb_would_apply_context_t *c, + const hb_ot_layout_lookup_accelerator_t *accel) const + { + if (unlikely (!c->len)) return false; + if (!accel->may_have (c->glyphs[0])) return false; + return dispatch (c); + } + + template + bool serialize_single (hb_serialize_context_t *c, + uint32_t lookup_props, + Glyphs glyphs, + Substitutes substitutes) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false); + if (c->push ()->u.single.serialize (c, hb_zip (glyphs, substitutes))) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + template + bool serialize (hb_serialize_context_t *c, + uint32_t lookup_props, + Iterator it) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false); + if (c->push ()->u.multiple. + serialize (c, it)) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + bool serialize_alternate (hb_serialize_context_t *c, + uint32_t lookup_props, + hb_sorted_array_t glyphs, + hb_array_t alternate_len_list, + hb_array_t alternate_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false); + + if (c->push ()->u.alternate. + serialize (c, + glyphs, + alternate_len_list, + alternate_glyphs_list)) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + bool serialize_ligature (hb_serialize_context_t *c, + uint32_t lookup_props, + hb_sorted_array_t first_glyphs, + hb_array_t ligature_per_first_glyph_count_list, + hb_array_t ligatures_list, + hb_array_t component_count_list, + hb_array_t component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false); + if (c->push ()->u.ligature. + serialize (c, + first_glyphs, + ligature_per_first_glyph_count_list, + ligatures_list, + component_count_list, + component_list)) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + template + static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); + + static inline typename hb_closure_context_t::return_t closure_glyphs_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index); + + static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index) + { + if (!c->should_visit_lookup (lookup_index)) + return hb_empty_t (); + + hb_closure_context_t::return_t ret = closure_glyphs_recurse_func (c, lookup_index, covered_seq_indices, seq_index, end_index); + + /* While in theory we should flush here, it will cause timeouts because a recursive + * lookup can keep growing the glyph set. Skip, and outer loop will retry up to + * HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */ + //c->flush (); + + return ret; + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { return Lookup::dispatch (c, std::forward (ds)...); } + + bool subset (hb_subset_context_t *c) const + { return Lookup::subset (c); } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GSUB_SUBSTLOOKUP_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookupSubTable.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookupSubTable.hh new file mode 100644 index 00000000000..a525fba0399 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookupSubTable.hh @@ -0,0 +1,77 @@ +#ifndef OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH +#define OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH + +#include "Common.hh" +#include "SingleSubst.hh" +#include "MultipleSubst.hh" +#include "AlternateSubst.hh" +#include "LigatureSubst.hh" +#include "ContextSubst.hh" +#include "ChainContextSubst.hh" +#include "ExtensionSubst.hh" +#include "ReverseChainSingleSubst.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct SubstLookupSubTable +{ + friend struct ::OT::Lookup; + friend struct SubstLookup; + + protected: + union { + SingleSubst single; + MultipleSubst multiple; + AlternateSubst alternate; + LigatureSubst ligature; + ContextSubst context; + ChainContextSubst chainContext; + ExtensionSubst extension; + ReverseChainSingleSubst reverseChainContextSingle; + } u; + public: + DEFINE_SIZE_MIN (0); + + enum Type { + Single = 1, + Multiple = 2, + Alternate = 3, + Ligature = 4, + Context = 5, + ChainContext = 6, + Extension = 7, + ReverseChainSingle = 8 + }; + + template + typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const + { + TRACE_DISPATCH (this, lookup_type); + switch (lookup_type) { + case Single: return_trace (u.single.dispatch (c, std::forward (ds)...)); + case Multiple: return_trace (u.multiple.dispatch (c, std::forward (ds)...)); + case Alternate: return_trace (u.alternate.dispatch (c, std::forward (ds)...)); + case Ligature: return_trace (u.ligature.dispatch (c, std::forward (ds)...)); + case Context: return_trace (u.context.dispatch (c, std::forward (ds)...)); + case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward (ds)...)); + case Extension: return_trace (u.extension.dispatch (c, std::forward (ds)...)); + case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c, std::forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c, lookup_type); + } +}; + + +} +} +} + +#endif /* HB_OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/types.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/types.hh new file mode 100644 index 00000000000..6a43403e94b --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/types.hh @@ -0,0 +1,66 @@ +/* + * Copyright ยฉ 2007,2008,2009 Red Hat, Inc. + * Copyright ยฉ 2010,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_TYPES_HH +#define OT_LAYOUT_TYPES_HH + +namespace OT { +namespace Layout { + +struct SmallTypes { + static constexpr unsigned size = 2; + using large_int = uint32_t; + using HBUINT = HBUINT16; + using HBGlyphID = HBGlyphID16; + using Offset = Offset16; + template + using OffsetTo = OT::Offset16To; + template + using ArrayOf = OT::Array16Of; + template + using SortedArrayOf = OT::SortedArray16Of; +}; + +struct MediumTypes { + static constexpr unsigned size = 3; + using large_int = uint64_t; + using HBUINT = HBUINT24; + using HBGlyphID = HBGlyphID24; + using Offset = Offset24; + template + using OffsetTo = OT::Offset24To; + template + using ArrayOf = OT::Array24Of; + template + using SortedArrayOf = OT::SortedArray24Of; +}; + +} +} + +#endif /* OT_LAYOUT_TYPES_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/CompositeGlyph.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/CompositeGlyph.hh new file mode 100644 index 00000000000..94e00d3aea3 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/CompositeGlyph.hh @@ -0,0 +1,399 @@ +#ifndef OT_GLYF_COMPOSITEGLYPH_HH +#define OT_GLYF_COMPOSITEGLYPH_HH + + +#include "../../hb-open-type.hh" +#include "composite-iter.hh" + + +namespace OT { +namespace glyf_impl { + + +struct CompositeGlyphRecord +{ + protected: + enum composite_glyph_flag_t + { + ARG_1_AND_2_ARE_WORDS = 0x0001, + ARGS_ARE_XY_VALUES = 0x0002, + ROUND_XY_TO_GRID = 0x0004, + WE_HAVE_A_SCALE = 0x0008, + MORE_COMPONENTS = 0x0020, + WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, + WE_HAVE_A_TWO_BY_TWO = 0x0080, + WE_HAVE_INSTRUCTIONS = 0x0100, + USE_MY_METRICS = 0x0200, + OVERLAP_COMPOUND = 0x0400, + SCALED_COMPONENT_OFFSET = 0x0800, + UNSCALED_COMPONENT_OFFSET = 0x1000, +#ifndef HB_NO_BEYOND_64K + GID_IS_24BIT = 0x2000 +#endif + }; + + public: + unsigned int get_size () const + { + unsigned int size = min_size; + /* glyphIndex is 24bit instead of 16bit */ +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) size += HBGlyphID24::static_size - HBGlyphID16::static_size; +#endif + /* arg1 and 2 are int16 */ + if (flags & ARG_1_AND_2_ARE_WORDS) size += 4; + /* arg1 and 2 are int8 */ + else size += 2; + + /* One x 16 bit (scale) */ + if (flags & WE_HAVE_A_SCALE) size += 2; + /* Two x 16 bit (xscale, yscale) */ + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4; + /* Four x 16 bit (xscale, scale01, scale10, yscale) */ + else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8; + + return size; + } + + void drop_instructions_flag () { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; } + void set_overlaps_flag () + { + flags = (uint16_t) flags | OVERLAP_COMPOUND; + } + + bool has_instructions () const { return flags & WE_HAVE_INSTRUCTIONS; } + + bool has_more () const { return flags & MORE_COMPONENTS; } + bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } + bool is_anchored () const { return !(flags & ARGS_ARE_XY_VALUES); } + void get_anchor_points (unsigned int &point1, unsigned int &point2) const + { + const auto *p = &StructAfter (flags); +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + p += HBGlyphID24::static_size; + else +#endif + p += HBGlyphID16::static_size; + if (flags & ARG_1_AND_2_ARE_WORDS) + { + point1 = ((const HBUINT16 *) p)[0]; + point2 = ((const HBUINT16 *) p)[1]; + } + else + { + point1 = p[0]; + point2 = p[1]; + } + } + + void transform_points (contour_point_vector_t &points, + const float (&matrix)[4], + const contour_point_t &trans) const + { + if (scaled_offsets ()) + { + points.translate (trans); + points.transform (matrix); + } + else + { + points.transform (matrix); + points.translate (trans); + } + } + + bool get_points (contour_point_vector_t &points) const + { + float matrix[4]; + contour_point_t trans; + get_transformation (matrix, trans); + if (unlikely (!points.resize (points.length + 1))) return false; + points[points.length - 1] = trans; + return true; + } + + unsigned compile_with_point (const contour_point_t &point, + char *out) const + { + const HBINT8 *p = &StructAfter (flags); +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + p += HBGlyphID24::static_size; + else +#endif + p += HBGlyphID16::static_size; + + unsigned len = get_size (); + unsigned len_before_val = (const char *)p - (const char *)this; + if (flags & ARG_1_AND_2_ARE_WORDS) + { + // no overflow, copy value + hb_memcpy (out, this, len); + + HBINT16 *o = reinterpret_cast (out + len_before_val); + o[0] = roundf (point.x); + o[1] = roundf (point.y); + } + else + { + int new_x = roundf (point.x); + int new_y = roundf (point.y); + if (new_x <= 127 && new_x >= -128 && + new_y <= 127 && new_y >= -128) + { + hb_memcpy (out, this, len); + HBINT8 *o = reinterpret_cast (out + len_before_val); + o[0] = new_x; + o[1] = new_y; + } + else + { + // new point value has an int8 overflow + hb_memcpy (out, this, len_before_val); + + //update flags + CompositeGlyphRecord *o = reinterpret_cast (out); + o->flags = flags | ARG_1_AND_2_ARE_WORDS; + out += len_before_val; + + HBINT16 new_value; + new_value = new_x; + hb_memcpy (out, &new_value, HBINT16::static_size); + out += HBINT16::static_size; + + new_value = new_y; + hb_memcpy (out, &new_value, HBINT16::static_size); + out += HBINT16::static_size; + + hb_memcpy (out, p+2, len - len_before_val - 2); + len += 2; + } + } + return len; + } + + protected: + bool scaled_offsets () const + { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } + + public: + bool get_transformation (float (&matrix)[4], contour_point_t &trans) const + { + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; + + const auto *p = &StructAfter (flags); +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + p += HBGlyphID24::static_size; + else +#endif + p += HBGlyphID16::static_size; + int tx, ty; + if (flags & ARG_1_AND_2_ARE_WORDS) + { + tx = *(const HBINT16 *) p; + p += HBINT16::static_size; + ty = *(const HBINT16 *) p; + p += HBINT16::static_size; + } + else + { + tx = *p++; + ty = *p++; + } + if (is_anchored ()) tx = ty = 0; + + trans.init ((float) tx, (float) ty); + + { + const F2DOT14 *points = (const F2DOT14 *) p; + if (flags & WE_HAVE_A_SCALE) + { + matrix[0] = matrix[3] = points[0].to_float (); + return true; + } + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) + { + matrix[0] = points[0].to_float (); + matrix[3] = points[1].to_float (); + return true; + } + else if (flags & WE_HAVE_A_TWO_BY_TWO) + { + matrix[0] = points[0].to_float (); + matrix[1] = points[1].to_float (); + matrix[2] = points[2].to_float (); + matrix[3] = points[3].to_float (); + return true; + } + } + return tx || ty; + } + + hb_codepoint_t get_gid () const + { +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + return StructAfter (flags); + else +#endif + return StructAfter (flags); + } + void set_gid (hb_codepoint_t gid) + { +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + StructAfter (flags) = gid; + else +#endif + /* TODO assert? */ + StructAfter (flags) = gid; + } + +#ifndef HB_NO_BEYOND_64K + void lower_gid_24_to_16 () + { + hb_codepoint_t gid = get_gid (); + if (!(flags & GID_IS_24BIT) || gid > 0xFFFFu) + return; + + /* Lower the flag and move the rest of the struct down. */ + + unsigned size = get_size (); + char *end = (char *) this + size; + char *p = &StructAfter (flags); + p += HBGlyphID24::static_size; + + flags = flags & ~GID_IS_24BIT; + set_gid (gid); + + memmove (p - HBGlyphID24::static_size + HBGlyphID16::static_size, p, end - p); + } +#endif + + protected: + HBUINT16 flags; + HBUINT24 pad; + public: + DEFINE_SIZE_MIN (4); +}; + +using composite_iter_t = composite_iter_tmpl; + +struct CompositeGlyph +{ + const GlyphHeader &header; + hb_bytes_t bytes; + CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + composite_iter_t iter () const + { return composite_iter_t (bytes, &StructAfter (header)); } + + unsigned int instructions_length (hb_bytes_t bytes) const + { + unsigned int start = bytes.length; + unsigned int end = bytes.length; + const CompositeGlyphRecord *last = nullptr; + for (auto &item : iter ()) + last = &item; + if (unlikely (!last)) return 0; + + if (last->has_instructions ()) + start = (char *) last - &bytes + last->get_size (); + if (unlikely (start > end)) return 0; + return end - start; + } + + /* Trimming for composites not implemented. + * If removing hints it falls out of that. */ + const hb_bytes_t trim_padding () const { return bytes; } + + void drop_hints () + { + for (const auto &_ : iter ()) + const_cast (_).drop_instructions_flag (); + } + + /* Chop instructions off the end */ + void drop_hints_bytes (hb_bytes_t &dest_start) const + { dest_start = bytes.sub_array (0, bytes.length - instructions_length (bytes)); } + + void set_overlaps_flag () + { + CompositeGlyphRecord& glyph_chain = const_cast ( + StructAfter (header)); + if (!bytes.check_range(&glyph_chain, CompositeGlyphRecord::min_size)) + return; + glyph_chain.set_overlaps_flag (); + } + + bool compile_bytes_with_deltas (const hb_bytes_t &source_bytes, + const contour_point_vector_t &points_with_deltas, + hb_bytes_t &dest_bytes /* OUT */) + { + if (source_bytes.length <= GlyphHeader::static_size || + header.numberOfContours != -1) + { + dest_bytes = hb_bytes_t (); + return true; + } + + unsigned source_len = source_bytes.length - GlyphHeader::static_size; + + /* try to allocate more memories than source glyph bytes + * in case that there might be an overflow for int8 value + * and we would need to use int16 instead */ + char *o = (char *) hb_calloc (source_len * 2, sizeof (char)); + if (unlikely (!o)) return false; + + const CompositeGlyphRecord *c = reinterpret_cast (source_bytes.arrayZ + GlyphHeader::static_size); + auto it = composite_iter_t (hb_bytes_t ((const char *)c, source_len), c); + + char *p = o; + unsigned i = 0, source_comp_len = 0; + for (const auto &component : it) + { + /* last 4 points in points_with_deltas are phantom points and should not be included */ + if (i >= points_with_deltas.length - 4) { + free (o); + return false; + } + + unsigned comp_len = component.get_size (); + if (component.is_anchored ()) + { + hb_memcpy (p, &component, comp_len); + p += comp_len; + } + else + { + unsigned new_len = component.compile_with_point (points_with_deltas[i], p); + p += new_len; + } + i++; + source_comp_len += comp_len; + } + + //copy instructions if any + if (source_len > source_comp_len) + { + unsigned instr_len = source_len - source_comp_len; + hb_memcpy (p, (const char *)c + source_comp_len, instr_len); + p += instr_len; + } + + unsigned len = p - o; + dest_bytes = hb_bytes_t (o, len); + return true; + } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_COMPOSITEGLYPH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/Glyph.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/Glyph.hh new file mode 100644 index 00000000000..9e15a6e6d03 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/Glyph.hh @@ -0,0 +1,566 @@ +#ifndef OT_GLYF_GLYPH_HH +#define OT_GLYF_GLYPH_HH + + +#include "../../hb-open-type.hh" + +#include "GlyphHeader.hh" +#include "SimpleGlyph.hh" +#include "CompositeGlyph.hh" +#include "VarCompositeGlyph.hh" +#include "coord-setter.hh" + + +namespace OT { + +struct glyf_accelerator_t; + +namespace glyf_impl { + + +enum phantom_point_index_t +{ + PHANTOM_LEFT = 0, + PHANTOM_RIGHT = 1, + PHANTOM_TOP = 2, + PHANTOM_BOTTOM = 3, + PHANTOM_COUNT = 4 +}; + +struct Glyph +{ + enum glyph_type_t { + EMPTY, + SIMPLE, + COMPOSITE, +#ifndef HB_NO_VAR_COMPOSITES + VAR_COMPOSITE, +#endif + }; + + public: + composite_iter_t get_composite_iterator () const + { + if (type != COMPOSITE) return composite_iter_t (); + return CompositeGlyph (*header, bytes).iter (); + } + var_composite_iter_t get_var_composite_iterator () const + { +#ifndef HB_NO_VAR_COMPOSITES + if (type != VAR_COMPOSITE) return var_composite_iter_t (); + return VarCompositeGlyph (*header, bytes).iter (); +#else + return var_composite_iter_t (); +#endif + } + + const hb_bytes_t trim_padding () const + { + switch (type) { +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: return VarCompositeGlyph (*header, bytes).trim_padding (); +#endif + case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding (); + case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding (); + case EMPTY: return bytes; + default: return bytes; + } + } + + void drop_hints () + { + switch (type) { +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: return; // No hinting +#endif + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return; + case EMPTY: return; + } + } + + void set_overlaps_flag () + { + switch (type) { +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: return; // No overlaps flag +#endif + case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return; + case SIMPLE: SimpleGlyph (*header, bytes).set_overlaps_flag (); return; + case EMPTY: return; + } + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + switch (type) { +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: return; // No hinting +#endif + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return; + case EMPTY: return; + } + } + + void update_mtx (const hb_subset_plan_t *plan, + int xMin, int xMax, + int yMin, int yMax, + const contour_point_vector_t &all_points) const + { + hb_codepoint_t new_gid = 0; + if (!plan->new_gid_for_old_gid (gid, &new_gid)) + return; + + if (type != EMPTY) + { + plan->bounds_width_map.set (new_gid, xMax - xMin); + plan->bounds_height_map.set (new_gid, yMax - yMin); + } + + unsigned len = all_points.length; + float leftSideX = all_points[len - 4].x; + float rightSideX = all_points[len - 3].x; + float topSideY = all_points[len - 2].y; + float bottomSideY = all_points[len - 1].y; + + signed hori_aw = roundf (rightSideX - leftSideX); + if (hori_aw < 0) hori_aw = 0; + int lsb = roundf (xMin - leftSideX); + plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb)); + //flag value should be computed using non-empty glyphs + if (type != EMPTY && lsb != xMin) + plan->head_maxp_info.allXMinIsLsb = false; + + signed vert_aw = roundf (topSideY - bottomSideY); + if (vert_aw < 0) vert_aw = 0; + int tsb = roundf (topSideY - yMax); + plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb)); + } + + bool compile_header_bytes (const hb_subset_plan_t *plan, + const contour_point_vector_t &all_points, + hb_bytes_t &dest_bytes /* OUT */) const + { + GlyphHeader *glyph_header = nullptr; + if (!plan->pinned_at_default && type != EMPTY && all_points.length >= 4) + { + glyph_header = (GlyphHeader *) hb_calloc (1, GlyphHeader::static_size); + if (unlikely (!glyph_header)) return false; + } + + float xMin = 0, xMax = 0; + float yMin = 0, yMax = 0; + if (all_points.length > 4) + { + xMin = xMax = all_points[0].x; + yMin = yMax = all_points[0].y; + } + + for (unsigned i = 1; i < all_points.length - 4; i++) + { + float x = all_points[i].x; + float y = all_points[i].y; + xMin = hb_min (xMin, x); + xMax = hb_max (xMax, x); + yMin = hb_min (yMin, y); + yMax = hb_max (yMax, y); + } + + update_mtx (plan, roundf (xMin), roundf (xMax), roundf (yMin), roundf (yMax), all_points); + + int rounded_xMin = roundf (xMin); + int rounded_xMax = roundf (xMax); + int rounded_yMin = roundf (yMin); + int rounded_yMax = roundf (yMax); + + if (type != EMPTY) + { + plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, rounded_xMin); + plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, rounded_yMin); + plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, rounded_xMax); + plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, rounded_yMax); + } + + /* when pinned at default, no need to compile glyph header + * and for empty glyphs: all_points only include phantom points. + * just update metrics and then return */ + if (!glyph_header) + return true; + + glyph_header->numberOfContours = header->numberOfContours; + + glyph_header->xMin = rounded_xMin; + glyph_header->yMin = rounded_yMin; + glyph_header->xMax = rounded_xMax; + glyph_header->yMax = rounded_yMax; + + dest_bytes = hb_bytes_t ((const char *)glyph_header, GlyphHeader::static_size); + return true; + } + + bool compile_bytes_with_deltas (const hb_subset_plan_t *plan, + hb_font_t *font, + const glyf_accelerator_t &glyf, + hb_bytes_t &dest_start, /* IN/OUT */ + hb_bytes_t &dest_end /* OUT */) + { + contour_point_vector_t all_points, points_with_deltas; + unsigned composite_contours = 0; + head_maxp_info_t *head_maxp_info_p = &plan->head_maxp_info; + unsigned *composite_contours_p = &composite_contours; + + // don't compute head/maxp values when glyph has no contours(type is EMPTY) + // also ignore .notdef glyph when --notdef-outline is not enabled + if (type == EMPTY || + (gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))) + { + head_maxp_info_p = nullptr; + composite_contours_p = nullptr; + } + + if (!get_points (font, glyf, all_points, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false)) + return false; + + // .notdef, set type to empty so we only update metrics and don't compile bytes for + // it + if (gid == 0 && + !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) + { + type = EMPTY; + dest_start = hb_bytes_t (); + dest_end = hb_bytes_t (); + } + + //dont compile bytes when pinned at default, just recalculate bounds + if (!plan->pinned_at_default) + { + switch (type) + { +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: + // TODO + dest_end = hb_bytes_t (); + break; +#endif + + case COMPOSITE: + if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start, + points_with_deltas, + dest_end)) + return false; + break; + case SIMPLE: + if (!SimpleGlyph (*header, bytes).compile_bytes_with_deltas (all_points, + plan->flags & HB_SUBSET_FLAGS_NO_HINTING, + dest_end)) + return false; + break; + case EMPTY: + /* set empty bytes for empty glyph + * do not use source glyph's pointers */ + dest_start = hb_bytes_t (); + dest_end = hb_bytes_t (); + break; + } + } + + if (!compile_header_bytes (plan, all_points, dest_start)) + { + dest_end.fini (); + return false; + } + return true; + } + + + /* Note: Recursively calls itself. + * all_points includes phantom points + */ + template + bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator, + contour_point_vector_t &all_points /* OUT */, + contour_point_vector_t *points_with_deltas = nullptr, /* OUT */ + head_maxp_info_t * head_maxp_info = nullptr, /* OUT */ + unsigned *composite_contours = nullptr, /* OUT */ + bool shift_points_hori = true, + bool use_my_metrics = true, + bool phantom_only = false, + hb_array_t coords = hb_array_t (), + unsigned int depth = 0, + unsigned *edge_count = nullptr) const + { + if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false; + unsigned stack_edge_count = 0; + if (!edge_count) edge_count = &stack_edge_count; + if (unlikely (*edge_count > HB_GLYF_MAX_EDGE_COUNT)) return false; + (*edge_count)++; + + if (head_maxp_info) + { + head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth); + } + + if (!coords) + coords = hb_array (font->coords, font->num_coords); + + contour_point_vector_t stack_points; + bool inplace = type == SIMPLE && all_points.length == 0; + /* Load into all_points if it's empty, as an optimization. */ + contour_point_vector_t &points = inplace ? all_points : stack_points; + + switch (type) { + case SIMPLE: + if (depth == 0 && head_maxp_info) + head_maxp_info->maxContours = hb_max (head_maxp_info->maxContours, (unsigned) header->numberOfContours); + if (depth > 0 && composite_contours) + *composite_contours += (unsigned) header->numberOfContours; + if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only))) + return false; + break; + case COMPOSITE: + { + for (auto &item : get_composite_iterator ()) + if (unlikely (!item.get_points (points))) return false; + break; + } +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: + { + for (auto &item : get_var_composite_iterator ()) + if (unlikely (!item.get_points (points))) return false; + } +#endif + case EMPTY: + break; + } + + /* Init phantom points */ + if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; + hb_array_t phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); + { + int lsb = 0; + int h_delta = glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ? + (int) header->xMin - lsb : 0; + HB_UNUSED int tsb = 0; + int v_orig = (int) header->yMax + +#ifndef HB_NO_VERTICAL + ((void) glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb) +#else + 0 +#endif + ; + unsigned h_adv = glyf_accelerator.hmtx->get_advance_without_var_unscaled (gid); + unsigned v_adv = +#ifndef HB_NO_VERTICAL + glyf_accelerator.vmtx->get_advance_without_var_unscaled (gid) +#else + - font->face->get_upem () +#endif + ; + phantoms[PHANTOM_LEFT].x = h_delta; + phantoms[PHANTOM_RIGHT].x = (int) h_adv + h_delta; + phantoms[PHANTOM_TOP].y = v_orig; + phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv; + } + +#ifndef HB_NO_VAR + glyf_accelerator.gvar->apply_deltas_to_points (gid, + coords, + points.as_array ()); +#endif + + // mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it + // with child glyphs' points + if (points_with_deltas != nullptr && depth == 0 && type == COMPOSITE) + { + if (unlikely (!points_with_deltas->resize (points.length))) return false; + points_with_deltas->copy_vector (points); + } + + switch (type) { + case SIMPLE: + if (depth == 0 && head_maxp_info) + head_maxp_info->maxPoints = hb_max (head_maxp_info->maxPoints, points.length - 4); + if (!inplace) + all_points.extend (points.as_array ()); + break; + case COMPOSITE: + { + contour_point_vector_t comp_points; + unsigned int comp_index = 0; + for (auto &item : get_composite_iterator ()) + { + comp_points.reset (); + if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ()) + .get_points (font, + glyf_accelerator, + comp_points, + points_with_deltas, + head_maxp_info, + composite_contours, + shift_points_hori, + use_my_metrics, + phantom_only, + coords, + depth + 1, + edge_count))) + return false; + + /* Copy phantom points from component if USE_MY_METRICS flag set */ + if (use_my_metrics && item.is_use_my_metrics ()) + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; + + float matrix[4]; + contour_point_t default_trans; + item.get_transformation (matrix, default_trans); + + /* Apply component transformation & translation (with deltas applied) */ + item.transform_points (comp_points, matrix, points[comp_index]); + + if (item.is_anchored ()) + { + unsigned int p1, p2; + item.get_anchor_points (p1, p2); + if (likely (p1 < all_points.length && p2 < comp_points.length)) + { + contour_point_t delta; + delta.init (all_points[p1].x - comp_points[p2].x, + all_points[p1].y - comp_points[p2].y); + + comp_points.translate (delta); + } + } + + all_points.extend (comp_points.as_array ().sub_array (0, comp_points.length - PHANTOM_COUNT)); + + if (all_points.length > HB_GLYF_MAX_POINTS) + return false; + + comp_index++; + } + + if (head_maxp_info && depth == 0) + { + if (composite_contours) + head_maxp_info->maxCompositeContours = hb_max (head_maxp_info->maxCompositeContours, *composite_contours); + head_maxp_info->maxCompositePoints = hb_max (head_maxp_info->maxCompositePoints, all_points.length); + head_maxp_info->maxComponentElements = hb_max (head_maxp_info->maxComponentElements, comp_index); + } + all_points.extend (phantoms); + } break; +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: + { + contour_point_vector_t comp_points; + hb_array_t points_left = points.as_array (); + for (auto &item : get_var_composite_iterator ()) + { + unsigned item_num_points = item.get_num_points (); + hb_array_t record_points = points_left.sub_array (0, item_num_points); + + comp_points.reset (); + + auto component_coords = coords; + if (item.is_reset_unspecified_axes ()) + component_coords = hb_array (); + + coord_setter_t coord_setter (component_coords); + item.set_variations (coord_setter, record_points); + + if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ()) + .get_points (font, + glyf_accelerator, + comp_points, + points_with_deltas, + head_maxp_info, + nullptr, + shift_points_hori, + use_my_metrics, + phantom_only, + coord_setter.get_coords (), + depth + 1, + edge_count))) + return false; + + /* Apply component transformation */ + item.transform_points (record_points, comp_points); + + /* Copy phantom points from component if USE_MY_METRICS flag set */ + if (use_my_metrics && item.is_use_my_metrics ()) + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; + + all_points.extend (comp_points.as_array ().sub_array (0, comp_points.length - PHANTOM_COUNT)); + + if (all_points.length > HB_GLYF_MAX_POINTS) + return false; + + points_left += item_num_points; + } + all_points.extend (phantoms); + } break; +#endif + case EMPTY: + all_points.extend (phantoms); + break; + } + + if (depth == 0 && shift_points_hori) /* Apply at top level */ + { + /* Undocumented rasterizer behavior: + * Shift points horizontally by the updated left side bearing + */ + contour_point_t delta; + delta.init (-phantoms[PHANTOM_LEFT].x, 0.f); + if (delta.x) all_points.translate (delta); + } + + return !all_points.in_error (); + } + + bool get_extents_without_var_scaled (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator, + hb_glyph_extents_t *extents) const + { + if (type == EMPTY) return true; /* Empty glyph; zero extents. */ + return header->get_extents_without_var_scaled (font, glyf_accelerator, gid, extents); + } + + hb_bytes_t get_bytes () const { return bytes; } + glyph_type_t get_type () const { return type; } + const GlyphHeader *get_header () const { return header; } + + Glyph () : bytes (), + header (bytes.as ()), + gid (-1), + type(EMPTY) + {} + + Glyph (hb_bytes_t bytes_, + hb_codepoint_t gid_ = (unsigned) -1) : bytes (bytes_), + header (bytes.as ()), + gid (gid_) + { + int num_contours = header->numberOfContours; + if (unlikely (num_contours == 0)) type = EMPTY; + else if (num_contours > 0) type = SIMPLE; +#ifndef HB_NO_VAR_COMPOSITES + else if (num_contours == -2) type = VAR_COMPOSITE; +#endif + else type = COMPOSITE; /* negative numbers */ + } + + protected: + hb_bytes_t bytes; + const GlyphHeader *header; + hb_codepoint_t gid; + glyph_type_t type; +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYPH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/GlyphHeader.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/GlyphHeader.hh new file mode 100644 index 00000000000..a43b6691ab4 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/GlyphHeader.hh @@ -0,0 +1,52 @@ +#ifndef OT_GLYF_GLYPHHEADER_HH +#define OT_GLYF_GLYPHHEADER_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { +namespace glyf_impl { + + +struct GlyphHeader +{ + bool has_data () const { return numberOfContours; } + + template + bool get_extents_without_var_scaled (hb_font_t *font, const accelerator_t &glyf_accelerator, + hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + /* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */ + /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */ + int lsb = hb_min (xMin, xMax); + (void) glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb); + extents->x_bearing = lsb; + extents->y_bearing = hb_max (yMin, yMax); + extents->width = hb_max (xMin, xMax) - hb_min (xMin, xMax); + extents->height = hb_min (yMin, yMax) - hb_max (yMin, yMax); + + font->scale_glyph_extents (extents); + + return true; + } + + HBINT16 numberOfContours; + /* If the number of contours is + * greater than or equal to zero, + * this is a simple glyph; if negative, + * this is a composite glyph. */ + FWORD xMin; /* Minimum x for coordinate data. */ + FWORD yMin; /* Minimum y for coordinate data. */ + FWORD xMax; /* Maximum x for coordinate data. */ + FWORD yMax; /* Maximum y for coordinate data. */ + public: + DEFINE_SIZE_STATIC (10); +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYPHHEADER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/SimpleGlyph.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/SimpleGlyph.hh new file mode 100644 index 00000000000..b6679b2dae8 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/SimpleGlyph.hh @@ -0,0 +1,345 @@ +#ifndef OT_GLYF_SIMPLEGLYPH_HH +#define OT_GLYF_SIMPLEGLYPH_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { +namespace glyf_impl { + + +struct SimpleGlyph +{ + enum simple_glyph_flag_t + { + FLAG_ON_CURVE = 0x01, + FLAG_X_SHORT = 0x02, + FLAG_Y_SHORT = 0x04, + FLAG_REPEAT = 0x08, + FLAG_X_SAME = 0x10, + FLAG_Y_SAME = 0x20, + FLAG_OVERLAP_SIMPLE = 0x40, + FLAG_CUBIC = 0x80 + }; + + const GlyphHeader &header; + hb_bytes_t bytes; + SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + unsigned int instruction_len_offset () const + { return GlyphHeader::static_size + 2 * header.numberOfContours; } + + unsigned int length (unsigned int instruction_len) const + { return instruction_len_offset () + 2 + instruction_len; } + + bool has_instructions_length () const + { + return instruction_len_offset () + 2 <= bytes.length; + } + + unsigned int instructions_length () const + { + unsigned int instruction_length_offset = instruction_len_offset (); + if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0; + + const HBUINT16 &instructionLength = StructAtOffset (&bytes, instruction_length_offset); + /* Out of bounds of the current glyph */ + if (unlikely (length (instructionLength) > bytes.length)) return 0; + return instructionLength; + } + + const hb_bytes_t trim_padding () const + { + /* based on FontTools _g_l_y_f.py::trim */ + const uint8_t *glyph = (uint8_t*) bytes.arrayZ; + const uint8_t *glyph_end = glyph + bytes.length; + /* simple glyph w/contours, possibly trimmable */ + glyph += instruction_len_offset (); + + if (unlikely (glyph + 2 >= glyph_end)) return hb_bytes_t (); + unsigned int num_coordinates = StructAtOffset (glyph - 2, 0) + 1; + unsigned int num_instructions = StructAtOffset (glyph, 0); + + glyph += 2 + num_instructions; + + unsigned int coord_bytes = 0; + unsigned int coords_with_flags = 0; + while (glyph < glyph_end) + { + uint8_t flag = *glyph; + glyph++; + + unsigned int repeat = 1; + if (flag & FLAG_REPEAT) + { + if (unlikely (glyph >= glyph_end)) return hb_bytes_t (); + repeat = *glyph + 1; + glyph++; + } + + unsigned int xBytes, yBytes; + xBytes = yBytes = 0; + if (flag & FLAG_X_SHORT) xBytes = 1; + else if ((flag & FLAG_X_SAME) == 0) xBytes = 2; + + if (flag & FLAG_Y_SHORT) yBytes = 1; + else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2; + + coord_bytes += (xBytes + yBytes) * repeat; + coords_with_flags += repeat; + if (coords_with_flags >= num_coordinates) break; + } + + if (unlikely (coords_with_flags != num_coordinates)) return hb_bytes_t (); + return bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph)); + } + + /* zero instruction length */ + void drop_hints () + { + if (!has_instructions_length ()) return; + GlyphHeader &glyph_header = const_cast (header); + (HBUINT16 &) StructAtOffset (&glyph_header, instruction_len_offset ()) = 0; + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + unsigned int instructions_len = instructions_length (); + unsigned int glyph_length = length (instructions_len); + dest_start = bytes.sub_array (0, glyph_length - instructions_len); + dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length); + } + + void set_overlaps_flag () + { + if (unlikely (!header.numberOfContours)) return; + + unsigned flags_offset = length (instructions_length ()); + if (unlikely (flags_offset + 1 > bytes.length)) return; + + HBUINT8 &first_flag = (HBUINT8 &) StructAtOffset (&bytes, flags_offset); + first_flag = (uint8_t) first_flag | FLAG_OVERLAP_SIMPLE; + } + + static bool read_flags (const HBUINT8 *&p /* IN/OUT */, + contour_point_vector_t &points_ /* IN/OUT */, + const HBUINT8 *end) + { + unsigned count = points_.length; + for (unsigned int i = 0; i < count;) + { + if (unlikely (p + 1 > end)) return false; + uint8_t flag = *p++; + points_.arrayZ[i++].flag = flag; + if (flag & FLAG_REPEAT) + { + if (unlikely (p + 1 > end)) return false; + unsigned int repeat_count = *p++; + unsigned stop = hb_min (i + repeat_count, count); + for (; i < stop; i++) + points_.arrayZ[i].flag = flag; + } + } + return true; + } + + static bool read_points (const HBUINT8 *&p /* IN/OUT */, + contour_point_vector_t &points_ /* IN/OUT */, + const HBUINT8 *end, + float contour_point_t::*m, + const simple_glyph_flag_t short_flag, + const simple_glyph_flag_t same_flag) + { + int v = 0; + + unsigned count = points_.length; + for (unsigned i = 0; i < count; i++) + { + unsigned flag = points_[i].flag; + if (flag & short_flag) + { + if (unlikely (p + 1 > end)) return false; + if (flag & same_flag) + v += *p++; + else + v -= *p++; + } + else + { + if (!(flag & same_flag)) + { + if (unlikely (p + HBINT16::static_size > end)) return false; + v += *(const HBINT16 *) p; + p += HBINT16::static_size; + } + } + points_.arrayZ[i].*m = v; + } + return true; + } + + bool get_contour_points (contour_point_vector_t &points_ /* OUT */, + bool phantom_only = false) const + { + const HBUINT16 *endPtsOfContours = &StructAfter (header); + int num_contours = header.numberOfContours; + assert (num_contours); + /* One extra item at the end, for the instruction-count below. */ + if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours]))) return false; + unsigned int num_points = endPtsOfContours[num_contours - 1] + 1; + + points_.alloc (num_points + 4, true); // Allocate for phantom points, to avoid a possible copy + if (!points_.resize (num_points)) return false; + if (phantom_only) return true; + + for (int i = 0; i < num_contours; i++) + points_[endPtsOfContours[i]].is_end_point = true; + + /* Skip instructions */ + const HBUINT8 *p = &StructAtOffset (&endPtsOfContours[num_contours + 1], + endPtsOfContours[num_contours]); + + if (unlikely ((const char *) p < bytes.arrayZ)) return false; /* Unlikely overflow */ + const HBUINT8 *end = (const HBUINT8 *) (bytes.arrayZ + bytes.length); + if (unlikely (p >= end)) return false; + + /* Read x & y coordinates */ + return read_flags (p, points_, end) + && read_points (p, points_, end, &contour_point_t::x, + FLAG_X_SHORT, FLAG_X_SAME) + && read_points (p, points_, end, &contour_point_t::y, + FLAG_Y_SHORT, FLAG_Y_SAME); + } + + static void encode_coord (int value, + uint8_t &flag, + const simple_glyph_flag_t short_flag, + const simple_glyph_flag_t same_flag, + hb_vector_t &coords /* OUT */) + { + if (value == 0) + { + flag |= same_flag; + } + else if (value >= -255 && value <= 255) + { + flag |= short_flag; + if (value > 0) flag |= same_flag; + else value = -value; + + coords.arrayZ[coords.length++] = (uint8_t) value; + } + else + { + int16_t val = value; + coords.arrayZ[coords.length++] = val >> 8; + coords.arrayZ[coords.length++] = val & 0xff; + } + } + + static void encode_flag (uint8_t &flag, + uint8_t &repeat, + uint8_t lastflag, + hb_vector_t &flags /* OUT */) + { + if (flag == lastflag && repeat != 255) + { + repeat++; + if (repeat == 1) + { + /* We know there's room. */ + flags.arrayZ[flags.length++] = flag; + } + else + { + unsigned len = flags.length; + flags.arrayZ[len-2] = flag | FLAG_REPEAT; + flags.arrayZ[len-1] = repeat; + } + } + else + { + repeat = 0; + flags.push (flag); + } + } + + bool compile_bytes_with_deltas (const contour_point_vector_t &all_points, + bool no_hinting, + hb_bytes_t &dest_bytes /* OUT */) + { + if (header.numberOfContours == 0 || all_points.length <= 4) + { + dest_bytes = hb_bytes_t (); + return true; + } + unsigned num_points = all_points.length - 4; + + hb_vector_t flags, x_coords, y_coords; + if (unlikely (!flags.alloc (num_points, true))) return false; + if (unlikely (!x_coords.alloc (2*num_points, true))) return false; + if (unlikely (!y_coords.alloc (2*num_points, true))) return false; + + uint8_t lastflag = 255, repeat = 0; + int prev_x = 0, prev_y = 0; + + for (unsigned i = 0; i < num_points; i++) + { + uint8_t flag = all_points.arrayZ[i].flag; + flag &= FLAG_ON_CURVE + FLAG_OVERLAP_SIMPLE; + + int cur_x = roundf (all_points.arrayZ[i].x); + int cur_y = roundf (all_points.arrayZ[i].y); + encode_coord (cur_x - prev_x, flag, FLAG_X_SHORT, FLAG_X_SAME, x_coords); + encode_coord (cur_y - prev_y, flag, FLAG_Y_SHORT, FLAG_Y_SAME, y_coords); + encode_flag (flag, repeat, lastflag, flags); + + prev_x = cur_x; + prev_y = cur_y; + lastflag = flag; + } + + unsigned len_before_instrs = 2 * header.numberOfContours + 2; + unsigned len_instrs = instructions_length (); + unsigned total_len = len_before_instrs + flags.length + x_coords.length + y_coords.length; + + if (!no_hinting) + total_len += len_instrs; + + char *p = (char *) hb_malloc (total_len); + if (unlikely (!p)) return false; + + const char *src = bytes.arrayZ + GlyphHeader::static_size; + char *cur = p; + hb_memcpy (p, src, len_before_instrs); + + cur += len_before_instrs; + src += len_before_instrs; + + if (!no_hinting) + { + hb_memcpy (cur, src, len_instrs); + cur += len_instrs; + } + + hb_memcpy (cur, flags.arrayZ, flags.length); + cur += flags.length; + + hb_memcpy (cur, x_coords.arrayZ, x_coords.length); + cur += x_coords.length; + + hb_memcpy (cur, y_coords.arrayZ, y_coords.length); + + dest_bytes = hb_bytes_t (p, total_len); + return true; + } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_SIMPLEGLYPH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/SubsetGlyph.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/SubsetGlyph.hh new file mode 100644 index 00000000000..26dc374eab6 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/SubsetGlyph.hh @@ -0,0 +1,152 @@ +#ifndef OT_GLYF_SUBSETGLYPH_HH +#define OT_GLYF_SUBSETGLYPH_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { + +struct glyf_accelerator_t; + +namespace glyf_impl { + + +struct SubsetGlyph +{ + hb_codepoint_t old_gid; + Glyph source_glyph; + hb_bytes_t dest_start; /* region of source_glyph to copy first */ + hb_bytes_t dest_end; /* region of source_glyph to copy second */ + bool allocated; + + bool serialize (hb_serialize_context_t *c, + bool use_short_loca, + const hb_subset_plan_t *plan) + { + TRACE_SERIALIZE (this); + + hb_bytes_t dest_glyph = dest_start.copy (c); + hb_bytes_t end_copy = dest_end.copy (c); + if (!end_copy.arrayZ || !dest_glyph.arrayZ) { + return false; + } + + dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + end_copy.length); + unsigned int pad_length = use_short_loca ? padding () : 0; + DEBUG_MSG (SUBSET, nullptr, "serialize %u byte glyph, width %u pad %u", dest_glyph.length, dest_glyph.length + pad_length, pad_length); + + HBUINT8 pad; + pad = 0; + while (pad_length > 0) + { + c->embed (pad); + pad_length--; + } + + if (unlikely (!dest_glyph.length)) return_trace (true); + + /* update components gids. */ + for (auto &_ : Glyph (dest_glyph).get_composite_iterator ()) + { + hb_codepoint_t new_gid; + if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid)) + const_cast (_).set_gid (new_gid); + } +#ifndef HB_NO_VAR_COMPOSITES + for (auto &_ : Glyph (dest_glyph).get_var_composite_iterator ()) + { + hb_codepoint_t new_gid; + if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid)) + const_cast (_).set_gid (new_gid); + } +#endif + +#ifndef HB_NO_BEYOND_64K + auto it = Glyph (dest_glyph).get_composite_iterator (); + if (it) + { + /* lower GID24 to GID16 in components if possible. + * + * TODO: VarComposite. Not as critical, since VarComposite supports + * gid24 from the first version. */ + char *p = it ? (char *) &*it : nullptr; + char *q = p; + const char *end = dest_glyph.arrayZ + dest_glyph.length; + while (it) + { + auto &rec = const_cast (*it); + ++it; + + q += rec.get_size (); + + rec.lower_gid_24_to_16 (); + + unsigned size = rec.get_size (); + + memmove (p, &rec, size); + + p += size; + } + memmove (p, q, end - q); + p += end - q; + + /* We want to shorten the glyph, but we can't do that without + * updating the length in the loca table, which is already + * written out :-(. So we just fill the rest of the glyph with + * harmless instructions, since that's what they will be + * interpreted as. + * + * Should move the lowering to _populate_subset_glyphs() to + * fix this issue. */ + + hb_memset (p, 0x7A /* TrueType instruction ROFF; harmless */, end - p); + p += end - p; + dest_glyph = hb_bytes_t (dest_glyph.arrayZ, p - (char *) dest_glyph.arrayZ); + + // TODO: Padding; & trim serialized bytes. + // TODO: Update length in loca. Ugh. + } +#endif + + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + Glyph (dest_glyph).drop_hints (); + + if (plan->flags & HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG) + Glyph (dest_glyph).set_overlaps_flag (); + + return_trace (true); + } + + bool compile_bytes_with_deltas (const hb_subset_plan_t *plan, + hb_font_t *font, + const glyf_accelerator_t &glyf) + { + allocated = source_glyph.compile_bytes_with_deltas (plan, font, glyf, dest_start, dest_end); + return allocated; + } + + void free_compiled_bytes () + { + if (likely (allocated)) { + allocated = false; + dest_start.fini (); + dest_end.fini (); + } + } + + void drop_hints_bytes () + { source_glyph.drop_hints_bytes (dest_start, dest_end); } + + unsigned int length () const { return dest_start.length + dest_end.length; } + /* pad to 2 to ensure 2-byte loca will be ok */ + unsigned int padding () const { return length () % 2; } + unsigned int padded_size () const { return length () + padding (); } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_SUBSETGLYPH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/VarCompositeGlyph.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/VarCompositeGlyph.hh new file mode 100644 index 00000000000..309ec473aa2 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/VarCompositeGlyph.hh @@ -0,0 +1,371 @@ +#ifndef OT_GLYF_VARCOMPOSITEGLYPH_HH +#define OT_GLYF_VARCOMPOSITEGLYPH_HH + + +#include "../../hb-open-type.hh" +#include "coord-setter.hh" + + +namespace OT { +namespace glyf_impl { + + +struct VarCompositeGlyphRecord +{ + protected: + enum var_composite_glyph_flag_t + { + USE_MY_METRICS = 0x0001, + AXIS_INDICES_ARE_SHORT = 0x0002, + UNIFORM_SCALE = 0x0004, + HAVE_TRANSLATE_X = 0x0008, + HAVE_TRANSLATE_Y = 0x0010, + HAVE_ROTATION = 0x0020, + HAVE_SCALE_X = 0x0040, + HAVE_SCALE_Y = 0x0080, + HAVE_SKEW_X = 0x0100, + HAVE_SKEW_Y = 0x0200, + HAVE_TCENTER_X = 0x0400, + HAVE_TCENTER_Y = 0x0800, + GID_IS_24BIT = 0x1000, + AXES_HAVE_VARIATION = 0x2000, + RESET_UNSPECIFIED_AXES = 0x4000, + }; + + public: + + unsigned int get_size () const + { + unsigned int size = min_size; + + unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 4 : 3; + size += numAxes * axis_width; + + // gid + size += 2; + if (flags & GID_IS_24BIT) size += 1; + + if (flags & HAVE_TRANSLATE_X) size += 2; + if (flags & HAVE_TRANSLATE_Y) size += 2; + if (flags & HAVE_ROTATION) size += 2; + if (flags & HAVE_SCALE_X) size += 2; + if (flags & HAVE_SCALE_Y) size += 2; + if (flags & HAVE_SKEW_X) size += 2; + if (flags & HAVE_SKEW_Y) size += 2; + if (flags & HAVE_TCENTER_X) size += 2; + if (flags & HAVE_TCENTER_Y) size += 2; + + return size; + } + + bool has_more () const { return true; } + + bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } + bool is_reset_unspecified_axes () const { return flags & RESET_UNSPECIFIED_AXES; } + + hb_codepoint_t get_gid () const + { + if (flags & GID_IS_24BIT) + return StructAfter (numAxes); + else + return StructAfter (numAxes); + } + + void set_gid (hb_codepoint_t gid) + { + if (flags & GID_IS_24BIT) + StructAfter (numAxes) = gid; + else + StructAfter (numAxes) = gid; + } + + unsigned get_numAxes () const + { + return numAxes; + } + + unsigned get_num_points () const + { + unsigned num = 0; + if (flags & AXES_HAVE_VARIATION) num += numAxes; + if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) num++; + if (flags & HAVE_ROTATION) num++; + if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y)) num++; + if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y)) num++; + if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) num++; + return num; + } + + void transform_points (hb_array_t record_points, + contour_point_vector_t &points) const + { + float matrix[4]; + contour_point_t trans; + + get_transformation_from_points (record_points, matrix, trans); + + points.transform (matrix); + points.translate (trans); + } + + static inline void transform (float (&matrix)[4], contour_point_t &trans, + float (other)[6]) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L268 + float xx1 = other[0]; + float xy1 = other[1]; + float yx1 = other[2]; + float yy1 = other[3]; + float dx1 = other[4]; + float dy1 = other[5]; + float xx2 = matrix[0]; + float xy2 = matrix[1]; + float yx2 = matrix[2]; + float yy2 = matrix[3]; + float dx2 = trans.x; + float dy2 = trans.y; + + matrix[0] = xx1*xx2 + xy1*yx2; + matrix[1] = xx1*xy2 + xy1*yy2; + matrix[2] = yx1*xx2 + yy1*yx2; + matrix[3] = yx1*xy2 + yy1*yy2; + trans.x = xx2*dx1 + yx2*dy1 + dx2; + trans.y = xy2*dx1 + yy2*dy1 + dy2; + } + + static void translate (float (&matrix)[4], contour_point_t &trans, + float translateX, float translateY) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L213 + float other[6] = {1.f, 0.f, 0.f, 1.f, translateX, translateY}; + transform (matrix, trans, other); + } + + static void scale (float (&matrix)[4], contour_point_t &trans, + float scaleX, float scaleY) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L224 + float other[6] = {scaleX, 0.f, 0.f, scaleY, 0.f, 0.f}; + transform (matrix, trans, other); + } + + static void rotate (float (&matrix)[4], contour_point_t &trans, + float rotation) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240 + rotation = rotation * HB_PI; + float c = cosf (rotation); + float s = sinf (rotation); + float other[6] = {c, s, -s, c, 0.f, 0.f}; + transform (matrix, trans, other); + } + + static void skew (float (&matrix)[4], contour_point_t &trans, + float skewX, float skewY) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255 + skewX = skewX * HB_PI; + skewY = skewY * HB_PI; + float other[6] = {1.f, tanf (skewY), tanf (skewX), 1.f, 0.f, 0.f}; + transform (matrix, trans, other); + } + + bool get_points (contour_point_vector_t &points) const + { + float translateX = 0.f; + float translateY = 0.f; + float rotation = 0.f; + float scaleX = 1.f * (1 << 10); + float scaleY = 1.f * (1 << 10); + float skewX = 0.f; + float skewY = 0.f; + float tCenterX = 0.f; + float tCenterY = 0.f; + + unsigned num_points = get_num_points (); + + if (unlikely (!points.resize (points.length + num_points))) return false; + + unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 2 : 1; + unsigned axes_size = numAxes * axis_width; + + const F2DOT14 *q = (const F2DOT14 *) (axes_size + + (flags & GID_IS_24BIT ? 3 : 2) + + &StructAfter (numAxes)); + + hb_array_t rec_points = points.as_array ().sub_array (points.length - num_points); + + unsigned count = numAxes; + if (flags & AXES_HAVE_VARIATION) + { + for (unsigned i = 0; i < count; i++) + rec_points[i].x = q++->to_int (); + rec_points += count; + } + else + q += count; + + const HBUINT16 *p = (const HBUINT16 *) q; + + if (flags & HAVE_TRANSLATE_X) translateX = * (const FWORD *) p++; + if (flags & HAVE_TRANSLATE_Y) translateY = * (const FWORD *) p++; + if (flags & HAVE_ROTATION) rotation = ((const F4DOT12 *) p++)->to_int (); + if (flags & HAVE_SCALE_X) scaleX = ((const F6DOT10 *) p++)->to_int (); + if (flags & HAVE_SCALE_Y) scaleY = ((const F6DOT10 *) p++)->to_int (); + if (flags & HAVE_SKEW_X) skewX = ((const F4DOT12 *) p++)->to_int (); + if (flags & HAVE_SKEW_Y) skewY = ((const F4DOT12 *) p++)->to_int (); + if (flags & HAVE_TCENTER_X) tCenterX = * (const FWORD *) p++; + if (flags & HAVE_TCENTER_Y) tCenterY = * (const FWORD *) p++; + + if ((flags & UNIFORM_SCALE) && !(flags & HAVE_SCALE_Y)) + scaleY = scaleX; + + if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) + { + rec_points[0].x = translateX; + rec_points[0].y = translateY; + rec_points++; + } + if (flags & HAVE_ROTATION) + { + rec_points[0].x = rotation; + rec_points++; + } + if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y)) + { + rec_points[0].x = scaleX; + rec_points[0].y = scaleY; + rec_points++; + } + if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y)) + { + rec_points[0].x = skewX; + rec_points[0].y = skewY; + rec_points++; + } + if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) + { + rec_points[0].x = tCenterX; + rec_points[0].y = tCenterY; + rec_points++; + } + assert (!rec_points); + + return true; + } + + void get_transformation_from_points (hb_array_t rec_points, + float (&matrix)[4], contour_point_t &trans) const + { + if (flags & AXES_HAVE_VARIATION) + rec_points += numAxes; + + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; + trans.init (0.f, 0.f); + + float translateX = 0.f; + float translateY = 0.f; + float rotation = 0.f; + float scaleX = 1.f; + float scaleY = 1.f; + float skewX = 0.f; + float skewY = 0.f; + float tCenterX = 0.f; + float tCenterY = 0.f; + + if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) + { + translateX = rec_points[0].x; + translateY = rec_points[0].y; + rec_points++; + } + if (flags & HAVE_ROTATION) + { + rotation = rec_points[0].x / (1 << 12); + rec_points++; + } + if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y)) + { + scaleX = rec_points[0].x / (1 << 10); + scaleY = rec_points[0].y / (1 << 10); + rec_points++; + } + if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y)) + { + skewX = rec_points[0].x / (1 << 12); + skewY = rec_points[0].y / (1 << 12); + rec_points++; + } + if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) + { + tCenterX = rec_points[0].x; + tCenterY = rec_points[0].y; + rec_points++; + } + assert (!rec_points); + + translate (matrix, trans, translateX + tCenterX, translateY + tCenterY); + rotate (matrix, trans, rotation); + scale (matrix, trans, scaleX, scaleY); + skew (matrix, trans, -skewX, skewY); + translate (matrix, trans, -tCenterX, -tCenterY); + } + + void set_variations (coord_setter_t &setter, + hb_array_t rec_points) const + { + bool have_variations = flags & AXES_HAVE_VARIATION; + unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 2 : 1; + + const HBUINT8 *p = (const HBUINT8 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24BIT ? 3 : 2)); + const HBUINT16 *q = (const HBUINT16 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24BIT ? 3 : 2)); + + const F2DOT14 *a = (const F2DOT14 *) ((HBUINT8 *) (axis_width == 1 ? (p + numAxes) : (HBUINT8 *) (q + numAxes))); + + unsigned count = numAxes; + for (unsigned i = 0; i < count; i++) + { + unsigned axis_index = axis_width == 1 ? (unsigned) *p++ : (unsigned) *q++; + + signed v = have_variations ? rec_points[i].x : a++->to_int (); + + v = hb_clamp (v, -(1<<14), (1<<14)); + setter[axis_index] = v; + } + } + + protected: + HBUINT16 flags; + HBUINT8 numAxes; + public: + DEFINE_SIZE_MIN (3); +}; + +using var_composite_iter_t = composite_iter_tmpl; + +struct VarCompositeGlyph +{ + const GlyphHeader &header; + hb_bytes_t bytes; + VarCompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + var_composite_iter_t iter () const + { return var_composite_iter_t (bytes, &StructAfter (header)); } + + const hb_bytes_t trim_padding () const + { + unsigned length = GlyphHeader::static_size; + for (auto &comp : iter ()) + length += comp.get_size (); + return bytes.sub_array (0, length); + } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_VARCOMPOSITEGLYPH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/composite-iter.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/composite-iter.hh new file mode 100644 index 00000000000..d05701f3d1e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/composite-iter.hh @@ -0,0 +1,68 @@ +#ifndef OT_GLYF_COMPOSITE_ITER_HH +#define OT_GLYF_COMPOSITE_ITER_HH + + +#include "../../hb.hh" + + +namespace OT { +namespace glyf_impl { + + +template +struct composite_iter_tmpl : hb_iter_with_fallback_t, + const CompositeGlyphRecord &> +{ + typedef const CompositeGlyphRecord *__item_t__; + composite_iter_tmpl (hb_bytes_t glyph_, __item_t__ current_) : + glyph (glyph_), current (nullptr), current_size (0) + { + set_current (current_); + } + + composite_iter_tmpl () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {} + + const CompositeGlyphRecord & __item__ () const { return *current; } + bool __more__ () const { return current; } + void __next__ () + { + if (!current->has_more ()) { current = nullptr; return; } + + set_current (&StructAtOffset (current, current_size)); + } + composite_iter_tmpl __end__ () const { return composite_iter_tmpl (); } + bool operator != (const composite_iter_tmpl& o) const + { return current != o.current; } + + + void set_current (__item_t__ current_) + { + if (!glyph.check_range (current_, CompositeGlyphRecord::min_size)) + { + current = nullptr; + current_size = 0; + return; + } + unsigned size = current_->get_size (); + if (!glyph.check_range (current_, size)) + { + current = nullptr; + current_size = 0; + return; + } + + current = current_; + current_size = size; + } + + private: + hb_bytes_t glyph; + __item_t__ current; + unsigned current_size; +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + +#endif /* OT_GLYF_COMPOSITE_ITER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/coord-setter.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/coord-setter.hh new file mode 100644 index 00000000000..df64ed5af7d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/coord-setter.hh @@ -0,0 +1,34 @@ +#ifndef OT_GLYF_COORD_SETTER_HH +#define OT_GLYF_COORD_SETTER_HH + + +#include "../../hb.hh" + + +namespace OT { +namespace glyf_impl { + + +struct coord_setter_t +{ + coord_setter_t (hb_array_t coords) : + coords (coords) {} + + int& operator [] (unsigned idx) + { + if (coords.length < idx + 1) + coords.resize (idx + 1); + return coords[idx]; + } + + hb_array_t get_coords () + { return coords.as_array (); } + + hb_vector_t coords; +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + +#endif /* OT_GLYF_COORD_SETTER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf-helpers.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf-helpers.hh new file mode 100644 index 00000000000..30106b2b98b --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf-helpers.hh @@ -0,0 +1,104 @@ +#ifndef OT_GLYF_GLYF_HELPERS_HH +#define OT_GLYF_GLYF_HELPERS_HH + + +#include "../../hb-open-type.hh" +#include "../../hb-subset-plan.hh" + +#include "loca.hh" + + +namespace OT { +namespace glyf_impl { + + +template +static void +_write_loca (IteratorIn&& it, bool short_offsets, IteratorOut&& dest) +{ + unsigned right_shift = short_offsets ? 1 : 0; + unsigned int offset = 0; + dest << 0; + + it + | hb_map ([=, &offset] (unsigned int padded_size) + { + offset += padded_size; + DEBUG_MSG (SUBSET, nullptr, "loca entry offset %u", offset); + return offset >> right_shift; + }) + | hb_sink (dest) + ; +} + +static bool +_add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca) +{ + hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table (plan->source); + hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob); + hb_blob_destroy (head_blob); + + if (unlikely (!head_prime_blob)) + return false; + + head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr); + head_prime->indexToLocFormat = use_short_loca ? 0 : 1; + if (plan->normalized_coords) + { + head_prime->xMin = plan->head_maxp_info.xMin; + head_prime->xMax = plan->head_maxp_info.xMax; + head_prime->yMin = plan->head_maxp_info.yMin; + head_prime->yMax = plan->head_maxp_info.yMax; + + unsigned orig_flag = head_prime->flags; + if (plan->head_maxp_info.allXMinIsLsb) + orig_flag |= 1 << 1; + else + orig_flag &= ~(1 << 1); + head_prime->flags = orig_flag; + } + bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob); + + hb_blob_destroy (head_prime_blob); + return success; +} + +template +static bool +_add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_short_loca) +{ + unsigned num_offsets = padded_offsets.len () + 1; + unsigned entry_size = use_short_loca ? 2 : 4; + char *loca_prime_data = (char *) hb_calloc (entry_size, num_offsets); + + if (unlikely (!loca_prime_data)) return false; + + DEBUG_MSG (SUBSET, nullptr, "loca entry_size %u num_offsets %u size %u", + entry_size, num_offsets, entry_size * num_offsets); + + if (use_short_loca) + _write_loca (padded_offsets, true, hb_array ((HBUINT16 *) loca_prime_data, num_offsets)); + else + _write_loca (padded_offsets, false, hb_array ((HBUINT32 *) loca_prime_data, num_offsets)); + + hb_blob_t *loca_blob = hb_blob_create (loca_prime_data, + entry_size * num_offsets, + HB_MEMORY_MODE_WRITABLE, + loca_prime_data, + hb_free); + + bool result = plan->add_table (HB_OT_TAG_loca, loca_blob) + && _add_head_and_set_loca_version (plan, use_short_loca); + + hb_blob_destroy (loca_blob); + return result; +} + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYF_HELPERS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf.hh new file mode 100644 index 00000000000..dd08dda6ee1 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf.hh @@ -0,0 +1,504 @@ +#ifndef OT_GLYF_GLYF_HH +#define OT_GLYF_GLYF_HH + + +#include "../../hb-open-type.hh" +#include "../../hb-ot-head-table.hh" +#include "../../hb-ot-hmtx-table.hh" +#include "../../hb-ot-var-gvar-table.hh" +#include "../../hb-draw.hh" +#include "../../hb-paint.hh" + +#include "glyf-helpers.hh" +#include "Glyph.hh" +#include "SubsetGlyph.hh" +#include "loca.hh" +#include "path-builder.hh" + + +namespace OT { + + +/* + * glyf -- TrueType Glyph Data + * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf + */ +#define HB_OT_TAG_glyf HB_TAG('g','l','y','f') + +struct glyf +{ + friend struct glyf_accelerator_t; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf; + + static bool has_valid_glyf_format(const hb_face_t* face) + { + const OT::head &head = *face->table.head; + return head.indexToLocFormat <= 1 && head.glyphDataFormat <= 1; + } + + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + /* Runtime checks as eager sanitizing each glyph is costy */ + return_trace (true); + } + + /* requires source of SubsetGlyph complains the identifier isn't declared */ + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + bool use_short_loca, + const hb_subset_plan_t *plan) + { + TRACE_SERIALIZE (this); + + unsigned init_len = c->length (); + for (auto &_ : it) + if (unlikely (!_.serialize (c, use_short_loca, plan))) + return false; + + /* As a special case when all glyph in the font are empty, add a zero byte + * to the table, so that OTS doesnโ€™t reject it, and to make the table work + * on Windows as well. + * See https://github.com/khaledhosny/ots/issues/52 */ + if (init_len == c->length ()) + { + HBUINT8 empty_byte; + empty_byte = 0; + c->copy (empty_byte); + } + return_trace (true); + } + + /* Byte region(s) per glyph to output + unpadded, hints removed if so requested + If we fail to process a glyph we produce an empty (0-length) glyph */ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + if (!has_valid_glyf_format (c->plan->source)) { + // glyf format is unknown don't attempt to subset it. + DEBUG_MSG (SUBSET, nullptr, + "unkown glyf format, dropping from subset."); + return_trace (false); + } + + glyf *glyf_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false); + + hb_font_t *font = nullptr; + if (c->plan->normalized_coords) + { + font = _create_font_for_instancing (c->plan); + if (unlikely (!font)) return false; + } + + hb_vector_t padded_offsets; + unsigned num_glyphs = c->plan->num_output_glyphs (); + if (unlikely (!padded_offsets.resize (num_glyphs))) + { + hb_font_destroy (font); + return false; + } + + hb_vector_t glyphs; + if (!_populate_subset_glyphs (c->plan, font, glyphs)) + { + hb_font_destroy (font); + return false; + } + + if (font) + hb_font_destroy (font); + + unsigned max_offset = 0; + for (unsigned i = 0; i < num_glyphs; i++) + { + padded_offsets[i] = glyphs[i].padded_size (); + max_offset += padded_offsets[i]; + } + + bool use_short_loca = false; + if (likely (!c->plan->force_long_loca)) + use_short_loca = max_offset < 0x1FFFF; + + if (!use_short_loca) { + for (unsigned i = 0; i < num_glyphs; i++) + padded_offsets[i] = glyphs[i].length (); + } + + bool result = glyf_prime->serialize (c->serializer, glyphs.writer (), use_short_loca, c->plan); + if (c->plan->normalized_coords && !c->plan->pinned_at_default) + _free_compiled_subset_glyphs (glyphs); + + if (!result) return false; + + if (unlikely (c->serializer->in_error ())) return_trace (false); + + return_trace (c->serializer->check_success (glyf_impl::_add_loca_and_head (c->plan, + padded_offsets.iter (), + use_short_loca))); + } + + bool + _populate_subset_glyphs (const hb_subset_plan_t *plan, + hb_font_t *font, + hb_vector_t &glyphs /* OUT */) const; + + hb_font_t * + _create_font_for_instancing (const hb_subset_plan_t *plan) const; + + void _free_compiled_subset_glyphs (hb_vector_t &glyphs) const + { + for (unsigned i = 0; i < glyphs.length; i++) + glyphs[i].free_compiled_bytes (); + } + + protected: + UnsizedArrayOf + dataZ; /* Glyphs data. */ + public: + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ +}; + +struct glyf_accelerator_t +{ + glyf_accelerator_t (hb_face_t *face) + { + short_offset = false; + num_glyphs = 0; + loca_table = nullptr; + glyf_table = nullptr; +#ifndef HB_NO_VAR + gvar = nullptr; +#endif + hmtx = nullptr; +#ifndef HB_NO_VERTICAL + vmtx = nullptr; +#endif + const OT::head &head = *face->table.head; + if (!glyf::has_valid_glyf_format (face)) + /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ + return; + short_offset = 0 == head.indexToLocFormat; + + loca_table = face->table.loca.get_blob (); // Needs no destruct! + glyf_table = hb_sanitize_context_t ().reference_table (face); +#ifndef HB_NO_VAR + gvar = face->table.gvar; +#endif + hmtx = face->table.hmtx; +#ifndef HB_NO_VERTICAL + vmtx = face->table.vmtx; +#endif + + num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; + num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ()); + } + ~glyf_accelerator_t () + { + glyf_table.destroy (); + } + + bool has_data () const { return num_glyphs; } + + protected: + template + bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const + { + if (gid >= num_glyphs) return false; + + /* Making this allocfree is not that easy + https://github.com/harfbuzz/harfbuzz/issues/2095 + mostly because of gvar handling in VF fonts, + perhaps a separate path for non-VF fonts can be considered */ + contour_point_vector_t all_points; + + bool phantom_only = !consumer.is_consuming_contour_points (); + if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, nullptr, nullptr, true, true, phantom_only))) + return false; + + if (consumer.is_consuming_contour_points ()) + { + unsigned count = all_points.length; + assert (count >= glyf_impl::PHANTOM_COUNT); + count -= glyf_impl::PHANTOM_COUNT; + for (unsigned point_index = 0; point_index < count; point_index++) + consumer.consume_point (all_points[point_index]); + consumer.points_end (); + } + + /* Where to write phantoms, nullptr if not requested */ + contour_point_t *phantoms = consumer.get_phantoms_sink (); + if (phantoms) + for (unsigned i = 0; i < glyf_impl::PHANTOM_COUNT; ++i) + phantoms[i] = all_points[all_points.length - glyf_impl::PHANTOM_COUNT + i]; + + return true; + } + + public: + +#ifndef HB_NO_VAR + struct points_aggregator_t + { + hb_font_t *font; + hb_glyph_extents_t *extents; + contour_point_t *phantoms; + bool scaled; + + struct contour_bounds_t + { + contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; } + + void add (const contour_point_t &p) + { + min_x = hb_min (min_x, p.x); + min_y = hb_min (min_y, p.y); + max_x = hb_max (max_x, p.x); + max_y = hb_max (max_y, p.y); + } + + bool empty () const { return (min_x >= max_x) || (min_y >= max_y); } + + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scaled) + { + if (unlikely (empty ())) + { + extents->width = 0; + extents->x_bearing = 0; + extents->height = 0; + extents->y_bearing = 0; + return; + } + { + extents->x_bearing = roundf (min_x); + extents->width = roundf (max_x - extents->x_bearing); + extents->y_bearing = roundf (max_y); + extents->height = roundf (min_y - extents->y_bearing); + + if (scaled) + font->scale_glyph_extents (extents); + } + } + + protected: + float min_x, min_y, max_x, max_y; + } bounds; + + points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_, bool scaled_) + { + font = font_; + extents = extents_; + phantoms = phantoms_; + scaled = scaled_; + if (extents) bounds = contour_bounds_t (); + } + + void consume_point (const contour_point_t &point) { bounds.add (point); } + void points_end () { bounds.get_extents (font, extents, scaled); } + + bool is_consuming_contour_points () { return extents; } + contour_point_t *get_phantoms_sink () { return phantoms; } + }; + + unsigned + get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + { + if (unlikely (gid >= num_glyphs)) return 0; + + bool success = false; + + contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; + if (font->num_coords) + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false)); + + if (unlikely (!success)) + return +#ifndef HB_NO_VERTICAL + is_vertical ? vmtx->get_advance_without_var_unscaled (gid) : +#endif + hmtx->get_advance_without_var_unscaled (gid); + + float result = is_vertical + ? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y + : phantoms[glyf_impl::PHANTOM_RIGHT].x - phantoms[glyf_impl::PHANTOM_LEFT].x; + return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2); + } + + bool get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical, int *lsb) const + { + if (unlikely (gid >= num_glyphs)) return false; + + hb_glyph_extents_t extents; + + contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; + if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false)))) + return false; + + *lsb = is_vertical + ? roundf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing + : roundf (phantoms[glyf_impl::PHANTOM_LEFT].x); + return true; + } +#endif + + bool get_leading_bearing_without_var_unscaled (hb_codepoint_t gid, bool is_vertical, int *lsb) const + { + if (unlikely (gid >= num_glyphs)) return false; + if (is_vertical) return false; // TODO Humm, what to do here? + + *lsb = glyph_for_gid (gid).get_header ()->xMin; + return true; + } + + public: + bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + if (unlikely (gid >= num_glyphs)) return false; + +#ifndef HB_NO_VAR + if (font->num_coords) + return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true)); +#endif + return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents); + } + + bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const + { + funcs->push_clip_glyph (data, gid, font); + funcs->color (data, true, foreground); + funcs->pop_clip (data); + + return true; + } + + const glyf_impl::Glyph + glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const + { + if (unlikely (gid >= num_glyphs)) return glyf_impl::Glyph (); + + unsigned int start_offset, end_offset; + + if (short_offset) + { + const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ; + start_offset = 2 * offsets[gid]; + end_offset = 2 * offsets[gid + 1]; + } + else + { + const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ; + start_offset = offsets[gid]; + end_offset = offsets[gid + 1]; + } + + if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ())) + return glyf_impl::Glyph (); + + glyf_impl::Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset, + end_offset - start_offset), gid); + return needs_padding_removal ? glyf_impl::Glyph (glyph.trim_padding (), gid) : glyph; + } + + bool + get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const + { return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session)); } + +#ifndef HB_NO_VAR + const gvar_accelerator_t *gvar; +#endif + const hmtx_accelerator_t *hmtx; +#ifndef HB_NO_VERTICAL + const vmtx_accelerator_t *vmtx; +#endif + + private: + bool short_offset; + unsigned int num_glyphs; + hb_blob_ptr_t loca_table; + hb_blob_ptr_t glyf_table; +}; + + +inline bool +glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan, + hb_font_t *font, + hb_vector_t& glyphs /* OUT */) const +{ + OT::glyf_accelerator_t glyf (plan->source); + unsigned num_glyphs = plan->num_output_glyphs (); + if (!glyphs.resize (num_glyphs)) return false; + + for (auto p : plan->glyph_map->iter ()) + { + unsigned new_gid = p.second; + glyf_impl::SubsetGlyph& subset_glyph = glyphs.arrayZ[new_gid]; + subset_glyph.old_gid = p.first; + + if (unlikely (new_gid == 0 && + !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) && + !plan->normalized_coords) + subset_glyph.source_glyph = glyf_impl::Glyph (); + else + { + /* If plan has an accelerator, the preprocessing step already trimmed glyphs. + * Don't trim them again! */ + subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, !plan->accelerator); + } + + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + subset_glyph.drop_hints_bytes (); + else + subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes (); + + if (font) + { + if (unlikely (!subset_glyph.compile_bytes_with_deltas (plan, font, glyf))) + { + // when pinned at default, only bounds are updated, thus no need to free + if (!plan->pinned_at_default) + _free_compiled_subset_glyphs (glyphs); + return false; + } + } + } + return true; +} + +inline hb_font_t * +glyf::_create_font_for_instancing (const hb_subset_plan_t *plan) const +{ + hb_font_t *font = hb_font_create (plan->source); + if (unlikely (font == hb_font_get_empty ())) return nullptr; + + hb_vector_t vars; + if (unlikely (!vars.alloc (plan->user_axes_location.get_population (), true))) + { + hb_font_destroy (font); + return nullptr; + } + + for (auto _ : plan->user_axes_location) + { + hb_variation_t var; + var.tag = _.first; + var.value = _.second; + vars.push (var); + } + +#ifndef HB_NO_VAR + hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ()); +#endif + return font; +} + + +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYF_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/loca.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/loca.hh new file mode 100644 index 00000000000..4481cba8ed2 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/loca.hh @@ -0,0 +1,43 @@ +#ifndef OT_GLYF_LOCA_HH +#define OT_GLYF_LOCA_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { + + +/* + * loca -- Index to Location + * https://docs.microsoft.com/en-us/typography/opentype/spec/loca + */ +#define HB_OT_TAG_loca HB_TAG('l','o','c','a') + +struct loca +{ + friend struct glyf; + friend struct glyf_accelerator_t; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_loca; + + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + return_trace (true); + } + + protected: + UnsizedArrayOf + dataZ; /* Location data. */ + public: + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ +}; + + +} /* namespace OT */ + + +#endif /* OT_GLYF_LOCA_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/path-builder.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/path-builder.hh new file mode 100644 index 00000000000..8916241f76e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/path-builder.hh @@ -0,0 +1,193 @@ +#ifndef OT_GLYF_PATH_BUILDER_HH +#define OT_GLYF_PATH_BUILDER_HH + + +#include "../../hb.hh" + + +namespace OT { +namespace glyf_impl { + + +struct path_builder_t +{ + hb_font_t *font; + hb_draw_session_t *draw_session; + + struct optional_point_t + { + optional_point_t () {} + optional_point_t (float x_, float y_) : has_data (true), x (x_), y (y_) {} + operator bool () const { return has_data; } + + bool has_data = false; + float x = 0.; + float y = 0.; + + optional_point_t lerp (optional_point_t p, float t) + { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); } + } first_oncurve, first_offcurve, first_offcurve2, last_offcurve, last_offcurve2; + + path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_) + { + font = font_; + draw_session = &draw_session_; + first_oncurve = first_offcurve = first_offcurve2 = last_offcurve = last_offcurve2 = optional_point_t (); + } + + /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287 + See also: + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html + * https://stackoverflow.com/a/20772557 + * + * Cubic support added. */ + void consume_point (const contour_point_t &point) + { + bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE; +#ifdef HB_NO_CUBIC_GLYF + bool is_cubic = false; +#else + bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC); +#endif + optional_point_t p (font->em_fscalef_x (point.x), font->em_fscalef_y (point.y)); + if (!first_oncurve) + { + if (is_on_curve) + { + first_oncurve = p; + draw_session->move_to (p.x, p.y); + } + else + { + if (is_cubic && !first_offcurve2) + { + first_offcurve2 = first_offcurve; + first_offcurve = p; + } + else if (first_offcurve) + { + optional_point_t mid = first_offcurve.lerp (p, .5f); + first_oncurve = mid; + last_offcurve = p; + draw_session->move_to (mid.x, mid.y); + } + else + first_offcurve = p; + } + } + else + { + if (last_offcurve) + { + if (is_on_curve) + { + if (last_offcurve2) + { + draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y, + last_offcurve.x, last_offcurve.y, + p.x, p.y); + last_offcurve2 = optional_point_t (); + } + else + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + p.x, p.y); + last_offcurve = optional_point_t (); + } + else + { + if (is_cubic && !last_offcurve2) + { + last_offcurve2 = last_offcurve; + last_offcurve = p; + } + else + { + optional_point_t mid = last_offcurve.lerp (p, .5f); + + if (is_cubic) + { + draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y, + last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + last_offcurve2 = optional_point_t (); + } + else + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + last_offcurve = p; + } + } + } + else + { + if (is_on_curve) + draw_session->line_to (p.x, p.y); + else + last_offcurve = p; + } + } + + if (point.is_end_point) + { + if (first_offcurve && last_offcurve) + { + optional_point_t mid = last_offcurve.lerp (first_offcurve2 ? + first_offcurve2 : + first_offcurve, .5f); + if (last_offcurve2) + draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y, + last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + else + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + last_offcurve = optional_point_t (); + } + /* now check the rest */ + + if (first_offcurve && first_oncurve) + { + if (first_offcurve2) + draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y, + first_offcurve.x, first_offcurve.y, + first_oncurve.x, first_oncurve.y); + else + draw_session->quadratic_to (first_offcurve.x, first_offcurve.y, + first_oncurve.x, first_oncurve.y); + } + else if (last_offcurve && first_oncurve) + { + if (last_offcurve2) + draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y, + last_offcurve.x, last_offcurve.y, + first_oncurve.x, first_oncurve.y); + else + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + first_oncurve.x, first_oncurve.y); + } + else if (first_oncurve) + draw_session->line_to (first_oncurve.x, first_oncurve.y); + else if (first_offcurve) + { + float x = first_offcurve.x, y = first_offcurve.y; + draw_session->move_to (x, y); + draw_session->quadratic_to (x, y, x, y); + } + + /* Getting ready for the next contour */ + first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t (); + draw_session->close_path (); + } + } + void points_end () {} + + bool is_consuming_contour_points () { return true; } + contour_point_t *get_phantoms_sink () { return nullptr; } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_PATH_BUILDER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/name/name.hh b/src/3rdparty/harfbuzz-ng/src/OT/name/name.hh new file mode 100644 index 00000000000..c1839f3b683 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/name/name.hh @@ -0,0 +1,589 @@ +/* + * Copyright ยฉ 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef OT_NAME_NAME_HH +#define OT_NAME_NAME_HH + +#include "../../hb-open-type.hh" +#include "../../hb-ot-name-language.hh" +#include "../../hb-aat-layout.hh" +#include "../../hb-utf.hh" + + +namespace OT { + +template +inline unsigned int +hb_ot_name_convert_utf (hb_bytes_t bytes, + unsigned int *text_size /* IN/OUT */, + typename out_utf_t::codepoint_t *text /* OUT */) +{ + unsigned int src_len = bytes.length / sizeof (typename in_utf_t::codepoint_t); + const typename in_utf_t::codepoint_t *src = (const typename in_utf_t::codepoint_t *) bytes.arrayZ; + const typename in_utf_t::codepoint_t *src_end = src + src_len; + + typename out_utf_t::codepoint_t *dst = text; + + hb_codepoint_t unicode; + const hb_codepoint_t replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; + + if (text_size && *text_size) + { + (*text_size)--; /* Save room for NUL-termination. */ + const typename out_utf_t::codepoint_t *dst_end = text + *text_size; + + while (src < src_end && dst < dst_end) + { + const typename in_utf_t::codepoint_t *src_next = in_utf_t::next (src, src_end, &unicode, replacement); + typename out_utf_t::codepoint_t *dst_next = out_utf_t::encode (dst, dst_end, unicode); + if (dst_next == dst) + break; /* Out-of-room. */ + + dst = dst_next; + src = src_next; + } + + *text_size = dst - text; + *dst = 0; /* NUL-terminate. */ + } + + /* Accumulate length of rest. */ + unsigned int dst_len = dst - text; + while (src < src_end) + { + src = in_utf_t::next (src, src_end, &unicode, replacement); + dst_len += out_utf_t::encode_len (unicode); + } + return dst_len; +} + +#define entry_score var.u16[0] +#define entry_index var.u16[1] + + +/* + * name -- Naming + * https://docs.microsoft.com/en-us/typography/opentype/spec/name + */ +#define HB_OT_TAG_name HB_TAG('n','a','m','e') + +#define UNSUPPORTED 42 + +struct NameRecord +{ + hb_language_t language (hb_face_t *face) const + { +#ifndef HB_NO_OT_NAME_LANGUAGE + unsigned int p = platformID; + unsigned int l = languageID; + + if (p == 3) + return _hb_ot_name_language_for_ms_code (l); + + if (p == 1) + return _hb_ot_name_language_for_mac_code (l); + +#ifndef HB_NO_OT_NAME_LANGUAGE_AAT + if (p == 0) + return face->table.ltag->get_language (l); +#endif + +#endif + return HB_LANGUAGE_INVALID; + } + + uint16_t score () const + { + /* Same order as in cmap::find_best_subtable(). */ + unsigned int p = platformID; + unsigned int e = encodingID; + + /* 32-bit. */ + if (p == 3 && e == 10) return 0; + if (p == 0 && e == 6) return 1; + if (p == 0 && e == 4) return 2; + + /* 16-bit. */ + if (p == 3 && e == 1) return 3; + if (p == 0 && e == 3) return 4; + if (p == 0 && e == 2) return 5; + if (p == 0 && e == 1) return 6; + if (p == 0 && e == 0) return 7; + + /* Symbol. */ + if (p == 3 && e == 0) return 8; + + /* We treat all Mac Latin names as ASCII only. */ + if (p == 1 && e == 0) return 10; /* 10 is magic number :| */ + + return UNSUPPORTED; + } + + NameRecord* copy (hb_serialize_context_t *c, const void *base +#ifdef HB_EXPERIMENTAL_API + , const hb_hashmap_t *name_table_overrides +#endif + ) const + { + TRACE_SERIALIZE (this); + HB_UNUSED auto snap = c->snapshot (); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); +#ifdef HB_EXPERIMENTAL_API + hb_ot_name_record_ids_t record_ids (platformID, encodingID, languageID, nameID); + hb_bytes_t* name_bytes; + + if (name_table_overrides->has (record_ids, &name_bytes)) { + hb_bytes_t encoded_bytes = *name_bytes; + char *name_str_utf16_be = nullptr; + + if (platformID != 1) + { + unsigned text_size = hb_ot_name_convert_utf (*name_bytes, nullptr, nullptr); + + text_size++; // needs to consider NULL terminator for use in hb_ot_name_convert_utf() + unsigned byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size; + name_str_utf16_be = (char *) hb_calloc (byte_len, 1); + if (!name_str_utf16_be) + { + c->revert (snap); + return_trace (nullptr); + } + hb_ot_name_convert_utf (*name_bytes, &text_size, + (hb_utf16_be_t::codepoint_t *) name_str_utf16_be); + + unsigned encoded_byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size; + if (!encoded_byte_len || !c->check_assign (out->length, encoded_byte_len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) { + c->revert (snap); + hb_free (name_str_utf16_be); + return_trace (nullptr); + } + + encoded_bytes = hb_bytes_t (name_str_utf16_be, encoded_byte_len); + } + else + { + // mac platform, copy the UTF-8 string(all ascii characters) as is + if (!c->check_assign (out->length, encoded_bytes.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) { + c->revert (snap); + return_trace (nullptr); + } + } + + out->offset = 0; + c->push (); + encoded_bytes.copy (c); + c->add_link (out->offset, c->pop_pack (), hb_serialize_context_t::Tail, 0); + hb_free (name_str_utf16_be); + } + else +#endif + { + out->offset.serialize_copy (c, offset, base, 0, hb_serialize_context_t::Tail, length); + } + return_trace (out); + } + + bool isUnicode () const + { + unsigned int p = platformID; + unsigned int e = encodingID; + + return (p == 0 || + (p == 3 && (e == 0 || e == 1 || e == 10))); + } + + static int cmp (const void *pa, const void *pb) + { + const NameRecord *a = (const NameRecord *)pa; + const NameRecord *b = (const NameRecord *)pb; + + if (a->platformID != b->platformID) + return a->platformID - b->platformID; + + if (a->encodingID != b->encodingID) + return a->encodingID - b->encodingID; + + if (a->languageID != b->languageID) + return a->languageID - b->languageID; + + if (a->nameID != b->nameID) + return a->nameID - b->nameID; + + if (a->length != b->length) + return a->length - b->length; + + return 0; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && offset.sanitize (c, base, length)); + } + + HBUINT16 platformID; /* Platform ID. */ + HBUINT16 encodingID; /* Platform-specific encoding ID. */ + HBUINT16 languageID; /* Language ID. */ + HBUINT16 nameID; /* Name ID. */ + HBUINT16 length; /* String length (in bytes). */ + NNOffset16To> + offset; /* String offset from start of storage area (in bytes). */ + public: + DEFINE_SIZE_STATIC (12); +}; + +static int +_hb_ot_name_entry_cmp_key (const void *pa, const void *pb, bool exact) +{ + const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa; + const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb; + + /* Compare by name_id, then language. */ + + if (a->name_id != b->name_id) + return a->name_id - b->name_id; + + if (a->language == b->language) return 0; + if (!a->language) return -1; + if (!b->language) return +1; + + const char *astr = hb_language_to_string (a->language); + const char *bstr = hb_language_to_string (b->language); + + signed c = strcmp (astr, bstr); + + // 'a' is the user request, and 'b' is string in the font. + // If eg. user asks for "en-us" and font has "en", approve. + if (!exact && c && + hb_language_matches (b->language, a->language)) + return 0; + + return c; +} + +static int +_hb_ot_name_entry_cmp (const void *pa, const void *pb) +{ + /* Compare by name_id, then language, then score, then index. */ + + int v = _hb_ot_name_entry_cmp_key (pa, pb, true); + if (v) + return v; + + const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa; + const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb; + + if (a->entry_score != b->entry_score) + return a->entry_score - b->entry_score; + + if (a->entry_index != b->entry_index) + return a->entry_index - b->entry_index; + + return 0; +} + +struct name +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_name; + + unsigned int get_size () const + { return min_size + count * nameRecordZ.item_size; } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + const void *src_string_pool +#ifdef HB_EXPERIMENTAL_API + , const hb_vector_t& insert_name_records + , const hb_hashmap_t *name_table_overrides +#endif + ) + { + TRACE_SERIALIZE (this); + + if (unlikely (!c->extend_min ((*this)))) return_trace (false); + + unsigned total_count = it.len () +#ifdef HB_EXPERIMENTAL_API + + insert_name_records.length +#endif + ; + this->format = 0; + if (!c->check_assign (this->count, total_count, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return false; + + NameRecord *name_records = (NameRecord *) hb_calloc (total_count, NameRecord::static_size); + if (unlikely (!name_records)) return_trace (false); + + hb_array_t records (name_records, total_count); + + for (const NameRecord& record : it) + { + hb_memcpy (name_records, &record, NameRecord::static_size); + name_records++; + } + +#ifdef HB_EXPERIMENTAL_API + for (unsigned i = 0; i < insert_name_records.length; i++) + { + const hb_ot_name_record_ids_t& ids = insert_name_records[i]; + NameRecord record; + record.platformID = ids.platform_id; + record.encodingID = ids.encoding_id; + record.languageID = ids.language_id; + record.nameID = ids.name_id; + record.length = 0; // handled in NameRecord copy() + record.offset = 0; + memcpy (name_records, &record, NameRecord::static_size); + name_records++; + } +#endif + + records.qsort (); + + c->copy_all (records, + src_string_pool +#ifdef HB_EXPERIMENTAL_API + , name_table_overrides +#endif + ); + hb_free (records.arrayZ); + + + if (unlikely (c->ran_out_of_room ())) return_trace (false); + + this->stringOffset = c->length (); + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + name *name_prime = c->serializer->start_embed (); + if (unlikely (!name_prime)) return_trace (false); + +#ifdef HB_EXPERIMENTAL_API + const hb_hashmap_t *name_table_overrides = + &c->plan->name_table_overrides; +#endif + + auto it = + + nameRecordZ.as_array (count) + | hb_filter (c->plan->name_ids, &NameRecord::nameID) + | hb_filter (c->plan->name_languages, &NameRecord::languageID) + | hb_filter ([&] (const NameRecord& namerecord) { + return + (c->plan->flags & HB_SUBSET_FLAGS_NAME_LEGACY) + || namerecord.isUnicode (); + }) +#ifdef HB_EXPERIMENTAL_API + | hb_filter ([&] (const NameRecord& namerecord) { + if (name_table_overrides->is_empty ()) + return true; + hb_ot_name_record_ids_t rec_ids (namerecord.platformID, + namerecord.encodingID, + namerecord.languageID, + namerecord.nameID); + + hb_bytes_t *p; + if (name_table_overrides->has (rec_ids, &p) && + (*p).length == 0) + return false; + return true; + }) +#endif + ; + +#ifdef HB_EXPERIMENTAL_API + hb_hashmap_t retained_name_record_ids; + for (const NameRecord& rec : it) + { + hb_ot_name_record_ids_t rec_ids (rec.platformID, + rec.encodingID, + rec.languageID, + rec.nameID); + retained_name_record_ids.set (rec_ids, 1); + } + + hb_vector_t insert_name_records; + if (!name_table_overrides->is_empty ()) + { + if (unlikely (!insert_name_records.alloc (name_table_overrides->get_population (), true))) + return_trace (false); + for (const auto& record_ids : name_table_overrides->keys ()) + { + if (name_table_overrides->get (record_ids).length == 0) + continue; + if (retained_name_record_ids.has (record_ids)) + continue; + insert_name_records.push (record_ids); + } + } +#endif + + return (name_prime->serialize (c->serializer, it, + std::addressof (this + stringOffset) +#ifdef HB_EXPERIMENTAL_API + , insert_name_records + , name_table_overrides +#endif + )); + } + + bool sanitize_records (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + const void *string_pool = (this+stringOffset).arrayZ; + return_trace (nameRecordZ.sanitize (c, count, string_pool)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (format == 0 || format == 1) && + c->check_array (nameRecordZ.arrayZ, count) && + c->check_range (this, stringOffset) && + sanitize_records (c)); + } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + this->table = hb_sanitize_context_t ().reference_table (face); + assert (this->table.get_length () >= this->table->stringOffset); + this->pool = (const char *) (const void *) (this->table+this->table->stringOffset); + this->pool_len = this->table.get_length () - this->table->stringOffset; + const hb_array_t all_names (this->table->nameRecordZ.arrayZ, + this->table->count); + + this->names.alloc (all_names.length, true); + + for (unsigned int i = 0; i < all_names.length; i++) + { + hb_ot_name_entry_t *entry = this->names.push (); + + entry->name_id = all_names[i].nameID; + entry->language = all_names[i].language (face); + entry->entry_score = all_names[i].score (); + entry->entry_index = i; + } + + this->names.qsort (_hb_ot_name_entry_cmp); + /* Walk and pick best only for each name_id,language pair, + * while dropping unsupported encodings. */ + unsigned int j = 0; + for (unsigned int i = 0; i < this->names.length; i++) + { + if (this->names[i].entry_score == UNSUPPORTED || + this->names[i].language == HB_LANGUAGE_INVALID) + continue; + if (i && + this->names[i - 1].name_id == this->names[i].name_id && + this->names[i - 1].language == this->names[i].language) + continue; + this->names[j++] = this->names[i]; + } + this->names.resize (j); + } + ~accelerator_t () + { + this->table.destroy (); + } + + int get_index (hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *width=nullptr) const + { + const hb_ot_name_entry_t key = {name_id, {0}, language}; + const hb_ot_name_entry_t *entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names, + this->names.length, + sizeof (hb_ot_name_entry_t), + _hb_ot_name_entry_cmp_key, + true); + + if (!entry) + { + entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names, + this->names.length, + sizeof (hb_ot_name_entry_t), + _hb_ot_name_entry_cmp_key, + false); + } + + if (!entry) + return -1; + + if (width) + *width = entry->entry_score < 10 ? 2 : 1; + + return entry->entry_index; + } + + hb_bytes_t get_name (unsigned int idx) const + { + const hb_array_t all_names (table->nameRecordZ.arrayZ, table->count); + const NameRecord &record = all_names[idx]; + const hb_bytes_t string_pool (pool, pool_len); + return string_pool.sub_array (record.offset, record.length); + } + + private: + const char *pool; + unsigned int pool_len; + public: + hb_blob_ptr_t table; + hb_vector_t names; + }; + + public: + /* We only implement format 0 for now. */ + HBUINT16 format; /* Format selector (=0/1). */ + HBUINT16 count; /* Number of name records. */ + NNOffset16To> + stringOffset; /* Offset to start of string storage (from start of table). */ + UnsizedArrayOf + nameRecordZ; /* The name records where count is the number of records. */ + public: + DEFINE_SIZE_ARRAY (6, nameRecordZ); +}; + +#undef entry_index +#undef entry_score + +struct name_accelerator_t : name::accelerator_t { + name_accelerator_t (hb_face_t *face) : name::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* OT_NAME_NAME_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/graph/classdef-graph.hh b/src/3rdparty/harfbuzz-ng/src/graph/classdef-graph.hh new file mode 100644 index 00000000000..c2e24a70678 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/classdef-graph.hh @@ -0,0 +1,216 @@ +/* + * Copyright ยฉ 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#include "graph.hh" +#include "../hb-ot-layout-common.hh" + +#ifndef GRAPH_CLASSDEF_GRAPH_HH +#define GRAPH_CLASSDEF_GRAPH_HH + +namespace graph { + +struct ClassDefFormat1 : public OT::ClassDefFormat1_3 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::ClassDefFormat1_3::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + classValue.get_size () - classValue.len.get_size (); + } +}; + +struct ClassDefFormat2 : public OT::ClassDefFormat2_4 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::ClassDefFormat2_4::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size (); + } +}; + +struct ClassDef : public OT::ClassDef +{ + template + static bool add_class_def (gsubgpos_graph_context_t& c, + unsigned parent_id, + unsigned link_position, + It glyph_and_class, + unsigned max_size) + { + unsigned class_def_prime_id = c.graph.new_node (nullptr, nullptr); + auto& class_def_prime_vertex = c.graph.vertices_[class_def_prime_id]; + if (!make_class_def (c, glyph_and_class, class_def_prime_id, max_size)) + return false; + + auto* class_def_link = c.graph.vertices_[parent_id].obj.real_links.push (); + class_def_link->width = SmallTypes::size; + class_def_link->objidx = class_def_prime_id; + class_def_link->position = link_position; + class_def_prime_vertex.parents.push (parent_id); + + return true; + } + + template + static bool make_class_def (gsubgpos_graph_context_t& c, + It glyph_and_class, + unsigned dest_obj, + unsigned max_size) + { + char* buffer = (char*) hb_calloc (1, max_size); + hb_serialize_context_t serializer (buffer, max_size); + OT::ClassDef_serialize (&serializer, glyph_and_class); + serializer.end_serialize (); + if (serializer.in_error ()) + { + hb_free (buffer); + return false; + } + + hb_bytes_t class_def_copy = serializer.copy_bytes (); + c.add_buffer ((char *) class_def_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer. + + auto& obj = c.graph.vertices_[dest_obj].obj; + obj.head = (char *) class_def_copy.arrayZ; + obj.tail = obj.head + class_def_copy.length; + + hb_free (buffer); + return true; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::ClassDef::min_size) return false; + switch (u.format) + { + case 1: return ((ClassDefFormat1*)this)->sanitize (vertex); + case 2: return ((ClassDefFormat2*)this)->sanitize (vertex); +#ifndef HB_NO_BEYOND_64K + // Not currently supported + case 3: + case 4: +#endif + default: return false; + } + } +}; + + +struct class_def_size_estimator_t +{ + template + class_def_size_estimator_t (It glyph_and_class) + : gids_consecutive (true), num_ranges_per_class (), glyphs_per_class () + { + unsigned last_gid = (unsigned) -1; + for (auto p : + glyph_and_class) + { + unsigned gid = p.first; + unsigned klass = p.second; + + if (last_gid != (unsigned) -1 && gid != last_gid + 1) + gids_consecutive = false; + last_gid = gid; + + hb_set_t* glyphs; + if (glyphs_per_class.has (klass, &glyphs) && glyphs) { + glyphs->add (gid); + continue; + } + + hb_set_t new_glyphs; + new_glyphs.add (gid); + glyphs_per_class.set (klass, std::move (new_glyphs)); + } + + if (in_error ()) return; + + for (unsigned klass : glyphs_per_class.keys ()) + { + if (!klass) continue; // class 0 doesn't get encoded. + + const hb_set_t& glyphs = glyphs_per_class.get (klass); + hb_codepoint_t start = HB_SET_VALUE_INVALID; + hb_codepoint_t end = HB_SET_VALUE_INVALID; + + unsigned count = 0; + while (glyphs.next_range (&start, &end)) + count++; + + num_ranges_per_class.set (klass, count); + } + } + + // Incremental increase in the Coverage and ClassDef table size + // (worst case) if all glyphs associated with 'klass' were added. + unsigned incremental_coverage_size (unsigned klass) const + { + // Coverage takes 2 bytes per glyph worst case, + return 2 * glyphs_per_class.get (klass).get_population (); + } + + // Incremental increase in the Coverage and ClassDef table size + // (worst case) if all glyphs associated with 'klass' were added. + unsigned incremental_class_def_size (unsigned klass) const + { + // ClassDef takes 6 bytes per range + unsigned class_def_2_size = 6 * num_ranges_per_class.get (klass); + if (gids_consecutive) + { + // ClassDef1 takes 2 bytes per glyph, but only can be used + // when gids are consecutive. + return hb_min (2 * glyphs_per_class.get (klass).get_population (), class_def_2_size); + } + + return class_def_2_size; + } + + bool in_error () + { + if (num_ranges_per_class.in_error ()) return true; + if (glyphs_per_class.in_error ()) return true; + + for (const hb_set_t& s : glyphs_per_class.values ()) + { + if (s.in_error ()) return true; + } + return false; + } + + private: + bool gids_consecutive; + hb_hashmap_t num_ranges_per_class; + hb_hashmap_t glyphs_per_class; +}; + + +} + +#endif // GRAPH_CLASSDEF_GRAPH_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/coverage-graph.hh b/src/3rdparty/harfbuzz-ng/src/graph/coverage-graph.hh new file mode 100644 index 00000000000..49d09363156 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/coverage-graph.hh @@ -0,0 +1,152 @@ +/* + * Copyright ยฉ 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#include "graph.hh" +#include "../OT/Layout/Common/Coverage.hh" + +#ifndef GRAPH_COVERAGE_GRAPH_HH +#define GRAPH_COVERAGE_GRAPH_HH + +namespace graph { + +struct CoverageFormat1 : public OT::Layout::Common::CoverageFormat1_3 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::Layout::Common::CoverageFormat1_3::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + glyphArray.get_size () - glyphArray.len.get_size (); + } +}; + +struct CoverageFormat2 : public OT::Layout::Common::CoverageFormat2_4 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::Layout::Common::CoverageFormat2_4::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size (); + } +}; + +struct Coverage : public OT::Layout::Common::Coverage +{ + static Coverage* clone_coverage (gsubgpos_graph_context_t& c, + unsigned coverage_id, + unsigned new_parent_id, + unsigned link_position, + unsigned start, unsigned end) + + { + unsigned coverage_size = c.graph.vertices_[coverage_id].table_size (); + auto& coverage_v = c.graph.vertices_[coverage_id]; + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + if (!coverage_table || !coverage_table->sanitize (coverage_v)) + return nullptr; + + auto new_coverage = + + hb_zip (coverage_table->iter (), hb_range ()) + | hb_filter ([&] (hb_pair_t p) { + return p.second >= start && p.second < end; + }) + | hb_map_retains_sorting (hb_first) + ; + + return add_coverage (c, new_parent_id, link_position, new_coverage, coverage_size); + } + + template + static Coverage* add_coverage (gsubgpos_graph_context_t& c, + unsigned parent_id, + unsigned link_position, + It glyphs, + unsigned max_size) + { + unsigned coverage_prime_id = c.graph.new_node (nullptr, nullptr); + auto& coverage_prime_vertex = c.graph.vertices_[coverage_prime_id]; + if (!make_coverage (c, glyphs, coverage_prime_id, max_size)) + return nullptr; + + auto* coverage_link = c.graph.vertices_[parent_id].obj.real_links.push (); + coverage_link->width = SmallTypes::size; + coverage_link->objidx = coverage_prime_id; + coverage_link->position = link_position; + coverage_prime_vertex.parents.push (parent_id); + + return (Coverage*) coverage_prime_vertex.obj.head; + } + + template + static bool make_coverage (gsubgpos_graph_context_t& c, + It glyphs, + unsigned dest_obj, + unsigned max_size) + { + char* buffer = (char*) hb_calloc (1, max_size); + hb_serialize_context_t serializer (buffer, max_size); + OT::Layout::Common::Coverage_serialize (&serializer, glyphs); + serializer.end_serialize (); + if (serializer.in_error ()) + { + hb_free (buffer); + return false; + } + + hb_bytes_t coverage_copy = serializer.copy_bytes (); + c.add_buffer ((char *) coverage_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer. + + auto& obj = c.graph.vertices_[dest_obj].obj; + obj.head = (char *) coverage_copy.arrayZ; + obj.tail = obj.head + coverage_copy.length; + + hb_free (buffer); + return true; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::Layout::Common::Coverage::min_size) return false; + switch (u.format) + { + case 1: return ((CoverageFormat1*)this)->sanitize (vertex); + case 2: return ((CoverageFormat2*)this)->sanitize (vertex); +#ifndef HB_NO_BEYOND_64K + // Not currently supported + case 3: + case 4: +#endif + default: return false; + } + } +}; + + +} + +#endif // GRAPH_COVERAGE_GRAPH_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/graph.hh b/src/3rdparty/harfbuzz-ng/src/graph/graph.hh new file mode 100644 index 00000000000..38ca5db0961 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/graph.hh @@ -0,0 +1,1392 @@ +/* + * Copyright ยฉ 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#include "../hb-set.hh" +#include "../hb-priority-queue.hh" +#include "../hb-serialize.hh" + +#ifndef GRAPH_GRAPH_HH +#define GRAPH_GRAPH_HH + +namespace graph { + +/** + * Represents a serialized table in the form of a graph. + * Provides methods for modifying and reordering the graph. + */ +struct graph_t +{ + struct vertex_t + { + hb_serialize_context_t::object_t obj; + int64_t distance = 0 ; + int64_t space = 0 ; + hb_vector_t parents; + unsigned start = 0; + unsigned end = 0; + unsigned priority = 0; + + + bool link_positions_valid (unsigned num_objects, bool removed_nil) + { + hb_set_t assigned_bytes; + for (const auto& l : obj.real_links) + { + if (l.objidx >= num_objects + || (removed_nil && !l.objidx)) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Invalid object index."); + return false; + } + + unsigned start = l.position; + unsigned end = start + l.width - 1; + + if (unlikely (l.width < 2 || l.width > 4)) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Invalid link width."); + return false; + } + + if (unlikely (end >= table_size ())) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Link position is out of bounds."); + return false; + } + + if (unlikely (assigned_bytes.intersects (start, end))) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Found offsets whose positions overlap."); + return false; + } + + assigned_bytes.add_range (start, end); + } + + return !assigned_bytes.in_error (); + } + + void normalize () + { + obj.real_links.qsort (); + for (auto& l : obj.real_links) + { + for (unsigned i = 0; i < l.width; i++) + { + obj.head[l.position + i] = 0; + } + } + } + + bool equals (const vertex_t& other, + const graph_t& graph, + const graph_t& other_graph, + unsigned depth) const + { + if (!(as_bytes () == other.as_bytes ())) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "vertex [%lu] bytes != [%lu] bytes, depth = %u", + (unsigned long) table_size (), + (unsigned long) other.table_size (), + depth); + + auto a = as_bytes (); + auto b = other.as_bytes (); + while (a || b) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " 0x%x %s 0x%x", (unsigned) *a, (*a == *b) ? "==" : "!=", (unsigned) *b); + a++; + b++; + } + return false; + } + + return links_equal (obj.real_links, other.obj.real_links, graph, other_graph, depth); + } + + hb_bytes_t as_bytes () const + { + return hb_bytes_t (obj.head, table_size ()); + } + + friend void swap (vertex_t& a, vertex_t& b) + { + hb_swap (a.obj, b.obj); + hb_swap (a.distance, b.distance); + hb_swap (a.space, b.space); + hb_swap (a.parents, b.parents); + hb_swap (a.start, b.start); + hb_swap (a.end, b.end); + hb_swap (a.priority, b.priority); + } + + hb_hashmap_t + position_to_index_map () const + { + hb_hashmap_t result; + + for (const auto& l : obj.real_links) { + result.set (l.position, l.objidx); + } + + return result; + } + + bool is_shared () const + { + return parents.length > 1; + } + + unsigned incoming_edges () const + { + return parents.length; + } + + void remove_parent (unsigned parent_index) + { + for (unsigned i = 0; i < parents.length; i++) + { + if (parents[i] != parent_index) continue; + parents.remove_unordered (i); + break; + } + } + + void remove_real_link (unsigned child_index, const void* offset) + { + for (unsigned i = 0; i < obj.real_links.length; i++) + { + auto& link = obj.real_links.arrayZ[i]; + if (link.objidx != child_index) + continue; + + if ((obj.head + link.position) != offset) + continue; + + obj.real_links.remove_unordered (i); + return; + } + } + + void remap_parents (const hb_vector_t& id_map) + { + for (unsigned i = 0; i < parents.length; i++) + parents[i] = id_map[parents[i]]; + } + + void remap_parent (unsigned old_index, unsigned new_index) + { + for (unsigned i = 0; i < parents.length; i++) + { + if (parents[i] == old_index) + parents[i] = new_index; + } + } + + bool is_leaf () const + { + return !obj.real_links.length && !obj.virtual_links.length; + } + + bool raise_priority () + { + if (has_max_priority ()) return false; + priority++; + return true; + } + + bool has_max_priority () const { + return priority >= 3; + } + + size_t table_size () const { + return obj.tail - obj.head; + } + + int64_t modified_distance (unsigned order) const + { + // TODO(garretrieger): once priority is high enough, should try + // setting distance = 0 which will force to sort immediately after + // it's parent where possible. + + int64_t modified_distance = + hb_min (hb_max(distance + distance_modifier (), 0), 0x7FFFFFFFFFF); + if (has_max_priority ()) { + modified_distance = 0; + } + return (modified_distance << 18) | (0x003FFFF & order); + } + + int64_t distance_modifier () const + { + if (!priority) return 0; + int64_t table_size = obj.tail - obj.head; + + if (priority == 1) + return -table_size / 2; + + return -table_size; + } + + private: + bool links_equal (const hb_vector_t& this_links, + const hb_vector_t& other_links, + const graph_t& graph, + const graph_t& other_graph, + unsigned depth) const + { + auto a = this_links.iter (); + auto b = other_links.iter (); + + while (a && b) + { + const auto& link_a = *a; + const auto& link_b = *b; + + if (link_a.width != link_b.width || + link_a.is_signed != link_b.is_signed || + link_a.whence != link_b.whence || + link_a.position != link_b.position || + link_a.bias != link_b.bias) + return false; + + if (!graph.vertices_[link_a.objidx].equals ( + other_graph.vertices_[link_b.objidx], graph, other_graph, depth + 1)) + return false; + + a++; + b++; + } + + if (bool (a) != bool (b)) + return false; + + return true; + } + }; + + template + struct vertex_and_table_t + { + vertex_and_table_t () : index (0), vertex (nullptr), table (nullptr) + {} + + unsigned index; + vertex_t* vertex; + T* table; + + operator bool () { + return table && vertex; + } + }; + + /* + * A topological sorting of an object graph. Ordered + * in reverse serialization order (first object in the + * serialization is at the end of the list). This matches + * the 'packed' object stack used internally in the + * serializer + */ + template + graph_t (const T& objects) + : parents_invalid (true), + distance_invalid (true), + positions_invalid (true), + successful (true), + buffers () + { + num_roots_for_space_.push (1); + bool removed_nil = false; + vertices_.alloc (objects.length); + vertices_scratch_.alloc (objects.length); + for (unsigned i = 0; i < objects.length; i++) + { + // If this graph came from a serialization buffer object 0 is the + // nil object. We don't need it for our purposes here so drop it. + if (i == 0 && !objects[i]) + { + removed_nil = true; + continue; + } + + vertex_t* v = vertices_.push (); + if (check_success (!vertices_.in_error ())) + v->obj = *objects[i]; + + check_success (v->link_positions_valid (objects.length, removed_nil)); + + if (!removed_nil) continue; + // Fix indices to account for removed nil object. + for (auto& l : v->obj.all_links_writer ()) { + l.objidx--; + } + } + } + + ~graph_t () + { + vertices_.fini (); + for (char* b : buffers) + hb_free (b); + } + + bool operator== (const graph_t& other) const + { + return root ().equals (other.root (), *this, other, 0); + } + + // Sorts links of all objects in a consistent manner and zeroes all offsets. + void normalize () + { + for (auto& v : vertices_.writer ()) + v.normalize (); + } + + bool in_error () const + { + return !successful || + vertices_.in_error () || + num_roots_for_space_.in_error (); + } + + const vertex_t& root () const + { + return vertices_[root_idx ()]; + } + + unsigned root_idx () const + { + // Object graphs are in reverse order, the first object is at the end + // of the vector. Since the graph is topologically sorted it's safe to + // assume the first object has no incoming edges. + return vertices_.length - 1; + } + + const hb_serialize_context_t::object_t& object (unsigned i) const + { + return vertices_[i].obj; + } + + void add_buffer (char* buffer) + { + buffers.push (buffer); + } + + /* + * Adds a 16 bit link from parent_id to child_id + */ + template + void add_link (T* offset, + unsigned parent_id, + unsigned child_id) + { + auto& v = vertices_[parent_id]; + auto* link = v.obj.real_links.push (); + link->width = 2; + link->objidx = child_id; + link->position = (char*) offset - (char*) v.obj.head; + vertices_[child_id].parents.push (parent_id); + } + + /* + * Generates a new topological sorting of graph ordered by the shortest + * distance to each node if positions are marked as invalid. + */ + void sort_shortest_distance_if_needed () + { + if (!positions_invalid) return; + sort_shortest_distance (); + } + + + /* + * Generates a new topological sorting of graph ordered by the shortest + * distance to each node. + */ + void sort_shortest_distance () + { + positions_invalid = true; + + if (vertices_.length <= 1) { + // Graph of 1 or less doesn't need sorting. + return; + } + + update_distances (); + + hb_priority_queue_t queue; + hb_vector_t &sorted_graph = vertices_scratch_; + if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return; + hb_vector_t id_map; + if (unlikely (!check_success (id_map.resize (vertices_.length)))) return; + + hb_vector_t removed_edges; + if (unlikely (!check_success (removed_edges.resize (vertices_.length)))) return; + update_parents (); + + queue.insert (root ().modified_distance (0), root_idx ()); + int new_id = root_idx (); + unsigned order = 1; + while (!queue.in_error () && !queue.is_empty ()) + { + unsigned next_id = queue.pop_minimum().second; + + hb_swap (sorted_graph[new_id], vertices_[next_id]); + const vertex_t& next = sorted_graph[new_id]; + + if (unlikely (!check_success(new_id >= 0))) { + // We are out of ids. Which means we've visited a node more than once. + // This graph contains a cycle which is not allowed. + DEBUG_MSG (SUBSET_REPACK, nullptr, "Invalid graph. Contains cycle."); + return; + } + + id_map[next_id] = new_id--; + + for (const auto& link : next.obj.all_links ()) { + removed_edges[link.objidx]++; + if (!(vertices_[link.objidx].incoming_edges () - removed_edges[link.objidx])) + // Add the order that the links were encountered to the priority. + // This ensures that ties between priorities objects are broken in a consistent + // way. More specifically this is set up so that if a set of objects have the same + // distance they'll be added to the topological order in the order that they are + // referenced from the parent object. + queue.insert (vertices_[link.objidx].modified_distance (order++), + link.objidx); + } + } + + check_success (!queue.in_error ()); + check_success (!sorted_graph.in_error ()); + + remap_all_obj_indices (id_map, &sorted_graph); + hb_swap (vertices_, sorted_graph); + + if (!check_success (new_id == -1)) + print_orphaned_nodes (); + } + + /* + * Finds the set of nodes (placed into roots) that should be assigned unique spaces. + * More specifically this looks for the top most 24 bit or 32 bit links in the graph. + * Some special casing is done that is specific to the layout of GSUB/GPOS tables. + */ + void find_space_roots (hb_set_t& visited, hb_set_t& roots) + { + int root_index = (int) root_idx (); + for (int i = root_index; i >= 0; i--) + { + if (visited.has (i)) continue; + + // Only real links can form 32 bit spaces + for (auto& l : vertices_[i].obj.real_links) + { + if (l.is_signed || l.width < 3) + continue; + + if (i == root_index && l.width == 3) + // Ignore 24bit links from the root node, this skips past the single 24bit + // pointer to the lookup list. + continue; + + if (l.width == 3) + { + // A 24bit offset forms a root, unless there is 32bit offsets somewhere + // in it's subgraph, then those become the roots instead. This is to make sure + // that extension subtables beneath a 24bit lookup become the spaces instead + // of the offset to the lookup. + hb_set_t sub_roots; + find_32bit_roots (l.objidx, sub_roots); + if (sub_roots) { + for (unsigned sub_root_idx : sub_roots) { + roots.add (sub_root_idx); + find_subgraph (sub_root_idx, visited); + } + continue; + } + } + + roots.add (l.objidx); + find_subgraph (l.objidx, visited); + } + } + } + + template + vertex_and_table_t as_table (unsigned parent, const void* offset, Ts... ds) + { + return as_table_from_index (index_for_offset (parent, offset), std::forward(ds)...); + } + + template + vertex_and_table_t as_mutable_table (unsigned parent, const void* offset, Ts... ds) + { + return as_table_from_index (mutable_index_for_offset (parent, offset), std::forward(ds)...); + } + + template + vertex_and_table_t as_table_from_index (unsigned index, Ts... ds) + { + if (index >= vertices_.length) + return vertex_and_table_t (); + + vertex_and_table_t r; + r.vertex = &vertices_[index]; + r.table = (T*) r.vertex->obj.head; + r.index = index; + if (!r.table) + return vertex_and_table_t (); + + if (!r.table->sanitize (*(r.vertex), std::forward(ds)...)) + return vertex_and_table_t (); + + return r; + } + + // Finds the object id of the object pointed to by the offset at 'offset' + // within object[node_idx]. + unsigned index_for_offset (unsigned node_idx, const void* offset) const + { + const auto& node = object (node_idx); + if (offset < node.head || offset >= node.tail) return -1; + + unsigned length = node.real_links.length; + for (unsigned i = 0; i < length; i++) + { + // Use direct access for increased performance, this is a hot method. + const auto& link = node.real_links.arrayZ[i]; + if (offset != node.head + link.position) + continue; + return link.objidx; + } + + return -1; + } + + // Finds the object id of the object pointed to by the offset at 'offset' + // within object[node_idx]. Ensures that the returned object is safe to mutate. + // That is, if the original child object is shared by parents other than node_idx + // it will be duplicated and the duplicate will be returned instead. + unsigned mutable_index_for_offset (unsigned node_idx, const void* offset) + { + unsigned child_idx = index_for_offset (node_idx, offset); + auto& child = vertices_[child_idx]; + for (unsigned p : child.parents) + { + if (p != node_idx) { + return duplicate (node_idx, child_idx); + } + } + + return child_idx; + } + + + /* + * Assign unique space numbers to each connected subgraph of 24 bit and/or 32 bit offset(s). + * Currently, this is implemented specifically tailored to the structure of a GPOS/GSUB + * (including with 24bit offsets) table. + */ + bool assign_spaces () + { + update_parents (); + + hb_set_t visited; + hb_set_t roots; + find_space_roots (visited, roots); + + // Mark everything not in the subgraphs of the roots as visited. This prevents + // subgraphs from being connected via nodes not in those subgraphs. + visited.invert (); + + if (!roots) return false; + + while (roots) + { + uint32_t next = HB_SET_VALUE_INVALID; + if (unlikely (!check_success (!roots.in_error ()))) break; + if (!roots.next (&next)) break; + + hb_set_t connected_roots; + find_connected_nodes (next, roots, visited, connected_roots); + if (unlikely (!check_success (!connected_roots.in_error ()))) break; + + isolate_subgraph (connected_roots); + if (unlikely (!check_success (!connected_roots.in_error ()))) break; + + unsigned next_space = this->next_space (); + num_roots_for_space_.push (0); + for (unsigned root : connected_roots) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Subgraph %u gets space %u", root, next_space); + vertices_[root].space = next_space; + num_roots_for_space_[next_space] = num_roots_for_space_[next_space] + 1; + distance_invalid = true; + positions_invalid = true; + } + + // TODO(grieger): special case for GSUB/GPOS use extension promotions to move 16 bit space + // into the 32 bit space as needed, instead of using isolation. + } + + + + return true; + } + + /* + * Isolates the subgraph of nodes reachable from root. Any links to nodes in the subgraph + * that originate from outside of the subgraph will be removed by duplicating the linked to + * object. + * + * Indices stored in roots will be updated if any of the roots are duplicated to new indices. + */ + bool isolate_subgraph (hb_set_t& roots) + { + update_parents (); + hb_map_t subgraph; + + // incoming edges to root_idx should be all 32 bit in length so we don't need to de-dup these + // set the subgraph incoming edge count to match all of root_idx's incoming edges + hb_set_t parents; + for (unsigned root_idx : roots) + { + subgraph.set (root_idx, wide_parents (root_idx, parents)); + find_subgraph (root_idx, subgraph); + } + + unsigned original_root_idx = root_idx (); + hb_map_t index_map; + bool made_changes = false; + for (auto entry : subgraph.iter ()) + { + const auto& node = vertices_[entry.first]; + unsigned subgraph_incoming_edges = entry.second; + + if (subgraph_incoming_edges < node.incoming_edges ()) + { + // Only de-dup objects with incoming links from outside the subgraph. + made_changes = true; + duplicate_subgraph (entry.first, index_map); + } + } + + if (in_error ()) + return false; + + if (!made_changes) + return false; + + if (original_root_idx != root_idx () + && parents.has (original_root_idx)) + { + // If the root idx has changed since parents was determined, update root idx in parents + parents.add (root_idx ()); + parents.del (original_root_idx); + } + + auto new_subgraph = + + subgraph.keys () + | hb_map([&] (uint32_t node_idx) { + const uint32_t *v; + if (index_map.has (node_idx, &v)) return *v; + return node_idx; + }) + ; + + remap_obj_indices (index_map, new_subgraph); + remap_obj_indices (index_map, parents.iter (), true); + + // Update roots set with new indices as needed. + uint32_t next = HB_SET_VALUE_INVALID; + while (roots.next (&next)) + { + const uint32_t *v; + if (index_map.has (next, &v)) + { + roots.del (next); + roots.add (*v); + } + } + + return true; + } + + void find_subgraph (unsigned node_idx, hb_map_t& subgraph) + { + for (const auto& link : vertices_[node_idx].obj.all_links ()) + { + const uint32_t *v; + if (subgraph.has (link.objidx, &v)) + { + subgraph.set (link.objidx, *v + 1); + continue; + } + subgraph.set (link.objidx, 1); + find_subgraph (link.objidx, subgraph); + } + } + + void find_subgraph (unsigned node_idx, hb_set_t& subgraph) + { + if (subgraph.has (node_idx)) return; + subgraph.add (node_idx); + for (const auto& link : vertices_[node_idx].obj.all_links ()) + find_subgraph (link.objidx, subgraph); + } + + size_t find_subgraph_size (unsigned node_idx, hb_set_t& subgraph, unsigned max_depth = -1) + { + if (subgraph.has (node_idx)) return 0; + subgraph.add (node_idx); + + const auto& o = vertices_[node_idx].obj; + size_t size = o.tail - o.head; + if (max_depth == 0) + return size; + + for (const auto& link : o.all_links ()) + size += find_subgraph_size (link.objidx, subgraph, max_depth - 1); + return size; + } + + /* + * Finds the topmost children of 32bit offsets in the subgraph starting + * at node_idx. Found indices are placed into 'found'. + */ + void find_32bit_roots (unsigned node_idx, hb_set_t& found) + { + for (const auto& link : vertices_[node_idx].obj.all_links ()) + { + if (!link.is_signed && link.width == 4) { + found.add (link.objidx); + continue; + } + find_32bit_roots (link.objidx, found); + } + } + + /* + * Moves the child of old_parent_idx pointed to by old_offset to a new + * vertex at the new_offset. + */ + template + void move_child (unsigned old_parent_idx, + const O* old_offset, + unsigned new_parent_idx, + const O* new_offset) + { + distance_invalid = true; + positions_invalid = true; + + auto& old_v = vertices_[old_parent_idx]; + auto& new_v = vertices_[new_parent_idx]; + + unsigned child_id = index_for_offset (old_parent_idx, + old_offset); + + auto* new_link = new_v.obj.real_links.push (); + new_link->width = O::static_size; + new_link->objidx = child_id; + new_link->position = (const char*) new_offset - (const char*) new_v.obj.head; + + auto& child = vertices_[child_id]; + child.parents.push (new_parent_idx); + + old_v.remove_real_link (child_id, old_offset); + child.remove_parent (old_parent_idx); + } + + /* + * duplicates all nodes in the subgraph reachable from node_idx. Does not re-assign + * links. index_map is updated with mappings from old id to new id. If a duplication has already + * been performed for a given index, then it will be skipped. + */ + void duplicate_subgraph (unsigned node_idx, hb_map_t& index_map) + { + if (index_map.has (node_idx)) + return; + + unsigned clone_idx = duplicate (node_idx); + if (!check_success (clone_idx != (unsigned) -1)) + return; + + index_map.set (node_idx, clone_idx); + for (const auto& l : object (node_idx).all_links ()) { + duplicate_subgraph (l.objidx, index_map); + } + } + + /* + * Creates a copy of node_idx and returns it's new index. + */ + unsigned duplicate (unsigned node_idx) + { + positions_invalid = true; + distance_invalid = true; + + auto* clone = vertices_.push (); + auto& child = vertices_[node_idx]; + if (vertices_.in_error ()) { + return -1; + } + + clone->obj.head = child.obj.head; + clone->obj.tail = child.obj.tail; + clone->distance = child.distance; + clone->space = child.space; + clone->parents.reset (); + + unsigned clone_idx = vertices_.length - 2; + for (const auto& l : child.obj.real_links) + { + clone->obj.real_links.push (l); + vertices_[l.objidx].parents.push (clone_idx); + } + for (const auto& l : child.obj.virtual_links) + { + clone->obj.virtual_links.push (l); + vertices_[l.objidx].parents.push (clone_idx); + } + + check_success (!clone->obj.real_links.in_error ()); + check_success (!clone->obj.virtual_links.in_error ()); + + // The last object is the root of the graph, so swap back the root to the end. + // The root's obj idx does change, however since it's root nothing else refers to it. + // all other obj idx's will be unaffected. + hb_swap (vertices_[vertices_.length - 2], *clone); + + // Since the root moved, update the parents arrays of all children on the root. + for (const auto& l : root ().obj.all_links ()) + vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ()); + + return clone_idx; + } + + /* + * Creates a copy of child and re-assigns the link from + * parent to the clone. The copy is a shallow copy, objects + * linked from child are not duplicated. + */ + unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx) + { + unsigned new_idx = duplicate (parent_idx, child_idx); + if (new_idx == (unsigned) -1) return child_idx; + return new_idx; + } + + + /* + * Creates a copy of child and re-assigns the link from + * parent to the clone. The copy is a shallow copy, objects + * linked from child are not duplicated. + */ + unsigned duplicate (unsigned parent_idx, unsigned child_idx) + { + update_parents (); + + unsigned links_to_child = 0; + for (const auto& l : vertices_[parent_idx].obj.all_links ()) + { + if (l.objidx == child_idx) links_to_child++; + } + + if (vertices_[child_idx].incoming_edges () <= links_to_child) + { + // Can't duplicate this node, doing so would orphan the original one as all remaining links + // to child are from parent. + DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %u => %u", + parent_idx, child_idx); + return -1; + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, " Duplicating %u => %u", + parent_idx, child_idx); + + unsigned clone_idx = duplicate (child_idx); + if (clone_idx == (unsigned) -1) return false; + // duplicate shifts the root node idx, so if parent_idx was root update it. + if (parent_idx == clone_idx) parent_idx++; + + auto& parent = vertices_[parent_idx]; + for (auto& l : parent.obj.all_links_writer ()) + { + if (l.objidx != child_idx) + continue; + + reassign_link (l, parent_idx, clone_idx); + } + + return clone_idx; + } + + + /* + * Adds a new node to the graph, not connected to anything. + */ + unsigned new_node (char* head, char* tail) + { + positions_invalid = true; + distance_invalid = true; + + auto* clone = vertices_.push (); + if (vertices_.in_error ()) { + return -1; + } + + clone->obj.head = head; + clone->obj.tail = tail; + clone->distance = 0; + clone->space = 0; + + unsigned clone_idx = vertices_.length - 2; + + // The last object is the root of the graph, so swap back the root to the end. + // The root's obj idx does change, however since it's root nothing else refers to it. + // all other obj idx's will be unaffected. + hb_swap (vertices_[vertices_.length - 2], *clone); + + // Since the root moved, update the parents arrays of all children on the root. + for (const auto& l : root ().obj.all_links ()) + vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ()); + + return clone_idx; + } + + /* + * Raises the sorting priority of all children. + */ + bool raise_childrens_priority (unsigned parent_idx) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, " Raising priority of all children of %u", + parent_idx); + // This operation doesn't change ordering until a sort is run, so no need + // to invalidate positions. It does not change graph structure so no need + // to update distances or edge counts. + auto& parent = vertices_[parent_idx].obj; + bool made_change = false; + for (auto& l : parent.all_links_writer ()) + made_change |= vertices_[l.objidx].raise_priority (); + return made_change; + } + + bool is_fully_connected () + { + update_parents(); + + if (root().parents) + // Root cannot have parents. + return false; + + for (unsigned i = 0; i < root_idx (); i++) + { + if (!vertices_[i].parents) + return false; + } + return true; + } + +#if 0 + /* + * Saves the current graph to a packed binary format which the repacker fuzzer takes + * as a seed. + */ + void save_fuzzer_seed (hb_tag_t tag) const + { + FILE* f = fopen ("./repacker_fuzzer_seed", "w"); + fwrite ((void*) &tag, sizeof (tag), 1, f); + + uint16_t num_objects = vertices_.length; + fwrite ((void*) &num_objects, sizeof (num_objects), 1, f); + + for (const auto& v : vertices_) + { + uint16_t blob_size = v.table_size (); + fwrite ((void*) &blob_size, sizeof (blob_size), 1, f); + fwrite ((const void*) v.obj.head, blob_size, 1, f); + } + + uint16_t link_count = 0; + for (const auto& v : vertices_) + link_count += v.obj.real_links.length; + + fwrite ((void*) &link_count, sizeof (link_count), 1, f); + + typedef struct + { + uint16_t parent; + uint16_t child; + uint16_t position; + uint8_t width; + } link_t; + + for (unsigned i = 0; i < vertices_.length; i++) + { + for (const auto& l : vertices_[i].obj.real_links) + { + link_t link { + (uint16_t) i, (uint16_t) l.objidx, + (uint16_t) l.position, (uint8_t) l.width + }; + fwrite ((void*) &link, sizeof (link), 1, f); + } + } + + fclose (f); + } +#endif + + void print_orphaned_nodes () + { + if (!DEBUG_ENABLED(SUBSET_REPACK)) return; + + DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected."); + parents_invalid = true; + update_parents(); + + if (root().parents) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Root node has incoming edges."); + } + + for (unsigned i = 0; i < root_idx (); i++) + { + const auto& v = vertices_[i]; + if (!v.parents) + DEBUG_MSG (SUBSET_REPACK, nullptr, "Node %u is orphaned.", i); + } + } + + unsigned num_roots_for_space (unsigned space) const + { + return num_roots_for_space_[space]; + } + + unsigned next_space () const + { + return num_roots_for_space_.length; + } + + void move_to_new_space (const hb_set_t& indices) + { + num_roots_for_space_.push (0); + unsigned new_space = num_roots_for_space_.length - 1; + + for (unsigned index : indices) { + auto& node = vertices_[index]; + num_roots_for_space_[node.space] = num_roots_for_space_[node.space] - 1; + num_roots_for_space_[new_space] = num_roots_for_space_[new_space] + 1; + node.space = new_space; + distance_invalid = true; + positions_invalid = true; + } + } + + unsigned space_for (unsigned index, unsigned* root = nullptr) const + { + const auto& node = vertices_[index]; + if (node.space) + { + if (root != nullptr) + *root = index; + return node.space; + } + + if (!node.parents) + { + if (root) + *root = index; + return 0; + } + + return space_for (node.parents[0], root); + } + + void err_other_error () { this->successful = false; } + + size_t total_size_in_bytes () const { + size_t total_size = 0; + for (unsigned i = 0; i < vertices_.length; i++) { + size_t size = vertices_[i].obj.tail - vertices_[i].obj.head; + total_size += size; + } + return total_size; + } + + + private: + + /* + * Returns the numbers of incoming edges that are 24 or 32 bits wide. + */ + unsigned wide_parents (unsigned node_idx, hb_set_t& parents) const + { + unsigned count = 0; + hb_set_t visited; + for (unsigned p : vertices_[node_idx].parents) + { + if (visited.has (p)) continue; + visited.add (p); + + // Only real links can be wide + for (const auto& l : vertices_[p].obj.real_links) + { + if (l.objidx == node_idx + && (l.width == 3 || l.width == 4) + && !l.is_signed) + { + count++; + parents.add (p); + } + } + } + return count; + } + + bool check_success (bool success) + { return this->successful && (success || ((void) err_other_error (), false)); } + + public: + /* + * Creates a map from objid to # of incoming edges. + */ + void update_parents () + { + if (!parents_invalid) return; + + for (unsigned i = 0; i < vertices_.length; i++) + vertices_[i].parents.reset (); + + for (unsigned p = 0; p < vertices_.length; p++) + { + for (auto& l : vertices_[p].obj.all_links ()) + { + vertices_[l.objidx].parents.push (p); + } + } + + for (unsigned i = 0; i < vertices_.length; i++) + // parents arrays must be accurate or downstream operations like cycle detection + // and sorting won't work correctly. + check_success (!vertices_[i].parents.in_error ()); + + parents_invalid = false; + } + + /* + * compute the serialized start and end positions for each vertex. + */ + void update_positions () + { + if (!positions_invalid) return; + + unsigned current_pos = 0; + for (int i = root_idx (); i >= 0; i--) + { + auto& v = vertices_[i]; + v.start = current_pos; + current_pos += v.obj.tail - v.obj.head; + v.end = current_pos; + } + + positions_invalid = false; + } + + /* + * Finds the distance to each object in the graph + * from the initial node. + */ + void update_distances () + { + if (!distance_invalid) return; + + // Uses Dijkstra's algorithm to find all of the shortest distances. + // https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm + // + // Implementation Note: + // Since our priority queue doesn't support fast priority decreases + // we instead just add new entries into the queue when a priority changes. + // Redundant ones are filtered out later on by the visited set. + // According to https://www3.cs.stonybrook.edu/~rezaul/papers/TR-07-54.pdf + // for practical performance this is faster then using a more advanced queue + // (such as a fibonacci queue) with a fast decrease priority. + for (unsigned i = 0; i < vertices_.length; i++) + { + if (i == vertices_.length - 1) + vertices_[i].distance = 0; + else + vertices_[i].distance = hb_int_max (int64_t); + } + + hb_priority_queue_t queue; + queue.insert (0, vertices_.length - 1); + + hb_vector_t visited; + visited.resize (vertices_.length); + + while (!queue.in_error () && !queue.is_empty ()) + { + unsigned next_idx = queue.pop_minimum ().second; + if (visited[next_idx]) continue; + const auto& next = vertices_[next_idx]; + int64_t next_distance = vertices_[next_idx].distance; + visited[next_idx] = true; + + for (const auto& link : next.obj.all_links ()) + { + if (visited[link.objidx]) continue; + + const auto& child = vertices_[link.objidx].obj; + unsigned link_width = link.width ? link.width : 4; // treat virtual offsets as 32 bits wide + int64_t child_weight = (child.tail - child.head) + + ((int64_t) 1 << (link_width * 8)) * (vertices_[link.objidx].space + 1); + int64_t child_distance = next_distance + child_weight; + + if (child_distance < vertices_[link.objidx].distance) + { + vertices_[link.objidx].distance = child_distance; + queue.insert (child_distance, link.objidx); + } + } + } + + check_success (!queue.in_error ()); + if (!check_success (queue.is_empty ())) + { + print_orphaned_nodes (); + return; + } + + distance_invalid = false; + } + + private: + /* + * Updates a link in the graph to point to a different object. Corrects the + * parents vector on the previous and new child nodes. + */ + void reassign_link (hb_serialize_context_t::object_t::link_t& link, + unsigned parent_idx, + unsigned new_idx) + { + unsigned old_idx = link.objidx; + link.objidx = new_idx; + vertices_[old_idx].remove_parent (parent_idx); + vertices_[new_idx].parents.push (parent_idx); + } + + /* + * Updates all objidx's in all links using the provided mapping. Corrects incoming edge counts. + */ + template + void remap_obj_indices (const hb_map_t& id_map, + Iterator subgraph, + bool only_wide = false) + { + if (!id_map) return; + for (unsigned i : subgraph) + { + for (auto& link : vertices_[i].obj.all_links_writer ()) + { + const uint32_t *v; + if (!id_map.has (link.objidx, &v)) continue; + if (only_wide && !(link.width == 4 && !link.is_signed)) continue; + + reassign_link (link, i, *v); + } + } + } + + /* + * Updates all objidx's in all links using the provided mapping. + */ + void remap_all_obj_indices (const hb_vector_t& id_map, + hb_vector_t* sorted_graph) const + { + for (unsigned i = 0; i < sorted_graph->length; i++) + { + (*sorted_graph)[i].remap_parents (id_map); + for (auto& link : (*sorted_graph)[i].obj.all_links_writer ()) + { + link.objidx = id_map[link.objidx]; + } + } + } + + /* + * Finds all nodes in targets that are reachable from start_idx, nodes in visited will be skipped. + * For this search the graph is treated as being undirected. + * + * Connected targets will be added to connected and removed from targets. All visited nodes + * will be added to visited. + */ + void find_connected_nodes (unsigned start_idx, + hb_set_t& targets, + hb_set_t& visited, + hb_set_t& connected) + { + if (unlikely (!check_success (!visited.in_error ()))) return; + if (visited.has (start_idx)) return; + visited.add (start_idx); + + if (targets.has (start_idx)) + { + targets.del (start_idx); + connected.add (start_idx); + } + + const auto& v = vertices_[start_idx]; + + // Graph is treated as undirected so search children and parents of start_idx + for (const auto& l : v.obj.all_links ()) + find_connected_nodes (l.objidx, targets, visited, connected); + + for (unsigned p : v.parents) + find_connected_nodes (p, targets, visited, connected); + } + + public: + // TODO(garretrieger): make private, will need to move most of offset overflow code into graph. + hb_vector_t vertices_; + hb_vector_t vertices_scratch_; + private: + bool parents_invalid; + bool distance_invalid; + bool positions_invalid; + bool successful; + hb_vector_t num_roots_for_space_; + hb_vector_t buffers; +}; + +} + +#endif // GRAPH_GRAPH_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.cc b/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.cc new file mode 100644 index 00000000000..b2044426d46 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.cc @@ -0,0 +1,70 @@ +/* + * Copyright ยฉ 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#include "gsubgpos-graph.hh" + +namespace graph { + +gsubgpos_graph_context_t::gsubgpos_graph_context_t (hb_tag_t table_tag_, + graph_t& graph_) + : table_tag (table_tag_), + graph (graph_), + lookup_list_index (0), + lookups () +{ + if (table_tag_ != HB_OT_TAG_GPOS + && table_tag_ != HB_OT_TAG_GSUB) + return; + + GSTAR* gstar = graph::GSTAR::graph_to_gstar (graph_); + if (gstar) { + gstar->find_lookups (graph, lookups); + lookup_list_index = gstar->get_lookup_list_index (graph_); + } +} + +unsigned gsubgpos_graph_context_t::create_node (unsigned size) +{ + char* buffer = (char*) hb_calloc (1, size); + if (!buffer) + return -1; + + add_buffer (buffer); + + return graph.new_node (buffer, buffer + size); +} + +unsigned gsubgpos_graph_context_t::num_non_ext_subtables () { + unsigned count = 0; + for (auto l : lookups.values ()) + { + if (l->is_extension (table_tag)) continue; + count += l->number_of_subtables (); + } + return count; +} + +} diff --git a/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.hh b/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.hh new file mode 100644 index 00000000000..9fe9662e645 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.hh @@ -0,0 +1,61 @@ +/* + * Copyright ยฉ 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#include "graph.hh" +#include "../hb-ot-layout-gsubgpos.hh" + +#ifndef GRAPH_GSUBGPOS_CONTEXT_HH +#define GRAPH_GSUBGPOS_CONTEXT_HH + +namespace graph { + +struct Lookup; + +struct gsubgpos_graph_context_t +{ + hb_tag_t table_tag; + graph_t& graph; + unsigned lookup_list_index; + hb_hashmap_t lookups; + + + HB_INTERNAL gsubgpos_graph_context_t (hb_tag_t table_tag_, + graph_t& graph_); + + HB_INTERNAL unsigned create_node (unsigned size); + + void add_buffer (char* buffer) + { + graph.add_buffer (buffer); + } + + private: + HB_INTERNAL unsigned num_non_ext_subtables (); +}; + +} + +#endif // GRAPH_GSUBGPOS_CONTEXT diff --git a/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-graph.hh b/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-graph.hh new file mode 100644 index 00000000000..c170638409f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-graph.hh @@ -0,0 +1,414 @@ +/* + * Copyright ยฉ 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#include "graph.hh" +#include "../hb-ot-layout-gsubgpos.hh" +#include "../OT/Layout/GSUB/ExtensionSubst.hh" +#include "gsubgpos-context.hh" +#include "pairpos-graph.hh" +#include "markbasepos-graph.hh" + +#ifndef GRAPH_GSUBGPOS_GRAPH_HH +#define GRAPH_GSUBGPOS_GRAPH_HH + +namespace graph { + +struct Lookup; + +template +struct ExtensionFormat1 : public OT::ExtensionFormat1 +{ + void reset(unsigned type) + { + this->format = 1; + this->extensionLookupType = type; + this->extensionOffset = 0; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + return vertex_len >= OT::ExtensionFormat1::static_size; + } + + unsigned get_lookup_type () const + { + return this->extensionLookupType; + } + + unsigned get_subtable_index (graph_t& graph, unsigned this_index) const + { + return graph.index_for_offset (this_index, &this->extensionOffset); + } +}; + +struct Lookup : public OT::Lookup +{ + unsigned number_of_subtables () const + { + return subTable.len; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::Lookup::min_size) return false; + return vertex_len >= this->get_size (); + } + + bool is_extension (hb_tag_t table_tag) const + { + return lookupType == extension_type (table_tag); + } + + bool make_extension (gsubgpos_graph_context_t& c, + unsigned this_index) + { + unsigned type = lookupType; + unsigned ext_type = extension_type (c.table_tag); + if (!ext_type || is_extension (c.table_tag)) + { + // NOOP + return true; + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Promoting lookup type %u (obj %u) to extension.", + type, + this_index); + + for (unsigned i = 0; i < subTable.len; i++) + { + unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]); + if (!make_subtable_extension (c, + this_index, + subtable_index)) + return false; + } + + lookupType = ext_type; + return true; + } + + bool split_subtables_if_needed (gsubgpos_graph_context_t& c, + unsigned this_index) + { + unsigned type = lookupType; + bool is_ext = is_extension (c.table_tag); + + if (c.table_tag != HB_OT_TAG_GPOS) + return true; + + if (!is_ext && + type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair && + type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase) + return true; + + hb_vector_t>> all_new_subtables; + for (unsigned i = 0; i < subTable.len; i++) + { + unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]); + unsigned parent_index = this_index; + if (is_ext) { + unsigned ext_subtable_index = subtable_index; + parent_index = ext_subtable_index; + ExtensionFormat1* extension = + (ExtensionFormat1*) + c.graph.object (ext_subtable_index).head; + if (!extension || !extension->sanitize (c.graph.vertices_[ext_subtable_index])) + continue; + + subtable_index = extension->get_subtable_index (c.graph, ext_subtable_index); + type = extension->get_lookup_type (); + if (type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair + && type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase) + continue; + } + + hb_vector_t new_sub_tables; + switch (type) + { + case 2: + new_sub_tables = split_subtable (c, parent_index, subtable_index); break; + case 4: + new_sub_tables = split_subtable (c, parent_index, subtable_index); break; + default: + break; + } + if (new_sub_tables.in_error ()) return false; + if (!new_sub_tables) continue; + hb_pair_t>* entry = all_new_subtables.push (); + entry->first = i; + entry->second = std::move (new_sub_tables); + } + + if (all_new_subtables) { + add_sub_tables (c, this_index, type, all_new_subtables); + } + + return true; + } + + template + hb_vector_t split_subtable (gsubgpos_graph_context_t& c, + unsigned parent_idx, + unsigned objidx) + { + T* sub_table = (T*) c.graph.object (objidx).head; + if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx])) + return hb_vector_t (); + + return sub_table->split_subtables (c, parent_idx, objidx); + } + + void add_sub_tables (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned type, + hb_vector_t>>& subtable_ids) + { + bool is_ext = is_extension (c.table_tag); + auto& v = c.graph.vertices_[this_index]; + fix_existing_subtable_links (c, this_index, subtable_ids); + + unsigned new_subtable_count = 0; + for (const auto& p : subtable_ids) + new_subtable_count += p.second.length; + + size_t new_size = v.table_size () + + new_subtable_count * OT::Offset16::static_size; + char* buffer = (char*) hb_calloc (1, new_size); + c.add_buffer (buffer); + hb_memcpy (buffer, v.obj.head, v.table_size()); + + v.obj.head = buffer; + v.obj.tail = buffer + new_size; + + Lookup* new_lookup = (Lookup*) buffer; + + unsigned shift = 0; + new_lookup->subTable.len = subTable.len + new_subtable_count; + for (const auto& p : subtable_ids) + { + unsigned offset_index = p.first + shift + 1; + shift += p.second.length; + + for (unsigned subtable_id : p.second) + { + if (is_ext) + { + unsigned ext_id = create_extension_subtable (c, subtable_id, type); + c.graph.vertices_[subtable_id].parents.push (ext_id); + subtable_id = ext_id; + } + + auto* link = v.obj.real_links.push (); + link->width = 2; + link->objidx = subtable_id; + link->position = (char*) &new_lookup->subTable[offset_index++] - + (char*) new_lookup; + c.graph.vertices_[subtable_id].parents.push (this_index); + } + } + + // Repacker sort order depends on link order, which we've messed up so resort it. + v.obj.real_links.qsort (); + + // The head location of the lookup has changed, invalidating the lookups map entry + // in the context. Update the map. + c.lookups.set (this_index, new_lookup); + } + + void fix_existing_subtable_links (gsubgpos_graph_context_t& c, + unsigned this_index, + hb_vector_t>>& subtable_ids) + { + auto& v = c.graph.vertices_[this_index]; + Lookup* lookup = (Lookup*) v.obj.head; + + unsigned shift = 0; + for (const auto& p : subtable_ids) + { + unsigned insert_index = p.first + shift; + unsigned pos_offset = p.second.length * OT::Offset16::static_size; + unsigned insert_offset = (char*) &lookup->subTable[insert_index] - (char*) lookup; + shift += p.second.length; + + for (auto& l : v.obj.all_links_writer ()) + { + if (l.position > insert_offset) l.position += pos_offset; + } + } + } + + unsigned create_extension_subtable (gsubgpos_graph_context_t& c, + unsigned subtable_index, + unsigned type) + { + unsigned extension_size = OT::ExtensionFormat1::static_size; + + unsigned ext_index = c.create_node (extension_size); + if (ext_index == (unsigned) -1) + return -1; + + auto& ext_vertex = c.graph.vertices_[ext_index]; + ExtensionFormat1* extension = + (ExtensionFormat1*) ext_vertex.obj.head; + extension->reset (type); + + // Make extension point at the subtable. + auto* l = ext_vertex.obj.real_links.push (); + + l->width = 4; + l->objidx = subtable_index; + l->position = 4; + + return ext_index; + } + + bool make_subtable_extension (gsubgpos_graph_context_t& c, + unsigned lookup_index, + unsigned subtable_index) + { + unsigned type = lookupType; + + unsigned ext_index = create_extension_subtable(c, subtable_index, type); + if (ext_index == (unsigned) -1) + return false; + + auto& lookup_vertex = c.graph.vertices_[lookup_index]; + for (auto& l : lookup_vertex.obj.real_links.writer ()) + { + if (l.objidx == subtable_index) + // Change lookup to point at the extension. + l.objidx = ext_index; + } + + // Make extension point at the subtable. + auto& ext_vertex = c.graph.vertices_[ext_index]; + auto& subtable_vertex = c.graph.vertices_[subtable_index]; + ext_vertex.parents.push (lookup_index); + subtable_vertex.remap_parent (lookup_index, ext_index); + + return true; + } + + private: + unsigned extension_type (hb_tag_t table_tag) const + { + switch (table_tag) + { + case HB_OT_TAG_GPOS: return 9; + case HB_OT_TAG_GSUB: return 7; + default: return 0; + } + } +}; + +template +struct LookupList : public OT::LookupList +{ + bool sanitize (const graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::LookupList::min_size) return false; + return vertex_len >= OT::LookupList::item_size * this->len; + } +}; + +struct GSTAR : public OT::GSUBGPOS +{ + static GSTAR* graph_to_gstar (graph_t& graph) + { + const auto& r = graph.root (); + + GSTAR* gstar = (GSTAR*) r.obj.head; + if (!gstar || !gstar->sanitize (r)) + return nullptr; + + return gstar; + } + + const void* get_lookup_list_field_offset () const + { + switch (u.version.major) { + case 1: return u.version1.get_lookup_list_offset (); +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.get_lookup_list_offset (); +#endif + default: return 0; + } + } + + bool sanitize (const graph_t::vertex_t& vertex) + { + int64_t len = vertex.obj.tail - vertex.obj.head; + if (len < OT::GSUBGPOS::min_size) return false; + return len >= get_size (); + } + + void find_lookups (graph_t& graph, + hb_hashmap_t& lookups /* OUT */) + { + switch (u.version.major) { + case 1: find_lookups (graph, lookups); break; +#ifndef HB_NO_BEYOND_64K + case 2: find_lookups (graph, lookups); break; +#endif + } + } + + unsigned get_lookup_list_index (graph_t& graph) + { + return graph.index_for_offset (graph.root_idx (), + get_lookup_list_field_offset()); + } + + template + void find_lookups (graph_t& graph, + hb_hashmap_t& lookups /* OUT */) + { + unsigned lookup_list_idx = get_lookup_list_index (graph); + const LookupList* lookupList = + (const LookupList*) graph.object (lookup_list_idx).head; + if (!lookupList || !lookupList->sanitize (graph.vertices_[lookup_list_idx])) + return; + + for (unsigned i = 0; i < lookupList->len; i++) + { + unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i])); + Lookup* lookup = (Lookup*) graph.object (lookup_idx).head; + if (!lookup || !lookup->sanitize (graph.vertices_[lookup_idx])) continue; + lookups.set (lookup_idx, lookup); + } + } +}; + + + + +} + +#endif /* GRAPH_GSUBGPOS_GRAPH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/graph/markbasepos-graph.hh b/src/3rdparty/harfbuzz-ng/src/graph/markbasepos-graph.hh new file mode 100644 index 00000000000..84ef5f71b93 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/markbasepos-graph.hh @@ -0,0 +1,510 @@ +/* + * Copyright ยฉ 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#ifndef GRAPH_MARKBASEPOS_GRAPH_HH +#define GRAPH_MARKBASEPOS_GRAPH_HH + +#include "split-helpers.hh" +#include "coverage-graph.hh" +#include "../OT/Layout/GPOS/MarkBasePos.hh" +#include "../OT/Layout/GPOS/PosLookupSubTable.hh" + +namespace graph { + +struct AnchorMatrix : public OT::Layout::GPOS_impl::AnchorMatrix +{ + bool sanitize (graph_t::vertex_t& vertex, unsigned class_count) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < AnchorMatrix::min_size) return false; + + return vertex_len >= AnchorMatrix::min_size + + OT::Offset16::static_size * class_count * this->rows; + } + + bool shrink (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned old_class_count, + unsigned new_class_count) + { + if (new_class_count >= old_class_count) return false; + auto& o = c.graph.vertices_[this_index].obj; + unsigned base_count = rows; + o.tail = o.head + + AnchorMatrix::min_size + + OT::Offset16::static_size * base_count * new_class_count; + + // Reposition links into the new indexing scheme. + for (auto& link : o.real_links.writer ()) + { + unsigned index = (link.position - 2) / 2; + unsigned base = index / old_class_count; + unsigned klass = index % old_class_count; + if (klass >= new_class_count) + // should have already been removed + return false; + + unsigned new_index = base * new_class_count + klass; + + link.position = (char*) &(this->matrixZ[new_index]) - (char*) this; + } + + return true; + } + + unsigned clone (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned start, + unsigned end, + unsigned class_count) + { + unsigned base_count = rows; + unsigned new_class_count = end - start; + unsigned size = AnchorMatrix::min_size + + OT::Offset16::static_size * new_class_count * rows; + unsigned prime_id = c.create_node (size); + if (prime_id == (unsigned) -1) return -1; + AnchorMatrix* prime = (AnchorMatrix*) c.graph.object (prime_id).head; + prime->rows = base_count; + + auto& o = c.graph.vertices_[this_index].obj; + int num_links = o.real_links.length; + for (int i = 0; i < num_links; i++) + { + const auto& link = o.real_links[i]; + unsigned old_index = (link.position - 2) / OT::Offset16::static_size; + unsigned klass = old_index % class_count; + if (klass < start || klass >= end) continue; + + unsigned base = old_index / class_count; + unsigned new_klass = klass - start; + unsigned new_index = base * new_class_count + new_klass; + + + unsigned child_idx = link.objidx; + c.graph.add_link (&(prime->matrixZ[new_index]), + prime_id, + child_idx); + + auto& child = c.graph.vertices_[child_idx]; + child.remove_parent (this_index); + + o.real_links.remove_unordered (i); + num_links--; + i--; + } + + return prime_id; + } +}; + +struct MarkArray : public OT::Layout::GPOS_impl::MarkArray +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + unsigned min_size = MarkArray::min_size; + if (vertex_len < min_size) return false; + + return vertex_len >= get_size (); + } + + bool shrink (gsubgpos_graph_context_t& c, + const hb_hashmap_t& mark_array_links, + unsigned this_index, + unsigned new_class_count) + { + auto& o = c.graph.vertices_[this_index].obj; + for (const auto& link : o.real_links) + c.graph.vertices_[link.objidx].remove_parent (this_index); + o.real_links.reset (); + + unsigned new_index = 0; + for (const auto& record : this->iter ()) + { + unsigned klass = record.klass; + if (klass >= new_class_count) continue; + + (*this)[new_index].klass = klass; + unsigned position = (char*) &record.markAnchor - (char*) this; + unsigned* objidx; + if (!mark_array_links.has (position, &objidx)) + { + new_index++; + continue; + } + + c.graph.add_link (&(*this)[new_index].markAnchor, this_index, *objidx); + new_index++; + } + + this->len = new_index; + o.tail = o.head + MarkArray::min_size + + OT::Layout::GPOS_impl::MarkRecord::static_size * new_index; + return true; + } + + unsigned clone (gsubgpos_graph_context_t& c, + unsigned this_index, + const hb_hashmap_t& pos_to_index, + hb_set_t& marks, + unsigned start_class) + { + unsigned size = MarkArray::min_size + + OT::Layout::GPOS_impl::MarkRecord::static_size * + marks.get_population (); + unsigned prime_id = c.create_node (size); + if (prime_id == (unsigned) -1) return -1; + MarkArray* prime = (MarkArray*) c.graph.object (prime_id).head; + prime->len = marks.get_population (); + + + unsigned i = 0; + for (hb_codepoint_t mark : marks) + { + (*prime)[i].klass = (*this)[mark].klass - start_class; + unsigned offset_pos = (char*) &((*this)[mark].markAnchor) - (char*) this; + unsigned* anchor_index; + if (pos_to_index.has (offset_pos, &anchor_index)) + c.graph.move_child (this_index, + &((*this)[mark].markAnchor), + prime_id, + &((*prime)[i].markAnchor)); + + i++; + } + + return prime_id; + } +}; + +struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + return vertex_len >= MarkBasePosFormat1::static_size; + } + + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + hb_set_t visited; + + const unsigned base_coverage_id = c.graph.index_for_offset (this_index, &baseCoverage); + const unsigned base_size = + OT::Layout::GPOS_impl::PairPosFormat1_3::min_size + + MarkArray::min_size + + AnchorMatrix::min_size + + c.graph.vertices_[base_coverage_id].table_size (); + + hb_vector_t class_to_info = get_class_info (c, this_index); + + unsigned class_count = classCount; + auto base_array = c.graph.as_table (this_index, + &baseArray, + class_count); + if (!base_array) return hb_vector_t (); + unsigned base_count = base_array.table->rows; + + unsigned partial_coverage_size = 4; + unsigned accumulated = base_size; + hb_vector_t split_points; + + for (unsigned klass = 0; klass < class_count; klass++) + { + class_info_t& info = class_to_info[klass]; + partial_coverage_size += OT::HBUINT16::static_size * info.marks.get_population (); + unsigned accumulated_delta = + OT::Layout::GPOS_impl::MarkRecord::static_size * info.marks.get_population () + + OT::Offset16::static_size * base_count; + + for (unsigned objidx : info.child_indices) + accumulated_delta += c.graph.find_subgraph_size (objidx, visited); + + accumulated += accumulated_delta; + unsigned total = accumulated + partial_coverage_size; + + if (total >= (1 << 16)) + { + split_points.push (klass); + accumulated = base_size + accumulated_delta; + partial_coverage_size = 4 + OT::HBUINT16::static_size * info.marks.get_population (); + visited.clear (); // node sharing isn't allowed between splits. + } + } + + + const unsigned mark_array_id = c.graph.index_for_offset (this_index, &markArray); + split_context_t split_context { + c, + this, + c.graph.duplicate_if_shared (parent_index, this_index), + std::move (class_to_info), + c.graph.vertices_[mark_array_id].position_to_index_map (), + }; + + return actuate_subtable_split (split_context, split_points); + } + + private: + + struct class_info_t { + hb_set_t marks; + hb_vector_t child_indices; + }; + + struct split_context_t { + gsubgpos_graph_context_t& c; + MarkBasePosFormat1* thiz; + unsigned this_index; + hb_vector_t class_to_info; + hb_hashmap_t mark_array_links; + + hb_set_t marks_for (unsigned start, unsigned end) + { + hb_set_t marks; + for (unsigned klass = start; klass < end; klass++) + { + + class_to_info[klass].marks.iter () + | hb_sink (marks) + ; + } + return marks; + } + + unsigned original_count () + { + return thiz->classCount; + } + + unsigned clone_range (unsigned start, unsigned end) + { + return thiz->clone_range (*this, this->this_index, start, end); + } + + bool shrink (unsigned count) + { + return thiz->shrink (*this, this->this_index, count); + } + }; + + hb_vector_t get_class_info (gsubgpos_graph_context_t& c, + unsigned this_index) + { + hb_vector_t class_to_info; + + unsigned class_count= classCount; + class_to_info.resize (class_count); + + auto mark_array = c.graph.as_table (this_index, &markArray); + if (!mark_array) return hb_vector_t (); + unsigned mark_count = mark_array.table->len; + for (unsigned mark = 0; mark < mark_count; mark++) + { + unsigned klass = (*mark_array.table)[mark].get_class (); + class_to_info[klass].marks.add (mark); + } + + for (const auto& link : mark_array.vertex->obj.real_links) + { + unsigned mark = (link.position - 2) / + OT::Layout::GPOS_impl::MarkRecord::static_size; + unsigned klass = (*mark_array.table)[mark].get_class (); + class_to_info[klass].child_indices.push (link.objidx); + } + + unsigned base_array_id = + c.graph.index_for_offset (this_index, &baseArray); + auto& base_array_v = c.graph.vertices_[base_array_id]; + + for (const auto& link : base_array_v.obj.real_links) + { + unsigned index = (link.position - 2) / OT::Offset16::static_size; + unsigned klass = index % class_count; + class_to_info[klass].child_indices.push (link.objidx); + } + + return class_to_info; + } + + bool shrink (split_context_t& sc, + unsigned this_index, + unsigned count) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Shrinking MarkBasePosFormat1 (%u) to [0, %u).", + this_index, + count); + + unsigned old_count = classCount; + if (count >= old_count) + return true; + + classCount = count; + + auto mark_coverage = sc.c.graph.as_mutable_table (this_index, + &markCoverage); + if (!mark_coverage) return false; + hb_set_t marks = sc.marks_for (0, count); + auto new_coverage = + + hb_enumerate (mark_coverage.table->iter ()) + | hb_filter (marks, hb_first) + | hb_map_retains_sorting (hb_second) + ; + if (!Coverage::make_coverage (sc.c, + new_coverage, + mark_coverage.index, + 4 + 2 * marks.get_population ())) + return false; + + + auto base_array = sc.c.graph.as_mutable_table (this_index, + &baseArray, + old_count); + if (!base_array || !base_array.table->shrink (sc.c, + base_array.index, + old_count, + count)) + return false; + + auto mark_array = sc.c.graph.as_mutable_table (this_index, + &markArray); + if (!mark_array || !mark_array.table->shrink (sc.c, + sc.mark_array_links, + mark_array.index, + count)) + return false; + + return true; + } + + // Create a new MarkBasePos that has all of the data for classes from [start, end). + unsigned clone_range (split_context_t& sc, + unsigned this_index, + unsigned start, unsigned end) const + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Cloning MarkBasePosFormat1 (%u) range [%u, %u).", this_index, start, end); + + graph_t& graph = sc.c.graph; + unsigned prime_size = OT::Layout::GPOS_impl::MarkBasePosFormat1_2::static_size; + + unsigned prime_id = sc.c.create_node (prime_size); + if (prime_id == (unsigned) -1) return -1; + + MarkBasePosFormat1* prime = (MarkBasePosFormat1*) graph.object (prime_id).head; + prime->format = this->format; + unsigned new_class_count = end - start; + prime->classCount = new_class_count; + + unsigned base_coverage_id = + graph.index_for_offset (sc.this_index, &baseCoverage); + graph.add_link (&(prime->baseCoverage), prime_id, base_coverage_id); + graph.duplicate (prime_id, base_coverage_id); + + auto mark_coverage = sc.c.graph.as_table (this_index, + &markCoverage); + if (!mark_coverage) return false; + hb_set_t marks = sc.marks_for (start, end); + auto new_coverage = + + hb_enumerate (mark_coverage.table->iter ()) + | hb_filter (marks, hb_first) + | hb_map_retains_sorting (hb_second) + ; + if (!Coverage::add_coverage (sc.c, + prime_id, + 2, + + new_coverage, + marks.get_population () * 2 + 4)) + return -1; + + auto mark_array = + graph.as_table (sc.this_index, &markArray); + if (!mark_array) return -1; + unsigned new_mark_array = + mark_array.table->clone (sc.c, + mark_array.index, + sc.mark_array_links, + marks, + start); + graph.add_link (&(prime->markArray), prime_id, new_mark_array); + + unsigned class_count = classCount; + auto base_array = + graph.as_table (sc.this_index, &baseArray, class_count); + if (!base_array) return -1; + unsigned new_base_array = + base_array.table->clone (sc.c, + base_array.index, + start, end, this->classCount); + graph.add_link (&(prime->baseArray), prime_id, new_base_array); + + return prime_id; + } +}; + + +struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos +{ + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + switch (u.format) { + case 1: + return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); +#ifndef HB_NO_BEYOND_64K + case 2: HB_FALLTHROUGH; + // Don't split 24bit PairPos's. +#endif + default: + return hb_vector_t (); + } + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < u.format.get_size ()) return false; + + switch (u.format) { + case 1: + return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex); +#ifndef HB_NO_BEYOND_64K + case 2: HB_FALLTHROUGH; +#endif + default: + // We don't handle format 3 and 4 here. + return false; + } + } +}; + + +} + +#endif // GRAPH_MARKBASEPOS_GRAPH_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/pairpos-graph.hh b/src/3rdparty/harfbuzz-ng/src/graph/pairpos-graph.hh new file mode 100644 index 00000000000..1c13eb24f94 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/pairpos-graph.hh @@ -0,0 +1,647 @@ +/* + * Copyright ยฉ 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#ifndef GRAPH_PAIRPOS_GRAPH_HH +#define GRAPH_PAIRPOS_GRAPH_HH + +#include "split-helpers.hh" +#include "coverage-graph.hh" +#include "classdef-graph.hh" +#include "../OT/Layout/GPOS/PairPos.hh" +#include "../OT/Layout/GPOS/PosLookupSubTable.hh" + +namespace graph { + +struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size; + if (vertex_len < min_size) return false; + + return vertex_len >= + min_size + pairSet.get_size () - pairSet.len.get_size(); + } + + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + hb_set_t visited; + + const unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); + const unsigned coverage_size = c.graph.vertices_[coverage_id].table_size (); + const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size; + + unsigned partial_coverage_size = 4; + unsigned accumulated = base_size; + hb_vector_t split_points; + for (unsigned i = 0; i < pairSet.len; i++) + { + unsigned pair_set_index = pair_set_graph_index (c, this_index, i); + unsigned accumulated_delta = + c.graph.find_subgraph_size (pair_set_index, visited) + + SmallTypes::size; // for PairSet offset. + partial_coverage_size += OT::HBUINT16::static_size; + + accumulated += accumulated_delta; + unsigned total = accumulated + hb_min (partial_coverage_size, coverage_size); + + if (total >= (1 << 16)) + { + split_points.push (i); + accumulated = base_size + accumulated_delta; + partial_coverage_size = 6; + visited.clear (); // node sharing isn't allowed between splits. + } + } + + split_context_t split_context { + c, + this, + c.graph.duplicate_if_shared (parent_index, this_index), + }; + + return actuate_subtable_split (split_context, split_points); + } + + private: + + struct split_context_t { + gsubgpos_graph_context_t& c; + PairPosFormat1* thiz; + unsigned this_index; + + unsigned original_count () + { + return thiz->pairSet.len; + } + + unsigned clone_range (unsigned start, unsigned end) + { + return thiz->clone_range (this->c, this->this_index, start, end); + } + + bool shrink (unsigned count) + { + return thiz->shrink (this->c, this->this_index, count); + } + }; + + bool shrink (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned count) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Shrinking PairPosFormat1 (%u) to [0, %u).", + this_index, + count); + unsigned old_count = pairSet.len; + if (count >= old_count) + return true; + + pairSet.len = count; + c.graph.vertices_[this_index].obj.tail -= (old_count - count) * SmallTypes::size; + + auto coverage = c.graph.as_mutable_table (this_index, &this->coverage); + if (!coverage) return false; + + unsigned coverage_size = coverage.vertex->table_size (); + auto new_coverage = + + hb_zip (coverage.table->iter (), hb_range ()) + | hb_filter ([&] (hb_pair_t p) { + return p.second < count; + }) + | hb_map_retains_sorting (hb_first) + ; + + return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size); + } + + // Create a new PairPos including PairSet's from start (inclusive) to end (exclusive). + // Returns object id of the new object. + unsigned clone_range (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned start, unsigned end) const + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Cloning PairPosFormat1 (%u) range [%u, %u).", this_index, start, end); + + unsigned num_pair_sets = end - start; + unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size + + num_pair_sets * SmallTypes::size; + + unsigned pair_pos_prime_id = c.create_node (prime_size); + if (pair_pos_prime_id == (unsigned) -1) return -1; + + PairPosFormat1* pair_pos_prime = (PairPosFormat1*) c.graph.object (pair_pos_prime_id).head; + pair_pos_prime->format = this->format; + pair_pos_prime->valueFormat[0] = this->valueFormat[0]; + pair_pos_prime->valueFormat[1] = this->valueFormat[1]; + pair_pos_prime->pairSet.len = num_pair_sets; + + for (unsigned i = start; i < end; i++) + { + c.graph.move_child<> (this_index, + &pairSet[i], + pair_pos_prime_id, + &pair_pos_prime->pairSet[i - start]); + } + + unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); + if (!Coverage::clone_coverage (c, + coverage_id, + pair_pos_prime_id, + 2, + start, end)) + return -1; + + return pair_pos_prime_id; + } + + + + unsigned pair_set_graph_index (gsubgpos_graph_context_t& c, unsigned this_index, unsigned i) const + { + return c.graph.index_for_offset (this_index, &pairSet[i]); + } +}; + +struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + size_t vertex_len = vertex.table_size (); + unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size; + if (vertex_len < min_size) return false; + + const unsigned class1_count = class1Count; + return vertex_len >= + min_size + class1_count * get_class1_record_size (); + } + + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size; + const unsigned class_def_2_size = size_of (c, this_index, &classDef2); + const Coverage* coverage = get_coverage (c, this_index); + const ClassDef* class_def_1 = get_class_def_1 (c, this_index); + auto gid_and_class = + + coverage->iter () + | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { + return hb_pair_t (gid, class_def_1->get_class (gid)); + }) + ; + class_def_size_estimator_t estimator (gid_and_class); + + const unsigned class1_count = class1Count; + const unsigned class2_count = class2Count; + const unsigned class1_record_size = get_class1_record_size (); + + const unsigned value_1_len = valueFormat1.get_len (); + const unsigned value_2_len = valueFormat2.get_len (); + const unsigned total_value_len = value_1_len + value_2_len; + + unsigned accumulated = base_size; + unsigned coverage_size = 4; + unsigned class_def_1_size = 4; + unsigned max_coverage_size = coverage_size; + unsigned max_class_def_1_size = class_def_1_size; + + hb_vector_t split_points; + + hb_hashmap_t device_tables = get_all_device_tables (c, this_index); + hb_vector_t format1_device_table_indices = valueFormat1.get_device_table_indices (); + hb_vector_t format2_device_table_indices = valueFormat2.get_device_table_indices (); + bool has_device_tables = bool(format1_device_table_indices) || bool(format2_device_table_indices); + + hb_set_t visited; + for (unsigned i = 0; i < class1_count; i++) + { + unsigned accumulated_delta = class1_record_size; + coverage_size += estimator.incremental_coverage_size (i); + class_def_1_size += estimator.incremental_class_def_size (i); + max_coverage_size = hb_max (max_coverage_size, coverage_size); + max_class_def_1_size = hb_max (max_class_def_1_size, class_def_1_size); + + if (has_device_tables) { + for (unsigned j = 0; j < class2_count; j++) + { + unsigned value1_index = total_value_len * (class2_count * i + j); + unsigned value2_index = value1_index + value_1_len; + accumulated_delta += size_of_value_record_children (c, + device_tables, + format1_device_table_indices, + value1_index, + visited); + accumulated_delta += size_of_value_record_children (c, + device_tables, + format2_device_table_indices, + value2_index, + visited); + } + } + + accumulated += accumulated_delta; + unsigned total = accumulated + + coverage_size + class_def_1_size + class_def_2_size + // The largest object will pack last and can exceed the size limit. + - hb_max (hb_max (coverage_size, class_def_1_size), class_def_2_size); + if (total >= (1 << 16)) + { + split_points.push (i); + // split does not include i, so add the size for i when we reset the size counters. + accumulated = base_size + accumulated_delta; + coverage_size = 4 + estimator.incremental_coverage_size (i); + class_def_1_size = 4 + estimator.incremental_class_def_size (i); + visited.clear (); // node sharing isn't allowed between splits. + } + } + + split_context_t split_context { + c, + this, + c.graph.duplicate_if_shared (parent_index, this_index), + class1_record_size, + total_value_len, + value_1_len, + value_2_len, + max_coverage_size, + max_class_def_1_size, + device_tables, + format1_device_table_indices, + format2_device_table_indices + }; + + return actuate_subtable_split (split_context, split_points); + } + private: + + struct split_context_t + { + gsubgpos_graph_context_t& c; + PairPosFormat2* thiz; + unsigned this_index; + unsigned class1_record_size; + unsigned value_record_len; + unsigned value1_record_len; + unsigned value2_record_len; + unsigned max_coverage_size; + unsigned max_class_def_size; + + const hb_hashmap_t& device_tables; + const hb_vector_t& format1_device_table_indices; + const hb_vector_t& format2_device_table_indices; + + unsigned original_count () + { + return thiz->class1Count; + } + + unsigned clone_range (unsigned start, unsigned end) + { + return thiz->clone_range (*this, start, end); + } + + bool shrink (unsigned count) + { + return thiz->shrink (*this, count); + } + }; + + size_t get_class1_record_size () const + { + const size_t class2_count = class2Count; + return + class2_count * (valueFormat1.get_size () + valueFormat2.get_size ()); + } + + unsigned clone_range (split_context_t& split_context, + unsigned start, unsigned end) const + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Cloning PairPosFormat2 (%u) range [%u, %u).", split_context.this_index, start, end); + + graph_t& graph = split_context.c.graph; + + unsigned num_records = end - start; + unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size + + num_records * split_context.class1_record_size; + + unsigned pair_pos_prime_id = split_context.c.create_node (prime_size); + if (pair_pos_prime_id == (unsigned) -1) return -1; + + PairPosFormat2* pair_pos_prime = + (PairPosFormat2*) graph.object (pair_pos_prime_id).head; + pair_pos_prime->format = this->format; + pair_pos_prime->valueFormat1 = this->valueFormat1; + pair_pos_prime->valueFormat2 = this->valueFormat2; + pair_pos_prime->class1Count = num_records; + pair_pos_prime->class2Count = this->class2Count; + clone_class1_records (split_context, + pair_pos_prime_id, + start, + end); + + unsigned coverage_id = + graph.index_for_offset (split_context.this_index, &coverage); + unsigned class_def_1_id = + graph.index_for_offset (split_context.this_index, &classDef1); + auto& coverage_v = graph.vertices_[coverage_id]; + auto& class_def_1_v = graph.vertices_[class_def_1_id]; + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head; + if (!coverage_table + || !coverage_table->sanitize (coverage_v) + || !class_def_1_table + || !class_def_1_table->sanitize (class_def_1_v)) + return -1; + + auto klass_map = + + coverage_table->iter () + | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { + return hb_pair_t (gid, class_def_1_table->get_class (gid)); + }) + | hb_filter ([&] (hb_codepoint_t klass) { + return klass >= start && klass < end; + }, hb_second) + | hb_map_retains_sorting ([&] (hb_pair_t gid_and_class) { + // Classes must be from 0...N so subtract start + return hb_pair_t (gid_and_class.first, gid_and_class.second - start); + }) + ; + + if (!Coverage::add_coverage (split_context.c, + pair_pos_prime_id, + 2, + + klass_map | hb_map_retains_sorting (hb_first), + split_context.max_coverage_size)) + return -1; + + // classDef1 + if (!ClassDef::add_class_def (split_context.c, + pair_pos_prime_id, + 8, + + klass_map, + split_context.max_class_def_size)) + return -1; + + // classDef2 + unsigned class_def_2_id = + graph.index_for_offset (split_context.this_index, &classDef2); + auto* class_def_link = graph.vertices_[pair_pos_prime_id].obj.real_links.push (); + class_def_link->width = SmallTypes::size; + class_def_link->objidx = class_def_2_id; + class_def_link->position = 10; + graph.vertices_[class_def_2_id].parents.push (pair_pos_prime_id); + graph.duplicate (pair_pos_prime_id, class_def_2_id); + + return pair_pos_prime_id; + } + + void clone_class1_records (split_context_t& split_context, + unsigned pair_pos_prime_id, + unsigned start, unsigned end) const + { + PairPosFormat2* pair_pos_prime = + (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head; + + char* start_addr = ((char*)&values[0]) + start * split_context.class1_record_size; + unsigned num_records = end - start; + hb_memcpy (&pair_pos_prime->values[0], + start_addr, + num_records * split_context.class1_record_size); + + if (!split_context.format1_device_table_indices + && !split_context.format2_device_table_indices) + // No device tables to move over. + return; + + unsigned class2_count = class2Count; + for (unsigned i = start; i < end; i++) + { + for (unsigned j = 0; j < class2_count; j++) + { + unsigned value1_index = split_context.value_record_len * (class2_count * i + j); + unsigned value2_index = value1_index + split_context.value1_record_len; + + unsigned new_value1_index = split_context.value_record_len * (class2_count * (i - start) + j); + unsigned new_value2_index = new_value1_index + split_context.value1_record_len; + + transfer_device_tables (split_context, + pair_pos_prime_id, + split_context.format1_device_table_indices, + value1_index, + new_value1_index); + + transfer_device_tables (split_context, + pair_pos_prime_id, + split_context.format2_device_table_indices, + value2_index, + new_value2_index); + } + } + } + + void transfer_device_tables (split_context_t& split_context, + unsigned pair_pos_prime_id, + const hb_vector_t& device_table_indices, + unsigned old_value_record_index, + unsigned new_value_record_index) const + { + PairPosFormat2* pair_pos_prime = + (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head; + + for (unsigned i : device_table_indices) + { + OT::Offset16* record = (OT::Offset16*) &values[old_value_record_index + i]; + unsigned record_position = ((char*) record) - ((char*) this); + if (!split_context.device_tables.has (record_position)) continue; + + split_context.c.graph.move_child ( + split_context.this_index, + record, + pair_pos_prime_id, + (OT::Offset16*) &pair_pos_prime->values[new_value_record_index + i]); + } + } + + bool shrink (split_context_t& split_context, + unsigned count) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Shrinking PairPosFormat2 (%u) to [0, %u).", + split_context.this_index, + count); + unsigned old_count = class1Count; + if (count >= old_count) + return true; + + graph_t& graph = split_context.c.graph; + class1Count = count; + graph.vertices_[split_context.this_index].obj.tail -= + (old_count - count) * split_context.class1_record_size; + + auto coverage = + graph.as_mutable_table (split_context.this_index, &this->coverage); + if (!coverage) return false; + + auto class_def_1 = + graph.as_mutable_table (split_context.this_index, &classDef1); + if (!class_def_1) return false; + + auto klass_map = + + coverage.table->iter () + | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { + return hb_pair_t (gid, class_def_1.table->get_class (gid)); + }) + | hb_filter ([&] (hb_codepoint_t klass) { + return klass < count; + }, hb_second) + ; + + auto new_coverage = + klass_map | hb_map_retains_sorting (hb_first); + if (!Coverage::make_coverage (split_context.c, + + new_coverage, + coverage.index, + // existing ranges my not be kept, worst case size is a format 1 + // coverage table. + 4 + new_coverage.len() * 2)) + return false; + + return ClassDef::make_class_def (split_context.c, + + klass_map, + class_def_1.index, + class_def_1.vertex->table_size ()); + } + + hb_hashmap_t + get_all_device_tables (gsubgpos_graph_context_t& c, + unsigned this_index) const + { + const auto& v = c.graph.vertices_[this_index]; + return v.position_to_index_map (); + } + + const Coverage* get_coverage (gsubgpos_graph_context_t& c, + unsigned this_index) const + { + unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); + auto& coverage_v = c.graph.vertices_[coverage_id]; + + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + if (!coverage_table || !coverage_table->sanitize (coverage_v)) + return &Null(Coverage); + return coverage_table; + } + + const ClassDef* get_class_def_1 (gsubgpos_graph_context_t& c, + unsigned this_index) const + { + unsigned class_def_1_id = c.graph.index_for_offset (this_index, &classDef1); + auto& class_def_1_v = c.graph.vertices_[class_def_1_id]; + + ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head; + if (!class_def_1_table || !class_def_1_table->sanitize (class_def_1_v)) + return &Null(ClassDef); + return class_def_1_table; + } + + unsigned size_of_value_record_children (gsubgpos_graph_context_t& c, + const hb_hashmap_t& device_tables, + const hb_vector_t device_table_indices, + unsigned value_record_index, + hb_set_t& visited) + { + unsigned size = 0; + for (unsigned i : device_table_indices) + { + OT::Layout::GPOS_impl::Value* record = &values[value_record_index + i]; + unsigned record_position = ((char*) record) - ((char*) this); + unsigned* obj_idx; + if (!device_tables.has (record_position, &obj_idx)) continue; + size += c.graph.find_subgraph_size (*obj_idx, visited); + } + return size; + } + + unsigned size_of (gsubgpos_graph_context_t& c, + unsigned this_index, + const void* offset) const + { + const unsigned id = c.graph.index_for_offset (this_index, offset); + return c.graph.vertices_[id].table_size (); + } +}; + +struct PairPos : public OT::Layout::GPOS_impl::PairPos +{ + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + switch (u.format) { + case 1: + return ((PairPosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); + case 2: + return ((PairPosFormat2*)(&u.format2))->split_subtables (c, parent_index, this_index); +#ifndef HB_NO_BEYOND_64K + case 3: HB_FALLTHROUGH; + case 4: HB_FALLTHROUGH; + // Don't split 24bit PairPos's. +#endif + default: + return hb_vector_t (); + } + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < u.format.get_size ()) return false; + + switch (u.format) { + case 1: + return ((PairPosFormat1*)(&u.format1))->sanitize (vertex); + case 2: + return ((PairPosFormat2*)(&u.format2))->sanitize (vertex); +#ifndef HB_NO_BEYOND_64K + case 3: HB_FALLTHROUGH; + case 4: HB_FALLTHROUGH; +#endif + default: + // We don't handle format 3 and 4 here. + return false; + } + } +}; + +} + +#endif // GRAPH_PAIRPOS_GRAPH_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/serialize.hh b/src/3rdparty/harfbuzz-ng/src/graph/serialize.hh new file mode 100644 index 00000000000..040fd1de5fd --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/serialize.hh @@ -0,0 +1,270 @@ +/* + * Copyright ยฉ 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#ifndef GRAPH_SERIALIZE_HH +#define GRAPH_SERIALIZE_HH + +namespace graph { + +struct overflow_record_t +{ + unsigned parent; + unsigned child; + + bool operator != (const overflow_record_t o) const + { return !(*this == o); } + + inline bool operator == (const overflow_record_t& o) const + { + return parent == o.parent && + child == o.child; + } + + inline uint32_t hash () const + { + uint32_t current = 0; + current = current * 31 + hb_hash (parent); + current = current * 31 + hb_hash (child); + return current; + } +}; + +inline +int64_t compute_offset ( + const graph_t& graph, + unsigned parent_idx, + const hb_serialize_context_t::object_t::link_t& link) +{ + const auto& parent = graph.vertices_[parent_idx]; + const auto& child = graph.vertices_[link.objidx]; + int64_t offset = 0; + switch ((hb_serialize_context_t::whence_t) link.whence) { + case hb_serialize_context_t::whence_t::Head: + offset = child.start - parent.start; break; + case hb_serialize_context_t::whence_t::Tail: + offset = child.start - parent.end; break; + case hb_serialize_context_t::whence_t::Absolute: + offset = child.start; break; + } + + assert (offset >= link.bias); + offset -= link.bias; + return offset; +} + +inline +bool is_valid_offset (int64_t offset, + const hb_serialize_context_t::object_t::link_t& link) +{ + if (unlikely (!link.width)) + // Virtual links can't overflow. + return link.is_signed || offset >= 0; + + if (link.is_signed) + { + if (link.width == 4) + return offset >= -((int64_t) 1 << 31) && offset < ((int64_t) 1 << 31); + else + return offset >= -(1 << 15) && offset < (1 << 15); + } + else + { + if (link.width == 4) + return offset >= 0 && offset < ((int64_t) 1 << 32); + else if (link.width == 3) + return offset >= 0 && offset < ((int32_t) 1 << 24); + else + return offset >= 0 && offset < (1 << 16); + } +} + +/* + * Will any offsets overflow on graph when it's serialized? + */ +inline bool +will_overflow (graph_t& graph, + hb_vector_t* overflows = nullptr) +{ + if (overflows) overflows->resize (0); + graph.update_positions (); + + hb_hashmap_t record_set; + const auto& vertices = graph.vertices_; + for (int parent_idx = vertices.length - 1; parent_idx >= 0; parent_idx--) + { + // Don't need to check virtual links for overflow + for (const auto& link : vertices[parent_idx].obj.real_links) + { + int64_t offset = compute_offset (graph, parent_idx, link); + if (is_valid_offset (offset, link)) + continue; + + if (!overflows) return true; + + overflow_record_t r; + r.parent = parent_idx; + r.child = link.objidx; + if (record_set.has(&r)) continue; // don't keep duplicate overflows. + + overflows->push (r); + record_set.set(&r, true); + } + } + + if (!overflows) return false; + return overflows->length; +} + +inline +void print_overflows (graph_t& graph, + const hb_vector_t& overflows) +{ + if (!DEBUG_ENABLED(SUBSET_REPACK)) return; + + graph.update_parents (); + int limit = 10; + for (const auto& o : overflows) + { + if (!limit--) break; + const auto& parent = graph.vertices_[o.parent]; + const auto& child = graph.vertices_[o.child]; + DEBUG_MSG (SUBSET_REPACK, nullptr, + " overflow from " + "%4u (%4u in, %4u out, space %2u) => " + "%4u (%4u in, %4u out, space %2u)", + o.parent, + parent.incoming_edges (), + parent.obj.real_links.length + parent.obj.virtual_links.length, + graph.space_for (o.parent), + o.child, + child.incoming_edges (), + child.obj.real_links.length + child.obj.virtual_links.length, + graph.space_for (o.child)); + } + if (overflows.length > 10) { + DEBUG_MSG (SUBSET_REPACK, nullptr, " ... plus %u more overflows.", overflows.length - 10); + } +} + +template inline void +serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link, + char* head, + hb_serialize_context_t* c) +{ + OT::Offset* offset = reinterpret_cast*> (head + link.position); + *offset = 0; + c->add_link (*offset, + // serializer has an extra nil object at the start of the + // object array. So all id's are +1 of what our id's are. + link.objidx + 1, + (hb_serialize_context_t::whence_t) link.whence, + link.bias); +} + +inline +void serialize_link (const hb_serialize_context_t::object_t::link_t& link, + char* head, + hb_serialize_context_t* c) +{ + switch (link.width) + { + case 0: + // Virtual links aren't serialized. + return; + case 4: + if (link.is_signed) + { + serialize_link_of_type (link, head, c); + } else { + serialize_link_of_type (link, head, c); + } + return; + case 2: + if (link.is_signed) + { + serialize_link_of_type (link, head, c); + } else { + serialize_link_of_type (link, head, c); + } + return; + case 3: + serialize_link_of_type (link, head, c); + return; + default: + // Unexpected link width. + assert (0); + } +} + +/* + * serialize graph into the provided serialization buffer. + */ +inline hb_blob_t* serialize (const graph_t& graph) +{ + hb_vector_t buffer; + size_t size = graph.total_size_in_bytes (); + if (!buffer.alloc (size)) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Unable to allocate output buffer."); + return nullptr; + } + hb_serialize_context_t c((void *) buffer, size); + + c.start_serialize (); + const auto& vertices = graph.vertices_; + for (unsigned i = 0; i < vertices.length; i++) { + c.push (); + + size_t size = vertices[i].obj.tail - vertices[i].obj.head; + char* start = c.allocate_size (size); + if (!start) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Buffer out of space."); + return nullptr; + } + + hb_memcpy (start, vertices[i].obj.head, size); + + // Only real links needs to be serialized. + for (const auto& link : vertices[i].obj.real_links) + serialize_link (link, start, &c); + + // All duplications are already encoded in the graph, so don't + // enable sharing during packing. + c.pop_pack (false); + } + c.end_serialize (); + + if (c.in_error ()) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Error during serialization. Err flag: %d", + c.errors); + return nullptr; + } + + return c.copy_blob (); +} + +} // namespace graph + +#endif // GRAPH_SERIALIZE_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/split-helpers.hh b/src/3rdparty/harfbuzz-ng/src/graph/split-helpers.hh new file mode 100644 index 00000000000..61fd7c2d2ff --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/split-helpers.hh @@ -0,0 +1,69 @@ +/* + * Copyright ยฉ 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#ifndef GRAPH_SPLIT_HELPERS_HH +#define GRAPH_SPLIT_HELPERS_HH + +namespace graph { + +template +HB_INTERNAL +hb_vector_t actuate_subtable_split (Context& split_context, + const hb_vector_t& split_points) +{ + hb_vector_t new_objects; + if (!split_points) + return new_objects; + + for (unsigned i = 0; i < split_points.length; i++) + { + unsigned start = split_points[i]; + unsigned end = (i < split_points.length - 1) + ? split_points[i + 1] + : split_context.original_count (); + unsigned id = split_context.clone_range (start, end); + + if (id == (unsigned) -1) + { + new_objects.reset (); + new_objects.allocated = -1; // mark error + return new_objects; + } + new_objects.push (id); + } + + if (!split_context.shrink (split_points[0])) + { + new_objects.reset (); + new_objects.allocated = -1; // mark error + } + + return new_objects; +} + +} + +#endif // GRAPH_SPLIT_HELPERS_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/test-classdef-graph.cc b/src/3rdparty/harfbuzz-ng/src/graph/test-classdef-graph.cc new file mode 100644 index 00000000000..55854ff5c29 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/test-classdef-graph.cc @@ -0,0 +1,119 @@ +/* + * Copyright ยฉ 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#include "gsubgpos-context.hh" +#include "classdef-graph.hh" + +typedef hb_pair_t gid_and_class_t; +typedef hb_vector_t gid_and_class_list_t; + + +static bool incremental_size_is (const gid_and_class_list_t& list, unsigned klass, + unsigned cov_expected, unsigned class_def_expected) +{ + graph::class_def_size_estimator_t estimator (list.iter ()); + + unsigned result = estimator.incremental_coverage_size (klass); + if (result != cov_expected) + { + printf ("FAIL: coverage expected size %u but was %u\n", cov_expected, result); + return false; + } + + result = estimator.incremental_class_def_size (klass); + if (result != class_def_expected) + { + printf ("FAIL: class def expected size %u but was %u\n", class_def_expected, result); + return false; + } + + return true; +} + +static void test_class_and_coverage_size_estimates () +{ + gid_and_class_list_t empty = { + }; + assert (incremental_size_is (empty, 0, 0, 0)); + assert (incremental_size_is (empty, 1, 0, 0)); + + gid_and_class_list_t class_zero = { + {5, 0}, + }; + assert (incremental_size_is (class_zero, 0, 2, 0)); + + gid_and_class_list_t consecutive = { + {4, 0}, + {5, 0}, + {6, 1}, + {7, 1}, + {8, 2}, + {9, 2}, + {10, 2}, + {11, 2}, + }; + assert (incremental_size_is (consecutive, 0, 4, 0)); + assert (incremental_size_is (consecutive, 1, 4, 4)); + assert (incremental_size_is (consecutive, 2, 8, 6)); + + gid_and_class_list_t non_consecutive = { + {4, 0}, + {5, 0}, + + {6, 1}, + {7, 1}, + + {9, 2}, + {10, 2}, + {11, 2}, + {12, 2}, + }; + assert (incremental_size_is (non_consecutive, 0, 4, 0)); + assert (incremental_size_is (non_consecutive, 1, 4, 6)); + assert (incremental_size_is (non_consecutive, 2, 8, 6)); + + gid_and_class_list_t multiple_ranges = { + {4, 0}, + {5, 0}, + + {6, 1}, + {7, 1}, + + {9, 1}, + + {11, 1}, + {12, 1}, + {13, 1}, + }; + assert (incremental_size_is (multiple_ranges, 0, 4, 0)); + assert (incremental_size_is (multiple_ranges, 1, 2 * 6, 3 * 6)); +} + +int +main (int argc, char **argv) +{ + test_class_and_coverage_size_estimates (); +} diff --git a/src/3rdparty/harfbuzz-ng/src/harfbuzz-subset.cc b/src/3rdparty/harfbuzz-ng/src/harfbuzz-subset.cc new file mode 100644 index 00000000000..c0e23b3eb82 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/harfbuzz-subset.cc @@ -0,0 +1,62 @@ +#include "graph/gsubgpos-context.cc" +#include "hb-aat-layout.cc" +#include "hb-aat-map.cc" +#include "hb-blob.cc" +#include "hb-buffer-serialize.cc" +#include "hb-buffer-verify.cc" +#include "hb-buffer.cc" +#include "hb-common.cc" +#include "hb-draw.cc" +#include "hb-face-builder.cc" +#include "hb-face.cc" +#include "hb-fallback-shape.cc" +#include "hb-font.cc" +#include "hb-map.cc" +#include "hb-number.cc" +#include "hb-ot-cff1-table.cc" +#include "hb-ot-cff2-table.cc" +#include "hb-ot-color.cc" +#include "hb-ot-face.cc" +#include "hb-ot-font.cc" +#include "hb-ot-layout.cc" +#include "hb-ot-map.cc" +#include "hb-ot-math.cc" +#include "hb-ot-meta.cc" +#include "hb-ot-metrics.cc" +#include "hb-ot-name.cc" +#include "hb-ot-shape-fallback.cc" +#include "hb-ot-shape-normalize.cc" +#include "hb-ot-shape.cc" +#include "hb-ot-shaper-arabic.cc" +#include "hb-ot-shaper-default.cc" +#include "hb-ot-shaper-hangul.cc" +#include "hb-ot-shaper-hebrew.cc" +#include "hb-ot-shaper-indic-table.cc" +#include "hb-ot-shaper-indic.cc" +#include "hb-ot-shaper-khmer.cc" +#include "hb-ot-shaper-myanmar.cc" +#include "hb-ot-shaper-syllabic.cc" +#include "hb-ot-shaper-thai.cc" +#include "hb-ot-shaper-use.cc" +#include "hb-ot-shaper-vowel-constraints.cc" +#include "hb-ot-tag.cc" +#include "hb-ot-var.cc" +#include "hb-outline.cc" +#include "hb-paint-extents.cc" +#include "hb-paint.cc" +#include "hb-set.cc" +#include "hb-shape-plan.cc" +#include "hb-shape.cc" +#include "hb-shaper.cc" +#include "hb-static.cc" +#include "hb-style.cc" +#include "hb-subset-cff-common.cc" +#include "hb-subset-cff1.cc" +#include "hb-subset-cff2.cc" +#include "hb-subset-input.cc" +#include "hb-subset-instancer-solver.cc" +#include "hb-subset-plan.cc" +#include "hb-subset-repacker.cc" +#include "hb-subset.cc" +#include "hb-ucd.cc" +#include "hb-unicode.cc" diff --git a/src/3rdparty/harfbuzz-ng/src/harfbuzz.cc b/src/3rdparty/harfbuzz-ng/src/harfbuzz.cc new file mode 100644 index 00000000000..d7e8a93f393 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/harfbuzz.cc @@ -0,0 +1,60 @@ +#include "hb-aat-layout.cc" +#include "hb-aat-map.cc" +#include "hb-blob.cc" +#include "hb-buffer-serialize.cc" +#include "hb-buffer-verify.cc" +#include "hb-buffer.cc" +#include "hb-common.cc" +#include "hb-coretext.cc" +#include "hb-directwrite.cc" +#include "hb-draw.cc" +#include "hb-face-builder.cc" +#include "hb-face.cc" +#include "hb-fallback-shape.cc" +#include "hb-font.cc" +#include "hb-ft.cc" +#include "hb-gdi.cc" +#include "hb-glib.cc" +#include "hb-graphite2.cc" +#include "hb-map.cc" +#include "hb-number.cc" +#include "hb-ot-cff1-table.cc" +#include "hb-ot-cff2-table.cc" +#include "hb-ot-color.cc" +#include "hb-ot-face.cc" +#include "hb-ot-font.cc" +#include "hb-ot-layout.cc" +#include "hb-ot-map.cc" +#include "hb-ot-math.cc" +#include "hb-ot-meta.cc" +#include "hb-ot-metrics.cc" +#include "hb-ot-name.cc" +#include "hb-ot-shape-fallback.cc" +#include "hb-ot-shape-normalize.cc" +#include "hb-ot-shape.cc" +#include "hb-ot-shaper-arabic.cc" +#include "hb-ot-shaper-default.cc" +#include "hb-ot-shaper-hangul.cc" +#include "hb-ot-shaper-hebrew.cc" +#include "hb-ot-shaper-indic-table.cc" +#include "hb-ot-shaper-indic.cc" +#include "hb-ot-shaper-khmer.cc" +#include "hb-ot-shaper-myanmar.cc" +#include "hb-ot-shaper-syllabic.cc" +#include "hb-ot-shaper-thai.cc" +#include "hb-ot-shaper-use.cc" +#include "hb-ot-shaper-vowel-constraints.cc" +#include "hb-ot-tag.cc" +#include "hb-ot-var.cc" +#include "hb-outline.cc" +#include "hb-paint-extents.cc" +#include "hb-paint.cc" +#include "hb-set.cc" +#include "hb-shape-plan.cc" +#include "hb-shape.cc" +#include "hb-shaper.cc" +#include "hb-static.cc" +#include "hb-style.cc" +#include "hb-ucd.cc" +#include "hb-unicode.cc" +#include "hb-uniscribe.cc" diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-ankr-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-ankr-table.hh new file mode 100644 index 00000000000..63fac84524f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-ankr-table.hh @@ -0,0 +1,98 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_LAYOUT_ANKR_TABLE_HH +#define HB_AAT_LAYOUT_ANKR_TABLE_HH + +#include "hb-aat-layout-common.hh" + +/* + * ankr -- Anchor Point + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ankr.html + */ +#define HB_AAT_TAG_ankr HB_TAG('a','n','k','r') + + +namespace AAT { + +using namespace OT; + + +struct Anchor +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + FWORD xCoordinate; + FWORD yCoordinate; + public: + DEFINE_SIZE_STATIC (4); +}; + +typedef Array32Of GlyphAnchors; + +struct ankr +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_ankr; + + const Anchor &get_anchor (hb_codepoint_t glyph_id, + unsigned int i, + unsigned int num_glyphs) const + { + const NNOffset16To *offset = (this+lookupTable).get_value (glyph_id, num_glyphs); + if (!offset) + return Null (Anchor); + const GlyphAnchors &anchors = &(this+anchorData) + *offset; + return anchors[i]; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version == 0 && + c->check_range (this, anchorData) && + lookupTable.sanitize (c, this, &(this+anchorData)))); + } + + protected: + HBUINT16 version; /* Version number (set to zero) */ + HBUINT16 flags; /* Flags (currently unused; set to zero) */ + Offset32To>> + lookupTable; /* Offset to the table's lookup table */ + NNOffset32To + anchorData; /* Offset to the glyph data table */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_ANKR_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-bsln-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-bsln-table.hh new file mode 100644 index 00000000000..bf12d2e699f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-bsln-table.hh @@ -0,0 +1,158 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_LAYOUT_BSLN_TABLE_HH +#define HB_AAT_LAYOUT_BSLN_TABLE_HH + +#include "hb-aat-layout-common.hh" + +/* + * bsln -- Baseline + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html + */ +#define HB_AAT_TAG_bsln HB_TAG('b','s','l','n') + + +namespace AAT { + + +struct BaselineTableFormat0Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + // Roman, Ideographic centered, Ideographic low, Hanging and Math + // are the default defined ones, but any other maybe accessed also. + HBINT16 deltas[32]; /* These are the FUnit distance deltas from + * the font's natural baseline to the other + * baselines used in the font. */ + public: + DEFINE_SIZE_STATIC (64); +}; + +struct BaselineTableFormat1Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + lookupTable.sanitize (c))); + } + + protected: + HBINT16 deltas[32]; /* ditto */ + Lookup + lookupTable; /* Lookup table that maps glyphs to their + * baseline values. */ + public: + DEFINE_SIZE_MIN (66); +}; + +struct BaselineTableFormat2Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBGlyphID16 stdGlyph; /* The specific glyph index number in this + * font that is used to set the baseline values. + * This is the standard glyph. + * This glyph must contain a set of control points + * (whose numbers are contained in the ctlPoints field) + * that are used to determine baseline distances. */ + HBUINT16 ctlPoints[32]; /* Set of control point numbers, + * associated with the standard glyph. + * A value of 0xFFFF means there is no corresponding + * control point in the standard glyph. */ + public: + DEFINE_SIZE_STATIC (66); +}; + +struct BaselineTableFormat3Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c))); + } + + protected: + HBGlyphID16 stdGlyph; /* ditto */ + HBUINT16 ctlPoints[32]; /* ditto */ + Lookup + lookupTable; /* Lookup table that maps glyphs to their + * baseline values. */ + public: + DEFINE_SIZE_MIN (68); +}; + +struct bsln +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_bsln; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this) && defaultBaseline < 32))) + return_trace (false); + + switch (format) + { + case 0: return_trace (parts.format0.sanitize (c)); + case 1: return_trace (parts.format1.sanitize (c)); + case 2: return_trace (parts.format2.sanitize (c)); + case 3: return_trace (parts.format3.sanitize (c)); + default:return_trace (true); + } + } + + protected: + FixedVersion<>version; /* Version number of the Baseline table. */ + HBUINT16 format; /* Format of the baseline table. Only one baseline + * format may be selected for the font. */ + HBUINT16 defaultBaseline;/* Default baseline value for all glyphs. + * This value can be from 0 through 31. */ + union { + // Distance-Based Formats + BaselineTableFormat0Part format0; + BaselineTableFormat1Part format1; + // Control Point-based Formats + BaselineTableFormat2Part format2; + BaselineTableFormat3Part format3; + } parts; + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_BSLN_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-common.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-common.hh new file mode 100644 index 00000000000..7d53c354dab --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-common.hh @@ -0,0 +1,919 @@ +/* + * Copyright ยฉ 2017 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_COMMON_HH +#define HB_AAT_LAYOUT_COMMON_HH + +#include "hb-aat-layout.hh" +#include "hb-aat-map.hh" +#include "hb-open-type.hh" + +namespace OT { +struct GDEF; +}; + +namespace AAT { + +using namespace OT; + + +struct ankr; + +struct hb_aat_apply_context_t : + hb_dispatch_context_t +{ + const char *get_name () { return "APPLY"; } + template + return_t dispatch (const T &obj) { return obj.apply (this); } + static return_t default_return_value () { return false; } + bool stop_sublookup_iteration (return_t r) const { return r; } + + const hb_ot_shape_plan_t *plan; + hb_font_t *font; + hb_face_t *face; + hb_buffer_t *buffer; + hb_sanitize_context_t sanitizer; + const ankr *ankr_table; + const OT::GDEF *gdef_table; + const hb_sorted_vector_t *range_flags = nullptr; + hb_mask_t subtable_flags = 0; + + /* Unused. For debug tracing only. */ + unsigned int lookup_index; + + HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_, + hb_font_t *font_, + hb_buffer_t *buffer_, + hb_blob_t *blob = const_cast (&Null (hb_blob_t))); + + HB_INTERNAL ~hb_aat_apply_context_t (); + + HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_); + + void set_lookup_index (unsigned int i) { lookup_index = i; } +}; + + +/* + * Lookup Table + */ + +template struct Lookup; + +template +struct LookupFormat0 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + if (unlikely (glyph_id >= num_glyphs)) return nullptr; + return &arrayZ[glyph_id]; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (arrayZ.sanitize (c, c->get_num_glyphs ())); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (arrayZ.sanitize (c, c->get_num_glyphs (), base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 0 */ + UnsizedArrayOf + arrayZ; /* Array of lookup values, indexed by glyph index. */ + public: + DEFINE_SIZE_UNBOUNDED (2); +}; + + +template +struct LookupSegmentSingle +{ + static constexpr unsigned TerminationWordCount = 2u; + + int cmp (hb_codepoint_t g) const + { return g < first ? -1 : g <= last ? 0 : +1 ; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c, base)); + } + + HBGlyphID16 last; /* Last GlyphID in this segment */ + HBGlyphID16 first; /* First GlyphID in this segment */ + T value; /* The lookup value (only one) */ + public: + DEFINE_SIZE_STATIC (4 + T::static_size); +}; + +template +struct LookupFormat2 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSegmentSingle *v = segments.bsearch (glyph_id); + return v ? &v->value : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + VarSizedBinSearchArrayOf> + segments; /* The actual segments. These must already be sorted, + * according to the first word in each one (the last + * glyph in each segment). */ + public: + DEFINE_SIZE_ARRAY (8, segments); +}; + +template +struct LookupSegmentArray +{ + static constexpr unsigned TerminationWordCount = 2u; + + const T* get_value (hb_codepoint_t glyph_id, const void *base) const + { + return first <= glyph_id && glyph_id <= last ? &(base+valuesZ)[glyph_id - first] : nullptr; + } + + int cmp (hb_codepoint_t g) const + { return g < first ? -1 : g <= last ? 0 : +1; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + first <= last && + valuesZ.sanitize (c, base, last - first + 1)); + } + template + bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + first <= last && + valuesZ.sanitize (c, base, last - first + 1, std::forward (ds)...)); + } + + HBGlyphID16 last; /* Last GlyphID in this segment */ + HBGlyphID16 first; /* First GlyphID in this segment */ + NNOffset16To> + valuesZ; /* A 16-bit offset from the start of + * the table to the data. */ + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct LookupFormat4 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSegmentArray *v = segments.bsearch (glyph_id); + return v ? v->get_value (glyph_id, this) : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c, this)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c, this, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 4 */ + VarSizedBinSearchArrayOf> + segments; /* The actual segments. These must already be sorted, + * according to the first word in each one (the last + * glyph in each segment). */ + public: + DEFINE_SIZE_ARRAY (8, segments); +}; + +template +struct LookupSingle +{ + static constexpr unsigned TerminationWordCount = 1u; + + int cmp (hb_codepoint_t g) const { return glyph.cmp (g); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c, base)); + } + + HBGlyphID16 glyph; /* Last GlyphID */ + T value; /* The lookup value (only one) */ + public: + DEFINE_SIZE_STATIC (2 + T::static_size); +}; + +template +struct LookupFormat6 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSingle *v = entries.bsearch (glyph_id); + return v ? &v->value : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (entries.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (entries.sanitize (c, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 6 */ + VarSizedBinSearchArrayOf> + entries; /* The actual entries, sorted by glyph index. */ + public: + DEFINE_SIZE_ARRAY (8, entries); +}; + +template +struct LookupFormat8 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + return firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount ? + &valueArrayZ[glyph_id - firstGlyph] : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 8 */ + HBGlyphID16 firstGlyph; /* First glyph index included in the trimmed array. */ + HBUINT16 glyphCount; /* Total number of glyphs (equivalent to the last + * glyph minus the value of firstGlyph plus 1). */ + UnsizedArrayOf + valueArrayZ; /* The lookup values (indexed by the glyph index + * minus the value of firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (6, valueArrayZ); +}; + +template +struct LookupFormat10 +{ + friend struct Lookup; + + private: + const typename T::type get_value_or_null (hb_codepoint_t glyph_id) const + { + if (!(firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount)) + return Null (T); + + const HBUINT8 *p = &valueArrayZ[(glyph_id - firstGlyph) * valueSize]; + + unsigned int v = 0; + unsigned int count = valueSize; + for (unsigned int i = 0; i < count; i++) + v = (v << 8) | *p++; + + return v; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + valueSize <= 4 && + valueArrayZ.sanitize (c, glyphCount * valueSize)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 8 */ + HBUINT16 valueSize; /* Byte size of each value. */ + HBGlyphID16 firstGlyph; /* First glyph index included in the trimmed array. */ + HBUINT16 glyphCount; /* Total number of glyphs (equivalent to the last + * glyph minus the value of firstGlyph plus 1). */ + UnsizedArrayOf + valueArrayZ; /* The lookup values (indexed by the glyph index + * minus the value of firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (8, valueArrayZ); +}; + +template +struct Lookup +{ + const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + switch (u.format) { + case 0: return u.format0.get_value (glyph_id, num_glyphs); + case 2: return u.format2.get_value (glyph_id); + case 4: return u.format4.get_value (glyph_id); + case 6: return u.format6.get_value (glyph_id); + case 8: return u.format8.get_value (glyph_id); + default:return nullptr; + } + } + + const typename T::type get_value_or_null (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + switch (u.format) { + /* Format 10 cannot return a pointer. */ + case 10: return u.format10.get_value_or_null (glyph_id); + default: + const T *v = get_value (glyph_id, num_glyphs); + return v ? *v : Null (T); + } + } + + typename T::type get_class (hb_codepoint_t glyph_id, + unsigned int num_glyphs, + unsigned int outOfRange) const + { + const T *v = get_value (glyph_id, num_glyphs); + return v ? *v : outOfRange; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 0: return_trace (u.format0.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 4: return_trace (u.format4.sanitize (c)); + case 6: return_trace (u.format6.sanitize (c)); + case 8: return_trace (u.format8.sanitize (c)); + case 10: return_trace (u.format10.sanitize (c)); + default:return_trace (true); + } + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 0: return_trace (u.format0.sanitize (c, base)); + case 2: return_trace (u.format2.sanitize (c, base)); + case 4: return_trace (u.format4.sanitize (c, base)); + case 6: return_trace (u.format6.sanitize (c, base)); + case 8: return_trace (u.format8.sanitize (c, base)); + case 10: return_trace (false); /* We don't support format10 here currently. */ + default:return_trace (true); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + LookupFormat0 format0; + LookupFormat2 format2; + LookupFormat4 format4; + LookupFormat6 format6; + LookupFormat8 format8; + LookupFormat10 format10; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; +DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2); + +enum { DELETED_GLYPH = 0xFFFF }; + +/* + * (Extended) State Table + */ + +template +struct Entry +{ + // This does seem like it's ever called. + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* Note, we don't recurse-sanitize data because we don't access it. + * That said, in our DEFINE_SIZE_STATIC we access T::static_size, + * which ensures that data has a simple sanitize(). To be determined + * if I need to remove that as well. + * + * HOWEVER! Because we are a template, our DEFINE_SIZE_STATIC + * assertion wouldn't be checked, hence the line below. */ + static_assert (T::static_size, ""); + + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 newState; /* Byte offset from beginning of state table + * to the new state. Really?!?! Or just state + * number? The latter in morx for sure. */ + HBUINT16 flags; /* Table specific. */ + T data; /* Optional offsets to per-glyph tables. */ + public: + DEFINE_SIZE_STATIC (4 + T::static_size); +}; + +template <> +struct Entry +{ + // This does seem like it's ever called. + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 newState; /* Byte offset from beginning of state table to the new state. */ + HBUINT16 flags; /* Table specific. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +template +struct StateTable +{ + typedef typename Types::HBUINT HBUINT; + typedef typename Types::HBUSHORT HBUSHORT; + typedef typename Types::ClassTypeNarrow ClassType; + + enum State + { + STATE_START_OF_TEXT = 0, + STATE_START_OF_LINE = 1, + }; + enum Class + { + CLASS_END_OF_TEXT = 0, + CLASS_OUT_OF_BOUNDS = 1, + CLASS_DELETED_GLYPH = 2, + CLASS_END_OF_LINE = 3, + }; + + int new_state (unsigned int newState) const + { return Types::extended ? newState : ((int) newState - (int) stateArrayTable) / (int) nClasses; } + + unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH; + return (this+classTable).get_class (glyph_id, num_glyphs, 1); + } + + const Entry *get_entries () const + { return (this+entryTable).arrayZ; } + + const Entry &get_entry (int state, unsigned int klass) const + { + if (unlikely (klass >= nClasses)) + klass = StateTable::CLASS_OUT_OF_BOUNDS; + + const HBUSHORT *states = (this+stateArrayTable).arrayZ; + const Entry *entries = (this+entryTable).arrayZ; + + unsigned int entry = states[state * nClasses + klass]; + DEBUG_MSG (APPLY, nullptr, "e%u", entry); + + return entries[entry]; + } + + bool sanitize (hb_sanitize_context_t *c, + unsigned int *num_entries_out = nullptr) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this) && + nClasses >= 4 /* Ensure pre-defined classes fit. */ && + classTable.sanitize (c, this)))) return_trace (false); + + const HBUSHORT *states = (this+stateArrayTable).arrayZ; + const Entry *entries = (this+entryTable).arrayZ; + + unsigned int num_classes = nClasses; + if (unlikely (hb_unsigned_mul_overflows (num_classes, states[0].static_size))) + return_trace (false); + unsigned int row_stride = num_classes * states[0].static_size; + + /* Apple 'kern' table has this peculiarity: + * + * "Because the stateTableOffset in the state table header is (strictly + * speaking) redundant, some 'kern' tables use it to record an initial + * state where that should not be StartOfText. To determine if this is + * done, calculate what the stateTableOffset should be. If it's different + * from the actual stateTableOffset, use it as the initial state." + * + * We implement this by calling the initial state zero, but allow *negative* + * states if the start state indeed was not the first state. Since the code + * is shared, this will also apply to 'mort' table. The 'kerx' / 'morx' + * tables are not affected since those address states by index, not offset. + */ + + int min_state = 0; + int max_state = 0; + unsigned int num_entries = 0; + + int state_pos = 0; + int state_neg = 0; + unsigned int entry = 0; + while (min_state < state_neg || state_pos <= max_state) + { + if (min_state < state_neg) + { + /* Negative states. */ + if (unlikely (hb_unsigned_mul_overflows (min_state, num_classes))) + return_trace (false); + if (unlikely (!c->check_range (&states[min_state * num_classes], + -min_state, + row_stride))) + return_trace (false); + if ((c->max_ops -= state_neg - min_state) <= 0) + return_trace (false); + { /* Sweep new states. */ + const HBUSHORT *stop = &states[min_state * num_classes]; + if (unlikely (stop > states)) + return_trace (false); + for (const HBUSHORT *p = states; stop < p; p--) + num_entries = hb_max (num_entries, *(p - 1) + 1u); + state_neg = min_state; + } + } + + if (state_pos <= max_state) + { + /* Positive states. */ + if (unlikely (!c->check_range (states, + max_state + 1, + row_stride))) + return_trace (false); + if ((c->max_ops -= max_state - state_pos + 1) <= 0) + return_trace (false); + { /* Sweep new states. */ + if (unlikely (hb_unsigned_mul_overflows ((max_state + 1), num_classes))) + return_trace (false); + const HBUSHORT *stop = &states[(max_state + 1) * num_classes]; + if (unlikely (stop < states)) + return_trace (false); + for (const HBUSHORT *p = &states[state_pos * num_classes]; p < stop; p++) + num_entries = hb_max (num_entries, *p + 1u); + state_pos = max_state + 1; + } + } + + if (unlikely (!c->check_array (entries, num_entries))) + return_trace (false); + if ((c->max_ops -= num_entries - entry) <= 0) + return_trace (false); + { /* Sweep new entries. */ + const Entry *stop = &entries[num_entries]; + for (const Entry *p = &entries[entry]; p < stop; p++) + { + int newState = new_state (p->newState); + min_state = hb_min (min_state, newState); + max_state = hb_max (max_state, newState); + } + entry = num_entries; + } + } + + if (num_entries_out) + *num_entries_out = num_entries; + + return_trace (true); + } + + protected: + HBUINT nClasses; /* Number of classes, which is the number of indices + * in a single line in the state array. */ + NNOffsetTo + classTable; /* Offset to the class table. */ + NNOffsetTo, HBUINT> + stateArrayTable;/* Offset to the state array. */ + NNOffsetTo>, HBUINT> + entryTable; /* Offset to the entry array. */ + + public: + DEFINE_SIZE_STATIC (4 * sizeof (HBUINT)); +}; + +template +struct ClassTable +{ + unsigned int get_class (hb_codepoint_t glyph_id, unsigned int outOfRange) const + { + unsigned int i = glyph_id - firstGlyph; + return i >= classArray.len ? outOfRange : classArray.arrayZ[i]; + } + unsigned int get_class (hb_codepoint_t glyph_id, + unsigned int num_glyphs HB_UNUSED, + unsigned int outOfRange) const + { + return get_class (glyph_id, outOfRange); + } + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && classArray.sanitize (c)); + } + protected: + HBGlyphID16 firstGlyph; /* First glyph index included in the trimmed array. */ + Array16Of classArray; /* The class codes (indexed by glyph index minus + * firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (4, classArray); +}; + +struct ObsoleteTypes +{ + static constexpr bool extended = false; + typedef HBUINT16 HBUINT; + typedef HBUINT8 HBUSHORT; + typedef ClassTable ClassTypeNarrow; + typedef ClassTable ClassTypeWide; + + template + static unsigned int offsetToIndex (unsigned int offset, + const void *base, + const T *array) + { + /* https://github.com/harfbuzz/harfbuzz/issues/3483 */ + /* If offset is less than base, return an offset that would + * result in an address half a 32bit address-space away, + * to make sure sanitize fails even on 32bit builds. */ + if (unlikely (offset < unsigned ((const char *) array - (const char *) base))) + return INT_MAX / T::static_size; + + /* https://github.com/harfbuzz/harfbuzz/issues/2816 */ + return (offset - unsigned ((const char *) array - (const char *) base)) / T::static_size; + } + template + static unsigned int byteOffsetToIndex (unsigned int offset, + const void *base, + const T *array) + { + return offsetToIndex (offset, base, array); + } + template + static unsigned int wordOffsetToIndex (unsigned int offset, + const void *base, + const T *array) + { + return offsetToIndex (2 * offset, base, array); + } +}; +struct ExtendedTypes +{ + static constexpr bool extended = true; + typedef HBUINT32 HBUINT; + typedef HBUINT16 HBUSHORT; + typedef Lookup ClassTypeNarrow; + typedef Lookup ClassTypeWide; + + template + static unsigned int offsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset; + } + template + static unsigned int byteOffsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset / 2; + } + template + static unsigned int wordOffsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset; + } +}; + +template +struct StateTableDriver +{ + using StateTableT = StateTable; + using EntryT = Entry; + + StateTableDriver (const StateTableT &machine_, + hb_buffer_t *buffer_, + hb_face_t *face_) : + machine (machine_), + buffer (buffer_), + num_glyphs (face_->get_num_glyphs ()) {} + + template + void drive (context_t *c, hb_aat_apply_context_t *ac) + { + if (!c->in_place) + buffer->clear_output (); + + int state = StateTableT::STATE_START_OF_TEXT; + // If there's only one range, we already checked the flag. + auto *last_range = ac->range_flags && (ac->range_flags->length > 1) ? &(*ac->range_flags)[0] : nullptr; + for (buffer->idx = 0; buffer->successful;) + { + /* This block is copied in NoncontextualSubtable::apply. Keep in sync. */ + if (last_range) + { + auto *range = last_range; + if (buffer->idx < buffer->len) + { + unsigned cluster = buffer->cur().cluster; + while (cluster < range->cluster_first) + range--; + while (cluster > range->cluster_last) + range++; + + + last_range = range; + } + if (!(range->flags & ac->subtable_flags)) + { + if (buffer->idx == buffer->len || unlikely (!buffer->successful)) + break; + + state = StateTableT::STATE_START_OF_TEXT; + (void) buffer->next_glyph (); + continue; + } + } + + unsigned int klass = buffer->idx < buffer->len ? + machine.get_class (buffer->cur().codepoint, num_glyphs) : + (unsigned) StateTableT::CLASS_END_OF_TEXT; + DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx); + const EntryT &entry = machine.get_entry (state, klass); + const int next_state = machine.new_state (entry.newState); + + /* Conditions under which it's guaranteed safe-to-break before current glyph: + * + * 1. There was no action in this transition; and + * + * 2. If we break before current glyph, the results will be the same. That + * is guaranteed if: + * + * 2a. We were already in start-of-text state; or + * + * 2b. We are epsilon-transitioning to start-of-text state; or + * + * 2c. Starting from start-of-text state seeing current glyph: + * + * 2c'. There won't be any actions; and + * + * 2c". We would end up in the same state that we were going to end up + * in now, including whether epsilon-transitioning. + * + * and + * + * 3. If we break before current glyph, there won't be any end-of-text action + * after previous glyph. + * + * This triples the transitions we need to look up, but is worth returning + * granular unsafe-to-break results. See eg.: + * + * https://github.com/harfbuzz/harfbuzz/issues/2860 + */ + const EntryT *wouldbe_entry; + bool safe_to_break = + /* 1. */ + !c->is_actionable (this, entry) + && + /* 2. */ + ( + /* 2a. */ + state == StateTableT::STATE_START_OF_TEXT + || + /* 2b. */ + ( + (entry.flags & context_t::DontAdvance) && + next_state == StateTableT::STATE_START_OF_TEXT + ) + || + /* 2c. */ + ( + wouldbe_entry = &machine.get_entry (StateTableT::STATE_START_OF_TEXT, klass) + , + /* 2c'. */ + !c->is_actionable (this, *wouldbe_entry) + && + /* 2c". */ + ( + next_state == machine.new_state (wouldbe_entry->newState) + && + (entry.flags & context_t::DontAdvance) == (wouldbe_entry->flags & context_t::DontAdvance) + ) + ) + ) + && + /* 3. */ + !c->is_actionable (this, machine.get_entry (state, StateTableT::CLASS_END_OF_TEXT)) + ; + + if (!safe_to_break && buffer->backtrack_len () && buffer->idx < buffer->len) + buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1); + + c->transition (this, entry); + + state = next_state; + DEBUG_MSG (APPLY, nullptr, "s%d", state); + + if (buffer->idx == buffer->len || unlikely (!buffer->successful)) + break; + + if (!(entry.flags & context_t::DontAdvance) || buffer->max_ops-- <= 0) + (void) buffer->next_glyph (); + } + + if (!c->in_place) + buffer->sync (); + } + + public: + const StateTableT &machine; + hb_buffer_t *buffer; + unsigned int num_glyphs; +}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_COMMON_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-feat-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-feat-table.hh new file mode 100644 index 00000000000..815a1fd2aa9 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-feat-table.hh @@ -0,0 +1,222 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_LAYOUT_FEAT_TABLE_HH +#define HB_AAT_LAYOUT_FEAT_TABLE_HH + +#include "hb-aat-layout-common.hh" + +/* + * feat -- Feature Name + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6feat.html + */ +#define HB_AAT_TAG_feat HB_TAG('f','e','a','t') + + +namespace AAT { + + +struct SettingName +{ + friend struct FeatureName; + + int cmp (hb_aat_layout_feature_selector_t key) const + { return (int) key - (int) setting; } + + hb_aat_layout_feature_selector_t get_selector () const + { return (hb_aat_layout_feature_selector_t) (unsigned) setting; } + + hb_aat_layout_feature_selector_info_t get_info (hb_aat_layout_feature_selector_t default_selector) const + { + return { + nameIndex, + (hb_aat_layout_feature_selector_t) (unsigned int) setting, + default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID + ? (hb_aat_layout_feature_selector_t) (setting + 1) + : default_selector, + 0 + }; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 setting; /* The setting. */ + NameID nameIndex; /* The name table index for the setting's name. */ + public: + DEFINE_SIZE_STATIC (4); +}; +DECLARE_NULL_NAMESPACE_BYTES (AAT, SettingName); + +struct feat; + +struct FeatureName +{ + int cmp (hb_aat_layout_feature_type_t key) const + { return (int) key - (int) feature; } + + enum { + Exclusive = 0x8000, /* If set, the feature settings are mutually exclusive. */ + NotDefault = 0x4000, /* If clear, then the setting with an index of 0 in + * the setting name array for this feature should + * be taken as the default for the feature + * (if one is required). If set, then bits 0-15 of this + * featureFlags field contain the index of the setting + * which is to be taken as the default. */ + IndexMask = 0x00FF /* If bits 30 and 31 are set, then these sixteen bits + * indicate the index of the setting in the setting name + * array for this feature which should be taken + * as the default. */ + }; + + unsigned int get_selector_infos (unsigned int start_offset, + unsigned int *selectors_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *pdefault_index, /* OUT. May be NULL. */ + const void *base) const + { + hb_array_t< const SettingName> settings_table = (base+settingTableZ).as_array (nSettings); + + static_assert (Index::NOT_FOUND_INDEX == HB_AAT_LAYOUT_NO_SELECTOR_INDEX, ""); + + hb_aat_layout_feature_selector_t default_selector = HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID; + unsigned int default_index = Index::NOT_FOUND_INDEX; + if (featureFlags & Exclusive) + { + default_index = (featureFlags & NotDefault) ? featureFlags & IndexMask : 0; + default_selector = settings_table[default_index].get_selector (); + } + if (pdefault_index) + *pdefault_index = default_index; + + if (selectors_count) + { + + settings_table.sub_array (start_offset, selectors_count) + | hb_map ([=] (const SettingName& setting) { return setting.get_info (default_selector); }) + | hb_sink (hb_array (selectors, *selectors_count)) + ; + } + return settings_table.length; + } + + hb_aat_layout_feature_type_t get_feature_type () const + { return (hb_aat_layout_feature_type_t) (unsigned int) feature; } + + hb_ot_name_id_t get_feature_name_id () const { return nameIndex; } + + bool is_exclusive () const { return featureFlags & Exclusive; } + + /* A FeatureName with no settings is meaningless */ + bool has_data () const { return nSettings; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (base+settingTableZ).sanitize (c, nSettings))); + } + + protected: + HBUINT16 feature; /* Feature type. */ + HBUINT16 nSettings; /* The number of records in the setting name array. */ + NNOffset32To> + settingTableZ; /* Offset in bytes from the beginning of this table to + * this feature's setting name array. The actual type of + * record this offset refers to will depend on the + * exclusivity value, as described below. */ + HBUINT16 featureFlags; /* Single-bit flags associated with the feature type. */ + HBINT16 nameIndex; /* The name table index for the feature's name. + * This index has values greater than 255 and + * less than 32768. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct feat +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_feat; + + bool has_data () const { return version.to_int (); } + + unsigned int get_feature_types (unsigned int start_offset, + unsigned int *count, + hb_aat_layout_feature_type_t *features) const + { + if (count) + { + + namesZ.as_array (featureNameCount).sub_array (start_offset, count) + | hb_map (&FeatureName::get_feature_type) + | hb_sink (hb_array (features, *count)) + ; + } + return featureNameCount; + } + + bool exposes_feature (hb_aat_layout_feature_type_t feature_type) const + { return get_feature (feature_type).has_data (); } + + const FeatureName& get_feature (hb_aat_layout_feature_type_t feature_type) const + { return namesZ.bsearch (featureNameCount, feature_type); } + + hb_ot_name_id_t get_feature_name_id (hb_aat_layout_feature_type_t feature) const + { return get_feature (feature).get_feature_name_id (); } + + unsigned int get_selector_infos (hb_aat_layout_feature_type_t feature_type, + unsigned int start_offset, + unsigned int *selectors_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *default_index /* OUT. May be NULL. */) const + { + return get_feature (feature_type).get_selector_infos (start_offset, selectors_count, selectors, + default_index, this); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version.major == 1 && + namesZ.sanitize (c, featureNameCount, this))); + } + + protected: + FixedVersion<>version; /* Version number of the feature name table + * (0x00010000 for the current version). */ + HBUINT16 featureNameCount; + /* The number of entries in the feature name array. */ + HBUINT16 reserved1; /* Reserved (set to zero). */ + HBUINT32 reserved2; /* Reserved (set to zero). */ + SortedUnsizedArrayOf + namesZ; /* The feature name array. */ + public: + DEFINE_SIZE_ARRAY (12, namesZ); +}; + +} /* namespace AAT */ + +#endif /* HB_AAT_LAYOUT_FEAT_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-just-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-just-table.hh new file mode 100644 index 00000000000..8fd3990f887 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-just-table.hh @@ -0,0 +1,417 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_LAYOUT_JUST_TABLE_HH +#define HB_AAT_LAYOUT_JUST_TABLE_HH + +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout.hh" +#include "hb-open-type.hh" + +#include "hb-aat-layout-morx-table.hh" + +/* + * just -- Justification + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6just.html + */ +#define HB_AAT_TAG_just HB_TAG('j','u','s','t') + + +namespace AAT { + +using namespace OT; + + +struct ActionSubrecordHeader +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 actionClass; /* The JustClass value associated with this + * ActionSubrecord. */ + HBUINT16 actionType; /* The type of postcompensation action. */ + HBUINT16 actionLength; /* Length of this ActionSubrecord record, which + * must be a multiple of 4. */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct DecompositionAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + ActionSubrecordHeader + header; + F16DOT16 lowerLimit; /* If the distance factor is less than this value, + * then the ligature is decomposed. */ + F16DOT16 upperLimit; /* If the distance factor is greater than this value, + * then the ligature is decomposed. */ + HBUINT16 order; /* Numerical order in which this ligature will + * be decomposed; you may want infrequent ligatures + * to decompose before more frequent ones. The ligatures + * on the line of text will decompose in increasing + * value of this field. */ + Array16Of + decomposedglyphs; + /* Number of 16-bit glyph indexes that follow; + * the ligature will be decomposed into these glyphs. + * + * Array of decomposed glyphs. */ + public: + DEFINE_SIZE_ARRAY (18, decomposedglyphs); +}; + +struct UnconditionalAddGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + ActionSubrecordHeader + header; + HBGlyphID16 addGlyph; /* Glyph that should be added if the distance factor + * is growing. */ + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct ConditionalAddGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + ActionSubrecordHeader + header; + F16DOT16 substThreshold; /* Distance growth factor (in ems) at which + * this glyph is replaced and the growth factor + * recalculated. */ + HBGlyphID16 addGlyph; /* Glyph to be added as kashida. If this value is + * 0xFFFF, no extra glyph will be added. Note that + * generally when a glyph is added, justification + * will need to be redone. */ + HBGlyphID16 substGlyph; /* Glyph to be substituted for this glyph if the + * growth factor equals or exceeds the value of + * substThreshold. */ + public: + DEFINE_SIZE_STATIC (14); +}; + +struct DuctileGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + ActionSubrecordHeader + header; + HBUINT32 variationAxis; /* The 4-byte tag identifying the ductile axis. + * This would normally be 0x64756374 ('duct'), + * but you may use any axis the font contains. */ + F16DOT16 minimumLimit; /* The lowest value for the ductility axis that + * still yields an acceptable appearance. Normally + * this will be 1.0. */ + F16DOT16 noStretchValue; /* This is the default value that corresponds to + * no change in appearance. Normally, this will + * be 1.0. */ + F16DOT16 maximumLimit; /* The highest value for the ductility axis that + * still yields an acceptable appearance. */ + public: + DEFINE_SIZE_STATIC (22); +}; + +struct RepeatedAddGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + ActionSubrecordHeader + header; + HBUINT16 flags; /* Currently unused; set to 0. */ + HBGlyphID16 glyph; /* Glyph that should be added if the distance factor + * is growing. */ + public: + DEFINE_SIZE_STATIC (10); +}; + +struct ActionSubrecord +{ + unsigned int get_length () const { return u.header.actionLength; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (u.header.actionType) + { + case 0: return_trace (u.decompositionAction.sanitize (c)); + case 1: return_trace (u.unconditionalAddGlyphAction.sanitize (c)); + case 2: return_trace (u.conditionalAddGlyphAction.sanitize (c)); + // case 3: return_trace (u.stretchGlyphAction.sanitize (c)); + case 4: return_trace (u.decompositionAction.sanitize (c)); + case 5: return_trace (u.decompositionAction.sanitize (c)); + default: return_trace (true); + } + } + + protected: + union { + ActionSubrecordHeader header; + DecompositionAction decompositionAction; + UnconditionalAddGlyphAction unconditionalAddGlyphAction; + ConditionalAddGlyphAction conditionalAddGlyphAction; + /* StretchGlyphAction stretchGlyphAction; -- Not supported by CoreText */ + DuctileGlyphAction ductileGlyphAction; + RepeatedAddGlyphAction repeatedAddGlyphAction; + } u; /* Data. The format of this data depends on + * the value of the actionType field. */ + public: + DEFINE_SIZE_UNION (6, header); +}; + +struct PostcompensationActionChain +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + unsigned int offset = min_size; + for (unsigned int i = 0; i < count; i++) + { + const ActionSubrecord& subrecord = StructAtOffset (this, offset); + if (unlikely (!subrecord.sanitize (c))) return_trace (false); + offset += subrecord.get_length (); + } + + return_trace (true); + } + + protected: + HBUINT32 count; + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct JustWidthDeltaEntry +{ + enum Flags + { + Reserved1 =0xE000,/* Reserved. You should set these bits to zero. */ + UnlimiteGap =0x1000,/* The glyph can take unlimited gap. When this + * glyph participates in the justification process, + * it and any other glyphs on the line having this + * bit set absorb all the remaining gap. */ + Reserved2 =0x0FF0,/* Reserved. You should set these bits to zero. */ + Priority =0x000F /* The justification priority of the glyph. */ + }; + + enum Priority + { + Kashida = 0, /* Kashida priority. This is the highest priority + * during justification. */ + Whitespace = 1, /* Whitespace priority. Any whitespace glyphs (as + * identified in the glyph properties table) will + * get this priority. */ + InterCharacter = 2, /* Inter-character priority. Give this to any + * remaining glyphs. */ + NullPriority = 3 /* Null priority. You should set this priority for + * glyphs that only participate in justification + * after the above priorities. Normally all glyphs + * have one of the previous three values. If you + * don't want a glyph to participate in justification, + * and you don't want to set its factors to zero, + * you may instead assign it to the null priority. */ + }; + + protected: + F16DOT16 beforeGrowLimit;/* The ratio by which the advance width of the + * glyph is permitted to grow on the left or top side. */ + F16DOT16 beforeShrinkLimit; + /* The ratio by which the advance width of the + * glyph is permitted to shrink on the left or top side. */ + F16DOT16 afterGrowLimit; /* The ratio by which the advance width of the glyph + * is permitted to shrink on the left or top side. */ + F16DOT16 afterShrinkLimit; + /* The ratio by which the advance width of the glyph + * is at most permitted to shrink on the right or + * bottom side. */ + HBUINT16 growFlags; /* Flags controlling the grow case. */ + HBUINT16 shrinkFlags; /* Flags controlling the shrink case. */ + + public: + DEFINE_SIZE_STATIC (20); +}; + +struct WidthDeltaPair +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT32 justClass; /* The justification category associated + * with the wdRecord field. Only 7 bits of + * this field are used. (The other bits are + * used as padding to guarantee longword + * alignment of the following record). */ + JustWidthDeltaEntry + wdRecord; /* The actual width delta record. */ + + public: + DEFINE_SIZE_STATIC (24); +}; + +typedef OT::Array32Of WidthDeltaCluster; + +struct JustificationCategory +{ + typedef void EntryData; + + enum Flags + { + SetMark =0x8000,/* If set, make the current glyph the marked + * glyph. */ + DontAdvance =0x4000,/* If set, don't advance to the next glyph before + * going to the new state. */ + MarkCategory =0x3F80,/* The justification category for the marked + * glyph if nonzero. */ + CurrentCategory =0x007F /* The justification category for the current + * glyph if nonzero. */ + }; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + morphHeader.sanitize (c) && + stHeader.sanitize (c))); + } + + protected: + ChainSubtable + morphHeader; /* Metamorphosis-style subtable header. */ + StateTable + stHeader; /* The justification insertion state table header */ + public: + DEFINE_SIZE_STATIC (30); +}; + +struct JustificationHeader +{ + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + justClassTable.sanitize (c, base, base) && + wdcTable.sanitize (c, base) && + pcTable.sanitize (c, base) && + lookupTable.sanitize (c, base))); + } + + protected: + Offset16To + justClassTable; /* Offset to the justification category state table. */ + Offset16To + wdcTable; /* Offset from start of justification table to start + * of the subtable containing the width delta factors + * for the glyphs in your font. + * + * The width delta clusters table. */ + Offset16To + pcTable; /* Offset from start of justification table to start + * of postcompensation subtable (set to zero if none). + * + * The postcompensation subtable, if present in the font. */ + Lookup> + lookupTable; /* Lookup table associating glyphs with width delta + * clusters. See the description of Width Delta Clusters + * table for details on how to interpret the lookup values. */ + + public: + DEFINE_SIZE_MIN (8); +}; + +struct just +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_just; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + return_trace (likely (c->check_struct (this) && + version.major == 1 && + horizData.sanitize (c, this, this) && + vertData.sanitize (c, this, this))); + } + + protected: + FixedVersion<>version; /* Version of the justification table + * (0x00010000u for version 1.0). */ + HBUINT16 format; /* Format of the justification table (set to 0). */ + Offset16To + horizData; /* Byte offset from the start of the justification table + * to the header for tables that contain justification + * information for horizontal text. + * If you are not including this information, + * store 0. */ + Offset16To + vertData; /* ditto, vertical */ + + public: + DEFINE_SIZE_STATIC (10); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_JUST_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-kerx-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-kerx-table.hh new file mode 100644 index 00000000000..35d7c84c2bc --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-kerx-table.hh @@ -0,0 +1,1001 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * Copyright ยฉ 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_KERX_TABLE_HH +#define HB_AAT_LAYOUT_KERX_TABLE_HH + +#include "hb-kern.hh" +#include "hb-aat-layout-ankr-table.hh" + +/* + * kerx -- Extended Kerning + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html + */ +#define HB_AAT_TAG_kerx HB_TAG('k','e','r','x') + + +namespace AAT { + +using namespace OT; + + +static inline int +kerxTupleKern (int value, + unsigned int tupleCount, + const void *base, + hb_aat_apply_context_t *c) +{ + if (likely (!tupleCount || !c)) return value; + + unsigned int offset = value; + const FWORD *pv = &StructAtOffset (base, offset); + if (unlikely (!c->sanitizer.check_array (pv, tupleCount))) return 0; + return *pv; +} + + +struct hb_glyph_pair_t +{ + hb_codepoint_t left; + hb_codepoint_t right; +}; + +struct KernPair +{ + int get_kerning () const { return value; } + + int cmp (const hb_glyph_pair_t &o) const + { + int ret = left.cmp (o.left); + if (ret) return ret; + return right.cmp (o.right); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBGlyphID16 left; + HBGlyphID16 right; + FWORD value; + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct KerxSubTableFormat0 +{ + int get_kerning (hb_codepoint_t left, hb_codepoint_t right, + hb_aat_apply_context_t *c = nullptr) const + { + hb_glyph_pair_t pair = {left, right}; + int v = pairs.bsearch (pair).get_kerning (); + return kerxTupleKern (v, header.tuple_count (), this, c); + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + accelerator_t accel (*this, c); + hb_kern_machine_t machine (accel, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + struct accelerator_t + { + const KerxSubTableFormat0 &table; + hb_aat_apply_context_t *c; + + accelerator_t (const KerxSubTableFormat0 &table_, + hb_aat_apply_context_t *c_) : + table (table_), c (c_) {} + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table.get_kerning (left, right, c); } + }; + + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (pairs.sanitize (c))); + } + + protected: + KernSubTableHeader header; + BinSearchArrayOf + pairs; /* Sorted kern records. */ + public: + DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 16, pairs); +}; + + +template +struct Format1Entry; + +template <> +struct Format1Entry +{ + enum Flags + { + Push = 0x8000, /* If set, push this glyph on the kerning stack. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * before going to the new state. */ + Reset = 0x2000, /* If set, reset the kerning data (clear the stack) */ + Reserved = 0x1FFF, /* Not used; set to 0. */ + }; + + struct EntryData + { + HBUINT16 kernActionIndex;/* Index into the kerning value array. If + * this index is 0xFFFF, then no kerning + * is to be performed. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + static bool performAction (const Entry &entry) + { return entry.data.kernActionIndex != 0xFFFF; } + + static unsigned int kernActionIndex (const Entry &entry) + { return entry.data.kernActionIndex; } +}; +template <> +struct Format1Entry +{ + enum Flags + { + Push = 0x8000, /* If set, push this glyph on the kerning stack. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * before going to the new state. */ + Offset = 0x3FFF, /* Byte offset from beginning of subtable to the + * value table for the glyphs on the kerning stack. */ + + Reset = 0x0000, /* Not supported? */ + }; + + typedef void EntryData; + + static bool performAction (const Entry &entry) + { return entry.flags & Offset; } + + static unsigned int kernActionIndex (const Entry &entry) + { return entry.flags & Offset; } +}; + +template +struct KerxSubTableFormat1 +{ + typedef typename KernSubTableHeader::Types Types; + typedef typename Types::HBUINT HBUINT; + + typedef Format1Entry Format1EntryT; + typedef typename Format1EntryT::EntryData EntryData; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum + { + DontAdvance = Format1EntryT::DontAdvance, + }; + + driver_context_t (const KerxSubTableFormat1 *table_, + hb_aat_apply_context_t *c_) : + c (c_), + table (table_), + /* Apparently the offset kernAction is from the beginning of the state-machine, + * similar to offsets in morx table, NOT from beginning of this table, like + * other subtables in kerx. Discovered via testing. */ + kernAction (&table->machine + table->kernAction), + depth (0), + crossStream (table->header.coverage & table->header.CrossStream) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { return Format1EntryT::performAction (entry); } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + if (flags & Format1EntryT::Reset) + depth = 0; + + if (flags & Format1EntryT::Push) + { + if (likely (depth < ARRAY_LENGTH (stack))) + stack[depth++] = buffer->idx; + else + depth = 0; /* Probably not what CoreText does, but better? */ + } + + if (Format1EntryT::performAction (entry) && depth) + { + unsigned int tuple_count = hb_max (1u, table->header.tuple_count ()); + + unsigned int kern_idx = Format1EntryT::kernActionIndex (entry); + kern_idx = Types::byteOffsetToIndex (kern_idx, &table->machine, kernAction.arrayZ); + const FWORD *actions = &kernAction[kern_idx]; + if (!c->sanitizer.check_array (actions, depth, tuple_count)) + { + depth = 0; + return; + } + + hb_mask_t kern_mask = c->plan->kern_mask; + + /* From Apple 'kern' spec: + * "Each pops one glyph from the kerning stack and applies the kerning value to it. + * The end of the list is marked by an odd value... */ + bool last = false; + while (!last && depth) + { + unsigned int idx = stack[--depth]; + int v = *actions; + actions += tuple_count; + if (idx >= buffer->len) continue; + + /* "The end of the list is marked by an odd value..." */ + last = v & 1; + v &= ~1; + + hb_glyph_position_t &o = buffer->pos[idx]; + + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + if (crossStream) + { + /* The following flag is undocumented in the spec, but described + * in the 'kern' table example. */ + if (v == -0x8000) + { + o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE; + o.attach_chain() = 0; + o.y_offset = 0; + } + else if (o.attach_type()) + { + o.y_offset += c->font->em_scale_y (v); + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + } + else if (buffer->info[idx].mask & kern_mask) + { + o.x_advance += c->font->em_scale_x (v); + o.x_offset += c->font->em_scale_x (v); + } + } + else + { + if (crossStream) + { + /* CoreText doesn't do crossStream kerning in vertical. We do. */ + if (v == -0x8000) + { + o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE; + o.attach_chain() = 0; + o.x_offset = 0; + } + else if (o.attach_type()) + { + o.x_offset += c->font->em_scale_x (v); + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + } + else if (buffer->info[idx].mask & kern_mask) + { + o.y_advance += c->font->em_scale_y (v); + o.y_offset += c->font->em_scale_y (v); + } + } + } + } + } + + private: + hb_aat_apply_context_t *c; + const KerxSubTableFormat1 *table; + const UnsizedArrayOf &kernAction; + unsigned int stack[8]; + unsigned int depth; + bool crossStream; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning && + !(header.coverage & header.CrossStream)) + return false; + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->font->face); + driver.drive (&dc, c); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (likely (c->check_struct (this) && + machine.sanitize (c))); + } + + protected: + KernSubTableHeader header; + StateTable machine; + NNOffsetTo, HBUINT> kernAction; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 5 * sizeof (HBUINT)); +}; + +template +struct KerxSubTableFormat2 +{ + typedef typename KernSubTableHeader::Types Types; + typedef typename Types::HBUINT HBUINT; + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right, + hb_aat_apply_context_t *c) const + { + unsigned int num_glyphs = c->sanitizer.get_num_glyphs (); + unsigned int l = (this+leftClassTable).get_class (left, num_glyphs, 0); + unsigned int r = (this+rightClassTable).get_class (right, num_glyphs, 0); + + const UnsizedArrayOf &arrayZ = this+array; + unsigned int kern_idx = l + r; + kern_idx = Types::offsetToIndex (kern_idx, this, arrayZ.arrayZ); + const FWORD *v = &arrayZ[kern_idx]; + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + + return kerxTupleKern (*v, header.tuple_count (), this, c); + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + accelerator_t accel (*this, c); + hb_kern_machine_t machine (accel, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + struct accelerator_t + { + const KerxSubTableFormat2 &table; + hb_aat_apply_context_t *c; + + accelerator_t (const KerxSubTableFormat2 &table_, + hb_aat_apply_context_t *c_) : + table (table_), c (c_) {} + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table.get_kerning (left, right, c); } + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + leftClassTable.sanitize (c, this) && + rightClassTable.sanitize (c, this) && + c->check_range (this, array))); + } + + protected: + KernSubTableHeader header; + HBUINT rowWidth; /* The width, in bytes, of a row in the table. */ + NNOffsetTo + leftClassTable; /* Offset from beginning of this subtable to + * left-hand class table. */ + NNOffsetTo + rightClassTable;/* Offset from beginning of this subtable to + * right-hand class table. */ + NNOffsetTo, HBUINT> + array; /* Offset from beginning of this subtable to + * the start of the kerning array. */ + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 4 * sizeof (HBUINT)); +}; + +template +struct KerxSubTableFormat4 +{ + typedef ExtendedTypes Types; + + struct EntryData + { + HBUINT16 ankrActionIndex;/* Either 0xFFFF (for no action) or the index of + * the action to perform. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + Mark = 0x8000, /* If set, remember this glyph as the marked glyph. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph before + * going to the new state. */ + Reserved = 0x3FFF, /* Not used; set to 0. */ + }; + + enum SubTableFlags + { + ActionType = 0xC0000000, /* A two-bit field containing the action type. */ + Unused = 0x3F000000, /* Unused - must be zero. */ + Offset = 0x00FFFFFF, /* Masks the offset in bytes from the beginning + * of the subtable to the beginning of the control + * point table. */ + }; + + driver_context_t (const KerxSubTableFormat4 *table, + hb_aat_apply_context_t *c_) : + c (c_), + action_type ((table->flags & ActionType) >> 30), + ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))), + mark_set (false), + mark (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { return entry.data.ankrActionIndex != 0xFFFF; } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len) + { + hb_glyph_position_t &o = buffer->cur_pos(); + switch (action_type) + { + case 0: /* Control Point Actions.*/ + { + /* Indexed into glyph outline. */ + /* Each action (record in ankrData) contains two 16-bit fields, so we must + double the ankrActionIndex to get the correct offset here. */ + const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2]; + if (!c->sanitizer.check_array (data, 2)) return; + unsigned int markControlPoint = *data++; + unsigned int currControlPoint = *data++; + hb_position_t markX = 0; + hb_position_t markY = 0; + hb_position_t currX = 0; + hb_position_t currY = 0; + if (!c->font->get_glyph_contour_point_for_origin (c->buffer->info[mark].codepoint, + markControlPoint, + HB_DIRECTION_LTR /*XXX*/, + &markX, &markY) || + !c->font->get_glyph_contour_point_for_origin (c->buffer->cur ().codepoint, + currControlPoint, + HB_DIRECTION_LTR /*XXX*/, + &currX, &currY)) + return; + + o.x_offset = markX - currX; + o.y_offset = markY - currY; + } + break; + + case 1: /* Anchor Point Actions. */ + { + /* Indexed into 'ankr' table. */ + /* Each action (record in ankrData) contains two 16-bit fields, so we must + double the ankrActionIndex to get the correct offset here. */ + const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2]; + if (!c->sanitizer.check_array (data, 2)) return; + unsigned int markAnchorPoint = *data++; + unsigned int currAnchorPoint = *data++; + const Anchor &markAnchor = c->ankr_table->get_anchor (c->buffer->info[mark].codepoint, + markAnchorPoint, + c->sanitizer.get_num_glyphs ()); + const Anchor &currAnchor = c->ankr_table->get_anchor (c->buffer->cur ().codepoint, + currAnchorPoint, + c->sanitizer.get_num_glyphs ()); + + o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate); + o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate); + } + break; + + case 2: /* Control Point Coordinate Actions. */ + { + /* Each action contains four 16-bit fields, so we multiply the ankrActionIndex + by 4 to get the correct offset for the given action. */ + const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex * 4]; + if (!c->sanitizer.check_array (data, 4)) return; + int markX = *data++; + int markY = *data++; + int currX = *data++; + int currY = *data++; + + o.x_offset = c->font->em_scale_x (markX) - c->font->em_scale_x (currX); + o.y_offset = c->font->em_scale_y (markY) - c->font->em_scale_y (currY); + } + break; + } + o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_MARK; + o.attach_chain() = (int) mark - (int) buffer->idx; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + + if (entry.flags & Mark) + { + mark_set = true; + mark = buffer->idx; + } + } + + private: + hb_aat_apply_context_t *c; + unsigned int action_type; + const HBUINT16 *ankrData; + bool mark_set; + unsigned int mark; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->font->face); + driver.drive (&dc, c); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (likely (c->check_struct (this) && + machine.sanitize (c))); + } + + protected: + KernSubTableHeader header; + StateTable machine; + HBUINT32 flags; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20); +}; + +template +struct KerxSubTableFormat6 +{ + enum Flags + { + ValuesAreLong = 0x00000001, + }; + + bool is_long () const { return flags & ValuesAreLong; } + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right, + hb_aat_apply_context_t *c) const + { + unsigned int num_glyphs = c->sanitizer.get_num_glyphs (); + if (is_long ()) + { + const typename U::Long &t = u.l; + unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs); + unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs); + unsigned int offset = l + r; + if (unlikely (offset < l)) return 0; /* Addition overflow. */ + if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0; + const FWORD32 *v = &StructAtOffset (&(this+t.array), offset * sizeof (FWORD32)); + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c); + } + else + { + const typename U::Short &t = u.s; + unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs); + unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs); + unsigned int offset = l + r; + const FWORD *v = &StructAtOffset (&(this+t.array), offset * sizeof (FWORD)); + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c); + } + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + accelerator_t accel (*this, c); + hb_kern_machine_t machine (accel, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (is_long () ? + ( + u.l.rowIndexTable.sanitize (c, this) && + u.l.columnIndexTable.sanitize (c, this) && + c->check_range (this, u.l.array) + ) : ( + u.s.rowIndexTable.sanitize (c, this) && + u.s.columnIndexTable.sanitize (c, this) && + c->check_range (this, u.s.array) + )) && + (header.tuple_count () == 0 || + c->check_range (this, vector)))); + } + + struct accelerator_t + { + const KerxSubTableFormat6 &table; + hb_aat_apply_context_t *c; + + accelerator_t (const KerxSubTableFormat6 &table_, + hb_aat_apply_context_t *c_) : + table (table_), c (c_) {} + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table.get_kerning (left, right, c); } + }; + + protected: + KernSubTableHeader header; + HBUINT32 flags; + HBUINT16 rowCount; + HBUINT16 columnCount; + union U + { + struct Long + { + NNOffset32To> rowIndexTable; + NNOffset32To> columnIndexTable; + NNOffset32To> array; + } l; + struct Short + { + NNOffset32To> rowIndexTable; + NNOffset32To> columnIndexTable; + NNOffset32To> array; + } s; + } u; + NNOffset32To> vector; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24); +}; + + +struct KerxSubTableHeader +{ + typedef ExtendedTypes Types; + + unsigned tuple_count () const { return tupleCount; } + bool is_horizontal () const { return !(coverage & Vertical); } + + enum Coverage + { + Vertical = 0x80000000u, /* Set if table has vertical kerning values. */ + CrossStream = 0x40000000u, /* Set if table has cross-stream kerning values. */ + Variation = 0x20000000u, /* Set if table has variation kerning values. */ + Backwards = 0x10000000u, /* If clear, process the glyphs forwards, that + * is, from first to last in the glyph stream. + * If we, process them from last to first. + * This flag only applies to state-table based + * 'kerx' subtables (types 1 and 4). */ + Reserved = 0x0FFFFF00u, /* Reserved, set to zero. */ + SubtableType= 0x000000FFu, /* Subtable type. */ + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT32 length; + HBUINT32 coverage; + HBUINT32 tupleCount; + public: + DEFINE_SIZE_STATIC (12); +}; + +struct KerxSubTable +{ + friend struct kerx; + + unsigned int get_size () const { return u.header.length; } + unsigned int get_type () const { return u.header.coverage & u.header.SubtableType; } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case 0: return_trace (c->dispatch (u.format0, std::forward (ds)...)); + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); + case 6: return_trace (c->dispatch (u.format6, std::forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.header.sanitize (c) || + u.header.length <= u.header.static_size || + !c->check_range (this, u.header.length)) + return_trace (false); + + return_trace (dispatch (c)); + } + + public: + union { + KerxSubTableHeader header; + KerxSubTableFormat0 format0; + KerxSubTableFormat1 format1; + KerxSubTableFormat2 format2; + KerxSubTableFormat4 format4; + KerxSubTableFormat6 format6; + } u; + public: + DEFINE_SIZE_MIN (12); +}; + + +/* + * The 'kerx' Table + */ + +template +struct KerxTable +{ + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const T* thiz () const { return static_cast (this); } + + bool has_state_machine () const + { + typedef typename T::SubTable SubTable; + + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if (st->get_type () == 1) + return true; + st = &StructAfter (*st); + } + return false; + } + + bool has_cross_stream () const + { + typedef typename T::SubTable SubTable; + + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if (st->u.header.coverage & st->u.header.CrossStream) + return true; + st = &StructAfter (*st); + } + return false; + } + + int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + typedef typename T::SubTable SubTable; + + int v = 0; + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) || + !st->u.header.is_horizontal ()) + continue; + v += st->get_kerning (left, right); + st = &StructAfter (*st); + } + return v; + } + + bool apply (AAT::hb_aat_apply_context_t *c) const + { + c->buffer->unsafe_to_concat (); + + typedef typename T::SubTable SubTable; + + bool ret = false; + bool seenCrossStream = false; + c->set_lookup_index (0); + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + bool reverse; + + if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation)) + goto skip; + + if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ()) + goto skip; + + reverse = bool (st->u.header.coverage & st->u.header.Backwards) != + HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); + + if (!c->buffer->message (c->font, "start subtable %u", c->lookup_index)) + goto skip; + + if (!seenCrossStream && + (st->u.header.coverage & st->u.header.CrossStream)) + { + /* Attach all glyphs into a chain. */ + seenCrossStream = true; + hb_glyph_position_t *pos = c->buffer->pos; + unsigned int count = c->buffer->len; + for (unsigned int i = 0; i < count; i++) + { + pos[i].attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_CURSIVE; + pos[i].attach_chain() = HB_DIRECTION_IS_FORWARD (c->buffer->props.direction) ? -1 : +1; + /* We intentionally don't set HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT, + * since there needs to be a non-zero attachment for post-positioning to + * be needed. */ + } + } + + if (reverse) + c->buffer->reverse (); + + { + /* See comment in sanitize() for conditional here. */ + hb_sanitize_with_object_t with (&c->sanitizer, i < count - 1 ? st : (const SubTable *) nullptr); + ret |= st->dispatch (c); + } + + if (reverse) + c->buffer->reverse (); + + (void) c->buffer->message (c->font, "end subtable %u", c->lookup_index); + + skip: + st = &StructAfter (*st); + c->set_lookup_index (c->lookup_index + 1); + } + + return ret; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!thiz()->version.sanitize (c) || + (unsigned) thiz()->version < (unsigned) T::minVersion || + !thiz()->tableCount.sanitize (c))) + return_trace (false); + + typedef typename T::SubTable SubTable; + + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if (unlikely (!st->u.header.sanitize (c))) + return_trace (false); + /* OpenType kern table has 2-byte subtable lengths. That's limiting. + * MS implementation also only supports one subtable, of format 0, + * anyway. Certain versions of some fonts, like Calibry, contain + * kern subtable that exceeds 64kb. Looks like, the subtable length + * is simply ignored. Which makes sense. It's only needed if you + * have multiple subtables. To handle such fonts, we just ignore + * the length for the last subtable. */ + hb_sanitize_with_object_t with (c, i < count - 1 ? st : (const SubTable *) nullptr); + + if (unlikely (!st->sanitize (c))) + return_trace (false); + + st = &StructAfter (*st); + } + + return_trace (true); + } +}; + +struct kerx : KerxTable +{ + friend struct KerxTable; + + static constexpr hb_tag_t tableTag = HB_AAT_TAG_kerx; + static constexpr unsigned minVersion = 2u; + + typedef KerxSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KerxSubTable SubTable; + + bool has_data () const { return version; } + + protected: + HBUINT16 version; /* The version number of the extended kerning table + * (currently 2, 3, or 4). */ + HBUINT16 unused; /* Set to 0. */ + HBUINT32 tableCount; /* The number of subtables included in the extended kerning + * table. */ + SubTable firstSubTable; /* Subtables. */ +/*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */ + + public: + DEFINE_SIZE_MIN (8); +}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-morx-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-morx-table.hh new file mode 100644 index 00000000000..f41ecc197fb --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-morx-table.hh @@ -0,0 +1,1210 @@ +/* + * Copyright ยฉ 2017 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_MORX_TABLE_HH +#define HB_AAT_LAYOUT_MORX_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout-common.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-aat-map.hh" + +/* + * morx -- Extended Glyph Metamorphosis + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html + */ +#define HB_AAT_TAG_morx HB_TAG('m','o','r','x') +#define HB_AAT_TAG_mort HB_TAG('m','o','r','t') + + +namespace AAT { + +using namespace OT; + +template +struct RearrangementSubtable +{ + typedef typename Types::HBUINT HBUINT; + + typedef void EntryData; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + MarkFirst = 0x8000, /* If set, make the current glyph the first + * glyph to be rearranged. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * before going to the new state. This means + * that the glyph index doesn't change, even + * if the glyph at that index has changed. */ + MarkLast = 0x2000, /* If set, make the current glyph the last + * glyph to be rearranged. */ + Reserved = 0x1FF0, /* These bits are reserved and should be set to 0. */ + Verb = 0x000F, /* The type of rearrangement specified. */ + }; + + driver_context_t (const RearrangementSubtable *table HB_UNUSED) : + ret (false), + start (0), end (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return (entry.flags & Verb) && start < end; + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + if (flags & MarkFirst) + start = buffer->idx; + + if (flags & MarkLast) + end = hb_min (buffer->idx + 1, buffer->len); + + if ((flags & Verb) && start < end) + { + /* The following map has two nibbles, for start-side + * and end-side. Values of 0,1,2 mean move that many + * to the other side. Value of 3 means move 2 and + * flip them. */ + const unsigned char map[16] = + { + 0x00, /* 0 no change */ + 0x10, /* 1 Ax => xA */ + 0x01, /* 2 xD => Dx */ + 0x11, /* 3 AxD => DxA */ + 0x20, /* 4 ABx => xAB */ + 0x30, /* 5 ABx => xBA */ + 0x02, /* 6 xCD => CDx */ + 0x03, /* 7 xCD => DCx */ + 0x12, /* 8 AxCD => CDxA */ + 0x13, /* 9 AxCD => DCxA */ + 0x21, /* 10 ABxD => DxAB */ + 0x31, /* 11 ABxD => DxBA */ + 0x22, /* 12 ABxCD => CDxAB */ + 0x32, /* 13 ABxCD => CDxBA */ + 0x23, /* 14 ABxCD => DCxAB */ + 0x33, /* 15 ABxCD => DCxBA */ + }; + + unsigned int m = map[flags & Verb]; + unsigned int l = hb_min (2u, m >> 4); + unsigned int r = hb_min (2u, m & 0x0F); + bool reverse_l = 3 == (m >> 4); + bool reverse_r = 3 == (m & 0x0F); + + if (end - start >= l + r && end-start <= HB_MAX_CONTEXT_LENGTH) + { + buffer->merge_clusters (start, hb_min (buffer->idx + 1, buffer->len)); + buffer->merge_clusters (start, end); + + hb_glyph_info_t *info = buffer->info; + hb_glyph_info_t buf[4]; + + hb_memcpy (buf, info + start, l * sizeof (buf[0])); + hb_memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); + + if (l != r) + memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0])); + + hb_memcpy (info + start, buf + 2, r * sizeof (buf[0])); + hb_memcpy (info + end - l, buf, l * sizeof (buf[0])); + if (reverse_l) + { + buf[0] = info[end - 1]; + info[end - 1] = info[end - 2]; + info[end - 2] = buf[0]; + } + if (reverse_r) + { + buf[0] = info[start]; + info[start] = info[start + 1]; + info[start + 1] = buf[0]; + } + } + } + } + + public: + bool ret; + private: + unsigned int start; + unsigned int end; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc, c); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (machine.sanitize (c)); + } + + protected: + StateTable machine; + public: + DEFINE_SIZE_STATIC (16); +}; + +template +struct ContextualSubtable +{ + typedef typename Types::HBUINT HBUINT; + + struct EntryData + { + HBUINT16 markIndex; /* Index of the substitution table for the + * marked glyph (use 0xFFFF for none). */ + HBUINT16 currentIndex; /* Index of the substitution table for the + * current glyph (use 0xFFFF for none). */ + public: + DEFINE_SIZE_STATIC (4); + }; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph before + * going to the new state. */ + Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */ + }; + + driver_context_t (const ContextualSubtable *table_, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + gdef (*c->gdef_table), + mark_set (false), + has_glyph_classes (gdef.has_glyph_classes ()), + mark (0), + table (table_), + subs (table+table->substitutionTables) {} + + bool is_actionable (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + if (buffer->idx == buffer->len && !mark_set) + return false; + + return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF; + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + /* Looks like CoreText applies neither mark nor current substitution for + * end-of-text if mark was not explicitly set. */ + if (buffer->idx == buffer->len && !mark_set) + return; + + const HBGlyphID16 *replacement; + + replacement = nullptr; + if (Types::extended) + { + if (entry.data.markIndex != 0xFFFF) + { + const Lookup &lookup = subs[entry.data.markIndex]; + replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs); + } + } + else + { + unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint; + const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!replacement->sanitize (&c->sanitizer) || !*replacement) + replacement = nullptr; + } + if (replacement) + { + buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len)); + buffer->info[mark].codepoint = *replacement; + if (has_glyph_classes) + _hb_glyph_info_set_glyph_props (&buffer->info[mark], + gdef.get_glyph_props (*replacement)); + ret = true; + } + + replacement = nullptr; + unsigned int idx = hb_min (buffer->idx, buffer->len - 1); + if (Types::extended) + { + if (entry.data.currentIndex != 0xFFFF) + { + const Lookup &lookup = subs[entry.data.currentIndex]; + replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs); + } + } + else + { + unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint; + const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!replacement->sanitize (&c->sanitizer) || !*replacement) + replacement = nullptr; + } + if (replacement) + { + buffer->info[idx].codepoint = *replacement; + if (has_glyph_classes) + _hb_glyph_info_set_glyph_props (&buffer->info[idx], + gdef.get_glyph_props (*replacement)); + ret = true; + } + + if (entry.flags & SetMark) + { + mark_set = true; + mark = buffer->idx; + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + const OT::GDEF &gdef; + bool mark_set; + bool has_glyph_classes; + unsigned int mark; + const ContextualSubtable *table; + const UnsizedListOfOffset16To, HBUINT, false> &subs; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc, c); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + unsigned int num_entries = 0; + if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false); + + if (!Types::extended) + return_trace (substitutionTables.sanitize (c, this, 0)); + + unsigned int num_lookups = 0; + + const Entry *entries = machine.get_entries (); + for (unsigned int i = 0; i < num_entries; i++) + { + const EntryData &data = entries[i].data; + + if (data.markIndex != 0xFFFF) + num_lookups = hb_max (num_lookups, 1u + data.markIndex); + if (data.currentIndex != 0xFFFF) + num_lookups = hb_max (num_lookups, 1u + data.currentIndex); + } + + return_trace (substitutionTables.sanitize (c, this, num_lookups)); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT, false>, HBUINT> + substitutionTables; + public: + DEFINE_SIZE_STATIC (20); +}; + + +template +struct LigatureEntry; + +template <> +struct LigatureEntry +{ + enum Flags + { + SetComponent = 0x8000, /* Push this glyph onto the component stack for + * eventual processing. */ + DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the + next iteration. */ + PerformAction = 0x2000, /* Use the ligActionIndex to process a ligature + * group. */ + Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */ + }; + + struct EntryData + { + HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry + * for processing this group, if indicated + * by the flags. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + static bool performAction (const Entry &entry) + { return entry.flags & PerformAction; } + + static unsigned int ligActionIndex (const Entry &entry) + { return entry.data.ligActionIndex; } +}; +template <> +struct LigatureEntry +{ + enum Flags + { + SetComponent = 0x8000, /* Push this glyph onto the component stack for + * eventual processing. */ + DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the + next iteration. */ + Offset = 0x3FFF, /* Byte offset from beginning of subtable to the + * ligature action list. This value must be a + * multiple of 4. */ + }; + + typedef void EntryData; + + static bool performAction (const Entry &entry) + { return entry.flags & Offset; } + + static unsigned int ligActionIndex (const Entry &entry) + { return entry.flags & Offset; } +}; + + +template +struct LigatureSubtable +{ + typedef typename Types::HBUINT HBUINT; + + typedef LigatureEntry LigatureEntryT; + typedef typename LigatureEntryT::EntryData EntryData; + + struct driver_context_t + { + static constexpr bool in_place = false; + enum + { + DontAdvance = LigatureEntryT::DontAdvance, + }; + enum LigActionFlags + { + LigActionLast = 0x80000000, /* This is the last action in the list. This also + * implies storage. */ + LigActionStore = 0x40000000, /* Store the ligature at the current cumulated index + * in the ligature table in place of the marked + * (i.e. currently-popped) glyph. */ + LigActionOffset = 0x3FFFFFFF, /* A 30-bit value which is sign-extended to 32-bits + * and added to the glyph ID, resulting in an index + * into the component table. */ + }; + + driver_context_t (const LigatureSubtable *table_, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + table (table_), + ligAction (table+table->ligAction), + component (table+table->component), + ligature (table+table->ligature), + match_length (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return LigatureEntryT::performAction (entry); + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx); + if (entry.flags & LigatureEntryT::SetComponent) + { + /* Never mark same index twice, in case DontAdvance was used... */ + if (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len) + match_length--; + + match_positions[match_length++ % ARRAY_LENGTH (match_positions)] = buffer->out_len; + DEBUG_MSG (APPLY, nullptr, "Set component at %u", buffer->out_len); + } + + if (LigatureEntryT::performAction (entry)) + { + DEBUG_MSG (APPLY, nullptr, "Perform action with %u", match_length); + unsigned int end = buffer->out_len; + + if (unlikely (!match_length)) + return; + + if (buffer->idx >= buffer->len) + return; /* TODO Work on previous instead? */ + + unsigned int cursor = match_length; + + unsigned int action_idx = LigatureEntryT::ligActionIndex (entry); + action_idx = Types::offsetToIndex (action_idx, table, ligAction.arrayZ); + const HBUINT32 *actionData = &ligAction[action_idx]; + + unsigned int ligature_idx = 0; + unsigned int action; + do + { + if (unlikely (!cursor)) + { + /* Stack underflow. Clear the stack. */ + DEBUG_MSG (APPLY, nullptr, "Stack underflow"); + match_length = 0; + break; + } + + DEBUG_MSG (APPLY, nullptr, "Moving to stack position %u", cursor - 1); + if (unlikely (!buffer->move_to (match_positions[--cursor % ARRAY_LENGTH (match_positions)]))) return; + + if (unlikely (!actionData->sanitize (&c->sanitizer))) break; + action = *actionData; + + uint32_t uoffset = action & LigActionOffset; + if (uoffset & 0x20000000) + uoffset |= 0xC0000000; /* Sign-extend. */ + int32_t offset = (int32_t) uoffset; + unsigned int component_idx = buffer->cur().codepoint + offset; + component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ); + const HBUINT16 &componentData = component[component_idx]; + if (unlikely (!componentData.sanitize (&c->sanitizer))) break; + ligature_idx += componentData; + + DEBUG_MSG (APPLY, nullptr, "Action store %d last %d", + bool (action & LigActionStore), + bool (action & LigActionLast)); + if (action & (LigActionStore | LigActionLast)) + { + ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ); + const HBGlyphID16 &ligatureData = ligature[ligature_idx]; + if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break; + hb_codepoint_t lig = ligatureData; + + DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig); + if (unlikely (!buffer->replace_glyph (lig))) return; + + unsigned int lig_end = match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] + 1u; + /* Now go and delete all subsequent components. */ + while (match_length - 1u > cursor) + { + DEBUG_MSG (APPLY, nullptr, "Skipping ligature component"); + if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return; + if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return; + } + + if (unlikely (!buffer->move_to (lig_end))) return; + buffer->merge_out_clusters (match_positions[cursor % ARRAY_LENGTH (match_positions)], buffer->out_len); + } + + actionData++; + } + while (!(action & LigActionLast)); + if (unlikely (!buffer->move_to (end))) return; + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + const LigatureSubtable *table; + const UnsizedArrayOf &ligAction; + const UnsizedArrayOf &component; + const UnsizedArrayOf &ligature; + unsigned int match_length; + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc, c); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (c->check_struct (this) && machine.sanitize (c) && + ligAction && component && ligature); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT> + ligAction; /* Offset to the ligature action table. */ + NNOffsetTo, HBUINT> + component; /* Offset to the component table. */ + NNOffsetTo, HBUINT> + ligature; /* Offset to the actual ligature lists. */ + public: + DEFINE_SIZE_STATIC (28); +}; + +template +struct NoncontextualSubtable +{ + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + const OT::GDEF &gdef (*c->gdef_table); + bool has_glyph_classes = gdef.has_glyph_classes (); + + bool ret = false; + unsigned int num_glyphs = c->face->get_num_glyphs (); + + hb_glyph_info_t *info = c->buffer->info; + unsigned int count = c->buffer->len; + // If there's only one range, we already checked the flag. + auto *last_range = c->range_flags && (c->range_flags->length > 1) ? &(*c->range_flags)[0] : nullptr; + for (unsigned int i = 0; i < count; i++) + { + /* This block copied from StateTableDriver::drive. Keep in sync. */ + if (last_range) + { + auto *range = last_range; + { + unsigned cluster = info[i].cluster; + while (cluster < range->cluster_first) + range--; + while (cluster > range->cluster_last) + range++; + + last_range = range; + } + if (!(range->flags & c->subtable_flags)) + continue; + } + + const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs); + if (replacement) + { + info[i].codepoint = *replacement; + if (has_glyph_classes) + _hb_glyph_info_set_glyph_props (&info[i], + gdef.get_glyph_props (*replacement)); + ret = true; + } + } + + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (substitute.sanitize (c)); + } + + protected: + Lookup substitute; + public: + DEFINE_SIZE_MIN (2); +}; + +template +struct InsertionSubtable +{ + typedef typename Types::HBUINT HBUINT; + + struct EntryData + { + HBUINT16 currentInsertIndex; /* Zero-based index into the insertion glyph table. + * The number of glyphs to be inserted is contained + * in the currentInsertCount field in the flags. + * A value of 0xFFFF indicates no insertion is to + * be done. */ + HBUINT16 markedInsertIndex; /* Zero-based index into the insertion glyph table. + * The number of glyphs to be inserted is contained + * in the markedInsertCount field in the flags. + * A value of 0xFFFF indicates no insertion is to + * be done. */ + public: + DEFINE_SIZE_STATIC (4); + }; + + struct driver_context_t + { + static constexpr bool in_place = false; + enum Flags + { + SetMark = 0x8000, /* If set, mark the current glyph. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph before + * going to the new state. This does not mean + * that the glyph pointed to is the same one as + * before. If you've made insertions immediately + * downstream of the current glyph, the next glyph + * processed would in fact be the first one + * inserted. */ + CurrentIsKashidaLike= 0x2000, /* If set, and the currentInsertList is nonzero, + * then the specified glyph list will be inserted + * as a kashida-like insertion, either before or + * after the current glyph (depending on the state + * of the currentInsertBefore flag). If clear, and + * the currentInsertList is nonzero, then the + * specified glyph list will be inserted as a + * split-vowel-like insertion, either before or + * after the current glyph (depending on the state + * of the currentInsertBefore flag). */ + MarkedIsKashidaLike= 0x1000, /* If set, and the markedInsertList is nonzero, + * then the specified glyph list will be inserted + * as a kashida-like insertion, either before or + * after the marked glyph (depending on the state + * of the markedInsertBefore flag). If clear, and + * the markedInsertList is nonzero, then the + * specified glyph list will be inserted as a + * split-vowel-like insertion, either before or + * after the marked glyph (depending on the state + * of the markedInsertBefore flag). */ + CurrentInsertBefore= 0x0800, /* If set, specifies that insertions are to be made + * to the left of the current glyph. If clear, + * they're made to the right of the current glyph. */ + MarkedInsertBefore= 0x0400, /* If set, specifies that insertions are to be + * made to the left of the marked glyph. If clear, + * they're made to the right of the marked glyph. */ + CurrentInsertCount= 0x3E0, /* This 5-bit field is treated as a count of the + * number of glyphs to insert at the current + * position. Since zero means no insertions, the + * largest number of insertions at any given + * current location is 31 glyphs. */ + MarkedInsertCount= 0x001F, /* This 5-bit field is treated as a count of the + * number of glyphs to insert at the marked + * position. Since zero means no insertions, the + * largest number of insertions at any given + * marked location is 31 glyphs. */ + }; + + driver_context_t (const InsertionSubtable *table, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + mark (0), + insertionAction (table+table->insertionAction) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) && + (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF); + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + unsigned mark_loc = buffer->out_len; + + if (entry.data.markedInsertIndex != 0xFFFF) + { + unsigned int count = (flags & MarkedInsertCount); + if (unlikely ((buffer->max_ops -= count) <= 0)) return; + unsigned int start = entry.data.markedInsertIndex; + const HBGlyphID16 *glyphs = &insertionAction[start]; + if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; + + bool before = flags & MarkedInsertBefore; + + unsigned int end = buffer->out_len; + if (unlikely (!buffer->move_to (mark))) return; + + if (buffer->idx < buffer->len && !before) + if (unlikely (!buffer->copy_glyph ())) return; + /* TODO We ignore KashidaLike setting. */ + if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return; + if (buffer->idx < buffer->len && !before) + buffer->skip_glyph (); + + if (unlikely (!buffer->move_to (end + count))) return; + + buffer->unsafe_to_break_from_outbuffer (mark, hb_min (buffer->idx + 1, buffer->len)); + } + + if (flags & SetMark) + mark = mark_loc; + + if (entry.data.currentInsertIndex != 0xFFFF) + { + unsigned int count = (flags & CurrentInsertCount) >> 5; + if (unlikely ((buffer->max_ops -= count) <= 0)) return; + unsigned int start = entry.data.currentInsertIndex; + const HBGlyphID16 *glyphs = &insertionAction[start]; + if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; + + bool before = flags & CurrentInsertBefore; + + unsigned int end = buffer->out_len; + + if (buffer->idx < buffer->len && !before) + if (unlikely (!buffer->copy_glyph ())) return; + /* TODO We ignore KashidaLike setting. */ + if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return; + if (buffer->idx < buffer->len && !before) + buffer->skip_glyph (); + + /* Humm. Not sure where to move to. There's this wording under + * DontAdvance flag: + * + * "If set, don't update the glyph index before going to the new state. + * This does not mean that the glyph pointed to is the same one as + * before. If you've made insertions immediately downstream of the + * current glyph, the next glyph processed would in fact be the first + * one inserted." + * + * This suggests that if DontAdvance is NOT set, we should move to + * end+count. If it *was*, then move to end, such that newly inserted + * glyphs are now visible. + * + * https://github.com/harfbuzz/harfbuzz/issues/1224#issuecomment-427691417 + */ + if (unlikely (!buffer->move_to ((flags & DontAdvance) ? end : end + count))) return; + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + unsigned int mark; + const UnsizedArrayOf &insertionAction; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc, c); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (c->check_struct (this) && machine.sanitize (c) && + insertionAction); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT> + insertionAction; /* Byte offset from stateHeader to the start of + * the insertion glyph table. */ + public: + DEFINE_SIZE_STATIC (20); +}; + + +struct Feature +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 featureType; /* The type of feature. */ + HBUINT16 featureSetting; /* The feature's setting (aka selector). */ + HBUINT32 enableFlags; /* Flags for the settings that this feature + * and setting enables. */ + HBUINT32 disableFlags; /* Complement of flags for the settings that this + * feature and setting disable. */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +template +struct ChainSubtable +{ + typedef typename Types::HBUINT HBUINT; + + template + friend struct Chain; + + unsigned int get_size () const { return length; } + unsigned int get_type () const { return coverage & 0xFF; } + unsigned int get_coverage () const { return coverage >> (sizeof (HBUINT) * 8 - 8); } + + enum Coverage + { + Vertical = 0x80, /* If set, this subtable will only be applied + * to vertical text. If clear, this subtable + * will only be applied to horizontal text. */ + Backwards = 0x40, /* If set, this subtable will process glyphs + * in descending order. If clear, it will + * process the glyphs in ascending order. */ + AllDirections = 0x20, /* If set, this subtable will be applied to + * both horizontal and vertical text (i.e. + * the state of bit 0x80000000 is ignored). */ + Logical = 0x10, /* If set, this subtable will process glyphs + * in logical order (or reverse logical order, + * depending on the value of bit 0x80000000). */ + }; + enum Type + { + Rearrangement = 0, + Contextual = 1, + Ligature = 2, + Noncontextual = 4, + Insertion = 5 + }; + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case Rearrangement: return_trace (c->dispatch (u.rearrangement, std::forward (ds)...)); + case Contextual: return_trace (c->dispatch (u.contextual, std::forward (ds)...)); + case Ligature: return_trace (c->dispatch (u.ligature, std::forward (ds)...)); + case Noncontextual: return_trace (c->dispatch (u.noncontextual, std::forward (ds)...)); + case Insertion: return_trace (c->dispatch (u.insertion, std::forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_sanitize_with_object_t with (&c->sanitizer, this); + return_trace (dispatch (c)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!length.sanitize (c) || + length <= min_size || + !c->check_range (this, length)) + return_trace (false); + + hb_sanitize_with_object_t with (c, this); + return_trace (dispatch (c)); + } + + protected: + HBUINT length; /* Total subtable length, including this header. */ + HBUINT coverage; /* Coverage flags and subtable type. */ + HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */ + union { + RearrangementSubtable rearrangement; + ContextualSubtable contextual; + LigatureSubtable ligature; + NoncontextualSubtable noncontextual; + InsertionSubtable insertion; + } u; + public: + DEFINE_SIZE_MIN (2 * sizeof (HBUINT) + 4); +}; + +template +struct Chain +{ + typedef typename Types::HBUINT HBUINT; + + hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const + { + hb_mask_t flags = defaultFlags; + { + unsigned int count = featureCount; + for (unsigned i = 0; i < count; i++) + { + const Feature &feature = featureZ[i]; + hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType; + hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting; + retry: + // Check whether this type/setting pair was requested in the map, and if so, apply its flags. + // (The search here only looks at the type and setting fields of feature_info_t.) + hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 }; + if (map->current_features.bsearch (info)) + { + flags &= feature.disableFlags; + flags |= feature.enableFlags; + } + else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE && setting == HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS) + { + /* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */ + type = HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE; + setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS; + goto retry; + } +#ifndef HB_NO_AAT + else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE && setting && + /* TODO: Rudimentary language matching. */ + hb_language_matches (map->face->table.ltag->get_language (setting - 1), map->props.language)) + { + flags &= feature.disableFlags; + flags |= feature.enableFlags; + } +#endif + } + } + return flags; + } + + void apply (hb_aat_apply_context_t *c) const + { + const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount)); + unsigned int count = subtableCount; + for (unsigned int i = 0; i < count; i++) + { + bool reverse; + + if (hb_none (hb_iter (c->range_flags) | + hb_map ([&subtable] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable->subFeatureFlags & (_.flags); }))) + goto skip; + c->subtable_flags = subtable->subFeatureFlags; + + if (!(subtable->get_coverage() & ChainSubtable::AllDirections) && + HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != + bool (subtable->get_coverage() & ChainSubtable::Vertical)) + goto skip; + + /* Buffer contents is always in logical direction. Determine if + * we need to reverse before applying this subtable. We reverse + * back after if we did reverse indeed. + * + * Quoting the spac: + * """ + * Bits 28 and 30 of the coverage field control the order in which + * glyphs are processed when the subtable is run by the layout engine. + * Bit 28 is used to indicate if the glyph processing direction is + * the same as logical order or layout order. Bit 30 is used to + * indicate whether glyphs are processed forwards or backwards within + * that order. + + Bit 30 Bit 28 Interpretation for Horizontal Text + 0 0 The subtable is processed in layout order + (the same order as the glyphs, which is + always left-to-right). + 1 0 The subtable is processed in reverse layout order + (the order opposite that of the glyphs, which is + always right-to-left). + 0 1 The subtable is processed in logical order + (the same order as the characters, which may be + left-to-right or right-to-left). + 1 1 The subtable is processed in reverse logical order + (the order opposite that of the characters, which + may be right-to-left or left-to-right). + */ + reverse = subtable->get_coverage () & ChainSubtable::Logical ? + bool (subtable->get_coverage () & ChainSubtable::Backwards) : + bool (subtable->get_coverage () & ChainSubtable::Backwards) != + HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); + + if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index)) + goto skip; + + if (reverse) + c->buffer->reverse (); + + subtable->apply (c); + + if (reverse) + c->buffer->reverse (); + + (void) c->buffer->message (c->font, "end chainsubtable %u", c->lookup_index); + + if (unlikely (!c->buffer->successful)) return; + + skip: + subtable = &StructAfter> (*subtable); + c->set_lookup_index (c->lookup_index + 1); + } + } + + unsigned int get_size () const { return length; } + + bool sanitize (hb_sanitize_context_t *c, unsigned int version HB_UNUSED) const + { + TRACE_SANITIZE (this); + if (!length.sanitize (c) || + length < min_size || + !c->check_range (this, length)) + return_trace (false); + + if (!c->check_array (featureZ.arrayZ, featureCount)) + return_trace (false); + + const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount)); + unsigned int count = subtableCount; + for (unsigned int i = 0; i < count; i++) + { + if (!subtable->sanitize (c)) + return_trace (false); + subtable = &StructAfter> (*subtable); + } + + return_trace (true); + } + + protected: + HBUINT32 defaultFlags; /* The default specification for subtables. */ + HBUINT32 length; /* Total byte count, including this header. */ + HBUINT featureCount; /* Number of feature subtable entries. */ + HBUINT subtableCount; /* The number of subtables in the chain. */ + + UnsizedArrayOf featureZ; /* Features. */ +/*ChainSubtable firstSubtable;*//* Subtables. */ +/*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */ + + public: + DEFINE_SIZE_MIN (8 + 2 * sizeof (HBUINT)); +}; + + +/* + * The 'mort'/'morx' Table + */ + +template +struct mortmorx +{ + static constexpr hb_tag_t tableTag = TAG; + + bool has_data () const { return version != 0; } + + void compile_flags (const hb_aat_map_builder_t *mapper, + hb_aat_map_t *map) const + { + const Chain *chain = &firstChain; + unsigned int count = chainCount; + if (unlikely (!map->chain_flags.resize (count))) + return; + for (unsigned int i = 0; i < count; i++) + { + map->chain_flags[i].push (hb_aat_map_t::range_flags_t {chain->compile_flags (mapper), + mapper->range_first, + mapper->range_last}); + chain = &StructAfter> (*chain); + } + } + + void apply (hb_aat_apply_context_t *c, + const hb_aat_map_t &map) const + { + if (unlikely (!c->buffer->successful)) return; + + c->buffer->unsafe_to_concat (); + + c->set_lookup_index (0); + const Chain *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + c->range_flags = &map.chain_flags[i]; + chain->apply (c); + if (unlikely (!c->buffer->successful)) return; + chain = &StructAfter> (*chain); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!version.sanitize (c) || !version || !chainCount.sanitize (c)) + return_trace (false); + + const Chain *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + if (!chain->sanitize (c, version)) + return_trace (false); + chain = &StructAfter> (*chain); + } + + return_trace (true); + } + + protected: + HBUINT16 version; /* Version number of the glyph metamorphosis table. + * 1, 2, or 3. */ + HBUINT16 unused; /* Set to 0. */ + HBUINT32 chainCount; /* Number of metamorphosis chains contained in this + * table. */ + Chain firstChain; /* Chains. */ + + public: + DEFINE_SIZE_MIN (8); +}; + +struct morx : mortmorx {}; +struct mort : mortmorx {}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-opbd-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-opbd-table.hh new file mode 100644 index 00000000000..51b650fc33c --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-opbd-table.hh @@ -0,0 +1,173 @@ +/* + * Copyright ยฉ 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_LAYOUT_OPBD_TABLE_HH +#define HB_AAT_LAYOUT_OPBD_TABLE_HH + +#include "hb-aat-layout-common.hh" +#include "hb-open-type.hh" + +/* + * opbd -- Optical Bounds + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html + */ +#define HB_AAT_TAG_opbd HB_TAG('o','p','b','d') + + +namespace AAT { + +struct OpticalBounds +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + FWORD leftSide; + FWORD topSide; + FWORD rightSide; + FWORD bottomSide; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct opbdFormat0 +{ + bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id, + hb_glyph_extents_t *extents, const void *base) const + { + const Offset16To *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ()); + if (!bounds_offset) return false; + const OpticalBounds &bounds = base+*bounds_offset; + + if (extents) + *extents = { + font->em_scale_x (bounds.leftSide), + font->em_scale_y (bounds.topSide), + font->em_scale_x (bounds.rightSide), + font->em_scale_y (bounds.bottomSide) + }; + return true; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base))); + } + + protected: + Lookup> + lookupTable; /* Lookup table associating glyphs with the four + * int16 values for the left-side, top-side, + * right-side, and bottom-side optical bounds. */ + public: + DEFINE_SIZE_MIN (2); +}; + +struct opbdFormat1 +{ + bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id, + hb_glyph_extents_t *extents, const void *base) const + { + const Offset16To *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ()); + if (!bounds_offset) return false; + const OpticalBounds &bounds = base+*bounds_offset; + + hb_position_t left = 0, top = 0, right = 0, bottom = 0, ignore; + if (font->get_glyph_contour_point (glyph_id, bounds.leftSide, &left, &ignore) || + font->get_glyph_contour_point (glyph_id, bounds.topSide, &ignore, &top) || + font->get_glyph_contour_point (glyph_id, bounds.rightSide, &right, &ignore) || + font->get_glyph_contour_point (glyph_id, bounds.bottomSide, &ignore, &bottom)) + { + if (extents) + *extents = {left, top, right, bottom}; + return true; + } + return false; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base))); + } + + protected: + Lookup> + lookupTable; /* Lookup table associating glyphs with the four + * int16 values for the left-side, top-side, + * right-side, and bottom-side optical bounds. */ + public: + DEFINE_SIZE_MIN (2); +}; + +struct opbd +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_opbd; + + bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id, + hb_glyph_extents_t *extents) const + { + switch (format) + { + case 0: return u.format0.get_bounds (font, glyph_id, extents, this); + case 1: return u.format1.get_bounds (font, glyph_id, extents, this); + default:return false; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this) || version.major != 1)) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, this)); + case 1: return_trace (u.format1.sanitize (c, this)); + default:return_trace (true); + } + } + + protected: + FixedVersion<>version; /* Version number of the optical bounds + * table (0x00010000 for the current version). */ + HBUINT16 format; /* Format of the optical bounds table. + * Format 0 indicates distance and Format 1 indicates + * control point. */ + union { + opbdFormat0 format0; + opbdFormat1 format1; + } u; + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_OPBD_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-trak-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-trak-table.hh new file mode 100644 index 00000000000..2ba9355b06b --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-trak-table.hh @@ -0,0 +1,230 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * Copyright ยฉ 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_TRAK_TABLE_HH +#define HB_AAT_LAYOUT_TRAK_TABLE_HH + +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout.hh" +#include "hb-open-type.hh" + +/* + * trak -- Tracking + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html + */ +#define HB_AAT_TAG_trak HB_TAG('t','r','a','k') + + +namespace AAT { + + +struct TrackTableEntry +{ + friend struct TrackData; + + float get_track_value () const { return track.to_float (); } + + int get_value (const void *base, unsigned int index, + unsigned int table_size) const + { return (base+valuesZ).as_array (table_size)[index]; } + + public: + bool sanitize (hb_sanitize_context_t *c, const void *base, + unsigned int table_size) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (valuesZ.sanitize (c, base, table_size)))); + } + + protected: + F16DOT16 track; /* Track value for this record. */ + NameID trackNameID; /* The 'name' table index for this track. + * (a short word or phrase like "loose" + * or "very tight") */ + NNOffset16To> + valuesZ; /* Offset from start of tracking table to + * per-size tracking values for this track. */ + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct TrackData +{ + float interpolate_at (unsigned int idx, + float target_size, + const TrackTableEntry &trackTableEntry, + const void *base) const + { + unsigned int sizes = nSizes; + hb_array_t size_table ((base+sizeTable).arrayZ, sizes); + + float s0 = size_table[idx].to_float (); + float s1 = size_table[idx + 1].to_float (); + float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0); + return t * trackTableEntry.get_value (base, idx + 1, sizes) + + (1.f - t) * trackTableEntry.get_value (base, idx, sizes); + } + + int get_tracking (const void *base, float ptem) const + { + /* + * Choose track. + */ + const TrackTableEntry *trackTableEntry = nullptr; + unsigned int count = nTracks; + for (unsigned int i = 0; i < count; i++) + { + /* Note: Seems like the track entries are sorted by values. But the + * spec doesn't explicitly say that. It just mentions it in the example. */ + + /* For now we only seek for track entries with zero tracking value */ + + if (trackTable[i].get_track_value () == 0.f) + { + trackTableEntry = &trackTable[i]; + break; + } + } + if (!trackTableEntry) return 0.; + + /* + * Choose size. + */ + unsigned int sizes = nSizes; + if (!sizes) return 0.; + if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes); + + hb_array_t size_table ((base+sizeTable).arrayZ, sizes); + unsigned int size_index; + for (size_index = 0; size_index < sizes - 1; size_index++) + if (size_table[size_index].to_float () >= ptem) + break; + + return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem, + *trackTableEntry, base)); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + sizeTable.sanitize (c, base, nSizes) && + trackTable.sanitize (c, nTracks, base, nSizes))); + } + + protected: + HBUINT16 nTracks; /* Number of separate tracks included in this table. */ + HBUINT16 nSizes; /* Number of point sizes included in this table. */ + NNOffset32To> + sizeTable; /* Offset from start of the tracking table to + * Array[nSizes] of size values.. */ + UnsizedArrayOf + trackTable; /* Array[nTracks] of TrackTableEntry records. */ + + public: + DEFINE_SIZE_ARRAY (8, trackTable); +}; + +struct trak +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_trak; + + bool has_data () const { return version.to_int (); } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + hb_mask_t trak_mask = c->plan->trak_mask; + + const float ptem = c->font->ptem; + if (unlikely (ptem <= 0.f)) + return_trace (false); + + hb_buffer_t *buffer = c->buffer; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + const TrackData &trackData = this+horizData; + int tracking = trackData.get_tracking (this, ptem); + hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2); + hb_position_t advance_to_add = c->font->em_scalef_x (tracking); + foreach_grapheme (buffer, start, end) + { + if (!(buffer->info[start].mask & trak_mask)) continue; + buffer->pos[start].x_advance += advance_to_add; + buffer->pos[start].x_offset += offset_to_add; + } + } + else + { + const TrackData &trackData = this+vertData; + int tracking = trackData.get_tracking (this, ptem); + hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2); + hb_position_t advance_to_add = c->font->em_scalef_y (tracking); + foreach_grapheme (buffer, start, end) + { + if (!(buffer->info[start].mask & trak_mask)) continue; + buffer->pos[start].y_advance += advance_to_add; + buffer->pos[start].y_offset += offset_to_add; + } + } + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + return_trace (likely (c->check_struct (this) && + version.major == 1 && + horizData.sanitize (c, this, this) && + vertData.sanitize (c, this, this))); + } + + protected: + FixedVersion<>version; /* Version of the tracking table + * (0x00010000u for version 1.0). */ + HBUINT16 format; /* Format of the tracking table (set to 0). */ + Offset16To + horizData; /* Offset from start of tracking table to TrackData + * for horizontal text (or 0 if none). */ + Offset16To + vertData; /* Offset from start of tracking table to TrackData + * for vertical text (or 0 if none). */ + HBUINT16 reserved; /* Reserved. Set to 0. */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_TRAK_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.cc b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.cc new file mode 100644 index 00000000000..c9147ff73b1 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.cc @@ -0,0 +1,440 @@ +/* + * Copyright ยฉ 2017 Google, Inc. + * Copyright ยฉ 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-aat-layout.hh" +#include "hb-aat-layout-ankr-table.hh" +#include "hb-aat-layout-bsln-table.hh" // Just so we compile it; unused otherwise. +#include "hb-aat-layout-feat-table.hh" +#include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise. +#include "hb-aat-layout-kerx-table.hh" +#include "hb-aat-layout-morx-table.hh" +#include "hb-aat-layout-trak-table.hh" +#include "hb-aat-ltag-table.hh" + + +/* + * hb_aat_apply_context_t + */ + +/* Note: This context is used for kerning, even without AAT, hence the condition. */ +#if !defined(HB_NO_AAT) || !defined(HB_NO_OT_KERN) + +AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_, + hb_font_t *font_, + hb_buffer_t *buffer_, + hb_blob_t *blob) : + plan (plan_), + font (font_), + face (font->face), + buffer (buffer_), + sanitizer (), + ankr_table (&Null (AAT::ankr)), + gdef_table (face->table.GDEF->table), + lookup_index (0) +{ + sanitizer.init (blob); + sanitizer.set_num_glyphs (face->get_num_glyphs ()); + sanitizer.start_processing (); + sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX); +} + +AAT::hb_aat_apply_context_t::~hb_aat_apply_context_t () +{ sanitizer.end_processing (); } + +void +AAT::hb_aat_apply_context_t::set_ankr_table (const AAT::ankr *ankr_table_) +{ ankr_table = ankr_table_; } + +#endif + + +/** + * SECTION:hb-aat-layout + * @title: hb-aat-layout + * @short_description: Apple Advanced Typography Layout + * @include: hb-aat.h + * + * Functions for querying AAT Layout features in the font face. + * + * HarfBuzz supports all of the AAT tables used to implement shaping. Other + * AAT tables and their associated features are not supported. + **/ + + +#if !defined(HB_NO_AAT) || defined(HAVE_CORETEXT) + +/* Mapping from OpenType feature tags to AAT feature names and selectors. + * + * Table data courtesy of Apple. Converted from mnemonics to integers + * when moving to this file. */ +static const hb_aat_feature_mapping_t feature_mappings[] = +{ + {HB_TAG ('a','f','r','c'), HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS}, + {HB_TAG ('c','2','p','c'), HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE}, + {HB_TAG ('c','2','s','c'), HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE}, + {HB_TAG ('c','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF}, + {HB_TAG ('c','a','s','e'), HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF}, + {HB_TAG ('c','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF}, + {HB_TAG ('c','p','s','p'), HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF}, + {HB_TAG ('c','s','w','h'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF}, + {HB_TAG ('d','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF}, + {HB_TAG ('e','x','p','t'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('f','r','a','c'), HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS}, + {HB_TAG ('f','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('h','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('h','i','s','t'), (hb_aat_layout_feature_type_t) 40, (hb_aat_layout_feature_selector_t) 0, (hb_aat_layout_feature_selector_t) 1}, + {HB_TAG ('h','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF}, + {HB_TAG ('h','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF}, + {HB_TAG ('h','n','g','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION, HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION}, + {HB_TAG ('h','o','j','o'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('h','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('i','t','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN, HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF}, + {HB_TAG ('j','p','0','4'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('j','p','7','8'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('j','p','8','3'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('j','p','9','0'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('l','i','g','a'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF}, + {HB_TAG ('l','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS, (hb_aat_layout_feature_selector_t) 2}, + {HB_TAG ('m','g','r','k'), HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS, HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF}, + {HB_TAG ('n','l','c','k'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('o','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS, (hb_aat_layout_feature_selector_t) 2}, + {HB_TAG ('o','r','d','n'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('p','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('p','c','a','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE}, + {HB_TAG ('p','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('p','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS, (hb_aat_layout_feature_selector_t) 4}, + {HB_TAG ('p','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('q','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('r','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF}, + {HB_TAG ('r','u','b','y'), HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF}, + {HB_TAG ('s','i','n','f'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('s','m','c','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE}, + {HB_TAG ('s','m','p','l'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('s','s','0','1'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF}, + {HB_TAG ('s','s','0','2'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF}, + {HB_TAG ('s','s','0','3'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF}, + {HB_TAG ('s','s','0','4'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF}, + {HB_TAG ('s','s','0','5'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF}, + {HB_TAG ('s','s','0','6'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF}, + {HB_TAG ('s','s','0','7'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF}, + {HB_TAG ('s','s','0','8'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF}, + {HB_TAG ('s','s','0','9'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF}, + {HB_TAG ('s','s','1','0'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF}, + {HB_TAG ('s','s','1','1'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF}, + {HB_TAG ('s','s','1','2'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF}, + {HB_TAG ('s','s','1','3'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF}, + {HB_TAG ('s','s','1','4'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF}, + {HB_TAG ('s','s','1','5'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF}, + {HB_TAG ('s','s','1','6'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF}, + {HB_TAG ('s','s','1','7'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF}, + {HB_TAG ('s','s','1','8'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF}, + {HB_TAG ('s','s','1','9'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF}, + {HB_TAG ('s','s','2','0'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF}, + {HB_TAG ('s','u','b','s'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('s','u','p','s'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('s','w','s','h'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF}, + {HB_TAG ('t','i','t','l'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS}, + {HB_TAG ('t','n','a','m'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('t','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS, (hb_aat_layout_feature_selector_t) 4}, + {HB_TAG ('t','r','a','d'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('t','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('u','n','i','c'), HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE, (hb_aat_layout_feature_selector_t) 14, (hb_aat_layout_feature_selector_t) 15}, + {HB_TAG ('v','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('v','e','r','t'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF}, + {HB_TAG ('v','h','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('v','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF}, + {HB_TAG ('v','p','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('v','r','t','2'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF}, + {HB_TAG ('v','r','t','r'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION, (hb_aat_layout_feature_selector_t) 2, (hb_aat_layout_feature_selector_t) 3}, + {HB_TAG ('z','e','r','o'), HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS, HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF}, +}; + +/** + * hb_aat_layout_find_feature_mapping: + * @tag: The requested #hb_tag_t feature tag + * + * Fetches the AAT feature-and-selector combination that corresponds + * to a given OpenType feature tag. + * + * Return value: the AAT features and selectors corresponding to the + * OpenType feature tag queried + * + **/ +const hb_aat_feature_mapping_t * +hb_aat_layout_find_feature_mapping (hb_tag_t tag) +{ + return hb_sorted_array (feature_mappings).bsearch (tag); +} +#endif + + +#ifndef HB_NO_AAT + +/* + * mort/morx/kerx/trak + */ + + +void +hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper, + hb_aat_map_t *map) +{ + const AAT::morx& morx = *mapper->face->table.morx; + if (morx.has_data ()) + { + morx.compile_flags (mapper, map); + return; + } + + const AAT::mort& mort = *mapper->face->table.mort; + if (mort.has_data ()) + { + mort.compile_flags (mapper, map); + return; + } +} + + +/** + * hb_aat_layout_has_substitution: + * @face: #hb_face_t to work upon + * + * Tests whether the specified face includes any substitutions in the + * `morx` or `mort` tables. + * + * Note: does not examine the `GSUB` table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.3.0 + */ +hb_bool_t +hb_aat_layout_has_substitution (hb_face_t *face) +{ + return face->table.morx->has_data () || + face->table.mort->has_data (); +} + +void +hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned num_features) +{ + hb_aat_map_builder_t builder (font->face, plan->props); + for (unsigned i = 0; i < num_features; i++) + builder.add_feature (features[i]); + hb_aat_map_t map; + builder.compile (map); + + hb_blob_t *morx_blob = font->face->table.morx.get_blob (); + const AAT::morx& morx = *morx_blob->as (); + if (morx.has_data ()) + { + AAT::hb_aat_apply_context_t c (plan, font, buffer, morx_blob); + if (!buffer->message (font, "start table morx")) return; + morx.apply (&c, map); + (void) buffer->message (font, "end table morx"); + return; + } + + hb_blob_t *mort_blob = font->face->table.mort.get_blob (); + const AAT::mort& mort = *mort_blob->as (); + if (mort.has_data ()) + { + AAT::hb_aat_apply_context_t c (plan, font, buffer, mort_blob); + if (!buffer->message (font, "start table mort")) return; + mort.apply (&c, map); + (void) buffer->message (font, "end table mort"); + return; + } +} + +void +hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + for (unsigned int i = 0; i < count; i++) + if (unlikely (info[i].codepoint == AAT::DELETED_GLYPH)) + pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; +} + +static bool +is_deleted_glyph (const hb_glyph_info_t *info) +{ + return info->codepoint == AAT::DELETED_GLYPH; +} + +void +hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer) +{ + buffer->delete_glyphs_inplace (is_deleted_glyph); +} + +/** + * hb_aat_layout_has_positioning: + * @face: #hb_face_t to work upon + * + * Tests whether the specified face includes any positioning information + * in the `kerx` table. + * + * Note: does not examine the `GPOS` table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.3.0 + */ +hb_bool_t +hb_aat_layout_has_positioning (hb_face_t *face) +{ + return face->table.kerx->has_data (); +} + +void +hb_aat_layout_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + hb_blob_t *kerx_blob = font->face->table.kerx.get_blob (); + const AAT::kerx& kerx = *kerx_blob->as (); + + AAT::hb_aat_apply_context_t c (plan, font, buffer, kerx_blob); + if (!buffer->message (font, "start table kerx")) return; + c.set_ankr_table (font->face->table.ankr.get ()); + kerx.apply (&c); + (void) buffer->message (font, "end table kerx"); +} + + +/** + * hb_aat_layout_has_tracking: + * @face:: #hb_face_t to work upon + * + * Tests whether the specified face includes any tracking information + * in the `trak` table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.3.0 + */ +hb_bool_t +hb_aat_layout_has_tracking (hb_face_t *face) +{ + return face->table.trak->has_data (); +} + +void +hb_aat_layout_track (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + const AAT::trak& trak = *font->face->table.trak; + + AAT::hb_aat_apply_context_t c (plan, font, buffer); + trak.apply (&c); +} + +/** + * hb_aat_layout_get_feature_types: + * @face: #hb_face_t to work upon + * @start_offset: offset of the first feature type to retrieve + * @feature_count: (inout) (optional): Input = the maximum number of feature types to return; + * Output = the actual number of feature types returned (may be zero) + * @features: (out caller-allocates) (array length=feature_count): Array of feature types found + * + * Fetches a list of the AAT feature types included in the specified face. + * + * Return value: Number of all available feature types. + * + * Since: 2.2.0 + */ +unsigned int +hb_aat_layout_get_feature_types (hb_face_t *face, + unsigned int start_offset, + unsigned int *feature_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_type_t *features /* OUT. May be NULL. */) +{ + return face->table.feat->get_feature_types (start_offset, feature_count, features); +} + +/** + * hb_aat_layout_feature_type_get_name_id: + * @face: #hb_face_t to work upon + * @feature_type: The #hb_aat_layout_feature_type_t of the requested feature type + * + * Fetches the name identifier of the specified feature type in the face's `name` table. + * + * Return value: Name identifier of the requested feature type + * + * Since: 2.2.0 + */ +hb_ot_name_id_t +hb_aat_layout_feature_type_get_name_id (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type) +{ + return face->table.feat->get_feature_name_id (feature_type); +} + +/** + * hb_aat_layout_feature_type_get_selector_infos: + * @face: #hb_face_t to work upon + * @feature_type: The #hb_aat_layout_feature_type_t of the requested feature type + * @start_offset: offset of the first feature type to retrieve + * @selector_count: (inout) (optional): Input = the maximum number of selectors to return; + * Output = the actual number of selectors returned (may be zero) + * @selectors: (out caller-allocates) (array length=selector_count) (optional): + * A buffer pointer. The selectors available for the feature type queries. + * @default_index: (out) (optional): The index of the feature's default selector, if any + * + * Fetches a list of the selectors available for the specified feature in the given face. + * + * If upon return, @default_index is set to #HB_AAT_LAYOUT_NO_SELECTOR_INDEX, then + * the feature type is non-exclusive. Otherwise, @default_index is the index of + * the selector that is selected by default. + * + * Return value: Number of all available feature selectors + * + * Since: 2.2.0 + */ +unsigned int +hb_aat_layout_feature_type_get_selector_infos (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type, + unsigned int start_offset, + unsigned int *selector_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *default_index /* OUT. May be NULL. */) +{ + return face->table.feat->get_selector_infos (feature_type, start_offset, selector_count, selectors, default_index); +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.h b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.h new file mode 100644 index 00000000000..9af27400886 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.h @@ -0,0 +1,795 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#if !defined(HB_AAT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_AAT_LAYOUT_H +#define HB_AAT_LAYOUT_H + +#include "hb.h" + +#include "hb-ot.h" + +HB_BEGIN_DECLS + +/** + * hb_aat_layout_feature_type_t: + * @HB_AAT_LAYOUT_FEATURE_TYPE_INVALID: Initial, unset feature type + * @HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC: [All Typographic Features](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type0) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES: [Ligatures](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type1) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION: [Cursive Connection](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type2) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE: [Letter Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type3) + * @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION: [Vertical Substitution](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type4) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT: [Linguistic Rearrangement](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type5) + * @HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING: [Number Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type6) + * @HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE: [Smart Swash](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type8) + * @HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE: [Diacritics](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type9) + * @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION: [Vertical Position](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type10) + * @HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS: [Fractions](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type11) + * @HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE: [Overlapping Characters](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type13) + * @HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS: [Typographic Extras](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type14) + * @HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS: [Mathematical Extras](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type15) + * @HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE: [Ornament Sets](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type16) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES: [Character Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type17) + * @HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE: [Design Complexity](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type18) + * @HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS: [Style Options](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type19) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE: [Character Shape](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type20) + * @HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE: [Number Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type21) + * @HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING: [Text Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type22) + * @HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION: [Transliteration](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type23) + * @HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE: [Annotation](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type24) + * @HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE: [Kana Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type25) + * @HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE: [Ideographic Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type26) + * @HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE: [Unicode Decomposition](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type27) + * @HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA: [Ruby Kana](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type28) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE: [CJK Symbol Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type29) + * @HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE: [Ideographic Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type30) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE: [CJK Vertical Roman Placement](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type31) + * @HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN: [Italic CJK Roman](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type32) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT: [Case Sensitive Layout](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type33) + * @HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA: [Alternate Kana](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type34) + * @HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES: [Stylistic Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type35) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES: [Contextual Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type36) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE: [Lower Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type37) + * @HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE: [Upper Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type38) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE: [Language Tag](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type39) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE: [CJK Roman Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type103) + * + * The possible feature types defined for AAT shaping, from Apple [Font Feature Registry](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html). + * + * Since: 2.2.0 + */ +typedef enum +{ + HB_AAT_LAYOUT_FEATURE_TYPE_INVALID = 0xFFFF, + + HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC = 0, + HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES = 1, + HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION = 2, + HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE = 3, + HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION = 4, + HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT = 5, + HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING = 6, + HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE = 8, + HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE = 9, + HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION = 10, + HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS = 11, + HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE = 13, + HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS = 14, + HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS = 15, + HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE = 16, + HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES = 17, + HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE = 18, + HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS = 19, + HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE = 20, + HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE = 21, + HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING = 22, + HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION = 23, + HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE = 24, + HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE = 25, + HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE = 26, + HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE = 27, + HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA = 28, + HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE = 29, + HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE = 30, + HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE = 31, + HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN = 32, + HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT = 33, + HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA = 34, + HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES = 35, + HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES = 36, + HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE = 37, + HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE = 38, + HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE = 39, + HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE = 103, + + /*< private >*/ + _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_aat_layout_feature_type_t; + +/** + * hb_aat_layout_feature_selector_t: + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID: Initial, unset feature selector + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UNCONNECTED: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PARTIALLY_CONNECTED: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CURSIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_AND_LOWER_CASE: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_CAPS: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_LOWER_CASE: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS_AND_SMALL_CAPS: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SHOW_DIACRITICS: for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HIDE_DIACRITICS: for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DECOMPOSE_DIACRITICS: for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ORNAMENTS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DINGBATS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PI_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FLEURONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DECORATIVE_BORDERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INTERNATIONAL_SYMBOLS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MATH_SYMBOLS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ALTERNATES: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL1: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL2: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL3: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL4: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL5: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DISPLAY_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ENGRAVED_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ILLUMINATED_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TALL_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FOUR: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HIRAGANA_TO_KATAKANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_KATAKANA_TO_HIRAGANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_KANA_TO_ROMANIZATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_HIRAGANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_KATAKANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROUNDED_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CIRCLE_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_CIRCLE_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PARENTHESIS_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIOD_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMAN_NUMERAL_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAMOND_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_ROUNDED_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_KANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_KANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_IDEOGRAPHS: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_IDEOGRAPHS: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_IDEOGRAPHS: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_RUBY_KANA: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF instead + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON instead + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_SYMBOL_ALTERNATIVES: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FOUR: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_IDEOGRAPHIC_ALTERNATIVES: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FOUR: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_CENTERED: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_HBASELINE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_ITALIC_ROMAN: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF instead + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON instead + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLISTIC_ALTERNATES: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE: for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE: for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE + * + * The selectors defined for specifying AAT feature settings. + * + * Since: 2.2.0 + */ +typedef enum +{ + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID = 0xFFFF, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_OFF = 11, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_ON = 12, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_OFF = 13, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_ON = 14, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_OFF = 15, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_ON = 16, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_OFF = 17, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON = 18, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF = 19, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON = 20, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF = 21, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_UNCONNECTED = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PARTIALLY_CONNECTED = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CURSIVE = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_AND_LOWER_CASE = 0, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_CAPS = 1, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_LOWER_CASE = 2, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS = 3, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS = 4, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS_AND_SMALL_CAPS = 5, /* deprecated */ + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_NUMBERS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_NUMBERS = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_OFF = 9, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_SHOW_DIACRITICS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HIDE_DIACRITICS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DECOMPOSE_DIACRITICS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS = 4, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_OFF = 11, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF = 11, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ORNAMENTS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DINGBATS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PI_CHARACTERS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FLEURONS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DECORATIVE_BORDERS = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INTERNATIONAL_SYMBOLS = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MATH_SYMBOLS = 6, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ALTERNATES = 0, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL1 = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL2 = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL3 = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL4 = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL5 = 4, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DISPLAY_TEXT = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ENGRAVED_TEXT = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ILLUMINATED_CAPS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TALL_CAPS = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_ONE = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_TWO = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_THREE = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FOUR = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FIVE = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS = 11, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS = 12, + HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS = 13, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS = 14, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT = 6, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HIRAGANA_TO_KATAKANA = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_KATAKANA_TO_HIRAGANA = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_KANA_TO_ROMANIZATION = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_HIRAGANA = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_KATAKANA = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_ONE = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_TWO = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_THREE = 9, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ANNOTATION = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_BOX_ANNOTATION = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROUNDED_BOX_ANNOTATION = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CIRCLE_ANNOTATION = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_CIRCLE_ANNOTATION = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PARENTHESIS_ANNOTATION = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIOD_ANNOTATION = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMAN_NUMERAL_ANNOTATION = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAMOND_ANNOTATION = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_BOX_ANNOTATION = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_ROUNDED_BOX_ANNOTATION= 10, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_KANA = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_KANA = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_IDEOGRAPHS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_IDEOGRAPHS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_IDEOGRAPHS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_OFF = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_RUBY_KANA = 0, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA = 1, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_SYMBOL_ALTERNATIVES = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_ONE = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_TWO = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_THREE = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FOUR = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FIVE = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_IDEOGRAPHIC_ALTERNATIVES = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_ONE = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_TWO = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_THREE = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FOUR = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FIVE = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_CENTERED = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_HBASELINE = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_ITALIC_ROMAN = 0, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN = 1, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLISTIC_ALTERNATES = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF = 11, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON = 12, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF = 13, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON = 14, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF = 15, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON = 16, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF = 17, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON = 18, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF = 19, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON = 20, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF = 21, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON = 22, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF = 23, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON = 24, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF = 25, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON = 26, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF = 27, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON = 28, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF = 29, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON = 30, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF = 31, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON = 32, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF = 33, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON = 34, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF = 35, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON = 36, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF = 37, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON = 38, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF = 39, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON = 40, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF = 41, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF= 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_CJK_ROMAN = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_CJK_ROMAN = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN = 3, + + /*< private >*/ + _HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_aat_layout_feature_selector_t; + +HB_EXTERN unsigned int +hb_aat_layout_get_feature_types (hb_face_t *face, + unsigned int start_offset, + unsigned int *feature_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_type_t *features /* OUT. May be NULL. */); + +HB_EXTERN hb_ot_name_id_t +hb_aat_layout_feature_type_get_name_id (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type); + +/** + * hb_aat_layout_feature_selector_info_t: + * @name_id: The selector's name identifier + * @enable: The value to turn the selector on + * @disable: The value to turn the selector off + * + * Structure representing a setting for an #hb_aat_layout_feature_type_t. + */ +typedef struct hb_aat_layout_feature_selector_info_t { + hb_ot_name_id_t name_id; + hb_aat_layout_feature_selector_t enable; + hb_aat_layout_feature_selector_t disable; + /*< private >*/ + unsigned int reserved; +} hb_aat_layout_feature_selector_info_t; + +/** + * HB_AAT_LAYOUT_NO_SELECTOR_INDEX + * + * Used when getting or setting AAT feature selectors. Indicates that + * there is no selector index corresponding to the selector of interest. + * + */ +#define HB_AAT_LAYOUT_NO_SELECTOR_INDEX 0xFFFFu + +HB_EXTERN unsigned int +hb_aat_layout_feature_type_get_selector_infos (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type, + unsigned int start_offset, + unsigned int *selector_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *default_index /* OUT. May be NULL. */); + + +/* + * morx/mort + */ + +HB_EXTERN hb_bool_t +hb_aat_layout_has_substitution (hb_face_t *face); + + +/* + * kerx + */ + +HB_EXTERN hb_bool_t +hb_aat_layout_has_positioning (hb_face_t *face); + + +/* + * trak + */ + +HB_EXTERN hb_bool_t +hb_aat_layout_has_tracking (hb_face_t *face); + + +HB_END_DECLS + +#endif /* HB_AAT_LAYOUT_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.hh new file mode 100644 index 00000000000..15c382aa92d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.hh @@ -0,0 +1,77 @@ +/* + * Copyright ยฉ 2017 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_HH +#define HB_AAT_LAYOUT_HH + +#include "hb.hh" + +#include "hb-ot-shape.hh" +#include "hb-aat-ltag-table.hh" + +struct hb_aat_feature_mapping_t +{ + hb_tag_t otFeatureTag; + hb_aat_layout_feature_type_t aatFeatureType; + hb_aat_layout_feature_selector_t selectorToEnable; + hb_aat_layout_feature_selector_t selectorToDisable; + + int cmp (hb_tag_t key) const + { return key < otFeatureTag ? -1 : key > otFeatureTag ? 1 : 0; } +}; + +HB_INTERNAL const hb_aat_feature_mapping_t * +hb_aat_layout_find_feature_mapping (hb_tag_t tag); + +HB_INTERNAL void +hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper, + hb_aat_map_t *map); + +HB_INTERNAL void +hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned num_features); + +HB_INTERNAL void +hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_track (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + + +#endif /* HB_AAT_LAYOUT_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-ltag-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-ltag-table.hh new file mode 100644 index 00000000000..6d771e1513e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-ltag-table.hh @@ -0,0 +1,92 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_LTAG_TABLE_HH +#define HB_AAT_LTAG_TABLE_HH + +#include "hb-open-type.hh" + +/* + * ltag -- Language Tag + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ltag.html + */ +#define HB_AAT_TAG_ltag HB_TAG('l','t','a','g') + + +namespace AAT { + +using namespace OT; + + +struct FTStringRange +{ + friend struct ltag; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && (base+tag).sanitize (c, length)); + } + + protected: + NNOffset16To> + tag; /* Offset from the start of the table to + * the beginning of the string */ + HBUINT16 length; /* String length (in bytes) */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct ltag +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_ltag; + + hb_language_t get_language (unsigned int i) const + { + const FTStringRange &range = tagRanges[i]; + return hb_language_from_string ((const char *) (this+range.tag).arrayZ, + range.length); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version >= 1 && + tagRanges.sanitize (c, this))); + } + + protected: + HBUINT32 version; /* Table version; currently 1 */ + HBUINT32 flags; /* Table flags; currently none defined */ + Array32Of + tagRanges; /* Range for each tag's string */ + public: + DEFINE_SIZE_ARRAY (12, tagRanges); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LTAG_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-map.cc b/src/3rdparty/harfbuzz-ng/src/hb-aat-map.cc new file mode 100644 index 00000000000..5bdb8004f2f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-map.cc @@ -0,0 +1,172 @@ +/* + * Copyright ยฉ 2009,2010 Red Hat, Inc. + * Copyright ยฉ 2010,2011,2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_AAT_SHAPE + +#include "hb-aat-map.hh" + +#include "hb-aat-layout.hh" +#include "hb-aat-layout-feat-table.hh" + + +void hb_aat_map_builder_t::add_feature (const hb_feature_t &feature) +{ + if (!face->table.feat->has_data ()) return; + + if (feature.tag == HB_TAG ('a','a','l','t')) + { + if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES)) + return; + feature_range_t *range = features.push(); + range->start = feature.start; + range->end = feature.end; + range->info.type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES; + range->info.setting = (hb_aat_layout_feature_selector_t) feature.value; + range->info.seq = features.length; + range->info.is_exclusive = true; + return; + } + + const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (feature.tag); + if (!mapping) return; + + const AAT::FeatureName* feature_name = &face->table.feat->get_feature (mapping->aatFeatureType); + if (!feature_name->has_data ()) + { + /* Special case: Chain::compile_flags will fall back to the deprecated version of + * small-caps if necessary, so we need to check for that possibility. + * https://github.com/harfbuzz/harfbuzz/issues/2307 */ + if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE && + mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS) + { + feature_name = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE); + if (!feature_name->has_data ()) return; + } + else return; + } + + feature_range_t *range = features.push(); + range->start = feature.start; + range->end = feature.end; + range->info.type = mapping->aatFeatureType; + range->info.setting = feature.value ? mapping->selectorToEnable : mapping->selectorToDisable; + range->info.seq = features.length; + range->info.is_exclusive = feature_name->is_exclusive (); +} + +void +hb_aat_map_builder_t::compile (hb_aat_map_t &m) +{ + /* Compute active features per range, and compile each. */ + + /* Sort features by start/end events. */ + hb_vector_t feature_events; + for (unsigned int i = 0; i < features.length; i++) + { + auto &feature = features[i]; + + if (features[i].start == features[i].end) + continue; + + feature_event_t *event; + + event = feature_events.push (); + event->index = features[i].start; + event->start = true; + event->feature = feature.info; + + event = feature_events.push (); + event->index = features[i].end; + event->start = false; + event->feature = feature.info; + } + feature_events.qsort (); + /* Add a strategic final event. */ + { + feature_info_t feature; + feature.seq = features.length + 1; + + feature_event_t *event = feature_events.push (); + event->index = -1; /* This value does magic. */ + event->start = false; + event->feature = feature; + } + + /* Scan events and save features for each range. */ + hb_sorted_vector_t active_features; + unsigned int last_index = 0; + for (unsigned int i = 0; i < feature_events.length; i++) + { + feature_event_t *event = &feature_events[i]; + + if (event->index != last_index) + { + /* Save a snapshot of active features and the range. */ + + /* Sort features and merge duplicates */ + current_features = active_features; + range_first = last_index; + range_last = event->index - 1; + if (current_features.length) + { + current_features.qsort (); + unsigned int j = 0; + for (unsigned int i = 1; i < current_features.length; i++) + if (current_features[i].type != current_features[j].type || + /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off + * respectively, so we mask out the low-order bit when checking for "duplicates" + * (selectors referring to the same feature setting) here. */ + (!current_features[i].is_exclusive && ((current_features[i].setting & ~1) != (current_features[j].setting & ~1)))) + current_features[++j] = current_features[i]; + current_features.shrink (j + 1); + } + + hb_aat_layout_compile_map (this, &m); + + last_index = event->index; + } + + if (event->start) + { + active_features.push (event->feature); + } else { + feature_info_t *feature = active_features.lsearch (event->feature); + if (feature) + active_features.remove_ordered (feature - active_features.arrayZ); + } + } + + for (auto &chain_flags : m.chain_flags) + // With our above setup this value is one less than desired; adjust it. + chain_flags.tail().cluster_last = HB_FEATURE_GLOBAL_END; +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-map.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-map.hh new file mode 100644 index 00000000000..cb22ffee42d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-map.hh @@ -0,0 +1,123 @@ +/* + * Copyright ยฉ 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_AAT_MAP_HH +#define HB_AAT_MAP_HH + +#include "hb.hh" + + +struct hb_aat_map_t +{ + friend struct hb_aat_map_builder_t; + + public: + struct range_flags_t + { + hb_mask_t flags; + unsigned cluster_first; + unsigned cluster_last; // end - 1 + }; + + public: + hb_vector_t> chain_flags; +}; + +struct hb_aat_map_builder_t +{ + public: + + HB_INTERNAL hb_aat_map_builder_t (hb_face_t *face_, + const hb_segment_properties_t props_) : + face (face_), + props (props_) {} + + HB_INTERNAL void add_feature (const hb_feature_t &feature); + + HB_INTERNAL void compile (hb_aat_map_t &m); + + public: + struct feature_info_t + { + hb_aat_layout_feature_type_t type; + hb_aat_layout_feature_selector_t setting; + bool is_exclusive; + unsigned seq; /* For stable sorting only. */ + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const feature_info_t *a = (const feature_info_t *) pa; + const feature_info_t *b = (const feature_info_t *) pb; + if (a->type != b->type) return (a->type < b->type ? -1 : 1); + if (!a->is_exclusive && + (a->setting & ~1) != (b->setting & ~1)) return (a->setting < b->setting ? -1 : 1); + return (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0); + } + + /* compares type & setting only */ + int cmp (const feature_info_t& f) const + { + return (f.type != type) ? (f.type < type ? -1 : 1) : + (f.setting != setting) ? (f.setting < setting ? -1 : 1) : 0; + } + }; + + struct feature_range_t + { + feature_info_t info; + unsigned start; + unsigned end; + }; + + private: + struct feature_event_t + { + unsigned int index; + bool start; + feature_info_t feature; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) { + const feature_event_t *a = (const feature_event_t *) pa; + const feature_event_t *b = (const feature_event_t *) pb; + return a->index < b->index ? -1 : a->index > b->index ? 1 : + a->start < b->start ? -1 : a->start > b->start ? 1 : + feature_info_t::cmp (&a->feature, &b->feature); + } + }; + + public: + hb_face_t *face; + hb_segment_properties_t props; + + public: + hb_sorted_vector_t features; + hb_sorted_vector_t current_features; + unsigned range_first = HB_FEATURE_GLOBAL_START; + unsigned range_last = HB_FEATURE_GLOBAL_END; +}; + + +#endif /* HB_AAT_MAP_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat.h b/src/3rdparty/harfbuzz-ng/src/hb-aat.h new file mode 100644 index 00000000000..c14313d1e2e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat.h @@ -0,0 +1,38 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_AAT_H +#define HB_AAT_H +#define HB_AAT_H_IN + +#include "hb.h" + +#include "hb-aat-layout.h" + +HB_BEGIN_DECLS + +HB_END_DECLS + +#undef HB_AAT_H_IN +#endif /* HB_AAT_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-algs.hh b/src/3rdparty/harfbuzz-ng/src/hb-algs.hh new file mode 100644 index 00000000000..13587eac017 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-algs.hh @@ -0,0 +1,1405 @@ +/* + * Copyright ยฉ 2017 Google, Inc. + * Copyright ยฉ 2019 Facebook, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_ALGS_HH +#define HB_ALGS_HH + +#include "hb.hh" +#include "hb-meta.hh" +#include "hb-null.hh" +#include "hb-number.hh" + +#include +#include +#include +#include + +/* + * Flags + */ + +/* Enable bitwise ops on enums marked as flags_t */ +/* To my surprise, looks like the function resolver is happy to silently cast + * one enum to another... So this doesn't provide the type-checking that I + * originally had in mind... :(. + * + * For MSVC warnings, see: https://github.com/harfbuzz/harfbuzz/pull/163 + */ +#ifdef _MSC_VER +# pragma warning(disable:4200) +# pragma warning(disable:4800) +#endif +#define HB_MARK_AS_FLAG_T(T) \ + extern "C++" { \ + static inline constexpr T operator | (T l, T r) { return T ((unsigned) l | (unsigned) r); } \ + static inline constexpr T operator & (T l, T r) { return T ((unsigned) l & (unsigned) r); } \ + static inline constexpr T operator ^ (T l, T r) { return T ((unsigned) l ^ (unsigned) r); } \ + static inline constexpr unsigned operator ~ (T r) { return (~(unsigned) r); } \ + static inline T& operator |= (T &l, T r) { l = l | r; return l; } \ + static inline T& operator &= (T& l, T r) { l = l & r; return l; } \ + static inline T& operator ^= (T& l, T r) { l = l ^ r; return l; } \ + } \ + static_assert (true, "") + +/* Useful for set-operations on small enums. + * For example, for testing "x โˆˆ {x1, x2, x3}" use: + * (FLAG_UNSAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3))) + */ +#define FLAG(x) (static_assert_expr ((unsigned)(x) < 32) + (((uint32_t) 1U) << (unsigned)(x))) +#define FLAG_UNSAFE(x) ((unsigned)(x) < 32 ? (((uint32_t) 1U) << (unsigned)(x)) : 0) +#define FLAG_RANGE(x,y) (static_assert_expr ((x) < (y)) + FLAG(y+1) - FLAG(x)) +#define FLAG64(x) (static_assert_expr ((unsigned)(x) < 64) + (((uint64_t) 1ULL) << (unsigned)(x))) +#define FLAG64_UNSAFE(x) ((unsigned)(x) < 64 ? (((uint64_t) 1ULL) << (unsigned)(x)) : 0) + + +/* + * Big-endian integers. + */ + +/* Endian swap, used in Windows related backends */ +static inline constexpr uint16_t hb_uint16_swap (uint16_t v) +{ return (v >> 8) | (v << 8); } +static inline constexpr uint32_t hb_uint32_swap (uint32_t v) +{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); } + +template +struct BEInt; +template +struct BEInt +{ + public: + BEInt () = default; + constexpr BEInt (Type V) : v {uint8_t (V)} {} + constexpr operator Type () const { return v; } + private: uint8_t v; +}; +template +struct BEInt +{ + public: + BEInt () = default; + constexpr BEInt (Type V) : v {uint8_t ((V >> 8) & 0xFF), + uint8_t ((V ) & 0xFF)} {} + + struct __attribute__((packed)) packed_uint16_t { uint16_t v; }; + constexpr operator Type () const + { +#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \ + defined(__BYTE_ORDER) && \ + (__BYTE_ORDER == __BIG_ENDIAN || \ + (__BYTE_ORDER == __LITTLE_ENDIAN && \ + hb_has_builtin(__builtin_bswap16))) + /* Spoon-feed the compiler a big-endian integer with alignment 1. + * https://github.com/harfbuzz/harfbuzz/pull/1398 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + return __builtin_bswap16 (((packed_uint16_t *) v)->v); +#else /* __BYTE_ORDER == __BIG_ENDIAN */ + return ((packed_uint16_t *) v)->v; +#endif +#else + return (v[0] << 8) + + (v[1] ); +#endif + } + private: uint8_t v[2]; +}; +template +struct BEInt +{ + static_assert (!std::is_signed::value, ""); + public: + BEInt () = default; + constexpr BEInt (Type V) : v {uint8_t ((V >> 16) & 0xFF), + uint8_t ((V >> 8) & 0xFF), + uint8_t ((V ) & 0xFF)} {} + + constexpr operator Type () const { return (v[0] << 16) + + (v[1] << 8) + + (v[2] ); } + private: uint8_t v[3]; +}; +template +struct BEInt +{ + public: + BEInt () = default; + constexpr BEInt (Type V) : v {uint8_t ((V >> 24) & 0xFF), + uint8_t ((V >> 16) & 0xFF), + uint8_t ((V >> 8) & 0xFF), + uint8_t ((V ) & 0xFF)} {} + + struct __attribute__((packed)) packed_uint32_t { uint32_t v; }; + constexpr operator Type () const { +#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \ + defined(__BYTE_ORDER) && \ + (__BYTE_ORDER == __BIG_ENDIAN || \ + (__BYTE_ORDER == __LITTLE_ENDIAN && \ + hb_has_builtin(__builtin_bswap32))) + /* Spoon-feed the compiler a big-endian integer with alignment 1. + * https://github.com/harfbuzz/harfbuzz/pull/1398 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + return __builtin_bswap32 (((packed_uint32_t *) v)->v); +#else /* __BYTE_ORDER == __BIG_ENDIAN */ + return ((packed_uint32_t *) v)->v; +#endif +#else + return (v[0] << 24) + + (v[1] << 16) + + (v[2] << 8) + + (v[3] ); +#endif + } + private: uint8_t v[4]; +}; + +/* Floats. */ + +/* We want our rounding towards +infinity. */ +static inline float +_hb_roundf (float x) { return floorf (x + .5f); } +#define roundf(x) _hb_roundf(x) + + +/* Encodes three unsigned integers in one 64-bit number. If the inputs have more than 21 bits, + * values will be truncated / overlap, and might not decode exactly. */ +#define HB_CODEPOINT_ENCODE3(x,y,z) (((uint64_t) (x) << 42) | ((uint64_t) (y) << 21) | (uint64_t) (z)) +#define HB_CODEPOINT_DECODE3_1(v) ((hb_codepoint_t) ((v) >> 42)) +#define HB_CODEPOINT_DECODE3_2(v) ((hb_codepoint_t) ((v) >> 21) & 0x1FFFFFu) +#define HB_CODEPOINT_DECODE3_3(v) ((hb_codepoint_t) (v) & 0x1FFFFFu) + +/* Custom encoding used by hb-ucd. */ +#define HB_CODEPOINT_ENCODE3_11_7_14(x,y,z) (((uint32_t) ((x) & 0x07FFu) << 21) | (((uint32_t) (y) & 0x007Fu) << 14) | (uint32_t) ((z) & 0x3FFFu)) +#define HB_CODEPOINT_DECODE3_11_7_14_1(v) ((hb_codepoint_t) ((v) >> 21)) +#define HB_CODEPOINT_DECODE3_11_7_14_2(v) ((hb_codepoint_t) (((v) >> 14) & 0x007Fu) | 0x0300) +#define HB_CODEPOINT_DECODE3_11_7_14_3(v) ((hb_codepoint_t) (v) & 0x3FFFu) + + +struct +{ + /* Note. This is dangerous in that if it's passed an rvalue, it returns rvalue-reference. */ + template constexpr auto + operator () (T&& v) const HB_AUTO_RETURN ( std::forward (v) ) +} +HB_FUNCOBJ (hb_identity); +struct +{ + /* Like identity(), but only retains lvalue-references. Rvalues are returned as rvalues. */ + template constexpr T& + operator () (T& v) const { return v; } + + template constexpr hb_remove_reference + operator () (T&& v) const { return v; } +} +HB_FUNCOBJ (hb_lidentity); +struct +{ + /* Like identity(), but always returns rvalue. */ + template constexpr hb_remove_reference + operator () (T&& v) const { return v; } +} +HB_FUNCOBJ (hb_ridentity); + +struct +{ + template constexpr bool + operator () (T&& v) const { return bool (std::forward (v)); } +} +HB_FUNCOBJ (hb_bool); + +struct +{ + private: + + template constexpr auto + impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ()) + + template constexpr auto + impl (const T& v, hb_priority<0>) const HB_RETURN (uint32_t, std::hash>{} (hb_deref (v))) + + public: + + template constexpr auto + operator () (const T& v) const HB_RETURN (uint32_t, impl (v, hb_prioritize)) +} +HB_FUNCOBJ (hb_hash); + + +struct +{ + private: + + /* Pointer-to-member-function. */ + template auto + impl (Appl&& a, hb_priority<2>, T &&v, Ts&&... ds) const HB_AUTO_RETURN + ((hb_deref (std::forward (v)).*std::forward (a)) (std::forward (ds)...)) + + /* Pointer-to-member. */ + template auto + impl (Appl&& a, hb_priority<1>, T &&v) const HB_AUTO_RETURN + ((hb_deref (std::forward (v))).*std::forward (a)) + + /* Operator(). */ + template auto + impl (Appl&& a, hb_priority<0>, Ts&&... ds) const HB_AUTO_RETURN + (hb_deref (std::forward (a)) (std::forward (ds)...)) + + public: + + template auto + operator () (Appl&& a, Ts&&... ds) const HB_AUTO_RETURN + ( + impl (std::forward (a), + hb_prioritize, + std::forward (ds)...) + ) +} +HB_FUNCOBJ (hb_invoke); + +template +struct hb_partial_t +{ + hb_partial_t (Appl a, V v) : a (a), v (v) {} + + static_assert (Pos > 0, ""); + + template auto + operator () (Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl), + hb_declval (V), + hb_declval (Ts)...)) + { + return hb_invoke (std::forward (a), + std::forward (v), + std::forward (ds)...); + } + template auto + operator () (T0&& d0, Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl), + hb_declval (T0), + hb_declval (V), + hb_declval (Ts)...)) + { + return hb_invoke (std::forward (a), + std::forward (d0), + std::forward (v), + std::forward (ds)...); + } + + private: + hb_reference_wrapper a; + V v; +}; +template +auto hb_partial (Appl&& a, V&& v) HB_AUTO_RETURN +(( hb_partial_t (a, v) )) + +/* The following, HB_PARTIALIZE, macro uses a particular corner-case + * of C++11 that is not particularly well-supported by all compilers. + * What's happening is that it's using "this" in a trailing return-type + * via decltype(). Broken compilers deduce the type of "this" pointer + * in that context differently from what it resolves to in the body + * of the function. + * + * One probable cause of this is that at the time of trailing return + * type declaration, "this" points to an incomplete type, whereas in + * the function body the type is complete. That doesn't justify the + * error in any way, but is probably what's happening. + * + * In the case of MSVC, we get around this by using C++14 "decltype(auto)" + * which deduces the type from the actual return statement. For gcc 4.8 + * we use "+this" instead of "this" which produces an rvalue that seems + * to be deduced as the same type with this particular compiler, and seem + * to be fine as default code path as well. + */ +#ifdef _MSC_VER +/* https://github.com/harfbuzz/harfbuzz/issues/1730 */ \ +#define HB_PARTIALIZE(Pos) \ + template \ + decltype(auto) operator () (_T&& _v) const \ + { return hb_partial (this, std::forward<_T> (_v)); } \ + static_assert (true, "") +#else +/* https://github.com/harfbuzz/harfbuzz/issues/1724 */ +#define HB_PARTIALIZE(Pos) \ + template \ + auto operator () (_T&& _v) const HB_AUTO_RETURN \ + (hb_partial (+this, std::forward<_T> (_v))) \ + static_assert (true, "") +#endif + + +struct +{ + private: + + template auto + impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_deref (std::forward (p)).has (std::forward (v)) + ) + + template auto + impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + hb_invoke (std::forward (p), + std::forward (v)) + ) + + public: + + template auto + operator () (Pred&& p, Val &&v) const HB_RETURN (bool, + impl (std::forward (p), + std::forward (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_has); + +struct +{ + private: + + template auto + impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_has (std::forward (p), + std::forward (v)) + ) + + template auto + impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + std::forward (p) == std::forward (v) + ) + + public: + + template auto + operator () (Pred&& p, Val &&v) const HB_RETURN (bool, + impl (std::forward (p), + std::forward (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_match); + +struct +{ + private: + + template auto + impl (Proj&& f, Val &&v, hb_priority<2>) const HB_AUTO_RETURN + ( + hb_deref (std::forward (f)).get (std::forward (v)) + ) + + template auto + impl (Proj&& f, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_invoke (std::forward (f), + std::forward (v)) + ) + + template auto + impl (Proj&& f, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + std::forward (f)[std::forward (v)] + ) + + public: + + template auto + operator () (Proj&& f, Val &&v) const HB_AUTO_RETURN + ( + impl (std::forward (f), + std::forward (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_get); + +struct +{ + private: + + template auto + impl (T1&& v1, T2 &&v2, hb_priority<3>) const HB_AUTO_RETURN + ( + std::forward (v2).cmp (std::forward (v1)) == 0 + ) + + template auto + impl (T1&& v1, T2 &&v2, hb_priority<2>) const HB_AUTO_RETURN + ( + std::forward (v1).cmp (std::forward (v2)) == 0 + ) + + template auto + impl (T1&& v1, T2 &&v2, hb_priority<1>) const HB_AUTO_RETURN + ( + std::forward (v1) == std::forward (v2) + ) + + template auto + impl (T1&& v1, T2 &&v2, hb_priority<0>) const HB_AUTO_RETURN + ( + std::forward (v2) == std::forward (v1) + ) + + public: + + template auto + operator () (T1&& v1, T2 &&v2) const HB_AUTO_RETURN + ( + impl (std::forward (v1), + std::forward (v2), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_equal); + +struct +{ + template void + operator () (T& a, T& b) const + { + using std::swap; // allow ADL + swap (a, b); + } +} +HB_FUNCOBJ (hb_swap); + + +template +struct hb_pair_t +{ + typedef T1 first_t; + typedef T2 second_t; + typedef hb_pair_t pair_t; + + template ::value && + std::is_default_constructible::value)> + hb_pair_t () : first (), second () {} + hb_pair_t (T1 a, T2 b) : first (std::forward (a)), second (std::forward (b)) {} + + template + operator hb_pair_t () { return hb_pair_t (first, second); } + + hb_pair_t reverse () const + { return hb_pair_t (second, first); } + + bool operator == (const pair_t& o) const { return first == o.first && second == o.second; } + bool operator != (const pair_t& o) const { return !(*this == o); } + bool operator < (const pair_t& o) const { return first < o.first || (first == o.first && second < o.second); } + bool operator >= (const pair_t& o) const { return !(*this < o); } + bool operator > (const pair_t& o) const { return first > o.first || (first == o.first && second > o.second); } + bool operator <= (const pair_t& o) const { return !(*this > o); } + + static int cmp (const void *pa, const void *pb) + { + pair_t *a = (pair_t *) pa; + pair_t *b = (pair_t *) pb; + + if (a->first < b->first) return -1; + if (a->first > b->first) return +1; + if (a->second < b->second) return -1; + if (a->second > b->second) return +1; + return 0; + } + + friend void swap (hb_pair_t& a, hb_pair_t& b) + { + hb_swap (a.first, b.first); + hb_swap (a.second, b.second); + } + + + T1 first; + T2 second; +}; +template static inline hb_pair_t +hb_pair (T1&& a, T2&& b) { return hb_pair_t (a, b); } + +struct +{ + template constexpr typename Pair::first_t + operator () (const Pair& pair) const { return pair.first; } +} +HB_FUNCOBJ (hb_first); + +struct +{ + template constexpr typename Pair::second_t + operator () (const Pair& pair) const { return pair.second; } +} +HB_FUNCOBJ (hb_second); + +/* Note. In min/max impl, we can use hb_type_identity for second argument. + * However, that would silently convert between different-signedness integers. + * Instead we accept two different types, such that compiler can err if + * comparing integers of different signedness. */ +struct +{ + template constexpr auto + operator () (T&& a, T2&& b) const HB_AUTO_RETURN + (a <= b ? a : b) +} +HB_FUNCOBJ (hb_min); +struct +{ + template constexpr auto + operator () (T&& a, T2&& b) const HB_AUTO_RETURN + (a >= b ? a : b) +} +HB_FUNCOBJ (hb_max); +struct +{ + template constexpr auto + operator () (T&& x, T2&& min, T3&& max) const HB_AUTO_RETURN + (hb_min (hb_max (std::forward (x), std::forward (min)), std::forward (max))) +} +HB_FUNCOBJ (hb_clamp); + +/* + * Bithacks. + */ + +/* Return the number of 1 bits in v. */ +template +static inline unsigned int +hb_popcount (T v) +{ +#if hb_has_builtin(__builtin_popcount) + if (sizeof (T) <= sizeof (unsigned int)) + return __builtin_popcount (v); +#endif + +#if hb_has_builtin(__builtin_popcountl) + if (sizeof (T) <= sizeof (unsigned long)) + return __builtin_popcountl (v); +#endif + +#if hb_has_builtin(__builtin_popcountll) + if (sizeof (T) <= sizeof (unsigned long long)) + return __builtin_popcountll (v); +#endif + + if (sizeof (T) <= 4) + { + /* "HACKMEM 169" */ + uint32_t y; + y = (v >> 1) &033333333333; + y = v - y - ((y >>1) & 033333333333); + return (((y + (y >> 3)) & 030707070707) % 077); + } + + if (sizeof (T) == 8) + { + unsigned int shift = 32; + return hb_popcount ((uint32_t) v) + hb_popcount ((uint32_t) (v >> shift)); + } + + if (sizeof (T) == 16) + { + unsigned int shift = 64; + return hb_popcount ((uint64_t) v) + hb_popcount ((uint64_t) (v >> shift)); + } + + assert (0); + return 0; /* Shut up stupid compiler. */ +} + +/* Returns the number of bits needed to store number */ +template +static inline unsigned int +hb_bit_storage (T v) +{ + if (unlikely (!v)) return 0; + +#if hb_has_builtin(__builtin_clz) + if (sizeof (T) <= sizeof (unsigned int)) + return sizeof (unsigned int) * 8 - __builtin_clz (v); +#endif + +#if hb_has_builtin(__builtin_clzl) + if (sizeof (T) <= sizeof (unsigned long)) + return sizeof (unsigned long) * 8 - __builtin_clzl (v); +#endif + +#if hb_has_builtin(__builtin_clzll) + if (sizeof (T) <= sizeof (unsigned long long)) + return sizeof (unsigned long long) * 8 - __builtin_clzll (v); +#endif + +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4)) + if (sizeof (T) <= sizeof (unsigned int)) + { + unsigned long where; + _BitScanReverse (&where, v); + return 1 + where; + } +# if defined(_WIN64) + if (sizeof (T) <= 8) + { + unsigned long where; + _BitScanReverse64 (&where, v); + return 1 + where; + } +# endif +#endif + + if (sizeof (T) <= 4) + { + /* "bithacks" */ + const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000}; + const unsigned int S[] = {1, 2, 4, 8, 16}; + unsigned int r = 0; + for (int i = 4; i >= 0; i--) + if (v & b[i]) + { + v >>= S[i]; + r |= S[i]; + } + return r + 1; + } + if (sizeof (T) <= 8) + { + /* "bithacks" */ + const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL}; + const unsigned int S[] = {1, 2, 4, 8, 16, 32}; + unsigned int r = 0; + for (int i = 5; i >= 0; i--) + if (v & b[i]) + { + v >>= S[i]; + r |= S[i]; + } + return r + 1; + } + if (sizeof (T) == 16) + { + unsigned int shift = 64; + return (v >> shift) ? hb_bit_storage ((uint64_t) (v >> shift)) + shift : + hb_bit_storage ((uint64_t) v); + } + + assert (0); + return 0; /* Shut up stupid compiler. */ +} + +/* Returns the number of zero bits in the least significant side of v */ +template +static inline unsigned int +hb_ctz (T v) +{ + if (unlikely (!v)) return 8 * sizeof (T); + +#if hb_has_builtin(__builtin_ctz) + if (sizeof (T) <= sizeof (unsigned int)) + return __builtin_ctz (v); +#endif + +#if hb_has_builtin(__builtin_ctzl) + if (sizeof (T) <= sizeof (unsigned long)) + return __builtin_ctzl (v); +#endif + +#if hb_has_builtin(__builtin_ctzll) + if (sizeof (T) <= sizeof (unsigned long long)) + return __builtin_ctzll (v); +#endif + +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4)) + if (sizeof (T) <= sizeof (unsigned int)) + { + unsigned long where; + _BitScanForward (&where, v); + return where; + } +# if defined(_WIN64) + if (sizeof (T) <= 8) + { + unsigned long where; + _BitScanForward64 (&where, v); + return where; + } +# endif +#endif + + if (sizeof (T) <= 4) + { + /* "bithacks" */ + unsigned int c = 32; + v &= - (int32_t) v; + if (v) c--; + if (v & 0x0000FFFF) c -= 16; + if (v & 0x00FF00FF) c -= 8; + if (v & 0x0F0F0F0F) c -= 4; + if (v & 0x33333333) c -= 2; + if (v & 0x55555555) c -= 1; + return c; + } + if (sizeof (T) <= 8) + { + /* "bithacks" */ + unsigned int c = 64; + v &= - (int64_t) (v); + if (v) c--; + if (v & 0x00000000FFFFFFFFULL) c -= 32; + if (v & 0x0000FFFF0000FFFFULL) c -= 16; + if (v & 0x00FF00FF00FF00FFULL) c -= 8; + if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4; + if (v & 0x3333333333333333ULL) c -= 2; + if (v & 0x5555555555555555ULL) c -= 1; + return c; + } + if (sizeof (T) == 16) + { + unsigned int shift = 64; + return (uint64_t) v ? hb_bit_storage ((uint64_t) v) : + hb_bit_storage ((uint64_t) (v >> shift)) + shift; + } + + assert (0); + return 0; /* Shut up stupid compiler. */ +} + + +/* + * Tiny stuff. + */ + +/* ASCII tag/character handling */ +static inline bool ISALPHA (unsigned char c) +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } +static inline bool ISALNUM (unsigned char c) +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); } +static inline bool ISSPACE (unsigned char c) +{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; } +static inline unsigned char TOUPPER (unsigned char c) +{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; } +static inline unsigned char TOLOWER (unsigned char c) +{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; } +static inline bool ISHEX (unsigned char c) +{ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } +static inline unsigned char TOHEX (uint8_t c) +{ return (c & 0xF) <= 9 ? (c & 0xF) + '0' : (c & 0xF) + 'a' - 10; } +static inline uint8_t FROMHEX (unsigned char c) +{ return (c >= '0' && c <= '9') ? c - '0' : TOLOWER (c) - 'a' + 10; } + +static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b) +{ return (a + (b - 1)) / b; } + + +#undef ARRAY_LENGTH +template +static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; } +/* A const version, but does not detect erratically being called on pointers. */ +#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0]))) + + +static inline void * +hb_memcpy (void *__restrict dst, const void *__restrict src, size_t len) +{ + /* It's illegal to pass 0 as size to memcpy. */ + if (unlikely (!len)) return dst; + return memcpy (dst, src, len); +} + +static inline int +hb_memcmp (const void *a, const void *b, unsigned int len) +{ + /* It's illegal to pass NULL to memcmp(), even if len is zero. + * So, wrap it. + * https://sourceware.org/bugzilla/show_bug.cgi?id=23878 */ + if (unlikely (!len)) return 0; + return memcmp (a, b, len); +} + +static inline void * +hb_memset (void *s, int c, unsigned int n) +{ + /* It's illegal to pass NULL to memset(), even if n is zero. */ + if (unlikely (!n)) return 0; + return memset (s, c, n); +} + +static inline unsigned int +hb_ceil_to_4 (unsigned int v) +{ + return ((v - 1) | 3) + 1; +} + +template static inline bool +hb_in_range (T u, T lo, T hi) +{ + static_assert (!std::is_signed::value, ""); + + /* The casts below are important as if T is smaller than int, + * the subtract results will become a signed int! */ + return (T)(u - lo) <= (T)(hi - lo); +} +template static inline bool +hb_in_ranges (T u, T lo1, T hi1) +{ + return hb_in_range (u, lo1, hi1); +} +template static inline bool +hb_in_ranges (T u, T lo1, T hi1, Ts... ds) +{ + return hb_in_range (u, lo1, hi1) || hb_in_ranges (u, ds...); +} + + +/* + * Overflow checking. + */ + +static inline bool +hb_unsigned_mul_overflows (unsigned int count, unsigned int size, unsigned *result = nullptr) +{ +#if hb_has_builtin(__builtin_mul_overflow) + unsigned stack_result; + if (!result) + result = &stack_result; + return __builtin_mul_overflow (count, size, result); +#endif + + if (result) + *result = count * size; + return (size > 0) && (count >= ((unsigned int) -1) / size); +} + + +/* + * Sort and search. + */ + +template +static int +_hb_cmp_method (const void *pkey, const void *pval, Ts... ds) +{ + const K& key = * (const K*) pkey; + const V& val = * (const V*) pval; + + return val.cmp (key, ds...); +} + +template +static inline bool +hb_bsearch_impl (unsigned *pos, /* Out */ + const K& key, + V* base, size_t nmemb, size_t stride, + int (*compar)(const void *_key, const void *_item, Ts... _ds), + Ts... ds) +{ + /* This is our *only* bsearch implementation. */ + + int min = 0, max = (int) nmemb - 1; + while (min <= max) + { + int mid = ((unsigned int) min + (unsigned int) max) / 2; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + V* p = (V*) (((const char *) base) + (mid * stride)); +#pragma GCC diagnostic pop + int c = compar ((const void *) std::addressof (key), (const void *) p, ds...); + if (c < 0) + max = mid - 1; + else if (c > 0) + min = mid + 1; + else + { + *pos = mid; + return true; + } + } + *pos = min; + return false; +} + +template +static inline V* +hb_bsearch (const K& key, V* base, + size_t nmemb, size_t stride = sizeof (V), + int (*compar)(const void *_key, const void *_item) = _hb_cmp_method) +{ + unsigned pos; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return hb_bsearch_impl (&pos, key, base, nmemb, stride, compar) ? + (V*) (((const char *) base) + (pos * stride)) : nullptr; +#pragma GCC diagnostic pop +} +template +static inline V* +hb_bsearch (const K& key, V* base, + size_t nmemb, size_t stride, + int (*compar)(const void *_key, const void *_item, Ts... _ds), + Ts... ds) +{ + unsigned pos; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return hb_bsearch_impl (&pos, key, base, nmemb, stride, compar, ds...) ? + (V*) (((const char *) base) + (pos * stride)) : nullptr; +#pragma GCC diagnostic pop +} + + +/* From https://github.com/noporpoise/sort_r + Feb 5, 2019 (c8c65c1e) + Modified to support optional argument using templates */ + +/* Isaac Turner 29 April 2014 Public Domain */ + +/* +hb_qsort function to be exported. +Parameters: + base is the array to be sorted + nel is the number of elements in the array + width is the size in bytes of each element of the array + compar is the comparison function + arg (optional) is a pointer to be passed to the comparison function + +void hb_qsort(void *base, size_t nel, size_t width, + int (*compar)(const void *_a, const void *_b, [void *_arg]), + [void *arg]); +*/ + +#define SORT_R_SWAP(a,b,tmp) ((void) ((tmp) = (a)), (void) ((a) = (b)), (b) = (tmp)) + +/* swap a and b */ +/* a and b must not be equal! */ +static inline void sort_r_swap(char *__restrict a, char *__restrict b, + size_t w) +{ + char tmp, *end = a+w; + for(; a < end; a++, b++) { SORT_R_SWAP(*a, *b, tmp); } +} + +/* swap a, b iff a>b */ +/* a and b must not be equal! */ +/* __restrict is same as restrict but better support on old machines */ +template +static inline int sort_r_cmpswap(char *__restrict a, + char *__restrict b, size_t w, + int (*compar)(const void *_a, + const void *_b, + Ts... _ds), + Ts... ds) +{ + if(compar(a, b, ds...) > 0) { + sort_r_swap(a, b, w); + return 1; + } + return 0; +} + +/* +Swap consecutive blocks of bytes of size na and nb starting at memory addr ptr, +with the smallest swap so that the blocks are in the opposite order. Blocks may +be internally re-ordered e.g. + 12345ab -> ab34512 + 123abc -> abc123 + 12abcde -> deabc12 +*/ +static inline void sort_r_swap_blocks(char *ptr, size_t na, size_t nb) +{ + if(na > 0 && nb > 0) { + if(na > nb) { sort_r_swap(ptr, ptr+na, nb); } + else { sort_r_swap(ptr, ptr+nb, na); } + } +} + +/* Implement recursive quicksort ourselves */ +/* Note: quicksort is not stable, equivalent values may be swapped */ +template +static inline void sort_r_simple(void *base, size_t nel, size_t w, + int (*compar)(const void *_a, + const void *_b, + Ts... _ds), + Ts... ds) +{ + char *b = (char *)base, *end = b + nel*w; + + /* for(size_t i=0; i b && sort_r_cmpswap(pj-w,pj,w,compar,ds...); pj -= w) {} + } + } + else + { + /* nel > 9; Quicksort */ + + int cmp; + char *pl, *ple, *pr, *pre, *pivot; + char *last = b+w*(nel-1), *tmp; + + /* + Use median of second, middle and second-last items as pivot. + First and last may have been swapped with pivot and therefore be extreme + */ + char *l[3]; + l[0] = b + w; + l[1] = b+w*(nel/2); + l[2] = last - w; + + /* printf("pivots: %i, %i, %i\n", *(int*)l[0], *(int*)l[1], *(int*)l[2]); */ + + if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); } + if(compar(l[1],l[2],ds...) > 0) { + SORT_R_SWAP(l[1], l[2], tmp); + if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); } + } + + /* swap mid value (l[1]), and last element to put pivot as last element */ + if(l[1] != last) { sort_r_swap(l[1], last, w); } + + /* + pl is the next item on the left to be compared to the pivot + pr is the last item on the right that was compared to the pivot + ple is the left position to put the next item that equals the pivot + ple is the last right position where we put an item that equals the pivot + v- end (beyond the array) + EEEEEELLLLLLLLuuuuuuuuGGGGGGGEEEEEEEE. + ^- b ^- ple ^- pl ^- pr ^- pre ^- last (where the pivot is) + Pivot comparison key: + E = equal, L = less than, u = unknown, G = greater than, E = equal + */ + pivot = last; + ple = pl = b; + pre = pr = last; + + /* + Strategy: + Loop into the list from the left and right at the same time to find: + - an item on the left that is greater than the pivot + - an item on the right that is less than the pivot + Once found, they are swapped and the loop continues. + Meanwhile items that are equal to the pivot are moved to the edges of the + array. + */ + while(pl < pr) { + /* Move left hand items which are equal to the pivot to the far left. + break when we find an item that is greater than the pivot */ + for(; pl < pr; pl += w) { + cmp = compar(pl, pivot, ds...); + if(cmp > 0) { break; } + else if(cmp == 0) { + if(ple < pl) { sort_r_swap(ple, pl, w); } + ple += w; + } + } + /* break if last batch of left hand items were equal to pivot */ + if(pl >= pr) { break; } + /* Move right hand items which are equal to the pivot to the far right. + break when we find an item that is less than the pivot */ + for(; pl < pr; ) { + pr -= w; /* Move right pointer onto an unprocessed item */ + cmp = compar(pr, pivot, ds...); + if(cmp == 0) { + pre -= w; + if(pr < pre) { sort_r_swap(pr, pre, w); } + } + else if(cmp < 0) { + if(pl < pr) { sort_r_swap(pl, pr, w); } + pl += w; + break; + } + } + } + + pl = pr; /* pr may have gone below pl */ + + /* + Now we need to go from: EEELLLGGGGEEEE + to: LLLEEEEEEEGGGG + Pivot comparison key: + E = equal, L = less than, u = unknown, G = greater than, E = equal + */ + sort_r_swap_blocks(b, ple-b, pl-ple); + sort_r_swap_blocks(pr, pre-pr, end-pre); + + /*for(size_t i=0; i static inline void +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2 = nullptr) +{ + static_assert (hb_is_trivially_copy_assignable (T), ""); + static_assert (hb_is_trivially_copy_assignable (T3), ""); + + for (unsigned int i = 1; i < len; i++) + { + unsigned int j = i; + while (j && compar (&array[j - 1], &array[i]) > 0) + j--; + if (i == j) + continue; + /* Move item i to occupy place for item j, shift what's in between. */ + { + T t = array[i]; + memmove (&array[j + 1], &array[j], (i - j) * sizeof (T)); + array[j] = t; + } + if (array2) + { + T3 t = array2[i]; + memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T3)); + array2[j] = t; + } + } +} + +static inline hb_bool_t +hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out) +{ + unsigned int v; + const char *p = s; + const char *end = p + len; + if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */, base))) + return false; + + *out = v; + return true; +} + + +/* Operators. */ + +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & b) +} +HB_FUNCOBJ (hb_bitwise_and); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | b) +} +HB_FUNCOBJ (hb_bitwise_or); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a ^ b) +} +HB_FUNCOBJ (hb_bitwise_xor); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (~a & b) +} +HB_FUNCOBJ (hb_bitwise_lt); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & ~b) +} +HB_FUNCOBJ (hb_bitwise_gt); // aka sub +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (~a | b) +} +HB_FUNCOBJ (hb_bitwise_le); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | ~b) +} +HB_FUNCOBJ (hb_bitwise_ge); +struct +{ + template constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (~a) +} +HB_FUNCOBJ (hb_bitwise_neg); + +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a + b) +} +HB_FUNCOBJ (hb_add); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a - b) +} +HB_FUNCOBJ (hb_sub); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (b - a) +} +HB_FUNCOBJ (hb_rsub); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a * b) +} +HB_FUNCOBJ (hb_mul); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a / b) +} +HB_FUNCOBJ (hb_div); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a % b) +} +HB_FUNCOBJ (hb_mod); +struct +{ + template constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (+a) +} +HB_FUNCOBJ (hb_pos); +struct +{ + template constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (-a) +} +HB_FUNCOBJ (hb_neg); +struct +{ + template constexpr auto + operator () (T &a) const HB_AUTO_RETURN (++a) +} +HB_FUNCOBJ (hb_inc); +struct +{ + template constexpr auto + operator () (T &a) const HB_AUTO_RETURN (--a) +} +HB_FUNCOBJ (hb_dec); + + +/* Adapted from kurbo implementation with extra parameters added, + * and finding for a particular range instead of 0. + * + * For documentation and implementation see: + * + * [ITP method]: https://en.wikipedia.org/wiki/ITP_Method + * [An Enhancement of the Bisection Method Average Performance Preserving Minmax Optimality]: https://dl.acm.org/doi/10.1145/3423597 + * https://docs.rs/kurbo/0.8.1/kurbo/common/fn.solve_itp.html + * https://github.com/linebender/kurbo/blob/fd839c25ea0c98576c7ce5789305822675a89938/src/common.rs#L162-L248 + */ +template +double solve_itp (func_t f, + double a, double b, + double epsilon, + double min_y, double max_y, + double &ya, double &yb, double &y) +{ + unsigned n1_2 = (unsigned) (hb_max (ceil (log2 ((b - a) / epsilon)) - 1.0, 0.0)); + const unsigned n0 = 1; // Hardwired + const double k1 = 0.2 / (b - a); // Hardwired. + unsigned nmax = n0 + n1_2; + double scaled_epsilon = epsilon * double (1llu << nmax); + double _2_epsilon = 2.0 * epsilon; + while (b - a > _2_epsilon) + { + double x1_2 = 0.5 * (a + b); + double r = scaled_epsilon - 0.5 * (b - a); + double xf = (yb * a - ya * b) / (yb - ya); + double sigma = x1_2 - xf; + double b_a = b - a; + // This has k2 = 2 hardwired for efficiency. + double b_a_k2 = b_a * b_a; + double delta = k1 * b_a_k2; + int sigma_sign = sigma >= 0 ? +1 : -1; + double xt = delta <= fabs (x1_2 - xf) ? xf + delta * sigma_sign : x1_2; + double xitp = fabs (xt - x1_2) <= r ? xt : x1_2 - r * sigma_sign; + double yitp = f (xitp); + if (yitp > max_y) + { + b = xitp; + yb = yitp; + } + else if (yitp < min_y) + { + a = xitp; + ya = yitp; + } + else + { + y = yitp; + return xitp; + } + scaled_epsilon *= 0.5; + } + return 0.5 * (a + b); +} + + +#endif /* HB_ALGS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-array.hh b/src/3rdparty/harfbuzz-ng/src/hb-array.hh new file mode 100644 index 00000000000..e82c081535e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-array.hh @@ -0,0 +1,494 @@ +/* + * Copyright ยฉ 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_ARRAY_HH +#define HB_ARRAY_HH + +#include "hb.hh" +#include "hb-algs.hh" +#include "hb-iter.hh" +#include "hb-null.hh" + + +template +struct hb_sorted_array_t; + +enum hb_not_found_t +{ + HB_NOT_FOUND_DONT_STORE, + HB_NOT_FOUND_STORE, + HB_NOT_FOUND_STORE_CLOSEST, +}; + + +template +struct hb_array_t : hb_iter_with_fallback_t, Type&> +{ + /* + * Constructors. + */ + hb_array_t () = default; + hb_array_t (const hb_array_t&) = default; + ~hb_array_t () = default; + hb_array_t& operator= (const hb_array_t&) = default; + hb_array_t& operator= (hb_array_t&&) = default; + + constexpr hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_) {} + template + constexpr hb_array_t (Type (&array_)[length_]) : hb_array_t (array_, length_) {} + + template + constexpr hb_array_t (const hb_array_t &o) : + hb_iter_with_fallback_t (), + arrayZ (o.arrayZ), length (o.length), backwards_length (o.backwards_length) {} + template + hb_array_t& operator = (const hb_array_t &o) + { arrayZ = o.arrayZ; length = o.length; backwards_length = o.backwards_length; return *this; } + + /* + * Iterator implementation. + */ + typedef Type& __item_t__; + static constexpr bool is_random_access_iterator = true; + Type& __item_at__ (unsigned i) const + { + if (unlikely (i >= length)) return CrapOrNull (Type); + return arrayZ[i]; + } + void __forward__ (unsigned n) + { + if (unlikely (n > length)) + n = length; + length -= n; + backwards_length += n; + arrayZ += n; + } + void __rewind__ (unsigned n) + { + if (unlikely (n > backwards_length)) + n = backwards_length; + length += n; + backwards_length -= n; + arrayZ -= n; + } + unsigned __len__ () const { return length; } + /* Ouch. The operator== compares the contents of the array. For range-based for loops, + * it's best if we can just compare arrayZ, though comparing contents is still fast, + * but also would require that Type has operator==. As such, we optimize this operator + * for range-based for loop and just compare arrayZ and length. + * + * The above comment is outdated now because we implemented separate begin/end to + * objects that were using hb_array_t for range-based loop before. */ + bool operator != (const hb_array_t& o) const + { return this->arrayZ != o.arrayZ || this->length != o.length; } + + /* Faster range-based for loop without bounds-check. */ + Type *begin () const { return arrayZ; } + Type *end () const { return arrayZ + length; } + + + /* Extra operators. + */ + Type * operator & () const { return arrayZ; } + operator hb_array_t () { return hb_array_t (arrayZ, length); } + template operator T * () const { return arrayZ; } + + HB_INTERNAL bool operator == (const hb_array_t &o) const; + + uint32_t hash () const + { + uint32_t current = 0; + for (auto &v : *this) + current = current * 31 + hb_hash (v); + return current; + } + + /* + * Compare, Sort, and Search. + */ + + /* Note: our compare is NOT lexicographic; it also does NOT call Type::cmp. */ + int cmp (const hb_array_t &a) const + { + if (length != a.length) + return (int) a.length - (int) length; + return hb_memcmp (a.arrayZ, arrayZ, get_size ()); + } + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + hb_array_t *a = (hb_array_t *) pa; + hb_array_t *b = (hb_array_t *) pb; + return b->cmp (*a); + } + + template + Type *lsearch (const T &x, Type *not_found = nullptr) + { + unsigned i; + return lfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + const Type *lsearch (const T &x, const Type *not_found = nullptr) const + { + unsigned i; + return lfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + bool lfind (const T &x, unsigned *pos = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { + for (unsigned i = 0; i < length; ++i) + if (hb_equal (x, this->arrayZ[i])) + { + if (pos) + *pos = i; + return true; + } + + if (pos) + { + switch (not_found) + { + case HB_NOT_FOUND_DONT_STORE: + break; + + case HB_NOT_FOUND_STORE: + *pos = to_store; + break; + + case HB_NOT_FOUND_STORE_CLOSEST: + *pos = length; + break; + } + } + return false; + } + + hb_sorted_array_t qsort (int (*cmp_)(const void*, const void*)) + { + //static_assert (hb_enable_if (hb_is_trivially_copy_assignable(Type)), ""); + if (likely (length)) + hb_qsort (arrayZ, length, this->get_item_size (), cmp_); + return hb_sorted_array_t (*this); + } + hb_sorted_array_t qsort () + { + //static_assert (hb_enable_if (hb_is_trivially_copy_assignable(Type)), ""); + if (likely (length)) + hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp); + return hb_sorted_array_t (*this); + } + + /* + * Other methods. + */ + + unsigned int get_size () const { return length * this->get_item_size (); } + + /* + * Reverse the order of items in this array in the range [start, end). + */ + void reverse (unsigned start = 0, unsigned end = -1) + { + start = hb_min (start, length); + end = hb_min (end, length); + + if (end < start + 2) + return; + + for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--) + hb_swap (arrayZ[rhs], arrayZ[lhs]); + } + + hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const + { + if (!start_offset && !seg_count) + return *this; + + unsigned int count = length; + if (unlikely (start_offset > count)) + count = 0; + else + count -= start_offset; + if (seg_count) + count = *seg_count = hb_min (count, *seg_count); + return hb_array_t (arrayZ + start_offset, count); + } + hb_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const + { return sub_array (start_offset, &seg_count); } + + hb_array_t truncate (unsigned length) const { return sub_array (0, length); } + + template + const T *as () const + { return length < hb_min_size (T) ? &Null (T) : reinterpret_cast (arrayZ); } + + template + bool check_range (const T *p, unsigned int size = T::static_size) const + { + return arrayZ <= ((const char *) p) + && ((const char *) p) <= arrayZ + length + && (unsigned int) (arrayZ + length - (const char *) p) >= size; + } + + /* Only call if you allocated the underlying array using hb_malloc() or similar. */ + void fini () + { hb_free ((void *) arrayZ); arrayZ = nullptr; length = 0; } + + template )))> + hb_array_t copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto* out = c->start_embed (arrayZ); + if (unlikely (!c->extend_size (out, get_size (), false))) return_trace (hb_array_t ()); + for (unsigned i = 0; i < length; i++) + out[i] = arrayZ[i]; /* TODO: add version that calls c->copy() */ + return_trace (hb_array_t (out, length)); + } + + template ))> + hb_array_t copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto* out = c->start_embed (arrayZ); + if (unlikely (!c->extend_size (out, get_size (), false))) return_trace (hb_array_t ()); + hb_memcpy (out, arrayZ, get_size ()); + return_trace (hb_array_t (out, length)); + } + + template + bool sanitize (hb_sanitize_context_t *c) const + { return c->check_array (arrayZ, length); } + + /* + * Members + */ + + public: + Type *arrayZ = nullptr; + unsigned int length = 0; + unsigned int backwards_length = 0; +}; +template inline hb_array_t +hb_array () +{ return hb_array_t (); } +template inline hb_array_t +hb_array (T *array, unsigned int length) +{ return hb_array_t (array, length); } +template inline hb_array_t +hb_array (T (&array_)[length_]) +{ return hb_array_t (array_); } + +template +struct hb_sorted_array_t : + hb_array_t, + hb_iter_t, Type&> +{ + typedef hb_iter_t iter_base_t; + HB_ITER_USING (iter_base_t); + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + + hb_sorted_array_t () = default; + hb_sorted_array_t (const hb_sorted_array_t&) = default; + ~hb_sorted_array_t () = default; + hb_sorted_array_t& operator= (const hb_sorted_array_t&) = default; + hb_sorted_array_t& operator= (hb_sorted_array_t&&) = default; + + constexpr hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t (array_, length_) {} + template + constexpr hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t (array_) {} + + template + constexpr hb_sorted_array_t (const hb_array_t &o) : + hb_array_t (o), + hb_iter_t () {} + template + hb_sorted_array_t& operator = (const hb_array_t &o) + { hb_array_t (*this) = o; return *this; } + + /* Iterator implementation. */ + + /* See comment in hb_array_of::operator != */ + bool operator != (const hb_sorted_array_t& o) const + { return this->arrayZ != o.arrayZ || this->length != o.length; } + + /* Faster range-based for loop without bounds-check. */ + Type *begin () const { return this->arrayZ; } + Type *end () const { return this->arrayZ + this->length; } + + + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const + { return hb_sorted_array_t (((const hb_array_t *) (this))->sub_array (start_offset, seg_count)); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const + { return sub_array (start_offset, &seg_count); } + + hb_sorted_array_t truncate (unsigned length) const { return sub_array (0, length); } + + template + Type *bsearch (const T &x, Type *not_found = nullptr) + { + unsigned int i; + return bfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + const Type *bsearch (const T &x, const Type *not_found = nullptr) const + { + unsigned int i; + return bfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + bool bfind (const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { + unsigned pos; + + if (bsearch_impl (x, &pos)) + { + if (i) + *i = pos; + return true; + } + + if (i) + { + switch (not_found) + { + case HB_NOT_FOUND_DONT_STORE: + break; + + case HB_NOT_FOUND_STORE: + *i = to_store; + break; + + case HB_NOT_FOUND_STORE_CLOSEST: + *i = pos; + break; + } + } + return false; + } + template + bool bsearch_impl (const T &x, unsigned *pos, Ts... ds) const + { + return hb_bsearch_impl (pos, + x, + this->arrayZ, + this->length, + sizeof (Type), + _hb_cmp_method, + std::forward (ds)...); + } +}; +template inline hb_sorted_array_t +hb_sorted_array (T *array, unsigned int length) +{ return hb_sorted_array_t (array, length); } +template inline hb_sorted_array_t +hb_sorted_array (T (&array_)[length_]) +{ return hb_sorted_array_t (array_); } + +template +inline bool hb_array_t::operator == (const hb_array_t &o) const +{ + if (o.length != this->length) return false; + for (unsigned int i = 0; i < this->length; i++) { + if (this->arrayZ[i] != o.arrayZ[i]) return false; + } + return true; +} +template <> +inline bool hb_array_t::operator == (const hb_array_t &o) const +{ + if (o.length != this->length) return false; + return 0 == hb_memcmp (arrayZ, o.arrayZ, length); +} +template <> +inline bool hb_array_t::operator == (const hb_array_t &o) const +{ + if (o.length != this->length) return false; + return 0 == hb_memcmp (arrayZ, o.arrayZ, length); +} + + +/* Specialize hash() for byte arrays. */ + +template <> +inline uint32_t hb_array_t::hash () const +{ + uint32_t current = 0; + unsigned i = 0; + +#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \ + ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) + struct __attribute__((packed)) packed_uint32_t { uint32_t v; }; + for (; i + 4 <= this->length; i += 4) + current = current * 31 + hb_hash ((uint32_t) ((packed_uint32_t *) &this->arrayZ[i])->v); +#endif + + for (; i < this->length; i++) + current = current * 31 + hb_hash (this->arrayZ[i]); + return current; +} + +template <> +inline uint32_t hb_array_t::hash () const +{ + uint32_t current = 0; + unsigned i = 0; + +#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \ + ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) + struct __attribute__((packed)) packed_uint32_t { uint32_t v; }; + for (; i + 4 <= this->length; i += 4) + current = current * 31 + hb_hash ((uint32_t) ((packed_uint32_t *) &this->arrayZ[i])->v); +#endif + + for (; i < this->length; i++) + current = current * 31 + hb_hash (this->arrayZ[i]); + return current; +} + + +typedef hb_array_t hb_bytes_t; +typedef hb_array_t hb_ubytes_t; + + + +#endif /* HB_ARRAY_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-atomic-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-atomic-private.hh deleted file mode 100644 index 93438401400..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-atomic-private.hh +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright ยฉ 2007 Chris Wilson - * Copyright ยฉ 2009,2010 Red Hat, Inc. - * Copyright ยฉ 2011,2012 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Contributor(s): - * Chris Wilson - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_ATOMIC_PRIVATE_HH -#define HB_ATOMIC_PRIVATE_HH - -#include "hb-private.hh" - - -/* atomic_int */ - -/* We need external help for these */ - -#if defined(hb_atomic_int_impl_add) \ - && defined(hb_atomic_ptr_impl_get) \ - && defined(hb_atomic_ptr_impl_cmpexch) - -/* Defined externally, i.e. in config.h; must have typedef'ed hb_atomic_int_impl_t as well. */ - - -#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__)) - -#include - -/* MinGW has a convoluted history of supporting MemoryBarrier - * properly. As such, define a function to wrap the whole - * thing. */ -static inline void _HBMemoryBarrier (void) { -#if !defined(MemoryBarrier) - long dummy = 0; - InterlockedExchange (&dummy, 1); -#else - MemoryBarrier (); -#endif -} - -typedef LONG hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) InterlockedExchangeAdd (&(AI), (V)) - -#define hb_atomic_ptr_impl_get(P) (_HBMemoryBarrier (), (void *) *(P)) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) (InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O)) - - -#elif !defined(HB_NO_MT) && defined(__APPLE__) - -#include -#ifdef __MAC_OS_X_MIN_REQUIRED -#include -#elif defined(__IPHONE_OS_MIN_REQUIRED) -#include -#endif - - -typedef int32_t hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) (OSAtomicAdd32Barrier ((V), &(AI)) - (V)) - -#define hb_atomic_ptr_impl_get(P) (OSMemoryBarrier (), (void *) *(P)) -#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P)) -#else -#if __ppc64__ || __x86_64__ || __aarch64__ -#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap64Barrier ((int64_t) (void *) (O), (int64_t) (void *) (N), (int64_t*) (P)) -#else -#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap32Barrier ((int32_t) (void *) (O), (int32_t) (void *) (N), (int32_t*) (P)) -#endif -#endif - - -#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) - -typedef int hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) __sync_fetch_and_add (&(AI), (V)) - -#define hb_atomic_ptr_impl_get(P) (void *) (__sync_synchronize (), *(P)) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) __sync_bool_compare_and_swap ((P), (O), (N)) - - -#elif !defined(HB_NO_MT) && defined(HAVE_SOLARIS_ATOMIC_OPS) - -#include -#include - -typedef unsigned int hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) ( ({__machine_rw_barrier ();}), atomic_add_int_nv (&(AI), (V)) - (V)) - -#define hb_atomic_ptr_impl_get(P) ( ({__machine_rw_barrier ();}), (void *) *(P)) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) ( ({__machine_rw_barrier ();}), atomic_cas_ptr ((void **) (P), (void *) (O), (void *) (N)) == (void *) (O) ? true : false) - - -#elif !defined(HB_NO_MT) && defined(_AIX) && defined(__IBMCPP__) - -#include - - -static inline int _hb_fetch_and_add(volatile int* AI, unsigned int V) { - __lwsync(); - int result = __fetch_and_add(AI, V); - __isync(); - return result; -} -static inline int _hb_compare_and_swaplp(volatile long* P, long O, long N) { - __sync(); - int result = __compare_and_swaplp (P, &O, N); - __sync(); - return result; -} - -typedef int hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) _hb_fetch_and_add (&(AI), (V)) - -#define hb_atomic_ptr_impl_get(P) (__sync(), (void *) *(P)) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_compare_and_swaplp ((long*)(P), (long)(O), (long)(N)) - -#elif !defined(HB_NO_MT) - -#define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */ - -typedef volatile int hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) (((AI) += (V)) - (V)) - -#define hb_atomic_ptr_impl_get(P) ((void *) *(P)) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void * volatile *) (P) == (void *) (O) ? (* (void * volatile *) (P) = (void *) (N), true) : false) - - -#else /* HB_NO_MT */ - -typedef int hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) (((AI) += (V)) - (V)) - -#define hb_atomic_ptr_impl_get(P) ((void *) *(P)) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false) - - -#endif - - -#define HB_ATOMIC_INT_INIT(V) {HB_ATOMIC_INT_IMPL_INIT(V)} - -struct hb_atomic_int_t -{ - hb_atomic_int_impl_t v; - - inline void set_unsafe (int v_) { v = v_; } - inline int get_unsafe (void) const { return v; } - inline int inc (void) { return hb_atomic_int_impl_add (const_cast (v), 1); } - inline int dec (void) { return hb_atomic_int_impl_add (const_cast (v), -1); } -}; - - -#define hb_atomic_ptr_get(P) hb_atomic_ptr_impl_get(P) -#define hb_atomic_ptr_cmpexch(P,O,N) hb_atomic_ptr_impl_cmpexch((P),(O),(N)) - - -#endif /* HB_ATOMIC_PRIVATE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-atomic.hh b/src/3rdparty/harfbuzz-ng/src/hb-atomic.hh new file mode 100644 index 00000000000..a6283de1408 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-atomic.hh @@ -0,0 +1,221 @@ +/* + * Copyright ยฉ 2007 Chris Wilson + * Copyright ยฉ 2009,2010 Red Hat, Inc. + * Copyright ยฉ 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_ATOMIC_HH +#define HB_ATOMIC_HH + +#include "hb.hh" +#include "hb-meta.hh" + + +/* + * Atomic integers and pointers. + */ + + +/* We need external help for these */ + +#if defined(hb_atomic_int_impl_add) \ + && defined(hb_atomic_ptr_impl_get) \ + && defined(hb_atomic_ptr_impl_cmpexch) + +/* Defined externally, i.e. in config.h. */ + + +#elif !defined(HB_NO_MT) && defined(__ATOMIC_ACQUIRE) + +/* C++11-style GCC primitives. We prefer these as they don't require linking to libstdc++ / libc++. */ + +#define _hb_memory_barrier() __sync_synchronize () + +#define hb_atomic_int_impl_add(AI, V) __atomic_fetch_add ((AI), (V), __ATOMIC_ACQ_REL) +#define hb_atomic_int_impl_set_relaxed(AI, V) __atomic_store_n ((AI), (V), __ATOMIC_RELAXED) +#define hb_atomic_int_impl_set(AI, V) __atomic_store_n ((AI), (V), __ATOMIC_RELEASE) +#define hb_atomic_int_impl_get_relaxed(AI) __atomic_load_n ((AI), __ATOMIC_RELAXED) +#define hb_atomic_int_impl_get(AI) __atomic_load_n ((AI), __ATOMIC_ACQUIRE) + +#define hb_atomic_ptr_impl_set_relaxed(P, V) __atomic_store_n ((P), (V), __ATOMIC_RELAXED) +#define hb_atomic_ptr_impl_get_relaxed(P) __atomic_load_n ((P), __ATOMIC_RELAXED) +#define hb_atomic_ptr_impl_get(P) __atomic_load_n ((P), __ATOMIC_ACQUIRE) +static inline bool +_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) +{ + const void *O = O_; // Need lvalue + return __atomic_compare_exchange_n ((void **) P, (void **) &O, (void *) N, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED); +} +#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N)) + + +#elif !defined(HB_NO_MT) + +/* C++11 atomics. */ + +#include + +#define _hb_memory_barrier() std::atomic_thread_fence(std::memory_order_ack_rel) +#define _hb_memory_r_barrier() std::atomic_thread_fence(std::memory_order_acquire) +#define _hb_memory_w_barrier() std::atomic_thread_fence(std::memory_order_release) + +#define hb_atomic_int_impl_add(AI, V) (reinterpret_cast::type> *> (AI)->fetch_add ((V), std::memory_order_acq_rel)) +#define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_relaxed)) +#define hb_atomic_int_impl_set(AI, V) (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_release)) +#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast::type> const *> (AI)->load (std::memory_order_relaxed)) +#define hb_atomic_int_impl_get(AI) (reinterpret_cast::type> const *> (AI)->load (std::memory_order_acquire)) + +#define hb_atomic_ptr_impl_set_relaxed(P, V) (reinterpret_cast *> (P)->store ((V), std::memory_order_relaxed)) +#define hb_atomic_ptr_impl_get_relaxed(P) (reinterpret_cast const *> (P)->load (std::memory_order_relaxed)) +#define hb_atomic_ptr_impl_get(P) (reinterpret_cast *> (P)->load (std::memory_order_acquire)) +static inline bool +_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) +{ + const void *O = O_; // Need lvalue + return reinterpret_cast *> (P)->compare_exchange_weak (O, N, std::memory_order_acq_rel, std::memory_order_relaxed); +} +#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N)) + + +#else /* defined(HB_NO_MT) */ + +#define hb_atomic_int_impl_add(AI, V) ((*(AI) += (V)) - (V)) +#define _hb_memory_barrier() do {} while (0) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false) + +#endif + + +/* This should never be disabled, even under HB_NO_MT. + * except that MSVC gives me an internal compiler error, so disabled there. + * + * https://github.com/harfbuzz/harfbuzz/pull/4119 + */ +#ifndef _hb_compiler_memory_r_barrier +#if defined(__ATOMIC_ACQUIRE) // gcc-like +#define _hb_compiler_memory_r_barrier() asm volatile("": : :"memory") +#elif !defined(_MSC_VER) +#include +#define _hb_compiler_memory_r_barrier() std::atomic_signal_fence (std::memory_order_acquire) +#else +#define _hb_compiler_memory_r_barrier() do {} while (0) +#endif +#endif + + + +#ifndef _hb_memory_r_barrier +#define _hb_memory_r_barrier() _hb_memory_barrier () +#endif +#ifndef _hb_memory_w_barrier +#define _hb_memory_w_barrier() _hb_memory_barrier () +#endif +#ifndef hb_atomic_int_impl_set_relaxed +#define hb_atomic_int_impl_set_relaxed(AI, V) (*(AI) = (V)) +#endif +#ifndef hb_atomic_int_impl_get_relaxed +#define hb_atomic_int_impl_get_relaxed(AI) (*(AI)) +#endif + +#ifndef hb_atomic_ptr_impl_set_relaxed +#define hb_atomic_ptr_impl_set_relaxed(P, V) (*(P) = (V)) +#endif +#ifndef hb_atomic_ptr_impl_get_relaxed +#define hb_atomic_ptr_impl_get_relaxed(P) (*(P)) +#endif +#ifndef hb_atomic_int_impl_set +inline void hb_atomic_int_impl_set (int *AI, int v) { _hb_memory_w_barrier (); *AI = v; } +inline void hb_atomic_int_impl_set (short *AI, short v) { _hb_memory_w_barrier (); *AI = v; } +#endif +#ifndef hb_atomic_int_impl_get +inline int hb_atomic_int_impl_get (const int *AI) { int v = *AI; _hb_memory_r_barrier (); return v; } +inline short hb_atomic_int_impl_get (const short *AI) { short v = *AI; _hb_memory_r_barrier (); return v; } +#endif +#ifndef hb_atomic_ptr_impl_get +inline void *hb_atomic_ptr_impl_get (void ** const P) { void *v = *P; _hb_memory_r_barrier (); return v; } +#endif + + +struct hb_atomic_short_t +{ + hb_atomic_short_t () = default; + constexpr hb_atomic_short_t (short v) : v (v) {} + + hb_atomic_short_t& operator = (short v_) { set_relaxed (v_); return *this; } + operator short () const { return get_relaxed (); } + + void set_relaxed (short v_) { hb_atomic_int_impl_set_relaxed (&v, v_); } + void set_release (short v_) { hb_atomic_int_impl_set (&v, v_); } + short get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); } + short get_acquire () const { return hb_atomic_int_impl_get (&v); } + short inc () { return hb_atomic_int_impl_add (&v, 1); } + short dec () { return hb_atomic_int_impl_add (&v, -1); } + + short v = 0; +}; + +struct hb_atomic_int_t +{ + hb_atomic_int_t () = default; + constexpr hb_atomic_int_t (int v) : v (v) {} + + hb_atomic_int_t& operator = (int v_) { set_relaxed (v_); return *this; } + operator int () const { return get_relaxed (); } + + void set_relaxed (int v_) { hb_atomic_int_impl_set_relaxed (&v, v_); } + void set_release (int v_) { hb_atomic_int_impl_set (&v, v_); } + int get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); } + int get_acquire () const { return hb_atomic_int_impl_get (&v); } + int inc () { return hb_atomic_int_impl_add (&v, 1); } + int dec () { return hb_atomic_int_impl_add (&v, -1); } + + int v = 0; +}; + +template +struct hb_atomic_ptr_t +{ + typedef hb_remove_pointer

T; + + hb_atomic_ptr_t () = default; + constexpr hb_atomic_ptr_t (T* v) : v (v) {} + + void init (T* v_ = nullptr) { set_relaxed (v_); } + void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); } + T *get_relaxed () const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); } + T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); } + bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } + + T * operator -> () const { return get_acquire (); } + template operator C * () const { return get_acquire (); } + + T *v = nullptr; +}; + + +#endif /* HB_ATOMIC_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-bimap.hh b/src/3rdparty/harfbuzz-ng/src/hb-bimap.hh new file mode 100644 index 00000000000..9edefd97106 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-bimap.hh @@ -0,0 +1,164 @@ +/* + * Copyright ยฉ 2019 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_BIMAP_HH +#define HB_BIMAP_HH + +#include "hb.hh" +#include "hb-map.hh" + +/* Bi-directional map */ +struct hb_bimap_t +{ + void reset () + { + forw_map.reset (); + back_map.reset (); + } + + void resize (unsigned pop) + { + forw_map.resize (pop); + back_map.resize (pop); + } + + bool in_error () const { return forw_map.in_error () || back_map.in_error (); } + + void set (hb_codepoint_t lhs, hb_codepoint_t rhs) + { + if (in_error ()) return; + if (unlikely (lhs == HB_MAP_VALUE_INVALID)) return; + if (unlikely (rhs == HB_MAP_VALUE_INVALID)) { del (lhs); return; } + + forw_map.set (lhs, rhs); + if (unlikely (in_error ())) return; + + back_map.set (rhs, lhs); + if (unlikely (in_error ())) forw_map.del (lhs); + } + + hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); } + hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map.get (rhs); } + + hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); } + bool has (hb_codepoint_t lhs) const { return forw_map.has (lhs); } + + + void del (hb_codepoint_t lhs) + { + back_map.del (get (lhs)); + forw_map.del (lhs); + } + + void clear () + { + forw_map.clear (); + back_map.clear (); + } + + bool is_empty () const { return forw_map.is_empty (); } + + unsigned int get_population () const { return forw_map.get_population (); } + + + protected: + hb_map_t forw_map; + hb_map_t back_map; + + public: + auto keys () const HB_AUTO_RETURN (+ forw_map.keys()) + auto values () const HB_AUTO_RETURN (+ forw_map.values()) + auto iter () const HB_AUTO_RETURN (+ forw_map.iter()) +}; + +/* Inremental bimap: only lhs is given, rhs is incrementally assigned */ +struct hb_inc_bimap_t : hb_bimap_t +{ + /* Add a mapping from lhs to rhs with a unique value if lhs is unknown. + * Return the rhs value as the result. + */ + hb_codepoint_t add (hb_codepoint_t lhs) + { + hb_codepoint_t rhs = forw_map[lhs]; + if (rhs == HB_MAP_VALUE_INVALID) + { + rhs = next_value++; + set (lhs, rhs); + } + return rhs; + } + + hb_codepoint_t skip () + { return next_value++; } + + hb_codepoint_t skip (unsigned count) + { return next_value += count; } + + hb_codepoint_t get_next_value () const + { return next_value; } + + void add_set (const hb_set_t *set) + { + hb_codepoint_t i = HB_SET_VALUE_INVALID; + while (hb_set_next (set, &i)) add (i); + } + + /* Create an identity map. */ + bool identity (unsigned int size) + { + clear (); + for (hb_codepoint_t i = 0; i < size; i++) set (i, i); + return !in_error (); + } + + protected: + static int cmp_id (const void* a, const void* b) + { return (int)*(const hb_codepoint_t *)a - (int)*(const hb_codepoint_t *)b; } + + public: + /* Optional: after finished adding all mappings in a random order, + * reassign rhs to lhs so that they are in the same order. */ + void sort () + { + hb_codepoint_t count = get_population (); + hb_vector_t work; + work.resize (count); + + for (hb_codepoint_t rhs = 0; rhs < count; rhs++) + work[rhs] = back_map[rhs]; + + work.qsort (cmp_id); + + clear (); + for (hb_codepoint_t rhs = 0; rhs < count; rhs++) + set (work[rhs], rhs); + } + + protected: + unsigned int next_value = 0; +}; + +#endif /* HB_BIMAP_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-bit-page.hh b/src/3rdparty/harfbuzz-ng/src/hb-bit-page.hh new file mode 100644 index 00000000000..9b027ac5905 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-bit-page.hh @@ -0,0 +1,340 @@ +/* + * Copyright ยฉ 2012,2017 Google, Inc. + * Copyright ยฉ 2021 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BIT_PAGE_HH +#define HB_BIT_PAGE_HH + +#include "hb.hh" + + +/* Compiler-assisted vectorization. */ + +/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))), + * basically a fixed-size bitset. We can't use the compiler type because hb_vector_t cannot + * guarantee alignment requirements. */ +template +struct hb_vector_size_t +{ + elt_t& operator [] (unsigned int i) { return v[i]; } + const elt_t& operator [] (unsigned int i) const { return v[i]; } + + void init0 () + { + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + v[i] = 0; + } + void init1 () + { + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + v[i] = (elt_t) -1; + } + + template + hb_vector_size_t process (const Op& op) const + { + hb_vector_size_t r; + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + r.v[i] = op (v[i]); + return r; + } + template + hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const + { + hb_vector_size_t r; + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + r.v[i] = op (v[i], o.v[i]); + return r; + } + hb_vector_size_t operator | (const hb_vector_size_t &o) const + { return process (hb_bitwise_or, o); } + hb_vector_size_t operator & (const hb_vector_size_t &o) const + { return process (hb_bitwise_and, o); } + hb_vector_size_t operator ^ (const hb_vector_size_t &o) const + { return process (hb_bitwise_xor, o); } + hb_vector_size_t operator ~ () const + { return process (hb_bitwise_neg); } + + hb_array_t iter () const + { return hb_array (v); } + + private: + static_assert (0 == byte_size % sizeof (elt_t), ""); + elt_t v[byte_size / sizeof (elt_t)]; +}; + + +struct hb_bit_page_t +{ + void init0 () { v.init0 (); } + void init1 () { v.init1 (); } + + static inline constexpr unsigned len () + { return ARRAY_LENGTH_CONST (v); } + + bool is_empty () const + { + return + + hb_iter (v) + | hb_none + ; + } + uint32_t hash () const + { + return + + hb_iter (v) + | hb_reduce ([] (uint32_t h, const elt_t &_) { return h * 31 + hb_hash (_); }, (uint32_t) 0u) + ; + } + + void add (hb_codepoint_t g) { elt (g) |= mask (g); } + void del (hb_codepoint_t g) { elt (g) &= ~mask (g); } + void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); } + bool get (hb_codepoint_t g) const { return elt (g) & mask (g); } + + void add_range (hb_codepoint_t a, hb_codepoint_t b) + { + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la |= (mask (b) << 1) - mask(a); + else + { + *la |= ~(mask (a) - 1); + la++; + + hb_memset (la, 0xff, (char *) lb - (char *) la); + + *lb |= ((mask (b) << 1) - 1); + } + } + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la &= ~((mask (b) << 1) - mask(a)); + else + { + *la &= mask (a) - 1; + la++; + + hb_memset (la, 0, (char *) lb - (char *) la); + + *lb &= ~((mask (b) << 1) - 1); + } + } + void set_range (hb_codepoint_t a, hb_codepoint_t b, bool v) + { if (v) add_range (a, b); else del_range (a, b); } + + + // Writes out page values to the array p. Returns the number of values + // written. At most size codepoints will be written. + unsigned int write (uint32_t base, + unsigned int start_value, + hb_codepoint_t *p, + unsigned int size) const + { + unsigned int start_v = start_value / ELT_BITS; + unsigned int start_bit = start_value & ELT_MASK; + unsigned int count = 0; + for (unsigned i = start_v; i < len () && count < size; i++) + { + elt_t bits = v[i]; + uint32_t v_base = base | (i * ELT_BITS); + for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++) + { + if ((elt_t(1) << j) & bits) { + *p++ = v_base | j; + count++; + } + } + start_bit = 0; + } + return count; + } + + // Writes out the values NOT in this page to the array p. Returns the + // number of values written. At most size codepoints will be written. + // Returns the number of codepoints written. next_value holds the next value + // that should be written (if not present in this page). This is used to fill + // any missing value gaps between this page and the previous page, if any. + // next_value is updated to one more than the last value present in this page. + unsigned int write_inverted (uint32_t base, + unsigned int start_value, + hb_codepoint_t *p, + unsigned int size, + hb_codepoint_t *next_value) const + { + unsigned int start_v = start_value / ELT_BITS; + unsigned int start_bit = start_value & ELT_MASK; + unsigned int count = 0; + for (unsigned i = start_v; i < len () && count < size; i++) + { + elt_t bits = v[i]; + uint32_t v_offset = i * ELT_BITS; + for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++) + { + if ((elt_t(1) << j) & bits) + { + hb_codepoint_t value = base | v_offset | j; + // Emit all the missing values from next_value up to value - 1. + for (hb_codepoint_t k = *next_value; k < value && count < size; k++) + { + *p++ = k; + count++; + } + // Skip over this value; + *next_value = value + 1; + } + } + start_bit = 0; + } + return count; + } + + bool is_equal (const hb_bit_page_t &other) const + { + for (unsigned i = 0; i < len (); i++) + if (v[i] != other.v[i]) + return false; + return true; + } + bool is_subset (const hb_bit_page_t &larger_page) const + { + for (unsigned i = 0; i < len (); i++) + if (~larger_page.v[i] & v[i]) + return false; + return true; + } + + unsigned int get_population () const + { + return + + hb_iter (v) + | hb_reduce ([] (unsigned pop, const elt_t &_) { return pop + hb_popcount (_); }, 0u) + ; + } + + bool next (hb_codepoint_t *codepoint) const + { + unsigned int m = (*codepoint + 1) & MASK; + if (!m) + { + *codepoint = INVALID; + return false; + } + unsigned int i = m / ELT_BITS; + unsigned int j = m & ELT_MASK; + + const elt_t vv = v[i] & ~((elt_t (1) << j) - 1); + for (const elt_t *p = &vv; i < len (); p = &v[++i]) + if (*p) + { + *codepoint = i * ELT_BITS + elt_get_min (*p); + return true; + } + + *codepoint = INVALID; + return false; + } + bool previous (hb_codepoint_t *codepoint) const + { + unsigned int m = (*codepoint - 1) & MASK; + if (m == MASK) + { + *codepoint = INVALID; + return false; + } + unsigned int i = m / ELT_BITS; + unsigned int j = m & ELT_MASK; + + /* Fancy mask to avoid shifting by elt_t bitsize, which is undefined. */ + const elt_t mask = j < 8 * sizeof (elt_t) - 1 ? + ((elt_t (1) << (j + 1)) - 1) : + (elt_t) -1; + const elt_t vv = v[i] & mask; + const elt_t *p = &vv; + while (true) + { + if (*p) + { + *codepoint = i * ELT_BITS + elt_get_max (*p); + return true; + } + if ((int) i <= 0) break; + p = &v[--i]; + } + + *codepoint = INVALID; + return false; + } + hb_codepoint_t get_min () const + { + for (unsigned int i = 0; i < len (); i++) + if (v[i]) + return i * ELT_BITS + elt_get_min (v[i]); + return INVALID; + } + hb_codepoint_t get_max () const + { + for (int i = len () - 1; i >= 0; i--) + if (v[i]) + return i * ELT_BITS + elt_get_max (v[i]); + return 0; + } + + static constexpr hb_codepoint_t INVALID = HB_SET_VALUE_INVALID; + + typedef unsigned long long elt_t; + static constexpr unsigned PAGE_BITS_LOG_2 = 9; // 512 bits + static constexpr unsigned PAGE_BITS = 1 << PAGE_BITS_LOG_2; + static_assert (1 << PAGE_BITS_LOG_2 == PAGE_BITS, ""); + static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, ""); + static constexpr unsigned PAGE_BITMASK = PAGE_BITS - 1; + + static unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); } + static unsigned int elt_get_max (const elt_t &elt) { return hb_bit_storage (elt) - 1; } + + typedef hb_vector_size_t vector_t; + + static constexpr unsigned ELT_BITS = sizeof (elt_t) * 8; + static constexpr unsigned ELT_MASK = ELT_BITS - 1; + + static constexpr unsigned BITS = sizeof (vector_t) * 8; + static constexpr unsigned MASK = BITS - 1; + static_assert ((unsigned) PAGE_BITS == (unsigned) BITS, ""); + + elt_t &elt (hb_codepoint_t g) { return v[(g & MASK) / ELT_BITS]; } + const elt_t& elt (hb_codepoint_t g) const { return v[(g & MASK) / ELT_BITS]; } + static constexpr elt_t mask (hb_codepoint_t g) { return elt_t (1) << (g & ELT_MASK); } + + vector_t v; +}; +static_assert (hb_bit_page_t::PAGE_BITS == sizeof (hb_bit_page_t) * 8, ""); + + +#endif /* HB_BIT_PAGE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-bit-set-invertible.hh b/src/3rdparty/harfbuzz-ng/src/hb-bit-set-invertible.hh new file mode 100644 index 00000000000..1eb1b1c2092 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-bit-set-invertible.hh @@ -0,0 +1,378 @@ +/* + * Copyright ยฉ 2012,2017 Google, Inc. + * Copyright ยฉ 2021 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BIT_SET_INVERTIBLE_HH +#define HB_BIT_SET_INVERTIBLE_HH + +#include "hb.hh" +#include "hb-bit-set.hh" + + +struct hb_bit_set_invertible_t +{ + hb_bit_set_t s; + bool inverted = false; + + hb_bit_set_invertible_t () = default; + hb_bit_set_invertible_t (const hb_bit_set_invertible_t& o) = default; + hb_bit_set_invertible_t (hb_bit_set_invertible_t&& other) : hb_bit_set_invertible_t () { hb_swap (*this, other); } + hb_bit_set_invertible_t& operator= (const hb_bit_set_invertible_t& o) = default; + hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& other) { hb_swap (*this, other); return *this; } + friend void swap (hb_bit_set_invertible_t &a, hb_bit_set_invertible_t &b) + { + if (likely (!a.s.successful || !b.s.successful)) + return; + hb_swap (a.inverted, b.inverted); + hb_swap (a.s, b.s); + } + + void init () { s.init (); inverted = false; } + void fini () { s.fini (); } + void err () { s.err (); } + bool in_error () const { return s.in_error (); } + explicit operator bool () const { return !is_empty (); } + + void alloc (unsigned sz) { s.alloc (sz); } + void reset () + { + s.reset (); + inverted = false; + } + void clear () + { + s.clear (); + if (likely (s.successful)) + inverted = false; + } + void invert () + { + if (likely (s.successful)) + inverted = !inverted; + } + + bool is_inverted () const + { + return inverted; + } + + bool is_empty () const + { + hb_codepoint_t v = INVALID; + next (&v); + return v == INVALID; + } + uint32_t hash () const { return s.hash () ^ (uint32_t) inverted; } + + hb_codepoint_t get_min () const + { + hb_codepoint_t v = INVALID; + next (&v); + return v; + } + hb_codepoint_t get_max () const + { + hb_codepoint_t v = INVALID; + previous (&v); + return v; + } + unsigned int get_population () const + { return inverted ? INVALID - s.get_population () : s.get_population (); } + + + void add (hb_codepoint_t g) { unlikely (inverted) ? s.del (g) : s.add (g); } + bool add_range (hb_codepoint_t a, hb_codepoint_t b) + { return unlikely (inverted) ? ((void) s.del_range (a, b), true) : s.add_range (a, b); } + + template + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { inverted ? s.del_array (array, count, stride) : s.add_array (array, count, stride); } + template + void add_array (const hb_array_t& arr) { add_array (&arr, arr.len ()); } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return inverted ? s.del_sorted_array (array, count, stride) : s.add_sorted_array (array, count, stride); } + template + bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } + + void del (hb_codepoint_t g) { unlikely (inverted) ? s.add (g) : s.del (g); } + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { unlikely (inverted) ? (void) s.add_range (a, b) : s.del_range (a, b); } + + bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; } + + /* Has interface. */ + bool operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k]; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + /* Sink interface. */ + hb_bit_set_invertible_t& operator << (hb_codepoint_t v) + { add (v); return *this; } + hb_bit_set_invertible_t& operator << (const hb_pair_t& range) + { add_range (range.first, range.second); return *this; } + + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const + { + hb_codepoint_t c = first - 1; + return next (&c) && c <= last; + } + + void set (const hb_bit_set_invertible_t &other) + { + s.set (other.s); + if (likely (s.successful)) + inverted = other.inverted; + } + + bool is_equal (const hb_bit_set_invertible_t &other) const + { + if (likely (inverted == other.inverted)) + return s.is_equal (other.s); + else + { + /* TODO Add iter_ranges() and use here. */ + auto it1 = iter (); + auto it2 = other.iter (); + return hb_all (+ hb_zip (it1, it2) + | hb_map ([](hb_pair_t _) { return _.first == _.second; })); + } + } + + bool is_subset (const hb_bit_set_invertible_t &larger_set) const + { + if (unlikely (inverted != larger_set.inverted)) + return hb_all (hb_iter (s) | hb_map (larger_set.s)); + else + return unlikely (inverted) ? larger_set.s.is_subset (s) : s.is_subset (larger_set.s); + } + + protected: + template + void process (const Op& op, const hb_bit_set_invertible_t &other) + { s.process (op, other.s); } + public: + void union_ (const hb_bit_set_invertible_t &other) + { + if (likely (inverted == other.inverted)) + { + if (unlikely (inverted)) + process (hb_bitwise_and, other); + else + process (hb_bitwise_or, other); /* Main branch. */ + } + else + { + if (unlikely (inverted)) + process (hb_bitwise_gt, other); + else + process (hb_bitwise_lt, other); + } + if (likely (s.successful)) + inverted = inverted || other.inverted; + } + void intersect (const hb_bit_set_invertible_t &other) + { + if (likely (inverted == other.inverted)) + { + if (unlikely (inverted)) + process (hb_bitwise_or, other); + else + process (hb_bitwise_and, other); /* Main branch. */ + } + else + { + if (unlikely (inverted)) + process (hb_bitwise_lt, other); + else + process (hb_bitwise_gt, other); + } + if (likely (s.successful)) + inverted = inverted && other.inverted; + } + void subtract (const hb_bit_set_invertible_t &other) + { + if (likely (inverted == other.inverted)) + { + if (unlikely (inverted)) + process (hb_bitwise_lt, other); + else + process (hb_bitwise_gt, other); /* Main branch. */ + } + else + { + if (unlikely (inverted)) + process (hb_bitwise_or, other); + else + process (hb_bitwise_and, other); + } + if (likely (s.successful)) + inverted = inverted && !other.inverted; + } + void symmetric_difference (const hb_bit_set_invertible_t &other) + { + process (hb_bitwise_xor, other); + if (likely (s.successful)) + inverted = inverted ^ other.inverted; + } + + bool next (hb_codepoint_t *codepoint) const + { + if (likely (!inverted)) + return s.next (codepoint); + + auto old = *codepoint; + if (unlikely (old + 1 == INVALID)) + { + *codepoint = INVALID; + return false; + } + + auto v = old; + s.next (&v); + if (old + 1 < v) + { + *codepoint = old + 1; + return true; + } + + v = old; + s.next_range (&old, &v); + + *codepoint = v + 1; + return *codepoint != INVALID; + } + bool previous (hb_codepoint_t *codepoint) const + { + if (likely (!inverted)) + return s.previous (codepoint); + + auto old = *codepoint; + if (unlikely (old - 1 == INVALID)) + { + *codepoint = INVALID; + return false; + } + + auto v = old; + s.previous (&v); + + if (old - 1 > v || v == INVALID) + { + *codepoint = old - 1; + return true; + } + + v = old; + s.previous_range (&v, &old); + + *codepoint = v - 1; + return *codepoint != INVALID; + } + bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + if (likely (!inverted)) + return s.next_range (first, last); + + if (!next (last)) + { + *last = *first = INVALID; + return false; + } + + *first = *last; + s.next (last); + --*last; + return true; + } + bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + if (likely (!inverted)) + return s.previous_range (first, last); + + if (!previous (first)) + { + *last = *first = INVALID; + return false; + } + + *last = *first; + s.previous (first); + ++*first; + return true; + } + + unsigned int next_many (hb_codepoint_t codepoint, + hb_codepoint_t *out, + unsigned int size) const + { + return inverted ? s.next_many_inverted (codepoint, out, size) + : s.next_many (codepoint, out, size); + } + + static constexpr hb_codepoint_t INVALID = hb_bit_set_t::INVALID; + + /* + * Iterator implementation. + */ + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + iter_t (const hb_bit_set_invertible_t &s_ = Null (hb_bit_set_invertible_t), + bool init = true) : s (&s_), v (INVALID), l(0) + { + if (init) + { + l = s->get_population () + 1; + __next__ (); + } + } + + typedef hb_codepoint_t __item_t__; + hb_codepoint_t __item__ () const { return v; } + bool __more__ () const { return v != INVALID; } + void __next__ () { s->next (&v); if (l) l--; } + void __prev__ () { s->previous (&v); } + unsigned __len__ () const { return l; } + iter_t end () const { return iter_t (*s, false); } + bool operator != (const iter_t& o) const + { return s != o.s || v != o.v; } + + protected: + const hb_bit_set_invertible_t *s; + hb_codepoint_t v; + unsigned l; + }; + iter_t iter () const { return iter_t (*this); } + operator iter_t () const { return iter (); } +}; + + +#endif /* HB_BIT_SET_INVERTIBLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-bit-set.hh b/src/3rdparty/harfbuzz-ng/src/hb-bit-set.hh new file mode 100644 index 00000000000..c30b2af7b0d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-bit-set.hh @@ -0,0 +1,968 @@ +/* + * Copyright ยฉ 2012,2017 Google, Inc. + * Copyright ยฉ 2021 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BIT_SET_HH +#define HB_BIT_SET_HH + +#include "hb.hh" +#include "hb-bit-page.hh" +#include "hb-machinery.hh" + + +struct hb_bit_set_t +{ + hb_bit_set_t () = default; + ~hb_bit_set_t () = default; + + hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other, true); } + hb_bit_set_t ( hb_bit_set_t&& other) : hb_bit_set_t () { hb_swap (*this, other); } + hb_bit_set_t& operator= (const hb_bit_set_t& other) { set (other); return *this; } + hb_bit_set_t& operator= (hb_bit_set_t&& other) { hb_swap (*this, other); return *this; } + friend void swap (hb_bit_set_t &a, hb_bit_set_t &b) + { + if (likely (!a.successful || !b.successful)) + return; + hb_swap (a.population, b.population); + hb_swap (a.last_page_lookup, b.last_page_lookup); + hb_swap (a.page_map, b.page_map); + hb_swap (a.pages, b.pages); + } + + void init () + { + successful = true; + population = 0; + last_page_lookup = 0; + page_map.init (); + pages.init (); + } + void fini () + { + page_map.fini (); + pages.fini (); + } + + using page_t = hb_bit_page_t; + struct page_map_t + { + int cmp (const page_map_t &o) const { return cmp (o.major); } + int cmp (uint32_t o_major) const { return (int) o_major - (int) major; } + + uint32_t major; + uint32_t index; + }; + + bool successful = true; /* Allocations successful */ + mutable unsigned int population = 0; + mutable hb_atomic_int_t last_page_lookup = 0; + hb_sorted_vector_t page_map; + hb_vector_t pages; + + void err () { if (successful) successful = false; } /* TODO Remove */ + bool in_error () const { return !successful; } + + bool resize (unsigned int count, bool clear = true, bool exact_size = false) + { + if (unlikely (!successful)) return false; + + if (pages.length == 0 && count == 1) + exact_size = true; // Most sets are small and local + + if (unlikely (!pages.resize (count, clear, exact_size) || !page_map.resize (count, clear, exact_size))) + { + pages.resize (page_map.length, clear, exact_size); + successful = false; + return false; + } + return true; + } + + void alloc (unsigned sz) + { + sz >>= (page_t::PAGE_BITS_LOG_2 - 1); + pages.alloc (sz); + page_map.alloc (sz); + } + + void reset () + { + successful = true; + clear (); + } + + void clear () + { + resize (0); + if (likely (successful)) + population = 0; + } + bool is_empty () const + { + unsigned int count = pages.length; + for (unsigned int i = 0; i < count; i++) + if (!pages[i].is_empty ()) + return false; + return true; + } + explicit operator bool () const { return !is_empty (); } + + uint32_t hash () const + { + uint32_t h = 0; + for (auto &map : page_map) + h = h * 31 + hb_hash (map.major) + hb_hash (pages[map.index]); + return h; + } + + private: + void dirty () { population = UINT_MAX; } + public: + + void add (hb_codepoint_t g) + { + if (unlikely (!successful)) return; + if (unlikely (g == INVALID)) return; + dirty (); + page_t *page = page_for (g, true); if (unlikely (!page)) return; + page->add (g); + } + bool add_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */ + if (unlikely (a > b || a == INVALID || b == INVALID)) return false; + dirty (); + unsigned int ma = get_major (a); + unsigned int mb = get_major (b); + if (ma == mb) + { + page_t *page = page_for (a, true); if (unlikely (!page)) return false; + page->add_range (a, b); + } + else + { + page_t *page = page_for (a, true); if (unlikely (!page)) return false; + page->add_range (a, major_start (ma + 1) - 1); + + for (unsigned int m = ma + 1; m < mb; m++) + { + page = page_for (major_start (m), true); if (unlikely (!page)) return false; + page->init1 (); + } + + page = page_for (b, true); if (unlikely (!page)) return false; + page->add_range (major_start (mb), b); + } + return true; + } + + template + void set_array (bool v, const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + if (unlikely (!successful)) return; + if (!count) return; + dirty (); + hb_codepoint_t g = *array; + while (count) + { + unsigned int m = get_major (g); + page_t *page = page_for (g, v); if (unlikely (v && !page)) return; + unsigned int start = major_start (m); + unsigned int end = major_start (m + 1); + do + { + if (g != INVALID && (v || page)) /* The v check is to optimize out the page check if v is true. */ + page->set (g, v); + + array = &StructAtOffsetUnaligned (array, stride); + count--; + } + while (count && (g = *array, start <= g && g < end)); + } + } + + template + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { set_array (true, array, count, stride); } + template + void add_array (const hb_array_t& arr) { add_array (&arr, arr.len ()); } + + template + void del_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { set_array (false, array, count, stride); } + template + void del_array (const hb_array_t& arr) { del_array (&arr, arr.len ()); } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template + bool set_sorted_array (bool v, const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */ + if (unlikely (!count)) return true; + dirty (); + hb_codepoint_t g = *array; + hb_codepoint_t last_g = g; + while (count) + { + unsigned int m = get_major (g); + page_t *page = page_for (g, v); if (unlikely (v && !page)) return false; + unsigned int end = major_start (m + 1); + do + { + /* If we try harder we can change the following comparison to <=; + * Not sure if it's worth it. */ + if (g < last_g) return false; + last_g = g; + + if (g != INVALID && (v || page)) /* The v check is to optimize out the page check if v is true. */ + page->add (g); + + array = &StructAtOffsetUnaligned (array, stride); + count--; + } + while (count && (g = *array, g < end)); + } + return true; + } + + template + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return set_sorted_array (true, array, count, stride); } + template + bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } + + template + bool del_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return set_sorted_array (false, array, count, stride); } + template + bool del_sorted_array (const hb_sorted_array_t& arr) { return del_sorted_array (&arr, arr.len ()); } + + void del (hb_codepoint_t g) + { + if (unlikely (!successful)) return; + page_t *page = page_for (g); + if (!page) + return; + dirty (); + page->del (g); + } + + private: + void del_pages (int ds, int de) + { + if (ds <= de) + { + // Pre-allocate the workspace that compact() will need so we can bail on allocation failure + // before attempting to rewrite the page map. + hb_vector_t compact_workspace; + if (unlikely (!allocate_compact_workspace (compact_workspace))) return; + + unsigned int write_index = 0; + for (unsigned int i = 0; i < page_map.length; i++) + { + int m = (int) page_map[i].major; + if (m < ds || de < m) + page_map[write_index++] = page_map[i]; + } + compact (compact_workspace, write_index); + resize (write_index); + } + } + + + public: + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (!successful)) return; + if (unlikely (a > b || a == INVALID)) return; + dirty (); + unsigned int ma = get_major (a); + unsigned int mb = get_major (b); + /* Delete pages from ds through de if ds <= de. */ + int ds = (a == major_start (ma))? (int) ma: (int) (ma + 1); + int de = (b + 1 == major_start (mb + 1))? (int) mb: ((int) mb - 1); + if (ds > de || (int) ma < ds) + { + page_t *page = page_for (a); + if (page) + { + if (ma == mb) + page->del_range (a, b); + else + page->del_range (a, major_start (ma + 1) - 1); + } + } + if (de < (int) mb && ma != mb) + { + page_t *page = page_for (b); + if (page) + page->del_range (major_start (mb), b); + } + del_pages (ds, de); + } + + bool get (hb_codepoint_t g) const + { + const page_t *page = page_for (g); + if (!page) + return false; + return page->get (g); + } + + /* Has interface. */ + bool operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k]; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + /* Sink interface. */ + hb_bit_set_t& operator << (hb_codepoint_t v) + { add (v); return *this; } + hb_bit_set_t& operator << (const hb_pair_t& range) + { add_range (range.first, range.second); return *this; } + + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const + { + hb_codepoint_t c = first - 1; + return next (&c) && c <= last; + } + void set (const hb_bit_set_t &other, bool exact_size = false) + { + if (unlikely (!successful)) return; + unsigned int count = other.pages.length; + if (unlikely (!resize (count, false, exact_size))) + return; + population = other.population; + + page_map = other.page_map; + pages = other.pages; + } + + bool is_equal (const hb_bit_set_t &other) const + { + if (has_population () && other.has_population () && + population != other.population) + return false; + + unsigned int na = pages.length; + unsigned int nb = other.pages.length; + + unsigned int a = 0, b = 0; + for (; a < na && b < nb; ) + { + if (page_at (a).is_empty ()) { a++; continue; } + if (other.page_at (b).is_empty ()) { b++; continue; } + if (page_map[a].major != other.page_map[b].major || + !page_at (a).is_equal (other.page_at (b))) + return false; + a++; + b++; + } + for (; a < na; a++) + if (!page_at (a).is_empty ()) { return false; } + for (; b < nb; b++) + if (!other.page_at (b).is_empty ()) { return false; } + + return true; + } + + bool is_subset (const hb_bit_set_t &larger_set) const + { + if (has_population () && larger_set.has_population () && + population > larger_set.population) + return false; + + uint32_t spi = 0; + for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++) + { + uint32_t spm = page_map[spi].major; + uint32_t lpm = larger_set.page_map[lpi].major; + auto sp = page_at (spi); + auto lp = larger_set.page_at (lpi); + + if (spm < lpm && !sp.is_empty ()) + return false; + + if (lpm < spm) + continue; + + if (!sp.is_subset (lp)) + return false; + + spi++; + } + + while (spi < page_map.length) + if (!page_at (spi++).is_empty ()) + return false; + + return true; + } + + private: + bool allocate_compact_workspace (hb_vector_t& workspace) + { + if (unlikely (!workspace.resize_exact (pages.length))) + { + successful = false; + return false; + } + + return true; + } + + /* + * workspace should be a pre-sized vector allocated to hold at exactly pages.length + * elements. + */ + void compact (hb_vector_t& workspace, + unsigned int length) + { + assert(workspace.length == pages.length); + hb_vector_t& old_index_to_page_map_index = workspace; + + hb_fill (old_index_to_page_map_index.writer(), 0xFFFFFFFF); + for (unsigned i = 0; i < length; i++) + old_index_to_page_map_index[page_map[i].index] = i; + + compact_pages (old_index_to_page_map_index); + } + void compact_pages (const hb_vector_t& old_index_to_page_map_index) + { + unsigned int write_index = 0; + for (unsigned int i = 0; i < pages.length; i++) + { + if (old_index_to_page_map_index[i] == 0xFFFFFFFF) continue; + + if (write_index < i) + pages[write_index] = pages[i]; + + page_map[old_index_to_page_map_index[i]].index = write_index; + write_index++; + } + } + public: + + void process_ (hb_bit_page_t::vector_t (*op) (const hb_bit_page_t::vector_t &, const hb_bit_page_t::vector_t &), + bool passthru_left, bool passthru_right, + const hb_bit_set_t &other) + { + if (unlikely (!successful)) return; + + dirty (); + + unsigned int na = pages.length; + unsigned int nb = other.pages.length; + unsigned int next_page = na; + + unsigned int count = 0, newCount = 0; + unsigned int a = 0, b = 0; + unsigned int write_index = 0; + + // Pre-allocate the workspace that compact() will need so we can bail on allocation failure + // before attempting to rewrite the page map. + hb_vector_t compact_workspace; + if (!passthru_left && unlikely (!allocate_compact_workspace (compact_workspace))) return; + + for (; a < na && b < nb; ) + { + if (page_map[a].major == other.page_map[b].major) + { + if (!passthru_left) + { + // Move page_map entries that we're keeping from the left side set + // to the front of the page_map vector. This isn't necessary if + // passthru_left is set since no left side pages will be removed + // in that case. + if (write_index < a) + page_map[write_index] = page_map[a]; + write_index++; + } + + count++; + a++; + b++; + } + else if (page_map[a].major < other.page_map[b].major) + { + if (passthru_left) + count++; + a++; + } + else + { + if (passthru_right) + count++; + b++; + } + } + if (passthru_left) + count += na - a; + if (passthru_right) + count += nb - b; + + if (!passthru_left) + { + na = write_index; + next_page = write_index; + compact (compact_workspace, write_index); + } + + if (unlikely (!resize (count))) + return; + + newCount = count; + + /* Process in-place backward. */ + a = na; + b = nb; + for (; a && b; ) + { + if (page_map.arrayZ[a - 1].major == other.page_map.arrayZ[b - 1].major) + { + a--; + b--; + count--; + page_map.arrayZ[count] = page_map.arrayZ[a]; + page_at (count).v = op (page_at (a).v, other.page_at (b).v); + } + else if (page_map.arrayZ[a - 1].major > other.page_map.arrayZ[b - 1].major) + { + a--; + if (passthru_left) + { + count--; + page_map.arrayZ[count] = page_map.arrayZ[a]; + } + } + else + { + b--; + if (passthru_right) + { + count--; + page_map.arrayZ[count].major = other.page_map.arrayZ[b].major; + page_map.arrayZ[count].index = next_page++; + page_at (count).v = other.page_at (b).v; + } + } + } + if (passthru_left) + while (a) + { + a--; + count--; + page_map.arrayZ[count] = page_map.arrayZ[a]; + } + if (passthru_right) + while (b) + { + b--; + count--; + page_map.arrayZ[count].major = other.page_map.arrayZ[b].major; + page_map.arrayZ[count].index = next_page++; + page_at (count).v = other.page_at (b).v; + } + assert (!count); + resize (newCount); + } + template + static hb_bit_page_t::vector_t + op_ (const hb_bit_page_t::vector_t &a, const hb_bit_page_t::vector_t &b) + { return Op{} (a, b); } + template + void process (const Op& op, const hb_bit_set_t &other) + { + process_ (op_, op (1, 0), op (0, 1), other); + } + + void union_ (const hb_bit_set_t &other) { process (hb_bitwise_or, other); } + void intersect (const hb_bit_set_t &other) { process (hb_bitwise_and, other); } + void subtract (const hb_bit_set_t &other) { process (hb_bitwise_gt, other); } + void symmetric_difference (const hb_bit_set_t &other) { process (hb_bitwise_xor, other); } + + bool next (hb_codepoint_t *codepoint) const + { + if (unlikely (*codepoint == INVALID)) { + *codepoint = get_min (); + return *codepoint != INVALID; + } + + const auto* page_map_array = page_map.arrayZ; + unsigned int major = get_major (*codepoint); + unsigned int i = last_page_lookup; + + if (unlikely (i >= page_map.length || page_map_array[i].major != major)) + { + page_map.bfind (major, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (i >= page_map.length) { + *codepoint = INVALID; + return false; + } + } + + const auto* pages_array = pages.arrayZ; + const page_map_t ¤t = page_map_array[i]; + if (likely (current.major == major)) + { + if (pages_array[current.index].next (codepoint)) + { + *codepoint += current.major * page_t::PAGE_BITS; + last_page_lookup = i; + return true; + } + i++; + } + + for (; i < page_map.length; i++) + { + const page_map_t ¤t = page_map_array[i]; + hb_codepoint_t m = pages_array[current.index].get_min (); + if (m != INVALID) + { + *codepoint = current.major * page_t::PAGE_BITS + m; + last_page_lookup = i; + return true; + } + } + last_page_lookup = 0; + *codepoint = INVALID; + return false; + } + bool previous (hb_codepoint_t *codepoint) const + { + if (unlikely (*codepoint == INVALID)) { + *codepoint = get_max (); + return *codepoint != INVALID; + } + + page_map_t map = {get_major (*codepoint), 0}; + unsigned int i; + page_map.bfind (map, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (i < page_map.length && page_map.arrayZ[i].major == map.major) + { + if (pages[page_map.arrayZ[i].index].previous (codepoint)) + { + *codepoint += page_map.arrayZ[i].major * page_t::PAGE_BITS; + return true; + } + } + i--; + for (; (int) i >= 0; i--) + { + hb_codepoint_t m = pages.arrayZ[page_map.arrayZ[i].index].get_max (); + if (m != INVALID) + { + *codepoint = page_map.arrayZ[i].major * page_t::PAGE_BITS + m; + return true; + } + } + *codepoint = INVALID; + return false; + } + bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + hb_codepoint_t i; + + i = *last; + if (!next (&i)) + { + *last = *first = INVALID; + return false; + } + + /* TODO Speed up. */ + *last = *first = i; + while (next (&i) && i == *last + 1) + (*last)++; + + return true; + } + bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + hb_codepoint_t i; + + i = *first; + if (!previous (&i)) + { + *last = *first = INVALID; + return false; + } + + /* TODO Speed up. */ + *last = *first = i; + while (previous (&i) && i == *first - 1) + (*first)--; + + return true; + } + + unsigned int next_many (hb_codepoint_t codepoint, + hb_codepoint_t *out, + unsigned int size) const + { + // By default, start at the first bit of the first page of values. + unsigned int start_page = 0; + unsigned int start_page_value = 0; + if (unlikely (codepoint != INVALID)) + { + const auto* page_map_array = page_map.arrayZ; + unsigned int major = get_major (codepoint); + unsigned int i = last_page_lookup; + if (unlikely (i >= page_map.length || page_map_array[i].major != major)) + { + page_map.bfind (major, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (i >= page_map.length) + return 0; // codepoint is greater than our max element. + } + start_page = i; + start_page_value = page_remainder (codepoint + 1); + if (unlikely (start_page_value == 0)) + { + // The export-after value was last in the page. Start on next page. + start_page++; + start_page_value = 0; + } + } + + unsigned int initial_size = size; + for (unsigned int i = start_page; i < page_map.length && size; i++) + { + uint32_t base = major_start (page_map[i].major); + unsigned int n = pages[page_map[i].index].write (base, start_page_value, out, size); + out += n; + size -= n; + start_page_value = 0; + } + return initial_size - size; + } + + unsigned int next_many_inverted (hb_codepoint_t codepoint, + hb_codepoint_t *out, + unsigned int size) const + { + unsigned int initial_size = size; + // By default, start at the first bit of the first page of values. + unsigned int start_page = 0; + unsigned int start_page_value = 0; + if (unlikely (codepoint != INVALID)) + { + const auto* page_map_array = page_map.arrayZ; + unsigned int major = get_major (codepoint); + unsigned int i = last_page_lookup; + if (unlikely (i >= page_map.length || page_map_array[i].major != major)) + { + page_map.bfind(major, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (unlikely (i >= page_map.length)) + { + // codepoint is greater than our max element. + while (++codepoint != INVALID && size) + { + *out++ = codepoint; + size--; + } + return initial_size - size; + } + } + start_page = i; + start_page_value = page_remainder (codepoint + 1); + if (unlikely (start_page_value == 0)) + { + // The export-after value was last in the page. Start on next page. + start_page++; + start_page_value = 0; + } + } + + hb_codepoint_t next_value = codepoint + 1; + for (unsigned int i=start_page; i= 0; i--) + { + const auto& map = page_map[(unsigned) i]; + const auto& page = pages[map.index]; + + if (!page.is_empty ()) + return map.major * page_t::PAGE_BITS + page.get_max (); + } + return INVALID; + } + + static constexpr hb_codepoint_t INVALID = page_t::INVALID; + + /* + * Iterator implementation. + */ + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + iter_t (const hb_bit_set_t &s_ = Null (hb_bit_set_t), + bool init = true) : s (&s_), v (INVALID), l(0) + { + if (init) + { + l = s->get_population () + 1; + __next__ (); + } + } + + typedef hb_codepoint_t __item_t__; + hb_codepoint_t __item__ () const { return v; } + bool __more__ () const { return v != INVALID; } + void __next__ () { s->next (&v); if (l) l--; } + void __prev__ () { s->previous (&v); } + unsigned __len__ () const { return l; } + iter_t end () const { return iter_t (*s, false); } + bool operator != (const iter_t& o) const + { return s != o.s || v != o.v; } + + protected: + const hb_bit_set_t *s; + hb_codepoint_t v; + unsigned l; + }; + iter_t iter () const { return iter_t (*this); } + operator iter_t () const { return iter (); } + + protected: + + page_t *page_for (hb_codepoint_t g, bool insert = false) + { + unsigned major = get_major (g); + + /* The extra page_map length is necessary; can't just rely on vector here, + * since the next check would be tricked because a null page also has + * major==0, which we can't distinguish from an actualy major==0 page... */ + unsigned i = last_page_lookup; + if (likely (i < page_map.length)) + { + auto &cached_page = page_map.arrayZ[i]; + if (cached_page.major == major) + return &pages.arrayZ[cached_page.index]; + } + + page_map_t map = {major, pages.length}; + if (!page_map.bfind (map, &i, HB_NOT_FOUND_STORE_CLOSEST)) + { + if (!insert) + return nullptr; + + if (unlikely (!resize (pages.length + 1))) + return nullptr; + + pages.arrayZ[map.index].init0 (); + memmove (page_map.arrayZ + i + 1, + page_map.arrayZ + i, + (page_map.length - 1 - i) * page_map.item_size); + page_map[i] = map; + } + + last_page_lookup = i; + return &pages.arrayZ[page_map.arrayZ[i].index]; + } + const page_t *page_for (hb_codepoint_t g) const + { + unsigned major = get_major (g); + + /* The extra page_map length is necessary; can't just rely on vector here, + * since the next check would be tricked because a null page also has + * major==0, which we can't distinguish from an actualy major==0 page... */ + unsigned i = last_page_lookup; + if (likely (i < page_map.length)) + { + auto &cached_page = page_map.arrayZ[i]; + if (cached_page.major == major) + return &pages.arrayZ[cached_page.index]; + } + + page_map_t key = {major}; + if (!page_map.bfind (key, &i)) + return nullptr; + + last_page_lookup = i; + return &pages.arrayZ[page_map[i].index]; + } + page_t &page_at (unsigned int i) + { + assert (i < page_map.length); + return pages.arrayZ[page_map.arrayZ[i].index]; + } + const page_t &page_at (unsigned int i) const + { + assert (i < page_map.length); + return pages.arrayZ[page_map.arrayZ[i].index]; + } + unsigned int get_major (hb_codepoint_t g) const { return g >> page_t::PAGE_BITS_LOG_2; } + unsigned int page_remainder (hb_codepoint_t g) const { return g & page_t::PAGE_BITMASK; } + hb_codepoint_t major_start (unsigned int major) const { return major << page_t::PAGE_BITS_LOG_2; } +}; + + +#endif /* HB_BIT_SET_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-blob.cc b/src/3rdparty/harfbuzz-ng/src/hb-blob.cc index 59c83336374..265effba037 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-blob.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-blob.cc @@ -1,5 +1,6 @@ /* * Copyright ยฉ 2009 Red Hat, Inc. + * Copyright ยฉ 2018 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -24,15 +25,8 @@ * Red Hat Author(s): Behdad Esfahbod */ -/* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */ -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 199309L -#endif - -#include "hb-private.hh" -#include "hb-debug.hh" - -#include "hb-object-private.hh" +#include "hb.hh" +#include "hb-blob.hh" #ifdef HAVE_SYS_MMAN_H #ifdef HAVE_UNISTD_H @@ -41,36 +35,19 @@ #include #endif /* HAVE_SYS_MMAN_H */ -#include -#include +/** + * SECTION: hb-blob + * @title: hb-blob + * @short_description: Binary data containers + * @include: hb.h + * + * Blobs wrap a chunk of binary data to handle lifecycle management of data + * while it is passed between client and HarfBuzz. Blobs are primarily used + * to create font faces, but also to access font face tables, as well as + * pass around other binary data. + **/ -struct hb_blob_t { - hb_object_header_t header; - ASSERT_POD (); - - bool immutable; - - const char *data; - unsigned int length; - hb_memory_mode_t mode; - - void *user_data; - hb_destroy_func_t destroy; -}; - - -static bool _try_writable (hb_blob_t *blob); - -static void -_hb_blob_destroy_user_data (hb_blob_t *blob) -{ - if (blob->destroy) { - blob->destroy (blob->user_data); - blob->user_data = nullptr; - blob->destroy = nullptr; - } -} /** * hb_blob_create: (skip) @@ -78,7 +55,7 @@ _hb_blob_destroy_user_data (hb_blob_t *blob) * @length: Length of @data in bytes. * @mode: Memory mode for @data. * @user_data: Data parameter to pass to @destroy. - * @destroy: Callback to call when @data is not needed anymore. + * @destroy: (nullable): Callback to call when @data is not needed anymore. * * Creates a new "blob" object wrapping @data. The @mode parameter is used * to negotiate ownership and lifecycle of @data. @@ -95,16 +72,54 @@ hb_blob_create (const char *data, void *user_data, hb_destroy_func_t destroy) { - hb_blob_t *blob; - - if (!length || - length >= 1u << 31 || - !(blob = hb_object_create ())) { + if (!length) + { if (destroy) destroy (user_data); return hb_blob_get_empty (); } + hb_blob_t *blob = hb_blob_create_or_fail (data, length, mode, + user_data, destroy); + return likely (blob) ? blob : hb_blob_get_empty (); +} + +/** + * hb_blob_create_or_fail: (skip) + * @data: Pointer to blob data. + * @length: Length of @data in bytes. + * @mode: Memory mode for @data. + * @user_data: Data parameter to pass to @destroy. + * @destroy: (nullable): Callback to call when @data is not needed anymore. + * + * Creates a new "blob" object wrapping @data. The @mode parameter is used + * to negotiate ownership and lifecycle of @data. + * + * Note that this function returns a freshly-allocated empty blob even if @length + * is zero. This is in contrast to hb_blob_create(), which returns the singleton + * empty blob (as returned by hb_blob_get_empty()) if @length is zero. + * + * Return value: New blob, or `NULL` if failed. Destroy with hb_blob_destroy(). + * + * Since: 2.8.2 + **/ +hb_blob_t * +hb_blob_create_or_fail (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy) +{ + hb_blob_t *blob; + + if (length >= 1u << 31 || + !(blob = hb_object_create ())) + { + if (destroy) + destroy (user_data); + return nullptr; + } + blob->data = data; blob->length = length; blob->mode = mode; @@ -114,9 +129,10 @@ hb_blob_create (const char *data, if (blob->mode == HB_MEMORY_MODE_DUPLICATE) { blob->mode = HB_MEMORY_MODE_READONLY; - if (!_try_writable (blob)) { + if (!blob->try_make_writable ()) + { hb_blob_destroy (blob); - return hb_blob_get_empty (); + return nullptr; } } @@ -136,7 +152,7 @@ _hb_blob_destroy (void *data) * @length: Length of sub-blob. * * Returns a blob that represents a range of bytes in @parent. The new - * blob is always created with %HB_MEMORY_MODE_READONLY, meaning that it + * blob is always created with #HB_MEMORY_MODE_READONLY, meaning that it * will never modify data in the parent blob. The parent data is not * expected to be modified, and will result in undefined behavior if it * is. @@ -156,13 +172,13 @@ hb_blob_create_sub_blob (hb_blob_t *parent, { hb_blob_t *blob; - if (!length || offset >= parent->length) + if (!length || !parent || offset >= parent->length) return hb_blob_get_empty (); hb_blob_make_immutable (parent); blob = hb_blob_create (parent->data + offset, - MIN (length, parent->length - offset), + hb_min (length, parent->length - offset), HB_MEMORY_MODE_READONLY, hb_blob_reference (parent), _hb_blob_destroy); @@ -170,6 +186,31 @@ hb_blob_create_sub_blob (hb_blob_t *parent, return blob; } +/** + * hb_blob_copy_writable_or_fail: + * @blob: A blob. + * + * Makes a writable copy of @blob. + * + * Return value: The new blob, or nullptr if allocation failed + * + * Since: 1.8.0 + **/ +hb_blob_t * +hb_blob_copy_writable_or_fail (hb_blob_t *blob) +{ + blob = hb_blob_create (blob->data, + blob->length, + HB_MEMORY_MODE_DUPLICATE, + nullptr, + nullptr); + + if (unlikely (blob == hb_blob_get_empty ())) + blob = nullptr; + + return blob; +} + /** * hb_blob_get_empty: * @@ -177,27 +218,14 @@ hb_blob_create_sub_blob (hb_blob_t *parent, * * See TODO:link object types for more information. * - * Return value: (transfer full): the empty blob. + * Return value: (transfer full): The empty blob. * * Since: 0.9.2 **/ hb_blob_t * -hb_blob_get_empty (void) +hb_blob_get_empty () { - static const hb_blob_t _hb_blob_nil = { - HB_OBJECT_HEADER_STATIC, - - true, /* immutable */ - - nullptr, /* data */ - 0, /* length */ - HB_MEMORY_MODE_READONLY, /* mode */ - - nullptr, /* user_data */ - nullptr /* destroy */ - }; - - return const_cast (&_hb_blob_nil); + return const_cast (&Null (hb_blob_t)); } /** @@ -222,7 +250,7 @@ hb_blob_reference (hb_blob_t *blob) * hb_blob_destroy: (skip) * @blob: a blob. * - * Descreases the reference count on @blob, and if it reaches zero, destroys + * Decreases the reference count on @blob, and if it reaches zero, destroys * @blob, freeing all memory, possibly calling the destroy-callback the blob * was created for if it has not been called already. * @@ -235,20 +263,20 @@ hb_blob_destroy (hb_blob_t *blob) { if (!hb_object_destroy (blob)) return; - _hb_blob_destroy_user_data (blob); - - free (blob); + hb_free (blob); } /** * hb_blob_set_user_data: (skip) - * @blob: a blob. - * @key: key for data to set. - * @data: data to set. - * @destroy: callback to call when @data is not needed anymore. - * @replace: whether to replace an existing data with the same key. + * @blob: An #hb_blob_t + * @key: The user-data key to set + * @data: A pointer to the user data to set + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key * - * Return value: + * Attaches a user-data key/data pair to the specified blob. + * + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -264,17 +292,18 @@ hb_blob_set_user_data (hb_blob_t *blob, /** * hb_blob_get_user_data: (skip) - * @blob: a blob. - * @key: key for data to get. + * @blob: a blob + * @key: The user-data key to query * - * + * Fetches the user data associated with the specified key, + * attached to the specified font-functions structure. * - * Return value: (transfer none): + * Return value: (transfer none): A pointer to the user data * * Since: 0.9.2 **/ void * -hb_blob_get_user_data (hb_blob_t *blob, +hb_blob_get_user_data (const hb_blob_t *blob, hb_user_data_key_t *key) { return hb_object_get_user_data (blob, key); @@ -283,35 +312,35 @@ hb_blob_get_user_data (hb_blob_t *blob, /** * hb_blob_make_immutable: - * @blob: a blob. + * @blob: a blob * - * + * Makes a blob immutable. * * Since: 0.9.2 **/ void hb_blob_make_immutable (hb_blob_t *blob) { - if (hb_object_is_inert (blob)) + if (hb_object_is_immutable (blob)) return; - blob->immutable = true; + hb_object_make_immutable (blob); } /** * hb_blob_is_immutable: * @blob: a blob. * - * + * Tests whether a blob is immutable. * - * Return value: TODO + * Return value: `true` if @blob is immutable, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t hb_blob_is_immutable (hb_blob_t *blob) { - return blob->immutable; + return hb_object_is_immutable (blob); } @@ -319,9 +348,9 @@ hb_blob_is_immutable (hb_blob_t *blob) * hb_blob_get_length: * @blob: a blob. * - * + * Fetches the length of a blob's data. * - * Return value: the length of blob data in bytes. + * Return value: the length of @blob data in bytes. * * Since: 0.9.2 **/ @@ -334,11 +363,11 @@ hb_blob_get_length (hb_blob_t *blob) /** * hb_blob_get_data: * @blob: a blob. - * @length: (out): + * @length: (out): The length in bytes of the data retrieved * - * + * Fetches the data from a blob. * - * Returns: (transfer none) (array length=length): + * Returns: (nullable) (transfer none) (array length=length): the byte data of @blob. * * Since: 0.9.2 **/ @@ -363,29 +392,27 @@ hb_blob_get_data (hb_blob_t *blob, unsigned int *length) * fails. * * Returns: (transfer none) (array length=length): Writable blob data, - * or %NULL if failed. + * or `NULL` if failed. * * Since: 0.9.2 **/ char * hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length) { - if (!_try_writable (blob)) { - if (length) - *length = 0; - + if (hb_object_is_immutable (blob) || + !blob->try_make_writable ()) + { + if (length) *length = 0; return nullptr; } - if (length) - *length = blob->length; - + if (length) *length = blob->length; return const_cast (blob->data); } -static hb_bool_t -_try_make_writable_inplace_unix (hb_blob_t *blob) +bool +hb_blob_t::try_make_writable_inplace_unix () { #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT) uintptr_t pagesize = -1, mask, length; @@ -400,25 +427,25 @@ _try_make_writable_inplace_unix (hb_blob_t *blob) #endif if ((uintptr_t) -1L == pagesize) { - DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno)); + DEBUG_MSG_FUNC (BLOB, this, "failed to get pagesize: %s", strerror (errno)); return false; } - DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize); + DEBUG_MSG_FUNC (BLOB, this, "pagesize is %lu", (unsigned long) pagesize); mask = ~(pagesize-1); - addr = (const char *) (((uintptr_t) blob->data) & mask); - length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask) - addr; - DEBUG_MSG_FUNC (BLOB, blob, + addr = (const char *) (((uintptr_t) this->data) & mask); + length = (const char *) (((uintptr_t) this->data + this->length + pagesize-1) & mask) - addr; + DEBUG_MSG_FUNC (BLOB, this, "calling mprotect on [%p..%p] (%lu bytes)", addr, addr+length, (unsigned long) length); if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) { - DEBUG_MSG_FUNC (BLOB, blob, "mprotect failed: %s", strerror (errno)); + DEBUG_MSG_FUNC (BLOB, this, "mprotect failed: %s", strerror (errno)); return false; } - blob->mode = HB_MEMORY_MODE_WRITABLE; + this->mode = HB_MEMORY_MODE_WRITABLE; - DEBUG_MSG_FUNC (BLOB, blob, + DEBUG_MSG_FUNC (BLOB, this, "successfully made [%p..%p] (%lu bytes) writable\n", addr, addr+length, (unsigned long) length); return true; @@ -427,53 +454,322 @@ _try_make_writable_inplace_unix (hb_blob_t *blob) #endif } -static bool -_try_writable_inplace (hb_blob_t *blob) +bool +hb_blob_t::try_make_writable_inplace () { - DEBUG_MSG_FUNC (BLOB, blob, "making writable inplace\n"); + DEBUG_MSG_FUNC (BLOB, this, "making writable inplace\n"); - if (_try_make_writable_inplace_unix (blob)) + if (this->try_make_writable_inplace_unix ()) return true; - DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n"); + DEBUG_MSG_FUNC (BLOB, this, "making writable -> FAILED\n"); /* Failed to make writable inplace, mark that */ - blob->mode = HB_MEMORY_MODE_READONLY; + this->mode = HB_MEMORY_MODE_READONLY; return false; } -static bool -_try_writable (hb_blob_t *blob) +bool +hb_blob_t::try_make_writable () { - if (blob->immutable) - return false; + if (unlikely (!length)) + mode = HB_MEMORY_MODE_WRITABLE; - if (blob->mode == HB_MEMORY_MODE_WRITABLE) + if (this->mode == HB_MEMORY_MODE_WRITABLE) return true; - if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob)) + if (this->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && this->try_make_writable_inplace ()) return true; - if (blob->mode == HB_MEMORY_MODE_WRITABLE) + if (this->mode == HB_MEMORY_MODE_WRITABLE) return true; - DEBUG_MSG_FUNC (BLOB, blob, "current data is -> %p\n", blob->data); + DEBUG_MSG_FUNC (BLOB, this, "current data is -> %p\n", this->data); char *new_data; - new_data = (char *) malloc (blob->length); + new_data = (char *) hb_malloc (this->length); if (unlikely (!new_data)) return false; - DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data); + DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data); - memcpy (new_data, blob->data, blob->length); - _hb_blob_destroy_user_data (blob); - blob->mode = HB_MEMORY_MODE_WRITABLE; - blob->data = new_data; - blob->user_data = new_data; - blob->destroy = free; + hb_memcpy (new_data, this->data, this->length); + this->destroy_user_data (); + this->mode = HB_MEMORY_MODE_WRITABLE; + this->data = new_data; + this->user_data = new_data; + this->destroy = hb_free; return true; } + +/* + * Mmap + */ + +#ifndef HB_NO_OPEN +#ifdef HAVE_MMAP +# if !defined(HB_NO_RESOURCE_FORK) && defined(__APPLE__) +# include +# endif +# include +# include +# include +#endif + +#ifdef _WIN32 +# include +#else +# ifndef O_BINARY +# define O_BINARY 0 +# endif +#endif + +#ifndef MAP_NORESERVE +# define MAP_NORESERVE 0 +#endif + +struct hb_mapped_file_t +{ + char *contents; + unsigned long length; +#ifdef _WIN32 + HANDLE mapping; +#endif +}; + +#if (defined(HAVE_MMAP) || defined(_WIN32)) && !defined(HB_NO_MMAP) +static void +_hb_mapped_file_destroy (void *file_) +{ + hb_mapped_file_t *file = (hb_mapped_file_t *) file_; +#ifdef HAVE_MMAP + munmap (file->contents, file->length); +#elif defined(_WIN32) + UnmapViewOfFile (file->contents); + CloseHandle (file->mapping); +#else + assert (0); // If we don't have mmap we shouldn't reach here +#endif + + hb_free (file); +} +#endif + +#ifdef _PATH_RSRCFORKSPEC +static int +_open_resource_fork (const char *file_name, hb_mapped_file_t *file) +{ + size_t name_len = strlen (file_name); + size_t len = name_len + sizeof (_PATH_RSRCFORKSPEC); + + char *rsrc_name = (char *) hb_malloc (len); + if (unlikely (!rsrc_name)) return -1; + + strncpy (rsrc_name, file_name, name_len); + strncpy (rsrc_name + name_len, _PATH_RSRCFORKSPEC, + sizeof (_PATH_RSRCFORKSPEC)); + + int fd = open (rsrc_name, O_RDONLY | O_BINARY, 0); + hb_free (rsrc_name); + + if (fd != -1) + { + struct stat st; + if (fstat (fd, &st) != -1) + file->length = (unsigned long) st.st_size; + else + { + close (fd); + fd = -1; + } + } + + return fd; +} +#endif + +/** + * hb_blob_create_from_file: + * @file_name: A font filename + * + * Creates a new blob containing the data from the + * specified binary font file. + * + * Returns: An #hb_blob_t pointer with the content of the file, + * or hb_blob_get_empty() if failed. + * + * Since: 1.7.7 + **/ +hb_blob_t * +hb_blob_create_from_file (const char *file_name) +{ + hb_blob_t *blob = hb_blob_create_from_file_or_fail (file_name); + return likely (blob) ? blob : hb_blob_get_empty (); +} + +/** + * hb_blob_create_from_file_or_fail: + * @file_name: A font filename + * + * Creates a new blob containing the data from the + * specified binary font file. + * + * Returns: An #hb_blob_t pointer with the content of the file, + * or `NULL` if failed. + * + * Since: 2.8.2 + **/ +hb_blob_t * +hb_blob_create_from_file_or_fail (const char *file_name) +{ + /* Adopted from glib's gmappedfile.c with Matthias Clasen and + Allison Lortie permission but changed a lot to suit our need. */ +#if defined(HAVE_MMAP) && !defined(HB_NO_MMAP) + hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t)); + if (unlikely (!file)) return nullptr; + + int fd = open (file_name, O_RDONLY | O_BINARY, 0); + if (unlikely (fd == -1)) goto fail_without_close; + + struct stat st; + if (unlikely (fstat (fd, &st) == -1)) goto fail; + + file->length = (unsigned long) st.st_size; + +#ifdef _PATH_RSRCFORKSPEC + if (unlikely (file->length == 0)) + { + int rfd = _open_resource_fork (file_name, file); + if (rfd != -1) + { + close (fd); + fd = rfd; + } + } +#endif + + file->contents = (char *) mmap (nullptr, file->length, PROT_READ, + MAP_PRIVATE | MAP_NORESERVE, fd, 0); + + if (unlikely (file->contents == MAP_FAILED)) goto fail; + + close (fd); + + return hb_blob_create_or_fail (file->contents, file->length, + HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file, + (hb_destroy_func_t) _hb_mapped_file_destroy); + +fail: + close (fd); +fail_without_close: + hb_free (file); + +#elif defined(_WIN32) && !defined(HB_NO_MMAP) + hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t)); + if (unlikely (!file)) return nullptr; + + HANDLE fd; + unsigned int size = strlen (file_name) + 1; + wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size); + if (unlikely (!wchar_file_name)) goto fail_without_close; + mbstowcs (wchar_file_name, file_name, size); +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + { + CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 }; + ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); + ceparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFFF; + ceparams.dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFF00000; + ceparams.dwSecurityQosFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0x000F0000; + ceparams.lpSecurityAttributes = nullptr; + ceparams.hTemplateFile = nullptr; + fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, + OPEN_EXISTING, &ceparams); + } +#else + fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, + nullptr); +#endif + hb_free (wchar_file_name); + + if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close; + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + { + LARGE_INTEGER length; + GetFileSizeEx (fd, &length); + file->length = length.LowPart; + file->mapping = CreateFileMappingFromApp (fd, nullptr, PAGE_READONLY, length.QuadPart, nullptr); + } +#else + file->length = (unsigned long) GetFileSize (fd, nullptr); + file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr); +#endif + if (unlikely (!file->mapping)) goto fail; + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0); +#else + file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0); +#endif + if (unlikely (!file->contents)) goto fail; + + CloseHandle (fd); + return hb_blob_create_or_fail (file->contents, file->length, + HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file, + (hb_destroy_func_t) _hb_mapped_file_destroy); + +fail: + CloseHandle (fd); +fail_without_close: + hb_free (file); + +#endif + + /* The following tries to read a file without knowing its size beforehand + It's used as a fallback for systems without mmap or to read from pipes */ + unsigned long len = 0, allocated = BUFSIZ * 16; + char *data = (char *) hb_malloc (allocated); + if (unlikely (!data)) return nullptr; + + FILE *fp = fopen (file_name, "rb"); + if (unlikely (!fp)) goto fread_fail_without_close; + + while (!feof (fp)) + { + if (allocated - len < BUFSIZ) + { + allocated *= 2; + /* Don't allocate and go more than ~536MB, our mmap reader still + can cover files like that but lets limit our fallback reader */ + if (unlikely (allocated > (2 << 28))) goto fread_fail; + char *new_data = (char *) hb_realloc (data, allocated); + if (unlikely (!new_data)) goto fread_fail; + data = new_data; + } + + unsigned long addition = fread (data + len, 1, allocated - len, fp); + + int err = ferror (fp); +#ifdef EINTR // armcc doesn't have it + if (unlikely (err == EINTR)) continue; +#endif + if (unlikely (err)) goto fread_fail; + + len += addition; + } + fclose (fp); + + return hb_blob_create_or_fail (data, len, HB_MEMORY_MODE_WRITABLE, data, + (hb_destroy_func_t) hb_free); + +fread_fail: + fclose (fp); +fread_fail_without_close: + hb_free (data); + return nullptr; +} +#endif /* !HB_NO_OPEN */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-blob.h b/src/3rdparty/harfbuzz-ng/src/hb-blob.h index ef3fc98c05d..db50067e164 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-blob.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-blob.h @@ -24,7 +24,7 @@ * Red Hat Author(s): Behdad Esfahbod */ -#ifndef HB_H_IN +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif @@ -36,25 +36,36 @@ HB_BEGIN_DECLS -/* - * Note re various memory-modes: +/** + * hb_memory_mode_t: + * @HB_MEMORY_MODE_DUPLICATE: HarfBuzz immediately makes a copy of the data. + * @HB_MEMORY_MODE_READONLY: HarfBuzz client will never modify the data, + * and HarfBuzz will never modify the data. + * @HB_MEMORY_MODE_WRITABLE: HarfBuzz client made a copy of the data solely + * for HarfBuzz, so HarfBuzz may modify the data. + * @HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE: See above + * + * Data type holding the memory modes available to + * client programs. + * + * Regarding these various memory-modes: * * - In no case shall the HarfBuzz client modify memory * that is passed to HarfBuzz in a blob. If there is - * any such possibility, MODE_DUPLICATE should be used + * any such possibility, @HB_MEMORY_MODE_DUPLICATE should be used * such that HarfBuzz makes a copy immediately, * - * - Use MODE_READONLY otherse, unless you really really + * - Use @HB_MEMORY_MODE_READONLY otherwise, unless you really really * really know what you are doing, * - * - MODE_WRITABLE is appropriate if you really made a + * - @HB_MEMORY_MODE_WRITABLE is appropriate if you really made a * copy of data solely for the purpose of passing to * HarfBuzz and doing that just once (no reuse!), * - * - If the font is mmap()ed, it's ok to use - * READONLY_MAY_MAKE_WRITABLE, however, using that mode - * correctly is very tricky. Use MODE_READONLY instead. - */ + * - If the font is mmap()ed, it's okay to use + * @HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, however, using that mode + * correctly is very tricky. Use @HB_MEMORY_MODE_READONLY instead. + **/ typedef enum { HB_MEMORY_MODE_DUPLICATE, HB_MEMORY_MODE_READONLY, @@ -62,6 +73,14 @@ typedef enum { HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE } hb_memory_mode_t; +/** + * hb_blob_t: + * + * Data type for blobs. A blob wraps a chunk of binary + * data and facilitates its lifecycle management between + * a client program and HarfBuzz. + * + **/ typedef struct hb_blob_t hb_blob_t; HB_EXTERN hb_blob_t * @@ -71,6 +90,19 @@ hb_blob_create (const char *data, void *user_data, hb_destroy_func_t destroy); +HB_EXTERN hb_blob_t * +hb_blob_create_or_fail (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy); + +HB_EXTERN hb_blob_t * +hb_blob_create_from_file (const char *file_name); + +HB_EXTERN hb_blob_t * +hb_blob_create_from_file_or_fail (const char *file_name); + /* Always creates with MEMORY_MODE_READONLY. * Even if the parent blob is writable, we don't * want the user of the sub-blob to be able to @@ -82,6 +114,9 @@ hb_blob_create_sub_blob (hb_blob_t *parent, unsigned int offset, unsigned int length); +HB_EXTERN hb_blob_t * +hb_blob_copy_writable_or_fail (hb_blob_t *blob); + HB_EXTERN hb_blob_t * hb_blob_get_empty (void); @@ -100,7 +135,7 @@ hb_blob_set_user_data (hb_blob_t *blob, HB_EXTERN void * -hb_blob_get_user_data (hb_blob_t *blob, +hb_blob_get_user_data (const hb_blob_t *blob, hb_user_data_key_t *key); @@ -120,7 +155,6 @@ hb_blob_get_data (hb_blob_t *blob, unsigned int *length); HB_EXTERN char * hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length); - HB_END_DECLS #endif /* HB_BLOB_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-blob.hh b/src/3rdparty/harfbuzz-ng/src/hb-blob.hh new file mode 100644 index 00000000000..b1b3b94d3dd --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-blob.hh @@ -0,0 +1,98 @@ +/* + * Copyright ยฉ 2009 Red Hat, Inc. + * Copyright ยฉ 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BLOB_HH +#define HB_BLOB_HH + +#include "hb.hh" + + +/* + * hb_blob_t + */ + +struct hb_blob_t +{ + ~hb_blob_t () { destroy_user_data (); } + + void destroy_user_data () + { + if (destroy) + { + destroy (user_data); + user_data = nullptr; + destroy = nullptr; + } + } + + HB_INTERNAL bool try_make_writable (); + HB_INTERNAL bool try_make_writable_inplace (); + HB_INTERNAL bool try_make_writable_inplace_unix (); + + hb_bytes_t as_bytes () const { return hb_bytes_t (data, length); } + template + const Type* as () const { return as_bytes ().as (); } + + public: + hb_object_header_t header; + + const char *data = nullptr; + unsigned int length = 0; + hb_memory_mode_t mode = (hb_memory_mode_t) 0; + + void *user_data = nullptr; + hb_destroy_func_t destroy = nullptr; +}; + + +/* + * hb_blob_ptr_t + */ + +template +struct hb_blob_ptr_t +{ + typedef hb_remove_pointer

T; + + hb_blob_ptr_t (hb_blob_t *b_ = nullptr) : b (b_) {} + hb_blob_t * operator = (hb_blob_t *b_) { return b = b_; } + const T * operator -> () const { return get (); } + const T & operator * () const { return *get (); } + template operator const C * () const { return get (); } + operator const char * () const { return (const char *) get (); } + const T * get () const { return b->as (); } + hb_blob_t * get_blob () const { return b.get_raw (); } + unsigned int get_length () const { return b.get ()->length; } + void destroy () { hb_blob_destroy (b.get_raw ()); b = nullptr; } + + private: + hb_nonnull_ptr_t b; +}; + + +#endif /* HB_BLOB_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-json.hh b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-json.hh index 3f626bda40d..004a9fb8b70 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-json.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-json.hh @@ -29,35 +29,41 @@ #ifndef HB_BUFFER_DESERIALIZE_JSON_HH #define HB_BUFFER_DESERIALIZE_JSON_HH -#include "hb-private.hh" +#include "hb.hh" -#line 36 "hb-buffer-deserialize-json.hh" +#line 33 "hb-buffer-deserialize-json.hh" static const unsigned char _deserialize_json_trans_keys[] = { - 0u, 0u, 9u, 123u, 9u, 34u, 97u, 103u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, - 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, - 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, - 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, - 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, - 65u, 122u, 34u, 122u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 123u, 0u, 0u, 0 + 0u, 0u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, + 48u, 57u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, + 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, + 9u, 125u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, + 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, + 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, + 9u, 125u, 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, + 9u, 123u, 0u, 0u, 0 }; static const char _deserialize_json_key_spans[] = { - 0, 115, 26, 7, 2, 1, 50, 49, - 10, 117, 117, 117, 1, 50, 49, 10, - 117, 117, 1, 1, 50, 49, 117, 117, - 2, 1, 50, 49, 10, 117, 117, 1, - 50, 49, 10, 117, 117, 1, 50, 49, - 58, 89, 117, 117, 85, 115, 0 + 0, 115, 26, 21, 2, 1, 50, 49, + 10, 117, 117, 85, 117, 1, 50, 49, + 10, 117, 117, 1, 1, 50, 49, 117, + 117, 2, 1, 50, 49, 10, 117, 117, + 1, 50, 49, 10, 117, 117, 1, 1, + 50, 49, 117, 117, 1, 50, 49, 59, + 117, 59, 117, 117, 1, 50, 49, 117, + 115, 0 }; static const short _deserialize_json_index_offsets[] = { - 0, 0, 116, 143, 151, 154, 156, 207, - 257, 268, 386, 504, 622, 624, 675, 725, - 736, 854, 972, 974, 976, 1027, 1077, 1195, - 1313, 1316, 1318, 1369, 1419, 1430, 1548, 1666, - 1668, 1719, 1769, 1780, 1898, 2016, 2018, 2069, - 2119, 2178, 2268, 2386, 2504, 2590, 2706 + 0, 0, 116, 143, 165, 168, 170, 221, + 271, 282, 400, 518, 604, 722, 724, 775, + 825, 836, 954, 1072, 1074, 1076, 1127, 1177, + 1295, 1413, 1416, 1418, 1469, 1519, 1530, 1648, + 1766, 1768, 1819, 1869, 1880, 1998, 2116, 2118, + 2120, 2171, 2221, 2339, 2457, 2459, 2510, 2560, + 2620, 2738, 2798, 2916, 3034, 3036, 3087, 3137, + 3255, 3371 }; static const char _deserialize_json_indicies[] = { @@ -79,41 +85,28 @@ static const char _deserialize_json_indicies[] = { 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 4, 1, - 5, 1, 6, 7, 1, 1, 8, 1, - 9, 10, 1, 11, 1, 11, 11, 11, - 11, 11, 1, 1, 1, 1, 1, 1, + 5, 1, 6, 7, 1, 8, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 11, 1, 1, 1, + 1, 1, 1, 1, 10, 1, 11, 12, + 1, 13, 1, 13, 13, 13, 13, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 12, 1, - 12, 12, 12, 12, 12, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 12, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 13, 1, 1, 14, - 15, 15, 15, 15, 15, 15, 15, 15, - 15, 1, 16, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 1, 18, 18, 18, - 18, 18, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 18, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 19, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 14, 1, 14, 14, + 14, 14, 14, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 14, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 15, 1, 1, 16, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 1, + 18, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 1, 20, 20, 20, 20, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 20, 1, 21, 21, 21, 21, 21, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 21, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 3, 1, + 1, 1, 20, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 21, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -124,57 +117,38 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 22, - 1, 18, 18, 18, 18, 18, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 18, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 19, 1, 1, 1, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 20, 1, 23, 1, 23, 23, 23, 23, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 24, 1, 24, 24, 24, 24, - 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 25, 1, 1, 26, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 1, 28, 29, - 29, 29, 29, 29, 29, 29, 29, 29, - 1, 30, 30, 30, 30, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 30, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 24, 1, 25, + 25, 25, 25, 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 26, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 32, 1, 30, - 30, 30, 30, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 30, 1, + 1, 1, 1, 27, 1, 20, 20, 20, + 20, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 31, 1, 1, 1, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, + 1, 1, 1, 1, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 21, 1, 1, 1, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -182,25 +156,27 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 32, 1, 33, 1, 34, - 1, 34, 34, 34, 34, 34, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 22, 1, 28, 1, 28, 28, 28, + 28, 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 34, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 35, 1, 35, 35, 35, 35, - 35, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 29, 1, + 29, 29, 29, 29, 29, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 35, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 29, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 36, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 1, 38, 38, - 38, 38, 38, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 30, 1, 1, 31, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 1, 33, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 1, 35, 35, 35, + 35, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 38, 1, 1, + 1, 1, 1, 1, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 39, 1, 1, 1, 1, 1, 1, + 36, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -210,14 +186,13 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 40, 1, 38, 38, 38, 38, - 38, 1, 1, 1, 1, 1, 1, 1, + 1, 37, 1, 35, 35, 35, 35, 35, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 38, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 39, - 1, 1, 1, 41, 41, 41, 41, 41, - 41, 41, 41, 41, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 35, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 36, 1, + 1, 1, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -225,26 +200,26 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 40, 1, 42, 43, 1, 44, 1, 44, - 44, 44, 44, 44, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 37, + 1, 38, 1, 39, 1, 39, 39, 39, + 39, 39, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 44, 1, + 1, 1, 1, 1, 39, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 40, 1, + 40, 40, 40, 40, 40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 45, 1, 45, 45, 45, 45, 45, 1, + 1, 1, 1, 1, 1, 1, 1, 40, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 41, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 1, 43, 43, 43, 43, 43, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 45, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 46, 1, - 1, 47, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 1, 49, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 1, 51, - 51, 51, 51, 51, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 51, 1, + 1, 43, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 44, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 52, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -253,42 +228,42 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 45, 1, + 43, 43, 43, 43, 43, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 53, 1, 51, 51, 51, - 51, 51, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 43, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 51, 1, 1, 1, + 1, 1, 1, 44, 1, 1, 1, 46, + 46, 46, 46, 46, 46, 46, 46, 46, + 46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 52, 1, 1, 1, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 45, 1, 47, 48, + 1, 49, 1, 49, 49, 49, 49, 49, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 53, 1, 54, 1, 54, 54, 54, - 54, 54, 1, 1, 1, 1, 1, 1, + 1, 1, 49, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 54, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 50, 1, 50, 50, + 50, 50, 50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 55, 1, - 55, 55, 55, 55, 55, 1, 1, 1, + 1, 1, 1, 1, 1, 50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 55, + 1, 1, 51, 1, 1, 52, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 1, + 54, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 1, 56, 56, 56, 56, 56, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 56, 1, 1, 57, - 58, 58, 58, 58, 58, 58, 58, 58, - 58, 1, 59, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 1, 61, 61, 61, - 61, 61, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 61, 1, 1, 1, + 1, 1, 56, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 62, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -297,59 +272,42 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 58, + 1, 56, 56, 56, 56, 56, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 63, 1, 61, 61, 61, 61, 61, 1, 1, 1, 1, 1, 1, 1, 1, + 56, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 57, 1, 1, 1, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 61, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 62, 1, - 1, 1, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 58, 1, 59, + 1, 59, 59, 59, 59, 59, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 63, - 1, 64, 1, 64, 64, 64, 64, 64, 1, 1, 1, 1, 1, 1, 1, 1, + 59, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 64, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 60, 1, 60, 60, 60, 60, + 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 65, 1, 65, 65, - 65, 65, 65, 1, 1, 1, 1, 1, + 1, 1, 1, 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 65, 1, 66, + 61, 1, 1, 62, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 1, 64, 65, + 65, 65, 65, 65, 65, 65, 65, 65, + 1, 66, 66, 66, 66, 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 67, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 1, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 1, 1, 1, 1, 1, 1, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 1, 70, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 71, 71, - 1, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 1, 1, 1, 1, 1, - 1, 1, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 1, 1, 1, 1, - 71, 1, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 1, 72, 72, 72, - 72, 72, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 72, 1, 1, 1, + 66, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 67, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 73, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -358,14 +316,14 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 68, 1, 66, + 66, 66, 66, 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 74, 1, 72, 72, 72, 72, 72, + 1, 1, 1, 1, 1, 1, 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 67, 1, 1, 1, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 72, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 73, 1, - 1, 1, 75, 75, 75, 75, 75, 75, - 75, 75, 75, 75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -373,21 +331,25 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 74, - 1, 76, 76, 76, 76, 76, 1, 1, + 1, 1, 1, 68, 1, 69, 1, 70, + 1, 70, 70, 70, 70, 70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 76, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 77, 1, 1, 1, + 70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 71, 1, 71, 71, 71, 71, + 71, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 71, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 72, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 1, 74, 74, + 74, 74, 74, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 78, 1, 0, - 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 74, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 0, 1, + 1, 75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -397,49 +359,182 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 76, 1, 74, 74, 74, 74, + 74, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 74, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 75, + 1, 1, 1, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 2, 1, 1, 0 + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 76, 1, 78, 1, 78, 78, 78, 78, + 78, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 78, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 79, 1, 79, + 79, 79, 79, 79, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 79, 1, + 80, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 81, 82, + 82, 82, 82, 82, 82, 82, 82, 82, + 1, 84, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 85, 83, 86, 86, 86, + 86, 86, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 86, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 87, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 88, 1, 83, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 83, 1, 89, + 89, 89, 89, 89, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 89, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 90, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 91, 1, 89, 89, 89, + 89, 89, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 89, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 90, 1, 1, 1, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 91, 1, 93, 1, 93, 93, 93, + 93, 93, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 93, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 94, 1, + 94, 94, 94, 94, 94, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 94, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 95, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 1, 89, 89, 89, 89, 89, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 89, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 90, 1, 1, + 1, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 91, 1, + 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 1, 0 }; static const char _deserialize_json_trans_targs[] = { - 1, 0, 2, 2, 3, 4, 18, 24, - 37, 5, 12, 6, 7, 8, 9, 11, - 9, 11, 10, 2, 44, 10, 44, 13, - 14, 15, 16, 17, 16, 17, 10, 2, - 44, 19, 20, 21, 22, 23, 10, 2, - 44, 23, 25, 31, 26, 27, 28, 29, - 30, 29, 30, 10, 2, 44, 32, 33, - 34, 35, 36, 35, 36, 10, 2, 44, - 38, 39, 40, 42, 43, 41, 10, 41, - 10, 2, 44, 43, 44, 45, 46 + 1, 0, 2, 2, 3, 4, 19, 25, + 38, 44, 52, 5, 13, 6, 7, 8, + 9, 12, 9, 12, 10, 2, 11, 10, + 11, 11, 56, 57, 14, 15, 16, 17, + 18, 17, 18, 10, 2, 11, 20, 21, + 22, 23, 24, 10, 2, 11, 24, 26, + 32, 27, 28, 29, 30, 31, 30, 31, + 10, 2, 11, 33, 34, 35, 36, 37, + 36, 37, 10, 2, 11, 39, 40, 41, + 42, 43, 10, 2, 11, 43, 45, 46, + 47, 50, 51, 47, 48, 49, 10, 2, + 11, 10, 2, 11, 51, 53, 54, 50, + 55, 55 }; static const char _deserialize_json_trans_actions[] = { 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2, 2, 2, - 0, 0, 3, 3, 4, 0, 5, 0, - 0, 2, 2, 2, 0, 0, 6, 6, - 7, 0, 0, 0, 2, 2, 8, 8, - 9, 0, 0, 0, 0, 0, 2, 2, - 2, 0, 0, 10, 10, 11, 0, 0, - 2, 2, 2, 0, 0, 12, 12, 13, - 0, 0, 0, 2, 2, 2, 14, 0, - 15, 15, 16, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 2, + 2, 2, 0, 0, 3, 3, 4, 0, + 5, 0, 0, 0, 0, 0, 2, 2, + 2, 0, 0, 6, 6, 7, 0, 0, + 0, 2, 2, 8, 8, 9, 0, 0, + 0, 0, 0, 2, 2, 2, 0, 0, + 10, 10, 11, 0, 0, 2, 2, 2, + 0, 0, 12, 12, 13, 0, 0, 0, + 2, 2, 14, 14, 15, 0, 0, 0, + 2, 16, 16, 0, 17, 0, 18, 18, + 19, 20, 20, 21, 17, 0, 0, 22, + 22, 23 }; static const int deserialize_json_start = 1; -static const int deserialize_json_first_final = 44; +static const int deserialize_json_first_final = 56; static const int deserialize_json_error = 0; static const int deserialize_json_en_main = 1; -#line 97 "hb-buffer-deserialize-json.rl" +#line 111 "hb-buffer-deserialize-json.rl" static hb_bool_t -_hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer, +_hb_buffer_deserialize_json (hb_buffer_t *buffer, const char *buf, unsigned int buf_len, const char **end_ptr, @@ -448,26 +543,24 @@ _hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer, const char *p = buf, *pe = buf + buf_len; /* Ensure we have positions. */ - (void) hb_buffer_get_glyph_positions (buffer, NULL); + (void) hb_buffer_get_glyph_positions (buffer, nullptr); while (p < pe && ISSPACE (*p)) p++; if (p < pe && *p == (buffer->len ? ',' : '[')) - { *end_ptr = ++p; - } - const char *tok = NULL; + const char *tok = nullptr; int cs; hb_glyph_info_t info = {0}; hb_glyph_position_t pos = {0}; -#line 466 "hb-buffer-deserialize-json.hh" +#line 552 "hb-buffer-deserialize-json.hh" { cs = deserialize_json_start; } -#line 471 "hb-buffer-deserialize-json.hh" +#line 555 "hb-buffer-deserialize-json.hh" { int _slen; int _trans; @@ -495,15 +588,15 @@ _resume: case 1: #line 38 "hb-buffer-deserialize-json.rl" { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); } break; case 5: #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; @@ -515,112 +608,171 @@ _resume: tok = p; } break; - case 14: + case 17: #line 55 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } + break; + case 23: +#line 56 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_unicode ())) return false; } + break; + case 18: +#line 58 "hb-buffer-deserialize-json.rl" { + /* TODO Unescape \" and \\ if found. */ if (!hb_font_glyph_from_string (font, - tok, p - tok, + tok+1, p - tok - 2, /* Skip "" */ &info.codepoint)) return false; } break; - case 15: -#line 62 "hb-buffer-deserialize-json.rl" + case 20: +#line 66 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.codepoint)) return false; } break; case 8: -#line 63 "hb-buffer-deserialize-json.rl" +#line 67 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.cluster )) return false; } break; case 10: -#line 64 "hb-buffer-deserialize-json.rl" +#line 68 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.x_offset )) return false; } break; case 12: -#line 65 "hb-buffer-deserialize-json.rl" +#line 69 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.y_offset )) return false; } break; case 3: -#line 66 "hb-buffer-deserialize-json.rl" +#line 70 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.x_advance)) return false; } break; case 6: -#line 67 "hb-buffer-deserialize-json.rl" +#line 71 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.y_advance)) return false; } break; + case 14: +#line 72 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } + break; case 16: -#line 62 "hb-buffer-deserialize-json.rl" +#line 51 "hb-buffer-deserialize-json.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } + break; + case 22: +#line 51 "hb-buffer-deserialize-json.rl" + { + tok = p; +} +#line 56 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_unicode ())) return false; } + break; + case 19: +#line 58 "hb-buffer-deserialize-json.rl" + { + /* TODO Unescape \" and \\ if found. */ + if (!hb_font_glyph_from_string (font, + tok+1, p - tok - 2, /* Skip "" */ + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 21: +#line 66 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.codepoint)) return false; } #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; case 9: -#line 63 "hb-buffer-deserialize-json.rl" +#line 67 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.cluster )) return false; } #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; case 11: -#line 64 "hb-buffer-deserialize-json.rl" +#line 68 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.x_offset )) return false; } #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; case 13: -#line 65 "hb-buffer-deserialize-json.rl" +#line 69 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.y_offset )) return false; } #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; case 4: -#line 66 "hb-buffer-deserialize-json.rl" +#line 70 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.x_advance)) return false; } #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; case 7: -#line 67 "hb-buffer-deserialize-json.rl" +#line 71 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.y_advance)) return false; } #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; -#line 624 "hb-buffer-deserialize-json.hh" + case 15: +#line 72 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 733 "hb-buffer-deserialize-json.hh" } _again: @@ -632,7 +784,7 @@ _again: _out: {} } -#line 125 "hb-buffer-deserialize-json.rl" +#line 137 "hb-buffer-deserialize-json.rl" *end_ptr = p; diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-glyphs.hh b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-glyphs.hh new file mode 100644 index 00000000000..5fe75659b49 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-glyphs.hh @@ -0,0 +1,692 @@ + +#line 1 "hb-buffer-deserialize-text-glyphs.rl" +/* + * Copyright ยฉ 2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH +#define HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH + +#include "hb.hh" + + +#line 33 "hb-buffer-deserialize-text-glyphs.hh" +static const unsigned char _deserialize_text_glyphs_trans_keys[] = { + 0u, 0u, 48u, 57u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, + 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 43u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 9u, 124u, 9u, 124u, 0 +}; + +static const char _deserialize_text_glyphs_key_spans[] = { + 0, 10, 13, 10, 13, 10, 10, 13, + 10, 1, 13, 10, 14, 82, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116 +}; + +static const short _deserialize_text_glyphs_index_offsets[] = { + 0, 0, 11, 25, 36, 50, 61, 72, + 86, 97, 99, 113, 124, 139, 222, 339, + 456, 573, 690, 807, 924, 1041, 1158, 1275, + 1392, 1509, 1626 +}; + +static const char _deserialize_text_glyphs_indicies[] = { + 0, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 1, 3, 1, 1, 4, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 1, 6, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 1, 8, 1, 1, + 9, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 1, 11, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 1, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 1, 15, 1, 1, 16, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 1, 18, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 1, 20, 1, 21, 1, 1, 22, + 23, 23, 23, 23, 23, 23, 23, 23, + 23, 1, 24, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 1, 20, 1, 1, + 1, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 1, 26, 26, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 26, 1, + 1, 26, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 26, 26, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 26, 1, 28, + 28, 28, 28, 28, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 28, 27, + 27, 29, 27, 27, 27, 27, 27, 27, + 27, 30, 1, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 31, 27, 27, 32, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 33, 1, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 28, 27, 34, 34, 34, 34, + 34, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 34, 26, 26, 35, 26, + 26, 26, 26, 26, 26, 26, 36, 1, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 37, 26, 26, 38, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 39, + 1, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 40, + 26, 41, 41, 41, 41, 41, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 41, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 42, 1, 43, 43, + 43, 43, 43, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 43, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 44, 1, 41, 41, 41, 41, 41, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 41, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 42, 1, + 46, 46, 46, 46, 46, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 46, + 1, 1, 47, 1, 1, 1, 1, 1, + 1, 1, 1, 48, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 49, 1, 50, 50, 50, + 50, 50, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 50, 1, 1, 51, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 52, 1, 50, 50, 50, 50, 50, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 50, 1, 1, 51, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 52, 1, 46, + 46, 46, 46, 46, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 46, 1, + 1, 47, 1, 1, 1, 1, 1, 1, + 1, 1, 48, 1, 1, 1, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 49, 1, 53, 53, 53, 53, + 53, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 53, 1, 1, 54, 1, + 1, 1, 1, 1, 1, 1, 55, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 56, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 57, + 1, 58, 58, 58, 58, 58, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 58, 1, 1, 59, 1, 1, 1, 1, + 1, 1, 1, 60, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 61, 1, 58, 58, + 58, 58, 58, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 58, 1, 1, + 59, 1, 1, 1, 1, 1, 1, 1, + 60, 1, 1, 1, 1, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 61, 1, 53, 53, 53, 53, 53, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 53, 1, 1, 54, 1, 1, + 1, 1, 1, 1, 1, 55, 1, 1, + 1, 1, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 1, 1, 1, 1, + 1, 1, 56, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 57, 1, + 0 +}; + +static const char _deserialize_text_glyphs_trans_targs[] = { + 16, 0, 18, 3, 19, 22, 19, 22, + 5, 20, 21, 20, 21, 23, 26, 8, + 9, 12, 9, 12, 10, 11, 24, 25, + 24, 25, 15, 15, 14, 1, 2, 6, + 7, 13, 15, 1, 2, 6, 7, 13, + 14, 17, 14, 17, 14, 18, 17, 1, + 4, 14, 17, 1, 14, 17, 1, 2, + 7, 14, 17, 1, 2, 14, 26 +}; + +static const char _deserialize_text_glyphs_trans_actions[] = { + 1, 0, 1, 1, 1, 1, 0, 0, + 1, 1, 1, 0, 0, 1, 1, 1, + 1, 1, 0, 0, 2, 1, 1, 1, + 0, 0, 0, 4, 3, 5, 5, 5, + 5, 4, 6, 7, 7, 7, 7, 0, + 6, 8, 8, 0, 0, 0, 9, 10, + 10, 9, 11, 12, 11, 13, 14, 14, + 14, 13, 15, 16, 16, 15, 0 +}; + +static const char _deserialize_text_glyphs_eof_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3, 6, + 8, 0, 8, 9, 11, 11, 9, 13, + 15, 15, 13 +}; + +static const int deserialize_text_glyphs_start = 14; +static const int deserialize_text_glyphs_first_final = 14; +static const int deserialize_text_glyphs_error = 0; + +static const int deserialize_text_glyphs_en_main = 14; + + +#line 98 "hb-buffer-deserialize-text-glyphs.rl" + + +static hb_bool_t +_hb_buffer_deserialize_text_glyphs (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len, *eof = pe, *orig_pe = pe; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, nullptr); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? '|' : '[')) + *end_ptr = ++p; + + const char *end = strchr ((char *) p, ']'); + if (end) + pe = eof = end; + else + { + end = strrchr ((char *) p, '|'); + if (end) + pe = eof = end; + else + pe = eof = p; + } + + const char *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + +#line 346 "hb-buffer-deserialize-text-glyphs.hh" + { + cs = deserialize_text_glyphs_start; + } + +#line 349 "hb-buffer-deserialize-text-glyphs.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_text_glyphs_trans_keys + (cs<<1); + _inds = _deserialize_text_glyphs_indicies + _deserialize_text_glyphs_index_offsets[cs]; + + _slen = _deserialize_text_glyphs_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_text_glyphs_trans_targs[_trans]; + + if ( _deserialize_text_glyphs_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_text_glyphs_trans_actions[_trans] ) { + case 1: +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} + break; + case 7: +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 14: +#line 63 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 2: +#line 64 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 16: +#line 65 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 10: +#line 66 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 12: +#line 67 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } + break; + case 4: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} + break; + case 6: +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 13: +#line 63 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 15: +#line 65 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 66 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 67 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 68 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 5: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 3: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 516 "hb-buffer-deserialize-text-glyphs.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + switch ( _deserialize_text_glyphs_eof_actions[cs] ) { + case 6: +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 13: +#line 63 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 15: +#line 65 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 66 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 67 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 68 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 3: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 616 "hb-buffer-deserialize-text-glyphs.hh" + } + } + + _out: {} + } + +#line 136 "hb-buffer-deserialize-text-glyphs.rl" + + + if (pe < orig_pe && *pe == ']') + { + pe++; + if (p == pe) + p++; + } + + *end_ptr = p; + + return p == pe; +} + +#endif /* HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-unicode.hh b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-unicode.hh new file mode 100644 index 00000000000..8ca73bf25f4 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-unicode.hh @@ -0,0 +1,332 @@ + +#line 1 "hb-buffer-deserialize-text-unicode.rl" +/* + * Copyright ยฉ 2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH +#define HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH + +#include "hb.hh" + + +#line 33 "hb-buffer-deserialize-text-unicode.hh" +static const unsigned char _deserialize_text_unicode_trans_keys[] = { + 0u, 0u, 9u, 117u, 43u, 102u, 48u, 102u, 48u, 57u, 9u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 0 +}; + +static const char _deserialize_text_unicode_key_spans[] = { + 0, 109, 60, 55, 10, 116, 116, 116, + 116 +}; + +static const short _deserialize_text_unicode_index_offsets[] = { + 0, 0, 110, 171, 227, 238, 355, 472, + 589 +}; + +static const char _deserialize_text_unicode_indicies[] = { + 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 3, + 1, 1, 1, 1, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 4, + 4, 4, 4, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 4, + 4, 4, 4, 1, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 4, + 4, 4, 4, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 4, + 4, 4, 4, 1, 5, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 1, 7, + 7, 7, 7, 7, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 1, 1, 1, 9, 1, 1, 1, 8, + 8, 8, 8, 8, 8, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8, + 8, 8, 8, 8, 8, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 10, 1, 11, 11, 11, 11, + 11, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 11, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, + 1, 12, 12, 12, 12, 12, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 12, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 13, 1, 12, 12, + 12, 12, 12, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 12, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 13, 1, 0 +}; + +static const char _deserialize_text_unicode_trans_targs[] = { + 1, 0, 2, 3, 5, 7, 8, 6, + 5, 4, 1, 6, 6, 1, 8 +}; + +static const char _deserialize_text_unicode_trans_actions[] = { + 0, 0, 1, 0, 2, 2, 2, 3, + 0, 4, 3, 0, 5, 5, 0 +}; + +static const char _deserialize_text_unicode_eof_actions[] = { + 0, 0, 0, 0, 0, 3, 0, 5, + 5 +}; + +static const int deserialize_text_unicode_start = 1; +static const int deserialize_text_unicode_first_final = 5; +static const int deserialize_text_unicode_error = 0; + +static const int deserialize_text_unicode_en_main = 1; + + +#line 79 "hb-buffer-deserialize-text-unicode.rl" + + +static hb_bool_t +_hb_buffer_deserialize_text_unicode (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len, *eof = pe, *orig_pe = pe; + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? '|' : '<')) + *end_ptr = ++p; + + const char *end = strchr ((char *) p, '>'); + if (end) + pe = eof = end; + else + { + end = strrchr ((char *) p, '|'); + if (end) + pe = eof = end; + else + pe = eof = p; + } + + + const char *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + const hb_glyph_position_t pos = {0}; + +#line 194 "hb-buffer-deserialize-text-unicode.hh" + { + cs = deserialize_text_unicode_start; + } + +#line 197 "hb-buffer-deserialize-text-unicode.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_text_unicode_trans_keys + (cs<<1); + _inds = _deserialize_text_unicode_indicies + _deserialize_text_unicode_index_offsets[cs]; + + _slen = _deserialize_text_unicode_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_text_unicode_trans_targs[_trans]; + + if ( _deserialize_text_unicode_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_text_unicode_trans_actions[_trans] ) { + case 1: +#line 38 "hb-buffer-deserialize-text-unicode.rl" + { + hb_memset (&info, 0, sizeof (info)); +} + break; + case 2: +#line 51 "hb-buffer-deserialize-text-unicode.rl" + { + tok = p; +} + break; + case 4: +#line 55 "hb-buffer-deserialize-text-unicode.rl" + {if (!parse_hex (tok, p, &info.codepoint )) return false; } + break; + case 3: +#line 55 "hb-buffer-deserialize-text-unicode.rl" + {if (!parse_hex (tok, p, &info.codepoint )) return false; } +#line 42 "hb-buffer-deserialize-text-unicode.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + if (buffer->have_positions) + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 5: +#line 57 "hb-buffer-deserialize-text-unicode.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 42 "hb-buffer-deserialize-text-unicode.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + if (buffer->have_positions) + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 256 "hb-buffer-deserialize-text-unicode.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + switch ( _deserialize_text_unicode_eof_actions[cs] ) { + case 3: +#line 55 "hb-buffer-deserialize-text-unicode.rl" + {if (!parse_hex (tok, p, &info.codepoint )) return false; } +#line 42 "hb-buffer-deserialize-text-unicode.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + if (buffer->have_positions) + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 5: +#line 57 "hb-buffer-deserialize-text-unicode.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 42 "hb-buffer-deserialize-text-unicode.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + if (buffer->have_positions) + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 289 "hb-buffer-deserialize-text-unicode.hh" + } + } + + _out: {} + } + +#line 115 "hb-buffer-deserialize-text-unicode.rl" + + + if (pe < orig_pe && *pe == '>') + { + pe++; + if (p == pe) + p++; + } + + *end_ptr = p; + + return p == pe; +} + +#endif /* HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text.hh b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text.hh deleted file mode 100644 index d2d8daae7ed..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text.hh +++ /dev/null @@ -1,571 +0,0 @@ - -#line 1 "hb-buffer-deserialize-text.rl" -/* - * Copyright ยฉ 2013 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_BUFFER_DESERIALIZE_TEXT_HH -#define HB_BUFFER_DESERIALIZE_TEXT_HH - -#include "hb-private.hh" - - -#line 36 "hb-buffer-deserialize-text.hh" -static const unsigned char _deserialize_text_trans_keys[] = { - 0u, 0u, 9u, 122u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, - 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, - 9u, 122u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, - 9u, 124u, 9u, 124u, 9u, 124u, 0 -}; - -static const char _deserialize_text_key_spans[] = { - 0, 114, 13, 10, 13, 10, 10, 13, - 10, 1, 13, 10, 14, 116, 116, 0, - 114, 116, 116, 116, 116, 116, 116, 116, - 116, 116, 116 -}; - -static const short _deserialize_text_index_offsets[] = { - 0, 0, 115, 129, 140, 154, 165, 176, - 190, 201, 203, 217, 228, 243, 360, 477, - 478, 593, 710, 827, 944, 1061, 1178, 1295, - 1412, 1529, 1646 -}; - -static const char _deserialize_text_indicies[] = { - 0, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 2, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 1, 1, 1, 1, 1, 1, - 1, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 1, 1, 1, 1, 1, - 1, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 1, 5, 1, 1, 6, - 7, 7, 7, 7, 7, 7, 7, 7, - 7, 1, 8, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 1, 10, 1, 1, - 11, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 1, 13, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 1, 15, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 1, 17, 1, 1, 18, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 1, 20, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 1, 22, 1, 23, 1, 1, 24, - 25, 25, 25, 25, 25, 25, 25, 25, - 25, 1, 26, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 1, 22, 1, 1, - 1, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 1, 28, 28, 28, 28, - 28, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 28, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 29, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 30, 1, 1, 31, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 32, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 33, - 1, 34, 34, 34, 34, 34, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 34, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 35, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 36, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 2, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 1, 1, 1, 1, 1, 1, 1, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 1, 1, 1, 1, 1, 1, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 1, 28, 28, 28, 28, 28, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 28, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 29, 1, 1, 1, - 1, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 1, 1, 1, 30, 1, - 1, 31, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 32, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 33, 1, 38, - 38, 38, 38, 38, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 38, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 39, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 40, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 41, 1, 42, 42, 42, 42, - 42, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 42, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 43, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 44, - 1, 42, 42, 42, 42, 42, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 42, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 43, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 44, 1, 38, 38, - 38, 38, 38, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 38, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 39, 1, 1, 1, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 40, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 41, 1, 45, 45, 45, 45, 45, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 45, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 46, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 47, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 48, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 49, 1, - 50, 50, 50, 50, 50, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 50, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 51, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 52, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 53, 1, 50, 50, 50, - 50, 50, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 50, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 51, - 1, 1, 1, 1, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 52, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 53, 1, 45, 45, 45, 45, 45, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 45, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 46, 1, 1, 1, - 1, 54, 54, 54, 54, 54, 54, 54, - 54, 54, 54, 1, 1, 1, 1, 1, - 1, 47, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 48, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 49, 1, 28, - 28, 28, 28, 28, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 28, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 29, 1, 55, 55, 1, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 1, 1, 1, 30, 1, 1, 31, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 55, 1, 1, 32, 1, 55, 1, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 55, 1, 33, 1, 0 -}; - -static const char _deserialize_text_trans_targs[] = { - 1, 0, 13, 17, 26, 3, 18, 21, - 18, 21, 5, 19, 20, 19, 20, 22, - 25, 8, 9, 12, 9, 12, 10, 11, - 23, 24, 23, 24, 14, 2, 6, 7, - 15, 16, 14, 15, 16, 17, 14, 4, - 15, 16, 14, 15, 16, 14, 2, 7, - 15, 16, 14, 2, 15, 16, 25, 26 -}; - -static const char _deserialize_text_trans_actions[] = { - 0, 0, 1, 1, 1, 2, 2, 2, - 0, 0, 2, 2, 2, 0, 0, 2, - 2, 2, 2, 2, 0, 0, 3, 2, - 2, 2, 0, 0, 4, 5, 5, 5, - 4, 4, 0, 0, 0, 0, 6, 7, - 6, 6, 8, 8, 8, 9, 10, 10, - 9, 9, 11, 12, 11, 11, 0, 0 -}; - -static const char _deserialize_text_eof_actions[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 4, 0, 0, - 0, 4, 6, 8, 8, 6, 9, 11, - 11, 9, 4 -}; - -static const int deserialize_text_start = 1; -static const int deserialize_text_first_final = 13; -static const int deserialize_text_error = 0; - -static const int deserialize_text_en_main = 1; - - -#line 91 "hb-buffer-deserialize-text.rl" - - -static hb_bool_t -_hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer, - const char *buf, - unsigned int buf_len, - const char **end_ptr, - hb_font_t *font) -{ - const char *p = buf, *pe = buf + buf_len; - - /* Ensure we have positions. */ - (void) hb_buffer_get_glyph_positions (buffer, NULL); - - while (p < pe && ISSPACE (*p)) - p++; - if (p < pe && *p == (buffer->len ? '|' : '[')) - { - *end_ptr = ++p; - } - - const char *eof = pe, *tok = NULL; - int cs; - hb_glyph_info_t info = {0}; - hb_glyph_position_t pos = {0}; - -#line 343 "hb-buffer-deserialize-text.hh" - { - cs = deserialize_text_start; - } - -#line 348 "hb-buffer-deserialize-text.hh" - { - int _slen; - int _trans; - const unsigned char *_keys; - const char *_inds; - if ( p == pe ) - goto _test_eof; - if ( cs == 0 ) - goto _out; -_resume: - _keys = _deserialize_text_trans_keys + (cs<<1); - _inds = _deserialize_text_indicies + _deserialize_text_index_offsets[cs]; - - _slen = _deserialize_text_key_spans[cs]; - _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && - (*p) <= _keys[1] ? - (*p) - _keys[0] : _slen ]; - - cs = _deserialize_text_trans_targs[_trans]; - - if ( _deserialize_text_trans_actions[_trans] == 0 ) - goto _again; - - switch ( _deserialize_text_trans_actions[_trans] ) { - case 2: -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} - break; - case 5: -#line 55 "hb-buffer-deserialize-text.rl" - { - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} - break; - case 10: -#line 62 "hb-buffer-deserialize-text.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } - break; - case 3: -#line 63 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.x_offset )) return false; } - break; - case 12: -#line 64 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } - break; - case 7: -#line 65 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } - break; - case 1: -#line 38 "hb-buffer-deserialize-text.rl" - { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); -} -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} - break; - case 4: -#line 55 "hb-buffer-deserialize-text.rl" - { - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 9: -#line 62 "hb-buffer-deserialize-text.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 11: -#line 64 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 6: -#line 65 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 8: -#line 66 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_advance)) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; -#line 480 "hb-buffer-deserialize-text.hh" - } - -_again: - if ( cs == 0 ) - goto _out; - if ( ++p != pe ) - goto _resume; - _test_eof: {} - if ( p == eof ) - { - switch ( _deserialize_text_eof_actions[cs] ) { - case 4: -#line 55 "hb-buffer-deserialize-text.rl" - { - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 9: -#line 62 "hb-buffer-deserialize-text.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 11: -#line 64 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 6: -#line 65 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 8: -#line 66 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_advance)) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; -#line 557 "hb-buffer-deserialize-text.hh" - } - } - - _out: {} - } - -#line 119 "hb-buffer-deserialize-text.rl" - - - *end_ptr = p; - - return p == pe && *(p-1) != ']'; -} - -#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-buffer-private.hh deleted file mode 100644 index 97bdc1be305..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-buffer-private.hh +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright ยฉ 1998-2004 David Turner and Werner Lemberg - * Copyright ยฉ 2004,2007,2009,2010 Red Hat, Inc. - * Copyright ยฉ 2011,2012 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Red Hat Author(s): Owen Taylor, Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_BUFFER_PRIVATE_HH -#define HB_BUFFER_PRIVATE_HH - -#include "hb-private.hh" -#include "hb-object-private.hh" -#include "hb-unicode-private.hh" - - -#ifndef HB_BUFFER_MAX_LEN_FACTOR -#define HB_BUFFER_MAX_LEN_FACTOR 32 -#endif -#ifndef HB_BUFFER_MAX_LEN_MIN -#define HB_BUFFER_MAX_LEN_MIN 8192 -#endif -#ifndef HB_BUFFER_MAX_LEN_DEFAULT -#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */ -#endif - -#ifndef HB_BUFFER_MAX_OPS_FACTOR -#define HB_BUFFER_MAX_OPS_FACTOR 64 -#endif -#ifndef HB_BUFFER_MAX_OPS_MIN -#define HB_BUFFER_MAX_OPS_MIN 1024 -#endif -#ifndef HB_BUFFER_MAX_OPS_DEFAULT -#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */ -#endif - -static_assert ((sizeof (hb_glyph_info_t) == 20), ""); -static_assert ((sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)), ""); - -HB_MARK_AS_FLAG_T (hb_buffer_flags_t); -HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t); -HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t); - -enum hb_buffer_scratch_flags_t { - HB_BUFFER_SCRATCH_FLAG_DEFAULT = 0x00000000u, - HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII = 0x00000001u, - HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES = 0x00000002u, - HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK = 0x00000004u, - HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT = 0x00000008u, - HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK = 0x00000010u, - - /* Reserved for complex shapers' internal use. */ - HB_BUFFER_SCRATCH_FLAG_COMPLEX0 = 0x01000000u, - HB_BUFFER_SCRATCH_FLAG_COMPLEX1 = 0x02000000u, - HB_BUFFER_SCRATCH_FLAG_COMPLEX2 = 0x04000000u, - HB_BUFFER_SCRATCH_FLAG_COMPLEX3 = 0x08000000u, -}; -HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t); - - -/* - * hb_buffer_t - */ - -struct hb_buffer_t { - hb_object_header_t header; - ASSERT_POD (); - - /* Information about how the text in the buffer should be treated */ - hb_unicode_funcs_t *unicode; /* Unicode functions */ - hb_buffer_flags_t flags; /* BOT / EOT / etc. */ - hb_buffer_cluster_level_t cluster_level; - hb_codepoint_t replacement; /* U+FFFD or something else. */ - hb_buffer_scratch_flags_t scratch_flags; /* Have space-flallback, etc. */ - unsigned int max_len; /* Maximum allowed len. */ - int max_ops; /* Maximum allowed operations. */ - - /* Buffer contents */ - hb_buffer_content_type_t content_type; - hb_segment_properties_t props; /* Script, language, direction */ - - bool in_error; /* Allocation failed */ - bool have_output; /* Whether we have an output buffer going on */ - bool have_positions; /* Whether we have positions */ - - unsigned int idx; /* Cursor into ->info and ->pos arrays */ - unsigned int len; /* Length of ->info and ->pos arrays */ - unsigned int out_len; /* Length of ->out array if have_output */ - - unsigned int allocated; /* Length of allocated arrays */ - hb_glyph_info_t *info; - hb_glyph_info_t *out_info; - hb_glyph_position_t *pos; - - unsigned int serial; - - /* Text before / after the main buffer contents. - * Always in Unicode, and ordered outward. - * Index 0 is for "pre-context", 1 for "post-context". */ - static const unsigned int CONTEXT_LENGTH = 5; - hb_codepoint_t context[2][CONTEXT_LENGTH]; - unsigned int context_len[2]; - - /* Debugging API */ - hb_buffer_message_func_t message_func; - void *message_data; - hb_destroy_func_t message_destroy; - - /* Internal debugging. */ - /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */ -#ifndef HB_NDEBUG - uint8_t allocated_var_bits; -#endif - - - /* Methods */ - - inline void allocate_var (unsigned int start, unsigned int count) - { -#ifndef HB_NDEBUG - unsigned int end = start + count; - assert (end <= 8); - unsigned int bits = (1u< (cluster, info[i].cluster); - return cluster; - } - inline void - _unsafe_to_break_set_mask (hb_glyph_info_t *info, - unsigned int start, unsigned int end, - unsigned int cluster) - { - for (unsigned int i = start; i < end; i++) - if (cluster != info[i].cluster) - { - scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK; - info[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK; - } - } - - inline void - unsafe_to_break_all (void) - { - for (unsigned int i = 0; i < len; i++) - info[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK; - } - inline void - safe_to_break_all (void) - { - for (unsigned int i = 0; i < len; i++) - info[i].mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK; - } -}; - - -/* Loop over clusters. Duplicated in foreach_syllable(). */ -#define foreach_cluster(buffer, start, end) \ - for (unsigned int \ - _count = buffer->len, \ - start = 0, end = _count ? _next_cluster (buffer, 0) : 0; \ - start < _count; \ - start = end, end = _next_cluster (buffer, start)) - -static inline unsigned int -_next_cluster (hb_buffer_t *buffer, unsigned int start) -{ - hb_glyph_info_t *info = buffer->info; - unsigned int count = buffer->len; - - unsigned int cluster = info[start].cluster; - while (++start < count && cluster == info[start].cluster) - ; - - return start; -} - - -#define HB_BUFFER_XALLOCATE_VAR(b, func, var) \ - b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \ - sizeof (b->info[0].var)) -#define HB_BUFFER_ALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, allocate_var, var ()) -#define HB_BUFFER_DEALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var ()) -#define HB_BUFFER_ASSERT_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, assert_var, var ()) - - -#endif /* HB_BUFFER_PRIVATE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-serialize.cc b/src/3rdparty/harfbuzz-ng/src/hb-buffer-serialize.cc index ea62e9ffdb2..16f189519b1 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-buffer-serialize.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer-serialize.cc @@ -24,10 +24,14 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-buffer-private.hh" +#include "hb.hh" + +#ifndef HB_NO_BUFFER_SERIALIZE + +#include "hb-buffer.hh" -static const char *serialize_formats[] = { +static const char *_hb_buffer_serialize_formats[] = { "text", "json", nullptr @@ -44,21 +48,21 @@ static const char *serialize_formats[] = { * Since: 0.9.7 **/ const char ** -hb_buffer_serialize_list_formats (void) +hb_buffer_serialize_list_formats () { - return serialize_formats; + return _hb_buffer_serialize_formats; } /** * hb_buffer_serialize_format_from_string: * @str: (array length=len) (element-type uint8_t): a string to parse - * @len: length of @str, or -1 if string is %NULL terminated + * @len: length of @str, or -1 if string is `NULL` terminated * * Parses a string into an #hb_buffer_serialize_format_t. Does not check if * @str is a valid buffer serialization format, use * hb_buffer_serialize_list_formats() to get the list of supported formats. * - * Return value: + * Return value: * The parsed #hb_buffer_serialize_format_t. * * Since: 0.9.7 @@ -74,41 +78,42 @@ hb_buffer_serialize_format_from_string (const char *str, int len) * hb_buffer_serialize_format_to_string: * @format: an #hb_buffer_serialize_format_t to convert. * - * Converts @format to the string corresponding it, or %NULL if it is not a valid + * Converts @format to the string corresponding it, or `NULL` if it is not a valid * #hb_buffer_serialize_format_t. * * Return value: (transfer none): - * A %NULL terminated string corresponding to @format. Should not be freed. + * A `NULL` terminated string corresponding to @format. Should not be freed. * * Since: 0.9.7 **/ const char * hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format) { - switch (format) + switch ((unsigned) format) { - case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return serialize_formats[0]; - case HB_BUFFER_SERIALIZE_FORMAT_JSON: return serialize_formats[1]; + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return _hb_buffer_serialize_formats[0]; + case HB_BUFFER_SERIALIZE_FORMAT_JSON: return _hb_buffer_serialize_formats[1]; default: - case HB_BUFFER_SERIALIZE_FORMAT_INVALID: return nullptr; + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: return nullptr; } } static unsigned int _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, - unsigned int start, - unsigned int end, - char *buf, - unsigned int buf_size, - unsigned int *buf_consumed, - hb_font_t *font, - hb_buffer_serialize_flags_t flags) + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_flags_t flags) { hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? - nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); + nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); *buf_consumed = 0; + hb_position_t x = 0, y = 0; for (unsigned int i = start; i < end; i++) { char b[1024]; @@ -120,6 +125,8 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, if (i) *p++ = ','; + else + *p++ = '['; *p++ = '{'; @@ -129,56 +136,119 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, char g[128]; hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g)); *p++ = '"'; - for (char *q = g; *q; q++) { - if (*q == '"') + for (char *q = g; *q; q++) + { + if (unlikely (*q == '"' || *q == '\\')) *p++ = '\\'; *p++ = *q; } *p++ = '"'; } else - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); } if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) { - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", - pos[i].x_offset, pos[i].y_offset)); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", - pos[i].x_advance, pos[i].y_advance)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", + x+pos[i].x_offset, y+pos[i].y_offset)); + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", + pos[i].x_advance, pos[i].y_advance)); } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) { if (info[i].mask & HB_GLYPH_FLAG_DEFINED) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED)); } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) { hb_glyph_extents_t extents; hb_font_get_glyph_extents(font, info[i].codepoint, &extents); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", - extents.x_bearing, extents.y_bearing)); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", - extents.width, extents.height)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", + extents.x_bearing, extents.y_bearing)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", + extents.width, extents.height)); } *p++ = '}'; + if (i == end-1) + *p++ = ']'; unsigned int l = p - b; if (buf_size > l) { - memcpy (buf, b, l); + hb_memcpy (buf, b, l); buf += l; buf_size -= l; *buf_consumed += l; *buf = '\0'; } else return i - start; + + if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + x += pos[i].x_advance; + y += pos[i].y_advance; + } + } + + return end - start; +} + +static unsigned int +_hb_buffer_serialize_unicode_json (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); + + *buf_consumed = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + if (i) + *p++ = ','; + else + *p++ = '['; + + *p++ = '{'; + + APPEND ("\"u\":"); + + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); + } + + *p++ = '}'; + + if (i == end-1) + *p++ = ']'; + + unsigned int l = p - b; + if (buf_size > l) + { + hb_memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + } return end - start; @@ -186,19 +256,20 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, static unsigned int _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, - unsigned int start, - unsigned int end, - char *buf, - unsigned int buf_size, - unsigned int *buf_consumed, - hb_font_t *font, - hb_buffer_serialize_flags_t flags) + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_flags_t flags) { hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? - nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); + nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); *buf_consumed = 0; + hb_position_t x = 0, y = 0; for (unsigned int i = start; i < end; i++) { char b[1024]; @@ -208,47 +279,109 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, if (i) *p++ = '|'; + else + *p++ = '['; if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES)) { + /* TODO Escape delimiters we use. */ hb_font_glyph_to_string (font, info[i].codepoint, p, 128); p += strlen (p); } else - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); } if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) { - if (pos[i].x_offset || pos[i].y_offset) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset)); + if (x+pos[i].x_offset || y+pos[i].y_offset) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset)); - *p++ = '+'; - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); - if (pos[i].y_advance) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + *p++ = '+'; + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); + if (pos[i].y_advance) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); + } } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) { - if (info[i].mask &HB_GLYPH_FLAG_DEFINED) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED)); + if (info[i].mask & HB_GLYPH_FLAG_DEFINED) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED)); } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) { hb_glyph_extents_t extents; hb_font_get_glyph_extents(font, info[i].codepoint, &extents); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); + } + + if (i == end-1) { + *p++ = ']'; } unsigned int l = p - b; if (buf_size > l) { - memcpy (buf, b, l); + hb_memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + + if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + x += pos[i].x_advance; + y += pos[i].y_advance; + } + } + + return end - start; +} + + +static unsigned int +_hb_buffer_serialize_unicode_text (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); + *buf_consumed = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + if (i) + *p++ = '|'; + else + *p++ = '<'; + + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "U+%04X", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); + } + + if (i == end-1) + *p++ = '>'; + + unsigned int l = p - b; + if (buf_size > l) + { + hb_memcpy (buf, b, l); buf += l; buf_size -= l; *buf_consumed += l; @@ -256,7 +389,6 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, } else return i - start; } - return end - start; } @@ -268,9 +400,9 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to * write serialized buffer into. * @buf_size: the size of @buf. - * @buf_consumed: (out) (allow-none): if not %NULL, will be set to the number of byes written into @buf. - * @font: (allow-none): the #hb_font_t used to shape this buffer, needed to - * read glyph names and extents. If %NULL, and empty font will be used. + * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf. + * @font: (nullable): the #hb_font_t used to shape this buffer, needed to + * read glyph names and extents. If `NULL`, an empty font will be used. * @format: the #hb_buffer_serialize_format_t to use for formatting the output. * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties * to serialize. @@ -286,6 +418,7 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, * ``` * [uni0651=0@518,0+0|uni0628=0+1897] * ``` + * * - The serialized glyphs are delimited with `[` and `]`. * - Glyphs are separated with `|` * - Each glyph starts with glyph name, or glyph index if @@ -294,30 +427,47 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, * - If #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set, the #hb_glyph_position_t in the format: * - If both #hb_glyph_position_t.x_offset and #hb_glyph_position_t.y_offset are not 0, `@x_offset,y_offset`. Then, * - `+x_advance`, then `,y_advance` if #hb_glyph_position_t.y_advance is not 0. Then, - * - If #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set, the - * #hb_glyph_extents_t in the format - * `<x_bearing,y_bearing,width,height>` + * - If #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set, the #hb_glyph_extents_t in the format `` * * ## json - * TODO. + * A machine-readable, structured format. + * The serialized glyphs will look something like: * - * Return value: + * ``` + * [{"g":"uni0651","cl":0,"dx":518,"dy":0,"ax":0,"ay":0}, + * {"g":"uni0628","cl":0,"dx":0,"dy":0,"ax":1897,"ay":0}] + * ``` + * + * Each glyph is a JSON object, with the following properties: + * - `g`: the glyph name or glyph index if + * #HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES flag is set. + * - `cl`: #hb_glyph_info_t.cluster if + * #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set. + * - `dx`,`dy`,`ax`,`ay`: #hb_glyph_position_t.x_offset, #hb_glyph_position_t.y_offset, + * #hb_glyph_position_t.x_advance and #hb_glyph_position_t.y_advance + * respectively, if #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set. + * - `xb`,`yb`,`w`,`h`: #hb_glyph_extents_t.x_bearing, #hb_glyph_extents_t.y_bearing, + * #hb_glyph_extents_t.width and #hb_glyph_extents_t.height respectively if + * #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set. + * + * Return value: * The number of serialized items. * * Since: 0.9.7 **/ unsigned int hb_buffer_serialize_glyphs (hb_buffer_t *buffer, - unsigned int start, - unsigned int end, - char *buf, - unsigned int buf_size, - unsigned int *buf_consumed, - hb_font_t *font, - hb_buffer_serialize_format_t format, - hb_buffer_serialize_flags_t flags) + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) { - assert (start <= end && end <= buffer->len); + end = hb_clamp (end, start, buffer->len); + start = hb_min (start, end); unsigned int sconsumed; if (!buf_consumed) @@ -326,8 +476,7 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, if (buf_size) *buf = '\0'; - assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || - buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + buffer->assert_glyphs (); if (!buffer->have_positions) flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS; @@ -342,13 +491,13 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, { case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return _hb_buffer_serialize_glyphs_text (buffer, start, end, - buf, buf_size, buf_consumed, - font, flags); + buf, buf_size, buf_consumed, + font, flags); case HB_BUFFER_SERIALIZE_FORMAT_JSON: return _hb_buffer_serialize_glyphs_json (buffer, start, end, - buf, buf_size, buf_consumed, - font, flags); + buf, buf_size, buf_consumed, + font, flags); default: case HB_BUFFER_SERIALIZE_FORMAT_INVALID: @@ -357,43 +506,214 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, } } - -static hb_bool_t -parse_uint (const char *pp, const char *end, uint32_t *pv) +/** + * hb_buffer_serialize_unicode: + * @buffer: an #hb_buffer_t buffer. + * @start: the first item in @buffer to serialize. + * @end: the last item in @buffer to serialize. + * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to + * write serialized buffer into. + * @buf_size: the size of @buf. + * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf. + * @format: the #hb_buffer_serialize_format_t to use for formatting the output. + * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties + * to serialize. + * + * Serializes @buffer into a textual representation of its content, + * when the buffer contains Unicode codepoints (i.e., before shaping). This is + * useful for showing the contents of the buffer, for example during debugging. + * There are currently two supported serialization formats: + * + * ## text + * A human-readable, plain text format. + * The serialized codepoints will look something like: + * + * ``` + * ย  + * ``` + * + * - Glyphs are separated with `|` + * - Unicode codepoints are expressed as zero-padded four (or more) + * digit hexadecimal numbers preceded by `U+` + * - If #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set, the cluster + * will be indicated with a `=` then #hb_glyph_info_t.cluster. + * + * ## json + * A machine-readable, structured format. + * The serialized codepoints will be a list of objects with the following + * properties: + * - `u`: the Unicode codepoint as a decimal integer + * - `cl`: #hb_glyph_info_t.cluster if + * #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set. + * + * For example: + * + * ``` + * [{u:1617,cl:0},{u:1576,cl:1}] + * ``` + * + * Return value: + * The number of serialized items. + * + * Since: 2.7.3 + **/ +unsigned int +hb_buffer_serialize_unicode (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp)); - strncpy (buf, pp, len); - buf[len] = '\0'; + end = hb_clamp (end, start, buffer->len); + start = hb_min (start, end); - char *p = buf; - char *pend = p; - uint32_t v; + unsigned int sconsumed; + if (!buf_consumed) + buf_consumed = &sconsumed; + *buf_consumed = 0; + if (buf_size) + *buf = '\0'; - errno = 0; - v = strtol (p, &pend, 10); - if (errno || p == pend || pend - p != end - pp) + buffer->assert_unicode (); + + if (unlikely (start == end)) + return 0; + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_serialize_unicode_text (buffer, start, end, + buf, buf_size, buf_consumed, flags); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_serialize_unicode_json (buffer, start, end, + buf, buf_size, buf_consumed, flags); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return 0; + + } +} + +static unsigned int +_hb_buffer_serialize_invalid (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) +{ + assert (!buffer->len); + + unsigned int sconsumed; + if (!buf_consumed) + buf_consumed = &sconsumed; + if (buf_size < 3) + return 0; + if (format == HB_BUFFER_SERIALIZE_FORMAT_JSON) { + *buf++ = '['; + *buf++ = ']'; + *buf = '\0'; + } else if (format == HB_BUFFER_SERIALIZE_FORMAT_TEXT) { + *buf++ = '!'; + *buf++ = '!'; + *buf = '\0'; + } + *buf_consumed = 2; + return 0; +} + +/** + * hb_buffer_serialize: + * @buffer: an #hb_buffer_t buffer. + * @start: the first item in @buffer to serialize. + * @end: the last item in @buffer to serialize. + * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to + * write serialized buffer into. + * @buf_size: the size of @buf. + * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf. + * @font: (nullable): the #hb_font_t used to shape this buffer, needed to + * read glyph names and extents. If `NULL`, an empty font will be used. + * @format: the #hb_buffer_serialize_format_t to use for formatting the output. + * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties + * to serialize. + * + * Serializes @buffer into a textual representation of its content, whether + * Unicode codepoints or glyph identifiers and positioning information. This is + * useful for showing the contents of the buffer, for example during debugging. + * See the documentation of hb_buffer_serialize_unicode() and + * hb_buffer_serialize_glyphs() for a description of the output format. + * + * Return value: + * The number of serialized items. + * + * Since: 2.7.3 + **/ +unsigned int +hb_buffer_serialize (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) +{ + switch (buffer->content_type) + { + + case HB_BUFFER_CONTENT_TYPE_GLYPHS: + return hb_buffer_serialize_glyphs (buffer, start, end, buf, buf_size, + buf_consumed, font, format, flags); + + case HB_BUFFER_CONTENT_TYPE_UNICODE: + return hb_buffer_serialize_unicode (buffer, start, end, buf, buf_size, + buf_consumed, format, flags); + + case HB_BUFFER_CONTENT_TYPE_INVALID: + default: + return _hb_buffer_serialize_invalid (buffer, start, end, buf, buf_size, + buf_consumed, format, flags); + } +} + +static bool +parse_int (const char *pp, const char *end, int32_t *pv) +{ + int v; + const char *p = pp; + if (unlikely (!hb_parse_int (&p, end, &v, true/* whole buffer */))) return false; *pv = v; return true; } -static hb_bool_t -parse_int (const char *pp, const char *end, int32_t *pv) +static bool +parse_uint (const char *pp, const char *end, uint32_t *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp)); - strncpy (buf, pp, len); - buf[len] = '\0'; + unsigned int v; + const char *p = pp; + if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */))) + return false; - char *p = buf; - char *pend = p; - int32_t v; + *pv = v; + return true; +} - errno = 0; - v = strtol (p, &pend, 10); - if (errno || p == pend || pend - p != end - pp) +static bool +parse_hex (const char *pp, const char *end, uint32_t *pv) +{ + unsigned int v; + const char *p = pp; + if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */, 16))) return false; *pv = v; @@ -401,38 +721,48 @@ parse_int (const char *pp, const char *end, int32_t *pv) } #include "hb-buffer-deserialize-json.hh" -#include "hb-buffer-deserialize-text.hh" +#include "hb-buffer-deserialize-text-glyphs.hh" +#include "hb-buffer-deserialize-text-unicode.hh" /** * hb_buffer_deserialize_glyphs: * @buffer: an #hb_buffer_t buffer. - * @buf: (array length=buf_len): - * @buf_len: - * @end_ptr: (out): - * @font: - * @format: + * @buf: (array length=buf_len): string to deserialize + * @buf_len: the size of @buf, or -1 if it is `NULL`-terminated + * @end_ptr: (out) (optional): output pointer to the character after last + * consumed one. + * @font: (nullable): font for getting glyph IDs + * @format: the #hb_buffer_serialize_format_t of the input @buf * - * + * Deserializes glyphs @buffer from textual representation in the format + * produced by hb_buffer_serialize_glyphs(). * - * Return value: + * Return value: `true` if parse was successful, `false` if an error + * occurred. * * Since: 0.9.7 **/ hb_bool_t hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, - const char *buf, - int buf_len, /* -1 means nul-terminated */ - const char **end_ptr, /* May be nullptr */ - hb_font_t *font, /* May be nullptr */ - hb_buffer_serialize_format_t format) + const char *buf, + int buf_len, /* -1 means nul-terminated */ + const char **end_ptr, /* May be NULL */ + hb_font_t *font, /* May be NULL */ + hb_buffer_serialize_format_t format) { const char *end; if (!end_ptr) end_ptr = &end; *end_ptr = buf; - assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || - buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + buffer->assert_glyphs (); + + if (unlikely (hb_object_is_immutable (buffer))) + { + if (end_ptr) + *end_ptr = buf; + return false; + } if (buf_len == -1) buf_len = strlen (buf); @@ -451,14 +781,14 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, switch (format) { case HB_BUFFER_SERIALIZE_FORMAT_TEXT: - return _hb_buffer_deserialize_glyphs_text (buffer, + return _hb_buffer_deserialize_text_glyphs (buffer, buf, buf_len, end_ptr, font); case HB_BUFFER_SERIALIZE_FORMAT_JSON: - return _hb_buffer_deserialize_glyphs_json (buffer, - buf, buf_len, end_ptr, - font); + return _hb_buffer_deserialize_json (buffer, + buf, buf_len, end_ptr, + font); default: case HB_BUFFER_SERIALIZE_FORMAT_INVALID: @@ -466,3 +796,77 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, } } + + +/** + * hb_buffer_deserialize_unicode: + * @buffer: an #hb_buffer_t buffer. + * @buf: (array length=buf_len): string to deserialize + * @buf_len: the size of @buf, or -1 if it is `NULL`-terminated + * @end_ptr: (out) (optional): output pointer to the character after last + * consumed one. + * @format: the #hb_buffer_serialize_format_t of the input @buf + * + * Deserializes Unicode @buffer from textual representation in the format + * produced by hb_buffer_serialize_unicode(). + * + * Return value: `true` if parse was successful, `false` if an error + * occurred. + * + * Since: 2.7.3 + **/ +hb_bool_t +hb_buffer_deserialize_unicode (hb_buffer_t *buffer, + const char *buf, + int buf_len, /* -1 means nul-terminated */ + const char **end_ptr, /* May be NULL */ + hb_buffer_serialize_format_t format) +{ + const char *end; + if (!end_ptr) + end_ptr = &end; + *end_ptr = buf; + + buffer->assert_unicode (); + + if (unlikely (hb_object_is_immutable (buffer))) + { + if (end_ptr) + *end_ptr = buf; + return false; + } + + if (buf_len == -1) + buf_len = strlen (buf); + + if (!buf_len) + { + *end_ptr = buf; + return false; + } + + hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); + + hb_font_t* font = hb_font_get_empty (); + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_deserialize_text_unicode (buffer, + buf, buf_len, end_ptr, + font); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_deserialize_json (buffer, + buf, buf_len, end_ptr, + font); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return false; + + } +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-verify.cc b/src/3rdparty/harfbuzz-ng/src/hb-buffer-verify.cc new file mode 100644 index 00000000000..f111b2d8dcf --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer-verify.cc @@ -0,0 +1,439 @@ +/* + * Copyright ยฉ 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_BUFFER_VERIFY + +#include "hb-buffer.hh" + + +#define BUFFER_VERIFY_ERROR "buffer verify error: " +static inline void +buffer_verify_error (hb_buffer_t *buffer, + hb_font_t *font, + const char *fmt, + ...) HB_PRINTF_FUNC(3, 4); + +static inline void +buffer_verify_error (hb_buffer_t *buffer, + hb_font_t *font, + const char *fmt, + ...) +{ + va_list ap; + va_start (ap, fmt); + if (buffer->messaging ()) + { + buffer->message_impl (font, fmt, ap); + } + else + { + fprintf (stderr, "harfbuzz "); + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n"); + } + va_end (ap); +} + +static bool +buffer_verify_monotone (hb_buffer_t *buffer, + hb_font_t *font) +{ + /* Check that clusters are monotone. */ + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES || + buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + { + bool is_forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); + + unsigned int num_glyphs; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + + for (unsigned int i = 1; i < num_glyphs; i++) + if (info[i-1].cluster != info[i].cluster && + (info[i-1].cluster < info[i].cluster) != is_forward) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "clusters are not monotone."); + return false; + } + } + + return true; +} + +static bool +buffer_verify_unsafe_to_break (hb_buffer_t *buffer, + hb_buffer_t *text_buffer, + hb_font_t *font, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shapers) +{ + if (buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES && + buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + { + /* Cannot perform this check without monotone clusters. */ + return true; + } + + /* Check that breaking up shaping at safe-to-break is indeed safe. */ + + hb_buffer_t *fragment = hb_buffer_create_similar (buffer); + hb_buffer_set_flags (fragment, (hb_buffer_flags_t (hb_buffer_get_flags (fragment) & ~HB_BUFFER_FLAG_VERIFY))); + hb_buffer_t *reconstruction = hb_buffer_create_similar (buffer); + hb_buffer_set_flags (reconstruction, (hb_buffer_flags_t (hb_buffer_get_flags (reconstruction) & ~HB_BUFFER_FLAG_VERIFY))); + + unsigned int num_glyphs; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + + unsigned int num_chars; + hb_glyph_info_t *text = hb_buffer_get_glyph_infos (text_buffer, &num_chars); + + /* Chop text and shape fragments. */ + bool forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); + unsigned int start = 0; + unsigned int text_start = forward ? 0 : num_chars; + unsigned int text_end = text_start; + for (unsigned int end = 1; end < num_glyphs + 1; end++) + { + if (end < num_glyphs && + (info[end].cluster == info[end-1].cluster || + info[end-(forward?0:1)].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)) + continue; + + /* Shape segment corresponding to glyphs start..end. */ + if (end == num_glyphs) + { + if (forward) + text_end = num_chars; + else + text_start = 0; + } + else + { + if (forward) + { + unsigned int cluster = info[end].cluster; + while (text_end < num_chars && text[text_end].cluster < cluster) + text_end++; + } + else + { + unsigned int cluster = info[end - 1].cluster; + while (text_start && text[text_start - 1].cluster >= cluster) + text_start--; + } + } + assert (text_start < text_end); + + if (0) + printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end); + + hb_buffer_clear_contents (fragment); + + hb_buffer_flags_t flags = hb_buffer_get_flags (fragment); + if (0 < text_start) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_BOT); + if (text_end < num_chars) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_EOT); + hb_buffer_set_flags (fragment, flags); + + hb_buffer_append (fragment, text_buffer, text_start, text_end); + if (!hb_shape_full (font, fragment, features, num_features, shapers)) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "shaping failed while shaping fragment."); + hb_buffer_destroy (reconstruction); + hb_buffer_destroy (fragment); + return false; + } + else if (!fragment->successful || fragment->shaping_failed) + { + hb_buffer_destroy (reconstruction); + hb_buffer_destroy (fragment); + return true; + } + hb_buffer_append (reconstruction, fragment, 0, -1); + + start = end; + if (forward) + text_start = text_end; + else + text_end = text_start; + } + + bool ret = true; + hb_buffer_diff_flags_t diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0); + if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-break test failed."); + ret = false; + + /* Return the reconstructed result instead so it can be inspected. */ + hb_buffer_set_length (buffer, 0); + hb_buffer_append (buffer, reconstruction, 0, -1); + } + + hb_buffer_destroy (reconstruction); + hb_buffer_destroy (fragment); + + return ret; +} + +static bool +buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, + hb_buffer_t *text_buffer, + hb_font_t *font, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shapers) +{ + if (buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES && + buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + { + /* Cannot perform this check without monotone clusters. */ + return true; + } + + /* Check that shuffling up text before shaping at safe-to-concat points + * is indeed safe. */ + + /* This is what we do: + * + * 1. We shape text once. Then segment the text at all the safe-to-concat + * points; + * + * 2. Then we create two buffers, one containing all the even segments and + * one all the odd segments. + * + * 3. Because all these segments were safe-to-concat at both ends, we + * expect that concatenating them and shaping should NOT change the + * shaping results of each segment. As such, we expect that after + * shaping the two buffers, we still get cluster boundaries at the + * segment boundaries, and that those all are safe-to-concat points. + * Moreover, that there are NOT any safe-to-concat points within the + * segments. + * + * 4. Finally, we reconstruct the shaping results of the original text by + * simply interleaving the shaping results of the segments from the two + * buffers, and assert that the total shaping results is the same as + * the one from original buffer in step 1. + */ + + hb_buffer_t *fragments[2] {hb_buffer_create_similar (buffer), + hb_buffer_create_similar (buffer)}; + hb_buffer_set_flags (fragments[0], (hb_buffer_flags_t (hb_buffer_get_flags (fragments[0]) & ~HB_BUFFER_FLAG_VERIFY))); + hb_buffer_set_flags (fragments[1], (hb_buffer_flags_t (hb_buffer_get_flags (fragments[1]) & ~HB_BUFFER_FLAG_VERIFY))); + hb_buffer_t *reconstruction = hb_buffer_create_similar (buffer); + hb_buffer_set_flags (reconstruction, (hb_buffer_flags_t (hb_buffer_get_flags (reconstruction) & ~HB_BUFFER_FLAG_VERIFY))); + hb_segment_properties_t props; + hb_buffer_get_segment_properties (buffer, &props); + hb_buffer_set_segment_properties (fragments[0], &props); + hb_buffer_set_segment_properties (fragments[1], &props); + hb_buffer_set_segment_properties (reconstruction, &props); + + unsigned num_glyphs; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + + unsigned num_chars; + hb_glyph_info_t *text = hb_buffer_get_glyph_infos (text_buffer, &num_chars); + + bool forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); + + if (!forward) + hb_buffer_reverse (buffer); + + /* + * Split text into segments and collect into to fragment streams. + */ + { + unsigned fragment_idx = 0; + unsigned start = 0; + unsigned text_start = 0; + unsigned text_end = 0; + for (unsigned end = 1; end < num_glyphs + 1; end++) + { + if (end < num_glyphs && + (info[end].cluster == info[end-1].cluster || + info[end].mask & HB_GLYPH_FLAG_UNSAFE_TO_CONCAT)) + continue; + + /* Accumulate segment corresponding to glyphs start..end. */ + if (end == num_glyphs) + text_end = num_chars; + else + { + unsigned cluster = info[end].cluster; + while (text_end < num_chars && text[text_end].cluster < cluster) + text_end++; + } + assert (text_start < text_end); + + if (0) + printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end); + +#if 0 + hb_buffer_flags_t flags = hb_buffer_get_flags (fragment); + if (0 < text_start) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_BOT); + if (text_end < num_chars) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_EOT); + hb_buffer_set_flags (fragment, flags); +#endif + + hb_buffer_append (fragments[fragment_idx], text_buffer, text_start, text_end); + + start = end; + text_start = text_end; + fragment_idx = 1 - fragment_idx; + } + } + + bool ret = true; + hb_buffer_diff_flags_t diff; + /* + * Shape the two fragment streams. + */ + if (!hb_shape_full (font, fragments[0], features, num_features, shapers)) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "shaping failed while shaping fragment."); + ret = false; + goto out; + } + else if (!fragments[0]->successful || fragments[0]->shaping_failed) + { + ret = true; + goto out; + } + if (!hb_shape_full (font, fragments[1], features, num_features, shapers)) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "shaping failed while shaping fragment."); + ret = false; + goto out; + } + else if (!fragments[1]->successful || fragments[1]->shaping_failed) + { + ret = true; + goto out; + } + + if (!forward) + { + hb_buffer_reverse (fragments[0]); + hb_buffer_reverse (fragments[1]); + } + + /* + * Reconstruct results. + */ + { + unsigned fragment_idx = 0; + unsigned fragment_start[2] {0, 0}; + unsigned fragment_num_glyphs[2]; + hb_glyph_info_t *fragment_info[2]; + for (unsigned i = 0; i < 2; i++) + fragment_info[i] = hb_buffer_get_glyph_infos (fragments[i], &fragment_num_glyphs[i]); + while (fragment_start[0] < fragment_num_glyphs[0] || + fragment_start[1] < fragment_num_glyphs[1]) + { + unsigned fragment_end = fragment_start[fragment_idx] + 1; + while (fragment_end < fragment_num_glyphs[fragment_idx] && + (fragment_info[fragment_idx][fragment_end].cluster == fragment_info[fragment_idx][fragment_end - 1].cluster || + fragment_info[fragment_idx][fragment_end].mask & HB_GLYPH_FLAG_UNSAFE_TO_CONCAT)) + fragment_end++; + + hb_buffer_append (reconstruction, fragments[fragment_idx], fragment_start[fragment_idx], fragment_end); + + fragment_start[fragment_idx] = fragment_end; + fragment_idx = 1 - fragment_idx; + } + } + + if (!forward) + { + hb_buffer_reverse (buffer); + hb_buffer_reverse (reconstruction); + } + + /* + * Diff results. + */ + diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0); + if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-concat test failed."); + ret = false; + + /* Return the reconstructed result instead so it can be inspected. */ + hb_buffer_set_length (buffer, 0); + hb_buffer_append (buffer, reconstruction, 0, -1); + } + + +out: + hb_buffer_destroy (reconstruction); + hb_buffer_destroy (fragments[0]); + hb_buffer_destroy (fragments[1]); + + return ret; +} + +bool +hb_buffer_t::verify (hb_buffer_t *text_buffer, + hb_font_t *font, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shapers) +{ + bool ret = true; + if (!buffer_verify_monotone (this, font)) + ret = false; + if (!buffer_verify_unsafe_to_break (this, text_buffer, font, features, num_features, shapers)) + ret = false; + if ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) != 0 && + !buffer_verify_unsafe_to_concat (this, text_buffer, font, features, num_features, shapers)) + ret = false; + if (!ret) + { +#ifndef HB_NO_BUFFER_SERIALIZE + unsigned len = text_buffer->len; + hb_vector_t bytes; + if (likely (bytes.resize (len * 10 + 16))) + { + hb_buffer_serialize_unicode (text_buffer, + 0, len, + bytes.arrayZ, bytes.length, + &len, + HB_BUFFER_SERIALIZE_FORMAT_TEXT, + HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS); + buffer_verify_error (this, font, BUFFER_VERIFY_ERROR "text was: %s.", bytes.arrayZ); + } +#endif + } + return ret; +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer.cc b/src/3rdparty/harfbuzz-ng/src/hb-buffer.cc index 7ead43b0184..616cee807f6 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-buffer.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer.cc @@ -27,20 +27,27 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-buffer-private.hh" -#include "hb-utf-private.hh" +#include "hb-buffer.hh" +#include "hb-utf.hh" /** * SECTION: hb-buffer - * @title: Buffers + * @title: hb-buffer * @short_description: Input and output buffers * @include: hb.h * - * Buffers serve dual role in HarfBuzz; they hold the input characters that are - * passed hb_shape(), and after shaping they hold the output glyphs. + * Buffers serve a dual role in HarfBuzz; before shaping, they hold + * the input characters that are passed to hb_shape(), and after + * shaping they hold the output glyphs. + * + * The input buffer is a sequence of Unicode codepoints, with + * associated attributes such as direction and script. The output + * buffer is a sequence of glyphs, with associated attributes such + * as position and cluster. **/ + /** * hb_segment_properties_equal: * @a: first #hb_segment_properties_t to compare. @@ -49,7 +56,7 @@ * Checks the equality of two #hb_segment_properties_t's. * * Return value: - * %true if all properties of @a equal those of @b, false otherwise. + * `true` if all properties of @a equal those of @b, `false` otherwise. * * Since: 0.9.7 **/ @@ -79,12 +86,51 @@ hb_segment_properties_equal (const hb_segment_properties_t *a, unsigned int hb_segment_properties_hash (const hb_segment_properties_t *p) { - return (unsigned int) p->direction ^ - (unsigned int) p->script ^ + return ((unsigned int) p->direction * 31 + + (unsigned int) p->script) * 31 + (intptr_t) (p->language); } +/** + * hb_segment_properties_overlay: + * @p: #hb_segment_properties_t to fill in. + * @src: #hb_segment_properties_t to fill in from. + * + * Fills in missing fields of @p from @src in a considered manner. + * + * First, if @p does not have direction set, direction is copied from @src. + * + * Next, if @p and @src have the same direction (which can be unset), if @p + * does not have script set, script is copied from @src. + * + * Finally, if @p and @src have the same direction and script (which either + * can be unset), if @p does not have language set, language is copied from + * @src. + * + * Since: 3.3.0 + **/ +void +hb_segment_properties_overlay (hb_segment_properties_t *p, + const hb_segment_properties_t *src) +{ + if (unlikely (!p || !src)) + return; + if (!p->direction) + p->direction = src->direction; + + if (p->direction != src->direction) + return; + + if (!p->script) + p->script = src->script; + + if (p->script != src->script) + return; + + if (!p->language) + p->language = src->language; +} /* Here is how the buffer works internally: * @@ -94,14 +140,15 @@ hb_segment_properties_hash (const hb_segment_properties_t *p) * As an optimization, both info and out_info may point to the * same piece of memory, which is owned by info. This remains the * case as long as out_len doesn't exceed i at any time. - * In that case, swap_buffers() is no-op and the glyph operations operate - * mostly in-place. + * In that case, sync() is mostly no-op and the glyph operations + * operate mostly in-place. * * As soon as out_info gets longer than info, out_info is moved over - * to an alternate buffer (which we reuse the pos buffer for!), and its + * to an alternate buffer (which we reuse the pos buffer for), and its * current contents (out_len entries) are copied to the new place. - * This should all remain transparent to the user. swap_buffers() then - * switches info and out_info. + * + * This should all remain transparent to the user. sync() then + * switches info over to out_info and does housekeeping. */ @@ -111,11 +158,11 @@ hb_segment_properties_hash (const hb_segment_properties_t *p) bool hb_buffer_t::enlarge (unsigned int size) { - if (unlikely (in_error)) + if (unlikely (!successful)) return false; if (unlikely (size > max_len)) { - in_error = true; + successful = false; return false; } @@ -124,22 +171,23 @@ hb_buffer_t::enlarge (unsigned int size) hb_glyph_info_t *new_info = nullptr; bool separate_out = out_info != info; - if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0])))) + if (unlikely (hb_unsigned_mul_overflows (size, sizeof (info[0])))) goto done; while (size >= new_allocated) new_allocated += (new_allocated >> 1) + 32; - static_assert ((sizeof (info[0]) == sizeof (pos[0])), ""); - if (unlikely (_hb_unsigned_int_mul_overflows (new_allocated, sizeof (info[0])))) + unsigned new_bytes; + if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0]), &new_bytes))) goto done; - new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0])); - new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0])); + static_assert (sizeof (info[0]) == sizeof (pos[0]), ""); + new_pos = (hb_glyph_position_t *) hb_realloc (pos, new_bytes); + new_info = (hb_glyph_info_t *) hb_realloc (info, new_bytes); done: if (unlikely (!new_pos || !new_info)) - in_error = true; + successful = false; if (likely (new_pos)) pos = new_pos; @@ -148,10 +196,10 @@ done: info = new_info; out_info = separate_out ? (hb_glyph_info_t *) pos : info; - if (likely (!in_error)) + if (likely (successful)) allocated = new_allocated; - return likely (!in_error); + return likely (successful); } bool @@ -166,7 +214,7 @@ hb_buffer_t::make_room_for (unsigned int num_in, assert (have_output); out_info = (hb_glyph_info_t *) pos; - memcpy (out_info, info, out_len * sizeof (out_info[0])); + hb_memcpy (out_info, info, out_len * sizeof (out_info[0])); } return true; @@ -182,8 +230,12 @@ hb_buffer_t::shift_forward (unsigned int count) if (idx + count > len) { /* Under memory failure we might expose this area. At least - * clean it up. Oh well... */ - memset (info + len, 0, (idx + count - len) * sizeof (info[0])); + * clean it up. Oh well... + * + * Ideally, we should at least set Default_Ignorable bits on + * these, as well as consistent cluster values. But the former + * is layering violation... */ + hb_memset (info + len, 0, (idx + count - len) * sizeof (info[0])); } len += count; idx += count; @@ -210,31 +262,40 @@ hb_buffer_t::get_scratch_buffer (unsigned int *size) /* HarfBuzz-Internal API */ void -hb_buffer_t::reset (void) +hb_buffer_t::similar (const hb_buffer_t &src) { - if (unlikely (hb_object_is_inert (this))) - return; - hb_unicode_funcs_destroy (unicode); - unicode = hb_unicode_funcs_get_default (); + unicode = hb_unicode_funcs_reference (src.unicode); + flags = src.flags; + cluster_level = src.cluster_level; + replacement = src.invisible; + invisible = src.invisible; + not_found = src.not_found; +} + +void +hb_buffer_t::reset () +{ + hb_unicode_funcs_destroy (unicode); + unicode = hb_unicode_funcs_reference (hb_unicode_funcs_get_default ()); flags = HB_BUFFER_FLAG_DEFAULT; + cluster_level = HB_BUFFER_CLUSTER_LEVEL_DEFAULT; replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; + invisible = 0; + not_found = 0; clear (); } void -hb_buffer_t::clear (void) +hb_buffer_t::clear () { - if (unlikely (hb_object_is_inert (this))) - return; - + content_type = HB_BUFFER_CONTENT_TYPE_INVALID; hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT; props = default_props; - scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; - content_type = HB_BUFFER_CONTENT_TYPE_INVALID; - in_error = false; + successful = true; + shaping_failed = false; have_output = false; have_positions = false; @@ -243,14 +304,42 @@ hb_buffer_t::clear (void) out_len = 0; out_info = info; - serial = 0; - - memset (context, 0, sizeof context); - memset (context_len, 0, sizeof context_len); + hb_memset (context, 0, sizeof context); + hb_memset (context_len, 0, sizeof context_len); deallocate_var_all (); + serial = 0; + scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; } +void +hb_buffer_t::enter () +{ + deallocate_var_all (); + serial = 0; + shaping_failed = false; + scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; + unsigned mul; + if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_LEN_FACTOR, &mul))) + { + max_len = hb_max (mul, (unsigned) HB_BUFFER_MAX_LEN_MIN); + } + if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_OPS_FACTOR, &mul))) + { + max_ops = hb_max (mul, (unsigned) HB_BUFFER_MAX_OPS_MIN); + } +} +void +hb_buffer_t::leave () +{ + max_len = HB_BUFFER_MAX_LEN_DEFAULT; + max_ops = HB_BUFFER_MAX_OPS_DEFAULT; + deallocate_var_all (); + serial = 0; + // Intentionally not reseting shaping_failed, such that it can be inspected. +} + + void hb_buffer_t::add (hb_codepoint_t codepoint, unsigned int cluster) @@ -261,7 +350,7 @@ hb_buffer_t::add (hb_codepoint_t codepoint, glyph = &info[len]; - memset (glyph, 0, sizeof (*glyph)); + hb_memset (glyph, 0, sizeof (*glyph)); glyph->codepoint = codepoint; glyph->mask = 0; glyph->cluster = cluster; @@ -281,123 +370,79 @@ hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info) void -hb_buffer_t::remove_output (void) +hb_buffer_t::clear_output () { - if (unlikely (hb_object_is_inert (this))) - return; - - have_output = false; - have_positions = false; - - out_len = 0; - out_info = info; -} - -void -hb_buffer_t::clear_output (void) -{ - if (unlikely (hb_object_is_inert (this))) - return; - have_output = true; have_positions = false; + idx = 0; out_len = 0; out_info = info; } void -hb_buffer_t::clear_positions (void) +hb_buffer_t::clear_positions () { - if (unlikely (hb_object_is_inert (this))) - return; - have_output = false; have_positions = true; out_len = 0; out_info = info; - memset (pos, 0, sizeof (pos[0]) * len); + hb_memset (pos, 0, sizeof (pos[0]) * len); } -void -hb_buffer_t::swap_buffers (void) +bool +hb_buffer_t::sync () { - if (unlikely (in_error)) return; + bool ret = false; assert (have_output); - have_output = false; + + assert (idx <= len); + + if (unlikely (!successful || !next_glyphs (len - idx))) + goto reset; if (out_info != info) { - hb_glyph_info_t *tmp_string; - tmp_string = info; + pos = (hb_glyph_position_t *) info; info = out_info; - out_info = tmp_string; - pos = (hb_glyph_position_t *) out_info; } - - unsigned int tmp; - tmp = len; len = out_len; - out_len = tmp; + ret = true; +reset: + have_output = false; + out_len = 0; + out_info = info; idx = 0; + + return ret; } - -void -hb_buffer_t::replace_glyphs (unsigned int num_in, - unsigned int num_out, - const uint32_t *glyph_data) +int +hb_buffer_t::sync_so_far () { - if (unlikely (!make_room_for (num_in, num_out))) return; + bool had_output = have_output; + unsigned out_i = out_len; + unsigned i = idx; + unsigned old_idx = idx; - merge_clusters (idx, idx + num_in); + if (sync ()) + idx = out_i; + else + idx = i; - hb_glyph_info_t orig_info = info[idx]; - hb_glyph_info_t *pinfo = &out_info[out_len]; - for (unsigned int i = 0; i < num_out; i++) + if (had_output) { - *pinfo = orig_info; - pinfo->codepoint = glyph_data[i]; - pinfo++; + have_output = true; + out_len = idx; } - idx += num_in; - out_len += num_out; -} + assert (idx <= len); -void -hb_buffer_t::output_glyph (hb_codepoint_t glyph_index) -{ - if (unlikely (!make_room_for (0, 1))) return; - - out_info[out_len] = info[idx]; - out_info[out_len].codepoint = glyph_index; - - out_len++; -} - -void -hb_buffer_t::output_info (const hb_glyph_info_t &glyph_info) -{ - if (unlikely (!make_room_for (0, 1))) return; - - out_info[out_len] = glyph_info; - - out_len++; -} - -void -hb_buffer_t::copy_glyph (void) -{ - if (unlikely (!make_room_for (0, 1))) return; - - out_info[out_len] = info[idx]; - - out_len++; + return idx - old_idx; } bool @@ -409,7 +454,7 @@ hb_buffer_t::move_to (unsigned int i) idx = i; return true; } - if (unlikely (in_error)) + if (unlikely (!successful)) return false; assert (i <= out_len + (len - idx)); @@ -429,8 +474,13 @@ hb_buffer_t::move_to (unsigned int i) unsigned int count = out_len - i; /* This will blow in our face if memory allocation fails later - * in this same lookup... */ - if (unlikely (idx < count && !shift_forward (count + 32))) return false; + * in this same lookup... + * + * We used to shift with extra 32 items. + * But that would leave empty slots in the buffer in case of allocation + * failures. See comments in shift_forward(). This can cause O(N^2) + * behavior more severely than adding 32 empty slots can... */ + if (unlikely (idx < count && !shift_forward (count - idx))) return false; assert (idx >= count); @@ -442,19 +492,6 @@ hb_buffer_t::move_to (unsigned int i) return true; } -void -hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index) -{ - if (unlikely (out_info != info || out_len != idx)) { - if (unlikely (!make_room_for (1, 1))) return; - out_info[out_len] = info[idx]; - } - out_info[out_len].codepoint = glyph_index; - - idx++; - out_len++; -} - void hb_buffer_t::set_masks (hb_mask_t value, @@ -468,79 +505,12 @@ hb_buffer_t::set_masks (hb_mask_t value, if (!mask) return; - if (cluster_start == 0 && cluster_end == (unsigned int)-1) { - unsigned int count = len; - for (unsigned int i = 0; i < count; i++) - info[i].mask = (info[i].mask & not_mask) | value; - return; - } - unsigned int count = len; for (unsigned int i = 0; i < count; i++) if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end) info[i].mask = (info[i].mask & not_mask) | value; } -void -hb_buffer_t::reverse_range (unsigned int start, - unsigned int end) -{ - unsigned int i, j; - - if (end - start < 2) - return; - - for (i = start, j = end - 1; i < j; i++, j--) { - hb_glyph_info_t t; - - t = info[i]; - info[i] = info[j]; - info[j] = t; - } - - if (have_positions) { - for (i = start, j = end - 1; i < j; i++, j--) { - hb_glyph_position_t t; - - t = pos[i]; - pos[i] = pos[j]; - pos[j] = t; - } - } -} - -void -hb_buffer_t::reverse (void) -{ - if (unlikely (!len)) - return; - - reverse_range (0, len); -} - -void -hb_buffer_t::reverse_clusters (void) -{ - unsigned int i, start, count, last_cluster; - - if (unlikely (!len)) - return; - - reverse (); - - count = len; - start = 0; - last_cluster = info[0].cluster; - for (i = 1; i < count; i++) { - if (last_cluster != info[i].cluster) { - reverse_range (start, i); - start = i; - last_cluster = info[i].cluster; - } - } - reverse_range (start, i); -} - void hb_buffer_t::merge_clusters_impl (unsigned int start, unsigned int end) @@ -554,18 +524,20 @@ hb_buffer_t::merge_clusters_impl (unsigned int start, unsigned int cluster = info[start].cluster; for (unsigned int i = start + 1; i < end; i++) - cluster = MIN (cluster, info[i].cluster); + cluster = hb_min (cluster, info[i].cluster); /* Extend end */ - while (end < len && info[end - 1].cluster == info[end].cluster) - end++; + if (cluster != info[end - 1].cluster) + while (end < len && info[end - 1].cluster == info[end].cluster) + end++; /* Extend start */ - while (idx < start && info[start - 1].cluster == info[start].cluster) - start--; + if (cluster != info[start].cluster) + while (idx < start && info[start - 1].cluster == info[start].cluster) + start--; /* If we hit the start of buffer, continue in out-buffer. */ - if (idx == start) + if (idx == start && info[start].cluster != cluster) for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--) set_cluster (out_info[i - 1], cluster); @@ -585,7 +557,7 @@ hb_buffer_t::merge_out_clusters (unsigned int start, unsigned int cluster = out_info[start].cluster; for (unsigned int i = start + 1; i < end; i++) - cluster = MIN (cluster, out_info[i].cluster); + cluster = hb_min (cluster, out_info[i].cluster); /* Extend start */ while (start && out_info[start - 1].cluster == out_info[start].cluster) @@ -609,7 +581,8 @@ hb_buffer_t::delete_glyph () /* The logic here is duplicated in hb_ot_hide_default_ignorables(). */ unsigned int cluster = info[idx].cluster; - if (idx + 1 < len && cluster == info[idx + 1].cluster) + if ((idx + 1 < len && cluster == info[idx + 1].cluster) || + (out_len && cluster == out_info[out_len - 1].cluster)) { /* Cluster survives; do nothing. */ goto done; @@ -640,36 +613,56 @@ done: } void -hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end) +hb_buffer_t::delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info)) { - unsigned int cluster = (unsigned int) -1; - cluster = _unsafe_to_break_find_min_cluster (info, start, end, cluster); - _unsafe_to_break_set_mask (info, start, end, cluster); -} -void -hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end) -{ - if (!have_output) + /* Merge clusters and delete filtered glyphs. + * NOTE! We can't use out-buffer as we have positioning data. */ + unsigned int j = 0; + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) { - unsafe_to_break_impl (start, end); - return; + if (filter (&info[i])) + { + /* Merge clusters. + * Same logic as delete_glyph(), but for in-place removal. */ + + unsigned int cluster = info[i].cluster; + if (i + 1 < count && cluster == info[i + 1].cluster) + continue; /* Cluster survives; do nothing. */ + + if (j) + { + /* Merge cluster backward. */ + if (cluster < info[j - 1].cluster) + { + unsigned int mask = info[i].mask; + unsigned int old_cluster = info[j - 1].cluster; + for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--) + set_cluster (info[k - 1], cluster, mask); + } + continue; + } + + if (i + 1 < count) + merge_clusters (i, i + 2); /* Merge cluster forward. */ + + continue; + } + + if (j != i) + { + info[j] = info[i]; + pos[j] = pos[i]; + } + j++; } - - assert (start <= out_len); - assert (idx <= end); - - unsigned int cluster = (unsigned int) -1; - cluster = _unsafe_to_break_find_min_cluster (out_info, start, out_len, cluster); - cluster = _unsafe_to_break_find_min_cluster (info, idx, end, cluster); - _unsafe_to_break_set_mask (out_info, start, out_len, cluster); - _unsafe_to_break_set_mask (info, idx, end, cluster); + len = j; } void -hb_buffer_t::guess_segment_properties (void) +hb_buffer_t::guess_segment_properties () { - assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || - (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + assert_unicode (); /* If script is set to INVALID, guess from buffer contents */ if (props.script == HB_SCRIPT_INVALID) { @@ -678,8 +671,8 @@ hb_buffer_t::guess_segment_properties (void) if (likely (script != HB_SCRIPT_COMMON && script != HB_SCRIPT_INHERITED && script != HB_SCRIPT_UNKNOWN)) { - props.script = script; - break; + props.script = script; + break; } } } @@ -687,6 +680,8 @@ hb_buffer_t::guess_segment_properties (void) /* If direction is set to INVALID, guess from script */ if (props.direction == HB_DIRECTION_INVALID) { props.direction = hb_script_get_horizontal_direction (props.script); + if (props.direction == HB_DIRECTION_INVALID) + props.direction = HB_DIRECTION_LTR; } /* If language is not set, use default language from locale */ @@ -699,22 +694,46 @@ hb_buffer_t::guess_segment_properties (void) /* Public API */ +DEFINE_NULL_INSTANCE (hb_buffer_t) = +{ + HB_OBJECT_HEADER_STATIC, + + const_cast (&_hb_Null_hb_unicode_funcs_t), + HB_BUFFER_FLAG_DEFAULT, + HB_BUFFER_CLUSTER_LEVEL_DEFAULT, + HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT, + 0, /* invisible */ + 0, /* not_found */ + + + HB_BUFFER_CONTENT_TYPE_INVALID, + HB_SEGMENT_PROPERTIES_DEFAULT, + + false, /* successful */ + true, /* shaping_failed */ + false, /* have_output */ + true /* have_positions */ + + /* Zero is good enough for everything else. */ +}; + + /** - * hb_buffer_create: (Xconstructor) + * hb_buffer_create: * * Creates a new #hb_buffer_t with all properties to defaults. * * Return value: (transfer full): * A newly allocated #hb_buffer_t with a reference count of 1. The initial * reference count should be released with hb_buffer_destroy() when you are done - * using the #hb_buffer_t. This function never returns %NULL. If memory cannot + * using the #hb_buffer_t. This function never returns `NULL`. If memory cannot * be allocated, a special #hb_buffer_t object will be returned on which - * hb_buffer_allocation_successful() returns %false. + * hb_buffer_allocation_successful() returns `false`. * * Since: 0.9.2 **/ hb_buffer_t * -hb_buffer_create (void) +hb_buffer_create () { hb_buffer_t *buffer; @@ -730,43 +749,63 @@ hb_buffer_create (void) } /** - * hb_buffer_get_empty: + * hb_buffer_create_similar: + * @src: An #hb_buffer_t * - * + * Creates a new #hb_buffer_t, similar to hb_buffer_create(). The only + * difference is that the buffer is configured similarly to @src. * * Return value: (transfer full): + * A newly allocated #hb_buffer_t, similar to hb_buffer_create(). + * + * Since: 3.3.0 + **/ +hb_buffer_t * +hb_buffer_create_similar (const hb_buffer_t *src) +{ + hb_buffer_t *buffer = hb_buffer_create (); + + buffer->similar (*src); + + return buffer; +} + +/** + * hb_buffer_reset: + * @buffer: An #hb_buffer_t + * + * Resets the buffer to its initial status, as if it was just newly created + * with hb_buffer_create(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_reset (hb_buffer_t *buffer) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->reset (); +} + +/** + * hb_buffer_get_empty: + * + * Fetches an empty #hb_buffer_t. + * + * Return value: (transfer full): The empty buffer * * Since: 0.9.2 **/ hb_buffer_t * -hb_buffer_get_empty (void) +hb_buffer_get_empty () { - static const hb_buffer_t _hb_buffer_nil = { - HB_OBJECT_HEADER_STATIC, - - const_cast (&_hb_unicode_funcs_nil), - HB_BUFFER_FLAG_DEFAULT, - HB_BUFFER_CLUSTER_LEVEL_DEFAULT, - HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT, - HB_BUFFER_SCRATCH_FLAG_DEFAULT, - HB_BUFFER_MAX_LEN_DEFAULT, - HB_BUFFER_MAX_OPS_DEFAULT, - - HB_BUFFER_CONTENT_TYPE_INVALID, - HB_SEGMENT_PROPERTIES_DEFAULT, - true, /* in_error */ - true, /* have_output */ - true /* have_positions */ - - /* Zero is good enough for everything else. */ - }; - - return const_cast (&_hb_buffer_nil); + return const_cast (&Null (hb_buffer_t)); } /** * hb_buffer_reference: (skip) - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Increases the reference count on @buffer by one. This prevents @buffer from * being destroyed until a matching call to hb_buffer_destroy() is made. @@ -784,7 +823,7 @@ hb_buffer_reference (hb_buffer_t *buffer) /** * hb_buffer_destroy: (skip) - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Deallocate the @buffer. * Decreases the reference count on @buffer by one. If the result is zero, then @@ -799,25 +838,27 @@ hb_buffer_destroy (hb_buffer_t *buffer) hb_unicode_funcs_destroy (buffer->unicode); - free (buffer->info); - free (buffer->pos); + hb_free (buffer->info); + hb_free (buffer->pos); +#ifndef HB_NO_BUFFER_MESSAGE if (buffer->message_destroy) buffer->message_destroy (buffer->message_data); +#endif - free (buffer); + hb_free (buffer); } /** * hb_buffer_set_user_data: (skip) - * @buffer: an #hb_buffer_t. - * @key: - * @data: - * @destroy: - * @replace: + * @buffer: An #hb_buffer_t + * @key: The user-data key + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key * - * + * Attaches a user-data key/data pair to the specified buffer. * - * Return value: + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -833,17 +874,18 @@ hb_buffer_set_user_data (hb_buffer_t *buffer, /** * hb_buffer_get_user_data: (skip) - * @buffer: an #hb_buffer_t. - * @key: + * @buffer: An #hb_buffer_t + * @key: The user-data key to query * - * + * Fetches the user data associated with the specified key, + * attached to the specified buffer. * - * Return value: + * Return value: (transfer none): A pointer to the user data * * Since: 0.9.2 **/ void * -hb_buffer_get_user_data (hb_buffer_t *buffer, +hb_buffer_get_user_data (const hb_buffer_t *buffer, hb_user_data_key_t *key) { return hb_object_get_user_data (buffer, key); @@ -852,11 +894,37 @@ hb_buffer_get_user_data (hb_buffer_t *buffer, /** * hb_buffer_set_content_type: - * @buffer: an #hb_buffer_t. - * @content_type: the type of buffer contents to set + * @buffer: An #hb_buffer_t + * @content_type: The type of buffer contents to set * - * Sets the type of @buffer contents, buffers are either empty, contain - * characters (before shaping) or glyphs (the result of shaping). + * Sets the type of @buffer contents. Buffers are either empty, contain + * characters (before shaping), or contain glyphs (the result of shaping). + * + * You rarely need to call this function, since a number of other + * functions transition the content type for you. Namely: + * + * - A newly created buffer starts with content type + * %HB_BUFFER_CONTENT_TYPE_INVALID. Calling hb_buffer_reset(), + * hb_buffer_clear_contents(), as well as calling hb_buffer_set_length() + * with an argument of zero all set the buffer content type to invalid + * as well. + * + * - Calling hb_buffer_add_utf8(), hb_buffer_add_utf16(), + * hb_buffer_add_utf32(), hb_buffer_add_codepoints() and + * hb_buffer_add_latin1() expect that buffer is either empty and + * have a content type of invalid, or that buffer content type is + * %HB_BUFFER_CONTENT_TYPE_UNICODE, and they also set the content + * type to Unicode if they added anything to an empty buffer. + * + * - Finally hb_shape() and hb_shape_full() expect that the buffer + * is either empty and have content type of invalid, or that buffer + * content type is %HB_BUFFER_CONTENT_TYPE_UNICODE, and upon + * success they set the buffer content type to + * %HB_BUFFER_CONTENT_TYPE_GLYPHS. + * + * The above transitions are designed such that one can use a buffer + * in a loop of "reset : add-text : shape" without needing to ever + * modify the content type manually. * * Since: 0.9.5 **/ @@ -869,17 +937,18 @@ hb_buffer_set_content_type (hb_buffer_t *buffer, /** * hb_buffer_get_content_type: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * - * see hb_buffer_set_content_type(). + * Fetches the type of @buffer contents. Buffers are either empty, contain + * characters (before shaping), or contain glyphs (the result of shaping). * * Return value: - * The type of @buffer contents. + * The type of @buffer contents * * Since: 0.9.5 **/ hb_buffer_content_type_t -hb_buffer_get_content_type (hb_buffer_t *buffer) +hb_buffer_get_content_type (const hb_buffer_t *buffer) { return buffer->content_type; } @@ -887,10 +956,11 @@ hb_buffer_get_content_type (hb_buffer_t *buffer) /** * hb_buffer_set_unicode_funcs: - * @buffer: an #hb_buffer_t. - * @unicode_funcs: + * @buffer: An #hb_buffer_t + * @unicode_funcs: The Unicode-functions structure * - * + * Sets the Unicode-functions structure of a buffer to + * @unicode_funcs. * * Since: 0.9.2 **/ @@ -898,13 +968,12 @@ void hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, hb_unicode_funcs_t *unicode_funcs) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; if (!unicode_funcs) unicode_funcs = hb_unicode_funcs_get_default (); - hb_unicode_funcs_reference (unicode_funcs); hb_unicode_funcs_destroy (buffer->unicode); buffer->unicode = unicode_funcs; @@ -912,23 +981,23 @@ hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, /** * hb_buffer_get_unicode_funcs: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * - * + * Fetches the Unicode-functions structure of a buffer. * - * Return value: + * Return value: The Unicode-functions structure * * Since: 0.9.2 **/ hb_unicode_funcs_t * -hb_buffer_get_unicode_funcs (hb_buffer_t *buffer) +hb_buffer_get_unicode_funcs (const hb_buffer_t *buffer) { return buffer->unicode; } /** * hb_buffer_set_direction: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * @direction: the #hb_direction_t of the @buffer * * Set the text flow direction of the buffer. No shaping can happen without @@ -944,9 +1013,8 @@ hb_buffer_get_unicode_funcs (hb_buffer_t *buffer) void hb_buffer_set_direction (hb_buffer_t *buffer, hb_direction_t direction) - { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->props.direction = direction; @@ -954,7 +1022,7 @@ hb_buffer_set_direction (hb_buffer_t *buffer, /** * hb_buffer_get_direction: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * See hb_buffer_set_direction() * @@ -964,15 +1032,15 @@ hb_buffer_set_direction (hb_buffer_t *buffer, * Since: 0.9.2 **/ hb_direction_t -hb_buffer_get_direction (hb_buffer_t *buffer) +hb_buffer_get_direction (const hb_buffer_t *buffer) { return buffer->props.direction; } /** * hb_buffer_set_script: - * @buffer: an #hb_buffer_t. - * @script: an #hb_script_t to set. + * @buffer: An #hb_buffer_t + * @script: An #hb_script_t to set. * * Sets the script of @buffer to @script. * @@ -982,7 +1050,7 @@ hb_buffer_get_direction (hb_buffer_t *buffer) * * You can pass one of the predefined #hb_script_t values, or use * hb_script_from_string() or hb_script_from_iso15924_tag() to get the - * corresponding script from an ISOย 15924 script tag. + * corresponding script from an ISO 15924 script tag. * * Since: 0.9.2 **/ @@ -990,7 +1058,7 @@ void hb_buffer_set_script (hb_buffer_t *buffer, hb_script_t script) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->props.script = script; @@ -998,25 +1066,25 @@ hb_buffer_set_script (hb_buffer_t *buffer, /** * hb_buffer_get_script: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * - * See hb_buffer_set_script(). + * Fetches the script of @buffer. * * Return value: - * The #hb_script_t of the @buffer. + * The #hb_script_t of the @buffer * * Since: 0.9.2 **/ hb_script_t -hb_buffer_get_script (hb_buffer_t *buffer) +hb_buffer_get_script (const hb_buffer_t *buffer) { return buffer->props.script; } /** * hb_buffer_set_language: - * @buffer: an #hb_buffer_t. - * @language: an hb_language_t to set. + * @buffer: An #hb_buffer_t + * @language: An hb_language_t to set * * Sets the language of @buffer to @language. * @@ -1025,7 +1093,7 @@ hb_buffer_get_script (hb_buffer_t *buffer) * are orthogonal to the scripts, and though they are related, they are * different concepts and should not be confused with each other. * - * Use hb_language_from_string() to convert from ISOย 639 language codes to + * Use hb_language_from_string() to convert from BCP 47 language tags to * #hb_language_t. * * Since: 0.9.2 @@ -1034,7 +1102,7 @@ void hb_buffer_set_language (hb_buffer_t *buffer, hb_language_t language) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->props.language = language; @@ -1042,7 +1110,7 @@ hb_buffer_set_language (hb_buffer_t *buffer, /** * hb_buffer_get_language: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * See hb_buffer_set_language(). * @@ -1052,15 +1120,15 @@ hb_buffer_set_language (hb_buffer_t *buffer, * Since: 0.9.2 **/ hb_language_t -hb_buffer_get_language (hb_buffer_t *buffer) +hb_buffer_get_language (const hb_buffer_t *buffer) { return buffer->props.language; } /** * hb_buffer_set_segment_properties: - * @buffer: an #hb_buffer_t. - * @props: an #hb_segment_properties_t to use. + * @buffer: An #hb_buffer_t + * @props: An #hb_segment_properties_t to use * * Sets the segment properties of the buffer, a shortcut for calling * hb_buffer_set_direction(), hb_buffer_set_script() and @@ -1072,7 +1140,7 @@ void hb_buffer_set_segment_properties (hb_buffer_t *buffer, const hb_segment_properties_t *props) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->props = *props; @@ -1080,15 +1148,15 @@ hb_buffer_set_segment_properties (hb_buffer_t *buffer, /** * hb_buffer_get_segment_properties: - * @buffer: an #hb_buffer_t. - * @props: (out): the output #hb_segment_properties_t. + * @buffer: An #hb_buffer_t + * @props: (out): The output #hb_segment_properties_t * * Sets @props to the #hb_segment_properties_t of @buffer. * * Since: 0.9.7 **/ void -hb_buffer_get_segment_properties (hb_buffer_t *buffer, +hb_buffer_get_segment_properties (const hb_buffer_t *buffer, hb_segment_properties_t *props) { *props = buffer->props; @@ -1097,8 +1165,8 @@ hb_buffer_get_segment_properties (hb_buffer_t *buffer, /** * hb_buffer_set_flags: - * @buffer: an #hb_buffer_t. - * @flags: the buffer flags to set. + * @buffer: An #hb_buffer_t + * @flags: The buffer flags to set * * Sets @buffer flags to @flags. See #hb_buffer_flags_t. * @@ -1108,7 +1176,7 @@ void hb_buffer_set_flags (hb_buffer_t *buffer, hb_buffer_flags_t flags) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->flags = flags; @@ -1116,35 +1184,37 @@ hb_buffer_set_flags (hb_buffer_t *buffer, /** * hb_buffer_get_flags: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * - * See hb_buffer_set_flags(). + * Fetches the #hb_buffer_flags_t of @buffer. * - * Return value: - * The @buffer flags. + * Return value: + * The @buffer flags * * Since: 0.9.7 **/ hb_buffer_flags_t -hb_buffer_get_flags (hb_buffer_t *buffer) +hb_buffer_get_flags (const hb_buffer_t *buffer) { return buffer->flags; } /** * hb_buffer_set_cluster_level: - * @buffer: an #hb_buffer_t. - * @cluster_level: + * @buffer: An #hb_buffer_t + * @cluster_level: The cluster level to set on the buffer * - * + * Sets the cluster level of a buffer. The #hb_buffer_cluster_level_t + * dictates one aspect of how HarfBuzz will treat non-base characters + * during shaping. * * Since: 0.9.42 **/ void -hb_buffer_set_cluster_level (hb_buffer_t *buffer, - hb_buffer_cluster_level_t cluster_level) +hb_buffer_set_cluster_level (hb_buffer_t *buffer, + hb_buffer_cluster_level_t cluster_level) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->cluster_level = cluster_level; @@ -1152,16 +1222,18 @@ hb_buffer_set_cluster_level (hb_buffer_t *buffer, /** * hb_buffer_get_cluster_level: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * - * + * Fetches the cluster level of a buffer. The #hb_buffer_cluster_level_t + * dictates one aspect of how HarfBuzz will treat non-base characters + * during shaping. * - * Return value: + * Return value: The cluster level of @buffer * * Since: 0.9.42 **/ hb_buffer_cluster_level_t -hb_buffer_get_cluster_level (hb_buffer_t *buffer) +hb_buffer_get_cluster_level (const hb_buffer_t *buffer) { return buffer->cluster_level; } @@ -1169,13 +1241,13 @@ hb_buffer_get_cluster_level (hb_buffer_t *buffer) /** * hb_buffer_set_replacement_codepoint: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * @replacement: the replacement #hb_codepoint_t * * Sets the #hb_codepoint_t that replaces invalid entries for a given encoding * when adding text to @buffer. * - * Default is %HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT. + * Default is #HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT. * * Since: 0.9.31 **/ @@ -1183,7 +1255,7 @@ void hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, hb_codepoint_t replacement) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->replacement = replacement; @@ -1191,40 +1263,106 @@ hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, /** * hb_buffer_get_replacement_codepoint: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * - * See hb_buffer_set_replacement_codepoint(). + * Fetches the #hb_codepoint_t that replaces invalid entries for a given encoding + * when adding text to @buffer. * - * Return value: - * The @buffer replacement #hb_codepoint_t. + * Return value: + * The @buffer replacement #hb_codepoint_t * * Since: 0.9.31 **/ hb_codepoint_t -hb_buffer_get_replacement_codepoint (hb_buffer_t *buffer) +hb_buffer_get_replacement_codepoint (const hb_buffer_t *buffer) { return buffer->replacement; } /** - * hb_buffer_reset: - * @buffer: an #hb_buffer_t. + * hb_buffer_set_invisible_glyph: + * @buffer: An #hb_buffer_t + * @invisible: the invisible #hb_codepoint_t * - * Resets the buffer to its initial status, as if it was just newly created - * with hb_buffer_create(). + * Sets the #hb_codepoint_t that replaces invisible characters in + * the shaping result. If set to zero (default), the glyph for the + * U+0020 SPACE character is used. Otherwise, this value is used + * verbatim. * - * Since: 0.9.2 + * Since: 2.0.0 **/ void -hb_buffer_reset (hb_buffer_t *buffer) +hb_buffer_set_invisible_glyph (hb_buffer_t *buffer, + hb_codepoint_t invisible) { - buffer->reset (); + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->invisible = invisible; } +/** + * hb_buffer_get_invisible_glyph: + * @buffer: An #hb_buffer_t + * + * See hb_buffer_set_invisible_glyph(). + * + * Return value: + * The @buffer invisible #hb_codepoint_t + * + * Since: 2.0.0 + **/ +hb_codepoint_t +hb_buffer_get_invisible_glyph (const hb_buffer_t *buffer) +{ + return buffer->invisible; +} + +/** + * hb_buffer_set_not_found_glyph: + * @buffer: An #hb_buffer_t + * @not_found: the not-found #hb_codepoint_t + * + * Sets the #hb_codepoint_t that replaces characters not found in + * the font during shaping. + * + * The not-found glyph defaults to zero, sometimes knows as the + * ".notdef" glyph. This API allows for differentiating the two. + * + * Since: 3.1.0 + **/ +void +hb_buffer_set_not_found_glyph (hb_buffer_t *buffer, + hb_codepoint_t not_found) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->not_found = not_found; +} + +/** + * hb_buffer_get_not_found_glyph: + * @buffer: An #hb_buffer_t + * + * See hb_buffer_set_not_found_glyph(). + * + * Return value: + * The @buffer not-found #hb_codepoint_t + * + * Since: 3.1.0 + **/ +hb_codepoint_t +hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer) +{ + return buffer->not_found; +} + + /** * hb_buffer_clear_contents: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Similar to hb_buffer_reset(), but does not clear the Unicode functions and * the replacement code point. @@ -1234,18 +1372,21 @@ hb_buffer_reset (hb_buffer_t *buffer) void hb_buffer_clear_contents (hb_buffer_t *buffer) { + if (unlikely (hb_object_is_immutable (buffer))) + return; + buffer->clear (); } /** * hb_buffer_pre_allocate: - * @buffer: an #hb_buffer_t. - * @size: number of items to pre allocate. + * @buffer: An #hb_buffer_t + * @size: Number of items to pre allocate. * * Pre allocates memory for @buffer to fit at least @size number of items. * * Return value: - * %true if @buffer memory allocation succeeded, %false otherwise. + * `true` if @buffer memory allocation succeeded, `false` otherwise * * Since: 0.9.2 **/ @@ -1257,26 +1398,26 @@ hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size) /** * hb_buffer_allocation_successful: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Check if allocating memory for the buffer succeeded. * * Return value: - * %true if @buffer memory allocation succeeded, %false otherwise. + * `true` if @buffer memory allocation succeeded, `false` otherwise. * * Since: 0.9.2 **/ hb_bool_t hb_buffer_allocation_successful (hb_buffer_t *buffer) { - return !buffer->in_error; + return buffer->successful; } /** * hb_buffer_add: - * @buffer: an #hb_buffer_t. - * @codepoint: a Unicode code point. - * @cluster: the cluster value of @codepoint. + * @buffer: An #hb_buffer_t + * @codepoint: A Unicode code point. + * @cluster: The cluster value of @codepoint. * * Appends a character with the Unicode value of @codepoint to @buffer, and * gives it the initial cluster value of @cluster. Clusters can be any thing @@ -1300,14 +1441,14 @@ hb_buffer_add (hb_buffer_t *buffer, /** * hb_buffer_set_length: - * @buffer: an #hb_buffer_t. - * @length: the new length of @buffer. + * @buffer: An #hb_buffer_t + * @length: The new length of @buffer * * Similar to hb_buffer_pre_allocate(), but clears any new items added at the * end. * - * Return value: - * %true if @buffer memory allocation succeeded, %false otherwise. + * Return value: + * `true` if @buffer memory allocation succeeded, `false` otherwise. * * Since: 0.9.2 **/ @@ -1315,17 +1456,17 @@ hb_bool_t hb_buffer_set_length (hb_buffer_t *buffer, unsigned int length) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return length == 0; - if (!buffer->ensure (length)) + if (unlikely (!buffer->ensure (length))) return false; /* Wipe the new space */ if (length > buffer->len) { - memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); + hb_memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); if (buffer->have_positions) - memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); + hb_memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); } buffer->len = length; @@ -1342,7 +1483,7 @@ hb_buffer_set_length (hb_buffer_t *buffer, /** * hb_buffer_get_length: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Returns the number of items in the buffer. * @@ -1353,15 +1494,15 @@ hb_buffer_set_length (hb_buffer_t *buffer, * Since: 0.9.2 **/ unsigned int -hb_buffer_get_length (hb_buffer_t *buffer) +hb_buffer_get_length (const hb_buffer_t *buffer) { return buffer->len; } /** * hb_buffer_get_glyph_infos: - * @buffer: an #hb_buffer_t. - * @length: (out): output array length. + * @buffer: An #hb_buffer_t + * @length: (out): The output-array length. * * Returns @buffer glyph information array. Returned pointer * is valid as long as @buffer contents are not modified. @@ -1374,7 +1515,7 @@ hb_buffer_get_length (hb_buffer_t *buffer) **/ hb_glyph_info_t * hb_buffer_get_glyph_infos (hb_buffer_t *buffer, - unsigned int *length) + unsigned int *length) { if (length) *length = buffer->len; @@ -1384,12 +1525,17 @@ hb_buffer_get_glyph_infos (hb_buffer_t *buffer, /** * hb_buffer_get_glyph_positions: - * @buffer: an #hb_buffer_t. - * @length: (out): output length. + * @buffer: An #hb_buffer_t + * @length: (out): The output length * * Returns @buffer glyph position array. Returned pointer * is valid as long as @buffer contents are not modified. * + * If buffer did not have positions before, the positions will be + * initialized to zeros, unless this function is called from + * within a buffer message callback (see hb_buffer_set_message_func()), + * in which case `NULL` is returned. + * * Return value: (transfer none) (array length=length): * The @buffer glyph position array. * The value valid as long as buffer has not been modified. @@ -1398,25 +1544,49 @@ hb_buffer_get_glyph_infos (hb_buffer_t *buffer, **/ hb_glyph_position_t * hb_buffer_get_glyph_positions (hb_buffer_t *buffer, - unsigned int *length) + unsigned int *length) { - if (!buffer->have_positions) - buffer->clear_positions (); - if (length) *length = buffer->len; + if (!buffer->have_positions) + { + if (unlikely (buffer->message_depth)) + return nullptr; + + buffer->clear_positions (); + } + return (hb_glyph_position_t *) buffer->pos; } +/** + * hb_buffer_has_positions: + * @buffer: an #hb_buffer_t. + * + * Returns whether @buffer has glyph position data. + * A buffer gains position data when hb_buffer_get_glyph_positions() is called on it, + * and cleared of position data when hb_buffer_clear_contents() is called. + * + * Return value: + * `true` if the @buffer has position array, `false` otherwise. + * + * Since: 2.7.3 + **/ +HB_EXTERN hb_bool_t +hb_buffer_has_positions (hb_buffer_t *buffer) +{ + return buffer->have_positions; +} + /** * hb_glyph_info_get_glyph_flags: - * @info: a #hb_glyph_info_t. + * @info: a #hb_glyph_info_t * * Returns glyph flags encoded within a #hb_glyph_info_t. * * Return value: - * The #hb_glyph_flags_t encoded within @info. + * The #hb_glyph_flags_t encoded within @info * * Since: 1.5.0 **/ @@ -1428,7 +1598,7 @@ hb_glyph_flags_t /** * hb_buffer_reverse: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Reverses buffer contents. * @@ -1442,11 +1612,11 @@ hb_buffer_reverse (hb_buffer_t *buffer) /** * hb_buffer_reverse_range: - * @buffer: an #hb_buffer_t. - * @start: start index. - * @end: end index. + * @buffer: An #hb_buffer_t + * @start: start index + * @end: end index * - * Reverses buffer contents between start to end. + * Reverses buffer contents between @start and @end. * * Since: 0.9.41 **/ @@ -1459,7 +1629,7 @@ hb_buffer_reverse_range (hb_buffer_t *buffer, /** * hb_buffer_reverse_clusters: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Reverses buffer clusters. That is, the buffer contents are * reversed, then each cluster (consecutive items having the @@ -1475,25 +1645,29 @@ hb_buffer_reverse_clusters (hb_buffer_t *buffer) /** * hb_buffer_guess_segment_properties: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Sets unset buffer segment properties based on buffer Unicode * contents. If buffer is not empty, it must have content type - * %HB_BUFFER_CONTENT_TYPE_UNICODE. + * #HB_BUFFER_CONTENT_TYPE_UNICODE. * - * If buffer script is not set (ie. is %HB_SCRIPT_INVALID), it + * If buffer script is not set (ie. is #HB_SCRIPT_INVALID), it * will be set to the Unicode script of the first character in - * the buffer that has a script other than %HB_SCRIPT_COMMON, - * %HB_SCRIPT_INHERITED, and %HB_SCRIPT_UNKNOWN. + * the buffer that has a script other than #HB_SCRIPT_COMMON, + * #HB_SCRIPT_INHERITED, and #HB_SCRIPT_UNKNOWN. * - * Next, if buffer direction is not set (ie. is %HB_DIRECTION_INVALID), + * Next, if buffer direction is not set (ie. is #HB_DIRECTION_INVALID), * it will be set to the natural horizontal direction of the * buffer script as returned by hb_script_get_horizontal_direction(). + * If hb_script_get_horizontal_direction() returns #HB_DIRECTION_INVALID, + * then #HB_DIRECTION_LTR is used. * - * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID), + * Finally, if buffer language is not set (ie. is #HB_LANGUAGE_INVALID), * it will be set to the process's default language as returned by * hb_language_get_default(). This may change in the future by * taking buffer script into consideration when choosing a language. + * Note that hb_language_get_default() is NOT threadsafe the first time + * it is called. See documentation for that function for details. * * Since: 0.9.7 **/ @@ -1514,10 +1688,9 @@ hb_buffer_add_utf (hb_buffer_t *buffer, typedef typename utf_t::codepoint_t T; const hb_codepoint_t replacement = buffer->replacement; - assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || - (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + buffer->assert_unicode (); - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; if (text_length == -1) @@ -1526,7 +1699,10 @@ hb_buffer_add_utf (hb_buffer_t *buffer, if (item_length == -1) item_length = text_length - item_offset; - buffer->ensure (buffer->len + item_length * sizeof (T) / 4); + if (unlikely (item_length < 0 || + item_length > INT_MAX / 8 || + !buffer->ensure (buffer->len + item_length * sizeof (T) / 4))) + return; /* If buffer is empty and pre-context provided, install it. * This check is written this way, to make sure people can @@ -1574,13 +1750,13 @@ hb_buffer_add_utf (hb_buffer_t *buffer, /** * hb_buffer_add_utf8: - * @buffer: an #hb_buffer_t. - * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8 + * @buffer: An #hb_buffer_t + * @text: (array length=text_length) (element-type uint8_t): An array of UTF-8 * characters to append. - * @text_length: the length of the @text, or -1 if it is %NULL terminated. - * @item_offset: the offset of the first character to add to the @buffer. - * @item_length: the number of characters to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated). + * @text_length: The length of the @text, or -1 if it is `NULL` terminated. + * @item_offset: The offset of the first character to add to the @buffer. + * @item_length: The number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is `NULL` terminated). * * See hb_buffer_add_codepoints(). * @@ -1601,12 +1777,12 @@ hb_buffer_add_utf8 (hb_buffer_t *buffer, /** * hb_buffer_add_utf16: - * @buffer: an #hb_buffer_t. - * @text: (array length=text_length): an array of UTF-16 characters to append. - * @text_length: the length of the @text, or -1 if it is %NULL terminated. - * @item_offset: the offset of the first character to add to the @buffer. - * @item_length: the number of characters to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated). + * @buffer: An #hb_buffer_t + * @text: (array length=text_length): An array of UTF-16 characters to append + * @text_length: The length of the @text, or -1 if it is `NULL` terminated + * @item_offset: The offset of the first character to add to the @buffer + * @item_length: The number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is `NULL` terminated) * * See hb_buffer_add_codepoints(). * @@ -1627,12 +1803,12 @@ hb_buffer_add_utf16 (hb_buffer_t *buffer, /** * hb_buffer_add_utf32: - * @buffer: an #hb_buffer_t. - * @text: (array length=text_length): an array of UTF-32 characters to append. - * @text_length: the length of the @text, or -1 if it is %NULL terminated. - * @item_offset: the offset of the first character to add to the @buffer. - * @item_length: the number of characters to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated). + * @buffer: An #hb_buffer_t + * @text: (array length=text_length): An array of UTF-32 characters to append + * @text_length: The length of the @text, or -1 if it is `NULL` terminated + * @item_offset: The offset of the first character to add to the @buffer + * @item_length: The number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is `NULL` terminated) * * See hb_buffer_add_codepoints(). * @@ -1648,18 +1824,18 @@ hb_buffer_add_utf32 (hb_buffer_t *buffer, unsigned int item_offset, int item_length) { - hb_buffer_add_utf > (buffer, text, text_length, item_offset, item_length); + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); } /** * hb_buffer_add_latin1: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8 - * characters to append. - * @text_length: the length of the @text, or -1 if it is %NULL terminated. - * @item_offset: the offset of the first character to add to the @buffer. + * characters to append + * @text_length: the length of the @text, or -1 if it is `NULL` terminated + * @item_offset: the offset of the first character to add to the @buffer * @item_length: the number of characters to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated). + * end of @text (assuming it is `NULL` terminated) * * Similar to hb_buffer_add_codepoints(), but allows only access to first 256 * Unicode code points that can fit in 8-bit strings. @@ -1682,10 +1858,10 @@ hb_buffer_add_latin1 (hb_buffer_t *buffer, * hb_buffer_add_codepoints: * @buffer: a #hb_buffer_t to append characters to. * @text: (array length=text_length): an array of Unicode code points to append. - * @text_length: the length of the @text, or -1 if it is %NULL terminated. + * @text_length: the length of the @text, or -1 if it is `NULL` terminated. * @item_offset: the offset of the first code point to add to the @buffer. * @item_length: the number of code points to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated). + * end of @text (assuming it is `NULL` terminated). * * Appends characters from @text array to @buffer. The @item_offset is the * position of the first character from @text that will be appended, and @@ -1698,7 +1874,9 @@ hb_buffer_add_latin1 (hb_buffer_t *buffer, * marks at stat of run. * * This function does not check the validity of @text, it is up to the caller - * to ensure it contains a valid Unicode code points. + * to ensure it contains a valid Unicode scalar values. In contrast, + * hb_buffer_add_utf32() can be used that takes similar input but performs + * sanity-check on the input. * * Since: 0.9.31 **/ @@ -1709,16 +1887,16 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer, unsigned int item_offset, int item_length) { - hb_buffer_add_utf > (buffer, text, text_length, item_offset, item_length); + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); } /** * hb_buffer_append: - * @buffer: an #hb_buffer_t. - * @source: source #hb_buffer_t. + * @buffer: An #hb_buffer_t + * @source: source #hb_buffer_t * @start: start index into source buffer to copy. Use 0 to copy from start of buffer. - * @end: end index into source buffer to copy. Use (unsigned int) -1 to copy to end of buffer. + * @end: end index into source buffer to copy. Use @HB_FEATURE_GLOBAL_END to copy to end of buffer. * * Append (part of) contents of another buffer to this buffer. * @@ -1726,7 +1904,7 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer, **/ HB_EXTERN void hb_buffer_append (hb_buffer_t *buffer, - hb_buffer_t *source, + const hb_buffer_t *source, unsigned int start, unsigned int end) { @@ -1743,25 +1921,49 @@ hb_buffer_append (hb_buffer_t *buffer, if (start == end) return; - if (!buffer->len) - buffer->content_type = source->content_type; - if (!buffer->have_positions && source->have_positions) - buffer->clear_positions (); - if (buffer->len + (end - start) < buffer->len) /* Overflows. */ { - buffer->in_error = true; + buffer->successful = false; return; } unsigned int orig_len = buffer->len; hb_buffer_set_length (buffer, buffer->len + (end - start)); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return; - memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0])); + if (!orig_len) + buffer->content_type = source->content_type; + if (!buffer->have_positions && source->have_positions) + buffer->clear_positions (); + + hb_segment_properties_overlay (&buffer->props, &source->props); + + hb_memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0])); if (buffer->have_positions) - memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0])); + hb_memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0])); + + if (source->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) + { + /* See similar logic in add_utf. */ + + /* pre-context */ + if (!orig_len && start + source->context_len[0] > 0) + { + buffer->clear_context (0); + while (start > 0 && buffer->context_len[0] < buffer->CONTEXT_LENGTH) + buffer->context[0][buffer->context_len[0]++] = source->info[--start].codepoint; + for (auto i = 0u; i < source->context_len[0] && buffer->context_len[0] < buffer->CONTEXT_LENGTH; i++) + buffer->context[0][buffer->context_len[0]++] = source->context[0][i]; + } + + /* post-context */ + buffer->clear_context (1); + while (end < source->len && buffer->context_len[1] < buffer->CONTEXT_LENGTH) + buffer->context[1][buffer->context_len[1]++] = source->info[end++].codepoint; + for (auto i = 0u; i < source->context_len[1] && buffer->context_len[1] < buffer->CONTEXT_LENGTH; i++) + buffer->context[1][buffer->context_len[1]++] = source->context[1][i]; + } } @@ -1822,7 +2024,7 @@ normalize_glyphs_cluster (hb_buffer_t *buffer, /** * hb_buffer_normalize_glyphs: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Reorders a glyph buffer to have canonical in-cluster glyph order / position. * The resulting clusters should behave identical to pre-reordering clusters. @@ -1835,23 +2037,13 @@ void hb_buffer_normalize_glyphs (hb_buffer_t *buffer) { assert (buffer->have_positions); - assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS || - (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + + buffer->assert_glyphs (); bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); - unsigned int count = buffer->len; - if (unlikely (!count)) return; - hb_glyph_info_t *info = buffer->info; - - unsigned int start = 0; - unsigned int end; - for (end = start + 1; end < count; end++) - if (info[start].cluster != info[end].cluster) { - normalize_glyphs_cluster (buffer, start, end, backward); - start = end; - } - normalize_glyphs_cluster (buffer, start, end, backward); + foreach_cluster (buffer, start, end) + normalize_glyphs_cluster (buffer, start, end, backward); } void @@ -1882,9 +2074,13 @@ hb_buffer_t::sort (unsigned int start, unsigned int end, int(*compar)(const hb_g /** * hb_buffer_diff: + * @buffer: a buffer. + * @reference: other buffer to compare to. + * @dottedcircle_glyph: glyph id of U+25CC DOTTED CIRCLE, or (hb_codepont_t) -1. + * @position_fuzz: allowed absolute difference in position values. * - * If dottedcircle_glyph is (hb_codepoint_t) -1 then %HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT - * and %HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned. This should be used by most + * If dottedcircle_glyph is (hb_codepoint_t) -1 then #HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT + * and #HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned. This should be used by most * callers if just comparing two buffers is needed. * * Since: 1.5.0 @@ -1914,9 +2110,9 @@ hb_buffer_diff (hb_buffer_t *buffer, for (i = 0; i < count; i++) { if (contains && info[i].codepoint == dottedcircle_glyph) - result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT; + result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT; if (contains && info[i].codepoint == 0) - result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT; + result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT; } result |= HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH; return hb_buffer_diff_flags_t (result); @@ -1933,7 +2129,7 @@ hb_buffer_diff (hb_buffer_t *buffer, result |= HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH; if (buf_info->cluster != ref_info->cluster) result |= HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH; - if ((buf_info->mask & HB_GLYPH_FLAG_DEFINED) != (ref_info->mask & HB_GLYPH_FLAG_DEFINED)) + if ((buf_info->mask ^ ref_info->mask) & HB_GLYPH_FLAG_DEFINED) result |= HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH; if (contains && ref_info->codepoint == dottedcircle_glyph) result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT; @@ -1951,12 +2147,12 @@ hb_buffer_diff (hb_buffer_t *buffer, for (unsigned int i = 0; i < count; i++) { if ((unsigned int) abs (buf_pos->x_advance - ref_pos->x_advance) > position_fuzz || - (unsigned int) abs (buf_pos->y_advance - ref_pos->y_advance) > position_fuzz || - (unsigned int) abs (buf_pos->x_offset - ref_pos->x_offset) > position_fuzz || - (unsigned int) abs (buf_pos->y_offset - ref_pos->y_offset) > position_fuzz) + (unsigned int) abs (buf_pos->y_advance - ref_pos->y_advance) > position_fuzz || + (unsigned int) abs (buf_pos->x_offset - ref_pos->x_offset) > position_fuzz || + (unsigned int) abs (buf_pos->y_offset - ref_pos->y_offset) > position_fuzz) { - result |= HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH; - break; + result |= HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH; + break; } buf_pos++; ref_pos++; @@ -1971,14 +2167,15 @@ hb_buffer_diff (hb_buffer_t *buffer, * Debugging. */ +#ifndef HB_NO_BUFFER_MESSAGE /** * hb_buffer_set_message_func: - * @buffer: an #hb_buffer_t. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @buffer: An #hb_buffer_t + * @func: (closure user_data) (destroy destroy) (scope notified): Callback function + * @user_data: (nullable): Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_buffer_message_func_t. * * Since: 1.1.3 **/ @@ -1987,6 +2184,13 @@ hb_buffer_set_message_func (hb_buffer_t *buffer, hb_buffer_message_func_t func, void *user_data, hb_destroy_func_t destroy) { + if (unlikely (hb_object_is_immutable (buffer))) + { + if (destroy) + destroy (user_data); + return; + } + if (buffer->message_destroy) buffer->message_destroy (buffer->message_data); @@ -2000,11 +2204,19 @@ hb_buffer_set_message_func (hb_buffer_t *buffer, buffer->message_destroy = nullptr; } } - bool hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap) { + assert (!have_output || (out_info == info && out_len == idx)); + + message_depth++; + char buf[100]; - vsnprintf (buf, sizeof (buf), fmt, ap); - return (bool) this->message_func (this, font, buf, this->message_data); + vsnprintf (buf, sizeof (buf), fmt, ap); + bool ret = (bool) this->message_func (this, font, buf, this->message_data); + + message_depth--; + + return ret; } +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer.h b/src/3rdparty/harfbuzz-ng/src/hb-buffer.h index a8a4b84e97b..bff78543c87 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-buffer.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer.h @@ -27,7 +27,7 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_H_IN +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif @@ -44,7 +44,6 @@ HB_BEGIN_DECLS * hb_glyph_info_t: * @codepoint: either a Unicode code point (before shaping) or a glyph index * (after shaping). - * @mask: * @cluster: the index of the character in the original text that corresponds * to this #hb_glyph_info_t, or whatever the client passes to * hb_buffer_add(). More than one #hb_glyph_info_t can have the same @@ -59,11 +58,12 @@ HB_BEGIN_DECLS * * The #hb_glyph_info_t is the structure that holds information about the * glyphs and their relation to input text. - * */ typedef struct hb_glyph_info_t { hb_codepoint_t codepoint; - hb_mask_t mask; /* Holds hb_glyph_flags_t after hb_shape(), plus other things. */ + /*< private >*/ + hb_mask_t mask; + /*< public >*/ uint32_t cluster; /*< private >*/ @@ -71,10 +71,98 @@ typedef struct hb_glyph_info_t { hb_var_int_t var2; } hb_glyph_info_t; +/** + * hb_glyph_flags_t: + * @HB_GLYPH_FLAG_UNSAFE_TO_BREAK: Indicates that if input text is broken at the + * beginning of the cluster this glyph is part of, + * then both sides need to be re-shaped, as the + * result might be different. + * On the flip side, it means that when this + * flag is not present, then it is safe to break + * the glyph-run at the beginning of this + * cluster, and the two sides will represent the + * exact same result one would get if breaking + * input text at the beginning of this cluster + * and shaping the two sides separately. + * This can be used to optimize paragraph + * layout, by avoiding re-shaping of each line + * after line-breaking. + * @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT: Indicates that if input text is changed on one + * side of the beginning of the cluster this glyph + * is part of, then the shaping results for the + * other side might change. + * Note that the absence of this flag will NOT by + * itself mean that it IS safe to concat text. + * Only two pieces of text both of which clear of + * this flag can be concatenated safely. + * This can be used to optimize paragraph + * layout, by avoiding re-shaping of each line + * after line-breaking, by limiting the + * reshaping to a small piece around the + * breaking positin only, even if the breaking + * position carries the + * #HB_GLYPH_FLAG_UNSAFE_TO_BREAK or when + * hyphenation or other text transformation + * happens at line-break position, in the following + * way: + * 1. Iterate back from the line-break position + * until the first cluster start position that is + * NOT unsafe-to-concat, 2. shape the segment from + * there till the end of line, 3. check whether the + * resulting glyph-run also is clear of the + * unsafe-to-concat at its start-of-text position; + * if it is, just splice it into place and the line + * is shaped; If not, move on to a position further + * back that is clear of unsafe-to-concat and retry + * from there, and repeat. + * At the start of next line a similar algorithm can + * be implemented. That is: 1. Iterate forward from + * the line-break position until the first cluster + * start position that is NOT unsafe-to-concat, 2. + * shape the segment from beginning of the line to + * that position, 3. check whether the resulting + * glyph-run also is clear of the unsafe-to-concat + * at its end-of-text position; if it is, just splice + * it into place and the beginning is shaped; If not, + * move on to a position further forward that is clear + * of unsafe-to-concat and retry up to there, and repeat. + * A slight complication will arise in the + * implementation of the algorithm above, + * because while our buffer API has a way to + * return flags for position corresponding to + * start-of-text, there is currently no position + * corresponding to end-of-text. This limitation + * can be alleviated by shaping more text than needed + * and looking for unsafe-to-concat flag within text + * clusters. + * The #HB_GLYPH_FLAG_UNSAFE_TO_BREAK flag will + * always imply this flag. + * To use this flag, you must enable the buffer flag + * @HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT during + * shaping, otherwise the buffer flag will not be + * reliably produced. + * Since: 4.0.0 + * @HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL: In scripts that use elongation (Arabic, + Mongolian, Syriac, etc.), this flag signifies + that it is safe to insert a U+0640 TATWEEL + character before this cluster for elongation. + This flag does not determine the + script-specific elongation places, but only + when it is safe to do the elongation without + interrupting text shaping. + Since: 5.1.0 + * @HB_GLYPH_FLAG_DEFINED: All the currently defined flags. + * + * Flags for #hb_glyph_info_t. + * + * Since: 1.5.0 + */ typedef enum { /*< flags >*/ - HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, + HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, + HB_GLYPH_FLAG_UNSAFE_TO_CONCAT = 0x00000002, + HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL = 0x00000004, - HB_GLYPH_FLAG_DEFINED = 0x00000001 /* OR of all defined flags */ + HB_GLYPH_FLAG_DEFINED = 0x00000007 /* OR of all defined flags */ } hb_glyph_flags_t; HB_EXTERN hb_glyph_flags_t @@ -129,6 +217,11 @@ typedef struct hb_segment_properties_t { void *reserved2; } hb_segment_properties_t; +/** + * HB_SEGMENT_PROPERTIES_DEFAULT: + * + * The default #hb_segment_properties_t of of freshly created #hb_buffer_t. + */ #define HB_SEGMENT_PROPERTIES_DEFAULT {HB_DIRECTION_INVALID, \ HB_SCRIPT_INVALID, \ HB_LANGUAGE_INVALID, \ @@ -142,6 +235,9 @@ hb_segment_properties_equal (const hb_segment_properties_t *a, HB_EXTERN unsigned int hb_segment_properties_hash (const hb_segment_properties_t *p); +HB_EXTERN void +hb_segment_properties_overlay (hb_segment_properties_t *p, + const hb_segment_properties_t *src); /** @@ -156,6 +252,13 @@ typedef struct hb_buffer_t hb_buffer_t; HB_EXTERN hb_buffer_t * hb_buffer_create (void); +HB_EXTERN hb_buffer_t * +hb_buffer_create_similar (const hb_buffer_t *src); + +HB_EXTERN void +hb_buffer_reset (hb_buffer_t *buffer); + + HB_EXTERN hb_buffer_t * hb_buffer_get_empty (void); @@ -173,7 +276,7 @@ hb_buffer_set_user_data (hb_buffer_t *buffer, hb_bool_t replace); HB_EXTERN void * -hb_buffer_get_user_data (hb_buffer_t *buffer, +hb_buffer_get_user_data (const hb_buffer_t *buffer, hb_user_data_key_t *key); @@ -182,6 +285,8 @@ hb_buffer_get_user_data (hb_buffer_t *buffer, * @HB_BUFFER_CONTENT_TYPE_INVALID: Initial value for new buffer. * @HB_BUFFER_CONTENT_TYPE_UNICODE: The buffer contains input characters (before shaping). * @HB_BUFFER_CONTENT_TYPE_GLYPHS: The buffer contains output glyphs (after shaping). + * + * The type of #hb_buffer_t contents. */ typedef enum { HB_BUFFER_CONTENT_TYPE_INVALID = 0, @@ -194,7 +299,7 @@ hb_buffer_set_content_type (hb_buffer_t *buffer, hb_buffer_content_type_t content_type); HB_EXTERN hb_buffer_content_type_t -hb_buffer_get_content_type (hb_buffer_t *buffer); +hb_buffer_get_content_type (const hb_buffer_t *buffer); HB_EXTERN void @@ -202,21 +307,21 @@ hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, hb_unicode_funcs_t *unicode_funcs); HB_EXTERN hb_unicode_funcs_t * -hb_buffer_get_unicode_funcs (hb_buffer_t *buffer); +hb_buffer_get_unicode_funcs (const hb_buffer_t *buffer); HB_EXTERN void hb_buffer_set_direction (hb_buffer_t *buffer, hb_direction_t direction); HB_EXTERN hb_direction_t -hb_buffer_get_direction (hb_buffer_t *buffer); +hb_buffer_get_direction (const hb_buffer_t *buffer); HB_EXTERN void hb_buffer_set_script (hb_buffer_t *buffer, hb_script_t script); HB_EXTERN hb_script_t -hb_buffer_get_script (hb_buffer_t *buffer); +hb_buffer_get_script (const hb_buffer_t *buffer); HB_EXTERN void hb_buffer_set_language (hb_buffer_t *buffer, @@ -224,14 +329,14 @@ hb_buffer_set_language (hb_buffer_t *buffer, HB_EXTERN hb_language_t -hb_buffer_get_language (hb_buffer_t *buffer); +hb_buffer_get_language (const hb_buffer_t *buffer); HB_EXTERN void hb_buffer_set_segment_properties (hb_buffer_t *buffer, const hb_segment_properties_t *props); HB_EXTERN void -hb_buffer_get_segment_properties (hb_buffer_t *buffer, +hb_buffer_get_segment_properties (const hb_buffer_t *buffer, hb_segment_properties_t *props); HB_EXTERN void @@ -247,13 +352,44 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer); * of the text without the full context. * @HB_BUFFER_FLAG_EOT: flag indicating that special handling of the end of text * paragraph can be applied to this buffer, similar to - * @HB_BUFFER_FLAG_EOT. + * @HB_BUFFER_FLAG_BOT. * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES: * flag indication that character with Default_Ignorable * Unicode property should use the corresponding glyph - * from the font, instead of hiding them (currently done - * by replacing them with the space glyph and zeroing the - * advance width.) + * from the font, instead of hiding them (done by + * replacing them with the space glyph and zeroing the + * advance width.) This flag takes precedence over + * @HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES. + * @HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES: + * flag indication that character with Default_Ignorable + * Unicode property should be removed from glyph string + * instead of hiding them (done by replacing them with the + * space glyph and zeroing the advance width.) + * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES takes + * precedence over this flag. Since: 1.8.0 + * @HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE: + * flag indicating that a dotted circle should + * not be inserted in the rendering of incorrect + * character sequences (such at <0905 093E>). Since: 2.4.0 + * @HB_BUFFER_FLAG_VERIFY: + * flag indicating that the hb_shape() call and its variants + * should perform various verification processes on the results + * of the shaping operation on the buffer. If the verification + * fails, then either a buffer message is sent, if a message + * handler is installed on the buffer, or a message is written + * to standard error. In either case, the shaping result might + * be modified to show the failed output. Since: 3.4.0 + * @HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT: + * flag indicating that the @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT + * glyph-flag should be produced by the shaper. By default + * it will not be produced since it incurs a cost. Since: 4.0.0 + * @HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL: + * flag indicating that the @HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL + * glyph-flag should be produced by the shaper. By default + * it will not be produced. Since: 5.1.0 + * @HB_BUFFER_FLAG_DEFINED: All currently defined flags: Since: 4.4.0 + * + * Flags for #hb_buffer_t. * * Since: 0.9.20 */ @@ -261,7 +397,14 @@ typedef enum { /*< flags >*/ HB_BUFFER_FLAG_DEFAULT = 0x00000000u, HB_BUFFER_FLAG_BOT = 0x00000001u, /* Beginning-of-text */ HB_BUFFER_FLAG_EOT = 0x00000002u, /* End-of-text */ - HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES = 0x00000004u + HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES = 0x00000004u, + HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES = 0x00000008u, + HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE = 0x00000010u, + HB_BUFFER_FLAG_VERIFY = 0x00000020u, + HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT = 0x00000040u, + HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL = 0x00000080u, + + HB_BUFFER_FLAG_DEFINED = 0x000000FFu } hb_buffer_flags_t; HB_EXTERN void @@ -269,9 +412,34 @@ hb_buffer_set_flags (hb_buffer_t *buffer, hb_buffer_flags_t flags); HB_EXTERN hb_buffer_flags_t -hb_buffer_get_flags (hb_buffer_t *buffer); +hb_buffer_get_flags (const hb_buffer_t *buffer); -/* +/** + * hb_buffer_cluster_level_t: + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES: Return cluster values grouped by graphemes into + * monotone order. + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS: Return cluster values grouped into monotone order. + * @HB_BUFFER_CLUSTER_LEVEL_CHARACTERS: Don't group cluster values. + * @HB_BUFFER_CLUSTER_LEVEL_DEFAULT: Default cluster level, + * equal to @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES. + * + * Data type for holding HarfBuzz's clustering behavior options. The cluster level + * dictates one aspect of how HarfBuzz will treat non-base characters + * during shaping. + * + * In @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES, non-base + * characters are merged into the cluster of the base character that precedes them. + * + * In @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS, non-base characters are initially + * assigned their own cluster values, which are not merged into preceding base + * clusters. This allows HarfBuzz to perform additional operations like reorder + * sequences of adjacent marks. + * + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES is the default, because it maintains + * backward compatibility with older versions of HarfBuzz. New client programs that + * do not need to maintain such backward compatibility are recommended to use + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS instead of the default. + * * Since: 0.9.42 */ typedef enum { @@ -286,7 +454,7 @@ hb_buffer_set_cluster_level (hb_buffer_t *buffer, hb_buffer_cluster_level_t cluster_level); HB_EXTERN hb_buffer_cluster_level_t -hb_buffer_get_cluster_level (hb_buffer_t *buffer); +hb_buffer_get_cluster_level (const hb_buffer_t *buffer); /** * HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT: @@ -303,18 +471,33 @@ hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, hb_codepoint_t replacement); HB_EXTERN hb_codepoint_t -hb_buffer_get_replacement_codepoint (hb_buffer_t *buffer); - +hb_buffer_get_replacement_codepoint (const hb_buffer_t *buffer); HB_EXTERN void -hb_buffer_reset (hb_buffer_t *buffer); +hb_buffer_set_invisible_glyph (hb_buffer_t *buffer, + hb_codepoint_t invisible); + +HB_EXTERN hb_codepoint_t +hb_buffer_get_invisible_glyph (const hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_not_found_glyph (hb_buffer_t *buffer, + hb_codepoint_t not_found); + +HB_EXTERN hb_codepoint_t +hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer); + + +/* + * Content API. + */ HB_EXTERN void hb_buffer_clear_contents (hb_buffer_t *buffer); HB_EXTERN hb_bool_t hb_buffer_pre_allocate (hb_buffer_t *buffer, - unsigned int size); + unsigned int size); HB_EXTERN hb_bool_t @@ -375,7 +558,7 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer, HB_EXTERN void hb_buffer_append (hb_buffer_t *buffer, - hb_buffer_t *source, + const hb_buffer_t *source, unsigned int start, unsigned int end); @@ -384,17 +567,20 @@ hb_buffer_set_length (hb_buffer_t *buffer, unsigned int length); HB_EXTERN unsigned int -hb_buffer_get_length (hb_buffer_t *buffer); +hb_buffer_get_length (const hb_buffer_t *buffer); /* Getting glyphs out of the buffer */ HB_EXTERN hb_glyph_info_t * hb_buffer_get_glyph_infos (hb_buffer_t *buffer, - unsigned int *length); + unsigned int *length); HB_EXTERN hb_glyph_position_t * hb_buffer_get_glyph_positions (hb_buffer_t *buffer, - unsigned int *length); + unsigned int *length); + +HB_EXTERN hb_bool_t +hb_buffer_has_positions (hb_buffer_t *buffer); HB_EXTERN void @@ -412,6 +598,10 @@ hb_buffer_normalize_glyphs (hb_buffer_t *buffer); * @HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS: do not serialize glyph position information. * @HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES: do no serialize glyph name. * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS: serialize glyph extents. + * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS: serialize glyph flags. Since: 1.5.0 + * @HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES: do not serialize glyph advances, + * glyph offsets will reflect absolute glyph positions. Since: 1.8.0 + * @HB_BUFFER_SERIALIZE_FLAG_DEFINED: All currently defined flags. Since: 4.4.0 * * Flags that control what glyph information are serialized in hb_buffer_serialize_glyphs(). * @@ -423,7 +613,10 @@ typedef enum { /*< flags >*/ HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS = 0x00000002u, HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES = 0x00000004u, HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS = 0x00000008u, - HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS = 0x00000010u + HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS = 0x00000010u, + HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES = 0x00000020u, + + HB_BUFFER_SERIALIZE_FLAG_DEFINED = 0x0000003Fu } hb_buffer_serialize_flags_t; /** @@ -463,6 +656,27 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, hb_buffer_serialize_format_t format, hb_buffer_serialize_flags_t flags); +HB_EXTERN unsigned int +hb_buffer_serialize_unicode (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags); + +HB_EXTERN unsigned int +hb_buffer_serialize (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags); + HB_EXTERN hb_bool_t hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, const char *buf, @@ -471,11 +685,48 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, hb_font_t *font, hb_buffer_serialize_format_t format); +HB_EXTERN hb_bool_t +hb_buffer_deserialize_unicode (hb_buffer_t *buffer, + const char *buf, + int buf_len, + const char **end_ptr, + hb_buffer_serialize_format_t format); + + /* * Compare buffers */ +/** + * hb_buffer_diff_flags_t: + * @HB_BUFFER_DIFF_FLAG_EQUAL: equal buffers. + * @HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH: buffers with different + * #hb_buffer_content_type_t. + * @HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH: buffers with differing length. + * @HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT: `.notdef` glyph is present in the + * reference buffer. + * @HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT: dotted circle glyph is present + * in the reference buffer. + * @HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH: difference in #hb_glyph_info_t.codepoint + * @HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH: difference in #hb_glyph_info_t.cluster + * @HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH: difference in #hb_glyph_flags_t. + * @HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH: difference in #hb_glyph_position_t. + * + * Flags from comparing two #hb_buffer_t's. + * + * Buffer with different #hb_buffer_content_type_t cannot be meaningfully + * compared in any further detail. + * + * For buffers with differing length, the per-glyph comparison is not + * attempted, though we do still scan reference buffer for dotted circle and + * `.notdef` glyphs. + * + * If the buffers have the same length, we compare them glyph-by-glyph and + * report which aspect(s) of the glyph info/position are different. + * + * Since: 1.5.0 + */ typedef enum { /*< flags >*/ HB_BUFFER_DIFF_FLAG_EQUAL = 0x0000, @@ -512,9 +763,26 @@ hb_buffer_diff (hb_buffer_t *buffer, /* - * Debugging. + * Tracing. */ +/** + * hb_buffer_message_func_t: + * @buffer: An #hb_buffer_t to work upon + * @font: The #hb_font_t the @buffer is shaped with + * @message: `NULL`-terminated message passed to the function + * @user_data: User data pointer passed by the caller + * + * A callback method for #hb_buffer_t. The method gets called with the + * #hb_buffer_t it was set on, the #hb_font_t the buffer is shaped with and a + * message describing what step of the shaping process will be performed. + * Returning `false` from this method will skip this shaping step and move to + * the next one. + * + * Return value: `true` to perform the shaping step, `false` to skip it. + * + * Since: 1.1.3 + */ typedef hb_bool_t (*hb_buffer_message_func_t) (hb_buffer_t *buffer, hb_font_t *font, const char *message, diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer.hh b/src/3rdparty/harfbuzz-ng/src/hb-buffer.hh new file mode 100644 index 00000000000..5a43cabcb75 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer.hh @@ -0,0 +1,668 @@ +/* + * Copyright ยฉ 1998-2004 David Turner and Werner Lemberg + * Copyright ยฉ 2004,2007,2009,2010 Red Hat, Inc. + * Copyright ยฉ 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_HH +#define HB_BUFFER_HH + +#include "hb.hh" +#include "hb-unicode.hh" +#include "hb-set-digest.hh" + + +static_assert ((sizeof (hb_glyph_info_t) == 20), ""); +static_assert ((sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)), ""); + +HB_MARK_AS_FLAG_T (hb_glyph_flags_t); +HB_MARK_AS_FLAG_T (hb_buffer_flags_t); +HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t); +HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t); + +enum hb_buffer_scratch_flags_t { + HB_BUFFER_SCRATCH_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII = 0x00000001u, + HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES = 0x00000002u, + HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK = 0x00000004u, + HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT = 0x00000008u, + HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000010u, + HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS = 0x00000020u, + HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE = 0x00000040u, + + /* Reserved for shapers' internal use. */ + HB_BUFFER_SCRATCH_FLAG_SHAPER0 = 0x01000000u, + HB_BUFFER_SCRATCH_FLAG_SHAPER1 = 0x02000000u, + HB_BUFFER_SCRATCH_FLAG_SHAPER2 = 0x04000000u, + HB_BUFFER_SCRATCH_FLAG_SHAPER3 = 0x08000000u, +}; +HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t); + + +/* + * hb_buffer_t + */ + +struct hb_buffer_t +{ + hb_object_header_t header; + + /* + * Information about how the text in the buffer should be treated. + */ + + hb_unicode_funcs_t *unicode; /* Unicode functions */ + hb_buffer_flags_t flags; /* BOT / EOT / etc. */ + hb_buffer_cluster_level_t cluster_level; + hb_codepoint_t replacement; /* U+FFFD or something else. */ + hb_codepoint_t invisible; /* 0 or something else. */ + hb_codepoint_t not_found; /* 0 or something else. */ + + /* + * Buffer contents + */ + + hb_buffer_content_type_t content_type; + hb_segment_properties_t props; /* Script, language, direction */ + + bool successful; /* Allocations successful */ + bool shaping_failed; /* Shaping failure */ + bool have_output; /* Whether we have an output buffer going on */ + bool have_positions; /* Whether we have positions */ + + unsigned int idx; /* Cursor into ->info and ->pos arrays */ + unsigned int len; /* Length of ->info and ->pos arrays */ + unsigned int out_len; /* Length of ->out_info array if have_output */ + + unsigned int allocated; /* Length of allocated arrays */ + hb_glyph_info_t *info; + hb_glyph_info_t *out_info; + hb_glyph_position_t *pos; + + /* Text before / after the main buffer contents. + * Always in Unicode, and ordered outward. + * Index 0 is for "pre-context", 1 for "post-context". */ + static constexpr unsigned CONTEXT_LENGTH = 5u; + hb_codepoint_t context[2][CONTEXT_LENGTH]; + unsigned int context_len[2]; + + + /* + * Managed by enter / leave + */ + + uint8_t allocated_var_bits; + uint8_t serial; + hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */ + unsigned int max_len; /* Maximum allowed len. */ + int max_ops; /* Maximum allowed operations. */ + /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */ + + + /* + * Messaging callback + */ + +#ifndef HB_NO_BUFFER_MESSAGE + hb_buffer_message_func_t message_func; + void *message_data; + hb_destroy_func_t message_destroy; + unsigned message_depth; /* How deeply are we inside a message callback? */ +#else + static constexpr unsigned message_depth = 0u; +#endif + + + + /* Methods */ + + HB_NODISCARD bool in_error () const { return !successful; } + + void allocate_var (unsigned int start, unsigned int count) + { + unsigned int end = start + count; + assert (end <= 8); + unsigned int bits = (1u< (info, len).reverse (start, end); + if (have_positions) + hb_array_t (pos, len).reverse (start, end); + } + void reverse () { reverse_range (0, len); } + + template + void reverse_groups (const FuncType& group, + bool merge_clusters = false) + { + if (unlikely (!len)) + return; + + unsigned start = 0; + unsigned i; + for (i = 1; i < len; i++) + { + if (!group (info[i - 1], info[i])) + { + if (merge_clusters) + this->merge_clusters (start, i); + reverse_range (start, i); + start = i; + } + } + if (merge_clusters) + this->merge_clusters (start, i); + reverse_range (start, i); + + reverse (); + } + + template + unsigned group_end (unsigned start, const FuncType& group) const + { + while (++start < len && group (info[start - 1], info[start])) + ; + + return start; + } + + static bool _cluster_group_func (const hb_glyph_info_t& a, + const hb_glyph_info_t& b) + { return a.cluster == b.cluster; } + + void reverse_clusters () { reverse_groups (_cluster_group_func); } + + HB_INTERNAL void guess_segment_properties (); + + HB_INTERNAL bool sync (); + HB_INTERNAL int sync_so_far (); + HB_INTERNAL void clear_output (); + HB_INTERNAL void clear_positions (); + + template + HB_NODISCARD bool replace_glyphs (unsigned int num_in, + unsigned int num_out, + const T *glyph_data) + { + if (unlikely (!make_room_for (num_in, num_out))) return false; + + assert (idx + num_in <= len); + + merge_clusters (idx, idx + num_in); + + hb_glyph_info_t &orig_info = idx < len ? cur() : prev(); + + hb_glyph_info_t *pinfo = &out_info[out_len]; + for (unsigned int i = 0; i < num_out; i++) + { + *pinfo = orig_info; + pinfo->codepoint = glyph_data[i]; + pinfo++; + } + + idx += num_in; + out_len += num_out; + return true; + } + + HB_NODISCARD bool replace_glyph (hb_codepoint_t glyph_index) + { return replace_glyphs (1, 1, &glyph_index); } + + /* Makes a copy of the glyph at idx to output and replace glyph_index */ + HB_NODISCARD bool output_glyph (hb_codepoint_t glyph_index) + { return replace_glyphs (0, 1, &glyph_index); } + + HB_NODISCARD bool output_info (const hb_glyph_info_t &glyph_info) + { + if (unlikely (!make_room_for (0, 1))) return false; + + out_info[out_len] = glyph_info; + + out_len++; + return true; + } + /* Copies glyph at idx to output but doesn't advance idx */ + HB_NODISCARD bool copy_glyph () + { + /* Extra copy because cur()'s return can be freed within + * output_info() call if buffer reallocates. */ + return output_info (hb_glyph_info_t (cur())); + } + + /* Copies glyph at idx to output and advance idx. + * If there's no output, just advance idx. */ + HB_NODISCARD bool next_glyph () + { + if (have_output) + { + if (out_info != info || out_len != idx) + { + if (unlikely (!make_room_for (1, 1))) return false; + out_info[out_len] = info[idx]; + } + out_len++; + } + + idx++; + return true; + } + /* Copies n glyphs at idx to output and advance idx. + * If there's no output, just advance idx. */ + HB_NODISCARD bool next_glyphs (unsigned int n) + { + if (have_output) + { + if (out_info != info || out_len != idx) + { + if (unlikely (!make_room_for (n, n))) return false; + memmove (out_info + out_len, info + idx, n * sizeof (out_info[0])); + } + out_len += n; + } + + idx += n; + return true; + } + /* Advance idx without copying to output. */ + void skip_glyph () { idx++; } + void reset_masks (hb_mask_t mask) + { + for (unsigned int j = 0; j < len; j++) + info[j].mask = mask; + } + void add_masks (hb_mask_t mask) + { + for (unsigned int j = 0; j < len; j++) + info[j].mask |= mask; + } + HB_INTERNAL void set_masks (hb_mask_t value, hb_mask_t mask, + unsigned int cluster_start, unsigned int cluster_end); + + void merge_clusters (unsigned int start, unsigned int end) + { + if (end - start < 2) + return; + merge_clusters_impl (start, end); + } + HB_INTERNAL void merge_clusters_impl (unsigned int start, unsigned int end); + HB_INTERNAL void merge_out_clusters (unsigned int start, unsigned int end); + /* Merge clusters for deleting current glyph, and skip it. */ + HB_INTERNAL void delete_glyph (); + HB_INTERNAL void delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info)); + + + + /* Adds glyph flags in mask to infos with clusters between start and end. + * The start index will be from out-buffer if from_out_buffer is true. + * If interior is true, then the cluster having the minimum value is skipped. */ + void _set_glyph_flags (hb_mask_t mask, + unsigned start = 0, + unsigned end = (unsigned) -1, + bool interior = false, + bool from_out_buffer = false) + { + end = hb_min (end, len); + + if (interior && !from_out_buffer && end - start < 2) + return; + + scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; + + if (!from_out_buffer || !have_output) + { + if (!interior) + { + for (unsigned i = start; i < end; i++) + info[i].mask |= mask; + } + else + { + unsigned cluster = _infos_find_min_cluster (info, start, end); + _infos_set_glyph_flags (info, start, end, cluster, mask); + } + } + else + { + assert (start <= out_len); + assert (idx <= end); + + if (!interior) + { + for (unsigned i = start; i < out_len; i++) + out_info[i].mask |= mask; + for (unsigned i = idx; i < end; i++) + info[i].mask |= mask; + } + else + { + unsigned cluster = _infos_find_min_cluster (info, idx, end); + cluster = _infos_find_min_cluster (out_info, start, out_len, cluster); + + _infos_set_glyph_flags (out_info, start, out_len, cluster, mask); + _infos_set_glyph_flags (info, idx, end, cluster, mask); + } + } + } + + void unsafe_to_break (unsigned int start = 0, unsigned int end = -1) + { + _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, + start, end, + true); + } + void safe_to_insert_tatweel (unsigned int start = 0, unsigned int end = -1) + { + if ((flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL) == 0) + { + unsafe_to_break (start, end); + return; + } + _set_glyph_flags (HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL, + start, end, + true); + } + void unsafe_to_concat (unsigned int start = 0, unsigned int end = -1) + { + if (likely ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0)) + return; + _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, + start, end, + true); + } + void unsafe_to_break_from_outbuffer (unsigned int start = 0, unsigned int end = -1) + { + _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, + start, end, + true, true); + } + void unsafe_to_concat_from_outbuffer (unsigned int start = 0, unsigned int end = -1) + { + if (likely ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0)) + return; + _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, + start, end, + false, true); + } + + + /* Internal methods */ + HB_NODISCARD HB_INTERNAL bool move_to (unsigned int i); /* i is output-buffer index. */ + + HB_NODISCARD HB_INTERNAL bool enlarge (unsigned int size); + + HB_NODISCARD bool ensure (unsigned int size) + { return likely (!size || size < allocated) ? true : enlarge (size); } + + HB_NODISCARD bool ensure_inplace (unsigned int size) + { return likely (!size || size < allocated); } + + void assert_glyphs () + { + assert ((content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS) || + (!len && (content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); + } + void assert_unicode () + { + assert ((content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) || + (!len && (content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); + } + HB_NODISCARD bool ensure_glyphs () + { + if (unlikely (content_type != HB_BUFFER_CONTENT_TYPE_GLYPHS)) + { + if (content_type != HB_BUFFER_CONTENT_TYPE_INVALID) + return false; + assert (len == 0); + content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; + } + return true; + } + HB_NODISCARD bool ensure_unicode () + { + if (unlikely (content_type != HB_BUFFER_CONTENT_TYPE_UNICODE)) + { + if (content_type != HB_BUFFER_CONTENT_TYPE_INVALID) + return false; + assert (len == 0); + content_type = HB_BUFFER_CONTENT_TYPE_UNICODE; + } + return true; + } + + HB_NODISCARD HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out); + HB_NODISCARD HB_INTERNAL bool shift_forward (unsigned int count); + + typedef long scratch_buffer_t; + HB_INTERNAL scratch_buffer_t *get_scratch_buffer (unsigned int *size); + + void clear_context (unsigned int side) { context_len[side] = 0; } + + HB_INTERNAL void sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)); + + bool messaging () + { +#ifdef HB_NO_BUFFER_MESSAGE + return false; +#else + return unlikely (message_func); +#endif + } + bool message (hb_font_t *font, const char *fmt, ...) HB_PRINTF_FUNC(3, 4) + { +#ifdef HB_NO_BUFFER_MESSAGE + return true; +#else + if (likely (!messaging ())) + return true; + + va_list ap; + va_start (ap, fmt); + bool ret = message_impl (font, fmt, ap); + va_end (ap); + + return ret; +#endif + } + HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0); + + static void + set_cluster (hb_glyph_info_t &inf, unsigned int cluster, unsigned int mask = 0) + { + if (inf.cluster != cluster) + inf.mask = (inf.mask & ~HB_GLYPH_FLAG_DEFINED) | (mask & HB_GLYPH_FLAG_DEFINED); + inf.cluster = cluster; + } + void + _infos_set_glyph_flags (hb_glyph_info_t *infos, + unsigned int start, unsigned int end, + unsigned int cluster, + hb_mask_t mask) + { + if (unlikely (start == end)) + return; + + unsigned cluster_first = infos[start].cluster; + unsigned cluster_last = infos[end - 1].cluster; + + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS || + (cluster != cluster_first && cluster != cluster_last)) + { + for (unsigned int i = start; i < end; i++) + if (cluster != infos[i].cluster) + { + scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; + infos[i].mask |= mask; + } + return; + } + + /* Monotone clusters */ + + if (cluster == cluster_first) + { + for (unsigned int i = end; start < i && infos[i - 1].cluster != cluster_first; i--) + { + scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; + infos[i - 1].mask |= mask; + } + } + else /* cluster == cluster_last */ + { + for (unsigned int i = start; i < end && infos[i].cluster != cluster_last; i++) + { + scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; + infos[i].mask |= mask; + } + } + } + unsigned + _infos_find_min_cluster (const hb_glyph_info_t *infos, + unsigned start, unsigned end, + unsigned cluster = UINT_MAX) + { + if (unlikely (start == end)) + return cluster; + + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + { + for (unsigned int i = start; i < end; i++) + cluster = hb_min (cluster, infos[i].cluster); + return cluster; + } + + return hb_min (cluster, hb_min (infos[start].cluster, infos[end - 1].cluster)); + } + + void clear_glyph_flags (hb_mask_t mask = 0) + { + for (unsigned int i = 0; i < len; i++) + info[i].mask = (info[i].mask & ~HB_GLYPH_FLAG_DEFINED) | (mask & HB_GLYPH_FLAG_DEFINED); + } +}; +DECLARE_NULL_INSTANCE (hb_buffer_t); + + +#define foreach_group(buffer, start, end, group_func) \ + for (unsigned int \ + _count = buffer->len, \ + start = 0, end = _count ? buffer->group_end (0, group_func) : 0; \ + start < _count; \ + start = end, end = buffer->group_end (start, group_func)) + +#define foreach_cluster(buffer, start, end) \ + foreach_group (buffer, start, end, hb_buffer_t::_cluster_group_func) + + +#define HB_BUFFER_XALLOCATE_VAR(b, func, var) \ + b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \ + sizeof (b->info[0].var)) +#define HB_BUFFER_ALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, allocate_var, var ()) +#define HB_BUFFER_TRY_ALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, try_allocate_var, var ()) +#define HB_BUFFER_DEALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var ()) +#define HB_BUFFER_ASSERT_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, assert_var, var ()) + + +#endif /* HB_BUFFER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cache-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-cache-private.hh deleted file mode 100644 index 24957e1e96f..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-cache-private.hh +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright ยฉ 2012 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_CACHE_PRIVATE_HH -#define HB_CACHE_PRIVATE_HH - -#include "hb-private.hh" - - -/* Implements a lock-free cache for int->int functions. */ - -template -struct hb_cache_t -{ - ASSERT_STATIC (key_bits >= cache_bits); - ASSERT_STATIC (key_bits + value_bits - cache_bits < 8 * sizeof (unsigned int)); - - inline void clear (void) - { - memset (values, 255, sizeof (values)); - } - - inline bool get (unsigned int key, unsigned int *value) - { - unsigned int k = key & ((1u<> value_bits) != (key >> cache_bits)) - return false; - *value = v & ((1u<> key_bits) || (value >> value_bits))) - return false; /* Overflows */ - unsigned int k = key & ((1u<>cache_bits)< hb_cmap_cache_t; -typedef hb_cache_t<16, 24, 8> hb_advance_cache_t; - - -#endif /* HB_CACHE_PRIVATE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cache.hh b/src/3rdparty/harfbuzz-ng/src/hb-cache.hh new file mode 100644 index 00000000000..8371465c6c2 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cache.hh @@ -0,0 +1,101 @@ +/* + * Copyright ยฉ 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_CACHE_HH +#define HB_CACHE_HH + +#include "hb.hh" + + +/* Implements a lockfree cache for int->int functions. + * + * The cache is a fixed-size array of 16-bit or 32-bit integers. + * The key is split into two parts: the cache index and the rest. + * + * The cache index is used to index into the array. The rest is used + * to store the key and the value. + * + * The value is stored in the least significant bits of the integer. + * The key is stored in the most significant bits of the integer. + * The key is shifted by cache_bits to the left to make room for the + * value. + */ + +template +struct hb_cache_t +{ + using item_t = typename std::conditional::type, + typename std::conditional::type + >::type; + + static_assert ((key_bits >= cache_bits), ""); + static_assert ((key_bits + value_bits <= cache_bits + 8 * sizeof (item_t)), ""); + + hb_cache_t () { init (); } + + void init () { clear (); } + + void clear () + { + for (unsigned i = 0; i < ARRAY_LENGTH (values); i++) + values[i] = -1; + } + + bool get (unsigned int key, unsigned int *value) const + { + unsigned int k = key & ((1u<> value_bits) != (key >> cache_bits)) + return false; + *value = v & ((1u<> key_bits) || (value >> value_bits))) + return false; /* Overflows */ + unsigned int k = key & ((1u<>cache_bits)< + +/* Some routines in this file were ported from BlackRenderer by Black Foundry. + * Used by permission to relicense to HarfBuzz license. + * + * https://github.com/BlackFoundryCom/black-renderer + */ + +#define PREALLOCATED_COLOR_STOPS 16 + +typedef struct { + float r, g, b, a; +} hb_cairo_color_t; + +static inline cairo_extend_t +hb_cairo_extend (hb_paint_extend_t extend) +{ + switch (extend) + { + case HB_PAINT_EXTEND_PAD: return CAIRO_EXTEND_PAD; + case HB_PAINT_EXTEND_REPEAT: return CAIRO_EXTEND_REPEAT; + case HB_PAINT_EXTEND_REFLECT: return CAIRO_EXTEND_REFLECT; + default: break; + } + + return CAIRO_EXTEND_PAD; +} + +#ifdef CAIRO_HAS_PNG_FUNCTIONS +typedef struct +{ + hb_blob_t *blob; + unsigned int offset; +} hb_cairo_read_blob_data_t; + +static cairo_status_t +hb_cairo_read_blob (void *closure, + unsigned char *data, + unsigned int length) +{ + hb_cairo_read_blob_data_t *r = (hb_cairo_read_blob_data_t *) closure; + const char *d; + unsigned int size; + + d = hb_blob_get_data (r->blob, &size); + + if (r->offset + length > size) + return CAIRO_STATUS_READ_ERROR; + + memcpy (data, d + r->offset, length); + r->offset += length; + + return CAIRO_STATUS_SUCCESS; +} +#endif + +static const cairo_user_data_key_t *_hb_cairo_surface_blob_user_data_key = {0}; + +static void +_hb_cairo_destroy_blob (void *p) +{ + hb_blob_destroy ((hb_blob_t *) p); +} + +hb_bool_t +_hb_cairo_paint_glyph_image (hb_cairo_context_t *c, + hb_blob_t *blob, + unsigned width, + unsigned height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents) +{ + cairo_t *cr = c->cr; + + if (!extents) /* SVG currently. */ + return false; + + cairo_surface_t *surface = nullptr; + +#ifdef CAIRO_HAS_PNG_FUNCTIONS + if (format == HB_PAINT_IMAGE_FORMAT_PNG) + { + hb_cairo_read_blob_data_t r; + r.blob = blob; + r.offset = 0; + surface = cairo_image_surface_create_from_png_stream (hb_cairo_read_blob, &r); + + /* For PNG, width,height can be unreliable, as is the case for NotoColorEmoji :(. + * Just pull them out of the surface. */ + width = cairo_image_surface_get_width (surface); + height = cairo_image_surface_get_width (surface); + } + else +#endif + if (format == HB_PAINT_IMAGE_FORMAT_BGRA) + { + /* Byte-endian conversion. */ + unsigned data_size = hb_blob_get_length (blob); + if (data_size < width * height * 4) + return false; + + unsigned char *data; +#ifdef __BYTE_ORDER + if (__BYTE_ORDER == __BIG_ENDIAN) + { + data = (unsigned char *) hb_blob_get_data_writable (blob, nullptr); + if (!data) + return false; + + unsigned count = width * height * 4; + for (unsigned i = 0; i < count; i += 4) + { + unsigned char b; + b = data[i]; + data[i] = data[i+3]; + data[i+3] = b; + b = data[i+1]; + data[i+1] = data[i+2]; + data[i+2] = b; + } + } + else +#endif + data = (unsigned char *) hb_blob_get_data (blob, nullptr); + + surface = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_ARGB32, + width, height, + width * 4); + + cairo_surface_set_user_data (surface, + _hb_cairo_surface_blob_user_data_key, + hb_blob_reference (blob), + _hb_cairo_destroy_blob); + } + + if (!surface) + return false; + + cairo_save (cr); + /* this clip is here to work around recording surface limitations */ + cairo_rectangle (cr, + extents->x_bearing, + extents->y_bearing, + extents->width, + extents->height); + cairo_clip (cr); + + cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface); + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); + + cairo_matrix_t matrix = {(double) width, 0, 0, (double) height, 0, 0}; + cairo_pattern_set_matrix (pattern, &matrix); + + /* Undo slant in the extents and apply it in the context. */ + extents->width -= extents->height * slant; + extents->x_bearing -= extents->y_bearing * slant; + cairo_matrix_t cairo_matrix = {1., 0., (double) slant, 1., 0., 0.}; + cairo_transform (cr, &cairo_matrix); + + cairo_translate (cr, extents->x_bearing, extents->y_bearing); + cairo_scale (cr, extents->width, extents->height); + cairo_set_source (cr, pattern); + + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + cairo_surface_destroy (surface); + + cairo_restore (cr); + + return true; +} + +static void +_hb_cairo_reduce_anchors (float x0, float y0, + float x1, float y1, + float x2, float y2, + float *xx0, float *yy0, + float *xx1, float *yy1) +{ + float q1x, q1y, q2x, q2y; + float s; + float k; + + q2x = x2 - x0; + q2y = y2 - y0; + q1x = x1 - x0; + q1y = y1 - y0; + + s = q2x * q2x + q2y * q2y; + if (s < 0.000001f) + { + *xx0 = x0; *yy0 = y0; + *xx1 = x1; *yy1 = y1; + return; + } + + k = (q2x * q1x + q2y * q1y) / s; + *xx0 = x0; + *yy0 = y0; + *xx1 = x1 - k * q2x; + *yy1 = y1 - k * q2y; +} + +static int +_hb_cairo_cmp_color_stop (const void *p1, + const void *p2) +{ + const hb_color_stop_t *c1 = (const hb_color_stop_t *) p1; + const hb_color_stop_t *c2 = (const hb_color_stop_t *) p2; + + if (c1->offset < c2->offset) + return -1; + else if (c1->offset > c2->offset) + return 1; + else + return 0; +} + +static void +_hb_cairo_normalize_color_line (hb_color_stop_t *stops, + unsigned int len, + float *omin, + float *omax) +{ + float min, max; + + hb_qsort (stops, len, sizeof (hb_color_stop_t), _hb_cairo_cmp_color_stop); + + min = max = stops[0].offset; + for (unsigned int i = 0; i < len; i++) + { + min = hb_min (min, stops[i].offset); + max = hb_max (max, stops[i].offset); + } + + if (min != max) + { + for (unsigned int i = 0; i < len; i++) + stops[i].offset = (stops[i].offset - min) / (max - min); + } + + *omin = min; + *omax = max; +} + +static bool +_hb_cairo_get_color_stops (hb_cairo_context_t *c, + hb_color_line_t *color_line, + unsigned *count, + hb_color_stop_t **stops) +{ + unsigned len = hb_color_line_get_color_stops (color_line, 0, nullptr, nullptr); + if (len > *count) + { + *stops = (hb_color_stop_t *) hb_malloc (len * sizeof (hb_color_stop_t)); + if (unlikely (!stops)) + return false; + } + hb_color_line_get_color_stops (color_line, 0, &len, *stops); + for (unsigned i = 0; i < len; i++) + if ((*stops)[i].is_foreground) + { +#ifdef HAVE_CAIRO_USER_SCALED_FONT_GET_FOREGROUND_SOURCE + double r, g, b, a; + cairo_pattern_t *foreground = cairo_user_scaled_font_get_foreground_source (c->scaled_font); + if (cairo_pattern_get_rgba (foreground, &r, &g, &b, &a) == CAIRO_STATUS_SUCCESS) + (*stops)[i].color = HB_COLOR (round (b * 255.), round (g * 255.), round (r * 255.), + round (a * hb_color_get_alpha ((*stops)[i].color))); + else +#endif + (*stops)[i].color = HB_COLOR (0, 0, 0, hb_color_get_alpha ((*stops)[i].color)); + } + + *count = len; + return true; +} + +void +_hb_cairo_paint_linear_gradient (hb_cairo_context_t *c, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2) +{ + cairo_t *cr = c->cr; + + unsigned int len = PREALLOCATED_COLOR_STOPS; + hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; + hb_color_stop_t *stops = stops_; + float xx0, yy0, xx1, yy1; + float xxx0, yyy0, xxx1, yyy1; + float min, max; + cairo_pattern_t *pattern; + + if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops))) + return; + _hb_cairo_normalize_color_line (stops, len, &min, &max); + + _hb_cairo_reduce_anchors (x0, y0, x1, y1, x2, y2, &xx0, &yy0, &xx1, &yy1); + + xxx0 = xx0 + min * (xx1 - xx0); + yyy0 = yy0 + min * (yy1 - yy0); + xxx1 = xx0 + max * (xx1 - xx0); + yyy1 = yy0 + max * (yy1 - yy0); + + pattern = cairo_pattern_create_linear ((double) xxx0, (double) yyy0, (double) xxx1, (double) yyy1); + cairo_pattern_set_extend (pattern, hb_cairo_extend (hb_color_line_get_extend (color_line))); + for (unsigned int i = 0; i < len; i++) + { + double r, g, b, a; + r = hb_color_get_red (stops[i].color) / 255.; + g = hb_color_get_green (stops[i].color) / 255.; + b = hb_color_get_blue (stops[i].color) / 255.; + a = hb_color_get_alpha (stops[i].color) / 255.; + cairo_pattern_add_color_stop_rgba (pattern, (double) stops[i].offset, r, g, b, a); + } + + cairo_set_source (cr, pattern); + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + + if (stops != stops_) + hb_free (stops); +} + +void +_hb_cairo_paint_radial_gradient (hb_cairo_context_t *c, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1) +{ + cairo_t *cr = c->cr; + + unsigned int len = PREALLOCATED_COLOR_STOPS; + hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; + hb_color_stop_t *stops = stops_; + float min, max; + float xx0, yy0, xx1, yy1; + float rr0, rr1; + cairo_pattern_t *pattern; + + if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops))) + return; + _hb_cairo_normalize_color_line (stops, len, &min, &max); + + xx0 = x0 + min * (x1 - x0); + yy0 = y0 + min * (y1 - y0); + xx1 = x0 + max * (x1 - x0); + yy1 = y0 + max * (y1 - y0); + rr0 = r0 + min * (r1 - r0); + rr1 = r0 + max * (r1 - r0); + + pattern = cairo_pattern_create_radial ((double) xx0, (double) yy0, (double) rr0, (double) xx1, (double) yy1, (double) rr1); + cairo_pattern_set_extend (pattern, hb_cairo_extend (hb_color_line_get_extend (color_line))); + + for (unsigned int i = 0; i < len; i++) + { + double r, g, b, a; + r = hb_color_get_red (stops[i].color) / 255.; + g = hb_color_get_green (stops[i].color) / 255.; + b = hb_color_get_blue (stops[i].color) / 255.; + a = hb_color_get_alpha (stops[i].color) / 255.; + cairo_pattern_add_color_stop_rgba (pattern, (double) stops[i].offset, r, g, b, a); + } + + cairo_set_source (cr, pattern); + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + + if (stops != stops_) + hb_free (stops); +} + +typedef struct { + float x, y; +} hb_cairo_point_t; + +static inline float +_hb_cairo_interpolate (float f0, float f1, float f) +{ + return f0 + f * (f1 - f0); +} + +static inline void +_hb_cairo_premultiply (hb_cairo_color_t *c) +{ + c->r *= c->a; + c->g *= c->a; + c->b *= c->a; +} + +static inline void +_hb_cairo_unpremultiply (hb_cairo_color_t *c) +{ + if (c->a != 0.f) + { + c->r /= c->a; + c->g /= c->a; + c->b /= c->a; + } +} + +static void +_hb_cairo_interpolate_colors (hb_cairo_color_t *c0, hb_cairo_color_t *c1, float k, hb_cairo_color_t *c) +{ + // According to the COLR specification, gradients + // should be interpolated in premultiplied form + _hb_cairo_premultiply (c0); + _hb_cairo_premultiply (c1); + c->r = c0->r + k * (c1->r - c0->r); + c->g = c0->g + k * (c1->g - c0->g); + c->b = c0->b + k * (c1->b - c0->b); + c->a = c0->a + k * (c1->a - c0->a); + _hb_cairo_unpremultiply (c); +} + +static inline float +_hb_cairo_dot (hb_cairo_point_t p, hb_cairo_point_t q) +{ + return p.x * q.x + p.y * q.y; +} + +static inline hb_cairo_point_t +_hb_cairo_normalize (hb_cairo_point_t p) +{ + float len = sqrtf (_hb_cairo_dot (p, p)); + + return hb_cairo_point_t { p.x / len, p.y / len }; +} + +static inline hb_cairo_point_t +_hb_cairo_sum (hb_cairo_point_t p, hb_cairo_point_t q) +{ + return hb_cairo_point_t { p.x + q.x, p.y + q.y }; +} + +static inline hb_cairo_point_t +_hb_cairo_difference (hb_cairo_point_t p, hb_cairo_point_t q) +{ + return hb_cairo_point_t { p.x - q.x, p.y - q.y }; +} + +static inline hb_cairo_point_t +_hb_cairo_scale (hb_cairo_point_t p, float f) +{ + return hb_cairo_point_t { p.x * f, p.y * f }; +} + +typedef struct { + hb_cairo_point_t center, p0, c0, c1, p1; + hb_cairo_color_t color0, color1; +} hb_cairo_patch_t; + +static void +_hb_cairo_add_patch (cairo_pattern_t *pattern, hb_cairo_point_t *center, hb_cairo_patch_t *p) +{ + cairo_mesh_pattern_begin_patch (pattern); + cairo_mesh_pattern_move_to (pattern, (double) center->x, (double) center->y); + cairo_mesh_pattern_line_to (pattern, (double) p->p0.x, (double) p->p0.y); + cairo_mesh_pattern_curve_to (pattern, + (double) p->c0.x, (double) p->c0.y, + (double) p->c1.x, (double) p->c1.y, + (double) p->p1.x, (double) p->p1.y); + cairo_mesh_pattern_line_to (pattern, (double) center->x, (double) center->y); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, + (double) p->color0.r, + (double) p->color0.g, + (double) p->color0.b, + (double) p->color0.a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 1, + (double) p->color0.r, + (double) p->color0.g, + (double) p->color0.b, + (double) p->color0.a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, + (double) p->color1.r, + (double) p->color1.g, + (double) p->color1.b, + (double) p->color1.a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 3, + (double) p->color1.r, + (double) p->color1.g, + (double) p->color1.b, + (double) p->color1.a); + cairo_mesh_pattern_end_patch (pattern); +} + +#define MAX_ANGLE (HB_PI / 8.f) + +static void +_hb_cairo_add_sweep_gradient_patches1 (float cx, float cy, float radius, + float a0, hb_cairo_color_t *c0, + float a1, hb_cairo_color_t *c1, + cairo_pattern_t *pattern) +{ + hb_cairo_point_t center = hb_cairo_point_t { cx, cy }; + int num_splits; + hb_cairo_point_t p0; + hb_cairo_color_t color0, color1; + + num_splits = ceilf (fabsf (a1 - a0) / MAX_ANGLE); + p0 = hb_cairo_point_t { cosf (a0), sinf (a0) }; + color0 = *c0; + + for (int a = 0; a < num_splits; a++) + { + float k = (a + 1.) / num_splits; + float angle1; + hb_cairo_point_t p1; + hb_cairo_point_t A, U; + hb_cairo_point_t C0, C1; + hb_cairo_patch_t patch; + + angle1 = _hb_cairo_interpolate (a0, a1, k); + _hb_cairo_interpolate_colors (c0, c1, k, &color1); + + patch.color0 = color0; + patch.color1 = color1; + + p1 = hb_cairo_point_t { cosf (angle1), sinf (angle1) }; + patch.p0 = _hb_cairo_sum (center, _hb_cairo_scale (p0, radius)); + patch.p1 = _hb_cairo_sum (center, _hb_cairo_scale (p1, radius)); + + A = _hb_cairo_normalize (_hb_cairo_sum (p0, p1)); + U = hb_cairo_point_t { -A.y, A.x }; + C0 = _hb_cairo_sum (A, _hb_cairo_scale (U, _hb_cairo_dot (_hb_cairo_difference (p0, A), p0) / _hb_cairo_dot (U, p0))); + C1 = _hb_cairo_sum (A, _hb_cairo_scale (U, _hb_cairo_dot (_hb_cairo_difference (p1, A), p1) / _hb_cairo_dot (U, p1))); + + patch.c0 = _hb_cairo_sum (center, _hb_cairo_scale (_hb_cairo_sum (C0, _hb_cairo_scale (_hb_cairo_difference (C0, p0), 0.33333f)), radius)); + patch.c1 = _hb_cairo_sum (center, _hb_cairo_scale (_hb_cairo_sum (C1, _hb_cairo_scale (_hb_cairo_difference (C1, p1), 0.33333f)), radius)); + + _hb_cairo_add_patch (pattern, ¢er, &patch); + + p0 = p1; + color0 = color1; + } +} + +static void +_hb_cairo_add_sweep_gradient_patches (hb_color_stop_t *stops, + unsigned int n_stops, + cairo_extend_t extend, + float cx, float cy, + float radius, + float start_angle, + float end_angle, + cairo_pattern_t *pattern) +{ + float angles_[PREALLOCATED_COLOR_STOPS]; + float *angles = angles_; + hb_cairo_color_t colors_[PREALLOCATED_COLOR_STOPS]; + hb_cairo_color_t *colors = colors_; + hb_cairo_color_t color0, color1; + + if (start_angle == end_angle) + { + if (extend == CAIRO_EXTEND_PAD) + { + hb_cairo_color_t c; + if (start_angle > 0) + { + c.r = hb_color_get_red (stops[0].color) / 255.; + c.g = hb_color_get_green (stops[0].color) / 255.; + c.b = hb_color_get_blue (stops[0].color) / 255.; + c.a = hb_color_get_alpha (stops[0].color) / 255.; + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + 0., &c, + start_angle, &c, + pattern); + } + if (end_angle < HB_2_PI) + { + c.r = hb_color_get_red (stops[n_stops - 1].color) / 255.; + c.g = hb_color_get_green (stops[n_stops - 1].color) / 255.; + c.b = hb_color_get_blue (stops[n_stops - 1].color) / 255.; + c.a = hb_color_get_alpha (stops[n_stops - 1].color) / 255.; + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + end_angle, &c, + HB_2_PI, &c, + pattern); + } + } + return; + } + + assert (start_angle != end_angle); + + /* handle directions */ + if (end_angle < start_angle) + { + hb_swap (start_angle, end_angle); + + for (unsigned i = 0; i < n_stops - 1 - i; i++) + hb_swap (stops[i], stops[n_stops - 1 - i]); + for (unsigned i = 0; i < n_stops; i++) + stops[i].offset = 1 - stops[i].offset; + } + + if (n_stops > PREALLOCATED_COLOR_STOPS) + { + angles = (float *) hb_malloc (sizeof (float) * n_stops); + colors = (hb_cairo_color_t *) hb_malloc (sizeof (hb_cairo_color_t) * n_stops); + if (unlikely (!angles || !colors)) + { + hb_free (angles); + hb_free (colors); + return; + } + } + + for (unsigned i = 0; i < n_stops; i++) + { + angles[i] = start_angle + stops[i].offset * (end_angle - start_angle); + colors[i].r = hb_color_get_red (stops[i].color) / 255.; + colors[i].g = hb_color_get_green (stops[i].color) / 255.; + colors[i].b = hb_color_get_blue (stops[i].color) / 255.; + colors[i].a = hb_color_get_alpha (stops[i].color) / 255.; + } + + if (extend == CAIRO_EXTEND_PAD) + { + unsigned pos; + + color0 = colors[0]; + for (pos = 0; pos < n_stops; pos++) + { + if (angles[pos] >= 0) + { + if (pos > 0) + { + float k = (0 - angles[pos - 1]) / (angles[pos] - angles[pos - 1]); + _hb_cairo_interpolate_colors (&colors[pos-1], &colors[pos], k, &color0); + } + break; + } + } + if (pos == n_stops) + { + /* everything is below 0 */ + color0 = colors[n_stops-1]; + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + 0., &color0, + HB_2_PI, &color0, + pattern); + goto done; + } + + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + 0., &color0, + angles[pos], &colors[pos], + pattern); + + for (pos++; pos < n_stops; pos++) + { + if (angles[pos] <= HB_2_PI) + { + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + angles[pos - 1], &colors[pos-1], + angles[pos], &colors[pos], + pattern); + } + else + { + float k = (HB_2_PI - angles[pos - 1]) / (angles[pos] - angles[pos - 1]); + _hb_cairo_interpolate_colors (&colors[pos - 1], &colors[pos], k, &color1); + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + angles[pos - 1], &colors[pos - 1], + HB_2_PI, &color1, + pattern); + break; + } + } + + if (pos == n_stops) + { + /* everything is below 2*M_PI */ + color0 = colors[n_stops - 1]; + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + angles[n_stops - 1], &color0, + HB_2_PI, &color0, + pattern); + goto done; + } + } + else + { + int k; + float span; + + span = angles[n_stops - 1] - angles[0]; + k = 0; + if (angles[0] >= 0) + { + float ss = angles[0]; + while (ss > 0) + { + if (span > 0) + { + ss -= span; + k--; + } + else + { + ss += span; + k++; + } + } + } + else if (angles[0] < 0) + { + float ee = angles[n_stops - 1]; + while (ee < 0) + { + if (span > 0) + { + ee += span; + k++; + } + else + { + ee -= span; + k--; + } + } + } + + //assert (angles[0] + k * span <= 0 && 0 < angles[n_stops - 1] + k * span); + span = fabs (span); + + for (signed l = k; l < 1000; l++) + { + for (unsigned i = 1; i < n_stops; i++) + { + float a0, a1; + hb_cairo_color_t *c0, *c1; + + if ((l % 2 != 0) && (extend == CAIRO_EXTEND_REFLECT)) + { + a0 = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - (i-1)] + l * span; + a1 = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - i] + l * span; + c0 = &colors[n_stops - 1 - (i - 1)]; + c1 = &colors[n_stops - 1 - i]; + } + else + { + a0 = angles[i-1] + l * span; + a1 = angles[i] + l * span; + c0 = &colors[i-1]; + c1 = &colors[i]; + } + + if (a1 < 0) + continue; + if (a0 < 0) + { + hb_cairo_color_t color; + float f = (0 - a0)/(a1 - a0); + _hb_cairo_interpolate_colors (c0, c1, f, &color); + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + 0, &color, + a1, c1, + pattern); + } + else if (a1 >= HB_2_PI) + { + hb_cairo_color_t color; + float f = (HB_2_PI - a0)/(a1 - a0); + _hb_cairo_interpolate_colors (c0, c1, f, &color); + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + a0, c0, + HB_2_PI, &color, + pattern); + goto done; + } + else + { + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + a0, c0, + a1, c1, + pattern); + } + } + } + } + +done: + + if (angles != angles_) + hb_free (angles); + if (colors != colors_) + hb_free (colors); +} + +void +_hb_cairo_paint_sweep_gradient (hb_cairo_context_t *c, + hb_color_line_t *color_line, + float cx, float cy, + float start_angle, + float end_angle) +{ + cairo_t *cr = c->cr; + + unsigned int len = PREALLOCATED_COLOR_STOPS; + hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; + hb_color_stop_t *stops = stops_; + cairo_extend_t extend; + double x1, y1, x2, y2; + float max_x, max_y, radius; + cairo_pattern_t *pattern; + + if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops))) + return; + + hb_qsort (stops, len, sizeof (hb_color_stop_t), _hb_cairo_cmp_color_stop); + + cairo_clip_extents (cr, &x1, &y1, &x2, &y2); + max_x = (float) hb_max ((x1 - (double) cx) * (x1 - (double) cx), (x2 - (double) cx) * (x2 - (double) cx)); + max_y = (float) hb_max ((y1 - (double) cy) * (y1 - (double) cy), (y2 - (double) cy) * (y2 - (double) cy)); + radius = sqrtf (max_x + max_y); + + extend = hb_cairo_extend (hb_color_line_get_extend (color_line)); + pattern = cairo_pattern_create_mesh (); + + _hb_cairo_add_sweep_gradient_patches (stops, len, extend, cx, cy, + radius, start_angle, end_angle, pattern); + + cairo_set_source (cr, pattern); + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + + if (stops != stops_) + hb_free (stops); +} + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cairo-utils.hh b/src/3rdparty/harfbuzz-ng/src/hb-cairo-utils.hh new file mode 100644 index 00000000000..a26bf59d918 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cairo-utils.hh @@ -0,0 +1,107 @@ +/* + * Copyright ยฉ 2022 Red Hat, Inc + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Matthias Clasen + */ + +#ifndef HB_CAIRO_UTILS_H +#define HB_CAIRO_UTILS_H + +#include "hb.hh" +#include "hb-cairo.h" + + +typedef struct +{ + cairo_scaled_font_t *scaled_font; + cairo_t *cr; + hb_map_t *color_cache; +} hb_cairo_context_t; + +static inline cairo_operator_t +_hb_paint_composite_mode_to_cairo (hb_paint_composite_mode_t mode) +{ + switch (mode) + { + case HB_PAINT_COMPOSITE_MODE_CLEAR: return CAIRO_OPERATOR_CLEAR; + case HB_PAINT_COMPOSITE_MODE_SRC: return CAIRO_OPERATOR_SOURCE; + case HB_PAINT_COMPOSITE_MODE_DEST: return CAIRO_OPERATOR_DEST; + case HB_PAINT_COMPOSITE_MODE_SRC_OVER: return CAIRO_OPERATOR_OVER; + case HB_PAINT_COMPOSITE_MODE_DEST_OVER: return CAIRO_OPERATOR_DEST_OVER; + case HB_PAINT_COMPOSITE_MODE_SRC_IN: return CAIRO_OPERATOR_IN; + case HB_PAINT_COMPOSITE_MODE_DEST_IN: return CAIRO_OPERATOR_DEST_IN; + case HB_PAINT_COMPOSITE_MODE_SRC_OUT: return CAIRO_OPERATOR_OUT; + case HB_PAINT_COMPOSITE_MODE_DEST_OUT: return CAIRO_OPERATOR_DEST_OUT; + case HB_PAINT_COMPOSITE_MODE_SRC_ATOP: return CAIRO_OPERATOR_ATOP; + case HB_PAINT_COMPOSITE_MODE_DEST_ATOP: return CAIRO_OPERATOR_DEST_ATOP; + case HB_PAINT_COMPOSITE_MODE_XOR: return CAIRO_OPERATOR_XOR; + case HB_PAINT_COMPOSITE_MODE_PLUS: return CAIRO_OPERATOR_ADD; + case HB_PAINT_COMPOSITE_MODE_SCREEN: return CAIRO_OPERATOR_SCREEN; + case HB_PAINT_COMPOSITE_MODE_OVERLAY: return CAIRO_OPERATOR_OVERLAY; + case HB_PAINT_COMPOSITE_MODE_DARKEN: return CAIRO_OPERATOR_DARKEN; + case HB_PAINT_COMPOSITE_MODE_LIGHTEN: return CAIRO_OPERATOR_LIGHTEN; + case HB_PAINT_COMPOSITE_MODE_COLOR_DODGE: return CAIRO_OPERATOR_COLOR_DODGE; + case HB_PAINT_COMPOSITE_MODE_COLOR_BURN: return CAIRO_OPERATOR_COLOR_BURN; + case HB_PAINT_COMPOSITE_MODE_HARD_LIGHT: return CAIRO_OPERATOR_HARD_LIGHT; + case HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT: return CAIRO_OPERATOR_SOFT_LIGHT; + case HB_PAINT_COMPOSITE_MODE_DIFFERENCE: return CAIRO_OPERATOR_DIFFERENCE; + case HB_PAINT_COMPOSITE_MODE_EXCLUSION: return CAIRO_OPERATOR_EXCLUSION; + case HB_PAINT_COMPOSITE_MODE_MULTIPLY: return CAIRO_OPERATOR_MULTIPLY; + case HB_PAINT_COMPOSITE_MODE_HSL_HUE: return CAIRO_OPERATOR_HSL_HUE; + case HB_PAINT_COMPOSITE_MODE_HSL_SATURATION: return CAIRO_OPERATOR_HSL_SATURATION; + case HB_PAINT_COMPOSITE_MODE_HSL_COLOR: return CAIRO_OPERATOR_HSL_COLOR; + case HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY: return CAIRO_OPERATOR_HSL_LUMINOSITY; + default: return CAIRO_OPERATOR_CLEAR; + } +} + +HB_INTERNAL hb_bool_t +_hb_cairo_paint_glyph_image (hb_cairo_context_t *c, + hb_blob_t *blob, + unsigned width, + unsigned height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents); + +HB_INTERNAL void +_hb_cairo_paint_linear_gradient (hb_cairo_context_t *c, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2); + +HB_INTERNAL void +_hb_cairo_paint_radial_gradient (hb_cairo_context_t *c, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1); + +HB_INTERNAL void +_hb_cairo_paint_sweep_gradient (hb_cairo_context_t *c, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, float end_angle); + + +#endif /* HB_CAIRO_UTILS_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cairo.cc b/src/3rdparty/harfbuzz-ng/src/hb-cairo.cc new file mode 100644 index 00000000000..f005afd17e7 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cairo.cc @@ -0,0 +1,1010 @@ +/* + * Copyright ยฉ 2022 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Matthias Clasen + */ + +#include "hb.hh" + +#ifdef HAVE_CAIRO + +#include "hb-cairo.h" + +#include "hb-cairo-utils.hh" + +#include "hb-machinery.hh" +#include "hb-utf.hh" + + +/** + * SECTION:hb-cairo + * @title: hb-cairo + * @short_description: Cairo integration + * @include: hb-cairo.h + * + * Functions for using HarfBuzz with the cairo library. + * + * HarfBuzz supports using cairo for rendering. + **/ + +static void +hb_cairo_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st HB_UNUSED, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + cairo_t *cr = (cairo_t *) draw_data; + + cairo_move_to (cr, (double) to_x, (double) to_y); +} + +static void +hb_cairo_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st HB_UNUSED, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + cairo_t *cr = (cairo_t *) draw_data; + + cairo_line_to (cr, (double) to_x, (double) to_y); +} + +static void +hb_cairo_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st HB_UNUSED, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + cairo_t *cr = (cairo_t *) draw_data; + + cairo_curve_to (cr, + (double) control1_x, (double) control1_y, + (double) control2_x, (double) control2_y, + (double) to_x, (double) to_y); +} + +static void +hb_cairo_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st HB_UNUSED, + void *user_data HB_UNUSED) +{ + cairo_t *cr = (cairo_t *) draw_data; + + cairo_close_path (cr); +} + +static inline void free_static_cairo_draw_funcs (); + +static struct hb_cairo_draw_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t +{ + static hb_draw_funcs_t *create () + { + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + + hb_draw_funcs_set_move_to_func (funcs, hb_cairo_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, hb_cairo_line_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, hb_cairo_cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, hb_cairo_close_path, nullptr, nullptr); + + hb_draw_funcs_make_immutable (funcs); + + hb_atexit (free_static_cairo_draw_funcs); + + return funcs; + } +} static_cairo_draw_funcs; + +static inline +void free_static_cairo_draw_funcs () +{ + static_cairo_draw_funcs.free_instance (); +} + +static hb_draw_funcs_t * +hb_cairo_draw_get_funcs () +{ + return static_cairo_draw_funcs.get_unconst (); +} + + +#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC + +static void +hb_cairo_push_transform (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_matrix_t m; + + cairo_save (cr); + cairo_matrix_init (&m, (double) xx, (double) yx, + (double) xy, (double) yy, + (double) dx, (double) dy); + cairo_transform (cr, &m); +} + +static void +hb_cairo_pop_transform (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_restore (cr); +} + +static void +hb_cairo_push_clip_glyph (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_save (cr); + cairo_new_path (cr); + hb_font_draw_glyph (font, glyph, hb_cairo_draw_get_funcs (), cr); + cairo_close_path (cr); + cairo_clip (cr); +} + +static void +hb_cairo_push_clip_rectangle (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + float xmin, float ymin, float xmax, float ymax, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_save (cr); + cairo_rectangle (cr, + (double) xmin, (double) ymin, + (double) (xmax - xmin), (double) (ymax - ymin)); + cairo_clip (cr); +} + +static void +hb_cairo_pop_clip (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_restore (cr); +} + +static void +hb_cairo_push_group (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_save (cr); + cairo_push_group (cr); +} + +static void +hb_cairo_pop_group (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_paint_composite_mode_t mode, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_pop_group_to_source (cr); + cairo_set_operator (cr, _hb_paint_composite_mode_to_cairo (mode)); + cairo_paint (cr); + + cairo_restore (cr); +} + +static void +hb_cairo_paint_color (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_bool_t use_foreground, + hb_color_t color, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + if (use_foreground) + { +#ifdef HAVE_CAIRO_USER_SCALED_FONT_GET_FOREGROUND_SOURCE + double r, g, b, a; + cairo_pattern_t *foreground = cairo_user_scaled_font_get_foreground_source (c->scaled_font); + if (cairo_pattern_get_rgba (foreground, &r, &g, &b, &a) == CAIRO_STATUS_SUCCESS) + cairo_set_source_rgba (cr, r, g, b, a * hb_color_get_alpha (color) / 255.); + else +#endif + cairo_set_source_rgba (cr, 0, 0, 0, hb_color_get_alpha (color) / 255.); + } + else + cairo_set_source_rgba (cr, + hb_color_get_red (color) / 255., + hb_color_get_green (color) / 255., + hb_color_get_blue (color) / 255., + hb_color_get_alpha (color) / 255.); + cairo_paint (cr); +} + +static hb_bool_t +hb_cairo_paint_image (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_blob_t *blob, + unsigned width, + unsigned height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + + return _hb_cairo_paint_glyph_image (c, blob, width, height, format, slant, extents); +} + +static void +hb_cairo_paint_linear_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + + _hb_cairo_paint_linear_gradient (c, color_line, x0, y0, x1, y1, x2, y2); +} + +static void +hb_cairo_paint_radial_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + + _hb_cairo_paint_radial_gradient (c, color_line, x0, y0, r0, x1, y1, r1); +} + +static void +hb_cairo_paint_sweep_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, float end_angle, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + + _hb_cairo_paint_sweep_gradient (c, color_line, x0, y0, start_angle, end_angle); +} + +static const cairo_user_data_key_t color_cache_key = {0}; + +static void +_hb_cairo_destroy_map (void *p) +{ + hb_map_destroy ((hb_map_t *) p); +} + +static hb_bool_t +hb_cairo_paint_custom_palette_color (hb_paint_funcs_t *funcs, + void *paint_data, + unsigned int color_index, + hb_color_t *color, + void *user_data HB_UNUSED) +{ +#ifdef HAVE_CAIRO_FONT_OPTIONS_GET_CUSTOM_PALETTE_COLOR + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + +#define HB_DEADBEEF HB_TAG(0xDE,0xAD,0xBE,0xEF) + + hb_map_t *color_cache = c->color_cache; + hb_codepoint_t *v; + if (likely (color_cache && color_cache->has (color_index, &v))) + { + if (*v == HB_DEADBEEF) + return false; + *color = *v; + return true; + } + + cairo_font_options_t *options; + double red, green, blue, alpha; + + options = cairo_font_options_create (); + cairo_get_font_options (cr, options); + if (CAIRO_STATUS_SUCCESS == + cairo_font_options_get_custom_palette_color (options, color_index, + &red, &green, &blue, &alpha)) + { + cairo_font_options_destroy (options); + *color = HB_COLOR (round (255 * blue), + round (255 * green), + round (255 * red), + round (255 * alpha)); + + if (likely (color_cache && *color != HB_DEADBEEF)) + color_cache->set (color_index, *color); + + return true; + } + cairo_font_options_destroy (options); + + if (likely (color_cache)) + color_cache->set (color_index, HB_DEADBEEF); + +#undef HB_DEADBEEF + +#endif + + return false; +} + +static inline void free_static_cairo_paint_funcs (); + +static struct hb_cairo_paint_funcs_lazy_loader_t : hb_paint_funcs_lazy_loader_t +{ + static hb_paint_funcs_t *create () + { + hb_paint_funcs_t *funcs = hb_paint_funcs_create (); + + hb_paint_funcs_set_push_transform_func (funcs, hb_cairo_push_transform, nullptr, nullptr); + hb_paint_funcs_set_pop_transform_func (funcs, hb_cairo_pop_transform, nullptr, nullptr); + hb_paint_funcs_set_push_clip_glyph_func (funcs, hb_cairo_push_clip_glyph, nullptr, nullptr); + hb_paint_funcs_set_push_clip_rectangle_func (funcs, hb_cairo_push_clip_rectangle, nullptr, nullptr); + hb_paint_funcs_set_pop_clip_func (funcs, hb_cairo_pop_clip, nullptr, nullptr); + hb_paint_funcs_set_push_group_func (funcs, hb_cairo_push_group, nullptr, nullptr); + hb_paint_funcs_set_pop_group_func (funcs, hb_cairo_pop_group, nullptr, nullptr); + hb_paint_funcs_set_color_func (funcs, hb_cairo_paint_color, nullptr, nullptr); + hb_paint_funcs_set_image_func (funcs, hb_cairo_paint_image, nullptr, nullptr); + hb_paint_funcs_set_linear_gradient_func (funcs, hb_cairo_paint_linear_gradient, nullptr, nullptr); + hb_paint_funcs_set_radial_gradient_func (funcs, hb_cairo_paint_radial_gradient, nullptr, nullptr); + hb_paint_funcs_set_sweep_gradient_func (funcs, hb_cairo_paint_sweep_gradient, nullptr, nullptr); + hb_paint_funcs_set_custom_palette_color_func (funcs, hb_cairo_paint_custom_palette_color, nullptr, nullptr); + + hb_paint_funcs_make_immutable (funcs); + + hb_atexit (free_static_cairo_paint_funcs); + + return funcs; + } +} static_cairo_paint_funcs; + +static inline +void free_static_cairo_paint_funcs () +{ + static_cairo_paint_funcs.free_instance (); +} + +static hb_paint_funcs_t * +hb_cairo_paint_get_funcs () +{ + return static_cairo_paint_funcs.get_unconst (); +} +#endif + +static const cairo_user_data_key_t hb_cairo_face_user_data_key = {0}; +static const cairo_user_data_key_t hb_cairo_font_user_data_key = {0}; +static const cairo_user_data_key_t hb_cairo_font_init_func_user_data_key = {0}; +static const cairo_user_data_key_t hb_cairo_font_init_user_data_user_data_key = {0}; +static const cairo_user_data_key_t hb_cairo_scale_factor_user_data_key = {0}; + +static void hb_cairo_face_destroy (void *p) { hb_face_destroy ((hb_face_t *) p); } +static void hb_cairo_font_destroy (void *p) { hb_font_destroy ((hb_font_t *) p); } + +static cairo_status_t +hb_cairo_init_scaled_font (cairo_scaled_font_t *scaled_font, + cairo_t *cr HB_UNUSED, + cairo_font_extents_t *extents) +{ + cairo_font_face_t *font_face = cairo_scaled_font_get_font_face (scaled_font); + + hb_font_t *font = (hb_font_t *) cairo_font_face_get_user_data (font_face, + &hb_cairo_font_user_data_key); + + if (!font) + { + hb_face_t *face = (hb_face_t *) cairo_font_face_get_user_data (font_face, + &hb_cairo_face_user_data_key); + font = hb_font_create (face); + +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,16,0) + cairo_font_options_t *font_options = cairo_font_options_create (); + + // Set variations + cairo_scaled_font_get_font_options (scaled_font, font_options); + const char *variations = cairo_font_options_get_variations (font_options); + hb_vector_t vars; + const char *p = variations; + while (p && *p) + { + const char *end = strpbrk ((char *) p, ", "); + hb_variation_t var; + if (hb_variation_from_string (p, end ? end - p : -1, &var)) + vars.push (var); + p = end ? end + 1 : nullptr; + } + hb_font_set_variations (font, &vars[0], vars.length); + + cairo_font_options_destroy (font_options); +#endif + + // Set scale; Note: should NOT set slant, or we'll double-slant. + unsigned scale_factor = hb_cairo_font_face_get_scale_factor (font_face); + if (scale_factor) + { + cairo_matrix_t font_matrix; + cairo_scaled_font_get_scale_matrix (scaled_font, &font_matrix); + hb_font_set_scale (font, + round (font_matrix.xx * scale_factor), + round (font_matrix.yy * scale_factor)); + } + + auto *init_func = (hb_cairo_font_init_func_t) + cairo_font_face_get_user_data (font_face, + &hb_cairo_font_init_func_user_data_key); + if (init_func) + { + void *user_data = cairo_font_face_get_user_data (font_face, + &hb_cairo_font_init_user_data_user_data_key); + font = init_func (font, scaled_font, user_data); + } + + hb_font_make_immutable (font); + } + + cairo_scaled_font_set_user_data (scaled_font, + &hb_cairo_font_user_data_key, + (void *) hb_font_reference (font), + hb_cairo_font_destroy); + + hb_position_t x_scale, y_scale; + hb_font_get_scale (font, &x_scale, &y_scale); + + hb_font_extents_t hb_extents; + hb_font_get_h_extents (font, &hb_extents); + + extents->ascent = (double) hb_extents.ascender / y_scale; + extents->descent = (double) -hb_extents.descender / y_scale; + extents->height = extents->ascent + extents->descent; + +#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC + hb_map_t *color_cache = hb_map_create (); + if (unlikely (CAIRO_STATUS_SUCCESS != cairo_scaled_font_set_user_data (scaled_font, + &color_cache_key, + color_cache, + _hb_cairo_destroy_map))) + hb_map_destroy (color_cache); +#endif + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +hb_cairo_text_to_glyphs (cairo_scaled_font_t *scaled_font, + const char *utf8, + int utf8_len, + cairo_glyph_t **glyphs, + int *num_glyphs, + cairo_text_cluster_t **clusters, + int *num_clusters, + cairo_text_cluster_flags_t *cluster_flags) +{ + hb_font_t *font = (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font, + &hb_cairo_font_user_data_key); + + hb_buffer_t *buffer = hb_buffer_create (); + hb_buffer_add_utf8 (buffer, utf8, utf8_len, 0, utf8_len); + hb_buffer_guess_segment_properties (buffer); + hb_shape (font, buffer, nullptr, 0); + + hb_cairo_glyphs_from_buffer (buffer, + true, + font->x_scale, font->y_scale, + 0., 0., + utf8, utf8_len, + glyphs, (unsigned *) num_glyphs, + clusters, (unsigned *) num_clusters, + cluster_flags); + + hb_buffer_destroy (buffer); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +hb_cairo_render_glyph (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *extents) +{ + hb_font_t *font = (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font, + &hb_cairo_font_user_data_key); + + hb_position_t x_scale, y_scale; + hb_font_get_scale (font, &x_scale, &y_scale); + cairo_scale (cr, +1./x_scale, -1./y_scale); + + hb_font_draw_glyph (font, glyph, hb_cairo_draw_get_funcs (), cr); + + cairo_fill (cr); + + return CAIRO_STATUS_SUCCESS; +} + +#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC + +static cairo_status_t +hb_cairo_render_color_glyph (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *extents) +{ + hb_font_t *font = (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font, + &hb_cairo_font_user_data_key); + + unsigned int palette = 0; +#ifdef CAIRO_COLOR_PALETTE_DEFAULT + cairo_font_options_t *options = cairo_font_options_create (); + cairo_scaled_font_get_font_options (scaled_font, options); + palette = cairo_font_options_get_color_palette (options); + cairo_font_options_destroy (options); +#endif + + hb_color_t color = HB_COLOR (0, 0, 0, 255); + hb_position_t x_scale, y_scale; + hb_font_get_scale (font, &x_scale, &y_scale); + cairo_scale (cr, +1./x_scale, -1./y_scale); + + hb_cairo_context_t c; + c.scaled_font = scaled_font; + c.cr = cr; + c.color_cache = (hb_map_t *) cairo_scaled_font_get_user_data (scaled_font, &color_cache_key); + + hb_font_paint_glyph (font, glyph, hb_cairo_paint_get_funcs (), &c, palette, color); + + + return CAIRO_STATUS_SUCCESS; +} + +#endif + +static cairo_font_face_t * +user_font_face_create (hb_face_t *face) +{ + cairo_font_face_t *cairo_face; + + cairo_face = cairo_user_font_face_create (); + cairo_user_font_face_set_init_func (cairo_face, hb_cairo_init_scaled_font); + cairo_user_font_face_set_text_to_glyphs_func (cairo_face, hb_cairo_text_to_glyphs); + cairo_user_font_face_set_render_glyph_func (cairo_face, hb_cairo_render_glyph); +#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC + if (hb_ot_color_has_png (face) || hb_ot_color_has_layers (face) || hb_ot_color_has_paint (face)) + cairo_user_font_face_set_render_color_glyph_func (cairo_face, hb_cairo_render_color_glyph); +#endif + + if (unlikely (CAIRO_STATUS_SUCCESS != cairo_font_face_set_user_data (cairo_face, + &hb_cairo_face_user_data_key, + (void *) hb_face_reference (face), + hb_cairo_face_destroy))) + hb_face_destroy (face); + + return cairo_face; +} + +/** + * hb_cairo_font_face_create_for_font: + * @font: a #hb_font_t + * + * Creates a #cairo_font_face_t for rendering text according + * to @font. + * + * Note that the scale of @font does not affect the rendering, + * but the variations and slant that are set on @font do. + * + * Returns: (transfer full): a newly created #cairo_font_face_t + * + * Since: 7.0.0 + */ +cairo_font_face_t * +hb_cairo_font_face_create_for_font (hb_font_t *font) +{ + hb_font_make_immutable (font); + + auto *cairo_face = user_font_face_create (font->face); + + if (unlikely (CAIRO_STATUS_SUCCESS != cairo_font_face_set_user_data (cairo_face, + &hb_cairo_font_user_data_key, + (void *) hb_font_reference (font), + hb_cairo_font_destroy))) + hb_font_destroy (font); + + return cairo_face; +} + +/** + * hb_cairo_font_face_get_font: + * @font_face: a #cairo_font_face_t + * + * Gets the #hb_font_t that @font_face was created from. + * + * Returns: (nullable) (transfer none): the #hb_font_t that @font_face was created from + * + * Since: 7.0.0 + */ +hb_font_t * +hb_cairo_font_face_get_font (cairo_font_face_t *font_face) +{ + return (hb_font_t *) cairo_font_face_get_user_data (font_face, + &hb_cairo_font_user_data_key); +} + +/** + * hb_cairo_font_face_create_for_face: + * @face: a #hb_face_t + * + * Creates a #cairo_font_face_t for rendering text according + * to @face. + * + * Returns: (transfer full): a newly created #cairo_font_face_t + * + * Since: 7.0.0 + */ +cairo_font_face_t * +hb_cairo_font_face_create_for_face (hb_face_t *face) +{ + hb_face_make_immutable (face); + + return user_font_face_create (face); +} + +/** + * hb_cairo_font_face_get_face: + * @font_face: a #cairo_font_face_t + * + * Gets the #hb_face_t associated with @font_face. + * + * Returns: (nullable) (transfer none): the #hb_face_t associated with @font_face + * + * Since: 7.0.0 + */ +hb_face_t * +hb_cairo_font_face_get_face (cairo_font_face_t *font_face) +{ + return (hb_face_t *) cairo_font_face_get_user_data (font_face, + &hb_cairo_face_user_data_key); +} + +/** + * hb_cairo_font_face_set_font_init_func: + * @font_face: a #cairo_font_face_t + * @func: The virtual method to use + * @user_data: user data accompanying the method + * @destroy: function to call when @user_data is not needed anymore + * + * Set the virtual method to be called when a cairo + * face created using hb_cairo_font_face_create_for_face() + * creates an #hb_font_t for a #cairo_scaled_font_t. + * + * Since: 7.0.0 + */ +void +hb_cairo_font_face_set_font_init_func (cairo_font_face_t *font_face, + hb_cairo_font_init_func_t func, + void *user_data, + hb_destroy_func_t destroy) +{ + cairo_font_face_set_user_data (font_face, + &hb_cairo_font_init_func_user_data_key, + (void *) func, + nullptr); + if (unlikely (CAIRO_STATUS_SUCCESS != cairo_font_face_set_user_data (font_face, + &hb_cairo_font_init_user_data_user_data_key, + (void *) user_data, + destroy)) && destroy) + { + destroy (user_data); + cairo_font_face_set_user_data (font_face, + &hb_cairo_font_init_func_user_data_key, + nullptr, + nullptr); + } +} + +/** + * hb_cairo_scaled_font_get_font: + * @scaled_font: a #cairo_scaled_font_t + * + * Gets the #hb_font_t associated with @scaled_font. + * + * Returns: (nullable) (transfer none): the #hb_font_t associated with @scaled_font + * + * Since: 7.0.0 + */ +hb_font_t * +hb_cairo_scaled_font_get_font (cairo_scaled_font_t *scaled_font) +{ + return (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font, &hb_cairo_font_user_data_key); +} + + +/** + * hb_cairo_font_face_set_scale_factor: + * @scale_factor: The scale factor to use. See below + * @font_face: a #cairo_font_face_t + * + * Sets the scale factor of the @font_face. Default scale + * factor is zero. + * + * When a #cairo_font_face_t is created from a #hb_face_t using + * hb_cairo_font_face_create_for_face(), such face will create + * #hb_font_t objects during scaled-font creation. The scale + * factor defines how the scale set on such #hb_font_t objects + * relates to the font-matrix (as such font size) of the cairo + * scaled-font. + * + * If the scale-factor is zero (default), then the scale of the + * #hb_font_t object will be left at default, which is the UPEM + * value of the respective #hb_face_t. + * + * If the scale-factor is set to non-zero, then the X and Y scale + * of the #hb_font_t object will be respectively set to the + * @scale_factor times the xx and yy elements of the scale-matrix + * of the cairo scaled-font being created. + * + * When using the hb_cairo_glyphs_from_buffer() API to convert the + * HarfBuzz glyph buffer that resulted from shaping with such a #hb_font_t, + * if the scale-factor was non-zero, you can pass it directly to + * that API as both X and Y scale factors. + * + * If the scale-factor was zero however, or the cairo face was + * created using the alternative constructor + * hb_cairo_font_face_create_for_font(), you need to calculate the + * correct X/Y scale-factors to pass to hb_cairo_glyphs_from_buffer() + * by dividing the #hb_font_t X/Y scale-factors by the + * cairo scaled-font's scale-matrix XX/YY components respectively + * and use those values. Or if you know that relationship offhand + * (because you set the scale of the #hb_font_t yourself), use + * the conversion rate involved. + * + * Since: 7.0.0 + */ +void +hb_cairo_font_face_set_scale_factor (cairo_font_face_t *font_face, + unsigned int scale_factor) +{ + cairo_font_face_set_user_data (font_face, + &hb_cairo_scale_factor_user_data_key, + (void *) (uintptr_t) scale_factor, + nullptr); +} + +/** + * hb_cairo_font_face_get_scale_factor: + * @font_face: a #cairo_font_face_t + * + * Gets the scale factor set on the @font_face. Defaults to zero. + * See hb_cairo_font_face_set_scale_factor() for details. + * + * Returns: the scale factor of @font_face + * + * Since: 7.0.0 + */ +unsigned int +hb_cairo_font_face_get_scale_factor (cairo_font_face_t *font_face) +{ + return (unsigned int) (uintptr_t) + cairo_font_face_get_user_data (font_face, + &hb_cairo_scale_factor_user_data_key); +} + + +/** + * hb_cairo_glyphs_from_buffer: + * @buffer: a #hb_buffer_t containing glyphs + * @utf8_clusters: `true` if @buffer clusters are in bytes, instead of characters + * @x_scale_factor: scale factor to divide #hb_position_t Y values by + * @y_scale_factor: scale factor to divide #hb_position_t X values by + * @x: X position to place first glyph + * @y: Y position to place first glyph + * @utf8: (nullable): the text that was shaped in @buffer + * @utf8_len: the length of @utf8 in bytes + * @glyphs: (out): return location for an array of #cairo_glyph_t + * @num_glyphs: (inout): return location for the length of @glyphs + * @clusters: (out) (nullable): return location for an array of cluster positions + * @num_clusters: (inout) (nullable): return location for the length of @clusters + * @cluster_flags: (out) (nullable): return location for cluster flags + * + * Extracts information from @buffer in a form that can be + * passed to cairo_show_text_glyphs() or cairo_show_glyphs(). + * This API is modeled after cairo_scaled_font_text_to_glyphs() and + * cairo_user_scaled_font_text_to_glyphs_func_t. + * + * The @num_glyphs argument should be preset to the number of glyph entries available + * in the @glyphs buffer. If the @glyphs buffer is `NULL`, the value of + * @num_glyphs must be zero. If the provided glyph array is too short for + * the conversion (or for convenience), a new glyph array may be allocated + * using cairo_glyph_allocate() and placed in @glyphs. Upon return, + * @num_glyphs should contain the number of generated glyphs. If the value + * @glyphs points at has changed after the call, the caller will free the + * allocated glyph array using cairo_glyph_free(). The caller will also free + * the original value of @glyphs, so this function shouldn't do so. + * + * If @clusters is not `NULL`, then @num_clusters and @cluster_flags + * should not be either, and @utf8 must be provided, and cluster + * mapping will be computed. The semantics of how + * cluster array allocation works is similar to the glyph array. That is, + * if @clusters initially points to a non-`NULL` value, that array may be used + * as a cluster buffer, and @num_clusters points to the number of cluster + * entries available there. If the provided cluster array is too short for + * the conversion (or for convenience), a new cluster array may be allocated + * using cairo_text_cluster_allocate() and placed in @clusters. In this case, + * the original value of @clusters will still be freed by the caller. Upon + * return, @num_clusters will contain the number of generated clusters. + * If the value @clusters points at has changed after the call, the caller + * will free the allocated cluster array using cairo_text_cluster_free(). + * + * See hb_cairo_font_face_set_scale_factor() for the details of + * the @scale_factor argument. + * + * The returned @glyphs vector actually has `@num_glyphs + 1` entries in + * it and the x,y values of the extra entry at the end add up the advance + * x,y of all the glyphs in the @buffer. + * + * Since: 7.0.0 + */ +void +hb_cairo_glyphs_from_buffer (hb_buffer_t *buffer, + hb_bool_t utf8_clusters, + double x_scale_factor, + double y_scale_factor, + double x, + double y, + const char *utf8, + int utf8_len, + cairo_glyph_t **glyphs, + unsigned int *num_glyphs, + cairo_text_cluster_t **clusters, + unsigned int *num_clusters, + cairo_text_cluster_flags_t *cluster_flags) +{ + if (utf8 && utf8_len < 0) + utf8_len = strlen (utf8); + + unsigned orig_num_glyphs = *num_glyphs; + *num_glyphs = hb_buffer_get_length (buffer); + hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, nullptr); + hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, nullptr); + if (orig_num_glyphs < *num_glyphs + 1) + *glyphs = cairo_glyph_allocate (*num_glyphs + 1); + + if (clusters && utf8) + { + unsigned orig_num_clusters = *num_clusters; + *num_clusters = *num_glyphs ? 1 : 0; + for (unsigned int i = 1; i < *num_glyphs; i++) + if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) + (*num_clusters)++; + if (orig_num_clusters < *num_clusters) + *clusters = cairo_text_cluster_allocate (*num_clusters); + } + + double x_scale = x_scale_factor ? 1. / x_scale_factor : 0.; + double y_scale = y_scale_factor ? 1. / y_scale_factor : 0.; + hb_position_t hx = 0, hy = 0; + int i; + for (i = 0; i < (int) *num_glyphs; i++) + { + (*glyphs)[i].index = hb_glyph[i].codepoint; + (*glyphs)[i].x = x + (+hb_position->x_offset + hx) * x_scale; + (*glyphs)[i].y = y + (-hb_position->y_offset + hy) * y_scale; + hx += hb_position->x_advance; + hy += -hb_position->y_advance; + + hb_position++; + } + (*glyphs)[i].index = -1; + (*glyphs)[i].x = round (hx * x_scale); + (*glyphs)[i].y = round (hy * y_scale); + + if (clusters && *num_clusters && utf8) + { + memset ((void *) *clusters, 0, *num_clusters * sizeof ((*clusters)[0])); + hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer)); + *cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0; + unsigned int cluster = 0; + const char *start = utf8, *end; + (*clusters)[cluster].num_glyphs++; + if (backward) + { + for (i = *num_glyphs - 2; i >= 0; i--) + { + if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) + { + assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster); + if (utf8_clusters) + end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster; + else + end = (const char *) hb_utf_offset_to_pointer ((const uint8_t *) start, + (signed) (hb_glyph[i].cluster - hb_glyph[i+1].cluster)); + (*clusters)[cluster].num_bytes = end - start; + start = end; + cluster++; + } + (*clusters)[cluster].num_glyphs++; + } + (*clusters)[cluster].num_bytes = utf8 + utf8_len - start; + } + else + { + for (i = 1; i < (int) *num_glyphs; i++) + { + if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) + { + assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster); + if (utf8_clusters) + end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster; + else + end = (const char *) hb_utf_offset_to_pointer ((const uint8_t *) start, + (signed) (hb_glyph[i].cluster - hb_glyph[i-1].cluster)); + (*clusters)[cluster].num_bytes = end - start; + start = end; + cluster++; + } + (*clusters)[cluster].num_glyphs++; + } + (*clusters)[cluster].num_bytes = utf8 + utf8_len - start; + } + } + else if (num_clusters) + *num_clusters = 0; +} + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cairo.h b/src/3rdparty/harfbuzz-ng/src/hb-cairo.h new file mode 100644 index 00000000000..21e284c8f93 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cairo.h @@ -0,0 +1,99 @@ +/* + * Copyright ยฉ 2022 Red Hat, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Matthias Clasen + */ + +#ifndef HB_CAIRO_H +#define HB_CAIRO_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + +HB_EXTERN cairo_font_face_t * +hb_cairo_font_face_create_for_font (hb_font_t *font); + +HB_EXTERN hb_font_t * +hb_cairo_font_face_get_font (cairo_font_face_t *font_face); + +HB_EXTERN cairo_font_face_t * +hb_cairo_font_face_create_for_face (hb_face_t *face); + +HB_EXTERN hb_face_t * +hb_cairo_font_face_get_face (cairo_font_face_t *font_face); + +/** + * hb_cairo_font_init_func_t: + * @font: The #hb_font_t being created + * @scaled_font: The respective #cairo_scaled_font_t + * @user_data: User data accompanying this method + * + * The type of a virtual method to be called when a cairo + * face created using hb_cairo_font_face_create_for_face() + * creates an #hb_font_t for a #cairo_scaled_font_t. + * + * Return value: the #hb_font_t value to use; in most cases same as @font + * + * Since: 7.0.0 + */ +typedef hb_font_t * (*hb_cairo_font_init_func_t) (hb_font_t *font, + cairo_scaled_font_t *scaled_font, + void *user_data); + +HB_EXTERN void +hb_cairo_font_face_set_font_init_func (cairo_font_face_t *font_face, + hb_cairo_font_init_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +HB_EXTERN hb_font_t * +hb_cairo_scaled_font_get_font (cairo_scaled_font_t *scaled_font); + +HB_EXTERN void +hb_cairo_font_face_set_scale_factor (cairo_font_face_t *font_face, + unsigned int scale_factor); + +HB_EXTERN unsigned int +hb_cairo_font_face_get_scale_factor (cairo_font_face_t *font_face); + +HB_EXTERN void +hb_cairo_glyphs_from_buffer (hb_buffer_t *buffer, + hb_bool_t utf8_clusters, + double x_scale_factor, + double y_scale_factor, + double x, + double y, + const char *utf8, + int utf8_len, + cairo_glyph_t **glyphs, + unsigned int *num_glyphs, + cairo_text_cluster_t **clusters, + unsigned int *num_clusters, + cairo_text_cluster_flags_t *cluster_flags); + +HB_END_DECLS + +#endif /* HB_CAIRO_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-common.hh b/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-common.hh new file mode 100644 index 00000000000..949bfebf9be --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-common.hh @@ -0,0 +1,643 @@ +/* + * Copyright ยฉ 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF_INTERP_COMMON_HH +#define HB_CFF_INTERP_COMMON_HH + +namespace CFF { + +using namespace OT; + +typedef unsigned int op_code_t; + + +/* === Dict operators === */ + +/* One byte operators (0-31) */ +#define OpCode_version 0 /* CFF Top */ +#define OpCode_Notice 1 /* CFF Top */ +#define OpCode_FullName 2 /* CFF Top */ +#define OpCode_FamilyName 3 /* CFF Top */ +#define OpCode_Weight 4 /* CFF Top */ +#define OpCode_FontBBox 5 /* CFF Top */ +#define OpCode_BlueValues 6 /* CFF Private, CFF2 Private */ +#define OpCode_OtherBlues 7 /* CFF Private, CFF2 Private */ +#define OpCode_FamilyBlues 8 /* CFF Private, CFF2 Private */ +#define OpCode_FamilyOtherBlues 9 /* CFF Private, CFF2 Private */ +#define OpCode_StdHW 10 /* CFF Private, CFF2 Private */ +#define OpCode_StdVW 11 /* CFF Private, CFF2 Private */ +#define OpCode_escape 12 /* All. Shared with CS */ +#define OpCode_UniqueID 13 /* CFF Top */ +#define OpCode_XUID 14 /* CFF Top */ +#define OpCode_charset 15 /* CFF Top (0) */ +#define OpCode_Encoding 16 /* CFF Top (0) */ +#define OpCode_CharStrings 17 /* CFF Top, CFF2 Top */ +#define OpCode_Private 18 /* CFF Top, CFF2 FD */ +#define OpCode_Subrs 19 /* CFF Private, CFF2 Private */ +#define OpCode_defaultWidthX 20 /* CFF Private (0) */ +#define OpCode_nominalWidthX 21 /* CFF Private (0) */ +#define OpCode_vsindexdict 22 /* CFF2 Private/CS */ +#define OpCode_blenddict 23 /* CFF2 Private/CS */ +#define OpCode_vstore 24 /* CFF2 Top */ +#define OpCode_reserved25 25 +#define OpCode_reserved26 26 +#define OpCode_reserved27 27 + +/* Numbers */ +#define OpCode_shortint 28 /* 16-bit integer, All */ +#define OpCode_longintdict 29 /* 32-bit integer, All */ +#define OpCode_BCD 30 /* Real number, CFF2 Top/FD */ +#define OpCode_reserved31 31 + +/* 1-byte integers */ +#define OpCode_OneByteIntFirst 32 /* All. beginning of the range of first byte ints */ +#define OpCode_OneByteIntLast 246 /* All. ending of the range of first byte int */ + +/* 2-byte integers */ +#define OpCode_TwoBytePosInt0 247 /* All. first byte of two byte positive int (+108 to +1131) */ +#define OpCode_TwoBytePosInt1 248 +#define OpCode_TwoBytePosInt2 249 +#define OpCode_TwoBytePosInt3 250 + +#define OpCode_TwoByteNegInt0 251 /* All. first byte of two byte negative int (-1131 to -108) */ +#define OpCode_TwoByteNegInt1 252 +#define OpCode_TwoByteNegInt2 253 +#define OpCode_TwoByteNegInt3 254 + +/* Two byte escape operators 12, (0-41) */ +#define OpCode_ESC_Base 256 +#define Make_OpCode_ESC(byte2) ((op_code_t)(OpCode_ESC_Base + (byte2))) + +inline op_code_t Unmake_OpCode_ESC (op_code_t op) { return (op_code_t)(op - OpCode_ESC_Base); } +inline bool Is_OpCode_ESC (op_code_t op) { return op >= OpCode_ESC_Base; } +inline unsigned int OpCode_Size (op_code_t op) { return Is_OpCode_ESC (op) ? 2: 1; } + +#define OpCode_Copyright Make_OpCode_ESC(0) /* CFF Top */ +#define OpCode_isFixedPitch Make_OpCode_ESC(1) /* CFF Top (false) */ +#define OpCode_ItalicAngle Make_OpCode_ESC(2) /* CFF Top (0) */ +#define OpCode_UnderlinePosition Make_OpCode_ESC(3) /* CFF Top (-100) */ +#define OpCode_UnderlineThickness Make_OpCode_ESC(4) /* CFF Top (50) */ +#define OpCode_PaintType Make_OpCode_ESC(5) /* CFF Top (0) */ +#define OpCode_CharstringType Make_OpCode_ESC(6) /* CFF Top (2) */ +#define OpCode_FontMatrix Make_OpCode_ESC(7) /* CFF Top, CFF2 Top (.001 0 0 .001 0 0)*/ +#define OpCode_StrokeWidth Make_OpCode_ESC(8) /* CFF Top (0) */ +#define OpCode_BlueScale Make_OpCode_ESC(9) /* CFF Private, CFF2 Private (0.039625) */ +#define OpCode_BlueShift Make_OpCode_ESC(10) /* CFF Private, CFF2 Private (7) */ +#define OpCode_BlueFuzz Make_OpCode_ESC(11) /* CFF Private, CFF2 Private (1) */ +#define OpCode_StemSnapH Make_OpCode_ESC(12) /* CFF Private, CFF2 Private */ +#define OpCode_StemSnapV Make_OpCode_ESC(13) /* CFF Private, CFF2 Private */ +#define OpCode_ForceBold Make_OpCode_ESC(14) /* CFF Private (false) */ +#define OpCode_reservedESC15 Make_OpCode_ESC(15) +#define OpCode_reservedESC16 Make_OpCode_ESC(16) +#define OpCode_LanguageGroup Make_OpCode_ESC(17) /* CFF Private, CFF2 Private (0) */ +#define OpCode_ExpansionFactor Make_OpCode_ESC(18) /* CFF Private, CFF2 Private (0.06) */ +#define OpCode_initialRandomSeed Make_OpCode_ESC(19) /* CFF Private (0) */ +#define OpCode_SyntheticBase Make_OpCode_ESC(20) /* CFF Top */ +#define OpCode_PostScript Make_OpCode_ESC(21) /* CFF Top */ +#define OpCode_BaseFontName Make_OpCode_ESC(22) /* CFF Top */ +#define OpCode_BaseFontBlend Make_OpCode_ESC(23) /* CFF Top */ +#define OpCode_reservedESC24 Make_OpCode_ESC(24) +#define OpCode_reservedESC25 Make_OpCode_ESC(25) +#define OpCode_reservedESC26 Make_OpCode_ESC(26) +#define OpCode_reservedESC27 Make_OpCode_ESC(27) +#define OpCode_reservedESC28 Make_OpCode_ESC(28) +#define OpCode_reservedESC29 Make_OpCode_ESC(29) +#define OpCode_ROS Make_OpCode_ESC(30) /* CFF Top_CID */ +#define OpCode_CIDFontVersion Make_OpCode_ESC(31) /* CFF Top_CID (0) */ +#define OpCode_CIDFontRevision Make_OpCode_ESC(32) /* CFF Top_CID (0) */ +#define OpCode_CIDFontType Make_OpCode_ESC(33) /* CFF Top_CID (0) */ +#define OpCode_CIDCount Make_OpCode_ESC(34) /* CFF Top_CID (8720) */ +#define OpCode_UIDBase Make_OpCode_ESC(35) /* CFF Top_CID */ +#define OpCode_FDArray Make_OpCode_ESC(36) /* CFF Top_CID, CFF2 Top */ +#define OpCode_FDSelect Make_OpCode_ESC(37) /* CFF Top_CID, CFF2 Top */ +#define OpCode_FontName Make_OpCode_ESC(38) /* CFF Top_CID */ + + +/* === CharString operators === */ + +#define OpCode_hstem 1 /* CFF, CFF2 */ +#define OpCode_Reserved2 2 +#define OpCode_vstem 3 /* CFF, CFF2 */ +#define OpCode_vmoveto 4 /* CFF, CFF2 */ +#define OpCode_rlineto 5 /* CFF, CFF2 */ +#define OpCode_hlineto 6 /* CFF, CFF2 */ +#define OpCode_vlineto 7 /* CFF, CFF2 */ +#define OpCode_rrcurveto 8 /* CFF, CFF2 */ +#define OpCode_Reserved9 9 +#define OpCode_callsubr 10 /* CFF, CFF2 */ +#define OpCode_return 11 /* CFF */ +//#define OpCode_escape 12 /* CFF, CFF2 */ +#define OpCode_Reserved13 13 +#define OpCode_endchar 14 /* CFF */ +#define OpCode_vsindexcs 15 /* CFF2 */ +#define OpCode_blendcs 16 /* CFF2 */ +#define OpCode_Reserved17 17 +#define OpCode_hstemhm 18 /* CFF, CFF2 */ +#define OpCode_hintmask 19 /* CFF, CFF2 */ +#define OpCode_cntrmask 20 /* CFF, CFF2 */ +#define OpCode_rmoveto 21 /* CFF, CFF2 */ +#define OpCode_hmoveto 22 /* CFF, CFF2 */ +#define OpCode_vstemhm 23 /* CFF, CFF2 */ +#define OpCode_rcurveline 24 /* CFF, CFF2 */ +#define OpCode_rlinecurve 25 /* CFF, CFF2 */ +#define OpCode_vvcurveto 26 /* CFF, CFF2 */ +#define OpCode_hhcurveto 27 /* CFF, CFF2 */ +//#define OpCode_shortint 28 /* CFF, CFF2 */ +#define OpCode_callgsubr 29 /* CFF, CFF2 */ +#define OpCode_vhcurveto 30 /* CFF, CFF2 */ +#define OpCode_hvcurveto 31 /* CFF, CFF2 */ + +#define OpCode_fixedcs 255 /* 32-bit fixed */ + +/* Two byte escape operators 12, (0-41) */ +#define OpCode_dotsection Make_OpCode_ESC(0) /* CFF (obsoleted) */ +#define OpCode_ReservedESC1 Make_OpCode_ESC(1) +#define OpCode_ReservedESC2 Make_OpCode_ESC(2) +#define OpCode_and Make_OpCode_ESC(3) /* CFF */ +#define OpCode_or Make_OpCode_ESC(4) /* CFF */ +#define OpCode_not Make_OpCode_ESC(5) /* CFF */ +#define OpCode_ReservedESC6 Make_OpCode_ESC(6) +#define OpCode_ReservedESC7 Make_OpCode_ESC(7) +#define OpCode_ReservedESC8 Make_OpCode_ESC(8) +#define OpCode_abs Make_OpCode_ESC(9) /* CFF */ +#define OpCode_add Make_OpCode_ESC(10) /* CFF */ +#define OpCode_sub Make_OpCode_ESC(11) /* CFF */ +#define OpCode_div Make_OpCode_ESC(12) /* CFF */ +#define OpCode_ReservedESC13 Make_OpCode_ESC(13) +#define OpCode_neg Make_OpCode_ESC(14) /* CFF */ +#define OpCode_eq Make_OpCode_ESC(15) /* CFF */ +#define OpCode_ReservedESC16 Make_OpCode_ESC(16) +#define OpCode_ReservedESC17 Make_OpCode_ESC(17) +#define OpCode_drop Make_OpCode_ESC(18) /* CFF */ +#define OpCode_ReservedESC19 Make_OpCode_ESC(19) +#define OpCode_put Make_OpCode_ESC(20) /* CFF */ +#define OpCode_get Make_OpCode_ESC(21) /* CFF */ +#define OpCode_ifelse Make_OpCode_ESC(22) /* CFF */ +#define OpCode_random Make_OpCode_ESC(23) /* CFF */ +#define OpCode_mul Make_OpCode_ESC(24) /* CFF */ +//#define OpCode_reservedESC25 Make_OpCode_ESC(25) +#define OpCode_sqrt Make_OpCode_ESC(26) /* CFF */ +#define OpCode_dup Make_OpCode_ESC(27) /* CFF */ +#define OpCode_exch Make_OpCode_ESC(28) /* CFF */ +#define OpCode_index Make_OpCode_ESC(29) /* CFF */ +#define OpCode_roll Make_OpCode_ESC(30) /* CFF */ +#define OpCode_reservedESC31 Make_OpCode_ESC(31) +#define OpCode_reservedESC32 Make_OpCode_ESC(32) +#define OpCode_reservedESC33 Make_OpCode_ESC(33) +#define OpCode_hflex Make_OpCode_ESC(34) /* CFF, CFF2 */ +#define OpCode_flex Make_OpCode_ESC(35) /* CFF, CFF2 */ +#define OpCode_hflex1 Make_OpCode_ESC(36) /* CFF, CFF2 */ +#define OpCode_flex1 Make_OpCode_ESC(37) /* CFF, CFF2 */ + + +#define OpCode_Invalid 0xFFFFu + + +struct number_t +{ + void set_int (int v) { value = v; } + int to_int () const { return value; } + + void set_fixed (int32_t v) { value = v / 65536.0; } + int32_t to_fixed () const { return value * 65536.0; } + + void set_real (double v) { value = v; } + double to_real () const { return value; } + + bool in_int_range () const + { return ((double) (int16_t) to_int () == value); } + + bool operator > (const number_t &n) const { return value > n.to_real (); } + bool operator < (const number_t &n) const { return n > *this; } + bool operator >= (const number_t &n) const { return !(*this < n); } + bool operator <= (const number_t &n) const { return !(*this > n); } + + const number_t &operator += (const number_t &n) + { + set_real (to_real () + n.to_real ()); + + return *this; + } + + protected: + double value = 0.; +}; + +/* byte string */ +struct UnsizedByteStr : UnsizedArrayOf +{ + hb_ubytes_t as_ubytes (unsigned l) const + { return hb_ubytes_t ((const unsigned char *) this, l); } + + // encode 2-byte int (Dict/CharString) or 4-byte int (Dict) + template + static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, V value) + { + TRACE_SERIALIZE (this); + + HBUINT8 *p = c->allocate_size (1); + if (unlikely (!p)) return_trace (false); + *p = intOp; + + T *ip = c->allocate_size (T::static_size); + if (unlikely (!ip)) return_trace (false); + return_trace (c->check_assign (*ip, value, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + template + static bool serialize_int4 (hb_serialize_context_t *c, V value) + { return serialize_int (c, OpCode_longintdict, value); } + + template + static bool serialize_int2 (hb_serialize_context_t *c, V value) + { return serialize_int (c, OpCode_shortint, value); } + + /* Defining null_size allows a Null object may be created. Should be safe because: + * A descendent struct Dict uses a Null pointer to indicate a missing table, + * checked before access. + */ + DEFINE_SIZE_MIN(0); +}; + +/* A byte string associated with the current offset and an error condition */ +struct byte_str_ref_t +{ + byte_str_ref_t () + : str () {} + + byte_str_ref_t (const hb_ubytes_t &str_, unsigned int offset_ = 0) + : str (str_) { set_offset (offset_); } + + void reset (const hb_ubytes_t &str_, unsigned int offset_ = 0) + { + str = str_; + set_offset (offset_); + } + + const unsigned char& operator [] (int i) { + if (unlikely ((unsigned int) (get_offset () + i) >= str.length)) + { + set_error (); + return Null (unsigned char); + } + return str.arrayZ[get_offset () + i]; + } + + unsigned char head_unchecked () const { return str.arrayZ[get_offset ()]; } + + /* Conversion to hb_ubytes_t */ + operator hb_ubytes_t () const { return str.sub_array (get_offset ()); } + + hb_ubytes_t sub_array (unsigned int offset_, unsigned int len_) const + { return str.sub_array (offset_, len_); } + + bool avail (unsigned int count=1) const + { return get_offset () + count <= str.length; } + void inc (unsigned int count=1) + { + /* Automatically puts us in error if count is out-of-range. */ + set_offset (get_offset () + count); + } + + /* We (ab)use ubytes backwards_length as a cursor (called offset), + * as well as to store error condition. */ + + unsigned get_offset () const { return str.backwards_length; } + void set_offset (unsigned offset) { str.backwards_length = offset; } + + void set_error () { str.backwards_length = str.length + 1; } + bool in_error () const { return str.backwards_length > str.length; } + + unsigned total_size () const { return str.length; } + + protected: + hb_ubytes_t str; +}; + +using byte_str_array_t = hb_vector_t; + +/* stack */ +template +struct cff_stack_t +{ + ELEM& operator [] (unsigned int i) + { + if (unlikely (i >= count)) + { + set_error (); + return Crap (ELEM); + } + return elements[i]; + } + + void push (const ELEM &v) + { + if (likely (count < LIMIT)) + elements[count++] = v; + else + set_error (); + } + ELEM &push () + { + if (likely (count < LIMIT)) + return elements[count++]; + else + { + set_error (); + return Crap (ELEM); + } + } + + ELEM& pop () + { + if (likely (count > 0)) + return elements[--count]; + else + { + set_error (); + return Crap (ELEM); + } + } + void pop (unsigned int n) + { + if (likely (count >= n)) + count -= n; + else + set_error (); + } + + const ELEM& peek () + { + if (unlikely (count == 0)) + { + set_error (); + return Null (ELEM); + } + return elements[count - 1]; + } + + void unpop () + { + if (likely (count < LIMIT)) + count++; + else + set_error (); + } + + void clear () { count = 0; } + + bool in_error () const { return (error); } + void set_error () { error = true; } + + unsigned int get_count () const { return count; } + bool is_empty () const { return !count; } + + hb_array_t sub_array (unsigned start, unsigned length) const + { return hb_array_t (elements).sub_array (start, length); } + + private: + bool error = false; + unsigned int count = 0; + ELEM elements[LIMIT]; +}; + +/* argument stack */ +template +struct arg_stack_t : cff_stack_t +{ + void push_int (int v) + { + ARG &n = S::push (); + n.set_int (v); + } + + void push_fixed (int32_t v) + { + ARG &n = S::push (); + n.set_fixed (v); + } + + void push_real (double v) + { + ARG &n = S::push (); + n.set_real (v); + } + + ARG& pop_num () { return this->pop (); } + + int pop_int () { return this->pop ().to_int (); } + + unsigned int pop_uint () + { + int i = pop_int (); + if (unlikely (i < 0)) + { + i = 0; + S::set_error (); + } + return (unsigned) i; + } + + void push_longint_from_substr (byte_str_ref_t& str_ref) + { + push_int ((str_ref[0] << 24) | (str_ref[1] << 16) | (str_ref[2] << 8) | (str_ref[3])); + str_ref.inc (4); + } + + bool push_fixed_from_substr (byte_str_ref_t& str_ref) + { + if (unlikely (!str_ref.avail (4))) + return false; + push_fixed ((int32_t)*(const HBUINT32*)&str_ref[0]); + str_ref.inc (4); + return true; + } + + private: + typedef cff_stack_t S; +}; + +/* an operator prefixed by its operands in a byte string */ +struct op_str_t +{ + /* This used to have a hb_ubytes_t. Using a pointer and length + * in a particular order, saves 8 bytes in this struct and more + * in our parsed_cs_op_t subclass. */ + + const unsigned char *ptr = nullptr; + + op_code_t op = OpCode_Invalid; + + uint8_t length = 0; +}; + +/* base of OP_SERIALIZER */ +struct op_serializer_t +{ + protected: + bool copy_opstr (hb_serialize_context_t *c, const op_str_t& opstr) const + { + TRACE_SERIALIZE (this); + + unsigned char *d = c->allocate_size (opstr.length); + if (unlikely (!d)) return_trace (false); + /* Faster than hb_memcpy for small strings. */ + for (unsigned i = 0; i < opstr.length; i++) + d[i] = opstr.ptr[i]; + return_trace (true); + } +}; + +template +struct parsed_values_t +{ + void init () + { + opStart = 0; + values.init (); + } + void fini () { values.fini (); } + + void alloc (unsigned n) + { + values.alloc (n, true); + } + + void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t (), const VAL &v = VAL ()) + { + VAL *val = values.push (v); + val->op = op; + auto arr = str_ref.sub_array (opStart, str_ref.get_offset () - opStart); + val->ptr = arr.arrayZ; + val->length = arr.length; + opStart = str_ref.get_offset (); + } + + bool has_op (op_code_t op) const + { + for (const auto& v : values) + if (v.op == op) return true; + return false; + } + + unsigned get_count () const { return values.length; } + const VAL &operator [] (unsigned int i) const { return values[i]; } + + unsigned int opStart; + hb_vector_t values; +}; + +template +struct interp_env_t +{ + interp_env_t () {} + interp_env_t (const hb_ubytes_t &str_) + { + str_ref.reset (str_); + } + bool in_error () const + { return str_ref.in_error () || argStack.in_error (); } + + void set_error () { str_ref.set_error (); } + + op_code_t fetch_op () + { + op_code_t op = OpCode_Invalid; + if (unlikely (!str_ref.avail ())) + return OpCode_Invalid; + op = (op_code_t) str_ref.head_unchecked (); + str_ref.inc (); + if (op == OpCode_escape) { + if (unlikely (!str_ref.avail ())) + return OpCode_Invalid; + op = Make_OpCode_ESC (str_ref.head_unchecked ()); + str_ref.inc (); + } + return op; + } + + const ARG& eval_arg (unsigned int i) { return argStack[i]; } + + ARG& pop_arg () { return argStack.pop (); } + void pop_n_args (unsigned int n) { argStack.pop (n); } + + void clear_args () { pop_n_args (argStack.get_count ()); } + + byte_str_ref_t + str_ref; + arg_stack_t + argStack; +}; + +using num_interp_env_t = interp_env_t<>; + +template +struct opset_t +{ + static void process_op (op_code_t op, interp_env_t& env) + { + switch (op) { + case OpCode_shortint: + env.argStack.push_int ((int16_t)((env.str_ref[0] << 8) | env.str_ref[1])); + env.str_ref.inc (2); + break; + + case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1: + case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3: + env.argStack.push_int ((int16_t)((op - OpCode_TwoBytePosInt0) * 256 + env.str_ref[0] + 108)); + env.str_ref.inc (); + break; + + case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1: + case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3: + env.argStack.push_int ((-(int16_t)(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108)); + env.str_ref.inc (); + break; + + default: + /* 1-byte integer */ + if (likely ((OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast))) + { + env.argStack.push_int ((int)op - 139); + } else { + /* invalid unknown operator */ + env.clear_args (); + env.set_error (); + } + break; + } + } +}; + +template +struct interpreter_t +{ + interpreter_t (ENV& env_) : env (env_) {} + ENV& env; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_COMMON_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-cs-common.hh b/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-cs-common.hh new file mode 100644 index 00000000000..f40be51f0d7 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-cs-common.hh @@ -0,0 +1,907 @@ +/* + * Copyright ยฉ 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF_INTERP_CS_COMMON_HH +#define HB_CFF_INTERP_CS_COMMON_HH + +#include "hb.hh" +#include "hb-cff-interp-common.hh" + +namespace CFF { + +using namespace OT; + +enum cs_type_t { + CSType_CharString, + CSType_GlobalSubr, + CSType_LocalSubr +}; + +struct call_context_t +{ + void init (const byte_str_ref_t substr_=byte_str_ref_t (), cs_type_t type_=CSType_CharString, unsigned int subr_num_=0) + { + str_ref = substr_; + type = type_; + subr_num = subr_num_; + } + + void fini () {} + + byte_str_ref_t str_ref; + cs_type_t type; + unsigned int subr_num; +}; + +/* call stack */ +const unsigned int kMaxCallLimit = 10; +struct call_stack_t : cff_stack_t {}; + +template +struct biased_subrs_t +{ + void init (const SUBRS *subrs_) + { + subrs = subrs_; + unsigned int nSubrs = get_count (); + if (nSubrs < 1240) + bias = 107; + else if (nSubrs < 33900) + bias = 1131; + else + bias = 32768; + } + + void fini () {} + + unsigned int get_count () const { return subrs ? subrs->count : 0; } + unsigned int get_bias () const { return bias; } + + hb_ubytes_t operator [] (unsigned int index) const + { + if (unlikely (!subrs || index >= subrs->count)) + return hb_ubytes_t (); + else + return (*subrs)[index]; + } + + protected: + unsigned int bias; + const SUBRS *subrs; +}; + +struct point_t +{ + void set_int (int _x, int _y) + { + x.set_int (_x); + y.set_int (_y); + } + + void move_x (const number_t &dx) { x += dx; } + void move_y (const number_t &dy) { y += dy; } + void move (const number_t &dx, const number_t &dy) { move_x (dx); move_y (dy); } + void move (const point_t &d) { move_x (d.x); move_y (d.y); } + + number_t x; + number_t y; +}; + +template +struct cs_interp_env_t : interp_env_t +{ + cs_interp_env_t (const hb_ubytes_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_) : + interp_env_t (str) + { + context.init (str, CSType_CharString); + seen_moveto = true; + seen_hintmask = false; + hstem_count = 0; + vstem_count = 0; + hintmask_size = 0; + pt.set_int (0, 0); + globalSubrs.init (globalSubrs_); + localSubrs.init (localSubrs_); + } + ~cs_interp_env_t () + { + globalSubrs.fini (); + localSubrs.fini (); + } + + bool in_error () const + { + return callStack.in_error () || SUPER::in_error (); + } + + bool pop_subr_num (const biased_subrs_t& biasedSubrs, unsigned int &subr_num) + { + subr_num = 0; + int n = SUPER::argStack.pop_int (); + n += biasedSubrs.get_bias (); + if (unlikely ((n < 0) || ((unsigned int)n >= biasedSubrs.get_count ()))) + return false; + + subr_num = (unsigned int)n; + return true; + } + + void call_subr (const biased_subrs_t& biasedSubrs, cs_type_t type) + { + unsigned int subr_num = 0; + + if (unlikely (!pop_subr_num (biasedSubrs, subr_num) + || callStack.get_count () >= kMaxCallLimit)) + { + SUPER::set_error (); + return; + } + context.str_ref = SUPER::str_ref; + callStack.push (context); + + context.init ( biasedSubrs[subr_num], type, subr_num); + SUPER::str_ref = context.str_ref; + } + + void return_from_subr () + { + if (unlikely (SUPER::str_ref.in_error ())) + SUPER::set_error (); + context = callStack.pop (); + SUPER::str_ref = context.str_ref; + } + + void determine_hintmask_size () + { + if (!seen_hintmask) + { + vstem_count += SUPER::argStack.get_count() / 2; + hintmask_size = (hstem_count + vstem_count + 7) >> 3; + seen_hintmask = true; + } + } + + void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; } + bool is_endchar () const { return endchar_flag; } + + const number_t &get_x () const { return pt.x; } + const number_t &get_y () const { return pt.y; } + const point_t &get_pt () const { return pt; } + + void moveto (const point_t &pt_ ) { pt = pt_; } + + public: + call_context_t context; + bool endchar_flag; + bool seen_moveto; + bool seen_hintmask; + + unsigned int hstem_count; + unsigned int vstem_count; + unsigned int hintmask_size; + call_stack_t callStack; + biased_subrs_t globalSubrs; + biased_subrs_t localSubrs; + + private: + point_t pt; + + typedef interp_env_t SUPER; +}; + +template +struct path_procs_null_t +{ + static void rmoveto (ENV &env, PARAM& param) {} + static void hmoveto (ENV &env, PARAM& param) {} + static void vmoveto (ENV &env, PARAM& param) {} + static void rlineto (ENV &env, PARAM& param) {} + static void hlineto (ENV &env, PARAM& param) {} + static void vlineto (ENV &env, PARAM& param) {} + static void rrcurveto (ENV &env, PARAM& param) {} + static void rcurveline (ENV &env, PARAM& param) {} + static void rlinecurve (ENV &env, PARAM& param) {} + static void vvcurveto (ENV &env, PARAM& param) {} + static void hhcurveto (ENV &env, PARAM& param) {} + static void vhcurveto (ENV &env, PARAM& param) {} + static void hvcurveto (ENV &env, PARAM& param) {} + static void moveto (ENV &env, PARAM& param, const point_t &pt) {} + static void line (ENV &env, PARAM& param, const point_t &pt1) {} + static void curve (ENV &env, PARAM& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) {} + static void hflex (ENV &env, PARAM& param) {} + static void flex (ENV &env, PARAM& param) {} + static void hflex1 (ENV &env, PARAM& param) {} + static void flex1 (ENV &env, PARAM& param) {} +}; + +template > +struct cs_opset_t : opset_t +{ + static void process_op (op_code_t op, ENV &env, PARAM& param) + { + switch (op) { + + case OpCode_return: + env.return_from_subr (); + break; + case OpCode_endchar: + OPSET::check_width (op, env, param); + env.set_endchar (true); + OPSET::flush_args_and_op (op, env, param); + break; + + case OpCode_fixedcs: + env.argStack.push_fixed_from_substr (env.str_ref); + break; + + case OpCode_callsubr: + env.call_subr (env.localSubrs, CSType_LocalSubr); + break; + + case OpCode_callgsubr: + env.call_subr (env.globalSubrs, CSType_GlobalSubr); + break; + + case OpCode_hstem: + case OpCode_hstemhm: + OPSET::check_width (op, env, param); + OPSET::process_hstem (op, env, param); + break; + case OpCode_vstem: + case OpCode_vstemhm: + OPSET::check_width (op, env, param); + OPSET::process_vstem (op, env, param); + break; + case OpCode_hintmask: + case OpCode_cntrmask: + OPSET::check_width (op, env, param); + OPSET::process_hintmask (op, env, param); + break; + case OpCode_rmoveto: + OPSET::check_width (op, env, param); + PATH::rmoveto (env, param); + OPSET::process_post_move (op, env, param); + break; + case OpCode_hmoveto: + OPSET::check_width (op, env, param); + PATH::hmoveto (env, param); + OPSET::process_post_move (op, env, param); + break; + case OpCode_vmoveto: + OPSET::check_width (op, env, param); + PATH::vmoveto (env, param); + OPSET::process_post_move (op, env, param); + break; + case OpCode_rlineto: + PATH::rlineto (env, param); + process_post_path (op, env, param); + break; + case OpCode_hlineto: + PATH::hlineto (env, param); + process_post_path (op, env, param); + break; + case OpCode_vlineto: + PATH::vlineto (env, param); + process_post_path (op, env, param); + break; + case OpCode_rrcurveto: + PATH::rrcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_rcurveline: + PATH::rcurveline (env, param); + process_post_path (op, env, param); + break; + case OpCode_rlinecurve: + PATH::rlinecurve (env, param); + process_post_path (op, env, param); + break; + case OpCode_vvcurveto: + PATH::vvcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_hhcurveto: + PATH::hhcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_vhcurveto: + PATH::vhcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_hvcurveto: + PATH::hvcurveto (env, param); + process_post_path (op, env, param); + break; + + case OpCode_hflex: + PATH::hflex (env, param); + OPSET::process_post_flex (op, env, param); + break; + + case OpCode_flex: + PATH::flex (env, param); + OPSET::process_post_flex (op, env, param); + break; + + case OpCode_hflex1: + PATH::hflex1 (env, param); + OPSET::process_post_flex (op, env, param); + break; + + case OpCode_flex1: + PATH::flex1 (env, param); + OPSET::process_post_flex (op, env, param); + break; + + default: + SUPER::process_op (op, env); + break; + } + } + + static void process_hstem (op_code_t op, ENV &env, PARAM& param) + { + env.hstem_count += env.argStack.get_count () / 2; + OPSET::flush_args_and_op (op, env, param); + } + + static void process_vstem (op_code_t op, ENV &env, PARAM& param) + { + env.vstem_count += env.argStack.get_count () / 2; + OPSET::flush_args_and_op (op, env, param); + } + + static void process_hintmask (op_code_t op, ENV &env, PARAM& param) + { + env.determine_hintmask_size (); + if (likely (env.str_ref.avail (env.hintmask_size))) + { + OPSET::flush_hintmask (op, env, param); + env.str_ref.inc (env.hintmask_size); + } + } + + static void process_post_flex (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args_and_op (op, env, param); + } + + static void check_width (op_code_t op, ENV &env, PARAM& param) + {} + + static void process_post_move (op_code_t op, ENV &env, PARAM& param) + { + if (!env.seen_moveto) + { + env.determine_hintmask_size (); + env.seen_moveto = true; + } + OPSET::flush_args_and_op (op, env, param); + } + + static void process_post_path (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args_and_op (op, env, param); + } + + static void flush_args_and_op (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args (env, param); + OPSET::flush_op (op, env, param); + } + + static void flush_args (ENV &env, PARAM& param) + { + env.pop_n_args (env.argStack.get_count ()); + } + + static void flush_op (op_code_t op, ENV &env, PARAM& param) + { + } + + static void flush_hintmask (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args_and_op (op, env, param); + } + + static bool is_number_op (op_code_t op) + { + switch (op) + { + case OpCode_shortint: + case OpCode_fixedcs: + case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1: + case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3: + case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1: + case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3: + return true; + + default: + /* 1-byte integer */ + return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast); + } + } + + protected: + typedef opset_t SUPER; +}; + +template +struct path_procs_t +{ + static void rmoveto (ENV &env, PARAM& param) + { + point_t pt1 = env.get_pt (); + const number_t &dy = env.pop_arg (); + const number_t &dx = env.pop_arg (); + pt1.move (dx, dy); + PATH::moveto (env, param, pt1); + } + + static void hmoveto (ENV &env, PARAM& param) + { + point_t pt1 = env.get_pt (); + pt1.move_x (env.pop_arg ()); + PATH::moveto (env, param, pt1); + } + + static void vmoveto (ENV &env, PARAM& param) + { + point_t pt1 = env.get_pt (); + pt1.move_y (env.pop_arg ()); + PATH::moveto (env, param, pt1); + } + + static void rlineto (ENV &env, PARAM& param) + { + for (unsigned int i = 0; i + 2 <= env.argStack.get_count (); i += 2) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + } + + static void hlineto (ENV &env, PARAM& param) + { + point_t pt1; + unsigned int i = 0; + for (; i + 2 <= env.argStack.get_count (); i += 2) + { + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + PATH::line (env, param, pt1); + pt1.move_y (env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + if (i < env.argStack.get_count ()) + { + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + PATH::line (env, param, pt1); + } + } + + static void vlineto (ENV &env, PARAM& param) + { + point_t pt1; + unsigned int i = 0; + for (; i + 2 <= env.argStack.get_count (); i += 2) + { + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + PATH::line (env, param, pt1); + pt1.move_x (env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + if (i < env.argStack.get_count ()) + { + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + PATH::line (env, param, pt1); + } + } + + static void rrcurveto (ENV &env, PARAM& param) + { + for (unsigned int i = 0; i + 6 <= env.argStack.get_count (); i += 6) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); + } + } + + static void rcurveline (ENV &env, PARAM& param) + { + unsigned int arg_count = env.argStack.get_count (); + if (unlikely (arg_count < 8)) + return; + + unsigned int i = 0; + unsigned int curve_limit = arg_count - 2; + for (; i + 6 <= curve_limit; i += 6) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); + } + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + + static void rlinecurve (ENV &env, PARAM& param) + { + unsigned int arg_count = env.argStack.get_count (); + if (unlikely (arg_count < 8)) + return; + + unsigned int i = 0; + unsigned int line_limit = arg_count - 6; + for (; i + 2 <= line_limit; i += 2) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); + } + + static void vvcurveto (ENV &env, PARAM& param) + { + unsigned int i = 0; + point_t pt1 = env.get_pt (); + if ((env.argStack.get_count () & 1) != 0) + pt1.move_x (env.eval_arg (i++)); + for (; i + 4 <= env.argStack.get_count (); i += 4) + { + pt1.move_y (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + } + } + + static void hhcurveto (ENV &env, PARAM& param) + { + unsigned int i = 0; + point_t pt1 = env.get_pt (); + if ((env.argStack.get_count () & 1) != 0) + pt1.move_y (env.eval_arg (i++)); + for (; i + 4 <= env.argStack.get_count (); i += 4) + { + pt1.move_x (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + } + } + + static void vhcurveto (ENV &env, PARAM& param) + { + point_t pt1, pt2, pt3; + unsigned int i = 0; + if ((env.argStack.get_count () % 8) >= 4) + { + point_t pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + i += 4; + + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_y (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+7)); + } + if (i < env.argStack.get_count ()) + pt3.move_y (env.eval_arg (i)); + PATH::curve (env, param, pt1, pt2, pt3); + } + else + { + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_x (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+7)); + if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0)) + pt3.move_x (env.eval_arg (i+8)); + PATH::curve (env, param, pt1, pt2, pt3); + } + } + } + + static void hvcurveto (ENV &env, PARAM& param) + { + point_t pt1, pt2, pt3; + unsigned int i = 0; + if ((env.argStack.get_count () % 8) >= 4) + { + point_t pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + i += 4; + + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_x (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+7)); + } + if (i < env.argStack.get_count ()) + pt3.move_x (env.eval_arg (i)); + PATH::curve (env, param, pt1, pt2, pt3); + } + else + { + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_y (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+7)); + if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0)) + pt3.move_y (env.eval_arg (i+8)); + PATH::curve (env, param, pt1, pt2, pt3); + } + } + } + + /* default actions to be overridden */ + static void moveto (ENV &env, PARAM& param, const point_t &pt) + { env.moveto (pt); } + + static void line (ENV &env, PARAM& param, const point_t &pt1) + { PATH::moveto (env, param, pt1); } + + static void curve (ENV &env, PARAM& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { PATH::moveto (env, param, pt3); } + + static void hflex (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 7)) + { + point_t pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (0)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (1), env.eval_arg (2)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (3)); + point_t pt4 = pt3; + pt4.move_x (env.eval_arg (4)); + point_t pt5 = pt4; + pt5.move_x (env.eval_arg (5)); + pt5.y = pt1.y; + point_t pt6 = pt5; + pt6.move_x (env.eval_arg (6)); + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + static void flex (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 13)) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (0), env.eval_arg (1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (2), env.eval_arg (3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (4), env.eval_arg (5)); + point_t pt4 = pt3; + pt4.move (env.eval_arg (6), env.eval_arg (7)); + point_t pt5 = pt4; + pt5.move (env.eval_arg (8), env.eval_arg (9)); + point_t pt6 = pt5; + pt6.move (env.eval_arg (10), env.eval_arg (11)); + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + static void hflex1 (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 9)) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (0), env.eval_arg (1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (2), env.eval_arg (3)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (4)); + point_t pt4 = pt3; + pt4.move_x (env.eval_arg (5)); + point_t pt5 = pt4; + pt5.move (env.eval_arg (6), env.eval_arg (7)); + point_t pt6 = pt5; + pt6.move_x (env.eval_arg (8)); + pt6.y = env.get_pt ().y; + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + static void flex1 (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 11)) + { + point_t d; + for (unsigned int i = 0; i < 10; i += 2) + d.move (env.eval_arg (i), env.eval_arg (i+1)); + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (0), env.eval_arg (1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (2), env.eval_arg (3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (4), env.eval_arg (5)); + point_t pt4 = pt3; + pt4.move (env.eval_arg (6), env.eval_arg (7)); + point_t pt5 = pt4; + pt5.move (env.eval_arg (8), env.eval_arg (9)); + point_t pt6 = pt5; + + if (fabs (d.x.to_real ()) > fabs (d.y.to_real ())) + { + pt6.move_x (env.eval_arg (10)); + pt6.y = env.get_pt ().y; + } + else + { + pt6.x = env.get_pt ().x; + pt6.move_y (env.eval_arg (10)); + } + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + protected: + static void curve2 (ENV &env, PARAM& param, + const point_t &pt1, const point_t &pt2, const point_t &pt3, + const point_t &pt4, const point_t &pt5, const point_t &pt6) + { + PATH::curve (env, param, pt1, pt2, pt3); + PATH::curve (env, param, pt4, pt5, pt6); + } +}; + +template +struct cs_interpreter_t : interpreter_t +{ + cs_interpreter_t (ENV& env_) : interpreter_t (env_) {} + + bool interpret (PARAM& param) + { + SUPER::env.set_endchar (false); + + unsigned max_ops = HB_CFF_MAX_OPS; + for (;;) { + if (unlikely (!--max_ops)) + { + SUPER::env.set_error (); + break; + } + OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param); + if (unlikely (SUPER::env.in_error ())) + return false; + if (SUPER::env.is_endchar ()) + break; + } + + return true; + } + + private: + typedef interpreter_t SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_CS_COMMON_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-dict-common.hh b/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-dict-common.hh new file mode 100644 index 00000000000..53226b227e9 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-dict-common.hh @@ -0,0 +1,201 @@ +/* + * Copyright ยฉ 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF_INTERP_DICT_COMMON_HH +#define HB_CFF_INTERP_DICT_COMMON_HH + +#include "hb-cff-interp-common.hh" + +namespace CFF { + +using namespace OT; + +/* an opstr and the parsed out dict value(s) */ +struct dict_val_t : op_str_t +{ + void init () {} + void fini () {} +}; + +typedef dict_val_t num_dict_val_t; + +template struct dict_values_t : parsed_values_t {}; + +template +struct top_dict_values_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + charStringsOffset = 0; + FDArrayOffset = 0; + } + void fini () { dict_values_t::fini (); } + + unsigned int charStringsOffset; + unsigned int FDArrayOffset; +}; + +struct dict_opset_t : opset_t +{ + static void process_op (op_code_t op, interp_env_t& env) + { + switch (op) { + case OpCode_longintdict: /* 5-byte integer */ + env.argStack.push_longint_from_substr (env.str_ref); + break; + + case OpCode_BCD: /* real number */ + env.argStack.push_real (parse_bcd (env.str_ref)); + break; + + default: + opset_t::process_op (op, env); + break; + } + } + + /* Turns CFF's BCD format into strtod understandable string */ + static double parse_bcd (byte_str_ref_t& str_ref) + { + if (unlikely (str_ref.in_error ())) return .0; + + enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END }; + + char buf[32]; + unsigned char byte = 0; + for (unsigned i = 0, count = 0; count < ARRAY_LENGTH (buf); ++i, ++count) + { + unsigned nibble; + if (!(i & 1)) + { + if (unlikely (!str_ref.avail ())) break; + + byte = str_ref[0]; + str_ref.inc (); + nibble = byte >> 4; + } + else + nibble = byte & 0x0F; + + if (unlikely (nibble == RESERVED)) break; + else if (nibble == END) + { + const char *p = buf; + double pv; + if (unlikely (!hb_parse_double (&p, p + count, &pv, true/* whole buffer */))) + break; + return pv; + } + else + { + buf[count] = "0123456789.EE?-?"[nibble]; + if (nibble == EXP_NEG) + { + ++count; + if (unlikely (count == ARRAY_LENGTH (buf))) break; + buf[count] = '-'; + } + } + } + + str_ref.set_error (); + return .0; + } + + static bool is_hint_op (op_code_t op) + { + switch (op) + { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ForceBold: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + return true; + default: + return false; + } + } +}; + +template +struct top_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, interp_env_t& env, top_dict_values_t & dictval) + { + switch (op) { + case OpCode_CharStrings: + dictval.charStringsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FDArray: + dictval.FDArrayOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FontMatrix: + env.clear_args (); + break; + default: + dict_opset_t::process_op (op, env); + break; + } + } +}; + +template +struct dict_interpreter_t : interpreter_t +{ + dict_interpreter_t (ENV& env_) : interpreter_t (env_) {} + + bool interpret (PARAM& param) + { + param.init (); + while (SUPER::env.str_ref.avail ()) + { + OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param); + if (unlikely (SUPER::env.in_error ())) + return false; + } + + return true; + } + + private: + typedef interpreter_t SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_DICT_COMMON_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cff1-interp-cs.hh b/src/3rdparty/harfbuzz-ng/src/hb-cff1-interp-cs.hh new file mode 100644 index 00000000000..d8868efa53f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cff1-interp-cs.hh @@ -0,0 +1,160 @@ +/* + * Copyright ยฉ 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF1_INTERP_CS_HH +#define HB_CFF1_INTERP_CS_HH + +#include "hb.hh" +#include "hb-cff-interp-cs-common.hh" + +namespace CFF { + +using namespace OT; + +typedef biased_subrs_t cff1_biased_subrs_t; + +struct cff1_cs_interp_env_t : cs_interp_env_t +{ + template + cff1_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd, + const int *coords_=nullptr, unsigned int num_coords_=0) + : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs) + { + processed_width = false; + has_width = false; + arg_start = 0; + in_seac = false; + } + + void set_width (bool has_width_) + { + if (likely (!processed_width && (SUPER::argStack.get_count () > 0))) + { + if (has_width_) + { + width = SUPER::argStack[0]; + has_width = true; + arg_start = 1; + } + } + processed_width = true; + } + + void clear_args () + { + arg_start = 0; + SUPER::clear_args (); + } + + void set_in_seac (bool _in_seac) { in_seac = _in_seac; } + + bool processed_width; + bool has_width; + unsigned int arg_start; + number_t width; + bool in_seac; + + private: + typedef cs_interp_env_t SUPER; +}; + +template > +struct cff1_cs_opset_t : cs_opset_t +{ + /* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */ + /* Type 1-originated deprecated opcodes, seac behavior of endchar and dotsection are supported */ + + static void process_op (op_code_t op, cff1_cs_interp_env_t &env, PARAM& param) + { + switch (op) { + case OpCode_dotsection: + SUPER::flush_args_and_op (op, env, param); + break; + + case OpCode_endchar: + OPSET::check_width (op, env, param); + if (env.argStack.get_count () >= 4) + { + OPSET::process_seac (env, param); + } + OPSET::flush_args_and_op (op, env, param); + env.set_endchar (true); + break; + + default: + SUPER::process_op (op, env, param); + } + } + + static void check_width (op_code_t op, cff1_cs_interp_env_t &env, PARAM& param) + { + if (!env.processed_width) + { + bool has_width = false; + switch (op) + { + case OpCode_endchar: + case OpCode_hstem: + case OpCode_hstemhm: + case OpCode_vstem: + case OpCode_vstemhm: + case OpCode_hintmask: + case OpCode_cntrmask: + has_width = ((env.argStack.get_count () & 1) != 0); + break; + case OpCode_hmoveto: + case OpCode_vmoveto: + has_width = (env.argStack.get_count () > 1); + break; + case OpCode_rmoveto: + has_width = (env.argStack.get_count () > 2); + break; + default: + return; + } + env.set_width (has_width); + } + } + + static void process_seac (cff1_cs_interp_env_t &env, PARAM& param) + { + } + + static void flush_args (cff1_cs_interp_env_t &env, PARAM& param) + { + SUPER::flush_args (env, param); + env.clear_args (); /* pop off width */ + } + + private: + typedef cs_opset_t SUPER; +}; + +template +using cff1_cs_interpreter_t = cs_interpreter_t; + +} /* namespace CFF */ + +#endif /* HB_CFF1_INTERP_CS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cff2-interp-cs.hh b/src/3rdparty/harfbuzz-ng/src/hb-cff2-interp-cs.hh new file mode 100644 index 00000000000..915b10cf39a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cff2-interp-cs.hh @@ -0,0 +1,282 @@ +/* + * Copyright ยฉ 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF2_INTERP_CS_HH +#define HB_CFF2_INTERP_CS_HH + +#include "hb.hh" +#include "hb-cff-interp-cs-common.hh" + +namespace CFF { + +using namespace OT; + +struct blend_arg_t : number_t +{ + void set_int (int v) { reset_blends (); number_t::set_int (v); } + void set_fixed (int32_t v) { reset_blends (); number_t::set_fixed (v); } + void set_real (double v) { reset_blends (); number_t::set_real (v); } + + void set_blends (unsigned int numValues_, unsigned int valueIndex_, + hb_array_t blends_) + { + numValues = numValues_; + valueIndex = valueIndex_; + unsigned numBlends = blends_.length; + if (unlikely (!deltas.resize_exact (numBlends))) + return; + for (unsigned int i = 0; i < numBlends; i++) + deltas.arrayZ[i] = blends_.arrayZ[i]; + } + + bool blending () const { return deltas.length > 0; } + void reset_blends () + { + numValues = valueIndex = 0; + deltas.shrink (0); + } + + unsigned int numValues; + unsigned int valueIndex; + hb_vector_t deltas; +}; + +typedef biased_subrs_t cff2_biased_subrs_t; + +template +struct cff2_cs_interp_env_t : cs_interp_env_t +{ + template + cff2_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd, + const int *coords_=nullptr, unsigned int num_coords_=0) + : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs) + { + coords = coords_; + num_coords = num_coords_; + varStore = acc.varStore; + seen_blend = false; + seen_vsindex_ = false; + scalars.init (); + do_blend = num_coords && coords && varStore->size; + set_ivs (acc.privateDicts[fd].ivs); + } + + void fini () + { + scalars.fini (); + SUPER::fini (); + } + + op_code_t fetch_op () + { + if (this->str_ref.avail ()) + return SUPER::fetch_op (); + + /* make up return or endchar op */ + if (this->callStack.is_empty ()) + return OpCode_endchar; + else + return OpCode_return; + } + + const ELEM& eval_arg (unsigned int i) + { + return SUPER::argStack[i]; + } + + const ELEM& pop_arg () + { + return SUPER::argStack.pop (); + } + + void process_blend () + { + if (!seen_blend) + { + region_count = varStore->varStore.get_region_index_count (get_ivs ()); + if (do_blend) + { + if (unlikely (!scalars.resize_exact (region_count))) + SUPER::set_error (); + else + varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords, + &scalars[0], region_count); + } + seen_blend = true; + } + } + + void process_vsindex () + { + unsigned int index = SUPER::argStack.pop_uint (); + if (unlikely (seen_vsindex () || seen_blend)) + { + SUPER::set_error (); + } + else + { + set_ivs (index); + } + seen_vsindex_ = true; + } + + unsigned int get_region_count () const { return region_count; } + void set_region_count (unsigned int region_count_) { region_count = region_count_; } + unsigned int get_ivs () const { return ivs; } + void set_ivs (unsigned int ivs_) { ivs = ivs_; } + bool seen_vsindex () const { return seen_vsindex_; } + + double blend_deltas (hb_array_t deltas) const + { + double v = 0; + if (do_blend) + { + if (likely (scalars.length == deltas.length)) + { + unsigned count = scalars.length; + for (unsigned i = 0; i < count; i++) + v += (double) scalars.arrayZ[i] * deltas.arrayZ[i].to_real (); + } + } + return v; + } + + bool have_coords () const { return num_coords; } + + protected: + const int *coords; + unsigned int num_coords; + const CFF2VariationStore *varStore; + unsigned int region_count; + unsigned int ivs; + hb_vector_t scalars; + bool do_blend; + bool seen_vsindex_; + bool seen_blend; + + typedef cs_interp_env_t SUPER; +}; +template , PARAM>> +struct cff2_cs_opset_t : cs_opset_t, PARAM, PATH> +{ + static void process_op (op_code_t op, cff2_cs_interp_env_t &env, PARAM& param) + { + switch (op) { + case OpCode_callsubr: + case OpCode_callgsubr: + /* a subroutine number shouldn't be a blended value */ +#if 0 + if (unlikely (env.argStack.peek ().blending ())) + { + env.set_error (); + break; + } +#endif + SUPER::process_op (op, env, param); + break; + + case OpCode_blendcs: + OPSET::process_blend (env, param); + break; + + case OpCode_vsindexcs: +#if 0 + if (unlikely (env.argStack.peek ().blending ())) + { + env.set_error (); + break; + } +#endif + OPSET::process_vsindex (env, param); + break; + + default: + SUPER::process_op (op, env, param); + } + } + + template + static void process_arg_blend (cff2_cs_interp_env_t &env, + ELEM &arg, + const hb_array_t blends, + unsigned n, unsigned i) + { + if (env.have_coords ()) + arg.set_int (round (arg.to_real () + env.blend_deltas (blends))); + else + arg.set_blends (n, i, blends); + } + template + static void process_arg_blend (cff2_cs_interp_env_t &env, + ELEM &arg, + const hb_array_t blends, + unsigned n, unsigned i) + { + arg.set_real (arg.to_real () + env.blend_deltas (blends)); + } + + static void process_blend (cff2_cs_interp_env_t &env, PARAM& param) + { + unsigned int n, k; + + env.process_blend (); + k = env.get_region_count (); + n = env.argStack.pop_uint (); + /* copy the blend values into blend array of the default values */ + unsigned int start = env.argStack.get_count () - ((k+1) * n); + /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */ + if (unlikely (start > env.argStack.get_count ())) + { + env.set_error (); + return; + } + for (unsigned int i = 0; i < n; i++) + { + const hb_array_t blends = env.argStack.sub_array (start + n + (i * k), k); + process_arg_blend (env, env.argStack[start + i], blends, n, i); + } + + /* pop off blend values leaving default values now adorned with blend values */ + env.argStack.pop (k * n); + } + + static void process_vsindex (cff2_cs_interp_env_t &env, PARAM& param) + { + env.process_vsindex (); + env.clear_args (); + } + + private: + typedef cs_opset_t, PARAM, PATH> SUPER; +}; + +template +using cff2_cs_interpreter_t = cs_interpreter_t, OPSET, PARAM>; + +} /* namespace CFF */ + +#endif /* HB_CFF2_INTERP_CS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-common.cc b/src/3rdparty/harfbuzz-ng/src/hb-common.cc index cb1fb43ffbb..282a8e4d0f6 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-common.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-common.cc @@ -26,33 +26,54 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-private.hh" +#include "hb.hh" +#include "hb-machinery.hh" -#include "hb-mutex-private.hh" -#include "hb-object-private.hh" -#include -#ifdef HAVE_XLOCALE_H -#include -#endif +/** + * SECTION:hb-common + * @title: hb-common + * @short_description: Common data types + * @include: hb.h + * + * Common data types used across HarfBuzz are defined here. + **/ /* hb_options_t */ -hb_options_union_t _hb_options; +hb_atomic_int_t _hb_options; void -_hb_options_init (void) +_hb_options_init () { hb_options_union_t u; u.i = 0; - u.opts.initialized = 1; + u.opts.initialized = true; - char *c = getenv ("HB_OPTIONS"); - u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible"); + const char *c = getenv ("HB_OPTIONS"); + if (c) + { + while (*c) + { + const char *p = strchr (c, ':'); + if (!p) + p = c + strlen (c); + +#define OPTION(name, symbol) \ + if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast(p - c)) do { u.opts.symbol = true; } while (0) + + OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible); + +#undef OPTION + + c = *p ? p + 1 : p; + } + + } /* This is idempotent and threadsafe. */ - _hb_options = u; + _hb_options = u.i; } @@ -60,12 +81,15 @@ _hb_options_init (void) /** * hb_tag_from_string: - * @str: (array length=len) (element-type uint8_t): - * @len: + * @str: (array length=len) (element-type uint8_t): String to convert + * @len: Length of @str, or -1 if it is `NULL`-terminated * - * + * Converts a string into an #hb_tag_t. Valid tags + * are four characters. Shorter input strings will be + * padded with spaces. Longer input strings will be + * truncated. * - * Return value: + * Return value: The #hb_tag_t corresponding to @str * * Since: 0.9.2 **/ @@ -90,10 +114,11 @@ hb_tag_from_string (const char *str, int len) /** * hb_tag_to_string: - * @tag: - * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): + * @tag: #hb_tag_t to convert + * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string * - * + * Converts an #hb_tag_t to a string and returns it in @buf. + * Strings will be four characters long. * * Since: 0.9.5 **/ @@ -109,7 +134,7 @@ hb_tag_to_string (hb_tag_t tag, char *buf) /* hb_direction_t */ -const char direction_strings[][4] = { +static const char direction_strings[][4] = { "ltr", "rtl", "ttb", @@ -118,12 +143,17 @@ const char direction_strings[][4] = { /** * hb_direction_from_string: - * @str: (array length=len) (element-type uint8_t): - * @len: + * @str: (array length=len) (element-type uint8_t): String to convert + * @len: Length of @str, or -1 if it is `NULL`-terminated * - * + * Converts a string to an #hb_direction_t. * - * Return value: + * Matching is loose and applies only to the first letter. For + * examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR. + * + * Unmatched strings will return #HB_DIRECTION_INVALID. + * + * Return value: The #hb_direction_t matching @str * * Since: 0.9.2 **/ @@ -146,11 +176,11 @@ hb_direction_from_string (const char *str, int len) /** * hb_direction_to_string: - * @direction: + * @direction: The #hb_direction_t to convert * - * + * Converts an #hb_direction_t to a string. * - * Return value: (transfer none): + * Return value: (transfer none): The string corresponding to @direction * * Since: 0.9.2 **/ @@ -176,7 +206,7 @@ static const char canon_map[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0, - '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-', 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0 @@ -219,20 +249,17 @@ struct hb_language_item_t { struct hb_language_item_t *next; hb_language_t lang; - inline bool operator == (const char *s) const { - return lang_equal (lang, s); - } + bool operator == (const char *s) const + { return lang_equal (lang, s); } - inline hb_language_item_t & operator = (const char *s) { - /* If a custom allocated is used calling strdup() pairs - badly with a call to the custom free() in finish() below. - Therefore don't call strdup(), implement its behavior. - */ + hb_language_item_t & operator = (const char *s) + { + /* We can't call strdup(), because we allow custom allocators. */ size_t len = strlen(s) + 1; - lang = (hb_language_t) malloc(len); + lang = (hb_language_t) hb_malloc(len); if (likely (lang)) { - memcpy((unsigned char *) lang, s, len); + hb_memcpy((unsigned char *) lang, s, len); for (unsigned char *p = (unsigned char *) lang; *p; p++) *p = canon_map[*p]; } @@ -240,59 +267,61 @@ struct hb_language_item_t { return *this; } - void finish (void) { free ((void *) lang); } + void fini () { hb_free ((void *) lang); } }; -/* Thread-safe lock-free language list */ +/* Thread-safe lockfree language list */ -static hb_language_item_t *langs; +static hb_atomic_ptr_t langs; -#ifdef HB_USE_ATEXIT -static void -free_langs (void) +static inline void +free_langs () { - while (langs) { - hb_language_item_t *next = langs->next; - langs->finish (); - free (langs); - langs = next; +retry: + hb_language_item_t *first_lang = langs; + if (unlikely (!langs.cmpexch (first_lang, nullptr))) + goto retry; + + while (first_lang) { + hb_language_item_t *next = first_lang->next; + first_lang->fini (); + hb_free (first_lang); + first_lang = next; } } -#endif static hb_language_item_t * lang_find_or_insert (const char *key) { retry: - hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs); + hb_language_item_t *first_lang = langs; for (hb_language_item_t *lang = first_lang; lang; lang = lang->next) if (*lang == key) return lang; /* Not found; allocate one. */ - hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t)); + hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t)); if (unlikely (!lang)) return nullptr; lang->next = first_lang; *lang = key; if (unlikely (!lang->lang)) { - free (lang); + hb_free (lang); return nullptr; } - if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) { - lang->finish (); - free (lang); + if (unlikely (!langs.cmpexch (first_lang, lang))) + { + lang->fini (); + hb_free (lang); goto retry; } -#ifdef HB_USE_ATEXIT if (!first_lang) - atexit (free_langs); /* First person registers atexit() callback. */ -#endif + hb_atexit (free_langs); /* First person registers atexit() callback. */ return lang; } @@ -301,14 +330,14 @@ retry: /** * hb_language_from_string: * @str: (array length=len) (element-type uint8_t): a string representing - * ISOย 639 language code - * @len: length of the @str, or -1 if it is %NULL-terminated. + * a BCP 47 language tag + * @len: length of the @str, or -1 if it is `NULL`-terminated. * - * Converts @str representing an ISOย 639 language code to the corresponding + * Converts @str representing a BCP 47 language tag to the corresponding * #hb_language_t. * * Return value: (transfer none): - * The #hb_language_t corresponding to the ISOย 639 language code. + * The #hb_language_t corresponding to the BCP 47 language tag. * * Since: 0.9.2 **/ @@ -323,8 +352,8 @@ hb_language_from_string (const char *str, int len) { /* NUL-terminate it. */ char strbuf[64]; - len = MIN (len, (int) sizeof (strbuf) - 1); - memcpy (strbuf, str, len); + len = hb_min (len, (int) sizeof (strbuf) - 1); + hb_memcpy (strbuf, str, len); strbuf[len] = '\0'; item = lang_find_or_insert (strbuf); } @@ -336,12 +365,12 @@ hb_language_from_string (const char *str, int len) /** * hb_language_to_string: - * @language: an #hb_language_t to convert. + * @language: The #hb_language_t to convert * - * See hb_language_from_string(). + * Converts an #hb_language_t to a string. * * Return value: (transfer none): - * A %NULL-terminated string representing the @language. Must not be freed by + * A `NULL`-terminated string representing the @language. Must not be freed by * the caller. * * Since: 0.9.2 @@ -349,31 +378,73 @@ hb_language_from_string (const char *str, int len) const char * hb_language_to_string (hb_language_t language) { - /* This is actually nullptr-safe! */ + if (unlikely (!language)) return nullptr; + return language->s; } /** * hb_language_get_default: * - * + * Fetch the default language from current locale. * - * Return value: (transfer none): + * Note that the first time this function is called, it calls + * "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying + * setlocale function is, in many implementations, NOT threadsafe. To avoid + * problems, call this function once before multiple threads can call it. + * This function is only used from hb_buffer_guess_segment_properties() by + * HarfBuzz itself. + * + * Return value: (transfer none): The default language of the locale as + * an #hb_language_t * * Since: 0.9.2 **/ hb_language_t -hb_language_get_default (void) +hb_language_get_default () { - static hb_language_t default_language = HB_LANGUAGE_INVALID; + static hb_atomic_ptr_t default_language; - hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language); - if (unlikely (language == HB_LANGUAGE_INVALID)) { - language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1); - (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language); + hb_language_t language = default_language; + if (unlikely (language == HB_LANGUAGE_INVALID)) + { + language = hb_language_from_string (hb_setlocale (LC_CTYPE, nullptr), -1); + (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language); } - return default_language; + return language; +} + +/** + * hb_language_matches: + * @language: The #hb_language_t to work on + * @specific: Another #hb_language_t + * + * Check whether a second language tag is the same or a more + * specific version of the provided language tag. For example, + * "fa_IR.utf8" is a more specific tag for "fa" or for "fa_IR". + * + * Return value: `true` if languages match, `false` otherwise. + * + * Since: 5.0.0 + **/ +hb_bool_t +hb_language_matches (hb_language_t language, + hb_language_t specific) +{ + if (language == specific) return true; + if (!language || !specific) return false; + + const char *l = language->s; + const char *s = specific->s; + unsigned ll = strlen (l); + unsigned sl = strlen (s); + + if (ll > sl) + return false; + + return strncmp (l, s, ll) == 0 && + (s[ll] == '\0' || s[ll] == '-'); } @@ -381,12 +452,12 @@ hb_language_get_default (void) /** * hb_script_from_iso15924_tag: - * @tag: an #hb_tag_t representing an ISOย 15924 tag. + * @tag: an #hb_tag_t representing an ISO 15924 tag. * - * Converts an ISOย 15924 script tag to a corresponding #hb_script_t. + * Converts an ISO 15924 script tag to a corresponding #hb_script_t. * - * Return value: - * An #hb_script_t corresponding to the ISOย 15924 tag. + * Return value: + * An #hb_script_t corresponding to the ISO 15924 tag. * * Since: 0.9.2 **/ @@ -407,8 +478,13 @@ hb_script_from_iso15924_tag (hb_tag_t tag) case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED; case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC; - /* Script variants from http://unicode.org/iso15924/ */ + /* Script variants from https://unicode.org/iso15924/ */ + case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC; case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC; + case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN; + case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN; + case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN; + case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL; case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN; case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN; case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC; @@ -427,15 +503,15 @@ hb_script_from_iso15924_tag (hb_tag_t tag) /** * hb_script_from_string: * @str: (array length=len) (element-type uint8_t): a string representing an - * ISOย 15924 tag. - * @len: length of the @str, or -1 if it is %NULL-terminated. + * ISO 15924 tag. + * @len: length of the @str, or -1 if it is `NULL`-terminated. * - * Converts a string @str representing an ISOย 15924 script tag to a + * Converts a string @str representing an ISO 15924 script tag to a * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then * hb_script_from_iso15924_tag(). * - * Return value: - * An #hb_script_t corresponding to the ISOย 15924 tag. + * Return value: + * An #hb_script_t corresponding to the ISO 15924 tag. * * Since: 0.9.2 **/ @@ -447,12 +523,12 @@ hb_script_from_string (const char *str, int len) /** * hb_script_to_iso15924_tag: - * @script: an #hb_script_ to convert. + * @script: an #hb_script_t to convert. * - * See hb_script_from_iso15924_tag(). + * Converts an #hb_script_t to a corresponding ISOย 15924 script tag. * * Return value: - * An #hb_tag_t representing an ISOย 15924 script tag. + * An #hb_tag_t representing an ISO 15924 script tag. * * Since: 0.9.2 **/ @@ -464,18 +540,23 @@ hb_script_to_iso15924_tag (hb_script_t script) /** * hb_script_get_horizontal_direction: - * @script: + * @script: The #hb_script_t to query * - * + * Fetches the #hb_direction_t of a script when it is + * set horizontally. All right-to-left scripts will return + * #HB_DIRECTION_RTL. All left-to-right scripts will return + * #HB_DIRECTION_LTR. Scripts that can be written either + * horizontally or vertically will return #HB_DIRECTION_INVALID. + * Unknown scripts will return #HB_DIRECTION_LTR. * - * Return value: + * Return value: The horizontal #hb_direction_t of @script * * Since: 0.9.2 **/ hb_direction_t hb_script_get_horizontal_direction (hb_script_t script) { - /* http://goo.gl/x9ilM */ + /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ switch ((hb_tag_t) script) { /* Unicode-1.1 additions */ @@ -524,57 +605,62 @@ hb_script_get_horizontal_direction (hb_script_t script) case HB_SCRIPT_PSALTER_PAHLAVI: /* Unicode-8.0 additions */ - case HB_SCRIPT_OLD_HUNGARIAN: + case HB_SCRIPT_HATRAN: /* Unicode-9.0 additions */ case HB_SCRIPT_ADLAM: + /* Unicode-11.0 additions */ + case HB_SCRIPT_HANIFI_ROHINGYA: + case HB_SCRIPT_OLD_SOGDIAN: + case HB_SCRIPT_SOGDIAN: + + /* Unicode-12.0 additions */ + case HB_SCRIPT_ELYMAIC: + + /* Unicode-13.0 additions */ + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_YEZIDI: + + /* Unicode-14.0 additions */ + case HB_SCRIPT_OLD_UYGHUR: + return HB_DIRECTION_RTL; + + + /* https://github.com/harfbuzz/harfbuzz/issues/1000 */ + case HB_SCRIPT_OLD_HUNGARIAN: + case HB_SCRIPT_OLD_ITALIC: + case HB_SCRIPT_RUNIC: + case HB_SCRIPT_TIFINAGH: + + return HB_DIRECTION_INVALID; } return HB_DIRECTION_LTR; } -/* hb_user_data_array_t */ - -bool -hb_user_data_array_t::set (hb_user_data_key_t *key, - void * data, - hb_destroy_func_t destroy, - hb_bool_t replace) -{ - if (!key) - return false; - - if (replace) { - if (!data && !destroy) { - items.remove (key, lock); - return true; - } - } - hb_user_data_item_t item = {key, data, destroy}; - bool ret = !!items.replace_or_insert (item, lock, (bool) replace); - - return ret; -} - -void * -hb_user_data_array_t::get (hb_user_data_key_t *key) -{ - hb_user_data_item_t item = {nullptr, nullptr, nullptr}; - - return items.find (key, &item, lock) ? item.data : nullptr; -} - - /* hb_version */ + +/** + * SECTION:hb-version + * @title: hb-version + * @short_description: Information about the version of HarfBuzz in use + * @include: hb.h + * + * These functions and macros allow accessing version of the HarfBuzz + * library used at compile- as well as run-time, and to direct code + * conditionally based on those versions, again, at compile- or run-time. + **/ + + /** * hb_version: - * @major: (out): Library major version component. - * @minor: (out): Library minor version component. - * @micro: (out): Library micro version component. + * @major: (out): Library major version component + * @minor: (out): Library minor version component + * @micro: (out): Library micro version component * * Returns library version as three integer components. * @@ -595,25 +681,27 @@ hb_version (unsigned int *major, * * Returns library version as a string with three components. * - * Return value: library version string. + * Return value: Library version string * * Since: 0.9.2 **/ const char * -hb_version_string (void) +hb_version_string () { return HB_VERSION_STRING; } /** * hb_version_atleast: - * @major: - * @minor: - * @micro: + * @major: Library major version component + * @minor: Library minor version component + * @micro: Library micro version component * - * + * Tests the library version against a minimum value, + * as three integer components. * - * Return value: + * Return value: `true` if the library is equal to or greater than + * the test value, `false` otherwise * * Since: 0.9.30 **/ @@ -652,125 +740,24 @@ parse_char (const char **pp, const char *end, char c) static bool parse_uint (const char **pp, const char *end, unsigned int *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); - strncpy (buf, *pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - unsigned int v; - - /* Intentionally use strtol instead of strtoul, such that - * -1 turns into "big number"... */ - errno = 0; - v = strtol (p, &pend, 0); - if (errno || p == pend) - return false; + /* Intentionally use hb_parse_int inside instead of hb_parse_uint, + * such that -1 turns into "big number"... */ + int v; + if (unlikely (!hb_parse_int (pp, end, &v))) return false; *pv = v; - *pp += pend - p; return true; } static bool parse_uint32 (const char **pp, const char *end, uint32_t *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); - strncpy (buf, *pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - unsigned int v; - - /* Intentionally use strtol instead of strtoul, such that - * -1 turns into "big number"... */ - errno = 0; - v = strtol (p, &pend, 0); - if (errno || p == pend) - return false; + /* Intentionally use hb_parse_int inside instead of hb_parse_uint, + * such that -1 turns into "big number"... */ + int v; + if (unlikely (!hb_parse_int (pp, end, &v))) return false; *pv = v; - *pp += pend - p; - return true; -} - -#if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L) -#define USE_XLOCALE 1 -#define HB_LOCALE_T locale_t -#define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr) -#define HB_FREE_LOCALE(loc) freelocale (loc) -#elif defined(_MSC_VER) -#define USE_XLOCALE 1 -#define HB_LOCALE_T _locale_t -#define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName) -#define HB_FREE_LOCALE(loc) _free_locale (loc) -#define strtod_l(a, b, c) _strtod_l ((a), (b), (c)) -#endif - -#ifdef USE_XLOCALE - -static HB_LOCALE_T C_locale; - -#ifdef HB_USE_ATEXIT -static void -free_C_locale (void) -{ - if (C_locale) - HB_FREE_LOCALE (C_locale); -} -#endif - -static HB_LOCALE_T -get_C_locale (void) -{ -retry: - HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale); - - if (unlikely (!C)) - { - C = HB_CREATE_LOCALE ("C"); - - if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C)) - { - HB_FREE_LOCALE (C_locale); - goto retry; - } - -#ifdef HB_USE_ATEXIT - atexit (free_C_locale); /* First person registers atexit() callback. */ -#endif - } - - return C; -} -#endif - -static bool -parse_float (const char **pp, const char *end, float *pv) -{ - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); - strncpy (buf, *pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - float v; - - errno = 0; -#ifdef USE_XLOCALE - v = strtod_l (p, &pend, get_C_locale ()); -#else - v = strtod (p, &pend); -#endif - if (errno || p == pend) - return false; - - *pv = v; - *pp += pend - p; return true; } @@ -784,9 +771,14 @@ parse_bool (const char **pp, const char *end, uint32_t *pv) (*pp)++; /* CSS allows on/off as aliases 1/0. */ - if (*pp - p == 2 && 0 == strncmp (p, "on", 2)) + if (*pp - p == 2 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'n') *pv = 1; - else if (*pp - p == 3 && 0 == strncmp (p, "off", 3)) + else if (*pp - p == 3 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'f' + && TOLOWER (p[2]) == 'f') *pv = 0; else return false; @@ -823,7 +815,7 @@ parse_tag (const char **pp, const char *end, hb_tag_t *tag) } const char *p = *pp; - while (*pp < end && ISALNUM(**pp)) + while (*pp < end && (ISALNUM(**pp) || **pp == '_')) (*pp)++; if (p == *pp || *pp - p > 4) @@ -852,15 +844,15 @@ parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) bool has_start; - feature->start = 0; - feature->end = (unsigned int) -1; + feature->start = HB_FEATURE_GLOBAL_START; + feature->end = HB_FEATURE_GLOBAL_END; if (!parse_char (pp, end, '[')) return true; has_start = parse_uint (pp, end, &feature->start); - if (parse_char (pp, end, ':')) { + if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) { parse_uint (pp, end, &feature->end); } else { if (has_start) @@ -875,10 +867,10 @@ parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *fea { bool had_equal = parse_char (pp, end, '='); bool had_value = parse_uint32 (pp, end, &feature->value) || - parse_bool (pp, end, &feature->value); + parse_bool (pp, end, &feature->value); /* CSS doesn't use equal-sign between tag and value. * If there was an equal-sign, then there *must* be a value. - * A value without an eqaul-sign is ok, but not required. */ + * A value without an equal-sign is ok, but not required. */ return !had_equal || had_value; } @@ -896,15 +888,49 @@ parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) /** * hb_feature_from_string: * @str: (array length=len) (element-type uint8_t): a string to parse - * @len: length of @str, or -1 if string is %NULL terminated + * @len: length of @str, or -1 if string is `NULL` terminated * @feature: (out): the #hb_feature_t to initialize with the parsed values * * Parses a string into a #hb_feature_t. * - * TODO: document the syntax here. + * The format for specifying feature strings follows. All valid CSS + * font-feature-settings values other than 'normal' and the global values are + * also accepted, though not documented below. CSS string escapes are not + * supported. + * + * The range indices refer to the positions between Unicode characters. The + * position before the first character is always 0. + * + * The format is Python-esque. Here is how it all works: + * + * + * + * + * Syntax Value Start End + * + * + * Setting value: + * kern 1 0 โˆž Turn feature on + * +kern 1 0 โˆž Turn feature on + * -kern 0 0 โˆž Turn feature off + * kern=0 0 0 โˆž Turn feature off + * kern=1 1 0 โˆž Turn feature on + * aalt=2 2 0 โˆž Choose 2nd alternate + * Setting index: + * kern[] 1 0 โˆž Turn feature on + * kern[:] 1 0 โˆž Turn feature on + * kern[5:] 1 5 โˆž Turn feature on, partial + * kern[:5] 1 0 5 Turn feature on, partial + * kern[3:5] 1 3 5 Turn feature on, range + * kern[3] 1 3 3+1 Turn feature on, single char + * Mixing it all: + * aalt[3:5]=2 2 3 5 Turn 2nd alternate on for range + * + * + * * * Return value: - * %true if @str is successfully parsed, %false otherwise. + * `true` if @str is successfully parsed, `false` otherwise * * Since: 0.9.5 **/ @@ -925,7 +951,7 @@ hb_feature_from_string (const char *str, int len, } if (feature) - memset (feature, 0, sizeof (*feature)); + hb_memset (feature, 0, sizeof (*feature)); return false; } @@ -935,7 +961,7 @@ hb_feature_from_string (const char *str, int len, * @buf: (array length=size) (out): output string * @size: the allocated size of @buf * - * Converts a #hb_feature_t into a %NULL-terminated string in the format + * Converts a #hb_feature_t into a `NULL`-terminated string in the format * understood by hb_feature_from_string(). The client in responsible for * allocating big enough size for @buf, 128 bytes is more than enough. * @@ -955,26 +981,26 @@ hb_feature_to_string (hb_feature_t *feature, len += 4; while (len && s[len - 1] == ' ') len--; - if (feature->start != 0 || feature->end != (unsigned int) -1) + if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END) { s[len++] = '['; if (feature->start) - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); if (feature->end != feature->start + 1) { s[len++] = ':'; - if (feature->end != (unsigned int) -1) - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); + if (feature->end != HB_FEATURE_GLOBAL_END) + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); } s[len++] = ']'; } if (feature->value > 1) { s[len++] = '='; - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); } assert (len < ARRAY_LENGTH (s)); - len = MIN (len, size - 1); - memcpy (buf, s, len); + len = hb_min (len, size - 1); + hb_memcpy (buf, s, len); buf[len] = '\0'; } @@ -984,7 +1010,11 @@ static bool parse_variation_value (const char **pp, const char *end, hb_variation_t *variation) { parse_char (pp, end, '='); /* Optional. */ - return parse_float (pp, end, &variation->value); + double v; + if (unlikely (!hb_parse_double (pp, end, &v))) return false; + + variation->value = v; + return true; } static bool @@ -998,6 +1028,21 @@ parse_one_variation (const char **pp, const char *end, hb_variation_t *variation /** * hb_variation_from_string: + * @str: (array length=len) (element-type uint8_t): a string to parse + * @len: length of @str, or -1 if string is `NULL` terminated + * @variation: (out): the #hb_variation_t to initialize with the parsed values + * + * Parses a string into a #hb_variation_t. + * + * The format for specifying variation settings follows. All valid CSS + * font-variation-settings values other than 'normal' and 'inherited' are also + * accepted, though, not documented below. + * + * The format is a tag, optionally followed by an equals sign, followed by a + * number. For example `wght=500`, or `slnt=-7.5`. + * + * Return value: + * `true` if @str is successfully parsed, `false` otherwise * * Since: 1.4.2 */ @@ -1018,12 +1063,60 @@ hb_variation_from_string (const char *str, int len, } if (variation) - memset (variation, 0, sizeof (*variation)); + hb_memset (variation, 0, sizeof (*variation)); return false; } +#ifndef HB_NO_SETLOCALE + +static inline void free_static_C_locale (); + +static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t, + hb_C_locale_lazy_loader_t> +{ + static hb_locale_t create () + { + hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL); + if (!l) + return l; + + hb_atexit (free_static_C_locale); + + return l; + } + static void destroy (hb_locale_t l) + { + freelocale (l); + } + static hb_locale_t get_null () + { + return (hb_locale_t) 0; + } +} static_C_locale; + +static inline +void free_static_C_locale () +{ + static_C_locale.free_instance (); +} + +static hb_locale_t +get_C_locale () +{ + return static_C_locale.get_unconst (); +} + +#endif + /** * hb_variation_to_string: + * @variation: an #hb_variation_t to convert + * @buf: (array length=size) (out caller-allocates): output string + * @size: the allocated size of @buf + * + * Converts an #hb_variation_t into a `NULL`-terminated string in the format + * understood by hb_variation_from_string(). The client in responsible for + * allocating big enough size for @buf, 128 bytes is more than enough. * * Since: 1.4.2 */ @@ -1040,10 +1133,88 @@ hb_variation_to_string (hb_variation_t *variation, while (len && s[len - 1] == ' ') len--; s[len++] = '='; - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value)); + + hb_locale_t oldlocale HB_UNUSED; + oldlocale = hb_uselocale (get_C_locale ()); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value)); + (void) hb_uselocale (oldlocale); assert (len < ARRAY_LENGTH (s)); - len = MIN (len, size - 1); - memcpy (buf, s, len); + len = hb_min (len, size - 1); + hb_memcpy (buf, s, len); buf[len] = '\0'; } + +/** + * hb_color_get_alpha: + * @color: an #hb_color_t we are interested in its channels. + * + * Fetches the alpha channel of the given @color. + * + * Return value: Alpha channel value + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_alpha) (hb_color_t color) +{ + return hb_color_get_alpha (color); +} + +/** + * hb_color_get_red: + * @color: an #hb_color_t we are interested in its channels. + * + * Fetches the red channel of the given @color. + * + * Return value: Red channel value + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_red) (hb_color_t color) +{ + return hb_color_get_red (color); +} + +/** + * hb_color_get_green: + * @color: an #hb_color_t we are interested in its channels. + * + * Fetches the green channel of the given @color. + * + * Return value: Green channel value + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_green) (hb_color_t color) +{ + return hb_color_get_green (color); +} + +/** + * hb_color_get_blue: + * @color: an #hb_color_t we are interested in its channels. + * + * Fetches the blue channel of the given @color. + * + * Return value: Blue channel value + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_blue) (hb_color_t color) +{ + return hb_color_get_blue (color); +} + + +/* If there is no visibility control, then hb-static.cc will NOT + * define anything. Instead, we get it to define one set in here + * only, so only libharfbuzz.so defines them, not other libs. */ +#ifdef HB_NO_VISIBILITY +#undef HB_NO_VISIBILITY +#include "hb-static.cc" +#define HB_NO_VISIBILITY 1 +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-common.h b/src/3rdparty/harfbuzz-ng/src/hb-common.h index 26200ce125b..a5da4e76a32 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-common.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-common.h @@ -26,13 +26,17 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_H_IN +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif #ifndef HB_COMMON_H #define HB_COMMON_H +#ifndef HB_EXTERN +#define HB_EXTERN extern +#endif + #ifndef HB_BEGIN_DECLS # ifdef __cplusplus # define HB_BEGIN_DECLS extern "C" { @@ -49,17 +53,72 @@ # include #elif defined (_AIX) # include +#elif defined (_MSC_VER) && _MSC_VER < 1600 +/* VS 2010 (_MSC_VER 1600) has stdint.h */ +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#elif defined (__KERNEL__) +# include #else # include #endif +#if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define HB_DEPRECATED __attribute__((__deprecated__)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1300) +#define HB_DEPRECATED __declspec(deprecated) +#else +#define HB_DEPRECATED +#endif + +#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define HB_DEPRECATED_FOR(f) __attribute__((__deprecated__("Use '" #f "' instead"))) +#elif defined(_MSC_FULL_VER) && (_MSC_FULL_VER > 140050320) +#define HB_DEPRECATED_FOR(f) __declspec(deprecated("is deprecated. Use '" #f "' instead")) +#else +#define HB_DEPRECATED_FOR(f) HB_DEPRECATED +#endif + + HB_BEGIN_DECLS - +/** + * hb_bool_t: + * + * Data type for booleans. + * + **/ typedef int hb_bool_t; +/** + * hb_codepoint_t: + * + * Data type for holding Unicode codepoints. Also + * used to hold glyph IDs. + * + **/ typedef uint32_t hb_codepoint_t; +/** + * hb_position_t: + * + * Data type for holding a single coordinate value. + * Contour points and other multi-dimensional data are + * stored as tuples of #hb_position_t's. + * + **/ typedef int32_t hb_position_t; +/** + * hb_mask_t: + * + * Data type for bitmasks. + * + **/ typedef uint32_t hb_mask_t; typedef union _hb_var_int_t { @@ -71,16 +130,76 @@ typedef union _hb_var_int_t { int8_t i8[4]; } hb_var_int_t; +typedef union _hb_var_num_t { + float f; + uint32_t u32; + int32_t i32; + uint16_t u16[2]; + int16_t i16[2]; + uint8_t u8[4]; + int8_t i8[4]; +} hb_var_num_t; + /* hb_tag_t */ +/** + * hb_tag_t: + * + * Data type for tag identifiers. Tags are four + * byte integers, each byte representing a character. + * + * Tags are used to identify tables, design-variation axes, + * scripts, languages, font features, and baselines with + * human-readable names. + * + **/ typedef uint32_t hb_tag_t; -#define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint8_t)(c1))<<24)|(((uint8_t)(c2))<<16)|(((uint8_t)(c3))<<8)|((uint8_t)(c4)))) -#define HB_UNTAG(tag) ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag)) +/** + * HB_TAG: + * @c1: 1st character of the tag + * @c2: 2nd character of the tag + * @c3: 3rd character of the tag + * @c4: 4th character of the tag + * + * Constructs an #hb_tag_t from four character literals. + * + **/ +#define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint32_t)(c1)&0xFF)<<24)|(((uint32_t)(c2)&0xFF)<<16)|(((uint32_t)(c3)&0xFF)<<8)|((uint32_t)(c4)&0xFF))) +/** + * HB_UNTAG: + * @tag: an #hb_tag_t + * + * Extracts four character literals from an #hb_tag_t. + * + * Since: 0.6.0 + * + **/ +#define HB_UNTAG(tag) (uint8_t)(((tag)>>24)&0xFF), (uint8_t)(((tag)>>16)&0xFF), (uint8_t)(((tag)>>8)&0xFF), (uint8_t)((tag)&0xFF) + +/** + * HB_TAG_NONE: + * + * Unset #hb_tag_t. + */ #define HB_TAG_NONE HB_TAG(0,0,0,0) +/** + * HB_TAG_MAX: + * + * Maximum possible unsigned #hb_tag_t. + * + * Since: 0.9.26 + */ #define HB_TAG_MAX HB_TAG(0xff,0xff,0xff,0xff) +/** + * HB_TAG_MAX_SIGNED: + * + * Maximum possible signed #hb_tag_t. + * + * Since: 0.9.33 + */ #define HB_TAG_MAX_SIGNED HB_TAG(0x7f,0xff,0xff,0xff) /* len=-1 means str is NUL-terminated. */ @@ -99,6 +218,13 @@ hb_tag_to_string (hb_tag_t tag, char *buf); * @HB_DIRECTION_RTL: Text is set horizontally from right to left. * @HB_DIRECTION_TTB: Text is set vertically from top to bottom. * @HB_DIRECTION_BTT: Text is set vertically from bottom to top. + * + * The direction of a text segment or buffer. + * + * A segment can also be tested for horizontal or vertical + * orientation (irrespective of specific direction) with + * HB_DIRECTION_IS_HORIZONTAL() or HB_DIRECTION_IS_VERTICAL(). + * */ typedef enum { HB_DIRECTION_INVALID = 0, @@ -115,17 +241,71 @@ hb_direction_from_string (const char *str, int len); HB_EXTERN const char * hb_direction_to_string (hb_direction_t direction); +/** + * HB_DIRECTION_IS_VALID: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction is valid. + * + **/ #define HB_DIRECTION_IS_VALID(dir) ((((unsigned int) (dir)) & ~3U) == 4) /* Direction must be valid for the following */ +/** + * HB_DIRECTION_IS_HORIZONTAL: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction is horizontal. Requires + * that the direction be valid. + * + **/ #define HB_DIRECTION_IS_HORIZONTAL(dir) ((((unsigned int) (dir)) & ~1U) == 4) +/** + * HB_DIRECTION_IS_VERTICAL: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction is vertical. Requires + * that the direction be valid. + * + **/ #define HB_DIRECTION_IS_VERTICAL(dir) ((((unsigned int) (dir)) & ~1U) == 6) +/** + * HB_DIRECTION_IS_FORWARD: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction moves forward (from left to right, or from + * top to bottom). Requires that the direction be valid. + * + **/ #define HB_DIRECTION_IS_FORWARD(dir) ((((unsigned int) (dir)) & ~2U) == 4) +/** + * HB_DIRECTION_IS_BACKWARD: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction moves backward (from right to left, or from + * bottom to top). Requires that the direction be valid. + * + **/ #define HB_DIRECTION_IS_BACKWARD(dir) ((((unsigned int) (dir)) & ~2U) == 5) +/** + * HB_DIRECTION_REVERSE: + * @dir: #hb_direction_t to reverse + * + * Reverses a text direction. Requires that the direction + * be valid. + * + **/ #define HB_DIRECTION_REVERSE(dir) ((hb_direction_t) (((unsigned int) (dir)) ^ 1)) /* hb_language_t */ +/** + * hb_language_t: + * + * Data type for languages. Each #hb_language_t corresponds to a BCP 47 + * language tag. + * + */ typedef const struct hb_language_impl_t *hb_language_t; HB_EXTERN hb_language_t @@ -134,198 +314,431 @@ hb_language_from_string (const char *str, int len); HB_EXTERN const char * hb_language_to_string (hb_language_t language); +/** + * HB_LANGUAGE_INVALID: + * + * An unset #hb_language_t. + * + * Since: 0.6.0 + */ #define HB_LANGUAGE_INVALID ((hb_language_t) 0) HB_EXTERN hb_language_t hb_language_get_default (void); +HB_EXTERN hb_bool_t +hb_language_matches (hb_language_t language, + hb_language_t specific); -/* hb_script_t */ +/** + * hb_script_t: + * @HB_SCRIPT_COMMON: `Zyyy` + * @HB_SCRIPT_INHERITED: `Zinh` + * @HB_SCRIPT_UNKNOWN: `Zzzz` + * @HB_SCRIPT_ARABIC: `Arab` + * @HB_SCRIPT_ARMENIAN: `Armn` + * @HB_SCRIPT_BENGALI: `Beng` + * @HB_SCRIPT_CYRILLIC: `Cyrl` + * @HB_SCRIPT_DEVANAGARI: `Deva` + * @HB_SCRIPT_GEORGIAN: `Geor` + * @HB_SCRIPT_GREEK: `Grek` + * @HB_SCRIPT_GUJARATI: `Gujr` + * @HB_SCRIPT_GURMUKHI: `Guru` + * @HB_SCRIPT_HANGUL: `Hang` + * @HB_SCRIPT_HAN: `Hani` + * @HB_SCRIPT_HEBREW: `Hebr` + * @HB_SCRIPT_HIRAGANA: `Hira` + * @HB_SCRIPT_KANNADA: `Knda` + * @HB_SCRIPT_KATAKANA: `Kana` + * @HB_SCRIPT_LAO: `Laoo` + * @HB_SCRIPT_LATIN: `Latn` + * @HB_SCRIPT_MALAYALAM: `Mlym` + * @HB_SCRIPT_ORIYA: `Orya` + * @HB_SCRIPT_TAMIL: `Taml` + * @HB_SCRIPT_TELUGU: `Telu` + * @HB_SCRIPT_THAI: `Thai` + * @HB_SCRIPT_TIBETAN: `Tibt` + * @HB_SCRIPT_BOPOMOFO: `Bopo` + * @HB_SCRIPT_BRAILLE: `Brai` + * @HB_SCRIPT_CANADIAN_SYLLABICS: `Cans` + * @HB_SCRIPT_CHEROKEE: `Cher` + * @HB_SCRIPT_ETHIOPIC: `Ethi` + * @HB_SCRIPT_KHMER: `Khmr` + * @HB_SCRIPT_MONGOLIAN: `Mong` + * @HB_SCRIPT_MYANMAR: `Mymr` + * @HB_SCRIPT_OGHAM: `Ogam` + * @HB_SCRIPT_RUNIC: `Runr` + * @HB_SCRIPT_SINHALA: `Sinh` + * @HB_SCRIPT_SYRIAC: `Syrc` + * @HB_SCRIPT_THAANA: `Thaa` + * @HB_SCRIPT_YI: `Yiii` + * @HB_SCRIPT_DESERET: `Dsrt` + * @HB_SCRIPT_GOTHIC: `Goth` + * @HB_SCRIPT_OLD_ITALIC: `Ital` + * @HB_SCRIPT_BUHID: `Buhd` + * @HB_SCRIPT_HANUNOO: `Hano` + * @HB_SCRIPT_TAGALOG: `Tglg` + * @HB_SCRIPT_TAGBANWA: `Tagb` + * @HB_SCRIPT_CYPRIOT: `Cprt` + * @HB_SCRIPT_LIMBU: `Limb` + * @HB_SCRIPT_LINEAR_B: `Linb` + * @HB_SCRIPT_OSMANYA: `Osma` + * @HB_SCRIPT_SHAVIAN: `Shaw` + * @HB_SCRIPT_TAI_LE: `Tale` + * @HB_SCRIPT_UGARITIC: `Ugar` + * @HB_SCRIPT_BUGINESE: `Bugi` + * @HB_SCRIPT_COPTIC: `Copt` + * @HB_SCRIPT_GLAGOLITIC: `Glag` + * @HB_SCRIPT_KHAROSHTHI: `Khar` + * @HB_SCRIPT_NEW_TAI_LUE: `Talu` + * @HB_SCRIPT_OLD_PERSIAN: `Xpeo` + * @HB_SCRIPT_SYLOTI_NAGRI: `Sylo` + * @HB_SCRIPT_TIFINAGH: `Tfng` + * @HB_SCRIPT_BALINESE: `Bali` + * @HB_SCRIPT_CUNEIFORM: `Xsux` + * @HB_SCRIPT_NKO: `Nkoo` + * @HB_SCRIPT_PHAGS_PA: `Phag` + * @HB_SCRIPT_PHOENICIAN: `Phnx` + * @HB_SCRIPT_CARIAN: `Cari` + * @HB_SCRIPT_CHAM: `Cham` + * @HB_SCRIPT_KAYAH_LI: `Kali` + * @HB_SCRIPT_LEPCHA: `Lepc` + * @HB_SCRIPT_LYCIAN: `Lyci` + * @HB_SCRIPT_LYDIAN: `Lydi` + * @HB_SCRIPT_OL_CHIKI: `Olck` + * @HB_SCRIPT_REJANG: `Rjng` + * @HB_SCRIPT_SAURASHTRA: `Saur` + * @HB_SCRIPT_SUNDANESE: `Sund` + * @HB_SCRIPT_VAI: `Vaii` + * @HB_SCRIPT_AVESTAN: `Avst` + * @HB_SCRIPT_BAMUM: `Bamu` + * @HB_SCRIPT_EGYPTIAN_HIEROGLYPHS: `Egyp` + * @HB_SCRIPT_IMPERIAL_ARAMAIC: `Armi` + * @HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: `Phli` + * @HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: `Prti` + * @HB_SCRIPT_JAVANESE: `Java` + * @HB_SCRIPT_KAITHI: `Kthi` + * @HB_SCRIPT_LISU: `Lisu` + * @HB_SCRIPT_MEETEI_MAYEK: `Mtei` + * @HB_SCRIPT_OLD_SOUTH_ARABIAN: `Sarb` + * @HB_SCRIPT_OLD_TURKIC: `Orkh` + * @HB_SCRIPT_SAMARITAN: `Samr` + * @HB_SCRIPT_TAI_THAM: `Lana` + * @HB_SCRIPT_TAI_VIET: `Tavt` + * @HB_SCRIPT_BATAK: `Batk` + * @HB_SCRIPT_BRAHMI: `Brah` + * @HB_SCRIPT_MANDAIC: `Mand` + * @HB_SCRIPT_CHAKMA: `Cakm` + * @HB_SCRIPT_MEROITIC_CURSIVE: `Merc` + * @HB_SCRIPT_MEROITIC_HIEROGLYPHS: `Mero` + * @HB_SCRIPT_MIAO: `Plrd` + * @HB_SCRIPT_SHARADA: `Shrd` + * @HB_SCRIPT_SORA_SOMPENG: `Sora` + * @HB_SCRIPT_TAKRI: `Takr` + * @HB_SCRIPT_BASSA_VAH: `Bass`, Since: 0.9.30 + * @HB_SCRIPT_CAUCASIAN_ALBANIAN: `Aghb`, Since: 0.9.30 + * @HB_SCRIPT_DUPLOYAN: `Dupl`, Since: 0.9.30 + * @HB_SCRIPT_ELBASAN: `Elba`, Since: 0.9.30 + * @HB_SCRIPT_GRANTHA: `Gran`, Since: 0.9.30 + * @HB_SCRIPT_KHOJKI: `Khoj`, Since: 0.9.30 + * @HB_SCRIPT_KHUDAWADI: `Sind`, Since: 0.9.30 + * @HB_SCRIPT_LINEAR_A: `Lina`, Since: 0.9.30 + * @HB_SCRIPT_MAHAJANI: `Mahj`, Since: 0.9.30 + * @HB_SCRIPT_MANICHAEAN: `Mani`, Since: 0.9.30 + * @HB_SCRIPT_MENDE_KIKAKUI: `Mend`, Since: 0.9.30 + * @HB_SCRIPT_MODI: `Modi`, Since: 0.9.30 + * @HB_SCRIPT_MRO: `Mroo`, Since: 0.9.30 + * @HB_SCRIPT_NABATAEAN: `Nbat`, Since: 0.9.30 + * @HB_SCRIPT_OLD_NORTH_ARABIAN: `Narb`, Since: 0.9.30 + * @HB_SCRIPT_OLD_PERMIC: `Perm`, Since: 0.9.30 + * @HB_SCRIPT_PAHAWH_HMONG: `Hmng`, Since: 0.9.30 + * @HB_SCRIPT_PALMYRENE: `Palm`, Since: 0.9.30 + * @HB_SCRIPT_PAU_CIN_HAU: `Pauc`, Since: 0.9.30 + * @HB_SCRIPT_PSALTER_PAHLAVI: `Phlp`, Since: 0.9.30 + * @HB_SCRIPT_SIDDHAM: `Sidd`, Since: 0.9.30 + * @HB_SCRIPT_TIRHUTA: `Tirh`, Since: 0.9.30 + * @HB_SCRIPT_WARANG_CITI: `Wara`, Since: 0.9.30 + * @HB_SCRIPT_AHOM: `Ahom`, Since: 0.9.30 + * @HB_SCRIPT_ANATOLIAN_HIEROGLYPHS: `Hluw`, Since: 0.9.30 + * @HB_SCRIPT_HATRAN: `Hatr`, Since: 0.9.30 + * @HB_SCRIPT_MULTANI: `Mult`, Since: 0.9.30 + * @HB_SCRIPT_OLD_HUNGARIAN: `Hung`, Since: 0.9.30 + * @HB_SCRIPT_SIGNWRITING: `Sgnw`, Since: 0.9.30 + * @HB_SCRIPT_ADLAM: `Adlm`, Since: 1.3.0 + * @HB_SCRIPT_BHAIKSUKI: `Bhks`, Since: 1.3.0 + * @HB_SCRIPT_MARCHEN: `Marc`, Since: 1.3.0 + * @HB_SCRIPT_OSAGE: `Osge`, Since: 1.3.0 + * @HB_SCRIPT_TANGUT: `Tang`, Since: 1.3.0 + * @HB_SCRIPT_NEWA: `Newa`, Since: 1.3.0 + * @HB_SCRIPT_MASARAM_GONDI: `Gonm`, Since: 1.6.0 + * @HB_SCRIPT_NUSHU: `Nshu`, Since: 1.6.0 + * @HB_SCRIPT_SOYOMBO: `Soyo`, Since: 1.6.0 + * @HB_SCRIPT_ZANABAZAR_SQUARE: `Zanb`, Since: 1.6.0 + * @HB_SCRIPT_DOGRA: `Dogr`, Since: 1.8.0 + * @HB_SCRIPT_GUNJALA_GONDI: `Gong`, Since: 1.8.0 + * @HB_SCRIPT_HANIFI_ROHINGYA: `Rohg`, Since: 1.8.0 + * @HB_SCRIPT_MAKASAR: `Maka`, Since: 1.8.0 + * @HB_SCRIPT_MEDEFAIDRIN: `Medf`, Since: 1.8.0 + * @HB_SCRIPT_OLD_SOGDIAN: `Sogo`, Since: 1.8.0 + * @HB_SCRIPT_SOGDIAN: `Sogd`, Since: 1.8.0 + * @HB_SCRIPT_ELYMAIC: `Elym`, Since: 2.4.0 + * @HB_SCRIPT_NANDINAGARI: `Nand`, Since: 2.4.0 + * @HB_SCRIPT_NYIAKENG_PUACHUE_HMONG: `Hmnp`, Since: 2.4.0 + * @HB_SCRIPT_WANCHO: `Wcho`, Since: 2.4.0 + * @HB_SCRIPT_CHORASMIAN: `Chrs`, Since: 2.6.7 + * @HB_SCRIPT_DIVES_AKURU: `Diak`, Since: 2.6.7 + * @HB_SCRIPT_KHITAN_SMALL_SCRIPT: `Kits`, Since: 2.6.7 + * @HB_SCRIPT_YEZIDI: `Yezi`, Since: 2.6.7 + * @HB_SCRIPT_CYPRO_MINOAN: `Cpmn`, Since: 3.0.0 + * @HB_SCRIPT_OLD_UYGHUR: `Ougr`, Since: 3.0.0 + * @HB_SCRIPT_TANGSA: `Tnsa`, Since: 3.0.0 + * @HB_SCRIPT_TOTO: `Toto`, Since: 3.0.0 + * @HB_SCRIPT_VITHKUQI: `Vith`, Since: 3.0.0 + * @HB_SCRIPT_MATH: `Zmth`, Since: 3.4.0 + * @HB_SCRIPT_KAWI: `Kawi`, Since: 5.2.0 + * @HB_SCRIPT_NAG_MUNDARI: `Nagm`, Since: 5.2.0 + * @HB_SCRIPT_INVALID: No script set + * + * Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding + * to the four-letter values defined by [ISO 15924](https://unicode.org/iso15924/). + * + * See also the Script (sc) property of the Unicode Character Database. + * + **/ -/* http://unicode.org/iso15924/ */ -/* http://goo.gl/x9ilM */ -/* Unicode Character Database property: Script (sc) */ +/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ typedef enum { - /*1.1*/ HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), - /*1.1*/ HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), - /*5.0*/ HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), + HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), /*1.1*/ + HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), /*1.1*/ + HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), /*5.0*/ - /*1.1*/ HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), - /*1.1*/ HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), - /*1.1*/ HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), - /*1.1*/ HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), - /*1.1*/ HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), - /*1.1*/ HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), - /*1.1*/ HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), - /*1.1*/ HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), - /*1.1*/ HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), - /*1.1*/ HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), - /*1.1*/ HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), - /*1.1*/ HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), - /*1.1*/ HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), - /*1.1*/ HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), - /*1.1*/ HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), - /*1.1*/ HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), - /*1.1*/ HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), - /*1.1*/ HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), - /*1.1*/ HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), - /*1.1*/ HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), - /*1.1*/ HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), - /*1.1*/ HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), + HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), /*1.1*/ + HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), /*1.1*/ + HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), /*1.1*/ + HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), /*1.1*/ + HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), /*1.1*/ + HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), /*1.1*/ + HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), /*1.1*/ + HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), /*1.1*/ + HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), /*1.1*/ + HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), /*1.1*/ + HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), /*1.1*/ + HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), /*1.1*/ + HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), /*1.1*/ + HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), /*1.1*/ + HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), /*1.1*/ + HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), /*1.1*/ + HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), /*1.1*/ + HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), /*1.1*/ + HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), /*1.1*/ + HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), /*1.1*/ + HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), /*1.1*/ + HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), /*1.1*/ - /*2.0*/ HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), + HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), /*2.0*/ - /*3.0*/ HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), - /*3.0*/ HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), - /*3.0*/ HB_SCRIPT_CANADIAN_SYLLABICS = HB_TAG ('C','a','n','s'), - /*3.0*/ HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), - /*3.0*/ HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), - /*3.0*/ HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), - /*3.0*/ HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), - /*3.0*/ HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), - /*3.0*/ HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), - /*3.0*/ HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), - /*3.0*/ HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), - /*3.0*/ HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), - /*3.0*/ HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), - /*3.0*/ HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), + HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), /*3.0*/ + HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), /*3.0*/ + HB_SCRIPT_CANADIAN_SYLLABICS = HB_TAG ('C','a','n','s'), /*3.0*/ + HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), /*3.0*/ + HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), /*3.0*/ + HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), /*3.0*/ + HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), /*3.0*/ + HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), /*3.0*/ + HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), /*3.0*/ + HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), /*3.0*/ + HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), /*3.0*/ + HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), /*3.0*/ + HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), /*3.0*/ + HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), /*3.0*/ - /*3.1*/ HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), - /*3.1*/ HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), - /*3.1*/ HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), + HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), /*3.1*/ + HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), /*3.1*/ + HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), /*3.1*/ - /*3.2*/ HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), - /*3.2*/ HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), - /*3.2*/ HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), - /*3.2*/ HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), + HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), /*3.2*/ + HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), /*3.2*/ + HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), /*3.2*/ + HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), /*3.2*/ - /*4.0*/ HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), - /*4.0*/ HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), - /*4.0*/ HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), - /*4.0*/ HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), - /*4.0*/ HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), - /*4.0*/ HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), - /*4.0*/ HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), + HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), /*4.0*/ + HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), /*4.0*/ + HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), /*4.0*/ + HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), /*4.0*/ + HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), /*4.0*/ + HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), /*4.0*/ + HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), /*4.0*/ - /*4.1*/ HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), - /*4.1*/ HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), - /*4.1*/ HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), - /*4.1*/ HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), - /*4.1*/ HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), - /*4.1*/ HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), - /*4.1*/ HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), - /*4.1*/ HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), + HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), /*4.1*/ + HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), /*4.1*/ + HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), /*4.1*/ + HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), /*4.1*/ + HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), /*4.1*/ + HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), /*4.1*/ + HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), /*4.1*/ + HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), /*4.1*/ - /*5.0*/ HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), - /*5.0*/ HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), - /*5.0*/ HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), - /*5.0*/ HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), - /*5.0*/ HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), + HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), /*5.0*/ + HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), /*5.0*/ + HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), /*5.0*/ + HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), /*5.0*/ + HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), /*5.0*/ - /*5.1*/ HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), - /*5.1*/ HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), - /*5.1*/ HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), - /*5.1*/ HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), - /*5.1*/ HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), - /*5.1*/ HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), - /*5.1*/ HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), - /*5.1*/ HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), - /*5.1*/ HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), - /*5.1*/ HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), - /*5.1*/ HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), + HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), /*5.1*/ + HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), /*5.1*/ + HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), /*5.1*/ + HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), /*5.1*/ + HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), /*5.1*/ + HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), /*5.1*/ + HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), /*5.1*/ + HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), /*5.1*/ + HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), /*5.1*/ + HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), /*5.1*/ + HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), /*5.1*/ - /*5.2*/ HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), - /*5.2*/ HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), - /*5.2*/ HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), - /*5.2*/ HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), - /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), - /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), - /*5.2*/ HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), - /*5.2*/ HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), - /*5.2*/ HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), - /*5.2*/ HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), - /*5.2*/ HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), - /*5.2*/ HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), - /*5.2*/ HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), - /*5.2*/ HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), - /*5.2*/ HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), + HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), /*5.2*/ + HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), /*5.2*/ + HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), /*5.2*/ + HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), /*5.2*/ + HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), /*5.2*/ + HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), /*5.2*/ + HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), /*5.2*/ + HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), /*5.2*/ + HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), /*5.2*/ + HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), /*5.2*/ + HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), /*5.2*/ + HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), /*5.2*/ + HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), /*5.2*/ + HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), /*5.2*/ + HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), /*5.2*/ - /*6.0*/ HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), - /*6.0*/ HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), - /*6.0*/ HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), + HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), /*6.0*/ + HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), /*6.0*/ + HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), /*6.0*/ - /*6.1*/ HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), - /*6.1*/ HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), - /*6.1*/ HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), - /*6.1*/ HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), - /*6.1*/ HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), - /*6.1*/ HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), - /*6.1*/ HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), + HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), /*6.1*/ + HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), /*6.1*/ + HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), /*6.1*/ + HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), /*6.1*/ + HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), /*6.1*/ + HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), /*6.1*/ + HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), /*6.1*/ /* * Since: 0.9.30 */ - /*7.0*/ HB_SCRIPT_BASSA_VAH = HB_TAG ('B','a','s','s'), - /*7.0*/ HB_SCRIPT_CAUCASIAN_ALBANIAN = HB_TAG ('A','g','h','b'), - /*7.0*/ HB_SCRIPT_DUPLOYAN = HB_TAG ('D','u','p','l'), - /*7.0*/ HB_SCRIPT_ELBASAN = HB_TAG ('E','l','b','a'), - /*7.0*/ HB_SCRIPT_GRANTHA = HB_TAG ('G','r','a','n'), - /*7.0*/ HB_SCRIPT_KHOJKI = HB_TAG ('K','h','o','j'), - /*7.0*/ HB_SCRIPT_KHUDAWADI = HB_TAG ('S','i','n','d'), - /*7.0*/ HB_SCRIPT_LINEAR_A = HB_TAG ('L','i','n','a'), - /*7.0*/ HB_SCRIPT_MAHAJANI = HB_TAG ('M','a','h','j'), - /*7.0*/ HB_SCRIPT_MANICHAEAN = HB_TAG ('M','a','n','i'), - /*7.0*/ HB_SCRIPT_MENDE_KIKAKUI = HB_TAG ('M','e','n','d'), - /*7.0*/ HB_SCRIPT_MODI = HB_TAG ('M','o','d','i'), - /*7.0*/ HB_SCRIPT_MRO = HB_TAG ('M','r','o','o'), - /*7.0*/ HB_SCRIPT_NABATAEAN = HB_TAG ('N','b','a','t'), - /*7.0*/ HB_SCRIPT_OLD_NORTH_ARABIAN = HB_TAG ('N','a','r','b'), - /*7.0*/ HB_SCRIPT_OLD_PERMIC = HB_TAG ('P','e','r','m'), - /*7.0*/ HB_SCRIPT_PAHAWH_HMONG = HB_TAG ('H','m','n','g'), - /*7.0*/ HB_SCRIPT_PALMYRENE = HB_TAG ('P','a','l','m'), - /*7.0*/ HB_SCRIPT_PAU_CIN_HAU = HB_TAG ('P','a','u','c'), - /*7.0*/ HB_SCRIPT_PSALTER_PAHLAVI = HB_TAG ('P','h','l','p'), - /*7.0*/ HB_SCRIPT_SIDDHAM = HB_TAG ('S','i','d','d'), - /*7.0*/ HB_SCRIPT_TIRHUTA = HB_TAG ('T','i','r','h'), - /*7.0*/ HB_SCRIPT_WARANG_CITI = HB_TAG ('W','a','r','a'), + HB_SCRIPT_BASSA_VAH = HB_TAG ('B','a','s','s'), /*7.0*/ + HB_SCRIPT_CAUCASIAN_ALBANIAN = HB_TAG ('A','g','h','b'), /*7.0*/ + HB_SCRIPT_DUPLOYAN = HB_TAG ('D','u','p','l'), /*7.0*/ + HB_SCRIPT_ELBASAN = HB_TAG ('E','l','b','a'), /*7.0*/ + HB_SCRIPT_GRANTHA = HB_TAG ('G','r','a','n'), /*7.0*/ + HB_SCRIPT_KHOJKI = HB_TAG ('K','h','o','j'), /*7.0*/ + HB_SCRIPT_KHUDAWADI = HB_TAG ('S','i','n','d'), /*7.0*/ + HB_SCRIPT_LINEAR_A = HB_TAG ('L','i','n','a'), /*7.0*/ + HB_SCRIPT_MAHAJANI = HB_TAG ('M','a','h','j'), /*7.0*/ + HB_SCRIPT_MANICHAEAN = HB_TAG ('M','a','n','i'), /*7.0*/ + HB_SCRIPT_MENDE_KIKAKUI = HB_TAG ('M','e','n','d'), /*7.0*/ + HB_SCRIPT_MODI = HB_TAG ('M','o','d','i'), /*7.0*/ + HB_SCRIPT_MRO = HB_TAG ('M','r','o','o'), /*7.0*/ + HB_SCRIPT_NABATAEAN = HB_TAG ('N','b','a','t'), /*7.0*/ + HB_SCRIPT_OLD_NORTH_ARABIAN = HB_TAG ('N','a','r','b'), /*7.0*/ + HB_SCRIPT_OLD_PERMIC = HB_TAG ('P','e','r','m'), /*7.0*/ + HB_SCRIPT_PAHAWH_HMONG = HB_TAG ('H','m','n','g'), /*7.0*/ + HB_SCRIPT_PALMYRENE = HB_TAG ('P','a','l','m'), /*7.0*/ + HB_SCRIPT_PAU_CIN_HAU = HB_TAG ('P','a','u','c'), /*7.0*/ + HB_SCRIPT_PSALTER_PAHLAVI = HB_TAG ('P','h','l','p'), /*7.0*/ + HB_SCRIPT_SIDDHAM = HB_TAG ('S','i','d','d'), /*7.0*/ + HB_SCRIPT_TIRHUTA = HB_TAG ('T','i','r','h'), /*7.0*/ + HB_SCRIPT_WARANG_CITI = HB_TAG ('W','a','r','a'), /*7.0*/ - /*8.0*/ HB_SCRIPT_AHOM = HB_TAG ('A','h','o','m'), - /*8.0*/ HB_SCRIPT_ANATOLIAN_HIEROGLYPHS = HB_TAG ('H','l','u','w'), - /*8.0*/ HB_SCRIPT_HATRAN = HB_TAG ('H','a','t','r'), - /*8.0*/ HB_SCRIPT_MULTANI = HB_TAG ('M','u','l','t'), - /*8.0*/ HB_SCRIPT_OLD_HUNGARIAN = HB_TAG ('H','u','n','g'), - /*8.0*/ HB_SCRIPT_SIGNWRITING = HB_TAG ('S','g','n','w'), + HB_SCRIPT_AHOM = HB_TAG ('A','h','o','m'), /*8.0*/ + HB_SCRIPT_ANATOLIAN_HIEROGLYPHS = HB_TAG ('H','l','u','w'), /*8.0*/ + HB_SCRIPT_HATRAN = HB_TAG ('H','a','t','r'), /*8.0*/ + HB_SCRIPT_MULTANI = HB_TAG ('M','u','l','t'), /*8.0*/ + HB_SCRIPT_OLD_HUNGARIAN = HB_TAG ('H','u','n','g'), /*8.0*/ + HB_SCRIPT_SIGNWRITING = HB_TAG ('S','g','n','w'), /*8.0*/ /* * Since 1.3.0 */ - /*9.0*/ HB_SCRIPT_ADLAM = HB_TAG ('A','d','l','m'), - /*9.0*/ HB_SCRIPT_BHAIKSUKI = HB_TAG ('B','h','k','s'), - /*9.0*/ HB_SCRIPT_MARCHEN = HB_TAG ('M','a','r','c'), - /*9.0*/ HB_SCRIPT_OSAGE = HB_TAG ('O','s','g','e'), - /*9.0*/ HB_SCRIPT_TANGUT = HB_TAG ('T','a','n','g'), - /*9.0*/ HB_SCRIPT_NEWA = HB_TAG ('N','e','w','a'), + HB_SCRIPT_ADLAM = HB_TAG ('A','d','l','m'), /*9.0*/ + HB_SCRIPT_BHAIKSUKI = HB_TAG ('B','h','k','s'), /*9.0*/ + HB_SCRIPT_MARCHEN = HB_TAG ('M','a','r','c'), /*9.0*/ + HB_SCRIPT_OSAGE = HB_TAG ('O','s','g','e'), /*9.0*/ + HB_SCRIPT_TANGUT = HB_TAG ('T','a','n','g'), /*9.0*/ + HB_SCRIPT_NEWA = HB_TAG ('N','e','w','a'), /*9.0*/ /* * Since 1.6.0 */ - /*10.0*/HB_SCRIPT_MASARAM_GONDI = HB_TAG ('G','o','n','m'), - /*10.0*/HB_SCRIPT_NUSHU = HB_TAG ('N','s','h','u'), - /*10.0*/HB_SCRIPT_SOYOMBO = HB_TAG ('S','o','y','o'), - /*10.0*/HB_SCRIPT_ZANABAZAR_SQUARE = HB_TAG ('Z','a','n','b'), + HB_SCRIPT_MASARAM_GONDI = HB_TAG ('G','o','n','m'), /*10.0*/ + HB_SCRIPT_NUSHU = HB_TAG ('N','s','h','u'), /*10.0*/ + HB_SCRIPT_SOYOMBO = HB_TAG ('S','o','y','o'), /*10.0*/ + HB_SCRIPT_ZANABAZAR_SQUARE = HB_TAG ('Z','a','n','b'), /*10.0*/ + + /* + * Since 1.8.0 + */ + HB_SCRIPT_DOGRA = HB_TAG ('D','o','g','r'), /*11.0*/ + HB_SCRIPT_GUNJALA_GONDI = HB_TAG ('G','o','n','g'), /*11.0*/ + HB_SCRIPT_HANIFI_ROHINGYA = HB_TAG ('R','o','h','g'), /*11.0*/ + HB_SCRIPT_MAKASAR = HB_TAG ('M','a','k','a'), /*11.0*/ + HB_SCRIPT_MEDEFAIDRIN = HB_TAG ('M','e','d','f'), /*11.0*/ + HB_SCRIPT_OLD_SOGDIAN = HB_TAG ('S','o','g','o'), /*11.0*/ + HB_SCRIPT_SOGDIAN = HB_TAG ('S','o','g','d'), /*11.0*/ + + /* + * Since 2.4.0 + */ + HB_SCRIPT_ELYMAIC = HB_TAG ('E','l','y','m'), /*12.0*/ + HB_SCRIPT_NANDINAGARI = HB_TAG ('N','a','n','d'), /*12.0*/ + HB_SCRIPT_NYIAKENG_PUACHUE_HMONG = HB_TAG ('H','m','n','p'), /*12.0*/ + HB_SCRIPT_WANCHO = HB_TAG ('W','c','h','o'), /*12.0*/ + + /* + * Since 2.6.7 + */ + HB_SCRIPT_CHORASMIAN = HB_TAG ('C','h','r','s'), /*13.0*/ + HB_SCRIPT_DIVES_AKURU = HB_TAG ('D','i','a','k'), /*13.0*/ + HB_SCRIPT_KHITAN_SMALL_SCRIPT = HB_TAG ('K','i','t','s'), /*13.0*/ + HB_SCRIPT_YEZIDI = HB_TAG ('Y','e','z','i'), /*13.0*/ + + /* + * Since 3.0.0 + */ + HB_SCRIPT_CYPRO_MINOAN = HB_TAG ('C','p','m','n'), /*14.0*/ + HB_SCRIPT_OLD_UYGHUR = HB_TAG ('O','u','g','r'), /*14.0*/ + HB_SCRIPT_TANGSA = HB_TAG ('T','n','s','a'), /*14.0*/ + HB_SCRIPT_TOTO = HB_TAG ('T','o','t','o'), /*14.0*/ + HB_SCRIPT_VITHKUQI = HB_TAG ('V','i','t','h'), /*14.0*/ + + /* + * Since 3.4.0 + */ + HB_SCRIPT_MATH = HB_TAG ('Z','m','t','h'), + + /* + * Since 5.2.0 + */ + HB_SCRIPT_KAWI = HB_TAG ('K','a','w','i'), /*15.0*/ + HB_SCRIPT_NAG_MUNDARI = HB_TAG ('N','a','g','m'), /*15.0*/ /* No script set. */ - HB_SCRIPT_INVALID = HB_TAG_NONE, + HB_SCRIPT_INVALID = HB_TAG_NONE, + + /*< private >*/ /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t - * without risking undefined behavior. Include both a signed and unsigned max, - * since technically enums are int, and indeed, hb_script_t ends up being signed. + * without risking undefined behavior. We have two, for historical reasons. + * HB_TAG_MAX used to be unsigned, but that was invalid Ansi C, so was changed + * to _HB_SCRIPT_MAX_VALUE to be equal to HB_TAG_MAX_SIGNED as well. + * * See this thread for technicalities: * - * http://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html + * https://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html */ - _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX, /*< skip >*/ + _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX_SIGNED, /*< skip >*/ _HB_SCRIPT_MAX_VALUE_SIGNED = HB_TAG_MAX_SIGNED /*< skip >*/ } hb_script_t; @@ -348,16 +761,64 @@ hb_script_get_horizontal_direction (hb_script_t script); /* User data */ +/** + * hb_user_data_key_t: + * + * Data structure for holding user-data keys. + * + **/ typedef struct hb_user_data_key_t { /*< private >*/ char unused; } hb_user_data_key_t; +/** + * hb_destroy_func_t: + * @user_data: the data to be destroyed + * + * A virtual method for destroy user-data callbacks. + * + */ typedef void (*hb_destroy_func_t) (void *user_data); /* Font features and variations. */ +/** + * HB_FEATURE_GLOBAL_START: + * + * Special setting for #hb_feature_t.start to apply the feature from the start + * of the buffer. + * + * Since: 2.0.0 + */ +#define HB_FEATURE_GLOBAL_START 0 + +/** + * HB_FEATURE_GLOBAL_END: + * + * Special setting for #hb_feature_t.end to apply the feature from to the end + * of the buffer. + * + * Since: 2.0.0 + */ +#define HB_FEATURE_GLOBAL_END ((unsigned int) -1) + +/** + * hb_feature_t: + * @tag: The #hb_tag_t tag of the feature + * @value: The value of the feature. 0 disables the feature, non-zero (usually + * 1) enables the feature. For features implemented as lookup type 3 (like + * 'salt') the @value is a one based index into the alternates. + * @start: the cluster to start applying this feature setting (inclusive). + * @end: the cluster to end applying this feature setting (exclusive). + * + * The #hb_feature_t is the structure that holds information about requested + * feature application. The feature will be applied with the given value to all + * glyphs which are in clusters between @start (inclusive) and @end (exclusive). + * Setting start to #HB_FEATURE_GLOBAL_START and end to #HB_FEATURE_GLOBAL_END + * specifies that the feature always applies to the entire buffer. + */ typedef struct hb_feature_t { hb_tag_t tag; uint32_t value; @@ -375,7 +836,13 @@ hb_feature_to_string (hb_feature_t *feature, /** * hb_variation_t: + * @tag: The #hb_tag_t tag of the variation-axis name + * @value: The value of the variation axis * + * Data type for holding variation data. Registered OpenType + * variation-axis tags are listed in + * [OpenType Axis Tag Registry](https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg). + * * Since: 1.4.2 */ typedef struct hb_variation_t { @@ -391,6 +858,70 @@ HB_EXTERN void hb_variation_to_string (hb_variation_t *variation, char *buf, unsigned int size); +/** + * hb_color_t: + * + * Data type for holding color values. Colors are eight bits per + * channel RGB plus alpha transparency. + * + * Since: 2.1.0 + */ +typedef uint32_t hb_color_t; + +/** + * HB_COLOR: + * @b: blue channel value + * @g: green channel value + * @r: red channel value + * @a: alpha channel value + * + * Constructs an #hb_color_t from four integers. + * + * Since: 2.1.0 + */ +#define HB_COLOR(b,g,r,a) ((hb_color_t) HB_TAG ((b),(g),(r),(a))) + +HB_EXTERN uint8_t +hb_color_get_alpha (hb_color_t color); +#define hb_color_get_alpha(color) ((color) & 0xFF) + +HB_EXTERN uint8_t +hb_color_get_red (hb_color_t color); +#define hb_color_get_red(color) (((color) >> 8) & 0xFF) + +HB_EXTERN uint8_t +hb_color_get_green (hb_color_t color); +#define hb_color_get_green(color) (((color) >> 16) & 0xFF) + +HB_EXTERN uint8_t +hb_color_get_blue (hb_color_t color); +#define hb_color_get_blue(color) (((color) >> 24) & 0xFF) + +/** + * hb_glyph_extents_t: + * @x_bearing: Distance from the x-origin to the left extremum of the glyph. + * @y_bearing: Distance from the top extremum of the glyph to the y-origin. + * @width: Distance from the left extremum of the glyph to the right extremum. + * @height: Distance from the top extremum of the glyph to the bottom extremum. + * + * Glyph extent values, measured in font units. + * + * Note that @height is negative, in coordinate systems that grow up. + **/ +typedef struct hb_glyph_extents_t { + hb_position_t x_bearing; + hb_position_t y_bearing; + hb_position_t width; + hb_position_t height; +} hb_glyph_extents_t; + +/** + * hb_font_t: + * + * Data type for holding fonts. + * + */ +typedef struct hb_font_t hb_font_t; HB_END_DECLS diff --git a/src/3rdparty/harfbuzz-ng/src/hb-config.hh b/src/3rdparty/harfbuzz-ng/src/hb-config.hh new file mode 100644 index 00000000000..52adaad4384 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-config.hh @@ -0,0 +1,195 @@ +/* + * Copyright ยฉ 2019 Facebook, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_CONFIG_HH +#define HB_CONFIG_HH + +#if 0 /* Make test happy. */ +#include "hb.hh" +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef HB_EXPERIMENTAL_API +#define HB_NO_BEYOND_64K +#define HB_NO_CUBIC_GLYF +#define HB_NO_VAR_COMPOSITES +#endif + +#ifdef HB_TINY +#define HB_LEAN +#define HB_MINI +#define HB_NO_MT +#define HB_NO_UCD_UNASSIGNED +#ifndef NDEBUG +#define NDEBUG +#endif +#ifndef __OPTIMIZE_SIZE__ +#define __OPTIMIZE_SIZE__ +#endif +#endif + +#ifdef HB_LEAN +#define HB_DISABLE_DEPRECATED +#define HB_NDEBUG +#define HB_NO_ATEXIT +#define HB_NO_BUFFER_MESSAGE +#define HB_NO_BUFFER_SERIALIZE +#define HB_NO_BUFFER_VERIFY +#define HB_NO_BITMAP +#define HB_NO_CFF +#define HB_NO_COLOR +#define HB_NO_DRAW +#define HB_NO_ERRNO +#define HB_NO_FACE_COLLECT_UNICODES +#define HB_NO_GETENV +#define HB_NO_HINTING +#define HB_NO_LANGUAGE_LONG +#define HB_NO_LANGUAGE_PRIVATE_SUBTAG +#define HB_NO_LAYOUT_FEATURE_PARAMS +#define HB_NO_LAYOUT_COLLECT_GLYPHS +#define HB_NO_LAYOUT_RARELY_USED +#define HB_NO_LAYOUT_UNUSED +#define HB_NO_MATH +#define HB_NO_META +#define HB_NO_METRICS +#define HB_NO_MMAP +#define HB_NO_NAME +#define HB_NO_OPEN +#define HB_NO_OT_FONT_GLYPH_NAMES +#define HB_NO_OT_SHAPE_FRACTIONS +#define HB_NO_PAINT +#define HB_NO_SETLOCALE +#define HB_NO_STYLE +#define HB_NO_SUBSET_LAYOUT +#define HB_NO_VERTICAL +#define HB_NO_VAR +#endif + +#ifdef HB_MINI +#define HB_NO_AAT +#define HB_NO_LEGACY +#define HB_NO_BORING_EXPANSION +#endif + +#if defined(HAVE_CONFIG_OVERRIDE_H) || defined(HB_CONFIG_OVERRIDE_H) +#ifndef HB_CONFIG_OVERRIDE_H +#define HB_CONFIG_OVERRIDE_H "config-override.h" +#endif +#include HB_CONFIG_OVERRIDE_H +#endif + +/* Closure of options. */ + +#ifdef HB_NO_BORING_EXPANSION +#define HB_NO_BEYOND_64K +#define HB_NO_AVAR2 +#endif + +#ifdef HB_DISABLE_DEPRECATED +#define HB_IF_NOT_DEPRECATED(x) +#else +#define HB_IF_NOT_DEPRECATED(x) x +#endif + +#ifdef HB_NO_SHAPER +#define HB_NO_OT_SHAPE +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_AAT +#define HB_NO_OT_NAME_LANGUAGE_AAT +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_BITMAP +#define HB_NO_OT_FONT_BITMAP +#endif + +#ifdef HB_NO_CFF +#define HB_NO_OT_FONT_CFF +#define HB_NO_SUBSET_CFF +#endif + +#ifdef HB_NO_DRAW +#define HB_NO_OUTLINE +#endif + +#ifdef HB_NO_GETENV +#define HB_NO_UNISCRIBE_BUG_COMPATIBLE +#endif + +#ifdef HB_NO_LEGACY +#define HB_NO_CMAP_LEGACY_SUBTABLES +#define HB_NO_FALLBACK_SHAPE +#define HB_NO_OT_KERN +#define HB_NO_OT_LAYOUT_BLOCKLIST +#define HB_NO_OT_SHAPE_FALLBACK +#endif + +#ifdef HB_NO_NAME +#define HB_NO_OT_NAME_LANGUAGE +#endif + +#ifdef HB_NO_OT +#define HB_NO_OT_FONT +#define HB_NO_OT_LAYOUT +#define HB_NO_OT_TAG +#define HB_NO_OT_SHAPE +#endif + +#ifdef HB_NO_OT_SHAPE +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_OT_SHAPE_FALLBACK +#define HB_NO_OT_SHAPER_ARABIC_FALLBACK +#define HB_NO_OT_SHAPER_HEBREW_FALLBACK +#define HB_NO_OT_SHAPER_THAI_FALLBACK +#define HB_NO_OT_SHAPER_VOWEL_CONSTRAINTS +#define HB_NO_OT_SHAPER_MYANMAR_ZAWGYI +#endif + +#ifdef NDEBUG +#ifndef HB_NDEBUG +#define HB_NDEBUG +#endif +#endif + +#ifdef __OPTIMIZE_SIZE__ +#ifndef HB_OPTIMIZE_SIZE +#define HB_OPTIMIZE_SIZE +#endif +#endif + +#ifdef HB_OPTIMIZE_SIZE +#define HB_NO_OT_LAYOUT_LOOKUP_CACHE +#endif + + +#endif /* HB_CONFIG_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-coretext.mm b/src/3rdparty/harfbuzz-ng/src/hb-coretext.cc similarity index 58% rename from src/3rdparty/harfbuzz-ng/src/hb-coretext.mm rename to src/3rdparty/harfbuzz-ng/src/hb-coretext.cc index 185f73cd681..a87cb5cd028 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-coretext.mm +++ b/src/3rdparty/harfbuzz-ng/src/hb-coretext.cc @@ -26,29 +26,24 @@ * Google Author(s): Behdad Esfahbod */ -#define HB_SHAPER coretext +#include "hb.hh" -#include "hb-private.hh" -#include "hb-debug.hh" -#include "hb-shaper-impl-private.hh" +#ifdef HAVE_CORETEXT + +#include "hb-shaper-impl.hh" #include "hb-coretext.h" -#include +#include "hb-aat-layout.hh" -#include - -typedef bool (*qt_get_font_table_func_t) (void *user_data, unsigned int tag, unsigned char *buffer, unsigned int *length); - -struct FontEngineFaceData { - void *user_data; - qt_get_font_table_func_t get_font_table; -}; - -struct CoreTextFontEngineData { - CTFontRef ctFont; - CGFontRef cgFont; -}; +/** + * SECTION:hb-coretext + * @title: hb-coretext + * @short_description: CoreText integration + * @include: hb-coretext.h + * + * Functions for using HarfBuzz with the CoreText fonts. + **/ /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */ #define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f @@ -61,7 +56,7 @@ release_table_data (void *user_data) } static hb_blob_t * -reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +_hb_cg_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) { CGFontRef cg_font = reinterpret_cast (user_data); CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag); @@ -71,7 +66,10 @@ reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) const char *data = reinterpret_cast (CFDataGetBytePtr (cf_data)); const size_t length = CFDataGetLength (cf_data); if (!data || !length) + { + CFRelease (cf_data); return nullptr; + } return hb_blob_create (data, length, HB_MEMORY_MODE_READONLY, reinterpret_cast (const_cast<__CFData *> (cf_data)), @@ -85,13 +83,8 @@ _hb_cg_font_release (void *data) } -HB_SHAPER_DATA_ENSURE_DEFINE(coretext, face) -HB_SHAPER_DATA_ENSURE_DEFINE_WITH_CONDITION(coretext, font, - fabs (CTFontGetSize ((CTFontRef) data) - font->ptem) <= .5 -) - static CTFontDescriptorRef -get_last_resort_font_desc (void) +get_last_resort_font_desc () { // TODO Handle allocation failures? CTFontDescriptorRef last_resort = CTFontDescriptorCreateWithNameAndSize (CFSTR("LastResort"), 0); @@ -117,7 +110,7 @@ static void release_data (void *info, const void *data, size_t size) { assert (hb_blob_get_length ((hb_blob_t *) info) == size && - hb_blob_get_data ((hb_blob_t *) info, nullptr) == data); + hb_blob_get_data ((hb_blob_t *) info, nullptr) == data); hb_blob_destroy ((hb_blob_t *) info); } @@ -132,7 +125,6 @@ create_cg_font (hb_face_t *face) } else { -#if 0 hb_blob_t *blob = hb_face_reference_blob (face); unsigned int blob_length; const char *blob_data = hb_blob_get_data (blob, &blob_length); @@ -147,11 +139,6 @@ create_cg_font (hb_face_t *face) DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); CGDataProviderRelease (provider); } -#else - FontEngineFaceData *fontEngineFaceData = (FontEngineFaceData *) face->user_data; - CoreTextFontEngineData *coreTextFontEngineData = (CoreTextFontEngineData *) fontEngineFaceData->user_data; - cg_font = CGFontRetain (coreTextFontEngineData->cgFont); -#endif } return cg_font; } @@ -168,6 +155,10 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) if (CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSText")) || CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSDisplay"))) { +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1080 +# define kCTFontUIFontSystem kCTFontSystemFontType +# define kCTFontUIFontEmphasizedSystem kCTFontEmphasizedSystemFontType +#endif CTFontUIFontType font_type = kCTFontUIFontSystem; if (CFStringHasSuffix (cg_postscript_name, CFSTR ("-Bold"))) font_type = kCTFontUIFontEmphasizedSystem; @@ -198,7 +189,10 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) * reconfiguring the cascade list causes CoreText crashes. For details, see * crbug.com/549610 */ // 0x00070000 stands for "kCTVersionNumber10_10", see CoreText.h +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" if (&CTGetCoreTextVersion != nullptr && CTGetCoreTextVersion() < 0x00070000) { +#pragma GCC diagnostic pop CFStringRef fontName = CTFontCopyPostScriptName (ct_font); bool isEmojiFont = CFStringCompare (fontName, CFSTR("AppleColorEmoji"), 0) == kCFCompareEqualTo; CFRelease (fontName); @@ -206,7 +200,18 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) return ct_font; } - CFURLRef original_url = (CFURLRef)CTFontCopyAttribute(ct_font, kCTFontURLAttribute); + CFURLRef original_url = nullptr; +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + ATSFontRef atsFont; + FSRef fsref; + OSStatus status; + atsFont = CTFontGetPlatformFont (ct_font, NULL); + status = ATSFontGetFileReference (atsFont, &fsref); + if (status == noErr) + original_url = CFURLCreateFromFSRef (NULL, &fsref); +#else + original_url = (CFURLRef) CTFontCopyAttribute (ct_font, kCTFontURLAttribute); +#endif /* Create font copy with cascade list that has LastResort first; this speeds up CoreText * font fallback which we don't need anyway. */ @@ -225,18 +230,26 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) * system locations that we cannot access from the sandboxed renderer * process in Blink. This can be detected by the new file URL location * that the newly found font points to. */ - CFURLRef new_url = (CFURLRef) CTFontCopyAttribute (new_ct_font, kCTFontURLAttribute); + CFURLRef new_url = nullptr; +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + atsFont = CTFontGetPlatformFont (new_ct_font, NULL); + status = ATSFontGetFileReference (atsFont, &fsref); + if (status == noErr) + new_url = CFURLCreateFromFSRef (NULL, &fsref); +#else + new_url = (CFURLRef) CTFontCopyAttribute (new_ct_font, kCTFontURLAttribute); +#endif // Keep reconfigured font if URL cannot be retrieved (seems to be the case // on Mac OS 10.12 Sierra), speculative fix for crbug.com/625606 if (!original_url || !new_url || CFEqual (original_url, new_url)) { - CFRelease (ct_font); - ct_font = new_ct_font; + CFRelease (ct_font); + ct_font = new_ct_font; } else { - CFRelease (new_ct_font); - DEBUG_MSG (CORETEXT, ct_font, "Discarding reconfigured CTFont, location changed."); + CFRelease (new_ct_font); + DEBUG_MSG (CORETEXT, ct_font, "Discarding reconfigured CTFont, location changed."); } if (new_url) - CFRelease (new_url); + CFRelease (new_url); } else DEBUG_MSG (CORETEXT, ct_font, "Font copy with empty cascade list failed"); @@ -247,7 +260,7 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) return ct_font; } -hb_coretext_shaper_face_data_t * +hb_coretext_face_data_t * _hb_coretext_shaper_face_data_create (hb_face_t *face) { CGFontRef cg_font = create_cg_font (face); @@ -258,40 +271,59 @@ _hb_coretext_shaper_face_data_create (hb_face_t *face) return nullptr; } - return (hb_coretext_shaper_face_data_t *) cg_font; + return (hb_coretext_face_data_t *) cg_font; } void -_hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) +_hb_coretext_shaper_face_data_destroy (hb_coretext_face_data_t *data) { CFRelease ((CGFontRef) data); } +/** + * hb_coretext_face_create: + * @cg_font: The CGFontRef to work upon + * + * Creates an #hb_face_t face object from the specified + * CGFontRef. + * + * Return value: the new #hb_face_t face object + * + * Since: 0.9.10 + */ hb_face_t * hb_coretext_face_create (CGFontRef cg_font) { - return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), _hb_cg_font_release); + return hb_face_create_for_tables (_hb_cg_reference_table, CGFontRetain (cg_font), _hb_cg_font_release); } -/* +/** + * hb_coretext_face_get_cg_font: + * @face: The #hb_face_t to work upon + * + * Fetches the CGFontRef associated with an #hb_face_t + * face object + * + * Return value: the CGFontRef found + * * Since: 0.9.10 */ CGFontRef hb_coretext_face_get_cg_font (hb_face_t *face) { - if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr; - return (CGFontRef) HB_SHAPER_DATA_GET (face); + return (CGFontRef) (const void *) face->data.coretext; } -hb_coretext_shaper_font_data_t * +hb_coretext_font_data_t * _hb_coretext_shaper_font_data_create (hb_font_t *font) { hb_face_t *face = font->face; - if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr; - CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face); + const hb_coretext_face_data_t *face_data = face->data.coretext; + if (unlikely (!face_data)) return nullptr; + CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; - CGFloat font_size = font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem; + CGFloat font_size = (CGFloat) (font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem); CTFontRef ct_font = create_ct_font (cg_font, font_size); if (unlikely (!ct_font)) @@ -300,66 +332,103 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font) return nullptr; } - return (hb_coretext_shaper_font_data_t *) ct_font; + if (font->num_coords) + { + CFMutableDictionaryRef variations = + CFDictionaryCreateMutable (kCFAllocatorDefault, + font->num_coords, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + for (unsigned i = 0; i < font->num_coords; i++) + { + if (font->coords[i] == 0.) continue; + + hb_ot_var_axis_info_t info; + unsigned int c = 1; + hb_ot_var_get_axis_infos (font->face, i, &c, &info); + float v = hb_clamp (font->design_coords[i], info.min_value, info.max_value); + + CFNumberRef tag_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag); + CFNumberRef value_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberFloatType, &v); + CFDictionarySetValue (variations, tag_number, value_number); + CFRelease (tag_number); + CFRelease (value_number); + } + + CFDictionaryRef attributes = + CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontVariationAttribute, + (const void **) &variations, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + CTFontDescriptorRef varDesc = CTFontDescriptorCreateWithAttributes (attributes); + CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0, nullptr, varDesc); + + CFRelease (ct_font); + CFRelease (attributes); + CFRelease (variations); + ct_font = new_ct_font; + } + + return (hb_coretext_font_data_t *) ct_font; } void -_hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data) +_hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data) { CFRelease ((CTFontRef) data); } -/* +/** + * hb_coretext_font_create: + * @ct_font: The CTFontRef to work upon + * + * Creates an #hb_font_t font object from the specified + * CTFontRef. + * + * Return value: the new #hb_font_t font object + * * Since: 1.7.2 - */ + **/ hb_font_t * hb_coretext_font_create (CTFontRef ct_font) { - CGFontRef cg_font = CTFontCopyGraphicsFont (ct_font, 0); + CGFontRef cg_font = CTFontCopyGraphicsFont (ct_font, nullptr); hb_face_t *face = hb_coretext_face_create (cg_font); CFRelease (cg_font); hb_font_t *font = hb_font_create (face); hb_face_destroy (face); - if (unlikely (hb_object_is_inert (font))) + if (unlikely (hb_object_is_immutable (font))) return font; hb_font_set_ptem (font, CTFontGetSize (ct_font)); /* Let there be dragons here... */ - HB_SHAPER_DATA_GET (font) = (hb_coretext_shaper_font_data_t *) CFRetain (ct_font); + font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font)); return font; } +/** + * hb_coretext_font_get_ct_font: + * @font: #hb_font_t to work upon + * + * Fetches the CTFontRef associated with the specified + * #hb_font_t font object. + * + * Return value: the CTFontRef found + * + * Since: 0.9.10 + */ CTFontRef hb_coretext_font_get_ct_font (hb_font_t *font) { - if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return nullptr; - return (CTFontRef) HB_SHAPER_DATA_GET (font); -} - - - -/* - * shaper shape_plan data - */ - -struct hb_coretext_shaper_shape_plan_data_t {}; - -hb_coretext_shaper_shape_plan_data_t * -_hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, - const hb_feature_t *user_features HB_UNUSED, - unsigned int num_user_features HB_UNUSED, - const int *coords HB_UNUSED, - unsigned int num_coords HB_UNUSED) -{ - return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; -} - -void -_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data HB_UNUSED) -{ + CTFontRef ct_font = (CTFontRef) (const void *) font->data.coretext; + return ct_font ? (CTFontRef) ct_font : nullptr; } @@ -376,7 +445,7 @@ struct active_feature_t { feature_record_t rec; unsigned int order; - static int cmp (const void *pa, const void *pb) { + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const active_feature_t *a = (const active_feature_t *) pa; const active_feature_t *b = (const active_feature_t *) pb; return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 : @@ -384,8 +453,8 @@ struct active_feature_t { a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 : 0; } - bool operator== (const active_feature_t *f) { - return cmp (this, f) == 0; + bool operator== (const active_feature_t& f) const { + return cmp (this, &f) == 0; } }; @@ -394,7 +463,7 @@ struct feature_event_t { bool start; active_feature_t feature; - static int cmp (const void *pa, const void *pb) { + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const feature_event_t *a = (const feature_event_t *) pa; const feature_event_t *b = (const feature_event_t *) pb; return a->index < b->index ? -1 : a->index > b->index ? 1 : @@ -410,200 +479,23 @@ struct range_record_t { }; -/* The following enum members are added in OS X 10.8. */ -#define kAltHalfWidthTextSelector 6 -#define kAltProportionalTextSelector 5 -#define kAlternateHorizKanaOffSelector 1 -#define kAlternateHorizKanaOnSelector 0 -#define kAlternateKanaType 34 -#define kAlternateVertKanaOffSelector 3 -#define kAlternateVertKanaOnSelector 2 -#define kCaseSensitiveLayoutOffSelector 1 -#define kCaseSensitiveLayoutOnSelector 0 -#define kCaseSensitiveLayoutType 33 -#define kCaseSensitiveSpacingOffSelector 3 -#define kCaseSensitiveSpacingOnSelector 2 -#define kContextualAlternatesOffSelector 1 -#define kContextualAlternatesOnSelector 0 -#define kContextualAlternatesType 36 -#define kContextualLigaturesOffSelector 19 -#define kContextualLigaturesOnSelector 18 -#define kContextualSwashAlternatesOffSelector 5 -#define kContextualSwashAlternatesOnSelector 4 -#define kDefaultLowerCaseSelector 0 -#define kDefaultUpperCaseSelector 0 -#define kHistoricalLigaturesOffSelector 21 -#define kHistoricalLigaturesOnSelector 20 -#define kHojoCharactersSelector 12 -#define kJIS2004CharactersSelector 11 -#define kLowerCasePetiteCapsSelector 2 -#define kLowerCaseSmallCapsSelector 1 -#define kLowerCaseType 37 -#define kMathematicalGreekOffSelector 11 -#define kMathematicalGreekOnSelector 10 -#define kNLCCharactersSelector 13 -#define kQuarterWidthTextSelector 4 -#define kScientificInferiorsSelector 4 -#define kStylisticAltEightOffSelector 17 -#define kStylisticAltEightOnSelector 16 -#define kStylisticAltEighteenOffSelector 37 -#define kStylisticAltEighteenOnSelector 36 -#define kStylisticAltElevenOffSelector 23 -#define kStylisticAltElevenOnSelector 22 -#define kStylisticAltFifteenOffSelector 31 -#define kStylisticAltFifteenOnSelector 30 -#define kStylisticAltFiveOffSelector 11 -#define kStylisticAltFiveOnSelector 10 -#define kStylisticAltFourOffSelector 9 -#define kStylisticAltFourOnSelector 8 -#define kStylisticAltFourteenOffSelector 29 -#define kStylisticAltFourteenOnSelector 28 -#define kStylisticAltNineOffSelector 19 -#define kStylisticAltNineOnSelector 18 -#define kStylisticAltNineteenOffSelector 39 -#define kStylisticAltNineteenOnSelector 38 -#define kStylisticAltOneOffSelector 3 -#define kStylisticAltOneOnSelector 2 -#define kStylisticAltSevenOffSelector 15 -#define kStylisticAltSevenOnSelector 14 -#define kStylisticAltSeventeenOffSelector 35 -#define kStylisticAltSeventeenOnSelector 34 -#define kStylisticAltSixOffSelector 13 -#define kStylisticAltSixOnSelector 12 -#define kStylisticAltSixteenOffSelector 33 -#define kStylisticAltSixteenOnSelector 32 -#define kStylisticAltTenOffSelector 21 -#define kStylisticAltTenOnSelector 20 -#define kStylisticAltThirteenOffSelector 27 -#define kStylisticAltThirteenOnSelector 26 -#define kStylisticAltThreeOffSelector 7 -#define kStylisticAltThreeOnSelector 6 -#define kStylisticAltTwelveOffSelector 25 -#define kStylisticAltTwelveOnSelector 24 -#define kStylisticAltTwentyOffSelector 41 -#define kStylisticAltTwentyOnSelector 40 -#define kStylisticAltTwoOffSelector 5 -#define kStylisticAltTwoOnSelector 4 -#define kStylisticAlternativesType 35 -#define kSwashAlternatesOffSelector 3 -#define kSwashAlternatesOnSelector 2 -#define kThirdWidthTextSelector 3 -#define kTraditionalNamesCharactersSelector 14 -#define kUpperCasePetiteCapsSelector 2 -#define kUpperCaseSmallCapsSelector 1 -#define kUpperCaseType 38 - -/* Table data courtesy of Apple. */ -static const struct feature_mapping_t { - FourCharCode otFeatureTag; - uint16_t aatFeatureType; - uint16_t selectorToEnable; - uint16_t selectorToDisable; -} feature_mappings[] = { - { 'c2pc', kUpperCaseType, kUpperCasePetiteCapsSelector, kDefaultUpperCaseSelector }, - { 'c2sc', kUpperCaseType, kUpperCaseSmallCapsSelector, kDefaultUpperCaseSelector }, - { 'calt', kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector }, - { 'case', kCaseSensitiveLayoutType, kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOffSelector }, - { 'clig', kLigaturesType, kContextualLigaturesOnSelector, kContextualLigaturesOffSelector }, - { 'cpsp', kCaseSensitiveLayoutType, kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOffSelector }, - { 'cswh', kContextualAlternatesType, kContextualSwashAlternatesOnSelector, kContextualSwashAlternatesOffSelector }, - { 'dlig', kLigaturesType, kRareLigaturesOnSelector, kRareLigaturesOffSelector }, - { 'expt', kCharacterShapeType, kExpertCharactersSelector, 16 }, - { 'frac', kFractionsType, kDiagonalFractionsSelector, kNoFractionsSelector }, - { 'fwid', kTextSpacingType, kMonospacedTextSelector, 7 }, - { 'halt', kTextSpacingType, kAltHalfWidthTextSelector, 7 }, - { 'hist', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector }, - { 'hkna', kAlternateKanaType, kAlternateHorizKanaOnSelector, kAlternateHorizKanaOffSelector, }, - { 'hlig', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector }, - { 'hngl', kTransliterationType, kHanjaToHangulSelector, kNoTransliterationSelector }, - { 'hojo', kCharacterShapeType, kHojoCharactersSelector, 16 }, - { 'hwid', kTextSpacingType, kHalfWidthTextSelector, 7 }, - { 'ital', kItalicCJKRomanType, kCJKItalicRomanOnSelector, kCJKItalicRomanOffSelector }, - { 'jp04', kCharacterShapeType, kJIS2004CharactersSelector, 16 }, - { 'jp78', kCharacterShapeType, kJIS1978CharactersSelector, 16 }, - { 'jp83', kCharacterShapeType, kJIS1983CharactersSelector, 16 }, - { 'jp90', kCharacterShapeType, kJIS1990CharactersSelector, 16 }, - { 'liga', kLigaturesType, kCommonLigaturesOnSelector, kCommonLigaturesOffSelector }, - { 'lnum', kNumberCaseType, kUpperCaseNumbersSelector, 2 }, - { 'mgrk', kMathematicalExtrasType, kMathematicalGreekOnSelector, kMathematicalGreekOffSelector }, - { 'nlck', kCharacterShapeType, kNLCCharactersSelector, 16 }, - { 'onum', kNumberCaseType, kLowerCaseNumbersSelector, 2 }, - { 'ordn', kVerticalPositionType, kOrdinalsSelector, kNormalPositionSelector }, - { 'palt', kTextSpacingType, kAltProportionalTextSelector, 7 }, - { 'pcap', kLowerCaseType, kLowerCasePetiteCapsSelector, kDefaultLowerCaseSelector }, - { 'pkna', kTextSpacingType, kProportionalTextSelector, 7 }, - { 'pnum', kNumberSpacingType, kProportionalNumbersSelector, 4 }, - { 'pwid', kTextSpacingType, kProportionalTextSelector, 7 }, - { 'qwid', kTextSpacingType, kQuarterWidthTextSelector, 7 }, - { 'ruby', kRubyKanaType, kRubyKanaOnSelector, kRubyKanaOffSelector }, - { 'sinf', kVerticalPositionType, kScientificInferiorsSelector, kNormalPositionSelector }, - { 'smcp', kLowerCaseType, kLowerCaseSmallCapsSelector, kDefaultLowerCaseSelector }, - { 'smpl', kCharacterShapeType, kSimplifiedCharactersSelector, 16 }, - { 'ss01', kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector }, - { 'ss02', kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector }, - { 'ss03', kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector }, - { 'ss04', kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector }, - { 'ss05', kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector }, - { 'ss06', kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector }, - { 'ss07', kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector }, - { 'ss08', kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector }, - { 'ss09', kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector }, - { 'ss10', kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector }, - { 'ss11', kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector }, - { 'ss12', kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector }, - { 'ss13', kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector }, - { 'ss14', kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector }, - { 'ss15', kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector }, - { 'ss16', kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector }, - { 'ss17', kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector }, - { 'ss18', kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector }, - { 'ss19', kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector }, - { 'ss20', kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector }, - { 'subs', kVerticalPositionType, kInferiorsSelector, kNormalPositionSelector }, - { 'sups', kVerticalPositionType, kSuperiorsSelector, kNormalPositionSelector }, - { 'swsh', kContextualAlternatesType, kSwashAlternatesOnSelector, kSwashAlternatesOffSelector }, - { 'titl', kStyleOptionsType, kTitlingCapsSelector, kNoStyleOptionsSelector }, - { 'tnam', kCharacterShapeType, kTraditionalNamesCharactersSelector, 16 }, - { 'tnum', kNumberSpacingType, kMonospacedNumbersSelector, 4 }, - { 'trad', kCharacterShapeType, kTraditionalCharactersSelector, 16 }, - { 'twid', kTextSpacingType, kThirdWidthTextSelector, 7 }, - { 'unic', kLetterCaseType, 14, 15 }, - { 'valt', kTextSpacingType, kAltProportionalTextSelector, 7 }, - { 'vert', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector }, - { 'vhal', kTextSpacingType, kAltHalfWidthTextSelector, 7 }, - { 'vkna', kAlternateKanaType, kAlternateVertKanaOnSelector, kAlternateVertKanaOffSelector }, - { 'vpal', kTextSpacingType, kAltProportionalTextSelector, 7 }, - { 'vrt2', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector }, - { 'zero', kTypographicExtrasType, kSlashedZeroOnSelector, kSlashedZeroOffSelector }, -}; - -static int -_hb_feature_mapping_cmp (const void *key_, const void *entry_) -{ - unsigned int key = * (unsigned int *) key_; - const feature_mapping_t * entry = (const feature_mapping_t *) entry_; - return key < entry->otFeatureTag ? -1 : - key > entry->otFeatureTag ? 1 : - 0; -} - hb_bool_t _hb_coretext_shape (hb_shape_plan_t *shape_plan, hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features) + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) { hb_face_t *face = font->face; - CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face); - CTFontRef ct_font = (CTFontRef) HB_SHAPER_DATA_GET (font); + CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; + CTFontRef ct_font = (CTFontRef) (const void *) font->data.coretext; CGFloat ct_font_size = CTFontGetSize (ct_font); CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size; CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size; /* Attach marks to their bases, to match the 'ot' shaper. - * Adapted from hb-ot-shape:hb_form_clusters(). + * Adapted from a very old version of hb-ot-shape:hb_form_clusters(). * Note that this only makes us be closer to the 'ot' shaper, * but by no means the same. For example, if there's * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will @@ -619,8 +511,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, buffer->merge_clusters (i - 1, i + 1); } - hb_auto_array_t feature_records; - hb_auto_array_t range_records; + hb_vector_t range_records; /* * Set up features. @@ -629,34 +520,32 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, if (num_features) { /* Sort features by start/end events. */ - hb_auto_array_t feature_events; + hb_vector_t feature_events; for (unsigned int i = 0; i < num_features; i++) { - const feature_mapping_t * mapping = (const feature_mapping_t *) bsearch (&features[i].tag, - feature_mappings, - ARRAY_LENGTH (feature_mappings), - sizeof (feature_mappings[0]), - _hb_feature_mapping_cmp); - if (!mapping) - continue; - active_feature_t feature; + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 + const hb_aat_feature_mapping_t * mapping = hb_aat_layout_find_feature_mapping (features[i].tag); + if (!mapping) + continue; + feature.rec.feature = mapping->aatFeatureType; feature.rec.setting = features[i].value ? mapping->selectorToEnable : mapping->selectorToDisable; +#else + feature.rec.feature = features[i].tag; + feature.rec.setting = features[i].value; +#endif feature.order = i; feature_event_t *event; event = feature_events.push (); - if (unlikely (!event)) - goto fail_features; event->index = features[i].start; event->start = true; event->feature = feature; event = feature_events.push (); - if (unlikely (!event)) - goto fail_features; event->index = features[i].end; event->start = false; event->feature = feature; @@ -670,35 +559,32 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, feature.order = num_features + 1; feature_event_t *event = feature_events.push (); - if (unlikely (!event)) - goto fail_features; event->index = 0; /* This value does magic. */ event->start = false; event->feature = feature; } /* Scan events and save features for each range. */ - hb_auto_array_t active_features; + hb_vector_t active_features; unsigned int last_index = 0; - for (unsigned int i = 0; i < feature_events.len; i++) + for (unsigned int i = 0; i < feature_events.length; i++) { feature_event_t *event = &feature_events[i]; if (event->index != last_index) { - /* Save a snapshot of active features and the range. */ + /* Save a snapshot of active features and the range. */ range_record_t *range = range_records.push (); - if (unlikely (!range)) - goto fail_features; - if (active_features.len) + if (active_features.length) { CFMutableArrayRef features_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); /* TODO sort and resolve conflicting features? */ /* active_features.qsort (); */ - for (unsigned int j = 0; j < active_features.len; j++) + for (unsigned int j = 0; j < active_features.length; j++) { +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 CFStringRef keys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey @@ -707,6 +593,17 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) }; +#else + char tag[5] = {HB_UNTAG (active_features[j].rec.feature)}; + CFTypeRef keys[] = { + kCTFontOpenTypeFeatureTag, + kCTFontOpenTypeFeatureValue + }; + CFTypeRef values[] = { + CFStringCreateWithCString (kCFAllocatorDefault, tag, kCFStringEncodingASCII), + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) + }; +#endif static_assert ((ARRAY_LENGTH_CONST (keys) == ARRAY_LENGTH_CONST (values)), ""); CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, (const void **) keys, @@ -747,30 +644,23 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, last_index = event->index; } - if (event->start) { - active_feature_t *feature = active_features.push (); - if (unlikely (!feature)) - goto fail_features; - *feature = event->feature; + if (event->start) + { + active_features.push (event->feature); } else { - active_feature_t *feature = active_features.find (&event->feature); + active_feature_t *feature = active_features.lsearch (event->feature); if (feature) - active_features.remove (feature - active_features.array); + active_features.remove_ordered (feature - active_features.arrayZ); } } } - else - { - fail_features: - num_features = 0; - } unsigned int scratch_size; hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); #define ALLOCATE_ARRAY(Type, name, len, on_no_room) \ Type *name = (Type *) scratch; \ - { \ + do { \ unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ if (unlikely (_consumed > scratch_size)) \ { \ @@ -779,9 +669,9 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, } \ scratch += _consumed; \ scratch_size -= _consumed; \ - } + } while (0) - ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/); + ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, ((void)nullptr) /*nothing*/); unsigned int chars_len = 0; for (unsigned int i = 0; i < buffer->len; i++) { hb_codepoint_t c = buffer->info[i].codepoint; @@ -795,7 +685,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, } } - ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, /*nothing*/); + ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, ((void)nullptr) /*nothing*/); chars_len = 0; for (unsigned int i = 0; i < buffer->len; i++) { @@ -811,13 +701,13 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \ ret = false; \ goto fail; \ - } HB_STMT_END; + } HB_STMT_END bool ret = true; CFStringRef string_ref = nullptr; CTLineRef line = nullptr; - if (0) + if (false) { resize_and_retry: DEBUG_MSG (CORETEXT, buffer, "Buffer resize"); @@ -873,15 +763,18 @@ resize_and_retry: /* What's the iOS equivalent of this check? * The symbols was introduced in iOS 7.0. * At any rate, our fallback is safe and works fine. */ -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1090 # define kCTLanguageAttributeName CFSTR ("NSLanguage") #endif - CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, + CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, hb_language_to_string (buffer->props.language), kCFStringEncodingUTF8, kCFAllocatorNull); if (unlikely (!lang)) + { + CFRelease (attr_string); FAIL ("CFStringCreateWithCStringNoCopy failed"); + } CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), kCTLanguageAttributeName, lang); CFRelease (lang); @@ -889,7 +782,7 @@ resize_and_retry: CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), kCTFontAttributeName, ct_font); - if (num_features && range_records.len) + if (num_features && range_records.length) { unsigned int start = 0; range_record_t *last_range = &range_records[0]; @@ -930,7 +823,7 @@ resize_and_retry: feature.start < chars_len && feature.start < feature.end) { CFRange feature_range = CFRangeMake (feature.start, - MIN (feature.end, chars_len) - feature.start); + hb_min (feature.end, chars_len) - feature.start); if (feature.value) CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName); else @@ -942,6 +835,9 @@ resize_and_retry: int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level); +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + extern const CFStringRef kCTTypesetterOptionForcedEmbeddingLevel; +#endif CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault, (const void **) &kCTTypesetterOptionForcedEmbeddingLevel, (const void **) &level_number, @@ -950,7 +846,10 @@ resize_and_retry: &kCFTypeDictionaryValueCallBacks); CFRelease (level_number); if (unlikely (!options)) - FAIL ("CFDictionaryCreate failed"); + { + CFRelease (attr_string); + FAIL ("CFDictionaryCreate failed"); + } CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options); CFRelease (options); @@ -969,12 +868,12 @@ resize_and_retry: DEBUG_MSG (CORETEXT, nullptr, "Num runs: %d", num_runs); buffer->len = 0; - uint32_t status_and = ~0, status_or = 0; - double advances_so_far = 0; + uint32_t status_or = 0; + CGFloat advances_so_far = 0; /* For right-to-left runs, CoreText returns the glyphs positioned such that * any trailing whitespace is to the left of (0,0). Adjust coordinate system * to fix for that. Test with any RTL string with trailing spaces. - * https://code.google.com/p/chromium/issues/detail?id=469028 + * https://crbug.com/469028 */ if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) { @@ -990,12 +889,11 @@ resize_and_retry: CTRunRef run = static_cast(CFArrayGetValueAtIndex (glyph_runs, i)); CTRunStatus run_status = CTRunGetStatus (run); status_or |= run_status; - status_and &= run_status; DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status); - double run_advance = CTRunGetTypographicBounds (run, range_all, nullptr, nullptr, nullptr); + CGFloat run_advance = CTRunGetTypographicBounds (run, range_all, nullptr, nullptr, nullptr); if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) run_advance = -run_advance; - DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance); + DEBUG_MSG (CORETEXT, run, "Run advance: %g", (double) run_advance); /* CoreText does automatic font fallback (AKA "cascading") for characters * not supported by the requested font, and provides no way to turn it off, @@ -1027,12 +925,12 @@ resize_and_retry: * However, even that wouldn't work if we were passed in the CGFont to * construct a hb_face to begin with. * - * See: http://github.com/harfbuzz/harfbuzz/pull/36 + * See: https://github.com/harfbuzz/harfbuzz/pull/36 * * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098 */ bool matched = false; - for (unsigned int i = 0; i < range_records.len; i++) + for (unsigned int i = 0; i < range_records.length; i++) if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font)) { matched = true; @@ -1040,7 +938,7 @@ resize_and_retry: } if (!matched) { - CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0); + CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, nullptr); if (run_cg_font) { matched = CFEqual (run_cg_font, cg_font); @@ -1060,7 +958,7 @@ resize_and_retry: if (!matched) { CFRange range = CTRunGetStringRange (run); - DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld", + DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld", range.location, range.location + range.length); if (!buffer->ensure_inplace (buffer->len + range.length)) goto resize_and_retry; @@ -1088,7 +986,7 @@ resize_and_retry: continue; } if (buffer->unicode->is_default_ignorable (ch)) - continue; + continue; info->codepoint = notdef; info->cluster = log_clusters[j]; @@ -1130,10 +1028,10 @@ resize_and_retry: #define SCRATCH_RESTORE() \ scratch_size = scratch_size_saved; \ - scratch = scratch_saved; + scratch = scratch_saved { /* Setup glyphs */ - SCRATCH_SAVE(); + SCRATCH_SAVE(); const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : nullptr; if (!glyphs) { ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry); @@ -1156,12 +1054,12 @@ resize_and_retry: SCRATCH_RESTORE(); } { - /* Setup positions. + /* Setup positions. * Note that CoreText does not return advances for glyphs. As such, * for all but last glyph, we use the delta position to next glyph as * advance (in the advance direction only), and for last glyph we set * whatever is needed to make the whole run's advance add up. */ - SCRATCH_SAVE(); + SCRATCH_SAVE(); const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : nullptr; if (!positions) { ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry); @@ -1171,32 +1069,34 @@ resize_and_retry: hb_glyph_info_t *info = run_info; if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) { - hb_position_t x_offset = (positions[0].x - advances_so_far) * x_mult; + hb_position_t x_offset = round ((positions[0].x - advances_so_far) * x_mult); for (unsigned int j = 0; j < num_glyphs; j++) { - double advance; + CGFloat advance; if (likely (j + 1 < num_glyphs)) advance = positions[j + 1].x - positions[j].x; else /* last glyph */ advance = run_advance - (positions[j].x - positions[0].x); - info->mask = advance * x_mult; + /* int cast necessary to pass through negative values. */ + info->mask = (int) round (advance * x_mult); info->var1.i32 = x_offset; - info->var2.i32 = positions[j].y * y_mult; + info->var2.i32 = round (positions[j].y * y_mult); info++; } } else { - hb_position_t y_offset = (positions[0].y - advances_so_far) * y_mult; + hb_position_t y_offset = round ((positions[0].y - advances_so_far) * y_mult); for (unsigned int j = 0; j < num_glyphs; j++) { - double advance; + CGFloat advance; if (likely (j + 1 < num_glyphs)) advance = positions[j + 1].y - positions[j].y; else /* last glyph */ advance = run_advance - (positions[j].y - positions[0].y); - info->mask = advance * y_mult; - info->var1.i32 = positions[j].x * x_mult; + /* int cast necessary to pass through negative values. */ + info->mask = (int) round (advance * y_mult); + info->var1.i32 = round (positions[j].x * x_mult); info->var2.i32 = y_offset; info++; } @@ -1212,21 +1112,6 @@ resize_and_retry: buffer->len += num_glyphs; } - /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel, - * or if it does, it doesn't resepct it. So we get runs with wrong - * directions. As such, disable the assert... It wouldn't crash, but - * cursoring will be off... - * - * http://crbug.com/419769 - */ - if (0) - { - /* Make sure all runs had the expected direction. */ - bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); - assert (bool (status_and & kCTRunStatusRightToLeft) == backward); - assert (bool (status_or & kCTRunStatusRightToLeft) == backward); - } - buffer->clear_positions (); unsigned int count = buffer->len; @@ -1239,9 +1124,7 @@ resize_and_retry: pos->x_offset = info->var1.i32; pos->y_offset = info->var2.i32; - info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK; - - info++, pos++; + info++; pos++; } else for (unsigned int i = 0; i < count; i++) @@ -1250,9 +1133,7 @@ resize_and_retry: pos->x_offset = info->var1.i32; pos->y_offset = info->var2.i32; - info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK; - - info++, pos++; + info++; pos++; } /* Fix up clusters so that we never return out-of-order indices; @@ -1265,7 +1146,8 @@ resize_and_retry: * This does *not* mean we'll form the same clusters as Uniscribe * or the native OT backend, only that the cluster indices will be * monotonic in the output buffer. */ - if (count > 1 && (status_or & kCTRunStatusNonMonotonic)) + if (count > 1 && (status_or & kCTRunStatusNonMonotonic) && + buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) { hb_glyph_info_t *info = buffer->info; if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) @@ -1273,7 +1155,7 @@ resize_and_retry: unsigned int cluster = info[count - 1].cluster; for (unsigned int i = count - 1; i > 0; i--) { - cluster = MIN (cluster, info[i - 1].cluster); + cluster = hb_min (cluster, info[i - 1].cluster); info[i - 1].cluster = cluster; } } @@ -1282,14 +1164,19 @@ resize_and_retry: unsigned int cluster = info[0].cluster; for (unsigned int i = 1; i < count; i++) { - cluster = MIN (cluster, info[i].cluster); + cluster = hb_min (cluster, info[i].cluster); info[i].cluster = cluster; } } } } - buffer->unsafe_to_break_all (); + /* TODO: Sometimes the above positioning code generates negative + * advance values. Fix them up. Example, with NotoNastaliqUrdu + * font and sequence ุงุจู‡ุฏ. */ + + buffer->clear_glyph_flags (); + buffer->unsafe_to_break (); #undef FAIL @@ -1299,7 +1186,7 @@ fail: if (line) CFRelease (line); - for (unsigned int i = 0; i < range_records.len; i++) + for (unsigned int i = 0; i < range_records.length; i++) if (range_records[i].font) CFRelease (range_records[i].font); @@ -1307,108 +1194,4 @@ fail: } -/* - * AAT shaper - */ - -HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, face) -HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, font) - -/* - * shaper face data - */ - -struct hb_coretext_aat_shaper_face_data_t {}; - -hb_coretext_aat_shaper_face_data_t * -_hb_coretext_aat_shaper_face_data_create (hb_face_t *face) -{ - static const hb_tag_t tags[] = {HB_CORETEXT_TAG_MORX, HB_CORETEXT_TAG_MORT, HB_CORETEXT_TAG_KERX, HB_CORETEXT_TAG_TRAK}; - - // Disable macOS 11 / iOS 14 specific hotfix for Qt on older versions, since it caused a - // regression. (Note: This code should not been upstreamed and is only applicable to Qt 5.15.x). - NSInteger majorVersion = NSProcessInfo.processInfo.operatingSystemVersion.majorVersion; - NSInteger hotfixMinimumVersion; -#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE - hotfixMinimumVersion = 14; -#else - hotfixMinimumVersion = 11; #endif - - unsigned int arrayLength = ARRAY_LENGTH (tags); - if (majorVersion < hotfixMinimumVersion) - arrayLength--; - - for (unsigned int i = 0; i < arrayLength; i++) - { - hb_blob_t *blob = face->reference_table (tags[i]); - if (hb_blob_get_length (blob)) - { - hb_blob_destroy (blob); - return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr; - } - hb_blob_destroy (blob); - } - - return nullptr; -} - -void -_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t *data HB_UNUSED) -{ -} - - -/* - * shaper font data - */ - -struct hb_coretext_aat_shaper_font_data_t {}; - -hb_coretext_aat_shaper_font_data_t * -_hb_coretext_aat_shaper_font_data_create (hb_font_t *font) -{ - return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr; -} - -void -_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_shaper_font_data_t *data HB_UNUSED) -{ -} - - -/* - * shaper shape_plan data - */ - -struct hb_coretext_aat_shaper_shape_plan_data_t {}; - -hb_coretext_aat_shaper_shape_plan_data_t * -_hb_coretext_aat_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, - const hb_feature_t *user_features HB_UNUSED, - unsigned int num_user_features HB_UNUSED, - const int *coords HB_UNUSED, - unsigned int num_coords HB_UNUSED) -{ - return (hb_coretext_aat_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; -} - -void -_hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shaper_shape_plan_data_t *data HB_UNUSED) -{ -} - - -/* - * shaper - */ - -hb_bool_t -_hb_coretext_aat_shape (hb_shape_plan_t *shape_plan, - hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features) -{ - return _hb_coretext_shape (shape_plan, font, buffer, features, num_features); -} diff --git a/src/3rdparty/harfbuzz-ng/src/hb-coretext.h b/src/3rdparty/harfbuzz-ng/src/hb-coretext.h index 12f7d2515bd..e53dbaf2c7f 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-coretext.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-coretext.h @@ -40,10 +40,42 @@ HB_BEGIN_DECLS +/** + * HB_CORETEXT_TAG_MORT: + * + * The #hb_tag_t tag for the `mort` (glyph metamorphosis) table, + * which holds AAT features. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html + * + **/ #define HB_CORETEXT_TAG_MORT HB_TAG('m','o','r','t') + +/** + * HB_CORETEXT_TAG_MORX: + * + * The #hb_tag_t tag for the `morx` (extended glyph metamorphosis) + * table, which holds AAT features. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html + * + **/ #define HB_CORETEXT_TAG_MORX HB_TAG('m','o','r','x') + +/** + * HB_CORETEXT_TAG_KERX: + * + * The #hb_tag_t tag for the `kerx` (extended kerning) table, which + * holds AAT kerning information. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html + * + **/ #define HB_CORETEXT_TAG_KERX HB_TAG('k','e','r','x') -#define HB_CORETEXT_TAG_TRAK HB_TAG('t','r','a','k') + HB_EXTERN hb_face_t * hb_coretext_face_create (CGFontRef cg_font); diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cplusplus.hh b/src/3rdparty/harfbuzz-ng/src/hb-cplusplus.hh new file mode 100644 index 00000000000..531ef1b7c83 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cplusplus.hh @@ -0,0 +1,223 @@ +/* + * Copyright ยฉ 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_CPLUSPLUS_HH +#define HB_CPLUSPLUS_HH + +#include "hb.h" + +HB_BEGIN_DECLS +HB_END_DECLS + +#ifdef __cplusplus + +#include +#include + +#if 0 +#if !(__cplusplus >= 201103L) +#error "HarfBuzz C++ helpers require C++11" +#endif +#endif + +namespace hb { + + +template +struct vtable; + +template +struct shared_ptr +{ + using element_type = T; + + using v = vtable; + + explicit shared_ptr (T *p = nullptr) : p (p) {} + shared_ptr (const shared_ptr &o) : p (v::reference (o.p)) {} + shared_ptr (shared_ptr &&o) : p (o.p) { o.p = nullptr; } + shared_ptr& operator = (const shared_ptr &o) { if (p != o.p) { destroy (); p = o.p; reference (); } return *this; } + shared_ptr& operator = (shared_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; } + ~shared_ptr () { v::destroy (p); p = nullptr; } + + T* get() const { return p; } + + void swap (shared_ptr &o) { std::swap (p, o.p); } + friend void swap (shared_ptr &a, shared_ptr &b) { std::swap (a.p, b.p); } + + operator T * () const { return p; } + T& operator * () const { return *get (); } + T* operator -> () const { return get (); } + operator bool () const { return p; } + bool operator == (const shared_ptr &o) const { return p == o.p; } + bool operator != (const shared_ptr &o) const { return p != o.p; } + + static T* get_empty() { return v::get_empty (); } + T* reference() { return v::reference (p); } + void destroy() { v::destroy (p); } + void set_user_data (hb_user_data_key_t *key, + void *value, + hb_destroy_func_t destroy, + hb_bool_t replace) { v::set_user_data (p, key, value, destroy, replace); } + void * get_user_data (hb_user_data_key_t *key) { return v::get_user_data (p, key); } + + private: + T *p; +}; + +template struct is_shared_ptr : std::false_type {}; +template struct is_shared_ptr> : std::true_type {}; + +template +struct unique_ptr +{ + using element_type = T; + + using v = vtable; + + explicit unique_ptr (T *p = nullptr) : p (p) {} + unique_ptr (const unique_ptr &o) = delete; + unique_ptr (unique_ptr &&o) : p (o.p) { o.p = nullptr; } + unique_ptr& operator = (const unique_ptr &o) = delete; + unique_ptr& operator = (unique_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; } + ~unique_ptr () { v::destroy (p); p = nullptr; } + + T* get() const { return p; } + T* release () { T* v = p; p = nullptr; return v; } + + void swap (unique_ptr &o) { std::swap (p, o.p); } + friend void swap (unique_ptr &a, unique_ptr &b) { std::swap (a.p, b.p); } + + operator T * () const { return p; } + T& operator * () const { return *get (); } + T* operator -> () const { return get (); } + operator bool () { return p; } + + private: + T *p; +}; + +template struct is_unique_ptr : std::false_type {}; +template struct is_unique_ptr> : std::true_type {}; + +template +struct vtable_t +{ + static constexpr auto get_empty = _get_empty; + static constexpr auto reference = _reference; + static constexpr auto destroy = _destroy; + static constexpr auto set_user_data = _set_user_data; + static constexpr auto get_user_data = _get_user_data; +}; + +#define HB_DEFINE_VTABLE(name) \ + template<> \ + struct vtable \ + : vtable_t {} + +HB_DEFINE_VTABLE (buffer); +HB_DEFINE_VTABLE (blob); +HB_DEFINE_VTABLE (face); +HB_DEFINE_VTABLE (font); +HB_DEFINE_VTABLE (font_funcs); +HB_DEFINE_VTABLE (map); +HB_DEFINE_VTABLE (set); +HB_DEFINE_VTABLE (shape_plan); +HB_DEFINE_VTABLE (unicode_funcs); +HB_DEFINE_VTABLE (draw_funcs); +HB_DEFINE_VTABLE (paint_funcs); + +#undef HB_DEFINE_VTABLE + + +#ifdef HB_SUBSET_H + +#define HB_DEFINE_VTABLE(name) \ + template<> \ + struct vtable \ + : vtable_t {} + + +HB_DEFINE_VTABLE (subset_input); +HB_DEFINE_VTABLE (subset_plan); + +#undef HB_DEFINE_VTABLE + +#endif + + +} // namespace hb + +/* Workaround for GCC < 7, see: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 + * https://stackoverflow.com/a/25594741 */ +namespace std { + + +template +struct hash> +{ + std::size_t operator()(const hb::shared_ptr& v) const noexcept + { + std::size_t h = std::hash{}(v.get ()); + return h; + } +}; + +template +struct hash> +{ + std::size_t operator()(const hb::unique_ptr& v) const noexcept + { + std::size_t h = std::hash{}(v.get ()); + return h; + } +}; + + +} // namespace std + +#endif /* __cplusplus */ + +#endif /* HB_CPLUSPLUS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-debug.hh b/src/3rdparty/harfbuzz-ng/src/hb-debug.hh index 6c425f7b9dd..0ac4515fa85 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-debug.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-debug.hh @@ -27,13 +27,62 @@ #ifndef HB_DEBUG_HH #define HB_DEBUG_HH -#include "hb-private.hh" +#include "hb.hh" +#include "hb-atomic.hh" +#include "hb-algs.hh" #ifndef HB_DEBUG #define HB_DEBUG 0 #endif + +/* + * Global runtime options. + */ + +struct hb_options_t +{ + bool unused : 1; /* In-case sign bit is here. */ + bool initialized : 1; + bool uniscribe_bug_compatible : 1; +}; + +union hb_options_union_t { + int i; + hb_options_t opts; +}; +static_assert ((sizeof (hb_atomic_int_t) >= sizeof (hb_options_union_t)), ""); + +HB_INTERNAL void +_hb_options_init (); + +extern HB_INTERNAL hb_atomic_int_t _hb_options; + +static inline hb_options_t +hb_options () +{ +#ifdef HB_NO_GETENV + return hb_options_t (); +#endif + /* Make a local copy, so we can access bitfield threadsafely. */ + hb_options_union_t u; + u.i = _hb_options; + + if (unlikely (!u.i)) + { + _hb_options_init (); + u.i = _hb_options; + } + + return u.opts; +} + + +/* + * Debug output (needs enabling at compile time.) + */ + static inline bool _hb_debug (unsigned int level, unsigned int max_level) @@ -64,7 +113,7 @@ _hb_print_func (const char *func) const char *paren = strchr (func, '('); if (paren) func_len = paren - func; - fprintf (stderr, "%.*s", func_len, func); + fprintf (stderr, "%.*s", (int) func_len, func); } } @@ -93,9 +142,9 @@ _hb_debug_msg_va (const char *what, fprintf (stderr, "%-10s", what ? what : ""); if (obj) - fprintf (stderr, "(%*p) ", (unsigned int) (2 * sizeof (void *)), obj); + fprintf (stderr, "(%*p) ", (int) (2 * sizeof (void *)), obj); else - fprintf (stderr, " %*s ", (unsigned int) (2 * sizeof (void *)), ""); + fprintf (stderr, " %*s ", (int) (2 * sizeof (void *)), ""); if (indented) { #define VBAR "\342\224\202" /* U+2502 BOX DRAWINGS LIGHT VERTICAL */ @@ -111,7 +160,7 @@ _hb_debug_msg_va (const char *what, VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR; fprintf (stderr, "%2u %s" VRBAR "%s", level, - bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level), + bars + sizeof (bars) - 1 - hb_min ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level), level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR); } else fprintf (stderr, " " VRBAR LBAR); @@ -126,7 +175,7 @@ _hb_debug_msg_va (const char *what, fprintf (stderr, "\n"); } -template <> inline void +template <> inline void HB_PRINTF_FUNC(7, 0) _hb_debug_msg_va<0> (const char *what HB_UNUSED, const void *obj HB_UNUSED, const char *func HB_UNUSED, @@ -145,7 +194,7 @@ _hb_debug_msg (const char *what, int level_dir, const char *message, ...) HB_PRINTF_FUNC(7, 8); -template static inline void +template static inline void HB_PRINTF_FUNC(7, 8) _hb_debug_msg (const char *what, const void *obj, const char *func, @@ -169,7 +218,7 @@ _hb_debug_msg<0> (const char *what HB_UNUSED, int level_dir HB_UNUSED, const char *message HB_UNUSED, ...) HB_PRINTF_FUNC(7, 8); -template <> inline void +template <> inline void HB_PRINTF_FUNC(7, 8) _hb_debug_msg<0> (const char *what HB_UNUSED, const void *obj HB_UNUSED, const char *func HB_UNUSED, @@ -180,7 +229,7 @@ _hb_debug_msg<0> (const char *what HB_UNUSED, ...) {} #define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...) _hb_debug_msg (#WHAT, (OBJ), nullptr, true, (LEVEL), (LEVEL_DIR), __VA_ARGS__) -#define DEBUG_MSG(WHAT, OBJ, ...) _hb_debug_msg (#WHAT, (OBJ), nullptr, false, 0, 0, __VA_ARGS__) +#define DEBUG_MSG(WHAT, OBJ, ...) _hb_debug_msg (#WHAT, (OBJ), nullptr, false, 0, 0, __VA_ARGS__) #define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg (#WHAT, (OBJ), HB_FUNC, false, 0, 0, __VA_ARGS__) @@ -199,8 +248,8 @@ struct hb_printer_t { }; template <> -struct hb_printer_t { - const char *print (hb_void_t) { return ""; } +struct hb_printer_t { + const char *print (hb_empty_t) { return ""; } }; @@ -216,7 +265,7 @@ static inline void _hb_warn_no_return (bool returned) } } template <> -/*static*/ inline void _hb_warn_no_return (bool returned HB_UNUSED) +/*static*/ inline void _hb_warn_no_return (bool returned HB_UNUSED) {} template @@ -237,7 +286,7 @@ struct hb_auto_trace_t _hb_debug_msg_va (what, obj, func, true, plevel ? *plevel : 0, +1, message, ap); va_end (ap); } - inline ~hb_auto_trace_t (void) + ~hb_auto_trace_t () { _hb_warn_no_return (returned); if (!returned) { @@ -246,20 +295,23 @@ struct hb_auto_trace_t if (plevel) --*plevel; } - inline ret_t ret (ret_t v, unsigned int line = 0) + template + T ret (T&& v, + const char *func = "", + unsigned int line = 0) { if (unlikely (returned)) { fprintf (stderr, "OUCH, double calls to return_trace(). This is a bug, please report.\n"); - return v; + return std::forward (v); } - _hb_debug_msg (what, obj, nullptr, true, plevel ? *plevel : 1, -1, - "return %s (line %d)", - hb_printer_t().print (v), line); + _hb_debug_msg (what, obj, func, true, plevel ? *plevel : 1, -1, + "return %s (line %u)", + hb_printer_t>().print (v), line); if (plevel) --*plevel; plevel = nullptr; returned = true; - return v; + return std::forward (v); } private: @@ -278,17 +330,23 @@ struct hb_auto_trace_t<0, ret_t> const char *message, ...) HB_PRINTF_FUNC(6, 7) {} - inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; } + template + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return std::forward (v); } }; /* For disabled tracing; optimize out everything. * https://github.com/harfbuzz/harfbuzz/pull/605 */ template struct hb_no_trace_t { - inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; } + template + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return std::forward (v); } }; -#define return_trace(RET) return trace.ret (RET, __LINE__) +#define return_trace(RET) return trace.ret (RET, HB_FUNC, __LINE__) /* @@ -315,8 +373,8 @@ struct hb_no_trace_t { #define HB_DEBUG_FT (HB_DEBUG+0) #endif -#ifndef HB_DEBUG_GET_COVERAGE -#define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0) +#ifndef HB_DEBUG_JUSTIFY +#define HB_DEBUG_JUSTIFY (HB_DEBUG+0) #endif #ifndef HB_DEBUG_OBJECT @@ -342,36 +400,12 @@ struct hb_no_trace_t { #define TRACE_APPLY(this) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "idx %d gid %u lookup %d", \ + "idx %u gid %u lookup %d", \ c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index) #else #define TRACE_APPLY(this) hb_no_trace_t trace #endif -#ifndef HB_DEBUG_CLOSURE -#define HB_DEBUG_CLOSURE (HB_DEBUG+0) -#endif -#if HB_DEBUG_CLOSURE -#define TRACE_CLOSURE(this) \ - hb_auto_trace_t trace \ - (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - " ") -#else -#define TRACE_CLOSURE(this) hb_no_trace_t trace HB_UNUSED -#endif - -#ifndef HB_DEBUG_COLLECT_GLYPHS -#define HB_DEBUG_COLLECT_GLYPHS (HB_DEBUG+0) -#endif -#if HB_DEBUG_COLLECT_GLYPHS -#define TRACE_COLLECT_GLYPHS(this) \ - hb_auto_trace_t trace \ - (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - " ") -#else -#define TRACE_COLLECT_GLYPHS(this) hb_no_trace_t trace HB_UNUSED -#endif - #ifndef HB_DEBUG_SANITIZE #define HB_DEBUG_SANITIZE (HB_DEBUG+0) #endif @@ -379,7 +413,7 @@ struct hb_no_trace_t { #define TRACE_SANITIZE(this) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - " "); + " ") #else #define TRACE_SANITIZE(this) hb_no_trace_t trace #endif @@ -391,41 +425,48 @@ struct hb_no_trace_t { #define TRACE_SERIALIZE(this) \ hb_auto_trace_t trace \ (&c->debug_depth, "SERIALIZE", c, HB_FUNC, \ - " "); + " ") #else #define TRACE_SERIALIZE(this) hb_no_trace_t trace #endif -#ifndef HB_DEBUG_WOULD_APPLY -#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0) +#ifndef HB_DEBUG_SUBSET +#define HB_DEBUG_SUBSET (HB_DEBUG+0) #endif -#if HB_DEBUG_WOULD_APPLY -#define TRACE_WOULD_APPLY(this) \ - hb_auto_trace_t trace \ - (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "%d glyphs", c->len); +#if HB_DEBUG_SUBSET +#define TRACE_SUBSET(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + " ") #else -#define TRACE_WOULD_APPLY(this) hb_no_trace_t trace +#define TRACE_SUBSET(this) hb_no_trace_t trace +#endif + +#ifndef HB_DEBUG_SUBSET_REPACK +#define HB_DEBUG_SUBSET_REPACK (HB_DEBUG+0) #endif #ifndef HB_DEBUG_DISPATCH #define HB_DEBUG_DISPATCH ( \ HB_DEBUG_APPLY + \ - HB_DEBUG_CLOSURE + \ - HB_DEBUG_COLLECT_GLYPHS + \ HB_DEBUG_SANITIZE + \ HB_DEBUG_SERIALIZE + \ - HB_DEBUG_WOULD_APPLY + \ + HB_DEBUG_SUBSET + \ 0) #endif #if HB_DEBUG_DISPATCH #define TRACE_DISPATCH(this, format) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "format %d", (int) format); + "format %u", (unsigned) format) #else #define TRACE_DISPATCH(this, format) hb_no_trace_t trace #endif +#ifndef HB_BUFFER_MESSAGE_MORE +#define HB_BUFFER_MESSAGE_MORE (HB_DEBUG+1) +#endif + + #endif /* HB_DEBUG_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-deprecated.h b/src/3rdparty/harfbuzz-ng/src/hb-deprecated.h index eac7efb42f1..b032a941b28 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-deprecated.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-deprecated.h @@ -24,7 +24,7 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_H_IN +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif @@ -36,30 +36,228 @@ #include "hb-font.h" #include "hb-set.h" + +/** + * SECTION:hb-deprecated + * @title: hb-deprecated + * @short_description: Deprecated API + * @include: hb.h + * + * These API have been deprecated in favor of newer API, or because they + * were deemed unnecessary. + **/ + + HB_BEGIN_DECLS #ifndef HB_DISABLE_DEPRECATED + +/** + * HB_SCRIPT_CANADIAN_ABORIGINAL: + * + * Use #HB_SCRIPT_CANADIAN_SYLLABICS instead: + * + * Deprecated: 0.9.20 + */ #define HB_SCRIPT_CANADIAN_ABORIGINAL HB_SCRIPT_CANADIAN_SYLLABICS +/** + * HB_BUFFER_FLAGS_DEFAULT: + * + * Use #HB_BUFFER_FLAG_DEFAULT instead. + * + * Deprecated: 0.9.20 + */ #define HB_BUFFER_FLAGS_DEFAULT HB_BUFFER_FLAG_DEFAULT +/** + * HB_BUFFER_SERIALIZE_FLAGS_DEFAULT: + * + * Use #HB_BUFFER_SERIALIZE_FLAG_DEFAULT instead. + * + * Deprecated: 0.9.20 + */ #define HB_BUFFER_SERIALIZE_FLAGS_DEFAULT HB_BUFFER_SERIALIZE_FLAG_DEFAULT +/** + * hb_font_get_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @unicode: The Unicode code point to query + * @variation_selector: The variation-selector code point to query + * @glyph: (out): The glyph ID retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the glyph ID for a specified Unicode code point + * font, with an optional variation selector. + * + * Return value: `true` if data found, `false` otherwise + * Deprecated: 1.2.3 + * + **/ typedef hb_bool_t (*hb_font_get_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t unicode, hb_codepoint_t variation_selector, hb_codepoint_t *glyph, void *user_data); +HB_DEPRECATED_FOR (hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func) HB_EXTERN void hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_func_t func, void *user_data, hb_destroy_func_t destroy); +/* https://github.com/harfbuzz/harfbuzz/pull/4207 */ +/** + * HB_UNICODE_COMBINING_CLASS_CCC133: + * + * [Tibetan] + * + * Deprecated: 7.2.0 + **/ +#define HB_UNICODE_COMBINING_CLASS_CCC133 133 + +/** + * hb_unicode_eastasian_width_func_t: + * @ufuncs: A Unicode-functions structure + * @unicode: The code point to query + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_unicode_funcs_t structure. + * + * Deprecated: 2.0.0 + */ +typedef unsigned int (*hb_unicode_eastasian_width_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); + +/** + * hb_unicode_funcs_set_eastasian_width_func: + * @ufuncs: a Unicode-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_unicode_eastasian_width_func_t. + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN HB_DEPRECATED void +hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_eastasian_width_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_eastasian_width: + * @ufuncs: a Unicode-function structure + * @unicode: The code point to query + * + * Don't use. Not used by HarfBuzz. + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN HB_DEPRECATED unsigned int +hb_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + + +/** + * hb_unicode_decompose_compatibility_func_t: + * @ufuncs: a Unicode function structure + * @u: codepoint to decompose + * @decomposed: address of codepoint array (of length #HB_UNICODE_MAX_DECOMPOSITION_LEN) to write decomposition into + * @user_data: user data pointer as passed to hb_unicode_funcs_set_decompose_compatibility_func() + * + * Fully decompose @u to its Unicode compatibility decomposition. The codepoints of the decomposition will be written to @decomposed. + * The complete length of the decomposition will be returned. + * + * If @u has no compatibility decomposition, zero should be returned. + * + * The Unicode standard guarantees that a buffer of length #HB_UNICODE_MAX_DECOMPOSITION_LEN codepoints will always be sufficient for any + * compatibility decomposition plus an terminating value of 0. Consequently, @decompose must be allocated by the caller to be at least this length. Implementations + * of this function type must ensure that they do not write past the provided array. + * + * Return value: number of codepoints in the full compatibility decomposition of @u, or 0 if no decomposition available. + * + * Deprecated: 2.0.0 + */ +typedef unsigned int (*hb_unicode_decompose_compatibility_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed, + void *user_data); + +/** + * HB_UNICODE_MAX_DECOMPOSITION_LEN: + * + * See Unicode 6.1 for details on the maximum decomposition length. + * + * Deprecated: 2.0.0 + */ +#define HB_UNICODE_MAX_DECOMPOSITION_LEN (18+1) /* codepoints */ + +/** + * hb_unicode_funcs_set_decompose_compatibility_func: + * @ufuncs: A Unicode-functions structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_unicode_decompose_compatibility_func_t. + * + * + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN HB_DEPRECATED void +hb_unicode_funcs_set_decompose_compatibility_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_decompose_compatibility_func_t func, + void *user_data, hb_destroy_func_t destroy); + +HB_EXTERN HB_DEPRECATED unsigned int +hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed); + + +/** + * hb_font_get_glyph_v_kerning_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the kerning-adjustment value for a glyph-pair in + * the specified font, for vertical text segments. + * + **/ +typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t; + +/** + * hb_font_funcs_set_glyph_v_kerning_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_v_kerning_func_t. + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ HB_EXTERN void -hb_set_invert (hb_set_t *set); +hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_kerning_func_t func, + void *user_data, hb_destroy_func_t destroy); + +HB_EXTERN hb_position_t +hb_font_get_glyph_v_kerning (hb_font_t *font, + hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph); #endif + HB_END_DECLS #endif /* HB_DEPRECATED_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-directwrite.cc b/src/3rdparty/harfbuzz-ng/src/hb-directwrite.cc new file mode 100644 index 00000000000..42764a244b1 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-directwrite.cc @@ -0,0 +1,884 @@ +/* + * Copyright ยฉ 2015-2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifdef HAVE_DIRECTWRITE + +#include "hb-shaper-impl.hh" + +#include + +#include "hb-directwrite.h" + +#include "hb-ms-feature-ranges.hh" + +/** + * SECTION:hb-directwrite + * @title: hb-directwrite + * @short_description: DirectWrite integration + * @include: hb-directwrite.h + * + * Functions for using HarfBuzz with DirectWrite fonts. + **/ + +/* Declare object creator for dynamic support of DWRITE */ +typedef HRESULT (WINAPI *t_DWriteCreateFactory)( + DWRITE_FACTORY_TYPE factoryType, + REFIID iid, + IUnknown **factory +); + + +/* + * DirectWrite font stream helpers + */ + +// This is a font loader which provides only one font (unlike its original design). +// For a better implementation which was also source of this +// and DWriteFontFileStream, have a look at to NativeFontResourceDWrite.cpp in Mozilla +class DWriteFontFileLoader : public IDWriteFontFileLoader +{ +private: + IDWriteFontFileStream *mFontFileStream; +public: + DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream) + { mFontFileStream = fontFileStream; } + + // IUnknown interface + IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) + { return S_OK; } + IFACEMETHOD_ (ULONG, AddRef) () { return 1; } + IFACEMETHOD_ (ULONG, Release) () { return 1; } + + // IDWriteFontFileLoader methods + virtual HRESULT STDMETHODCALLTYPE + CreateStreamFromKey (void const* fontFileReferenceKey, + uint32_t fontFileReferenceKeySize, + OUT IDWriteFontFileStream** fontFileStream) + { + *fontFileStream = mFontFileStream; + return S_OK; + } + + virtual ~DWriteFontFileLoader() {} +}; + +class DWriteFontFileStream : public IDWriteFontFileStream +{ +private: + uint8_t *mData; + uint32_t mSize; +public: + DWriteFontFileStream (uint8_t *aData, uint32_t aSize) + { + mData = aData; + mSize = aSize; + } + + // IUnknown interface + IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) + { return S_OK; } + IFACEMETHOD_ (ULONG, AddRef) () { return 1; } + IFACEMETHOD_ (ULONG, Release) () { return 1; } + + // IDWriteFontFileStream methods + virtual HRESULT STDMETHODCALLTYPE + ReadFileFragment (void const** fragmentStart, + UINT64 fileOffset, + UINT64 fragmentSize, + OUT void** fragmentContext) + { + // We are required to do bounds checking. + if (fileOffset + fragmentSize > mSize) return E_FAIL; + + // truncate the 64 bit fileOffset to size_t sized index into mData + size_t index = static_cast (fileOffset); + + // We should be alive for the duration of this. + *fragmentStart = &mData[index]; + *fragmentContext = nullptr; + return S_OK; + } + + virtual void STDMETHODCALLTYPE + ReleaseFileFragment (void* fragmentContext) {} + + virtual HRESULT STDMETHODCALLTYPE + GetFileSize (OUT UINT64* fileSize) + { + *fileSize = mSize; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE + GetLastWriteTime (OUT UINT64* lastWriteTime) { return E_NOTIMPL; } + + virtual ~DWriteFontFileStream() {} +}; + + +/* +* shaper face data +*/ + +struct hb_directwrite_face_data_t +{ + HMODULE dwrite_dll; + IDWriteFactory *dwriteFactory; + IDWriteFontFile *fontFile; + DWriteFontFileStream *fontFileStream; + DWriteFontFileLoader *fontFileLoader; + IDWriteFontFace *fontFace; + hb_blob_t *faceBlob; +}; + +hb_directwrite_face_data_t * +_hb_directwrite_shaper_face_data_create (hb_face_t *face) +{ + hb_directwrite_face_data_t *data = new hb_directwrite_face_data_t; + if (unlikely (!data)) + return nullptr; + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \ + return nullptr; \ + } HB_STMT_END + + data->dwrite_dll = LoadLibrary (TEXT ("DWRITE")); + if (unlikely (!data->dwrite_dll)) + FAIL ("Cannot find DWrite.DLL"); + + t_DWriteCreateFactory p_DWriteCreateFactory; + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + + p_DWriteCreateFactory = (t_DWriteCreateFactory) + GetProcAddress (data->dwrite_dll, "DWriteCreateFactory"); + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + + if (unlikely (!p_DWriteCreateFactory)) + FAIL ("Cannot find DWriteCreateFactory()."); + + HRESULT hr; + + // TODO: factory and fontFileLoader should be cached separately + IDWriteFactory* dwriteFactory; + hr = p_DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory), + (IUnknown**) &dwriteFactory); + + if (unlikely (hr != S_OK)) + FAIL ("Failed to run DWriteCreateFactory()."); + + hb_blob_t *blob = hb_face_reference_blob (face); + DWriteFontFileStream *fontFileStream; + fontFileStream = new DWriteFontFileStream ((uint8_t *) hb_blob_get_data (blob, nullptr), + hb_blob_get_length (blob)); + + DWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream); + dwriteFactory->RegisterFontFileLoader (fontFileLoader); + + IDWriteFontFile *fontFile; + uint64_t fontFileKey = 0; + hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey), + fontFileLoader, &fontFile); + + if (FAILED (hr)) + FAIL ("Failed to load font file from data!"); + + BOOL isSupported; + DWRITE_FONT_FILE_TYPE fileType; + DWRITE_FONT_FACE_TYPE faceType; + uint32_t numberOfFaces; + hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces); + if (FAILED (hr) || !isSupported) + FAIL ("Font file is not supported."); + +#undef FAIL + + IDWriteFontFace *fontFace; + dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0, + DWRITE_FONT_SIMULATIONS_NONE, &fontFace); + + data->dwriteFactory = dwriteFactory; + data->fontFile = fontFile; + data->fontFileStream = fontFileStream; + data->fontFileLoader = fontFileLoader; + data->fontFace = fontFace; + data->faceBlob = blob; + + return data; +} + +void +_hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data) +{ + if (data->fontFace) + data->fontFace->Release (); + if (data->fontFile) + data->fontFile->Release (); + if (data->dwriteFactory) + { + if (data->fontFileLoader) + data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader); + data->dwriteFactory->Release (); + } + delete data->fontFileLoader; + delete data->fontFileStream; + hb_blob_destroy (data->faceBlob); + if (data->dwrite_dll) + FreeLibrary (data->dwrite_dll); + delete data; +} + + +/* + * shaper font data + */ + +struct hb_directwrite_font_data_t {}; + +hb_directwrite_font_data_t * +_hb_directwrite_shaper_font_data_create (hb_font_t *font) +{ + return (hb_directwrite_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data) +{ +} + + +// Most of TextAnalysis is originally written by Bas Schouten for Mozilla project +// but now is relicensed to MIT for HarfBuzz use +class TextAnalysis : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink +{ +public: + + IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) + { return S_OK; } + IFACEMETHOD_ (ULONG, AddRef) () { return 1; } + IFACEMETHOD_ (ULONG, Release) () { return 1; } + + // A single contiguous run of characters containing the same analysis + // results. + struct Run + { + uint32_t mTextStart; // starting text position of this run + uint32_t mTextLength; // number of contiguous code units covered + uint32_t mGlyphStart; // starting glyph in the glyphs array + uint32_t mGlyphCount; // number of glyphs associated with this run + // text + DWRITE_SCRIPT_ANALYSIS mScript; + uint8_t mBidiLevel; + bool mIsSideways; + + bool ContainsTextPosition (uint32_t aTextPosition) const + { + return aTextPosition >= mTextStart && + aTextPosition < mTextStart + mTextLength; + } + + Run *nextRun; + }; + +public: + TextAnalysis (const wchar_t* text, uint32_t textLength, + const wchar_t* localeName, DWRITE_READING_DIRECTION readingDirection) + : mTextLength (textLength), mText (text), mLocaleName (localeName), + mReadingDirection (readingDirection), mCurrentRun (nullptr) {} + ~TextAnalysis () + { + // delete runs, except mRunHead which is part of the TextAnalysis object + for (Run *run = mRunHead.nextRun; run;) + { + Run *origRun = run; + run = run->nextRun; + delete origRun; + } + } + + STDMETHODIMP + GenerateResults (IDWriteTextAnalyzer* textAnalyzer, Run **runHead) + { + // Analyzes the text using the script analyzer and returns + // the result as a series of runs. + + HRESULT hr = S_OK; + + // Initially start out with one result that covers the entire range. + // This result will be subdivided by the analysis processes. + mRunHead.mTextStart = 0; + mRunHead.mTextLength = mTextLength; + mRunHead.mBidiLevel = + (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + mRunHead.nextRun = nullptr; + mCurrentRun = &mRunHead; + + // Call each of the analyzers in sequence, recording their results. + if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this))) + *runHead = &mRunHead; + + return hr; + } + + // IDWriteTextAnalysisSource implementation + + IFACEMETHODIMP + GetTextAtPosition (uint32_t textPosition, + OUT wchar_t const** textString, + OUT uint32_t* textLength) + { + if (textPosition >= mTextLength) + { + // No text at this position, valid query though. + *textString = nullptr; + *textLength = 0; + } + else + { + *textString = mText + textPosition; + *textLength = mTextLength - textPosition; + } + return S_OK; + } + + IFACEMETHODIMP + GetTextBeforePosition (uint32_t textPosition, + OUT wchar_t const** textString, + OUT uint32_t* textLength) + { + if (textPosition == 0 || textPosition > mTextLength) + { + // Either there is no text before here (== 0), or this + // is an invalid position. The query is considered valid though. + *textString = nullptr; + *textLength = 0; + } + else + { + *textString = mText; + *textLength = textPosition; + } + return S_OK; + } + + IFACEMETHODIMP_ (DWRITE_READING_DIRECTION) + GetParagraphReadingDirection () { return mReadingDirection; } + + IFACEMETHODIMP GetLocaleName (uint32_t textPosition, uint32_t* textLength, + wchar_t const** localeName) + { return S_OK; } + + IFACEMETHODIMP + GetNumberSubstitution (uint32_t textPosition, + OUT uint32_t* textLength, + OUT IDWriteNumberSubstitution** numberSubstitution) + { + // We do not support number substitution. + *numberSubstitution = nullptr; + *textLength = mTextLength - textPosition; + + return S_OK; + } + + // IDWriteTextAnalysisSink implementation + + IFACEMETHODIMP + SetScriptAnalysis (uint32_t textPosition, uint32_t textLength, + DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) + { + SetCurrentRun (textPosition); + SplitCurrentRun (textPosition); + while (textLength > 0) + { + Run *run = FetchNextRun (&textLength); + run->mScript = *scriptAnalysis; + } + + return S_OK; + } + + IFACEMETHODIMP + SetLineBreakpoints (uint32_t textPosition, + uint32_t textLength, + const DWRITE_LINE_BREAKPOINT* lineBreakpoints) + { return S_OK; } + + IFACEMETHODIMP SetBidiLevel (uint32_t textPosition, uint32_t textLength, + uint8_t explicitLevel, uint8_t resolvedLevel) + { return S_OK; } + + IFACEMETHODIMP + SetNumberSubstitution (uint32_t textPosition, uint32_t textLength, + IDWriteNumberSubstitution* numberSubstitution) + { return S_OK; } + +protected: + Run *FetchNextRun (IN OUT uint32_t* textLength) + { + // Used by the sink setters, this returns a reference to the next run. + // Position and length are adjusted to now point after the current run + // being returned. + + Run *origRun = mCurrentRun; + // Split the tail if needed (the length remaining is less than the + // current run's size). + if (*textLength < mCurrentRun->mTextLength) + SplitCurrentRun (mCurrentRun->mTextStart + *textLength); + else + // Just advance the current run. + mCurrentRun = mCurrentRun->nextRun; + *textLength -= origRun->mTextLength; + + // Return a reference to the run that was just current. + return origRun; + } + + void SetCurrentRun (uint32_t textPosition) + { + // Move the current run to the given position. + // Since the analyzers generally return results in a forward manner, + // this will usually just return early. If not, find the + // corresponding run for the text position. + + if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition)) + return; + + for (Run *run = &mRunHead; run; run = run->nextRun) + if (run->ContainsTextPosition (textPosition)) + { + mCurrentRun = run; + return; + } + assert (0); // We should always be able to find the text position in one of our runs + } + + void SplitCurrentRun (uint32_t splitPosition) + { + if (!mCurrentRun) + { + assert (0); // SplitCurrentRun called without current run + // Shouldn't be calling this when no current run is set! + return; + } + // Split the current run. + if (splitPosition <= mCurrentRun->mTextStart) + { + // No need to split, already the start of a run + // or before it. Usually the first. + return; + } + Run *newRun = new Run; + + *newRun = *mCurrentRun; + + // Insert the new run in our linked list. + newRun->nextRun = mCurrentRun->nextRun; + mCurrentRun->nextRun = newRun; + + // Adjust runs' text positions and lengths. + uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart; + newRun->mTextStart += splitPoint; + newRun->mTextLength -= splitPoint; + mCurrentRun->mTextLength = splitPoint; + mCurrentRun = newRun; + } + +protected: + // Input + // (weak references are fine here, since this class is a transient + // stack-based helper that doesn't need to copy data) + uint32_t mTextLength; + const wchar_t* mText; + const wchar_t* mLocaleName; + DWRITE_READING_DIRECTION mReadingDirection; + + // Current processing state. + Run *mCurrentRun; + + // Output is a list of runs starting here + Run mRunHead; +}; + +/* + * shaper + */ + +hb_bool_t +_hb_directwrite_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + const hb_directwrite_face_data_t *face_data = face->data.directwrite; + IDWriteFactory *dwriteFactory = face_data->dwriteFactory; + IDWriteFontFace *fontFace = face_data->fontFace; + + IDWriteTextAnalyzer* analyzer; + dwriteFactory->CreateTextAnalyzer (&analyzer); + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); +#define ALLOCATE_ARRAY(Type, name, len) \ + Type *name = (Type *) scratch; \ + do { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + assert (_consumed <= scratch_size); \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } while (0) + +#define utf16_index() var1.u32 + + ALLOCATE_ARRAY (wchar_t, textString, buffer->len * 2); + + unsigned int chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + buffer->info[i].utf16_index () = chars_len; + if (likely (c <= 0xFFFFu)) + textString[chars_len++] = c; + else if (unlikely (c > 0x10FFFFu)) + textString[chars_len++] = 0xFFFDu; + else + { + textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); + } + } + + ALLOCATE_ARRAY (WORD, log_clusters, chars_len); + /* Need log_clusters to assign features. */ + chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + unsigned int cluster = buffer->info[i].cluster; + log_clusters[chars_len++] = cluster; + if (hb_in_range (c, 0x10000u, 0x10FFFFu)) + log_clusters[chars_len++] = cluster; /* Surrogates. */ + } + + DWRITE_READING_DIRECTION readingDirection; + readingDirection = buffer->props.direction ? + DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : + DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; + + /* + * There's an internal 16-bit limit on some things inside the analyzer, + * but we never attempt to shape a word longer than 64K characters + * in a single gfxShapedWord, so we cannot exceed that limit. + */ + uint32_t textLength = chars_len; + + TextAnalysis analysis (textString, textLength, nullptr, readingDirection); + TextAnalysis::Run *runHead; + HRESULT hr; + hr = analysis.GenerateResults (analyzer, &runHead); + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \ + return false; \ + } HB_STMT_END + + if (FAILED (hr)) + FAIL ("Analyzer failed to generate results."); + + uint32_t maxGlyphCount = 3 * textLength / 2 + 16; + uint32_t glyphCount; + bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + + const wchar_t localeName[20] = {0}; + if (buffer->props.language) + mbstowcs ((wchar_t*) localeName, + hb_language_to_string (buffer->props.language), 20); + + /* + * Set up features. + */ + static_assert ((sizeof (DWRITE_TYPOGRAPHIC_FEATURES) == sizeof (hb_ms_features_t)), ""); + static_assert ((sizeof (DWRITE_FONT_FEATURE) == sizeof (hb_ms_feature_t)), ""); + hb_vector_t range_features; + hb_vector_t range_char_counts; + if (num_features) + { + hb_vector_t feature_records; + hb_vector_t range_records; + if (hb_ms_setup_features (features, num_features, feature_records, range_records)) + hb_ms_make_feature_ranges (feature_records, + range_records, + 0, + chars_len, + log_clusters, + range_features, + range_char_counts); + } + + uint16_t* clusterMap; + clusterMap = new uint16_t[textLength]; + DWRITE_SHAPING_TEXT_PROPERTIES* textProperties; + textProperties = new DWRITE_SHAPING_TEXT_PROPERTIES[textLength]; + +retry_getglyphs: + uint16_t* glyphIndices = new uint16_t[maxGlyphCount]; + DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties; + glyphProperties = new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount]; + + hr = analyzer->GetGlyphs (textString, + chars_len, + fontFace, + false, + isRightToLeft, + &runHead->mScript, + localeName, + nullptr, + (const DWRITE_TYPOGRAPHIC_FEATURES**) range_features.arrayZ, + range_char_counts.arrayZ, + range_features.length, + maxGlyphCount, + clusterMap, + textProperties, + glyphIndices, + glyphProperties, + &glyphCount); + + if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))) + { + delete [] glyphIndices; + delete [] glyphProperties; + + maxGlyphCount *= 2; + + goto retry_getglyphs; + } + if (FAILED (hr)) + FAIL ("Analyzer failed to get glyphs."); + + float* glyphAdvances = new float[maxGlyphCount]; + DWRITE_GLYPH_OFFSET* glyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount]; + + /* The -2 in the following is to compensate for possible + * alignment needed after the WORD array. sizeof (WORD) == 2. */ + unsigned int glyphs_size = (scratch_size * sizeof (int) - 2) + / (sizeof (WORD) + + sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) + + sizeof (int) + + sizeof (DWRITE_GLYPH_OFFSET) + + sizeof (uint32_t)); + ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size); + +#undef ALLOCATE_ARRAY + + int fontEmSize = font->face->get_upem (); + if (fontEmSize < 0) fontEmSize = -fontEmSize; + + if (fontEmSize < 0) fontEmSize = -fontEmSize; + double x_mult = (double) font->x_scale / fontEmSize; + double y_mult = (double) font->y_scale / fontEmSize; + + hr = analyzer->GetGlyphPlacements (textString, + clusterMap, + textProperties, + chars_len, + glyphIndices, + glyphProperties, + glyphCount, + fontFace, + fontEmSize, + false, + isRightToLeft, + &runHead->mScript, + localeName, + (const DWRITE_TYPOGRAPHIC_FEATURES**) range_features.arrayZ, + range_char_counts.arrayZ, + range_features.length, + glyphAdvances, + glyphOffsets); + + if (FAILED (hr)) + FAIL ("Analyzer failed to get glyph placements."); + + /* Ok, we've got everything we need, now compose output buffer, + * very, *very*, carefully! */ + + /* Calculate visual-clusters. That's what we ship. */ + for (unsigned int i = 0; i < glyphCount; i++) + vis_clusters[i] = (uint32_t) -1; + for (unsigned int i = 0; i < buffer->len; i++) + { + uint32_t *p = + &vis_clusters[log_clusters[buffer->info[i].utf16_index ()]]; + *p = hb_min (*p, buffer->info[i].cluster); + } + for (unsigned int i = 1; i < glyphCount; i++) + if (vis_clusters[i] == (uint32_t) -1) + vis_clusters[i] = vis_clusters[i - 1]; + +#undef utf16_index + + if (unlikely (!buffer->ensure (glyphCount))) + FAIL ("Buffer in error"); + +#undef FAIL + + /* Set glyph infos */ + buffer->len = 0; + for (unsigned int i = 0; i < glyphCount; i++) + { + hb_glyph_info_t *info = &buffer->info[buffer->len++]; + + info->codepoint = glyphIndices[i]; + info->cluster = vis_clusters[i]; + + /* The rest is crap. Let's store position info there for now. */ + info->mask = glyphAdvances[i]; + info->var1.i32 = glyphOffsets[i].advanceOffset; + info->var2.i32 = glyphOffsets[i].ascenderOffset; + } + + /* Set glyph positions */ + buffer->clear_positions (); + for (unsigned int i = 0; i < glyphCount; i++) + { + hb_glyph_info_t *info = &buffer->info[i]; + hb_glyph_position_t *pos = &buffer->pos[i]; + + /* TODO vertical */ + pos->x_advance = x_mult * (int32_t) info->mask; + pos->x_offset = x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32); + pos->y_offset = y_mult * info->var2.i32; + } + + if (isRightToLeft) hb_buffer_reverse (buffer); + + buffer->clear_glyph_flags (); + buffer->unsafe_to_break (); + + delete [] clusterMap; + delete [] glyphIndices; + delete [] textProperties; + delete [] glyphProperties; + delete [] glyphAdvances; + delete [] glyphOffsets; + + /* Wow, done! */ + return true; +} + +struct _hb_directwrite_font_table_context { + IDWriteFontFace *face; + void *table_context; +}; + +static void +_hb_directwrite_table_data_release (void *data) +{ + _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) data; + context->face->ReleaseFontTable (context->table_context); + hb_free (context); +} + +static hb_blob_t * +_hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + IDWriteFontFace *dw_face = ((IDWriteFontFace *) user_data); + const void *data; + uint32_t length; + void *table_context; + BOOL exists; + if (!dw_face || FAILED (dw_face->TryGetFontTable (hb_uint32_swap (tag), &data, + &length, &table_context, &exists))) + return nullptr; + + if (!data || !exists || !length) + { + dw_face->ReleaseFontTable (table_context); + return nullptr; + } + + _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) hb_malloc (sizeof (_hb_directwrite_font_table_context)); + context->face = dw_face; + context->table_context = table_context; + + return hb_blob_create ((const char *) data, length, HB_MEMORY_MODE_READONLY, + context, _hb_directwrite_table_data_release); +} + +static void +_hb_directwrite_font_release (void *data) +{ + if (data) + ((IDWriteFontFace *) data)->Release (); +} + +/** + * hb_directwrite_face_create: + * @font_face: a DirectWrite IDWriteFontFace object. + * + * Constructs a new face object from the specified DirectWrite IDWriteFontFace. + * + * Return value: #hb_face_t object corresponding to the given input + * + * Since: 2.4.0 + **/ +hb_face_t * +hb_directwrite_face_create (IDWriteFontFace *font_face) +{ + if (font_face) + font_face->AddRef (); + return hb_face_create_for_tables (_hb_directwrite_reference_table, font_face, + _hb_directwrite_font_release); +} + +/** +* hb_directwrite_face_get_font_face: +* @face: a #hb_face_t object +* +* Gets the DirectWrite IDWriteFontFace associated with @face. +* +* Return value: DirectWrite IDWriteFontFace object corresponding to the given input +* +* Since: 2.5.0 +**/ +IDWriteFontFace * +hb_directwrite_face_get_font_face (hb_face_t *face) +{ + return face->data.directwrite->fontFace; +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-directwrite.h b/src/3rdparty/harfbuzz-ng/src/hb-directwrite.h new file mode 100644 index 00000000000..f837627a286 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-directwrite.h @@ -0,0 +1,40 @@ +/* + * Copyright ยฉ 2015-2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_DIRECTWRITE_H +#define HB_DIRECTWRITE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +HB_EXTERN hb_face_t * +hb_directwrite_face_create (IDWriteFontFace *font_face); + +HB_EXTERN IDWriteFontFace * +hb_directwrite_face_get_font_face (hb_face_t *face); + +HB_END_DECLS + +#endif /* HB_DIRECTWRITE_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-dispatch.hh b/src/3rdparty/harfbuzz-ng/src/hb-dispatch.hh new file mode 100644 index 00000000000..37ca6814653 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-dispatch.hh @@ -0,0 +1,60 @@ +/* + * Copyright ยฉ 2007,2008,2009,2010 Red Hat, Inc. + * Copyright ยฉ 2012,2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_DISPATCH_HH +#define HB_DISPATCH_HH + +#include "hb.hh" + +/* + * Dispatch + */ + +template +struct hb_dispatch_context_t +{ + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const Context* thiz () const { return static_cast (this); } + Context* thiz () { return static_cast< Context *> (this); } + public: + const char *get_name () { return "UNKNOWN"; } + static constexpr unsigned max_debug_depth = MaxDebugDepth; + typedef Return return_t; + template + bool may_dispatch (const T *obj HB_UNUSED, const F *format HB_UNUSED) { return true; } + template + return_t dispatch (const T &obj, Ts&&... ds) + { return obj.dispatch (thiz (), std::forward (ds)...); } + static return_t no_dispatch_return_value () { return Context::default_return_value (); } + static bool stop_sublookup_iteration (const return_t r HB_UNUSED) { return false; } + unsigned debug_depth = 0; +}; + + +#endif /* HB_DISPATCH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-draw.cc b/src/3rdparty/harfbuzz-ng/src/hb-draw.cc new file mode 100644 index 00000000000..f204f56bc7a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-draw.cc @@ -0,0 +1,458 @@ +/* + * Copyright ยฉ 2019-2020 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifndef HB_NO_DRAW + +#include "hb-draw.hh" + +/** + * SECTION:hb-draw + * @title: hb-draw + * @short_description: Glyph drawing + * @include: hb.h + * + * Functions for drawing (extracting) glyph shapes. + * + * The #hb_draw_funcs_t struct can be used with hb_font_draw_glyph(). + **/ + +static void +hb_draw_move_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED, + hb_draw_state_t *st HB_UNUSED, + float to_x HB_UNUSED, float to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +hb_draw_line_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED, + hb_draw_state_t *st HB_UNUSED, + float to_x HB_UNUSED, float to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +hb_draw_quadratic_to_nil (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ +#define HB_ONE_THIRD 0.33333333f + dfuncs->emit_cubic_to (draw_data, *st, + (st->current_x + 2.f * control_x) * HB_ONE_THIRD, + (st->current_y + 2.f * control_y) * HB_ONE_THIRD, + (to_x + 2.f * control_x) * HB_ONE_THIRD, + (to_y + 2.f * control_y) * HB_ONE_THIRD, + to_x, to_y); +#undef HB_ONE_THIRD +} + +static void +hb_draw_cubic_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED, + hb_draw_state_t *st HB_UNUSED, + float control1_x HB_UNUSED, float control1_y HB_UNUSED, + float control2_x HB_UNUSED, float control2_y HB_UNUSED, + float to_x HB_UNUSED, float to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +hb_draw_close_path_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED, + hb_draw_state_t *st HB_UNUSED, + void *user_data HB_UNUSED) {} + + +static bool +_hb_draw_funcs_set_preamble (hb_draw_funcs_t *dfuncs, + bool func_is_null, + void **user_data, + hb_destroy_func_t *destroy) +{ + if (hb_object_is_immutable (dfuncs)) + { + if (*destroy) + (*destroy) (*user_data); + return false; + } + + if (func_is_null) + { + if (*destroy) + (*destroy) (*user_data); + *destroy = nullptr; + *user_data = nullptr; + } + + return true; +} + +static bool +_hb_draw_funcs_set_middle (hb_draw_funcs_t *dfuncs, + void *user_data, + hb_destroy_func_t destroy) +{ + if (user_data && !dfuncs->user_data) + { + dfuncs->user_data = (decltype (dfuncs->user_data)) hb_calloc (1, sizeof (*dfuncs->user_data)); + if (unlikely (!dfuncs->user_data)) + goto fail; + } + if (destroy && !dfuncs->destroy) + { + dfuncs->destroy = (decltype (dfuncs->destroy)) hb_calloc (1, sizeof (*dfuncs->destroy)); + if (unlikely (!dfuncs->destroy)) + goto fail; + } + + return true; + +fail: + if (destroy) + (destroy) (user_data); + return false; +} + +#define HB_DRAW_FUNC_IMPLEMENT(name) \ + \ +void \ +hb_draw_funcs_set_##name##_func (hb_draw_funcs_t *dfuncs, \ + hb_draw_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ +{ \ + if (!_hb_draw_funcs_set_preamble (dfuncs, !func, &user_data, &destroy))\ + return; \ + \ + if (dfuncs->destroy && dfuncs->destroy->name) \ + dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); \ + \ + if (!_hb_draw_funcs_set_middle (dfuncs, user_data, destroy)) \ + return; \ + \ + if (func) \ + dfuncs->func.name = func; \ + else \ + dfuncs->func.name = hb_draw_##name##_nil; \ + \ + if (dfuncs->user_data) \ + dfuncs->user_data->name = user_data; \ + if (dfuncs->destroy) \ + dfuncs->destroy->name = destroy; \ +} + +HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + +/** + * hb_draw_funcs_create: + * + * Creates a new draw callbacks object. + * + * Return value: (transfer full): + * A newly allocated #hb_draw_funcs_t with a reference count of 1. The initial + * reference count should be released with hb_draw_funcs_destroy when you are + * done using the #hb_draw_funcs_t. This function never returns `NULL`. If + * memory cannot be allocated, a special singleton #hb_draw_funcs_t object will + * be returned. + * + * Since: 4.0.0 + **/ +hb_draw_funcs_t * +hb_draw_funcs_create () +{ + hb_draw_funcs_t *dfuncs; + if (unlikely (!(dfuncs = hb_object_create ()))) + return const_cast (&Null (hb_draw_funcs_t)); + + dfuncs->func = Null (hb_draw_funcs_t).func; + + return dfuncs; +} + +DEFINE_NULL_INSTANCE (hb_draw_funcs_t) = +{ + HB_OBJECT_HEADER_STATIC, + + { +#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_nil, + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } +}; + +/** + * hb_draw_funcs_get_empty: + * + * Fetches the singleton empty draw-functions structure. + * + * Return value: (transfer full): The empty draw-functions structure + * + * Since: 7.0.0 + **/ +hb_draw_funcs_t * +hb_draw_funcs_get_empty () +{ + return const_cast (&Null (hb_draw_funcs_t)); +} + +/** + * hb_draw_funcs_reference: (skip) + * @dfuncs: draw functions + * + * Increases the reference count on @dfuncs by one. + * + * This prevents @dfuncs from being destroyed until a matching + * call to hb_draw_funcs_destroy() is made. + * + * Return value: (transfer full): + * The referenced #hb_draw_funcs_t. + * + * Since: 4.0.0 + **/ +hb_draw_funcs_t * +hb_draw_funcs_reference (hb_draw_funcs_t *dfuncs) +{ + return hb_object_reference (dfuncs); +} + +/** + * hb_draw_funcs_destroy: (skip) + * @dfuncs: draw functions + * + * Deallocate the @dfuncs. + * Decreases the reference count on @dfuncs by one. If the result is zero, then + * @dfuncs and all associated resources are freed. See hb_draw_funcs_reference(). + * + * Since: 4.0.0 + **/ +void +hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs) +{ + if (!hb_object_destroy (dfuncs)) return; + + if (dfuncs->destroy) + { +#define HB_DRAW_FUNC_IMPLEMENT(name) \ + if (dfuncs->destroy->name) dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } + + hb_free (dfuncs->destroy); + hb_free (dfuncs->user_data); + + hb_free (dfuncs); +} + +/** + * hb_draw_funcs_set_user_data: (skip) + * @dfuncs: The draw-functions structure + * @key: The user-data key + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified draw-functions structure. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 7.0.0 + **/ +hb_bool_t +hb_draw_funcs_set_user_data (hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (dfuncs, key, data, destroy, replace); +} + +/** + * hb_draw_funcs_get_user_data: (skip) + * @dfuncs: The draw-functions structure + * @key: The user-data key to query + * + * Fetches the user-data associated with the specified key, + * attached to the specified draw-functions structure. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 7.0.0 + **/ +void * +hb_draw_funcs_get_user_data (const hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (dfuncs, key); +} + +/** + * hb_draw_funcs_make_immutable: + * @dfuncs: draw functions + * + * Makes @dfuncs object immutable. + * + * Since: 4.0.0 + **/ +void +hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs) +{ + if (hb_object_is_immutable (dfuncs)) + return; + + hb_object_make_immutable (dfuncs); +} + +/** + * hb_draw_funcs_is_immutable: + * @dfuncs: draw functions + * + * Checks whether @dfuncs is immutable. + * + * Return value: `true` if @dfuncs is immutable, `false` otherwise + * + * Since: 4.0.0 + **/ +hb_bool_t +hb_draw_funcs_is_immutable (hb_draw_funcs_t *dfuncs) +{ + return hb_object_is_immutable (dfuncs); +} + + +/** + * hb_draw_move_to: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * @to_x: X component of target point + * @to_y: Y component of target point + * + * Perform a "move-to" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_move_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y) +{ + dfuncs->move_to (draw_data, *st, + to_x, to_y); +} + +/** + * hb_draw_line_to: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * @to_x: X component of target point + * @to_y: Y component of target point + * + * Perform a "line-to" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_line_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y) +{ + dfuncs->line_to (draw_data, *st, + to_x, to_y); +} + +/** + * hb_draw_quadratic_to: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * @control_x: X component of control point + * @control_y: Y component of control point + * @to_x: X component of target point + * @to_y: Y component of target point + * + * Perform a "quadratic-to" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_quadratic_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y) +{ + dfuncs->quadratic_to (draw_data, *st, + control_x, control_y, + to_x, to_y); +} + +/** + * hb_draw_cubic_to: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * @control1_x: X component of first control point + * @control1_y: Y component of first control point + * @control2_x: X component of second control point + * @control2_y: Y component of second control point + * @to_x: X component of target point + * @to_y: Y component of target point + * + * Perform a "cubic-to" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_cubic_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y) +{ + dfuncs->cubic_to (draw_data, *st, + control1_x, control1_y, + control2_x, control2_y, + to_x, to_y); +} + +/** + * hb_draw_close_path: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * + * Perform a "close-path" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_close_path (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st) +{ + dfuncs->close_path (draw_data, *st); +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-draw.h b/src/3rdparty/harfbuzz-ng/src/hb-draw.h new file mode 100644 index 00000000000..9ca0b4006e4 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-draw.h @@ -0,0 +1,340 @@ +/* + * Copyright ยฉ 2019-2020 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_DRAW_H +#define HB_DRAW_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +/** + * hb_draw_state_t + * @path_open: Whether there is an open path + * @path_start_x: X component of the start of current path + * @path_start_y: Y component of the start of current path + * @current_x: X component of current point + * @current_y: Y component of current point + * + * Current drawing state. + * + * Since: 4.0.0 + **/ +typedef struct hb_draw_state_t { + hb_bool_t path_open; + + float path_start_x; + float path_start_y; + + float current_x; + float current_y; + + /*< private >*/ + hb_var_num_t reserved1; + hb_var_num_t reserved2; + hb_var_num_t reserved3; + hb_var_num_t reserved4; + hb_var_num_t reserved5; + hb_var_num_t reserved6; + hb_var_num_t reserved7; +} hb_draw_state_t; + +/** + * HB_DRAW_STATE_DEFAULT: + * + * The default #hb_draw_state_t at the start of glyph drawing. + */ +#define HB_DRAW_STATE_DEFAULT {0, 0.f, 0.f, 0.f, 0.f, {0.}, {0.}, {0.}} + + +/** + * hb_draw_funcs_t: + * + * Glyph draw callbacks. + * + * #hb_draw_move_to_func_t, #hb_draw_line_to_func_t and + * #hb_draw_cubic_to_func_t calls are necessary to be defined but we translate + * #hb_draw_quadratic_to_func_t calls to #hb_draw_cubic_to_func_t if the + * callback isn't defined. + * + * Since: 4.0.0 + **/ + +typedef struct hb_draw_funcs_t hb_draw_funcs_t; + + +/** + * hb_draw_move_to_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() + * @st: current draw state + * @to_x: X component of target point + * @to_y: Y component of target point + * @user_data: User data pointer passed to hb_draw_funcs_set_move_to_func() + * + * A virtual method for the #hb_draw_funcs_t to perform a "move-to" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_move_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data); + +/** + * hb_draw_line_to_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() + * @st: current draw state + * @to_x: X component of target point + * @to_y: Y component of target point + * @user_data: User data pointer passed to hb_draw_funcs_set_line_to_func() + * + * A virtual method for the #hb_draw_funcs_t to perform a "line-to" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_line_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data); + +/** + * hb_draw_quadratic_to_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() + * @st: current draw state + * @control_x: X component of control point + * @control_y: Y component of control point + * @to_x: X component of target point + * @to_y: Y component of target point + * @user_data: User data pointer passed to hb_draw_funcs_set_quadratic_to_func() + * + * A virtual method for the #hb_draw_funcs_t to perform a "quadratic-to" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_quadratic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data); + +/** + * hb_draw_cubic_to_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() + * @st: current draw state + * @control1_x: X component of first control point + * @control1_y: Y component of first control point + * @control2_x: X component of second control point + * @control2_y: Y component of second control point + * @to_x: X component of target point + * @to_y: Y component of target point + * @user_data: User data pointer passed to hb_draw_funcs_set_cubic_to_func() + * + * A virtual method for the #hb_draw_funcs_t to perform a "cubic-to" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_cubic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data); + +/** + * hb_draw_close_path_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() + * @st: current draw state + * @user_data: User data pointer passed to hb_draw_funcs_set_close_path_func() + * + * A virtual method for the #hb_draw_funcs_t to perform a "close-path" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_close_path_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + void *user_data); + +/** + * hb_draw_funcs_set_move_to_func: + * @dfuncs: draw functions object + * @func: (closure user_data) (destroy destroy) (scope notified): move-to callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets move-to callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *dfuncs, + hb_draw_move_to_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_draw_funcs_set_line_to_func: + * @dfuncs: draw functions object + * @func: (closure user_data) (destroy destroy) (scope notified): line-to callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets line-to callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *dfuncs, + hb_draw_line_to_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_draw_funcs_set_quadratic_to_func: + * @dfuncs: draw functions object + * @func: (closure user_data) (destroy destroy) (scope notified): quadratic-to callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets quadratic-to callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *dfuncs, + hb_draw_quadratic_to_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_draw_funcs_set_cubic_to_func: + * @dfuncs: draw functions + * @func: (closure user_data) (destroy destroy) (scope notified): cubic-to callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets cubic-to callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *dfuncs, + hb_draw_cubic_to_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_draw_funcs_set_close_path_func: + * @dfuncs: draw functions object + * @func: (closure user_data) (destroy destroy) (scope notified): close-path callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets close-path callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *dfuncs, + hb_draw_close_path_func_t func, + void *user_data, hb_destroy_func_t destroy); + + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_create (void); + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_get_empty (void); + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_reference (hb_draw_funcs_t *dfuncs); + +HB_EXTERN void +hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs); + +HB_EXTERN hb_bool_t +hb_draw_funcs_set_user_data (hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_draw_funcs_get_user_data (const hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs); + +HB_EXTERN hb_bool_t +hb_draw_funcs_is_immutable (hb_draw_funcs_t *dfuncs); + + +HB_EXTERN void +hb_draw_move_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y); + +HB_EXTERN void +hb_draw_line_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y); + +HB_EXTERN void +hb_draw_quadratic_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y); + +HB_EXTERN void +hb_draw_cubic_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y); + +HB_EXTERN void +hb_draw_close_path (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st); + + +HB_END_DECLS + +#endif /* HB_DRAW_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-draw.hh b/src/3rdparty/harfbuzz-ng/src/hb-draw.hh new file mode 100644 index 00000000000..768f51a8756 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-draw.hh @@ -0,0 +1,231 @@ +/* + * Copyright ยฉ 2020 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_DRAW_HH +#define HB_DRAW_HH + +#include "hb.hh" + + +/* + * hb_draw_funcs_t + */ + +#define HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS \ + HB_DRAW_FUNC_IMPLEMENT (move_to) \ + HB_DRAW_FUNC_IMPLEMENT (line_to) \ + HB_DRAW_FUNC_IMPLEMENT (quadratic_to) \ + HB_DRAW_FUNC_IMPLEMENT (cubic_to) \ + HB_DRAW_FUNC_IMPLEMENT (close_path) \ + /* ^--- Add new callbacks here */ + +struct hb_draw_funcs_t +{ + hb_object_header_t header; + + struct { +#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_func_t name; + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } func; + + struct { +#define HB_DRAW_FUNC_IMPLEMENT(name) void *name; + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } *user_data; + + struct { +#define HB_DRAW_FUNC_IMPLEMENT(name) hb_destroy_func_t name; + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } *destroy; + + void emit_move_to (void *draw_data, hb_draw_state_t &st, + float to_x, float to_y) + { func.move_to (this, draw_data, &st, + to_x, to_y, + !user_data ? nullptr : user_data->move_to); } + void emit_line_to (void *draw_data, hb_draw_state_t &st, + float to_x, float to_y) + { func.line_to (this, draw_data, &st, + to_x, to_y, + !user_data ? nullptr : user_data->line_to); } + void emit_quadratic_to (void *draw_data, hb_draw_state_t &st, + float control_x, float control_y, + float to_x, float to_y) + { func.quadratic_to (this, draw_data, &st, + control_x, control_y, + to_x, to_y, + !user_data ? nullptr : user_data->quadratic_to); } + void emit_cubic_to (void *draw_data, hb_draw_state_t &st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y) + { func.cubic_to (this, draw_data, &st, + control1_x, control1_y, + control2_x, control2_y, + to_x, to_y, + !user_data ? nullptr : user_data->cubic_to); } + void emit_close_path (void *draw_data, hb_draw_state_t &st) + { func.close_path (this, draw_data, &st, + !user_data ? nullptr : user_data->close_path); } + + + void move_to (void *draw_data, hb_draw_state_t &st, + float to_x, float to_y) + { + if (st.path_open) close_path (draw_data, st); + st.current_x = to_x; + st.current_y = to_y; + } + + void line_to (void *draw_data, hb_draw_state_t &st, + float to_x, float to_y) + { + if (!st.path_open) start_path (draw_data, st); + emit_line_to (draw_data, st, to_x, to_y); + st.current_x = to_x; + st.current_y = to_y; + } + + void + quadratic_to (void *draw_data, hb_draw_state_t &st, + float control_x, float control_y, + float to_x, float to_y) + { + if (!st.path_open) start_path (draw_data, st); + emit_quadratic_to (draw_data, st, control_x, control_y, to_x, to_y); + st.current_x = to_x; + st.current_y = to_y; + } + + void + cubic_to (void *draw_data, hb_draw_state_t &st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y) + { + if (!st.path_open) start_path (draw_data, st); + emit_cubic_to (draw_data, st, control1_x, control1_y, control2_x, control2_y, to_x, to_y); + st.current_x = to_x; + st.current_y = to_y; + } + + void + close_path (void *draw_data, hb_draw_state_t &st) + { + if (st.path_open) + { + if ((st.path_start_x != st.current_x) || (st.path_start_y != st.current_y)) + emit_line_to (draw_data, st, st.path_start_x, st.path_start_y); + emit_close_path (draw_data, st); + } + st.path_open = false; + st.path_start_x = st.current_x = st.path_start_y = st.current_y = 0; + } + + protected: + + void start_path (void *draw_data, hb_draw_state_t &st) + { + assert (!st.path_open); + emit_move_to (draw_data, st, st.current_x, st.current_y); + st.path_open = true; + st.path_start_x = st.current_x; + st.path_start_y = st.current_y; + } +}; +DECLARE_NULL_INSTANCE (hb_draw_funcs_t); + +struct hb_draw_session_t +{ + hb_draw_session_t (hb_draw_funcs_t *funcs_, void *draw_data_, float slant_ = 0.f) + : slant {slant_}, not_slanted {slant == 0.f}, + funcs {funcs_}, draw_data {draw_data_}, st HB_DRAW_STATE_DEFAULT + {} + + ~hb_draw_session_t () { close_path (); } + + void move_to (float to_x, float to_y) + { + if (likely (not_slanted)) + funcs->move_to (draw_data, st, + to_x, to_y); + else + funcs->move_to (draw_data, st, + to_x + to_y * slant, to_y); + } + void line_to (float to_x, float to_y) + { + if (likely (not_slanted)) + funcs->line_to (draw_data, st, + to_x, to_y); + else + funcs->line_to (draw_data, st, + to_x + to_y * slant, to_y); + } + void + quadratic_to (float control_x, float control_y, + float to_x, float to_y) + { + if (likely (not_slanted)) + funcs->quadratic_to (draw_data, st, + control_x, control_y, + to_x, to_y); + else + funcs->quadratic_to (draw_data, st, + control_x + control_y * slant, control_y, + to_x + to_y * slant, to_y); + } + void + cubic_to (float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y) + { + if (likely (not_slanted)) + funcs->cubic_to (draw_data, st, + control1_x, control1_y, + control2_x, control2_y, + to_x, to_y); + else + funcs->cubic_to (draw_data, st, + control1_x + control1_y * slant, control1_y, + control2_x + control2_y * slant, control2_y, + to_x + to_y * slant, to_y); + } + void close_path () + { + funcs->close_path (draw_data, st); + } + + protected: + float slant; + bool not_slanted; + hb_draw_funcs_t *funcs; + void *draw_data; + hb_draw_state_t st; +}; + +#endif /* HB_DRAW_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-dsalgs.hh b/src/3rdparty/harfbuzz-ng/src/hb-dsalgs.hh deleted file mode 100644 index 4e8f0431c6a..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-dsalgs.hh +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright ยฉ 2017 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_DSALGS_HH -#define HB_DSALGS_HH - -#include "hb-private.hh" - -#if defined(__ghs) -// GHS compiler doesn't support the __restrict keyword -#define __restrict -#endif - - - -static inline void * -hb_bsearch_r (const void *key, const void *base, - size_t nmemb, size_t size, - int (*compar)(const void *_key, const void *_item, void *_arg), - void *arg) -{ - int min = 0, max = (int) nmemb - 1; - while (min <= max) - { - int mid = (min + max) / 2; - const void *p = (const void *) (((const char *) base) + (mid * size)); - int c = compar (key, p, arg); - if (c < 0) - max = mid - 1; - else if (c > 0) - min = mid + 1; - else - return (void *) p; - } - return NULL; -} - - - -/* From https://github.com/noporpoise/sort_r */ - -/* Isaac Turner 29 April 2014 Public Domain */ - -/* - -hb_sort_r function to be exported. - -Parameters: - base is the array to be sorted - nel is the number of elements in the array - width is the size in bytes of each element of the array - compar is the comparison function - arg is a pointer to be passed to the comparison function - -void hb_sort_r(void *base, size_t nel, size_t width, - int (*compar)(const void *_a, const void *_b, void *_arg), - void *arg); -*/ - - -/* swap a, b iff a>b */ -/* __restrict is same as restrict but better support on old machines */ -static int sort_r_cmpswap(char *__restrict a, char *__restrict b, size_t w, - int (*compar)(const void *_a, const void *_b, - void *_arg), - void *arg) -{ - char tmp, *end = a+w; - if(compar(a, b, arg) > 0) { - for(; a < end; a++, b++) { tmp = *a; *a = *b; *b = tmp; } - return 1; - } - return 0; -} - -/* Note: quicksort is not stable, equivalent values may be swapped */ -static inline void sort_r_simple(void *base, size_t nel, size_t w, - int (*compar)(const void *_a, const void *_b, - void *_arg), - void *arg) -{ - char *b = (char *)base, *end = b + nel*w; - if(nel < 7) { - /* Insertion sort for arbitrarily small inputs */ - char *pi, *pj; - for(pi = b+w; pi < end; pi += w) { - for(pj = pi; pj > b && sort_r_cmpswap(pj-w,pj,w,compar,arg); pj -= w) {} - } - } - else - { - /* nel > 6; Quicksort */ - - /* Use median of first, middle and last items as pivot */ - char *x, *y, *xend, ch; - char *pl, *pr; - char *last = b+w*(nel-1), *tmp; - char *l[3]; - l[0] = b; - l[1] = b+w*(nel/2); - l[2] = last; - - if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; } - if(compar(l[1],l[2],arg) > 0) { - tmp=l[1]; l[1]=l[2]; l[2]=tmp; /* swap(l[1],l[2]) */ - if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; } - } - - /* swap l[id], l[2] to put pivot as last element */ - for(x = l[1], y = last, xend = x+w; x tables; +}; + +static int compare_entries (const void* pa, const void* pb) +{ + const auto& a = * (const hb_pair_t *) pa; + const auto& b = * (const hb_pair_t *) pb; + + /* Order by blob size first (smallest to largest) and then table tag */ + + if (a.second.order != b.second.order) + return a.second.order < b.second.order ? -1 : +1; + + if (a.second.data->length != b.second.data->length) + return a.second.data->length < b.second.data->length ? -1 : +1; + + return a.first < b.first ? -1 : a.first == b.first ? 0 : +1; +} + +static hb_face_builder_data_t * +_hb_face_builder_data_create () +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) hb_calloc (1, sizeof (hb_face_builder_data_t)); + if (unlikely (!data)) + return nullptr; + + data->tables.init (); + + return data; +} + +static void +_hb_face_builder_data_destroy (void *user_data) +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; + + for (auto info : data->tables.values()) + hb_blob_destroy (info.data); + + data->tables.fini (); + + hb_free (data); +} + +static hb_blob_t * +_hb_face_builder_data_reference_blob (hb_face_builder_data_t *data) +{ + + unsigned int table_count = data->tables.get_population (); + unsigned int face_length = table_count * 16 + 12; + + for (auto info : data->tables.values()) + face_length += hb_ceil_to_4 (hb_blob_get_length (info.data)); + + char *buf = (char *) hb_malloc (face_length); + if (unlikely (!buf)) + return nullptr; + + hb_serialize_context_t c (buf, face_length); + c.propagate_error (data->tables); + OT::OpenTypeFontFile *f = c.start_serialize (); + + bool is_cff = (data->tables.has (HB_TAG ('C','F','F',' ')) + || data->tables.has (HB_TAG ('C','F','F','2'))); + hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag; + + // Sort the tags so that produced face is deterministic. + hb_vector_t> sorted_entries; + data->tables.iter () | hb_sink (sorted_entries); + if (unlikely (sorted_entries.in_error ())) + { + hb_free (buf); + return nullptr; + } + + sorted_entries.qsort (compare_entries); + + bool ret = f->serialize_single (&c, + sfnt_tag, + + sorted_entries.iter() + | hb_map ([&] (hb_pair_t _) { + return hb_pair_t (_.first, _.second.data); + })); + + c.end_serialize (); + + if (unlikely (!ret)) + { + hb_free (buf); + return nullptr; + } + + return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, hb_free); +} + +static hb_blob_t * +_hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; + + if (!tag) + return _hb_face_builder_data_reference_blob (data); + + return hb_blob_reference (data->tables[tag].data); +} + + +/** + * hb_face_builder_create: + * + * Creates a #hb_face_t that can be used with hb_face_builder_add_table(). + * After tables are added to the face, it can be compiled to a binary + * font file by calling hb_face_reference_blob(). + * + * Return value: (transfer full): New face. + * + * Since: 1.9.0 + **/ +hb_face_t * +hb_face_builder_create () +{ + hb_face_builder_data_t *data = _hb_face_builder_data_create (); + if (unlikely (!data)) return hb_face_get_empty (); + + return hb_face_create_for_tables (_hb_face_builder_reference_table, + data, + _hb_face_builder_data_destroy); +} + +/** + * hb_face_builder_add_table: + * @face: A face object created with hb_face_builder_create() + * @tag: The #hb_tag_t of the table to add + * @blob: The blob containing the table data to add + * + * Add table for @tag with data provided by @blob to the face. @face must + * be created using hb_face_builder_create(). + * + * Since: 1.9.0 + **/ +hb_bool_t +hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) +{ + if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) + return false; + + if (tag == HB_MAP_VALUE_INVALID) + return false; + + hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; + + hb_blob_t* previous = data->tables.get (tag).data; + if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), -1})) + { + hb_blob_destroy (blob); + return false; + } + + hb_blob_destroy (previous); + return true; +} + +/** + * hb_face_builder_sort_tables: + * @face: A face object created with hb_face_builder_create() + * @tags: (array zero-terminated=1): ordered list of table tags terminated by + * %HB_TAG_NONE + * + * Set the ordering of tables for serialization. Any tables not + * specified in the tags list will be ordered after the tables in + * tags, ordered by the default sort ordering. + * + * Since: 5.3.0 + **/ +void +hb_face_builder_sort_tables (hb_face_t *face, + const hb_tag_t *tags) +{ + if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) + return; + + hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; + + // Sort all unspecified tables after any specified tables. + for (auto& info : data->tables.values_ref()) + info.order = (unsigned) -1; + + signed order = 0; + for (const hb_tag_t* tag = tags; + *tag; + tag++) + { + face_table_info_t* info; + if (!data->tables.has (*tag, &info)) continue; + info->order = order++; + } +} diff --git a/src/3rdparty/harfbuzz-ng/src/hb-face.cc b/src/3rdparty/harfbuzz-ng/src/hb-face.cc index 26fddbe5292..e340710586c 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-face.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-face.cc @@ -26,23 +26,70 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-private.hh" +#include "hb.hh" -#include "hb-face-private.hh" -#include "hb-open-file-private.hh" -#include "hb-ot-head-table.hh" -#include "hb-ot-maxp-table.hh" +#include "hb-face.hh" +#include "hb-blob.hh" +#include "hb-open-file.hh" +#include "hb-ot-face.hh" +#include "hb-ot-cmap-table.hh" +/** + * SECTION:hb-face + * @title: hb-face + * @short_description: Font face objects + * @include: hb.h + * + * A font face is an object that represents a single face from within a + * font family. + * + * More precisely, a font face represents a single face in a binary font file. + * Font faces are typically built from a binary blob and a face index. + * Font faces are used to create fonts. + * + * A font face can be created from a binary blob using hb_face_create(). + * The face index is used to select a face from a binary blob that contains + * multiple faces. For example, a binary blob that contains both a regular + * and a bold face can be used to create two font faces, one for each face + * index. + **/ + + +/** + * hb_face_count: + * @blob: a blob. + * + * Fetches the number of faces in a blob. + * + * Return value: Number of faces in @blob + * + * Since: 1.7.7 + **/ +unsigned int +hb_face_count (hb_blob_t *blob) +{ + if (unlikely (!blob)) + return 0; + + /* TODO We shouldn't be sanitizing blob. Port to run sanitizer and return if not sane. */ + /* Make API signature const after. */ + hb_blob_t *sanitized = hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)); + const OT::OpenTypeFontFile& ot = *sanitized->as (); + unsigned int ret = ot.get_face_count (); + hb_blob_destroy (sanitized); + + return ret; +} + /* * hb_face_t */ -const hb_face_t _hb_face_nil = { +DEFINE_NULL_INSTANCE (hb_face_t) = +{ HB_OBJECT_HEADER_STATIC, - true, /* immutable */ - nullptr, /* reference_table_func */ nullptr, /* user_data */ nullptr, /* destroy */ @@ -51,25 +98,25 @@ const hb_face_t _hb_face_nil = { 1000, /* upem */ 0, /* num_glyphs */ - { -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID, -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT - }, - - nullptr, /* shape_plans */ + /* Zero for the rest is fine. */ }; /** * hb_face_create_for_tables: - * @reference_table_func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @reference_table_func: (closure user_data) (destroy destroy) (scope notified): Table-referencing function + * @user_data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore * - * + * Variant of hb_face_create(), built for those cases where it is more + * convenient to provide data for individual tables instead of the whole font + * data. With the caveat that hb_face_get_table_tags() does not currently work + * with faces created this way. * - * Return value: (transfer full) + * Creates a new face object from the specified @user_data and @reference_table_func, + * with the @destroy callback. + * + * Return value: (transfer full): The new face object * * Since: 0.9.2 **/ @@ -90,8 +137,10 @@ hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, face->user_data = user_data; face->destroy = destroy; - face->upem = 0; - face->num_glyphs = (unsigned int) -1; + face->num_glyphs = -1; + + face->data.init0 (face); + face->table.init0 (face); return face; } @@ -99,7 +148,7 @@ hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, typedef struct hb_face_for_data_closure_t { hb_blob_t *blob; - unsigned int index; + uint16_t index; } hb_face_for_data_closure_t; static hb_face_for_data_closure_t * @@ -107,12 +156,12 @@ _hb_face_for_data_closure_create (hb_blob_t *blob, unsigned int index) { hb_face_for_data_closure_t *closure; - closure = (hb_face_for_data_closure_t *) calloc (1, sizeof (hb_face_for_data_closure_t)); + closure = (hb_face_for_data_closure_t *) hb_calloc (1, sizeof (hb_face_for_data_closure_t)); if (unlikely (!closure)) return nullptr; closure->blob = blob; - closure->index = index; + closure->index = (uint16_t) (index & 0xFFFFu); return closure; } @@ -123,7 +172,7 @@ _hb_face_for_data_closure_destroy (void *data) hb_face_for_data_closure_t *closure = (hb_face_for_data_closure_t *) data; hb_blob_destroy (closure->blob); - free (closure); + hb_free (closure); } static hb_blob_t * @@ -134,24 +183,38 @@ _hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void if (tag == HB_TAG_NONE) return hb_blob_reference (data->blob); - const OT::OpenTypeFontFile &ot_file = *OT::Sanitizer::lock_instance (data->blob); - const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index); + const OT::OpenTypeFontFile &ot_file = *data->blob->as (); + unsigned int base_offset; + const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index, &base_offset); const OT::OpenTypeTable &table = ot_face.get_table_by_tag (tag); - hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, table.offset, table.length); + hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, base_offset + table.offset, table.length); return blob; } /** - * hb_face_create: (Xconstructor) - * @blob: - * @index: + * hb_face_create: + * @blob: #hb_blob_t to work upon + * @index: The index of the face within @blob * - * + * Constructs a new face object from the specified blob and + * a face index into that blob. * - * Return value: (transfer full): + * The face index is used for blobs of file formats such as TTC and + * DFont that can contain more than one face. Face indices within + * such collections are zero-based. + * + * Note: If the blob font format is not a collection, @index + * is ignored. Otherwise, only the lower 16-bits of @index are used. + * The unmodified @index can be accessed via hb_face_get_index(). + * + * Note: The high 16-bits of @index, if non-zero, are used by + * hb_font_create() to load named-instances in variable fonts. See + * hb_font_create() for details. + * + * Return value: (transfer full): The new face object * * Since: 0.9.2 **/ @@ -164,10 +227,15 @@ hb_face_create (hb_blob_t *blob, if (unlikely (!blob)) blob = hb_blob_get_empty (); - hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (OT::Sanitizer::sanitize (hb_blob_reference (blob)), index); + blob = hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)); + + hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (blob, index); if (unlikely (!closure)) + { + hb_blob_destroy (blob); return hb_face_get_empty (); + } face = hb_face_create_for_tables (_hb_face_for_data_reference_table, closure, @@ -181,26 +249,26 @@ hb_face_create (hb_blob_t *blob, /** * hb_face_get_empty: * - * + * Fetches the singleton empty face object. * - * Return value: (transfer full) + * Return value: (transfer full): The empty face object * * Since: 0.9.2 **/ hb_face_t * -hb_face_get_empty (void) +hb_face_get_empty () { - return const_cast (&_hb_face_nil); + return const_cast (&Null (hb_face_t)); } /** * hb_face_reference: (skip) - * @face: a face. + * @face: A face object * - * + * Increases the reference count on a face object. * - * Return value: + * Return value: The @face object * * Since: 0.9.2 **/ @@ -212,9 +280,11 @@ hb_face_reference (hb_face_t *face) /** * hb_face_destroy: (skip) - * @face: a face. + * @face: A face object * - * + * Decreases the reference count on a face object. When the + * reference count reaches zero, the face is destroyed, + * freeing all memory. * * Since: 0.9.2 **/ @@ -223,35 +293,36 @@ hb_face_destroy (hb_face_t *face) { if (!hb_object_destroy (face)) return; +#ifndef HB_NO_SHAPER for (hb_face_t::plan_node_t *node = face->shape_plans; node; ) { hb_face_t::plan_node_t *next = node->next; hb_shape_plan_destroy (node->shape_plan); - free (node); + hb_free (node); node = next; } +#endif -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, face); -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT + face->data.fini (); + face->table.fini (); if (face->destroy) face->destroy (face->user_data); - free (face); + hb_free (face); } /** * hb_face_set_user_data: (skip) - * @face: a face. - * @key: - * @data: - * @destroy: - * @replace: + * @face: A face object + * @key: The user-data key to set + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key * - * + * Attaches a user-data key/data pair to the given face object. * - * Return value: + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -267,17 +338,18 @@ hb_face_set_user_data (hb_face_t *face, /** * hb_face_get_user_data: (skip) - * @face: a face. - * @key: + * @face: A face object + * @key: The user-data key to query * - * + * Fetches the user data associated with the specified key, + * attached to the specified face object. * - * Return value: (transfer none): + * Return value: (transfer none): A pointer to the user data * * Since: 0.9.2 **/ void * -hb_face_get_user_data (hb_face_t *face, +hb_face_get_user_data (const hb_face_t *face, hb_user_data_key_t *key) { return hb_object_get_user_data (face, key); @@ -285,63 +357,69 @@ hb_face_get_user_data (hb_face_t *face, /** * hb_face_make_immutable: - * @face: a face. + * @face: A face object * - * + * Makes the given face object immutable. * * Since: 0.9.2 **/ void hb_face_make_immutable (hb_face_t *face) { - if (unlikely (hb_object_is_inert (face))) + if (hb_object_is_immutable (face)) return; - face->immutable = true; + hb_object_make_immutable (face); } /** * hb_face_is_immutable: - * @face: a face. + * @face: A face object * - * + * Tests whether the given face object is immutable. * - * Return value: + * Return value: `true` is @face is immutable, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_face_is_immutable (hb_face_t *face) +hb_face_is_immutable (const hb_face_t *face) { - return face->immutable; + return hb_object_is_immutable (face); } /** * hb_face_reference_table: - * @face: a face. - * @tag: + * @face: A face object + * @tag: The #hb_tag_t of the table to query * - * + * Fetches a reference to the specified table within + * the specified face. * - * Return value: (transfer full): + * Return value: (transfer full): A pointer to the @tag table within @face * * Since: 0.9.2 **/ hb_blob_t * -hb_face_reference_table (hb_face_t *face, - hb_tag_t tag) +hb_face_reference_table (const hb_face_t *face, + hb_tag_t tag) { + if (unlikely (tag == HB_TAG_NONE)) + return hb_blob_get_empty (); + return face->reference_table (tag); } /** * hb_face_reference_blob: - * @face: a face. + * @face: A face object * - * + * Fetches a pointer to the binary blob that contains the + * specified face. Returns an empty blob if referencing face data is not + * possible. * - * Return value: (transfer full): + * Return value: (transfer full): A pointer to the blob for @face * * Since: 0.9.2 **/ @@ -353,10 +431,14 @@ hb_face_reference_blob (hb_face_t *face) /** * hb_face_set_index: - * @face: a face. - * @index: + * @face: A face object + * @index: The index to assign * - * + * Assigns the specified face-index to @face. Fails if the + * face is immutable. + * + * Note: changing the index has no effect on the face itself + * This only changes the value returned by hb_face_get_index(). * * Since: 0.9.2 **/ @@ -364,7 +446,7 @@ void hb_face_set_index (hb_face_t *face, unsigned int index) { - if (face->immutable) + if (hb_object_is_immutable (face)) return; face->index = index; @@ -372,26 +454,30 @@ hb_face_set_index (hb_face_t *face, /** * hb_face_get_index: - * @face: a face. + * @face: A face object * - * + * Fetches the face-index corresponding to the given face. * - * Return value: + * Note: face indices within a collection are zero-based. + * + * Return value: The index of @face. * * Since: 0.9.2 **/ unsigned int -hb_face_get_index (hb_face_t *face) +hb_face_get_index (const hb_face_t *face) { return face->index; } /** * hb_face_set_upem: - * @face: a face. - * @upem: + * @face: A face object + * @upem: The units-per-em value to assign * - * + * Sets the units-per-em (upem) for a face object to the specified value. + * + * This API is used in rare circumstances. * * Since: 0.9.2 **/ @@ -399,7 +485,7 @@ void hb_face_set_upem (hb_face_t *face, unsigned int upem) { - if (face->immutable) + if (hb_object_is_immutable (face)) return; face->upem = upem; @@ -407,35 +493,31 @@ hb_face_set_upem (hb_face_t *face, /** * hb_face_get_upem: - * @face: a face. + * @face: A face object * - * + * Fetches the units-per-em (UPEM) value of the specified face object. * - * Return value: + * Typical UPEM values for fonts are 1000, or 2048, but any value + * in between 16 and 16,384 is allowed for OpenType fonts. + * + * Return value: The upem value of @face * * Since: 0.9.2 **/ unsigned int -hb_face_get_upem (hb_face_t *face) +hb_face_get_upem (const hb_face_t *face) { return face->get_upem (); } -void -hb_face_t::load_upem (void) const -{ - hb_blob_t *head_blob = OT::Sanitizer::sanitize (reference_table (HB_OT_TAG_head)); - const OT::head *head_table = OT::Sanitizer::lock_instance (head_blob); - upem = head_table->get_upem (); - hb_blob_destroy (head_blob); -} - /** * hb_face_set_glyph_count: - * @face: a face. - * @glyph_count: + * @face: A face object + * @glyph_count: The glyph-count value to assign * - * + * Sets the glyph count for a face object to the specified value. + * + * This API is used in rare circumstances. * * Since: 0.9.7 **/ @@ -443,7 +525,7 @@ void hb_face_set_glyph_count (hb_face_t *face, unsigned int glyph_count) { - if (face->immutable) + if (hb_object_is_immutable (face)) return; face->num_glyphs = glyph_count; @@ -451,46 +533,42 @@ hb_face_set_glyph_count (hb_face_t *face, /** * hb_face_get_glyph_count: - * @face: a face. + * @face: A face object * - * + * Fetches the glyph-count value of the specified face object. * - * Return value: + * Return value: The glyph-count value of @face * * Since: 0.9.7 **/ unsigned int -hb_face_get_glyph_count (hb_face_t *face) +hb_face_get_glyph_count (const hb_face_t *face) { return face->get_num_glyphs (); } -void -hb_face_t::load_num_glyphs (void) const -{ - hb_blob_t *maxp_blob = OT::Sanitizer::sanitize (reference_table (HB_OT_TAG_maxp)); - const OT::maxp *maxp_table = OT::Sanitizer::lock_instance (maxp_blob); - num_glyphs = maxp_table->get_num_glyphs (); - hb_blob_destroy (maxp_blob); -} - /** * hb_face_get_table_tags: - * @face: a face. + * @face: A face object + * @start_offset: The index of first table tag to retrieve + * @table_count: (inout): Input = the maximum number of table tags to return; + * Output = the actual number of table tags returned (may be zero) + * @table_tags: (out) (array length=table_count): The array of table tags found * - * Retrieves table tags for a face, if possible. + * Fetches a list of all table tags for a face, if possible. The list returned will + * begin at the offset provided * - * Return value: total number of tables, or 0 if not possible to list. + * Return value: Total number of tables, or zero if it is not possible to list * * Since: 1.6.0 **/ unsigned int -hb_face_get_table_tags (hb_face_t *face, +hb_face_get_table_tags (const hb_face_t *face, unsigned int start_offset, unsigned int *table_count, /* IN/OUT */ hb_tag_t *table_tags /* OUT */) { - if (face->destroy != _hb_face_for_data_closure_destroy) + if (face->destroy != (hb_destroy_func_t) _hb_face_for_data_closure_destroy) { if (table_count) *table_count = 0; @@ -499,8 +577,88 @@ hb_face_get_table_tags (hb_face_t *face, hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) face->user_data; - const OT::OpenTypeFontFile &ot_file = *OT::Sanitizer::lock_instance (data->blob); + const OT::OpenTypeFontFile &ot_file = *data->blob->as (); const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index); return ot_face.get_table_tags (start_offset, table_count, table_tags); } + + +/* + * Character set. + */ + + +#ifndef HB_NO_FACE_COLLECT_UNICODES +/** + * hb_face_collect_unicodes: + * @face: A face object + * @out: (out): The set to add Unicode characters to + * + * Collects all of the Unicode characters covered by @face and adds + * them to the #hb_set_t set @out. + * + * Since: 1.9.0 + */ +void +hb_face_collect_unicodes (hb_face_t *face, + hb_set_t *out) +{ + face->table.cmap->collect_unicodes (out, face->get_num_glyphs ()); +} +/** + * hb_face_collect_nominal_glyph_mapping: + * @face: A face object + * @mapping: (out): The map to add Unicode-to-glyph mapping to + * @unicodes: (nullable) (out): The set to add Unicode characters to, or `NULL` + * + * Collects the mapping from Unicode characters to nominal glyphs of the @face, + * and optionally all of the Unicode characters covered by @face. + * + * Since: 7.0.0 + */ +void +hb_face_collect_nominal_glyph_mapping (hb_face_t *face, + hb_map_t *mapping, + hb_set_t *unicodes) +{ + hb_set_t stack_unicodes; + if (!unicodes) + unicodes = &stack_unicodes; + face->table.cmap->collect_mapping (unicodes, mapping, face->get_num_glyphs ()); +} +/** + * hb_face_collect_variation_selectors: + * @face: A face object + * @out: (out): The set to add Variation Selector characters to + * + * Collects all Unicode "Variation Selector" characters covered by @face and adds + * them to the #hb_set_t set @out. + * + * Since: 1.9.0 + */ +void +hb_face_collect_variation_selectors (hb_face_t *face, + hb_set_t *out) +{ + face->table.cmap->collect_variation_selectors (out); +} +/** + * hb_face_collect_variation_unicodes: + * @face: A face object + * @variation_selector: The Variation Selector to query + * @out: (out): The set to add Unicode characters to + * + * Collects all Unicode characters for @variation_selector covered by @face and adds + * them to the #hb_set_t set @out. + * + * Since: 1.9.0 + */ +void +hb_face_collect_variation_unicodes (hb_face_t *face, + hb_codepoint_t variation_selector, + hb_set_t *out) +{ + face->table.cmap->collect_variation_unicodes (variation_selector, out); +} +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-face.h b/src/3rdparty/harfbuzz-ng/src/hb-face.h index 9842d52b650..2e54ccf13b7 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-face.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-face.h @@ -24,7 +24,7 @@ * Red Hat Author(s): Behdad Esfahbod */ -#ifndef HB_H_IN +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif @@ -33,20 +33,45 @@ #include "hb-common.h" #include "hb-blob.h" +#include "hb-map.h" +#include "hb-set.h" HB_BEGIN_DECLS +HB_EXTERN unsigned int +hb_face_count (hb_blob_t *blob); + + /* * hb_face_t */ +/** + * hb_face_t: + * + * Data type for holding font faces. + * + **/ typedef struct hb_face_t hb_face_t; HB_EXTERN hb_face_t * hb_face_create (hb_blob_t *blob, unsigned int index); +/** + * hb_reference_table_func_t: + * @face: an #hb_face_t to reference table for + * @tag: the tag of the table to reference + * @user_data: User data pointer passed by the caller + * + * Callback function for hb_face_create_for_tables(). + * + * Return value: (transfer full): A pointer to the @tag table within @face + * + * Since: 0.9.2 + */ + typedef hb_blob_t * (*hb_reference_table_func_t) (hb_face_t *face, hb_tag_t tag, void *user_data); /* calls destroy() when not needing user_data anymore */ @@ -71,21 +96,20 @@ hb_face_set_user_data (hb_face_t *face, hb_destroy_func_t destroy, hb_bool_t replace); - HB_EXTERN void * -hb_face_get_user_data (hb_face_t *face, +hb_face_get_user_data (const hb_face_t *face, hb_user_data_key_t *key); HB_EXTERN void hb_face_make_immutable (hb_face_t *face); HB_EXTERN hb_bool_t -hb_face_is_immutable (hb_face_t *face); +hb_face_is_immutable (const hb_face_t *face); HB_EXTERN hb_blob_t * -hb_face_reference_table (hb_face_t *face, - hb_tag_t tag); +hb_face_reference_table (const hb_face_t *face, + hb_tag_t tag); HB_EXTERN hb_blob_t * hb_face_reference_blob (hb_face_t *face); @@ -95,28 +119,69 @@ hb_face_set_index (hb_face_t *face, unsigned int index); HB_EXTERN unsigned int -hb_face_get_index (hb_face_t *face); +hb_face_get_index (const hb_face_t *face); HB_EXTERN void hb_face_set_upem (hb_face_t *face, unsigned int upem); HB_EXTERN unsigned int -hb_face_get_upem (hb_face_t *face); +hb_face_get_upem (const hb_face_t *face); HB_EXTERN void hb_face_set_glyph_count (hb_face_t *face, unsigned int glyph_count); HB_EXTERN unsigned int -hb_face_get_glyph_count (hb_face_t *face); +hb_face_get_glyph_count (const hb_face_t *face); HB_EXTERN unsigned int -hb_face_get_table_tags (hb_face_t *face, +hb_face_get_table_tags (const hb_face_t *face, unsigned int start_offset, unsigned int *table_count, /* IN/OUT */ hb_tag_t *table_tags /* OUT */); + +/* + * Character set. + */ + +HB_EXTERN void +hb_face_collect_unicodes (hb_face_t *face, + hb_set_t *out); + +HB_EXTERN void +hb_face_collect_nominal_glyph_mapping (hb_face_t *face, + hb_map_t *mapping, + hb_set_t *unicodes); + +HB_EXTERN void +hb_face_collect_variation_selectors (hb_face_t *face, + hb_set_t *out); + +HB_EXTERN void +hb_face_collect_variation_unicodes (hb_face_t *face, + hb_codepoint_t variation_selector, + hb_set_t *out); + + +/* + * Builder face. + */ + +HB_EXTERN hb_face_t * +hb_face_builder_create (void); + +HB_EXTERN hb_bool_t +hb_face_builder_add_table (hb_face_t *face, + hb_tag_t tag, + hb_blob_t *blob); + +HB_EXTERN void +hb_face_builder_sort_tables (hb_face_t *face, + const hb_tag_t *tags); + + HB_END_DECLS #endif /* HB_FACE_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-face-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-face.hh similarity index 58% rename from src/3rdparty/harfbuzz-ng/src/hb-face-private.hh rename to src/3rdparty/harfbuzz-ng/src/hb-face.hh index 43e7b1cb3ff..aff3ff0d07c 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-face-private.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-face.hh @@ -26,86 +26,86 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_FACE_PRIVATE_HH -#define HB_FACE_PRIVATE_HH +#ifndef HB_FACE_HH +#define HB_FACE_HH -#include "hb-private.hh" +#include "hb.hh" -#include "hb-object-private.hh" -#include "hb-shaper-private.hh" -#include "hb-shape-plan-private.hh" +#include "hb-shaper.hh" +#include "hb-shape-plan.hh" +#include "hb-ot-face.hh" /* * hb_face_t */ -struct hb_face_t { - hb_object_header_t header; - ASSERT_POD (); +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, face); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT - hb_bool_t immutable; +struct hb_face_t +{ + hb_object_header_t header; hb_reference_table_func_t reference_table_func; void *user_data; hb_destroy_func_t destroy; unsigned int index; /* Face index in a collection, zero-based. */ - mutable unsigned int upem; /* Units-per-EM. */ - mutable unsigned int num_glyphs; /* Number of glyphs. */ + mutable hb_atomic_int_t upem; /* Units-per-EM. */ + mutable hb_atomic_int_t num_glyphs; /* Number of glyphs. */ - struct hb_shaper_data_t shaper_data; /* Various shaper data. */ - - /* Various non-shaping data. */ - /* ... */ + hb_shaper_object_dataset_t data;/* Various shaper data. */ + hb_ot_face_t table; /* All the face's tables. */ /* Cache */ - struct plan_node_t { + struct plan_node_t + { hb_shape_plan_t *shape_plan; plan_node_t *next; - } *shape_plans; + }; +#ifndef HB_NO_SHAPER + hb_atomic_ptr_t shape_plans; +#endif - - inline hb_blob_t *reference_table (hb_tag_t tag) const + hb_blob_t *reference_table (hb_tag_t tag) const { hb_blob_t *blob; if (unlikely (!reference_table_func)) return hb_blob_get_empty (); - blob = reference_table_func (/*XXX*/const_cast (this), tag, user_data); + blob = reference_table_func (/*Oh, well.*/const_cast (this), tag, user_data); if (unlikely (!blob)) return hb_blob_get_empty (); return blob; } - inline HB_PURE_FUNC unsigned int get_upem (void) const + unsigned int get_upem () const { - if (unlikely (!upem)) - load_upem (); - return upem; + unsigned int ret = upem; + if (unlikely (!ret)) + { + return load_upem (); + } + return ret; } - inline unsigned int get_num_glyphs (void) const + unsigned int get_num_glyphs () const { - if (unlikely (num_glyphs == (unsigned int) -1)) - load_num_glyphs (); - return num_glyphs; + unsigned int ret = num_glyphs; + if (unlikely (ret == UINT_MAX)) + return load_num_glyphs (); + return ret; } private: - HB_INTERNAL void load_upem (void) const; - HB_INTERNAL void load_num_glyphs (void) const; + HB_INTERNAL unsigned int load_upem () const; + HB_INTERNAL unsigned int load_num_glyphs () const; }; - -extern HB_INTERNAL const hb_face_t _hb_face_nil; - -#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, face); -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT -#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS +DECLARE_NULL_INSTANCE (hb_face_t); -#endif /* HB_FACE_PRIVATE_HH */ +#endif /* HB_FACE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-fallback-shape.cc b/src/3rdparty/harfbuzz-ng/src/hb-fallback-shape.cc index 3f09c3f5306..c54ad8764bc 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-fallback-shape.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-fallback-shape.cc @@ -24,28 +24,24 @@ * Google Author(s): Behdad Esfahbod */ -#define HB_SHAPER fallback -#include "hb-shaper-impl-private.hh" - - -HB_SHAPER_DATA_ENSURE_DEFINE(fallback, face) -HB_SHAPER_DATA_ENSURE_DEFINE(fallback, font) +#include "hb-shaper-impl.hh" +#ifndef HB_NO_FALLBACK_SHAPE /* * shaper face data */ -struct hb_fallback_shaper_face_data_t {}; +struct hb_fallback_face_data_t {}; -hb_fallback_shaper_face_data_t * +hb_fallback_face_data_t * _hb_fallback_shaper_face_data_create (hb_face_t *face HB_UNUSED) { - return (hb_fallback_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; + return (hb_fallback_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; } void -_hb_fallback_shaper_face_data_destroy (hb_fallback_shaper_face_data_t *data HB_UNUSED) +_hb_fallback_shaper_face_data_destroy (hb_fallback_face_data_t *data HB_UNUSED) { } @@ -54,38 +50,16 @@ _hb_fallback_shaper_face_data_destroy (hb_fallback_shaper_face_data_t *data HB_U * shaper font data */ -struct hb_fallback_shaper_font_data_t {}; +struct hb_fallback_font_data_t {}; -hb_fallback_shaper_font_data_t * +hb_fallback_font_data_t * _hb_fallback_shaper_font_data_create (hb_font_t *font HB_UNUSED) { - return (hb_fallback_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; + return (hb_fallback_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; } void -_hb_fallback_shaper_font_data_destroy (hb_fallback_shaper_font_data_t *data HB_UNUSED) -{ -} - - -/* - * shaper shape_plan data - */ - -struct hb_fallback_shaper_shape_plan_data_t {}; - -hb_fallback_shaper_shape_plan_data_t * -_hb_fallback_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, - const hb_feature_t *user_features HB_UNUSED, - unsigned int num_user_features HB_UNUSED, - const int *coords HB_UNUSED, - unsigned int num_coords HB_UNUSED) -{ - return (hb_fallback_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; -} - -void -_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shaper_shape_plan_data_t *data HB_UNUSED) +_hb_fallback_shaper_font_data_destroy (hb_fallback_font_data_t *data HB_UNUSED) { } @@ -101,16 +75,6 @@ _hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, const hb_feature_t *features HB_UNUSED, unsigned int num_features HB_UNUSED) { - /* TODO - * - * - Apply fallback kern. - * - Handle Variation Selectors? - * - Apply normalization? - * - * This will make the fallback shaper into a dumb "TrueType" - * shaper which many people unfortunately still request. - */ - hb_codepoint_t space; bool has_space = (bool) font->get_nominal_glyph (' ', &space); @@ -143,7 +107,9 @@ _hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, if (HB_DIRECTION_IS_BACKWARD (direction)) hb_buffer_reverse (buffer); - buffer->safe_to_break_all (); + buffer->clear_glyph_flags (); return true; } + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-font-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-font-private.hh deleted file mode 100644 index d2801fb86ec..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-font-private.hh +++ /dev/null @@ -1,555 +0,0 @@ -/* - * Copyright ยฉ 2009 Red Hat, Inc. - * Copyright ยฉ 2011 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_FONT_PRIVATE_HH -#define HB_FONT_PRIVATE_HH - -#include "hb-private.hh" - -#include "hb-object-private.hh" -#include "hb-face-private.hh" -#include "hb-shaper-private.hh" - - - -/* - * hb_font_funcs_t - */ - -#define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \ - HB_FONT_FUNC_IMPLEMENT (font_h_extents) \ - HB_FONT_FUNC_IMPLEMENT (font_v_extents) \ - HB_FONT_FUNC_IMPLEMENT (nominal_glyph) \ - HB_FONT_FUNC_IMPLEMENT (variation_glyph) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning) \ - HB_FONT_FUNC_IMPLEMENT (glyph_extents) \ - HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \ - HB_FONT_FUNC_IMPLEMENT (glyph_name) \ - HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \ - /* ^--- Add new callbacks here */ - -struct hb_font_funcs_t { - hb_object_header_t header; - ASSERT_POD (); - - hb_bool_t immutable; - - struct { -#define HB_FONT_FUNC_IMPLEMENT(name) void *name; - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - } user_data; - - struct { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_destroy_func_t name; - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - } destroy; - - /* Don't access these directly. Call font->get_*() instead. */ - union get_t { - struct get_funcs_t { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_func_t name; - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - } f; - void (*array[VAR]) (void); - } get; -}; - - - -/* - * hb_font_t - */ - -struct hb_font_t { - hb_object_header_t header; - ASSERT_POD (); - - hb_bool_t immutable; - - hb_font_t *parent; - hb_face_t *face; - - int x_scale; - int y_scale; - - unsigned int x_ppem; - unsigned int y_ppem; - - float ptem; - - /* Font variation coordinates. */ - unsigned int num_coords; - int *coords; - - hb_font_funcs_t *klass; - void *user_data; - hb_destroy_func_t destroy; - - struct hb_shaper_data_t shaper_data; - - - /* Convert from font-space to user-space */ - inline int dir_scale (hb_direction_t direction) - { return HB_DIRECTION_IS_VERTICAL(direction) ? y_scale : x_scale; } - inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, x_scale); } - inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, y_scale); } - inline hb_position_t em_scalef_x (float v) { return em_scalef (v, this->x_scale); } - inline hb_position_t em_scalef_y (float v) { return em_scalef (v, this->y_scale); } - inline hb_position_t em_scale_dir (int16_t v, hb_direction_t direction) - { return em_scale (v, dir_scale (direction)); } - - /* Convert from parent-font user-space to our user-space */ - inline hb_position_t parent_scale_x_distance (hb_position_t v) { - if (unlikely (parent && parent->x_scale != x_scale)) - return (hb_position_t) (v * (int64_t) this->x_scale / this->parent->x_scale); - return v; - } - inline hb_position_t parent_scale_y_distance (hb_position_t v) { - if (unlikely (parent && parent->y_scale != y_scale)) - return (hb_position_t) (v * (int64_t) this->y_scale / this->parent->y_scale); - return v; - } - inline hb_position_t parent_scale_x_position (hb_position_t v) { - return parent_scale_x_distance (v); - } - inline hb_position_t parent_scale_y_position (hb_position_t v) { - return parent_scale_y_distance (v); - } - - inline void parent_scale_distance (hb_position_t *x, hb_position_t *y) { - *x = parent_scale_x_distance (*x); - *y = parent_scale_y_distance (*y); - } - inline void parent_scale_position (hb_position_t *x, hb_position_t *y) { - *x = parent_scale_x_position (*x); - *y = parent_scale_y_position (*y); - } - - - /* Public getters */ - - HB_INTERNAL bool has_func (unsigned int i); - - /* has_* ... */ -#define HB_FONT_FUNC_IMPLEMENT(name) \ - bool \ - has_##name##_func (void) \ - { \ - hb_font_funcs_t *funcs = this->klass; \ - unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \ - return has_func (i); \ - } - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - - inline hb_bool_t get_font_h_extents (hb_font_extents_t *extents) - { - memset (extents, 0, sizeof (*extents)); - return klass->get.f.font_h_extents (this, user_data, - extents, - klass->user_data.font_h_extents); - } - inline hb_bool_t get_font_v_extents (hb_font_extents_t *extents) - { - memset (extents, 0, sizeof (*extents)); - return klass->get.f.font_v_extents (this, user_data, - extents, - klass->user_data.font_v_extents); - } - - inline bool has_glyph (hb_codepoint_t unicode) - { - hb_codepoint_t glyph; - return get_nominal_glyph (unicode, &glyph); - } - - inline hb_bool_t get_nominal_glyph (hb_codepoint_t unicode, - hb_codepoint_t *glyph) - { - *glyph = 0; - return klass->get.f.nominal_glyph (this, user_data, - unicode, glyph, - klass->user_data.nominal_glyph); - } - - inline hb_bool_t get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector, - hb_codepoint_t *glyph) - { - *glyph = 0; - return klass->get.f.variation_glyph (this, user_data, - unicode, variation_selector, glyph, - klass->user_data.variation_glyph); - } - - inline hb_position_t get_glyph_h_advance (hb_codepoint_t glyph) - { - return klass->get.f.glyph_h_advance (this, user_data, - glyph, - klass->user_data.glyph_h_advance); - } - - inline hb_position_t get_glyph_v_advance (hb_codepoint_t glyph) - { - return klass->get.f.glyph_v_advance (this, user_data, - glyph, - klass->user_data.glyph_v_advance); - } - - inline hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - *x = *y = 0; - return klass->get.f.glyph_h_origin (this, user_data, - glyph, x, y, - klass->user_data.glyph_h_origin); - } - - inline hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - *x = *y = 0; - return klass->get.f.glyph_v_origin (this, user_data, - glyph, x, y, - klass->user_data.glyph_v_origin); - } - - inline hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, hb_codepoint_t right_glyph) - { - return klass->get.f.glyph_h_kerning (this, user_data, - left_glyph, right_glyph, - klass->user_data.glyph_h_kerning); - } - - inline hb_position_t get_glyph_v_kerning (hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph) - { - return klass->get.f.glyph_v_kerning (this, user_data, - top_glyph, bottom_glyph, - klass->user_data.glyph_v_kerning); - } - - inline hb_bool_t get_glyph_extents (hb_codepoint_t glyph, - hb_glyph_extents_t *extents) - { - memset (extents, 0, sizeof (*extents)); - return klass->get.f.glyph_extents (this, user_data, - glyph, - extents, - klass->user_data.glyph_extents); - } - - inline hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index, - hb_position_t *x, hb_position_t *y) - { - *x = *y = 0; - return klass->get.f.glyph_contour_point (this, user_data, - glyph, point_index, - x, y, - klass->user_data.glyph_contour_point); - } - - inline hb_bool_t get_glyph_name (hb_codepoint_t glyph, - char *name, unsigned int size) - { - if (size) *name = '\0'; - return klass->get.f.glyph_name (this, user_data, - glyph, - name, size, - klass->user_data.glyph_name); - } - - inline hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */ - hb_codepoint_t *glyph) - { - *glyph = 0; - if (len == -1) len = strlen (name); - return klass->get.f.glyph_from_name (this, user_data, - name, len, - glyph, - klass->user_data.glyph_from_name); - } - - - /* A bit higher-level, and with fallback */ - - inline void get_h_extents_with_fallback (hb_font_extents_t *extents) - { - if (!get_font_h_extents (extents)) - { - extents->ascender = y_scale * .8; - extents->descender = extents->ascender - y_scale; - extents->line_gap = 0; - } - } - inline void get_v_extents_with_fallback (hb_font_extents_t *extents) - { - if (!get_font_v_extents (extents)) - { - extents->ascender = x_scale / 2; - extents->descender = extents->ascender - x_scale; - extents->line_gap = 0; - } - } - - inline void get_extents_for_direction (hb_direction_t direction, - hb_font_extents_t *extents) - { - if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) - get_h_extents_with_fallback (extents); - else - get_v_extents_with_fallback (extents); - } - - inline void get_glyph_advance_for_direction (hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) - { - if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { - *x = get_glyph_h_advance (glyph); - *y = 0; - } else { - *x = 0; - *y = get_glyph_v_advance (glyph); - } - } - - inline void guess_v_origin_minus_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - *x = get_glyph_h_advance (glyph) / 2; - - /* TODO cache this somehow?! */ - hb_font_extents_t extents; - get_h_extents_with_fallback (&extents); - *y = extents.ascender; - } - - inline void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - if (!get_glyph_h_origin (glyph, x, y) && - get_glyph_v_origin (glyph, x, y)) - { - hb_position_t dx, dy; - guess_v_origin_minus_h_origin (glyph, &dx, &dy); - *x -= dx; *y -= dy; - } - } - inline void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - if (!get_glyph_v_origin (glyph, x, y) && - get_glyph_h_origin (glyph, x, y)) - { - hb_position_t dx, dy; - guess_v_origin_minus_h_origin (glyph, &dx, &dy); - *x += dx; *y += dy; - } - } - - inline void get_glyph_origin_for_direction (hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) - { - if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) - get_glyph_h_origin_with_fallback (glyph, x, y); - else - get_glyph_v_origin_with_fallback (glyph, x, y); - } - - inline void add_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - hb_position_t origin_x, origin_y; - - get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x += origin_x; - *y += origin_y; - } - inline void add_glyph_v_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - hb_position_t origin_x, origin_y; - - get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x += origin_x; - *y += origin_y; - } - inline void add_glyph_origin_for_direction (hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) - { - hb_position_t origin_x, origin_y; - - get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); - - *x += origin_x; - *y += origin_y; - } - - inline void subtract_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - hb_position_t origin_x, origin_y; - - get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x -= origin_x; - *y -= origin_y; - } - inline void subtract_glyph_v_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - hb_position_t origin_x, origin_y; - - get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x -= origin_x; - *y -= origin_y; - } - inline void subtract_glyph_origin_for_direction (hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) - { - hb_position_t origin_x, origin_y; - - get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); - - *x -= origin_x; - *y -= origin_y; - } - - inline void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) - { - if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { - *x = get_glyph_h_kerning (first_glyph, second_glyph); - *y = 0; - } else { - *x = 0; - *y = get_glyph_v_kerning (first_glyph, second_glyph); - } - } - - inline hb_bool_t get_glyph_extents_for_origin (hb_codepoint_t glyph, - hb_direction_t direction, - hb_glyph_extents_t *extents) - { - hb_bool_t ret = get_glyph_extents (glyph, extents); - - if (ret) - subtract_glyph_origin_for_direction (glyph, direction, &extents->x_bearing, &extents->y_bearing); - - return ret; - } - - inline hb_bool_t get_glyph_contour_point_for_origin (hb_codepoint_t glyph, unsigned int point_index, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) - { - hb_bool_t ret = get_glyph_contour_point (glyph, point_index, x, y); - - if (ret) - subtract_glyph_origin_for_direction (glyph, direction, x, y); - - return ret; - } - - /* Generates gidDDD if glyph has no name. */ - inline void - glyph_to_string (hb_codepoint_t glyph, - char *s, unsigned int size) - { - if (get_glyph_name (glyph, s, size)) return; - - if (size && snprintf (s, size, "gid%u", glyph) < 0) - *s = '\0'; - } - - /* Parses gidDDD and uniUUUU strings automatically. */ - inline hb_bool_t - glyph_from_string (const char *s, int len, /* -1 means nul-terminated */ - hb_codepoint_t *glyph) - { - if (get_glyph_from_name (s, len, glyph)) return true; - - if (len == -1) len = strlen (s); - - /* Straight glyph index. */ - if (hb_codepoint_parse (s, len, 10, glyph)) - return true; - - if (len > 3) - { - /* gidDDD syntax for glyph indices. */ - if (0 == strncmp (s, "gid", 3) && - hb_codepoint_parse (s + 3, len - 3, 10, glyph)) - return true; - - /* uniUUUU and other Unicode character indices. */ - hb_codepoint_t unichar; - if (0 == strncmp (s, "uni", 3) && - hb_codepoint_parse (s + 3, len - 3, 16, &unichar) && - get_nominal_glyph (unichar, glyph)) - return true; - } - - return false; - } - - inline hb_position_t em_scale (int16_t v, int scale) - { - int upem = face->get_upem (); - int64_t scaled = v * (int64_t) scale; - scaled += scaled >= 0 ? upem/2 : -upem/2; /* Round. */ - return (hb_position_t) (scaled / upem); - } - inline hb_position_t em_scalef (float v, int scale) - { - return (hb_position_t) (v * scale / face->get_upem ()); - } -}; - -#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, font); -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT -#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS - - -#endif /* HB_FONT_PRIVATE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-font.cc b/src/3rdparty/harfbuzz-ng/src/hb-font.cc index f3534b686bf..688513112a7 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-font.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-font.cc @@ -26,9 +26,45 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-private.hh" +#include "hb.hh" -#include "hb-font-private.hh" +#include "hb-font.hh" +#include "hb-draw.hh" +#include "hb-paint.hh" +#include "hb-machinery.hh" + +#include "hb-ot.h" + +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-fvar-table.hh" + + +/** + * SECTION:hb-font + * @title: hb-font + * @short_description: Font objects + * @include: hb.h + * + * Functions for working with font objects. + * + * A font object represents a font face at a specific size and with + * certain other parameters (pixels-per-em, points-per-em, variation + * settings) specified. Font objects are created from font face + * objects, and are used as input to hb_shape(), among other things. + * + * Client programs can optionally pass in their own functions that + * implement the basic, lower-level queries of font objects. This set + * of font functions is defined by the virtual methods in + * #hb_font_funcs_t. + * + * HarfBuzz provides a built-in set of lightweight default + * functions for each method in #hb_font_funcs_t. + * + * The default font functions are implemented in terms of the + * #hb_font_funcs_t methods of the parent font object. This allows + * client programs to override only the methods they need to, and + * otherwise inherit the parent font's implementation, if any. + **/ /* @@ -36,149 +72,267 @@ */ static hb_bool_t -hb_font_get_font_h_extents_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_font_extents_t *metrics, - void *user_data HB_UNUSED) +hb_font_get_font_h_extents_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) { - memset (metrics, 0, sizeof (*metrics)); + hb_memset (extents, 0, sizeof (*extents)); return false; } + static hb_bool_t -hb_font_get_font_h_extents_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_font_extents_t *metrics, - void *user_data HB_UNUSED) +hb_font_get_font_h_extents_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) { - hb_bool_t ret = font->parent->get_font_h_extents (metrics); + hb_bool_t ret = font->parent->get_font_h_extents (extents); if (ret) { - metrics->ascender = font->parent_scale_y_distance (metrics->ascender); - metrics->descender = font->parent_scale_y_distance (metrics->descender); - metrics->line_gap = font->parent_scale_y_distance (metrics->line_gap); + extents->ascender = font->parent_scale_y_distance (extents->ascender); + extents->descender = font->parent_scale_y_distance (extents->descender); + extents->line_gap = font->parent_scale_y_distance (extents->line_gap); } return ret; } static hb_bool_t -hb_font_get_font_v_extents_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_font_extents_t *metrics, - void *user_data HB_UNUSED) +hb_font_get_font_v_extents_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) { - memset (metrics, 0, sizeof (*metrics)); + hb_memset (extents, 0, sizeof (*extents)); return false; } + static hb_bool_t -hb_font_get_font_v_extents_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_font_extents_t *metrics, - void *user_data HB_UNUSED) +hb_font_get_font_v_extents_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) { - hb_bool_t ret = font->parent->get_font_v_extents (metrics); + hb_bool_t ret = font->parent->get_font_v_extents (extents); if (ret) { - metrics->ascender = font->parent_scale_x_distance (metrics->ascender); - metrics->descender = font->parent_scale_x_distance (metrics->descender); - metrics->line_gap = font->parent_scale_x_distance (metrics->line_gap); + extents->ascender = font->parent_scale_x_distance (extents->ascender); + extents->descender = font->parent_scale_x_distance (extents->descender); + extents->line_gap = font->parent_scale_x_distance (extents->line_gap); } return ret; } static hb_bool_t -hb_font_get_nominal_glyph_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t unicode, +hb_font_get_nominal_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, hb_codepoint_t *glyph, - void *user_data HB_UNUSED) + void *user_data HB_UNUSED) { *glyph = 0; return false; } + static hb_bool_t -hb_font_get_nominal_glyph_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t unicode, - hb_codepoint_t *glyph, - void *user_data HB_UNUSED) +hb_font_get_nominal_glyph_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) { + if (font->has_nominal_glyphs_func_set ()) + { + return font->get_nominal_glyphs (1, &unicode, 0, glyph, 0); + } return font->parent->get_nominal_glyph (unicode, glyph); } +#define hb_font_get_nominal_glyphs_nil hb_font_get_nominal_glyphs_default + +static unsigned int +hb_font_get_nominal_glyphs_default (hb_font_t *font, + void *font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + if (font->has_nominal_glyph_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + if (!font->get_nominal_glyph (*first_unicode, first_glyph)) + return i; + + first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + } + return count; + } + + return font->parent->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride); +} + static hb_bool_t -hb_font_get_variation_glyph_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t unicode, - hb_codepoint_t variation_selector, +hb_font_get_variation_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + hb_codepoint_t variation_selector HB_UNUSED, hb_codepoint_t *glyph, - void *user_data HB_UNUSED) + void *user_data HB_UNUSED) { *glyph = 0; return false; } + static hb_bool_t -hb_font_get_variation_glyph_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t unicode, - hb_codepoint_t variation_selector, - hb_codepoint_t *glyph, - void *user_data HB_UNUSED) +hb_font_get_variation_glyph_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) { return font->parent->get_variation_glyph (unicode, variation_selector, glyph); } static hb_position_t -hb_font_get_glyph_h_advance_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_h_advance_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + void *user_data HB_UNUSED) { return font->x_scale; } + static hb_position_t -hb_font_get_glyph_h_advance_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_h_advance_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) { + if (font->has_glyph_h_advances_func_set ()) + { + hb_position_t ret; + font->get_glyph_h_advances (1, &glyph, 0, &ret, 0); + return ret; + } return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph)); } static hb_position_t -hb_font_get_glyph_v_advance_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_v_advance_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + void *user_data HB_UNUSED) { /* TODO use font_extents.ascender+descender */ return font->y_scale; } + static hb_position_t -hb_font_get_glyph_v_advance_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_v_advance_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) { + if (font->has_glyph_v_advances_func_set ()) + { + hb_position_t ret; + font->get_glyph_v_advances (1, &glyph, 0, &ret, 0); + return ret; + } return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph)); } +#define hb_font_get_glyph_h_advances_nil hb_font_get_glyph_h_advances_default + +static void +hb_font_get_glyph_h_advances_default (hb_font_t* font, + void* font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_h_advance_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->get_glyph_h_advance (*first_glyph); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + + font->parent->get_glyph_h_advances (count, + first_glyph, glyph_stride, + first_advance, advance_stride); + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->parent_scale_x_distance (*first_advance); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } +} + +#define hb_font_get_glyph_v_advances_nil hb_font_get_glyph_v_advances_default +static void +hb_font_get_glyph_v_advances_default (hb_font_t* font, + void* font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_v_advance_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->get_glyph_v_advance (*first_glyph); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + + font->parent->get_glyph_v_advances (count, + first_glyph, glyph_stride, + first_advance, advance_stride); + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->parent_scale_y_distance (*first_advance); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } +} + static hb_bool_t -hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { *x = *y = 0; return true; } + static hb_bool_t -hb_font_get_glyph_h_origin_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_font_get_glyph_h_origin_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y); if (ret) @@ -187,23 +341,24 @@ hb_font_get_glyph_h_origin_parent (hb_font_t *font, } static hb_bool_t -hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { *x = *y = 0; return false; } + static hb_bool_t -hb_font_get_glyph_v_origin_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_font_get_glyph_v_origin_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y); if (ret) @@ -212,59 +367,64 @@ hb_font_get_glyph_v_origin_parent (hb_font_t *font, } static hb_position_t -hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t left_glyph, - hb_codepoint_t right_glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t left_glyph HB_UNUSED, + hb_codepoint_t right_glyph HB_UNUSED, + void *user_data HB_UNUSED) { return 0; } + static hb_position_t -hb_font_get_glyph_h_kerning_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t left_glyph, - hb_codepoint_t right_glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_h_kerning_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) { return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph)); } +#ifndef HB_DISABLE_DEPRECATED static hb_position_t -hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t top_glyph, - hb_codepoint_t bottom_glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t top_glyph HB_UNUSED, + hb_codepoint_t bottom_glyph HB_UNUSED, + void *user_data HB_UNUSED) { return 0; } + static hb_position_t -hb_font_get_glyph_v_kerning_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t top_glyph, - hb_codepoint_t bottom_glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_v_kerning_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph, + void *user_data HB_UNUSED) { return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph)); } +#endif static hb_bool_t -hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, +hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, hb_glyph_extents_t *extents, - void *user_data HB_UNUSED) + void *user_data HB_UNUSED) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return false; } + static hb_bool_t -hb_font_get_glyph_extents_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_glyph_extents_t *extents, - void *user_data HB_UNUSED) +hb_font_get_glyph_extents_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) { hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents); if (ret) { @@ -275,25 +435,26 @@ hb_font_get_glyph_extents_parent (hb_font_t *font, } static hb_bool_t -hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - unsigned int point_index, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + unsigned int point_index HB_UNUSED, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { *x = *y = 0; return false; } + static hb_bool_t -hb_font_get_glyph_contour_point_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - unsigned int point_index, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_font_get_glyph_contour_point_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y); if (ret) @@ -302,86 +463,245 @@ hb_font_get_glyph_contour_point_parent (hb_font_t *font, } static hb_bool_t -hb_font_get_glyph_name_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - char *name, unsigned int size, - void *user_data HB_UNUSED) +hb_font_get_glyph_name_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + char *name, + unsigned int size, + void *user_data HB_UNUSED) { if (size) *name = '\0'; return false; } + static hb_bool_t -hb_font_get_glyph_name_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - char *name, unsigned int size, - void *user_data HB_UNUSED) +hb_font_get_glyph_name_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + char *name, + unsigned int size, + void *user_data HB_UNUSED) { return font->parent->get_glyph_name (glyph, name, size); } static hb_bool_t -hb_font_get_glyph_from_name_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - const char *name, int len, /* -1 means nul-terminated */ +hb_font_get_glyph_from_name_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + const char *name HB_UNUSED, + int len HB_UNUSED, /* -1 means nul-terminated */ hb_codepoint_t *glyph, - void *user_data HB_UNUSED) + void *user_data HB_UNUSED) { *glyph = 0; return false; } + static hb_bool_t -hb_font_get_glyph_from_name_parent (hb_font_t *font, - void *font_data HB_UNUSED, - const char *name, int len, /* -1 means nul-terminated */ - hb_codepoint_t *glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_from_name_default (hb_font_t *font, + void *font_data HB_UNUSED, + const char *name, + int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) { return font->parent->get_glyph_from_name (name, len, glyph); } -static const hb_font_funcs_t _hb_font_funcs_nil = { +static void +hb_font_draw_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data HB_UNUSED) +{ +} + +static void +hb_font_paint_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_paint_funcs_t *paint_funcs HB_UNUSED, + void *paint_data HB_UNUSED, + unsigned int palette HB_UNUSED, + hb_color_t foreground HB_UNUSED, + void *user_data HB_UNUSED) +{ +} + +typedef struct hb_font_draw_glyph_default_adaptor_t { + hb_draw_funcs_t *draw_funcs; + void *draw_data; + float x_scale; + float y_scale; + float slant; +} hb_font_draw_glyph_default_adaptor_t; + +static void +hb_draw_move_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; + float x_scale = adaptor->x_scale; + float y_scale = adaptor->y_scale; + float slant = adaptor->slant; + + adaptor->draw_funcs->emit_move_to (adaptor->draw_data, *st, + x_scale * to_x + slant * to_y, y_scale * to_y); +} + +static void +hb_draw_line_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; + float x_scale = adaptor->x_scale; + float y_scale = adaptor->y_scale; + float slant = adaptor->slant; + + st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_y = st->current_y * y_scale; + + adaptor->draw_funcs->emit_line_to (adaptor->draw_data, *st, + x_scale * to_x + slant * to_y, y_scale * to_y); +} + +static void +hb_draw_quadratic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; + float x_scale = adaptor->x_scale; + float y_scale = adaptor->y_scale; + float slant = adaptor->slant; + + st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_y = st->current_y * y_scale; + + adaptor->draw_funcs->emit_quadratic_to (adaptor->draw_data, *st, + x_scale * control_x + slant * control_y, y_scale * control_y, + x_scale * to_x + slant * to_y, y_scale * to_y); +} + +static void +hb_draw_cubic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; + float x_scale = adaptor->x_scale; + float y_scale = adaptor->y_scale; + float slant = adaptor->slant; + + st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_y = st->current_y * y_scale; + + adaptor->draw_funcs->emit_cubic_to (adaptor->draw_data, *st, + x_scale * control1_x + slant * control1_y, y_scale * control1_y, + x_scale * control2_x + slant * control2_y, y_scale * control2_y, + x_scale * to_x + slant * to_y, y_scale * to_y); +} + +static void +hb_draw_close_path_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, + hb_draw_state_t *st, + void *user_data HB_UNUSED) +{ + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; + + adaptor->draw_funcs->emit_close_path (adaptor->draw_data, *st); +} + +static const hb_draw_funcs_t _hb_draw_funcs_default = { HB_OBJECT_HEADER_STATIC, - true, /* immutable */ + { +#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_default, + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } +}; - { -#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - }, - { -#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - }, +static void +hb_font_draw_glyph_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data HB_UNUSED) +{ + hb_font_draw_glyph_default_adaptor_t adaptor = { + draw_funcs, + draw_data, + font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0.f, + font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f, + font->parent->y_scale ? (font->slant - font->parent->slant) * + (float) font->x_scale / (float) font->parent->y_scale : 0.f + }; + + font->parent->draw_glyph (glyph, + const_cast (&_hb_draw_funcs_default), + &adaptor); +} + +static void +hb_font_paint_glyph_default (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, + void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) +{ + paint_funcs->push_transform (paint_data, + font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0.f, + font->parent->y_scale ? (font->slant - font->parent->slant) * + (float) font->x_scale / (float) font->parent->y_scale : 0.f, + 0.f, + font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f, + 0.f, 0.f); + + font->parent->paint_glyph (glyph, paint_funcs, paint_data, palette, foreground); + + paint_funcs->pop_transform (paint_data); +} + +DEFINE_NULL_INSTANCE (hb_font_funcs_t) = +{ + HB_OBJECT_HEADER_STATIC, + + nullptr, + nullptr, { { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil, +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_nil, HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } } }; -static const hb_font_funcs_t _hb_font_funcs_parent = { + +static const hb_font_funcs_t _hb_font_funcs_default = { HB_OBJECT_HEADER_STATIC, - true, /* immutable */ - - { -#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - }, - { -#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - }, + nullptr, + nullptr, { { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_parent, +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_default, HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } @@ -390,23 +710,23 @@ static const hb_font_funcs_t _hb_font_funcs_parent = { /** - * hb_font_funcs_create: (Xconstructor) + * hb_font_funcs_create: * - * + * Creates a new #hb_font_funcs_t structure of font functions. * - * Return value: (transfer full): + * Return value: (transfer full): The font-functions structure * * Since: 0.9.2 **/ hb_font_funcs_t * -hb_font_funcs_create (void) +hb_font_funcs_create () { hb_font_funcs_t *ffuncs; if (!(ffuncs = hb_object_create ())) return hb_font_funcs_get_empty (); - ffuncs->get = _hb_font_funcs_parent.get; + ffuncs->get = _hb_font_funcs_default.get; return ffuncs; } @@ -414,25 +734,25 @@ hb_font_funcs_create (void) /** * hb_font_funcs_get_empty: * - * + * Fetches an empty font-functions structure. * - * Return value: (transfer full): + * Return value: (transfer full): The font-functions structure * * Since: 0.9.2 **/ hb_font_funcs_t * -hb_font_funcs_get_empty (void) +hb_font_funcs_get_empty () { - return const_cast (&_hb_font_funcs_parent); + return const_cast (&_hb_font_funcs_default); } /** * hb_font_funcs_reference: (skip) - * @ffuncs: font functions. + * @ffuncs: The font-functions structure * - * + * Increases the reference count on a font-functions structure. * - * Return value: + * Return value: The font-functions structure * * Since: 0.9.2 **/ @@ -444,9 +764,11 @@ hb_font_funcs_reference (hb_font_funcs_t *ffuncs) /** * hb_font_funcs_destroy: (skip) - * @ffuncs: font functions. + * @ffuncs: The font-functions structure * - * + * Decreases the reference count on a font-functions structure. When + * the reference count reaches zero, the font-functions structure is + * destroyed, freeing all memory. * * Since: 0.9.2 **/ @@ -455,25 +777,31 @@ hb_font_funcs_destroy (hb_font_funcs_t *ffuncs) { if (!hb_object_destroy (ffuncs)) return; -#define HB_FONT_FUNC_IMPLEMENT(name) if (ffuncs->destroy.name) \ - ffuncs->destroy.name (ffuncs->user_data.name); - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS + if (ffuncs->destroy) + { +#define HB_FONT_FUNC_IMPLEMENT(get_,name) if (ffuncs->destroy->name) \ + ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name); + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT + } - free (ffuncs); + hb_free (ffuncs->destroy); + hb_free (ffuncs->user_data); + + hb_free (ffuncs); } /** * hb_font_funcs_set_user_data: (skip) - * @ffuncs: font functions. - * @key: - * @data: - * @destroy: - * @replace: + * @ffuncs: The font-functions structure + * @key: The user-data key to set + * @data: A pointer to the user data set + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key * - * + * Attaches a user-data key/data pair to the specified font-functions structure. * - * Return value: + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -481,7 +809,7 @@ hb_bool_t hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy, + hb_destroy_func_t destroy /* May be NULL. */, hb_bool_t replace) { return hb_object_set_user_data (ffuncs, key, data, destroy, replace); @@ -489,18 +817,19 @@ hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_get_user_data: (skip) - * @ffuncs: font functions. - * @key: + * @ffuncs: The font-functions structure + * @key: The user-data key to query * - * + * Fetches the user data associated with the specified key, + * attached to the specified font-functions structure. * - * Return value: (transfer none): + * Return value: (transfer none): A pointer to the user data * * Since: 0.9.2 **/ void * -hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, - hb_user_data_key_t *key) +hb_font_funcs_get_user_data (const hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key) { return hb_object_get_user_data (ffuncs, key); } @@ -508,92 +837,148 @@ hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_make_immutable: - * @ffuncs: font functions. + * @ffuncs: The font-functions structure * - * + * Makes a font-functions structure immutable. * * Since: 0.9.2 **/ void hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs) { - if (unlikely (hb_object_is_inert (ffuncs))) + if (hb_object_is_immutable (ffuncs)) return; - ffuncs->immutable = true; + hb_object_make_immutable (ffuncs); } /** * hb_font_funcs_is_immutable: - * @ffuncs: font functions. + * @ffuncs: The font-functions structure * - * + * Tests whether a font-functions structure is immutable. * - * Return value: + * Return value: `true` if @ffuncs is immutable, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs) { - return ffuncs->immutable; + return hb_object_is_immutable (ffuncs); } -#define HB_FONT_FUNC_IMPLEMENT(name) \ - \ +static bool +_hb_font_funcs_set_preamble (hb_font_funcs_t *ffuncs, + bool func_is_null, + void **user_data, + hb_destroy_func_t *destroy) +{ + if (hb_object_is_immutable (ffuncs)) + { + if (*destroy) + (*destroy) (*user_data); + return false; + } + + if (func_is_null) + { + if (*destroy) + (*destroy) (*user_data); + *destroy = nullptr; + *user_data = nullptr; + } + + return true; +} + +static bool +_hb_font_funcs_set_middle (hb_font_funcs_t *ffuncs, + void *user_data, + hb_destroy_func_t destroy) +{ + if (user_data && !ffuncs->user_data) + { + ffuncs->user_data = (decltype (ffuncs->user_data)) hb_calloc (1, sizeof (*ffuncs->user_data)); + if (unlikely (!ffuncs->user_data)) + goto fail; + } + if (destroy && !ffuncs->destroy) + { + ffuncs->destroy = (decltype (ffuncs->destroy)) hb_calloc (1, sizeof (*ffuncs->destroy)); + if (unlikely (!ffuncs->destroy)) + goto fail; + } + + return true; + +fail: + if (destroy) + (destroy) (user_data); + return false; +} + +#define HB_FONT_FUNC_IMPLEMENT(get_,name) \ + \ void \ hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ - hb_font_get_##name##_func_t func, \ - void *user_data, \ - hb_destroy_func_t destroy) \ + hb_font_##get_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ { \ - if (ffuncs->immutable) { \ - if (destroy) \ - destroy (user_data); \ - return; \ - } \ + if (!_hb_font_funcs_set_preamble (ffuncs, !func, &user_data, &destroy))\ + return; \ + \ + if (ffuncs->destroy && ffuncs->destroy->name) \ + ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name); \ \ - if (ffuncs->destroy.name) \ - ffuncs->destroy.name (ffuncs->user_data.name); \ - \ - if (func) { \ + if (!_hb_font_funcs_set_middle (ffuncs, user_data, destroy)) \ + return; \ + \ + if (func) \ ffuncs->get.f.name = func; \ - ffuncs->user_data.name = user_data; \ - ffuncs->destroy.name = destroy; \ - } else { \ - ffuncs->get.f.name = hb_font_get_##name##_parent; \ - ffuncs->user_data.name = nullptr; \ - ffuncs->destroy.name = nullptr; \ - } \ + else \ + ffuncs->get.f.name = hb_font_##get_##name##_default; \ + \ + if (ffuncs->user_data) \ + ffuncs->user_data->name = user_data; \ + if (ffuncs->destroy) \ + ffuncs->destroy->name = destroy; \ } HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT +bool +hb_font_t::has_func_set (unsigned int i) +{ + return this->klass->get.array[i] != _hb_font_funcs_default.get.array[i]; +} + bool hb_font_t::has_func (unsigned int i) { - if (parent && parent != hb_font_get_empty () && parent->has_func (i)) - return true; - return this->klass->get.array[i] != _hb_font_funcs_parent.get.array[i]; + return has_func_set (i) || + (parent && parent != &_hb_Null_hb_font_t && parent->has_func (i)); } /* Public getters */ /** * hb_font_get_h_extents: - * @font: a font. - * @extents: (out): + * @font: #hb_font_t to work upon + * @extents: (out): The font extents retrieved * + * Fetches the extents for a specified font, for horizontal + * text segments. * - * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 1.1.3 **/ hb_bool_t -hb_font_get_h_extents (hb_font_t *font, +hb_font_get_h_extents (hb_font_t *font, hb_font_extents_t *extents) { return font->get_font_h_extents (extents); @@ -601,17 +986,18 @@ hb_font_get_h_extents (hb_font_t *font, /** * hb_font_get_v_extents: - * @font: a font. - * @extents: (out): + * @font: #hb_font_t to work upon + * @extents: (out): The font extents retrieved * + * Fetches the extents for a specified font, for vertical + * text segments. * - * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 1.1.3 **/ hb_bool_t -hb_font_get_v_extents (hb_font_t *font, +hb_font_get_v_extents (hb_font_t *font, hb_font_extents_t *extents) { return font->get_font_v_extents (extents); @@ -619,20 +1005,25 @@ hb_font_get_v_extents (hb_font_t *font, /** * hb_font_get_glyph: - * @font: a font. - * @unicode: - * @variation_selector: - * @glyph: (out): + * @font: #hb_font_t to work upon + * @unicode: The Unicode code point to query + * @variation_selector: A variation-selector code point + * @glyph: (out): The glyph ID retrieved * - * + * Fetches the glyph ID for a Unicode code point in the specified + * font, with an optional variation selector. * - * Return value: + * If @variation_selector is 0, calls hb_font_get_nominal_glyph(); + * otherwise calls hb_font_get_variation_glyph(). + * + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph (hb_font_t *font, - hb_codepoint_t unicode, hb_codepoint_t variation_selector, +hb_font_get_glyph (hb_font_t *font, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, hb_codepoint_t *glyph) { if (unlikely (variation_selector)) @@ -642,40 +1033,78 @@ hb_font_get_glyph (hb_font_t *font, /** * hb_font_get_nominal_glyph: - * @font: a font. - * @unicode: - * @glyph: (out): + * @font: #hb_font_t to work upon + * @unicode: The Unicode code point to query + * @glyph: (out): The glyph ID retrieved * - * + * Fetches the nominal glyph ID for a Unicode code point in the + * specified font. * - * Return value: + * This version of the function should not be used to fetch glyph IDs + * for code points modified by variation selectors. For variation-selector + * support, user hb_font_get_variation_glyph() or use hb_font_get_glyph(). + * + * Return value: `true` if data found, `false` otherwise * * Since: 1.2.3 **/ hb_bool_t -hb_font_get_nominal_glyph (hb_font_t *font, - hb_codepoint_t unicode, +hb_font_get_nominal_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t *glyph) { return font->get_nominal_glyph (unicode, glyph); } +/** + * hb_font_get_nominal_glyphs: + * @font: #hb_font_t to work upon + * @count: number of code points to query + * @first_unicode: The first Unicode code point to query + * @unicode_stride: The stride between successive code points + * @first_glyph: (out): The first glyph ID retrieved + * @glyph_stride: The stride between successive glyph IDs + * + * Fetches the nominal glyph IDs for a sequence of Unicode code points. Glyph + * IDs must be returned in a #hb_codepoint_t output parameter. Stopes at the + * first unsupported glyph ID. + * + * Return value: the number of code points processed + * + * Since: 2.6.3 + **/ +unsigned int +hb_font_get_nominal_glyphs (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride) +{ + return font->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride); +} + /** * hb_font_get_variation_glyph: - * @font: a font. - * @unicode: - * @variation_selector: - * @glyph: (out): + * @font: #hb_font_t to work upon + * @unicode: The Unicode code point to query + * @variation_selector: The variation-selector code point to query + * @glyph: (out): The glyph ID retrieved * - * + * Fetches the glyph ID for a Unicode code point when followed by + * by the specified variation-selector code point, in the specified + * font. * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 1.2.3 **/ hb_bool_t -hb_font_get_variation_glyph (hb_font_t *font, - hb_codepoint_t unicode, hb_codepoint_t variation_selector, +hb_font_get_variation_glyph (hb_font_t *font, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, hb_codepoint_t *glyph) { return font->get_variation_glyph (unicode, variation_selector, glyph); @@ -683,135 +1112,204 @@ hb_font_get_variation_glyph (hb_font_t *font, /** * hb_font_get_glyph_h_advance: - * @font: a font. - * @glyph: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query * - * + * Fetches the advance for a glyph ID in the specified font, + * for horizontal text segments. * - * Return value: + * Return value: The advance of @glyph within @font * * Since: 0.9.2 **/ hb_position_t -hb_font_get_glyph_h_advance (hb_font_t *font, - hb_codepoint_t glyph) +hb_font_get_glyph_h_advance (hb_font_t *font, + hb_codepoint_t glyph) { return font->get_glyph_h_advance (glyph); } /** * hb_font_get_glyph_v_advance: - * @font: a font. - * @glyph: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query * - * + * Fetches the advance for a glyph ID in the specified font, + * for vertical text segments. * - * Return value: + * Return value: The advance of @glyph within @font * * Since: 0.9.2 **/ hb_position_t -hb_font_get_glyph_v_advance (hb_font_t *font, - hb_codepoint_t glyph) +hb_font_get_glyph_v_advance (hb_font_t *font, + hb_codepoint_t glyph) { return font->get_glyph_v_advance (glyph); } +/** + * hb_font_get_glyph_h_advances: + * @font: #hb_font_t to work upon + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_advance: (out): The first advance retrieved + * @advance_stride: The stride between successive advances + * + * Fetches the advances for a sequence of glyph IDs in the specified + * font, for horizontal text segments. + * + * Since: 1.8.6 + **/ +void +hb_font_get_glyph_h_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) +{ + font->get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); +} +/** + * hb_font_get_glyph_v_advances: + * @font: #hb_font_t to work upon + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_advance: (out): The first advance retrieved + * @advance_stride: (out): The stride between successive advances + * + * Fetches the advances for a sequence of glyph IDs in the specified + * font, for vertical text segments. + * + * Since: 1.8.6 + **/ +void +hb_font_get_glyph_v_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) +{ + font->get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); +} + /** * hb_font_get_glyph_h_origin: - * @font: a font. - * @glyph: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @x: (out): The X coordinate of the origin + * @y: (out): The Y coordinate of the origin * - * + * Fetches the (X,Y) coordinates of the origin for a glyph ID + * in the specified font, for horizontal text segments. * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_h_origin (hb_font_t *font, - hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_h_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y) { return font->get_glyph_h_origin (glyph, x, y); } /** * hb_font_get_glyph_v_origin: - * @font: a font. - * @glyph: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @x: (out): The X coordinate of the origin + * @y: (out): The Y coordinate of the origin * - * + * Fetches the (X,Y) coordinates of the origin for a glyph ID + * in the specified font, for vertical text segments. * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_v_origin (hb_font_t *font, - hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_v_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y) { return font->get_glyph_v_origin (glyph, x, y); } /** * hb_font_get_glyph_h_kerning: - * @font: a font. - * @left_glyph: - * @right_glyph: + * @font: #hb_font_t to work upon + * @left_glyph: The glyph ID of the left glyph in the glyph pair + * @right_glyph: The glyph ID of the right glyph in the glyph pair * - * + * Fetches the kerning-adjustment value for a glyph-pair in + * the specified font, for horizontal text segments. * - * Return value: + * It handles legacy kerning only (as returned by the corresponding + * #hb_font_funcs_t function). + * + * Return value: The kerning adjustment value * * Since: 0.9.2 **/ hb_position_t -hb_font_get_glyph_h_kerning (hb_font_t *font, - hb_codepoint_t left_glyph, hb_codepoint_t right_glyph) +hb_font_get_glyph_h_kerning (hb_font_t *font, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph) { return font->get_glyph_h_kerning (left_glyph, right_glyph); } +#ifndef HB_DISABLE_DEPRECATED /** * hb_font_get_glyph_v_kerning: - * @font: a font. - * @top_glyph: - * @bottom_glyph: + * @font: #hb_font_t to work upon + * @top_glyph: The glyph ID of the top glyph in the glyph pair + * @bottom_glyph: The glyph ID of the bottom glyph in the glyph pair * - * + * Fetches the kerning-adjustment value for a glyph-pair in + * the specified font, for vertical text segments. * - * Return value: + * It handles legacy kerning only (as returned by the corresponding + * #hb_font_funcs_t function). + * + * Return value: The kerning adjustment value * * Since: 0.9.2 + * Deprecated: 2.0.0 **/ hb_position_t -hb_font_get_glyph_v_kerning (hb_font_t *font, - hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph) +hb_font_get_glyph_v_kerning (hb_font_t *font, + hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph) { return font->get_glyph_v_kerning (top_glyph, bottom_glyph); } +#endif /** * hb_font_get_glyph_extents: - * @font: a font. - * @glyph: - * @extents: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @extents: (out): The #hb_glyph_extents_t retrieved * - * + * Fetches the #hb_glyph_extents_t data for a glyph ID + * in the specified font. * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_extents (hb_font_t *font, - hb_codepoint_t glyph, +hb_font_get_glyph_extents (hb_font_t *font, + hb_codepoint_t glyph, hb_glyph_extents_t *extents) { return font->get_glyph_extents (glyph, extents); @@ -819,211 +1317,362 @@ hb_font_get_glyph_extents (hb_font_t *font, /** * hb_font_get_glyph_contour_point: - * @font: a font. - * @glyph: - * @point_index: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @point_index: The contour-point index to query + * @x: (out): The X value retrieved for the contour point + * @y: (out): The Y value retrieved for the contour point * - * + * Fetches the (x,y) coordinates of a specified contour-point index + * in the specified glyph, within the specified font. * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_contour_point (hb_font_t *font, - hb_codepoint_t glyph, unsigned int point_index, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_contour_point (hb_font_t *font, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y) { return font->get_glyph_contour_point (glyph, point_index, x, y); } /** * hb_font_get_glyph_name: - * @font: a font. - * @glyph: - * @name: (array length=size): - * @size: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @name: (out) (array length=size): Name string retrieved for the glyph ID + * @size: Length of the glyph-name string retrieved * - * + * Fetches the glyph-name string for a glyph ID in the specified @font. * - * Return value: + * According to the OpenType specification, glyph names are limited to 63 + * characters and can only contain (a subset of) ASCII. + * + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_name (hb_font_t *font, - hb_codepoint_t glyph, - char *name, unsigned int size) +hb_font_get_glyph_name (hb_font_t *font, + hb_codepoint_t glyph, + char *name, + unsigned int size) { return font->get_glyph_name (glyph, name, size); } /** * hb_font_get_glyph_from_name: - * @font: a font. - * @name: (array length=len): - * @len: - * @glyph: (out): + * @font: #hb_font_t to work upon + * @name: (array length=len): The name string to query + * @len: The length of the name queried + * @glyph: (out): The glyph ID retrieved * - * + * Fetches the glyph ID that corresponds to a name string in the specified @font. * - * Return value: + * Note: @len == -1 means the name string is null-terminated. + * + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_from_name (hb_font_t *font, - const char *name, int len, /* -1 means nul-terminated */ +hb_font_get_glyph_from_name (hb_font_t *font, + const char *name, + int len, /* -1 means nul-terminated */ hb_codepoint_t *glyph) { return font->get_glyph_from_name (name, len, glyph); } +/** + * hb_font_get_glyph_shape: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID + * @dfuncs: #hb_draw_funcs_t to draw to + * @draw_data: User data to pass to draw callbacks + * + * Fetches the glyph shape that corresponds to a glyph in the specified @font. + * The shape is returned by way of calls to the callbacks of the @dfuncs + * objects, with @draw_data passed to them. + * + * Since: 4.0.0 + * Deprecated: 7.0.0: Use hb_font_draw_glyph() instead + */ +void +hb_font_get_glyph_shape (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + hb_font_draw_glyph (font, glyph, dfuncs, draw_data); +} + +/** + * hb_font_draw_glyph: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID + * @dfuncs: #hb_draw_funcs_t to draw to + * @draw_data: User data to pass to draw callbacks + * + * Draws the outline that corresponds to a glyph in the specified @font. + * + * The outline is returned by way of calls to the callbacks of the @dfuncs + * objects, with @draw_data passed to them. + * + * Since: 7.0.0 + **/ +void +hb_font_draw_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + font->draw_glyph (glyph, dfuncs, draw_data); +} + +/** + * hb_font_paint_glyph: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID + * @pfuncs: #hb_paint_funcs_t to paint with + * @paint_data: User data to pass to paint callbacks + * @palette_index: The index of the font's color palette to use + * @foreground: The foreground color, unpremultipled + * + * Paints the glyph. + * + * The painting instructions are returned by way of calls to + * the callbacks of the @funcs object, with @paint_data passed + * to them. + * + * If the font has color palettes (see hb_ot_color_has_palettes()), + * then @palette_index selects the palette to use. If the font only + * has one palette, this will be 0. + * + * Since: 7.0.0 + */ +void +hb_font_paint_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground) +{ + font->paint_glyph (glyph, pfuncs, paint_data, palette_index, foreground); +} /* A bit higher-level, and with fallback */ /** * hb_font_get_extents_for_direction: - * @font: a font. - * @direction: - * @extents: + * @font: #hb_font_t to work upon + * @direction: The direction of the text segment + * @extents: (out): The #hb_font_extents_t retrieved * + * Fetches the extents for a font in a text segment of the + * specified direction. * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. * * Since: 1.1.3 **/ void -hb_font_get_extents_for_direction (hb_font_t *font, - hb_direction_t direction, +hb_font_get_extents_for_direction (hb_font_t *font, + hb_direction_t direction, hb_font_extents_t *extents) { - return font->get_extents_for_direction (direction, extents); + font->get_extents_for_direction (direction, extents); } /** * hb_font_get_glyph_advance_for_direction: - * @font: a font. - * @glyph: - * @direction: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @x: (out): The horizontal advance retrieved + * @y: (out): The vertical advance retrieved * - * + * Fetches the advance for a glyph ID from the specified font, + * in a text segment of the specified direction. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. * * Since: 0.9.2 **/ void -hb_font_get_glyph_advance_for_direction (hb_font_t *font, - hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_advance_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) { - return font->get_glyph_advance_for_direction (glyph, direction, x, y); + font->get_glyph_advance_for_direction (glyph, direction, x, y); +} +/** + * hb_font_get_glyph_advances_for_direction: + * @font: #hb_font_t to work upon + * @direction: The direction of the text segment + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_advance: (out): The first advance retrieved + * @advance_stride: (out): The stride between successive advances + * + * Fetches the advances for a sequence of glyph IDs in the specified + * font, in a text segment of the specified direction. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Since: 1.8.6 + **/ +HB_EXTERN void +hb_font_get_glyph_advances_for_direction (hb_font_t* font, + hb_direction_t direction, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) +{ + font->get_glyph_advances_for_direction (direction, count, first_glyph, glyph_stride, first_advance, advance_stride); } /** * hb_font_get_glyph_origin_for_direction: - * @font: a font. - * @glyph: - * @direction: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @x: (out): The X coordinate retrieved for the origin + * @y: (out): The Y coordinate retrieved for the origin * - * + * Fetches the (X,Y) coordinates of the origin for a glyph in + * the specified font. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. * * Since: 0.9.2 **/ void -hb_font_get_glyph_origin_for_direction (hb_font_t *font, - hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) { return font->get_glyph_origin_for_direction (glyph, direction, x, y); } /** * hb_font_add_glyph_origin_for_direction: - * @font: a font. - * @glyph: - * @direction: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @x: (inout): Input = The original X coordinate + * Output = The X coordinate plus the X-coordinate of the origin + * @y: (inout): Input = The original Y coordinate + * Output = The Y coordinate plus the Y-coordinate of the origin * - * + * Adds the origin coordinates to an (X,Y) point coordinate, in + * the specified glyph ID in the specified font. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. * * Since: 0.9.2 **/ void -hb_font_add_glyph_origin_for_direction (hb_font_t *font, - hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) +hb_font_add_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) { return font->add_glyph_origin_for_direction (glyph, direction, x, y); } /** * hb_font_subtract_glyph_origin_for_direction: - * @font: a font. - * @glyph: - * @direction: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @x: (inout): Input = The original X coordinate + * Output = The X coordinate minus the X-coordinate of the origin + * @y: (inout): Input = The original Y coordinate + * Output = The Y coordinate minus the Y-coordinate of the origin * - * + * Subtracts the origin coordinates from an (X,Y) point coordinate, + * in the specified glyph ID in the specified font. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. * * Since: 0.9.2 **/ void -hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, - hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) +hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) { return font->subtract_glyph_origin_for_direction (glyph, direction, x, y); } /** * hb_font_get_glyph_kerning_for_direction: - * @font: a font. - * @first_glyph: - * @second_glyph: - * @direction: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @first_glyph: The glyph ID of the first glyph in the glyph pair to query + * @second_glyph: The glyph ID of the second glyph in the glyph pair to query + * @direction: The direction of the text segment + * @x: (out): The horizontal kerning-adjustment value retrieved + * @y: (out): The vertical kerning-adjustment value retrieved * - * + * Fetches the kerning-adjustment value for a glyph-pair in the specified font. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. * * Since: 0.9.2 **/ void -hb_font_get_glyph_kerning_for_direction (hb_font_t *font, - hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_kerning_for_direction (hb_font_t *font, + hb_codepoint_t first_glyph, + hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) { return font->get_glyph_kerning_for_direction (first_glyph, second_glyph, direction, x, y); } /** * hb_font_get_glyph_extents_for_origin: - * @font: a font. - * @glyph: - * @direction: - * @extents: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @extents: (out): The #hb_glyph_extents_t retrieved * - * + * Fetches the #hb_glyph_extents_t data for a glyph ID + * in the specified font, with respect to the origin in + * a text segment in the specified direction. * - * Return value: + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_extents_for_origin (hb_font_t *font, - hb_codepoint_t glyph, - hb_direction_t direction, +hb_font_get_glyph_extents_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, hb_glyph_extents_t *extents) { return font->get_glyph_extents_for_origin (glyph, direction, extents); @@ -1031,65 +1680,82 @@ hb_font_get_glyph_extents_for_origin (hb_font_t *font, /** * hb_font_get_glyph_contour_point_for_origin: - * @font: a font. - * @glyph: - * @point_index: - * @direction: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @point_index: The contour-point index to query + * @direction: The direction of the text segment + * @x: (out): The X value retrieved for the contour point + * @y: (out): The Y value retrieved for the contour point * - * + * Fetches the (X,Y) coordinates of a specified contour-point index + * in the specified glyph ID in the specified font, with respect + * to the origin in a text segment in the specified direction. * - * Return value: + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, - hb_codepoint_t glyph, unsigned int point_index, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) { return font->get_glyph_contour_point_for_origin (glyph, point_index, direction, x, y); } -/* Generates gidDDD if glyph has no name. */ /** * hb_font_glyph_to_string: - * @font: a font. - * @glyph: - * @s: (array length=size): - * @size: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @s: (out) (array length=size): The string containing the glyph name + * @size: Length of string @s * - * + * Fetches the name of the specified glyph ID in @font and returns + * it in string @s. + * + * If the glyph ID has no name in @font, a string of the form `gidDDD` is + * generated, with `DDD` being the glyph ID. + * + * According to the OpenType specification, glyph names are limited to 63 + * characters and can only contain (a subset of) ASCII. * * Since: 0.9.2 **/ void -hb_font_glyph_to_string (hb_font_t *font, - hb_codepoint_t glyph, - char *s, unsigned int size) +hb_font_glyph_to_string (hb_font_t *font, + hb_codepoint_t glyph, + char *s, + unsigned int size) { font->glyph_to_string (glyph, s, size); } -/* Parses gidDDD and uniUUUU strings automatically. */ /** * hb_font_glyph_from_string: - * @font: a font. - * @s: (array length=len) (element-type uint8_t): - * @len: - * @glyph: (out): + * @font: #hb_font_t to work upon + * @s: (array length=len) (element-type uint8_t): string to query + * @len: The length of the string @s + * @glyph: (out): The glyph ID corresponding to the string requested * - * + * Fetches the glyph ID from @font that matches the specified string. + * Strings of the format `gidDDD` or `uniUUUU` are parsed automatically. * - * Return value: + * Note: @len == -1 means the string is null-terminated. + * + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_glyph_from_string (hb_font_t *font, - const char *s, int len, /* -1 means nul-terminated */ +hb_font_glyph_from_string (hb_font_t *font, + const char *s, + int len, hb_codepoint_t *glyph) { return font->glyph_from_string (s, len, glyph); @@ -1100,23 +1766,53 @@ hb_font_glyph_from_string (hb_font_t *font, * hb_font_t */ -/** - * hb_font_create: (Xconstructor) - * @face: a face. - * - * - * - * Return value: (transfer full): - * - * Since: 0.9.2 - **/ -hb_font_t * -hb_font_create (hb_face_t *face) +DEFINE_NULL_INSTANCE (hb_font_t) = +{ + HB_OBJECT_HEADER_STATIC, + + 0, /* serial */ + 0, /* serial_coords */ + + nullptr, /* parent */ + const_cast (&_hb_Null_hb_face_t), + + 1000, /* x_scale */ + 1000, /* y_scale */ + 0.f, /* x_embolden */ + 0.f, /* y_embolden */ + true, /* embolden_in_place */ + 0, /* x_strength */ + 0, /* y_strength */ + 0.f, /* slant */ + 0.f, /* slant_xy; */ + 1.f, /* x_multf */ + 1.f, /* y_multf */ + 1<<16, /* x_mult */ + 1<<16, /* y_mult */ + + 0, /* x_ppem */ + 0, /* y_ppem */ + 0, /* ptem */ + + HB_FONT_NO_VAR_NAMED_INSTANCE, /* instance_index */ + 0, /* num_coords */ + nullptr, /* coords */ + nullptr, /* design_coords */ + + const_cast (&_hb_Null_hb_font_funcs_t), + + /* Zero for the rest is fine. */ +}; + + +static hb_font_t * +_hb_font_create (hb_face_t *face) { hb_font_t *font; if (unlikely (!face)) face = hb_face_get_empty (); + if (!(font = hb_object_create ())) return hb_font_get_empty (); @@ -1124,19 +1820,75 @@ hb_font_create (hb_face_t *face) font->parent = hb_font_get_empty (); font->face = hb_face_reference (face); font->klass = hb_font_funcs_get_empty (); - - font->x_scale = font->y_scale = hb_face_get_upem (face); + font->data.init0 (font); + font->x_scale = font->y_scale = face->get_upem (); + font->embolden_in_place = true; + font->x_multf = font->y_multf = 1.f; + font->x_mult = font->y_mult = 1 << 16; + font->instance_index = HB_FONT_NO_VAR_NAMED_INSTANCE; return font; } +/** + * hb_font_create: + * @face: a face. + * + * Constructs a new font object from the specified face. + * + * Note: If @face's index value (as passed to hb_face_create() + * has non-zero top 16-bits, those bits minus one are passed to + * hb_font_set_var_named_instance(), effectively loading a named-instance + * of a variable font, instead of the default-instance. This allows + * specifying which named-instance to load by default when creating the + * face. + * + * Return value: (transfer full): The new font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_create (hb_face_t *face) +{ + hb_font_t *font = _hb_font_create (face); + +#ifndef HB_NO_OT_FONT + /* Install our in-house, very lightweight, funcs. */ + hb_ot_font_set_funcs (font); +#endif + +#ifndef HB_NO_VAR + if (face && face->index >> 16) + hb_font_set_var_named_instance (font, (face->index >> 16) - 1); +#endif + + return font; +} + +static void +_hb_font_adopt_var_coords (hb_font_t *font, + int *coords, /* 2.14 normalized */ + float *design_coords, + unsigned int coords_length) +{ + hb_free (font->coords); + hb_free (font->design_coords); + + font->coords = coords; + font->design_coords = design_coords; + font->num_coords = coords_length; + + font->mults_changed (); // Easiest to call this to drop cached data +} + /** * hb_font_create_sub_font: - * @parent: parent font. + * @parent: The parent font object * - * + * Constructs a sub-font font object from the specified @parent font, + * replicating the parent's properties. * - * Return value: (transfer full): + * Return value: (transfer full): The new sub-font font object * * Since: 0.9.2 **/ @@ -1146,86 +1898,68 @@ hb_font_create_sub_font (hb_font_t *parent) if (unlikely (!parent)) parent = hb_font_get_empty (); - hb_font_t *font = hb_font_create (parent->face); + hb_font_t *font = _hb_font_create (parent->face); - if (unlikely (hb_object_is_inert (font))) + if (unlikely (hb_object_is_immutable (font))) return font; font->parent = hb_font_reference (parent); font->x_scale = parent->x_scale; font->y_scale = parent->y_scale; + font->x_embolden = parent->x_embolden; + font->y_embolden = parent->y_embolden; + font->embolden_in_place = parent->embolden_in_place; + font->slant = parent->slant; font->x_ppem = parent->x_ppem; font->y_ppem = parent->y_ppem; font->ptem = parent->ptem; - font->num_coords = parent->num_coords; - if (!font->num_coords) - font->coords = nullptr; - else + unsigned int num_coords = parent->num_coords; + if (num_coords) { - unsigned int size = parent->num_coords * sizeof (parent->coords[0]); - font->coords = (int *) malloc (size); - if (unlikely (!font->coords)) - font->num_coords = 0; + int *coords = (int *) hb_calloc (num_coords, sizeof (parent->coords[0])); + float *design_coords = (float *) hb_calloc (num_coords, sizeof (parent->design_coords[0])); + if (likely (coords && design_coords)) + { + hb_memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0])); + hb_memcpy (design_coords, parent->design_coords, num_coords * sizeof (parent->design_coords[0])); + _hb_font_adopt_var_coords (font, coords, design_coords, num_coords); + } else - memcpy (font->coords, parent->coords, size); + { + hb_free (coords); + hb_free (design_coords); + } } + font->mults_changed (); + return font; } /** * hb_font_get_empty: * - * + * Fetches the empty font object. * - * Return value: (transfer full) + * Return value: (transfer full): The empty font object * * Since: 0.9.2 **/ hb_font_t * -hb_font_get_empty (void) +hb_font_get_empty () { - static const hb_font_t _hb_font_nil = { - HB_OBJECT_HEADER_STATIC, - - true, /* immutable */ - - nullptr, /* parent */ - const_cast (&_hb_face_nil), - - 1000, /* x_scale */ - 1000, /* y_scale */ - - 0, /* x_ppem */ - 0, /* y_ppem */ - 0, /* ptem */ - - 0, /* num_coords */ - nullptr, /* coords */ - - const_cast (&_hb_font_funcs_nil), /* klass */ - nullptr, /* user_data */ - nullptr, /* destroy */ - - { -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID, -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT - } - }; - - return const_cast (&_hb_font_nil); + return const_cast (&Null (hb_font_t)); } /** * hb_font_reference: (skip) - * @font: a font. + * @font: #hb_font_t to work upon * - * + * Increases the reference count on the given font object. * - * Return value: (transfer full): + * Return value: (transfer full): The @font object * * Since: 0.9.2 **/ @@ -1237,9 +1971,11 @@ hb_font_reference (hb_font_t *font) /** * hb_font_destroy: (skip) - * @font: a font. + * @font: #hb_font_t to work upon * - * + * Decreases the reference count on the given font object. When the + * reference count reaches zero, the font is destroyed, + * freeing all memory. * * Since: 0.9.2 **/ @@ -1248,9 +1984,7 @@ hb_font_destroy (hb_font_t *font) { if (!hb_object_destroy (font)) return; -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, font); -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT + font->data.fini (); if (font->destroy) font->destroy (font->user_data); @@ -1259,22 +1993,23 @@ hb_font_destroy (hb_font_t *font) hb_face_destroy (font->face); hb_font_funcs_destroy (font->klass); - free (font->coords); + hb_free (font->coords); + hb_free (font->design_coords); - free (font); + hb_free (font); } /** * hb_font_set_user_data: (skip) - * @font: a font. - * @key: - * @data: - * @destroy: - * @replace: + * @font: #hb_font_t to work upon + * @key: The user-data key + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key * - * + * Attaches a user-data key/data pair to the specified font object. * - * Return value: + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -1282,25 +2017,29 @@ hb_bool_t hb_font_set_user_data (hb_font_t *font, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy, + hb_destroy_func_t destroy /* May be NULL. */, hb_bool_t replace) { + if (!hb_object_is_immutable (font)) + font->serial++; + return hb_object_set_user_data (font, key, data, destroy, replace); } /** * hb_font_get_user_data: (skip) - * @font: a font. - * @key: + * @font: #hb_font_t to work upon + * @key: The user-data key to query * - * + * Fetches the user-data object associated with the specified key, + * attached to the specified font object. * - * Return value: (transfer none): + * Return value: (transfer none): Pointer to the user data * * Since: 0.9.2 **/ void * -hb_font_get_user_data (hb_font_t *font, +hb_font_get_user_data (const hb_font_t *font, hb_user_data_key_t *key) { return hb_object_get_user_data (font, key); @@ -1308,46 +2047,85 @@ hb_font_get_user_data (hb_font_t *font, /** * hb_font_make_immutable: - * @font: a font. + * @font: #hb_font_t to work upon * - * + * Makes @font immutable. * * Since: 0.9.2 **/ void hb_font_make_immutable (hb_font_t *font) { - if (unlikely (hb_object_is_inert (font))) + if (hb_object_is_immutable (font)) return; if (font->parent) hb_font_make_immutable (font->parent); - font->immutable = true; + hb_object_make_immutable (font); } /** * hb_font_is_immutable: - * @font: a font. + * @font: #hb_font_t to work upon * - * + * Tests whether a font object is immutable. * - * Return value: + * Return value: `true` if @font is immutable, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t hb_font_is_immutable (hb_font_t *font) { - return font->immutable; + return hb_object_is_immutable (font); +} + +/** + * hb_font_get_serial: + * @font: #hb_font_t to work upon + * + * Returns the internal serial number of the font. The serial + * number is increased every time a setting on the font is + * changed, using a setter function. + * + * Return value: serial number + * + * Since: 4.4.0 + **/ +unsigned int +hb_font_get_serial (hb_font_t *font) +{ + return font->serial; +} + +/** + * hb_font_changed: + * @font: #hb_font_t to work upon + * + * Notifies the @font that underlying font data has changed. + * This has the effect of increasing the serial as returned + * by hb_font_get_serial(), which invalidates internal caches. + * + * Since: 4.4.0 + **/ +void +hb_font_changed (hb_font_t *font) +{ + if (hb_object_is_immutable (font)) + return; + + font->serial++; + + font->mults_changed (); } /** * hb_font_set_parent: - * @font: a font. - * @parent: new parent. + * @font: #hb_font_t to work upon + * @parent: The parent font object to assign * - * Sets parent font of @font. + * Sets the parent font of @font. * * Since: 1.0.5 **/ @@ -1355,9 +2133,14 @@ void hb_font_set_parent (hb_font_t *font, hb_font_t *parent) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; + if (parent == font->parent) + return; + + font->serial++; + if (!parent) parent = hb_font_get_empty (); @@ -1370,11 +2153,11 @@ hb_font_set_parent (hb_font_t *font, /** * hb_font_get_parent: - * @font: a font. + * @font: #hb_font_t to work upon * - * + * Fetches the parent font of @font. * - * Return value: (transfer none): + * Return value: (transfer none): The parent font object * * Since: 0.9.2 **/ @@ -1386,10 +2169,10 @@ hb_font_get_parent (hb_font_t *font) /** * hb_font_set_face: - * @font: a font. - * @face: new face. + * @font: #hb_font_t to work upon + * @face: The #hb_face_t to assign * - * Sets font-face of @font. + * Sets @face as the font-face value of @font. * * Since: 1.4.3 **/ @@ -1397,26 +2180,33 @@ void hb_font_set_face (hb_font_t *font, hb_face_t *face) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; + if (face == font->face) + return; + + font->serial++; + if (unlikely (!face)) face = hb_face_get_empty (); hb_face_t *old = font->face; + hb_face_make_immutable (face); font->face = hb_face_reference (face); + font->mults_changed (); hb_face_destroy (old); } /** * hb_font_get_face: - * @font: a font. + * @font: #hb_font_t to work upon * - * + * Fetches the face associated with the specified font object. * - * Return value: (transfer none): + * Return value: (transfer none): The #hb_face_t value * * Since: 0.9.2 **/ @@ -1429,12 +2219,13 @@ hb_font_get_face (hb_font_t *font) /** * hb_font_set_funcs: - * @font: a font. - * @klass: (closure font_data) (destroy destroy) (scope notified): - * @font_data: - * @destroy: + * @font: #hb_font_t to work upon + * @klass: (closure font_data) (destroy destroy) (scope notified): The font-functions structure. + * @font_data: Data to attach to @font + * @destroy: (nullable): The function to call when @font_data is not needed anymore * - * + * Replaces the font-functions structure attached to a font, updating + * the font's user-data with @font-data and the @destroy callback. * * Since: 0.9.2 **/ @@ -1442,14 +2233,17 @@ void hb_font_set_funcs (hb_font_t *font, hb_font_funcs_t *klass, void *font_data, - hb_destroy_func_t destroy) + hb_destroy_func_t destroy /* May be NULL. */) { - if (font->immutable) { + if (hb_object_is_immutable (font)) + { if (destroy) destroy (font_data); return; } + font->serial++; + if (font->destroy) font->destroy (font->user_data); @@ -1465,26 +2259,30 @@ hb_font_set_funcs (hb_font_t *font, /** * hb_font_set_funcs_data: - * @font: a font. - * @font_data: (destroy destroy) (scope notified): - * @destroy: + * @font: #hb_font_t to work upon + * @font_data: (destroy destroy) (scope notified): Data to attach to @font + * @destroy: (nullable): The function to call when @font_data is not needed anymore * - * + * Replaces the user data attached to a font, updating the font's + * @destroy callback. * * Since: 0.9.2 **/ void hb_font_set_funcs_data (hb_font_t *font, void *font_data, - hb_destroy_func_t destroy) + hb_destroy_func_t destroy /* May be NULL. */) { /* Destroy user_data? */ - if (font->immutable) { + if (hb_object_is_immutable (font)) + { if (destroy) destroy (font_data); return; } + font->serial++; + if (font->destroy) font->destroy (font->user_data); @@ -1495,40 +2293,71 @@ hb_font_set_funcs_data (hb_font_t *font, /** * hb_font_set_scale: - * @font: a font. - * @x_scale: - * @y_scale: + * @font: #hb_font_t to work upon + * @x_scale: Horizontal scale value to assign + * @y_scale: Vertical scale value to assign * - * + * Sets the horizontal and vertical scale of a font. + * + * The font scale is a number related to, but not the same as, + * font size. Typically the client establishes a scale factor + * to be used between the two. For example, 64, or 256, which + * would be the fractional-precision part of the font scale. + * This is necessary because #hb_position_t values are integer + * types and you need to leave room for fractional values + * in there. + * + * For example, to set the font size to 20, with 64 + * levels of fractional precision you would call + * `hb_font_set_scale(font, 20 * 64, 20 * 64)`. + * + * In the example above, even what font size 20 means is up to + * you. It might be 20 pixels, or 20 points, or 20 millimeters. + * HarfBuzz does not care about that. You can set the point + * size of the font using hb_font_set_ptem(), and the pixel + * size using hb_font_set_ppem(). + * + * The choice of scale is yours but needs to be consistent between + * what you set here, and what you expect out of #hb_position_t + * as well has draw / paint API output values. + * + * Fonts default to a scale equal to the UPEM value of their face. + * A font with this setting is sometimes called an "unscaled" font. * * Since: 0.9.2 **/ void hb_font_set_scale (hb_font_t *font, - int x_scale, - int y_scale) + int x_scale, + int y_scale) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; + if (font->x_scale == x_scale && font->y_scale == y_scale) + return; + + font->serial++; + font->x_scale = x_scale; font->y_scale = y_scale; + font->mults_changed (); } /** * hb_font_get_scale: - * @font: a font. - * @x_scale: (out): - * @y_scale: (out): + * @font: #hb_font_t to work upon + * @x_scale: (out): Horizontal scale value + * @y_scale: (out): Vertical scale value * - * + * Fetches the horizontal and vertical scale of a font. * * Since: 0.9.2 **/ void hb_font_get_scale (hb_font_t *font, - int *x_scale, - int *y_scale) + int *x_scale, + int *y_scale) { if (x_scale) *x_scale = font->x_scale; if (y_scale) *y_scale = font->y_scale; @@ -1536,38 +2365,47 @@ hb_font_get_scale (hb_font_t *font, /** * hb_font_set_ppem: - * @font: a font. - * @x_ppem: - * @y_ppem: + * @font: #hb_font_t to work upon + * @x_ppem: Horizontal ppem value to assign + * @y_ppem: Vertical ppem value to assign * - * + * Sets the horizontal and vertical pixels-per-em (PPEM) of a font. + * + * These values are used for pixel-size-specific adjustment to + * shaping and draw results, though for the most part they are + * unused and can be left unset. * * Since: 0.9.2 **/ void -hb_font_set_ppem (hb_font_t *font, - unsigned int x_ppem, - unsigned int y_ppem) +hb_font_set_ppem (hb_font_t *font, + unsigned int x_ppem, + unsigned int y_ppem) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; + if (font->x_ppem == x_ppem && font->y_ppem == y_ppem) + return; + + font->serial++; + font->x_ppem = x_ppem; font->y_ppem = y_ppem; } /** * hb_font_get_ppem: - * @font: a font. - * @x_ppem: (out): - * @y_ppem: (out): + * @font: #hb_font_t to work upon + * @x_ppem: (out): Horizontal ppem value + * @y_ppem: (out): Vertical ppem value * - * + * Fetches the horizontal and vertical points-per-em (ppem) of a font. * * Since: 0.9.2 **/ void -hb_font_get_ppem (hb_font_t *font, +hb_font_get_ppem (hb_font_t *font, unsigned int *x_ppem, unsigned int *y_ppem) { @@ -1577,31 +2415,41 @@ hb_font_get_ppem (hb_font_t *font, /** * hb_font_set_ptem: - * @font: a font. - * @ptem: + * @font: #hb_font_t to work upon + * @ptem: font size in points. * - * Sets "point size" of the font. + * Sets the "point size" of a font. Set to zero to unset. + * Used in CoreText to implement optical sizing. + * + * Note: There are 72 points in an inch. * * Since: 1.6.0 **/ void -hb_font_set_ptem (hb_font_t *font, float ptem) +hb_font_set_ptem (hb_font_t *font, + float ptem) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; + if (font->ptem == ptem) + return; + + font->serial++; + font->ptem = ptem; } /** * hb_font_get_ptem: - * @font: a font. + * @font: #hb_font_t to work upon * - * Gets the "point size" of the font. A value of 0 means unset. + * Fetches the "point size" of a font. Used in CoreText to + * implement optical sizing. * - * Return value: Point size. + * Return value: Point size. A value of zero means "not set." * - * Since: 0.9.2 + * Since: 1.6.0 **/ float hb_font_get_ptem (hb_font_t *font) @@ -1609,106 +2457,433 @@ hb_font_get_ptem (hb_font_t *font) return font->ptem; } +/** + * hb_font_set_synthetic_bold: + * @font: #hb_font_t to work upon + * @x_embolden: the amount to embolden horizontally + * @y_embolden: the amount to embolden vertically + * @in_place: whether to embolden glyphs in-place + * + * Sets the "synthetic boldness" of a font. + * + * Positive values for @x_embolden / @y_embolden make a font + * bolder, negative values thinner. Typical values are in the + * 0.01 to 0.05 range. The default value is zero. + * + * Synthetic boldness is applied by offsetting the contour + * points of the glyph shape. + * + * Synthetic boldness is applied when rendering a glyph via + * hb_font_draw_glyph(). + * + * If @in_place is `false`, then glyph advance-widths are also + * adjusted, otherwise they are not. The in-place mode is + * useful for simulating [font grading](https://fonts.google.com/knowledge/glossary/grade). + * + * + * Since: 7.0.0 + **/ +void +hb_font_set_synthetic_bold (hb_font_t *font, + float x_embolden, + float y_embolden, + hb_bool_t in_place) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->x_embolden == x_embolden && + font->y_embolden == y_embolden && + font->embolden_in_place == (bool) in_place) + return; + + font->serial++; + + font->x_embolden = x_embolden; + font->y_embolden = y_embolden; + font->embolden_in_place = in_place; + font->mults_changed (); +} + +/** + * hb_font_get_synthetic_bold: + * @font: #hb_font_t to work upon + * @x_embolden: (out): return location for horizontal value + * @y_embolden: (out): return location for vertical value + * @in_place: (out): return location for in-place value + * + * Fetches the "synthetic boldness" parameters of a font. + * + * Since: 7.0.0 + **/ +void +hb_font_get_synthetic_bold (hb_font_t *font, + float *x_embolden, + float *y_embolden, + hb_bool_t *in_place) +{ + if (x_embolden) *x_embolden = font->x_embolden; + if (y_embolden) *y_embolden = font->y_embolden; + if (in_place) *in_place = font->embolden_in_place; +} + +/** + * hb_font_set_synthetic_slant: + * @font: #hb_font_t to work upon + * @slant: synthetic slant value. + * + * Sets the "synthetic slant" of a font. By default is zero. + * Synthetic slant is the graphical skew applied to the font + * at rendering time. + * + * HarfBuzz needs to know this value to adjust shaping results, + * metrics, and style values to match the slanted rendering. + * + * Note: The glyph shape fetched via the hb_font_draw_glyph() + * function is slanted to reflect this value as well. + * + * Note: The slant value is a ratio. For example, a + * 20% slant would be represented as a 0.2 value. + * + * Since: 3.3.0 + **/ +HB_EXTERN void +hb_font_set_synthetic_slant (hb_font_t *font, float slant) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->slant == slant) + return; + + font->serial++; + + font->slant = slant; + font->mults_changed (); +} + +/** + * hb_font_get_synthetic_slant: + * @font: #hb_font_t to work upon + * + * Fetches the "synthetic slant" of a font. + * + * Return value: Synthetic slant. By default is zero. + * + * Since: 3.3.0 + **/ +HB_EXTERN float +hb_font_get_synthetic_slant (hb_font_t *font) +{ + return font->slant; +} + +#ifndef HB_NO_VAR /* * Variations */ -static void -_hb_font_adopt_var_coords_normalized (hb_font_t *font, - int *coords, /* 2.14 normalized */ - unsigned int coords_length) -{ - free (font->coords); - - font->coords = coords; - font->num_coords = coords_length; -} - /** * hb_font_set_variations: + * @font: #hb_font_t to work upon + * @variations: (array length=variations_length): Array of variation settings to apply + * @variations_length: Number of variations to apply + * + * Applies a list of font-variation settings to a font. + * + * Note that this overrides all existing variations set on @font. + * Axes not included in @variations will be effectively set to their + * default values. * * Since: 1.4.2 */ void -hb_font_set_variations (hb_font_t *font, +hb_font_set_variations (hb_font_t *font, const hb_variation_t *variations, - unsigned int variations_length) + unsigned int variations_length) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; - if (!variations_length) + font->serial_coords = ++font->serial; + + if (!variations_length && font->instance_index == HB_FONT_NO_VAR_NAMED_INSTANCE) { hb_font_set_var_coords_normalized (font, nullptr, 0); return; } - unsigned int coords_length = hb_ot_var_get_axis_count (font->face); + const OT::fvar &fvar = *font->face->table.fvar; + auto axes = fvar.get_axes (); + const unsigned coords_length = axes.length; - int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; - if (unlikely (coords_length && !normalized)) + int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + hb_free (normalized); + hb_free (design_coords); + return; + } + + /* Initialize design coords. */ + for (unsigned int i = 0; i < coords_length; i++) + design_coords[i] = axes[i].get_default (); + if (font->instance_index != HB_FONT_NO_VAR_NAMED_INSTANCE) + { + unsigned count = coords_length; + /* This may fail if index is out-of-range; + * That's why we initialize design_coords from fvar above + * unconditionally. */ + hb_ot_var_named_instance_get_design_coords (font->face, font->instance_index, + &count, design_coords); + } + + for (unsigned int i = 0; i < variations_length; i++) + { + const auto tag = variations[i].tag; + const auto v = variations[i].value; + for (unsigned axis_index = 0; axis_index < coords_length; axis_index++) + if (axes[axis_index].axisTag == tag) + design_coords[axis_index] = v; + } + font->face->table.avar->map_coords (normalized, coords_length); + + hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized); + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); +} + +/** + * hb_font_set_variation: + * @font: #hb_font_t to work upon + * @tag: The #hb_tag_t tag of the variation-axis name + * @value: The value of the variation axis + * + * Change the value of one variation axis on the font. + * + * Note: This function is expensive to be called repeatedly. + * If you want to set multiple variation axes at the same time, + * use hb_font_set_variations() instead. + * + * Since: 7.1.0 + */ +void +hb_font_set_variation (hb_font_t *font, + hb_tag_t tag, + float value) +{ + if (hb_object_is_immutable (font)) return; - hb_ot_var_normalize_variations (font->face, - variations, variations_length, - normalized, coords_length); - _hb_font_adopt_var_coords_normalized (font, normalized, coords_length); + font->serial_coords = ++font->serial; + + // TODO Share some of this code with set_variations() + + const OT::fvar &fvar = *font->face->table.fvar; + auto axes = fvar.get_axes (); + const unsigned coords_length = axes.length; + + int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + hb_free (normalized); + hb_free (design_coords); + return; + } + + /* Initialize design coords. */ + if (font->design_coords) + { + assert (coords_length == font->num_coords); + for (unsigned int i = 0; i < coords_length; i++) + design_coords[i] = font->design_coords[i]; + } + else + { + for (unsigned int i = 0; i < coords_length; i++) + design_coords[i] = axes[i].get_default (); + if (font->instance_index != HB_FONT_NO_VAR_NAMED_INSTANCE) + { + unsigned count = coords_length; + /* This may fail if index is out-of-range; + * That's why we initialize design_coords from fvar above + * unconditionally. */ + hb_ot_var_named_instance_get_design_coords (font->face, font->instance_index, + &count, design_coords); + } + } + + for (unsigned axis_index = 0; axis_index < coords_length; axis_index++) + if (axes[axis_index].axisTag == tag) + design_coords[axis_index] = value; + + font->face->table.avar->map_coords (normalized, coords_length); + + hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized); + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); + } /** * hb_font_set_var_coords_design: + * @font: #hb_font_t to work upon + * @coords: (array length=coords_length): Array of variation coordinates to apply + * @coords_length: Number of coordinates to apply + * + * Applies a list of variation coordinates (in design-space units) + * to a font. + * + * Note that this overrides all existing variations set on @font. + * Axes not included in @coords will be effectively set to their + * default values. * * Since: 1.4.2 */ void -hb_font_set_var_coords_design (hb_font_t *font, - const float *coords, - unsigned int coords_length) +hb_font_set_var_coords_design (hb_font_t *font, + const float *coords, + unsigned int coords_length) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; - int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; - if (unlikely (coords_length && !normalized)) + font->serial_coords = ++font->serial; + + int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + hb_free (normalized); + hb_free (design_coords); return; + } + + if (coords_length) + hb_memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0])); hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized); - _hb_font_adopt_var_coords_normalized (font, normalized, coords_length); + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); +} + +/** + * hb_font_set_var_named_instance: + * @font: a font. + * @instance_index: named instance index. + * + * Sets design coords of a font from a named-instance index. + * + * Since: 2.6.0 + */ +void +hb_font_set_var_named_instance (hb_font_t *font, + unsigned int instance_index) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->instance_index == instance_index) + return; + + font->serial_coords = ++font->serial; + + font->instance_index = instance_index; + hb_font_set_variations (font, nullptr, 0); +} + +/** + * hb_font_get_var_named_instance: + * @font: a font. + * + * Returns the currently-set named-instance index of the font. + * + * Return value: Named-instance index or %HB_FONT_NO_VAR_NAMED_INSTANCE. + * + * Since: 7.0.0 + **/ +unsigned int +hb_font_get_var_named_instance (hb_font_t *font) +{ + return font->instance_index; } /** * hb_font_set_var_coords_normalized: + * @font: #hb_font_t to work upon + * @coords: (array length=coords_length): Array of variation coordinates to apply + * @coords_length: Number of coordinates to apply + * + * Applies a list of variation coordinates (in normalized units) + * to a font. + * + * Note that this overrides all existing variations set on @font. + * Axes not included in @coords will be effectively set to their + * default values. + * + * Note: Coordinates should be normalized to 2.14. * * Since: 1.4.2 */ void -hb_font_set_var_coords_normalized (hb_font_t *font, - const int *coords, /* 2.14 normalized */ - unsigned int coords_length) +hb_font_set_var_coords_normalized (hb_font_t *font, + const int *coords, /* 2.14 normalized */ + unsigned int coords_length) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; - int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr; - if (unlikely (coords_length && !copy)) + font->serial_coords = ++font->serial; + + int *copy = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; + int *unmapped = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (design_coords[0])) : nullptr; + + if (unlikely (coords_length && !(copy && unmapped && design_coords))) + { + hb_free (copy); + hb_free (unmapped); + hb_free (design_coords); return; + } if (coords_length) - memcpy (copy, coords, coords_length * sizeof (coords[0])); + { + hb_memcpy (copy, coords, coords_length * sizeof (coords[0])); + hb_memcpy (unmapped, coords, coords_length * sizeof (coords[0])); + } - _hb_font_adopt_var_coords_normalized (font, copy, coords_length); + /* Best effort design coords simulation */ + font->face->table.avar->unmap_coords (unmapped, coords_length); + for (unsigned int i = 0; i < coords_length; ++i) + design_coords[i] = font->face->table.fvar->unnormalize_axis_value (i, unmapped[i]); + hb_free (unmapped); + + _hb_font_adopt_var_coords (font, copy, design_coords, coords_length); } /** * hb_font_get_var_coords_normalized: + * @font: #hb_font_t to work upon + * @length: (out): Number of coordinates retrieved + * + * Fetches the list of normalized variation coordinates currently + * set on a font. + * + * Note that this returned array may only contain values for some + * (or none) of the axes; omitted axes effectively have zero values. * * Return value is valid as long as variation coordinates of the font * are not modified. * + * Return value: coordinates array + * * Since: 1.4.2 */ const int * -hb_font_get_var_coords_normalized (hb_font_t *font, +hb_font_get_var_coords_normalized (hb_font_t *font, unsigned int *length) { if (length) @@ -1717,9 +2892,37 @@ hb_font_get_var_coords_normalized (hb_font_t *font, return font->coords; } +/** + * hb_font_get_var_coords_design: + * @font: #hb_font_t to work upon + * @length: (out): Number of coordinates retrieved + * + * Fetches the list of variation coordinates (in design-space units) currently + * set on a font. + * + * Note that this returned array may only contain values for some + * (or none) of the axes; omitted axes effectively have their default + * values. + * + * Return value is valid as long as variation coordinates of the font + * are not modified. + * + * Return value: coordinates array + * + * Since: 3.3.0 + */ +const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length) +{ + if (length) + *length = font->num_coords; + + return font->design_coords; +} +#endif #ifndef HB_DISABLE_DEPRECATED - /* * Deprecated get_glyph_func(): */ @@ -1746,7 +2949,7 @@ trampoline_create (FuncType func, { typedef hb_trampoline_t trampoline_t; - trampoline_t *trampoline = (trampoline_t *) calloc (1, sizeof (trampoline_t)); + trampoline_t *trampoline = (trampoline_t *) hb_calloc (1, sizeof (trampoline_t)); if (unlikely (!trampoline)) return nullptr; @@ -1775,29 +2978,29 @@ trampoline_destroy (void *user_data) if (closure->destroy) closure->destroy (closure->user_data); - free (closure); + hb_free (closure); } typedef hb_trampoline_t hb_font_get_glyph_trampoline_t; static hb_bool_t -hb_font_get_nominal_glyph_trampoline (hb_font_t *font, - void *font_data, - hb_codepoint_t unicode, +hb_font_get_nominal_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, hb_codepoint_t *glyph, - void *user_data) + void *user_data) { hb_font_get_glyph_trampoline_t *trampoline = (hb_font_get_glyph_trampoline_t *) user_data; return trampoline->func (font, font_data, unicode, 0, glyph, trampoline->closure.user_data); } static hb_bool_t -hb_font_get_variation_glyph_trampoline (hb_font_t *font, - void *font_data, - hb_codepoint_t unicode, - hb_codepoint_t variation_selector, +hb_font_get_variation_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, hb_codepoint_t *glyph, - void *user_data) + void *user_data) { hb_font_get_glyph_trampoline_t *trampoline = (hb_font_get_glyph_trampoline_t *) user_data; return trampoline->func (font, font_data, unicode, variation_selector, glyph, trampoline->closure.user_data); @@ -1805,10 +3008,10 @@ hb_font_get_variation_glyph_trampoline (hb_font_t *font, /** * hb_font_funcs_set_glyph_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: The font-functions structure + * @func: (closure user_data) (destroy destroy) (scope notified): callback function + * @user_data: data to pass to @func + * @destroy: (nullable): function to call when @user_data is not needed anymore * * Deprecated. Use hb_font_funcs_set_nominal_glyph_func() and * hb_font_funcs_set_variation_glyph_func() instead. @@ -1817,10 +3020,18 @@ hb_font_get_variation_glyph_trampoline (hb_font_t *font, * Deprecated: 1.2.3 **/ void -hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, - hb_font_get_glyph_func_t func, - void *user_data, hb_destroy_func_t destroy) +hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_func_t func, + void *user_data, + hb_destroy_func_t destroy /* May be NULL. */) { + if (hb_object_is_immutable (ffuncs)) + { + if (destroy) + destroy (user_data); + return; + } + hb_font_get_glyph_trampoline_t *trampoline; trampoline = trampoline_create (func, user_data, destroy); @@ -1831,16 +3042,27 @@ hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, return; } + /* Since we pass it to two destroying functions. */ + trampoline_reference (&trampoline->closure); + hb_font_funcs_set_nominal_glyph_func (ffuncs, hb_font_get_nominal_glyph_trampoline, trampoline, trampoline_destroy); - trampoline_reference (&trampoline->closure); hb_font_funcs_set_variation_glyph_func (ffuncs, hb_font_get_variation_glyph_trampoline, trampoline, trampoline_destroy); } +#endif -#endif /* HB_DISABLE_DEPRECATED */ + +void +hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_shape_func_t func, + void *user_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + hb_font_funcs_set_draw_glyph_func (ffuncs, func, user_data, destroy); +} diff --git a/src/3rdparty/harfbuzz-ng/src/hb-font.h b/src/3rdparty/harfbuzz-ng/src/hb-font.h index 540cdcab953..f3b589bd0d8 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-font.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-font.h @@ -24,7 +24,7 @@ * Red Hat Author(s): Behdad Esfahbod */ -#ifndef HB_H_IN +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif @@ -33,17 +33,28 @@ #include "hb-common.h" #include "hb-face.h" +#include "hb-draw.h" +#include "hb-paint.h" HB_BEGIN_DECLS - -typedef struct hb_font_t hb_font_t; - - /* * hb_font_funcs_t */ +/** + * hb_font_funcs_t: + * + * Data type containing a set of virtual methods used for + * working on #hb_font_t font objects. + * + * HarfBuzz provides a lightweight default function for each of + * the methods in #hb_font_funcs_t. Client programs can implement + * their own replacements for the individual font functions, as + * needed, and replace the default by calling the setter for a + * method. + * + **/ typedef struct hb_font_funcs_t hb_font_funcs_t; HB_EXTERN hb_font_funcs_t * @@ -67,8 +78,8 @@ hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, HB_EXTERN void * -hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, - hb_user_data_key_t *key); +hb_font_funcs_get_user_data (const hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key); HB_EXTERN void @@ -78,14 +89,23 @@ HB_EXTERN hb_bool_t hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs); -/* font and glyph extents */ +/* font extents */ -/* Note that typically ascender is positive and descender negative in coordinate systems that grow up. */ -typedef struct hb_font_extents_t -{ - hb_position_t ascender; /* typographic ascender. */ - hb_position_t descender; /* typographic descender. */ - hb_position_t line_gap; /* suggested line spacing gap. */ +/** + * hb_font_extents_t: + * @ascender: The height of typographic ascenders. + * @descender: The depth of typographic descenders. + * @line_gap: The suggested line-spacing gap. + * + * Font-wide extent values, measured in font units. + * + * Note that typically @ascender is positive and @descender + * negative, in coordinate systems that grow up. + **/ +typedef struct hb_font_extents_t { + hb_position_t ascender; + hb_position_t descender; + hb_position_t line_gap; /*< private >*/ hb_position_t reserved9; hb_position_t reserved8; @@ -98,84 +118,443 @@ typedef struct hb_font_extents_t hb_position_t reserved1; } hb_font_extents_t; -/* Note that height is negative in coordinate systems that grow up. */ -typedef struct hb_glyph_extents_t -{ - hb_position_t x_bearing; /* left side of glyph from origin. */ - hb_position_t y_bearing; /* top side of glyph from origin. */ - hb_position_t width; /* distance from left to right side. */ - hb_position_t height; /* distance from top to bottom side. */ -} hb_glyph_extents_t; - /* func types */ +/** + * hb_font_get_font_extents_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @extents: (out): The font extents retrieved + * @user_data: User data pointer passed by the caller + * + * This method should retrieve the extents for a font. + * + **/ typedef hb_bool_t (*hb_font_get_font_extents_func_t) (hb_font_t *font, void *font_data, - hb_font_extents_t *metrics, + hb_font_extents_t *extents, void *user_data); + +/** + * hb_font_get_font_h_extents_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the extents for a font, for horizontal-direction + * text segments. Extents must be returned in an #hb_glyph_extents output + * parameter. + * + **/ typedef hb_font_get_font_extents_func_t hb_font_get_font_h_extents_func_t; + +/** + * hb_font_get_font_v_extents_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the extents for a font, for vertical-direction + * text segments. Extents must be returned in an #hb_glyph_extents output + * parameter. + * + **/ typedef hb_font_get_font_extents_func_t hb_font_get_font_v_extents_func_t; +/** + * hb_font_get_nominal_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @unicode: The Unicode code point to query + * @glyph: (out): The glyph ID retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the nominal glyph ID for a specified Unicode code + * point. Glyph IDs must be returned in a #hb_codepoint_t output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_nominal_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t unicode, hb_codepoint_t *glyph, void *user_data); + +/** + * hb_font_get_variation_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @unicode: The Unicode code point to query + * @variation_selector: The variation-selector code point to query + * @glyph: (out): The glyph ID retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the glyph ID for a specified Unicode code point + * followed by a specified Variation Selector code point. Glyph IDs must be + * returned in a #hb_codepoint_t output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_variation_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t unicode, hb_codepoint_t variation_selector, hb_codepoint_t *glyph, void *user_data); +/** + * hb_font_get_nominal_glyphs_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @count: number of code points to query + * @first_unicode: The first Unicode code point to query + * @unicode_stride: The stride between successive code points + * @first_glyph: (out): The first glyph ID retrieved + * @glyph_stride: The stride between successive glyph IDs + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the nominal glyph IDs for a sequence of + * Unicode code points. Glyph IDs must be returned in a #hb_codepoint_t + * output parameter. + * + * Return value: the number of code points processed + * + **/ +typedef unsigned int (*hb_font_get_nominal_glyphs_func_t) (hb_font_t *font, void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data); + +/** + * hb_font_get_glyph_advance_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advance for a specified glyph. The + * method must return an #hb_position_t. + * + * Return value: The advance of @glyph within @font + * + **/ typedef hb_position_t (*hb_font_get_glyph_advance_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, void *user_data); + +/** + * hb_font_get_glyph_h_advance_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advance for a specified glyph, in + * horizontal-direction text segments. Advances must be returned in + * an #hb_position_t output parameter. + * + **/ typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t; + +/** + * hb_font_get_glyph_v_advance_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advance for a specified glyph, in + * vertical-direction text segments. Advances must be returned in + * an #hb_position_t output parameter. + * + **/ typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t; +/** + * hb_font_get_glyph_advances_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_advance: (out): The first advance retrieved + * @advance_stride: The stride between successive advances + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advances for a sequence of glyphs. + * + **/ +typedef void (*hb_font_get_glyph_advances_func_t) (hb_font_t* font, void* font_data, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data); + +/** + * hb_font_get_glyph_h_advances_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advances for a sequence of glyphs, in + * horizontal-direction text segments. + * + **/ +typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_h_advances_func_t; + +/** + * hb_font_get_glyph_v_advances_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advances for a sequence of glyphs, in + * vertical-direction text segments. + * + **/ +typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t; + +/** + * hb_font_get_glyph_origin_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @x: (out): The X coordinate of the origin + * @y: (out): The Y coordinate of the origin + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in font units) of the + * origin for a glyph. Each coordinate must be returned in an #hb_position_t + * output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y, void *user_data); + +/** + * hb_font_get_glyph_h_origin_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in font units) of the + * origin for a glyph, for horizontal-direction text segments. Each + * coordinate must be returned in an #hb_position_t output parameter. + * + **/ typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t; + +/** + * hb_font_get_glyph_v_origin_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in font units) of the + * origin for a glyph, for vertical-direction text segments. Each coordinate + * must be returned in an #hb_position_t output parameter. + * + **/ typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t; +/** + * hb_font_get_glyph_kerning_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @first_glyph: The glyph ID of the first glyph in the glyph pair + * @second_glyph: The glyph ID of the second glyph in the glyph pair + * @user_data: User data pointer passed by the caller + * + * This method should retrieve the kerning-adjustment value for a glyph-pair in + * the specified font, for horizontal text segments. + * + **/ typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, void *user_data); +/** + * hb_font_get_glyph_h_kerning_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the kerning-adjustment value for a glyph-pair in + * the specified font, for horizontal text segments. + * + **/ typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t; -typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t; +/** + * hb_font_get_glyph_extents_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @extents: (out): The #hb_glyph_extents_t retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the extents for a specified glyph. Extents must be + * returned in an #hb_glyph_extents output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_glyph_extents_t *extents, void *user_data); + +/** + * hb_font_get_glyph_contour_point_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @point_index: The contour-point index to query + * @x: (out): The X value retrieved for the contour point + * @y: (out): The Y value retrieved for the contour point + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in font units) for a + * specified contour point in a glyph. Each coordinate must be returned as + * an #hb_position_t output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, unsigned int point_index, hb_position_t *x, hb_position_t *y, void *user_data); +/** + * hb_font_get_glyph_name_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @name: (out) (array length=size): Name string retrieved for the glyph ID + * @size: Length of the glyph-name string retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the glyph name that corresponds to a + * glyph ID. The name should be returned in a string output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_glyph_name_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, char *name, unsigned int size, void *user_data); + +/** + * hb_font_get_glyph_from_name_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @name: (array length=len): The name string to query + * @len: The length of the name queried + * @glyph: (out): The glyph ID retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the glyph ID that corresponds to a glyph-name + * string. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *font_data, const char *name, int len, /* -1 means nul-terminated */ hb_codepoint_t *glyph, void *user_data); +/** + * hb_font_get_glyph_shape_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @draw_funcs: The draw functions to send the shape data to + * @draw_data: The data accompanying the draw functions + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: 4.0.0 + * Deprecated: 7.0.0: Use #hb_font_draw_glyph_func_t instead + **/ +typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data); + +/** + * hb_font_draw_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @draw_funcs: The draw functions to send the shape data to + * @draw_data: The data accompanying the draw functions + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: 7.0.0 + * + **/ +typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data); + +/** + * hb_font_paint_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @paint_funcs: The paint functions to use + * @paint_data: The data accompanying the paint functions + * @palette_index: The color palette to use + * @foreground: The foreground color + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: 7.0.0 + */ +typedef void (*hb_font_paint_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data); /* func setters */ /** * hb_font_funcs_set_font_h_extents_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: - * + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * + * Sets the implementation function for #hb_font_get_font_h_extents_func_t. * * Since: 1.1.2 **/ @@ -186,12 +565,12 @@ hb_font_funcs_set_font_h_extents_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_font_v_extents_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: - * + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * + * Sets the implementation function for #hb_font_get_font_v_extents_func_t. * * Since: 1.1.2 **/ @@ -202,12 +581,12 @@ hb_font_funcs_set_font_v_extents_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_nominal_glyph_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_nominal_glyph_func_t. * * Since: 1.2.3 **/ @@ -217,13 +596,29 @@ hb_font_funcs_set_nominal_glyph_func (hb_font_funcs_t *ffuncs, void *user_data, hb_destroy_func_t destroy); /** - * hb_font_funcs_set_variation_glyph_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * hb_font_funcs_set_nominal_glyphs_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_nominal_glyphs_func_t. + * + * Since: 2.0.0 + **/ +HB_EXTERN void +hb_font_funcs_set_nominal_glyphs_func (hb_font_funcs_t *ffuncs, + hb_font_get_nominal_glyphs_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_variation_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_variation_glyph_func_t. * * Since: 1.2.3 **/ @@ -234,12 +629,12 @@ hb_font_funcs_set_variation_glyph_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_h_advance_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_h_advance_func_t. * * Since: 0.9.2 **/ @@ -250,12 +645,12 @@ hb_font_funcs_set_glyph_h_advance_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_v_advance_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_v_advance_func_t. * * Since: 0.9.2 **/ @@ -265,13 +660,45 @@ hb_font_funcs_set_glyph_v_advance_func (hb_font_funcs_t *ffuncs, void *user_data, hb_destroy_func_t destroy); /** - * hb_font_funcs_set_glyph_h_origin_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * hb_font_funcs_set_glyph_h_advances_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_h_advances_func_t. + * + * Since: 1.8.6 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_advances_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_advances_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_advances_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_v_advances_func_t. + * + * Since: 1.8.6 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_advances_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_advances_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_origin_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_h_origin_func_t. * * Since: 0.9.2 **/ @@ -282,12 +709,12 @@ hb_font_funcs_set_glyph_h_origin_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_v_origin_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_v_origin_func_t. * * Since: 0.9.2 **/ @@ -298,12 +725,12 @@ hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_h_kerning_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_h_kerning_func_t. * * Since: 0.9.2 **/ @@ -312,30 +739,14 @@ hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_h_kerning_func_t func, void *user_data, hb_destroy_func_t destroy); -/** - * hb_font_funcs_set_glyph_v_kerning_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: - * - * - * - * Since: 0.9.2 - **/ -HB_EXTERN void -hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs, - hb_font_get_glyph_v_kerning_func_t func, - void *user_data, hb_destroy_func_t destroy); - /** * hb_font_funcs_set_glyph_extents_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_extents_func_t. * * Since: 0.9.2 **/ @@ -346,12 +757,12 @@ hb_font_funcs_set_glyph_extents_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_contour_point_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_contour_point_func_t. * * Since: 0.9.2 **/ @@ -362,12 +773,12 @@ hb_font_funcs_set_glyph_contour_point_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_name_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_name_func_t. * * Since: 0.9.2 **/ @@ -378,12 +789,12 @@ hb_font_funcs_set_glyph_name_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_from_name_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_from_name_func_t. * * Since: 0.9.2 **/ @@ -392,6 +803,57 @@ hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_from_name_func_t func, void *user_data, hb_destroy_func_t destroy); +/** + * hb_font_funcs_set_glyph_shape_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_shape_func_t, + * which is the same as #hb_font_draw_glyph_func_t. + * + * Since: 4.0.0 + * Deprecated: 7.0.0: Use hb_font_funcs_set_draw_glyph_func() instead + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_shape_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_draw_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_draw_glyph_func_t, + * which is the same as #hb_font_get_glyph_shape_func_t. + * + * Since: 7.0.0 + **/ +HB_EXTERN void +hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_draw_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_paint_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is no longer needed + * + * Sets the implementation function for #hb_font_paint_glyph_func_t. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_font_funcs_set_paint_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_paint_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + /* func dispatch */ HB_EXTERN hb_bool_t @@ -410,6 +872,14 @@ hb_font_get_variation_glyph (hb_font_t *font, hb_codepoint_t unicode, hb_codepoint_t variation_selector, hb_codepoint_t *glyph); +HB_EXTERN unsigned int +hb_font_get_nominal_glyphs (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride); + HB_EXTERN hb_position_t hb_font_get_glyph_h_advance (hb_font_t *font, hb_codepoint_t glyph); @@ -417,6 +887,21 @@ HB_EXTERN hb_position_t hb_font_get_glyph_v_advance (hb_font_t *font, hb_codepoint_t glyph); +HB_EXTERN void +hb_font_get_glyph_h_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride); +HB_EXTERN void +hb_font_get_glyph_v_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride); + HB_EXTERN hb_bool_t hb_font_get_glyph_h_origin (hb_font_t *font, hb_codepoint_t glyph, @@ -429,9 +914,6 @@ hb_font_get_glyph_v_origin (hb_font_t *font, HB_EXTERN hb_position_t hb_font_get_glyph_h_kerning (hb_font_t *font, hb_codepoint_t left_glyph, hb_codepoint_t right_glyph); -HB_EXTERN hb_position_t -hb_font_get_glyph_v_kerning (hb_font_t *font, - hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph); HB_EXTERN hb_bool_t hb_font_get_glyph_extents (hb_font_t *font, @@ -452,11 +934,27 @@ hb_font_get_glyph_from_name (hb_font_t *font, const char *name, int len, /* -1 means nul-terminated */ hb_codepoint_t *glyph); +HB_EXTERN void +hb_font_get_glyph_shape (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data); + +HB_EXTERN void +hb_font_draw_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data); + +HB_EXTERN void +hb_font_paint_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground); /* high-level funcs, with fallback */ /* Calls either hb_font_get_nominal_glyph() if variation_selector is 0, - * otherwise callse hb_font_get_variation_glyph(). */ + * otherwise calls hb_font_get_variation_glyph(). */ HB_EXTERN hb_bool_t hb_font_get_glyph (hb_font_t *font, hb_codepoint_t unicode, hb_codepoint_t variation_selector, @@ -472,6 +970,14 @@ hb_font_get_glyph_advance_for_direction (hb_font_t *font, hb_direction_t direction, hb_position_t *x, hb_position_t *y); HB_EXTERN void +hb_font_get_glyph_advances_for_direction (hb_font_t* font, + hb_direction_t direction, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride); +HB_EXTERN void hb_font_get_glyph_origin_for_direction (hb_font_t *font, hb_codepoint_t glyph, hb_direction_t direction, @@ -547,7 +1053,7 @@ hb_font_set_user_data (hb_font_t *font, HB_EXTERN void * -hb_font_get_user_data (hb_font_t *font, +hb_font_get_user_data (const hb_font_t *font, hb_user_data_key_t *key); HB_EXTERN void @@ -556,6 +1062,12 @@ hb_font_make_immutable (hb_font_t *font); HB_EXTERN hb_bool_t hb_font_is_immutable (hb_font_t *font); +HB_EXTERN unsigned int +hb_font_get_serial (hb_font_t *font); + +HB_EXTERN void +hb_font_changed (hb_font_t *font); + HB_EXTERN void hb_font_set_parent (hb_font_t *font, hb_font_t *parent); @@ -580,8 +1092,8 @@ hb_font_set_funcs (hb_font_t *font, /* Be *very* careful with this function! */ HB_EXTERN void hb_font_set_funcs_data (hb_font_t *font, - void *font_data, - hb_destroy_func_t destroy); + void *font_data, + hb_destroy_func_t destroy); HB_EXTERN void @@ -617,16 +1129,41 @@ hb_font_set_ptem (hb_font_t *font, float ptem); HB_EXTERN float hb_font_get_ptem (hb_font_t *font); +HB_EXTERN void +hb_font_set_synthetic_bold (hb_font_t *font, + float x_embolden, float y_embolden, + hb_bool_t in_place); + +HB_EXTERN void +hb_font_get_synthetic_bold (hb_font_t *font, + float *x_embolden, float *y_embolden, + hb_bool_t *in_place); + +HB_EXTERN void +hb_font_set_synthetic_slant (hb_font_t *font, float slant); + +HB_EXTERN float +hb_font_get_synthetic_slant (hb_font_t *font); + HB_EXTERN void hb_font_set_variations (hb_font_t *font, const hb_variation_t *variations, unsigned int variations_length); +HB_EXTERN void +hb_font_set_variation (hb_font_t *font, + hb_tag_t tag, + float value); + HB_EXTERN void hb_font_set_var_coords_design (hb_font_t *font, const float *coords, unsigned int coords_length); +HB_EXTERN const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length); + HB_EXTERN void hb_font_set_var_coords_normalized (hb_font_t *font, const int *coords, /* 2.14 normalized */ @@ -636,6 +1173,24 @@ HB_EXTERN const int * hb_font_get_var_coords_normalized (hb_font_t *font, unsigned int *length); +/** + * HB_FONT_NO_VAR_NAMED_INSTANCE: + * + * Constant signifying that a font does not have any + * named-instance index set. This is the default of + * a font. + * + * Since: 7.0.0 + */ +#define HB_FONT_NO_VAR_NAMED_INSTANCE 0xFFFFFFFF + +HB_EXTERN void +hb_font_set_var_named_instance (hb_font_t *font, + unsigned int instance_index); + +HB_EXTERN unsigned int +hb_font_get_var_named_instance (hb_font_t *font); + HB_END_DECLS #endif /* HB_FONT_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-font.hh b/src/3rdparty/harfbuzz-ng/src/hb-font.hh new file mode 100644 index 00000000000..f503575c34a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-font.hh @@ -0,0 +1,720 @@ +/* + * Copyright ยฉ 2009 Red Hat, Inc. + * Copyright ยฉ 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FONT_HH +#define HB_FONT_HH + +#include "hb.hh" + +#include "hb-face.hh" +#include "hb-shaper.hh" + + +/* + * hb_font_funcs_t + */ + +#define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \ + HB_FONT_FUNC_IMPLEMENT (get_,font_h_extents) \ + HB_FONT_FUNC_IMPLEMENT (get_,font_v_extents) \ + HB_FONT_FUNC_IMPLEMENT (get_,nominal_glyph) \ + HB_FONT_FUNC_IMPLEMENT (get_,nominal_glyphs) \ + HB_FONT_FUNC_IMPLEMENT (get_,variation_glyph) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_advance) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_advance) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_advances) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_advances) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_origin) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_origin) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_kerning) \ + HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_kerning)) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_extents) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_contour_point) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_name) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_from_name) \ + HB_FONT_FUNC_IMPLEMENT (,draw_glyph) \ + HB_FONT_FUNC_IMPLEMENT (,paint_glyph) \ + /* ^--- Add new callbacks here */ + +struct hb_font_funcs_t +{ + hb_object_header_t header; + + struct { +#define HB_FONT_FUNC_IMPLEMENT(get_,name) void *name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } *user_data; + + struct { +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_destroy_func_t name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } *destroy; + + /* Don't access these directly. Call font->get_*() instead. */ + union get_t { + struct get_funcs_t { +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_func_t name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } f; + void (*array[0 +#define HB_FONT_FUNC_IMPLEMENT(get_,name) +1 + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + ]) (); + } get; +}; +DECLARE_NULL_INSTANCE (hb_font_funcs_t); + + +/* + * hb_font_t + */ + +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, font); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +struct hb_font_t +{ + hb_object_header_t header; + unsigned int serial; + unsigned int serial_coords; + + hb_font_t *parent; + hb_face_t *face; + + int32_t x_scale; + int32_t y_scale; + + float x_embolden; + float y_embolden; + bool embolden_in_place; + int32_t x_strength; /* x_embolden, in scaled units. */ + int32_t y_strength; /* y_embolden, in scaled units. */ + + float slant; + float slant_xy; + + float x_multf; + float y_multf; + int64_t x_mult; + int64_t y_mult; + + unsigned int x_ppem; + unsigned int y_ppem; + + float ptem; + + /* Font variation coordinates. */ + unsigned int instance_index; + unsigned int num_coords; + int *coords; + float *design_coords; + + hb_font_funcs_t *klass; + void *user_data; + hb_destroy_func_t destroy; + + hb_shaper_object_dataset_t data; /* Various shaper data. */ + + + /* Convert from font-space to user-space */ + int64_t dir_mult (hb_direction_t direction) + { return HB_DIRECTION_IS_VERTICAL(direction) ? y_mult : x_mult; } + hb_position_t em_scale_x (int16_t v) { return em_mult (v, x_mult); } + hb_position_t em_scale_y (int16_t v) { return em_mult (v, y_mult); } + hb_position_t em_scalef_x (float v) { return em_multf (v, x_multf); } + hb_position_t em_scalef_y (float v) { return em_multf (v, y_multf); } + float em_fscale_x (int16_t v) { return em_fmult (v, x_multf); } + float em_fscale_y (int16_t v) { return em_fmult (v, y_multf); } + float em_fscalef_x (float v) { return em_fmultf (v, x_multf); } + float em_fscalef_y (float v) { return em_fmultf (v, y_multf); } + hb_position_t em_scale_dir (int16_t v, hb_direction_t direction) + { return em_mult (v, dir_mult (direction)); } + + /* Convert from parent-font user-space to our user-space */ + hb_position_t parent_scale_x_distance (hb_position_t v) + { + if (unlikely (parent && parent->x_scale != x_scale)) + return (hb_position_t) (v * (int64_t) this->x_scale / this->parent->x_scale); + return v; + } + hb_position_t parent_scale_y_distance (hb_position_t v) + { + if (unlikely (parent && parent->y_scale != y_scale)) + return (hb_position_t) (v * (int64_t) this->y_scale / this->parent->y_scale); + return v; + } + hb_position_t parent_scale_x_position (hb_position_t v) + { return parent_scale_x_distance (v); } + hb_position_t parent_scale_y_position (hb_position_t v) + { return parent_scale_y_distance (v); } + + void parent_scale_distance (hb_position_t *x, hb_position_t *y) + { + *x = parent_scale_x_distance (*x); + *y = parent_scale_y_distance (*y); + } + void parent_scale_position (hb_position_t *x, hb_position_t *y) + { + *x = parent_scale_x_position (*x); + *y = parent_scale_y_position (*y); + } + + void scale_glyph_extents (hb_glyph_extents_t *extents) + { + float x1 = em_fscale_x (extents->x_bearing); + float y1 = em_fscale_y (extents->y_bearing); + float x2 = em_fscale_x (extents->x_bearing + extents->width); + float y2 = em_fscale_y (extents->y_bearing + extents->height); + + /* Apply slant. */ + if (slant_xy) + { + x1 += hb_min (y1 * slant_xy, y2 * slant_xy); + x2 += hb_max (y1 * slant_xy, y2 * slant_xy); + } + + extents->x_bearing = floorf (x1); + extents->y_bearing = floorf (y1); + extents->width = ceilf (x2) - extents->x_bearing; + extents->height = ceilf (y2) - extents->y_bearing; + + if (x_strength || y_strength) + { + /* Y */ + int y_shift = y_strength; + if (y_scale < 0) y_shift = -y_shift; + extents->y_bearing += y_shift; + extents->height -= y_shift; + + /* X */ + int x_shift = x_strength; + if (x_scale < 0) x_shift = -x_shift; + if (embolden_in_place) + extents->x_bearing -= x_shift / 2; + extents->width += x_shift; + } + } + + + /* Public getters */ + + HB_INTERNAL bool has_func (unsigned int i); + HB_INTERNAL bool has_func_set (unsigned int i); + + /* has_* ... */ +#define HB_FONT_FUNC_IMPLEMENT(get_,name) \ + bool \ + has_##name##_func () \ + { \ + hb_font_funcs_t *funcs = this->klass; \ + unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \ + return has_func (i); \ + } \ + bool \ + has_##name##_func_set () \ + { \ + hb_font_funcs_t *funcs = this->klass; \ + unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \ + return has_func_set (i); \ + } + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + + hb_bool_t get_font_h_extents (hb_font_extents_t *extents) + { + hb_memset (extents, 0, sizeof (*extents)); + return klass->get.f.font_h_extents (this, user_data, + extents, + !klass->user_data ? nullptr : klass->user_data->font_h_extents); + } + hb_bool_t get_font_v_extents (hb_font_extents_t *extents) + { + hb_memset (extents, 0, sizeof (*extents)); + return klass->get.f.font_v_extents (this, user_data, + extents, + !klass->user_data ? nullptr : klass->user_data->font_v_extents); + } + + bool has_glyph (hb_codepoint_t unicode) + { + hb_codepoint_t glyph; + return get_nominal_glyph (unicode, &glyph); + } + + hb_bool_t get_nominal_glyph (hb_codepoint_t unicode, + hb_codepoint_t *glyph, + hb_codepoint_t not_found = 0) + { + *glyph = not_found; + return klass->get.f.nominal_glyph (this, user_data, + unicode, glyph, + !klass->user_data ? nullptr : klass->user_data->nominal_glyph); + } + unsigned int get_nominal_glyphs (unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride) + { + return klass->get.f.nominal_glyphs (this, user_data, + count, + first_unicode, unicode_stride, + first_glyph, glyph_stride, + !klass->user_data ? nullptr : klass->user_data->nominal_glyphs); + } + + hb_bool_t get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + hb_codepoint_t not_found = 0) + { + *glyph = not_found; + return klass->get.f.variation_glyph (this, user_data, + unicode, variation_selector, glyph, + !klass->user_data ? nullptr : klass->user_data->variation_glyph); + } + + hb_position_t get_glyph_h_advance (hb_codepoint_t glyph) + { + return klass->get.f.glyph_h_advance (this, user_data, + glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_h_advance); + } + + hb_position_t get_glyph_v_advance (hb_codepoint_t glyph) + { + return klass->get.f.glyph_v_advance (this, user_data, + glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_v_advance); + } + + void get_glyph_h_advances (unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride) + { + return klass->get.f.glyph_h_advances (this, user_data, + count, + first_glyph, glyph_stride, + first_advance, advance_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_h_advances); + } + + void get_glyph_v_advances (unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride) + { + return klass->get.f.glyph_v_advances (this, user_data, + count, + first_glyph, glyph_stride, + first_advance, advance_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_v_advances); + } + + hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_h_origin (this, user_data, + glyph, x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_h_origin); + } + + hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_v_origin (this, user_data, + glyph, x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_v_origin); + } + + hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph) + { +#ifdef HB_DISABLE_DEPRECATED + return 0; +#else + return klass->get.f.glyph_h_kerning (this, user_data, + left_glyph, right_glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_h_kerning); +#endif + } + + hb_position_t get_glyph_v_kerning (hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph) + { +#ifdef HB_DISABLE_DEPRECATED + return 0; +#else + return klass->get.f.glyph_v_kerning (this, user_data, + top_glyph, bottom_glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_v_kerning); +#endif + } + + hb_bool_t get_glyph_extents (hb_codepoint_t glyph, + hb_glyph_extents_t *extents) + { + hb_memset (extents, 0, sizeof (*extents)); + return klass->get.f.glyph_extents (this, user_data, + glyph, + extents, + !klass->user_data ? nullptr : klass->user_data->glyph_extents); + } + + hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_contour_point (this, user_data, + glyph, point_index, + x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_contour_point); + } + + hb_bool_t get_glyph_name (hb_codepoint_t glyph, + char *name, unsigned int size) + { + if (size) *name = '\0'; + return klass->get.f.glyph_name (this, user_data, + glyph, + name, size, + !klass->user_data ? nullptr : klass->user_data->glyph_name); + } + + hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) + { + *glyph = 0; + if (len == -1) len = strlen (name); + return klass->get.f.glyph_from_name (this, user_data, + name, len, + glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_from_name); + } + + void draw_glyph (hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data) + { + klass->get.f.draw_glyph (this, user_data, + glyph, + draw_funcs, draw_data, + !klass->user_data ? nullptr : klass->user_data->draw_glyph); + } + + void paint_glyph (hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground) + { + klass->get.f.paint_glyph (this, user_data, + glyph, + paint_funcs, paint_data, + palette, foreground, + !klass->user_data ? nullptr : klass->user_data->paint_glyph); + } + + /* A bit higher-level, and with fallback */ + + void get_h_extents_with_fallback (hb_font_extents_t *extents) + { + if (!get_font_h_extents (extents)) + { + extents->ascender = y_scale * .8; + extents->descender = extents->ascender - y_scale; + extents->line_gap = 0; + } + } + void get_v_extents_with_fallback (hb_font_extents_t *extents) + { + if (!get_font_v_extents (extents)) + { + extents->ascender = x_scale / 2; + extents->descender = extents->ascender - x_scale; + extents->line_gap = 0; + } + } + + void get_extents_for_direction (hb_direction_t direction, + hb_font_extents_t *extents) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_h_extents_with_fallback (extents); + else + get_v_extents_with_fallback (extents); + } + + void get_glyph_advance_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + *x = get_glyph_h_advance (glyph); + else + *y = get_glyph_v_advance (glyph); + } + void get_glyph_advances_for_direction (hb_direction_t direction, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); + else + get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); + } + + void guess_v_origin_minus_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = get_glyph_h_advance (glyph) / 2; + + hb_font_extents_t extents; + get_h_extents_with_fallback (&extents); + *y = extents.ascender; + } + + void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + if (!get_glyph_h_origin (glyph, x, y) && + get_glyph_v_origin (glyph, x, y)) + { + hb_position_t dx, dy; + guess_v_origin_minus_h_origin (glyph, &dx, &dy); + *x -= dx; *y -= dy; + } + } + void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + if (!get_glyph_v_origin (glyph, x, y) && + get_glyph_h_origin (glyph, x, y)) + { + hb_position_t dx, dy; + guess_v_origin_minus_h_origin (glyph, &dx, &dy); + *x += dx; *y += dy; + } + } + + void get_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_glyph_h_origin_with_fallback (glyph, x, y); + else + get_glyph_v_origin_with_fallback (glyph, x, y); + } + + void add_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + void add_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + void add_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + + void subtract_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + void subtract_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + void subtract_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + + void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { + *y = 0; + *x = get_glyph_h_kerning (first_glyph, second_glyph); + } else { + *x = 0; + *y = get_glyph_v_kerning (first_glyph, second_glyph); + } + } + + hb_bool_t get_glyph_extents_for_origin (hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents) + { + hb_bool_t ret = get_glyph_extents (glyph, extents); + + if (ret) + subtract_glyph_origin_for_direction (glyph, direction, &extents->x_bearing, &extents->y_bearing); + + return ret; + } + + hb_bool_t get_glyph_contour_point_for_origin (hb_codepoint_t glyph, unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_bool_t ret = get_glyph_contour_point (glyph, point_index, x, y); + + if (ret) + subtract_glyph_origin_for_direction (glyph, direction, x, y); + + return ret; + } + + /* Generates gidDDD if glyph has no name. */ + void + glyph_to_string (hb_codepoint_t glyph, + char *s, unsigned int size) + { + if (get_glyph_name (glyph, s, size)) return; + + if (size && snprintf (s, size, "gid%u", glyph) < 0) + *s = '\0'; + } + + /* Parses gidDDD and uniUUUU strings automatically. */ + hb_bool_t + glyph_from_string (const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) + { + if (get_glyph_from_name (s, len, glyph)) return true; + + if (len == -1) len = strlen (s); + + /* Straight glyph index. */ + if (hb_codepoint_parse (s, len, 10, glyph)) + return true; + + if (len > 3) + { + /* gidDDD syntax for glyph indices. */ + if (0 == strncmp (s, "gid", 3) && + hb_codepoint_parse (s + 3, len - 3, 10, glyph)) + return true; + + /* uniUUUU and other Unicode character indices. */ + hb_codepoint_t unichar; + if (0 == strncmp (s, "uni", 3) && + hb_codepoint_parse (s + 3, len - 3, 16, &unichar) && + get_nominal_glyph (unichar, glyph)) + return true; + } + + return false; + } + + void mults_changed () + { + float upem = face->get_upem (); + + x_multf = x_scale / upem; + y_multf = y_scale / upem; + bool x_neg = x_scale < 0; + x_mult = (x_neg ? -((int64_t) -x_scale << 16) : ((int64_t) x_scale << 16)) / upem; + bool y_neg = y_scale < 0; + y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem; + + x_strength = fabsf (roundf (x_scale * x_embolden)); + y_strength = fabsf (roundf (y_scale * y_embolden)); + + slant_xy = y_scale ? slant * x_scale / y_scale : 0.f; + + data.fini (); + } + + hb_position_t em_mult (int16_t v, int64_t mult) + { return (hb_position_t) ((v * mult + 32768) >> 16); } + hb_position_t em_multf (float v, float mult) + { return (hb_position_t) roundf (em_fmultf (v, mult)); } + float em_fmultf (float v, float mult) + { return v * mult; } + float em_fmult (int16_t v, float mult) + { return (float) v * mult; } +}; +DECLARE_NULL_INSTANCE (hb_font_t); + + +#endif /* HB_FONT_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ft-colr.hh b/src/3rdparty/harfbuzz-ng/src/hb-ft-colr.hh new file mode 100644 index 00000000000..fa5712f9b33 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ft-colr.hh @@ -0,0 +1,567 @@ +/* + * Copyright ยฉ 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_FT_COLR_HH +#define HB_FT_COLR_HH + +#include "hb.hh" + +#include "hb-paint-extents.hh" + +#include FT_COLOR_H + + +static hb_paint_composite_mode_t +_hb_ft_paint_composite_mode (FT_Composite_Mode mode) +{ + switch (mode) + { + case FT_COLR_COMPOSITE_CLEAR: return HB_PAINT_COMPOSITE_MODE_CLEAR; + case FT_COLR_COMPOSITE_SRC: return HB_PAINT_COMPOSITE_MODE_SRC; + case FT_COLR_COMPOSITE_DEST: return HB_PAINT_COMPOSITE_MODE_DEST; + case FT_COLR_COMPOSITE_SRC_OVER: return HB_PAINT_COMPOSITE_MODE_SRC_OVER; + case FT_COLR_COMPOSITE_DEST_OVER: return HB_PAINT_COMPOSITE_MODE_DEST_OVER; + case FT_COLR_COMPOSITE_SRC_IN: return HB_PAINT_COMPOSITE_MODE_SRC_IN; + case FT_COLR_COMPOSITE_DEST_IN: return HB_PAINT_COMPOSITE_MODE_DEST_IN; + case FT_COLR_COMPOSITE_SRC_OUT: return HB_PAINT_COMPOSITE_MODE_SRC_OUT; + case FT_COLR_COMPOSITE_DEST_OUT: return HB_PAINT_COMPOSITE_MODE_DEST_OUT; + case FT_COLR_COMPOSITE_SRC_ATOP: return HB_PAINT_COMPOSITE_MODE_SRC_ATOP; + case FT_COLR_COMPOSITE_DEST_ATOP: return HB_PAINT_COMPOSITE_MODE_DEST_ATOP; + case FT_COLR_COMPOSITE_XOR: return HB_PAINT_COMPOSITE_MODE_XOR; + case FT_COLR_COMPOSITE_PLUS: return HB_PAINT_COMPOSITE_MODE_PLUS; + case FT_COLR_COMPOSITE_SCREEN: return HB_PAINT_COMPOSITE_MODE_SCREEN; + case FT_COLR_COMPOSITE_OVERLAY: return HB_PAINT_COMPOSITE_MODE_OVERLAY; + case FT_COLR_COMPOSITE_DARKEN: return HB_PAINT_COMPOSITE_MODE_DARKEN; + case FT_COLR_COMPOSITE_LIGHTEN: return HB_PAINT_COMPOSITE_MODE_LIGHTEN; + case FT_COLR_COMPOSITE_COLOR_DODGE: return HB_PAINT_COMPOSITE_MODE_COLOR_DODGE; + case FT_COLR_COMPOSITE_COLOR_BURN: return HB_PAINT_COMPOSITE_MODE_COLOR_BURN; + case FT_COLR_COMPOSITE_HARD_LIGHT: return HB_PAINT_COMPOSITE_MODE_HARD_LIGHT; + case FT_COLR_COMPOSITE_SOFT_LIGHT: return HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT; + case FT_COLR_COMPOSITE_DIFFERENCE: return HB_PAINT_COMPOSITE_MODE_DIFFERENCE; + case FT_COLR_COMPOSITE_EXCLUSION: return HB_PAINT_COMPOSITE_MODE_EXCLUSION; + case FT_COLR_COMPOSITE_MULTIPLY: return HB_PAINT_COMPOSITE_MODE_MULTIPLY; + case FT_COLR_COMPOSITE_HSL_HUE: return HB_PAINT_COMPOSITE_MODE_HSL_HUE; + case FT_COLR_COMPOSITE_HSL_SATURATION: return HB_PAINT_COMPOSITE_MODE_HSL_SATURATION; + case FT_COLR_COMPOSITE_HSL_COLOR: return HB_PAINT_COMPOSITE_MODE_HSL_COLOR; + case FT_COLR_COMPOSITE_HSL_LUMINOSITY: return HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY; + + case FT_COLR_COMPOSITE_MAX: HB_FALLTHROUGH; + default: return HB_PAINT_COMPOSITE_MODE_CLEAR; + } +} + +typedef struct hb_ft_paint_context_t hb_ft_paint_context_t; + +static void +_hb_ft_paint (hb_ft_paint_context_t *c, + FT_OpaquePaint opaque_paint); + +struct hb_ft_paint_context_t +{ + hb_ft_paint_context_t (const hb_ft_font_t *ft_font, + hb_font_t *font, + hb_paint_funcs_t *paint_funcs, void *paint_data, + FT_Color *palette, + unsigned palette_index, + hb_color_t foreground) : + ft_font (ft_font), font(font), + funcs (paint_funcs), data (paint_data), + palette (palette), palette_index (palette_index), foreground (foreground) {} + + void recurse (FT_OpaquePaint paint) + { + if (unlikely (depth_left <= 0 || edge_count <= 0)) return; + depth_left--; + edge_count--; + _hb_ft_paint (this, paint); + depth_left++; + } + + const hb_ft_font_t *ft_font; + hb_font_t *font; + hb_paint_funcs_t *funcs; + void *data; + FT_Color *palette; + unsigned palette_index; + hb_color_t foreground; + int depth_left = HB_MAX_NESTING_LEVEL; + int edge_count = HB_COLRV1_MAX_EDGE_COUNT; +}; + +static unsigned +_hb_ft_color_line_get_color_stops (hb_color_line_t *color_line, + void *color_line_data, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops, + void *user_data) +{ + FT_ColorLine *cl = (FT_ColorLine *) color_line_data; + hb_ft_paint_context_t *c = (hb_ft_paint_context_t *) user_data; + + if (count) + { + FT_ColorStop stop; + unsigned wrote = 0; + FT_ColorStopIterator iter = cl->color_stop_iterator; + + if (start >= cl->color_stop_iterator.num_color_stops) + { + *count = 0; + return cl->color_stop_iterator.num_color_stops; + } + + while (cl->color_stop_iterator.current_color_stop < start) + FT_Get_Colorline_Stops(c->ft_font->ft_face, + &stop, + &cl->color_stop_iterator); + + while (count && *count && + FT_Get_Colorline_Stops(c->ft_font->ft_face, + &stop, + &cl->color_stop_iterator)) + { + // https://github.com/harfbuzz/harfbuzz/issues/4013 + if (sizeof stop.stop_offset == 2) + color_stops->offset = stop.stop_offset / 16384.f; + else + color_stops->offset = stop.stop_offset / 65536.f; + + color_stops->is_foreground = stop.color.palette_index == 0xFFFF; + if (color_stops->is_foreground) + color_stops->color = HB_COLOR (hb_color_get_blue (c->foreground), + hb_color_get_green (c->foreground), + hb_color_get_red (c->foreground), + (hb_color_get_alpha (c->foreground) * stop.color.alpha) >> 14); + else + { + hb_color_t color; + if (c->funcs->custom_palette_color (c->data, stop.color.palette_index, &color)) + { + color_stops->color = HB_COLOR (hb_color_get_blue (color), + hb_color_get_green (color), + hb_color_get_red (color), + (hb_color_get_alpha (color) * stop.color.alpha) >> 14); + } + else + { + FT_Color ft_color = c->palette[stop.color.palette_index]; + color_stops->color = HB_COLOR (ft_color.blue, + ft_color.green, + ft_color.red, + (ft_color.alpha * stop.color.alpha) >> 14); + } + } + + color_stops++; + wrote++; + } + + *count = wrote; + + // reset the iterator for next time + cl->color_stop_iterator = iter; + } + + return cl->color_stop_iterator.num_color_stops; +} + +static hb_paint_extend_t +_hb_ft_color_line_get_extend (hb_color_line_t *color_line, + void *color_line_data, + void *user_data) +{ + FT_ColorLine *c = (FT_ColorLine *) color_line_data; + switch (c->extend) + { + default: + case FT_COLR_PAINT_EXTEND_PAD: return HB_PAINT_EXTEND_PAD; + case FT_COLR_PAINT_EXTEND_REPEAT: return HB_PAINT_EXTEND_REPEAT; + case FT_COLR_PAINT_EXTEND_REFLECT: return HB_PAINT_EXTEND_REFLECT; + } +} + +void +_hb_ft_paint (hb_ft_paint_context_t *c, + FT_OpaquePaint opaque_paint) +{ + FT_Face ft_face = c->ft_font->ft_face; + FT_COLR_Paint paint; + if (!FT_Get_Paint (ft_face, opaque_paint, &paint)) + return; + + switch (paint.format) + { + case FT_COLR_PAINTFORMAT_COLR_LAYERS: + { + FT_OpaquePaint other_paint = {0}; + while (FT_Get_Paint_Layers (ft_face, + &paint.u.colr_layers.layer_iterator, + &other_paint)) + { + c->funcs->push_group (c->data); + c->recurse (other_paint); + c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER); + } + } + break; + case FT_COLR_PAINTFORMAT_SOLID: + { + bool is_foreground = paint.u.solid.color.palette_index == 0xFFFF; + hb_color_t color; + if (is_foreground) + color = HB_COLOR (hb_color_get_blue (c->foreground), + hb_color_get_green (c->foreground), + hb_color_get_red (c->foreground), + (hb_color_get_alpha (c->foreground) * paint.u.solid.color.alpha) >> 14); + else + { + if (c->funcs->custom_palette_color (c->data, paint.u.solid.color.palette_index, &color)) + { + color = HB_COLOR (hb_color_get_blue (color), + hb_color_get_green (color), + hb_color_get_red (color), + (hb_color_get_alpha (color) * paint.u.solid.color.alpha) >> 14); + } + else + { + FT_Color ft_color = c->palette[paint.u.solid.color.palette_index]; + color = HB_COLOR (ft_color.blue, + ft_color.green, + ft_color.red, + (ft_color.alpha * paint.u.solid.color.alpha) >> 14); + } + } + c->funcs->color (c->data, is_foreground, color); + } + break; + case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT: + { + hb_color_line_t cl = { + &paint.u.linear_gradient.colorline, + _hb_ft_color_line_get_color_stops, c, + _hb_ft_color_line_get_extend, nullptr + }; + + c->funcs->linear_gradient (c->data, &cl, + paint.u.linear_gradient.p0.x / 65536.f, + paint.u.linear_gradient.p0.y / 65536.f, + paint.u.linear_gradient.p1.x / 65536.f, + paint.u.linear_gradient.p1.y / 65536.f, + paint.u.linear_gradient.p2.x / 65536.f, + paint.u.linear_gradient.p2.y / 65536.f); + } + break; + case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT: + { + hb_color_line_t cl = { + &paint.u.linear_gradient.colorline, + _hb_ft_color_line_get_color_stops, c, + _hb_ft_color_line_get_extend, nullptr + }; + + c->funcs->radial_gradient (c->data, &cl, + paint.u.radial_gradient.c0.x / 65536.f, + paint.u.radial_gradient.c0.y / 65536.f, + paint.u.radial_gradient.r0 / 65536.f, + paint.u.radial_gradient.c1.x / 65536.f, + paint.u.radial_gradient.c1.y / 65536.f, + paint.u.radial_gradient.r1 / 65536.f); + } + break; + case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: + { + hb_color_line_t cl = { + &paint.u.linear_gradient.colorline, + _hb_ft_color_line_get_color_stops, c, + _hb_ft_color_line_get_extend, nullptr + }; + + c->funcs->sweep_gradient (c->data, &cl, + paint.u.sweep_gradient.center.x / 65536.f, + paint.u.sweep_gradient.center.y / 65536.f, + (paint.u.sweep_gradient.start_angle / 65536.f + 1) * HB_PI, + (paint.u.sweep_gradient.end_angle / 65536.f + 1) * HB_PI); + } + break; + case FT_COLR_PAINTFORMAT_GLYPH: + { + c->funcs->push_inverse_root_transform (c->data, c->font); + c->ft_font->lock.unlock (); + c->funcs->push_clip_glyph (c->data, paint.u.glyph.glyphID, c->font); + c->ft_font->lock.lock (); + c->funcs->push_root_transform (c->data, c->font); + c->recurse (paint.u.glyph.paint); + c->funcs->pop_transform (c->data); + c->funcs->pop_clip (c->data); + c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_COLR_GLYPH: + { + FT_OpaquePaint other_paint = {0}; + if (FT_Get_Color_Glyph_Paint (ft_face, paint.u.colr_glyph.glyphID, + FT_COLOR_NO_ROOT_TRANSFORM, + &other_paint)) + { + bool has_clip_box; + FT_ClipBox clip_box; + has_clip_box = FT_Get_Color_Glyph_ClipBox (ft_face, paint.u.colr_glyph.glyphID, &clip_box); + + if (has_clip_box) + { + /* The FreeType ClipBox is in scaled coordinates, whereas we need + * unscaled clipbox here. Oh well... + */ + + float upem = c->font->face->get_upem (); + float xscale = upem / (c->font->x_scale ? c->font->x_scale : upem); + float yscale = upem / (c->font->y_scale ? c->font->y_scale : upem); + + c->funcs->push_clip_rectangle (c->data, + clip_box.bottom_left.x * xscale, + clip_box.bottom_left.y * yscale, + clip_box.top_right.x * xscale, + clip_box.top_right.y * yscale); + } + + c->recurse (other_paint); + + if (has_clip_box) + c->funcs->pop_clip (c->data); + } + } + break; + case FT_COLR_PAINTFORMAT_TRANSFORM: + { + c->funcs->push_transform (c->data, + paint.u.transform.affine.xx / 65536.f, + paint.u.transform.affine.yx / 65536.f, + paint.u.transform.affine.xy / 65536.f, + paint.u.transform.affine.yy / 65536.f, + paint.u.transform.affine.dx / 65536.f, + paint.u.transform.affine.dy / 65536.f); + c->recurse (paint.u.transform.paint); + c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_TRANSLATE: + { + float dx = paint.u.translate.dx / 65536.f; + float dy = paint.u.translate.dy / 65536.f; + + bool p1 = c->funcs->push_translate (c->data, dx, dy); + c->recurse (paint.u.translate.paint); + if (p1) c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_SCALE: + { + float dx = paint.u.scale.center_x / 65536.f; + float dy = paint.u.scale.center_y / 65536.f; + float sx = paint.u.scale.scale_x / 65536.f; + float sy = paint.u.scale.scale_y / 65536.f; + + bool p1 = c->funcs->push_translate (c->data, +dx, +dy); + bool p2 = c->funcs->push_scale (c->data, sx, sy); + bool p3 = c->funcs->push_translate (c->data, -dx, -dy); + c->recurse (paint.u.scale.paint); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_ROTATE: + { + float dx = paint.u.rotate.center_x / 65536.f; + float dy = paint.u.rotate.center_y / 65536.f; + float a = paint.u.rotate.angle / 65536.f; + + bool p1 = c->funcs->push_translate (c->data, +dx, +dy); + bool p2 = c->funcs->push_rotate (c->data, a); + bool p3 = c->funcs->push_translate (c->data, -dx, -dy); + c->recurse (paint.u.rotate.paint); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_SKEW: + { + float dx = paint.u.skew.center_x / 65536.f; + float dy = paint.u.skew.center_y / 65536.f; + float sx = paint.u.skew.x_skew_angle / 65536.f; + float sy = paint.u.skew.y_skew_angle / 65536.f; + + bool p1 = c->funcs->push_translate (c->data, +dx, +dy); + bool p2 = c->funcs->push_skew (c->data, sx, sy); + bool p3 = c->funcs->push_translate (c->data, -dx, -dy); + c->recurse (paint.u.skew.paint); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_COMPOSITE: + { + c->recurse (paint.u.composite.backdrop_paint); + c->funcs->push_group (c->data); + c->recurse (paint.u.composite.source_paint); + c->funcs->pop_group (c->data, _hb_ft_paint_composite_mode (paint.u.composite.composite_mode)); + } + break; + + case FT_COLR_PAINT_FORMAT_MAX: break; + default: HB_FALLTHROUGH; + case FT_COLR_PAINTFORMAT_UNSUPPORTED: break; + } +} + + +static bool +hb_ft_paint_glyph_colr (hb_font_t *font, + void *font_data, + hb_codepoint_t gid, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + + /* Face is locked. */ + + FT_Error error; + FT_Color* palette; + FT_LayerIterator iterator; + + FT_Bool have_layers; + FT_UInt layer_glyph_index; + FT_UInt layer_color_index; + + error = FT_Palette_Select(ft_face, palette_index, &palette); + if (error) + palette = NULL; + + /* COLRv1 */ + FT_OpaquePaint paint = {0}; + if (FT_Get_Color_Glyph_Paint (ft_face, gid, + FT_COLOR_NO_ROOT_TRANSFORM, + &paint)) + { + hb_ft_paint_context_t c (ft_font, font, + paint_funcs, paint_data, + palette, palette_index, foreground); + + bool is_bounded = true; + FT_ClipBox clip_box; + if (FT_Get_Color_Glyph_ClipBox (ft_face, gid, &clip_box)) + { + c.funcs->push_clip_rectangle (c.data, + clip_box.bottom_left.x + + roundf (hb_min (font->slant_xy * clip_box.bottom_left.y, + font->slant_xy * clip_box.top_left.y)), + clip_box.bottom_left.y, + clip_box.top_right.x + + roundf (hb_max (font->slant_xy * clip_box.bottom_right.y, + font->slant_xy * clip_box.top_right.y)), + clip_box.top_right.y); + } + else + { + + auto *extents_funcs = hb_paint_extents_get_funcs (); + hb_paint_extents_context_t extents_data; + hb_ft_paint_context_t ce (ft_font, font, + extents_funcs, &extents_data, + palette, palette_index, foreground); + ce.funcs->push_root_transform (ce.data, font); + ce.recurse (paint); + ce.funcs->pop_transform (ce.data); + hb_extents_t extents = extents_data.get_extents (); + is_bounded = extents_data.is_bounded (); + + c.funcs->push_clip_rectangle (c.data, + extents.xmin, + extents.ymin, + extents.xmax, + extents.ymax); + } + + c.funcs->push_root_transform (c.data, font); + + if (is_bounded) + c.recurse (paint); + + c.funcs->pop_transform (c.data); + c.funcs->pop_clip (c.data); + + return true; + } + + /* COLRv0 */ + iterator.p = NULL; + have_layers = FT_Get_Color_Glyph_Layer(ft_face, + gid, + &layer_glyph_index, + &layer_color_index, + &iterator); + + if (palette && have_layers) + { + do + { + hb_bool_t is_foreground = true; + hb_color_t color = foreground; + + if ( layer_color_index != 0xFFFF ) + { + FT_Color layer_color = palette[layer_color_index]; + color = HB_COLOR (layer_color.blue, + layer_color.green, + layer_color.red, + layer_color.alpha); + is_foreground = false; + } + + ft_font->lock.unlock (); + paint_funcs->push_clip_glyph (paint_data, layer_glyph_index, font); + ft_font->lock.lock (); + paint_funcs->color (paint_data, is_foreground, color); + paint_funcs->pop_clip (paint_data); + + } while (FT_Get_Color_Glyph_Layer(ft_face, + gid, + &layer_glyph_index, + &layer_color_index, + &iterator)); + return true; + } + + return false; +} + + +#endif /* HB_FT_COLR_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ft.cc b/src/3rdparty/harfbuzz-ng/src/hb-ft.cc new file mode 100644 index 00000000000..1105862fbc9 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ft.cc @@ -0,0 +1,1500 @@ +/* + * Copyright ยฉ 2009 Red Hat, Inc. + * Copyright ยฉ 2009 Keith Stribley + * Copyright ยฉ 2015 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_FREETYPE + +#include "hb-ft.h" + +#include "hb-cache.hh" +#include "hb-draw.hh" +#include "hb-font.hh" +#include "hb-machinery.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-shaper-arabic-pua.hh" +#include "hb-paint.hh" + +#include FT_ADVANCES_H +#include FT_MULTIPLE_MASTERS_H +#include FT_OUTLINE_H +#include FT_TRUETYPE_TABLES_H +#include FT_SYNTHESIS_H +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300 +#include FT_COLOR_H +#endif + + +/** + * SECTION:hb-ft + * @title: hb-ft + * @short_description: FreeType integration + * @include: hb-ft.h + * + * Functions for using HarfBuzz with the FreeType library. + * + * HarfBuzz supports using FreeType to provide face and + * font data. + * + * Note that FreeType is not thread-safe, therefore these + * functions are not thread-safe either. + **/ + + +/* TODO: + * + * In general, this file does a fine job of what it's supposed to do. + * There are, however, things that need more work: + * + * - FreeType works in 26.6 mode. Clients can decide to use that mode, and everything + * would work fine. However, we also abuse this API for performing in font-space, + * but don't pass the correct flags to FreeType. We just abuse the no-hinting mode + * for that, such that no rounding etc happens. As such, we don't set ppem, and + * pass NO_HINTING as load_flags. Would be much better to use NO_SCALE, and scale + * ourselves. + * + * - We don't handle / allow for emboldening / obliqueing. + * + * - In the future, we should add constructors to create fonts in font space? + */ + + +using hb_ft_advance_cache_t = hb_cache_t<16, 24, 8, false>; + +struct hb_ft_font_t +{ + int load_flags; + bool symbol; /* Whether selected cmap is symbol cmap. */ + bool unref; /* Whether to destroy ft_face when done. */ + bool transform; /* Whether to apply FT_Face's transform. */ + + mutable hb_mutex_t lock; /* Protects members below. */ + FT_Face ft_face; + mutable unsigned cached_serial; + mutable hb_ft_advance_cache_t advance_cache; +}; + +static hb_ft_font_t * +_hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) +{ + hb_ft_font_t *ft_font = (hb_ft_font_t *) hb_calloc (1, sizeof (hb_ft_font_t)); + if (unlikely (!ft_font)) return nullptr; + + ft_font->lock.init (); + ft_font->ft_face = ft_face; + ft_font->symbol = symbol; + ft_font->unref = unref; + + ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; + + ft_font->cached_serial = (unsigned) -1; + ft_font->advance_cache.init (); + + return ft_font; +} + +static void +_hb_ft_face_destroy (void *data) +{ + FT_Done_Face ((FT_Face) data); +} + +static void +_hb_ft_font_destroy (void *data) +{ + hb_ft_font_t *ft_font = (hb_ft_font_t *) data; + + if (ft_font->unref) + _hb_ft_face_destroy (ft_font->ft_face); + + ft_font->lock.fini (); + + hb_free (ft_font); +} + + +/* hb_font changed, update FT_Face. */ +static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face) +{ + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + float x_mult = 1.f, y_mult = 1.f; + + if (font->x_scale < 0) x_mult = -x_mult; + if (font->y_scale < 0) y_mult = -y_mult; + + if (FT_Set_Char_Size (ft_face, + abs (font->x_scale), abs (font->y_scale), + 0, 0 +#if 0 + font->x_ppem * 72 * 64 / font->x_scale, + font->y_ppem * 72 * 64 / font->y_scale +#endif + ) && ft_face->num_fixed_sizes) + { +#ifdef HAVE_FT_GET_TRANSFORM + /* Bitmap font, eg. bitmap color emoji. */ + /* Pick largest size? */ + int x_scale = ft_face->available_sizes[ft_face->num_fixed_sizes - 1].x_ppem; + int y_scale = ft_face->available_sizes[ft_face->num_fixed_sizes - 1].y_ppem; + FT_Set_Char_Size (ft_face, + x_scale, y_scale, + 0, 0); + + /* This contains the sign that was previously in x_mult/y_mult. */ + x_mult = (float) font->x_scale / x_scale; + y_mult = (float) font->y_scale / y_scale; +#endif + } + else + { /* Shrug */ } + + + if (x_mult != 1.f || y_mult != 1.f) + { + FT_Matrix matrix = { (int) roundf (x_mult * (1<<16)), 0, + 0, (int) roundf (y_mult * (1<<16))}; + FT_Set_Transform (ft_face, &matrix, nullptr); + ft_font->transform = true; + } + +#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) + unsigned int num_coords; + const float *coords = hb_font_get_var_coords_design (font, &num_coords); + if (num_coords) + { + FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (num_coords, sizeof (FT_Fixed)); + if (ft_coords) + { + for (unsigned int i = 0; i < num_coords; i++) + ft_coords[i] = coords[i] * 65536.f; + FT_Set_Var_Design_Coordinates (ft_face, num_coords, ft_coords); + hb_free (ft_coords); + } + } +#endif +} + +/* Check if hb_font changed, update FT_Face. */ +static inline bool +_hb_ft_hb_font_check_changed (hb_font_t *font, + const hb_ft_font_t *ft_font) +{ + if (font->serial != ft_font->cached_serial) + { + _hb_ft_hb_font_changed (font, ft_font->ft_face); + ft_font->advance_cache.clear (); + ft_font->cached_serial = font->serial; + return true; + } + return false; +} + + +/** + * hb_ft_font_set_load_flags: + * @font: #hb_font_t to work upon + * @load_flags: The FreeType load flags to set + * + * Sets the FT_Load_Glyph load flags for the specified #hb_font_t. + * + * For more information, see + * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx + * + * This function works with #hb_font_t objects created by + * hb_ft_font_create() or hb_ft_font_create_referenced(). + * + * Since: 1.0.5 + **/ +void +hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) +{ + if (hb_object_is_immutable (font)) + return; + + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return; + + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + ft_font->load_flags = load_flags; +} + +/** + * hb_ft_font_get_load_flags: + * @font: #hb_font_t to work upon + * + * Fetches the FT_Load_Glyph load flags of the specified #hb_font_t. + * + * For more information, see + * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx + * + * This function works with #hb_font_t objects created by + * hb_ft_font_create() or hb_ft_font_create_referenced(). + * + * Return value: FT_Load_Glyph flags found, or 0 + * + * Since: 1.0.5 + **/ +int +hb_ft_font_get_load_flags (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return 0; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->load_flags; +} + +/** + * hb_ft_font_get_face: (skip) + * @font: #hb_font_t to work upon + * + * Fetches the FT_Face associated with the specified #hb_font_t + * font object. + * + * This function works with #hb_font_t objects created by + * hb_ft_font_create() or hb_ft_font_create_referenced(). + * + * Return value: (nullable): the FT_Face found or `NULL` + * + * Since: 0.9.2 + **/ +FT_Face +hb_ft_font_get_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return nullptr; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->ft_face; +} + +/** + * hb_ft_font_lock_face: (skip) + * @font: #hb_font_t to work upon + * + * Gets the FT_Face associated with @font. + * + * This face will be kept around and access to the FT_Face object + * from other HarfBuzz API wil be blocked until you call hb_ft_font_unlock_face(). + * + * This function works with #hb_font_t objects created by + * hb_ft_font_create() or hb_ft_font_create_referenced(). + * + * Return value: (nullable) (transfer none): the FT_Face associated with @font or `NULL` + * Since: 2.6.5 + **/ +FT_Face +hb_ft_font_lock_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return nullptr; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + ft_font->lock.lock (); + + return ft_font->ft_face; +} + +/** + * hb_ft_font_unlock_face: (skip) + * @font: #hb_font_t to work upon + * + * Releases an FT_Face previously obtained with hb_ft_font_lock_face(). + * + * Since: 2.6.5 + **/ +void +hb_ft_font_unlock_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + ft_font->lock.unlock (); +} + + +static hb_bool_t +hb_ft_get_nominal_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + unsigned int g = FT_Get_Char_Index (ft_font->ft_face, unicode); + + if (unlikely (!g)) + { + if (unlikely (ft_font->symbol)) + { + switch ((unsigned) font->face->table.OS2->get_font_page ()) { + case OT::OS2::font_page_t::FONT_PAGE_NONE: + if (unicode <= 0x00FFu) + /* For symbol-encoded OpenType fonts, we duplicate the + * U+F000..F0FF range at U+0000..U+00FF. That's what + * Windows seems to do, and that's hinted about at: + * https://docs.microsoft.com/en-us/typography/opentype/spec/recom + * under "Non-Standard (Symbol) Fonts". */ + g = FT_Get_Char_Index (ft_font->ft_face, 0xF000u + unicode); + break; +#ifndef HB_NO_OT_SHAPER_ARABIC_FALLBACK + case OT::OS2::font_page_t::FONT_PAGE_SIMP_ARABIC: + g = FT_Get_Char_Index (ft_font->ft_face, _hb_arabic_pua_simp_map (unicode)); + break; + case OT::OS2::font_page_t::FONT_PAGE_TRAD_ARABIC: + g = FT_Get_Char_Index (ft_font->ft_face, _hb_arabic_pua_trad_map (unicode)); + break; +#endif + default: + break; + } + if (!g) + return false; + } + else + return false; + } + + *glyph = g; + return true; +} + +static unsigned int +hb_ft_get_nominal_glyphs (hb_font_t *font HB_UNUSED, + void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + unsigned int done; + for (done = 0; + done < count && (*first_glyph = FT_Get_Char_Index (ft_font->ft_face, *first_unicode)); + done++) + { + first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + } + /* We don't need to do ft_font->symbol dance here, since HB calls the singular + * nominal_glyph() for what we don't handle here. */ + return done; +} + + +static hb_bool_t +hb_ft_get_variation_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + unsigned int g = FT_Face_GetCharVariantIndex (ft_font->ft_face, unicode, variation_selector); + + if (unlikely (!g)) + return false; + + *glyph = g; + return true; +} + +static void +hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, + unsigned count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_position_t *orig_first_advance = first_advance; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + int load_flags = ft_font->load_flags; + float x_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f; + x_mult *= font->x_scale < 0 ? -1 : +1; + } + else +#endif + { + x_mult = font->x_scale < 0 ? -1 : +1; + } + + for (unsigned int i = 0; i < count; i++) + { + FT_Fixed v = 0; + hb_codepoint_t glyph = *first_glyph; + + unsigned int cv; + if (ft_font->advance_cache.get (glyph, &cv)) + v = cv; + else + { + FT_Get_Advance (ft_face, glyph, load_flags, &v); + /* Work around bug that FreeType seems to return negative advance + * for variable-set fonts if x_scale is negative! */ + v = abs (v); + v = (int) (v * x_mult + (1<<9)) >> 10; + ft_font->advance_cache.set (glyph, v); + } + + *first_advance = v; + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + + if (font->x_strength && !font->embolden_in_place) + { + /* Emboldening. */ + hb_position_t x_strength = font->x_scale >= 0 ? font->x_strength : -font->x_strength; + first_advance = orig_first_advance; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? x_strength : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } +} + +#ifndef HB_NO_VERTICAL +static hb_position_t +hb_ft_get_glyph_v_advance (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Fixed v; + float y_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_font->ft_face, &matrix, nullptr); + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + y_mult *= font->y_scale < 0 ? -1 : +1; + } + else +#endif + { + y_mult = font->y_scale < 0 ? -1 : +1; + } + + if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v))) + return 0; + + v = (int) (y_mult * v); + + /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates + * have a Y growing upward. Hence the extra negation. */ + + hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength; + return ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength); +} +#endif + +#ifndef HB_NO_VERTICAL +static hb_bool_t +hb_ft_get_glyph_v_origin (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + float x_mult, y_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f; + x_mult *= font->x_scale < 0 ? -1 : +1; + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + y_mult *= font->y_scale < 0 ? -1 : +1; + } + else +#endif + { + x_mult = font->x_scale < 0 ? -1 : +1; + y_mult = font->y_scale < 0 ? -1 : +1; + } + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates + * have a Y growing upward. Hence the extra negation. */ + *x = ft_face->glyph->metrics.horiBearingX - ft_face->glyph->metrics.vertBearingX; + *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY); + + *x = (hb_position_t) (x_mult * *x); + *y = (hb_position_t) (y_mult * *y); + + return true; +} +#endif + +#ifndef HB_NO_OT_SHAPE_FALLBACK +static hb_position_t +hb_ft_get_glyph_h_kerning (hb_font_t *font, + void *font_data, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Vector kerningv; + + FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED; + if (FT_Get_Kerning (ft_font->ft_face, left_glyph, right_glyph, mode, &kerningv)) + return 0; + + return kerningv.x; +} +#endif + +static hb_bool_t +hb_ft_get_glyph_extents (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + float x_mult, y_mult; + float slant_xy = font->slant_xy; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f; + x_mult *= font->x_scale < 0 ? -1 : +1; + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + y_mult *= font->y_scale < 0 ? -1 : +1; + } + else +#endif + { + x_mult = font->x_scale < 0 ? -1 : +1; + y_mult = font->y_scale < 0 ? -1 : +1; + } + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + /* Copied from hb_font_t::scale_glyph_extents. */ + + float x1 = x_mult * ft_face->glyph->metrics.horiBearingX; + float y1 = y_mult * ft_face->glyph->metrics.horiBearingY; + float x2 = x1 + x_mult * ft_face->glyph->metrics.width; + float y2 = y1 + y_mult * -ft_face->glyph->metrics.height; + + /* Apply slant. */ + if (slant_xy) + { + x1 += hb_min (y1 * slant_xy, y2 * slant_xy); + x2 += hb_max (y1 * slant_xy, y2 * slant_xy); + } + + extents->x_bearing = floorf (x1); + extents->y_bearing = floorf (y1); + extents->width = ceilf (x2) - extents->x_bearing; + extents->height = ceilf (y2) - extents->y_bearing; + + if (font->x_strength || font->y_strength) + { + /* Y */ + int y_shift = font->y_strength; + if (font->y_scale < 0) y_shift = -y_shift; + extents->y_bearing += y_shift; + extents->height -= y_shift; + + /* X */ + int x_shift = font->x_strength; + if (font->x_scale < 0) x_shift = -x_shift; + if (font->embolden_in_place) + extents->x_bearing -= x_shift / 2; + extents->width += x_shift; + } + + return true; +} + +static hb_bool_t +hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)) + return false; + + if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points)) + return false; + + *x = ft_face->glyph->outline.points[point_index].x; + *y = ft_face->glyph->outline.points[point_index].y; + + return true; +} + +static hb_bool_t +hb_ft_get_glyph_name (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size); + if (ret && (size && !*name)) + ret = false; + + return ret; +} + +static hb_bool_t +hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED, + void *font_data, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + if (len < 0) + *glyph = FT_Get_Name_Index (ft_face, (FT_String *) name); + else { + /* Make a nul-terminated version. */ + char buf[128]; + len = hb_min (len, (int) sizeof (buf) - 1); + strncpy (buf, name, len); + buf[len] = '\0'; + *glyph = FT_Get_Name_Index (ft_face, buf); + } + + if (*glyph == 0) + { + /* Check whether the given name was actually the name of glyph 0. */ + char buf[128]; + if (!FT_Get_Glyph_Name(ft_face, 0, buf, sizeof (buf)) && + len < 0 ? !strcmp (buf, name) : !strncmp (buf, name, len)) + return true; + } + + return *glyph != 0; +} + +static hb_bool_t +hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, + void *font_data, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + float y_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + y_mult *= font->y_scale < 0 ? -1 : +1; + } + else +#endif + { + y_mult = font->y_scale < 0 ? -1 : +1; + } + + if (ft_face->units_per_EM != 0) + { + metrics->ascender = FT_MulFix(ft_face->ascender, ft_face->size->metrics.y_scale); + metrics->descender = FT_MulFix(ft_face->descender, ft_face->size->metrics.y_scale); + metrics->line_gap = FT_MulFix( ft_face->height, ft_face->size->metrics.y_scale ) - (metrics->ascender - metrics->descender); + } + else + { + /* Bitmap-only font, eg. color bitmap font. */ + metrics->ascender = ft_face->size->metrics.ascender; + metrics->descender = ft_face->size->metrics.descender; + metrics->line_gap = ft_face->size->metrics.height - (metrics->ascender - metrics->descender); + } + + metrics->ascender = (hb_position_t) (y_mult * (metrics->ascender + font->y_strength)); + metrics->descender = (hb_position_t) (y_mult * metrics->descender); + metrics->line_gap = (hb_position_t) (y_mult * metrics->line_gap); + + return true; +} + +#ifndef HB_NO_DRAW + +static int +_hb_ft_move_to (const FT_Vector *to, + void *arg) +{ + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; + drawing->move_to (to->x, to->y); + return FT_Err_Ok; +} + +static int +_hb_ft_line_to (const FT_Vector *to, + void *arg) +{ + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; + drawing->line_to (to->x, to->y); + return FT_Err_Ok; +} + +static int +_hb_ft_conic_to (const FT_Vector *control, + const FT_Vector *to, + void *arg) +{ + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; + drawing->quadratic_to (control->x, control->y, + to->x, to->y); + return FT_Err_Ok; +} + +static int +_hb_ft_cubic_to (const FT_Vector *control1, + const FT_Vector *control2, + const FT_Vector *to, + void *arg) +{ + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; + drawing->cubic_to (control1->x, control1->y, + control2->x, control2->y, + to->x, to->y); + return FT_Err_Ok; +} + +static void +hb_ft_draw_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, + FT_LOAD_NO_BITMAP | ft_font->load_flags))) + return; + + if (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) + return; + + const FT_Outline_Funcs outline_funcs = { + _hb_ft_move_to, + _hb_ft_line_to, + _hb_ft_conic_to, + _hb_ft_cubic_to, + 0, /* shift */ + 0, /* delta */ + }; + + hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy); + + /* Embolden */ + if (font->x_strength || font->y_strength) + { + FT_Outline_EmboldenXY (&ft_face->glyph->outline, font->x_strength, font->y_strength); + + int x_shift = 0; + int y_shift = 0; + if (font->embolden_in_place) + { + /* Undo the FreeType shift. */ + x_shift = -font->x_strength / 2; + y_shift = 0; + if (font->y_scale < 0) y_shift = -font->y_strength; + } + else + { + /* FreeType applied things in the wrong direction for negative scale; fix up. */ + if (font->x_scale < 0) x_shift = -font->x_strength; + if (font->y_scale < 0) y_shift = -font->y_strength; + } + if (x_shift || y_shift) + { + auto &outline = ft_face->glyph->outline; + for (auto &point : hb_iter (outline.points, outline.contours[outline.n_contours - 1] + 1)) + { + point.x += x_shift; + point.y += y_shift; + } + } + } + + + FT_Outline_Decompose (&ft_face->glyph->outline, + &outline_funcs, + &draw_session); +} +#endif + +#ifndef HB_NO_PAINT +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300 + +#include "hb-ft-colr.hh" + +static void +hb_ft_paint_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t gid, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + /* We release the lock before calling into glyph callbacks, such that + * eg. draw API can call back into the face.*/ + + if (unlikely (FT_Load_Glyph (ft_face, gid, + ft_font->load_flags | FT_LOAD_COLOR))) + return; + + if (ft_face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) + { + if (hb_ft_paint_glyph_colr (font, font_data, gid, + paint_funcs, paint_data, + palette_index, foreground, + user_data)) + return; + + /* Simple outline. */ + ft_font->lock.unlock (); + paint_funcs->push_clip_glyph (paint_data, gid, font); + ft_font->lock.lock (); + paint_funcs->color (paint_data, true, foreground); + paint_funcs->pop_clip (paint_data); + + return; + } + + auto *glyph = ft_face->glyph; + if (glyph->format == FT_GLYPH_FORMAT_BITMAP) + { + auto &bitmap = glyph->bitmap; + if (bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) + { + if (bitmap.pitch != (signed) bitmap.width * 4) + return; + + ft_font->lock.unlock (); + + hb_blob_t *blob = hb_blob_create ((const char *) bitmap.buffer, + bitmap.pitch * bitmap.rows, + HB_MEMORY_MODE_DUPLICATE, + nullptr, nullptr); + + hb_glyph_extents_t extents; + if (!hb_font_get_glyph_extents (font, gid, &extents)) + goto out; + + if (!paint_funcs->image (paint_data, + blob, + bitmap.width, + bitmap.rows, + HB_PAINT_IMAGE_FORMAT_BGRA, + font->slant_xy, + &extents)) + { + /* TODO Try a forced outline load and paint? */ + } + + out: + hb_blob_destroy (blob); + ft_font->lock.lock (); + } + + return; + } +} +#endif +#endif + + +static inline void free_static_ft_funcs (); + +static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t +{ + static hb_font_funcs_t *create () + { + hb_font_funcs_t *funcs = hb_font_funcs_create (); + + hb_font_funcs_set_nominal_glyph_func (funcs, hb_ft_get_nominal_glyph, nullptr, nullptr); + hb_font_funcs_set_nominal_glyphs_func (funcs, hb_ft_get_nominal_glyphs, nullptr, nullptr); + hb_font_funcs_set_variation_glyph_func (funcs, hb_ft_get_variation_glyph, nullptr, nullptr); + + hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ft_get_glyph_h_advances, nullptr, nullptr); + //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ft_get_glyph_h_origin, nullptr, nullptr); + +#ifndef HB_NO_VERTICAL + //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, nullptr, nullptr); + hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, nullptr, nullptr); +#endif + +#ifndef HB_NO_OT_SHAPE_FALLBACK + hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ft_get_glyph_h_kerning, nullptr, nullptr); +#endif + //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ft_get_glyph_v_kerning, nullptr, nullptr); + hb_font_funcs_set_glyph_extents_func (funcs, hb_ft_get_glyph_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ft_get_glyph_contour_point, nullptr, nullptr); + hb_font_funcs_set_glyph_name_func (funcs, hb_ft_get_glyph_name, nullptr, nullptr); + hb_font_funcs_set_glyph_from_name_func (funcs, hb_ft_get_glyph_from_name, nullptr, nullptr); + +#ifndef HB_NO_DRAW + hb_font_funcs_set_draw_glyph_func (funcs, hb_ft_draw_glyph, nullptr, nullptr); +#endif + +#ifndef HB_NO_PAINT +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300 + hb_font_funcs_set_paint_glyph_func (funcs, hb_ft_paint_glyph, nullptr, nullptr); +#endif +#endif + + hb_font_funcs_make_immutable (funcs); + + hb_atexit (free_static_ft_funcs); + + return funcs; + } +} static_ft_funcs; + +static inline +void free_static_ft_funcs () +{ + static_ft_funcs.free_instance (); +} + +static hb_font_funcs_t * +_hb_ft_get_font_funcs () +{ + return static_ft_funcs.get_unconst (); +} + +static void +_hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref) +{ + bool symbol = ft_face->charmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL; + + hb_ft_font_t *ft_font = _hb_ft_font_create (ft_face, symbol, unref); + if (unlikely (!ft_font)) return; + + hb_font_set_funcs (font, + _hb_ft_get_font_funcs (), + ft_font, + _hb_ft_font_destroy); +} + + +static hb_blob_t * +_hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + FT_Face ft_face = (FT_Face) user_data; + FT_Byte *buffer; + FT_ULong length = 0; + FT_Error error; + + /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */ + + error = FT_Load_Sfnt_Table (ft_face, tag, 0, nullptr, &length); + if (error) + return nullptr; + + buffer = (FT_Byte *) hb_malloc (length); + if (!buffer) + return nullptr; + + error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length); + if (error) + { + hb_free (buffer); + return nullptr; + } + + return hb_blob_create ((const char *) buffer, length, + HB_MEMORY_MODE_WRITABLE, + buffer, hb_free); +} + +/** + * hb_ft_face_create: + * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon + * @destroy: (nullable): A callback to call when the face object is not needed anymore + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * Note that this is using the FT_Face object just to get at the underlying + * font data, and fonts created from the returned #hb_face_t will use the native + * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them. + * + * This variant of the function does not provide any life-cycle management. + * + * Most client programs should use hb_ft_face_create_referenced() + * (or, perhaps, hb_ft_face_create_cached()) instead. + * + * If you know you have valid reasons not to use hb_ft_face_create_referenced(), + * then it is the client program's responsibility to destroy @ft_face + * after the #hb_face_t face object has been destroyed. + * + * Return value: (transfer full): the new #hb_face_t face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_ft_face_create (FT_Face ft_face, + hb_destroy_func_t destroy) +{ + hb_face_t *face; + + if (!ft_face->stream->read) { + hb_blob_t *blob; + + blob = hb_blob_create ((const char *) ft_face->stream->base, + (unsigned int) ft_face->stream->size, + HB_MEMORY_MODE_READONLY, + ft_face, destroy); + face = hb_face_create (blob, ft_face->face_index); + hb_blob_destroy (blob); + } else { + face = hb_face_create_for_tables (_hb_ft_reference_table, ft_face, destroy); + } + + hb_face_set_index (face, ft_face->face_index); + hb_face_set_upem (face, ft_face->units_per_EM); + + return face; +} + +/** + * hb_ft_face_create_referenced: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * Note that this is using the FT_Face object just to get at the underlying + * font data, and fonts created from the returned #hb_face_t will use the native + * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them. + * + * This is the preferred variant of the hb_ft_face_create* + * function family, because it calls FT_Reference_Face() on @ft_face, + * ensuring that @ft_face remains alive as long as the resulting + * #hb_face_t face object remains alive. Also calls FT_Done_Face() + * when the #hb_face_t face object is destroyed. + * + * Use this version unless you know you have good reasons not to. + * + * Return value: (transfer full): the new #hb_face_t face object + * + * Since: 0.9.38 + **/ +hb_face_t * +hb_ft_face_create_referenced (FT_Face ft_face) +{ + FT_Reference_Face (ft_face); + return hb_ft_face_create (ft_face, _hb_ft_face_destroy); +} + +static void +hb_ft_face_finalize (void *arg) +{ + FT_Face ft_face = (FT_Face) arg; + hb_face_destroy ((hb_face_t *) ft_face->generic.data); +} + +/** + * hb_ft_face_create_cached: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * Note that this is using the FT_Face object just to get at the underlying + * font data, and fonts created from the returned #hb_face_t will use the native + * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them. + * + * This variant of the function caches the newly created #hb_face_t + * face object, using the @generic pointer of @ft_face. Subsequent function + * calls that are passed the same @ft_face parameter will have the same + * #hb_face_t returned to them, and that #hb_face_t will be correctly + * reference counted. + * + * However, client programs are still responsible for destroying + * @ft_face after the last #hb_face_t face object has been destroyed. + * + * Return value: (transfer full): the new #hb_face_t face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_ft_face_create_cached (FT_Face ft_face) +{ + if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize)) + { + if (ft_face->generic.finalizer) + ft_face->generic.finalizer (ft_face); + + ft_face->generic.data = hb_ft_face_create (ft_face, nullptr); + ft_face->generic.finalizer = hb_ft_face_finalize; + } + + return hb_face_reference ((hb_face_t *) ft_face->generic.data); +} + +/** + * hb_ft_font_create: + * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon + * @destroy: (nullable): A callback to call when the font object is not needed anymore + * + * Creates an #hb_font_t font object from the specified FT_Face. + * + * Note: You must set the face size on @ft_face before calling + * hb_ft_font_create() on it. HarfBuzz assumes size is always set and will + * access `size` member of FT_Face unconditionally. + * + * This variant of the function does not provide any life-cycle management. + * + * Most client programs should use hb_ft_font_create_referenced() + * instead. + * + * If you know you have valid reasons not to use hb_ft_font_create_referenced(), + * then it is the client program's responsibility to destroy @ft_face + * after the #hb_font_t font object has been destroyed. + * + * HarfBuzz will use the @destroy callback on the #hb_font_t font object + * if it is supplied when you use this function. However, even if @destroy + * is provided, it is the client program's responsibility to destroy @ft_face, + * and it is the client program's responsibility to ensure that @ft_face is + * destroyed only after the #hb_font_t font object has been destroyed. + * + * Return value: (transfer full): the new #hb_font_t font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_ft_font_create (FT_Face ft_face, + hb_destroy_func_t destroy) +{ + hb_font_t *font; + hb_face_t *face; + + face = hb_ft_face_create (ft_face, destroy); + font = hb_font_create (face); + hb_face_destroy (face); + _hb_ft_font_set_funcs (font, ft_face, false); + hb_ft_font_changed (font); + return font; +} + +/** + * hb_ft_font_changed: + * @font: #hb_font_t to work upon + * + * Refreshes the state of @font when the underlying FT_Face has changed. + * This function should be called after changing the size or + * variation-axis settings on the FT_Face. + * + * Since: 1.0.5 + **/ +void +hb_ft_font_changed (hb_font_t *font) +{ + if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + return; + + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + FT_Face ft_face = ft_font->ft_face; + + hb_font_set_scale (font, + (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16), + (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16)); +#if 0 /* hb-ft works in no-hinting model */ + hb_font_set_ppem (font, + ft_face->size->metrics.x_ppem, + ft_face->size->metrics.y_ppem); +#endif + +#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) + FT_MM_Var *mm_var = nullptr; + if (!FT_Get_MM_Var (ft_face, &mm_var)) + { + FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (mm_var->num_axis, sizeof (FT_Fixed)); + int *coords = (int *) hb_calloc (mm_var->num_axis, sizeof (int)); + if (coords && ft_coords) + { + if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, ft_coords)) + { + bool nonzero = false; + + for (unsigned int i = 0; i < mm_var->num_axis; ++i) + { + coords[i] = ft_coords[i] >>= 2; + nonzero = nonzero || coords[i]; + } + + if (nonzero) + hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis); + else + hb_font_set_var_coords_normalized (font, nullptr, 0); + } + } + hb_free (coords); + hb_free (ft_coords); +#ifdef HAVE_FT_DONE_MM_VAR + FT_Done_MM_Var (ft_face->glyph->library, mm_var); +#else + hb_free (mm_var); +#endif + } +#endif + + ft_font->advance_cache.clear (); + ft_font->cached_serial = font->serial; +} + +/** + * hb_ft_hb_font_changed: + * @font: #hb_font_t to work upon + * + * Refreshes the state of the underlying FT_Face of @font when the hb_font_t + * @font has changed. + * This function should be called after changing the size or + * variation-axis settings on the @font. + * This call is fast if nothing has changed on @font. + * + * Return value: true if changed, false otherwise + * + * Since: 4.4.0 + **/ +hb_bool_t +hb_ft_hb_font_changed (hb_font_t *font) +{ + if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + return false; + + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + return _hb_ft_hb_font_check_changed (font, ft_font); +} + +/** + * hb_ft_font_create_referenced: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_font_t font object from the specified FT_Face. + * + * Note: You must set the face size on @ft_face before calling + * hb_ft_font_create_referenced() on it. HarfBuzz assumes size is always set + * and will access `size` member of FT_Face unconditionally. + * + * This is the preferred variant of the hb_ft_font_create* + * function family, because it calls FT_Reference_Face() on @ft_face, + * ensuring that @ft_face remains alive as long as the resulting + * #hb_font_t font object remains alive. + * + * Use this version unless you know you have good reasons not to. + * + * Return value: (transfer full): the new #hb_font_t font object + * + * Since: 0.9.38 + **/ +hb_font_t * +hb_ft_font_create_referenced (FT_Face ft_face) +{ + FT_Reference_Face (ft_face); + return hb_ft_font_create (ft_face, _hb_ft_face_destroy); +} + +static inline void free_static_ft_library (); + +static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t, + hb_ft_library_lazy_loader_t> +{ + static FT_Library create () + { + FT_Library l; + if (FT_Init_FreeType (&l)) + return nullptr; + + hb_atexit (free_static_ft_library); + + return l; + } + static void destroy (FT_Library l) + { + FT_Done_FreeType (l); + } + static FT_Library get_null () + { + return nullptr; + } +} static_ft_library; + +static inline +void free_static_ft_library () +{ + static_ft_library.free_instance (); +} + +static FT_Library +get_ft_library () +{ + return static_ft_library.get_unconst (); +} + +static void +_release_blob (void *arg) +{ + FT_Face ft_face = (FT_Face) arg; + hb_blob_destroy ((hb_blob_t *) ft_face->generic.data); +} + +/** + * hb_ft_font_set_funcs: + * @font: #hb_font_t to work upon + * + * Configures the font-functions structure of the specified + * #hb_font_t font object to use FreeType font functions. + * + * In particular, you can use this function to configure an + * existing #hb_face_t face object for use with FreeType font + * functions even if that #hb_face_t face object was initially + * created with hb_face_create(), and therefore was not + * initially configured to use FreeType font functions. + * + * An #hb_font_t object created with hb_ft_font_create() + * is preconfigured for FreeType font functions and does not + * require this function to be used. + * + * Note that if you modify the underlying #hb_font_t after + * calling this function, you need to call hb_ft_hb_font_changed() + * to update the underlying FT_Face. + * + * Note: Internally, this function creates an FT_Face. +* + * + * Since: 1.0.5 + **/ +void +hb_ft_font_set_funcs (hb_font_t *font) +{ + hb_blob_t *blob = hb_face_reference_blob (font->face); + unsigned int blob_length; + const char *blob_data = hb_blob_get_data (blob, &blob_length); + if (unlikely (!blob_length)) + DEBUG_MSG (FT, font, "Font face has empty blob"); + + FT_Face ft_face = nullptr; + FT_Error err = FT_New_Memory_Face (get_ft_library (), + (const FT_Byte *) blob_data, + blob_length, + hb_face_get_index (font->face), + &ft_face); + + if (unlikely (err)) { + hb_blob_destroy (blob); + DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed"); + return; + } + + if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL)) + FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); + + + ft_face->generic.data = blob; + ft_face->generic.finalizer = _release_blob; + + _hb_ft_font_set_funcs (font, ft_face, true); + hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); + + _hb_ft_hb_font_changed (font, ft_face); +} + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ft.h b/src/3rdparty/harfbuzz-ng/src/hb-ft.h new file mode 100644 index 00000000000..6a8a7abe8cc --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ft.h @@ -0,0 +1,145 @@ +/* + * Copyright ยฉ 2009 Red Hat, Inc. + * Copyright ยฉ 2015 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FT_H +#define HB_FT_H + +#include "hb.h" + +#include +#include FT_FREETYPE_H + +HB_BEGIN_DECLS + +/* + * Note: FreeType is not thread-safe. + * Hence, these functions are not either. + */ + +/* + * hb-face from ft-face. + */ + +/* This one creates a new hb-face for given ft-face. + * When the returned hb-face is destroyed, the destroy + * callback is called (if not NULL), with the ft-face passed + * to it. + * + * The client is responsible to make sure that ft-face is + * destroyed after hb-face is destroyed. + * + * Most often you don't want this function. You should use either + * hb_ft_face_create_cached(), or hb_ft_face_create_referenced(). + * In particular, if you are going to pass NULL as destroy, you + * probably should use (the more recent) hb_ft_face_create_referenced() + * instead. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create (FT_Face ft_face, + hb_destroy_func_t destroy); + +/* This version is like hb_ft_face_create(), except that it caches + * the hb-face using the generic pointer of the ft-face. This means + * that subsequent calls to this function with the same ft-face will + * return the same hb-face (correctly referenced). + * + * Client is still responsible for making sure that ft-face is destroyed + * after hb-face is. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create_cached (FT_Face ft_face); + +/* This version is like hb_ft_face_create(), except that it calls + * FT_Reference_Face() on ft-face, as such keeping ft-face alive + * as long as the hb-face is. + * + * This is the most convenient version to use. Use it unless you have + * very good reasons not to. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create_referenced (FT_Face ft_face); + + +/* + * hb-font from ft-face. + */ + +/* + * Note: + * + * Set face size on ft-face before creating hb-font from it. + * Otherwise hb-ft would NOT pick up the font size correctly. + */ + +/* See notes on hb_ft_face_create(). Same issues re lifecycle-management + * apply here. Use hb_ft_font_create_referenced() if you can. */ +HB_EXTERN hb_font_t * +hb_ft_font_create (FT_Face ft_face, + hb_destroy_func_t destroy); + +/* See notes on hb_ft_face_create_referenced() re lifecycle-management + * issues. */ +HB_EXTERN hb_font_t * +hb_ft_font_create_referenced (FT_Face ft_face); + +HB_EXTERN FT_Face +hb_ft_font_get_face (hb_font_t *font); + +HB_EXTERN FT_Face +hb_ft_font_lock_face (hb_font_t *font); + +HB_EXTERN void +hb_ft_font_unlock_face (hb_font_t *font); + +HB_EXTERN void +hb_ft_font_set_load_flags (hb_font_t *font, int load_flags); + +HB_EXTERN int +hb_ft_font_get_load_flags (hb_font_t *font); + +/* Call when size or variations settings on underlying FT_Face changed, + * and you want to update the hb_font_t from it. */ +HB_EXTERN void +hb_ft_font_changed (hb_font_t *font); + +/* Call when size or variations settings on underlying hb_font_t may have + * changed, and you want to update the FT_Face from it. This call is fast + * if nothing changed on hb_font_t. Returns true if changed. */ +HB_EXTERN hb_bool_t +hb_ft_hb_font_changed (hb_font_t *font); + +/* Makes an hb_font_t use FreeType internally to implement font functions. + * Note: this internally creates an FT_Face. Use it when you create your + * hb_face_t using hb_face_create(). */ +HB_EXTERN void +hb_ft_font_set_funcs (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_FT_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-gdi.cc b/src/3rdparty/harfbuzz-ng/src/hb-gdi.cc new file mode 100644 index 00000000000..8e7589beacb --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-gdi.cc @@ -0,0 +1,85 @@ +/* + * Copyright ยฉ 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifdef HAVE_GDI + +#include "hb-gdi.h" + + +/** + * SECTION:hb-gdi + * @title: hb-gdi + * @short_description: GDI integration + * @include: hb-gdi.h + * + * Functions for using HarfBuzz with GDI fonts. + **/ + +static hb_blob_t * +_hb_gdi_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + char *buffer = nullptr; + DWORD length = 0; + + HDC hdc = GetDC (nullptr); + if (unlikely (!SelectObject (hdc, (HFONT) user_data))) goto fail; + + length = GetFontData (hdc, hb_uint32_swap (tag), 0, buffer, length); + if (unlikely (length == GDI_ERROR)) goto fail_with_releasedc; + + buffer = (char *) hb_malloc (length); + if (unlikely (!buffer)) goto fail_with_releasedc; + length = GetFontData (hdc, hb_uint32_swap (tag), 0, buffer, length); + if (unlikely (length == GDI_ERROR)) goto fail_with_releasedc_and_free; + ReleaseDC (nullptr, hdc); + + return hb_blob_create ((const char *) buffer, length, HB_MEMORY_MODE_WRITABLE, buffer, hb_free); + +fail_with_releasedc_and_free: + hb_free (buffer); +fail_with_releasedc: + ReleaseDC (nullptr, hdc); +fail: + return hb_blob_get_empty (); +} + +/** + * hb_gdi_face_create: + * @hfont: a HFONT object. + * + * Constructs a new face object from the specified GDI HFONT. + * + * Return value: #hb_face_t object corresponding to the given input + * + * Since: 2.6.0 + **/ +hb_face_t * +hb_gdi_face_create (HFONT hfont) +{ + return hb_face_create_for_tables (_hb_gdi_reference_table, (void *) hfont, nullptr); +} + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-gdi.h b/src/3rdparty/harfbuzz-ng/src/hb-gdi.h new file mode 100644 index 00000000000..68cc43917e0 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-gdi.h @@ -0,0 +1,39 @@ +/* + * Copyright ยฉ 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_GDI_H +#define HB_GDI_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + +HB_EXTERN hb_face_t * +hb_gdi_face_create (HFONT hfont); + +HB_END_DECLS + +#endif /* HB_GDI_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-glib.cc b/src/3rdparty/harfbuzz-ng/src/hb-glib.cc new file mode 100644 index 00000000000..1da81696e75 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-glib.cc @@ -0,0 +1,232 @@ +/* + * Copyright ยฉ 2009 Red Hat, Inc. + * Copyright ยฉ 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_GLIB + +#include "hb-glib.h" + +#include "hb-machinery.hh" + + +/** + * SECTION:hb-glib + * @title: hb-glib + * @short_description: GLib integration + * @include: hb-glib.h + * + * Functions for using HarfBuzz with the GLib library. + * + * HarfBuzz supports using GLib to provide Unicode data, by attaching + * GLib functions to the virtual methods in a #hb_unicode_funcs_t function + * structure. + **/ + + +/** + * hb_glib_script_to_script: + * @script: The GUnicodeScript identifier to query + * + * Fetches the #hb_script_t script that corresponds to the + * specified GUnicodeScript identifier. + * + * Return value: the #hb_script_t script found + * + * Since: 0.9.38 + **/ +hb_script_t +hb_glib_script_to_script (GUnicodeScript script) +{ + return (hb_script_t) g_unicode_script_to_iso15924 (script); +} + +/** + * hb_glib_script_from_script: + * @script: The #hb_script_t to query + * + * Fetches the GUnicodeScript identifier that corresponds to the + * specified #hb_script_t script. + * + * Return value: the GUnicodeScript identifier found + * + * Since: 0.9.38 + **/ +GUnicodeScript +hb_glib_script_from_script (hb_script_t script) +{ + return g_unicode_script_from_iso15924 (script); +} + + +static hb_unicode_combining_class_t +hb_glib_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + return (hb_unicode_combining_class_t) g_unichar_combining_class (unicode); +} + +static hb_unicode_general_category_t +hb_glib_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + /* hb_unicode_general_category_t and GUnicodeType are identical */ + return (hb_unicode_general_category_t) g_unichar_type (unicode); +} + +static hb_codepoint_t +hb_glib_unicode_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + g_unichar_get_mirror_char (unicode, &unicode); + return unicode; +} + +static hb_script_t +hb_glib_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return hb_glib_script_to_script (g_unichar_get_script (unicode)); +} + +static hb_bool_t +hb_glib_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab, + void *user_data HB_UNUSED) +{ +#if GLIB_CHECK_VERSION(2,29,12) + return g_unichar_compose (a, b, ab); +#else + return false; +#endif +} + +static hb_bool_t +hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b, + void *user_data HB_UNUSED) +{ +#if GLIB_CHECK_VERSION(2,29,12) + return g_unichar_decompose (ab, a, b); +#else + return false; +#endif +} + + +static inline void free_static_glib_funcs (); + +static struct hb_glib_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t +{ + static hb_unicode_funcs_t *create () + { + hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); + + hb_unicode_funcs_set_combining_class_func (funcs, hb_glib_unicode_combining_class, nullptr, nullptr); + hb_unicode_funcs_set_general_category_func (funcs, hb_glib_unicode_general_category, nullptr, nullptr); + hb_unicode_funcs_set_mirroring_func (funcs, hb_glib_unicode_mirroring, nullptr, nullptr); + hb_unicode_funcs_set_script_func (funcs, hb_glib_unicode_script, nullptr, nullptr); + hb_unicode_funcs_set_compose_func (funcs, hb_glib_unicode_compose, nullptr, nullptr); + hb_unicode_funcs_set_decompose_func (funcs, hb_glib_unicode_decompose, nullptr, nullptr); + + hb_unicode_funcs_make_immutable (funcs); + + hb_atexit (free_static_glib_funcs); + + return funcs; + } +} static_glib_funcs; + +static inline +void free_static_glib_funcs () +{ + static_glib_funcs.free_instance (); +} + +/** + * hb_glib_get_unicode_funcs: + * + * Fetches a Unicode-functions structure that is populated + * with the appropriate GLib function for each method. + * + * Return value: (transfer none): a pointer to the #hb_unicode_funcs_t Unicode-functions structure + * + * Since: 0.9.38 + **/ +hb_unicode_funcs_t * +hb_glib_get_unicode_funcs () +{ + return static_glib_funcs.get_unconst (); +} + + + +#if GLIB_CHECK_VERSION(2,31,10) + +static void +_hb_g_bytes_unref (void *data) +{ + g_bytes_unref ((GBytes *) data); +} + +/** + * hb_glib_blob_create: + * @gbytes: the GBytes structure to work upon + * + * Creates an #hb_blob_t blob from the specified + * GBytes data structure. + * + * Return value: (transfer full): the new #hb_blob_t blob object + * + * Since: 0.9.38 + **/ +hb_blob_t * +hb_glib_blob_create (GBytes *gbytes) +{ + gsize size = 0; + gconstpointer data = g_bytes_get_data (gbytes, &size); + return hb_blob_create ((const char *) data, + size, + HB_MEMORY_MODE_READONLY, + g_bytes_ref (gbytes), + _hb_g_bytes_unref); +} +#endif + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-tag.h b/src/3rdparty/harfbuzz-ng/src/hb-glib.h similarity index 69% rename from src/3rdparty/harfbuzz-ng/src/hb-ot-tag.h rename to src/3rdparty/harfbuzz-ng/src/hb-glib.h index 54fb747f582..5f04183ba19 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-tag.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-glib.h @@ -1,5 +1,6 @@ /* * Copyright ยฉ 2009 Red Hat, Inc. + * Copyright ยฉ 2011 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -22,38 +23,34 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod */ -#ifndef HB_OT_H_IN -#error "Include instead." -#endif - -#ifndef HB_OT_TAG_H -#define HB_OT_TAG_H +#ifndef HB_GLIB_H +#define HB_GLIB_H #include "hb.h" +#include + HB_BEGIN_DECLS -#define HB_OT_TAG_DEFAULT_SCRIPT HB_TAG ('D', 'F', 'L', 'T') -#define HB_OT_TAG_DEFAULT_LANGUAGE HB_TAG ('d', 'f', 'l', 't') - -HB_EXTERN void -hb_ot_tags_from_script (hb_script_t script, - hb_tag_t *script_tag_1, - hb_tag_t *script_tag_2); - HB_EXTERN hb_script_t -hb_ot_tag_to_script (hb_tag_t tag); +hb_glib_script_to_script (GUnicodeScript script); -HB_EXTERN hb_tag_t -hb_ot_tag_from_language (hb_language_t language); +HB_EXTERN GUnicodeScript +hb_glib_script_from_script (hb_script_t script); -HB_EXTERN hb_language_t -hb_ot_tag_to_language (hb_tag_t tag); +HB_EXTERN hb_unicode_funcs_t * +hb_glib_get_unicode_funcs (void); + +#if GLIB_CHECK_VERSION(2,31,10) +HB_EXTERN hb_blob_t * +hb_glib_blob_create (GBytes *gbytes); +#endif HB_END_DECLS -#endif /* HB_OT_TAG_H */ +#endif /* HB_GLIB_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-shape-plan-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-gobject-enums.cc.tmpl similarity index 50% rename from src/3rdparty/harfbuzz-ng/src/hb-shape-plan-private.hh rename to src/3rdparty/harfbuzz-ng/src/hb-gobject-enums.cc.tmpl index aa0413a272d..87a11dd407e 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-shape-plan-private.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-gobject-enums.cc.tmpl @@ -1,5 +1,6 @@ +/*** BEGIN file-header ***/ /* - * Copyright ยฉ 2012 Google, Inc. + * Copyright (C) 2011 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -24,44 +25,56 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_SHAPE_PLAN_PRIVATE_HH -#define HB_SHAPE_PLAN_PRIVATE_HH +#include "hb.hh" -#include "hb-private.hh" -#include "hb-object-private.hh" -#include "hb-shaper-private.hh" +#ifdef HAVE_GOBJECT +/* g++ didn't like older gtype.h gcc-only code path. */ +#include +#if !GLIB_CHECK_VERSION(2,29,16) +#undef __GNUC__ +#undef __GNUC_MINOR__ +#define __GNUC__ 2 +#define __GNUC_MINOR__ 6 +#endif -struct hb_shape_plan_t +#include "hb-gobject.h" + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@basename@" */ +/*** END file-production ***/ + +/*** BEGIN file-tail ***/ + +#endif +/*** END file-tail ***/ + +/*** BEGIN value-header ***/ +GType +@enum_name@_get_type () { - hb_object_header_t header; - ASSERT_POD (); + static gsize type_id = 0; - hb_bool_t default_shaper_list; - hb_face_t *face_unsafe; /* We don't carry a reference to face. */ - hb_segment_properties_t props; + if (g_once_init_enter (&type_id)) + { + static const G@Type@Value values[] = { +/*** END value-header ***/ - hb_shape_func_t *shaper_func; - const char *shaper_name; +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ - hb_feature_t *user_features; - unsigned int num_user_features; +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + GType id = + g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); + g_once_init_leave (&type_id, id); + } - int *coords; - unsigned int num_coords; + return type_id; +} - struct hb_shaper_data_t shaper_data; -}; - -#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS \ - , const hb_feature_t *user_features \ - , unsigned int num_user_features \ - , const int *coords \ - , unsigned int num_coords -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, shape_plan); -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT -#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS - - -#endif /* HB_SHAPE_PLAN_PRIVATE_HH */ +/*** END value-tail ***/ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-gobject-enums.h.tmpl b/src/3rdparty/harfbuzz-ng/src/hb-gobject-enums.h.tmpl new file mode 100644 index 00000000000..6dd98f7fed6 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-gobject-enums.h.tmpl @@ -0,0 +1,56 @@ +/*** BEGIN file-header ***/ +/* + * Copyright (C) 2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_GOBJECT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_GOBJECT_ENUMS_H +#define HB_GOBJECT_ENUMS_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +/*** END file-header ***/ + +/*** BEGIN value-header ***/ +HB_EXTERN GType +@enum_name@_get_type (void) G_GNUC_CONST; +#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) + +/*** END value-header ***/ + +/*** BEGIN file-tail ***/ + +HB_END_DECLS + +#endif /* HB_GOBJECT_ENUMS_H */ +/*** END file-tail ***/ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.cc b/src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.cc new file mode 100644 index 00000000000..332cc84888d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.cc @@ -0,0 +1,116 @@ +/* + * Copyright ยฉ 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_GOBJECT + + +/** + * SECTION:hb-gobject + * @title: hb-gobject + * @short_description: GObject integration support + * @include: hb-gobject.h + * + * Support for using HarfBuzz with the GObject library to provide + * type data. + * + * The types and functions listed here are solely a linkage between + * HarfBuzz's public data types and the GTypes used by the GObject framework. + * HarfBuzz uses GObject introspection to generate its Python bindings + * (and potentially other language bindings); client programs should never need + * to access the GObject-integration mechanics. + * + * For client programs using the GNOME and GTK software stack, please see the + * GLib and FreeType integration pages. + **/ + + +/* g++ didn't like older gtype.h gcc-only code path. */ +#include +#if !GLIB_CHECK_VERSION(2,29,16) +#undef __GNUC__ +#undef __GNUC_MINOR__ +#define __GNUC__ 2 +#define __GNUC_MINOR__ 6 +#endif + +#include "hb-gobject.h" + +#define HB_DEFINE_BOXED_TYPE(name,copy_func,free_func) \ +GType \ +hb_gobject_##name##_get_type () \ +{ \ + static gsize type_id = 0; \ + if (g_once_init_enter (&type_id)) { \ + GType id = g_boxed_type_register_static (g_intern_static_string ("hb_" #name "_t"), \ + (GBoxedCopyFunc) copy_func, \ + (GBoxedFreeFunc) free_func); \ + g_once_init_leave (&type_id, id); \ + } \ + return type_id; \ +} + +#define HB_DEFINE_OBJECT_TYPE(name) \ + HB_DEFINE_BOXED_TYPE (name, hb_##name##_reference, hb_##name##_destroy) + +#define HB_DEFINE_VALUE_TYPE(name) \ + static hb_##name##_t *_hb_##name##_reference (const hb_##name##_t *l) \ + { \ + hb_##name##_t *c = (hb_##name##_t *) hb_calloc (1, sizeof (hb_##name##_t)); \ + if (unlikely (!c)) return nullptr; \ + *c = *l; \ + return c; \ + } \ + static void _hb_##name##_destroy (hb_##name##_t *l) { hb_free (l); } \ + HB_DEFINE_BOXED_TYPE (name, _hb_##name##_reference, _hb_##name##_destroy) + +HB_DEFINE_OBJECT_TYPE (buffer) +HB_DEFINE_OBJECT_TYPE (blob) +HB_DEFINE_OBJECT_TYPE (draw_funcs) +HB_DEFINE_OBJECT_TYPE (paint_funcs) +HB_DEFINE_OBJECT_TYPE (face) +HB_DEFINE_OBJECT_TYPE (font) +HB_DEFINE_OBJECT_TYPE (font_funcs) +HB_DEFINE_OBJECT_TYPE (set) +HB_DEFINE_OBJECT_TYPE (map) +HB_DEFINE_OBJECT_TYPE (shape_plan) +HB_DEFINE_OBJECT_TYPE (unicode_funcs) +HB_DEFINE_VALUE_TYPE (feature) +HB_DEFINE_VALUE_TYPE (glyph_info) +HB_DEFINE_VALUE_TYPE (glyph_position) +HB_DEFINE_VALUE_TYPE (segment_properties) +HB_DEFINE_VALUE_TYPE (draw_state) +HB_DEFINE_VALUE_TYPE (color_stop) +HB_DEFINE_VALUE_TYPE (color_line) +HB_DEFINE_VALUE_TYPE (user_data_key) + +HB_DEFINE_VALUE_TYPE (ot_var_axis_info) +HB_DEFINE_VALUE_TYPE (ot_math_glyph_variant) +HB_DEFINE_VALUE_TYPE (ot_math_glyph_part) + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.h b/src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.h new file mode 100644 index 00000000000..b7b5f55ce67 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_GOBJECT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_GOBJECT_STRUCTS_H +#define HB_GOBJECT_STRUCTS_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +/* Object types */ + +HB_EXTERN GType +hb_gobject_blob_get_type (void); +#define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ()) + +HB_EXTERN GType +hb_gobject_buffer_get_type (void); +#define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ()) + +HB_EXTERN GType +hb_gobject_draw_funcs_get_type (void); +#define HB_GOBJECT_TYPE_DRAW_FUNCS (hb_gobject_draw_funcs_get_type ()) + +HB_EXTERN GType +hb_gobject_paint_funcs_get_type (void); +#define HB_GOBJECT_TYPE_PAINT_FUNCS (hb_gobject_paint_funcs_get_type ()) + +HB_EXTERN GType +hb_gobject_face_get_type (void); +#define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ()) + +HB_EXTERN GType +hb_gobject_font_get_type (void); +#define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ()) + +HB_EXTERN GType +hb_gobject_font_funcs_get_type (void); +#define HB_GOBJECT_TYPE_FONT_FUNCS (hb_gobject_font_funcs_get_type ()) + +HB_EXTERN GType +hb_gobject_set_get_type (void); +#define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ()) + +HB_EXTERN GType +hb_gobject_map_get_type (void); +#define HB_GOBJECT_TYPE_MAP (hb_gobject_map_get_type ()) + +HB_EXTERN GType +hb_gobject_shape_plan_get_type (void); +#define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ()) + +HB_EXTERN GType +hb_gobject_unicode_funcs_get_type (void); +#define HB_GOBJECT_TYPE_UNICODE_FUNCS (hb_gobject_unicode_funcs_get_type ()) + +/* Value types */ + +HB_EXTERN GType +hb_gobject_feature_get_type (void); +#define HB_GOBJECT_TYPE_FEATURE (hb_gobject_feature_get_type ()) + +HB_EXTERN GType +hb_gobject_glyph_info_get_type (void); +#define HB_GOBJECT_TYPE_GLYPH_INFO (hb_gobject_glyph_info_get_type ()) + +HB_EXTERN GType +hb_gobject_glyph_position_get_type (void); +#define HB_GOBJECT_TYPE_GLYPH_POSITION (hb_gobject_glyph_position_get_type ()) + +HB_EXTERN GType +hb_gobject_segment_properties_get_type (void); +#define HB_GOBJECT_TYPE_SEGMENT_PROPERTIES (hb_gobject_segment_properties_get_type ()) + +HB_EXTERN GType +hb_gobject_draw_state_get_type (void); +#define HB_GOBJECT_TYPE_DRAW_STATE (hb_gobject_draw_state_get_type ()) + +HB_EXTERN GType +hb_gobject_color_stop_get_type (void); +#define HB_GOBJECT_TYPE_COLOR_STOP (hb_gobject_color_stop_get_type ()) + +HB_EXTERN GType +hb_gobject_color_line_get_type (void); +#define HB_GOBJECT_TYPE_COLOR_LINE (hb_gobject_color_line_get_type ()) + +HB_EXTERN GType +hb_gobject_user_data_key_get_type (void); +#define HB_GOBJECT_TYPE_USER_DATA_KEY (hb_gobject_user_data_key_get_type ()) + +HB_EXTERN GType +hb_gobject_ot_var_axis_info_get_type (void); +#define HB_GOBJECT_TYPE_OT_VAR_AXIS_INFO (hb_gobject_ot_var_axis_info_get_type ()) + +HB_EXTERN GType +hb_gobject_ot_math_glyph_variant_get_type (void); +#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_VARIANT (hb_gobject_ot_math_glyph_variant_get_type ()) + +HB_EXTERN GType +hb_gobject_ot_math_glyph_part_get_type (void); +#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART (hb_gobject_ot_math_glyph_part_get_type ()) + + +HB_END_DECLS + +#endif /* HB_GOBJECT_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-warning.cc b/src/3rdparty/harfbuzz-ng/src/hb-gobject.h similarity index 68% rename from src/3rdparty/harfbuzz-ng/src/hb-warning.cc rename to src/3rdparty/harfbuzz-ng/src/hb-gobject.h index 8f322bcb10d..8891aa0ee79 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-warning.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-gobject.h @@ -1,5 +1,5 @@ /* - * Copyright ยฉ 2012 Google, Inc. + * Copyright (C) 2011 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -24,16 +24,17 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-atomic-private.hh" -#include "hb-mutex-private.hh" +#ifndef HB_GOBJECT_H +#define HB_GOBJECT_H +#define HB_GOBJECT_H_IN +#include "hb.h" -#if defined(HB_ATOMIC_INT_NIL) -#error "Could not find any system to define atomic_int macros, library WILL NOT be thread-safe" -#error "Check hb-atomic-private.hh for possible resolutions." -#endif +#include "hb-gobject-enums.h" +#include "hb-gobject-structs.h" -#if defined(HB_MUTEX_IMPL_NIL) -#error "Could not find any system to define mutex macros, library WILL NOT be thread-safe" -#error "Check hb-mutex-private.hh for possible resolutions." -#endif +HB_BEGIN_DECLS +HB_END_DECLS + +#undef HB_GOBJECT_H_IN +#endif /* HB_GOBJECT_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-graphite2.cc b/src/3rdparty/harfbuzz-ng/src/hb-graphite2.cc new file mode 100644 index 00000000000..9e068f8d84d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-graphite2.cc @@ -0,0 +1,449 @@ +/* + * Copyright ยฉ 2011 Martin Hosken + * Copyright ยฉ 2011 SIL International + * Copyright ยฉ 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_GRAPHITE2 + +#include "hb-shaper-impl.hh" + +#include "hb-graphite2.h" + +#include + +#include "hb-ot-layout.h" + + +/** + * SECTION:hb-graphite2 + * @title: hb-graphite2 + * @short_description: Graphite2 integration + * @include: hb-graphite2.h + * + * Functions for using HarfBuzz with fonts that include Graphite features. + * + * For Graphite features to work, you must be sure that HarfBuzz was compiled + * with the `graphite2` shaping engine enabled. Currently, the default is to + * not enable `graphite2` shaping. + **/ + + +/* + * shaper face data + */ + +typedef struct hb_graphite2_tablelist_t +{ + struct hb_graphite2_tablelist_t *next; + hb_blob_t *blob; + unsigned int tag; +} hb_graphite2_tablelist_t; + +struct hb_graphite2_face_data_t +{ + hb_face_t *face; + gr_face *grface; + hb_atomic_ptr_t tlist; +}; + +static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len) +{ + hb_graphite2_face_data_t *face_data = (hb_graphite2_face_data_t *) data; + hb_graphite2_tablelist_t *tlist = face_data->tlist; + + hb_blob_t *blob = nullptr; + + for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next) + if (p->tag == tag) { + blob = p->blob; + break; + } + + if (unlikely (!blob)) + { + blob = face_data->face->reference_table (tag); + + hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) hb_calloc (1, sizeof (hb_graphite2_tablelist_t)); + if (unlikely (!p)) { + hb_blob_destroy (blob); + return nullptr; + } + p->blob = blob; + p->tag = tag; + +retry: + hb_graphite2_tablelist_t *tlist = face_data->tlist; + p->next = tlist; + + if (unlikely (!face_data->tlist.cmpexch (tlist, p))) + goto retry; + } + + unsigned int tlen; + const char *d = hb_blob_get_data (blob, &tlen); + *len = tlen; + return d; +} + +hb_graphite2_face_data_t * +_hb_graphite2_shaper_face_data_create (hb_face_t *face) +{ + hb_blob_t *silf_blob = face->reference_table (HB_GRAPHITE2_TAG_SILF); + /* Umm, we just reference the table to check whether it exists. + * Maybe add better API for this? */ + if (!hb_blob_get_length (silf_blob)) + { + hb_blob_destroy (silf_blob); + return nullptr; + } + hb_blob_destroy (silf_blob); + + hb_graphite2_face_data_t *data = (hb_graphite2_face_data_t *) hb_calloc (1, sizeof (hb_graphite2_face_data_t)); + if (unlikely (!data)) + return nullptr; + + data->face = face; + const gr_face_ops ops = {sizeof(gr_face_ops), &hb_graphite2_get_table, NULL}; + data->grface = gr_make_face_with_ops (data, &ops, gr_face_preloadAll); + + if (unlikely (!data->grface)) { + hb_free (data); + return nullptr; + } + + return data; +} + +void +_hb_graphite2_shaper_face_data_destroy (hb_graphite2_face_data_t *data) +{ + hb_graphite2_tablelist_t *tlist = data->tlist; + + while (tlist) + { + hb_graphite2_tablelist_t *old = tlist; + hb_blob_destroy (tlist->blob); + tlist = tlist->next; + hb_free (old); + } + + gr_face_destroy (data->grface); + + hb_free (data); +} + +/** + * hb_graphite2_face_get_gr_face: (skip) + * @face: @hb_face_t to query + * + * Fetches the Graphite2 gr_face corresponding to the specified + * #hb_face_t face object. + * + * Return value: the gr_face found + * + * Since: 0.9.10 + */ +gr_face * +hb_graphite2_face_get_gr_face (hb_face_t *face) +{ + const hb_graphite2_face_data_t *data = face->data.graphite2; + return data ? data->grface : nullptr; +} + + +/* + * shaper font data + */ + +struct hb_graphite2_font_data_t {}; + +hb_graphite2_font_data_t * +_hb_graphite2_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_graphite2_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_graphite2_shaper_font_data_destroy (hb_graphite2_font_data_t *data HB_UNUSED) +{ +} + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_graphite2_font_get_gr_font: (skip) + * @font: An #hb_font_t + * + * Always returns `NULL`. Use hb_graphite2_face_get_gr_face() instead. + * + * Return value: (nullable): Graphite2 font associated with @font. + * + * Since: 0.9.10 + * Deprecated: 1.4.2 + */ +gr_font * +hb_graphite2_font_get_gr_font (hb_font_t *font HB_UNUSED) +{ + return nullptr; +} +#endif + + +/* + * shaper + */ + +struct hb_graphite2_cluster_t { + unsigned int base_char; + unsigned int num_chars; + unsigned int base_glyph; + unsigned int num_glyphs; + unsigned int cluster; + int advance; +}; + +hb_bool_t +_hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + gr_face *grface = face->data.graphite2->grface; + + const char *lang = hb_language_to_string (hb_buffer_get_language (buffer)); + const char *lang_end = lang ? strchr (lang, '-') : nullptr; + int lang_len = lang_end ? lang_end - lang : -1; + gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0); + + for (unsigned int i = 0; i < num_features; i++) + { + const gr_feature_ref *fref = gr_face_find_fref (grface, features[i].tag); + if (fref) + gr_fref_set_feature_value (fref, features[i].value, feats); + } + + gr_segment *seg = nullptr; + const gr_slot *is; + unsigned int ci = 0, ic = 0; + unsigned int curradvx = 0, curradvy = 0; + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); + + uint32_t *chars = (uint32_t *) scratch; + + for (unsigned int i = 0; i < buffer->len; ++i) + chars[i] = buffer->info[i].codepoint; + + /* TODO ensure_native_direction. */ + + hb_tag_t script_tag[HB_OT_MAX_TAGS_PER_SCRIPT]; + unsigned int count = HB_OT_MAX_TAGS_PER_SCRIPT; + hb_ot_tags_from_script_and_language (hb_buffer_get_script (buffer), + HB_LANGUAGE_INVALID, + &count, + script_tag, + nullptr, nullptr); + + seg = gr_make_seg (nullptr, grface, + count ? script_tag[count - 1] : HB_OT_TAG_DEFAULT_SCRIPT, + feats, + gr_utf32, chars, buffer->len, + 2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0)); + + if (unlikely (!seg)) { + if (feats) gr_featureval_destroy (feats); + return false; + } + + unsigned int glyph_count = gr_seg_n_slots (seg); + if (unlikely (!glyph_count)) { + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + buffer->len = 0; + return true; + } + + (void) buffer->ensure (glyph_count); + scratch = buffer->get_scratch_buffer (&scratch_size); + while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) + + DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size) + { + if (unlikely (!buffer->ensure (buffer->allocated * 2))) + { + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + return false; + } + scratch = buffer->get_scratch_buffer (&scratch_size); + } + +#define ALLOCATE_ARRAY(Type, name, len) \ + Type *name = (Type *) scratch; \ + do { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + assert (_consumed <= scratch_size); \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } while (0) + + ALLOCATE_ARRAY (hb_graphite2_cluster_t, clusters, buffer->len); + ALLOCATE_ARRAY (hb_codepoint_t, gids, glyph_count); + +#undef ALLOCATE_ARRAY + + hb_memset (clusters, 0, sizeof (clusters[0]) * buffer->len); + + hb_codepoint_t *pg = gids; + clusters[0].cluster = buffer->info[0].cluster; + unsigned int upem = hb_face_get_upem (face); + float xscale = (float) font->x_scale / upem; + float yscale = (float) font->y_scale / upem; + yscale *= yscale / xscale; + unsigned int curradv = 0; + if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + { + curradv = gr_slot_origin_X(gr_seg_first_slot(seg)) * xscale; + clusters[0].advance = gr_seg_advance_X(seg) * xscale - curradv; + } + else + clusters[0].advance = 0; + for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++) + { + unsigned int before = gr_slot_before (is); + unsigned int after = gr_slot_after (is); + *pg = gr_slot_gid (is); + pg++; + while (clusters[ci].base_char > before && ci) + { + clusters[ci-1].num_chars += clusters[ci].num_chars; + clusters[ci-1].num_glyphs += clusters[ci].num_glyphs; + clusters[ci-1].advance += clusters[ci].advance; + ci--; + } + + if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars) + { + hb_graphite2_cluster_t *c = clusters + ci + 1; + c->base_char = clusters[ci].base_char + clusters[ci].num_chars; + c->cluster = buffer->info[c->base_char].cluster; + c->num_chars = before - c->base_char; + c->base_glyph = ic; + c->num_glyphs = 0; + if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + { + c->advance = curradv - gr_slot_origin_X(is) * xscale; + curradv -= c->advance; + } + else + { + c->advance = 0; + clusters[ci].advance += gr_slot_origin_X(is) * xscale - curradv; + curradv += clusters[ci].advance; + } + ci++; + } + clusters[ci].num_glyphs++; + + if (clusters[ci].base_char + clusters[ci].num_chars < after + 1) + clusters[ci].num_chars = after + 1 - clusters[ci].base_char; + } + + if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + clusters[ci].advance += curradv; + else + clusters[ci].advance += gr_seg_advance_X(seg) * xscale - curradv; + ci++; + + for (unsigned int i = 0; i < ci; ++i) + { + for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j) + { + hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j]; + info->codepoint = gids[clusters[i].base_glyph + j]; + info->cluster = clusters[i].cluster; + info->var1.i32 = clusters[i].advance; // all glyphs in the cluster get the same advance + } + } + buffer->len = glyph_count; + + /* Positioning. */ + unsigned int currclus = UINT_MAX; + const hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, nullptr); + if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + { + curradvx = 0; + for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is)) + { + pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx; + pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; + if (info->cluster != currclus) { + pPos->x_advance = info->var1.i32; + curradvx += pPos->x_advance; + currclus = info->cluster; + } else + pPos->x_advance = 0.; + + pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; + curradvy += pPos->y_advance; + } + } + else + { + curradvx = gr_seg_advance_X(seg) * xscale; + for (is = gr_seg_first_slot (seg); is; pPos++, info++, is = gr_slot_next_in_segment (is)) + { + if (info->cluster != currclus) + { + pPos->x_advance = info->var1.i32; + curradvx -= pPos->x_advance; + currclus = info->cluster; + } else + pPos->x_advance = 0.; + + pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; + curradvy -= pPos->y_advance; + pPos->x_offset = gr_slot_origin_X (is) * xscale - info->var1.i32 - curradvx + pPos->x_advance; + pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; + } + hb_buffer_reverse_clusters (buffer); + } + + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + + buffer->clear_glyph_flags (); + buffer->unsafe_to_break (); + + return true; +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-graphite2.h b/src/3rdparty/harfbuzz-ng/src/hb-graphite2.h new file mode 100644 index 00000000000..ee9229b8b00 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-graphite2.h @@ -0,0 +1,61 @@ +/* + * Copyright ยฉ 2011 Martin Hosken + * Copyright ยฉ 2011 SIL International + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_GRAPHITE2_H +#define HB_GRAPHITE2_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + +/** + * HB_GRAPHITE2_TAG_SILF: + * + * The #hb_tag_t tag for the `Silf` table, which holds Graphite + * features. + * + * For more information, see http://graphite.sil.org/ + * + **/ +#define HB_GRAPHITE2_TAG_SILF HB_TAG('S','i','l','f') + + +HB_EXTERN gr_face * +hb_graphite2_face_get_gr_face (hb_face_t *face); + +#ifndef HB_DISABLE_DEPRECATED + +HB_DEPRECATED_FOR (hb_graphite2_face_get_gr_face) +HB_EXTERN gr_font * +hb_graphite2_font_get_gr_font (hb_font_t *font); + +#endif + + +HB_END_DECLS + +#endif /* HB_GRAPHITE2_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-icu.cc b/src/3rdparty/harfbuzz-ng/src/hb-icu.cc new file mode 100644 index 00000000000..e46401f7a61 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-icu.cc @@ -0,0 +1,290 @@ +/* + * Copyright ยฉ 2009 Red Hat, Inc. + * Copyright ยฉ 2009 Keith Stribley + * Copyright ยฉ 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_ICU + +#include "hb-icu.h" + +#include "hb-machinery.hh" + +#include +#include +#include +#include +#include + +/* ICU extra semicolon, fixed since 65, https://github.com/unicode-org/icu/commit/480bec3 */ +#if U_ICU_VERSION_MAJOR_NUM < 65 && (defined(__GNUC__) || defined(__clang__)) +#define HB_ICU_EXTRA_SEMI_IGNORED +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wextra-semi-stmt" +#endif + +/** + * SECTION:hb-icu + * @title: hb-icu + * @short_description: ICU integration + * @include: hb-icu.h + * + * Functions for using HarfBuzz with the International Components for Unicode + * (ICU) library. HarfBuzz supports using ICU to provide Unicode data, by attaching + * ICU functions to the virtual methods in a #hb_unicode_funcs_t function + * structure. + **/ + +/** + * hb_icu_script_to_script: + * @script: The UScriptCode identifier to query + * + * Fetches the #hb_script_t script that corresponds to the + * specified UScriptCode identifier. + * + * Return value: the #hb_script_t script found + * + **/ + +hb_script_t +hb_icu_script_to_script (UScriptCode script) +{ + if (unlikely (script == USCRIPT_INVALID_CODE)) + return HB_SCRIPT_INVALID; + + return hb_script_from_string (uscript_getShortName (script), -1); +} + +/** + * hb_icu_script_from_script: + * @script: The #hb_script_t script to query + * + * Fetches the UScriptCode identifier that corresponds to the + * specified #hb_script_t script. + * + * Return value: the UScriptCode identifier found + * + **/ +UScriptCode +hb_icu_script_from_script (hb_script_t script) +{ + if (unlikely (script == HB_SCRIPT_INVALID)) + return USCRIPT_INVALID_CODE; + + unsigned int numScriptCode = 1 + u_getIntPropertyMaxValue (UCHAR_SCRIPT); + for (unsigned int i = 0; i < numScriptCode; i++) + if (unlikely (hb_icu_script_to_script ((UScriptCode) i) == script)) + return (UScriptCode) i; + + return USCRIPT_UNKNOWN; +} + + +static hb_unicode_combining_class_t +hb_icu_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + return (hb_unicode_combining_class_t) u_getCombiningClass (unicode); +} + +static hb_unicode_general_category_t +hb_icu_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + switch (u_getIntPropertyValue(unicode, UCHAR_GENERAL_CATEGORY)) + { + case U_UNASSIGNED: return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED; + + case U_UPPERCASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER; + case U_LOWERCASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER; + case U_TITLECASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER; + case U_MODIFIER_LETTER: return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER; + case U_OTHER_LETTER: return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER; + + case U_NON_SPACING_MARK: return HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK; + case U_ENCLOSING_MARK: return HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK; + case U_COMBINING_SPACING_MARK: return HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK; + + case U_DECIMAL_DIGIT_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER; + case U_LETTER_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER; + case U_OTHER_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER; + + case U_SPACE_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR; + case U_LINE_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR; + case U_PARAGRAPH_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR; + + case U_CONTROL_CHAR: return HB_UNICODE_GENERAL_CATEGORY_CONTROL; + case U_FORMAT_CHAR: return HB_UNICODE_GENERAL_CATEGORY_FORMAT; + case U_PRIVATE_USE_CHAR: return HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE; + case U_SURROGATE: return HB_UNICODE_GENERAL_CATEGORY_SURROGATE; + + + case U_DASH_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION; + case U_START_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION; + case U_END_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION; + case U_CONNECTOR_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION; + case U_OTHER_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION; + + case U_MATH_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL; + case U_CURRENCY_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL; + case U_MODIFIER_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL; + case U_OTHER_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL; + + case U_INITIAL_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION; + case U_FINAL_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION; + } + + return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED; +} + +static hb_codepoint_t +hb_icu_unicode_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return u_charMirror(unicode); +} + +static hb_script_t +hb_icu_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + UErrorCode status = U_ZERO_ERROR; + UScriptCode scriptCode = uscript_getScript(unicode, &status); + + if (unlikely (U_FAILURE (status))) + return HB_SCRIPT_UNKNOWN; + + return hb_icu_script_to_script (scriptCode); +} + +static hb_bool_t +hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab, + void *user_data) +{ + const UNormalizer2 *normalizer = (const UNormalizer2 *) user_data; + UChar32 ret = unorm2_composePair (normalizer, a, b); + if (ret < 0) return false; + *ab = ret; + return true; +} + +static hb_bool_t +hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b, + void *user_data) +{ + const UNormalizer2 *normalizer = (const UNormalizer2 *) user_data; + UChar decomposed[4]; + int len; + UErrorCode icu_err = U_ZERO_ERROR; + len = unorm2_getRawDecomposition (normalizer, ab, decomposed, + ARRAY_LENGTH (decomposed), &icu_err); + if (U_FAILURE (icu_err) || len < 0) return false; + + len = u_countChar32 (decomposed, len); + if (len == 1) + { + U16_GET_UNSAFE (decomposed, 0, *a); + *b = 0; + return *a != ab; + } + else if (len == 2) + { + len = 0; + U16_NEXT_UNSAFE (decomposed, len, *a); + U16_NEXT_UNSAFE (decomposed, len, *b); + } + return true; +} + + +static inline void free_static_icu_funcs (); + +static struct hb_icu_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t +{ + static hb_unicode_funcs_t *create () + { + void *user_data = nullptr; + UErrorCode icu_err = U_ZERO_ERROR; + user_data = (void *) unorm2_getNFCInstance (&icu_err); + assert (user_data); + + hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); + + hb_unicode_funcs_set_combining_class_func (funcs, hb_icu_unicode_combining_class, nullptr, nullptr); + hb_unicode_funcs_set_general_category_func (funcs, hb_icu_unicode_general_category, nullptr, nullptr); + hb_unicode_funcs_set_mirroring_func (funcs, hb_icu_unicode_mirroring, nullptr, nullptr); + hb_unicode_funcs_set_script_func (funcs, hb_icu_unicode_script, nullptr, nullptr); + hb_unicode_funcs_set_compose_func (funcs, hb_icu_unicode_compose, user_data, nullptr); + hb_unicode_funcs_set_decompose_func (funcs, hb_icu_unicode_decompose, user_data, nullptr); + + hb_unicode_funcs_make_immutable (funcs); + + hb_atexit (free_static_icu_funcs); + + return funcs; + } +} static_icu_funcs; + +static inline +void free_static_icu_funcs () +{ + static_icu_funcs.free_instance (); +} + +/** + * hb_icu_get_unicode_funcs: + * + * Fetches a Unicode-functions structure that is populated + * with the appropriate ICU function for each method. + * + * Return value: (transfer none): a pointer to the #hb_unicode_funcs_t Unicode-functions structure + * + * Since: 0.9.38 + **/ +hb_unicode_funcs_t * +hb_icu_get_unicode_funcs () +{ + return static_icu_funcs.get_unconst (); +} + +#ifdef HB_ICU_EXTRA_SEMI_IGNORED +#pragma GCC diagnostic pop +#endif + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-icu.h b/src/3rdparty/harfbuzz-ng/src/hb-icu.h new file mode 100644 index 00000000000..2db6a7b6797 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-icu.h @@ -0,0 +1,52 @@ +/* + * Copyright ยฉ 2009 Red Hat, Inc. + * Copyright ยฉ 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_ICU_H +#define HB_ICU_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +HB_EXTERN hb_script_t +hb_icu_script_to_script (UScriptCode script); + +HB_EXTERN UScriptCode +hb_icu_script_from_script (hb_script_t script); + + +HB_EXTERN hb_unicode_funcs_t * +hb_icu_get_unicode_funcs (void); + + +HB_END_DECLS + +#endif /* HB_ICU_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-iter.hh b/src/3rdparty/harfbuzz-ng/src/hb-iter.hh new file mode 100644 index 00000000000..b123b2f27cf --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-iter.hh @@ -0,0 +1,1026 @@ +/* + * Copyright ยฉ 2018 Google, Inc. + * Copyright ยฉ 2019 Facebook, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_ITER_HH +#define HB_ITER_HH + +#include "hb.hh" +#include "hb-algs.hh" +#include "hb-meta.hh" + + +/* Unified iterator object. + * + * The goal of this template is to make the same iterator interface + * available to all types, and make it very easy and compact to use. + * hb_iter_tator objects are small, light-weight, objects that can be + * copied by value. If the collection / object being iterated on + * is writable, then the iterator returns lvalues, otherwise it + * returns rvalues. + * + * If iterator implementation implements operator!=, then it can be + * used in range-based for loop. That already happens if the iterator + * is random-access. Otherwise, the range-based for loop incurs + * one traversal to find end(), which can be avoided if written + * as a while-style for loop, or if iterator implements a faster + * __end__() method. */ + +/* + * Base classes for iterators. + */ + +/* Base class for all iterators. */ +template +struct hb_iter_t +{ + typedef Item item_t; + constexpr unsigned get_item_size () const { return hb_static_size (Item); } + static constexpr bool is_iterator = true; + static constexpr bool is_random_access_iterator = false; + static constexpr bool is_sorted_iterator = false; + + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const iter_t* thiz () const { return static_cast (this); } + iter_t* thiz () { return static_cast< iter_t *> (this); } + public: + + /* Operators. */ + iter_t iter () const { return *thiz(); } + iter_t operator + () const { return *thiz(); } + iter_t _begin () const { return *thiz(); } + iter_t begin () const { return _begin (); } + iter_t _end () const { return thiz()->__end__ (); } + iter_t end () const { return _end (); } + explicit operator bool () const { return thiz()->__more__ (); } + unsigned len () const { return thiz()->__len__ (); } + /* The following can only be enabled if item_t is reference type. Otherwise + * it will be returning pointer to temporary rvalue. */ + template ::value)> + hb_remove_reference* operator -> () const { return std::addressof (**thiz()); } + item_t operator * () const { return thiz()->__item__ (); } + item_t operator * () { return thiz()->__item__ (); } + item_t operator [] (unsigned i) const { return thiz()->__item_at__ (i); } + item_t operator [] (unsigned i) { return thiz()->__item_at__ (i); } + iter_t& operator += (unsigned count) & { thiz()->__forward__ (count); return *thiz(); } + iter_t operator += (unsigned count) && { thiz()->__forward__ (count); return *thiz(); } + iter_t& operator ++ () & { thiz()->__next__ (); return *thiz(); } + iter_t operator ++ () && { thiz()->__next__ (); return *thiz(); } + iter_t& operator -= (unsigned count) & { thiz()->__rewind__ (count); return *thiz(); } + iter_t operator -= (unsigned count) && { thiz()->__rewind__ (count); return *thiz(); } + iter_t& operator -- () & { thiz()->__prev__ (); return *thiz(); } + iter_t operator -- () && { thiz()->__prev__ (); return *thiz(); } + iter_t operator + (unsigned count) const { auto c = thiz()->iter (); c += count; return c; } + friend iter_t operator + (unsigned count, const iter_t &it) { return it + count; } + iter_t operator ++ (int) { iter_t c (*thiz()); ++*thiz(); return c; } + iter_t operator - (unsigned count) const { auto c = thiz()->iter (); c -= count; return c; } + iter_t operator -- (int) { iter_t c (*thiz()); --*thiz(); return c; } + template + iter_t& operator >> (T &v) & { v = **thiz(); ++*thiz(); return *thiz(); } + template + iter_t operator >> (T &v) && { v = **thiz(); ++*thiz(); return *thiz(); } + template + iter_t& operator << (const T v) & { **thiz() = v; ++*thiz(); return *thiz(); } + template + iter_t operator << (const T v) && { **thiz() = v; ++*thiz(); return *thiz(); } + + protected: + hb_iter_t () = default; + hb_iter_t (const hb_iter_t &o HB_UNUSED) = default; + hb_iter_t (hb_iter_t &&o HB_UNUSED) = default; + hb_iter_t& operator = (const hb_iter_t &o HB_UNUSED) = default; + hb_iter_t& operator = (hb_iter_t &&o HB_UNUSED) = default; +}; + +#define HB_ITER_USING(Name) \ + using item_t = typename Name::item_t; \ + using Name::_begin; \ + using Name::begin; \ + using Name::_end; \ + using Name::end; \ + using Name::get_item_size; \ + using Name::is_iterator; \ + using Name::iter; \ + using Name::operator bool; \ + using Name::len; \ + using Name::operator ->; \ + using Name::operator *; \ + using Name::operator []; \ + using Name::operator +=; \ + using Name::operator ++; \ + using Name::operator -=; \ + using Name::operator --; \ + using Name::operator +; \ + using Name::operator -; \ + using Name::operator >>; \ + using Name::operator <<; \ + static_assert (true, "") + +/* Returns iterator / item type of a type. */ +template +using hb_iter_type = decltype (hb_deref (hb_declval (Iterable)).iter ()); +template +using hb_item_type = decltype (*hb_deref (hb_declval (Iterable)).iter ()); + + +template struct hb_array_t; +template struct hb_sorted_array_t; + +struct +{ + template hb_iter_type + operator () (T&& c) const + { return hb_deref (std::forward (c)).iter (); } + + /* Specialization for C arrays. */ + + template inline hb_array_t + operator () (Type *array, unsigned int length) const + { return hb_array_t (array, length); } + + template hb_array_t + operator () (Type (&array)[length]) const + { return hb_array_t (array, length); } + +} +HB_FUNCOBJ (hb_iter); +struct +{ + template auto + impl (T&& c, hb_priority<1>) const HB_RETURN (unsigned, c.len ()) + + template auto + impl (T&& c, hb_priority<0>) const HB_RETURN (unsigned, c.len) + + public: + + template auto + operator () (T&& c) const HB_RETURN (unsigned, impl (std::forward (c), hb_prioritize)) +} +HB_FUNCOBJ (hb_len); + +/* Mixin to fill in what the subclass doesn't provide. */ +template +struct hb_iter_fallback_mixin_t +{ + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const iter_t* thiz () const { return static_cast (this); } + iter_t* thiz () { return static_cast< iter_t *> (this); } + public: + + /* Access: Implement __item__(), or __item_at__() if random-access. */ + item_t __item__ () const { return (*thiz())[0]; } + item_t __item_at__ (unsigned i) const { return *(*thiz() + i); } + + /* Termination: Implement __more__(), or __len__() if random-access. */ + bool __more__ () const { return bool (thiz()->len ()); } + unsigned __len__ () const + { iter_t c (*thiz()); unsigned l = 0; while (c) { c++; l++; } return l; } + + /* Advancing: Implement __next__(), or __forward__() if random-access. */ + void __next__ () { *thiz() += 1; } + void __forward__ (unsigned n) { while (*thiz() && n--) ++*thiz(); } + + /* Rewinding: Implement __prev__() or __rewind__() if bidirectional. */ + void __prev__ () { *thiz() -= 1; } + void __rewind__ (unsigned n) { while (*thiz() && n--) --*thiz(); } + + /* Range-based for: Implement __end__() if can be done faster, + * and operator!=. */ + iter_t __end__ () const + { + if (thiz()->is_random_access_iterator) + return *thiz() + thiz()->len (); + /* Above expression loops twice. Following loops once. */ + auto it = *thiz(); + while (it) ++it; + return it; + } + + protected: + hb_iter_fallback_mixin_t () = default; + hb_iter_fallback_mixin_t (const hb_iter_fallback_mixin_t &o HB_UNUSED) = default; + hb_iter_fallback_mixin_t (hb_iter_fallback_mixin_t &&o HB_UNUSED) = default; + hb_iter_fallback_mixin_t& operator = (const hb_iter_fallback_mixin_t &o HB_UNUSED) = default; + hb_iter_fallback_mixin_t& operator = (hb_iter_fallback_mixin_t &&o HB_UNUSED) = default; +}; + +template +struct hb_iter_with_fallback_t : + hb_iter_t, + hb_iter_fallback_mixin_t +{ + protected: + hb_iter_with_fallback_t () = default; + hb_iter_with_fallback_t (const hb_iter_with_fallback_t &o HB_UNUSED) = default; + hb_iter_with_fallback_t (hb_iter_with_fallback_t &&o HB_UNUSED) = default; + hb_iter_with_fallback_t& operator = (const hb_iter_with_fallback_t &o HB_UNUSED) = default; + hb_iter_with_fallback_t& operator = (hb_iter_with_fallback_t &&o HB_UNUSED) = default; +}; + +/* + * Meta-programming predicates. + */ + +/* hb_is_iterator() / hb_is_iterator_of() */ + +template +struct hb_is_iterator_of +{ + template + static hb_true_type impl (hb_priority<2>, hb_iter_t> *); + static hb_false_type impl (hb_priority<0>, const void *); + + public: + static constexpr bool value = decltype (impl (hb_prioritize, hb_declval (Iter*)))::value; +}; +#define hb_is_iterator_of(Iter, Item) hb_is_iterator_of::value +#define hb_is_iterator(Iter) hb_is_iterator_of (Iter, typename Iter::item_t) +#define hb_is_sorted_iterator_of(Iter, Item) (hb_is_iterator_of::value && Iter::is_sorted_iterator) +#define hb_is_sorted_iterator(Iter) hb_is_sorted_iterator_of (Iter, typename Iter::item_t) + +/* hb_is_iterable() */ + +template +struct hb_is_iterable +{ + private: + + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (U).iter (), hb_true_type ()); + + template + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_iterable(Iterable) hb_is_iterable::value + +/* hb_is_source_of() / hb_is_sink_of() */ + +template +struct hb_is_source_of +{ + private: + template ))> + static hb_true_type impl (hb_priority<2>); + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (Iter2) >> hb_declval (Item &), hb_true_type ()); + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_source_of(Iter, Item) hb_is_source_of::value + +template +struct hb_is_sink_of +{ + private: + template ))> + static hb_true_type impl (hb_priority<2>); + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (Iter2) << hb_declval (Item), hb_true_type ()); + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_sink_of(Iter, Item) hb_is_sink_of::value + +/* This is commonly used, so define: */ +#define hb_is_sorted_source_of(Iter, Item) \ + (hb_is_source_of(Iter, Item) && Iter::is_sorted_iterator) + + +/* Range-based 'for' for iterables. */ + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +/* begin()/end() are NOT looked up non-ADL. So each namespace must declare them. + * Do it for namespace OT. */ +namespace OT { + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +} + + +/* + * Adaptors, combiners, etc. + */ + +template +static inline auto +operator | (Lhs&& lhs, Rhs&& rhs) HB_AUTO_RETURN (std::forward (rhs) (std::forward (lhs))) + +/* hb_map(), hb_filter(), hb_reduce() */ + +enum class hb_function_sortedness_t { + NOT_SORTED, + RETAINS_SORTING, + SORTED, +}; + +template +struct hb_map_iter_t : + hb_iter_t, + decltype (hb_get (hb_declval (Proj), *hb_declval (Iter)))> +{ + hb_map_iter_t (const Iter& it, Proj f_) : it (it), f (f_) {} + + typedef decltype (hb_get (hb_declval (Proj), *hb_declval (Iter))) __item_t__; + static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator; + static constexpr bool is_sorted_iterator = + Sorted == hb_function_sortedness_t::SORTED ? true : + Sorted == hb_function_sortedness_t::RETAINS_SORTING ? Iter::is_sorted_iterator : + false; + __item_t__ __item__ () const { return hb_get (f.get (), *it); } + __item_t__ __item_at__ (unsigned i) const { return hb_get (f.get (), it[i]); } + bool __more__ () const { return bool (it); } + unsigned __len__ () const { return it.len (); } + void __next__ () { ++it; } + void __forward__ (unsigned n) { it += n; } + void __prev__ () { --it; } + void __rewind__ (unsigned n) { it -= n; } + hb_map_iter_t __end__ () const { return hb_map_iter_t (it._end (), f); } + bool operator != (const hb_map_iter_t& o) const + { return it != o.it; } + + private: + Iter it; + hb_reference_wrapper f; +}; + +template +struct hb_map_iter_factory_t +{ + hb_map_iter_factory_t (Proj f) : f (f) {} + + template + hb_map_iter_t + operator () (Iter it) + { return hb_map_iter_t (it, f); } + + private: + Proj f; +}; +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map); +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map_retains_sorting); +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map_sorted); + +template +struct hb_filter_iter_t : + hb_iter_with_fallback_t, + typename Iter::item_t> +{ + hb_filter_iter_t (const Iter& it_, Pred p_, Proj f_) : it (it_), p (p_), f (f_) + { while (it && !hb_has (p.get (), hb_get (f.get (), *it))) ++it; } + + typedef typename Iter::item_t __item_t__; + static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator; + __item_t__ __item__ () const { return *it; } + bool __more__ () const { return bool (it); } + void __next__ () { do ++it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } + void __prev__ () { do --it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } + hb_filter_iter_t __end__ () const { return hb_filter_iter_t (it._end (), p, f); } + bool operator != (const hb_filter_iter_t& o) const + { return it != o.it; } + + private: + Iter it; + hb_reference_wrapper p; + hb_reference_wrapper f; +}; +template +struct hb_filter_iter_factory_t +{ + hb_filter_iter_factory_t (Pred p, Proj f) : p (p), f (f) {} + + template + hb_filter_iter_t + operator () (Iter it) + { return hb_filter_iter_t (it, p, f); } + + private: + Pred p; + Proj f; +}; +struct +{ + template + hb_filter_iter_factory_t + operator () (Pred&& p = hb_identity, Proj&& f = hb_identity) const + { return hb_filter_iter_factory_t (p, f); } +} +HB_FUNCOBJ (hb_filter); + +template +struct hb_reduce_t +{ + hb_reduce_t (Redu r, InitT init_value) : r (r), init_value (init_value) {} + + template > + AccuT + operator () (Iter it) + { + AccuT value = init_value; + for (; it; ++it) + value = r (value, *it); + return value; + } + + private: + Redu r; + InitT init_value; +}; +struct +{ + template + hb_reduce_t + operator () (Redu&& r, InitT init_value) const + { return hb_reduce_t (r, init_value); } +} +HB_FUNCOBJ (hb_reduce); + + +/* hb_zip() */ + +template +struct hb_zip_iter_t : + hb_iter_t, + hb_pair_t> +{ + hb_zip_iter_t () {} + hb_zip_iter_t (const A& a, const B& b) : a (a), b (b) {} + + typedef hb_pair_t __item_t__; + static constexpr bool is_random_access_iterator = + A::is_random_access_iterator && + B::is_random_access_iterator; + /* Note. The following categorization is only valid if A is strictly sorted, + * ie. does NOT have duplicates. Previously I tried to categorize sortedness + * more granularly, see commits: + * + * 513762849a683914fc266a17ddf38f133cccf072 + * 4d3cf2adb669c345cc43832d11689271995e160a + * + * However, that was not enough, since hb_sorted_array_t, hb_sorted_vector_t, + * SortedArrayOf, etc all needed to be updated to add more variants. At that + * point I saw it not worth the effort, and instead we now deem all sorted + * collections as essentially strictly-sorted for the purposes of zip. + * + * The above assumption is not as bad as it sounds. Our "sorted" comes with + * no guarantees. It's just a contract, put in place to help you remember, + * and think about, whether an iterator you receive is expected to be + * sorted or not. As such, it's not perfect by definition, and should not + * be treated so. The inaccuracy here just errs in the direction of being + * more permissive, so your code compiles instead of erring on the side of + * marking your zipped iterator unsorted in which case your code won't + * compile. + * + * This semantical limitation does NOT affect logic in any other place I + * know of as of this writing. + */ + static constexpr bool is_sorted_iterator = A::is_sorted_iterator; + + __item_t__ __item__ () const { return __item_t__ (*a, *b); } + __item_t__ __item_at__ (unsigned i) const { return __item_t__ (a[i], b[i]); } + bool __more__ () const { return bool (a) && bool (b); } + unsigned __len__ () const { return hb_min (a.len (), b.len ()); } + void __next__ () { ++a; ++b; } + void __forward__ (unsigned n) { a += n; b += n; } + void __prev__ () { --a; --b; } + void __rewind__ (unsigned n) { a -= n; b -= n; } + hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a._end (), b._end ()); } + /* Note, we should stop if ANY of the iters reaches end. As such two compare + * unequal if both items are unequal, NOT if either is unequal. */ + bool operator != (const hb_zip_iter_t& o) const + { return a != o.a && b != o.b; } + + private: + A a; + B b; +}; +struct +{ HB_PARTIALIZE(2); + template + hb_zip_iter_t, hb_iter_type> + operator () (A&& a, B&& b) const + { return hb_zip_iter_t, hb_iter_type> (hb_iter (a), hb_iter (b)); } +} +HB_FUNCOBJ (hb_zip); + +/* hb_concat() */ + +template +struct hb_concat_iter_t : + hb_iter_t, typename A::item_t> +{ + hb_concat_iter_t () {} + hb_concat_iter_t (A& a, B& b) : a (a), b (b) {} + hb_concat_iter_t (const A& a, const B& b) : a (a), b (b) {} + + + typedef typename A::item_t __item_t__; + static constexpr bool is_random_access_iterator = + A::is_random_access_iterator && + B::is_random_access_iterator; + static constexpr bool is_sorted_iterator = false; + + __item_t__ __item__ () const + { + if (!a) + return *b; + return *a; + } + + __item_t__ __item_at__ (unsigned i) const + { + unsigned a_len = a.len (); + if (i < a_len) + return a[i]; + return b[i - a_len]; + } + + bool __more__ () const { return bool (a) || bool (b); } + + unsigned __len__ () const { return a.len () + b.len (); } + + void __next__ () + { + if (a) + ++a; + else + ++b; + } + + void __forward__ (unsigned n) + { + if (!n) return; + if (!is_random_access_iterator) { + while (n-- && *this) { + (*this)++; + } + return; + } + + unsigned a_len = a.len (); + if (n > a_len) { + n -= a_len; + a.__forward__ (a_len); + b.__forward__ (n); + } else { + a.__forward__ (n); + } + } + + hb_concat_iter_t __end__ () const { return hb_concat_iter_t (a._end (), b._end ()); } + bool operator != (const hb_concat_iter_t& o) const + { + return a != o.a + || b != o.b; + } + + private: + A a; + B b; +}; +struct +{ HB_PARTIALIZE(2); + template + hb_concat_iter_t, hb_iter_type> + operator () (A&& a, B&& b) const + { return hb_concat_iter_t, hb_iter_type> (hb_iter (a), hb_iter (b)); } +} +HB_FUNCOBJ (hb_concat); + +/* hb_apply() */ + +template +struct hb_apply_t +{ + hb_apply_t (Appl a) : a (a) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + (void) hb_invoke (a, *it); + } + + private: + Appl a; +}; +struct +{ + template hb_apply_t + operator () (Appl&& a) const + { return hb_apply_t (a); } + + template hb_apply_t + operator () (Appl *a) const + { return hb_apply_t (*a); } +} +HB_FUNCOBJ (hb_apply); + +/* hb_range()/hb_iota()/hb_repeat() */ + +template +struct hb_range_iter_t : + hb_iter_t, T> +{ + hb_range_iter_t (T start, T end_, S step) : v (start), end_ (end_for (start, end_, step)), step (step) {} + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return hb_ridentity (v); } + __item_t__ __item_at__ (unsigned j) const { return v + j * step; } + bool __more__ () const { return v != end_; } + unsigned __len__ () const { return !step ? UINT_MAX : (end_ - v) / step; } + void __next__ () { v += step; } + void __forward__ (unsigned n) { v += n * step; } + void __prev__ () { v -= step; } + void __rewind__ (unsigned n) { v -= n * step; } + hb_range_iter_t __end__ () const { return hb_range_iter_t (end_, end_, step); } + bool operator != (const hb_range_iter_t& o) const + { return v != o.v; } + + private: + static inline T end_for (T start, T end_, S step) + { + if (!step) + return end_; + auto res = (end_ - start) % step; + if (!res) + return end_; + end_ += step - res; + return end_; + } + + private: + T v; + T end_; + S step; +}; +struct +{ + template hb_range_iter_t + operator () (T end = (unsigned) -1) const + { return hb_range_iter_t (0, end, 1u); } + + template hb_range_iter_t + operator () (T start, T end, S step = 1u) const + { return hb_range_iter_t (start, end, step); } +} +HB_FUNCOBJ (hb_range); + +template +struct hb_iota_iter_t : + hb_iter_with_fallback_t, T> +{ + hb_iota_iter_t (T start, S step) : v (start), step (step) {} + + private: + + template + auto + inc (hb_type_identity s, hb_priority<1>) + -> hb_void_t (s), hb_declval ()))> + { v = hb_invoke (std::forward (s), v); } + + void + inc (S s, hb_priority<0>) + { v += s; } + + public: + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return hb_ridentity (v); } + bool __more__ () const { return true; } + unsigned __len__ () const { return UINT_MAX; } + void __next__ () { inc (step, hb_prioritize); } + void __prev__ () { v -= step; } + hb_iota_iter_t __end__ () const { return *this; } + bool operator != (const hb_iota_iter_t& o) const { return true; } + + private: + T v; + S step; +}; +struct +{ + template hb_iota_iter_t + operator () (T start = 0u, S step = 1u) const + { return hb_iota_iter_t (start, step); } +} +HB_FUNCOBJ (hb_iota); + +template +struct hb_repeat_iter_t : + hb_iter_t, T> +{ + hb_repeat_iter_t (T value) : v (value) {} + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return v; } + __item_t__ __item_at__ (unsigned j) const { return v; } + bool __more__ () const { return true; } + unsigned __len__ () const { return UINT_MAX; } + void __next__ () {} + void __forward__ (unsigned) {} + void __prev__ () {} + void __rewind__ (unsigned) {} + hb_repeat_iter_t __end__ () const { return *this; } + bool operator != (const hb_repeat_iter_t& o) const { return true; } + + private: + T v; +}; +struct +{ + template hb_repeat_iter_t + operator () (T value) const + { return hb_repeat_iter_t (value); } +} +HB_FUNCOBJ (hb_repeat); + +/* hb_enumerate()/hb_take() */ + +struct +{ + template + auto operator () (Iterable&& it, Index start = 0u) const HB_AUTO_RETURN + ( hb_zip (hb_iota (start), it) ) +} +HB_FUNCOBJ (hb_enumerate); + +struct +{ HB_PARTIALIZE(2); + template + auto operator () (Iterable&& it, unsigned count) const HB_AUTO_RETURN + ( hb_zip (hb_range (count), it) | hb_map (hb_second) ) + + /* Specialization arrays. */ + + template inline hb_array_t + operator () (hb_array_t array, unsigned count) const + { return array.sub_array (0, count); } + + template inline hb_sorted_array_t + operator () (hb_sorted_array_t array, unsigned count) const + { return array.sub_array (0, count); } +} +HB_FUNCOBJ (hb_take); + +struct +{ HB_PARTIALIZE(2); + template + auto operator () (Iter it, unsigned count) const HB_AUTO_RETURN + ( + + hb_iota (it, hb_add (count)) + | hb_map (hb_take (count)) + | hb_take ((hb_len (it) + count - 1) / count) + ) +} +HB_FUNCOBJ (hb_chop); + +/* hb_sink() */ + +template +struct hb_sink_t +{ + hb_sink_t (Sink s) : s (s) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + s << *it; + } + + private: + Sink s; +}; +struct +{ + template hb_sink_t + operator () (Sink&& s) const + { return hb_sink_t (s); } + + template hb_sink_t + operator () (Sink *s) const + { return hb_sink_t (*s); } +} +HB_FUNCOBJ (hb_sink); + +/* hb-drain: hb_sink to void / blackhole / /dev/null. */ + +struct +{ + template + void operator () (Iter it) const + { + for (; it; ++it) + (void) *it; + } +} +HB_FUNCOBJ (hb_drain); + +/* hb_unzip(): unzip and sink to two sinks. */ + +template +struct hb_unzip_t +{ + hb_unzip_t (Sink1 s1, Sink2 s2) : s1 (s1), s2 (s2) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + { + const auto &v = *it; + s1 << v.first; + s2 << v.second; + } + } + + private: + Sink1 s1; + Sink2 s2; +}; +struct +{ + template hb_unzip_t + operator () (Sink1&& s1, Sink2&& s2) const + { return hb_unzip_t (s1, s2); } + + template hb_unzip_t + operator () (Sink1 *s1, Sink2 *s2) const + { return hb_unzip_t (*s1, *s2); } +} +HB_FUNCOBJ (hb_unzip); + + +/* hb-all, hb-any, hb-none. */ + +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (!hb_match (std::forward (p), hb_get (std::forward (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_all); +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (hb_match (std::forward (p), hb_get (std::forward (f), *it))) + return true; + return false; + } +} +HB_FUNCOBJ (hb_any); +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (hb_match (std::forward (p), hb_get (std::forward (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_none); + +/* + * Algorithms operating on iterators. + */ + +template +inline void +hb_fill (C&& c, const V &v) +{ + for (auto i = hb_iter (c); i; i++) + *i = v; +} + +template +inline void +hb_copy (S&& is, D&& id) +{ + hb_iter (is) | hb_sink (id); +} + + +#endif /* HB_ITER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-kern.hh b/src/3rdparty/harfbuzz-ng/src/hb-kern.hh new file mode 100644 index 00000000000..9ea945caed6 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-kern.hh @@ -0,0 +1,145 @@ +/* + * Copyright ยฉ 2017 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_KERN_HH +#define HB_KERN_HH + +#include "hb-open-type.hh" +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout-gpos-table.hh" + + +namespace OT { + + +template +struct hb_kern_machine_t +{ + hb_kern_machine_t (const Driver &driver_, + bool crossStream_ = false) : + driver (driver_), + crossStream (crossStream_) {} + + HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW + void kern (hb_font_t *font, + hb_buffer_t *buffer, + hb_mask_t kern_mask, + bool scale = true) const + { + if (!buffer->message (font, "start kern")) + return; + + buffer->unsafe_to_concat (); + OT::hb_ot_apply_context_t c (1, font, buffer); + c.set_lookup_mask (kern_mask); + c.set_lookup_props (OT::LookupFlag::IgnoreMarks); + auto &skippy_iter = c.iter_input; + + bool horizontal = HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction); + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + for (unsigned int idx = 0; idx < count;) + { + if (!(info[idx].mask & kern_mask)) + { + idx++; + continue; + } + + skippy_iter.reset (idx, 1); + unsigned unsafe_to; + if (!skippy_iter.next (&unsafe_to)) + { + idx++; + continue; + } + + unsigned int i = idx; + unsigned int j = skippy_iter.idx; + + hb_position_t kern = driver.get_kerning (info[i].codepoint, + info[j].codepoint); + + + if (likely (!kern)) + goto skip; + + if (horizontal) + { + if (scale) + kern = font->em_scale_x (kern); + if (crossStream) + { + pos[j].y_offset = kern; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + else + { + hb_position_t kern1 = kern >> 1; + hb_position_t kern2 = kern - kern1; + pos[i].x_advance += kern1; + pos[j].x_advance += kern2; + pos[j].x_offset += kern2; + } + } + else + { + if (scale) + kern = font->em_scale_y (kern); + if (crossStream) + { + pos[j].x_offset = kern; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + else + { + hb_position_t kern1 = kern >> 1; + hb_position_t kern2 = kern - kern1; + pos[i].y_advance += kern1; + pos[j].y_advance += kern2; + pos[j].y_offset += kern2; + } + } + + buffer->unsafe_to_break (i, j + 1); + + skip: + idx = skippy_iter.idx; + } + + (void) buffer->message (font, "end kern"); + } + + const Driver &driver; + bool crossStream; +}; + + +} /* namespace OT */ + + +#endif /* HB_KERN_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-limits.hh b/src/3rdparty/harfbuzz-ng/src/hb-limits.hh new file mode 100644 index 00000000000..0f60e9e2101 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-limits.hh @@ -0,0 +1,109 @@ +/* + * Copyright ยฉ 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_LIMITS_HH +#define HB_LIMITS_HH + +#include "hb.hh" + + +#ifndef HB_BUFFER_MAX_LEN_FACTOR +#define HB_BUFFER_MAX_LEN_FACTOR 64 +#endif +#ifndef HB_BUFFER_MAX_LEN_MIN +#define HB_BUFFER_MAX_LEN_MIN 16384 +#endif +#ifndef HB_BUFFER_MAX_LEN_DEFAULT +#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */ +#endif + +#ifndef HB_BUFFER_MAX_OPS_FACTOR +#define HB_BUFFER_MAX_OPS_FACTOR 1024 +#endif +#ifndef HB_BUFFER_MAX_OPS_MIN +#define HB_BUFFER_MAX_OPS_MIN 16384 +#endif +#ifndef HB_BUFFER_MAX_OPS_DEFAULT +#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */ +#endif + + +#ifndef HB_MAX_NESTING_LEVEL +#define HB_MAX_NESTING_LEVEL 64 +#endif + + +#ifndef HB_MAX_CONTEXT_LENGTH +#define HB_MAX_CONTEXT_LENGTH 64 +#endif + +#ifndef HB_CLOSURE_MAX_STAGES +/* + * The maximum number of times a lookup can be applied during shaping. + * Used to limit the number of iterations of the closure algorithm. + * This must be larger than the number of times add_gsub_pause() is + * called in a collect_features call of any shaper. + */ +#define HB_CLOSURE_MAX_STAGES 12 +#endif + +#ifndef HB_MAX_SCRIPTS +#define HB_MAX_SCRIPTS 500 +#endif + +#ifndef HB_MAX_LANGSYS +#define HB_MAX_LANGSYS 2000 +#endif + +#ifndef HB_MAX_LANGSYS_FEATURE_COUNT +#define HB_MAX_LANGSYS_FEATURE_COUNT 50000 +#endif + +#ifndef HB_MAX_FEATURE_INDICES +#define HB_MAX_FEATURE_INDICES 1500 +#endif + +#ifndef HB_MAX_LOOKUP_VISIT_COUNT +#define HB_MAX_LOOKUP_VISIT_COUNT 35000 +#endif + + +#ifndef HB_GLYF_MAX_POINTS +#define HB_GLYF_MAX_POINTS 20000 +#endif + +#ifndef HB_GLYF_MAX_EDGE_COUNT +#define HB_GLYF_MAX_EDGE_COUNT 1024 +#endif + +#ifndef HB_CFF_MAX_OPS +#define HB_CFF_MAX_OPS 10000 +#endif + +#ifndef HB_COLRV1_MAX_EDGE_COUNT +#define HB_COLRV1_MAX_EDGE_COUNT 1024 +#endif + + +#endif /* HB_LIMITS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-machinery.hh b/src/3rdparty/harfbuzz-ng/src/hb-machinery.hh new file mode 100644 index 00000000000..1084725af2a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-machinery.hh @@ -0,0 +1,325 @@ +/* + * Copyright ยฉ 2007,2008,2009,2010 Red Hat, Inc. + * Copyright ยฉ 2012,2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_MACHINERY_HH +#define HB_MACHINERY_HH + +#include "hb.hh" +#include "hb-blob.hh" + +#include "hb-dispatch.hh" +#include "hb-sanitize.hh" + + +/* + * Casts + */ + +/* StructAtOffset(P,Ofs) returns the struct T& that is placed at memory + * location pointed to by P plus Ofs bytes. */ +template +static inline const Type& StructAtOffset(const void *P, unsigned int offset) +{ return * reinterpret_cast ((const char *) P + offset); } +template +static inline Type& StructAtOffset(void *P, unsigned int offset) +{ return * reinterpret_cast ((char *) P + offset); } +template +static inline const Type& StructAtOffsetUnaligned(const void *P, unsigned int offset) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return * reinterpret_cast ((const char *) P + offset); +#pragma GCC diagnostic pop +} +template +static inline Type& StructAtOffsetUnaligned(void *P, unsigned int offset) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return * reinterpret_cast ((char *) P + offset); +#pragma GCC diagnostic pop +} + +/* StructAfter(X) returns the struct T& that is placed after X. + * Works with X of variable size also. X must implement get_size() */ +template +static inline const Type& StructAfter(const TObject &X) +{ return StructAtOffset(&X, X.get_size()); } +template +static inline Type& StructAfter(TObject &X) +{ return StructAtOffset(&X, X.get_size()); } + + +/* + * Size checking + */ + +/* Size signifying variable-sized array */ +#ifndef HB_VAR_ARRAY +#define HB_VAR_ARRAY 1 +#endif + +/* Check _assertion in a method environment */ +#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \ + void _instance_assertion_on_line_##_line () const \ + { static_assert ((_assertion), ""); } +# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion) +# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion) + +/* Check that _code compiles in a method environment */ +#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \ + void _compiles_assertion_on_line_##_line () const \ + { _code; } +# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code) +# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code) + + +#define DEFINE_SIZE_STATIC(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)) \ + unsigned int get_size () const { return (size); } \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size); \ + static constexpr unsigned static_size = (size) + +#define DEFINE_SIZE_UNION(size, _member) \ + DEFINE_COMPILES_ASSERTION ((void) this->u._member.static_size) \ + DEFINE_INSTANCE_ASSERTION (sizeof(this->u._member) == (size)) \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_MIN(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_UNBOUNDED(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_ARRAY(size, array) \ + DEFINE_COMPILES_ASSERTION ((void) (array)[0].static_size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + (HB_VAR_ARRAY+0) * sizeof ((array)[0])) \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_ARRAY_SIZED(size, array) \ + unsigned int get_size () const { return (size - (array).min_size + (array).get_size ()); } \ + DEFINE_SIZE_ARRAY(size, array) + + + +/* + * Lazy loaders. + * + * The lazy-loaders are thread-safe pointer-like objects that create their + * instead on-demand. They also support access to a "data" object that is + * necessary for creating their instance. The data object, if specified, + * is accessed via pointer math, located at a location before the position + * of the loader itself. This avoids having to store a pointer to data + * for every lazy-loader. Multiple lazy-loaders can access the same data. + */ + +template +struct hb_data_wrapper_t +{ + static_assert (WheresData > 0, ""); + + Data * get_data () const + { return *(((Data **) (void *) this) - WheresData); } + + bool is_inert () const { return !get_data (); } + + template + Stored * call_create () const { return Subclass::create (get_data ()); } +}; +template <> +struct hb_data_wrapper_t +{ + bool is_inert () const { return false; } + + template + Stored * call_create () const { return Funcs::create (); } +}; + +template struct hb_non_void_t { typedef T1 value; }; +template struct hb_non_void_t { typedef T2 value; }; + +template +struct hb_lazy_loader_t : hb_data_wrapper_t +{ + typedef typename hb_non_void_t + >::value Funcs; + + void init0 () {} /* Init, when memory is already set to 0. No-op for us. */ + void init () { instance.set_relaxed (nullptr); } + void fini () { do_destroy (instance.get_acquire ()); init (); } + + void free_instance () + { + retry: + Stored *p = instance.get_acquire (); + if (unlikely (p && !cmpexch (p, nullptr))) + goto retry; + do_destroy (p); + } + + static void do_destroy (Stored *p) + { + if (p && p != const_cast (Funcs::get_null ())) + Funcs::destroy (p); + } + + const Returned * operator -> () const { return get (); } + template + const U & operator * () const { return *get (); } + explicit operator bool () const + { return get_stored () != Funcs::get_null (); } + template operator const C * () const { return get (); } + + Stored * get_stored () const + { + retry: + Stored *p = this->instance.get_acquire (); + if (unlikely (!p)) + { + if (unlikely (this->is_inert ())) + return const_cast (Funcs::get_null ()); + + p = this->template call_create (); + if (unlikely (!p)) + p = const_cast (Funcs::get_null ()); + + if (unlikely (!cmpexch (nullptr, p))) + { + do_destroy (p); + goto retry; + } + } + return p; + } + Stored * get_stored_relaxed () const + { + return this->instance.get_relaxed (); + } + + bool cmpexch (Stored *current, Stored *value) const + { + /* This function can only be safely called directly if no + * other thread is accessing. */ + return this->instance.cmpexch (current, value); + } + + const Returned * get () const { return Funcs::convert (get_stored ()); } + const Returned * get_relaxed () const { return Funcs::convert (get_stored_relaxed ()); } + Returned * get_unconst () const { return const_cast (Funcs::convert (get_stored ())); } + + /* To be possibly overloaded by subclasses. */ + static Returned* convert (Stored *p) { return p; } + + /* By default null/init/fini the object. */ + static const Stored* get_null () { return &Null (Stored); } + static Stored *create (Data *data) + { + Stored *p = (Stored *) hb_calloc (1, sizeof (Stored)); + if (likely (p)) + p = new (p) Stored (data); + return p; + } + static Stored *create () + { + Stored *p = (Stored *) hb_calloc (1, sizeof (Stored)); + if (likely (p)) + p = new (p) Stored (); + return p; + } + static void destroy (Stored *p) + { + p->~Stored (); + hb_free (p); + } + + private: + /* Must only have one pointer. */ + hb_atomic_ptr_t instance; +}; + +/* Specializations. */ + +template +struct hb_face_lazy_loader_t : hb_lazy_loader_t, + hb_face_t, WheresFace> {}; + +template +struct hb_table_lazy_loader_t : hb_lazy_loader_t, + hb_face_t, WheresFace, + hb_blob_t> +{ + static hb_blob_t *create (hb_face_t *face) + { + auto c = hb_sanitize_context_t (); + if (core) + c.set_num_glyphs (0); // So we don't recurse ad infinitum, or doesn't need num_glyphs + return c.reference_table (face); + } + static void destroy (hb_blob_t *p) { hb_blob_destroy (p); } + + static const hb_blob_t *get_null () + { return hb_blob_get_empty (); } + + static const T* convert (const hb_blob_t *blob) + { return blob->as (); } + + hb_blob_t* get_blob () const { return this->get_stored (); } +}; + +#define HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T(Type) \ + template \ + struct hb_##Type##_funcs_lazy_loader_t : hb_lazy_loader_t \ + { \ + static void destroy (hb_##Type##_funcs_t *p) \ + { hb_##Type##_funcs_destroy (p); } \ + static const hb_##Type##_funcs_t *get_null () \ + { return hb_##Type##_funcs_get_empty (); } \ + } + +HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (font); +HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (unicode); +HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (draw); +HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (paint); + +#undef HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T + + +#endif /* HB_MACHINERY_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-map.cc b/src/3rdparty/harfbuzz-ng/src/hb-map.cc new file mode 100644 index 00000000000..5d67cd9a12d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-map.cc @@ -0,0 +1,419 @@ +/* + * Copyright ยฉ 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-map.hh" + + +/** + * SECTION:hb-map + * @title: hb-map + * @short_description: Object representing integer to integer mapping + * @include: hb.h + * + * Map objects are integer-to-integer hash-maps. Currently they are + * not used in the HarfBuzz public API, but are provided for client's + * use if desired. + **/ + + +/** + * hb_map_create: + * + * Creates a new, initially empty map. + * + * Return value: (transfer full): The new #hb_map_t + * + * Since: 1.7.7 + **/ +hb_map_t * +hb_map_create () +{ + hb_map_t *map; + + if (!(map = hb_object_create ())) + return hb_map_get_empty (); + + return map; +} + +/** + * hb_map_get_empty: + * + * Fetches the singleton empty #hb_map_t. + * + * Return value: (transfer full): The empty #hb_map_t + * + * Since: 1.7.7 + **/ +hb_map_t * +hb_map_get_empty () +{ + return const_cast (&Null (hb_map_t)); +} + +/** + * hb_map_reference: (skip) + * @map: A map + * + * Increases the reference count on a map. + * + * Return value: (transfer full): The map + * + * Since: 1.7.7 + **/ +hb_map_t * +hb_map_reference (hb_map_t *map) +{ + return hb_object_reference (map); +} + +/** + * hb_map_destroy: (skip) + * @map: A map + * + * Decreases the reference count on a map. When + * the reference count reaches zero, the map is + * destroyed, freeing all memory. + * + * Since: 1.7.7 + **/ +void +hb_map_destroy (hb_map_t *map) +{ + if (!hb_object_destroy (map)) return; + + hb_free (map); +} + +/** + * hb_map_set_user_data: (skip) + * @map: A map + * @key: The user-data key to set + * @data: A pointer to the user data to set + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified map. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_set_user_data (hb_map_t *map, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (map, key, data, destroy, replace); +} + +/** + * hb_map_get_user_data: (skip) + * @map: A map + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified map. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 1.7.7 + **/ +void * +hb_map_get_user_data (const hb_map_t *map, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (map, key); +} + + +/** + * hb_map_allocation_successful: + * @map: A map + * + * Tests whether memory allocation for a set was successful. + * + * Return value: `true` if allocation succeeded, `false` otherwise + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_allocation_successful (const hb_map_t *map) +{ + return map->successful; +} + +/** + * hb_map_copy: + * @map: A map + * + * Allocate a copy of @map. + * + * Return value: (transfer full): Newly-allocated map. + * + * Since: 4.4.0 + **/ +hb_map_t * +hb_map_copy (const hb_map_t *map) +{ + hb_map_t *copy = hb_map_create (); + if (unlikely (copy->in_error ())) + return hb_map_get_empty (); + + *copy = *map; + return copy; +} + +/** + * hb_map_set: + * @map: A map + * @key: The key to store in the map + * @value: The value to store for @key + * + * Stores @key:@value in the map. + * + * Since: 1.7.7 + **/ +void +hb_map_set (hb_map_t *map, + hb_codepoint_t key, + hb_codepoint_t value) +{ + /* Immutable-safe. */ + map->set (key, value); +} + +/** + * hb_map_get: + * @map: A map + * @key: The key to query + * + * Fetches the value stored for @key in @map. + * + * Since: 1.7.7 + **/ +hb_codepoint_t +hb_map_get (const hb_map_t *map, + hb_codepoint_t key) +{ + return map->get (key); +} + +/** + * hb_map_del: + * @map: A map + * @key: The key to delete + * + * Removes @key and its stored value from @map. + * + * Since: 1.7.7 + **/ +void +hb_map_del (hb_map_t *map, + hb_codepoint_t key) +{ + /* Immutable-safe. */ + map->del (key); +} + +/** + * hb_map_has: + * @map: A map + * @key: The key to query + * + * Tests whether @key is an element of @map. + * + * Return value: `true` if @key is found in @map, `false` otherwise + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_has (const hb_map_t *map, + hb_codepoint_t key) +{ + return map->has (key); +} + + +/** + * hb_map_clear: + * @map: A map + * + * Clears out the contents of @map. + * + * Since: 1.7.7 + **/ +void +hb_map_clear (hb_map_t *map) +{ + return map->clear (); +} + +/** + * hb_map_is_empty: + * @map: A map + * + * Tests whether @map is empty (contains no elements). + * + * Return value: `true` if @map is empty + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_is_empty (const hb_map_t *map) +{ + return map->is_empty (); +} + +/** + * hb_map_get_population: + * @map: A map + * + * Returns the number of key-value pairs in the map. + * + * Return value: The population of @map + * + * Since: 1.7.7 + **/ +unsigned int +hb_map_get_population (const hb_map_t *map) +{ + return map->get_population (); +} + +/** + * hb_map_is_equal: + * @map: A map + * @other: Another map + * + * Tests whether @map and @other are equal (contain the same + * elements). + * + * Return value: `true` if the two maps are equal, `false` otherwise. + * + * Since: 4.3.0 + **/ +hb_bool_t +hb_map_is_equal (const hb_map_t *map, + const hb_map_t *other) +{ + return map->is_equal (*other); +} + +/** + * hb_map_hash: + * @map: A map + * + * Creates a hash representing @map. + * + * Return value: + * A hash of @map. + * + * Since: 4.4.0 + **/ +unsigned int +hb_map_hash (const hb_map_t *map) +{ + return map->hash (); +} + +/** + * hb_map_update: + * @map: A map + * @other: Another map + * + * Add the contents of @other to @map. + * + * Since: 7.0.0 + **/ +HB_EXTERN void +hb_map_update (hb_map_t *map, + const hb_map_t *other) +{ + map->update (*other); +} + +/** + * hb_map_next: + * @map: A map + * @idx: (inout): Iterator internal state + * @key: (out): Key retrieved + * @value: (out): Value retrieved + * + * Fetches the next key/value paire in @map. + * + * Set @idx to -1 to get started. + * + * If the map is modified during iteration, the behavior is undefined. + * + * The order in which the key/values are returned is undefined. + * + * Return value: `true` if there was a next value, `false` otherwise + * + * Since: 7.0.0 + **/ +hb_bool_t +hb_map_next (const hb_map_t *map, + int *idx, + hb_codepoint_t *key, + hb_codepoint_t *value) +{ + return map->next (idx, key, value); +} + +/** + * hb_map_keys: + * @map: A map + * @keys: A set + * + * Add the keys of @map to @keys. + * + * Since: 7.0.0 + **/ +void +hb_map_keys (const hb_map_t *map, + hb_set_t *keys) +{ + hb_copy (map->keys() , *keys); +} + +/** + * hb_map_values: + * @map: A map + * @values: A set + * + * Add the values of @map to @values. + * + * Since: 7.0.0 + **/ +void +hb_map_values (const hb_map_t *map, + hb_set_t *values) +{ + hb_copy (map->values() , *values); +} diff --git a/src/3rdparty/harfbuzz-ng/src/hb-map.h b/src/3rdparty/harfbuzz-ng/src/hb-map.h new file mode 100644 index 00000000000..e928628fa7a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-map.h @@ -0,0 +1,143 @@ +/* + * Copyright ยฉ 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_MAP_H +#define HB_MAP_H + +#include "hb-common.h" +#include "hb-set.h" + +HB_BEGIN_DECLS + + +/** + * HB_MAP_VALUE_INVALID: + * + * Unset #hb_map_t value. + * + * Since: 1.7.7 + */ +#define HB_MAP_VALUE_INVALID ((hb_codepoint_t) -1) + +/** + * hb_map_t: + * + * Data type for holding integer-to-integer hash maps. + * + **/ +typedef struct hb_map_t hb_map_t; + + +HB_EXTERN hb_map_t * +hb_map_create (void); + +HB_EXTERN hb_map_t * +hb_map_get_empty (void); + +HB_EXTERN hb_map_t * +hb_map_reference (hb_map_t *map); + +HB_EXTERN void +hb_map_destroy (hb_map_t *map); + +HB_EXTERN hb_bool_t +hb_map_set_user_data (hb_map_t *map, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_map_get_user_data (const hb_map_t *map, + hb_user_data_key_t *key); + + +/* Returns false if allocation has failed before */ +HB_EXTERN hb_bool_t +hb_map_allocation_successful (const hb_map_t *map); + +HB_EXTERN hb_map_t * +hb_map_copy (const hb_map_t *map); + +HB_EXTERN void +hb_map_clear (hb_map_t *map); + +HB_EXTERN hb_bool_t +hb_map_is_empty (const hb_map_t *map); + +HB_EXTERN unsigned int +hb_map_get_population (const hb_map_t *map); + +HB_EXTERN hb_bool_t +hb_map_is_equal (const hb_map_t *map, + const hb_map_t *other); + +HB_EXTERN unsigned int +hb_map_hash (const hb_map_t *map); + +HB_EXTERN void +hb_map_set (hb_map_t *map, + hb_codepoint_t key, + hb_codepoint_t value); + +HB_EXTERN hb_codepoint_t +hb_map_get (const hb_map_t *map, + hb_codepoint_t key); + +HB_EXTERN void +hb_map_del (hb_map_t *map, + hb_codepoint_t key); + +HB_EXTERN hb_bool_t +hb_map_has (const hb_map_t *map, + hb_codepoint_t key); + +HB_EXTERN void +hb_map_update (hb_map_t *map, + const hb_map_t *other); + +/* Pass -1 in for idx to get started. */ +HB_EXTERN hb_bool_t +hb_map_next (const hb_map_t *map, + int *idx, + hb_codepoint_t *key, + hb_codepoint_t *value); + +HB_EXTERN void +hb_map_keys (const hb_map_t *map, + hb_set_t *keys); + +HB_EXTERN void +hb_map_values (const hb_map_t *map, + hb_set_t *values); + +HB_END_DECLS + +#endif /* HB_MAP_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-map.hh b/src/3rdparty/harfbuzz-ng/src/hb-map.hh new file mode 100644 index 00000000000..041b8829afb --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-map.hh @@ -0,0 +1,490 @@ +/* + * Copyright ยฉ 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_MAP_HH +#define HB_MAP_HH + +#include "hb.hh" + +#include "hb-set.hh" + + +/* + * hb_hashmap_t + */ + +extern HB_INTERNAL const hb_codepoint_t minus_1; + +template +struct hb_hashmap_t +{ + hb_hashmap_t () { init (); } + ~hb_hashmap_t () { fini (); } + + hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { resize (o.population); hb_copy (o, *this); } + hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); } + hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); resize (o.population); hb_copy (o, *this); return *this; } + hb_hashmap_t& operator= (hb_hashmap_t&& o) { hb_swap (*this, o); return *this; } + + hb_hashmap_t (std::initializer_list> lst) : hb_hashmap_t () + { + for (auto&& item : lst) + set (item.first, item.second); + } + template + hb_hashmap_t (const Iterable &o) : hb_hashmap_t () + { + auto iter = hb_iter (o); + if (iter.is_random_access_iterator) + resize (hb_len (iter)); + hb_copy (iter, *this); + } + + struct item_t + { + K key; + uint32_t hash : 30; + uint32_t is_used_ : 1; + uint32_t is_tombstone_ : 1; + V value; + + item_t () : key (), + hash (0), + is_used_ (false), is_tombstone_ (false), + value () {} + + bool is_used () const { return is_used_; } + void set_used (bool is_used) { is_used_ = is_used; } + bool is_tombstone () const { return is_tombstone_; } + void set_tombstone (bool is_tombstone) { is_tombstone_ = is_tombstone; } + bool is_real () const { return is_used_ && !is_tombstone_; } + + template + static inline const V& default_value () { return Null(V); }; + template + static inline const V& default_value () + { + static_assert (hb_is_same (V, hb_codepoint_t), ""); + return minus_1; + }; + + bool operator == (const K &o) const { return hb_deref (key) == hb_deref (o); } + bool operator == (const item_t &o) const { return *this == o.key; } + hb_pair_t get_pair() const { return hb_pair_t (key, value); } + hb_pair_t get_pair_ref() const { return hb_pair_t (key, value); } + + uint32_t total_hash () const + { return (hash * 31) + hb_hash (value); } + }; + + hb_object_header_t header; + unsigned int successful : 1; /* Allocations successful */ + unsigned int population : 31; /* Not including tombstones. */ + unsigned int occupancy; /* Including tombstones. */ + unsigned int mask; + unsigned int prime; + item_t *items; + + friend void swap (hb_hashmap_t& a, hb_hashmap_t& b) + { + if (unlikely (!a.successful || !b.successful)) + return; + unsigned tmp = a.population; + a.population = b.population; + b.population = tmp; + //hb_swap (a.population, b.population); + hb_swap (a.occupancy, b.occupancy); + hb_swap (a.mask, b.mask); + hb_swap (a.prime, b.prime); + hb_swap (a.items, b.items); + } + void init () + { + hb_object_init (this); + + successful = true; + population = occupancy = 0; + mask = 0; + prime = 0; + items = nullptr; + } + void fini () + { + hb_object_fini (this); + + if (likely (items)) { + unsigned size = mask + 1; + for (unsigned i = 0; i < size; i++) + items[i].~item_t (); + hb_free (items); + items = nullptr; + } + population = occupancy = 0; + } + + void reset () + { + successful = true; + clear (); + } + + bool in_error () const { return !successful; } + + bool resize (unsigned new_population = 0) + { + if (unlikely (!successful)) return false; + + if (new_population != 0 && (new_population + new_population / 2) < mask) return true; + + unsigned int power = hb_bit_storage (hb_max ((unsigned) population, new_population) * 2 + 8); + unsigned int new_size = 1u << power; + item_t *new_items = (item_t *) hb_malloc ((size_t) new_size * sizeof (item_t)); + if (unlikely (!new_items)) + { + successful = false; + return false; + } + for (auto &_ : hb_iter (new_items, new_size)) + new (&_) item_t (); + + unsigned int old_size = size (); + item_t *old_items = items; + + /* Switch to new, empty, array. */ + population = occupancy = 0; + mask = new_size - 1; + prime = prime_for (power); + items = new_items; + + /* Insert back old items. */ + for (unsigned int i = 0; i < old_size; i++) + { + if (old_items[i].is_real ()) + { + set_with_hash (std::move (old_items[i].key), + old_items[i].hash, + std::move (old_items[i].value)); + } + old_items[i].~item_t (); + } + + hb_free (old_items); + + return true; + } + + template + bool set_with_hash (KK&& key, uint32_t hash, VV&& value, bool is_delete=false) + { + if (unlikely (!successful)) return false; + if (unlikely ((occupancy + occupancy / 2) >= mask && !resize ())) return false; + item_t &item = item_for_hash (key, hash); + + if (is_delete && !(item == key)) + return true; /* Trying to delete non-existent key. */ + + if (item.is_used ()) + { + occupancy--; + if (!item.is_tombstone ()) + population--; + } + + item.key = std::forward (key); + item.value = std::forward (value); + item.hash = hash; + item.set_used (true); + item.set_tombstone (is_delete); + + occupancy++; + if (!is_delete) + population++; + + return true; + } + + template + bool set (const K &key, VV&& value) { return set_with_hash (key, hb_hash (key), std::forward (value)); } + template + bool set (K &&key, VV&& value) { return set_with_hash (std::move (key), hb_hash (key), std::forward (value)); } + + const V& get_with_hash (const K &key, uint32_t hash) const + { + if (unlikely (!items)) return item_t::default_value (); + auto &item = item_for_hash (key, hash); + return item.is_real () && item == key ? item.value : item_t::default_value (); + } + const V& get (const K &key) const + { + if (unlikely (!items)) return item_t::default_value (); + return get_with_hash (key, hb_hash (key)); + } + + void del (const K &key) { set_with_hash (key, hb_hash (key), item_t::default_value (), true); } + + /* Has interface. */ + const V& operator [] (K k) const { return get (k); } + template + bool has (K key, VV **vp = nullptr) const + { + if (unlikely (!items)) + return false; + auto &item = item_for_hash (key, hb_hash (key)); + if (item.is_real () && item == key) + { + if (vp) *vp = std::addressof (item.value); + return true; + } + else + return false; + } + /* Projection. */ + V operator () (K k) const { return get (k); } + + unsigned size () const { return mask ? mask + 1 : 0; } + + void clear () + { + if (unlikely (!successful)) return; + + for (auto &_ : hb_iter (items, size ())) + { + /* Reconstruct items. */ + _.~item_t (); + new (&_) item_t (); + } + + population = occupancy = 0; + } + + bool is_empty () const { return population == 0; } + explicit operator bool () const { return !is_empty (); } + + uint32_t hash () const + { + return + + iter_items () + | hb_reduce ([] (uint32_t h, const item_t &_) { return h ^ _.total_hash (); }, (uint32_t) 0u) + ; + } + + bool is_equal (const hb_hashmap_t &other) const + { + if (population != other.population) return false; + + for (auto pair : iter ()) + if (other.get (pair.first) != pair.second) + return false; + + return true; + } + bool operator == (const hb_hashmap_t &other) const { return is_equal (other); } + bool operator != (const hb_hashmap_t &other) const { return !is_equal (other); } + + unsigned int get_population () const { return population; } + + void update (const hb_hashmap_t &other) + { + if (unlikely (!successful)) return; + + hb_copy (other, *this); + } + + /* + * Iterator + */ + + auto iter_items () const HB_AUTO_RETURN + ( + + hb_iter (items, size ()) + | hb_filter (&item_t::is_real) + ) + auto iter_ref () const HB_AUTO_RETURN + ( + + iter_items () + | hb_map (&item_t::get_pair_ref) + ) + auto iter () const HB_AUTO_RETURN + ( + + iter_items () + | hb_map (&item_t::get_pair) + ) + auto keys_ref () const HB_AUTO_RETURN + ( + + iter_items () + | hb_map (&item_t::key) + ) + auto keys () const HB_AUTO_RETURN + ( + + iter_items () + | hb_map (&item_t::key) + | hb_map (hb_ridentity) + ) + auto values_ref () const HB_AUTO_RETURN + ( + + iter_items () + | hb_map (&item_t::value) + ) + auto values () const HB_AUTO_RETURN + ( + + iter_items () + | hb_map (&item_t::value) + | hb_map (hb_ridentity) + ) + + /* C iterator. */ + bool next (int *idx, + K *key, + V *value) const + { + unsigned i = (unsigned) (*idx + 1); + + unsigned count = size (); + while (i < count && !items[i].is_real ()) + i++; + + if (i >= count) + { + *idx = -1; + return false; + } + + *key = items[i].key; + *value = items[i].value; + + *idx = (signed) i; + return true; + } + + /* Sink interface. */ + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (v.first, v.second); return *this; } + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (v.first, std::move (v.second)); return *this; } + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (std::move (v.first), v.second); return *this; } + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (std::move (v.first), std::move (v.second)); return *this; } + + item_t& item_for_hash (const K &key, uint32_t hash) const + { + hash &= 0x3FFFFFFF; // We only store lower 30bit of hash + unsigned int i = hash % prime; + unsigned int step = 0; + unsigned int tombstone = (unsigned) -1; + while (items[i].is_used ()) + { + if (items[i].hash == hash && items[i] == key) + return items[i]; + if (tombstone == (unsigned) -1 && items[i].is_tombstone ()) + tombstone = i; + i = (i + ++step) & mask; + } + return items[tombstone == (unsigned) -1 ? i : tombstone]; + } + + static unsigned int prime_for (unsigned int shift) + { + /* Following comment and table copied from glib. */ + /* Each table size has an associated prime modulo (the first prime + * lower than the table size) used to find the initial bucket. Probing + * then works modulo 2^n. The prime modulo is necessary to get a + * good distribution with poor hash functions. + */ + /* Not declaring static to make all kinds of compilers happy... */ + /*static*/ const unsigned int prime_mod [32] = + { + 1, /* For 1 << 0 */ + 2, + 3, + 7, + 13, + 31, + 61, + 127, + 251, + 509, + 1021, + 2039, + 4093, + 8191, + 16381, + 32749, + 65521, /* For 1 << 16 */ + 131071, + 262139, + 524287, + 1048573, + 2097143, + 4194301, + 8388593, + 16777213, + 33554393, + 67108859, + 134217689, + 268435399, + 536870909, + 1073741789, + 2147483647 /* For 1 << 31 */ + }; + + if (unlikely (shift >= ARRAY_LENGTH (prime_mod))) + return prime_mod[ARRAY_LENGTH (prime_mod) - 1]; + + return prime_mod[shift]; + } +}; + +/* + * hb_map_t + */ + +struct hb_map_t : hb_hashmap_t +{ + using hashmap = hb_hashmap_t; + + ~hb_map_t () = default; + hb_map_t () : hashmap () {} + hb_map_t (const hb_map_t &o) : hashmap ((hashmap &) o) {} + hb_map_t (hb_map_t &&o) : hashmap (std::move ((hashmap &) o)) {} + hb_map_t& operator= (const hb_map_t&) = default; + hb_map_t& operator= (hb_map_t&&) = default; + hb_map_t (std::initializer_list> lst) : hashmap (lst) {} + template + hb_map_t (const Iterable &o) : hashmap (o) {} +}; + + +#endif /* HB_MAP_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-meta.hh b/src/3rdparty/harfbuzz-ng/src/hb-meta.hh new file mode 100644 index 00000000000..31aa7fa6f16 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-meta.hh @@ -0,0 +1,238 @@ +/* + * Copyright ยฉ 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_META_HH +#define HB_META_HH + +#include "hb.hh" + +#include +#include +#include + + +/* + * C++ template meta-programming & fundamentals used with them. + */ + +/* Void! For when we need a expression-type of void. */ +struct hb_empty_t {}; + +/* https://en.cppreference.com/w/cpp/types/void_t */ +template struct _hb_void_t { typedef void type; }; +template using hb_void_t = typename _hb_void_t::type; + +template struct _hb_head_t { typedef Head type; }; +template using hb_head_t = typename _hb_head_t::type; + +template struct hb_integral_constant { static constexpr T value = v; }; +template using hb_bool_constant = hb_integral_constant; +using hb_true_type = hb_bool_constant; +using hb_false_type = hb_bool_constant; + +/* Static-assert as expression. */ +template struct static_assert_expr; +template <> struct static_assert_expr : hb_false_type {}; +#define static_assert_expr(C) static_assert_expr::value + +/* Basic type SFINAE. */ + +template struct hb_enable_if {}; +template struct hb_enable_if { typedef T type; }; +#define hb_enable_if(Cond) typename hb_enable_if<(Cond)>::type* = nullptr +/* Concepts/Requires alias: */ +#define hb_requires(Cond) hb_enable_if((Cond)) + +template struct hb_is_same : hb_false_type {}; +template struct hb_is_same : hb_true_type {}; +#define hb_is_same(T, T2) hb_is_same::value + +/* Function overloading SFINAE and priority. */ + +#define HB_RETURN(Ret, E) -> hb_head_t { return (E); } +#define HB_AUTO_RETURN(E) -> decltype ((E)) { return (E); } +#define HB_VOID_RETURN(E) -> hb_void_t { (E); } + +template struct hb_priority : hb_priority {}; +template <> struct hb_priority<0> {}; +#define hb_prioritize hb_priority<16> () + +#define HB_FUNCOBJ(x) static_const x HB_UNUSED + + +template struct hb_type_identity_t { typedef T type; }; +template using hb_type_identity = typename hb_type_identity_t::type; + +template static inline T hb_declval (); +#define hb_declval(T) (hb_declval ()) + +template struct hb_match_const : hb_type_identity_t, hb_false_type {}; +template struct hb_match_const : hb_type_identity_t, hb_true_type {}; +template using hb_remove_const = typename hb_match_const::type; + +template struct hb_match_reference : hb_type_identity_t, hb_false_type {}; +template struct hb_match_reference : hb_type_identity_t, hb_true_type {}; +template struct hb_match_reference : hb_type_identity_t, hb_true_type {}; +template using hb_remove_reference = typename hb_match_reference::type; +template auto _hb_try_add_lvalue_reference (hb_priority<1>) -> hb_type_identity; +template auto _hb_try_add_lvalue_reference (hb_priority<0>) -> hb_type_identity; +template using hb_add_lvalue_reference = decltype (_hb_try_add_lvalue_reference (hb_prioritize)); +template auto _hb_try_add_rvalue_reference (hb_priority<1>) -> hb_type_identity; +template auto _hb_try_add_rvalue_reference (hb_priority<0>) -> hb_type_identity; +template using hb_add_rvalue_reference = decltype (_hb_try_add_rvalue_reference (hb_prioritize)); + +template struct hb_match_pointer : hb_type_identity_t, hb_false_type {}; +template struct hb_match_pointer : hb_type_identity_t, hb_true_type {}; +template using hb_remove_pointer = typename hb_match_pointer::type; +template auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity*>; +template auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity; +template using hb_add_pointer = decltype (_hb_try_add_pointer (hb_prioritize)); + + +template using hb_decay = typename std::decay::type; + +#define hb_is_convertible(From,To) std::is_convertible::value + +template +using hb_is_cr_convertible = hb_bool_constant< + hb_is_same (hb_decay, hb_decay) && + (!std::is_const::value || std::is_const::value) && + (!std::is_reference::value || std::is_const::value || std::is_reference::value) +>; +#define hb_is_cr_convertible(From,To) hb_is_cr_convertible::value + + +struct +{ + template constexpr auto + operator () (T&& v) const HB_AUTO_RETURN (std::forward (v)) + + template constexpr auto + operator () (T *v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (const hb::shared_ptr& v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (hb::shared_ptr& v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (const hb::unique_ptr& v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (hb::unique_ptr& v) const HB_AUTO_RETURN (*v) +} +HB_FUNCOBJ (hb_deref); + +template +struct hb_reference_wrapper +{ + hb_reference_wrapper (T v) : v (v) {} + bool operator == (const hb_reference_wrapper& o) const { return v == o.v; } + bool operator != (const hb_reference_wrapper& o) const { return v != o.v; } + operator T () const { return v; } + T get () const { return v; } + T v; +}; +template +struct hb_reference_wrapper +{ + hb_reference_wrapper (T& v) : v (std::addressof (v)) {} + bool operator == (const hb_reference_wrapper& o) const { return v == o.v; } + bool operator != (const hb_reference_wrapper& o) const { return v != o.v; } + operator T& () const { return *v; } + T& get () const { return *v; } + T* v; +}; + + +/* Type traits */ + +template struct hb_int_min; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template struct hb_int_min : hb_integral_constant {}; +#define hb_int_min(T) hb_int_min::value +template struct hb_int_max; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +#define hb_int_max(T) hb_int_max::value + +#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__) +#define hb_is_trivially_copyable(T) __has_trivial_copy(T) +#define hb_is_trivially_copy_assignable(T) __has_trivial_assign(T) +#define hb_is_trivially_constructible(T) __has_trivial_constructor(T) +#define hb_is_trivially_copy_constructible(T) __has_trivial_copy_constructor(T) +#define hb_is_trivially_destructible(T) __has_trivial_destructor(T) +#else +#define hb_is_trivially_copyable(T) std::is_trivially_copyable::value +#define hb_is_trivially_copy_assignable(T) std::is_trivially_copy_assignable::value +#define hb_is_trivially_constructible(T) std::is_trivially_constructible::value +#define hb_is_trivially_copy_constructible(T) std::is_trivially_copy_constructible::value +#define hb_is_trivially_destructible(T) std::is_trivially_destructible::value +#endif + +/* Class traits. */ + +#define HB_DELETE_COPY_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete +#define HB_DELETE_CREATE_COPY_ASSIGN(TypeName) \ + TypeName() = delete; \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete + +/* hb_unwrap_type (T) + * If T has no T::type, returns T. Otherwise calls itself on T::type recursively. + */ + +template +struct _hb_unwrap_type : hb_type_identity_t {}; +template +struct _hb_unwrap_type> : _hb_unwrap_type {}; +template +using hb_unwrap_type = _hb_unwrap_type; +#define hb_unwrap_type(T) typename hb_unwrap_type::type + +#endif /* HB_META_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ms-feature-ranges.hh b/src/3rdparty/harfbuzz-ng/src/hb-ms-feature-ranges.hh new file mode 100644 index 00000000000..f7649ab76e4 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ms-feature-ranges.hh @@ -0,0 +1,232 @@ +/* + * Copyright ยฉ 2011,2012,2013 Google, Inc. + * Copyright ยฉ 2021 Khaled Hosny + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_MS_FEATURE_RANGES_HH +#define HB_MS_FEATURE_RANGES_HH + +#include "hb.hh" + +/* Variations of this code exist in hb-coretext.cc as well + * as hb-aat-map.cc... */ + +typedef struct hb_ms_feature_t { + uint32_t tag_le; + uint32_t value; +} hb_ms_feature_t; + +typedef struct hb_ms_features_t { + hb_ms_feature_t *features; + uint32_t num_features; +} hb_ms_features_t; + +struct hb_ms_active_feature_t { + hb_ms_feature_t fea; + unsigned int order; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) { + const auto *a = (const hb_ms_active_feature_t *) pa; + const auto *b = (const hb_ms_active_feature_t *) pb; + return a->fea.tag_le < b->fea.tag_le ? -1 : a->fea.tag_le > b->fea.tag_le ? 1 : + a->order < b->order ? -1 : a->order > b->order ? 1 : + a->fea.value < b->fea.value ? -1 : a->fea.value > b->fea.value ? 1 : + 0; + } + bool operator== (const hb_ms_active_feature_t& f) const + { return cmp (this, &f) == 0; } +}; + +struct hb_ms_feature_event_t { + unsigned int index; + bool start; + hb_ms_active_feature_t feature; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const auto *a = (const hb_ms_feature_event_t *) pa; + const auto *b = (const hb_ms_feature_event_t *) pb; + return a->index < b->index ? -1 : a->index > b->index ? 1 : + a->start < b->start ? -1 : a->start > b->start ? 1 : + hb_ms_active_feature_t::cmp (&a->feature, &b->feature); + } +}; + +struct hb_ms_range_record_t { + hb_ms_features_t features; + unsigned int index_first; /* == start */ + unsigned int index_last; /* == end - 1 */ +}; + +static inline bool +hb_ms_setup_features (const hb_feature_t *features, + unsigned int num_features, + hb_vector_t &feature_records, /* OUT */ + hb_vector_t &range_records /* OUT */) +{ + feature_records.shrink(0); + range_records.shrink(0); + + /* Sort features by start/end events. */ + hb_vector_t feature_events; + for (unsigned int i = 0; i < num_features; i++) + { + hb_ms_active_feature_t feature; + feature.fea.tag_le = hb_uint32_swap (features[i].tag); + feature.fea.value = features[i].value; + feature.order = i; + + hb_ms_feature_event_t *event; + + event = feature_events.push (); + event->index = features[i].start; + event->start = true; + event->feature = feature; + + event = feature_events.push (); + event->index = features[i].end; + event->start = false; + event->feature = feature; + } + feature_events.qsort (); + /* Add a strategic final event. */ + { + hb_ms_active_feature_t feature; + feature.fea.tag_le = 0; + feature.fea.value = 0; + feature.order = num_features + 1; + + auto *event = feature_events.push (); + event->index = 0; /* This value does magic. */ + event->start = false; + event->feature = feature; + } + + /* Scan events and save features for each range. */ + hb_vector_t active_features; + unsigned int last_index = 0; + for (unsigned int i = 0; i < feature_events.length; i++) + { + auto *event = &feature_events[i]; + + if (event->index != last_index) + { + /* Save a snapshot of active features and the range. */ + auto *range = range_records.push (); + auto offset = feature_records.length; + + active_features.qsort (); + for (unsigned int j = 0; j < active_features.length; j++) + { + if (!j || active_features[j].fea.tag_le != feature_records[feature_records.length - 1].tag_le) + { + feature_records.push (active_features[j].fea); + } + else + { + /* Overrides value for existing feature. */ + feature_records[feature_records.length - 1].value = active_features[j].fea.value; + } + } + + /* Will convert to pointer after all is ready, since feature_records.array + * may move as we grow it. */ + range->features.features = reinterpret_cast (offset); + range->features.num_features = feature_records.length - offset; + range->index_first = last_index; + range->index_last = event->index - 1; + + last_index = event->index; + } + + if (event->start) + { + active_features.push (event->feature); + } + else + { + auto *feature = active_features.lsearch (event->feature); + if (feature) + active_features.remove_ordered (feature - active_features.arrayZ); + } + } + + if (!range_records.length) /* No active feature found. */ + num_features = 0; + + /* Fixup the pointers. */ + for (unsigned int i = 0; i < range_records.length; i++) + { + auto *range = &range_records[i]; + range->features.features = (hb_ms_feature_t *) feature_records + reinterpret_cast (range->features.features); + } + + return !!num_features; +} + +static inline void +hb_ms_make_feature_ranges (hb_vector_t &feature_records, + hb_vector_t &range_records, + unsigned int chars_offset, + unsigned int chars_len, + uint16_t *log_clusters, + hb_vector_t &range_features, /* OUT */ + hb_vector_t &range_counts /* OUT */) +{ + range_features.shrink (0); + range_counts.shrink (0); + + auto *last_range = &range_records[0]; + for (unsigned int i = chars_offset; i < chars_len; i++) + { + auto *range = last_range; + while (log_clusters[i] < range->index_first) + range--; + while (log_clusters[i] > range->index_last) + range++; + if (!range_features.length || + &range->features != range_features[range_features.length - 1]) + { + auto **features = range_features.push (); + auto *c = range_counts.push (); + if (unlikely (!features || !c)) + { + range_features.shrink (0); + range_counts.shrink (0); + break; + } + *features = &range->features; + *c = 1; + } + else + { + range_counts[range_counts.length - 1]++; + } + + last_range = range; + } +} + +#endif /* HB_MS_FEATURE_RANGES_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-multimap.hh b/src/3rdparty/harfbuzz-ng/src/hb-multimap.hh new file mode 100644 index 00000000000..b4a8cc62a3e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-multimap.hh @@ -0,0 +1,92 @@ +/* + * Copyright ยฉ 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_MULTIMAP_HH +#define HB_MULTIMAP_HH + +#include "hb.hh" +#include "hb-map.hh" +#include "hb-vector.hh" + + +/* + * hb_multimap_t + */ + +struct hb_multimap_t +{ + void add (hb_codepoint_t k, hb_codepoint_t v) + { + hb_codepoint_t *i; + if (multiples_indices.has (k, &i)) + { + multiples_values[*i].push (v); + return; + } + + hb_codepoint_t *old_v; + if (singulars.has (k, &old_v)) + { + hb_codepoint_t old = *old_v; + singulars.del (k); + + multiples_indices.set (k, multiples_values.length); + auto *vec = multiples_values.push (); + + vec->push (old); + vec->push (v); + + return; + } + + singulars.set (k, v); + } + + hb_array_t get (hb_codepoint_t k) const + { + const hb_codepoint_t *v; + if (singulars.has (k, &v)) + return hb_array (v, 1); + + hb_codepoint_t *i; + if (multiples_indices.has (k, &i)) + return multiples_values[*i].as_array (); + + return hb_array_t (); + } + + bool in_error () const + { + return singulars.in_error () || multiples_indices.in_error () || multiples_values.in_error (); + } + + protected: + hb_map_t singulars; + hb_map_t multiples_indices; + hb_vector_t> multiples_values; +}; + + + +#endif /* HB_MULTIMAP_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-mutex-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-mutex.hh similarity index 54% rename from src/3rdparty/harfbuzz-ng/src/hb-mutex-private.hh rename to src/3rdparty/harfbuzz-ng/src/hb-mutex.hh index 49ed10e08a6..e329d9864f1 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-mutex-private.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-mutex.hh @@ -29,18 +29,17 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_MUTEX_PRIVATE_HH -#define HB_MUTEX_PRIVATE_HH +#ifndef HB_MUTEX_HH +#define HB_MUTEX_HH -#include "hb-private.hh" +#include "hb.hh" /* mutex */ /* We need external help for these */ -#if defined(HB_MUTEX_IMPL_INIT) \ - && defined(hb_mutex_impl_init) \ +#if defined(hb_mutex_impl_init) \ && defined(hb_mutex_impl_lock) \ && defined(hb_mutex_impl_unlock) \ && defined(hb_mutex_impl_finish) @@ -48,12 +47,20 @@ /* Defined externally, i.e. in config.h; must have typedef'ed hb_mutex_impl_t as well. */ -#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__)) +#elif !defined(HB_NO_MT) && !defined(HB_MUTEX_IMPL_STD_MUTEX) && (defined(HAVE_PTHREAD) || defined(__APPLE__)) + +#include +typedef pthread_mutex_t hb_mutex_impl_t; +#define hb_mutex_impl_init(M) pthread_mutex_init (M, nullptr) +#define hb_mutex_impl_lock(M) pthread_mutex_lock (M) +#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M) +#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M) + + +#elif !defined(HB_NO_MT) && !defined(HB_MUTEX_IMPL_STD_MUTEX) && defined(_WIN32) -#include typedef CRITICAL_SECTION hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT {0} -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) #define hb_mutex_impl_init(M) InitializeCriticalSectionEx (M, 0, 0) #else #define hb_mutex_impl_init(M) InitializeCriticalSection (M) @@ -63,57 +70,19 @@ typedef CRITICAL_SECTION hb_mutex_impl_t; #define hb_mutex_impl_finish(M) DeleteCriticalSection (M) -#elif !defined(HB_NO_MT) && (defined(HAVE_PTHREAD) || defined(__APPLE__)) - -#include -typedef pthread_mutex_t hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT PTHREAD_MUTEX_INITIALIZER -#define hb_mutex_impl_init(M) pthread_mutex_init (M, nullptr) -#define hb_mutex_impl_lock(M) pthread_mutex_lock (M) -#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M) -#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M) - - -#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) - -#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) -# include -# define HB_SCHED_YIELD() sched_yield () -#else -# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END -#endif - -/* This actually is not a totally awful implementation. */ -typedef volatile int hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT 0 -#define hb_mutex_impl_init(M) *(M) = 0 -#define hb_mutex_impl_lock(M) HB_STMT_START { while (__sync_lock_test_and_set((M), 1)) HB_SCHED_YIELD (); } HB_STMT_END -#define hb_mutex_impl_unlock(M) __sync_lock_release (M) -#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END - - #elif !defined(HB_NO_MT) -#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) -# include -# define HB_SCHED_YIELD() sched_yield () -#else -# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END -#endif - -#define HB_MUTEX_INT_NIL 1 /* Warn that fallback implementation is in use. */ -typedef volatile int hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT 0 -#define hb_mutex_impl_init(M) *(M) = 0 -#define hb_mutex_impl_lock(M) HB_STMT_START { while (*(M)) HB_SCHED_YIELD (); (*(M))++; } HB_STMT_END -#define hb_mutex_impl_unlock(M) (*(M))--; -#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END +#include +typedef std::mutex hb_mutex_impl_t; +#define hb_mutex_impl_init(M) HB_STMT_START { new (M) hb_mutex_impl_t; } HB_STMT_END +#define hb_mutex_impl_lock(M) (M)->lock () +#define hb_mutex_impl_unlock(M) (M)->unlock () +#define hb_mutex_impl_finish(M) HB_STMT_START { (M)->~hb_mutex_impl_t(); } HB_STMT_END -#else /* HB_NO_MT */ +#else /* defined(HB_NO_MT) */ typedef int hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT 0 #define hb_mutex_impl_init(M) HB_STMT_START {} HB_STMT_END #define hb_mutex_impl_lock(M) HB_STMT_START {} HB_STMT_END #define hb_mutex_impl_unlock(M) HB_STMT_START {} HB_STMT_END @@ -123,19 +92,31 @@ typedef int hb_mutex_impl_t; #endif -#define HB_MUTEX_INIT {HB_MUTEX_IMPL_INIT} - struct hb_mutex_t { - /* TODO Add tracing. */ + /* Create space for, but do not initialize m. */ + alignas(hb_mutex_impl_t) char m[sizeof (hb_mutex_impl_t)]; - hb_mutex_impl_t m; + hb_mutex_t () { init (); } + ~hb_mutex_t () { fini (); } - inline void init (void) { hb_mutex_impl_init (&m); } - inline void lock (void) { hb_mutex_impl_lock (&m); } - inline void unlock (void) { hb_mutex_impl_unlock (&m); } - inline void finish (void) { hb_mutex_impl_finish (&m); } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + void init () { hb_mutex_impl_init ((hb_mutex_impl_t *) m); } + void lock () { hb_mutex_impl_lock ((hb_mutex_impl_t *) m); } + void unlock () { hb_mutex_impl_unlock ((hb_mutex_impl_t *) m); } + void fini () { hb_mutex_impl_finish ((hb_mutex_impl_t *) m); } +#pragma GCC diagnostic pop +}; + +struct hb_lock_t +{ + hb_lock_t (hb_mutex_t &mutex_) : mutex (&mutex_) { mutex->lock (); } + hb_lock_t (hb_mutex_t *mutex_) : mutex (mutex_) { if (mutex) mutex->lock (); } + ~hb_lock_t () { if (mutex) mutex->unlock (); } + private: + hb_mutex_t *mutex; }; -#endif /* HB_MUTEX_PRIVATE_HH */ +#endif /* HB_MUTEX_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-null.hh b/src/3rdparty/harfbuzz-ng/src/hb-null.hh new file mode 100644 index 00000000000..0d7f4da79e7 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-null.hh @@ -0,0 +1,226 @@ +/* + * Copyright ยฉ 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_NULL_HH +#define HB_NULL_HH + +#include "hb.hh" +#include "hb-meta.hh" + + +/* + * Static pools + */ + +/* Global nul-content Null pool. Enlarge as necessary. */ + +#define HB_NULL_POOL_SIZE 448 + +template +struct _hb_has_min_size : hb_false_type {}; +template +struct _hb_has_min_size> + : hb_true_type {}; +template +using hb_has_min_size = _hb_has_min_size; +#define hb_has_min_size(T) hb_has_min_size::value + +template +struct _hb_has_null_size : hb_false_type {}; +template +struct _hb_has_null_size> + : hb_true_type {}; +template +using hb_has_null_size = _hb_has_null_size; +#define hb_has_null_size(T) hb_has_null_size::value + +/* Use SFINAE to sniff whether T has min_size; in which case return the larger + * of sizeof(T) and T::null_size, otherwise return sizeof(T). + * + * The main purpose of this is to let structs communicate that they are not nullable, + * by defining min_size but *not* null_size. */ + +/* The hard way... + * https://stackoverflow.com/questions/7776448/sfinae-tried-with-bool-gives-compiler-error-template-argument-tvalue-invol + */ + +template +struct _hb_null_size : hb_integral_constant {}; +template +struct _hb_null_size> + : hb_integral_constant T::null_size ? sizeof (T) : T::null_size)> {}; +template +using hb_null_size = _hb_null_size; +#define hb_null_size(T) hb_null_size::value + +/* These doesn't belong here, but since is copy/paste from above, put it here. */ + +/* hb_static_size (T) + * Returns T::static_size if T::min_size is defined, or sizeof (T) otherwise. */ + +template +struct _hb_static_size : hb_integral_constant {}; +template +struct _hb_static_size> : hb_integral_constant {}; +template +using hb_static_size = _hb_static_size; +#define hb_static_size(T) hb_static_size::value + +template +struct _hb_min_size : hb_integral_constant {}; +template +struct _hb_min_size> : hb_integral_constant {}; +template +using hb_min_size = _hb_min_size; +#define hb_min_size(T) hb_min_size::value + + +/* + * Null() + */ + +extern HB_INTERNAL +uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)]; + +/* Generic nul-content Null objects. */ +template +struct Null { + static Type const & get_null () + { + static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); + return *reinterpret_cast (_hb_NullPool); + } +}; +template +struct NullHelper +{ + typedef hb_remove_const> Type; + static const Type & get_null () { return Null::get_null (); } +}; +#define Null(Type) NullHelper::get_null () + +/* Specializations for arbitrary-content Null objects expressed in bytes. */ +#define DECLARE_NULL_NAMESPACE_BYTES(Namespace, Type) \ + } /* Close namespace. */ \ + extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[hb_null_size (Namespace::Type)]; \ + template <> \ + struct Null { \ + static Namespace::Type const & get_null () { \ + return *reinterpret_cast (_hb_Null_##Namespace##_##Type); \ + } \ + }; \ + namespace Namespace { \ + static_assert (true, "") /* Require semicolon after. */ +#define DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1(Namespace, Type, Size) \ + } /* Close namespace. */ \ + extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Size]; \ + template \ + struct Null> { \ + static Namespace::Type const & get_null () { \ + return *reinterpret_cast *> (_hb_Null_##Namespace##_##Type); \ + } \ + }; \ + namespace Namespace { \ + static_assert (true, "") /* Require semicolon after. */ +#define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \ + const unsigned char _hb_Null_##Namespace##_##Type[sizeof (_hb_Null_##Namespace##_##Type)] + +/* Specializations for arbitrary-content Null objects expressed as struct initializer. */ +#define DECLARE_NULL_INSTANCE(Type) \ + extern HB_INTERNAL const Type _hb_Null_##Type; \ + template <> \ + struct Null { \ + static Type const & get_null () { \ + return _hb_Null_##Type; \ + } \ + }; \ + static_assert (true, "") /* Require semicolon after. */ +#define DEFINE_NULL_INSTANCE(Type) \ + const Type _hb_Null_##Type + +/* Global writable pool. Enlarge as necessary. */ + +/* To be fully correct, CrapPool must be thread_local. However, we do not rely on CrapPool + * for correct operation. It only exist to catch and divert program logic bugs instead of + * causing bad memory access. So, races there are not actually introducing incorrectness + * in the code. Has ~12kb binary size overhead to have it, also clang build fails with it. */ +extern HB_INTERNAL +/*thread_local*/ uint64_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)]; + +/* CRAP pool: Common Region for Access Protection. */ +template +static inline Type& Crap () { + static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); + Type *obj = reinterpret_cast (_hb_CrapPool); + memcpy (obj, &Null (Type), sizeof (*obj)); + return *obj; +} +template +struct CrapHelper +{ + typedef hb_remove_const> Type; + static Type & get_crap () { return Crap (); } +}; +#define Crap(Type) CrapHelper::get_crap () + +template +struct CrapOrNullHelper { + static Type & get () { return Crap (Type); } +}; +template +struct CrapOrNullHelper { + static const Type & get () { return Null (Type); } +}; +#define CrapOrNull(Type) CrapOrNullHelper::get () + + +/* + * hb_nonnull_ptr_t + */ + +template +struct hb_nonnull_ptr_t +{ + typedef hb_remove_pointer

T; + + hb_nonnull_ptr_t (T *v_ = nullptr) : v (v_) {} + T * operator = (T *v_) { return v = v_; } + T * operator -> () const { return get (); } + T & operator * () const { return *get (); } + T ** operator & () const { return &v; } + /* Only auto-cast to const types. */ + template operator const C * () const { return get (); } + operator const char * () const { return (const char *) get (); } + T * get () const { return v ? v : const_cast (&Null (T)); } + T * get_raw () const { return v; } + + private: + T *v; +}; + + +#endif /* HB_NULL_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-number-parser.hh b/src/3rdparty/harfbuzz-ng/src/hb-number-parser.hh new file mode 100644 index 00000000000..ec68c3a7281 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-number-parser.hh @@ -0,0 +1,237 @@ + +#line 1 "hb-number-parser.rl" +/* + * Copyright ยฉ 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#ifndef HB_NUMBER_PARSER_HH +#define HB_NUMBER_PARSER_HH + +#include "hb.hh" + + +#line 32 "hb-number-parser.hh" +static const unsigned char _double_parser_trans_keys[] = { + 0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u, + 46u, 101u, 0 +}; + +static const char _double_parser_key_spans[] = { + 0, 15, 12, 10, 15, 10, 54, 10, + 56 +}; + +static const unsigned char _double_parser_index_offsets[] = { + 0, 0, 16, 29, 40, 56, 67, 122, + 133 +}; + +static const char _double_parser_indicies[] = { + 0, 1, 2, 3, 1, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 1, 3, 1, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 1, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 1, 6, 1, 7, 1, 1, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 1, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 1, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 1, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 1, 3, 1, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 1, 0 +}; + +static const char _double_parser_trans_targs[] = { + 2, 0, 2, 3, 8, 6, 5, 5, + 7, 4 +}; + +static const char _double_parser_trans_actions[] = { + 0, 0, 1, 0, 2, 3, 0, 4, + 5, 0 +}; + +static const int double_parser_start = 1; +static const int double_parser_first_final = 6; +static const int double_parser_error = 0; + +static const int double_parser_en_main = 1; + + +#line 68 "hb-number-parser.rl" + + +/* Works only for n < 512 */ +static inline double +_pow10 (unsigned exponent) +{ + static const double _powers_of_10[] = + { + 1.0e+256, + 1.0e+128, + 1.0e+64, + 1.0e+32, + 1.0e+16, + 1.0e+8, + 10000., + 100., + 10. + }; + unsigned mask = 1 << (ARRAY_LENGTH (_powers_of_10) - 1); + double result = 1; + for (const double *power = _powers_of_10; mask; ++power, mask >>= 1) + if (exponent & mask) result *= *power; + return result; +} + +/* a variant of strtod that also gets end of buffer in its second argument */ +static inline double +strtod_rl (const char *p, const char **end_ptr /* IN/OUT */) +{ + double value = 0; + double frac = 0; + double frac_count = 0; + unsigned exp = 0; + bool neg = false, exp_neg = false, exp_overflow = false; + const unsigned long long MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 2^52-1 */ + const unsigned MAX_EXP = 0x7FFu; /* 2^11-1 */ + + const char *pe = *end_ptr; + while (p < pe && ISSPACE (*p)) + p++; + + int cs; + +#line 132 "hb-number-parser.hh" + { + cs = double_parser_start; + } + +#line 135 "hb-number-parser.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _double_parser_trans_keys + (cs<<1); + _inds = _double_parser_indicies + _double_parser_index_offsets[cs]; + + _slen = _double_parser_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _double_parser_trans_targs[_trans]; + + if ( _double_parser_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _double_parser_trans_actions[_trans] ) { + case 1: +#line 37 "hb-number-parser.rl" + { neg = true; } + break; + case 4: +#line 38 "hb-number-parser.rl" + { exp_neg = true; } + break; + case 2: +#line 40 "hb-number-parser.rl" + { + value = value * 10. + ((*p) - '0'); +} + break; + case 3: +#line 43 "hb-number-parser.rl" + { + if (likely (frac <= MAX_FRACT / 10)) + { + frac = frac * 10. + ((*p) - '0'); + ++frac_count; + } +} + break; + case 5: +#line 50 "hb-number-parser.rl" + { + if (likely (exp * 10 + ((*p) - '0') <= MAX_EXP)) + exp = exp * 10 + ((*p) - '0'); + else + exp_overflow = true; +} + break; +#line 187 "hb-number-parser.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + _out: {} + } + +#line 113 "hb-number-parser.rl" + + + *end_ptr = p; + + if (frac_count) value += frac / _pow10 (frac_count); + if (neg) value *= -1.; + + if (unlikely (exp_overflow)) + { + if (value == 0) return value; + if (exp_neg) return neg ? -DBL_MIN : DBL_MIN; + else return neg ? -DBL_MAX : DBL_MAX; + } + + if (exp) + { + if (exp_neg) value /= _pow10 (exp); + else value *= _pow10 (exp); + } + + return value; +} + +#endif /* HB_NUMBER_PARSER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-number.cc b/src/3rdparty/harfbuzz-ng/src/hb-number.cc new file mode 100644 index 00000000000..c52b284e1d2 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-number.cc @@ -0,0 +1,79 @@ +/* + * Copyright ยฉ 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#include "hb.hh" +#include "hb-number.hh" +#include "hb-number-parser.hh" + +template +static bool +_parse_number (const char **pp, const char *end, T *pv, + bool whole_buffer, Func f) +{ + char buf[32]; + unsigned len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned) (end - *pp)); + strncpy (buf, *pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + + errno = 0; + *pv = f (p, &pend); + if (unlikely (errno || p == pend || + /* Check if consumed whole buffer if is requested */ + (whole_buffer && pend - p != end - *pp))) + return false; + + *pp += pend - p; + return true; +} + +bool +hb_parse_int (const char **pp, const char *end, int *pv, bool whole_buffer) +{ + return _parse_number (pp, end, pv, whole_buffer, + [] (const char *p, char **end) + { return strtol (p, end, 10); }); +} + +bool +hb_parse_uint (const char **pp, const char *end, unsigned *pv, + bool whole_buffer, int base) +{ + return _parse_number (pp, end, pv, whole_buffer, + [base] (const char *p, char **end) + { return strtoul (p, end, base); }); +} + +bool +hb_parse_double (const char **pp, const char *end, double *pv, bool whole_buffer) +{ + const char *pend = end; + *pv = strtod_rl (*pp, &pend); + if (unlikely (*pp == pend)) return false; + *pp = pend; + return !whole_buffer || end == pend; +} diff --git a/src/3rdparty/harfbuzz-ng/src/hb-number.hh b/src/3rdparty/harfbuzz-ng/src/hb-number.hh new file mode 100644 index 00000000000..14d1260aa3d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-number.hh @@ -0,0 +1,41 @@ +/* + * Copyright ยฉ 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#ifndef HB_NUMBER_HH +#define HB_NUMBER_HH + +HB_INTERNAL bool +hb_parse_int (const char **pp, const char *end, int *pv, + bool whole_buffer = false); + +HB_INTERNAL bool +hb_parse_uint (const char **pp, const char *end, unsigned int *pv, + bool whole_buffer = false, int base = 10); + +HB_INTERNAL bool +hb_parse_double (const char **pp, const char *end, double *pv, + bool whole_buffer = false); + +#endif /* HB_NUMBER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-object-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-object-private.hh deleted file mode 100644 index baa1f8f05cc..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-object-private.hh +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright ยฉ 2007 Chris Wilson - * Copyright ยฉ 2009,2010 Red Hat, Inc. - * Copyright ยฉ 2011,2012 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Contributor(s): - * Chris Wilson - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_OBJECT_PRIVATE_HH -#define HB_OBJECT_PRIVATE_HH - -#include "hb-private.hh" -#include "hb-debug.hh" - -#include "hb-atomic-private.hh" -#include "hb-mutex-private.hh" - - -/* reference_count */ - -#define HB_REFERENCE_COUNT_INERT_VALUE -1 -#define HB_REFERENCE_COUNT_POISON_VALUE -0x0000DEAD -#define HB_REFERENCE_COUNT_INIT {HB_ATOMIC_INT_INIT(HB_REFERENCE_COUNT_INERT_VALUE)} - -struct hb_reference_count_t -{ - hb_atomic_int_t ref_count; - - inline void init (int v) { ref_count.set_unsafe (v); } - inline int get_unsafe (void) const { return ref_count.get_unsafe (); } - inline int inc (void) { return ref_count.inc (); } - inline int dec (void) { return ref_count.dec (); } - inline void finish (void) { ref_count.set_unsafe (HB_REFERENCE_COUNT_POISON_VALUE); } - - inline bool is_inert (void) const { return ref_count.get_unsafe () == HB_REFERENCE_COUNT_INERT_VALUE; } - inline bool is_valid (void) const { return ref_count.get_unsafe () > 0; } -}; - - -/* user_data */ - -#define HB_USER_DATA_ARRAY_INIT {HB_MUTEX_INIT, HB_LOCKABLE_SET_INIT} -struct hb_user_data_array_t -{ - struct hb_user_data_item_t { - hb_user_data_key_t *key; - void *data; - hb_destroy_func_t destroy; - - inline bool operator == (hb_user_data_key_t *other_key) const { return key == other_key; } - inline bool operator == (hb_user_data_item_t &other) const { return key == other.key; } - - void finish (void) { if (destroy) destroy (data); } - }; - - hb_mutex_t lock; - hb_lockable_set_t items; - - inline void init (void) { lock.init (); items.init (); } - - HB_INTERNAL bool set (hb_user_data_key_t *key, - void * data, - hb_destroy_func_t destroy, - hb_bool_t replace); - - HB_INTERNAL void *get (hb_user_data_key_t *key); - - inline void finish (void) { items.finish (lock); lock.finish (); } -}; - - -/* object_header */ - -struct hb_object_header_t -{ - hb_reference_count_t ref_count; - hb_user_data_array_t user_data; - -#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INIT, HB_USER_DATA_ARRAY_INIT} - - private: - ASSERT_POD (); -}; - - -/* object */ - -template -static inline void hb_object_trace (const Type *obj, const char *function) -{ - DEBUG_MSG (OBJECT, (void *) obj, - "%s refcount=%d", - function, - obj ? obj->header.ref_count.get_unsafe () : 0); -} - -template -static inline Type *hb_object_create (void) -{ - Type *obj = (Type *) calloc (1, sizeof (Type)); - - if (unlikely (!obj)) - return obj; - - hb_object_init (obj); - hb_object_trace (obj, HB_FUNC); - return obj; -} -template -static inline void hb_object_init (Type *obj) -{ - obj->header.ref_count.init (1); - obj->header.user_data.init (); -} -template -static inline bool hb_object_is_inert (const Type *obj) -{ - return unlikely (obj->header.ref_count.is_inert ()); -} -template -static inline bool hb_object_is_valid (const Type *obj) -{ - return likely (obj->header.ref_count.is_valid ()); -} -template -static inline Type *hb_object_reference (Type *obj) -{ - hb_object_trace (obj, HB_FUNC); - if (unlikely (!obj || hb_object_is_inert (obj))) - return obj; - assert (hb_object_is_valid (obj)); - obj->header.ref_count.inc (); - return obj; -} -template -static inline bool hb_object_destroy (Type *obj) -{ - hb_object_trace (obj, HB_FUNC); - if (unlikely (!obj || hb_object_is_inert (obj))) - return false; - assert (hb_object_is_valid (obj)); - if (obj->header.ref_count.dec () != 1) - return false; - - obj->header.ref_count.finish (); /* Do this before user_data */ - obj->header.user_data.finish (); - return true; -} -template -static inline bool hb_object_set_user_data (Type *obj, - hb_user_data_key_t *key, - void * data, - hb_destroy_func_t destroy, - hb_bool_t replace) -{ - if (unlikely (!obj || hb_object_is_inert (obj))) - return false; - assert (hb_object_is_valid (obj)); - return obj->header.user_data.set (key, data, destroy, replace); -} - -template -static inline void *hb_object_get_user_data (Type *obj, - hb_user_data_key_t *key) -{ - if (unlikely (!obj || hb_object_is_inert (obj))) - return nullptr; - assert (hb_object_is_valid (obj)); - return obj->header.user_data.get (key); -} - - -#endif /* HB_OBJECT_PRIVATE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-object.hh b/src/3rdparty/harfbuzz-ng/src/hb-object.hh new file mode 100644 index 00000000000..e2c2c3394cb --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-object.hh @@ -0,0 +1,357 @@ +/* + * Copyright ยฉ 2007 Chris Wilson + * Copyright ยฉ 2009,2010 Red Hat, Inc. + * Copyright ยฉ 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OBJECT_HH +#define HB_OBJECT_HH + +#include "hb.hh" +#include "hb-atomic.hh" +#include "hb-mutex.hh" +#include "hb-vector.hh" + + +/* + * Lockable set + */ + +template +struct hb_lockable_set_t +{ + hb_vector_t items; + + void init () { items.init (); } + + template + item_t *replace_or_insert (T v, lock_t &l, bool replace) + { + l.lock (); + item_t *item = items.lsearch (v); + if (item) { + if (replace) { + item_t old = *item; + *item = v; + l.unlock (); + old.fini (); + } + else { + item = nullptr; + l.unlock (); + } + } else { + item = items.push (v); + l.unlock (); + } + return items.in_error () ? nullptr : item; + } + + template + void remove (T v, lock_t &l) + { + l.lock (); + item_t *item = items.lsearch (v); + if (item) + { + item_t old = *item; + *item = std::move (items.tail ()); + items.pop (); + l.unlock (); + old.fini (); + } else { + l.unlock (); + } + } + + template + bool find (T v, item_t *i, lock_t &l) + { + l.lock (); + item_t *item = items.lsearch (v); + if (item) + *i = *item; + l.unlock (); + return !!item; + } + + template + item_t *find_or_insert (T v, lock_t &l) + { + l.lock (); + item_t *item = items.find (v); + if (!item) { + item = items.push (v); + } + l.unlock (); + return item; + } + + void fini (lock_t &l) + { + if (!items.length) + { + /* No need to lock. */ + items.fini (); + return; + } + l.lock (); + while (items.length) + { + item_t old = items.tail (); + items.pop (); + l.unlock (); + old.fini (); + l.lock (); + } + items.fini (); + l.unlock (); + } + +}; + + +/* + * Reference-count. + */ + +struct hb_reference_count_t +{ + mutable hb_atomic_int_t ref_count; + + void init (int v = 1) { ref_count = v; } + int get_relaxed () const { return ref_count; } + int inc () const { return ref_count.inc (); } + int dec () const { return ref_count.dec (); } + void fini () { ref_count = -0x0000DEAD; } + + bool is_inert () const { return !ref_count; } + bool is_valid () const { return ref_count > 0; } +}; + + +/* user_data */ + +struct hb_user_data_array_t +{ + struct hb_user_data_item_t { + hb_user_data_key_t *key; + void *data; + hb_destroy_func_t destroy; + + bool operator == (const hb_user_data_key_t *other_key) const { return key == other_key; } + bool operator == (const hb_user_data_item_t &other) const { return key == other.key; } + + void fini () { if (destroy) destroy (data); } + }; + + hb_mutex_t lock; + hb_lockable_set_t items; + + void init () { lock.init (); items.init (); } + + void fini () { items.fini (lock); lock.fini (); } + + bool set (hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) + { + if (!key) + return false; + + if (replace) { + if (!data && !destroy) { + items.remove (key, lock); + return true; + } + } + hb_user_data_item_t item = {key, data, destroy}; + bool ret = !!items.replace_or_insert (item, lock, (bool) replace); + + return ret; + } + + void *get (hb_user_data_key_t *key) + { + hb_user_data_item_t item = {nullptr, nullptr, nullptr}; + + return items.find (key, &item, lock) ? item.data : nullptr; + } +}; + + +/* + * Object header + */ + +struct hb_object_header_t +{ + hb_reference_count_t ref_count; + mutable hb_atomic_int_t writable = 0; + hb_atomic_ptr_t user_data; + + bool is_inert () const { return !ref_count.get_relaxed (); } +}; +#define HB_OBJECT_HEADER_STATIC {} + + +/* + * Object + */ + +template +static inline void hb_object_trace (const Type *obj, const char *function) +{ + DEBUG_MSG (OBJECT, (void *) obj, + "%s refcount=%d", + function, + obj ? obj->header.ref_count.get_relaxed () : 0); +} + +template +static inline Type *hb_object_create (Ts... ds) +{ + Type *obj = (Type *) hb_calloc (1, sizeof (Type)); + + if (unlikely (!obj)) + return obj; + + new (obj) Type (std::forward (ds)...); + + hb_object_init (obj); + hb_object_trace (obj, HB_FUNC); + + return obj; +} +template +static inline void hb_object_init (Type *obj) +{ + obj->header.ref_count.init (); + obj->header.writable = true; + obj->header.user_data.init (); +} +template +static inline bool hb_object_is_valid (const Type *obj) +{ + return likely (obj->header.ref_count.is_valid ()); +} +template +static inline bool hb_object_is_immutable (const Type *obj) +{ + return !obj->header.writable; +} +template +static inline void hb_object_make_immutable (const Type *obj) +{ + obj->header.writable = false; +} +template +static inline Type *hb_object_reference (Type *obj) +{ + hb_object_trace (obj, HB_FUNC); + if (unlikely (!obj || obj->header.is_inert ())) + return obj; + assert (hb_object_is_valid (obj)); + obj->header.ref_count.inc (); + return obj; +} +template +static inline bool hb_object_destroy (Type *obj) +{ + hb_object_trace (obj, HB_FUNC); + if (unlikely (!obj || obj->header.is_inert ())) + return false; + assert (hb_object_is_valid (obj)); + if (obj->header.ref_count.dec () != 1) + return false; + + hb_object_fini (obj); + + if (!std::is_trivially_destructible::value) + obj->~Type (); + + return true; +} +template +static inline void hb_object_fini (Type *obj) +{ + obj->header.ref_count.fini (); /* Do this before user_data */ + hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); + if (user_data) + { + user_data->fini (); + hb_free (user_data); + obj->header.user_data.set_relaxed (nullptr); + } +} +template +static inline bool hb_object_set_user_data (Type *obj, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + if (unlikely (!obj || obj->header.is_inert ())) + return false; + assert (hb_object_is_valid (obj)); + +retry: + hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); + if (unlikely (!user_data)) + { + user_data = (hb_user_data_array_t *) hb_calloc (sizeof (hb_user_data_array_t), 1); + if (unlikely (!user_data)) + return false; + user_data->init (); + if (unlikely (!obj->header.user_data.cmpexch (nullptr, user_data))) + { + user_data->fini (); + hb_free (user_data); + goto retry; + } + } + + return user_data->set (key, data, destroy, replace); +} + +template +static inline void *hb_object_get_user_data (Type *obj, + hb_user_data_key_t *key) +{ + if (unlikely (!obj || obj->header.is_inert ())) + return nullptr; + assert (hb_object_is_valid (obj)); + hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); + if (!user_data) + return nullptr; + return user_data->get (key); +} + + +#endif /* HB_OBJECT_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-open-file-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-open-file-private.hh deleted file mode 100644 index 644e0b40f6f..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-open-file-private.hh +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright ยฉ 2007,2008,2009 Red Hat, Inc. - * Copyright ยฉ 2012 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_OPEN_FILE_PRIVATE_HH -#define HB_OPEN_FILE_PRIVATE_HH - -#include "hb-open-type-private.hh" - - -namespace OT { - - -/* - * - * The OpenType Font File - * - */ - - -/* - * Organization of an OpenType Font - */ - -struct OpenTypeFontFile; -struct OffsetTable; -struct TTCHeader; - - -typedef struct TableRecord -{ - int cmp (Tag t) const - { return t.cmp (tag); } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - Tag tag; /* 4-byte identifier. */ - CheckSum checkSum; /* CheckSum for this table. */ - UINT32 offset; /* Offset from beginning of TrueType font - * file. */ - UINT32 length; /* Length of this table. */ - public: - DEFINE_SIZE_STATIC (16); -} OpenTypeTable; - -typedef struct OffsetTable -{ - friend struct OpenTypeFontFile; - - inline unsigned int get_table_count (void) const - { return tables.len; } - inline const TableRecord& get_table (unsigned int i) const - { - return tables[i]; - } - inline unsigned int get_table_tags (unsigned int start_offset, - unsigned int *table_count, /* IN/OUT */ - hb_tag_t *table_tags /* OUT */) const - { - if (table_count) - { - if (start_offset >= tables.len) - *table_count = 0; - else - *table_count = MIN (*table_count, tables.len - start_offset); - - const TableRecord *sub_tables = tables.array + start_offset; - unsigned int count = *table_count; - for (unsigned int i = 0; i < count; i++) - table_tags[i] = sub_tables[i].tag; - } - return tables.len; - } - inline bool find_table_index (hb_tag_t tag, unsigned int *table_index) const - { - Tag t; - t.set (tag); - /* Linear-search for small tables to work around fonts with unsorted - * table list. */ - int i = tables.len < 64 ? tables.lsearch (t) : tables.bsearch (t); - if (table_index) - *table_index = i == -1 ? Index::NOT_FOUND_INDEX : (unsigned int) i; - return i != -1; - } - inline const TableRecord& get_table_by_tag (hb_tag_t tag) const - { - unsigned int table_index; - find_table_index (tag, &table_index); - return get_table (table_index); - } - - public: - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && tables.sanitize (c)); - } - - protected: - Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */ - BinSearchArrayOf - tables; - public: - DEFINE_SIZE_ARRAY (12, tables); -} OpenTypeFontFace; - - -/* - * TrueType Collections - */ - -struct TTCHeaderVersion1 -{ - friend struct TTCHeader; - - inline unsigned int get_face_count (void) const { return table.len; } - inline const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (table.sanitize (c, this)); - } - - protected: - Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ - FixedVersion<>version; /* Version of the TTC Header (1.0), - * 0x00010000u */ - ArrayOf, UINT32> - table; /* Array of offsets to the OffsetTable for each font - * from the beginning of the file */ - public: - DEFINE_SIZE_ARRAY (12, table); -}; - -struct TTCHeader -{ - friend struct OpenTypeFontFile; - - private: - - inline unsigned int get_face_count (void) const - { - switch (u.header.version.major) { - case 2: /* version 2 is compatible with version 1 */ - case 1: return u.version1.get_face_count (); - default:return 0; - } - } - inline const OpenTypeFontFace& get_face (unsigned int i) const - { - switch (u.header.version.major) { - case 2: /* version 2 is compatible with version 1 */ - case 1: return u.version1.get_face (i); - default:return Null(OpenTypeFontFace); - } - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (unlikely (!u.header.version.sanitize (c))) return_trace (false); - switch (u.header.version.major) { - case 2: /* version 2 is compatible with version 1 */ - case 1: return_trace (u.version1.sanitize (c)); - default:return_trace (true); - } - } - - protected: - union { - struct { - Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ - FixedVersion<>version; /* Version of the TTC Header (1.0 or 2.0), - * 0x00010000u or 0x00020000u */ - } header; - TTCHeaderVersion1 version1; - } u; -}; - - -/* - * OpenType Font File - */ - -struct OpenTypeFontFile -{ - static const hb_tag_t tableTag = HB_TAG ('_','_','_','_'); /* Sanitizer needs this. */ - - static const hb_tag_t CFFTag = HB_TAG ('O','T','T','O'); /* OpenType with Postscript outlines */ - static const hb_tag_t TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ); /* OpenType with TrueType outlines */ - static const hb_tag_t TTCTag = HB_TAG ('t','t','c','f'); /* TrueType Collection */ - static const hb_tag_t TrueTag = HB_TAG ('t','r','u','e'); /* Obsolete Apple TrueType */ - static const hb_tag_t Typ1Tag = HB_TAG ('t','y','p','1'); /* Obsolete Apple Type1 font in SFNT container */ - - inline hb_tag_t get_tag (void) const { return u.tag; } - - inline unsigned int get_face_count (void) const - { - switch (u.tag) { - case CFFTag: /* All the non-collection tags */ - case TrueTag: - case Typ1Tag: - case TrueTypeTag: return 1; - case TTCTag: return u.ttcHeader.get_face_count (); - default: return 0; - } - } - inline const OpenTypeFontFace& get_face (unsigned int i) const - { - switch (u.tag) { - /* Note: for non-collection SFNT data we ignore index. This is because - * Apple dfont container is a container of SFNT's. So each SFNT is a - * non-TTC, but the index is more than zero. */ - case CFFTag: /* All the non-collection tags */ - case TrueTag: - case Typ1Tag: - case TrueTypeTag: return u.fontFace; - case TTCTag: return u.ttcHeader.get_face (i); - default: return Null(OpenTypeFontFace); - } - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (unlikely (!u.tag.sanitize (c))) return_trace (false); - switch (u.tag) { - case CFFTag: /* All the non-collection tags */ - case TrueTag: - case Typ1Tag: - case TrueTypeTag: return_trace (u.fontFace.sanitize (c)); - case TTCTag: return_trace (u.ttcHeader.sanitize (c)); - default: return_trace (true); - } - } - - protected: - union { - Tag tag; /* 4-byte identifier. */ - OpenTypeFontFace fontFace; - TTCHeader ttcHeader; - } u; - public: - DEFINE_SIZE_UNION (4, tag); -}; - - -} /* namespace OT */ - - -#endif /* HB_OPEN_FILE_PRIVATE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-open-file.hh b/src/3rdparty/harfbuzz-ng/src/hb-open-file.hh new file mode 100644 index 00000000000..13570a46e0c --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-open-file.hh @@ -0,0 +1,537 @@ +/* + * Copyright ยฉ 2007,2008,2009 Red Hat, Inc. + * Copyright ยฉ 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OPEN_FILE_HH +#define HB_OPEN_FILE_HH + +#include "hb-open-type.hh" +#include "hb-ot-head-table.hh" + + +namespace OT { + +/* + * + * The OpenType Font File + * + */ + + +/* + * Organization of an OpenType Font + */ + +struct OpenTypeFontFile; +struct OpenTypeOffsetTable; +struct TTCHeader; + + +typedef struct TableRecord +{ + int cmp (Tag t) const { return -t.cmp (tag); } + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const TableRecord *a = (const TableRecord *) pa; + const TableRecord *b = (const TableRecord *) pb; + return b->cmp (a->tag); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + Tag tag; /* 4-byte identifier. */ + CheckSum checkSum; /* CheckSum for this table. */ + Offset32 offset; /* Offset from beginning of TrueType font + * file. */ + HBUINT32 length; /* Length of this table. */ + public: + DEFINE_SIZE_STATIC (16); +} OpenTypeTable; + +typedef struct OpenTypeOffsetTable +{ + friend struct OpenTypeFontFile; + + unsigned int get_table_count () const { return tables.len; } + const TableRecord& get_table (unsigned int i) const + { return tables[i]; } + unsigned int get_table_tags (unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */) const + { + if (table_count) + { + + tables.as_array ().sub_array (start_offset, table_count) + | hb_map (&TableRecord::tag) + | hb_sink (hb_array (table_tags, *table_count)) + ; + } + return tables.len; + } + bool find_table_index (hb_tag_t tag, unsigned int *table_index) const + { + Tag t; + t = tag; + /* Use lfind for small fonts; there are fonts that have unsorted table entries; + * those tend to work in other tools, so tolerate them. + * https://github.com/harfbuzz/harfbuzz/issues/3065 */ + if (tables.len < 16) + return tables.lfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); + else + return tables.bfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); + } + const TableRecord& get_table_by_tag (hb_tag_t tag) const + { + unsigned int table_index; + find_table_index (tag, &table_index); + return get_table (table_index); + } + + public: + + template >::value))> + bool serialize (hb_serialize_context_t *c, + hb_tag_t sfnt_tag, + Iterator it) + { + TRACE_SERIALIZE (this); + /* Alloc 12 for the OTHeader. */ + if (unlikely (!c->extend_min (this))) return_trace (false); + /* Write sfntVersion (bytes 0..3). */ + sfnt_version = sfnt_tag; + /* Take space for numTables, searchRange, entrySelector, RangeShift + * and the TableRecords themselves. */ + unsigned num_items = it.len (); + if (unlikely (!tables.serialize (c, num_items))) return_trace (false); + + const char *dir_end = (const char *) c->head; + HBUINT32 *checksum_adjustment = nullptr; + + /* Write OffsetTables, alloc for and write actual table blobs. */ + unsigned i = 0; + for (hb_pair_t entry : it) + { + hb_blob_t *blob = entry.second; + unsigned len = blob->length; + + /* Allocate room for the table and copy it. */ + char *start = (char *) c->allocate_size (len); + if (unlikely (!start)) return false; + + TableRecord &rec = tables.arrayZ[i]; + rec.tag = entry.first; + rec.length = len; + rec.offset = 0; + if (unlikely (!c->check_assign (rec.offset, + (unsigned) ((char *) start - (char *) this), + HB_SERIALIZE_ERROR_OFFSET_OVERFLOW))) + return_trace (false); + + if (likely (len)) + hb_memcpy (start, blob->data, len); + + /* 4-byte alignment. */ + c->align (4); + const char *end = (const char *) c->head; + + if (entry.first == HB_OT_TAG_head && + (unsigned) (end - start) >= head::static_size) + { + head *h = (head *) start; + checksum_adjustment = &h->checkSumAdjustment; + *checksum_adjustment = 0; + } + + rec.checkSum.set_for_data (start, end - start); + i++; + } + + tables.qsort (); + + if (checksum_adjustment) + { + CheckSum checksum; + + /* The following line is a slower version of the following block. */ + //checksum.set_for_data (this, (const char *) c->head - (const char *) this); + checksum.set_for_data (this, dir_end - (const char *) this); + for (unsigned int i = 0; i < num_items; i++) + { + TableRecord &rec = tables.arrayZ[i]; + checksum = checksum + rec.checkSum; + } + + *checksum_adjustment = 0xB1B0AFBAu - checksum; + } + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && tables.sanitize (c)); + } + + protected: + Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */ + BinSearchArrayOf + tables; + public: + DEFINE_SIZE_ARRAY (12, tables); +} OpenTypeFontFace; + + +/* + * TrueType Collections + */ + +struct TTCHeaderVersion1 +{ + friend struct TTCHeader; + + unsigned int get_face_count () const { return table.len; } + const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (table.sanitize (c, this)); + } + + protected: + Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ + FixedVersion<>version; /* Version of the TTC Header (1.0), + * 0x00010000u */ + Array32Of> + table; /* Array of offsets to the OffsetTable for each font + * from the beginning of the file */ + public: + DEFINE_SIZE_ARRAY (12, table); +}; + +struct TTCHeader +{ + friend struct OpenTypeFontFile; + + private: + + unsigned int get_face_count () const + { + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return u.version1.get_face_count (); + default:return 0; + } + } + const OpenTypeFontFace& get_face (unsigned int i) const + { + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return u.version1.get_face (i); + default:return Null (OpenTypeFontFace); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.header.version.sanitize (c))) return_trace (false); + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return_trace (u.version1.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + struct { + Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ + FixedVersion<>version; /* Version of the TTC Header (1.0 or 2.0), + * 0x00010000u or 0x00020000u */ + } header; + TTCHeaderVersion1 version1; + } u; +}; + +/* + * Mac Resource Fork + * + * http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html + */ + +struct ResourceRecord +{ + const OpenTypeFontFace & get_face (const void *data_base) const + { return * reinterpret_cast ((data_base+offset).arrayZ); } + + bool sanitize (hb_sanitize_context_t *c, + const void *data_base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + offset.sanitize (c, data_base) && + get_face (data_base).sanitize (c)); + } + + protected: + HBUINT16 id; /* Resource ID. */ + HBINT16 nameOffset; /* Offset from beginning of resource name list + * to resource name, -1 means there is none. */ + HBUINT8 attrs; /* Resource attributes */ + NNOffset24To> + offset; /* Offset from beginning of data block to + * data for this resource */ + HBUINT32 reserved; /* Reserved for handle to resource */ + public: + DEFINE_SIZE_STATIC (12); +}; + +#define HB_TAG_sfnt HB_TAG ('s','f','n','t') + +struct ResourceTypeRecord +{ + unsigned int get_resource_count () const + { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; } + + bool is_sfnt () const { return tag == HB_TAG_sfnt; } + + const ResourceRecord& get_resource_record (unsigned int i, + const void *type_base) const + { return (type_base+resourcesZ).as_array (get_resource_count ())[i]; } + + bool sanitize (hb_sanitize_context_t *c, + const void *type_base, + const void *data_base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + resourcesZ.sanitize (c, type_base, + get_resource_count (), + data_base)); + } + + protected: + Tag tag; /* Resource type. */ + HBUINT16 resCountM1; /* Number of resources minus 1. */ + NNOffset16To> + resourcesZ; /* Offset from beginning of resource type list + * to reference item list for this type. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct ResourceMap +{ + unsigned int get_face_count () const + { + unsigned int count = get_type_count (); + for (unsigned int i = 0; i < count; i++) + { + const ResourceTypeRecord& type = get_type_record (i); + if (type.is_sfnt ()) + return type.get_resource_count (); + } + return 0; + } + + const OpenTypeFontFace& get_face (unsigned int idx, + const void *data_base) const + { + unsigned int count = get_type_count (); + for (unsigned int i = 0; i < count; i++) + { + const ResourceTypeRecord& type = get_type_record (i); + /* The check for idx < count is here because ResourceRecord is NOT null-safe. + * Because an offset of 0 there does NOT mean null. */ + if (type.is_sfnt () && idx < type.get_resource_count ()) + return type.get_resource_record (idx, &(this+typeList)).get_face (data_base); + } + return Null (OpenTypeFontFace); + } + + bool sanitize (hb_sanitize_context_t *c, const void *data_base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + typeList.sanitize (c, this, + &(this+typeList), + data_base)); + } + + private: + unsigned int get_type_count () const { return (this+typeList).lenM1 + 1; } + + const ResourceTypeRecord& get_type_record (unsigned int i) const + { return (this+typeList)[i]; } + + protected: + HBUINT8 reserved0[16]; /* Reserved for copy of resource header */ + HBUINT32 reserved1; /* Reserved for handle to next resource map */ + HBUINT16 resreved2; /* Reserved for file reference number */ + HBUINT16 attrs; /* Resource fork attribute */ + NNOffset16To> + typeList; /* Offset from beginning of map to + * resource type list */ + Offset16 nameList; /* Offset from beginning of map to + * resource name list */ + public: + DEFINE_SIZE_STATIC (28); +}; + +struct ResourceForkHeader +{ + unsigned int get_face_count () const + { return (this+map).get_face_count (); } + + const OpenTypeFontFace& get_face (unsigned int idx, + unsigned int *base_offset = nullptr) const + { + const OpenTypeFontFace &face = (this+map).get_face (idx, &(this+data)); + if (base_offset) + *base_offset = (const char *) &face - (const char *) this; + return face; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + data.sanitize (c, this, dataLen) && + map.sanitize (c, this, &(this+data))); + } + + protected: + NNOffset32To> + data; /* Offset from beginning of resource fork + * to resource data */ + NNOffset32To + map; /* Offset from beginning of resource fork + * to resource map */ + HBUINT32 dataLen; /* Length of resource data */ + HBUINT32 mapLen; /* Length of resource map */ + public: + DEFINE_SIZE_STATIC (16); +}; + +/* + * OpenType Font File + */ + +struct OpenTypeFontFile +{ + enum { + CFFTag = HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */ + TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */ + TTCTag = HB_TAG ('t','t','c','f'), /* TrueType Collection */ + DFontTag = HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */ + TrueTag = HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */ + Typ1Tag = HB_TAG ('t','y','p','1') /* Obsolete Apple Type1 font in SFNT container */ + }; + + hb_tag_t get_tag () const { return u.tag; } + + unsigned int get_face_count () const + { + switch (u.tag) { + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return 1; + case TTCTag: return u.ttcHeader.get_face_count (); + case DFontTag: return u.rfHeader.get_face_count (); + default: return 0; + } + } + const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const + { + if (base_offset) + *base_offset = 0; + switch (u.tag) { + /* Note: for non-collection SFNT data we ignore index. This is because + * Apple dfont container is a container of SFNT's. So each SFNT is a + * non-TTC, but the index is more than zero. */ + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return u.fontFace; + case TTCTag: return u.ttcHeader.get_face (i); + case DFontTag: return u.rfHeader.get_face (i, base_offset); + default: return Null (OpenTypeFontFace); + } + } + + template >::value))> + bool serialize_single (hb_serialize_context_t *c, + hb_tag_t sfnt_tag, + Iterator items) + { + TRACE_SERIALIZE (this); + assert (sfnt_tag != TTCTag); + if (unlikely (!c->extend_min (this))) return_trace (false); + return_trace (u.fontFace.serialize (c, sfnt_tag, items)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.tag.sanitize (c))) return_trace (false); + switch (u.tag) { + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return_trace (u.fontFace.sanitize (c)); + case TTCTag: return_trace (u.ttcHeader.sanitize (c)); + case DFontTag: return_trace (u.rfHeader.sanitize (c)); + default: return_trace (true); + } + } + + protected: + union { + Tag tag; /* 4-byte identifier. */ + OpenTypeFontFace fontFace; + TTCHeader ttcHeader; + ResourceForkHeader rfHeader; + } u; + public: + DEFINE_SIZE_UNION (4, tag); +}; + + +} /* namespace OT */ + + +#endif /* HB_OPEN_FILE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-open-type-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-open-type-private.hh deleted file mode 100644 index 2f4e1b9e9e9..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-open-type-private.hh +++ /dev/null @@ -1,1184 +0,0 @@ -/* - * Copyright ยฉ 2007,2008,2009,2010 Red Hat, Inc. - * Copyright ยฉ 2012 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_OPEN_TYPE_PRIVATE_HH -#define HB_OPEN_TYPE_PRIVATE_HH - -#include "hb-private.hh" -#include "hb-debug.hh" -#include "hb-face-private.hh" - - -namespace OT { - - - -/* - * Casts - */ - -/* Cast to struct T, reference to reference */ -template -static inline const Type& CastR(const TObject &X) -{ return reinterpret_cast (X); } -template -static inline Type& CastR(TObject &X) -{ return reinterpret_cast (X); } - -/* Cast to struct T, pointer to pointer */ -template -static inline const Type* CastP(const TObject *X) -{ return reinterpret_cast (X); } -template -static inline Type* CastP(TObject *X) -{ return reinterpret_cast (X); } - -/* StructAtOffset(P,Ofs) returns the struct T& that is placed at memory - * location pointed to by P plus Ofs bytes. */ -template -static inline const Type& StructAtOffset(const void *P, unsigned int offset) -{ return * reinterpret_cast ((const char *) P + offset); } -template -static inline Type& StructAtOffset(void *P, unsigned int offset) -{ return * reinterpret_cast ((char *) P + offset); } - -/* StructAfter(X) returns the struct T& that is placed after X. - * Works with X of variable size also. X must implement get_size() */ -template -static inline const Type& StructAfter(const TObject &X) -{ return StructAtOffset(&X, X.get_size()); } -template -static inline Type& StructAfter(TObject &X) -{ return StructAtOffset(&X, X.get_size()); } - - - -/* - * Size checking - */ - -/* Check _assertion in a method environment */ -#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \ - inline void _instance_assertion_on_line_##_line (void) const \ - { \ - static_assert ((_assertion), ""); \ - ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \ - } -# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion) -# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion) - -/* Check that _code compiles in a method environment */ -#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \ - inline void _compiles_assertion_on_line_##_line (void) const \ - { _code; } -# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code) -# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code) - - -#define DEFINE_SIZE_STATIC(size) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \ - static const unsigned int static_size = (size); \ - static const unsigned int min_size = (size); \ - inline unsigned int get_size (void) const { return (size); } - -#define DEFINE_SIZE_UNION(size, _member) \ - DEFINE_INSTANCE_ASSERTION (0*sizeof(this->u._member.static_size) + sizeof(this->u._member) == (size)); \ - static const unsigned int min_size = (size) - -#define DEFINE_SIZE_MIN(size) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \ - static const unsigned int min_size = (size) - -#define DEFINE_SIZE_ARRAY(size, array) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \ - DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \ - static const unsigned int min_size = (size) - -#define DEFINE_SIZE_ARRAY2(size, array1, array2) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \ - DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \ - static const unsigned int min_size = (size) - - - -/* - * Null objects - */ - -/* Global nul-content Null pool. Enlarge as necessary. */ - -#define HB_NULL_POOL_SIZE 264 -static_assert (HB_NULL_POOL_SIZE % sizeof (void *) == 0, "Align HB_NULL_POOL_SIZE."); -extern HB_INTERNAL const void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)]; - -/* Generic nul-content Null objects. */ -template -static inline const Type& Null (void) { - static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); - return *CastP (_hb_NullPool); -} - -/* Specializaiton for arbitrary-content arbitrary-sized Null objects. */ -#define DEFINE_NULL_DATA(Type, data) \ -static const char _Null##Type[sizeof (Type) + 1] = data; /* +1 is for nul-termination in data */ \ -template <> \ -/*static*/ inline const Type& Null (void) { \ - return *CastP (_Null##Type); \ -} /* The following line really exists such that we end in a place needing semicolon */ \ -static_assert (Type::min_size + 1 <= sizeof (_Null##Type), "Null pool too small. Enlarge.") - -/* Accessor macro. */ -#define Null(Type) Null() - - -/* - * Dispatch - */ - -template -struct hb_dispatch_context_t -{ - static const unsigned int max_debug_depth = MaxDebugDepth; - typedef Return return_t; - template - inline bool may_dispatch (const T *obj, const F *format) { return true; } - static return_t no_dispatch_return_value (void) { return Context::default_return_value (); } -}; - - -/* - * Sanitize - */ - -/* This limits sanitizing time on really broken fonts. */ -#ifndef HB_SANITIZE_MAX_EDITS -#define HB_SANITIZE_MAX_EDITS 32 -#endif - -struct hb_sanitize_context_t : - hb_dispatch_context_t -{ - inline hb_sanitize_context_t (void) : - debug_depth (0), - start (nullptr), end (nullptr), - writable (false), edit_count (0), - blob (nullptr) {} - - inline const char *get_name (void) { return "SANITIZE"; } - template - inline bool may_dispatch (const T *obj, const F *format) - { return format->sanitize (this); } - template - inline return_t dispatch (const T &obj) { return obj.sanitize (this); } - static return_t default_return_value (void) { return true; } - static return_t no_dispatch_return_value (void) { return false; } - bool stop_sublookup_iteration (const return_t r) const { return !r; } - - inline void init (hb_blob_t *b) - { - this->blob = hb_blob_reference (b); - this->writable = false; - } - - inline void start_processing (void) - { - this->start = hb_blob_get_data (this->blob, nullptr); - this->end = this->start + hb_blob_get_length (this->blob); - assert (this->start <= this->end); /* Must not overflow. */ - this->edit_count = 0; - this->debug_depth = 0; - - DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1, - "start [%p..%p] (%lu bytes)", - this->start, this->end, - (unsigned long) (this->end - this->start)); - } - - inline void end_processing (void) - { - DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, - "end [%p..%p] %u edit requests", - this->start, this->end, this->edit_count); - - hb_blob_destroy (this->blob); - this->blob = nullptr; - this->start = this->end = nullptr; - } - - inline bool check_range (const void *base, unsigned int len) const - { - const char *p = (const char *) base; - bool ok = this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len; - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s", - p, p + len, len, - this->start, this->end, - ok ? "OK" : "OUT-OF-RANGE"); - - return likely (ok); - } - - inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const - { - const char *p = (const char *) base; - bool overflows = _hb_unsigned_int_mul_overflows (len, record_size); - unsigned int array_size = record_size * len; - bool ok = !overflows && this->check_range (base, array_size); - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s", - p, p + (record_size * len), record_size, len, (unsigned int) array_size, - this->start, this->end, - overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE"); - - return likely (ok); - } - - template - inline bool check_struct (const Type *obj) const - { - return likely (this->check_range (obj, obj->min_size)); - } - - inline bool may_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED) - { - if (this->edit_count >= HB_SANITIZE_MAX_EDITS) - return false; - - const char *p = (const char *) base; - this->edit_count++; - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", - this->edit_count, - p, p + len, len, - this->start, this->end, - this->writable ? "GRANTED" : "DENIED"); - - return this->writable; - } - - template - inline bool try_set (const Type *obj, const ValueType &v) { - if (this->may_edit (obj, obj->static_size)) { - const_cast (obj)->set (v); - return true; - } - return false; - } - - mutable unsigned int debug_depth; - const char *start, *end; - bool writable; - unsigned int edit_count; - hb_blob_t *blob; -}; - - - -/* Template to sanitize an object. */ -template -struct Sanitizer -{ - static hb_blob_t *sanitize (hb_blob_t *blob) { - hb_sanitize_context_t c[1]; - bool sane; - - /* TODO is_sane() stuff */ - - c->init (blob); - - retry: - DEBUG_MSG_FUNC (SANITIZE, c->start, "start"); - - c->start_processing (); - - if (unlikely (!c->start)) { - c->end_processing (); - return blob; - } - - Type *t = CastP (const_cast (c->start)); - - sane = t->sanitize (c); - if (sane) { - if (c->edit_count) { - DEBUG_MSG_FUNC (SANITIZE, c->start, "passed first round with %d edits; going for second round", c->edit_count); - - /* sanitize again to ensure no toe-stepping */ - c->edit_count = 0; - sane = t->sanitize (c); - if (c->edit_count) { - DEBUG_MSG_FUNC (SANITIZE, c->start, "requested %d edits in second round; FAILLING", c->edit_count); - sane = false; - } - } - } else { - unsigned int edit_count = c->edit_count; - if (edit_count && !c->writable) { - c->start = hb_blob_get_data_writable (blob, nullptr); - c->end = c->start + hb_blob_get_length (blob); - - if (c->start) { - c->writable = true; - /* ok, we made it writable by relocating. try again */ - DEBUG_MSG_FUNC (SANITIZE, c->start, "retry"); - goto retry; - } - } - } - - c->end_processing (); - - DEBUG_MSG_FUNC (SANITIZE, c->start, sane ? "PASSED" : "FAILED"); - if (sane) - return blob; - else { - hb_blob_destroy (blob); - return hb_blob_get_empty (); - } - } - - static const Type* lock_instance (hb_blob_t *blob) { - hb_blob_make_immutable (blob); - const char *base = hb_blob_get_data (blob, nullptr); - return unlikely (!base) ? &Null(Type) : CastP (base); - } -}; - - - -/* - * Serialize - */ - - -struct hb_serialize_context_t -{ - inline hb_serialize_context_t (void *start_, unsigned int size) - { - this->start = (char *) start_; - this->end = this->start + size; - - this->ran_out_of_room = false; - this->head = this->start; - this->debug_depth = 0; - } - - template - inline Type *start_serialize (void) - { - DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1, - "start [%p..%p] (%lu bytes)", - this->start, this->end, - (unsigned long) (this->end - this->start)); - - return start_embed (); - } - - inline void end_serialize (void) - { - DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, - "end [%p..%p] serialized %d bytes; %s", - this->start, this->end, - (int) (this->head - this->start), - this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room"); - - } - - template - inline Type *copy (void) - { - assert (!this->ran_out_of_room); - unsigned int len = this->head - this->start; - void *p = malloc (len); - if (p) - memcpy (p, this->start, len); - return reinterpret_cast (p); - } - - template - inline Type *allocate_size (unsigned int size) - { - if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) { - this->ran_out_of_room = true; - return nullptr; - } - memset (this->head, 0, size); - char *ret = this->head; - this->head += size; - return reinterpret_cast (ret); - } - - template - inline Type *allocate_min (void) - { - return this->allocate_size (Type::min_size); - } - - template - inline Type *start_embed (void) - { - Type *ret = reinterpret_cast (this->head); - return ret; - } - - template - inline Type *embed (const Type &obj) - { - unsigned int size = obj.get_size (); - Type *ret = this->allocate_size (size); - if (unlikely (!ret)) return nullptr; - memcpy (ret, obj, size); - return ret; - } - - template - inline Type *extend_min (Type &obj) - { - unsigned int size = obj.min_size; - assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); - if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return nullptr; - return reinterpret_cast (&obj); - } - - template - inline Type *extend (Type &obj) - { - unsigned int size = obj.get_size (); - assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); - if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return nullptr; - return reinterpret_cast (&obj); - } - - inline void truncate (void *new_head) - { - assert (this->start < new_head && new_head <= this->head); - this->head = (char *) new_head; - } - - unsigned int debug_depth; - char *start, *end, *head; - bool ran_out_of_room; -}; - -template -struct Supplier -{ - inline Supplier (const Type *array, unsigned int len_) - { - head = array; - len = len_; - } - inline const Type operator [] (unsigned int i) const - { - if (unlikely (i >= len)) return Type (); - return head[i]; - } - - inline void advance (unsigned int count) - { - if (unlikely (count > len)) - count = len; - len -= count; - head += count; - } - - private: - inline Supplier (const Supplier &); /* Disallow copy */ - inline Supplier& operator= (const Supplier &); /* Disallow copy */ - - unsigned int len; - const Type *head; -}; - - - - -/* - * - * The OpenType Font File: Data Types - */ - - -/* "The following data types are used in the OpenType font file. - * All OpenType fonts use Motorola-style byte ordering (Big Endian):" */ - -/* - * Int types - */ - - -template struct BEInt; - -template -struct BEInt -{ - public: - inline void set (Type V) - { - v = V; - } - inline operator Type (void) const - { - return v; - } - private: uint8_t v; -}; -template -struct BEInt -{ - public: - inline void set (Type V) - { - v[0] = (V >> 8) & 0xFF; - v[1] = (V ) & 0xFF; - } - inline operator Type (void) const - { - return (v[0] << 8) - + (v[1] ); - } - private: uint8_t v[2]; -}; -template -struct BEInt -{ - public: - inline void set (Type V) - { - v[0] = (V >> 16) & 0xFF; - v[1] = (V >> 8) & 0xFF; - v[2] = (V ) & 0xFF; - } - inline operator Type (void) const - { - return (v[0] << 16) - + (v[1] << 8) - + (v[2] ); - } - private: uint8_t v[3]; -}; -template -struct BEInt -{ - public: - inline void set (Type V) - { - v[0] = (V >> 24) & 0xFF; - v[1] = (V >> 16) & 0xFF; - v[2] = (V >> 8) & 0xFF; - v[3] = (V ) & 0xFF; - } - inline operator Type (void) const - { - return (v[0] << 24) - + (v[1] << 16) - + (v[2] << 8) - + (v[3] ); - } - private: uint8_t v[4]; -}; - -/* Integer types in big-endian order and no alignment requirement */ -template -struct IntType -{ - inline void set (Type i) { v.set (i); } - inline operator Type(void) const { return v; } - inline bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } - inline bool operator != (const IntType &o) const { return !(*this == o); } - static inline int cmp (const IntType *a, const IntType *b) { return b->cmp (*a); } - template - inline int cmp (Type2 a) const - { - Type b = v; - if (sizeof (Type) < sizeof (int) && sizeof (Type2) < sizeof (int)) - return (int) a - (int) b; - else - return a < b ? -1 : a == b ? 0 : +1; - } - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); - } - protected: - BEInt v; - public: - DEFINE_SIZE_STATIC (Size); -}; - -typedef IntType UINT8; /* 8-bit unsigned integer. */ -typedef IntType INT8; /* 8-bit signed integer. */ -typedef IntType UINT16; /* 16-bit unsigned integer. */ -typedef IntType INT16; /* 16-bit signed integer. */ -typedef IntType UINT32; /* 32-bit unsigned integer. */ -typedef IntType INT32; /* 32-bit signed integer. */ -typedef IntType UINT24; /* 24-bit unsigned integer. */ - -/* 16-bit signed integer (INT16) that describes a quantity in FUnits. */ -typedef INT16 FWORD; - -/* 16-bit unsigned integer (UINT16) that describes a quantity in FUnits. */ -typedef UINT16 UFWORD; - -/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */ -struct F2DOT14 : INT16 -{ - //inline float to_float (void) const { return ???; } - //inline void set_float (float f) { v.set (f * ???); } - public: - DEFINE_SIZE_STATIC (2); -}; - -/* 32-bit signed fixed-point number (16.16). */ -struct Fixed: INT32 -{ - //inline float to_float (void) const { return ???; } - //inline void set_float (float f) { v.set (f * ???); } - public: - DEFINE_SIZE_STATIC (4); -}; - -/* Date represented in number of seconds since 12:00 midnight, January 1, - * 1904. The value is represented as a signed 64-bit integer. */ -struct LONGDATETIME -{ - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); - } - protected: - INT32 major; - UINT32 minor; - public: - DEFINE_SIZE_STATIC (8); -}; - -/* Array of four uint8s (length = 32 bits) used to identify a script, language - * system, feature, or baseline */ -struct Tag : UINT32 -{ - /* What the char* converters return is NOT nul-terminated. Print using "%.4s" */ - inline operator const char* (void) const { return reinterpret_cast (&this->v); } - inline operator char* (void) { return reinterpret_cast (&this->v); } - public: - DEFINE_SIZE_STATIC (4); -}; -DEFINE_NULL_DATA (Tag, " "); - -/* Glyph index number, same as uint16 (length = 16 bits) */ -typedef UINT16 GlyphID; - -/* Script/language-system/feature index */ -struct Index : UINT16 { - static const unsigned int NOT_FOUND_INDEX = 0xFFFFu; -}; -DEFINE_NULL_DATA (Index, "\xff\xff"); - -/* Offset, Null offset = 0 */ -template -struct Offset : Type -{ - inline bool is_null (void) const { return 0 == *this; } - public: - DEFINE_SIZE_STATIC (sizeof(Type)); -}; - -typedef Offset Offset16; -typedef Offset Offset32; - - -/* CheckSum */ -struct CheckSum : UINT32 -{ - /* This is reference implementation from the spec. */ - static inline uint32_t CalcTableChecksum (const UINT32 *Table, uint32_t Length) - { - uint32_t Sum = 0L; - const UINT32 *EndPtr = Table+((Length+3) & ~3) / UINT32::static_size; - - while (Table < EndPtr) - Sum += *Table++; - return Sum; - } - - /* Note: data should be 4byte aligned and have 4byte padding at the end. */ - inline void set_for_data (const void *data, unsigned int length) - { set (CalcTableChecksum ((const UINT32 *) data, length)); } - - public: - DEFINE_SIZE_STATIC (4); -}; - - -/* - * Version Numbers - */ - -template -struct FixedVersion -{ - inline uint32_t to_int (void) const { return (major << (sizeof(FixedType) * 8)) + minor; } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - FixedType major; - FixedType minor; - public: - DEFINE_SIZE_STATIC (2 * sizeof(FixedType)); -}; - - - -/* - * Template subclasses of Offset that do the dereferencing. - * Use: (base+offset) - */ - -template -struct OffsetTo : Offset -{ - inline const Type& operator () (const void *base) const - { - unsigned int offset = *this; - if (unlikely (!offset)) return Null(Type); - return StructAtOffset (base, offset); - } - - inline Type& serialize (hb_serialize_context_t *c, const void *base) - { - Type *t = c->start_embed (); - this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */ - return *t; - } - - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const - { - TRACE_SANITIZE (this); - if (unlikely (!c->check_struct (this))) return_trace (false); - unsigned int offset = *this; - if (unlikely (!offset)) return_trace (true); - if (unlikely (!c->check_range (base, offset))) return_trace (false); - const Type &obj = StructAtOffset (base, offset); - return_trace (likely (obj.sanitize (c)) || neuter (c)); - } - template - inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const - { - TRACE_SANITIZE (this); - if (unlikely (!c->check_struct (this))) return_trace (false); - unsigned int offset = *this; - if (unlikely (!offset)) return_trace (true); - if (unlikely (!c->check_range (base, offset))) return_trace (false); - const Type &obj = StructAtOffset (base, offset); - return_trace (likely (obj.sanitize (c, user_data)) || neuter (c)); - } - - /* Set the offset to Null */ - inline bool neuter (hb_sanitize_context_t *c) const { - return c->try_set (this, 0); - } - DEFINE_SIZE_STATIC (sizeof(OffsetType)); -}; -template struct LOffsetTo : OffsetTo {}; -template -static inline const Type& operator + (const Base &base, const OffsetTo &offset) { return offset (base); } -template -static inline Type& operator + (Base &base, OffsetTo &offset) { return offset (base); } - - -/* - * Array Types - */ - -/* An array with a number of elements. */ -template -struct ArrayOf -{ - const Type *sub_array (unsigned int start_offset, unsigned int *pcount /* IN/OUT */) const - { - unsigned int count = len; - if (unlikely (start_offset > count)) - count = 0; - else - count -= start_offset; - count = MIN (count, *pcount); - *pcount = count; - return array + start_offset; - } - - inline const Type& operator [] (unsigned int i) const - { - if (unlikely (i >= len)) return Null(Type); - return array[i]; - } - inline Type& operator [] (unsigned int i) - { - return array[i]; - } - inline unsigned int get_size (void) const - { return len.static_size + len * Type::static_size; } - - inline bool serialize (hb_serialize_context_t *c, - unsigned int items_len) - { - TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); - len.set (items_len); /* TODO(serialize) Overflow? */ - if (unlikely (!c->extend (*this))) return_trace (false); - return_trace (true); - } - - inline bool serialize (hb_serialize_context_t *c, - Supplier &items, - unsigned int items_len) - { - TRACE_SERIALIZE (this); - if (unlikely (!serialize (c, items_len))) return_trace (false); - for (unsigned int i = 0; i < items_len; i++) - array[i] = items[i]; - items.advance (items_len); - return_trace (true); - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - - /* Note: for structs that do not reference other structs, - * we do not need to call their sanitize() as we already did - * a bound check on the aggregate array size. We just include - * a small unreachable expression to make sure the structs - * pointed to do have a simple sanitize(), ie. they do not - * reference other structs via offsets. - */ - (void) (false && array[0].sanitize (c)); - - return_trace (true); - } - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const - { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - unsigned int count = len; - for (unsigned int i = 0; i < count; i++) - if (unlikely (!array[i].sanitize (c, base))) - return_trace (false); - return_trace (true); - } - template - inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const - { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - unsigned int count = len; - for (unsigned int i = 0; i < count; i++) - if (unlikely (!array[i].sanitize (c, base, user_data))) - return_trace (false); - return_trace (true); - } - - template - inline int lsearch (const SearchType &x) const - { - unsigned int count = len; - for (unsigned int i = 0; i < count; i++) - if (!this->array[i].cmp (x)) - return i; - return -1; - } - - private: - inline bool sanitize_shallow (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (len.sanitize (c) && c->check_array (array, Type::static_size, len)); - } - - public: - LenType len; - Type array[VAR]; - public: - DEFINE_SIZE_ARRAY (sizeof (LenType), array); -}; -template struct LArrayOf : ArrayOf {}; - -/* Array of Offset's */ -template -struct OffsetArrayOf : ArrayOf > {}; - -/* Array of offsets relative to the beginning of the array itself. */ -template -struct OffsetListOf : OffsetArrayOf -{ - inline const Type& operator [] (unsigned int i) const - { - if (unlikely (i >= this->len)) return Null(Type); - return this+this->array[i]; - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (OffsetArrayOf::sanitize (c, this)); - } - template - inline bool sanitize (hb_sanitize_context_t *c, T user_data) const - { - TRACE_SANITIZE (this); - return_trace (OffsetArrayOf::sanitize (c, this, user_data)); - } -}; - - -/* An array starting at second element. */ -template -struct HeadlessArrayOf -{ - inline const Type& operator [] (unsigned int i) const - { - if (unlikely (i >= len || !i)) return Null(Type); - return array[i-1]; - } - inline unsigned int get_size (void) const - { return len.static_size + (len ? len - 1 : 0) * Type::static_size; } - - inline bool serialize (hb_serialize_context_t *c, - Supplier &items, - unsigned int items_len) - { - TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); - len.set (items_len); /* TODO(serialize) Overflow? */ - if (unlikely (!items_len)) return_trace (true); - if (unlikely (!c->extend (*this))) return_trace (false); - for (unsigned int i = 0; i < items_len - 1; i++) - array[i] = items[i]; - items.advance (items_len - 1); - return_trace (true); - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - - /* Note: for structs that do not reference other structs, - * we do not need to call their sanitize() as we already did - * a bound check on the aggregate array size. We just include - * a small unreachable expression to make sure the structs - * pointed to do have a simple sanitize(), ie. they do not - * reference other structs via offsets. - */ - (void) (false && array[0].sanitize (c)); - - return_trace (true); - } - - private: - inline bool sanitize_shallow (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (len.sanitize (c) && - (!len || c->check_array (array, Type::static_size, len - 1))); - } - - public: - LenType len; - Type array[VAR]; - public: - DEFINE_SIZE_ARRAY (sizeof (LenType), array); -}; - - -/* - * An array with sorted elements. Supports binary searching. - */ -template -struct SortedArrayOf : ArrayOf -{ - template - inline int bsearch (const SearchType &x) const - { - /* Hand-coded bsearch here since this is in the hot inner loop. */ - const Type *array = this->array; - int min = 0, max = (int) this->len - 1; - while (min <= max) - { - int mid = (min + max) / 2; - int c = array[mid].cmp (x); - if (c < 0) - max = mid - 1; - else if (c > 0) - min = mid + 1; - else - return mid; - } - return -1; - } -}; - -/* - * Binary-search arrays - */ - -struct BinSearchHeader -{ - inline operator uint32_t (void) const { return len; } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - protected: - UINT16 len; - UINT16 searchRangeZ; - UINT16 entrySelectorZ; - UINT16 rangeShiftZ; - - public: - DEFINE_SIZE_STATIC (8); -}; - -template -struct BinSearchArrayOf : SortedArrayOf {}; - - -/* Lazy struct and blob loaders. */ - -/* Logic is shared between hb_lazy_loader_t and hb_lazy_table_loader_t */ -template -struct hb_lazy_loader_t -{ - inline void init (hb_face_t *face_) - { - face = face_; - instance = nullptr; - } - - inline void fini (void) - { - if (instance && instance != &OT::Null(T)) - { - instance->fini(); - free (instance); - } - } - - inline const T* get (void) const - { - retry: - T *p = (T *) hb_atomic_ptr_get (&instance); - if (unlikely (!p)) - { - p = (T *) calloc (1, sizeof (T)); - if (unlikely (!p)) - p = const_cast (&OT::Null(T)); - else - p->init (face); - if (unlikely (!hb_atomic_ptr_cmpexch (const_cast(&instance), nullptr, p))) - { - if (p != &OT::Null(T)) - p->fini (); - goto retry; - } - } - return p; - } - - inline const T* operator-> (void) const - { - return get (); - } - - private: - hb_face_t *face; - T *instance; -}; - -/* Logic is shared between hb_lazy_loader_t and hb_lazy_table_loader_t */ -template -struct hb_lazy_table_loader_t -{ - inline void init (hb_face_t *face_) - { - face = face_; - instance = nullptr; - blob = nullptr; - } - - inline void fini (void) - { - hb_blob_destroy (blob); - } - - inline const T* get (void) const - { - retry: - T *p = (T *) hb_atomic_ptr_get (&instance); - if (unlikely (!p)) - { - hb_blob_t *blob_ = OT::Sanitizer::sanitize (face->reference_table (T::tableTag)); - p = const_cast(OT::Sanitizer::lock_instance (blob_)); - if (!hb_atomic_ptr_cmpexch (const_cast(&instance), nullptr, p)) - { - hb_blob_destroy (blob_); - goto retry; - } - blob = blob_; - } - return p; - } - - inline const T* operator-> (void) const - { - return get(); - } - - private: - hb_face_t *face; - T *instance; - mutable hb_blob_t *blob; -}; - - -} /* namespace OT */ - - -#endif /* HB_OPEN_TYPE_PRIVATE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-open-type.hh b/src/3rdparty/harfbuzz-ng/src/hb-open-type.hh new file mode 100644 index 00000000000..4c9bfebcec1 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-open-type.hh @@ -0,0 +1,1147 @@ +/* + * Copyright ยฉ 2007,2008,2009,2010 Red Hat, Inc. + * Copyright ยฉ 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OPEN_TYPE_HH +#define HB_OPEN_TYPE_HH + +#include "hb.hh" +#include "hb-blob.hh" +#include "hb-face.hh" +#include "hb-machinery.hh" +#include "hb-meta.hh" +#include "hb-subset.hh" + + +namespace OT { + + +/* + * + * The OpenType Font File: Data Types + */ + + +/* "The following data types are used in the OpenType font file. + * All OpenType fonts use Motorola-style byte ordering (Big Endian):" */ + +/* + * Int types + */ + +/* Integer types in big-endian order and no alignment requirement */ +template +struct IntType +{ + typedef Type type; + + IntType () = default; + explicit constexpr IntType (Type V) : v {V} {} + IntType& operator = (Type i) { v = i; return *this; } + /* For reason we define cast out operator for signed/unsigned, instead of Type, see: + * https://github.com/harfbuzz/harfbuzz/pull/2875/commits/09836013995cab2b9f07577a179ad7b024130467 */ + operator typename std::conditional::value, signed, unsigned>::type () const { return v; } + + bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } + bool operator != (const IntType &o) const { return !(*this == o); } + + IntType& operator += (unsigned count) { *this = *this + count; return *this; } + IntType& operator -= (unsigned count) { *this = *this - count; return *this; } + IntType& operator ++ () { *this += 1; return *this; } + IntType& operator -- () { *this -= 1; return *this; } + IntType operator ++ (int) { IntType c (*this); ++*this; return c; } + IntType operator -- (int) { IntType c (*this); --*this; return c; } + + HB_INTERNAL static int cmp (const IntType *a, const IntType *b) + { return b->cmp (*a); } + HB_INTERNAL static int cmp (const void *a, const void *b) + { + IntType *pa = (IntType *) a; + IntType *pb = (IntType *) b; + + return pb->cmp (*pa); + } + template ::value && + sizeof (Type2) < sizeof (int) && + sizeof (Type) < sizeof (int))> + int cmp (Type2 a) const + { + Type b = v; + return (int) a - (int) b; + } + template + int cmp (Type2 a) const + { + Type b = v; + return a < b ? -1 : a == b ? 0 : +1; + } + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + protected: + BEInt v; + public: + DEFINE_SIZE_STATIC (Size); +}; + +typedef IntType HBUINT8; /* 8-bit unsigned integer. */ +typedef IntType HBINT8; /* 8-bit signed integer. */ +typedef IntType HBUINT16; /* 16-bit unsigned integer. */ +typedef IntType HBINT16; /* 16-bit signed integer. */ +typedef IntType HBUINT32; /* 32-bit unsigned integer. */ +typedef IntType HBINT32; /* 32-bit signed integer. */ +/* Note: we cannot defined a signed HBINT24 because there's no corresponding C type. + * Works for unsigned, but not signed, since we rely on compiler for sign-extension. */ +typedef IntType HBUINT24; /* 24-bit unsigned integer. */ + +/* 15-bit unsigned number; top bit used for extension. */ +struct HBUINT15 : HBUINT16 +{ + /* TODO Flesh out; actually mask top bit. */ + HBUINT15& operator = (uint16_t i ) { HBUINT16::operator= (i); return *this; } + public: + DEFINE_SIZE_STATIC (2); +}; + +/* 16-bit signed integer (HBINT16) that describes a quantity in FUnits. */ +typedef HBINT16 FWORD; + +/* 32-bit signed integer (HBINT32) that describes a quantity in FUnits. */ +typedef HBINT32 FWORD32; + +/* 16-bit unsigned integer (HBUINT16) that describes a quantity in FUnits. */ +typedef HBUINT16 UFWORD; + +template +struct HBFixed : Type +{ + static constexpr float shift = (float) (1 << fraction_bits); + static_assert (Type::static_size * 8 > fraction_bits, ""); + + operator signed () const = delete; + operator unsigned () const = delete; + typename Type::type to_int () const { return Type::v; } + void set_int (typename Type::type i ) { Type::v = i; } + float to_float (float offset = 0) const { return ((int32_t) Type::v + offset) / shift; } + void set_float (float f) { Type::v = roundf (f * shift); } + public: + DEFINE_SIZE_STATIC (Type::static_size); +}; + +/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */ +using F2DOT14 = HBFixed; +using F4DOT12 = HBFixed; +using F6DOT10 = HBFixed; + +/* 32-bit signed fixed-point number (16.16). */ +using F16DOT16 = HBFixed; + +/* Date represented in number of seconds since 12:00 midnight, January 1, + * 1904. The value is represented as a signed 64-bit integer. */ +struct LONGDATETIME +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + protected: + HBINT32 major; + HBUINT32 minor; + public: + DEFINE_SIZE_STATIC (8); +}; + +/* Array of four uint8s (length = 32 bits) used to identify a script, language + * system, feature, or baseline */ +struct Tag : HBUINT32 +{ + Tag& operator = (hb_tag_t i) { HBUINT32::operator= (i); return *this; } + /* What the char* converters return is NOT nul-terminated. Print using "%.4s" */ + operator const char* () const { return reinterpret_cast (this); } + operator char* () { return reinterpret_cast (this); } + public: + DEFINE_SIZE_STATIC (4); +}; + +/* Glyph index number, same as uint16 (length = 16 bits) */ +struct HBGlyphID16 : HBUINT16 +{ + HBGlyphID16& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } +}; +struct HBGlyphID24 : HBUINT24 +{ + HBGlyphID24& operator = (uint32_t i) { HBUINT24::operator= (i); return *this; } +}; + +/* Script/language-system/feature index */ +struct Index : HBUINT16 { + static constexpr unsigned NOT_FOUND_INDEX = 0xFFFFu; + Index& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, Index); + +typedef Index NameID; + +struct VarIdx : HBUINT32 { + static constexpr unsigned NO_VARIATION = 0xFFFFFFFFu; + static_assert (NO_VARIATION == HB_OT_LAYOUT_NO_VARIATIONS_INDEX, ""); + static uint32_t add (uint32_t i, unsigned short v) + { + if (i == NO_VARIATION) return i; + return i + v; + } + VarIdx& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; } +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, VarIdx); + +/* Offset, Null offset = 0 */ +template +struct Offset : Type +{ + Offset& operator = (typename Type::type i) { Type::operator= (i); return *this; } + + typedef Type type; + + bool is_null () const { return has_null && 0 == *this; } + + public: + DEFINE_SIZE_STATIC (sizeof (Type)); +}; + +typedef Offset Offset16; +typedef Offset Offset24; +typedef Offset Offset32; + + +/* CheckSum */ +struct CheckSum : HBUINT32 +{ + CheckSum& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; } + + /* This is reference implementation from the spec. */ + static uint32_t CalcTableChecksum (const HBUINT32 *Table, uint32_t Length) + { + uint32_t Sum = 0L; + assert (0 == (Length & 3)); + const HBUINT32 *EndPtr = Table + Length / HBUINT32::static_size; + + while (Table < EndPtr) + Sum += *Table++; + return Sum; + } + + /* Note: data should be 4byte aligned and have 4byte padding at the end. */ + void set_for_data (const void *data, unsigned int length) + { *this = CalcTableChecksum ((const HBUINT32 *) data, length); } + + public: + DEFINE_SIZE_STATIC (4); +}; + + +/* + * Version Numbers + */ + +template +struct FixedVersion +{ + uint32_t to_int () const { return (major << (sizeof (FixedType) * 8)) + minor; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + FixedType major; + FixedType minor; + public: + DEFINE_SIZE_STATIC (2 * sizeof (FixedType)); +}; + + +/* + * Template subclasses of Offset that do the dereferencing. + * Use: (base+offset) + */ + +template +struct _hb_has_null +{ + static const Type *get_null () { return nullptr; } + static Type *get_crap () { return nullptr; } +}; +template +struct _hb_has_null +{ + static const Type *get_null () { return &Null (Type); } + static Type *get_crap () { return &Crap (Type); } +}; + +template +struct OffsetTo : Offset +{ + // Make sure Type is not unbounded; works only for types that are fully defined at OffsetTo time. + static_assert (has_null == false || + (hb_has_null_size (Type) || !hb_has_min_size (Type)), ""); + + HB_DELETE_COPY_ASSIGN (OffsetTo); + OffsetTo () = default; + + OffsetTo& operator = (typename OffsetType::type i) { OffsetType::operator= (i); return *this; } + + const Type& operator () (const void *base) const + { + if (unlikely (this->is_null ())) return *_hb_has_null::get_null (); + return StructAtOffset (base, *this); + } + Type& operator () (void *base) const + { + if (unlikely (this->is_null ())) return *_hb_has_null::get_crap (); + return StructAtOffset (base, *this); + } + + template + friend const Type& operator + (const Base &base, const OffsetTo &offset) { return offset ((const void *) base); } + template + friend const Type& operator + (const OffsetTo &offset, const Base &base) { return offset ((const void *) base); } + template + friend Type& operator + (Base &&base, OffsetTo &offset) { return offset ((void *) base); } + template + friend Type& operator + (OffsetTo &offset, Base &&base) { return offset ((void *) base); } + + + template + bool serialize_subset (hb_subset_context_t *c, const OffsetTo& src, + const void *src_base, Ts&&... ds) + { + *this = 0; + if (src.is_null ()) + return false; + + auto *s = c->serializer; + + s->push (); + + bool ret = c->dispatch (src_base+src, std::forward (ds)...); + + if (ret || !has_null) + s->add_link (*this, s->pop_pack ()); + else + s->pop_discard (); + + return ret; + } + + + template + bool serialize_serialize (hb_serialize_context_t *c, Ts&&... ds) + { + *this = 0; + + Type* obj = c->push (); + bool ret = obj->serialize (c, std::forward (ds)...); + + if (ret) + c->add_link (*this, c->pop_pack ()); + else + c->pop_discard (); + + return ret; + } + + /* TODO: Somehow merge this with previous function into a serialize_dispatch(). */ + /* Workaround clang bug: https://bugs.llvm.org/show_bug.cgi?id=23029 + * Can't compile: whence = hb_serialize_context_t::Head followed by Ts&&... + */ + template + bool serialize_copy (hb_serialize_context_t *c, const OffsetTo& src, + const void *src_base, unsigned dst_bias, + hb_serialize_context_t::whence_t whence, + Ts&&... ds) + { + *this = 0; + if (src.is_null ()) + return false; + + c->push (); + + bool ret = c->copy (src_base+src, std::forward (ds)...); + + c->add_link (*this, c->pop_pack (), whence, dst_bias); + + return ret; + } + + bool serialize_copy (hb_serialize_context_t *c, const OffsetTo& src, + const void *src_base, unsigned dst_bias = 0) + { return serialize_copy (c, src, src_base, dst_bias, hb_serialize_context_t::Head); } + + bool sanitize_shallow (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + if (unlikely (this->is_null ())) return_trace (true); + if (unlikely ((const char *) base + (unsigned) *this < (const char *) base)) return_trace (false); + return_trace (true); + } + + template + bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace (sanitize_shallow (c, base) && + (this->is_null () || + c->dispatch (StructAtOffset (base, *this), std::forward (ds)...) || + neuter (c))); + } + + /* Set the offset to Null */ + bool neuter (hb_sanitize_context_t *c) const + { + if (!has_null) return false; + return c->try_set (this, 0); + } + DEFINE_SIZE_STATIC (sizeof (OffsetType)); +}; +/* Partial specializations. */ +template using Offset16To = OffsetTo; +template using Offset24To = OffsetTo; +template using Offset32To = OffsetTo; + +template using NNOffsetTo = OffsetTo; +template using NNOffset16To = Offset16To; +template using NNOffset24To = Offset24To; +template using NNOffset32To = Offset32To; + + +/* + * Array Types + */ + +template +struct UnsizedArrayOf +{ + typedef Type item_t; + static constexpr unsigned item_size = hb_static_size (Type); + + HB_DELETE_CREATE_COPY_ASSIGN (UnsizedArrayOf); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + const Type *p = &arrayZ[i]; + if (unlikely ((const void *) p < (const void *) arrayZ)) return Null (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); + return *p; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + Type *p = &arrayZ[i]; + if (unlikely ((const void *) p < (const void *) arrayZ)) return Crap (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); + return *p; + } + + unsigned int get_size (unsigned int len) const + { return len * Type::static_size; } + + template operator T * () { return arrayZ; } + template operator const T * () const { return arrayZ; } + hb_array_t as_array (unsigned int len) + { return hb_array (arrayZ, len); } + hb_array_t as_array (unsigned int len) const + { return hb_array (arrayZ, len); } + + template + Type &lsearch (unsigned int len, const T &x, Type ¬_found = Crap (Type)) + { return *as_array (len).lsearch (x, ¬_found); } + template + const Type &lsearch (unsigned int len, const T &x, const Type ¬_found = Null (Type)) const + { return *as_array (len).lsearch (x, ¬_found); } + template + bool lfind (unsigned int len, const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array (len).lfind (x, i, not_found, to_store); } + + void qsort (unsigned int len, unsigned int start = 0, unsigned int end = (unsigned int) -1) + { as_array (len).qsort (start, end); } + + bool serialize (hb_serialize_context_t *c, unsigned int items_len, bool clear = true) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_size (this, get_size (items_len), clear))) return_trace (false); + return_trace (true); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = hb_len (items); + if (unlikely (!serialize (c, count, false))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; + return_trace (true); + } + + UnsizedArrayOf* copy (hb_serialize_context_t *c, unsigned count) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!as_array (count).copy (c))) return_trace (nullptr); + return_trace (out); + } + + template + bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c, count))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + bool sanitize_shallow (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_array (arrayZ, count)); + } + + public: + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_UNBOUNDED (0); +}; + +/* Unsized array of offset's */ +template +using UnsizedArray16OfOffsetTo = UnsizedArrayOf>; + +/* Unsized array of offsets relative to the beginning of the array itself. */ +template +struct UnsizedListOfOffset16To : UnsizedArray16OfOffsetTo +{ + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + const OffsetTo *p = &this->arrayZ[i]; + if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Null (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); + return this+*p; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + const OffsetTo *p = &this->arrayZ[i]; + if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Crap (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); + return this+*p; + } + + template + bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace ((UnsizedArray16OfOffsetTo + ::sanitize (c, count, this, std::forward (ds)...))); + } +}; + +/* An array with sorted elements. Supports binary searching. */ +template +struct SortedUnsizedArrayOf : UnsizedArrayOf +{ + hb_sorted_array_t as_array (unsigned int len) + { return hb_sorted_array (this->arrayZ, len); } + hb_sorted_array_t as_array (unsigned int len) const + { return hb_sorted_array (this->arrayZ, len); } + operator hb_sorted_array_t () { return as_array (); } + operator hb_sorted_array_t () const { return as_array (); } + + template + Type &bsearch (unsigned int len, const T &x, Type ¬_found = Crap (Type)) + { return *as_array (len).bsearch (x, ¬_found); } + template + const Type &bsearch (unsigned int len, const T &x, const Type ¬_found = Null (Type)) const + { return *as_array (len).bsearch (x, ¬_found); } + template + bool bfind (unsigned int len, const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array (len).bfind (x, i, not_found, to_store); } +}; + + +/* An array with a number of elements. */ +template +struct ArrayOf +{ + typedef Type item_t; + static constexpr unsigned item_size = hb_static_size (Type); + + HB_DELETE_CREATE_COPY_ASSIGN (ArrayOf); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= len)) return Null (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i]; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= len)) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i]; + } + + unsigned int get_size () const + { return len.static_size + len * Type::static_size; } + + explicit operator bool () const { return len; } + + void pop () { len--; } + + hb_array_t< Type> as_array () { return hb_array (arrayZ, len); } + hb_array_t as_array () const { return hb_array (arrayZ, len); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + /* Faster range-based for loop. */ + const Type *begin () const { return arrayZ; } + const Type *end () const { return arrayZ + len; } + + template + Type &lsearch (const T &x, Type ¬_found = Crap (Type)) + { return *as_array ().lsearch (x, ¬_found); } + template + const Type &lsearch (const T &x, const Type ¬_found = Null (Type)) const + { return *as_array ().lsearch (x, ¬_found); } + template + bool lfind (const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array ().lfind (x, i, not_found, to_store); } + + void qsort () + { as_array ().qsort (); } + + HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned items_len, bool clear = true) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + c->check_assign (len, items_len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); + if (unlikely (!c->extend_size (this, get_size (), clear))) return_trace (false); + return_trace (true); + } + template + HB_NODISCARD bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = hb_len (items); + if (unlikely (!serialize (c, count, false))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; + return_trace (true); + } + + Type* serialize_append (hb_serialize_context_t *c) + { + TRACE_SERIALIZE (this); + len++; + if (unlikely (!len || !c->extend (this))) + { + len--; + return_trace (nullptr); + } + return_trace (&arrayZ[len - 1]); + } + + ArrayOf* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!c->extend_min (out))) return_trace (nullptr); + c->check_assign (out->len, len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); + if (unlikely (!as_array ().copy (c))) return_trace (nullptr); + return_trace (out); + } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (len.sanitize (c) && c->check_array (arrayZ, len)); + } + + public: + LenType len; + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); +}; +template using Array16Of = ArrayOf; +template using Array24Of = ArrayOf; +template using Array32Of = ArrayOf; +using PString = ArrayOf; + +/* Array of Offset's */ +template using Array16OfOffset16To = ArrayOf, HBUINT16>; +template using Array16OfOffset32To = ArrayOf, HBUINT16>; +template using Array32OfOffset32To = ArrayOf, HBUINT32>; + +/* Array of offsets relative to the beginning of the array itself. */ +template +struct List16OfOffsetTo : ArrayOf, HBUINT16> +{ + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= this->len)) return Null (Type); + _hb_compiler_memory_r_barrier (); + return this+this->arrayZ[i]; + } + const Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= this->len)) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return this+this->arrayZ[i]; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + struct List16OfOffsetTo *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + unsigned int count = this->len; + for (unsigned int i = 0; i < count; i++) + out->arrayZ[i].serialize_subset (c, this->arrayZ[i], this, out); + return_trace (true); + } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace ((Array16Of>::sanitize (c, this, std::forward (ds)...))); + } +}; + +template +using List16OfOffset16To = List16OfOffsetTo; + +/* An array starting at second element. */ +template +struct HeadlessArrayOf +{ + static constexpr unsigned item_size = Type::static_size; + + HB_DELETE_CREATE_COPY_ASSIGN (HeadlessArrayOf); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= lenP1 || !i)) return Null (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i-1]; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= lenP1 || !i)) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i-1]; + } + unsigned int get_size () const + { return lenP1.static_size + get_length () * Type::static_size; } + + unsigned get_length () const { return lenP1 ? lenP1 - 1 : 0; } + + hb_array_t< Type> as_array () { return hb_array (arrayZ, get_length ()); } + hb_array_t as_array () const { return hb_array (arrayZ, get_length ()); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + /* Faster range-based for loop. */ + const Type *begin () const { return arrayZ; } + const Type *end () const { return arrayZ + get_length (); } + + HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned int items_len, bool clear = true) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + c->check_assign (lenP1, items_len + 1, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); + if (unlikely (!c->extend_size (this, get_size (), clear))) return_trace (false); + return_trace (true); + } + template + HB_NODISCARD bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = hb_len (items); + if (unlikely (!serialize (c, count, false))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; + return_trace (true); + } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + unsigned int count = get_length (); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + private: + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (lenP1.sanitize (c) && + (!lenP1 || c->check_array (arrayZ, lenP1 - 1))); + } + + public: + LenType lenP1; + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); +}; + +/* An array storing length-1. */ +template +struct ArrayOfM1 +{ + HB_DELETE_CREATE_COPY_ASSIGN (ArrayOfM1); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i > lenM1)) return Null (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i]; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i > lenM1)) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i]; + } + unsigned int get_size () const + { return lenM1.static_size + (lenM1 + 1) * Type::static_size; } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + unsigned int count = lenM1 + 1; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + private: + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (lenM1.sanitize (c) && + (c->check_array (arrayZ, lenM1 + 1))); + } + + public: + LenType lenM1; + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); +}; + +/* An array with sorted elements. Supports binary searching. */ +template +struct SortedArrayOf : ArrayOf +{ + hb_sorted_array_t< Type> as_array () { return hb_sorted_array (this->arrayZ, this->len); } + hb_sorted_array_t as_array () const { return hb_sorted_array (this->arrayZ, this->len); } + + /* Iterator. */ + typedef hb_sorted_array_t iter_t; + typedef hb_sorted_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + /* Faster range-based for loop. */ + const Type *begin () const { return this->arrayZ; } + const Type *end () const { return this->arrayZ + this->len; } + + bool serialize (hb_serialize_context_t *c, unsigned int items_len) + { + TRACE_SERIALIZE (this); + bool ret = ArrayOf::serialize (c, items_len); + return_trace (ret); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + bool ret = ArrayOf::serialize (c, items); + return_trace (ret); + } + + template + Type &bsearch (const T &x, Type ¬_found = Crap (Type)) + { return *as_array ().bsearch (x, ¬_found); } + template + const Type &bsearch (const T &x, const Type ¬_found = Null (Type)) const + { return *as_array ().bsearch (x, ¬_found); } + template + bool bfind (const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array ().bfind (x, i, not_found, to_store); } +}; + +template using SortedArray16Of = SortedArrayOf; +template using SortedArray24Of = SortedArrayOf; +template using SortedArray32Of = SortedArrayOf; + +/* + * Binary-search arrays + */ + +template +struct BinSearchHeader +{ + operator uint32_t () const { return len; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + BinSearchHeader& operator = (unsigned int v) + { + len = v; + assert (len == v); + entrySelector = hb_max (1u, hb_bit_storage (v)) - 1; + searchRange = 16 * (1u << entrySelector); + rangeShift = v * 16 > searchRange + ? 16 * v - searchRange + : 0; + return *this; + } + + protected: + LenType len; + LenType searchRange; + LenType entrySelector; + LenType rangeShift; + + public: + DEFINE_SIZE_STATIC (8); +}; + +template +using BinSearchArrayOf = SortedArrayOf>; + + +struct VarSizedBinSearchHeader +{ + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 unitSize; /* Size of a lookup unit for this search in bytes. */ + HBUINT16 nUnits; /* Number of units of the preceding size to be searched. */ + HBUINT16 searchRange; /* The value of unitSize times the largest power of 2 + * that is less than or equal to the value of nUnits. */ + HBUINT16 entrySelector; /* The log base 2 of the largest power of 2 less than + * or equal to the value of nUnits. */ + HBUINT16 rangeShift; /* The value of unitSize times the difference of the + * value of nUnits minus the largest power of 2 less + * than or equal to the value of nUnits. */ + public: + DEFINE_SIZE_STATIC (10); +}; + +template +struct VarSizedBinSearchArrayOf +{ + static constexpr unsigned item_size = Type::static_size; + + HB_DELETE_CREATE_COPY_ASSIGN (VarSizedBinSearchArrayOf); + + bool last_is_terminator () const + { + if (unlikely (!header.nUnits)) return false; + + /* Gah. + * + * "The number of termination values that need to be included is table-specific. + * The value that indicates binary search termination is 0xFFFF." */ + const HBUINT16 *words = &StructAtOffset (&bytesZ, (header.nUnits - 1) * header.unitSize); + unsigned int count = Type::TerminationWordCount; + for (unsigned int i = 0; i < count; i++) + if (words[i] != 0xFFFFu) + return false; + return true; + } + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= get_length ())) return Null (Type); + _hb_compiler_memory_r_barrier (); + return StructAtOffset (&bytesZ, i * header.unitSize); + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= get_length ())) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return StructAtOffset (&bytesZ, i * header.unitSize); + } + unsigned int get_length () const + { return header.nUnits - last_is_terminator (); } + unsigned int get_size () const + { return header.static_size + header.nUnits * header.unitSize; } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + unsigned int count = get_length (); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!(*this)[i].sanitize (c, std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + template + const Type *bsearch (const T &key) const + { + unsigned pos; + return hb_bsearch_impl (&pos, + key, + (const void *) bytesZ, + get_length (), + header.unitSize, + _hb_cmp_method) + ? (const Type *) (((const char *) &bytesZ) + (pos * header.unitSize)) + : nullptr; + } + + private: + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (header.sanitize (c) && + Type::static_size <= header.unitSize && + c->check_range (bytesZ.arrayZ, + header.nUnits, + header.unitSize)); + } + + protected: + VarSizedBinSearchHeader header; + UnsizedArrayOf bytesZ; + public: + DEFINE_SIZE_ARRAY (10, bytesZ); +}; + + +} /* namespace OT */ + + +#endif /* HB_OPEN_TYPE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cbdt-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-cbdt-table.hh deleted file mode 100644 index 415625e5f80..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-cbdt-table.hh +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright ยฉ 2016 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Seigo Nonaka - */ - -#ifndef HB_OT_CBDT_TABLE_HH -#define HB_OT_CBDT_TABLE_HH - -#include "hb-open-type-private.hh" - -namespace OT { - -struct SmallGlyphMetrics -{ - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - inline void get_extents (hb_glyph_extents_t *extents) const - { - extents->x_bearing = bearingX; - extents->y_bearing = bearingY; - extents->width = width; - extents->height = -height; - } - - UINT8 height; - UINT8 width; - INT8 bearingX; - INT8 bearingY; - UINT8 advance; - - DEFINE_SIZE_STATIC(5); -}; - -struct BigGlyphMetrics : SmallGlyphMetrics -{ - INT8 vertBearingX; - INT8 vertBearingY; - UINT8 vertAdvance; - - DEFINE_SIZE_STATIC(8); -}; - -struct SBitLineMetrics -{ - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - INT8 ascender; - INT8 decender; - UINT8 widthMax; - INT8 caretSlopeNumerator; - INT8 caretSlopeDenominator; - INT8 caretOffset; - INT8 minOriginSB; - INT8 minAdvanceSB; - INT8 maxBeforeBL; - INT8 minAfterBL; - INT8 padding1; - INT8 padding2; - - DEFINE_SIZE_STATIC(12); -}; - - -/* - * Index Subtables. - */ - -struct IndexSubtableHeader -{ - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - UINT16 indexFormat; - UINT16 imageFormat; - UINT32 imageDataOffset; - - DEFINE_SIZE_STATIC(8); -}; - -template -struct IndexSubtableFormat1Or3 -{ - inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - c->check_array (offsetArrayZ, offsetArrayZ[0].static_size, glyph_count + 1)); - } - - bool get_image_data (unsigned int idx, - unsigned int *offset, - unsigned int *length) const - { - if (unlikely (offsetArrayZ[idx + 1] <= offsetArrayZ[idx])) - return false; - - *offset = header.imageDataOffset + offsetArrayZ[idx]; - *length = offsetArrayZ[idx + 1] - offsetArrayZ[idx]; - return true; - } - - IndexSubtableHeader header; - Offset offsetArrayZ[VAR]; - - DEFINE_SIZE_ARRAY(8, offsetArrayZ); -}; - -struct IndexSubtableFormat1 : IndexSubtableFormat1Or3 {}; -struct IndexSubtableFormat3 : IndexSubtableFormat1Or3 {}; - -struct IndexSubtable -{ - inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const - { - TRACE_SANITIZE (this); - if (!u.header.sanitize (c)) return_trace (false); - switch (u.header.indexFormat) { - case 1: return_trace (u.format1.sanitize (c, glyph_count)); - case 3: return_trace (u.format3.sanitize (c, glyph_count)); - default:return_trace (true); - } - } - - inline bool get_extents (hb_glyph_extents_t *extents) const - { - switch (u.header.indexFormat) { - case 2: case 5: /* TODO */ - case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */ - default:return (false); - } - } - - bool get_image_data (unsigned int idx, - unsigned int *offset, - unsigned int *length, - unsigned int *format) const - { - *format = u.header.imageFormat; - switch (u.header.indexFormat) { - case 1: return u.format1.get_image_data (idx, offset, length); - case 3: return u.format3.get_image_data (idx, offset, length); - default: return false; - } - } - - protected: - union { - IndexSubtableHeader header; - IndexSubtableFormat1 format1; - IndexSubtableFormat3 format3; - /* TODO: Format 2, 4, 5. */ - } u; - public: - DEFINE_SIZE_UNION (8, header); -}; - -struct IndexSubtableRecord -{ - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - firstGlyphIndex <= lastGlyphIndex && - offsetToSubtable.sanitize (c, this, lastGlyphIndex - firstGlyphIndex + 1)); - } - - inline bool get_extents (hb_glyph_extents_t *extents) const - { - return (this+offsetToSubtable).get_extents (extents); - } - - bool get_image_data (unsigned int gid, - unsigned int *offset, - unsigned int *length, - unsigned int *format) const - { - if (gid < firstGlyphIndex || gid > lastGlyphIndex) - { - return false; - } - return (this+offsetToSubtable).get_image_data (gid - firstGlyphIndex, - offset, length, format); - } - - UINT16 firstGlyphIndex; - UINT16 lastGlyphIndex; - LOffsetTo offsetToSubtable; - - DEFINE_SIZE_STATIC(8); -}; - -struct IndexSubtableArray -{ - inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const - { - TRACE_SANITIZE (this); - if (unlikely (!c->check_array (&indexSubtablesZ, indexSubtablesZ[0].static_size, count))) - return_trace (false); - for (unsigned int i = 0; i < count; i++) - if (unlikely (!indexSubtablesZ[i].sanitize (c, this))) - return_trace (false); - return_trace (true); - } - - public: - const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const - { - for (unsigned int i = 0; i < numTables; ++i) - { - unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex; - unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex; - if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex) { - return &indexSubtablesZ[i]; - } - } - return nullptr; - } - - protected: - IndexSubtableRecord indexSubtablesZ[VAR]; - - public: - DEFINE_SIZE_ARRAY(0, indexSubtablesZ); -}; - -struct BitmapSizeTable -{ - friend struct CBLC; - - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) && - c->check_range (&(base+indexSubtableArrayOffset), indexTablesSize) && - horizontal.sanitize (c) && - vertical.sanitize (c)); - } - - const IndexSubtableRecord *find_table (hb_codepoint_t glyph, const void *base) const - { - return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables); - } - - protected: - LOffsetTo indexSubtableArrayOffset; - UINT32 indexTablesSize; - UINT32 numberOfIndexSubtables; - UINT32 colorRef; - SBitLineMetrics horizontal; - SBitLineMetrics vertical; - UINT16 startGlyphIndex; - UINT16 endGlyphIndex; - UINT8 ppemX; - UINT8 ppemY; - UINT8 bitDepth; - INT8 flags; - - public: - DEFINE_SIZE_STATIC(48); -}; - - -/* - * Glyph Bitmap Data Formats. - */ - -struct GlyphBitmapDataFormat17 -{ - SmallGlyphMetrics glyphMetrics; - UINT32 dataLen; - UINT8 dataZ[VAR]; - - DEFINE_SIZE_ARRAY(9, dataZ); -}; - - -/* - * CBLC -- Color Bitmap Location Table - */ - -#define HB_OT_TAG_CBLC HB_TAG('C','B','L','C') - -struct CBLC -{ - friend struct CBDT; - - static const hb_tag_t tableTag = HB_OT_TAG_CBLC; - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - likely (version.major == 2 || version.major == 3) && - sizeTables.sanitize (c, this)); - } - - protected: - const IndexSubtableRecord *find_table (hb_codepoint_t glyph, - unsigned int *x_ppem, unsigned int *y_ppem) const - { - /* TODO: Make it possible to select strike. */ - - unsigned int count = sizeTables.len; - for (uint32_t i = 0; i < count; ++i) - { - unsigned int startGlyphIndex = sizeTables.array[i].startGlyphIndex; - unsigned int endGlyphIndex = sizeTables.array[i].endGlyphIndex; - if (startGlyphIndex <= glyph && glyph <= endGlyphIndex) - { - *x_ppem = sizeTables[i].ppemX; - *y_ppem = sizeTables[i].ppemY; - return sizeTables[i].find_table (glyph, this); - } - } - - return nullptr; - } - - protected: - FixedVersion<> version; - LArrayOf sizeTables; - - public: - DEFINE_SIZE_ARRAY(8, sizeTables); -}; - -/* - * CBDT -- Color Bitmap Data Table - */ -#define HB_OT_TAG_CBDT HB_TAG('C','B','D','T') - -struct CBDT -{ - static const hb_tag_t tableTag = HB_OT_TAG_CBDT; - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - likely (version.major == 2 || version.major == 3)); - } - - struct accelerator_t - { - inline void init (hb_face_t *face) - { - upem = face->get_upem(); - - cblc_blob = Sanitizer::sanitize (face->reference_table (HB_OT_TAG_CBLC)); - cbdt_blob = Sanitizer::sanitize (face->reference_table (HB_OT_TAG_CBDT)); - cbdt_len = hb_blob_get_length (cbdt_blob); - - if (hb_blob_get_length (cblc_blob) == 0) { - cblc = nullptr; - cbdt = nullptr; - return; /* Not a bitmap font. */ - } - cblc = Sanitizer::lock_instance (cblc_blob); - cbdt = Sanitizer::lock_instance (cbdt_blob); - - } - - inline void fini (void) - { - hb_blob_destroy (this->cblc_blob); - hb_blob_destroy (this->cbdt_blob); - } - - inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const - { - unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */ - - if (!cblc) - return false; // Not a color bitmap font. - - const IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem); - if (!subtable_record || !x_ppem || !y_ppem) - return false; - - if (subtable_record->get_extents (extents)) - return true; - - unsigned int image_offset = 0, image_length = 0, image_format = 0; - if (!subtable_record->get_image_data (glyph, &image_offset, &image_length, &image_format)) - return false; - - { - if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) - return false; - - switch (image_format) - { - case 17: { - if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) - return false; - - const GlyphBitmapDataFormat17& glyphFormat17 = - StructAtOffset (this->cbdt, image_offset); - glyphFormat17.glyphMetrics.get_extents (extents); - } - break; - default: - // TODO: Support other image formats. - return false; - } - } - - /* Convert to the font units. */ - extents->x_bearing *= upem / (float) x_ppem; - extents->y_bearing *= upem / (float) y_ppem; - extents->width *= upem / (float) x_ppem; - extents->height *= upem / (float) y_ppem; - - return true; - } - - private: - hb_blob_t *cblc_blob; - hb_blob_t *cbdt_blob; - const CBLC *cblc; - const CBDT *cbdt; - - unsigned int cbdt_len; - unsigned int upem; - }; - - - protected: - FixedVersion<>version; - UINT8 dataZ[VAR]; - - public: - DEFINE_SIZE_ARRAY(4, dataZ); -}; - -} /* namespace OT */ - -#endif /* HB_OT_CBDT_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cff-common.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff-common.hh new file mode 100644 index 00000000000..f22824fc699 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff-common.hh @@ -0,0 +1,516 @@ +/* + * Copyright ยฉ 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_OT_CFF_COMMON_HH +#define HB_OT_CFF_COMMON_HH + +#include "hb-open-type.hh" +#include "hb-bimap.hh" +#include "hb-ot-layout-common.hh" +#include "hb-cff-interp-dict-common.hh" +#include "hb-subset-plan.hh" + +namespace CFF { + +using namespace OT; + +#define CFF_UNDEF_CODE 0xFFFFFFFF + +using objidx_t = hb_serialize_context_t::objidx_t; +using whence_t = hb_serialize_context_t::whence_t; + +/* utility macro */ +template +static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset) +{ return offset ? StructAtOffset (P, offset) : Null (Type); } + +struct code_pair_t +{ + hb_codepoint_t code; + hb_codepoint_t glyph; +}; + +using str_buff_t = hb_vector_t; +using str_buff_vec_t = hb_vector_t; + +/* CFF INDEX */ +template +struct CFFIndex +{ + unsigned int offset_array_size () const + { return offSize * (count + 1); } + + CFFIndex *copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + unsigned int size = get_size (); + CFFIndex *out = c->allocate_size (size, false); + if (likely (out)) + hb_memcpy (out, this, size); + return_trace (out); + } + + template + bool serialize (hb_serialize_context_t *c, + const Iterable &iterable) + { + TRACE_SERIALIZE (this); + auto it = hb_iter (iterable); + serialize_header(c, + it | hb_map (hb_iter) | hb_map (hb_len)); + for (const auto &_ : +it) + hb_iter (_).copy (c); + return_trace (true); + } + + template + bool serialize_header (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + + unsigned total = + it | hb_reduce (hb_add, 0); + unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8; + + /* serialize CFFIndex header */ + if (unlikely (!c->extend_min (this))) return_trace (false); + this->count = it.len (); + if (!this->count) return_trace (true); + if (unlikely (!c->extend (this->offSize))) return_trace (false); + this->offSize = off_size; + if (unlikely (!c->allocate_size (off_size * (this->count + 1), false))) + return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + unsigned int i = 0; + for (unsigned _ : +it) + { + set_offset_at (i++, offset); + offset += _; + } + set_offset_at (i, offset); + + return_trace (true); + } + + template + static unsigned total_size (const Iterable &iterable) + { + auto it = + hb_iter (iterable) | hb_map (hb_iter) | hb_map (hb_len); + if (!it) return 0; + + unsigned total = + it | hb_reduce (hb_add, 0); + unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8; + + return min_size + HBUINT8::static_size + (hb_len (it) + 1) * off_size + total; + } + + void set_offset_at (unsigned int index, unsigned int offset) + { + assert (index <= count); + HBUINT8 *p = offsets + offSize * index + offSize; + unsigned int size = offSize; + for (; size; size--) + { + --p; + *p = offset & 0xFF; + offset >>= 8; + } + } + + private: + unsigned int offset_at (unsigned int index) const + { + assert (index <= count); + + unsigned int size = offSize; + const HBUINT8 *p = offsets + size * index; + switch (size) + { + case 1: return * (HBUINT8 *) p; + case 2: return * (HBUINT16 *) p; + case 3: return * (HBUINT24 *) p; + case 4: return * (HBUINT32 *) p; + default: return 0; + } + } + + unsigned int length_at (unsigned int index) const + { + unsigned offset0 = offset_at (index); + unsigned offset1 = offset_at (index + 1); + if (unlikely (offset1 < offset0 || offset1 > offset_at (count))) + return 0; + return offset1 - offset0; + } + + const unsigned char *data_base () const + { return (const unsigned char *) this + min_size + offSize.static_size + offset_array_size (); } + public: + + hb_ubytes_t operator [] (unsigned int index) const + { + if (unlikely (index >= count)) return hb_ubytes_t (); + _hb_compiler_memory_r_barrier (); + unsigned length = length_at (index); + if (unlikely (!length)) return hb_ubytes_t (); + return hb_ubytes_t (data_base () + offset_at (index) - 1, length); + } + + unsigned int get_size () const + { + if (count) + return min_size + offSize.static_size + offset_array_size () + (offset_at (count) - 1); + return min_size; /* empty CFFIndex contains count only */ + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (count == 0 || /* empty INDEX */ + (count < count + 1u && + c->check_struct (&offSize) && offSize >= 1 && offSize <= 4 && + c->check_array (offsets, offSize, count + 1u) && + c->check_array ((const HBUINT8*) data_base (), 1, offset_at (count) - 1))))); + } + + public: + COUNT count; /* Number of object data. Note there are (count+1) offsets */ + private: + HBUINT8 offSize; /* The byte size of each offset in the offsets array. */ + HBUINT8 offsets[HB_VAR_ARRAY]; + /* The array of (count + 1) offsets into objects array (1-base). */ + /* HBUINT8 data[HB_VAR_ARRAY]; Object data */ + public: + DEFINE_SIZE_MIN (COUNT::static_size); +}; + +template +struct CFFIndexOf : CFFIndex +{ + template + bool serialize (hb_serialize_context_t *c, + unsigned int offSize_, + const DATA *dataArray, + unsigned int dataArrayLen, + const hb_vector_t &dataSizeArray, + const PARAM1 ¶m1, + const PARAM2 ¶m2) + { + TRACE_SERIALIZE (this); + /* serialize CFFIndex header */ + if (unlikely (!c->extend_min (this))) return_trace (false); + this->count = dataArrayLen; + this->offSize = offSize_; + if (unlikely (!c->allocate_size (offSize_ * (dataArrayLen + 1), false))) + return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + unsigned int i = 0; + for (; i < dataArrayLen; i++) + { + this->set_offset_at (i, offset); + offset += dataSizeArray[i]; + } + this->set_offset_at (i, offset); + + /* serialize data */ + for (unsigned int i = 0; i < dataArrayLen; i++) + { + TYPE *dest = c->start_embed (); + if (unlikely (!dest || !dest->serialize (c, dataArray[i], param1, param2))) + return_trace (false); + } + return_trace (true); + } +}; + +/* Top Dict, Font Dict, Private Dict */ +struct Dict : UnsizedByteStr +{ + template + bool serialize (hb_serialize_context_t *c, + const DICTVAL &dictval, + OP_SERIALIZER& opszr, + Ts&&... ds) + { + TRACE_SERIALIZE (this); + for (unsigned int i = 0; i < dictval.get_count (); i++) + if (unlikely (!opszr.serialize (c, dictval[i], std::forward (ds)...))) + return_trace (false); + + return_trace (true); + } + + template + static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, V value, op_code_t intOp) + { + if (unlikely ((!serialize_int (c, intOp, value)))) + return false; + + TRACE_SERIALIZE (this); + /* serialize the opcode */ + HBUINT8 *p = c->allocate_size (OpCode_Size (op), false); + if (unlikely (!p)) return_trace (false); + if (Is_OpCode_ESC (op)) + { + *p = OpCode_escape; + op = Unmake_OpCode_ESC (op); + p++; + } + *p = op; + return_trace (true); + } + + template + static bool serialize_int4_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op (c, op, value, OpCode_longintdict); } + + template + static bool serialize_int2_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op (c, op, value, OpCode_shortint); } + + template + static bool serialize_link_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence) + { + T &ofs = *(T *) (c->head + OpCode_Size (int_op)); + if (unlikely (!serialize_int_op (c, op, 0, int_op))) return false; + c->add_link (ofs, link, whence); + return true; + } + + static bool serialize_link4_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head) + { return serialize_link_op (c, op, link, whence); } + + static bool serialize_link2_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head) + { return serialize_link_op (c, op, link, whence); } +}; + +struct TopDict : Dict {}; +struct FontDict : Dict {}; +struct PrivateDict : Dict {}; + +struct table_info_t +{ + void init () { offset = size = 0; link = 0; } + + unsigned int offset; + unsigned int size; + objidx_t link; +}; + +template +struct FDArray : CFFIndexOf +{ + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + OP_SERIALIZER& opszr) + { + TRACE_SERIALIZE (this); + + /* serialize INDEX data */ + hb_vector_t sizes; + c->push (); + + it + | hb_map ([&] (const hb_pair_t &_) + { + FontDict *dict = c->start_embed (); + dict->serialize (c, _.first, opszr, _.second); + return c->head - (const char*)dict; + }) + | hb_sink (sizes) + ; + c->pop_pack (false); + + /* serialize INDEX header */ + return_trace (CFFIndex::serialize_header (c, hb_iter (sizes))); + } +}; + +/* FDSelect */ +struct FDSelect0 { + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this)))) + return_trace (false); + if (unlikely (!c->check_array (fds, c->get_num_glyphs ()))) + return_trace (false); + + return_trace (true); + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { return (hb_codepoint_t) fds[glyph]; } + + unsigned int get_size (unsigned int num_glyphs) const + { return HBUINT8::static_size * num_glyphs; } + + HBUINT8 fds[HB_VAR_ARRAY]; + + DEFINE_SIZE_MIN (0); +}; + +template +struct FDSelect3_4_Range +{ + bool sanitize (hb_sanitize_context_t *c, const void * /*nullptr*/, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + return_trace (first < c->get_num_glyphs () && (fd < fdcount)); + } + + GID_TYPE first; + FD_TYPE fd; + public: + DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size); +}; + +template +struct FDSelect3_4 +{ + unsigned int get_size () const + { return GID_TYPE::static_size * 2 + ranges.get_size (); } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this) || !ranges.sanitize (c, nullptr, fdcount) || + (nRanges () == 0) || ranges[0].first != 0)) + return_trace (false); + + for (unsigned int i = 1; i < nRanges (); i++) + if (unlikely (ranges[i - 1].first >= ranges[i].first)) + return_trace (false); + + if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ()))) + return_trace (false); + + return_trace (true); + } + + static int _cmp_range (const void *_key, const void *_item) + { + hb_codepoint_t glyph = * (hb_codepoint_t *) _key; + FDSelect3_4_Range *range = (FDSelect3_4_Range *) _item; + + if (glyph < range[0].first) return -1; + if (glyph < range[1].first) return 0; + return +1; + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { + auto *range = hb_bsearch (glyph, &ranges[0], nRanges () - 1, sizeof (ranges[0]), _cmp_range); + return range ? range->fd : ranges[nRanges () - 1].fd; + } + + GID_TYPE &nRanges () { return ranges.len; } + GID_TYPE nRanges () const { return ranges.len; } + GID_TYPE &sentinel () { return StructAfter (ranges[nRanges () - 1]); } + const GID_TYPE &sentinel () const { return StructAfter (ranges[nRanges () - 1]); } + + ArrayOf, GID_TYPE> ranges; + /* GID_TYPE sentinel */ + + DEFINE_SIZE_ARRAY (GID_TYPE::static_size, ranges); +}; + +typedef FDSelect3_4 FDSelect3; +typedef FDSelect3_4_Range FDSelect3_Range; + +struct FDSelect +{ + bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (num_glyphs); + FDSelect *dest = c->allocate_size (size, false); + if (unlikely (!dest)) return_trace (false); + hb_memcpy (dest, &src, size); + return_trace (true); + } + + unsigned int get_size (unsigned int num_glyphs) const + { + switch (format) + { + case 0: return format.static_size + u.format0.get_size (num_glyphs); + case 3: return format.static_size + u.format3.get_size (); + default:return 0; + } + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { + if (this == &Null (FDSelect)) return 0; + + switch (format) + { + case 0: return u.format0.get_fd (glyph); + case 3: return u.format3.get_fd (glyph); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, fdcount)); + case 3: return_trace (u.format3.sanitize (c, fdcount)); + default:return_trace (false); + } + } + + HBUINT8 format; + union { + FDSelect0 format0; + FDSelect3 format3; + } u; + public: + DEFINE_SIZE_MIN (1); +}; + +template +struct Subrs : CFFIndex +{ + typedef COUNT count_type; + typedef CFFIndex SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_OT_CFF_COMMON_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-std-str.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-std-str.hh new file mode 100644 index 00000000000..65d56ae18b5 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-std-str.hh @@ -0,0 +1,425 @@ +/* + * Copyright ยฉ 2019 Adobe, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF1_STD_STR_HH +#if 0 /* Make checks happy. */ +#define HB_OT_CFF1_STD_STR_HH +#include "hb.hh" +#endif + +_S(".notdef") +_S("space") +_S("exclam") +_S("quotedbl") +_S("numbersign") +_S("dollar") +_S("percent") +_S("ampersand") +_S("quoteright") +_S("parenleft") +_S("parenright") +_S("asterisk") +_S("plus") +_S("comma") +_S("hyphen") +_S("period") +_S("slash") +_S("zero") +_S("one") +_S("two") +_S("three") +_S("four") +_S("five") +_S("six") +_S("seven") +_S("eight") +_S("nine") +_S("colon") +_S("semicolon") +_S("less") +_S("equal") +_S("greater") +_S("question") +_S("at") +_S("A") +_S("B") +_S("C") +_S("D") +_S("E") +_S("F") +_S("G") +_S("H") +_S("I") +_S("J") +_S("K") +_S("L") +_S("M") +_S("N") +_S("O") +_S("P") +_S("Q") +_S("R") +_S("S") +_S("T") +_S("U") +_S("V") +_S("W") +_S("X") +_S("Y") +_S("Z") +_S("bracketleft") +_S("backslash") +_S("bracketright") +_S("asciicircum") +_S("underscore") +_S("quoteleft") +_S("a") +_S("b") +_S("c") +_S("d") +_S("e") +_S("f") +_S("g") +_S("h") +_S("i") +_S("j") +_S("k") +_S("l") +_S("m") +_S("n") +_S("o") +_S("p") +_S("q") +_S("r") +_S("s") +_S("t") +_S("u") +_S("v") +_S("w") +_S("x") +_S("y") +_S("z") +_S("braceleft") +_S("bar") +_S("braceright") +_S("asciitilde") +_S("exclamdown") +_S("cent") +_S("sterling") +_S("fraction") +_S("yen") +_S("florin") +_S("section") +_S("currency") +_S("quotesingle") +_S("quotedblleft") +_S("guillemotleft") +_S("guilsinglleft") +_S("guilsinglright") +_S("fi") +_S("fl") +_S("endash") +_S("dagger") +_S("daggerdbl") +_S("periodcentered") +_S("paragraph") +_S("bullet") +_S("quotesinglbase") +_S("quotedblbase") +_S("quotedblright") +_S("guillemotright") +_S("ellipsis") +_S("perthousand") +_S("questiondown") +_S("grave") +_S("acute") +_S("circumflex") +_S("tilde") +_S("macron") +_S("breve") +_S("dotaccent") +_S("dieresis") +_S("ring") +_S("cedilla") +_S("hungarumlaut") +_S("ogonek") +_S("caron") +_S("emdash") +_S("AE") +_S("ordfeminine") +_S("Lslash") +_S("Oslash") +_S("OE") +_S("ordmasculine") +_S("ae") +_S("dotlessi") +_S("lslash") +_S("oslash") +_S("oe") +_S("germandbls") +_S("onesuperior") +_S("logicalnot") +_S("mu") +_S("trademark") +_S("Eth") +_S("onehalf") +_S("plusminus") +_S("Thorn") +_S("onequarter") +_S("divide") +_S("brokenbar") +_S("degree") +_S("thorn") +_S("threequarters") +_S("twosuperior") +_S("registered") +_S("minus") +_S("eth") +_S("multiply") +_S("threesuperior") +_S("copyright") +_S("Aacute") +_S("Acircumflex") +_S("Adieresis") +_S("Agrave") +_S("Aring") +_S("Atilde") +_S("Ccedilla") +_S("Eacute") +_S("Ecircumflex") +_S("Edieresis") +_S("Egrave") +_S("Iacute") +_S("Icircumflex") +_S("Idieresis") +_S("Igrave") +_S("Ntilde") +_S("Oacute") +_S("Ocircumflex") +_S("Odieresis") +_S("Ograve") +_S("Otilde") +_S("Scaron") +_S("Uacute") +_S("Ucircumflex") +_S("Udieresis") +_S("Ugrave") +_S("Yacute") +_S("Ydieresis") +_S("Zcaron") +_S("aacute") +_S("acircumflex") +_S("adieresis") +_S("agrave") +_S("aring") +_S("atilde") +_S("ccedilla") +_S("eacute") +_S("ecircumflex") +_S("edieresis") +_S("egrave") +_S("iacute") +_S("icircumflex") +_S("idieresis") +_S("igrave") +_S("ntilde") +_S("oacute") +_S("ocircumflex") +_S("odieresis") +_S("ograve") +_S("otilde") +_S("scaron") +_S("uacute") +_S("ucircumflex") +_S("udieresis") +_S("ugrave") +_S("yacute") +_S("ydieresis") +_S("zcaron") +_S("exclamsmall") +_S("Hungarumlautsmall") +_S("dollaroldstyle") +_S("dollarsuperior") +_S("ampersandsmall") +_S("Acutesmall") +_S("parenleftsuperior") +_S("parenrightsuperior") +_S("twodotenleader") +_S("onedotenleader") +_S("zerooldstyle") +_S("oneoldstyle") +_S("twooldstyle") +_S("threeoldstyle") +_S("fouroldstyle") +_S("fiveoldstyle") +_S("sixoldstyle") +_S("sevenoldstyle") +_S("eightoldstyle") +_S("nineoldstyle") +_S("commasuperior") +_S("threequartersemdash") +_S("periodsuperior") +_S("questionsmall") +_S("asuperior") +_S("bsuperior") +_S("centsuperior") +_S("dsuperior") +_S("esuperior") +_S("isuperior") +_S("lsuperior") +_S("msuperior") +_S("nsuperior") +_S("osuperior") +_S("rsuperior") +_S("ssuperior") +_S("tsuperior") +_S("ff") +_S("ffi") +_S("ffl") +_S("parenleftinferior") +_S("parenrightinferior") +_S("Circumflexsmall") +_S("hyphensuperior") +_S("Gravesmall") +_S("Asmall") +_S("Bsmall") +_S("Csmall") +_S("Dsmall") +_S("Esmall") +_S("Fsmall") +_S("Gsmall") +_S("Hsmall") +_S("Ismall") +_S("Jsmall") +_S("Ksmall") +_S("Lsmall") +_S("Msmall") +_S("Nsmall") +_S("Osmall") +_S("Psmall") +_S("Qsmall") +_S("Rsmall") +_S("Ssmall") +_S("Tsmall") +_S("Usmall") +_S("Vsmall") +_S("Wsmall") +_S("Xsmall") +_S("Ysmall") +_S("Zsmall") +_S("colonmonetary") +_S("onefitted") +_S("rupiah") +_S("Tildesmall") +_S("exclamdownsmall") +_S("centoldstyle") +_S("Lslashsmall") +_S("Scaronsmall") +_S("Zcaronsmall") +_S("Dieresissmall") +_S("Brevesmall") +_S("Caronsmall") +_S("Dotaccentsmall") +_S("Macronsmall") +_S("figuredash") +_S("hypheninferior") +_S("Ogoneksmall") +_S("Ringsmall") +_S("Cedillasmall") +_S("questiondownsmall") +_S("oneeighth") +_S("threeeighths") +_S("fiveeighths") +_S("seveneighths") +_S("onethird") +_S("twothirds") +_S("zerosuperior") +_S("foursuperior") +_S("fivesuperior") +_S("sixsuperior") +_S("sevensuperior") +_S("eightsuperior") +_S("ninesuperior") +_S("zeroinferior") +_S("oneinferior") +_S("twoinferior") +_S("threeinferior") +_S("fourinferior") +_S("fiveinferior") +_S("sixinferior") +_S("seveninferior") +_S("eightinferior") +_S("nineinferior") +_S("centinferior") +_S("dollarinferior") +_S("periodinferior") +_S("commainferior") +_S("Agravesmall") +_S("Aacutesmall") +_S("Acircumflexsmall") +_S("Atildesmall") +_S("Adieresissmall") +_S("Aringsmall") +_S("AEsmall") +_S("Ccedillasmall") +_S("Egravesmall") +_S("Eacutesmall") +_S("Ecircumflexsmall") +_S("Edieresissmall") +_S("Igravesmall") +_S("Iacutesmall") +_S("Icircumflexsmall") +_S("Idieresissmall") +_S("Ethsmall") +_S("Ntildesmall") +_S("Ogravesmall") +_S("Oacutesmall") +_S("Ocircumflexsmall") +_S("Otildesmall") +_S("Odieresissmall") +_S("OEsmall") +_S("Oslashsmall") +_S("Ugravesmall") +_S("Uacutesmall") +_S("Ucircumflexsmall") +_S("Udieresissmall") +_S("Yacutesmall") +_S("Thornsmall") +_S("Ydieresissmall") +_S("001.000") +_S("001.001") +_S("001.002") +_S("001.003") +_S("Black") +_S("Bold") +_S("Book") +_S("Light") +_S("Medium") +_S("Regular") +_S("Roman") +_S("Semibold") + +#endif /* HB_OT_CFF1_STD_STR_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.cc b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.cc new file mode 100644 index 00000000000..5040c746234 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.cc @@ -0,0 +1,620 @@ +/* + * Copyright ยฉ 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb.hh" + +#ifndef HB_NO_CFF + +#include "hb-draw.hh" +#include "hb-algs.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-cff1-interp-cs.hh" + +using namespace CFF; + +struct sid_to_gid_t +{ + uint16_t sid; + uint8_t gid; + + int cmp (uint16_t a) const + { + if (a == sid) return 0; + return (a < sid) ? -1 : 1; + } +}; + +/* SID to code */ +static const uint8_t standard_encoding_to_code [] = +{ + 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 177, + 178, 179, 180, 182, 183, 184, 185, 186, 187, 188, 189, 191, 193, 194, 195, 196, + 197, 198, 199, 200, 202, 203, 205, 206, 207, 208, 225, 227, 232, 233, 234, 235, + 241, 245, 248, 249, 250, 251 +}; + +/* SID to code */ +static const uint8_t expert_encoding_to_code [] = +{ + 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 45, 46, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 189, 0, 0, 188, 0, + 0, 0, 0, 190, 202, 0, 0, 0, 0, 203, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 34, 36, 37, 38, 39, 40, 41, 42, 43, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 60, 61, 62, 63, 65, 66, 67, + 68, 69, 73, 76, 77, 78, 79, 82, 83, 84, 86, 89, 90, 91, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 161, 162, 163, 166, 167, 168, 169, 170, 172, 175, 178, 179, 182, 183, 184, 191, + 192, 193, 194, 195, 196, 197, 200, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +}; + +/* glyph ID to SID */ +static const uint16_t expert_charset_to_sid [] = +{ + 0, 1, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 13, 14, 15, 99, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 158, 155, 163, 319, 320, 321, 322, 323, 324, 325, 326, 150, + 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, + 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, + 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, + 373, 374, 375, 376, 377, 378 +}; + +/* glyph ID to SID */ +static const uint16_t expert_subset_charset_to_sid [] = +{ + 0, 1, 231, 232, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, 267, 268, 269, 270, 272, + 300, 301, 302, 305, 314, 315, 158, 155, 163, 320, 321, 322, 323, 324, 325, 326, + 150, 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346 +}; + +/* SID to glyph ID */ +static const sid_to_gid_t expert_charset_sid_to_gid [] = +{ + { 1, 1 }, { 13, 12 }, { 14, 13 }, { 15, 14 }, + { 27, 26 }, { 28, 27 }, { 99, 15 }, { 109, 46 }, + { 110, 47 }, { 150, 111 }, { 155, 101 }, { 158, 100 }, + { 163, 102 }, { 164, 112 }, { 169, 113 }, { 229, 2 }, + { 230, 3 }, { 231, 4 }, { 232, 5 }, { 233, 6 }, + { 234, 7 }, { 235, 8 }, { 236, 9 }, { 237, 10 }, + { 238, 11 }, { 239, 16 }, { 240, 17 }, { 241, 18 }, + { 242, 19 }, { 243, 20 }, { 244, 21 }, { 245, 22 }, + { 246, 23 }, { 247, 24 }, { 248, 25 }, { 249, 28 }, + { 250, 29 }, { 251, 30 }, { 252, 31 }, { 253, 32 }, + { 254, 33 }, { 255, 34 }, { 256, 35 }, { 257, 36 }, + { 258, 37 }, { 259, 38 }, { 260, 39 }, { 261, 40 }, + { 262, 41 }, { 263, 42 }, { 264, 43 }, { 265, 44 }, + { 266, 45 }, { 267, 48 }, { 268, 49 }, { 269, 50 }, + { 270, 51 }, { 271, 52 }, { 272, 53 }, { 273, 54 }, + { 274, 55 }, { 275, 56 }, { 276, 57 }, { 277, 58 }, + { 278, 59 }, { 279, 60 }, { 280, 61 }, { 281, 62 }, + { 282, 63 }, { 283, 64 }, { 284, 65 }, { 285, 66 }, + { 286, 67 }, { 287, 68 }, { 288, 69 }, { 289, 70 }, + { 290, 71 }, { 291, 72 }, { 292, 73 }, { 293, 74 }, + { 294, 75 }, { 295, 76 }, { 296, 77 }, { 297, 78 }, + { 298, 79 }, { 299, 80 }, { 300, 81 }, { 301, 82 }, + { 302, 83 }, { 303, 84 }, { 304, 85 }, { 305, 86 }, + { 306, 87 }, { 307, 88 }, { 308, 89 }, { 309, 90 }, + { 310, 91 }, { 311, 92 }, { 312, 93 }, { 313, 94 }, + { 314, 95 }, { 315, 96 }, { 316, 97 }, { 317, 98 }, + { 318, 99 }, { 319, 103 }, { 320, 104 }, { 321, 105 }, + { 322, 106 }, { 323, 107 }, { 324, 108 }, { 325, 109 }, + { 326, 110 }, { 327, 114 }, { 328, 115 }, { 329, 116 }, + { 330, 117 }, { 331, 118 }, { 332, 119 }, { 333, 120 }, + { 334, 121 }, { 335, 122 }, { 336, 123 }, { 337, 124 }, + { 338, 125 }, { 339, 126 }, { 340, 127 }, { 341, 128 }, + { 342, 129 }, { 343, 130 }, { 344, 131 }, { 345, 132 }, + { 346, 133 }, { 347, 134 }, { 348, 135 }, { 349, 136 }, + { 350, 137 }, { 351, 138 }, { 352, 139 }, { 353, 140 }, + { 354, 141 }, { 355, 142 }, { 356, 143 }, { 357, 144 }, + { 358, 145 }, { 359, 146 }, { 360, 147 }, { 361, 148 }, + { 362, 149 }, { 363, 150 }, { 364, 151 }, { 365, 152 }, + { 366, 153 }, { 367, 154 }, { 368, 155 }, { 369, 156 }, + { 370, 157 }, { 371, 158 }, { 372, 159 }, { 373, 160 }, + { 374, 161 }, { 375, 162 }, { 376, 163 }, { 377, 164 }, + { 378, 165 } +}; + +/* SID to glyph ID */ +static const sid_to_gid_t expert_subset_charset_sid_to_gid [] = +{ + { 1, 1 }, { 13, 8 }, { 14, 9 }, { 15, 10 }, + { 27, 22 }, { 28, 23 }, { 99, 11 }, { 109, 41 }, + { 110, 42 }, { 150, 64 }, { 155, 55 }, { 158, 54 }, + { 163, 56 }, { 164, 65 }, { 169, 66 }, { 231, 2 }, + { 232, 3 }, { 235, 4 }, { 236, 5 }, { 237, 6 }, + { 238, 7 }, { 239, 12 }, { 240, 13 }, { 241, 14 }, + { 242, 15 }, { 243, 16 }, { 244, 17 }, { 245, 18 }, + { 246, 19 }, { 247, 20 }, { 248, 21 }, { 249, 24 }, + { 250, 25 }, { 251, 26 }, { 253, 27 }, { 254, 28 }, + { 255, 29 }, { 256, 30 }, { 257, 31 }, { 258, 32 }, + { 259, 33 }, { 260, 34 }, { 261, 35 }, { 262, 36 }, + { 263, 37 }, { 264, 38 }, { 265, 39 }, { 266, 40 }, + { 267, 43 }, { 268, 44 }, { 269, 45 }, { 270, 46 }, + { 272, 47 }, { 300, 48 }, { 301, 49 }, { 302, 50 }, + { 305, 51 }, { 314, 52 }, { 315, 53 }, { 320, 57 }, + { 321, 58 }, { 322, 59 }, { 323, 60 }, { 324, 61 }, + { 325, 62 }, { 326, 63 }, { 327, 67 }, { 328, 68 }, + { 329, 69 }, { 330, 70 }, { 331, 71 }, { 332, 72 }, + { 333, 73 }, { 334, 74 }, { 335, 75 }, { 336, 76 }, + { 337, 77 }, { 338, 78 }, { 339, 79 }, { 340, 80 }, + { 341, 81 }, { 342, 82 }, { 343, 83 }, { 344, 84 }, + { 345, 85 }, { 346, 86 } +}; + +/* code to SID */ +static const uint8_t standard_encoding_to_sid [] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 0, 111, 112, 113, 114, 0, 115, 116, 117, 118, 119, 120, 121, 122, 0, 123, + 0, 124, 125, 126, 127, 128, 129, 130, 131, 0, 132, 133, 0, 134, 135, 136, + 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 138, 0, 139, 0, 0, 0, 0, 140, 141, 142, 143, 0, 0, 0, 0, + 0, 144, 0, 0, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 0, 0 +}; + +hb_codepoint_t OT::cff1::lookup_standard_encoding_for_code (hb_codepoint_t sid) +{ + if (sid < ARRAY_LENGTH (standard_encoding_to_code)) + return (hb_codepoint_t)standard_encoding_to_code[sid]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_encoding_for_code (hb_codepoint_t sid) +{ + if (sid < ARRAY_LENGTH (expert_encoding_to_code)) + return (hb_codepoint_t)expert_encoding_to_code[sid]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_charset_for_sid (hb_codepoint_t glyph) +{ + if (glyph < ARRAY_LENGTH (expert_charset_to_sid)) + return (hb_codepoint_t)expert_charset_to_sid[glyph]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph) +{ + if (glyph < ARRAY_LENGTH (expert_subset_charset_to_sid)) + return (hb_codepoint_t)expert_subset_charset_to_sid[glyph]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_charset_for_glyph (hb_codepoint_t sid) +{ + const auto *pair = hb_sorted_array (expert_charset_sid_to_gid).bsearch (sid); + return pair ? pair->gid : 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_glyph (hb_codepoint_t sid) +{ + const auto *pair = hb_sorted_array (expert_subset_charset_sid_to_gid).bsearch (sid); + return pair ? pair->gid : 0; +} + +hb_codepoint_t OT::cff1::lookup_standard_encoding_for_sid (hb_codepoint_t code) +{ + if (code < ARRAY_LENGTH (standard_encoding_to_sid)) + return (hb_codepoint_t)standard_encoding_to_sid[code]; + else + return CFF_UNDEF_SID; +} + +struct bounds_t +{ + void init () + { + min.set_int (INT_MAX, INT_MAX); + max.set_int (INT_MIN, INT_MIN); + } + + void update (const point_t &pt) + { + if (pt.x < min.x) min.x = pt.x; + if (pt.x > max.x) max.x = pt.x; + if (pt.y < min.y) min.y = pt.y; + if (pt.y > max.y) max.y = pt.y; + } + + void merge (const bounds_t &b) + { + if (empty ()) + *this = b; + else if (!b.empty ()) + { + if (b.min.x < min.x) min.x = b.min.x; + if (b.max.x > max.x) max.x = b.max.x; + if (b.min.y < min.y) min.y = b.min.y; + if (b.max.y > max.y) max.y = b.max.y; + } + } + + void offset (const point_t &delta) + { + if (!empty ()) + { + min.move (delta); + max.move (delta); + } + } + + bool empty () const { return (min.x >= max.x) || (min.y >= max.y); } + + point_t min; + point_t max; +}; + +struct cff1_extents_param_t +{ + cff1_extents_param_t (const OT::cff1::accelerator_t *_cff) : cff (_cff) + { + bounds.init (); + } + + void start_path () { path_open = true; } + void end_path () { path_open = false; } + bool is_path_open () const { return path_open; } + + bool path_open = false; + bounds_t bounds; + + const OT::cff1::accelerator_t *cff; +}; + +struct cff1_path_procs_extents_t : path_procs_t +{ + static void moveto (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt) + { + param.end_path (); + env.moveto (pt); + } + + static void line (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.bounds.update (env.get_pt ()); + } + env.moveto (pt1); + param.bounds.update (env.get_pt ()); + } + + static void curve (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.bounds.update (env.get_pt ()); + } + /* include control points */ + param.bounds.update (pt1); + param.bounds.update (pt2); + env.moveto (pt3); + param.bounds.update (env.get_pt ()); + } +}; + +static bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, bounds_t &bounds, bool in_seac=false); + +struct cff1_cs_opset_extents_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, cff1_extents_param_t& param) + { + unsigned int n = env.argStack.get_count (); + point_t delta; + delta.x = env.argStack[n-4]; + delta.y = env.argStack[n-3]; + hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ()); + hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ()); + + bounds_t base_bounds, accent_bounds; + if (likely (!env.in_seac && base && accent + && _get_bounds (param.cff, base, base_bounds, true) + && _get_bounds (param.cff, accent, accent_bounds, true))) + { + param.bounds.merge (base_bounds); + accent_bounds.offset (delta); + param.bounds.merge (accent_bounds); + } + else + env.set_error (); + } +}; + +bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, bounds_t &bounds, bool in_seac) +{ + bounds.init (); + if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false; + + unsigned int fd = cff->fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*cff->charStrings)[glyph]; + cff1_cs_interp_env_t env (str, *cff, fd); + env.set_in_seac (in_seac); + cff1_cs_interpreter_t interp (env); + cff1_extents_param_t param (cff); + if (unlikely (!interp.interpret (param))) return false; + bounds = param.bounds; + return true; +} + +bool OT::cff1::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + bounds_t bounds; + + if (!_get_bounds (this, glyph, bounds)) + return false; + + if (bounds.min.x >= bounds.max.x) + { + extents->width = 0; + extents->x_bearing = 0; + } + else + { + extents->x_bearing = roundf (bounds.min.x.to_real ()); + extents->width = roundf (bounds.max.x.to_real () - extents->x_bearing); + } + if (bounds.min.y >= bounds.max.y) + { + extents->height = 0; + extents->y_bearing = 0; + } + else + { + extents->y_bearing = roundf (bounds.max.y.to_real ()); + extents->height = roundf (bounds.min.y.to_real () - extents->y_bearing); + } + + font->scale_glyph_extents (extents); + + return true; +} + +struct cff1_path_param_t +{ + cff1_path_param_t (const OT::cff1::accelerator_t *cff_, hb_font_t *font_, + hb_draw_session_t &draw_session_, point_t *delta_) + { + draw_session = &draw_session_; + cff = cff_; + font = font_; + delta = delta_; + } + + void move_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + draw_session->move_to (font->em_fscalef_x (point.x.to_real ()), font->em_fscalef_y (point.y.to_real ())); + } + + void line_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + draw_session->line_to (font->em_fscalef_x (point.x.to_real ()), font->em_fscalef_y (point.y.to_real ())); + } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + point_t point1 = p1, point2 = p2, point3 = p3; + if (delta) + { + point1.move (*delta); + point2.move (*delta); + point3.move (*delta); + } + draw_session->cubic_to (font->em_fscalef_x (point1.x.to_real ()), font->em_fscalef_y (point1.y.to_real ()), + font->em_fscalef_x (point2.x.to_real ()), font->em_fscalef_y (point2.y.to_real ()), + font->em_fscalef_x (point3.x.to_real ()), font->em_fscalef_y (point3.y.to_real ())); + } + + void end_path () { draw_session->close_path (); } + + hb_font_t *font; + hb_draw_session_t *draw_session; + point_t *delta; + + const OT::cff1::accelerator_t *cff; +}; + +struct cff1_path_procs_path_t : path_procs_t +{ + static void moveto (cff1_cs_interp_env_t &env, cff1_path_param_t& param, const point_t &pt) + { + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff1_cs_interp_env_t &env, cff1_path_param_t ¶m, const point_t &pt1) + { + param.line_to (pt1); + env.moveto (pt1); + } + + static void curve (cff1_cs_interp_env_t &env, cff1_path_param_t ¶m, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +static bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + hb_draw_session_t &draw_session, bool in_seac = false, point_t *delta = nullptr); + +struct cff1_cs_opset_path_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, cff1_path_param_t& param) + { + /* End previous path */ + param.end_path (); + + unsigned int n = env.argStack.get_count (); + point_t delta; + delta.x = env.argStack[n-4]; + delta.y = env.argStack[n-3]; + hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ()); + hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ()); + + if (unlikely (!(!env.in_seac && base && accent + && _get_path (param.cff, param.font, base, *param.draw_session, true) + && _get_path (param.cff, param.font, accent, *param.draw_session, true, &delta)))) + env.set_error (); + } +}; + +bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + hb_draw_session_t &draw_session, bool in_seac, point_t *delta) +{ + if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false; + + unsigned int fd = cff->fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*cff->charStrings)[glyph]; + cff1_cs_interp_env_t env (str, *cff, fd); + env.set_in_seac (in_seac); + cff1_cs_interpreter_t interp (env); + cff1_path_param_t param (cff, font, draw_session, delta); + if (unlikely (!interp.interpret (param))) return false; + + /* Let's end the path specially since it is called inside seac also */ + param.end_path (); + + return true; +} + +bool OT::cff1::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const +{ + funcs->push_clip_glyph (data, glyph, font); + funcs->color (data, true, foreground); + funcs->pop_clip (data); + + return true; +} + +bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + return _get_path (this, font, glyph, draw_session); +} + +struct get_seac_param_t +{ + get_seac_param_t (const OT::cff1::accelerator_t *_cff) : cff (_cff) {} + + bool has_seac () const { return base && accent; } + + const OT::cff1::accelerator_t *cff; + hb_codepoint_t base = 0; + hb_codepoint_t accent = 0; +}; + +struct cff1_cs_opset_seac_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, get_seac_param_t& param) + { + unsigned int n = env.argStack.get_count (); + hb_codepoint_t base_char = (hb_codepoint_t)env.argStack[n-2].to_int (); + hb_codepoint_t accent_char = (hb_codepoint_t)env.argStack[n-1].to_int (); + + param.base = param.cff->std_code_to_glyph (base_char); + param.accent = param.cff->std_code_to_glyph (accent_char); + } +}; + +bool OT::cff1::accelerator_t::get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const +{ + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*charStrings)[glyph]; + cff1_cs_interp_env_t env (str, *this, fd); + cff1_cs_interpreter_t interp (env); + get_seac_param_t param (this); + if (unlikely (!interp.interpret (param))) return false; + + if (param.has_seac ()) + { + *base = param.base; + *accent = param.accent; + return true; + } + return false; +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.hh new file mode 100644 index 00000000000..f461a230449 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.hh @@ -0,0 +1,1484 @@ +/* + * Copyright ยฉ 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF1_TABLE_HH +#define HB_OT_CFF1_TABLE_HH + +#include "hb-ot-cff-common.hh" +#include "hb-subset-cff1.hh" +#include "hb-draw.hh" +#include "hb-paint.hh" + +#define HB_STRING_ARRAY_NAME cff1_std_strings +#define HB_STRING_ARRAY_LIST "hb-ot-cff1-std-str.hh" +#include "hb-string-array.hh" +#undef HB_STRING_ARRAY_LIST +#undef HB_STRING_ARRAY_NAME + +namespace CFF { + +/* + * CFF -- Compact Font Format (CFF) + * https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf + */ +#define HB_OT_TAG_cff1 HB_TAG('C','F','F',' ') + +#define CFF_UNDEF_SID CFF_UNDEF_CODE + +enum EncodingID { StandardEncoding = 0, ExpertEncoding = 1 }; +enum CharsetID { ISOAdobeCharset = 0, ExpertCharset = 1, ExpertSubsetCharset = 2 }; + +typedef CFFIndex CFF1Index; +template struct CFF1IndexOf : CFFIndexOf {}; + +typedef CFFIndex CFF1Index; +typedef CFF1Index CFF1CharStrings; +typedef Subrs CFF1Subrs; + +struct CFF1FDSelect : FDSelect {}; + +/* Encoding */ +struct Encoding0 { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (codes.sanitize (c)); + } + + hb_codepoint_t get_code (hb_codepoint_t glyph) const + { + assert (glyph > 0); + glyph--; + if (glyph < nCodes ()) + { + return (hb_codepoint_t)codes[glyph]; + } + else + return CFF_UNDEF_CODE; + } + + HBUINT8 &nCodes () { return codes.len; } + HBUINT8 nCodes () const { return codes.len; } + + ArrayOf codes; + + DEFINE_SIZE_ARRAY_SIZED (1, codes); +}; + +struct Encoding1_Range { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT8 first; + HBUINT8 nLeft; + + DEFINE_SIZE_STATIC (2); +}; + +struct Encoding1 { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (ranges.sanitize (c)); + } + + hb_codepoint_t get_code (hb_codepoint_t glyph) const + { + assert (glyph > 0); + glyph--; + for (unsigned int i = 0; i < nRanges (); i++) + { + if (glyph <= ranges[i].nLeft) + { + hb_codepoint_t code = (hb_codepoint_t) ranges[i].first + glyph; + return (likely (code < 0x100) ? code: CFF_UNDEF_CODE); + } + glyph -= (ranges[i].nLeft + 1); + } + return CFF_UNDEF_CODE; + } + + HBUINT8 &nRanges () { return ranges.len; } + HBUINT8 nRanges () const { return ranges.len; } + + ArrayOf ranges; + + DEFINE_SIZE_ARRAY_SIZED (1, ranges); +}; + +struct SuppEncoding { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT8 code; + HBUINT16 glyph; + + DEFINE_SIZE_STATIC (3); +}; + +struct CFF1SuppEncData { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (supps.sanitize (c)); + } + + void get_codes (hb_codepoint_t sid, hb_vector_t &codes) const + { + for (unsigned int i = 0; i < nSups (); i++) + if (sid == supps[i].glyph) + codes.push (supps[i].code); + } + + HBUINT8 &nSups () { return supps.len; } + HBUINT8 nSups () const { return supps.len; } + + ArrayOf supps; + + DEFINE_SIZE_ARRAY_SIZED (1, supps); +}; + +struct Encoding +{ + /* serialize a fullset Encoding */ + bool serialize (hb_serialize_context_t *c, const Encoding &src) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (); + Encoding *dest = c->allocate_size (size); + if (unlikely (!dest)) return_trace (false); + hb_memcpy (dest, &src, size); + return_trace (true); + } + + /* serialize a subset Encoding */ + bool serialize (hb_serialize_context_t *c, + uint8_t format, + unsigned int enc_count, + const hb_vector_t& code_ranges, + const hb_vector_t& supp_codes) + { + TRACE_SERIALIZE (this); + Encoding *dest = c->extend_min (this); + if (unlikely (!dest)) return_trace (false); + dest->format = format | ((supp_codes.length > 0) ? 0x80 : 0); + switch (format) { + case 0: + { + Encoding0 *fmt0 = c->allocate_size (Encoding0::min_size + HBUINT8::static_size * enc_count); + if (unlikely (!fmt0)) return_trace (false); + fmt0->nCodes () = enc_count; + unsigned int glyph = 0; + for (unsigned int i = 0; i < code_ranges.length; i++) + { + hb_codepoint_t code = code_ranges[i].code; + for (int left = (int)code_ranges[i].glyph; left >= 0; left--) + fmt0->codes[glyph++] = code++; + if (unlikely (!((glyph <= 0x100) && (code <= 0x100)))) + return_trace (false); + } + } + break; + + case 1: + { + Encoding1 *fmt1 = c->allocate_size (Encoding1::min_size + Encoding1_Range::static_size * code_ranges.length); + if (unlikely (!fmt1)) return_trace (false); + fmt1->nRanges () = code_ranges.length; + for (unsigned int i = 0; i < code_ranges.length; i++) + { + if (unlikely (!((code_ranges[i].code <= 0xFF) && (code_ranges[i].glyph <= 0xFF)))) + return_trace (false); + fmt1->ranges[i].first = code_ranges[i].code; + fmt1->ranges[i].nLeft = code_ranges[i].glyph; + } + } + break; + + } + + if (supp_codes.length) + { + CFF1SuppEncData *suppData = c->allocate_size (CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_codes.length); + if (unlikely (!suppData)) return_trace (false); + suppData->nSups () = supp_codes.length; + for (unsigned int i = 0; i < supp_codes.length; i++) + { + suppData->supps[i].code = supp_codes[i].code; + suppData->supps[i].glyph = supp_codes[i].glyph; /* actually SID */ + } + } + + return_trace (true); + } + + unsigned int get_size () const + { + unsigned int size = min_size; + switch (table_format ()) + { + case 0: size += u.format0.get_size (); break; + case 1: size += u.format1.get_size (); break; + } + if (has_supplement ()) + size += suppEncData ().get_size (); + return size; + } + + hb_codepoint_t get_code (hb_codepoint_t glyph) const + { + switch (table_format ()) + { + case 0: return u.format0.get_code (glyph); + case 1: return u.format1.get_code (glyph); + default:return 0; + } + } + + uint8_t table_format () const { return format & 0x7F; } + bool has_supplement () const { return format & 0x80; } + + void get_supplement_codes (hb_codepoint_t sid, hb_vector_t &codes) const + { + codes.resize (0); + if (has_supplement ()) + suppEncData().get_codes (sid, codes); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (table_format ()) + { + case 0: if (unlikely (!u.format0.sanitize (c))) { return_trace (false); } break; + case 1: if (unlikely (!u.format1.sanitize (c))) { return_trace (false); } break; + default:return_trace (false); + } + return_trace (likely (!has_supplement () || suppEncData ().sanitize (c))); + } + + protected: + const CFF1SuppEncData &suppEncData () const + { + switch (table_format ()) + { + case 0: return StructAfter (u.format0.codes[u.format0.nCodes ()-1]); + case 1: return StructAfter (u.format1.ranges[u.format1.nRanges ()-1]); + default:return Null (CFF1SuppEncData); + } + } + + public: + HBUINT8 format; + union { + Encoding0 format0; + Encoding1 format1; + } u; + /* CFF1SuppEncData suppEncData; */ + + DEFINE_SIZE_MIN (1); +}; + +/* Charset */ +struct Charset0 { + bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && sids[num_glyphs - 1].sanitize (c)); + } + + hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs) const + { + if (unlikely (glyph >= num_glyphs)) return 0; + if (glyph == 0) + return 0; + else + return sids[glyph - 1]; + } + + void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const + { + for (hb_codepoint_t gid = 1; gid < num_glyphs; gid++) + mapping->set (gid, sids[gid - 1]); + } + + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + { + if (sid == 0) + return 0; + + for (unsigned int glyph = 1; glyph < num_glyphs; glyph++) + { + if (sids[glyph-1] == sid) + return glyph; + } + return 0; + } + + unsigned int get_size (unsigned int num_glyphs) const + { + assert (num_glyphs > 0); + return HBUINT16::static_size * (num_glyphs - 1); + } + + HBUINT16 sids[HB_VAR_ARRAY]; + + DEFINE_SIZE_ARRAY(0, sids); +}; + +template +struct Charset_Range { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 first; + TYPE nLeft; + + DEFINE_SIZE_STATIC (HBUINT16::static_size + TYPE::static_size); +}; + +template +struct Charset1_2 { + bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + num_glyphs--; + for (unsigned int i = 0; num_glyphs > 0; i++) + { + if (unlikely (!ranges[i].sanitize (c) || (num_glyphs < ranges[i].nLeft + 1))) + return_trace (false); + num_glyphs -= (ranges[i].nLeft + 1); + } + return_trace (true); + } + + hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs) const + { + if (unlikely (glyph >= num_glyphs)) return 0; + if (glyph == 0) return 0; + glyph--; + for (unsigned int i = 0;; i++) + { + if (glyph <= ranges[i].nLeft) + return (hb_codepoint_t) ranges[i].first + glyph; + glyph -= (ranges[i].nLeft + 1); + } + + return 0; + } + + void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const + { + hb_codepoint_t gid = 1; + if (gid >= num_glyphs) + return; + for (unsigned i = 0;; i++) + { + hb_codepoint_t sid = ranges[i].first; + unsigned count = ranges[i].nLeft + 1; + for (unsigned j = 0; j < count; j++) + mapping->set (gid++, sid++); + + if (gid >= num_glyphs) + break; + } + } + + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + { + if (sid == 0) return 0; + hb_codepoint_t glyph = 1; + for (unsigned int i = 0;; i++) + { + if (glyph >= num_glyphs) + return 0; + if ((ranges[i].first <= sid) && (sid <= ranges[i].first + ranges[i].nLeft)) + return glyph + (sid - ranges[i].first); + glyph += (ranges[i].nLeft + 1); + } + + return 0; + } + + unsigned int get_size (unsigned int num_glyphs) const + { + unsigned int size = HBUINT8::static_size; + int glyph = (int)num_glyphs; + + assert (glyph > 0); + glyph--; + for (unsigned int i = 0; glyph > 0; i++) + { + glyph -= (ranges[i].nLeft + 1); + size += Charset_Range::static_size; + } + + return size; + } + + Charset_Range ranges[HB_VAR_ARRAY]; + + DEFINE_SIZE_ARRAY (0, ranges); +}; + +typedef Charset1_2 Charset1; +typedef Charset1_2 Charset2; +typedef Charset_Range Charset1_Range; +typedef Charset_Range Charset2_Range; + +struct Charset +{ + /* serialize a fullset Charset */ + bool serialize (hb_serialize_context_t *c, const Charset &src, unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (num_glyphs); + Charset *dest = c->allocate_size (size); + if (unlikely (!dest)) return_trace (false); + hb_memcpy (dest, &src, size); + return_trace (true); + } + + /* serialize a subset Charset */ + bool serialize (hb_serialize_context_t *c, + uint8_t format, + unsigned int num_glyphs, + const hb_vector_t& sid_ranges) + { + TRACE_SERIALIZE (this); + Charset *dest = c->extend_min (this); + if (unlikely (!dest)) return_trace (false); + dest->format = format; + switch (format) + { + case 0: + { + Charset0 *fmt0 = c->allocate_size (Charset0::min_size + HBUINT16::static_size * (num_glyphs - 1)); + if (unlikely (!fmt0)) return_trace (false); + unsigned int glyph = 0; + for (unsigned int i = 0; i < sid_ranges.length; i++) + { + hb_codepoint_t sid = sid_ranges[i].code; + for (int left = (int)sid_ranges[i].glyph; left >= 0; left--) + fmt0->sids[glyph++] = sid++; + } + } + break; + + case 1: + { + Charset1 *fmt1 = c->allocate_size (Charset1::min_size + Charset1_Range::static_size * sid_ranges.length); + if (unlikely (!fmt1)) return_trace (false); + for (unsigned int i = 0; i < sid_ranges.length; i++) + { + if (unlikely (!(sid_ranges[i].glyph <= 0xFF))) + return_trace (false); + fmt1->ranges[i].first = sid_ranges[i].code; + fmt1->ranges[i].nLeft = sid_ranges[i].glyph; + } + } + break; + + case 2: + { + Charset2 *fmt2 = c->allocate_size (Charset2::min_size + Charset2_Range::static_size * sid_ranges.length); + if (unlikely (!fmt2)) return_trace (false); + for (unsigned int i = 0; i < sid_ranges.length; i++) + { + if (unlikely (!(sid_ranges[i].glyph <= 0xFFFF))) + return_trace (false); + fmt2->ranges[i].first = sid_ranges[i].code; + fmt2->ranges[i].nLeft = sid_ranges[i].glyph; + } + } + break; + + } + return_trace (true); + } + + unsigned int get_size (unsigned int num_glyphs) const + { + switch (format) + { + case 0: return min_size + u.format0.get_size (num_glyphs); + case 1: return min_size + u.format1.get_size (num_glyphs); + case 2: return min_size + u.format2.get_size (num_glyphs); + default:return 0; + } + } + + hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned int num_glyphs) const + { + switch (format) + { + case 0: return u.format0.get_sid (glyph, num_glyphs); + case 1: return u.format1.get_sid (glyph, num_glyphs); + case 2: return u.format2.get_sid (glyph, num_glyphs); + default:return 0; + } + } + + void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const + { + switch (format) + { + case 0: u.format0.collect_glyph_to_sid_map (mapping, num_glyphs); return; + case 1: u.format1.collect_glyph_to_sid_map (mapping, num_glyphs); return; + case 2: u.format2.collect_glyph_to_sid_map (mapping, num_glyphs); return; + default:return; + } + } + + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + { + switch (format) + { + case 0: return u.format0.get_glyph (sid, num_glyphs); + case 1: return u.format1.get_glyph (sid, num_glyphs); + case 2: return u.format2.get_glyph (sid, num_glyphs); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, c->get_num_glyphs ())); + case 1: return_trace (u.format1.sanitize (c, c->get_num_glyphs ())); + case 2: return_trace (u.format2.sanitize (c, c->get_num_glyphs ())); + default:return_trace (false); + } + } + + HBUINT8 format; + union { + Charset0 format0; + Charset1 format1; + Charset2 format2; + } u; + + DEFINE_SIZE_MIN (1); +}; + +struct CFF1StringIndex : CFF1Index +{ + bool serialize (hb_serialize_context_t *c, const CFF1StringIndex &strings, + const hb_inc_bimap_t &sidmap) + { + TRACE_SERIALIZE (this); + if (unlikely ((strings.count == 0) || (sidmap.get_population () == 0))) + { + if (unlikely (!c->extend_min (this->count))) + return_trace (false); + count = 0; + return_trace (true); + } + + byte_str_array_t bytesArray; + if (!bytesArray.resize (sidmap.get_population ())) + return_trace (false); + for (unsigned int i = 0; i < strings.count; i++) + { + hb_codepoint_t j = sidmap[i]; + if (j != HB_MAP_VALUE_INVALID) + bytesArray[j] = strings[i]; + } + + bool result = CFF1Index::serialize (c, bytesArray); + return_trace (result); + } +}; + +struct cff1_top_dict_interp_env_t : num_interp_env_t +{ + cff1_top_dict_interp_env_t () + : num_interp_env_t(), prev_offset(0), last_offset(0) {} + cff1_top_dict_interp_env_t (const hb_ubytes_t &bytes) + : num_interp_env_t(bytes), prev_offset(0), last_offset(0) {} + + unsigned int prev_offset; + unsigned int last_offset; +}; + +struct name_dict_values_t +{ + enum name_dict_val_index_t + { + version, + notice, + copyright, + fullName, + familyName, + weight, + postscript, + fontName, + baseFontName, + registry, + ordering, + + ValCount + }; + + void init () + { + for (unsigned int i = 0; i < ValCount; i++) + values[i] = CFF_UNDEF_SID; + } + + unsigned int& operator[] (unsigned int i) + { assert (i < ValCount); return values[i]; } + + unsigned int operator[] (unsigned int i) const + { assert (i < ValCount); return values[i]; } + + static enum name_dict_val_index_t name_op_to_index (op_code_t op) + { + switch (op) { + default: // can't happen - just make some compiler happy + case OpCode_version: + return version; + case OpCode_Notice: + return notice; + case OpCode_Copyright: + return copyright; + case OpCode_FullName: + return fullName; + case OpCode_FamilyName: + return familyName; + case OpCode_Weight: + return weight; + case OpCode_PostScript: + return postscript; + case OpCode_FontName: + return fontName; + case OpCode_BaseFontName: + return baseFontName; + } + } + + unsigned int values[ValCount]; +}; + +struct cff1_top_dict_val_t : op_str_t +{ + unsigned int last_arg_offset; +}; + +struct cff1_top_dict_values_t : top_dict_values_t +{ + void init () + { + top_dict_values_t::init (); + + nameSIDs.init (); + ros_supplement = 0; + cidCount = 8720; + EncodingOffset = 0; + CharsetOffset = 0; + FDSelectOffset = 0; + privateDictInfo.init (); + } + void fini () { top_dict_values_t::fini (); } + + bool is_CID () const + { return nameSIDs[name_dict_values_t::registry] != CFF_UNDEF_SID; } + + name_dict_values_t nameSIDs; + unsigned int ros_supplement_offset; + unsigned int ros_supplement; + unsigned int cidCount; + + unsigned int EncodingOffset; + unsigned int CharsetOffset; + unsigned int FDSelectOffset; + table_info_t privateDictInfo; +}; + +struct cff1_top_dict_opset_t : top_dict_opset_t +{ + static void process_op (op_code_t op, cff1_top_dict_interp_env_t& env, cff1_top_dict_values_t& dictval) + { + cff1_top_dict_val_t val; + val.last_arg_offset = (env.last_offset-1) - dictval.opStart; /* offset to the last argument */ + + switch (op) { + case OpCode_version: + case OpCode_Notice: + case OpCode_Copyright: + case OpCode_FullName: + case OpCode_FontName: + case OpCode_FamilyName: + case OpCode_Weight: + case OpCode_PostScript: + case OpCode_BaseFontName: + dictval.nameSIDs[name_dict_values_t::name_op_to_index (op)] = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_isFixedPitch: + case OpCode_ItalicAngle: + case OpCode_UnderlinePosition: + case OpCode_UnderlineThickness: + case OpCode_PaintType: + case OpCode_CharstringType: + case OpCode_UniqueID: + case OpCode_StrokeWidth: + case OpCode_SyntheticBase: + case OpCode_CIDFontVersion: + case OpCode_CIDFontRevision: + case OpCode_CIDFontType: + case OpCode_UIDBase: + case OpCode_FontBBox: + case OpCode_XUID: + case OpCode_BaseFontBlend: + env.clear_args (); + break; + + case OpCode_CIDCount: + dictval.cidCount = env.argStack.pop_uint (); + env.clear_args (); + break; + + case OpCode_ROS: + dictval.ros_supplement = env.argStack.pop_uint (); + dictval.nameSIDs[name_dict_values_t::ordering] = env.argStack.pop_uint (); + dictval.nameSIDs[name_dict_values_t::registry] = env.argStack.pop_uint (); + env.clear_args (); + break; + + case OpCode_Encoding: + dictval.EncodingOffset = env.argStack.pop_uint (); + env.clear_args (); + if (unlikely (dictval.EncodingOffset == 0)) return; + break; + + case OpCode_charset: + dictval.CharsetOffset = env.argStack.pop_uint (); + env.clear_args (); + if (unlikely (dictval.CharsetOffset == 0)) return; + break; + + case OpCode_FDSelect: + dictval.FDSelectOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + case OpCode_Private: + dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.size = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + env.last_offset = env.str_ref.get_offset (); + top_dict_opset_t::process_op (op, env, dictval); + /* Record this operand below if stack is empty, otherwise done */ + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref, val); + } +}; + +struct cff1_font_dict_values_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + privateDictInfo.init (); + fontName = CFF_UNDEF_SID; + } + void fini () { dict_values_t::fini (); } + + table_info_t privateDictInfo; + unsigned int fontName; +}; + +struct cff1_font_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff1_font_dict_values_t& dictval) + { + switch (op) { + case OpCode_FontName: + dictval.fontName = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FontMatrix: + case OpCode_PaintType: + env.clear_args (); + break; + case OpCode_Private: + dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.size = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } +}; + +template +struct cff1_private_dict_values_base_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + subrsOffset = 0; + localSubrs = &Null (CFF1Subrs); + } + void fini () { dict_values_t::fini (); } + + unsigned int subrsOffset; + const CFF1Subrs *localSubrs; +}; + +typedef cff1_private_dict_values_base_t cff1_private_dict_values_subset_t; +typedef cff1_private_dict_values_base_t cff1_private_dict_values_t; + +struct cff1_private_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff1_private_dict_values_t& dictval) + { + num_dict_val_t val; + val.init (); + + switch (op) { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ForceBold: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + case OpCode_initialRandomSeed: + case OpCode_defaultWidthX: + case OpCode_nominalWidthX: + env.clear_args (); + break; + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref, val); + } +}; + +struct cff1_private_dict_opset_subset : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff1_private_dict_values_subset_t& dictval) + { + switch (op) { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ForceBold: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + case OpCode_initialRandomSeed: + case OpCode_defaultWidthX: + case OpCode_nominalWidthX: + env.clear_args (); + break; + + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } +}; + +typedef dict_interpreter_t cff1_top_dict_interpreter_t; +typedef dict_interpreter_t cff1_font_dict_interpreter_t; + +typedef CFF1Index CFF1NameIndex; +typedef CFF1IndexOf CFF1TopDictIndex; + +struct cff1_font_dict_values_mod_t +{ + cff1_font_dict_values_mod_t() { init (); } + + void init () { init ( &Null (cff1_font_dict_values_t), CFF_UNDEF_SID ); } + + void init (const cff1_font_dict_values_t *base_, + unsigned int fontName_) + { + base = base_; + fontName = fontName_; + privateDictInfo.init (); + } + + unsigned get_count () const { return base->get_count (); } + + const op_str_t &operator [] (unsigned int i) const { return (*base)[i]; } + + const cff1_font_dict_values_t *base; + table_info_t privateDictInfo; + unsigned int fontName; +}; + +struct CFF1FDArray : FDArray +{ + /* FDArray::serialize() requires this partial specialization to compile */ + template + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray::serialize (c, it, opszr); } +}; + +} /* namespace CFF */ + +namespace OT { + +using namespace CFF; + +struct cff1 +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_cff1; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 1)); + } + + template + struct accelerator_templ_t + { + void init (hb_face_t *face) + { + topDict.init (); + fontDicts.init (); + privateDicts.init (); + + this->blob = sc.reference_table (face); + + /* setup for run-time santization */ + sc.init (this->blob); + sc.start_processing (); + + const OT::cff1 *cff = this->blob->template as (); + + if (cff == &Null (OT::cff1)) + { fini (); return; } + + nameIndex = &cff->nameIndex (cff); + if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc)) + { fini (); return; } + + topDictIndex = &StructAtOffset (nameIndex, nameIndex->get_size ()); + if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0)) + { fini (); return; } + + { /* parse top dict */ + const hb_ubytes_t topDictStr = (*topDictIndex)[0]; + if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; } + cff1_top_dict_interp_env_t env (topDictStr); + cff1_top_dict_interpreter_t top_interp (env); + if (unlikely (!top_interp.interpret (topDict))) { fini (); return; } + } + + if (is_predef_charset ()) + charset = &Null (Charset); + else + { + charset = &StructAtOffsetOrNull (cff, topDict.CharsetOffset); + if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc))) { fini (); return; } + } + + fdCount = 1; + if (is_CID ()) + { + fdArray = &StructAtOffsetOrNull (cff, topDict.FDArrayOffset); + fdSelect = &StructAtOffsetOrNull (cff, topDict.FDSelectOffset); + if (unlikely ((fdArray == &Null (CFF1FDArray)) || !fdArray->sanitize (&sc) || + (fdSelect == &Null (CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) + { fini (); return; } + + fdCount = fdArray->count; + } + else + { + fdArray = &Null (CFF1FDArray); + fdSelect = &Null (CFF1FDSelect); + } + + encoding = &Null (Encoding); + if (is_CID ()) + { + if (unlikely (charset == &Null (Charset))) { fini (); return; } + } + else + { + if (!is_predef_encoding ()) + { + encoding = &StructAtOffsetOrNull (cff, topDict.EncodingOffset); + if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) { fini (); return; } + } + } + + stringIndex = &StructAtOffset (topDictIndex, topDictIndex->get_size ()); + if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc)) + { fini (); return; } + + globalSubrs = &StructAtOffset (stringIndex, stringIndex->get_size ()); + if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc)) + { fini (); return; } + + charStrings = &StructAtOffsetOrNull (cff, topDict.charStringsOffset); + + if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc))) + { fini (); return; } + + num_glyphs = charStrings->count; + if (num_glyphs != sc.get_num_glyphs ()) + { fini (); return; } + + if (unlikely (!privateDicts.resize (fdCount))) + { fini (); return; } + for (unsigned int i = 0; i < fdCount; i++) + privateDicts[i].init (); + + // parse CID font dicts and gather private dicts + if (is_CID ()) + { + for (unsigned int i = 0; i < fdCount; i++) + { + hb_ubytes_t fontDictStr = (*fdArray)[i]; + if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; } + cff1_font_dict_values_t *font; + cff1_top_dict_interp_env_t env (fontDictStr); + cff1_font_dict_interpreter_t font_interp (env); + font = fontDicts.push (); + if (unlikely (fontDicts.in_error ())) { fini (); return; } + + font->init (); + if (unlikely (!font_interp.interpret (*font))) { fini (); return; } + PRIVDICTVAL *priv = &privateDicts[i]; + const hb_ubytes_t privDictStr = StructAtOffset (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } + num_interp_env_t env2 (privDictStr); + dict_interpreter_t priv_interp (env2); + priv->init (); + if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } + + priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); + if (priv->localSubrs != &Null (CFF1Subrs) && + unlikely (!priv->localSubrs->sanitize (&sc))) + { fini (); return; } + } + } + else /* non-CID */ + { + cff1_top_dict_values_t *font = &topDict; + PRIVDICTVAL *priv = &privateDicts[0]; + + const hb_ubytes_t privDictStr = StructAtOffset (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } + num_interp_env_t env (privDictStr); + dict_interpreter_t priv_interp (env); + priv->init (); + if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } + + priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); + if (priv->localSubrs != &Null (CFF1Subrs) && + unlikely (!priv->localSubrs->sanitize (&sc))) + { fini (); return; } + } + } + + void fini () + { + sc.end_processing (); + topDict.fini (); + fontDicts.fini (); + privateDicts.fini (); + hb_blob_destroy (blob); + blob = nullptr; + } + + bool is_valid () const { return blob; } + bool is_CID () const { return topDict.is_CID (); } + + bool is_predef_charset () const { return topDict.CharsetOffset <= ExpertSubsetCharset; } + + unsigned int std_code_to_glyph (hb_codepoint_t code) const + { + hb_codepoint_t sid = lookup_standard_encoding_for_sid (code); + if (unlikely (sid == CFF_UNDEF_SID)) + return 0; + + if (charset != &Null (Charset)) + return charset->get_glyph (sid, num_glyphs); + else if ((topDict.CharsetOffset == ISOAdobeCharset) + && (code <= 228 /*zcaron*/)) return sid; + return 0; + } + + bool is_predef_encoding () const { return topDict.EncodingOffset <= ExpertEncoding; } + + hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const + { + if (encoding != &Null (Encoding)) + return encoding->get_code (glyph); + else + { + hb_codepoint_t sid = glyph_to_sid (glyph); + if (sid == 0) return 0; + hb_codepoint_t code = 0; + switch (topDict.EncodingOffset) + { + case StandardEncoding: + code = lookup_standard_encoding_for_code (sid); + break; + case ExpertEncoding: + code = lookup_expert_encoding_for_code (sid); + break; + default: + break; + } + return code; + } + } + + hb_map_t *create_glyph_to_sid_map () const + { + if (charset != &Null (Charset)) + { + hb_map_t *mapping = hb_map_create (); + mapping->set (0, 0); + charset->collect_glyph_to_sid_map (mapping, num_glyphs); + return mapping; + } + else + return nullptr; + } + + hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const + { + if (charset != &Null (Charset)) + return charset->get_sid (glyph, num_glyphs); + else + { + hb_codepoint_t sid = 0; + switch (topDict.CharsetOffset) + { + case ISOAdobeCharset: + if (glyph <= 228 /*zcaron*/) sid = glyph; + break; + case ExpertCharset: + sid = lookup_expert_charset_for_sid (glyph); + break; + case ExpertSubsetCharset: + sid = lookup_expert_subset_charset_for_sid (glyph); + break; + default: + break; + } + return sid; + } + } + + hb_codepoint_t sid_to_glyph (hb_codepoint_t sid) const + { + if (charset != &Null (Charset)) + return charset->get_glyph (sid, num_glyphs); + else + { + hb_codepoint_t glyph = 0; + switch (topDict.CharsetOffset) + { + case ISOAdobeCharset: + if (sid <= 228 /*zcaron*/) glyph = sid; + break; + case ExpertCharset: + glyph = lookup_expert_charset_for_glyph (sid); + break; + case ExpertSubsetCharset: + glyph = lookup_expert_subset_charset_for_glyph (sid); + break; + default: + break; + } + return glyph; + } + } + + protected: + hb_sanitize_context_t sc; + + public: + hb_blob_t *blob = nullptr; + const Encoding *encoding = nullptr; + const Charset *charset = nullptr; + const CFF1NameIndex *nameIndex = nullptr; + const CFF1TopDictIndex *topDictIndex = nullptr; + const CFF1StringIndex *stringIndex = nullptr; + const CFF1Subrs *globalSubrs = nullptr; + const CFF1CharStrings *charStrings = nullptr; + const CFF1FDArray *fdArray = nullptr; + const CFF1FDSelect *fdSelect = nullptr; + unsigned int fdCount = 0; + + cff1_top_dict_values_t topDict; + hb_vector_t + fontDicts; + hb_vector_t privateDicts; + + unsigned int num_glyphs = 0; + }; + + struct accelerator_t : accelerator_templ_t + { + accelerator_t (hb_face_t *face) + { + SUPER::init (face); + + glyph_names.set_relaxed (nullptr); + + if (!is_valid ()) return; + if (is_CID ()) return; + + } + ~accelerator_t () + { + hb_sorted_vector_t *names = glyph_names.get_relaxed (); + if (names) + { + names->fini (); + hb_free (names); + } + + SUPER::fini (); + } + + bool get_glyph_name (hb_codepoint_t glyph, + char *buf, unsigned int buf_len) const + { + if (unlikely (glyph >= num_glyphs)) return false; + if (unlikely (!is_valid ())) return false; + if (is_CID()) return false; + if (unlikely (!buf_len)) return true; + hb_codepoint_t sid = glyph_to_sid (glyph); + const char *str; + size_t str_len; + if (sid < cff1_std_strings_length) + { + hb_bytes_t byte_str = cff1_std_strings (sid); + str = byte_str.arrayZ; + str_len = byte_str.length; + } + else + { + hb_ubytes_t ubyte_str = (*stringIndex)[sid - cff1_std_strings_length]; + str = (const char *)ubyte_str.arrayZ; + str_len = ubyte_str.length; + } + if (!str_len) return false; + unsigned int len = hb_min (buf_len - 1, str_len); + strncpy (buf, (const char*)str, len); + buf[len] = '\0'; + return true; + } + + bool get_glyph_from_name (const char *name, int len, + hb_codepoint_t *glyph) const + { + if (unlikely (!is_valid ())) return false; + if (is_CID()) return false; + if (len < 0) len = strlen (name); + if (unlikely (!len)) return false; + + retry: + hb_sorted_vector_t *names = glyph_names.get_acquire (); + if (unlikely (!names)) + { + names = (hb_sorted_vector_t *) hb_calloc (sizeof (hb_sorted_vector_t), 1); + if (likely (names)) + { + names->init (); + /* TODO */ + + /* fill glyph names */ + for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + { + hb_codepoint_t sid = glyph_to_sid (gid); + gname_t gname; + gname.sid = sid; + if (sid < cff1_std_strings_length) + gname.name = cff1_std_strings (sid); + else + { + hb_ubytes_t ustr = (*stringIndex)[sid - cff1_std_strings_length]; + gname.name = hb_bytes_t ((const char*) ustr.arrayZ, ustr.length); + } + if (unlikely (!gname.name.arrayZ)) + gname.name = hb_bytes_t ("", 0); /* To avoid nullptr. */ + names->push (gname); + } + names->qsort (); + } + if (unlikely (!glyph_names.cmpexch (nullptr, names))) + { + if (names) + { + names->fini (); + hb_free (names); + } + goto retry; + } + } + + gname_t key = { hb_bytes_t (name, len), 0 }; + const gname_t *gname = names ? names->bsearch (key) : nullptr; + if (!gname) return false; + hb_codepoint_t gid = sid_to_glyph (gname->sid); + if (!gid && gname->sid) return false; + *glyph = gid; + return true; + } + + HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; + HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const; + HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const; + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; + + private: + struct gname_t + { + hb_bytes_t name; + uint16_t sid; + + static int cmp (const void *a_, const void *b_) + { + const gname_t *a = (const gname_t *)a_; + const gname_t *b = (const gname_t *)b_; + unsigned minlen = hb_min (a->name.length, b->name.length); + int ret = strncmp (a->name.arrayZ, b->name.arrayZ, minlen); + if (ret) return ret; + return a->name.length - b->name.length; + } + + int cmp (const gname_t &a) const { return cmp (&a, this); } + }; + + mutable hb_atomic_ptr_t> glyph_names; + + typedef accelerator_templ_t SUPER; + }; + + struct accelerator_subset_t : accelerator_templ_t {}; + + bool subset (hb_subset_context_t *c) const { return hb_subset_cff1 (c); } + + protected: + HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_code (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_encoding_for_code (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_sid (hb_codepoint_t glyph); + HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph); + HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_glyph (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_glyph (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_sid (hb_codepoint_t code); + + public: + FixedVersion version; /* Version of CFF table. set to 0x0100u */ + NNOffsetTo nameIndex; /* headerSize = Offset to Name INDEX. */ + HBUINT8 offSize; /* offset size (unused?) */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct cff1_accelerator_t : cff1::accelerator_t { + cff1_accelerator_t (hb_face_t *face) : cff1::accelerator_t (face) {} +}; + +} /* namespace OT */ + +#endif /* HB_OT_CFF1_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.cc b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.cc new file mode 100644 index 00000000000..79555655519 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.cc @@ -0,0 +1,222 @@ +/* + * Copyright ยฉ 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_FONT_CFF + +#include "hb-ot-cff2-table.hh" +#include "hb-cff2-interp-cs.hh" +#include "hb-draw.hh" + +using namespace CFF; + +struct cff2_extents_param_t +{ + cff2_extents_param_t () + { + min_x.set_int (INT_MAX); + min_y.set_int (INT_MAX); + max_x.set_int (INT_MIN); + max_y.set_int (INT_MIN); + } + + void start_path () { path_open = true; } + void end_path () { path_open = false; } + bool is_path_open () const { return path_open; } + + void update_bounds (const point_t &pt) + { + if (pt.x < min_x) min_x = pt.x; + if (pt.x > max_x) max_x = pt.x; + if (pt.y < min_y) min_y = pt.y; + if (pt.y > max_y) max_y = pt.y; + } + + bool path_open = false; + number_t min_x; + number_t min_y; + number_t max_x; + number_t max_y; +}; + +struct cff2_path_procs_extents_t : path_procs_t, cff2_extents_param_t> +{ + static void moveto (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt) + { + param.end_path (); + env.moveto (pt); + } + + static void line (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.update_bounds (env.get_pt ()); + } + env.moveto (pt1); + param.update_bounds (env.get_pt ()); + } + + static void curve (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.update_bounds (env.get_pt ()); + } + /* include control points */ + param.update_bounds (pt1); + param.update_bounds (pt2); + env.moveto (pt3); + param.update_bounds (env.get_pt ()); + } +}; + +struct cff2_cs_opset_extents_t : cff2_cs_opset_t {}; + +bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*charStrings)[glyph]; + cff2_cs_interp_env_t env (str, *this, fd, font->coords, font->num_coords); + cff2_cs_interpreter_t interp (env); + cff2_extents_param_t param; + if (unlikely (!interp.interpret (param))) return false; + + if (param.min_x >= param.max_x) + { + extents->width = 0; + extents->x_bearing = 0; + } + else + { + extents->x_bearing = roundf (param.min_x.to_real ()); + extents->width = roundf (param.max_x.to_real () - extents->x_bearing); + } + if (param.min_y >= param.max_y) + { + extents->height = 0; + extents->y_bearing = 0; + } + else + { + extents->y_bearing = roundf (param.max_y.to_real ()); + extents->height = roundf (param.min_y.to_real () - extents->y_bearing); + } + + font->scale_glyph_extents (extents); + + return true; +} + +bool OT::cff2::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const +{ + funcs->push_clip_glyph (data, glyph, font); + funcs->color (data, true, foreground); + funcs->pop_clip (data); + + return true; +} + +struct cff2_path_param_t +{ + cff2_path_param_t (hb_font_t *font_, hb_draw_session_t &draw_session_) + { + draw_session = &draw_session_; + font = font_; + } + + void move_to (const point_t &p) + { draw_session->move_to (font->em_fscalef_x (p.x.to_real ()), font->em_fscalef_y (p.y.to_real ())); } + + void line_to (const point_t &p) + { draw_session->line_to (font->em_fscalef_x (p.x.to_real ()), font->em_fscalef_y (p.y.to_real ())); } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + draw_session->cubic_to (font->em_fscalef_x (p1.x.to_real ()), font->em_fscalef_y (p1.y.to_real ()), + font->em_fscalef_x (p2.x.to_real ()), font->em_fscalef_y (p2.y.to_real ()), + font->em_fscalef_x (p3.x.to_real ()), font->em_fscalef_y (p3.y.to_real ())); + } + + protected: + hb_draw_session_t *draw_session; + hb_font_t *font; +}; + +struct cff2_path_procs_path_t : path_procs_t, cff2_path_param_t> +{ + static void moveto (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt) + { + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1) + { + param.line_to (pt1); + env.moveto (pt1); + } + + static void curve (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +struct cff2_cs_opset_path_t : cff2_cs_opset_t {}; + +bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*charStrings)[glyph]; + cff2_cs_interp_env_t env (str, *this, fd, font->coords, font->num_coords); + cff2_cs_interpreter_t interp (env); + cff2_path_param_t param (font, draw_session); + if (unlikely (!interp.interpret (param))) return false; + return true; +} + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.hh new file mode 100644 index 00000000000..b9a8819ab85 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.hh @@ -0,0 +1,540 @@ +/* + * Copyright ยฉ 2018 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF2_TABLE_HH +#define HB_OT_CFF2_TABLE_HH + +#include "hb-ot-cff-common.hh" +#include "hb-subset-cff2.hh" +#include "hb-draw.hh" +#include "hb-paint.hh" + +namespace CFF { + +/* + * CFF2 -- Compact Font Format (CFF) Version 2 + * https://docs.microsoft.com/en-us/typography/opentype/spec/cff2 + */ +#define HB_OT_TAG_cff2 HB_TAG('C','F','F','2') + +typedef CFFIndex CFF2Index; +template struct CFF2IndexOf : CFFIndexOf {}; + +typedef CFF2Index CFF2CharStrings; +typedef Subrs CFF2Subrs; + +typedef FDSelect3_4 FDSelect4; +typedef FDSelect3_4_Range FDSelect4_Range; + +struct CFF2FDSelect +{ + bool serialize (hb_serialize_context_t *c, const CFF2FDSelect &src, unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (num_glyphs); + CFF2FDSelect *dest = c->allocate_size (size); + if (unlikely (!dest)) return_trace (false); + hb_memcpy (dest, &src, size); + return_trace (true); + } + + unsigned int get_size (unsigned int num_glyphs) const + { + switch (format) + { + case 0: return format.static_size + u.format0.get_size (num_glyphs); + case 3: return format.static_size + u.format3.get_size (); + case 4: return format.static_size + u.format4.get_size (); + default:return 0; + } + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { + if (this == &Null (CFF2FDSelect)) + return 0; + + switch (format) + { + case 0: return u.format0.get_fd (glyph); + case 3: return u.format3.get_fd (glyph); + case 4: return u.format4.get_fd (glyph); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, fdcount)); + case 3: return_trace (u.format3.sanitize (c, fdcount)); + case 4: return_trace (u.format4.sanitize (c, fdcount)); + default:return_trace (false); + } + } + + HBUINT8 format; + union { + FDSelect0 format0; + FDSelect3 format3; + FDSelect4 format4; + } u; + public: + DEFINE_SIZE_MIN (2); +}; + +struct CFF2VariationStore +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this)) && c->check_range (&varStore, size) && varStore.sanitize (c)); + } + + bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore) + { + TRACE_SERIALIZE (this); + unsigned int size_ = varStore->get_size (); + CFF2VariationStore *dest = c->allocate_size (size_); + if (unlikely (!dest)) return_trace (false); + hb_memcpy (dest, varStore, size_); + return_trace (true); + } + + unsigned int get_size () const { return HBUINT16::static_size + size; } + + HBUINT16 size; + VariationStore varStore; + + DEFINE_SIZE_MIN (2 + VariationStore::min_size); +}; + +struct cff2_top_dict_values_t : top_dict_values_t<> +{ + void init () + { + top_dict_values_t<>::init (); + vstoreOffset = 0; + FDSelectOffset = 0; + } + void fini () { top_dict_values_t<>::fini (); } + + unsigned int vstoreOffset; + unsigned int FDSelectOffset; +}; + +struct cff2_top_dict_opset_t : top_dict_opset_t<> +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff2_top_dict_values_t& dictval) + { + switch (op) { + case OpCode_FontMatrix: + { + dict_val_t val; + val.init (); + dictval.add_op (op, env.str_ref); + env.clear_args (); + } + break; + + case OpCode_vstore: + dictval.vstoreOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FDSelect: + dictval.FDSelectOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + SUPER::process_op (op, env, dictval); + /* Record this operand below if stack is empty, otherwise done */ + if (!env.argStack.is_empty ()) return; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } + + typedef top_dict_opset_t<> SUPER; +}; + +struct cff2_font_dict_values_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + privateDictInfo.init (); + } + void fini () { dict_values_t::fini (); } + + table_info_t privateDictInfo; +}; + +struct cff2_font_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff2_font_dict_values_t& dictval) + { + switch (op) { + case OpCode_Private: + dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.size = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + SUPER::process_op (op, env); + if (!env.argStack.is_empty ()) + return; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } + + private: + typedef dict_opset_t SUPER; +}; + +template +struct cff2_private_dict_values_base_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + subrsOffset = 0; + localSubrs = &Null (CFF2Subrs); + ivs = 0; + } + void fini () { dict_values_t::fini (); } + + unsigned int subrsOffset; + const CFF2Subrs *localSubrs; + unsigned int ivs; +}; + +typedef cff2_private_dict_values_base_t cff2_private_dict_values_subset_t; +typedef cff2_private_dict_values_base_t cff2_private_dict_values_t; + +struct cff2_priv_dict_interp_env_t : num_interp_env_t +{ + cff2_priv_dict_interp_env_t (const hb_ubytes_t &str) : + num_interp_env_t (str) {} + + void process_vsindex () + { + if (likely (!seen_vsindex)) + { + set_ivs (argStack.pop_uint ()); + } + seen_vsindex = true; + } + + unsigned int get_ivs () const { return ivs; } + void set_ivs (unsigned int ivs_) { ivs = ivs_; } + + protected: + unsigned int ivs = 0; + bool seen_vsindex = false; +}; + +struct cff2_private_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_dict_values_t& dictval) + { + num_dict_val_t val; + val.init (); + + switch (op) { + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ExpansionFactor: + case OpCode_LanguageGroup: + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + env.clear_args (); + break; + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_vsindexdict: + env.process_vsindex (); + dictval.ivs = env.get_ivs (); + env.clear_args (); + break; + case OpCode_blenddict: + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref, val); + } +}; + +struct cff2_private_dict_opset_subset_t : dict_opset_t +{ + static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_dict_values_subset_t& dictval) + { + switch (op) { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + env.clear_args (); + break; + + case OpCode_blenddict: + env.clear_args (); + return; + + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + SUPER::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } + + private: + typedef dict_opset_t SUPER; +}; + +typedef dict_interpreter_t cff2_top_dict_interpreter_t; +typedef dict_interpreter_t cff2_font_dict_interpreter_t; + +struct CFF2FDArray : FDArray +{ + /* FDArray::serialize does not compile without this partial specialization */ + template + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray::serialize (c, it, opszr); } +}; + +} /* namespace CFF */ + +namespace OT { + +using namespace CFF; + +struct cff2 +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_cff2; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2)); + } + + template + struct accelerator_templ_t + { + accelerator_templ_t (hb_face_t *face) + { + topDict.init (); + fontDicts.init (); + privateDicts.init (); + + this->blob = sc.reference_table (face); + + /* setup for run-time santization */ + sc.init (this->blob); + sc.start_processing (); + + const OT::cff2 *cff2 = this->blob->template as (); + + if (cff2 == &Null (OT::cff2)) + goto fail; + + { /* parse top dict */ + hb_ubytes_t topDictStr = (cff2 + cff2->topDict).as_ubytes (cff2->topDictSize); + if (unlikely (!topDictStr.sanitize (&sc))) goto fail; + num_interp_env_t env (topDictStr); + cff2_top_dict_interpreter_t top_interp (env); + topDict.init (); + if (unlikely (!top_interp.interpret (topDict))) goto fail; + } + + globalSubrs = &StructAtOffset (cff2, cff2->topDict + cff2->topDictSize); + varStore = &StructAtOffsetOrNull (cff2, topDict.vstoreOffset); + charStrings = &StructAtOffsetOrNull (cff2, topDict.charStringsOffset); + fdArray = &StructAtOffsetOrNull (cff2, topDict.FDArrayOffset); + fdSelect = &StructAtOffsetOrNull (cff2, topDict.FDSelectOffset); + + if (((varStore != &Null (CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || + (charStrings == &Null (CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) || + (globalSubrs == &Null (CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) || + (fdArray == &Null (CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) || + (((fdSelect != &Null (CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count))))) + goto fail; + + num_glyphs = charStrings->count; + if (num_glyphs != sc.get_num_glyphs ()) + goto fail; + + fdCount = fdArray->count; + if (!privateDicts.resize (fdCount)) + goto fail; + + /* parse font dicts and gather private dicts */ + for (unsigned int i = 0; i < fdCount; i++) + { + const hb_ubytes_t fontDictStr = (*fdArray)[i]; + if (unlikely (!fontDictStr.sanitize (&sc))) goto fail; + cff2_font_dict_values_t *font; + num_interp_env_t env (fontDictStr); + cff2_font_dict_interpreter_t font_interp (env); + font = fontDicts.push (); + if (unlikely (font == &Crap (cff2_font_dict_values_t))) goto fail; + font->init (); + if (unlikely (!font_interp.interpret (*font))) goto fail; + + const hb_ubytes_t privDictStr = StructAtOffsetOrNull (cff2, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + cff2_priv_dict_interp_env_t env2 (privDictStr); + dict_interpreter_t priv_interp (env2); + privateDicts[i].init (); + if (unlikely (!priv_interp.interpret (privateDicts[i]))) goto fail; + + privateDicts[i].localSubrs = &StructAtOffsetOrNull (&privDictStr[0], privateDicts[i].subrsOffset); + if (privateDicts[i].localSubrs != &Null (CFF2Subrs) && + unlikely (!privateDicts[i].localSubrs->sanitize (&sc))) + goto fail; + } + + + return; + + fail: + _fini (); + } + ~accelerator_templ_t () { _fini (); } + void _fini () + { + sc.end_processing (); + topDict.fini (); + fontDicts.fini (); + privateDicts.fini (); + hb_blob_destroy (blob); + blob = nullptr; + } + + hb_map_t *create_glyph_to_sid_map () const + { + return nullptr; + } + + bool is_valid () const { return blob; } + + protected: + hb_sanitize_context_t sc; + + public: + hb_blob_t *blob = nullptr; + cff2_top_dict_values_t topDict; + const CFF2Subrs *globalSubrs = nullptr; + const CFF2VariationStore *varStore = nullptr; + const CFF2CharStrings *charStrings = nullptr; + const CFF2FDArray *fdArray = nullptr; + const CFF2FDSelect *fdSelect = nullptr; + unsigned int fdCount = 0; + + hb_vector_t fontDicts; + hb_vector_t privateDicts; + + unsigned int num_glyphs = 0; + }; + + struct accelerator_t : accelerator_templ_t + { + accelerator_t (hb_face_t *face) : accelerator_templ_t (face) {} + + HB_INTERNAL bool get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const; + HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const; + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; + }; + + typedef accelerator_templ_t accelerator_subset_t; + + bool subset (hb_subset_context_t *c) const { return hb_subset_cff2 (c); } + + public: + FixedVersion version; /* Version of CFF2 table. set to 0x0200u */ + NNOffsetTo topDict; /* headerSize = Offset to Top DICT. */ + HBUINT16 topDictSize; /* Top DICT size */ + + public: + DEFINE_SIZE_STATIC (5); +}; + +struct cff2_accelerator_t : cff2::accelerator_t { + cff2_accelerator_t (hb_face_t *face) : cff2::accelerator_t (face) {} +}; + +} /* namespace OT */ + +#endif /* HB_OT_CFF2_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cmap-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-cmap-table.hh index 883d7b3f0b2..cf5ccd53e95 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-cmap-table.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cmap-table.hh @@ -27,41 +27,67 @@ #ifndef HB_OT_CMAP_TABLE_HH #define HB_OT_CMAP_TABLE_HH -#include "hb-open-type-private.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-shaper-arabic-pua.hh" +#include "hb-open-type.hh" +#include "hb-set.hh" +#include "hb-cache.hh" +/* + * cmap -- Character to Glyph Index Mapping + * https://docs.microsoft.com/en-us/typography/opentype/spec/cmap + */ +#define HB_OT_TAG_cmap HB_TAG('c','m','a','p') namespace OT { -/* - * cmap -- Character To Glyph Index Mapping Table - */ - -#define HB_OT_TAG_cmap HB_TAG('c','m','a','p') - - struct CmapSubtableFormat0 { - inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0; - if (!gid) + if (unlikely (!gid)) return false; *glyph = gid; return true; } - inline bool sanitize (hb_sanitize_context_t *c) const + unsigned get_language () const + { + return language; + } + + void collect_unicodes (hb_set_t *out) const + { + for (unsigned int i = 0; i < 256; i++) + if (glyphIdArray[i]) + out->add (i); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + for (unsigned i = 0; i < 256; i++) + if (glyphIdArray[i]) + { + hb_codepoint_t glyph = glyphIdArray[i]; + unicodes->add (i); + mapping->set (i, glyph); + } + } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } protected: - UINT16 format; /* Format number is set to 0. */ - UINT16 lengthZ; /* Byte length of this subtable. */ - UINT16 languageZ; /* Ignore. */ - UINT8 glyphIdArray[256];/* An array that maps character + HBUINT16 format; /* Format number is set to 0. */ + HBUINT16 length; /* Byte length of this subtable. */ + HBUINT16 language; /* Ignore. */ + HBUINT8 glyphIdArray[256];/* An array that maps character * code to glyph index values. */ public: DEFINE_SIZE_STATIC (6 + 256); @@ -69,12 +95,300 @@ struct CmapSubtableFormat0 struct CmapSubtableFormat4 { + + + template + void to_ranges (Iterator it, Writer& range_writer) + { + hb_codepoint_t start_cp = 0, prev_run_start_cp = 0, run_start_cp = 0, end_cp = 0, last_gid = 0; + int run_length = 0 , delta = 0, prev_delta = 0; + + enum { + FIRST_SUB_RANGE, + FOLLOWING_SUB_RANGE, + } mode; + + while (it) { + // Start a new range + { + const auto& pair = *it; + start_cp = pair.first; + prev_run_start_cp = start_cp; + run_start_cp = start_cp; + end_cp = start_cp; + last_gid = pair.second; + run_length = 1; + prev_delta = 0; + } + + delta = last_gid - start_cp; + mode = FIRST_SUB_RANGE; + it++; + + while (it) { + // Process range + const auto& pair = *it; + hb_codepoint_t next_cp = pair.first; + hb_codepoint_t next_gid = pair.second; + if (next_cp != end_cp + 1) { + // Current range is over, stop processing. + break; + } + + if (next_gid == last_gid + 1) { + // The current run continues. + end_cp = next_cp; + run_length++; + last_gid = next_gid; + it++; + continue; + } + + // A new run is starting, decide if we want to commit the current run. + int split_cost = (mode == FIRST_SUB_RANGE) ? 8 : 16; + int run_cost = run_length * 2; + if (run_cost >= split_cost) { + commit_current_range(start_cp, + prev_run_start_cp, + run_start_cp, + end_cp, + delta, + prev_delta, + split_cost, + range_writer); + start_cp = next_cp; + } + + // Start the new run + mode = FOLLOWING_SUB_RANGE; + prev_run_start_cp = run_start_cp; + run_start_cp = next_cp; + end_cp = next_cp; + prev_delta = delta; + delta = next_gid - run_start_cp; + run_length = 1; + last_gid = next_gid; + it++; + } + + // Finalize range + commit_current_range (start_cp, + prev_run_start_cp, + run_start_cp, + end_cp, + delta, + prev_delta, + 8, + range_writer); + } + + if (likely (end_cp != 0xFFFF)) { + range_writer (0xFFFF, 0xFFFF, 1); + } + } + + /* + * Writes the current range as either one or two ranges depending on what is most efficient. + */ + template + void commit_current_range (hb_codepoint_t start, + hb_codepoint_t prev_run_start, + hb_codepoint_t run_start, + hb_codepoint_t end, + int run_delta, + int previous_run_delta, + int split_cost, + Writer& range_writer) { + bool should_split = false; + if (start < run_start && run_start < end) { + int run_cost = (end - run_start + 1) * 2; + if (run_cost >= split_cost) { + should_split = true; + } + } + + // TODO(grieger): handle case where delta is legitimately 0, mark range offset array instead? + if (should_split) { + if (start == prev_run_start) + range_writer (start, run_start - 1, previous_run_delta); + else + range_writer (start, run_start - 1, 0); + range_writer (run_start, end, run_delta); + return; + } + + + if (start == run_start) { + // Range is only a run + range_writer (start, end, run_delta); + return; + } + + // Write only a single non-run range. + range_writer (start, end, 0); + } + + template + unsigned serialize_find_segcount (Iterator it) { + struct Counter { + unsigned segcount = 0; + + void operator() (hb_codepoint_t start, + hb_codepoint_t end, + int delta) { + segcount++; + } + } counter; + + to_ranges (+it, counter); + return counter.segcount; + } + + + template + bool serialize_start_end_delta_arrays (hb_serialize_context_t *c, + Iterator it, + int segcount) + { + struct Writer { + hb_serialize_context_t *serializer_; + HBUINT16* end_code_; + HBUINT16* start_code_; + HBINT16* id_delta_; + int index_; + + Writer(hb_serialize_context_t *serializer) + : serializer_(serializer), + end_code_(nullptr), + start_code_(nullptr), + id_delta_(nullptr), + index_ (0) {} + void operator() (hb_codepoint_t start, + hb_codepoint_t end, + int delta) { + start_code_[index_] = start; + end_code_[index_] = end; + id_delta_[index_] = delta; + index_++; + } + } writer(c); + + writer.end_code_ = c->allocate_size (HBUINT16::static_size * segcount); + c->allocate_size (2); // padding + writer.start_code_ = c->allocate_size (HBUINT16::static_size * segcount); + writer.id_delta_ = c->allocate_size (HBINT16::static_size * segcount); + + if (unlikely (!writer.end_code_ || !writer.start_code_ || !writer.id_delta_)) return false; + + to_ranges (+it, writer); + return true; + } + + template + HBUINT16* serialize_rangeoffset_glyid (hb_serialize_context_t *c, + Iterator it, + HBUINT16 *endCode, + HBUINT16 *startCode, + HBINT16 *idDelta, + unsigned segcount) + { + hb_map_t cp_to_gid { it }; + + HBUINT16 *idRangeOffset = c->allocate_size (HBUINT16::static_size * segcount); + if (unlikely (!c->check_success (idRangeOffset))) return nullptr; + if (unlikely ((char *)idRangeOffset - (char *)idDelta != (int) segcount * (int) HBINT16::static_size)) return nullptr; + + for (unsigned i : + hb_range (segcount) + | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; })) + { + idRangeOffset[i] = 2 * (c->start_embed () - idRangeOffset - i); + for (hb_codepoint_t cp = startCode[i]; cp <= endCode[i]; cp++) + { + HBUINT16 gid; + gid = cp_to_gid[cp]; + c->copy (gid); + } + } + + return idRangeOffset; + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it) + { + auto format4_iter = + + it + | hb_filter ([&] (const hb_pair_t _) + { return _.first <= 0xFFFF; }) + ; + + if (!format4_iter) return; + + unsigned table_initpos = c->length (); + if (unlikely (!c->extend_min (this))) return; + this->format = 4; + + hb_vector_t> cp_to_gid { + format4_iter + }; + + //serialize endCode[], startCode[], idDelta[] + HBUINT16* endCode = c->start_embed (); + unsigned segcount = serialize_find_segcount (cp_to_gid.iter()); + if (unlikely (!serialize_start_end_delta_arrays (c, cp_to_gid.iter(), segcount))) + return; + + HBUINT16 *startCode = endCode + segcount + 1; + HBINT16 *idDelta = ((HBINT16*)startCode) + segcount; + + HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c, + cp_to_gid.iter (), + endCode, + startCode, + idDelta, + segcount); + if (unlikely (!c->check_success (idRangeOffset))) return; + + this->length = c->length () - table_initpos; + if ((long long) this->length != (long long) c->length () - table_initpos) + { + // Length overflowed. Discard the current object before setting the error condition, otherwise + // discard is a noop which prevents the higher level code from reverting the serializer to the + // pre-error state in cmap4 overflow handling code. + c->pop_discard (); + c->err (HB_SERIALIZE_ERROR_INT_OVERFLOW); + return; + } + + this->segCountX2 = segcount * 2; + this->entrySelector = hb_max (1u, hb_bit_storage (segcount)) - 1; + this->searchRange = 2 * (1u << this->entrySelector); + this->rangeShift = segcount * 2 > this->searchRange + ? 2 * segcount - this->searchRange + : 0; + } + + unsigned get_language () const + { + return language; + } + struct accelerator_t { - inline void init (const CmapSubtableFormat4 *subtable) + accelerator_t () {} + accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); } + + void init (const CmapSubtableFormat4 *subtable) { segCount = subtable->segCountX2 / 2; - endCount = subtable->values; + endCount = subtable->values.arrayZ; startCount = endCount + segCount + 1; idDelta = startCount + segCount; idRangeOffset = idDelta + segCount; @@ -82,68 +396,162 @@ struct CmapSubtableFormat4 glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2; } - static inline bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { - const accelerator_t *thiz = (const accelerator_t *) obj; - - /* Custom two-array bsearch. */ - int min = 0, max = (int) thiz->segCount - 1; - const UINT16 *startCount = thiz->startCount; - const UINT16 *endCount = thiz->endCount; - unsigned int i; - while (min <= max) + struct CustomRange { - int mid = (min + max) / 2; - if (codepoint < startCount[mid]) - max = mid - 1; - else if (codepoint > endCount[mid]) - min = mid + 1; - else + int cmp (hb_codepoint_t k, + unsigned distance) const { - i = mid; - goto found; + if (k > last) return +1; + if (k < (&last)[distance]/*first*/) return -1; + return 0; } - } - return false; + HBUINT16 last; + }; + + const HBUINT16 *found = hb_bsearch (codepoint, + this->endCount, + this->segCount, + sizeof (CustomRange), + _hb_cmp_method, + this->segCount + 1); + if (unlikely (!found)) + return false; + unsigned int i = found - endCount; - found: hb_codepoint_t gid; - unsigned int rangeOffset = thiz->idRangeOffset[i]; + unsigned int rangeOffset = this->idRangeOffset[i]; if (rangeOffset == 0) - gid = codepoint + thiz->idDelta[i]; + gid = codepoint + this->idDelta[i]; else { /* Somebody has been smoking... */ - unsigned int index = rangeOffset / 2 + (codepoint - thiz->startCount[i]) + i - thiz->segCount; - if (unlikely (index >= thiz->glyphIdArrayLength)) + unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) return false; - gid = thiz->glyphIdArray[index]; + gid = this->glyphIdArray[index]; if (unlikely (!gid)) return false; - gid += thiz->idDelta[i]; + gid += this->idDelta[i]; } - - *glyph = gid & 0xFFFFu; + gid &= 0xFFFFu; + if (unlikely (!gid)) + return false; + *glyph = gid; return true; } - const UINT16 *endCount; - const UINT16 *startCount; - const UINT16 *idDelta; - const UINT16 *idRangeOffset; - const UINT16 *glyphIdArray; + HB_INTERNAL static bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) + { return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph); } + + void collect_unicodes (hb_set_t *out) const + { + unsigned int count = this->segCount; + if (count && this->startCount[count - 1] == 0xFFFFu) + count--; /* Skip sentinel segment. */ + for (unsigned int i = 0; i < count; i++) + { + hb_codepoint_t start = this->startCount[i]; + hb_codepoint_t end = this->endCount[i]; + unsigned int rangeOffset = this->idRangeOffset[i]; + out->add_range(start, end); + if (rangeOffset == 0) + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu; + if (unlikely (!gid)) + out->del(codepoint); + } + } + else + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) + { + out->del_range (codepoint, end); + break; + } + hb_codepoint_t gid = this->glyphIdArray[index]; + if (unlikely (!gid)) + out->del(codepoint); + } + } + } + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + // TODO(grieger): optimize similar to collect_unicodes + // (ie. use add_range()) + unsigned count = this->segCount; + if (count && this->startCount[count - 1] == 0xFFFFu) + count--; /* Skip sentinel segment. */ + for (unsigned i = 0; i < count; i++) + { + hb_codepoint_t start = this->startCount[i]; + hb_codepoint_t end = this->endCount[i]; + unsigned rangeOffset = this->idRangeOffset[i]; + if (rangeOffset == 0) + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu; + if (unlikely (!gid)) + continue; + unicodes->add (codepoint); + mapping->set (codepoint, gid); + } + } + else + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + unsigned index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) + break; + hb_codepoint_t gid = this->glyphIdArray[index]; + if (unlikely (!gid)) + continue; + unicodes->add (codepoint); + mapping->set (codepoint, gid); + } + } + } + } + + const HBUINT16 *endCount; + const HBUINT16 *startCount; + const HBUINT16 *idDelta; + const HBUINT16 *idRangeOffset; + const HBUINT16 *glyphIdArray; unsigned int segCount; unsigned int glyphIdArrayLength; }; - inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { - accelerator_t accel; - accel.init (this); + accelerator_t accel (this); return accel.get_glyph_func (&accel, codepoint, glyph); } + void collect_unicodes (hb_set_t *out) const + { + accelerator_t accel (this); + accel.collect_unicodes (out); + } - inline bool sanitize (hb_sanitize_context_t *c) const + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + accelerator_t accel (this); + accel.collect_mapping (unicodes, mapping); + } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) @@ -154,9 +562,9 @@ struct CmapSubtableFormat4 /* Some broken fonts have too long of a "length" value. * If that is the case, just change the value to truncate * the subtable at the end of the blob. */ - uint16_t new_length = (uint16_t) MIN ((uintptr_t) 65535, - (uintptr_t) (c->end - - (char *) this)); + uint16_t new_length = (uint16_t) hb_min ((uintptr_t) 65535, + (uintptr_t) (c->end - + (char *) this)); if (!c->try_set (&length, new_length)) return_trace (false); } @@ -164,25 +572,29 @@ struct CmapSubtableFormat4 return_trace (16 + 4 * (unsigned int) segCountX2 <= length); } - protected: - UINT16 format; /* Format number is set to 4. */ - UINT16 length; /* This is the length in bytes of the - * subtable. */ - UINT16 languageZ; /* Ignore. */ - UINT16 segCountX2; /* 2 x segCount. */ - UINT16 searchRangeZ; /* 2 * (2**floor(log2(segCount))) */ - UINT16 entrySelectorZ; /* log2(searchRange/2) */ - UINT16 rangeShiftZ; /* 2 x segCount - searchRange */ - UINT16 values[VAR]; + + protected: + HBUINT16 format; /* Format number is set to 4. */ + HBUINT16 length; /* This is the length in bytes of the + * subtable. */ + HBUINT16 language; /* Ignore. */ + HBUINT16 segCountX2; /* 2 x segCount. */ + HBUINT16 searchRange; /* 2 * (2**floor(log2(segCount))) */ + HBUINT16 entrySelector; /* log2(searchRange/2) */ + HBUINT16 rangeShift; /* 2 x segCount - searchRange */ + + UnsizedArrayOf + values; #if 0 - UINT16 endCount[segCount]; /* End characterCode for each segment, + HBUINT16 endCount[segCount]; /* End characterCode for each segment, * last=0xFFFFu. */ - UINT16 reservedPad; /* Set to 0. */ - UINT16 startCount[segCount]; /* Start character code for each segment. */ - INT16 idDelta[segCount]; /* Delta for all character codes in segment. */ - UINT16 idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */ - UINT16 glyphIdArray[VAR]; /* Glyph index array (arbitrary length) */ + HBUINT16 reservedPad; /* Set to 0. */ + HBUINT16 startCount[segCount]; /* Start character code for each segment. */ + HBINT16 idDelta[segCount]; /* Delta for all character codes in segment. */ + HBUINT16 idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */ + UnsizedArrayOf + glyphIdArray; /* Glyph index array (arbitrary length) */ #endif public: @@ -193,6 +605,9 @@ struct CmapSubtableLongGroup { friend struct CmapSubtableFormat12; friend struct CmapSubtableFormat13; + template + friend struct CmapSubtableLongSegmented; + friend struct cmap; int cmp (hb_codepoint_t codepoint) const { @@ -201,35 +616,65 @@ struct CmapSubtableLongGroup return 0; } - inline bool sanitize (hb_sanitize_context_t *c) const + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } private: - UINT32 startCharCode; /* First character code in this group. */ - UINT32 endCharCode; /* Last character code in this group. */ - UINT32 glyphID; /* Glyph index; interpretation depends on - * subtable format. */ + HBUINT32 startCharCode; /* First character code in this group. */ + HBUINT32 endCharCode; /* Last character code in this group. */ + HBUINT32 glyphID; /* Glyph index; interpretation depends on + * subtable format. */ public: DEFINE_SIZE_STATIC (12); }; +DECLARE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup); template struct CmapSubtableTrimmed { - inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { /* Rely on our implicit array bound-checking. */ hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode]; - if (!gid) + if (unlikely (!gid)) return false; *glyph = gid; return true; } - inline bool sanitize (hb_sanitize_context_t *c) const + unsigned get_language () const + { + return language; + } + + void collect_unicodes (hb_set_t *out) const + { + hb_codepoint_t start = startCharCode; + unsigned int count = glyphIdArray.len; + for (unsigned int i = 0; i < count; i++) + if (glyphIdArray[i]) + out->add (start + i); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + hb_codepoint_t start_cp = startCharCode; + unsigned count = glyphIdArray.len; + for (unsigned i = 0; i < count; i++) + if (glyphIdArray[i]) + { + hb_codepoint_t unicode = start_cp + i; + hb_codepoint_t glyphid = glyphIdArray[i]; + unicodes->add (unicode); + mapping->set (unicode, glyphid); + } + } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && glyphIdArray.sanitize (c)); @@ -237,43 +682,111 @@ struct CmapSubtableTrimmed protected: UINT formatReserved; /* Subtable format and (maybe) padding. */ - UINT lengthZ; /* Byte length of this subtable. */ - UINT languageZ; /* Ignore. */ + UINT length; /* Byte length of this subtable. */ + UINT language; /* Ignore. */ UINT startCharCode; /* First character code covered. */ - ArrayOf + ArrayOf glyphIdArray; /* Array of glyph index values for character * codes in the range. */ public: DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray); }; -struct CmapSubtableFormat6 : CmapSubtableTrimmed {}; -struct CmapSubtableFormat10 : CmapSubtableTrimmed {}; +struct CmapSubtableFormat6 : CmapSubtableTrimmed {}; +struct CmapSubtableFormat10 : CmapSubtableTrimmed {}; template struct CmapSubtableLongSegmented { - inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + friend struct cmap; + + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { - int i = groups.bsearch (codepoint); - if (i == -1) + hb_codepoint_t gid = T::group_get_glyph (groups.bsearch (codepoint), codepoint); + if (unlikely (!gid)) return false; - *glyph = T::group_get_glyph (groups[i], codepoint); + *glyph = gid; return true; } - inline bool sanitize (hb_sanitize_context_t *c) const + unsigned get_language () const + { + return language; + } + + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const + { + for (unsigned int i = 0; i < this->groups.len; i++) + { + hb_codepoint_t start = this->groups[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + (hb_codepoint_t) HB_UNICODE_MAX); + hb_codepoint_t gid = this->groups[i].glyphID; + if (!gid) + { + /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */ + if (! T::group_get_glyph (this->groups[i], end)) continue; + start++; + gid++; + } + if (unlikely ((unsigned int) gid >= num_glyphs)) continue; + if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs)) + end = start + (hb_codepoint_t) num_glyphs - gid; + + out->add_range (start, hb_min (end, 0x10FFFFu)); + } + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping, /* OUT */ + unsigned num_glyphs) const + { + hb_codepoint_t last_end = 0; + for (unsigned i = 0; i < this->groups.len; i++) + { + hb_codepoint_t start = this->groups[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + (hb_codepoint_t) HB_UNICODE_MAX); + if (unlikely (start > end || start < last_end)) { + // Range is not in order and is invalid, skip it. + continue; + } + last_end = end; + + + hb_codepoint_t gid = this->groups[i].glyphID; + if (!gid) + { + /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */ + if (! T::group_get_glyph (this->groups[i], end)) continue; + start++; + gid++; + } + if (unlikely ((unsigned int) gid >= num_glyphs)) continue; + if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs)) + end = start + (hb_codepoint_t) num_glyphs - gid; + + for (unsigned cp = start; cp <= end; cp++) + { + unicodes->add (cp); + mapping->set (cp, gid); + gid++; + } + } + } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && groups.sanitize (c)); } protected: - UINT16 format; /* Subtable format; set to 12. */ - UINT16 reservedZ; /* Reserved; set to 0. */ - UINT32 lengthZ; /* Byte length of this subtable. */ - UINT32 languageZ; /* Ignore. */ - SortedArrayOf + HBUINT16 format; /* Subtable format; set to 12. */ + HBUINT16 reserved; /* Reserved; set to 0. */ + HBUINT32 length; /* Byte length of this subtable. */ + HBUINT32 language; /* Ignore. */ + SortedArray32Of groups; /* Groupings. */ public: DEFINE_SIZE_ARRAY (16, groups); @@ -281,15 +794,80 @@ struct CmapSubtableLongSegmented struct CmapSubtableFormat12 : CmapSubtableLongSegmented { - static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, - hb_codepoint_t u) - { return group.glyphID + (u - group.startCharCode); } + static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, + hb_codepoint_t u) + { return likely (group.startCharCode <= group.endCharCode) ? + group.glyphID + (u - group.startCharCode) : 0; } + + + template + void serialize (hb_serialize_context_t *c, + Iterator it) + { + if (!it) return; + unsigned table_initpos = c->length (); + if (unlikely (!c->extend_min (this))) return; + + hb_codepoint_t startCharCode = (hb_codepoint_t) -1, endCharCode = (hb_codepoint_t) -1; + hb_codepoint_t glyphID = 0; + + for (const auto& _ : +it) + { + if (startCharCode == (hb_codepoint_t) -1) + { + startCharCode = _.first; + endCharCode = _.first; + glyphID = _.second; + } + else if (!_is_gid_consecutive (endCharCode, startCharCode, glyphID, _.first, _.second)) + { + CmapSubtableLongGroup grouprecord; + grouprecord.startCharCode = startCharCode; + grouprecord.endCharCode = endCharCode; + grouprecord.glyphID = glyphID; + c->copy (grouprecord); + + startCharCode = _.first; + endCharCode = _.first; + glyphID = _.second; + } + else + endCharCode = _.first; + } + + CmapSubtableLongGroup record; + record.startCharCode = startCharCode; + record.endCharCode = endCharCode; + record.glyphID = glyphID; + c->copy (record); + + this->format = 12; + this->reserved = 0; + this->length = c->length () - table_initpos; + this->groups.len = (this->length - min_size) / CmapSubtableLongGroup::static_size; + } + + static size_t get_sub_table_size (const hb_sorted_vector_t &groups_data) + { return 16 + 12 * groups_data.length; } + + private: + static bool _is_gid_consecutive (hb_codepoint_t endCharCode, + hb_codepoint_t startCharCode, + hb_codepoint_t glyphID, + hb_codepoint_t cp, + hb_codepoint_t new_gid) + { + return (cp - 1 == endCharCode) && + new_gid == glyphID + (cp - startCharCode); + } + }; struct CmapSubtableFormat13 : CmapSubtableLongSegmented { - static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, - hb_codepoint_t u HB_UNUSED) + static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, + hb_codepoint_t u HB_UNUSED) { return group.glyphID; } }; @@ -302,76 +880,274 @@ typedef enum struct UnicodeValueRange { - inline int cmp (const hb_codepoint_t &codepoint) const + int cmp (const hb_codepoint_t &codepoint) const { if (codepoint < startUnicodeValue) return -1; if (codepoint > startUnicodeValue + additionalCount) return +1; return 0; } - inline bool sanitize (hb_sanitize_context_t *c) const + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } - UINT24 startUnicodeValue; /* First value in this range. */ - UINT8 additionalCount; /* Number of additional values in this + HBUINT24 startUnicodeValue; /* First value in this range. */ + HBUINT8 additionalCount; /* Number of additional values in this * range. */ public: DEFINE_SIZE_STATIC (4); }; -typedef SortedArrayOf DefaultUVS; +struct DefaultUVS : SortedArray32Of +{ + void collect_unicodes (hb_set_t *out) const + { + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + hb_codepoint_t first = arrayZ[i].startUnicodeValue; + hb_codepoint_t last = hb_min ((hb_codepoint_t) (first + arrayZ[i].additionalCount), + (hb_codepoint_t) HB_UNICODE_MAX); + out->add_range (first, last); + } + } + + DefaultUVS* copy (hb_serialize_context_t *c, + const hb_set_t *unicodes) const + { + DefaultUVS *out = c->start_embed (); + if (unlikely (!out)) return nullptr; + auto snap = c->snapshot (); + + HBUINT32 len; + len = 0; + if (unlikely (!c->copy (len))) return nullptr; + unsigned init_len = c->length (); + + if (this->len > unicodes->get_population () * hb_bit_storage ((unsigned) this->len)) + { + hb_codepoint_t start = HB_SET_VALUE_INVALID; + hb_codepoint_t end = HB_SET_VALUE_INVALID; + + for (hb_codepoint_t u = HB_SET_VALUE_INVALID; + unicodes->next (&u);) + { + if (!as_array ().bsearch (u)) + continue; + if (start == HB_SET_VALUE_INVALID) + { + start = u; + end = start - 1; + } + if (end + 1 != u || end - start == 255) + { + UnicodeValueRange rec; + rec.startUnicodeValue = start; + rec.additionalCount = end - start; + c->copy (rec); + start = u; + } + end = u; + } + if (start != HB_SET_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = start; + rec.additionalCount = end - start; + c->copy (rec); + } + + } + else + { + hb_codepoint_t lastCode = HB_SET_VALUE_INVALID; + int count = -1; + + for (const UnicodeValueRange& _ : *this) + { + hb_codepoint_t curEntry = (hb_codepoint_t) (_.startUnicodeValue - 1); + hb_codepoint_t end = curEntry + _.additionalCount + 2; + + for (; unicodes->next (&curEntry) && curEntry < end;) + { + count += 1; + if (lastCode == HB_SET_VALUE_INVALID) + lastCode = curEntry; + else if (lastCode + count != curEntry) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count - 1; + c->copy (rec); + + lastCode = curEntry; + count = 0; + } + } + } + + if (lastCode != HB_MAP_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count; + c->copy (rec); + } + } + + if (c->length () - init_len == 0) + { + c->revert (snap); + return nullptr; + } + else + { + if (unlikely (!c->check_assign (out->len, + (c->length () - init_len) / UnicodeValueRange::static_size, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) return nullptr; + return out; + } + } + + public: + DEFINE_SIZE_ARRAY (4, *this); +}; struct UVSMapping { - inline int cmp (const hb_codepoint_t &codepoint) const - { - return unicodeValue.cmp (codepoint); - } + int cmp (const hb_codepoint_t &codepoint) const + { return unicodeValue.cmp (codepoint); } - inline bool sanitize (hb_sanitize_context_t *c) const + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } - UINT24 unicodeValue; /* Base Unicode value of the UVS */ - GlyphID glyphID; /* Glyph ID of the UVS */ + HBUINT24 unicodeValue; /* Base Unicode value of the UVS */ + HBGlyphID16 glyphID; /* Glyph ID of the UVS */ public: DEFINE_SIZE_STATIC (5); }; -typedef SortedArrayOf NonDefaultUVS; +struct NonDefaultUVS : SortedArray32Of +{ + void collect_unicodes (hb_set_t *out) const + { + for (const auto& a : as_array ()) + out->add (a.unicodeValue); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + for (const auto& a : as_array ()) + { + hb_codepoint_t unicode = a.unicodeValue; + hb_codepoint_t glyphid = a.glyphID; + unicodes->add (unicode); + mapping->set (unicode, glyphid); + } + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + as_array () + | hb_filter (unicodes, &UVSMapping::unicodeValue) + | hb_map (&UVSMapping::glyphID) + | hb_sink (glyphset) + ; + } + + NonDefaultUVS* copy (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map) const + { + NonDefaultUVS *out = c->start_embed (); + if (unlikely (!out)) return nullptr; + + auto it = + + as_array () + | hb_filter ([&] (const UVSMapping& _) + { + return unicodes->has (_.unicodeValue) || glyphs_requested->has (_.glyphID); + }) + ; + + if (!it) return nullptr; + + HBUINT32 len; + len = it.len (); + if (unlikely (!c->copy (len))) return nullptr; + + for (const UVSMapping& _ : it) + { + UVSMapping mapping; + mapping.unicodeValue = _.unicodeValue; + mapping.glyphID = glyph_map->get (_.glyphID); + c->copy (mapping); + } + + return out; + } + + public: + DEFINE_SIZE_ARRAY (4, *this); +}; struct VariationSelectorRecord { - inline glyph_variant_t get_glyph (hb_codepoint_t codepoint, - hb_codepoint_t *glyph, - const void *base) const + glyph_variant_t get_glyph (hb_codepoint_t codepoint, + hb_codepoint_t *glyph, + const void *base) const { - int i; - const DefaultUVS &defaults = base+defaultUVS; - i = defaults.bsearch (codepoint); - if (i != -1) + if ((base+defaultUVS).bfind (codepoint)) return GLYPH_VARIANT_USE_DEFAULT; - const NonDefaultUVS &nonDefaults = base+nonDefaultUVS; - i = nonDefaults.bsearch (codepoint); - if (i != -1) + const UVSMapping &nonDefault = (base+nonDefaultUVS).bsearch (codepoint); + if (nonDefault.glyphID) { - *glyph = nonDefaults[i].glyphID; + *glyph = nonDefault.glyphID; return GLYPH_VARIANT_FOUND; } return GLYPH_VARIANT_NOT_FOUND; } - inline int cmp (const hb_codepoint_t &variation_selector) const + VariationSelectorRecord(const VariationSelectorRecord& other) { - return varSelector.cmp (variation_selector); + *this = other; } - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + void operator= (const VariationSelectorRecord& other) + { + varSelector = other.varSelector; + HBUINT32 offset = other.defaultUVS; + defaultUVS = offset; + offset = other.nonDefaultUVS; + nonDefaultUVS = offset; + } + + void collect_unicodes (hb_set_t *out, const void *base) const + { + (base+defaultUVS).collect_unicodes (out); + (base+nonDefaultUVS).collect_unicodes (out); + } + + void collect_mapping (const void *base, + hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + (base+defaultUVS).collect_unicodes (unicodes); + (base+nonDefaultUVS).collect_mapping (unicodes, mapping); + } + + int cmp (const hb_codepoint_t &variation_selector) const + { return varSelector.cmp (variation_selector); } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && @@ -379,25 +1155,177 @@ struct VariationSelectorRecord nonDefaultUVS.sanitize (c, base)); } - UINT24 varSelector; /* Variation selector. */ - LOffsetTo - defaultUVS; /* Offset to Default UVS Table. May be 0. */ - LOffsetTo - nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */ + hb_pair_t + copy (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map, + const void *base) const + { + auto snap = c->snapshot (); + auto *out = c->embed (*this); + if (unlikely (!out)) return hb_pair (0, 0); + + out->defaultUVS = 0; + out->nonDefaultUVS = 0; + + unsigned non_default_uvs_objidx = 0; + if (nonDefaultUVS != 0) + { + c->push (); + if (c->copy (base+nonDefaultUVS, unicodes, glyphs_requested, glyph_map)) + non_default_uvs_objidx = c->pop_pack (); + else c->pop_discard (); + } + + unsigned default_uvs_objidx = 0; + if (defaultUVS != 0) + { + c->push (); + if (c->copy (base+defaultUVS, unicodes)) + default_uvs_objidx = c->pop_pack (); + else c->pop_discard (); + } + + + if (!default_uvs_objidx && !non_default_uvs_objidx) + c->revert (snap); + + return hb_pair (default_uvs_objidx, non_default_uvs_objidx); + } + + HBUINT24 varSelector; /* Variation selector. */ + Offset32To + defaultUVS; /* Offset to Default UVS Table. May be 0. */ + Offset32To + nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */ public: DEFINE_SIZE_STATIC (11); }; struct CmapSubtableFormat14 { - inline glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, - hb_codepoint_t variation_selector, - hb_codepoint_t *glyph) const + glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this); } + + void collect_variation_selectors (hb_set_t *out) const { - return record[record.bsearch(variation_selector)].get_glyph (codepoint, glyph, this); + for (const auto& a : record.as_array ()) + out->add (a.varSelector); + } + void collect_variation_unicodes (hb_codepoint_t variation_selector, + hb_set_t *out) const + { record.bsearch (variation_selector).collect_unicodes (out, this); } + + void serialize (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map, + const void *base) + { + auto snap = c->snapshot (); + unsigned table_initpos = c->length (); + const char* init_tail = c->tail; + + if (unlikely (!c->extend_min (this))) return; + this->format = 14; + + auto src_tbl = reinterpret_cast (base); + + /* + * Some versions of OTS require that offsets are in order. Due to the use + * of push()/pop_pack() serializing the variation records in order results + * in the offsets being in reverse order (first record has the largest + * offset). While this is perfectly valid, it will cause some versions of + * OTS to consider this table bad. + * + * So to prevent this issue we serialize the variation records in reverse + * order, so that the offsets are ordered from small to large. Since + * variation records are supposed to be in increasing order of varSelector + * we then have to reverse the order of the written variation selector + * records after everything is finalized. + */ + hb_vector_t> obj_indices; + for (int i = src_tbl->record.len - 1; i >= 0; i--) + { + hb_pair_t result = src_tbl->record[i].copy (c, unicodes, glyphs_requested, glyph_map, base); + if (result.first || result.second) + obj_indices.push (result); + } + + if (c->length () - table_initpos == CmapSubtableFormat14::min_size) + { + c->revert (snap); + return; + } + + if (unlikely (!c->check_success (!obj_indices.in_error ()))) + return; + + int tail_len = init_tail - c->tail; + c->check_assign (this->length, c->length () - table_initpos + tail_len, + HB_SERIALIZE_ERROR_INT_OVERFLOW); + c->check_assign (this->record.len, + (c->length () - table_initpos - CmapSubtableFormat14::min_size) / + VariationSelectorRecord::static_size, + HB_SERIALIZE_ERROR_INT_OVERFLOW); + + /* Correct the incorrect write order by reversing the order of the variation + records array. */ + _reverse_variation_records (); + + /* Now that records are in the right order, we can set up the offsets. */ + _add_links_to_variation_records (c, obj_indices); } - inline bool sanitize (hb_sanitize_context_t *c) const + void _reverse_variation_records () + { + record.as_array ().reverse (); + } + + void _add_links_to_variation_records (hb_serialize_context_t *c, + const hb_vector_t>& obj_indices) + { + for (unsigned i = 0; i < obj_indices.length; i++) + { + /* + * Since the record array has been reversed (see comments in copy()) + * but obj_indices has not been, the indices at obj_indices[i] + * are for the variation record at record[j]. + */ + int j = obj_indices.length - 1 - i; + c->add_link (record[j].defaultUVS, obj_indices[i].first); + c->add_link (record[j].nonDefaultUVS, obj_indices[i].second); + } + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + hb_iter (record) + | hb_filter (hb_bool, &VariationSelectorRecord::nonDefaultUVS) + | hb_map (&VariationSelectorRecord::nonDefaultUVS) + | hb_map (hb_add (this)) + | hb_apply ([=] (const NonDefaultUVS& _) { _.closure_glyphs (unicodes, glyphset); }) + ; + } + + void collect_unicodes (hb_set_t *out) const + { + for (const VariationSelectorRecord& _ : record) + _.collect_unicodes (out, this); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + for (const VariationSelectorRecord& _ : record) + _.collect_mapping (this, unicodes, mapping); + } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && @@ -405,9 +1333,9 @@ struct CmapSubtableFormat14 } protected: - UINT16 format; /* Format number is set to 14. */ - UINT32 lengthZ; /* Byte length of this subtable. */ - SortedArrayOf + HBUINT16 format; /* Format number is set to 14. */ + HBUINT32 length; /* Byte length of this subtable. */ + SortedArray32Of record; /* Variation selector records; sorted * in increasing order of `varSelector'. */ public: @@ -418,22 +1346,81 @@ struct CmapSubtable { /* Note: We intentionally do NOT implement subtable formats 2 and 8. */ - inline bool get_glyph (hb_codepoint_t codepoint, - hb_codepoint_t *glyph) const + bool get_glyph (hb_codepoint_t codepoint, + hb_codepoint_t *glyph) const { switch (u.format) { - case 0: return u.format0 .get_glyph(codepoint, glyph); - case 4: return u.format4 .get_glyph(codepoint, glyph); - case 6: return u.format6 .get_glyph(codepoint, glyph); - case 10: return u.format10.get_glyph(codepoint, glyph); - case 12: return u.format12.get_glyph(codepoint, glyph); - case 13: return u.format13.get_glyph(codepoint, glyph); + case 0: return u.format0 .get_glyph (codepoint, glyph); + case 4: return u.format4 .get_glyph (codepoint, glyph); + case 6: return u.format6 .get_glyph (codepoint, glyph); + case 10: return u.format10.get_glyph (codepoint, glyph); + case 12: return u.format12.get_glyph (codepoint, glyph); + case 13: return u.format13.get_glyph (codepoint, glyph); case 14: default: return false; } } + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const + { + switch (u.format) { + case 0: u.format0 .collect_unicodes (out); return; + case 4: u.format4 .collect_unicodes (out); return; + case 6: u.format6 .collect_unicodes (out); return; + case 10: u.format10.collect_unicodes (out); return; + case 12: u.format12.collect_unicodes (out, num_glyphs); return; + case 13: u.format13.collect_unicodes (out, num_glyphs); return; + case 14: + default: return; + } + } - inline bool sanitize (hb_sanitize_context_t *c) const + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping, /* OUT */ + unsigned num_glyphs = UINT_MAX) const + { + switch (u.format) { + case 0: u.format0 .collect_mapping (unicodes, mapping); return; + case 4: u.format4 .collect_mapping (unicodes, mapping); return; + case 6: u.format6 .collect_mapping (unicodes, mapping); return; + case 10: u.format10.collect_mapping (unicodes, mapping); return; + case 12: u.format12.collect_mapping (unicodes, mapping, num_glyphs); return; + case 13: u.format13.collect_mapping (unicodes, mapping, num_glyphs); return; + case 14: + default: return; + } + } + + unsigned get_language () const + { + switch (u.format) { + case 0: return u.format0 .get_language (); + case 4: return u.format4 .get_language (); + case 6: return u.format6 .get_language (); + case 10: return u.format10.get_language (); + case 12: return u.format12.get_language (); + case 13: return u.format13.get_language (); + case 14: + default: return 0; + } + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it, + unsigned format, + const hb_subset_plan_t *plan, + const void *base) + { + switch (format) { + case 4: return u.format4.serialize (c, it); + case 12: return u.format12.serialize (c, it); + case 14: return u.format14.serialize (c, &plan->unicodes, &plan->glyphs_requested, plan->glyph_map, base); + default: return; + } + } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (!u.format.sanitize (c)) return_trace (false); @@ -451,7 +1438,7 @@ struct CmapSubtable public: union { - UINT16 format; /* Format identifier */ + HBUINT16 format; /* Format identifier */ CmapSubtableFormat0 format0; CmapSubtableFormat4 format4; CmapSubtableFormat6 format6; @@ -467,7 +1454,7 @@ struct CmapSubtable struct EncodingRecord { - inline int cmp (const EncodingRecord &other) const + int cmp (const EncodingRecord &other) const { int ret; ret = platformID.cmp (other.platformID); @@ -477,26 +1464,597 @@ struct EncodingRecord return 0; } - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && subtable.sanitize (c, base)); } - UINT16 platformID; /* Platform ID. */ - UINT16 encodingID; /* Platform-specific encoding ID. */ - LOffsetTo + template + EncodingRecord* copy (hb_serialize_context_t *c, + Iterator it, + unsigned format, + const void *base, + const hb_subset_plan_t *plan, + /* INOUT */ unsigned *objidx) const + { + TRACE_SERIALIZE (this); + auto snap = c->snapshot (); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + out->subtable = 0; + + if (*objidx == 0) + { + CmapSubtable *cmapsubtable = c->push (); + unsigned origin_length = c->length (); + cmapsubtable->serialize (c, it, format, plan, &(base+subtable)); + if (c->length () - origin_length > 0) *objidx = c->pop_pack (); + else c->pop_discard (); + } + + if (*objidx == 0) + { + c->revert (snap); + return_trace (nullptr); + } + + c->add_link (out->subtable, *objidx); + return_trace (out); + } + + HBUINT16 platformID; /* Platform ID. */ + HBUINT16 encodingID; /* Platform-specific encoding ID. */ + Offset32To subtable; /* Byte offset from beginning of table to the subtable for this encoding. */ public: DEFINE_SIZE_STATIC (8); }; +struct cmap; + +struct SubtableUnicodesCache { + + private: + hb_blob_ptr_t base_blob; + const char* base; + hb_hashmap_t> cached_unicodes; + + public: + + static SubtableUnicodesCache* create (hb_blob_ptr_t source_table) + { + SubtableUnicodesCache* cache = + (SubtableUnicodesCache*) hb_malloc (sizeof(SubtableUnicodesCache)); + new (cache) SubtableUnicodesCache (source_table); + return cache; + } + + static void destroy (void* value) { + if (!value) return; + + SubtableUnicodesCache* cache = (SubtableUnicodesCache*) value; + cache->~SubtableUnicodesCache (); + hb_free (cache); + } + + SubtableUnicodesCache(const void* cmap_base) + : base_blob(), + base ((const char*) cmap_base), + cached_unicodes () + {} + + SubtableUnicodesCache(hb_blob_ptr_t base_blob_) + : base_blob(base_blob_), + base ((const char *) base_blob.get()), + cached_unicodes () + {} + + ~SubtableUnicodesCache() + { + base_blob.destroy (); + } + + bool same_base(const void* other) const + { + return other == (const void*) base; + } + + const hb_set_t* set_for (const EncodingRecord* record, + SubtableUnicodesCache& mutable_cache) const + { + if (cached_unicodes.has ((unsigned) ((const char *) record - base))) + return cached_unicodes.get ((unsigned) ((const char *) record - base)); + + return mutable_cache.set_for (record); + } + + const hb_set_t* set_for (const EncodingRecord* record) + { + if (!cached_unicodes.has ((unsigned) ((const char *) record - base))) + { + hb_set_t *s = hb_set_create (); + if (unlikely (s->in_error ())) + return hb_set_get_empty (); + + (base+record->subtable).collect_unicodes (s); + + if (unlikely (!cached_unicodes.set ((unsigned) ((const char *) record - base), hb::unique_ptr {s}))) + return hb_set_get_empty (); + + return s; + } + return cached_unicodes.get ((unsigned) ((const char *) record - base)); + } + +}; + +static inline uint_fast16_t +_hb_symbol_pua_map (unsigned codepoint) +{ + if (codepoint <= 0x00FFu) + { + /* For symbol-encoded OpenType fonts, we duplicate the + * U+F000..F0FF range at U+0000..U+00FF. That's what + * Windows seems to do, and that's hinted about at: + * https://docs.microsoft.com/en-us/typography/opentype/spec/recom + * under "Non-Standard (Symbol) Fonts". */ + return 0xF000u + codepoint; + } + return 0; +} + struct cmap { - static const hb_tag_t tableTag = HB_OT_TAG_cmap; + static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap; - inline bool sanitize (hb_sanitize_context_t *c) const + + static SubtableUnicodesCache* create_filled_cache(hb_blob_ptr_t source_table) { + const cmap* cmap = source_table.get(); + auto it = + + hb_iter (cmap->encodingRecord) + | hb_filter ([&](const EncodingRecord& _) { + return cmap::filter_encoding_records_for_subset (cmap, _); + }) + ; + + SubtableUnicodesCache* cache = SubtableUnicodesCache::create(source_table); + for (const EncodingRecord& _ : it) + cache->set_for(&_); // populate the cache for this encoding record. + + return cache; + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + EncodingRecIter encodingrec_iter, + const void *base, + hb_subset_plan_t *plan, + bool drop_format_4 = false) + { + if (unlikely (!c->extend_min ((*this)))) return false; + this->version = 0; + + unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0; + auto snap = c->snapshot (); + + SubtableUnicodesCache local_unicodes_cache (base); + const SubtableUnicodesCache* unicodes_cache = &local_unicodes_cache; + + if (plan->accelerator && + plan->accelerator->cmap_cache && + plan->accelerator->cmap_cache->same_base (base)) + unicodes_cache = plan->accelerator->cmap_cache; + + for (const EncodingRecord& _ : encodingrec_iter) + { + if (c->in_error ()) + return false; + + unsigned format = (base+_.subtable).u.format; + if (format != 4 && format != 12 && format != 14) continue; + + const hb_set_t* unicodes_set = unicodes_cache->set_for (&_, local_unicodes_cache); + + if (!drop_format_4 && format == 4) + { + c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 4u, base, plan, &format4objidx); + if (c->in_error () && c->only_overflow ()) + { + // cmap4 overflowed, reset and retry serialization without format 4 subtables. + c->revert (snap); + return serialize (c, it, + encodingrec_iter, + base, + plan, + true); + } + } + + else if (format == 12) + { + if (_can_drop (_, + *unicodes_set, + base, + *unicodes_cache, + local_unicodes_cache, + + it | hb_map (hb_first), encodingrec_iter)) + continue; + c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 12u, base, plan, &format12objidx); + } + else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx); + } + c->check_assign(this->encodingRecord.len, + (c->length () - cmap::min_size)/EncodingRecord::static_size, + HB_SERIALIZE_ERROR_INT_OVERFLOW); + + // Fail if format 4 was dropped and there is no cmap12. + return !drop_format_4 || format12objidx; + } + + template + bool _can_drop (const EncodingRecord& cmap12, + const hb_set_t& cmap12_unicodes, + const void* base, + const SubtableUnicodesCache& unicodes_cache, + SubtableUnicodesCache& local_unicodes_cache, + Iterator subset_unicodes, + EncodingRecordIterator encoding_records) + { + for (auto cp : + subset_unicodes | hb_filter (cmap12_unicodes)) + { + if (cp >= 0x10000) return false; + } + + unsigned target_platform; + unsigned target_encoding; + unsigned target_language = (base+cmap12.subtable).get_language (); + + if (cmap12.platformID == 0 && cmap12.encodingID == 4) + { + target_platform = 0; + target_encoding = 3; + } else if (cmap12.platformID == 3 && cmap12.encodingID == 10) { + target_platform = 3; + target_encoding = 1; + } else { + return false; + } + + for (const auto& _ : encoding_records) + { + if (_.platformID != target_platform + || _.encodingID != target_encoding + || (base+_.subtable).get_language() != target_language) + continue; + + const hb_set_t* sibling_unicodes = unicodes_cache.set_for (&_, local_unicodes_cache); + + auto cmap12 = + subset_unicodes | hb_filter (cmap12_unicodes); + auto sibling = + subset_unicodes | hb_filter (*sibling_unicodes); + for (; cmap12 && sibling; cmap12++, sibling++) + { + unsigned a = *cmap12; + unsigned b = *sibling; + if (a != b) return false; + } + + return !cmap12 && !sibling; + } + + return false; + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + hb_iter (encodingRecord) + | hb_map (&EncodingRecord::subtable) + | hb_map (hb_add (this)) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == 14; }) + | hb_apply ([=] (const CmapSubtable& _) { _.u.format14.closure_glyphs (unicodes, glyphset); }) + ; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + cmap *cmap_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (cmap_prime))) return_trace (false); + + auto encodingrec_iter = + + hb_iter (encodingRecord) + | hb_filter ([&](const EncodingRecord& _) { + return cmap::filter_encoding_records_for_subset (this, _); + }) + ; + + if (unlikely (!encodingrec_iter.len ())) return_trace (false); + + const EncodingRecord *unicode_bmp= nullptr, *unicode_ucs4 = nullptr, *ms_bmp = nullptr, *ms_ucs4 = nullptr; + bool has_format12 = false; + + for (const EncodingRecord& _ : encodingrec_iter) + { + unsigned format = (this + _.subtable).u.format; + if (format == 12) has_format12 = true; + + const EncodingRecord *table = std::addressof (_); + if (_.platformID == 0 && _.encodingID == 3) unicode_bmp = table; + else if (_.platformID == 0 && _.encodingID == 4) unicode_ucs4 = table; + else if (_.platformID == 3 && _.encodingID == 1) ms_bmp = table; + else if (_.platformID == 3 && _.encodingID == 10) ms_ucs4 = table; + } + + if (unlikely (!has_format12 && !unicode_bmp && !ms_bmp)) return_trace (false); + if (unlikely (has_format12 && (!unicode_ucs4 && !ms_ucs4))) return_trace (false); + + auto it = + + c->plan->unicode_to_new_gid_list.iter () + | hb_filter ([&] (const hb_pair_t _) + { return (_.second != HB_MAP_VALUE_INVALID); }) + ; + + return_trace (cmap_prime->serialize (c->serializer, + it, + encodingrec_iter, + this, + c->plan)); + } + + const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const + { + if (symbol) *symbol = false; + + const CmapSubtable *subtable; + + /* Symbol subtable. + * Prefer symbol if available. + * https://github.com/harfbuzz/harfbuzz/issues/1918 */ + if ((subtable = this->find_subtable (3, 0))) + { + if (symbol) *symbol = true; + return subtable; + } + + /* 32-bit subtables. */ + if ((subtable = this->find_subtable (3, 10))) return subtable; + if ((subtable = this->find_subtable (0, 6))) return subtable; + if ((subtable = this->find_subtable (0, 4))) return subtable; + + /* 16-bit subtables. */ + if ((subtable = this->find_subtable (3, 1))) return subtable; + if ((subtable = this->find_subtable (0, 3))) return subtable; + if ((subtable = this->find_subtable (0, 2))) return subtable; + if ((subtable = this->find_subtable (0, 1))) return subtable; + if ((subtable = this->find_subtable (0, 0))) return subtable; + + /* Meh. */ + return &Null (CmapSubtable); + } + + struct accelerator_t + { + using cache_t = hb_cache_t<21, 16, 8, true>; + + accelerator_t (hb_face_t *face) + { + this->table = hb_sanitize_context_t ().reference_table (face); + bool symbol; + this->subtable = table->find_best_subtable (&symbol); + this->subtable_uvs = &Null (CmapSubtableFormat14); + { + const CmapSubtable *st = table->find_subtable (0, 5); + if (st && st->u.format == 14) + subtable_uvs = &st->u.format14; + } + + this->get_glyph_data = subtable; + if (unlikely (symbol)) + { + switch ((unsigned) face->table.OS2->get_font_page ()) { + case OS2::font_page_t::FONT_PAGE_NONE: + this->get_glyph_funcZ = get_glyph_from_symbol; + break; +#ifndef HB_NO_OT_SHAPER_ARABIC_FALLBACK + case OS2::font_page_t::FONT_PAGE_SIMP_ARABIC: + this->get_glyph_funcZ = get_glyph_from_symbol; + break; + case OS2::font_page_t::FONT_PAGE_TRAD_ARABIC: + this->get_glyph_funcZ = get_glyph_from_symbol; + break; +#endif + default: + this->get_glyph_funcZ = get_glyph_from; + break; + } + } + else + { + switch (subtable->u.format) { + /* Accelerate format 4 and format 12. */ + default: + this->get_glyph_funcZ = get_glyph_from; + break; + case 12: + this->get_glyph_funcZ = get_glyph_from; + break; + case 4: + { + this->format4_accel.init (&subtable->u.format4); + this->get_glyph_data = &this->format4_accel; + this->get_glyph_funcZ = this->format4_accel.get_glyph_func; + break; + } + } + } + } + ~accelerator_t () { this->table.destroy (); } + + inline bool _cached_get (hb_codepoint_t unicode, + hb_codepoint_t *glyph, + cache_t *cache) const + { + unsigned v; + if (cache && cache->get (unicode, &v)) + { + *glyph = v; + return true; + } + bool ret = this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph); + + if (cache && ret) + cache->set (unicode, *glyph); + return ret; + } + + bool get_nominal_glyph (hb_codepoint_t unicode, + hb_codepoint_t *glyph, + cache_t *cache = nullptr) const + { + if (unlikely (!this->get_glyph_funcZ)) return 0; + return _cached_get (unicode, glyph, cache); + } + + unsigned int get_nominal_glyphs (unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + cache_t *cache = nullptr) const + { + if (unlikely (!this->get_glyph_funcZ)) return 0; + + unsigned int done; + for (done = 0; + done < count && _cached_get (*first_unicode, first_glyph, cache); + done++) + { + first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + } + return done; + } + + bool get_variation_glyph (hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + cache_t *cache = nullptr) const + { + switch (this->subtable_uvs->get_glyph_variant (unicode, + variation_selector, + glyph)) + { + case GLYPH_VARIANT_NOT_FOUND: return false; + case GLYPH_VARIANT_FOUND: return true; + case GLYPH_VARIANT_USE_DEFAULT: break; + } + + return get_nominal_glyph (unicode, glyph, cache); + } + + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const + { subtable->collect_unicodes (out, num_glyphs); } + void collect_mapping (hb_set_t *unicodes, hb_map_t *mapping, + unsigned num_glyphs = UINT_MAX) const + { subtable->collect_mapping (unicodes, mapping, num_glyphs); } + void collect_variation_selectors (hb_set_t *out) const + { subtable_uvs->collect_variation_selectors (out); } + void collect_variation_unicodes (hb_codepoint_t variation_selector, + hb_set_t *out) const + { subtable_uvs->collect_variation_unicodes (variation_selector, out); } + + protected: + typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph); + typedef uint_fast16_t (*hb_pua_remap_func_t) (unsigned); + + template + HB_INTERNAL static bool get_glyph_from (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) + { + const Type *typed_obj = (const Type *) obj; + return typed_obj->get_glyph (codepoint, glyph); + } + + template + HB_INTERNAL static bool get_glyph_from_symbol (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) + { + const Type *typed_obj = (const Type *) obj; + if (likely (typed_obj->get_glyph (codepoint, glyph))) + return true; + + if (hb_codepoint_t c = remap (codepoint)) + return typed_obj->get_glyph (c, glyph); + + return false; + } + + private: + hb_nonnull_ptr_t subtable; + hb_nonnull_ptr_t subtable_uvs; + + hb_cmap_get_glyph_func_t get_glyph_funcZ; + const void *get_glyph_data; + + CmapSubtableFormat4::accelerator_t format4_accel; + + public: + hb_blob_ptr_t table; + }; + + protected: + + const CmapSubtable *find_subtable (unsigned int platform_id, + unsigned int encoding_id) const + { + EncodingRecord key; + key.platformID = platform_id; + key.encodingID = encoding_id; + + const EncodingRecord &result = encodingRecord.bsearch (key); + if (!result.subtable) + return nullptr; + + return &(this+result.subtable); + } + + const EncodingRecord *find_encodingrec (unsigned int platform_id, + unsigned int encoding_id) const + { + EncodingRecord key; + key.platformID = platform_id; + key.encodingID = encoding_id; + + return encodingRecord.as_array ().bsearch (key); + } + + bool find_subtable (unsigned format) const + { + auto it = + + hb_iter (encodingRecord) + | hb_map (&EncodingRecord::subtable) + | hb_map (hb_add (this)) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == format; }) + ; + + return it.len (); + } + + public: + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && @@ -504,163 +2062,30 @@ struct cmap encodingRecord.sanitize (c, this)); } - struct accelerator_t + private: + + static bool filter_encoding_records_for_subset(const cmap* cmap, + const EncodingRecord& _) { - inline void init (hb_face_t *face) - { - this->blob = OT::Sanitizer::sanitize (face->reference_table (HB_OT_TAG_cmap)); - const OT::cmap *cmap = OT::Sanitizer::lock_instance (this->blob); - const OT::CmapSubtable *subtable = nullptr; - const OT::CmapSubtableFormat14 *subtable_uvs = nullptr; - - bool symbol = false; - /* 32-bit subtables. */ - if (!subtable) subtable = cmap->find_subtable (3, 10); - if (!subtable) subtable = cmap->find_subtable (0, 6); - if (!subtable) subtable = cmap->find_subtable (0, 4); - /* 16-bit subtables. */ - if (!subtable) subtable = cmap->find_subtable (3, 1); - if (!subtable) subtable = cmap->find_subtable (0, 3); - if (!subtable) subtable = cmap->find_subtable (0, 2); - if (!subtable) subtable = cmap->find_subtable (0, 1); - if (!subtable) subtable = cmap->find_subtable (0, 0); - if (!subtable) - { - subtable = cmap->find_subtable (3, 0); - if (subtable) symbol = true; - } - /* Meh. */ - if (!subtable) subtable = &OT::Null(OT::CmapSubtable); - - /* UVS subtable. */ - if (!subtable_uvs) - { - const OT::CmapSubtable *st = cmap->find_subtable (0, 5); - if (st && st->u.format == 14) - subtable_uvs = &st->u.format14; - } - /* Meh. */ - if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtableFormat14); - - this->uvs_table = subtable_uvs; - - this->get_glyph_data = subtable; - if (unlikely (symbol)) - this->get_glyph_func = get_glyph_from_symbol; - else - switch (subtable->u.format) { - /* Accelerate format 4 and format 12. */ - default: this->get_glyph_func = get_glyph_from; break; - case 12: this->get_glyph_func = get_glyph_from; break; - case 4: - { - this->format4_accel.init (&subtable->u.format4); - this->get_glyph_data = &this->format4_accel; - this->get_glyph_func = this->format4_accel.get_glyph_func; - } - break; - } - } - - inline void fini (void) - { - hb_blob_destroy (this->blob); - } - - inline bool get_nominal_glyph (hb_codepoint_t unicode, - hb_codepoint_t *glyph) const - { - return this->get_glyph_func (this->get_glyph_data, unicode, glyph); - } - - inline bool get_variation_glyph (hb_codepoint_t unicode, - hb_codepoint_t variation_selector, - hb_codepoint_t *glyph) const - { - switch (this->uvs_table->get_glyph_variant (unicode, - variation_selector, - glyph)) - { - case OT::GLYPH_VARIANT_NOT_FOUND: return false; - case OT::GLYPH_VARIANT_FOUND: return true; - case OT::GLYPH_VARIANT_USE_DEFAULT: break; - } - - return get_nominal_glyph (unicode, glyph); - } - - protected: - typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, - hb_codepoint_t codepoint, - hb_codepoint_t *glyph); - - template - static inline bool get_glyph_from (const void *obj, - hb_codepoint_t codepoint, - hb_codepoint_t *glyph) - { - const Type *typed_obj = (const Type *) obj; - return typed_obj->get_glyph (codepoint, glyph); - } - - template - static inline bool get_glyph_from_symbol (const void *obj, - hb_codepoint_t codepoint, - hb_codepoint_t *glyph) - { - const Type *typed_obj = (const Type *) obj; - if (likely (typed_obj->get_glyph (codepoint, glyph))) - return true; - - if (codepoint <= 0x00FFu) - { - /* For symbol-encoded OpenType fonts, we duplicate the - * U+F000..F0FF range at U+0000..U+00FF. That's what - * Windows seems to do, and that's hinted about at: - * http://www.microsoft.com/typography/otspec/recom.htm - * under "Non-Standard (Symbol) Fonts". */ - return typed_obj->get_glyph (0xF000u + codepoint, glyph); - } - - return false; - } - - private: - hb_cmap_get_glyph_func_t get_glyph_func; - const void *get_glyph_data; - OT::CmapSubtableFormat4::accelerator_t format4_accel; - - const OT::CmapSubtableFormat14 *uvs_table; - hb_blob_t *blob; - }; - - protected: - - inline const CmapSubtable *find_subtable (unsigned int platform_id, - unsigned int encoding_id) const - { - EncodingRecord key; - key.platformID.set (platform_id); - key.encodingID.set (encoding_id); - - /* Note: We can use bsearch, but since it has no performance - * implications, we use lsearch and as such accept fonts with - * unsorted subtable list. */ - int result = encodingRecord./*bsearch*/lsearch (key); - if (result == -1 || !encodingRecord[result].subtable) - return nullptr; - - return &(this+encodingRecord[result].subtable); + return + (_.platformID == 0 && _.encodingID == 3) || + (_.platformID == 0 && _.encodingID == 4) || + (_.platformID == 3 && _.encodingID == 1) || + (_.platformID == 3 && _.encodingID == 10) || + (cmap + _.subtable).u.format == 14; } protected: - UINT16 version; /* Table version number (0). */ - SortedArrayOf - encodingRecord; /* Encoding tables. */ + HBUINT16 version; /* Table version number (0). */ + SortedArray16Of + encodingRecord; /* Encoding tables. */ public: DEFINE_SIZE_ARRAY (4, encodingRecord); }; +struct cmap_accelerator_t : cmap::accelerator_t { + cmap_accelerator_t (hb_face_t *face) : cmap::accelerator_t (face) {} +}; } /* namespace OT */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-color.cc b/src/3rdparty/harfbuzz-ng/src/hb-ot-color.cc new file mode 100644 index 00000000000..37d42e08d94 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-color.cc @@ -0,0 +1,363 @@ +/* + * Copyright ยฉ 2016 Google, Inc. + * Copyright ยฉ 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Sascha Brawer, Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_COLOR + +#include "hb-ot.h" + +#include "OT/Color/CBDT/CBDT.hh" +#include "OT/Color/COLR/COLR.hh" +#include "OT/Color/CPAL/CPAL.hh" +#include "OT/Color/sbix/sbix.hh" +#include "OT/Color/svg/svg.hh" + + +/** + * SECTION:hb-ot-color + * @title: hb-ot-color + * @short_description: OpenType Color Fonts + * @include: hb-ot.h + * + * Functions for fetching color-font information from OpenType font faces. + * + * HarfBuzz supports `COLR`/`CPAL`, `sbix`, `CBDT`, and `SVG` color fonts. + **/ + + +/* + * CPAL + */ + + +/** + * hb_ot_color_has_palettes: + * @face: #hb_face_t to work upon + * + * Tests whether a face includes a `CPAL` color-palette table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_palettes (hb_face_t *face) +{ + return face->table.CPAL->has_data (); +} + +/** + * hb_ot_color_palette_get_count: + * @face: #hb_face_t to work upon + * + * Fetches the number of color palettes in a face. + * + * Return value: the number of palettes found + * + * Since: 2.1.0 + */ +unsigned int +hb_ot_color_palette_get_count (hb_face_t *face) +{ + return face->table.CPAL->get_palette_count (); +} + +/** + * hb_ot_color_palette_get_name_id: + * @face: #hb_face_t to work upon + * @palette_index: The index of the color palette + * + * Fetches the `name` table Name ID that provides display names for + * a `CPAL` color palette. + * + * Palette display names can be generic (e.g., "Default") or provide + * specific, themed names (e.g., "Spring", "Summer", "Fall", and "Winter"). + * + * Return value: the Named ID found for the palette. + * If the requested palette has no name the result is #HB_OT_NAME_ID_INVALID. + * + * Since: 2.1.0 + */ +hb_ot_name_id_t +hb_ot_color_palette_get_name_id (hb_face_t *face, + unsigned int palette_index) +{ + return face->table.CPAL->get_palette_name_id (palette_index); +} + +/** + * hb_ot_color_palette_color_get_name_id: + * @face: #hb_face_t to work upon + * @color_index: The index of the color + * + * Fetches the `name` table Name ID that provides display names for + * the specified color in a face's `CPAL` color palette. + * + * Display names can be generic (e.g., "Background") or specific + * (e.g., "Eye color"). + * + * Return value: the Name ID found for the color. + * + * Since: 2.1.0 + */ +hb_ot_name_id_t +hb_ot_color_palette_color_get_name_id (hb_face_t *face, + unsigned int color_index) +{ + return face->table.CPAL->get_color_name_id (color_index); +} + +/** + * hb_ot_color_palette_get_flags: + * @face: #hb_face_t to work upon + * @palette_index: The index of the color palette + * + * Fetches the flags defined for a color palette. + * + * Return value: the #hb_ot_color_palette_flags_t of the requested color palette + * + * Since: 2.1.0 + */ +hb_ot_color_palette_flags_t +hb_ot_color_palette_get_flags (hb_face_t *face, + unsigned int palette_index) +{ + return face->table.CPAL->get_palette_flags (palette_index); +} + +/** + * hb_ot_color_palette_get_colors: + * @face: #hb_face_t to work upon + * @palette_index: the index of the color palette to query + * @start_offset: offset of the first color to retrieve + * @color_count: (inout) (optional): Input = the maximum number of colors to return; + * Output = the actual number of colors returned (may be zero) + * @colors: (out) (array length=color_count) (nullable): The array of #hb_color_t records found + * + * Fetches a list of the colors in a color palette. + * + * After calling this function, @colors will be filled with the palette + * colors. If @colors is NULL, the function will just return the number + * of total colors without storing any actual colors; this can be used + * for allocating a buffer of suitable size before calling + * hb_ot_color_palette_get_colors() a second time. + * + * The RGBA values in the palette are unpremultiplied. See the + * OpenType spec [CPAL](https://learn.microsoft.com/en-us/typography/opentype/spec/cpal) + * section for details. + * + * Return value: the total number of colors in the palette + * + * Since: 2.1.0 + */ +unsigned int +hb_ot_color_palette_get_colors (hb_face_t *face, + unsigned int palette_index, + unsigned int start_offset, + unsigned int *colors_count /* IN/OUT. May be NULL. */, + hb_color_t *colors /* OUT. May be NULL. */) +{ + return face->table.CPAL->get_palette_colors (palette_index, start_offset, colors_count, colors); +} + + +/* + * COLR + */ + +/** + * hb_ot_color_has_layers: + * @face: #hb_face_t to work upon + * + * Tests whether a face includes a `COLR` table + * with data according to COLRv0. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_layers (hb_face_t *face) +{ + return face->table.COLR->has_v0_data (); +} + +/** + * hb_ot_color_has_paint: + * @face: #hb_face_t to work upon + * + * Tests where a face includes a `COLR` table + * with data according to COLRv1. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 7.0.0 + */ +hb_bool_t +hb_ot_color_has_paint (hb_face_t *face) +{ + return face->table.COLR->has_v1_data (); +} + +/** + * hb_ot_color_glyph_has_paint: + * @face: #hb_face_t to work upon + * @glyph: The glyph index to query + * + * Tests where a face includes COLRv1 paint + * data for @glyph. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 7.0.0 + */ +hb_bool_t +hb_ot_color_glyph_has_paint (hb_face_t *face, + hb_codepoint_t glyph) +{ + return face->table.COLR->has_paint_for_glyph (glyph); +} + +/** + * hb_ot_color_glyph_get_layers: + * @face: #hb_face_t to work upon + * @glyph: The glyph index to query + * @start_offset: offset of the first layer to retrieve + * @layer_count: (inout) (optional): Input = the maximum number of layers to return; + * Output = the actual number of layers returned (may be zero) + * @layers: (out) (array length=layer_count) (nullable): The array of layers found + * + * Fetches a list of all color layers for the specified glyph index in the specified + * face. The list returned will begin at the offset provided. + * + * Return value: Total number of layers available for the glyph index queried + * + * Since: 2.1.0 + */ +unsigned int +hb_ot_color_glyph_get_layers (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *layer_count, /* IN/OUT. May be NULL. */ + hb_ot_color_layer_t *layers /* OUT. May be NULL. */) +{ + return face->table.COLR->get_glyph_layers (glyph, start_offset, layer_count, layers); +} + + +/* + * SVG + */ + +/** + * hb_ot_color_has_svg: + * @face: #hb_face_t to work upon. + * + * Tests whether a face includes any `SVG` glyph images. + * + * Return value: `true` if data found, `false` otherwise. + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_svg (hb_face_t *face) +{ + return face->table.SVG->has_data (); +} + +/** + * hb_ot_color_glyph_reference_svg: + * @face: #hb_face_t to work upon + * @glyph: a svg glyph index + * + * Fetches the SVG document for a glyph. The blob may be either plain text or gzip-encoded. + * + * If the glyph has no SVG document, the singleton empty blob is returned. + * + * Return value: (transfer full): An #hb_blob_t containing the SVG document of the glyph, if available + * + * Since: 2.1.0 + */ +hb_blob_t * +hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph) +{ + return face->table.SVG->reference_blob_for_glyph (glyph); +} + + +/* + * PNG: CBDT or sbix + */ + +/** + * hb_ot_color_has_png: + * @face: #hb_face_t to work upon + * + * Tests whether a face has PNG glyph images (either in `CBDT` or `sbix` tables). + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_png (hb_face_t *face) +{ + return face->table.CBDT->has_data () || face->table.sbix->has_data (); +} + +/** + * hb_ot_color_glyph_reference_png: + * @font: #hb_font_t to work upon + * @glyph: a glyph index + * + * Fetches the PNG image for a glyph. This function takes a font object, not a face object, + * as input. To get an optimally sized PNG blob, the PPEM values must be set on the @font + * object. If PPEM is unset, the blob returned will be the largest PNG available. + * + * If the glyph has no PNG image, the singleton empty blob is returned. + * + * Return value: (transfer full): An #hb_blob_t containing the PNG image for the glyph, if available + * + * Since: 2.1.0 + */ +hb_blob_t * +hb_ot_color_glyph_reference_png (hb_font_t *font, hb_codepoint_t glyph) +{ + hb_blob_t *blob = hb_blob_get_empty (); + + if (font->face->table.sbix->has_data ()) + blob = font->face->table.sbix->reference_png (font, glyph, nullptr, nullptr, nullptr); + + if (!blob->length && font->face->table.CBDT->has_data ()) + blob = font->face->table.CBDT->reference_png (font, glyph); + + return blob; +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-color.h b/src/3rdparty/harfbuzz-ng/src/hb-ot-color.h new file mode 100644 index 00000000000..22ee497e388 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-color.h @@ -0,0 +1,155 @@ +/* + * Copyright ยฉ 2016 Google, Inc. + * Copyright ยฉ 2018 Khaled Hosny + * Copyright ยฉ 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Sascha Brawer, Behdad Esfahbod + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_OT_COLOR_H +#define HB_OT_COLOR_H + +#include "hb.h" +#include "hb-ot-name.h" + +HB_BEGIN_DECLS + + +/* + * Color palettes. + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_palettes (hb_face_t *face); + +HB_EXTERN unsigned int +hb_ot_color_palette_get_count (hb_face_t *face); + +HB_EXTERN hb_ot_name_id_t +hb_ot_color_palette_get_name_id (hb_face_t *face, + unsigned int palette_index); + +HB_EXTERN hb_ot_name_id_t +hb_ot_color_palette_color_get_name_id (hb_face_t *face, + unsigned int color_index); + +/** + * hb_ot_color_palette_flags_t: + * @HB_OT_COLOR_PALETTE_FLAG_DEFAULT: Default indicating that there is nothing special + * to note about a color palette. + * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND: Flag indicating that the color + * palette is appropriate to use when displaying the font on a light background such as white. + * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND: Flag indicating that the color + * palette is appropriate to use when displaying the font on a dark background such as black. + * + * Flags that describe the properties of color palette. + * + * Since: 2.1.0 + */ +typedef enum { /*< flags >*/ + HB_OT_COLOR_PALETTE_FLAG_DEFAULT = 0x00000000u, + HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND = 0x00000001u, + HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND = 0x00000002u +} hb_ot_color_palette_flags_t; + +HB_EXTERN hb_ot_color_palette_flags_t +hb_ot_color_palette_get_flags (hb_face_t *face, + unsigned int palette_index); + +HB_EXTERN unsigned int +hb_ot_color_palette_get_colors (hb_face_t *face, + unsigned int palette_index, + unsigned int start_offset, + unsigned int *color_count, /* IN/OUT. May be NULL. */ + hb_color_t *colors /* OUT. May be NULL. */); + + +/* + * Color layers. + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_layers (hb_face_t *face); + +/** + * hb_ot_color_layer_t: + * @glyph: the glyph ID of the layer + * @color_index: the palette color index of the layer + * + * Pairs of glyph and color index. + * + * A color index of 0xFFFF does not refer to a palette + * color, but indicates that the foreground color should + * be used. + * + * Since: 2.1.0 + **/ +typedef struct hb_ot_color_layer_t { + hb_codepoint_t glyph; + unsigned int color_index; +} hb_ot_color_layer_t; + +HB_EXTERN unsigned int +hb_ot_color_glyph_get_layers (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *layer_count, /* IN/OUT. May be NULL. */ + hb_ot_color_layer_t *layers /* OUT. May be NULL. */); + +/* COLRv1 */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_paint (hb_face_t *face); + +HB_EXTERN hb_bool_t +hb_ot_color_glyph_has_paint (hb_face_t *face, + hb_codepoint_t glyph); + +/* + * SVG + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_svg (hb_face_t *face); + +HB_EXTERN hb_blob_t * +hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph); + +/* + * PNG: CBDT or sbix + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_png (hb_face_t *face); + +HB_EXTERN hb_blob_t * +hb_ot_color_glyph_reference_png (hb_font_t *font, hb_codepoint_t glyph); + + +HB_END_DECLS + +#endif /* HB_OT_COLOR_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-deprecated.h b/src/3rdparty/harfbuzz-ng/src/hb-ot-deprecated.h new file mode 100644 index 00000000000..60672ab1283 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-deprecated.h @@ -0,0 +1,147 @@ +/* + * Copyright ยฉ 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_OT_DEPRECATED_H +#define HB_OT_DEPRECATED_H + +#include "hb.h" +#include "hb-ot-name.h" + + +HB_BEGIN_DECLS + +#ifndef HB_DISABLE_DEPRECATED + + +/* https://github.com/harfbuzz/harfbuzz/issues/1734 */ +/** + * HB_MATH_GLYPH_PART_FLAG_EXTENDER: + * + * Use #HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER instead. + * + * Deprecated: 2.5.1 + */ +#define HB_MATH_GLYPH_PART_FLAG_EXTENDER HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER + +/* https://github.com/harfbuzz/harfbuzz/pull/3417 */ +/** + * HB_OT_MATH_SCRIPT: + * + * Use #HB_SCRIPT_MATH or #HB_OT_TAG_MATH_SCRIPT instead. + * + * Previous versions of this documentation recommended passing + * #HB_OT_MATH_SCRIPT to hb_buffer_set_script() to enable math shaping, but this + * usage is no longer supported. Use #HB_SCRIPT_MATH instead. + * + * Since: 1.3.3 + * Deprecated: 3.4.0 + */ +#define HB_OT_MATH_SCRIPT HB_OT_TAG_MATH_SCRIPT + + +/* Like hb_ot_layout_table_find_script, but takes zero-terminated array of scripts to test */ +HB_DEPRECATED_FOR (hb_ot_layout_table_select_script) +HB_EXTERN hb_bool_t +hb_ot_layout_table_choose_script (hb_face_t *face, + hb_tag_t table_tag, + const hb_tag_t *script_tags, + unsigned int *script_index, + hb_tag_t *chosen_script); + +HB_DEPRECATED_FOR (hb_ot_layout_script_select_language) +HB_EXTERN hb_bool_t +hb_ot_layout_script_find_language (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + hb_tag_t language_tag, + unsigned int *language_index); + +HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) +HB_EXTERN void +hb_ot_tags_from_script (hb_script_t script, + hb_tag_t *script_tag_1, + hb_tag_t *script_tag_2); + +HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) +HB_EXTERN hb_tag_t +hb_ot_tag_from_language (hb_language_t language); + + +/** + * HB_OT_VAR_NO_AXIS_INDEX: + * + * Do not use. + * + * Since: 1.4.2 + * Deprecated: 2.2.0 + */ +#define HB_OT_VAR_NO_AXIS_INDEX 0xFFFFFFFFu + +/** + * hb_ot_var_axis_t: + * @tag: axis tag + * @name_id: axis name identifier + * @min_value: minimum value of the axis + * @default_value: default value of the axis + * @max_value: maximum value of the axis + * + * Use #hb_ot_var_axis_info_t instead. + * + * Since: 1.4.2 + * Deprecated: 2.2.0 + */ +typedef struct hb_ot_var_axis_t { + hb_tag_t tag; + hb_ot_name_id_t name_id; + float min_value; + float default_value; + float max_value; +} hb_ot_var_axis_t; + +HB_DEPRECATED_FOR (hb_ot_var_get_axis_infos) +HB_EXTERN unsigned int +hb_ot_var_get_axes (hb_face_t *face, + unsigned int start_offset, + unsigned int *axes_count /* IN/OUT */, + hb_ot_var_axis_t *axes_array /* OUT */); + +HB_DEPRECATED_FOR (hb_ot_var_find_axis_info) +HB_EXTERN hb_bool_t +hb_ot_var_find_axis (hb_face_t *face, + hb_tag_t axis_tag, + unsigned int *axis_index, + hb_ot_var_axis_t *axis_info); + + +#endif + +HB_END_DECLS + +#endif /* HB_OT_DEPRECATED_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-face-table-list.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-face-table-list.hh new file mode 100644 index 00000000000..b552dfdd9da --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-face-table-list.hh @@ -0,0 +1,152 @@ +/* + * Copyright ยฉ 2007,2008,2009 Red Hat, Inc. + * Copyright ยฉ 2012,2013 Google, Inc. + * Copyright ยฉ 2019, Facebook Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_FACE_TABLE_LIST_HH +#define HB_OT_FACE_TABLE_LIST_HH +#endif /* HB_OT_FACE_TABLE_LIST_HH */ /* Dummy header guards */ + +#ifndef HB_OT_CORE_TABLE +#define HB_OT_CORE_TABLE(Namespace, Type) HB_OT_TABLE (Namespace, Type) +#define _HB_OT_CORE_TABLE_UNDEF +#endif + +#ifndef HB_OT_ACCELERATOR +#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type) +#define _HB_OT_ACCELERATOR_UNDEF +#endif + + +/* This lists font tables that the hb_face_t will contain and lazily + * load. Don't add a table unless it's used though. This is not + * exactly zero-cost. */ + +/* v--- Add new tables in the right place here. */ + + +/* OpenType fundamentals. */ +HB_OT_CORE_TABLE (OT, head) +HB_OT_CORE_TABLE (OT, maxp) +#if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT) +HB_OT_ACCELERATOR (OT, cmap) +#endif +HB_OT_CORE_TABLE (OT, hhea) +HB_OT_ACCELERATOR (OT, hmtx) +HB_OT_CORE_TABLE (OT, OS2) +#if !defined(HB_NO_OT_FONT_GLYPH_NAMES) || !defined(HB_NO_METRICS) || !defined(HB_NO_STYLE) +HB_OT_ACCELERATOR (OT, post) +#endif +#ifndef HB_NO_NAME +HB_OT_ACCELERATOR (OT, name) +#endif +#ifndef HB_NO_STYLE +HB_OT_CORE_TABLE (OT, STAT) +#endif +#ifndef HB_NO_META +HB_OT_ACCELERATOR (OT, meta) +#endif + +/* Vertical layout. */ +#ifndef HB_NO_VERTICAL +HB_OT_CORE_TABLE (OT, vhea) +HB_OT_ACCELERATOR (OT, vmtx) +HB_OT_CORE_TABLE (OT, VORG) +#endif + +/* TrueType outlines. */ +HB_OT_CORE_TABLE (OT, loca) // Also used to determine number of glyphs +HB_OT_ACCELERATOR (OT, glyf) + +/* CFF outlines. */ +#ifndef HB_NO_CFF +HB_OT_ACCELERATOR (OT, cff1) +HB_OT_ACCELERATOR (OT, cff2) +#endif + +/* OpenType variations. */ +#ifndef HB_NO_VAR +HB_OT_CORE_TABLE (OT, fvar) +HB_OT_CORE_TABLE (OT, avar) +HB_OT_CORE_TABLE (OT, cvar) +HB_OT_ACCELERATOR (OT, gvar) +HB_OT_CORE_TABLE (OT, MVAR) +#endif + +/* Legacy kern. */ +#ifndef HB_NO_OT_KERN +HB_OT_CORE_TABLE (OT, kern) +#endif + +/* OpenType shaping. */ +#ifndef HB_NO_OT_LAYOUT +HB_OT_ACCELERATOR (OT, GDEF) +HB_OT_ACCELERATOR (OT, GSUB) +HB_OT_ACCELERATOR (OT, GPOS) +//HB_OT_CORE_TABLE (OT, JSTF) +#endif + +/* OpenType baseline. */ +#ifndef HB_NO_BASE +HB_OT_CORE_TABLE (OT, BASE) +#endif + +/* AAT shaping. */ +#ifndef HB_NO_AAT +HB_OT_TABLE (AAT, morx) +HB_OT_TABLE (AAT, mort) +HB_OT_TABLE (AAT, kerx) +HB_OT_TABLE (AAT, ankr) +HB_OT_TABLE (AAT, trak) +HB_OT_TABLE (AAT, ltag) +HB_OT_TABLE (AAT, feat) +// HB_OT_TABLE (AAT, opbd) +#endif + +/* OpenType color fonts. */ +#ifndef HB_NO_COLOR +HB_OT_CORE_TABLE (OT, COLR) +HB_OT_CORE_TABLE (OT, CPAL) +HB_OT_ACCELERATOR (OT, CBDT) +HB_OT_ACCELERATOR (OT, sbix) +HB_OT_ACCELERATOR (OT, SVG) +#endif + +/* OpenType math. */ +#ifndef HB_NO_MATH +HB_OT_CORE_TABLE (OT, MATH) +#endif + + +#ifdef _HB_OT_ACCELERATOR_UNDEF +#undef HB_OT_ACCELERATOR +#endif + +#ifdef _HB_OT_CORE_TABLE_UNDEF +#undef HB_OT_CORE_TABLE +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-face.cc b/src/3rdparty/harfbuzz-ng/src/hb-ot-face.cc new file mode 100644 index 00000000000..2243ee02874 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-face.cc @@ -0,0 +1,58 @@ +/* + * Copyright ยฉ 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-ot-face.hh" + +#include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-kern-table.hh" +#include "hb-ot-meta-table.hh" +#include "hb-ot-name-table.hh" +#include "hb-ot-post-table.hh" +#include "OT/Color/CBDT/CBDT.hh" +#include "OT/Color/sbix/sbix.hh" +#include "OT/Color/svg/svg.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gsub-table.hh" +#include "hb-ot-layout-gpos-table.hh" + + +void hb_ot_face_t::init0 (hb_face_t *face) +{ + this->face = face; +#define HB_OT_TABLE(Namespace, Type) Type.init0 (); +#include "hb-ot-face-table-list.hh" +#undef HB_OT_TABLE +} +void hb_ot_face_t::fini () +{ +#define HB_OT_TABLE(Namespace, Type) Type.fini (); +#include "hb-ot-face-table-list.hh" +#undef HB_OT_TABLE +} diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-face.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-face.hh new file mode 100644 index 00000000000..415dae8e209 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-face.hh @@ -0,0 +1,77 @@ +/* + * Copyright ยฉ 2007,2008,2009 Red Hat, Inc. + * Copyright ยฉ 2012,2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_FACE_HH +#define HB_OT_FACE_HH + +#include "hb.hh" + +#include "hb-machinery.hh" + + +/* + * hb_ot_face_t + */ + +/* Declare tables. */ +#define HB_OT_TABLE(Namespace, Type) namespace Namespace { struct Type; } +#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type##_accelerator_t) +#include "hb-ot-face-table-list.hh" +#undef HB_OT_ACCELERATOR +#undef HB_OT_TABLE + +struct hb_ot_face_t +{ + HB_INTERNAL void init0 (hb_face_t *face); + HB_INTERNAL void fini (); + +#define HB_OT_TABLE_ORDER(Namespace, Type) \ + HB_PASTE (ORDER_, HB_PASTE (Namespace, HB_PASTE (_, Type))) + enum order_t + { + ORDER_ZERO, +#define HB_OT_TABLE(Namespace, Type) HB_OT_TABLE_ORDER (Namespace, Type), +#include "hb-ot-face-table-list.hh" +#undef HB_OT_TABLE + }; + + hb_face_t *face; /* MUST be JUST before the lazy loaders. */ +#define HB_OT_TABLE(Namespace, Type) \ + hb_table_lazy_loader_t Type; +#define HB_OT_CORE_TABLE(Namespace, Type) \ + hb_table_lazy_loader_t Type; +#define HB_OT_ACCELERATOR(Namespace, Type) \ + hb_face_lazy_loader_t Type; +#include "hb-ot-face-table-list.hh" +#undef HB_OT_ACCELERATOR +#undef HB_OT_CORE_TABLE +#undef HB_OT_TABLE +}; + + +#endif /* HB_OT_FACE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-font.cc b/src/3rdparty/harfbuzz-ng/src/hb-ot-font.cc index 9864064b112..06f7092a59d 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-font.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-font.cc @@ -24,78 +24,137 @@ * Google Author(s): Behdad Esfahbod, Roozbeh Pournader */ -#include "hb-private.hh" +#include "hb.hh" + +#ifndef HB_NO_OT_FONT #include "hb-ot.h" -#include "hb-font-private.hh" +#include "hb-cache.hh" +#include "hb-font.hh" +#include "hb-machinery.hh" +#include "hb-ot-face.hh" +#include "hb-outline.hh" #include "hb-ot-cmap-table.hh" -#include "hb-ot-cbdt-table.hh" #include "hb-ot-glyf-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" #include "hb-ot-hmtx-table.hh" -#include "hb-ot-kern-table.hh" #include "hb-ot-post-table.hh" +#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise. +#include "hb-ot-vorg-table.hh" +#include "OT/Color/CBDT/CBDT.hh" +#include "OT/Color/COLR/COLR.hh" +#include "OT/Color/sbix/sbix.hh" +#include "OT/Color/svg/svg.hh" +/** + * SECTION:hb-ot-font + * @title: hb-ot-font + * @short_description: OpenType font implementation + * @include: hb-ot.h + * + * Functions for using OpenType fonts with hb_shape(). Note that fonts returned + * by hb_font_create() default to using these functions, so most clients would + * never need to call these functions directly. + **/ + +using hb_ot_font_cmap_cache_t = hb_cache_t<21, 16, 8, true>; +using hb_ot_font_advance_cache_t = hb_cache_t<24, 16, 8, true>; + +static hb_user_data_key_t hb_ot_font_cmap_cache_user_data_key; + struct hb_ot_font_t { - OT::cmap::accelerator_t cmap; - OT::hmtx::accelerator_t h_metrics; - OT::vmtx::accelerator_t v_metrics; - OT::hb_lazy_loader_t glyf; - OT::hb_lazy_loader_t cbdt; - OT::hb_lazy_loader_t post; - OT::hb_lazy_loader_t kern; + const hb_ot_face_t *ot_face; + + hb_ot_font_cmap_cache_t *cmap_cache; + + /* h_advance caching */ + mutable hb_atomic_int_t cached_coords_serial; + mutable hb_atomic_ptr_t advance_cache; }; - static hb_ot_font_t * -_hb_ot_font_create (hb_face_t *face) +_hb_ot_font_create (hb_font_t *font) { - hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t)); - + hb_ot_font_t *ot_font = (hb_ot_font_t *) hb_calloc (1, sizeof (hb_ot_font_t)); if (unlikely (!ot_font)) return nullptr; - ot_font->cmap.init (face); - ot_font->h_metrics.init (face); - ot_font->v_metrics.init (face, ot_font->h_metrics.ascender - ot_font->h_metrics.descender); /* TODO Can we do this lazily? */ - ot_font->glyf.init (face); - ot_font->cbdt.init (face); - ot_font->post.init (face); - ot_font->kern.init (face); + ot_font->ot_face = &font->face->table; + + // retry: + auto *cmap_cache = (hb_ot_font_cmap_cache_t *) hb_face_get_user_data (font->face, + &hb_ot_font_cmap_cache_user_data_key); + if (!cmap_cache) + { + cmap_cache = (hb_ot_font_cmap_cache_t *) hb_malloc (sizeof (hb_ot_font_cmap_cache_t)); + if (unlikely (!cmap_cache)) goto out; + cmap_cache->init (); + if (unlikely (!hb_face_set_user_data (font->face, + &hb_ot_font_cmap_cache_user_data_key, + cmap_cache, + hb_free, + false))) + { + hb_free (cmap_cache); + cmap_cache = nullptr; + /* Normally we would retry here, but that would + * infinite-loop if the face is the empty-face. + * Just let it go and this font will be uncached if it + * happened to collide with another thread creating the + * cache at the same time. */ + // goto retry; + } + } + out: + ot_font->cmap_cache = cmap_cache; return ot_font; } static void -_hb_ot_font_destroy (void *data) +_hb_ot_font_destroy (void *font_data) { - hb_ot_font_t *ot_font = (hb_ot_font_t *) data; + hb_ot_font_t *ot_font = (hb_ot_font_t *) font_data; - ot_font->cmap.fini (); - ot_font->h_metrics.fini (); - ot_font->v_metrics.fini (); - ot_font->glyf.fini (); - ot_font->cbdt.fini (); - ot_font->post.fini (); - ot_font->kern.fini (); + auto *cache = ot_font->advance_cache.get_relaxed (); + hb_free (cache); - free (ot_font); + hb_free (ot_font); } - static hb_bool_t hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED, void *font_data, hb_codepoint_t unicode, hb_codepoint_t *glyph, void *user_data HB_UNUSED) - { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return ot_font->cmap.get_nominal_glyph (unicode, glyph); + const hb_ot_face_t *ot_face = ot_font->ot_face; + return ot_face->cmap->get_nominal_glyph (unicode, glyph, ot_font->cmap_cache); +} + +static unsigned int +hb_ot_get_nominal_glyphs (hb_font_t *font HB_UNUSED, + void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + return ot_face->cmap->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride, + ot_font->cmap_cache); } static hb_bool_t @@ -107,169 +166,464 @@ hb_ot_get_variation_glyph (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return ot_font->cmap.get_variation_glyph (unicode, variation_selector, glyph); + const hb_ot_face_t *ot_face = ot_font->ot_face; + return ot_face->cmap->get_variation_glyph (unicode, + variation_selector, glyph, + ot_font->cmap_cache); } -static hb_position_t -hb_ot_get_glyph_h_advance (hb_font_t *font, - void *font_data, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) +static void +hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, + unsigned count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data HB_UNUSED) { + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return font->em_scale_x (ot_font->h_metrics.get_advance (glyph, font)); + const hb_ot_face_t *ot_face = ot_font->ot_face; + const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx; + + hb_position_t *orig_first_advance = first_advance; + +#ifndef HB_NO_VAR + const OT::HVAR &HVAR = *hmtx.var_table; + const OT::VariationStore &varStore = &HVAR + HVAR.varStore; + OT::VariationStore::cache_t *varStore_cache = font->num_coords * count >= 128 ? varStore.create_cache () : nullptr; + + bool use_cache = font->num_coords; +#else + OT::VariationStore::cache_t *varStore_cache = nullptr; + bool use_cache = false; +#endif + + hb_ot_font_advance_cache_t *cache = nullptr; + if (use_cache) + { + retry: + cache = ot_font->advance_cache.get_acquire (); + if (unlikely (!cache)) + { + cache = (hb_ot_font_advance_cache_t *) hb_malloc (sizeof (hb_ot_font_advance_cache_t)); + if (unlikely (!cache)) + { + use_cache = false; + goto out; + } + + cache->init (); + if (unlikely (!ot_font->advance_cache.cmpexch (nullptr, cache))) + { + hb_free (cache); + goto retry; + } + ot_font->cached_coords_serial.set_release (font->serial_coords); + } + } + out: + + if (!use_cache) + { + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->em_scale_x (hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache)); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } + else + { /* Use cache. */ + if (ot_font->cached_coords_serial.get_acquire () != (int) font->serial_coords) + { + ot_font->advance_cache->init (); + ot_font->cached_coords_serial.set_release (font->serial_coords); + } + + for (unsigned int i = 0; i < count; i++) + { + hb_position_t v; + unsigned cv; + if (ot_font->advance_cache->get (*first_glyph, &cv)) + v = cv; + else + { + v = hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache); + ot_font->advance_cache->set (*first_glyph, v); + } + *first_advance = font->em_scale_x (v); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } + +#ifndef HB_NO_VAR + OT::VariationStore::destroy_cache (varStore_cache); +#endif + + if (font->x_strength && !font->embolden_in_place) + { + /* Emboldening. */ + hb_position_t x_strength = font->x_scale >= 0 ? font->x_strength : -font->x_strength; + first_advance = orig_first_advance; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? x_strength : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } } -static hb_position_t -hb_ot_get_glyph_v_advance (hb_font_t *font, - void *font_data, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) +#ifndef HB_NO_VERTICAL +static void +hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, + unsigned count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data HB_UNUSED) { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph, font)); -} + const hb_ot_face_t *ot_face = ot_font->ot_face; + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; -static hb_position_t -hb_ot_get_glyph_h_kerning (hb_font_t *font, - void *font_data, - hb_codepoint_t left_glyph, - hb_codepoint_t right_glyph, - void *user_data HB_UNUSED) + hb_position_t *orig_first_advance = first_advance; + + if (vmtx.has_data ()) + { +#ifndef HB_NO_VAR + const OT::VVAR &VVAR = *vmtx.var_table; + const OT::VariationStore &varStore = &VVAR + VVAR.varStore; + OT::VariationStore::cache_t *varStore_cache = font->num_coords ? varStore.create_cache () : nullptr; +#else + OT::VariationStore::cache_t *varStore_cache = nullptr; +#endif + + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->em_scale_y (-(int) vmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache)); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + +#ifndef HB_NO_VAR + OT::VariationStore::destroy_cache (varStore_cache); +#endif + } + else + { + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + hb_position_t advance = -(font_extents.ascender - font_extents.descender); + + for (unsigned int i = 0; i < count; i++) + { + *first_advance = advance; + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } + + if (font->y_strength && !font->embolden_in_place) + { + /* Emboldening. */ + hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength; + first_advance = orig_first_advance; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? y_strength : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } +} +#endif + +#ifndef HB_NO_VERTICAL +static hb_bool_t +hb_ot_get_glyph_v_origin (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return font->em_scale_x (ot_font->kern->get_h_kerning (left_glyph, right_glyph)); + const hb_ot_face_t *ot_face = ot_font->ot_face; + + *x = font->get_glyph_h_advance (glyph) / 2; + + const OT::VORG &VORG = *ot_face->VORG; + if (VORG.has_data ()) + { + float delta = 0; + +#ifndef HB_NO_VAR + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + const OT::VVAR &VVAR = *vmtx.var_table; + if (font->num_coords) + VVAR.get_vorg_delta_unscaled (glyph, + font->coords, font->num_coords, + &delta); +#endif + + *y = font->em_scalef_y (VORG.get_y_origin (glyph) + delta); + return true; + } + + hb_glyph_extents_t extents = {0}; + if (ot_face->glyf->get_extents (font, glyph, &extents)) + { + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + int tsb = 0; + if (vmtx.get_leading_bearing_with_var_unscaled (font, glyph, &tsb)) + { + *y = extents.y_bearing + font->em_scale_y (tsb); + return true; + } + + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + hb_position_t advance = font_extents.ascender - font_extents.descender; + int diff = advance - -extents.height; + *y = extents.y_bearing + (diff >> 1); + return true; + } + + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + *y = font_extents.ascender; + + return true; } +#endif static hb_bool_t -hb_ot_get_glyph_extents (hb_font_t *font HB_UNUSED, +hb_ot_get_glyph_extents (hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_glyph_extents_t *extents, void *user_data HB_UNUSED) { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - bool ret = ot_font->glyf->get_extents (glyph, extents); - if (!ret) - ret = ot_font->cbdt->get_extents (glyph, extents); - // TODO Hook up side-bearings variations. - extents->x_bearing = font->em_scale_x (extents->x_bearing); - extents->y_bearing = font->em_scale_y (extents->y_bearing); - extents->width = font->em_scale_x (extents->width); - extents->height = font->em_scale_y (extents->height); + const hb_ot_face_t *ot_face = ot_font->ot_face; + +#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR) + if (ot_face->sbix->get_extents (font, glyph, extents)) return true; + if (ot_face->CBDT->get_extents (font, glyph, extents)) return true; +#endif +#if !defined(HB_NO_COLOR) && !defined(HB_NO_PAINT) + if (ot_face->COLR->get_extents (font, glyph, extents)) return true; +#endif + if (ot_face->glyf->get_extents (font, glyph, extents)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_extents (font, glyph, extents)) return true; + if (ot_face->cff2->get_extents (font, glyph, extents)) return true; +#endif + + return false; +} + +#ifndef HB_NO_OT_FONT_GLYPH_NAMES +static hb_bool_t +hb_ot_get_glyph_name (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + + if (ot_face->post->get_glyph_name (glyph, name, size)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_glyph_name (glyph, name, size)) return true; +#endif + return false; +} +static hb_bool_t +hb_ot_get_glyph_from_name (hb_font_t *font HB_UNUSED, + void *font_data, + const char *name, int len, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + + if (ot_face->post->get_glyph_from_name (name, len, glyph)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_glyph_from_name (name, len, glyph)) return true; +#endif + return false; +} +#endif + +static hb_bool_t +hb_ot_get_font_h_extents (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + bool ret = _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); + + /* Embolden */ + int y_shift = font->y_strength; + if (font->y_scale < 0) y_shift = -y_shift; + metrics->ascender += y_shift; + return ret; } +#ifndef HB_NO_VERTICAL static hb_bool_t -hb_ot_get_glyph_name (hb_font_t *font HB_UNUSED, - void *font_data, - hb_codepoint_t glyph, - char *name, unsigned int size, - void *user_data HB_UNUSED) -{ - const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return ot_font->post->get_glyph_name (glyph, name, size); -} - -static hb_bool_t -hb_ot_get_glyph_from_name (hb_font_t *font HB_UNUSED, - void *font_data, - const char *name, int len, - hb_codepoint_t *glyph, - void *user_data HB_UNUSED) -{ - const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return ot_font->post->get_glyph_from_name (name, len, glyph); -} - -static hb_bool_t -hb_ot_get_font_h_extents (hb_font_t *font HB_UNUSED, - void *font_data, +hb_ot_get_font_v_extents (hb_font_t *font, + void *font_data HB_UNUSED, hb_font_extents_t *metrics, void *user_data HB_UNUSED) { - const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - metrics->ascender = font->em_scale_y (ot_font->h_metrics.ascender); - metrics->descender = font->em_scale_y (ot_font->h_metrics.descender); - metrics->line_gap = font->em_scale_y (ot_font->h_metrics.line_gap); - // TODO Hook up variations. - return ot_font->h_metrics.has_font_extents; -} - -static hb_bool_t -hb_ot_get_font_v_extents (hb_font_t *font HB_UNUSED, - void *font_data, - hb_font_extents_t *metrics, - void *user_data HB_UNUSED) -{ - const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - metrics->ascender = font->em_scale_x (ot_font->v_metrics.ascender); - metrics->descender = font->em_scale_x (ot_font->v_metrics.descender); - metrics->line_gap = font->em_scale_x (ot_font->v_metrics.line_gap); - // TODO Hook up variations. - return ot_font->v_metrics.has_font_extents; -} - -static hb_font_funcs_t *static_ot_funcs = nullptr; - -#ifdef HB_USE_ATEXIT -static -void free_static_ot_funcs (void) -{ - hb_font_funcs_destroy (static_ot_funcs); + return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_LINE_GAP, &metrics->line_gap); } #endif -static hb_font_funcs_t * -_hb_ot_get_font_funcs (void) +#ifndef HB_NO_DRAW +static void +hb_ot_draw_glyph (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data) { -retry: - hb_font_funcs_t *funcs = (hb_font_funcs_t *) hb_atomic_ptr_get (&static_ot_funcs); + bool embolden = font->x_strength || font->y_strength; + hb_outline_t outline; - if (unlikely (!funcs)) + { // Need draw_session to be destructed before emboldening. + hb_draw_session_t draw_session (embolden ? hb_outline_recording_pen_get_funcs () : draw_funcs, + embolden ? &outline : draw_data, font->slant_xy); + if (!font->face->table.glyf->get_path (font, glyph, draw_session)) +#ifndef HB_NO_CFF + if (!font->face->table.cff1->get_path (font, glyph, draw_session)) + if (!font->face->table.cff2->get_path (font, glyph, draw_session)) +#endif + {} + } + + if (embolden) { - funcs = hb_font_funcs_create (); + float x_shift = font->embolden_in_place ? 0 : (float) font->x_strength / 2; + float y_shift = (float) font->y_strength / 2; + if (font->x_scale < 0) x_shift = -x_shift; + if (font->y_scale < 0) y_shift = -y_shift; + outline.embolden (font->x_strength, font->y_strength, + x_shift, y_shift); + + outline.replay (draw_funcs, draw_data); + } +} +#endif + +#ifndef HB_NO_PAINT +static void +hb_ot_paint_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) +{ +#ifndef HB_NO_COLOR + if (font->face->table.COLR->paint_glyph (font, glyph, paint_funcs, paint_data, palette, foreground)) return; + if (font->face->table.SVG->paint_glyph (font, glyph, paint_funcs, paint_data)) return; +#ifndef HB_NO_OT_FONT_BITMAP + if (font->face->table.CBDT->paint_glyph (font, glyph, paint_funcs, paint_data)) return; + if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return; +#endif +#endif + if (font->face->table.glyf->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; +#ifndef HB_NO_CFF + if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; + if (font->face->table.cff2->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; +#endif +} +#endif + +static inline void free_static_ot_funcs (); + +static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t +{ + static hb_font_funcs_t *create () + { + hb_font_funcs_t *funcs = hb_font_funcs_create (); + + hb_font_funcs_set_nominal_glyph_func (funcs, hb_ot_get_nominal_glyph, nullptr, nullptr); + hb_font_funcs_set_nominal_glyphs_func (funcs, hb_ot_get_nominal_glyphs, nullptr, nullptr); + hb_font_funcs_set_variation_glyph_func (funcs, hb_ot_get_variation_glyph, nullptr, nullptr); hb_font_funcs_set_font_h_extents_func (funcs, hb_ot_get_font_h_extents, nullptr, nullptr); - hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, nullptr, nullptr); - hb_font_funcs_set_nominal_glyph_func (funcs, hb_ot_get_nominal_glyph, nullptr, nullptr); - hb_font_funcs_set_variation_glyph_func (funcs, hb_ot_get_variation_glyph, nullptr, nullptr); - hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ot_get_glyph_h_advance, nullptr, nullptr); - hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ot_get_glyph_v_advance, nullptr, nullptr); + hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ot_get_glyph_h_advances, nullptr, nullptr); //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, nullptr, nullptr); - //hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr); - hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ot_get_glyph_h_kerning, nullptr, nullptr); - //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ot_get_glyph_v_kerning, nullptr, nullptr); + +#ifndef HB_NO_VERTICAL + hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_v_advances_func (funcs, hb_ot_get_glyph_v_advances, nullptr, nullptr); + hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr); +#endif + +#ifndef HB_NO_DRAW + hb_font_funcs_set_draw_glyph_func (funcs, hb_ot_draw_glyph, nullptr, nullptr); +#endif + +#ifndef HB_NO_PAINT + hb_font_funcs_set_paint_glyph_func (funcs, hb_ot_paint_glyph, nullptr, nullptr); +#endif + hb_font_funcs_set_glyph_extents_func (funcs, hb_ot_get_glyph_extents, nullptr, nullptr); //hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ot_get_glyph_contour_point, nullptr, nullptr); + +#ifndef HB_NO_OT_FONT_GLYPH_NAMES hb_font_funcs_set_glyph_name_func (funcs, hb_ot_get_glyph_name, nullptr, nullptr); hb_font_funcs_set_glyph_from_name_func (funcs, hb_ot_get_glyph_from_name, nullptr, nullptr); +#endif hb_font_funcs_make_immutable (funcs); - if (!hb_atomic_ptr_cmpexch (&static_ot_funcs, nullptr, funcs)) { - hb_font_funcs_destroy (funcs); - goto retry; - } + hb_atexit (free_static_ot_funcs); -#ifdef HB_USE_ATEXIT - atexit (free_static_ot_funcs); /* First person registers atexit() callback. */ -#endif - }; + return funcs; + } +} static_ot_funcs; - return funcs; +static inline +void free_static_ot_funcs () +{ + static_ot_funcs.free_instance (); +} + +static hb_font_funcs_t * +_hb_ot_get_font_funcs () +{ + return static_ot_funcs.get_unconst (); } /** * hb_ot_font_set_funcs: + * @font: #hb_font_t to work upon + * + * Sets the font functions to use when working with @font. * * Since: 0.9.28 **/ void hb_ot_font_set_funcs (hb_font_t *font) { - hb_ot_font_t *ot_font = _hb_ot_font_create (font->face); + hb_ot_font_t *ot_font = _hb_ot_font_create (font); if (unlikely (!ot_font)) return; @@ -278,3 +632,5 @@ hb_ot_font_set_funcs (hb_font_t *font) ot_font, _hb_ot_font_destroy); } + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-font.h b/src/3rdparty/harfbuzz-ng/src/hb-ot-font.h index 80eaa54b1ad..e7959d1ae28 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-font.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-font.h @@ -24,7 +24,7 @@ * Google Author(s): Behdad Esfahbod, Roozbeh Pournader */ -#ifndef HB_OT_H_IN +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-gasp-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-gasp-table.hh new file mode 100644 index 00000000000..f2a9cad464b --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-gasp-table.hh @@ -0,0 +1,84 @@ +/* + * Copyright ยฉ 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_OT_GASP_TABLE_HH +#define HB_OT_GASP_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-var-hvar-table.hh" + +/* + * gasp -- Grid-fitting and Scan-conversion Procedure + * https://docs.microsoft.com/en-us/typography/opentype/spec/gasp + */ +#define HB_OT_TAG_gasp HB_TAG('g','a','s','p') + + +namespace OT { + +struct GaspRange +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 rangeMaxPPEM; /* Upper limit of range, in PPEM */ + HBUINT16 rangeGaspBehavior; + /* Flags describing desired rasterizer behavior. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct gasp +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_gasp; + + const GaspRange &get_gasp_range (unsigned int i) const + { return gaspRanges[i]; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + gaspRanges.sanitize (c)); + } + + protected: + HBUINT16 version; /* Version number (set to 1) */ + Array16Of + gaspRanges; /* Number of records to follow + * Sorted by ppem */ + public: + DEFINE_SIZE_ARRAY (4, gaspRanges); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_GASP_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-glyf-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-glyf-table.hh index 88d3850b6ef..c32ff7636d5 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-glyf-table.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-glyf-table.hh @@ -1,5 +1,7 @@ /* * Copyright ยฉ 2015 Google, Inc. + * Copyright ยฉ 2019 Adobe Inc. + * Copyright ยฉ 2019 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -21,160 +23,13 @@ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * - * Google Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger, Roderick Sheeter + * Adobe Author(s): Michiharu Ariza */ #ifndef HB_OT_GLYF_TABLE_HH #define HB_OT_GLYF_TABLE_HH -#include "hb-open-type-private.hh" -#include "hb-ot-head-table.hh" - - -namespace OT { - - -/* - * loca -- Index to Location - */ - -#define HB_OT_TAG_loca HB_TAG('l','o','c','a') - - -struct loca -{ - friend struct glyf; - - static const hb_tag_t tableTag = HB_OT_TAG_loca; - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (true); - } - - protected: - UINT8 dataX[VAR]; /* Location data. */ - DEFINE_SIZE_ARRAY (0, dataX); -}; - - -/* - * glyf -- TrueType Glyph Data - */ - -#define HB_OT_TAG_glyf HB_TAG('g','l','y','f') - - -struct glyf -{ - static const hb_tag_t tableTag = HB_OT_TAG_glyf; - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - /* We don't check for anything specific here. The users of the - * struct do all the hard work... */ - return_trace (true); - } - - struct GlyphHeader - { - INT16 numberOfContours; /* If the number of contours is - * greater than or equal to zero, - * this is a simple glyph; if negative, - * this is a composite glyph. */ - FWORD xMin; /* Minimum x for coordinate data. */ - FWORD yMin; /* Minimum y for coordinate data. */ - FWORD xMax; /* Maximum x for coordinate data. */ - FWORD yMax; /* Maximum y for coordinate data. */ - - DEFINE_SIZE_STATIC (10); - }; - - struct accelerator_t - { - inline void init (hb_face_t *face) - { - hb_blob_t *head_blob = Sanitizer::sanitize (face->reference_table (HB_OT_TAG_head)); - const head *head_table = Sanitizer::lock_instance (head_blob); - if ((unsigned int) head_table->indexToLocFormat > 1 || head_table->glyphDataFormat != 0) - { - /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ - hb_blob_destroy (head_blob); - return; - } - short_offset = 0 == head_table->indexToLocFormat; - hb_blob_destroy (head_blob); - - loca_blob = Sanitizer::sanitize (face->reference_table (HB_OT_TAG_loca)); - loca_table = Sanitizer::lock_instance (loca_blob); - glyf_blob = Sanitizer::sanitize (face->reference_table (HB_OT_TAG_glyf)); - glyf_table = Sanitizer::lock_instance (glyf_blob); - - num_glyphs = MAX (1u, hb_blob_get_length (loca_blob) / (short_offset ? 2 : 4)) - 1; - glyf_len = hb_blob_get_length (glyf_blob); - } - - inline void fini (void) - { - hb_blob_destroy (loca_blob); - hb_blob_destroy (glyf_blob); - } - - inline bool get_extents (hb_codepoint_t glyph, - hb_glyph_extents_t *extents) const - { - if (unlikely (glyph >= num_glyphs)) - return false; - - unsigned int start_offset, end_offset; - if (short_offset) - { - const UINT16 *offsets = (const UINT16 *) loca_table->dataX; - start_offset = 2 * offsets[glyph]; - end_offset = 2 * offsets[glyph + 1]; - } - else - { - const UINT32 *offsets = (const UINT32 *) loca_table->dataX; - start_offset = offsets[glyph]; - end_offset = offsets[glyph + 1]; - } - - if (start_offset > end_offset || end_offset > glyf_len) - return false; - - if (end_offset - start_offset < GlyphHeader::static_size) - return true; /* Empty glyph; zero extents. */ - - const GlyphHeader &glyph_header = StructAtOffset (glyf_table, start_offset); - - extents->x_bearing = MIN (glyph_header.xMin, glyph_header.xMax); - extents->y_bearing = MAX (glyph_header.yMin, glyph_header.yMax); - extents->width = MAX (glyph_header.xMin, glyph_header.xMax) - extents->x_bearing; - extents->height = MIN (glyph_header.yMin, glyph_header.yMax) - extents->y_bearing; - - return true; - } - - private: - bool short_offset; - unsigned int num_glyphs; - const loca *loca_table; - const glyf *glyf_table; - hb_blob_t *loca_blob; - hb_blob_t *glyf_blob; - unsigned int glyf_len; - }; - - protected: - UINT8 dataX[VAR]; /* Glyphs data. */ - - DEFINE_SIZE_ARRAY (0, dataX); -}; - -} /* namespace OT */ - +#include "OT/glyf/glyf.hh" #endif /* HB_OT_GLYF_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-hdmx-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-hdmx-table.hh new file mode 100644 index 00000000000..3bfd75502a4 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-hdmx-table.hh @@ -0,0 +1,170 @@ +/* + * Copyright ยฉ 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#ifndef HB_OT_HDMX_TABLE_HH +#define HB_OT_HDMX_TABLE_HH + +#include "hb-open-type.hh" + +/* + * hdmx -- Horizontal Device Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/hdmx + */ +#define HB_OT_TAG_hdmx HB_TAG('h','d','m','x') + + +namespace OT { + + +struct DeviceRecord +{ + static unsigned int get_size (unsigned count) + { return hb_ceil_to_4 (min_size + count * HBUINT8::static_size); } + + template + bool serialize (hb_serialize_context_t *c, unsigned pixelSize, Iterator it) + { + TRACE_SERIALIZE (this); + + unsigned length = it.len (); + + if (unlikely (!c->extend (this, length))) return_trace (false); + + this->pixelSize = pixelSize; + this->maxWidth = + + it + | hb_reduce (hb_max, 0u); + + + it + | hb_sink (widthsZ.as_array (length)); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c, unsigned sizeDeviceRecord) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + c->check_range (this, sizeDeviceRecord))); + } + + HBUINT8 pixelSize; /* Pixel size for following widths (as ppem). */ + HBUINT8 maxWidth; /* Maximum width. */ + UnsizedArrayOf widthsZ; /* Array of widths (numGlyphs is from the 'maxp' table). */ + public: + DEFINE_SIZE_UNBOUNDED (2); +}; + + +struct hdmx +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_hdmx; + + unsigned int get_size () const + { return min_size + numRecords * sizeDeviceRecord; } + + template + bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it) + { + TRACE_SERIALIZE (this); + + if (unlikely (!c->extend_min ((*this)))) return_trace (false); + + this->version = version; + this->numRecords = it.len (); + this->sizeDeviceRecord = DeviceRecord::get_size (it ? (*it).second.len () : 0); + + for (const hb_item_type& _ : +it) + c->start_embed ()->serialize (c, _.first, _.second); + + return_trace (c->successful ()); + } + + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + hdmx *hdmx_prime = c->serializer->start_embed (); + if (unlikely (!hdmx_prime)) return_trace (false); + + auto it = + + hb_range ((unsigned) numRecords) + | hb_map ([c, this] (unsigned _) + { + const DeviceRecord *device_record = + &StructAtOffset (&firstDeviceRecord, + _ * sizeDeviceRecord); + auto row = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map (c->plan->reverse_glyph_map) + | hb_map ([this, c, device_record] (hb_codepoint_t _) + { + if (c->plan->is_empty_glyph (_)) + return Null (HBUINT8); + return device_record->widthsZ.as_array (get_num_glyphs ()) [_]; + }) + ; + return hb_pair ((unsigned) device_record->pixelSize, +row); + }) + ; + + hdmx_prime->serialize (c->serializer, version, it); + return_trace (true); + } + + unsigned get_num_glyphs () const + { + return sizeDeviceRecord - DeviceRecord::min_size; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + !hb_unsigned_mul_overflows (numRecords, sizeDeviceRecord) && + min_size + numRecords * sizeDeviceRecord > numRecords * sizeDeviceRecord && + sizeDeviceRecord >= DeviceRecord::min_size && + c->check_range (this, get_size ())); + } + + protected: + HBUINT16 version; /* Table version number (0) */ + HBUINT16 numRecords; /* Number of device records. */ + HBUINT32 sizeDeviceRecord; + /* Size of a device record, 32-bit aligned. */ + DeviceRecord firstDeviceRecord; + /* Array of device records. */ + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_HDMX_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-head-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-head-table.hh index dd4349ef85a..770cf52d173 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-head-table.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-head-table.hh @@ -29,30 +29,77 @@ #ifndef HB_OT_HEAD_TABLE_HH #define HB_OT_HEAD_TABLE_HH -#include "hb-open-type-private.hh" +#include "hb-open-type.hh" + +/* + * head -- Font Header + * https://docs.microsoft.com/en-us/typography/opentype/spec/head + */ +#define HB_OT_TAG_head HB_TAG('h','e','a','d') namespace OT { -/* - * head -- Font Header - */ - -#define HB_OT_TAG_head HB_TAG('h','e','a','d') - struct head { - static const hb_tag_t tableTag = HB_OT_TAG_head; + friend struct OpenTypeOffsetTable; - inline unsigned int get_upem (void) const + static constexpr hb_tag_t tableTag = HB_OT_TAG_head; + + unsigned int get_upem () const { unsigned int upem = unitsPerEm; /* If no valid head table found, assume 1000, which matches typical Type1 usage. */ return 16 <= upem && upem <= 16384 ? upem : 1000; } - inline bool sanitize (hb_sanitize_context_t *c) const + bool serialize (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace ((bool) c->embed (this)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + head *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (c->plan->normalized_coords) + { + if (unlikely (!c->serializer->check_assign (out->xMin, c->plan->head_maxp_info.xMin, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) + return_trace (false); + if (unlikely (!c->serializer->check_assign (out->xMax, c->plan->head_maxp_info.xMax, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) + return_trace (false); + if (unlikely (!c->serializer->check_assign (out->yMin, c->plan->head_maxp_info.yMin, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) + return_trace (false); + if (unlikely (!c->serializer->check_assign (out->yMax, c->plan->head_maxp_info.yMax, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) + return_trace (false); + } + return_trace (true); + } + + enum mac_style_flag_t { + BOLD = 1u<<0, + ITALIC = 1u<<1, + UNDERLINE = 1u<<2, + OUTLINE = 1u<<3, + SHADOW = 1u<<4, + CONDENSED = 1u<<5, + EXPANDED = 1u<<6, + }; + + bool is_bold () const { return macStyle & BOLD; } + bool is_italic () const { return macStyle & ITALIC; } + bool is_condensed () const { return macStyle & CONDENSED; } + bool is_expanded () const { return macStyle & EXPANDED; } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && @@ -64,11 +111,12 @@ struct head FixedVersion<>version; /* Version of the head table--currently * 0x00010000u for version 1.0. */ FixedVersion<>fontRevision; /* Set by font manufacturer. */ - UINT32 checkSumAdjustment; /* To compute: set it to 0, sum the - * entire font as UINT32, then store + HBUINT32 checkSumAdjustment; /* To compute: set it to 0, sum the + * entire font as HBUINT32, then store * 0xB1B0AFBAu - sum. */ - UINT32 magicNumber; /* Set to 0x5F0F3CF5u. */ - UINT16 flags; /* Bit 0: Baseline for font at y=0; + HBUINT32 magicNumber; /* Set to 0x5F0F3CF5u. */ + public: + HBUINT16 flags; /* Bit 0: Baseline for font at y=0; * Bit 1: Left sidebearing point at x=0; * Bit 2: Instructions may depend on point size; * Bit 3: Force ppem to integer values for all @@ -76,7 +124,6 @@ struct head * ppem sizes if this bit is clear; * Bit 4: Instructions may alter advance width * (the advance widths might not scale linearly); - * Bits 5-10: These should be set according to * Apple's specification. However, they are not * implemented in OpenType. @@ -96,7 +143,6 @@ struct head * contains any strong right-to-left glyphs. * Bit 10: This bit should be set if the font * contains Indic-style rearrangement effects. - * Bit 11: Font data is 'lossless,' as a result * of having been compressed and decompressed * with the Agfa MicroType Express engine. @@ -114,18 +160,21 @@ struct head * encoded in the cmap subtables represent proper * support for those code points. * Bit 15: Reserved, set to 0. */ - UINT16 unitsPerEm; /* Valid range is from 16 to 16384. This value + protected: + HBUINT16 unitsPerEm; /* Valid range is from 16 to 16384. This value * should be a power of 2 for fonts that have * TrueType outlines. */ LONGDATETIME created; /* Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer */ LONGDATETIME modified; /* Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer */ - INT16 xMin; /* For all glyph bounding boxes. */ - INT16 yMin; /* For all glyph bounding boxes. */ - INT16 xMax; /* For all glyph bounding boxes. */ - INT16 yMax; /* For all glyph bounding boxes. */ - UINT16 macStyle; /* Bit 0: Bold (if set to 1); + public: + HBINT16 xMin; /* For all glyph bounding boxes. */ + HBINT16 yMin; /* For all glyph bounding boxes. */ + HBINT16 xMax; /* For all glyph bounding boxes. */ + HBINT16 yMax; /* For all glyph bounding boxes. */ + protected: + HBUINT16 macStyle; /* Bit 0: Bold (if set to 1); * Bit 1: Italic (if set to 1) * Bit 2: Underline (if set to 1) * Bit 3: Outline (if set to 1) @@ -133,16 +182,16 @@ struct head * Bit 5: Condensed (if set to 1) * Bit 6: Extended (if set to 1) * Bits 7-15: Reserved (set to 0). */ - UINT16 lowestRecPPEM; /* Smallest readable size in pixels. */ - INT16 fontDirectionHint; /* Deprecated (Set to 2). + HBUINT16 lowestRecPPEM; /* Smallest readable size in pixels. */ + HBINT16 fontDirectionHint; /* Deprecated (Set to 2). * 0: Fully mixed directional glyphs; * 1: Only strongly left to right; * 2: Like 1 but also contains neutrals; * -1: Only strongly right to left; * -2: Like -1 but also contains neutrals. */ public: - INT16 indexToLocFormat; /* 0 for short offsets, 1 for long. */ - INT16 glyphDataFormat; /* 0 for current format. */ + HBUINT16 indexToLocFormat; /* 0 for short offsets, 1 for long. */ + HBUINT16 glyphDataFormat; /* 0 for current format. */ DEFINE_SIZE_STATIC (54); }; diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-hhea-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-hhea-table.hh index dca014148ea..d9c9bd35377 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-hhea-table.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-hhea-table.hh @@ -27,68 +27,74 @@ #ifndef HB_OT_HHEA_TABLE_HH #define HB_OT_HHEA_TABLE_HH -#include "hb-open-type-private.hh" +#include "hb-open-type.hh" + +/* + * hhea -- Horizontal Header + * https://docs.microsoft.com/en-us/typography/opentype/spec/hhea + * vhea -- Vertical Header + * https://docs.microsoft.com/en-us/typography/opentype/spec/vhea + */ +#define HB_OT_TAG_hhea HB_TAG('h','h','e','a') +#define HB_OT_TAG_vhea HB_TAG('v','h','e','a') namespace OT { -/* - * hhea -- The Horizontal Header Table - * vhea -- The Vertical Header Table - */ - -#define HB_OT_TAG_hhea HB_TAG('h','h','e','a') -#define HB_OT_TAG_vhea HB_TAG('v','h','e','a') - - +template struct _hea { - inline bool sanitize (hb_sanitize_context_t *c) const + bool has_data () const { return version.major; } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && likely (version.major == 1)); } public: - FixedVersion<>version; /* 0x00010000u for version 1.0. */ - FWORD ascender; /* Typographic ascent. */ - FWORD descender; /* Typographic descent. */ - FWORD lineGap; /* Typographic line gap. */ - UFWORD advanceMax; /* Maximum advance width/height value in - * metrics table. */ - FWORD minLeadingBearing; /* Minimum left/top sidebearing value in - * metrics table. */ - FWORD minTrailingBearing; /* Minimum right/bottom sidebearing value; - * calculated as Min(aw - lsb - - * (xMax - xMin)) for horizontal. */ - FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)), - * vertical: minLeadingBearing+(yMax-yMin). */ - INT16 caretSlopeRise; /* Used to calculate the slope of the - * cursor (rise/run); 1 for vertical caret, - * 0 for horizontal.*/ - INT16 caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */ - INT16 caretOffset; /* The amount by which a slanted - * highlight on a glyph needs - * to be shifted to produce the - * best appearance. Set to 0 for - * non-slanted fonts. */ - INT16 reserved1; /* Set to 0. */ - INT16 reserved2; /* Set to 0. */ - INT16 reserved3; /* Set to 0. */ - INT16 reserved4; /* Set to 0. */ - INT16 metricDataFormat; /* 0 for current format. */ - UINT16 numberOfLongMetrics; /* Number of LongMetric entries in metric - * table. */ + FixedVersion<>version; /* 0x00010000u for version 1.0. */ + FWORD ascender; /* Typographic ascent. */ + FWORD descender; /* Typographic descent. */ + FWORD lineGap; /* Typographic line gap. */ + UFWORD advanceMax; /* Maximum advance width/height value in + * metrics table. */ + FWORD minLeadingBearing; + /* Minimum left/top sidebearing value in + * metrics table. */ + FWORD minTrailingBearing; + /* Minimum right/bottom sidebearing value; + * calculated as Min(aw - lsb - + * (xMax - xMin)) for horizontal. */ + FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)), + * vertical: minLeadingBearing+(yMax-yMin). */ + HBINT16 caretSlopeRise; /* Used to calculate the slope of the + * cursor (rise/run); 1 for vertical caret, + * 0 for horizontal.*/ + HBINT16 caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */ + HBINT16 caretOffset; /* The amount by which a slanted + * highlight on a glyph needs + * to be shifted to produce the + * best appearance. Set to 0 for + * non-slanted fonts. */ + HBINT16 reserved1; /* Set to 0. */ + HBINT16 reserved2; /* Set to 0. */ + HBINT16 reserved3; /* Set to 0. */ + HBINT16 reserved4; /* Set to 0. */ + HBINT16 metricDataFormat;/* 0 for current format. */ + HBUINT16 numberOfLongMetrics; + /* Number of LongMetric entries in metric + * table. */ public: DEFINE_SIZE_STATIC (36); }; -struct hhea : _hea { - static const hb_tag_t tableTag = HB_OT_TAG_hhea; +struct hhea : _hea { + static constexpr hb_tag_t tableTag = HB_OT_TAG_hhea; }; -struct vhea : _hea { - static const hb_tag_t tableTag = HB_OT_TAG_vhea; +struct vhea : _hea { + static constexpr hb_tag_t tableTag = HB_OT_TAG_vhea; }; diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-hmtx-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-hmtx-table.hh index e710aee42ec..835a1a585e8 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-hmtx-table.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-hmtx-table.hh @@ -21,42 +21,55 @@ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * - * Google Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Roderick Sheeter */ #ifndef HB_OT_HMTX_TABLE_HH #define HB_OT_HMTX_TABLE_HH -#include "hb-open-type-private.hh" +#include "hb-open-type.hh" +#include "hb-ot-maxp-table.hh" #include "hb-ot-hhea-table.hh" -#include "hb-ot-os2-table.hh" #include "hb-ot-var-hvar-table.hh" +#include "hb-ot-var-mvar-table.hh" +#include "hb-ot-metrics.hh" + +/* + * hmtx -- Horizontal Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx + * vmtx -- Vertical Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/vmtx + */ +#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x') +#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x') + + +HB_INTERNAL bool +_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical, int *lsb); + +HB_INTERNAL unsigned +_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); + +HB_INTERNAL bool +_glyf_get_leading_bearing_without_var_unscaled (hb_face_t *face, hb_codepoint_t gid, bool is_vertical, int *lsb); namespace OT { -/* - * hmtx -- The Horizontal Metrics Table - * vmtx -- The Vertical Metrics Table - */ - -#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x') -#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x') - - struct LongMetric { UFWORD advance; /* Advance width/height. */ - FWORD lsb; /* Leading (left/top) side bearing. */ + FWORD sb; /* Leading (left/top) side bearing. */ public: DEFINE_SIZE_STATIC (4); }; -template + +template struct hmtxvmtx { - inline bool sanitize (hb_sanitize_context_t *c) const + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const { TRACE_SANITIZE (this); /* We don't check for anything specific here. The users of the @@ -64,143 +77,390 @@ struct hmtxvmtx return_trace (true); } + const hb_hashmap_t>* get_mtx_map (const hb_subset_plan_t *plan) const + { return T::is_horizontal ? &plan->hmtx_map : &plan->vmtx_map; } + + bool subset_update_header (hb_subset_context_t *c, + unsigned int num_hmetrics, + const hb_hashmap_t> *mtx_map, + const hb_map_t *bounds_map) const + { + hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table (c->plan->source, H::tableTag); + hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob); + hb_blob_destroy (src_blob); + + if (unlikely (!dest_blob)) { + return false; + } + + unsigned int length; + H *table = (H *) hb_blob_get_data (dest_blob, &length); + c->serializer->check_assign (table->numberOfLongMetrics, num_hmetrics, HB_SERIALIZE_ERROR_INT_OVERFLOW); + +#ifndef HB_NO_VAR + if (c->plan->normalized_coords) + { + auto &MVAR = *c->plan->source->table.MVAR; + if (T::is_horizontal) + { + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE, caretSlopeRise); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN, caretSlopeRun); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET, caretOffset); + } + else + { + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_RISE, caretSlopeRise); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_RUN, caretSlopeRun); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET, caretOffset); + } + + int min_lsb = 0x7FFF; + int min_rsb = 0x7FFF; + int max_extent = -0x7FFF; + unsigned max_adv = 0; + for (const auto _ : *mtx_map) + { + hb_codepoint_t gid = _.first; + unsigned adv = _.second.first; + int lsb = _.second.second; + max_adv = hb_max (max_adv, adv); + + if (bounds_map->has (gid)) + { + unsigned bound_width = bounds_map->get (gid); + int rsb = adv - lsb - bound_width; + int extent = lsb + bound_width; + min_lsb = hb_min (min_lsb, lsb); + min_rsb = hb_min (min_rsb, rsb); + max_extent = hb_max (max_extent, extent); + } + } + + table->advanceMax = max_adv; + if (!bounds_map->is_empty ()) + { + table->minLeadingBearing = min_lsb; + table->minTrailingBearing = min_rsb; + table->maxExtent = max_extent; + } + } +#endif + + bool result = c->plan->add_table (H::tableTag, dest_blob); + hb_blob_destroy (dest_blob); + + return result; + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it, + unsigned num_long_metrics) + { + unsigned idx = 0; + for (auto _ : it) + { + if (idx < num_long_metrics) + { + LongMetric lm; + lm.advance = _.first; + lm.sb = _.second; + if (unlikely (!c->embed (&lm))) return; + } + else if (idx < 0x10000u) + { + FWORD *sb = c->allocate_size (FWORD::static_size); + if (unlikely (!sb)) return; + *sb = _.second; + } + else + { + // TODO: This does not do tail optimization. + UFWORD *adv = c->allocate_size (UFWORD::static_size); + if (unlikely (!adv)) return; + *adv = _.first; + } + idx++; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + T *table_prime = c->serializer->start_embed (); + if (unlikely (!table_prime)) return_trace (false); + + accelerator_t _mtx (c->plan->source); + unsigned num_long_metrics; + const hb_hashmap_t> *mtx_map = get_mtx_map (c->plan); + { + /* Determine num_long_metrics to encode. */ + auto& plan = c->plan; + + num_long_metrics = hb_min (plan->num_output_glyphs (), 0xFFFFu); + unsigned int last_advance = get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 1, _mtx); + while (num_long_metrics > 1 && + last_advance == get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 2, _mtx)) + { + num_long_metrics--; + } + } + + auto it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map ([c, &_mtx, mtx_map] (unsigned _) + { + if (!mtx_map->has (_)) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (_, &old_gid)) + return hb_pair (0u, 0); + int lsb = 0; + if (!_mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb)) + (void) _glyf_get_leading_bearing_without_var_unscaled (c->plan->source, old_gid, !T::is_horizontal, &lsb); + return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb); + } + return mtx_map->get (_); + }) + ; + + table_prime->serialize (c->serializer, it, num_long_metrics); + + if (unlikely (c->serializer->in_error ())) + return_trace (false); + + // Amend header num hmetrics + if (unlikely (!subset_update_header (c, num_long_metrics, mtx_map, + T::is_horizontal ? &c->plan->bounds_width_map : &c->plan->bounds_height_map))) + return_trace (false); + + return_trace (true); + } + struct accelerator_t { - inline void init (hb_face_t *face, - unsigned int default_advance_ = 0) + friend struct hmtxvmtx; + + accelerator_t (hb_face_t *face) { - default_advance = default_advance_ ? default_advance_ : face->get_upem (); + table = hb_sanitize_context_t ().reference_table (face, T::tableTag); + var_table = hb_sanitize_context_t ().reference_table (face, T::variationsTag); - bool got_font_extents = false; - if (T::os2Tag) - { - hb_blob_t *os2_blob = Sanitizer::sanitize (face->reference_table (T::os2Tag)); - const os2 *os2_table = Sanitizer::lock_instance (os2_blob); -#define USE_TYPO_METRICS (1u<<7) - if (0 != (os2_table->fsSelection & USE_TYPO_METRICS)) - { - ascender = os2_table->sTypoAscender; - descender = os2_table->sTypoDescender; - line_gap = os2_table->sTypoLineGap; - got_font_extents = (ascender | descender) != 0; - } - hb_blob_destroy (os2_blob); - } + default_advance = T::is_horizontal ? hb_face_get_upem (face) / 2 : hb_face_get_upem (face); - hb_blob_t *_hea_blob = Sanitizer<_hea>::sanitize (face->reference_table (T::headerTag)); - const _hea *_hea_table = Sanitizer<_hea>::lock_instance (_hea_blob); - num_advances = _hea_table->numberOfLongMetrics; - if (!got_font_extents) - { - ascender = _hea_table->ascender; - descender = _hea_table->descender; - line_gap = _hea_table->lineGap; - got_font_extents = (ascender | descender) != 0; - } - hb_blob_destroy (_hea_blob); + /* Populate count variables and sort them out as we go */ - has_font_extents = got_font_extents; + unsigned int len = table.get_length (); + if (len & 1) + len--; - blob = Sanitizer::sanitize (face->reference_table (T::tableTag)); + num_long_metrics = T::is_horizontal ? + face->table.hhea->numberOfLongMetrics : +#ifndef HB_NO_VERTICAL + face->table.vhea->numberOfLongMetrics +#else + 0 +#endif + ; + if (unlikely (num_long_metrics * 4 > len)) + num_long_metrics = len / 4; + len -= num_long_metrics * 4; - /* Cap num_metrics() and num_advances() based on table length. */ - unsigned int len = hb_blob_get_length (blob); - if (unlikely (num_advances * 4 > len)) - num_advances = len / 4; - num_metrics = num_advances + (len - 4 * num_advances) / 2; + num_bearings = face->table.maxp->get_num_glyphs (); - /* We MUST set num_metrics to zero if num_advances is zero. + if (unlikely (num_bearings < num_long_metrics)) + num_bearings = num_long_metrics; + if (unlikely ((num_bearings - num_long_metrics) * 2 > len)) + num_bearings = num_long_metrics + len / 2; + len -= (num_bearings - num_long_metrics) * 2; + + /* We MUST set num_bearings to zero if num_long_metrics is zero. * Our get_advance() depends on that. */ + if (unlikely (!num_long_metrics)) + num_bearings = num_long_metrics = 0; + + num_advances = num_bearings + len / 2; + num_glyphs = face->get_num_glyphs (); + if (num_glyphs < num_advances) + num_glyphs = num_advances; + } + ~accelerator_t () + { + table.destroy (); + var_table.destroy (); + } + + bool has_data () const { return (bool) num_bearings; } + + bool get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph, + int *lsb) const + { + if (glyph < num_long_metrics) + { + *lsb = table->longMetricZ[glyph].sb; + return true; + } + + if (unlikely (glyph >= num_bearings)) + return false; + + const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics]; + *lsb = bearings[glyph - num_long_metrics]; + return true; + } + + bool get_leading_bearing_with_var_unscaled (hb_font_t *font, + hb_codepoint_t glyph, + int *lsb) const + { + if (!font->num_coords) + return get_leading_bearing_without_var_unscaled (glyph, lsb); + +#ifndef HB_NO_VAR + float delta; + if (var_table->get_lsb_delta_unscaled (glyph, font->coords, font->num_coords, &delta) && + get_leading_bearing_without_var_unscaled (glyph, lsb)) + { + *lsb += roundf (delta); + return true; + } + + return _glyf_get_leading_bearing_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx, lsb); +#else + return false; +#endif + } + + unsigned int get_advance_without_var_unscaled (hb_codepoint_t glyph) const + { + /* OpenType case. */ + if (glyph < num_bearings) + return table->longMetricZ[hb_min (glyph, (uint32_t) num_long_metrics - 1)].advance; + + /* If num_advances is zero, it means we don't have the metrics table + * for this direction: return default advance. Otherwise, there's a + * well-defined answer. */ if (unlikely (!num_advances)) - { - num_metrics = num_advances = 0; - hb_blob_destroy (blob); - blob = hb_blob_get_empty (); - } - table = Sanitizer::lock_instance (blob); + return default_advance; - var_blob = Sanitizer::sanitize (face->reference_table (T::variationsTag)); - var_table = Sanitizer::lock_instance (var_blob); +#ifdef HB_NO_BEYOND_64K + return 0; +#endif + + if (unlikely (glyph >= num_glyphs)) + return 0; + + /* num_bearings <= glyph < num_glyphs; + * num_bearings <= num_advances */ + + if (num_bearings == num_advances) + return get_advance_without_var_unscaled (num_bearings - 1); + + const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics]; + const UFWORD *advances = (const UFWORD *) &bearings[num_bearings - num_long_metrics]; + + return advances[hb_min (glyph - num_bearings, num_advances - num_bearings - 1)]; } - inline void fini (void) + unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph, + hb_font_t *font, + VariationStore::cache_t *store_cache = nullptr) const { - hb_blob_destroy (blob); - hb_blob_destroy (var_blob); + unsigned int advance = get_advance_without_var_unscaled (glyph); + +#ifndef HB_NO_VAR + if (unlikely (glyph >= num_bearings) || !font->num_coords) + return advance; + + if (var_table.get_length ()) + return advance + roundf (var_table->get_advance_delta_unscaled (glyph, + font->coords, font->num_coords, + store_cache)); + + return _glyf_get_advance_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx); +#else + return advance; +#endif } - inline unsigned int get_advance (hb_codepoint_t glyph, - hb_font_t *font) const - { - if (unlikely (glyph >= num_metrics)) - { - /* If num_metrics is zero, it means we don't have the metrics table - * for this direction: return default advance. Otherwise, it means that the - * glyph index is out of bound: return zero. */ - if (num_metrics) - return 0; - else - return default_advance; - } + protected: + // 0 <= num_long_metrics <= num_bearings <= num_advances <= num_glyphs + unsigned num_long_metrics; + unsigned num_bearings; + unsigned num_advances; + unsigned num_glyphs; - return table->longMetric[MIN (glyph, (uint32_t) num_advances - 1)].advance - + var_table->get_advance_var (glyph, font->coords, font->num_coords); // TODO Optimize?! - } - - public: - bool has_font_extents; - unsigned short ascender; - unsigned short descender; - unsigned short line_gap; - - private: - unsigned int num_metrics; - unsigned int num_advances; unsigned int default_advance; - const hmtxvmtx *table; - hb_blob_t *blob; - const HVARVVAR *var_table; - hb_blob_t *var_blob; + public: + hb_blob_ptr_t table; + hb_blob_ptr_t var_table; }; + /* get advance: when no variations, call get_advance_without_var_unscaled. + * when there're variations, get advance value from mtx_map in subset_plan*/ + unsigned get_new_gid_advance_unscaled (const hb_subset_plan_t *plan, + const hb_hashmap_t> *mtx_map, + unsigned new_gid, + const accelerator_t &_mtx) const + { + if (mtx_map->is_empty ()) + { + hb_codepoint_t old_gid = 0; + return plan->old_gid_for_new_gid (new_gid, &old_gid) ? + _mtx.get_advance_without_var_unscaled (old_gid) : 0; + } + return mtx_map->get (new_gid).first; + } + protected: - LongMetric longMetric[VAR]; /* Paired advance width and leading - * bearing values for each glyph. The - * value numOfHMetrics comes from - * the 'hhea' table. If the font is - * monospaced, only one entry need - * be in the array, but that entry is - * required. The last entry applies to - * all subsequent glyphs. */ - FWORD leadingBearingX[VAR]; /* Here the advance is assumed - * to be the same as the advance - * for the last entry above. The - * number of entries in this array is - * derived from numGlyphs (from 'maxp' - * table) minus numberOfLongMetrics. - * This generally is used with a run - * of monospaced glyphs (e.g., Kanji - * fonts or Courier fonts). Only one - * run is allowed and it must be at - * the end. This allows a monospaced - * font to vary the side bearing - * values for each glyph. */ + UnsizedArrayOf + longMetricZ; /* Paired advance width and leading + * bearing values for each glyph. The + * value numOfHMetrics comes from + * the 'hhea' table. If the font is + * monospaced, only one entry need + * be in the array, but that entry is + * required. The last entry applies to + * all subsequent glyphs. */ +/*UnsizedArrayOf leadingBearingX;*/ + /* Here the advance is assumed + * to be the same as the advance + * for the last entry above. The + * number of entries in this array is + * derived from numGlyphs (from 'maxp' + * table) minus numberOfLongMetrics. + * This generally is used with a run + * of monospaced glyphs (e.g., Kanji + * fonts or Courier fonts). Only one + * run is allowed and it must be at + * the end. This allows a monospaced + * font to vary the side bearing + * values for each glyph. */ +/*UnsizedArrayOfadvancesX;*/ + /* TODO Document. */ public: - DEFINE_SIZE_ARRAY2 (0, longMetric, leadingBearingX); + DEFINE_SIZE_ARRAY (0, longMetricZ); }; -struct hmtx : hmtxvmtx { - static const hb_tag_t tableTag = HB_OT_TAG_hmtx; - static const hb_tag_t headerTag = HB_OT_TAG_hhea; - static const hb_tag_t variationsTag = HB_OT_TAG_HVAR; - static const hb_tag_t os2Tag = HB_OT_TAG_os2; +struct hmtx : hmtxvmtx { + static constexpr hb_tag_t tableTag = HB_OT_TAG_hmtx; + static constexpr hb_tag_t variationsTag = HB_OT_TAG_HVAR; + static constexpr bool is_horizontal = true; }; -struct vmtx : hmtxvmtx { - static const hb_tag_t tableTag = HB_OT_TAG_vmtx; - static const hb_tag_t headerTag = HB_OT_TAG_vhea; - static const hb_tag_t variationsTag = HB_OT_TAG_VVAR; - static const hb_tag_t os2Tag = HB_TAG_NONE; +struct vmtx : hmtxvmtx { + static constexpr hb_tag_t tableTag = HB_OT_TAG_vmtx; + static constexpr hb_tag_t variationsTag = HB_OT_TAG_VVAR; + static constexpr bool is_horizontal = false; +}; + +struct hmtx_accelerator_t : hmtx::accelerator_t { + hmtx_accelerator_t (hb_face_t *face) : hmtx::accelerator_t (face) {} +}; +struct vmtx_accelerator_t : vmtx::accelerator_t { + vmtx_accelerator_t (hb_face_t *face) : vmtx::accelerator_t (face) {} }; } /* namespace OT */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-kern-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-kern-table.hh index e07faca63f3..ffa11bc2496 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-kern-table.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-kern-table.hh @@ -27,365 +27,330 @@ #ifndef HB_OT_KERN_TABLE_HH #define HB_OT_KERN_TABLE_HH -#include "hb-open-type-private.hh" - -namespace OT { +#include "hb-aat-layout-kerx-table.hh" /* * kern -- Kerning + * https://docs.microsoft.com/en-us/typography/opentype/spec/kern + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html */ - #define HB_OT_TAG_kern HB_TAG('k','e','r','n') -struct hb_glyph_pair_t -{ - hb_codepoint_t left; - hb_codepoint_t right; -}; -struct KernPair -{ - inline int get_kerning (void) const - { return value; } +namespace OT { - inline int cmp (const hb_glyph_pair_t &o) const + +template +struct KernSubTableFormat3 +{ + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const { - int ret = left.cmp (o.left); - if (ret) return ret; - return right.cmp (o.right); + hb_array_t kernValue = kernValueZ.as_array (kernValueCount); + hb_array_t leftClass = StructAfter> (kernValue).as_array (glyphCount); + hb_array_t rightClass = StructAfter> (leftClass).as_array (glyphCount); + hb_array_t kernIndex = StructAfter> (rightClass).as_array (leftClassCount * rightClassCount); + + unsigned int leftC = leftClass[left]; + unsigned int rightC = rightClass[right]; + if (unlikely (leftC >= leftClassCount || rightC >= rightClassCount)) + return 0; + unsigned int i = leftC * rightClassCount + rightC; + return kernValue[kernIndex[i]]; } - inline bool sanitize (hb_sanitize_context_t *c) const + bool apply (AAT::hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + hb_kern_machine_t machine (*this, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + c->check_range (kernValueZ, + kernValueCount * sizeof (FWORD) + + glyphCount * 2 + + leftClassCount * rightClassCount)); + } + + protected: + KernSubTableHeader + header; + HBUINT16 glyphCount; /* The number of glyphs in this font. */ + HBUINT8 kernValueCount; /* The number of kerning values. */ + HBUINT8 leftClassCount; /* The number of left-hand classes. */ + HBUINT8 rightClassCount;/* The number of right-hand classes. */ + HBUINT8 flags; /* Set to zero (reserved for future use). */ + UnsizedArrayOf + kernValueZ; /* The kerning values. + * Length kernValueCount. */ +#if 0 + UnsizedArrayOf + leftClass; /* The left-hand classes. + * Length glyphCount. */ + UnsizedArrayOf + rightClass; /* The right-hand classes. + * Length glyphCount. */ + UnsizedArrayOfkernIndex; + /* The indices into the kernValue array. + * Length leftClassCount * rightClassCount */ +#endif + public: + DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 6, kernValueZ); +}; + +template +struct KernSubTable +{ + unsigned int get_size () const { return u.header.length; } + unsigned int get_type () const { return u.header.format; } + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + switch (get_type ()) { + /* This method hooks up to hb_font_t's get_h_kerning. Only support Format0. */ + case 0: return u.format0.get_kerning (left, right); + default:return 0; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case 0: return_trace (c->dispatch (u.format0)); +#ifndef HB_NO_AAT_SHAPE + case 1: return_trace (u.header.apple ? c->dispatch (u.format1, std::forward (ds)...) : c->default_return_value ()); +#endif + case 2: return_trace (c->dispatch (u.format2)); +#ifndef HB_NO_AAT_SHAPE + case 3: return_trace (u.header.apple ? c->dispatch (u.format3, std::forward (ds)...) : c->default_return_value ()); +#endif + default: return_trace (c->default_return_value ()); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.header.sanitize (c) || + u.header.length < u.header.min_size || + !c->check_range (this, u.header.length))) return_trace (false); + + return_trace (dispatch (c)); + } + + public: + union { + KernSubTableHeader header; + AAT::KerxSubTableFormat0 format0; + AAT::KerxSubTableFormat1 format1; + AAT::KerxSubTableFormat2 format2; + KernSubTableFormat3 format3; + } u; + public: + DEFINE_SIZE_MIN (KernSubTableHeader::static_size); +}; + + +struct KernOTSubTableHeader +{ + static constexpr bool apple = false; + typedef AAT::ObsoleteTypes Types; + + unsigned tuple_count () const { return 0; } + bool is_horizontal () const { return (coverage & Horizontal); } + + enum Coverage + { + Horizontal = 0x01u, + Minimum = 0x02u, + CrossStream = 0x04u, + Override = 0x08u, + + /* Not supported: */ + Backwards = 0x00u, + Variation = 0x00u, + }; + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } - protected: - GlyphID left; - GlyphID right; - FWORD value; + public: + HBUINT16 versionZ; /* Unused. */ + HBUINT16 length; /* Length of the subtable (including this header). */ + HBUINT8 format; /* Subtable format. */ + HBUINT8 coverage; /* Coverage bits. */ public: DEFINE_SIZE_STATIC (6); }; -struct KernSubTableFormat0 +struct KernOT : AAT::KerxTable { - inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const - { - hb_glyph_pair_t pair = {left, right}; - int i = pairs.bsearch (pair); - if (i == -1) - return 0; - return pairs[i].get_kerning (); - } + friend struct AAT::KerxTable; - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (pairs.sanitize (c)); - } + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; + static constexpr unsigned minVersion = 0u; + + typedef KernOTSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KernSubTable SubTable; protected: - BinSearchArrayOf pairs; /* Array of kerning pairs. */ + HBUINT16 version; /* Version--0x0000u */ + HBUINT16 tableCount; /* Number of subtables in the kerning table. */ + SubTable firstSubTable; /* Subtables. */ public: - DEFINE_SIZE_ARRAY (8, pairs); + DEFINE_SIZE_MIN (4); }; -struct KernClassTable -{ - inline unsigned int get_class (hb_codepoint_t g) const { return classes[g - firstGlyph]; } - inline bool sanitize (hb_sanitize_context_t *c) const +struct KernAATSubTableHeader +{ + static constexpr bool apple = true; + typedef AAT::ObsoleteTypes Types; + + unsigned tuple_count () const { return 0; } + bool is_horizontal () const { return !(coverage & Vertical); } + + enum Coverage + { + Vertical = 0x80u, + CrossStream = 0x40u, + Variation = 0x20u, + + /* Not supported: */ + Backwards = 0x00u, + }; + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (firstGlyph.sanitize (c) && classes.sanitize (c)); + return_trace (c->check_struct (this)); } - protected: - UINT16 firstGlyph; /* First glyph in class range. */ - ArrayOf classes; /* Glyph classes. */ public: - DEFINE_SIZE_ARRAY (4, classes); + HBUINT32 length; /* Length of the subtable (including this header). */ + HBUINT8 coverage; /* Coverage bits. */ + HBUINT8 format; /* Subtable format. */ + HBUINT16 tupleIndex; /* The tuple index (used for variations fonts). + * This value specifies which tuple this subtable covers. + * Note: We don't implement. */ + public: + DEFINE_SIZE_STATIC (8); }; -struct KernSubTableFormat2 +struct KernAAT : AAT::KerxTable { - inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const - { - unsigned int l = (this+leftClassTable).get_class (left); - unsigned int r = (this+leftClassTable).get_class (left); - unsigned int offset = l * rowWidth + r * sizeof (FWORD); - const FWORD *arr = &(this+array); - if (unlikely ((const void *) arr < (const void *) this || (const void *) arr >= (const void *) end)) - return 0; - const FWORD *v = &StructAtOffset (arr, offset); - if (unlikely ((const void *) v < (const void *) arr || (const void *) (v + 1) > (const void *) end)) - return 0; - return *v; - } + friend struct AAT::KerxTable; - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (rowWidth.sanitize (c) && - leftClassTable.sanitize (c, this) && - rightClassTable.sanitize (c, this) && - array.sanitize (c, this)); - } + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; + static constexpr unsigned minVersion = 0x00010000u; + + typedef KernAATSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KernSubTable SubTable; protected: - UINT16 rowWidth; /* The width, in bytes, of a row in the table. */ - OffsetTo - leftClassTable; /* Offset from beginning of this subtable to - * left-hand class table. */ - OffsetTo - rightClassTable;/* Offset from beginning of this subtable to - * right-hand class table. */ - OffsetTo - array; /* Offset from beginning of this subtable to - * the start of the kerning array. */ + HBUINT32 version; /* Version--0x00010000u */ + HBUINT32 tableCount; /* Number of subtables in the kerning table. */ + SubTable firstSubTable; /* Subtables. */ public: DEFINE_SIZE_MIN (8); }; -struct KernSubTable -{ - inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end, unsigned int format) const - { - switch (format) { - case 0: return u.format0.get_kerning (left, right); - case 2: return u.format2.get_kerning (left, right, end); - default:return 0; - } - } - - inline bool sanitize (hb_sanitize_context_t *c, unsigned int format) const - { - TRACE_SANITIZE (this); - switch (format) { - case 0: return_trace (u.format0.sanitize (c)); - case 2: return_trace (u.format2.sanitize (c)); - default:return_trace (true); - } - } - - protected: - union { - KernSubTableFormat0 format0; - KernSubTableFormat2 format2; - } u; - public: - DEFINE_SIZE_MIN (0); -}; - - -template -struct KernSubTableWrapper -{ - /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ - inline const T* thiz (void) const { return static_cast (this); } - - inline bool is_horizontal (void) const - { return (thiz()->coverage & T::COVERAGE_CHECK_FLAGS) == T::COVERAGE_CHECK_HORIZONTAL; } - - inline bool is_override (void) const - { return bool (thiz()->coverage & T::COVERAGE_OVERRIDE_FLAG); } - - inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const - { return thiz()->subtable.get_kerning (left, right, end, thiz()->format); } - - inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const - { return is_horizontal () ? get_kerning (left, right, end) : 0; } - - inline unsigned int get_size (void) const { return thiz()->length; } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (thiz()) && - thiz()->length >= thiz()->min_size && - c->check_array (thiz(), 1, thiz()->length) && - thiz()->subtable.sanitize (c, thiz()->format)); - } -}; - -template -struct KernTable -{ - /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ - inline const T* thiz (void) const { return static_cast (this); } - - inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const - { - int v = 0; - const typename T::SubTableWrapper *st = CastP (thiz()->data); - unsigned int count = thiz()->nTables; - for (unsigned int i = 0; i < count; i++) - { - if (st->is_override ()) - v = 0; - v += st->get_h_kerning (left, right, table_length + (const char *) this); - st = &StructAfter (*st); - } - return v; - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (unlikely (!c->check_struct (thiz()) || - thiz()->version != T::VERSION)) - return_trace (false); - - const typename T::SubTableWrapper *st = CastP (thiz()->data); - unsigned int count = thiz()->nTables; - for (unsigned int i = 0; i < count; i++) - { - if (unlikely (!st->sanitize (c))) - return_trace (false); - st = &StructAfter (*st); - } - - return_trace (true); - } -}; - -struct KernOT : KernTable -{ - friend struct KernTable; - - static const uint16_t VERSION = 0x0000u; - - struct SubTableWrapper : KernSubTableWrapper - { - friend struct KernSubTableWrapper; - - enum coverage_flags_t { - COVERAGE_DIRECTION_FLAG = 0x01u, - COVERAGE_MINIMUM_FLAG = 0x02u, - COVERAGE_CROSSSTREAM_FLAG = 0x04u, - COVERAGE_OVERRIDE_FLAG = 0x08u, - - COVERAGE_VARIATION_FLAG = 0x00u, /* Not supported. */ - - COVERAGE_CHECK_FLAGS = 0x07u, - COVERAGE_CHECK_HORIZONTAL = 0x01u - }; - - protected: - UINT16 versionZ; /* Unused. */ - UINT16 length; /* Length of the subtable (including this header). */ - UINT8 format; /* Subtable format. */ - UINT8 coverage; /* Coverage bits. */ - KernSubTable subtable; /* Subtable data. */ - public: - DEFINE_SIZE_MIN (6); - }; - - protected: - UINT16 version; /* Version--0x0000u */ - UINT16 nTables; /* Number of subtables in the kerning table. */ - UINT8 data[VAR]; - public: - DEFINE_SIZE_ARRAY (4, data); -}; - -struct KernAAT : KernTable -{ - friend struct KernTable; - - static const uint32_t VERSION = 0x00010000u; - - struct SubTableWrapper : KernSubTableWrapper - { - friend struct KernSubTableWrapper; - - enum coverage_flags_t { - COVERAGE_DIRECTION_FLAG = 0x80u, - COVERAGE_CROSSSTREAM_FLAG = 0x40u, - COVERAGE_VARIATION_FLAG = 0x20u, - - COVERAGE_OVERRIDE_FLAG = 0x00u, /* Not supported. */ - - COVERAGE_CHECK_FLAGS = 0xE0u, - COVERAGE_CHECK_HORIZONTAL = 0x00u - }; - - protected: - UINT32 length; /* Length of the subtable (including this header). */ - UINT8 coverage; /* Coverage bits. */ - UINT8 format; /* Subtable format. */ - UINT16 tupleIndex; /* The tuple index (used for variations fonts). - * This value specifies which tuple this subtable covers. */ - KernSubTable subtable; /* Subtable data. */ - public: - DEFINE_SIZE_MIN (8); - }; - - protected: - UINT32 version; /* Version--0x00010000u */ - UINT32 nTables; /* Number of subtables in the kerning table. */ - UINT8 data[VAR]; - public: - DEFINE_SIZE_ARRAY (8, data); -}; - struct kern { - static const hb_tag_t tableTag = HB_OT_TAG_kern; + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; - inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const + bool has_data () const { return u.version32; } + unsigned get_type () const { return u.major; } + + bool has_state_machine () const { - switch (u.major) { - case 0: return u.ot.get_h_kerning (left, right, table_length); - case 1: return u.aat.get_h_kerning (left, right, table_length); + switch (get_type ()) { + case 0: return u.ot.has_state_machine (); +#ifndef HB_NO_AAT_SHAPE + case 1: return u.aat.has_state_machine (); +#endif + default:return false; + } + } + + bool has_cross_stream () const + { + switch (get_type ()) { + case 0: return u.ot.has_cross_stream (); +#ifndef HB_NO_AAT_SHAPE + case 1: return u.aat.has_cross_stream (); +#endif + default:return false; + } + } + + int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + switch (get_type ()) { + case 0: return u.ot.get_h_kerning (left, right); +#ifndef HB_NO_AAT_SHAPE + case 1: return u.aat.get_h_kerning (left, right); +#endif default:return 0; } } - inline bool sanitize (hb_sanitize_context_t *c) const + bool apply (AAT::hb_aat_apply_context_t *c) const + { return dispatch (c); } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - TRACE_SANITIZE (this); - if (!u.major.sanitize (c)) return_trace (false); - switch (u.major) { - case 0: return_trace (u.ot.sanitize (c)); - case 1: return_trace (u.aat.sanitize (c)); - default:return_trace (true); + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case 0: return_trace (c->dispatch (u.ot, std::forward (ds)...)); +#ifndef HB_NO_AAT_SHAPE + case 1: return_trace (c->dispatch (u.aat, std::forward (ds)...)); +#endif + default: return_trace (c->default_return_value ()); } } - struct accelerator_t + bool sanitize (hb_sanitize_context_t *c) const { - inline void init (hb_face_t *face) - { - blob = Sanitizer::sanitize (face->reference_table (HB_OT_TAG_kern)); - table = Sanitizer::lock_instance (blob); - table_length = hb_blob_get_length (blob); - } - inline void fini (void) - { - hb_blob_destroy (blob); - } - - inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const - { return table->get_h_kerning (left, right, table_length); } - - private: - hb_blob_t *blob; - const kern *table; - unsigned int table_length; - }; + TRACE_SANITIZE (this); + if (!u.version32.sanitize (c)) return_trace (false); + return_trace (dispatch (c)); + } protected: union { - UINT16 major; + HBUINT32 version32; + HBUINT16 major; KernOT ot; +#ifndef HB_NO_AAT_SHAPE KernAAT aat; +#endif } u; public: - DEFINE_SIZE_UNION (2, major); + DEFINE_SIZE_UNION (4, version32); }; } /* namespace OT */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-layout-base-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-layout-base-table.hh new file mode 100644 index 00000000000..8179e5acd52 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-layout-base-table.hh @@ -0,0 +1,525 @@ +/* + * Copyright ยฉ 2016 Elie Roux + * Copyright ยฉ 2018 Google, Inc. + * Copyright ยฉ 2018-2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_LAYOUT_BASE_TABLE_HH +#define HB_OT_LAYOUT_BASE_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" + +namespace OT { + +/* + * BASE -- Baseline + * https://docs.microsoft.com/en-us/typography/opentype/spec/base + */ + +struct BaseCoordFormat1 +{ + hb_position_t get_coord (hb_font_t *font, hb_direction_t direction) const + { + return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + FWORD coordinate; /* X or Y value, in design units */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BaseCoordFormat2 +{ + hb_position_t get_coord (hb_font_t *font, hb_direction_t direction) const + { + /* TODO */ + return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + FWORD coordinate; /* X or Y value, in design units */ + HBGlyphID16 referenceGlyph; /* Glyph ID of control glyph */ + HBUINT16 coordPoint; /* Index of contour point on the + * reference glyph */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct BaseCoordFormat3 +{ + hb_position_t get_coord (hb_font_t *font, + const VariationStore &var_store, + hb_direction_t direction) const + { + const Device &device = this+deviceTable; + + return HB_DIRECTION_IS_HORIZONTAL (direction) + ? font->em_scale_y (coordinate) + device.get_y_delta (font, var_store) + : font->em_scale_x (coordinate) + device.get_x_delta (font, var_store); + } + + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + deviceTable.sanitize (c, this))); + } + + protected: + HBUINT16 format; /* Format identifier--format = 3 */ + FWORD coordinate; /* X or Y value, in design units */ + Offset16To + deviceTable; /* Offset to Device table for X or + * Y value, from beginning of + * BaseCoord table (may be NULL). */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseCoord +{ + bool has_data () const { return u.format; } + + hb_position_t get_coord (hb_font_t *font, + const VariationStore &var_store, + hb_direction_t direction) const + { + switch (u.format) { + case 1: return u.format1.get_coord (font, direction); + case 2: return u.format2.get_coord (font, direction); + case 3: return u.format3.get_coord (font, var_store, direction); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.format.sanitize (c))) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + default:return_trace (false); + } + } + + protected: + union { + HBUINT16 format; + BaseCoordFormat1 format1; + BaseCoordFormat2 format2; + BaseCoordFormat3 format3; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + +struct FeatMinMaxRecord +{ + int cmp (hb_tag_t key) const { return tag.cmp (key); } + + bool has_data () const { return tag; } + + void get_min_max (const BaseCoord **min, const BaseCoord **max) const + { + if (likely (min)) *min = &(this+minCoord); + if (likely (max)) *max = &(this+maxCoord); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + minCoord.sanitize (c, this) && + maxCoord.sanitize (c, this))); + } + + protected: + Tag tag; /* 4-byte feature identification tag--must + * match feature tag in FeatureList */ + Offset16To + minCoord; /* Offset to BaseCoord table that defines + * the minimum extent value, from beginning + * of MinMax table (may be NULL) */ + Offset16To + maxCoord; /* Offset to BaseCoord table that defines + * the maximum extent value, from beginning + * of MinMax table (may be NULL) */ + public: + DEFINE_SIZE_STATIC (8); + +}; + +struct MinMax +{ + void get_min_max (hb_tag_t feature_tag, + const BaseCoord **min, + const BaseCoord **max) const + { + const FeatMinMaxRecord &minMaxCoord = featMinMaxRecords.bsearch (feature_tag); + if (minMaxCoord.has_data ()) + minMaxCoord.get_min_max (min, max); + else + { + if (likely (min)) *min = &(this+minCoord); + if (likely (max)) *max = &(this+maxCoord); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + minCoord.sanitize (c, this) && + maxCoord.sanitize (c, this) && + featMinMaxRecords.sanitize (c, this))); + } + + protected: + Offset16To + minCoord; /* Offset to BaseCoord table that defines + * minimum extent value, from the beginning + * of MinMax table (may be NULL) */ + Offset16To + maxCoord; /* Offset to BaseCoord table that defines + * maximum extent value, from the beginning + * of MinMax table (may be NULL) */ + SortedArray16Of + featMinMaxRecords; + /* Array of FeatMinMaxRecords, in alphabetical + * order by featureTableTag */ + public: + DEFINE_SIZE_ARRAY (6, featMinMaxRecords); +}; + +struct BaseValues +{ + const BaseCoord &get_base_coord (int baseline_tag_index) const + { + if (baseline_tag_index == -1) baseline_tag_index = defaultIndex; + return this+baseCoords[baseline_tag_index]; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + baseCoords.sanitize (c, this))); + } + + protected: + Index defaultIndex; /* Index number of default baseline for this + * script โ€” equals index position of baseline tag + * in baselineTags array of the BaseTagList */ + Array16OfOffset16To + baseCoords; /* Number of BaseCoord tables defined โ€” should equal + * baseTagCount in the BaseTagList + * + * Array of offsets to BaseCoord tables, from beginning of + * BaseValues table โ€” order matches baselineTags array in + * the BaseTagList */ + public: + DEFINE_SIZE_ARRAY (4, baseCoords); +}; + +struct BaseLangSysRecord +{ + int cmp (hb_tag_t key) const { return baseLangSysTag.cmp (key); } + + bool has_data () const { return baseLangSysTag; } + + const MinMax &get_min_max () const { return this+minMax; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + minMax.sanitize (c, this))); + } + + protected: + Tag baseLangSysTag; /* 4-byte language system identification tag */ + Offset16To + minMax; /* Offset to MinMax table, from beginning + * of BaseScript table */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseScript +{ + const MinMax &get_min_max (hb_tag_t language_tag) const + { + const BaseLangSysRecord& record = baseLangSysRecords.bsearch (language_tag); + return record.has_data () ? record.get_min_max () : this+defaultMinMax; + } + + const BaseCoord &get_base_coord (int baseline_tag_index) const + { return (this+baseValues).get_base_coord (baseline_tag_index); } + + bool has_data () const { return baseValues; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + baseValues.sanitize (c, this) && + defaultMinMax.sanitize (c, this) && + baseLangSysRecords.sanitize (c, this))); + } + + protected: + Offset16To + baseValues; /* Offset to BaseValues table, from beginning + * of BaseScript table (may be NULL) */ + Offset16To + defaultMinMax; /* Offset to MinMax table, from beginning of + * BaseScript table (may be NULL) */ + SortedArray16Of + baseLangSysRecords; + /* Number of BaseLangSysRecords + * defined โ€” may be zero (0) */ + + public: + DEFINE_SIZE_ARRAY (6, baseLangSysRecords); +}; + +struct BaseScriptList; +struct BaseScriptRecord +{ + int cmp (hb_tag_t key) const { return baseScriptTag.cmp (key); } + + bool has_data () const { return baseScriptTag; } + + const BaseScript &get_base_script (const BaseScriptList *list) const + { return list+baseScript; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + baseScript.sanitize (c, base))); + } + + protected: + Tag baseScriptTag; /* 4-byte script identification tag */ + Offset16To + baseScript; /* Offset to BaseScript table, from beginning + * of BaseScriptList */ + + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseScriptList +{ + const BaseScript &get_base_script (hb_tag_t script) const + { + const BaseScriptRecord *record = &baseScriptRecords.bsearch (script); + if (!record->has_data ()) record = &baseScriptRecords.bsearch (HB_TAG ('D','F','L','T')); + return record->has_data () ? record->get_base_script (this) : Null (BaseScript); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + baseScriptRecords.sanitize (c, this)); + } + + protected: + SortedArray16Of + baseScriptRecords; + + public: + DEFINE_SIZE_ARRAY (2, baseScriptRecords); +}; + +struct Axis +{ + bool get_baseline (hb_tag_t baseline_tag, + hb_tag_t script_tag, + hb_tag_t language_tag, + const BaseCoord **coord) const + { + const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); + if (!base_script.has_data ()) + { + *coord = nullptr; + return false; + } + + if (likely (coord)) + { + unsigned int tag_index = 0; + if (!(this+baseTagList).bfind (baseline_tag, &tag_index)) + { + *coord = nullptr; + return false; + } + *coord = &base_script.get_base_coord (tag_index); + } + + return true; + } + + bool get_min_max (hb_tag_t script_tag, + hb_tag_t language_tag, + hb_tag_t feature_tag, + const BaseCoord **min_coord, + const BaseCoord **max_coord) const + { + const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); + if (!base_script.has_data ()) + { + *min_coord = *max_coord = nullptr; + return false; + } + + base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord); + + return true; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (this+baseTagList).sanitize (c) && + (this+baseScriptList).sanitize (c))); + } + + protected: + Offset16To> + baseTagList; /* Offset to BaseTagList table, from beginning + * of Axis table (may be NULL) + * Array of 4-byte baseline identification tags โ€” must + * be in alphabetical order */ + Offset16To + baseScriptList; /* Offset to BaseScriptList table, from beginning + * of Axis table + * Array of BaseScriptRecords, in alphabetical order + * by baseScriptTag */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BASE +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_BASE; + + const Axis &get_axis (hb_direction_t direction) const + { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; } + + const VariationStore &get_var_store () const + { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; } + + bool get_baseline (hb_font_t *font, + hb_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *base) const + { + const BaseCoord *base_coord = nullptr; + if (unlikely (!get_axis (direction).get_baseline (baseline_tag, script_tag, language_tag, &base_coord) || + !base_coord || !base_coord->has_data ())) + return false; + + if (likely (base)) + *base = base_coord->get_coord (font, get_var_store (), direction); + + return true; + } + + /* TODO: Expose this separately sometime? */ + bool get_min_max (hb_font_t *font, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_tag_t feature_tag, + hb_position_t *min, + hb_position_t *max) + { + const BaseCoord *min_coord, *max_coord; + if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag, + &min_coord, &max_coord)) + return false; + + const VariationStore &var_store = get_var_store (); + if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction); + if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction); + return true; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + likely (version.major == 1) && + hAxis.sanitize (c, this) && + vAxis.sanitize (c, this) && + (version.to_int () < 0x00010001u || varStore.sanitize (c, this)))); + } + + protected: + FixedVersion<>version; /* Version of the BASE table */ + Offset16TohAxis; /* Offset to horizontal Axis table, from beginning + * of BASE table (may be NULL) */ + Offset16TovAxis; /* Offset to vertical Axis table, from beginning + * of BASE table (may be NULL) */ + Offset32To + varStore; /* Offset to the table of Item Variation + * Store--from beginning of BASE + * header (may be NULL). Introduced + * in version 0x00010001. */ + public: + DEFINE_SIZE_MIN (8); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_BASE_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-layout-common-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-layout-common-private.hh deleted file mode 100644 index 5e699e1967f..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-layout-common-private.hh +++ /dev/null @@ -1,1772 +0,0 @@ -/* - * Copyright ยฉ 2007,2008,2009 Red Hat, Inc. - * Copyright ยฉ 2010,2012 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH -#define HB_OT_LAYOUT_COMMON_PRIVATE_HH - -#include "hb-private.hh" -#include "hb-debug.hh" -#include "hb-ot-layout-private.hh" -#include "hb-open-type-private.hh" -#include "hb-set-private.hh" - - -#ifndef HB_MAX_NESTING_LEVEL -#define HB_MAX_NESTING_LEVEL 6 -#endif -#ifndef HB_MAX_CONTEXT_LENGTH -#define HB_MAX_CONTEXT_LENGTH 64 -#endif - - -namespace OT { - - -#define NOT_COVERED ((unsigned int) -1) - - - -/* - * - * OpenType Layout Common Table Formats - * - */ - - -/* - * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList - */ - -template -struct Record -{ - inline int cmp (hb_tag_t a) const { - return tag.cmp (a); - } - - struct sanitize_closure_t { - hb_tag_t tag; - const void *list_base; - }; - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const - { - TRACE_SANITIZE (this); - const sanitize_closure_t closure = {tag, base}; - return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure)); - } - - Tag tag; /* 4-byte Tag identifier */ - OffsetTo - offset; /* Offset from beginning of object holding - * the Record */ - public: - DEFINE_SIZE_STATIC (6); -}; - -template -struct RecordArrayOf : SortedArrayOf > { - inline const Tag& get_tag (unsigned int i) const - { - /* We cheat slightly and don't define separate Null objects - * for Record types. Instead, we return the correct Null(Tag) - * here. */ - if (unlikely (i >= this->len)) return Null(Tag); - return (*this)[i].tag; - } - inline unsigned int get_tags (unsigned int start_offset, - unsigned int *record_count /* IN/OUT */, - hb_tag_t *record_tags /* OUT */) const - { - if (record_count) { - const Record *arr = this->sub_array (start_offset, record_count); - unsigned int count = *record_count; - for (unsigned int i = 0; i < count; i++) - record_tags[i] = arr[i].tag; - } - return this->len; - } - inline bool find_index (hb_tag_t tag, unsigned int *index) const - { - /* If we want to allow non-sorted data, we can lsearch(). */ - int i = this->/*lsearch*/bsearch (tag); - if (i != -1) { - if (index) *index = i; - return true; - } else { - if (index) *index = Index::NOT_FOUND_INDEX; - return false; - } - } -}; - -template -struct RecordListOf : RecordArrayOf -{ - inline const Type& operator [] (unsigned int i) const - { return this+RecordArrayOf::operator [](i).offset; } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (RecordArrayOf::sanitize (c, this)); - } -}; - - -struct RangeRecord -{ - inline int cmp (hb_codepoint_t g) const { - return g < start ? -1 : g <= end ? 0 : +1 ; - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - inline bool intersects (const hb_set_t *glyphs) const { - return glyphs->intersects (start, end); - } - - template - inline bool add_coverage (set_t *glyphs) const { - return glyphs->add_range (start, end); - } - - GlyphID start; /* First GlyphID in the range */ - GlyphID end; /* Last GlyphID in the range */ - UINT16 value; /* Value */ - public: - DEFINE_SIZE_STATIC (6); -}; -DEFINE_NULL_DATA (RangeRecord, "\000\001"); - - -struct IndexArray : ArrayOf -{ - inline unsigned int get_indexes (unsigned int start_offset, - unsigned int *_count /* IN/OUT */, - unsigned int *_indexes /* OUT */) const - { - if (_count) { - const UINT16 *arr = this->sub_array (start_offset, _count); - unsigned int count = *_count; - for (unsigned int i = 0; i < count; i++) - _indexes[i] = arr[i]; - } - return this->len; - } -}; - - -struct Script; -struct LangSys; -struct Feature; - - -struct LangSys -{ - inline unsigned int get_feature_count (void) const - { return featureIndex.len; } - inline hb_tag_t get_feature_index (unsigned int i) const - { return featureIndex[i]; } - inline unsigned int get_feature_indexes (unsigned int start_offset, - unsigned int *feature_count /* IN/OUT */, - unsigned int *feature_indexes /* OUT */) const - { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); } - - inline bool has_required_feature (void) const { return reqFeatureIndex != 0xFFFFu; } - inline unsigned int get_required_feature_index (void) const - { - if (reqFeatureIndex == 0xFFFFu) - return Index::NOT_FOUND_INDEX; - return reqFeatureIndex;; - } - - inline bool sanitize (hb_sanitize_context_t *c, - const Record::sanitize_closure_t * = nullptr) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && featureIndex.sanitize (c)); - } - - Offset16 lookupOrderZ; /* = Null (reserved for an offset to a - * reordering table) */ - UINT16 reqFeatureIndex;/* Index of a feature required for this - * language system--if no required features - * = 0xFFFFu */ - IndexArray featureIndex; /* Array of indices into the FeatureList */ - public: - DEFINE_SIZE_ARRAY (6, featureIndex); -}; -DEFINE_NULL_DATA (LangSys, "\0\0\xFF\xFF"); - - -struct Script -{ - inline unsigned int get_lang_sys_count (void) const - { return langSys.len; } - inline const Tag& get_lang_sys_tag (unsigned int i) const - { return langSys.get_tag (i); } - inline unsigned int get_lang_sys_tags (unsigned int start_offset, - unsigned int *lang_sys_count /* IN/OUT */, - hb_tag_t *lang_sys_tags /* OUT */) const - { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); } - inline const LangSys& get_lang_sys (unsigned int i) const - { - if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys (); - return this+langSys[i].offset; - } - inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const - { return langSys.find_index (tag, index); } - - inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; } - inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; } - - inline bool sanitize (hb_sanitize_context_t *c, - const Record