Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  printfun.cxx

  Sprache: C
 

/* -*- 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 <scitems.hxx>
#include <editeng/eeitem.hxx>

#include <printfun.hxx>

#include <editeng/adjustitem.hxx>
#include <editeng/borderline.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/brushitem.hxx>
#include <svtools/colorcfg.hxx>
#include <editeng/editstat.hxx>
#include <svx/fmview.hxx>
#include <vcl/pdfextoutdevdata.hxx>
#include <editeng/frmdiritem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/paperinf.hxx>
#include <editeng/pbinitem.hxx>
#include <editeng/shaditem.hxx>
#include <editeng/sizeitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/ulspitem.hxx>
#include <sfx2/printer.hxx>
#include <tools/multisel.hxx>
#include <sfx2/docfile.hxx>
#include <tools/urlobj.hxx>
#include <osl/diagnose.h>

#include <editutil.hxx>
#include <docsh.hxx>
#include <output.hxx>
#include <viewdata.hxx>
#include <viewopti.hxx>
#include <stlpool.hxx>
#include <pagepar.hxx>
#include <attrib.hxx>
#include <patattr.hxx>
#include <dociter.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <pagedata.hxx>
#include <printopt.hxx>
#include <prevloc.hxx>
#include <scmod.hxx>
#include <drwlayer.hxx>
#include <fillinfo.hxx>
#include <postit.hxx>

#include <memory>
#include <com/sun/star/document/XDocumentProperties.hpp>

#define ZOOM_MIN    10

namespace{

bool lcl_GetBool(const SfxItemSet* pSet, sal_uInt16 nWhich)
{
    return static_cast<const SfxBoolItem&>(pSet->Get(nWhich)).GetValue();
}

sal_uInt16 lcl_GetUShort(const SfxItemSet* pSet, sal_uInt16 nWhich)
{
    return static_cast<const SfxUInt16Item&>(pSet->Get(nWhich)).GetValue();
}

bool lcl_GetShow(const SfxItemSet* pSet, sal_uInt16 nWhich)
{
    return ScVObjMode::VOBJ_MODE_SHOW == static_cast<const ScViewObjectModeItem&>(pSet->Get(nWhich)).GetValue();
}


// namespace

ScPageRowEntry::ScPageRowEntry(const ScPageRowEntry& r)
{
    nStartRow = r.nStartRow;
    nEndRow   = r.nEndRow;
    nPagesX   = r.nPagesX;
    aHidden   = r.aHidden;
    aHidden.resize(nPagesX, false);
}

ScPageRowEntry& ScPageRowEntry::operator=(const ScPageRowEntry& r)
{
    nStartRow = r.nStartRow;
    nEndRow   = r.nEndRow;
    nPagesX   = r.nPagesX;
    aHidden   = r.aHidden;
    aHidden.resize(nPagesX, false);
    return *this;
}

void ScPageRowEntry::SetPagesX(size_t nNew)
{
    nPagesX = nNew;
    aHidden.resize(nPagesX, false);
}

void ScPageRowEntry::SetHidden(size_t nX)
{
    if ( nX < nPagesX )
    {
        if ( nX+1 == nPagesX )  // last page?
            --nPagesX;
        else
        {
            aHidden.resize(nPagesX, false);
            aHidden[nX] = true;
        }
    }
}

bool ScPageRowEntry::IsHidden(size_t nX) const
{
    return nX >= nPagesX || aHidden[nX];       //! inline?
}

size_t ScPageRowEntry::CountVisible() const
{
    if (!aHidden.empty())
    {
        size_t nVis = 0;
        for (size_t i=0; i<nPagesX; i++)
            if (!aHidden[i])
                ++nVis;
        return nVis;
    }
    else
        return nPagesX;
}

static tools::Long lcl_LineTotal(const ::editeng::SvxBorderLine* pLine)
{
    return pLine ? ( pLine->GetScaledWidth() ) : 0;
}

void ScPrintFunc::Construct( const ScPrintOptions* pOptions )
{
    rDocShell.UpdatePendingRowHeights( nPrintTab );

    SfxPrinter* pDocPrinter = rDoc.GetPrinter();   // use the printer, even for preview
    if (pDocPrinter)
        aOldPrinterMode = pDocPrinter->GetMapMode();

    //  unified MapMode for all calls (e.g. Repaint!!!)
    //  else, EditEngine outputs different text heights
    pDev->SetMapMode(MapMode(MapUnit::MapPixel));

    pBorderItem = nullptr;
    pBackgroundItem = nullptr;
    pShadowItem = nullptr;

    pEditEngine = nullptr;
    pEditDefaults = nullptr;

    ScStyleSheetPool* pStylePool    = rDoc.GetStyleSheetPool();
    SfxStyleSheetBase* pStyleSheet  = pStylePool->Find(
                                            rDoc.GetPageStyle( nPrintTab ),
                                            SfxStyleFamily::Page );
    if (pStyleSheet)
        pParamSet = &pStyleSheet->GetItemSet();
    else
    {
        OSL_FAIL("Template not found" );
        pParamSet = nullptr;
    }

    if (!bFromPrintState)
        nZoom = 100;
    nManualZoom = 100;
    bClearWin = false;
    bUseStyleColor = false;
    bIsRender = false;

    InitParam(pOptions);

    pPageData = nullptr;       // is only needed for initialisation
}

ScPrintFunc::ScPrintFunc(ScDocShell& rShell, SfxPrinter* pNewPrinter, SCTAB nTab, tools::Long nPage,
                         tools::Long nDocP, const ScRange* pArea, const ScPrintOptions* pOptions,
                         ScPageBreakData* pData, Size aSize, bool bPrintLandscape, bool bUsed)
    :   rDocShell           ( rShell ),
        rDoc(rDocShell.GetDocument()),
        pPrinter            ( pNewPrinter ),
        pDrawView           ( nullptr ),
        nPrintTab           ( nTab ),
        nPageStart          ( nPage ),
        nDocPages           ( nDocP ),
        pUserArea           ( pArea ),
        bFromPrintState     ( false ),
        bSourceRangeValid   ( false ),
        bPrintCurrentTable  ( false ),
        bMultiArea          ( false ),
        mbHasPrintRange(true),
        nTabPages           ( 0 ),
        nTotalPages         ( 0 ),
        bPrintAreaValid     ( false ),
        pPageData           ( pData ),
        aPrintPageSize      ( aSize ),
        bPrintPageLandscape ( bPrintLandscape ),
        bUsePrintDialogSetting ( bUsed )
{
    pDev = pPrinter.get();
    aSrcOffset = pPrinter->PixelToLogic(pPrinter->GetPageOffsetPixel(), MapMode(MapUnit::Map100thMM));
    m_aRanges.m_xPageEndX = std::make_shared<std::vector<SCCOL>>();
    m_aRanges.m_xPageEndY = std::make_shared<std::vector<SCROW>>();
    m_aRanges.m_xPageRows = std::make_shared<std::map<size_t, ScPageRowEntry>>();
    Construct( pOptions );
}

ScPrintFunc::ScPrintFunc(ScDocShell& rShell, SfxPrinter* pNewPrinter, const ScPrintState& rState,
                         const ScPrintOptions* pOptions, Size aSize, bool bPrintLandscape,
                         bool bUsed)
    :   rDocShell           ( rShell ),
        rDoc(rDocShell.GetDocument()),
        pPrinter            ( pNewPrinter ),
        pDrawView           ( nullptr ),
        pUserArea           ( nullptr ),
        bSourceRangeValid   ( false ),
        bPrintCurrentTable  ( false ),
        bMultiArea          ( false ),
        mbHasPrintRange(true),
        pPageData           ( nullptr ),
        aPrintPageSize      ( aSize ),
        bPrintPageLandscape ( bPrintLandscape ),
        bUsePrintDialogSetting ( bUsed )
{
    pDev = pPrinter.get();

    nPrintTab   = rState.nPrintTab;
    nStartCol   = rState.nStartCol;
    nStartRow   = rState.nStartRow;
    nEndCol     = rState.nEndCol;
    nEndRow     = rState.nEndRow;
    bPrintAreaValid = rState.bPrintAreaValid;
    nZoom       = rState.nZoom;
    m_aRanges = rState.m_aRanges;
    nTabPages   = rState.nTabPages;
    nTotalPages = rState.nTotalPages;
    nPageStart  = rState.nPageStart;
    nDocPages   = rState.nDocPages;
    bFromPrintState = true;

    aSrcOffset = pPrinter->PixelToLogic(pPrinter->GetPageOffsetPixel(), MapMode(MapUnit::Map100thMM));
    Construct( pOptions );
}

ScPrintFunc::ScPrintFunc( OutputDevice* pOutDev, ScDocShell& rShell, SCTAB nTab,
                            tools::Long nPage, tools::Long nDocP, const ScRange* pArea,
                            const ScPrintOptions* pOptions )
    :   rDocShell           ( rShell ),
        rDoc(rDocShell.GetDocument()),
        pPrinter            ( nullptr ),
        pDrawView           ( nullptr ),
        nPrintTab           ( nTab ),
        nPageStart          ( nPage ),
        nDocPages           ( nDocP ),
        pUserArea           ( pArea ),
        bFromPrintState     ( false ),
        bSourceRangeValid   ( false ),
        bPrintCurrentTable  ( false ),
        bMultiArea          ( false ),
        mbHasPrintRange(true),
        nTabPages           ( 0 ),
        nTotalPages         ( 0 ),
        bPrintAreaValid     ( false ),
        pPageData           ( nullptr ),
        aPrintPageSize      ( Size() ),
        bPrintPageLandscape ( false ),
        bUsePrintDialogSetting ( false )
{
    pDev = pOutDev;
    m_aRanges.m_xPageEndX = std::make_shared<std::vector<SCCOL>>();
    m_aRanges.m_xPageEndY = std::make_shared<std::vector<SCROW>>();
    m_aRanges.m_xPageRows = std::make_shared<std::map<size_t, ScPageRowEntry>>();
    Construct( pOptions );
}

ScPrintFunc::ScPrintFunc(OutputDevice* pOutDev, ScDocShell& rShell, const ScPrintState& rState,
                         const ScPrintOptions* pOptions, Size aSize, bool bPrintLandscape,
                         bool bUsed)
    :   rDocShell           ( rShell ),
        rDoc(rDocShell.GetDocument()),
        pPrinter            ( nullptr ),
        pDrawView           ( nullptr ),
        pUserArea           ( nullptr ),
        bSourceRangeValid   ( false ),
        bPrintCurrentTable  ( false ),
        bMultiArea          ( false ),
        mbHasPrintRange(true),
        pPageData           ( nullptr ),
        aPrintPageSize      ( aSize ),
        bPrintPageLandscape ( bPrintLandscape ),
        bUsePrintDialogSetting ( bUsed )
{
    pDev = pOutDev;

    nPrintTab   = rState.nPrintTab;
    nStartCol   = rState.nStartCol;
    nStartRow   = rState.nStartRow;
    nEndCol     = rState.nEndCol;
    nEndRow     = rState.nEndRow;
    bPrintAreaValid = rState.bPrintAreaValid;
    nZoom       = rState.nZoom;
    m_aRanges   = rState.m_aRanges;
    nTabPages   = rState.nTabPages;
    nTotalPages = rState.nTotalPages;
    nPageStart  = rState.nPageStart;
    nDocPages   = rState.nDocPages;
    bFromPrintState = true;

    Construct( pOptions );
}

void ScPrintFunc::GetPrintState(ScPrintState& rState)
{
    rState.nPrintTab    = nPrintTab;
    rState.nStartCol    = nStartCol;
    rState.nStartRow    = nStartRow;
    rState.nEndCol      = nEndCol;
    rState.nEndRow      = nEndRow;
    rState.bPrintAreaValid = bPrintAreaValid;
    rState.nZoom        = nZoom;
    rState.nTabPages    = nTabPages;
    rState.nTotalPages  = nTotalPages;
    rState.nPageStart   = nPageStart;
    rState.nDocPages    = nDocPages;
    rState.m_aRanges = m_aRanges;
}

bool ScPrintFunc::GetLastSourceRange( ScRange& rRange ) const
{
    rRange = aLastSourceRange;
    return bSourceRangeValid;
}

void ScPrintFunc::FillPageData()
{
    if (!pPageData)
        return;

    sal_uInt16 nCount = sal::static_int_cast<sal_uInt16>( pPageData->GetCount() );
    ScPrintRangeData& rData = pPageData->GetData(nCount);       // count up

    assert( bPrintAreaValid );
    rData.SetPrintRange( ScRange( nStartCol, nStartRow, nPrintTab,
                                    nEndCol, nEndRow, nPrintTab ) );
    // #i123672#
    if(m_aRanges.m_xPageEndX->empty())
    {
        OSL_ENSURE(false"vector access error for maPageEndX (!)");
    }
    else
    {
        rData.SetPagesX( m_aRanges.m_nPagesX, m_aRanges.m_xPageEndX->data());
    }

    // #i123672#
    if(m_aRanges.m_xPageEndY->empty())
    {
        OSL_ENSURE(false"vector access error for maPageEndY (!)");
    }
    else
    {
        rData.SetPagesY( m_aRanges.m_nTotalY, m_aRanges.m_xPageEndY->data());
    }

    //  Settings
    rData.SetTopDown( aTableParam.bTopDown );
    rData.SetAutomatic( !aAreaParam.bPrintArea );
}

ScPrintFunc::~ScPrintFunc()
{
    pEditDefaults.reset();
    pEditEngine.reset();

    //  Printer settings are now restored from outside

    //  For DrawingLayer/Charts, the MapMode of the printer (RefDevice) must always be correct
    SfxPrinter* pDocPrinter = rDoc.GetPrinter();   // use Preview also for the printer
    if (pDocPrinter)
        pDocPrinter->SetMapMode(aOldPrinterMode);
}

void ScPrintFunc::SetDrawView( FmFormView* pNew )
{
    pDrawView = pNew;
}

static void lcl_HidePrint( const ScTableInfo& rTabInfo, SCCOL nX1, SCCOL nX2 )
{
    for (SCSIZE nArrY=1; nArrY+1<rTabInfo.mnArrCount; nArrY++)
    {
        RowInfo* pThisRowInfo = &rTabInfo.mpRowInfo[nArrY];
        for (SCCOL nX=nX1; nX<=nX2; nX++)
        {
            ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nX);
            ScBasicCellInfo& rBasicCellInfo = pThisRowInfo->basicCellInfo(nX);
            if (!rBasicCellInfo.bEmptyCellText)
                if (rCellInfo.pPatternAttr->
                            GetItem(ATTR_PROTECTION, rCellInfo.pConditionSet).GetHidePrint())
                {
                    rCellInfo.maCell.clear();
                    rBasicCellInfo.bEmptyCellText = true;
                }
        }
    }
}

//          output to Device (static)
//
//      us used for:
//      -   Clipboard/Bitmap
//      -   Ole-Object (DocShell::Draw)
//      -   Preview of templates

void ScPrintFunc::DrawToDev(ScDocument& rDoc, OutputDevice* pDev, double /* nPrintFactor */,
                            const tools::Rectangle& rBound, ScViewData& rViewData, bool bMetaFile)
{
    //! evaluate nPrintFactor !!!

    SCTAB nTab = rViewData.GetTabNo();

    bool bDoGrid, bNullVal, bFormula;
    ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
    SfxStyleSheetBase* pStyleSheet = pStylePool->Find( rDoc.GetPageStyle( nTab ), SfxStyleFamily::Page );
    if (pStyleSheet)
    {
        SfxItemSet& rSet = pStyleSheet->GetItemSet();
        bDoGrid  = rSet.Get(ATTR_PAGE_GRID).GetValue();
        bNullVal = rSet.Get(ATTR_PAGE_NULLVALS).GetValue();
        bFormula = rSet.Get(ATTR_PAGE_FORMULAS).GetValue();
    }
    else
    {
        const ScViewOptions& rOpt = rDoc.GetViewOptions();
        bDoGrid  = rOpt.GetOption(VOPT_GRID);
        bNullVal = rOpt.GetOption(VOPT_NULLVALS);
        bFormula = rOpt.GetOption(VOPT_FORMULAS);
    }

    MapMode aMode = pDev->GetMapMode();

    tools::Rectangle aRect = rBound;

    if (aRect.Right() < aRect.Left() || aRect.Bottom() < aRect.Top())
        aRect = tools::Rectangle( Point(), pDev->GetOutputSize() );

    SCCOL nX1 = 0;
    SCROW nY1 = 0;
    SCCOL nX2 = OLE_STD_CELLS_X - 1;
    SCROW nY2 = OLE_STD_CELLS_Y - 1;
    if (bMetaFile)
    {
        ScRange aRange = rDoc.GetRange( nTab, rBound );
        nX1 = aRange.aStart.Col();
        nY1 = aRange.aStart.Row();
        nX2 = aRange.aEnd.Col();
        nY2 = aRange.aEnd.Row();
    }
    else
    {
        ScSplitPos eWhich = rViewData.GetActivePart();
        ScHSplitPos eHWhich = WhichH(eWhich);
        ScVSplitPos eVWhich = WhichV(eWhich);
        nX1 = rViewData.GetPosX(eHWhich);
        nY1 = rViewData.GetPosY(eVWhich);
        nX2 = nX1 + rViewData.VisibleCellsX(eHWhich);
        if (nX2>nX1) --nX2;
        nY2 = nY1 + rViewData.VisibleCellsY(eVWhich);
        if (nY2>nY1) --nY2;
    }

    if (nX1 > rDoc.MaxCol()) nX1 = rDoc.MaxCol();
    if (nX2 > rDoc.MaxCol()) nX2 = rDoc.MaxCol();
    if (nY1 > rDoc.MaxRow()) nY1 = rDoc.MaxRow();
    if (nY2 > rDoc.MaxRow()) nY2 = rDoc.MaxRow();

    tools::Long nDevSizeX = aRect.Right()-aRect.Left()+1;
    tools::Long nDevSizeY = aRect.Bottom()-aRect.Top()+1;

    tools::Long nTwipsSizeX = 0;
    for (SCCOL i=nX1; i<=nX2; i++)
        nTwipsSizeX += rDoc.GetColWidth( i, nTab );
    tools::Long nTwipsSizeY = rDoc.GetRowHeight( nY1, nY2, nTab );

    //  if no lines, still space for the outline frame (20 Twips = 1pt)
    //  (HasLines initializes aLines to 0,0,0,0)
    nTwipsSizeX += 20;
    nTwipsSizeY += 20;

    double nScaleX = static_cast<double>(nDevSizeX) / nTwipsSizeX;
    double nScaleY = static_cast<double>(nDevSizeY) / nTwipsSizeY;

                            //!     hand over Flag at FillInfo !!!!!
    ScRange aERange;
    bool bEmbed = rDoc.IsEmbedded();
    if (bEmbed)
    {
        rDoc.GetEmbedded(aERange);
        rDoc.ResetEmbedded();
    }

    //  Assemble data

    ScTableInfo aTabInfo(nY1, nY2, true);
    rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nTab,
                   nScaleX, nScaleY, false, bFormula );
    lcl_HidePrint( aTabInfo, nX1, nX2 );

    if (bEmbed)
        rDoc.SetEmbedded(aERange);

    tools::Long nScrX = aRect.Left();
    tools::Long nScrY = aRect.Top();

    //  If no lines, still leave space for grid lines
    //  (would be elseways cut away)
    // tdf#135891 - adjust the x position to ensure the correct starting point
    const Size aOnePixel = pDev->PixelToLogic(Size(1, 1));
    nScrX += aOnePixel.Width();
    nScrY += 1;

    ScOutputData aOutputData( pDev, OUTTYPE_PRINTER, aTabInfo, &rDoc, nTab,
                                nScrX, nScrY, nX1, nY1, nX2, nY2, nScaleX, nScaleY );
    aOutputData.SetMetaFileMode(bMetaFile);
    aOutputData.SetShowNullValues(bNullVal);
    aOutputData.SetShowFormulas(bFormula);

    vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(pDev->GetExtOutDevData());
    bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
    if (bTaggedPDF)
    {
        bool bReopen = aOutputData.ReopenPDFStructureElement(vcl::pdf::StructElement::Part);
        if (!bReopen)
        {
            sal_Int32 nId = pPDF->EnsureStructureElement(nullptr);
            pPDF->InitStructureElement(nId, vcl::pdf::StructElement::Part, u"Worksheet"_ustr);
            pPDF->BeginStructureElement(nId);
            pPDF->GetScPDFState()->m_WorksheetId = nId;
        }
    }

    ScDrawLayer* pModel = rDoc.GetDrawLayer();
    std::unique_ptr<FmFormView> pDrawView;

    if( pModel )
    {
        pDrawView.reset(
            new FmFormView(
                *pModel,
                pDev));
        pDrawView->ShowSdrPage(pDrawView->GetModel().GetPage(nTab));
        pDrawView->SetPrintPreview();
        aOutputData.SetDrawView( pDrawView.get() );
    }

    //! SetUseStyleColor ??

    if ( bMetaFile && pDev->IsVirtual() )
        aOutputData.SetSnapPixel();

    Point aLogStart = pDev->PixelToLogic(Point(nScrX, nScrY), MapMode(MapUnit::Map100thMM));
    tools::Long nLogStX = aLogStart.X();
    tools::Long nLogStY = aLogStart.Y();

    //!     nZoom for GetFont in OutputData ???

    if (!bMetaFile)
        pDev->SetMapMode(rViewData.GetLogicMode(rViewData.GetActivePart()));

    // #i72502#
    const Point aMMOffset(aOutputData.PrePrintDrawingLayer(nLogStX, nLogStY));
    aOutputData.PrintDrawingLayer(SC_LAYER_BACK, aMMOffset);

    if (!bMetaFile)
        pDev->SetMapMode(aMode);

    aOutputData.DrawBackground(*pDev);

    aOutputData.DrawShadow();
    aOutputData.DrawFrame(*pDev);
    aOutputData.DrawSparklines(*pDev);
    aOutputData.DrawStrings();

    if (!bMetaFile)
        pDev->SetMapMode(rViewData.GetLogicMode(rViewData.GetActivePart()));

    aOutputData.DrawEdit(!bMetaFile);

    if (bDoGrid)
    {
        if (!bMetaFile)
            pDev->SetMapMode(aMode);

        aOutputData.DrawGrid(*pDev, truefalse);    // no page breaks

        pDev->SetLineColor( COL_BLACK );

        Size aOne = pDev->PixelToLogic( Size(1,1) );
        if (bMetaFile)
            aOne = Size(1,1);   // compatible with DrawGrid
        tools::Long nRight = nScrX + aOutputData.GetScrW() - aOne.Width();
        tools::Long nBottom = nScrY + aOutputData.GetScrH() - aOne.Height();

        bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );

        // extra line at the left edge for left-to-right, right for right-to-left
        if ( bLayoutRTL )
            pDev->DrawLine( Point(nRight,nScrY), Point(nRight,nBottom) );
        else
            pDev->DrawLine( Point(nScrX,nScrY), Point(nScrX,nBottom) );
        // extra line at the top in both cases
        pDev->DrawLine( Point(nScrX,nScrY), Point(nRight,nScrY) );
    }

    // #i72502#
    aOutputData.PrintDrawingLayer(SC_LAYER_FRONT, aMMOffset);

    if (bTaggedPDF)
        pPDF->EndStructureElement();

    aOutputData.PrintDrawingLayer(SC_LAYER_INTERN, aMMOffset);
    aOutputData.PostPrintDrawingLayer(aMMOffset); // #i74768#
}

//          Printing

static void lcl_FillHFParam( ScPrintHFParam& rParam, const SfxItemSet* pHFSet )
{
    //  nDistance must be initialized differently before

    if ( pHFSet == nullptr )
    {
        rParam.bEnable  = false;
        rParam.pBorder  = nullptr;
        rParam.pBack    = nullptr;
        rParam.pShadow  = nullptr;
    }
    else
    {
        rParam.bEnable  = pHFSet->Get(ATTR_PAGE_ON).GetValue();
        rParam.bDynamic = pHFSet->Get(ATTR_PAGE_DYNAMIC).GetValue();
        rParam.bShared  = pHFSet->Get(ATTR_PAGE_SHARED).GetValue();
        rParam.bSharedFirst = pHFSet->Get(ATTR_PAGE_SHARED_FIRST).GetValue();
        rParam.nHeight  = pHFSet->Get(ATTR_PAGE_SIZE).GetSize().Height();
        const SvxLRSpaceItem* pHFLR = &pHFSet->Get(ATTR_LRSPACE);
        tools::Long nTmp;
        nTmp = pHFLR->ResolveLeft({});
        rParam.nLeft = nTmp < 0 ? 0 : sal_uInt16(nTmp);
        nTmp = pHFLR->ResolveRight({});
        rParam.nRight = nTmp < 0 ? 0 : sal_uInt16(nTmp);
        rParam.pBorder  = &pHFSet->Get(ATTR_BORDER);
        rParam.pBack    = &pHFSet->Get(ATTR_BACKGROUND);
        rParam.pShadow  = &pHFSet->Get(ATTR_SHADOW);

//   now back in the dialog:
//      rParam.nHeight += rParam.nDistance;             // not in the dialog any more ???

        rParam.nHeight += lcl_LineTotal( rParam.pBorder->GetTop() ) +
                          lcl_LineTotal( rParam.pBorder->GetBottom() );

        rParam.nManHeight = rParam.nHeight;
    }

    if (!rParam.bEnable)
        rParam.nHeight = 0;
}

//  bNew = TRUE:    search for used part of the document
//  bNew = FALSE:   only limit whole lines/columns

bool ScPrintFunc::AdjustPrintArea( bool bNew )
{
    SCCOL nOldEndCol = nEndCol; // only important for !bNew
    SCROW nOldEndRow = nEndRow;
    bool bChangeCol = true;         // at bNew both are being adjusted
    bool bChangeRow = true;

    bool bNotes = aTableParam.bNotes;
    if ( bNew )
    {
        nStartCol = 0;
        nStartRow = 0;
        if (!rDoc.GetPrintArea( nPrintTab, nEndCol, nEndRow, bNotes ) && aTableParam.bSkipEmpty)
            return false;   // nothing
        bPrintAreaValid = true;
    }
    else
    {
        bool bFound = true;
        bChangeCol = ( nStartCol == 0 && nEndCol == rDoc.MaxCol() );
        bChangeRow = ( nStartRow == 0 && nEndRow == rDoc.MaxRow() );
        bool bForcedChangeRow = false;

        // #i53558# Crop entire column of old row limit to real print area with
        // some fuzzyness.
        if (!bChangeRow && nStartRow == 0)
        {
            SCROW nPAEndRow;
            bFound = rDoc.GetPrintAreaVer( nPrintTab, nStartCol, nEndCol, nPAEndRow, bNotes );
            // Say we don't want to print more than ~1000 empty rows, which are
            // about 14 pages intentionally left blank...
            const SCROW nFuzzy = 23*42;
            if (nPAEndRow + nFuzzy < nEndRow)
            {
                bForcedChangeRow = true;
                nEndRow = nPAEndRow;
            }
            else
                bFound = true;  // user seems to _want_ to print some empty rows
        }
        // TODO: in case we extend the number of columns we may have to do the
        // same for horizontal cropping.

        if ( bChangeCol && bChangeRow )
            bFound = rDoc.GetPrintArea( nPrintTab, nEndCol, nEndRow, bNotes );
        else if ( bChangeCol )
            bFound = rDoc.GetPrintAreaHor( nPrintTab, nStartRow, nEndRow, nEndCol );
        else if ( bChangeRow )
            bFound = rDoc.GetPrintAreaVer( nPrintTab, nStartCol, nEndCol, nEndRow, bNotes );

        if (!bFound)
            return false;   // empty

        bPrintAreaValid = true;
        if (bForcedChangeRow)
            bChangeRow = true;
    }

    assert( bPrintAreaValid );
    rDoc.ExtendMerge( nStartCol,nStartRow, nEndCol,nEndRow, nPrintTab );  // no Refresh, incl. Attrs

    if ( bChangeCol )
    {
        OutputDevice* pRefDev = rDoc.GetPrinter();     // use the printer also for Preview
        pRefDev->SetMapMode(MapMode(MapUnit::MapPixel)); // important for GetNeededSize

        rDoc.ExtendPrintArea( pRefDev,
                            nPrintTab, nStartCol, nStartRow, nEndCol, nEndRow );
        //  changing nEndCol
    }

    if ( nEndCol < rDoc.MaxCol() && rDoc.HasAttrib(
                    nEndCol,nStartRow,nPrintTab, nEndCol,nEndRow,nPrintTab, HasAttrFlags::ShadowRight ) )
        ++nEndCol;
    if ( nEndRow < rDoc.MaxRow() && rDoc.HasAttrib(
                    nStartCol,nEndRow,nPrintTab, nEndCol,nEndRow,nPrintTab, HasAttrFlags::ShadowDown ) )
        ++nEndRow;

    if (!bChangeCol) nEndCol = nOldEndCol;
    if (!bChangeRow) nEndRow = nOldEndRow;

    return true;
}

tools::Long ScPrintFunc::TextHeight( const EditTextObject* pObject )
{
    if (!pObject)
        return 0;

    pEditEngine->SetTextTempDefaults(*pObject, *pEditDefaults);

    return static_cast<tools::Long>(pEditEngine->GetTextHeight());
}

//  nZoom must be set !!!
//  and the respective Twip-MapMode configured

void ScPrintFunc::UpdateHFHeight( ScPrintHFParam& rParam )
{
    OSL_ENSURE( aPageSize.Width(), "UpdateHFHeight without aPageSize");

    if (!(rParam.bEnable && rParam.bDynamic))
        return;

    //  calculate nHeight from content

    MakeEditEngine();
    tools::Long nPaperWidth = ( aPageSize.Width() - nLeftMargin - nRightMargin -
                            rParam.nLeft - rParam.nRight ) * 100 / nZoom;
    if (rParam.pBorder)
        nPaperWidth -= ( rParam.pBorder->GetDistance(SvxBoxItemLine::LEFT) +
                         rParam.pBorder->GetDistance(SvxBoxItemLine::RIGHT) +
                         lcl_LineTotal(rParam.pBorder->GetLeft()) +
                         lcl_LineTotal(rParam.pBorder->GetRight()) ) * 100 / nZoom;

    if (rParam.pShadow && rParam.pShadow->GetLocation() != SvxShadowLocation::NONE)
        nPaperWidth -= ( rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::LEFT) +
                         rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::RIGHT) ) * 100 / nZoom;

    if (nPaperWidth <= 0)
    {
        SAL_WARN("sc.ui""Header/Footer unreasonably narrow width of: " << nPaperWidth << ", cannot calculate height");
        return;
    }

    pEditEngine->SetPaperSize( Size( nPaperWidth, 10000 ) );

    tools::Long nMaxHeight = 0;
    if ( rParam.pLeft )
    {
        nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pLeft->GetLeftArea() ) );
        nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pLeft->GetCenterArea() ) );
        nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pLeft->GetRightArea() ) );
    }
    if ( rParam.pRight )
    {
        nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pRight->GetLeftArea() ) );
        nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pRight->GetCenterArea() ) );
        nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pRight->GetRightArea() ) );
    }
    if ( rParam.pFirst )
    {
        nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pFirst->GetLeftArea() ) );
        nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pFirst->GetCenterArea() ) );
        nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pFirst->GetRightArea() ) );
    }

    rParam.nHeight = nMaxHeight + rParam.nDistance;
    if (rParam.pBorder)
        rParam.nHeight += rParam.pBorder->GetDistance(SvxBoxItemLine::TOP) +
                          rParam.pBorder->GetDistance(SvxBoxItemLine::BOTTOM) +
                          lcl_LineTotal( rParam.pBorder->GetTop() ) +
                          lcl_LineTotal( rParam.pBorder->GetBottom() );
    if (rParam.pShadow && rParam.pShadow->GetLocation() != SvxShadowLocation::NONE)
        rParam.nHeight += rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::TOP) +
                          rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::BOTTOM);

    if (rParam.nHeight < rParam.nManHeight)
        rParam.nHeight = rParam.nManHeight;         // configured minimum
}

void ScPrintFunc::InitParam( const ScPrintOptions* pOptions )
{
    if (!pParamSet)
        return;

                                // TabPage "Page"
    const SvxLRSpaceItem* pLRItem = &pParamSet->Get( ATTR_LRSPACE );
    tools::Long nTmp;
    nTmp = pLRItem->ResolveLeft({});
    nLeftMargin = nTmp < 0 ? 0 : sal_uInt16(nTmp);
    nTmp = pLRItem->ResolveRight({});
    nRightMargin = nTmp < 0 ? 0 : sal_uInt16(nTmp);
    const SvxULSpaceItem* pULItem = &pParamSet->Get( ATTR_ULSPACE );
    nTopMargin    = pULItem->GetUpper();
    nBottomMargin = pULItem->GetLower();

    const SvxPageItem* pPageItem = &pParamSet->Get( ATTR_PAGE );
    nPageUsage          = pPageItem->GetPageUsage();
    bLandscape          = bUsePrintDialogSetting ? bPrintPageLandscape : pPageItem->IsLandscape();
    aFieldData.eNumType = pPageItem->GetNumType();

    bCenterHor = pParamSet->Get(ATTR_PAGE_HORCENTER).GetValue();
    bCenterVer = pParamSet->Get(ATTR_PAGE_VERCENTER).GetValue();

    aPageSize = bUsePrintDialogSetting ? aPrintPageSize : pParamSet->Get(ATTR_PAGE_SIZE).GetSize();
    if ( !aPageSize.Width() || !aPageSize.Height() )
    {
        OSL_FAIL("PageSize Null ?!?!?");
        aPageSize = SvxPaperInfo::GetPaperSize( PAPER_A4 );
    }

    pBorderItem     = &pParamSet->Get(ATTR_BORDER);
    pBackgroundItem = &pParamSet->Get(ATTR_BACKGROUND);
    pShadowItem     = &pParamSet->Get(ATTR_SHADOW);

                                // TabPage "Headline"

    aHdr.pLeft      = &pParamSet->Get(ATTR_PAGE_HEADERLEFT);      // Content
    aHdr.pRight     = &pParamSet->Get(ATTR_PAGE_HEADERRIGHT);
    aHdr.pFirst     = &pParamSet->Get(ATTR_PAGE_HEADERFIRST);

    const SfxItemSet* pHeaderSet = nullptr;
    if ( const SvxSetItem* pHeaderSetItem = pParamSet->GetItemIfSet( ATTR_PAGE_HEADERSET, false ) )
    {
        pHeaderSet = &pHeaderSetItem->GetItemSet();
                                                        // Headline has space below
        aHdr.nDistance  = pHeaderSet->Get(ATTR_ULSPACE).GetLower();
    }
    lcl_FillHFParam( aHdr, pHeaderSet );

                                // TabPage "Footline"

    aFtr.pLeft      = &pParamSet->Get(ATTR_PAGE_FOOTERLEFT);      // Content
    aFtr.pRight     = &pParamSet->Get(ATTR_PAGE_FOOTERRIGHT);
    aFtr.pFirst     = &pParamSet->Get(ATTR_PAGE_FOOTERFIRST);

    const SfxItemSet* pFooterSet = nullptr;
    if ( const SvxSetItem* pFooterSetItem = pParamSet->GetItemIfSet( ATTR_PAGE_FOOTERSET, false ) )
    {
        pFooterSet = &pFooterSetItem->GetItemSet();
                                                        // Footline has space above
        aFtr.nDistance  = pFooterSet->Get(ATTR_ULSPACE).GetUpper();
    }
    lcl_FillHFParam( aFtr, pFooterSet );

    // Compile Table-/Area-Params from single Items

    // TabPage "Table"

    const SfxUInt16Item*     pScaleItem          = nullptr;
    const ScPageScaleToItem* pScaleToItem        = nullptr;
    const SfxUInt16Item*     pScaleToPagesItem   = nullptr;
    SfxItemState             eState;

    eState = pParamSet->GetItemState( ATTR_PAGE_SCALE, false,
                                      reinterpret_cast<const SfxPoolItem**>(&pScaleItem) );
    if ( SfxItemState::DEFAULT == eState )
        pScaleItem = &pParamSet->GetPool()->GetUserOrPoolDefaultItem( ATTR_PAGE_SCALE );

    eState = pParamSet->GetItemState( ATTR_PAGE_SCALETO, false,
                                      reinterpret_cast<const SfxPoolItem**>(&pScaleToItem) );
    if ( SfxItemState::DEFAULT == eState )
        pScaleToItem = &pParamSet->GetPool()->GetUserOrPoolDefaultItem( ATTR_PAGE_SCALETO );

    eState = pParamSet->GetItemState( ATTR_PAGE_SCALETOPAGES, false,
                                      reinterpret_cast<const SfxPoolItem**>(&pScaleToPagesItem) );
    if ( SfxItemState::DEFAULT == eState )
        pScaleToPagesItem = &pParamSet->GetPool()->GetUserOrPoolDefaultItem( ATTR_PAGE_SCALETOPAGES );

    OSL_ENSURE( pScaleItem && pScaleToItem && pScaleToPagesItem, "Missing ScaleItem! :-/" );

    aTableParam.bCellContent    = true;
    aTableParam.bNotes          = lcl_GetBool(pParamSet,ATTR_PAGE_NOTES);
    aTableParam.bGrid           = lcl_GetBool(pParamSet,ATTR_PAGE_GRID);
    aTableParam.bHeaders        = lcl_GetBool(pParamSet,ATTR_PAGE_HEADERS);
    aTableParam.bFormulas       = lcl_GetBool(pParamSet,ATTR_PAGE_FORMULAS);
    aTableParam.bNullVals       = lcl_GetBool(pParamSet,ATTR_PAGE_NULLVALS);
    aTableParam.bCharts         = lcl_GetShow(pParamSet,ATTR_PAGE_CHARTS);
    aTableParam.bObjects        = lcl_GetShow(pParamSet,ATTR_PAGE_OBJECTS);
    aTableParam.bDrawings       = lcl_GetShow(pParamSet,ATTR_PAGE_DRAWINGS);
    aTableParam.bTopDown        = lcl_GetBool(pParamSet,ATTR_PAGE_TOPDOWN);
    aTableParam.bLeftRight      = !aTableParam.bLeftRight;
    aTableParam.nFirstPageNo    = lcl_GetUShort(pParamSet,ATTR_PAGE_FIRSTPAGENO);
    if (!aTableParam.nFirstPageNo)
        aTableParam.nFirstPageNo = static_cast<sal_uInt16>(nPageStart);     // from previous table

    if ( pScaleItem && pScaleToItem && pScaleToPagesItem )
    {
        sal_uInt16  nScaleAll     = pScaleItem->GetValue();
        sal_uInt16  nScaleToPages = pScaleToPagesItem->GetValue();

        aTableParam.bScaleNone      = (nScaleAll     == 100);
        aTableParam.bScaleAll       = (nScaleAll      > 0  );
        aTableParam.bScaleTo        = pScaleToItem->IsValid();
        aTableParam.bScalePageNum   = (nScaleToPages  > 0  );
        aTableParam.nScaleAll       = nScaleAll;
        aTableParam.nScaleWidth     = pScaleToItem->GetWidth();
        aTableParam.nScaleHeight    = pScaleToItem->GetHeight();
        aTableParam.nScalePageNum   = nScaleToPages;
    }
    else
    {
        aTableParam.bScaleNone      = true;
        aTableParam.bScaleAll       = false;
        aTableParam.bScaleTo        = false;
        aTableParam.bScalePageNum   = false;
        aTableParam.nScaleAll       = 0;
        aTableParam.nScaleWidth     = 0;
        aTableParam.nScaleHeight    = 0;
        aTableParam.nScalePageNum   = 0;
    }

    //  skip empty pages only if options with that flag are passed
    aTableParam.bSkipEmpty = pOptions && pOptions->GetSkipEmpty();
    if ( pPageData )
        aTableParam.bSkipEmpty = false;
    // If pPageData is set, only the breaks are interesting for the
    // pagebreak preview, empty pages are not addressed separately.

    aTableParam.bForceBreaks = pOptions && pOptions->GetForceBreaks();

    // TabPage "Parts":

    //! walk through all PrintAreas of the table !!!
    const ScRange*  pPrintArea = rDoc.GetPrintRange( nPrintTab, 0 );
    std::optional<ScRange> oRepeatCol = rDoc.GetRepeatColRange( nPrintTab );
    std::optional<ScRange> oRepeatRow = rDoc.GetRepeatRowRange( nPrintTab );

    //  ignoring ATTR_PAGE_PRINTTABLES

    bool bHasPrintRange = rDoc.HasPrintRange();
    sal_uInt16 nPrintRangeCount = rDoc.GetPrintRangeCount(nPrintTab);
    bool bPrintEntireSheet = rDoc.IsPrintEntireSheet(nPrintTab);

    if (!bPrintEntireSheet && !nPrintRangeCount)
        mbHasPrintRange = false;

    if ( pUserArea )                // UserArea (selection) has priority
    {
        bPrintCurrentTable    =
        aAreaParam.bPrintArea = true;                   // Selection
        aAreaParam.aPrintArea = *pUserArea;

        //  The table-query is already in DocShell::Print, here always
        aAreaParam.aPrintArea.aStart.SetTab(nPrintTab);
        aAreaParam.aPrintArea.aEnd.SetTab(nPrintTab);
    }
    else if (bHasPrintRange)
    {
        if ( pPrintArea )                               // at least one set?
        {
            bPrintCurrentTable    =
            aAreaParam.bPrintArea = true;
            aAreaParam.aPrintArea = *pPrintArea;

            bMultiArea = nPrintRangeCount > 1;
        }
        else
        {
            // do not print hidden sheets with "Print entire sheet" flag
            bPrintCurrentTable = rDoc.IsPrintEntireSheet( nPrintTab ) && rDoc.IsVisible( nPrintTab );
            aAreaParam.bPrintArea = !bPrintCurrentTable;    // otherwise the table is always counted
        }
    }
    else
    {
        //  don't print hidden tables if there's no print range defined there
        if ( rDoc.IsVisible( nPrintTab ) )
        {
            aAreaParam.bPrintArea = false;
            bPrintCurrentTable = true;
        }
        else
        {
            aAreaParam.bPrintArea = true;   // otherwise the table is always counted
            bPrintCurrentTable = false;
        }
    }

    if ( oRepeatCol )
    {
        aAreaParam.bRepeatCol = true;
        nRepeatStartCol = oRepeatCol->aStart.Col();
        nRepeatEndCol   = oRepeatCol->aEnd  .Col();
    }
    else
    {
        aAreaParam.bRepeatCol = false;
        nRepeatStartCol = nRepeatEndCol = SCCOL_REPEAT_NONE;
    }

    if ( oRepeatRow )
    {
        aAreaParam.bRepeatRow = true;
        nRepeatStartRow = oRepeatRow->aStart.Row();
        nRepeatEndRow   = oRepeatRow->aEnd  .Row();
    }
    else
    {
        aAreaParam.bRepeatRow = false;
        nRepeatStartRow = nRepeatEndRow = SCROW_REPEAT_NONE;
    }

            //  Split pages

    if (!bPrintAreaValid)
    {
        nTabPages = CountPages();                                   // also calculates zoom
        nTotalPages = nTabPages;
        nTotalPages += CountNotePages();
    }
    else
    {
        CalcPages();            // search breaks only
        CountNotePages();       // Count notes, even if number of pages is already known
    }

    if (nDocPages)
        aFieldData.nTotalPages = nDocPages;
    else
        aFieldData.nTotalPages = nTotalPages;

    SetDateTime( DateTime( DateTime::SYSTEM ) );

    if( rDocShell.getDocProperties()->getTitle().getLength() != 0 )
        aFieldData.aTitle = rDocShell.getDocProperties()->getTitle();
    else
        aFieldData.aTitle = rDocShell.GetTitle();

    const INetURLObject& rURLObj = rDocShell.GetMedium()->GetURLObject();
    aFieldData.aLongDocName = rURLObj.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
    if ( !aFieldData.aLongDocName.isEmpty() )
        aFieldData.aShortDocName = rURLObj.GetLastName(INetURLObject::DecodeMechanism::Unambiguous);
    else
        aFieldData.aShortDocName = aFieldData.aLongDocName = aFieldData.aTitle;

    //  Printer settings (Orientation, Paper) at DoPrint
}

Size ScPrintFunc::GetDataSize() const
{
    Size aSize = aPageSize;
    aSize.AdjustWidth( -(nLeftMargin + nRightMargin) );
    aSize.AdjustHeight( -(nTopMargin + nBottomMargin) );
    aSize.AdjustHeight( -(aHdr.nHeight + aFtr.nHeight) );
    return aSize;
}

void ScPrintFunc::GetScaleData( Size& rPhysSize, tools::Long& rDocHdr, tools::Long& rDocFtr )
{
    rPhysSize = aPageSize;
    rPhysSize.AdjustWidth( -(nLeftMargin + nRightMargin) );
    rPhysSize.AdjustHeight( -(nTopMargin + nBottomMargin) );

    rDocHdr = aHdr.nHeight;
    rDocFtr = aFtr.nHeight;
}

void ScPrintFunc::SetDateTime( const DateTime& rDateTime )
{
    aFieldData.aDateTime = rDateTime;
}

static void lcl_DrawGraphic( const Graphic &rGraphic, vcl::RenderContext& rOutDev,
                      const tools::Rectangle &rGrf, const tools::Rectangle &rOut )
{
    const bool bNotInside = !rOut.Contains( rGrf );
    if ( bNotInside )
    {
        rOutDev.Push();
        rOutDev.IntersectClipRegion( rOut );
    }

    rGraphic.Draw(rOutDev, rGrf.TopLeft(), rGrf.GetSize());

    if ( bNotInside )
        rOutDev.Pop();
}

static void lcl_DrawGraphic( const SvxBrushItem &rBrush, vcl::RenderContext& rOutDev, const OutputDevice* pRefDev,
                        const tools::Rectangle &rOrg, const tools::Rectangle &rOut,
                        OUString const & referer )
{
    Size aGrfSize(0,0);
    const Graphic *pGraphic = rBrush.GetGraphic(referer);
    SvxGraphicPosition ePos;
    if ( pGraphic && pGraphic->IsSupportedGraphic() )
    {
        const MapMode aMapMM( MapUnit::Map100thMM );
        if ( pGraphic->GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel )
            aGrfSize = pRefDev->PixelToLogic( pGraphic->GetPrefSize(), aMapMM );
        else
            aGrfSize = OutputDevice::LogicToLogic( pGraphic->GetPrefSize(),
                                    pGraphic->GetPrefMapMode(), aMapMM );
        ePos = rBrush.GetGraphicPos();
    }
    else
        ePos = GPOS_NONE;

    Point aPos;
    Size aDrawSize = aGrfSize;

    bool bDraw = true;
    switch ( ePos )
    {
        case GPOS_LT: aPos = rOrg.TopLeft();
                      break;
        case GPOS_MT: aPos.setY( rOrg.Top() );
                      aPos.setX( rOrg.Left() + rOrg.GetSize().Width()/2 - aGrfSize.Width()/2 );
                      break;
        case GPOS_RT: aPos.setY( rOrg.Top() );
                      aPos.setX( rOrg.Right() - aGrfSize.Width() );
                      break;

        case GPOS_LM: aPos.setY( rOrg.Top() + rOrg.GetSize().Height()/2 - aGrfSize.Height()/2 );
                      aPos.setX( rOrg.Left() );
                      break;
        case GPOS_MM: aPos.setY( rOrg.Top() + rOrg.GetSize().Height()/2 - aGrfSize.Height()/2 );
                      aPos.setX( rOrg.Left() + rOrg.GetSize().Width()/2 - aGrfSize.Width()/2 );
                      break;
        case GPOS_RM: aPos.setY( rOrg.Top() + rOrg.GetSize().Height()/2 - aGrfSize.Height()/2 );
                      aPos.setX( rOrg.Right() - aGrfSize.Width() );
                      break;

        case GPOS_LB: aPos.setY( rOrg.Bottom() - aGrfSize.Height() );
                      aPos.setX( rOrg.Left() );
                      break;
        case GPOS_MB: aPos.setY( rOrg.Bottom() - aGrfSize.Height() );
                      aPos.setX( rOrg.Left() + rOrg.GetSize().Width()/2 - aGrfSize.Width()/2 );
                      break;
        case GPOS_RB: aPos.setY( rOrg.Bottom() - aGrfSize.Height() );
                      aPos.setX( rOrg.Right() - aGrfSize.Width() );
                      break;

        case GPOS_AREA:
                      aPos = rOrg.TopLeft();
                      aDrawSize = rOrg.GetSize();
                      break;
        case GPOS_TILED:
                    {
                        //  use GraphicObject::DrawTiled instead of an own loop
                        //  (pixel rounding is handled correctly, and a very small bitmap
                        //  is duplicated into a bigger one for better performance)

                        GraphicObject aObject( *pGraphic );

                        if( rOutDev.GetOutDevType() == OUTDEV_PDF &&
                            (aObject.GetType() == GraphicType::Bitmap || aObject.GetType() == GraphicType::Default) )
                        {
                            // For PDF export, every draw
                            // operation for bitmaps takes a noticeable
                            // amount of place (~50 characters). Thus,
                            // optimize between tile bitmap size and
                            // number of drawing operations here.
                            //
                            //                  A_out
                            // n_chars = k1 *  ---------- + k2 * A_bitmap
                            //                  A_bitmap
                            //
                            // minimum n_chars is obtained for (derive for
                            // A_bitmap, set to 0, take positive
                            // solution):
                            //                   k1
                            // A_bitmap = Sqrt( ---- A_out )
                            //                   k2
                            //
                            // where k1 is the number of chars per draw
                            // operation, and k2 is the number of chars
                            // per bitmap pixel. This is approximately 50
                            // and 7 for current PDF writer, respectively.

                            const double    k1( 50 );
                            const double    k2( 7 );
                            const Size      aSize( rOrg.GetSize() );
                            const double    Abitmap( k1/k2 * aSize.Width()*aSize.Height() );

                            aObject.DrawTiled( rOutDev, rOrg, aGrfSize, Size(0,0),
                                               ::std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) );
                        }
                        else
                        {
                            aObject.DrawTiled( rOutDev, rOrg, aGrfSize, Size(0,0) );
                        }

                        bDraw = false;
                    }
                    break;

        case GPOS_NONE:
                      bDraw = false;
                      break;

        default: OSL_ENSURE( false"new Graphic position?" );
    }
    tools::Rectangle aGrf( aPos,aDrawSize );
    if ( bDraw && aGrf.Overlaps( rOut ) )
    {
        lcl_DrawGraphic( *pGraphic, rOutDev, aGrf, rOut );
    }
}

// The frame is drawn inwards

void ScPrintFunc::DrawBorder( tools::Long nScrX, tools::Long nScrY, tools::Long nScrW, tools::Long nScrH,
                                const SvxBoxItem* pBorderData, const SvxBrushItem* pBackground,
                                const SvxShadowItem* pShadow )
{
    //!     direct output from SvxBoxItem !!!

    if (pBorderData)
        if ( !pBorderData->GetTop() && !pBorderData->GetBottom() && !pBorderData->GetLeft() &&
                                        !pBorderData->GetRight() )
            pBorderData = nullptr;

    if (!pBorderData && !pBackground && !pShadow)
        return;                                     // nothing to do

    tools::Long nLeft   = 0;
    tools::Long nRight  = 0;
    tools::Long nTop    = 0;
    tools::Long nBottom = 0;

    //  aFrameRect - outside around frame, without shadow
    if ( pShadow && pShadow->GetLocation() != SvxShadowLocation::NONE )
    {
        nLeft   += static_cast<tools::Long>( pShadow->CalcShadowSpace(SvxShadowItemSide::LEFT)   * nScaleX );
        nRight  += static_cast<tools::Long>( pShadow->CalcShadowSpace(SvxShadowItemSide::RIGHT)  * nScaleX );
        nTop    += static_cast<tools::Long>( pShadow->CalcShadowSpace(SvxShadowItemSide::TOP)    * nScaleY );
        nBottom += static_cast<tools::Long>( pShadow->CalcShadowSpace(SvxShadowItemSide::BOTTOM) * nScaleY );
    }
    tools::Rectangle aFrameRect( Point(nScrX+nLeft, nScrY+nTop),
                          Size(nScrW-nLeft-nRight, nScrH-nTop-nBottom) );

    //  center of frame, to paint lines through OutputData
    if (pBorderData)
    {
        nLeft   += static_cast<tools::Long>( lcl_LineTotal(pBorderData->GetLeft())   * nScaleX / 2 );
        nRight  += static_cast<tools::Long>( lcl_LineTotal(pBorderData->GetRight())  * nScaleX / 2 );
        nTop    += static_cast<tools::Long>( lcl_LineTotal(pBorderData->GetTop())    * nScaleY / 2 );
        nBottom += static_cast<tools::Long>( lcl_LineTotal(pBorderData->GetBottom()) * nScaleY / 2 );
    }
    tools::Long nEffHeight = nScrH - nTop - nBottom;
    tools::Long nEffWidth = nScrW - nLeft - nRight;
    if (nEffHeight<=0 || nEffWidth<=0)
        return;                                         // empty

    if ( pBackground )
    {
        if (pBackground->GetGraphicPos() != GPOS_NONE)
        {
            OutputDevice* pRefDev;
            if ( bIsRender )
                pRefDev = pDev;                 // don't use printer for PDF
            else
                pRefDev = rDoc.GetPrinter();   // use printer also for preview
            OUString referer;
            if (rDocShell.HasName()) {
                referer = rDocShell.GetMedium()->GetName();
            }
            lcl_DrawGraphic(*pBackground, *pDev, pRefDev, aFrameRect, aFrameRect, referer);
        }
        else
        {
            if (pBackground->GetColor() == COL_TRANSPARENT)
                pDev->SetFillColor();
            else
                pDev->SetFillColor(pBackground->GetColor());
            pDev->SetLineColor();
            pDev->DrawRect(aFrameRect);
        }
    }

    if ( pShadow && pShadow->GetLocation() != SvxShadowLocation::NONE )
    {
        pDev->SetFillColor(pShadow->GetColor());
        pDev->SetLineColor();
        tools::Long nShadowX = static_cast<tools::Long>( pShadow->GetWidth() * nScaleX );
        tools::Long nShadowY = static_cast<tools::Long>( pShadow->GetWidth() * nScaleY );
        switch (pShadow->GetLocation())
        {
            case SvxShadowLocation::TopLeft:
                pDev->DrawRect( tools::Rectangle(
                        aFrameRect.Left()-nShadowX, aFrameRect.Top()-nShadowY,
                        aFrameRect.Right()-nShadowX, aFrameRect.Top() ) );
                pDev->DrawRect( tools::Rectangle(
                        aFrameRect.Left()-nShadowX, aFrameRect.Top()-nShadowY,
                        aFrameRect.Left(), aFrameRect.Bottom()-nShadowY ) );
                break;
            case SvxShadowLocation::TopRight:
                pDev->DrawRect( tools::Rectangle(
                        aFrameRect.Left()+nShadowX, aFrameRect.Top()-nShadowY,
                        aFrameRect.Right()+nShadowX, aFrameRect.Top() ) );
                pDev->DrawRect( tools::Rectangle(
                        aFrameRect.Right(), aFrameRect.Top()-nShadowY,
                        aFrameRect.Right()+nShadowX, aFrameRect.Bottom()-nShadowY ) );
                break;
            case SvxShadowLocation::BottomLeft:
                pDev->DrawRect( tools::Rectangle(
                        aFrameRect.Left()-nShadowX, aFrameRect.Bottom(),
                        aFrameRect.Right()-nShadowX, aFrameRect.Bottom()+nShadowY ) );
                pDev->DrawRect( tools::Rectangle(
                        aFrameRect.Left()-nShadowX, aFrameRect.Top()+nShadowY,
                        aFrameRect.Left(), aFrameRect.Bottom()+nShadowY ) );
                break;
            case SvxShadowLocation::BottomRight:
                pDev->DrawRect( tools::Rectangle(
                        aFrameRect.Left()+nShadowX, aFrameRect.Bottom(),
                        aFrameRect.Right()+nShadowX, aFrameRect.Bottom()+nShadowY ) );
                pDev->DrawRect( tools::Rectangle(
                        aFrameRect.Right(), aFrameRect.Top()+nShadowY,
                        aFrameRect.Right()+nShadowX, aFrameRect.Bottom()+nShadowY ) );
                break;
            default:
            {
                // added to avoid warnings
            }
        }
    }

    if (!pBorderData)
        return;

    ScDocumentUniquePtr pBorderDoc(new ScDocument( SCDOCMODE_UNDO ));
    pBorderDoc->InitUndo( rDoc, 0,0, true,true );
    pBorderDoc->ApplyAttr( 0,0,0, *pBorderData );

    ScTableInfo aTabInfo(0, 1, false);
    pBorderDoc->FillInfo( aTabInfo, 0,0, 0,0, 0,
                                        nScaleX, nScaleY, falsefalse );
    OSL_ENSURE(aTabInfo.mnArrCount,"nArrCount == 0");

    aTabInfo.mpRowInfo[1].nHeight = static_cast<sal_uInt16>(nEffHeight);
    aTabInfo.mpRowInfo[0].basicCellInfo(0).nWidth =
        aTabInfo.mpRowInfo[1].basicCellInfo(0).nWidth = static_cast<sal_uInt16>(nEffWidth);

    ScOutputData aOutputData( pDev, OUTTYPE_PRINTER, aTabInfo, pBorderDoc.get(), 0,
                                nScrX+nLeft, nScrY+nTop, 0,0, 0,0, nScaleX, nScaleY );
    aOutputData.SetUseStyleColor( bUseStyleColor );

    aOutputData.DrawFrame(*pDev);
}

void ScPrintFunc::PrintColHdr( SCCOL nX1, SCCOL nX2, tools::Long nScrX, tools::Long nScrY )
{
    bool bLayoutRTL = rDoc.IsLayoutRTL( nPrintTab );
    tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;

    Size aOnePixel = pDev->PixelToLogic(Size(1,1));
    tools::Long nOneX = aOnePixel.Width();
    tools::Long nOneY = aOnePixel.Height();
    SCCOL nCol;

    tools::Long nHeight = static_cast<tools::Long>(PRINT_HEADER_HEIGHT * nScaleY);
    tools::Long nEndY = nScrY + nHeight - nOneY;

    tools::Long nPosX = nScrX;
    if ( bLayoutRTL )
    {
        for (nCol=nX1; nCol<=nX2; nCol++)
            nPosX += static_cast<tools::Long>( rDoc.GetColWidth( nCol, nPrintTab ) * nScaleX );
    }
    else
        nPosX -= nOneX;
    tools::Long nPosY = nScrY - nOneY;
    OUString aText;

    for (nCol=nX1; nCol<=nX2; nCol++)
    {
        sal_uInt16 nDocW = rDoc.GetColWidth( nCol, nPrintTab );
        if (nDocW)
        {
            tools::Long nWidth = static_cast<tools::Long>(nDocW * nScaleX);
            tools::Long nEndX = nPosX + nWidth * nLayoutSign;

            pDev->DrawRect( tools::Rectangle( nPosX,nPosY,nEndX,nEndY ) );

            aText = ::ScColToAlpha( nCol);
            tools::Long nTextWidth = pDev->GetTextWidth(aText);
            tools::Long nTextHeight = pDev->GetTextHeight();
            tools::Long nAddX = ( nWidth  - nTextWidth  ) / 2;
            tools::Long nAddY = ( nHeight - nTextHeight ) / 2;
            tools::Long nTextPosX = nPosX+nAddX;
            if ( bLayoutRTL )
                nTextPosX -= nWidth;
            pDev->DrawText( Point( nTextPosX,nPosY+nAddY ), aText );

            nPosX = nEndX;
        }
    }
}

void ScPrintFunc::PrintRowHdr( SCROW nY1, SCROW nY2, tools::Long nScrX, tools::Long nScrY )
{
    Size aOnePixel = pDev->PixelToLogic(Size(1,1));
    tools::Long nOneX = aOnePixel.Width();
    tools::Long nOneY = aOnePixel.Height();

    bool bLayoutRTL = rDoc.IsLayoutRTL( nPrintTab );

    tools::Long nWidth = static_cast<tools::Long>(PRINT_HEADER_WIDTH * nScaleX);
    tools::Long nEndX = nScrX + nWidth;
    tools::Long nPosX = nScrX;
    if ( !bLayoutRTL )
    {
        nEndX -= nOneX;
        nPosX -= nOneX;
    }
    tools::Long nPosY = nScrY - nOneY;
    OUString aText;

    for (SCROW nRow=nY1; nRow<=nY2; nRow++)
    {
        sal_uInt16 nDocH = rDoc.GetRowHeight( nRow, nPrintTab );
        if (nDocH)
        {
            tools::Long nHeight = static_cast<tools::Long>(nDocH * nScaleY);
            tools::Long nEndY = nPosY + nHeight;

            pDev->DrawRect( tools::Rectangle( nPosX,nPosY,nEndX,nEndY ) );

            aText = OUString::number( nRow+1 );
            tools::Long nTextWidth = pDev->GetTextWidth(aText);
            tools::Long nTextHeight = pDev->GetTextHeight();
            tools::Long nAddX = ( nWidth  - nTextWidth  ) / 2;
            tools::Long nAddY = ( nHeight - nTextHeight ) / 2;
            pDev->DrawText( Point( nPosX+nAddX,nPosY+nAddY ), aText );

            nPosY = nEndY;
        }
    }
}

void ScPrintFunc::LocateColHdr( SCCOL nX1, SCCOL nX2, tools::Long nScrX, tools::Long nScrY,
                                bool bRepCol, ScPreviewLocationData& rLocationData )
{
    Size aOnePixel = pDev->PixelToLogic(Size(1,1));
    tools::Long nOneX = aOnePixel.Width();
    tools::Long nOneY = aOnePixel.Height();

    tools::Long nHeight = static_cast<tools::Long>(PRINT_HEADER_HEIGHT * nScaleY);
    tools::Long nEndY = nScrY + nHeight - nOneY;

    tools::Long nPosX = nScrX - nOneX;
    for (SCCOL nCol=nX1; nCol<=nX2; nCol++)
    {
        sal_uInt16 nDocW = rDoc.GetColWidth( nCol, nPrintTab );
        if (nDocW)
            nPosX += static_cast<tools::Long>(nDocW * nScaleX);
    }
    tools::Rectangle aCellRect( nScrX, nScrY, nPosX, nEndY );
    rLocationData.AddColHeaders( aCellRect, nX1, nX2, bRepCol );
}

void ScPrintFunc::LocateRowHdr( SCROW nY1, SCROW nY2, tools::Long nScrX, tools::Long nScrY,
                                bool bRepRow, ScPreviewLocationData& rLocationData )
{
    Size aOnePixel = pDev->PixelToLogic(Size(1,1));
    tools::Long nOneX = aOnePixel.Width();
    tools::Long nOneY = aOnePixel.Height();

    bool bLayoutRTL = rDoc.IsLayoutRTL( nPrintTab );

    tools::Long nWidth = static_cast<tools::Long>(PRINT_HEADER_WIDTH * nScaleX);
    tools::Long nEndX = nScrX + nWidth;
    if ( !bLayoutRTL )
        nEndX -= nOneX;

    tools::Long nPosY = nScrY - nOneY;
    nPosY += rDoc.GetScaledRowHeight( nY1, nY2, nPrintTab, nScaleY);
    tools::Rectangle aCellRect( nScrX, nScrY, nEndX, nPosY );
    rLocationData.AddRowHeaders( aCellRect, nY1, nY2, bRepRow );
}

void ScPrintFunc::LocateArea( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
                                tools::Long nScrX, tools::Long nScrY, bool bRepCol, bool bRepRow,
                                ScPreviewLocationData& rLocationData )
{
    //  get MapMode for drawing objects (same MapMode as in ScOutputData::PrintDrawingLayer)

    Point aLogPos = OutputDevice::LogicToLogic(Point(nScrX,nScrY), aOffsetMode, aLogicMode);
    tools::Long nLogStX = aLogPos.X();
    tools::Long nLogStY = aLogPos.Y();

    SCCOL nCol;
    Point aTwipOffset;
    for (nCol=0; nCol<nX1; nCol++)
        aTwipOffset.AdjustX( -(rDoc.GetColWidth( nCol, nPrintTab )) );
    aTwipOffset.AdjustY( -sal_Int32(rDoc.GetRowHeight( 0, nY1-1, nPrintTab )) );

    Point aMMOffset(o3tl::convert(aTwipOffset, o3tl::Length::twip, o3tl::Length::mm100));
    aMMOffset += Point( nLogStX, nLogStY );
    MapMode aDrawMapMode( MapUnit::Map100thMM, aMMOffset, aLogicMode.GetScaleX(), aLogicMode.GetScaleY() );

    //  get pixel rectangle

    Size aOnePixel = pDev->PixelToLogic(Size(1,1));
    tools::Long nOneX = aOnePixel.Width();
    tools::Long nOneY = aOnePixel.Height();

    tools::Long nPosX = nScrX - nOneX;
    for (nCol=nX1; nCol<=nX2; nCol++)
    {
        sal_uInt16 nDocW = rDoc.GetColWidth( nCol, nPrintTab );
        if (nDocW)
            nPosX += static_cast<tools::Long>(nDocW * nScaleX);
    }

    tools::Long nPosY = nScrY - nOneY;
    nPosY += rDoc.GetScaledRowHeight( nY1, nY2, nPrintTab, nScaleY);
    tools::Rectangle aCellRect( nScrX, nScrY, nPosX, nPosY );
    rLocationData.AddCellRange( aCellRect, ScRange( nX1,nY1,nPrintTab, nX2,nY2,nPrintTab ),
                                bRepCol, bRepRow, aDrawMapMode );
}

void ScPrintFunc::PrintArea( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
                                tools::Long nScrX, tools::Long nScrY,
                                bool bShLeft, bool bShTop, bool bShRight, bool bShBottom )
{
    // #i47547# nothing to do if the end of the print area is before the end of
    // the repeat columns/rows (don't use negative size for ScOutputData)
    if ( nX2 < nX1 || nY2 < nY1 )
        return;

                            //!     hand over Flag at FillInfo  !!!!!
    ScRange aERange;
    bool bEmbed = rDoc.IsEmbedded();
    if (bEmbed)
    {
        rDoc.GetEmbedded(aERange);
        rDoc.ResetEmbedded();
    }

    Point aPos = OutputDevice::LogicToLogic(Point(nScrX,nScrY), aOffsetMode, aLogicMode);
    tools::Long nLogStX = aPos.X();
    tools::Long nLogStY = aPos.Y();

                    //  Assemble data

    ScTableInfo aTabInfo(nY1, nY2, true);
    rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nPrintTab,
                                        nScaleX, nScaleY, true, aTableParam.bFormulas );
    lcl_HidePrint( aTabInfo, nX1, nX2 );

    if (bEmbed)
        rDoc.SetEmbedded(aERange);

    ScOutputData aOutputData( pDev, OUTTYPE_PRINTER, aTabInfo, &rDoc, nPrintTab,
                                nScrX, nScrY, nX1, nY1, nX2, nY2, nScaleX, nScaleY );

    vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(pDev->GetExtOutDevData());
    bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
    if (bTaggedPDF)
    {
        bool bReopen = aOutputData.ReopenPDFStructureElement(vcl::pdf::StructElement::Part);
        if (!bReopen)
        {
            sal_Int32 nId = pPDF->EnsureStructureElement(nullptr);
            pPDF->InitStructureElement(nId, vcl::pdf::StructElement::Part, u"Worksheet"_ustr);
            pPDF->BeginStructureElement(nId);
            pPDF->GetScPDFState()->m_WorksheetId = nId;
        }
    }

    aOutputData.SetDrawView( pDrawView );

    // test if all paint parts are hidden, then a paint is not necessary at all
    const Point aMMOffset(aOutputData.PrePrintDrawingLayer(nLogStX, nLogStY));
    const bool bHideAllDrawingLayer( pDrawView && pDrawView->getHideOle() && pDrawView->getHideChart()
            && pDrawView->getHideDraw() && pDrawView->getHideFormControl() );

    if(!bHideAllDrawingLayer)
    {
        pDev->SetMapMode(aLogicMode);
        //  don's set Clipping here (Mapmode is being moved)

        // #i72502#
        aOutputData.PrintDrawingLayer(SC_LAYER_BACK, aMMOffset);
    }

    pDev->SetMapMode(aOffsetMode);

    aOutputData.SetShowFormulas( aTableParam.bFormulas );
    aOutputData.SetShowNullValues( aTableParam.bNullVals );
    aOutputData.SetUseStyleColor( bUseStyleColor );

    Color aGridColor( COL_BLACK );
    if ( bUseStyleColor )
        aGridColor = ScModule::get()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
    aOutputData.SetGridColor( aGridColor );

    if ( !pPrinter )
    {
        OutputDevice* pRefDev = rDoc.GetPrinter();     // use the printer also for Preview
        Fraction aPrintFrac( nZoom, 100 );              // without nManualZoom
        //  MapMode, as it would arrive at the printer:
        pRefDev->SetMapMode( MapMode( MapUnit::Map100thMM, Point(), aPrintFrac, aPrintFrac ) );

        //  when rendering (PDF), don't use printer as ref device, but printer's MapMode
        //  has to be set anyway, as charts still use it (#106409#)
        if ( !bIsRender )
            aOutputData.SetRefDevice( pRefDev );
    }

    if( aTableParam.bCellContent )
        aOutputData.DrawBackground(*pDev);

    pDev->SetClipRegion(vcl::Region(tools::Rectangle(
                aPos, Size(aOutputData.GetScrW(), aOutputData.GetScrH()))));
    pDev->SetClipRegion();

    if( aTableParam.bCellContent )
    {
        aOutputData.DrawExtraShadow( bShLeft, bShTop, bShRight, bShBottom );
        aOutputData.DrawFrame(*pDev);
        aOutputData.DrawSparklines(*pDev);
        aOutputData.DrawStrings();
        aOutputData.DrawEdit(false);
    }

    if (aTableParam.bGrid)
        aOutputData.DrawGrid(*pDev, truefalse);    // no page breaks

    aOutputData.AddPDFNotes();      // has no effect if not rendering PDF with notes enabled

    // test if all paint parts are hidden, then a paint is not necessary at all
    if(!bHideAllDrawingLayer)
    {
        // #i72502#
        aOutputData.PrintDrawingLayer(SC_LAYER_FRONT, aMMOffset);
    }

    if (bTaggedPDF)
    {
        aOutputData.PrintDrawingLayer(SC_LAYER_CONTROLS, aMMOffset);
        pPDF->EndStructureElement();
    }

    // #i72502#
    aOutputData.PrintDrawingLayer(SC_LAYER_INTERN, aMMOffset);

    if (!bTaggedPDF)
        aOutputData.PostPrintDrawingLayer(aMMOffset); // #i74768#
}

bool ScPrintFunc::IsMirror( tools::Long nPageNo )          // Mirror margins?
{
    return nPageUsage == SvxPageUsage::Mirror && (nPageNo & 1);
}

bool ScPrintFunc::IsLeft( tools::Long nPageNo )            // left foot notes?
{
    bool bLeft;
    if (nPageUsage == SvxPageUsage::Left)
        bLeft = true;
    else if (nPageUsage == SvxPageUsage::Right)
        bLeft = false;
    else
        bLeft = (nPageNo & 1) != 0;
    return bLeft;
}

void ScPrintFunc::MakeTableString()
{
    OUString aTmp;
    rDoc.GetName(nPrintTab, aTmp);
    aFieldData.aTabName = aTmp;
}

void ScPrintFunc::MakeEditEngine()
{
    if (!pEditEngine)
    {
        //  can't use document's edit engine pool here,
        //  because pool must have twips as default metric
        pEditEngine.reset( new ScHeaderEditEngine( EditEngine::CreatePool().get() ) );

        pEditEngine->EnableUndo(false);
        //fdo#45869 we want text to be positioned as it would be for the
        //high dpi printed output, not as would be ideal for the 96dpi preview
        //window itself
        pEditEngine->SetRefDevice(pPrinter ? pPrinter : rDoc.GetRefDevice());
        pEditEngine->SetWordDelimiters(
                ScEditUtil::ModifyDelimiters( pEditEngine->GetWordDelimiters() ) );
        pEditEngine->SetControlWord( pEditEngine->GetControlWord() & ~EEControlBits::RTFSTYLESHEETS );
        rDoc.ApplyAsianEditSettings( *pEditEngine );
        pEditEngine->EnableAutoColor( bUseStyleColor );

        //  Default-Set for alignment
        pEditDefaults.reset( new SfxItemSet( pEditEngine->GetEmptyItemSet() ) );

        const ScPatternAttr& rPattern(rDoc.getCellAttributeHelper().getDefaultCellAttribute());

        rPattern.FillEditItemSet( pEditDefaults.get() );
        //  FillEditItemSet adjusts font height to 1/100th mm,
        //  but for header/footer twips is needed, as in the PatternAttr:
        pEditDefaults->Put( rPattern.GetItem(ATTR_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT) );
        pEditDefaults->Put( rPattern.GetItem(ATTR_CJK_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK) );
        pEditDefaults->Put( rPattern.GetItem(ATTR_CTL_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL) );
        //  don't use font color, because background color is not used
        //! there's no way to set the background for note pages
        pEditDefaults->ClearItem( EE_CHAR_COLOR );
        if (ScGlobal::IsSystemRTL())
            pEditDefaults->Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_RL_TB, EE_PARA_WRITINGDIR ) );
    }

    pEditEngine->SetData( aFieldData );     // Set page count etc.
}

//  nStartY = logic
void ScPrintFunc::PrintHF( tools::Long nPageNo, bool bHeader, tools::Long nStartY,
                            bool bDoPrint, ScPreviewLocationData* pLocationData )
{
    vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(pDev->GetExtOutDevData());
    bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
    if (bTaggedPDF)
        pPDF->WrapBeginStructureElement(vcl::pdf::StructElement::NonStructElement);

    const ScPrintHFParam& rParam = bHeader ? aHdr : aFtr;

    pDev->SetMapMode( aTwipMode );          // Head-/Footlines in Twips

    bool bFirst = 0 == nPageNo && !rParam.bSharedFirst;
    bool bLeft = IsLeft(nPageNo) && !rParam.bShared;
    const ScPageHFItem* pHFItem = bFirst ? rParam.pFirst : (bLeft ? rParam.pLeft : rParam.pRight);

    tools::Long nLineStartX = aPageRect.Left()  + rParam.nLeft;
    tools::Long nLineEndX   = aPageRect.Right() - rParam.nRight;
    tools::Long nLineWidth  = nLineEndX - nLineStartX + 1;

    //  Edit-Engine

    Point aStart( nLineStartX, nStartY );
    Size aPaperSize( nLineWidth, rParam.nHeight-rParam.nDistance );
    if ( rParam.pBorder )
    {
        tools::Long nLeft = lcl_LineTotal( rParam.pBorder->GetLeft() ) + rParam.pBorder->GetDistance(SvxBoxItemLine::LEFT);
        tools::Long nTop = lcl_LineTotal( rParam.pBorder->GetTop() ) + rParam.pBorder->GetDistance(SvxBoxItemLine::TOP);
        aStart.AdjustX(nLeft );
        aStart.AdjustY(nTop );
        aPaperSize.AdjustWidth( -(nLeft + lcl_LineTotal( rParam.pBorder->GetRight() ) + rParam.pBorder->GetDistance(SvxBoxItemLine::RIGHT)) );
        aPaperSize.AdjustHeight( -(nTop + lcl_LineTotal( rParam.pBorder->GetBottom() ) + rParam.pBorder->GetDistance(SvxBoxItemLine::BOTTOM)) );
    }

    if ( rParam.pShadow && rParam.pShadow->GetLocation() != SvxShadowLocation::NONE )
    {
        tools::Long nLeft  = rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::LEFT);
        tools::Long nTop   = rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::TOP);
        aStart.AdjustX(nLeft );
        aStart.AdjustY(nTop );
        aPaperSize.AdjustWidth( -(nLeft + rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::RIGHT)) );
        aPaperSize.AdjustHeight( -(nTop + rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::BOTTOM)) );
    }

    aFieldData.nPageNo = nPageNo+aTableParam.nFirstPageNo;
    MakeEditEngine();

    pEditEngine->SetPaperSize(aPaperSize);

    //  Frame / Background

    Point aBorderStart( nLineStartX, nStartY );
    Size aBorderSize( nLineWidth, rParam.nHeight-rParam.nDistance );
    if ( rParam.bDynamic )
    {
        //  adjust here again, for even/odd head-/footlines
        //  and probably other breaks by variable (page number etc.)

        tools::Long nMaxHeight = 0;
        nMaxHeight = std::max( nMaxHeight, TextHeight( pHFItem->GetLeftArea() ) );
        nMaxHeight = std::max( nMaxHeight, TextHeight( pHFItem->GetCenterArea() ) );
        nMaxHeight = std::max( nMaxHeight, TextHeight( pHFItem->GetRightArea() ) );
        if (rParam.pBorder)
            nMaxHeight += lcl_LineTotal( rParam.pBorder->GetTop() ) +
                          lcl_LineTotal( rParam.pBorder->GetBottom() ) +
                                    rParam.pBorder->GetDistance(SvxBoxItemLine::TOP) +
                                    rParam.pBorder->GetDistance(SvxBoxItemLine::BOTTOM);
        if (rParam.pShadow && rParam.pShadow->GetLocation() != SvxShadowLocation::NONE)
            nMaxHeight += rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::TOP) +
                          rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::BOTTOM);

        if (nMaxHeight < rParam.nManHeight-rParam.nDistance)
            nMaxHeight = rParam.nManHeight-rParam.nDistance;        // configured Minimum

        aBorderSize.setHeight( nMaxHeight );
    }

    if ( bDoPrint )
    {
        double nOldScaleX = nScaleX;
        double nOldScaleY = nScaleY;
        nScaleX = nScaleY = 1.0;            // output directly in Twips
        DrawBorder( aBorderStart.X(), aBorderStart.Y(), aBorderSize.Width(), aBorderSize.Height(),
                        rParam.pBorder, rParam.pBack, rParam.pShadow );
        nScaleX = nOldScaleX;
        nScaleY = nOldScaleY;

        //  Clipping for Text

        pDev->SetClipRegion(vcl::Region(tools::Rectangle(aStart, aPaperSize)));

        //  left

        const EditTextObject* pObject = pHFItem->GetLeftArea();
        if (pObject)
        {
            pEditDefaults->Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) );
            pEditEngine->SetTextTempDefaults(*pObject, *pEditDefaults);
            Point aDraw = aStart;
            tools::Long nDif = aPaperSize.Height() - static_cast<tools::Long>(pEditEngine->GetTextHeight());
            if (nDif > 0)
                aDraw.AdjustY(nDif / 2 );
            pEditEngine->Draw(*pDev, aDraw);
        }

        //  center

        pObject = pHFItem->GetCenterArea();
        if (pObject)
        {
            pEditDefaults->Put( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) );
            pEditEngine->SetTextTempDefaults(*pObject, *pEditDefaults);
            Point aDraw = aStart;
            tools::Long nDif = aPaperSize.Height() - static_cast<tools::Long>(pEditEngine->GetTextHeight());
            if (nDif > 0)
                aDraw.AdjustY(nDif / 2 );
            pEditEngine->Draw(*pDev, aDraw);
        }

        //  right

        pObject = pHFItem->GetRightArea();
        if (pObject)
        {
            pEditDefaults->Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) );
            pEditEngine->SetTextTempDefaults(*pObject, *pEditDefaults);
            Point aDraw = aStart;
            tools::Long nDif = aPaperSize.Height() - static_cast<tools::Long>(pEditEngine->GetTextHeight());
            if (nDif > 0)
                aDraw.AdjustY(nDif / 2 );
            pEditEngine->Draw(*pDev, aDraw);
        }

        pDev->SetClipRegion();
    }

    if ( pLocationData )
    {
        tools::Rectangle aHeaderRect( aBorderStart, aBorderSize );
        pLocationData->AddHeaderFooter( aHeaderRect, bHeader, bLeft );
    }

    if (bTaggedPDF)
        pPDF->EndStructureElement();
}

tools::Long ScPrintFunc::DoNotes( tools::Long nNoteStart, bool bDoPrint, ScPreviewLocationData* pLocationData )
{
    if (bDoPrint)
        pDev->SetMapMode(aTwipMode);

    MakeEditEngine();
    pEditDefaults->Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) );
    pEditEngine->SetDefaults( *pEditDefaults );

    vcl::Font aMarkFont;
    ScAutoFontColorMode eColorMode = bUseStyleColor ? ScAutoFontColorMode::Display : ScAutoFontColorMode::Print;
    rDoc.getCellAttributeHelper().getDefaultCellAttribute().fillFont(aMarkFont, eColorMode);
    pDev->SetFont(aMarkFont);
    tools::Long nMarkLen = pDev->GetTextWidth(u"GW99999:"_ustr);
    // without Space-Char, because it rarely arrives there

    Size aDataSize = aPageRect.GetSize();
    if ( nMarkLen > aDataSize.Width() / 2 )     // everything much too small?
        nMarkLen = aDataSize.Width() / 2;       // split the page appropriately
    aDataSize.AdjustWidth( -nMarkLen );

    pEditEngine->SetPaperSize( aDataSize );
    tools::Long nPosX = aPageRect.Left() + nMarkLen;
    tools::Long nPosY = aPageRect.Top();

    tools::Long nCount = 0;
    tools::Long nSize = aNotePosList.size();
    bool bOk;
    do
    {
        bOk = false;
        if ( nNoteStart + nCount < nSize)
        {
            ScAddress &rPos = aNotePosList[ nNoteStart + nCount ];

            ifconst ScPostIt* pNote = rDoc.GetNote( rPos ) )
            {
                if(const EditTextObject *pEditText = pNote->GetEditTextObject())
                    pEditEngine->SetTextCurrentDefaults(*pEditText);
                tools::Long nTextHeight = pEditEngine->GetTextHeight();
                if ( nPosY + nTextHeight < aPageRect.Bottom() )
                {
                    if (bDoPrint)
                    {
                        pEditEngine->Draw(*pDev, Point(nPosX, nPosY));

                        OUString aMarkStr(rPos.Format(ScRefFlags::VALID, &rDoc, rDoc.GetAddressConvention()) ":");

                        //  cell position also via EditEngine, for correct positioning
                        pEditEngine->SetTextCurrentDefaults(aMarkStr);
                        pEditEngine->Draw(*pDev, Point(aPageRect.Left(), nPosY));
                    }

                    if ( pLocationData )
                    {
                        tools::Rectangle aTextRect( Point( nPosX, nPosY ), Size( aDataSize.Width(), nTextHeight ) );
                        pLocationData->AddNoteText( aTextRect, rPos );
                        tools::Rectangle aMarkRect( Point( aPageRect.Left(), nPosY ), Size( nMarkLen, nTextHeight ) );
                        pLocationData->AddNoteMark( aMarkRect, rPos );
                    }

                    nPosY += nTextHeight;
                    nPosY += 200;                   // Distance
                    ++nCount;
                    bOk = true;
                }
            }
        }
    }
    while (bOk);

    return nCount;
}

tools::Long ScPrintFunc::PrintNotes( tools::Long nPageNo, tools::Long nNoteStart, bool bDoPrint, ScPreviewLocationData* pLocationData )
{
    if ( nNoteStart >= static_cast<tools::Long>(aNotePosList.size()) || !aTableParam.bNotes )
        return 0;

    if ( bDoPrint && bClearWin )
    {
        //!  aggregate PrintPage !!!

        Color aBackgroundColor( COL_WHITE );
        if ( bUseStyleColor )
            aBackgroundColor = ScModule::get()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;

        pDev->SetMapMode(aOffsetMode);
        pDev->SetLineColor();
        pDev->SetFillColor(aBackgroundColor);
        pDev->DrawRect(tools::Rectangle(Point(),
                Size(static_cast<tools::Long>(aPageSize.Width() * nScaleX * 100 / nZoom),
                     static_cast<tools::Long>(aPageSize.Height() * nScaleY * 100 / nZoom))));
    }

    //      adjust aPageRect for left/right page

    tools::Rectangle aTempRect( Point(), aPageSize );
    if (IsMirror(nPageNo))
    {
        aPageRect.SetLeft( ( aTempRect.Left()  + nRightMargin ) * 100 / nZoom );
        aPageRect.SetRight( ( aTempRect.Right() - nLeftMargin  ) * 100 / nZoom );
    }
    else
    {
        aPageRect.SetLeft( ( aTempRect.Left()  + nLeftMargin  ) * 100 / nZoom );
        aPageRect.SetRight( ( aTempRect.Right() - nRightMargin ) * 100 / nZoom );
    }

    if ( pPrinter && bDoPrint )
    {
        OSL_FAIL( "StartPage does not exist anymore" );
    }

    if ( bDoPrint || pLocationData )
    {
        //  Head and foot lines

        if (aHdr.bEnable)
        {
            tools::Long nHeaderY = aPageRect.Top()-aHdr.nHeight;
            PrintHF( nPageNo, true, nHeaderY, bDoPrint, pLocationData );
        }
        if (aFtr.bEnable)
        {
            tools::Long nFooterY = aPageRect.Bottom()+aFtr.nDistance;
            PrintHF( nPageNo, false, nFooterY, bDoPrint, pLocationData );
        }
    }

    tools::Long nCount = DoNotes( nNoteStart, bDoPrint, pLocationData );

    if ( pPrinter && bDoPrint )
    {
        OSL_FAIL( "EndPage does not exist anymore" );
    }

    return nCount;
}

void ScPrintFunc::PrintPage( tools::Long nPageNo, SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
                                bool bDoPrint, ScPreviewLocationData* pLocationData )
{
    bool bLayoutRTL = rDoc.IsLayoutRTL( nPrintTab );
    tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;

    //  nPageNo is the page number within all sheets of one "start page" setting

    if ( bClearWin && bDoPrint )
    {
        //  must exactly fit to painting the frame in preview.cxx !!!

        Color aBackgroundColor( COL_WHITE );
        if ( bUseStyleColor )
            aBackgroundColor = ScModule::get()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;

        pDev->SetMapMode(aOffsetMode);
        pDev->SetLineColor();
        pDev->SetFillColor(aBackgroundColor);
        pDev->DrawRect(tools::Rectangle(Point(),
                Size(static_cast<tools::Long>(aPageSize.Width() * nScaleX * 100 / nZoom),
                     static_cast<tools::Long>(aPageSize.Height() * nScaleY * 100 / nZoom))));
    }

    //      adjust aPageRect for left/right page

    tools::Rectangle aTempRect( Point(), aPageSize );
    if (IsMirror(nPageNo))
    {
        aPageRect.SetLeft( ( aTempRect.Left()  + nRightMargin ) * 100 / nZoom );
        aPageRect.SetRight( ( aTempRect.Right() - nLeftMargin  ) * 100 / nZoom );
    }
    else
    {
        aPageRect.SetLeft( ( aTempRect.Left()  + nLeftMargin  ) * 100 / nZoom );
        aPageRect.SetRight( ( aTempRect.Right() - nRightMargin ) * 100 / nZoom );
    }

    if ( aAreaParam.bRepeatCol )
        if ( nX1 > nRepeatStartCol && nX1 <= nRepeatEndCol )
            nX1 = nRepeatEndCol + 1;
    bool bDoRepCol = (aAreaParam.bRepeatCol && nX1 > nRepeatEndCol);
    if ( aAreaParam.bRepeatRow )
        if ( nY1 > nRepeatStartRow && nY1 <= nRepeatEndRow )
            nY1 = nRepeatEndRow + 1;
    bool bDoRepRow = (aAreaParam.bRepeatRow && nY1 > nRepeatEndRow);

    // use new object hide flags in SdrPaintView
    if(pDrawView)
    {
        pDrawView->setHideOle(!aTableParam.bObjects);
        pDrawView->setHideChart(!aTableParam.bCharts);
        pDrawView->setHideDraw(!aTableParam.bDrawings);
        pDrawView->setHideFormControl(!aTableParam.bDrawings);
    }

    if ( pPrinter && bDoPrint )
    {
        OSL_FAIL( "StartPage does not exist anymore" );
    }

    //  head and foot lines (without centering)

    if (aHdr.bEnable)
    {
        tools::Long nHeaderY = aPageRect.Top()-aHdr.nHeight;
        PrintHF( nPageNo, true, nHeaderY, bDoPrint, pLocationData );
    }
    if (aFtr.bEnable)
    {
        tools::Long nFooterY = aPageRect.Bottom()+aFtr.nDistance;
        PrintHF( nPageNo, false, nFooterY, bDoPrint, pLocationData );
    }

    //  Position ( margins / centering )

    tools::Long nLeftSpace = aPageRect.Left();     // Document-Twips
    tools::Long nTopSpace  = aPageRect.Top();
    if ( bCenterHor || bLayoutRTL )
    {
        tools::Long nDataWidth = 0;
        SCCOL i;
        for (i=nX1; i<=nX2; i++)
            nDataWidth += rDoc.GetColWidth( i,nPrintTab );
        if (bDoRepCol)
            for (i=nRepeatStartCol; i<=nRepeatEndCol; i++)
                nDataWidth += rDoc.GetColWidth( i,nPrintTab );
        if (aTableParam.bHeaders)
            nDataWidth += tools::Long(PRINT_HEADER_WIDTH);
        if (pBorderItem)
            nDataWidth += pBorderItem->GetDistance(SvxBoxItemLine::LEFT) +
                           pBorderItem->GetDistance(SvxBoxItemLine::RIGHT);        //! Line width?
        if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
            nDataWidth += pShadowItem->CalcShadowSpace(SvxShadowItemSide::LEFT) +
                           pShadowItem->CalcShadowSpace(SvxShadowItemSide::RIGHT);
        if ( bCenterHor )
        {
            nLeftSpace += ( aPageRect.GetWidth() - nDataWidth ) / 2;        // LTR or RTL
            if (pBorderItem)
                nLeftSpace -= lcl_LineTotal(pBorderItem->GetLeft());
        }
        else if ( bLayoutRTL )
            nLeftSpace += aPageRect.GetWidth() - nDataWidth;                // align to the right edge of the page
    }
    if ( bCenterVer )
    {
        tools::Long nDataHeight = rDoc.GetRowHeight( nY1, nY2, nPrintTab);
        if (bDoRepRow)
            nDataHeight += rDoc.GetRowHeight( nRepeatStartRow,
                    nRepeatEndRow, nPrintTab);
        if (aTableParam.bHeaders)
            nDataHeight += tools::Long(PRINT_HEADER_HEIGHT);
        if (pBorderItem)
            nDataHeight += pBorderItem->GetDistance(SvxBoxItemLine::TOP) +
                           pBorderItem->GetDistance(SvxBoxItemLine::BOTTOM);       //! Line width?
        if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
            nDataHeight += pShadowItem->CalcShadowSpace(SvxShadowItemSide::TOP) +
                           pShadowItem->CalcShadowSpace(SvxShadowItemSide::BOTTOM);
        nTopSpace += ( aPageRect.GetHeight() - nDataHeight ) / 2;
        if (pBorderItem)
            nTopSpace -= lcl_LineTotal(pBorderItem->GetTop());
    }

    //  calculate sizes of the elements for partitioning
    //  (header, repeat, data)

    tools::Long nHeaderWidth   = 0;
    tools::Long nHeaderHeight  = 0;
    tools::Long nRepeatWidth   = 0;
    tools::Long nRepeatHeight  = 0;
    tools::Long nContentWidth  = 0;        // scaled - not the same as nDataWidth above
    tools::Long nContentHeight = 0;
    if (aTableParam.bHeaders)
    {
        nHeaderWidth  = static_cast<tools::Long>(PRINT_HEADER_WIDTH * nScaleX);
        nHeaderHeight = static_cast<tools::Long>(PRINT_HEADER_HEIGHT * nScaleY);
    }
    if (bDoRepCol)
        for (SCCOL i=nRepeatStartCol; i<=nRepeatEndCol; i++)
            nRepeatWidth += static_cast<tools::Long>(rDoc.GetColWidth(i,nPrintTab) * nScaleX);
    if (bDoRepRow)
        nRepeatHeight += rDoc.GetScaledRowHeight( nRepeatStartRow,
                nRepeatEndRow, nPrintTab, nScaleY);
    for (SCCOL i=nX1; i<=nX2; i++)
        nContentWidth += static_cast<tools::Long>(rDoc.GetColWidth(i,nPrintTab) * nScaleX);
    nContentHeight += rDoc.GetScaledRowHeight( nY1, nY2, nPrintTab,
            nScaleY);

    //  partition the page

    tools::Long nStartX = static_cast<tools::Long>( nLeftSpace * nScaleX );
    tools::Long nStartY = static_cast<tools::Long>( nTopSpace  * nScaleY );
    tools::Long nInnerStartX = nStartX;
    tools::Long nInnerStartY = nStartY;
    if (pBorderItem)
    {
        nInnerStartX += static_cast<tools::Long>( ( lcl_LineTotal(pBorderItem->GetLeft()) +
                                    pBorderItem->GetDistance(SvxBoxItemLine::LEFT) ) * nScaleX );
        nInnerStartY += static_cast<tools::Long>( ( lcl_LineTotal(pBorderItem->GetTop()) +
                                    pBorderItem->GetDistance(SvxBoxItemLine::TOP) ) * nScaleY );
    }
    if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
    {
        nInnerStartX += static_cast<tools::Long>( pShadowItem->CalcShadowSpace(SvxShadowItemSide::LEFT) * nScaleX );
        nInnerStartY += static_cast<tools::Long>( pShadowItem->CalcShadowSpace(SvxShadowItemSide::TOP) * nScaleY );
    }

    if ( bLayoutRTL )
    {
        //  arrange elements starting from the right edge
        nInnerStartX += nHeaderWidth + nRepeatWidth + nContentWidth;

        //  make rounding easier so the elements are really next to each other in preview
        Size aOffsetOnePixel = pDev->PixelToLogic( Size(1,1), aOffsetMode );
        tools::Long nOffsetOneX = aOffsetOnePixel.Width();
        nInnerStartX += nOffsetOneX / 2;
    }

    tools::Long nFrameStartX = nInnerStartX;
    tools::Long nFrameStartY = nInnerStartY;

    tools::Long nRepStartX = nInnerStartX + nHeaderWidth * nLayoutSign;    // widths/heights are 0 if not used
    tools::Long nRepStartY = nInnerStartY + nHeaderHeight;
    tools::Long nDataX = nRepStartX + nRepeatWidth * nLayoutSign;
    tools::Long nDataY = nRepStartY + nRepeatHeight;
    tools::Long nEndX = nDataX + nContentWidth * nLayoutSign;
    tools::Long nEndY = nDataY + nContentHeight;
    tools::Long nFrameEndX = nEndX;
    tools::Long nFrameEndY = nEndY;

    if ( bLayoutRTL )
    {
        //  each element's start position is its left edge
        //! subtract one pixel less?
        nInnerStartX -= nHeaderWidth;       // used for header
        nRepStartX   -= nRepeatWidth;
        nDataX       -= nContentWidth;

        //  continue right of the main elements again
        nEndX += nHeaderWidth + nRepeatWidth + nContentWidth;
    }

    //  Page frame / background

    //! adjust nEndX/Y

    tools::Long nBorderEndX = nEndX;
    tools::Long nBorderEndY = nEndY;
    if (pBorderItem)
    {
        nBorderEndX += static_cast<tools::Long>( ( lcl_LineTotal(pBorderItem->GetRight()) +
                                    pBorderItem->GetDistance(SvxBoxItemLine::RIGHT) ) * nScaleX );
        nBorderEndY += static_cast<tools::Long>( ( lcl_LineTotal(pBorderItem->GetBottom()) +
                                    pBorderItem->GetDistance(SvxBoxItemLine::BOTTOM) ) * nScaleY );
    }
    if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
    {
        nBorderEndX += static_cast<tools::Long>( pShadowItem->CalcShadowSpace(SvxShadowItemSide::RIGHT) * nScaleX );
        nBorderEndY += static_cast<tools::Long>( pShadowItem->CalcShadowSpace(SvxShadowItemSide::BOTTOM) * nScaleY );
    }

    if ( bDoPrint )
    {
        pDev->SetMapMode( aOffsetMode );
        DrawBorder( nStartX, nStartY, nBorderEndX-nStartX, nBorderEndY-nStartY,
                        pBorderItem, pBackgroundItem, pShadowItem );

        pDev->SetMapMode( aTwipMode );
    }

    pDev->SetMapMode( aOffsetMode );

    //  Output repeating rows/columns

    if (bDoRepCol && bDoRepRow)
    {
        if ( bDoPrint )
            PrintArea( nRepeatStartCol,nRepeatStartRow, nRepeatEndCol,nRepeatEndRow,
                            nRepStartX,nRepStartY, truetruefalsefalse );
        if ( pLocationData )
            LocateArea( nRepeatStartCol,nRepeatStartRow, nRepeatEndCol,nRepeatEndRow,
                            nRepStartX,nRepStartY, truetrue, *pLocationData );
    }
    if (bDoRepCol)
    {
        if ( bDoPrint )
            PrintArea( nRepeatStartCol,nY1, nRepeatEndCol,nY2, nRepStartX,nDataY,
                        true, !bDoRepRow, falsetrue );
        if ( pLocationData )
            LocateArea( nRepeatStartCol,nY1, nRepeatEndCol,nY2, nRepStartX,nDataY, truefalse, *pLocationData );
    }
    if (bDoRepRow)
    {
        if ( bDoPrint )
            PrintArea( nX1,nRepeatStartRow, nX2,nRepeatEndRow, nDataX,nRepStartY,
                        !bDoRepCol, truetruefalse );
        if ( pLocationData )
            LocateArea( nX1,nRepeatStartRow, nX2,nRepeatEndRow, nDataX,nRepStartY, falsetrue, *pLocationData );
    }

    //  output data

    if ( bDoPrint )
        PrintArea( nX1,nY1, nX2,nY2, nDataX,nDataY, !bDoRepCol,!bDoRepRow, truetrue );
    if ( pLocationData )
        LocateArea( nX1,nY1, nX2,nY2, nDataX,nDataY, false,false, *pLocationData );

    //  output column/row headers
    //  after data (through probably shadow)

    Color aGridColor( COL_BLACK );
    if ( bUseStyleColor )
        aGridColor = ScModule::get()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;

    if (aTableParam.bHeaders)
    {
        if ( bDoPrint )
        {
            pDev->SetLineColor( aGridColor );
            pDev->SetFillColor();
            pDev->SetMapMode(aOffsetMode);
        }

        ScPatternAttr aPattern(rDoc.getCellAttributeHelper());
        vcl::Font aFont;
        ScAutoFontColorMode eColorMode = bUseStyleColor ? ScAutoFontColorMode::Display : ScAutoFontColorMode::Print;
        aPattern.fillFont(aFont, eColorMode, pDev);
        pDev->SetFont(aFont);

        if (bDoRepCol)
        {
            if ( bDoPrint )
                PrintColHdr( nRepeatStartCol,nRepeatEndCol, nRepStartX,nInnerStartY );
            if ( pLocationData )
                LocateColHdr( nRepeatStartCol,nRepeatEndCol, nRepStartX,nInnerStartY, true, *pLocationData );
        }
        if ( bDoPrint )
            PrintColHdr( nX1,nX2, nDataX,nInnerStartY );
        if ( pLocationData )
            LocateColHdr( nX1,nX2, nDataX,nInnerStartY, false, *pLocationData );
        if (bDoRepRow)
        {
            if ( bDoPrint )
                PrintRowHdr( nRepeatStartRow,nRepeatEndRow, nInnerStartX,nRepStartY );
            if ( pLocationData )
                LocateRowHdr( nRepeatStartRow,nRepeatEndRow, nInnerStartX,nRepStartY, true, *pLocationData );
        }
        if ( bDoPrint )
            PrintRowHdr( nY1,nY2, nInnerStartX,nDataY );
        if ( pLocationData )
            LocateRowHdr( nY1,nY2, nInnerStartX,nDataY, false, *pLocationData );
    }

    //  simple frame

    if ( bDoPrint && ( aTableParam.bGrid || aTableParam.bHeaders ) )
    {
        Size aOnePixel = pDev->PixelToLogic(Size(1,1));
        tools::Long nOneX = aOnePixel.Width();
        tools::Long nOneY = aOnePixel.Height();

        tools::Long nLeftX   = nFrameStartX;
        tools::Long nTopY    = nFrameStartY - nOneY;
        tools::Long nRightX  = nFrameEndX;
        tools::Long nBottomY = nFrameEndY - nOneY;
        if ( !bLayoutRTL )
        {
            nLeftX   -= nOneX;
            nRightX  -= nOneX;
        }
        pDev->SetMapMode(aOffsetMode);
        pDev->SetLineColor( aGridColor );
        pDev->SetFillColor();
        pDev->DrawRect( tools::Rectangle( nLeftX, nTopY, nRightX, nBottomY ) );
        //  nEndX/Y without frame-adaptation
    }

    if ( pPrinter && bDoPrint )
    {
        OSL_FAIL( "EndPage does not exist anymore" );
    }

    aLastSourceRange = ScRange( nX1, nY1, nPrintTab, nX2, nY2, nPrintTab );
    bSourceRangeValid = true;
}

void ScPrintFunc::SetOffset( const Point& rOfs )
{
    aSrcOffset = rOfs;
}

void ScPrintFunc::SetManualZoom( sal_uInt16 nNewZoom )
{
    nManualZoom = nNewZoom;
}

void ScPrintFunc::SetClearFlag( bool bFlag )
{
    bClearWin = bFlag;
}

void ScPrintFunc::SetUseStyleColor( bool bFlag )
{
    bUseStyleColor = bFlag;
    if (pEditEngine)
        pEditEngine->EnableAutoColor( bUseStyleColor );
}

void ScPrintFunc::SetRenderFlag( bool bFlag )
{
    bIsRender = bFlag;      // set when using XRenderable (PDF)
}

void ScPrintFunc::SetExclusivelyDrawOleAndDrawObjects()
{
    aTableParam.bCellContent = false;
    aTableParam.bNotes = false;
    aTableParam.bGrid = false;
    aTableParam.bHeaders = false;
    aTableParam.bFormulas = false;
    aTableParam.bNullVals = false;
}

//  UpdatePages is only called from outside to set the breaks correctly for viewing
//  - always without UserArea

bool ScPrintFunc::UpdatePages()
{
    if (!pParamSet)
        return false;

    //  Zoom

    nZoom = 100;
    if (aTableParam.bScalePageNum || aTableParam.bScaleTo)
        nZoom = ZOOM_MIN;                       // correct for breaks
    else if (aTableParam.bScaleAll)
    {
        nZoom = aTableParam.nScaleAll;
        if ( nZoom <= ZOOM_MIN )
            nZoom = ZOOM_MIN;
    }

    OUString aName = rDoc.GetPageStyle( nPrintTab );
    SCTAB nTabCount = rDoc.GetTableCount();
    for (SCTAB nTab=0; nTab<nTabCount; nTab++)
        if ( nTab==nPrintTab || rDoc.GetPageStyle(nTab)==aName )
        {
            //  Repeating rows/columns
            rDoc.SetRepeatArea( nTab, nRepeatStartCol,nRepeatEndCol, nRepeatStartRow,nRepeatEndRow );

            //  set breaks
            ResetBreaks(nTab);
            rDocShell.PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Grid);
        }

    return true;
}

tools::Long ScPrintFunc::CountPages()                          // sets also nPagesX, nPagesY
{
    bool bAreaOk = false;

    if (rDoc.HasTable( nPrintTab ))
    {
        if (aAreaParam.bPrintArea)                          // Specify print area?
        {
            if ( bPrintCurrentTable )
            {
                ScRange& rRange = aAreaParam.aPrintArea;

                //  Here, no comparison of the tables any more. Area is always valid for this table
                //  If comparison should be done here, the table of print ranges must be adjusted
                //  when inserting tables etc.!

                nStartCol = rRange.aStart.Col();
                nStartRow = rRange.aStart.Row();
                nEndCol   = rRange.aEnd  .Col();
                nEndRow   = rRange.aEnd  .Row();
                bAreaOk   = AdjustPrintArea(false);         // limit
            }
            else
                bAreaOk = false;
        }
        else                                                // search from document
            bAreaOk = AdjustPrintArea(true);
    }

    if (bAreaOk)
    {
        tools::Long nPages = 0;
        size_t nY;
        if (bMultiArea)
        {
            sal_uInt16 nRCount = rDoc.GetPrintRangeCount( nPrintTab );
            for (sal_uInt16 i=0; i<nRCount; i++)
            {
                CalcZoom(i);
                if ( aTableParam.bSkipEmpty )
                    for (nY=0; nY< m_aRanges.m_nPagesY; nY++)
                        nPages += (*m_aRanges.m_xPageRows)[nY].CountVisible();
                else
                    nPages += static_cast<tools::Long>(m_aRanges.m_nPagesX) * m_aRanges.m_nPagesY;
                if ( pPageData )
                    FillPageData();
            }
        }
        else
        {
            CalcZoom(RANGENO_NORANGE);                      // calculate Zoom
            if ( aTableParam.bSkipEmpty )
                for (nY=0; nY<m_aRanges.m_nPagesY; nY++)
                    nPages += (*m_aRanges.m_xPageRows)[nY].CountVisible();
            else
                nPages += static_cast<tools::Long>(m_aRanges.m_nPagesX) * m_aRanges.m_nPagesY;
            if ( pPageData )
                FillPageData();
        }
        return nPages;
    }
    else
    {
        m_aRanges.m_nPagesX = m_aRanges.m_nPagesY = m_aRanges.m_nTotalY = 0;
        return 0;
    }
}

tools::Long ScPrintFunc::CountNotePages()
{
    if ( !aTableParam.bNotes || !bPrintCurrentTable )
        return 0;

    bool bError = false;
    if (!aAreaParam.bPrintArea)
        bError = !AdjustPrintArea(true);            // completely search in Doc

    sal_uInt16 nRepeats = 1;                            // how often go through it ?
    if (bMultiArea)
        nRepeats = rDoc.GetPrintRangeCount(nPrintTab);
    if (bError)
        nRepeats = 0;

    for (sal_uInt16 nStep=0; nStep<nRepeats; nStep++)
    {
        bool bDoThis = true;
        if (bMultiArea)             // go through all Areas
        {
            const ScRange* pThisRange = rDoc.GetPrintRange( nPrintTab, nStep );
            if ( pThisRange )
            {
                nStartCol = pThisRange->aStart.Col();
                nStartRow = pThisRange->aStart.Row();
                nEndCol   = pThisRange->aEnd  .Col();
                nEndRow   = pThisRange->aEnd  .Row();
                bDoThis = AdjustPrintArea(false);
            }
        }

        if (bDoThis)
        {
            assert( bPrintAreaValid );
            for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
            {
                if (rDoc.HasColNotes(nCol, nPrintTab))
                {
                    for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
                    {
                        if ( rDoc.HasNote(nCol, nRow, nPrintTab) )
                            aNotePosList.emplace_back( nCol, nRow, nPrintTab );
                    }
                }
            }
        }
    }

    tools::Long nPages = 0;
    tools::Long nNoteNr = 0;
    tools::Long nNoteAdd;
    do
    {
        nNoteAdd = PrintNotes( nPages, nNoteNr, false, nullptr );
        if (nNoteAdd)
        {
            nNoteNr += nNoteAdd;
            ++nPages;
        }
    }
    while (nNoteAdd);

    return nPages;
}

void ScPrintFunc::InitModes()               // set MapModes from  nZoom etc.
{
    aOffset = Point( aSrcOffset.X()*100/nZoom, aSrcOffset.Y()*100/nZoom );

    tools::Long nEffZoom = nZoom * static_cast<tools::Long>(nManualZoom);
    constexpr auto HMM_PER_TWIPS = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100);
    nScaleX = nScaleY = HMM_PER_TWIPS;  // output in 1/100 mm

    Fraction aZoomFract( nEffZoom,10000 );
    Fraction aHorFract = aZoomFract;

    if ( !pPrinter && !bIsRender )                          // adjust scale for preview
    {
        double nFact = rDocShell.GetOutputFactor();
        aHorFract = Fraction( static_cast<tools::Long>( nEffZoom / nFact ), 10000 );
    }

    aLogicMode = MapMode( MapUnit::Map100thMM, Point(), aHorFract, aZoomFract );

    Point aLogicOfs( -aOffset.X(), -aOffset.Y() );
    aOffsetMode = MapMode( MapUnit::Map100thMM, aLogicOfs, aHorFract, aZoomFract );

    Point aTwipsOfs( static_cast<tools::Long>( -aOffset.X() / nScaleX + 0.5 ), static_cast<tools::Long>( -aOffset.Y() / nScaleY + 0.5 ) );
    aTwipMode = MapMode( MapUnit::MapTwip, aTwipsOfs, aHorFract, aZoomFract );
}

void ScPrintFunc::ApplyPrintSettings()
{
    if ( !pPrinter )
        return;

    //  Configure Printer to Printing

    Size aEnumSize = aPageSize;

    pPrinter->SetOrientation( bLandscape ? Orientation::Landscape : Orientation::Portrait );
    if ( bLandscape )
    {
            // landscape is always interpreted as a rotation by 90 degrees !
            // this leads to non WYSIWIG but at least it prints!
            // #i21775#
            tools::Long nTemp = aEnumSize.Width();
            aEnumSize.setWidth( aEnumSize.Height() );
            aEnumSize.setHeight( nTemp );
    }
    Paper ePaper = SvxPaperInfo::GetSvxPaper( aEnumSize, MapUnit::MapTwip );
    sal_uInt16 nPaperBin = pParamSet->Get(ATTR_PAGE_PAPERBIN).GetValue();

    pPrinter->SetPaper( ePaper );
    if ( PAPER_USER == ePaper )
    {
        MapMode aPrinterMode = pPrinter->GetMapMode();
        MapMode aLocalMode( MapUnit::MapTwip );
        pPrinter->SetMapMode( aLocalMode );
        pPrinter->SetPaperSizeUser( aEnumSize );
        pPrinter->SetMapMode( aPrinterMode );
    }

    pPrinter->SetPaperBin( nPaperBin );
}

//  rPageRanges   = range for all tables
//  nStartPage    = rPageRanges starts at nStartPage
//  nDisplayStart = continuous number for displaying the page number

tools::Long ScPrintFunc::DoPrint( const MultiSelection& rPageRanges,
                                tools::Long nStartPage, tools::Long nDisplayStart, bool bDoPrint,
                                ScPreviewLocationData* pLocationData )
{
    OSL_ENSURE(pDev,"Device == NULL");
    if (!pParamSet)
        return 0;

    if ( pPrinter && bDoPrint )
        ApplyPrintSettings();

    InitModes();
    if ( pLocationData )
    {
        pLocationData->SetCellMapMode( aOffsetMode );
        pLocationData->SetPrintTab( nPrintTab );
    }

    MakeTableString();

    tools::Long nPageNo = 0;
    tools::Long nPrinted = 0;
    tools::Long nEndPage = rPageRanges.GetTotalRange().Max();

    sal_uInt16 nRepeats = 1;
    if (bMultiArea)
        nRepeats = rDoc.GetPrintRangeCount(nPrintTab);
    for (sal_uInt16 nStep=0; nStep<nRepeats; nStep++)
    {
        if (bMultiArea)                     // replace area
        {
            CalcZoom(nStep);                // also sets nStartCol etc. new
            InitModes();
        }

        SCCOL nX1;
        SCROW nY1;
        SCCOL nX2;
        SCROW nY2;
        size_t nCountX;
        size_t nCountY;

        if (aTableParam.bTopDown)                           // top-bottom
        {
            nX1 = nStartCol;
            for (nCountX=0; nCountX<m_aRanges.m_nPagesX; nCountX++)
            {
                OSL_ENSURE(nCountX < m_aRanges.m_xPageEndX->size(), "vector access error for aPageEndX (!)");
                nX2 = (*m_aRanges.m_xPageEndX)[nCountX];
                for (nCountY=0; nCountY<m_aRanges.m_nPagesY; nCountY++)
                {
                    auto& rPageRow = (*m_aRanges.m_xPageRows)[nCountY];
                    nY1 = rPageRow.GetStartRow();
                    nY2 = rPageRow.GetEndRow();
                    if ( !aTableParam.bSkipEmpty || !rPageRow.IsHidden(nCountX) )
                    {
                        if ( rPageRanges.IsSelected( nPageNo+nStartPage+1 ) )
                        {
                            PrintPage( nPageNo+nDisplayStart, nX1, nY1, nX2, nY2,
                                        bDoPrint, pLocationData );
                            ++nPrinted;
                        }
                        ++nPageNo;
                    }
                }
                nX1 = nX2 + 1;
            }
        }
        else                                                // left to right
        {
            for (nCountY=0; nCountY<m_aRanges.m_nPagesY; nCountY++)
            {
                auto& rPageRow = (*m_aRanges.m_xPageRows)[nCountY];
                nY1 = rPageRow.GetStartRow();
                nY2 = rPageRow.GetEndRow();
                nX1 = nStartCol;
                for (nCountX=0; nCountX<m_aRanges.m_nPagesX; nCountX++)
                {
                    OSL_ENSURE(nCountX < m_aRanges.m_xPageEndX->size(), "vector access error for aPageEndX");
                    nX2 = (*m_aRanges.m_xPageEndX)[nCountX];
                    if ( !aTableParam.bSkipEmpty || !rPageRow.IsHidden(nCountX) )
                    {
                        if ( rPageRanges.IsSelected( nPageNo+nStartPage+1 ) )
                        {
                            PrintPage( nPageNo+nDisplayStart, nX1, nY1, nX2, nY2,
                                        bDoPrint, pLocationData );
                            ++nPrinted;
                        }
                        ++nPageNo;
                    }
                    nX1 = nX2 + 1;
                }
            }
        }
    }

    aFieldData.aTabName = ScResId( STR_NOTES );

    tools::Long nNoteNr = 0;
    tools::Long nNoteAdd;
    do
    {
        if ( nPageNo+nStartPage <= nEndPage )
        {
            bool bPageSelected = rPageRanges.IsSelected( nPageNo+nStartPage+1 );
            nNoteAdd = PrintNotes( nPageNo+nStartPage, nNoteNr, bDoPrint && bPageSelected,
                                    ( bPageSelected ? pLocationData : nullptr ) );
            if ( nNoteAdd )
            {
                nNoteNr += nNoteAdd;
                if (bPageSelected)
                {
                    ++nPrinted;
                    bSourceRangeValid = false;      // last page was no cell range
                }
                ++nPageNo;
            }
        }
        else
            nNoteAdd = 0;
    }
    while (nNoteAdd);

    if ( bMultiArea )
        ResetBreaks(nPrintTab);                         //breaks correct for displaying

    return nPrinted;
}

void ScPrintFunc::CalcZoom( sal_uInt16 nRangeNo )                       // calculate zoom
{
    sal_uInt16 nRCount = rDoc.GetPrintRangeCount( nPrintTab );
    const ScRange* pThisRange = nullptr;
    if (nRangeNo != RANGENO_NORANGE && nRangeNo < nRCount)
        pThisRange = rDoc.GetPrintRange( nPrintTab, nRangeNo );
    if ( pThisRange )
    {
        nStartCol = pThisRange->aStart.Col();
        nStartRow = pThisRange->aStart.Row();
        nEndCol   = pThisRange->aEnd  .Col();
        nEndRow   = pThisRange->aEnd  .Row();
    }

    if (!AdjustPrintArea(false))                        // empty
    {
        nZoom = 100;
        m_aRanges.m_nPagesX = m_aRanges.m_nPagesY = m_aRanges.m_nTotalY = 0;
        return;
    }

    rDoc.SetRepeatArea( nPrintTab, nRepeatStartCol,nRepeatEndCol, nRepeatStartRow,nRepeatEndRow );

    if (aTableParam.bScalePageNum)
    {
        nZoom = 100;
        sal_uInt16 nPagesToFit = aTableParam.nScalePageNum;

        // If manual breaks are forced, calculate minimum # pages required
        if (aTableParam.bForceBreaks)
        {
             sal_uInt16 nMinPages = 0;
             std::set<SCROW> aRowBreaks;
             std::set<SCCOL> aColBreaks;
             rDoc.GetAllRowBreaks(aRowBreaks, nPrintTab, falsetrue);
             rDoc.GetAllColBreaks(aColBreaks, nPrintTab, falsetrue);
             nMinPages = (aRowBreaks.size() + 1) * (aColBreaks.size() + 1);

             // #i54993# use min forced by breaks if it's > # pages in
             // scale parameter to avoid bottoming out at <= ZOOM_MIN
             nPagesToFit = std::max(nMinPages, nPagesToFit);
        }

        sal_uInt16 nLastFitZoom = 0, nLastNonFitZoom = 0;
        while (true)
        {
            if (nZoom <= ZOOM_MIN)
                break;

            CalcPages();
            bool bFitsPage = (m_aRanges.m_nPagesX * m_aRanges.m_nPagesY <= nPagesToFit);

            if (bFitsPage)
            {
                if (nZoom == 100)
                    // If it fits at 100%, it's good enough for me.
                    break;

                nLastFitZoom = nZoom;
                nZoom = (nLastNonFitZoom + nZoom) / 2;

                if (nLastFitZoom == nZoom)
                    // It converged.  Use this zoom level.
                    break;
            }
            else
            {
                if (nZoom - nLastFitZoom <= 1)
                {
                    nZoom = nLastFitZoom;
                    CalcPages();
                    break;
                }

                nLastNonFitZoom = nZoom;
                nZoom = (nLastFitZoom + nZoom) / 2;
            }
        }
    }
    else if (aTableParam.bScaleTo)
    {
        nZoom = 100;
        sal_uInt16 nW = aTableParam.nScaleWidth;
        sal_uInt16 nH = aTableParam.nScaleHeight;

        // If manual breaks are forced, calculate minimum # pages required
        if (aTableParam.bForceBreaks)
        {
             sal_uInt16 nMinPagesW = 0, nMinPagesH = 0;
             std::set<SCROW> aRowBreaks;
             std::set<SCCOL> aColBreaks;
             rDoc.GetAllRowBreaks(aRowBreaks, nPrintTab, falsetrue);
             rDoc.GetAllColBreaks(aColBreaks, nPrintTab, falsetrue);
             nMinPagesW = aColBreaks.size() + 1;
             nMinPagesH = aRowBreaks.size() + 1;

             // #i54993# use min forced by breaks if it's > # pages in
             // scale parameters to avoid bottoming out at <= ZOOM_MIN
             nW = std::max(nMinPagesW, nW);
             nH = std::max(nMinPagesH, nH);
        }

        sal_uInt16 nLastFitZoom = 0, nLastNonFitZoom = 0;
        while (true)
        {
            if (nZoom <= ZOOM_MIN)
                break;

            CalcPages();
            bool bFitsPage = ((!nW || (m_aRanges.m_nPagesX <= nW)) && (!nH || (m_aRanges.m_nPagesY <= nH)));

            if (bFitsPage)
            {
                if (nZoom == 100)
                    // If it fits at 100%, it's good enough for me.
                    break;

                nLastFitZoom = nZoom;
                nZoom = (nLastNonFitZoom + nZoom) / 2;

                if (nLastFitZoom == nZoom)
                    // It converged.  Use this zoom level.
                    break;
            }
            else
            {
                if (nZoom - nLastFitZoom <= 1)
                {
                    nZoom = nLastFitZoom;
                    CalcPages();
                    break;
                }

                nLastNonFitZoom = nZoom;
                nZoom = (nLastFitZoom + nZoom) / 2;
            }
        }
        // tdf#103516 remove the almost blank page(s) for better
        // interoperability by using slightly smaller zoom
        if (nW > 0 && nH == 0 && m_aRanges.m_nPagesY > 1)
        {
            sal_uInt32 nLastPagesY = m_aRanges.m_nPagesY;
            nLastFitZoom = nZoom;
            nZoom *= 0.98;
            if (nZoom < nLastFitZoom)
            {
                CalcPages();
                // same page count with smaller zoom: use the original zoom
                if (m_aRanges.m_nPagesY == nLastPagesY)
                {
                    nZoom = nLastFitZoom;
                    CalcPages();
                }
            }
        }
    }
    else if (aTableParam.bScaleAll)
    {
        nZoom = aTableParam.nScaleAll;
        if ( nZoom <= ZOOM_MIN )
            nZoom = ZOOM_MIN;
        CalcPages();
    }
    else
    {
        OSL_ENSURE( aTableParam.bScaleNone, "no scale flag is set" );
        nZoom = 100;
        CalcPages();
    }
}

Size ScPrintFunc::GetDocPageSize()
{
                        // Adjust height of head/foot line

    InitModes();                            // initialize aTwipMode from nZoom
    pDev->SetMapMode( aTwipMode );          // head/foot line in Twips
    UpdateHFHeight( aHdr );
    UpdateHFHeight( aFtr );

                        //  Page size in Document-Twips
                        //  Calculating Left / Right also in PrintPage

    aPageRect = tools::Rectangle( Point(), aPageSize );
    aPageRect.SetLeft( ( aPageRect.Left()   + nLeftMargin                  ) * 100 / nZoom );
    aPageRect.SetRight( ( aPageRect.Right()  - nRightMargin                 ) * 100 / nZoom );
    aPageRect.SetTop( ( aPageRect.Top()    + nTopMargin    ) * 100 / nZoom + aHdr.nHeight );
    aPageRect.SetBottom( ( aPageRect.Bottom() - nBottomMargin ) * 100 / nZoom - aFtr.nHeight );

    Size aDocPageSize = aPageRect.GetSize();
    if (aTableParam.bHeaders)
    {
        aDocPageSize.AdjustWidth( -(tools::Long(PRINT_HEADER_WIDTH)) );
        aDocPageSize.AdjustHeight( -(tools::Long(PRINT_HEADER_HEIGHT)) );
    }
    if (pBorderItem)
    {
        aDocPageSize.AdjustWidth( -(lcl_LineTotal(pBorderItem->GetLeft()) +
                                 lcl_LineTotal(pBorderItem->GetRight()) +
                                 pBorderItem->GetDistance(SvxBoxItemLine::LEFT) +
                                 pBorderItem->GetDistance(SvxBoxItemLine::RIGHT)) );
        aDocPageSize.AdjustHeight( -(lcl_LineTotal(pBorderItem->GetTop()) +
                                 lcl_LineTotal(pBorderItem->GetBottom()) +
                                 pBorderItem->GetDistance(SvxBoxItemLine::TOP) +
                                 pBorderItem->GetDistance(SvxBoxItemLine::BOTTOM)) );
    }
    if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
    {
        aDocPageSize.AdjustWidth( -(pShadowItem->CalcShadowSpace(SvxShadowItemSide::LEFT) +
                                 pShadowItem->CalcShadowSpace(SvxShadowItemSide::RIGHT)) );
        aDocPageSize.AdjustHeight( -(pShadowItem->CalcShadowSpace(SvxShadowItemSide::TOP) +
                                 pShadowItem->CalcShadowSpace(SvxShadowItemSide::BOTTOM)) );
    }
    return aDocPageSize;
}

void ScPrintFunc::ResetBreaks( SCTAB nTab )         // Set Breaks correctly for view
{
    rDoc.SetPageSize( nTab, GetDocPageSize() );
    rDoc.UpdatePageBreaks( nTab );
}

static void lcl_SetHidden( const ScDocument& rDoc, SCTAB nPrintTab, ScPageRowEntry&&nbsp;rPageRowEntry,
                    SCCOL nStartCol, const std::vector< SCCOL >& rPageEndX )
{
    size_t nPagesX   = rPageRowEntry.GetPagesX();
    SCROW nStartRow = rPageRowEntry.GetStartRow();
    SCROW nEndRow   = rPageRowEntry.GetEndRow();

    bool bLeftIsEmpty = false;
    ScRange aTempRange;
    tools::Rectangle aTempRect = rDoc.GetMMRect( 0,0, 0,0, 0 );

    for (size_t i=0; i<nPagesX; i++)
    {
        OSL_ENSURE(i < rPageEndX.size(), "vector access error for aPageEndX");
        SCCOL nEndCol = rPageEndX[i];
        if ( rDoc.IsPrintEmpty( nStartCol, nStartRow, nEndCol, nEndRow, nPrintTab,
                                    bLeftIsEmpty, &aTempRange, &aTempRect ) )
        {
            rPageRowEntry.SetHidden(i);
            bLeftIsEmpty = true;
        }
        else
            bLeftIsEmpty = false;

        nStartCol = nEndCol+1;
    }
}

void ScPrintFunc::CalcPages()               // calculates aPageRect and pages from nZoom
{
    assert( bPrintAreaValid );

    sc::PrintPageRangesInput aInput(aTableParam.bSkipEmpty, aAreaParam.bPrintArea,
                                    ScRange(nStartCol, nStartRow, nPrintTab, nEndCol, nEndRow, nPrintTab),
                                    GetDocPageSize());
    m_aRanges.calculate(rDoc, aInput);
}

namespace sc
{

PrintPageRanges::PrintPageRanges()
    : m_nPagesX(0)
    , m_nPagesY(0)
    , m_nTotalY(0)
{}

void PrintPageRanges::calculate(ScDocument& rDoc, PrintPageRangesInput const;rInput)
{
    // Already calculated?
    if (m_aInput == rInput)
        return;

    m_aInput = rInput;

    rDoc.SetPageSize(m_aInput.getPrintTab(), m_aInput.m_aDocSize);

    // Clear the map to prevent any outdated values to "survive" when
    // we have to recalculate the new values anyway
    m_xPageRows->clear();

    // #i123672# use dynamic mem to react on size changes
    if (m_xPageEndX->size() < static_cast<size_t>(rDoc.MaxCol()) + 1)
    {
        m_xPageEndX->resize(rDoc.MaxCol()+1, SCCOL());
    }

    if (m_aInput.m_bPrintArea)
    {
        ScRange aRange(m_aInput.getStartColumn(), m_aInput.getStartRow(), m_aInput.getPrintTab(), m_aInput.getEndColumn(), m_aInput.getEndRow(), m_aInput.getPrintTab());
        rDoc.UpdatePageBreaks(m_aInput.getPrintTab(), &aRange);
    }
    else
    {
        rDoc.UpdatePageBreaks(m_aInput.getPrintTab()); // else, end is marked
    }

    const size_t nRealCnt = m_aInput.getEndRow() - m_aInput.getStartRow() + 1;

    // #i123672# use dynamic mem to react on size changes
    if (m_xPageEndY->size() < nRealCnt+1)
    {
        m_xPageEndY->resize(nRealCnt + 1, SCROW());
    }

    //  Page alignment/splitting after breaks in Col/RowFlags
    //  Of several breaks in a hidden area, only one counts.

    m_nPagesX = 0;
    m_nPagesY = 0;
    m_nTotalY = 0;

    bool bVisCol = false;
    for (SCCOL i = m_aInput.getStartColumn(); i <= m_aInput.getEndColumn(); i++)
    {
        bool bHidden = rDoc.ColHidden(i, m_aInput.getPrintTab());
        bool bPageBreak(rDoc.HasColBreak(i, m_aInput.getPrintTab()) & ScBreakType::Page);
        if (i > m_aInput.getStartColumn() && bVisCol && bPageBreak)
        {
            OSL_ENSURE(m_nPagesX < m_xPageEndX->size(), "vector access error for aPageEndX");
            (*m_xPageEndX)[m_nPagesX] = i-1;
            ++m_nPagesX;
            bVisCol = false;
        }
        if (!bHidden)
            bVisCol = true;
    }
    if (bVisCol) // also at the end, no empty pages
    {
        OSL_ENSURE(m_nPagesX < m_xPageEndX->size(), "vector access error for aPageEndX");
        (*m_xPageEndX)[m_nPagesX] = m_aInput.getEndColumn();
        ++m_nPagesX;
    }

    bool bVisRow = false;
    SCROW nPageStartRow = m_aInput.getStartRow();
    SCROW nLastVisibleRow = -1;

    std::unique_ptr<ScRowBreakIterator> pRowBreakIter(rDoc.GetRowBreakIterator(m_aInput.getPrintTab()));
    SCROW nNextPageBreak = pRowBreakIter->first();
    while (nNextPageBreak != ScRowBreakIterator::NOT_FOUND && nNextPageBreak < m_aInput.getStartRow())
        // Skip until the page break position is at the start row or greater.
        nNextPageBreak = pRowBreakIter->next();

    for (SCROW nRow = m_aInput.getStartRow(); nRow <= m_aInput.getEndRow(); ++nRow)
    {
        bool bPageBreak = (nNextPageBreak == nRow);
        if (bPageBreak)
            nNextPageBreak = pRowBreakIter->next();

        if (nRow > m_aInput.getStartRow() && bVisRow && bPageBreak)
        {
            OSL_ENSURE(m_nTotalY < m_xPageEndY->size(), "vector access error for rPageEndY");
            (*m_xPageEndY)[m_nTotalY] = nRow - 1;
            ++m_nTotalY;

            if (!m_aInput.m_bSkipEmpty || !rDoc.IsPrintEmpty(m_aInput.getStartColumn(), nPageStartRow, m_aInput.getEndColumn(), nRow-1, m_aInput.getPrintTab()))
            {
                auto& rPageRow = (*m_xPageRows)[m_nPagesY];
                rPageRow.SetStartRow(nPageStartRow);
                rPageRow.SetEndRow(nRow - 1);
                rPageRow.SetPagesX(m_nPagesX);
                if (m_aInput.m_bSkipEmpty)
                    lcl_SetHidden(rDoc, m_aInput.getPrintTab(), rPageRow, m_aInput.getStartColumn(), *m_xPageEndX);
                ++m_nPagesY;
            }

            nPageStartRow = nRow;
            bVisRow = false;
        }

        if (nRow <= nLastVisibleRow)
        {
            // This row is still visible.  Don't bother calling RowHidden() to
            // find out, for speed optimization.
            bVisRow = true;
            continue;
        }

        SCROW nLastRow = -1;
        if (!rDoc.RowHidden(nRow, m_aInput.getPrintTab(), nullptr, &nLastRow))
        {
            bVisRow = true;
            nLastVisibleRow = nLastRow;
        }
        else
        {
            // Skip all hidden rows until next pagebreak.
            nRow = ((nNextPageBreak == ScRowBreakIterator::NOT_FOUND) ? nLastRow :
                    std::min(nLastRow, nNextPageBreak - 1));
        }
    }

    if (!bVisRow)
        return;

    OSL_ENSURE(m_nTotalY < m_xPageEndY->size(), "vector access error for maPageEndY");
    (*m_xPageEndY)[m_nTotalY] = m_aInput.getEndRow();
    ++m_nTotalY;

    if (!m_aInput.m_bSkipEmpty || !rDoc.IsPrintEmpty(m_aInput.getStartColumn(), nPageStartRow, m_aInput.getEndColumn(), m_aInput.getEndRow(), m_aInput.getPrintTab()))
    {
        auto& rPageRow = (*m_xPageRows)[m_nPagesY];
        rPageRow.SetStartRow(nPageStartRow);
        rPageRow.SetEndRow(m_aInput.getEndRow());
        rPageRow.SetPagesX(m_nPagesX);
        if (m_aInput.m_bSkipEmpty)
            lcl_SetHidden(rDoc, m_aInput.getPrintTab(), rPageRow, m_aInput.getStartColumn(), *m_xPageEndX);
        ++m_nPagesY;
    }
}

// end namespace sc
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5 in Prozent
C=92 H=94 G=92

¤ Dauer der Verarbeitung: 0.48 Sekunden  (vorverarbeitet am  2026-05-07) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge