1 /*
2 * Copyright 2010 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkTypes.h"
9
10 #ifdef SK_SUPPORT_PDF
11
12 #include "include/core/SkStream.h"
13 #include "include/private/base/SkTDArray.h"
14 #include "include/private/base/SkTemplates.h"
15 #include "include/private/base/SkTo.h"
16 #include "src/pdf/SkPDFGlyphUse.h"
17 #include "src/pdf/SkPDFMakeToUnicodeCmap.h"
18 #include "tests/Test.h"
19
20 #include <algorithm>
21 #include <cstdint>
22 #include <cstring>
23
24 using namespace skia_private;
25
26 static constexpr SkGlyphID kMaximumGlyphIndex = UINT16_MAX;
27
stream_equals(const SkDynamicMemoryWStream & stream,const char * buffer)28 static bool stream_equals(const SkDynamicMemoryWStream& stream, const char* buffer) {
29 const size_t streamSize = stream.bytesWritten();
30 AutoTMalloc<char> data(streamSize);
31 stream.copyTo(data.get());
32
33 if ((false)) {
34 SkDebugf("Output\n%.*s\n", SkToInt(streamSize), data.data());
35 }
36
37 if (streamSize != strlen(buffer)) {
38 return false;
39 }
40 return memcmp(data.get(), buffer, streamSize) == 0;
41 }
42
DEF_TEST(SkPDF_ToUnicode,reporter)43 DEF_TEST(SkPDF_ToUnicode, reporter) {
44 SkTDArray<SkUnichar> glyphToUnicode;
45 THashMap<SkGlyphID, SkString> glyphToUnicodeEx;
46 SkTDArray<uint16_t> glyphsInSubset;
47 SkPDFGlyphUse subset(1, kMaximumGlyphIndex);
48
49 glyphToUnicode.push_back(0); // 0
50 glyphToUnicode.push_back(0); // 1
51 glyphToUnicode.push_back(0); // 2
52 glyphsInSubset.push_back(3);
53 glyphToUnicode.push_back(0x20); // 3
54 glyphsInSubset.push_back(4);
55 glyphToUnicode.push_back(0x25); // 4
56 glyphsInSubset.push_back(5);
57 glyphToUnicode.push_back(0x27); // 5
58 glyphsInSubset.push_back(6);
59 glyphToUnicode.push_back(0x28); // 6
60 glyphsInSubset.push_back(7);
61 glyphToUnicode.push_back(0x29); // 7
62 glyphsInSubset.push_back(8);
63 glyphToUnicode.push_back(0x2F); // 8
64 glyphsInSubset.push_back(9);
65 glyphToUnicode.push_back(0x33); // 9
66 glyphToUnicode.push_back(0); // 10
67 glyphsInSubset.push_back(11);
68 glyphToUnicode.push_back(0x35); // 11
69 glyphsInSubset.push_back(12);
70 glyphToUnicode.push_back(0x36); // 12
71 glyphsInSubset.push_back(13);
72 glyphToUnicode.push_back(0x37); // 13
73 for (uint16_t i = 14; i < 0xFE; ++i) {
74 glyphToUnicode.push_back(0); // Zero from index 0x9 to 0xFD
75 }
76 glyphsInSubset.push_back(0xFE);
77 glyphToUnicode.push_back(0x1010);
78 glyphsInSubset.push_back(0xFF);
79 glyphToUnicode.push_back(0x1011);
80 glyphsInSubset.push_back(0x100);
81 glyphToUnicode.push_back(0x1012);
82 glyphsInSubset.push_back(0x101);
83 glyphToUnicode.push_back(0x1013);
84
85 glyphToUnicodeEx.set(0x9, SkString("ffi"));
86 glyphToUnicodeEx.set(0xFC, SkString("st"));
87
88 SkGlyphID lastGlyphID = SkToU16(glyphToUnicode.size() - 1);
89
90 SkDynamicMemoryWStream buffer;
91 for (uint16_t v : glyphsInSubset) {
92 subset.set(v);
93 }
94 SkPDFAppendCmapSections(glyphToUnicode.data(), glyphToUnicodeEx, &subset, &buffer, true, 0,
95 std::min<SkGlyphID>(0xFFFF, lastGlyphID));
96
97 char expectedResult[] =
98 "4 beginbfchar\n\
99 <0003> <0020>\n\
100 <0004> <0025>\n\
101 <0008> <002F>\n\
102 <0009> <0033>\n\
103 endbfchar\n\
104 2 beginbfchar\n\
105 <0009> <006600660069>\n\
106 <00FC> <00730074>\n\
107 endbfchar\n\
108 4 beginbfrange\n\
109 <0005> <0007> <0027>\n\
110 <000B> <000D> <0035>\n\
111 <00FE> <00FF> <1010>\n\
112 <0100> <0101> <1012>\n\
113 endbfrange\n";
114
115 REPORTER_ASSERT(reporter, stream_equals(buffer, expectedResult));
116
117 // Remove characters and ranges.
118 buffer.reset();
119
120 SkPDFAppendCmapSections(glyphToUnicode.data(), glyphToUnicodeEx, &subset, &buffer, true, 8,
121 std::min<SkGlyphID>(0x00FF, lastGlyphID));
122
123 char expectedResultChop1[] =
124 "2 beginbfchar\n\
125 <0008> <002F>\n\
126 <0009> <0033>\n\
127 endbfchar\n\
128 2 beginbfchar\n\
129 <0009> <006600660069>\n\
130 <00FC> <00730074>\n\
131 endbfchar\n\
132 2 beginbfrange\n\
133 <000B> <000D> <0035>\n\
134 <00FE> <00FF> <1010>\n\
135 endbfrange\n";
136
137 REPORTER_ASSERT(reporter, stream_equals(buffer, expectedResultChop1));
138
139 // Remove characters from range to downdrade it to one char.
140 buffer.reset();
141
142 SkPDFAppendCmapSections(glyphToUnicode.data(), glyphToUnicodeEx, &subset, &buffer, true, 0x00D,
143 std::min<SkGlyphID>(0x00FE, lastGlyphID));
144
145 char expectedResultChop2[] =
146 "2 beginbfchar\n\
147 <000D> <0037>\n\
148 <00FE> <1010>\n\
149 endbfchar\n\
150 1 beginbfchar\n\
151 <00FC> <00730074>\n\
152 endbfchar\n";
153
154 REPORTER_ASSERT(reporter, stream_equals(buffer, expectedResultChop2));
155
156 buffer.reset();
157
158 SkPDFAppendCmapSections(glyphToUnicode.data(), glyphToUnicodeEx, &subset, &buffer, false, 0xFC,
159 std::min<SkGlyphID>(0x110, lastGlyphID));
160
161 char expectedResultSingleBytes[] =
162 "1 beginbfchar\n\
163 <01> <00730074>\n\
164 endbfchar\n\
165 1 beginbfrange\n\
166 <03> <06> <1010>\n\
167 endbfrange\n";
168
169 REPORTER_ASSERT(reporter, stream_equals(buffer, expectedResultSingleBytes));
170
171 glyphToUnicode.reset();
172 glyphToUnicodeEx.reset();
173 glyphsInSubset.reset();
174 SkPDFGlyphUse subset2(1, kMaximumGlyphIndex);
175
176 // Test mapping:
177 // I n s t a l
178 // Glyph id 2c 51 56 57 44 4f
179 // Unicode 49 6e 73 74 61 6c
180 for (SkUnichar i = 0; i < 100; ++i) {
181 glyphToUnicode.push_back(i + 29);
182 }
183 lastGlyphID = SkToU16(glyphToUnicode.size() - 1);
184
185 glyphsInSubset.push_back(0x2C);
186 glyphsInSubset.push_back(0x44);
187 glyphsInSubset.push_back(0x4F);
188 glyphsInSubset.push_back(0x51);
189 glyphsInSubset.push_back(0x56);
190 glyphsInSubset.push_back(0x57);
191
192 SkDynamicMemoryWStream buffer2;
193 for (uint16_t v : glyphsInSubset) {
194 subset2.set(v);
195 }
196 SkPDFAppendCmapSections(glyphToUnicode.data(), glyphToUnicodeEx, &subset2, &buffer2, true, 0,
197 std::min<SkGlyphID>(0xFFFF, lastGlyphID));
198
199 char expectedResult2[] =
200 "4 beginbfchar\n\
201 <002C> <0049>\n\
202 <0044> <0061>\n\
203 <004F> <006C>\n\
204 <0051> <006E>\n\
205 endbfchar\n\
206 1 beginbfrange\n\
207 <0056> <0057> <0073>\n\
208 endbfrange\n";
209
210 REPORTER_ASSERT(reporter, stream_equals(buffer2, expectedResult2));
211 }
212
213 #endif
214