summaryrefslogtreecommitdiff
path: root/vcl/unx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/unx')
-rw-r--r--vcl/unx/generic/gdi/cairotextrender.cxx2
-rw-r--r--vcl/unx/generic/gdi/gcach_xpeer.hxx2
-rw-r--r--vcl/unx/generic/glyphs/gcach_layout.cxx621
-rw-r--r--vcl/unx/generic/glyphs/glyphcache.cxx379
-rw-r--r--vcl/unx/generic/glyphs/graphite_serverfont.cxx135
-rw-r--r--vcl/unx/generic/glyphs/scrptrun.cxx234
-rw-r--r--vcl/unx/generic/glyphs/scrptrun.h173
7 files changed, 1544 insertions, 2 deletions
diff --git a/vcl/unx/generic/gdi/cairotextrender.cxx b/vcl/unx/generic/gdi/cairotextrender.cxx
index 97af78fc5800..5e39d0e162c5 100644
--- a/vcl/unx/generic/gdi/cairotextrender.cxx
+++ b/vcl/unx/generic/gdi/cairotextrender.cxx
@@ -28,7 +28,7 @@
#include "generic/printergfx.hxx"
#include "generic/genpspgraphics.h"
#include "generic/geninst.h"
-#include "generic/glyphcache.hxx"
+#include "unx/glyphcache.hxx"
#include "PhysicalFontFace.hxx"
#include "impfont.hxx"
diff --git a/vcl/unx/generic/gdi/gcach_xpeer.hxx b/vcl/unx/generic/gdi/gcach_xpeer.hxx
index 5d3b89ffe3f4..d933c8b61490 100644
--- a/vcl/unx/generic/gdi/gcach_xpeer.hxx
+++ b/vcl/unx/generic/gdi/gcach_xpeer.hxx
@@ -20,7 +20,7 @@
#ifndef INCLUDED_VCL_UNX_GENERIC_GDI_GCACH_XPEER_HXX
#define INCLUDED_VCL_UNX_GENERIC_GDI_GCACH_XPEER_HXX
-#include "generic/glyphcache.hxx"
+#include "unx/glyphcache.hxx"
class X11GlyphCache : public GlyphCache
{
diff --git a/vcl/unx/generic/glyphs/gcach_layout.cxx b/vcl/unx/generic/glyphs/gcach_layout.cxx
new file mode 100644
index 000000000000..70ac1901f09d
--- /dev/null
+++ b/vcl/unx/generic/glyphs/gcach_layout.cxx
@@ -0,0 +1,621 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "unx/freetype_glyphcache.hxx"
+#include <sallayout.hxx>
+#include <salgdi.hxx>
+#include <scrptrun.h>
+
+#include <i18nlangtag/mslangid.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/unohelp.hxx>
+
+#include <rtl/instance.hxx>
+
+#include <hb-icu.h>
+#include <hb-ot.h>
+
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+
+#ifndef HB_VERSION_ATLEAST
+#define HB_VERSION_ATLEAST(a,b,c) 0
+#endif
+
+// layout implementation for ServerFont
+ServerFontLayout::ServerFontLayout( ServerFont& rFont )
+: mrServerFont( rFont )
+{
+}
+
+void ServerFontLayout::DrawText( SalGraphics& rSalGraphics ) const
+{
+ rSalGraphics.DrawServerFontLayout( *this );
+}
+
+bool ServerFontLayout::LayoutText( ImplLayoutArgs& rArgs )
+{
+ return mrServerFont.GetLayoutEngine()->Layout(*this, rArgs);
+}
+
+void ServerFontLayout::AdjustLayout( ImplLayoutArgs& rArgs )
+{
+ GenericSalLayout::AdjustLayout( rArgs );
+
+ // apply asian kerning if the glyphs are not already formatted
+ if( (rArgs.mnFlags & SalLayoutFlags::KerningAsian)
+ && !(rArgs.mnFlags & SalLayoutFlags::Vertical) )
+ if( (rArgs.mpDXArray != nullptr) || (rArgs.mnLayoutWidth != 0) )
+ ApplyAsianKerning(rArgs.mrStr);
+
+ // insert kashidas where requested by the formatting array
+ if( (rArgs.mnFlags & SalLayoutFlags::KashidaJustification) && rArgs.mpDXArray )
+ {
+ int nKashidaIndex = mrServerFont.GetGlyphIndex( 0x0640 );
+ if( nKashidaIndex != 0 )
+ {
+ const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nKashidaIndex );
+ KashidaJustify( nKashidaIndex, rGM.GetCharWidth() );
+ // TODO: kashida-GSUB/GPOS
+ }
+ }
+}
+
+void ServerFontLayout::SetNeedFallback(ImplLayoutArgs& rArgs, sal_Int32 nCharPos,
+ bool bRightToLeft)
+{
+ if (nCharPos < 0)
+ return;
+
+ using namespace ::com::sun::star;
+
+ if (!mxBreak.is())
+ mxBreak = vcl::unohelper::CreateBreakIterator();
+
+ lang::Locale aLocale(rArgs.maLanguageTag.getLocale());
+
+ //if position nCharPos is missing in the font, grab the entire grapheme and
+ //mark all glyphs as missing so the whole thing is rendered with the same
+ //font
+ sal_Int32 nDone;
+ sal_Int32 nGraphemeStartPos =
+ mxBreak->previousCharacters(rArgs.mrStr, nCharPos+1, aLocale,
+ i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
+ sal_Int32 nGraphemeEndPos =
+ mxBreak->nextCharacters(rArgs.mrStr, nCharPos, aLocale,
+ i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
+
+ rArgs.NeedFallback(nGraphemeStartPos, nGraphemeEndPos, bRightToLeft);
+}
+
+std::ostream &operator <<(std::ostream& s, ServerFont* pFont)
+{
+#ifndef SAL_LOG_INFO
+ (void) pFont;
+#else
+ FT_Face aFace = pFont->GetFtFace();
+ const char* pName = FT_Get_Postscript_Name(aFace);
+ if (pName)
+ s << pName;
+ else
+ s << pFont->GetFontFileName();
+#endif
+ return s;
+}
+
+static hb_blob_t *getFontTable(hb_face_t* /*face*/, hb_tag_t nTableTag, void* pUserData)
+{
+ char pTagName[5];
+ pTagName[0] = (char)(nTableTag >> 24);
+ pTagName[1] = (char)(nTableTag >> 16);
+ pTagName[2] = (char)(nTableTag >> 8);
+ pTagName[3] = (char)(nTableTag);
+ pTagName[4] = 0;
+
+ ServerFont* pFont = static_cast<ServerFont*>(pUserData);
+
+ SAL_INFO("vcl.harfbuzz", "getFontTable(" << pFont << ", " << pTagName << ")");
+
+ sal_uLong nLength;
+ const unsigned char* pBuffer = pFont->GetTable(pTagName, &nLength);
+
+ hb_blob_t* pBlob = nullptr;
+ if (pBuffer != nullptr)
+ pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, HB_MEMORY_MODE_READONLY, const_cast<unsigned char *>(pBuffer), nullptr);
+
+ return pBlob;
+}
+
+static hb_bool_t getFontGlyph(hb_font_t* /*font*/, void* pFontData,
+ hb_codepoint_t ch, hb_codepoint_t vs,
+ hb_codepoint_t* nGlyphIndex,
+ void* /*pUserData*/)
+{
+ ServerFont* pFont = static_cast<ServerFont*>(pFontData);
+ *nGlyphIndex = pFont->GetRawGlyphIndex(ch, vs);
+
+ // tdf#89231 if the font is missing non-breaking space, then use a normal space
+ if (*nGlyphIndex == 0 && ch == 0x202F && !vs)
+ *nGlyphIndex = pFont->GetRawGlyphIndex(' ');
+
+ return *nGlyphIndex != 0;
+}
+
+static hb_position_t getGlyphAdvanceH(hb_font_t* /*font*/, void* pFontData,
+ hb_codepoint_t nGlyphIndex,
+ void* /*pUserData*/)
+{
+ ServerFont* pFont = static_cast<ServerFont*>(pFontData);
+ const GlyphMetric& rGM = pFont->GetGlyphMetric(nGlyphIndex);
+ return rGM.GetCharWidth() << 6;
+}
+
+#if !HB_VERSION_ATLEAST(1, 1, 2)
+static hb_position_t getGlyphAdvanceV(hb_font_t* /*font*/, void* /*pFontData*/,
+ hb_codepoint_t /*nGlyphIndex*/,
+ void* /*pUserData*/)
+{
+ // XXX: vertical metrics
+ return 0;
+}
+
+static hb_bool_t getGlyphOriginH(hb_font_t* /*font*/, void* /*pFontData*/,
+ hb_codepoint_t /*nGlyphIndex*/,
+ hb_position_t* /*x*/, hb_position_t* /*y*/,
+ void* /*pUserData*/)
+{
+ // the horizontal origin is always (0, 0)
+ return true;
+}
+
+static hb_bool_t getGlyphOriginV(hb_font_t* /*font*/, void* /*pFontData*/,
+ hb_codepoint_t /*nGlyphIndex*/,
+ hb_position_t* /*x*/, hb_position_t* /*y*/,
+ void* /*pUserData*/)
+{
+ // XXX: vertical origin
+ return true;
+}
+#endif
+
+static hb_position_t getGlyphKerningH(hb_font_t* /*font*/, void* pFontData,
+ hb_codepoint_t nGlyphIndex1, hb_codepoint_t nGlyphIndex2,
+ void* /*pUserData*/)
+{
+ // This callback is for old style 'kern' table, GPOS kerning is handled by HarfBuzz directly
+
+ ServerFont* pFont = static_cast<ServerFont*>(pFontData);
+ FT_Face aFace = pFont->GetFtFace();
+
+ SAL_INFO("vcl.harfbuzz", "getGlyphKerningH(" << pFont << ", " << nGlyphIndex1 << ", " << nGlyphIndex2 << ")");
+
+ FT_Error error;
+ FT_Vector kerning;
+ hb_position_t ret;
+
+ error = FT_Get_Kerning(aFace, nGlyphIndex1, nGlyphIndex2, FT_KERNING_DEFAULT, &kerning);
+ if (error)
+ ret = 0;
+ else
+ ret = kerning.x;
+
+ return ret;
+}
+
+#if !HB_VERSION_ATLEAST(1, 1, 2)
+static hb_position_t getGlyphKerningV(hb_font_t* /*font*/, void* /*pFontData*/,
+ hb_codepoint_t /*nGlyphIndex1*/, hb_codepoint_t /*nGlyphIndex2*/,
+ void* /*pUserData*/)
+{
+ // XXX vertical kerning
+ return 0;
+}
+#endif
+
+static hb_bool_t getGlyphExtents(hb_font_t* /*font*/, void* pFontData,
+ hb_codepoint_t nGlyphIndex,
+ hb_glyph_extents_t* pExtents,
+ void* /*pUserData*/)
+{
+ ServerFont* pFont = static_cast<ServerFont*>(pFontData);
+ FT_Face aFace = pFont->GetFtFace();
+
+ SAL_INFO("vcl.harfbuzz", "getGlyphExtents(" << pFont << ", " << nGlyphIndex << ")");
+
+ FT_Error error;
+ error = FT_Load_Glyph(aFace, nGlyphIndex, FT_LOAD_DEFAULT);
+ if (!error)
+ {
+ pExtents->x_bearing = aFace->glyph->metrics.horiBearingX;
+ pExtents->y_bearing = aFace->glyph->metrics.horiBearingY;
+ pExtents->width = aFace->glyph->metrics.width;
+ pExtents->height = -aFace->glyph->metrics.height;
+ }
+
+ return !error;
+}
+
+static hb_bool_t getGlyphContourPoint(hb_font_t* /*font*/, void* pFontData,
+ hb_codepoint_t nGlyphIndex, unsigned int nPointIndex,
+ hb_position_t *x, hb_position_t *y,
+ void* /*pUserData*/)
+{
+ bool ret = false;
+ ServerFont* pFont = static_cast<ServerFont*>(pFontData);
+ FT_Face aFace = pFont->GetFtFace();
+
+ SAL_INFO("vcl.harfbuzz", "getGlyphContourPoint(" << pFont << ", " << nGlyphIndex << ", " << nPointIndex << ")");
+
+ FT_Error error;
+ error = FT_Load_Glyph(aFace, nGlyphIndex, FT_LOAD_DEFAULT);
+ if (!error)
+ {
+ if (aFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
+ {
+ if (nPointIndex < (unsigned int) aFace->glyph->outline.n_points)
+ {
+ *x = aFace->glyph->outline.points[nPointIndex].x;
+ *y = aFace->glyph->outline.points[nPointIndex].y;
+ ret = true;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static hb_font_funcs_t* getFontFuncs()
+{
+ static hb_font_funcs_t* funcs = hb_font_funcs_create();
+
+ hb_font_funcs_set_glyph_func (funcs, getFontGlyph, nullptr, nullptr);
+ hb_font_funcs_set_glyph_h_advance_func (funcs, getGlyphAdvanceH, nullptr, nullptr);
+ hb_font_funcs_set_glyph_h_kerning_func (funcs, getGlyphKerningH, nullptr, nullptr);
+ hb_font_funcs_set_glyph_extents_func (funcs, getGlyphExtents, nullptr, nullptr);
+ hb_font_funcs_set_glyph_contour_point_func (funcs, getGlyphContourPoint, nullptr, nullptr);
+#if !HB_VERSION_ATLEAST(1, 1, 2)
+ hb_font_funcs_set_glyph_v_advance_func (funcs, getGlyphAdvanceV, nullptr, nullptr);
+ hb_font_funcs_set_glyph_h_origin_func (funcs, getGlyphOriginH, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_origin_func (funcs, getGlyphOriginV, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_kerning_func (funcs, getGlyphKerningV, nullptr, nullptr);
+#endif
+
+ return funcs;
+}
+
+#if !HB_VERSION_ATLEAST(1, 1, 0)
+// Disabled Unicode compatibility decomposition, see fdo#66715
+static unsigned int unicodeDecomposeCompatibility(hb_unicode_funcs_t* /*ufuncs*/,
+ hb_codepoint_t /*u*/,
+ hb_codepoint_t* /*decomposed*/,
+ void* /*user_data*/)
+{
+ return 0;
+}
+
+static hb_unicode_funcs_t* getUnicodeFuncs()
+{
+ static hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
+ hb_unicode_funcs_set_decompose_compatibility_func(ufuncs, unicodeDecomposeCompatibility, nullptr, nullptr);
+ return ufuncs;
+}
+#endif
+
+class HbLayoutEngine : public ServerFontLayoutEngine
+{
+private:
+ hb_script_t maHbScript;
+ hb_face_t* mpHbFace;
+ int mnUnitsPerEM;
+
+public:
+ explicit HbLayoutEngine(ServerFont&);
+ virtual ~HbLayoutEngine();
+
+ virtual bool Layout(ServerFontLayout&, ImplLayoutArgs&) override;
+};
+
+HbLayoutEngine::HbLayoutEngine(ServerFont& rServerFont)
+: maHbScript(HB_SCRIPT_INVALID),
+ mpHbFace(nullptr),
+ mnUnitsPerEM(0)
+{
+ FT_Face aFtFace = rServerFont.GetFtFace();
+ mnUnitsPerEM = rServerFont.GetEmUnits();
+
+ mpHbFace = hb_face_create_for_tables(getFontTable, &rServerFont, nullptr);
+ hb_face_set_index(mpHbFace, aFtFace->face_index);
+ hb_face_set_upem(mpHbFace, mnUnitsPerEM);
+}
+
+HbLayoutEngine::~HbLayoutEngine()
+{
+ hb_face_destroy(mpHbFace);
+}
+
+struct HbScriptRun
+{
+ int32_t mnMin;
+ int32_t mnEnd;
+ hb_script_t maScript;
+
+ HbScriptRun(int32_t nMin, int32_t nEnd, UScriptCode aScript)
+ : mnMin(nMin), mnEnd(nEnd),
+ maScript(hb_icu_script_to_script(aScript))
+ {}
+};
+
+typedef std::vector<HbScriptRun> HbScriptRuns;
+
+namespace vcl {
+ struct Run
+ {
+ int32_t nStart;
+ int32_t nEnd;
+ UScriptCode nCode;
+ Run(int32_t nStart_, int32_t nEnd_, UScriptCode nCode_)
+ : nStart(nStart_), nEnd(nEnd_), nCode(nCode_)
+ {}
+ };
+
+ class TextLayoutCache
+ {
+ public:
+ std::vector<vcl::Run> runs;
+ TextLayoutCache(sal_Unicode const* pStr, sal_Int32 const nEnd)
+ {
+ vcl::ScriptRun aScriptRun(
+ reinterpret_cast<const UChar *>(pStr),
+ nEnd);
+ while (aScriptRun.next())
+ {
+ runs.push_back(Run(aScriptRun.getScriptStart(),
+ aScriptRun.getScriptEnd(), aScriptRun.getScriptCode()));
+ }
+ }
+ };
+}
+
+std::shared_ptr<vcl::TextLayoutCache> ServerFontLayout::CreateTextLayoutCache(
+ OUString const& rString) const
+{
+ return std::make_shared<vcl::TextLayoutCache>(rString.getStr(), rString.getLength());
+}
+
+bool HbLayoutEngine::Layout(ServerFontLayout& rLayout, ImplLayoutArgs& rArgs)
+{
+ ServerFont& rFont = rLayout.GetServerFont();
+ FT_Face aFtFace = rFont.GetFtFace();
+
+ SAL_INFO("vcl.harfbuzz", "layout(" << this << ",rArgs=" << rArgs << ")");
+
+ static hb_font_funcs_t* pHbFontFuncs = getFontFuncs();
+
+ hb_font_t *pHbFont = hb_font_create(mpHbFace);
+ hb_font_set_funcs(pHbFont, pHbFontFuncs, &rFont, nullptr);
+ hb_font_set_scale(pHbFont,
+ ((uint64_t) aFtFace->size->metrics.x_scale * (uint64_t) mnUnitsPerEM) >> 16,
+ ((uint64_t) aFtFace->size->metrics.y_scale * (uint64_t) mnUnitsPerEM) >> 16);
+ hb_font_set_ppem(pHbFont, aFtFace->size->metrics.x_ppem, aFtFace->size->metrics.y_ppem);
+
+ // allocate temporary arrays, note: round to even
+ int nGlyphCapacity = (3 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos) | 15) + 1;
+ int32_t nVirtAdv = int32_t(aFtFace->size->metrics.height*rFont.GetStretch())>>6;
+
+ rLayout.Reserve(nGlyphCapacity);
+
+ const int nLength = rArgs.mrStr.getLength();
+ const sal_Unicode *pStr = rArgs.mrStr.getStr();
+
+ std::unique_ptr<vcl::TextLayoutCache> pNewScriptRun;
+ vcl::TextLayoutCache const* pTextLayout;
+ if (rArgs.m_pTextLayoutCache)
+ {
+ pTextLayout = rArgs.m_pTextLayoutCache; // use cache!
+ }
+ else
+ {
+ pNewScriptRun.reset(new vcl::TextLayoutCache(pStr, rArgs.mnEndCharPos));
+ pTextLayout = pNewScriptRun.get();
+ }
+
+ Point aCurrPos(0, 0);
+ while (true)
+ {
+ int nBidiMinRunPos, nBidiEndRunPos;
+ bool bRightToLeft;
+ if (!rArgs.GetNextRun(&nBidiMinRunPos, &nBidiEndRunPos, &bRightToLeft))
+ break;
+
+ // Find script subruns.
+ int nCurrentPos = nBidiMinRunPos;
+ HbScriptRuns aScriptSubRuns;
+ size_t k = 0;
+ for (; k < pTextLayout->runs.size(); ++k)
+ {
+ vcl::Run const& rRun(pTextLayout->runs[k]);
+ if (rRun.nStart <= nCurrentPos && nCurrentPos < rRun.nEnd)
+ {
+ break;
+ }
+ }
+
+ while (nCurrentPos < nBidiEndRunPos && k < pTextLayout->runs.size())
+ {
+ int32_t nMinRunPos = nCurrentPos;
+ int32_t nEndRunPos = std::min(pTextLayout->runs[k].nEnd, nBidiEndRunPos);
+ HbScriptRun aRun(nMinRunPos, nEndRunPos, pTextLayout->runs[k].nCode);
+ aScriptSubRuns.push_back(aRun);
+
+ nCurrentPos = nEndRunPos;
+ ++k;
+ }
+
+ // RTL subruns should be reversed to ensure that final glyph order is
+ // correct.
+ if (bRightToLeft)
+ std::reverse(aScriptSubRuns.begin(), aScriptSubRuns.end());
+
+ for (HbScriptRuns::iterator it = aScriptSubRuns.begin(); it != aScriptSubRuns.end(); ++it)
+ {
+ int nMinRunPos = it->mnMin;
+ int nEndRunPos = it->mnEnd;
+ int nRunLen = nEndRunPos - nMinRunPos;
+ maHbScript = it->maScript;
+ // hb_language_from_string() accept ISO639-3 language tag except for Chinese.
+ LanguageTag &rTag = rArgs.maLanguageTag;
+ OString sLanguage = OUStringToOString( MsLangId::isChinese(rTag.getLanguageType()) ? rTag.getBcp47():rTag.getLanguage() , RTL_TEXTENCODING_UTF8 );
+
+ int nHbFlags = HB_BUFFER_FLAGS_DEFAULT;
+ if (nMinRunPos == 0)
+ nHbFlags |= HB_BUFFER_FLAG_BOT; /* Beginning-of-text */
+ if (nEndRunPos == nLength)
+ nHbFlags |= HB_BUFFER_FLAG_EOT; /* End-of-text */
+
+ hb_buffer_t *pHbBuffer = hb_buffer_create();
+#if !HB_VERSION_ATLEAST(1, 1, 0)
+ static hb_unicode_funcs_t* pHbUnicodeFuncs = getUnicodeFuncs();
+ hb_buffer_set_unicode_funcs(pHbBuffer, pHbUnicodeFuncs);
+#endif
+ hb_buffer_set_direction(pHbBuffer, bRightToLeft ? HB_DIRECTION_RTL: HB_DIRECTION_LTR);
+ hb_buffer_set_script(pHbBuffer, maHbScript);
+ hb_buffer_set_language(pHbBuffer, hb_language_from_string(sLanguage.getStr(), -1));
+ hb_buffer_set_flags(pHbBuffer, (hb_buffer_flags_t) nHbFlags);
+ hb_buffer_add_utf16(
+ pHbBuffer, reinterpret_cast<uint16_t const *>(pStr), nLength,
+ nMinRunPos, nRunLen);
+ hb_shape(pHbFont, pHbBuffer, nullptr, 0);
+
+ int nRunGlyphCount = hb_buffer_get_length(pHbBuffer);
+ hb_glyph_info_t *pHbGlyphInfos = hb_buffer_get_glyph_infos(pHbBuffer, nullptr);
+ hb_glyph_position_t *pHbPositions = hb_buffer_get_glyph_positions(pHbBuffer, nullptr);
+
+ for (int i = 0; i < nRunGlyphCount; ++i) {
+ int32_t nGlyphIndex = pHbGlyphInfos[i].codepoint;
+ int32_t nCharPos = pHbGlyphInfos[i].cluster;
+
+ // if needed request glyph fallback by updating LayoutArgs
+ if (!nGlyphIndex)
+ {
+ rLayout.SetNeedFallback(rArgs, nCharPos, bRightToLeft);
+ if (SalLayoutFlags::ForFallback & rArgs.mnFlags)
+ continue;
+ }
+
+ // apply vertical flags and glyph substitution
+ // XXX: Use HB_DIRECTION_TTB above and apply whatever flags magic
+ // FixupGlyphIndex() is doing, minus the GSUB part.
+ if (nCharPos >= 0)
+ {
+ sal_UCS4 aChar = rArgs.mrStr[nCharPos];
+ nGlyphIndex = rFont.FixupGlyphIndex(nGlyphIndex, aChar);
+ }
+
+ bool bInCluster = false;
+ if (i > 0 && pHbGlyphInfos[i].cluster == pHbGlyphInfos[i - 1].cluster)
+ bInCluster = true;
+
+ long nGlyphFlags = 0;
+ if (bRightToLeft)
+ nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
+
+ if (bInCluster)
+ nGlyphFlags |= GlyphItem::IS_IN_CLUSTER;
+
+ // The whole IS_DIACRITIC concept is a stupid hack that was
+ // introduced ages ago to work around the utter brokenness of the
+ // way justification adjustments are applied (the DXArray fiasco).
+ // Since it is such a stupid hack, there is no sane way to directly
+ // map to concepts of the "outside" world, so we do some rather
+ // ugly hacks:
+ // * If the font has a GDEF table, we check for glyphs with mark
+ // glyph class which is sensible, except that some fonts
+ // (fdo#70968) assign mark class to spacing marks (which is wrong
+ // but usually harmless), so we try to sniff what HarfBuzz thinks
+ // about this glyph by checking if it gives it a zero advance
+ // width.
+ // * If the font has no GDEF table, we just check if the glyph has
+ // zero advance width, but this is stupid and can be wrong. A
+ // better way would to check the character's Unicode combining
+ // class, but unfortunately glyph gives combining marks the
+ // cluster value of its base character, so nCharPos will be
+ // pointing to the wrong character (but HarfBuzz might change
+ // this in the future).
+ bool bDiacritic = false;
+ if (hb_ot_layout_has_glyph_classes(mpHbFace))
+ {
+ // the font has GDEF table
+ bool bMark = hb_ot_layout_get_glyph_class(mpHbFace, nGlyphIndex) == HB_OT_LAYOUT_GLYPH_CLASS_MARK;
+ if (bMark && pHbPositions[i].x_advance == 0)
+ bDiacritic = true;
+ }
+ else
+ {
+ // the font lacks GDEF table
+ if (pHbPositions[i].x_advance == 0)
+ bDiacritic = true;
+ }
+
+ if (bDiacritic)
+ nGlyphFlags |= GlyphItem::IS_DIACRITIC;
+
+ int32_t nXOffset = pHbPositions[i].x_offset >> 6;
+ int32_t nYOffset = pHbPositions[i].y_offset >> 6;
+ int32_t nXAdvance = pHbPositions[i].x_advance >> 6;
+ int32_t nYAdvance = pHbPositions[i].y_advance >> 6;
+ if ( nGlyphIndex & GF_ROTMASK )
+ nXAdvance = nVirtAdv;
+
+ Point aNewPos = Point(aCurrPos.X() + nXOffset, -(aCurrPos.Y() + nYOffset));
+ const GlyphItem aGI(nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nXAdvance, nXOffset, nYOffset);
+ rLayout.AppendGlyph(aGI);
+
+ aCurrPos.X() += nXAdvance;
+ aCurrPos.Y() += nYAdvance;
+ }
+
+ hb_buffer_destroy(pHbBuffer);
+ }
+ }
+
+ hb_font_destroy(pHbFont);
+
+ // sort glyphs in visual order
+ // and then in logical order (e.g. diacritics after cluster start)
+ // XXX: why?
+ rLayout.SortGlyphItems();
+
+ // determine need for kashida justification
+ if((rArgs.mpDXArray || rArgs.mnLayoutWidth)
+ && ((maHbScript == HB_SCRIPT_ARABIC) || (maHbScript == HB_SCRIPT_SYRIAC)))
+ rArgs.mnFlags |= SalLayoutFlags::KashidaJustification;
+
+ return true;
+}
+
+ServerFontLayoutEngine* ServerFont::GetLayoutEngine()
+{
+ // find best layout engine for font, platform, script and language
+ if (!mpLayoutEngine) {
+ mpLayoutEngine = new HbLayoutEngine(*this);
+ }
+ return mpLayoutEngine;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/glyphs/glyphcache.cxx b/vcl/unx/generic/glyphs/glyphcache.cxx
new file mode 100644
index 000000000000..33f06df2faab
--- /dev/null
+++ b/vcl/unx/generic/glyphs/glyphcache.cxx
@@ -0,0 +1,379 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include "unx/freetype_glyphcache.hxx"
+
+#include <vcl/svapp.hxx>
+#include <vcl/bitmap.hxx>
+#include <fontinstance.hxx>
+#include <fontattributes.hxx>
+
+#include <config_graphite.h>
+#if ENABLE_GRAPHITE
+#include <graphite_features.hxx>
+#endif
+
+#include <rtl/ustring.hxx>
+#include <osl/file.hxx>
+#include <tools/debug.hxx>
+
+static GlyphCache* pInstance = nullptr;
+
+GlyphCache::GlyphCache()
+: mnMaxSize( 1500000 ),
+ mnBytesUsed(sizeof(GlyphCache)),
+ mnLruIndex(0),
+ mnGlyphCount(0),
+ mpCurrentGCFont(nullptr),
+ mpFtManager(nullptr)
+{
+ pInstance = this;
+ mpFtManager = new FreetypeManager;
+}
+
+GlyphCache::~GlyphCache()
+{
+ InvalidateAllGlyphs();
+ delete mpFtManager;
+}
+
+void GlyphCache::InvalidateAllGlyphs()
+{
+ for( FontList::iterator it = maFontList.begin(), end = maFontList.end(); it != end; ++it )
+ {
+ ServerFont* pServerFont = it->second;
+ // free all pServerFont related data
+ pServerFont->GarbageCollect( mnLruIndex+0x10000000 );
+ delete pServerFont;
+ }
+
+ maFontList.clear();
+ mpCurrentGCFont = nullptr;
+}
+
+inline
+size_t GlyphCache::IFSD_Hash::operator()( const FontSelectPattern& rFontSelData ) const
+{
+ // TODO: is it worth to improve this hash function?
+ sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFontSelData.mpFontData );
+#if ENABLE_GRAPHITE
+ if (rFontSelData.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
+ != -1)
+ {
+ OString aFeatName = OUStringToOString( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
+ nFontId ^= aFeatName.hashCode();
+ }
+#endif
+ size_t nHash = nFontId << 8;
+ nHash += rFontSelData.mnHeight;
+ nHash += rFontSelData.mnOrientation;
+ nHash += size_t(rFontSelData.mbVertical);
+ nHash += rFontSelData.GetSlantType();
+ nHash += rFontSelData.GetWeight();
+#if ENABLE_GRAPHITE
+ nHash += rFontSelData.meLanguage;
+#endif
+ return nHash;
+}
+
+bool GlyphCache::IFSD_Equal::operator()( const FontSelectPattern& rA, const FontSelectPattern& rB) const
+{
+ // check font ids
+ sal_IntPtr nFontIdA = reinterpret_cast<sal_IntPtr>( rA.mpFontData );
+ sal_IntPtr nFontIdB = reinterpret_cast<sal_IntPtr>( rB.mpFontData );
+ if( nFontIdA != nFontIdB )
+ return false;
+
+ // compare with the requested metrics
+ if( (rA.mnHeight != rB.mnHeight)
+ || (rA.mnOrientation != rB.mnOrientation)
+ || (rA.mbVertical != rB.mbVertical)
+ || (rA.mbNonAntialiased != rB.mbNonAntialiased) )
+ return false;
+
+ if( (rA.GetSlantType() != rB.GetSlantType())
+ || (rA.GetWeight() != rB.GetWeight()) )
+ return false;
+
+ // NOTE: ignoring meFamily deliberately
+
+ // compare with the requested width, allow default width
+ int nAWidth = rA.mnWidth != 0 ? rA.mnWidth : rA.mnHeight;
+ int nBWidth = rB.mnWidth != 0 ? rB.mnWidth : rB.mnHeight;
+ if( nAWidth != nBWidth )
+ return false;
+
+#if ENABLE_GRAPHITE
+ if (rA.meLanguage != rB.meLanguage)
+ return false;
+ // check for features
+ if ((rA.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
+ != -1 ||
+ rB.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
+ != -1) && rA.maTargetName != rB.maTargetName)
+ return false;
+#endif
+
+ if (rA.mbEmbolden != rB.mbEmbolden)
+ return false;
+
+ if (rA.maItalicMatrix != rB.maItalicMatrix)
+ return false;
+
+ return true;
+}
+
+GlyphCache& GlyphCache::GetInstance()
+{
+ return *pInstance;
+}
+
+void GlyphCache::AddFontFile( const OString& rNormalizedName, int nFaceNum,
+ sal_IntPtr nFontId, const FontAttributes& rDFA)
+{
+ if( mpFtManager )
+ mpFtManager->AddFontFile( rNormalizedName, nFaceNum, nFontId, rDFA);
+}
+
+void GlyphCache::AnnounceFonts( PhysicalFontCollection* pFontCollection ) const
+{
+ if( mpFtManager )
+ mpFtManager->AnnounceFonts( pFontCollection );
+}
+
+void GlyphCache::ClearFontCache()
+{
+ InvalidateAllGlyphs();
+ if (mpFtManager)
+ mpFtManager->ClearFontList();
+}
+
+ServerFont* GlyphCache::CacheFont( const FontSelectPattern& rFontSelData )
+{
+ // a serverfont request has pFontData
+ if( rFontSelData.mpFontData == nullptr )
+ return nullptr;
+ // a serverfont request has a fontid > 0
+ sal_IntPtr nFontId = rFontSelData.mpFontData->GetFontId();
+ if( nFontId <= 0 )
+ return nullptr;
+
+ // the FontList's key mpFontData member is reinterpreted as font id
+ FontSelectPattern aFontSelData = rFontSelData;
+ aFontSelData.mpFontData = reinterpret_cast<PhysicalFontFace*>( nFontId );
+ FontList::iterator it = maFontList.find( aFontSelData );
+ if( it != maFontList.end() )
+ {
+ ServerFont* pFound = it->second;
+ if( pFound )
+ pFound->AddRef();
+ return pFound;
+ }
+
+ // font not cached yet => create new font item
+ ServerFont* pNew = nullptr;
+ if( mpFtManager )
+ pNew = mpFtManager->CreateFont( aFontSelData );
+
+ if( pNew )
+ {
+ maFontList[ aFontSelData ] = pNew;
+ mnBytesUsed += pNew->GetByteCount();
+
+ // enable garbage collection for new font
+ if( !mpCurrentGCFont )
+ {
+ mpCurrentGCFont = pNew;
+ pNew->mpNextGCFont = pNew;
+ pNew->mpPrevGCFont = pNew;
+ }
+ else
+ {
+ pNew->mpNextGCFont = mpCurrentGCFont;
+ pNew->mpPrevGCFont = mpCurrentGCFont->mpPrevGCFont;
+ pNew->mpPrevGCFont->mpNextGCFont = pNew;
+ mpCurrentGCFont->mpPrevGCFont = pNew;
+ }
+ }
+
+ return pNew;
+}
+
+void GlyphCache::UncacheFont( ServerFont& rServerFont )
+{
+ if( (rServerFont.Release() <= 0) && (mnMaxSize <= mnBytesUsed) )
+ {
+ mpCurrentGCFont = &rServerFont;
+ GarbageCollect();
+ }
+}
+
+void GlyphCache::GarbageCollect()
+{
+ // when current GC font has been destroyed get another one
+ if( !mpCurrentGCFont )
+ {
+ FontList::iterator it = maFontList.begin();
+ if( it != maFontList.end() )
+ mpCurrentGCFont = it->second;
+ }
+
+ // unless there is no other font to collect
+ if( !mpCurrentGCFont )
+ return;
+
+ // prepare advance to next font for garbage collection
+ ServerFont* const pServerFont = mpCurrentGCFont;
+ mpCurrentGCFont = pServerFont->mpNextGCFont;
+
+ if( (pServerFont == mpCurrentGCFont) // no other fonts
+ || (pServerFont->GetRefCount() > 0) ) // font still used
+ {
+ // try to garbage collect at least a few bytes
+ pServerFont->GarbageCollect( mnLruIndex - mnGlyphCount/2 );
+ }
+ else // current GC font is unreferenced
+ {
+ DBG_ASSERT( (pServerFont->GetRefCount() == 0),
+ "GlyphCache::GC detected RefCount underflow" );
+
+ // free all pServerFont related data
+ pServerFont->GarbageCollect( mnLruIndex+0x10000000 );
+ if( pServerFont == mpCurrentGCFont )
+ mpCurrentGCFont = nullptr;
+ const FontSelectPattern& rIFSD = pServerFont->GetFontSelData();
+ maFontList.erase( rIFSD );
+ mnBytesUsed -= pServerFont->GetByteCount();
+
+ // remove font from list of garbage collected fonts
+ if( pServerFont->mpPrevGCFont )
+ pServerFont->mpPrevGCFont->mpNextGCFont = pServerFont->mpNextGCFont;
+ if( pServerFont->mpNextGCFont )
+ pServerFont->mpNextGCFont->mpPrevGCFont = pServerFont->mpPrevGCFont;
+ if( pServerFont == mpCurrentGCFont )
+ mpCurrentGCFont = nullptr;
+
+ delete pServerFont;
+ }
+}
+
+inline void GlyphCache::UsingGlyph( ServerFont&, GlyphData& rGlyphData )
+{
+ rGlyphData.SetLruValue( mnLruIndex++ );
+}
+
+inline void GlyphCache::AddedGlyph( ServerFont& rServerFont, GlyphData& rGlyphData )
+{
+ ++mnGlyphCount;
+ mnBytesUsed += sizeof( rGlyphData );
+ UsingGlyph( rServerFont, rGlyphData );
+ GrowNotify();
+}
+
+void GlyphCache::GrowNotify()
+{
+ if( mnBytesUsed > mnMaxSize )
+ GarbageCollect();
+}
+
+inline void GlyphCache::RemovingGlyph()
+{
+ mnBytesUsed -= sizeof( GlyphData );
+ --mnGlyphCount;
+}
+
+void ServerFont::ReleaseFromGarbageCollect()
+{
+ // remove from GC list
+ ServerFont* pPrev = mpPrevGCFont;
+ ServerFont* pNext = mpNextGCFont;
+ if( pPrev ) pPrev->mpNextGCFont = pNext;
+ if( pNext ) pNext->mpPrevGCFont = pPrev;
+ mpPrevGCFont = nullptr;
+ mpNextGCFont = nullptr;
+}
+
+long ServerFont::Release() const
+{
+ DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" );
+ return --mnRefCount;
+}
+
+GlyphData& ServerFont::GetGlyphData( sal_GlyphId aGlyphId )
+{
+ // usually the GlyphData is cached
+ GlyphList::iterator it = maGlyphList.find( aGlyphId );
+ if( it != maGlyphList.end() ) {
+ GlyphData& rGlyphData = it->second;
+ GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData );
+ return rGlyphData;
+ }
+
+ // sometimes not => we need to create and initialize it ourselves
+ GlyphData& rGlyphData = maGlyphList[ aGlyphId ];
+ mnBytesUsed += sizeof( GlyphData );
+ InitGlyphData( aGlyphId, rGlyphData );
+ GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData );
+ return rGlyphData;
+}
+
+void ServerFont::GarbageCollect( long nMinLruIndex )
+{
+ GlyphList::iterator it = maGlyphList.begin();
+ while( it != maGlyphList.end() )
+ {
+ GlyphData& rGD = it->second;
+ if( (nMinLruIndex - rGD.GetLruValue()) > 0 )
+ {
+ OSL_ASSERT( mnBytesUsed >= sizeof(GlyphData) );
+ mnBytesUsed -= sizeof( GlyphData );
+ GlyphCache::GetInstance().RemovingGlyph();
+ it = maGlyphList.erase( it );
+ }
+ else
+ ++it;
+ }
+}
+
+ServerFontInstance::ServerFontInstance( FontSelectPattern& rFSD )
+: LogicalFontInstance( rFSD )
+, mpServerFont( nullptr )
+, mbGotFontOptions( false )
+{}
+
+void ServerFontInstance::SetServerFont(ServerFont* p)
+{
+ if (p == mpServerFont)
+ return;
+ if (mpServerFont)
+ mpServerFont->Release();
+ mpServerFont = p;
+ if (mpServerFont)
+ mpServerFont->AddRef();
+}
+
+ServerFontInstance::~ServerFontInstance()
+{
+ // TODO: remove the ServerFont here instead of in the GlyphCache
+ if (mpServerFont)
+ mpServerFont->Release();
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/glyphs/graphite_serverfont.cxx b/vcl/unx/generic/glyphs/graphite_serverfont.cxx
new file mode 100644
index 000000000000..d2126b05f377
--- /dev/null
+++ b/vcl/unx/generic/glyphs/graphite_serverfont.cxx
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// Header files
+
+// Platform
+#include <i18nlangtag/languagetag.hxx>
+#include <sallayout.hxx>
+// Module
+#include "unx/freetype_glyphcache.hxx"
+#include "unx/glyphcache.hxx"
+#include <graphite_features.hxx>
+#include <graphite_serverfont.hxx>
+
+float freetypeServerFontAdvance(const void* appFontHandle, gr_uint16 glyphId)
+{
+ ServerFont * pServerFont =
+ const_cast<ServerFont*>
+ (static_cast<const ServerFont*>(appFontHandle));
+ if (pServerFont)
+ {
+ return static_cast<float>(pServerFont->GetGlyphMetric(glyphId).GetCharWidth());
+ }
+ return .0f;
+}
+
+// An implementation of the GraphiteLayout interface to enable Graphite enabled fonts to be used.
+
+GraphiteServerFontLayout::GraphiteServerFontLayout(ServerFont& rServerFont) throw()
+ : ServerFontLayout(rServerFont),
+ maImpl(rServerFont.GetGraphiteFace()->face(), rServerFont)
+ , mpFeatures(nullptr)
+{
+ gr_font * pFont = rServerFont.GetGraphiteFace()->font(rServerFont.GetFontSelData().mnHeight, rServerFont.NeedsArtificialBold(), rServerFont.NeedsArtificialItalic());
+ if (!pFont)
+ {
+ pFont = gr_make_font_with_advance_fn(
+ // need to use mnHeight here, mfExactHeight can give wrong values
+ static_cast<float>(rServerFont.GetFontSelData().mnHeight),
+ &rServerFont,
+ freetypeServerFontAdvance,
+ rServerFont.GetGraphiteFace()->face());
+ rServerFont.GetGraphiteFace()->addFont(rServerFont.GetFontSelData().mnHeight, pFont, rServerFont.NeedsArtificialBold(), rServerFont.NeedsArtificialItalic());
+ }
+ maImpl.SetFont(pFont);
+ OString aLang("");
+ if (rServerFont.GetFontSelData().meLanguage != LANGUAGE_DONTKNOW)
+ {
+ aLang = OUStringToOString( LanguageTag( rServerFont.GetFontSelData().meLanguage ).getBcp47(),
+ RTL_TEXTENCODING_UTF8 );
+ }
+ OString name = OUStringToOString(
+ rServerFont.GetFontSelData().maTargetName, RTL_TEXTENCODING_UTF8 );
+#ifdef DEBUG
+ printf("GraphiteServerFontLayout %lx %s size %d %f\n", (long unsigned int)this, name.getStr(),
+ rServerFont.GetMetricsFT().x_ppem,
+ rServerFont.GetFontSelData().mfExactHeight);
+#endif
+ sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1;
+ if (nFeat > 0)
+ {
+ OString aFeat = name.copy(nFeat, name.getLength() - nFeat);
+ mpFeatures = new grutils::GrFeatureParser(
+ rServerFont.GetGraphiteFace()->face(), aFeat, aLang);
+#ifdef DEBUG
+ if (mpFeatures)
+ printf("GraphiteServerFontLayout %s/%s/%s %x language %d features %d errors\n",
+ OUStringToOString( rServerFont.GetFontSelData().GetFamilyName(),
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ OUStringToOString( rServerFont.GetFontSelData().maTargetName,
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ OUStringToOString( rServerFont.GetFontSelData().maSearchName,
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ rServerFont.GetFontSelData().meLanguage,
+ (int)mpFeatures->numFeatures(), mpFeatures->parseErrors());
+#endif
+ }
+ else
+ {
+ mpFeatures = new grutils::GrFeatureParser(
+ rServerFont.GetGraphiteFace()->face(), aLang);
+ }
+ maImpl.SetFeatures(mpFeatures);
+}
+
+GraphiteServerFontLayout::~GraphiteServerFontLayout() throw()
+{
+ delete mpFeatures;
+ mpFeatures = nullptr;
+}
+
+bool GraphiteServerFontLayout::IsGraphiteEnabledFont(ServerFont& rServerFont)
+{
+ if (rServerFont.GetGraphiteFace())
+ {
+#ifdef DEBUG
+ printf("IsGraphiteEnabledFont\n");
+#endif
+ return true;
+ }
+ return false;
+}
+
+sal_GlyphId GraphiteLayoutImpl::getKashidaGlyph(int & width)
+{
+ int nKashidaIndex = mrServerFont.GetGlyphIndex( 0x0640 );
+ if( nKashidaIndex != 0 )
+ {
+ const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nKashidaIndex );
+ width = rGM.GetCharWidth();
+ }
+ else
+ {
+ width = 0;
+ }
+ return nKashidaIndex;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/glyphs/scrptrun.cxx b/vcl/unx/generic/glyphs/scrptrun.cxx
new file mode 100644
index 000000000000..d6557d79a894
--- /dev/null
+++ b/vcl/unx/generic/glyphs/scrptrun.cxx
@@ -0,0 +1,234 @@
+/*
+ *******************************************************************************
+ *
+ * Copyright (c) 1995-2013 International Business Machines Corporation and others
+ *
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * provided that the above copyright notice(s) and this permission notice appear
+ * in all copies of the Software and that both the above copyright notice(s) and
+ * this permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
+ * NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE
+ * LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY
+ * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings in
+ * this Software without prior written authorization of the copyright holder.
+ *
+ *******************************************************************************
+ * file name: scrptrun.cpp
+ *
+ * created on: 10/17/2001
+ * created by: Eric R. Mader
+ */
+/**
+ * This file is largely copied from the ICU project,
+ * under folder source/extra/scrptrun/scrptrun.cpp
+ */
+#include "unicode/utypes.h"
+#include "unicode/uscript.h"
+
+#include "scrptrun.h"
+#include <algorithm>
+
+namespace {
+
+struct PairIndices
+{
+ int8_t ma00[0xff];
+ int8_t ma20[0x7f];
+ int8_t ma30[0x7f];
+
+ PairIndices()
+ {
+ std::fill_n(ma00, 0xff, -1);
+ std::fill_n(ma20, 0x7f, -1);
+ std::fill_n(ma30, 0x7f, -1);
+
+ // characters in the range 0x0000 - 0x007e (inclusive)
+ // ascii paired punctuation
+ ma00[0x28] = 0;
+ ma00[0x29] = 1;
+ ma00[0x3c] = 2;
+ ma00[0x3e] = 3;
+ ma00[0x5b] = 4;
+ ma00[0x5d] = 5;
+ ma00[0x7b] = 6;
+ ma00[0x7d] = 7;
+ // guillemets
+ ma00[0xab] = 8;
+ ma00[0xbb] = 9;
+
+ // characters in the range 0x2000 - 0x207e (inclusive)
+ // general punctuation
+ ma20[0x18] = 10;
+ ma20[0x19] = 11;
+ ma20[0x1c] = 12;
+ ma20[0x1d] = 13;
+ ma20[0x39] = 14;
+ ma20[0x3a] = 15;
+
+ // characters in the range 0x3000 - 0x307e (inclusive)
+ // chinese paired punctuation
+ ma30[0x08] = 16;
+ ma30[0x09] = 17;
+ ma30[0x0a] = 18;
+ ma30[0x0b] = 19;
+ ma30[0x0c] = 20;
+ ma30[0x0d] = 21;
+ ma30[0x0e] = 22;
+ ma30[0x0f] = 23;
+ ma30[0x10] = 24;
+ ma30[0x11] = 25;
+ ma30[0x14] = 26;
+ ma30[0x15] = 27;
+ ma30[0x16] = 28;
+ ma30[0x17] = 29;
+ ma30[0x18] = 30;
+ ma30[0x19] = 31;
+ ma30[0x1a] = 32;
+ ma30[0x1b] = 33;
+ }
+
+ inline int32_t getPairIndex(UChar32 ch) const
+ {
+ if (ch < 0xff)
+ return ma00[ch];
+ if (ch >= 0x2000 && ch < 0x207f)
+ return ma20[ch - 0x2000];
+ if (ch >= 0x3000 && ch < 0x307f)
+ return ma30[ch - 0x3000];
+ return -1;
+ }
+
+};
+
+}
+
+static const PairIndices gPairIndices;
+
+
+namespace vcl {
+
+const char ScriptRun::fgClassID=0;
+
+static inline UBool sameScript(int32_t scriptOne, int32_t scriptTwo)
+{
+ return scriptOne <= USCRIPT_INHERITED || scriptTwo <= USCRIPT_INHERITED || scriptOne == scriptTwo;
+}
+
+UBool ScriptRun::next()
+{
+ int32_t startSP = parenSP; // used to find the first new open character
+ UErrorCode error = U_ZERO_ERROR;
+
+ // if we've fallen off the end of the text, we're done
+ if (scriptEnd >= charLimit) {
+ return false;
+ }
+
+ scriptCode = USCRIPT_COMMON;
+
+ for (scriptStart = scriptEnd; scriptEnd < charLimit; scriptEnd += 1) {
+ UChar high = charArray[scriptEnd];
+ UChar32 ch = high;
+
+ // if the character is a high surrogate and it's not the last one
+ // in the text, see if it's followed by a low surrogate
+ if (high >= 0xD800 && high <= 0xDBFF && scriptEnd < charLimit - 1)
+ {
+ UChar low = charArray[scriptEnd + 1];
+
+ // if it is followed by a low surrogate,
+ // consume it and form the full character
+ if (low >= 0xDC00 && low <= 0xDFFF) {
+ ch = (high - 0xD800) * 0x0400 + low - 0xDC00 + 0x10000;
+ scriptEnd += 1;
+ }
+ }
+
+ UScriptCode sc = uscript_getScript(ch, &error);
+ int32_t pairIndex = gPairIndices.getPairIndex(ch);
+
+ // Paired character handling:
+
+ // if it's an open character, push it onto the stack.
+ // if it's a close character, find the matching open on the
+ // stack, and use that script code. Any non-matching open
+ // characters above it on the stack will be poped.
+ if (pairIndex >= 0) {
+ if ((pairIndex & 1) == 0) {
+ ++parenSP;
+ int32_t nVecSize = parenStack.size();
+ if (parenSP == nVecSize)
+ parenStack.resize(nVecSize + 128);
+ parenStack[parenSP].pairIndex = pairIndex;
+ parenStack[parenSP].scriptCode = scriptCode;
+ } else if (parenSP >= 0) {
+ int32_t pi = pairIndex & ~1;
+
+ while (parenSP >= 0 && parenStack[parenSP].pairIndex != pi) {
+ parenSP -= 1;
+ }
+
+ if (parenSP < startSP) {
+ startSP = parenSP;
+ }
+
+ if (parenSP >= 0) {
+ sc = parenStack[parenSP].scriptCode;
+ }
+ }
+ }
+
+ if (sameScript(scriptCode, sc)) {
+ if (scriptCode <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) {
+ scriptCode = sc;
+
+ // now that we have a final script code, fix any open
+ // characters we pushed before we knew the script code.
+ while (startSP < parenSP) {
+ parenStack[++startSP].scriptCode = scriptCode;
+ }
+ }
+
+ // if this character is a close paired character,
+ // pop it from the stack
+ if (pairIndex >= 0 && (pairIndex & 1) != 0 && parenSP >= 0) {
+ parenSP -= 1;
+ /* decrement startSP only if it is >= 0,
+ decrementing it unnecessarily will lead to memory corruption
+ while processing the above while block.
+ e.g. startSP = -4 , parenSP = -1
+ */
+ if (startSP >= 0) {
+ startSP -= 1;
+ }
+ }
+ } else {
+ // if the run broke on a surrogate pair,
+ // end it before the high surrogate
+ if (ch >= 0x10000) {
+ scriptEnd -= 1;
+ }
+
+ break;
+ }
+ }
+
+ return true;
+}
+
+}
diff --git a/vcl/unx/generic/glyphs/scrptrun.h b/vcl/unx/generic/glyphs/scrptrun.h
new file mode 100644
index 000000000000..2efcff4110ba
--- /dev/null
+++ b/vcl/unx/generic/glyphs/scrptrun.h
@@ -0,0 +1,173 @@
+/*
+ *******************************************************************************
+ *
+ * Copyright (c) 1995-2013 International Business Machines Corporation and others
+ *
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * provided that the above copyright notice(s) and this permission notice appear
+ * in all copies of the Software and that both the above copyright notice(s) and
+ * this permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
+ * NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE
+ * LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY
+ * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings in
+ * this Software without prior written authorization of the copyright holder.
+ *
+ *******************************************************************************
+ * file name: scrptrun.h
+ *
+ * created on: 10/17/2001
+ * created by: Eric R. Mader
+ */
+
+#ifndef INCLUDED_VCL_GENERIC_GLYPHS_SCRPTRUN_H
+#define INCLUDED_VCL_GENERIC_GLYPHS_SCRPTRUN_H
+
+#include <sal/config.h>
+
+#include <sal/types.h>
+#include "unicode/utypes.h"
+#include "unicode/uobject.h"
+#include "unicode/uscript.h"
+#include <vector>
+
+namespace vcl {
+
+struct ParenStackEntry
+{
+ int32_t pairIndex;
+ UScriptCode scriptCode;
+ ParenStackEntry()
+ : pairIndex(0)
+ , scriptCode(USCRIPT_INVALID_CODE)
+ {
+ }
+};
+
+class ScriptRun : public UObject {
+public:
+ ScriptRun();
+
+ ScriptRun(const UChar chars[], int32_t length);
+
+ ScriptRun(const UChar chars[], int32_t start, int32_t length);
+
+ void reset();
+
+ void reset(int32_t start, int32_t count);
+
+ void reset(const UChar chars[], int32_t start, int32_t length);
+
+ int32_t getScriptStart();
+
+ int32_t getScriptEnd();
+
+ UScriptCode getScriptCode();
+
+ UBool next();
+
+ /**
+s * ICU "poor man's RTTI", returns a UClassID for the actual class.
+ *
+ * @stable ICU 2.2
+ */
+ virtual inline UClassID getDynamicClassID() const override { return getStaticClassID(); }
+
+ /**
+ * ICU "poor man's RTTI", returns a UClassID for this class.
+ *
+ * @stable ICU 2.2
+ */
+ static inline UClassID getStaticClassID() { return static_cast<UClassID>(const_cast<char *>(&fgClassID)); }
+
+private:
+
+ int32_t charStart;
+ int32_t charLimit;
+ const UChar *charArray;
+
+ int32_t scriptStart;
+ int32_t scriptEnd;
+ UScriptCode scriptCode;
+
+ std::vector<ParenStackEntry> parenStack;
+ int32_t parenSP;
+
+ /**
+ * The address of this static class variable serves as this class's ID
+ * for ICU "poor man's RTTI".
+ */
+ static const char fgClassID;
+};
+
+inline ScriptRun::ScriptRun()
+{
+ reset(NULL, 0, 0);
+}
+
+inline ScriptRun::ScriptRun(const UChar chars[], int32_t length)
+{
+ reset(chars, 0, length);
+}
+
+inline ScriptRun::ScriptRun(const UChar chars[], int32_t start, int32_t length)
+{
+ reset(chars, start, length);
+}
+
+inline int32_t ScriptRun::getScriptStart()
+{
+ return scriptStart;
+}
+
+inline int32_t ScriptRun::getScriptEnd()
+{
+ return scriptEnd;
+}
+
+inline UScriptCode ScriptRun::getScriptCode()
+{
+ return scriptCode;
+}
+
+inline void ScriptRun::reset()
+{
+ scriptStart = charStart;
+ scriptEnd = charStart;
+ scriptCode = USCRIPT_INVALID_CODE;
+ parenSP = -1;
+ parenStack.resize(128);
+}
+
+inline void ScriptRun::reset(int32_t start, int32_t length)
+{
+ charStart = start;
+ charLimit = start + length;
+
+ reset();
+}
+
+inline void ScriptRun::reset(const UChar chars[], int32_t start, int32_t length)
+{
+ charArray = chars;
+
+ reset(start, length);
+}
+
+}
+
+#endif