[PATCH] Add support for using HarfBuzz instead of ICU LE

Khaled Hosny (via Code Review) gerrit at gerrit.libreoffice.org
Sun Apr 21 01:19:29 PDT 2013


Hi,

I have submitted a patch for review:

    https://gerrit.libreoffice.org/3518

To pull it, you can do:

    git pull ssh://gerrit.libreoffice.org:29418/core refs/changes/18/3518/1

Add support for using HarfBuzz instead of ICU LE

Change-Id: I07e698f7486379bae68329771695cd94d6e561b5
---
M vcl/generic/glyphs/gcach_layout.cxx
1 file changed, 178 insertions(+), 15 deletions(-)



diff --git a/vcl/generic/glyphs/gcach_layout.cxx b/vcl/generic/glyphs/gcach_layout.cxx
index ddf6d7c..02392c2 100644
--- a/vcl/generic/glyphs/gcach_layout.cxx
+++ b/vcl/generic/glyphs/gcach_layout.cxx
@@ -17,6 +17,7 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
+#include <config_harfbuzz.h>
 #include <gcach_ftyp.hxx>
 #include <sallayout.hxx>
 #include <salgdi.hxx>
@@ -30,10 +31,15 @@
 #include <sal/alloca.h>
 #include <rtl/instance.hxx>
 
+#if ENABLE_HARFBUZZ
+#include <hb-ft.h>
+#include <hb-icu.h>
+#else
 #include <layout/LayoutEngine.h>
 #include <layout/LEFontInstance.h>
 #include <layout/LELanguages.h>
 #include <layout/LEScripts.h>
+#endif // ENABLE_HARFBUZZ
 
 #include <unicode/uscript.h>
 #include <unicode/ubidi.h>
@@ -86,6 +92,172 @@
     }
 }
 
+// =======================================================================
+
+static bool lcl_CharIsJoiner(sal_Unicode cChar)
+{
+    return ((cChar == 0x200C) || (cChar == 0x200D));
+}
+
+static bool needPreviousCode(sal_Unicode cChar)
+{
+    return lcl_CharIsJoiner(cChar) || U16_IS_LEAD(cChar);
+}
+
+static bool needNextCode(sal_Unicode cChar)
+{
+    return lcl_CharIsJoiner(cChar) || U16_IS_TRAIL(cChar);
+}
+
+#if ENABLE_HARFBUZZ
+class HbLayoutEngine : public ServerFontLayoutEngine
+{
+private:
+    UScriptCode             meScriptCode;
+
+public:
+                            HbLayoutEngine(ServerFont&);
+    virtual                 ~HbLayoutEngine(){};
+
+    virtual bool            layout(ServerFontLayout&, ImplLayoutArgs&);
+};
+
+HbLayoutEngine::HbLayoutEngine(ServerFont& /*rServerFont*/)
+:   meScriptCode(USCRIPT_INVALID_CODE)
+{}
+
+bool HbLayoutEngine::layout(ServerFontLayout& rLayout, ImplLayoutArgs& rArgs)
+{
+    ServerFont& rFont = rLayout.GetServerFont();
+    FT_Face aFace = rFont.GetFtFace();
+
+    // allocate temporary arrays, note: round to even
+    int nGlyphCapacity = (3 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos) | 15) + 1;
+
+    rLayout.Reserve(nGlyphCapacity);
+
+    Point aNewPos(0, 0);
+    while (true)
+    {
+        int nMinRunPos, nEndRunPos;
+        bool bRightToLeft;
+        if (!rArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRightToLeft))
+            break;
+
+        int nRunLen = nEndRunPos - nMinRunPos;
+
+        // find matching script
+        // TODO: use ICU's UScriptRun API
+        UScriptCode eScriptCode = USCRIPT_INVALID_CODE;
+        for (int i = nMinRunPos; i < nEndRunPos; ++i)
+        {
+            UErrorCode rcI18n = U_ZERO_ERROR;
+            UScriptCode eNextScriptCode = uscript_getScript(rArgs.mpStr[i], &rcI18n);
+            if ((eNextScriptCode > USCRIPT_INHERITED))
+            {
+                eScriptCode = eNextScriptCode;
+                if (eNextScriptCode != USCRIPT_LATIN)
+                    break;
+            }
+        }
+        if (eScriptCode < 0)   // TODO: handle errors better
+            eScriptCode = USCRIPT_LATIN;
+
+        meScriptCode = eScriptCode;
+
+        hb_font_t *aHbFont = hb_ft_font_create(aFace, NULL);
+
+        LanguageTag aLangTag(rArgs.meLanguage);
+        OString sLanguage = OUStringToOString(aLangTag.getLanguage(), RTL_TEXTENCODING_UTF8);
+
+        hb_buffer_t *aHbBuffer = hb_buffer_create();
+        hb_buffer_set_direction(aHbBuffer, bRightToLeft ? HB_DIRECTION_RTL: HB_DIRECTION_LTR);
+        hb_buffer_set_script(aHbBuffer, hb_icu_script_to_script(eScriptCode));
+        hb_buffer_set_language(aHbBuffer, hb_language_from_string(sLanguage.getStr(), -1));
+        hb_buffer_add_utf16(aHbBuffer, rArgs.mpStr, nRunLen, nMinRunPos, nRunLen);
+        hb_shape(aHbFont, aHbBuffer, NULL, 0);
+
+        int nRunGlyphCount = hb_buffer_get_length(aHbBuffer);
+        hb_glyph_info_t *aHbGlyphInfos = hb_buffer_get_glyph_infos(aHbBuffer, NULL);
+        hb_glyph_position_t *aHbPositions = hb_buffer_get_glyph_positions(aHbBuffer, NULL);
+
+        int32_t nLastCluster = -1;
+        for (int i = 0; i < nRunGlyphCount; ++i) {
+            int32_t nGlyphIndex = aHbGlyphInfos[i].codepoint;
+            int32_t nCluster = aHbGlyphInfos[i].cluster;
+            int32_t nCharPos = nCluster;
+
+            // if needed request glyph fallback by updating LayoutArgs
+            if (!nGlyphIndex)
+            {
+                if (nCharPos >= 0)
+                {
+                    rArgs.NeedFallback(nCharPos, bRightToLeft);
+                    // XXX: do we need this in harfbuzz?
+                    if  ((nCharPos > 0) && needPreviousCode(rArgs.mpStr[nCharPos-1]))
+                        rArgs.NeedFallback(nCharPos-1, bRightToLeft);
+                    else if  ((nCharPos + 1 < nEndRunPos) && needNextCode(rArgs.mpStr[nCharPos+1]))
+                        rArgs.NeedFallback(nCharPos+1, bRightToLeft);
+                }
+
+                if (SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags)
+                    continue;
+            }
+
+            const GlyphMetric& rGM = rFont.GetGlyphMetric(nGlyphIndex);
+            int nGlyphWidth = rGM.GetCharWidth();
+
+            long nGlyphFlags = 0;
+            if (bRightToLeft)
+                nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
+
+            // what is this for?
+            // XXX: rtl clusters
+            bool bInCluster = false;
+            if (nCluster == nLastCluster)
+                bInCluster = true;
+            nLastCluster = nCluster;
+            if (bInCluster)
+                nGlyphFlags |= GlyphItem::IS_IN_CLUSTER;
+
+            // XXX: query GDEF glyph class? Do we even need this?
+            if (aHbPositions[i].x_advance == 0)
+                nGlyphFlags |= GlyphItem::IS_DIACRITIC;
+
+            aHbPositions[i].x_offset /= 64;
+            aHbPositions[i].y_offset /= 64;
+            aHbPositions[i].x_advance /= 64;
+            aHbPositions[i].y_advance /= 64;
+
+            aNewPos = Point(aNewPos.X() + aHbPositions[i].x_offset, aNewPos.Y() - aHbPositions[i].y_offset);
+
+            GlyphItem aGI(nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth);
+
+            if (i + 1 < nRunGlyphCount)
+                aGI.mnNewWidth = nGlyphWidth + (aHbPositions[i + 1].x_offset / 64);
+
+            rLayout.AppendGlyph(aGI);
+
+            aNewPos.X() += aHbPositions[i].x_advance;
+            aNewPos.Y() += aHbPositions[i].y_advance;
+        }
+
+        hb_buffer_destroy(aHbBuffer);
+        hb_font_destroy(aHbFont);
+    }
+
+    // sort glyphs in visual order
+    // and then in logical order (e.g. diacritics after cluster start)
+    rLayout.SortGlyphItems();
+
+    // determine need for kashida justification
+    if((rArgs.mpDXArray || rArgs.mnLayoutWidth)
+    && ((meScriptCode == USCRIPT_ARABIC) || (meScriptCode == USCRIPT_SYRIAC)))
+        rArgs.mnFlags |= SAL_LAYOUT_KASHIDA_JUSTIFICATON;
+
+    return true;
+}
+#else
 // =======================================================================
 // bridge to ICU LayoutEngine
 // =======================================================================
@@ -301,21 +473,6 @@
 }
 
 // -----------------------------------------------------------------------
-
-static bool lcl_CharIsJoiner(sal_Unicode cChar)
-{
-    return ((cChar == 0x200C) || (cChar == 0x200D));
-}
-
-static bool needPreviousCode(sal_Unicode cChar)
-{
-    return lcl_CharIsJoiner(cChar) || U16_IS_LEAD(cChar);
-}
-
-static bool needNextCode(sal_Unicode cChar)
-{
-    return lcl_CharIsJoiner(cChar) || U16_IS_TRAIL(cChar);
-}
 
 namespace
 {
@@ -742,6 +899,7 @@
 #ifdef ARABIC_BANDAID
             aGI.mnNewWidth = nNewWidth;
 #endif
+
             rLayout.AppendGlyph( aGI );
             ++nFilteredRunGlyphCount;
             nLastCharPos = nCharPos;
@@ -762,6 +920,7 @@
 
     return true;
 }
+#endif // ENABLE_HARFBUZZ
 
 // =======================================================================
 
@@ -769,7 +928,11 @@
 {
     // find best layout engine for font, platform, script and language
     if (!mpLayoutEngine)
+#if ENABLE_HARFBUZZ
+        mpLayoutEngine = new HbLayoutEngine(*this);
+#else
         mpLayoutEngine = new IcuLayoutEngine(*this);
+#endif // ENABLE_HARFBUZZ
     return mpLayoutEngine;
 }
 

-- 
To view, visit https://gerrit.libreoffice.org/3518
To unsubscribe, visit https://gerrit.libreoffice.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I07e698f7486379bae68329771695cd94d6e561b5
Gerrit-PatchSet: 1
Gerrit-Project: core
Gerrit-Branch: master
Gerrit-Owner: Khaled Hosny <khaledhosny at eglug.org>



More information about the LibreOffice mailing list