solenv/clang-format/excludelist | 2 vcl/Library_vcl.mk | 4 vcl/Library_vclplug_osx.mk | 4 vcl/inc/quartz/CoreTextFont.hxx | 74 ++++++++++ vcl/inc/quartz/CoreTextFontFace.hxx | 69 +++++++++ vcl/inc/quartz/SystemFontList.hxx | 50 ++++++- vcl/inc/quartz/salgdi.h | 73 ---------- vcl/osx/saldata.cxx | 1 vcl/quartz/CoreTextFont.cxx | 235 +++++++++++++++++++++++++++++++++ vcl/quartz/CoreTextFontFace.cxx | 98 ++++++++++++++ vcl/quartz/SystemFontList.cxx | 251 ------------------------------------ vcl/quartz/salgdi.cxx | 22 --- vcl/skia/osx/gdiimpl.cxx | 3 13 files changed, 536 insertions(+), 350 deletions(-)
New commits: commit 3b7eb222411da48a9b7a585b39ef7a240f6222bc Author: Khaled Hosny <[email protected]> AuthorDate: Wed Aug 9 12:03:06 2023 +0300 Commit: خالد حسني <[email protected]> CommitDate: Wed Aug 9 12:42:57 2023 +0200 vcl: Organize CoreText font code a bit The code was all over the place with classes split between files or grouped in some files and I couldn’t make a head or tail of it. Move each class to a dedicated source/header file. Change-Id: I35daa05b4684c13339c637819dc30fa47a60cf65 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155503 Tested-by: Jenkins Reviewed-by: خالد حسني <[email protected]> diff --git a/solenv/clang-format/excludelist b/solenv/clang-format/excludelist index 64eded82acab..ce890e04fb61 100644 --- a/solenv/clang-format/excludelist +++ b/solenv/clang-format/excludelist @@ -14558,7 +14558,7 @@ vcl/qa/cppunit/graphicfilter/filters-tga-test.cxx vcl/qa/cppunit/lifecycle.cxx vcl/qa/cppunit/svm/svmtest.cxx vcl/qa/cppunit/timer.cxx -vcl/quartz/ctfonts.cxx +vcl/quartz/SystemFontList.cxx vcl/quartz/salbmp.cxx vcl/quartz/salgdi.cxx vcl/quartz/salgdicommon.cxx diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index c29801fe58a3..f56c913f656f 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -688,7 +688,9 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/ios/iOSTransferable \ vcl/ios/DataFlavorMapping \ vcl/ios/HtmlFmtFlt \ - vcl/quartz/ctfonts \ + vcl/quartz/CoreTextFont \ + vcl/quartz/CoreTextFontFace \ + vcl/quartz/SystemFontList \ vcl/quartz/salbmp \ vcl/quartz/salgdi \ vcl/quartz/salgdicommon \ diff --git a/vcl/Library_vclplug_osx.mk b/vcl/Library_vclplug_osx.mk index fbb0e68ec904..0545af8dd33a 100644 --- a/vcl/Library_vclplug_osx.mk +++ b/vcl/Library_vclplug_osx.mk @@ -134,7 +134,9 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_osx,\ vcl/osx/salsys \ vcl/osx/saltimer \ vcl/osx/service_entry \ - vcl/quartz/ctfonts \ + vcl/quartz/CoreTextFont \ + vcl/quartz/CoreTextFontFace \ + vcl/quartz/SystemFontList \ vcl/quartz/salbmp \ vcl/quartz/salgdi \ vcl/quartz/salgdicommon \ diff --git a/vcl/inc/quartz/CoreTextFont.hxx b/vcl/inc/quartz/CoreTextFont.hxx new file mode 100644 index 000000000000..90cf6416772c --- /dev/null +++ b/vcl/inc/quartz/CoreTextFont.hxx @@ -0,0 +1,74 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <premac.h> +#ifdef MACOSX +#include <ApplicationServices/ApplicationServices.h> +#include <osx/osxvcltypes.h> +#include <osx/salframe.h> +#else +#include <CoreGraphics/CoreGraphics.h> +#include <CoreText/CoreText.h> +#endif +#include <postmac.h> + +#ifdef IOS +// iOS defines a different Point class so include salgeom.hxx after postmac.h +// so that it will use the Point class in tools/gen.hxx +#include "salgeom.hxx" +#endif + +#include <font/LogicalFontInstance.hxx> +#include <font/FontMetricData.hxx> +#include <salgdi.hxx> + +#include <quartz/salgdicommon.hxx> + +#include <quartz/CGHelpers.hxx> +#include <quartz/CoreTextFontFace.hxx> + +class CoreTextFont final : public LogicalFontInstance +{ + friend rtl::Reference<LogicalFontInstance> + CoreTextFontFace::CreateFontInstance(const vcl::font::FontSelectPattern&) const; + +public: + ~CoreTextFont() override; + + void GetFontMetric(FontMetricDataRef const&); + bool GetGlyphOutline(sal_GlyphId, basegfx::B2DPolyPolygon&, bool) const override; + + CTFontRef GetCTFont() const { return mpCTFont; } + + /// <1.0: font is squeezed, >1.0 font is stretched, else 1.0 + float mfFontStretch; + /// text rotation in radian + float mfFontRotation; + +private: + explicit CoreTextFont(const CoreTextFontFace&, const vcl::font::FontSelectPattern&); + + CTFontRef mpCTFont; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/quartz/CoreTextFontFace.hxx b/vcl/inc/quartz/CoreTextFontFace.hxx new file mode 100644 index 000000000000..482b0b700dae --- /dev/null +++ b/vcl/inc/quartz/CoreTextFontFace.hxx @@ -0,0 +1,69 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <vector> + +#include <premac.h> +#ifdef MACOSX +#include <ApplicationServices/ApplicationServices.h> +#include <osx/osxvcltypes.h> +#include <osx/salframe.h> +#else +#include <CoreGraphics/CoreGraphics.h> +#include <CoreText/CoreText.h> +#endif +#include <postmac.h> + +#include <font/LogicalFontInstance.hxx> +#include <font/PhysicalFontFace.hxx> +#include <salgdi.hxx> + +#include <quartz/salgdicommon.hxx> +#include <hb.h> + +class AquaSalFrame; +class FontAttributes; + +// CoreText-specific physically available font face +class CoreTextFontFace final : public vcl::font::PhysicalFontFace +{ +public: + CoreTextFontFace(const FontAttributes&, CTFontDescriptorRef xRef); + ~CoreTextFontFace() override; + + sal_IntPtr GetFontId() const override; + + CTFontDescriptorRef GetFontDescriptorRef() const { return mxFontDescriptor; } + + rtl::Reference<LogicalFontInstance> + CreateFontInstance(const vcl::font::FontSelectPattern&) const override; + + hb_blob_t* GetHbTable(hb_tag_t nTag) const override; + + const std::vector<hb_variation_t>& GetVariations(const LogicalFontInstance&) const override; + +private: + CTFontDescriptorRef mxFontDescriptor; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/quartz/ctfonts.hxx b/vcl/inc/quartz/SystemFontList.hxx similarity index 53% rename from vcl/inc/quartz/ctfonts.hxx rename to vcl/inc/quartz/SystemFontList.hxx index be68dea71c9a..f923b47998a7 100644 --- a/vcl/inc/quartz/ctfonts.hxx +++ b/vcl/inc/quartz/SystemFontList.hxx @@ -17,15 +17,51 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#ifndef INCLUDED_VCL_INC_QUARTZ_CTFONTS_HXX -#define INCLUDED_VCL_INC_QUARTZ_CTFONTS_HXX +#pragma once -#include <quartz/salgdi.h> -#include <sallayout.hxx> +#include <sal/config.h> -std::unique_ptr<SystemFontList> GetCoretextFontList(); -FontAttributes DevFontFromCTFontDescriptor(CTFontDescriptorRef, bool*); +#include <premac.h> +#ifdef MACOSX +#include <ApplicationServices/ApplicationServices.h> +#include <osx/osxvcltypes.h> +#include <osx/salframe.h> +#else +#include <CoreGraphics/CoreGraphics.h> +#include <CoreText/CoreText.h> +#endif +#include <postmac.h> + +#include <font/PhysicalFontFace.hxx> + +#include <unordered_map> + +// TODO: move into cross-platform headers + +class CoreTextFontFace; + +class SystemFontList +{ +public: + SystemFontList(); + ~SystemFontList(); -#endif // INCLUDED_VCL_INC_QUARTZ_CTFONTS_HXX + bool Init(); + void AddFont(CoreTextFontFace*); + + void AnnounceFonts(vcl::font::PhysicalFontCollection&) const; + CoreTextFontFace* GetFontDataFromId(sal_IntPtr nFontId) const; + + CTFontCollectionRef fontCollection() { return mpCTFontCollection; } + +private: + CTFontCollectionRef mpCTFontCollection; + CFArrayRef mpCTFontArray; + + std::unordered_map<sal_IntPtr, rtl::Reference<CoreTextFontFace>> maFontContainer; +}; + +FontAttributes DevFontFromCTFontDescriptor(CTFontDescriptorRef, bool*); +std::unique_ptr<SystemFontList> GetCoretextFontList(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h index 24b657e10886..c8befcc50280 100644 --- a/vcl/inc/quartz/salgdi.h +++ b/vcl/inc/quartz/salgdi.h @@ -23,7 +23,6 @@ #include <vector> -#include <basegfx/polygon/b2dpolypolygon.hxx> #include <tools/long.hxx> #include <premac.h> @@ -49,85 +48,15 @@ #include <font/LogicalFontInstance.hxx> #include <font/FontMetricData.hxx> -#include <font/PhysicalFontFace.hxx> #include <salgdi.hxx> #include <quartz/salgdicommon.hxx> -#include <unordered_map> -#include <hb-ot.h> #include <quartz/CGHelpers.hxx> class AquaSalFrame; -class FontAttributes; class XorEmulation; - -// CoreText-specific physically available font face -class CoreTextFontFace final : public vcl::font::PhysicalFontFace -{ -public: - CoreTextFontFace( const FontAttributes&, CTFontDescriptorRef xRef ); - ~CoreTextFontFace() override; - - sal_IntPtr GetFontId() const override; - - CTFontDescriptorRef GetFontDescriptorRef() const { return mxFontDescriptor; } - - rtl::Reference<LogicalFontInstance> CreateFontInstance(const vcl::font::FontSelectPattern&) const override; - - hb_blob_t* GetHbTable(hb_tag_t nTag) const override; - - const std::vector<hb_variation_t>& GetVariations(const LogicalFontInstance&) const override; - -private: - CTFontDescriptorRef mxFontDescriptor; -}; - -class CoreTextFont final : public LogicalFontInstance -{ - friend rtl::Reference<LogicalFontInstance> CoreTextFontFace::CreateFontInstance(const vcl::font::FontSelectPattern&) const; - -public: - ~CoreTextFont() override; - - void GetFontMetric( FontMetricDataRef const & ); - bool GetGlyphOutline(sal_GlyphId, basegfx::B2DPolyPolygon&, bool) const override; - - CTFontRef GetCTFont() const { return mpCTFont; } - - /// <1.0: font is squeezed, >1.0 font is stretched, else 1.0 - float mfFontStretch; - /// text rotation in radian - float mfFontRotation; - -private: - explicit CoreTextFont(const CoreTextFontFace&, const vcl::font::FontSelectPattern&); - - CTFontRef mpCTFont; -}; - -// TODO: move into cross-platform headers - -class SystemFontList -{ -public: - SystemFontList( void ); - ~SystemFontList( void ); - - bool Init( void ); - void AddFont( CoreTextFontFace* ); - - void AnnounceFonts( vcl::font::PhysicalFontCollection& ) const; - CoreTextFontFace* GetFontDataFromId( sal_IntPtr nFontId ) const; - - CTFontCollectionRef fontCollection() { return mpCTFontCollection; } - -private: - CTFontCollectionRef mpCTFontCollection; - CFArrayRef mpCTFontArray; - - std::unordered_map<sal_IntPtr, rtl::Reference<CoreTextFontFace>> maFontContainer; -}; +class CoreTextFont; namespace sal::aqua { diff --git a/vcl/osx/saldata.cxx b/vcl/osx/saldata.cxx index e3b6703d0878..1f49d1ef1e14 100644 --- a/vcl/osx/saldata.cxx +++ b/vcl/osx/saldata.cxx @@ -32,6 +32,7 @@ #include <bitmaps.hlst> #include <cursor_hotspots.hxx> #include <quartz/salgdi.h> +#include <quartz/SystemFontList.hxx> #import "apple_remote/RemoteMainController.h" diff --git a/vcl/quartz/CoreTextFont.cxx b/vcl/quartz/CoreTextFont.cxx new file mode 100644 index 000000000000..6248a255c64e --- /dev/null +++ b/vcl/quartz/CoreTextFont.cxx @@ -0,0 +1,235 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 <sal/config.h> +#include <sal/log.hxx> + +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> + +#ifdef MACOSX +#include <osx/saldata.hxx> +#include <osx/salinst.h> +#endif +#include <font/LogicalFontInstance.hxx> +#include <impglyphitem.hxx> +#include <quartz/CoreTextFont.hxx> +#include <quartz/CoreTextFontFace.hxx> +#include <quartz/salgdi.h> +#include <quartz/utils.h> +#include <hb.h> + +CoreTextFont::CoreTextFont(const CoreTextFontFace& rPFF, const vcl::font::FontSelectPattern& rFSP) + : LogicalFontInstance(rPFF, rFSP) + , mfFontStretch(1.0) + , mfFontRotation(0.0) + , mpCTFont(nullptr) +{ + double fScaledFontHeight = rFSP.mfExactHeight; + + // convert font rotation to radian + mfFontRotation = toRadians(rFSP.mnOrientation); + + // dummy matrix so we can use CGAffineTransformConcat() below + CGAffineTransform aMatrix = CGAffineTransformMakeTranslation(0, 0); + + // handle font stretching if any + if ((rFSP.mnWidth != 0) && (rFSP.mnWidth != rFSP.mnHeight)) + { + mfFontStretch = float(rFSP.mnWidth) / rFSP.mnHeight; + aMatrix = CGAffineTransformConcat(aMatrix, CGAffineTransformMakeScale(mfFontStretch, 1.0F)); + } + + // artificial italic + if (NeedsArtificialItalic()) + aMatrix = CGAffineTransformConcat( + aMatrix, CGAffineTransformMake(1, 0, ARTIFICIAL_ITALIC_SKEW, 1, 0, 0)); + + CTFontDescriptorRef pFontDesc = rPFF.GetFontDescriptorRef(); + mpCTFont = CTFontCreateWithFontDescriptor(pFontDesc, fScaledFontHeight, &aMatrix); +} + +CoreTextFont::~CoreTextFont() +{ + if (mpCTFont) + CFRelease(mpCTFont); +} + +void CoreTextFont::GetFontMetric(FontMetricDataRef const& rxFontMetric) +{ + rxFontMetric->ImplCalcLineSpacing(this); + rxFontMetric->ImplInitBaselines(this); + + // since FontMetricData::mnWidth is only used for stretching/squeezing fonts + // setting this width to the pixel height of the fontsize is good enough + // it also makes the calculation of the stretch factor simple + rxFontMetric->SetWidth(lrint(CTFontGetSize(mpCTFont) * mfFontStretch)); + + rxFontMetric->SetMinKashida(GetKashidaWidth()); +} + +namespace +{ +// callbacks from CTFontCreatePathForGlyph+CGPathApply for GetGlyphOutline() +struct GgoData +{ + basegfx::B2DPolygon maPolygon; + basegfx::B2DPolyPolygon* mpPolyPoly; +}; +} + +static void MyCGPathApplierFunc(void* pData, const CGPathElement* pElement) +{ + basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon; + const int nPointCount = rPolygon.count(); + + switch (pElement->type) + { + case kCGPathElementCloseSubpath: + case kCGPathElementMoveToPoint: + if (nPointCount > 0) + { + static_cast<GgoData*>(pData)->mpPolyPoly->append(rPolygon); + rPolygon.clear(); + } + // fall through for kCGPathElementMoveToPoint: + if (pElement->type != kCGPathElementMoveToPoint) + { + break; + } + [[fallthrough]]; + case kCGPathElementAddLineToPoint: + rPolygon.append(basegfx::B2DPoint(+pElement->points[0].x, -pElement->points[0].y)); + break; + + case kCGPathElementAddCurveToPoint: + rPolygon.append(basegfx::B2DPoint(+pElement->points[2].x, -pElement->points[2].y)); + rPolygon.setNextControlPoint( + nPointCount - 1, basegfx::B2DPoint(pElement->points[0].x, -pElement->points[0].y)); + rPolygon.setPrevControlPoint( + nPointCount + 0, basegfx::B2DPoint(pElement->points[1].x, -pElement->points[1].y)); + break; + + case kCGPathElementAddQuadCurveToPoint: + { + const basegfx::B2DPoint aStartPt = rPolygon.getB2DPoint(nPointCount - 1); + const basegfx::B2DPoint aCtrPt1((aStartPt.getX() + 2 * pElement->points[0].x) / 3.0, + (aStartPt.getY() - 2 * pElement->points[0].y) / 3.0); + const basegfx::B2DPoint aCtrPt2( + (+2 * pElement->points[0].x + pElement->points[1].x) / 3.0, + (-2 * pElement->points[0].y - pElement->points[1].y) / 3.0); + rPolygon.append(basegfx::B2DPoint(+pElement->points[1].x, -pElement->points[1].y)); + rPolygon.setNextControlPoint(nPointCount - 1, aCtrPt1); + rPolygon.setPrevControlPoint(nPointCount + 0, aCtrPt2); + } + break; + } +} + +bool CoreTextFont::GetGlyphOutline(sal_GlyphId nId, basegfx::B2DPolyPolygon& rResult, bool) const +{ + rResult.clear(); + + CGGlyph nCGGlyph = nId; + + SAL_WNODEPRECATED_DECLARATIONS_PUSH + const CTFontOrientation aFontOrientation = kCTFontDefaultOrientation; + SAL_WNODEPRECATED_DECLARATIONS_POP + CGRect aCGRect + = CTFontGetBoundingRectsForGlyphs(mpCTFont, aFontOrientation, &nCGGlyph, nullptr, 1); + + if (!CGRectIsNull(aCGRect) && CGRectIsEmpty(aCGRect)) + { + // CTFontCreatePathForGlyph returns NULL for blank glyphs, but we want + // to return true for them. + return true; + } + + CGPathRef xPath = CTFontCreatePathForGlyph(mpCTFont, nCGGlyph, nullptr); + if (!xPath) + { + return false; + } + + GgoData aGgoData; + aGgoData.mpPolyPoly = &rResult; + CGPathApply(xPath, static_cast<void*>(&aGgoData), MyCGPathApplierFunc); +#if 0 // TODO: does OSX ensure that the last polygon is always closed? + const CGPathElement aClosingElement = { kCGPathElementCloseSubpath, NULL }; + MyCGPathApplierFunc( (void*)&aGgoData, &aClosingElement ); +#endif + CFRelease(xPath); + + return true; +} + +hb_blob_t* CoreTextFontFace::GetHbTable(hb_tag_t nTag) const +{ + hb_blob_t* pBlob = nullptr; + CTFontRef pFont = CTFontCreateWithFontDescriptor(mxFontDescriptor, 0.0, nullptr); + + if (!nTag) + { + // If nTag is 0, the whole font data is requested. CoreText does not + // give us that, so we will construct an HarfBuzz face from CoreText + // table data and return the blob of that face. + + auto pTags = CTFontCopyAvailableTables(pFont, kCTFontTableOptionNoOptions); + CFIndex nTags = pTags ? CFArrayGetCount(pTags) : 0; + if (nTags > 0) + { + hb_face_t* pHbFace = hb_face_builder_create(); + for (CFIndex i = 0; i < nTags; i++) + { + auto nTable = reinterpret_cast<intptr_t>(CFArrayGetValueAtIndex(pTags, i)); + assert(nTable); + auto pTable = GetHbTable(nTable); + assert(pTable); + hb_face_builder_add_table(pHbFace, nTable, pTable); + } + pBlob = hb_face_reference_blob(pHbFace); + + hb_face_destroy(pHbFace); + } + if (pTags) + CFRelease(pTags); + } + else + { + CFDataRef pData = CTFontCopyTable(pFont, nTag, kCTFontTableOptionNoOptions); + const CFIndex nLength = pData ? CFDataGetLength(pData) : 0; + if (nLength > 0) + { + auto pBuffer = new UInt8[nLength]; + const CFRange aRange = CFRangeMake(0, nLength); + CFDataGetBytes(pData, aRange, pBuffer); + + pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, + HB_MEMORY_MODE_READONLY, pBuffer, + [](void* data) { delete[] static_cast<UInt8*>(data); }); + } + if (pData) + CFRelease(pData); + } + + CFRelease(pFont); + return pBlob; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/quartz/CoreTextFontFace.cxx b/vcl/quartz/CoreTextFontFace.cxx new file mode 100644 index 000000000000..662b439a9eae --- /dev/null +++ b/vcl/quartz/CoreTextFontFace.cxx @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 <sal/config.h> +#include <sal/log.hxx> + +#ifdef MACOSX +#include <osx/saldata.hxx> +#include <osx/salinst.h> +#endif +#include <font/LogicalFontInstance.hxx> +#include <quartz/CoreTextFont.hxx> +#include <quartz/CoreTextFontFace.hxx> +#include <quartz/salgdi.h> +#include <quartz/utils.h> + +CoreTextFontFace::CoreTextFontFace(const FontAttributes& rDFA, CTFontDescriptorRef xFontDescriptor) + : vcl::font::PhysicalFontFace(rDFA) + , mxFontDescriptor(xFontDescriptor) +{ + CFRetain(mxFontDescriptor); +} + +CoreTextFontFace::~CoreTextFontFace() { CFRelease(mxFontDescriptor); } + +sal_IntPtr CoreTextFontFace::GetFontId() const +{ + return reinterpret_cast<sal_IntPtr>(mxFontDescriptor); +} + +const std::vector<hb_variation_t>& CoreTextFontFace::GetVariations(const LogicalFontInstance&) const +{ + CTFontRef pFont = CTFontCreateWithFontDescriptor(mxFontDescriptor, 0.0, nullptr); + + if (!mxVariations) + { + mxVariations.emplace(); + CFArrayRef pAxes = CTFontCopyVariationAxes(pFont); + if (pAxes) + { + CFDictionaryRef pVariations = CTFontCopyVariation(pFont); + if (pVariations) + { + CFIndex nAxes = CFArrayGetCount(pAxes); + for (CFIndex i = 0; i < nAxes; ++i) + { + auto pAxis = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(pAxes, i)); + if (pAxis) + { + hb_tag_t nTag; + auto pTag = static_cast<CFNumberRef>( + CFDictionaryGetValue(pAxis, kCTFontVariationAxisIdentifierKey)); + if (!pTag) + continue; + CFNumberGetValue(pTag, kCFNumberIntType, &nTag); + + float fValue; + auto pValue + = static_cast<CFNumberRef>(CFDictionaryGetValue(pVariations, pTag)); + if (!pValue) + continue; + CFNumberGetValue(pValue, kCFNumberFloatType, &fValue); + + mxVariations->push_back({ nTag, fValue }); + } + } + CFRelease(pVariations); + } + CFRelease(pAxes); + } + } + + return *mxVariations; +} + +rtl::Reference<LogicalFontInstance> +CoreTextFontFace::CreateFontInstance(const vcl::font::FontSelectPattern& rFSD) const +{ + return new CoreTextFont(*this, rFSD); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/quartz/ctfonts.cxx b/vcl/quartz/SystemFontList.cxx similarity index 52% rename from vcl/quartz/ctfonts.cxx rename to vcl/quartz/SystemFontList.cxx index 3d3298f6f50f..2bc7544c8010 100644 --- a/vcl/quartz/ctfonts.cxx +++ b/vcl/quartz/SystemFontList.cxx @@ -20,269 +20,22 @@ #include <sal/config.h> #include <sal/log.hxx> -#include <basegfx/polygon/b2dpolygon.hxx> -#include <basegfx/matrix/b2dhommatrix.hxx> #include <tools/long.hxx> -#include <vcl/settings.hxx> -#include <quartz/ctfonts.hxx> +#include <quartz/SystemFontList.hxx> #include <impfont.hxx> #ifdef MACOSX #include <osx/saldata.hxx> #include <osx/salinst.h> #endif -#include <font/LogicalFontInstance.hxx> #include <fontattributes.hxx> -#include <impglyphitem.hxx> #include <font/PhysicalFontCollection.hxx> +#include <quartz/CoreTextFontFace.hxx> #include <quartz/salgdi.h> #include <quartz/utils.h> #include <sallayout.hxx> -#include <hb-coretext.h> -CoreTextFont::CoreTextFont(const CoreTextFontFace& rPFF, const vcl::font::FontSelectPattern& rFSP) - : LogicalFontInstance(rPFF, rFSP) - , mfFontStretch( 1.0 ) - , mfFontRotation( 0.0 ) - , mpCTFont(nullptr) -{ - double fScaledFontHeight = rFSP.mfExactHeight; - - // convert font rotation to radian - mfFontRotation = toRadians(rFSP.mnOrientation); - - // dummy matrix so we can use CGAffineTransformConcat() below - CGAffineTransform aMatrix = CGAffineTransformMakeTranslation(0, 0); - - // handle font stretching if any - if( (rFSP.mnWidth != 0) && (rFSP.mnWidth != rFSP.mnHeight) ) - { - mfFontStretch = float(rFSP.mnWidth) / rFSP.mnHeight; - aMatrix = CGAffineTransformConcat(aMatrix, CGAffineTransformMakeScale(mfFontStretch, 1.0F)); - } - - // artificial italic - if (NeedsArtificialItalic()) - aMatrix = CGAffineTransformConcat(aMatrix, CGAffineTransformMake(1, 0, ARTIFICIAL_ITALIC_SKEW, 1, 0, 0)); - - CTFontDescriptorRef pFontDesc = rPFF.GetFontDescriptorRef(); - mpCTFont = CTFontCreateWithFontDescriptor( pFontDesc, fScaledFontHeight, &aMatrix ); -} - -CoreTextFont::~CoreTextFont() -{ - if (mpCTFont) - CFRelease(mpCTFont); -} - -void CoreTextFont::GetFontMetric( FontMetricDataRef const & rxFontMetric ) -{ - rxFontMetric->ImplCalcLineSpacing(this); - rxFontMetric->ImplInitBaselines(this); - - // since FontMetricData::mnWidth is only used for stretching/squeezing fonts - // setting this width to the pixel height of the fontsize is good enough - // it also makes the calculation of the stretch factor simple - rxFontMetric->SetWidth( lrint( CTFontGetSize(mpCTFont) * mfFontStretch) ); - - rxFontMetric->SetMinKashida(GetKashidaWidth()); -} - -namespace { - -// callbacks from CTFontCreatePathForGlyph+CGPathApply for GetGlyphOutline() -struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; }; - -} - -static void MyCGPathApplierFunc( void* pData, const CGPathElement* pElement ) -{ - basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon; - const int nPointCount = rPolygon.count(); - - switch( pElement->type ) - { - case kCGPathElementCloseSubpath: - case kCGPathElementMoveToPoint: - if( nPointCount > 0 ) - { - static_cast<GgoData*>(pData)->mpPolyPoly->append( rPolygon ); - rPolygon.clear(); - } - // fall through for kCGPathElementMoveToPoint: - if( pElement->type != kCGPathElementMoveToPoint ) - { - break; - } - [[fallthrough]]; - case kCGPathElementAddLineToPoint: - rPolygon.append( basegfx::B2DPoint( +pElement->points[0].x, -pElement->points[0].y ) ); - break; - - case kCGPathElementAddCurveToPoint: - rPolygon.append( basegfx::B2DPoint( +pElement->points[2].x, -pElement->points[2].y ) ); - rPolygon.setNextControlPoint( nPointCount - 1, - basegfx::B2DPoint( pElement->points[0].x, - -pElement->points[0].y ) ); - rPolygon.setPrevControlPoint( nPointCount + 0, - basegfx::B2DPoint( pElement->points[1].x, - -pElement->points[1].y ) ); - break; - - case kCGPathElementAddQuadCurveToPoint: - { - const basegfx::B2DPoint aStartPt = rPolygon.getB2DPoint( nPointCount-1 ); - const basegfx::B2DPoint aCtrPt1( (aStartPt.getX() + 2 * pElement->points[0].x) / 3.0, - (aStartPt.getY() - 2 * pElement->points[0].y) / 3.0 ); - const basegfx::B2DPoint aCtrPt2( (+2 * pElement->points[0].x + pElement->points[1].x) / 3.0, - (-2 * pElement->points[0].y - pElement->points[1].y) / 3.0 ); - rPolygon.append( basegfx::B2DPoint( +pElement->points[1].x, -pElement->points[1].y ) ); - rPolygon.setNextControlPoint( nPointCount-1, aCtrPt1 ); - rPolygon.setPrevControlPoint( nPointCount+0, aCtrPt2 ); - } - break; - } -} - -bool CoreTextFont::GetGlyphOutline(sal_GlyphId nId, basegfx::B2DPolyPolygon& rResult, bool) const -{ - rResult.clear(); - - CGGlyph nCGGlyph = nId; - - SAL_WNODEPRECATED_DECLARATIONS_PUSH - const CTFontOrientation aFontOrientation = kCTFontDefaultOrientation; - SAL_WNODEPRECATED_DECLARATIONS_POP - CGRect aCGRect = CTFontGetBoundingRectsForGlyphs(mpCTFont, aFontOrientation, &nCGGlyph, nullptr, 1); - - if (!CGRectIsNull(aCGRect) && CGRectIsEmpty(aCGRect)) - { - // CTFontCreatePathForGlyph returns NULL for blank glyphs, but we want - // to return true for them. - return true; - } - - CGPathRef xPath = CTFontCreatePathForGlyph(mpCTFont, nCGGlyph, nullptr); - if (!xPath) - { - return false; - } - - GgoData aGgoData; - aGgoData.mpPolyPoly = &rResult; - CGPathApply( xPath, static_cast<void*>(&aGgoData), MyCGPathApplierFunc ); -#if 0 // TODO: does OSX ensure that the last polygon is always closed? - const CGPathElement aClosingElement = { kCGPathElementCloseSubpath, NULL }; - MyCGPathApplierFunc( (void*)&aGgoData, &aClosingElement ); -#endif - CFRelease( xPath ); - - return true; -} - -hb_blob_t* CoreTextFontFace::GetHbTable(hb_tag_t nTag) const -{ - hb_blob_t* pBlob = nullptr; - CTFontRef pFont = CTFontCreateWithFontDescriptor(mxFontDescriptor, 0.0, nullptr); - - if (!nTag) - { - // If nTag is 0, the whole font data is requested. CoreText does not - // give us that, so we will construct an HarfBuzz face from CoreText - // table data and return the blob of that face. - - auto pTags = CTFontCopyAvailableTables(pFont, kCTFontTableOptionNoOptions); - CFIndex nTags = pTags ? CFArrayGetCount(pTags) : 0; - if (nTags > 0) - { - hb_face_t* pHbFace = hb_face_builder_create(); - for (CFIndex i = 0; i < nTags; i++) - { - auto nTable = reinterpret_cast<intptr_t>(CFArrayGetValueAtIndex(pTags, i)); - assert(nTable); - auto pTable = GetHbTable(nTable); - assert(pTable); - hb_face_builder_add_table(pHbFace, nTable, pTable); - } - pBlob = hb_face_reference_blob(pHbFace); - - hb_face_destroy(pHbFace); - } - if (pTags) - CFRelease(pTags); - } - else - { - CFDataRef pData = CTFontCopyTable(pFont, nTag, kCTFontTableOptionNoOptions); - const CFIndex nLength = pData ? CFDataGetLength(pData) : 0; - if (nLength > 0) - { - auto pBuffer = new UInt8[nLength]; - const CFRange aRange = CFRangeMake(0, nLength); - CFDataGetBytes(pData, aRange, pBuffer); - - pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, - HB_MEMORY_MODE_READONLY, pBuffer, - [](void* data) { delete[] static_cast<UInt8*>(data); }); - } - if (pData) - CFRelease(pData); - } - - CFRelease(pFont); - return pBlob; -} - -const std::vector<hb_variation_t>& CoreTextFontFace::GetVariations(const LogicalFontInstance&) const -{ - CTFontRef pFont = CTFontCreateWithFontDescriptor(mxFontDescriptor, 0.0, nullptr); - - if (!mxVariations) - { - mxVariations.emplace(); - CFArrayRef pAxes = CTFontCopyVariationAxes(pFont); - if (pAxes) - { - CFDictionaryRef pVariations = CTFontCopyVariation(pFont); - if (pVariations) - { - CFIndex nAxes = CFArrayGetCount(pAxes); - for (CFIndex i = 0; i < nAxes; ++i) - { - auto pAxis = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(pAxes, i)); - if (pAxis) - { - hb_tag_t nTag; - auto pTag = static_cast<CFNumberRef>( - CFDictionaryGetValue(pAxis, kCTFontVariationAxisIdentifierKey)); - if (!pTag) - continue; - CFNumberGetValue(pTag, kCFNumberIntType, &nTag); - - float fValue; - auto pValue - = static_cast<CFNumberRef>(CFDictionaryGetValue(pVariations, pTag)); - if (!pValue) - continue; - CFNumberGetValue(pValue, kCFNumberFloatType, &fValue); - - mxVariations->push_back({ nTag, fValue }); - } - } - CFRelease(pVariations); - } - CFRelease(pAxes); - } - } - - return *mxVariations; -} - -rtl::Reference<LogicalFontInstance> CoreTextFontFace::CreateFontInstance(const vcl::font::FontSelectPattern& rFSD) const -{ - return new CoreTextFont(*this, rFSD); -} FontAttributes DevFontFromCTFontDescriptor( CTFontDescriptorRef pFD, bool* bFontEnabled ) { diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx index 26f1cc32184a..f800e3c15829 100644 --- a/vcl/quartz/salgdi.cxx +++ b/vcl/quartz/salgdi.cxx @@ -39,7 +39,6 @@ #include <vcl/svapp.hxx> #include <vcl/sysdata.hxx> -#include <quartz/ctfonts.hxx> #include <fontsubset.hxx> #include <impfont.hxx> #include <font/FontMetricData.hxx> @@ -61,6 +60,10 @@ #include <skia/osx/gdiimpl.hxx> #endif +#include <quartz/SystemFontList.hxx> +#include <quartz/CoreTextFont.hxx> +#include <quartz/CoreTextFontFace.hxx> + using namespace vcl; namespace { @@ -120,23 +123,6 @@ bool CoreTextGlyphFallbackSubstititution::FindFontSubstitute(vcl::font::FontSele return bFound; } -CoreTextFontFace::CoreTextFontFace( const FontAttributes& rDFA, CTFontDescriptorRef xFontDescriptor ) - : vcl::font::PhysicalFontFace( rDFA ) - , mxFontDescriptor( xFontDescriptor ) -{ - CFRetain(mxFontDescriptor); -} - -CoreTextFontFace::~CoreTextFontFace() -{ - CFRelease(mxFontDescriptor); -} - -sal_IntPtr CoreTextFontFace::GetFontId() const -{ - return reinterpret_cast<sal_IntPtr>(mxFontDescriptor); -} - AquaSalGraphics::AquaSalGraphics(bool bPrinter) : mnRealDPIX( 0 ) , mnRealDPIY( 0 ) diff --git a/vcl/skia/osx/gdiimpl.cxx b/vcl/skia/osx/gdiimpl.cxx index 89ae4224fb8e..d774077b4168 100644 --- a/vcl/skia/osx/gdiimpl.cxx +++ b/vcl/skia/osx/gdiimpl.cxx @@ -25,7 +25,8 @@ #include <tools/sk_app/mac/WindowContextFactory_mac.h> -#include <quartz/ctfonts.hxx> +#include <quartz/CoreTextFont.hxx> +#include <quartz/SystemFontList.hxx> #include <skia/quartz/cgutils.h> #include <SkBitmap.h>
