/* -*- 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 .
*/
//Class declaration; here because they are only used in this file enumclass SubColFlags {
Page = 0x01, //Helplines of the page
Tab = 0x08, //Helplines inside tables
Fly = 0x10, //Helplines inside fly frames
Sect = 0x20, //Helplines inside sections
};
class SwLineRects
{ public:
std::vector<SwLineRect> m_aLineRects; typedef std::vector< SwLineRect >::const_iterator const_iterator; typedef std::vector< SwLineRect >::iterator iterator; typedef std::vector< SwLineRect >::reverse_iterator reverse_iterator; typedef std::vector< SwLineRect >::size_type size_type;
size_t m_nLastCount; //avoid unnecessary cycles in PaintLines
SwLineRects()
: m_nLastCount(0)
{ #ifdef IOS // Work around what is either a compiler bug in Xcode 5.1.1, // or some unknown problem in this file. If I ifdef out this // call, I get a crash in SwSubsRects::PaintSubsidiary: the // address of the rLi reference variable is claimed to be // 0x4000000!
dummy_function(); #endif
} void AddLineRect( const SwRect& rRect, const Color *pColor, const SvxBorderLineStyle nStyle, const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const &properties ); void ConnectEdges( OutputDevice const *pOut, SwPaintProperties const &properties ); void PaintLines ( OutputDevice *pOut, SwPaintProperties const &properties ); void LockLines( bool bLock );
/** * Container for static properties
*/ struct SwPaintProperties { // Only repaint the Fly content as well as the background of the Fly content if // a metafile is taken of the Fly. bool bSFlyMetafile;
VclPtr<OutputDevice> pSFlyMetafileOut;
SwViewShell *pSGlobalShell;
// Retouch for transparent Flys is done by the background of the Flys. // The Fly itself should certainly not be spared out. See PaintSwFrameBackground and // lcl_SubtractFlys()
SwFlyFrame *pSRetoucheFly;
SwFlyFrame *pSRetoucheFly2;
SwFlyFrame *pSFlyOnlyDraw;
// The borders will be collected in pSLines during the Paint and later // possibly merge them. // The help lines will be collected and merged in gProp.pSSubsLines. These will // be compared with pSLines before the work in order to avoid help lines // to hide borders.
std::unique_ptr<BorderLines> pBLines;
std::unique_ptr<SwLineRects> pSLines;
std::unique_ptr<SwSubsRects> pSSubsLines;
// global variable for sub-lines of body, header, footer, section and footnote frames.
std::unique_ptr<SwSubsRects> pSSpecSubsLines;
SfxProgress *pSProgress;
// Sizes of a pixel and the corresponding halves. Will be reset when // entering SwRootFrame::PaintSwFrame
tools::Long nSPixelSzW;
tools::Long nSPixelSzH;
tools::Long nSHalfPixelSzW;
tools::Long nSHalfPixelSzH;
tools::Long nSMinDistPixelW;
tools::Long nSMinDistPixelH;
Color aSGlobalRetoucheColor;
// Current zoom factor double aSScaleX; double aSScaleY;
/** * To be able to save the statics so the paint is more or less reentrant
*/ class SwSavePaintStatics : public SwPaintProperties
{ public:
SwSavePaintStatics();
~SwSavePaintStatics();
};
void SwSubsRects::RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const & properties )
{ // All help lines that are covered by any border will be removed or split for (size_t i = 0; i < m_aLineRects.size(); ++i)
{ // get a copy instead of a reference, because an <insert> may destroy // the object due to a necessary array resize. const SwLineRect aSubsLineRect(m_aLineRects[i]);
// add condition <aSubsLineRect.IsLocked()> in order to consider only // border lines, which are *not* locked. if ( aSubsLineRect.IsPainted() ||
aSubsLineRect.IsLocked() ) continue;
void SwLineRects::PaintLines( OutputDevice *pOut, SwPaintProperties const &properties )
{ // Paint the borders. Sadly two passes are needed. // Once for the inside and once for the outside edges of tables if (m_aLineRects.size() == m_nLastCount) return;
// #i16816# tagged pdf support
SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
// #i16816# tagged pdf support
SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
// Remove all help line that are almost covered (tables) for (sal_Int32 i = 0; i != static_cast<sal_Int32>(m_aLineRects.size()); ++i)
{
SwLineRect& rLi = m_aLineRects[i]; constbool bVerticalSubs = rLi.Height() > rLi.Width();
for (size_type k = i + 1; k != m_aLineRects.size(); ++k)
{
SwLineRect& rLk = m_aLineRects[k]; if ( rLi.SSize() == rLk.SSize() )
{ if ( bVerticalSubs == ( rLk.Height() > rLk.Width() ) )
{ if ( bVerticalSubs )
{
tools::Long nLi = rLi.Right();
tools::Long nLk = rLk.Right(); if ( rLi.Top() == rLk.Top() &&
((nLi < rLk.Left() && nLi+21 > rLk.Left()) ||
(nLk < rLi.Left() && nLk+21 > rLi.Left())))
{
m_aLineRects.erase(m_aLineRects.begin() + i); // don't continue with inner loop any more: // the array may shrink!
--i; break;
}
} else
{
tools::Long nLi = rLi.Bottom();
tools::Long nLk = rLk.Bottom(); if ( rLi.Left() == rLk.Left() &&
((nLi < rLk.Top() && nLi+21 > rLk.Top()) ||
(nLk < rLi.Top() && nLk+21 > rLi.Top())))
{
m_aLineRects.erase(m_aLineRects.begin() + i); // don't continue with inner loop any more: // the array may shrink!
--i; break;
}
}
}
}
}
}
if (pRects && (!pRects->m_aLineRects.empty()))
RemoveSuperfluousSubsidiaryLines( *pRects, properties );
// Reset draw mode in high contrast mode in order to get fill color // set at output device. Recover draw mode after draw of lines. // Necessary for the subsidiary lines painted by the fly frames.
DrawModeFlags nOldDrawMode = pOut->GetDrawMode(); if( gProp.pSGlobalShell->GetWin() &&
Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
{
pOut->SetDrawMode( DrawModeFlags::Default );
}
for (SwLineRect& rLRect : m_aLineRects)
{ // Add condition <!rLRect.IsLocked()> to prevent paint of locked subsidiary lines. if ( !rLRect.IsPainted() &&
!rLRect.IsLocked() )
{ const Color *pCol = nullptr;
SwViewShell *pShell = properties.pSGlobalShell; const SwViewOption *pOpt = pShell->GetViewOptions(); switch ( rLRect.GetSubColor() )
{ case SubColFlags::Page: pCol = &pOpt->GetDocBoundariesColor(); break; case SubColFlags::Tab: pCol = &pOpt->GetTableBoundariesColor(); break; case SubColFlags::Fly: case SubColFlags::Sect: pCol = &pOpt->GetSectionBoundColor(); break;
}
/** * Function <SwAlignRect(..)> is also used outside this file * * Correction: adjust rectangle on pixel level in order to make sure, * that the border "leaves its original pixel", if it has to * No prior adjustments for odd relation between pixel and twip
*/ void SwAlignRect( SwRect &rRect, const SwViewShell *pSh, const vcl::RenderContext* pRenderContext )
{ if( !rRect.HasArea() ) return;
// Make sure that view shell (parameter <pSh>) exists, if the output device // is taken from this view shell --> no output device, no alignment // Output device taken from view shell <pSh>, if <gProp.bSFlyMetafile> not set if ( !gProp.bSFlyMetafile && !pSh )
{ return;
}
// Hold original rectangle in pixel const tools::Rectangle aOrgPxRect = pOut->LogicToPixel( rRect.SVRect() ); // Determine pixel-center rectangle in twip const SwRect aPxCenterRect( pOut->PixelToLogic( aOrgPxRect ) );
// Perform adjustments on pixel level.
SwRect aAlignedPxRect( aOrgPxRect ); if ( rRect.Top() > aPxCenterRect.Top() )
{ // 'leave pixel overlapping on top'
aAlignedPxRect.AddTop( 1 );
}
if ( rRect.Bottom() < aPxCenterRect.Bottom() )
{ // 'leave pixel overlapping on bottom'
aAlignedPxRect.AddBottom( - 1 );
}
if ( rRect.Left() > aPxCenterRect.Left() )
{ // 'leave pixel overlapping on left'
aAlignedPxRect.AddLeft( 1 );
}
if ( rRect.Right() < aPxCenterRect.Right() )
{ // 'leave pixel overlapping on right'
aAlignedPxRect.AddRight( - 1 );
}
// Consider negative width/height check, if aligned SwRect has negative width/height. // If Yes, adjust it to width/height = 0 twip. // NOTE: A SwRect with negative width/height can occur, if the width/height // of the given SwRect in twip was less than a pixel in twip and that // the alignment calculates that the aligned SwRect should not contain // the pixels the width/height is on. if ( aAlignedPxRect.Width() < 0 )
{
aAlignedPxRect.Width(0);
} if ( aAlignedPxRect.Height() < 0 )
{
aAlignedPxRect.Height(0);
} // Consider zero width/height for converting a rectangle from // pixel to logic it needs a width/height. Thus, set width/height // to one, if it's zero and correct this on the twip level after the conversion. bool bZeroWidth = false; if ( aAlignedPxRect.Width() == 0 )
{
aAlignedPxRect.Width(1);
bZeroWidth = true;
} bool bZeroHeight = false; if ( aAlignedPxRect.Height() == 0 )
{
aAlignedPxRect.Height(1);
bZeroHeight = true;
}
// Consider zero width/height and adjust calculated aligned twip rectangle. // Reset width/height to zero; previous negative width/height haven't to be considered. if ( bZeroWidth )
{
rRect.Width(0);
} if ( bZeroHeight )
{
rRect.Height(0);
}
}
/** * Method to pixel-align rectangle for drawing graphic object * * Because we are drawing graphics from the left-top-corner in conjunction * with size coordinates, these coordinates have to be calculated at a pixel * level. * Thus, we convert the rectangle to pixel and then convert to left-top-corner * and then get size of pixel rectangle back to logic. * This calculation is necessary, because there's a different between * the conversion from logic to pixel of a normal rectangle with its left-top- * and right-bottom-corner and the same conversion of the same rectangle * with left-top-corner and size. * * NOTE: Call this method before each <GraphicObject.Draw(...)>
*/ void SwAlignGrfRect( SwRect *pGrfRect, const vcl::RenderContext &rOut )
{
tools::Rectangle aPxRect = rOut.LogicToPixel( pGrfRect->SVRect() );
pGrfRect->Pos( rOut.PixelToLogic( aPxRect.TopLeft() ) );
pGrfRect->SSize( rOut.PixelToLogic( aPxRect.GetSize() ) );
}
/** * Calculate PrtArea plus surrounding plus shadow
*/ staticvoid lcl_CalcBorderRect( SwRect &rRect, const SwFrame *pFrame, const SwBorderAttrs &rAttrs, constbool bShadow,
SwPaintProperties const & properties)
{ // Special handling for cell frames. // The printing area of a cell frame is completely enclosed in the frame area // and a cell frame has no shadow. Thus, for cell frames the calculated // area equals the frame area. // Notes: Borders of cell frames in R2L text direction will switch its side // - left border is painted on the right; right border on the left. // See <lcl_PaintLeftLine> and <lcl_PaintRightLine>. if( pFrame->IsSctFrame() )
{
rRect = pFrame->getFramePrintArea();
rRect.Pos() += pFrame->getFrameArea().Pos();
} elseif ( pFrame->IsCellFrame() )
rRect = pFrame->getFrameArea(); else
{
rRect = pFrame->getFramePrintArea();
rRect.Pos() += pFrame->getFrameArea().Pos();
//For character bound Flys only examine those Flys in which it is not //anchored itself. //Why only for character bound ones you may ask? It never makes sense to //subtract frames in which it is anchored itself right? if (pSelfFly && pSelfFly->IsLowerOf(pFly)) continue;
//Any why does it not apply for the RetoucheFly too? if (gProp.pSRetoucheFly && gProp.pSRetoucheFly->IsLowerOf(pFly)) continue;
#if OSL_DEBUG_LEVEL > 0 //Flys who are anchored inside their own one, must have a bigger OrdNum //or be character bound. if (pSelfFly && bLowerOfSelf)
{
OSL_ENSURE( pFly->IsFlyInContentFrame() ||
pSdrObj->GetOrdNumDirect() > pSelfFly->GetVirtDrawObj()->GetOrdNumDirect(), "Fly with wrong z-Order" );
} #endif
bool bStopOnHell = true; if (pSelfFly)
{ const SdrObject *pTmp = pSelfFly->GetVirtDrawObj(); if (pSdrObj->GetLayer() == pTmp->GetLayer())
{ if (pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect()) //In the same layer we only observe those that are above. continue;
} else
{ if (!bLowerOfSelf && !pFly->GetFormat()->GetOpaque().GetValue()) //From other layers we are only interested in non //transparent ones or those that are internal continue;
bStopOnHell = false;
}
} if (gProp.pSRetoucheFly)
{ const SdrObject *pTmp = gProp.pSRetoucheFly->GetVirtDrawObj(); if ( pSdrObj->GetLayer() == pTmp->GetLayer() )
{ if ( pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect() ) //In the same layer we only observe those that are above. continue;
} else
{ if (!pFly->IsLowerOf( gProp.pSRetoucheFly ) && !pFly->GetFormat()->GetOpaque().GetValue()) //From other layers we are only interested in non //transparent ones or those that are internal continue;
bStopOnHell = false;
}
}
//If the content of the Fly is transparent, we subtract it only if it's //contained in the hell layer. const IDocumentDrawModelAccess& rIDDMA = pFly->GetFormat()->getIDocumentDrawModelAccess(); bool bHell = pSdrObj->GetLayer() == rIDDMA.GetHellId(); if (bStopOnHell && bHell) continue;
/// Change internal order of condition /// first check "!bHell", then "..->Lower()" and "..->IsNoTextFrame()" /// have not to be performed, if frame is in "Hell" const SwFrame* pLower = pFly->Lower(); if (!bHell && pLower && pLower->IsNoTextFrame() &&
(static_cast<SwNoTextFrame const*>(pLower)->IsTransparent() || static_cast<SwNoTextFrame const*>(pLower)->HasAnimation() ||
pFly->GetFormat()->GetSurround().IsContour()
)
) continue;
// Own if-statements for transparent background/shadow of fly frames // in order to handle special conditions. if (pFly->IsBackgroundTransparent())
{ // Background <pFly> is transparent drawn. Thus normally, its region // have not to be subtracted from given region. // But, if method is called for a fly frame and // <pFly> is a direct lower of this fly frame and // <pFly> inherites its transparent background brush from its parent, // then <pFly> frame area have to be subtracted from given region. // NOTE: Because in Status Quo transparent backgrounds can only be // assigned to fly frames, the handle of this special case // avoids drawing of transparent areas more than once, if // a fly frame inherites a transparent background from its // parent fly frame. if (pFrame->IsFlyFrame() &&
(pFly->GetAnchorFrame()->FindFlyFrame() == pFrame) &&
pFly->GetFormat()->IsBackgroundBrushInherited()
)
{
SwRect aRect;
SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) ); const SwBorderAttrs &rAttrs = *aAccess.Get();
::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
rRegion -= aRect;
rClipState.subtractRange(lcl_ShrinkFly(aRect)); continue;
} else
{ continue;
}
}
if (bHell && pFly->GetAnchorFrame()->IsInFly())
{ //So the border won't get dismantled by the background of the other //Fly.
SwRect aRect;
SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) ); const SwBorderAttrs &rAttrs = *aAccess.Get();
::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
rRegion -= aRect;
rClipState.subtractRange(lcl_ShrinkFly(aRect));
} else
{
SwRect aRect( pFly->getFramePrintArea() );
aRect += pFly->getFrameArea().Pos();
rRegion -= aRect;
rClipState.subtractRange(lcl_ShrinkFly(aRect));
}
} if (gProp.pSRetoucheFly == gProp.pSRetoucheFly2)
gProp.pSRetoucheFly = nullptr;
}
staticvoid lcl_implDrawGraphicBackground(const SvxBrushItem& _rBackgrdBrush,
vcl::RenderContext& _rOut, const SwRect& _rAlignedPaintRect, const GraphicObject& _rGraphicObj,
SwPaintProperties const & properties)
{ /// determine color of background /// If color of background brush is not "no fill"/"auto fill" or /// <SwPaintProperties.bSFlyMetafile> is set, use color of background brush, otherwise /// use global retouche color. const Color aColor( ( (_rBackgrdBrush.GetColor() != COL_TRANSPARENT) || properties.bSFlyMetafile )
? _rBackgrdBrush.GetColor()
: aGlobalRetoucheColor );
/// determine, if background color have to be drawn transparent /// and calculate transparency percent value
sal_Int8 nTransparencyPercent = 0; bool bDrawTransparent = false; if ( aColor.IsTransparent() ) /// background color is transparent --> draw transparent.
{
bDrawTransparent = true;
nTransparencyPercent = ((255 - aColor.GetAlpha())*100 + 0x7F)/0xFF;
} elseif ( (_rGraphicObj.GetAttr().IsTransparent()) &&
(_rBackgrdBrush.GetColor() == COL_TRANSPARENT) ) /// graphic is drawn transparent and background color is /// "no fill"/"auto fill" --> draw transparent
{
bDrawTransparent = true;
nTransparencyPercent = 100 - (_rGraphicObj.GetAttr().GetAlpha() * 100 + 127) / 255;
}
/** * This is a local help method to draw a background for a graphic * * Under certain circumstances we have to draw a background for a graphic. * This method takes care of the conditions and draws the background with the * corresponding color. * Method introduced for bug fix #103876# in order to optimize drawing tiled * background graphics. Previously, this code was integrated in method * <lcl_DrawGraphic>. * Method implemented as an inline, checking the conditions and calling method * method <lcl_implDrawGraphicBackground(..)> for the intrinsic drawing. * * @param _rBackgrdBrush * background brush contain the color the background has to be drawn. * * @param _rOut * output device the background has to be drawn in. * * @param _rAlignedPaintRect * paint rectangle in the output device, which has to be drawn with the background. * rectangle have to be aligned by method ::SwAlignRect * * @param _rGraphicObj * graphic object, for which the background has to be drawn. Used for checking * the transparency of its bitmap, its type and if the graphic is drawn transparent * * @param _bNumberingGraphic * boolean indicating that graphic is used as a numbering. * * @param _bBackgrdAlreadyDrawn * boolean (optional; default: false) indicating, if the background is already drawn.
*/ staticvoid lcl_DrawGraphicBackground( const SvxBrushItem& _rBackgrdBrush,
OutputDevice& _rOut, const SwRect& _rAlignedPaintRect, const GraphicObject& _rGraphicObj, bool _bNumberingGraphic,
SwPaintProperties const & properties, bool _bBackgrdAlreadyDrawn = false)
{ // draw background with background color, if // (1) graphic is not used as a numbering AND // (2) background is not already drawn AND // (3) intrinsic graphic is transparent OR intrinsic graphic doesn't exists if ( !_bNumberingGraphic &&
!_bBackgrdAlreadyDrawn &&
( _rGraphicObj.IsTransparent() || _rGraphicObj.GetType() == GraphicType::NONE )
)
{
lcl_implDrawGraphicBackground( _rBackgrdBrush, _rOut, _rAlignedPaintRect, _rGraphicObj, properties );
}
}
/** * NNOTE: the transparency of the background graphic is saved in * SvxBrushItem.GetGraphicObject(<shell>).GetAttr().Set/GetTransparency() * and is considered in the drawing of the graphic * * Thus, to provide transparent background graphic for text frames nothing * has to be coded * * Use align rectangle for drawing graphic Pixel-align coordinates for * drawing graphic * Outsource code for drawing background of the graphic * with a background color in method <lcl_DrawGraphicBackground> * * Also, change type of <bGrfNum> and <bClip> from <bool> to <bool>
*/ staticvoid lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext &rOutDev, const SwViewShell &rSh, const SwRect &rGrf, const SwRect &rOut, bool bGrfNum,
SwPaintProperties const & properties, bool bBackgrdAlreadyDrawn ) // add parameter <bBackgrdAlreadyDrawn> to indicate // that the background is already drawn.
{ // Calculate align rectangle from parameter <rGrf> and use aligned // rectangle <aAlignedGrfRect> in the following code
SwRect aAlignedGrfRect = rGrf;
::SwAlignRect( aAlignedGrfRect, &rSh, &rOutDev );
// Change type from <bool> to <bool>. constbool bNotInside = !rOut.Contains( aAlignedGrfRect ); if ( bNotInside )
{
rOutDev.Push( vcl::PushFlags::CLIPREGION );
rOutDev.IntersectClipRegion( rOut.SVRect() );
}
// Outsource drawing of background with a background color
::lcl_DrawGraphicBackground( rBrush, rOutDev, aAlignedGrfRect, *pGrf, bGrfNum, properties, bBackgrdAlreadyDrawn );
// Because for drawing a graphic left-top-corner and size coordinates are // used, these coordinates have to be determined on pixel level.
::SwAlignGrfRect( &aAlignedGrfRect, rOutDev );
if (!aPaintRange.isEmpty() &&
!rPaintRegion.empty() &&
!basegfx::fTools::equalZero(aPaintRange.getWidth()) &&
!basegfx::fTools::equalZero(aPaintRange.getHeight()))
{ // need to expand for correct AAed and non-AAed visualization as primitive. // This must probably be removed again when we will be able to get all Writer visualization // as primitives and Writer prepares all it's stuff in high precision coordinates (also // needs to avoid moving boundaries around to better show overlapping stuff...) if (comphelper::IsFuzzing() || SvtOptionsDrawinglayer::IsAntiAliasing())
{ // if AAed in principle expand by 0.5 in all directions. Since painting edges of // AAed regions does not add to no transparence (0.5 opacity covered by 0.5 opacity // is not full opacity but 0.75 opacity) we need some overlap here to avoid paint // artifacts. Checked experimentally - a little bit more in Y is needed, probably // due to still existing integer alignment and crunching in writer. staticconstdouble fExpandX = 0.55; staticconstdouble fExpandY = 0.70; const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(fExpandX, fExpandY));
aPaintRange.expand(aPaintRange.getMinimum() - aSingleUnit);
aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
} else
{ // if not AAed expand by one unit to bottom right due to the missing unit // from SwRect/Rectangle integer handling const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(1.0, 1.0));
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.