vcl/inc/unx/fc_fontoptions.hxx          |    3 ++
 vcl/quartz/salgdi.cxx                   |   38 ++++++++++++++++++++++++++++
 vcl/unx/generic/gdi/cairotextrender.cxx |   43 +++++++++++++++++++++++++++++---
 3 files changed, 80 insertions(+), 4 deletions(-)

New commits:
commit 33afa8dc1479a21783cc7f92e376449d5c7948d8
Author:     Khaled Hosny <[email protected]>
AuthorDate: Mon Mar 2 01:02:18 2026 +0200
Commit:     Khaled Hosny <[email protected]>
CommitDate: Mon Mar 2 01:02:18 2026 +0200

    xx
    
    Change-Id: I9383b49d2a520fb9c03e9971e802af60eb142969

diff --git a/vcl/inc/unx/fc_fontoptions.hxx b/vcl/inc/unx/fc_fontoptions.hxx
index 73bcf3421bc5..5fc1da59a9b0 100644
--- a/vcl/inc/unx/fc_fontoptions.hxx
+++ b/vcl/inc/unx/fc_fontoptions.hxx
@@ -21,6 +21,8 @@
 
 #include <rtl/string.hxx>
 #include <vcl/dllapi.h>
+#include <hb.h>
+#include <vector>
 
 typedef struct _FcPattern   FcPattern;
 class VCL_DLLPUBLIC FontConfigFontOptions
@@ -31,6 +33,7 @@ public:
                         ~FontConfigFontOptions();
 
     void                SyncPattern(const OString& rFileName, sal_uInt32 
nFontFace, sal_uInt32 nFontVariation, bool bEmbolden);
+    void                AddVariations(const std::vector<hb_variation_t>& 
rVariations);
     FcPattern*          GetPattern() const;
     static void         cairo_font_options_substitute(FcPattern* pPattern);
 private:
diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx
index af5a620484c8..4ea145303218 100644
--- a/vcl/quartz/salgdi.cxx
+++ b/vcl/quartz/salgdi.cxx
@@ -382,6 +382,37 @@ void AquaGraphicsBackend::drawTextLayout(const 
GenericSalLayout& rLayout)
     }
 
     CTFontRef pCTFont = rFont.GetCTFont();
+    CTFontRef pVarFont = nullptr;
+    const auto& variations = rFont.GetVariations();
+    if (!variations.empty())
+    {
+        CFMutableDictionaryRef pVarDict = 
CFDictionaryCreateMutable(kCFAllocatorDefault, variations.size(),
+                                                                     
&kCFTypeDictionaryKeyCallBacks,
+                                                                     
&kCFTypeDictionaryValueCallBacks);
+        for (const auto& var : variations)
+        {
+            hb_tag_t nTag = var.tag;
+            CFNumberRef pTag = CFNumberCreate(kCFAllocatorDefault, 
kCFNumberSInt32Type, &nTag);
+            double fValue = var.value;
+            CFNumberRef pValue = CFNumberCreate(kCFAllocatorDefault, 
kCFNumberDoubleType, &fValue);
+            CFDictionaryAddValue(pVarDict, pTag, pValue);
+            CFRelease(pTag);
+            CFRelease(pValue);
+        }
+        CFDictionaryRef pAttrDict = CFDictionaryCreate(kCFAllocatorDefault,
+                                                       (const 
void**)&kCTFontVariationAttribute,
+                                                       (const 
void**)&pVarDict, 1,
+                                                       
&kCFTypeDictionaryKeyCallBacks,
+                                                       
&kCFTypeDictionaryValueCallBacks);
+        CTFontDescriptorRef pVarDesc = 
CTFontDescriptorCreateWithAttributes(pAttrDict);
+        pVarFont = CTFontCreateCopyWithAttributes(pCTFont, 0.0, nullptr, 
pVarDesc);
+        pCTFont = pVarFont;
+
+        CFRelease(pVarDesc);
+        CFRelease(pAttrDict);
+        CFRelease(pVarDict);
+    }
+
     CGAffineTransform aRotMatrix = 
CGAffineTransformMakeRotation(-rFont.mfFontRotation);
 
     basegfx::B2DPoint aPos;
@@ -412,7 +443,11 @@ void AquaGraphicsBackend::drawTextLayout(const 
GenericSalLayout& rLayout)
     }
 
     if (aGlyphIds.empty())
+    {
+        if (pVarFont)
+            CFRelease(pVarFont);
         return;
+    }
 
     assert(aGlyphIds.size() == aGlyphPos.size());
 #if 0
@@ -481,6 +516,9 @@ void AquaGraphicsBackend::drawTextLayout(const 
GenericSalLayout& rLayout)
     }
 
     mrShared.maContextHolder.restoreState();
+
+    if (pVarFont)
+        CFRelease(pVarFont);
 }
 
 void AquaSalGraphics::SetFont(LogicalFontInstance* pReqFont, int 
nFallbackLevel)
diff --git a/vcl/unx/generic/gdi/cairotextrender.cxx 
b/vcl/unx/generic/gdi/cairotextrender.cxx
index 14ee838df33d..c118aa396ee4 100644
--- a/vcl/unx/generic/gdi/cairotextrender.cxx
+++ b/vcl/unx/generic/gdi/cairotextrender.cxx
@@ -29,8 +29,11 @@
 #include <sallayout.hxx>
 #include <salinst.hxx>
 
+#include <rtl/ustrbuf.hxx>
+#include <hb.h>
 #include <cairo.h>
 #include <cairo-ft.h>
+#include <fontconfig/fontconfig.h>
 #if defined(CAIRO_HAS_SVG_SURFACE)
 #include <cairo-svg.h>
 #elif defined(CAIRO_HAS_PDF_SURFACE)
@@ -43,6 +46,10 @@ namespace {
 
 typedef struct FT_FaceRec_* FT_Face;
 
+#ifndef FC_FONT_VARIATIONS
+#define FC_FONT_VARIATIONS "fontvariations"
+#endif
+
 class CairoFontsCache : public CacheOwner
 {
 public:
@@ -52,12 +59,22 @@ public:
         const FontConfigFontOptions *mpOptions;
         bool mbEmbolden;
         bool mbVerticalMetrics;
+        std::vector<hb_variation_t> maVariations;
         bool operator ==(const CacheId& rOther) const
         {
-            return maFace == rOther.maFace &&
-                mpOptions == rOther.mpOptions &&
-                mbEmbolden == rOther.mbEmbolden &&
-                mbVerticalMetrics == rOther.mbVerticalMetrics;
+            if (maFace != rOther.maFace ||
+                mpOptions != rOther.mpOptions ||
+                mbEmbolden != rOther.mbEmbolden ||
+                mbVerticalMetrics != rOther.mbVerticalMetrics ||
+                maVariations.size() != rOther.maVariations.size())
+                return false;
+            for (size_t i = 0; i < maVariations.size(); ++i)
+            {
+                if (maVariations[i].tag != rOther.maVariations[i].tag ||
+                    maVariations[i].value != rOther.maVariations[i].value)
+                    return false;
+            }
+            return true;
         }
     };
 
@@ -227,7 +244,24 @@ static void ApplyFont(cairo_t* cr, const 
CairoFontsCache::CacheId& rId, double n
     {
         const FontConfigFontOptions *pOptions = rId.mpOptions;
         FcPattern *pPattern = pOptions->GetPattern();
+        if (!rId.maVariations.empty())
+        {
+            pPattern = FcPatternDuplicate(pPattern);
+            OStringBuffer aVarStr;
+            for (const auto& var : rId.maVariations)
+            {
+                if (!aVarStr.isEmpty())
+                    aVarStr.append(',');
+                char tag[4];
+                hb_tag_to_string(var.tag, tag);
+                aVarStr.append(tag, 4);
+                aVarStr.append(tag + "=" + OString::number(var.value));
+            }
+            FcPatternAddString(pPattern, FC_FONT_VARIATIONS, 
reinterpret_cast<const FcChar8*>(aVarStr.getStr()));
+        }
         font_face = cairo_ft_font_face_create_for_pattern(pPattern);
+        if (!rId.maVariations.empty())
+            FcPatternDestroy(pPattern);
         rCache.CacheFont(font_face, rId);
     }
     cairo_set_font_face(cr, font_face);
@@ -268,6 +302,7 @@ static CairoFontsCache::CacheId makeCacheId(const 
GenericSalLayout& rLayout)
     aId.mpOptions = rFont.GetFontOptions();
     aId.mbEmbolden = rInstance.NeedsArtificialBold();
     aId.mbVerticalMetrics = false;
+    aId.maVariations = rInstance.GetVariations();
 
     return aId;
 }

Reply via email to