Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: src/pdf/SkPDFDevice.cpp

Issue 2322403002: SkPDF: Implement /ActualText to make text extraction correct. (Closed)
Patch Set: comments from bungeman@ Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | src/pdf/SkPDFMakeToUnicodeCmap.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | src/pdf/SkPDFMakeToUnicodeCmap.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698