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