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(double x0, double y0, double x1, double y1);\r
45 PdfObject *begin_page(double w, double 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() { table.push_back(0); }\r
74 ~PdfXref() { table.clear(); }\r
75 \r
76 long get_size() { return (long) table.size() - 1; }\r
77 long get_entry(long id) { return table.at(id); }\r
78 \r
79 void update_entry(long id, long offset) {\r
80 table.at(id) = offset;\r
81 }\r
82 \r
83 long add_entry(long offset = 0) {\r
84 table.push_back(offset);\r
85 return get_size();\r
86 }\r
87 \r
88 protected:\r
89 std::vector<long> table;\r
90 };\r
91 \r
92 \r
93 class PdfObject : public Inkscape::SVGOStringStream {\r
94 public:\r
95 PdfObject(long i) : next(NULL), name(NULL) { id = i; setf(std::ios::fixed); }\r
96 ~PdfObject() { if (name) g_free(name); }\r
97 \r
98 long get_id() { return id; }\r
99 const char *get_name() { return name; }\r
100 long get_length() { return str().length(); }\r
101 void set_type(enum pdf_resource_type type) {\r
102 if (name) g_free(name);\r
103 name = g_strdup_printf("/%s%ld", pdf_res_short[type], id);\r
104 }\r
105 PdfObject *next;\r
106 \r
107 protected:\r
108 long id;\r
109 gchar *name;\r
110 };\r
111 \r
112 PdfFile::PdfFile(FILE *_stream) {\r
113 fp = _stream;\r
114 xref = new PdfXref();\r
115 }\r
116 \r
117 PdfFile::~PdfFile() {\r
118 delete xref;\r
119 }\r
120 \r
121 long PdfFile::puts(const char *str) {\r
122 long res = fprintf(fp, "%s", str);\r
123 if (res >= 0) {\r
124 length += res;\r
125 res = length;\r
126 }\r
127 return res;\r
128 }\r
129 \r
130 long PdfFile::puts(PdfObject *object) {\r
131 xref->update_entry( object->get_id(), tell() );\r
132 return puts(object->str());\r
133 }\r
134 \r
135 long PdfFile::puts(PdfXref *table) {\r
136 Inkscape::SVGOStringStream os;\r
137 char buffer[32];\r
138 int i;\r
139 \r
140 int size = table->get_size() + 1;\r
141 \r
142 os << "xref\n";\r
143 os << "0" << " " << size << "\n";\r
144 \r
145 snprintf(buffer, sizeof(buffer), "%010d %05d %c \n", 0, 65535, 'f');\r
146 buffer[sizeof(buffer)-1] = 0;\r
147 os << buffer;\r
148 \r
149 for (i = 1; i < size; i++) {\r
150 snprintf(buffer, sizeof(buffer), "%010d %05d %c \n", (int)table->get_entry(i), 0, 'n');\r
151 buffer[sizeof(buffer)-1] = 0;\r
152 os << buffer;\r
153 }\r
154 \r
155 return puts(os);\r
156 }\r
157 \r
158 long PdfFile::tell() {\r
159 return length;\r
160 }\r
161 \r
162 PdfObject *PdfFile::begin_document(double version) {\r
163 Inkscape::SVGOStringStream os;\r
164 char bin[5] = {0x80|'B', 0x80|'i', 0x80|'n', 0x80|'!', 0};\r
165 length = 0;\r
166 pages = new PdfXref();;\r
167 \r
168 os << "%PDF-" << version << "\n";\r
169 os << "%" << bin << "\n";\r
170 \r
171 obj_info = begin_object();\r
172 *obj_info << "<<\n";\r
173 \r
174 obj_catalog = begin_object();\r
175 \r
176 obj_root = begin_object();\r
177 *obj_root << "<<\n";\r
178 *obj_root << " /Type /Catalog\n";\r
179 *obj_root << " /Pages " << obj_catalog->get_id() << " 0 R\n";\r
180 *obj_root << ">>\n";\r
181 end_object(obj_root);\r
182 \r
183 puts(os);\r
184 puts(obj_root);\r
185 \r
186 return obj_info;\r
187 }\r
188 \r
189 void PdfFile::end_document(PdfObject *doc_info) {\r
190 Inkscape::SVGOStringStream os;\r
191 long startxref;\r
192 int i;\r
193 \r
194 *doc_info << ">>\n";\r
195 end_object(doc_info);\r
196 puts(doc_info);\r
197 \r
198 *obj_catalog << "<<\n"\r
199 << " /Type /Pages\n"\r
200 << " /Count " << pages->get_size() << "\n"\r
201 << " /Kids [\n";\r
202 for (i=1; i<=pages->get_size(); i++) {\r
203 *obj_catalog << " " << pages->get_entry(i) << " 0 R\n";\r
204 }\r
205 *obj_catalog << " ]\n"\r
206 << ">>\n";\r
207 end_object(obj_catalog);\r
208 puts(obj_catalog);\r
209 \r
210 startxref = tell();\r
211 \r
212 puts(xref);\r
213 \r
214 os << "trailer\n"\r
215 << "<<\n"\r
216 << " /Size " << xref->get_size() << "\n"\r
217 << " /Root " << obj_root->get_id() << " 0 R\n"\r
218 << " /Info " << obj_info->get_id() << " 0 R\n"\r
219 << ">>\n";\r
220 os << "startxref\n"\r
221 << startxref << "\n";\r
222 os << "%%EOF\n";\r
223 \r
224 puts(os);\r
225 \r
226 delete pages;\r
227 delete obj_root;\r
228 delete obj_catalog;\r
229 delete obj_info;\r
230 }\r
231 \r
232 PdfObject *PdfFile::begin_page(double x0, double y0, double x1, double y1) {\r
233 Inkscape::SVGOStringStream os;\r
234 \r
235 resources = new PdfXref[11]();\r
236 PdfObject *obj_page = begin_object();\r
237 \r
238 obj_resources = begin_object();\r
239 obj_contents = begin_object();\r
240 obj_length = begin_object();\r
241 \r
242 *obj_page << "<<\n"\r
243 << " /Type /Page\n"\r
244 << " /Parent " << obj_catalog->get_id() << " 0 R\n"\r
245 << " /MediaBox [ " << x0 << " " << y0 << " " << x1 << " " << y1 << " ]\n"\r
246 << " /Resources " << obj_resources->get_id() << " 0 R\n"\r
247 << " /Contents " << obj_contents->get_id() << " 0 R\n"\r
248 << " /Group\n"\r
249 << " << /Type /Group\n"\r
250 << " /S /Transparency\n"\r
251 << " /CS /DeviceRGB\n"\r
252 << " >>\n"\r
253 << ">>\n";\r
254 end_object(obj_page);\r
255 puts(obj_page);\r
256 \r
257 pages->add_entry( obj_page->get_id() );\r
258 \r
259 *obj_contents << "<<\n"\r
260 << " /Length " << obj_length->get_id() << " 0 R\n"\r
261 << ">>\n"\r
262 << "stream\n";\r
263 puts(obj_contents);\r
264 stream_pos = ftell(fp);\r
265 \r
266 return obj_contents;\r
267 }\r
268 \r
269 void PdfFile::end_page(PdfObject *page) {\r
270 long stream_length = ftell(fp) - stream_pos;\r
271 \r
272 puts("endstream\n");\r
273 puts("endobj\n");\r
274 \r
275 *obj_length << stream_length << "\n";\r
276 end_object(obj_length);\r
277 puts(obj_length);\r
278 \r
279 *obj_resources << "<<\n";\r
280 *obj_resources << " /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n";\r
281 int res;\r
282 for (res=pdf_extgstate; res<=pdf_properties; res++) {\r
283 if (resources[res].get_size()) {\r
284 *obj_resources << " /" << pdf_res_long[res] << "\n";\r
285 *obj_resources << " <<\n";\r
286 int i;\r
287 for (i=1; i<=resources[res].get_size(); i++) {\r
288 int id = resources[res].get_entry(i);\r
289 *obj_resources << " /"\r
290 << pdf_res_short[res] << id\r
291 << " " << id << " 0 R\n";\r
292 }\r
293 *obj_resources << " >>\n";\r
294 }\r
295 }\r
296 *obj_resources << ">>\n";\r
297 end_object(obj_resources);\r
298 puts(obj_resources);\r
299 \r
300 PdfObject *o = obj_resources->next;\r
301 while (o) {\r
302 PdfObject *t = o->next;\r
303 puts(o);\r
304 delete o;\r
305 o = t;\r
306 }\r
307 \r
308 delete obj_resources;\r
309 delete obj_length;\r
310 delete obj_contents;\r
311 \r
312 delete[] resources;\r
313 }\r
314 \r
315 PdfObject *PdfFile::begin_object() {\r
316 long id = xref->add_entry();\r
317 PdfObject *obj = new PdfObject(id);\r
318 *obj << id << " 0 obj\n";\r
319 return obj;\r
320 }\r
321 \r
322 void PdfFile::end_object(PdfObject *object) {\r
323 *object << "endobj\n";\r
324 }\r
325 \r
326 PdfObject *PdfFile::begin_resource(enum pdf_resource_type res) {\r
327 PdfObject *obj = begin_object();\r
328 if (res != pdf_none) {\r
329 resources[res].add_entry(obj->get_id());\r
330 obj->set_type(res);\r
331 }\r
332 obj->next = obj_resources->next;\r
333 obj_resources->next = obj;\r
334 return obj;\r
335 }\r
336 \r
337 void PdfFile::end_resource(PdfObject *resource) {\r
338 end_object(resource);\r
339 }\r
340 \r
341 \r
342 #endif /* !PDF_MINI_H_SEEN */\r