summaryrefslogtreecommitdiff
path: root/xmloff/source/text
diff options
context:
space:
mode:
authorMichael Stahl <Michael.Stahl@cib.de>2019-10-18 16:21:00 +0200
committerMichael Stahl <michael.stahl@cib.de>2019-10-23 13:07:52 +0200
commit6eb1c2304d257d16858b7b51cad63f1dc2bde88b (patch)
tree64041f71c3d1ee7fd91885ab5175c30799a71594 /xmloff/source/text
parent26599f049df7c52eca001ba52a0684888201e1ba (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>
Diffstat (limited to 'xmloff/source/text')
-rw-r--r--xmloff/source/text/XMLTextMarkImportContext.cxx179
-rw-r--r--xmloff/source/text/txtimp.cxx23
2 files changed, 117 insertions, 85 deletions
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;
}
}