cool#15668 sw: keep adjacent caption links clickable in a pdf
Exporting several cross-reference links placed right next to each other, separated only by punctuation, kept only the last one clickable. The export checks every link rectangle for the text it covers, by hit-testing the middle of the rectangle. For a link only one digit wide that point falls exactly where the hit test advances to the next character, which belongs to the neighbouring link, so the rectangle was dropped. Ask which character contains that point, so a one-glyph link is matched against its own position. Signed-off-by: Samuel Mehrbrodt <samuel.mehrbrodt@collabora.com> Change-Id: I722aa2c83af50e99b8c06e051ff0bff9b0f76e0a Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/10251 Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com> Tested-by: Jenkins CPCI <releng@collaboraoffice.com> (cherry picked from commit 1284d1a3e3b93f02bd31068c9898743fa65e6e64) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210922 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
committed by
Xisco Fauli
parent
eddeaaf22b
commit
64b5464df4
@@ -444,6 +444,8 @@ bool lcl_TryMoveToNonHiddenField(SwEditShell& rShell, const SwTextNode& rNd, con
|
||||
::std::vector<SwRect> 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)
|
||||
{
|
||||
|
||||
@@ -20,16 +20,24 @@
|
||||
#include <com/sun/star/beans/XPropertySet.hpp>
|
||||
#include <com/sun/star/drawing/XShape.hpp>
|
||||
#include <com/sun/star/text/XTextDocument.hpp>
|
||||
#include <com/sun/star/text/XTextField.hpp>
|
||||
#include <com/sun/star/text/ControlCharacter.hpp>
|
||||
#include <com/sun/star/text/ReferenceFieldPart.hpp>
|
||||
#include <com/sun/star/text/ReferenceFieldSource.hpp>
|
||||
#include <com/sun/star/container/XNamed.hpp>
|
||||
#include <com/sun/star/document/XFilter.hpp>
|
||||
#include <com/sun/star/document/XExporter.hpp>
|
||||
#include <com/sun/star/io/XOutputStream.hpp>
|
||||
|
||||
#include <comphelper/scopeguard.hxx>
|
||||
#include <comphelper/propertysequence.hxx>
|
||||
#include <comphelper/scopeguard.hxx>
|
||||
#include <comphelper/sequenceashashmap.hxx>
|
||||
#include <basegfx/vector/b2dsize.hxx>
|
||||
#include <unotools/tempfile.hxx>
|
||||
#include <vcl/filter/pdfdocument.hxx>
|
||||
#include <vcl/settings.hxx>
|
||||
#include <vcl/svapp.hxx>
|
||||
#include <tools/zcodec.hxx>
|
||||
#include <vcl/graphicfilter.hxx>
|
||||
#include <basegfx/matrix/b2dhommatrix.hxx>
|
||||
@@ -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<lang::XComponent>& xComponent,
|
||||
float fCharHeight)
|
||||
{
|
||||
uno::Reference<text::XTextDocument> xTextDocument(xComponent, uno::UNO_QUERY);
|
||||
uno::Reference<text::XText> xText = xTextDocument->getText();
|
||||
uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
|
||||
uno::Reference<lang::XMultiServiceFactory> xFactory(xComponent, uno::UNO_QUERY);
|
||||
uno::Reference<beans::XPropertySet> 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<text::XTextContent> xBookmark(
|
||||
xFactory->createInstance(u"com.sun.star.text.Bookmark"_ustr), uno::UNO_QUERY);
|
||||
uno::Reference<container::XNamed> 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<text::XTextField> xField(
|
||||
xFactory->createInstance(u"com.sun.star.text.TextField.GetReference"_ustr),
|
||||
uno::UNO_QUERY);
|
||||
uno::Reference<beans::XPropertySet> 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<vcl::pdf::PDFiumDocument>& pPdfDocument)
|
||||
{
|
||||
std::unique_ptr<vcl::pdf::PDFiumPage> 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.
|
||||
|
||||
Reference in New Issue
Block a user