| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2011 Google Inc. | 2 * Copyright 2011 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "SkPDFDevice.h" | 8 #include "SkPDFDevice.h" |
| 9 | 9 |
| 10 #include "SkAnnotationKeys.h" | 10 #include "SkAnnotationKeys.h" |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 78 result.setStrokeWidth(width); | 78 result.setStrokeWidth(width); |
| 79 } | 79 } |
| 80 return result; | 80 return result; |
| 81 } | 81 } |
| 82 | 82 |
| 83 SkPDFDevice::GraphicStateEntry::GraphicStateEntry() | 83 SkPDFDevice::GraphicStateEntry::GraphicStateEntry() |
| 84 : fColor(SK_ColorBLACK) | 84 : fColor(SK_ColorBLACK) |
| 85 , fTextScaleX(SK_Scalar1) | 85 , fTextScaleX(SK_Scalar1) |
| 86 , fTextFill(SkPaint::kFill_Style) | 86 , fTextFill(SkPaint::kFill_Style) |
| 87 , fShaderIndex(-1) | 87 , fShaderIndex(-1) |
| 88 , fGraphicStateIndex(-1) | 88 , fGraphicStateIndex(-1) { |
| 89 , fFont(nullptr) | |
| 90 , fTextSize(SK_ScalarNaN) { | |
| 91 fMatrix.reset(); | 89 fMatrix.reset(); |
| 92 } | 90 } |
| 93 | 91 |
| 94 bool SkPDFDevice::GraphicStateEntry::compareInitialState( | 92 bool SkPDFDevice::GraphicStateEntry::compareInitialState( |
| 95 const GraphicStateEntry& cur) { | 93 const GraphicStateEntry& cur) { |
| 96 return fColor == cur.fColor && | 94 return fColor == cur.fColor && |
| 97 fShaderIndex == cur.fShaderIndex && | 95 fShaderIndex == cur.fShaderIndex && |
| 98 fGraphicStateIndex == cur.fGraphicStateIndex && | 96 fGraphicStateIndex == cur.fGraphicStateIndex && |
| 99 fMatrix == cur.fMatrix && | 97 fMatrix == cur.fMatrix && |
| 100 fClipStack == cur.fClipStack && | 98 fClipStack == cur.fClipStack && |
| (...skipping 753 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 854 | 852 |
| 855 namespace { | 853 namespace { |
| 856 class GlyphPositioner { | 854 class GlyphPositioner { |
| 857 public: | 855 public: |
| 858 GlyphPositioner(SkDynamicMemoryWStream* content, | 856 GlyphPositioner(SkDynamicMemoryWStream* content, |
| 859 SkScalar textSkewX, | 857 SkScalar textSkewX, |
| 860 bool wideChars, | 858 bool wideChars, |
| 861 bool defaultPositioning, | 859 bool defaultPositioning, |
| 862 SkPoint origin) | 860 SkPoint origin) |
| 863 : fContent(content) | 861 : fContent(content) |
| 864 , fCurrentMatrixOrigin{0.0f, 0.0f} | 862 , fCurrentMatrixOrigin(origin) |
| 865 , fXAdvance(0.0f) | 863 , fTextSkewX(textSkewX) |
| 866 , fWideChars(wideChars) | 864 , fWideChars(wideChars) |
| 867 , fInText(false) | |
| 868 , fDefaultPositioning(defaultPositioning) { | 865 , fDefaultPositioning(defaultPositioning) { |
| 869 // Flip the text about the x-axis to account for origin swap and include | |
| 870 // the passed parameters. | |
| 871 fContent->writeText("1 0 "); | |
| 872 SkPDFUtils::AppendScalar(0 - textSkewX, fContent); | |
| 873 fContent->writeText(" -1 "); | |
| 874 SkPDFUtils::AppendScalar(origin.x(), fContent); | |
| 875 fContent->writeText(" "); | |
| 876 SkPDFUtils::AppendScalar(origin.y(), fContent); | |
| 877 fContent->writeText(" Tm\n"); | |
| 878 } | 866 } |
| 879 ~GlyphPositioner() { this->flush(); } | 867 ~GlyphPositioner() { this->flush(); } |
| 880 void flush() { | 868 void flush() { |
| 881 if (fInText) { | 869 if (fInText) { |
| 882 fContent->writeText("> Tj\n"); | 870 fContent->writeText("> Tj\n"); |
| 883 fInText = false; | 871 fInText = false; |
| 884 } | 872 } |
| 885 } | 873 } |
| 886 void setWideChars(bool wideChars) { | |
| 887 if (fWideChars != wideChars) { | |
| 888 SkASSERT(!fInText); | |
| 889 SkASSERT(fWideChars == wideChars); | |
| 890 fWideChars = wideChars; | |
| 891 } | |
| 892 } | |
| 893 void writeGlyph(SkPoint xy, | 874 void writeGlyph(SkPoint xy, |
| 894 SkScalar advanceWidth, | 875 SkScalar advanceWidth, |
| 895 uint16_t glyph) { | 876 uint16_t glyph) { |
| 877 if (!fInitialized) { |
| 878 // Flip the text about the x-axis to account for origin swap and inc
lude |
| 879 // the passed parameters. |
| 880 fContent->writeText("1 0 "); |
| 881 SkPDFUtils::AppendScalar(-fTextSkewX, fContent); |
| 882 fContent->writeText(" -1 "); |
| 883 SkPDFUtils::AppendScalar(fCurrentMatrixOrigin.x(), fContent); |
| 884 fContent->writeText(" "); |
| 885 SkPDFUtils::AppendScalar(fCurrentMatrixOrigin.y(), fContent); |
| 886 fContent->writeText(" Tm\n"); |
| 887 fCurrentMatrixOrigin.set(0.0f, 0.0f); |
| 888 fInitialized = true; |
| 889 } |
| 896 if (!fDefaultPositioning) { | 890 if (!fDefaultPositioning) { |
| 897 SkPoint position = xy - fCurrentMatrixOrigin; | 891 SkPoint position = xy - fCurrentMatrixOrigin; |
| 898 if (position != SkPoint{fXAdvance, 0}) { | 892 if (position != SkPoint{fXAdvance, 0}) { |
| 899 this->flush(); | 893 this->flush(); |
| 900 SkPDFUtils::AppendScalar(position.x(), fContent); | 894 SkPDFUtils::AppendScalar(position.x(), fContent); |
| 901 fContent->writeText(" "); | 895 fContent->writeText(" "); |
| 902 SkPDFUtils::AppendScalar(-position.y(), fContent); | 896 SkPDFUtils::AppendScalar(-position.y(), fContent); |
| 903 fContent->writeText(" Td "); | 897 fContent->writeText(" Td "); |
| 904 fCurrentMatrixOrigin = xy; | 898 fCurrentMatrixOrigin = xy; |
| 905 fXAdvance = 0; | 899 fXAdvance = 0; |
| 906 } | 900 } |
| 907 fXAdvance += advanceWidth; | 901 fXAdvance += advanceWidth; |
| 908 } | 902 } |
| 909 if (!fInText) { | 903 if (!fInText) { |
| 910 fContent->writeText("<"); | 904 fContent->writeText("<"); |
| 911 fInText = true; | 905 fInText = true; |
| 912 } | 906 } |
| 913 if (fWideChars) { | 907 if (fWideChars) { |
| 914 SkPDFUtils::WriteUInt16BE(fContent, glyph); | 908 SkPDFUtils::WriteUInt16BE(fContent, glyph); |
| 915 } else { | 909 } else { |
| 916 SkASSERT(0 == glyph >> 8); | 910 SkASSERT(0 == glyph >> 8); |
| 917 SkPDFUtils::WriteUInt8(fContent, static_cast<uint8_t>(glyph)); | 911 SkPDFUtils::WriteUInt8(fContent, static_cast<uint8_t>(glyph)); |
| 918 } | 912 } |
| 919 } | 913 } |
| 920 | 914 |
| 921 private: | 915 private: |
| 922 SkDynamicMemoryWStream* fContent; | 916 SkDynamicMemoryWStream* fContent; |
| 923 SkPoint fCurrentMatrixOrigin; | 917 SkPoint fCurrentMatrixOrigin; |
| 924 SkScalar fXAdvance; | 918 SkScalar fXAdvance = 0.0f; |
| 919 SkScalar fTextSkewX; |
| 925 bool fWideChars; | 920 bool fWideChars; |
| 926 bool fInText; | 921 bool fInText = false; |
| 922 bool fInitialized = false; |
| 927 const bool fDefaultPositioning; | 923 const bool fDefaultPositioning; |
| 928 }; | 924 }; |
| 929 } // namespace | 925 } // namespace |
| 930 | 926 |
| 931 static void draw_transparent_text(SkPDFDevice* device, | 927 static void draw_transparent_text(SkPDFDevice* device, |
| 932 const SkDraw& d, | 928 const SkDraw& d, |
| 933 const void* text, size_t len, | 929 const void* text, size_t len, |
| 934 SkScalar x, SkScalar y, | 930 SkScalar x, SkScalar y, |
| 935 const SkPaint& srcPaint) { | 931 const SkPaint& srcPaint) { |
| 936 sk_sp<SkTypeface> defaultFace = SkTypeface::MakeDefault(); | 932 sk_sp<SkTypeface> defaultFace = SkTypeface::MakeDefault(); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 962 case SkPaint::kUTF16_TextEncoding: | 958 case SkPaint::kUTF16_TextEncoding: |
| 963 case SkPaint::kUTF32_TextEncoding: | 959 case SkPaint::kUTF32_TextEncoding: |
| 964 transparent.setTextEncoding(srcPaint.getTextEncoding()); | 960 transparent.setTextEncoding(srcPaint.getTextEncoding()); |
| 965 device->drawText(d, text, len, x, y, transparent); | 961 device->drawText(d, text, len, x, y, transparent); |
| 966 break; | 962 break; |
| 967 default: | 963 default: |
| 968 SkFAIL("unknown text encoding"); | 964 SkFAIL("unknown text encoding"); |
| 969 } | 965 } |
| 970 } | 966 } |
| 971 | 967 |
| 968 static void update_font(SkWStream* wStream, int fontIndex, SkScalar textSize) { |
| 969 wStream->writeText("/"); |
| 970 char prefix = SkPDFResourceDict::GetResourceTypePrefix(SkPDFResourceDict::kF
ont_ResourceType); |
| 971 wStream->write(&prefix, 1); |
| 972 wStream->writeDecAsText(fontIndex); |
| 973 wStream->writeText(" "); |
| 974 SkPDFUtils::AppendScalar(textSize, wStream); |
| 975 wStream->writeText(" Tf\n"); |
| 976 } |
| 977 |
| 972 void SkPDFDevice::internalDrawText( | 978 void SkPDFDevice::internalDrawText( |
| 973 const SkDraw& d, const void* sourceText, size_t sourceByteCount, | 979 const SkDraw& d, const void* sourceText, size_t sourceByteCount, |
| 974 const SkScalar pos[], SkTextBlob::GlyphPositioning positioning, | 980 const SkScalar pos[], SkTextBlob::GlyphPositioning positioning, |
| 975 SkPoint offset, const SkPaint& srcPaint, const uint32_t* clusters, | 981 SkPoint offset, const SkPaint& srcPaint, const uint32_t* clusters, |
| 976 uint32_t textByteLength, const char* utf8Text) { | 982 uint32_t textByteLength, const char* utf8Text) { |
| 977 NOT_IMPLEMENTED(srcPaint.getMaskFilter() != nullptr, false); | 983 NOT_IMPLEMENTED(srcPaint.getMaskFilter() != nullptr, false); |
| 978 if (srcPaint.getMaskFilter() != nullptr) { | 984 if (srcPaint.getMaskFilter() != nullptr) { |
| 979 // Don't pretend we support drawing MaskFilters, it makes for artifacts | 985 // Don't pretend we support drawing MaskFilters, it makes for artifacts |
| 980 // making text unreadable (e.g. same text twice when using CSS shadows). | 986 // making text unreadable (e.g. same text twice when using CSS shadows). |
| 981 return; | 987 return; |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1060 } | 1066 } |
| 1061 offset.offset(alignmentFactor * advance, 0); | 1067 offset.offset(alignmentFactor * advance, 0); |
| 1062 } | 1068 } |
| 1063 ScopedContentEntry content(this, d, paint, true); | 1069 ScopedContentEntry content(this, d, paint, true); |
| 1064 if (!content.entry()) { | 1070 if (!content.entry()) { |
| 1065 return; | 1071 return; |
| 1066 } | 1072 } |
| 1067 SkDynamicMemoryWStream* out = &content.entry()->fContent; | 1073 SkDynamicMemoryWStream* out = &content.entry()->fContent; |
| 1068 SkScalar textSize = paint.getTextSize(); | 1074 SkScalar textSize = paint.getTextSize(); |
| 1069 | 1075 |
| 1070 int index = 0; | |
| 1071 while (glyphs[index] > maxGlyphID) { // Invalid glyphID for this font. | |
| 1072 ++index; // Skip this glyphID | |
| 1073 if (index == glyphCount) { | |
| 1074 return; // all glyphIDs were bad. | |
| 1075 } | |
| 1076 } | |
| 1077 | |
| 1078 out->writeText("BT\n"); | 1076 out->writeText("BT\n"); |
| 1079 SK_AT_SCOPE_EXIT(out->writeText("ET\n")); | 1077 SK_AT_SCOPE_EXIT(out->writeText("ET\n")); |
| 1080 | 1078 |
| 1081 SkPDFFont* font = this->updateFont( | 1079 bool multiByteGlyphs = SkPDFFont::IsMultiByte(SkPDFFont::FontType(*metrics))
; |
| 1082 typeface, textSize, glyphs[index], content.entry()); | |
| 1083 SkASSERT(font); // All preconditions for SkPDFFont::GetFontResource are met
. | |
| 1084 if (!font) { return; } | |
| 1085 | |
| 1086 GlyphPositioner glyphPositioner(out, | 1080 GlyphPositioner glyphPositioner(out, |
| 1087 paint.getTextSkewX(), | 1081 paint.getTextSkewX(), |
| 1088 font->multiByteGlyphs(), | 1082 multiByteGlyphs, |
| 1089 defaultPositioning, | 1083 defaultPositioning, |
| 1090 offset); | 1084 offset); |
| 1091 | 1085 SkPDFFont* font = nullptr; |
| 1092 while (index < glyphCount) { | 1086 for (int index = 0; index < glyphCount; ++index) { |
| 1093 int stretch = font->countStretch(&glyphs[index], glyphCount - index, max
GlyphID); | 1087 SkGlyphID gid = glyphs[index]; |
| 1094 SkASSERT(index + stretch <= glyphCount); | 1088 if (gid > maxGlyphID) { |
| 1095 if (stretch < 1) { | 1089 continue; // Skip this invalid glyphID. |
| 1096 // The current pdf font cannot encode the next glyph. | 1090 } |
| 1097 // Try to get a pdf font which can encode the next glyph. | 1091 if (!font || !font->hasGlyph(gid)) { |
| 1092 // Either this is the first loop iteration or the current |
| 1093 // PDFFont cannot encode this glyph. |
| 1098 glyphPositioner.flush(); | 1094 glyphPositioner.flush(); |
| 1099 // first, validate the next glyph | 1095 // Try to get a font which can encode the glyph. |
| 1100 while (glyphs[index] > maxGlyphID) { | 1096 int fontIndex = this->getFontResourceIndex(typeface, gid); |
| 1101 ++index; // Skip this glyphID | 1097 SkASSERT(fontIndex >= 0); |
| 1102 if (index == glyphCount) { | 1098 if (fontIndex < 0) { return; } |
| 1103 return; // all remainng glyphIDs were bad. | 1099 update_font(out, fontIndex, textSize); |
| 1104 } | 1100 font = fFontResources[fontIndex]; |
| 1105 } | 1101 SkASSERT(font); // All preconditions for SkPDFFont::GetFontResource
are met. |
| 1106 SkASSERT(index < glyphCount); | |
| 1107 font = this->updateFont(typeface, textSize, glyphs[index], content.e
ntry()); | |
| 1108 SkASSERT(font); // preconditions for SkPDFFont::GetFontResource met
. | |
| 1109 if (!font) { return; } | 1102 if (!font) { return; } |
| 1110 glyphPositioner.setWideChars(font->multiByteGlyphs()); | 1103 SkASSERT(font->multiByteGlyphs() == multiByteGlyphs); |
| 1111 // Get stretch for this new font. | 1104 } |
| 1112 stretch = font->countStretch(&glyphs[index], glyphCount - index, max
GlyphID); | 1105 font->noteGlyphUsage(gid); |
| 1113 if (stretch < 1) { | 1106 SkScalar advance{0.0f}; |
| 1114 SkDEBUGFAIL("PDF could not encode glyph."); | 1107 SkPoint xy{0.0f, 0.0f}; |
| 1115 return; | 1108 if (!defaultPositioning) { |
| 1109 advance = glyphCache->getGlyphIDAdvance(gid).fAdvanceX; |
| 1110 xy = SkTextBlob::kFull_Positioning == positioning |
| 1111 ? SkPoint{pos[2 * index], pos[2 * index + 1]} |
| 1112 : SkPoint{pos[index], 0}; |
| 1113 if (alignment != SkPaint::kLeft_Align) { |
| 1114 xy.offset(alignmentFactor * advance, 0); |
| 1116 } | 1115 } |
| 1117 } | 1116 } |
| 1118 while (stretch-- > 0) { | 1117 SkGlyphID encodedGlyph = |
| 1119 SkGlyphID gid = glyphs[index]; | 1118 multiByteGlyphs ? gid : font->glyphToPDFFontEncoding(gid); |
| 1120 if (gid <= maxGlyphID) { | 1119 glyphPositioner.writeGlyph(xy, advance, encodedGlyph); |
| 1121 font->noteGlyphUsage(gid); | |
| 1122 SkGlyphID encodedGlyph = font->glyphToPDFFontEncoding(gid); | |
| 1123 if (defaultPositioning) { | |
| 1124 glyphPositioner.writeGlyph(SkPoint{0, 0}, 0, encodedGlyph); | |
| 1125 } else { | |
| 1126 SkScalar advance = glyphCache->getGlyphIDAdvance(gid).fAdvan
ceX; | |
| 1127 SkPoint xy = SkTextBlob::kFull_Positioning == positioning | |
| 1128 ? SkPoint{pos[2 * index], pos[2 * index + 1]} | |
| 1129 : SkPoint{pos[index], 0}; | |
| 1130 if (alignment != SkPaint::kLeft_Align) { | |
| 1131 xy.offset(alignmentFactor * advance, 0); | |
| 1132 } | |
| 1133 glyphPositioner.writeGlyph(xy, advance, encodedGlyph); | |
| 1134 } | |
| 1135 } | |
| 1136 ++index; | |
| 1137 } | |
| 1138 } | 1120 } |
| 1139 } | 1121 } |
| 1140 | 1122 |
| 1141 void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, | 1123 void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, |
| 1142 SkScalar x, SkScalar y, const SkPaint& paint) { | 1124 SkScalar x, SkScalar y, const SkPaint& paint) { |
| 1143 this->internalDrawText(d, text, len, nullptr, SkTextBlob::kDefault_Positioni
ng, | 1125 this->internalDrawText(d, text, len, nullptr, SkTextBlob::kDefault_Positioni
ng, |
| 1144 SkPoint{x, y}, paint, nullptr, 0, nullptr); | 1126 SkPoint{x, y}, paint, nullptr, 0, nullptr); |
| 1145 } | 1127 } |
| 1146 | 1128 |
| 1147 void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len, | 1129 void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len, |
| (...skipping 682 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1830 // Assumes that xobject has been canonicalized (so we can directly compare | 1812 // Assumes that xobject has been canonicalized (so we can directly compare |
| 1831 // pointers). | 1813 // pointers). |
| 1832 int result = fXObjectResources.find(xObject); | 1814 int result = fXObjectResources.find(xObject); |
| 1833 if (result < 0) { | 1815 if (result < 0) { |
| 1834 result = fXObjectResources.count(); | 1816 result = fXObjectResources.count(); |
| 1835 fXObjectResources.push(SkRef(xObject)); | 1817 fXObjectResources.push(SkRef(xObject)); |
| 1836 } | 1818 } |
| 1837 return result; | 1819 return result; |
| 1838 } | 1820 } |
| 1839 | 1821 |
| 1840 SkPDFFont* SkPDFDevice::updateFont(SkTypeface* typeface, | |
| 1841 SkScalar textSize, | |
| 1842 uint16_t glyphID, | |
| 1843 SkPDFDevice::ContentEntry* contentEntry) { | |
| 1844 if (contentEntry->fState.fFont == nullptr || | |
| 1845 contentEntry->fState.fTextSize != textSize || | |
| 1846 !contentEntry->fState.fFont->hasGlyph(glyphID)) { | |
| 1847 int fontIndex = getFontResourceIndex(typeface, glyphID); | |
| 1848 if (fontIndex < 0) { | |
| 1849 SkDebugf("SkPDF: Font error."); | |
| 1850 return nullptr; | |
| 1851 } | |
| 1852 contentEntry->fContent.writeText("/"); | |
| 1853 contentEntry->fContent.writeText(SkPDFResourceDict::getResourceName( | |
| 1854 SkPDFResourceDict::kFont_ResourceType, | |
| 1855 fontIndex).c_str()); | |
| 1856 contentEntry->fContent.writeText(" "); | |
| 1857 SkPDFUtils::AppendScalar(textSize, &contentEntry->fContent); | |
| 1858 contentEntry->fContent.writeText(" Tf\n"); | |
| 1859 contentEntry->fState.fFont = fFontResources[fontIndex]; | |
| 1860 } | |
| 1861 return contentEntry->fState.fFont; | |
| 1862 } | |
| 1863 | |
| 1864 int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) { | 1822 int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) { |
| 1865 sk_sp<SkPDFFont> newFont( | 1823 sk_sp<SkPDFFont> newFont( |
| 1866 SkPDFFont::GetFontResource(fDocument->canon(), typeface, glyphID)); | 1824 SkPDFFont::GetFontResource(fDocument->canon(), typeface, glyphID)); |
| 1867 if (!newFont) { | 1825 if (!newFont) { |
| 1868 return -1; | 1826 return -1; |
| 1869 } | 1827 } |
| 1870 int resourceIndex = fFontResources.find(newFont.get()); | 1828 int resourceIndex = fFontResources.find(newFont.get()); |
| 1871 if (resourceIndex < 0) { | 1829 if (resourceIndex < 0) { |
| 1872 fDocument->registerFont(newFont.get()); | 1830 fDocument->registerFont(newFont.get()); |
| 1873 resourceIndex = fFontResources.count(); | 1831 resourceIndex = fFontResources.count(); |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2081 } | 2039 } |
| 2082 | 2040 |
| 2083 sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkImage* image) { | 2041 sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkImage* image) { |
| 2084 return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(image->width(), image->
height()), | 2042 return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(image->width(), image->
height()), |
| 2085 image->makeNonTextureImage()); | 2043 image->makeNonTextureImage()); |
| 2086 } | 2044 } |
| 2087 | 2045 |
| 2088 sk_sp<SkSpecialImage> SkPDFDevice::snapSpecial() { | 2046 sk_sp<SkSpecialImage> SkPDFDevice::snapSpecial() { |
| 2089 return nullptr; | 2047 return nullptr; |
| 2090 } | 2048 } |
| OLD | NEW |