Flutter Engine
The Flutter Engine
SkPDFType1Font.cpp
Go to the documentation of this file.
1// Copyright 2019 Google LLC.
2// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3
5
19#include "src/core/SkGlyph.h"
21#include "src/core/SkTHash.h"
23#include "src/pdf/SkPDFFont.h"
24#include "src/pdf/SkPDFTypes.h"
25
26#include <ctype.h>
27#include <cstdint>
28#include <cstring>
29#include <memory>
30#include <utility>
31#include <vector>
32
33using namespace skia_private;
34
35/*
36 "A standard Type 1 font program, as described in the Adobe Type 1
37 Font Format specification, consists of three parts: a clear-text
38 portion (written using PostScript syntax), an encrypted portion, and
39 a fixed-content portion. The fixed-content portion contains 512
40 ASCII zeros followed by a cleartomark operator, and perhaps followed
41 by additional data. Although the encrypted portion of a standard
42 Type 1 font may be in binary or ASCII hexadecimal format, PDF
43 supports only the binary format."
44*/
45static bool parsePFBSection(const uint8_t** src, size_t* len, int sectionType,
46 size_t* size) {
47 // PFB sections have a two or six bytes header. 0x80 and a one byte
48 // section type followed by a four byte section length. Type one is
49 // an ASCII section (includes a length), type two is a binary section
50 // (includes a length) and type three is an EOF marker with no length.
51 const uint8_t* buf = *src;
52 if (*len < 2 || buf[0] != 0x80 || buf[1] != sectionType) {
53 return false;
54 } else if (buf[1] == 3) {
55 return true;
56 } else if (*len < 6) {
57 return false;
58 }
59
60 *size = (size_t)buf[2] | ((size_t)buf[3] << 8) | ((size_t)buf[4] << 16) |
61 ((size_t)buf[5] << 24);
62 size_t consumed = *size + 6;
63 if (consumed > *len) {
64 return false;
65 }
66 *src = *src + consumed;
67 *len = *len - consumed;
68 return true;
69}
70
71static bool parsePFB(const uint8_t* src, size_t size, size_t* headerLen,
72 size_t* dataLen, size_t* trailerLen) {
73 const uint8_t* srcPtr = src;
74 size_t remaining = size;
75
76 return parsePFBSection(&srcPtr, &remaining, 1, headerLen) &&
77 parsePFBSection(&srcPtr, &remaining, 2, dataLen) &&
78 parsePFBSection(&srcPtr, &remaining, 1, trailerLen) &&
79 parsePFBSection(&srcPtr, &remaining, 3, nullptr);
80}
81
82/* The sections of a PFA file are implicitly defined. The body starts
83 * after the line containing "eexec," and the trailer starts with 512
84 * literal 0's followed by "cleartomark" (plus arbitrary white space).
85 *
86 * This function assumes that src is NUL terminated, but the NUL
87 * termination is not included in size.
88 *
89 */
90static bool parsePFA(const char* src, size_t size, size_t* headerLen,
91 size_t* hexDataLen, size_t* dataLen, size_t* trailerLen) {
92 const char* end = src + size;
93
94 const char* dataPos = strstr(src, "eexec");
95 if (!dataPos) {
96 return false;
97 }
98 dataPos += strlen("eexec");
99 while ((*dataPos == '\n' || *dataPos == '\r' || *dataPos == ' ') &&
100 dataPos < end) {
101 dataPos++;
102 }
103 *headerLen = dataPos - src;
104
105 const char* trailerPos = strstr(dataPos, "cleartomark");
106 if (!trailerPos) {
107 return false;
108 }
109 int zeroCount = 0;
110 for (trailerPos--; trailerPos > dataPos && zeroCount < 512; trailerPos--) {
111 if (*trailerPos == '\n' || *trailerPos == '\r' || *trailerPos == ' ') {
112 continue;
113 } else if (*trailerPos == '0') {
114 zeroCount++;
115 } else {
116 return false;
117 }
118 }
119 if (zeroCount != 512) {
120 return false;
121 }
122
123 *hexDataLen = trailerPos - src - *headerLen;
124 *trailerLen = size - *headerLen - *hexDataLen;
125
126 // Verify that the data section is hex encoded and count the bytes.
127 int nibbles = 0;
128 for (; dataPos < trailerPos; dataPos++) {
129 if (isspace(*dataPos)) {
130 continue;
131 }
132 // isxdigit() is locale-sensitive https://bugs.skia.org/8285
133 if (nullptr == strchr("0123456789abcdefABCDEF", *dataPos)) {
134 return false;
135 }
136 nibbles++;
137 }
138 *dataLen = (nibbles + 1) / 2;
139
140 return true;
141}
142
143static int8_t hexToBin(uint8_t c) {
144 if (!isxdigit(c)) {
145 return -1;
146 } else if (c <= '9') {
147 return c - '0';
148 } else if (c <= 'F') {
149 return c - 'A' + 10;
150 } else if (c <= 'f') {
151 return c - 'a' + 10;
152 }
153 return -1;
154}
155
156static sk_sp<SkData> convert_type1_font_stream(std::unique_ptr<SkStreamAsset> srcStream,
157 size_t* headerLen,
158 size_t* dataLen,
159 size_t* trailerLen) {
160 size_t srcLen = srcStream ? srcStream->getLength() : 0;
161 SkASSERT(srcLen);
162 if (!srcLen) {
163 return nullptr;
164 }
165 // Flatten and Nul-terminate the source stream so that we can use
166 // strstr() to search it.
167 AutoTMalloc<uint8_t> sourceBuffer(SkToInt(srcLen + 1));
168 (void)srcStream->read(sourceBuffer.get(), srcLen);
169 sourceBuffer[SkToInt(srcLen)] = 0;
170 const uint8_t* src = sourceBuffer.get();
171
172 if (parsePFB(src, srcLen, headerLen, dataLen, trailerLen)) {
173 static const int kPFBSectionHeaderLength = 6;
174 const size_t length = *headerLen + *dataLen + *trailerLen;
175 SkASSERT(length > 0);
176 SkASSERT(length + (2 * kPFBSectionHeaderLength) <= srcLen);
177
179
180 const uint8_t* const srcHeader = src + kPFBSectionHeaderLength;
181 // There is a six-byte section header before header and data
182 // (but not trailer) that we're not going to copy.
183 const uint8_t* const srcData = srcHeader + *headerLen + kPFBSectionHeaderLength;
184 const uint8_t* const srcTrailer = srcData + *headerLen;
185
186 uint8_t* const resultHeader = (uint8_t*)data->writable_data();
187 uint8_t* const resultData = resultHeader + *headerLen;
188 uint8_t* const resultTrailer = resultData + *dataLen;
189
190 SkASSERT(resultTrailer + *trailerLen == resultHeader + length);
191
192 memcpy(resultHeader, srcHeader, *headerLen);
193 memcpy(resultData, srcData, *dataLen);
194 memcpy(resultTrailer, srcTrailer, *trailerLen);
195
196 return data;
197 }
198
199 // A PFA has to be converted for PDF.
200 size_t hexDataLen;
201 if (!parsePFA((const char*)src, srcLen, headerLen, &hexDataLen, dataLen,
202 trailerLen)) {
203 return nullptr;
204 }
205 const size_t length = *headerLen + *dataLen + *trailerLen;
206 SkASSERT(length > 0);
208 uint8_t* buffer = (uint8_t*)data->writable_data();
209
210 memcpy(buffer, src, *headerLen);
211 uint8_t* const resultData = &(buffer[*headerLen]);
212
213 const uint8_t* hexData = src + *headerLen;
214 const uint8_t* trailer = hexData + hexDataLen;
215 size_t outputOffset = 0;
216 uint8_t dataByte = 0; // To hush compiler.
217 bool highNibble = true;
218 for (; hexData < trailer; hexData++) {
219 int8_t curNibble = hexToBin(*hexData);
220 if (curNibble < 0) {
221 continue;
222 }
223 if (highNibble) {
224 dataByte = curNibble << 4;
225 highNibble = false;
226 } else {
227 dataByte |= curNibble;
228 highNibble = true;
229 resultData[outputOffset++] = dataByte;
230 }
231 }
232 if (!highNibble) {
233 resultData[outputOffset++] = dataByte;
234 }
235 SkASSERT(outputOffset == *dataLen);
236
237 uint8_t* const resultTrailer = &(buffer[SkToInt(*headerLen + outputOffset)]);
238 memcpy(resultTrailer, src + *headerLen + hexDataLen, *trailerLen);
239 return data;
240}
241
242inline static bool can_embed(const SkAdvancedTypefaceMetrics& metrics) {
244}
245
246inline static SkScalar from_font_units(SkScalar scaled, uint16_t emSize) {
247 return emSize == 1000 ? scaled : scaled * 1000 / emSize;
248}
249
251 const SkTypeface* typeface,
253 SkPDFDict descriptor("FontDescriptor");
254 uint16_t emSize = SkToU16(typeface->getUnitsPerEm());
255 if (info) {
256 SkPDFFont::PopulateCommonFontDescriptor(&descriptor, *info, emSize, 0);
257 if (can_embed(*info)) {
258 int ttcIndex;
261 size_t trailer SK_INIT_TO_AVOID_WARNING;
262 std::unique_ptr<SkStreamAsset> rawFontData = typeface->openStream(&ttcIndex);
263 sk_sp<SkData> fontData = convert_type1_font_stream(std::move(rawFontData),
264 &header, &data, &trailer);
265 if (fontData) {
266 std::unique_ptr<SkPDFDict> dict = SkPDFMakeDict();
267 dict->insertInt("Length1", header);
268 dict->insertInt("Length2", data);
269 dict->insertInt("Length3", trailer);
270 auto fontStream = SkMemoryStream::Make(std::move(fontData));
271 descriptor.insertRef("FontFile",
272 SkPDFStreamOut(std::move(dict), std::move(fontStream),
274 }
275 }
276 }
277 return doc->emit(descriptor);
278}
279
280
281static const std::vector<SkString>& type_1_glyphnames(SkPDFDocument* canon,
282 const SkTypeface* typeface) {
283 SkTypefaceID typefaceID = typeface->uniqueID();
284 const std::vector<SkString>* glyphNames = canon->fType1GlyphNames.find(typefaceID);
285 if (!glyphNames) {
286 std::vector<SkString> names(typeface->countGlyphs());
287 SkPDFFont::GetType1GlyphNames(*typeface, names.data());
288 glyphNames = canon->fType1GlyphNames.set(typefaceID, std::move(names));
289 }
290 SkASSERT(glyphNames);
291 return *glyphNames;
292}
293
295 const SkTypeface* typeface) {
296 SkTypefaceID typefaceID = typeface->uniqueID();
297 if (SkPDFIndirectReference* ptr = doc->fFontDescriptors.find(typefaceID)) {
298 return *ptr;
299 }
301 auto fontDescriptor = make_type1_font_descriptor(doc, typeface, info);
302 doc->fFontDescriptors.set(typefaceID, fontDescriptor);
303 return fontDescriptor;
304}
305
306
307void SkPDFEmitType1Font(const SkPDFFont& pdfFont, SkPDFDocument* doc) {
308 SkTypeface* typeface = pdfFont.typeface();
309 const std::vector<SkString>& glyphNames = type_1_glyphnames(doc, typeface);
310 SkGlyphID firstGlyphID = pdfFont.firstGlyphID();
311 SkGlyphID lastGlyphID = pdfFont.lastGlyphID();
312
313 SkPDFDict font("Font");
314 font.insertRef("FontDescriptor", type1_font_descriptor(doc, typeface));
315 font.insertName("Subtype", "Type1");
316 if (const SkAdvancedTypefaceMetrics* info = SkPDFFont::GetMetrics(typeface, doc)) {
317 font.insertName("BaseFont", info->fPostScriptName);
318 }
319
320 // glyphCount not including glyph 0
321 unsigned glyphCount = 1 + lastGlyphID - firstGlyphID;
322 SkASSERT(glyphCount > 0 && glyphCount <= 255);
323 font.insertInt("FirstChar", (size_t)0);
324 font.insertInt("LastChar", (size_t)glyphCount);
325 {
326 int emSize;
327 auto widths = SkPDFMakeArray();
328
329 int glyphRangeSize = lastGlyphID - firstGlyphID + 2;
330 AutoTArray<SkGlyphID> glyphIDs{glyphRangeSize};
331 glyphIDs[0] = 0;
332 for (unsigned gId = firstGlyphID; gId <= lastGlyphID; gId++) {
333 glyphIDs[gId - firstGlyphID + 1] = gId;
334 }
335 SkStrikeSpec strikeSpec = SkStrikeSpec::MakePDFVector(*typeface, &emSize);
336 SkBulkGlyphMetrics metrics{strikeSpec};
337 auto glyphs = metrics.glyphs(SkSpan(glyphIDs.get(), glyphRangeSize));
338 for (int i = 0; i < glyphRangeSize; ++i) {
339 widths->appendScalar(from_font_units(glyphs[i]->advanceX(), SkToU16(emSize)));
340 }
341 font.insertObject("Widths", std::move(widths));
342 }
343 auto encDiffs = SkPDFMakeArray();
344 encDiffs->reserve(lastGlyphID - firstGlyphID + 3);
345 encDiffs->appendInt(0);
346
347 SkASSERT(glyphNames.size() > lastGlyphID);
348 const SkString unknown("UNKNOWN");
349 encDiffs->appendName(glyphNames[0].isEmpty() ? unknown : glyphNames[0]);
350 for (int gID = firstGlyphID; gID <= lastGlyphID; gID++) {
351 encDiffs->appendName(glyphNames[gID].isEmpty() ? unknown : glyphNames[gID]);
352 }
353
354 auto encoding = SkPDFMakeDict("Encoding");
355 encoding->insertObject("Differences", std::move(encDiffs));
356 font.insertObject("Encoding", std::move(encoding));
357
358 doc->emit(font, pdfFont.indirectReference());
359}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
uint16_t glyphs[5]
Definition: FontMgrTest.cpp:46
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define SK_INIT_TO_AVOID_WARNING
Definition: SkMacros.h:58
static bool parsePFBSection(const uint8_t **src, size_t *len, int sectionType, size_t *size)
static const std::vector< SkString > & type_1_glyphnames(SkPDFDocument *canon, const SkTypeface *typeface)
static sk_sp< SkData > convert_type1_font_stream(std::unique_ptr< SkStreamAsset > srcStream, size_t *headerLen, size_t *dataLen, size_t *trailerLen)
void SkPDFEmitType1Font(const SkPDFFont &pdfFont, SkPDFDocument *doc)
static SkPDFIndirectReference make_type1_font_descriptor(SkPDFDocument *doc, const SkTypeface *typeface, const SkAdvancedTypefaceMetrics *info)
static bool parsePFB(const uint8_t *src, size_t size, size_t *headerLen, size_t *dataLen, size_t *trailerLen)
static SkScalar from_font_units(SkScalar scaled, uint16_t emSize)
static bool parsePFA(const char *src, size_t size, size_t *headerLen, size_t *hexDataLen, size_t *dataLen, size_t *trailerLen)
static bool can_embed(const SkAdvancedTypefaceMetrics &metrics)
static int8_t hexToBin(uint8_t c)
static SkPDFIndirectReference type1_font_descriptor(SkPDFDocument *doc, const SkTypeface *typeface)
SkPDFIndirectReference SkPDFStreamOut(std::unique_ptr< SkPDFDict > dict, std::unique_ptr< SkStreamAsset > content, SkPDFDocument *doc, SkPDFSteamCompressionEnabled compress)
Definition: SkPDFTypes.cpp:591
static std::unique_ptr< SkPDFDict > SkPDFMakeDict(const char *type=nullptr)
Definition: SkPDFTypes.h:185
static std::unique_ptr< SkPDFArray > SkPDFMakeArray(Args... args)
Definition: SkPDFTypes.h:125
SkSpan(Container &&) -> SkSpan< std::remove_pointer_t< decltype(std::data(std::declval< Container >()))> >
constexpr uint16_t SkToU16(S x)
Definition: SkTo.h:24
constexpr int SkToInt(S x)
Definition: SkTo.h:29
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
uint32_t SkTypefaceID
Definition: SkTypeface.h:38
uint16_t SkGlyphID
Definition: SkTypes.h:179
const SkScalar widths[]
Definition: StrokerTest.cpp:39
static sk_sp< SkData > MakeUninitialized(size_t length)
Definition: SkData.cpp:116
static std::unique_ptr< SkMemoryStream > Make(sk_sp< SkData > data)
Definition: SkStream.cpp:314
void insertRef(const char key[], SkPDFIndirectReference)
Definition: SkPDFTypes.cpp:476
SkPDFIndirectReference emit(const SkPDFObject &, SkPDFIndirectReference)
skia_private::THashMap< uint32_t, SkPDFIndirectReference > fFontDescriptors
skia_private::THashMap< uint32_t, std::vector< SkString > > fType1GlyphNames
static const SkAdvancedTypefaceMetrics * GetMetrics(const SkTypeface *typeface, SkPDFDocument *canon)
Definition: SkPDFFont.cpp:118
static void PopulateCommonFontDescriptor(SkPDFDict *descriptor, const SkAdvancedTypefaceMetrics &, uint16_t emSize, int16_t defaultWidth)
Definition: SkPDFFont.cpp:265
SkPDFIndirectReference indirectReference() const
Definition: SkPDFFont.h:83
SkGlyphID lastGlyphID() const
Definition: SkPDFFont.h:121
SkGlyphID firstGlyphID() const
Definition: SkPDFFont.h:120
static void GetType1GlyphNames(const SkTypeface &, SkString *)
Definition: SkPDFFont.cpp:59
SkTypeface * typeface() const
Definition: SkPDFFont.h:41
static SkStrikeSpec MakePDFVector(const SkTypeface &typeface, int *size)
int countGlyphs() const
Definition: SkTypeface.cpp:432
SkTypefaceID uniqueID() const
Definition: SkTypeface.h:101
int getUnitsPerEm() const
Definition: SkTypeface.cpp:436
std::unique_ptr< SkStreamAsset > openStream(int *ttcIndex) const
Definition: SkTypeface.cpp:332
V * find(const K &key) const
Definition: SkTHash.h:494
V * set(K key, V val)
Definition: SkTHash.h:487
float SkScalar
Definition: extension.cpp:12
glong glong end
size_t length
static const char *const names[]
Definition: symbols.cc:24
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
font
Font Metadata and Metrics.
static const char header[]
Definition: skpbench.cpp:88
@ kNotEmbeddable_FontFlag
May not be embedded.
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63