libreoffice/xmloff/source/text/XMLFootnoteImportContext.cxx
Mike Kaganski 512ef23224 tdf#163974: ignore nested footnotes on ODF import
That is a valid, but unsupported, markup, that is explicitly mentioned
in ODF 1.4 part 3 "Schema" sect. 6.3.4 "<text:note-body>":

  Note: The schema allows for the inclusion of <text:note> elements as
  a descendant of a child of the <text:note-body> element. While this
  may be reasonable for note types, it is not reasonable for footnotes
  and endnotes. Conforming consumers need not support notes inside notes.

(The "reasonable for note types" is obviously an editorial error from the time when ODF 1.1 text "reasonable for some future note types"
was reviewed for ODF 1.2.)

As the ODF also specifies, that a conforming consumer "need not
interpret the semantics of all elements, attributes and attribute values"
(ibid., sect. 2.4 "Consumer"), it is OK to silently drop this data, just
as done for other similar cases.

Change-Id: I0a417981087ebf225565628f14409661058d100d
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176906
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Tested-by: Jenkins
2024-11-21 12:47:15 +01:00

179 lines
5.6 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include "XMLFootnoteImportContext.hxx"
#include <comphelper/diagnose_ex.hxx>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <xmloff/xmlimp.hxx>
#include <xmloff/txtimp.hxx>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/xmltoken.hxx>
#include "XMLFootnoteBodyImportContext.hxx"
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/text/XTextContent.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/text/XFootnote.hpp>
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::text;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::xml::sax;
using namespace ::xmloff::token;
XMLFootnoteImportContext::XMLFootnoteImportContext(
SvXMLImport& rImport,
XMLTextImportHelper& rHlp )
: SvXMLImportContext(rImport)
, mbListContextPushed(false)
, rHelper(rHlp)
{
}
void XMLFootnoteImportContext::startFastElement(
sal_Int32 /*nElement*/,
const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
{
// create footnote
Reference<XMultiServiceFactory> xFactory(GetImport().GetModel(),
UNO_QUERY);
if( !xFactory.is() )
return;
// create endnote or footnote
bool bIsEndnote = false;
for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
{
if( aIter.getToken() == XML_ELEMENT(TEXT, XML_NOTE_CLASS) )
{
if( IsXMLToken( aIter, XML_ENDNOTE ) )
bIsEndnote = true;
break;
}
}
Reference<XInterface> xIfc = xFactory->createInstance(
bIsEndnote ?
u"com.sun.star.text.Endnote"_ustr :
u"com.sun.star.text.Footnote"_ustr );
// attach footnote to document
Reference<XTextContent> xTextContent(xIfc, UNO_QUERY);
rHelper.InsertTextContent(xTextContent);
// process id attribute
for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
{
if( aIter.getToken() == XML_ELEMENT(TEXT, XML_ID) )
{
// get ID ...
Reference<XPropertySet> xPropertySet(xTextContent, UNO_QUERY);
Any aAny =xPropertySet->getPropertyValue(u"ReferenceId"_ustr);
sal_Int16 nID = 0;
aAny >>= nID;
// ... and insert into map
rHelper.InsertFootnoteID( aIter.toString(), nID);
break;
}
}
// save old cursor and install new one
xOldCursor = rHelper.GetCursor();
Reference<XText> xText(xTextContent, UNO_QUERY);
try
{
// May fail e.g. for a nested footnote, which is formally a valid ODF, but is not supported
rHelper.SetCursor(xText->createTextCursor());
}
catch (css::uno::RuntimeException&)
{
TOOLS_WARN_EXCEPTION("xmloff.text", "skipping the footnote: caught");
mbIsValid = false;
}
// remember old list item and block (#89891#) and reset them
// for the footnote
rHelper.PushListContext();
mbListContextPushed = true;
// remember footnote (for CreateChildContext)
xFootnote.set(xTextContent, UNO_QUERY);
// else: ignore footnote! Content will be merged into document.
}
void XMLFootnoteImportContext::endFastElement(sal_Int32 )
{
// get rid of last dummy paragraph
rHelper.DeleteParagraph();
// reinstall old cursor
rHelper.SetCursor(xOldCursor);
// reinstall old list item
if (mbListContextPushed) {
rHelper.PopListContext();
}
}
css::uno::Reference< css::xml::sax::XFastContextHandler > XMLFootnoteImportContext::createFastChildContext(
sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
{
if (!mbIsValid)
return {};
SvXMLImportContextRef xContext;
switch(nElement)
{
case XML_ELEMENT(TEXT, XML_NOTE_CITATION):
{
// little hack: we only care for one attribute of the citation
// element. We handle that here, and then return a
// default context.
for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
{
if ( aIter.getToken() == XML_ELEMENT(TEXT, XML_LABEL) )
xFootnote->setLabel(aIter.toString());
}
// ignore content: return default context
break;
}
case XML_ELEMENT(TEXT, XML_NOTE_BODY):
// return footnote body
xContext = new XMLFootnoteBodyImportContext(GetImport());
break;
default:
XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
}
return xContext;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */