oox: do not guess qFormat
- if set on import - reuse - if not set - guess to be sure we show default styles when document was built from scratch - do not force non-used automatical styles to be always visible - styles like "Heading" are automatically added when we use some ODT file - on DOCX side there is no matching style and we assumed it is a custom style and should be "favourite" Change-Id: Ifc2b704b8051f93d46a783ef90ecd18e8fcf273d Reviewed-on: https://gerrit.collaboraoffice.com/c/core/+/1345 Reviewed-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com> Tested-by: Jenkins CPCI <releng@collaboraoffice.com> (cherry picked from commit 9cb18a05167ff6a079a649956cca969df4564927) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/206359 Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Tested-by: Jenkins
This commit is contained in:
+14
-4
@@ -24,9 +24,11 @@
|
||||
#include "BorderCacheOwner.hxx"
|
||||
#include "calbck.hxx"
|
||||
#include "hintids.hxx"
|
||||
#include "poolfmt.hxx"
|
||||
#include "swatrset.hxx"
|
||||
#include "names.hxx"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
class IDocumentSettingAccess;
|
||||
class IDocumentDrawModelAccess;
|
||||
@@ -64,7 +66,7 @@ class SW_DLLPUBLIC SwFormat : public sw::BorderCacheOwner, public sw::Broadcasti
|
||||
bool m_bAutoUpdateOnDirectFormat : 1;/**< TRUE: Set attributes of a whole paragraph
|
||||
at format (UI-side!). */
|
||||
bool m_bHidden : 1;
|
||||
bool m_bIsFavourite : 1; ///< Show in the basic UI
|
||||
std::optional<bool> m_bIsFavourite; ///< Show in the basic UI; unset means unknown
|
||||
std::shared_ptr<SfxGrabBagItem> m_pGrabBagItem; ///< Style InteropGrabBag.
|
||||
virtual void InvalidateInSwFntCache(sal_uInt16) {};
|
||||
virtual void InvalidateInSwFntCache() {};
|
||||
@@ -165,7 +167,15 @@ public:
|
||||
|
||||
/// Get and set Pool style IDs.
|
||||
SwPoolFormatId GetPoolFormatId() const { return m_nPoolFormatId; }
|
||||
void SetPoolFormatId( SwPoolFormatId nId ) { m_nPoolFormatId = nId; }
|
||||
void SetPoolFormatId( SwPoolFormatId nId )
|
||||
{
|
||||
m_nPoolFormatId = nId;
|
||||
if (nId == SwPoolFormatId::UNKNOWN)
|
||||
{
|
||||
// it's non-default style
|
||||
m_bIsFavourite = std::optional<bool>(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get and set Help-IDs for document templates.
|
||||
sal_uInt32 GetPoolHelpId() const { return m_nPoolHelpId; }
|
||||
@@ -182,8 +192,8 @@ public:
|
||||
bool IsAuto() const { return m_bAutoFormat; }
|
||||
void SetAuto( bool bNew ) { m_bAutoFormat = bNew; }
|
||||
|
||||
bool IsFavourite() const { return m_bIsFavourite; }
|
||||
void SetFavourite(bool bValue) { m_bIsFavourite = bValue; }
|
||||
std::optional<bool> IsFavourite() const { return m_bIsFavourite; }
|
||||
void SetFavourite(std::optional<bool> oValue) { m_bIsFavourite = oValue; }
|
||||
|
||||
bool IsHidden() const { return m_bHidden; }
|
||||
void SetHidden( bool bValue ) { m_bHidden = bValue; }
|
||||
|
||||
@@ -731,8 +731,12 @@ CPPUNIT_TEST_FIXTURE(Test, testQFormatPreservation)
|
||||
|
||||
xmlDocUniquePtr pXmlStyles = parseExport(u"word/styles.xml"_ustr);
|
||||
|
||||
assertXPath(pXmlStyles, "//w:style[@w:styleId='Heading']/w:qFormat", 1);
|
||||
// not used currently and had qFormat = 0 on import
|
||||
// "Heading" is the LO pool style auto-created as parent of Heading 1-9.
|
||||
// The source DOCX has no such style, so the export must not invent qFormat
|
||||
// for it via the lcl_guessQFormat fallback - import silence is preserved.
|
||||
assertXPath(pXmlStyles, "//w:style[@w:styleId='Heading']/w:qFormat", 0);
|
||||
// "No spacing" had w:qFormat w:val="0" in the source - that explicit
|
||||
// "no qFormat" must round-trip as no qFormat element on export.
|
||||
assertXPath(pXmlStyles, "//w:style[@w:styleId='No spacing']/w:qFormat", 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,13 +50,17 @@ SwFormat::SwFormat( SwAttrPool& rPool, const UIName& rFormatNm,
|
||||
m_bAutoUpdateOnDirectFormat = false; // LAYER_IMPL
|
||||
m_bAutoFormat = true;
|
||||
m_bFormatInDTOR = m_bHidden = false;
|
||||
m_bIsFavourite = true;
|
||||
|
||||
if( pDrvdFrame )
|
||||
{
|
||||
pDrvdFrame->Add(*this);
|
||||
m_aSet.SetParent( &pDrvdFrame->m_aSet );
|
||||
}
|
||||
|
||||
// m_bIsFavourite left as std::nullopt: unknown until the format is
|
||||
// imported from DOCX (SetGrabBagItem -> ParseFavourites). For non-DOCX
|
||||
// sources (new documents, ODT import) the optional stays unset so the
|
||||
// DOCX exporter falls back to lcl_guessQFormat for well-known styles.
|
||||
}
|
||||
|
||||
SwFormat::SwFormat( const SwFormat& rFormat ) :
|
||||
@@ -745,14 +749,14 @@ void SwFormat::ParseFavourites()
|
||||
if (aIt->second >>= nVal)
|
||||
{
|
||||
if (nVal == 0)
|
||||
SetFavourite(false);
|
||||
SetFavourite(std::optional<bool>(false));
|
||||
else
|
||||
SetFavourite(true);
|
||||
SetFavourite(std::optional<bool>(true));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetFavourite(false);
|
||||
SetFavourite(std::optional<bool>(false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7137,12 +7137,30 @@ static bool lcl_guessQFormat(const OUString& rName, sal_uInt16 nWwId)
|
||||
return aAllowlist.find(rName) != aAllowlist.end();
|
||||
}
|
||||
|
||||
/// Detect whether the source document was loaded from DOCX. The writerfilter
|
||||
/// import populates the document-level InteropGrabBag (latentStyles, default
|
||||
/// tab stops, etc.) on every DOCX it loads; new documents and ODT imports
|
||||
/// leave it empty. A non-empty bag is therefore a reliable "DOCX origin"
|
||||
/// signal we can use to suppress the lcl_guessQFormat fallback for styles
|
||||
/// that were auto-created (e.g. the Heading pool style spawned as parent of
|
||||
/// Heading 1) rather than imported from the source.
|
||||
static bool lcl_sourceWasDocx(const MSWordExportBase& rExport)
|
||||
{
|
||||
if (!rExport.m_xTextDoc.is())
|
||||
return false;
|
||||
uno::Sequence<beans::PropertyValue> aDocGrabBag;
|
||||
rExport.m_xTextDoc->getPropertyValue(u"InteropGrabBag"_ustr) >>= aDocGrabBag;
|
||||
return aDocGrabBag.hasElements();
|
||||
}
|
||||
|
||||
void DocxAttributeOutput::StartStyle( const OUString& rName, StyleType eType,
|
||||
sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nLink, sal_uInt16 nWwId, sal_uInt16 nSlot, bool bAutoUpdate )
|
||||
{
|
||||
bool bUnhideWhenUsed = false, bSemiHidden = false, bLocked = false, bDefault = false, bCustomStyle = false;
|
||||
bool bQFormat = false; // DEPRECATED: from grab-bag
|
||||
bool bRealQFormat = true; // from SwFormat
|
||||
// unset means we have no information from import: fall back to lcl_guessQFormat.
|
||||
// Set means the format was imported from a DOCX (via ParseFavourites) and the
|
||||
// import-time qFormat state must be preserved on export.
|
||||
std::optional<bool> oQFormat;
|
||||
|
||||
OUString aRsid, aUiPriority;
|
||||
rtl::Reference<FastAttributeList> pStyleAttributeList = FastSerializerHelper::createAttrList();
|
||||
@@ -7151,7 +7169,7 @@ void DocxAttributeOutput::StartStyle( const OUString& rName, StyleType eType,
|
||||
{
|
||||
const SwFormat* pFormat = m_rExport.m_pStyles->GetSwFormat(nSlot);
|
||||
pFormat->GetGrabBagItem(aAny);
|
||||
bRealQFormat = pFormat->IsFavourite();
|
||||
oQFormat = pFormat->IsFavourite();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -7165,7 +7183,14 @@ void DocxAttributeOutput::StartStyle( const OUString& rName, StyleType eType,
|
||||
if (rProp.Name == "uiPriority")
|
||||
aUiPriority = rProp.Value.get<OUString>();
|
||||
else if (rProp.Name == "qFormat")
|
||||
bQFormat = true;
|
||||
{
|
||||
// For paragraph and character styles SwFormat::IsFavourite already
|
||||
// carries the import-time decision (set by ParseFavourites). Only
|
||||
// honor the grab-bag value when we have no SwFormat-level state,
|
||||
// which today only happens for numbering styles.
|
||||
if (!oQFormat.has_value())
|
||||
oQFormat = rProp.Value.get<sal_Int32>() != 0;
|
||||
}
|
||||
else if (rProp.Name == "rsid")
|
||||
aRsid = rProp.Value.get<OUString>();
|
||||
else if (rProp.Name == "unhideWhenUsed")
|
||||
@@ -7229,8 +7254,19 @@ void DocxAttributeOutput::StartStyle( const OUString& rName, StyleType eType,
|
||||
m_pSerializer->singleElementNS(XML_w, XML_semiHidden);
|
||||
if (bUnhideWhenUsed)
|
||||
m_pSerializer->singleElementNS(XML_w, XML_unhideWhenUsed);
|
||||
// by default we use old guess, if user marks style as non-favourite -> do not export qFormat
|
||||
if (bRealQFormat && (bQFormat || lcl_guessQFormat(rName, nWwId)))
|
||||
// Honor the explicit qFormat value from import. Otherwise: for DOCX-sourced
|
||||
// documents the absence of qFormat means the style was auto-created and
|
||||
// wasn't in the source - preserve that silence. For new or ODT-imported
|
||||
// documents fall back to guessing so well-known built-in styles still get
|
||||
// qFormat in their first DOCX export.
|
||||
bool bEmitQFormat;
|
||||
if (oQFormat.has_value())
|
||||
bEmitQFormat = oQFormat.value();
|
||||
else if (lcl_sourceWasDocx(m_rExport))
|
||||
bEmitQFormat = false;
|
||||
else
|
||||
bEmitQFormat = lcl_guessQFormat(rName, nWwId);
|
||||
if (bEmitQFormat)
|
||||
m_pSerializer->singleElementNS(XML_w, XML_qFormat);
|
||||
if (bLocked)
|
||||
m_pSerializer->singleElementNS(XML_w, XML_locked);
|
||||
|
||||
@@ -3341,7 +3341,7 @@ SfxStyleSheetBase* SwStyleSheetIterator::First()
|
||||
if ( tmpMask & SfxStyleSearchBits::Favourite )
|
||||
{
|
||||
SwFormat* pFormat = rDoc.FindTextFormatCollByName( pColl->GetName() );
|
||||
if (!(pFormat && pFormat->IsFavourite())) continue;
|
||||
if (!(pFormat && pFormat->IsFavourite().value_or(false))) continue;
|
||||
}
|
||||
|
||||
// searched for used and found none
|
||||
@@ -3628,7 +3628,7 @@ void SwStyleSheetIterator::AppendStyleList(const std::vector<OUString>& rList,
|
||||
bUsed = rDoc.getIDocumentStylePoolAccess().IsPoolTextCollUsed( nId );
|
||||
SwFormat* pFormat = rDoc.FindTextFormatCollByName( i );
|
||||
bHidden = pFormat && pFormat->IsHidden( );
|
||||
bFavourite = pFormat && pFormat->IsFavourite();
|
||||
bFavourite = pFormat && pFormat->IsFavourite().value_or(false);
|
||||
}
|
||||
break;
|
||||
case SwGetPoolIdFromName::ChrFmt:
|
||||
@@ -3636,7 +3636,7 @@ void SwStyleSheetIterator::AppendStyleList(const std::vector<OUString>& rList,
|
||||
bUsed = rDoc.getIDocumentStylePoolAccess().IsPoolFormatUsed( nId );
|
||||
SwFormat* pFormat = rDoc.FindCharFormatByName( i );
|
||||
bHidden = pFormat && pFormat->IsHidden( );
|
||||
bFavourite = pFormat && pFormat->IsFavourite();
|
||||
bFavourite = pFormat && pFormat->IsFavourite().value_or(false);
|
||||
}
|
||||
break;
|
||||
case SwGetPoolIdFromName::FrmFmt:
|
||||
|
||||
Reference in New Issue
Block a user