From 47f0e83989c4c03d9690229b6433a5541032a3eb Mon Sep 17 00:00:00 2001 From: Mike Kaganski Date: Thu, 22 Jun 2017 18:10:14 +0300 Subject: tdf#89139: pivotCache: output sharedItems children only for string fields ... to avoid "corrupted" warning from Excel. In case of string fields, Excel expects the item list to be present, and containsXXX attributes of sharedItems to be absent, otherwise it shows a warning about file corruption. For numeric fields, it doesn't expect item list, othervise it also warns about file corruption. Change-Id: I5ded5b836587bed3177eb0a6b6c418e459e6be8b Reviewed-on: https://gerrit.libreoffice.org/39114 Tested-by: Jenkins Reviewed-by: Mike Kaganski --- sc/source/filter/excel/xepivotxml.cxx | 102 ++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 37 deletions(-) diff --git a/sc/source/filter/excel/xepivotxml.cxx b/sc/source/filter/excel/xepivotxml.cxx index bf820838e0ee..a8ce9f886588 100644 --- a/sc/source/filter/excel/xepivotxml.cxx +++ b/sc/source/filter/excel/xepivotxml.cxx @@ -235,52 +235,80 @@ void XclExpXmlPivotCaches::SavePivotCacheXml( XclExpXmlStream& rStrm, const Entr ScDPCache::ScDPItemDataVec::const_iterator it = rFieldItems.begin(), itEnd = rFieldItems.end(); std::set aDPTypes; + double fMin = std::numeric_limits::infinity(), fMax = -std::numeric_limits::infinity(); for (; it != itEnd; ++it) { - aDPTypes.insert(it->GetType()); + ScDPItemData::Type eType = it->GetType(); + aDPTypes.insert(eType); + if (eType == ScDPItemData::Value) + { + double fVal = it->GetValue(); + fMin = std::min(fMin, fVal); + fMax = std::max(fMax, fVal); + } } auto aDPTypeEnd = aDPTypes.cend(); - pDefStrm->startElement(XML_sharedItems, - XML_count, OString::number(static_cast(rFieldItems.size())).getStr(), - XML_containsMixedTypes, XclXmlUtils::ToPsz10(aDPTypes.size() > 1), - XML_containsSemiMixedTypes, XclXmlUtils::ToPsz10(aDPTypes.size() > 1), - XML_containsString, XclXmlUtils::ToPsz10(aDPTypes.find(ScDPItemData::String) != aDPTypeEnd), - XML_containsNumber, XclXmlUtils::ToPsz10(aDPTypes.find(ScDPItemData::Value) != aDPTypeEnd), - FSEND); + auto pAttList = sax_fastparser::FastSerializerHelper::createAttrList(); + // tdf#89139: Only create item list for string-only fields. + // Using containsXXX attributes in this case makes Excel think the file is corrupted. + // OTOH listing items for e.g. number fields also triggers "corrupted" warning in Excel. + bool bListItems = aDPTypes.size() == 1 && aDPTypes.find(ScDPItemData::String) != aDPTypeEnd; + if (bListItems) + { + pAttList->add(XML_count, OString::number(static_cast(rFieldItems.size()))); + } + else + { + pAttList->add(XML_containsMixedTypes, XclXmlUtils::ToPsz10(aDPTypes.size() > 1)); + pAttList->add(XML_containsSemiMixedTypes, XclXmlUtils::ToPsz10(aDPTypes.size() > 1)); + pAttList->add(XML_containsString, XclXmlUtils::ToPsz10(aDPTypes.find(ScDPItemData::String) != aDPTypeEnd)); + if (aDPTypes.find(ScDPItemData::Value) != aDPTypeEnd) + { + pAttList->add(XML_containsNumber, XclXmlUtils::ToPsz10(true)); + pAttList->add(XML_minValue, OString::number(fMin)); + pAttList->add(XML_maxValue, OString::number(fMax)); + } + } + sax_fastparser::XFastAttributeListRef xAttributeList(pAttList); - it = rFieldItems.begin(); - for (; it != itEnd; ++it) + pDefStrm->startElement(XML_sharedItems, xAttributeList); + + if (bListItems) { - const ScDPItemData& rItem = *it; - switch (rItem.GetType()) + it = rFieldItems.begin(); + for (; it != itEnd; ++it) { - case ScDPItemData::String: - pDefStrm->singleElement(XML_s, - XML_v, XclXmlUtils::ToOString(rItem.GetString()).getStr(), - FSEND); - break; - case ScDPItemData::Value: - pDefStrm->singleElement(XML_n, - XML_v, OString::number(rItem.GetValue()).getStr(), - FSEND); - break; - case ScDPItemData::Empty: - pDefStrm->singleElement(XML_m, FSEND); - break; - case ScDPItemData::Error: - pDefStrm->singleElement(XML_e, - XML_v, XclXmlUtils::ToOString(rItem.GetString()).getStr(), - FSEND); - break; - case ScDPItemData::GroupValue: - case ScDPItemData::RangeStart: - // TODO : What do we do with these types? - pDefStrm->singleElement(XML_m, FSEND); - break; - default: - ; + const ScDPItemData& rItem = *it; + switch (rItem.GetType()) + { + case ScDPItemData::String: + pDefStrm->singleElement(XML_s, + XML_v, XclXmlUtils::ToOString(rItem.GetString()), + FSEND); + break; + case ScDPItemData::Value: + pDefStrm->singleElement(XML_n, + XML_v, OString::number(rItem.GetValue()), + FSEND); + break; + case ScDPItemData::Empty: + pDefStrm->singleElement(XML_m, FSEND); + break; + case ScDPItemData::Error: + pDefStrm->singleElement(XML_e, + XML_v, XclXmlUtils::ToOString(rItem.GetString()), + FSEND); + break; + case ScDPItemData::GroupValue: + case ScDPItemData::RangeStart: + // TODO : What do we do with these types? + pDefStrm->singleElement(XML_m, FSEND); + break; + default: + ; + } } } -- cgit