sw/qa/extras/uiwriter/data/embed-unrestricted1.odt |binary sw/qa/extras/uiwriter/data/embed-unrestricted2.odt |binary sw/qa/extras/uiwriter/uiwriter10.cxx | 52 ++++++++++++++++++++ sw/qa/inc/swmodeltestbase.hxx | 2 sw/qa/unit/swmodeltestbase.cxx | 4 + vcl/inc/win/salgdi.h | 2 vcl/win/gdi/salfont.cxx | 54 --------------------- vcl/win/gdi/salgdi_gdiplus.cxx | 24 +++++++++ 8 files changed, 83 insertions(+), 55 deletions(-)
New commits: commit e9bdc5e28993c12dfcdd8837e5fabd89f114dcd3 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Fri Aug 8 11:20:34 2025 +0500 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Fri Aug 8 17:04:56 2025 +0200 tdf#167849: unit test After commit 87ef004115d07c16fe4899b3d423cc16a12a0fce (tdf#167849: don't release embedded fonts in WinSalGraphics::ClearDevFontCache, 2025-08-07). Change-Id: I4b2ff7e8c3a9bd71befe537d76308e18968c99c8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189145 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/sw/qa/extras/uiwriter/data/embed-unrestricted1.odt b/sw/qa/extras/uiwriter/data/embed-unrestricted1.odt new file mode 100644 index 000000000000..168d10051048 Binary files /dev/null and b/sw/qa/extras/uiwriter/data/embed-unrestricted1.odt differ diff --git a/sw/qa/extras/uiwriter/data/embed-unrestricted2.odt b/sw/qa/extras/uiwriter/data/embed-unrestricted2.odt new file mode 100644 index 000000000000..f3f52a82ddc5 Binary files /dev/null and b/sw/qa/extras/uiwriter/data/embed-unrestricted2.odt differ diff --git a/sw/qa/extras/uiwriter/uiwriter10.cxx b/sw/qa/extras/uiwriter/uiwriter10.cxx index dc7af3eb6687..dad4bda7b3d1 100644 --- a/sw/qa/extras/uiwriter/uiwriter10.cxx +++ b/sw/qa/extras/uiwriter/uiwriter10.cxx @@ -9,6 +9,8 @@ #include <swmodeltestbase.hxx> +#include <com/sun/star/awt/Toolkit.hpp> +#include <com/sun/star/awt/XFontMappingUse.hpp> #include <com/sun/star/chart/XChartDocument.hpp> #include <com/sun/star/chart2/XChartDocument.hpp> #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> @@ -2096,6 +2098,56 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, testTdf166012) getProperty<short>(getRun(getParagraph(1), 1), u"CharScriptHint"_ustr)); } +#if !defined(MACOSX) +CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, testTdf167849) +{ + auto assertNoSubstitution = [](const uno::Reference<awt::XFontMappingUse>& xUse, int pass) { + const auto trackingData = xUse->finishTrackingFontMappingUse(); + for (const auto& element : trackingData) + { + for (const auto& used : element.usedFonts) + { + OUString message = "Pass " + OUString::number(pass) + ": " + element.originalFont + + " substituted with " + used; + CPPUNIT_ASSERT_MESSAGE(message.toUtf8().getStr(), + used.startsWith(element.originalFont)); + } + } + xUse->startTrackingFontMappingUse(); + }; + + auto xFontMappingUse = awt::Toolkit::create(comphelper::getProcessComponentContext()) + .queryThrow<awt::XFontMappingUse>(); + xFontMappingUse->startTrackingFontMappingUse(); + + // Given two documents with embedded fonts, that will not require substitution, if present: + + // Load the first document + createSwDoc("embed-unrestricted1.odt"); + // At this point, 'Manbow Solid' embedded font is loaded + std::swap(mxComponent, mxComponent2); // keep it from unloading upon the next load + + assertNoSubstitution(xFontMappingUse, 1); + + // Load the second document + createSwDoc("embed-unrestricted2.odt"); + // At this point, 'Unsteady Oversteer' font is also loaded + + assertNoSubstitution(xFontMappingUse, 2); + + // Re-layout both documents; both fonts must still be loaded + calcLayout(true); + std::swap(mxComponent, mxComponent2); + calcLayout(true); + + // Without the fix, it would fail with + // - Pass 3: Manbow Solid substituted with Liberation Serif/Regular + // because loading the second document unregistered the embedded font from the first one. + assertNoSubstitution(xFontMappingUse, 3); + xFontMappingUse->finishTrackingFontMappingUse(); +} +#endif + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/inc/swmodeltestbase.hxx b/sw/qa/inc/swmodeltestbase.hxx index 7bac6f7dd076..c77a0d212141 100644 --- a/sw/qa/inc/swmodeltestbase.hxx +++ b/sw/qa/inc/swmodeltestbase.hxx @@ -98,7 +98,7 @@ protected: CPPUNIT_FAIL( "verify method must be overridden" ); } - void calcLayout(); + void calcLayout(bool bRecalc = false); /// Get the body text of the whole document. OUString getBodyText() const; diff --git a/sw/qa/unit/swmodeltestbase.cxx b/sw/qa/unit/swmodeltestbase.cxx index 69aab12a38ba..523c5186d358 100644 --- a/sw/qa/unit/swmodeltestbase.cxx +++ b/sw/qa/unit/swmodeltestbase.cxx @@ -89,8 +89,10 @@ void SwModelTestBase::dumpLayout(SwDoc* pDoc) xmlFreeTextWriter(pXmlWriter); } -void SwModelTestBase::calcLayout() +void SwModelTestBase::calcLayout(bool bRecalc) { + if (bRecalc) + getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell()->Reformat(); getSwDoc()->getIDocumentLayoutAccess().GetCurrentViewShell()->CalcLayout(); } commit e2e1c5cb10cc33878495fa767d0c2a21459f924d Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Fri Aug 8 16:06:05 2025 +0500 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Fri Aug 8 17:04:48 2025 +0200 Use Gdiplus::PrivateFontCollection to obtain family name from TTF This avoids the path length limitation of CreateScalableFontResourceW, which manifests e.g. in CI - see https://ci.libreoffice.org/job/gerrit_windows/203249/console: warn:vcl.fonts:3512:11352:vcl/win/gdi/salfont.cxx:962: CreateScalableFontResource failed for C: warn:vcl.fonts:3512:11352:vcl/win/gdi/salfont.cxx:994: error extracting font family from file:///C:/cygwin64/home/tdf/jenkins/workspace/gerrit_windows/workdir/CppunitTest/sw_uiwriter10.test.user/user/temp/embeddedfonts/fromdocs/Manbow Solid0.ttf The implementation is put into vcl/win/gdi/salgdi_gdiplus.cxx, because the GDI+ headers create conflicts when put into vcl/win/gdi/salfont.cxx; the file name of salgdi_gdiplus.cxx looked relevant (although I don't see any actual GDI+-related code there). By the way, this new implementation seems to also perform better: in a test, it finished in ~3 ms in a dbgutil build, while the previous code took about 8 ms in an optimized build. Change-Id: I95b09847e2fcba13489050d3a61a56776d8c8e47 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189186 Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Tested-by: Jenkins diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h index 20e28c7c03bb..ed6ef27d45a1 100644 --- a/vcl/inc/win/salgdi.h +++ b/vcl/inc/win/salgdi.h @@ -281,6 +281,8 @@ private: // local helpers void DrawTextLayout(const GenericSalLayout&, HDC, bool bUseDWrite, bool bRenderingModeNatural); + static OUString getFontFamilyNameFromTTF(const OUString& url); + protected: std::unique_ptr<SalGraphicsImpl> mpImpl; WinSalGraphicsImplBase * mWinSalGraphicsImplBase; diff --git a/vcl/win/gdi/salfont.cxx b/vcl/win/gdi/salfont.cxx index 7f5252adfbd9..2ca870aad98d 100644 --- a/vcl/win/gdi/salfont.cxx +++ b/vcl/win/gdi/salfont.cxx @@ -933,62 +933,10 @@ void ImplReleaseTempFonts(SalData& rSalData) } } -static OUString lcl_GetFontFamilyName(std::u16string_view rFontFileURL) -{ - // Create temporary file name - OUString aTempFileURL; - if (osl::File::E_None != osl::File::createTempFile(nullptr, nullptr, &aTempFileURL)) - return OUString(); - osl::File::remove(aTempFileURL); - OUString aResSystemPath; - osl::FileBase::getSystemPathFromFileURL(aTempFileURL, aResSystemPath); - - // Create font resource file (.fot) - // There is a limit of 127 characters for the full path passed via lpszFile, so we have to - // split the font URL and pass it as two parameters. As a result we can't use - // CreateScalableFontResource for renaming, as it now expects the font in the system path. - // But it's still good to use it for family name extraction, we're currently after. - // BTW: it doesn't help to prefix the lpszFile with \?\ to support larger paths. - // TODO: use TTLoadEmbeddedFont (needs an EOT as input, so we have to add a header to the TTF) - // TODO: forward the EOT from the AddTempDevFont call side, if VCL supports it - INetURLObject aTTFUrl(rFontFileURL); - // GetBase() strips the extension - OUString aFilename = aTTFUrl.GetLastName(INetURLObject::DecodeMechanism::WithCharset); - if (!CreateScalableFontResourceW(0, o3tl::toW(aResSystemPath.getStr()), - o3tl::toW(aFilename.getStr()), o3tl::toW(aTTFUrl.GetPath().getStr()))) - { - sal_uInt32 nError = GetLastError(); - SAL_WARN("vcl.fonts", "CreateScalableFontResource failed for " << aResSystemPath << " " - << aFilename << " " << aTTFUrl.GetPath() << " " << nError); - return OUString(); - } - - // Open and read the font resource file - osl::File aFotFile(aTempFileURL); - if (osl::FileBase::E_None != aFotFile.open(osl_File_OpenFlag_Read)) - return OUString(); - - sal_uInt64 nBytesRead = 0; - char aBuffer[4096]; - aFotFile.read( aBuffer, sizeof( aBuffer ), nBytesRead ); - // clean up temporary resource file - aFotFile.close(); - osl::File::remove(aTempFileURL); - - // retrieve font family name from byte offset 0x4F6 - static const sal_uInt64 nNameOfs = 0x4F6; - sal_uInt64 nPos = nNameOfs; - for (; (nPos < nBytesRead) && (aBuffer[nPos] != 0); nPos++); - if (nPos >= nBytesRead || (nPos == nNameOfs)) - return OUString(); - - return OUString(aBuffer + nNameOfs, nPos - nNameOfs, osl_getThreadTextEncoding()); -} - bool WinSalGraphics::AddTempDevFont(vcl::font::PhysicalFontCollection* pFontCollection, const OUString& rFontFileURL, const OUString& rFontName) { - OUString aFontFamily = lcl_GetFontFamilyName(rFontFileURL); + OUString aFontFamily = getFontFamilyNameFromTTF(rFontFileURL); if (aFontFamily.isEmpty()) { SAL_WARN("vcl.fonts", "error extracting font family from " << rFontFileURL); diff --git a/vcl/win/gdi/salgdi_gdiplus.cxx b/vcl/win/gdi/salgdi_gdiplus.cxx index 45b169b480fc..ba64c92e59d8 100644 --- a/vcl/win/gdi/salgdi_gdiplus.cxx +++ b/vcl/win/gdi/salgdi_gdiplus.cxx @@ -26,6 +26,13 @@ #include "gdiimpl.hxx" +#include <prewin.h> +#include <gdiplus.h> +#include <postwin.h> + +#include <osl/file.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + void WinSalGraphics::drawPolyPolygon( const basegfx::B2DHomMatrix& rObjectToDevice, const basegfx::B2DPolyPolygon& rPolyPolygon, @@ -85,4 +92,21 @@ bool WinSalGraphics::hasFastDrawTransformedBitmap() const return mpImpl->hasFastDrawTransformedBitmap(); } +// static +OUString WinSalGraphics::getFontFamilyNameFromTTF(const OUString& url) +{ + OUString localFileName; + osl::FileBase::getSystemPathFromFileURL(url, localFileName); + Gdiplus::PrivateFontCollection collection; + collection.AddFontFile(o3tl::toW(localFileName.getStr())); + if (collection.GetFamilyCount() == 0) + return {}; + Gdiplus::FontFamily aFamily; + if (INT ret; collection.GetFamilies(1, &aFamily, &ret) != Gdiplus::Status::Ok || ret < 1) + return {}; + WCHAR familyName[LF_FACESIZE]{}; + aFamily.GetFamilyName(familyName, LANG_NEUTRAL); + return OUString(o3tl::toU(familyName)); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */