tdf#61274 sd PDF export: fix links ending up on wrong pages with hidden slides

SdPage::IsExcluded() decides if a slide is hidden,
SdXImpressDocument::render() checks for this and returns early if
needed. In that case PDFExport::ExportSelection() detects that the
produced metafile has no actions and avoids creating a PDF page.

Then Impress links are created using the
vcl::PDFExtOutDevData::CreateLink() call in
drawinglayer::processor2d::VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(),
not specifying the PDF page number explicitly. This means the link is
created on the "current" page number, set in
vcl::PDFExtOutDevData::SetCurrentPageNumber(), called by
PDFExport::ExportSelection(), but that filter/ code can't know about
hidden slides in sd/.

Fix the problem by setting the page number again in
SdXImpressDocument::render(), that way the link created by drawinglayer
will end on the correct page.

(cherry picked from commit 01dbb38680aa39a4d3bc7afd05d44a4b2c9bc6ab)

Conflicts:
	vcl/qa/cppunit/pdfexport/pdfexport.cxx

Change-Id: Ic29e345d45bc7c944d65e6e450f1d742dd0e9f8c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/90551
Tested-by: Jenkins
Reviewed-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
diff --git a/sd/source/ui/unoidl/unomodel.cxx b/sd/source/ui/unoidl/unomodel.cxx
index 6f751048..8bc98bb 100644
--- a/sd/source/ui/unoidl/unomodel.cxx
+++ b/sd/source/ui/unoidl/unomodel.cxx
@@ -1884,6 +1884,14 @@ void SAL_CALL SdXImpressDocument::render( sal_Int32 nRenderer, const uno::Any& r
        (pPDFExtOutDevData && pPDFExtOutDevData->GetIsExportHiddenSlides())) )
        return;

    if (pPDFExtOutDevData)
    {
        // Calculate the page number in the PDF output, which may be smaller than the page number in
        // case of hidden slides.
        sal_Int32 nOutputPageNum = CalcOutputPageNum(pPDFExtOutDevData, mpDoc, nPageNumber);
        pPDFExtOutDevData->SetCurrentPageNumber(nOutputPageNum);
    }

    std::unique_ptr<::sd::ClientView> pView( new ::sd::ClientView( mpDocShell, pOut ) );
    ::tools::Rectangle aVisArea( Point(), mpDoc->GetSdPage( static_cast<sal_uInt16>(nPageNumber) - 1, ePageKind )->GetSize() );
    vcl::Region                       aRegion( aVisArea );
diff --git a/vcl/qa/cppunit/pdfexport/data/link-wrong-page.odp b/vcl/qa/cppunit/pdfexport/data/link-wrong-page.odp
new file mode 100644
index 0000000..b6787af
--- /dev/null
+++ b/vcl/qa/cppunit/pdfexport/data/link-wrong-page.odp
Binary files differ
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index 9b95717d..48f1208 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -137,6 +137,7 @@ public:
    void testTdf121615();
    void testTocLink();
    void testPdfImageResourceInlineXObjectRef();
    void testLinkWrongPage();

    CPPUNIT_TEST_SUITE(PdfExportTest);
    CPPUNIT_TEST(testTdf106059);
@@ -174,6 +175,7 @@ public:
    CPPUNIT_TEST(testTdf121615);
    CPPUNIT_TEST(testTocLink);
    CPPUNIT_TEST(testPdfImageResourceInlineXObjectRef);
    CPPUNIT_TEST(testLinkWrongPage);
    CPPUNIT_TEST_SUITE_END();
};

@@ -1861,6 +1863,38 @@ void PdfExportTest::testTocLink()
    CPPUNIT_ASSERT(FPDFLink_Enumerate(pPdfPage.get(), &nStartPos, &pLinkAnnot));
}

bool HasLinksOnPage(PageHolder& pPdfPage)
{
    int nStartPos = 0;
    FPDF_LINK pLinkAnnot = nullptr;
    return FPDFLink_Enumerate(pPdfPage.get(), &nStartPos, &pLinkAnnot);
}

void PdfExportTest::testLinkWrongPage()
{
    // Import the bugdoc and export as PDF.
    OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "link-wrong-page.odp";
    utl::MediaDescriptor aMediaDescriptor;
    aMediaDescriptor["FilterName"] <<= OUString("impress_pdf_Export");
    DocumentHolder pPdfDocument = exportAndParse(aURL, aMediaDescriptor);

    // The document has 2 pages.
    CPPUNIT_ASSERT_EQUAL(2, FPDF_GetPageCount(pPdfDocument.get()));

    // First page should have 1 link (2nd slide, 1st was hidden).
    PageHolder pPdfPage(FPDF_LoadPage(pPdfDocument.get(), /*page_index=*/0));
    CPPUNIT_ASSERT(pPdfPage.get());

    // Without the accompanying fix in place, this test would have failed, as the link of the first
    // page went to the second page due to the hidden first slide.
    CPPUNIT_ASSERT(HasLinksOnPage(pPdfPage));

    // Second page should have no links (3rd slide).
    PageHolder pPdfPage2(FPDF_LoadPage(pPdfDocument.get(), /*page_index=*/1));
    CPPUNIT_ASSERT(pPdfPage2.get());
    CPPUNIT_ASSERT(!HasLinksOnPage(pPdfPage2));
}

void PdfExportTest::testPdfImageResourceInlineXObjectRef()
{
    // Create an empty document.