add libharu binding and basic harfbuzz binding

This commit is contained in:
Tan, Kian-ting 2025-09-26 01:18:16 +08:00
parent 62f7045fce
commit 02b9e82747
16 changed files with 309 additions and 7 deletions

23
LICENSE-MIT Normal file
View file

@ -0,0 +1,23 @@
uahgi2
MIT License
Copyright (c) 2025 chenjt30
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

7
README.md Normal file
View file

@ -0,0 +1,7 @@
uahgi2
======
README text here.
## Dependencies
- Racket 8.10
- fontconfig (for its `fc-match`)

5
fonts/LICENSE Normal file
View file

@ -0,0 +1,5 @@
NotoSerifCJKtc-VF.ttf and NotoSansCJKtc-VF.ttf are under
under Open Font License https://openfontlicense.org/
from https://github.com/notofonts/

BIN
fonts/NotoSansCJKtc-VF.ttf Normal file

Binary file not shown.

BIN
fonts/NotoSerifCJKtc-VF.ttf Normal file

Binary file not shown.

9
info.rkt Normal file
View file

@ -0,0 +1,9 @@
#lang info
(define collection "uahgi2")
(define deps '("base"))
(define build-deps '("scribble-lib" "racket-doc" "rackunit-lib"))
(define scribblings '(("scribblings/uahgi2.scrbl" ())))
(define pkg-desc "Description Here")
(define version "0.0")
(define pkg-authors '(chenjt30))
(define license '(Apache-2.0 OR MIT))

View file

@ -2,7 +2,8 @@
(require "lexer.rkt" brag/support rackunit) (require "lexer.rkt" brag/support rackunit)
(define (lex str) (define (lex str)
(apply-port-proc basic-lexer str)) (apply-port-proc uahgi2-lexer str))
(lex "@a123") (lex "@a123")
(lex "{@_a12|foo|123.12}123abc\\@\\{mn̂g他@@愛@@貓,也愛這塊土地%註解%。這也是%%。") (lex "{@_a12|foo|123.12}123abc\\@\\{mn̂g他@@愛@@貓,也愛這塊土地%註解%。這也是%%。")
(lex "|測試}abc")

View file

@ -5,7 +5,7 @@
(define-lex-abbrev raw-id (:: (:or "_" (:/ "a" "z") (:/ "A" "Z")) (:* (:or "_" (:/ "0" "9") (:/ "a" "z") (:/ "A" "Z"))))) (define-lex-abbrev raw-id (:: (:or "_" (:/ "a" "z") (:/ "A" "Z")) (:* (:or "_" (:/ "0" "9") (:/ "a" "z") (:/ "A" "Z")))))
(define-lex-abbrev any-other-char (:~ (char-set "0123456789@\\{}%|"))) (define-lex-abbrev any-other-char (:~ (char-set "0123456789@\\{}%|")))
(define basic-lexer (define uahgi2-lexer
(lexer-srcloc (lexer-srcloc
[(:or "\r\n" "\n") (token 'NEWLINE lexeme)] ; newline [(:or "\r\n" "\n") (token 'NEWLINE lexeme)] ; newline
[whitespace (token lexeme #:skip? #t)] ; whitespace [whitespace (token lexeme #:skip? #t)] ; whitespace
@ -17,10 +17,11 @@
["}" (token 'R_PAREN lexeme)] ; } ["}" (token 'R_PAREN lexeme)] ; }
["|" (token 'SEPERATOR lexeme)] ; | (general) seperator ["|" (token 'SEPERATOR lexeme)] ; | (general) seperator
["@@" (token 'G_SEPERATOR lexeme)] ; @@ great seperator ["@@" (token 'G_SEPERATOR lexeme)] ; @@ great seperator
["`" (token 'NUM_CONVERTER lexeme)] ; ` convert to number
[digits (token 'INTEGER (string->number lexeme))] ;123 [digits (token 'INTEGER (string->number lexeme))] ;123
[(:: (:? (char-set "-")) digits "." digits) ; 123.456 [(:: (:? (char-set "-")) digits "." digits) ; 123.456
(token 'DECIMAL (string->number lexeme))] (token 'DECIMAL (string->number lexeme))]
[ (from/to "%" "%") ; %COMMENT% [ (from/to "%" "%") ; %COMMENT%
(token 'COMMENT #:skip? #t)] (token 'COMMENT #:skip? #t)]
[any-other-char (token 'CHAR lexeme)])) ; any char into char [any-other-char (token 'CHAR lexeme)])) ; any char into char
(provide basic-lexer) (provide uahgi2-lexer)

13
parser-test.rkt Normal file
View file

@ -0,0 +1,13 @@
#lang br
(require uahgi2/parser uahgi2/tokenizer brag/support)
(define str #<<Here
{@set|@paperwidth|`2100}
{@set|@paperheight|`2970}
{@set|@textsize|`12.3}
{@maintext@@
{@hd2|我是貓 I'm a cat}天地人123
}
Here
)
(parse-to-datum (apply-tokenizer make-tokenizer str))

17
parser.rkt Normal file
View file

@ -0,0 +1,17 @@
#lang brag
u-program: [/NEWLINE+] (u-expr [/NEWLINE+])*
u-expr:
u-series ; string series
| u-atom
| u-converting-num
| u-sexp
u-sexp:
L_PAREN u-expr (SEPERATOR u-expr)* R_PAREN
| L_PAREN u-expr (G_SEPERATOR [(/" "|/NEWLINE)+] u-expr [(/" "|/NEWLINE)+])* R_PAREN
u-series: (u-atom | u-sexp)+
u-converting-num: NUM_CONVERTER (u-id | u-number)
u-atom: u-id | u-string | u-number | NEWLINE+
u-number: INTEGER | DECIMAL
u-id: ID
u-string: u-char+
u-char: CHAR

View file

@ -1,7 +1,8 @@
#lang uahgi2 #lang uahgi2
{@set|@paperwidth|2100} {@set|@paperwidth|`2100}
{@set|@paperheight|2970} {@set|@paperheight|`2970}
{@set|@textsize|12} {@set|@textsize|`12.3}
{@maintext@@ {@maintext@@
{@hd2|測試}
他對這塊土地有著愛惜的情感。今天是@today星期@dayofweek。 他對這塊土地有著愛惜的情感。今天是@today星期@dayofweek。
@@} @@}

10
scribblings/uahgi2.scrbl Normal file
View file

@ -0,0 +1,10 @@
#lang scribble/manual
@require[@for-label[uahgi2
racket/base]]
@title{uahgi2}
@author{chenjt30}
@defmodule[uahgi2]
Package Description Here

10
tokenizer.rkt Normal file
View file

@ -0,0 +1,10 @@
#lang br
(require "lexer.rkt" brag/support)
(define (make-tokenizer ip [path #f])
(port-count-lines! ip)
(lexer-file-path path)
(define (next-token) (uahgi2-lexer ip))
next-token)
(provide make-tokenizer)

122
unsafe/libharfbuzz.rkt Normal file
View file

@ -0,0 +1,122 @@
#lang racket/base
(require ffi/unsafe)
; don't use the complete lib. It will crash.
(define libhb (ffi-lib "../3rdparty/libharfbuzz-subset"))
(define-cpointer-type _hb_face_t)
(define-cpointer-type _hb_font_t)
(define-cstruct _hb_glyph_info_t
([codepoint _uint32] ;_hb_codepoint_t=_uint32_t
[mask _uint32] ; priv. var.
[cluster _uint32]
[var1 _uint32] ; priv. var.
[var2 _uint32] ; priv. var.
))
(define-cstruct _hb_glyph_position_t
([x_advance _int32] ;_hb_position_t=_int32_t
[y_advance _int32]
[x_offset _int32]
[y_offset _int32]
[var _int32] ; priv. var.
))
;hb_buffer_t *
;hb_buffer_create (void);
(define hb-buffer-create-raw
(get-ffi-obj "hb_buffer_create" libhb
(_fun -> _pointer)))
;void
;hb_buffer_add_utf8 (hb_buffer_t *buffer,
; const char *text,
; int text_length,
; unsigned int item_offset,
; int item_length);
(define hb-buffer-add-utf8-raw
(get-ffi-obj "hb_buffer_add_utf8" libhb
(_fun _pointer _string/utf-8 _int _uint _int -> _void)))
; not implemented for now. maybe implemented in the future.
; // If you know the direction, script, and language
; hb_buffer_set_direction(buf, HB_DIRECTION_LTR)
; hb_buffer_set_script(buf, HB_SCRIPT_LATIN);
; hb_buffer_set_language(buf, hb_language_from_string("en", -1));
;hb_blob_t *
;hb_blob_create_from_file (const char *file_name);
(define hb-blob-create-from-file-raw
(get-ffi-obj "hb_blob_create_from_file" libhb
(_fun _string/utf-8 -> _pointer)))
;hb_face_t *
;hb_face_create (hb_blob_t *blob,
; unsigned int index);
(define hb-face-create-raw
(get-ffi-obj "hb_face_create" libhb
(_fun _pointer _uint -> _hb_face_t)))
; hb_font_t *
; hb_font_create (hb_face_t *face);
(define hb-font-create-raw
(get-ffi-obj "hb_font_create" libhb
(_fun _hb_face_t -> _hb_font_t)))
;void
;hb_shape (hb_font_t *font,
; hb_buffer_t *buffer,
; const hb_feature_t *features,
; unsigned int num_features);
(define hb-shape-raw
(get-ffi-obj "hb_shape" libhb
(_fun _hb_font_t _pointer _pointer _uint -> _void)))
;void
;hb_buffer_guess_segment_properties (hb_buffer_t *buffer)
(define hb-buffer-guess-segment-properties-raw
(get-ffi-obj "hb_buffer_guess_segment_properties" libhb
(_fun _pointer -> _void)))
(define hb-buffer-get-glyph-infos-raw
(get-ffi-obj "hb_buffer_get_glyph_infos" libhb
(_fun
[buf : _pointer]
[glyph-length : (_ptr o _uint)]
-> [glyph-info : _pointer]
-> (values glyph-info glyph-length))))
(define hb-buffer-get-glyph-positions-raw
(get-ffi-obj "hb_buffer_get_glyph_positions" libhb
(_fun
[buf : _pointer]
[glyph-length : (_ptr o _uint)]
-> [glyph-position : _pointer]
-> (values glyph-position glyph-length))))
(define hb-buffer-has-positions-raw
(get-ffi-obj "hb_buffer_has_positions" libhb
(_fun [buf : _pointer] -> _bool)))
(define (create-hb-buffer) (hb-buffer-create-raw))
; test area
(define buf (create-hb-buffer))
(hb-buffer-add-utf8-raw buf "123abc" -1 0 -1)
(define font-path "/usr/share/fonts/truetype/freefont/FreeSansBold.ttf")
(define blob (hb-blob-create-from-file-raw font-path))
(define NULL (cast 0 _int64 _pointer))
(define face (hb-face-create-raw blob 0))
(define font (hb-font-create-raw face))
(hb-buffer-guess-segment-properties-raw buf)
(hb-shape-raw font buf NULL 0)
(define-values (glyph-info glyph-length) (hb-buffer-get-glyph-infos-raw buf))
(define-values (glyph-pos glyph-length2) (hb-buffer-get-glyph-positions-raw buf))
(printf "has pos ?: ~a" (hb-buffer-has-positions-raw buf))
(for ([i glyph-length])
(displayln (hb_glyph_info_t-cluster (ptr-ref glyph-info _hb_glyph_info_t i))))
(for ([i glyph-length2])
(displayln (hb_glyph_position_t-x_advance (ptr-ref glyph-pos _hb_glyph_position_t i))))

Binary file not shown.

83
unsafe/libharu.rkt Normal file
View file

@ -0,0 +1,83 @@
#lang racket/base
(require ffi/unsafe)
(define libhpdf (ffi-lib "../3rdparty/libhpdf"))
(define pdf-new-raw
(get-ffi-obj "HPDF_New" libhpdf
(_fun _int _int -> _pointer)))
(define pdf-save-raw
(get-ffi-obj "HPDF_SaveToFile" libhpdf
(_fun _pointer _string/utf-8 -> _int)))
(define pdf-free-raw
(get-ffi-obj "HPDF_Free" libhpdf
(_fun _pointer -> _void)))
(define add-page-raw
(get-ffi-obj "HPDF_AddPage" libhpdf
(_fun _pointer -> _pointer)))
(define load-ttf-raw
(get-ffi-obj "HPDF_LoadTTFontFromFile" libhpdf
(_fun _pointer _string/utf-8 _bool -> _string)))
(define use-utf-encodingd-raw
(get-ffi-obj "HPDF_UseUTFEncodings" libhpdf
(_fun _pointer -> _int)))
(define set-current-encoder-raw
(get-ffi-obj "HPDF_SetCurrentEncoder" libhpdf
(_fun _pointer _string -> _pointer)))
(define get-font-raw
(get-ffi-obj "HPDF_GetFont" libhpdf
(_fun _pointer _string _string/utf-8 -> _pointer)))
(define set-font-size-raw
(get-ffi-obj "HPDF_Page_SetFontAndSize" libhpdf
(_fun _pointer _pointer _float -> _pointer)))
(define page-begin-text-raw
(get-ffi-obj "HPDF_Page_BeginText" libhpdf
(_fun _pointer -> _pointer)))
(define page-text-out-raw
(get-ffi-obj "HPDF_Page_TextOut" libhpdf
(_fun _pointer _float _float _string/utf-8 -> _pointer)))
(define page-end-text-raw
(get-ffi-obj "HPDF_Page_EndText" libhpdf
(_fun _pointer -> _pointer)))
(define (new-pdf) (pdf-new-raw 0 0))
(define (save-pdf pdf path) (pdf-save-raw pdf path))
(define (new-page pdf) (add-page-raw pdf))
(define (free-pdf p) (pdf-free-raw p)) ; Error invalid memory reference. Some debugging context lost
(define (new-ttf-font p font)
(define ttf-loaded (load-ttf-raw p font #t))
(get-font-raw p ttf-loaded "UTF-8"))
(define (use-utf8 p)
(use-utf-encodingd-raw p)
(set-current-encoder-raw p "UTF-8"))
(define (set-font-size page font size) (set-font-size-raw page font size))
(define (put-text page x y text)
(page-begin-text-raw page)
(page-text-out-raw page x y text)
(page-end-text-raw page)
)
; 以下是測試區
(define doc (new-pdf))
(define pg (new-page doc))
(use-utf8 doc)
(define ian-sui (new-ttf-font doc "/home/yoxem/.local/share/fonts/Iansui-Regular.ttf"))
(set-font-size pg ian-sui 15.0)
(put-text pg 140.0 150.0 "天地人123abc")
(save-pdf doc "/tmp/a.pdf")
(free-pdf doc)