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