summaryrefslogtreecommitdiff
path: root/writerfilter/source
diff options
context:
space:
mode:
authorVasily Melenchuk <vasily.melenchuk@cib.de>2021-11-24 14:50:12 +0300
committerThorsten Behrens <thorsten.behrens@allotropia.de>2021-12-21 23:23:51 +0100
commita4432eb0946c0bc775b3d30b634bef5d66544f8d (patch)
tree67b339d47ba727f55b989322091f20804d9c6c09 /writerfilter/source
parentc3d3eb7bdcb07824af7d205112f36201634aee59 (diff)
tdf#104823: support for sdt plain text fields
This is a squashed commit containing set of changes: * Create a input field from sdt text block. * Advanced support for reading field data from data bindings which can point to custom xml or properties xml. For this XOOXMLDocumentPropertiesImporter idl interface was extrended with extra getterrs to get properties as xml dom elements. * Support for exporting of this feature back to docx. For this some extra parameters for sdt block are kept in newly introduced grabbag for input fields. If field does not contain grabbag it being exported as before (FILLIN or whatsoever), otherwise sdt block is counstructed based on data from grabbag. * Basic support for updating custom xml values back into custom xmls with usage of xslt transformations. To achieve this extra parameters were introduced to XXSLTTransformer: now it is able to support in-memory transformation stylesheets. Some unittests were corrected: since sdt plain text edit area is a field located inside paragraph in outout corresponding sdt is also located inside paragraph (instead of Word's approach with paragraph inside sdt). Seems this is not critical. Change-Id: I1a73ef300db3619804f7adf18579bea708764c14 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/127015 Tested-by: Jenkins Reviewed-by: Thorsten Behrens <thorsten.behrens@allotropia.de> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/127127
Diffstat (limited to 'writerfilter/source')
-rw-r--r--writerfilter/source/dmapper/DomainMapper.cxx40
-rw-r--r--writerfilter/source/dmapper/SdtHelper.cxx158
-rw-r--r--writerfilter/source/dmapper/SdtHelper.hxx15
3 files changed, 194 insertions, 19 deletions
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx
index a7b759ed0e2d..b5e08cfe5bc9 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -1071,6 +1071,11 @@ void DomainMapper::lcl_attribute(Id nName, Value & val)
}
break;
case NS_ooxml::LN_CT_SdtBlock_sdtContent:
+ if (m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::unknown)
+ {
+ // Still not determined content type? and it is even not unsupported? Then it is plain text field
+ m_pImpl->m_pSdtHelper->setControlType(SdtControlType::plainText);
+ }
m_pImpl->SetSdt(true);
break;
case NS_ooxml::LN_CT_SdtBlock_sdtEndContent:
@@ -1088,6 +1093,9 @@ void DomainMapper::lcl_attribute(Id nName, Value & val)
case SdtControlType::dropDown:
m_pImpl->m_pSdtHelper->createDropDownControl();
break;
+ case SdtControlType::plainText:
+ m_pImpl->m_pSdtHelper->createPlainTextControl();
+ break;
case SdtControlType::datePicker:
m_pImpl->m_pSdtHelper->createDateContentControl();
break;
@@ -2701,6 +2709,17 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext )
m_pImpl->m_pSdtHelper->getLocale().append(sStringValue);
}
break;
+ case NS_ooxml::LN_CT_SdtPr_text:
+ {
+ m_pImpl->m_pSdtHelper->setControlType(SdtControlType::plainText);
+ enableInteropGrabBag("ooxml:CT_SdtPr_text");
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ pProperties->resolve(*this);
+ m_pImpl->m_pSdtHelper->appendToInteropGrabBag(getInteropGrabBag());
+ m_pImpl->disableInteropGrabBag();
+ }
+ break;
case NS_ooxml::LN_CT_SdtPr_dataBinding:
case NS_ooxml::LN_CT_SdtPr_equation:
case NS_ooxml::LN_CT_SdtPr_checkbox:
@@ -2709,7 +2728,6 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext )
case NS_ooxml::LN_CT_SdtPr_picture:
case NS_ooxml::LN_CT_SdtPr_citation:
case NS_ooxml::LN_CT_SdtPr_group:
- case NS_ooxml::LN_CT_SdtPr_text:
case NS_ooxml::LN_CT_SdtPr_id:
case NS_ooxml::LN_CT_SdtPr_alias:
case NS_ooxml::LN_CT_SdtPlaceholder_docPart:
@@ -2727,13 +2745,21 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext )
case NS_ooxml::LN_CT_SdtPr_picture: sName = "ooxml:CT_SdtPr_picture"; break;
case NS_ooxml::LN_CT_SdtPr_citation: sName = "ooxml:CT_SdtPr_citation"; break;
case NS_ooxml::LN_CT_SdtPr_group: sName = "ooxml:CT_SdtPr_group"; break;
- case NS_ooxml::LN_CT_SdtPr_text: sName = "ooxml:CT_SdtPr_text"; break;
case NS_ooxml::LN_CT_SdtPr_id: sName = "ooxml:CT_SdtPr_id"; break;
case NS_ooxml::LN_CT_SdtPr_alias: sName = "ooxml:CT_SdtPr_alias"; break;
case NS_ooxml::LN_CT_SdtPlaceholder_docPart: sName = "ooxml:CT_SdtPlaceholder_docPart"; break;
case NS_ooxml::LN_CT_SdtPr_color: sName = "ooxml:CT_SdtPr_color"; break;
default: assert(false);
};
+ if (
+ nSprmId == NS_ooxml::LN_CT_SdtPr_checkbox ||
+ nSprmId == NS_ooxml::LN_CT_SdtPr_docPartObj ||
+ nSprmId == NS_ooxml::LN_CT_SdtPr_docPartList ||
+ nSprmId == NS_ooxml::LN_CT_SdtPr_picture ||
+ nSprmId == NS_ooxml::LN_CT_SdtPr_citation)
+ {
+ m_pImpl->m_pSdtHelper->setControlType(SdtControlType::unsupported);
+ }
enableInteropGrabBag(sName);
// process subitems
@@ -3556,6 +3582,16 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, size_t len)
return;
}
}
+ else if (m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::plainText)
+ {
+ m_pImpl->m_pSdtHelper->getSdtTexts().append(sText);
+ if (bNewLine)
+ {
+ m_pImpl->m_pSdtHelper->createPlainTextControl();
+ finishParagraph();
+ }
+ return;
+ }
else if (!m_pImpl->m_pSdtHelper->isInteropGrabBagEmpty())
{
// there are unsupported SDT properties in the document
diff --git a/writerfilter/source/dmapper/SdtHelper.cxx b/writerfilter/source/dmapper/SdtHelper.cxx
index c53a93f62de6..c5ec47f2be23 100644
--- a/writerfilter/source/dmapper/SdtHelper.cxx
+++ b/writerfilter/source/dmapper/SdtHelper.cxx
@@ -12,26 +12,30 @@
#include <com/sun/star/drawing/XControlShape.hpp>
#include <com/sun/star/text/VertOrientation.hpp>
#include <editeng/unoprnms.hxx>
+#include <sal/log.hxx>
#include <vcl/svapp.hxx>
#include <vcl/outdev.hxx>
+#include <comphelper/string.hxx>
#include <comphelper/sequence.hxx>
#include <xmloff/odffields.hxx>
#include <com/sun/star/text/XTextField.hpp>
#include "DomainMapper_Impl.hxx"
#include "StyleSheetTable.hxx"
#include <officecfg/Office/Writer.hxx>
-
#include <com/sun/star/util/XRefreshable.hpp>
#include <com/sun/star/text/XTextFieldsSupplier.hpp>
-
+#include <com/sun/star/document/XOOXMLDocumentPropertiesImporter.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
#include <ooxml/OOXMLDocument.hxx>
#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
#include <com/sun/star/xml/dom/XNode.hpp>
namespace writerfilter::dmapper
{
using namespace ::com::sun::star;
using namespace ::css::xml::xpath;
+using namespace ::comphelper;
/// w:sdt's w:dropDownList doesn't have width, so guess the size based on the longest string.
static awt::Size lcl_getOptimalWidth(const StyleSheetTablePtr& pStyleSheet,
@@ -80,39 +84,126 @@ SdtHelper::SdtHelper(DomainMapper_Impl& rDM_Impl,
, m_aControlType(SdtControlType::unknown)
, m_bHasElements(false)
, m_bOutsideAParagraph(false)
+ , m_bPropertiesXMLsLoaded(false)
{
}
SdtHelper::~SdtHelper() = default;
+void SdtHelper::loadPropertiesXMLs()
+{
+ // Initialize properties xml storage (m_xPropertiesXMLs)
+ uno::Reference<uno::XInterface> xTemp
+ = m_xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.document.OOXMLDocumentPropertiesImporter", m_xComponentContext);
+ uno::Reference<document::XOOXMLDocumentPropertiesImporter> xImporter(xTemp, uno::UNO_QUERY);
+ if (!xImporter.is())
+ return;
+
+ uno::Reference<xml::dom::XDocumentBuilder> xDomBuilder(
+ xml::dom::DocumentBuilder::create(m_xComponentContext));
+ if (!xDomBuilder.is())
+ return;
+
+ std::vector<uno::Reference<xml::dom::XDocument>> aPropDocs;
+
+ // Load core properties
+ try
+ {
+ auto xCorePropsStream = xImporter->getCorePropertiesStream(m_rDM_Impl.m_xDocumentStorage);
+ aPropDocs.push_back(xDomBuilder->parse(xCorePropsStream));
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN("writerfilter",
+ "SdtHelper::loadPropertiesXMLs: failed loading core properties XML");
+ }
+
+ // Load extended properties
+ try
+ {
+ auto xExtPropsStream
+ = xImporter->getExtendedPropertiesStream(m_rDM_Impl.m_xDocumentStorage);
+ aPropDocs.push_back(xDomBuilder->parse(xExtPropsStream));
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN("writerfilter",
+ "SdtHelper::loadPropertiesXMLs: failed loading extended properties XML");
+ }
+
+ // TODO: some other property items?
+
+ // Add custom XMLs
+ uno::Sequence<uno::Reference<xml::dom::XDocument>> aCustomXmls
+ = m_rDM_Impl.getDocumentReference()->getCustomXmlDomList();
+ for (const auto& xDoc : aCustomXmls)
+ {
+ aPropDocs.push_back(xDoc);
+ }
+
+ m_xPropertiesXMLs = comphelper::containerToSequence(aPropDocs);
+ m_bPropertiesXMLsLoaded = true;
+}
+
+static void lcl_registerNamespaces(const OUString& sNamespaceString,
+ const uno::Reference<XXPathAPI>& xXPathAPI)
+{
+ // Split namespaces and register it in XPathAPI
+ auto aNamespaces = string::split(sNamespaceString, ' ');
+ for (const auto& sNamespace : aNamespaces)
+ {
+ // Here we have just one namespace in format "xmlns:ns0='http://someurl'"
+ auto aNamespace = string::split(sNamespace, '=');
+ if (aNamespace.size() < 2)
+ {
+ SAL_WARN("writerfilter",
+ "SdtHelper::getValueFromDataBinding: invalid namespace: " << sNamespace);
+ continue;
+ }
+
+ auto aNamespaceId = string::split(aNamespace[0], ':');
+ if (aNamespaceId.size() < 2)
+ {
+ SAL_WARN("writerfilter",
+ "SdtHelper::getValueFromDataBinding: invalid namespace: " << aNamespace[0]);
+ continue;
+ }
+
+ OUString sNamespaceURL = aNamespace[1];
+ sNamespaceURL = string::strip(sNamespaceURL, ' ');
+ sNamespaceURL = string::strip(sNamespaceURL, '\'');
+
+ xXPathAPI->registerNS(aNamespaceId[1], sNamespaceURL);
+ }
+}
+
std::optional<OUString> SdtHelper::getValueFromDataBinding()
{
// No xpath - nothing to do
if (m_sDataBindingXPath.isEmpty())
return {};
- writerfilter::ooxml::OOXMLDocument* pDocument = m_rDM_Impl.getDocumentReference();
- assert(pDocument);
- if (!pDocument)
- return {};
+ // Load properties XMLs
+ if (!m_bPropertiesXMLsLoaded)
+ loadPropertiesXMLs();
- // Iterate all custom xmls documents and evaluate xpath to get value
- uno::Sequence<uno::Reference<xml::dom::XDocument>> aCustomXmls
- = pDocument->getCustomXmlDomList();
- for (const auto& xCustomXml : aCustomXmls)
- {
- uno::Reference<XXPathAPI> xXpathAPI = XPathAPI::create(m_xComponentContext);
+ uno::Reference<XXPathAPI> xXpathAPI = XPathAPI::create(m_xComponentContext);
+
+ lcl_registerNamespaces(m_sDataBindingPrefixMapping, xXpathAPI);
- //xXpathAPI->registerNS("ns0", m_sDataBindingPrefixMapping);
- xXpathAPI->registerNS("ns0", "http://schemas.microsoft.com/vsto/samples");
- uno::Reference<XXPathObject> xResult = xXpathAPI->eval(xCustomXml, m_sDataBindingXPath);
+ // Iterate all properties xml documents and try to fetch data
+ for (const auto& xDocument : m_xPropertiesXMLs)
+ {
+ uno::Reference<XXPathObject> xResult = xXpathAPI->eval(xDocument, m_sDataBindingXPath);
- if (xResult.is())
+ if (xResult.is() && xResult->getNodeList() && xResult->getNodeList()->getLength())
{
return xResult->getString();
}
}
+ // No data
return {};
}
@@ -176,6 +267,38 @@ void SdtHelper::createDropDownControl()
setControlType(SdtControlType::unknown);
}
+void SdtHelper::createPlainTextControl()
+{
+ assert(getControlType() == SdtControlType::plainText);
+
+ OUString aDefaultText = m_aSdtTexts.makeStringAndClear();
+
+ // create field
+ uno::Reference<css::text::XTextField> xControlModel(
+ m_rDM_Impl.GetTextFactory()->createInstance("com.sun.star.text.TextField.Input"),
+ uno::UNO_QUERY);
+
+ // set properties
+ uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY);
+
+ std::optional<OUString> oData = getValueFromDataBinding();
+ if (oData.has_value())
+ aDefaultText = *oData;
+
+ xPropertySet->setPropertyValue("Content", uno::makeAny(aDefaultText));
+
+ // add it into document
+ m_rDM_Impl.appendTextContent(xControlModel, uno::Sequence<beans::PropertyValue>());
+
+ // Store all unused sdt parameters from grabbag
+ xPropertySet->setPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG,
+ uno::makeAny(getInteropGrabBagAndClear()));
+
+ // clean up
+ m_aDropDownItems.clear();
+ setControlType(SdtControlType::unknown);
+}
+
void SdtHelper::createDateContentControl()
{
if (!m_xDateFieldStartRange.is())
@@ -251,7 +374,8 @@ void SdtHelper::createDateContentControl()
setControlType(SdtControlType::unknown);
// Store all unused sdt parameters from grabbag
- xNameCont->insertByName("SdtParams", uno::makeAny(getInteropGrabBagAndClear()));
+ xNameCont->insertByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG,
+ uno::makeAny(getInteropGrabBagAndClear()));
}
void SdtHelper::createControlShape(awt::Size aSize,
diff --git a/writerfilter/source/dmapper/SdtHelper.hxx b/writerfilter/source/dmapper/SdtHelper.hxx
index 9d2c6b1da7cf..e58d73168d79 100644
--- a/writerfilter/source/dmapper/SdtHelper.hxx
+++ b/writerfilter/source/dmapper/SdtHelper.hxx
@@ -14,6 +14,8 @@
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/xml/dom/XDocument.hpp>
#include <rtl/ustrbuf.hxx>
#include <tools/ref.hxx>
@@ -39,6 +41,8 @@ enum class SdtControlType
{
datePicker,
dropDown,
+ plainText,
+ unsupported, // Sdt block is defined, but we still do not support such type of field
unknown
};
@@ -82,6 +86,13 @@ class SdtHelper final : public virtual SvRefBase
/// The last stored SDT element is outside paragraphs.
bool m_bOutsideAParagraph;
+ /// Storage for all properties documents as xml::dom::XDocument for later quering xpath for data
+ css::uno::Sequence<css::uno::Reference<css::xml::dom::XDocument>> m_xPropertiesXMLs;
+
+ /// Check if m_xPropertiesXMLs is initialized and loaded (need extra flag to distinguish
+ /// empty sequence from not yet initialized)
+ bool m_bPropertiesXMLsLoaded;
+
/// Create and append the drawing::XControlShape, containing the various models.
void createControlShape(css::awt::Size aSize,
css::uno::Reference<css::awt::XControlModel> const& xControlModel,
@@ -89,6 +100,8 @@ class SdtHelper final : public virtual SvRefBase
std::optional<OUString> getValueFromDataBinding();
+ void loadPropertiesXMLs();
+
public:
explicit SdtHelper(DomainMapper_Impl& rDM_Impl,
css::uno::Reference<css::uno::XComponentContext> const& xContext);
@@ -132,6 +145,8 @@ public:
/// Create date control from w:sdt's w:date.
void createDateContentControl();
+ void createPlainTextControl();
+
void appendToInteropGrabBag(const css::beans::PropertyValue& rValue);
css::uno::Sequence<css::beans::PropertyValue> getInteropGrabBagAndClear();
bool isInteropGrabBagEmpty() const;