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 )

Reply via email to