1 /* AbiWord
2  * Copyright (C) 2000 AbiSource, Inc.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301 USA.
18  */
19 
20 #include <stdio.h>
21 #include <string.h>
22 #include <string>
23 #include <map>
24 
25 #include "ap_Convert.h"
26 #include "xap_App.h"
27 #include "ie_exp.h"
28 #include "ie_imp.h"
29 #include "ut_types.h"
30 #include "ut_string.h"
31 #include "ut_string_class.h"
32 #include "ut_misc.h"
33 
34 #include "ut_assert.h"
35 #include "ut_debugmsg.h"
36 
37 #include "gr_DrawArgs.h"
38 #include "gr_Graphics.h"
39 #include "fv_View.h"
40 #include "fl_BlockLayout.h"
41 #include "ap_EditMethods.h"
42 
43 // needed for convertToPNG
44 #include "ie_impGraphic.h"
45 #include "ut_bytebuf.h"
46 #include "ie_mailmerge.h"
47 
48 //////////////////////////////////////////////////////////////////
49 
AP_Convert(int inVerbose)50 AP_Convert::AP_Convert(int inVerbose)
51 	: m_iVerbose(inVerbose)
52 {
53 }
54 
~AP_Convert(void)55 AP_Convert::~AP_Convert(void)
56 {
57 }
58 
setMergeSource(const char * source)59 void AP_Convert::setMergeSource (const char * source)
60 {
61 	m_mergeSource = source;
62 }
63 
64 /////////////////////////////////////////////////////////////////
65 
66 class ABI_EXPORT Save_MailMerge_Listener : public IE_MailMerge::IE_MailMerge_Listener
67 {
68 public:
69 
Save_MailMerge_Listener(PD_Document * pDoc,const UT_UTF8String & szOut,IEFileType out_ieft,const UT_UTF8String & szExpProps)70 	explicit Save_MailMerge_Listener (PD_Document * pDoc,
71 									  const UT_UTF8String & szOut,
72 									  IEFileType out_ieft,
73 									  const UT_UTF8String & szExpProps)
74 		: IE_MailMerge::IE_MailMerge_Listener (), m_doc (pDoc),
75 		  m_szFile(szOut), m_count(0), m_ieft(out_ieft), m_expProps(szExpProps)
76 		{
77 		}
78 
~Save_MailMerge_Listener()79 	virtual ~Save_MailMerge_Listener ()
80 		{
81 		}
82 
getMergeDocument() const83 	virtual PD_Document* getMergeDocument () const
84 		{
85 			return m_doc;
86 		}
87 
fireUpdate()88 	virtual bool fireUpdate ()
89 		{
90 			if (!m_doc)
91 				return false;
92 
93 			UT_UTF8String out_file (UT_UTF8String_sprintf("%s-%d",
94 														  m_szFile.utf8_str(),
95 														  m_count++));
96 
97 			if (UT_OK == static_cast<AD_Document*>(m_doc)->saveAs (out_file.utf8_str(), m_ieft, m_expProps.utf8_str()))
98 				return true;
99 			return false;
100 		}
101 
102 private:
103 	PD_Document *m_doc;
104 	UT_UTF8String m_szFile;
105 	UT_uint32 m_count;
106 	IEFileType m_ieft;
107 	UT_UTF8String m_expProps;
108 };
109 
110 class ABI_EXPORT Print_MailMerge_Listener : public IE_MailMerge::IE_MailMerge_Listener
111 {
112 public:
113 
Print_MailMerge_Listener(PD_Document * pd,GR_Graphics * pGraphics,const UT_UTF8String & szFile)114 	explicit Print_MailMerge_Listener (PD_Document * pd,
115 									   GR_Graphics * pGraphics,
116 									   const UT_UTF8String & szFile)
117 		: IE_MailMerge::IE_MailMerge_Listener (), m_doc (pd),
118 		  m_szFile(szFile), m_pGraphics(pGraphics), m_bPrintedFirstPage(false), m_iter(1)
119 		{
120 		}
121 
~Print_MailMerge_Listener()122 	virtual ~Print_MailMerge_Listener ()
123 		{
124 			if (m_bPrintedFirstPage)
125 				m_pGraphics->endPrint();
126 		}
127 
getMergeDocument() const128 	virtual PD_Document* getMergeDocument () const
129 		{
130 			return m_doc;
131 		}
132 
fireUpdate()133 	virtual bool fireUpdate ()
134 		{
135 			FL_DocLayout *pDocLayout = new FL_DocLayout(m_doc,m_pGraphics);
136 			FV_View printView(XAP_App::getApp(),0,pDocLayout);
137 			//pDocLayout->setView (&printView);
138 			pDocLayout->fillLayouts();
139 			pDocLayout->formatAll();
140 			pDocLayout->recalculateTOCFields();
141 
142 			if (!m_bPrintedFirstPage)
143 				if (m_pGraphics->startPrint())
144 					m_bPrintedFirstPage = true;
145 
146 
147 			if (m_bPrintedFirstPage) {
148 
149 				dg_DrawArgs da;
150 				memset(&da, 0, sizeof(da));
151 				da.pG = m_pGraphics;
152 
153 				for (UT_sint32 k = 1; (k <= pDocLayout->countPages()); k++)
154 				{
155 					UT_uint32 iHeight = pDocLayout->getHeight() / pDocLayout->countPages();
156 					m_pGraphics->m_iRasterPosition = (k-1)*iHeight;
157 					m_pGraphics->startPage(m_szFile.utf8_str(), m_iter++, printView.getPageSize().isPortrait(), pDocLayout->getWidth(), iHeight);
158 					printView.draw(k-1, &da);
159 				}
160 			}
161 
162 			DELETEP(pDocLayout);
163 
164 			// sure, we'll process more data if it exists
165 			return true;
166 		}
167 
168 private:
169 	PD_Document *m_doc;
170 	UT_UTF8String m_szFile;
171 
172 	GR_Graphics * m_pGraphics;
173 
174 	bool m_bPrintedFirstPage;
175 	UT_uint32 m_iter;
176 };
177 
handleMerge(const char * szMailMergeFile,IE_MailMerge::IE_MailMerge_Listener & listener)178 static UT_Error handleMerge(const char * szMailMergeFile,
179 			    IE_MailMerge::IE_MailMerge_Listener & listener){
180 	IE_MailMerge * pie = NULL;
181 	UT_Error errorCode = IE_MailMerge::constructMerger(szMailMergeFile, IEMT_Unknown, &pie);
182 	if (!errorCode)
183 	{
184 		pie->setListener (&listener);
185 		errorCode = pie->mergeFile (szMailMergeFile);
186 		DELETEP(pie);
187 	}
188 
189 	return errorCode;
190 }
191 
192 /////////////////////////////////////////////////////////////////
193 
getImportFileType(const char * szSuffixOrMime)194 static IEFileType getImportFileType(const char * szSuffixOrMime)
195 {
196   IEFileType ieft = IEFT_Unknown;
197 
198   if(szSuffixOrMime && *szSuffixOrMime) {
199     IE_Imp::fileTypeForMimetype(szSuffixOrMime);
200     if(ieft == IEFT_Unknown) {
201       UT_String suffix;
202 
203       if(*szSuffixOrMime != '.')
204 	suffix = ".";
205       suffix += szSuffixOrMime;
206       ieft = IE_Imp::fileTypeForSuffix(suffix.c_str());
207     }
208   }
209 
210   return ieft;
211 }
212 
getExportFileType(const char * szSuffixOrMime)213 static IEFileType getExportFileType(const char * szSuffixOrMime)
214 {
215   IEFileType ieft = IEFT_Unknown;
216 
217   if(szSuffixOrMime && *szSuffixOrMime) {
218     IE_Exp::fileTypeForMimetype(szSuffixOrMime);
219     if(ieft == IEFT_Unknown) {
220       UT_String suffix;
221 
222       if(*szSuffixOrMime != '.')
223 	suffix = ".";
224       suffix += szSuffixOrMime;
225       ieft = IE_Exp::fileTypeForSuffix(suffix.c_str());
226     }
227   }
228 
229   return ieft;
230 }
231 
convertTo(const char * szSourceFilename,IEFileType sourceFormat,const char * szTargetFilename,IEFileType targetFormat)232 bool AP_Convert::convertTo(const char * szSourceFilename,
233 			   IEFileType sourceFormat,
234 			   const char * szTargetFilename,
235 			   IEFileType targetFormat)
236 {
237 	UT_Error error = UT_OK;
238 
239 	UT_return_val_if_fail(targetFormat != IEFT_Unknown, false);
240 	UT_return_val_if_fail(szSourceFilename != NULL, false);
241 	UT_return_val_if_fail(szTargetFilename != NULL, false);
242 
243 	PD_Document * pNewDoc = new PD_Document();
244 	UT_return_val_if_fail(pNewDoc, false);
245 
246 	char * uri = UT_go_shell_arg_to_uri (szSourceFilename);
247 	error = pNewDoc->readFromFile(uri, sourceFormat, m_impProps.utf8_str());
248 	g_free (uri);
249 
250 	if (!UT_IS_IE_SUCCESS(error)) {
251 		switch (error) {
252 		case UT_INVALIDFILENAME:
253 			if (m_iVerbose > 0)
254 				fprintf(stderr, "AbiWord: [%s] is not a valid file name.\n", szSourceFilename);
255 			break;
256 		case UT_IE_NOMEMORY:
257 			if (m_iVerbose > 0)
258 				fprintf(stderr, "AbiWord: Arrrgh... I don't have enough memory!\n");
259 			break;
260 		case UT_NOPIECETABLE:
261 			// TODO
262 		default:
263 			if (m_iVerbose > 0)
264 				fprintf(stderr, "AbiWord: could not open the file [%s]\n", szSourceFilename);
265 		}
266 
267 		UNREFP(pNewDoc);
268 		return (error == UT_OK);
269 	}
270 
271 	if (m_mergeSource.size()) {
272 		uri = UT_go_shell_arg_to_uri (szTargetFilename);
273 		IE_MailMerge::IE_MailMerge_Listener * listener = new Save_MailMerge_Listener (pNewDoc, uri, targetFormat, m_expProps);
274 		g_free(uri);
275 
276 		uri = UT_go_shell_arg_to_uri (m_mergeSource.utf8_str());
277 		handleMerge (uri, *listener);
278 		g_free (uri);
279 		DELETEP(listener);
280 	} else {
281 		uri = UT_go_shell_arg_to_uri (szTargetFilename);
282 		error = static_cast<AD_Document*>(pNewDoc)->saveAs(uri, targetFormat, m_expProps.utf8_str());
283 		g_free(uri);
284 
285 		switch (error) {
286 		case UT_OK:
287 			if (m_iVerbose > 1)
288 				printf("AbiWord: [%s] -> [%s]\tConversion ok!\n", szSourceFilename, szTargetFilename);
289 			break;
290 		case UT_SAVE_EXPORTERROR:
291 			if (m_iVerbose > 0)
292 				fprintf(stderr, "AbiWord: Uch! Are you sure that you've specified a valid exporter?\n");
293 			break;
294 		case UT_SAVE_WRITEERROR:
295 			if (m_iVerbose > 0)
296 				fprintf(stderr, "AbiWord: Uch! Could not write the file [%s]\n", szTargetFilename);
297 			break;
298 		default:
299 			if (m_iVerbose > 0)
300 				fprintf(stderr, "AbiWord: could not write the file [%s]\n", szTargetFilename);
301 			break;
302 		}
303 	}
304 
305 	UNREFP(pNewDoc);
306 
307 	return UT_IS_IE_SUCCESS(error);
308 }
309 
convertTo(const char * szFilename,const char * szSourceSuffixOrMime,const char * szTargetFilename,const char * szTargetSuffixOrMime)310 bool AP_Convert::convertTo(const char * szFilename,
311 			   const char * szSourceSuffixOrMime,
312 			   const char * szTargetFilename,
313 			   const char * szTargetSuffixOrMime)
314 {
315   return convertTo(szFilename, getImportFileType(szSourceSuffixOrMime), szTargetFilename, getExportFileType(szTargetSuffixOrMime));
316 }
317 
convertTo(const char * szFilename,const char * szSourceSuffixOrMime,const char * szTargetSuffixOrMime)318 bool AP_Convert::convertTo(const char * szFilename,
319 			   const char * szSourceSuffixOrMime,
320 			   const char * szTargetSuffixOrMime)
321 {
322   UT_return_val_if_fail(szTargetSuffixOrMime, false);
323   UT_return_val_if_fail(strlen(szTargetSuffixOrMime)>0, false);
324 
325   UT_String ext;
326   IEFileType ieft = IEFT_Unknown;
327 
328   UT_String file;
329 
330   // maybe it is a mime type. try that first
331   ieft = IE_Exp::fileTypeForMimetype(szTargetSuffixOrMime);
332   if(ieft != IEFT_Unknown) {
333     ext = IE_Exp::preferredSuffixForFileType(ieft).utf8_str();
334   }
335   else
336     {
337       std::string suffix = UT_pathSuffix(szTargetSuffixOrMime);
338       if (!suffix.empty())
339 	{
340 	  // suffix is ".txt" or ".html"
341 	  ieft = IE_Exp::fileTypeForSuffix(suffix.c_str());
342 
343 	  // szTargetSuffixOrMime is something like "file://home/dom/foo.html", so use it as our target filename
344 	  if (suffix.size() != strlen(szTargetSuffixOrMime))
345 	    file = szTargetSuffixOrMime;
346 	}
347       else
348 	{
349 	  // assume that szSourceSuffixOrMime is "txt" or "html"
350 	  ext = ".";
351 	  ext += szTargetSuffixOrMime;
352 	  ieft = IE_Exp::fileTypeForSuffix(ext.c_str());
353 	}
354 
355       // unknown suffix and mime type
356       if(ieft == IEFT_Unknown)
357 	return false;
358     }
359 
360   if (file.empty())
361     {
362       char * fileDup = g_strdup ( szFilename );
363 
364       char *tmp = strrchr(fileDup, '.');
365       if (tmp != NULL)
366 	*tmp = '\0';
367 
368       file = fileDup;
369       file += ext;
370 
371       FREEP(fileDup);
372     }
373 
374   return convertTo(szFilename, getImportFileType(szSourceSuffixOrMime), file.c_str(), ieft);
375 }
376 
setVerbose(int level)377 void AP_Convert::setVerbose(int level)
378 {
379 	if ((level >= 0) && (level <= 2))
380 		m_iVerbose = level;
381 }
382 
print(const char * szFile,GR_Graphics * pGraphics,const char * szFileExtensionOrMime)383 bool AP_Convert::print(const char * szFile, GR_Graphics * pGraphics, const char * szFileExtensionOrMime)
384 {
385 	// get the current document
386 	PD_Document *pDoc = new PD_Document();
387 	UT_Error err;
388 	char * uri = UT_go_shell_arg_to_uri (szFile);
389 
390 	IEFileType ieft = getImportFileType(szFileExtensionOrMime);
391 
392 	err = pDoc->readFromFile(uri, ieft, m_impProps.utf8_str());
393 	g_free(uri);
394 
395 	if( err != UT_OK)
396 	{
397 		fprintf(stderr, "AbiWord: Error importing file. [%s]  Could not print \n", szFile);
398 		UNREFP(pDoc);
399 		return false;
400 	}
401 	if (m_mergeSource.size()){
402 		IE_MailMerge::IE_MailMerge_Listener * listener = new Print_MailMerge_Listener(pDoc, pGraphics, szFile);
403 
404 		uri = UT_go_shell_arg_to_uri (m_mergeSource.utf8_str());
405 		handleMerge (uri, *listener);
406 		g_free (uri);
407 
408 		DELETEP(listener);
409 	} else {
410 
411 		// create a new layout and view object for the doc
412 		FL_DocLayout *pDocLayout = new FL_DocLayout(pDoc,pGraphics);
413 		FV_View printView(XAP_App::getApp(),0,pDocLayout);
414 		pDocLayout->setView (&printView);
415 		pDocLayout->fillLayouts();
416 		pDocLayout->formatAll();
417 		pDocLayout->recalculateTOCFields();
418 
419 		bool bCollate = true;
420 		UT_sint32 nCopies = 1;
421 		std::set<UT_sint32> pages;
422 
423 		std::map<std::string, std::string> props_map;
424 		UT_parse_properties(m_expProps.utf8_str(), props_map);
425 
426 		if (props_map.find("collate") != props_map.end())
427 		  {
428 		    bCollate = UT_parseBool(props_map["collate"].c_str(), true);
429 		  }
430 
431 		if (props_map.find("copies") != props_map.end())
432 		  {
433 		    nCopies = atoi(props_map["copies"].c_str());
434 		    if (nCopies <= 0)
435 		      nCopies = 1;
436 		  }
437 
438 		if (props_map.find("pages") != props_map.end())
439 		  {
440 		    char **page_descriptions;
441 
442 		    page_descriptions = g_strsplit(props_map["pages"].c_str(), ",", -1);
443 
444 		    int i = 0;
445 		    while (page_descriptions[i] != NULL)
446 		      {
447 			char *description = page_descriptions[i];
448 			i++;
449 
450 			int start_page, end_page;
451 
452 			if (2 == sscanf(description, "%d-%d", &start_page, &end_page))
453 			  {
454 			  }
455 			else if (1 == sscanf(description, "%d", &start_page))
456 			  {
457 			    end_page = start_page;
458 			  }
459 			else
460 			  {
461 			    // invalid page specification
462 			    continue;
463 			  }
464 
465 			for (int pageno = start_page; pageno <= end_page; pageno++)
466 			  {
467 			    if ((pageno > 0) && (pageno <= (int)pDocLayout->countPages()))
468 			      pages.insert(pageno);
469 			  }
470 		      }
471 
472 		    g_strfreev(page_descriptions);
473 		  }
474 
475 		if (pages.empty())
476 		  {
477 		    for (UT_sint32 i = 1; i <= pDocLayout->countPages(); i++)
478 		      {
479 			pages.insert(i);
480 		      }
481 		  }
482 
483 		if(!s_actuallyPrint (pDoc, pGraphics,
484 				     &printView, szFile,
485 				     nCopies, bCollate,
486 				     pDocLayout->getWidth(), pDocLayout->getHeight() / pDocLayout->countPages(),
487 				     pages))
488 		  err = UT_SAVE_WRITEERROR;
489 
490 		DELETEP(pDocLayout);
491 	}
492 
493 	UNREFP(pDoc);
494 
495 	return (err == UT_OK);
496 }
497 
498 
printFirstPage(GR_Graphics * pGraphics,PD_Document * pDoc)499 bool AP_Convert::printFirstPage(GR_Graphics * pGraphics,PD_Document * pDoc)
500 {
501 		// create a new layout and view object for the doc
502 
503 	FL_DocLayout *pDocLayout = new FL_DocLayout(pDoc,pGraphics);
504 	FV_View printView(XAP_App::getApp(),0,pDocLayout);
505 	pDocLayout->setView (&printView);
506 	pDocLayout->fillLayouts();
507 	pDocLayout->formatAll();
508 
509 	bool success = s_actuallyPrint (pDoc, pGraphics,
510 					&printView, "pngThumb",
511 					1, true,
512 					pDocLayout->getWidth(), pDocLayout->getHeight() / pDocLayout->countPages(),
513 					1, 1);
514 
515 	DELETEP(pDocLayout);
516 
517 	return success;
518 }
519