| Index: src/pdf/SkPDFDevice.cpp
|
| diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
|
| index a36125f38090f26fa574e36a2c2ac0bb31808cb9..24df879a9d1b27b6318feb96ece87ace73aa3563 100644
|
| --- a/src/pdf/SkPDFDevice.cpp
|
| +++ b/src/pdf/SkPDFDevice.cpp
|
| @@ -28,11 +28,11 @@
|
| #include "SkPDFUtils.h"
|
| #include "SkRasterClip.h"
|
| #include "SkRRect.h"
|
| +#include "SkScopeExit.h"
|
| #include "SkString.h"
|
| #include "SkSurface.h"
|
| #include "SkTextFormatParams.h"
|
| #include "SkTemplates.h"
|
| -#include "SkTypefacePriv.h"
|
| #include "SkXfermodeInterpretation.h"
|
|
|
| #define DPI_FOR_RASTER_SCALE_ONE 72
|
| @@ -76,19 +76,6 @@ static SkPaint calculate_text_paint(const SkPaint& paint) {
|
| return result;
|
| }
|
|
|
| -static void set_text_transform(SkScalar x, SkScalar y, SkScalar textSkewX,
|
| - SkWStream* content) {
|
| - // Flip the text about the x-axis to account for origin swap and include
|
| - // the passed parameters.
|
| - content->writeText("1 0 ");
|
| - SkPDFUtils::AppendScalar(0 - textSkewX, content);
|
| - content->writeText(" -1 ");
|
| - SkPDFUtils::AppendScalar(x, content);
|
| - content->writeText(" ");
|
| - SkPDFUtils::AppendScalar(y, content);
|
| - content->writeText(" Tm\n");
|
| -}
|
| -
|
| SkPDFDevice::GraphicStateEntry::GraphicStateEntry()
|
| : fColor(SK_ColorBLACK)
|
| , fTextScaleX(SK_Scalar1)
|
| @@ -965,15 +952,22 @@ public:
|
| bool defaultPositioning,
|
| SkPoint origin)
|
| : fContent(content)
|
| - , fCurrentMatrixX(0.0f)
|
| - , fCurrentMatrixY(0.0f)
|
| + , fCurrentMatrixOrigin{0.0f, 0.0f}
|
| , fXAdvance(0.0f)
|
| , fWideChars(wideChars)
|
| , fInText(false)
|
| , fDefaultPositioning(defaultPositioning) {
|
| - set_text_transform(origin.x(), origin.y(), textSkewX, fContent);
|
| - }
|
| - ~GlyphPositioner() { SkASSERT(!fInText); /* flush first */ }
|
| + // Flip the text about the x-axis to account for origin swap and include
|
| + // the passed parameters.
|
| + fContent->writeText("1 0 ");
|
| + SkPDFUtils::AppendScalar(0 - textSkewX, fContent);
|
| + fContent->writeText(" -1 ");
|
| + SkPDFUtils::AppendScalar(origin.x(), fContent);
|
| + fContent->writeText(" ");
|
| + SkPDFUtils::AppendScalar(origin.y(), fContent);
|
| + fContent->writeText(" Tm\n");
|
| + }
|
| + ~GlyphPositioner() { this->flush(); }
|
| void flush() {
|
| if (fInText) {
|
| fContent->writeText("> Tj\n");
|
| @@ -987,21 +981,18 @@ public:
|
| fWideChars = wideChars;
|
| }
|
| }
|
| - void writeGlyph(SkScalar x,
|
| - SkScalar y,
|
| + void writeGlyph(SkPoint xy,
|
| SkScalar advanceWidth,
|
| uint16_t glyph) {
|
| if (!fDefaultPositioning) {
|
| - SkScalar xPosition = x - fCurrentMatrixX;
|
| - SkScalar yPosition = y - fCurrentMatrixY;
|
| - if (xPosition != fXAdvance || yPosition != 0) {
|
| + SkPoint position = xy - fCurrentMatrixOrigin;
|
| + if (position != SkPoint{fXAdvance, 0}) {
|
| this->flush();
|
| - SkPDFUtils::AppendScalar(xPosition, fContent);
|
| + SkPDFUtils::AppendScalar(position.x(), fContent);
|
| fContent->writeText(" ");
|
| - SkPDFUtils::AppendScalar(-yPosition, fContent);
|
| + SkPDFUtils::AppendScalar(-position.y(), fContent);
|
| fContent->writeText(" Td ");
|
| - fCurrentMatrixX = x;
|
| - fCurrentMatrixY = y;
|
| + fCurrentMatrixOrigin = xy;
|
| fXAdvance = 0;
|
| }
|
| fXAdvance += advanceWidth;
|
| @@ -1020,8 +1011,7 @@ public:
|
|
|
| private:
|
| SkDynamicMemoryWStream* fContent;
|
| - SkScalar fCurrentMatrixX;
|
| - SkScalar fCurrentMatrixY;
|
| + SkPoint fCurrentMatrixOrigin;
|
| SkScalar fXAdvance;
|
| bool fWideChars;
|
| bool fInText;
|
| @@ -1034,12 +1024,13 @@ static void draw_transparent_text(SkPDFDevice* device,
|
| const void* text, size_t len,
|
| SkScalar x, SkScalar y,
|
| const SkPaint& srcPaint) {
|
| - SkPaint transparent;
|
| - if (!SkPDFFont::CanEmbedTypeface(transparent.getTypeface(),
|
| - device->getCanon())) {
|
| + sk_sp<SkTypeface> defaultFace = SkTypeface::MakeDefault();
|
| + if (!SkPDFFont::CanEmbedTypeface(defaultFace.get(), device->getCanon())) {
|
| SkDebugf("SkPDF: default typeface should be embeddable");
|
| return; // Avoid infinite loop in release.
|
| }
|
| + SkPaint transparent;
|
| + transparent.setTypeface(std::move(defaultFace));
|
| transparent.setTextSize(srcPaint.getTextSize());
|
| transparent.setColor(SK_ColorTRANSPARENT);
|
| switch (srcPaint.getTextEncoding()) {
|
| @@ -1097,11 +1088,14 @@ void SkPDFDevice::internalDrawText(
|
| SkDebugf("SkPDF: SkTypeface::MakeDefault() returned nullptr.\n");
|
| return;
|
| }
|
| - int typefaceGlyphCount = typeface->countGlyphs();
|
| - if (typefaceGlyphCount < 1) {
|
| - SkDebugf("SkPDF: SkTypeface has no glyphs.\n");
|
| +
|
| + const SkAdvancedTypefaceMetrics* metrics =
|
| + SkPDFFont::GetMetrics(typeface, fDocument->canon());
|
| + if (!metrics) {
|
| return;
|
| }
|
| + // TODO(halcanary): use metrics->fGlyphToUnicode to check Unicode mapping.
|
| + const SkGlyphID maxGlyphID = metrics->fLastGlyphID;
|
| if (!SkPDFFont::CanEmbedTypeface(typeface, fDocument->canon())) {
|
| SkPath path; // https://bug.skia.org/3866
|
| paint.getTextPath(sourceText, sourceByteCount,
|
| @@ -1112,21 +1106,19 @@ void SkPDFDevice::internalDrawText(
|
| offset.x(), offset.y(), paint);
|
| return;
|
| }
|
| - // Always make a copy (1) to validate user-input glyphs and
|
| - // (2) because we may modify the glyphs in place (for
|
| - // single-byte-glyph-id PDF fonts).
|
| int glyphCount = paint.textToGlyphs(sourceText, sourceByteCount, nullptr);
|
| - if (glyphCount <= 0) { return; }
|
| - SkAutoSTMalloc<128, SkGlyphID> glyphStorage(SkToSizeT(glyphCount));
|
| - SkGlyphID* glyphs = glyphStorage.get();
|
| - (void)paint.textToGlyphs(sourceText, sourceByteCount, glyphs);
|
| + if (glyphCount <= 0) {
|
| + return;
|
| + }
|
| + SkAutoSTMalloc<128, SkGlyphID> glyphStorage;
|
| + const SkGlyphID* glyphs = nullptr;
|
| if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
|
| - // Validate user-input glyphs.
|
| - SkGlyphID maxGlyphID = SkToU16(typefaceGlyphCount - 1);
|
| - for (int i = 0; i < glyphCount; ++i) {
|
| - glyphs[i] = SkTMin(maxGlyphID, glyphs[i]);
|
| - }
|
| + glyphs = (const SkGlyphID*)sourceText;
|
| + // validate input later.
|
| } else {
|
| + glyphStorage.reset(SkToSizeT(glyphCount));
|
| + (void)paint.textToGlyphs(sourceText, sourceByteCount, glyphStorage.get());
|
| + glyphs = glyphStorage.get();
|
| paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
|
| }
|
|
|
| @@ -1135,89 +1127,92 @@ void SkPDFDevice::internalDrawText(
|
| SkAutoGlyphCache glyphCache(paint, nullptr, nullptr);
|
|
|
| SkPaint::Align alignment = paint.getTextAlign();
|
| + float alignmentFactor = SkPaint::kLeft_Align == alignment ? 0.0f :
|
| + SkPaint::kCenter_Align == alignment ? -0.5f :
|
| + /* SkPaint::kRight_Align */ -1.0f;
|
| if (defaultPositioning && alignment != SkPaint::kLeft_Align) {
|
| - SkScalar advance{0};
|
| + SkScalar advance = 0;
|
| for (int i = 0; i < glyphCount; ++i) {
|
| advance += glyphCache->getGlyphIDAdvance(glyphs[i]).fAdvanceX;
|
| }
|
| - SkScalar m = alignment == SkPaint::kCenter_Align
|
| - ? 0.5f * advance : advance;
|
| - offset -= SkPoint{m, 0};
|
| + offset.offset(alignmentFactor * advance, 0);
|
| }
|
| ScopedContentEntry content(this, d, paint, true);
|
| if (!content.entry()) {
|
| return;
|
| }
|
| SkDynamicMemoryWStream* out = &content.entry()->fContent;
|
| - out->writeText("BT\n");
|
| - if (!this->updateFont(paint, glyphs[0], content.entry())) {
|
| - SkDebugf("SkPDF: Font error.");
|
| - out->writeText("ET\n%SkPDF: Font error.\n");
|
| - return;
|
| + SkScalar textSize = paint.getTextSize();
|
| +
|
| + int index = 0;
|
| + while (glyphs[index] > maxGlyphID) { // Invalid glyphID for this font.
|
| + ++index; // Skip this glyphID
|
| + if (index == glyphCount) {
|
| + return; // all glyphIDs were bad.
|
| + }
|
| }
|
| - SkPDFFont* font = content.entry()->fState.fFont;
|
| +
|
| + out->writeText("BT\n");
|
| + SK_AT_SCOPE_EXIT(out->writeText("ET\n"));
|
| +
|
| + SkPDFFont* font = this->updateFont(
|
| + typeface, textSize, glyphs[index], content.entry());
|
| + SkASSERT(font); // All preconditions for SkPDFFont::GetFontResource are met.
|
| + if (!font) { return; }
|
| +
|
| GlyphPositioner glyphPositioner(out,
|
| paint.getTextSkewX(),
|
| font->multiByteGlyphs(),
|
| defaultPositioning,
|
| offset);
|
| - const SkGlyphID* const glyphsEnd = glyphs + glyphCount;
|
| -
|
| - while (glyphs < glyphsEnd) {
|
| - font = content.entry()->fState.fFont;
|
| - int stretch = font->multiByteGlyphs()
|
| - ? SkToInt(glyphsEnd - glyphs)
|
| - : font->glyphsToPDFFontEncodingCount(glyphs, SkToInt(glyphsEnd - glyphs));
|
| - SkASSERT(glyphs + stretch <= glyphsEnd);
|
| +
|
| + while (index < glyphCount) {
|
| + int stretch = font->countStretch(&glyphs[index], glyphCount - index, maxGlyphID);
|
| + SkASSERT(index + stretch <= glyphCount);
|
| if (stretch < 1) {
|
| - SkASSERT(!font->multiByteGlyphs());
|
| // The current pdf font cannot encode the next glyph.
|
| // Try to get a pdf font which can encode the next glyph.
|
| glyphPositioner.flush();
|
| - if (!this->updateFont(paint, *glyphs, content.entry())) {
|
| - SkDebugf("SkPDF: Font error.");
|
| - out->writeText("ET\n%SkPDF: Font error.\n");
|
| - return;
|
| + // first, validate the next glyph
|
| + while (glyphs[index] > maxGlyphID) {
|
| + ++index; // Skip this glyphID
|
| + if (index == glyphCount) {
|
| + return; // all remainng glyphIDs were bad.
|
| + }
|
| }
|
| - font = content.entry()->fState.fFont;
|
| + SkASSERT(index < glyphCount);
|
| + font = this->updateFont(typeface, textSize, glyphs[index], content.entry());
|
| + SkASSERT(font); // preconditions for SkPDFFont::GetFontResource met.
|
| + if (!font) { return; }
|
| glyphPositioner.setWideChars(font->multiByteGlyphs());
|
| - // try again
|
| - stretch = font->glyphsToPDFFontEncodingCount(glyphs,
|
| - SkToInt(glyphsEnd - glyphs));
|
| + // Get stretch for this new font.
|
| + stretch = font->countStretch(&glyphs[index], glyphCount - index, maxGlyphID);
|
| if (stretch < 1) {
|
| SkDEBUGFAIL("PDF could not encode glyph.");
|
| - glyphPositioner.flush();
|
| - out->writeText("ET\n%SkPDF: Font encoding error.\n");
|
| return;
|
| }
|
| }
|
| - font->noteGlyphUsage(glyphs, stretch);
|
| - if (defaultPositioning) {
|
| - (void)font->glyphsToPDFFontEncoding(glyphs, SkToInt(glyphsEnd - glyphs));
|
| - while (stretch-- > 0) {
|
| - glyphPositioner.writeGlyph(0, 0, 0, *glyphs);
|
| - ++glyphs;
|
| - }
|
| - } else {
|
| - while (stretch-- > 0) {
|
| - SkScalar advance = glyphCache->getGlyphIDAdvance(*glyphs).fAdvanceX;
|
| - SkScalar x = *pos++;
|
| - // evaluate x and y in order!
|
| - SkScalar y = SkTextBlob::kFull_Positioning == positioning ? *pos++ : 0;
|
| - SkPoint xy{x, y};
|
| - if (alignment != SkPaint::kLeft_Align) {
|
| - SkScalar m = alignment == SkPaint::kCenter_Align
|
| - ? 0.5f * advance : advance;
|
| - xy -= SkPoint{m, 0};
|
| + while (stretch-- > 0) {
|
| + SkGlyphID gid = glyphs[index];
|
| + if (gid <= maxGlyphID) {
|
| + font->noteGlyphUsage(gid);
|
| + SkGlyphID encodedGlyph = font->glyphToPDFFontEncoding(gid);
|
| + if (defaultPositioning) {
|
| + glyphPositioner.writeGlyph(SkPoint{0, 0}, 0, encodedGlyph);
|
| + } else {
|
| + SkScalar advance = glyphCache->getGlyphIDAdvance(gid).fAdvanceX;
|
| + SkPoint xy = SkTextBlob::kFull_Positioning == positioning
|
| + ? SkPoint{pos[2 * index], pos[2 * index + 1]}
|
| + : SkPoint{pos[index], 0};
|
| + if (alignment != SkPaint::kLeft_Align) {
|
| + xy.offset(alignmentFactor * advance, 0);
|
| + }
|
| + glyphPositioner.writeGlyph(xy, advance, encodedGlyph);
|
| }
|
| - (void)font->glyphsToPDFFontEncoding(glyphs, 1);
|
| - glyphPositioner.writeGlyph(xy.x(), xy.y(), advance, *glyphs);
|
| - ++glyphs;
|
| }
|
| + ++index;
|
| }
|
| }
|
| - glyphPositioner.flush();
|
| - out->writeText("ET\n");
|
| }
|
|
|
| void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
|
| @@ -1903,26 +1898,28 @@ int SkPDFDevice::addXObjectResource(SkPDFObject* xObject) {
|
| return result;
|
| }
|
|
|
| -bool SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID,
|
| - SkPDFDevice::ContentEntry* contentEntry) {
|
| - SkTypeface* typeface = paint.getTypeface();
|
| +SkPDFFont* SkPDFDevice::updateFont(SkTypeface* typeface,
|
| + SkScalar textSize,
|
| + uint16_t glyphID,
|
| + SkPDFDevice::ContentEntry* contentEntry) {
|
| if (contentEntry->fState.fFont == nullptr ||
|
| - contentEntry->fState.fTextSize != paint.getTextSize() ||
|
| + contentEntry->fState.fTextSize != textSize ||
|
| !contentEntry->fState.fFont->hasGlyph(glyphID)) {
|
| int fontIndex = getFontResourceIndex(typeface, glyphID);
|
| if (fontIndex < 0) {
|
| - return false;
|
| + SkDebugf("SkPDF: Font error.");
|
| + return nullptr;
|
| }
|
| contentEntry->fContent.writeText("/");
|
| contentEntry->fContent.writeText(SkPDFResourceDict::getResourceName(
|
| SkPDFResourceDict::kFont_ResourceType,
|
| fontIndex).c_str());
|
| contentEntry->fContent.writeText(" ");
|
| - SkPDFUtils::AppendScalar(paint.getTextSize(), &contentEntry->fContent);
|
| + SkPDFUtils::AppendScalar(textSize, &contentEntry->fContent);
|
| contentEntry->fContent.writeText(" Tf\n");
|
| contentEntry->fState.fFont = fFontResources[fontIndex];
|
| }
|
| - return true;
|
| + return contentEntry->fState.fFont;
|
| }
|
|
|
| int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
|
|
|