diff options
-rw-r--r-- | include/xmloff/txtparae.hxx | 4 | ||||
-rw-r--r-- | include/xmloff/xmltoken.hxx | 3 | ||||
-rw-r--r-- | schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng | 15 | ||||
-rw-r--r-- | xmloff/qa/unit/text.cxx | 36 | ||||
-rw-r--r-- | xmloff/source/core/xmltoken.cxx | 3 | ||||
-rw-r--r-- | xmloff/source/text/txtparae.cxx | 41 | ||||
-rw-r--r-- | xmloff/source/token/tokens.txt | 2 |
7 files changed, 104 insertions, 0 deletions
diff --git a/include/xmloff/txtparae.hxx b/include/xmloff/txtparae.hxx index 70716c1f77a2..546bf2adca3c 100644 --- a/include/xmloff/txtparae.hxx +++ b/include/xmloff/txtparae.hxx @@ -368,6 +368,10 @@ protected: const css::uno::Reference< css::beans::XPropertySet> & i_xPortion, bool i_bAutoStyles, bool i_isProgress, bool & rPrevCharIsSpace); + /// Exports a <loext:content-control> element. + void ExportContentControl(const css::uno::Reference<css::beans::XPropertySet>& xPortion, + bool bAutoStyles, bool isProgress, bool& rPrevCharIsSpace); + bool isAutoStylesCollected() const { return mbCollected; } virtual void exportTableAutoStyles(); diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index 86cc93f9a3a2..cdfb259f59ce 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -3487,6 +3487,9 @@ namespace xmloff::token { XML_COLOR_LUM_MOD, XML_COLOR_LUM_OFF, + XML_CONTENT_CONTROL, + XML_SHOWING_PLACE_HOLDER, + XML_TOKEN_END }; diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng index ccbc6e4336e8..68ac5c155d6a 100644 --- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng @@ -2803,6 +2803,21 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. </rng:element> </rng:define> + <!-- TODO(vmiklos) no proposal --> + <rng:define name="paragraph-content" combine="choice"> + <rng:element name="loext:content-control"> + <rng:optional> + <!-- default value: false --> + <rng:attribute name="loext:showing-place-holder"> + <rng:ref name="boolean"/> + </rng:attribute> + </rng:optional> + <rng:zeroOrMore> + <rng:ref name="paragraph-content-or-hyperlink"/> + </rng:zeroOrMore> + </rng:element> + </rng:define> + <!-- TODO no proposal --> <rng:define name="animation-element" combine="choice"> <rng:choice> diff --git a/xmloff/qa/unit/text.cxx b/xmloff/qa/unit/text.cxx index 3b067120eb7b..a7765e214066 100644 --- a/xmloff/qa/unit/text.cxx +++ b/xmloff/qa/unit/text.cxx @@ -383,6 +383,42 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testRelativeWidth) assertXPath(pXmlDoc, "//draw:frame", "width", "3.1492in"); } +CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testContentControlExport) +{ + // Given a document with a content control around one or more text portions: + getComponent() = loadFromDesktop("private:factory/swriter"); + uno::Reference<lang::XMultiServiceFactory> xMSF(getComponent(), uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xTextDocument(getComponent(), uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("ShowingPlaceHolder", uno::makeAny(true)); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // When exporting to ODT: + uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY); + uno::Sequence<beans::PropertyValue> aStoreProps = comphelper::InitPropertySequence({ + { "FilterName", uno::makeAny(OUString("writer8")) }, + }); + utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + xStorable->storeToURL(aTempFile.GetURL(), aStoreProps); + validate(aTempFile.GetFileName(), test::ODF); + + // Then make sure the expected markup is used: + std::unique_ptr<SvStream> pStream = parseExportStream(aTempFile, "content.xml"); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + // Without the accompanying fix in place, this failed with: + // - XPath '//loext:content-control' number of nodes is incorrect + // i.e. the content control was lost on export. + assertXPath(pXmlDoc, "//loext:content-control", "showing-place-holder", "true"); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index 34ae9d98cb71..2839bd0d1832 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -3490,6 +3490,9 @@ namespace xmloff::token { TOKEN("color-lum-mod", XML_COLOR_LUM_MOD ), TOKEN("color-lum-off", XML_COLOR_LUM_OFF ), + TOKEN("content-control", XML_CONTENT_CONTROL ), + TOKEN("showing-place-holder", XML_SHOWING_PLACE_HOLDER ), + #if OSL_DEBUG_LEVEL > 0 { 0, nullptr, std::nullopt, XML_TOKEN_END } diff --git a/xmloff/source/text/txtparae.cxx b/xmloff/source/text/txtparae.cxx index b813885f0812..5b8c07b70919 100644 --- a/xmloff/source/text/txtparae.cxx +++ b/xmloff/source/text/txtparae.cxx @@ -2212,6 +2212,10 @@ void XMLTextParagraphExport::exportTextRangeEnumeration( { exportMeta(xPropSet, bAutoStyles, bIsProgress, rPrevCharIsSpace); } + else if (sType == "ContentControl") + { + ExportContentControl(xPropSet, bAutoStyles, bIsProgress, rPrevCharIsSpace); + } else if (sType == gsTextFieldStart) { Reference< css::text::XFormField > xFormField(xPropSet->getPropertyValue(gsBookmark), UNO_QUERY); @@ -3868,6 +3872,43 @@ void XMLTextParagraphExport::exportMeta( exportTextRangeEnumeration(xTextEnum, i_bAutoStyles, i_isProgress, rPrevCharIsSpace); } +void XMLTextParagraphExport::ExportContentControl( + const uno::Reference<beans::XPropertySet>& xPortion, bool bAutoStyles, bool isProgress, + bool& rPrevCharIsSpace) +{ + // Do not export the element in the autostyle case. + bool bExport = !bAutoStyles; + if (!(GetExport().getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)) + { + bExport = false; + } + + uno::Reference<text::XTextContent> xTextContent(xPortion->getPropertyValue("ContentControl"), + uno::UNO_QUERY_THROW); + uno::Reference<container::XEnumerationAccess> xEA(xTextContent, uno::UNO_QUERY_THROW); + uno::Reference<container::XEnumeration> xTextEnum = xEA->createEnumeration(); + + if (bExport) + { + uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY_THROW); + bool bShowingPlaceHolder = false; + xPropertySet->getPropertyValue("ShowingPlaceHolder") >>= bShowingPlaceHolder; + if (bShowingPlaceHolder) + { + OUStringBuffer aBuffer; + sax::Converter::convertBool(aBuffer, bShowingPlaceHolder); + GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_SHOWING_PLACE_HOLDER, + aBuffer.makeStringAndClear()); + } + } + + SvXMLElementExport aElem(GetExport(), bExport, XML_NAMESPACE_LO_EXT, XML_CONTENT_CONTROL, false, + false); + + // Recurse to export content. + exportTextRangeEnumeration(xTextEnum, bAutoStyles, isProgress, rPrevCharIsSpace); +} + void XMLTextParagraphExport::PreventExportOfControlsInMuteSections( const Reference<XIndexAccess> & rShapes, const rtl::Reference<xmloff::OFormLayerXMLExport>& xFormExport ) diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index da3080aad63a..1a8d60b4ec1b 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -3233,4 +3233,6 @@ hlink folHlink color-lum-mod color-lum-off +content-control +showing-place-holder TOKEN_END_DUMMY |