blob: 8690b3eaea7b1ede6c78f104d8c285566020c737 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2010 The Android Open Source Project
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000010#include "SkPDFCatalog.h"
11#include "SkPDFTypes.h"
12#include "SkStream.h"
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +000013#include "SkTypes.h"
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000014
vandebo@chromium.org421d6442011-07-20 17:39:01 +000015SkPDFCatalog::SkPDFCatalog(SkPDFDocument::Flags flags)
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +000016 : fFirstPageCount(0),
17 fNextObjNum(1),
vandebo@chromium.org421d6442011-07-20 17:39:01 +000018 fNextFirstPageObjNum(0),
19 fDocumentFlags(flags) {
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +000020}
21
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000022SkPDFCatalog::~SkPDFCatalog() {
23 fSubstituteResourcesRemaining.safeUnrefAll();
24 fSubstituteResourcesFirstPage.safeUnrefAll();
25}
vandebo@chromium.orgf66025d2010-10-01 23:26:55 +000026
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +000027SkPDFObject* SkPDFCatalog::addObject(SkPDFObject* obj, bool onFirstPage) {
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000028 if (findObjectIndex(obj) != -1) { // object already added
29 return obj;
30 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +000031 SkASSERT(fNextFirstPageObjNum == 0);
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +000032 if (onFirstPage) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +000033 fFirstPageCount++;
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +000034 }
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000035
36 struct Rec newEntry(obj, onFirstPage);
37 fCatalog.append(1, &newEntry);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +000038 return obj;
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000039}
40
vandebo@chromium.org30580f62012-07-12 20:22:04 +000041size_t SkPDFCatalog::setFileOffset(SkPDFObject* obj, off_t offset) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +000042 int objIndex = assignObjNum(obj) - 1;
43 SkASSERT(fCatalog[objIndex].fObjNumAssigned);
44 SkASSERT(fCatalog[objIndex].fFileOffset == 0);
45 fCatalog[objIndex].fFileOffset = offset;
46
vandebo@chromium.orgd3a094c2011-07-25 22:22:25 +000047 return getSubstituteObject(obj)->getOutputSize(this, true);
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +000048}
49
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000050void SkPDFCatalog::emitObjectNumber(SkWStream* stream, SkPDFObject* obj) {
51 stream->writeDecAsText(assignObjNum(obj));
52 stream->writeText(" 0"); // Generation number is always 0.
53}
54
55size_t SkPDFCatalog::getObjectNumberSize(SkPDFObject* obj) {
56 SkDynamicMemoryWStream buffer;
57 emitObjectNumber(&buffer, obj);
58 return buffer.getOffset();
59}
60
61int SkPDFCatalog::findObjectIndex(SkPDFObject* obj) const {
62 for (int i = 0; i < fCatalog.count(); i++) {
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +000063 if (fCatalog[i].fObject == obj) {
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000064 return i;
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +000065 }
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000066 }
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +000067 // If it's not in the main array, check if it's a substitute object.
68 for (int i = 0; i < fSubstituteMap.count(); ++i) {
69 if (fSubstituteMap[i].fSubstitute == obj) {
70 return findObjectIndex(fSubstituteMap[i].fOriginal);
71 }
72 }
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000073 return -1;
74}
75
76int SkPDFCatalog::assignObjNum(SkPDFObject* obj) {
77 int pos = findObjectIndex(obj);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +000078 // If this assert fails, it means you probably forgot to add an object
79 // to the resource list.
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000080 SkASSERT(pos >= 0);
81 uint32_t currentIndex = pos;
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +000082 if (fCatalog[currentIndex].fObjNumAssigned) {
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000083 return currentIndex + 1;
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +000084 }
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000085
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +000086 // First assignment.
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +000087 if (fNextFirstPageObjNum == 0) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +000088 fNextFirstPageObjNum = fCatalog.count() - fFirstPageCount + 1;
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +000089 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +000090
91 uint32_t objNum;
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000092 if (fCatalog[currentIndex].fOnFirstPage) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +000093 objNum = fNextFirstPageObjNum;
94 fNextFirstPageObjNum++;
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000095 } else {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +000096 objNum = fNextObjNum;
97 fNextObjNum++;
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +000098 }
99
100 // When we assign an object an object number, we put it in that array
101 // offset (minus 1 because object number 0 is reserved).
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000102 SkASSERT(!fCatalog[objNum - 1].fObjNumAssigned);
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000103 if (objNum - 1 != currentIndex) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000104 SkTSwap(fCatalog[objNum - 1], fCatalog[currentIndex]);
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000105 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000106 fCatalog[objNum - 1].fObjNumAssigned = true;
107 return objNum;
108}
109
110int32_t SkPDFCatalog::emitXrefTable(SkWStream* stream, bool firstPage) {
111 int first = -1;
112 int last = fCatalog.count() - 1;
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000113 // TODO(vandebo): Support linearized format.
ctguil@chromium.orga5c72342011-08-15 23:55:03 +0000114 // int last = fCatalog.count() - fFirstPageCount - 1;
115 // if (firstPage) {
116 // first = fCatalog.count() - fFirstPageCount;
117 // last = fCatalog.count() - 1;
118 // }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000119
120 stream->writeText("xref\n");
121 stream->writeDecAsText(first + 1);
122 stream->writeText(" ");
123 stream->writeDecAsText(last - first + 1);
124 stream->writeText("\n");
125
126 if (first == -1) {
127 stream->writeText("0000000000 65535 f \n");
128 first++;
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000129 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000130 for (int i = first; i <= last; i++) {
reed@google.com46ee9c62013-03-11 18:55:04 +0000131 // For 32 bits platforms, the maximum offset has to fit within off_t
132 // which is a 32 bits signed integer on these platforms.
133 SkDEBUGCODE(static const off_t kMaxOff = SK_MaxS32;)
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000134 SkASSERT(fCatalog[i].fFileOffset > 0);
sugoi@google.comdfc867b2013-03-11 18:45:12 +0000135 SkASSERT(fCatalog[i].fFileOffset < kMaxOff);
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000136 stream->writeBigDecAsText(fCatalog[i].fFileOffset, 10);
137 stream->writeText(" 00000 n \n");
138 }
139
140 return fCatalog.count() + 1;
vandebo@chromium.org8459d4e2010-09-24 22:25:30 +0000141}
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000142
143void SkPDFCatalog::setSubstitute(SkPDFObject* original,
144 SkPDFObject* substitute) {
145#if defined(SK_DEBUG)
146 // Sanity check: is the original already in substitute list?
147 for (int i = 0; i < fSubstituteMap.count(); ++i) {
148 if (original == fSubstituteMap[i].fSubstitute ||
149 original == fSubstituteMap[i].fOriginal) {
150 SkASSERT(false);
151 return;
152 }
153 }
154#endif
155 // Check if the original is on first page.
156 bool onFirstPage = false;
157 for (int i = 0; i < fCatalog.count(); ++i) {
158 if (fCatalog[i].fObject == original) {
159 onFirstPage = fCatalog[i].fOnFirstPage;
160 break;
161 }
162#if defined(SK_DEBUG)
163 if (i == fCatalog.count() - 1) {
164 SkASSERT(false); // original not in catalog
165 return;
166 }
167#endif
168 }
169
170 SubstituteMapping newMapping(original, substitute);
171 fSubstituteMap.append(1, &newMapping);
172
173 // Add resource objects of substitute object to catalog.
edisonn@google.com6addb192013-04-02 15:33:08 +0000174 SkTSet<SkPDFObject*>* targetSet = getSubstituteList(onFirstPage);
175 SkTSet<SkPDFObject*> newResourceObjects;
176 newMapping.fSubstitute->getResources(*targetSet, &newResourceObjects);
177 for (int i = 0; i < newResourceObjects.count(); ++i) {
178 addObject(newResourceObjects[i], onFirstPage);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000179 }
edisonn@google.com6addb192013-04-02 15:33:08 +0000180 // mergeInto returns the number of duplicates.
181 // If there are duplicates, there is a bug and we mess ref counting.
182 SkDEBUGCODE(int duplicates =) targetSet->mergeInto(newResourceObjects);
183 SkASSERT(duplicates == 0);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000184}
185
186SkPDFObject* SkPDFCatalog::getSubstituteObject(SkPDFObject* object) {
187 for (int i = 0; i < fSubstituteMap.count(); ++i) {
188 if (object == fSubstituteMap[i].fOriginal) {
189 return fSubstituteMap[i].fSubstitute;
190 }
191 }
192 return object;
193}
194
195off_t SkPDFCatalog::setSubstituteResourcesOffsets(off_t fileOffset,
196 bool firstPage) {
edisonn@google.com6addb192013-04-02 15:33:08 +0000197 SkTSet<SkPDFObject*>* targetSet = getSubstituteList(firstPage);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000198 off_t offsetSum = fileOffset;
edisonn@google.com6addb192013-04-02 15:33:08 +0000199 for (int i = 0; i < targetSet->count(); ++i) {
200 offsetSum += setFileOffset((*targetSet)[i], offsetSum);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000201 }
vandebo@chromium.orgf5181a42011-07-20 00:33:53 +0000202 return offsetSum - fileOffset;
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000203}
204
205void SkPDFCatalog::emitSubstituteResources(SkWStream *stream, bool firstPage) {
edisonn@google.com6addb192013-04-02 15:33:08 +0000206 SkTSet<SkPDFObject*>* targetSet = getSubstituteList(firstPage);
207 for (int i = 0; i < targetSet->count(); ++i) {
208 (*targetSet)[i]->emit(stream, this, true);
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000209 }
210}
211
edisonn@google.com6addb192013-04-02 15:33:08 +0000212SkTSet<SkPDFObject*>* SkPDFCatalog::getSubstituteList(bool firstPage) {
vandebo@chromium.org2ef12d42011-07-06 23:31:24 +0000213 return firstPage ? &fSubstituteResourcesFirstPage :
214 &fSubstituteResourcesRemaining;
vandebo@chromium.orgf5181a42011-07-20 00:33:53 +0000215}