1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 /***************************************************************************
8  *   Copyright (C) 2004 by Riku Leino                                      *
9  *   tsoots@gmail.com                                                      *
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  *   This program is distributed in the hope that it will be useful,       *
17  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
19  *   GNU General Public License for more details.                          *
20  *                                                                         *
21  *   You should have received a copy of the GNU General Public License     *
22  *   along with this program; if not, write to the                         *
23  *   Free Software Foundation, Inc.,                                       *
24  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.             *
25  ***************************************************************************/
26 
27 #include "gtgettext.h"
28 #include "pluginmanager.h"
29 #include "scpaths.h"
30 #include "pageitem.h"
31 #include "scribusdoc.h"
32 #include "selection.h"
33 #include "ui/gtdialogs.h"
34 #include "gtwriter.h"
35 #include <QPixmap>
36 
37 // Constructor
gtGetText(ScribusDoc * doc)38 gtGetText::gtGetText(ScribusDoc* doc)
39 {
40 	m_dias = nullptr;
41 	// Attach to the active document
42 	m_Doc = doc;
43 	// Load the plugins array.
44 	loadImporterPlugins();
45 } // gtGetText::gtGetText(ScribusDoc* doc)
46 
47 // Look at the results of the file selection dialog and figure out if you need to use an importer.
48 // Prompt the user if the importer to use isn't obvious.
launchImporter(int importer,const QString & filename,bool textOnly,const QString & encoding,bool append,bool prefix,PageItem * target)49 void gtGetText::launchImporter(int importer, const QString& filename, bool textOnly,
50 								const QString& encoding, bool append, bool prefix, PageItem* target)
51 {
52 	// Struct for the plugin info, we'll load this up from the array.
53 	struct ImporterData ida;
54 	// Do we need to call an importer? Unsure what happens if this ever becomes false.
55 	bool callImporter = true;
56 	// Check and see if the file selection dialog returned the index position of the
57 	// importer to be used. If it isn't, try to figure out what it is. If we can't,
58 	// prompt the user.
59 	if (importer == -1)
60 	{
61 		// Attempt to determine the importer based on the file's extension.
62 		// Create a Qstring with what could be an extension.
63 		QString fend = filename.right(filename.length() - filename.lastIndexOf(".") - 1);
64 		QString fendL(fend.toLower());
65 		// Look for that extension in the importer QMap.
66 		if (m_importerMap.find(fend) != m_importerMap.end())
67 			// If the map is found, assign ida to the corresponding struct in the map.
68 			ida = *m_importerMap[fend];
69 		// Otherwise, test for the lowercase version
70 		else if (m_importerMap.find(fendL) != m_importerMap.end())
71 			// If the map is found, assign ida to the corresponding struct in the map.
72 			ida = *m_importerMap[fendL];
73 		// Otherwise, try and ask the user.
74 		else
75 		{
76 			// Create a new dialog
77 			m_dias = new gtDialogs();
78 			// Pop up the dialog asking the user to select the type from our list (ilist) of
79 			// importable file types. If one is not selected, set callImporter to false.
80 			callImporter = m_dias->runImporterDialog(filename, m_ilist);
81 			// If we're gonna call an importer, we need to copy it's struct to ida.
82 			if (callImporter)
83 				ida = m_importers[m_dias->getImporter()];
84 			// Destroy the diag
85 			delete m_dias;
86 		} // else - if (importerMap.find(fend) != importerMap.end())
87 	}
88 	else // If we know which importer to use
89 	{
90 		// Copy the importer's struct to ida.
91 		ida = m_importers[importer];
92 	}	// else - if (importer == -1)
93 
94 	// Create a target text frame for the imported text and assign it to the parameter "target"
95 	PageItem* targetFrame=target;
96 
97 	// If the targetframe is 0 ( no frame selected/created? ) then reassign it to the
98 	// (questionable interpretation here) first frame in the documentation.
99 	if (targetFrame==nullptr)
100 		targetFrame=m_Doc->m_Selection->itemAt(0);
101 
102 	// If the targetframe is not zero, and we do need to call the importer,
103 	// Run the importer via "CallDLL" and pass it what it needs to know.
104 	if (targetFrame!=nullptr && callImporter)
105 		CallDLL(ida, filename, encoding, textOnly, append, prefix, targetFrame);
106 }  //void gtGetText::launchImporter(int importer, const QString& filename, bool textOnly,
107    //						const QString& encoding, bool append, PageItem* target)
108 
109 // Find the available plugins based on the environment, validate they load, and
110 // create quick lookup mappings.
loadImporterPlugins()111 void gtGetText::loadImporterPlugins()
112 {
113 	// Get the path to the plugins
114 	QString gtdir = ScPaths::instance().pluginDir();
115 	// Append the gettext path to the plugin
116 	gtdir += "gettext";
117 	// Set the search parameteer for the platform specific extension for the plugins ( DLL, so, etc. )
118 	QString libPattern = QString("*.%1*").arg(PluginManager::platformDllExtension());
119 	// Search for matches.
120 	QDir d(gtdir, libPattern, QDir::Name, (QDir::Filter) PluginManager::platformDllSearchFlags());
121 
122 	// Initialize a structure for the importers found
123 	struct ImporterData ida;
124 	ida.fileFormatName = "";
125 
126 	// Check and see if the directory existed and if the count of files matching is greater than 0.
127 	if ((d.exists()) && (d.count() != 0))
128 	{
129 		// loop through the entries.
130 		for (uint dc = 0; dc < d.count(); ++dc)
131 		{
132 			// Verify the Plugin will load. If it does, collect the info on the plugin ( Name, extension, format, etc )
133 			if (DLLName(d[dc], &ida.fileFormatName, &ida.fileEndings))
134 			{
135 				// no plugin's "format name" marks "don't load plug"
136 				if (ida.fileFormatName.isNull())
137 					continue;
138 				// Store the path to the plugin.
139 				ida.soFilePath = d[dc];
140 				// If the plugin path does not begin with /, prepend a /.
141 				if (ida.soFilePath.left(1) != "/")
142 					ida.soFilePath = "/" + ida.soFilePath;
143 				// Add the plugin data to the end of the importer's vector.
144 				m_importers.push_back(ida);
145 			}	// if (DLLName(d[dc], &ida.fileFormatName, &ida.fileEndings))
146 		}  // for (uint dc = 0; dc < d.count(); ++dc)
147 	}  // if ((d.exists()) && (d.count() != 0))
148 	// Create the Importer Extension to Plugin data qmap.
149 	createMap();
150 }  // void gtGetText::loadImporterPlugins()
151 
152 
getSupportedTypes()153 QStringList gtGetText::getSupportedTypes()
154 {
155 	QStringList result;
156 	for (size_t i = 0; i < m_importers.size(); ++i)
157 	{
158 		const ImporterData& importerData = m_importers[i];
159 		if (importerData.fileEndings.count() <= 0)
160 			continue;
161 		for (int j = 0; j < importerData.fileEndings.count(); ++j)
162 			result.append(importerData.fileEndings[j].toLower());
163 	}
164 	return result;
165 }
166 
167 // Creates the dialog for the user to import a file based on the supported file formats.
run()168 ImportSetup gtGetText::run()
169 {
170 	// Initialize a filters list.
171 	QString filters;
172 
173 	// Create a string for the "All supported files filter". Start with the label then loop through
174 	// the importers vector and add all of the file extensions supported.
175 	QString allSupported = QObject::tr("All Supported Formats") + " (";
176 	// Loop through the importers vector.
177 	for (size_t i = 0; i < m_importers.size(); ++i)
178 	{
179 		const ImporterData& importerData = m_importers[i];
180 		// If there are any file extnsions declared by the importer
181 		if (importerData.fileEndings.count() <= 0)
182 			continue;
183 		// Add the importer name to the filters list
184 		filters += importerData.fileFormatName + " (";
185 		// Loop though the extensions supported by the importer
186 		for (int j = 0; j < importerData.fileEndings.count(); ++j)
187 		{
188 			// Add the extension to both the filter and allSupported strings
189 			filters += "*." + importerData.fileEndings[j] + " ";
190 			allSupported += "*." + importerData.fileEndings[j] + " ";
191 		}
192 		// Trim the Qstring
193 		filters = filters.trimmed();
194 		// Append "entry of entry" information to the end of the filter.
195 		filters += ");;";
196 	}
197 
198 	// Trim the allSupported QString and append "end of entry" data to the end of it.
199 	allSupported = allSupported.trimmed();
200 	allSupported += ");;";
201 
202 	// Prepend allSupported to the filters Qstring.
203 	filters = allSupported + filters;
204 	// Add an "all files" entry to the end of the filters QString
205 	filters += QObject::tr("All Files (*)");
206 	// Populate ilist with the file importer names.
207 	for (size_t i = 0;  i < m_importers.size(); ++i)
208 		m_ilist.append(m_importers[i].fileFormatName);
209 
210 	// Create a new dialog.
211 	m_dias = new gtDialogs();
212 	// Create a new ImportSetup struct
213 	ImportSetup impsetup;
214 	// INitialize runDialog to false
215 	impsetup.runDialog=false;
216 	// If we get a true back from the File selection Dialog ( which we send our filters and extensions lists )
217 	if (m_dias->runFileDialog(filters, m_ilist))
218 	{
219 		// Set the runDialog to true
220 		impsetup.runDialog = true;
221 		// Copy the other values for the struct from the dialog results
222 		impsetup.encoding = m_dias->getEncoding();
223 		impsetup.filename = m_dias->getFileName();
224 		impsetup.importer = m_dias->getImporter();
225 		impsetup.textOnly = m_dias->importTextOnly();
226 		impsetup.prefixNames = m_dias->prefixStyles();
227 	}
228 	// Destroy the dialog.
229 	delete m_dias;
230 	// Return the ImportSetup struct.
231 	return impsetup;
232 }  // ImportSetup gtGetText::run()
233 
234 // Loads, validates, and executes the Importer code.
CallDLL(const ImporterData & idata,const QString & filePath,const QString & encoding,bool textOnly,bool append,bool prefix,PageItem * importItem)235 void gtGetText::CallDLL(const ImporterData& idata, const QString& filePath,
236 						const QString& encoding, bool textOnly, bool append, bool prefix, PageItem* importItem)
237 {
238 	// Pointer for the loaded plugin.
239 	void* gtplugin;
240 	// Type definition for GetText pointer in the function in question.
241 	typedef void (*gt2ptr)(const QString& filename, const QString& encoding, bool textOnly, bool prefix, bool append, PageItem *textframe);
242 	// Type definition for GetText pointer in the function in question.
243 	typedef void (*sdem)(const QString& filename, const QString& encoding, bool textOnly, gtWriter *writer);
244 	// The point to the above.
245 	gt2ptr fp_GetText2;
246 	sdem fp_GetText;
247 	// Initialize Path to the "DLL"
248 	QString pluginFilePath = QString("%1/gettext/%2").arg(ScPaths::instance().pluginDir(), idata.soFilePath);
249 	// Attempt to load the plugin, store the pointer in gtplugin
250 	gtplugin = PluginManager::loadDLL(pluginFilePath);
251 	// If gtplugin is nullptr we failed to load the plugin. Report an error to the user and exit the method.
252 	if (!gtplugin)
253 	{
254 		qWarning("Failed to load plugin %s", pluginFilePath.toLatin1().constData());
255 		return;
256 	} // if (!gtplugin)
257 
258 	fp_GetText2 = (gt2ptr) PluginManager::resolveSym(gtplugin,"GetText2");
259 	if (fp_GetText2)
260 	{
261 		if (!append)
262 			importItem->itemText.clear();
263 		// Execute the importer's "GetText2" method.
264 		(*fp_GetText2)(filePath, encoding, textOnly, prefix, append, importItem);
265 	}  // if (!fp_GetText2)
266 	else
267 	{
268 		// Attempt to map the old GetText method to to the pointer via the PluginManager. Store the result in fp_GetText.
269 		fp_GetText = (sdem) PluginManager::resolveSym(gtplugin,"GetText");
270 		// If fp_GetText is nullptr, we could not find the symbol,report the error, unload the "DLL" and exit the method.
271 		if (fp_GetText)
272 		{
273 			// Create a new writer object in "append"'s mode (true or false ) attached to the importItem
274 			gtWriter *w = new gtWriter(append, importItem);
275 			// Execute the importer's "GetText" method.
276 			(*fp_GetText)(filePath, encoding, textOnly, w);
277 			// Destroy the writer
278 			delete w;
279 		}  // if (!fp_GetText)
280 		else
281 		{
282 			qWarning("Failed to get GetText() from %s",pluginFilePath.toLatin1().constData());
283 		}
284 	}
285 	// GetText is not quite up to date vs styles, clean char formatting already specified at paragraph level
286 	importItem->itemText.fixLegacyFormatting();
287 	// Unload the plugin.
288 	PluginManager::unloadDLL(gtplugin);
289 }  // void gtGetText::CallDLL(const ImporterData& idata, const QString& filePath,
290    //                     const QString& encoding, bool textOnly, bool append, PageItem* importItem)
291 
292 // Loads the "DLL", validates the importer is good, populates the passed parameters with
293 // the plugin information.
DLLName(const QString & name,QString * ffName,QStringList * fEndings)294 bool gtGetText::DLLName(const QString& name, QString *ffName, QStringList *fEndings)
295 {
296 	// Pointer to the plugin, once loaded
297 	void* gtplugin;
298 	// typedef of Qstring to map the importer name (FileFormatName) method results to.
299 	typedef QString (*sdem0)();
300 	// typedef of QStringList to map the file extensions supported method results to.
301 	typedef QStringList (*sdem1)();
302 	// The actual importer name object
303 	sdem0 fp_FileFormatName;
304 	// The actual extensions supported object
305 	sdem1 fp_FileExtensions;
306 	// Initialise the plugin file path ( with filename )
307 	QString pluginFilePath = QString("%1/gettext/%2").arg(ScPaths::instance().pluginDir(), name);
308 	// Attempt to load the plugin.
309 	gtplugin = PluginManager::loadDLL(pluginFilePath);
310 	// if gtplugin is nullptr we were unable to load the plugin. Return an error and exit the method.
311 	if (!gtplugin)
312 	{
313 		qWarning("Failed to load plugin %s", pluginFilePath.toLatin1().constData());
314 		return false;
315 	}
316 	// Attempt to resolve the plugin symbol to the importer name (FileFormatName)
317 	fp_FileFormatName = (sdem0) PluginManager::resolveSym( gtplugin, "FileFormatName");
318 	// if fp_FileFormatName is nullptr, we could not find the FileFormatName symbol. The plugin is incomplete.
319 	// Report an error, unload the plugin, and exit the method.
320 	if (!fp_FileFormatName)
321 	{
322 		qWarning("Failed to get FileFormatName() from %s", pluginFilePath.toLatin1().constData());
323 		PluginManager::unloadDLL(gtplugin);
324 		return false;
325 	}
326 	// Attempt to resolve the plugin symbol to the list of supported file extensions.
327 	fp_FileExtensions = (sdem1) PluginManager::resolveSym( gtplugin, "FileExtensions");
328 	// if fp_FileExtensions is nullptr, we could not find the FileExtensions symbol. The plugin is incomplete.
329 	// Report an error, unload the plugin, and exit the method.
330 	if (!fp_FileExtensions)
331 	{
332 		qWarning("Failed to get FileExtensions() from %s", pluginFilePath.toLatin1().constData());
333 		PluginManager::unloadDLL(gtplugin);
334 		return false;
335 	}
336 	// Set the format name based on the resolved method.
337 	*ffName = (*fp_FileFormatName)();
338 	// Set the extensions list based on the resolved method.
339 	*fEndings = (*fp_FileExtensions)();
340 	// Unload the plugin
341 	PluginManager::unloadDLL(gtplugin);
342 	// Successfully return!
343 	return true;
344 }  // bool gtGetText::DLLName(QString name, QString *ffName, QStringList *fEndings)
345 
346 // Create the importer Qmap.
createMap()347 void gtGetText::createMap()
348 {
349 	// Loop through the importers Vector
350 	for (uint i = 0; i < m_importers.size(); ++i)
351 	{
352 		// Loop through each file extension the importer uses/importers and create an individual
353 		// Qmap entry for it.
354 		for (int j = 0; j < m_importers[i].fileEndings.count(); ++j)
355 				m_importerMap.insert(m_importers[i].fileEndings[j], &m_importers[i]);
356 	}  // for (uint i = 0; i < importers.size(); ++i)
357 }  // void gtGetText::createMap()
358 
359 // Destructor
~gtGetText()360 gtGetText::~gtGetText()
361 {
362 	// Nothing needs to happen here.
363 }
364