diff --git a/sc/CppunitTest_sc_uicalc2.mk b/sc/CppunitTest_sc_uicalc2.mk index 991ecc846108..84848c35c838 100644 --- a/sc/CppunitTest_sc_uicalc2.mk +++ b/sc/CppunitTest_sc_uicalc2.mk @@ -36,6 +36,7 @@ $(eval $(call gb_CppunitTest_use_libraries,sc_uicalc2, \ svx \ svxcore \ test \ + tk \ tl \ unotest \ utl \ diff --git a/sc/qa/unit/uicalc/data/tdf171366.fods b/sc/qa/unit/uicalc/data/tdf171366.fods new file mode 100644 index 000000000000..04d76dddaf03 --- /dev/null +++ b/sc/qa/unit/uicalc/data/tdf171366.fods @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + row1 + row2 + row3 + row4 + row5 + row6 + row7 + row8 + row9 + row10 + row11 + row12 + row13 + row14 + row15 + row16 + row17 + row18 + row19 + row20 + row21 + row22 + row23 + row24 + row25 + row26 + row27 + row28 + row29 + row30 + row31 + row32 + row33 + row34 + row35 + row36 + row37 + row38 + row39 + row40 + row41 + row42 + row43 + row44 + row45 + row46 + row47 + row48 + row49 + row50 + row51 + row52 + row53 + row54 + row55 + row56 + row57 + row58 + row59 + row60 + row61 + row62 + row63 + row64 + row65 + row66 + row67 + row68 + row69 + row70 + row71 + row72 + row73 + row74 + row75 + row76 + row77 + row78 + row79 + row80 + row81 + row82 + row83 + row84 + row85 + row86 + row87 + row88 + row89 + row90 + row91 + row92 + row93 + row94 + row95 + row96 + row97 + row98 + row99 + row100 + + + + diff --git a/sc/qa/unit/uicalc/uicalc2.cxx b/sc/qa/unit/uicalc/uicalc2.cxx index f21aa1e882c2..65d66b2ed9b9 100644 --- a/sc/qa/unit/uicalc/uicalc2.cxx +++ b/sc/qa/unit/uicalc/uicalc2.cxx @@ -14,23 +14,33 @@ #include #include #include +#include +#include #include #include +#include #include +#include #include +#include +#include #include #include #include #include #include +#include #include #include +#include #include #include #include #include +#include #include +#include #include #include #include @@ -2595,6 +2605,120 @@ CPPUNIT_TEST_FIXTURE(ScUiCalcTest2, testSetHyperlinkKeepSelection) CPPUNIT_ASSERT_EQUAL(u"foo"_ustr, pURLField->GetRepresentation()); } +namespace +{ +// Prints the document once and collects the cell text of each page, in drawing order. The +// printer records into a metafile, so this is the text that would have reached the paper. +std::vector> +lcl_getPrintedPages(const uno::Reference& xComponent, Printer* pPrinter) +{ + rtl::Reference pVclDevice = new VCLXDevice(); + pVclDevice->SetOutputDevice(pPrinter); + + uno::Reference xRenderable(xComponent, uno::UNO_QUERY_THROW); + css::uno::Any aSelection(xComponent); + + std::vector aOptions{ + comphelper::makePropertyValue(u"RenderDevice"_ustr, + uno::Reference(pVclDevice)), + comphelper::makePropertyValue(u"IsPrinter"_ustr, true), + comphelper::makePropertyValue(u"IsLastPage"_ustr, false), + }; + + sal_Int32 nPages + = xRenderable->getRendererCount(aSelection, comphelper::containerToSequence(aOptions)); + + std::vector> aPages; + for (sal_Int32 nPage = 0; nPage < nPages; ++nPage) + { + aOptions.back().Value <<= (nPage + 1 == nPages); + css::uno::Sequence aSequence + = comphelper::containerToSequence(aOptions); + + GDIMetaFile aMetaFile; + pPrinter->SetConnectMetaFile(&aMetaFile); + xRenderable->getRenderer(nPage, aSelection, aSequence); + xRenderable->render(nPage, aSelection, aSequence); + pPrinter->SetConnectMetaFile(nullptr); + + std::vector aTexts; + for (size_t nAction = 0; nAction < aMetaFile.GetActionSize(); ++nAction) + { + MetaAction* pAction = aMetaFile.GetAction(nAction); + OUString aText; + if (pAction->GetType() == MetaActionType::TEXTARRAY) + aText = static_cast(pAction)->GetText(); + else if (pAction->GetType() == MetaActionType::TEXT) + aText = static_cast(pAction)->GetText(); + else if (pAction->GetType() == MetaActionType::STRETCHTEXT) + aText = static_cast(pAction)->GetText(); + if (aText.startsWith(u"row")) + aTexts.push_back(aText); + } + aPages.push_back(std::move(aTexts)); + } + return aPages; +} + +// Names the first and the last cell of every page on one line. A failure then shows which rows +// each page carried. +OUString lcl_describePages(const std::vector>& rPages) +{ + OUStringBuffer aBuffer; + for (const auto& rPage : rPages) + { + if (!aBuffer.isEmpty()) + aBuffer.append(" "); + if (rPage.empty()) + aBuffer.append("empty"); + else + aBuffer.append(rPage.front() + ".." + rPage.back()); + } + return aBuffer.makeStringAndClear(); +} +} + +CPPUNIT_TEST_FIXTURE(ScUiCalcTest2, testTdf171366) +{ + // The sheet holds 100 rows on A4 portrait paper. That is few enough per landscape page that + // the two orientations break the sheet at different rows. + createScDoc("tdf171366.fods"); + + // The view shell hands back the document's own printer, which is the one a print job uses. + SfxViewShell* pViewShell = getViewShell(); + SfxPrinter* pPrinter = pViewShell->GetPrinter(true); + CPPUNIT_ASSERT(pPrinter); + + // The Print Dialog turns the paper size itself and hands it on in 1/100 mm. + const Size aTurnedPaper(29700, 21001); + + // The user chooses Landscape in the Print Dialog. The dialog gives the printer the turned + // size and marks the printer as carrying a setting of its own. + pPrinter->SetPrintPageSize(aTurnedPaper); + pPrinter->SetOrientation(Orientation::Landscape); + pPrinter->SetUsePrintDialogSetting(true); + const OUString aFromDialog = lcl_describePages(lcl_getPrintedPages(mxComponent, pPrinter)); + + // The user makes the same turn in Format - Page Style instead. That path always worked. + pPrinter->SetUsePrintDialogSetting(false); + uno::Reference xSupplier(mxComponent, uno::UNO_QUERY_THROW); + uno::Reference xPageStyles( + xSupplier->getStyleFamilies()->getByName(u"PageStyles"_ustr), uno::UNO_QUERY_THROW); + uno::Reference xPageStyle(xPageStyles->getByName(u"Default"_ustr), + uno::UNO_QUERY_THROW); + xPageStyle->setPropertyValue(u"IsLandscape"_ustr, css::uno::Any(true)); + xPageStyle->setPropertyValue( + u"Size"_ustr, css::uno::Any(awt::Size(aTurnedPaper.Width(), aTurnedPaper.Height()))); + const OUString aFromPageStyle = lcl_describePages(lcl_getPrintedPages(mxComponent, pPrinter)); + + // Turning the paper in the Print Dialog splits the sheet the same way as turning it in the + // page style. Without the accompanying fix, only the first page was laid out for landscape + // paper and the rest kept the portrait splits, so this test failed with: + // - Expected: row1..row37 row38..row74 row75..row100 + // - Actual : row1..row37 row57..row100 empty + CPPUNIT_ASSERT_EQUAL(aFromPageStyle, aFromDialog); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/inc/printfun.hxx b/sc/source/ui/inc/printfun.hxx index 2fa314daed79..539f868ddb56 100644 --- a/sc/source/ui/inc/printfun.hxx +++ b/sc/source/ui/inc/printfun.hxx @@ -174,7 +174,9 @@ public: size_t m_nPagesY; size_t m_nTotalY; - PrintPageRangesInput m_aInput; + // The ranges above were calculated for this input. Every copy shares both, so the input + // always matches the result. + std::shared_ptr m_pInput; void calculate(ScDocument& rDoc, PrintPageRangesInput const& rInput); }; diff --git a/sc/source/ui/unoobj/docuno.cxx b/sc/source/ui/unoobj/docuno.cxx index 9efeea10b9d2..6f5d2ea239b3 100644 --- a/sc/source/ui/unoobj/docuno.cxx +++ b/sc/source/ui/unoobj/docuno.cxx @@ -2928,19 +2928,6 @@ void SAL_CALL ScModelObj::render( sal_Int32 nSelRenderer, const uno::Any& aSelec (void)pPrintFunc->DoPrint( aPage, nTabStart, nDisplayStart, true, nullptr ); - if (pPrinter) - { - // reset the print area created by the Print Dialog to the page style's print area - if (pPrinter->IsUsePrintDialogSetting()) - { - bUsePrintDialogSetting = false; - if (m_pPrintState && m_pPrintState->nPrintTab == nTab && !pSelRange) - pPrintFunc.reset(new ScPrintFunc(pDev, *pDocShell, *m_pPrintState, - &aStatus.GetOptions(), aPrintPageSize, - bPrintPageLandscape, bUsePrintDialogSetting)); - } - } - vcl::PDFExtOutDevData* pPDFData = dynamic_cast(pDev->GetExtOutDevData()); const bool bFinishTagging(pPDFData && pPDFData->GetIsExportTaggedPDF() && bIsLastPage); if (bFinishTagging) @@ -2956,6 +2943,12 @@ void SAL_CALL ScModelObj::render( sal_Int32 nSelRenderer, const uno::Any& aSelec if (bFinishTagging) lcl_PDFExportFinishTagging(*pPDFData); + + // The document carried the Print Dialog's paper size and orientation for the length of the + // job. A last count after the final page puts back the page size and the page breaks that + // the page style describes. + if (pPrinter && pPrinter->IsUsePrintDialogSetting() && bIsLastPage) + pPrintFuncCache.reset(new ScPrintFuncCache(*pDocShell, aMark, aStatus)); } // XLinkTargetSupplier diff --git a/sc/source/ui/view/printfun.cxx b/sc/source/ui/view/printfun.cxx index ae8ef36a4453..0ae390238e3a 100644 --- a/sc/source/ui/view/printfun.cxx +++ b/sc/source/ui/view/printfun.cxx @@ -3072,17 +3072,18 @@ PrintPageRanges::PrintPageRanges() : m_nPagesX(0) , m_nPagesY(0) , m_nTotalY(0) + , m_pInput(std::make_shared()) {} void PrintPageRanges::calculate(ScDocument& rDoc, PrintPageRangesInput const& rInput) { // Already calculated? - if (m_aInput == rInput) + if (*m_pInput == rInput) return; - m_aInput = rInput; + *m_pInput = rInput; - rDoc.SetPageSize(m_aInput.getPrintTab(), m_aInput.m_aDocSize); + rDoc.SetPageSize(m_pInput->getPrintTab(), m_pInput->m_aDocSize); // Clear the map to prevent any outdated values to "survive" when // we have to recalculate the new values anyway @@ -3094,17 +3095,18 @@ void PrintPageRanges::calculate(ScDocument& rDoc, PrintPageRangesInput const& rI m_xPageEndX->resize(rDoc.MaxCol()+1, SCCOL()); } - if (m_aInput.m_bPrintArea) + if (m_pInput->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); + ScRange aRange(m_pInput->getStartColumn(), m_pInput->getStartRow(), m_pInput->getPrintTab(), + m_pInput->getEndColumn(), m_pInput->getEndRow(), m_pInput->getPrintTab()); + rDoc.UpdatePageBreaks(m_pInput->getPrintTab(), &aRange); } else { - rDoc.UpdatePageBreaks(m_aInput.getPrintTab()); // else, end is marked + rDoc.UpdatePageBreaks(m_pInput->getPrintTab()); // else, end is marked } - const size_t nRealCnt = m_aInput.getEndRow() - m_aInput.getStartRow() + 1; + const size_t nRealCnt = m_pInput->getEndRow() - m_pInput->getStartRow() + 1; // #i123672# use dynamic mem to react on size changes if (m_xPageEndY->size() < nRealCnt+1) @@ -3120,11 +3122,11 @@ void PrintPageRanges::calculate(ScDocument& rDoc, PrintPageRangesInput const& rI m_nTotalY = 0; bool bVisCol = false; - for (SCCOL i = m_aInput.getStartColumn(); i <= m_aInput.getEndColumn(); i++) + for (SCCOL i = m_pInput->getStartColumn(); i <= m_pInput->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) + bool bHidden = rDoc.ColHidden(i, m_pInput->getPrintTab()); + bool bPageBreak(rDoc.HasColBreak(i, m_pInput->getPrintTab()) & ScBreakType::Page); + if (i > m_pInput->getStartColumn() && bVisCol && bPageBreak) { OSL_ENSURE(m_nPagesX < m_xPageEndX->size(), "vector access error for aPageEndX"); (*m_xPageEndX)[m_nPagesX] = i-1; @@ -3137,40 +3139,45 @@ void PrintPageRanges::calculate(ScDocument& rDoc, PrintPageRangesInput const& rI 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_xPageEndX)[m_nPagesX] = m_pInput->getEndColumn(); ++m_nPagesX; } bool bVisRow = false; - SCROW nPageStartRow = m_aInput.getStartRow(); + SCROW nPageStartRow = m_pInput->getStartRow(); SCROW nLastVisibleRow = -1; - std::unique_ptr pRowBreakIter(rDoc.GetRowBreakIterator(m_aInput.getPrintTab())); + std::unique_ptr pRowBreakIter( + rDoc.GetRowBreakIterator(m_pInput->getPrintTab())); SCROW nNextPageBreak = pRowBreakIter->first(); - while (nNextPageBreak != ScRowBreakIterator::NOT_FOUND && nNextPageBreak < m_aInput.getStartRow()) + while (nNextPageBreak != ScRowBreakIterator::NOT_FOUND + && nNextPageBreak < m_pInput->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) + for (SCROW nRow = m_pInput->getStartRow(); nRow <= m_pInput->getEndRow(); ++nRow) { bool bPageBreak = (nNextPageBreak == nRow); if (bPageBreak) nNextPageBreak = pRowBreakIter->next(); - if (nRow > m_aInput.getStartRow() && bVisRow && bPageBreak) + if (nRow > m_pInput->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())) + if (!m_pInput->m_bSkipEmpty + || !rDoc.IsPrintEmpty(m_pInput->getStartColumn(), nPageStartRow, + m_pInput->getEndColumn(), nRow-1, m_pInput->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); + if (m_pInput->m_bSkipEmpty) + lcl_SetHidden(rDoc, m_pInput->getPrintTab(), rPageRow, + m_pInput->getStartColumn(), *m_xPageEndX); ++m_nPagesY; } @@ -3187,7 +3194,7 @@ void PrintPageRanges::calculate(ScDocument& rDoc, PrintPageRangesInput const& rI } SCROW nLastRow = -1; - if (!rDoc.RowHidden(nRow, m_aInput.getPrintTab(), nullptr, &nLastRow)) + if (!rDoc.RowHidden(nRow, m_pInput->getPrintTab(), nullptr, &nLastRow)) { bVisRow = true; nLastVisibleRow = nLastRow; @@ -3204,17 +3211,20 @@ void PrintPageRanges::calculate(ScDocument& rDoc, PrintPageRangesInput const& rI return; OSL_ENSURE(m_nTotalY < m_xPageEndY->size(), "vector access error for maPageEndY"); - (*m_xPageEndY)[m_nTotalY] = m_aInput.getEndRow(); + (*m_xPageEndY)[m_nTotalY] = m_pInput->getEndRow(); ++m_nTotalY; - if (!m_aInput.m_bSkipEmpty || !rDoc.IsPrintEmpty(m_aInput.getStartColumn(), nPageStartRow, m_aInput.getEndColumn(), m_aInput.getEndRow(), m_aInput.getPrintTab())) + if (!m_pInput->m_bSkipEmpty + || !rDoc.IsPrintEmpty(m_pInput->getStartColumn(), nPageStartRow, m_pInput->getEndColumn(), + m_pInput->getEndRow(), m_pInput->getPrintTab())) { auto& rPageRow = (*m_xPageRows)[m_nPagesY]; rPageRow.SetStartRow(nPageStartRow); - rPageRow.SetEndRow(m_aInput.getEndRow()); + rPageRow.SetEndRow(m_pInput->getEndRow()); rPageRow.SetPagesX(m_nPagesX); - if (m_aInput.m_bSkipEmpty) - lcl_SetHidden(rDoc, m_aInput.getPrintTab(), rPageRow, m_aInput.getStartColumn(), *m_xPageEndX); + if (m_pInput->m_bSkipEmpty) + lcl_SetHidden(rDoc, m_pInput->getPrintTab(), rPageRow, m_pInput->getStartColumn(), + *m_xPageEndX); ++m_nPagesY; } }