diff options
author | Michael Stahl <michael.stahl@allotropia.de> | 2022-06-27 19:45:53 +0200 |
---|---|---|
committer | Michael Stahl <michael.stahl@allotropia.de> | 2022-07-08 14:32:03 +0200 |
commit | 463178fef5c22f1a04d10e54491852d56e2038b0 (patch) | |
tree | c8a813bc28fdb2921013d313f10045ad5053562a /xmloff/source | |
parent | a3e9fede7e998979db7941a527496eb98c822e98 (diff) |
xmloff, sw: ODF import/export of fieldmark separator
Previously there were only the fieldmark-start and fieldmark-end
elements; now add a fieldmark-separator, so that the field command can
be stored as character data (and elements if there is any formatting)
instead of attribute value.
The import is a bit more complicated because there are already existing
documents that are missing the separator; it's not possible to predict
if there will be a separator at the time when the start element is seen,
so add some hack to toggle inserting the SwXFieldmark with separator
at the end (when the document contains a separator, i.e. after the
command) or separator at the start (when the document is missing a
separator, i.e. after the result).
Change-Id: I14d2f50f57d690e75643df5d14fd881ebc759a41
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136513
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
Diffstat (limited to 'xmloff/source')
-rw-r--r-- | xmloff/source/core/xmltoken.cxx | 1 | ||||
-rw-r--r-- | xmloff/source/text/XMLTextMarkImportContext.cxx | 46 | ||||
-rw-r--r-- | xmloff/source/text/XMLTextMarkImportContext.hxx | 3 | ||||
-rw-r--r-- | xmloff/source/text/txtimp.cxx | 36 | ||||
-rw-r--r-- | xmloff/source/text/txtparae.cxx | 14 | ||||
-rw-r--r-- | xmloff/source/text/txtparai.cxx | 1 | ||||
-rw-r--r-- | xmloff/source/token/tokens.txt | 1 |
7 files changed, 87 insertions, 15 deletions
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index a996c4e9fb54..1606dbc68ab3 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -3318,6 +3318,7 @@ namespace xmloff::token { // enhanced fields TOKEN( "fieldmark", XML_FIELDMARK ), TOKEN( "fieldmark-start", XML_FIELDMARK_START ), + TOKEN( "fieldmark-separator", XML_FIELDMARK_SEPARATOR ), TOKEN( "fieldmark-end", XML_FIELDMARK_END ), TOKEN( "image-scale", XML_IMAGE_SCALE ), diff --git a/xmloff/source/text/XMLTextMarkImportContext.cxx b/xmloff/source/text/XMLTextMarkImportContext.cxx index 8e823d6c1757..4e42899e709c 100644 --- a/xmloff/source/text/XMLTextMarkImportContext.cxx +++ b/xmloff/source/text/XMLTextMarkImportContext.cxx @@ -35,6 +35,7 @@ #include <com/sun/star/xml/sax/XAttributeList.hpp> #include <com/sun/star/text/ControlCharacter.hpp> #include <com/sun/star/text/XTextContent.hpp> +#include <com/sun/star/text/XTextRangeCompare.hpp> #include <com/sun/star/beans/XPropertySet.hpp> #include <com/sun/star/lang/XMultiServiceFactory.hpp> #include <com/sun/star/container/XNamed.hpp> @@ -105,7 +106,7 @@ namespace { enum lcl_MarkType { TypeReference, TypeReferenceStart, TypeReferenceEnd, TypeBookmark, TypeBookmarkStart, TypeBookmarkEnd, - TypeFieldmark, TypeFieldmarkStart, TypeFieldmarkEnd + TypeFieldmark, TypeFieldmarkStart, TypeFieldmarkSeparator, TypeFieldmarkEnd }; } @@ -120,6 +121,7 @@ SvXMLEnumMapEntry<lcl_MarkType> const lcl_aMarkTypeMap[] = { XML_BOOKMARK_END, TypeBookmarkEnd }, { XML_FIELDMARK, TypeFieldmark }, { XML_FIELDMARK_START, TypeFieldmarkStart }, + { XML_FIELDMARK_SEPARATOR, TypeFieldmarkSeparator }, { XML_FIELDMARK_END, TypeFieldmarkEnd }, { XML_TOKEN_INVALID, lcl_MarkType(0) }, }; @@ -173,12 +175,12 @@ void XMLTextMarkImportContext::startFastElement( sal_Int32 nElement, } static auto InsertFieldmark(SvXMLImport & rImport, - XMLTextImportHelper & rHelper, OUString const& rName) -> void + XMLTextImportHelper & rHelper, bool const isFieldmarkSeparatorMissing) -> void { assert(rHelper.hasCurrentFieldCtx()); // was set up in StartElement() // fdo#86795 check if it's actually a checkbox first - OUString const type(rHelper.getCurrentFieldType()); + auto const [ name, type ] = rHelper.getCurrentFieldType(); OUString const fieldmarkTypeName = lcl_getFieldmarkName(type); if (fieldmarkTypeName == ODF_FORMCHECKBOX || fieldmarkTypeName == ODF_FORMDROPDOWN) @@ -187,9 +189,20 @@ static auto InsertFieldmark(SvXMLImport & rImport, return; } + uno::Reference<text::XTextRange> const xStartRange(rHelper.getCurrentFieldStart()); + uno::Reference<text::XTextCursor> const xCursor( + rHelper.GetText()->createTextCursorByRange(xStartRange)); + uno::Reference<text::XTextRangeCompare> const xCompare(rHelper.GetText(), uno::UNO_QUERY); + if (xCompare->compareRegionStarts(xStartRange, rHelper.GetCursorAsRange()) < 0) + { + SAL_WARN("xmloff.text", "invalid field mark positions"); + assert(false); + } + xCursor->gotoRange(rHelper.GetCursorAsRange(), true); + Reference<XTextContent> const xContent = XMLTextMarkImportContext::CreateAndInsertMark( - rImport, "com.sun.star.text.Fieldmark", - rName, rHelper.GetCursorAsRange()); + rImport, "com.sun.star.text.Fieldmark", name, xCursor, + OUString(), isFieldmarkSeparatorMissing); if (!xContent.is()) return; @@ -256,7 +269,7 @@ void XMLTextMarkImportContext::endFastElement(sal_Int32 nElement) if (!SvXMLUnitConverter::convertEnum(nTmp, SvXMLImport::getNameFromToken(nElement), lcl_aMarkTypeMap)) return; - if (m_sBookmarkName.isEmpty() && TypeFieldmarkEnd != nTmp) + if (m_sBookmarkName.isEmpty() && TypeFieldmarkEnd != nTmp && TypeFieldmarkSeparator != nTmp) return; switch (nTmp) @@ -412,13 +425,21 @@ void XMLTextMarkImportContext::endFastElement(sal_Int32 nElement) // else: no start found -> ignore! break; } - case TypeFieldmarkStart: // no separator, so insert at start + case TypeFieldmarkStart: + { + break; + } + case TypeFieldmarkSeparator: { - InsertFieldmark(GetImport(), m_rHelper, m_sBookmarkName); + InsertFieldmark(GetImport(), m_rHelper, false); break; } case TypeFieldmarkEnd: { + if (!m_rHelper.hasCurrentFieldSeparator()) + { // backward compat for old files without separator + InsertFieldmark(GetImport(), m_rHelper, true); + } PopFieldmark(m_rHelper); break; } @@ -446,7 +467,8 @@ Reference<XTextContent> XMLTextMarkImportContext::CreateAndInsertMark( const OUString& sServiceName, const OUString& sMarkName, const Reference<XTextRange> & rRange, - const OUString& i_rXmlId) + const OUString& i_rXmlId, + bool const isFieldmarkSeparatorMissing) { // create mark const Reference<XMultiServiceFactory> xFactory(rImport.GetModel(), @@ -478,6 +500,12 @@ Reference<XTextContent> XMLTextMarkImportContext::CreateAndInsertMark( } } + if (isFieldmarkSeparatorMissing) + { + uno::Reference<beans::XPropertySet> const xProps(xIfc, uno::UNO_QUERY_THROW); + xProps->setPropertyValue("PrivateSeparatorAtStart", uno::Any(true)); + } + // cast to XTextContent and attach to document const Reference<XTextContent> xTextContent(xIfc, UNO_QUERY); if (xTextContent.is()) diff --git a/xmloff/source/text/XMLTextMarkImportContext.hxx b/xmloff/source/text/XMLTextMarkImportContext.hxx index 407cddbadf7f..da05f524083e 100644 --- a/xmloff/source/text/XMLTextMarkImportContext.hxx +++ b/xmloff/source/text/XMLTextMarkImportContext.hxx @@ -96,7 +96,8 @@ public: const OUString& sServiceName, const OUString& sMarkName, const css::uno::Reference<css::text::XTextRange> & rRange, - const OUString& i_rXmlId = OUString()); + const OUString& i_rXmlId = OUString(), + bool const isFieldmarkSeparatorMissing = false); bool FindName( const css::uno::Reference<css::xml::sax::XFastAttributeList> & xAttrList); diff --git a/xmloff/source/text/txtimp.cxx b/xmloff/source/text/txtimp.cxx index 079e8df8d82d..f26ce21e66ab 100644 --- a/xmloff/source/text/txtimp.cxx +++ b/xmloff/source/text/txtimp.cxx @@ -166,7 +166,7 @@ struct XMLTextImportHelper::Impl typedef ::std::pair< OUString, OUString> field_name_type_t; typedef ::std::pair< OUString, OUString > field_param_t; typedef ::std::vector< field_param_t > field_params_t; - typedef ::std::tuple<field_name_type_t, field_params_t, uno::Reference<text::XFormField>> field_stack_item_t; + typedef ::std::tuple<field_name_type_t, field_params_t, uno::Reference<text::XFormField>, uno::Reference<text::XTextRange>> field_stack_item_t; typedef ::std::stack< field_stack_item_t > field_stack_t; field_stack_t m_FieldStack; @@ -2105,7 +2105,7 @@ bool XMLTextImportHelper::FindAndRemoveBookmarkStartRange( void XMLTextImportHelper::pushFieldCtx( const OUString& name, const OUString& type ) { m_xImpl->m_FieldStack.push(Impl::field_stack_item_t( - Impl::field_name_type_t(name, type), Impl::field_params_t(), uno::Reference<text::XFormField>{})); + Impl::field_name_type_t(name, type), Impl::field_params_t(), uno::Reference<text::XFormField>{}, GetCursor()->getStart())); } uno::Reference<text::XFormField> @@ -2133,16 +2133,42 @@ void XMLTextImportHelper::addFieldParam( const OUString& name, const OUString& v } } -OUString XMLTextImportHelper::getCurrentFieldType() +::std::pair<OUString, OUString> XMLTextImportHelper::getCurrentFieldType() const { assert(!m_xImpl->m_FieldStack.empty()); if (!m_xImpl->m_FieldStack.empty()) { - return std::get<0>(m_xImpl->m_FieldStack.top()).second; + return std::get<0>(m_xImpl->m_FieldStack.top()); } else { - return OUString(); + return {}; + } +} + +uno::Reference<text::XTextRange> XMLTextImportHelper::getCurrentFieldStart() const +{ + assert(!m_xImpl->m_FieldStack.empty()); + if (!m_xImpl->m_FieldStack.empty()) + { + return std::get<3>(m_xImpl->m_FieldStack.top()); + } + else + { + return {}; + } +} + +bool XMLTextImportHelper::hasCurrentFieldSeparator() const +{ + assert(!m_xImpl->m_FieldStack.empty()); + if (!m_xImpl->m_FieldStack.empty()) + { + return std::get<2>(m_xImpl->m_FieldStack.top()).is(); + } + else + { + return {}; } } diff --git a/xmloff/source/text/txtparae.cxx b/xmloff/source/text/txtparae.cxx index 5d9bd3768d01..63232f8b2822 100644 --- a/xmloff/source/text/txtparae.cxx +++ b/xmloff/source/text/txtparae.cxx @@ -181,6 +181,7 @@ constexpr OUStringLiteral gsVisitedCharStyleName(u"VisitedCharStyleName"); constexpr OUStringLiteral gsWidth(u"Width"); constexpr OUStringLiteral gsWidthType( u"WidthType" ); constexpr OUStringLiteral gsTextFieldStart( u"TextFieldStart" ); +constexpr OUStringLiteral gsTextFieldSep(u"TextFieldSeparator"); constexpr OUStringLiteral gsTextFieldEnd( u"TextFieldEnd" ); constexpr OUStringLiteral gsTextFieldStartEnd( u"TextFieldStartEnd" ); @@ -2284,6 +2285,19 @@ void XMLTextParagraphExport::exportTextRangeEnumeration( } } } + else if (sType == gsTextFieldSep) + { + Reference<text::XFormField> const xFormField(xPropSet->getPropertyValue(gsBookmark), UNO_QUERY); + if (!bAutoStyles) + { + if (GetExport().getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) + { + SvXMLElementExport aElem( GetExport(), !bAutoStyles, + XML_NAMESPACE_FIELD, XML_FIELDMARK_SEPARATOR, + false, false ); + } + } + } else if (sType == gsTextFieldEnd) { if (!bAutoStyles) diff --git a/xmloff/source/text/txtparai.cxx b/xmloff/source/text/txtparai.cxx index 3641d7e1af20..5a37d1e46894 100644 --- a/xmloff/source/text/txtparai.cxx +++ b/xmloff/source/text/txtparai.cxx @@ -1433,6 +1433,7 @@ css::uno::Reference< css::xml::sax::XFastContextHandler > XMLImpSpanContext_Impl case XML_ELEMENT(FIELD, XML_FIELDMARK): case XML_ELEMENT(FIELD, XML_FIELDMARK_START): + case XML_ELEMENT(FIELD, XML_FIELDMARK_SEPARATOR): case XML_ELEMENT(FIELD, XML_FIELDMARK_END): pContext = new XMLTextMarkImportContext(rImport, *rImport.GetTextImport(), rHints.GetCrossRefHeadingBookmark()); diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index 849802878614..568122e0f582 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -3093,6 +3093,7 @@ use-zero ignore fieldmark fieldmark-start +fieldmark-separator fieldmark-end image-scale isotropic |