1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <string>
18
19#include "Test.h"
20#include "SkPDFCatalog.h"
21#include "SkPDFStream.h"
22#include "SkPDFTypes.h"
23#include "SkScalar.h"
24#include "SkStream.h"
25
26static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj,
27                              const std::string& representation,
28                              bool indirect) {
29    size_t directSize = obj->getOutputSize(NULL, false);
30    REPORTER_ASSERT(reporter, directSize == representation.size());
31
32    SkDynamicMemoryWStream buffer;
33    obj->emitObject(&buffer, NULL, false);
34    REPORTER_ASSERT(reporter, directSize == buffer.getOffset());
35    REPORTER_ASSERT(reporter, memcmp(buffer.getStream(), representation.c_str(),
36                                     directSize) == 0);
37
38    if (indirect) {
39        // Indirect output.
40        static char header[] = "1 0 obj\n";
41        static size_t headerLen = strlen(header);
42        static char footer[] = "\nendobj\n";
43        static size_t footerLen = strlen(footer);
44
45        SkPDFCatalog catalog;
46        catalog.addObject(obj, false);
47
48        size_t indirectSize = obj->getOutputSize(&catalog, true);
49        REPORTER_ASSERT(reporter,
50                        indirectSize == directSize + headerLen + footerLen);
51
52        buffer.reset();
53        obj->emitObject(&buffer, &catalog, true);
54        REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset());
55        REPORTER_ASSERT(reporter, memcmp(buffer.getStream(), header,
56                                         headerLen) == 0);
57        REPORTER_ASSERT(reporter,
58                        memcmp(buffer.getStream() + headerLen,
59                               representation.c_str(), directSize) == 0);
60        REPORTER_ASSERT(reporter,
61                        memcmp(buffer.getStream() + headerLen + directSize,
62                               footer, footerLen) == 0);
63    }
64}
65
66static void TestCatalog(skiatest::Reporter* reporter) {
67    SkPDFCatalog catalog;
68    SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
69    int1->unref();  // SkRefPtr and new both took a reference.
70    SkRefPtr<SkPDFInt> int2 = new SkPDFInt(2);
71    int2->unref();  // SkRefPtr and new both took a reference.
72    SkRefPtr<SkPDFInt> int3 = new SkPDFInt(3);
73    int3->unref();  // SkRefPtr and new both took a reference.
74    SkRefPtr<SkPDFInt> int1Again(int1.get());
75
76    catalog.addObject(int1.get(), false);
77    catalog.addObject(int2.get(), false);
78    catalog.addObject(int3.get(), false);
79
80    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
81    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
82    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int3.get()) == 3);
83
84    SkDynamicMemoryWStream buffer;
85    catalog.emitObjectNumber(&buffer, int1.get());
86    catalog.emitObjectNumber(&buffer, int2.get());
87    catalog.emitObjectNumber(&buffer, int3.get());
88    catalog.emitObjectNumber(&buffer, int1Again.get());
89    char expectedResult[] = "1 02 03 01 0";
90    REPORTER_ASSERT(reporter, memcmp(buffer.getStream(), expectedResult,
91                                     strlen(expectedResult)) == 0);
92}
93
94static void TestObjectRef(skiatest::Reporter* reporter) {
95    SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
96    int1->unref();  // SkRefPtr and new both took a reference.
97    SkRefPtr<SkPDFInt> int2 = new SkPDFInt(2);
98    int2->unref();  // SkRefPtr and new both took a reference.
99    SkRefPtr<SkPDFObjRef> int2ref = new SkPDFObjRef(int2.get());
100    int2ref->unref();  // SkRefPtr and new both took a reference.
101
102    SkPDFCatalog catalog;
103    catalog.addObject(int1.get(), false);
104    catalog.addObject(int2.get(), false);
105    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
106    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
107
108    char expectedResult[] = "2 0 R";
109    SkDynamicMemoryWStream buffer;
110    int2ref->emitObject(&buffer, &catalog, false);
111    REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
112    REPORTER_ASSERT(reporter, memcmp(buffer.getStream(), expectedResult,
113                                     buffer.getOffset()) == 0);
114}
115
116static void TestPDFPrimitives(skiatest::Reporter* reporter) {
117    SkRefPtr<SkPDFInt> int42 = new SkPDFInt(42);
118    int42->unref();  // SkRefPtr and new both took a reference.
119    CheckObjectOutput(reporter, int42.get(), "42", true);
120
121    SkRefPtr<SkPDFScalar> realHalf = new SkPDFScalar(SK_ScalarHalf);
122    realHalf->unref();  // SkRefPtr and new both took a reference.
123    CheckObjectOutput(reporter, realHalf.get(), "0.5", true);
124
125#if defined(SK_SCALAR_IS_FLOAT)
126    SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75);
127    bigScalar->unref();  // SkRefPtr and new both took a reference.
128#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
129    CheckObjectOutput(reporter, bigScalar.get(), "111000", true);
130#else
131    CheckObjectOutput(reporter, bigScalar.get(), "110999.75", true);
132
133    SkRefPtr<SkPDFScalar> biggerScalar = new SkPDFScalar(50000000.1);
134    biggerScalar->unref();  // SkRefPtr and new both took a reference.
135    CheckObjectOutput(reporter, biggerScalar.get(), "50000000", true);
136
137    SkRefPtr<SkPDFScalar> smallestScalar = new SkPDFScalar(1.0/65536);
138    smallestScalar->unref();  // SkRefPtr and new both took a reference.
139    CheckObjectOutput(reporter, smallestScalar.get(), "0.00001526", true);
140#endif
141#endif
142
143    SkRefPtr<SkPDFString> stringSimple = new SkPDFString("test ) string ( foo");
144    stringSimple->unref();  // SkRefPtr and new both took a reference.
145    CheckObjectOutput(reporter, stringSimple.get(), "(test \\) string \\( foo)",
146                      true);
147    SkRefPtr<SkPDFString> stringComplex =
148        new SkPDFString("\ttest ) string ( foo");
149    stringComplex->unref();  // SkRefPtr and new both took a reference.
150    CheckObjectOutput(reporter, stringComplex.get(),
151                      "<0974657374202920737472696E67202820666F6F>", true);
152
153    SkRefPtr<SkPDFName> name = new SkPDFName("Test name\twith#tab");
154    name->unref();  // SkRefPtr and new both took a reference.
155    CheckObjectOutput(reporter, name.get(), "/Test#20name#09with#23tab", false);
156
157    SkRefPtr<SkPDFArray> array = new SkPDFArray;
158    array->unref();  // SkRefPtr and new both took a reference.
159    CheckObjectOutput(reporter, array.get(), "[]", true);
160    array->append(int42.get());
161    CheckObjectOutput(reporter, array.get(), "[42]", true);
162    array->append(realHalf.get());
163    CheckObjectOutput(reporter, array.get(), "[42 0.5]", true);
164    SkRefPtr<SkPDFInt> int0 = new SkPDFInt(0);
165    int0->unref();  // SkRefPtr and new both took a reference.
166    array->append(int0.get());
167    CheckObjectOutput(reporter, array.get(), "[42 0.5 0]", true);
168    SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
169    int1->unref();  // SkRefPtr and new both took a reference.
170    array->setAt(0, int1.get());
171    CheckObjectOutput(reporter, array.get(), "[1 0.5 0]", true);
172
173    SkRefPtr<SkPDFDict> dict = new SkPDFDict;
174    dict->unref();  // SkRefPtr and new both took a reference.
175    CheckObjectOutput(reporter, dict.get(), "<<>>", true);
176    SkRefPtr<SkPDFName> n1 = new SkPDFName("n1");
177    n1->unref();  // SkRefPtr and new both took a reference.
178    dict->insert(n1.get(), int42.get());
179    CheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>", true);
180    SkRefPtr<SkPDFName> n2 = new SkPDFName("n2");
181    n2->unref();  // SkRefPtr and new both took a reference.
182    SkRefPtr<SkPDFName> n3 = new SkPDFName("n3");
183    n3->unref();  // SkRefPtr and new both took a reference.
184    dict->insert(n2.get(), realHalf.get());
185    dict->insert(n3.get(), array.get());
186    CheckObjectOutput(reporter, dict.get(),
187                      "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>", true);
188
189    char streamBytes[] = "Test\nFoo\tBar";
190    SkRefPtr<SkMemoryStream> streamData = new SkMemoryStream(
191        streamBytes, strlen(streamBytes), true);
192    streamData->unref();  // SkRefPtr and new both took a reference.
193    SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData.get());
194    stream->unref();  // SkRefPtr and new both took a reference.
195    CheckObjectOutput(reporter, stream.get(),
196                      "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream",
197                      true);
198    stream->insert(n1.get(), int42.get());
199    CheckObjectOutput(reporter, stream.get(),
200                      "<</Length 12\n/n1 42\n>> stream\nTest\nFoo\tBar"
201                      "\nendstream",
202                      true);
203
204    TestCatalog(reporter);
205
206    TestObjectRef(reporter);
207}
208
209#include "TestClassDef.h"
210DEFINE_TESTCLASS("PDFPrimitives", PDFPrimitivesTestClass, TestPDFPrimitives)
211