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);