Code

PDF export patch by Ulf Erikson
[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     sprintf(buffer, "%010d %05d %c \n", 0, 65535, 'f');\r
155     os << buffer;\r
156 \r
157     for (i = 1; i < size; i++) {\r
158         sprintf(buffer, "%010d %05d %c \n", table->get_entry(i), 0, 'n');\r
159         os << buffer;\r
160     }\r
161 \r
162     return puts(os);\r
163 }\r
164 \r
165 long PdfFile::tell() {\r
166     return length;\r
167 }\r
168 \r
169 PdfObject *PdfFile::begin_document(double version) {\r
170     Inkscape::SVGOStringStream os;\r
171 \r
172     length = 0;\r
173     pages = new PdfXref();;\r
174 \r
175     os << "%PDF-" << version << "\n";\r
176 \r
177     obj_info = begin_object();\r
178     *obj_info << "<<\n";\r
179 \r
180     obj_catalog = begin_object();\r
181 \r
182     obj_root = begin_object();\r
183     *obj_root << "<<\n";\r
184     *obj_root << "  /Type /Catalog\n";\r
185     *obj_root << "  /Pages " << obj_catalog->get_id() << " 0 R\n";\r
186     *obj_root << ">>\n";\r
187     end_object(obj_root);\r
188 \r
189     puts(os);\r
190     puts(obj_root);\r
191 \r
192     return obj_info;\r
193 }\r
194 \r
195 void PdfFile::end_document(PdfObject *doc_info) {\r
196     Inkscape::SVGOStringStream os;\r
197     long startxref;\r
198     int i;\r
199 \r
200     *doc_info << ">>\n";\r
201     end_object(doc_info);\r
202     puts(doc_info);\r
203     \r
204     *obj_catalog << "<<\n"\r
205                  << "  /Type /Pages\n"\r
206                  << "  /Count " << pages->get_size() << "\n"\r
207                  << "  /Kids [\n";\r
208     for (i=1; i<=pages->get_size(); i++) {\r
209         *obj_catalog << "    " << pages->get_entry(i) << " 0 R\n";\r
210     }\r
211     *obj_catalog << "  ]\n"\r
212                  << ">>\n";\r
213     end_object(obj_catalog);\r
214     puts(obj_catalog);\r
215     \r
216     startxref = tell();\r
217 \r
218     puts(xref);\r
219     \r
220     os << "trailer\n"\r
221        << "<<\n"\r
222        << "  /Size " << xref->get_size() << "\n"\r
223        << "  /Root " << obj_root->get_id() << " 0 R\n"\r
224        << "  /Info " << obj_info->get_id() << " 0 R\n"\r
225        << ">>\n";\r
226     os << "startxref\n"\r
227        << startxref << "\n";\r
228     os << "%%EOF\n";\r
229 \r
230     puts(os);\r
231 \r
232     delete pages;\r
233     delete obj_root;\r
234     delete obj_catalog;\r
235     delete obj_info;\r
236 }\r
237 \r
238 PdfObject *PdfFile::begin_page(int x0, int y0, int x1, int y1) {\r
239     Inkscape::SVGOStringStream os;\r
240 \r
241     resources = new PdfXref[11]();\r
242     PdfObject *obj_page = begin_object();\r
243    \r
244     obj_resources = begin_object();\r
245     obj_contents  = begin_object();\r
246     obj_length = begin_object();\r
247     \r
248     *obj_page << "<<\n"\r
249               << "  /Type /Page\n"\r
250               << "  /Parent " << obj_catalog->get_id() << " 0 R\n"\r
251               << "  /MediaBox [ " << x0 << " " << y0 << " " << x1 << " " << y1 << " ]\n"\r
252               << "  /Resources " << obj_resources->get_id() << " 0 R\n"\r
253               << "  /Contents " << obj_contents->get_id() << " 0 R\n"\r
254               << ">>\n";\r
255     end_object(obj_page);\r
256     puts(obj_page);\r
257 \r
258     pages->add_entry( obj_page->get_id() );\r
259 \r
260     *obj_contents << "<<\n"\r
261                   << "  /Length " << obj_length->get_id() << " 0 R\n"\r
262                   << ">>\n"\r
263                   << "stream\n";\r
264     stream_pos = obj_contents->get_length();\r
265 \r
266     return obj_contents;\r
267 }\r
268 \r
269 void PdfFile::end_page(PdfObject *page) {\r
270     long stream_length = page->get_length() - stream_pos;\r
271     \r
272     *page << "endstream\n";\r
273     end_object(page);\r
274     puts(page);\r
275     \r
276     *obj_length << stream_length << "\n";\r
277     end_object(obj_length);\r
278     puts(obj_length);\r
279 \r
280     *obj_resources << "<<\n";\r
281     *obj_resources << "  /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n";\r
282     int res;\r
283     for (res=pdf_extgstate; res<=pdf_properties; res++) {\r
284         if (resources[res].get_size()) {\r
285             *obj_resources << "  /" << pdf_res_long[res] << "\n";\r
286             *obj_resources << "  <<\n";\r
287             int i;\r
288             for (i=1; i<=resources[res].get_size(); i++) {\r
289                 int id = resources[res].get_entry(i);\r
290                 *obj_resources << "    /"\r
291                                << pdf_res_short[res] << id\r
292                                << " " << id << " 0 R\n";\r
293             }\r
294             *obj_resources << "  >>\n";\r
295         }\r
296     }\r
297     *obj_resources << ">>\n";\r
298     end_object(obj_resources);\r
299     puts(obj_resources);\r
300 \r
301     PdfObject *o = obj_resources->next;\r
302     while (o) {\r
303         PdfObject *t = o->next;\r
304         puts(o);\r
305         delete o;\r
306         o = t;\r
307     }\r
308     \r
309     delete obj_resources;\r
310     delete obj_length;\r
311     delete obj_contents;\r
312     \r
313     delete resources;\r
314 }\r
315 \r
316 PdfObject *PdfFile::begin_object() {\r
317     long id = xref->add_entry();\r
318     PdfObject *obj = new PdfObject(id);\r
319     *obj << id << " 0 obj\n";\r
320     return obj;\r
321 }\r
322 \r
323 void PdfFile::end_object(PdfObject *object) {\r
324     *object << "endobj\n";\r
325 }\r
326 \r
327 PdfObject *PdfFile::begin_resource(enum pdf_resource_type res) {\r
328     PdfObject *obj = begin_object();\r
329     if (res != pdf_none) {\r
330         resources[res].add_entry(obj->get_id());\r
331         obj->set_type(res);\r
332     }\r
333     obj->next = obj_resources->next;\r
334     obj_resources->next = obj;\r
335     return obj;\r
336 }\r
337 \r
338 void PdfFile::end_resource(PdfObject *resource) {\r
339     end_object(resource);\r
340 }\r
341 \r
342 \r
343 #endif /* !PDF_MINI_H_SEEN */\r