filter/source/pdf/impdialog.cxx    |   14 ++++++++++++++
 filter/source/pdf/impdialog.hxx    |    2 ++
 filter/source/pdf/pdfexport.cxx    |    4 ++++
 filter/uiconfig/ui/pdflinkspage.ui |   20 ++++++++++++++++++++
 include/vcl/pdfwriter.hxx          |    3 ++-
 vcl/source/gdi/pdfwriter_impl.cxx  |    2 ++
 6 files changed, 44 insertions(+), 1 deletion(-)

New commits:
commit 32aa68a652007a718f9e3f41b49e191f0527d518
Author:     Floris Bos <b...@je-eigen-domein.nl>
AuthorDate: Sun Jul 13 13:49:55 2025 +0200
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Sun Jul 20 22:13:21 2025 +0200

    tdf#167490: PDF export: add option to disable external links in PDF
    
    - Adds a new PDFViewSelection option to tell LibreOffice NOT to include
      hyperlinks to external files/URLs in the generated PDF document.
    - New option can be used both through CLI and through a new radio button in 
GUI.
    
    Known limitations: when this option is used it prevents LibreOffice from 
generating
    link annotations in the generated PDF file.
    But it does not prevent that some PDF viewers may still transform anything 
that
    looks like an URL in the document text to a link automatically, even when 
there
    is no explicit link in the PDF file at all.
    (If that is undesired, check the settings of your PDF client to see if
    that can be changed. E.g. if you intend to serve the generated PDF through
    a web application to end-users, an option would be to use the pdf.js
    Javascript based PDF viewer that has an "enableAutoLinking: false" option)
    
    Change-Id: I264f85f7adb38a748f292badd6c5aac60fbd9957
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187800
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>
    Tested-by: Jenkins

diff --git a/filter/source/pdf/impdialog.cxx b/filter/source/pdf/impdialog.cxx
index 19a5c7f2633a..788eccd670f5 100644
--- a/filter/source/pdf/impdialog.cxx
+++ b/filter/source/pdf/impdialog.cxx
@@ -1598,12 +1598,14 @@ ImpPDFTabLinksPage::ImpPDFTabLinksPage(weld::Container* 
pPage, weld::DialogContr
     , mbOpnLnksDefaultUserState(false)
     , mbOpnLnksLaunchUserState(false)
     , mbOpnLnksBrowserUserState(false)
+    , mbOpnLnksRemoveUserState(false)
     , m_xCbExprtBmkrToNmDst(m_xBuilder->weld_check_button(u"export"_ustr))
     , m_xCbOOoToPDFTargets(m_xBuilder->weld_check_button(u"convert"_ustr))
     , 
m_xCbExportRelativeFsysLinks(m_xBuilder->weld_check_button(u"exporturl"_ustr))
     , m_xRbOpnLnksDefault(m_xBuilder->weld_radio_button(u"default"_ustr))
     , m_xRbOpnLnksLaunch(m_xBuilder->weld_radio_button(u"openpdf"_ustr))
     , m_xRbOpnLnksBrowser(m_xBuilder->weld_radio_button(u"openinternet"_ustr))
+    , 
m_xRbOpnLnksRemove(m_xBuilder->weld_radio_button(u"removeexternallinks"_ustr))
 {
 }
 
@@ -1632,6 +1634,8 @@ void ImpPDFTabLinksPage::GetFilterConfigItem( 
ImpPDFTabDialog* pParent  )
         mbOpnLnksLaunchUserState =  m_xRbOpnLnksLaunch->get_active();
         mbOpnLnksBrowserUserState = m_xRbOpnLnksBrowser->get_active();
     }
+    // Option to remove links is possible regardless of PDF version
+    mbOpnLnksRemoveUserState = m_xRbOpnLnksRemove->get_active();
     // the control states, or the saved is used
     // to form the stored selection
     pParent->mnViewPDFMode = 0;
@@ -1639,6 +1643,8 @@ void ImpPDFTabLinksPage::GetFilterConfigItem( 
ImpPDFTabDialog* pParent  )
         pParent->mnViewPDFMode = 2;
     else if( mbOpnLnksLaunchUserState )
         pParent->mnViewPDFMode = 1;
+    else if( mbOpnLnksRemoveUserState )
+        pParent->mnViewPDFMode = 3;
 
     pParent->mbConvertOOoTargets = m_xCbOOoToPDFTargets->get_active();
     pParent->mbExportBmkToPDFDestination = m_xCbExprtBmkrToNmDst->get_active();
@@ -1668,6 +1674,10 @@ void ImpPDFTabLinksPage::SetFilterConfigItem( const  
ImpPDFTabDialog* pParent )
         m_xRbOpnLnksBrowser->set_active(true);
         mbOpnLnksBrowserUserState = true;
         break;
+    case 3:
+        m_xRbOpnLnksRemove->set_active(true);
+        mbOpnLnksRemoveUserState = true;
+        break;
     }
 
     // now check the status of PDF/A selection
@@ -1693,6 +1703,7 @@ void ImpPDFTabLinksPage::ImplPDFALinkControl( bool 
bEnableLaunch )
         m_xRbOpnLnksDefault->set_active(mbOpnLnksDefaultUserState);
         m_xRbOpnLnksLaunch->set_active(mbOpnLnksLaunchUserState);
         m_xRbOpnLnksBrowser->set_active(mbOpnLnksBrowserUserState);
+        m_xRbOpnLnksRemove->set_active(mbOpnLnksRemoveUserState);
     }
     else
     {
@@ -1700,6 +1711,7 @@ void ImpPDFTabLinksPage::ImplPDFALinkControl( bool 
bEnableLaunch )
         mbOpnLnksDefaultUserState = m_xRbOpnLnksDefault->get_active();
         mbOpnLnksLaunchUserState = m_xRbOpnLnksLaunch->get_active();
         mbOpnLnksBrowserUserState = m_xRbOpnLnksBrowser->get_active();
+        mbOpnLnksRemoveUserState = m_xRbOpnLnksRemove->get_active();
         m_xRbOpnLnksLaunch->set_sensitive(false);
         if (mbOpnLnksLaunchUserState)
             m_xRbOpnLnksBrowser->set_active(true);
@@ -1712,6 +1724,7 @@ IMPL_LINK_NOARG(ImpPDFTabLinksPage, 
ClickRbOpnLnksDefaultHdl, weld::Toggleable&,
     mbOpnLnksDefaultUserState = m_xRbOpnLnksDefault->get_active();
     mbOpnLnksLaunchUserState = m_xRbOpnLnksLaunch->get_active();
     mbOpnLnksBrowserUserState = m_xRbOpnLnksBrowser->get_active();
+    mbOpnLnksRemoveUserState = m_xRbOpnLnksRemove->get_active();
 }
 
 /// Reset the memory of a launch action present when PDF/A-1 was requested
@@ -1720,6 +1733,7 @@ IMPL_LINK_NOARG(ImpPDFTabLinksPage, 
ClickRbOpnLnksBrowserHdl, weld::Toggleable&,
     mbOpnLnksDefaultUserState = m_xRbOpnLnksDefault->get_active();
     mbOpnLnksLaunchUserState = m_xRbOpnLnksLaunch->get_active();
     mbOpnLnksBrowserUserState = m_xRbOpnLnksBrowser->get_active();
+    mbOpnLnksRemoveUserState = m_xRbOpnLnksRemove->get_active();
 }
 
 ImplErrorDialog::ImplErrorDialog(weld::Window* pParent, const 
std::set<vcl::PDFWriter::ErrorCode>& rErrors)
diff --git a/filter/source/pdf/impdialog.hxx b/filter/source/pdf/impdialog.hxx
index aff0023acda0..6d3bc4a3b695 100644
--- a/filter/source/pdf/impdialog.hxx
+++ b/filter/source/pdf/impdialog.hxx
@@ -389,6 +389,7 @@ class ImpPDFTabLinksPage : public SfxTabPage
     bool                        mbOpnLnksDefaultUserState;
     bool                        mbOpnLnksLaunchUserState;
     bool                        mbOpnLnksBrowserUserState;
+    bool                        mbOpnLnksRemoveUserState;
 
     std::unique_ptr<weld::CheckButton> m_xCbExprtBmkrToNmDst;
     std::unique_ptr<weld::CheckButton> m_xCbOOoToPDFTargets;
@@ -396,6 +397,7 @@ class ImpPDFTabLinksPage : public SfxTabPage
     std::unique_ptr<weld::RadioButton> m_xRbOpnLnksDefault;
     std::unique_ptr<weld::RadioButton> m_xRbOpnLnksLaunch;
     std::unique_ptr<weld::RadioButton> m_xRbOpnLnksBrowser;
+    std::unique_ptr<weld::RadioButton> m_xRbOpnLnksRemove;
 
     DECL_LINK(ClickRbOpnLnksDefaultHdl, weld::Toggleable&, void);
     DECL_LINK(ClickRbOpnLnksBrowserHdl, weld::Toggleable&, void);
diff --git a/filter/source/pdf/pdfexport.cxx b/filter/source/pdf/pdfexport.cxx
index b89244fa0242..8b50dc1bbe45 100644
--- a/filter/source/pdf/pdfexport.cxx
+++ b/filter/source/pdf/pdfexport.cxx
@@ -989,6 +989,10 @@ bool PDFExport::Export( const OUString& rFile, const 
Sequence< PropertyValue >&
                     // view PDF through an Internet browser
                     aContext.DefaultLinkAction = 
vcl::PDFWriter::URIActionDestination;
                     break;
+                case 3:
+                    // do not emit Link annotations for external links
+                    aContext.DefaultLinkAction = 
vcl::PDFWriter::RemoveExternalLinks;
+                    break;
                 }
                 aContext.ConvertOOoTargetToPDFTarget = 
bConvertOOoTargetToPDFTarget;
 
diff --git a/filter/uiconfig/ui/pdflinkspage.ui 
b/filter/uiconfig/ui/pdflinkspage.ui
index f1229cc9681e..93d3047737a9 100644
--- a/filter/uiconfig/ui/pdflinkspage.ui
+++ b/filter/uiconfig/ui/pdflinkspage.ui
@@ -175,6 +175,26 @@
                 <property name="top-attach">2</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkRadioButton" id="removeexternallinks">
+                <property name="label" translatable="yes" 
context="pdflinkspage|removeexternallinks">Remove cross-document 
links</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">False</property>
+                <property name="use-underline">True</property>
+                <property name="draw-indicator">True</property>
+                <property name="group">default</property>
+                <child internal-child="accessible">
+                  <object class="AtkObject" id="removeexternallinks-atkobject">
+                    <property name="AtkObject::accessible-description" 
translatable="yes" 
context="pdflinkspage|extended_tip|removeexternallinks">Cross-document links 
will be removed from your PDF document.</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left-attach">0</property>
+                <property name="top-attach">3</property>
+              </packing>
+            </child>
           </object>
         </child>
         <child type="label">
diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx
index 5cd034c9878a..91efaeeeee82 100644
--- a/include/vcl/pdfwriter.hxx
+++ b/include/vcl/pdfwriter.hxx
@@ -609,7 +609,8 @@ public:
     {
         URIAction,
         URIActionDestination,
-        LaunchAction
+        LaunchAction,
+        RemoveExternalLinks
     };
 
     struct PDFDocInfo
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index bf564d0571e0..4d5ffe83c18a 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -3593,6 +3593,8 @@ bool PDFWriterImpl::emitLinkAnnotations()
         const PDFLink& rLink            = m_aLinks[i];
         if( ! updateObject( rLink.m_nObject ) )
             continue;
+        if( m_aContext.DefaultLinkAction == PDFWriter::RemoveExternalLinks && 
rLink.m_nDest < 0 )
+            continue;
 
         OStringBuffer aLine( 1024 );
         COSWriter aWriter(aLine, m_aContext.Encryption.getParams(), 
m_pPDFEncryptor);

Reply via email to