Code

PNG output for Cairo renderer
[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(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