OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2010 The Android Open Source Project | 2 * Copyright 2010 The Android Open Source Project |
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 "SkBitmap.h" | 8 #include "SkBitmap.h" |
9 #include "SkCanvas.h" | 9 #include "SkCanvas.h" |
10 #include "SkData.h" | 10 #include "SkData.h" |
11 #include "SkDocument.h" | 11 #include "SkDocument.h" |
12 #include "SkFlate.h" | 12 #include "SkFlate.h" |
13 #include "SkImageEncoder.h" | 13 #include "SkImageEncoder.h" |
14 #include "SkMatrix.h" | 14 #include "SkMatrix.h" |
15 #include "SkPDFCanon.h" | 15 #include "SkPDFCanon.h" |
16 #include "SkPDFCatalog.h" | |
17 #include "SkPDFDevice.h" | 16 #include "SkPDFDevice.h" |
18 #include "SkPDFStream.h" | 17 #include "SkPDFStream.h" |
19 #include "SkPDFTypes.h" | 18 #include "SkPDFTypes.h" |
20 #include "SkReadBuffer.h" | 19 #include "SkReadBuffer.h" |
21 #include "SkScalar.h" | 20 #include "SkScalar.h" |
22 #include "SkStream.h" | 21 #include "SkStream.h" |
23 #include "SkTypes.h" | 22 #include "SkTypes.h" |
24 #include "Test.h" | 23 #include "Test.h" |
25 | 24 |
26 #define DUMMY_TEXT "DCT compessed stream." | 25 #define DUMMY_TEXT "DCT compessed stream." |
27 | 26 |
28 static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset, | 27 static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset, |
29 const void* buffer, size_t len) { | 28 const void* buffer, size_t len) { |
30 SkAutoDataUnref data(stream.copyToData()); | 29 SkAutoDataUnref data(stream.copyToData()); |
31 if (offset + len > data->size()) { | 30 if (offset + len > data->size()) { |
32 return false; | 31 return false; |
33 } | 32 } |
34 return memcmp(data->bytes() + offset, buffer, len) == 0; | 33 return memcmp(data->bytes() + offset, buffer, len) == 0; |
35 } | 34 } |
36 | 35 |
37 static void emit_object(SkPDFObject* object, | 36 static void emit_object(SkPDFObject* object, |
38 SkWStream* stream, | 37 SkWStream* stream, |
39 SkPDFCatalog* catalog, | 38 const SkPDFObjNumMap& objNumMap, |
| 39 const SkPDFSubstituteMap& substitutes, |
40 bool indirect) { | 40 bool indirect) { |
41 SkPDFObject* realObject = catalog->getSubstituteObject(object); | 41 SkPDFObject* realObject = substitutes.getSubstitute(object); |
42 if (indirect) { | 42 if (indirect) { |
43 stream->writeDecAsText(catalog->getObjectNumber(object)); | 43 stream->writeDecAsText(objNumMap.getObjectNumber(realObject)); |
44 stream->writeText(" 0 obj\n"); // Generation number is always 0. | 44 stream->writeText(" 0 obj\n"); // Generation number is always 0. |
45 realObject->emitObject(stream, catalog); | 45 realObject->emitObject(stream, objNumMap, substitutes); |
46 stream->writeText("\nendobj\n"); | 46 stream->writeText("\nendobj\n"); |
47 } else { | 47 } else { |
48 realObject->emitObject(stream, catalog); | 48 realObject->emitObject(stream, objNumMap, substitutes); |
49 } | 49 } |
50 } | 50 } |
51 | 51 |
52 static size_t get_output_size(SkPDFObject* object, | 52 static size_t get_output_size(SkPDFObject* object, |
53 SkPDFCatalog* catalog, | 53 const SkPDFObjNumMap& objNumMap, |
| 54 const SkPDFSubstituteMap& substitutes, |
54 bool indirect) { | 55 bool indirect) { |
55 SkDynamicMemoryWStream buffer; | 56 SkDynamicMemoryWStream buffer; |
56 emit_object(object, &buffer, catalog, indirect); | 57 emit_object(object, &buffer, objNumMap, substitutes, indirect); |
57 return buffer.getOffset(); | 58 return buffer.getOffset(); |
58 } | 59 } |
59 | 60 |
60 static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj, | 61 static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj, |
61 const char* expectedData, size_t expectedSize, | 62 const char* expectedData, size_t expectedSize, |
62 bool indirect) { | 63 bool indirect) { |
63 SkPDFCatalog catalog; | 64 SkPDFSubstituteMap substituteMap; |
64 size_t directSize = get_output_size(obj, &catalog, false); | 65 SkPDFObjNumMap catalog; |
| 66 size_t directSize = get_output_size(obj, catalog, substituteMap, false); |
65 REPORTER_ASSERT(reporter, directSize == expectedSize); | 67 REPORTER_ASSERT(reporter, directSize == expectedSize); |
66 | 68 |
67 SkDynamicMemoryWStream buffer; | 69 SkDynamicMemoryWStream buffer; |
68 emit_object(obj, &buffer, &catalog, false); | 70 emit_object(obj, &buffer, catalog, substituteMap, false); |
69 REPORTER_ASSERT(reporter, directSize == buffer.getOffset()); | 71 REPORTER_ASSERT(reporter, directSize == buffer.getOffset()); |
70 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData, | 72 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData, |
71 directSize)); | 73 directSize)); |
72 | 74 |
73 if (indirect) { | 75 if (indirect) { |
74 // Indirect output. | 76 // Indirect output. |
75 static char header[] = "1 0 obj\n"; | 77 static char header[] = "1 0 obj\n"; |
76 static size_t headerLen = strlen(header); | 78 static size_t headerLen = strlen(header); |
77 static char footer[] = "\nendobj\n"; | 79 static char footer[] = "\nendobj\n"; |
78 static size_t footerLen = strlen(footer); | 80 static size_t footerLen = strlen(footer); |
79 | 81 |
80 catalog.addObject(obj); | 82 catalog.addObject(obj); |
81 | 83 |
82 size_t indirectSize = get_output_size(obj, &catalog, true); | 84 size_t indirectSize = |
| 85 get_output_size(obj, catalog, substituteMap, true); |
83 REPORTER_ASSERT(reporter, | 86 REPORTER_ASSERT(reporter, |
84 indirectSize == directSize + headerLen + footerLen); | 87 indirectSize == directSize + headerLen + footerLen); |
85 | 88 |
86 buffer.reset(); | 89 buffer.reset(); |
87 emit_object(obj, &buffer, &catalog, true); | 90 emit_object(obj, &buffer, catalog, substituteMap, true); |
88 REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset()); | 91 REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset()); |
89 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen)); | 92 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen)); |
90 REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData, | 93 REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData, |
91 directSize)); | 94 directSize)); |
92 REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize, | 95 REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize, |
93 footer, footerLen)); | 96 footer, footerLen)); |
94 } | 97 } |
95 } | 98 } |
96 | 99 |
97 static void SimpleCheckObjectOutput(skiatest::Reporter* reporter, | 100 static void SimpleCheckObjectOutput(skiatest::Reporter* reporter, |
98 SkPDFObject* obj, | 101 SkPDFObject* obj, |
99 const char* expectedResult) { | 102 const char* expectedResult) { |
100 CheckObjectOutput(reporter, obj, expectedResult, | 103 CheckObjectOutput(reporter, obj, expectedResult, |
101 strlen(expectedResult), true); | 104 strlen(expectedResult), true); |
102 } | 105 } |
103 | 106 |
104 static void TestPDFStream(skiatest::Reporter* reporter) { | 107 static void TestPDFStream(skiatest::Reporter* reporter) { |
105 char streamBytes[] = "Test\nFoo\tBar"; | 108 char streamBytes[] = "Test\nFoo\tBar"; |
106 SkAutoTDelete<SkMemoryStream> streamData(new SkMemoryStream( | 109 SkAutoTDelete<SkMemoryStream> streamData(new SkMemoryStream( |
107 streamBytes, strlen(streamBytes), true)); | 110 streamBytes, strlen(streamBytes), true)); |
108 SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData.get())); | 111 SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData.get())); |
109 SimpleCheckObjectOutput( | 112 SimpleCheckObjectOutput( |
110 reporter, stream.get(), | 113 reporter, stream.get(), |
111 "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream"); | 114 "<</Length 12>> stream\nTest\nFoo\tBar\nendstream"); |
112 stream->insert("Attribute", new SkPDFInt(42))->unref(); | 115 stream->insert("Attribute", new SkPDFInt(42))->unref(); |
113 SimpleCheckObjectOutput(reporter, stream.get(), | 116 SimpleCheckObjectOutput(reporter, stream.get(), |
114 "<</Length 12\n/Attribute 42\n>> stream\n" | 117 "<</Length 12\n/Attribute 42>> stream\n" |
115 "Test\nFoo\tBar\nendstream"); | 118 "Test\nFoo\tBar\nendstream"); |
116 | 119 |
117 { | 120 { |
118 char streamBytes2[] = "This is a longer string, so that compression " | 121 char streamBytes2[] = "This is a longer string, so that compression " |
119 "can do something with it. With shorter strings, " | 122 "can do something with it. With shorter strings, " |
120 "the short circuit logic cuts in and we end up " | 123 "the short circuit logic cuts in and we end up " |
121 "with an uncompressed string."; | 124 "with an uncompressed string."; |
122 SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2, | 125 SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2, |
123 strlen(streamBytes2))); | 126 strlen(streamBytes2))); |
124 SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData2.get())); | 127 SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData2.get())); |
125 | 128 |
126 SkDynamicMemoryWStream compressedByteStream; | 129 SkDynamicMemoryWStream compressedByteStream; |
127 SkFlate::Deflate(streamData2.get(), &compressedByteStream); | 130 SkFlate::Deflate(streamData2.get(), &compressedByteStream); |
128 SkAutoDataUnref compressedData(compressedByteStream.copyToData()); | 131 SkAutoDataUnref compressedData(compressedByteStream.copyToData()); |
129 | 132 |
130 SkDynamicMemoryWStream expected; | 133 SkDynamicMemoryWStream expected; |
131 expected.writeText("<</Filter /FlateDecode\n/Length 116\n" | 134 expected.writeText("<</Filter /FlateDecode\n/Length 116>> stream\n"); |
132 ">> stream\n"); | |
133 expected.write(compressedData->data(), compressedData->size()); | 135 expected.write(compressedData->data(), compressedData->size()); |
134 expected.writeText("\nendstream"); | 136 expected.writeText("\nendstream"); |
135 SkAutoDataUnref expectedResultData2(expected.copyToData()); | 137 SkAutoDataUnref expectedResultData2(expected.copyToData()); |
136 CheckObjectOutput(reporter, stream.get(), | 138 CheckObjectOutput(reporter, stream.get(), |
137 (const char*) expectedResultData2->data(), | 139 (const char*) expectedResultData2->data(), |
138 expectedResultData2->size(), true); | 140 expectedResultData2->size(), true); |
139 } | 141 } |
140 } | 142 } |
141 | 143 |
142 static void TestCatalog(skiatest::Reporter* reporter) { | 144 static void TestCatalog(skiatest::Reporter* reporter) { |
143 SkPDFCatalog catalog; | 145 SkPDFSubstituteMap substituteMap; |
| 146 SkPDFObjNumMap catalog; |
144 SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1)); | 147 SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1)); |
145 SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2)); | 148 SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2)); |
146 SkAutoTUnref<SkPDFInt> int3(new SkPDFInt(3)); | 149 SkAutoTUnref<SkPDFInt> int3(new SkPDFInt(3)); |
147 int1.get()->ref(); | 150 int1.get()->ref(); |
148 SkAutoTUnref<SkPDFInt> int1Again(int1.get()); | 151 SkAutoTUnref<SkPDFInt> int1Again(int1.get()); |
149 | 152 |
150 catalog.addObject(int1.get()); | 153 catalog.addObject(int1.get()); |
151 catalog.addObject(int2.get()); | 154 catalog.addObject(int2.get()); |
152 catalog.addObject(int3.get()); | 155 catalog.addObject(int3.get()); |
153 | 156 |
154 REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1.get()) == 1); | 157 REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1.get()) == 1); |
155 REPORTER_ASSERT(reporter, catalog.getObjectNumber(int2.get()) == 2); | 158 REPORTER_ASSERT(reporter, catalog.getObjectNumber(int2.get()) == 2); |
156 REPORTER_ASSERT(reporter, catalog.getObjectNumber(int3.get()) == 3); | 159 REPORTER_ASSERT(reporter, catalog.getObjectNumber(int3.get()) == 3); |
157 REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1Again.get()) == 1); | 160 REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1Again.get()) == 1); |
158 } | 161 } |
159 | 162 |
160 static void TestObjectRef(skiatest::Reporter* reporter) { | 163 static void TestObjectRef(skiatest::Reporter* reporter) { |
161 SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1)); | 164 SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1)); |
162 SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2)); | 165 SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2)); |
163 SkAutoTUnref<SkPDFObjRef> int2ref(new SkPDFObjRef(int2.get())); | 166 SkAutoTUnref<SkPDFObjRef> int2ref(new SkPDFObjRef(int2.get())); |
164 | 167 |
165 SkPDFCatalog catalog; | 168 SkPDFSubstituteMap substituteMap; |
| 169 SkPDFObjNumMap catalog; |
166 catalog.addObject(int1.get()); | 170 catalog.addObject(int1.get()); |
167 catalog.addObject(int2.get()); | 171 catalog.addObject(int2.get()); |
168 REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1.get()) == 1); | 172 REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1.get()) == 1); |
169 REPORTER_ASSERT(reporter, catalog.getObjectNumber(int2.get()) == 2); | 173 REPORTER_ASSERT(reporter, catalog.getObjectNumber(int2.get()) == 2); |
170 | 174 |
171 char expectedResult[] = "2 0 R"; | 175 char expectedResult[] = "2 0 R"; |
172 SkDynamicMemoryWStream buffer; | 176 SkDynamicMemoryWStream buffer; |
173 int2ref->emitObject(&buffer, &catalog); | 177 int2ref->emitObject(&buffer, catalog, substituteMap); |
174 REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult)); | 178 REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult)); |
175 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult, | 179 REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult, |
176 buffer.getOffset())); | 180 buffer.getOffset())); |
177 } | 181 } |
178 | 182 |
179 static void TestSubstitute(skiatest::Reporter* reporter) { | 183 static void TestSubstitute(skiatest::Reporter* reporter) { |
180 SkAutoTUnref<SkPDFDict> proxy(new SkPDFDict()); | 184 SkAutoTUnref<SkPDFDict> proxy(new SkPDFDict()); |
181 SkAutoTUnref<SkPDFDict> stub(new SkPDFDict()); | 185 SkAutoTUnref<SkPDFDict> stub(new SkPDFDict()); |
182 | 186 |
183 proxy->insert("Value", new SkPDFInt(33))->unref(); | 187 proxy->insert("Value", new SkPDFInt(33))->unref(); |
184 stub->insert("Value", new SkPDFInt(44))->unref(); | 188 stub->insert("Value", new SkPDFInt(44))->unref(); |
185 | 189 |
186 SkPDFCatalog catalog; | 190 SkPDFSubstituteMap substituteMap; |
| 191 substituteMap.setSubstitute(proxy.get(), stub.get()); |
| 192 SkPDFObjNumMap catalog; |
187 catalog.addObject(proxy.get()); | 193 catalog.addObject(proxy.get()); |
188 catalog.setSubstitute(proxy.get(), stub.get()); | |
189 | 194 |
190 REPORTER_ASSERT(reporter, stub.get() == catalog.getSubstituteObject(proxy)); | 195 REPORTER_ASSERT(reporter, stub.get() == substituteMap.getSubstitute(proxy)); |
191 REPORTER_ASSERT(reporter, proxy.get() != catalog.getSubstituteObject(stub)); | 196 REPORTER_ASSERT(reporter, proxy.get() != substituteMap.getSubstitute(stub)); |
192 } | 197 } |
193 | 198 |
194 // This test used to assert without the fix submitted for | 199 // This test used to assert without the fix submitted for |
195 // http://code.google.com/p/skia/issues/detail?id=1083. | 200 // http://code.google.com/p/skia/issues/detail?id=1083. |
196 // SKP files might have invalid glyph ids. This test ensures they are ignored, | 201 // SKP files might have invalid glyph ids. This test ensures they are ignored, |
197 // and there is no assert on input data in Debug mode. | 202 // and there is no assert on input data in Debug mode. |
198 static void test_issue1083() { | 203 static void test_issue1083() { |
199 SkDynamicMemoryWStream outStream; | 204 SkDynamicMemoryWStream outStream; |
200 SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&outStream)); | 205 SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&outStream)); |
201 SkCanvas* canvas = doc->beginPage(100.0f, 100.0f); | 206 SkCanvas* canvas = doc->beginPage(100.0f, 100.0f); |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
265 array->append(int0.get()); | 270 array->append(int0.get()); |
266 SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]"); | 271 SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]"); |
267 SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1)); | 272 SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1)); |
268 array->setAt(0, int1.get()); | 273 array->setAt(0, int1.get()); |
269 SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]"); | 274 SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]"); |
270 | 275 |
271 SkAutoTUnref<SkPDFDict> dict(new SkPDFDict); | 276 SkAutoTUnref<SkPDFDict> dict(new SkPDFDict); |
272 SimpleCheckObjectOutput(reporter, dict.get(), "<<>>"); | 277 SimpleCheckObjectOutput(reporter, dict.get(), "<<>>"); |
273 SkAutoTUnref<SkPDFName> n1(new SkPDFName("n1")); | 278 SkAutoTUnref<SkPDFName> n1(new SkPDFName("n1")); |
274 dict->insert(n1.get(), int42.get()); | 279 dict->insert(n1.get(), int42.get()); |
275 SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>"); | 280 SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42>>"); |
276 SkAutoTUnref<SkPDFName> n2(new SkPDFName("n2")); | 281 SkAutoTUnref<SkPDFName> n2(new SkPDFName("n2")); |
277 SkAutoTUnref<SkPDFName> n3(new SkPDFName("n3")); | 282 SkAutoTUnref<SkPDFName> n3(new SkPDFName("n3")); |
278 dict->insert(n2.get(), realHalf.get()); | 283 dict->insert(n2.get(), realHalf.get()); |
279 dict->insert(n3.get(), array.get()); | 284 dict->insert(n3.get(), array.get()); |
280 SimpleCheckObjectOutput(reporter, dict.get(), | 285 SimpleCheckObjectOutput(reporter, dict.get(), |
281 "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>"); | 286 "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]>>"); |
282 | 287 |
283 TestPDFStream(reporter); | 288 TestPDFStream(reporter); |
284 | 289 |
285 TestCatalog(reporter); | 290 TestCatalog(reporter); |
286 | 291 |
287 TestObjectRef(reporter); | 292 TestObjectRef(reporter); |
288 | 293 |
289 TestSubstitute(reporter); | 294 TestSubstitute(reporter); |
290 | 295 |
291 test_issue1083(); | 296 test_issue1083(); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
339 // Filter just created; should be unvisited. | 344 // Filter just created; should be unvisited. |
340 REPORTER_ASSERT(reporter, !filter->visited()); | 345 REPORTER_ASSERT(reporter, !filter->visited()); |
341 SkPaint paint; | 346 SkPaint paint; |
342 paint.setImageFilter(filter.get()); | 347 paint.setImageFilter(filter.get()); |
343 canvas->drawRect(SkRect::MakeWH(100, 100), paint); | 348 canvas->drawRect(SkRect::MakeWH(100, 100), paint); |
344 doc->close(); | 349 doc->close(); |
345 | 350 |
346 // Filter was used in rendering; should be visited. | 351 // Filter was used in rendering; should be visited. |
347 REPORTER_ASSERT(reporter, filter->visited()); | 352 REPORTER_ASSERT(reporter, filter->visited()); |
348 } | 353 } |
OLD | NEW |