Code

r13108@tres: ted | 2006-07-30 12:27:51 -0700
[inkscape.git] / src / extension / internal / pdf-mini.h
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