glyphs

Difference between version 16 and 20 - Previous - Next
'''Glyphs''' is a pure-tcl library for digging into TrueType font-files.

'''Glyphs''' is able to extract the vectorial paths of each glyph - points, lines, curves.

[Image glyphBeta]

'''Download'''   * '''glyphs 1.2.7''' [https://sourceforge.net/projects/irrational-numbers/files/Glyphs/Glyphs-1.2.7.zip/download] (28-Sep-2022)
   ** No API changes. Use an updated and more accurate logic for selecting detailed info from the NAME table. 
   * '''glyphs 1.2.6''' [https://sourceforge.net/projects/irrational-numbers/files/Glyphs/Glyphs-1.2.6,.zip/download] (9-Feb-201820)
   ** No API changes. Use an updated and more accurate math library for Bezier curves.    * '''glyphs 1.2''' [https://sourceforge.net/projects/irrational-numbers/files/Glyphs/Glyphs-1.2.zip/download] (2-Jul-2018)
   ** Added support for OpenTypeCollections  (*.ttc)
   ** Added support for CFF font-files (Postscript outlines)  (*.otf)
   ** nameinfo method is now deprecated ; use the new name.info method
   *** WARNING: nameinfo returns a triplet {id idstring value} ; 
   *** name.info returns just the value.   * '''glyphs 1.1.2''' [https://sourceforge.net/projects/irrational-numbers/files/Glyphs/Glyphs-1.1.2.zip/download] (9-May-2018)
   ** bug-fix:  on error during the initial parsing of a tff, file-handle is not closed (FIXED)
   ** fixed some properties names (Ascender Descender LineGap ...); they become 'camelCase' (ascender descender lineGap ...) . Old names are still supported although deprecated   * '''glyphs 1.1.1''' [https://sourceforge.net/projects/irrational-numbers/files/Glyphs/Glyphs-1.1.1.zip/download]
   ** bug-fix: wrong SVG countours for glyphs having no control-points on-the-curve (FIXED)
   ** bug-fix: wrong glyph selection for "notdef" glyphes or empty glyphes (like "space") (FIXED)   * '''glyphs 1.1''' [https://gsourceforgled.net/privoje.cts/irrational-numbers/hofilest/0B74bNhLEGm4ZX2R6V0RCX1o3U2lyphs/Glyphs-1.1.zip/download]
   ** added methods for getting glyph's advancewidth, leftsidebearing, chars
   ** added methods for kerning
   ** updated test-suite    * '''glyphs 1.0.1''' [https://gsourceforgled.net/privoje.cts/irrational-numbers/hofilest/0B74bNhLEGm4ZX2R6V0RCX1o3U2lyphs/Glyphs-1.0.1.zip/download]
   ** FIX for TclTk 8.6.1 BUG (Itcl incompatibility). Changes now works with TclTk 8.6.1 as well with previous versions.   * '''glyphs 1.0''' [https://sourceforge.net/projects/irrational-numbers.googlecode.com/files/Glyphs/Glyphs-1.0.zip/download] (BUG)
   * '''glyphs 0.9''' [https://sourceforge.net/projects/irrational-numbers.googlecode.com/files/Glyphs/glyphs-0.9.zip/download] (OLD)

See also [glyphs-demo]

** Opening and closing **

This is a first quick trip; just open a .ttf, look inside and close
======
> package require Glyphs
> set fObj [Glyphs::new "arial.ttf"]
> set ng [$fObj get numGlyphs]
> puts "found $ng glyphs"
> $fObj destroy
======

** Inspecting font file's properties **

Reopen the .ttf file
======
> set fObj [Glyphs::new "arial.ttf"]
======
we already know how to get the number-of-glyphs
======
> set res1 [$fObj get numGlyphs]
1674
======
and we can also get the overall-bounding box, or where the descender-line is placed
======
> set res2 [$fObj get bbox]
-1361 -665 4096 2060
>set res3 [$fObj get descender]
-434
======
but there're a lot of properties and the better way to know which properties are set is:

======
> set props [$fObj get]
fontPath numGlyphs bbox unitsPerEm fontRevision ascender descender .....
======
Other font properties can be retrieved from the internal 'name' table:
======
> foreach {id name} [$fObj nameinfo] {
>    lassign [$fObj nameinfo $id] id name value
>    puts "$id - $name\n$value\n"
> }
1 - Font Family name
Arial

2 - Font Subfamily name
Regular

3 - Unique font identifier
Monotype:Arial Regular:Version 3.00 (Microsoft)

4 - Full font name
Arial
 ....
======

** Working with OpenType Collections **
An OpenTypeCollection (*.ttc) contains more than one font.
If you dont' specify any, the first font is selected
======
> set mfObj [Glyphs::new "AmericanTypewriter.ttc"]
> set ng [$mfObj get numGlyphs]
> puts "found $ng glyphs"
======
You can also get the list of included fonts
======
> foreach subFont [$mfObj get subFonts] {
>    puts $subFont"
> }
======
If you want to work with a specific subFont, then specify its index ( 0..n )
when opening the fontfile
======
> $mfObj close
> set mfObj [Glyphs::new "AmericanTypewriter.ttc" 3]
>  ...
======

** Locating a single glyph **
The quickest way to locate a single glyph is through its glyph-index.
We know that "arial.ttf" has 1674 glyphs, therefore we can get all glyphs from 0 to 1674.

Now let's take the 144th glyph
======
>set g144 [$fObj glyph 144]
======
As with fObj's properties, we can get some specific glyph's property:
======
>$g144 get index 
144
>$g144 get bbox
1 0 1936 1466
======
In order to list all the available properties
======
> $g144 get
index bbox points instructions pathLengths paths ...
======
Forget about the "instructions" property it's a binary string that "Glyphs" is not
currently able to decode. (Probably it will removed in the next revision.)

The most important properties are '''points '''and '''paths'''

Let's take a glyph simpler than 144
======
>set g103 [$fObj glyph 103]
>set L [$g103 get points]
{99 714 1 99 1079 0 ...} {299 711 1 299 446 0 ...} {516 1556 1 516 1761 1 ...} {889 1556 1 ...}
======
Result looks like a list of 4 lists (4 contours).

Each contour is made of a sequence of triples: x y flag. 

Flag "1" means that point (x,y) is on-curve, Flag "0" means point (x,y) is the control point of a Quadratic Bezier curve.

But, how to translate these "points" in a parametric curve?

======
>set L [$g103 get paths]
{{M 99 714} {Q 99 1079 295.0 1285.5} ...} 
{{M 299 711} {Q 299 446 441.5 293.5} ... }
{{M 516 1556} {L 516 1761} ... }
{{M 889 1556} {L 889 1761} ...}
======
Result is a list of (4) contours. 

Each contour is made of a sequence of simple abstract commands:
   * M x y -- set (x,y) as the current point
   * L x y -- draw a line from current point to (x,y).  (x,y) then becomes the current point
   * Q x1 y1 x2 y2 -- draw a quadratic bezier from current point, to (x1,y1) (control point) and to (x2,y2) (end-point). (x2,y2) then becomes the current point.
   * C x1 y1 x2 y2 x3 y3 -- draw a cubic bezier from current point, to (x1,y1) (control point), (x2,y2) (control point) and to (x3,y3) (end-point). (x3,y3) then becomes the current point

It's your app's responsability to translate these 'abstract commands' in real drawing commands.
A very simple implementation for the standard canvas-widget could be the following:

======
proc Paths2Canvas { cvs paths } {
    foreach path $paths {
         # first command should be M (MOVETO)
        foreach pCmd $path {
            set points [lassign $pCmd cmd]
            switch -- $cmd {
                M {
                    ;
                }
                L {
                        $cvs create line $lastX $lastY {*}$points
                }
                Q {
                    $cvs create line $lastX $lastY {*}$points -smooth true
                }
                C { ;# CUBICTO
                    $cvs create line $lastX $lastY {*}$points -smooth raw
                }
                default { error "unrecognized path command \"$cmd\"" }            
            }
            set lastX [lindex $points end-1]
            set lastY [lindex $points end]            
        }
    }
}
======
A better implementation should take care to join consecutive segments in a
single polyline ....
Look at [glyphs-demo] code for a more complete example.


** Converting a glyph in a polyline, tangents, normals and more .. **

The "onUniformDistance" method is the last, more powerful method acting on a glyph.

We can 'split' the whole glyphs in a series of 2D points equally spaced.
======
> $g103 onUniformDistance 100.0 "at"
======
returns N (long) lists (one list for each glyph's countour) made of x y pairs;
each (x,y) is a point on the glyph, and all these points are equally spaced 
(with some arrangements for dealing with the curves extremities ...)

We can also get the tangent or normal *versors* for these points.
======
> $g103 onUniformDistance 100.0 "tangent_at"
> $g103 onUniformDistance 100.0 "normal_at"
======
and finally we can get the tangent or normal *vectors* (i.e., a segment starting
from point "at" having the tangent/normal direction).
======
> $g103 onUniformDistance 100.0 "vtangent_at"
> $g103 onUniformDistance 100.0 "vnormal_at"
======
We suggest to do some experiment with the [glyphs-demo] app.

** Getting more glyph metrics **

Getting the AdvanceWidth, LeftSideBearing, chars (list of all characters (unicode) sharing a glyph)
======
> $g103 get advanceWidth
1593
> $g103 get leftSideBearing
99
> $g103 get chars
214
======
The above commands are equivalent to:
======
> $fObj gget 103 advanceWidth
> $fObj gget 103 leftSideBearing
> $fObj gget 103 chars
======

Getting the horizontal kerning value for the "A" "V" letters
======
> $fObj getKerning [$fObj unicode2glyphIndex "A"] [$fObj unicode2glyphIndex "V"] "H"
-152
======

----

There's not much more to do with a single-glyph.
Since it's a dynamic object, you can free space when it's no more useful,
======
>$g103 destroy
======  
but, you are not required to do it. In fact, when the 'font-file' is destroyed
======
>$fObj destroy 
======  
all space used for its allocated glyphs is freed.


** More on accessing glyphs **

We've seen the standard way to access a glyph by-index.
The major part of TrueType Fonts  holds a table for converting a "character" (unicode) to index.
======
>$fObj unicode2glyphIndex "A"
>$fObj unicode2glyphIndex "ß"    ; # unicode char \u03B2 (greek letter "Beta")
>$fObj unicode2glyphIndex \u03B2 ; # unicode char \u03B2 (greek letter "Beta")
>$fObj numcode2glyphIndex 946    ; # it's always the greek letter "Beta" !
======
For your convenience, you can access a glyph, with two new methods

======
>$fObj glyphByUnicode  "ß"  ; # glyph by Unicode
>$fObj glyphByCharCode 946  ; # glyph by CharCode
======

** Comments and Suggestions **

[ABU]  '''Glyphs ''' requires more documentation. Within the package some documentation and a full test-suite are included.

<<categories>> Package