Page 1 of 1

RegisterFontFile() and Open Type Font

Posted: Thu Sep 28, 2023 11:11 pm
by StarBootics
Hello to Mac OSX users,

In the documentation about RegisterFontFile() it's written that only True Type font are supported.

luis and me have confirmed that on Windows and Linux it's possible to to Register Open Type Fonts successfully.

See the discussion here : viewtopic.php?t=82523

I personally don't have a Mac computer so I can't test this my self but to make correction to the documentation we need confirmation that RegisterFontFile() work on Mac OSX with Open Type Fonts. It would be nice if someone having a Mac Computer take few minutes to confirm that yes or no Open Type fonts can be Registered and used.

We all want accurate documentation.

Best regards
StarBootics

Re: RegisterFontFile() and Open Type Font

Posted: Fri Sep 29, 2023 7:07 am
by yuki
Looks OK here (though I wish macOS enjoyed high-DPI canvas support).

OTF → Adobe Source Code Pro Semibold
TTF → Adobe Source Code Pro ExtraLight

macOS 13.6:
Image

Windows 11:
Image

Instant repro:

Code: Select all

; This app makes use of the Source Code Pro font, which is © 2023 Adobe (http://www.adobe.com/),
; with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the 
; United States and/or other countries.
;
; The Source Code Pro repository and license details can be found at:
;   https://github.com/adobe-fonts/source-code-pro/
;
; A copy of the license should be downloaded and made available with this program upon launch,
; as precondition for downloading the actual fonts.
;

EnableExplicit

UseZipPacker()
UseSHA3Fingerprint()  ; Definitely not overkill.

; Constants.
; ═══════════════════════════════════════════════════════

#OUT_DIR     = "pb-" + #PB_Compiler_Version + "-otf-ttf-test/"
#OUT_LICENSE = #OUT_DIR + "LICENSE.md"
#OUT_PKG_OTF = #OUT_DIR + "source-code-pro-2.042-otf.zip"
#OUT_PKG_TTF = #OUT_DIR + "source-code-pro-2.042-ttf.zip"
#OUT_FNT_OTF = #OUT_DIR + "source-code-pro-semibold.otf"
#OUT_FNT_TTF = #OUT_DIR + "source-code-pro-extralight.ttf"

#SRC_LICENSE = "https://raw.githubusercontent.com/adobe-fonts/source-code-pro/f1680b3312c48cff23da1374515b0b7c1ff87cee/LICENSE.md"
#SRC_PKG_OTF = "https://github.com/adobe-fonts/source-code-pro/releases/download/2.042R-u%2F1.062R-i%2F1.026R-vf/OTF-source-code-pro-2.042R-u_1.062R-i.zip"
#SRC_PKG_TTF = "https://github.com/adobe-fonts/source-code-pro/releases/download/2.042R-u%2F1.062R-i%2F1.026R-vf/TTF-source-code-pro-2.042R-u_1.062R-i.zip"
#SRC_ARC_OTF = "OTF/SourceCodePro-Semibold.otf"
#SRC_ARC_TTF = "TTF/SourceCodePro-ExtraLight.ttf"

#CRC_LICENSE = "fb5eb2de5735e2208b8427aad9b9b90ecfcc86ea220fe021ff2cd58770aff83b6cf79ca4f87e07462871430ceb1da4dd1f595195724fe21fdd7f9f34716de91c"
#CRC_PKG_OTF = "c1b4ffe67bc6474ce42fe57d8d0abea60d55fb9ec1b72931536e4b4c77864770ea0f5d646bc2c5dc8ca20899832839e998f3efaca0a55319ba08f72272e6f144"
#CRC_PKG_TTF = "211074c8d86b2ed022ebad8cbd02ed78a30b10615cc38745f519e21b514a614c9e375fad1bd886e1bde24e4333dde353eee2474635cf21c5a8456183f1f9d194"
#CRC_FNT_OTF = "d1b3ca3858659590bb641df62547857b02ae67f38ac82203ab30bc5d462e76172ca4e06606fc5bc0c132bef97c7936498a4c1792fe0916e08e0230246776ae98"
#CRC_FNT_TTF = "f27846e9a6ffafe97d4e72e8360ee23d285fa1482f191c7bc1df8e4e6bdcff4c88af70497a9cf46dfdb146d8de9a8548144b772a4b9b6cd64e9c98f27b34e0d9"

#FNT_NAME_OTF = "Source Code Pro Semibold"
#FNT_NAME_TTF = "Source Code Pro ExtraLight"

; Whether or not a dummy StartDrawing():StopDrawing() sequence should be executed prior to
; registering fonts.
; For reproducing Linux bug: https://www.purebasic.fr/english/viewtopic.php?t=81284
#TEST_SHOULD_DUMMY_DRAW_BEFORE_REGISTER_FONT = #True

; Whether or not the OTF font should be registered.
#TEST_LOAD_OTF = #True

; Whether or not the TTF font should be registered.
#TEST_LOAD_TTF = #True

; Helpers.
; ═══════════════════════════════════════════════════════

Procedure DownloadIfNotPresent(url.s, outPath.s, expectedHash.s)
  Protected humanName.s = GetFilePart(outPath)
  
  ; Exit early if already present with expected hash.
  If FileSize(outPath) > 0 And FileFingerprint(outPath, #PB_Cipher_SHA3, 512) = expectedHash
    Debug "Reusing existing " + humanName + "!"
    ProcedureReturn
  EndIf
  
  ; Attempt download...
  Debug "Downloading " + humanName + "..."
  If Not ReceiveHTTPFile(url, outPath)
    Debug "Download failed!"
    End
  EndIf
  
  ; Verify hash of downloaded file...
  Protected resultHash.s = FileFingerprint(outPath, #PB_Cipher_SHA3, 512)
  If resultHash <> expectedHash
    Debug "Resulting hash " + resultHash + " does not match expectation of: " + expectedHash
    End
  EndIf
  
  Debug "Download OK!" + #CRLF$
EndProcedure

Procedure ExtractIfNotPresent(archivePath.s, archiveEntryPath.s, outPath.s, expectedHash.s)
  Protected humanName.s = GetFilePart(outPath)
  
  ; Exit early if already present with expected hash.
  If FileSize(outPath) > 0 And FileFingerprint(outPath, #PB_Cipher_SHA3, 512) = expectedHash
    Debug "Reusing existing " + humanName + "!"
    ProcedureReturn
  EndIf
  
  ; Attempt extract...
  Protected pack = OpenPack(#PB_Any, archivePath, #PB_PackerPlugin_Zip)
  Debug "Extracting " + archiveEntryPath + "..."
  If Not pack
    Debug "Failed to open pack: " + archivePath
    End
  EndIf
  
  If UncompressPackFile(pack, outPath, archiveEntryPath) <= 0
    Debug "Failed to decompress file '" + archiveEntryPath + "' of archive: " + archivePath
    End
  EndIf
  
  ; Verify hash of extracted file...
  Protected resultHash.s = FileFingerprint(outPath, #PB_Cipher_SHA3, 512)
  If resultHash <> expectedHash
    Debug "Resulting hash " + resultHash + " does not match expectation of: " + expectedHash
    End
  EndIf
  
  ClosePack(pack)
  
  Debug "Extract OK!" + #CRLF$
EndProcedure

; Entry.
; ═══════════════════════════════════════════════════════

; Create directory if missing.
If FileSize(#OUT_DIR) <> -2
  CreateDirectory(#OUT_DIR)
EndIf

; Download license + fonts if missing...
DownloadIfNotPresent(#SRC_LICENSE, #OUT_LICENSE, #CRC_LICENSE)
DownloadIfNotPresent(#SRC_PKG_OTF, #OUT_PKG_OTF, #CRC_PKG_OTF)
DownloadIfNotPresent(#SRC_PKG_TTF, #OUT_PKG_TTF, #CRC_PKG_TTF)

; Extract fonts...
ExtractIfNotPresent(#OUT_PKG_OTF, #SRC_ARC_OTF, #OUT_FNT_OTF, #CRC_FNT_OTF)
ExtractIfNotPresent(#OUT_PKG_TTF, #SRC_ARC_TTF, #OUT_FNT_TTF, #CRC_FNT_TTF)

; Trigger a Linux font use bug where desired.
CompilerIf #TEST_SHOULD_DUMMY_DRAW_BEFORE_REGISTER_FONT
  Define dummyImg = CreateImage(#PB_Any, 512, 512, 32)
  StartDrawing(ImageOutput(dummyImg))
  StopDrawing()
  FreeImage(dummyImg)
CompilerEndIf

; Register OTF unless disabled.
CompilerIf #TEST_LOAD_OTF
  Define regFontRes = RegisterFontFile(#OUT_FNT_OTF)
  Debug "Register OTF (" + #OUT_FNT_OTF + "): " + regFontRes
CompilerEndIf

; Register TTF unless disabled.
CompilerIf #TEST_LOAD_TTF
  Define regFontRes = RegisterFontFile(#OUT_FNT_TTF)
  Debug "Register TTF (" + #OUT_FNT_TTF + "): " + regFontRes
CompilerEndIf

; Try and load fonts.
Global fntOTF = LoadFont(#PB_Any, #FNT_NAME_OTF, 16)
Global fntTTF = LoadFont(#PB_Any, #FNT_NAME_TTF, 16)

; Various pangrams because the quick brown fox tired itself out jumping over dogs all day.
Global Dim pangrams.s(3)
pangrams(0) = "Sphinx of black quartz, judge my vow."
pangrams(1) = "The five boxing wizards jump quickly."
pangrams(2) = "Jackdaws love my big sphinx of quartz."
pangrams(3) = "Pack my box with five dozen liquor jugs."

; Finally... create window and render.
Global wnd = OpenWindow(#PB_Any, 0, 0, 800, 360, "OTF/TTF RegisterFont(...) Test",  #PB_Window_ScreenCentered |
                                                                                    #PB_Window_MinimizeGadget |
                                                                                    #PB_Window_MaximizeGadget |
                                                                                    #PB_Window_SizeGadget |
                                                                                    #PB_Window_Invisible)
Global canvas = CanvasGadget(#PB_Any, 0, 0, WindowWidth(wnd) / 2, WindowHeight(wnd) - 20)
Global labelLicense = TextGadget(#PB_Any, 0, WindowHeight(wnd) - 20, WindowWidth(wnd), 20, "See 3rd party font licenses: " + #OUT_LICENSE, #PB_Text_Center)
SetGadgetColor(labelLicense, #PB_Gadget_BackColor, $000000)
SetGadgetColor(labelLicense, #PB_Gadget_FrontColor, $ffffff)
SetWindowColor(wnd, 0)
SmartWindowRefresh(wnd, #True)
AddKeyboardShortcut(wnd, #PB_Shortcut_Escape, 1)

Procedure render()
  Protected y
  
  StartDrawing(CanvasOutput(canvas))
    Box(0, 0, OutputWidth(), OutputHeight(), $151515)
    
    DrawingMode(#PB_2DDrawing_Transparent)
    DrawingFont(#PB_Default)
    DrawText(8, 8, "OTF", $ffffff)
    DrawingFont(FontID(fntOTF))
    For y = 0 To ArraySize(pangrams())
      DrawText(8, DesktopScaledY(32 + 32 * y), pangrams(y), $ffffff)
    Next y
    
    DrawingFont(#PB_Default)
    DrawText(8, 8 + OutputHeight() / 2, "TTF", $ffffff)
    DrawingFont(FontID(fntTTF))
    For y = 0 To ArraySize(pangrams())
      DrawText(8, DesktopScaledY(32 + 32 * y) + OutputHeight() / 2, pangrams(y), $ffffff)
    Next y
    
  StopDrawing()
EndProcedure

Procedure onResize()
  Protected wndWidth = WindowWidth(wnd)
  Protected wndHeight = WindowHeight(wnd)
  Protected canvasHeight = wndHeight - 20
  
  If canvasHeight < 1
    canvasHeight = 1
  EndIf
  
  ResizeGadget(canvas, #PB_Ignore, #PB_Ignore, wndWidth, canvasHeight)
  ResizeGadget(labelLicense, #PB_Ignore, wndHeight - 20, wndWidth, #PB_Ignore)
  
  render()
EndProcedure

BindEvent(#PB_Event_SizeWindow, @onResize())
onResize()
HideWindow(wnd, #False)

Repeat 
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow, #PB_Event_Menu
      Break
  EndSelect
ForEver