editeng/source/editeng/impedit3.cxx      |   52 +++++++++++++++++---------
 sc/qa/extras/scpdfexport.cxx             |   60 +++++++++++++++++++++++++++++++
 sc/qa/extras/testdocuments/tdf159065.ods |binary
 3 files changed, 94 insertions(+), 18 deletions(-)

New commits:
commit 4202218af61573825edfe5ec5a1bba710d7df1f7
Author:     Tibor Nagy <tibor.nagy.ext...@allotropia.de>
AuthorDate: Tue Jan 9 23:54:43 2024 +0100
Commit:     Nagy Tibor <tibor.nagy.ext...@allotropia.de>
CommitDate: Wed Jan 10 08:49:29 2024 +0100

    tdf#159065 sc: fix PDF/UA link annotation
    
    Change-Id: I1586608780cc82908eaef62158887afee69c148f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/161843
    Tested-by: Jenkins
    Reviewed-by: Nagy Tibor <tibor.nagy.ext...@allotropia.de>

diff --git a/editeng/source/editeng/impedit3.cxx 
b/editeng/source/editeng/impedit3.cxx
index d50c2eb1338e..bc93802694fd 100644
--- a/editeng/source/editeng/impedit3.cxx
+++ b/editeng/source/editeng/impedit3.cxx
@@ -3855,6 +3855,25 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                              ' ' == aText[nTextStart + 
nTextLen - 1] )
                                             --nTextLen;
 
+                                        // PDF export:
+                                        const SvxFieldData* pFieldData = 
nullptr;
+                                        if (pPDFExtOutDevData)
+                                        {
+                                            if (rTextPortion.GetKind() == 
PortionKind::FIELD)
+                                            {
+                                                const EditCharAttrib* pAttr = 
pPortion->GetNode()->GetCharAttribs().FindFeature(nIndex);
+                                                const SvxFieldItem* pFieldItem 
= dynamic_cast<const SvxFieldItem*>(pAttr->GetItem());
+                                                if (pFieldItem)
+                                                {
+                                                    pFieldData = 
pFieldItem->GetField();
+                                                    auto pUrlField = 
dynamic_cast<const SvxURLField*>(pFieldData);
+                                                    if (pUrlField)
+                                                        if 
(pPDFExtOutDevData->GetIsExportTaggedPDF())
+                                                            
pPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Link, "Link");
+                                                }
+                                            }
+                                        }
+
                                         // output directly
                                         aTmpFont.QuickDrawText( &rOutDev, 
aRealOutPos, aText, nTextStart, nTextLen, pDXArray, pKashidaArray );
 
@@ -3869,27 +3888,24 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                         }
 
                                         // PDF export:
-                                        if ( pPDFExtOutDevData )
+                                        if (pPDFExtOutDevData)
                                         {
-                                            if ( rTextPortion.GetKind() == 
PortionKind::FIELD )
+                                            if (auto pUrlField = 
dynamic_cast<const SvxURLField*>(pFieldData))
                                             {
-                                                const EditCharAttrib* pAttr = 
pPortion->GetNode()->GetCharAttribs().FindFeature(nIndex);
-                                                const SvxFieldItem* pFieldItem 
= dynamic_cast<const SvxFieldItem*>(pAttr->GetItem());
-                                                if( pFieldItem )
+                                                Point aTopLeft(aTmpPos);
+                                                
aTopLeft.AdjustY(-(pLine->GetMaxAscent()));
+
+                                                tools::Rectangle 
aRect(aTopLeft, rTextPortion.GetSize());
+                                                vcl::PDFExtOutDevBookmarkEntry 
aBookmark;
+                                                aBookmark.nLinkId = 
pPDFExtOutDevData->CreateLink(aRect, pUrlField->GetRepresentation());
+                                                aBookmark.aBookmark = 
pUrlField->GetURL();
+                                                std::vector< 
vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = 
pPDFExtOutDevData->GetBookmarks();
+                                                
rBookmarks.push_back(aBookmark);
+
+                                                if 
(pPDFExtOutDevData->GetIsExportTaggedPDF())
                                                 {
-                                                    const SvxFieldData* 
pFieldData = pFieldItem->GetField();
-                                                    if ( auto pUrlField = 
dynamic_cast< const SvxURLField* >( pFieldData ) )
-                                                    {
-                                                        Point aTopLeft( 
aTmpPos );
-                                                        aTopLeft.AdjustY( 
-(pLine->GetMaxAscent()) );
-
-                                                        tools::Rectangle 
aRect( aTopLeft, rTextPortion.GetSize() );
-                                                        
vcl::PDFExtOutDevBookmarkEntry aBookmark;
-                                                        aBookmark.nLinkId = 
pPDFExtOutDevData->CreateLink(aRect, pUrlField->GetRepresentation());
-                                                        aBookmark.aBookmark = 
pUrlField->GetURL();
-                                                        std::vector< 
vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = 
pPDFExtOutDevData->GetBookmarks();
-                                                        rBookmarks.push_back( 
aBookmark );
-                                                    }
+                                                    
pPDFExtOutDevData->SetStructureAttributeNumerical(vcl::PDFWriter::LinkAnnotation,
 aBookmark.nLinkId);
+                                                    
pPDFExtOutDevData->EndStructureElement();
                                                 }
                                             }
                                         }
diff --git a/sc/qa/extras/scpdfexport.cxx b/sc/qa/extras/scpdfexport.cxx
index 3bfa55eb3dcf..ae85dbb9b442 100644
--- a/sc/qa/extras/scpdfexport.cxx
+++ b/sc/qa/extras/scpdfexport.cxx
@@ -65,6 +65,7 @@ public:
     void testExportFitToPage_Tdf103516();
     void testUnoCommands_Tdf120161();
     void testTdf64703_hiddenPageBreak();
+    void testTdf159065();
     void testTdf123870();
     void testTdf143978();
     void testTdf120190();
@@ -77,6 +78,7 @@ public:
     CPPUNIT_TEST(testExportFitToPage_Tdf103516);
     CPPUNIT_TEST(testUnoCommands_Tdf120161);
     CPPUNIT_TEST(testTdf64703_hiddenPageBreak);
+    CPPUNIT_TEST(testTdf159065);
     CPPUNIT_TEST(testTdf123870);
     CPPUNIT_TEST(testTdf143978);
     CPPUNIT_TEST(testTdf120190);
@@ -394,6 +396,64 @@ void ScPDFExportTest::testTdf64703_hiddenPageBreak()
     }
 }
 
+void ScPDFExportTest::testTdf159065()
+{
+    loadFromFile(u"tdf159065.ods");
+    uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
+
+    // A1:A3
+    ScRange range1(0, 0, 0, 0, 2, 0);
+    exportToPDF(xModel, range1);
+
+    vcl::filter::PDFDocument aDocument;
+    SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+    CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+    // The document has one page.
+    std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+    vcl::filter::PDFObjectElement* pContents = 
aPages[0]->LookupObject("Contents"_ostr);
+    CPPUNIT_ASSERT(pContents);
+    vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
+    CPPUNIT_ASSERT(pStream);
+
+    SvMemoryStream& rObjectStream = pStream->GetMemory();
+    // Uncompress it.
+    SvMemoryStream aUncompressed;
+    ZCodec aZCodec;
+    aZCodec.BeginCompression();
+    rObjectStream.Seek(0);
+    aZCodec.Decompress(rObjectStream, aUncompressed);
+    CPPUNIT_ASSERT(aZCodec.EndCompression());
+
+    auto pStart = static_cast<const char*>(aUncompressed.GetData());
+    const char* const pEnd = pStart + aUncompressed.GetSize();
+
+    auto nLink(0);
+    auto nLine(0);
+    while (true)
+    {
+        ++nLine;
+        auto const pLine = ::std::find(pStart, pEnd, '
');
+        if (pLine == pEnd)
+        {
+            break;
+        }
+        std::string_view const line(pStart, pLine - pStart);
+        pStart = pLine + 1;
+        if (!line.empty() && line[0] != '%')
+        {
+            ::std::cerr << nLine << ": " << line << "
 ";
+            if (o3tl::starts_with(line, "/Link<</MCID") && 
o3tl::ends_with(line, ">>BDC"))
+                nLink++;
+        }
+    }
+
+    // The tagged PDF file have to contains two link annotation
+    CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nLink)>(2), nLink);
+}
+
 void ScPDFExportTest::testTdf123870()
 {
     loadFromFile(u"tdf123870.ods");
diff --git a/sc/qa/extras/testdocuments/tdf159065.ods 
b/sc/qa/extras/testdocuments/tdf159065.ods
new file mode 100644
index 000000000000..c75d782f9914
Binary files /dev/null and b/sc/qa/extras/testdocuments/tdf159065.ods differ

Reply via email to