/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include constexpr sal_Int32 DEF_WRITER_LSPACE = 500; //Standard Indentation constexpr sal_Int32 DEF_DRAW_LSPACE = 800; //Standard Indentation constexpr sal_uInt16 NUMITEM_VERSION_03 = 0x03; constexpr sal_uInt16 NUMITEM_VERSION_04 = 0x04; using namespace ::com::sun::star; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::text; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::style; sal_Int32 SvxNumberType::nRefCount = 0; css::uno::Reference SvxNumberType::xFormatter; static void lcl_getFormatter(css::uno::Reference& _xFormatter) { if(_xFormatter.is()) return; try { Reference xContext( ::comphelper::getProcessComponentContext() ); Reference xRet = text::DefaultNumberingProvider::create(xContext); _xFormatter.set(xRet, UNO_QUERY); } catch(const Exception&) { SAL_WARN("editeng", "service missing: \"com.sun.star.text.DefaultNumberingProvider\""); } } SvxNumberType::SvxNumberType(SvxNumType nType) : nNumType(nType), bShowSymbol(true) { nRefCount++; } SvxNumberType::SvxNumberType(const SvxNumberType& rType) : nNumType(rType.nNumType), bShowSymbol(rType.bShowSymbol) { nRefCount++; } SvxNumberType::~SvxNumberType() { if(!--nRefCount) xFormatter = nullptr; } OUString SvxNumberType::GetNumStr( sal_Int32 nNo ) const { LanguageTag aLang = comphelper::IsFuzzing() ? LanguageTag(u"en-US"_ustr) : Application::GetSettings().GetLanguageTag(); return GetNumStr( nNo, aLang.getLocale() ); } static bool isArabicNumberingType(SvxNumType t) { return t == SVX_NUM_ARABIC || t == SVX_NUM_ARABIC_ZERO || t == SVX_NUM_ARABIC_ZERO3 || t == SVX_NUM_ARABIC_ZERO4 || t == SVX_NUM_ARABIC_ZERO5; } OUString SvxNumberType::GetNumStr( sal_Int32 nNo, const css::lang::Locale& rLocale, bool bIsLegal ) const { lcl_getFormatter(xFormatter); if(!xFormatter.is()) return OUString(); if(bShowSymbol) { switch(nNumType) { case NumberingType::CHAR_SPECIAL: case NumberingType::BITMAP: break; default: { // '0' allowed for ARABIC numberings if(NumberingType::ARABIC == nNumType && 0 == nNo ) return OUString('0'); else { SvxNumType nActType = !bIsLegal || isArabicNumberingType(nNumType) ? nNumType : SVX_NUM_ARABIC; static constexpr OUString sNumberingType = u"NumberingType"_ustr; static constexpr OUString sValue = u"Value"_ustr; Sequence< PropertyValue > aProperties { comphelper::makePropertyValue(sNumberingType, static_cast(nActType)), comphelper::makePropertyValue(sValue, nNo) }; try { return xFormatter->makeNumberingString( aProperties, rLocale ); } catch(const Exception&) { } } } } } return OUString(); } void SvxNumberType::dumpAsXml( xmlTextWriterPtr pWriter ) const { (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxNumberType")); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("NumType"), BAD_CAST(OString::number(nNumType).getStr())); (void)xmlTextWriterEndElement(pWriter); } SvxNumberFormat::SvxNumberFormat( SvxNumType eType ) : SvxNumberType(eType), eNumAdjust(SvxAdjust::Left), nInclUpperLevels(1), nStart(1), cBullet(SVX_DEF_BULLET), nBulletRelSize(100), nBulletColor(COL_BLACK), mePositionAndSpaceMode( LABEL_WIDTH_AND_POSITION ), nFirstLineOffset(0), nAbsLSpace(0), nCharTextDistance(0), meLabelFollowedBy( LISTTAB ), mnListtabPos( 0 ), mnFirstLineIndent( 0 ), mnIndentAt( 0 ), eVertOrient(text::VertOrientation::NONE) { } SvxNumberFormat::SvxNumberFormat(const SvxNumberFormat& rFormat) : SvxNumberType(rFormat), mePositionAndSpaceMode( rFormat.mePositionAndSpaceMode ) { *this = rFormat; } SvxNumberFormat::SvxNumberFormat( SvStream &rStream ) : nStart(0) , nBulletRelSize(100) , nFirstLineOffset(0) , nAbsLSpace(0) , nCharTextDistance(0) { sal_uInt16 nTmp16(0); sal_Int32 nTmp32(0); rStream.ReadUInt16( nTmp16 ); // Version number rStream.ReadUInt16( nTmp16 ); SetNumberingType( static_cast(nTmp16) ); rStream.ReadUInt16( nTmp16 ); eNumAdjust = static_cast(nTmp16); rStream.ReadUInt16( nTmp16 ); nInclUpperLevels = nTmp16; rStream.ReadUInt16( nStart ); rStream.ReadUInt16( nTmp16 ); cBullet = static_cast(nTmp16); sal_Int16 temp = 0; rStream.ReadInt16( temp ); nFirstLineOffset = temp; temp = 0; rStream.ReadInt16( temp ); nAbsLSpace = temp; rStream.SeekRel(2); //skip old now unused nLSpace; rStream.ReadInt16( nCharTextDistance ); sPrefix = rStream.ReadUniOrByteString( rStream.GetStreamCharSet() ); sSuffix = rStream.ReadUniOrByteString( rStream.GetStreamCharSet() ); sCharStyleName = rStream.ReadUniOrByteString( rStream.GetStreamCharSet() ); sal_uInt16 hasGraphicBrush = 0; rStream.ReadUInt16( hasGraphicBrush ); if ( hasGraphicBrush ) { pGraphicBrush.reset(new SvxBrushItem(SID_ATTR_BRUSH)); legacy::SvxBrush::Create(*pGraphicBrush, rStream, BRUSH_GRAPHIC_VERSION); } else pGraphicBrush = nullptr; rStream.ReadUInt16( nTmp16 ); eVertOrient = nTmp16; sal_uInt16 hasBulletFont = 0; rStream.ReadUInt16( hasBulletFont ); if ( hasBulletFont ) { pBulletFont.emplace(); ReadFont( rStream, *pBulletFont ); } else pBulletFont.reset(); tools::GenericTypeSerializer aSerializer(rStream); aSerializer.readSize(aGraphicSize); aSerializer.readColor(nBulletColor); rStream.ReadUInt16( nBulletRelSize ); rStream.ReadUInt16( nTmp16 ); SetShowSymbol( nTmp16 != 0 ); rStream.ReadUInt16( nTmp16 ); mePositionAndSpaceMode = static_cast(nTmp16); rStream.ReadUInt16( nTmp16 ); meLabelFollowedBy = static_cast(nTmp16); rStream.ReadInt32( nTmp32 ); mnListtabPos = nTmp32; rStream.ReadInt32( nTmp32 ); mnFirstLineIndent = nTmp32; rStream.ReadInt32( nTmp32 ); mnIndentAt = nTmp32; } SvxNumberFormat::~SvxNumberFormat() { } void SvxNumberFormat::Store(SvStream &rStream, FontToSubsFontConverter pConverter) { if(pConverter && pBulletFont) { cBullet = ConvertFontToSubsFontChar(pConverter, cBullet); OUString sFontName = GetFontToSubsFontName(pConverter); pBulletFont->SetFamilyName(sFontName); } tools::GenericTypeSerializer aSerializer(rStream); rStream.WriteUInt16( NUMITEM_VERSION_04 ); rStream.WriteUInt16( GetNumberingType() ); rStream.WriteUInt16( static_cast(eNumAdjust) ); rStream.WriteUInt16( nInclUpperLevels ); rStream.WriteUInt16( nStart ); rStream.WriteUInt16( cBullet ); rStream.WriteInt16( sal_Int16(std::clamp(nFirstLineOffset, SAL_MIN_INT16, SAL_MAX_INT16)) ); //TODO: better way to handle out-of-bounds value? rStream.WriteInt16( sal_Int16(std::clamp(nAbsLSpace, SAL_MIN_INT16, SAL_MAX_INT16)) ); //TODO: better way to handle out-of-bounds value? rStream.WriteInt16( 0 ); // write a dummy for old now unused nLSpace rStream.WriteInt16( nCharTextDistance ); rtl_TextEncoding eEnc = osl_getThreadTextEncoding(); rStream.WriteUniOrByteString(sPrefix, eEnc); rStream.WriteUniOrByteString(sSuffix, eEnc); rStream.WriteUniOrByteString(sCharStyleName, eEnc); if(pGraphicBrush) { rStream.WriteUInt16( 1 ); // in SD or SI force bullet itself to be stored, // for that purpose throw away link when link and graphic // are present, so Brush save is forced if(!pGraphicBrush->GetGraphicLink().isEmpty() && pGraphicBrush->GetGraphic()) { pGraphicBrush->SetGraphicLink(u""_ustr); } legacy::SvxBrush::Store(*pGraphicBrush, rStream, BRUSH_GRAPHIC_VERSION); } else rStream.WriteUInt16( 0 ); rStream.WriteUInt16( eVertOrient ); if(pBulletFont) { rStream.WriteUInt16( 1 ); WriteFont( rStream, *pBulletFont ); } else rStream.WriteUInt16( 0 ); aSerializer.writeSize(aGraphicSize); Color nTempColor = nBulletColor; if(COL_AUTO == nBulletColor) nTempColor = COL_BLACK; aSerializer.writeColor(nTempColor); rStream.WriteUInt16( nBulletRelSize ); rStream.WriteUInt16( sal_uInt16(IsShowSymbol()) ); rStream.WriteUInt16( mePositionAndSpaceMode ); rStream.WriteUInt16( meLabelFollowedBy ); rStream.WriteInt32( mnListtabPos ); rStream.WriteInt32( mnFirstLineIndent ); rStream.WriteInt32( mnIndentAt ); } SvxNumberFormat& SvxNumberFormat::operator=( const SvxNumberFormat& rFormat ) { if (& rFormat == this) { return *this; } SvxNumberType::SetNumberingType(rFormat.GetNumberingType()); eNumAdjust = rFormat.eNumAdjust ; nInclUpperLevels = rFormat.nInclUpperLevels ; nStart = rFormat.nStart ; cBullet = rFormat.cBullet ; mePositionAndSpaceMode = rFormat.mePositionAndSpaceMode; nFirstLineOffset = rFormat.nFirstLineOffset; nAbsLSpace = rFormat.nAbsLSpace ; nCharTextDistance = rFormat.nCharTextDistance ; meLabelFollowedBy = rFormat.meLabelFollowedBy; mnListtabPos = rFormat.mnListtabPos; mnFirstLineIndent = rFormat.mnFirstLineIndent; mnIndentAt = rFormat.mnIndentAt; eVertOrient = rFormat.eVertOrient; sPrefix = rFormat.sPrefix; sSuffix = rFormat.sSuffix; sListFormat = rFormat.sListFormat; aGraphicSize = rFormat.aGraphicSize ; nBulletColor = rFormat.nBulletColor ; nBulletRelSize = rFormat.nBulletRelSize; SetShowSymbol(rFormat.IsShowSymbol()); sCharStyleName = rFormat.sCharStyleName; pGraphicBrush.reset(); if(rFormat.pGraphicBrush) { pGraphicBrush.reset( new SvxBrushItem(*rFormat.pGraphicBrush) ); } pBulletFont.reset(); if(rFormat.pBulletFont) pBulletFont = *rFormat.pBulletFont; mbIsLegal = rFormat.mbIsLegal; return *this; } bool SvxNumberFormat::operator==( const SvxNumberFormat& rFormat) const { if( GetNumberingType() != rFormat.GetNumberingType() || eNumAdjust != rFormat.eNumAdjust || nInclUpperLevels != rFormat.nInclUpperLevels || nStart != rFormat.nStart || cBullet != rFormat.cBullet || mePositionAndSpaceMode != rFormat.mePositionAndSpaceMode || nFirstLineOffset != rFormat.nFirstLineOffset || nAbsLSpace != rFormat.nAbsLSpace || nCharTextDistance != rFormat.nCharTextDistance || meLabelFollowedBy != rFormat.meLabelFollowedBy || mnListtabPos != rFormat.mnListtabPos || mnFirstLineIndent != rFormat.mnFirstLineIndent || mnIndentAt != rFormat.mnIndentAt || eVertOrient != rFormat.eVertOrient || sPrefix != rFormat.sPrefix || sSuffix != rFormat.sSuffix || sListFormat != rFormat.sListFormat || aGraphicSize != rFormat.aGraphicSize || nBulletColor != rFormat.nBulletColor || nBulletRelSize != rFormat.nBulletRelSize || IsShowSymbol() != rFormat.IsShowSymbol() || sCharStyleName != rFormat.sCharStyleName || mbIsLegal != rFormat.mbIsLegal ) return false; if ( (pGraphicBrush && !rFormat.pGraphicBrush) || (!pGraphicBrush && rFormat.pGraphicBrush) || (pGraphicBrush && *pGraphicBrush != *rFormat.pGraphicBrush) ) { return false; } if ( (pBulletFont && !rFormat.pBulletFont) || (!pBulletFont && rFormat.pBulletFont) || (pBulletFont && *pBulletFont != *rFormat.pBulletFont) ) { return false; } return true; } void SvxNumberFormat::SetGraphicBrush( const SvxBrushItem* pBrushItem, const Size* pSize, const sal_Int16* pOrient) { if (!pBrushItem) pGraphicBrush.reset(); else if ( !pGraphicBrush || (*pBrushItem != *pGraphicBrush) ) pGraphicBrush.reset(pBrushItem->Clone()); if(pOrient) eVertOrient = *pOrient; else eVertOrient = text::VertOrientation::NONE; if(pSize) aGraphicSize = *pSize; else { aGraphicSize.setWidth(0); aGraphicSize.setHeight(0); } } void SvxNumberFormat::SetGraphic( const OUString& rName ) { if( pGraphicBrush && pGraphicBrush->GetGraphicLink() == rName ) return ; pGraphicBrush.reset( new SvxBrushItem( rName, u""_ustr, GPOS_AREA, 0 ) ); if( eVertOrient == text::VertOrientation::NONE ) eVertOrient = text::VertOrientation::TOP; aGraphicSize.setWidth(0); aGraphicSize.setHeight(0); } sal_Int16 SvxNumberFormat::GetVertOrient() const { return eVertOrient; } void SvxNumberFormat::SetBulletFont(const vcl::Font* pFont) { if (pFont) pBulletFont = *pFont; else pBulletFont.reset(); } void SvxNumberFormat::SetPositionAndSpaceMode( SvxNumPositionAndSpaceMode ePositionAndSpaceMode ) { mePositionAndSpaceMode = ePositionAndSpaceMode; } sal_Int32 SvxNumberFormat::GetAbsLSpace() const { return mePositionAndSpaceMode == LABEL_WIDTH_AND_POSITION ? nAbsLSpace : static_cast( GetFirstLineIndent() + GetIndentAt() ); } sal_Int32 SvxNumberFormat::GetFirstLineOffset() const { return mePositionAndSpaceMode == LABEL_WIDTH_AND_POSITION ? nFirstLineOffset : static_cast( GetFirstLineIndent() ); } short SvxNumberFormat::GetCharTextDistance() const { return mePositionAndSpaceMode == LABEL_WIDTH_AND_POSITION ? nCharTextDistance : 0; } void SvxNumberFormat::SetLabelFollowedBy( const LabelFollowedBy eLabelFollowedBy ) { meLabelFollowedBy = eLabelFollowedBy; } OUString SvxNumberFormat::GetLabelFollowedByAsString() const { switch (meLabelFollowedBy) { case LISTTAB: return u"\t"_ustr; case SPACE: return u" "_ustr; case NEWLINE: return u"\n"_ustr; case NOTHING: // intentionally left blank. return OUString(); default: SAL_WARN("editeng", "Unknown SvxNumberFormat::GetLabelFollowedBy() return value"); assert(false); } return OUString(); } void SvxNumberFormat::SetListtabPos( const tools::Long nListtabPos ) { mnListtabPos = nListtabPos; } void SvxNumberFormat::SetFirstLineIndent( const tools::Long nFirstLineIndent ) { mnFirstLineIndent = nFirstLineIndent; } void SvxNumberFormat::SetIndentAt( const tools::Long nIndentAt ) { mnIndentAt = nIndentAt; } Size SvxNumberFormat::GetGraphicSizeMM100(const Graphic* pGraphic) { const MapMode aMapMM100( MapUnit::Map100thMM ); const Size& rSize = pGraphic->GetPrefSize(); Size aRetSize; if ( pGraphic->GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel ) { OutputDevice* pOutDev = Application::GetDefaultDevice(); MapMode aOldMap( pOutDev->GetMapMode() ); pOutDev->SetMapMode( aMapMM100 ); aRetSize = pOutDev->PixelToLogic( rSize ); pOutDev->SetMapMode( aOldMap ); } else aRetSize = OutputDevice::LogicToLogic( rSize, pGraphic->GetPrefMapMode(), aMapMM100 ); return aRetSize; } OUString SvxNumberFormat::CreateRomanString( sal_Int32 nNo, bool bUpper ) { OUStringBuffer sRet; constexpr char romans[][13] = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; constexpr sal_Int32 values[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; for (size_t i = 0; i < std::size(romans); ++i) { while(nNo - values[i] >= 0) { sRet.appendAscii(romans[i]); nNo -= values[i]; } } return bUpper ? sRet.makeStringAndClear() : sRet.makeStringAndClear().toAsciiLowerCase(); } void SvxNumberFormat::SetPrefix(const OUString& rSet) { // ListFormat manages the prefix. If badly changed via this function, sListFormat is invalidated if (sListFormat) sListFormat.reset(); sPrefix = rSet; } void SvxNumberFormat::SetSuffix(const OUString& rSet) { // ListFormat manages the suffix. If badly changed via this function, sListFormat is invalidated if (sListFormat) sListFormat.reset(); sSuffix = rSet; } void SvxNumberFormat::SetListFormat(const OUString& rPrefix, const OUString& rSuffix, int nLevel) { sPrefix = rPrefix; sSuffix = rSuffix; // Generate list format sListFormat = std::make_optional(sPrefix); for (int i = 1; i <= nInclUpperLevels; i++) { int nLevelId = nLevel - nInclUpperLevels + i; if (nLevelId < 0) // There can be cases with current level 1, but request to show 10 upper levels. Trim it continue; *sListFormat += "%"; *sListFormat += OUString::number(nLevelId + 1); *sListFormat += "%"; if (i != nInclUpperLevels) *sListFormat += "."; // Default separator for older ODT } *sListFormat += sSuffix; } void SvxNumberFormat::SetListFormat(std::optional oSet) { sPrefix.clear(); sSuffix.clear(); sListFormat = oSet; if (!oSet.has_value()) { return; } // For backward compatibility and UI we should create something looking like // a prefix, suffix and included levels also. This is not possible in general case // since level format string is much more flexible. But for most cases is okay // If properly formatted, sListFormat should look something like "%1%…%10%" // with an optional prefix or suffix (which could theoretically include a percent symbol) const sal_Int32 nLen = sListFormat->getLength(); sal_Int32 nFirstReplacement = sListFormat->indexOf('%'); while (nFirstReplacement > -1 && nFirstReplacement < nLen - 1 && ((*sListFormat)[nFirstReplacement + 1] < '1' || (*sListFormat)[nFirstReplacement + 1] > '9')) { nFirstReplacement = sListFormat->indexOf('%', nFirstReplacement + 1); } sal_Int32 nLastReplacement = nFirstReplacement == -1 ? -1 : sListFormat->lastIndexOf('%'); while (nLastReplacement > 0 && ((*sListFormat)[nLastReplacement - 1] < '0' || (*sListFormat)[nLastReplacement - 1] > '9')) { nLastReplacement = sListFormat->lastIndexOf('%', nLastReplacement); } if (nLastReplacement < nFirstReplacement) nLastReplacement = nFirstReplacement; else ++nLastReplacement; if (nFirstReplacement > 0) // Everything before first '%' will be prefix sPrefix = sListFormat->copy(0, nFirstReplacement); if (nLastReplacement >= 0 && nLastReplacement < nLen) // Everything beyond last '%' is a suffix sSuffix = sListFormat->copy(nLastReplacement); sal_uInt8 nPercents = 0; for (sal_Int32 i = nFirstReplacement > 0 ? nFirstReplacement : 0; i < nLastReplacement; i++) { if ((*sListFormat)[i] == '%') nPercents++; } nInclUpperLevels = nPercents/2; if (nInclUpperLevels < 1) { // There should be always at least one level. This will be not required // in future (when we get rid of prefix/suffix), but nowadays there // are too many conversions "list format" <-> "prefix/suffix/inclUpperLevel" nInclUpperLevels = 1; } } OUString SvxNumberFormat::GetListFormat(bool bIncludePrefixSuffix /*= true*/) const { assert(sListFormat.has_value()); if (bIncludePrefixSuffix) return *sListFormat; // Strip prefix & suffix from string return sListFormat->copy(sPrefix.getLength(), sListFormat->getLength() - sPrefix.getLength() - sSuffix.getLength()); } OUString SvxNumberFormat::GetCharFormatName()const { return sCharStyleName; } sal_Int32 SvxNumRule::nRefCount = 0; static SvxNumberFormat* pStdNumFmt = nullptr; static SvxNumberFormat* pStdOutlineNumFmt = nullptr; SvxNumRule::SvxNumRule( SvxNumRuleFlags nFeatures, sal_uInt16 nLevels, bool bCont, SvxNumRuleType eType, SvxNumberFormat::SvxNumPositionAndSpaceMode eDefaultNumberFormatPositionAndSpaceMode ) : nLevelCount(nLevels), nFeatureFlags(nFeatures), eNumberingType(eType), bContinuousNumbering(bCont) { ++nRefCount; for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++) { if(i < nLevels) { aFmts[i].reset( new SvxNumberFormat(SVX_NUM_CHARS_UPPER_LETTER) ); // It is a distinction between writer and draw if(nFeatures & SvxNumRuleFlags::CONTINUOUS) { if ( eDefaultNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) { aFmts[i]->SetAbsLSpace(o3tl::toTwips(DEF_WRITER_LSPACE * (i+1), o3tl::Length::mm100)); aFmts[i]->SetFirstLineOffset(o3tl::toTwips(-DEF_WRITER_LSPACE, o3tl::Length::mm100)); } else if ( eDefaultNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT ) { // first line indent of general numbering in inch: -0,25 inch constexpr tools::Long cFirstLineIndent = o3tl::toTwips(-0.25, o3tl::Length::in); // indent values of general numbering in inch: // 0,5 0,75 1,0 1,25 1,5 // 1,75 2,0 2,25 2,5 2,75 constexpr tools::Long cIndentAt = o3tl::toTwips(0.25, o3tl::Length::in); aFmts[i]->SetPositionAndSpaceMode( SvxNumberFormat::LABEL_ALIGNMENT ); aFmts[i]->SetLabelFollowedBy( SvxNumberFormat::LISTTAB ); aFmts[i]->SetListtabPos( cIndentAt * (i+2) ); aFmts[i]->SetFirstLineIndent( cFirstLineIndent ); aFmts[i]->SetIndentAt( cIndentAt * (i+2) ); } } else { aFmts[i]->SetAbsLSpace( DEF_DRAW_LSPACE * i ); } } else aFmts[i] = nullptr; aFmtsSet[i] = false; } } SvxNumRule::SvxNumRule(const SvxNumRule& rCopy) { ++nRefCount; nLevelCount = rCopy.nLevelCount ; nFeatureFlags = rCopy.nFeatureFlags ; bContinuousNumbering = rCopy.bContinuousNumbering; eNumberingType = rCopy.eNumberingType; for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++) { if(rCopy.aFmts[i]) aFmts[i].reset( new SvxNumberFormat(*rCopy.aFmts[i]) ); else aFmts[i].reset(); aFmtsSet[i] = rCopy.aFmtsSet[i]; } } SvxNumRule::SvxNumRule(SvxNumRule&& rCopy) noexcept { ++nRefCount; nLevelCount = rCopy.nLevelCount ; nFeatureFlags = rCopy.nFeatureFlags ; bContinuousNumbering = rCopy.bContinuousNumbering; eNumberingType = rCopy.eNumberingType; for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++) { if(rCopy.aFmts[i]) aFmts[i] = std::move(rCopy.aFmts[i]); aFmtsSet[i] = rCopy.aFmtsSet[i]; } } SvxNumRule::SvxNumRule( SvStream &rStream ) : nLevelCount(0) { sal_uInt16 nTmp16(0); rStream.ReadUInt16( nTmp16 ); // NUM_ITEM_VERSION rStream.ReadUInt16( nLevelCount ); if (nLevelCount > SVX_MAX_NUM) { SAL_WARN("editeng", "nLevelCount: " << nLevelCount << " greater than max of: " << SVX_MAX_NUM); nLevelCount = SVX_MAX_NUM; } // first nFeatureFlags of old Versions rStream.ReadUInt16( nTmp16 ); nFeatureFlags = static_cast(nTmp16); rStream.ReadUInt16( nTmp16 ); bContinuousNumbering = nTmp16; rStream.ReadUInt16( nTmp16 ); eNumberingType = static_cast(nTmp16); for (sal_uInt16 i = 0; i < SVX_MAX_NUM; i++) { rStream.ReadUInt16( nTmp16 ); bool hasNumberingFormat = nTmp16 & 1; aFmtsSet[i] = nTmp16 & 2; // fdo#68648 reset flag if ( hasNumberingFormat ){ aFmts[i].reset( new SvxNumberFormat( rStream ) ); } else { aFmts[i].reset(); aFmtsSet[i] = false; // actually only false is valid } } //second nFeatureFlags for new versions rStream.ReadUInt16( nTmp16 ); nFeatureFlags = static_cast(nTmp16); } void SvxNumRule::Store( SvStream &rStream ) { rStream.WriteUInt16( NUMITEM_VERSION_03 ); rStream.WriteUInt16( nLevelCount ); //first save of nFeatureFlags for old versions rStream.WriteUInt16( static_cast(nFeatureFlags) ); rStream.WriteUInt16( sal_uInt16(bContinuousNumbering) ); rStream.WriteUInt16( static_cast(eNumberingType) ); FontToSubsFontConverter pConverter = nullptr; bool bConvertBulletFont = ( rStream.GetVersion() <= SOFFICE_FILEFORMAT_50 ) && ( rStream.GetVersion() ); for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++) { sal_uInt16 nSetFlag(aFmtsSet[i] ? 2 : 0); // fdo#68648 store that too if(aFmts[i]) { rStream.WriteUInt16( 1 | nSetFlag ); if(bConvertBulletFont && aFmts[i]->GetBulletFont()) { if(!pConverter) pConverter = CreateFontToSubsFontConverter(aFmts[i]->GetBulletFont()->GetFamilyName(), FontToSubsFontFlags::EXPORT); } aFmts[i]->Store(rStream, pConverter); } else rStream.WriteUInt16( 0 | nSetFlag ); } //second save of nFeatureFlags for new versions rStream.WriteUInt16( static_cast(nFeatureFlags) ); } void SvxNumRule::dumpAsXml(xmlTextWriterPtr pWriter) const { (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxNumRule")); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("levelCount"), BAD_CAST(OString::number(nLevelCount).getStr())); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("continuousNumbering"), BAD_CAST(OString::boolean(bContinuousNumbering).getStr())); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("numberingType"), BAD_CAST(OString::number(static_cast(eNumberingType)).getStr())); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("featureFlags"), BAD_CAST(OString::number(static_cast(nFeatureFlags)).getStr())); for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++) { if(aFmts[i]) { (void)xmlTextWriterStartElement(pWriter, BAD_CAST("aFmts")); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("i"), BAD_CAST(OString::number(i).getStr())); (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", aFmts[i].get()); (void)xmlTextWriterEndElement(pWriter); } } (void)xmlTextWriterEndElement(pWriter); } SvxNumRule::~SvxNumRule() { if(!--nRefCount) { delete pStdNumFmt; pStdNumFmt = nullptr; delete pStdOutlineNumFmt; pStdOutlineNumFmt = nullptr; } } SvxNumRule& SvxNumRule::operator=( const SvxNumRule& rCopy ) { if (this != &rCopy) { nLevelCount = rCopy.nLevelCount; nFeatureFlags = rCopy.nFeatureFlags; bContinuousNumbering = rCopy.bContinuousNumbering; eNumberingType = rCopy.eNumberingType; for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++) { if(rCopy.aFmts[i]) aFmts[i].reset( new SvxNumberFormat(*rCopy.aFmts[i]) ); else aFmts[i].reset(); aFmtsSet[i] = rCopy.aFmtsSet[i]; } } return *this; } SvxNumRule& SvxNumRule::operator=( SvxNumRule&& rCopy ) noexcept { if (this != &rCopy) { nLevelCount = rCopy.nLevelCount; nFeatureFlags = rCopy.nFeatureFlags; bContinuousNumbering = rCopy.bContinuousNumbering; eNumberingType = rCopy.eNumberingType; for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++) { if(rCopy.aFmts[i]) aFmts[i] = std::move(rCopy.aFmts[i]); aFmtsSet[i] = rCopy.aFmtsSet[i]; } } return *this; } bool SvxNumRule::operator==( const SvxNumRule& rCopy) const { if(nLevelCount != rCopy.nLevelCount || nFeatureFlags != rCopy.nFeatureFlags || bContinuousNumbering != rCopy.bContinuousNumbering || eNumberingType != rCopy.eNumberingType) return false; for(sal_uInt16 i = 0; i < nLevelCount; i++) { if ( (aFmtsSet[i] != rCopy.aFmtsSet[i]) || (!aFmts[i] && rCopy.aFmts[i]) || (aFmts[i] && !rCopy.aFmts[i]) || (aFmts[i] && *aFmts[i] != *rCopy.aFmts[i]) ) { return false; } } return true; } const SvxNumberFormat* SvxNumRule::Get(sal_uInt16 nLevel)const { DBG_ASSERT(nLevel < SVX_MAX_NUM, "Wrong Level" ); if( nLevel < SVX_MAX_NUM ) return aFmtsSet[nLevel] ? aFmts[nLevel].get() : nullptr; else return nullptr; } const SvxNumberFormat& SvxNumRule::GetLevel(sal_uInt16 nLevel)const { if(!pStdNumFmt) { pStdNumFmt = new SvxNumberFormat(SVX_NUM_ARABIC); pStdOutlineNumFmt = new SvxNumberFormat(SVX_NUM_NUMBER_NONE); } DBG_ASSERT(nLevel < SVX_MAX_NUM, "Wrong Level" ); return ( ( nLevel < SVX_MAX_NUM ) && aFmts[nLevel] ) ? *aFmts[nLevel] : eNumberingType == SvxNumRuleType::NUMBERING ? *pStdNumFmt : *pStdOutlineNumFmt; } void SvxNumRule::SetLevel( sal_uInt16 i, const SvxNumberFormat& rNumFmt, bool bIsValid ) { DBG_ASSERT(i < SVX_MAX_NUM, "Wrong Level" ); if( i >= SVX_MAX_NUM ) return; bool bReplace = !aFmtsSet[i]; if (!bReplace) { const SvxNumberFormat *pFmt = Get(i); bReplace = pFmt == nullptr || rNumFmt != *pFmt; } if (bReplace) { aFmts[i].reset( new SvxNumberFormat(rNumFmt) ); aFmtsSet[i] = bIsValid; } } void SvxNumRule::SetLevel(sal_uInt16 nLevel, const SvxNumberFormat* pFmt) { DBG_ASSERT(nLevel < SVX_MAX_NUM, "Wrong Level" ); if( nLevel < SVX_MAX_NUM ) { aFmtsSet[nLevel] = nullptr != pFmt; if(pFmt) SetLevel(nLevel, *pFmt); else { aFmts[nLevel].reset(); } } } OUString SvxNumRule::MakeNumString( const SvxNodeNum& rNum ) const { OUStringBuffer aStr; if( SVX_NO_NUM > rNum.GetLevel() && !( SVX_NO_NUMLEVEL & rNum.GetLevel() ) ) { const SvxNumberFormat& rMyNFmt = GetLevel( rNum.GetLevel() ); aStr.append(rMyNFmt.GetPrefix()); if( SVX_NUM_NUMBER_NONE != rMyNFmt.GetNumberingType() ) { sal_uInt8 i = rNum.GetLevel(); if( !IsContinuousNumbering() && 1 < rMyNFmt.GetIncludeUpperLevels() ) // only on own level? { sal_uInt8 n = rMyNFmt.GetIncludeUpperLevels(); if( 1 < n ) { if( i+1 >= n ) i -= n - 1; else i = 0; } } for( ; i <= rNum.GetLevel(); ++i ) { const SvxNumberFormat& rNFmt = GetLevel( i ); if( SVX_NUM_NUMBER_NONE == rNFmt.GetNumberingType() ) { continue; } bool bDot = true; if( rNum.GetLevelVal()[ i ] ) { if(SVX_NUM_BITMAP != rNFmt.GetNumberingType()) { const LanguageTag& rLang = Application::GetSettings().GetLanguageTag(); aStr.append(rNFmt.GetNumStr( rNum.GetLevelVal()[ i ], rLang.getLocale(), rMyNFmt.GetIsLegal() )); } else bDot = false; } else aStr.append("0"); // all 0-levels are a 0 if( i != rNum.GetLevel() && bDot) aStr.append("."); } } aStr.append(rMyNFmt.GetSuffix()); } return aStr.makeStringAndClear(); } // changes linked to embedded bitmaps void SvxNumRule::UnLinkGraphics() { for(sal_uInt16 i = 0; i < GetLevelCount(); i++) { SvxNumberFormat aFmt(GetLevel(i)); const SvxBrushItem* pBrush = aFmt.GetBrush(); if(SVX_NUM_BITMAP == aFmt.GetNumberingType()) { if(pBrush && !pBrush->GetGraphicLink().isEmpty()) { const Graphic* pGraphic = pBrush->GetGraphic(); if (pGraphic) { SvxBrushItem aTempItem(*pBrush); aTempItem.SetGraphicLink(u""_ustr); aTempItem.SetGraphic(*pGraphic); sal_Int16 eOrient = aFmt.GetVertOrient(); aFmt.SetGraphicBrush( &aTempItem, &aFmt.GetGraphicSize(), &eOrient ); } } } else if((SVX_NUM_BITMAP|LINK_TOKEN) == static_cast(aFmt.GetNumberingType())) aFmt.SetNumberingType(SVX_NUM_BITMAP); SetLevel(i, aFmt); } } SvxNumBulletItem::SvxNumBulletItem(SvxNumRule const & rRule) : SfxPoolItem(SID_ATTR_NUMBERING_RULE, SfxItemType::SvxNumBulletItemType), maNumRule(rRule) { } SvxNumBulletItem::SvxNumBulletItem(SvxNumRule && rRule) : SfxPoolItem(SID_ATTR_NUMBERING_RULE, SfxItemType::SvxNumBulletItemType), maNumRule(std::move(rRule)) { } SvxNumBulletItem::SvxNumBulletItem(SvxNumRule const & rRule, sal_uInt16 _nWhich ) : SfxPoolItem(_nWhich, SfxItemType::SvxNumBulletItemType), maNumRule(rRule) { } SvxNumBulletItem::SvxNumBulletItem(SvxNumRule && rRule, sal_uInt16 _nWhich ) : SfxPoolItem(_nWhich, SfxItemType::SvxNumBulletItemType), maNumRule(std::move(rRule)) { } SvxNumBulletItem::SvxNumBulletItem(const SvxNumBulletItem& rCopy) : SfxPoolItem(rCopy), maNumRule(rCopy.maNumRule) { } SvxNumBulletItem::~SvxNumBulletItem() { } bool SvxNumBulletItem::operator==( const SfxPoolItem& rCopy) const { return SfxPoolItem::operator==(rCopy) && maNumRule == static_cast(rCopy).maNumRule; } SvxNumBulletItem* SvxNumBulletItem::Clone( SfxItemPool * ) const { return new SvxNumBulletItem(*this); } bool SvxNumBulletItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const { rVal <<= SvxCreateNumRule( maNumRule ); return true; } bool SvxNumBulletItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) { uno::Reference< container::XIndexReplace > xRule; if( rVal >>= xRule ) { try { SvxNumRule aNewRule( SvxGetNumRule( xRule ) ); if( aNewRule.GetLevelCount() != maNumRule.GetLevelCount() || aNewRule.GetNumRuleType() != maNumRule.GetNumRuleType() ) { aNewRule = SvxConvertNumRule( aNewRule, maNumRule.GetLevelCount(), maNumRule.GetNumRuleType() ); } maNumRule = std::move( aNewRule ); return true; } catch(const lang::IllegalArgumentException&) { } } return false; } void SvxNumBulletItem::dumpAsXml(xmlTextWriterPtr pWriter) const { (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxNumBulletItem")); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); maNumRule.dumpAsXml(pWriter); (void)xmlTextWriterEndElement(pWriter); } SvxNumRule SvxConvertNumRule( const SvxNumRule& rRule, sal_uInt16 nLevels, SvxNumRuleType eType ) { const sal_uInt16 nSrcLevels = rRule.GetLevelCount(); SvxNumRule aNewRule(rRule.GetFeatureFlags(), nLevels, rRule.IsContinuousNumbering(), eType ); for( sal_uInt16 nLevel = 0; (nLevel < nLevels) && (nLevel < nSrcLevels); nLevel++ ) aNewRule.SetLevel( nLevel, rRule.GetLevel( nLevel ) ); return aNewRule; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */