1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2 /* AbiWord
3 * Copyright (C) 1998-2005 AbiSource, Inc.
4 * Copyright (C) 2005 Dom Lachowicz <cinamod@hotmail.com>
5 * Copyright (C) 2008 Robert Staudinger
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301 USA.
21 */
22
23 #include <string.h>
24 #include "ut_debugmsg.h"
25 #include "ut_string.h"
26 #include "ie_exp_PDF.h"
27 #include "pd_Document.h"
28 #include "gr_CairoPrintGraphics.h"
29 #include "fv_View.h"
30 #include "fl_DocLayout.h"
31 #include "ap_EditMethods.h"
32 #include "xap_App.h"
33 #include "ut_misc.h"
34
35 #include <cairo-pdf.h>
36 #include <cairo-ps.h>
37
38 #if 0
39 #include <cairo-svg.h>
40 #endif
41
42 #include <gsf/gsf-output-memory.h>
43 #include <gsf/gsf-input-stdio.h>
44 #include <glib/gstdio.h>
45
46 #include <unistd.h>
47 #include <set>
48
49 /*****************************************************************/
50 /*****************************************************************/
51
52 static cairo_status_t
ie_exp_cairo_write_func(void * closure,const unsigned char * data,unsigned int length)53 ie_exp_cairo_write_func (void *closure, const unsigned char *data, unsigned int length)
54 {
55 if (!gsf_output_write((GsfOutput*)closure, length, data))
56 return CAIRO_STATUS_WRITE_ERROR;
57 return CAIRO_STATUS_SUCCESS;
58 }
59
60 class ABI_EXPORT IE_Exp_Cairo : public IE_Exp
61 {
62 public:
63 typedef enum
64 {
65 BACKEND_PS,
66 BACKEND_PDF,
67 BACKEND_SVG
68 } Format;
69
70 IE_Exp_Cairo::Format mFormat;
71
IE_Exp_Cairo(PD_Document * pDocument,IE_Exp_Cairo::Format format)72 IE_Exp_Cairo(PD_Document * pDocument, IE_Exp_Cairo::Format format)
73 : IE_Exp(pDocument), mFormat(format)
74 {
75 }
76
~IE_Exp_Cairo()77 virtual ~IE_Exp_Cairo()
78 {
79 }
80
_writeDocument(void)81 virtual UT_Error _writeDocument(void)
82 {
83 cairo_t *cr = NULL;
84 cairo_surface_t *surface = NULL;
85 GR_CairoPrintGraphics *print_graphics = NULL;
86 FL_DocLayout *pDocLayout = NULL;
87 FV_View *printView = NULL;
88
89 std::set<UT_sint32> pages;
90 const std::string & pages_prop = getProperty ("pages");
91
92 double width, height;
93
94 width = getDoc()->m_docPageSize.Width (DIM_IN);
95 height = getDoc()->m_docPageSize.Height (DIM_IN);
96
97 // Cairo expects the width/height of the surface in points, with 1 point == 1/72 inch). For details, see
98 // http://cairographics.org/manual/cairo-pdf-surface.html#cairo-pdf-surface-create-for-stream
99 // Fixes bug 11343 - export to pdf uses wrong document size
100 UT_uint32 dpi = 72;
101 if (BACKEND_PDF == mFormat)
102 surface = cairo_pdf_surface_create_for_stream(ie_exp_cairo_write_func, getFp(), width * dpi, height * dpi);
103 else if (BACKEND_PS == mFormat)
104 surface = cairo_ps_surface_create_for_stream(ie_exp_cairo_write_func, getFp(), width * dpi, height * dpi);
105 else if (BACKEND_SVG == mFormat)
106 {
107 // surface = cairo_svg_surface_create_for_stream(ie_exp_cairo_write_func, getFp(), width * dpi, height * dpi);
108 return UT_ERROR;
109 }
110 else
111 {
112 return UT_ERROR;
113 }
114
115 cr = cairo_create(surface);
116 cairo_surface_destroy(surface), surface = NULL;
117
118 print_graphics = new GR_CairoPrintGraphics(cr, dpi);
119 pDocLayout = new FL_DocLayout(getDoc(), print_graphics);
120 printView = new FV_View(XAP_App::getApp(), 0, pDocLayout);
121 printView->getLayout()->fillLayouts();
122 printView->getLayout()->formatAll();
123 printView->getLayout()->recalculateTOCFields();
124
125 // TODO lifecycle of "surface" and "cr"?
126
127 if (!pages_prop.empty())
128 {
129 char **page_descriptions = g_strsplit(pages_prop.c_str(), ",", -1);
130
131 int i = 0;
132 while (page_descriptions[i] != NULL)
133 {
134 char *description = page_descriptions[i];
135 i++;
136
137 int start_page, end_page;
138
139 if (2 == sscanf(description, "%d-%d", &start_page, &end_page))
140 {
141 }
142 else if (1 == sscanf(description, "%d", &start_page))
143 {
144 end_page = start_page;
145 }
146 else
147 {
148 // invalid page specification
149 continue;
150 }
151
152 for (int pageno = start_page; pageno <= end_page; pageno++)
153 {
154 if ((pageno > 0) && (pageno <= (int)pDocLayout->countPages()))
155 pages.insert(pageno);
156 }
157 }
158
159 g_strfreev(page_descriptions);
160 }
161
162 if (pages.empty())
163 {
164 for (UT_sint32 i = 1; i <= pDocLayout->countPages(); i++)
165 {
166 pages.insert(i);
167 }
168 }
169
170 s_actuallyPrint (getDoc(), print_graphics,
171 printView, getFileName(),
172 1, true,
173 pDocLayout->getWidth(), pDocLayout->getHeight() / pDocLayout->countPages(),
174 pages);
175
176 DELETEP(print_graphics);
177
178 DELETEP(pDocLayout);
179 DELETEP(printView);
180 DELETEP(print_graphics);
181 return UT_OK;
182 }
183 };
184
185 /*****************************************************************/
186 /*****************************************************************/
187
IE_Exp_PS_Sniffer()188 IE_Exp_PS_Sniffer::IE_Exp_PS_Sniffer()
189 : IE_ExpSniffer("application/postscript", false)
190 {
191 }
192
~IE_Exp_PS_Sniffer()193 IE_Exp_PS_Sniffer::~IE_Exp_PS_Sniffer ()
194 {
195 }
196
supportsMIME(const char * szMIME)197 UT_Confidence_t IE_Exp_PS_Sniffer::supportsMIME (const char * szMIME)
198 {
199 if(!g_ascii_strcasecmp(szMIME, "application/postscript"))
200 return UT_CONFIDENCE_PERFECT;
201 return UT_CONFIDENCE_ZILCH;
202 }
203
recognizeSuffix(const char * szSuffix)204 bool IE_Exp_PS_Sniffer::recognizeSuffix (const char * szSuffix)
205 {
206 return !g_ascii_strcasecmp(szSuffix,".ps");
207 }
208
getDlgLabels(const char ** szDesc,const char ** szSuffixList,IEFileType * ft)209 bool IE_Exp_PS_Sniffer::getDlgLabels (const char ** szDesc,
210 const char ** szSuffixList,
211 IEFileType * ft)
212 {
213 *szDesc = "Postscript (.ps)";
214 *szSuffixList = "*.ps";
215 *ft = getFileType();
216 return true;
217 }
218
constructExporter(PD_Document * pDocument,IE_Exp ** ppie)219 UT_Error IE_Exp_PS_Sniffer::constructExporter (PD_Document * pDocument,
220 IE_Exp ** ppie)
221 {
222 *ppie = new IE_Exp_Cairo(pDocument, IE_Exp_Cairo::BACKEND_PS);
223 return UT_OK;
224 }
225
226 /*****************************************************************/
227 /*****************************************************************/
228
IE_Exp_SVG_Sniffer()229 IE_Exp_SVG_Sniffer::IE_Exp_SVG_Sniffer()
230 : IE_ExpSniffer("image/svg+xml", false)
231 {
232 }
233
~IE_Exp_SVG_Sniffer()234 IE_Exp_SVG_Sniffer::~IE_Exp_SVG_Sniffer ()
235 {
236 }
237
supportsMIME(const char * szMIME)238 UT_Confidence_t IE_Exp_SVG_Sniffer::supportsMIME (const char * szMIME)
239 {
240 if(!g_ascii_strcasecmp(szMIME, "image/svg+xml") ||
241 !g_ascii_strcasecmp(szMIME, "image/svg") ||
242 !g_ascii_strcasecmp(szMIME, "image/svg-xml") ||
243 !g_ascii_strcasecmp(szMIME, "image/vnd.adobe.svg+xml") ||
244 !g_ascii_strcasecmp(szMIME, "text/xml-svg"))
245 return UT_CONFIDENCE_PERFECT;
246 return UT_CONFIDENCE_ZILCH;
247 }
248
recognizeSuffix(const char * szSuffix)249 bool IE_Exp_SVG_Sniffer::recognizeSuffix (const char * szSuffix)
250 {
251 return !g_ascii_strcasecmp(szSuffix,".svg");
252 }
253
getDlgLabels(const char ** szDesc,const char ** szSuffixList,IEFileType * ft)254 bool IE_Exp_SVG_Sniffer::getDlgLabels (const char ** szDesc,
255 const char ** szSuffixList,
256 IEFileType * ft)
257 {
258 *szDesc = "Scalable Vector Graphics (.svg)";
259 *szSuffixList = "*.svg";
260 *ft = getFileType();
261 return true;
262 }
263
constructExporter(PD_Document * pDocument,IE_Exp ** ppie)264 UT_Error IE_Exp_SVG_Sniffer::constructExporter (PD_Document * pDocument,
265 IE_Exp ** ppie)
266 {
267 *ppie = new IE_Exp_Cairo(pDocument, IE_Exp_Cairo::BACKEND_SVG);
268 return UT_OK;
269 }
270
271 /*****************************************************************/
272 /*****************************************************************/
273
IE_Exp_PDF_Sniffer()274 IE_Exp_PDF_Sniffer::IE_Exp_PDF_Sniffer()
275 : IE_ExpSniffer("application/pdf", false)
276 {
277 }
278
~IE_Exp_PDF_Sniffer()279 IE_Exp_PDF_Sniffer::~IE_Exp_PDF_Sniffer ()
280 {
281 }
282
supportsMIME(const char * szMIME)283 UT_Confidence_t IE_Exp_PDF_Sniffer::supportsMIME (const char * szMIME)
284 {
285 if(!g_ascii_strcasecmp(szMIME, "application/pdf"))
286 return UT_CONFIDENCE_PERFECT;
287 return UT_CONFIDENCE_ZILCH;
288 }
289
recognizeSuffix(const char * szSuffix)290 bool IE_Exp_PDF_Sniffer::recognizeSuffix (const char * szSuffix)
291 {
292 return !g_ascii_strcasecmp(szSuffix,".pdf");
293 }
294
getDlgLabels(const char ** szDesc,const char ** szSuffixList,IEFileType * ft)295 bool IE_Exp_PDF_Sniffer::getDlgLabels (const char ** szDesc,
296 const char ** szSuffixList,
297 IEFileType * ft)
298 {
299 *szDesc = "Portable Document Format (.pdf)";
300 *szSuffixList = "*.pdf";
301 *ft = getFileType();
302 return true;
303 }
304
constructExporter(PD_Document * pDocument,IE_Exp ** ppie)305 UT_Error IE_Exp_PDF_Sniffer::constructExporter (PD_Document * pDocument,
306 IE_Exp ** ppie)
307 {
308 *ppie = new IE_Exp_Cairo(pDocument, IE_Exp_Cairo::BACKEND_PDF);
309 return UT_OK;
310 }
311
312 /*****************************************************************/
313 /*****************************************************************/
314