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 "SkAdvancedTypefaceMetrics.h" | |
11 #include "SkAnnotationKeys.h" | 10 #include "SkAnnotationKeys.h" |
12 #include "SkBitmapDevice.h" | 11 #include "SkBitmapDevice.h" |
13 #include "SkBitmapKey.h" | 12 #include "SkBitmapKey.h" |
14 #include "SkColor.h" | 13 #include "SkColor.h" |
15 #include "SkColorFilter.h" | 14 #include "SkColorFilter.h" |
16 #include "SkDraw.h" | 15 #include "SkDraw.h" |
17 #include "SkDrawFilter.h" | 16 #include "SkDrawFilter.h" |
18 #include "SkGlyphCache.h" | 17 #include "SkGlyphCache.h" |
19 #include "SkMakeUnique.h" | 18 #include "SkMakeUnique.h" |
20 #include "SkPath.h" | 19 #include "SkPath.h" |
(...skipping 10 matching lines...) Expand all Loading... |
31 #include "SkPDFTypes.h" | 30 #include "SkPDFTypes.h" |
32 #include "SkPDFUtils.h" | 31 #include "SkPDFUtils.h" |
33 #include "SkRasterClip.h" | 32 #include "SkRasterClip.h" |
34 #include "SkRRect.h" | 33 #include "SkRRect.h" |
35 #include "SkScopeExit.h" | 34 #include "SkScopeExit.h" |
36 #include "SkString.h" | 35 #include "SkString.h" |
37 #include "SkSurface.h" | 36 #include "SkSurface.h" |
38 #include "SkTemplates.h" | 37 #include "SkTemplates.h" |
39 #include "SkTextBlobRunIterator.h" | 38 #include "SkTextBlobRunIterator.h" |
40 #include "SkTextFormatParams.h" | 39 #include "SkTextFormatParams.h" |
41 #include "SkUtils.h" | |
42 #include "SkXfermodeInterpretation.h" | 40 #include "SkXfermodeInterpretation.h" |
43 | 41 |
44 #define DPI_FOR_RASTER_SCALE_ONE 72 | 42 #define DPI_FOR_RASTER_SCALE_ONE 72 |
45 | 43 |
46 // Utility functions | 44 // Utility functions |
47 | 45 |
48 // If the paint will definitely draw opaquely, replace kSrc_Mode with | 46 // If the paint will definitely draw opaquely, replace kSrc_Mode with |
49 // kSrcOver_Mode. http://crbug.com/473572 | 47 // kSrcOver_Mode. http://crbug.com/473572 |
50 static void replace_srcmode_on_opaque_paint(SkPaint* paint) { | 48 static void replace_srcmode_on_opaque_paint(SkPaint* paint) { |
51 if (kSrcOver_SkXfermodeInterpretation | 49 if (kSrcOver_SkXfermodeInterpretation |
(...skipping 865 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
917 private: | 915 private: |
918 SkDynamicMemoryWStream* fContent; | 916 SkDynamicMemoryWStream* fContent; |
919 SkPoint fCurrentMatrixOrigin; | 917 SkPoint fCurrentMatrixOrigin; |
920 SkScalar fXAdvance = 0.0f; | 918 SkScalar fXAdvance = 0.0f; |
921 SkScalar fTextSkewX; | 919 SkScalar fTextSkewX; |
922 bool fWideChars; | 920 bool fWideChars; |
923 bool fInText = false; | 921 bool fInText = false; |
924 bool fInitialized = false; | 922 bool fInitialized = false; |
925 const bool fDefaultPositioning; | 923 const bool fDefaultPositioning; |
926 }; | 924 }; |
927 | |
928 /** Given the m-to-n glyph-to-character mapping data (as returned by | |
929 harfbuzz), iterate over the clusters. */ | |
930 class Clusterator { | |
931 public: | |
932 Clusterator() : fClusters(nullptr), fUtf8Text(nullptr), fGlyphCount(0), fTex
tByteLength(0) {} | |
933 explicit Clusterator(uint32_t glyphCount) | |
934 : fClusters(nullptr) | |
935 , fUtf8Text(nullptr) | |
936 , fGlyphCount(glyphCount) | |
937 , fTextByteLength(0) {} | |
938 // The clusters[] array is an array of offsets into utf8Text[], | |
939 // one offset for each glyph. See SkTextBlobBuilder for more info. | |
940 Clusterator(const uint32_t* clusters, | |
941 const char* utf8Text, | |
942 uint32_t glyphCount, | |
943 uint32_t textByteLength) | |
944 : fClusters(clusters) | |
945 , fUtf8Text(utf8Text) | |
946 , fGlyphCount(glyphCount) | |
947 , fTextByteLength(textByteLength) { | |
948 // This is a cheap heuristic for /ReversedChars which seems to | |
949 // work for clusters produced by HarfBuzz, which either | |
950 // increase from zero (LTR) or decrease to zero (RTL). | |
951 // "ReversedChars" is how PDF deals with RTL text. | |
952 fReversedChars = | |
953 fUtf8Text && fClusters && fGlyphCount && fClusters[0] != 0; | |
954 } | |
955 struct Cluster { | |
956 const char* fUtf8Text; | |
957 uint32_t fTextByteLength; | |
958 uint32_t fGlyphIndex; | |
959 uint32_t fGlyphCount; | |
960 explicit operator bool() const { return fGlyphCount != 0; } | |
961 }; | |
962 // True if this looks like right-to-left text. | |
963 bool reversedChars() const { return fReversedChars; } | |
964 Cluster next() { | |
965 if ((!fUtf8Text || !fClusters) && fGlyphCount) { | |
966 // These glyphs have no text. Treat as one "cluster". | |
967 uint32_t glyphCount = fGlyphCount; | |
968 fGlyphCount = 0; | |
969 return Cluster{nullptr, 0, 0, glyphCount}; | |
970 } | |
971 if (fGlyphCount == 0 || fTextByteLength == 0) { | |
972 return Cluster{nullptr, 0, 0, 0}; // empty | |
973 } | |
974 uint32_t cluster = fClusters[0]; | |
975 if (cluster >= fTextByteLength) { | |
976 return Cluster{nullptr, 0, 0, 0}; // bad input. | |
977 } | |
978 uint32_t glyphsInCluster = 1; | |
979 while (fClusters[glyphsInCluster] == cluster && | |
980 glyphsInCluster < fGlyphCount) { | |
981 ++glyphsInCluster; | |
982 } | |
983 SkASSERT(glyphsInCluster <= fGlyphCount); | |
984 uint32_t textLength = 0; | |
985 if (glyphsInCluster == fGlyphCount) { | |
986 // consumes rest of glyphs and rest of text | |
987 if (kInvalidCluster == fPreviousCluster) { // LTR text or single clu
ster | |
988 textLength = fTextByteLength - cluster; | |
989 } else { // RTL text; last cluster. | |
990 SkASSERT(fPreviousCluster < fTextByteLength); | |
991 if (fPreviousCluster <= cluster) { // bad input. | |
992 return Cluster{nullptr, 0, 0, 0}; | |
993 } | |
994 textLength = fPreviousCluster - cluster; | |
995 } | |
996 fGlyphCount = 0; | |
997 return Cluster{fUtf8Text + cluster, | |
998 textLength, | |
999 fGlyphIndex, | |
1000 glyphsInCluster}; | |
1001 } | |
1002 uint32_t nextCluster = fClusters[glyphsInCluster]; | |
1003 if (nextCluster >= fTextByteLength) { | |
1004 return Cluster{nullptr, 0, 0, 0}; // bad input. | |
1005 } | |
1006 if (nextCluster > cluster) { // LTR text | |
1007 if (kInvalidCluster != fPreviousCluster) { | |
1008 return Cluster{nullptr, 0, 0, 0}; // bad input. | |
1009 } | |
1010 textLength = nextCluster - cluster; | |
1011 } else { // RTL text | |
1012 SkASSERT(nextCluster < cluster); | |
1013 if (kInvalidCluster == fPreviousCluster) { // first cluster | |
1014 textLength = fTextByteLength - cluster; | |
1015 } else { // later cluster | |
1016 if (fPreviousCluster <= cluster) { | |
1017 return Cluster{nullptr, 0, 0, 0}; // bad input. | |
1018 } | |
1019 textLength = fPreviousCluster - cluster; | |
1020 } | |
1021 fPreviousCluster = cluster; | |
1022 } | |
1023 uint32_t glyphIndex = fGlyphIndex; | |
1024 fGlyphCount -= glyphsInCluster; | |
1025 fGlyphIndex += glyphsInCluster; | |
1026 fClusters += glyphsInCluster; | |
1027 return Cluster{fUtf8Text + cluster, | |
1028 textLength, | |
1029 glyphIndex, | |
1030 glyphsInCluster}; | |
1031 } | |
1032 | |
1033 private: | |
1034 static constexpr uint32_t kInvalidCluster = 0xFFFFFFFF; | |
1035 const uint32_t* fClusters; | |
1036 const char* fUtf8Text; | |
1037 uint32_t fGlyphCount; | |
1038 uint32_t fTextByteLength; | |
1039 uint32_t fGlyphIndex = 0; | |
1040 uint32_t fPreviousCluster = kInvalidCluster; | |
1041 bool fReversedChars = false; | |
1042 }; | |
1043 | |
1044 struct TextStorage { | |
1045 SkAutoTMalloc<char> fUtf8textStorage; | |
1046 SkAutoTMalloc<uint32_t> fClusterStorage; | |
1047 SkAutoTMalloc<SkGlyphID> fGlyphStorage; | |
1048 }; | |
1049 } // namespace | 925 } // namespace |
1050 | 926 |
1051 /** Given some unicode text (as passed to drawText(), convert to | |
1052 glyphs (via primitive shaping), while preserving | |
1053 glyph-to-character mapping information. */ | |
1054 static Clusterator make_clusterator( | |
1055 const void* sourceText, | |
1056 size_t sourceByteCount, | |
1057 const SkPaint& paint, | |
1058 TextStorage* storage, | |
1059 int glyphCount) { | |
1060 SkASSERT(SkPaint::kGlyphID_TextEncoding != paint.getTextEncoding()); | |
1061 SkASSERT(glyphCount == paint.textToGlyphs(sourceText, sourceByteCount, nullp
tr)); | |
1062 SkASSERT(glyphCount > 0); | |
1063 storage->fGlyphStorage.reset(SkToSizeT(glyphCount)); | |
1064 (void)paint.textToGlyphs(sourceText, sourceByteCount, storage->fGlyphStorage
.get()); | |
1065 storage->fClusterStorage.reset(SkToSizeT(glyphCount)); | |
1066 uint32_t* clusters = storage->fClusterStorage.get(); | |
1067 uint32_t utf8ByteCount = 0; | |
1068 const char* utf8Text = nullptr; | |
1069 switch (paint.getTextEncoding()) { | |
1070 case SkPaint::kUTF8_TextEncoding: { | |
1071 const char* txtPtr = (const char*)sourceText; | |
1072 for (int i = 0; i < glyphCount; ++i) { | |
1073 clusters[i] = SkToU32(txtPtr - (const char*)sourceText); | |
1074 txtPtr += SkUTF8_LeadByteToCount(*(const unsigned char*)txtPtr); | |
1075 SkASSERT(txtPtr <= (const char*)sourceText + sourceByteCount); | |
1076 } | |
1077 utf8ByteCount = SkToU32(sourceByteCount); | |
1078 utf8Text = (const char*)sourceText; | |
1079 break; | |
1080 } | |
1081 case SkPaint::kUTF16_TextEncoding: { | |
1082 const uint16_t* utf16ptr = (const uint16_t*)sourceText; | |
1083 int utf16count = SkToInt(sourceByteCount / sizeof(uint16_t)); | |
1084 utf8ByteCount = SkToU32(SkUTF16_ToUTF8(utf16ptr, utf16count)); | |
1085 storage->fUtf8textStorage.reset(utf8ByteCount); | |
1086 char* txtPtr = storage->fUtf8textStorage.get(); | |
1087 utf8Text = txtPtr; | |
1088 int clusterIndex = 0; | |
1089 while (utf16ptr < (const uint16_t*)sourceText + sourceByteCount) { | |
1090 clusters[clusterIndex++] = SkToU32(txtPtr - utf8Text); | |
1091 SkUnichar uni = SkUTF16_NextUnichar(&utf16ptr); | |
1092 txtPtr += SkUTF8_FromUnichar(uni, txtPtr); | |
1093 } | |
1094 SkASSERT(clusterIndex == glyphCount); | |
1095 SkASSERT(txtPtr == storage->fUtf8textStorage.get() + utf8ByteCount); | |
1096 SkASSERT(utf16ptr == (const uint16_t*)sourceText + sourceByteCount); | |
1097 break; | |
1098 } | |
1099 case SkPaint::kUTF32_TextEncoding: { | |
1100 const SkUnichar* utf32 = (const SkUnichar*)sourceText; | |
1101 for (size_t i = 0; i < sourceByteCount / sizeof(SkUnichar); ++i) { | |
1102 utf8ByteCount += SkToU32(SkUTF8_FromUnichar(utf32[i])); | |
1103 } | |
1104 storage->fUtf8textStorage.reset(SkToSizeT(utf8ByteCount)); | |
1105 char* txtPtr = storage->fUtf8textStorage.get(); | |
1106 utf8Text = txtPtr; | |
1107 for (size_t i = 0; i < sourceByteCount / sizeof(SkUnichar); ++i) { | |
1108 clusters[i] = SkToU32(txtPtr - utf8Text); | |
1109 txtPtr += SkUTF8_FromUnichar(utf32[i], txtPtr); | |
1110 } | |
1111 break; | |
1112 } | |
1113 default: | |
1114 SkDEBUGFAIL(""); | |
1115 break; | |
1116 } | |
1117 return Clusterator(clusters, utf8Text, SkToU32(glyphCount), utf8ByteCount); | |
1118 } | |
1119 | |
1120 static void draw_transparent_text(SkPDFDevice* device, | 927 static void draw_transparent_text(SkPDFDevice* device, |
1121 const SkDraw& d, | 928 const SkDraw& d, |
1122 const void* text, size_t len, | 929 const void* text, size_t len, |
1123 SkScalar x, SkScalar y, | 930 SkScalar x, SkScalar y, |
1124 const SkPaint& srcPaint) { | 931 const SkPaint& srcPaint) { |
1125 sk_sp<SkTypeface> defaultFace = SkTypeface::MakeDefault(); | 932 sk_sp<SkTypeface> defaultFace = SkTypeface::MakeDefault(); |
1126 if (!SkPDFFont::CanEmbedTypeface(defaultFace.get(), device->getCanon())) { | 933 if (!SkPDFFont::CanEmbedTypeface(defaultFace.get(), device->getCanon())) { |
1127 SkDebugf("SkPDF: default typeface should be embeddable"); | 934 SkDebugf("SkPDF: default typeface should be embeddable"); |
1128 return; // Avoid infinite loop in release. | 935 return; // Avoid infinite loop in release. |
1129 } | 936 } |
(...skipping 21 matching lines...) Expand all Loading... |
1151 case SkPaint::kUTF16_TextEncoding: | 958 case SkPaint::kUTF16_TextEncoding: |
1152 case SkPaint::kUTF32_TextEncoding: | 959 case SkPaint::kUTF32_TextEncoding: |
1153 transparent.setTextEncoding(srcPaint.getTextEncoding()); | 960 transparent.setTextEncoding(srcPaint.getTextEncoding()); |
1154 device->drawText(d, text, len, x, y, transparent); | 961 device->drawText(d, text, len, x, y, transparent); |
1155 break; | 962 break; |
1156 default: | 963 default: |
1157 SkFAIL("unknown text encoding"); | 964 SkFAIL("unknown text encoding"); |
1158 } | 965 } |
1159 } | 966 } |
1160 | 967 |
1161 static SkUnichar map_glyph(const SkTDArray<SkUnichar>& glyphToUnicode, SkGlyphID
glyph) { | |
1162 return SkToInt(glyph) < glyphToUnicode.count() ? glyphToUnicode[SkToInt(glyp
h)] : -1; | |
1163 } | |
1164 | |
1165 static void update_font(SkWStream* wStream, int fontIndex, SkScalar textSize) { | 968 static void update_font(SkWStream* wStream, int fontIndex, SkScalar textSize) { |
1166 wStream->writeText("/"); | 969 wStream->writeText("/"); |
1167 char prefix = SkPDFResourceDict::GetResourceTypePrefix(SkPDFResourceDict::kF
ont_ResourceType); | 970 char prefix = SkPDFResourceDict::GetResourceTypePrefix(SkPDFResourceDict::kF
ont_ResourceType); |
1168 wStream->write(&prefix, 1); | 971 wStream->write(&prefix, 1); |
1169 wStream->writeDecAsText(fontIndex); | 972 wStream->writeDecAsText(fontIndex); |
1170 wStream->writeText(" "); | 973 wStream->writeText(" "); |
1171 SkPDFUtils::AppendScalar(textSize, wStream); | 974 SkPDFUtils::AppendScalar(textSize, wStream); |
1172 wStream->writeText(" Tf\n"); | 975 wStream->writeText(" Tf\n"); |
1173 } | 976 } |
1174 | 977 |
1175 void SkPDFDevice::internalDrawText( | 978 void SkPDFDevice::internalDrawText( |
1176 const SkDraw& d, const void* sourceText, size_t sourceByteCount, | 979 const SkDraw& d, const void* sourceText, size_t sourceByteCount, |
1177 const SkScalar pos[], SkTextBlob::GlyphPositioning positioning, | 980 const SkScalar pos[], SkTextBlob::GlyphPositioning positioning, |
1178 SkPoint offset, const SkPaint& srcPaint, const uint32_t* clusters, | 981 SkPoint offset, const SkPaint& srcPaint, const uint32_t* clusters, |
1179 uint32_t textByteLength, const char* utf8Text) { | 982 uint32_t textByteLength, const char* utf8Text) { |
1180 NOT_IMPLEMENTED(srcPaint.getMaskFilter() != nullptr, false); | 983 NOT_IMPLEMENTED(srcPaint.getMaskFilter() != nullptr, false); |
1181 if (srcPaint.getMaskFilter() != nullptr) { | 984 if (srcPaint.getMaskFilter() != nullptr) { |
1182 // Don't pretend we support drawing MaskFilters, it makes for artifacts | 985 // Don't pretend we support drawing MaskFilters, it makes for artifacts |
1183 // 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). |
1184 return; | 987 return; |
1185 } | 988 } |
1186 NOT_IMPLEMENTED(srcPaint.isVerticalText(), false); | 989 NOT_IMPLEMENTED(srcPaint.isVerticalText(), false); |
1187 if (srcPaint.isVerticalText()) { | 990 if (srcPaint.isVerticalText()) { |
1188 // Don't pretend we support drawing vertical text. It is not | 991 // Don't pretend we support drawing vertical text. It is not |
1189 // clear to me how to switch to "vertical writing" mode in PDF. | 992 // clear to me how to switch to "vertical writing" mode in PDF. |
1190 // Currently neither Chromium or Android set this flag. | 993 // Currently neither Chromium or Android set this flag. |
1191 // https://bug.skia.org/5665 | 994 // https://bug.skia.org/5665 |
1192 return; | 995 return; |
1193 } | 996 } |
1194 if (0 == sourceByteCount || !sourceText) { | 997 // TODO(halcanary): implement /ActualText with these values. |
1195 return; | 998 (void)clusters; |
| 999 (void)textByteLength; |
| 1000 (void)utf8Text; |
| 1001 if (textByteLength > 0) { |
| 1002 SkASSERT(clusters); |
| 1003 SkASSERT(utf8Text); |
| 1004 SkASSERT(srcPaint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding); |
| 1005 } else { |
| 1006 SkASSERT(nullptr == clusters); |
| 1007 SkASSERT(nullptr == utf8Text); |
1196 } | 1008 } |
| 1009 |
1197 SkPaint paint = calculate_text_paint(srcPaint); | 1010 SkPaint paint = calculate_text_paint(srcPaint); |
1198 replace_srcmode_on_opaque_paint(&paint); | 1011 replace_srcmode_on_opaque_paint(&paint); |
1199 if (!paint.getTypeface()) { | 1012 if (!paint.getTypeface()) { |
1200 paint.setTypeface(SkTypeface::MakeDefault()); | 1013 paint.setTypeface(SkTypeface::MakeDefault()); |
1201 } | 1014 } |
1202 SkTypeface* typeface = paint.getTypeface(); | 1015 SkTypeface* typeface = paint.getTypeface(); |
1203 if (!typeface) { | 1016 if (!typeface) { |
1204 SkDebugf("SkPDF: SkTypeface::MakeDefault() returned nullptr.\n"); | 1017 SkDebugf("SkPDF: SkTypeface::MakeDefault() returned nullptr.\n"); |
1205 return; | 1018 return; |
1206 } | 1019 } |
1207 | 1020 |
1208 const SkAdvancedTypefaceMetrics* metrics = | 1021 const SkAdvancedTypefaceMetrics* metrics = |
1209 SkPDFFont::GetMetrics(typeface, fDocument->canon()); | 1022 SkPDFFont::GetMetrics(typeface, fDocument->canon()); |
1210 if (!metrics) { | 1023 if (!metrics) { |
1211 return; | 1024 return; |
1212 } | 1025 } |
1213 int glyphCount = paint.textToGlyphs(sourceText, sourceByteCount, nullptr); | 1026 int glyphCount = paint.textToGlyphs(sourceText, sourceByteCount, nullptr); |
1214 if (glyphCount <= 0) { | 1027 if (glyphCount <= 0) { |
1215 return; | 1028 return; |
1216 } | 1029 } |
1217 // TODO(halcanary): use metrics->fGlyphToUnicode to check Unicode mapping. | 1030 // TODO(halcanary): use metrics->fGlyphToUnicode to check Unicode mapping. |
| 1031 const SkGlyphID maxGlyphID = metrics->fLastGlyphID; |
1218 if (!SkPDFFont::CanEmbedTypeface(typeface, fDocument->canon())) { | 1032 if (!SkPDFFont::CanEmbedTypeface(typeface, fDocument->canon())) { |
1219 SkPath path; // https://bug.skia.org/3866 | 1033 SkPath path; // https://bug.skia.org/3866 |
1220 switch (positioning) { | 1034 switch (positioning) { |
1221 case SkTextBlob::kDefault_Positioning: | 1035 case SkTextBlob::kDefault_Positioning: |
1222 srcPaint.getTextPath(sourceText, sourceByteCount, | 1036 srcPaint.getTextPath(sourceText, sourceByteCount, |
1223 offset.x(), offset.y(), &path); | 1037 offset.x(), offset.y(), &path); |
1224 break; | 1038 break; |
1225 case SkTextBlob::kHorizontal_Positioning: { | 1039 case SkTextBlob::kHorizontal_Positioning: { |
1226 SkAutoTMalloc<SkPoint> positionsBuffer(glyphCount); | 1040 SkAutoTMalloc<SkPoint> positionsBuffer(glyphCount); |
1227 for (int i = 0; i < glyphCount; ++i) { | 1041 for (int i = 0; i < glyphCount; ++i) { |
(...skipping 12 matching lines...) Expand all Loading... |
1240 &positionsBuffer[0], &path); | 1054 &positionsBuffer[0], &path); |
1241 break; | 1055 break; |
1242 } | 1056 } |
1243 } | 1057 } |
1244 this->drawPath(d, path, srcPaint, &SkMatrix::I(), true); | 1058 this->drawPath(d, path, srcPaint, &SkMatrix::I(), true); |
1245 // Draw text transparently to make it copyable/searchable/accessable. | 1059 // Draw text transparently to make it copyable/searchable/accessable. |
1246 draw_transparent_text(this, d, sourceText, sourceByteCount, | 1060 draw_transparent_text(this, d, sourceText, sourceByteCount, |
1247 offset.x(), offset.y(), paint); | 1061 offset.x(), offset.y(), paint); |
1248 return; | 1062 return; |
1249 } | 1063 } |
| 1064 SkAutoSTMalloc<128, SkGlyphID> glyphStorage; |
| 1065 const SkGlyphID* glyphs = nullptr; |
| 1066 if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) { |
| 1067 glyphs = (const SkGlyphID*)sourceText; |
| 1068 // validate input later. |
| 1069 } else { |
| 1070 glyphStorage.reset(SkToSizeT(glyphCount)); |
| 1071 (void)paint.textToGlyphs(sourceText, sourceByteCount, glyphStorage.get()
); |
| 1072 glyphs = glyphStorage.get(); |
| 1073 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); |
| 1074 } |
1250 | 1075 |
1251 // These three heap buffers are only used in the case where no glyphs | |
1252 // are passed to drawText() (most clients pass glyphs or a textblob). | |
1253 TextStorage storage; | |
1254 const SkGlyphID* glyphs = nullptr; | |
1255 Clusterator clusterator; | |
1256 if (textByteLength > 0) { | |
1257 SkASSERT(glyphCount == SkToInt(sourceByteCount / sizeof(SkGlyphID))); | |
1258 glyphs = (const SkGlyphID*)sourceText; | |
1259 clusterator = Clusterator(clusters, utf8Text, SkToU32(glyphCount), textB
yteLength); | |
1260 SkASSERT(clusters); | |
1261 SkASSERT(utf8Text); | |
1262 SkASSERT(srcPaint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding); | |
1263 SkASSERT(glyphCount == paint.textToGlyphs(sourceText, sourceByteCount, n
ullptr)); | |
1264 } else if (SkPaint::kGlyphID_TextEncoding == srcPaint.getTextEncoding()) { | |
1265 SkASSERT(glyphCount == SkToInt(sourceByteCount / sizeof(SkGlyphID))); | |
1266 glyphs = (const SkGlyphID*)sourceText; | |
1267 clusterator = Clusterator(SkToU32(glyphCount)); | |
1268 SkASSERT(glyphCount == paint.textToGlyphs(sourceText, sourceByteCount, n
ullptr)); | |
1269 SkASSERT(nullptr == clusters); | |
1270 SkASSERT(nullptr == utf8Text); | |
1271 } else { | |
1272 SkASSERT(nullptr == clusters); | |
1273 SkASSERT(nullptr == utf8Text); | |
1274 clusterator = make_clusterator(sourceText, sourceByteCount, srcPaint, | |
1275 &storage, glyphCount); | |
1276 glyphs = storage.fGlyphStorage; | |
1277 } | |
1278 bool defaultPositioning = (positioning == SkTextBlob::kDefault_Positioning); | 1076 bool defaultPositioning = (positioning == SkTextBlob::kDefault_Positioning); |
1279 paint.setHinting(SkPaint::kNo_Hinting); | 1077 paint.setHinting(SkPaint::kNo_Hinting); |
1280 SkAutoGlyphCache glyphCache(paint, nullptr, nullptr); | 1078 SkAutoGlyphCache glyphCache(paint, nullptr, nullptr); |
1281 | 1079 |
1282 SkPaint::Align alignment = paint.getTextAlign(); | 1080 SkPaint::Align alignment = paint.getTextAlign(); |
1283 float alignmentFactor = SkPaint::kLeft_Align == alignment ? 0.0f : | 1081 float alignmentFactor = SkPaint::kLeft_Align == alignment ? 0.0f : |
1284 SkPaint::kCenter_Align == alignment ? -0.5f : | 1082 SkPaint::kCenter_Align == alignment ? -0.5f : |
1285 /* SkPaint::kRight_Align */ -1.0f; | 1083 /* SkPaint::kRight_Align */ -1.0f; |
1286 if (defaultPositioning && alignment != SkPaint::kLeft_Align) { | 1084 if (defaultPositioning && alignment != SkPaint::kLeft_Align) { |
1287 SkScalar advance = 0; | 1085 SkScalar advance = 0; |
1288 for (int i = 0; i < glyphCount; ++i) { | 1086 for (int i = 0; i < glyphCount; ++i) { |
1289 advance += glyphCache->getGlyphIDAdvance(glyphs[i]).fAdvanceX; | 1087 advance += glyphCache->getGlyphIDAdvance(glyphs[i]).fAdvanceX; |
1290 } | 1088 } |
1291 offset.offset(alignmentFactor * advance, 0); | 1089 offset.offset(alignmentFactor * advance, 0); |
1292 } | 1090 } |
1293 ScopedContentEntry content(this, d, paint, true); | 1091 ScopedContentEntry content(this, d, paint, true); |
1294 if (!content.entry()) { | 1092 if (!content.entry()) { |
1295 return; | 1093 return; |
1296 } | 1094 } |
1297 SkDynamicMemoryWStream* out = &content.entry()->fContent; | 1095 SkDynamicMemoryWStream* out = &content.entry()->fContent; |
1298 SkScalar textSize = paint.getTextSize(); | 1096 SkScalar textSize = paint.getTextSize(); |
1299 const SkTDArray<SkUnichar>& glyphToUnicode = metrics->fGlyphToUnicode; | |
1300 | 1097 |
1301 out->writeText("BT\n"); | 1098 out->writeText("BT\n"); |
1302 SK_AT_SCOPE_EXIT(out->writeText("ET\n")); | 1099 SK_AT_SCOPE_EXIT(out->writeText("ET\n")); |
1303 | 1100 |
1304 const SkGlyphID maxGlyphID = metrics->fLastGlyphID; | |
1305 bool multiByteGlyphs = SkPDFFont::IsMultiByte(SkPDFFont::FontType(*metrics))
; | 1101 bool multiByteGlyphs = SkPDFFont::IsMultiByte(SkPDFFont::FontType(*metrics))
; |
1306 if (clusterator.reversedChars()) { | |
1307 out->writeText("/ReversedChars BMC\n"); | |
1308 } | |
1309 SK_AT_SCOPE_EXIT(if (clusterator.reversedChars()) { out->writeText("EMC\n");
} ); | |
1310 GlyphPositioner glyphPositioner(out, | 1102 GlyphPositioner glyphPositioner(out, |
1311 paint.getTextSkewX(), | 1103 paint.getTextSkewX(), |
1312 multiByteGlyphs, | 1104 multiByteGlyphs, |
1313 defaultPositioning, | 1105 defaultPositioning, |
1314 offset); | 1106 offset); |
1315 SkPDFFont* font = nullptr; | 1107 SkPDFFont* font = nullptr; |
1316 | 1108 for (int index = 0; index < glyphCount; ++index) { |
1317 while (Clusterator::Cluster c = clusterator.next()) { | 1109 SkGlyphID gid = glyphs[index]; |
1318 int index = c.fGlyphIndex; | 1110 if (gid > maxGlyphID) { |
1319 int glyphLimit = index + c.fGlyphCount; | 1111 continue; // Skip this invalid glyphID. |
1320 | 1112 } |
1321 bool actualText = false; | 1113 if (!font || !font->hasGlyph(gid)) { |
1322 SK_AT_SCOPE_EXIT(if (actualText) { glyphPositioner.flush(); out->writeTe
xt("EMC\n"); } ); | 1114 // Either this is the first loop iteration or the current |
1323 if (c.fUtf8Text) { // real cluster | 1115 // PDFFont cannot encode this glyph. |
1324 // Check if `/ActualText` needed. | 1116 glyphPositioner.flush(); |
1325 const char* textPtr = c.fUtf8Text; | 1117 // Try to get a font which can encode the glyph. |
1326 // TODO(halcanary): validate utf8 input. | 1118 int fontIndex = this->getFontResourceIndex(typeface, gid); |
1327 SkUnichar unichar = SkUTF8_NextUnichar(&textPtr); | 1119 SkASSERT(fontIndex >= 0); |
1328 const char* textEnd = c.fUtf8Text + c.fTextByteLength; | 1120 if (fontIndex < 0) { return; } |
1329 if (textPtr < textEnd || // more ch
aracters left | 1121 update_font(out, fontIndex, textSize); |
1330 glyphLimit > index + 1 || // toUnico
de wouldn't work | 1122 font = fFontResources[fontIndex]; |
1331 unichar != map_glyph(glyphToUnicode, glyphs[index])) // test si
ngle Unichar map | 1123 SkASSERT(font); // All preconditions for SkPDFFont::GetFontResource
are met. |
1332 { | 1124 if (!font) { return; } |
1333 glyphPositioner.flush(); | 1125 SkASSERT(font->multiByteGlyphs() == multiByteGlyphs); |
1334 out->writeText("/Span<</ActualText <"); | 1126 } |
1335 SkPDFUtils::WriteUTF16beHex(out, 0xFEFF); // U+FEFF = BYTE ORDE
R MARK | 1127 font->noteGlyphUsage(gid); |
1336 // the BOM marks this text as UTF-16BE, not PDFDocEncoding. | 1128 SkScalar advance{0.0f}; |
1337 SkPDFUtils::WriteUTF16beHex(out, unichar); // first char | 1129 SkPoint xy{0.0f, 0.0f}; |
1338 while (textPtr < textEnd) { | 1130 if (!defaultPositioning) { |
1339 unichar = SkUTF8_NextUnichar(&textPtr); | 1131 advance = glyphCache->getGlyphIDAdvance(gid).fAdvanceX; |
1340 SkPDFUtils::WriteUTF16beHex(out, unichar); | 1132 xy = SkTextBlob::kFull_Positioning == positioning |
1341 } | 1133 ? SkPoint{pos[2 * index], pos[2 * index + 1]} |
1342 out->writeText("> >> BDC\n"); // begin marked-content sequence | 1134 : SkPoint{pos[index], 0}; |
1343 // with an associated property li
st. | 1135 if (alignment != SkPaint::kLeft_Align) { |
1344 actualText = true; | 1136 xy.offset(alignmentFactor * advance, 0); |
1345 } | 1137 } |
1346 } | 1138 } |
1347 for (; index < glyphLimit; ++index) { | 1139 SkGlyphID encodedGlyph = |
1348 SkGlyphID gid = glyphs[index]; | 1140 multiByteGlyphs ? gid : font->glyphToPDFFontEncoding(gid); |
1349 if (gid > maxGlyphID) { | 1141 glyphPositioner.writeGlyph(xy, advance, encodedGlyph); |
1350 continue; | |
1351 } | |
1352 if (!font || !font->hasGlyph(gid)) { | |
1353 // Not yet specified font or need to switch font. | |
1354 int fontIndex = this->getFontResourceIndex(typeface, gid); | |
1355 // All preconditions for SkPDFFont::GetFontResource are met. | |
1356 SkASSERT(fontIndex >= 0); | |
1357 if (fontIndex < 0) { | |
1358 return; | |
1359 } | |
1360 glyphPositioner.flush(); | |
1361 update_font(out, fontIndex, textSize); | |
1362 font = fFontResources[fontIndex]; | |
1363 SkASSERT(font); // All preconditions for SkPDFFont::GetFontReso
urce are met. | |
1364 if (!font) { | |
1365 return; | |
1366 } | |
1367 SkASSERT(font->multiByteGlyphs() == multiByteGlyphs); | |
1368 } | |
1369 SkPoint xy{0, 0}; | |
1370 SkScalar advance{0}; | |
1371 if (!defaultPositioning) { | |
1372 advance = glyphCache->getGlyphIDAdvance(gid).fAdvanceX; | |
1373 xy = SkTextBlob::kFull_Positioning == positioning | |
1374 ? SkPoint{pos[2 * index], pos[2 * index + 1]} | |
1375 : SkPoint{pos[index], 0}; | |
1376 if (alignment != SkPaint::kLeft_Align) { | |
1377 xy.offset(alignmentFactor * advance, 0); | |
1378 } | |
1379 } | |
1380 font->noteGlyphUsage(gid); | |
1381 SkGlyphID encodedGlyph = multiByteGlyphs ? gid : font->glyphToPDFFon
tEncoding(gid); | |
1382 glyphPositioner.writeGlyph(xy, advance, encodedGlyph); | |
1383 } | |
1384 } | 1142 } |
1385 } | 1143 } |
1386 | 1144 |
1387 void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, | 1145 void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, |
1388 SkScalar x, SkScalar y, const SkPaint& paint) { | 1146 SkScalar x, SkScalar y, const SkPaint& paint) { |
1389 this->internalDrawText(d, text, len, nullptr, SkTextBlob::kDefault_Positioni
ng, | 1147 this->internalDrawText(d, text, len, nullptr, SkTextBlob::kDefault_Positioni
ng, |
1390 SkPoint{x, y}, paint, nullptr, 0, nullptr); | 1148 SkPoint{x, y}, paint, nullptr, 0, nullptr); |
1391 } | 1149 } |
1392 | 1150 |
1393 void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len, | 1151 void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len, |
(...skipping 909 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2303 } | 2061 } |
2304 | 2062 |
2305 sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkImage* image) { | 2063 sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkImage* image) { |
2306 return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(image->width(), image->
height()), | 2064 return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(image->width(), image->
height()), |
2307 image->makeNonTextureImage()); | 2065 image->makeNonTextureImage()); |
2308 } | 2066 } |
2309 | 2067 |
2310 sk_sp<SkSpecialImage> SkPDFDevice::snapSpecial() { | 2068 sk_sp<SkSpecialImage> SkPDFDevice::snapSpecial() { |
2311 return nullptr; | 2069 return nullptr; |
2312 } | 2070 } |
OLD | NEW |