summaryrefslogtreecommitdiffstats
path: root/vcl/quartz/salgdi.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/quartz/salgdi.cxx')
-rw-r--r--vcl/quartz/salgdi.cxx495
1 files changed, 495 insertions, 0 deletions
diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx
new file mode 100644
index 0000000000..0522ff8d58
--- /dev/null
+++ b/vcl/quartz/salgdi.cxx
@@ -0,0 +1,495 @@
+/* -*- 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 <sal/config.h>
+
+#include <memory>
+
+#include <sal/log.hxx>
+#include <config_folders.h>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <rtl/bootstrap.h>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/long.hxx>
+#include <comphelper/lok.hxx>
+
+#include <vcl/metric.hxx>
+#include <vcl/fontcharmap.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/sysdata.hxx>
+
+#include <fontsubset.hxx>
+#include <impfont.hxx>
+#include <font/FontMetricData.hxx>
+#include <font/fontsubstitution.hxx>
+#include <font/PhysicalFontCollection.hxx>
+
+#ifdef MACOSX
+#include <osx/salframe.h>
+#endif
+#include <quartz/utils.h>
+#ifdef IOS
+#include <ios/iosinst.hxx>
+#endif
+#include <sallayout.hxx>
+
+#include <config_features.h>
+#include <vcl/skia/SkiaHelper.hxx>
+#if HAVE_FEATURE_SKIA
+#include <skia/osx/gdiimpl.hxx>
+#endif
+
+#include <quartz/SystemFontList.hxx>
+#include <quartz/CoreTextFont.hxx>
+#include <quartz/CoreTextFontFace.hxx>
+
+using namespace vcl;
+
+namespace {
+
+class CoreTextGlyphFallbackSubstititution
+: public vcl::font::GlyphFallbackFontSubstitution
+{
+public:
+ bool FindFontSubstitute(vcl::font::FontSelectPattern&, LogicalFontInstance* pLogicalFont, OUString&) const override;
+};
+
+bool FontHasCharacter(CTFontRef pFont, const OUString& rString, sal_Int32 nIndex, sal_Int32 nLen)
+{
+ auto const glyphs = std::make_unique<CGGlyph[]>(nLen);
+ return CTFontGetGlyphsForCharacters(pFont, reinterpret_cast<const UniChar*>(rString.getStr() + nIndex), glyphs.get(), nLen);
+}
+
+}
+
+bool CoreTextGlyphFallbackSubstititution::FindFontSubstitute(vcl::font::FontSelectPattern& rPattern, LogicalFontInstance* pLogicalFont,
+ OUString& rMissingChars) const
+{
+ bool bFound = false;
+ CoreTextFont* pFont = static_cast<CoreTextFont*>(pLogicalFont);
+ CFStringRef pStr = CreateCFString(rMissingChars);
+ if (pStr)
+ {
+ CTFontRef pFallback = CTFontCreateForString(pFont->GetCTFont(), pStr, CFRangeMake(0, CFStringGetLength(pStr)));
+ if (pFallback)
+ {
+ bFound = true;
+
+ // tdf#148470 remove the resolved chars from rMissing to flag which ones are still missing
+ // for an attempt with another font
+ OUStringBuffer aStillMissingChars;
+ for (sal_Int32 nStrIndex = 0; nStrIndex < rMissingChars.getLength();)
+ {
+ sal_Int32 nOldStrIndex = nStrIndex;
+ rMissingChars.iterateCodePoints(&nStrIndex);
+ sal_Int32 nCharLength = nStrIndex - nOldStrIndex;
+ if (!FontHasCharacter(pFallback, rMissingChars, nOldStrIndex, nCharLength))
+ aStillMissingChars.append(rMissingChars.getStr() + nOldStrIndex, nCharLength);
+ }
+ rMissingChars = aStillMissingChars.toString();
+
+ CTFontDescriptorRef pDesc = CTFontCopyFontDescriptor(pFallback);
+ FontAttributes rAttr = DevFontFromCTFontDescriptor(pDesc, nullptr);
+
+ rPattern.maSearchName = rAttr.GetFamilyName();
+
+ CFRelease(pFallback);
+ CFRelease(pDesc);
+ }
+ CFRelease(pStr);
+ }
+
+ return bFound;
+}
+
+AquaSalGraphics::AquaSalGraphics(bool bPrinter)
+ : mnRealDPIX( 0 )
+ , mnRealDPIY( 0 )
+{
+ SAL_INFO( "vcl.quartz", "AquaSalGraphics::AquaSalGraphics() this=" << this );
+
+#if HAVE_FEATURE_SKIA
+ // tdf#146842 Do not use Skia for printing
+ // Skia does not work with a native print graphics contexts. I am not sure
+ // why but from what I can see, the Skia implementation drawing to a bitmap
+ // buffer. However, in an NSPrintOperation, the print view's backing buffer
+ // is CGPDFContext so even if this bug could be solved by blitting the
+ // Skia bitmap buffer, the printed PDF would not have selectable text so
+ // always disable Skia for print graphics contexts.
+ if(!bPrinter && SkiaHelper::isVCLSkiaEnabled())
+ mpBackend.reset(new AquaSkiaSalGraphicsImpl(*this, maShared));
+#else
+ (void)bPrinter;
+ if(false)
+ ;
+#endif
+ else
+ mpBackend.reset(new AquaGraphicsBackend(maShared));
+
+ for (int i = 0; i < MAX_FALLBACK; ++i)
+ mpFont[i] = nullptr;
+
+ if (comphelper::LibreOfficeKit::isActive())
+ initWidgetDrawBackends(true);
+}
+
+AquaSalGraphics::~AquaSalGraphics()
+{
+ SAL_INFO( "vcl.quartz", "AquaSalGraphics::~AquaSalGraphics() this=" << this );
+
+ maShared.unsetClipPath();
+
+ ReleaseFonts();
+
+ maShared.mpXorEmulation.reset();
+
+#ifdef IOS
+ if (maShared.mbForeignContext)
+ return;
+#endif
+ if (maShared.maLayer.isSet())
+ {
+ CGLayerRelease(maShared.maLayer.get());
+ }
+ else if (maShared.maContextHolder.isSet()
+#ifdef MACOSX
+ && maShared.mbWindow
+#endif
+ )
+ {
+ // destroy backbuffer bitmap context that we created ourself
+ CGContextRelease(maShared.maContextHolder.get());
+ maShared.maContextHolder.set(nullptr);
+ }
+}
+
+SalGraphicsImpl* AquaSalGraphics::GetImpl() const
+{
+ return mpBackend->GetImpl();
+}
+
+void AquaSalGraphics::SetTextColor( Color nColor )
+{
+ maShared.maTextColor = nColor;
+}
+
+void AquaSalGraphics::GetFontMetric(FontMetricDataRef& rxFontMetric, int nFallbackLevel)
+{
+ if (nFallbackLevel < MAX_FALLBACK && mpFont[nFallbackLevel])
+ {
+ mpFont[nFallbackLevel]->GetFontMetric(rxFontMetric);
+ }
+}
+
+static bool AddTempDevFont(const OUString& rFontFileURL)
+{
+ OUString aUSystemPath;
+ OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSystemPath ) );
+ OString aCFileName = OUStringToOString( aUSystemPath, RTL_TEXTENCODING_UTF8 );
+
+ CFStringRef rFontPath = CFStringCreateWithCString(nullptr, aCFileName.getStr(), kCFStringEncodingUTF8);
+ CFURLRef rFontURL = CFURLCreateWithFileSystemPath(nullptr, rFontPath, kCFURLPOSIXPathStyle, true);
+
+ CFErrorRef error;
+ bool success = CTFontManagerRegisterFontsForURL(rFontURL, kCTFontManagerScopeProcess, &error);
+ if (!success)
+ {
+ CFRelease(error);
+ }
+ CFRelease(rFontPath);
+ CFRelease(rFontURL);
+
+ return success;
+}
+
+static void AddTempFontDir( const OUString &rFontDirUrl )
+{
+ osl::Directory aFontDir( rFontDirUrl );
+ osl::FileBase::RC rcOSL = aFontDir.open();
+ if( rcOSL == osl::FileBase::E_None )
+ {
+ osl::DirectoryItem aDirItem;
+
+ while( aFontDir.getNextItem( aDirItem, 10 ) == osl::FileBase::E_None )
+ {
+ osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileURL );
+ rcOSL = aDirItem.getFileStatus( aFileStatus );
+ if ( rcOSL == osl::FileBase::E_None )
+ {
+ AddTempDevFont(aFileStatus.getFileURL());
+ }
+ }
+ }
+}
+
+static void AddLocalTempFontDirs()
+{
+ static bool bFirst = true;
+ if( !bFirst )
+ return;
+
+ bFirst = false;
+
+ // add private font files
+
+ OUString aBrandStr( "$BRAND_BASE_DIR" );
+ rtl_bootstrap_expandMacros( &aBrandStr.pData );
+
+ // internal font resources, required for normal operation, like OpenSymbol
+ AddTempFontDir( aBrandStr + "/" LIBO_SHARE_RESOURCE_FOLDER "/common/fonts/" );
+
+ AddTempFontDir( aBrandStr + "/" LIBO_SHARE_FOLDER "/fonts/truetype/" );
+}
+
+void AquaSalGraphics::GetDevFontList(vcl::font::PhysicalFontCollection* pFontCollection)
+{
+ SAL_WARN_IF( !pFontCollection, "vcl", "AquaSalGraphics::GetDevFontList(NULL) !");
+
+ AddLocalTempFontDirs();
+
+ SalData* pSalData = GetSalData();
+ pSalData->mpFontList = GetCoretextFontList();
+
+ // Copy all PhysicalFontFace objects contained in the SystemFontList
+ pSalData->mpFontList->AnnounceFonts( *pFontCollection );
+
+ static CoreTextGlyphFallbackSubstititution aSubstFallback;
+ pFontCollection->SetFallbackHook(&aSubstFallback);
+}
+
+void AquaSalGraphics::ClearDevFontCache()
+{
+ SalData* pSalData = GetSalData();
+ pSalData->mpFontList.reset();
+}
+
+bool AquaSalGraphics::AddTempDevFont(vcl::font::PhysicalFontCollection*,
+ const OUString& rFontFileURL, const OUString& /*rFontName*/)
+{
+ return ::AddTempDevFont(rFontFileURL);
+}
+
+void AquaSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout)
+{
+ mpBackend->drawTextLayout(rLayout);
+}
+
+void AquaGraphicsBackend::drawTextLayout(const GenericSalLayout& rLayout)
+{
+#ifdef IOS
+ if (!mrShared.checkContext())
+ {
+ SAL_WARN("vcl.quartz", "AquaSalGraphics::DrawTextLayout() without context");
+ return;
+ }
+#endif
+
+ const CoreTextFont& rFont = *static_cast<const CoreTextFont*>(&rLayout.GetFont());
+ const vcl::font::FontSelectPattern& rFontSelect = rFont.GetFontSelectPattern();
+ if (rFontSelect.mnHeight == 0)
+ {
+ SAL_WARN("vcl.quartz", "AquaSalGraphics::DrawTextLayout(): rFontSelect.mnHeight is zero!?");
+ return;
+ }
+
+ CTFontRef pCTFont = rFont.GetCTFont();
+ CGAffineTransform aRotMatrix = CGAffineTransformMakeRotation(-rFont.mfFontRotation);
+
+ basegfx::B2DPoint aPos;
+ const GlyphItem* pGlyph;
+ std::vector<CGGlyph> aGlyphIds;
+ std::vector<CGPoint> aGlyphPos;
+ std::vector<bool> aGlyphOrientation;
+ int nStart = 0;
+ while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
+ {
+ CGPoint aGCPos = CGPointMake(aPos.getX(), -aPos.getY());
+
+ // Whether the glyph should be upright in vertical mode or not
+ bool bUprightGlyph = false;
+
+ if (rFont.mfFontRotation)
+ {
+ if (pGlyph->IsVertical())
+ bUprightGlyph = true;
+ else
+ // Transform the position of rotated glyphs.
+ aGCPos = CGPointApplyAffineTransform(aGCPos, aRotMatrix);
+ }
+
+ aGlyphIds.push_back(pGlyph->glyphId());
+ aGlyphPos.push_back(aGCPos);
+ aGlyphOrientation.push_back(bUprightGlyph);
+ }
+
+ if (aGlyphIds.empty())
+ return;
+
+ assert(aGlyphIds.size() == aGlyphPos.size());
+#if 0
+ std::cerr << "aGlyphIds:[";
+ for (unsigned i = 0; i < aGlyphIds.size(); i++)
+ {
+ if (i > 0)
+ std::cerr << ",";
+ std::cerr << aGlyphIds[i];
+ }
+ std::cerr << "]\n";
+ std::cerr << "aGlyphPos:[";
+ for (unsigned i = 0; i < aGlyphPos.size(); i++)
+ {
+ if (i > 0)
+ std::cerr << ",";
+ std::cerr << aGlyphPos[i];
+ }
+ std::cerr << "]\n";
+#endif
+
+ mrShared.maContextHolder.saveState();
+ RGBAColor textColor( mrShared.maTextColor );
+
+ // The view is vertically flipped (no idea why), flip it back.
+ CGContextScaleCTM(mrShared.maContextHolder.get(), 1.0, -1.0);
+ CGContextSetShouldAntialias(mrShared.maContextHolder.get(), !mrShared.mbNonAntialiasedText);
+ CGContextSetFillColor(mrShared.maContextHolder.get(), textColor.AsArray());
+
+ if (rFont.NeedsArtificialBold())
+ {
+
+ float fSize = rFontSelect.mnHeight / 23.0f;
+ CGContextSetStrokeColor(mrShared.maContextHolder.get(), textColor.AsArray());
+ CGContextSetLineWidth(mrShared.maContextHolder.get(), fSize);
+ CGContextSetTextDrawingMode(mrShared.maContextHolder.get(), kCGTextFillStroke);
+ }
+
+ if (rLayout.GetSubpixelPositioning())
+ {
+ CGContextSetAllowsFontSubpixelQuantization(mrShared.maContextHolder.get(), false);
+ CGContextSetShouldSubpixelQuantizeFonts(mrShared.maContextHolder.get(), false);
+ CGContextSetAllowsFontSubpixelPositioning(mrShared.maContextHolder.get(), true);
+ CGContextSetShouldSubpixelPositionFonts(mrShared.maContextHolder.get(), true);
+ }
+
+ auto aIt = aGlyphOrientation.cbegin();
+ while (aIt != aGlyphOrientation.cend())
+ {
+ bool bUprightGlyph = *aIt;
+ // Find the boundary of the run of glyphs with the same rotation, to be
+ // drawn together.
+ auto aNext = std::find(aIt, aGlyphOrientation.cend(), !bUprightGlyph);
+ size_t nStartIndex = std::distance(aGlyphOrientation.cbegin(), aIt);
+ size_t nLen = std::distance(aIt, aNext);
+
+ mrShared.maContextHolder.saveState();
+ if (rFont.mfFontRotation && !bUprightGlyph)
+ {
+ CGContextRotateCTM(mrShared.maContextHolder.get(), rFont.mfFontRotation);
+ }
+ CTFontDrawGlyphs(pCTFont, &aGlyphIds[nStartIndex], &aGlyphPos[nStartIndex], nLen, mrShared.maContextHolder.get());
+ mrShared.maContextHolder.restoreState();
+
+ aIt = aNext;
+ }
+
+ mrShared.maContextHolder.restoreState();
+}
+
+void AquaSalGraphics::SetFont(LogicalFontInstance* pReqFont, int nFallbackLevel)
+{
+ // release the font
+ for (int i = nFallbackLevel; i < MAX_FALLBACK; ++i)
+ {
+ if (!mpFont[i])
+ break;
+ mpFont[i].clear();
+ }
+
+ if (!pReqFont)
+ return;
+
+ // update the font
+ mpFont[nFallbackLevel] = static_cast<CoreTextFont*>(pReqFont);
+}
+
+std::unique_ptr<GenericSalLayout> AquaSalGraphics::GetTextLayout(int nFallbackLevel)
+{
+ assert(mpFont[nFallbackLevel]);
+ if (!mpFont[nFallbackLevel])
+ return nullptr;
+ return std::make_unique<GenericSalLayout>(*mpFont[nFallbackLevel]);
+}
+
+FontCharMapRef AquaSalGraphics::GetFontCharMap() const
+{
+ if (!mpFont[0])
+ {
+ return FontCharMapRef( new FontCharMap() );
+ }
+
+ return mpFont[0]->GetFontFace()->GetFontCharMap();
+}
+
+bool AquaSalGraphics::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
+{
+ if (!mpFont[0])
+ return false;
+
+ return mpFont[0]->GetFontFace()->GetFontCapabilities(rFontCapabilities);
+}
+
+void AquaSalGraphics::Flush()
+{
+ mpBackend->Flush();
+}
+
+void AquaSalGraphics::Flush( const tools::Rectangle& rRect )
+{
+ mpBackend->Flush( rRect );
+}
+
+void AquaSalGraphics::WindowBackingPropertiesChanged()
+{
+ mpBackend->WindowBackingPropertiesChanged();
+}
+
+#ifdef IOS
+
+bool AquaSharedAttributes::checkContext()
+{
+ if (mbForeignContext)
+ {
+ SAL_INFO("vcl.ios", "CheckContext() this=" << this << ", mbForeignContext, return true");
+ return true;
+ }
+
+ SAL_INFO( "vcl.ios", "CheckContext() this=" << this << ", not foreign, return false");
+ return false;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */