Index: third_party/harfbuzz/src/harfbuzz-shaper.cpp |
diff --git a/third_party/harfbuzz/src/harfbuzz-shaper.cpp b/third_party/harfbuzz/src/harfbuzz-shaper.cpp |
deleted file mode 100644 |
index f1606e6a79813f5214248f76da56f2050b08d60b..0000000000000000000000000000000000000000 |
--- a/third_party/harfbuzz/src/harfbuzz-shaper.cpp |
+++ /dev/null |
@@ -1,1357 +0,0 @@ |
-/* |
- * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) |
- * |
- * This is part of HarfBuzz, an OpenType Layout engine 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 "harfbuzz-shaper.h" |
-#include "harfbuzz-shaper-private.h" |
- |
-#include "harfbuzz-stream-private.h" |
-#include <assert.h> |
-#include <stdio.h> |
- |
-#define HB_MIN(a, b) ((a) < (b) ? (a) : (b)) |
-#define HB_MAX(a, b) ((a) > (b) ? (a) : (b)) |
- |
-// ----------------------------------------------------------------------------------------------------- |
-// |
-// The line break algorithm. See http://www.unicode.org/reports/tr14/tr14-13.html |
-// |
-// ----------------------------------------------------------------------------------------------------- |
- |
-/* The Unicode algorithm does in our opinion allow line breaks at some |
- places they shouldn't be allowed. The following changes were thus |
- made in comparison to the Unicode reference: |
- |
- EX->AL from DB to IB |
- SY->AL from DB to IB |
- SY->PO from DB to IB |
- SY->PR from DB to IB |
- SY->OP from DB to IB |
- AL->PR from DB to IB |
- AL->PO from DB to IB |
- PR->PR from DB to IB |
- PO->PO from DB to IB |
- PR->PO from DB to IB |
- PO->PR from DB to IB |
- HY->PO from DB to IB |
- HY->PR from DB to IB |
- HY->OP from DB to IB |
- NU->EX from PB to IB |
- EX->PO from DB to IB |
-*/ |
- |
-// The following line break classes are not treated by the table: |
-// AI, BK, CB, CR, LF, NL, SA, SG, SP, XX |
- |
-enum break_class { |
- // the first 4 values have to agree with the enum in QCharAttributes |
- ProhibitedBreak, // PB in table |
- DirectBreak, // DB in table |
- IndirectBreak, // IB in table |
- CombiningIndirectBreak, // CI in table |
- CombiningProhibitedBreak // CP in table |
-}; |
-#define DB DirectBreak |
-#define IB IndirectBreak |
-#define CI CombiningIndirectBreak |
-#define CP CombiningProhibitedBreak |
-#define PB ProhibitedBreak |
- |
-static const hb_uint8 breakTable[HB_LineBreak_JT+1][HB_LineBreak_JT+1] = |
-{ |
-/* OP CL QU GL NS EX SY IS PR PO NU AL ID IN HY BA BB B2 ZW CM WJ H2 H3 JL JV JT */ |
-/* OP */ { PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, CP, PB, PB, PB, PB, PB, PB }, |
-/* CL */ { DB, PB, IB, IB, PB, PB, PB, PB, IB, IB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* QU */ { PB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB }, |
-/* GL */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB }, |
-/* NS */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* EX */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* SY */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* IS */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* PR */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, DB, IB, IB, DB, DB, PB, CI, PB, IB, IB, IB, IB, IB }, |
-/* PO */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* NU */ { IB, PB, IB, IB, IB, IB, PB, PB, IB, IB, IB, IB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* AL */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* ID */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* IN */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* HY */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, DB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* BA */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* BB */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB }, |
-/* B2 */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, DB, IB, IB, DB, PB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* ZW */ { DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, PB, DB, DB, DB, DB, DB, DB, DB }, |
-/* CM */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, IB, IB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB }, |
-/* WJ */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB }, |
-/* H2 */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, IB, IB }, |
-/* H3 */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, IB }, |
-/* JL */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, IB, IB, IB, IB, DB }, |
-/* JV */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, IB, IB }, |
-/* JT */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, IB } |
-}; |
-#undef DB |
-#undef IB |
-#undef CI |
-#undef CP |
-#undef PB |
- |
-static const hb_uint8 graphemeTable[HB_Grapheme_LVT + 1][HB_Grapheme_LVT + 1] = |
-{ |
-// Other, CR, LF, Control,Extend,L, V, T, LV, LVT |
- { true , true , true , true , true , true , true , true , true , true }, // Other, |
- { true , true , true , true , true , true , true , true , true , true }, // CR, |
- { true , false, true , true , true , true , true , true , true , true }, // LF, |
- { true , true , true , true , true , true , true , true , true , true }, // Control, |
- { false, true , true , true , false, false, false, false, false, false }, // Extend, |
- { true , true , true , true , true , false, true , true , true , true }, // L, |
- { true , true , true , true , true , false, false, true , false, true }, // V, |
- { true , true , true , true , true , true , false, false, false, false }, // T, |
- { true , true , true , true , true , false, true , true , true , true }, // LV, |
- { true , true , true , true , true , false, true , true , true , true }, // LVT |
-}; |
- |
-static void calcLineBreaks(const HB_UChar16 *uc, hb_uint32 len, HB_CharAttributes *charAttributes) |
-{ |
- if (!len) |
- return; |
- |
- // ##### can this fail if the first char is a surrogate? |
- HB_LineBreakClass cls; |
- HB_GraphemeClass grapheme; |
- HB_GetGraphemeAndLineBreakClass(*uc, &grapheme, &cls); |
- // handle case where input starts with an LF |
- if (cls == HB_LineBreak_LF) |
- cls = HB_LineBreak_BK; |
- |
- charAttributes[0].whiteSpace = (cls == HB_LineBreak_SP || cls == HB_LineBreak_BK); |
- charAttributes[0].charStop = true; |
- |
- int lcls = cls; |
- for (hb_uint32 i = 1; i < len; ++i) { |
- charAttributes[i].whiteSpace = false; |
- charAttributes[i].charStop = true; |
- |
- HB_UChar32 code = uc[i]; |
- HB_GraphemeClass ngrapheme; |
- HB_LineBreakClass ncls; |
- HB_GetGraphemeAndLineBreakClass(code, &ngrapheme, &ncls); |
- charAttributes[i].charStop = graphemeTable[ngrapheme][grapheme]; |
- // handle surrogates |
- if (ncls == HB_LineBreak_SG) { |
- if (HB_IsHighSurrogate(uc[i]) && i < len - 1 && HB_IsLowSurrogate(uc[i+1])) { |
- continue; |
- } else if (HB_IsLowSurrogate(uc[i]) && HB_IsHighSurrogate(uc[i-1])) { |
- code = HB_SurrogateToUcs4(uc[i-1], uc[i]); |
- HB_GetGraphemeAndLineBreakClass(code, &ngrapheme, &ncls); |
- charAttributes[i].charStop = false; |
- } else { |
- ncls = HB_LineBreak_AL; |
- } |
- } |
- |
- // set white space and char stop flag |
- if (ncls >= HB_LineBreak_SP) |
- charAttributes[i].whiteSpace = true; |
- |
- HB_LineBreakType lineBreakType = HB_NoBreak; |
- if (cls >= HB_LineBreak_LF) { |
- lineBreakType = HB_ForcedBreak; |
- } else if(cls == HB_LineBreak_CR) { |
- lineBreakType = (ncls == HB_LineBreak_LF) ? HB_NoBreak : HB_ForcedBreak; |
- } |
- |
- if (ncls == HB_LineBreak_SP) |
- goto next_no_cls_update; |
- if (ncls >= HB_LineBreak_CR) |
- goto next; |
- |
- // two complex chars (thai or lao), thai_attributes might override, but here we do a best guess |
- if (cls == HB_LineBreak_SA && ncls == HB_LineBreak_SA) { |
- lineBreakType = HB_Break; |
- goto next; |
- } |
- |
- { |
- int tcls = ncls; |
- if (tcls >= HB_LineBreak_SA) |
- tcls = HB_LineBreak_ID; |
- if (cls >= HB_LineBreak_SA) |
- cls = HB_LineBreak_ID; |
- |
- int brk = breakTable[cls][tcls]; |
- switch (brk) { |
- case DirectBreak: |
- lineBreakType = HB_Break; |
- if (uc[i-1] == 0xad) // soft hyphen |
- lineBreakType = HB_SoftHyphen; |
- break; |
- case IndirectBreak: |
- lineBreakType = (lcls == HB_LineBreak_SP) ? HB_Break : HB_NoBreak; |
- break; |
- case CombiningIndirectBreak: |
- lineBreakType = HB_NoBreak; |
- if (lcls == HB_LineBreak_SP){ |
- if (i > 1) |
- charAttributes[i-2].lineBreakType = HB_Break; |
- } else { |
- goto next_no_cls_update; |
- } |
- break; |
- case CombiningProhibitedBreak: |
- lineBreakType = HB_NoBreak; |
- if (lcls != HB_LineBreak_SP) |
- goto next_no_cls_update; |
- case ProhibitedBreak: |
- default: |
- break; |
- } |
- } |
- next: |
- cls = ncls; |
- next_no_cls_update: |
- lcls = ncls; |
- grapheme = ngrapheme; |
- charAttributes[i-1].lineBreakType = lineBreakType; |
- } |
- charAttributes[len-1].lineBreakType = HB_ForcedBreak; |
-} |
- |
-// -------------------------------------------------------------------------------------------------------------------------------------------- |
-// |
-// Basic processing |
-// |
-// -------------------------------------------------------------------------------------------------------------------------------------------- |
- |
-static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast) |
-{ |
- int nmarks = glast - gfrom; |
- assert(nmarks > 0); |
- |
- HB_Glyph *glyphs = item->glyphs; |
- HB_GlyphAttributes *attributes = item->attributes; |
- |
- HB_GlyphMetrics baseMetrics; |
- item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics); |
- |
- if (item->item.script == HB_Script_Hebrew |
- && (-baseMetrics.y) > baseMetrics.height) |
- // we need to attach below the baseline, because of the hebrew iud. |
- baseMetrics.height = -baseMetrics.y; |
- |
-// qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast); |
-// qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff); |
- |
- HB_Fixed size = item->font->klass->getFontMetric(item->font, HB_FontAscent) / 10; |
- HB_Fixed offsetBase = HB_FIXED_CONSTANT(1) + (size - HB_FIXED_CONSTANT(4)) / 4; |
- if (size > HB_FIXED_CONSTANT(4)) |
- offsetBase += HB_FIXED_CONSTANT(4); |
- else |
- offsetBase += size; |
- //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1; |
-// qDebug("offset = %f", offsetBase); |
- |
- bool rightToLeft = item->item.bidiLevel % 2; |
- |
- int i; |
- unsigned char lastCmb = 0; |
- HB_GlyphMetrics attachmentRect; |
- memset(&attachmentRect, 0, sizeof(attachmentRect)); |
- |
- for(i = 1; i <= nmarks; i++) { |
- HB_Glyph mark = glyphs[gfrom+i]; |
- HB_GlyphMetrics markMetrics; |
- item->font->klass->getGlyphMetrics(item->font, mark, &markMetrics); |
- HB_FixedPoint p; |
- p.x = p.y = 0; |
-// qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff); |
- |
- HB_Fixed offset = offsetBase; |
- unsigned char cmb = attributes[gfrom+i].combiningClass; |
- |
- // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some |
- // bits in the glyphAttributes structure. |
- if (cmb < 200) { |
- // fixed position classes. We approximate by mapping to one of the others. |
- // currently I added only the ones for arabic, hebrew, lao and thai. |
- |
- // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes) |
- |
- // add a bit more offset to arabic, a bit hacky |
- if (cmb >= 27 && cmb <= 36 && offset < 3) |
- offset +=1; |
- // below |
- if ((cmb >= 10 && cmb <= 18) || |
- cmb == 20 || cmb == 22 || |
- cmb == 29 || cmb == 32) |
- cmb = HB_Combining_Below; |
- // above |
- else if (cmb == 23 || cmb == 27 || cmb == 28 || |
- cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36)) |
- cmb = HB_Combining_Above; |
- //below-right |
- else if (cmb == 9 || cmb == 103 || cmb == 118) |
- cmb = HB_Combining_BelowRight; |
- // above-right |
- else if (cmb == 24 || cmb == 107 || cmb == 122) |
- cmb = HB_Combining_AboveRight; |
- else if (cmb == 25) |
- cmb = HB_Combining_AboveLeft; |
- // fixed: |
- // 19 21 |
- |
- } |
- |
- // combining marks of different class don't interact. Reset the rectangle. |
- if (cmb != lastCmb) { |
- //qDebug("resetting rect"); |
- attachmentRect = baseMetrics; |
- } |
- |
- switch(cmb) { |
- case HB_Combining_DoubleBelow: |
- // ### wrong in rtl context! |
- case HB_Combining_BelowLeft: |
- p.y += offset; |
- case HB_Combining_BelowLeftAttached: |
- p.x += attachmentRect.x - markMetrics.x; |
- p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y; |
- break; |
- case HB_Combining_Below: |
- p.y += offset; |
- case HB_Combining_BelowAttached: |
- p.x += attachmentRect.x - markMetrics.x; |
- p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y; |
- |
- p.x += (attachmentRect.width - markMetrics.width) / 2; |
- break; |
- case HB_Combining_BelowRight: |
- p.y += offset; |
- case HB_Combining_BelowRightAttached: |
- p.x += attachmentRect.x + attachmentRect.width - markMetrics.width - markMetrics.x; |
- p.y += attachmentRect.y + attachmentRect.height - markMetrics.y; |
- break; |
- case HB_Combining_Left: |
- p.x -= offset; |
- case HB_Combining_LeftAttached: |
- break; |
- case HB_Combining_Right: |
- p.x += offset; |
- case HB_Combining_RightAttached: |
- break; |
- case HB_Combining_DoubleAbove: |
- // ### wrong in RTL context! |
- case HB_Combining_AboveLeft: |
- p.y -= offset; |
- case HB_Combining_AboveLeftAttached: |
- p.x += attachmentRect.x - markMetrics.x; |
- p.y += attachmentRect.y - markMetrics.y - markMetrics.height; |
- break; |
- case HB_Combining_Above: |
- p.y -= offset; |
- case HB_Combining_AboveAttached: |
- p.x += attachmentRect.x - markMetrics.x; |
- p.y += attachmentRect.y - markMetrics.y - markMetrics.height; |
- |
- p.x += (attachmentRect.width - markMetrics.width) / 2; |
- break; |
- case HB_Combining_AboveRight: |
- p.y -= offset; |
- case HB_Combining_AboveRightAttached: |
- p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - markMetrics.width; |
- p.y += attachmentRect.y - markMetrics.y - markMetrics.height; |
- break; |
- |
- case HB_Combining_IotaSubscript: |
- default: |
- break; |
- } |
-// qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y()); |
- markMetrics.x += p.x; |
- markMetrics.y += p.y; |
- |
- HB_GlyphMetrics unitedAttachmentRect = attachmentRect; |
- unitedAttachmentRect.x = HB_MIN(attachmentRect.x, markMetrics.x); |
- unitedAttachmentRect.y = HB_MIN(attachmentRect.y, markMetrics.y); |
- unitedAttachmentRect.width = HB_MAX(attachmentRect.x + attachmentRect.width, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x; |
- unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.height, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y; |
- attachmentRect = unitedAttachmentRect; |
- |
- lastCmb = cmb; |
- if (rightToLeft) { |
- item->offsets[gfrom+i].x = p.x; |
- item->offsets[gfrom+i].y = p.y; |
- } else { |
- item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset; |
- item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset; |
- } |
- item->advances[gfrom+i] = 0; |
- } |
-} |
- |
-void HB_HeuristicPosition(HB_ShaperItem *item) |
-{ |
- HB_GetGlyphAdvances(item); |
- HB_GlyphAttributes *attributes = item->attributes; |
- |
- int cEnd = -1; |
- int i = item->num_glyphs; |
- while (i--) { |
- if (cEnd == -1 && attributes[i].mark) { |
- cEnd = i; |
- } else if (cEnd != -1 && !attributes[i].mark) { |
- positionCluster(item, i, cEnd); |
- cEnd = -1; |
- } |
- } |
-} |
- |
-// set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs |
-// and no reordering. |
-// also computes logClusters heuristically |
-void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item) |
-{ |
- const HB_UChar16 *uc = item->string + item->item.pos; |
- hb_uint32 length = item->item.length; |
- |
- // ### zeroWidth and justification are missing here!!!!! |
- |
- assert(length <= item->num_glyphs); |
- |
-// qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs); |
- HB_GlyphAttributes *attributes = item->attributes; |
- unsigned short *logClusters = item->log_clusters; |
- |
- hb_uint32 glyph_pos = 0; |
- hb_uint32 i; |
- for (i = 0; i < length; i++) { |
- if (HB_IsHighSurrogate(uc[i]) && i < length - 1 |
- && HB_IsLowSurrogate(uc[i + 1])) { |
- logClusters[i] = glyph_pos; |
- logClusters[++i] = glyph_pos; |
- } else { |
- logClusters[i] = glyph_pos; |
- } |
- ++glyph_pos; |
- } |
- |
- // first char in a run is never (treated as) a mark |
- int cStart = 0; |
- const bool symbolFont = item->face->isSymbolFont; |
- attributes[0].mark = false; |
- attributes[0].clusterStart = true; |
- attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]); |
- |
- int pos = 0; |
- HB_CharCategory lastCat; |
- int dummy; |
- HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy); |
- for (i = 1; i < length; ++i) { |
- if (logClusters[i] == pos) |
- // same glyph |
- continue; |
- ++pos; |
- while (pos < logClusters[i]) { |
- attributes[pos] = attributes[pos-1]; |
- ++pos; |
- } |
- // hide soft-hyphens by default |
- if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i])) |
- attributes[pos].dontPrint = true; |
- HB_CharCategory cat; |
- int cmb; |
- HB_GetUnicodeCharProperties(uc[i], &cat, &cmb); |
- if (cat != HB_Mark_NonSpacing) { |
- attributes[pos].mark = false; |
- attributes[pos].clusterStart = true; |
- attributes[pos].combiningClass = 0; |
- cStart = logClusters[i]; |
- } else { |
- if (cmb == 0) { |
- // Fix 0 combining classes |
- if ((uc[pos] & 0xff00) == 0x0e00) { |
- // thai or lao |
- if (uc[pos] == 0xe31 || |
- uc[pos] == 0xe34 || |
- uc[pos] == 0xe35 || |
- uc[pos] == 0xe36 || |
- uc[pos] == 0xe37 || |
- uc[pos] == 0xe47 || |
- uc[pos] == 0xe4c || |
- uc[pos] == 0xe4d || |
- uc[pos] == 0xe4e) { |
- cmb = HB_Combining_AboveRight; |
- } else if (uc[pos] == 0xeb1 || |
- uc[pos] == 0xeb4 || |
- uc[pos] == 0xeb5 || |
- uc[pos] == 0xeb6 || |
- uc[pos] == 0xeb7 || |
- uc[pos] == 0xebb || |
- uc[pos] == 0xecc || |
- uc[pos] == 0xecd) { |
- cmb = HB_Combining_Above; |
- } else if (uc[pos] == 0xebc) { |
- cmb = HB_Combining_Below; |
- } |
- } |
- } |
- |
- attributes[pos].mark = true; |
- attributes[pos].clusterStart = false; |
- attributes[pos].combiningClass = cmb; |
- logClusters[i] = cStart; |
- } |
- // one gets an inter character justification point if the current char is not a non spacing mark. |
- // as then the current char belongs to the last one and one gets a space justification point |
- // after the space char. |
- if (lastCat == HB_Separator_Space) |
- attributes[pos-1].justification = HB_Space; |
- else if (cat != HB_Mark_NonSpacing) |
- attributes[pos-1].justification = HB_Character; |
- else |
- attributes[pos-1].justification = HB_NoJustification; |
- |
- lastCat = cat; |
- } |
- pos = logClusters[length-1]; |
- if (lastCat == HB_Separator_Space) |
- attributes[pos].justification = HB_Space; |
- else |
- attributes[pos].justification = HB_Character; |
-} |
- |
-#ifndef NO_OPENTYPE |
-static const HB_OpenTypeFeature basic_features[] = { |
- { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty }, |
- { HB_MAKE_TAG('l', 'i', 'g', 'a'), CcmpProperty }, |
- { HB_MAKE_TAG('c', 'l', 'i', 'g'), CcmpProperty }, |
- {0, 0} |
-}; |
-#endif |
- |
-HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item) |
-{ |
- if (shaper_item->glyphIndicesPresent) { |
- shaper_item->num_glyphs = shaper_item->initialGlyphCount; |
- shaper_item->glyphIndicesPresent = false; |
- return true; |
- } |
- return shaper_item->font->klass |
- ->convertStringToGlyphIndices(shaper_item->font, |
- shaper_item->string + shaper_item->item.pos, shaper_item->item.length, |
- shaper_item->glyphs, &shaper_item->num_glyphs, |
- shaper_item->item.bidiLevel % 2); |
-} |
- |
-HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item) |
-{ |
-#ifndef NO_OPENTYPE |
- const int availableGlyphs = shaper_item->num_glyphs; |
-#endif |
- |
- if (!HB_ConvertStringToGlyphIndices(shaper_item)) |
- return false; |
- |
- HB_HeuristicSetGlyphAttributes(shaper_item); |
- |
-#ifndef NO_OPENTYPE |
- if (HB_SelectScript(shaper_item, basic_features)) { |
- HB_OpenTypeShape(shaper_item, /*properties*/0); |
- return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true); |
- } |
-#endif |
- |
- HB_HeuristicPosition(shaper_item); |
- return true; |
-} |
- |
-const HB_ScriptEngine HB_ScriptEngines[] = { |
- // Common |
- { HB_BasicShape, 0}, |
- // Greek |
- { HB_BasicShape, 0}, |
- // Cyrillic |
- { HB_BasicShape, 0}, |
- // Armenian |
- { HB_BasicShape, 0}, |
- // Hebrew |
- { HB_HebrewShape, 0 }, |
- // Arabic |
- { HB_ArabicShape, 0}, |
- // Syriac |
- { HB_ArabicShape, 0}, |
- // Thaana |
- { HB_BasicShape, 0 }, |
- // Devanagari |
- { HB_IndicShape, HB_IndicAttributes }, |
- // Bengali |
- { HB_IndicShape, HB_IndicAttributes }, |
- // Gurmukhi |
- { HB_IndicShape, HB_IndicAttributes }, |
- // Gujarati |
- { HB_IndicShape, HB_IndicAttributes }, |
- // Oriya |
- { HB_IndicShape, HB_IndicAttributes }, |
- // Tamil |
- { HB_IndicShape, HB_IndicAttributes }, |
- // Telugu |
- { HB_IndicShape, HB_IndicAttributes }, |
- // Kannada |
- { HB_IndicShape, HB_IndicAttributes }, |
- // Malayalam |
- { HB_IndicShape, HB_IndicAttributes }, |
- // Sinhala |
- { HB_IndicShape, HB_IndicAttributes }, |
- // Thai |
- { HB_BasicShape, HB_ThaiAttributes }, |
- // Lao |
- { HB_BasicShape, 0 }, |
- // Tibetan |
- { HB_TibetanShape, HB_TibetanAttributes }, |
- // Myanmar |
- { HB_MyanmarShape, HB_MyanmarAttributes }, |
- // Georgian |
- { HB_BasicShape, 0 }, |
- // Hangul |
- { HB_HangulShape, 0 }, |
- // Ogham |
- { HB_BasicShape, 0 }, |
- // Runic |
- { HB_BasicShape, 0 }, |
- // Khmer |
- { HB_KhmerShape, HB_KhmerAttributes }, |
- // N'Ko |
- { HB_ArabicShape, 0} |
-}; |
- |
-void HB_GetCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength, |
- const HB_ScriptItem *items, hb_uint32 numItems, |
- HB_CharAttributes *attributes) |
-{ |
- calcLineBreaks(string, stringLength, attributes); |
- |
- for (hb_uint32 i = 0; i < numItems; ++i) { |
- HB_Script script = items[i].script; |
- if (script == HB_Script_Inherited) |
- script = HB_Script_Common; |
- HB_AttributeFunction attributeFunction = HB_ScriptEngines[script].charAttributes; |
- if (!attributeFunction) |
- continue; |
- attributeFunction(script, string, items[i].pos, items[i].length, attributes); |
- } |
-} |
- |
- |
-enum BreakRule { NoBreak = 0, Break = 1, Middle = 2 }; |
- |
-static const hb_uint8 wordbreakTable[HB_Word_ExtendNumLet + 1][HB_Word_ExtendNumLet + 1] = { |
-// Other Format Katakana ALetter MidLetter MidNum Numeric ExtendNumLet |
- { Break, Break, Break, Break, Break, Break, Break, Break }, // Other |
- { Break, Break, Break, Break, Break, Break, Break, Break }, // Format |
- { Break, Break, NoBreak, Break, Break, Break, Break, NoBreak }, // Katakana |
- { Break, Break, Break, NoBreak, Middle, Break, NoBreak, NoBreak }, // ALetter |
- { Break, Break, Break, Break, Break, Break, Break, Break }, // MidLetter |
- { Break, Break, Break, Break, Break, Break, Break, Break }, // MidNum |
- { Break, Break, Break, NoBreak, Break, Middle, NoBreak, NoBreak }, // Numeric |
- { Break, Break, NoBreak, NoBreak, Break, Break, NoBreak, NoBreak }, // ExtendNumLet |
-}; |
- |
-void HB_GetWordBoundaries(const HB_UChar16 *string, hb_uint32 stringLength, |
- const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/, |
- HB_CharAttributes *attributes) |
-{ |
- if (stringLength == 0) |
- return; |
- unsigned int brk = HB_GetWordClass(string[0]); |
- attributes[0].wordBoundary = true; |
- for (hb_uint32 i = 1; i < stringLength; ++i) { |
- if (!attributes[i].charStop) { |
- attributes[i].wordBoundary = false; |
- continue; |
- } |
- hb_uint32 nbrk = HB_GetWordClass(string[i]); |
- if (nbrk == HB_Word_Format) { |
- attributes[i].wordBoundary = (HB_GetSentenceClass(string[i-1]) == HB_Sentence_Sep); |
- continue; |
- } |
- BreakRule rule = (BreakRule)wordbreakTable[brk][nbrk]; |
- if (rule == Middle) { |
- rule = Break; |
- hb_uint32 lookahead = i + 1; |
- while (lookahead < stringLength) { |
- hb_uint32 testbrk = HB_GetWordClass(string[lookahead]); |
- if (testbrk == HB_Word_Format && HB_GetSentenceClass(string[lookahead]) != HB_Sentence_Sep) { |
- ++lookahead; |
- continue; |
- } |
- if (testbrk == brk) { |
- rule = NoBreak; |
- while (i < lookahead) |
- attributes[i++].wordBoundary = false; |
- nbrk = testbrk; |
- } |
- break; |
- } |
- } |
- attributes[i].wordBoundary = (rule == Break); |
- brk = nbrk; |
- } |
-} |
- |
- |
-enum SentenceBreakStates { |
- SB_Initial, |
- SB_Upper, |
- SB_UpATerm, |
- SB_ATerm, |
- SB_ATermC, |
- SB_ACS, |
- SB_STerm, |
- SB_STermC, |
- SB_SCS, |
- SB_BAfter, |
- SB_Break, |
- SB_Look |
-}; |
- |
-static const hb_uint8 sentenceBreakTable[HB_Sentence_Close + 1][HB_Sentence_Close + 1] = { |
-// Other Sep Format Sp Lower Upper OLetter Numeric ATerm STerm Close |
- { SB_Initial, SB_BAfter , SB_Initial, SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_ATerm , SB_STerm , SB_Initial }, // SB_Initial, |
- { SB_Initial, SB_BAfter , SB_Upper , SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_UpATerm, SB_STerm , SB_Initial }, // SB_Upper |
- |
- { SB_Look , SB_BAfter , SB_UpATerm, SB_ACS , SB_Initial, SB_Upper , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_UpATerm |
- { SB_Look , SB_BAfter , SB_ATerm , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATerm |
- { SB_Look , SB_BAfter , SB_ATermC , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATermC, |
- { SB_Look , SB_BAfter , SB_ACS , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_Look }, // SB_ACS, |
- |
- { SB_Break , SB_BAfter , SB_STerm , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STerm, |
- { SB_Break , SB_BAfter , SB_STermC , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STermC, |
- { SB_Break , SB_BAfter , SB_SCS , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_Break }, // SB_SCS, |
- { SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break }, // SB_BAfter, |
-}; |
- |
-void HB_GetSentenceBoundaries(const HB_UChar16 *string, hb_uint32 stringLength, |
- const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/, |
- HB_CharAttributes *attributes) |
-{ |
- if (stringLength == 0) |
- return; |
- hb_uint32 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[0])]; |
- attributes[0].sentenceBoundary = true; |
- for (hb_uint32 i = 1; i < stringLength; ++i) { |
- if (!attributes[i].charStop) { |
- attributes[i].sentenceBoundary = false; |
- continue; |
- } |
- brk = sentenceBreakTable[brk][HB_GetSentenceClass(string[i])]; |
- if (brk == SB_Look) { |
- brk = SB_Break; |
- hb_uint32 lookahead = i + 1; |
- while (lookahead < stringLength) { |
- hb_uint32 sbrk = HB_GetSentenceClass(string[lookahead]); |
- if (sbrk != HB_Sentence_Other && sbrk != HB_Sentence_Numeric && sbrk != HB_Sentence_Close) { |
- break; |
- } else if (sbrk == HB_Sentence_Lower) { |
- brk = SB_Initial; |
- break; |
- } |
- ++lookahead; |
- } |
- if (brk == SB_Initial) { |
- while (i < lookahead) |
- attributes[i++].sentenceBoundary = false; |
- } |
- } |
- if (brk == SB_Break) { |
- attributes[i].sentenceBoundary = true; |
- brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[i])]; |
- } else { |
- attributes[i].sentenceBoundary = false; |
- } |
- } |
-} |
- |
- |
-static inline char *tag_to_string(HB_UInt tag) |
-{ |
- static char string[5]; |
- string[0] = (tag >> 24)&0xff; |
- string[1] = (tag >> 16)&0xff; |
- string[2] = (tag >> 8)&0xff; |
- string[3] = tag&0xff; |
- string[4] = 0; |
- return string; |
-} |
- |
-#ifdef OT_DEBUG |
-static void dump_string(HB_Buffer buffer) |
-{ |
- for (uint i = 0; i < buffer->in_length; ++i) { |
- qDebug(" %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster); |
- } |
-} |
-#define DEBUG printf |
-#else |
-#define DEBUG if (1) ; else printf |
-#endif |
- |
-#define DefaultLangSys 0xffff |
-#define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T') |
- |
-enum { |
- RequiresGsub = 1, |
- RequiresGpos = 2 |
-}; |
- |
-struct OTScripts { |
- unsigned int tag; |
- int flags; |
-}; |
-static const OTScripts ot_scripts [] = { |
- // Common |
- { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 }, |
- // Greek |
- { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 }, |
- // Cyrillic |
- { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 }, |
- // Armenian |
- { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 }, |
- // Hebrew |
- { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 }, |
- // Arabic |
- { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 }, |
- // Syriac |
- { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 }, |
- // Thaana |
- { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 }, |
- // Devanagari |
- { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 }, |
- // Bengali |
- { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 }, |
- // Gurmukhi |
- { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 }, |
- // Gujarati |
- { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 }, |
- // Oriya |
- { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 }, |
- // Tamil |
- { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 }, |
- // Telugu |
- { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 }, |
- // Kannada |
- { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 }, |
- // Malayalam |
- { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 }, |
- // Sinhala |
- { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 }, |
- // Thai |
- { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 }, |
- // Lao |
- { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 }, |
- // Tibetan |
- { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 }, |
- // Myanmar |
- { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 }, |
- // Georgian |
- { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 }, |
- // Hangul |
- { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 }, |
- // Ogham |
- { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 }, |
- // Runic |
- { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 }, |
- // Khmer |
- { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 }, |
- // N'Ko |
- { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 } |
-}; |
-enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) }; |
- |
-static HB_Bool checkScript(HB_Face face, int script) |
-{ |
- assert(script < HB_ScriptCount); |
- |
- if (!face->gsub && !face->gpos) |
- return false; |
- |
- unsigned int tag = ot_scripts[script].tag; |
- int requirements = ot_scripts[script].flags; |
- |
- if (requirements & RequiresGsub) { |
- if (!face->gsub) |
- return false; |
- |
- HB_UShort script_index; |
- HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index); |
- if (error) { |
- DEBUG("could not select script %d in GSub table: %d", (int)script, error); |
- error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index); |
- if (error) |
- return false; |
- } |
- } |
- |
- if (requirements & RequiresGpos) { |
- if (!face->gpos) |
- return false; |
- |
- HB_UShort script_index; |
- HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index); |
- if (error) { |
- DEBUG("could not select script in gpos table: %d", error); |
- error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index); |
- if (error) |
- return false; |
- } |
- |
- } |
- return true; |
-} |
- |
-static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag) |
-{ |
- HB_Error error; |
- HB_UInt length = 0; |
- HB_Stream stream = 0; |
- |
- if (!font) |
- return 0; |
- |
- error = tableFunc(font, tag, 0, &length); |
- if (error) |
- return 0; |
- stream = (HB_Stream)malloc(sizeof(HB_StreamRec)); |
- if (!stream) |
- return 0; |
- stream->base = (HB_Byte*)malloc(length); |
- if (!stream->base) { |
- free(stream); |
- return 0; |
- } |
- error = tableFunc(font, tag, stream->base, &length); |
- if (error) { |
- _hb_close_stream(stream); |
- return 0; |
- } |
- stream->size = length; |
- stream->pos = 0; |
- stream->cursor = NULL; |
- return stream; |
-} |
- |
-HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc) |
-{ |
- HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec)); |
- if (!face) |
- return 0; |
- |
- face->isSymbolFont = false; |
- face->gdef = 0; |
- face->gpos = 0; |
- face->gsub = 0; |
- face->current_script = HB_ScriptCount; |
- face->current_flags = HB_ShaperFlag_Default; |
- face->has_opentype_kerning = false; |
- face->tmpAttributes = 0; |
- face->tmpLogClusters = 0; |
- face->glyphs_substituted = false; |
- face->buffer = 0; |
- |
- HB_Error error; |
- HB_Stream stream; |
- HB_Stream gdefStream; |
- |
- gdefStream = getTableStream(font, tableFunc, TTAG_GDEF); |
- if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) { |
- //DEBUG("error loading gdef table: %d", error); |
- face->gdef = 0; |
- } |
- |
- //DEBUG() << "trying to load gsub table"; |
- stream = getTableStream(font, tableFunc, TTAG_GSUB); |
- if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) { |
- face->gsub = 0; |
- if (error != HB_Err_Not_Covered) { |
- //DEBUG("error loading gsub table: %d", error); |
- } else { |
- //DEBUG("face doesn't have a gsub table"); |
- } |
- } |
- _hb_close_stream(stream); |
- |
- stream = getTableStream(font, tableFunc, TTAG_GPOS); |
- if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) { |
- face->gpos = 0; |
- DEBUG("error loading gpos table: %d", error); |
- } |
- _hb_close_stream(stream); |
- |
- _hb_close_stream(gdefStream); |
- |
- for (unsigned int i = 0; i < HB_ScriptCount; ++i) |
- face->supported_scripts[i] = checkScript(face, i); |
- |
- if (hb_buffer_new(&face->buffer) != HB_Err_Ok) { |
- HB_FreeFace(face); |
- return 0; |
- } |
- |
- return face; |
-} |
- |
-void HB_FreeFace(HB_Face face) |
-{ |
- if (!face) |
- return; |
- if (face->gpos) |
- HB_Done_GPOS_Table(face->gpos); |
- if (face->gsub) |
- HB_Done_GSUB_Table(face->gsub); |
- if (face->gdef) |
- HB_Done_GDEF_Table(face->gdef); |
- if (face->buffer) |
- hb_buffer_free(face->buffer); |
- if (face->tmpAttributes) |
- free(face->tmpAttributes); |
- if (face->tmpLogClusters) |
- free(face->tmpLogClusters); |
- free(face); |
-} |
- |
-HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features) |
-{ |
- HB_Script script = shaper_item->item.script; |
- |
- if (!shaper_item->face->supported_scripts[script]) |
- return false; |
- |
- HB_Face face = shaper_item->face; |
- if (face->current_script == script && face->current_flags == shaper_item->shaperFlags) |
- return true; |
- |
- face->current_script = script; |
- face->current_flags = shaper_item->shaperFlags; |
- |
- assert(script < HB_ScriptCount); |
- // find script in our list of supported scripts. |
- unsigned int tag = ot_scripts[script].tag; |
- |
- if (face->gsub && features) { |
-#ifdef OT_DEBUG |
- { |
- HB_FeatureList featurelist = face->gsub->FeatureList; |
- int numfeatures = featurelist.FeatureCount; |
- DEBUG("gsub table has %d features", numfeatures); |
- for (int i = 0; i < numfeatures; i++) { |
- HB_FeatureRecord *r = featurelist.FeatureRecord + i; |
- DEBUG(" feature '%s'", tag_to_string(r->FeatureTag)); |
- } |
- } |
-#endif |
- HB_GSUB_Clear_Features(face->gsub); |
- HB_UShort script_index; |
- HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index); |
- if (!error) { |
- DEBUG("script %s has script index %d", tag_to_string(script), script_index); |
- while (features->tag) { |
- HB_UShort feature_index; |
- error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index); |
- if (!error) { |
- DEBUG(" adding feature %s", tag_to_string(features->tag)); |
- HB_GSUB_Add_Feature(face->gsub, feature_index, features->property); |
- } |
- ++features; |
- } |
- } |
- } |
- |
- // reset |
- face->has_opentype_kerning = false; |
- |
- if (face->gpos) { |
- HB_GPOS_Clear_Features(face->gpos); |
- HB_UShort script_index; |
- HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index); |
- if (!error) { |
-#ifdef OT_DEBUG |
- { |
- HB_FeatureList featurelist = face->gpos->FeatureList; |
- int numfeatures = featurelist.FeatureCount; |
- DEBUG("gpos table has %d features", numfeatures); |
- for(int i = 0; i < numfeatures; i++) { |
- HB_FeatureRecord *r = featurelist.FeatureRecord + i; |
- HB_UShort feature_index; |
- HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index); |
- DEBUG(" feature '%s'", tag_to_string(r->FeatureTag)); |
- } |
- } |
-#endif |
- HB_UInt *feature_tag_list_buffer; |
- error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer); |
- if (!error) { |
- HB_UInt *feature_tag_list = feature_tag_list_buffer; |
- while (*feature_tag_list) { |
- HB_UShort feature_index; |
- if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) { |
- if (face->current_flags & HB_ShaperFlag_NoKerning) { |
- ++feature_tag_list; |
- continue; |
- } |
- face->has_opentype_kerning = true; |
- } |
- error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index); |
- if (!error) |
- HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties); |
- ++feature_tag_list; |
- } |
- FREE(feature_tag_list_buffer); |
- } |
- } |
- } |
- |
- return true; |
-} |
- |
-HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties) |
-{ |
- HB_GlyphAttributes *tmpAttributes; |
- unsigned int *tmpLogClusters; |
- |
- HB_Face face = item->face; |
- |
- face->length = item->num_glyphs; |
- |
- hb_buffer_clear(face->buffer); |
- |
- tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes)); |
- if (!tmpAttributes) |
- return false; |
- face->tmpAttributes = tmpAttributes; |
- |
- tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int)); |
- if (!tmpLogClusters) |
- return false; |
- face->tmpLogClusters = tmpLogClusters; |
- |
- for (int i = 0; i < face->length; ++i) { |
- hb_buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i); |
- face->tmpAttributes[i] = item->attributes[i]; |
- face->tmpLogClusters[i] = item->log_clusters[i]; |
- } |
- |
-#ifdef OT_DEBUG |
- DEBUG("-----------------------------------------"); |
-// DEBUG("log clusters before shaping:"); |
-// for (int j = 0; j < length; j++) |
-// DEBUG(" log[%d] = %d", j, item->log_clusters[j]); |
- DEBUG("original glyphs: %p", item->glyphs); |
- for (int i = 0; i < length; ++i) |
- DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex); |
-// dump_string(hb_buffer); |
-#endif |
- |
- face->glyphs_substituted = false; |
- if (face->gsub) { |
- unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer); |
- if (error && error != HB_Err_Not_Covered) |
- return false; |
- face->glyphs_substituted = (error != HB_Err_Not_Covered); |
- } |
- |
-#ifdef OT_DEBUG |
-// DEBUG("log clusters before shaping:"); |
-// for (int j = 0; j < length; j++) |
-// DEBUG(" log[%d] = %d", j, item->log_clusters[j]); |
- DEBUG("shaped glyphs:"); |
- for (int i = 0; i < length; ++i) |
- DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex); |
- DEBUG("-----------------------------------------"); |
-// dump_string(hb_buffer); |
-#endif |
- |
- return true; |
-} |
- |
-/* See comments near the definition of HB_ShaperFlag_ForceMarksToZeroWidth for a description |
- of why this function exists. */ |
-void HB_FixupZeroWidth(HB_ShaperItem *item) |
-{ |
- HB_UShort property; |
- |
- if (!item->face->gdef) |
- return; |
- |
- for (unsigned int i = 0; i < item->num_glyphs; ++i) { |
- /* If the glyph is a mark, force its advance to zero. */ |
- if (HB_GDEF_Get_Glyph_Property (item->face->gdef, item->glyphs[i], &property) == HB_Err_Ok && |
- property == HB_GDEF_MARK) { |
- item->advances[i] = 0; |
- } |
- } |
-} |
- |
-HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters) |
-{ |
- HB_Face face = item->face; |
- |
- bool glyphs_positioned = false; |
- if (face->gpos) { |
- if (face->buffer->positions) |
- memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec)); |
- // #### check that passing "false,false" is correct |
- glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered; |
- } |
- |
- if (!face->glyphs_substituted && !glyphs_positioned) { |
- HB_GetGlyphAdvances(item); |
- if (item->face->current_flags & HB_ShaperFlag_ForceMarksToZeroWidth) |
- HB_FixupZeroWidth(item); |
- return true; // nothing to do for us |
- } |
- |
- // make sure we have enough space to write everything back |
- if (availableGlyphs < (int)face->buffer->in_length) { |
- item->num_glyphs = face->buffer->in_length; |
- return false; |
- } |
- |
- HB_Glyph *glyphs = item->glyphs; |
- HB_GlyphAttributes *attributes = item->attributes; |
- |
- for (unsigned int i = 0; i < face->buffer->in_length; ++i) { |
- glyphs[i] = face->buffer->in_string[i].gindex; |
- attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster]; |
- if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster) |
- attributes[i].clusterStart = false; |
- } |
- item->num_glyphs = face->buffer->in_length; |
- |
- if (doLogClusters && face->glyphs_substituted) { |
- // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper. |
- unsigned short *logClusters = item->log_clusters; |
- int clusterStart = 0; |
- int oldCi = 0; |
- // #### the reconstruction of the logclusters currently does not work if the original string |
- // contains surrogate pairs |
- for (unsigned int i = 0; i < face->buffer->in_length; ++i) { |
- int ci = face->buffer->in_string[i].cluster; |
- // DEBUG(" ci[%d] = %d mark=%d, cmb=%d, cs=%d", |
- // i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart); |
- if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) { |
- for (int j = oldCi; j < ci; j++) |
- logClusters[j] = clusterStart; |
- clusterStart = i; |
- oldCi = ci; |
- } |
- } |
- for (int j = oldCi; j < face->length; j++) |
- logClusters[j] = clusterStart; |
- } |
- |
- // calulate the advances for the shaped glyphs |
-// DEBUG("unpositioned: "); |
- |
- // positioning code: |
- if (glyphs_positioned) { |
- HB_GetGlyphAdvances(item); |
- HB_Position positions = face->buffer->positions; |
- HB_Fixed *advances = item->advances; |
- |
-// DEBUG("positioned glyphs:"); |
- for (unsigned int i = 0; i < face->buffer->in_length; i++) { |
-// DEBUG(" %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i, |
-// glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(), |
-// (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6), |
-// (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6), |
-// positions[i].back, positions[i].new_advance); |
- |
- HB_Fixed adjustment = (item->item.bidiLevel % 2) ? -positions[i].x_advance : positions[i].x_advance; |
- |
- if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics)) |
- adjustment = HB_FIXED_ROUND(adjustment); |
- |
- if (positions[i].new_advance) { |
- advances[i] = adjustment; |
- } else { |
- advances[i] += adjustment; |
- } |
- |
- int back = 0; |
- HB_FixedPoint *offsets = item->offsets; |
- offsets[i].x = positions[i].x_pos; |
- offsets[i].y = positions[i].y_pos; |
- while (positions[i - back].back) { |
- back += positions[i - back].back; |
- offsets[i].x += positions[i - back].x_pos; |
- offsets[i].y += positions[i - back].y_pos; |
- } |
- offsets[i].y = -offsets[i].y; |
- |
- if (item->item.bidiLevel % 2) { |
- // ### may need to go back multiple glyphs like in ltr |
- back = positions[i].back; |
- while (back--) |
- offsets[i].x -= advances[i-back]; |
- } else { |
- back = 0; |
- while (positions[i - back].back) { |
- back += positions[i - back].back; |
- offsets[i].x -= advances[i-back]; |
- } |
- } |
-// DEBUG(" ->\tadv=%d\tpos=(%d/%d)", |
-// glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt()); |
- } |
- item->kerning_applied = face->has_opentype_kerning; |
- } else { |
- HB_HeuristicPosition(item); |
- } |
- |
-#ifdef OT_DEBUG |
- if (doLogClusters) { |
- DEBUG("log clusters after shaping:"); |
- for (int j = 0; j < length; j++) |
- DEBUG(" log[%d] = %d", j, item->log_clusters[j]); |
- } |
- DEBUG("final glyphs:"); |
- for (int i = 0; i < (int)hb_buffer->in_length; ++i) |
- DEBUG(" glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d", |
- glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attributes.mark, |
- glyphs[i].attributes.combiningClass, glyphs[i].attributes.clusterStart, |
- glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(), |
- glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt()); |
- DEBUG("-----------------------------------------"); |
-#endif |
- return true; |
-} |
- |
-HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item) |
-{ |
- HB_Bool result = false; |
- if (shaper_item->num_glyphs < shaper_item->item.length) { |
- shaper_item->num_glyphs = shaper_item->item.length; |
- return false; |
- } |
- assert(shaper_item->item.script < HB_ScriptCount); |
- result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item); |
- shaper_item->glyphIndicesPresent = false; |
- return result; |
-} |
- |