desktop/source/lib/lokinteractionhandler.cxx | 4 include/sfx2/docfile.hxx | 8 include/vcl/embeddedfontsmanager.hxx | 12 - offapi/UnoApi_offapi.mk | 1 offapi/com/sun/star/document/FontsDisallowEditingRequest.idl | 26 ++ sd/source/ui/func/fudraw.cxx | 2 sd/source/ui/unoidl/unomodel.cxx | 2 sfx2/source/doc/docfile.cxx | 15 + sfx2/source/doc/objmisc.cxx | 2 sfx2/source/doc/objserv.cxx | 3 sfx2/source/view/viewfrm.cxx | 12 - uui/inc/strings.hrc | 3 uui/source/iahndl.cxx | 49 ++++ uui/source/iahndl.hxx | 3 vcl/source/font/EOTConverter.cxx | 2 vcl/source/gdi/embeddedfontsmanager.cxx | 128 +++++++++-- 16 files changed, 247 insertions(+), 25 deletions(-)
New commits: commit 14d2a0e9f8a46366ca740bc96fa582875aa568f0 Author: Mike Kaganski <[email protected]> AuthorDate: Mon Aug 11 17:55:27 2025 +0500 Commit: Miklos Vajna <[email protected]> CommitDate: Thu Aug 14 09:59:02 2025 +0200 tdf#145967: Initial support for docs with restricted embedded fonts Previously, we silently discarded fonts which rights disallowed editing of documents. This change introduces a choice, where user can opt to either discard the restricted fonts, or switch to read-only mode. 1. When user opens a document with restricted embedded fonts, which are already installed on the system, those fonts are not considered as restricted. 2. Trying to switch to edit mode will show the dialog again, allowing to discard those fonts to allow editing. 3. Saving the document will discard restricted fonts not installed on the system in any case. 4. Even though saving discards those fonts from file, it doesn't switch to edit mode, because it doesn't reload the document, that is needed to reload embedded font data (and release respective font files). 5. Opening a document initially in read-only mode skips the dialog. TODO: avoid showing those fonts in the font lists. Change-Id: Ia7c9b6c3f727720e64e4df8d1c4a8e3641b067bd Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189392 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/desktop/source/lib/lokinteractionhandler.cxx b/desktop/source/lib/lokinteractionhandler.cxx index 723a87fb934b..858793b856b9 100644 --- a/desktop/source/lib/lokinteractionhandler.cxx +++ b/desktop/source/lib/lokinteractionhandler.cxx @@ -23,6 +23,7 @@ #include <cppuhelper/supportsservice.hxx> #include <com/sun/star/document/BrokenPackageRequest.hpp> +#include <com/sun/star/document/FontsDisallowEditingRequest.hpp> #include <com/sun/star/beans/NamedValue.hpp> #include <com/sun/star/task/XInteractionAbort.hpp> #include <com/sun/star/task/XInteractionApprove.hpp> @@ -153,6 +154,9 @@ bool ShouldFallbackToStandard(const uno::Reference<task::XInteractionRequest>& x if (document::FilterOptionsRequest aStruct; request >>= aStruct) return true; + if (document::FontsDisallowEditingRequest aStruct; request >>= aStruct) + return true; + if (beans::NamedValue aStruct; request >>= aStruct) if (aStruct.Name == "LoadReadOnlyRequest") if (OUString aFileName; aStruct.Value >>= aFileName) diff --git a/include/sfx2/docfile.hxx b/include/sfx2/docfile.hxx index e15983b65a6c..ec95a16bacf9 100644 --- a/include/sfx2/docfile.hxx +++ b/include/sfx2/docfile.hxx @@ -209,7 +209,13 @@ public: // independent of later changes via SetOpenMode; used for SID_RELOAD: [[nodiscard]] bool IsOriginallyLoadedReadOnly() const; - // Activates the collected embedded fonts. + // Whether the medium has embedded fonts that disallow editing of the document, + // meaning that it can't be switched to editing mode at all, without reloading + // and discarding these fonts: + [[nodiscard]] bool HasRestrictedFonts() const; + + // Activates collected embedded fonts. Here it may ask user to approve use of restricted fonts, + // and switch tyo read-only mode. void activateEmbeddedFonts(); [[nodiscard]] bool IsRepairPackage() const; diff --git a/include/vcl/embeddedfontsmanager.hxx b/include/vcl/embeddedfontsmanager.hxx index 798de1f310fb..a1cb9e3021aa 100644 --- a/include/vcl/embeddedfontsmanager.hxx +++ b/include/vcl/embeddedfontsmanager.hxx @@ -20,6 +20,7 @@ namespace com::sun::star::frame { class XModel; } namespace com::sun::star::io { class XInputStream; } +namespace com::sun::star::task { class XInteractionHandler; } namespace com::sun::star::uno { template <typename > class Reference; } /** Helper functions for handling embedded fonts in documents. */ @@ -61,8 +62,17 @@ public: /** Adds the passed fonts to the list of known fonts. The fonts are used only until application exit. + + If some fonts are restricted (i.e., block document editing), and 'silentlyAllowRestricted' is + false, it checks if interaction handle is set; if it is, then asks user to approve read-only + mode. If 'silentlyAllowRestricted' is true; or if user approved switching to read-only mode, + then it activates all the fonts (and sets 'activatedRestrictedFonts' to true). Otherwise, it + removes these fonts from 'fonts', releases them, and only activates unrestricted fonts. */ - static void activateFonts(std::vector<std::pair<OUString, OUString>>& fonts); + static void activateFonts(std::vector<std::pair<OUString, OUString>>& fonts, + bool silentlyAllowRestrictedFonts, + const css::uno::Reference<css::task::XInteractionHandler>& xHandler, + bool& activatedRestrictedFonts); /** Returns if the restrictions specified in the font (if present) allow embedding diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk index 40e1f96b70c4..90049126196c 100644 --- a/offapi/UnoApi_offapi.mk +++ b/offapi/UnoApi_offapi.mk @@ -2210,6 +2210,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/document,\ EventObject \ ExoticFileLoadException \ FilterOptionsRequest \ + FontsDisallowEditingRequest \ LinkUpdateModes \ LockFileCorruptRequest \ LockFileIgnoreRequest \ diff --git a/offapi/com/sun/star/document/FontsDisallowEditingRequest.idl b/offapi/com/sun/star/document/FontsDisallowEditingRequest.idl new file mode 100644 index 000000000000..5a47f440bff6 --- /dev/null +++ b/offapi/com/sun/star/document/FontsDisallowEditingRequest.idl @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +module com { module sun { module star { module document { + +/** Some fonts embedded in the document have no editing permissions. + The document can only be opened read-only. + + @since LibreOffice 26.2 +*/ +exception FontsDisallowEditingRequest : ::com::sun::star::uno::Exception +{ + /** The names of the fonts that lack editing permission ( -separated) */ + string aFontNames; + +}; + +}; }; }; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sd/source/ui/func/fudraw.cxx b/sd/source/ui/func/fudraw.cxx index 28e1bf7e96f3..0df63c9d5e7d 100644 --- a/sd/source/ui/func/fudraw.cxx +++ b/sd/source/ui/func/fudraw.cxx @@ -245,7 +245,7 @@ bool FuDraw::MouseMove(const MouseEvent& rMEvt) bOrtho = rMEvt.IsShift() != pFrameView->IsOrtho(); } - bool bSnapModPressed = rMEvt.IsMod2(); + bool bSnapModPressed = mpView->IsDragObj() ? rMEvt.IsMod2() : rMEvt.IsMod1(); mpView->SetDragWithCopy(rMEvt.IsMod1() && pFrameView->IsDragWithCopy()); if (mpView->IsOrtho() != bOrtho) diff --git a/sd/source/ui/unoidl/unomodel.cxx b/sd/source/ui/unoidl/unomodel.cxx index d89f6b39797c..d6b0a6c1f706 100644 --- a/sd/source/ui/unoidl/unomodel.cxx +++ b/sd/source/ui/unoidl/unomodel.cxx @@ -2893,7 +2893,7 @@ uno::Any SAL_CALL SdXImpressDocument::getPropertyValue( const OUString& Property uno::Any aAny; if( nullptr == mpDoc ) - throw lang::DisposedException(); + throw lang::DisposedException({}, getXWeak()); const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(PropertyName); diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx index 16ecc6ea1ebf..a5ec8ab17e0f 100644 --- a/sfx2/source/doc/docfile.cxx +++ b/sfx2/source/doc/docfile.cxx @@ -414,6 +414,9 @@ public: /// if true, xStorage is an inner package and not directly from xStream bool m_bODFWholesomeEncryption = false; + /// If there are such fonts, the document can't be set into editing mode; + /// these can't be embedded on save. + bool hasRestrictedFonts = false; /// font family, file URL std::vector<std::pair<OUString, OUString>> m_aEmbeddedFonts; std::vector<std::pair<OUString, OUString>> m_aEmbeddedFontsToActivate; @@ -4000,6 +4003,10 @@ bool SfxMedium::IsReadOnly() const bReadOnly = pItem->GetValue(); } + // d) Embedded fonts may disallow editing + if (!bReadOnly) + bReadOnly = HasRestrictedFonts(); + return bReadOnly; } @@ -4018,8 +4025,11 @@ bool SfxMedium::IsOriginallyLoadedReadOnly() const return pImpl->m_bOriginallyLoadedReadOnly; } +bool SfxMedium::HasRestrictedFonts() const { return pImpl->hasRestrictedFonts; } + void SfxMedium::TransferEmbeddedFontsTo(SfxMedium& target) { + target.pImpl->hasRestrictedFonts = target.pImpl->hasRestrictedFonts || pImpl->hasRestrictedFonts; target.pImpl->m_aEmbeddedFonts.insert(target.pImpl->m_aEmbeddedFonts.end(), pImpl->m_aEmbeddedFonts.begin(), pImpl->m_aEmbeddedFonts.end()); @@ -4039,7 +4049,10 @@ void SfxMedium::AddEmbeddedFonts( void SfxMedium::activateEmbeddedFonts() { - EmbeddedFontsManager::activateFonts(pImpl->m_aEmbeddedFontsToActivate); + bool bActivatedRestrictedFonts; + EmbeddedFontsManager::activateFonts(pImpl->m_aEmbeddedFontsToActivate, IsReadOnly(), + GetInteractionHandler(), bActivatedRestrictedFonts); + pImpl->hasRestrictedFonts = pImpl->hasRestrictedFonts || bActivatedRestrictedFonts; pImpl->m_aEmbeddedFonts.insert(pImpl->m_aEmbeddedFonts.end(), pImpl->m_aEmbeddedFontsToActivate.begin(), pImpl->m_aEmbeddedFontsToActivate.end()); diff --git a/sfx2/source/doc/objmisc.cxx b/sfx2/source/doc/objmisc.cxx index fe51d08b98a2..e3f3fe7d54b8 100644 --- a/sfx2/source/doc/objmisc.cxx +++ b/sfx2/source/doc/objmisc.cxx @@ -434,7 +434,7 @@ void SfxObjectShell::SetReadOnly() bool SfxObjectShell::IsReadOnly() const { - return pImpl->bReadOnlyUI || pMedium == nullptr; + return pImpl->bReadOnlyUI || pMedium == nullptr || pMedium->HasRestrictedFonts(); } diff --git a/sfx2/source/doc/objserv.cxx b/sfx2/source/doc/objserv.cxx index 1e3a0d9a4972..6a97e49fb6ff 100644 --- a/sfx2/source/doc/objserv.cxx +++ b/sfx2/source/doc/objserv.cxx @@ -1303,7 +1303,8 @@ void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq) if ( ( nId == SID_SAVEASDOC || nId == SID_SAVEASREMOTE ) && nErrorCode == ERRCODE_NONE ) { const SfxBoolItem* saveTo = rReq.GetArg<SfxBoolItem>(SID_SAVETO); - if (saveTo == nullptr || !saveTo->GetValue()) + // IsReadOnly may still return true, e.g. when embedded fonts disallow editing + if ((saveTo == nullptr || !saveTo->GetValue()) && !IsReadOnly()) { if (SfxViewFrame* pFrame = GetFrame()) pFrame->RemoveInfoBar(u"readonly"); diff --git a/sfx2/source/view/viewfrm.cxx b/sfx2/source/view/viewfrm.cxx index 0ffdfe9a954d..79cc5beddff6 100644 --- a/sfx2/source/view/viewfrm.cxx +++ b/sfx2/source/view/viewfrm.cxx @@ -278,6 +278,9 @@ bool physObjIsOlder(INetURLObject const & aMedObj, INetURLObject const & aPhysOb void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) { + // Open as editable? + std::optional<bool> oForEdit; + SfxObjectShell* pSh = GetObjectShell(); switch ( rReq.GetSlot() ) { @@ -402,6 +405,8 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) nOpenMode = pSh->IsOriginallyReadOnlyMedium() ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE; aReadOnlyUIGuard.m_bSetRO = false; + if (pMed->HasRestrictedFonts()) + bNeedsReload = true; // Let it ask user, reload fonts, etc. // if only the view was in the readonly mode then there is no need to do the reload if ( !pSh->IsReadOnlyMedium() ) @@ -609,6 +614,8 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) || bNeedsReload) ); rReq.AppendItem( SfxBoolItem( SID_SILENT, true )); + oForEdit = !aReadOnlyUIGuard.m_bSetRO; + [[fallthrough]]; //TODO ??? } @@ -637,7 +644,8 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) m_pImpl->bReloading = true; const SfxStringItem* pURLItem = rReq.GetArg<SfxStringItem>(SID_FILE_NAME); // Open as editable? - bool bForEdit = !pSh->IsReadOnly(); + if (!oForEdit.has_value()) + oForEdit = !pSh->IsReadOnly(); // If possible ask the User bool bDo = GetViewShell()->PrepareClose(); @@ -825,7 +833,7 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) } else if ( rReq.GetSlot() == SID_EDITDOC || rReq.GetSlot() == SID_READONLYDOC ) { - xNewObj->SetReadOnlyUI( !bForEdit ); + xNewObj->SetReadOnlyUI(!*oForEdit); } #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT diff --git a/uui/inc/strings.hrc b/uui/inc/strings.hrc index 66cc704e97fb..28c0ccd83ad0 100644 --- a/uui/inc/strings.hrc +++ b/uui/inc/strings.hrc @@ -81,4 +81,7 @@ #define STR_LOADREADONLY_MSG NC_("STR_LOADREADONLY_MSG", "The author would like you to open '$(ARG1)' as read-only unless you need to make changes. Open as read-only?") #define STR_VERIFY_CERT NC_("STR_VERIFY_CERT", "You need to view the certificate first.") +#define STR_READONLY_FONT_TITLE NC_("STR_READONLY_FONT_TITLE", "Font Disallows Editing") +#define STR_READONLY_FONT_MSG NC_("STR_READONLY_FONT_MSG", "One or more fonts embedded in the document have no editing permission. Open document in read-only mode? Pressing [ No ] will drop these fonts from the document: $(ARG1)") + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/uui/source/iahndl.cxx b/uui/source/iahndl.cxx index de8809c404fc..8bf09c3f3dee 100644 --- a/uui/source/iahndl.cxx +++ b/uui/source/iahndl.cxx @@ -26,6 +26,7 @@ #include <com/sun/star/container/XHierarchicalNameAccess.hpp> #include <com/sun/star/document/BrokenPackageRequest.hpp> #include <com/sun/star/document/ExoticFileLoadException.hpp> +#include <com/sun/star/document/FontsDisallowEditingRequest.hpp> #include <com/sun/star/task/DocumentMacroConfirmationRequest.hpp> #include <com/sun/star/java/WrongJavaVersionException.hpp> #include <com/sun/star/lang/XInitialization.hpp> @@ -697,6 +698,9 @@ UUIInteractionHelper::handleRequest_impl( return true; } + if (handleFontsDisallowEditingRequest(rRequest)) + return true; + task::ErrorCodeRequest2 aErrorCodeRequest2; if (aAnyRequest >>= aErrorCodeRequest2) { @@ -1151,6 +1155,51 @@ UUIInteractionHelper::handleBrokenPackageRequest( } } +bool UUIInteractionHelper::handleFontsDisallowEditingRequest( + const uno::Reference<task::XInteractionRequest>& rRequest) +{ + document::FontsDisallowEditingRequest aRequest; + if (!(rRequest->getRequest() >>= aRequest)) + return false; + + uno::Reference<task::XInteractionApprove> xApprove; + uno::Reference<task::XInteractionDisapprove> xDisapprove; + getContinuations(rRequest->getContinuations(), &xApprove, &xDisapprove); + + if (xApprove.is() && xDisapprove.is()) + { + std::locale aResLocale = Translate::Create("uui"); + OUString title(utl::ConfigManager::getProductName()); + + OUString title2 = Translate::get(STR_READONLY_FONT_TITLE, aResLocale); + if (!title.isEmpty() && !title2.isEmpty()) + title += " - "; + title += title2; + + OUString aMessage = replaceMessageWithArguments( + Translate::get(STR_READONLY_FONT_MSG, aResLocale), { aRequest.aFontNames }); + + switch (executeMessageBox(Application::GetFrameWeld(getParentXWindow()), title, aMessage, + VclMessageType::Question)) + { + case DialogMask::ButtonsNo: + if (xDisapprove.is()) + xDisapprove->select(); + break; + + case DialogMask::ButtonsYes: + if (xApprove.is()) + xApprove->select(); + break; + + default: + break; + } + } + + return true; +} + // ErrorResource Implementation bool ErrorResource::getString(ErrCode nErrorCode, OUString &rString) const { diff --git a/uui/source/iahndl.hxx b/uui/source/iahndl.hxx index d9a31ec77723..6677707b6b64 100644 --- a/uui/source/iahndl.hxx +++ b/uui/source/iahndl.hxx @@ -199,6 +199,9 @@ private: bool & bHasErrorString, OUString & rErrorString); + bool handleFontsDisallowEditingRequest( + const css::uno::Reference<css::task::XInteractionRequest>& rRequest); + bool handleLockedDocumentRequest( css::uno::Reference< css::task::XInteractionRequest > const & rRequest); diff --git a/vcl/source/font/EOTConverter.cxx b/vcl/source/font/EOTConverter.cxx index 89b1199e2a40..f3d3a9b1a849 100644 --- a/vcl/source/font/EOTConverter.cxx +++ b/vcl/source/font/EOTConverter.cxx @@ -92,7 +92,7 @@ bool EOTConverter::convert(std::vector<sal_uInt8>& rEotOutput) pEot->nWeight = pOS2->nWeightClass; // FIXME: Should use OS2->fsType, but some TrueType fonts set it to an over-restrictive value. // Since ATS does not enforce this on Mac OS X, we do not enforce it either. - pEot->nFsType = 0x0000; + pEot->nFsType = pOS2->nFsType; pEot->nUnicodeRange1 = pOS2->nUnicodeRange1; pEot->nUnicodeRange2 = pOS2->nUnicodeRange2; pEot->nUnicodeRange3 = pOS2->nUnicodeRange3; diff --git a/vcl/source/gdi/embeddedfontsmanager.cxx b/vcl/source/gdi/embeddedfontsmanager.cxx index df73525ee695..f69e373a50cf 100644 --- a/vcl/source/gdi/embeddedfontsmanager.cxx +++ b/vcl/source/gdi/embeddedfontsmanager.cxx @@ -11,6 +11,7 @@ #include <memory> #include <mutex> +#include <set> #include <unordered_map> #include <frozen/bits/defines.h> #include <frozen/bits/elsa_std.h> @@ -18,6 +19,7 @@ #include <config_folders.h> #include <config_eot.h> +#include <o3tl/temporary.hxx> #include <osl/file.hxx> #include <rtl/bootstrap.hxx> #include <rtl/uri.hxx> @@ -26,6 +28,7 @@ #include <vcl/embeddedfontsmanager.hxx> #include <com/sun/star/io/XInputStream.hpp> #include <comphelper/diagnose_ex.hxx> +#include <comphelper/interaction.hxx> #include <comphelper/propertyvalue.hxx> #include <comphelper/storagehelper.hxx> @@ -35,6 +38,8 @@ #include <sft.hxx> #include <com/sun/star/beans/StringPair.hpp> +#include <com/sun/star/document/FontsDisallowEditingRequest.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> #include <com/sun/star/frame/XModel2.hpp> #if ENABLE_EOT @@ -64,6 +69,7 @@ struct EmbeddedFontData { OUString familyName; int refcount = 0; + bool isRestricted = true; // Has restricted permissions, *and* isn't installed locally bool isActivated = false; }; @@ -71,6 +77,24 @@ std::mutex s_EmbeddedFontsMutex; // file URL -> EmbeddedFontData std::unordered_map<OUString, EmbeddedFontData> s_EmbeddedFonts; +bool isFontAvailableUnrestricted(std::u16string_view family, const OUString& fileURL) +{ + // Check if the font is already installed on system. It is either available and not among + // existing embedded files; or it could be listed among embedded, but without restrictions + // (because it was checked here before, and restrictions were removed). The idea is, that + // you can always edit your files with the restricted fonts taken from your own system. + + if (Application::GetDefaultDevice()->IsFontAvailable(family)) + { + std::unique_lock lock(s_EmbeddedFontsMutex); + auto it = s_EmbeddedFonts.find(fileURL); + if (it == s_EmbeddedFonts.end() || !it->second.isRestricted) + return true; + } + + return false; +} + void clearDir( const OUString& path ) { osl::Directory dir( path ); @@ -192,8 +216,9 @@ EmbeddedFontsManager::~EmbeddedFontsManager() COVERITY_NOEXCEPT_FALSE } } - // Failed to transfer the fonts to the document. Activate them here. - activateFonts(m_aAccumulatedFonts); + // Failed to transfer the fonts to the document. Activate them here, discarding restricted. + // They won't be released, so will stay until the application shutdown. + activateFonts(m_aAccumulatedFonts, false, {}, o3tl::temporary(bool())); } void EmbeddedFontsManager::clearTemporaryFontFiles() @@ -251,14 +276,6 @@ bool EmbeddedFontsManager::addEmbeddedFont( const uno::Reference< io::XInputStre { sufficientFontRights = sufficientTTFRights(fontData.data(), fontData.size(), FontRights::EditingAllowed); } - if( !sufficientFontRights ) - { - // It would be actually better to open the document in read-only mode in this case, - // warn the user about this, and provide a button to drop the font(s) in order - // to switch to editing. - SAL_INFO( "vcl.fonts", "Ignoring embedded font that is not usable for editing" ); - return false; - } if (bSubsetted) { @@ -293,12 +310,20 @@ bool EmbeddedFontsManager::addEmbeddedFont( const uno::Reference< io::XInputStre if (fileUrl.isEmpty()) return false; - // Register it, and increase its refcount in s_EmbeddedFonts + // Must not call isFontAvailableUnrestricted under unique_lock: it will deadlock + if (!sufficientFontRights && isFontAvailableUnrestricted(fontName, fileUrl)) + sufficientFontRights = true; + + // Register it / increase its refcount in s_EmbeddedFonts { std::unique_lock lock(s_EmbeddedFontsMutex); auto& rData = s_EmbeddedFonts[fileUrl]; - assert(rData.familyName.isEmpty() || rData.familyName == fontName); - rData.familyName = fontName; + if (rData.refcount == 0) + { + rData.familyName = fontName; + rData.isRestricted = !sufficientFontRights; + } + assert(rData.familyName == fontName); ++rData.refcount; } @@ -323,18 +348,83 @@ namespace }; } -void EmbeddedFontsManager::activateFonts(std::vector<std::pair<OUString, OUString>>& fonts) +void EmbeddedFontsManager::activateFonts(std::vector<std::pair<OUString, OUString>>& fonts, + bool silentlyAllowRestrictedFonts, + const uno::Reference<task::XInteractionHandler>& xHandler, + bool& activatedRestrictedFonts) { + activatedRestrictedFonts = false; if (fonts.empty()) return; std::vector<std::pair<OUString, OUString>> temp; { std::unique_lock lock(s_EmbeddedFontsMutex); - // Only activate fonts that need activation. + // Handle restricted fonts + for (auto it1 = fonts.begin(); it1 != fonts.end();) + { + auto it2 = s_EmbeddedFonts.find(it1->second); + if (it2 == s_EmbeddedFonts.end()) + { + SAL_WARN("vcl.fonts", "Trying to activate a font not in s_EmbeddedFonts"); + it1 = fonts.erase(it1); + continue; + } + assert(it2->second.familyName == it1->first); + + if (!silentlyAllowRestrictedFonts && it2->second.isRestricted) + { + temp.push_back(*it1); + it1 = fonts.erase(it1); + continue; + } + + ++it1; + } + } + + if (!temp.empty()) + { + bool allowRestrictedFonts = false; + if (xHandler) + { + std::set<OUString> filteredFamilies; // families can repeat, e.g. for bold/italic + for (const auto& pair : temp) + filteredFamilies.insert(pair.first); + OUString fontlist; + for (const auto& family : filteredFamilies) + fontlist += " " + family; + rtl::Reference pRequest(new comphelper::OInteractionRequest( + uno::Any(document::FontsDisallowEditingRequest({}, {}, fontlist)))); + rtl::Reference pApprove(new comphelper::OInteractionApprove); + pRequest->addContinuation(pApprove); + pRequest->addContinuation(new comphelper::OInteractionDisapprove); + xHandler->handle(pRequest); + allowRestrictedFonts = pApprove->wasSelected(); + } + if (allowRestrictedFonts) + { + activatedRestrictedFonts = true; + fonts.insert(fonts.end(), temp.begin(), temp.end()); + } + else + { + releaseFonts(temp); + } + temp.clear(); + } + + { + std::unique_lock lock(s_EmbeddedFontsMutex); + // Only activate fonts that need activation. It goes after restricted fonts handling, + // because we must ask user about a second document embedding the same restricted font. + // We do not remove from fonts: the unlocking must happen only when the document is closed, + // so that necessary fonts are not unregistered. for (const auto& pair : fonts) { auto it = s_EmbeddedFonts.find(pair.second); + // At this point, we must find a match: neither releaseFonts above, nor other possible + // intermediate changes of s_EmbeddedFonts must not remove our locked entries assert(it != s_EmbeddedFonts.end()); if (!it->second.isActivated) { @@ -422,6 +512,14 @@ bool EmbeddedFontsManager::sufficientTTFRights( const void* data, tools::Long si OUString EmbeddedFontsManager::fontFileUrl( std::u16string_view familyName, FontFamily family, FontItalic italic, FontWeight weight, FontPitch pitch, FontRights rights ) { + // Do not embed restricted fonts coming from another document. If a font is among embedded, and + // is restricted, it means that it isn't installed locally. See isFontAvailableUnrestricted. + for (const auto& pair : s_EmbeddedFonts) + { + if (pair.second.familyName == familyName && pair.second.isRestricted) + return {}; + } + OUString path = GetEmbeddedFontsRoot() + "fromsystem/"; osl::Directory::createPath( path ); OUString filename = OUString::Concat(familyName) + "_" + OUString::number( family ) + "_" + OUString::number( italic )
