diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx b/sw/source/core/text/EnhancedPDFExportHelper.cxx index 59f7ef16b86c..d65f46064005 100644 --- a/sw/source/core/text/EnhancedPDFExportHelper.cxx +++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx @@ -444,6 +444,8 @@ bool lcl_TryMoveToNonHiddenField(SwEditShell& rShell, const SwTextNode& rNd, con ::std::vector ret; SwRects rects; rShell.GetLayout()->CalcFrameRects(*rShell.GetCursor_(), rects, SwRootFrame::RectsMode::NoAnchoredFlys); + auto const [pStart, pEnd] = rShell.GetCursor_()->StartEnd(); + for (SwRect const& rRect : rects) { Point center(rRect.Center()); @@ -451,8 +453,10 @@ bool lcl_TryMoveToNonHiddenField(SwEditShell& rShell, const SwTextNode& rNd, con SwCursorMoveState cms(CursorMoveState::NONE); cms.m_pSpecialPos = &special; cms.m_bFieldInfo = true; + // the centre of a one-glyph rectangle falls in that glyph's second half, so the hit + // test below returns the character that contains the point + cms.m_bPosMatchesBounds = true; SwPosition pos(rShell.GetDoc()->GetNodes()); - auto const [pStart, pEnd] = rShell.GetCursor_()->StartEnd(); if (rShell.GetLayout()->GetModelPositionForViewPoint(&pos, center, &cms) && *pStart <= pos && pos <= *pEnd) { diff --git a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx index be08bb47a7eb..58b238adb029 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx @@ -20,16 +20,24 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -511,6 +519,124 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTocLink) CPPUNIT_ASSERT(pPdfPage->hasLinks()); } +// Inserts three cross-reference fields to a one-digit bookmark target, separated only by +// ", ", the way consecutive caption numbers are. +void lcl_insertAdjacentReferenceFields(const uno::Reference& xComponent, + float fCharHeight) +{ + uno::Reference xTextDocument(xComponent, uno::UNO_QUERY); + uno::Reference xText = xTextDocument->getText(); + uno::Reference xCursor = xText->createTextCursor(); + uno::Reference xFactory(xComponent, uno::UNO_QUERY); + uno::Reference xCursorProps(xCursor, uno::UNO_QUERY); + xCursorProps->setPropertyValue(u"CharHeight"_ustr, uno::Any(fCharHeight)); + + xText->insertString(xCursor, u"1"_ustr, /*bAbsorb=*/false); + xCursor->goLeft(1, /*bExpand=*/true); + uno::Reference xBookmark( + xFactory->createInstance(u"com.sun.star.text.Bookmark"_ustr), uno::UNO_QUERY); + uno::Reference xBookmarkName(xBookmark, uno::UNO_QUERY); + xBookmarkName->setName(u"Target"_ustr); + xText->insertTextContent(xCursor, xBookmark, /*bAbsorb=*/true); + + xCursor->gotoEnd(/*bExpand=*/false); + xText->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, + /*bAbsorb=*/false); + + auto insertReferenceField = [&]() { + xCursor->gotoEnd(/*bExpand=*/false); + uno::Reference xField( + xFactory->createInstance(u"com.sun.star.text.TextField.GetReference"_ustr), + uno::UNO_QUERY); + uno::Reference xFieldProps(xField, uno::UNO_QUERY); + xFieldProps->setPropertyValue(u"ReferenceFieldSource"_ustr, + uno::Any(sal_Int16(text::ReferenceFieldSource::BOOKMARK))); + xFieldProps->setPropertyValue(u"ReferenceFieldPart"_ustr, + uno::Any(sal_Int16(text::ReferenceFieldPart::TEXT))); + xFieldProps->setPropertyValue(u"SourceName"_ustr, uno::Any(u"Target"_ustr)); + xField->attach(xCursor); + }; + + insertReferenceField(); + xCursor->gotoEnd(/*bExpand=*/false); + xText->insertString(xCursor, u", "_ustr, /*bAbsorb=*/false); + insertReferenceField(); + xCursor->gotoEnd(/*bExpand=*/false); + xText->insertString(xCursor, u", "_ustr, /*bAbsorb=*/false); + insertReferenceField(); +} + +int lcl_countAnnotations(const std::unique_ptr& pPdfDocument) +{ + std::unique_ptr pPdfPage = pPdfDocument->openPage(/*nIndex=*/0); + CPPUNIT_ASSERT(pPdfPage); + return pPdfPage->getAnnotationCount(); +} + +CPPUNIT_TEST_FIXTURE(PdfExportTest2, testAdjacentReferenceFieldLinks_12) +{ + // Every one of three adjacent one-digit cross-reference fields gets its own clickable + // area in the exported PDF, at every font size. + loadFromURL(u"private:factory/swriter"_ustr); + lcl_insertAdjacentReferenceFields(mxComponent, 12.0f); + + save(TestFilter::PDF_WRITER); + + OString aMessage("font size " + OString::number(12.0f)); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + aMessage.getStr(), 3, lcl_countAnnotations(parsePDFExport(vcl::pdf::PDFiumLibrary::get()))); +} + +CPPUNIT_TEST_FIXTURE(PdfExportTest2, testAdjacentReferenceFieldLinks_10) +{ + // Every one of three adjacent one-digit cross-reference fields gets its own clickable + // area in the exported PDF, at every font size. + loadFromURL(u"private:factory/swriter"_ustr); + lcl_insertAdjacentReferenceFields(mxComponent, 10.0f); + + save(TestFilter::PDF_WRITER); + + OString aMessage("font size " + OString::number(10.0f)); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + aMessage.getStr(), 3, lcl_countAnnotations(parsePDFExport(vcl::pdf::PDFiumLibrary::get()))); +} + +CPPUNIT_TEST_FIXTURE(PdfExportTest2, testAdjacentReferenceFieldLinks_8) +{ + // Every one of three adjacent one-digit cross-reference fields gets its own clickable + // area in the exported PDF, at every font size. + loadFromURL(u"private:factory/swriter"_ustr); + lcl_insertAdjacentReferenceFields(mxComponent, 8.0f); + + save(TestFilter::PDF_WRITER); + + OString aMessage("font size " + OString::number(8.0f)); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + aMessage.getStr(), 3, lcl_countAnnotations(parsePDFExport(vcl::pdf::PDFiumLibrary::get()))); +} + +CPPUNIT_TEST_FIXTURE(PdfExportTest2, testAdjacentReferenceFieldLinksWideCaret) +{ + // The width of the text caret is a desktop setting, and can be set wider than a small + // glyph. The clickable areas stay independent of it. + AllSettings aOldSettings(Application::GetSettings()); + comphelper::ScopeGuard aSettingsGuard( + [&aOldSettings] { Application::SetSettings(aOldSettings); }); + AllSettings aSettings(aOldSettings); + StyleSettings aStyleSettings(aSettings.GetStyleSettings()); + // 6 pixels is 90 twips at 96 dots per inch, wider than an 8 point digit. + aStyleSettings.SetCursorSize(6); + aSettings.SetStyleSettings(aStyleSettings); + Application::SetSettings(aSettings); + + loadFromURL(u"private:factory/swriter"_ustr); + lcl_insertAdjacentReferenceFields(mxComponent, 8.0f); + + save(TestFilter::PDF_WRITER); + + CPPUNIT_ASSERT_EQUAL(3, lcl_countAnnotations(parsePDFExport(vcl::pdf::PDFiumLibrary::get()))); +} + CPPUNIT_TEST_FIXTURE(PdfExportTest2, testReduceSmallImage) { // Load the Writer document.