summaryrefslogtreecommitdiffstats
path: root/vcl/quartz/salgdi.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vcl/quartz/salgdi.cxx888
1 files changed, 888 insertions, 0 deletions
diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx
new file mode 100644
index 000000000..83aebe2ab
--- /dev/null
+++ b/vcl/quartz/salgdi.cxx
@@ -0,0 +1,888 @@
+/* -*- 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 <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 <comphelper/lok.hxx>
+
+#include <vcl/metric.hxx>
+#include <vcl/fontcharmap.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/sysdata.hxx>
+
+#include <quartz/ctfonts.hxx>
+#include <fontsubset.hxx>
+#include <impfont.hxx>
+#include <impfontcharmap.hxx>
+#include <impfontmetricdata.hxx>
+#include <outdev.h>
+#include <PhysicalFontCollection.hxx>
+
+#ifdef MACOSX
+#include <osx/salframe.h>
+#endif
+#include <quartz/utils.h>
+#ifdef IOS
+#include "saldatabasic.hxx"
+#endif
+#include <sallayout.hxx>
+#include <sft.hxx>
+
+using namespace vcl;
+
+namespace {
+
+class CoreTextGlyphFallbackSubstititution
+: public ImplGlyphFallbackFontSubstitution
+{
+public:
+ bool FindFontSubstitute(FontSelectPattern&, LogicalFontInstance* pLogicalFont, OUString&) const override;
+};
+
+}
+
+bool CoreTextGlyphFallbackSubstititution::FindFontSubstitute(FontSelectPattern& rPattern, LogicalFontInstance* pLogicalFont,
+ OUString& rMissingChars) const
+{
+ bool bFound = false;
+ CoreTextStyle* pStyle = static_cast<CoreTextStyle*>(pLogicalFont);
+ CTFontRef pFont = static_cast<CTFontRef>(CFDictionaryGetValue(pStyle->GetStyleDict(), kCTFontAttributeName));
+ CFStringRef pStr = CreateCFString(rMissingChars);
+ if (pStr)
+ {
+ CTFontRef pFallback = CTFontCreateForString(pFont, pStr, CFRangeMake(0, CFStringGetLength(pStr)));
+ if (pFallback)
+ {
+ bFound = true;
+
+ CTFontDescriptorRef pDesc = CTFontCopyFontDescriptor(pFallback);
+ FontAttributes rAttr = DevFontFromCTFontDescriptor(pDesc, nullptr);
+
+ rPattern.maSearchName = rAttr.GetFamilyName();
+
+ rPattern.SetWeight(rAttr.GetWeight());
+ rPattern.SetItalic(rAttr.GetItalic());
+ rPattern.SetPitch(rAttr.GetPitch());
+ rPattern.SetWidthType(rAttr.GetWidthType());
+
+ CFRelease(pFallback);
+ CFRelease(pDesc);
+ }
+ CFRelease(pStr);
+ }
+
+ return bFound;
+}
+
+CoreTextFontFace::CoreTextFontFace( const FontAttributes& rDFA, sal_IntPtr nFontId )
+ : PhysicalFontFace( rDFA )
+ , mnFontId( nFontId )
+ , mbFontCapabilitiesRead( false )
+{
+}
+
+CoreTextFontFace::~CoreTextFontFace()
+{
+}
+
+sal_IntPtr CoreTextFontFace::GetFontId() const
+{
+ return mnFontId;
+}
+
+FontCharMapRef CoreTextFontFace::GetFontCharMap() const
+{
+ // return the cached charmap
+ if( mxCharMap.is() )
+ return mxCharMap;
+
+ // set the default charmap
+ FontCharMapRef pCharMap( new FontCharMap() );
+ mxCharMap = pCharMap;
+
+ // get the CMAP byte size
+ // allocate a buffer for the CMAP raw data
+ const int nBufSize = GetFontTable( "cmap", nullptr );
+ SAL_WARN_IF( (nBufSize <= 0), "vcl", "CoreTextFontFace::GetFontCharMap : GetFontTable1 failed!");
+ if( nBufSize <= 0 )
+ return mxCharMap;
+
+ // get the CMAP raw data
+ std::vector<unsigned char> aBuffer( nBufSize );
+ const int nRawLength = GetFontTable( "cmap", aBuffer.data() );
+ SAL_WARN_IF( (nRawLength <= 0), "vcl", "CoreTextFontFace::GetFontCharMap : GetFontTable2 failed!");
+ if( nRawLength <= 0 )
+ return mxCharMap;
+
+ SAL_WARN_IF( (nBufSize!=nRawLength), "vcl", "CoreTextFontFace::GetFontCharMap : ByteCount mismatch!");
+
+ // parse the CMAP
+ CmapResult aCmapResult;
+ if( ParseCMAP( aBuffer.data(), nRawLength, aCmapResult ) )
+ {
+ FontCharMapRef xDefFontCharMap( new FontCharMap(aCmapResult) );
+ // create the matching charmap
+ mxCharMap = xDefFontCharMap;
+ }
+
+ return mxCharMap;
+}
+
+bool CoreTextFontFace::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
+{
+ // read this only once per font
+ if( mbFontCapabilitiesRead )
+ {
+ rFontCapabilities = maFontCapabilities;
+ return rFontCapabilities.oUnicodeRange || rFontCapabilities.oCodePageRange;
+ }
+ mbFontCapabilitiesRead = true;
+
+ int nBufSize = GetFontTable( "OS/2", nullptr );
+ if( nBufSize > 0 )
+ {
+ // allocate a buffer for the OS/2 raw data
+ std::vector<unsigned char> aBuffer( nBufSize );
+ // get the OS/2 raw data
+ const int nRawLength = GetFontTable( "OS/2", aBuffer.data() );
+ if( nRawLength > 0 )
+ {
+ const unsigned char* pOS2Table = aBuffer.data();
+ vcl::getTTCoverage( maFontCapabilities.oUnicodeRange,
+ maFontCapabilities.oCodePageRange,
+ pOS2Table, nRawLength);
+ }
+ }
+ rFontCapabilities = maFontCapabilities;
+ return rFontCapabilities.oUnicodeRange || rFontCapabilities.oCodePageRange;
+}
+
+AquaSalGraphics::AquaSalGraphics()
+ : mpXorEmulation( nullptr )
+ , mnXorMode( 0 )
+ , mnWidth( 0 )
+ , mnHeight( 0 )
+ , mnBitmapDepth( 0 )
+ , mnRealDPIX( 0 )
+ , mnRealDPIY( 0 )
+ , mxClipPath( nullptr )
+ , maLineColor( COL_WHITE )
+ , maFillColor( COL_BLACK )
+ , maTextColor( COL_BLACK )
+ , mbNonAntialiasedText( false )
+#ifdef MACOSX
+ , mpFrame( nullptr )
+#endif
+ , mbPrinter( false )
+ , mbVirDev( false )
+#ifdef MACOSX
+ , mbWindow( false )
+#else
+ , mbForeignContext( false )
+#endif
+{
+ SAL_INFO( "vcl.quartz", "AquaSalGraphics::AquaSalGraphics() this=" << this );
+
+ for (int i = 0; i < MAX_FALLBACK; ++i)
+ mpTextStyle[i] = nullptr;
+
+ if (comphelper::LibreOfficeKit::isActive())
+ initWidgetDrawBackends(true);
+}
+
+AquaSalGraphics::~AquaSalGraphics()
+{
+ SAL_INFO( "vcl.quartz", "AquaSalGraphics::~AquaSalGraphics() this=" << this );
+
+ if( mxClipPath )
+ {
+ CGPathRelease( mxClipPath );
+ }
+
+ ReleaseFonts();
+
+ if( mpXorEmulation )
+ delete mpXorEmulation;
+
+#ifdef IOS
+ if (mbForeignContext)
+ return;
+#endif
+ if (maLayer.isSet())
+ {
+ CGLayerRelease(maLayer.get());
+ }
+ else if (maContextHolder.isSet()
+#ifdef MACOSX
+ && mbWindow
+#endif
+ )
+ {
+ // destroy backbuffer bitmap context that we created ourself
+ CGContextRelease(maContextHolder.get());
+ maContextHolder.set(nullptr);
+ }
+}
+
+SalGraphicsImpl* AquaSalGraphics::GetImpl() const
+{
+ return nullptr;
+}
+
+void AquaSalGraphics::SetTextColor( Color nColor )
+{
+ maTextColor = RGBAColor( nColor );
+ // SAL_ DEBUG(std::hex << nColor << std::dec << "={" << maTextColor.GetRed() << ", " << maTextColor.GetGreen() << ", " << maTextColor.GetBlue() << ", " << maTextColor.GetAlpha() << "}");
+}
+
+void AquaSalGraphics::GetFontMetric(ImplFontMetricDataRef& rxFontMetric, int nFallbackLevel)
+{
+ if (nFallbackLevel < MAX_FALLBACK && mpTextStyle[nFallbackLevel])
+ {
+ mpTextStyle[nFallbackLevel]->GetFontMetric(rxFontMetric);
+ }
+}
+
+static bool AddTempDevFont(const OUString& rFontFileURL)
+{
+ OUString aUSytemPath;
+ OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSytemPath ) );
+ OString aCFileName = OUStringToOString( aUSytemPath, 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( PhysicalFontCollection* pFontCollection )
+{
+ SAL_WARN_IF( !pFontCollection, "vcl", "AquaSalGraphics::GetDevFontList(NULL) !");
+
+ AddLocalTempFontDirs();
+
+ // The idea is to cache the list of system fonts once it has been generated.
+ // SalData seems to be a good place for this caching. However we have to
+ // carefully make the access to the font list thread-safe. If we register
+ // a font-change event handler to update the font list in case fonts have
+ // changed on the system we have to lock access to the list. The right
+ // way to do that is the solar mutex since GetDevFontList is protected
+ // through it as should be all event handlers
+
+ SalData* pSalData = GetSalData();
+ if( !pSalData->mpFontList )
+ 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();
+ delete pSalData->mpFontList;
+ pSalData->mpFontList = nullptr;
+}
+
+bool AquaSalGraphics::AddTempDevFont( PhysicalFontCollection*,
+ const OUString& rFontFileURL, const OUString& /*rFontName*/ )
+{
+ return ::AddTempDevFont(rFontFileURL);
+}
+
+void AquaSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout)
+{
+#ifdef IOS
+ if (!CheckContext())
+ {
+ SAL_WARN("vcl.quartz", "AquaSalGraphics::DrawTextLayout() without context");
+ return;
+ }
+#endif
+
+ const CoreTextStyle& rStyle = *static_cast<const CoreTextStyle*>(&rLayout.GetFont());
+ const FontSelectPattern& rFontSelect = rStyle.GetFontSelectPattern();
+ if (rFontSelect.mnHeight == 0)
+ {
+ SAL_WARN("vcl.quartz", "AquaSalGraphics::DrawTextLayout(): rFontSelect.mnHeight is zero!?");
+ return;
+ }
+
+ CTFontRef pFont = static_cast<CTFontRef>(CFDictionaryGetValue(rStyle.GetStyleDict(), kCTFontAttributeName));
+ CGAffineTransform aRotMatrix = CGAffineTransformMakeRotation(-rStyle.mfFontRotation);
+
+ Point 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.X(), -aPos.Y());
+
+ // Whether the glyph should be upright in vertical mode or not
+ bool bUprightGlyph = false;
+
+ if (rStyle.mfFontRotation)
+ {
+ if (pGlyph->IsVertical())
+ {
+ bUprightGlyph = true;
+ // Adjust the position of upright (vertical) glyphs.
+ aGCPos.y -= CTFontGetAscent(pFont) - CTFontGetDescent(pFont);
+ }
+ 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
+
+ maContextHolder.saveState();
+
+ // The view is vertically flipped (no idea why), flip it back.
+ CGContextScaleCTM(maContextHolder.get(), 1.0, -1.0);
+ CGContextSetShouldAntialias(maContextHolder.get(), !mbNonAntialiasedText);
+ CGContextSetFillColor(maContextHolder.get(), maTextColor.AsArray());
+
+ if (rStyle.mbFauxBold)
+ {
+
+ float fSize = rFontSelect.mnHeight / 23.0f;
+ CGContextSetStrokeColor(maContextHolder.get(), maTextColor.AsArray());
+ CGContextSetLineWidth(maContextHolder.get(), fSize);
+ CGContextSetTextDrawingMode(maContextHolder.get(), kCGTextFillStroke);
+ }
+
+ 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);
+
+ maContextHolder.saveState();
+ if (rStyle.mfFontRotation && !bUprightGlyph)
+ {
+ CGContextRotateCTM(maContextHolder.get(), rStyle.mfFontRotation);
+ }
+ CTFontDrawGlyphs(pFont, &aGlyphIds[nStartIndex], &aGlyphPos[nStartIndex], nLen, maContextHolder.get());
+ maContextHolder.restoreState();
+
+ aIt = aNext;
+ }
+
+ maContextHolder.restoreState();
+}
+
+void AquaSalGraphics::SetFont(LogicalFontInstance* pReqFont, int nFallbackLevel)
+{
+ // release the text style
+ for (int i = nFallbackLevel; i < MAX_FALLBACK; ++i)
+ {
+ if (!mpTextStyle[i])
+ break;
+ mpTextStyle[i].clear();
+ }
+
+ if (!pReqFont)
+ return;
+
+ // update the text style
+ mpTextStyle[nFallbackLevel] = static_cast<CoreTextStyle*>(pReqFont);
+}
+
+std::unique_ptr<GenericSalLayout> AquaSalGraphics::GetTextLayout(int nFallbackLevel)
+{
+ assert(mpTextStyle[nFallbackLevel]);
+ if (!mpTextStyle[nFallbackLevel])
+ return nullptr;
+ return std::make_unique<GenericSalLayout>(*mpTextStyle[nFallbackLevel]);
+}
+
+FontCharMapRef AquaSalGraphics::GetFontCharMap() const
+{
+ if (!mpTextStyle[0])
+ {
+ return FontCharMapRef( new FontCharMap() );
+ }
+
+ return static_cast<const CoreTextFontFace*>(mpTextStyle[0]->GetFontFace())->GetFontCharMap();
+}
+
+bool AquaSalGraphics::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
+{
+ if (!mpTextStyle[0])
+ return false;
+
+ return static_cast<const CoreTextFontFace*>(mpTextStyle[0]->GetFontFace())->GetFontCapabilities(rFontCapabilities);
+}
+
+// fake a SFNT font directory entry for a font table
+// see http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html#Directory
+static void FakeDirEntry( const char aTag[5], ByteCount nOfs, ByteCount nLen,
+ const unsigned char* /*pData*/, unsigned char*& rpDest )
+{
+ // write entry tag
+ rpDest[ 0] = aTag[0];
+ rpDest[ 1] = aTag[1];
+ rpDest[ 2] = aTag[2];
+ rpDest[ 3] = aTag[3];
+ // TODO: get entry checksum and write it
+ // not too important since the subsetter doesn't care currently
+ // for( pData+nOfs ... pData+nOfs+nLen )
+ // write entry offset
+ rpDest[ 8] = static_cast<char>(nOfs >> 24);
+ rpDest[ 9] = static_cast<char>(nOfs >> 16);
+ rpDest[10] = static_cast<char>(nOfs >> 8);
+ rpDest[11] = static_cast<char>(nOfs >> 0);
+ // write entry length
+ rpDest[12] = static_cast<char>(nLen >> 24);
+ rpDest[13] = static_cast<char>(nLen >> 16);
+ rpDest[14] = static_cast<char>(nLen >> 8);
+ rpDest[15] = static_cast<char>(nLen >> 0);
+ // advance to next entry
+ rpDest += 16;
+}
+
+// fake a TTF or CFF font as directly accessing font file is not possible
+// when only the fontid is known. This approach also handles *.font fonts.
+bool AquaSalGraphics::GetRawFontData( const PhysicalFontFace* pFontData,
+ std::vector<unsigned char>& rBuffer, bool* pJustCFF )
+{
+ const CoreTextFontFace* pMacFont = static_cast<const CoreTextFontFace*>(pFontData);
+
+ // short circuit for CFF-only fonts
+ const int nCffSize = pMacFont->GetFontTable( "CFF ", nullptr);
+ if( pJustCFF != nullptr )
+ {
+ *pJustCFF = (nCffSize > 0);
+ if( *pJustCFF)
+ {
+ rBuffer.resize( nCffSize);
+ const int nCffRead = pMacFont->GetFontTable( "CFF ", rBuffer.data());
+ if( nCffRead != nCffSize)
+ {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ // get font table availability and size in bytes
+ const int nHeadSize = pMacFont->GetFontTable( "head", nullptr);
+ if( nHeadSize <= 0)
+ return false;
+
+ const int nMaxpSize = pMacFont->GetFontTable( "maxp", nullptr);
+ if( nMaxpSize <= 0)
+ return false;
+
+ const int nCmapSize = pMacFont->GetFontTable( "cmap", nullptr);
+ if( nCmapSize <= 0)
+ return false;
+
+ const int nNameSize = pMacFont->GetFontTable( "name", nullptr);
+ if( nNameSize <= 0)
+ return false;
+
+ const int nHheaSize = pMacFont->GetFontTable( "hhea", nullptr);
+ if( nHheaSize <= 0)
+ return false;
+
+ const int nHmtxSize = pMacFont->GetFontTable( "hmtx", nullptr);
+ if( nHmtxSize <= 0)
+ return false;
+
+ // get the ttf-glyf outline tables
+ int nLocaSize = 0;
+ int nGlyfSize = 0;
+ if( nCffSize <= 0)
+ {
+ nLocaSize = pMacFont->GetFontTable( "loca", nullptr);
+ if( nLocaSize <= 0)
+ return false;
+
+ nGlyfSize = pMacFont->GetFontTable( "glyf", nullptr);
+ if( nGlyfSize <= 0)
+ return false;
+ }
+
+ int nPrepSize = 0, nCvtSize = 0, nFpgmSize = 0;
+ if( nGlyfSize) // TODO: reduce PDF size by making hint subsetting optional
+ {
+ nPrepSize = pMacFont->GetFontTable( "prep", nullptr);
+ nCvtSize = pMacFont->GetFontTable( "cvt ", nullptr);
+ nFpgmSize = pMacFont->GetFontTable( "fpgm", nullptr);
+ }
+
+ // prepare a byte buffer for a fake font
+ int nTableCount = 7;
+ nTableCount += (nPrepSize>0?1:0) + (nCvtSize>0?1:0) + (nFpgmSize>0?1:0) + (nGlyfSize>0?1:0);
+ const ByteCount nFdirSize = 12 + 16*nTableCount;
+ ByteCount nTotalSize = nFdirSize;
+ nTotalSize += nHeadSize + nMaxpSize + nNameSize + nCmapSize;
+
+ if( nGlyfSize )
+ {
+ nTotalSize += nLocaSize + nGlyfSize;
+ }
+ else
+ {
+ nTotalSize += nCffSize;
+ }
+ nTotalSize += nHheaSize + nHmtxSize;
+ nTotalSize += nPrepSize + nCvtSize + nFpgmSize;
+ rBuffer.resize( nTotalSize );
+
+ // fake a SFNT font directory header
+ if( nTableCount < 16 )
+ {
+ int nLog2 = 0;
+ while( (nTableCount >> nLog2) > 1 ) ++nLog2;
+ rBuffer[ 1] = 1; // Win-TTF style scaler
+ rBuffer[ 5] = nTableCount; // table count
+ rBuffer[ 7] = nLog2*16; // searchRange
+ rBuffer[ 9] = nLog2; // entrySelector
+ rBuffer[11] = (nTableCount-nLog2)*16; // rangeShift
+ }
+
+ // get font table raw data and update the fake directory entries
+ ByteCount nOfs = nFdirSize;
+ unsigned char* pFakeEntry = &rBuffer[12];
+ if( nCmapSize != pMacFont->GetFontTable( "cmap", &rBuffer[nOfs]))
+ return false;
+
+ FakeDirEntry( "cmap", nOfs, nCmapSize, rBuffer.data(), pFakeEntry );
+ nOfs += nCmapSize;
+ if( nCvtSize )
+ {
+ if( nCvtSize != pMacFont->GetFontTable( "cvt ", &rBuffer[nOfs]))
+ return false;
+
+ FakeDirEntry( "cvt ", nOfs, nCvtSize, rBuffer.data(), pFakeEntry );
+ nOfs += nCvtSize;
+ }
+ if( nFpgmSize )
+ {
+ if( nFpgmSize != pMacFont->GetFontTable( "fpgm", &rBuffer[nOfs]))
+ return false;
+
+ FakeDirEntry( "fpgm", nOfs, nFpgmSize, rBuffer.data(), pFakeEntry );
+ nOfs += nFpgmSize;
+ }
+ if( nCffSize )
+ {
+ if( nCffSize != pMacFont->GetFontTable( "CFF ", &rBuffer[nOfs]))
+ return false;
+
+ FakeDirEntry( "CFF ", nOfs, nCffSize, rBuffer.data(), pFakeEntry );
+ nOfs += nGlyfSize;
+ }
+ else
+ {
+ if( nGlyfSize != pMacFont->GetFontTable( "glyf", &rBuffer[nOfs]))
+ return false;
+
+ FakeDirEntry( "glyf", nOfs, nGlyfSize, rBuffer.data(), pFakeEntry );
+ nOfs += nGlyfSize;
+
+ if( nLocaSize != pMacFont->GetFontTable( "loca", &rBuffer[nOfs]))
+ return false;
+
+ FakeDirEntry( "loca", nOfs, nLocaSize, rBuffer.data(), pFakeEntry );
+ nOfs += nLocaSize;
+ }
+ if( nHeadSize != pMacFont->GetFontTable( "head", &rBuffer[nOfs]))
+ return false;
+
+ FakeDirEntry( "head", nOfs, nHeadSize, rBuffer.data(), pFakeEntry );
+ nOfs += nHeadSize;
+
+ if( nHheaSize != pMacFont->GetFontTable( "hhea", &rBuffer[nOfs]))
+ return false;
+
+ FakeDirEntry( "hhea", nOfs, nHheaSize, rBuffer.data(), pFakeEntry );
+ nOfs += nHheaSize;
+ if( nHmtxSize != pMacFont->GetFontTable( "hmtx", &rBuffer[nOfs]))
+ return false;
+
+ FakeDirEntry( "hmtx", nOfs, nHmtxSize, rBuffer.data(), pFakeEntry );
+ nOfs += nHmtxSize;
+ if( nMaxpSize != pMacFont->GetFontTable( "maxp", &rBuffer[nOfs]))
+ return false;
+
+ FakeDirEntry( "maxp", nOfs, nMaxpSize, rBuffer.data(), pFakeEntry );
+ nOfs += nMaxpSize;
+ if( nNameSize != pMacFont->GetFontTable( "name", &rBuffer[nOfs]))
+ return false;
+
+ FakeDirEntry( "name", nOfs, nNameSize, rBuffer.data(), pFakeEntry );
+ nOfs += nNameSize;
+ if( nPrepSize )
+ {
+ if( nPrepSize != pMacFont->GetFontTable( "prep", &rBuffer[nOfs]))
+ return false;
+
+ FakeDirEntry( "prep", nOfs, nPrepSize, rBuffer.data(), pFakeEntry );
+ nOfs += nPrepSize;
+ }
+
+ SAL_WARN_IF( (nOfs!=nTotalSize), "vcl", "AquaSalGraphics::GetRawFontData (nOfs!=nTotalSize)");
+
+ return true;
+}
+
+void AquaSalGraphics::GetGlyphWidths( const PhysicalFontFace* pFontData, bool bVertical,
+ std::vector< sal_Int32 >& rGlyphWidths, Ucs2UIntMap& rUnicodeEnc )
+{
+ rGlyphWidths.clear();
+ rUnicodeEnc.clear();
+
+ std::vector<unsigned char> aBuffer;
+ if( !GetRawFontData( pFontData, aBuffer, nullptr ) )
+ return;
+
+ // TODO: modernize psprint's horrible fontsubset C-API
+ // this probably only makes sense after the switch to another SCM
+ // that can preserve change history after file renames
+
+ // use the font subsetter to get the widths
+ TrueTypeFont* pSftFont = nullptr;
+ SFErrCodes nRC = ::OpenTTFontBuffer( static_cast<void*>(aBuffer.data()), aBuffer.size(), 0, &pSftFont);
+ if( nRC != SFErrCodes::Ok )
+ return;
+
+ const int nGlyphCount = ::GetTTGlyphCount( pSftFont );
+ if( nGlyphCount > 0 )
+ {
+ // get glyph metrics
+ rGlyphWidths.resize(nGlyphCount);
+ std::vector<sal_uInt16> aGlyphIds(nGlyphCount);
+ for( int i = 0; i < nGlyphCount; i++ )
+ {
+ aGlyphIds[i] = static_cast<sal_uInt16>(i);
+ }
+
+ std::unique_ptr<sal_uInt16[]> pGlyphMetrics = ::GetTTSimpleGlyphMetrics( pSftFont, aGlyphIds.data(),
+ nGlyphCount, bVertical );
+ if( pGlyphMetrics )
+ {
+ for( int i = 0; i < nGlyphCount; ++i )
+ {
+ rGlyphWidths[i] = pGlyphMetrics[i];
+ }
+ pGlyphMetrics.reset();
+ }
+
+ rtl::Reference<CoreTextFontFace> rCTFontData(new CoreTextFontFace(*pFontData, pFontData->GetFontId()));
+ FontCharMapRef xFCMap = rCTFontData->GetFontCharMap();
+ SAL_WARN_IF( !xFCMap.is() || !xFCMap->GetCharCount(), "vcl", "no charmap" );
+
+ // get unicode<->glyph encoding
+ // TODO? avoid sft mapping by using the xFCMap itself
+ int nCharCount = xFCMap->GetCharCount();
+ sal_uInt32 nChar = xFCMap->GetFirstChar();
+ for( ; --nCharCount >= 0; nChar = xFCMap->GetNextChar( nChar ) )
+ {
+ if( nChar > 0xFFFF ) // TODO: allow UTF-32 chars
+ break;
+
+ sal_Ucs nUcsChar = static_cast<sal_Ucs>(nChar);
+ sal_uInt32 nGlyph = ::MapChar( pSftFont, nUcsChar );
+ if( nGlyph > 0 )
+ {
+ rUnicodeEnc[ nUcsChar ] = nGlyph;
+ }
+ }
+
+ xFCMap = nullptr;
+ }
+
+ ::CloseTTFont( pSftFont );
+}
+
+const void* AquaSalGraphics::GetEmbedFontData(const PhysicalFontFace*, long* /*pDataLen*/)
+{
+ return nullptr;
+}
+
+void AquaSalGraphics::FreeEmbedFontData( const void* pData, long /*nDataLen*/ )
+{
+ // TODO: implementing this only makes sense when the implementation of
+ // AquaSalGraphics::GetEmbedFontData() returns non-NULL
+ SAL_WARN_IF( (pData==nullptr), "vcl", "AquaSalGraphics::FreeEmbedFontData() is not implemented");
+}
+
+bool AquaSalGraphics::IsFlipped() const
+{
+#ifdef MACOSX
+ return mbWindow;
+#else
+ return false;
+#endif
+}
+
+void AquaSalGraphics::RefreshRect(float lX, float lY, float lWidth, float lHeight)
+{
+#ifdef MACOSX
+ if( ! mbWindow ) // view only on Window graphics
+ return;
+
+ if( mpFrame )
+ {
+ // update a little more around the designated rectangle
+ // this helps with antialiased rendering
+ // Rounding down x and width can accumulate a rounding error of up to 2
+ // The decrementing of x, the rounding error and the antialiasing border
+ // require that the width and the height need to be increased by four
+ const tools::Rectangle aVclRect(Point(static_cast<long int>(lX-1),
+ static_cast<long int>(lY-1) ),
+ Size( static_cast<long int>(lWidth+4),
+ static_cast<long int>(lHeight+4) ) );
+ mpFrame->maInvalidRect.Union( aVclRect );
+ }
+#else
+ (void) lX;
+ (void) lY;
+ (void) lWidth;
+ (void) lHeight;
+ return;
+#endif
+}
+
+#ifdef IOS
+
+bool AquaSalGraphics::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;
+}
+
+CGContextRef AquaSalGraphics::GetContext()
+{
+ if (!maContextHolder.isSet())
+ CheckContext();
+
+ return maContextHolder.get();
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */