diff options
author | Akash Jain <akash96j@gmail.com> | 2016-06-14 14:38:12 +0530 |
---|---|---|
committer | Khaled Hosny <khaledhosny@eglug.org> | 2016-10-18 20:41:29 +0200 |
commit | 2b12f247ff909a3e536034938370642efab011ad (patch) | |
tree | 04c66e74555b62afe287d88ac0cf4e84eada5d7a /vcl | |
parent | 73308f05c18012327731fc5722f89707048e9aba (diff) |
GSoC: Add new CommonSalLayout class
Change-Id: Ic11e573da2f5fd6ef931f53ab674f8894815c3b4
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/Library_vcl.mk | 1 | ||||
-rw-r--r-- | vcl/inc/CommonSalLayout.hxx | 73 | ||||
-rw-r--r-- | vcl/inc/quartz/salgdi.h | 1 | ||||
-rw-r--r-- | vcl/quartz/ctfonts.cxx | 1 | ||||
-rw-r--r-- | vcl/source/gdi/CommonSalLayout.cxx | 408 |
5 files changed, 484 insertions, 0 deletions
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index a0dfee3e551b..67bcbe580d50 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -303,6 +303,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/gdi/virdev \ vcl/source/gdi/wall \ vcl/source/gdi/scrptrun \ + vcl/source/gdi/CommonSalLayout \ vcl/source/bitmap/bitmapfilter \ vcl/source/bitmap/bitmapscalesuper \ vcl/source/bitmap/BitmapScaleConvolution \ diff --git a/vcl/inc/CommonSalLayout.hxx b/vcl/inc/CommonSalLayout.hxx new file mode 100644 index 000000000000..11455673e233 --- /dev/null +++ b/vcl/inc/CommonSalLayout.hxx @@ -0,0 +1,73 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_VCL_INC_COMMONSALLAYOUT_HXX +#define INCLUDED_VCL_INC_COMMONSALLAYOUT_HXX + +#ifdef _WIN32 +#include "win/winlayout.hxx" + +#elif defined(MACOSX) || defined(IOS) +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include "quartz/ctfonts.hxx" + +#else +#include "unx/freetype_glyphcache.hxx" +#endif + +#include <sallayout.hxx> +#include <hb-icu.h> +#include <hb-ot.h> + +class CommonSalLayout : public GenericSalLayout +{ + hb_face_t* mpHbFace; + FontSelectPattern maFontSelData; + css::uno::Reference<css::i18n::XBreakIterator> mxBreak; +#ifdef _WIN32 + HDC mhDC; + HFONT mhFont; +#elif defined(MACOSX) || defined(IOS) + const CoreTextStyle& mrCoreTextStyle; +#else + ServerFont& mrServerFont; +#endif + + hb_font_t* GetHbFont(); +public: +#if defined(_WIN32) + explicit CommonSalLayout(HDC, WinFontInstance&); + void InitFont() const override; +#elif defined(MACOSX) || defined(IOS) + explicit CommonSalLayout(const CoreTextStyle&); + const CoreTextStyle& getFontData() const { return mrCoreTextStyle; }; +#else + explicit CommonSalLayout(ServerFont&); + const ServerFont& getFontData() const { return mrServerFont; }; +#endif + + ~CommonSalLayout(); + void SetNeedFallback(ImplLayoutArgs&, sal_Int32, bool); + void AdjustLayout(ImplLayoutArgs&) override; + bool LayoutText(ImplLayoutArgs&) override; + void DrawText(SalGraphics&) const override; + std::shared_ptr<vcl::TextLayoutCache> CreateTextLayoutCache(OUString const&) const override; +}; + +#endif diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h index 75896412af73..0f5c9f564523 100644 --- a/vcl/inc/quartz/salgdi.h +++ b/vcl/inc/quartz/salgdi.h @@ -103,6 +103,7 @@ public: float mfFontStretch; /// text rotation in radian float mfFontRotation; + FontSelectPattern maFontSelData; private: /// CoreText text style object diff --git a/vcl/quartz/ctfonts.cxx b/vcl/quartz/ctfonts.cxx index bd2ab4f87482..36b155d5241e 100644 --- a/vcl/quartz/ctfonts.cxx +++ b/vcl/quartz/ctfonts.cxx @@ -48,6 +48,7 @@ CoreTextStyle::CoreTextStyle( const FontSelectPattern& rFSD ) : mpFontData( static_cast<CoreTextFontFace const *>(rFSD.mpFontData) ) , mfFontStretch( 1.0 ) , mfFontRotation( 0.0 ) + , maFontSelData( rFSD ) , mpStyleDict( nullptr ) { const FontSelectPattern* const pReqFont = &rFSD; diff --git a/vcl/source/gdi/CommonSalLayout.cxx b/vcl/source/gdi/CommonSalLayout.cxx new file mode 100644 index 000000000000..291153db26bc --- /dev/null +++ b/vcl/source/gdi/CommonSalLayout.cxx @@ -0,0 +1,408 @@ +/* -*- 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 "CommonSalLayout.hxx" + +#include <vcl/unohelp.hxx> +#include <scrptrun.h> +#include <com/sun/star/i18n/CharacterIteratorMode.hpp> +#include <i18nlangtag/mslangid.hxx> +#include <limits> +#include <salgdi.hxx> + +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; + + sal_uLong nLength = 0; +#if defined(_WIN32) + const unsigned char* pBuffer = nullptr; + HDC* phDC = static_cast<HDC*>(pUserData); + nLength = WinFontFace::GetTable(pTagName, pBuffer, *phDC); +#elif defined(MACOSX) || defined(IOS) + unsigned char* pBuffer = nullptr; + CoreTextFontFace* pFont = static_cast<CoreTextFontFace*>(pUserData); + nLength = pFont->GetFontTable(pTagName, nullptr); + if (nLength > 0) + { + pBuffer = new unsigned char[nLength]; + } + pFont->GetFontTable(pTagName, pBuffer); +#else + const unsigned char* pBuffer = nullptr; + ServerFont* pFont = static_cast<ServerFont*>(pUserData); + pBuffer = pFont->GetTable(pTagName, &nLength); +#endif + + hb_blob_t* pBlob = nullptr; + if (pBuffer != nullptr) + pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, HB_MEMORY_MODE_READONLY, nullptr, nullptr); + + return pBlob; +} + +static hb_unicode_funcs_t* getUnicodeFuncs() +{ + static hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_create(hb_icu_get_unicode_funcs()); +#if !HB_VERSION_ATLEAST(1, 1, 0) + hb_unicode_funcs_set_decompose_compatibility_func(ufuncs, unicodeDecomposeCompatibility, nullptr, nullptr); +#endif + return ufuncs; +} + +#if defined(_WIN32) +CommonSalLayout::CommonSalLayout(HDC hDC, WinFontInstance& rWinFontInstance) +: mhDC(hDC), + mpHbFace(nullptr), + maFontSelData(rWinFontInstance.maFontSelData) +{ + mpHbFace = hb_face_create_for_tables(getFontTable, &hDC, nullptr); +} + +#elif defined(MACOSX) || defined(IOS) +CommonSalLayout::CommonSalLayout(const CoreTextStyle& rCoreTextStyle) +: mpHbFace(nullptr), + maFontSelData(rCoreTextStyle.maFontSelData), + mrCoreTextStyle(rCoreTextStyle) +{ + mpHbFace = hb_face_create_for_tables(getFontTable, const_cast<CoreTextFontFace*>(rCoreTextStyle.mpFontData), nullptr); +} + +#else +CommonSalLayout::CommonSalLayout(ServerFont& rServerFont) +: mpHbFace(nullptr), + maFontSelData(rServerFont.GetFontSelData()), + mrServerFont(rServerFont) +{ + mpHbFace = hb_face_create_for_tables(getFontTable, &rServerFont, nullptr); +} +#endif + +CommonSalLayout::~CommonSalLayout() +{ + hb_face_destroy(mpHbFace); +} + +hb_font_t* CommonSalLayout::GetHbFont() +{ + // HACK. TODO: Get rid of HACK +#if defined(_WIN32) + if (maFontSelData.mnWidth) + maFontSelData.mnWidth = (double)maFontSelData.mnWidth*1.812; +#endif + + hb_font_t* pHbFont = hb_font_create(mpHbFace); + hb_font_set_ppem(pHbFont, maFontSelData.mnWidth? maFontSelData.mnWidth:maFontSelData.mnHeight , maFontSelData.mnHeight); + hb_font_set_scale(pHbFont, (uint64_t)(maFontSelData.mnWidth? maFontSelData.mnWidth:maFontSelData.mnHeight) << 6, + (uint64_t)maFontSelData.mnHeight << 6); + hb_ot_font_set_funcs(pHbFont); + return pHbFont; +} + +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> CommonSalLayout::CreateTextLayoutCache(OUString const& rString) const +{ + return std::make_shared<vcl::TextLayoutCache>(rString.getStr(), rString.getLength()); +} + +void CommonSalLayout::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); +} + +void CommonSalLayout::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); + + if ((rArgs.mnFlags & SalLayoutFlags::KashidaJustification) && rArgs.mpDXArray) + { + hb_font_t* pHbFont = GetHbFont(); + hb_codepoint_t nKashidaCodePoint = 0x0640; + hb_codepoint_t nKashidaGlyphIndex; + + if (hb_font_get_glyph(pHbFont, nKashidaCodePoint, 0, &nKashidaGlyphIndex)) + { + if (nKashidaGlyphIndex) + { + KashidaJustify(nKashidaGlyphIndex, hb_font_get_glyph_h_advance(pHbFont, nKashidaGlyphIndex) >> 6); + } + } + hb_font_destroy(pHbFont); + } +} + +void CommonSalLayout::DrawText(SalGraphics&) const +{ + //call platform dependent DrawText functions +} + +bool CommonSalLayout::LayoutText(ImplLayoutArgs& rArgs) +{ + hb_font_t* pHbFont = GetHbFont(); + hb_script_t aHbScript = HB_SCRIPT_INVALID; + + int nGlyphCapacity = 2 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos); + 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; + aHbScript = 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(); + static hb_unicode_funcs_t* pHbUnicodeFuncs = getUnicodeFuncs(); +#if !HB_VERSION_ATLEAST(1, 1, 0) + 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, aHbScript); + 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); +#if HB_VERSION_ATLEAST(0, 9, 42) + hb_buffer_set_cluster_level(pHbBuffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); +#endif + 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) + { + SetNeedFallback(rArgs, nCharPos, bRightToLeft); + if (SalLayoutFlags::ForFallback & rArgs.mnFlags) + continue; + } + + sal_Int32 indexUtf16 = nCharPos; + sal_UCS4 aChar = rArgs.mrStr.iterateCodePoints(&indexUtf16, 0); + + 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; + + 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 + { +#if HB_VERSION_ATLEAST(0, 9, 42) + if (hb_unicode_general_category (pHbUnicodeFuncs, aChar) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) + bDiacritic = true; +#else + // the font lacks GDEF table + if (pHbPositions[i].x_advance == 0) + bDiacritic = true; +#endif + } + + 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; + + Point aNewPos = Point(aCurrPos.X() + nXOffset, -(aCurrPos.Y() + nYOffset)); + const GlyphItem aGI(nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nXAdvance, nXOffset); + 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? + SortGlyphItems(); + + // determine need for kashida justification + if ((rArgs.mpDXArray || rArgs.mnLayoutWidth) + && ((aHbScript == HB_SCRIPT_ARABIC) || (aHbScript == HB_SCRIPT_SYRIAC))) + rArgs.mnFlags |= SalLayoutFlags::KashidaJustification; + + return true; +} |