Prüfen ob ein Zeichen in einer Font existiert

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6996
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Prüfen ob ein Zeichen in einer Font existiert

Beitrag von STARGÅTE »

Tachchen,

ich habe da mal eine Frage:

Wie kann ich von einer Font (die zB. mit LoadFont geladen wurde, oder einer API) herausfinden, welche Zeichen wirklich enthalten sind.
Normalerweise ist es ja bei Fonts so, dass bei Zeichen die nicht wirklich existieren so n Recheck kommt.
Genau diese Zeichen will ich nun filtern, sodass, ähnlich wie bei der MS-Zeichentabelle, nur Zeichen dargestellt werden, die auch wirklich enthalten sind.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
Danilo
-= Anfänger =-
Beiträge: 2284
Registriert: 29.08.2004 03:07

Re: Prüfen ob ein Zeichen in einer Font existiert

Beitrag von Danilo »

Schau Dir mal folgende 2 Themen an:
The GetFontUnicodeRanges function returns information about which Unicode characters are supported by a font.
und
GetTextMetrics
Die TEXTMETRIC Struktur enthält 'TCHAR tmDefaultChar': The value of the character to be substituted for characters not in the font.
GetFontUnicodeRanges klingt interessant. So solltest Du an eine Map kommen,
welche Chars wirklich enthalten sind.
cya,
...Danilo
"Ein Genie besteht zu 10% aus Inspiration und zu 90% aus Transpiration" - Max Planck
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6996
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Prüfen ob ein Zeichen in einer Font existiert

Beitrag von STARGÅTE »

Danke Danilo, aber leider helfen mir beide Sachen nicht weiter.

GetTextMetrics enthält ja nur den Hinweis auf das zu verwendende Zeichen, wenn ein anderes Zeichen nicht existiert.
Ob es nicht existiert, kann ich daraus aber nicht ablesen.

GetFontUnicodeRanges wird (zumindest bei mir) garnicht unterstützt, fällt somit eh weg.

Ich habe auch schon Probiert, die Zeichenbreiten zu vergleichen, sodass Zeichen die gleich dem Default-Zeichen sind, rausfliegen, nur leider gibt es dabei zu viele Fehler ...
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
RSBasic
Admin
Beiträge: 8022
Registriert: 05.10.2006 18:55
Wohnort: Gernsbach
Kontaktdaten:

Re: Prüfen ob ein Zeichen in einer Font existiert

Beitrag von RSBasic »

@STARGÅTE
Ja leider bietet PB nicht die volle WinAPI-Unterstützung.
GetFontUnicodeRanges_() musst du vorher aus der Gdi32.dll importieren, so dass dieser Befehl verwendet werden kann.
Aus privaten Gründen habe ich leider nicht mehr so viel Zeit wie früher. Bitte habt Verständnis dafür.
Bild
Bild
Benutzeravatar
Danilo
-= Anfänger =-
Beiträge: 2284
Registriert: 29.08.2004 03:07

Re: Prüfen ob ein Zeichen in einer Font existiert

Beitrag von Danilo »

STARGÅTE hat geschrieben:GetFontUnicodeRanges wird (zumindest bei mir) garnicht unterstützt, fällt somit eh weg.
Nicht so schnell aufgeben! :)

Da es mit Imports auch nicht ging (PB hat keine aktuellen WinAPI-Import-Libs),
muß man es halt wieder über OpenLibrary() machen. Kein Problem.

Ich habe jetzt für Dich ein komplettes Beispiel geschrieben, welches für einen
ausgewählten Font alle Unicode-Ranges und alle dazugehörigen Unicode-Chars
anzeigt. Ich hoffe es hilft Dir als Einstieg.

Code: Alles auswählen

;
; by Danilo, April 2011, PB 4.51 (x86)
;
; GetFontUnicodeRanges_() demo
; 
; - displays unicode character ranges for a given font
; - displays the unicode characters
;
; requires Windows 2000+
;
; http://forums.purebasic.com/german/viewtopic.php?f=16&t=24209
;
EnableExplicit

CompilerIf #PB_Compiler_Unicode = 0
   MessageRequester("ERROR","Compile with Unicode support!")
   End
CompilerEndIf

;---------------------------------------------------------------
; WinAPI Imports and Declarations
;---------------------------------------------------------------
Structure WCRANGE
   wcLow.u
   cGlyphs.u
EndStructure

Structure GLYPHSET
  cbThis.l
  flAccel.l
  cGlyphsSupported.l
  cRanges.l
  ranges.WCRANGE[$FFFF]
EndStructure

Prototype.l GetFontUnicodeRanges(hdc.l, *gs.GLYPHSET)

Procedure.l GetFontUnicodeRanges_(hdc.l, *gs.GLYPHSET)
  ;
  ; [in]  hdc             A handle to the device context.
  ; [out] *gs.GLYPHSET    A pointer to a GLYPHSET structure that receives
  ;                       the glyph set information.
  ;                       If this parameter is #Null, the function returns
  ;                       the size of the GLYPHSET Structure required
  ;                       To store the information.
  ;
  ; ReturnValue:          If the function succeeds, it returns number of bytes
  ;                       written To the GLYPHSET Structure Or, If the *gs parameter
  ;                       is #Null, it returns the size of the GLYPHSET Structure
  ;                       required To store the information.
  ;                       If the function fails, it returns zero.
  ;
  ; Requirements:         Windows 2000 Professional
  ;
  Define gdi32.l, returnValue.l
  Define gfur.GetFontUnicodeRanges

  If OSVersion() >= #PB_OS_Windows_2000
    gdi32 = OpenLibrary(#PB_Any,"gdi32.dll")
    If gdi32
      gfur = GetFunction(gdi32,"GetFontUnicodeRanges")
      If gfur
         returnValue = gfur(hdc,*gs)
      EndIf
      CloseLibrary(gdi32)
    EndIf
  EndIf
  ProcedureReturn returnValue
EndProcedure
;---------------------------------------------------------------



Procedure getFontRanges( _font, List _ranges.WCRANGE())
    ;
    ; getFontRanges() fills the LinkedList _ranges() 
    ; with the wide char ranges supported by _font
    ;
    Define hdc, size, i, oldFont
    Define *gs.GLYPHSET

    ClearList( _ranges() )
    
    hdc     = GetDC_( 0 )
    oldFont = SelectObject_(hdc,FontID(_font))
    size    = GetFontUnicodeRanges_(hdc,#Null)
    If size
       *gs = AllocateMemory(size)
       If *gs
          If GetFontUnicodeRanges_(hdc,*gs)
             For i = 0 To *gs\cRanges-1
                If AddElement( _ranges() )
                   _ranges()\wcLow   = *gs\ranges[i]\wcLow
                   _ranges()\cGlyphs = *gs\ranges[i]\cGlyphs
                EndIf
             Next i
          EndIf
          FreeMemory(*gs)
       EndIf
    EndIf
    SelectObject_(hdc,oldFont)
    ReleaseDC_(0,hdc)

EndProcedure


Procedure UpdateListBoxes(lbRanges, lbOut, font)
    ;
    ; get the WCRANGES for font
    ; and update the listboxes
    ;
    NewList ranges.WCRANGE()

    getFontRanges( font, ranges() )
    
    SendMessage_(GadgetID(lbRanges),#WM_SETREDRAW,0,0)
    SendMessage_(GadgetID(lbOut),#WM_SETREDRAW,0,0)
    
    ClearGadgetItems(lbRanges)
    ClearGadgetItems(lbOut)

    AddGadgetItem(lbRanges,-1,"     All")
    ForEach ranges()
       Define range$ = RSet(StrU(ranges()\wcLow,#PB_Unicode),5) + " - " + RSet(StrU(ranges()\wcLow+ranges()\cGlyphs-1,#PB_Unicode),5)
       AddGadgetItem(lbRanges,-1,range$)
    Next
    SetGadgetState(lbRanges,0)
    
    ForEach ranges()
       Define j
       For j = 0 To ranges()\cGlyphs-1
          AddGadgetItem(lbOut,-1, Chr(ranges()\wcLow+j) )
       Next
       ;AddGadgetItem(lbOut,-1,"-------------------")
    Next

    SendMessage_ (GadgetID(lbRanges),#WM_SETREDRAW,1,0)
    SendMessage_ (GadgetID(lbOut)   ,#WM_SETREDRAW,1,0)
    RedrawWindow_(GadgetID(lbRanges),0,0,#RDW_ERASE|#RDW_FRAME|#RDW_INVALIDATE|#RDW_ALLCHILDREN)
    RedrawWindow_(GadgetID(lbOut)   ,0,0,#RDW_ERASE|#RDW_FRAME|#RDW_INVALIDATE|#RDW_ALLCHILDREN)

EndProcedure



Define mainWindow
Define fontButton, fontInfo, listBoxRanges, listBoxOutput
Define listBoxRangesFont, listBoxOutputFont, fontInfoFont, tempFont
Define currentFontName$, currentFontSize, currentFontStyle
Define eventGadget
Define listBoxRangesSelection$
Define listBoxRangesSelectionStart.q, listBoxRangesSelectionEnd.q
Define i

mainWindow = OpenWindow(#PB_Any,0,0,800,500,"GetFontUnicodeRanges_",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
If mainWindow
    
    currentFontName$ = "Arial"
    currentFontSize  = 24
    currentFontStyle = 0
    
    listBoxOutputFont = LoadFont(#PB_Any, currentFontName$, currentFontSize, currentFontStyle)
    listBoxRangesFont = LoadFont(#PB_Any, "Lucida Console", 12)
    fontInfoFont      = LoadFont(#PB_Any, "Lucida Console", 18, #PB_Font_Bold)
    
    fontButton = ButtonGadget(#PB_Any,10,5,150,25,"Set Font")
    fontInfo   = TextGadget(#PB_Any,170,5,620,25,currentFontName$+", "+Str(currentFontSize))
    SetGadgetFont(fontInfo,FontID(fontInfoFont))
    
    TextGadget(#PB_Any,10,35,155,20,"Unicode Ranges:")
    listBoxRanges = ListViewGadget(#PB_Any,10,55,155,440)
    SetGadgetFont(listBoxRanges,FontID(listBoxRangesFont))
    SetGadgetColor(listBoxRanges,#PB_Gadget_FrontColor,RGB($00,$FF,$80))
    SetGadgetColor(listBoxRanges,#PB_Gadget_BackColor ,RGB($00,$00,$00))

    TextGadget(#PB_Any,170,35,150,20,"Valid Characters:")
    listBoxOutput = ListViewGadget(#PB_Any,170,55,620,440)
    SetGadgetFont(listBoxOutput,FontID(listBoxOutputFont))
    SetGadgetColor(listBoxOutput,#PB_Gadget_FrontColor,RGB($00,$FF,$80))
    SetGadgetColor(listBoxOutput,#PB_Gadget_BackColor ,RGB($00,$00,$00))

    UpdateListBoxes(listBoxRanges,listBoxOutput,listBoxOutputFont)

    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Break
        Case #PB_Event_Gadget
          eventGadget = EventGadget()
          If EventGadget = fontButton
             If FontRequester(currentFontName$, currentFontSize,0,0,currentFontStyle)
                currentFontName$ = SelectedFontName()
                currentFontSize  = SelectedFontSize()
                currentFontStyle = SelectedFontStyle()
                tempFont =  listBoxOutputFont
                listBoxOutputFont = LoadFont(#PB_Any, currentFontName$, currentFontSize, currentFontStyle)
                SetGadgetFont(listBoxOutput,FontID(listBoxOutputFont))
                SetGadgetText(fontInfo, currentFontName$+", "+Str(currentFontSize))
                UpdateListBoxes(listBoxRanges,listBoxOutput,listBoxOutputFont)
                FreeFont(tempFont)
             EndIf
          ElseIf EventGadget = listBoxRanges
             listBoxRangesSelection$ = GetGadgetItemText(listBoxRanges,GetGadgetState(listBoxRanges))
             If listBoxRangesSelection$ = "     All"
                UpdateListBoxes(listBoxRanges,listBoxOutput,listBoxOutputFont)
             Else
                 listBoxRangesSelectionStart = Val(StringField(listBoxRangesSelection$,1,"-"))
                 listBoxRangesSelectionEnd   = Val(StringField(listBoxRangesSelection$,2,"-"))
                 SendMessage_ (GadgetID(listBoxOutput),#WM_SETREDRAW,0,0)
                 ClearGadgetItems(listBoxOutput)
                 For i = listBoxRangesSelectionStart To listBoxRangesSelectionEnd
                    AddGadgetItem(listBoxOutput,-1, Chr(i) )
                 Next i
                 SendMessage_ (GadgetID(listBoxOutput),#WM_SETREDRAW,1,0)
                 RedrawWindow_(GadgetID(listBoxOutput),0,0,#RDW_ERASE|#RDW_FRAME|#RDW_INVALIDATE|#RDW_ALLCHILDREN)
             EndIf
          EndIf
      EndSelect
    ForEver
EndIf
EDIT: WCRANGE\cGlyphs.w zu WCRANGE\cGlyphs.u geändert
cya,
...Danilo
"Ein Genie besteht zu 10% aus Inspiration und zu 90% aus Transpiration" - Max Planck
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6996
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Prüfen ob ein Zeichen in einer Font existiert

Beitrag von STARGÅTE »

Danke Danilo,

dieser Code ist schon mal ein Schritt in die richtige Richtung.

Ganz zufriden bin ich jedoch trotzdem nicht mit dem Ergebnis, da bei manchen Fonts immer noch diese Rechtecke dabei sind.
Aber ich vermute dort, dass die Font einfach "zu billig" ist, und diese Intervalle garnicht unterstützt, oder irgendwie so.

Auf jedenfall hilft mir dein Code weiter, also danke ...
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
Danilo
-= Anfänger =-
Beiträge: 2284
Registriert: 29.08.2004 03:07

Re: Prüfen ob ein Zeichen in einer Font existiert

Beitrag von Danilo »

STARGÅTE hat geschrieben:Ganz zufrieden bin ich jedoch trotzdem nicht mit dem Ergebnis, da bei manchen Fonts immer noch diese Rechtecke dabei sind.
Aber ich vermute dort, dass die Font einfach "zu billig" ist, und diese Intervalle garnicht unterstützt, oder irgendwie so.
Das ist mir auch aufgefallen, bei einigen Fonts. Ich meine auch, daß das
ein Fehler in den Fonts ist (manche Zeichen nicht implementiert, aber
trotzdem in der Unicode-Range angegeben), denn was wir hier nutzen ist
der offiziell dokumentierte Weg über GetFontUnicodeRanges_().
Microsofts Fonts scheinen aber sauber entwickelt worden zu sein.
cya,
...Danilo
"Ein Genie besteht zu 10% aus Inspiration und zu 90% aus Transpiration" - Max Planck
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6996
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Prüfen ob ein Zeichen in einer Font existiert

Beitrag von STARGÅTE »

Also ich habe eben mal eine Test-Font mit FontStruct erstellt:

Mit nur 8 Zeichen : abcd.ttf

Nach dem installieren, arbeitet der Code mit dieser Font richtig:
All
0 - 0
13 - 13
32 - 32
65 - 68
97 - 100
Klar 32 gibs immer (Leerzeichen), und 0 und 13 sind vermutlich FontStuct spezifisch ^^
Auf jedenfall werden wirklich nur a-d und A-D angezeigt

Also Danke nochmal ;-)
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
juergenkulow
Beiträge: 188
Registriert: 22.12.2016 12:49
Wohnort: :D_üsseldorf-Wersten

Re: Prüfen ob ein Zeichen in einer Font existiert U+1D400-7F

Beitrag von juergenkulow »

Hallo,

ich versuche zu prüfen ob ein Unicode-Zeichen größer $10000 in einem Font existiert.

Code: Alles auswählen

currentFontName$ = "Cambria Math" 
Leider wird mir bei Unicode-Ranges als letztes dezimal 65024-65024 (hex $FE00) angezeigt.
EDIT: WCRANGE\cGlyphs.w zu WCRANGE\cGlyphs.u geändert
Liegt da ein Fehler bei der Größe des Typs vor, cGlyphs WCHAR: A 16-bit Unicode character kann $1D400 nicht darstellen?
Oder gibt es einen anderen Weg Windows nach der Existenz eines hohen Unicode-Zeichens in einem Font zu befragen?

Beispiel zu hohen Unicode Zeichen:

Code: Alles auswählen

Procedure.s UnicodeChr(Number.i)
  Protected Buffer.q
  If Number <= $FFFF
    Buffer = Number
    ProcedureReturn PeekS(@Buffer, -1, #PB_Unicode)
  Else
    Buffer = (Number-$10000)>>10 | ((Number-$10000)&$3FF)<<16 + $DC00D800
    ProcedureReturn PeekS(@Buffer, -1, #PB_Unicode)
  EndIf
EndProcedure

For i=$1D400 To $1D7FF :   Text$+UnicodeChr(i) :  Next i 

If OpenWindow(0, 0, 0, 1900, 1000, "VectorDrawing", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  CanvasGadget(0, 0, 0, 1900, 1000)
  LoadFont(0, "Cambria Math", 20)
    If StartVectorDrawing(CanvasVectorOutput(0))
   VectorFont(FontID(0), 20)
    j=0
    For i=1 To Len(Text$) Step 26*4 
      MovePathCursor(0,j)
      j+20 
      DrawVectorText(Mid(Text$,i,26*4))
    Next i        
    StopVectorDrawing()
  EndIf
 MessageRequester("$1D400-$1D7FF",Text$) 
;   Repeat
;     Event = WaitWindowEvent()
;   Until Event = #PB_Event_CloseWindow
EndIf 
https://en.wikibooks.org/wiki/Unicode/C ... D000-1DFFF
Bitte stelle Deine Fragen, denn den Erkenntnisapparat einschalten entscheidet über das einzig bekannte Leben im Universum.

Jürgen Kulow Wersten :D_üsseldorf NRW D Europa Erde Sonnensystem Lokale_Flocke Lokale_Blase Orion-Arm
Milchstraße Lokale_Gruppe Virgo-Superhaufen Laniakea Sichtbares_Universum
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6996
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Prüfen ob ein Zeichen in einer Font existiert

Beitrag von STARGÅTE »

Gute Frage juergenkulow,

auch ich habe nach dieser Möglichkeit schon gesucht, da ich auch einige Zeichen über $FFFF brauche, aber wissen will ob sie überhaupt existieren.
Nachdem ja nun DrawText usw. diese Zeichen mit den Surrogates richtig anzeigen, kann ich aber leider nicht die WIN API nutzen, da die Struktur dies wie du selbst erkannt hast nicht ausreicht (sie ist aber richtig definiert und kann nur bis $FFFF)

Ich habe leider bislang nichts gefunden.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Antworten