Seite 1 von 1

Prüfen ob ein Zeichen in einer Font existiert

Verfasst: 28.04.2011 18:58
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.

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

Verfasst: 28.04.2011 20:04
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.

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

Verfasst: 28.04.2011 23:23
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 ...

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

Verfasst: 28.04.2011 23:29
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.

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

Verfasst: 29.04.2011 09:22
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

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

Verfasst: 29.04.2011 11:38
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 ...

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

Verfasst: 29.04.2011 11:49
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.

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

Verfasst: 29.04.2011 12:31
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 ;-)

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

Verfasst: 07.02.2018 21:25
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

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

Verfasst: 08.02.2018 12:14
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.