/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "xmlimp.hxx" #include #include #include #include #include #include #include #include #include #include "xmlfmt.hxx" #include "xmlictxt.hxx" #include "xmlmetai.hxx" #include "xmltext.hxx" using namespace com::sun::star; namespace writerperfect { namespace exp { namespace { /// Looks up mime type for a given image extension. OUString GetMimeType(const OUString &rExtension) { static const std::unordered_map vMimeTypes = { {"gif", "image/gif"}, {"jpg", "image/jpeg"}, {"png", "image/png"}, {"svg", "image/svg+xml"}, }; auto it = vMimeTypes.find(rExtension); return it == vMimeTypes.end() ? OUString() : it->second; } /// Determines the base directory for cover images, XMP metadata, popup images. OUString FindMediaDir(const OUString &rDocumentBaseURL, const uno::Sequence &rFilterData) { OUString aMediaDir; // See if filter data contains a media directory explicitly. for (sal_Int32 i = 0; i < rFilterData.getLength(); ++i) { if (rFilterData[i].Name == "RVNGMediaDir") { rFilterData[i].Value >>= aMediaDir; break; } } if (!aMediaDir.isEmpty()) return aMediaDir + "/"; // Not set explicitly, try to pick it up from the base directory. INetURLObject aURL(rDocumentBaseURL); try { aMediaDir = rtl::Uri::convertRelToAbs(rDocumentBaseURL, aURL.GetBase()) + "/"; } catch (const rtl::MalformedUriException &rException) { SAL_WARN("writerperfect", "FindMediaDir: convertRelToAbs() failed:" << rException.getMessage()); } return aMediaDir; } /// Picks up a cover image from the base directory. OUString FindCoverImage(const OUString &rDocumentBaseURL, OUString &rMimeType, const uno::Sequence &rFilterData) { OUString aRet; // See if filter data contains a cover image explicitly. for (sal_Int32 i = 0; i < rFilterData.getLength(); ++i) { if (rFilterData[i].Name == "RVNGCoverImage") { rFilterData[i].Value >>= aRet; break; } } if (!aRet.isEmpty()) { INetURLObject aRetURL(aRet); rMimeType = GetMimeType(aRetURL.GetExtension()); return aRet; } // Not set explicitly, try to pick it up from the base directory. if (rDocumentBaseURL.isEmpty()) return aRet; static const std::initializer_list vExtensions = { "gif", "jpg", "png", "svg" }; OUString aMediaDir = FindMediaDir(rDocumentBaseURL, rFilterData); for (const auto &rExtension : vExtensions) { aRet = aMediaDir + "cover." + rExtension; if (!aRet.isEmpty()) { SvFileStream aStream(aRet, StreamMode::READ); if (aStream.IsOpen()) { rMimeType = GetMimeType(rExtension); // File exists. return aRet; } else aRet.clear(); } } return aRet; } /// Picks up XMP metadata from the base directory. void FindXMPMetadata(const uno::Reference &xContext, const OUString &rDocumentBaseURL, const uno::Sequence &rFilterData, librevenge::RVNGPropertyList &rMetaData) { // See if filter data contains metadata explicitly. OUString aValue; for (sal_Int32 i = 0; i < rFilterData.getLength(); ++i) { if (rFilterData[i].Name == "RVNGIdentifier") { rFilterData[i].Value >>= aValue; if (!aValue.isEmpty()) rMetaData.insert("dc:identifier", aValue.toUtf8().getStr()); } else if (rFilterData[i].Name == "RVNGTitle") { rFilterData[i].Value >>= aValue; if (!aValue.isEmpty()) rMetaData.insert("dc:title", aValue.toUtf8().getStr()); } else if (rFilterData[i].Name == "RVNGInitialCreator") { rFilterData[i].Value >>= aValue; if (!aValue.isEmpty()) rMetaData.insert("meta:initial-creator", aValue.toUtf8().getStr()); } else if (rFilterData[i].Name == "RVNGLanguage") { rFilterData[i].Value >>= aValue; if (!aValue.isEmpty()) rMetaData.insert("dc:language", aValue.toUtf8().getStr()); } else if (rFilterData[i].Name == "RVNGDate") { rFilterData[i].Value >>= aValue; if (!aValue.isEmpty()) rMetaData.insert("dc:date", aValue.toUtf8().getStr()); } } // If not set explicitly, try to pick it up from the base directory. if (rDocumentBaseURL.isEmpty()) return; OUString aMediaDir = FindMediaDir(rDocumentBaseURL, rFilterData); INetURLObject aDocumentBaseURL(rDocumentBaseURL); OUString aURL = aMediaDir + aDocumentBaseURL.GetBase() + ".xmp"; SvFileStream aStream(aURL, StreamMode::READ); if (!aStream.IsOpen()) return; xml::sax::InputSource aInputSource; uno::Reference xStream(new utl::OStreamWrapper(aStream)); aInputSource.aInputStream = xStream; uno::Reference xParser = xml::sax::Parser::create(xContext); rtl::Reference xXMP(new XMPParser(rMetaData)); uno::Reference xDocumentHandler(xXMP.get()); xParser->setDocumentHandler(xDocumentHandler); try { xParser->parseStream(aInputSource); } catch (const uno::Exception &rException) { SAL_WARN("writerperfect", "FindXMPMetadata: parseStream() failed:" << rException.Message); return; } } } /// Handler for . class XMLBodyContext : public XMLImportContext { public: XMLBodyContext(XMLImport &rImport); rtl::Reference CreateChildContext(const OUString &rName, const css::uno::Reference &/*xAttribs*/) override; }; XMLBodyContext::XMLBodyContext(XMLImport &rImport) : XMLImportContext(rImport) { } rtl::Reference XMLBodyContext::CreateChildContext(const OUString &rName, const css::uno::Reference &/*xAttribs*/) { if (rName == "office:text") return new XMLBodyContentContext(mrImport); return nullptr; } /// Handler for . class XMLOfficeDocContext : public XMLImportContext { public: XMLOfficeDocContext(XMLImport &rImport); rtl::Reference CreateChildContext(const OUString &rName, const css::uno::Reference &/*xAttribs*/) override; }; XMLOfficeDocContext::XMLOfficeDocContext(XMLImport &rImport) : XMLImportContext(rImport) { } rtl::Reference XMLOfficeDocContext::CreateChildContext(const OUString &rName, const css::uno::Reference &/*xAttribs*/) { if (rName == "office:body") return new XMLBodyContext(mrImport); else if (rName == "office:meta") return new XMLMetaDocumentContext(mrImport); else if (rName == "office:automatic-styles") return new XMLStylesContext(mrImport, /*bAutomatic=*/true); else if (rName == "office:styles") return new XMLStylesContext(mrImport, /*bAutomatic=*/false); else if (rName == "office:font-face-decls") return new XMLFontFaceDeclsContext(mrImport); return nullptr; } XMLImport::XMLImport(const uno::Reference &xContext, librevenge::RVNGTextInterface &rGenerator, const OUString &rURL, const uno::Sequence &rDescriptor) : mrGenerator(rGenerator), mxContext(xContext) { uno::Sequence aFilterData; for (sal_Int32 i = 0; i < rDescriptor.getLength(); ++i) { if (rDescriptor[i].Name == "FilterData") { rDescriptor[i].Value >>= aFilterData; break; } } maMediaDir = FindMediaDir(rURL, aFilterData); OUString aMimeType; OUString aCoverImage = FindCoverImage(rURL, aMimeType, aFilterData); if (!aCoverImage.isEmpty()) { librevenge::RVNGBinaryData aBinaryData; SvFileStream aStream(aCoverImage, StreamMode::READ); SvMemoryStream aMemoryStream; aMemoryStream.WriteStream(aStream); aBinaryData.append(static_cast(aMemoryStream.GetBuffer()), aMemoryStream.GetSize()); librevenge::RVNGPropertyList aCoverImageProperties; aCoverImageProperties.insert("office:binary-data", aBinaryData); aCoverImageProperties.insert("librevenge:mime-type", aMimeType.toUtf8().getStr()); maCoverImages.append(aCoverImageProperties); } FindXMPMetadata(mxContext, rURL, aFilterData, maMetaData); mxUriReferenceFactory = uri::UriReferenceFactory::create(mxContext); } const librevenge::RVNGPropertyListVector &XMLImport::GetCoverImages() { return maCoverImages; } const librevenge::RVNGPropertyList &XMLImport::GetMetaData() { return maMetaData; } bool XMLImport::FillPopupData(const OUString &rURL, librevenge::RVNGPropertyList &rPropList) { uno::Reference xUriRef; try { xUriRef = mxUriReferenceFactory->parse(rURL); } catch (const uno::Exception &rException) { SAL_WARN("writerperfect", "XMLImport::FillPopupData: XUriReference::parse() failed:" << rException.Message); } bool bRelative = false; if (xUriRef.is()) bRelative = !xUriRef->isAbsolute(); if (!bRelative) return false; OUString aAbs = maMediaDir + rURL; if (aAbs.isEmpty()) return false; SvFileStream aStream(aAbs, StreamMode::READ); if (aStream.IsOpen()) { librevenge::RVNGBinaryData aBinaryData; SvMemoryStream aMemoryStream; aMemoryStream.WriteStream(aStream); aBinaryData.append(static_cast(aMemoryStream.GetBuffer()), aMemoryStream.GetSize()); rPropList.insert("office:binary-data", aBinaryData); INetURLObject aAbsURL(aAbs); OUString aMimeType = GetMimeType(aAbsURL.GetExtension()); rPropList.insert("librevenge:mime-type", aMimeType.toUtf8().getStr()); return true; } return false; } rtl::Reference XMLImport::CreateContext(const OUString &rName, const css::uno::Reference &/*xAttribs*/) { if (rName == "office:document") return new XMLOfficeDocContext(*this); return nullptr; } librevenge::RVNGTextInterface &XMLImport::GetGenerator() const { return mrGenerator; } std::map &XMLImport::GetAutomaticTextStyles() { return maAutomaticTextStyles; } std::map &XMLImport::GetAutomaticParagraphStyles() { return maAutomaticParagraphStyles; } std::map &XMLImport::GetAutomaticCellStyles() { return maAutomaticCellStyles; } std::map &XMLImport::GetAutomaticColumnStyles() { return maAutomaticColumnStyles; } std::map &XMLImport::GetAutomaticRowStyles() { return maAutomaticRowStyles; } std::map &XMLImport::GetAutomaticTableStyles() { return maAutomaticTableStyles; } std::map &XMLImport::GetAutomaticGraphicStyles() { return maAutomaticGraphicStyles; } std::map &XMLImport::GetTextStyles() { return maTextStyles; } std::map &XMLImport::GetParagraphStyles() { return maParagraphStyles; } std::map &XMLImport::GetCellStyles() { return maCellStyles; } std::map &XMLImport::GetColumnStyles() { return maColumnStyles; } std::map &XMLImport::GetRowStyles() { return maRowStyles; } std::map &XMLImport::GetTableStyles() { return maTableStyles; } std::map &XMLImport::GetGraphicStyles() { return maGraphicStyles; } void XMLImport::startDocument() { mrGenerator.startDocument(librevenge::RVNGPropertyList()); } void XMLImport::endDocument() { mrGenerator.endDocument(); } void XMLImport::startElement(const OUString &rName, const css::uno::Reference &xAttribs) { rtl::Reference xContext; if (!maContexts.empty()) { if (maContexts.top().is()) xContext = maContexts.top()->CreateChildContext(rName, xAttribs); } else xContext = CreateContext(rName, xAttribs); if (xContext.is()) xContext->startElement(rName, xAttribs); maContexts.push(xContext); } void XMLImport::endElement(const OUString &rName) { if (maContexts.empty()) return; if (maContexts.top().is()) maContexts.top()->endElement(rName); maContexts.pop(); } void XMLImport::characters(const OUString &rChars) { if (maContexts.top().is()) maContexts.top()->characters(rChars); } void XMLImport::ignorableWhitespace(const OUString &/*rWhitespaces*/) { } void XMLImport::processingInstruction(const OUString &/*rTarget*/, const OUString &/*rData*/) { } void XMLImport::setDocumentLocator(const css::uno::Reference &/*xLocator*/) { } } // namespace exp } // namespace writerperfect /* vim:set shiftwidth=4 softtabstop=4 expandtab: */