1 /** \file\r
2 * PDF Mini library.\r
3 */\r
4 /*\r
5 * Authors:\r
6 * Ulf Erikson <ulferikson@users.sf.net>\r
7 *\r
8 * Minimalistic library to support generation of PDF files.\r
9 *\r
10 * Copyright (C) 2006 Authors\r
11 *\r
12 * Released under GNU GPL, read the file 'COPYING' for more information\r
13 */\r
14 \r
15 #ifndef PDF_MINI_H_SEEN\r
16 #define PDF_MINI_H_SEEN\r
17 \r
18 \r
19 class PdfFile;\r
20 class PdfXref;\r
21 class PdfObject;\r
22 \r
23 enum pdf_resource_type { pdf_none, pdf_extgstate, pdf_colorspace, pdf_pattern, pdf_shading, pdf_xobject, pdf_font, pdf_properties };\r
24 \r
25 char pdf_res_short[][5] = { "", "GS", "CS", "Pa", "Sh", "XObj", "F", "Prop" };\r
26 char pdf_res_long[][20] = { "", "ExtGState", "ColorSpace", "Pattern",\r
27 "Shading", "XObject", "Font", "Properties" };\r
28 \r
29 class PdfFile {\r
30 public:\r
31 PdfFile(FILE *_stream);\r
32 ~PdfFile();\r
33 \r
34 long puts(const char *str);\r
35 long puts(std::string ss) { return puts(ss.c_str()); }\r
36 long puts(Inkscape::SVGOStringStream &os) { return puts(os.str().c_str()); }\r
37 long puts(PdfXref *table);\r
38 long puts(PdfObject *object);\r
39 \r
40 long tell();\r
41 \r
42 PdfObject *begin_document(double version = 1.2);\r
43 void end_document(PdfObject *doc_info);\r
44 PdfObject *begin_page(int x0, int y0, int x1, int y1);\r
45 PdfObject *begin_page(int w, int h) { return begin_page(0,0, w, h); }\r
46 void end_page(PdfObject *page);\r
47 PdfObject *begin_object();\r
48 void end_object(PdfObject *object);\r
49 PdfObject *begin_resource(enum pdf_resource_type);\r
50 void end_resource(PdfObject *resource);\r
51 \r
52 protected:\r
53 FILE *fp;\r
54 long length;\r
55 long stream_pos;\r
56 \r
57 PdfXref *xref;\r
58 \r
59 PdfXref *pages;\r
60 PdfXref *resources;\r
61 \r
62 PdfObject *obj_root;\r
63 PdfObject *obj_catalog;\r
64 PdfObject *obj_info;\r
65 PdfObject *obj_resources;\r
66 PdfObject *obj_contents;\r
67 PdfObject *obj_length;\r
68 };\r
69 \r
70 \r
71 class PdfXref {\r
72 public:\r
73 PdfXref() { size = 10; table = (long*) malloc(size*sizeof(long)); entries = 0; }\r
74 ~PdfXref() { free(table); }\r
75 \r
76 long get_size() { return entries; }\r
77 long get_entry(long id) { return table[id]; }\r
78 \r
79 long alloc_id() {\r
80 entries++;\r
81 if (entries >= size) {\r
82 size = size*2;\r
83 table = (long *) realloc(table, size*sizeof(long));\r
84 }\r
85 return entries;\r
86 }\r
87 \r
88 void update_entry(long id, long offset) {\r
89 table[id] = offset;\r
90 }\r
91 \r
92 long add_entry(long offset = 0) {\r
93 long id = alloc_id();\r
94 update_entry(id, offset);\r
95 return id;\r
96 }\r
97 \r
98 protected:\r
99 long *table;\r
100 long entries;\r
101 long size;\r
102 };\r
103 \r
104 \r
105 class PdfObject : public Inkscape::SVGOStringStream {\r
106 public:\r
107 PdfObject(long i) : next(NULL) { id = i; setf(std::ios::fixed); }\r
108 ~PdfObject() {}\r
109 \r
110 long get_id() { return id; }\r
111 const char *get_name() { return name.str().c_str(); }\r
112 long get_length() { return str().length(); }\r
113 void set_type(enum pdf_resource_type type) { name << "/" << pdf_res_short[type] << id; }\r
114 PdfObject *next;\r
115 \r
116 protected:\r
117 long id;\r
118 Inkscape::SVGOStringStream name;\r
119 };\r
120 \r
121 PdfFile::PdfFile(FILE *_stream) {\r
122 fp = _stream;\r
123 xref = new PdfXref();\r
124 }\r
125 \r
126 PdfFile::~PdfFile() {\r
127 delete xref;\r
128 }\r
129 \r
130 long PdfFile::puts(const char *str) {\r
131 long res = fprintf(fp, "%s", str);\r
132 if (res >= 0) {\r
133 length += res;\r
134 res = length;\r
135 }\r
136 return res;\r
137 }\r
138 \r
139 long PdfFile::puts(PdfObject *object) {\r
140 xref->update_entry( object->get_id(), tell() );\r
141 return puts(object->str());\r
142 }\r
143 \r
144 long PdfFile::puts(PdfXref *table) {\r
145 Inkscape::SVGOStringStream os;\r
146 char buffer[32];\r
147 int i;\r
148 \r
149 int size = table->get_size() + 1;\r
150 \r
151 os << "xref\n";\r
152 os << "0" << " " << size << "\n";\r
153 \r
154 snprintf(buffer, sizeof(buffer), "%010d %05d %c \n", 0, 65535, 'f');\r
155 buffer[sizeof(buffer)-1] = 0;\r
156 os << buffer;\r
157 \r
158 for (i = 1; i < size; i++) {\r
159 snprintf(buffer, sizeof(buffer), "%010d %05d %c \n", (int)table->get_entry(i), 0, 'n');\r
160 buffer[sizeof(buffer)-1] = 0;\r
161 os << buffer;\r
162 }\r
163 \r
164 return puts(os);\r
165 }\r
166 \r
167 long PdfFile::tell() {\r
168 return length;\r
169 }\r
170 \r
171 PdfObject *PdfFile::begin_document(double version) {\r
172 Inkscape::SVGOStringStream os;\r
173 char bin[5] = {0x80|'B', 0x80|'i', 0x80|'n', 0x80|'!', 0};\r
174 length = 0;\r
175 pages = new PdfXref();;\r
176 \r
177 os << "%PDF-" << version << "\n";\r
178 os << "%" << bin << "\n";\r
179 \r
180 obj_info = begin_object();\r
181 *obj_info << "<<\n";\r
182 \r
183 obj_catalog = begin_object();\r
184 \r
185 obj_root = begin_object();\r
186 *obj_root << "<<\n";\r
187 *obj_root << " /Type /Catalog\n";\r
188 *obj_root << " /Pages " << obj_catalog->get_id() << " 0 R\n";\r
189 *obj_root << ">>\n";\r
190 end_object(obj_root);\r
191 \r
192 puts(os);\r
193 puts(obj_root);\r
194 \r
195 return obj_info;\r
196 }\r
197 \r
198 void PdfFile::end_document(PdfObject *doc_info) {\r
199 Inkscape::SVGOStringStream os;\r
200 long startxref;\r
201 int i;\r
202 \r
203 *doc_info << ">>\n";\r
204 end_object(doc_info);\r
205 puts(doc_info);\r
206 \r
207 *obj_catalog << "<<\n"\r
208 << " /Type /Pages\n"\r
209 << " /Count " << pages->get_size() << "\n"\r
210 << " /Kids [\n";\r
211 for (i=1; i<=pages->get_size(); i++) {\r
212 *obj_catalog << " " << pages->get_entry(i) << " 0 R\n";\r
213 }\r
214 *obj_catalog << " ]\n"\r
215 << ">>\n";\r
216 end_object(obj_catalog);\r
217 puts(obj_catalog);\r
218 \r
219 startxref = tell();\r
220 \r
221 puts(xref);\r
222 \r
223 os << "trailer\n"\r
224 << "<<\n"\r
225 << " /Size " << xref->get_size() << "\n"\r
226 << " /Root " << obj_root->get_id() << " 0 R\n"\r
227 << " /Info " << obj_info->get_id() << " 0 R\n"\r
228 << ">>\n";\r
229 os << "startxref\n"\r
230 << startxref << "\n";\r
231 os << "%%EOF\n";\r
232 \r
233 puts(os);\r
234 \r
235 delete pages;\r
236 delete obj_root;\r
237 delete obj_catalog;\r
238 delete obj_info;\r
239 }\r
240 \r
241 PdfObject *PdfFile::begin_page(int x0, int y0, int x1, int y1) {\r
242 Inkscape::SVGOStringStream os;\r
243 \r
244 resources = new PdfXref[11]();\r
245 PdfObject *obj_page = begin_object();\r
246 \r
247 obj_resources = begin_object();\r
248 obj_contents = begin_object();\r
249 obj_length = begin_object();\r
250 \r
251 *obj_page << "<<\n"\r
252 << " /Type /Page\n"\r
253 << " /Parent " << obj_catalog->get_id() << " 0 R\n"\r
254 << " /MediaBox [ " << x0 << " " << y0 << " " << x1 << " " << y1 << " ]\n"\r
255 << " /Resources " << obj_resources->get_id() << " 0 R\n"\r
256 << " /Contents " << obj_contents->get_id() << " 0 R\n"\r
257 << ">>\n";\r
258 end_object(obj_page);\r
259 puts(obj_page);\r
260 \r
261 pages->add_entry( obj_page->get_id() );\r
262 \r
263 *obj_contents << "<<\n"\r
264 << " /Length " << obj_length->get_id() << " 0 R\n"\r
265 << ">>\n"\r
266 << "stream\n";\r
267 stream_pos = obj_contents->get_length();\r
268 \r
269 return obj_contents;\r
270 }\r
271 \r
272 void PdfFile::end_page(PdfObject *page) {\r
273 long stream_length = page->get_length() - stream_pos;\r
274 \r
275 *page << "endstream\n";\r
276 end_object(page);\r
277 puts(page);\r
278 \r
279 *obj_length << stream_length << "\n";\r
280 end_object(obj_length);\r
281 puts(obj_length);\r
282 \r
283 *obj_resources << "<<\n";\r
284 *obj_resources << " /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n";\r
285 int res;\r
286 for (res=pdf_extgstate; res<=pdf_properties; res++) {\r
287 if (resources[res].get_size()) {\r
288 *obj_resources << " /" << pdf_res_long[res] << "\n";\r
289 *obj_resources << " <<\n";\r
290 int i;\r
291 for (i=1; i<=resources[res].get_size(); i++) {\r
292 int id = resources[res].get_entry(i);\r
293 *obj_resources << " /"\r
294 << pdf_res_short[res] << id\r
295 << " " << id << " 0 R\n";\r
296 }\r
297 *obj_resources << " >>\n";\r
298 }\r
299 }\r
300 *obj_resources << ">>\n";\r
301 end_object(obj_resources);\r
302 puts(obj_resources);\r
303 \r
304 PdfObject *o = obj_resources->next;\r
305 while (o) {\r
306 PdfObject *t = o->next;\r
307 puts(o);\r
308 delete o;\r
309 o = t;\r
310 }\r
311 \r
312 delete obj_resources;\r
313 delete obj_length;\r
314 delete obj_contents;\r
315 \r
316 delete[] resources;\r
317 }\r
318 \r
319 PdfObject *PdfFile::begin_object() {\r
320 long id = xref->add_entry();\r
321 PdfObject *obj = new PdfObject(id);\r
322 *obj << id << " 0 obj\n";\r
323 return obj;\r
324 }\r
325 \r
326 void PdfFile::end_object(PdfObject *object) {\r
327 *object << "endobj\n";\r
328 }\r
329 \r
330 PdfObject *PdfFile::begin_resource(enum pdf_resource_type res) {\r
331 PdfObject *obj = begin_object();\r
332 if (res != pdf_none) {\r
333 resources[res].add_entry(obj->get_id());\r
334 obj->set_type(res);\r
335 }\r
336 obj->next = obj_resources->next;\r
337 obj_resources->next = obj;\r
338 return obj;\r
339 }\r
340 \r
341 void PdfFile::end_resource(PdfObject *resource) {\r
342 end_object(resource);\r
343 }\r
344 \r
345 \r
346 #endif /* !PDF_MINI_H_SEEN */\r