[squeak-dev] The Inbox: TrueType-enno.9.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[squeak-dev] The Inbox: TrueType-enno.9.mcz

commits-2
A new version of TrueType was added to project The Inbox:
http://source.squeak.org/inbox/TrueType-enno.9.mcz

==================== Summary ====================

Name: TrueType-enno.9
Author: enno
Time: 13 September 2009, 5:33:25 am
UUID: c5f974ba-96fc-014c-82cf-1703c7165a52
Ancestors: TrueType-ar.8

incorporates typo fix from
http://code.google.com/p/pharo/issues/detail?id=1045
into trunk

==================== Snapshot ====================

SystemOrganization addCategory: #'TrueType-Fonts'!
SystemOrganization addCategory: #'TrueType-Support'!

Object subclass: #TTContourConstruction
        instanceVariableNames: 'points'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'TrueType-Support'!

!TTContourConstruction commentStamp: '<historical>' prior: 0!
This class represents a temporary contour structure during the construction of a TTGlyph from a TrueType file.

Instance variables:
        points <Array of: TTPoint> The points defining this contour!

----- Method: TTContourConstruction class>>on: (in category 'instance creation') -----
on: points

        ^self new points: points!

----- Method: TTContourConstruction>>asCompressedPoints (in category 'converting') -----
asCompressedPoints
        "Return the receiver compressed into a PointArray.
        All lines will be converted into bezier segments with
        the control point set to the start point"
        | out minPt maxPt fullRange |
        minPt := -16r7FFF asPoint.
        maxPt := 16r8000 asPoint.
        "Check if we need full 32bit range"
        fullRange := points anySatisfy: [:any| any asPoint < minPt or:[any asPoint > maxPt]].
        fullRange ifTrue:[
                out := WriteStream on: (PointArray new: points size).
        ] ifFalse:[
                out := WriteStream on: (ShortPointArray new: points size).
        ].
        self segmentsDo:[:segment|
                out nextPut: segment start.
                segment isBezier2Segment
                        ifTrue:[out nextPut: segment via]
                        ifFalse:[out nextPut: segment start].
                out nextPut: segment end.
        ].
        ^out contents!

----- Method: TTContourConstruction>>points (in category 'accessing') -----
points
        ^points!

----- Method: TTContourConstruction>>points: (in category 'accessing') -----
points: anArray
        points := anArray asArray.!

----- Method: TTContourConstruction>>printOn: (in category 'printing') -----
printOn: aStream

        aStream
                nextPutAll: self class name;
                nextPut:$(;
                print: points size;
                "space;
                print: self type;"
                nextPut:$)!

----- Method: TTContourConstruction>>segments (in category 'accessing') -----
segments

        | segments |
        segments := OrderedCollection new.
        self segmentsDo:[:seg| segments add: seg].
        ^segments!

----- Method: TTContourConstruction>>segmentsDo: (in category 'enumerating') -----
segmentsDo: aBlock
        "Evaluate aBlock with the segments of the receiver. This may either be straight line
        segments or quadratic bezier curves. The decision is made upon the type flags
        in TTPoint as follows:
        a) Two subsequent #OnCurve points define a straight segment
        b) An #OnCurve point followed by an #OffCurve point followed
                by an #OnCurve point defines a quadratic bezier segment
        c) Two subsequent #OffCurve points have an implicitely defined
                #OnCurve point at half the distance between them"
        | last next mid index i |
        last := points first.
        "Handle case where first point is off-curve"
        (last type == #OnCurve) ifFalse: [
                i := points findFirst: [:pt | pt type == #OnCurve].
                i = 0
                        ifTrue: [mid := TTPoint new
                                                        type: #OnCurve;
                                                        x: points first x + points last x // 2;
                                                        y: points first y + points last y // 2.
                                        points := (Array with: mid), points]
                        ifFalse: [points := (points copyFrom: i to: points size), (points copyFrom: 1 to: i)].
                last := points first].
        index := 2.
        [index <= points size] whileTrue:[
                mid := points at: index.
                mid type == #OnCurve ifTrue:[
                        "Straight segment"
                        aBlock value: (LineSegment from: last asPoint to: mid asPoint).
                        last := mid.
                ] ifFalse:["Quadratic bezier"
                        "Read ahead if the next point is on curve"
                        next := (index < points size) ifTrue:[points at: (index+1)] ifFalse:[points first].
                        next type == #OnCurve ifTrue:[
                                "We'll continue after the end point"
                                index := index + 1.
                        ] ifFalse:[ "Calculate center"
                                next := (next asPoint + mid asPoint) // 2].
                        aBlock value:(Bezier2Segment from: last asPoint via: mid asPoint to: next asPoint).
                        last := next].
                index := index + 1].
        (index = (points size + 1)) ifTrue:[
                aBlock value:(LineSegment from: points last asPoint to: points first asPoint)]!

Object subclass: #TTFileDescription
        instanceVariableNames: 'fileName fileOffset familyName subfamilyName ascender descender lineGap unitsPerEm numGlyphs indexToLocOffset indexToLocFormat glyphTableOffset cmapType cmapOffset numHMetrics hmtxTableOffset sTypoAscender sTypoDescender sTypoLineGap'
        classVariableNames: 'AllFontsAndFiles FontPaths OfferNonPortableFonts'
        poolDictionaries: ''
        category: 'TrueType-Fonts'!

!TTFileDescription commentStamp: 'ar 7/29/2009 22:18' prior: 0!
Contrary to TTFontDescritption, this class leaves true type files on disk and only reads the required portions when constructing glyphs. This avoids the need of reading the entire font into memory at the cost of having to hit disk whenever a glyph is requested.!

----- Method: TTFileDescription class>>allFamilyNamesAndFiles (in category 'font paths') -----
allFamilyNamesAndFiles
        "Answer a dictionary of all known family names and their corresponding file names."
        | names |
        AllFontsAndFiles ifNil:[
                AllFontsAndFiles := Dictionary new.
                Cursor wait showWhile:[self allFontsDo:[:font|
                        names := AllFontsAndFiles at: font familyName
                                        ifAbsentPut:[OrderedCollection new].
                        names add: font fileName]]].
        ^AllFontsAndFiles !

----- Method: TTFileDescription class>>allFontsAndFiles (in category 'font paths') -----
allFontsAndFiles
        "Answer a dictionary of all known family names and their corresponding file names."
        | names |
        AllFontsAndFiles ifNil:[
                AllFontsAndFiles := Dictionary new.
                Cursor wait showWhile:[self allFontsDo:[:font|
                        names := AllFontsAndFiles at: font familyName
                                        ifAbsentPut:[OrderedCollection new].
                        names add: font fileName]]].
        ^AllFontsAndFiles !

----- Method: TTFileDescription class>>allFontsDo: (in category 'font paths') -----
allFontsDo: aBlock
        "Evaluate aBlock with all the fonts we can find. Use sparingly."
        self fontPathsDo:[:path|
                self fontFilesIn: path do:[:font|
                        font familyName
                                ifNotNil:[aBlock value: font]]]!

----- Method: TTFileDescription class>>findFontFile: (in category 'font paths') -----
findFontFile: fontFileName
        "Find the path containing the font with the given name.
        If it can't be found, return nil."
        | fd |
        self fontPathsDo:[:path|
                fd := FileDirectory on: path.
                ([fd fileExists: fontFileName] on: Error do:[false])
                        ifTrue:[^fd fullNameFor: fontFileName].
        ].
        ^nil!

----- Method: TTFileDescription class>>fontFilesIn:do: (in category 'font paths') -----
fontFilesIn: path do: aBlock
        "TTFileDescription loadAllFilesIn: 'C:\Windows\Fonts'"
        "Load all the TTF files we can find in the given path"
        | fd |
        fd := FileDirectory on: path.
        (fd fileNamesMatching: '*.ttf;*.ttc') do:[:fn|
                (self readFontsFrom: fn) do:[:font| aBlock value: font]].!

----- Method: TTFileDescription class>>fontFromUser (in category 'user interaction') -----
fontFromUser
        "TTFileDescription fontFromUser"
        ^self fontFromUser: TextStyle defaultFont!

----- Method: TTFileDescription class>>fontFromUser: (in category 'user interaction') -----
fontFromUser: priorFont
        ^self fontFromUser: priorFont allowKeyboard: true!

----- Method: TTFileDescription class>>fontFromUser:allowKeyboard: (in category 'user interaction') -----
fontFromUser: priorFont allowKeyboard: aBoolean
        "TTFileDescription fontFromUser"
        | fontMenu active ptMenu label fontNames builder resultBlock result item style font widget |
        builder := ToolBuilder default.
        fontNames := self allFontsAndFiles keys asArray sort.
        fontMenu := builder pluggableMenuSpec new.
        fontMenu label: 'Non-portable fonts'.
        resultBlock := [:value| result := value].
        fontNames do: [:fontName |
                active := priorFont familyName sameAs: fontName.
                ptMenu := builder pluggableMenuSpec new.
                TTCFont pointSizes do: [:pt |
                        label := pt printString, ' pt'.
                        item := ptMenu add: label
                                target: resultBlock
                                selector: #value:
                                argumentList: {{fontName. pt}}.
                        item checked: (active and:[pt = priorFont pointSize]).
                ].
                item := fontMenu add: fontName action: nil.
                item subMenu: ptMenu.
                item checked: active.
        ].
        widget := builder open: fontMenu.
        builder runModal: widget.
        result ifNil:[^nil].
        style := (TextStyle named: result first) ifNil:[self installFamilyNamed: result first].
        style ifNil: [^ self].
        font := style fonts detect: [:any | any pointSize = result last] ifNone: [nil].
        ^ font
!

----- Method: TTFileDescription class>>fontOffsetsInFile: (in category 'instance creation') -----
fontOffsetsInFile: file
        "Answer a collection of font offsets in the given file"
        | tag version nFonts |
        file position: 0.
        tag := file next: 4.
        tag caseOf:{
                ['true' asByteArray] -> ["Version 1.0 TTF file"
                        "http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html
                        The values 'true' (0x74727565) and 0x00010000 are recognized by the Mac OS
                        as referring to TrueType fonts."
                        ^Array with: 0 "only one font"
                ].
                [#(0 1 0 0) asByteArray] -> ["Version 1.0 TTF file"
                        ^Array with: 0 "only one font"
                ].
                ['ttcf' asByteArray] -> ["TTC file"
                        version := file next: 4.
                        version = #(0 1 0 0) asByteArray ifFalse:[^self error: 'Unsupported TTC version'].
                        nFonts := file nextNumber: 4.
                        ^(1 to: nFonts) collect:[:i| file nextNumber: 4].
                ].
        } otherwise:[
                self error: 'This is not a valid Truetype file'.
        ].!

----- Method: TTFileDescription class>>fontPathsDo: (in category 'font paths') -----
fontPathsDo: aBlock
        "Evaluate aBlock with all of the font paths that should be searched on the current platform"

        "Start with the current directory"
        aBlock value: FileDirectory default pathName.

        "Then subdirectory 'fonts'"
        aBlock value: (FileDirectory default directoryNamed: 'fonts') pathName.

        "Platform specific directories"
        SmalltalkImage current platformName caseOf:{
                ['Win32'] -> [
                        "Standard Windows fonts directory"
                        aBlock value: 'C:\Windows\Fonts'.
                ].
                ['Mac OS'] -> [
                        "Standard system fonts directory"
                        aBlock value: '/Library/Fonts'.
                ].
                ['unix'] -> [ | base |
                        "Standard fonts are in /usr/share/fonts/*"
                        base := '/usr/share/fonts'.
                        (FileDirectory on: base) directoryNames
                                do:[:dn| aBlock value: base, '/', dn].
                ].
        } otherwise:[].
        !

----- Method: TTFileDescription class>>initialize (in category 'class initialization') -----
initialize
        "TTFileDescription initialize"
        Smalltalk addToShutDownList: self.
        FontPaths := Dictionary new.
        AllFontsAndFiles := nil.!

----- Method: TTFileDescription class>>installFamilyNamed: (in category 'instance creation') -----
installFamilyNamed: familyName
        "Install all the corresponding fonts for this family"
        "
                TTFileDescription installFamilyNamed: 'Arial'.
                TTFileDescription installFamilyNamed: 'Batang'.
        "
        | fontFiles ttDesc |
        fontFiles := self allFontsAndFiles at: familyName ifAbsent:[#()].
        fontFiles do:[:fileName|
                ttDesc := (self readFontsFrom: fileName) detect:[:fnt| fnt familyName = familyName].
                TTCFont newTextStyleFromTT: ttDesc.
        ].
        ^TextStyle named: familyName!

----- Method: TTFileDescription class>>loadAllFontFiles (in category 'examples') -----
loadAllFontFiles
        "Load all the TTF files we can find in all font paths"
        "
                TTFileDescription loadAllFontFiles.
        "
        self fontPathsDo:[:path| | fd |
                fd := FileDirectory on: path.
                (fd fileNamesMatching: '*.ttf;*.ttc') do:[:fn|
                        (self readFontsFrom: fn) do:[:font|
                                (1 to: font numGlyphs)
                                        do:[:i| font readGlyphAt: i-1]
                                        displayingProgress: 'Reading ', font name].
                ] displayingProgress: 'Scanning ', path.
        ].!

----- Method: TTFileDescription class>>offerNonPortableFonts (in category 'user interaction') -----
offerNonPortableFonts
        "Should native fonts be offered when displaying font menus?"
        <preference: 'Offer Native Fonts'
                category: 'Morphic'
                description: 'When true, an additional menu is offered for choosing non-portable fonts'
                type: #Boolean>
        ^OfferNonPortableFonts ifNil:[true]!

----- Method: TTFileDescription class>>offerNonPortableFonts: (in category 'user interaction') -----
offerNonPortableFonts: aBool
        "Should native fonts be offered when displaying font menus?"
        OfferNonPortableFonts := aBool.!

----- Method: TTFileDescription class>>openFontFile:do: (in category 'instance creation') -----
openFontFile: fontFileName do: aBlock
        "Open the font with the given font file name"
        | fontFilePath file |
        fontFilePath := FontPaths at: fontFileName
                ifAbsentPut:[self findFontFile: fontFileName].
        fontFilePath ifNil:[^nil].
        [file := FileStream readOnlyFileNamed: fontFilePath] on: Error do:[:ex|
                "We lost the font; someone might have moved it away"
                fontFilePath removeKey: fontFileName ifAbsent:[].
                ^nil
        ].
        ^[aBlock value: file binary] ensure:[file close].!

----- Method: TTFileDescription class>>readFontsFrom: (in category 'instance creation') -----
readFontsFrom: aFilename
        "Reads and returns all the fonts in the given file"
        "
                TTFileDescription readFontsFrom: 'batang.ttc'.
        "
        ^self openFontFile: aFilename do:[:file|
                (self fontOffsetsInFile: file)
                        collect:[:offset| self new on: aFilename offset: offset]
                        thenSelect:[:font| font notNil]].
        !

----- Method: TTFileDescription class>>shutDown (in category 'class initialization') -----
shutDown
        "Flush my caches"
        FontPaths := Dictionary new.
        AllFontsAndFiles := nil.!

----- Method: TTFileDescription>>ascender (in category 'accessing') -----
ascender
        "Ascender of the font. Relative to unitsPerEm.
        Easily confused with the typographic ascender."
        ^ascender!

----- Method: TTFileDescription>>at: (in category 'accessing') -----
at: charOrCode
        "Compatibility with TTFontDescription"
        ^self glyphAt: charOrCode!

----- Method: TTFileDescription>>childGlyphAt:in:fromFile: (in category 'glyphs') -----
childGlyphAt: glyphIndex in: glyphCache fromFile: fontFile
        "Get the glyph with the given glyph index. Look in cache first, then read from file.
        Ensure file is positioned at point where it was when it came here."
        ^glyphCache at: glyphIndex ifAbsentPut:[ | glyph filePos |
                filePos := fontFile position.
                glyph := self readGlyphAt: glyphIndex fromFile: fontFile.
                fontFile position: filePos.
                glyph].!

----- Method: TTFileDescription>>descender (in category 'accessing') -----
descender
        "Descender of the font. Relative to unitsPerEm.
        Easily confused with the typographic descender."
        ^descender!

----- Method: TTFileDescription>>displayAll (in category 'private') -----
displayAll
        "Read all the glyphs and display them"
        | glyph form scale points x y |
        points := 24.
        scale := points asFloat / unitsPerEm.
        x := y := 0.
        Display deferUpdates: true.
        1 to: numGlyphs do:[:i|
                glyph := self readGlyphAt: i-1.
                form := glyph asFormWithScale: scale
                        ascender: ascender
                        descender: descender.
                Display fillWhite: (x@y extent: form extent).
                form displayOn: Display at: x@y rule: 34.
                Display forceToScreen: (x@y extent: form extent).
                x := x + form width.
                x > Display width ifTrue:[y := y + form height. x := 0].
                y > Display height ifTrue:[y := 0].
                Sensor anyButtonPressed ifTrue:[^Display restore].
        ].!

----- Method: TTFileDescription>>familyName (in category 'accessing') -----
familyName
        "The family name for the font"
        ^familyName!

----- Method: TTFileDescription>>fileName (in category 'accessing') -----
fileName
        "The name of the Truetype file"
        ^fileName!

----- Method: TTFileDescription>>findTable:in: (in category 'ttf tables') -----
findTable: tag in: fontFile
        "Position the fontFile at the beginning of the table with the given tag.
        Answer true if we found the table, false otherwise."
        | maxTables chksum offset length table |
        fontFile position: fileOffset.
        fontFile skip: 4. "version"
        maxTables := fontFile nextNumber: 2.
        fontFile skip: 6.
        1 to: maxTables do:[:i|
                table := (fontFile next: 4) asString.
                chksum := fontFile nextNumber: 4.
                offset := fontFile nextNumber: 4.
                length := fontFile nextNumber: 4.
                table = tag ifTrue:[
                        fontFile position: offset.
                        ^true].
        ].
        chksum. length. "fake usage"
        ^false!

----- Method: TTFileDescription>>fontHeight (in category 'accessing') -----
fontHeight
        ^ascender - descender!

----- Method: TTFileDescription>>getGlyphFlagsFrom:size: (in category 'glyphs') -----
getGlyphFlagsFrom: fontFile size: nPts
        "Read in the flags for this glyph.  The outer loop gathers the flags that
        are actually contained in the table.  If the repeat bit is set in a flag
        then the next byte is read from the table; this is the number of times
        to repeat the last flag.  The inner loop does this, incrementing the
        outer loops index each time."
        | flags index repCount flagBits |
        flags := ByteArray new: nPts.
        index := 1.
        [index <= nPts] whileTrue:[
                flagBits := fontFile next.
                flags at: index put: flagBits.
                (flagBits bitAnd: 8) = 8 ifTrue:[
                        repCount := fontFile next.
                        repCount timesRepeat:[
                                index := index + 1.
                                flags at: index put: flagBits]].
                index := index + 1].
        ^flags!

----- Method: TTFileDescription>>glyphAt: (in category 'glyphs') -----
glyphAt: charOrCode
        "Answer the glyph with the given code point"
        | codePoint glyphIndex glyph |
        codePoint := charOrCode asCharacter charCode.
        self withFileDo:[:fontFile|
                glyphIndex := self readCmapTableAt: codePoint fromFile: fontFile.
                glyph := self readGlyphAt: glyphIndex fromFile: fontFile.
                self updateGlyphMetrics: glyph fromFile: fontFile.
        ].
        ^glyph!

----- Method: TTFileDescription>>lineGap (in category 'accessing') -----
lineGap
        "Leading of the font. Relative to unitsPerEm.
        Easily confused with the typographic linegap."
        ^lineGap!

----- Method: TTFileDescription>>name (in category 'accessing') -----
name
        "For compatibility with TTFontDescription"
        ^familyName!

----- Method: TTFileDescription>>numGlyphs (in category 'accessing') -----
numGlyphs
        "The number of glyphs represented in this font"
        ^numGlyphs!

----- Method: TTFileDescription>>on: (in category 'initialize') -----
on: aFileName
        "Initialize the receiver from a file name"
        fileName := aFileName.
        self withFileDo:[:fontFile|
                (self findTable: 'head' in: fontFile)
                        ifFalse:[^self error: 'File does not have a header table'].
                self processFontHeaderTable: fontFile.
                (self findTable: 'maxp' in: fontFile)
                        ifFalse:[^self error: 'File does not have a profile table'].
                self processMaximumProfileTable: fontFile.
                (self findTable: 'name' in: fontFile)
                        ifFalse:[^self error: 'File does not have a naming table'].
                self processNamingTable: fontFile.
                (self findTable: 'hhea' in: fontFile)
                        ifFalse:[^self error: 'File does not have a horizontal header table'].
                self processHorizontalHeaderTable: fontFile.
                (self findTable: 'hmtx' in: fontFile)
                        ifFalse:[^self error: 'File does not have a horizontal header table'].
                hmtxTableOffset := fontFile position.
                (self findTable: 'loca' in: fontFile)
                        ifFalse:[^self error: 'File does not have a naming table'].
                indexToLocOffset := fontFile position.
                (self findTable: 'glyf' in: fontFile)
                        ifFalse:[^self error: 'File does not have a naming table'].
                glyphTableOffset := fontFile position.
                (self findTable: 'cmap' in: fontFile)
                        ifFalse:[^self error: 'File does not have a header table'].
                self processCharacterMappingTable: fontFile.
        ].!

----- Method: TTFileDescription>>on:offset: (in category 'initialize') -----
on: aFileName offset: fontOffset
        "Initialize the receiver from a file name"
        fileName := aFileName.
        fileOffset := fontOffset.
        self withFileDo:[:fontFile|
                "Some bitmap fonts are called .ttf; skip anything that doesn't have a header"
                (self findTable: 'head' in: fontFile) ifFalse:[^nil].
                self processFontHeaderTable: fontFile.
                (self findTable: 'maxp' in: fontFile)
                        ifFalse:[^self error: 'File does not have a profile table'].
                self processMaximumProfileTable: fontFile.
                (self findTable: 'name' in: fontFile)
                        ifFalse:[^self error: 'File does not have a naming table'].
                self processNamingTable: fontFile.
                (self findTable: 'hhea' in: fontFile)
                        ifFalse:[^self error: 'File does not have a horizontal header table'].
                self processHorizontalHeaderTable: fontFile.
                (self findTable: 'OS/2' in: fontFile)
                        ifTrue:[self processOS2Table: fontFile].
                (self findTable: 'hmtx' in: fontFile)
                        ifFalse:[^self error: 'File does not have a horizontal header table'].
                hmtxTableOffset := fontFile position.
                (self findTable: 'loca' in: fontFile)
                        ifFalse:[^self error: 'File does not have a naming table'].
                indexToLocOffset := fontFile position.
                (self findTable: 'glyf' in: fontFile)
                        ifFalse:[^self error: 'File does not have a naming table'].
                glyphTableOffset := fontFile position.
                (self findTable: 'cmap' in: fontFile)
                        ifFalse:[^self error: 'File does not have a header table'].
                self processCharacterMappingTable: fontFile.
        ].!

----- Method: TTFileDescription>>printOn: (in category 'printing') -----
printOn: aStream
        super printOn: aStream.
        aStream nextPutAll: '('; print: fileName; nextPutAll: ')'.!

----- Method: TTFileDescription>>processCharacterMappingTable: (in category 'ttf tables') -----
processCharacterMappingTable: fontFile
        "Read the font's character to glyph index mapping table."
        | initialOffset nSubTables pID sID offset |
        initialOffset := fontFile position.
        fontFile skip: 2. "Skip table version"
        nSubTables := fontFile nextNumber: 2.
        1 to: nSubTables do:[:i|
                pID := fontFile nextNumber: 2.
                sID := fontFile nextNumber: 2.
                offset := fontFile nextNumber: 4.
                "Check if this is either a Unicode (0), Macintosh (1),
                or a Windows (3) encoded table"
                (#(0 1 3) includes: pID) ifTrue:[
                        cmapType := pID.
                        cmapOffset := initialOffset + offset.
                        cmapType = 0 ifTrue:[^self]. "found Unicode table; use it"
                ].
        ].!

----- Method: TTFileDescription>>processCompositeGlyph:contours:from: (in category 'glyphs') -----
processCompositeGlyph: glyph contours: nContours from: fontFile
        "Read a composite glyph from the font data. The glyph passed into this method contains some state variables that must be copied into the resulting composite glyph."
        | flags glyphIndex hasInstr ofsX ofsY iLen a11 a12 a21 a22 m glyphCache |
        glyphCache := Dictionary new.
        a11 := a22 := 16r4000. "1.0 in F2Dot14"
        a21 := a12 := 0. "0.0 in F2Dot14"
        "Copy state"
        hasInstr := false.
        [ flags := fontFile nextNumber: 2.
        glyphIndex := fontFile nextNumber: 2.
        (flags bitAnd: 1) = 1 ifTrue:[
                ofsX := self short: (fontFile nextNumber: 2).
                ofsY := self short: (fontFile nextNumber: 2).
        ] ifFalse:[
                (ofsX := fontFile next) > 127 ifTrue:[ofsX := ofsX - 256].
                (ofsY := fontFile next) > 127 ifTrue:[ofsY := ofsY - 256].
        ].
        ((flags bitAnd: 2) = 2) ifFalse:[
                | i1 i2 p1 p2 |
                (flags bitAnd: 1) = 1 ifTrue: [
                        i1 := ofsX + 65536 \\ 65536.
                        i2 := ofsY + 65536 \\ 65536]
                 ifFalse: [
                        i1 := ofsX + 256 \\ 256.
                        i2 := ofsY + 256 \\ 256].
                p1 := glyph referenceVertexAt: i1+1.
                p2 := (self childGlyphAt: glyphIndex in: glyphCache fromFile: fontFile) referenceVertexAt: i2+1.
                ofsX := p1 x - p2 x.
                ofsY := p1 y - p2 y.
        ].
        (flags bitAnd: 8) = 8 ifTrue:[
                a11 := a22 := self short: (fontFile nextNumber: 2)].
        (flags bitAnd: 64) = 64 ifTrue:[
                a11 := self short: (fontFile nextNumber: 2).
                a22 := self short: (fontFile nextNumber: 2).
        ].
        (flags bitAnd: 128) = 128 ifTrue:[
                "2x2 transformation"
                a11 := self short: (fontFile nextNumber: 2).
                a21 := self short: (fontFile nextNumber: 2).
                a12 := self short: (fontFile nextNumber: 2).
                a22 := self short: (fontFile nextNumber: 2).
        ].
        m := MatrixTransform2x3 new.
        "Convert entries from F2Dot14 to float"
        m a11: (a11 asFloat / 16r4000).
        m a12: (a12 asFloat / 16r4000).
        m a21: (a21 asFloat / 16r4000).
        m a22: (a22 asFloat / 16r4000).
        m a13: ofsX.
        m a23: ofsY.
        glyph addGlyph: (self childGlyphAt: glyphIndex in: glyphCache fromFile: fontFile) transformation: m.
        hasInstr := hasInstr or:[ (flags bitAnd: 256) = 256].
        "Continue as long as the MORE:=COMPONENTS bit is set"
        (flags bitAnd: 32) = 32] whileTrue.
        hasInstr ifTrue:[
                iLen := fontFile nextNumber: 2.
                fontFile skip: iLen].!

----- Method: TTFileDescription>>processFontHeaderTable: (in category 'ttf tables') -----
processFontHeaderTable: fontFile
"Value Data Type    Description
unitsPerEm USHORT      Granularity of the font's em square.
xMax USHORT      Maximum X-coordinate for the entire font.
xMin USHORT      Minimum X-coordinate for the entire font.
yMax USHORT      Maximum Y-coordinate for the entire font.
yMin USHORT      Minimum Y-coordinate for the entire font.
indexToLocFormat SHORT       Used when processing the Index To Loc Table."

        fontFile skip: 4. "Skip table version number"
        fontFile skip: 4. "Skip font revision number"
        fontFile skip: 4. "Skip check sum adjustment"
        fontFile skip: 4. "Skip magic number"
        fontFile skip: 2. "Skip flags"

        unitsPerEm := fontFile nextNumber: 2.

        fontFile skip: 8. "Skip creation date"
        fontFile skip: 8. "Skip modification date"

        "Skip min/max values of all glyphs"
        fontFile skip: 2.
        fontFile skip: 2.
        fontFile skip: 2.
        fontFile skip: 2.

        fontFile skip: 2. "Skip mac style"
        fontFile skip: 2. "Skip lowest rec PPEM"
        fontFile skip: 2. "Skip font direction hint"

        indexToLocFormat := fontFile nextNumber: 2.
!

----- Method: TTFileDescription>>processHorizontalHeaderTable: (in category 'ttf tables') -----
processHorizontalHeaderTable: fontFile
"
ascender           SHORT          Typographic ascent.
descender          SHORT          Typographic descent.
lineGap            SHORT          Typographic lineGap.
numberOfHMetrics   USHORT         Number hMetric entries in the HTMX
                                               Table; may be smaller than the total
                                             number of glyphs.
"
        fontFile skip: 4. "Skip table version"
        ascender := self short: (fontFile nextNumber: 2).
        descender := self short: (fontFile nextNumber: 2).
        lineGap := self short: (fontFile nextNumber: 2).
        fontFile skip: 2. "Skip advanceWidthMax"
        fontFile skip: 2. "Skip minLeftSideBearing"
        fontFile skip: 2. "Skip minRightSideBearing"
        fontFile skip: 2. "Skip xMaxExtent"
        fontFile skip: 2. "Skip caretSlopeRise"
        fontFile skip: 2. "Skip caretSlopeRun"
        fontFile skip: 10. "Skip 5 reserved shorts"
        fontFile skip: 2. "Skip metricDataFormat"

        numHMetrics := fontFile nextNumber: 2.

        ^numHMetrics!

----- Method: TTFileDescription>>processMaximumProfileTable: (in category 'ttf tables') -----
processMaximumProfileTable: fontFile
"
numGlyphs         USHORT      The number of glyphs in the font.
"
        fontFile skip: 4. "Skip Table version number"
        numGlyphs := fontFile nextNumber: 2.!

----- Method: TTFileDescription>>processNamingTable: (in category 'ttf tables') -----
processNamingTable: fontFile
"copyright         CHARPTR     The font's copyright notice.
familyName        CHARPTR     The font's family name.
subfamilyName     CHARPTR     The font's subfamily name.
uniqueName        CHARPTR     A unique identifier for this font.
fullName          CHARPTR     The font's full name (a combination of
                                          familyName and subfamilyName).
versionName       CHARPTR     The font's version string.
"
        | nRecords initialOffset storageOffset pID sID nID length offset string |
        initialOffset := fontFile position.
        fontFile skip: 2. "Skip format selector"
        "Get the number of name records"
        nRecords := fontFile nextNumber: 2.
        "Offset from the beginning of this table"
        storageOffset := (fontFile nextNumber: 2) + initialOffset.
        1 to: nRecords do:[:i|
                fontFile position: initialOffset + 6 + ((i-1) * 12).
                pID := fontFile nextNumber: 2.
                sID := fontFile nextNumber: 2.
                "lID := "fontFile nextNumber: 2.
                nID := fontFile nextNumber: 2.
                length := fontFile nextNumber: 2.
                offset := fontFile nextNumber: 2.
                "Read only Macintosh or Microsoft strings"
                (pID = 1 or:[pID = 3 and:[sID = 1]]) ifTrue:[
                        "MS uses Unicode all others single byte"
                        "multiBytes := pID = 3."
                        fontFile position: storageOffset+offset.
                        string := (fontFile next: length) asString.
                        pID = 3 ifTrue:[ | keep |
                                keep := true.
                                string := string select:[:ch| keep := keep not].
                        ].
                        nID caseOf: {
                                "[0] -> [copyright := string]."
                                [1] -> [(pID = 1 or:[familyName == nil]) ifTrue:[familyName := string]].
                                [2] -> [(pID = 1 or:[subfamilyName == nil]) ifTrue:[subfamilyName := string]].
                                "[3] -> [(pID = 1 or:[uniqueName == nil]) ifTrue:[uniqueName := string]]."
                                "[4] -> [(pID = 1 or:[fullName == nil]) ifTrue:[fullName := string]]."
                                "[5] -> [(pID = 1 or:[versionName == nil]) ifTrue:[versionName := string]]."
                                "[6] -> [(pID = 1 or:[postscriptName == nil]) ifTrue:[postscriptName := string]]."
                                "[7] -> [(pID = 1 or:[trademark == nil]) ifTrue:[trademark := string]]."
                        } otherwise:["ignore"].
                ].
        ].
!

----- Method: TTFileDescription>>processOS2Table: (in category 'ttf tables') -----
processOS2Table: fontFile
"
        USHORT   version   0x0004
        SHORT xAvgCharWidth
        USHORT usWeightClass
        USHORT usWidthClass
        USHORT fsType
        SHORT ySubscriptXSize
        SHORT ySubscriptYSize
        SHORT ySubscriptXOffset
        SHORT ySubscriptYOffset
        SHORT ySuperscriptXSize
        SHORT ySuperscriptYSize
        SHORT ySuperscriptXOffset
        SHORT ySuperscriptYOffset
        SHORT yStrikeoutSize
        SHORT yStrikeoutPosition
        SHORT sFamilyClass
        BYTE panose[10]
        ULONG ulUnicodeRange1 Bits 0-31
        ULONG ulUnicodeRange2 Bits 32-63
        ULONG ulUnicodeRange3 Bits 64-95
        ULONG ulUnicodeRange4 Bits 96-127
        CHAR achVendID[4]
        USHORT fsSelection
        USHORT usFirstCharIndex
        USHORT usLastCharIndex
        SHORT sTypoAscender
        SHORT sTypoDescender
        SHORT sTypoLineGap
        USHORT usWinAscent
        USHORT usWinDescent
        ULONG ulCodePageRange1 Bits 0-31
        ULONG ulCodePageRange2 Bits 32-63
        SHORT sxHeight
        SHORT sCapHeight
        USHORT usDefaultChar
        USHORT usBreakChar
        USHORT usMaxContext "
        | version fsSelection minAscii maxAscii |
        version := self short: (fontFile nextNumber: 2). "table version"
        version = 0 ifTrue:[^self].
        fontFile skip: 60.
        fsSelection := fontFile nextNumber: 2.
        minAscii := fontFile nextNumber: 2.
        maxAscii := fontFile nextNumber: 2.
        sTypoAscender := self short: (fontFile nextNumber: 2).
        sTypoDescender := self short: (fontFile nextNumber: 2).
        sTypoLineGap := self short: (fontFile nextNumber: 2).
!

----- Method: TTFileDescription>>processSimpleGlyph:contours:from: (in category 'glyphs') -----
processSimpleGlyph: glyph contours: nContours from: fontFile
        "Construct a simple glyph frm the font file"
        | endPts  nPts iLength flags |
        endPts := Array new: nContours.
        1 to: nContours do:[:i| endPts at: i put: (fontFile nextNumber: 2)].
        glyph initializeContours: nContours with: endPts.
        nContours = 0 ifTrue:[^self].
        nPts := endPts last + 1.
        iLength := fontFile nextNumber: 2. "instruction length"
        fontFile skip: iLength.
        flags := self getGlyphFlagsFrom: fontFile size: nPts.
        self readGlyphXCoords: fontFile glyph: glyph nContours: nContours flags: flags endPoints: endPts.
        self readGlyphYCoords: fontFile glyph: glyph nContours: nContours flags: flags endPoints: endPts.!

----- Method: TTFileDescription>>profileAll (in category 'private') -----
profileAll
        "Profile reading all the glyphs"
        MessageTally spyOn:[
                1 to: numGlyphs do:[:glyphIndex| self readGlyphAt: glyphIndex-1].
        ].!

----- Method: TTFileDescription>>readCmapTableAt:fromFile: (in category 'glyphs') -----
readCmapTableAt: codePoint fromFile: fontFile
        | cmapFmt length firstCode entryCount segCount segIndex  startCode endCode idDelta idRangeOffset offset |
        fontFile position: cmapOffset.
        cmapFmt := fontFile nextNumber: 2.
        length := fontFile nextNumber: 2.
        fontFile skip: 2. "skip version"

        cmapFmt = 0 ifTrue:["byte encoded table"
                codePoint > 255 ifTrue:[^0].
                length := length - 6. "should be always 256"
                length <= 0 ifTrue: [^0]. "but sometimes, this table is empty"
                fontFile skip: codePoint. "move to correct byte offset in table"
                ^fontFile next].

        cmapFmt = 4 ifTrue:[ "segment mapping to deltavalues"
                codePoint > 16rFFFF ifTrue:[^0].
                segCount := (fontFile nextNumber: 2) // 2.
                fontFile skip: 6. "skip searchRange, entrySelector, rangeShift"
                segIndex := (0 to: segCount-1)
                        detect:[:i| (endCode := (fontFile nextNumber: 2)) >= codePoint].
                fontFile position: cmapOffset + 16 + (segCount*2) + (segIndex*2).
                startCode := fontFile nextNumber: 2.
                startCode <= codePoint ifFalse:[^0]. "not in segment range"
                fontFile position: cmapOffset + 16 + (segCount*4) + (segIndex*2).
                idDelta := fontFile nextNumber: 2.
                fontFile position: cmapOffset + 16 + (segCount*6) + (segIndex*2).
                idRangeOffset := fontFile nextNumber: 2.
                idRangeOffset = 0 ifTrue:[^(idDelta + codePoint) bitAnd: 16rFFFF].
                offset := (fontFile position - 2) + idRangeOffset + ((codePoint - startCode) * 2).
                fontFile position: offset.
                ^fontFile nextNumber: 2.
        ].

        cmapFmt = 6 ifTrue:[ "trimmed table"
                firstCode := fontFile nextNumber: 2.
                entryCount := fontFile nextNumber: 2.
                (codePoint between: firstCode and: firstCode+entryCount) ifFalse:[^0].
                fontFile skip: (codePoint-firstCode) * 2.
                ^fontFile nextNumber: 2].

        ^0!

----- Method: TTFileDescription>>readGlyphAt: (in category 'glyphs') -----
readGlyphAt: glyphIndex
        | glyph |
        self withFileDo:[:fontFile|
                glyph := self readGlyphAt: glyphIndex fromFile: fontFile.
                self updateGlyphMetrics: glyph fromFile: fontFile.
        ].
        ^glyph!

----- Method: TTFileDescription>>readGlyphAt:fromFile: (in category 'glyphs') -----
readGlyphAt: glyphIndex fromFile: fontFile
        "Answer the glyph with the given glyph index"
        | glyphOffset nextOffset glyphLength glyph nContours left top right bottom |
        indexToLocFormat = 0 ifTrue:["Format0: offset/2 is stored"
                fontFile position: indexToLocOffset+(glyphIndex * 2).
                glyphOffset := (fontFile nextNumber: 2) * 2.
                nextOffset := (fontFile nextNumber: 2) * 2.
        ] ifFalse:["Format1: store actual offset"
                fontFile position: indexToLocOffset+(glyphIndex * 4).
                glyphOffset := fontFile nextNumber: 4.
                nextOffset := fontFile nextNumber: 4.
        ].
        glyphLength := nextOffset - glyphOffset.
        glyphLength = 0 ifTrue:[^TTGlyph new glyphIndex: glyphIndex].

        fontFile position: glyphTableOffset+glyphOffset.
        nContours := self short: (fontFile nextNumber: 2).
        left := self short: (fontFile nextNumber: 2).
        top := self short: (fontFile nextNumber: 2).
        right := self short: (fontFile nextNumber: 2).
        bottom := self short: (fontFile nextNumber: 2).
        nContours >= 0 ifTrue:[
                glyph := TTGlyph new glyphIndex: glyphIndex.
                self processSimpleGlyph: glyph contours: nContours from: fontFile.
        ] ifFalse:[
                glyph := TTCompositeGlyph new glyphIndex: glyphIndex.
                self processCompositeGlyph: glyph contours: nContours from: fontFile.
        ].
        glyph buildAllContours.
        glyph bounds: (left@top corner: right@bottom).
        ^glyph
!

----- Method: TTFileDescription>>readGlyphXCoords:glyph:nContours:flags:endPoints: (in category 'glyphs') -----
readGlyphXCoords: fontFile glyph: glyph nContours: nContours flags: flags endPoints: endPts
        "Read the x coordinates for the given glyph from the font file."
        | startPoint endPoint flagBits xValue contour ttPoint |
        startPoint := 1.
        1 to: nContours do:[:i|
                contour := glyph contours at: i.
                "Get the end point"
                endPoint := (endPts at: i) + 1.
                "Store number of points"
                startPoint to: endPoint do:[:j|
                        ttPoint := contour points at: (j - startPoint + 1).
                        flagBits := flags at: j.
                        "If bit zero in the flag is set then this point is an on-curve
                        point, if not, then it is an off-curve point."
                        (flagBits bitAnd: 1) = 1
                                ifTrue:[ ttPoint type: #OnCurve]
                                ifFalse:[ttPoint type: #OffCurve].
                        "First we check to see if bit one is set.  This would indicate that
                        the corresponding coordinate data in the table is 1 byte long.
                        If the bit is not set, then the coordinate data is 2 bytes long."
                        (flagBits bitAnd: 2) = 2 ifTrue:[ "one byte"
                                xValue := fontFile next.
                                xValue := (flagBits bitAnd: 16)=16 ifTrue:[xValue] ifFalse:[xValue negated].
                                ttPoint x: xValue.
                        ] ifFalse:[ "two byte"
                                "If bit four is set, then this coordinate is the same as the
                                last one, so the relative offset (of zero) is stored.  If bit
                                is not set, then read in two bytes and store it as a signed value."
                                (flagBits bitAnd: 16) = 16 ifTrue:[ ttPoint x: 0 ]
                                ifFalse:[
                                        xValue := self short: (fontFile nextNumber: 2).
                                        ttPoint x: xValue]]].
                startPoint := endPoint + 1]!

----- Method: TTFileDescription>>readGlyphYCoords:glyph:nContours:flags:endPoints: (in category 'glyphs') -----
readGlyphYCoords: fontFile glyph: glyph nContours: nContours flags: flags endPoints: endPts
        "Read the y coordinates for the given glyph from the font file."
        | startPoint endPoint flagBits yValue contour ttPoint |
        startPoint := 1.
        1 to: nContours do:[:i|
                contour := glyph contours at: i.
                "Get the end point"
                endPoint := (endPts at: i) + 1.
                "Store number of points"
                startPoint to: endPoint do:[:j|
                        ttPoint := contour points at: (j - startPoint + 1).
                        flagBits := flags at: j.
                        "Check if this value one or two byte encoded"
                        (flagBits bitAnd: 4) = 4 ifTrue:[ "one byte"
                                yValue := fontFile next.
                                yValue := (flagBits bitAnd: 32)=32 ifTrue:[yValue] ifFalse:[yValue negated].
                                ttPoint y: yValue.
                        ] ifFalse:[ "two byte"
                                (flagBits bitAnd: 32) = 32 ifTrue:[ ttPoint y: 0 ]
                                ifFalse:[
                                        yValue := self short: (fontFile nextNumber: 2).
                                        ttPoint y: yValue]]].
                startPoint := endPoint + 1]!

----- Method: TTFileDescription>>renderGlyph:height:fgColor:bgColor:depth: (in category 'rendering') -----
renderGlyph: code height: height fgColor: fgColor bgColor: bgColor depth: depth
        "Render the glyph with the given code point at the specified pixel height."
        ^(self at: code)
                asFormWithScale: height asFloat / (ascender - descender)
                        ascender: ascender
                        descender: descender
                        fgColor: fgColor bgColor: bgColor depth: depth!

----- Method: TTFileDescription>>short: (in category 'private') -----
short: aNumber
        (aNumber bitAnd: 16r8000) = 0
                ifTrue: [^aNumber]
                ifFalse: [^-1 - (aNumber bitXor: 16rFFFF)]!

----- Method: TTFileDescription>>size (in category 'accessing') -----
size
        "Compatibility with TTFontDescription"
        ^16rFFFF!

----- Method: TTFileDescription>>subfamilyName (in category 'accessing') -----
subfamilyName
        "The subfamily name for the font"
        ^subfamilyName!

----- Method: TTFileDescription>>typographicAscender (in category 'accessing') -----
typographicAscender
        "Microsoft defines this as the 'true typographic metrics' of the font."
        ^sTypoAscender ifNil:[ascender]!

----- Method: TTFileDescription>>typographicDescender (in category 'accessing') -----
typographicDescender
        "Microsoft defines this as the 'true typographic metrics' of the font."
        ^sTypoDescender ifNil:[descender]!

----- Method: TTFileDescription>>typographicLineGap (in category 'accessing') -----
typographicLineGap
        "Microsoft defines this as the 'true typographic metrics' of the font."
        ^sTypoLineGap ifNil:[lineGap]!

----- Method: TTFileDescription>>updateGlyphMetrics:fromFile: (in category 'glyphs') -----
updateGlyphMetrics: glyph fromFile: fontFile
        "Update the horizontal metrics for the given glyph"
        | glyphIndex |
        glyphIndex := glyph glyphIndex.
        glyphIndex <= numHMetrics ifTrue:[
                fontFile position: hmtxTableOffset + (glyphIndex*4).
                glyph advanceWidth: (fontFile nextNumber: 2).
                glyph leftSideBearing: (self short: (fontFile nextNumber: 2)).
        ] ifFalse:[
                fontFile position: hmtxTableOffset + ((numHMetrics-1) *4).
                glyph advanceWidth: (fontFile nextNumber: 2).
                fontFile position: hmtxTableOffset + (numHMetrics * 4) + ((glyphIndex-numHMetrics)*2).
                glyph leftSideBearing: (self short: (fontFile nextNumber: 2)).
        ].
        glyph updateRightSideBearing.!

----- Method: TTFileDescription>>withFileDo: (in category 'initialize') -----
withFileDo: aBlock
        "Open the font file for the duration of aBlock"
        ^self class openFontFile: fileName do: aBlock.!

Object subclass: #TTFontDescription
        instanceVariableNames: 'glyphTable glyphs kernPairs copyright familyName fullName subfamilyName uniqueName versionName postscriptName trademark bounds unitsPerEm ascender descender lineGap sTypoAscender sTypoDescender sTypoLineGap'
        classVariableNames: 'Default Descriptions'
        poolDictionaries: ''
        category: 'TrueType-Fonts'!

!TTFontDescription commentStamp: '<historical>' prior: 0!
Holds a TrueType font in memory.  Is used by TTSampleStringMorph as its font.  

Class owns a default example.  !

----- Method: TTFontDescription class>>addFromTTFile: (in category 'instance creations') -----
addFromTTFile: fileName
"
        self addFromTTFile: 'C:\WINDOWS\Fonts\ARIALN.TTF'
"
        ^self addFromTTStream: (FileStream readOnlyFileNamed: fileName).
!

----- Method: TTFontDescription class>>addFromTTStream: (in category 'instance creations') -----
addFromTTStream: readStream
"
        self addFromTTFile: 'C:\WINDOWS\Fonts\ARIALN.TTF'
"

        | tt old |
        tt := TTFontReader readFrom: readStream.
        old := Descriptions detect: [:f | f name = tt name and: [f subfamilyName = tt subfamilyName]] ifNone: [nil].
        old ifNotNil: [Descriptions remove: old].
        Descriptions add: tt.
        ^ tt.
!

----- Method: TTFontDescription class>>clearDefault (in category 'instance creations') -----
clearDefault
"
        self clearDefault
"

        Default := nil.
!

----- Method: TTFontDescription class>>clearDescriptions (in category 'instance creations') -----
clearDescriptions
"
        self clearDescriptions
"

        Descriptions := Set new.
        Default ifNotNil: [Descriptions add: Default].
!

----- Method: TTFontDescription class>>default (in category 'instance creations') -----
default
        ^ Default!

----- Method: TTFontDescription class>>descriptionFullNamed: (in category 'instance creations') -----
descriptionFullNamed: descriptionFullName
        ^ Descriptions
                detect: [:f | f fullName = descriptionFullName]
                ifNone: [Default]!

----- Method: TTFontDescription class>>descriptionNamed: (in category 'instance creations') -----
descriptionNamed: descriptionName

        ^ Descriptions detect: [:f | f name = descriptionName] ifNone: [Default].
!

----- Method: TTFontDescription class>>initialize (in category 'instance creations') -----
initialize
"
        self initialize
"

        self clearDescriptions.
!

----- Method: TTFontDescription class>>removeDescriptionNamed: (in category 'instance creations') -----
removeDescriptionNamed: descriptionName

        | tt |
        Descriptions ifNil: [^ self].
        [(tt :=  Descriptions detect: [:f | f name = descriptionName] ifNone: [nil]) notNil] whileTrue:[
                 Descriptions remove: tt
        ].
!

----- Method: TTFontDescription class>>removeDescriptionNamed:subfamilyName: (in category 'instance creations') -----
removeDescriptionNamed: descriptionName subfamilyName: subfamilyName

        | tts |
        Descriptions ifNil: [^ self].
        tts := Descriptions select: [:f | f name = descriptionName and: [f subfamilyName = subfamilyName]].
        tts do: [:f | Descriptions remove: f].
!

----- Method: TTFontDescription class>>setDefault (in category 'instance creations') -----
setDefault
"
        self setDefault
"

        Default := TTFontReader readFrom: (FileStream readOnlyFileNamed: 'C:\WINDOWS\Fonts\comic.ttf').
!

----- Method: TTFontDescription>>asStrikeFontScale: (in category 'converting') -----
asStrikeFontScale: scale
        "Generate a StrikeFont (actually a FormSetFont) for this TTF font at a given scale."

        | forms |
        forms := (0 to: 255) collect:
                [:i |
                (self at: i)
                        asFormWithScale: scale
                        ascender: ascender
                        descender: descender].
        ^ FormSetFont new
                fromFormArray: forms
                asciiStart: 0
                ascent: (ascender * scale) rounded!

----- Method: TTFontDescription>>ascender (in category 'properties') -----
ascender
        "Ascender of the font. Relative to unitsPerEm.
        Easily confused with the typographic ascender."
        ^ascender!

----- Method: TTFontDescription>>at: (in category 'accessing') -----
at: aCharOrInteger
        ^glyphTable at: aCharOrInteger asInteger+1!

----- Method: TTFontDescription>>at:put: (in category 'accessing') -----
at: index put: value
        ^self shouldNotImplement!

----- Method: TTFontDescription>>blankGlyphForSeparators (in category 'migration') -----
blankGlyphForSeparators

        | space |
        space := (self at: Character space charCode) copy.
        space contours: #().
        Character separators do: [:s |
                glyphTable at: s charCode +1 put: space.
        ].
!

----- Method: TTFontDescription>>bounds (in category 'properties') -----
bounds
        ^bounds!

----- Method: TTFontDescription>>copyright (in category 'information') -----
copyright
        ^copyright!

----- Method: TTFontDescription>>deepCopy (in category 'copying') -----
deepCopy

        "Since it shouldn't be copied for transmitting or any reason, it returns self."
        ^ self.
!

----- Method: TTFontDescription>>descender (in category 'properties') -----
descender
        "Descender of the font. Relative to unitsPerEm.
        Easily confused with the typographic descender."
        ^descender!

----- Method: TTFontDescription>>familyName (in category 'information') -----
familyName
        ^familyName!

----- Method: TTFontDescription>>flipAroundY (in category 'private-initialization') -----
flipAroundY
        bounds := (bounds origin x @ bounds corner y negated) corner:
                                (bounds corner x @ bounds origin y negated).
        glyphs do:[:glyph| glyph flipAroundY]!

----- Method: TTFontDescription>>fontHeight (in category 'accessing') -----
fontHeight
        ^ascender - descender!

----- Method: TTFontDescription>>fullName (in category 'information') -----
fullName
        ^fullName!

----- Method: TTFontDescription>>lineGap (in category 'properties') -----
lineGap
        "Leading of the font. Relative to unitsPerEm.
        Easily confused with the typographic linegap."
        ^lineGap!

----- Method: TTFontDescription>>name (in category 'accessing') -----
name

        ^ self familyName copyWithout: Character space.
!

----- Method: TTFontDescription>>objectForDataStream: (in category 'copying') -----
objectForDataStream: refStrm
        | dp |
        "I am about to be written on an object file.  Write a reference to a known Font in the other system instead.  "

        "A path to me"
        (TextConstants at: #forceFontWriting ifAbsent: [false]) ifTrue: [^ self].
                "special case for saving the default fonts on the disk.  See collectionFromFileNamed:"

        dp := DiskProxy global: #TTFontDescription selector: #descriptionFullNamed:
                        args: {self fullName}.
        refStrm replace: self with: dp.
        ^ dp.
!

----- Method: TTFontDescription>>postscriptName (in category 'information') -----
postscriptName
        ^postscriptName!

----- Method: TTFontDescription>>printOn: (in category 'printing') -----
printOn: aStream
        super printOn: aStream.
        aStream nextPut: $(.
        familyName printOn: aStream.
        aStream nextPut:$).!

----- Method: TTFontDescription>>renderGlyph:height:fgColor:bgColor:depth: (in category 'rendering') -----
renderGlyph: code height: fontHeight fgColor: fgColor bgColor: bgColor depth: depth
        "Render the glyph with the given code point at the specified pixel height."
        ^(self at: code)
                asFormWithScale: fontHeight asFloat / (ascender - descender)
                        ascender: ascender
                        descender: descender
                        fgColor: fgColor bgColor: bgColor depth: depth!

----- Method: TTFontDescription>>setAscender:descender:lineGap: (in category 'private-initialization') -----
setAscender: asc descender: desc lineGap: lgap
        ascender := asc.
        descender := desc.
        lineGap := lgap!

----- Method: TTFontDescription>>setBounds:unitsPerEm: (in category 'private-initialization') -----
setBounds: aRect unitsPerEm: aNumber
        bounds := aRect.
        unitsPerEm := aNumber.!

----- Method: TTFontDescription>>setGlyphs:mapping: (in category 'private-initialization') -----
setGlyphs: glyphArray mapping: mappingTable
        glyphs := glyphArray.
        glyphTable := mappingTable.!

----- Method: TTFontDescription>>setKernPairs: (in category 'private-initialization') -----
setKernPairs: array
        kernPairs := array!

----- Method: TTFontDescription>>setStrings: (in category 'private-initialization') -----
setStrings: anArray
        copyright := anArray at: 1.
        familyName := anArray at: 2.
        subfamilyName := anArray at: 3.
        uniqueName := anArray at: 4.
        fullName := anArray at: 5.
        versionName := anArray at: 6.
        postscriptName := anArray at: 7.
        trademark := anArray at: 8.
!

----- Method: TTFontDescription>>setTypographicAscender:descender:lineGap: (in category 'private-initialization') -----
setTypographicAscender: asc descender: desc lineGap: lGap
        sTypoAscender := asc.
        sTypoDescender := desc.
        sTypoLineGap := lGap.
!

----- Method: TTFontDescription>>size (in category 'accessing') -----
size
        "Answer the logical number of characters in this font"
        ^glyphTable size - 1
!

----- Method: TTFontDescription>>subfamilyName (in category 'information') -----
subfamilyName
        ^subfamilyName!

----- Method: TTFontDescription>>trademark (in category 'information') -----
trademark
        ^trademark!

----- Method: TTFontDescription>>typographicAscender (in category 'accessing') -----
typographicAscender
        "Microsoft defines this as the 'true typographic metrics' of the font."
        ^sTypoAscender ifNil:[ascender]!

----- Method: TTFontDescription>>typographicDescender (in category 'accessing') -----
typographicDescender
        "Microsoft defines this as the 'true typographic metrics' of the font."
        ^sTypoDescender ifNil:[descender]!

----- Method: TTFontDescription>>typographicLineGap (in category 'accessing') -----
typographicLineGap
        "Microsoft defines this as the 'true typographic metrics' of the font."
        ^sTypoLineGap ifNil:[lineGap]!

----- Method: TTFontDescription>>uniqueName (in category 'information') -----
uniqueName
        ^uniqueName!

----- Method: TTFontDescription>>unitsPerEm (in category 'properties') -----
unitsPerEm
        ^unitsPerEm!

----- Method: TTFontDescription>>versionName (in category 'information') -----
versionName
        ^versionName!

----- Method: TTFontDescription>>veryDeepCopyWith: (in category 'copying') -----
veryDeepCopyWith: deepCopier
        "Return self.  I am shared.  Do not record me."
!

Object subclass: #TTFontReader
        instanceVariableNames: 'charMap glyphs nGlyphs kernPairs infoBar fontDescription'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'TrueType-Support'!

!TTFontReader commentStamp: '<historical>' prior: 0!
TTFontReader constructs a TTFontDescription from a TrueType font (.ttf).!

----- Method: TTFontReader class>>fileReaderServicesForFile:suffix: (in category 'class initialization') -----
fileReaderServicesForFile: fullName suffix: suffix


        ^(suffix = 'fnt')  | (suffix = '*')
                ifTrue: [ self services]
                ifFalse: [#()]
!

----- Method: TTFontReader class>>initialize (in category 'class initialization') -----
initialize
        "self initialize"

        FileList registerFileReader: self!

----- Method: TTFontReader class>>installTTF:asTextStyle:sizes: (in category 'instance creation') -----
installTTF: ttfFileName asTextStyle: textStyleName sizes: sizeArray
        "Sizes are in pixels."
        "TTFontReader
                installTTF: 'F:\fonts\amazon:=:=.TTF'
                asTextStyle: #Amazon
                sizes: #(24 60)"

        | ttf fontArray |
        ttf := self parseFileNamed: ttfFileName.
        fontArray := sizeArray collect:
                [:each |
                (ttf asStrikeFontScale: each / ttf unitsPerEm)
                        name: textStyleName;
                        pointSize: each].
        TextConstants at: textStyleName asSymbol put: (TextStyle fontArray: fontArray)!

----- Method: TTFontReader class>>openTTFFile: (in category 'class initialization') -----
openTTFFile: fullName

        (TTFontReader parseFileNamed: fullName) asMorph open!

----- Method: TTFontReader class>>parseFileNamed: (in category 'instance creation') -----
parseFileNamed: aString
        "TTFontReader parseFileNamed:'c:\windows\fonts\arial.ttf'"
        "TTFontReader parseFileNamed:'c:\windows\times.ttf'"
        | contents |
        contents := (FileStream readOnlyFileNamed: aString) binary contentsOfEntireFile.
        ^self readFrom: (ReadStream on: contents)!

----- Method: TTFontReader class>>readFrom: (in category 'instance creation') -----
readFrom: aStream

        ^self new readFrom: aStream!

----- Method: TTFontReader class>>readTTFFrom: (in category 'instance creation') -----
readTTFFrom: aStream

        ^self new readTTFFrom: aStream!

----- Method: TTFontReader class>>serviceOpenTrueTypeFont (in category 'class initialization') -----
serviceOpenTrueTypeFont

        ^ SimpleServiceEntry
                                provider: self
                                label: 'open true type font'
                                selector: #openTTFFile:
                                description: 'open true type font'!

----- Method: TTFontReader class>>services (in category 'class initialization') -----
services

        ^ Array with: self serviceOpenTrueTypeFont
!

----- Method: TTFontReader class>>unload (in category 'class initialization') -----
unload

        FileList unregisterFileReader: self !

----- Method: TTFontReader>>decodeCmapFmtTable: (in category 'private') -----
decodeCmapFmtTable: entry
        | cmapFmt length cmap firstCode entryCount segCount segments offset code |
        cmapFmt := entry nextUShort.
        length := entry nextUShort.
        entry skip: 2. "skip version"

        cmapFmt = 0 ifTrue: "byte encoded table"
                [length := length - 6. "should be always 256"
                length <= 0 ifTrue: [^ nil]. "but sometimes, this table is empty"
                cmap := Array new: length.
                entry nextBytes: length into: cmap startingAt: entry offset.
                ^ cmap].

        cmapFmt = 4 ifTrue: "segment mapping to deltavalues"
                [segCount := entry nextUShort // 2.
                entry skip: 6. "skip searchRange, entrySelector, rangeShift"
                segments := Array new: segCount.
                segments := (1 to: segCount) collect: [:e | Array new: 4].
                1 to: segCount do: [:i | (segments at: i) at: 2 put: entry nextUShort]. "endCount"
                entry skip: 2. "skip reservedPad"
                1 to: segCount do: [:i | (segments at: i) at: 1 put: entry nextUShort]. "startCount"
                1 to: segCount do: [:i | (segments at: i) at: 3 put: entry nextShort]. "idDelta"
                offset := entry offset.
                1 to: segCount do: [:i | (segments at: i) at: 4 put: entry nextUShort]. "idRangeOffset"
                entryCount := segments inject: 0 into: [:max :seg | max max: seg second].
                cmap := Array new: entryCount+1 withAll: 0..
                segments withIndexDo:
                        [:seg :si |
                        seg first to: seg second do:
                                [:i |
                                seg last > 0 ifTrue:
                                        ["offset to glypthIdArray - this is really C-magic!!"
                                        entry offset: i - seg first - 1 * 2 + seg last + si + si + offset.
                                        code := entry nextUShort.
                                        code > 0 ifTrue: [code := code + seg third]]
                                ifFalse:
                                        ["simple offset"
                                        code := i + seg third].
                                cmap at: i + 1 put: code]].
                ^ cmap].

        cmapFmt = 6 ifTrue: "trimmed table"
                [firstCode := entry nextUShort.
                entryCount := entry nextUShort.
                cmap := Array new: entryCount + firstCode withAll: 0.
                entryCount timesRepeat:
                        [cmap at: (firstCode := firstCode + 1) put: entry nextUShort].
                ^ cmap].
        ^ nil!

----- Method: TTFontReader>>getGlyphFlagsFrom:size: (in category 'private') -----
getGlyphFlagsFrom: entry size: nPts
        "Read in the flags for this glyph.  The outer loop gathers the flags that
        are actually contained in the table.  If the repeat bit is set in a flag
        then the next byte is read from the table; this is the number of times
        to repeat the last flag.  The inner loop does this, incrementing the
        outer loops index each time."
        | flags index repCount flagBits |
        flags := ByteArray new: nPts.
        index := 1.
        [index <= nPts] whileTrue:[
                flagBits := entry nextByte.
                flags at: index put: flagBits.
                (flagBits bitAnd: 8) = 8 ifTrue:[
                        repCount := entry nextByte.
                        repCount timesRepeat:[
                                index := index + 1.
                                flags at: index put: flagBits]].
                index := index + 1].
        ^flags!

----- Method: TTFontReader>>getTableDirEntry:from: (in category 'private') -----
getTableDirEntry: tagString from: fontData
        "Find the table named tagString in fontData and return a table directory entry for it."
        | nTables pos currentTag tag |
        nTables := fontData shortAt: 5 bigEndian: true.
        tag := ByteArray new: 4.
        1 to: 4 do:[:i| tag byteAt: i put: (tagString at: i) asInteger].
        tag := tag longAt: 1 bigEndian: true.
        pos := 13.
        1 to: nTables do:[:i|
                currentTag := fontData longAt: pos bigEndian: true.
                currentTag = tag ifTrue:[^TTFontTableDirEntry on: fontData at: pos].
                pos := pos+16].
        ^nil!

----- Method: TTFontReader>>macToWin: (in category 'private') -----
macToWin: index
        ^ (index - 1) asCharacter macToSqueak asciiValue + 1!

----- Method: TTFontReader>>processCharMap: (in category 'processing') -----
processCharMap: assoc
        "Process the given character map"

        | charTable glyph cmap |
        cmap := assoc value.

        assoc key = 0 ifTrue: "Unicode table"
                [charTable := SparseLargeTable new: cmap size
                        chunkSize: 256 arrayClass: Array base: 1
                        defaultValue: glyphs first.
                1 to: charTable size do:
                        [:i |
                        glyph := glyphs at: (cmap at: i) + 1 ifAbsent: [glyphs first].
                        charTable at: i put: glyph].
                charTable zapDefaultOnlyEntries.
                ^charTable].

        charTable := Array new: 256 withAll: glyphs first. "Initialize with default glyph"

        assoc key = 1 ifTrue: "Mac encoded table"
                [1 to: (cmap size min: charTable size) do:
                        [:i |
                        glyph := glyphs at: (cmap at: i) + 1.
                        charTable at: (self macToWin: i) put: glyph]].

        assoc key = 3 ifTrue: "Win encoded table"
                [1 to: (cmap size min: charTable size) do:
                        [:i |
                        glyph := glyphs at: (cmap at: i) + 1.
                        charTable at: i put: glyph]].

        ^ charTable!

----- Method: TTFontReader>>processCharacterMappingTable: (in category 'processing') -----
processCharacterMappingTable: entry
        "Read the font's character to glyph index mapping table.
        If an appropriate mapping can be found then return an association
        with the format identifier and the contents of the table"
        | copy initialOffset nSubTables pID sID offset cmap assoc |
        initialOffset := entry offset.
        entry skip: 2. "Skip table version"
        nSubTables := entry nextUShort.
        1 to: nSubTables do:[:i|
                pID := entry nextUShort.
                sID := entry nextUShort.
                offset := entry nextULong.
                "Check if this is either a Unicode (0), Macintosh (1),
                or a Windows (3) encoded table"
                (#(0 1 3) includes: pID) ifTrue:[
                        "Go to the beginning of the table"
                        copy := entry copy.
                        copy offset: initialOffset + offset.
                        cmap := self decodeCmapFmtTable: copy.
                        (pID = 0 and: [cmap notNil]) "Prefer Unicode encoding over everything else"
                                ifTrue: [^ pID -> cmap].
                        assoc := pID -> cmap. "Keep it in case we don't find a better table"
                ].
        ].
        ^assoc!

----- Method: TTFontReader>>processCompositeGlyph:contours:from: (in category 'processing') -----
processCompositeGlyph: glyph contours: nContours from: entry
        "Read a composite glyph from the font data. The glyph passed into this method contains some state variables that must be copied into the resulting composite glyph."
        | flags glyphIndex hasInstr cGlyph ofsX ofsY iLen a11 a12 a21 a22 m |
        cGlyph := TTCompositeGlyph new.
        a11 := a22 := 16r4000. "1.0 in F2Dot14"
        a21 := a12 := 0. "0.0 in F2Dot14"
        "Copy state"
        cGlyph bounds: glyph bounds; glyphIndex: glyph glyphIndex.
        hasInstr := false.
        [ flags := entry nextUShort.
        glyphIndex := entry nextUShort + 1.
        (flags bitAnd: 1) = 1 ifTrue:[
                ofsX := entry nextShort.
                ofsY := entry nextShort.
        ] ifFalse:[
                (ofsX := entry nextByte) > 127 ifTrue:[ofsX := ofsX - 256].
                (ofsY := entry nextByte) > 127 ifTrue:[ofsY := ofsY - 256]].
        ((flags bitAnd: 2) = 2) ifFalse:[self halt].
        (flags bitAnd: 8) = 8 ifTrue:[
                a11 := a22 := entry nextShort].
        (flags bitAnd: 64) = 64 ifTrue:[
                a11 := entry nextShort.
                a22 := entry nextShort].
        (flags bitAnd: 128) = 128 ifTrue:[
                "2x2 transformation"
                a11 := entry nextShort.
                a21 := entry nextShort.
                a12 := entry nextShort.
                a22 := entry nextShort].
        m := MatrixTransform2x3 new.
        "Convert entries from F2Dot14 to float"
        m a11: (a11 asFloat / 16r4000).
        m a12: (a12 asFloat / 16r4000).
        m a21: (a21 asFloat / 16r4000).
        m a22: (a22 asFloat / 16r4000).
        m a13: ofsX.
        m a23: ofsY.
        cGlyph addGlyph: (glyphs at: glyphIndex) transformation: m.
        hasInstr := hasInstr or:[ (flags bitAnd: 256) = 256].
        "Continue as long as the MORE:=COMPONENTS bit is set"
        (flags bitAnd: 32) = 32] whileTrue.
        hasInstr ifTrue:[
                iLen := entry nextUShort.
                entry skip: iLen].
        ^cGlyph!

----- Method: TTFontReader>>processFontHeaderTable: (in category 'processing') -----
processFontHeaderTable: entry
"Value Data Type    Description
unitsPerEm USHORT      Granularity of the font's em square.
xMax USHORT      Maximum X-coordinate for the entire font.
xMin USHORT      Minimum X-coordinate for the entire font.
yMax USHORT      Maximum Y-coordinate for the entire font.
yMin USHORT      Minimum Y-coordinate for the entire font.
indexToLocFormat SHORT       Used when processing the Index To Loc Table."
        | origin corner units indexToLocFormat |
        entry skip: 4. "Skip table version number"
        entry skip: 4. "Skip font revision number"
        entry skip: 4. "Skip check sum adjustment"
        entry skip: 4. "Skip magic number"
        entry skip: 2. "Skip flags"

        units := entry nextUShort.

        entry skip: 8. "Skip creation date"
        entry skip: 8. "Skip modification date"

        "Get min/max values of all glyphs"
        origin := entry nextShort @ entry nextShort.
        corner := entry nextShort @ entry nextShort.

        entry skip: 2. "Skip mac style"
        entry skip: 2. "Skip lowest rec PPEM"
        entry skip: 2. "Skip font direction hint"
        indexToLocFormat := entry nextShort.

        fontDescription setBounds: (origin corner: corner) unitsPerEm: units.
        ^indexToLocFormat!

----- Method: TTFontReader>>processGlyphDataTable:offsets: (in category 'processing') -----
processGlyphDataTable: entry offsets: offsetArray
        "Read the actual glyph data from the font.
        offsetArray contains the start offsets in the data for each glyph."
        | initialOffset glyph nextOffset glyphLength glyphOffset nContours origin corner |
        initialOffset := entry offset.
        glyphs := Array new: nGlyphs.
        1 to: nGlyphs do:[:i |
                glyphs at: i put: (TTGlyph new glyphIndex: i-1)].
        'Reading glyph data'
                displayProgressAt: Sensor cursorPoint
                from: 1 to: nGlyphs during:[:bar|

        1 to: nGlyphs do:[:glyphIndex |
                bar value: glyphIndex.
                glyph := glyphs at: glyphIndex.
                glyphOffset := offsetArray at: glyphIndex.
                nextOffset := offsetArray at: glyphIndex+1.
                glyphLength := nextOffset - glyphOffset.
                glyphLength = 0 ifFalse:[
                        entry offset: initialOffset + glyphOffset.
                        nContours := entry nextShort.
                        origin := entry nextShort @ entry nextShort.
                        corner := entry nextShort @ entry nextShort.
                        glyph bounds: (origin corner: corner).
                        nContours >= 0 ifTrue:[
                                self processSimpleGlyph: glyph contours: nContours from: entry
                        ] ifFalse:[
                                glyph := self processCompositeGlyph: glyph contours: nContours from: entry.
                                glyphs at: glyphIndex put: glyph]]]
        ].!

----- Method: TTFontReader>>processHorizontalHeaderTable: (in category 'processing') -----
processHorizontalHeaderTable: entry
"
ascender           SHORT          Typographic ascent.
descender          SHORT          Typographic descent.
lineGap            SHORT          Typographic lineGap.
numberOfHMetrics   USHORT         Number hMetric entries in the HTMX
                                               Table; may be smaller than the total
                                             number of glyphs.
"
        | asc desc lGap numHMetrics |
        entry skip: 4. "Skip table version"
        asc := entry nextShort.
        desc := entry nextShort.
        lGap := entry nextShort.
        entry skip: 2. "Skip advanceWidthMax"
        entry skip: 2. "Skip minLeftSideBearing"
        entry skip: 2. "Skip minRightSideBearing"
        entry skip: 2. "Skip xMaxExtent"
        entry skip: 2. "Skip caretSlopeRise"
        entry skip: 2. "Skip caretSlopeRun"
        entry skip: 10. "Skip 5 reserved shorts"
        entry skip: 2. "Skip metricDataFormat"

        numHMetrics := entry nextUShort.

        fontDescription setAscender: asc descender: desc lineGap: lGap.
        ^numHMetrics!

----- Method: TTFontReader>>processHorizontalMetricsTable:length: (in category 'processing') -----
processHorizontalMetricsTable: entry length: numHMetrics
        "Extract the advance width, left side bearing, and right
        side bearing for each glyph from the Horizontal Metrics Table."
        |  index lastAW glyph |
        index := 1.
        [index <= numHMetrics] whileTrue:[
                glyph := glyphs at: index.
                glyph advanceWidth: entry nextUShort.
                glyph leftSideBearing: entry nextShort.
                glyph updateRightSideBearing.
                index := index + 1].
        index = (nGlyphs +1) ifTrue:[^true].
        lastAW := (glyphs at: index-1) advanceWidth.

        [index <= nGlyphs] whileTrue:[
                glyph := glyphs at: index.
                glyph advanceWidth: lastAW.
                glyph leftSideBearing: entry nextShort.
                glyph updateRightSideBearing.
                index := index + 1].!

----- Method: TTFontReader>>processIndexToLocationTable:format: (in category 'processing') -----
processIndexToLocationTable: entry format: indexToLocFormat
"glyphOffset    ULONG[numGlyphs]   An array that contains each glyph's
                                 offset into the Glyph Data Table.
" | glyphOffset offset|
        glyphOffset := Array new: nGlyphs+1.
        1 to: nGlyphs+1 do:[:i|
                (indexToLocFormat = 0) ifTrue:[ "Format0: offset/2 is stored"
                        offset := entry nextUShort * 2.
                ] ifFalse:["Format1: store actual offset"
                        offset := entry nextULong].
                glyphOffset at: i put: offset].
        ^glyphOffset!

----- Method: TTFontReader>>processKerningTable: (in category 'processing') -----
processKerningTable: entry
        "Extract the kerning information for pairs of glyphs."
        | covLow covHigh nKernPairs kp |
        entry skip: 2. "Skip table version"
        entry skip: 2. "Skip number of sub tables -- we're using the first one only"
        entry skip: 2. "Skip current subtable number"
        entry skip: 2. "Skip length of subtable"
        covHigh := entry nextByte.
        covLow := entry nextByte.

        "Make sure the format is right (kerning table and format type 0)"
        ((covLow bitAnd: 2) = 2 or:[ covHigh ~= 0]) ifTrue:[^false].
        nKernPairs := entry nextUShort.
        entry skip: 2. "Skip search range"
        entry skip: 2. "Skip entry selector"
        entry skip: 2. "Skip range shift"
        kernPairs := Array new: nKernPairs.
        1 to: nKernPairs do:[:i|
                kp := TTKernPair new.
                kp left: entry nextUShort.
                kp right: entry nextUShort.
                kp value: entry nextShort.
                kernPairs at: i put: kp].
        ^true!

----- Method: TTFontReader>>processMaximumProfileTable: (in category 'processing') -----
processMaximumProfileTable: entry
"
numGlyphs         USHORT      The number of glyphs in the font.
"
        entry skip: 4. "Skip Table version number"
        nGlyphs := entry nextUShort.!

----- Method: TTFontReader>>processNamingTable: (in category 'processing') -----
processNamingTable: entry
"copyright         CHARPTR     The font's copyright notice.
familyName        CHARPTR     The font's family name.
subfamilyName     CHARPTR     The font's subfamily name.
uniqueName        CHARPTR     A unique identifier for this font.
fullName          CHARPTR     The font's full name (a combination of
                                          familyName and subfamilyName).
versionName       CHARPTR     The font's version string.
" | nRecords initialOffset storageOffset pID sID lID nID length offset multiBytes string strings |
        strings := Array new: 8.
        strings atAllPut:''.
        initialOffset := entry offset.
        entry skip: 2. "Skip format selector"
        "Get the number of name records"
        nRecords := entry nextUShort.
        "Offset from the beginning of this table"
        storageOffset := entry nextUShort + initialOffset.
        1 to: nRecords do:[:i|
                pID := entry nextUShort.
                sID := entry nextUShort.
                lID := entry nextUShort.
                nID := entry nextUShort.
                length := entry nextUShort.
                offset := entry nextUShort.
                "Read only Macintosh or Microsoft strings"
                (pID = 1 or:[pID = 3 and:[sID = 1]]) ifTrue:[
                        "MS uses Unicode all others single byte"
                        multiBytes := pID = 3.
                        string := entry stringAt: storageOffset + offset length: length multiByte: multiBytes.
                        "Put the name at the right location.
                        Note: We prefer Macintosh strings about everything else."
                        nID < strings size ifTrue:[
                                (pID = 1 or:[(strings at: nID+1) = ''])
                                        ifTrue:[strings at: nID+1 put: string].
                        ].
                ].
        ].
        fontDescription setStrings: strings.!

----- Method: TTFontReader>>processOS2Table: (in category 'processing') -----
processOS2Table: entry
"
        USHORT   version   0x0004
        SHORT xAvgCharWidth
        USHORT usWeightClass
        USHORT usWidthClass
        USHORT fsType
        SHORT ySubscriptXSize
        SHORT ySubscriptYSize
        SHORT ySubscriptXOffset
        SHORT ySubscriptYOffset
        SHORT ySuperscriptXSize
        SHORT ySuperscriptYSize
        SHORT ySuperscriptXOffset
        SHORT ySuperscriptYOffset
        SHORT yStrikeoutSize
        SHORT yStrikeoutPosition
        SHORT sFamilyClass
        BYTE panose[10]
        ULONG ulUnicodeRange1 Bits 0-31
        ULONG ulUnicodeRange2 Bits 32-63
        ULONG ulUnicodeRange3 Bits 64-95
        ULONG ulUnicodeRange4 Bits 96-127
        CHAR achVendID[4]
        USHORT fsSelection
        USHORT usFirstCharIndex
        USHORT usLastCharIndex
        SHORT sTypoAscender
        SHORT sTypoDescender
        SHORT sTypoLineGap
        USHORT usWinAscent
        USHORT usWinDescent
        ULONG ulCodePageRange1 Bits 0-31
        ULONG ulCodePageRange2 Bits 32-63
        SHORT sxHeight
        SHORT sCapHeight
        USHORT usDefaultChar
        USHORT usBreakChar
        USHORT usMaxContext "
        | version fsSelection minAscii maxAscii asc desc lGap |
        version := entry nextShort. "table version"
        version = 0 ifTrue:[^self].
        entry skip: 60.
        fsSelection := entry nextUShort.
        minAscii := entry nextUShort.
        maxAscii := entry nextUShort.
        asc := entry nextShort.
        desc := entry nextShort.
        lGap := entry nextShort.
        fontDescription setTypographicAscender: asc descender: desc lineGap: lGap.!

----- Method: TTFontReader>>processSimpleGlyph:contours:from: (in category 'processing') -----
processSimpleGlyph: glyph contours: nContours from: entry

        | endPts  nPts iLength flags |
        endPts := Array new: nContours.
        1 to: nContours do:[:i| endPts at: i put: entry nextUShort].
        glyph initializeContours: nContours with: endPts.
        nPts := endPts last + 1.
        iLength := entry nextUShort. "instruction length"
        entry skip: iLength.
        flags := self getGlyphFlagsFrom: entry size: nPts.
        self readGlyphXCoords: entry glyph: glyph nContours: nContours flags: flags endPoints: endPts.
        self readGlyphYCoords: entry glyph: glyph nContours: nContours flags: flags endPoints: endPts.
        glyph buildContours.!

----- Method: TTFontReader>>readFrom: (in category 'public') -----
readFrom: aStream

        | fontData headerEntry maxProfileEntry nameEntry indexLocEntry charMapEntry glyphEntry horzHeaderEntry horzMetricsEntry kerningEntry glyphOffset cmap numHMetrics indexToLocFormat |

        "Read the raw font byte data"
        aStream binary.
        fontData := aStream contents asByteArray.
        fontDescription := TTFontDescription new.

        "Search the tables required to build the font"
        (headerEntry := self getTableDirEntry: 'head' from: fontData) == nil ifTrue:[
                ^self error:'This font does not have a header table'].
        (maxProfileEntry := self getTableDirEntry: 'maxp' from: fontData) == nil ifTrue:[
                ^self error:'This font does not have a maximum profile table'].
        (nameEntry := self getTableDirEntry: 'name' from: fontData) == nil ifTrue:[
                ^self error:'This font does not have a name table'].
        (indexLocEntry := self getTableDirEntry: 'loca' from: fontData) == nil ifTrue:[
                ^self error:'This font does not have a relocation table'].
        (charMapEntry := self getTableDirEntry: 'cmap' from: fontData) == nil ifTrue:[
                ^self error:'This font does not have a character map table'].
        (glyphEntry := self getTableDirEntry: 'glyf' from: fontData) == nil ifTrue:[
                ^self error:'This font does not have a glyph table'].
        (horzHeaderEntry := self getTableDirEntry: 'hhea' from: fontData) == nil ifTrue:[
                ^self error:'This font does not have a horizontal header table'].
        (horzMetricsEntry := self getTableDirEntry: 'hmtx' from: fontData) == nil ifTrue:[
                ^self error:'This font does not have a horizontal metrics table'].
        (kerningEntry := self getTableDirEntry: 'kern' from: fontData) == nil ifTrue:[
                Transcript cr; show:'This font does not have a kerning table';endEntry].


        "Process the data"
        indexToLocFormat := self processFontHeaderTable: headerEntry.
        self processMaximumProfileTable: maxProfileEntry.
        self processNamingTable: nameEntry.
        glyphOffset := self processIndexToLocationTable: indexLocEntry format: indexToLocFormat.
        cmap := self processCharacterMappingTable: charMapEntry.
        (cmap == nil or:[cmap value == nil])
                ifTrue:[^self error:'This font has no suitable character mappings'].
        self processGlyphDataTable: glyphEntry offsets: glyphOffset.
        numHMetrics := self processHorizontalHeaderTable: horzHeaderEntry.
        self processHorizontalMetricsTable: horzMetricsEntry length: numHMetrics.
        kerningEntry isNil
                ifTrue:[kernPairs := #()]
                ifFalse:[self processKerningTable: kerningEntry].
        charMap := self processCharMap: cmap.
        fontDescription setGlyphs: glyphs mapping: charMap.
        fontDescription setKernPairs: kernPairs.
        ^fontDescription!

----- Method: TTFontReader>>readGlyphXCoords:glyph:nContours:flags:endPoints: (in category 'private') -----
readGlyphXCoords:entry glyph: glyph nContours: nContours flags: flags endPoints: endPts
        "Read the x coordinates for the given glyph from the font file."
        | startPoint endPoint flagBits xValue contour ttPoint |
        startPoint := 1.
        1 to: nContours do:[:i|
                contour := glyph contours at: i.
                "Get the end point"
                endPoint := (endPts at: i) + 1.
                "Store number of points"
                startPoint to: endPoint do:[:j|
                        ttPoint := contour points at: (j - startPoint + 1).
                        flagBits := flags at: j.
                        "If bit zero in the flag is set then this point is an on-curve
                        point, if not, then it is an off-curve point."
                        (flagBits bitAnd: 1) = 1
                                ifTrue:[ ttPoint type: #OnCurve]
                                ifFalse:[ttPoint type: #OffCurve].
                        "First we check to see if bit one is set.  This would indicate that
                        the corresponding coordinate data in the table is 1 byte long.
                        If the bit is not set, then the coordinate data is 2 bytes long."
                        (flagBits bitAnd: 2) = 2 ifTrue:[ "one byte"
                                xValue := entry nextByte.
                                xValue := (flagBits bitAnd: 16)=16 ifTrue:[xValue] ifFalse:[xValue negated].
                                ttPoint x: xValue.
                        ] ifFalse:[ "two byte"
                                "If bit four is set, then this coordinate is the same as the
                                last one, so the relative offset (of zero) is stored.  If bit
                                is not set, then read in two bytes and store it as a signed value."
                                (flagBits bitAnd: 16) = 16 ifTrue:[ ttPoint x: 0 ]
                                ifFalse:[
                                        xValue := entry nextShort.
                                        ttPoint x: xValue]]].
                startPoint := endPoint + 1]!

----- Method: TTFontReader>>readGlyphYCoords:glyph:nContours:flags:endPoints: (in category 'private') -----
readGlyphYCoords:entry glyph: glyph nContours: nContours flags: flags endPoints: endPts
        "Read the y coordinates for the given glyph from the font file."
        | startPoint endPoint flagBits yValue contour ttPoint |
        startPoint := 1.
        1 to: nContours do:[:i|
                contour := glyph contours at: i.
                "Get the end point"
                endPoint := (endPts at: i) + 1.
                "Store number of points"
                startPoint to: endPoint do:[:j|
                        ttPoint := contour points at: (j - startPoint + 1).
                        flagBits := flags at: j.
                        "Check if this value one or two byte encoded"
                        (flagBits bitAnd: 4) = 4 ifTrue:[ "one byte"
                                yValue := entry nextByte.
                                yValue := (flagBits bitAnd: 32)=32 ifTrue:[yValue] ifFalse:[yValue negated].
                                ttPoint y: yValue.
                        ] ifFalse:[ "two byte"
                                (flagBits bitAnd: 32) = 32 ifTrue:[ ttPoint y: 0 ]
                                ifFalse:[
                                        yValue := entry nextShort.
                                        ttPoint y: yValue]]].
                startPoint := endPoint + 1]!

----- Method: TTFontReader>>warn: (in category 'private') -----
warn: aString
        Transcript cr; show: aString; endEntry.!

----- Method: TTFontReader>>winToMac: (in category 'private') -----
winToMac: index
        ^ (index - 1) asCharacter squeakToMac asciiValue + 1!

Object subclass: #TTFontTableDirEntry
        instanceVariableNames: 'tag fontData offset length checkSum'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'TrueType-Support'!

!TTFontTableDirEntry commentStamp: '<historical>' prior: 0!
This class represents an entry in a truetype font table directory. Used by TTFontReader only.!

----- Method: TTFontTableDirEntry class>>on:at: (in category 'instance creation') -----
on: fontData at: index

        ^self new on: fontData at: index!

----- Method: TTFontTableDirEntry>>nextByte (in category 'accessing') -----
nextByte

        | value |
        value := fontData byteAt: offset.
        offset := offset + 1.
        ^value!

----- Method: TTFontTableDirEntry>>nextBytes:into:startingAt: (in category 'accessing') -----
nextBytes: numBytes into: array startingAt: byteOffset

        1 to: numBytes do:[:i|
                array at: i put: (fontData byteAt: byteOffset + i - 1)].!

----- Method: TTFontTableDirEntry>>nextLong (in category 'accessing') -----
nextLong

        | value |
        value := fontData longAt: offset bigEndian: true.
        offset := offset + 4.
        ^value!

----- Method: TTFontTableDirEntry>>nextShort (in category 'accessing') -----
nextShort

        | value |
        value := fontData shortAt: offset bigEndian: true.
        offset := offset + 2.
        ^value!

----- Method: TTFontTableDirEntry>>nextULong (in category 'accessing') -----
nextULong

        | value |
        value := fontData unsignedLongAt: offset bigEndian: true.
        offset := offset + 4.
        ^value!

----- Method: TTFontTableDirEntry>>nextUShort (in category 'accessing') -----
nextUShort

        | value |
        value := fontData unsignedShortAt: offset bigEndian: true.
        offset := offset + 2.
        ^value!

----- Method: TTFontTableDirEntry>>offset (in category 'accessing') -----
offset
        ^offset!

----- Method: TTFontTableDirEntry>>offset: (in category 'accessing') -----
offset: newOffset
        offset := newOffset!

----- Method: TTFontTableDirEntry>>on:at: (in category 'initialize-release') -----
on: fd at: index

        fontData := fd.
        tag := fontData longAt: index bigEndian: true.
        checkSum := fontData longAt: index+4 bigEndian: true.
        offset := (fontData longAt: index+8 bigEndian: true) + 1.
        length := fontData longAt: index+12 bigEndian: true.!

----- Method: TTFontTableDirEntry>>skip: (in category 'accessing') -----
skip: n
        "Skip n bytes"
        offset := offset + n.!

----- Method: TTFontTableDirEntry>>stringAt:length:multiByte: (in category 'accessing') -----
stringAt: stringOffset length: byteLength multiByte: aBoolean

        | string index stringLength |
        aBoolean ifFalse:[
                stringLength := byteLength.
                string := String new: stringLength.
                index := stringOffset.
                1 to: stringLength do:[:i|
                        string at: i put: (Character value: (fontData byteAt: index + i - 1))].
                ^string
        ] ifTrue:[
                stringLength := byteLength // 2.
                string := String new: stringLength.
                index := stringOffset.
                1 to: stringLength do:[:i|
                        string at: i put: (Character value: (fontData byteAt: index + 1)).
                        index := index + 2].
                ^string]!

Object subclass: #TTGlyph
        instanceVariableNames: 'bounds contours advanceWidth leftSideBearing rightSideBearing glyphIndex'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'TrueType-Fonts'!

!TTGlyph commentStamp: '<historical>' prior: 0!
This class represents a glyph of a TrueType font.

Instance variables:
        bounds <Rectangle> The receiver's bounds
        contours <Array of: PointArray> The compressed contours in the receiver
        advanceWidth <Integer> advance width of the glyph
        leftSideBearing <Integer> left side bearing
        rightSideBearing <Integer> right side bearing
        glyphIndex <Integer> the original index of the glyph (used for kerning)!

TTGlyph subclass: #TTCompositeGlyph
        instanceVariableNames: 'glyphs'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'TrueType-Fonts'!

!TTCompositeGlyph commentStamp: '<historical>' prior: 0!
This class represents a composite TrueType glyph, e.g.one which contains many simple TTGlyphs.!

----- Method: TTCompositeGlyph>>addGlyph:transformation: (in category 'accessing') -----
addGlyph: aGlyph transformation: aMatrix
        glyphs := glyphs copyWith: (aMatrix -> aGlyph)!

----- Method: TTCompositeGlyph>>buildAllContours (in category 'accessing') -----
buildAllContours
        "Build the contours in all non-composite glyphs."
        glyphs do:[:assoc| assoc value buildAllContours].!

----- Method: TTCompositeGlyph>>computeContours (in category 'private') -----
computeContours
        | out |
        out := WriteStream on: (Array new: glyphs size * 4).
        self glyphsAndTransformationsDo:[:glyph :transform|
                glyph contours do:[:ptArray|
                        out nextPut: (transform localPointsToGlobal: ptArray).
                ].
        ].
        ^out contents!

----- Method: TTCompositeGlyph>>contours (in category 'accessing') -----
contours
        ^contours ifNil:[contours := self computeContours]!

----- Method: TTCompositeGlyph>>flipAroundY (in category 'private') -----
flipAroundY
        bounds := (bounds origin x @ bounds corner y negated) corner:
                                (bounds corner x @ bounds origin y negated).
        contours := nil.!

----- Method: TTCompositeGlyph>>glyphs (in category 'accessing') -----
glyphs

        ^glyphs collect:[:assoc| assoc value].!

----- Method: TTCompositeGlyph>>glyphsAndTransformationsDo: (in category 'accessing') -----
glyphsAndTransformationsDo: aBlock
        glyphs do:[:assoc|
                aBlock value: assoc value value: assoc key.
        ].!

----- Method: TTCompositeGlyph>>initialize (in category 'initialize') -----
initialize
        glyphs := #().!

----- Method: TTCompositeGlyph>>isComposite (in category 'testing') -----
isComposite
        ^true!

----- Method: TTCompositeGlyph>>referenceVertexAt: (in category 'initialize') -----
referenceVertexAt: index
        "Only used while reading before constructing contours"
        | i p |
        i := index.
        self glyphsAndTransformationsDo: [:glyph :transform |
                p := glyph referenceVertexAt: i.
                p isPoint
                        ifTrue: [^transform localPointToGlobal: p].
                i := i - p].
        self error: ['this should not happen']!

----- Method: TTGlyph>>advanceWidth (in category 'accessing') -----
advanceWidth
        ^advanceWidth!

----- Method: TTGlyph>>advanceWidth: (in category 'accessing') -----
advanceWidth: aNumber
        advanceWidth := aNumber.!

----- Method: TTGlyph>>asFormWithScale:ascender:descender: (in category 'converting') -----
asFormWithScale: scale ascender: ascender descender: descender
        ^ self
                asFormWithScale: scale
                ascender: ascender
                descender: descender
                fgColor: Color black
                bgColor: Color white
                depth: 8
                replaceColor: true.
!

----- Method: TTGlyph>>asFormWithScale:ascender:descender:fgColor:bgColor:depth: (in category 'converting') -----
asFormWithScale: scale ascender: ascender descender: descender fgColor: fgColor bgColor: bgColor depth: depth

        ^ self
                asFormWithScale: scale
                ascender: ascender
                descender: descender
                fgColor: fgColor
                bgColor: bgColor
                depth: depth
                replaceColor: false.
!

----- Method: TTGlyph>>asFormWithScale:ascender:descender:fgColor:bgColor:depth:replaceColor: (in category 'converting') -----
asFormWithScale: scale ascender: ascender descender: descender fgColor: fgColor bgColor: bgColor depth: depth replaceColor: replaceColorFlag

        ^ self
                asFormWithScale: scale
                ascender: ascender
                descender: descender
                fgColor: fgColor
                bgColor: bgColor
                depth: depth
                replaceColor: replaceColorFlag
                lineGlyph: nil
                lingGlyphWidth: 0
                emphasis: 0.!

----- Method: TTGlyph>>asFormWithScale:ascender:descender:fgColor:bgColor:depth:replaceColor:lineGlyph:lingGlyphWidth:emphasis: (in category 'converting') -----
asFormWithScale: scale ascender: ascender descender: descender fgColor: fgColor bgColor: bgColor depth: depth replaceColor: replaceColorFlag lineGlyph: lineGlyph lingGlyphWidth: lWidth emphasis: code

        | form canvas newScale |
        form := Form extent: (advanceWidth @ (ascender - descender) * scale) rounded depth: depth.
        form fillColor: bgColor.
        canvas := BalloonCanvas on: form.
        canvas aaLevel: 4.
        canvas transformBy: (MatrixTransform2x3 withScale: scale asPoint * (1 @ -1)).
        canvas transformBy: (MatrixTransform2x3 withOffset: 0 @ ascender negated).
        canvas
                drawGeneralBezierShape: self contours
                color: fgColor
                borderWidth: 0
                borderColor: fgColor.
        ((code bitAnd: 4) ~= 0 or: [(code bitAnd: 16) ~= 0]) ifTrue: [
                newScale := (form width + 1) asFloat / lineGlyph calculateWidth asFloat.
                canvas transformBy: (MatrixTransform2x3 withScale: (newScale / scale)@1.0).

                (code bitAnd: 4) ~= 0 ifTrue: [
                        canvas
                                drawGeneralBezierShape: lineGlyph contours
                                color: fgColor
                                borderWidth: 0
                                borderColor: fgColor.
                ].

                (code bitAnd: 16) ~= 0 ifTrue: [
                        canvas transformBy: (MatrixTransform2x3 withOffset: 0@(ascender // 2)).
                        canvas
                                drawGeneralBezierShape: lineGlyph contours
                                color: fgColor
                                borderWidth: 0
                                borderColor: fgColor.
                ].
        ].

        replaceColorFlag ifTrue: [
                form replaceColor: bgColor withColor: Color transparent.
        ].
        ^ form!

----- Method: TTGlyph>>bounds (in category 'accessing') -----
bounds
        ^bounds!

----- Method: TTGlyph>>bounds: (in category 'accessing') -----
bounds: aRectangle
        bounds := aRectangle!

----- Method: TTGlyph>>buildAllContours (in category 'private-initialization') -----
buildAllContours
        "Build the contours in all non-composite glyphs."
        ^self buildContours!

----- Method: TTGlyph>>buildContours (in category 'private-initialization') -----
buildContours
        "Build the contours in the receiver glyph.
        The contour is constructed by converting the points
        form each contour into an absolute value and then
        compressing the contours into PointArrays."
        | tx ty points |
        tx := ty := 0.
        contours := contours collect:[:contour|
                contour isCollection ifTrue:[^self]. "already built"
                points := contour points.
                points do:[:pt|
                        pt x: (tx := tx + pt x).
                        pt y: (ty := ty + pt y)].
                contour asCompressedPoints].!

----- Method: TTGlyph>>calculateWidth (in category 'private') -----
calculateWidth

        | min max |
        min := SmallInteger maxVal.
        max := SmallInteger minVal.
        self contours do: [:a | a do: [:p |
                p x > max ifTrue: [max := p x].
                p x < min ifTrue: [min := p x].
        ]].
        ^ max - min.
!

----- Method: TTGlyph>>contours (in category 'accessing') -----
contours
        ^contours!

----- Method: TTGlyph>>contours: (in category 'accessing') -----
contours: aCollection
        contours := aCollection asArray.!

----- Method: TTGlyph>>display (in category 'private') -----
display
        | canvas |
        canvas := Display getCanvas.
        self contours do:[:ptArray|
                1 to: ptArray size by: 3 do:[:i|
                        canvas line: (ptArray at: i) // 10
                                        to: (ptArray at: i+2) // 10
                                        width: 1 color: Color black.
                ].
        ].!

----- Method: TTGlyph>>flipAroundY (in category 'private') -----
flipAroundY
        bounds := (bounds origin x @ bounds corner y negated) corner:
                                (bounds corner x @ bounds origin y negated).
        contours := self contours collect:[:contour| contour collect:[:pt| pt x @ pt y negated]].!

----- Method: TTGlyph>>glyphIndex (in category 'accessing') -----
glyphIndex
        ^glyphIndex!

----- Method: TTGlyph>>glyphIndex: (in category 'accessing') -----
glyphIndex: anInteger
        glyphIndex := anInteger!

----- Method: TTGlyph>>glyphsAndTransformationsDo: (in category 'accessing') -----
glyphsAndTransformationsDo: aBlock
        aBlock value: self value: MatrixTransform2x3 identity!

----- Method: TTGlyph>>initialize (in category 'initialize-release') -----
initialize

        bounds := 0@0 corner: 0@0.
        contours := #().
        advanceWidth := 0.
        leftSideBearing := 0.
        rightSideBearing := 0.!

----- Method: TTGlyph>>initializeContours:with: (in category 'private-initialization') -----
initializeContours: numContours with: endPoints
        "Initialize the contours for creation of the glyph."
        | startPt pts endPt |
        contours := Array new: numContours.
        startPt := -1.
        1 to: numContours do:[:i|
                endPt := endPoints at: i.
                pts := Array new: endPt - startPt.
                1 to: pts size do:[:j| pts at: j put: TTPoint new].
                contours at: i put: (TTContourConstruction on: pts).
                startPt := endPt].!

----- Method: TTGlyph>>isComposite (in category 'testing') -----
isComposite
        ^false!

----- Method: TTGlyph>>leftSideBearing (in category 'accessing') -----
leftSideBearing
        ^leftSideBearing!

----- Method: TTGlyph>>leftSideBearing: (in category 'accessing') -----
leftSideBearing: aNumber
        leftSideBearing := aNumber.!

----- Method: TTGlyph>>printOn: (in category 'printing') -----
printOn: aStream

        aStream
                nextPutAll: self class name;
                nextPut:$(;
                print: (contours ifNil: [0] ifNotNil: [contours size]);
                nextPut:$).!

----- Method: TTGlyph>>referenceVertexAt: (in category 'private-initialization') -----
referenceVertexAt: index
        "Only used while reading before constructing contours"
        | count vertices |
        count := 0.
        contours do: [:construction |
                vertices := construction points.
                index - count > vertices size
                        ifTrue: [count := count + vertices size]
                        ifFalse: [^(vertices at: index - count) asPoint]].
        ^count!

----- Method: TTGlyph>>rightSideBearing (in category 'accessing') -----
rightSideBearing
        ^rightSideBearing!

----- Method: TTGlyph>>rightSideBearing: (in category 'accessing') -----
rightSideBearing: aNumber
        rightSideBearing := aNumber.!

----- Method: TTGlyph>>updateRightSideBearing (in category 'private-initialization') -----
updateRightSideBearing
        "Update the right side bearing value"
        "@@: Is the following really correct?!!?!!"
        rightSideBearing := advanceWidth - leftSideBearing - bounds corner x + bounds origin x!

Object subclass: #TTKernPair
        instanceVariableNames: 'left right value mask'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'TrueType-Fonts'!

!TTKernPair commentStamp: '<historical>' prior: 0!
A TTKernPair represents a TrueType kerning pair.

Instance variables:
        left <Integer> The glyph index for the left character.
        right <Integer> The glyph index for the right character.
        value <Integer> The amount of kerning.
        mask <Integer> An efficient representation for the left and the right value.!

----- Method: TTKernPair class>>maskFor:with: (in category 'accessing') -----
maskFor: left with: right
        ^(left bitShift: 12) + right!

----- Method: TTKernPair>>left (in category 'accessing') -----
left
        ^left!

----- Method: TTKernPair>>left: (in category 'accessing') -----
left: aNumber

        left := aNumber!

----- Method: TTKernPair>>mask (in category 'accessing') -----
mask
        ^mask ifNil:[mask := self class maskFor: left with: right]!

----- Method: TTKernPair>>right (in category 'accessing') -----
right
        ^right!

----- Method: TTKernPair>>right: (in category 'accessing') -----
right: aNumber

        right := aNumber!

----- Method: TTKernPair>>value (in category 'accessing') -----
value
        ^value!

----- Method: TTKernPair>>value: (in category 'accessing') -----
value: aNumber

        value := aNumber!

Object subclass: #TTPoint
        instanceVariableNames: 'x y type'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'TrueType-Support'!

!TTPoint commentStamp: '<historical>' prior: 0!
A representation of a TrueType point which includes a 'type' flag defining whether this point is an 'on' or an 'off' curve point.!

----- Method: TTPoint>>asPoint (in category 'converting') -----
asPoint
        ^x@y!

----- Method: TTPoint>>printOn: (in category 'printing') -----
printOn: aStream

        aStream
                nextPutAll: self class name;
                nextPut:$(;
                print: x;
                nextPut:$@;
                print: y;
                nextPut:$|;
                print: type;
                nextPut:$)!

----- Method: TTPoint>>type (in category 'accessing') -----
type
        ^type!

----- Method: TTPoint>>type: (in category 'accessing') -----
type: aSymbol

        type := aSymbol!

----- Method: TTPoint>>x (in category 'accessing') -----
x
        ^x!

----- Method: TTPoint>>x: (in category 'accessing') -----
x: aNumber

        x := aNumber!

----- Method: TTPoint>>y (in category 'accessing') -----
y
        ^y!

----- Method: TTPoint>>y: (in category 'accessing') -----
y: aNumber
        y := aNumber!