Platforms to show: All Mac Windows Linux Cross-Platform

/MacCG/CoreText/CoreText VariableFont


Required plugins for this example: MBS MacCF Plugin, MBS MacBase Plugin, MBS MacCocoa Plugin, MBS MacCG Plugin, MBS Main Plugin

Last modified Sun, 14th Feb 2026.

You find this example project in your MBS Xojo Plugin download as a Xojo project file within the examples folder: /MacCG/CoreText/CoreText VariableFont

Download this example: CoreText VariableFont.zip

Project "CoreText VariableFont.xojo_binary_project"
Class App Inherits DesktopApplication
Const kEditClear = "&Delete"
Const kFileQuit = "&Quit"
Const kFileQuitShortcut = ""
EventHandler Sub Opening() me.AllowAutoQuit = True End EventHandler
End Class
Class Window1 Inherits DesktopWindow
Control Label1 Inherits DesktopLabel
ControlInstance Label1 Inherits DesktopLabel
End Control
Control LabelFontSize Inherits DesktopLabel
ControlInstance LabelFontSize Inherits DesktopLabel
End Control
Control SliderFontSize Inherits DesktopSlider
ControlInstance SliderFontSize Inherits DesktopSlider
EventHandler Sub ValueChanged() Var fontSize As Double = Me.Value ' Convert the CTFontDescriptor to an NSFontDescriptor Var n As NSFontDescriptorMBS = NSFontDescriptorMBS.fontDescriptorWithCTFontDescriptor(Self.CTFontDescriptor) ' Create an NSFont from the descriptor at the specified size Var theNSFont As NSFontMBS = NSFontMBS.fontWithDescriptor(n, fontSize) ' Apply the font to TextArea TextArea1.NSTextViewMBS.Font = theNSFont LabelFontSize.Text = Me.Value.ToString End EventHandler
End Control
Control PopupMenuFamily Inherits DesktopPopupMenu
ControlInstance PopupMenuFamily Inherits DesktopPopupMenu
EventHandler Sub SelectionChanged(item As DesktopMenuItem) #Pragma Unused item ///////////////////////////////////////////// ' Make Font Style Menu PopupMenuStyle.RemoveAllRows ' Get the shared NSFontManager instance and retrieve all members of the selected font family Var NSFontManager As New NSFontManagerMBS Var Members() As Variant = NSFontManager.availableMembersOfFontFamily(Me.SelectedRowText) ' Iterate through each font family member For Each Member As Variant In Members ' Unpack the member info array Var MemberInfos() As Variant = Member Var PostScriptName As String = MemberInfos(0) Var StyleName As String = MemberInfos(1) ' Var Weight As Integer = MemberInfos(2) ' Add the style name as the display label, and store the PostScript name as the row tag PopupMenuStyle.AddRow StyleName PopupMenuStyle.RowTagAt(PopupMenuStyle.LastAddedRowIndex) = PostScriptName Next PopupMenuStyle.SelectedRowIndex = 0 End EventHandler
End Control
Control PopupMenuStyle Inherits DesktopPopupMenu
ControlInstance PopupMenuStyle Inherits DesktopPopupMenu
EventHandler Sub SelectionChanged(item As DesktopMenuItem) #Pragma Unused item ///////////////////////////////////////////// ' Make NSFont Var fontPSName As String = Me.RowTagAt(me.SelectedRowIndex) Var fontSize As Double = TextArea1.NSTextViewMBS.Font.pointSize var theCTFont as CTFontMBS = CTFontMBS.CreateWithName(fontPSName, fontSize) Self.CTFontDescriptor = theCTFont.FontDescriptor Var n As NSFontDescriptorMBS = NSFontDescriptorMBS.fontDescriptorWithCTFontDescriptor(Self.CTFontDescriptor) var theNSFont as NSFontMBS = NSFontMBS.fontWithDescriptor(n, fontSize) ///////////////////////////////////////////// ' Set to Listbox Self.SetListbox(theCTFont) ///////////////////////////////////////////// ' Make Axis Dictionaries Self.MakeAxisDics(theCTFont) ///////////////////////////////////////////// ' Set Slider Self.SetSlider() ///////////////////////////////////////////// ' Set to TextArea TextArea1.NSTextViewMBS.Font = theNSFont TextArea1.NSTextViewMBS.needsDisplay = True End EventHandler
End Control
Control TextArea1 Inherits DesktopTextArea
ControlInstance TextArea1 Inherits DesktopTextArea
EventHandler Sub Opening() Self.SetLineHeight(TextArea1.NSTextViewMBS) End EventHandler
End Control
Control Label11 Inherits DesktopLabel
ControlInstance Label11 Inherits DesktopLabel
End Control
Control Label12 Inherits DesktopLabel
ControlInstance Label12 Inherits DesktopLabel
End Control
Control ListBox1 Inherits DesktopListBox
ControlInstance ListBox1 Inherits DesktopListBox
EventHandler Sub RowExpanded(row As Integer) ' Retrieve the axis info dictionary stored as the row tag Var AxisDic As Dictionary = Me.RowTagAt(row) ' Extract each variation axis property from the dictionary Var AxisName As String = AxisDic.Value(CTFontMBS.kCTFontVariationAxisNameKey) Var AxisID As Integer = AxisDic.Value(CTFontMBS.kCTFontVariationAxisIdentifierKey) Var AxisMin As Double = AxisDic.Value(CTFontMBS.kCTFontVariationAxisMinimumValueKey) Var AxisMax As Double = AxisDic.Value(CTFontMBS.kCTFontVariationAxisMaximumValueKey) Var AxisDefault As Double = AxisDic.Value(CTFontMBS.kCTFontVariationAxisDefaultValueKey) ' Display all axis properties as key-value rows Me.AddRow("kCTFontVariationAxisNameKey", AxisName) Me.AddRow("kCTFontVariationAxisIdentifierKey", AxisID.ToString) Me.AddRow("kCTFontVariationAxisMinimumValueKey", AxisMin.ToString) Me.AddRow("kCTFontVariationAxisMaximumValueKey", AxisMax.ToString) Me.AddRow("kCTFontVariationAxisDefaultValueKey", AxisDefault.ToString) ' Get the current axis values of the style Var ctFont As CTFontMBS = Me.CellTagAt(row, 0) Var axisValueDic As Dictionary = ctFont.Variation ' If the font has a value set for this axis, display it If axisValueDic.HasKey(AxisID) Then Var axisValue As Double = axisValueDic.Value(AxisID) Me.AddRow("Axis value of this style", axisValue.ToString) End If End EventHandler
End Control
Control LabelAxis Inherits DesktopLabel
ControlInstance LabelAxis(0) Inherits DesktopLabel
ControlInstance LabelAxis(1) Inherits DesktopLabel
ControlInstance LabelAxis(2) Inherits DesktopLabel
ControlInstance LabelAxis(3) Inherits DesktopLabel
End Control
Control AxisValue Inherits DesktopLabel
ControlInstance AxisValue(0) Inherits DesktopLabel
ControlInstance AxisValue(1) Inherits DesktopLabel
ControlInstance AxisValue(2) Inherits DesktopLabel
ControlInstance AxisValue(3) Inherits DesktopLabel
End Control
Control SliderAxis Inherits DecimalSlider
ControlInstance SliderAxis(0) Inherits DecimalSlider
ControlInstance SliderAxis(1) Inherits DecimalSlider
ControlInstance SliderAxis(2) Inherits DecimalSlider
ControlInstance SliderAxis(3) Inherits DecimalSlider
EventHandler Sub ValueChanged(index as Integer) AxisValue(index).Text = Me.DecimalValue.ToString ' Change Axis Value Var fontSize As Double = TextArea1.NSTextViewMBS.Font.pointSize var axisID as Integer = LabelAxisID(index).Text.ToInteger var axisValue as Double = Me.DecimalValue Self.CTFontDescriptor = Self.CTFontDescriptor.CopyWithVariation(axisID, axisValue) Var n As NSFontDescriptorMBS = NSFontDescriptorMBS.fontDescriptorWithCTFontDescriptor(Self.CTFontDescriptor) Var theNSFont As NSFontMBS = NSFontMBS.fontWithDescriptor(n, fontSize) TextArea1.NSTextViewMBS.Font = theNSFont TextArea1.NSTextViewMBS.needsDisplay = True End EventHandler
End Control
Control LabelAxisID Inherits DesktopLabel
ControlInstance LabelAxisID(0) Inherits DesktopLabel
ControlInstance LabelAxisID(1) Inherits DesktopLabel
ControlInstance LabelAxisID(2) Inherits DesktopLabel
ControlInstance LabelAxisID(3) Inherits DesktopLabel
End Control
EventHandler Sub Opening() //////////////////////////////////////// ' Make Font Family Menu PopupMenuFamily.RemoveAllRows ' Get the shared NSFontManager instance and retrieve all available font families Var NSFontManager As New NSFontManagerMBS Var fontFamilies() As String = NSFontManager.availableFontFamilies ' Iterate through each font family and add only variable fonts to the menu For Each fontFamilyName As String In fontFamilies If Self.IsVariableFont(fontFamilyName) Then PopupMenuFamily.AddRow fontFamilyName End If Next PopupMenuFamily.SelectedRowIndex = 0 End EventHandler
Private Function IsVariableFont(FontFamilyName as String) As Boolean var NSFontManager as new NSFontManagerMBS var Members() as variant = NSFontManager.availableMembersOfFontFamily(FontFamilyName) var MemberInfos() as Variant = Members(0) Var PostScriptName As String = MemberInfos(0) Var theCTFont As CTFontMBS = CTFontMBS.CreateWithName(PostScriptName, 10) Var AxisDics() As Dictionary = theCTFont.VariationAxes If AxisDics.LastIndex>-1 Then Return True End Function
Private Sub MakeAxisDics(ctFont as CTFontMBS) Redim Self.Axis(-1) Var AxisDics() As Dictionary = ctFont.VariationAxes Var c As Integer = AxisDics.LastIndex If c=-1 Then Return For i As Integer=0 To c Var AxisDic As Dictionary = AxisDics(i) Var AxisName As String = AxisDic.Value(CTFontMBS.kCTFontVariationAxisNameKey) Var AxisID As Integer = AxisDic.Value(CTFontMBS.kCTFontVariationAxisIdentifierKey) Var AxisMin As Double = AxisDic.Value(CTFontMBS.kCTFontVariationAxisMinimumValueKey) Var AxisMax As Double = AxisDic.Value(CTFontMBS.kCTFontVariationAxisMaximumValueKey) Var AxisDefault As Double = AxisDic.Value(CTFontMBS.kCTFontVariationAxisDefaultValueKey) Var AxisStyleValue As Double Var axisValueDic As Dictionary = ctFont.Variation If axisValueDic.HasKey(AxisID) Then AxisStyleValue = axisValueDic.Value(AxisID) else AxisStyleValue = AxisDefault End If ' make dictionary Var d As New Dictionary d.Value("Name") = AxisName d.Value("ID") = AxisID d.Value("Min") = AxisMin d.Value("Max") = AxisMax d.Value("Default") = AxisDefault d.Value("StyleValue") = AxisStyleValue Self.Axis.Add d Next End Sub
Private Sub SetLineHeight(t as NSTextViewMBS) t.layoutManager.usesFontLeading = False Var n As NSParagraphStyleMBS = t.defaultParagraphStyle If n Is Nil Then n = NSParagraphStyleMBS.defaultParagraphStyle End If Dim m As NSMutableParagraphStyleMBS = n.mutableCopy m.setLineHeightMultiple(1.3) Dim d As New Dictionary d.Value(t.layoutManager.attributedString.NSParagraphStyleAttributeName) = m t.typingAttributes = d t.textStorage.addAttributes(d, NSMakeRangeMBS(0, t.textStorage.length)) End Sub
Private Sub SetListbox(ctFont as CTFontMBS) Listbox1.RemoveAllRows Var AxisDics() As Dictionary = ctFont.VariationAxes Var c As Integer = AxisDics.LastIndex If c=-1 Then Return For i As Integer=0 To c Var AxisDic As Dictionary = AxisDics(i) ' Var AxisName As String = AxisDic.Value(CTFontMBS.kCTFontVariationAxisNameKey) ' Var AxisID As Integer = AxisDic.Value(CTFontMBS.kCTFontVariationAxisIdentifierKey) ' Var AxisMin As Double = AxisDic.Value(CTFontMBS.kCTFontVariationAxisMinimumValueKey) ' Var AxisMax As Double = AxisDic.Value(CTFontMBS.kCTFontVariationAxisMaximumValueKey) ' Var AxisDefault As Double = AxisDic.Value(CTFontMBS.kCTFontVariationAxisDefaultValueKey) Listbox1.AddExpandableRow "Axis("+i.toString+")" var row as Integer = Listbox1.LastAddedRowIndex ListBox1.CellTagAt(row,0) = ctFont ListBox1.RowTagAt(row) = AxisDic Listbox1.RowExpandedAt(row) = True Next End Sub
Private Sub SetSlider() Var i As Integer ' invisible all for i=0 to 3 LabelAxis(i).Visible = False AxisValue(i).Visible = False SliderAxis(i).Visible = False Next ' set slider Var c As Integer = Self.Axis.LastIndex If c=-1 Then Return For i=0 To c if i>3 then Exit var d as Dictionary = Self.Axis(i) var AxisName as String = d.Value("Name") var AxisID as Integer = d.Value("ID") var AxisMin as Double = d.Value("Min") Var AxisMax As Double = d.Value("Max") var AxisStyleValue as Double = d.Value("StyleValue") LabelAxis(i).Text = d.Value("Name") AxisValue(i).Text = AxisStyleValue.ToString SliderAxis(i).DecimalMinimum = AxisMin SliderAxis(i).DecimalMaximum = AxisMax SliderAxis(i).DecimalValue = AxisStyleValue LabelAxisID(i).Text = AxisID.ToString LabelAxis(i).Visible = True AxisValue(i).Visible = True SliderAxis(i).Visible = True Next End Sub
Property Private Axis() As Dictionary
Property Private CTFontDescriptor As CTFontDescriptorMBS
End Class
MenuBar MainMenuBar
MenuItem FileMenu = "&File"
MenuItem FileQuit = "#App.kFileQuit"
MenuItem EditMenu = "&Edit"
MenuItem EditUndo = "&Undo"
MenuItem EditSeparator1 = "-"
MenuItem EditCut = "Cu&t"
MenuItem EditCopy = "&Copy"
MenuItem EditPaste = "&Paste"
MenuItem EditClear = "#App.kEditClear"
MenuItem EditSeparator2 = "-"
MenuItem EditSelectAll = "Select &All"
MenuItem WindowMenu = "Window"
MenuItem HelpMenu = "&Help"
End MenuBar
Sign
End Sign
Class DecimalSlider Inherits DesktopSlider
ComputedProperty DecimalMaximum As Double
Sub Set() mDecimalMaximum = value Var scale As Integer = CurrentScale() Var maxVal As Integer = Round(mDecimalMaximum * scale) Var minVal As Integer = Round(mDecimalMinimum * scale) Self.MaximumValue = maxVal self.MinimumValue = minVal End Set
Sub Get() Return mDecimalMaximum End Get
End ComputedProperty
ComputedProperty DecimalMinimum As Double
Sub Set() mDecimalMinimum = value Var scale As Integer = CurrentScale() Var maxVal As Integer = Round(mDecimalMaximum * scale) Var minVal As Integer = Round(mDecimalMinimum * scale) Self.MaximumValue = maxVal self.MinimumValue = minVal End Set
Sub Get() Return mDecimalMinimum End Get
End ComputedProperty
ComputedProperty DecimalValue As Double
Sub Set() Var scaled As Integer = Round(value * CurrentScale()) self.Value = scaled End Set
Sub Get() Return self.Value / CurrentScale() End Get
End ComputedProperty
Private Function CurrentScale() As Integer Var sMax As Integer = DetectScale(mDecimalMaximum) Var sMin As Integer = DetectScale(mDecimalMinimum) Return If(sMax > sMin, sMax, sMin) End Function
Private Function DetectScale(value As Double) As Integer Var v As Double = Abs(value) Var scale As Integer = 1 While (v - Floor(v)) > 1e-9 And scale < 1000 v = v * 10 scale = scale * 10 Wend Return scale End Function
Property Private mDecimalMaximum As Double = 100.0
Property Private mDecimalMinimum As Double = 0.0
End Class
End Project

See also:

Download this example: CoreText VariableFont.zip

The items on this page are in the following plugins: MBS MacCG Plugin.


The biggest plugin in space...