/* -*- 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 .
*/
AsianCompressionFlags GetCharTypeForCompression( sal_Unicode cChar )
{ switch ( cChar )
{ case 0x3008: case 0x300A: case 0x300C: case 0x300E: case 0x3010: case 0x3014: case 0x3016: case 0x3018: case 0x301A: case 0x301D: case 0xFF09: case 0xFF3D: case 0xFF5D:
{ return AsianCompressionFlags::PunctuationRight;
} case 0x3001: case 0x3002: case 0x3009: case 0x300B: case 0x300D: case 0x300F: case 0x3011: case 0x3015: case 0x3017: case 0x3019: case 0x301B: case 0x301E: case 0x301F: case 0xFF08: case 0xFF0C: case 0xFF0E: case 0xFF1A: case 0xFF1B: case 0xFF3B: case 0xFF5B:
{ return AsianCompressionFlags::PunctuationLeft;
} default:
{ return ( ( 0x3040 <= cChar ) && ( 0x3100 > cChar ) ) ? AsianCompressionFlags::Kana : AsianCompressionFlags::Normal;
}
}
}
staticvoid lcl_DrawRedLines( OutputDevice& rOutDev,
tools::Long nFontHeight, const Point& rPoint,
size_t nIndex,
size_t nMaxEnd,
KernArraySpan pDXArray,
WrongList const * pWrongs,
Degree10 nOrientation, const Point& rOrigin, bool bVertical, bool bIsRightToLeft )
{ // But only if font is not too small...
tools::Long nHeight = rOutDev.LogicToPixel(Size(0, nFontHeight)).Height(); if (WRONG_SHOW_MIN >= nHeight) return;
if (nStart < nIndex) // Corrected
nStart = nIndex;
if (nEnd > nMaxEnd)
nEnd = nMaxEnd;
Point aPoint1(rPoint); if (bVertical)
{ // VCL doesn't know that the text is vertical, and is manipulating // the positions a little bit in y direction...
tools::Long nOnePixel = rOutDev.PixelToLogic(Size(0, 1)).Height();
tools::Long nCorrect = 2 * nOnePixel;
aPoint1.AdjustY(-nCorrect);
aPoint1.AdjustX(-nCorrect);
} if (nStart > nIndex)
{ if (!bVertical)
{ // since for RTL portions rPoint is on the visual right end of the portion // (i.e. at the start of the first RTL char) we need to subtract the offset // for RTL portions...
aPoint1.AdjustX((bIsRightToLeft ? -1 : 1) * pDXArray[nStart - nIndex - 1]);
} else
aPoint1.AdjustY(pDXArray[nStart - nIndex - 1]);
}
Point aPoint2(rPoint);
assert(nEnd > nIndex && "RedLine: aPnt2?"); if (!bVertical)
{ // since for RTL portions rPoint is on the visual right end of the portion // (i.e. at the start of the first RTL char) we need to subtract the offset // for RTL portions...
aPoint2.AdjustX((bIsRightToLeft ? -1 : 1) * pDXArray[nEnd - nIndex - 1]);
} else
{
aPoint2.AdjustY(pDXArray[nEnd - nIndex - 1]);
}
if (nOrientation)
{
rOrigin.RotateAround(aPoint1, nOrientation);
rOrigin.RotateAround(aPoint2, nOrientation);
}
// #i97146# check if that view is still available // else probably the idle format timer fired while we're already // downing
EditView* pView = maIdleFormatter.GetView(); for (EditView* aEditView : maEditViews)
{ if( aEditView == pView )
{
FormatAndLayout( pView ); break;
}
}
}
void ImpEditEngine::CheckIdleFormatter()
{
maIdleFormatter.ForceTimeout(); // If not idle, but still not formatted: if ( !IsFormatted() )
FormatDoc();
}
for (sal_Int32 nParagraph = 0; nParagraph < nParaCount; nParagraph++)
{
ParaPortion& rParaPortion = GetParaPortions().getRef(nParagraph); if (rParaPortion.MustRepaint() || (rParaPortion.IsInvalid() && rParaPortion.IsVisible()))
{ // No formatting should be necessary for MustRepaint()! if (CreateLines(nParagraph, nY))
{ if (!bGrow && GetTextRanger())
{ // For a change in height all below must be reformatted... for (sal_Int32 n = nParagraph + 1; n < nParaCount; n++)
{
ParaPortion& rParaPortionToInvalidate = GetParaPortions().getRef(n);
rParaPortionToInvalidate.MarkSelectionInvalid(0);
rParaPortionToInvalidate.GetLines().Reset();
}
}
bGrow = true; if (IsCallParaInsertedOrDeleted())
{
GetEditEnginePtr()->ParagraphHeightChanged(nParagraph);
for (EditView* pView : maEditViews)
{
pView->getImpl().ScrollStateChange();
}
}
rParaPortion.SetMustRepaint(false);
}
aRepaintParagraphList.insert(nParagraph);
}
nY += rParaPortion.GetHeight(); if (!isInEmptyClusterAtTheEnd(rParaPortion, bIsScaling))
nResult = nY; // The total height excluding trailing blank paragraphs
} return nResult;
}
size_t nCurrentScaleLevel = 0; while (bOverflow && nCurrentScaleLevel < constScaleLevels.size())
{ // Clean-up and reset paragraphs
aRepaintParagraphList.clear(); for (auto& pParaPortionToInvalidate : GetParaPortions())
{
pParaPortionToInvalidate->GetLines().Reset();
pParaPortionToInvalidate->MarkSelectionInvalid(0);
pParaPortionToInvalidate->SetMustRepaint(true);
}
// Get new scaling parameters
maScalingParameters = constScaleLevels[nCurrentScaleLevel];
// Try again with different scaling factor
nHeight = FormatParagraphs(aRepaintParagraphList, true);
bOverflow = nHeight > (maMaxAutoPaperSize.Height() * mnColumns);
// Increase scale level
nCurrentScaleLevel++;
}
}
void ImpEditEngine::EnsureDocumentFormatted()
{ if (!IsFormatted())
FormatDoc();
}
void ImpEditEngine::FormatDoc()
{ if (!IsUpdateLayout() || IsFormatting()) return;
mbIsFormatting = true;
// Then I can also start the spell-timer... if (GetStatus().DoOnlineSpelling())
StartOnlineSpellTimer();
// Reserve, as it should match the current number of paragraphs
o3tl::sorted_vector<sal_Int32> aRepaintParagraphList;
aRepaintParagraphList.reserve(GetParaPortions().Count());
if (maStatus.DoStretch())
ScaleContentToFitWindow(aRepaintParagraphList); else
FormatParagraphs(aRepaintParagraphList, false);
maInvalidRect = tools::Rectangle(); // make empty
// One can also get into the formatting through UpdateMode ON=>OFF=>ON... // enable optimization first after Vobis delivery...
{
tools::Long nNewHeight = CalcTextHeight();
tools::Long nDiff = nNewHeight - mnCurTextHeight; if ( nDiff )
{
maInvalidRect.Union(tools::Rectangle::Normalize(
{ 0, nNewHeight }, { getWidthDirectionAware(maPaperSize), mnCurTextHeight }));
maStatus.GetStatusWord() |= !IsEffectivelyVertical() ? EditStatusFlags::TextHeightChanged : EditStatusFlags::TEXTWIDTHCHANGED;
}
SetValidPaperSize( maPaperSize ); // consider Min, Max
if ( maPaperSize == aPrevPaperSize ) return;
if ( ( !IsEffectivelyVertical() && ( maPaperSize.Width() != aPrevPaperSize.Width() ) )
|| ( IsEffectivelyVertical() && ( maPaperSize.Height() != aPrevPaperSize.Height() ) ) )
{ // If ahead is centered / right or tabs...
maStatus.GetStatusWord() |= !IsEffectivelyVertical() ? EditStatusFlags::TEXTWIDTHCHANGED : EditStatusFlags::TextHeightChanged; for ( sal_Int32 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
{ // Only paragraphs which are not aligned to the left need to be // reformatted, the height can not be changed here anymore.
ParaPortion& rParaPortion = GetParaPortions().getRef(nPara);
SvxAdjust eJustification = GetJustification( nPara ); if ( eJustification != SvxAdjust::Left )
{
rParaPortion.MarkSelectionInvalid(0);
CreateLines( nPara, 0 ); // 0: For AutoPageSize no TextRange!
}
}
}
if (nTxtHeight > nBoxHeight && !bOnlyOneEmptyPara)
{ // which paragraph is the first to cause higher size of the box?
ImplUpdateOverflowingParaNum( nBoxHeight); // XXX: currently only for horizontal text //maStatus.SetPageOverflow(true);
mbNeedsChainingHandling = true;
} else
{ // No overflow if within box boundaries //maStatus.SetPageOverflow(false);
mbNeedsChainingHandling = false;
}
bool ImpEditEngine::createLinesForEmptyParagraph(ParaPortion& rParaPortion)
{ // fast special treatment... if (rParaPortion.GetTextPortions().Count())
rParaPortion.GetTextPortions().Reset(); if (rParaPortion.GetLines().Count())
rParaPortion.GetLines().Reset();
// If PaperSize == long_max, one cannot take away any negative // first line indent. (Overflow) if (nMaxLineWidth < 0 && nStartX < 0)
nMaxLineWidth
= GetColumnWidth(maPaperSize) - scaleXSpacingValue(rLRItem.ResolveRight(rMetrics));
// If still less than 0, it may be just the right edge. if (nMaxLineWidth <= 0)
nMaxLineWidth = 1;
if (pTPRubyStart && nTextPos >= pNextRubyAttr->GetEnd())
{ auto pRubyInfo = std::make_unique<RubyPortionInfo>();
// Get ruby text width
// TODO: Style support is unimplemented. For now, use a hard-coded 50% scale
aRubyStartFont.SetFontSize(aRubyStartFont.GetFontSize() / 2);
aRubyStartFont.SetPhysFont(*GetRefDevice());
auto aRubyMetrics = GetRefDevice()->GetFontMetric(); auto nRubyAscent = static_cast<tools::Long>(aRubyMetrics.GetAscent());
bool ImpEditEngine::CreateLines( sal_Int32 nPara, sal_uInt32 nStartPosY )
{
assert(GetParaPortions().exists(nPara) && "Portion paragraph index is not valid");
ParaPortion& rParaPortion = GetParaPortions().getRef(nPara);
// sal_Bool: Changes in the height of paragraph Yes / No - sal_True/sal_False
assert(rParaPortion.GetNode() && "Portion without Node in CreateLines" );
DBG_ASSERT( rParaPortion.IsVisible(), "Invisible paragraphs not formatted!" );
DBG_ASSERT( rParaPortion.IsInvalid(), "CreateLines: Portion not invalid!" );
// Fast special treatment for empty paragraphs... bool bEmptyParagraph = rParaPortion.GetNode()->Len() == 0 && !GetTextRanger(); if (bEmptyParagraph) return createLinesForEmptyParagraph(rParaPortion);
sal_Int64 nCurrentPosY = nStartPosY; // If we're allowed to skip parts outside and this cannot possibly fit in the given height, // bail out to avoid possibly formatting a lot of text that will not be used. For the first // paragraph still format at least a bit. if( mbSkipOutsideFormat && nPara != 0
&& !maStatus.AutoPageHeight() && maPaperSize.Height() < nCurrentPosY )
{ returnfalse;
}
//If the paragraph SvxFrameDirection is Stacked, use STACKED const SvxFrameDirectionItem* pFrameDirItem = &GetParaAttrib(nPara, EE_PARA_WRITINGDIR); bool bStacked = pFrameDirItem->GetValue() == SvxFrameDirection::Stacked; if (bStacked)
maStatus.TurnOnFlags(EEControlBits::STACKED); else
maStatus.TurnOffFlags(EEControlBits::STACKED);
// Initialization...
if (rParaPortion.GetLines().Count() == 0)
{
rParaPortion.GetLines().Append(std::make_unique<EditLine>());
}
// Determine if quick format should be used if (!bEmptyNodeWithPolygon && !HasScriptType(nPara, i18n::ScriptType::COMPLEX))
{ if (rParaPortion.IsSimpleInvalid() &&
rParaPortion.GetInvalidDiff() > 0 &&
pNode->GetString().indexOf(CH_FEATURE, nInvalidStart) > nInvalidEnd)
{
bQuickFormat = true;
} elseif (rParaPortion.IsSimpleInvalid() && nInvalidDiff < 0)
{ // check if delete over the portion boundaries was done...
sal_Int32 nStart = nInvalidStart; // DOUBLE !!!!!!!!!!!!!!!
sal_Int32 nEnd = nStart - nInvalidDiff; // negative
bQuickFormat = true;
sal_Int32 nPos = 0; for (autoconst& pTextPortion : rParaPortion.GetTextPortions())
{ // There must be no start / end in the deleted area.
nPos = nPos + pTextPortion->GetLen(); if (nPos > nStart && nPos < nEnd)
{
bQuickFormat = false; break;
}
}
}
}
// Saving both layout mode and language (since I'm potentially changing both)
GetRefDevice()->Push( vcl::PushFlags::TEXTLAYOUTMODE|vcl::PushFlags::TEXTLANGUAGE );
ImplInitLayoutMode(*GetRefDevice(), nPara, -1);
sal_Int32 nRealInvalidStart = nInvalidStart;
if (bEmptyNodeWithPolygon)
{
TextPortion* pDummyPortion = new TextPortion( 0 );
rParaPortion.GetTextPortions().Reset();
rParaPortion.GetTextPortions().Append(pDummyPortion);
} elseif ( bQuickFormat )
{ // faster Method:
RecalcTextPortion(rParaPortion, nInvalidStart, nInvalidDiff);
} else// nRealInvalidStart can be before InvalidStart, since Portions were deleted...
{
CreateTextPortions(rParaPortion, nRealInvalidStart);
}
// Search for line with InvalidPos, start one line before // Flag the line => do not remove it !
sal_Int32 nLine = rParaPortion.GetLines().Count()-1; for ( sal_Int32 nL = 0; nL <= nLine; nL++ )
{
EditLine& rLine = rParaPortion.GetLines()[nL]; if ( rLine.GetEnd() > nRealInvalidStart ) // not nInvalidStart!
{
nLine = nL; break;
}
rLine.SetValid();
} // Begin one line before... // If it is typed at the end, the line in front cannot change. if (nLine && (!rParaPortion.IsSimpleInvalid() ||
(nInvalidEnd < pNode->Len()) ||
(nInvalidDiff <= 0)))
{
nLine--;
}
tools::Rectangle aBulletArea{Point(), Point()};
if (!nLine)
{
aBulletArea = GetEditEnginePtr()->GetBulletArea(GetParaPortions().GetPos(&rParaPortion)); if ( !aBulletArea.IsWidthEmpty() && aBulletArea.Right() > 0 )
rParaPortion.SetBulletX(sal_Int32(scaleXSpacingValue(aBulletArea.Right()))); else
rParaPortion.SetBulletX( 0 ); // if Bullet is set incorrectly
}
auto stMetrics = GetFontUnitMetrics(pNode);
tools::Long nStartX
= scaleXSpacingValue(rLRItem.ResolveTextLeft(stMetrics) + nSpaceBeforeAndMinLabelWidth); // Multiline hyperlink may need to know if the next line is bigger.
tools::Long nStartXNextLine = nStartX; if ( nIndex == 0 )
{
tools::Long nFI = scaleXSpacingValue(rLRItem.ResolveTextFirstLineOffset(stMetrics));
nStartX += nFI;
// Problem: // Since formatting starts a line _before_ the invalid position, // the positions unfortunately have to be redefined... // Solution: // The line before can only become longer, not smaller // =>...
pLine->GetCharPosArray().clear();
// tdf#162803: Stale kashida position data also needs to be cleared on each layout.
pLine->GetKashidaArray().clear();
nXWidth = 0; while ( !nXWidth )
{
tools::Long nYOff = nTextY + nTextExtraYOffset;
tools::Long nYDiff = nTextLineHeight; if ( IsEffectivelyVertical() )
{
tools::Long nMaxPolygonX = GetTextRanger()->GetBoundRect().Right();
nYOff = nMaxPolygonX-nYOff;
nYDiff = -nTextLineHeight;
}
pTextRanges = GetTextRanger()->GetTextRanges( Range( nYOff, nYOff + nYDiff ) );
assert( pTextRanges && "GetTextRanges?!" );
tools::Long nMaxRangeWidth = 0; // Use the widest range... // The widest range could be a bit confusing, so normally it // is the first one. Best with gaps.
assert(pTextRanges->size() % 2 == 0 && "textranges are always in pairs"); if (!pTextRanges->empty())
{
tools::Long nA = pTextRanges->at(0);
tools::Long nB = pTextRanges->at(1);
DBG_ASSERT( nA <= nB, "TextRange distorted?" );
tools::Long nW = nB - nA; if ( nW > nMaxRangeWidth )
{
nMaxRangeWidth = nW;
nTextXOffset = nA;
}
}
nXWidth = nMaxRangeWidth; if (nXWidth)
nMaxLineWidth
= nXWidth - nStartX - scaleXSpacingValue(rLRItem.ResolveRight(stMetrics)); else
{ // Try further down in the polygon. // Below the polygon use the Paper Width.
nTextExtraYOffset += std::max( static_cast<tools::Long>(nTextLineHeight / 10), tools::Long(1) ); if ( ( nTextY + nTextExtraYOffset ) > GetTextRanger()->GetBoundRect().Bottom() )
{
nXWidth = getWidthDirectionAware(GetPaperSize()); if ( !nXWidth ) // AutoPaperSize
nXWidth = 0x7FFFFFFF;
}
}
}
}
// search for Portion that no longer fits in line...
TextPortion* pPortion = nullptr;
sal_Int32 nPortionLen = 0; bool bContinueLastPortion = false; bool bBrokenLine = false;
bLineBreak = false; const EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature( pLine->GetStart() ); while ( ( nTmpWidth < nXWidth ) && !bEOL )
{ const sal_Int32 nTextPortions = rParaPortion.GetTextPortions().Count();
assert(nTextPortions > 0);
bContinueLastPortion = (nTmpPortion >= nTextPortions); if (bContinueLastPortion)
{ if (nTmpPos >= pNode->Len()) break; // while
// Continue with remainder. This only to have *some* valid // X-values and not endlessly create new lines until DOOM... // Happened in the scenario of tdf#104152 where inserting a // paragraph lead to a11y attempting to format the doc to // obtain content when notified.
nTmpPortion = nTextPortions - 1;
SAL_WARN("editeng","ImpEditEngine::CreateLines - continuation of a broken portion");
}
nPortionStart = nTmpPos;
pPortion = &rParaPortion.GetTextPortions()[nTmpPortion]; if ( !bContinueLastPortion && pPortion->GetKind() == PortionKind::HYPHENATOR )
{ // Throw away a Portion, if necessary correct the one before, // if the Hyph portion has swallowed a character...
sal_Int32 nTmpLen = pPortion->GetLen();
rParaPortion.GetTextPortions().Remove( nTmpPortion ); if (nTmpPortion && nTmpLen)
{
nTmpPortion--;
TextPortion& rPrev = rParaPortion.GetTextPortions()[nTmpPortion];
DBG_ASSERT( rPrev.GetKind() == PortionKind::TEXT, "Portion?!" );
nTmpWidth -= rPrev.GetSize().Width();
nTmpPos = nTmpPos - rPrev.GetLen();
rPrev.SetLen(rPrev.GetLen() + nTmpLen);
rPrev.setWidth(-1);
}
assert(nTmpPortion < rParaPortion.GetTextPortions().Count() && "No more Portions left!");
pPortion = &rParaPortion.GetTextPortions()[nTmpPortion];
}
if (bContinueLastPortion)
{ // Note that this may point behind the portion and is only to // be used with the node's string offsets to generate X-values.
nPortionLen = pNode->Len() - nPortionStart;
} else
{
nPortionLen = pPortion->GetLen();
}
DBG_ASSERT( pPortion->GetKind() != PortionKind::HYPHENATOR, "CreateLines: Hyphenator-Portion!" );
DBG_ASSERT( nPortionLen || bProcessingEmptyLine, "Empty Portion in CreateLines ?!" ); if ( pNextFeature && ( pNextFeature->GetStart() == nTmpPos ) )
{
SAL_WARN_IF( bContinueLastPortion, "editeng","ImpEditEngine::CreateLines - feature in continued portion will be wrong");
sal_uInt16 nWhich = pNextFeature->GetItem()->Which(); switch ( nWhich )
{ case EE_FEATURE_TAB:
{
tools::Long nOldTmpWidth = nTmpWidth;
// If this is the first token on the line, // and nTmpWidth > maPaperSize.Width, => infinite loop! if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
{ // What now? // make the tab fitting
pPortion->setWidth( nXWidth-nOldTmpWidth );
nTmpWidth = nXWidth-1;
bEOL = true;
bBrokenLine = true;
}
KernArray& rArray = pLine->GetCharPosArray();
size_t nPos = nTmpPos - pLine->GetStart();
rArray.insert(rArray.begin()+nPos, pPortion->GetSize().Width());
bCompressedChars = false;
} break; case EE_FEATURE_LINEBR:
{
assert( pPortion );
pPortion->setWidth(0);
bEOL = true;
bLineBreak = true;
pPortion->SetKind( PortionKind::LINEBREAK );
bCompressedChars = false;
KernArray& rArray = pLine->GetCharPosArray();
size_t nPos = nTmpPos - pLine->GetStart();
rArray.insert(rArray.begin()+nPos, pPortion->GetSize().Width());
} break; case EE_FEATURE_FIELD:
{
SeekCursor( pNode, nTmpPos+1, aTmpFont );
aTmpFont.SetPhysFont(*GetRefDevice());
ImplInitDigitMode(*GetRefDevice(), aTmpFont.GetLanguage());
OUString aFieldValue = static_cast<const EditCharAttribField*>(pNextFeature)->GetFieldValue(); // get size, but also DXArray to allow length information in line breaking below
KernArray aTmpDXArray;
pPortion->SetSize(aTmpFont.QuickGetTextSize(GetRefDevice(),
aFieldValue, 0, aFieldValue.getLength(), &aTmpDXArray));
// So no scrolling for oversized fields if (pPortion->GetSize().Width() > nXWidth - nTmpWidth)
{ // create ExtraPortionInfo on-demand, flush lineBreaksList
ExtraPortionInfo *pExtraInfo = pPortion->GetExtraInfos();
// iterate over CellBreaks using XBreakIterator to be on the // safe side with international texts/charSets
Reference < i18n::XBreakIterator > xBreakIterator(ImplGetBreakIterator()); const sal_Int32 nTextLength(aFieldValue.getLength()); const lang::Locale aLocale(GetLocale(EditPaM(pNode, nPortionStart)));
sal_Int32 nDone(0);
sal_Int32 nNextCellBreak(
xBreakIterator->nextCharacters(
aFieldValue,
0,
aLocale,
css::i18n::CharacterIteratorMode::SKIPCELL,
0,
nDone));
sal_Int32 nLastCellBreak(0);
sal_Int32 nLineStartX(0);
nLineStartX = -nTmpWidth;
// always add 1st line break (safe, we already know we are larger than nXWidth)
pExtraInfo->lineBreaksList.push_back(0);
for(sal_Int32 a(0); a < nTextLength; a++)
{ if(a == nNextCellBreak)
{ // check width if(aTmpDXArray[a] - nLineStartX > nXWidth)
{ // new CellBreak does not fit in current line, need to // create a break at LastCellBreak - but do not add 1st // line break twice for very tall frames if(0 != a)
{
pExtraInfo->lineBreaksList.push_back(a); // the following lines may be different sized if (nStartX > nStartXNextLine)
{
nXWidth += nStartX - nStartXNextLine;
pLine->SetNextLinePosXDiff(nStartX
- nStartXNextLine);
nStartXNextLine = nStartX;
}
} else
{ //even the 1. char does not fit.. //this means the field should start on next line //except if the actual line is a full line already if (nLineStartX < 0 || nStartX > nStartXNextLine)
bFieldStartNextLine = true;
}
// moveLineStart forward in X
nLineStartX = aTmpDXArray[nLastCellBreak];
}
// update CellBreak iteration values
nLastCellBreak = a;
nNextCellBreak = xBreakIterator->nextCharacters(
aFieldValue,
a,
aLocale,
css::i18n::CharacterIteratorMode::SKIPCELL,
1,
nDone);
}
} //next Line should start here... after this field end if (!bFieldStartNextLine)
nStartNextLineAfterMultiLineField
= aTmpDXArray[nTextLength - 1] - nLineStartX; elseif (pExtraInfo)
{ // if the 1. character does not fit, // but there is pExtraInfo, then delete it
pPortion->SetExtraInfos(nullptr);
pExtraInfo = nullptr;
}
}
nTmpWidth += pPortion->GetSize().Width();
KernArray& rArray = pLine->GetCharPosArray();
size_t nPos = nTmpPos - pLine->GetStart();
rArray.insert(rArray.begin()+nPos, pPortion->GetSize().Width());
pPortion->SetKind(PortionKind::FIELD); // If this is the first token on the line, // and nTmpWidth > maPaperSize.Width, => infinite loop! if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
{
nTmpWidth = nXWidth-1;
bEOL = true;
bBrokenLine = true;
} // Compression in Fields???? // I think this could be a little bit difficult and is not very useful
bCompressedChars = false;
} break; default: OSL_FAIL( "What feature?" );
}
pNextFeature = pNode->GetCharAttribs().FindFeature( pNextFeature->GetStart() + 1 );
} else
{
DBG_ASSERT( nPortionLen || bProcessingEmptyLine, "Empty Portion - Extra Space?!" );
SeekCursor( pNode, nTmpPos+1, aTmpFont );
aTmpFont.SetPhysFont(*GetRefDevice());
ImplInitDigitMode(*GetRefDevice(), aTmpFont.GetLanguage());
if (!bContinueLastPortion)
pPortion->SetRightToLeftLevel( GetRightToLeft( nPara, nTmpPos+1 ) );
// this was possibly a portion too far: bool bFixedEnd = false; if (maStatus.OneCharPerLine())
{ // State before Portion (apart from nTmpWidth):
nTmpPos -= pPortion ? nPortionLen : 0;
nPortionStart = nTmpPos;
nTmpPortion--;
bEOL = true;
bEOC = false;
// And now just one character:
nTmpPos++;
nTmpPortion++;
nPortionEnd = nTmpPortion; // one Non-Feature-Portion has to be wrapped if ( pPortion && nPortionLen > 1 )
{
DBG_ASSERT( pPortion->GetKind() == PortionKind::TEXT, "Len>1, but no TextPortion?" );
nTmpWidth -= pPortion->GetSize().Width();
sal_Int32 nP = SplitTextPortion(rParaPortion, nTmpPos, pLine);
nTmpWidth += rParaPortion.GetTextPortions()[nP].GetSize().Width();
}
} elseif ( nTmpWidth >= nXWidth )
{
nPortionEnd = nTmpPos;
nTmpPos -= pPortion ? nPortionLen : 0;
nPortionStart = nTmpPos;
nTmpPortion--;
bEOL = false;
bEOC = false; if( pPortion ) switch ( pPortion->GetKind() )
{ case PortionKind::TEXT:
{
nTmpWidth -= pPortion->GetSize().Width();
} break; case PortionKind::FIELD: case PortionKind::TAB:
{
nTmpWidth -= pPortion->GetSize().Width();
bEOL = true;
bFixedEnd = true;
} break; default:
{ // A feature is not wrapped:
DBG_ASSERT( ( pPortion->GetKind() == PortionKind::LINEBREAK ), "What Feature ?" );
bEOL = true;
bFixedEnd = true;
}
}
} else
{
bEOL = true;
bEOC = true;
pLine->SetEnd( nPortionEnd );
assert(rParaPortion.GetTextPortions().Count() && "No TextPortions?");
pLine->SetEndPortion(rParaPortion.GetTextPortions().Count() - 1);
}
// The font metrics can not be calculated continuously, if the font is // set anyway, because a large font only after wrapping suddenly ends // up in the next line => Font metrics too big.
FormatterFontMetric aFormatterMetrics;
sal_Int32 nTPos = pLine->GetStart(); for ( sal_Int32 nP = pLine->GetStartPortion(); nP <= pLine->GetEndPortion(); nP++ )
{ const TextPortion& rTP = rParaPortion.GetTextPortions()[nP]; // problem with hard font height attribute, when everything but the line break has this attribute if ( rTP.GetKind() != PortionKind::LINEBREAK )
{
SeekCursor( pNode, nTPos+1, aTmpFont );
aTmpFont.SetPhysFont(*GetRefDevice());
ImplInitDigitMode(*GetRefDevice(), aTmpFont.GetLanguage());
RecalcFormatterFontMetrics( aFormatterMetrics, aTmpFont );
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.