LibreOffice Module writerperfect (master) 1
MSWorksCalcImportFilter.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/* MSWorksCalcImportFilter: Sets up the filter, and calls DocumentCollector
3 * to do the actual filtering
4 *
5 * This file is part of the LibreOffice project.
6 *
7 * This Source Code Form is subject to the terms of the Mozilla Public
8 * License, v. 2.0. If a copy of the MPL was not distributed with this
9 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 */
11
12#include <com/sun/star/awt/XWindow.hpp>
13#include <com/sun/star/container/XChild.hpp>
14#include <com/sun/star/sdbc/XResultSet.hpp>
15#include <com/sun/star/sdbc/XRow.hpp>
16#include <com/sun/star/ucb/XContent.hpp>
17#include <com/sun/star/ucb/XContentAccess.hpp>
18#include <sal/log.hxx>
21#include <sfx2/passwd.hxx>
22#include <tools/urlobj.hxx>
23#include <ucbhelper/content.hxx>
24
25#include <libwps/libwps.h>
26
29#include <WPFTResMgr.hxx>
31#include <strings.hrc>
32
33#include <iostream>
34#include <map>
35#include <utility>
36
37using namespace ::com::sun::star;
38
40{
43getResultSet(const css::uno::Reference<css::ucb::XContent>& xPackageContent)
44{
45 try
46 {
47 if (xPackageContent.is())
48 {
49 ucbhelper::Content packageContent(xPackageContent,
52 uno::Sequence<OUString> lPropNames{ "Title" };
54 packageContent.createCursor(lPropNames, ucbhelper::INCLUDE_DOCUMENTS_ONLY));
55 return xResultSet;
56 }
58 }
59 catch (...)
60 {
61 SAL_WARN("writerperfect",
62 "ignoring Exception in MSWorksCalcImportFilterInternal:getResultSet");
64 }
65}
66
67namespace
68{
71class FolderStream : public librevenge::RVNGInputStream
72{
73public:
75 explicit FolderStream(css::uno::Reference<css::ucb::XContent> xContent)
76 : m_xContent(std::move(xContent))
77 {
78 }
79
81 void addFile(OUString const& path, std::string const& shortName)
82 {
83 m_nameToPathMap[shortName] = path;
84 }
89 const unsigned char* read(unsigned long, unsigned long&) override { return nullptr; }
91 long tell() override { return 0; }
95 int seek(long, librevenge::RVNG_SEEK_TYPE) override { return 1; }
97 bool isEnd() override { return true; }
98
102 bool isStructured() override { return true; }
106 unsigned subStreamCount() override { return unsigned(m_nameToPathMap.size()); }
108 const char* subStreamName(unsigned id) override
109 {
110 if (m_nameToPathMap.size() < id)
111 return nullptr;
112
113 std::map<std::string, OUString>::const_iterator it = m_nameToPathMap.begin();
114 std::advance(it, id);
115 return it->first.c_str();
116 }
118 bool existsSubStream(const char* name) override
119 {
120 return name && m_nameToPathMap.find(name) != m_nameToPathMap.end();
121 }
123 librevenge::RVNGInputStream* getSubStreamByName(const char* name) override
124 {
125 if (m_nameToPathMap.find(name) == m_nameToPathMap.end() || !m_xContent.is())
126 return nullptr;
127
128 try
129 {
131 if (xResultSet.is() && xResultSet->first())
132 {
133 const uno::Reference<ucb::XContentAccess> xContentAccess(xResultSet,
134 uno::UNO_QUERY_THROW);
135 const uno::Reference<sdbc::XRow> xRow(xResultSet, uno::UNO_QUERY_THROW);
136 OUString lPath = m_nameToPathMap.find(name)->second;
137 do
138 {
139 const OUString aTitle(xRow->getString(1));
140 if (aTitle != lPath)
141 continue;
142
143 const uno::Reference<ucb::XContent> xSubContent(xContentAccess->queryContent());
144 ucbhelper::Content aSubContent(xSubContent,
147 uno::Reference<io::XInputStream> xInputStream = aSubContent.openStream();
148 if (xInputStream.is())
149 return new writerperfect::WPXSvInputStream(xInputStream);
150 break;
151 } while (xResultSet->next());
152 }
153 }
154 catch (...)
155 {
156 SAL_WARN("writerperfect", "ignoring Exception in "
157 "MSWorksCalcImportFilterInternal::FolderStream::"
158 "getSubStreamByName");
159 }
160
161 return nullptr;
162 }
164 librevenge::RVNGInputStream* getSubStreamById(unsigned id) override
165 {
166 char const* name = subStreamName(id);
167 return name ? getSubStreamByName(name) : nullptr;
168 }
169
170private:
174 std::map<std::string, OUString> m_nameToPathMap;
175 FolderStream(const FolderStream&) = delete;
176 FolderStream& operator=(const FolderStream&) = delete;
177};
178}
179}
180
183 librevenge::RVNGInputStream& rInput,
184 OdsGenerator& rGenerator,
185 utl::MediaDescriptor& mediaDescriptor)
186{
187 libwps::WPSKind kind = libwps::WPS_TEXT;
188 libwps::WPSCreator creator;
189 bool needEncoding;
190 const libwps::WPSConfidence confidence
191 = libwps::WPSDocument::isFileFormatSupported(&rInput, kind, creator, needEncoding);
192
193 if ((kind != libwps::WPS_SPREADSHEET && kind != libwps::WPS_DATABASE)
194 || (confidence == libwps::WPS_CONFIDENCE_NONE))
195 return false;
196 std::string fileEncoding;
197 if (needEncoding)
198 {
199 OUString encoding;
200 // first check if we can find the encoding in the filter options (headless mode)
201 mediaDescriptor[utl::MediaDescriptor::PROP_FILTEROPTIONS] >>= encoding;
202 if (!encoding.isEmpty()) // TODO: check if the encoding string is valid
203 fileEncoding = encoding.toUtf8().getStr();
204 else
205 {
206 OUString title;
207 switch (creator)
208 {
209 case libwps::WPS_MSWORKS:
210 title = WpResId(STR_ENCODING_DIALOG_TITLE_MSWORKS);
211 encoding = "CP850";
212 break;
213 case libwps::WPS_LOTUS:
214 title = WpResId(STR_ENCODING_DIALOG_TITLE_LOTUS);
215 encoding = "CP437";
216 break;
217 case libwps::WPS_SYMPHONY:
218 title = WpResId(STR_ENCODING_DIALOG_TITLE_SYMPHONY);
219 encoding = "CP437";
220 break;
221 case libwps::WPS_QUATTRO_PRO:
222 title = WpResId(STR_ENCODING_DIALOG_TITLE_QUATTROPRO);
223 encoding = "CP437";
224 break;
225 case libwps::WPS_RESERVED_2:
226 title = WpResId(STR_ENCODING_DIALOG_TITLE_MSMULTIPLAN);
227 encoding = "CP437";
228 break;
229 default:
230 SAL_INFO("writerperfect", "unexpected creator: " << creator);
231 title = WpResId(STR_ENCODING_DIALOG_TITLE);
232 encoding = "CP437";
233 break;
234 }
235
236 fileEncoding = encoding.toUtf8().getStr(); // set default to the proposed encoding
237 try
238 {
239 writerperfect::WPFTEncodingDialog aDlg(pParent, title, encoding);
240 if (aDlg.run() == RET_OK)
241 {
242 if (!aDlg.GetEncoding().isEmpty())
243 fileEncoding = aDlg.GetEncoding().toUtf8().getStr();
244 }
245 // we can fail because we are in headless mode, the user has cancelled conversion, ...
246 else if (aDlg.hasUserCalledCancel())
247 return false;
248 }
249 catch (...)
250 {
251 SAL_WARN("writerperfect",
252 "ignoring Exception in MSWorksCalcImportFilter::doImportDocument");
253 }
254 }
255 }
256 OString aUtf8Passwd;
257 if (confidence == libwps::WPS_CONFIDENCE_SUPPORTED_ENCRYPTION)
258 {
259 OUString sPassword;
260 // now check if we can find the password in the properties
261 // (just in case, "soffice --headless" adds an option to send password)
262 mediaDescriptor[utl::MediaDescriptor::PROP_PASSWORD] >>= sPassword;
263 if (!sPassword.isEmpty())
264 aUtf8Passwd = OUStringToOString(sPassword, RTL_TEXTENCODING_UTF8);
265 else
266 {
267 // ok, ask the user for a password
268 try
269 {
270 SfxPasswordDialog aPasswdDlg(pParent);
271 aPasswdDlg.SetMinLen(1);
272 if (!aPasswdDlg.run())
273 return false;
274 OUString aPasswd = aPasswdDlg.GetPassword();
275 aUtf8Passwd = OUStringToOString(aPasswd, RTL_TEXTENCODING_UTF8);
276 }
277 catch (...)
278 {
279 return false;
280 }
281 }
282 }
283 return libwps::WPS_OK
284 == libwps::WPSDocument::parse(&rInput, &rGenerator,
285 confidence == libwps::WPS_CONFIDENCE_SUPPORTED_ENCRYPTION
286 ? aUtf8Passwd.getStr()
287 : nullptr,
288 fileEncoding.c_str());
289}
290
291//XExtendedFilterDetection
293MSWorksCalcImportFilter::filter(const css::uno::Sequence<css::beans::PropertyValue>& rDescriptor)
294{
295 OUString sUrl;
296 css::uno::Reference<css::io::XInputStream> xInputStream;
297 css::uno::Reference<ucb::XContent> xContent;
298 css::uno::Reference<css::awt::XWindow> xDialogParent;
299
300 for (const auto& rValue : rDescriptor)
301 {
302 if (rValue.Name == "InputStream")
303 rValue.Value >>= xInputStream;
304 else if (rValue.Name == "UCBContent")
305 rValue.Value >>= xContent;
306 else if (rValue.Name == "FileName" || rValue.Name == "URL")
307 rValue.Value >>= sUrl;
308 else if (rValue.Name == "ParentWindow")
309 rValue.Value >>= xDialogParent;
310 }
311
312 if (!getXContext().is() || !xInputStream.is())
313 {
314 OSL_ASSERT(false);
315 return false;
316 }
317
318 // An XML import service: what we push sax messages to...
319 css::uno::Reference<XInterface> xInternalFilter
320 = getXContext()->getServiceManager()->createInstanceWithContext(
322 assert(xInternalFilter);
323 css::uno::Reference<css::xml::sax::XFastDocumentHandler> xInternalHandler(xInternalFilter,
324 css::uno::UNO_QUERY);
325 assert(xInternalHandler);
326
327 // The XImporter sets up an empty target document for XDocumentHandler to write to...
328 css::uno::Reference<css::document::XImporter> xImporter(xInternalHandler, css::uno::UNO_QUERY);
329 assert(xImporter);
330 xImporter->setTargetDocument(getTargetDocument());
331
332 // OO Graphics Handler: abstract class to handle document SAX messages, concrete implementation here
333 // writes to in-memory target doc
335 new SvXMLLegacyToFastDocHandler(static_cast<SvXMLImport*>(xInternalHandler.get())));
336
337 writerperfect::WPXSvInputStream input(xInputStream);
338 OdsGenerator exporter;
339 exporter.addDocumentHandler(&aHandler, ODF_FLAT_XML);
340 doRegisterHandlers(exporter);
341
342 utl::MediaDescriptor aDescriptor(rDescriptor);
343 try
344 {
345 // time to check if the file is a WK3 file and a FM3 file is
346 // present
347 bool checkForFM3 = false;
348 if (input.seek(0, librevenge::RVNG_SEEK_SET) == 0 && xContent.is()
349 && INetURLObject(sUrl).getExtension().equalsIgnoreAsciiCase("WK3"))
350 {
351 // check if the file header corresponds to a .wk3 file
352 unsigned long numBytesRead;
353 const unsigned char* data = input.read(6, numBytesRead);
354 if (data && numBytesRead == 6 && data[0] == 0 && data[1] == 0 && data[2] == 0x1a
355 && data[3] == 0 && data[4] < 2 && data[5] == 0x10)
356 checkForFM3 = true;
357 }
358 if (checkForFM3)
359 {
360 // check if the format file exists
361 const css::uno::Reference<container::XChild> xChild(xContent, uno::UNO_QUERY);
362 if (xChild.is())
363 {
364 OUString sWM3Name;
365 OUString sFM3Name;
366 const css::uno::Reference<ucb::XContent> xPackageContent(xChild->getParent(),
367 uno::UNO_QUERY);
370 if (xResultSet.is() && xResultSet->first())
371 {
372 const uno::Reference<ucb::XContentAccess> xContentAccess(xResultSet,
373 uno::UNO_QUERY_THROW);
374 const uno::Reference<sdbc::XRow> xRow(xResultSet, uno::UNO_QUERY_THROW);
375 INetURLObject aTmpUrl(sUrl);
376 sWM3Name = aTmpUrl.getName(INetURLObject::LAST_SEGMENT, true,
378 aTmpUrl.setExtension(u"FM3");
379 const OUString& sTestFM3Name
380 = aTmpUrl.getName(INetURLObject::LAST_SEGMENT, true,
382 do
383 {
384 const OUString& aTitle(xRow->getString(1));
385 if (aTitle.equalsIgnoreAsciiCase(sTestFM3Name))
386 sFM3Name = aTitle;
387 } while (xResultSet->next() && sFM3Name.isEmpty());
388 }
389 if (!sFM3Name.isEmpty())
390 {
391 MSWorksCalcImportFilterInternal::FolderStream structuredInput(xPackageContent);
392 structuredInput.addFile(sWM3Name, "WK3");
393 structuredInput.addFile(sFM3Name, "FM3");
394
395 libwps::WPSKind kind = libwps::WPS_TEXT;
396 libwps::WPSCreator creator;
397 bool needEncoding;
398 const libwps::WPSConfidence confidence
399 = libwps::WPSDocument::isFileFormatSupported(&structuredInput, kind,
400 creator, needEncoding);
401 if (confidence != libwps::WPS_CONFIDENCE_NONE)
402 return doImportDocument(Application::GetFrameWeld(xDialogParent),
403 structuredInput, exporter, aDescriptor);
404 }
405 }
406 }
407 }
408 catch (...)
409 {
410 }
411
412 return doImportDocument(Application::GetFrameWeld(xDialogParent), input, exporter, aDescriptor);
413}
414
415bool MSWorksCalcImportFilter::doDetectFormat(librevenge::RVNGInputStream& rInput,
416 OUString& rTypeName)
417{
418 libwps::WPSKind kind = libwps::WPS_TEXT;
419 libwps::WPSCreator creator;
420 bool needEncoding;
421 const libwps::WPSConfidence confidence
422 = libwps::WPSDocument::isFileFormatSupported(&rInput, kind, creator, needEncoding);
423
424 if ((kind == libwps::WPS_SPREADSHEET || kind == libwps::WPS_DATABASE)
425 && confidence != libwps::WPS_CONFIDENCE_NONE)
426 {
427 switch (creator)
428 {
429 case libwps::WPS_MSWORKS:
430 rTypeName = "calc_MS_Works_Document";
431 break;
432 case libwps::WPS_LOTUS:
433 case libwps::WPS_SYMPHONY:
434 rTypeName = "calc_WPS_Lotus_Document";
435 break;
436 case libwps::WPS_QUATTRO_PRO:
437 rTypeName = "calc_WPS_QPro_Document";
438 break;
439 case libwps::WPS_RESERVED_2:
440 rTypeName = "calc_MS_Multiplan";
441 break;
442 default:
443 break;
444 }
445 }
446
447 return !rTypeName.isEmpty();
448}
449
451
452// XServiceInfo
454{
455 return "com.sun.star.comp.Calc.MSWorksCalcImportFilter";
456}
457
458sal_Bool SAL_CALL MSWorksCalcImportFilter::supportsService(const OUString& rServiceName)
459{
460 return cppu::supportsService(this, rServiceName);
461}
462
463css::uno::Sequence<OUString> SAL_CALL MSWorksCalcImportFilter::getSupportedServiceNames()
464{
465 return { "com.sun.star.document.ImportFilter", "com.sun.star.document.ExtendedTypeDetection" };
466}
467
468extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
470 css::uno::XComponentContext* const context, const css::uno::Sequence<css::uno::Any>&)
471{
472 return cppu::acquire(new MSWorksCalcImportFilter(context));
473}
474
475/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
uno::Reference< ucb::XContent > m_xContent
the main container
std::map< std::string, OUString > m_nameToPathMap
the map short name to path
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_comp_Calc_MSWorksCalcImportFilter_get_implementation(css::uno::XComponentContext *const context, const css::uno::Sequence< css::uno::Any > &)
OUString WpResId(TranslateId aId)
Definition: WPFTResMgr.hxx:7
OString name
Name of the stream.
static weld::Window * GetFrameWeld(const css::uno::Reference< css::awt::XWindow > &rWindow)
OUString getName(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
bool setExtension(std::u16string_view rTheExtension, sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
virtual bool doImportDocument(weld::Window *pParent, librevenge::RVNGInputStream &rInput, OdsGenerator &rGenerator, utl::MediaDescriptor &) override
virtual bool doDetectFormat(librevenge::RVNGInputStream &rInput, OUString &rTypeName) override
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
virtual void doRegisterHandlers(OdsGenerator &rGenerator) override
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
virtual OUString SAL_CALL getImplementationName() override
virtual sal_Bool SAL_CALL filter(const css::uno::Sequence< css::beans::PropertyValue > &rDescriptor) override
OUString GetPassword() const
virtual short run() override
void SetMinLen(sal_uInt16 Len)
css::uno::Reference< css::sdbc::XResultSet > createCursor(const css::uno::Sequence< OUString > &rPropertyNames, ResultSetInclude eMode=INCLUDE_FOLDERS_AND_DOCUMENTS)
static constexpr OUStringLiteral PROP_PASSWORD
static constexpr OUStringLiteral PROP_FILTEROPTIONS
virtual short run()
virtual WRITERPERFECT_DLLPUBLIC const unsigned char * read(unsigned long numBytes, unsigned long &numBytesRead) override
virtual WRITERPERFECT_DLLPUBLIC int seek(long offset, librevenge::RVNG_SEEK_TYPE seekType) override
float u
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
static uno::Reference< sdbc::XResultSet > getResultSet(const css::uno::Reference< css::ucb::XContent > &xPackageContent)
returns the list of stream name present in a folder
Reference< XComponentContext > getProcessComponentContext()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
bool equalsIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
INCLUDE_DOCUMENTS_ONLY
Definition of XML import service used with a Generator.
unsigned char sal_Bool
RET_OK