Font bearing - lining up texts and outlines

67 views
Skip to first unread message

Jens Axel Søgaard

unread,
Jul 10, 2020, 5:45:29 AM7/10/20
to Racket list
Problem:
  Given a character, say ♖ (a white chess rook), how can I get the vertical bearing
  (the vertical distance from the baseline to topmost border of the glyph)?

image.png

Use case:
  The expression (text "♖") creates a pict that draws a white rook.
  The goal is to create a mask for the rook - so basically we need to fill the rook.
  The outline of the rook is available through  get-text-outline  which produces
  a list of paths that can be filled. This works - but in order to line
  the mask and the original (text "♖") up to make a filled rook, information
  on how the paths are placed relative to the baseline is needed.

Here is the rook with baseline:
image.png
Here is the result I am after:
image.png
Here I have manually adjusted the placement of the mask. 
Given the vertical bearing I hope to automate the process.

The glyph image is from:
https://www.freetype.org/freetype2/docs/tutorial/step2.html

/Jens Axel
https://racket-stories.com

WarGrey Gyoudmon Ju

unread,
Jul 10, 2020, 11:51:09 PM7/10/20
to Jens Axel Søgaard, Racket list
Hi, Jens

I have done this before in my graphics package by using pangocairo apis directly.


which is exemplified in

Screen Shot 2020-07-11 at 11.47.24.png


--
You received this message because you are subscribed to the Google Groups "Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to racket-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/CABefVgyi-FuRQOHneWSBRLztkPJY4Rg2KtqTZx4mjSLHK_5wbw%40mail.gmail.com.

Jens Axel Søgaard

unread,
Jul 20, 2020, 1:15:08 PM7/20/20
to WarGrey Gyoudmon Ju, Racket list
Thanks for the response.

I have attempted to go in your foot steps and have made a single file example, so it is easy to try out.
Just to recap, the goal is to get the vertical bearing.

The first step is to get the pict `text` numbers and the pango numbers (ascent, descent and height) - 
but I can't get them to match. Any hints or ideas are welcome.

The output of the example is:

chess-glyph-info.rkt>
"pict: text with font height 12"
'(w 12.0)
'(h 16.078125)
'(a 12.826171875)
'(d 3.251953125)
"Pango Height: 12"
'((baseline 9.046875)
  (ink ((w 7.0) (h 8.0) (x 0.0) (y 3.138671875) (y+h 11.138671875)))
  (logical ((w 7.224609375) (h 13.96875) (x 0.0) (y 0.0) (y+h 13.96875))))
"Pango Height: 12*alpha"
'((baseline 12.8203125)
  (ink ((w 8.0) (h 9.0) (x 0.0) (y 3.8203125) (y+h 12.8203125)))
  (logical
   ((w 8.314453125) (h 16.0771484375) (x 0.0) (y 0.0) (y+h 16.0771484375))))



#lang racket/base
(provide (all-defined-out))
(require racket/draw/unsafe/pango
         racket/draw/unsafe/cairo
         racket/draw/unsafe/cairo-lib

         ffi/unsafe
         ffi/unsafe/define
         ffi/unsafe/atomic
         racket/unsafe/ops)

(define-ffi-definer define-cairo cairo-lib #:provide provide)
(define-syntax-rule (_cfun spec ...) (_fun #:lock-name "cairo-pango-lock" spec ...))
(define-syntax-rule (_pfun spec ...) (_fun #:lock-name "cairo-pango-lock" spec ...))
(define-cairo cairo_status_to_string (_cfun _int -> _string))

(define ~fx
  (lambda [fl]
    (unsafe-fl->fx (unsafe-flceiling fl))))

(define ~metric ; divide by 1024
  (lambda [val]
    (unsafe-fl/ (unsafe-fx->fl val)
                (unsafe-fx->fl PANGO_SCALE))))

(define (create_font_desc font-face font-size weight style stretch)
  (define font-desc (pango_font_description_from_string font-face))
  (when weight      (pango_font_description_set_weight  font-desc weight))
  (when style       (pango_font_description_set_style   font-desc style))
  ; (when stretch     (pango_font_description_set_stretch font-desc stretch))
  (pango_font_description_set_absolute_size font-desc (* font-size PANGO_SCALE))
  font-desc)

(define cairo-create-argb-image-surface
  (lambda [flwidth flheight density scale?]
    (define-values (surface cr width height) (cairo-create-argb-image-surface* flwidth flheight density scale?))
    (values surface cr)))

(define cairo-create-image-surface
  ; NOTE: (cairo_image_surface_create_for_data) does not work here since Racket bytes may be moved by GC.
  ; NOTE: CAIRO_FORMAT_ARGB32 is alpha-multiplied.
  (lambda [flwidth flheight density]
    (define width   (unsafe-fxmax (~fx (unsafe-fl* flwidth density)) 1))
    (define height  (unsafe-fxmax (~fx (unsafe-fl* flheight density)) 1))
    (define surface (cairo_image_surface_create CAIRO_FORMAT_ARGB32 width height))
    (define status  (cairo_surface_status surface))

    (unless (unsafe-fx= status CAIRO_STATUS_SUCCESS)
      (raise-arguments-error (or (let use-next-id ([stacks (reverse (continuation-mark-set->context (current-continuation-marks)))])
                                   (and (pair? stacks)
                                        (let ([stack (unsafe-car (unsafe-car stacks))])
                                          (or (and (symbol? stack)
                                                   (let ([$stack (symbol->string stack)])
                                                     (and (unsafe-fx> (string-length $stack) 6)
                                                          (string=? (substring $stack 0 6) "bitmap")))
                                                   stack)
                                              (use-next-id (unsafe-cdr stacks))))))
                                 'make-image)
                             (cairo_status_to_string status)
                             "width" flwidth "height" flheight
                             "density" density))
    (values surface width height)))

(define cairo-create-argb-image-surface*
  (lambda [flwidth flheight density scale?]
    (define-values (surface width height) (cairo-create-image-surface flwidth flheight density))
    (define cr (cairo_create surface))
    (unless (or (not scale?) (unsafe-fl= density 1.0))
      (cairo_scale cr density density))
    (values surface cr width height)))

(define-values (the-surface the-cairo) (cairo-create-argb-image-surface 1.0 1.0 1.0 #false))

(define the-layout
  (let ([&layout (box #false)])
    (lambda []
      (or (unbox &layout)
          (let ([layout (pango_cairo_create_layout the-cairo)])
            (set-box! &layout layout)
            layout)))))

(define (font-desc->get-extent font-desc)
  (define layout (the-layout))
  (pango_layout_set_font_description layout font-desc)
  (let ([baseline (pango_layout_get_baseline layout)])
    (values baseline
            (λ [hint-char]
              (~extent layout baseline hint-char)))))

(define &ink     (make-PangoRectangle 0 0 0 0))
(define &logical (make-PangoRectangle 0 0 0 0))

(define (~extent layout baseline hint-char)
  (pango_layout_set_text    layout hint-char)
  (pango_layout_get_extents layout &ink &logical)
  (list (list 'ink     (rectangle->list &ink))
        (list 'logical (rectangle->list &logical))))

(define (rectangle->list pr)
  (list (list 'w   (~metric (PangoRectangle-width  pr)))
        (list 'h   (~metric (PangoRectangle-height pr)))
        (list 'x   (~metric (PangoRectangle-x      pr)))
        (list 'y   (~metric (PangoRectangle-y      pr)))
        (list 'y+h (~metric (+ (PangoRectangle-y pr)
                               (PangoRectangle-height pr))))))

(define (chess-metrics font-desc content)
  (start-atomic)
  (define-values (baseline get-extent) (font-desc->get-extent font-desc))
  (define out (cons (list 'baseline (~metric baseline)) (get-extent content)))
  (end-atomic)
  out)

(provide (all-defined-out))

;;;
;;; Example
;;;

(require pict)
"pict: text with font height 12"
(define p (text "♖" "Courier" 12))
(list 'w (pict-width   p))
(list 'h (pict-height  p))
(list 'a (pict-ascent  p))
(list 'd (pict-descent p))


(define α (/ 16.078125 13.96875)) ; scale factor to get same height
"Pango Height: 12"
(chess-metrics (create_font_desc "Courier"      12  #f #f 0) "♖")
"Pango Height: 12*alpha"
(chess-metrics (create_font_desc "Courier" (* α 12) #f #f 0) "♖")









--
--
Jens Axel Søgaard

Jens Axel Søgaard

unread,
Jul 21, 2020, 7:25:30 AM7/21/20
to WarGrey Gyoudmon Ju, Racket list
Den man. 20. jul. 2020 kl. 19.14 skrev Jens Axel Søgaard <jens...@soegaard.net>:
Thanks for the response.

I have attempted to go in your foot steps and have made a single file example, so it is easy to try out.
Just to recap, the goal is to get the vertical bearing.

The first step is to get the pict `text` numbers and the pango numbers (ascent, descent and height) - 
but I can't get them to match. Any hints or ideas are welcome.

It helps to use the correct font...

It turns out the chess rook is not in "Courier", it is in  "Arial Unicode MS".
The raw pango functions aren't clever enough to find an alternative font,
but I think `text` from `pict` is. I am not sure of the details here though.

/Jens Axel

Matthew Butterick

unread,
Jul 21, 2020, 11:37:46 AM7/21/20
to Jens Axel Søgaard, WarGrey Gyoudmon Ju, Racket list
The `fontland` library does not have an official public API but it will give you the answer, which is 1446.

#lang racket
(require fontland fontland/ttf-glyph)
(define f (open-font "/System/Library/Fonts/Supplemental/Arial Unicode.ttf"))
;; `layout` uses OpenType positioning & substitution tables to create a glyph run,
;; which is a structure with two vectors: a vector of glyphs and a vector of glyph positions
;; once we have the glyph we can `glyph-decode` it and inspect its yMax value.
(match (layout f "♖")
  [(glyphrun (vector glyph _ ...) _) (hash-ref (glyph-decode glyph) 'yMax)])

WarGrey Gyoudmon Ju

unread,
Jul 22, 2020, 6:53:51 AM7/22/20
to Jens Axel Søgaard, Racket list
Yeah, finding a suitable font is a quite time consuming task.

`racket/draw` just hard-codes the "Arial Unicode MS" as an alternative font for macOS if some glyphs are missing.

Anyway, these days I have been writing docs for my library, and this thread made me added some glyphs utilities to explore font.
Hours later the docs and this example will be updated and seen in https://docs.racket-lang.org/bitmap/font_scrbl.html

macOS has two fonts suitable
Screen Shot 2020-07-22 at 18.27.50.png
Reply all
Reply to author
Forward
0 new messages