1 /*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/pdf/SkPDFFont.h"
9
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkCanvas.h"
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkData.h"
15 #include "include/core/SkDrawable.h"
16 #include "include/core/SkFont.h"
17 #include "include/core/SkFontMetrics.h"
18 #include "include/core/SkFontStyle.h"
19 #include "include/core/SkFontTypes.h"
20 #include "include/core/SkImage.h"
21 #include "include/core/SkImageInfo.h"
22 #include "include/core/SkMaskFilter.h"
23 #include "include/core/SkMatrix.h"
24 #include "include/core/SkPaint.h"
25 #include "include/core/SkPath.h"
26 #include "include/core/SkPathTypes.h"
27 #include "include/core/SkPoint.h"
28 #include "include/core/SkRect.h"
29 #include "include/core/SkRefCnt.h"
30 #include "include/core/SkScalar.h"
31 #include "include/core/SkSize.h"
32 #include "include/core/SkStream.h"
33 #include "include/core/SkString.h"
34 #include "include/core/SkTypeface.h"
35 #include "include/effects/SkDashPathEffect.h"
36 #include "include/encode/SkJpegEncoder.h"
37 #include "include/private/base/SkDebug.h"
38 #include "include/private/base/SkTPin.h"
39 #include "include/private/base/SkTemplates.h"
40 #include "include/private/base/SkTo.h"
41 #include "src/base/SkBitmaskEnum.h"
42 #include "src/core/SkDescriptor.h"
43 #include "src/core/SkDevice.h"
44 #include "src/core/SkGlyph.h"
45 #include "src/core/SkMask.h"
46 #include "src/core/SkMaskFilterBase.h"
47 #include "src/core/SkPathEffectBase.h"
48 #include "src/core/SkStrike.h"
49 #include "src/core/SkStrikeSpec.h"
50 #include "src/core/SkTHash.h"
51 #include "src/pdf/SkPDFBitmap.h"
52 #include "src/pdf/SkPDFDevice.h"
53 #include "src/pdf/SkPDFDocumentPriv.h"
54 #include "src/pdf/SkPDFFormXObject.h"
55 #include "src/pdf/SkPDFGraphicState.h"
56 #include "src/pdf/SkPDFMakeCIDGlyphWidthsArray.h"
57 #include "src/pdf/SkPDFMakeToUnicodeCmap.h"
58 #include "src/pdf/SkPDFSubsetFont.h"
59 #include "src/pdf/SkPDFType1Font.h"
60 #include "src/pdf/SkPDFUtils.h"
61
62 #include <limits.h>
63 #include <algorithm>
64 #include <cstddef>
65 #include <initializer_list>
66 #include <memory>
67 #include <utility>
68
69 using namespace skia_private;
70
GetType1GlyphNames(const SkTypeface & face,SkString * dst)71 void SkPDFFont::GetType1GlyphNames(const SkTypeface& face, SkString* dst) {
72 face.getPostScriptGlyphNames(dst);
73 }
74
75 namespace {
76 // PDF's notion of symbolic vs non-symbolic is related to the character set, not
77 // symbols vs. characters. Rarely is a font the right character set to call it
78 // non-symbolic, so always call it symbolic. (PDF 1.4 spec, section 5.7.1)
79 static const int32_t kPdfSymbolic = 4;
80
81 // scale from em-units to base-1000, returning as a SkScalar
from_font_units(SkScalar scaled,uint16_t emSize)82 inline SkScalar from_font_units(SkScalar scaled, uint16_t emSize) {
83 return emSize == 1000 ? scaled : scaled * 1000 / emSize;
84 }
85
scaleFromFontUnits(int16_t val,uint16_t emSize)86 inline SkScalar scaleFromFontUnits(int16_t val, uint16_t emSize) {
87 return from_font_units(SkIntToScalar(val), emSize);
88 }
89
setGlyphWidthAndBoundingBox(SkScalar width,SkIRect box,SkDynamicMemoryWStream * content)90 void setGlyphWidthAndBoundingBox(SkScalar width, SkIRect box,
91 SkDynamicMemoryWStream* content) {
92 // Specify width and bounding box for the glyph.
93 SkPDFUtils::AppendScalar(width, content);
94 content->writeText(" 0 ");
95 content->writeDecAsText(box.fLeft);
96 content->writeText(" ");
97 content->writeDecAsText(box.fTop);
98 content->writeText(" ");
99 content->writeDecAsText(box.fRight);
100 content->writeText(" ");
101 content->writeDecAsText(box.fBottom);
102 content->writeText(" d1\n");
103 }
104
105 } // namespace
106
scale_paint(SkPaint & paint,SkScalar fontToEMScale)107 static bool scale_paint(SkPaint& paint, SkScalar fontToEMScale) {
108 // What we really want here is a way ask the path effect or mask filter for a scaled
109 // version of itself (if it is linearly scalable).
110
111 if (SkMaskFilterBase* mfb = as_MFB(paint.getMaskFilter())) {
112 SkMaskFilterBase::BlurRec blurRec;
113 if (mfb->asABlur(&blurRec)) {
114 // asABlur returns false if ignoring the CTM
115 blurRec.fSigma *= fontToEMScale;
116 paint.setMaskFilter(SkMaskFilter::MakeBlur(blurRec.fStyle, blurRec.fSigma, true));
117 } else {
118 return false;
119 }
120 }
121 if (SkPathEffectBase* peb = as_PEB(paint.getPathEffect())) {
122 AutoSTMalloc<4, SkScalar> intervals;
123 SkPathEffectBase::DashInfo dashInfo(intervals, 4, 0);
124 if (peb->asADash(&dashInfo) == SkPathEffectBase::DashType::kDash) {
125 if (dashInfo.fCount > 4) {
126 intervals.realloc(dashInfo.fCount);
127 peb->asADash(&dashInfo);
128 }
129 for (int32_t i = 0; i < dashInfo.fCount; ++i) {
130 dashInfo.fIntervals[i] *= fontToEMScale;
131 }
132 dashInfo.fPhase *= fontToEMScale;
133 paint.setPathEffect(
134 SkDashPathEffect::Make(dashInfo.fIntervals, dashInfo.fCount, dashInfo.fPhase));
135 } else {
136 return false;
137 }
138 }
139
140 if (paint.getStyle() != SkPaint::kFill_Style && paint.getStrokeWidth() > 0) {
141 paint.setStrokeMiter(paint.getStrokeMiter() * fontToEMScale);
142 paint.setStrokeWidth(paint.getStrokeWidth() * fontToEMScale);
143 }
144
145 return true;
146 }
147
148
SkPDFStrikeSpec(SkStrikeSpec strikeSpec,SkScalar em)149 SkPDFStrikeSpec::SkPDFStrikeSpec(SkStrikeSpec strikeSpec, SkScalar em)
150 : fStrikeSpec(std::move(strikeSpec))
151 , fUnitsPerEM(em)
152 {}
153
Make(SkPDFDocument * doc,const SkFont & font,const SkPaint & paint)154 sk_sp<SkPDFStrike> SkPDFStrike::Make(SkPDFDocument* doc, const SkFont& font, const SkPaint& paint) {
155 #ifdef SK_PDF_BITMAP_GLYPH_RASTER_SIZE
156 static constexpr float kBitmapFontSize = SK_PDF_BITMAP_GLYPH_RASTER_SIZE;
157 #else
158 static constexpr float kBitmapFontSize = 64;
159 #endif
160
161 SkScalar unitsPerEm = static_cast<SkScalar>(font.getTypeface()->getUnitsPerEm());
162 SkASSERT(0 < unitsPerEm);
163
164 SkFont canonFont(font);
165 canonFont.setBaselineSnap(false); // canonicalize
166 canonFont.setEdging(SkFont::Edging::kAntiAlias); // canonicalize
167 canonFont.setEmbeddedBitmaps(false); // canonicalize
168 //canonFont.setEmbolden(); // applied by scaler context, sets glyph path to modified
169 canonFont.setForceAutoHinting(false); // canonicalize
170 canonFont.setHinting(SkFontHinting::kNone); // canonicalize
171 canonFont.setLinearMetrics(true); // canonicalize
172 canonFont.setScaleX(1.0f); // original value applied by SkPDFDevice
173 //canonFont.setSize(unitsPerEm); // canonicalize below, adjusted by SkPDFDevice
174 canonFont.setSkewX(0.0f); // original value applied by SkPDFDevice
175 canonFont.setSubpixel(false); // canonicalize
176 //canonFont.setTypeface();
177
178 SkPaint pathPaint(paint);
179 if (scale_paint(pathPaint, unitsPerEm / font.getSize())) {
180 canonFont.setSize(unitsPerEm);
181 } else {
182 canonFont.setSize(font.getSize());
183 }
184 SkScalar pathStrikeEM = canonFont.getSize();
185 SkStrikeSpec pathStrikeSpec = SkStrikeSpec::MakeWithNoDevice(canonFont, &pathPaint);
186
187 if (sk_sp<SkPDFStrike>* strike = doc->fStrikes.find(pathStrikeSpec.descriptor())) {
188 return *strike;
189 }
190
191 if (kBitmapFontSize <= 0) {
192 // old code path compatibility
193 sk_sp<SkPDFStrike> strike(new SkPDFStrike(SkPDFStrikeSpec(pathStrikeSpec, pathStrikeEM),
194 SkPDFStrikeSpec(pathStrikeSpec, pathStrikeEM),
195 pathPaint.getMaskFilter(), doc));
196 doc->fStrikes.set(strike);
197 return strike;
198 }
199
200 SkPaint imagePaint(paint);
201 if (scale_paint(imagePaint, kBitmapFontSize / font.getSize())) {
202 canonFont.setSize(kBitmapFontSize);
203 } else {
204 canonFont.setSize(font.getSize());
205 }
206 SkScalar imageStrikeEM = canonFont.getSize();
207 SkStrikeSpec imageStrikeSpec = SkStrikeSpec::MakeWithNoDevice(canonFont, &imagePaint);
208
209 sk_sp<SkPDFStrike> strike(new SkPDFStrike(SkPDFStrikeSpec(pathStrikeSpec, pathStrikeEM),
210 SkPDFStrikeSpec(imageStrikeSpec, imageStrikeEM),
211 pathPaint.getMaskFilter(), doc));
212 doc->fStrikes.set(strike);
213 return strike;
214
215 }
216
SkPDFStrike(SkPDFStrikeSpec path,SkPDFStrikeSpec image,bool hasMaskFilter,SkPDFDocument * doc)217 SkPDFStrike::SkPDFStrike(SkPDFStrikeSpec path, SkPDFStrikeSpec image, bool hasMaskFilter,
218 SkPDFDocument* doc)
219 : fPath(std::move(path))
220 , fImage(std::move(image))
221 , fHasMaskFilter(hasMaskFilter)
222 , fDoc(doc)
223 {
224 SkASSERT(fDoc);
225 }
226
GetKey(const sk_sp<SkPDFStrike> & strike)227 const SkDescriptor& SkPDFStrike::Traits::GetKey(const sk_sp<SkPDFStrike>& strike) {
228 return strike->fPath.fStrikeSpec.descriptor();
229 }
Hash(const SkDescriptor & descriptor)230 uint32_t SkPDFStrike::Traits::Hash(const SkDescriptor& descriptor) {
231 return descriptor.getChecksum();
232 }
233
234 ///////////////////////////////////////////////////////////////////////////////
235 // class SkPDFFont
236 ///////////////////////////////////////////////////////////////////////////////
237
238 /* Resources are canonicalized and uniqueified by pointer so there has to be
239 * some additional state indicating which subset of the font is used. It
240 * must be maintained at the document granularity.
241 */
242
243 SkPDFFont::~SkPDFFont() = default;
244
245 SkPDFFont::SkPDFFont(SkPDFFont&&) = default;
246
can_embed(const SkAdvancedTypefaceMetrics & metrics)247 static bool can_embed(const SkAdvancedTypefaceMetrics& metrics) {
248 return !SkToBool(metrics.fFlags & SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag);
249 }
250
can_subset(const SkAdvancedTypefaceMetrics & metrics)251 static bool can_subset(const SkAdvancedTypefaceMetrics& metrics) {
252 return !SkToBool(metrics.fFlags & SkAdvancedTypefaceMetrics::kNotSubsettable_FontFlag);
253 }
254
GetMetrics(const SkTypeface & typeface,SkPDFDocument * canon)255 const SkAdvancedTypefaceMetrics* SkPDFFont::GetMetrics(const SkTypeface& typeface,
256 SkPDFDocument* canon) {
257 SkTypefaceID id = typeface.uniqueID();
258 if (std::unique_ptr<SkAdvancedTypefaceMetrics>* ptr = canon->fTypefaceMetrics.find(id)) {
259 return ptr->get(); // canon retains ownership.
260 }
261
262 int count = typeface.countGlyphs();
263 if (count <= 0 || count > 1 + SkTo<int>(UINT16_MAX)) {
264 // Cache nullptr to skip this check. Use SkSafeUnref().
265 canon->fTypefaceMetrics.set(id, nullptr);
266 return nullptr;
267 }
268
269 std::unique_ptr<SkAdvancedTypefaceMetrics> metrics = typeface.getAdvancedMetrics();
270 if (!metrics) {
271 metrics = std::make_unique<SkAdvancedTypefaceMetrics>();
272 }
273 if (0 == metrics->fStemV || 0 == metrics->fCapHeight) {
274 SkFont font;
275 font.setHinting(SkFontHinting::kNone);
276 font.setTypeface(sk_ref_sp(&typeface));
277 font.setSize(1000); // glyph coordinate system
278 if (0 == metrics->fStemV) {
279 // Figure out a good guess for StemV - Min width of i, I, !, 1.
280 // This probably isn't very good with an italic font.
281 int16_t stemV = SHRT_MAX;
282 for (char c : {'i', 'I', '!', '1'}) {
283 uint16_t g = font.unicharToGlyph(c);
284 SkRect bounds;
285 font.getBounds(&g, 1, &bounds, nullptr);
286 stemV = std::min(stemV, SkToS16(SkScalarRoundToInt(bounds.width())));
287 }
288 metrics->fStemV = stemV;
289 }
290 if (0 == metrics->fCapHeight) {
291 // Figure out a good guess for CapHeight: average the height of M and X.
292 SkScalar capHeight = 0;
293 for (char c : {'M', 'X'}) {
294 uint16_t g = font.unicharToGlyph(c);
295 SkRect bounds;
296 font.getBounds(&g, 1, &bounds, nullptr);
297 capHeight += bounds.height();
298 }
299 metrics->fCapHeight = SkToS16(SkScalarRoundToInt(capHeight / 2));
300 }
301 }
302 // Fonts are always subset, so always prepend the subset tag.
303 metrics->fPostScriptName.prepend(canon->nextFontSubsetTag());
304 return canon->fTypefaceMetrics.set(id, std::move(metrics))->get();
305 }
306
GetUnicodeMap(const SkTypeface & typeface,SkPDFDocument * canon)307 const std::vector<SkUnichar>& SkPDFFont::GetUnicodeMap(const SkTypeface& typeface,
308 SkPDFDocument* canon) {
309 SkASSERT(canon);
310 SkTypefaceID id = typeface.uniqueID();
311 if (std::vector<SkUnichar>* ptr = canon->fToUnicodeMap.find(id)) {
312 return *ptr;
313 }
314 std::vector<SkUnichar> buffer(typeface.countGlyphs());
315 typeface.getGlyphToUnicodeMap(buffer.data());
316 return *canon->fToUnicodeMap.set(id, std::move(buffer));
317 }
318
GetUnicodeMapEx(const SkTypeface & typeface,SkPDFDocument * canon)319 THashMap<SkGlyphID, SkString>& SkPDFFont::GetUnicodeMapEx(const SkTypeface& typeface,
320 SkPDFDocument* canon) {
321 SkASSERT(canon);
322 SkTypefaceID id = typeface.uniqueID();
323 if (THashMap<SkGlyphID, SkString>* ptr = canon->fToUnicodeMapEx.find(id)) {
324 return *ptr;
325 }
326 return *canon->fToUnicodeMapEx.set(id, THashMap<SkGlyphID, SkString>());
327 }
328
FontType(const SkPDFStrike & pdfStrike,const SkAdvancedTypefaceMetrics & metrics)329 SkAdvancedTypefaceMetrics::FontType SkPDFFont::FontType(const SkPDFStrike& pdfStrike,
330 const SkAdvancedTypefaceMetrics& metrics) {
331 if (SkToBool(metrics.fFlags & SkAdvancedTypefaceMetrics::kVariable_FontFlag) ||
332 // PDF is actually interested in the encoding of the data, not just the logical format.
333 // If the TrueType is actually wOFF or wOF2 then it should not be directly embedded in PDF.
334 // For now export these as Type3 until the subsetter can handle table based fonts.
335 // See https://github.com/harfbuzz/harfbuzz/issues/3609 and
336 // https://skia-review.googlesource.com/c/skia/+/543485
337 SkToBool(metrics.fFlags & SkAdvancedTypefaceMetrics::kAltDataFormat_FontFlag) ||
338 SkToBool(metrics.fFlags & SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag) ||
339 // Something like 45eeeddb00741493 and 7c86e7641b348ca7b0 to output OpenType should work,
340 // but requires PDF 1.6 which is still not supported by all printers. One could fix this by
341 // using bare CFF like 31a170226c22244cbd00497b67f6ae181f0f3e76 which is only PDF 1.3,
342 // but this only works when the CFF CIDs == CFF index == GlyphID as PDF bare CFF prefers
343 // CFF CIDs instead of GlyphIDs and Skia doesn't know the CIDs.
344 metrics.fType == SkAdvancedTypefaceMetrics::kCFF_Font ||
345 pdfStrike.fHasMaskFilter)
346 {
347 // force Type3 fallback.
348 return SkAdvancedTypefaceMetrics::kOther_Font;
349 }
350 return metrics.fType;
351 }
352
first_nonzero_glyph_for_single_byte_encoding(SkGlyphID gid)353 static SkGlyphID first_nonzero_glyph_for_single_byte_encoding(SkGlyphID gid) {
354 return gid != 0 ? gid - (gid - 1) % 255 : 1;
355 }
356
getFontResource(const SkGlyph * glyph)357 SkPDFFont* SkPDFStrike::getFontResource(const SkGlyph* glyph) {
358 const SkTypeface& typeface = fPath.fStrikeSpec.typeface();
359 const SkAdvancedTypefaceMetrics* fontMetrics = SkPDFFont::GetMetrics(typeface, fDoc);
360 SkASSERT(fontMetrics); // SkPDFDevice::internalDrawText ensures the typeface is good.
361 // GetMetrics only returns null to signify a bad typeface.
362 const SkAdvancedTypefaceMetrics& metrics = *fontMetrics;
363
364 // Determine the FontType.
365 // 1. Can the "original" font data be used directly
366 // (simple OpenType, no non-default variations, not WOFF, etc).
367 // 2. Is the glyph to be drawn unmodified from the font data
368 // (no path effect, stroking, fake bolding, extra matrix, mask filter).
369 // 3. Will PDF viewers draw this glyph the way we want
370 // (at the moment this means an unmodified glyph path).
371 SkAdvancedTypefaceMetrics::FontType type = SkPDFFont::FontType(*this, metrics);
372 // Keep the type (and original data) if the glyph is empty or the glyph has an unmodified path.
373 // Otherwise, fall back to Type3.
374 if (!(glyph->isEmpty() || (glyph->path() && !glyph->pathIsModified()))) {
375 type = SkAdvancedTypefaceMetrics::kOther_Font;
376 }
377
378 bool multibyte = SkPDFFont::IsMultiByte(type);
379 SkGlyphID subsetCode =
380 multibyte ? 0 : first_nonzero_glyph_for_single_byte_encoding(glyph->getGlyphID());
381 if (SkPDFFont* font = fFontMap.find(subsetCode)) {
382 SkASSERT(multibyte == font->multiByteGlyphs());
383 return font;
384 }
385
386 SkGlyphID lastGlyph = SkToU16(typeface.countGlyphs() - 1);
387 SkASSERT(glyph->getGlyphID() <= lastGlyph); // should be caught by SkPDFDevice::internalDrawText
388
389 SkGlyphID firstNonZeroGlyph;
390 if (multibyte) {
391 firstNonZeroGlyph = 1;
392 } else {
393 firstNonZeroGlyph = subsetCode;
394 lastGlyph = SkToU16(std::min<int>((int)lastGlyph, 254 + (int)subsetCode));
395 }
396 auto ref = fDoc->reserveRef();
397 return fFontMap.set(subsetCode, SkPDFFont(this, firstNonZeroGlyph, lastGlyph, type, ref));
398 }
399
SkPDFFont(const SkPDFStrike * strike,SkGlyphID firstGlyphID,SkGlyphID lastGlyphID,SkAdvancedTypefaceMetrics::FontType fontType,SkPDFIndirectReference indirectReference)400 SkPDFFont::SkPDFFont(const SkPDFStrike* strike,
401 SkGlyphID firstGlyphID,
402 SkGlyphID lastGlyphID,
403 SkAdvancedTypefaceMetrics::FontType fontType,
404 SkPDFIndirectReference indirectReference)
405 : fStrike(strike)
406 , fGlyphUsage(firstGlyphID, lastGlyphID)
407 , fIndirectReference(indirectReference)
408 , fFontType(fontType)
409 {
410 // Always include glyph 0
411 this->noteGlyphUsage(0);
412 }
413
PopulateCommonFontDescriptor(SkPDFDict * descriptor,const SkAdvancedTypefaceMetrics & metrics,uint16_t emSize,int16_t defaultWidth)414 void SkPDFFont::PopulateCommonFontDescriptor(SkPDFDict* descriptor,
415 const SkAdvancedTypefaceMetrics& metrics,
416 uint16_t emSize,
417 int16_t defaultWidth) {
418 descriptor->insertName("FontName", metrics.fPostScriptName);
419 descriptor->insertInt("Flags", (size_t)(metrics.fStyle | kPdfSymbolic));
420 descriptor->insertScalar("Ascent",
421 scaleFromFontUnits(metrics.fAscent, emSize));
422 descriptor->insertScalar("Descent",
423 scaleFromFontUnits(metrics.fDescent, emSize));
424 descriptor->insertScalar("StemV",
425 scaleFromFontUnits(metrics.fStemV, emSize));
426 descriptor->insertScalar("CapHeight",
427 scaleFromFontUnits(metrics.fCapHeight, emSize));
428 descriptor->insertInt("ItalicAngle", metrics.fItalicAngle);
429 descriptor->insertObject("FontBBox",
430 SkPDFMakeArray(scaleFromFontUnits(metrics.fBBox.left(), emSize),
431 scaleFromFontUnits(metrics.fBBox.bottom(), emSize),
432 scaleFromFontUnits(metrics.fBBox.right(), emSize),
433 scaleFromFontUnits(metrics.fBBox.top(), emSize)));
434 if (defaultWidth > 0) {
435 descriptor->insertScalar("MissingWidth",
436 scaleFromFontUnits(defaultWidth, emSize));
437 }
438 }
439
440 ///////////////////////////////////////////////////////////////////////////////
441 // Type0Font
442 ///////////////////////////////////////////////////////////////////////////////
443
emit_subset_type0(const SkPDFFont & font,SkPDFDocument * doc)444 static void emit_subset_type0(const SkPDFFont& font, SkPDFDocument* doc) {
445 const SkTypeface& typeface = font.strike().fPath.fStrikeSpec.typeface();
446 const SkAdvancedTypefaceMetrics* metricsPtr = SkPDFFont::GetMetrics(typeface, doc);
447 SkASSERT(metricsPtr);
448 if (!metricsPtr) {
449 return;
450 }
451 const SkAdvancedTypefaceMetrics& metrics = *metricsPtr;
452 SkASSERT(can_embed(metrics));
453 SkAdvancedTypefaceMetrics::FontType type = font.getType();
454
455 auto descriptor = SkPDFMakeDict("FontDescriptor");
456 uint16_t emSize = SkToU16(SkScalarRoundToInt(font.strike().fPath.fUnitsPerEM));
457 SkPDFFont::PopulateCommonFontDescriptor(descriptor.get(), metrics, emSize, 0);
458
459 int ttcIndex;
460 std::unique_ptr<SkStreamAsset> fontAsset = typeface.openStream(&ttcIndex);
461 size_t fontSize = fontAsset ? fontAsset->getLength() : 0;
462 if (0 == fontSize) {
463 SkDebugf("Error: (SkTypeface)(%p)::openStream() returned "
464 "empty stream (%p) when identified as kType1CID_Font "
465 "or kTrueType_Font.\n", &typeface, fontAsset.get());
466 } else if (type == SkAdvancedTypefaceMetrics::kTrueType_Font) {
467 sk_sp<SkData> subsetFontData;
468 if (can_subset(metrics)) {
469 SkASSERT(font.firstGlyphID() == 1);
470 subsetFontData = SkPDFSubsetFont(typeface, font.glyphUsage());
471 }
472 std::unique_ptr<SkStreamAsset> subsetFontAsset;
473 if (subsetFontData) {
474 subsetFontAsset = SkMemoryStream::Make(std::move(subsetFontData));
475 } else {
476 // If subsetting fails, fall back to original font data.
477 subsetFontAsset = std::move(fontAsset);
478 }
479 std::unique_ptr<SkPDFDict> streamDict = SkPDFMakeDict();
480 streamDict->insertInt("Length1", subsetFontAsset->getLength());
481 descriptor->insertRef("FontFile2",
482 SkPDFStreamOut(std::move(streamDict), std::move(subsetFontAsset),
483 doc, SkPDFSteamCompressionEnabled::Yes));
484 } else if (type == SkAdvancedTypefaceMetrics::kType1CID_Font) {
485 std::unique_ptr<SkPDFDict> streamDict = SkPDFMakeDict();
486 streamDict->insertName("Subtype", "CIDFontType0C");
487 descriptor->insertRef("FontFile3",
488 SkPDFStreamOut(std::move(streamDict), std::move(fontAsset),
489 doc, SkPDFSteamCompressionEnabled::Yes));
490 } else {
491 SkASSERT(false);
492 }
493
494 auto newCIDFont = SkPDFMakeDict("Font");
495 newCIDFont->insertRef("FontDescriptor", doc->emit(*descriptor));
496 newCIDFont->insertName("BaseFont", metrics.fPostScriptName);
497
498 switch (type) {
499 case SkAdvancedTypefaceMetrics::kType1CID_Font:
500 newCIDFont->insertName("Subtype", "CIDFontType0");
501 break;
502 case SkAdvancedTypefaceMetrics::kTrueType_Font:
503 newCIDFont->insertName("Subtype", "CIDFontType2");
504 newCIDFont->insertName("CIDToGIDMap", "Identity");
505 break;
506 default:
507 SkASSERT(false);
508 }
509 auto sysInfo = SkPDFMakeDict();
510 // These are actually ASCII strings.
511 sysInfo->insertByteString("Registry", "Adobe");
512 sysInfo->insertByteString("Ordering", "Identity");
513 sysInfo->insertInt("Supplement", 0);
514 newCIDFont->insertObject("CIDSystemInfo", std::move(sysInfo));
515
516 // Unfortunately, poppler enforces DW (default width) must be an integer.
517 int32_t defaultWidth = 0;
518 {
519 std::unique_ptr<SkPDFArray> widths = SkPDFMakeCIDGlyphWidthsArray(
520 font.strike().fPath, font.glyphUsage(), &defaultWidth);
521 if (widths && widths->size() > 0) {
522 newCIDFont->insertObject("W", std::move(widths));
523 }
524 newCIDFont->insertInt("DW", defaultWidth);
525 }
526
527 ////////////////////////////////////////////////////////////////////////////
528
529 SkPDFDict fontDict("Font");
530 fontDict.insertName("Subtype", "Type0");
531 fontDict.insertName("BaseFont", metrics.fPostScriptName);
532 fontDict.insertName("Encoding", "Identity-H");
533 auto descendantFonts = SkPDFMakeArray();
534 descendantFonts->appendRef(doc->emit(*newCIDFont));
535 fontDict.insertObject("DescendantFonts", std::move(descendantFonts));
536
537 const std::vector<SkUnichar>& glyphToUnicode =
538 SkPDFFont::GetUnicodeMap(typeface, doc);
539 SkASSERT(SkToSizeT(typeface.countGlyphs()) == glyphToUnicode.size());
540 std::unique_ptr<SkStreamAsset> toUnicode =
541 SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
542 SkPDFFont::GetUnicodeMapEx(typeface, doc),
543 &font.glyphUsage(),
544 font.multiByteGlyphs(),
545 font.firstGlyphID(),
546 font.lastGlyphID());
547 fontDict.insertRef("ToUnicode", SkPDFStreamOut(nullptr, std::move(toUnicode), doc));
548
549 doc->emit(fontDict, font.indirectReference());
550 }
551
552 ///////////////////////////////////////////////////////////////////////////////
553 // PDFType3Font
554 ///////////////////////////////////////////////////////////////////////////////
555
556 namespace {
557 // returns [0, first, first+1, ... last-1, last]
558 struct SingleByteGlyphIdIterator {
SingleByteGlyphIdIterator__anon63c3b1de0211::SingleByteGlyphIdIterator559 SingleByteGlyphIdIterator(SkGlyphID first, SkGlyphID last)
560 : fFirst(first), fLast(last) {
561 SkASSERT(fFirst > 0);
562 SkASSERT(fLast >= first);
563 }
564 struct Iter {
operator ++__anon63c3b1de0211::SingleByteGlyphIdIterator::Iter565 void operator++() {
566 fCurrent = (0 == fCurrent) ? fFirst : fCurrent + 1;
567 }
568 // This is an input_iterator
operator *__anon63c3b1de0211::SingleByteGlyphIdIterator::Iter569 SkGlyphID operator*() const { return (SkGlyphID)fCurrent; }
operator !=__anon63c3b1de0211::SingleByteGlyphIdIterator::Iter570 bool operator!=(const Iter& rhs) const {
571 return fCurrent != rhs.fCurrent;
572 }
Iter__anon63c3b1de0211::SingleByteGlyphIdIterator::Iter573 Iter(SkGlyphID f, int c) : fFirst(f), fCurrent(c) {}
574 private:
575 const SkGlyphID fFirst;
576 int fCurrent; // must be int to make fLast+1 to fit
577 };
begin__anon63c3b1de0211::SingleByteGlyphIdIterator578 Iter begin() const { return Iter(fFirst, 0); }
end__anon63c3b1de0211::SingleByteGlyphIdIterator579 Iter end() const { return Iter(fFirst, (int)fLast + 1); }
580 private:
581 const SkGlyphID fFirst;
582 const SkGlyphID fLast;
583 };
584 } // namespace
585
586 struct ImageAndOffset {
587 sk_sp<SkImage> fImage;
588 SkIPoint fOffset;
589 };
to_image(SkGlyphID gid,SkBulkGlyphMetricsAndImages * smallGlyphs)590 static ImageAndOffset to_image(SkGlyphID gid, SkBulkGlyphMetricsAndImages* smallGlyphs) {
591 const SkGlyph* glyph = smallGlyphs->glyph(SkPackedGlyphID{gid});
592 SkMask mask = glyph->mask();
593 if (!mask.fImage) {
594 return {nullptr, {0, 0}};
595 }
596 SkIRect bounds = mask.fBounds;
597 SkBitmap bm;
598 switch (mask.fFormat) {
599 case SkMask::kBW_Format: {
600 // Make a gray image, used to smask a rectangle.
601 // TODO: emit as MaskImage?
602 const SkISize size = bounds.size();
603 bm.allocPixels(SkImageInfo::Make(size, kGray_8_SkColorType, kUnknown_SkAlphaType));
604 for (int y = 0; y < bm.height(); ++y) {
605 for (int x8 = 0; x8 < bm.width(); x8 += 8) {
606 uint8_t v = *mask.getAddr1(x8 + bounds.x(), y + bounds.y());
607 int e = std::min(x8 + 8, bm.width());
608 for (int x = x8; x < e; ++x) {
609 *bm.getAddr8(x, y) = (v >> (x & 0x7)) & 0x1 ? 0xFF : 0x00;
610 }
611 }
612 }
613 bm.setImmutable();
614 return {bm.asImage(), {bounds.x(), bounds.y()}};
615 }
616 case SkMask::kA8_Format:
617 case SkMask::k3D_Format: // just do the A8 part
618 // Make a gray image, used to smask a rectangle.
619 return {SkImages::RasterFromData(
620 SkImageInfo::Make(bounds.size(), kGray_8_SkColorType, kUnknown_SkAlphaType),
621 SkData::MakeWithCopy(mask.fImage, mask.computeImageSize()),
622 mask.fRowBytes),
623 {bounds.x(), bounds.y()}};
624 case SkMask::kARGB32_Format:
625 // These will be drawn as images directly.
626 return {SkImages::RasterFromData(
627 SkImageInfo::MakeN32Premul(bounds.size()),
628 SkData::MakeWithCopy(mask.fImage, mask.computeTotalImageSize()),
629 mask.fRowBytes),
630 {bounds.x(), bounds.y()}};
631 case SkMask::kLCD16_Format:
632 default:
633 SkASSERT(false);
634 return {nullptr, {0, 0}};
635 }
636 }
637
type3_descriptor(SkPDFDocument * doc,const SkTypeface & typeface,SkScalar xHeight)638 static SkPDFIndirectReference type3_descriptor(SkPDFDocument* doc,
639 const SkTypeface& typeface,
640 SkScalar xHeight) {
641 if (SkPDFIndirectReference* ptr = doc->fType3FontDescriptors.find(typeface.uniqueID())) {
642 return *ptr;
643 }
644
645 SkPDFDict descriptor("FontDescriptor");
646 int32_t fontDescriptorFlags = kPdfSymbolic;
647
648 /** PDF32000_2008: FontFamily should be used for Type3 fonts in Tagged PDF documents. */
649 SkString familyName;
650 typeface.getFamilyName(&familyName);
651 if (!familyName.isEmpty()) {
652 descriptor.insertByteString("FontFamily", familyName);
653 }
654
655 /** PDF32000_2008: FontStretch should be used for Type3 fonts in Tagged PDF documents. */
656 static constexpr const char* stretchNames[9] = {
657 "UltraCondensed",
658 "ExtraCondensed",
659 "Condensed",
660 "SemiCondensed",
661 "Normal",
662 "SemiExpanded",
663 "Expanded",
664 "ExtraExpanded",
665 "UltraExpanded",
666 };
667 const char* stretchName = stretchNames[typeface.fontStyle().width() - 1];
668 descriptor.insertName("FontStretch", stretchName);
669
670 /** PDF32000_2008: FontWeight should be used for Type3 fonts in Tagged PDF documents. */
671 int weight = (typeface.fontStyle().weight() + 50) / 100;
672 descriptor.insertInt("FontWeight", SkTPin(weight, 1, 9) * 100);
673
674 if (const SkAdvancedTypefaceMetrics* metrics = SkPDFFont::GetMetrics(typeface, doc)) {
675 // Type3 FontDescriptor does not require all the same fields.
676 descriptor.insertName("FontName", metrics->fPostScriptName);
677 descriptor.insertInt("ItalicAngle", metrics->fItalicAngle);
678 fontDescriptorFlags |= (int32_t)metrics->fStyle;
679 // Adobe requests CapHeight, XHeight, and StemV be added
680 // to "greatly help our workflow downstream".
681 if (metrics->fCapHeight != 0) { descriptor.insertInt("CapHeight", metrics->fCapHeight); }
682 if (metrics->fStemV != 0) { descriptor.insertInt("StemV", metrics->fStemV); }
683 if (xHeight != 0) {
684 descriptor.insertScalar("XHeight", xHeight);
685 }
686 }
687 descriptor.insertInt("Flags", fontDescriptorFlags);
688 SkPDFIndirectReference ref = doc->emit(descriptor);
689 doc->fType3FontDescriptors.set(typeface.uniqueID(), ref);
690 return ref;
691 }
692
emit_subset_type3(const SkPDFFont & pdfFont,SkPDFDocument * doc)693 static void emit_subset_type3(const SkPDFFont& pdfFont, SkPDFDocument* doc) {
694 const SkPDFStrike& pdfStrike = pdfFont.strike();
695 SkGlyphID firstGlyphID = pdfFont.firstGlyphID();
696 SkGlyphID lastGlyphID = pdfFont.lastGlyphID();
697 const SkPDFGlyphUse& subset = pdfFont.glyphUsage();
698 SkASSERT(lastGlyphID >= firstGlyphID);
699 // Remove unused glyphs at the end of the range.
700 // Keep the lastGlyphID >= firstGlyphID invariant true.
701 while (lastGlyphID > firstGlyphID && !subset.has(lastGlyphID)) {
702 --lastGlyphID;
703 }
704 SkScalar emSize = pdfStrike.fPath.fUnitsPerEM;
705 sk_sp<SkStrike> strike = pdfStrike.fPath.fStrikeSpec.findOrCreateStrike();
706 SkASSERT(strike);
707 SkScalar xHeight = strike->getFontMetrics().fXHeight;
708 SkBulkGlyphMetricsAndPaths metricsAndPaths((sk_sp<SkStrike>(strike)));
709 SkBulkGlyphMetricsAndDrawables metricsAndDrawables(std::move(strike));
710
711 SkBulkGlyphMetricsAndImages smallGlyphs(pdfFont.strike().fImage.fStrikeSpec);
712 float bitmapScale = emSize / pdfStrike.fImage.fUnitsPerEM;
713
714 SkPDFDict font("Font");
715 font.insertName("Subtype", "Type3");
716 // Flip about the x-axis and scale by 1/emSize.
717 SkMatrix fontMatrix;
718 fontMatrix.setScale(SkScalarInvert(emSize), -SkScalarInvert(emSize));
719 font.insertObject("FontMatrix", SkPDFUtils::MatrixToArray(fontMatrix));
720
721 auto charProcs = SkPDFMakeDict();
722 auto encoding = SkPDFMakeDict("Encoding");
723
724 auto encDiffs = SkPDFMakeArray();
725 // length(firstGlyphID .. lastGlyphID) == lastGlyphID - firstGlyphID + 1
726 // plus 1 for glyph 0;
727 SkASSERT(firstGlyphID > 0);
728 SkASSERT(lastGlyphID >= firstGlyphID);
729 int glyphCount = lastGlyphID - firstGlyphID + 2;
730 // one other entry for the index of first glyph.
731 encDiffs->reserve(glyphCount + 1);
732 encDiffs->appendInt(0); // index of first glyph
733
734 auto widthArray = SkPDFMakeArray();
735 widthArray->reserve(glyphCount);
736
737 SkIRect bbox = SkIRect::MakeEmpty();
738
739 std::unique_ptr<SkPDFDict> xobjects = SkPDFMakeDict();
740 std::unique_ptr<SkPDFDict> graphicStates = SkPDFMakeDict();
741 for (SkGlyphID gID : SingleByteGlyphIdIterator(firstGlyphID, lastGlyphID)) {
742 SkString characterName;
743 SkScalar advance = 0.0f;
744
745 if (gID != 0 && !subset.has(gID)) {
746 characterName.set("g0");
747 advance = 0.0f;
748 encDiffs->appendName(std::move(characterName));
749 widthArray->appendScalar(advance);
750 continue;
751 }
752
753 const SkGlyph* pathGlyph = metricsAndPaths.glyph(gID);
754 const SkGlyph* drawableGlyph = metricsAndDrawables.glyph(gID);
755
756 characterName.printf("g%X", gID);
757 advance = pathGlyph->advanceX();
758 encDiffs->appendName(characterName);
759 widthArray->appendScalar(advance);
760
761 SkIRect glyphBBox = pathGlyph->iRect();
762 bbox.join(glyphBBox);
763 const SkPath* path = pathGlyph->path();
764 SkDrawable* drawable = drawableGlyph->drawable();
765 SkDynamicMemoryWStream content;
766 if (drawable && !drawable->getBounds().isEmpty()) {
767 sk_sp<SkPDFDevice> glyphDevice = sk_make_sp<SkPDFDevice>(glyphBBox.size(), doc);
768 SkCanvas canvas(glyphDevice);
769 canvas.translate(-glyphBBox.fLeft, -glyphBBox.fTop);
770 canvas.drawDrawable(drawable);
771 SkPDFIndirectReference xobject = SkPDFMakeFormXObject(
772 doc, glyphDevice->content(),
773 SkPDFMakeArray(0, 0, glyphBBox.width(), glyphBBox.height()),
774 glyphDevice->makeResourceDict(),
775 SkMatrix::Translate(glyphBBox.fLeft, glyphBBox.fTop), nullptr);
776 xobjects->insertRef(SkStringPrintf("Xg%X", gID), xobject);
777 SkPDFUtils::AppendScalar(drawableGlyph->advanceX(), &content);
778 content.writeText(" 0 d0\n1 0 0 1 0 0 cm\n/X");
779 content.write(characterName.c_str(), characterName.size());
780 content.writeText(" Do\n");
781 } else if (path && !path->isEmpty() && !pdfStrike.fHasMaskFilter) {
782 setGlyphWidthAndBoundingBox(pathGlyph->advanceX(), glyphBBox, &content);
783 SkPaint::Style style = pathGlyph->pathIsHairline() ? SkPaint::kStroke_Style
784 : SkPaint::kFill_Style;
785 SkPDFUtils::EmitPath(*path, style, &content);
786 SkPDFUtils::PaintPath(style, path->getFillType(), &content);
787 } else if (auto pimg = to_image(gID, &smallGlyphs); pimg.fImage) {
788 using SkPDFUtils::AppendScalar;
789 if (pimg.fImage->colorType() != kGray_8_SkColorType) {
790 AppendScalar(pathGlyph->advanceX(), &content);
791 content.writeText(" 0 d0\n");
792 AppendScalar(pimg.fImage->width() * bitmapScale, &content);
793 content.writeText(" 0 0 ");
794 AppendScalar(-pimg.fImage->height() * bitmapScale, &content);
795 content.writeText(" ");
796 AppendScalar(pimg.fOffset.x() * bitmapScale, &content);
797 content.writeText(" ");
798 AppendScalar((pimg.fImage->height() + pimg.fOffset.y()) * bitmapScale,&content);
799 content.writeText(" cm\n");
800 content.writeText("/X");
801 content.write(characterName.c_str(), characterName.size());
802 content.writeText(" Do\n");
803 SkPDFIndirectReference image = SkPDFSerializeImage(pimg.fImage.get(), doc);
804 xobjects->insertRef(SkStringPrintf("Xg%X", gID), image);
805 } else {
806 // TODO: For A1, put ImageMask on the PDF image and draw the image?
807 // The A8 mask has been converted to a Gray image
808
809 // This is a `d1` glyph (shaded with the current fill)
810 const SkGlyph* smallGlyph = smallGlyphs.glyph(SkPackedGlyphID{gID});
811 SkRect smallBBox = smallGlyph->rect();
812 SkIRect smallIBox;
813 SkMatrix::Scale(bitmapScale, bitmapScale).mapRect(smallBBox).roundOut(&smallIBox);
814 bbox.join(smallIBox);
815 setGlyphWidthAndBoundingBox(pathGlyph->advanceX(), smallIBox, &content);
816
817 AppendScalar(bitmapScale, &content);
818 content.writeText(" 0 0 ");
819 AppendScalar(bitmapScale, &content);
820 content.writeText(" ");
821 AppendScalar(pimg.fOffset.x() * bitmapScale, &content);
822 content.writeText(" ");
823 AppendScalar(pimg.fOffset.y() * bitmapScale, &content);
824 content.writeText(" cm\n");
825
826 // Convert Gray image to jpeg if needed
827 if (pdfStrike.fHasMaskFilter) {
828 SkJpegEncoder::Options jpegOptions;
829 jpegOptions.fQuality = 50; // SK_PDF_MASK_QUALITY
830 SkImage* image = pimg.fImage.get();
831 sk_sp<SkData> jpegData = SkJpegEncoder::Encode(nullptr, image, jpegOptions);
832 if (jpegData) {
833 sk_sp<SkImage> jpegImage = SkImages::DeferredFromEncodedData(jpegData);
834 SkASSERT(jpegImage);
835 if (jpegImage) {
836 pimg.fImage = jpegImage;
837 }
838 }
839 }
840
841 // Draw image into a Form XObject
842 const SkISize imageSize = pimg.fImage->dimensions();
843 sk_sp<SkPDFDevice> glyphDevice = sk_sp(new SkPDFDevice(imageSize, doc));
844 SkCanvas canvas(glyphDevice);
845 canvas.drawImage(pimg.fImage, 0, 0);
846 SkPDFIndirectReference sMask = SkPDFMakeFormXObject(
847 doc, glyphDevice->content(),
848 SkPDFMakeArray(0, 0, pimg.fImage->width(), pimg.fImage->height()),
849 glyphDevice->makeResourceDict(),
850 SkMatrix(), "DeviceGray");
851
852 // Use Form XObject as SMask (luminosity) on the graphics state
853 SkPDFIndirectReference smaskGraphicState = SkPDFGraphicState::GetSMaskGraphicState(
854 sMask, false,
855 SkPDFGraphicState::kLuminosity_SMaskMode, doc);
856 SkPDFUtils::ApplyGraphicState(smaskGraphicState.fValue, &content);
857
858 // Draw a rectangle the size of the glyph (masked by SMask)
859 SkPDFUtils::AppendRectangle(SkRect::Make(pimg.fImage->bounds()), &content);
860 SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPathFillType::kWinding, &content);
861
862 // Add glyph resources to font resource dict
863 xobjects->insertRef(SkStringPrintf("Xg%X", gID), sMask);
864 // TODO: name must match ApplyGraphicState
865 graphicStates->insertRef(SkStringPrintf("G%d", smaskGraphicState.fValue),
866 smaskGraphicState);
867 }
868 } else {
869 setGlyphWidthAndBoundingBox(pathGlyph->advanceX(), glyphBBox, &content);
870 }
871 charProcs->insertRef(std::move(characterName),
872 SkPDFStreamOut(nullptr, content.detachAsStream(), doc));
873 }
874
875 if (xobjects->size() || graphicStates->size()) {
876 auto resources = SkPDFMakeDict();
877 if (xobjects->size()) {
878 resources->insertObject("XObject", std::move(xobjects));
879 }
880 if (graphicStates->size()) {
881 resources->insertObject("ExtGState", std::move(graphicStates));
882 }
883 font.insertObject("Resources", std::move(resources));
884 }
885
886 encoding->insertObject("Differences", std::move(encDiffs));
887 font.insertInt("FirstChar", 0);
888 font.insertInt("LastChar", lastGlyphID - firstGlyphID + 1);
889 /* FontBBox: "A rectangle expressed in the glyph coordinate
890 system, specifying the font bounding box. This is the smallest
891 rectangle enclosing the shape that would result if all of the
892 glyphs of the font were placed with their origins coincident and
893 then filled." */
894 font.insertObject("FontBBox", SkPDFMakeArray(bbox.left(),
895 bbox.bottom(),
896 bbox.right(),
897 bbox.top()));
898
899 font.insertName("CIDToGIDMap", "Identity");
900
901 const SkTypeface& pathTypeface = pdfStrike.fPath.fStrikeSpec.typeface();
902 const std::vector<SkUnichar>& glyphToUnicode = SkPDFFont::GetUnicodeMap(pathTypeface, doc);
903 SkASSERT(glyphToUnicode.size() == SkToSizeT(pathTypeface.countGlyphs()));
904 auto toUnicodeCmap = SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
905 SkPDFFont::GetUnicodeMapEx(pathTypeface, doc),
906 &subset,
907 false,
908 firstGlyphID,
909 lastGlyphID);
910 font.insertRef("ToUnicode", SkPDFStreamOut(nullptr, std::move(toUnicodeCmap), doc));
911 font.insertRef("FontDescriptor", type3_descriptor(doc, pathTypeface, xHeight));
912 font.insertObject("Widths", std::move(widthArray));
913 font.insertObject("Encoding", std::move(encoding));
914 font.insertObject("CharProcs", std::move(charProcs));
915
916 doc->emit(font, pdfFont.indirectReference());
917 }
918
emitSubset(SkPDFDocument * doc) const919 void SkPDFFont::emitSubset(SkPDFDocument* doc) const {
920 switch (fFontType) {
921 case SkAdvancedTypefaceMetrics::kType1CID_Font:
922 case SkAdvancedTypefaceMetrics::kTrueType_Font:
923 return emit_subset_type0(*this, doc);
924 #ifndef SK_PDF_DO_NOT_SUPPORT_TYPE_1_FONTS
925 case SkAdvancedTypefaceMetrics::kType1_Font:
926 return SkPDFEmitType1Font(*this, doc);
927 #endif
928 default:
929 return emit_subset_type3(*this, doc);
930 }
931 }
932
933 ////////////////////////////////////////////////////////////////////////////////
934
CanEmbedTypeface(const SkTypeface & typeface,SkPDFDocument * doc)935 bool SkPDFFont::CanEmbedTypeface(const SkTypeface& typeface, SkPDFDocument* doc) {
936 const SkAdvancedTypefaceMetrics* metrics = SkPDFFont::GetMetrics(typeface, doc);
937 return metrics && can_embed(*metrics);
938 }
939
940