resume in embedded:encode

main
Matthew Butterick 7 years ago
parent fa4eef1325
commit f5b128dcb7

@ -15,10 +15,11 @@ class EmbeddedFont extends PDFFont
@bbox = @font.bbox
encode: (text, features) ->
#console.log(@font.layout text, features + " ")
{glyphs, positions} = @font.layout text, features
for glyph, i in glyphs
console.log("glyphid="+glyph.id)
console.log("position="+positions[i].toString())
#console.log("text=" + text)
#for glyph, i in glyphs
# console.log("glyphs=" + glyph.id)
res = []
for glyph, i in glyphs
@ -33,6 +34,9 @@ class EmbeddedFont extends PDFFont
positions[i].advanceWidth = glyph.advanceWidth * @scale
#for glyph, i in glyphs
#console.log("gid:res:width = " + glyph.id + ":" + res[i] + ":" + positions[i].advanceWidth)
return [res, positions]
widthOfString: (string, size, features) ->

@ -0,0 +1,51 @@
#lang pitfall/racket
(provide BBox bbox->list)
(define-subclass object% (BBox
; The minimum X position in the bounding box
[minX +inf.0]
; The minimum Y position in the bounding box
[minY +inf.0]
; The maxmimum X position in the bounding box
[maxX -inf.0]
; The maxmimum Y position in the bounding box
[maxY -inf.0])
(super-new)
(as-methods
width
height
addPoint
copy))
;; The width of the bounding box
(define/contract (width this)
(->m number?)
(- (· this maxX) (· this minX)))
;; The height of the bounding box
(define/contract (height this)
(->m number?)
(- (· this maxY) (· this minY)))
(define/contract (addPoint this x y)
(number? number? . ->m . void?)
(set-field! minX this (min x (· this minX)))
(set-field! minY this (min y (· this minY)))
(set-field! maxX this (max x (· this maxX)))
(set-field! maxY this (max y (· this maxY))))
(define/contract (copy this)
(->m (is-a?/c BBox))
(make-object BBox (· this minX)
(· this minY)
(· this maxX)
(· this maxY)))
(define/contract (bbox->list this)
((is-a?/c BBox) . -> . (list/c number? number? number? number?))
(list (· this minX) (· this minY) (· this maxX) (· this maxY)))

@ -0,0 +1,5 @@
#lang pitfall/racket
(provide CmapProcessor)
(define-subclass object% (CmapProcessor cmapTable)
(super-new))

@ -1,14 +1,13 @@
#lang pitfall/racket
(require "font.rkt")
(require "font.rkt" "glyph-position.rkt" "glyphrun.rkt")
(provide EmbeddedFont)
(define-subclass PDFFont (EmbeddedFont document font id)
(super-new)
(field [subset (· this font createSubset)]
;; always include the missing glyph (gid = 0)
[unicode '((0))]
;; always include the width of the missing glyph (gid = 0)
[unicode '((0))] ; always include the missing glyph (gid = 0)
[widths (list (send (send (· this font) getGlyph 0) advanceWidth))]
;; always include the width of the missing glyph (gid = 0)
[name (· font postscriptName)]
[scale (/ 1000 (· font unitsPerEm))]
@ -33,22 +32,33 @@ For now, we'll just measure width of the characters.
#;(* width scale)
(send (· this font) measure-string str size))
;; called from text.rkt
(define/contract (encode this text [features #f])
((string?) ((or/c list? #f)) . ->*m . list?)
(error 'embedded:encode-not-implemented))
((string?) ((or/c list? #f)) . ->*m .
(list/c (listof string?) (listof (is-a?/c GlyphPosition))))
(define glyphRun (send (· this font) layout text features))
(define glyphs (· glyphRun glyphs))
(define positions (· glyphRun positions))
(define-values (subset-idxs new-positions)
(for/lists (idxs posns)
([(glyph i) (in-indexed glyphs)])
(values i (* i i))))
(report (list subset-idxs new-positions))
(error 'unimplemented-encode))
(module+ test
(require rackunit "fontkit.rkt")
(require rackunit "fontkit.rkt" "bbox.rkt")
(define f (openSync "test/assets/Charter.ttf" #f))
(define ef (make-object EmbeddedFont #f f #f))
(check-equal? (send ef widthOfString "f" 1000) 321.0)
(check-equal? (· ef ascender) 980)
(check-equal? (· ef descender) -238)
(check-equal? (· ef lineGap) 0)
(check-equal? (· ef bbox) '(-161 -236 1193 963))
(check-equal? (bbox->list (· ef bbox)) '(-161 -236 1193 963))
(define H-gid 41)
(check-equal? (· ef widths) '(278))
(check-equal? (send (send (· ef font) getGlyph H-gid) advanceWidth) 738)
(· ef subset)
(send ef encode "foo")
)

@ -1,5 +1,5 @@
#lang pitfall/racket
(require "freetype-ffi.rkt" ffi/unsafe racket/runtime-path "subset.rkt" "glyph.rkt")
(require "freetype-ffi.rkt" ffi/unsafe racket/runtime-path "subset.rkt" "glyph.rkt" "layout-engine.rkt" "bbox.rkt" "glyphrun.rkt" "cmap-processor.rkt")
(provide (all-defined-out))
(define-runtime-path charter-path "test/assets/charter.ttf")
@ -9,7 +9,9 @@
(define-subclass object% (TTFFont filename)
(super-new)
(field [_glyphs (mhash)])
(field [_tables (mhash)]
[_glyphs (mhash)]
[_layoutEngine #f])
(field [buffer (file->bytes filename)])
@ -38,37 +40,62 @@
createSubset
has-table?
has-cff-table?
getGlyph))
has-morx-table?
has-gsub-table?
has-gpos-table?
getGlyph
layout
glyphsForString
glyphForCodePoint))
;; The unique PostScript name for this font
(define/contract (postscriptName this)
(->m string?)
(FT_Get_Postscript_Name (· this ft-face)))
;; The size of the fonts internal coordinate grid
(define/contract (unitsPerEm this)
(->m number?)
(FT_FaceRec-units_per_EM (· this ft-face)))
;; The fonts [ascender](https://en.wikipedia.org/wiki/Ascender_(typography))
(define/contract (ascent this)
(->m number?)
(FT_FaceRec-ascender (· this ft-face)))
;; The fonts [descender](https://en.wikipedia.org/wiki/Descender)
(define/contract (descent this)
(->m number?)
(FT_FaceRec-descender (· this ft-face)))
;; The amount of space that should be included between lines
(define/contract (lineGap this)
(->m number?)
(define hhea-table (cast (FT_Get_Sfnt_Table (· this ft-face) 'ft_sfnt_hhea) _pointer _FT_HoriHeader-pointer))
(FT_HoriHeader-lineGap hhea-table))
;; The fonts bounding box, i.e. the box that encloses all glyphs in the font.
(define/contract (bbox this)
(->m any/c)
(->m (is-a?/c BBox))
(let ([bbox (FT_FaceRec-bbox (· this ft-face))])
(list (FT_BBox-xMin bbox)
(FT_BBox-yMin bbox)
(FT_BBox-xMax bbox)
(FT_BBox-yMax bbox))))
(make-object BBox (FT_BBox-xMin bbox)
(FT_BBox-yMin bbox)
(FT_BBox-xMax bbox)
(FT_BBox-yMax bbox))))
(define/contract (_cmapProcessor this)
(->m (is-a?/c CmapProcessor))
(make-object CmapProcessor (· this cmap)))
;; Returns a Subset for this font.
(define/contract (createSubset this)
(->m (is-a?/c Subset))
(make-object (if (· this has-cff-table?)
@ -76,21 +103,59 @@
TTFSubset) this))
(define (has-table? this tag)
(FT_Load_Sfnt_Table (· this ft-face) (tag->int tag) 0 0 0))
(define (has-cff-table? this)
(has-table? this #"CFF "))
(define (has-cff-table? this) (has-table? this #"CFF "))
(define (has-morx-table? this) (has-table? this #"morx"))
(define (has-gpos-table? this) (has-table? this #"GPOS"))
(define (has-gsub-table? this) (has-table? this #"GSUB"))
;; Returns a glyph object for the given glyph id.
;; You can pass the array of code points this glyph represents for
;; your use later, and it will be stored in the glyph object.
(define/contract (getGlyph this glyph [characters null])
((number?) (list?) . ->*m . object?)
((index?) ((listof index?)) . ->*m . (is-a?/c Glyph))
(make-object (if (· this has-cff-table?)
CFFGlyph
TTFGlyph) glyph characters this))
;; Returns a GlyphRun object, which includes an array of Glyphs and GlyphPositions for the given string.
(define/contract (layout this string [userFeatures #f] [script #f] [language #f])
((string?) ((or/c (listof symbol?) #f) (or/c symbol? #f) (or/c symbol? #f)) . ->*m . (is-a?/c GlyphRun))
(unless (· this _layoutEngine)
(set-field! _layoutEngine this (make-object LayoutEngine this)))
(send (· this _layoutEngine) layout string userFeatures script language))
;; Returns an array of Glyph objects for the given string.
;; This is only a one-to-one mapping from characters to glyphs.
;; For most uses, you should use font.layout (described below), which
;; provides a much more advanced mapping supporting AAT and OpenType shaping.
(define/contract (glyphsForString this string)
(string? . ->m . (listof (is-a?/c Glyph)))
;; todo: make this handle UTF-16 with surrogate bytes
;; for now, just use UTF-8
(define codepoints (map char->integer (string->list string)))
(for/list ([cp (in-list codepoints)])
(send this glyphForCodePoint cp)))
;; Maps a single unicode code point to a Glyph object.
;; Does not perform any advanced substitutions (there is no context to do so).
(define/contract (glyphForCodePoint this codePoint)
(index? . ->m . (is-a?/c Glyph))
(define glyph-idx (FT_Get_Char_Index (· this ft-face) codePoint))
(send this getGlyph glyph-idx (list codePoint)))
(define/contract (measure-char-width this char)
(char? . ->m . number?)
(define glyph-idx (FT_Get_Char_Index (· this ft-face) (char->integer char)))
@ -140,8 +205,12 @@
(check-equal? (· f descent) -238)
(check-equal? (measure-string f "f" (· f unitsPerEm)) 321.0)
(check-false (· f has-cff-table?))
(check-false (· f has-morx-table?))
(check-false (· f has-gsub-table?))
(check-false (· f has-gpos-table?))
(check-true (send f has-table? #"cmap"))
(check-equal? (· f lineGap) 0)
(· f createSubset)
#;(· f createSubset)
)

@ -0,0 +1,16 @@
#lang pitfall/racket
(provide GlyphPosition)
;; Represents positioning information for a glyph in a GlyphRun.
(define-subclass object% (GlyphPosition
;; The amount to move the virtual pen in the X direction after rendering this glyph.
[xAdvance 0]
;; The amount to move the virtual pen in the Y direction after rendering this glyph.
[yAdvance 0]
;; The offset from the pen position in the X direction at which to render this glyph.
[xOffset 0]
;; The offset from the pen position in the Y direction at which to render this glyph.
[yOffset 0])
(super-new)
)

@ -18,13 +18,13 @@
;; approximates
;; https://github.com/devongovett/fontkit/blob/master/src/glyph/Glyph.js
(define (is-mark? str)
(define (is-mark? codepoint)
;; mark classes = Mn Me Mc
(regexp-match #px"\\p{Mn}|\\p{Me}|\\p{Mc}" str))
(regexp-match #px"\\p{Mn}|\\p{Me}|\\p{Mc}" (string (integer->char codepoint))))
(module+ test
(check-true (and (is-mark? "#\u300") #t))
(check-false (and (is-mark? "#\u299") #t)))
(check-true (and (is-mark? #x300) #t))
(check-false (and (is-mark? #x2ee) #t)))
(define-subclass object% (Glyph id codePoints font)
(super-new)

@ -0,0 +1,37 @@
#lang pitfall/racket
(require "bbox.rkt" "script.rkt")
(provide GlyphRun)
;; Represents a run of Glyph and GlyphPosition objects.
;; Returned by the font layout method.
(define-subclass object% (GlyphRun
glyphs ; An array of Glyph objects in the run
features-in
script ; The script that was requested for shaping. This was either passed in or detected automatically.
language) ; The language requested for shaping, as passed in. If `null`, the default language for the script was used.
(super-new)
;; An array of GlyphPosition objects for each glyph in the run
(field [positions #f])
;; The directionality of the requested script (either ltr or rtl).
(field [direction (script-direction script)])
;; The features requested during shaping. This is a combination of user
;; specified features and features chosen by the shaper.
(field [features (cond
[(hash? features-in) features-in]
;; Convert features to an object
[(list? features-in)
(define f (mhash))
(for ([tag (in-list features)])
(hash-set! f tag #t))
f]
[(not features-in) (mhash)]
[else (error 'glyphrun:unknown-features-type)])])
)

@ -1,5 +1,5 @@
#lang racket/base
(require (for-syntax racket/base racket/syntax) racket/class sugar/list racket/list (only-in br/list push! pop!) racket/string racket/format)
(require (for-syntax racket/base racket/syntax) racket/class sugar/list racket/list (only-in br/list push! pop!) racket/string racket/format racket/contract)
(provide (all-defined-out) push! pop!)
(define-syntax (· stx)
@ -180,4 +180,9 @@
"x0" "x") (~r b #:base 16)))) (bytes->list bstr)))
(module+ test
(check-equal? (bytes->hex #"PNG") '(x50 x4e x47)))
(check-equal? (bytes->hex #"PNG") '(x50 x4e x47)))
(define (layout? x)
(and (hash? x) (hash-has-key? x 'glyphs) (hash-has-key? x 'positions)))
(define index? (and/c (not/c negative?) integer?))

@ -0,0 +1,108 @@
#lang pitfall/racket
(require "script.rkt" "glyph.rkt" "glyphrun.rkt" "glyph-position.rkt")
(provide LayoutEngine)
(define-subclass object% (LayoutEngine font)
(super-new)
(field [unicodeLayoutEngine #f]
[kernProcessor #f]
[engine
;; Choose an advanced layout engine.
;; We try the AAT morx table first since more
;; scripts are currently supported because
;; the shaping logic is built into the font.
(cond
[(· this font has-morx-table?) (error 'morx-layout-unimplemented)]
[(or (· this font has-gsub-table?) (· this font has-gpos-table?))
(error 'ot-layout-unimplemented)]
[else #f])])
(as-methods
layout
substitute
position
hideDefaultIgnorables
isDefaultIgnorable))
(define/contract (layout this str-or-glyphs [features #f]
;; Attempt to detect the script if not provided.
[script (if (string? str-or-glyphs)
(script-for-string str-or-glyphs)
(script-for-codepoints (append-map (λ (g) (· g codePoints)) str-or-glyphs)))]
[language #f])
(((or/c string? (listof (is-a?/c Glyph)))) ((or/c list? #f) (or/c symbol? #f) (or/c symbol? #f)) . ->*m . (is-a?/c GlyphRun))
(define glyphs
(if (string? str-or-glyphs)
(send (· this font) glyphsForString str-or-glyphs)
str-or-glyphs))
(define glyphRun (make-object GlyphRun glyphs features script language))
(if (empty? glyphs)
(set-field! positions glyphRun empty)
(begin
;; Setup the advanced layout engine ; todo
;; Substitute and position the glyphs
(send this substitute glyphRun)
(send this position glyphRun)
(send this hideDefaultIgnorables glyphRun)
;; Let the layout engine clean up any state it might have
(and (· this engine) (· this engine cleanup))))
glyphRun)
(define/contract (substitute this glyphRun)
((is-a?/c GlyphRun) . ->m . void?)
;; Call the advanced layout engine to make substitutions
(when (and (· this engine) (· this engine substitute))
(send (· this engine) substitute glyphRun)))
(define/contract (position this glyphRun)
((is-a?/c GlyphRun) . ->m . void?)
(define positions (for/list ([g (in-list (· glyphRun glyphs))])
(make-object GlyphPosition (· g advanceWidth))))
(set-field! positions glyphRun positions)
;; Call the advanced layout engine. Returns the features applied.
(define positioned
(and (· this engine) (· this engine position)
(send (· this engine) position glyphRun)))
;; if there is no GPOS table, use unicode properties to position marks.
;; todo: implement unicodelayoutengine
;; if kerning is not supported by GPOS, do kerning with the TrueType/AAT kern table
;; todo: implement kerning
(void)
)
(define/contract (hideDefaultIgnorables this glyphRun)
((is-a?/c GlyphRun) . ->m . void?)
(define space (send (· this font) glyphForCodePoint #x20))
(define-values (new-glyphs new-positions)
(for/lists (ngs nps)
([glyph (in-list (· glyphRun glyphs))]
[pos (in-list (· glyphRun positions))])
(cond
[(send this isDefaultIgnorable (car (· glyph codePoints)))
(define new-pos pos)
(set-field! xAdvance new-pos 0)
(set-field! yAdvance new-pos 0)
(values space new-pos)]
[else (values glyph pos)])))
(set-field! glyphs glyphRun new-glyphs)
(set-field! positions glyphRun new-positions))
(define/contract (isDefaultIgnorable this codepoint)
(index? . ->m . boolean?)
#f ; todo: everything
)

@ -0,0 +1,23 @@
#lang pitfall/racket
(provide (all-defined-out))
;; approximates
;; https://github.com/devongovett/fontkit/blob/master/src/layout/Script.js
(define/contract (script-for-string str)
(string? . -> . symbol?)
;; infers unicode script from string.
;; todo: everything
'latn)
(define/contract (script-for-codepoints codepoints)
((listof integer?) . -> . symbol?)
;; infers unicode script from string.
;; todo: everything
(error 'script-for-codepoints-unimplemented))
(define/contract (script-direction script)
((or/c symbol? #f) . -> . symbol?)
'ltr) ; todo everything

@ -1,5 +1,5 @@
#lang pitfall/racket
(require "afm-font.rkt" "font.rkt")
(require "afm-font.rkt" "font.rkt" "glyph-position.rkt")
(require racket/runtime-path (for-syntax racket/base racket/path racket/syntax sugar/debug))
(provide isStandardFont standard-fonts StandardFont)
@ -28,7 +28,7 @@
(define/contract (encode this text [options #f])
((string?) ((or/c hash? #f)) . ->*m . (list/c (listof string?) (listof hash?)))
((string?) ((or/c hash? #f)) . ->*m . (list/c (listof string?) (listof (is-a?/c GlyphPosition))))
(define this-font (· this font))
(define encoded (send this-font encodeText text))
(define glyphs (send this-font glyphsForString text))

@ -1,8 +1,6 @@
#lang pitfall/racket
(provide Subset CFFSubset TTFSubset)
(define index? (and/c (not/c negative?) integer?))
;; approximates
;; https://github.com/devongovett/fontkit/blob/master/src/subset/Subset.js

Binary file not shown.

@ -186,7 +186,7 @@
(if (not (zero? wordSpacing))
(error 'unimplemented-brach) ; todo
(send (· this _font) encode text (hash-ref options 'features #f))))
(define scale (/ (· this _fontSize) 1000.0))
(define commands empty)
(define last 0)

Loading…
Cancel
Save