diff options
author | halcanary <halcanary@google.com> | 2015-10-12 13:05:04 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-10-12 13:05:04 -0700 |
commit | 66a82f3872abf4ebb98b3915b2a9ecc73ad352c5 (patch) | |
tree | e8bafe893fcce8e95d9d8b20f0c7f12d80ea8e0e | |
parent | 88d064d0e481949184305c7b1d6b282dddffac39 (diff) |
SkPDF: fall back on paths for unembeddable fonts.
Add GM, SkPDFFont::CanEmbedTypeface
BUG=skia:3866
Review URL: https://codereview.chromium.org/1401763002
-rw-r--r-- | gm/pdf_never_embed.cpp | 58 | ||||
-rw-r--r-- | resources/fonts/Roboto2-Regular_NoEmbed.ttf | bin | 0 -> 6156 bytes | |||
-rw-r--r-- | src/pdf/SkPDFCanon.h | 2 | ||||
-rw-r--r-- | src/pdf/SkPDFDevice.cpp | 72 | ||||
-rw-r--r-- | src/pdf/SkPDFDevice.h | 2 | ||||
-rw-r--r-- | src/pdf/SkPDFFont.cpp | 37 | ||||
-rw-r--r-- | src/pdf/SkPDFFont.h | 6 | ||||
-rw-r--r-- | tests/PDFPrimitivesTest.cpp | 20 |
8 files changed, 184 insertions, 13 deletions
diff --git a/gm/pdf_never_embed.cpp b/gm/pdf_never_embed.cpp new file mode 100644 index 0000000000..ef7974e3d7 --- /dev/null +++ b/gm/pdf_never_embed.cpp @@ -0,0 +1,58 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Resources.h" +#include "SkTypeface.h" +#include "gm.h" + +static void excercise_draw_pos_text(SkCanvas* canvas, + const char* text, + SkScalar x, SkScalar y, + const SkPaint& paint) { + size_t textLen = strlen(text); + SkAutoTArray<SkScalar> widths(SkToInt(textLen)); + paint.getTextWidths(text, textLen, &widths[0]); + SkAutoTArray<SkPoint> pos(SkToInt(textLen)); + for (int i = 0; i < SkToInt(textLen); ++i) { + pos[i].set(x, y); + x += widths[i]; + } + canvas->drawPosText(text, textLen, &pos[0], paint); +} + +DEF_SIMPLE_GM(pdf_never_embed, canvas, 512, 512) { + const char resource[] = "fonts/Roboto2-Regular_NoEmbed.ttf"; + SkAutoTUnref<SkTypeface> typeface(GetResourceAsTypeface(resource)); + if (!typeface) { + return; + } + SkPaint p; + p.setTextSize(60); + p.setTypeface(typeface); + p.setAntiAlias(true); + + const char text[] = "HELLO, WORLD!"; + + canvas->drawColor(SK_ColorWHITE); + excercise_draw_pos_text(canvas, text, 30, 90, p); + + canvas->save(); + canvas->rotate(45.0f); + p.setColor(0xF0800000); + excercise_draw_pos_text(canvas, text, 30, 45, p); + canvas->restore(); + + canvas->save(); + canvas->scale(1, 4.0); + p.setColor(0xF0008000); + excercise_draw_pos_text(canvas, text, 15, 70, p); + canvas->restore(); + + canvas->scale(1.0, 0.5); + p.setColor(0xF0000080); + canvas->drawText(text, strlen(text), 30, 700, p); +} diff --git a/resources/fonts/Roboto2-Regular_NoEmbed.ttf b/resources/fonts/Roboto2-Regular_NoEmbed.ttf Binary files differnew file mode 100644 index 0000000000..0892862370 --- /dev/null +++ b/resources/fonts/Roboto2-Regular_NoEmbed.ttf diff --git a/src/pdf/SkPDFCanon.h b/src/pdf/SkPDFCanon.h index a55024d611..988b2855e1 100644 --- a/src/pdf/SkPDFCanon.h +++ b/src/pdf/SkPDFCanon.h @@ -78,6 +78,8 @@ public: void addPDFBitmap(uint32_t imageUniqueID, SkPDFObject*); const SkImage* bitmapToImage(const SkBitmap&); + SkTHashMap<uint32_t, bool> fCanEmbedTypeface; + private: struct FontRec { SkPDFFont* fFont; diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index 9134d802ff..c27cc64bfb 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -1231,8 +1231,57 @@ static SkString format_wide_string(const uint16_t* input, } } +static void draw_transparent_text(SkPDFDevice* device, + const SkDraw& d, + const void* text, size_t len, + SkScalar x, SkScalar y, + const SkPaint& srcPaint) { + + SkPaint transparent; + if (!SkPDFFont::CanEmbedTypeface(transparent.getTypeface(), + device->getCanon())) { + SkDEBUGFAIL("default typeface should be embeddable"); + return; // Avoid infinite loop in release. + } + transparent.setTextSize(srcPaint.getTextSize()); + transparent.setColor(SK_ColorTRANSPARENT); + switch (srcPaint.getTextEncoding()) { + case SkPaint::kGlyphID_TextEncoding: { + // Since a glyphId<->Unicode mapping is typeface-specific, + // map back to Unicode first. + size_t glyphCount = len / 2; + SkAutoTMalloc<SkUnichar> unichars(glyphCount); + srcPaint.glyphsToUnichars( + (const uint16_t*)text, SkToInt(glyphCount), &unichars[0]); + transparent.setTextEncoding(SkPaint::kUTF32_TextEncoding); + device->drawText(d, &unichars[0], + glyphCount * sizeof(SkUnichar), + x, y, transparent); + break; + } + case SkPaint::kUTF8_TextEncoding: + case SkPaint::kUTF16_TextEncoding: + case SkPaint::kUTF32_TextEncoding: + transparent.setTextEncoding(srcPaint.getTextEncoding()); + device->drawText(d, text, len, x, y, transparent); + break; + default: + SkFAIL("unknown text encoding"); + } +} + + void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, SkScalar x, SkScalar y, const SkPaint& srcPaint) { + if (!SkPDFFont::CanEmbedTypeface(srcPaint.getTypeface(), fCanon)) { + // http://skbug.com/3866 + SkPath path; + srcPaint.getTextPath(text, len, x, y, &path); + this->drawPath(d, path, srcPaint, &SkMatrix::I(), true); + // Draw text transparently to make it copyable/searchable/accessable. + draw_transparent_text(this, d, text, len, x, y, srcPaint); + return; + } SkPaint paint = srcPaint; replace_srcmode_on_opaque_paint(&paint); @@ -1285,6 +1334,29 @@ void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len, const SkScalar pos[], int scalarsPerPos, const SkPoint& offset, const SkPaint& srcPaint) { + if (!SkPDFFont::CanEmbedTypeface(srcPaint.getTypeface(), fCanon)) { + const SkPoint* positions = reinterpret_cast<const SkPoint*>(pos); + SkAutoTMalloc<SkPoint> positionsBuffer; + if (2 != scalarsPerPos) { + int glyphCount = srcPaint.textToGlyphs(text, len, NULL); + positionsBuffer.reset(glyphCount); + for (int i = 0; i < glyphCount; ++i) { + positionsBuffer[i].set(pos[i], 0.0f); + } + positions = &positionsBuffer[0]; + } + SkPath path; + srcPaint.getPosTextPath(text, len, positions, &path); + SkMatrix matrix; + matrix.setTranslate(offset); + this->drawPath(d, path, srcPaint, &matrix, true); + // Draw text transparently to make it copyable/searchable/accessable. + draw_transparent_text( + this, d, text, len, offset.x() + positions[0].x(), + offset.y() + positions[0].y(), srcPaint); + return; + } + SkPaint paint = srcPaint; replace_srcmode_on_opaque_paint(&paint); diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h index 75447f9be2..742e45ca5c 100644 --- a/src/pdf/SkPDFDevice.h +++ b/src/pdf/SkPDFDevice.h @@ -189,9 +189,7 @@ public: return *(fFontGlyphUsage.get()); } -#ifdef SK_DEBUG SkPDFCanon* getCanon() const { return fCanon; } -#endif // SK_DEBUG protected: const SkBitmap& onAccessBitmap() override { diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp index 8df59b77f1..9cbada8364 100644 --- a/src/pdf/SkPDFFont.cpp +++ b/src/pdf/SkPDFFont.cpp @@ -1051,10 +1051,7 @@ bool SkPDFCIDFont::addFontDescriptor(int16_t defaultWidth, this->insertObjRef("FontDescriptor", descriptor.detach()); return false; } - if (!canEmbed()) { - this->insertObjRef("FontDescriptor", descriptor.detach()); - return true; - } + SkASSERT(this->canEmbed()); switch (getType()) { case SkAdvancedTypefaceMetrics::kTrueType_Font: { @@ -1222,13 +1219,12 @@ bool SkPDFType1Font::addFontDescriptor(int16_t defaultWidth) { if (fontData.get() == nullptr) { return false; } - if (canEmbed()) { - SkAutoTUnref<SkPDFStream> fontStream(new SkPDFStream(fontData.get())); - fontStream->insertInt("Length1", header); - fontStream->insertInt("Length2", data); - fontStream->insertInt("Length3", trailer); - descriptor->insertObjRef("FontFile", fontStream.detach()); - } + SkASSERT(this->canEmbed()); + SkAutoTUnref<SkPDFStream> fontStream(new SkPDFStream(fontData.get())); + fontStream->insertInt("Length1", header); + fontStream->insertInt("Length2", data); + fontStream->insertInt("Length3", trailer); + descriptor->insertObjRef("FontFile", fontStream.detach()); this->insertObjRef("FontDescriptor", descriptor.detach()); @@ -1418,3 +1414,22 @@ SkPDFFont::Match SkPDFFont::IsMatch(SkPDFFont* existingFont, return (existingGlyphID == searchGlyphID) ? SkPDFFont::kExact_Match : SkPDFFont::kRelated_Match; } + +// Since getAdvancedTypefaceMetrics is expensive, cache the result. +bool SkPDFFont::CanEmbedTypeface(SkTypeface* typeface, SkPDFCanon* canon) { + SkAutoResolveDefaultTypeface face(typeface); + uint32_t id = face->uniqueID(); + if (bool* value = canon->fCanEmbedTypeface.find(id)) { + return *value; + } + bool canEmbed = true; + SkAutoTUnref<const SkAdvancedTypefaceMetrics> fontMetrics( + face->getAdvancedTypefaceMetrics( + SkTypeface::kNo_PerGlyphInfo, nullptr, 0)); + if (fontMetrics) { + canEmbed = !SkToBool( + fontMetrics->fFlags & + SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag); + } + return *canon->fCanEmbedTypeface.set(id, canEmbed); +} diff --git a/src/pdf/SkPDFFont.h b/src/pdf/SkPDFFont.h index 404e8b7621..ca0a51fca4 100644 --- a/src/pdf/SkPDFFont.h +++ b/src/pdf/SkPDFFont.h @@ -149,6 +149,12 @@ public: uint32_t searchFontID, uint16_t searchGlyphID); + /** + * Return false iff the typeface has its NotEmbeddable flag set. + * If typeface is NULL, the default typeface is checked. + */ + static bool CanEmbedTypeface(SkTypeface*, SkPDFCanon*); + protected: // Common constructor to handle common members. SkPDFFont(const SkAdvancedTypefaceMetrics* fontInfo, diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp index 3253fcaf71..5fce53ea0a 100644 --- a/tests/PDFPrimitivesTest.cpp +++ b/tests/PDFPrimitivesTest.cpp @@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "Resources.h" #include "SkBitmap.h" #include "SkCanvas.h" #include "SkData.h" @@ -14,6 +15,7 @@ #include "SkMatrix.h" #include "SkPDFCanon.h" #include "SkPDFDevice.h" +#include "SkPDFFont.h" #include "SkPDFStream.h" #include "SkPDFTypes.h" #include "SkReadBuffer.h" @@ -21,6 +23,7 @@ #include "SkStream.h" #include "SkTypes.h" #include "Test.h" +#include "sk_tool_utils.h" #define DUMMY_TEXT "DCT compessed stream." @@ -415,3 +418,20 @@ DEF_TEST(PDFImageFilter, reporter) { // Filter was used in rendering; should be visited. REPORTER_ASSERT(reporter, filter->visited()); } + +// Check that PDF rendering of image filters successfully falls back to +// CPU rasterization. +DEF_TEST(PDFFontCanEmbedTypeface, reporter) { + SkPDFCanon canon; + + const char resource[] = "fonts/Roboto2-Regular_NoEmbed.ttf"; + SkAutoTUnref<SkTypeface> noEmbedTypeface(GetResourceAsTypeface(resource)); + if (noEmbedTypeface) { + REPORTER_ASSERT(reporter, + !SkPDFFont::CanEmbedTypeface(noEmbedTypeface, &canon)); + } + SkAutoTUnref<SkTypeface> portableTypeface( + sk_tool_utils::create_portable_typeface(NULL, SkTypeface::kNormal)); + REPORTER_ASSERT(reporter, + SkPDFFont::CanEmbedTypeface(portableTypeface, &canon)); +} |