diff options
author | Michael Stahl <Michael.Stahl@cib.de> | 2019-10-18 16:21:00 +0200 |
---|---|---|
committer | Michael Stahl <michael.stahl@cib.de> | 2019-10-23 13:07:52 +0200 |
commit | 6eb1c2304d257d16858b7b51cad63f1dc2bde88b (patch) | |
tree | 64041f71c3d1ee7fd91885ab5175c30799a71594 | |
parent | 26599f049df7c52eca001ba52a0684888201e1ba (diff) |
xmloff: ODF import: reimplement fieldmark-start/fieldmark-end
This needs to work in a similar way to writerfilter, because it has the
same problem: fieldmark has 3 positions but attach() only recieves 2
positions.
Insert the fieldmark at the fieldmark-start and move the cursor before
the CH_TXT_ATR_FIELDEND character, and when the fieldmark-end arrives,
move the cursor forward again.
This will slightly change the import of invalid documents, e.g.
when a fieldmark-end is missing, but that seems not so important and
without a name attribute on the fieldmark-end we can't do any better.
Change-Id: I3f0bd738277f56a999e79e4def911101b64b07e7
Reviewed-on: https://gerrit.libreoffice.org/81082
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@cib.de>
-rw-r--r-- | include/xmloff/txtimp.hxx | 2 | ||||
-rw-r--r-- | xmloff/source/text/XMLTextMarkImportContext.cxx | 179 | ||||
-rw-r--r-- | xmloff/source/text/txtimp.cxx | 23 |
3 files changed, 118 insertions, 86 deletions
diff --git a/include/xmloff/txtimp.hxx b/include/xmloff/txtimp.hxx index f2db2826cf2d..db79f9c24dbd 100644 --- a/include/xmloff/txtimp.hxx +++ b/include/xmloff/txtimp.hxx @@ -566,7 +566,7 @@ public: OUString FindActiveBookmarkName(); void pushFieldCtx( const OUString& name, const OUString& type ); - void popFieldCtx(); + css::uno::Reference<css::text::XFormField> popFieldCtx(); void addFieldParam( const OUString& name, const OUString& value ); void setCurrentFieldParamsTo(css::uno::Reference< css::text::XFormField> const &xFormField); OUString getCurrentFieldType(); diff --git a/xmloff/source/text/XMLTextMarkImportContext.cxx b/xmloff/source/text/XMLTextMarkImportContext.cxx index f3346887dc09..9d13c810b240 100644 --- a/xmloff/source/text/XMLTextMarkImportContext.cxx +++ b/xmloff/source/text/XMLTextMarkImportContext.cxx @@ -158,11 +158,6 @@ void XMLTextMarkImportContext::StartElement( m_sBookmarkName.clear(); } - if (IsXMLToken(GetLocalName(), XML_FIELDMARK_END)) - { - m_sBookmarkName = m_rHelper.FindActiveBookmarkName(); - } - if (IsXMLToken(GetLocalName(), XML_FIELDMARK_START) || IsXMLToken(GetLocalName(), XML_FIELDMARK)) { if (m_sBookmarkName.isEmpty()) @@ -180,17 +175,75 @@ void XMLTextMarkImportContext::StartElement( } } +static auto InsertFieldmark(SvXMLImport & rImport, + XMLTextImportHelper & rHelper, OUString const& rName) -> void +{ + assert(rHelper.hasCurrentFieldCtx()); // was set up in StartElement() + + // fdo#86795 check if it's actually a checkbox first + OUString const type(rHelper.getCurrentFieldType()); + OUString const fieldmarkTypeName = lcl_getFieldmarkName(type); + if (fieldmarkTypeName == ODF_FORMCHECKBOX || + fieldmarkTypeName == ODF_FORMDROPDOWN) + { // sw can't handle checkbox with start+end + SAL_INFO("xmloff.text", "invalid fieldmark-start/fieldmark-end ignored"); + return; + } + + Reference<XTextContent> const xContent = XMLTextMarkImportContext::CreateAndInsertMark( + rImport, "com.sun.star.text.Fieldmark", + rName, rHelper.GetCursorAsRange()); + + if (xContent.is()) + { + // setup fieldmark... + Reference<text::XFormField> const xFormField(xContent, UNO_QUERY); + assert(xFormField.is()); + xFormField->setFieldType(fieldmarkTypeName); + rHelper.setCurrentFieldParamsTo(xFormField); + // move cursor after setFieldType as that may delete/re-insert + rHelper.GetCursor()->gotoRange(xContent->getAnchor()->getEnd(), false); + rHelper.GetCursor()->goLeft(1, false); // move before CH_TXT_ATR_FIELDEND + } +} + +static auto PopFieldmark(XMLTextImportHelper & rHelper) -> void +{ + // can't verify name because it's not written as an attribute... + uno::Reference<text::XTextContent> const xField(rHelper.popFieldCtx(), + uno::UNO_QUERY); + if (xField.is()) + { + if (rHelper.GetText() == xField->getAnchor()->getText()) + { + try + { // skip CH_TXT_ATR_FIELDEND + rHelper.GetCursor()->gotoRange(xField->getAnchor()->getEnd(), false); + } + catch (uno::Exception const&) + { + assert(false); // must succeed + } + } + else + { + SAL_INFO("xmloff.text", "fieldmark has invalid positions"); + // could either dispose it or leave it to end at the end of the document? + xField->dispose(); + } + } +} + void XMLTextMarkImportContext::EndElement() { SvXMLImportContext::EndElement(); static const char sAPI_bookmark[] = "com.sun.star.text.Bookmark"; - if (!m_sBookmarkName.isEmpty()) + lcl_MarkType nTmp{}; + if (SvXMLUnitConverter::convertEnum(nTmp, GetLocalName(), lcl_aMarkTypeMap)) { - lcl_MarkType nTmp{}; - if (SvXMLUnitConverter::convertEnum(nTmp, GetLocalName(), - lcl_aMarkTypeMap)) + if (!m_sBookmarkName.isEmpty() || TypeFieldmarkEnd == nTmp) { switch (nTmp) { @@ -249,7 +302,6 @@ void XMLTextMarkImportContext::EndElement() } break; - case TypeFieldmarkStart: case TypeBookmarkStart: // save XTextRange for later construction of bookmark { @@ -270,23 +322,20 @@ void XMLTextMarkImportContext::EndElement() break; case TypeBookmarkEnd: + { + // tdf#94804: detect duplicate heading cross reference bookmarks + if (m_sBookmarkName.startsWith("__RefHeading__")) { - // tdf#94804: detect duplicate heading cross reference bookmarks - if (m_sBookmarkName.startsWith("__RefHeading__")) + if (m_rxCrossRefHeadingBookmark.is()) { - if (m_rxCrossRefHeadingBookmark.is()) - { - uno::Reference<container::XNamed> const xNamed( - m_rxCrossRefHeadingBookmark, uno::UNO_QUERY); - m_rHelper.AddCrossRefHeadingMapping( - m_sBookmarkName, xNamed->getName()); - break; // don't insert - } + uno::Reference<container::XNamed> const xNamed( + m_rxCrossRefHeadingBookmark, uno::UNO_QUERY); + m_rHelper.AddCrossRefHeadingMapping( + m_sBookmarkName, xNamed->getName()); + break; // don't insert } } - [[fallthrough]]; - case TypeFieldmarkEnd: - { + // get old range, and construct Reference<XTextRange> xStartRange; std::shared_ptr< ::xmloff::ParsedRDFaAttributes > @@ -306,7 +355,7 @@ void XMLTextMarkImportContext::EndElement() m_rHelper.GetText()->createTextCursorByRange( xEndRange); try { - xInsertionCursor->gotoRange(xStartRange, true); + xInsertionCursor->gotoRange(xStartRange, true); } catch (uno::Exception&) { OSL_ENSURE(false, "cannot go to end position of bookmark"); @@ -318,64 +367,27 @@ void XMLTextMarkImportContext::EndElement() // create a file with subsequence // start/end elements - bool bImportAsField = (nTmp==TypeFieldmarkEnd && m_rHelper.hasCurrentFieldCtx()); - - // fdo#86795 check if it's actually a checkbox first - bool isInvalid(false); - OUString fieldmarkTypeName; - if (bImportAsField && m_rHelper.hasCurrentFieldCtx()) - { - - OUString const type(m_rHelper.getCurrentFieldType()); - fieldmarkTypeName = lcl_getFieldmarkName(type); - if (fieldmarkTypeName == ODF_FORMCHECKBOX || - fieldmarkTypeName == ODF_FORMDROPDOWN) - { // sw can't handle checkbox with start+end - SAL_INFO("xmloff.text", "invalid fieldmark-start/fieldmark-end ignored"); - isInvalid = true; - } - } - Reference<XInterface> xContent; - if (!isInvalid) + // insert reference + xContent = CreateAndInsertMark(GetImport(), + sAPI_bookmark, + m_sBookmarkName, + xInsertionCursor, + m_sXmlId); + if (xRDFaAttributes) { - // insert reference - xContent = CreateAndInsertMark(GetImport(), - (bImportAsField - ? OUString("com.sun.star.text.Fieldmark") - : sAPI_bookmark), - m_sBookmarkName, - xInsertionCursor, - m_sXmlId); - if (xRDFaAttributes) - { - const Reference<rdf::XMetadatable> - xMeta(xContent, UNO_QUERY); - GetImport().GetRDFaImportHelper().AddRDFa( - xMeta, xRDFaAttributes); - } - const Reference<XPropertySet> xPropertySet(xContent, UNO_QUERY); - if (xPropertySet.is()) - { - xPropertySet->setPropertyValue("BookmarkHidden", uno::Any(m_rHelper.getBookmarkHidden(m_sBookmarkName))); - xPropertySet->setPropertyValue("BookmarkCondition", uno::Any(m_rHelper.getBookmarkCondition(m_sBookmarkName))); - } + const Reference<rdf::XMetadatable> + xMeta(xContent, UNO_QUERY); + GetImport().GetRDFaImportHelper().AddRDFa( + xMeta, xRDFaAttributes); } - - if (nTmp==TypeFieldmarkEnd) { - if (xContent.is() && bImportAsField) { - // setup fieldmark... - Reference< css::text::XFormField> xFormField(xContent, UNO_QUERY); - if (xFormField.is() && m_rHelper.hasCurrentFieldCtx()) { - - xFormField->setFieldType(fieldmarkTypeName); - m_rHelper.setCurrentFieldParamsTo(xFormField); - } - } - m_rHelper.popFieldCtx(); + const Reference<XPropertySet> xPropertySet(xContent, UNO_QUERY); + if (xPropertySet.is()) + { + xPropertySet->setPropertyValue("BookmarkHidden", uno::Any(m_rHelper.getBookmarkHidden(m_sBookmarkName))); + xPropertySet->setPropertyValue("BookmarkCondition", uno::Any(m_rHelper.getBookmarkCondition(m_sBookmarkName))); } - if (TypeBookmarkEnd == nTmp - && m_sBookmarkName.startsWith("__RefHeading__")) + if (m_sBookmarkName.startsWith("__RefHeading__")) { assert(xContent.is()); m_rxCrossRefHeadingBookmark = xContent; @@ -386,7 +398,16 @@ void XMLTextMarkImportContext::EndElement() // else: no start found -> ignore! break; } - + case TypeFieldmarkStart: // no separator, so insert at start + { + InsertFieldmark(GetImport(), m_rHelper, m_sBookmarkName); + break; + } + case TypeFieldmarkEnd: + { + PopFieldmark(m_rHelper); + break; + } case TypeReferenceStart: case TypeReferenceEnd: OSL_FAIL("reference start/end are handled in txtparai !"); diff --git a/xmloff/source/text/txtimp.cxx b/xmloff/source/text/txtimp.cxx index 7dc6152ae6d8..ab1500fc76e5 100644 --- a/xmloff/source/text/txtimp.cxx +++ b/xmloff/source/text/txtimp.cxx @@ -585,7 +585,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::pair< field_name_type_t, field_params_t > field_stack_item_t; + typedef ::std::tuple<field_name_type_t, field_params_t, uno::Reference<text::XFormField>> field_stack_item_t; typedef ::std::stack< field_stack_item_t > field_stack_t; field_stack_t m_FieldStack; @@ -2632,13 +2632,23 @@ OUString XMLTextImportHelper::FindActiveBookmarkName() 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())); + Impl::field_name_type_t(name, type), Impl::field_params_t(), uno::Reference<text::XFormField>{})); } -void XMLTextImportHelper::popFieldCtx() +uno::Reference<text::XFormField> +XMLTextImportHelper::popFieldCtx() { + uno::Reference<text::XFormField> xRet; if ( !m_xImpl->m_FieldStack.empty() ) + { + xRet = std::get<2>(m_xImpl->m_FieldStack.top()); m_xImpl->m_FieldStack.pop(); + } + else + { + SAL_INFO("xmloff.text", "unexpected fieldmark end"); + } + return xRet; } void XMLTextImportHelper::addFieldParam( const OUString& name, const OUString& value ) @@ -2646,7 +2656,7 @@ void XMLTextImportHelper::addFieldParam( const OUString& name, const OUString& v assert(!m_xImpl->m_FieldStack.empty()); if (!m_xImpl->m_FieldStack.empty()) { Impl::field_stack_item_t & FieldStackItem(m_xImpl->m_FieldStack.top()); - FieldStackItem.second.emplace_back( name, value ); + std::get<1>(FieldStackItem).emplace_back( name, value ); } } @@ -2655,7 +2665,7 @@ OUString XMLTextImportHelper::getCurrentFieldType() assert(!m_xImpl->m_FieldStack.empty()); if (!m_xImpl->m_FieldStack.empty()) { - return m_xImpl->m_FieldStack.top().first.second; + return std::get<0>(m_xImpl->m_FieldStack.top()).second; } else { @@ -2673,8 +2683,9 @@ void XMLTextImportHelper::setCurrentFieldParamsTo(css::uno::Reference< css::text assert(!m_xImpl->m_FieldStack.empty()); if (!m_xImpl->m_FieldStack.empty() && xFormField.is()) { - FieldParamImporter(&m_xImpl->m_FieldStack.top().second, + FieldParamImporter(&std::get<1>(m_xImpl->m_FieldStack.top()), xFormField->getParameters()).Import(); + std::get<2>(m_xImpl->m_FieldStack.top()) = xFormField; } } |