1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 #include <config_folders.h>
23 
24 #include <basegfx/matrix/b2dhommatrix.hxx>
25 #include <basegfx/matrix/b2dhommatrixtools.hxx>
26 #include <basegfx/polygon/b2dpolygon.hxx>
27 #include <basegfx/polygon/b2dpolygontools.hxx>
28 #include <basegfx/range/b2drectangle.hxx>
29 #include <osl/file.hxx>
30 #include <osl/process.h>
31 #include <rtl/bootstrap.h>
32 #include <rtl/strbuf.hxx>
33 #include <tools/long.hxx>
34 #include <comphelper/lok.hxx>
35 
36 #include <vcl/metric.hxx>
37 #include <vcl/fontcharmap.hxx>
38 #include <vcl/svapp.hxx>
39 #include <vcl/sysdata.hxx>
40 
41 #include <quartz/ctfonts.hxx>
42 #include <fontsubset.hxx>
43 #include <impfont.hxx>
44 #include <impfontcharmap.hxx>
45 #include <impfontmetricdata.hxx>
46 #include <outdev.h>
47 #include <PhysicalFontCollection.hxx>
48 
49 #ifdef MACOSX
50 #include <osx/salframe.h>
51 #endif
52 #include <quartz/utils.h>
53 #ifdef IOS
54 #include "saldatabasic.hxx"
55 #endif
56 #include <sallayout.hxx>
57 #include <sft.hxx>
58 
59 using namespace vcl;
60 
61 namespace {
62 
63 class CoreTextGlyphFallbackSubstititution
64 :    public ImplGlyphFallbackFontSubstitution
65 {
66 public:
67     bool FindFontSubstitute(FontSelectPattern&, LogicalFontInstance* pLogicalFont, OUString&) const override;
68 };
69 
70 }
71 
FindFontSubstitute(FontSelectPattern & rPattern,LogicalFontInstance * pLogicalFont,OUString & rMissingChars) const72 bool CoreTextGlyphFallbackSubstititution::FindFontSubstitute(FontSelectPattern& rPattern, LogicalFontInstance* pLogicalFont,
73     OUString& rMissingChars) const
74 {
75     bool bFound = false;
76     CoreTextStyle* pStyle = static_cast<CoreTextStyle*>(pLogicalFont);
77     CTFontRef pFont = static_cast<CTFontRef>(CFDictionaryGetValue(pStyle->GetStyleDict(), kCTFontAttributeName));
78     CFStringRef pStr = CreateCFString(rMissingChars);
79     if (pStr)
80     {
81         CTFontRef pFallback = CTFontCreateForString(pFont, pStr, CFRangeMake(0, CFStringGetLength(pStr)));
82         if (pFallback)
83         {
84             bFound = true;
85 
86             CTFontDescriptorRef pDesc = CTFontCopyFontDescriptor(pFallback);
87             FontAttributes rAttr = DevFontFromCTFontDescriptor(pDesc, nullptr);
88 
89             rPattern.maSearchName = rAttr.GetFamilyName();
90 
91             rPattern.SetWeight(rAttr.GetWeight());
92             rPattern.SetItalic(rAttr.GetItalic());
93             rPattern.SetPitch(rAttr.GetPitch());
94             rPattern.SetWidthType(rAttr.GetWidthType());
95 
96             CFRelease(pFallback);
97             CFRelease(pDesc);
98         }
99         CFRelease(pStr);
100     }
101 
102     return bFound;
103 }
104 
CoreTextFontFace(const FontAttributes & rDFA,sal_IntPtr nFontId)105 CoreTextFontFace::CoreTextFontFace( const FontAttributes& rDFA, sal_IntPtr nFontId )
106   : PhysicalFontFace( rDFA )
107   , mnFontId( nFontId )
108   , mbFontCapabilitiesRead( false )
109 {
110 }
111 
~CoreTextFontFace()112 CoreTextFontFace::~CoreTextFontFace()
113 {
114 }
115 
GetFontId() const116 sal_IntPtr CoreTextFontFace::GetFontId() const
117 {
118     return mnFontId;
119 }
120 
GetFontCharMap() const121 FontCharMapRef CoreTextFontFace::GetFontCharMap() const
122 {
123     // return the cached charmap
124     if( mxCharMap.is() )
125         return mxCharMap;
126 
127     // set the default charmap
128     FontCharMapRef pCharMap( new FontCharMap() );
129     mxCharMap = pCharMap;
130 
131     // get the CMAP byte size
132     // allocate a buffer for the CMAP raw data
133     const int nBufSize = GetFontTable( "cmap", nullptr );
134     SAL_WARN_IF( (nBufSize <= 0), "vcl", "CoreTextFontFace::GetFontCharMap : GetFontTable1 failed!");
135     if( nBufSize <= 0 )
136         return mxCharMap;
137 
138     // get the CMAP raw data
139     std::vector<unsigned char> aBuffer( nBufSize );
140     const int nRawLength = GetFontTable( "cmap", aBuffer.data() );
141     SAL_WARN_IF( (nRawLength <= 0), "vcl", "CoreTextFontFace::GetFontCharMap : GetFontTable2 failed!");
142     if( nRawLength <= 0 )
143         return mxCharMap;
144 
145     SAL_WARN_IF( (nBufSize!=nRawLength), "vcl", "CoreTextFontFace::GetFontCharMap : ByteCount mismatch!");
146 
147     // parse the CMAP
148     CmapResult aCmapResult;
149     if( ParseCMAP( aBuffer.data(), nRawLength, aCmapResult ) )
150     {
151         FontCharMapRef xDefFontCharMap( new FontCharMap(aCmapResult) );
152         // create the matching charmap
153         mxCharMap = xDefFontCharMap;
154     }
155 
156     return mxCharMap;
157 }
158 
GetFontCapabilities(vcl::FontCapabilities & rFontCapabilities) const159 bool CoreTextFontFace::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
160 {
161     // read this only once per font
162     if( mbFontCapabilitiesRead )
163     {
164         rFontCapabilities = maFontCapabilities;
165         return rFontCapabilities.oUnicodeRange || rFontCapabilities.oCodePageRange;
166     }
167     mbFontCapabilitiesRead = true;
168 
169     int nBufSize = GetFontTable( "OS/2", nullptr );
170     if( nBufSize > 0 )
171     {
172         // allocate a buffer for the OS/2 raw data
173         std::vector<unsigned char> aBuffer( nBufSize );
174         // get the OS/2 raw data
175         const int nRawLength = GetFontTable( "OS/2", aBuffer.data() );
176         if( nRawLength > 0 )
177         {
178             const unsigned char* pOS2Table = aBuffer.data();
179             vcl::getTTCoverage( maFontCapabilities.oUnicodeRange,
180                                 maFontCapabilities.oCodePageRange,
181                                 pOS2Table, nRawLength);
182         }
183     }
184     rFontCapabilities = maFontCapabilities;
185     return rFontCapabilities.oUnicodeRange || rFontCapabilities.oCodePageRange;
186 }
187 
AquaSalGraphics()188 AquaSalGraphics::AquaSalGraphics()
189     : mpBackend(new AquaGraphicsBackend(maShared))
190     , mnRealDPIX( 0 )
191     , mnRealDPIY( 0 )
192     , maTextColor( COL_BLACK )
193     , mbNonAntialiasedText( false )
194 {
195     SAL_INFO( "vcl.quartz", "AquaSalGraphics::AquaSalGraphics() this=" << this );
196 
197     for (int i = 0; i < MAX_FALLBACK; ++i)
198         mpTextStyle[i] = nullptr;
199 
200     if (comphelper::LibreOfficeKit::isActive())
201         initWidgetDrawBackends(true);
202 }
203 
~AquaSalGraphics()204 AquaSalGraphics::~AquaSalGraphics()
205 {
206     SAL_INFO( "vcl.quartz", "AquaSalGraphics::~AquaSalGraphics() this=" << this );
207 
208     maShared.unsetClipPath();
209 
210     ReleaseFonts();
211 
212     maShared.mpXorEmulation.reset();
213 
214 #ifdef IOS
215     if (maShared.mbForeignContext)
216         return;
217 #endif
218     if (maShared.maLayer.isSet())
219     {
220         CGLayerRelease(maShared.maLayer.get());
221     }
222     else if (maShared.maContextHolder.isSet()
223 #ifdef MACOSX
224              && maShared.mbWindow
225 #endif
226              )
227     {
228         // destroy backbuffer bitmap context that we created ourself
229         CGContextRelease(maShared.maContextHolder.get());
230         maShared.maContextHolder.set(nullptr);
231     }
232 }
233 
GetImpl() const234 SalGraphicsImpl* AquaSalGraphics::GetImpl() const
235 {
236     return mpBackend.get();
237 }
238 
SetTextColor(Color nColor)239 void AquaSalGraphics::SetTextColor( Color nColor )
240 {
241     maTextColor = RGBAColor( nColor );
242     // SAL_ DEBUG(std::hex << nColor << std::dec << "={" << maTextColor.GetRed() << ", " << maTextColor.GetGreen() << ", " << maTextColor.GetBlue() << ", " << maTextColor.GetAlpha() << "}");
243 }
244 
GetFontMetric(ImplFontMetricDataRef & rxFontMetric,int nFallbackLevel)245 void AquaSalGraphics::GetFontMetric(ImplFontMetricDataRef& rxFontMetric, int nFallbackLevel)
246 {
247     if (nFallbackLevel < MAX_FALLBACK && mpTextStyle[nFallbackLevel])
248     {
249         mpTextStyle[nFallbackLevel]->GetFontMetric(rxFontMetric);
250     }
251 }
252 
AddTempDevFont(const OUString & rFontFileURL)253 static bool AddTempDevFont(const OUString& rFontFileURL)
254 {
255     OUString aUSystemPath;
256     OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSystemPath ) );
257     OString aCFileName = OUStringToOString( aUSystemPath, RTL_TEXTENCODING_UTF8 );
258 
259     CFStringRef rFontPath = CFStringCreateWithCString(nullptr, aCFileName.getStr(), kCFStringEncodingUTF8);
260     CFURLRef rFontURL = CFURLCreateWithFileSystemPath(nullptr, rFontPath, kCFURLPOSIXPathStyle, true);
261 
262     CFErrorRef error;
263     bool success = CTFontManagerRegisterFontsForURL(rFontURL, kCTFontManagerScopeProcess, &error);
264     if (!success)
265     {
266         CFRelease(error);
267     }
268     CFRelease(rFontPath);
269     CFRelease(rFontURL);
270 
271     return success;
272 }
273 
AddTempFontDir(const OUString & rFontDirUrl)274 static void AddTempFontDir( const OUString &rFontDirUrl )
275 {
276     osl::Directory aFontDir( rFontDirUrl );
277     osl::FileBase::RC rcOSL = aFontDir.open();
278     if( rcOSL == osl::FileBase::E_None )
279     {
280         osl::DirectoryItem aDirItem;
281 
282         while( aFontDir.getNextItem( aDirItem, 10 ) == osl::FileBase::E_None )
283         {
284             osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileURL );
285             rcOSL = aDirItem.getFileStatus( aFileStatus );
286             if ( rcOSL == osl::FileBase::E_None )
287             {
288                 AddTempDevFont(aFileStatus.getFileURL());
289             }
290         }
291     }
292 }
293 
AddLocalTempFontDirs()294 static void AddLocalTempFontDirs()
295 {
296     static bool bFirst = true;
297     if( !bFirst )
298         return;
299 
300     bFirst = false;
301 
302     // add private font files
303 
304     OUString aBrandStr( "$BRAND_BASE_DIR" );
305     rtl_bootstrap_expandMacros( &aBrandStr.pData );
306 
307     // internal font resources, required for normal operation, like OpenSymbol
308     AddTempFontDir( aBrandStr + "/" LIBO_SHARE_RESOURCE_FOLDER "/common/fonts/" );
309 
310     AddTempFontDir( aBrandStr + "/" LIBO_SHARE_FOLDER "/fonts/truetype/" );
311 }
312 
GetDevFontList(PhysicalFontCollection * pFontCollection)313 void AquaSalGraphics::GetDevFontList( PhysicalFontCollection* pFontCollection )
314 {
315     SAL_WARN_IF( !pFontCollection, "vcl", "AquaSalGraphics::GetDevFontList(NULL) !");
316 
317     AddLocalTempFontDirs();
318 
319     // The idea is to cache the list of system fonts once it has been generated.
320     // SalData seems to be a good place for this caching. However we have to
321     // carefully make the access to the font list thread-safe. If we register
322     // a font-change event handler to update the font list in case fonts have
323     // changed on the system we have to lock access to the list. The right
324     // way to do that is the solar mutex since GetDevFontList is protected
325     // through it as should be all event handlers
326 
327     SalData* pSalData = GetSalData();
328     if( !pSalData->mpFontList )
329         pSalData->mpFontList = GetCoretextFontList();
330 
331     // Copy all PhysicalFontFace objects contained in the SystemFontList
332     pSalData->mpFontList->AnnounceFonts( *pFontCollection );
333 
334     static CoreTextGlyphFallbackSubstititution aSubstFallback;
335     pFontCollection->SetFallbackHook(&aSubstFallback);
336 }
337 
ClearDevFontCache()338 void AquaSalGraphics::ClearDevFontCache()
339 {
340     SalData* pSalData = GetSalData();
341     delete pSalData->mpFontList;
342     pSalData->mpFontList = nullptr;
343 }
344 
AddTempDevFont(PhysicalFontCollection *,const OUString & rFontFileURL,const OUString &)345 bool AquaSalGraphics::AddTempDevFont( PhysicalFontCollection*,
346     const OUString& rFontFileURL, const OUString& /*rFontName*/ )
347 {
348     return ::AddTempDevFont(rFontFileURL);
349 }
350 
DrawTextLayout(const GenericSalLayout & rLayout)351 void AquaSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout)
352 {
353 #ifdef IOS
354     if (!maShared.checkContext())
355     {
356         SAL_WARN("vcl.quartz", "AquaSalGraphics::DrawTextLayout() without context");
357         return;
358     }
359 #endif
360 
361     const CoreTextStyle& rStyle = *static_cast<const CoreTextStyle*>(&rLayout.GetFont());
362     const FontSelectPattern& rFontSelect = rStyle.GetFontSelectPattern();
363     if (rFontSelect.mnHeight == 0)
364     {
365         SAL_WARN("vcl.quartz", "AquaSalGraphics::DrawTextLayout(): rFontSelect.mnHeight is zero!?");
366         return;
367     }
368 
369     CTFontRef pFont = static_cast<CTFontRef>(CFDictionaryGetValue(rStyle.GetStyleDict(), kCTFontAttributeName));
370     CGAffineTransform aRotMatrix = CGAffineTransformMakeRotation(-rStyle.mfFontRotation);
371 
372     Point aPos;
373     const GlyphItem* pGlyph;
374     std::vector<CGGlyph> aGlyphIds;
375     std::vector<CGPoint> aGlyphPos;
376     std::vector<bool> aGlyphOrientation;
377     int nStart = 0;
378     while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
379     {
380         CGPoint aGCPos = CGPointMake(aPos.X(), -aPos.Y());
381 
382         // Whether the glyph should be upright in vertical mode or not
383         bool bUprightGlyph = false;
384 
385         if (rStyle.mfFontRotation)
386         {
387             if (pGlyph->IsVertical())
388             {
389                 bUprightGlyph = true;
390                 // Adjust the position of upright (vertical) glyphs.
391                 aGCPos.y -= CTFontGetAscent(pFont) - CTFontGetDescent(pFont);
392             }
393             else
394             {
395                 // Transform the position of rotated glyphs.
396                 aGCPos = CGPointApplyAffineTransform(aGCPos, aRotMatrix);
397             }
398         }
399 
400         aGlyphIds.push_back(pGlyph->glyphId());
401         aGlyphPos.push_back(aGCPos);
402         aGlyphOrientation.push_back(bUprightGlyph);
403     }
404 
405     if (aGlyphIds.empty())
406         return;
407 
408     assert(aGlyphIds.size() == aGlyphPos.size());
409 #if 0
410     std::cerr << "aGlyphIds:[";
411     for (unsigned i = 0; i < aGlyphIds.size(); i++)
412     {
413         if (i > 0)
414             std::cerr << ",";
415         std::cerr << aGlyphIds[i];
416     }
417     std::cerr << "]\n";
418     std::cerr << "aGlyphPos:[";
419     for (unsigned i = 0; i < aGlyphPos.size(); i++)
420     {
421         if (i > 0)
422             std::cerr << ",";
423         std::cerr << aGlyphPos[i];
424     }
425     std::cerr << "]\n";
426 #endif
427 
428     maShared.maContextHolder.saveState();
429 
430     // The view is vertically flipped (no idea why), flip it back.
431     CGContextScaleCTM(maShared.maContextHolder.get(), 1.0, -1.0);
432     CGContextSetShouldAntialias(maShared.maContextHolder.get(), !mbNonAntialiasedText);
433     CGContextSetFillColor(maShared.maContextHolder.get(), maTextColor.AsArray());
434 
435     if (rStyle.mbFauxBold)
436     {
437 
438         float fSize = rFontSelect.mnHeight / 23.0f;
439         CGContextSetStrokeColor(maShared.maContextHolder.get(), maTextColor.AsArray());
440         CGContextSetLineWidth(maShared.maContextHolder.get(), fSize);
441         CGContextSetTextDrawingMode(maShared.maContextHolder.get(), kCGTextFillStroke);
442     }
443 
444     auto aIt = aGlyphOrientation.cbegin();
445     while (aIt != aGlyphOrientation.cend())
446     {
447         bool bUprightGlyph = *aIt;
448         // Find the boundary of the run of glyphs with the same rotation, to be
449         // drawn together.
450         auto aNext = std::find(aIt, aGlyphOrientation.cend(), !bUprightGlyph);
451         size_t nStartIndex = std::distance(aGlyphOrientation.cbegin(), aIt);
452         size_t nLen = std::distance(aIt, aNext);
453 
454         maShared.maContextHolder.saveState();
455         if (rStyle.mfFontRotation && !bUprightGlyph)
456         {
457             CGContextRotateCTM(maShared.maContextHolder.get(), rStyle.mfFontRotation);
458         }
459         CTFontDrawGlyphs(pFont, &aGlyphIds[nStartIndex], &aGlyphPos[nStartIndex], nLen, maShared.maContextHolder.get());
460         maShared.maContextHolder.restoreState();
461 
462         aIt = aNext;
463     }
464 
465     maShared.maContextHolder.restoreState();
466 }
467 
SetFont(LogicalFontInstance * pReqFont,int nFallbackLevel)468 void AquaSalGraphics::SetFont(LogicalFontInstance* pReqFont, int nFallbackLevel)
469 {
470     // release the text style
471     for (int i = nFallbackLevel; i < MAX_FALLBACK; ++i)
472     {
473         if (!mpTextStyle[i])
474             break;
475         mpTextStyle[i].clear();
476     }
477 
478     if (!pReqFont)
479         return;
480 
481     // update the text style
482     mpTextStyle[nFallbackLevel] = static_cast<CoreTextStyle*>(pReqFont);
483 }
484 
GetTextLayout(int nFallbackLevel)485 std::unique_ptr<GenericSalLayout> AquaSalGraphics::GetTextLayout(int nFallbackLevel)
486 {
487     assert(mpTextStyle[nFallbackLevel]);
488     if (!mpTextStyle[nFallbackLevel])
489         return nullptr;
490     return std::make_unique<GenericSalLayout>(*mpTextStyle[nFallbackLevel]);
491 }
492 
GetFontCharMap() const493 FontCharMapRef AquaSalGraphics::GetFontCharMap() const
494 {
495     if (!mpTextStyle[0])
496     {
497         return FontCharMapRef( new FontCharMap() );
498     }
499 
500     return static_cast<const CoreTextFontFace*>(mpTextStyle[0]->GetFontFace())->GetFontCharMap();
501 }
502 
GetFontCapabilities(vcl::FontCapabilities & rFontCapabilities) const503 bool AquaSalGraphics::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
504 {
505     if (!mpTextStyle[0])
506         return false;
507 
508     return static_cast<const CoreTextFontFace*>(mpTextStyle[0]->GetFontFace())->GetFontCapabilities(rFontCapabilities);
509 }
510 
511 // fake a SFNT font directory entry for a font table
512 // see http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html#Directory
FakeDirEntry(const char aTag[5],ByteCount nOfs,ByteCount nLen,const unsigned char *,unsigned char * & rpDest)513 static void FakeDirEntry( const char aTag[5], ByteCount nOfs, ByteCount nLen,
514                           const unsigned char* /*pData*/, unsigned char*& rpDest )
515 {
516     // write entry tag
517     rpDest[ 0] = aTag[0];
518     rpDest[ 1] = aTag[1];
519     rpDest[ 2] = aTag[2];
520     rpDest[ 3] = aTag[3];
521     // TODO: get entry checksum and write it
522     //      not too important since the subsetter doesn't care currently
523     //      for( pData+nOfs ... pData+nOfs+nLen )
524     // write entry offset
525     rpDest[ 8] = static_cast<char>(nOfs >> 24);
526     rpDest[ 9] = static_cast<char>(nOfs >> 16);
527     rpDest[10] = static_cast<char>(nOfs >>  8);
528     rpDest[11] = static_cast<char>(nOfs >>  0);
529     // write entry length
530     rpDest[12] = static_cast<char>(nLen >> 24);
531     rpDest[13] = static_cast<char>(nLen >> 16);
532     rpDest[14] = static_cast<char>(nLen >>  8);
533     rpDest[15] = static_cast<char>(nLen >>  0);
534     // advance to next entry
535     rpDest += 16;
536 }
537 
538 // fake a TTF or CFF font as directly accessing font file is not possible
539 // when only the fontid is known. This approach also handles *.font fonts.
GetRawFontData(const PhysicalFontFace * pFontData,std::vector<unsigned char> & rBuffer,bool * pJustCFF)540 bool AquaSalGraphics::GetRawFontData( const PhysicalFontFace* pFontData,
541                                       std::vector<unsigned char>& rBuffer, bool* pJustCFF )
542 {
543     const CoreTextFontFace* pMacFont = static_cast<const CoreTextFontFace*>(pFontData);
544 
545     // short circuit for CFF-only fonts
546     const int nCffSize = pMacFont->GetFontTable( "CFF ", nullptr);
547     if( pJustCFF != nullptr )
548     {
549         *pJustCFF = (nCffSize > 0);
550         if( *pJustCFF)
551         {
552             rBuffer.resize( nCffSize);
553             const int nCffRead = pMacFont->GetFontTable( "CFF ", rBuffer.data());
554             if( nCffRead != nCffSize)
555             {
556                 return false;
557             }
558             return true;
559         }
560     }
561 
562     // get font table availability and size in bytes
563     const int nHeadSize = pMacFont->GetFontTable( "head", nullptr);
564     if( nHeadSize <= 0)
565         return false;
566 
567     const int nMaxpSize = pMacFont->GetFontTable( "maxp", nullptr);
568     if( nMaxpSize <= 0)
569         return false;
570 
571     const int nCmapSize = pMacFont->GetFontTable( "cmap", nullptr);
572     if( nCmapSize <= 0)
573         return false;
574 
575     const int nNameSize = pMacFont->GetFontTable( "name", nullptr);
576     if( nNameSize <= 0)
577         return false;
578 
579     const int nHheaSize = pMacFont->GetFontTable( "hhea", nullptr);
580     if( nHheaSize <= 0)
581         return false;
582 
583     const int nHmtxSize = pMacFont->GetFontTable( "hmtx", nullptr);
584     if( nHmtxSize <= 0)
585         return false;
586 
587     // get the ttf-glyf outline tables
588     int nLocaSize = 0;
589     int nGlyfSize = 0;
590     if( nCffSize <= 0)
591     {
592         nLocaSize = pMacFont->GetFontTable( "loca", nullptr);
593         if( nLocaSize <= 0)
594             return false;
595 
596         nGlyfSize = pMacFont->GetFontTable( "glyf", nullptr);
597         if( nGlyfSize <= 0)
598             return false;
599     }
600 
601     int nPrepSize = 0, nCvtSize = 0, nFpgmSize = 0;
602     if( nGlyfSize) // TODO: reduce PDF size by making hint subsetting optional
603     {
604         nPrepSize = pMacFont->GetFontTable( "prep", nullptr);
605         nCvtSize  = pMacFont->GetFontTable( "cvt ", nullptr);
606         nFpgmSize = pMacFont->GetFontTable( "fpgm", nullptr);
607     }
608 
609     // prepare a byte buffer for a fake font
610     int nTableCount = 7;
611     nTableCount += (nPrepSize>0?1:0) + (nCvtSize>0?1:0) + (nFpgmSize>0?1:0) + (nGlyfSize>0?1:0);
612     const ByteCount nFdirSize = 12 + 16*nTableCount;
613     ByteCount nTotalSize = nFdirSize;
614     nTotalSize += nHeadSize + nMaxpSize + nNameSize + nCmapSize;
615 
616     if( nGlyfSize )
617     {
618         nTotalSize += nLocaSize + nGlyfSize;
619     }
620     else
621     {
622         nTotalSize += nCffSize;
623     }
624     nTotalSize += nHheaSize + nHmtxSize;
625     nTotalSize += nPrepSize + nCvtSize + nFpgmSize;
626     rBuffer.resize( nTotalSize );
627 
628     // fake a SFNT font directory header
629     if( nTableCount < 16 )
630     {
631         int nLog2 = 0;
632         while( (nTableCount >> nLog2) > 1 ) ++nLog2;
633         rBuffer[ 1] = 1;                        // Win-TTF style scaler
634         rBuffer[ 5] = nTableCount;              // table count
635         rBuffer[ 7] = nLog2*16;                 // searchRange
636         rBuffer[ 9] = nLog2;                    // entrySelector
637         rBuffer[11] = (nTableCount-nLog2)*16;   // rangeShift
638     }
639 
640     // get font table raw data and update the fake directory entries
641     ByteCount nOfs = nFdirSize;
642     unsigned char* pFakeEntry = &rBuffer[12];
643     if( nCmapSize != pMacFont->GetFontTable( "cmap", &rBuffer[nOfs]))
644         return false;
645 
646     FakeDirEntry( "cmap", nOfs, nCmapSize, rBuffer.data(), pFakeEntry );
647     nOfs += nCmapSize;
648     if( nCvtSize )
649     {
650         if( nCvtSize != pMacFont->GetFontTable( "cvt ", &rBuffer[nOfs]))
651             return false;
652 
653         FakeDirEntry( "cvt ", nOfs, nCvtSize, rBuffer.data(), pFakeEntry );
654         nOfs += nCvtSize;
655     }
656     if( nFpgmSize )
657     {
658         if( nFpgmSize != pMacFont->GetFontTable( "fpgm", &rBuffer[nOfs]))
659             return false;
660 
661         FakeDirEntry( "fpgm", nOfs, nFpgmSize, rBuffer.data(), pFakeEntry );
662         nOfs += nFpgmSize;
663     }
664     if( nCffSize )
665     {
666         if( nCffSize != pMacFont->GetFontTable( "CFF ", &rBuffer[nOfs]))
667             return false;
668 
669         FakeDirEntry( "CFF ", nOfs, nCffSize, rBuffer.data(), pFakeEntry );
670         nOfs += nGlyfSize;
671     }
672     else
673     {
674         if( nGlyfSize != pMacFont->GetFontTable( "glyf", &rBuffer[nOfs]))
675             return false;
676 
677         FakeDirEntry( "glyf", nOfs, nGlyfSize, rBuffer.data(), pFakeEntry );
678         nOfs += nGlyfSize;
679 
680         if( nLocaSize != pMacFont->GetFontTable( "loca", &rBuffer[nOfs]))
681             return false;
682 
683         FakeDirEntry( "loca", nOfs, nLocaSize, rBuffer.data(), pFakeEntry );
684         nOfs += nLocaSize;
685     }
686     if( nHeadSize != pMacFont->GetFontTable( "head", &rBuffer[nOfs]))
687         return false;
688 
689     FakeDirEntry( "head", nOfs, nHeadSize, rBuffer.data(), pFakeEntry );
690     nOfs += nHeadSize;
691 
692     if( nHheaSize != pMacFont->GetFontTable( "hhea", &rBuffer[nOfs]))
693         return false;
694 
695     FakeDirEntry( "hhea", nOfs, nHheaSize, rBuffer.data(), pFakeEntry );
696     nOfs += nHheaSize;
697     if( nHmtxSize != pMacFont->GetFontTable( "hmtx", &rBuffer[nOfs]))
698         return false;
699 
700     FakeDirEntry( "hmtx", nOfs, nHmtxSize, rBuffer.data(), pFakeEntry );
701     nOfs += nHmtxSize;
702     if( nMaxpSize != pMacFont->GetFontTable( "maxp", &rBuffer[nOfs]))
703         return false;
704 
705     FakeDirEntry( "maxp", nOfs, nMaxpSize, rBuffer.data(), pFakeEntry );
706     nOfs += nMaxpSize;
707     if( nNameSize != pMacFont->GetFontTable( "name", &rBuffer[nOfs]))
708         return false;
709 
710     FakeDirEntry( "name", nOfs, nNameSize, rBuffer.data(), pFakeEntry );
711     nOfs += nNameSize;
712     if( nPrepSize )
713     {
714         if( nPrepSize != pMacFont->GetFontTable( "prep", &rBuffer[nOfs]))
715             return false;
716 
717         FakeDirEntry( "prep", nOfs, nPrepSize, rBuffer.data(), pFakeEntry );
718         nOfs += nPrepSize;
719     }
720 
721     SAL_WARN_IF( (nOfs!=nTotalSize), "vcl", "AquaSalGraphics::GetRawFontData (nOfs!=nTotalSize)");
722 
723     return true;
724 }
725 
GetGlyphWidths(const PhysicalFontFace * pFontData,bool bVertical,std::vector<sal_Int32> & rGlyphWidths,Ucs2UIntMap & rUnicodeEnc)726 void AquaSalGraphics::GetGlyphWidths( const PhysicalFontFace* pFontData, bool bVertical,
727     std::vector< sal_Int32 >& rGlyphWidths, Ucs2UIntMap& rUnicodeEnc )
728 {
729     rGlyphWidths.clear();
730     rUnicodeEnc.clear();
731 
732     std::vector<unsigned char> aBuffer;
733     if( !GetRawFontData( pFontData, aBuffer, nullptr ) )
734         return;
735 
736     // TODO: modernize psprint's horrible fontsubset C-API
737     // this probably only makes sense after the switch to another SCM
738     // that can preserve change history after file renames
739 
740     // use the font subsetter to get the widths
741     TrueTypeFont* pSftFont = nullptr;
742     SFErrCodes nRC = ::OpenTTFontBuffer(static_cast<void*>(aBuffer.data()), aBuffer.size(), 0, &pSftFont,
743                                         pFontData->GetFontCharMap());
744     if( nRC != SFErrCodes::Ok )
745         return;
746 
747     SalGraphics::GetGlyphWidths(*pSftFont, *pFontData, bVertical, rGlyphWidths, rUnicodeEnc);
748 
749     ::CloseTTFont( pSftFont );
750 }
751 
GetEmbedFontData(const PhysicalFontFace *,tools::Long *)752 const void* AquaSalGraphics::GetEmbedFontData(const PhysicalFontFace*, tools::Long* /*pDataLen*/)
753 {
754     return nullptr;
755 }
756 
FreeEmbedFontData(const void * pData,tools::Long)757 void AquaSalGraphics::FreeEmbedFontData( const void* pData, tools::Long /*nDataLen*/ )
758 {
759     // TODO: implementing this only makes sense when the implementation of
760     //      AquaSalGraphics::GetEmbedFontData() returns non-NULL
761     SAL_WARN_IF( (pData==nullptr), "vcl", "AquaSalGraphics::FreeEmbedFontData() is not implemented");
762 }
763 
764 #ifdef IOS
765 
checkContext()766 bool AquaSharedAttributes::checkContext()
767 {
768     if (mbForeignContext)
769     {
770         SAL_INFO("vcl.ios", "CheckContext() this=" << this << ", mbForeignContext, return true");
771         return true;
772     }
773 
774     SAL_INFO( "vcl.ios", "CheckContext() this=" << this << ",  not foreign, return false");
775     return false;
776 }
777 
778 #endif
779 
780 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
781