[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