comphelper/source/misc/sequenceashashmap.cxx | 137 ++++++++++++++ desktop/source/lib/init.cxx | 123 ------------ filter/source/pdf/pdffilter.cxx | 13 + include/comphelper/propertysequence.hxx | 6 vcl/qa/cppunit/pdfexport/data/link-wrong-page-partial.odg |binary vcl/qa/cppunit/pdfexport/pdfexport.cxx | 21 ++ 6 files changed, 178 insertions(+), 122 deletions(-)
New commits: commit c5c0ba7a87b5ad8f2d9314d9685c755546de0117 Author: Miklos Vajna <[email protected]> AuthorDate: Mon Jan 24 08:28:21 2022 +0100 Commit: Mike Kaganski <[email protected]> CommitDate: Tue Feb 1 09:07:55 2022 +0100 PDF export: allow setting filter data keys from the cmdline Follow-up improvement to commit 3eb9eb9906c9 (lok: add pdf version option for export, 2021-11-15), possibilities are endless. For example, to skip the first page of a Draw document: soffice --convert-to 'pdf:draw_pdf_Export:{"PageRange":{"type":"string","value":"2-"}}' test.odg Add watermark: soffice --convert-to 'pdf:draw_pdf_Export:{"TiledWatermark":{"type":"string","value":"draft"}}' test.odg Encrypt: soffice --convert-to 'pdf:draw_pdf_Export:{"EncryptFile":{"type":"boolean","value":"true"},"DocumentOpenPassword":{"type":"string","value":"secret"}}' test.odg Version 1.5 (instead of the default 1.6): soffice --convert-to 'pdf:draw_pdf_Export:{"SelectPdfVersion":{"type":"long","value":"15"}}' test.odg The cost of the verbose syntax is probably smaller than the benefit of having this 1:1 mapping from string to PropertyValues. (cherry picked from commit 0c3b8792b712e939d2ad524d554f96616b4844be) Change-Id: I2093a3a787a9578dd02a154593b7a020f4a0ba31 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129225 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Mike Kaganski <[email protected]> diff --git a/filter/source/pdf/pdffilter.cxx b/filter/source/pdf/pdffilter.cxx index 3bdc5a8cb756..c38c12df0a08 100644 --- a/filter/source/pdf/pdffilter.cxx +++ b/filter/source/pdf/pdffilter.cxx @@ -30,6 +30,9 @@ #include <com/sun/star/io/XOutputStream.hpp> +#include <comphelper/propertysequence.hxx> +#include <comphelper/sequence.hxx> + using namespace ::com::sun::star::io; PDFFilter::PDFFilter( const Reference< XComponentContext > &rxContext ) : @@ -47,6 +50,7 @@ bool PDFFilter::implExport( const Sequence< PropertyValue >& rDescriptor ) { Reference< XOutputStream > xOStm; Sequence< PropertyValue > aFilterData; + OUString aFilterOptions; sal_Int32 nLength = rDescriptor.getLength(); const PropertyValue* pValue = rDescriptor.getConstArray(); bool bIsRedactMode = false; @@ -60,6 +64,8 @@ bool PDFFilter::implExport( const Sequence< PropertyValue >& rDescriptor ) pValue[ i ].Value >>= xOStm; else if ( pValue[ i ].Name == "FilterData" ) pValue[ i ].Value >>= aFilterData; + else if ( pValue[ i ].Name == "FilterOptions" ) + pValue[ i ].Value >>= aFilterOptions; else if ( pValue[ i ].Name == "StatusIndicator" ) pValue[ i ].Value >>= xStatusIndicator; else if ( pValue[i].Name == "InteractionHandler" ) @@ -72,6 +78,13 @@ bool PDFFilter::implExport( const Sequence< PropertyValue >& rDescriptor ) pValue[i].Value >>= bIsRedactMode; } + if (!aFilterData.hasElements() && !aFilterOptions.isEmpty()) + { + // Allow setting filter data keys from the cmdline. + std::vector<PropertyValue> aData = comphelper::JsonToPropertyValues(aFilterOptions.toUtf8()); + aFilterData = comphelper::containerToSequence(aData); + } + /* we don't get FilterData if we are exporting directly to pdf, but we have to use the last user settings (especially for the CompressMode) */ if ( !aFilterData.hasElements() ) diff --git a/vcl/qa/cppunit/pdfexport/data/link-wrong-page-partial.odg b/vcl/qa/cppunit/pdfexport/data/link-wrong-page-partial.odg new file mode 100644 index 000000000000..1fad913e0493 Binary files /dev/null and b/vcl/qa/cppunit/pdfexport/data/link-wrong-page-partial.odg differ diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx index ba1468af5253..b6908df15184 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx @@ -1942,6 +1942,27 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testLinkWrongPage) CPPUNIT_ASSERT(!HasLinksOnPage(pPdfPage2)); } +CPPUNIT_TEST_FIXTURE(PdfExportTest, testPageRange) +{ + // Given a document with 3 pages: + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "link-wrong-page-partial.odg"; + + // When exporting that document to PDF, skipping the first page: + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export"); + aMediaDescriptor["FilterOptions"] + <<= OUString("{\"PageRange\":{\"type\":\"string\",\"value\":\"2-\"}}"); + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = exportAndParse(aURL, aMediaDescriptor); + + // Then make sure the resulting PDF has 2 pages: + CPPUNIT_ASSERT(pPdfDocument); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 2 + // - Actual : 3 + // i.e. FilterOptions was ignored. + CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount()); +} + CPPUNIT_TEST_FIXTURE(PdfExportTest, testLargePage) { // Import the bugdoc and export as PDF. commit 0ade7309e774e48a2074d09765856b78d5f72067 Author: Miklos Vajna <[email protected]> AuthorDate: Thu Jan 20 21:02:35 2022 +0100 Commit: Mike Kaganski <[email protected]> CommitDate: Tue Feb 1 09:07:43 2022 +0100 comphelper: move JsonToPropertyValues() from desktop/ Because filter/ code will need this in a bit, and that can't depend on desktop/. (cherry picked from commit 4871cae48c1c9f522a0c0cc85a852b0568ca31e6) Conflicts: desktop/source/lib/init.cxx Change-Id: I07f0f8ef30942a2d11388c6721c7f277e117bfba Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129224 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Mike Kaganski <[email protected]> diff --git a/comphelper/source/misc/sequenceashashmap.cxx b/comphelper/source/misc/sequenceashashmap.cxx index eb78e60c55d2..c6ac57326935 100644 --- a/comphelper/source/misc/sequenceashashmap.cxx +++ b/comphelper/source/misc/sequenceashashmap.cxx @@ -19,11 +19,82 @@ #include <sal/config.h> +#include <boost/property_tree/json_parser.hpp> + #include <com/sun/star/beans/NamedValue.hpp> #include <com/sun/star/beans/PropertyValue.hpp> #include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/reflection/XIdlField.hpp> +#include <com/sun/star/reflection/theCoreReflection.hpp> #include <comphelper/sequenceashashmap.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <sal/log.hxx> + +using namespace com::sun::star; +namespace +{ +uno::Any jsonToUnoAny(const boost::property_tree::ptree& aTree) +{ + uno::Any aAny; + uno::Any aValue; + sal_Int32 nFields; + uno::Reference<reflection::XIdlField> aField; + boost::property_tree::ptree aNodeNull, aNodeValue, aNodeField; + const std::string& rType = aTree.get<std::string>("type", ""); + const std::string& rValue = aTree.get<std::string>("value", ""); + uno::Sequence<uno::Reference<reflection::XIdlField>> aFields; + uno::Reference<reflection::XIdlClass> xIdlClass + = css::reflection::theCoreReflection::get(comphelper::getProcessComponentContext()) + ->forName(OUString::fromUtf8(rType.c_str())); + if (xIdlClass.is()) + { + uno::TypeClass aTypeClass = xIdlClass->getTypeClass(); + xIdlClass->createObject(aAny); + aFields = xIdlClass->getFields(); + nFields = aFields.getLength(); + aNodeValue = aTree.get_child("value", aNodeNull); + if (nFields > 0 && aNodeValue != aNodeNull) + { + for (sal_Int32 itField = 0; itField < nFields; ++itField) + { + aField = aFields[itField]; + aNodeField = aNodeValue.get_child(aField->getName().toUtf8().getStr(), aNodeNull); + if (aNodeField != aNodeNull) + { + aValue = jsonToUnoAny(aNodeField); + aField->set(aAny, aValue); + } + } + } + else if (!rValue.empty()) + { + if (aTypeClass == uno::TypeClass_VOID) + aAny.clear(); + else if (aTypeClass == uno::TypeClass_BYTE) + aAny <<= static_cast<sal_Int8>(OString(rValue.c_str()).toInt32()); + else if (aTypeClass == uno::TypeClass_BOOLEAN) + aAny <<= OString(rValue.c_str()).toBoolean(); + else if (aTypeClass == uno::TypeClass_SHORT) + aAny <<= static_cast<sal_Int16>(OString(rValue.c_str()).toInt32()); + else if (aTypeClass == uno::TypeClass_UNSIGNED_SHORT) + aAny <<= static_cast<sal_uInt16>(OString(rValue.c_str()).toUInt32()); + else if (aTypeClass == uno::TypeClass_LONG) + aAny <<= OString(rValue.c_str()).toInt32(); + else if (aTypeClass == uno::TypeClass_UNSIGNED_LONG) + aAny <<= static_cast<sal_uInt32>(OString(rValue.c_str()).toInt32()); + else if (aTypeClass == uno::TypeClass_FLOAT) + aAny <<= OString(rValue.c_str()).toFloat(); + else if (aTypeClass == uno::TypeClass_DOUBLE) + aAny <<= OString(rValue.c_str()).toDouble(); + else if (aTypeClass == uno::TypeClass_STRING) + aAny <<= OUString::fromUtf8(rValue.c_str()); + } + } + return aAny; +} +} namespace comphelper{ @@ -234,6 +305,72 @@ void SequenceAsHashMap::update(const SequenceAsHashMap& rUpdate) } } +std::vector<css::beans::PropertyValue> JsonToPropertyValues(const OString& rJson) +{ + std::vector<beans::PropertyValue> aArguments; + boost::property_tree::ptree aTree, aNodeNull, aNodeValue; + std::stringstream aStream(rJson.getStr()); + boost::property_tree::read_json(aStream, aTree); + + for (const auto& rPair : aTree) + { + const std::string& rType = rPair.second.get<std::string>("type", ""); + const std::string& rValue = rPair.second.get<std::string>("value", ""); + + beans::PropertyValue aValue; + aValue.Name = OUString::fromUtf8(rPair.first.c_str()); + if (rType == "string") + aValue.Value <<= OUString::fromUtf8(rValue.c_str()); + else if (rType == "boolean") + aValue.Value <<= OString(rValue.c_str()).toBoolean(); + else if (rType == "float") + aValue.Value <<= OString(rValue.c_str()).toFloat(); + else if (rType == "long") + aValue.Value <<= OString(rValue.c_str()).toInt32(); + else if (rType == "short") + aValue.Value <<= sal_Int16(OString(rValue.c_str()).toInt32()); + else if (rType == "unsigned short") + aValue.Value <<= sal_uInt16(OString(rValue.c_str()).toUInt32()); + else if (rType == "int64") + aValue.Value <<= OString(rValue.c_str()).toInt64(); + else if (rType == "int32") + aValue.Value <<= OString(rValue.c_str()).toInt32(); + else if (rType == "int16") + aValue.Value <<= sal_Int16(OString(rValue.c_str()).toInt32()); + else if (rType == "uint64") + aValue.Value <<= OString(rValue.c_str()).toUInt64(); + else if (rType == "uint32") + aValue.Value <<= OString(rValue.c_str()).toUInt32(); + else if (rType == "uint16") + aValue.Value <<= sal_uInt16(OString(rValue.c_str()).toUInt32()); + else if (rType == "[]byte") + { + aNodeValue = rPair.second.get_child("value", aNodeNull); + if (aNodeValue != aNodeNull && aNodeValue.size() == 0) + { + uno::Sequence<sal_Int8> aSeqByte(reinterpret_cast<const sal_Int8*>(rValue.c_str()), + rValue.size()); + aValue.Value <<= aSeqByte; + } + } + else if (rType == "[]any") + { + aNodeValue = rPair.second.get_child("value", aNodeNull); + if (aNodeValue != aNodeNull && !aNodeValue.empty()) + { + uno::Sequence<uno::Any> aSeq(aNodeValue.size()); + std::transform(aNodeValue.begin(), aNodeValue.end(), aSeq.getArray(), + [](const auto& rSeqPair) { return jsonToUnoAny(rSeqPair.second); }); + aValue.Value <<= aSeq; + } + } + else + SAL_WARN("comphelper", "JsonToPropertyValues: unhandled type '" << rType << "'"); + aArguments.push_back(aValue); + } + return aArguments; +} + } // namespace comphelper /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 9ae83863d341..1c2952ab8953 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -73,9 +73,6 @@ #include <com/sun/star/lang/Locale.hpp> #include <com/sun/star/lang/XComponent.hpp> #include <com/sun/star/lang/XMultiServiceFactory.hpp> -#include <com/sun/star/reflection/theCoreReflection.hpp> -#include <com/sun/star/reflection/XIdlClass.hpp> -#include <com/sun/star/reflection/XIdlReflection.hpp> #include <com/sun/star/style/XStyleFamiliesSupplier.hpp> #include <com/sun/star/util/URLTransformer.hpp> #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp> @@ -372,130 +369,12 @@ static OUString getAbsoluteURL(const char* pURL) return OUString(); } -static uno::Any jsonToUnoAny(const boost::property_tree::ptree& aTree) -{ - uno::Any aAny; - uno::Any aValue; - sal_Int32 nFields; - uno::Reference< reflection::XIdlField > aField; - boost::property_tree::ptree aNodeNull, aNodeValue, aNodeField; - const std::string& rType = aTree.get<std::string>("type", ""); - const std::string& rValue = aTree.get<std::string>("value", ""); - uno::Sequence< uno::Reference< reflection::XIdlField > > aFields; - uno::Reference< reflection:: XIdlClass > xIdlClass = - css::reflection::theCoreReflection::get(comphelper::getProcessComponentContext())->forName(OUString::fromUtf8(rType.c_str())); - if (xIdlClass.is()) - { - uno::TypeClass aTypeClass = xIdlClass->getTypeClass(); - xIdlClass->createObject(aAny); - aFields = xIdlClass->getFields(); - nFields = aFields.getLength(); - aNodeValue = aTree.get_child("value", aNodeNull); - if (nFields > 0 && aNodeValue != aNodeNull) - { - for (sal_Int32 itField = 0; itField < nFields; ++itField) - { - aField = aFields[itField]; - aNodeField = aNodeValue.get_child(aField->getName().toUtf8().getStr(), aNodeNull); - if (aNodeField != aNodeNull) - { - aValue = jsonToUnoAny(aNodeField); - aField->set(aAny, aValue); - } - } - } - else if (!rValue.empty()) - { - if (aTypeClass == uno::TypeClass_VOID) - aAny.clear(); - else if (aTypeClass == uno::TypeClass_BYTE) - aAny <<= static_cast<sal_Int8>(OString(rValue.c_str()).toInt32()); - else if (aTypeClass == uno::TypeClass_BOOLEAN) - aAny <<= OString(rValue.c_str()).toBoolean(); - else if (aTypeClass == uno::TypeClass_SHORT) - aAny <<= static_cast<sal_Int16>(OString(rValue.c_str()).toInt32()); - else if (aTypeClass == uno::TypeClass_UNSIGNED_SHORT) - aAny <<= static_cast<sal_uInt16>(OString(rValue.c_str()).toUInt32()); - else if (aTypeClass == uno::TypeClass_LONG) - aAny <<= OString(rValue.c_str()).toInt32(); - else if (aTypeClass == uno::TypeClass_UNSIGNED_LONG) - aAny <<= static_cast<sal_uInt32>(OString(rValue.c_str()).toInt32()); - else if (aTypeClass == uno::TypeClass_FLOAT) - aAny <<= OString(rValue.c_str()).toFloat(); - else if (aTypeClass == uno::TypeClass_DOUBLE) - aAny <<= OString(rValue.c_str()).toDouble(); - else if (aTypeClass == uno::TypeClass_STRING) - aAny <<= OUString::fromUtf8(rValue.c_str()); - } - } - return aAny; -} - std::vector<beans::PropertyValue> desktop::jsonToPropertyValuesVector(const char* pJSON) { std::vector<beans::PropertyValue> aArguments; if (pJSON && pJSON[0] != '\0') { - boost::property_tree::ptree aTree, aNodeNull, aNodeValue; - std::stringstream aStream(pJSON); - boost::property_tree::read_json(aStream, aTree); - - for (const auto& rPair : aTree) - { - const std::string& rType = rPair.second.get<std::string>("type", ""); - const std::string& rValue = rPair.second.get<std::string>("value", ""); - - beans::PropertyValue aValue; - aValue.Name = OUString::fromUtf8(rPair.first.c_str()); - if (rType == "string") - aValue.Value <<= OUString::fromUtf8(rValue.c_str()); - else if (rType == "boolean") - aValue.Value <<= OString(rValue.c_str()).toBoolean(); - else if (rType == "float") - aValue.Value <<= OString(rValue.c_str()).toFloat(); - else if (rType == "long") - aValue.Value <<= OString(rValue.c_str()).toInt32(); - else if (rType == "short") - aValue.Value <<= sal_Int16(OString(rValue.c_str()).toInt32()); - else if (rType == "unsigned short") - aValue.Value <<= sal_uInt16(OString(rValue.c_str()).toUInt32()); - else if (rType == "int64") - aValue.Value <<= OString(rValue.c_str()).toInt64(); - else if (rType == "int32") - aValue.Value <<= OString(rValue.c_str()).toInt32(); - else if (rType == "int16") - aValue.Value <<= sal_Int16(OString(rValue.c_str()).toInt32()); - else if (rType == "uint64") - aValue.Value <<= OString(rValue.c_str()).toUInt64(); - else if (rType == "uint32") - aValue.Value <<= OString(rValue.c_str()).toUInt32(); - else if (rType == "uint16") - aValue.Value <<= sal_uInt16(OString(rValue.c_str()).toUInt32()); - else if (rType == "[]byte") - { - aNodeValue = rPair.second.get_child("value", aNodeNull); - if (aNodeValue != aNodeNull && aNodeValue.size() == 0) - { - uno::Sequence< sal_Int8 > aSeqByte(reinterpret_cast<const sal_Int8*>(rValue.c_str()), rValue.size()); - aValue.Value <<= aSeqByte; - } - } - else if (rType == "[]any") - { - aNodeValue = rPair.second.get_child("value", aNodeNull); - if (aNodeValue != aNodeNull && !aNodeValue.empty()) - { - sal_Int32 itSeq = 0; - uno::Sequence< uno::Any > aSeq(aNodeValue.size()); - for (const auto& rSeqPair : aNodeValue) - aSeq[itSeq++] = jsonToUnoAny(rSeqPair.second); - aValue.Value <<= aSeq; - } - } - else - SAL_WARN("desktop.lib", "jsonToPropertyValuesVector: unhandled type '"<<rType<<"'"); - aArguments.push_back(aValue); - } + aArguments = comphelper::JsonToPropertyValues(pJSON); } return aArguments; } diff --git a/include/comphelper/propertysequence.hxx b/include/comphelper/propertysequence.hxx index 3f9838f9ab8f..a73109b0ffa9 100644 --- a/include/comphelper/propertysequence.hxx +++ b/include/comphelper/propertysequence.hxx @@ -13,10 +13,14 @@ #include <utility> #include <algorithm> #include <initializer_list> +#include <vector> + #include <com/sun/star/uno/Any.hxx> #include <com/sun/star/uno/Sequence.hxx> #include <com/sun/star/beans/PropertyValue.hpp> +#include <comphelper/comphelperdllapi.h> + namespace comphelper { /// Init list for property sequences. @@ -48,6 +52,8 @@ namespace comphelper }); return vResult; } + + COMPHELPER_DLLPUBLIC std::vector<css::beans::PropertyValue> JsonToPropertyValues(const OString& rJson); } // namespace comphelper
