commit ca93f7224c760c344a71392c356a01adfbf7d253 Author: Marcel Fabian Krüger Date: Tue Dec 28 10:18:21 2021 +0100 Initial commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1de9a1a --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +# TEXLIVE_SRC must be a precompiled TEXLIVE build with dynamic libraries +TEXLIVE_SRC = $(HOME)/texlive-source +# We expect to find a precompiled static library compiled with -fpic here +LUA_DIR = $(HOME)/lua/ +# HarfBuzz also should be compiled static, position independent in a build_meson subdirectory +HARFBUZZ_DIR = $(HOME)/harfbuzz +# Graphite also should be compiled static, position independent in a build_meson subdirectory +GRAPHITE_DIR = $(HOME)/graphite + +HB_INCLUDE_DIRS = $(HARFBUZZ_DIR)/src +TL_INCLUDE_DIRS = $(TEXLIVE_SRC)/Work/texk $(TEXLIVE_SRC)/texk +LUA_INCLUDE_DIRS = $(LUA_DIR) +LINK.so = $(LINK.o) -shared + +LUAHARFBUZZ_SRCS = $(wildcard luaharfbuzz/src/luaharfbuzz/*.c) +LUAHARFBUZZ_OBJS = $(patsubst %.c,%.o,$(LUAHARFBUZZ_SRCS)) + +override CFLAGS += -fpic -DHAVE_DECL_ISASCII=1 $(patsubst %,-I%,$(INCLUDE_DIRS)) +override LDFLAGS += -Wl,linkscript.ld +# For Windows instead: +# override LDFLAGS += -Wl,-version-script=versionscript + +luaharfbuzz/src/luaharfbuzz/%.o: INCLUDE_DIRS = $(HB_INCLUDE_DIRS) $(LUA_INCLUDE_DIRS) +luametalatex-kpse/lkpselib.o: INCLUDE_DIRS = $(TL_INCLUDE_DIRS) $(LUA_INCLUDE_DIRS) + +luametalatex.so: luametalatex-kpse/lkpselib.o $(LUAHARFBUZZ_OBJS) $(TEXLIVE_SRC)/Work/texk/kpathsea/.libs/libkpathsea.a $(HARFBUZZ_DIR)/build_meson/src/libharfbuzz.a $(GRAPHITE_DIR)/build/src/libgraphite2.a $(LUA_DIR)/liblua.a + +%.so: + $(LINK.so) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +.PHONY: clean +clean: + rm $(LUAHARFBUZZ_OBJS) luametalatex-kpse/lkpselib.o luametalatex.so + +# lkpselib.o: lkpselib.c diff --git a/libs.lua b/libs.lua new file mode 100644 index 0000000..a696dc9 --- /dev/null +++ b/libs.lua @@ -0,0 +1 @@ +print(require'luametalatex.kpse', require'luametalatex.harfbuzz') diff --git a/linkscript.ld b/linkscript.ld new file mode 100644 index 0000000..e0a0437 --- /dev/null +++ b/linkscript.ld @@ -0,0 +1,9 @@ +luaopen_luametalatex_kpse = luaopen_kpse; +luaopen_luametalatex_harfbuzz = luaopen_luaharfbuzz; + +VERSION { +{ + global: luaopen_luametalatex_*; + local: *; +}; +} diff --git a/luaharfbuzz/.busted b/luaharfbuzz/.busted new file mode 100644 index 0000000..a0ebd21 --- /dev/null +++ b/luaharfbuzz/.busted @@ -0,0 +1,13 @@ +return { + _all = { + coverage = true, + verbose = true, + ROOT = { "spec" } + }, + default = { + ["exclude-tags"] = "mac" + }, + mac = { + tags = "mac" + } +} diff --git a/luaharfbuzz/.gitignore b/luaharfbuzz/.gitignore new file mode 100644 index 0000000..b2dffea --- /dev/null +++ b/luaharfbuzz/.gitignore @@ -0,0 +1,11 @@ +*.la +*.lo +*.o +*.so +.libs/* +build/* +luacov.report.out +luacov.stats.out +.luacheckcache +api +*.src.rock diff --git a/luaharfbuzz/.luacheckrc b/luaharfbuzz/.luacheckrc new file mode 100644 index 0000000..ced6b23 --- /dev/null +++ b/luaharfbuzz/.luacheckrc @@ -0,0 +1,13 @@ +cache = true +files["spec/"].std = "+busted" +files["examples/luatex/"].globals = { + "texio", + "fontloader", + "callback", + "tex", + "node", + "read_font", + "unicode", + "font" +} + diff --git a/luaharfbuzz/LICENSE b/luaharfbuzz/LICENSE new file mode 100644 index 0000000..157ffe5 --- /dev/null +++ b/luaharfbuzz/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015-16 Deepak Jois + +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. + diff --git a/luaharfbuzz/Makefile b/luaharfbuzz/Makefile new file mode 100644 index 0000000..71d1fd2 --- /dev/null +++ b/luaharfbuzz/Makefile @@ -0,0 +1,15 @@ +DOCS_DIR := docs + +spec: + busted . + +clean: + rm -rf build *.so + +lint: + luacheck src spec examples + +doc: + ldoc -d ${DOCS_DIR} . + +.PHONY: clean lint spec doc diff --git a/luaharfbuzz/README.md b/luaharfbuzz/README.md new file mode 100644 index 0000000..c5cee58 --- /dev/null +++ b/luaharfbuzz/README.md @@ -0,0 +1,165 @@ +# _luaharfbuzz_ + +Lua bindings for [Harfbuzz]. + +[Harfbuzz]:http://harfbuzz.org + +## Contents + +* [Overview](#overview) +* [Installing Harfbuzz](#installing-harfbuzz) +* [Installing _luaharfbuzz_](#installing-luaharfbuzz) +* [Documentation](#documentation) +* [Sample Code](#sample-code) +* [Development](#development) +* [Contact](#contact) + +## Overview +HarfBuzz is an OpenType text shaping engine. It is used in software like Qt, +Pango, Firefox, Chromium, XeTeX and LibreOffice. + +_luaharfbuzz_ provides bindings for the most common types in Harfbuzz. The +initial motivation for building it was to use Harfbuzz with the [LuaTeX] +typesetting system. However, the module isn’t tied to LuaTeX in any way. It +can be used with any Lua codebase. + +luaharfbuzz is currently being used inside [HarfTeX], a TeX engine built +on top of LuaTeX and designed to use Harfbuzz for shaping text. For more +details read this TUGboat journal article titled [Bringing world scripts to LuaTeX: The +HarfBuzz experiment][tugboat-article] + +[HarfTeX]: https://github.com/khaledhosny/harftex +[tugboat-article]: https://tug.org/TUGboat/tb40-1/tb124hosny-harfbuzz.pdf +[LuaTeX]:luatex.org + +## Installing Harfbuzz + +Make sure [Harfbuzz] libraries and headers are installed. before trying to +install _luaharfbuzz_ + +#### OS X + +Install via [Homebrew](http://brew.sh/) + +``` +brew install harfbuzz +``` +#### Ubuntu Linux + +``` +apt-get install libharfbuzz0b libharfbuzz-dev +``` + +#### Other Platforms +_Send a pull request if you want to include specific instructions to install +Harfbuzz on your preferred platform._ + +Before building the package, LuaRocks populates the `HARFBUZZ_INCDIR` and `HARFBUZZ_LIBDIR` to point to the correct locations. If you can populate these variables manually before running LuaRocks, you can install _luaharfbuzz_ on any system that supports Lua and Harfbuzz. + +## Installing _luaharfbuzz_ + +#### Luarocks +If [Luarocks] and Harfbuzz are installed, _luaharfbuzz_ can be installed like this: + +``` +luarocks install luaharfbuzz +``` + +[Luarocks]: https://luarocks.org + +## Documentation +* [API Docs](http://ufytex.github.io/luaharfbuzz/) +* [Wiki](http://github.com/ufytex/luaharfbuzz/wiki) + +## Sample Code + +Here is some sample code, showcasing the core types and methods in the API. + +```lua +local harfbuzz = require('harfbuzz') +local serpent = require('serpent') -- luarocks install serpent + +-- Harfbuzz API Version +print("Harfbuzz API version", harfbuzz.version()) + +-- Shapers available +print("Shapers:", serpent.line({ harfbuzz.shapers() }, {comment = false})) + +-- harfbuzz.Face +local face = harfbuzz.Face.new('../fonts/notonastaliq.ttf') +print('\nFace upem = '..face:get_upem()) + +-- harfbuzz.Font +local font = harfbuzz.Font.new(face) +local xs, xy = font:get_scale() +print("\nDefault font scale = X: "..xs..", Y: "..xy) + +-- harfbuzz.Buffer +local text = "یہ" -- U+06CC U+06C1 +local buf = harfbuzz.Buffer.new() +buf:add_utf8(text) + +-- harfbuzz.shape (Shapes text) +print("\nShaping '"..text.."' set with Noto Nastaliq Urdu") +harfbuzz.shape(font, buf, { language = harfbuzz.Language.new("urd"), script = harfbuzz.Script.new("Arab"), direction = harfbuzz.Direction.RTL}) + +local glyphs = buf:get_glyphs() +print("No. of glyphs", #glyphs) +print(serpent.line(glyphs, {comment = false})) + +local opts = { language = harfbuzz.Language.new("eng"), script = harfbuzz.Script.new("Latn"), direction = harfbuzz.Direction.LTR } +local amiri_face = harfbuzz.Face.new('../fonts/amiri-regular.ttf') +local amiri_font = harfbuzz.Font.new(amiri_face) + +-- shaping '123' w/o features +print("\nShaping '123' set with Amiri Regular and no features") +buf= harfbuzz.Buffer.new() +buf:add_utf8("123") +harfbuzz.shape(amiri_font, buf, opts) +glyphs = buf:get_glyphs() +print(serpent.line(glyphs, {comment = false})) + +-- shaping '123' with '+numr' (numerators) +print("\nShaping '123' set with Amiri Regular with 'numr' feature turned on") +buf= harfbuzz.Buffer.new() +buf:add_utf8("123") +opts.features = "+numr" +harfbuzz.shape(amiri_font, buf, opts) +glyphs = buf:get_glyphs() +print(serpent.line(glyphs, {comment = false})) +``` + +## Development + +#### Building +You can build the package for development purposes using LuaRocks as well. It is recommended that you build it to your local tree (using `--local`) to isolate it from your actual installation. + +``` +luarocks --local make +``` + +#### Testing and Linting +In order to make changes to the code and run the tests, the following dependencies need to be installed: + +* [Busted](http://olivinelabs.com/busted/) – `luarocks install busted` +* [luacheck](luacheck.readthedocs.org) – `luarocks install luacheck` +* [luacov](https://keplerproject.github.io/luacov/) – `luarocks install luacov` +* [ldoc](https://stevedonovan.github.io/ldoc/) – `luarocks install ldoc` + +Run the test suite: +``` +make spec +``` + +Lint the codebase: +``` +make lint +``` + +Generate documentation from sources: +``` +make doc +``` + +## Contact +Open a Github issue, or email me at . diff --git a/luaharfbuzz/THANKS b/luaharfbuzz/THANKS new file mode 100644 index 0000000..1d99682 --- /dev/null +++ b/luaharfbuzz/THANKS @@ -0,0 +1,2 @@ +Simon Cozens +Michal Hoftich diff --git a/luaharfbuzz/config.ld b/luaharfbuzz/config.ld new file mode 100644 index 0000000..d37be65 --- /dev/null +++ b/luaharfbuzz/config.ld @@ -0,0 +1,9 @@ +project = 'luaharfbuzz' +description = 'Lua bindings to Harfbuzz' +full_description = 'The documentation is available on the @{https://github.com/ufytex/luaharfbuzz/wiki|wiki}' +use_markdown_titles = true +backtick_references = false +title = 'luaharfbuzz Documentation' +file = 'src/harfbuzz.luadoc' +examples = 'examples' +format = 'markdown' diff --git a/luaharfbuzz/docs/examples/core_types.lua.html b/luaharfbuzz/docs/examples/core_types.lua.html new file mode 100644 index 0000000..6345ea3 --- /dev/null +++ b/luaharfbuzz/docs/examples/core_types.lua.html @@ -0,0 +1,108 @@ + + + + + luaharfbuzz Documentation + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

core_types.lua

+
+local harfbuzz = require('harfbuzz')
+local serpent  = require('serpent') -- luarocks install serpent
+
+-- Harfbuzz API Version
+print("Harfbuzz API version", harfbuzz.version())
+
+-- Shapers available
+print("Shapers:", serpent.line({ harfbuzz.shapers() }, {comment = false}))
+
+-- harfbuzz.Face
+local face = harfbuzz.Face.new('../fonts/notonastaliq.ttf')
+print('\nFace upem = '..face:get_upem())
+
+-- harfbuzz.Font
+local font = harfbuzz.Font.new(face)
+local xs, xy = font:get_scale()
+print("\nDefault font scale = X: "..xs..", Y: "..xy)
+
+-- harfbuzz.Buffer
+local text = "یہ" -- U+06CC U+06C1
+local buf = harfbuzz.Buffer.new()
+buf:add_utf8(text)
+
+-- harfbuzz.shape (Shapes text)
+print("\nShaping '"..text.."' set with Noto Nastaliq Urdu")
+harfbuzz.shape(font, buf, { language = harfbuzz.Language.new("urd"), script = harfbuzz.Script.new("Arab"), direction = harfbuzz.Direction.RTL})
+
+local glyphs = buf:get_glyphs()
+print("No. of glyphs", #glyphs)
+print(serpent.line(glyphs, {comment = false}))
+
+local opts = { language = harfbuzz.Language.new("eng"), script = harfbuzz.Script.new("Latn"), direction = harfbuzz.Direction.LTR }
+local amiri_face = harfbuzz.Face.new('../fonts/amiri-regular.ttf')
+local amiri_font = harfbuzz.Font.new(amiri_face)
+
+-- shaping '123' w/o features
+print("\nShaping '123' set with Amiri Regular and no features")
+buf= harfbuzz.Buffer.new()
+buf:add_utf8("123")
+harfbuzz.shape(amiri_font, buf, opts)
+glyphs = buf:get_glyphs()
+print(serpent.line(glyphs, {comment = false}))
+
+-- shaping '123' with '+numr' (numerators)
+print("\nShaping '123' set with Amiri Regular with 'numr' feature turned on")
+buf= harfbuzz.Buffer.new()
+buf:add_utf8("123")
+opts.features = "+numr"
+harfbuzz.shape(amiri_font, buf, opts)
+glyphs = buf:get_glyphs()
+print(serpent.line(glyphs, {comment = false}))
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2019-07-31 18:39:39 +
+
+ + diff --git a/luaharfbuzz/docs/index.html b/luaharfbuzz/docs/index.html new file mode 100644 index 0000000..d1aaf74 --- /dev/null +++ b/luaharfbuzz/docs/index.html @@ -0,0 +1,2829 @@ + + + + + luaharfbuzz Documentation + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module harfbuzz

+

Lua bindings to Harfbuzz.

+

+ +

+

+

Info:

+ + + +

Functions

+ + + + + + + + + +
version ()Wraps hb_version
shape (font, buffer[, options])Wraps hb_shape.
+

Class Blob

+ + + + + + + + + + + + + + + + + +
Blob.new (data)Wraps hb_blob_create.
Blob.new_from_file (filename)Wraps hb_blob_create_from_file.
Blob:get_length ()Wraps hb_blob_get_length.
Blob:get_data ()Wraps hb_blob_get_data.
+

Class Face

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Face.new_from_blob (blob[, font_index=0])Wraps hb_face_create.
Face.new (file[, font_index=0])Create a new Face from a file.
Face:collect_unicodes ()Wraps hb_face_collect_unicodes.
Face:get_glyph_count ()Wraps hb_face_get_glyph_count.
Face:get_table (tag)Wraps hb_face_reference_table.
Face:get_table_tags ()Wraps hb_face_get_table_tags.
Face:get_upem ()Wraps hb_face_get_upem.
Face:ot_color_has_palettes ()Wraps hb_ot_color_has_palettes.
Face:ot_color_palette_get_count ()Wraps hb_ot_color_palette_get_count.
Face:ot_color_palette_get_colors ()Wraps hb_ot_color_palette_get_colors.
Face:ot_color_has_layers ()Wraps hb_ot_color_has_layers.
Face:ot_color_glyph_get_layers ()Wraps hb_ot_color_glyph_get_layers.
Face:ot_color_has_png ()Wraps hb_ot_color_has_png.
Face:ot_layout_get_script_tags ()Wraps hb_ot_layout_table_get_script_tags.
Face:ot_layout_get_language_tags ()Wraps hb_ot_layout_script_get_language_tags.
Face:ot_layout_get_feature_tags ()Wraps hb_ot_layout_language_get_feature_tags.
Face:ot_layout_find_script ()Wraps hb_ot_layout_table_find_script.
Face:ot_layout_find_language ()Wraps hb_ot_layout_script_find_language.
Face:ot_layout_find_feature ()Wraps hb_ot_layout_language_find_feature.
+

Class Font

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Font.new (face)Wraps hb_font_create, and sets up some defaults for scale and shaping functions.
Font:get_scale ()Wraps hb_font_get_scale.
Font:set_scale (x_scale, y_scale)Wraps hb_font_set_scale.
Font:get_h_extents ()Wraps hb_font_get_h_extents.
Font:get_v_extents ()Wraps hb_font_get_v_extents.
Font:get_glyph_extents (glyph)Wraps hb_font_get_glyph_extents.
Font:get_glyph_name (glyph)Wraps hb_font_get_glyph_name.
Font:get_glyph_from_name (name)Wraps hb_font_get_glyph_from_name.
Font:get_glyph_h_advance (glyph)Wraps hb_font_get_glyph_h_advance.
Font:get_glyph_v_advance (glyph)Wraps hb_font_get_glyph_v_advance.
Font:get_nominal_glyph (codepoint.)Wraps hb_font_get_nominal_glyph.
Font:ot_color_glyph_get_png ()Wraps hb_ot_color_glyph_get_png.
+

Class Buffer

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Buffer.new ()Wraps hb_buffer_create.
Buffer:add_utf8 (text[, item_offset=0[, item_length=-1]])Wraps hb_buffer_add_utf8.
Buffer:add_codepoints (text[, item_offset=0[, item_length=-1]])Wraps hb_buffer_add_codepoints.
Buffer:set_direction (dir)Wraps hb_buffer_set_direction.
Buffer:get_direction ()Wraps hb_buffer_get_direction.
Buffer:set_script (script)Wraps hb_buffer_set_script.
Buffer:get_script ()Wraps hb_buffer_get_script.
Buffer:set_language (lang)Wraps hb_buffer_set_language.
Buffer:get_language ()Wraps hb_buffer_get_language.
Buffer:reverse ()Wraps hb_buffer_reverse.
Buffer:get_length ()Wraps hb_buffer_get_length.
Buffer:get_cluster_level ()Wraps hb_buffer_get_cluster_level.
Buffer:set_cluster_level (level)Wraps hb_buffer_set_cluster_level.
Buffer:guess_segment_properties ()Wraps hb_buffer_guess_segment_properties.
Buffer:get_glyphs ()Helper method to get shaped glyph data.
+

Cluster Levels

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Buffer.CLUSTER_LEVEL_MONOTONE_GRAPHEMESWraps HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES.
Buffer.CLUSTER_LEVEL_MONOTONE_CHARACTERSWraps HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS.
Buffer.CLUSTER_LEVEL_CHARACTERSWraps HB_BUFFER_CLUSTER_LEVEL_CHARACTERS.
Buffer.CLUSTER_LEVEL_DEFAULTWraps HB_BUFFER_CLUSTER_LEVEL_DEFAULT.
Buffer.GLYPH_FLAG_UNSAFE_TO_BREAKWraps HB_GLYPH_FLAG_UNSAFE_TO_BREAK.
Buffer.GLYPH_FLAG_DEFINEDWraps HB_GLYPH_FLAG_DEFINED.
+

Class Feature

+ + + + + + + + + +
Feature.new (feature_string)Wraps hb_feature_from_string
Feature:__tostring ()Wraps hb_feature_to_string.
+

Class Tag

+ + + + + + + + + + + + + +
Tag.new (string)Wraps hb_tag_from_string.
Tag:__to_string ()Wraps hb_tag_to_string.
Tag:__eq ()Enables equality comparisions with == between two tags.
+

Class Script

+ + + + + + + + + + + + + + + + + + + + + +
Script.new (script)Wraps hb_script_from_string.
Script.from_iso15924_tag (tag)Wraps hb_script_from_iso15924_tag
Script:to_iso15924_tag ()Wraps hb_script_to_iso15924_tag.
Script:__to_string ()Enable nice output with tostring(…)
Script:__eq ()Enables equality comparisions with == between two scripts.
+

Predefined Script Codes

+ + + + + + + + + + + + + + + + + +
Script.COMMONWraps HB_SCRIPT_COMMON.
Script.INHERITEDWraps HB_SCRIPT_INHERITED.
Script.UNKNOWNWraps HB_SCRIPT_UNKNOWN.
Script.INVALIDWraps HB_SCRIPT_INVALID.
+

Class Direction

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Direction.new (dir)Wraps hb_direction_from_string.
Direction:__to_string ()Wraps hb_direction_to_string.
Direction:__eq ()Enables equality comparisions with == between two directions.
Direction:is_valid ()Wraps HB_DIRECTION_IS_VALID.
Direction:is_horizontal ()Wraps HB_DIRECTION_IS_HORIZONTAL.
Direction:is_vertical ()Wraps HB_DIRECTION_IS_VERTICAL.
Direction:is_forward ()Wraps HB_DIRECTION_IS_FORWARD.
Direction:is_backward ()Wraps HB_DIRECTION_IS_BACKWARD.
+

Predefined directions

+ + + + + + + + + + + + + + + + + +
Direction.LTRWraps HB_DIRECTION_LTR.
Direction.RTLWraps HB_DIRECTION_RTL.
Direction.TTBWraps HB_DIRECTION_TTB.
Direction.BTTWraps HB_DIRECTION_LTR.
+

Class Language

+ + + + + + + + + + + + + +
Language.new (lang)Wraps hb_language_from_string.
Language:__to_string ()Wraps hb_language_to_string.
Language:__eq ()Enables equality comparisions with == between two languages.
+

Predefined languages

+ + + + + +
Language.INVALIDWraps HB_LANGUAGE_INVALID.
+

Unicode functions

+ + + + + +
unicode.script (char)Wraps hb_unicode_script
+

Predefined Name IDs

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ot.NAME_ID_COPYRIGHTWraps HB_OT_NAME_ID_COPYRIGHT
ot.NAME_ID_FONT_FAMILYWraps HB_OT_NAME_ID_FONT_FAMILY
ot.NAME_ID_FONT_SUBFAMILYWraps HB_OT_NAME_ID_FONT_SUBFAMILY
ot.NAME_ID_UNIQUE_IDWraps HB_OT_NAME_ID_UNIQUE_ID
ot.NAME_ID_FULL_NAMEWraps HB_OT_NAME_ID_FULL_NAME
ot.NAME_ID_VERSION_STRINGWraps HB_OT_NAME_ID_VERSION_STRING
ot.NAME_ID_POSTSCRIPT_NAMEWraps HB_OT_NAME_ID_POSTSCRIPT_NAME
ot.NAME_ID_TRADEMARKWraps HB_OT_NAME_ID_TRADEMARK
ot.NAME_ID_MANUFACTURERWraps HB_OT_NAME_ID_MANUFACTURER
ot.NAME_ID_DESIGNERWraps HB_OT_NAME_ID_DESIGNER
ot.NAME_ID_DESCRIPTIONWraps HB_OT_NAME_ID_DESCRIPTION
ot.NAME_ID_VENDOR_URLWraps HB_OT_NAME_ID_VENDOR_URL
ot.NAME_ID_DESIGNER_URLWraps HB_OT_NAME_ID_DESIGNER_URL
ot.NAME_ID_LICENSEWraps HB_OT_NAME_ID_LICENSE
ot.NAME_ID_LICENSE_URLWraps HB_OT_NAME_ID_LICENSE_URL
ot.NAME_ID_TYPOGRAPHIC_FAMILYWraps HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY
ot.NAME_ID_TYPOGRAPHIC_SUBFAMILYWraps HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY
ot.NAME_ID_MAC_FULL_NAMEWraps HB_OT_NAME_ID_MAC_FULL_NAME
ot.NAME_ID_SAMPLE_TEXTWraps HB_OT_NAME_ID_SAMPLE_TEXT
ot.NAME_ID_CID_FINDFONT_NAMEWraps HB_OT_NAME_ID_CID_FINDFONT_NAME
ot.NAME_ID_WWS_FAMILYWraps HB_OT_NAME_ID_WWS_FAMILY
ot.NAME_ID_WWS_SUBFAMILYWraps HB_OT_NAME_ID_WWS_SUBFAMILY
ot.NAME_ID_LIGHT_BACKGROUNDWraps HB_OT_NAME_ID_LIGHT_BACKGROUND
ot.NAME_ID_DARK_BACKGROUNDWraps HB_OT_NAME_ID_DARK_BACKGROUND
ot.NAME_ID_VARIATIONS_PS_PREFIXWraps HB_OT_NAME_ID_VARIATIONS_PS_PREFIX
ot.NAME_ID_INVALIDWraps HB_OT_NAME_ID_INVALID
ot.LAYOUT_NO_SCRIPT_INDEXWraps HB_OT_LAYOUT_NO_SCRIPT_INDEX
ot.LAYOUT_NO_FEATURE_INDEXWraps HB_OT_LAYOUT_NO_FEATURE_INDEX
ot.LAYOUT_DEFAULT_LANGUAGE_INDEXWraps HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX
ot.LAYOUT_NO_VARIATIONS_INDEXWraps HB_OT_LAYOUT_NO_VARIATIONS_INDEX
+ +
+
+ + +

Functions

+ +
+
+ + version () +
+
+ Wraps hb_version + + + + + + + +
+
+ + shape (font, buffer[, options]) +
+
+ Wraps hb_shape. + + +

Parameters:

+
    +
  • font + Font to use for shaping +
  • +
  • buffer + Buffer to shape +
  • +
  • options + +

    table containing one or more supported options:

    + +
      +
    • direction: A Direction object representing the object.
    • +
    • script: A Script object representing the script.
    • +
    • language: A Language object representing the language.
    • +
    • features: features to enable, specified as either of the following. +
    • +
    + + (optional) +
  • +
+ + + + + +
+
+

Class Blob

+ +
+ Lua wrapper for hb_blob_t type +
+
+
+ + Blob.new (data) +
+
+ Wraps hb_blob_create. + Initializes a new hb_blob_t. + + +

Parameters:

+
    +
  • data + lua string containing binary or character data. +
  • +
+ + + + + +
+
+ + Blob.new_from_file (filename) +
+
+ Wraps hb_blob_create_from_file. + Initializes a new hb_blob_t. + + +

Parameters:

+
    +
  • filename + lua string. +
  • +
+ + + + + +
+
+ + Blob:get_length () +
+
+ Wraps hb_blob_get_length. + + + + + + + +
+
+ + Blob:get_data () +
+
+ Wraps hb_blob_get_data. + + + + + + + +
+
+

Class Face

+ +
+ Lua wrapper for hb_face_t type +
+
+
+ + Face.new_from_blob (blob[, font_index=0]) +
+
+ Wraps hb_face_create. + Initializes a new hb_face_t from a Blob object. + + +

Parameters:

+
    +
  • blob + Blob to read the font from. +
  • +
  • font_index + index of font to read. + (default 0) +
  • +
+ + + + + +
+
+ + Face.new (file[, font_index=0]) +
+
+ Create a new Face from a file. + Makes a call to Face:new_from_blob after creating a Blob from the + file contents. + + +

Parameters:

+
    +
  • file + path to font file. +
  • +
  • font_index + index of font to read. + (default 0) +
  • +
+ + + + + +
+
+ + Face:collect_unicodes () +
+
+ Wraps hb_face_collect_unicodes. + + + +

Returns:

+
    + + table of codepoints supported by the face. +
+ + + + +
+
+ + Face:get_glyph_count () +
+
+ Wraps hb_face_get_glyph_count. + + + + + + + +
+
+ + Face:get_table (tag) +
+
+ Wraps hb_face_reference_table. + + +

Parameters:

+
    +
  • tag + Tag object of the table. +
  • +
+ +

Returns:

+
    + + Blob object for the face table of tag. +
+ + + + +
+
+ + Face:get_table_tags () +
+
+ Wraps hb_face_get_table_tags. + + + +

Returns:

+
    + + table of Tags representing face table tags. +
+ + + + +
+
+ + Face:get_upem () +
+
+ Wraps hb_face_get_upem. + + + + + + + +
+
+ + Face:ot_color_has_palettes () +
+
+ Wraps hb_ot_color_has_palettes. + + + + + + + +
+
+ + Face:ot_color_palette_get_count () +
+
+ Wraps hb_ot_color_palette_get_count. + + + + + + + +
+
+ + Face:ot_color_palette_get_colors () +
+
+ Wraps hb_ot_color_palette_get_colors. + + + + + + + +
+
+ + Face:ot_color_has_layers () +
+
+ Wraps hb_ot_color_has_layers. + + + + + + + +
+
+ + Face:ot_color_glyph_get_layers () +
+
+ Wraps hb_ot_color_glyph_get_layers. + + + + + + + +
+
+ + Face:ot_color_has_png () +
+
+ Wraps hb_ot_color_has_png. + + + + + + + +
+
+ + Face:ot_layout_get_script_tags () +
+
+ Wraps hb_ot_layout_table_get_script_tags. + + + + + + + +
+
+ + Face:ot_layout_get_language_tags () +
+
+ Wraps hb_ot_layout_script_get_language_tags. + + + + + + + +
+
+ + Face:ot_layout_get_feature_tags () +
+
+ Wraps hb_ot_layout_language_get_feature_tags. + + + + + + + +
+
+ + Face:ot_layout_find_script () +
+
+ Wraps hb_ot_layout_table_find_script. + + + + + + + +
+
+ + Face:ot_layout_find_language () +
+
+ Wraps hb_ot_layout_script_find_language. + + + + + + + +
+
+ + Face:ot_layout_find_feature () +
+
+ Wraps hb_ot_layout_language_find_feature. + + + + + + + +
+
+

Class Font

+ +
+ Lua wrapper for hb_font_t type +
+
+
+ + Font.new (face) +
+
+ Wraps hb_font_create, and sets up some defaults for scale and shaping functions. + Initializes a new hb_font_t from a Face object. Sets the default scale + to the face’s upem value, and sets the font shaping functions by + calling hb_ot_font_set_funcs on it. + + +

Parameters:

+
    +
  • face + Face object. +
  • +
+ + + + + +
+
+ + Font:get_scale () +
+
+ Wraps hb_font_get_scale. + + + +

Returns:

+
    + + two values for the x-scale and y-scale of the font. +
+ + + + +
+
+ + Font:set_scale (x_scale, y_scale) +
+
+ Wraps hb_font_set_scale. + + +

Parameters:

+
    +
  • x_scale + desired x-scale of font. +
  • +
  • y_scale + desired y-scale of font. +
  • +
+ + + + + +
+
+ + Font:get_h_extents () +
+
+ Wraps hb_font_get_h_extents. + + + +

Returns:

+
    + + +

    font extents table for horizontal direction, contains the following + or nil if HarfBuzz fails to load font extents:

    + +
      +
    • ascender: typographic ascender.
    • +
    • descender: typographic descender.
    • +
    • line_gap: line spacing gap.
    • +
    + +
+ + + + +
+
+ + Font:get_v_extents () +
+
+ Wraps hb_font_get_v_extents. + + + +

Returns:

+
    + + font extents table for vertical direction, similar to + Font:get_h_extents, or nil if HarfBuzz fails to load font extents: +
+ + + + +
+
+ + Font:get_glyph_extents (glyph) +
+
+ Wraps hb_font_get_glyph_extents. + + +

Parameters:

+
    +
  • glyph + index inside the font. +
  • +
+ +

Returns:

+
    + + +

    extents table contains the following or nil if HarfBuzz fails to + load glyph extents:

    + +
      +
    • x_bearing: left side of glyph from origin.
    • +
    • y_bearing: top side of glyph from origin.
    • +
    • width: distance from left to right side.
    • +
    • height: distance from top to bottom side.
    • +
    + +
+ + + + +
+
+ + Font:get_glyph_name (glyph) +
+
+ Wraps hb_font_get_glyph_name. + + +

Parameters:

+
    +
  • glyph + index inside the font. +
  • +
+ +

Returns:

+
    + + name of the glyph or nil. +
+ + + + +
+
+ + Font:get_glyph_from_name (name) +
+
+ Wraps hb_font_get_glyph_from_name. + + +

Parameters:

+
    +
  • name + of the glyph. +
  • +
+ +

Returns:

+
    + + glyph index inside the font or nil. +
+ + + + +
+
+ + Font:get_glyph_h_advance (glyph) +
+
+ Wraps hb_font_get_glyph_h_advance. + + +

Parameters:

+
    +
  • glyph + index inside the font. +
  • +
+ +

Returns:

+
    + + advance glyph advance of the glyph in horizontal direction. +
+ + + + +
+
+ + Font:get_glyph_v_advance (glyph) +
+
+ Wraps hb_font_get_glyph_v_advance. + + +

Parameters:

+
    +
  • glyph + index inside the font. +
  • +
+ +

Returns:

+
    + + advance glyph advance of the glyph in vertical direction. +
+ + + + +
+
+ + Font:get_nominal_glyph (codepoint.) +
+
+ Wraps hb_font_get_nominal_glyph. + + +

Parameters:

+
    +
  • codepoint. + + + +
  • +
+ +

Returns:

+
    + + glyph index or nil if codepoint is not supported by the font. +
+ + + + +
+
+ + Font:ot_color_glyph_get_png () +
+
+ Wraps hb_ot_color_glyph_get_png. + + + + + + + +
+
+

Class Buffer

+ +
+ Lua wrapper for hb_buffer_t type. +
+
+
+ + Buffer.new () +
+
+ Wraps hb_buffer_create. + + + + + + + +
+
+ + Buffer:add_utf8 (text[, item_offset=0[, item_length=-1]]) +
+
+ Wraps hb_buffer_add_utf8. + + +

Parameters:

+
    +
  • text + UTF8 encoded string. +
  • +
  • item_offset + 0-indexed offset in text, from where to start adding. + (default 0) +
  • +
  • item_length + length to add from item_offset. -1 adds till end of text. + (default -1) +
  • +
+ + + + + +
+
+ + Buffer:add_codepoints (text[, item_offset=0[, item_length=-1]]) +
+
+ Wraps hb_buffer_add_codepoints. + + +

Parameters:

+
    +
  • text + table with codepoints as lua numbers. +
  • +
  • item_offset + 0-indexed offset in text, from where to start adding. + (default 0) +
  • +
  • item_length + length to add from item_offset. -1 adds till end of text. + (default -1) +
  • +
+ + + + + +
+
+ + Buffer:set_direction (dir) +
+
+ Wraps hb_buffer_set_direction. + + +

Parameters:

+
    +
  • dir + A Direction object. +
  • +
+ + + + + +
+
+ + Buffer:get_direction () +
+
+ Wraps hb_buffer_get_direction. + + + +

Returns:

+
    + + A Direction object. +
+ + + + +
+
+ + Buffer:set_script (script) +
+
+ Wraps hb_buffer_set_script. + + +

Parameters:

+
    +
  • script + A Script object. +
  • +
+ + + + + +
+
+ + Buffer:get_script () +
+
+ Wraps hb_buffer_get_script. + + + +

Returns:

+
    + + A Script object. +
+ + + + +
+
+ + Buffer:set_language (lang) +
+
+ Wraps hb_buffer_set_language. + + +

Parameters:

+
    +
  • lang + A Language object +
  • +
+ + + + + +
+
+ + Buffer:get_language () +
+
+ Wraps hb_buffer_get_language. + + + +

Returns:

+
    + + A Language object +
+ + + + +
+
+ + Buffer:reverse () +
+
+ Wraps hb_buffer_reverse. + + + + + + + +
+
+ + Buffer:get_length () +
+
+ Wraps hb_buffer_get_length. + + + + + + + +
+
+ + Buffer:get_cluster_level () +
+
+ Wraps hb_buffer_get_cluster_level. + + + +

Returns:

+
    + + see Cluster Levels +
+ + + + +
+
+ + Buffer:set_cluster_level (level) +
+
+ Wraps hb_buffer_set_cluster_level. + + +

Parameters:

+ + + + + + +
+
+ + Buffer:guess_segment_properties () +
+
+ Wraps hb_buffer_guess_segment_properties. + + + + + + + +
+
+ + Buffer:get_glyphs () +
+
+ Helper method to get shaped glyph data. + Calls hb_buffer_get_glyph_infos, hb_buffer_get_glyph_positions and + hb_glyph_info_get_glyph_flags, and assembles the data into a Lua table. + + + +

Returns:

+
    + + +

    table containing data for each glyph, in a nested table. Each nested + table contains the following:

    + +
      +
    • x_advance: horizontal advance.
    • +
    • y_advance: vertical advance.
    • +
    • x_offset: horizontal displacement.
    • +
    • y_offset: vertical displacement.
    • +
    • cluster: glyph cluster index within input.
    • +
    • codepoint: glyph index inside the font (this field name is a bit misleading, but that’s what Harfbuzz uses).
    • +
    • flags: glyph flags
    • +
    + +
+ + + + +
+
+

Cluster Levels

+ +
+ See Harfbuzz docs for more details + about what each of these levels mean. +
+
+
+ + Buffer.CLUSTER_LEVEL_MONOTONE_GRAPHEMES +
+
+ Wraps HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES. + + + + + + + +
+
+ + Buffer.CLUSTER_LEVEL_MONOTONE_CHARACTERS +
+
+ Wraps HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS. + + + + + + + +
+
+ + Buffer.CLUSTER_LEVEL_CHARACTERS +
+
+ Wraps HB_BUFFER_CLUSTER_LEVEL_CHARACTERS. + + + + + + + +
+
+ + Buffer.CLUSTER_LEVEL_DEFAULT +
+
+ Wraps HB_BUFFER_CLUSTER_LEVEL_DEFAULT. + + + + + + + +
+
+ + Buffer.GLYPH_FLAG_UNSAFE_TO_BREAK +
+
+ Wraps HB_GLYPH_FLAG_UNSAFE_TO_BREAK. + + + + + + + +
+
+ + Buffer.GLYPH_FLAG_DEFINED +
+
+ Wraps HB_GLYPH_FLAG_DEFINED. + + + + + + + +
+
+

Class Feature

+ +
+ Lua wrapper for hb_feature_t type +
+
+
+ + Feature.new (feature_string) +
+
+ Wraps hb_feature_from_string + + +

Parameters:

+ + + + + + +
+
+ + Feature:__tostring () +
+
+ Wraps hb_feature_to_string. + Enables nice output with tostring(…). + + + + + + + +
+
+

Class Tag

+ +
+ Lua wrapper for hb_tag_t type. +
+
+
+ + Tag.new (string) +
+
+ Wraps hb_tag_from_string. + + +

Parameters:

+
    +
  • string + to be converted to a Tag object. +
  • +
+ +

Returns:

+
    + + a Tag object. +
+ + + + +
+
+ + Tag:__to_string () +
+
+ Wraps hb_tag_to_string. Enable nice output with tostring(…). + + + +

Returns:

+
    + + Returns a string representation for the tag object. +
+ + + + +
+
+ + Tag:__eq () +
+
+ Enables equality comparisions with == between two tags. + + + +

Returns:

+
    + + true or false depending on whether the two tags are equal. +
+ + + + +
+
+

Class Script

+ +
+ Lua wrapper for hb_script_t type. +
+
+
+ + Script.new (script) +
+
+ Wraps hb_script_from_string. + + +

Parameters:

+ + +

Returns:

+
    + + a Script object. +
+ + + + +
+
+ + Script.from_iso15924_tag (tag) +
+
+ Wraps hb_script_from_iso15924_tag + + +

Parameters:

+ + + + + + +
+
+ + Script:to_iso15924_tag () +
+
+ Wraps hb_script_to_iso15924_tag. + + + +

Returns:

+
    + + a Tag object representing the script. +
+ + + + +
+
+ + Script:__to_string () +
+
+ Enable nice output with tostring(…) + + + +

Returns:

+
    + + Returns a 4-letter ISO 15924 script code for the script object. +
+ + + + +
+
+ + Script:__eq () +
+
+ Enables equality comparisions with == between two scripts. + + + +

Returns:

+
    + + true or false depending on whether the two scripts are equal. +
+ + + + +
+
+

Predefined Script Codes

+ +
+ Predefined directions that correspond to their original definitions in Harfbuzz. +
+
+
+ + Script.COMMON +
+
+ Wraps HB_SCRIPT_COMMON. + + + + + + + +
+
+ + Script.INHERITED +
+
+ Wraps HB_SCRIPT_INHERITED. + + + + + + + +
+
+ + Script.UNKNOWN +
+
+ Wraps HB_SCRIPT_UNKNOWN. + + + + + + + +
+
+ + Script.INVALID +
+
+ Wraps HB_SCRIPT_INVALID. + + + + + + + +
+
+

Class Direction

+ +
+ Lua wrapper for hb_direction_t type. +
+
+
+ + Direction.new (dir) +
+
+ Wraps hb_direction_from_string. + + +

Parameters:

+
    +
  • dir + can be one of ltr, rtl, ttb, btt or invalid. +
  • +
+ +

Returns:

+
    + + a Direction object. +
+ + + + +
+
+ + Direction:__to_string () +
+
+ Wraps hb_direction_to_string. Enable nice output with tostring(…). + + + +

Returns:

+
    + + Returns a string representation for direction. +
+ + + + +
+
+ + Direction:__eq () +
+
+ Enables equality comparisions with == between two directions. + + + +

Returns:

+
    + + true or false depending on whether the two tags are equal. +
+ + + + +
+
+ + Direction:is_valid () +
+
+ Wraps HB_DIRECTION_IS_VALID. + + + +

Returns:

+
    + + a boolean value +
+ + + + +
+
+ + Direction:is_horizontal () +
+
+ Wraps HB_DIRECTION_IS_HORIZONTAL. + + + +

Returns:

+
    + + a boolean value +
+ + + + +
+
+ + Direction:is_vertical () +
+
+ Wraps HB_DIRECTION_IS_VERTICAL. + + + +

Returns:

+
    + + a boolean value +
+ + + + +
+
+ + Direction:is_forward () +
+
+ Wraps HB_DIRECTION_IS_FORWARD. + + + +

Returns:

+
    + + a boolean value +
+ + + + +
+
+ + Direction:is_backward () +
+
+ Wraps HB_DIRECTION_IS_BACKWARD. + + + +

Returns:

+
    + + a boolean value +
+ + + + +
+
+

Predefined directions

+ +
+ Predefined directions that correspond to their original definitions in Harfbuzz. +
+
+
+ + Direction.LTR +
+
+ Wraps HB_DIRECTION_LTR. + + + + + + + +
+
+ + Direction.RTL +
+
+ Wraps HB_DIRECTION_RTL. + + + + + + + +
+
+ + Direction.TTB +
+
+ Wraps HB_DIRECTION_TTB. + + + + + + + +
+
+ + Direction.BTT +
+
+ Wraps HB_DIRECTION_LTR. + + + + + + + +
+
+

Class Language

+ +
+ Lua wrapper for hb_language_t type. +
+
+
+ + Language.new (lang) +
+
+ Wraps hb_language_from_string. + + +

Parameters:

+ + +

Returns:

+
    + + a Language object. +
+ + + + +
+
+ + Language:__to_string () +
+
+ Wraps hb_language_to_string. Enable nice output with tostring(…). + + + +

Returns:

+
    + + Returns a string representation for the language object. +
+ + + + +
+
+ + Language:__eq () +
+
+ Enables equality comparisions with == between two languages. + + + +

Returns:

+
    + + true or false depending on whether the two languages are equal. +
+ + + + +
+
+

Predefined languages

+ +
+ Predefined languages that correspond to their original definitions in Harfbuzz. +
+
+
+ + Language.INVALID +
+
+ Wraps HB_LANGUAGE_INVALID. + + + + + + + +
+
+

Unicode functions

+ +
+
+ + unicode.script (char) +
+
+ Wraps hb_unicode_script + + +

Parameters:

+
    +
  • char + Unicode codepoint +
  • +
+ +

Returns:

+
    + + a Script object. +
+ + + + +
+
+

Predefined Name IDs

+ +
+ Predefined OpenType 'name' table name identifier. +
+
+
+ + ot.NAME_ID_COPYRIGHT +
+
+ Wraps HB_OT_NAME_ID_COPYRIGHT + + + + + + + +
+
+ + ot.NAME_ID_FONT_FAMILY +
+
+ Wraps HB_OT_NAME_ID_FONT_FAMILY + + + + + + + +
+
+ + ot.NAME_ID_FONT_SUBFAMILY +
+
+ Wraps HB_OT_NAME_ID_FONT_SUBFAMILY + + + + + + + +
+
+ + ot.NAME_ID_UNIQUE_ID +
+
+ Wraps HB_OT_NAME_ID_UNIQUE_ID + + + + + + + +
+
+ + ot.NAME_ID_FULL_NAME +
+
+ Wraps HB_OT_NAME_ID_FULL_NAME + + + + + + + +
+
+ + ot.NAME_ID_VERSION_STRING +
+
+ Wraps HB_OT_NAME_ID_VERSION_STRING + + + + + + + +
+
+ + ot.NAME_ID_POSTSCRIPT_NAME +
+
+ Wraps HB_OT_NAME_ID_POSTSCRIPT_NAME + + + + + + + +
+
+ + ot.NAME_ID_TRADEMARK +
+
+ Wraps HB_OT_NAME_ID_TRADEMARK + + + + + + + +
+
+ + ot.NAME_ID_MANUFACTURER +
+
+ Wraps HB_OT_NAME_ID_MANUFACTURER + + + + + + + +
+
+ + ot.NAME_ID_DESIGNER +
+
+ Wraps HB_OT_NAME_ID_DESIGNER + + + + + + + +
+
+ + ot.NAME_ID_DESCRIPTION +
+
+ Wraps HB_OT_NAME_ID_DESCRIPTION + + + + + + + +
+
+ + ot.NAME_ID_VENDOR_URL +
+
+ Wraps HB_OT_NAME_ID_VENDOR_URL + + + + + + + +
+
+ + ot.NAME_ID_DESIGNER_URL +
+
+ Wraps HB_OT_NAME_ID_DESIGNER_URL + + + + + + + +
+
+ + ot.NAME_ID_LICENSE +
+
+ Wraps HB_OT_NAME_ID_LICENSE + + + + + + + +
+
+ + ot.NAME_ID_LICENSE_URL +
+
+ Wraps HB_OT_NAME_ID_LICENSE_URL + + + + + + + +
+
+ + ot.NAME_ID_TYPOGRAPHIC_FAMILY +
+
+ Wraps HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY + + + + + + + +
+
+ + ot.NAME_ID_TYPOGRAPHIC_SUBFAMILY +
+
+ Wraps HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY + + + + + + + +
+
+ + ot.NAME_ID_MAC_FULL_NAME +
+
+ Wraps HB_OT_NAME_ID_MAC_FULL_NAME + + + + + + + +
+
+ + ot.NAME_ID_SAMPLE_TEXT +
+
+ Wraps HB_OT_NAME_ID_SAMPLE_TEXT + + + + + + + +
+
+ + ot.NAME_ID_CID_FINDFONT_NAME +
+
+ Wraps HB_OT_NAME_ID_CID_FINDFONT_NAME + + + + + + + +
+
+ + ot.NAME_ID_WWS_FAMILY +
+
+ Wraps HB_OT_NAME_ID_WWS_FAMILY + + + + + + + +
+
+ + ot.NAME_ID_WWS_SUBFAMILY +
+
+ Wraps HB_OT_NAME_ID_WWS_SUBFAMILY + + + + + + + +
+
+ + ot.NAME_ID_LIGHT_BACKGROUND +
+
+ Wraps HB_OT_NAME_ID_LIGHT_BACKGROUND + + + + + + + +
+
+ + ot.NAME_ID_DARK_BACKGROUND +
+
+ Wraps HB_OT_NAME_ID_DARK_BACKGROUND + + + + + + + +
+
+ + ot.NAME_ID_VARIATIONS_PS_PREFIX +
+
+ Wraps HB_OT_NAME_ID_VARIATIONS_PS_PREFIX + + + + + + + +
+
+ + ot.NAME_ID_INVALID +
+
+ Wraps HB_OT_NAME_ID_INVALID + + + + + + + +
+
+ + ot.LAYOUT_NO_SCRIPT_INDEX +
+
+ Wraps HB_OT_LAYOUT_NO_SCRIPT_INDEX + + + + + + + +
+
+ + ot.LAYOUT_NO_FEATURE_INDEX +
+
+ Wraps HB_OT_LAYOUT_NO_FEATURE_INDEX + + + + + + + +
+
+ + ot.LAYOUT_DEFAULT_LANGUAGE_INDEX +
+
+ Wraps HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX + + + + + + + +
+
+ + ot.LAYOUT_NO_VARIATIONS_INDEX +
+
+ Wraps HB_OT_LAYOUT_NO_VARIATIONS_INDEX + + + + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2019-07-31 18:39:39 +
+
+ + diff --git a/luaharfbuzz/docs/ldoc.css b/luaharfbuzz/docs/ldoc.css new file mode 100644 index 0000000..52c4ad2 --- /dev/null +++ b/luaharfbuzz/docs/ldoc.css @@ -0,0 +1,303 @@ +/* BEGIN RESET + +Copyright (c) 2010, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.html +version: 2.8.2r1 +*/ +html { + color: #000; + background: #FFF; +} +body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td { + margin: 0; + padding: 0; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +fieldset,img { + border: 0; +} +address,caption,cite,code,dfn,em,strong,th,var,optgroup { + font-style: inherit; + font-weight: inherit; +} +del,ins { + text-decoration: none; +} +li { + margin-left: 20px; +} +caption,th { + text-align: left; +} +h1,h2,h3,h4,h5,h6 { + font-size: 100%; + font-weight: bold; +} +q:before,q:after { + content: ''; +} +abbr,acronym { + border: 0; + font-variant: normal; +} +sup { + vertical-align: baseline; +} +sub { + vertical-align: baseline; +} +legend { + color: #000; +} +input,button,textarea,select,optgroup,option { + font-family: inherit; + font-size: inherit; + font-style: inherit; + font-weight: inherit; +} +input,button,textarea,select {*font-size:100%; +} +/* END RESET */ + +body { + margin-left: 1em; + margin-right: 1em; + font-family: arial, helvetica, geneva, sans-serif; + background-color: #ffffff; margin: 0px; +} + +code, tt { font-family: monospace; font-size: 1.1em; } +span.parameter { font-family:monospace; } +span.parameter:after { content:":"; } +span.types:before { content:"("; } +span.types:after { content:")"; } +.type { font-weight: bold; font-style:italic } + +body, p, td, th { font-size: .95em; line-height: 1.2em;} + +p, ul { margin: 10px 0 0 0px;} + +strong { font-weight: bold;} + +em { font-style: italic;} + +h1 { + font-size: 1.5em; + margin: 20px 0 20px 0; +} +h2, h3, h4 { margin: 15px 0 10px 0; } +h2 { font-size: 1.25em; } +h3 { font-size: 1.15em; } +h4 { font-size: 1.06em; } + +a:link { font-weight: bold; color: #004080; text-decoration: none; } +a:visited { font-weight: bold; color: #006699; text-decoration: none; } +a:link:hover { text-decoration: underline; } + +hr { + color:#cccccc; + background: #00007f; + height: 1px; +} + +blockquote { margin-left: 3em; } + +ul { list-style-type: disc; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; +} + +pre { + background-color: rgb(245, 245, 245); + border: 1px solid #C0C0C0; /* silver */ + padding: 10px; + margin: 10px 0 10px 0; + overflow: auto; + font-family: "Andale Mono", monospace; +} + +pre.example { + font-size: .85em; +} + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } + +#container { + margin-left: 1em; + margin-right: 1em; + background-color: #f0f0f0; +} + +#product { + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#main { + background-color: #f0f0f0; + border-left: 2px solid #cccccc; +} + +#navigation { + float: left; + width: 14em; + vertical-align: top; + background-color: #f0f0f0; + overflow: visible; +} + +#navigation h2 { + background-color:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align: left; + padding:0.2em; + border-top:1px solid #dddddd; + border-bottom:1px solid #dddddd; +} + +#navigation ul +{ + font-size:1em; + list-style-type: none; + margin: 1px 1px 10px 1px; +} + +#navigation li { + text-indent: -1em; + display: block; + margin: 3px 0px 0px 22px; +} + +#navigation li li a { + margin: 0px 3px 0px -1em; +} + +#content { + margin-left: 14em; + padding: 1em; + width: 700px; + border-left: 2px solid #cccccc; + border-right: 2px solid #cccccc; + background-color: #ffffff; +} + +#about { + clear: both; + padding: 5px; + border-top: 2px solid #cccccc; + background-color: #ffffff; +} + +@media print { + body { + font: 12pt "Times New Roman", "TimeNR", Times, serif; + } + a { font-weight: bold; color: #004080; text-decoration: underline; } + + #main { + background-color: #ffffff; + border-left: 0px; + } + + #container { + margin-left: 2%; + margin-right: 2%; + background-color: #ffffff; + } + + #content { + padding: 1em; + background-color: #ffffff; + } + + #navigation { + display: none; + } + pre.example { + font-family: "Andale Mono", monospace; + font-size: 10pt; + page-break-inside: avoid; + } +} + +table.module_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.module_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.module_list td.name { background-color: #f0f0f0; min-width: 200px; } +table.module_list td.summary { width: 100%; } + + +table.function_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.function_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.function_list td.name { background-color: #f0f0f0; min-width: 200px; } +table.function_list td.summary { width: 100%; } + +ul.nowrap { + overflow:auto; + white-space:nowrap; +} + +dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} +dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} +dl.table h3, dl.function h3 {font-size: .95em;} + +/* stop sublists from having initial vertical space */ +ul ul { margin-top: 0px; } +ol ul { margin-top: 0px; } +ol ol { margin-top: 0px; } +ul ol { margin-top: 0px; } + +/* make the target distinct; helps when we're navigating to a function */ +a:target + * { + background-color: #FF9; +} + + +/* styles for prettification of source */ +pre .comment { color: #558817; } +pre .constant { color: #a8660d; } +pre .escape { color: #844631; } +pre .keyword { color: #aa5050; font-weight: bold; } +pre .library { color: #0e7c6b; } +pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } +pre .string { color: #8080ff; } +pre .number { color: #f8660d; } +pre .operator { color: #2239a8; font-weight: bold; } +pre .preprocessor, pre .prepro { color: #a33243; } +pre .global { color: #800080; } +pre .user-keyword { color: #800080; } +pre .prompt { color: #558817; } +pre .url { color: #272fc2; text-decoration: underline; } + diff --git a/luaharfbuzz/examples/core_types.lua b/luaharfbuzz/examples/core_types.lua new file mode 100644 index 0000000..ec3d0d4 --- /dev/null +++ b/luaharfbuzz/examples/core_types.lua @@ -0,0 +1,52 @@ +local harfbuzz = require('harfbuzz') +local serpent = require('serpent') -- luarocks install serpent + +-- Harfbuzz API Version +print("Harfbuzz API version", harfbuzz.version()) + +-- Shapers available +print("Shapers:", serpent.line({ harfbuzz.shapers() }, {comment = false})) + +-- harfbuzz.Face +local face = harfbuzz.Face.new('../fonts/notonastaliq.ttf') +print('\nFace upem = '..face:get_upem()) + +-- harfbuzz.Font +local font = harfbuzz.Font.new(face) +local xs, xy = font:get_scale() +print("\nDefault font scale = X: "..xs..", Y: "..xy) + +-- harfbuzz.Buffer +local text = "یہ" -- U+06CC U+06C1 +local buf = harfbuzz.Buffer.new() +buf:add_utf8(text) + +-- harfbuzz.shape (Shapes text) +print("\nShaping '"..text.."' set with Noto Nastaliq Urdu") +harfbuzz.shape(font, buf, { language = harfbuzz.Language.new("urd"), script = harfbuzz.Script.new("Arab"), direction = harfbuzz.Direction.RTL}) + +local glyphs = buf:get_glyphs() +print("No. of glyphs", #glyphs) +print(serpent.line(glyphs, {comment = false})) + +local opts = { language = harfbuzz.Language.new("eng"), script = harfbuzz.Script.new("Latn"), direction = harfbuzz.Direction.LTR } +local amiri_face = harfbuzz.Face.new('../fonts/amiri-regular.ttf') +local amiri_font = harfbuzz.Font.new(amiri_face) + +-- shaping '123' w/o features +print("\nShaping '123' set with Amiri Regular and no features") +buf= harfbuzz.Buffer.new() +buf:add_utf8("123") +harfbuzz.shape(amiri_font, buf, opts) +glyphs = buf:get_glyphs() +print(serpent.line(glyphs, {comment = false})) + +-- shaping '123' with '+numr' (numerators) +print("\nShaping '123' set with Amiri Regular with 'numr' feature turned on") +buf= harfbuzz.Buffer.new() +buf:add_utf8("123") +opts.features = "+numr" +harfbuzz.shape(amiri_font, buf, opts) +glyphs = buf:get_glyphs() +print(serpent.line(glyphs, {comment = false})) + diff --git a/luaharfbuzz/fixtures/AppleGothic_korean_issue_22.json b/luaharfbuzz/fixtures/AppleGothic_korean_issue_22.json new file mode 100644 index 0000000..2fa441b --- /dev/null +++ b/luaharfbuzz/fixtures/AppleGothic_korean_issue_22.json @@ -0,0 +1 @@ +[{"g":2651,"cl":0,"dx":0,"dy":0,"ax":1000,"ay":0},{"g":2942,"cl":1,"dx":0,"dy":0,"ax":1000,"ay":0},{"g":3083,"cl":2,"dx":0,"dy":0,"ax":1000,"ay":0}] diff --git a/luaharfbuzz/fixtures/amiri-regular_123.json b/luaharfbuzz/fixtures/amiri-regular_123.json new file mode 100644 index 0000000..b4bc7fb --- /dev/null +++ b/luaharfbuzz/fixtures/amiri-regular_123.json @@ -0,0 +1 @@ +[{"g":20,"cl":0,"dx":0,"dy":0,"ax":1090,"ay":0},{"g":21,"cl":1,"dx":0,"dy":0,"ax":1090,"ay":0},{"g":22,"cl":2,"dx":0,"dy":0,"ax":1090,"ay":0}] diff --git a/luaharfbuzz/fixtures/amiri-regular_123_numr.json b/luaharfbuzz/fixtures/amiri-regular_123_numr.json new file mode 100644 index 0000000..f542332 --- /dev/null +++ b/luaharfbuzz/fixtures/amiri-regular_123_numr.json @@ -0,0 +1 @@ +[{"g":6673,"cl":0,"dx":0,"dy":0,"ax":600,"ay":0},{"g":6674,"cl":1,"dx":0,"dy":0,"ax":600,"ay":0},{"g":6675,"cl":2,"dx":0,"dy":0,"ax":600,"ay":0}] diff --git a/luaharfbuzz/fixtures/notonastaliq_U06CC_U06C1.json b/luaharfbuzz/fixtures/notonastaliq_U06CC_U06C1.json new file mode 100644 index 0000000..218dcb0 --- /dev/null +++ b/luaharfbuzz/fixtures/notonastaliq_U06CC_U06C1.json @@ -0,0 +1 @@ +[{"g":277,"cl":2,"dx":0,"dy":0,"ax":472,"ay":0,"fl":1},{"g":19,"cl":0,"dx":310,"dy":-383,"ax":0,"ay":0},{"g":985,"cl":0,"dx":0,"dy":0,"ax":0,"ay":0},{"g":316,"cl":0,"dx":0,"dy":-68,"ax":731,"ay":0}] diff --git a/luaharfbuzz/fonts/Rajdhani-Regular.ttf b/luaharfbuzz/fonts/Rajdhani-Regular.ttf new file mode 100755 index 0000000..4b6ccef Binary files /dev/null and b/luaharfbuzz/fonts/Rajdhani-Regular.ttf differ diff --git a/luaharfbuzz/fonts/TwitterColorEmoji-SVGinOT.ttf b/luaharfbuzz/fonts/TwitterColorEmoji-SVGinOT.ttf new file mode 100644 index 0000000..a60d3ff Binary files /dev/null and b/luaharfbuzz/fonts/TwitterColorEmoji-SVGinOT.ttf differ diff --git a/luaharfbuzz/fonts/amiri-regular.ttf b/luaharfbuzz/fonts/amiri-regular.ttf new file mode 100644 index 0000000..f898e3b Binary files /dev/null and b/luaharfbuzz/fonts/amiri-regular.ttf differ diff --git a/luaharfbuzz/fonts/amiriquran-colored.ttf b/luaharfbuzz/fonts/amiriquran-colored.ttf new file mode 100644 index 0000000..5ebf488 Binary files /dev/null and b/luaharfbuzz/fonts/amiriquran-colored.ttf differ diff --git a/luaharfbuzz/fonts/notocoloremoji-subset.ttf b/luaharfbuzz/fonts/notocoloremoji-subset.ttf new file mode 100644 index 0000000..875ca15 Binary files /dev/null and b/luaharfbuzz/fonts/notocoloremoji-subset.ttf differ diff --git a/luaharfbuzz/fonts/notonastaliq.ttf b/luaharfbuzz/fonts/notonastaliq.ttf new file mode 100644 index 0000000..891f633 Binary files /dev/null and b/luaharfbuzz/fonts/notonastaliq.ttf differ diff --git a/luaharfbuzz/inspect.lua b/luaharfbuzz/inspect.lua new file mode 100644 index 0000000..e2e3806 --- /dev/null +++ b/luaharfbuzz/inspect.lua @@ -0,0 +1,334 @@ +local inspect ={ + _VERSION = 'inspect.lua 3.1.0', + _URL = 'http://github.com/kikito/inspect.lua', + _DESCRIPTION = 'human-readable representations of tables', + _LICENSE = [[ + MIT LICENSE + + Copyright (c) 2013 Enrique García Cota + + 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. + ]] +} + +local tostring = tostring + +inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end}) +inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end}) + +local function rawpairs(t) + return next, t, nil +end + +-- Apostrophizes the string if it has quotes, but not aphostrophes +-- Otherwise, it returns a regular quoted string +local function smartQuote(str) + if str:match('"') and not str:match("'") then + return "'" .. str .. "'" + end + return '"' .. str:gsub('"', '\\"') .. '"' +end + +-- \a => '\\a', \0 => '\\0', 31 => '\31' +local shortControlCharEscapes = { + ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", + ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v" +} +local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031 +for i=0, 31 do + local ch = string.char(i) + if not shortControlCharEscapes[ch] then + shortControlCharEscapes[ch] = "\\"..i + longControlCharEscapes[ch] = string.format("\\%03d", i) + end +end + +local function escape(str) + return (str:gsub("\\", "\\\\") + :gsub("(%c)%f[0-9]", longControlCharEscapes) + :gsub("%c", shortControlCharEscapes)) +end + +local function isIdentifier(str) + return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" ) +end + +local function isSequenceKey(k, sequenceLength) + return type(k) == 'number' + and 1 <= k + and k <= sequenceLength + and math.floor(k) == k +end + +local defaultTypeOrders = { + ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4, + ['function'] = 5, ['userdata'] = 6, ['thread'] = 7 +} + +local function sortKeys(a, b) + local ta, tb = type(a), type(b) + + -- strings and numbers are sorted numerically/alphabetically + if ta == tb and (ta == 'string' or ta == 'number') then return a < b end + + local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb] + -- Two default types are compared according to the defaultTypeOrders table + if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb] + elseif dta then return true -- default types before custom ones + elseif dtb then return false -- custom types after default ones + end + + -- custom types are sorted out alphabetically + return ta < tb +end + +-- For implementation reasons, the behavior of rawlen & # is "undefined" when +-- tables aren't pure sequences. So we implement our own # operator. +local function getSequenceLength(t) + local len = 1 + local v = rawget(t,len) + while v ~= nil do + len = len + 1 + v = rawget(t,len) + end + return len - 1 +end + +local function getNonSequentialKeys(t) + local keys, keysLength = {}, 0 + local sequenceLength = getSequenceLength(t) + for k,_ in rawpairs(t) do + if not isSequenceKey(k, sequenceLength) then + keysLength = keysLength + 1 + keys[keysLength] = k + end + end + table.sort(keys, sortKeys) + return keys, keysLength, sequenceLength +end + +local function countTableAppearances(t, tableAppearances) + tableAppearances = tableAppearances or {} + + if type(t) == 'table' then + if not tableAppearances[t] then + tableAppearances[t] = 1 + for k,v in rawpairs(t) do + countTableAppearances(k, tableAppearances) + countTableAppearances(v, tableAppearances) + end + countTableAppearances(getmetatable(t), tableAppearances) + else + tableAppearances[t] = tableAppearances[t] + 1 + end + end + + return tableAppearances +end + +local copySequence = function(s) + local copy, len = {}, #s + for i=1, len do copy[i] = s[i] end + return copy, len +end + +local function makePath(path, ...) + local keys = {...} + local newPath, len = copySequence(path) + for i=1, #keys do + newPath[len + i] = keys[i] + end + return newPath +end + +local function processRecursive(process, item, path, visited) + if item == nil then return nil end + if visited[item] then return visited[item] end + + local processed = process(item, path) + if type(processed) == 'table' then + local processedCopy = {} + visited[item] = processedCopy + local processedKey + + for k,v in rawpairs(processed) do + processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited) + if processedKey ~= nil then + processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited) + end + end + + local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) + if type(mt) ~= 'table' then mt = nil end -- ignore not nil/table __metatable field + setmetatable(processedCopy, mt) + processed = processedCopy + end + return processed +end + + + +------------------------------------------------------------------- + +local Inspector = {} +local Inspector_mt = {__index = Inspector} + +function Inspector:puts(...) + local args = {...} + local buffer = self.buffer + local len = #buffer + for i=1, #args do + len = len + 1 + buffer[len] = args[i] + end +end + +function Inspector:down(f) + self.level = self.level + 1 + f() + self.level = self.level - 1 +end + +function Inspector:tabify() + self:puts(self.newline, string.rep(self.indent, self.level)) +end + +function Inspector:alreadyVisited(v) + return self.ids[v] ~= nil +end + +function Inspector:getId(v) + local id = self.ids[v] + if not id then + local tv = type(v) + id = (self.maxIds[tv] or 0) + 1 + self.maxIds[tv] = id + self.ids[v] = id + end + return tostring(id) +end + +function Inspector:putKey(k) + if isIdentifier(k) then return self:puts(k) end + self:puts("[") + self:putValue(k) + self:puts("]") +end + +function Inspector:putTable(t) + if t == inspect.KEY or t == inspect.METATABLE then + self:puts(tostring(t)) + elseif self:alreadyVisited(t) then + self:puts('') + elseif self.level >= self.depth then + self:puts('{...}') + else + if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end + + local nonSequentialKeys, nonSequentialKeysLength, sequenceLength = getNonSequentialKeys(t) + local mt = getmetatable(t) + + self:puts('{') + self:down(function() + local count = 0 + for i=1, sequenceLength do + if count > 0 then self:puts(',') end + self:puts(' ') + self:putValue(t[i]) + count = count + 1 + end + + for i=1, nonSequentialKeysLength do + local k = nonSequentialKeys[i] + if count > 0 then self:puts(',') end + self:tabify() + self:putKey(k) + self:puts(' = ') + self:putValue(t[k]) + count = count + 1 + end + + if type(mt) == 'table' then + if count > 0 then self:puts(',') end + self:tabify() + self:puts(' = ') + self:putValue(mt) + end + end) + + if nonSequentialKeysLength > 0 or type(mt) == 'table' then -- result is multi-lined. Justify closing } + self:tabify() + elseif sequenceLength > 0 then -- array tables have one extra space before closing } + self:puts(' ') + end + + self:puts('}') + end +end + +function Inspector:putValue(v) + local tv = type(v) + + if tv == 'string' then + self:puts(smartQuote(escape(v))) + elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or + tv == 'cdata' or tv == 'ctype' then + self:puts(tostring(v)) + elseif tv == 'table' then + self:putTable(v) + else + self:puts('<', tv, ' ', self:getId(v), '>') + end +end + +------------------------------------------------------------------- + +function inspect.inspect(root, options) + options = options or {} + + local depth = options.depth or math.huge + local newline = options.newline or '\n' + local indent = options.indent or ' ' + local process = options.process + + if process then + root = processRecursive(process, root, {}, {}) + end + + local inspector = setmetatable({ + depth = depth, + level = 0, + buffer = {}, + ids = {}, + maxIds = {}, + newline = newline, + indent = indent, + tableAppearances = countTableAppearances(root) + }, Inspector_mt) + + inspector:putValue(root) + + return table.concat(inspector.buffer) +end + +setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end }) + +return inspect + diff --git a/luaharfbuzz/luaharfbuzz-1.1.0-1.rockspec b/luaharfbuzz/luaharfbuzz-1.1.0-1.rockspec new file mode 100644 index 0000000..17a8453 --- /dev/null +++ b/luaharfbuzz/luaharfbuzz-1.1.0-1.rockspec @@ -0,0 +1,46 @@ +package = "luaharfbuzz" +version = "1.1.0-1" +source = { + url = "git://github.com/ufytex/luaharfbuzz", + tag = "v1.1.0" +} +description = { + summary = "Lua bindings for the Harfbuzz text shaping library", + homepage = "https://github.com/ufytex/luaharfbuzz", + license = "MIT", + maintainer = "Deepak Jois " +} +dependencies = { + "lua >= 5.2" +} +build = { + type = "builtin", + modules = { + harfbuzz ="src/harfbuzz.lua", + luaharfbuzz= { + sources = { + "src/luaharfbuzz/luaharfbuzz.c", + "src/luaharfbuzz/blob.c", + "src/luaharfbuzz/face.c", + "src/luaharfbuzz/font.c", + "src/luaharfbuzz/buffer.c", + "src/luaharfbuzz/feature.c", + "src/luaharfbuzz/tag.c", + "src/luaharfbuzz/ot.c", + "src/luaharfbuzz/unicode.c", + "src/luaharfbuzz/script.c", + "src/luaharfbuzz/direction.c", + "src/luaharfbuzz/language.c", + "src/luaharfbuzz/class_utils.c" + }, + libraries = {"harfbuzz"}, + incdirs = {"$(HARFBUZZ_INCDIR)/harfbuzz"}, + libdirs = {"$(HARFBUZZ_LIBDIR)"} + } + } +} +external_dependencies = { + HARFBUZZ = { + header = "harfbuzz/hb.h" + } +} diff --git a/luaharfbuzz/luaharfbuzz-scm-1.rockspec b/luaharfbuzz/luaharfbuzz-scm-1.rockspec new file mode 100644 index 0000000..31ae2ab --- /dev/null +++ b/luaharfbuzz/luaharfbuzz-scm-1.rockspec @@ -0,0 +1,48 @@ +rockspec_format = "3.0" +package = "luaharfbuzz" +version = "scm-1" +source = { + url = "git://github.com/ufytex/luaharfbuzz" +} +description = { + summary = "Lua bindings for the Harfbuzz text shaping library", + homepage = "https://github.com/ufytex/luaharfbuzz", + license = "MIT", + maintainer = "Deepak Jois ", + issues_url = "https://github.com/ufytex/luaharfbuzz/issues", + labels = {"unicode", "fonts"} +} +dependencies = { + "lua >= 5.2" +} +build = { + type = "builtin", + modules = { + harfbuzz ="src/harfbuzz.lua", + luaharfbuzz= { + sources = { + "src/luaharfbuzz/luaharfbuzz.c", + "src/luaharfbuzz/blob.c", + "src/luaharfbuzz/face.c", + "src/luaharfbuzz/font.c", + "src/luaharfbuzz/buffer.c", + "src/luaharfbuzz/feature.c", + "src/luaharfbuzz/tag.c", + "src/luaharfbuzz/ot.c", + "src/luaharfbuzz/unicode.c", + "src/luaharfbuzz/script.c", + "src/luaharfbuzz/direction.c", + "src/luaharfbuzz/language.c", + "src/luaharfbuzz/class_utils.c" + }, + libraries = {"harfbuzz"}, + incdirs = {"$(HARFBUZZ_INCDIR)/harfbuzz"}, + libdirs = {"$(HARFBUZZ_LIBDIR)"} + } + } +} +external_dependencies = { + HARFBUZZ = { + header = "harfbuzz/hb.h" + } +} diff --git a/luaharfbuzz/scripts/generate_harfbuzz_api_list.sh b/luaharfbuzz/scripts/generate_harfbuzz_api_list.sh new file mode 100755 index 0000000..07d4770 --- /dev/null +++ b/luaharfbuzz/scripts/generate_harfbuzz_api_list.sh @@ -0,0 +1,2 @@ +#!/bin/sh +grep -e "^hb_" /usr/local/include/harfbuzz/hb*.h | grep "(" | sed -e 's/ .*$//' | awk -F: '{print $2}' | sort > status/full_api.txt diff --git a/luaharfbuzz/scripts/generate_hb_shape_json.sh b/luaharfbuzz/scripts/generate_hb_shape_json.sh new file mode 100755 index 0000000..5389845 --- /dev/null +++ b/luaharfbuzz/scripts/generate_hb_shape_json.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +SHAPER_OPTS="--font-funcs=ot --shapers=ot" +CLUSTER_OPTS="--utf8-clusters" +FORMAT_OPTS="--show-flags --no-glyph-names --output-format=json" +STANDARD_OPTS="$SHAPER_OPTS $CLUSTER_OPTS $FORMAT_OPTS" + +hb-shape $STANDARD_OPTS fonts/notonastaliq.ttf "یہ" > fixtures/notonastaliq_U06CC_U06C1.json +hb-shape $STANDARD_OPTS fonts/amiri-regular.ttf "123" > fixtures/amiri-regular_123.json +hb-shape $STANDARD_OPTS --features="+numr" fonts/amiri-regular.ttf "123" > fixtures/amiri-regular_123_numr.json +hb-shape $SHAPER_OPTS $FORMAT_OPTS /Library/Fonts/AppleGothic.ttf "가나다" > fixtures/AppleGothic_korean_issue_22.json diff --git a/luaharfbuzz/spec/buffer_spec.lua b/luaharfbuzz/spec/buffer_spec.lua new file mode 100644 index 0000000..503b902 --- /dev/null +++ b/luaharfbuzz/spec/buffer_spec.lua @@ -0,0 +1,236 @@ +local harfbuzz = require("harfbuzz") + +describe("harfbuzz.Buffer", function() + it("can be created", function() + harfbuzz.Buffer.new() + end) + + it("can add a single codepoints with explicit cluster value", function() + local b = harfbuzz.Buffer.new() + b:add(0x06CC, 42) + local glyphs = b:get_glyphs() + assert.are_equal(#glyphs, 1) + assert.are_equal(glyphs[1].cluster, 42) + assert.are_equal(glyphs[1].codepoint, 0x06CC) + end) + + it("can add a UTF8 string", function() + local b = harfbuzz.Buffer.new() + local s = "Some String" + b:add_utf8(s) + assert.are_equal(string.len(s), b:get_length()) + end) + + it("can add a UTF 8 string with item_offset", function() + local b = harfbuzz.Buffer.new() + local s = "Some String" + local o = 5 + b:add_utf8(s,o) + assert.are_equal(string.len(s) - o, b:get_length()) + end) + + it("can add a UTF 8 string with item_length", function() + local b = harfbuzz.Buffer.new() + local s = "Some String" + local o = 5 + local l = 2 + b:add_utf8(s,o,l) + assert.are_equal(l, b:get_length()) + end) + + it("can add codepoints", function() + local b = harfbuzz.Buffer.new() + local s = { 0x06CC, 0x06C1 } + b:add_codepoints(s) + assert.are_equal(#s, b:get_length()) + end) + + it("can add codepoints with item_offset", function() + local b = harfbuzz.Buffer.new() + local s = { 0x06CC, 0x06C1 } + local o = 1 + b:add_codepoints(s,o) + assert.are_equal(#s - o, b:get_length()) + end) + + it("can add codepoints with item_length", function() + local b = harfbuzz.Buffer.new() + local s = { 0x06CC, 0x06C1 } + local o = 1 + local l = 1 + b:add_codepoints(s,o,l) + assert.are_equal(l, b:get_length()) + end) + + it("can call guess_segment_properties", function() + local b = harfbuzz.Buffer.new() + b:add_utf8("Some String") + b:guess_segment_properties() + end) + + it("can get and set the direction of a buffer", function() + local b = harfbuzz.Buffer.new() + b:add_utf8("abc") + local dir = harfbuzz.Direction.RTL + b:set_direction(dir) + assert.are_equal(dir, b:get_direction()) + end) + + it("sets direction to INVALID if direction is invalid", function() + local b = harfbuzz.Buffer.new() + b:set_direction(harfbuzz.Direction.new("invalid")) + assert.are_equal(harfbuzz.Direction.INVALID, b:get_direction()) + end) + + it("can get the direction correctly", function() + local b = harfbuzz.Buffer.new() + b:add_utf8("یہ") + b:guess_segment_properties() + assert.are_equal(harfbuzz.Direction.RTL, b:get_direction()) + end) + + it("can get and set the language of a buffer", function() + local b = harfbuzz.Buffer.new() + b:add_utf8("یہ") + local urd = harfbuzz.Language.new("urd") + b:set_language(urd) + assert.are_equal(urd, b:get_language()) + end) + + it("Sets language to INVALID if language is invalid", function() + local b = harfbuzz.Buffer.new() + b:set_language(harfbuzz.Language.INVALID) + assert.are_equal(harfbuzz.Language.INVALID, b:get_language()) + end) + + it("can get the language correctly", function() + local b = harfbuzz.Buffer.new() + b:add_utf8("یہ") + b:guess_segment_properties() + assert.are_not_equal(harfbuzz.Language.INVALID, b:get_language()) + end) + + it("can get and set the script of a buffer", function() + local b = harfbuzz.Buffer.new() + b:add_utf8("abc") + b:set_script(harfbuzz.Script.new("latn")) + assert.are_equal("Latn", tostring(b:get_script())) + end) + + it("returns script as UNKNOWN if script is invalid", function() + local b = harfbuzz.Buffer.new() + b:set_script(harfbuzz.Script.new("xxx")) + assert.are_equal(harfbuzz.Script.UNKNOWN, b:get_script()) + end) + + it("can get the script correctly", function() + local b = harfbuzz.Buffer.new() + b:add_utf8("یہ") + assert.are_equal(harfbuzz.Script.new(""), b:get_script()) + b:guess_segment_properties() + assert.are_equal(harfbuzz.Script.new("Arab"), b:get_script()) + end) + + it("can reverse the buffer", function() + local face = harfbuzz.Face.new('fonts/notonastaliq.ttf') + local font = harfbuzz.Font.new(face) + local urdu_text = "یہ" -- U+06CC U+06C1 + local options = { language = harfbuzz.Language.new("urd"), script = harfbuzz.Script.new("Arab"), direction = harfbuzz.Direction.RTL } + + local buf= harfbuzz.Buffer.new() + buf:add_utf8(urdu_text) + harfbuzz.shape(font, buf, options) + local orig_glyphs = buf:get_glyphs() + buf:reverse() + local reversed_glyphs = buf:get_glyphs() + + assert.are_equal(#orig_glyphs, #reversed_glyphs) + + for c = 1, #orig_glyphs do + local g = orig_glyphs[#orig_glyphs - (c - 1)] + local r = reversed_glyphs[c] + assert.are_equal(g.codepoint, r.codepoint) + assert.are_equal(g.cluster, r.cluster) + assert.are_equal(g.x_advance, r.x_advance) + assert.are_equal(g.y_advance, r.y_advance) + assert.are_equal(g.x_offset, r.x_offset) + assert.are_equal(g.y_offset, r.y_offset) + end + + end) + + it("can get the length of the buffer", function() + local b = harfbuzz.Buffer.new() + local s = "some string" + b:add_utf8(s) + assert.are_equal(string.len(s), b:get_length()) + end) + + it("can get the cluster level of the buffer", function() + local b = harfbuzz.Buffer.new() + assert.are_equal(harfbuzz.Buffer.CLUSTER_LEVEL_DEFAULT, b:get_cluster_level()) + end) + + it("can set the cluster level of the buffer", function() + local b = harfbuzz.Buffer.new() + b:set_cluster_level(harfbuzz.Buffer.CLUSTER_LEVEL_CHARACTERS) + assert.are_equal(harfbuzz.Buffer.CLUSTER_LEVEL_CHARACTERS, b:get_cluster_level()) + end) + + it("can get the replacement glyph for invisible characters of the buffer", function() + local b = harfbuzz.Buffer.new() + assert.are_equal(0, b:get_invisible_glyph()) + end) + + it("can set the replacement glyph for invisible characters of the buffer", function() + local b = harfbuzz.Buffer.new() + b:set_invisible_glyph(3) + assert.are_equal(3, b:get_invisible_glyph()) + end) + + it("can get the replacement codepoint of the buffer", function() + local b = harfbuzz.Buffer.new() + assert.are_equal(0xFFFD, b:get_replacement_codepoint()) + end) + + it("can set the replacement codepoint of the buffer", function() + local b = harfbuzz.Buffer.new() + b:set_replacement_codepoint(0xFFFF) + assert.are_equal(0xFFFF, b:get_replacement_codepoint()) + b:add_utf8("\xFF") + local glyphs = b:get_glyphs() + assert.are_equal(1, #glyphs) + assert.are_equal(0xFFFF, glyphs[1].codepoint) + end) + + it("can get the flags of the buffer", function() + local b = harfbuzz.Buffer.new() + assert.are_equal(harfbuzz.Buffer.FLAG_DEFAULT, b:get_flags()) + end) + + it("can set the flags of the buffer", function() + local b = harfbuzz.Buffer.new() + b:set_flags(harfbuzz.Buffer.FLAG_DEFAULT | harfbuzz.Buffer.FLAG_BOT) + assert.are_equal(harfbuzz.Buffer.FLAG_DEFAULT | harfbuzz.Buffer.FLAG_BOT, b:get_flags()) + end) + + it("can clear the buffer", function() + local b = harfbuzz.Buffer.new() + b:add_utf8("abc") + b:set_flags(harfbuzz.Buffer.FLAG_DEFAULT | harfbuzz.Buffer.FLAG_BOT) + b:clear_contents() + assert.are_equal(0, b:get_length()) + assert.are_equal(harfbuzz.Buffer.FLAG_DEFAULT | harfbuzz.Buffer.FLAG_BOT, b:get_flags()) + end) + + it("can reset the buffer", function() + local b = harfbuzz.Buffer.new() + b:add_utf8("abc") + b:set_flags(harfbuzz.Buffer.FLAG_DEFAULT | harfbuzz.Buffer.FLAG_BOT) + b:reset() + assert.are_equal(0, b:get_length()) + assert.are_equal(harfbuzz.Buffer.FLAG_DEFAULT, b:get_flags()) + end) +end) + + diff --git a/luaharfbuzz/spec/harfbuzz_spec.lua b/luaharfbuzz/spec/harfbuzz_spec.lua new file mode 100644 index 0000000..dfae07d --- /dev/null +++ b/luaharfbuzz/spec/harfbuzz_spec.lua @@ -0,0 +1,541 @@ +local harfbuzz = require("harfbuzz") + +describe("harfbuzz module", function() + + it("returns a valid version string", function() + assert.are_equal("string", type(harfbuzz.version())) + end) + + it("returns a valid list of shapers", function() + local shapers = { harfbuzz.shapers } + assert.is_not.True(#shapers == 0) + end) + + describe("harfbuzz.Blob", function() + it("can be initialized with a string", function() + local s = "test string" + local blob = harfbuzz.Blob.new(s) + assert.are_equal(string.len(s), blob:get_length()) + end) + end) + + describe("harfbuzz.Face", function() + local face = harfbuzz.Face.new('fonts/notonastaliq.ttf') + it("can be initialized with a blob", function() + local fontfile = io.open('fonts/notonastaliq.ttf', "r") + local fontdata = fontfile:read("*all") + fontfile:close() + + local blob = harfbuzz.Blob.new(fontdata) + harfbuzz.Face.new_from_blob(blob,0) + end) + + it("can be initialized with a file and a font index", function() + harfbuzz.Face.new('fonts/notonastaliq.ttf',0) + end) + + it("can be initialized with a file only", function() + harfbuzz.Face.new('fonts/notonastaliq.ttf') + end) + + it("returns a valid upem value", function() + assert.are_equal(2048,face:get_upem()) + end) + + it("can return SFNT table", function() + local b = face:get_table(harfbuzz.Tag.new("OS/2")) + local d = b:get_data() + assert.are_equal(96,b:get_length()) + assert.are_equal(96,#d) + assert.are_equal(4,string.unpack(">H", d, 1)) + assert.are_equal(1155,string.unpack(">h", d, 3)) + assert.are_equal(5,string.unpack(">H", d, -2)) + end) + + it("can return table tags", function() + local t = face:get_table_tags() + assert.are_equal(14,#t) + assert.are_equal("GDEF",tostring(t[1])) + assert.are_equal("post",tostring(t[#t])) + end) + + it("can return glyph count", function() + assert.are_equal(1133,face:get_glyph_count()) + end) + + it("can return unicode characters supported by face", function() + local u = face:collect_unicodes() + assert.are_equal(267,#u) + assert.are_equal(0x0000,u[1]) + assert.are_equal(0xFEFF,u[#u]) + end) + + it("can return face names", function() + assert.are_equal("Copyright 2014 Google Inc. All Rights Reserved.",face:get_name(harfbuzz.ot.NAME_ID_COPYRIGHT)) + assert.are_equal("Noto Nastaliq Urdu",face:get_name(harfbuzz.ot.NAME_ID_FONT_FAMILY)) + assert.are_equal("Regular",face:get_name(harfbuzz.ot.NAME_ID_FONT_SUBFAMILY)) + assert.are_equal("Noto Nastaliq Urdu",face:get_name(harfbuzz.ot.NAME_ID_FULL_NAME)) + assert.are_equal("NotoNastaliqUrdu",face:get_name(harfbuzz.ot.NAME_ID_POSTSCRIPT_NAME)) + assert.are_equal("Noto is a trademark of Google Inc.",face:get_name(harfbuzz.ot.NAME_ID_TRADEMARK)) + assert.are_equal(331,#face:get_name(harfbuzz.ot.NAME_ID_LICENSE)) + assert.are_equal(nil,face:get_name(harfbuzz.ot.NAME_ID_INVALID)) + end) + + it("can return face names with language", function() + local f = harfbuzz.Face.new('fonts/amiri-regular.ttf') + local ar = harfbuzz.Language.new("ar") + local en = harfbuzz.Language.new("en") + assert.are_equal("حقوق النشر 2010-2015، خالد حسني .",f:get_name(harfbuzz.ot.NAME_ID_COPYRIGHT, ar)) + assert.are_equal("Copyright (c) 2010-2015, Khaled Hosny .\nPortions copyright (c) 2010, Sebastian Kosch .",f:get_name(harfbuzz.ot.NAME_ID_COPYRIGHT, en)) + assert.are_equal("عادي",f:get_name(harfbuzz.ot.NAME_ID_FONT_SUBFAMILY, ar)) + assert.are_equal("Regular",f:get_name(harfbuzz.ot.NAME_ID_FONT_SUBFAMILY, en)) + assert.are_equal("إصدارة 000٫108",f:get_name(harfbuzz.ot.NAME_ID_VERSION_STRING, ar)) + assert.are_equal("Version 000.108 ",f:get_name(harfbuzz.ot.NAME_ID_VERSION_STRING, en)) + assert.are_equal("خالد حسني",f:get_name(harfbuzz.ot.NAME_ID_DESIGNER, ar)) + assert.are_equal("Khaled Hosny",f:get_name(harfbuzz.ot.NAME_ID_DESIGNER, en)) + assert.are_equal(512,#f:get_name(harfbuzz.ot.NAME_ID_DESCRIPTION, ar)) + assert.are_equal(263,#f:get_name(harfbuzz.ot.NAME_ID_DESCRIPTION, en)) + assert.are_equal("صِفْ خَلْقَ خَوْدٍ كَمِثْلِ ٱلشَّمْسِ إِذْ بَزَغَتْ يَحْظَىٰ ٱلضَّجِيعُ بِهَا نَجْلَاءَ مِعْطَارِ.",f:get_name(harfbuzz.ot.NAME_ID_SAMPLE_TEXT, ar)) + assert.are_equal("صِفْ خَلْقَ خَوْدٍ كَمِثْلِ ٱلشَّمْسِ إِذْ بَزَغَتْ يَحْظَىٰ ٱلضَّجِيعُ بِهَا نَجْلَاءَ مِعْطَارِ.",f:get_name(harfbuzz.ot.NAME_ID_SAMPLE_TEXT, en)) + end) + + it("can check color palettes", function() + local f = harfbuzz.Face.new('fonts/amiriquran-colored.ttf') + assert.are_equal(false,face:ot_color_has_palettes()) + assert.are_equal(true,f:ot_color_has_palettes()) + end) + + it("can return number of color palettes", function() + local f = harfbuzz.Face.new('fonts/amiriquran-colored.ttf') + assert.are_equal(0,face:ot_color_palette_get_count()) + assert.are_equal(1,f:ot_color_palette_get_count()) + end) + + it("can return palette colors", function() + local f = harfbuzz.Face.new('fonts/amiriquran-colored.ttf') + assert.are_equal(nil,face:ot_color_palette_get_colors()) + local colors = { + { alpha = 255, blue = 51, green = 51, red = 204, }, + { alpha = 255, blue = 80, green = 165, red = 0, }, + { alpha = 255, blue = 51, green = 153, red = 238, }, + { alpha = 255, blue = 153, green = 102, red = 51, }, + } + assert.are_same(colors,f:ot_color_palette_get_colors()) + end) + + it("can check color layers", function() + local f = harfbuzz.Face.new('fonts/amiriquran-colored.ttf') + assert.are_equal(false,face:ot_color_has_layers()) + assert.are_equal(true,f:ot_color_has_layers()) + end) + + it("can return glyph color layers", function() + local f = harfbuzz.Face.new('fonts/amiriquran-colored.ttf') + assert.are_equal(nil,face:ot_color_glyph_get_layers(100)) + assert.are_equal(nil,f:ot_color_glyph_get_layers(2)) + local layers = { + { color_index = 65535, glyph = 1341 }, + { color_index = 1, glyph = 1370 }, + } + assert.are_same(layers,f:ot_color_glyph_get_layers(100)) + end) + + it("can check PNG glyph support", function() + local f = harfbuzz.Face.new('fonts/notocoloremoji-subset.ttf') + assert.are_equal(false,face:ot_color_has_png()) + assert.are_equal(true,f:ot_color_has_png()) + end) + + it("can check SVG glyph support", function() + local f = harfbuzz.Face.new('fonts/TwitterColorEmoji-SVGinOT.ttf') + assert.are_equal(false,face:ot_color_has_svg()) + assert.are_equal(true,f:ot_color_has_svg()) + end) + + it("can return glyph color png", function() + local f = harfbuzz.Face.new('fonts/TwitterColorEmoji-SVGinOT.ttf') + + assert.are_equal(nil,face:ot_color_glyph_get_svg(100)) + assert.are_equal(nil,f:ot_color_glyph_get_svg(0)) + assert.are_same(751,f:ot_color_glyph_get_svg(5):get_length()) + assert.are_same(804,f:ot_color_glyph_get_svg(6):get_length()) + assert.are_same("",f:ot_color_glyph_get_svg(5):get_data():sub(1, 38)) + end) + + it("can return script tags", function() + local t + local tags = { + harfbuzz.Tag.new("arab"), + harfbuzz.Tag.new("dflt"), + harfbuzz.Tag.new("latn"), + } + t = face:ot_layout_get_script_tags(harfbuzz.Tag.new("GSUB")) + assert.are_same(tags, t) + t = face:ot_layout_get_script_tags(harfbuzz.Tag.new("GPOS")) + assert.are_same({ tags[1] }, t) + end) + + it("can return language tags", function() + local t + local tags = { + harfbuzz.Tag.new("ARA "), + harfbuzz.Tag.new("FAR "), + harfbuzz.Tag.new("KSH "), + harfbuzz.Tag.new("SND "), + harfbuzz.Tag.new("URD "), + } + t = face:ot_layout_get_language_tags(harfbuzz.Tag.new("GSUB"), 0) + assert.are_same(tags, t) + t = face:ot_layout_get_language_tags(harfbuzz.Tag.new("GPOS"), 0) + assert.are_equal(nil, t) + end) + + it("can return feature tags", function() + local t, tags + tags = { + harfbuzz.Tag.new("ccmp"), + harfbuzz.Tag.new("isol"), + harfbuzz.Tag.new("init"), + harfbuzz.Tag.new("medi"), + harfbuzz.Tag.new("fina"), + harfbuzz.Tag.new("rlig"), + } + t = face:ot_layout_get_feature_tags(harfbuzz.Tag.new("GSUB"), 0, 0) + assert.are_same(tags, t) + tags = { + harfbuzz.Tag.new("curs"), + harfbuzz.Tag.new("mark"), + harfbuzz.Tag.new("mkmk"), + } + t = face:ot_layout_get_feature_tags(harfbuzz.Tag.new("GPOS"), 0, harfbuzz.ot.LAYOUT_DEFAULT_LANGUAGE_INDEX) + assert.are_same(tags, t) + end) + + it("can find scripts, languages and features", function() + local r, i + r, i = face:ot_layout_find_script(harfbuzz.Tag.new("GSUB"), harfbuzz.Tag.new("latn")) + assert.True(r) + assert.are_same(2, i) + r, i = face:ot_layout_find_language(harfbuzz.Tag.new("GSUB"), i, harfbuzz.Tag.new("ENG ")) + assert.False(r) + assert.are_same(harfbuzz.ot.LAYOUT_DEFAULT_LANGUAGE_INDEX, i) + r, i = face:ot_layout_find_language(harfbuzz.Tag.new("GSUB"), 0, harfbuzz.Tag.new("ARA ")) + assert.True(r) + assert.are_same(0, i) + r, i = face:ot_layout_find_feature(harfbuzz.Tag.new("GSUB"), 0, i, harfbuzz.Tag.new("rlig")) + assert.True(r) + assert.are_same(13, i) + + r, i = face:ot_layout_find_feature(harfbuzz.Tag.new("GSUB"), 1, harfbuzz.ot.LAYOUT_DEFAULT_LANGUAGE_INDEX, harfbuzz.Tag.new("rlig")) + assert.True(r) + assert.are_same(13, i) + end) + end) + + describe("harfbuzz.Font", function() + local face = harfbuzz.Face.new('fonts/notonastaliq.ttf') + it("can be initialized with a face", function() + harfbuzz.Font.new(face) + end) + + it("has a default scale set to the fonts upem", function() + local font = harfbuzz.Font.new(face) + local upem = face:get_upem() + local xs, ys = font:get_scale() + assert.are_equal(upem, xs) + assert.are_equal(upem, ys) + end) + + it("can set the scale of the font using set_scale", function() + local font = harfbuzz.Font.new(face) + font:set_scale(1024,2048) + local xs, ys = font:get_scale() + assert.are_equal(1024, xs) + assert.are_equal(2048, ys) + end) + + it("can get glyph extents using get_glyph_extents", function() + local font = harfbuzz.Font.new(face) + local extents = font:get_glyph_extents(0) + assert.are_equal(145, extents.x_bearing) + assert.are_equal(2452, extents.y_bearing) + assert.are_equal(1553, extents.width) + assert.are_equal(-2452, extents.height) + extents = font:get_glyph_extents(1) + assert.are_equal(0, extents.x_bearing) + assert.are_equal(0, extents.y_bearing) + assert.are_equal(0, extents.width) + assert.are_equal(0, extents.height) + end) + + it("can get font extents", function() + local font = harfbuzz.Font.new(face) + local extents = font:get_h_extents(0) + assert.are_equal(3900, extents.ascender) + assert.are_equal(-1220, extents.descender) + assert.are_equal(0, extents.line_gap) + extents = font:get_v_extents(1) + assert.are_equal(nil, extents) + end) + + it("can get glyph name using get_glyph_name", function() + local font = harfbuzz.Font.new(face) + assert.are_equal(".notdef", font:get_glyph_name(0)) + assert.are_equal("null", font:get_glyph_name(1)) + end) + + it("can get glyph using get_glyph_from_name", function() + local font = harfbuzz.Font.new(face) + assert.are_equal(0, font:get_glyph_from_name(".notdef")) + assert.are_equal(1, font:get_glyph_from_name("null")) + end) + + it("can get glyph advance using get_glyph_h_advance", function() + local font = harfbuzz.Font.new(face) + assert.are_equal(1843, font:get_glyph_h_advance(0)) + assert.are_equal(0, font:get_glyph_h_advance(1)) + end) + + it("can get glyph advance using get_glyph_v_advance", function() + local font = harfbuzz.Font.new(face) + assert.are_equal(-2048, font:get_glyph_v_advance(0)) + assert.are_equal(-2048, font:get_glyph_v_advance(1)) + end) + + it("can get nominal glyph for codepoint", function() + local font = harfbuzz.Font.new(face) + assert.are_equal(nil, font:get_nominal_glyph(0x0041)) + assert.are_equal(858, font:get_nominal_glyph(0x0627)) + end) + + it("can return glyph color png", function() + local font = harfbuzz.Font.new(face) + local f = harfbuzz.Font.new(harfbuzz.Face.new('fonts/notocoloremoji-subset.ttf')) + + assert.are_equal(nil,font:ot_color_glyph_get_png(100)) + assert.are_equal(nil,f:ot_color_glyph_get_png(0)) + assert.are_same(2233,f:ot_color_glyph_get_png(1):get_length()) + assert.are_same(2857,f:ot_color_glyph_get_png(2):get_length()) + assert.are_same("\137PNG",f:ot_color_glyph_get_png(2):get_data():sub(1, 4)) + end) + end) + + describe("harfbuzz.Feature", function() + it("can be initialised with a valid feature string", function() + harfbuzz.Feature.new('kern') + harfbuzz.Feature.new('+kern') + end) + + it("throws an error when trying to initialise a new Feature with an invalid string", function() + assert.are_equal(nil, harfbuzz.Feature.new('')) + assert.are_equal(nil, harfbuzz.Feature.new('#kern')) + end) + + it("has a valid tostring value", function() + local fs = 'kern' + local f = harfbuzz.Feature.new(fs) + assert.are_equal(fs, tostring(f)) + end) + + it("has visible fields", function() + local f = harfbuzz.Feature.new('-kern') + print(getmetatable(f).__index) + assert.are_equal(tostring(f.tag), 'kern') + assert.are_equal(f.value, 0) + assert.are_equal(f.start, nil) + assert.are_equal(f._end, nil) + + f = harfbuzz.Feature.new('aalt[3:5]=4') + assert.are_equal(tostring(f.tag), 'aalt') + assert.are_equal(f.value, 4) + assert.are_equal(f.start, 3) + assert.are_equal(f._end, 5) + end) + + it("has editable fields", function() + local f = harfbuzz.Feature.new('-kern') + f.tag, f.value, f.start, f._end = harfbuzz.Tag.new"aalt", 4, 3, 5 + assert.are_equal(tostring(f), "aalt[3:5]=4") + + f.tag, f.value, f.start, f._end = harfbuzz.Tag.new"harf", 0, nil, nil + assert.are_equal(tostring(f), "-harf") + end) + end) + + describe("harfbuzz.Tag", function() + it("can be initialised with a valid tag string", function() + harfbuzz.Tag.new('Zyyy') + end) + + it("can be initialised to NONE with nil or empty argument", function() + local t = harfbuzz.Tag.new() + assert.are_equal(harfbuzz.Tag.NONE, t) + t = harfbuzz.Tag.new(nil) + assert.are_equal(harfbuzz.Tag.NONE, t) + end) + + it("has a valid tostring value", function() + local ts = 'Arab' + local t = harfbuzz.Tag.new(ts) + assert.are_equal(ts, tostring(t)) + end) + + it("has a valid equality check functions", function() + local ts = 'Arab' + local t1 = harfbuzz.Tag.new(ts) + local t2 = harfbuzz.Tag.new(ts) + local t3 = harfbuzz.Tag.new("Latn") + assert.are_equal(t1, t2) + assert.are_not_equal(t1, t3) + end) + + it("has a preset value for NONE", function() + local n = harfbuzz.Tag.NONE + assert.is_not_nil(n) + assert.are_equal("", tostring(n)) + assert.are_equal(harfbuzz.Tag.NONE, harfbuzz.Tag.new("")) + end) + end) + + describe("harfbuzz.Script", function() + it("can be initialised with a string", function() + harfbuzz.Script.new('Arab') + end) + + it("can be initialised to INVALID with nil or empty argument", function() + local t = harfbuzz.Script.new() + assert.are_equal(harfbuzz.Script.INVALID, t) + t = harfbuzz.Script.new(nil) + assert.are_equal(harfbuzz.Script.INVALID, t) + end) + + it("can be initialised with a tag", function() + local ts = "Arab" + local s = harfbuzz.Script.from_iso15924_tag(harfbuzz.Tag.new(ts)) + assert.are_equal(ts, tostring(s)) + end) + + it("can be converted to a tag", function() + local s = 'Arab' + local sc = harfbuzz.Script.new(s) + assert.are_equal(s, tostring(sc:to_iso15924_tag())) + end) + + it("has a valid tostring value", function() + local ts = 'Arab' + local t = harfbuzz.Script.new(ts) + assert.are_equal(ts, tostring(t)) + end) + + it("has a valid equality check functions", function() + local ts = 'Arab' + local t1 = harfbuzz.Script.new(ts) + local t2 = harfbuzz.Script.new(ts) + local t3 = harfbuzz.Script.new("Latn") + assert.are_equal(t1, t2) + assert.are_not_equal(t1, t3) + end) + end) + + describe("harfbuzz.Direction", function() + it("can be initialised with a valid tag string", function() + harfbuzz.Direction.new('LTR') + end) + + it("can be initialised with invalid strings", function() + local d1 = harfbuzz.Direction.new("i") + local d2 = harfbuzz.Direction.new("inv") + + assert.are_equal(d1, d2) + assert.are_equal("invalid", tostring(d1)) + end) + + it("has a valid tostring value", function() + local ts = 'ltr' + local t = harfbuzz.Direction.new(ts) + assert.are_equal(ts, tostring(t)) + + t = harfbuzz.Direction.new("LTR") + assert.are_equal(ts, tostring(t)) + end) + + it("has a valid equality check functions", function() + local ts = 'ltr' + local t1 = harfbuzz.Direction.new(ts) + local t2 = harfbuzz.Direction.new(ts) + local t3 = harfbuzz.Direction.new("rtl") + assert.are_equal(t1, t2) + assert.are_not_equal(t1, t3) + end) + + it("has a is_valid function", function() + assert.True(harfbuzz.Direction.LTR:is_valid()) + assert.False(harfbuzz.Direction.INVALID:is_valid()) + end) + + it("has a is_horizontal function", function() + assert.True(harfbuzz.Direction.LTR:is_horizontal()) + assert.False(harfbuzz.Direction.TTB:is_horizontal()) + end) + + it("has a is_vertical function", function() + assert.True(harfbuzz.Direction.TTB:is_vertical()) + assert.False(harfbuzz.Direction.LTR:is_vertical()) + end) + + it("has a is_forward function", function() + assert.True(harfbuzz.Direction.LTR:is_forward()) + assert.False(harfbuzz.Direction.RTL:is_forward()) + end) + + it("has a is_backward function", function() + assert.True(harfbuzz.Direction.RTL:is_backward()) + assert.False(harfbuzz.Direction.LTR:is_backward()) + end) + end) + + describe("harfbuzz.Language", function() + it("can be initialised with a valid language string", function() + harfbuzz.Language.new('urd') + end) + + it("can be initialised to INVALID with nil or empty argument", function() + local t = harfbuzz.Language.new() + assert.are_equal(harfbuzz.Language.INVALID, t) + t = harfbuzz.Language.new(nil) + assert.are_equal(harfbuzz.Language.INVALID, t) + end) + + it("has a valid tostring value", function() + local ts = 'urd' + local t = harfbuzz.Language.new(ts) + assert.are_equal(ts, tostring(t)) + end) + + it("has a valid equality check functions", function() + local ts = 'urd' + local t1 = harfbuzz.Language.new(ts) + local t2 = harfbuzz.Language.new(ts) + local t3 = harfbuzz.Language.new("hin") + assert.are_equal(t1, t2) + assert.are_not_equal(t1, t3) + end) + + it("has a preset value for INVALID", function() + local n = harfbuzz.Language.INVALID + assert.is_not_nil(n) + assert.are_equal(harfbuzz.Language.INVALID, harfbuzz.Language.new()) + assert.are_equal("", tostring(n)) + end) + end) + + describe("harfbuzz.unicode", function() + describe("script function returns a valid script for a codepoint",function() + local s = harfbuzz.unicode.script(0x0020) + assert.are_equal(harfbuzz.Script.COMMON, s) + s = harfbuzz.unicode.script(0x0041) + assert.are_equal(harfbuzz.Script.new("Latn"), s) + end) + end) +end) + diff --git a/luaharfbuzz/spec/shaping_spec.lua b/luaharfbuzz/spec/shaping_spec.lua new file mode 100644 index 0000000..8ab810e --- /dev/null +++ b/luaharfbuzz/spec/shaping_spec.lua @@ -0,0 +1,157 @@ +local harfbuzz = require("harfbuzz") + +local compare_glyphs_against_fixture = function(glyphs, fixture) + local json = require('dkjson') + local f = io.open("fixtures/"..fixture) + local s = f:read("*all") + f:close() + local hb_shape_glyphs = json.decode(s) + assert.are_equal(#hb_shape_glyphs, #glyphs) + for c = 1, #glyphs do + local g = glyphs[c] + local h = hb_shape_glyphs[c] + assert.are_equal(h.g, g.codepoint) + assert.are_equal(h.cl, g.cluster) + assert.are_equal(h.ax, g.x_advance) + assert.are_equal(h.ay, g.y_advance) + assert.are_equal(h.dx, g.x_offset) + assert.are_equal(h.dy, g.y_offset) + assert.are_equal(h.fl, g.flags) + end +end + + +describe("harfbuzz module shaping functions", function() + local face = harfbuzz.Face.new('fonts/notonastaliq.ttf') + local font = harfbuzz.Font.new(face) + local urdu_text = "یہ" -- U+06CC U+06C1 + + it("can take a buffer and font and shape it, with output matching hb-shape", function() + local buf = harfbuzz.Buffer.new() + buf:add_utf8(urdu_text) + + harfbuzz.shape(font, buf) + local glyphs = buf:get_glyphs() + assert.True(#glyphs > 0) + + -- Compare against output of hb-shape + compare_glyphs_against_fixture(glyphs, 'notonastaliq_U06CC_U06C1.json') + end) + + it("can take a buffer, font and an options table with script, language and direction settings.", function() + local buf = harfbuzz.Buffer.new() + buf:add_utf8(urdu_text) + + harfbuzz.shape(font, buf, { language = harfbuzz.Language.new("urd"), script = harfbuzz.Script.new("Arab"), direction = harfbuzz.Direction.HB_DIRECTION_RTL }) + local glyphs = buf:get_glyphs() + assert.True(#glyphs > 0) + + -- Compare against output of hb-shape + compare_glyphs_against_fixture(glyphs, 'notonastaliq_U06CC_U06C1.json') + end) + + it("can take codepoints, font and an options table with script, language and direction settings. #mac", function() + local buf = harfbuzz.Buffer.new() + local korean_text = { 0xAC00, 0xB098, 0xB2E4 } + buf:add_codepoints(korean_text) + + local face_korean = harfbuzz.Face.new('/Library/Fonts/AppleGothic.ttf') + local font_korean = harfbuzz.Font.new(face_korean) + + harfbuzz.shape(font_korean, buf, { language = harfbuzz.Language.new("KOR"), script = harfbuzz.Script.new("hang"), direction = harfbuzz.Direction.HB_DIRECTION_LTR }) + local glyphs = buf:get_glyphs() + assert.True(#glyphs > 0) + + -- Compare against output of hb-shape + compare_glyphs_against_fixture(glyphs, 'AppleGothic_korean_issue_22.json') + end) + + it("can take a string containing a comma-delimited list of valid features", function() + local buf = harfbuzz.Buffer.new() + buf:add_utf8(urdu_text) + + harfbuzz.shape(font, buf, { language = harfbuzz.Language.new("urd"), script = harfbuzz.Script.new("Arab"), direction = harfbuzz.Direction.HB_DIRECTION_RTL, features = "+kern,smcp" }) + local glyphs = buf:get_glyphs() + assert.True(#glyphs > 0) + end) + + describe("features option", function() + local buf + local options + + before_each(function() + buf= harfbuzz.Buffer.new() + buf:add_utf8(urdu_text) + options = { language = harfbuzz.Language.new("urd"), script = harfbuzz.Script.new("Arab"), direction = harfbuzz.Direction.HB_DIRECTION_RTL } + end) + + it("can take a table containing a valid features", function() + options.features = { + harfbuzz.Feature.new('+kern'), + harfbuzz.Feature.new('smcp') + } + + harfbuzz.shape(font, buf, options) + local glyphs = buf:get_glyphs() + assert.True(#glyphs > 0) + end) + + it("throws an error if feature string is invalid", function() + options.features = "#kern" + assert.has_error(function() + harfbuzz.shape(font, buf, options) + end, "Invalid feature string: '#kern'") + end) + + it("throws an error if feature option is not a table or string", function() + options.features = 25 + assert.has_error(function() + harfbuzz.shape(font, buf, options) + end, "Invalid features option") + end) + + it("throws an error if features table does not contain a feature", function() + options.features = { + harfbuzz.Feature.new('+kern'), + 25, + harfbuzz.Feature.new('smcp') + } + assert.has_error(function() + harfbuzz.shape(font, buf, options) + end, "bad argument #-1 to 'shape_full' (harfbuzz.Feature expected, got number)") + end) + + it("shapes a string appropriately with the features turned on",function() + buf= harfbuzz.Buffer.new() + buf:add_utf8("123") + local opts = { language = harfbuzz.Language.new("eng"), script = harfbuzz.Script.new("Latn"), direction = harfbuzz.Direction.HB_DIRECTION_LTR } + local amiri_face = harfbuzz.Face.new('fonts/amiri-regular.ttf') + local amiri_font = harfbuzz.Font.new(amiri_face) + + -- Check normal shaping w/o features + buf= harfbuzz.Buffer.new() + buf:add_utf8("123") + + harfbuzz.shape(amiri_font, buf, opts) + local glyphs = buf:get_glyphs() + compare_glyphs_against_fixture(glyphs, "amiri-regular_123.json") + + -- Check shaping with '+numr' + buf= harfbuzz.Buffer.new() + buf:add_utf8("123") + opts.features = "+numr" + harfbuzz.shape(amiri_font, buf, opts) + glyphs = buf:get_glyphs() + compare_glyphs_against_fixture(glyphs, "amiri-regular_123_numr.json") + end) + + it("can set specefic shaper",function() + options.shapers = { "fallback"} + harfbuzz.shape(font, buf, options) + local glyphs = buf:get_glyphs() + assert.are_equal(2, #glyphs) + assert.are_equal(906, glyphs[1].codepoint) + assert.are_equal(909, glyphs[2].codepoint) + end) + end) +end) diff --git a/luaharfbuzz/src/harfbuzz.lua b/luaharfbuzz/src/harfbuzz.lua new file mode 100644 index 0000000..b7b0d7f --- /dev/null +++ b/luaharfbuzz/src/harfbuzz.lua @@ -0,0 +1,84 @@ +local hb = require("luaharfbuzz") + +-- special tags +hb.Tag.NONE = hb.Tag.new() + +-- special script codes (ISO 15924) +hb.Script.COMMON = hb.Script.new("Zyyy") +hb.Script.INHERITED = hb.Script.new("Zinh") +hb.Script.UNKNOWN = hb.Script.new("Zzzz") +hb.Script.INVALID = hb.Script.from_iso15924_tag(hb.Tag.NONE) + +-- directions +hb.Direction.INVALID = hb.Direction.new("invalid") +hb.Direction.LTR = hb.Direction.new("ltr") +hb.Direction.RTL = hb.Direction.new("rtl") +hb.Direction.TTB = hb.Direction.new("ttb") +hb.Direction.BTT = hb.Direction.new("btt") + +-- special languages +hb.Language.INVALID = hb.Language.new() + +hb.shape = function(font, buf, options) + options = options or { } + + -- Apply options to buffer if they are set. + if options.language then buf:set_language(options.language) end + if options.script then buf:set_script(options.script) end + if options.direction then buf:set_direction(options.direction) end + + -- Guess segment properties, in case all steps above have failed + -- to set the right properties. + buf:guess_segment_properties() + + local features = {} + + -- Parse features + if type(options.features) == "string" then + for fs in string.gmatch(options.features, '([^,]+)') do + local feature = hb.Feature.new(fs) + if feature then + table.insert(features, hb.Feature.new(fs)) + else + error(string.format("Invalid feature string: '%s'", fs)) + end + end + elseif type(options.features) == "table" then + features = options.features + elseif options.features then -- non-nil but not a string or table + error("Invalid features option") + end + + return hb.shape_full(font,buf,features,options.shapers or {}) +end + +-- For backward compatibility +hb.Buffer.HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES = hb.Buffer.CLUSTER_LEVEL_MONOTONE_GRAPHEMES +hb.Buffer.HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS = hb.Buffer.CLUSTER_LEVEL_MONOTONE_CHARACTERS +hb.Buffer.HB_BUFFER_CLUSTER_LEVEL_CHARACTERS = hb.Buffer.CLUSTER_LEVEL_CHARACTERS +hb.Buffer.HB_BUFFER_CLUSTER_LEVEL_DEFAULT = hb.Buffer.CLUSTER_LEVEL_DEFAULT + +hb.Tag.HB_TAG_NONE = hb.Tag.NONE + +hb.Script.HB_SCRIPT_COMMON = hb.Script.COMMON +hb.Script.HB_SCRIPT_INHERITED = hb.Script.INHERITED +hb.Script.HB_SCRIPT_UNKNOWN = hb.Script.UNKNOWN +hb.Script.HB_SCRIPT_INVALID = hb.Script.INVALID + +hb.Language.HB_LANGUAGE_INVALID = hb.Language.INVALID + +hb.Direction.HB_DIRECTION_INVALID = hb.Direction.INVALID +hb.Direction.HB_DIRECTION_LTR = hb.Direction.LTR +hb.Direction.HB_DIRECTION_RTL = hb.Direction.RTL +hb.Direction.HB_DIRECTION_TTB = hb.Direction.TTB +hb.Direction.HB_DIRECTION_BTT = hb.Direction.BTT + +hb.Direction.HB_DIRECTION_IS_VALID = hb.Direction.is_valid +hb.Direction.HB_DIRECTION_IS_HORIZONTAL = hb.Direction.is_horizontal +hb.Direction.HB_DIRECTION_IS_VERTICAL = hb.Direction.is_vertical +hb.Direction.HB_DIRECTION_IS_FORWARD = hb.Direction.is_forward +hb.Direction.HB_DIRECTION_IS_BACKWARD = hb.Direction.is_backward + +hb.Buffer.get_glyph_infos_and_positions = hb.Buffer.get_glyphs + +return hb diff --git a/luaharfbuzz/src/harfbuzz.luadoc b/luaharfbuzz/src/harfbuzz.luadoc new file mode 100644 index 0000000..3a30a03 --- /dev/null +++ b/luaharfbuzz/src/harfbuzz.luadoc @@ -0,0 +1,535 @@ +----------- +-- Lua bindings to Harfbuzz. +-- * [Wiki](http://github.com/ufytex/luaharfbuzz/wiki) +-- * [Source on Github](https://github.com/ufytex/luaharfbuzz) +-- * [API Coverage Status](https://github.com/ufytex/luaharfbuzz/blob/master/status/done.txt) +-- +-- @author Deepak Jois <> +-- @copyright 2016 +-- @license MIT +-- @module harfbuzz + +--- Wraps `hb_version` +-- @function version + +--- Wraps `hb_shape`. +-- @param font `Font` to use for shaping +-- +-- @param buffer `Buffer` to shape +-- +-- @param[opt] options table containing one or more supported options: +-- +-- * `direction`: A `Direction` object representing the object. +-- * `script`: A `Script` object representing the script. +-- * `language`: A `Language` object representing the language. +-- * `features`: features to enable, specified as either of the following. +-- - comma-separated list of features. See [feature string syntax reference](https://github.com/ufytex/luaharfbuzz/wiki/Feature-Strings) +-- - table of `Feature` objects +-- @function shape + +--- Lua wrapper for `hb_blob_t` type +-- @type Blob + +--- Wraps `hb_blob_create`. +-- Initializes a new `hb_blob_t`. +-- @param data lua string containing binary or character data. +-- @function Blob.new + +--- Wraps `hb_blob_create_from_file`. +-- Initializes a new `hb_blob_t`. +-- @param filename lua string. +-- @function Blob.new_from_file + +--- Wraps `hb_blob_get_length`. +-- @function Blob:get_length + +--- Wraps `hb_blob_get_data`. +-- @function Blob:get_data + +--- Lua wrapper for `hb_face_t` type +-- @type Face + +--- Wraps `hb_face_create`. +-- Initializes a new `hb_face_t` from a `Blob` object. +-- @param blob `Blob` to read the font from. +-- @param[opt=0] font_index index of font to read. +-- @function Face.new_from_blob + +--- Create a new `Face` from a file. +-- Makes a call to `Face:new_from_blob` after creating a `Blob` from the +-- file contents. +-- @param file path to font file. +-- @param[opt=0] font_index index of font to read. +-- @function Face.new + +--- Wraps `hb_face_collect_unicodes`. +-- @return table of codepoints supported by the face. +-- @function Face:collect_unicodes + +--- Wraps `hb_face_get_glyph_count`. +-- @function Face:get_glyph_count + +--- Wraps `hb_face_reference_table`. +-- @param tag `Tag` object of the table. +-- @return `Blob` object for the face table of `tag`. +-- @function Face:get_table + +--- Wraps `hb_face_get_table_tags`. +-- @return table of `Tag`s representing face table tags. +-- @function Face:get_table_tags + +--- Wraps `hb_face_get_upem`. +-- @function Face:get_upem + +--- Wraps `hb_ot_color_has_palettes`. +-- @function Face:ot_color_has_palettes + +--- Wraps `hb_ot_color_palette_get_count`. +-- @function Face:ot_color_palette_get_count + +--- Wraps `hb_ot_color_palette_get_colors`. +-- @function Face:ot_color_palette_get_colors + +--- Wraps `hb_ot_color_has_layers`. +-- @function Face:ot_color_has_layers + +--- Wraps `hb_ot_color_glyph_get_layers`. +-- @function Face:ot_color_glyph_get_layers + +--- Wraps `hb_ot_color_has_png`. +-- @function Face:ot_color_has_png + +--- Wraps `hb_ot_layout_table_get_script_tags`. +-- @function Face:ot_layout_get_script_tags + +--- Wraps `hb_ot_layout_script_get_language_tags`. +-- @function Face:ot_layout_get_language_tags + +--- Wraps `hb_ot_layout_language_get_feature_tags`. +-- @function Face:ot_layout_get_feature_tags + +--- Wraps `hb_ot_layout_table_find_script`. +-- @function Face:ot_layout_find_script + +--- Wraps `hb_ot_layout_script_find_language`. +-- @function Face:ot_layout_find_language + +--- Wraps `hb_ot_layout_language_find_feature`. +-- @function Face:ot_layout_find_feature + + +--- Lua wrapper for `hb_font_t` type +-- @type Font + +--- Wraps `hb_font_create`, and sets up some defaults for scale and shaping functions. +-- Initializes a new `hb_font_t` from a `Face` object. Sets the default scale +-- to the face’s upem value, and sets the font shaping functions by +-- calling `hb_ot_font_set_funcs` on it. +-- @param face `Face` object. +-- @function Font.new + +--- Wraps `hb_font_get_scale`. +-- @return two values for the x-scale and y-scale of the font. +-- @function Font:get_scale + +--- Wraps `hb_font_set_scale`. +-- @param x_scale desired x-scale of font. +-- @param y_scale desired y-scale of font. +-- @function Font:set_scale + +--- Wraps `hb_font_get_h_extents`. +-- @return font extents table for horizontal direction, contains the following +-- or `nil` if HarfBuzz fails to load font extents: +-- +-- * `ascender`: typographic ascender. +-- * `descender`: typographic descender. +-- * `line_gap`: line spacing gap. +-- @function Font:get_h_extents + +--- Wraps `hb_font_get_v_extents`. +-- @return font extents table for vertical direction, similar to +-- `Font:get_h_extents`, or `nil` if HarfBuzz fails to load font extents: +-- @function Font:get_v_extents + +--- Wraps `hb_font_get_glyph_extents`. +-- @param glyph index inside the font. +-- @return extents table contains the following or `nil` if HarfBuzz fails to +-- load glyph extents: +-- +-- * `x_bearing`: left side of glyph from origin. +-- * `y_bearing`: top side of glyph from origin. +-- * `width`: distance from left to right side. +-- * `height`: distance from top to bottom side. +-- @function Font:get_glyph_extents + +--- Wraps `hb_font_get_glyph_name`. +-- @param glyph index inside the font. +-- @return name of the glyph or nil. +-- @function Font:get_glyph_name + +--- Wraps `hb_font_get_glyph_from_name`. +-- @param name of the glyph. +-- @return glyph index inside the font or nil. +-- @function Font:get_glyph_from_name + +--- Wraps `hb_font_get_glyph_h_advance`. +-- @param glyph index inside the font. +-- @return advance glyph advance of the glyph in horizontal direction. +-- @function Font:get_glyph_h_advance + +--- Wraps `hb_font_get_glyph_v_advance`. +-- @param glyph index inside the font. +-- @return advance glyph advance of the glyph in vertical direction. +-- @function Font:get_glyph_v_advance + +--- Wraps `hb_font_get_nominal_glyph`. +-- @param codepoint. +-- @return glyph index or `nil` if `codepoint` is not supported by the font. +-- @function Font:get_nominal_glyph + +--- Wraps `hb_ot_color_glyph_get_png`. +-- @function Font:ot_color_glyph_get_png + +--- Lua wrapper for `hb_buffer_t` type. +-- @type Buffer + +--- Wraps `hb_buffer_create`. +-- @function Buffer.new + +--- Wraps `hb_buffer_add_utf8`. +-- @param text UTF8 encoded string. +-- @param[opt=0] item_offset 0-indexed offset in `text`, from where to start adding. +-- @param[opt=-1] item_length length to add from `item_offset`. `-1` adds till end of `text`. +-- @function Buffer:add_utf8 + +--- Wraps `hb_buffer_add_codepoints`. +-- @param text table with codepoints as lua numbers. +-- @param[opt=0] item_offset 0-indexed offset in `text`, from where to start adding. +-- @param[opt=-1] item_length length to add from `item_offset`. `-1` adds till end of `text`. +-- @function Buffer:add_codepoints + +--- Wraps `hb_buffer_set_direction`. +-- @param dir A `Direction` object. +-- @function Buffer:set_direction + +--- Wraps `hb_buffer_get_direction`. +-- @return A `Direction` object. +-- @function Buffer:get_direction + +--- Wraps `hb_buffer_set_script`. +-- @param script A `Script` object. +-- @function Buffer:set_script + +--- Wraps `hb_buffer_get_script`. +-- @return A `Script` object. +-- @function Buffer:get_script + +--- Wraps `hb_buffer_set_language`. +-- @param lang A `Language` object +-- @function Buffer:set_language + +--- Wraps `hb_buffer_get_language`. +-- @return A `Language` object +-- @function Buffer:get_language + +--- Wraps `hb_buffer_reverse`. +-- @function Buffer:reverse + +--- Wraps `hb_buffer_get_length`. +-- @function Buffer:get_length + +--- Wraps `hb_buffer_get_cluster_level`. +-- @return see [Cluster Levels](#Cluster_Levels) +-- @function Buffer:get_cluster_level + +--- Wraps `hb_buffer_set_cluster_level`. +-- @param level see [Cluster Levels](#Cluster_Levels) +-- @function Buffer:set_cluster_level + +--- Wraps `hb_buffer_guess_segment_properties`. +-- @function Buffer:guess_segment_properties + +--- Helper method to get shaped glyph data. +-- Calls `hb_buffer_get_glyph_infos`, `hb_buffer_get_glyph_positions` and +-- `hb_glyph_info_get_glyph_flags`, and assembles the data into a Lua table. +-- @return table containing data for each glyph, in a nested table. Each nested +-- table contains the following: +-- +-- * `x_advance`: horizontal advance. +-- * `y_advance`: vertical advance. +-- * `x_offset`: horizontal displacement. +-- * `y_offset`: vertical displacement. +-- * `cluster`: glyph cluster index within input. +-- * `codepoint`: glyph index inside the font _(this field name is a bit misleading, but that’s what Harfbuzz uses)_. +-- * `flags`: glyph flags +-- @function Buffer:get_glyphs + +--- Cluster Levels. +-- See [Harfbuzz docs](http://behdad.github.io/harfbuzz/clusters.html) for more details +-- about what each of these levels mean. +-- @section + +--- Wraps `HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES`. +-- @field Buffer.CLUSTER_LEVEL_MONOTONE_GRAPHEMES + +--- Wraps `HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS`. +-- @field Buffer.CLUSTER_LEVEL_MONOTONE_CHARACTERS + +--- Wraps `HB_BUFFER_CLUSTER_LEVEL_CHARACTERS`. +-- @field Buffer.CLUSTER_LEVEL_CHARACTERS + +--- Wraps `HB_BUFFER_CLUSTER_LEVEL_DEFAULT`. +-- @field Buffer.CLUSTER_LEVEL_DEFAULT + +--- Wraps `HB_GLYPH_FLAG_UNSAFE_TO_BREAK`. +-- @field Buffer.GLYPH_FLAG_UNSAFE_TO_BREAK + +--- Wraps `HB_GLYPH_FLAG_DEFINED`. +-- @field Buffer.GLYPH_FLAG_DEFINED + +--- Lua wrapper for `hb_feature_t` type +-- @type Feature + +--- Wraps `hb_feature_from_string` +-- @param feature_string See [feature string syntax reference](https://github.com/ufytex/luaharfbuzz/wiki/Feature-Strings) +-- @function Feature.new + +--- Wraps `hb_feature_to_string`. +-- Enables nice output with `tostring(…)`. +-- @function Feature:__tostring + +--- Lua wrapper for `hb_tag_t` type. +-- @type Tag + +--- Wraps `hb_tag_from_string`. +-- @param string to be converted to a `Tag` object. +-- @return a `Tag` object. +-- @function Tag.new + +--- Wraps `hb_tag_to_string`. Enable nice output with `tostring(…)`. +-- @return Returns a string representation for the tag object. +-- @function Tag:__to_string + +--- Enables equality comparisions with `==` between two tags. +-- @return `true` or `false` depending on whether the two tags are equal. +-- @function Tag:__eq + + +--- Lua wrapper for `hb_script_t` type. +-- @type Script + +--- Wraps `hb_script_from_string`. +-- @param script 4-letter script code according to the [ISO 15924 standard](http://www.unicode.org/iso15924/iso15924-num.html). +-- @return a `Script` object. +-- @function Script.new + +--- Wraps `hb_script_from_iso15924_tag` +-- @param tag a `Tag` object representing a [ISO 15924 script](http://www.unicode.org/iso15924/iso15924-num.html). +-- @function Script.from_iso15924_tag + +--- Wraps `hb_script_to_iso15924_tag`. +-- @return a `Tag` object representing the script. +-- @function Script:to_iso15924_tag + +--- Enable nice output with `tostring(…)` +-- @return Returns a 4-letter [ISO 15924 script code](http://www.unicode.org/iso15924/iso15924-num.html) for the script object. +-- @function Script:__to_string + +--- Enables equality comparisions with `==` between two scripts. +-- @return `true` or `false` depending on whether the two scripts are equal. +-- @function Script:__eq + +--- Predefined Script Codes. +-- Predefined directions that correspond to their original definitions in Harfbuzz. +-- @section + +--- Wraps `HB_SCRIPT_COMMON`. +-- @field Script.COMMON + +--- Wraps `HB_SCRIPT_INHERITED`. +-- @field Script.INHERITED + +--- Wraps `HB_SCRIPT_UNKNOWN`. +-- @field Script.UNKNOWN + +--- Wraps `HB_SCRIPT_INVALID`. +-- @field Script.INVALID + +--- Lua wrapper for `hb_direction_t` type. +-- @type Direction + +--- Wraps `hb_direction_from_string`. +-- @param dir can be one of `ltr`, `rtl`, `ttb`, `btt` or `invalid`. +-- @return a `Direction` object. +-- @function Direction.new + +--- Wraps `hb_direction_to_string`. Enable nice output with `tostring(…)`. +-- @return Returns a string representation for direction. +-- @function Direction:__to_string + +--- Enables equality comparisions with `==` between two directions. +-- @return `true` or `false` depending on whether the two tags are equal. +-- @function Direction:__eq + +--- Wraps `HB_DIRECTION_IS_VALID`. +-- @return a boolean value +-- @function Direction:is_valid + +--- Wraps `HB_DIRECTION_IS_HORIZONTAL`. +-- @return a boolean value +-- @function Direction:is_horizontal + +--- Wraps `HB_DIRECTION_IS_VERTICAL`. +-- @return a boolean value +-- @function Direction:is_vertical + +--- Wraps `HB_DIRECTION_IS_FORWARD`. +-- @return a boolean value +-- @function Direction:is_forward + +--- Wraps `HB_DIRECTION_IS_BACKWARD`. +-- @return a boolean value +-- @function Direction:is_backward + +--- Predefined directions. +-- Predefined directions that correspond to their original definitions in Harfbuzz. +-- @section + +--- Wraps `HB_DIRECTION_LTR`. +-- @field Direction.LTR + +--- Wraps `HB_DIRECTION_RTL`. +-- @field Direction.RTL + +--- Wraps `HB_DIRECTION_TTB`. +-- @field Direction.TTB + +--- Wraps `HB_DIRECTION_LTR`. +-- @field Direction.BTT + +--- Lua wrapper for `hb_language_t` type. +-- @type Language + +--- Wraps `hb_language_from_string`. +-- @param lang [three-letter language tag](http://www.microsoft.com/typography/otspec/languagetags.htm) to be converted to a `Language` object. +-- @return a `Language` object. +-- @function Language.new + +--- Wraps `hb_language_to_string`. Enable nice output with `tostring(…)`. +-- @return Returns a string representation for the language object. +-- @function Language:__to_string + +--- Enables equality comparisions with `==` between two languages. +-- @return `true` or `false` depending on whether the two languages are equal. +-- @function Language:__eq + +--- Predefined languages. +-- Predefined languages that correspond to their original definitions in Harfbuzz. +-- @section + +--- Wraps `HB_LANGUAGE_INVALID`. +-- @field Language.INVALID + + +--- Unicode functions. +-- @section + +--- Wraps `hb_unicode_script` +-- @param char Unicode codepoint +-- @return a `Script` object. +-- @function unicode.script + + +--- Predefined Name IDs. +-- Predefined OpenType 'name' table name identifier. +-- @section + +--- Wraps `HB_OT_NAME_ID_COPYRIGHT` +-- @field ot.NAME_ID_COPYRIGHT + +--- Wraps `HB_OT_NAME_ID_FONT_FAMILY` +-- @field ot.NAME_ID_FONT_FAMILY + +--- Wraps `HB_OT_NAME_ID_FONT_SUBFAMILY` +-- @field ot.NAME_ID_FONT_SUBFAMILY + +--- Wraps `HB_OT_NAME_ID_UNIQUE_ID` +-- @field ot.NAME_ID_UNIQUE_ID + +--- Wraps `HB_OT_NAME_ID_FULL_NAME` +-- @field ot.NAME_ID_FULL_NAME + +--- Wraps `HB_OT_NAME_ID_VERSION_STRING` +-- @field ot.NAME_ID_VERSION_STRING + +--- Wraps `HB_OT_NAME_ID_POSTSCRIPT_NAME` +-- @field ot.NAME_ID_POSTSCRIPT_NAME + +--- Wraps `HB_OT_NAME_ID_TRADEMARK` +-- @field ot.NAME_ID_TRADEMARK + +--- Wraps `HB_OT_NAME_ID_MANUFACTURER` +-- @field ot.NAME_ID_MANUFACTURER + +--- Wraps `HB_OT_NAME_ID_DESIGNER` +-- @field ot.NAME_ID_DESIGNER + +--- Wraps `HB_OT_NAME_ID_DESCRIPTION` +-- @field ot.NAME_ID_DESCRIPTION + +--- Wraps `HB_OT_NAME_ID_VENDOR_URL` +-- @field ot.NAME_ID_VENDOR_URL + +--- Wraps `HB_OT_NAME_ID_DESIGNER_URL` +-- @field ot.NAME_ID_DESIGNER_URL + +--- Wraps `HB_OT_NAME_ID_LICENSE` +-- @field ot.NAME_ID_LICENSE + +--- Wraps `HB_OT_NAME_ID_LICENSE_URL` +-- @field ot.NAME_ID_LICENSE_URL + +--- Wraps `HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY` +-- @field ot.NAME_ID_TYPOGRAPHIC_FAMILY + +--- Wraps `HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY` +-- @field ot.NAME_ID_TYPOGRAPHIC_SUBFAMILY + +--- Wraps `HB_OT_NAME_ID_MAC_FULL_NAME` +-- @field ot.NAME_ID_MAC_FULL_NAME + +--- Wraps `HB_OT_NAME_ID_SAMPLE_TEXT` +-- @field ot.NAME_ID_SAMPLE_TEXT + +--- Wraps `HB_OT_NAME_ID_CID_FINDFONT_NAME` +-- @field ot.NAME_ID_CID_FINDFONT_NAME + +--- Wraps `HB_OT_NAME_ID_WWS_FAMILY` +-- @field ot.NAME_ID_WWS_FAMILY + +--- Wraps `HB_OT_NAME_ID_WWS_SUBFAMILY` +-- @field ot.NAME_ID_WWS_SUBFAMILY + +--- Wraps `HB_OT_NAME_ID_LIGHT_BACKGROUND` +-- @field ot.NAME_ID_LIGHT_BACKGROUND + +--- Wraps `HB_OT_NAME_ID_DARK_BACKGROUND` +-- @field ot.NAME_ID_DARK_BACKGROUND + +--- Wraps `HB_OT_NAME_ID_VARIATIONS_PS_PREFIX` +-- @field ot.NAME_ID_VARIATIONS_PS_PREFIX + +--- Wraps `HB_OT_NAME_ID_INVALID` +-- @field ot.NAME_ID_INVALID + +--- Wraps `HB_OT_LAYOUT_NO_SCRIPT_INDEX` +-- @field ot.LAYOUT_NO_SCRIPT_INDEX + +--- Wraps `HB_OT_LAYOUT_NO_FEATURE_INDEX` +-- @field ot.LAYOUT_NO_FEATURE_INDEX + +--- Wraps `HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX` +-- @field ot.LAYOUT_DEFAULT_LANGUAGE_INDEX + +--- Wraps `HB_OT_LAYOUT_NO_VARIATIONS_INDEX` +-- @field ot.LAYOUT_NO_VARIATIONS_INDEX diff --git a/luaharfbuzz/src/luaharfbuzz/blob.c b/luaharfbuzz/src/luaharfbuzz/blob.c new file mode 100644 index 0000000..889f5b1 --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/blob.c @@ -0,0 +1,60 @@ +#include "luaharfbuzz.h" + +static int blob_new(lua_State *L) { + Blob *b; + size_t data_l; + const char *data = luaL_checklstring(L, 1, &data_l); + + b = (Blob *)lua_newuserdata(L, sizeof(*b)); + luaL_getmetatable(L, "harfbuzz.Blob"); + lua_setmetatable(L, -2); + + *b = hb_blob_create(data, data_l, HB_MEMORY_MODE_DUPLICATE, (void*)data, NULL); + return 1; +} + +static int blob_new_from_file(lua_State *L) { + Blob *b; + const char *file_name = luaL_checkstring(L, 1); + + b = (Blob *)lua_newuserdata(L, sizeof(*b)); + luaL_getmetatable(L, "harfbuzz.Blob"); + lua_setmetatable(L, -2); + + *b = hb_blob_create_from_file(file_name); + return 1; +} + +static int blob_get_length(lua_State *L) { + Blob *b = (Blob *)luaL_checkudata(L, 1, "harfbuzz.Blob"); + + lua_pushinteger(L, hb_blob_get_length(*b)); + return 1; +} + +static int blob_get_data(lua_State *L) { + Blob *b = (Blob *)luaL_checkudata(L, 1, "harfbuzz.Blob"); + unsigned int l; + const char *d; + + d = hb_blob_get_data(*b, &l); + lua_pushlstring(L, d, l); + + return 1; +} + +static const struct luaL_Reg blob_methods[] = { + { "get_length", blob_get_length }, + { "get_data", blob_get_data }, + { NULL, NULL } +}; + +static const struct luaL_Reg blob_functions[] = { + { "new", blob_new }, + { "new_from_file", blob_new_from_file }, + { NULL, NULL } +}; + +int register_blob(lua_State *L) { + return register_class(L, "harfbuzz.Blob", blob_methods, blob_functions, NULL); +} diff --git a/luaharfbuzz/src/luaharfbuzz/buffer.c b/luaharfbuzz/src/luaharfbuzz/buffer.c new file mode 100644 index 0000000..5eb12eb --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/buffer.c @@ -0,0 +1,338 @@ +#include "luaharfbuzz.h" + +static int buffer_new(lua_State *L) { + Buffer *b; + + b = (Buffer *)lua_newuserdata(L, sizeof(*b)); + luaL_getmetatable(L, "harfbuzz.Buffer"); + lua_setmetatable(L, -2); + + *b = hb_buffer_create(); + return 1; +} + +static int buffer_guess_segment_properties(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + hb_buffer_guess_segment_properties(*b); + return 0; +} + +static int buffer_get_direction(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + Direction *dp = (Direction *)lua_newuserdata(L, sizeof(*dp)); + luaL_getmetatable(L, "harfbuzz.Direction"); + lua_setmetatable(L, -2); + + *dp = hb_buffer_get_direction(*b); + return 1; +} + +static int buffer_set_direction(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + Direction* dir = (Direction *)luaL_checkudata(L, 2, "harfbuzz.Direction"); + + hb_buffer_set_direction(*b, *dir); + return 0; +} + +static int buffer_get_flags(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + lua_pushinteger(L, hb_buffer_get_flags(*b)); + return 1; +} + +static int buffer_set_flags(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + unsigned int l = luaL_checkinteger(L, 2); + + hb_buffer_set_flags(*b, l); + return 0; +} + +static int buffer_get_language(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + Language *lp = (Language *)lua_newuserdata(L, sizeof(*lp)); + luaL_getmetatable(L, "harfbuzz.Language"); + lua_setmetatable(L, -2); + + *lp = hb_buffer_get_language(*b); + return 1; +} + +static int buffer_set_language(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + Language *lang = (Language *)luaL_checkudata(L, 2, "harfbuzz.Language"); + + hb_buffer_set_language(*b, *lang); + return 0; +} + +static int buffer_get_script(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + Script *sp = (Script *)lua_newuserdata(L, sizeof(*sp)); + luaL_getmetatable(L, "harfbuzz.Script"); + lua_setmetatable(L, -2); + + *sp = hb_buffer_get_script(*b); + return 1; +} + +static int buffer_set_script(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + Script *script = (Script *)luaL_checkudata(L, 2, "harfbuzz.Script"); + + hb_buffer_set_script(*b, *script); + return 0; +} + +static int buffer_get_invisible_glyph(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + lua_pushinteger(L, hb_buffer_get_invisible_glyph(*b)); + return 1; +} + +static int buffer_set_invisible_glyph(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + hb_codepoint_t cp = (hb_codepoint_t) luaL_checkinteger(L, 2); + + hb_buffer_set_invisible_glyph(*b, cp); + return 0; +} + +static int buffer_get_replacement_codepoint(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + lua_pushinteger(L, hb_buffer_get_replacement_codepoint(*b)); + return 1; +} + +static int buffer_set_replacement_codepoint(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + hb_codepoint_t cp = (hb_codepoint_t) luaL_checkinteger(L, 2); + + hb_buffer_set_replacement_codepoint(*b, cp); + return 0; +} + +static int buffer_add(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + hb_codepoint_t c = (hb_codepoint_t) luaL_checkinteger(L, 2); + unsigned int cluster = luaL_checkinteger(L, 3); + + hb_buffer_add(*b, c, cluster); + + return 0; +} + +static int buffer_add_codepoints(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + unsigned int item_offset; + int item_length; + + luaL_checktype(L, 2, LUA_TTABLE); + item_offset = luaL_optinteger(L, 3, 0); + item_length = luaL_optinteger(L, 4, -1); + + int n = lua_rawlen(L, 2); + + hb_codepoint_t *text = (hb_codepoint_t *) malloc(n * sizeof(hb_codepoint_t)); + + for (unsigned int i = 0; i != n; ++i) { + lua_geti(L, 2, i + 1); + hb_codepoint_t c = (hb_codepoint_t) luaL_checkinteger(L, -1); + text[i] = c; + lua_pop(L, 1); + } + + hb_buffer_add_codepoints(*b, text, n, item_offset, item_length); + + free(text); + + return 0; +} + +static int buffer_clear_contents(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + hb_buffer_clear_contents(*b); + return 0; +} + +static int buffer_reset(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + hb_buffer_reset(*b); + return 0; +} + +static int buffer_add_utf8(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + const char *text; + unsigned int item_offset; + int item_length; + + text = luaL_checkstring(L, 2); + item_offset = luaL_optinteger(L, 3, 0); + item_length = luaL_optinteger(L, 4, -1); + + hb_buffer_add_utf8(*b, text, -1, item_offset, item_length); + + return 0; +} + +static int buffer_destroy(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + hb_buffer_destroy(*b); + return 0; +} + +static int buffer_get_glyphs(lua_State *L) { + Buffer *buf = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + // Get glyph info and positions out of buffer + unsigned int len = hb_buffer_get_length(*buf); + hb_glyph_info_t *info = hb_buffer_get_glyph_infos(*buf, NULL); + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(*buf, NULL); + hb_glyph_flags_t flags; + unsigned int i; + + // Create Lua table and push glyph data onto it. + lua_createtable(L, len, 0); // parent table + + for (i = 0; i < len; i++) { + lua_pushinteger(L, i+1); // 1-indexed key parent table + lua_createtable(L, 0, 7); // child table + + lua_pushinteger(L, info[i].codepoint); + lua_setfield(L, -2, "codepoint"); + + lua_pushinteger(L, info[i].cluster); + lua_setfield(L, -2, "cluster"); + + lua_pushnumber(L, pos[i].x_advance); + lua_setfield(L, -2, "x_advance"); + + lua_pushnumber(L, pos[i].y_advance); + lua_setfield(L, -2, "y_advance"); + + lua_pushnumber(L, pos[i].x_offset); + lua_setfield(L, -2, "x_offset"); + + lua_pushnumber(L, pos[i].y_offset); + lua_setfield(L, -2, "y_offset"); + + flags = hb_glyph_info_get_glyph_flags(&(info[i])); + if (flags & HB_GLYPH_FLAG_DEFINED) { + lua_pushnumber(L, flags); + lua_setfield(L, -2, "flags"); + } + + lua_settable(L, -3); // Add child table at index i+1 to parent table + } + + return 1; +} + +static int buffer_reverse(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + hb_buffer_reverse(*b); + return 0; +} + +static int buffer_get_length(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + lua_pushinteger(L, hb_buffer_get_length(*b)); + return 1; +} + +static int buffer_get_cluster_level(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + lua_pushinteger(L, hb_buffer_get_cluster_level(*b)); + return 1; +} + +static int buffer_set_cluster_level(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + unsigned int l = luaL_checkinteger(L, 2); + + hb_buffer_set_cluster_level(*b, l); + return 0; +} + +static int buffer_pre_allocate(lua_State *L) { + Buffer *b = (Buffer *)luaL_checkudata(L, 1, "harfbuzz.Buffer"); + + unsigned int n = luaL_checkinteger(L, 2); + + lua_pushboolean(L, hb_buffer_pre_allocate(*b, n)); + return 1; +} + +static const struct luaL_Reg buffer_methods[] = { + { "__gc", buffer_destroy }, + { "add", buffer_add }, + { "add_utf8", buffer_add_utf8 }, + { "add_codepoints", buffer_add_codepoints }, + { "clear_contents", buffer_clear_contents }, + { "set_direction", buffer_set_direction }, + { "get_direction", buffer_get_direction }, + { "set_flags", buffer_set_flags }, + { "get_flags", buffer_get_flags }, + { "set_language", buffer_set_language }, + { "get_language", buffer_get_language }, + { "set_script", buffer_set_script }, + { "get_script", buffer_get_script }, + { "set_invisible_glyph", buffer_set_invisible_glyph }, + { "get_invisible_glyph", buffer_get_invisible_glyph }, + { "set_replacement_codepoint", buffer_set_replacement_codepoint }, + { "get_replacement_codepoint", buffer_get_replacement_codepoint }, + { "get_glyphs", buffer_get_glyphs }, + { "guess_segment_properties", buffer_guess_segment_properties }, + { "reset", buffer_reset }, + { "reverse", buffer_reverse }, + { "get_length", buffer_get_length }, + { "get_cluster_level", buffer_get_cluster_level }, + { "set_cluster_level", buffer_set_cluster_level }, + { "pre_allocate", buffer_pre_allocate }, + { NULL, NULL } +}; + +static const struct luaL_Reg buffer_functions[] = { + { "new", buffer_new }, + { NULL, NULL } +}; + +static const struct luahb_constant_t buffer_constants[] = { + { "CLUSTER_LEVEL_MONOTONE_GRAPHEMES", HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES }, + { "CLUSTER_LEVEL_MONOTONE_CHARACTERS", HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS }, + { "CLUSTER_LEVEL_CHARACTERS", HB_BUFFER_CLUSTER_LEVEL_CHARACTERS }, + { "CLUSTER_LEVEL_DEFAULT", HB_BUFFER_CLUSTER_LEVEL_DEFAULT }, + { "FLAG_DEFAULT", HB_BUFFER_FLAG_DEFAULT }, + { "FLAG_BOT", HB_BUFFER_FLAG_BOT }, + { "FLAG_EOT", HB_BUFFER_FLAG_EOT }, + { "FLAG_PRESERVE_DEFAULT_IGNORABLES", HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES }, + { "FLAG_REMOVE_DEFAULT_IGNORABLES", HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES }, + { "FLAG_DO_NOT_INSERT_DOTTED_CIRCLE", HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE }, + { "GLYPH_FLAG_UNSAFE_TO_BREAK", HB_GLYPH_FLAG_UNSAFE_TO_BREAK }, + { "GLYPH_FLAG_DEFINED", HB_GLYPH_FLAG_DEFINED }, + { NULL, 0 } +}; + +int register_buffer(lua_State *L) { + return register_class(L, "harfbuzz.Buffer", buffer_methods, buffer_functions, buffer_constants); +} diff --git a/luaharfbuzz/src/luaharfbuzz/class_utils.c b/luaharfbuzz/src/luaharfbuzz/class_utils.c new file mode 100644 index 0000000..b51bef7 --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/class_utils.c @@ -0,0 +1,25 @@ +// Utility functions to create Lua classes. +#include "luaharfbuzz.h" + +int register_class(lua_State *L, const char *name, const luaL_Reg *methods, const luaL_Reg *functions, const luahb_constant_t *constants) { + luaL_newmetatable(L, name); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + + if (constants) { + while (constants->name) { + lua_pushinteger(L, constants->value); + lua_setfield(L, -2, constants->name); + constants++; + } + } + luaL_setfuncs(L, methods, 0); + lua_pop(L, 1); + + lua_newtable(L); + luaL_setfuncs(L, functions, 0); + luaL_getmetatable(L, name); + lua_setmetatable(L, -2); + return 1; +} + diff --git a/luaharfbuzz/src/luaharfbuzz/direction.c b/luaharfbuzz/src/luaharfbuzz/direction.c new file mode 100644 index 0000000..644b3af --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/direction.c @@ -0,0 +1,84 @@ +// harfbuzz.Feature +#include "luaharfbuzz.h" + +static int direction_new(lua_State *L) { + Direction *d; + const char *dir = luaL_checkstring(L, 1); + + d = (Direction *)lua_newuserdata(L, sizeof(*d)); + luaL_getmetatable(L, "harfbuzz.Direction"); + lua_setmetatable(L, -2); + + *d = hb_direction_from_string(dir, -1); + return 1; +} + +static int direction_to_string(lua_State *L) { + Direction* d = (Direction *)luaL_checkudata(L, 1, "harfbuzz.Direction"); + + lua_pushstring(L, hb_direction_to_string(*d)); + return 1; +} + +static int direction_equals(lua_State *L) { + Direction* lhs = (Direction *)luaL_checkudata(L, 1, "harfbuzz.Direction"); + Direction* rhs = (Direction *)luaL_checkudata(L, 2, "harfbuzz.Direction"); + + lua_pushboolean(L, *lhs == *rhs); + return 1; +} + +static int direction_is_valid(lua_State *L) { + Direction* d = (Direction *)luaL_checkudata(L, 1, "harfbuzz.Direction"); + + lua_pushboolean(L, HB_DIRECTION_IS_VALID(*d)); + return 1; +} + +static int direction_is_horizontal(lua_State *L) { + Direction* d = (Direction *)luaL_checkudata(L, 1, "harfbuzz.Direction"); + + lua_pushboolean(L, HB_DIRECTION_IS_HORIZONTAL(*d)); + return 1; +} + +static int direction_is_vertical(lua_State *L) { + Direction* d = (Direction *)luaL_checkudata(L, 1, "harfbuzz.Direction"); + + lua_pushboolean(L, HB_DIRECTION_IS_VERTICAL(*d)); + return 1; +} + +static int direction_is_forward(lua_State *L) { + Direction* d = (Direction *)luaL_checkudata(L, 1, "harfbuzz.Direction"); + + lua_pushboolean(L, HB_DIRECTION_IS_FORWARD(*d)); + return 1; +} + +static int direction_is_backward(lua_State *L) { + Direction* d = (Direction *)luaL_checkudata(L, 1, "harfbuzz.Direction"); + + lua_pushboolean(L, HB_DIRECTION_IS_BACKWARD(*d)); + return 1; +} + +static const struct luaL_Reg direction_methods[] = { + { "__tostring", direction_to_string }, + { "__eq", direction_equals }, + { "is_valid", direction_is_valid }, + { "is_horizontal", direction_is_horizontal }, + { "is_vertical", direction_is_vertical }, + { "is_forward", direction_is_forward }, + { "is_backward", direction_is_backward }, + { NULL, NULL } +}; + +static const struct luaL_Reg direction_functions[] = { + { "new", direction_new }, + { NULL, NULL } +}; + +int register_direction(lua_State *L) { + return register_class(L, "harfbuzz.Direction", direction_methods, direction_functions, NULL); +} diff --git a/luaharfbuzz/src/luaharfbuzz/face.c b/luaharfbuzz/src/luaharfbuzz/face.c new file mode 100644 index 0000000..5538c70 --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/face.c @@ -0,0 +1,466 @@ +#include "luaharfbuzz.h" + +/* Size of static arrays we use to avoid heap allocating memory when reading + * data from HarfBuzz. */ +#define STATIC_ARRAY_SIZE 128 + +static int face_new(lua_State *L) { + Face *f; + hb_blob_t *blob; + hb_face_t *face; + const char *file_name = luaL_checkstring(L, 1); + unsigned int face_index = (unsigned int) luaL_optinteger(L, 2, 0); + + blob = hb_blob_create_from_file(file_name); + face = hb_face_create(blob, face_index); + + if (blob == hb_blob_get_empty() || face == hb_face_get_empty()) { + lua_pushnil(L); + } else { + f = (Face *)lua_newuserdata(L, sizeof(*f)); + *f = face; + luaL_getmetatable(L, "harfbuzz.Face"); + lua_setmetatable(L, -2); + } + return 1; +} + +static int face_new_from_blob(lua_State *L) { + Face *f; + hb_face_t *face; + Blob *blob = luaL_checkudata(L, 1, "harfbuzz.Blob"); + unsigned int face_index = (unsigned int) luaL_optinteger(L, 2, 0); + + face = hb_face_create(*blob, face_index); + + if (*blob == hb_blob_get_empty() || face == hb_face_get_empty()) { + lua_pushnil(L); + } else { + f = (Face *)lua_newuserdata(L, sizeof(*f)); + *f = face; + luaL_getmetatable(L, "harfbuzz.Face"); + lua_setmetatable(L, -2); + } + return 1; +} + +static int face_get_glyph_count(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + + lua_pushinteger(L, hb_face_get_glyph_count(*f)); + return 1; +} + +static int face_get_name(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + hb_ot_name_id_t name_id = (hb_ot_name_id_t) luaL_checkinteger(L, 2); + hb_language_t lang = HB_LANGUAGE_INVALID; + char name[STATIC_ARRAY_SIZE]; + unsigned int text_size = STATIC_ARRAY_SIZE, len; + + if (lua_gettop(L) > 2) + lang = *((Language*)luaL_checkudata(L, 3, "harfbuzz.Language")); + + len = hb_ot_name_get_utf8(*f, name_id, lang, &text_size, name); + if (len) { + if (len < STATIC_ARRAY_SIZE) { + lua_pushstring(L, name); + } else { + char *name = malloc(len + 1); + text_size = len + 1; + hb_ot_name_get_utf8(*f, name_id, lang, &text_size, name); + lua_pushstring(L, name); + free(name); + } + } else { + lua_pushnil(L); + } + + return 1; +} + +static int face_get_table(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + Tag *t = (Tag *)luaL_checkudata(L, 2, "harfbuzz.Tag"); + Blob *b; + + b = (Blob *)lua_newuserdata(L, sizeof(*b)); + luaL_getmetatable(L, "harfbuzz.Blob"); + lua_setmetatable(L, -2); + + *b = hb_face_reference_table(*f, *t); + + return 1; +} + +static int face_get_table_tags(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + hb_tag_t tags[STATIC_ARRAY_SIZE]; + unsigned int count = hb_face_get_table_tags(*f, 0, NULL, NULL); + + if (count) { + unsigned int i = 0, offset = 0; + lua_createtable(L, count, 0); + do { + count = STATIC_ARRAY_SIZE; + hb_face_get_table_tags(*f, offset, &count, tags); + for (i = 0; i < count; i++) { + lua_pushnumber(L, offset + i + 1); + + Tag *tp = (Tag *)lua_newuserdata(L, sizeof(*tp)); + luaL_getmetatable(L, "harfbuzz.Tag"); + lua_setmetatable(L, -2); + *tp = tags[i]; + + lua_rawset(L, -3); + } + offset += count; + } while (count == STATIC_ARRAY_SIZE); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int face_ot_layout_get_script_tags(lua_State *L) { + Face *face = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + Tag *table = (Tag *)luaL_checkudata(L, 2, "harfbuzz.Tag"); + + hb_tag_t tags[STATIC_ARRAY_SIZE]; + unsigned int count = hb_ot_layout_table_get_script_tags(*face, *table, 0, NULL, NULL); + + if (count) { + unsigned int i = 0, offset = 0; + lua_createtable(L, count, 0); + do { + count = STATIC_ARRAY_SIZE; + hb_ot_layout_table_get_script_tags(*face, *table, offset, &count, tags); + for (i = 0; i < count; i++) { + lua_pushnumber(L, offset + i + 1); + + Tag *tp = (Tag *)lua_newuserdata(L, sizeof(*tp)); + luaL_getmetatable(L, "harfbuzz.Tag"); + lua_setmetatable(L, -2); + *tp = tags[i]; + + lua_rawset(L, -3); + } + offset += count; + } while (count == STATIC_ARRAY_SIZE); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int face_ot_layout_get_language_tags(lua_State *L) { + Face *face = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + Tag *table = (Tag *)luaL_checkudata(L, 2, "harfbuzz.Tag"); + unsigned int script_index = (unsigned int) luaL_checkinteger(L, 3); + + hb_tag_t tags[STATIC_ARRAY_SIZE]; + unsigned int count = hb_ot_layout_script_get_language_tags(*face, *table, script_index, 0, NULL, NULL); + + if (count) { + unsigned int i = 0, offset = 0; + lua_createtable(L, count, 0); + do { + count = STATIC_ARRAY_SIZE; + hb_ot_layout_script_get_language_tags(*face, *table, script_index, offset, &count, tags); + for (i = 0; i < count; i++) { + lua_pushnumber(L, offset + i + 1); + + Tag *tp = (Tag *)lua_newuserdata(L, sizeof(*tp)); + luaL_getmetatable(L, "harfbuzz.Tag"); + lua_setmetatable(L, -2); + *tp = tags[i]; + + lua_rawset(L, -3); + } + offset += count; + } while (count == STATIC_ARRAY_SIZE); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int face_ot_layout_get_feature_tags(lua_State *L) { + Face *face = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + Tag *table = (Tag *)luaL_checkudata(L, 2, "harfbuzz.Tag"); + unsigned int script_index = (unsigned int) luaL_checkinteger(L, 3); + unsigned int language_index = (unsigned int) luaL_checkinteger(L, 4); + + hb_tag_t tags[STATIC_ARRAY_SIZE]; + unsigned int count = hb_ot_layout_language_get_feature_tags(*face, *table, script_index, language_index, 0, NULL, NULL); + + if (count) { + unsigned int i = 0, offset = 0; + lua_createtable(L, count, 0); + do { + count = STATIC_ARRAY_SIZE; + hb_ot_layout_language_get_feature_tags(*face, *table, script_index, language_index, offset, &count, tags); + for (i = 0; i < count; i++) { + lua_pushnumber(L, offset + i + 1); + + Tag *tp = (Tag *)lua_newuserdata(L, sizeof(*tp)); + luaL_getmetatable(L, "harfbuzz.Tag"); + lua_setmetatable(L, -2); + *tp = tags[i]; + + lua_rawset(L, -3); + } + offset += count; + } while (count == STATIC_ARRAY_SIZE); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int face_ot_layout_find_script(lua_State *L) { + Face *face = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + Tag *table = (Tag *)luaL_checkudata(L, 2, "harfbuzz.Tag"); + Tag *script = (Tag *)luaL_checkudata(L, 3, "harfbuzz.Tag"); + unsigned int index = 0; + + int found = hb_ot_layout_table_find_script(*face, *table, *script, &index); + lua_pushboolean(L, found); + lua_pushinteger(L, index); + return 2; +} + +static int face_ot_layout_find_language(lua_State *L) { + Face *face = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + Tag *table = (Tag *)luaL_checkudata(L, 2, "harfbuzz.Tag"); + unsigned int script_index = (unsigned int) luaL_checkinteger(L, 3); + Tag *language = (Tag *)luaL_checkudata(L, 4, "harfbuzz.Tag"); + unsigned int index = 0; + + int found = hb_ot_layout_script_select_language(*face, *table, script_index, 1, &(*language), &index); + lua_pushboolean(L, found); + lua_pushinteger(L, index); + return 2; +} + +static int face_ot_layout_find_feature(lua_State *L) { + Face *face = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + Tag *table = (Tag *)luaL_checkudata(L, 2, "harfbuzz.Tag"); + unsigned int script_index = (unsigned int) luaL_checkinteger(L, 3); + unsigned int language_index = (unsigned int) luaL_checkinteger(L, 4); + Tag *feature = (Tag *)luaL_checkudata(L, 5, "harfbuzz.Tag"); + unsigned int index = 0; + + int found = hb_ot_layout_language_find_feature(*face, *table, script_index, language_index, *feature, &index); + lua_pushboolean(L, found); + lua_pushinteger(L, index); + return 2; +} + +static int face_collect_unicodes(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + hb_set_t *codes = hb_set_create(); + + hb_face_collect_unicodes (*f, codes); + lua_createtable(L, hb_set_get_population(codes), 0); + if (!hb_set_is_empty(codes)) { + unsigned int i = 0; + hb_codepoint_t c = HB_SET_VALUE_INVALID; + + while (hb_set_next(codes, &c)) { + lua_pushnumber(L, ++i); + lua_pushnumber(L, c); + lua_rawset(L, -3); + } + } + + hb_set_destroy(codes); + + return 1; +} + +static int face_get_upem(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + + lua_pushinteger(L, hb_face_get_upem(*f)); + return 1; +} + +static int face_ot_color_has_palettes(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + + lua_pushboolean(L, hb_ot_color_has_palettes(*f)); + return 1; +} + +static int face_ot_color_palette_get_count(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + + lua_pushinteger(L, hb_ot_color_palette_get_count(*f)); + return 1; +} + +static int face_ot_color_palette_get_colors(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + unsigned int index = (unsigned int) luaL_optinteger(L, 2, 1) - 1; + + hb_color_t colors[STATIC_ARRAY_SIZE]; + unsigned int count = hb_ot_color_palette_get_colors(*f, index, 0, NULL, NULL); + + if (count) { + unsigned int i = 0, offset = 0; + lua_createtable(L, count, 0); // parent table + do { + count = STATIC_ARRAY_SIZE; + hb_ot_color_palette_get_colors(*f, index, offset, &count, colors); + for (i = 0; i < count; i++) { + hb_color_t color = colors[i]; + + lua_pushnumber(L, offset + i + 1); // 1-indexed key parent table + lua_createtable(L, 0, 4); // child table + + lua_pushinteger(L, hb_color_get_red(color)); + lua_setfield(L, -2, "red"); + + lua_pushinteger(L, hb_color_get_green(color)); + lua_setfield(L, -2, "green"); + + lua_pushinteger(L, hb_color_get_blue(color)); + lua_setfield(L, -2, "blue"); + + lua_pushinteger(L, hb_color_get_alpha(color)); + lua_setfield(L, -2, "alpha"); + + lua_settable(L, -3); // Add child table at index i+1 to parent table + } + offset += count; + } while (count == STATIC_ARRAY_SIZE); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int face_ot_color_has_layers(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + + lua_pushboolean(L, hb_ot_color_has_layers(*f)); + return 1; +} + +static int face_ot_color_glyph_get_layers(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + hb_codepoint_t gid = (hb_codepoint_t) luaL_checkinteger(L, 2); + hb_ot_color_layer_t layers[STATIC_ARRAY_SIZE]; + unsigned int count = hb_ot_color_glyph_get_layers(*f, gid, 0, NULL, NULL); + + if (count) { + unsigned int i = 0, offset = 0; + lua_createtable(L, count, 0); // parent table + do { + count = STATIC_ARRAY_SIZE; + hb_ot_color_glyph_get_layers(*f, gid, offset, &count, layers); + for (i = 0; i < count; i++) { + hb_ot_color_layer_t layer = layers[i]; + unsigned int color_index = layer.color_index; + if (color_index != 0xFFFF) + color_index++; // make it 1-indexed + + lua_pushnumber(L, offset + i + 1); // 1-indexed key parent table + lua_createtable(L, 0, 2); // child table + + lua_pushinteger(L, layer.glyph); + lua_setfield(L, -2, "glyph"); + + lua_pushinteger(L, color_index); + lua_setfield(L, -2, "color_index"); + + lua_settable(L, -3); // Add child table at index i+1 to parent table + } + offset += count; + } while (count == STATIC_ARRAY_SIZE); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int face_ot_color_has_png(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + + lua_pushboolean(L, hb_ot_color_has_png(*f)); + return 1; +} + +static int face_ot_color_has_svg(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + + lua_pushboolean(L, hb_ot_color_has_svg(*f)); + return 1; +} + +static int face_ot_color_glyph_get_svg(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + hb_codepoint_t gid = (hb_codepoint_t) luaL_checkinteger(L, 2); + hb_blob_t* blob = hb_ot_color_glyph_reference_svg(*f, gid); + + if (hb_blob_get_length(blob) != 0) { + Blob *b = (Blob *)lua_newuserdata(L, sizeof(*b)); + luaL_getmetatable(L, "harfbuzz.Blob"); + lua_setmetatable(L, -2); + + *b = blob; + } else { + lua_pushnil(L); + } + + return 1; +} + +static int face_destroy(lua_State *L) { + Face *f = (Face *)luaL_checkudata(L, 1, "harfbuzz.Face"); + + hb_face_destroy(*f); + return 0; +} + +static const struct luaL_Reg face_methods[] = { + { "__gc", face_destroy }, + { "collect_unicodes", face_collect_unicodes }, + { "get_glyph_count", face_get_glyph_count }, + { "get_name", face_get_name }, + { "get_table", face_get_table }, + { "get_table_tags", face_get_table_tags }, + { "get_upem", face_get_upem }, + { "ot_color_has_palettes", face_ot_color_has_palettes }, + { "ot_color_palette_get_count", face_ot_color_palette_get_count }, + { "ot_color_palette_get_colors", face_ot_color_palette_get_colors }, + { "ot_color_has_layers", face_ot_color_has_layers }, + { "ot_color_glyph_get_layers", face_ot_color_glyph_get_layers }, + { "ot_color_has_png", face_ot_color_has_png }, + { "ot_color_has_svg", face_ot_color_has_svg }, + { "ot_color_glyph_get_svg", face_ot_color_glyph_get_svg }, + { "ot_layout_get_script_tags", face_ot_layout_get_script_tags }, + { "ot_layout_get_language_tags", face_ot_layout_get_language_tags }, + { "ot_layout_get_feature_tags", face_ot_layout_get_feature_tags }, + { "ot_layout_find_script", face_ot_layout_find_script }, + { "ot_layout_find_language", face_ot_layout_find_language }, + { "ot_layout_find_feature", face_ot_layout_find_feature }, + { NULL, NULL } +}; + +static const struct luaL_Reg face_functions[] = { + { "new", face_new }, + { "new_from_blob", face_new_from_blob }, + { NULL, NULL } +}; + +int register_face(lua_State *L) { + return register_class(L, "harfbuzz.Face", face_methods, face_functions, NULL); +} diff --git a/luaharfbuzz/src/luaharfbuzz/feature.c b/luaharfbuzz/src/luaharfbuzz/feature.c new file mode 100644 index 0000000..d6cd1ec --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/feature.c @@ -0,0 +1,110 @@ +// harfbuzz.Feature +#include "luaharfbuzz.h" + +static int feature_new(lua_State *L) { + Feature f; + const char *feature = luaL_checkstring(L, 1); + + if (hb_feature_from_string(feature, -1, &f)) { + Feature *fp = (Feature *)lua_newuserdata(L, sizeof(*fp)); + luaL_getmetatable(L, "harfbuzz.Feature"); + lua_setmetatable(L, -2); + *fp = f; + } else { + lua_pushnil(L); + } + + return 1; +} + +static int feature_to_string(lua_State *L) { + Feature* f = (Feature *)luaL_checkudata(L, 1, "harfbuzz.Feature"); + char feature[128]; + + hb_feature_to_string(f, feature, 128); + lua_pushstring(L, feature); + return 1; +} + +static const char *feature_tag_ptr; +static const char *feature_value_ptr; +static const char *feature_start_ptr; +static const char *feature_end_ptr; + +static int feature_index(lua_State *L) { + Feature* f = (Feature *)luaL_checkudata(L, 1, "harfbuzz.Feature"); + const char *key = lua_tostring(L, 2); + + if (key == feature_tag_ptr) { + Tag *tag = (Tag *)lua_newuserdata(L, sizeof(*tag)); + luaL_getmetatable(L, "harfbuzz.Tag"); + lua_setmetatable(L, -2); + *tag = f->tag; + } else if (key == feature_value_ptr) { + lua_pushinteger(L, f->value); + } else if (key == feature_start_ptr) { + if (f->start == HB_FEATURE_GLOBAL_START) + lua_pushnil(L); + else + lua_pushinteger(L, f->start); + } else if (key == feature_end_ptr) { + if (f->end == HB_FEATURE_GLOBAL_END) + lua_pushnil(L); + else + lua_pushinteger(L, f->end); + } else { + lua_pushnil(L); + } + return 1; +} + +static int feature_newindex(lua_State *L) { + Feature* f = (Feature *)luaL_checkudata(L, 1, "harfbuzz.Feature"); + const char *key = lua_tostring(L, 2); + + if (key == feature_tag_ptr) { + f->tag = *(Tag *)luaL_checkudata(L, 3, "harfbuzz.Tag"); + } else if (key == feature_value_ptr) { + f->value = luaL_checkinteger(L, 3); + } else if (key == feature_start_ptr) { + if (lua_toboolean(L, 3)) + f->start = luaL_checkinteger(L, 3); + else + f->start = HB_FEATURE_GLOBAL_START; + } else if (key == feature_end_ptr) { + if (lua_toboolean(L, 3)) + f->end = luaL_checkinteger(L, 3); + else + f->end = HB_FEATURE_GLOBAL_END; + } + return 0; +} + +static const struct luaL_Reg feature_methods[] = { + { "__index", feature_index }, + { "__newindex", feature_newindex }, + { "__tostring", feature_to_string }, + { NULL, NULL }, +}; + +static const struct luaL_Reg feature_functions[] = { + { "new", feature_new }, + { NULL, NULL } +}; + +int register_feature(lua_State *L) { + lua_pushliteral(L, "tag"); + feature_tag_ptr = lua_tostring(L, -1); + (void) luaL_ref (L, LUA_REGISTRYINDEX); + lua_pushliteral(L, "value"); + feature_value_ptr = lua_tostring(L, -1); + (void) luaL_ref (L, LUA_REGISTRYINDEX); + lua_pushliteral(L, "start"); + feature_start_ptr = lua_tostring(L, -1); + (void) luaL_ref (L, LUA_REGISTRYINDEX); + lua_pushliteral(L, "_end"); /* _end instead of end to avoid Lua keyword */ + feature_end_ptr = lua_tostring(L, -1); + (void) luaL_ref (L, LUA_REGISTRYINDEX); + + return register_class(L, "harfbuzz.Feature", feature_methods, feature_functions, NULL); +} diff --git a/luaharfbuzz/src/luaharfbuzz/font.c b/luaharfbuzz/src/luaharfbuzz/font.c new file mode 100644 index 0000000..c601673 --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/font.c @@ -0,0 +1,218 @@ +#include "luaharfbuzz.h" + +static int font_new(lua_State *L) { + Font *f; + Face *face = luaL_checkudata(L, 1, "harfbuzz.Face"); + + f = (Font *)lua_newuserdata(L, sizeof(*f)); + luaL_getmetatable(L, "harfbuzz.Font"); + lua_setmetatable(L, -2); + + *f = hb_font_create(*face); + + // Set default scale to be the face's upem value + unsigned int upem = hb_face_get_upem(*face); + hb_font_set_scale(*f, upem, upem); + + // Set shaping functions to OpenType functions + hb_ot_font_set_funcs(*f); + return 1; +} + +static int font_set_scale(lua_State *L) { + Font *f = (Font *)luaL_checkudata(L, 1, "harfbuzz.Font"); + int x_scale = luaL_checkinteger(L, 2); + int y_scale = luaL_checkinteger(L, 3); + + hb_font_set_scale(*f, x_scale, y_scale); + return 0; +} + +static int font_get_scale(lua_State *L) { + Font *f = (Font *)luaL_checkudata(L, 1, "harfbuzz.Font"); + int x_scale, y_scale; + + hb_font_get_scale(*f, &x_scale, &y_scale); + + lua_pushinteger(L, x_scale); + lua_pushinteger(L, y_scale); + return 2; +} + +static int font_get_h_extents(lua_State *L) { + Font *f = (Font *)luaL_checkudata(L, 1, "harfbuzz.Font"); + hb_font_extents_t extents; + + if (hb_font_get_h_extents(*f, &extents)) { + lua_createtable(L, 0, 3); + + lua_pushnumber(L, extents.ascender); + lua_setfield(L, -2, "ascender"); + + lua_pushnumber(L, extents.descender); + lua_setfield(L, -2, "descender"); + + lua_pushnumber(L, extents.line_gap); + lua_setfield(L, -2, "line_gap"); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int font_get_v_extents(lua_State *L) { + Font *f = (Font *)luaL_checkudata(L, 1, "harfbuzz.Font"); + hb_font_extents_t extents; + + if (hb_font_get_v_extents(*f, &extents)) { + lua_createtable(L, 0, 3); + + lua_pushnumber(L, extents.ascender); + lua_setfield(L, -2, "ascender"); + + lua_pushnumber(L, extents.descender); + lua_setfield(L, -2, "descender"); + + lua_pushnumber(L, extents.line_gap); + lua_setfield(L, -2, "line_gap"); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int font_get_glyph_extents(lua_State *L) { + Font *f = (Font *)luaL_checkudata(L, 1, "harfbuzz.Font"); + hb_codepoint_t glyph = luaL_checkinteger(L, 2); + hb_glyph_extents_t extents; + + if (hb_font_get_glyph_extents(*f, glyph, &extents)) { + lua_createtable(L, 0, 4); + + lua_pushnumber(L, extents.x_bearing); + lua_setfield(L, -2, "x_bearing"); + + lua_pushnumber(L, extents.y_bearing); + lua_setfield(L, -2, "y_bearing"); + + lua_pushnumber(L, extents.width); + lua_setfield(L, -2, "width"); + + lua_pushnumber(L, extents.height); + lua_setfield(L, -2, "height"); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int font_get_glyph_name(lua_State *L) { + Font *f = (Font *)luaL_checkudata(L, 1, "harfbuzz.Font"); + hb_codepoint_t glyph = luaL_checkinteger(L, 2); + +#define NAME_LEN 128 + char name[NAME_LEN]; + if (hb_font_get_glyph_name(*f, glyph, name, NAME_LEN)) + lua_pushstring(L, name); + else + lua_pushnil(L); +#undef NAME_LEN + + return 1; +} + +static int font_get_glyph_from_name(lua_State *L) { + Font *f = (Font *)luaL_checkudata(L, 1, "harfbuzz.Font"); + const char *name = luaL_checkstring(L, 2); + hb_codepoint_t glyph; + + if (hb_font_get_glyph_from_name(*f, name, -1, &glyph)) + lua_pushinteger(L, glyph); + else + lua_pushnil(L); + + return 1; +} + +static int font_get_glyph_h_advance(lua_State *L) { + Font *f = (Font *)luaL_checkudata(L, 1, "harfbuzz.Font"); + hb_codepoint_t glyph = luaL_checkinteger(L, 2); + + lua_pushinteger(L, hb_font_get_glyph_h_advance(*f, glyph)); + return 1; +} + +static int font_get_glyph_v_advance(lua_State *L) { + Font *f = (Font *)luaL_checkudata(L, 1, "harfbuzz.Font"); + hb_codepoint_t glyph = luaL_checkinteger(L, 2); + + lua_pushinteger(L, hb_font_get_glyph_v_advance(*f, glyph)); + return 1; +} + +static int font_get_nominal_glyph(lua_State *L) { + Font *f = (Font *)luaL_checkudata(L, 1, "harfbuzz.Font"); + hb_codepoint_t uni = luaL_checkinteger(L, 2); + hb_codepoint_t glyph; + + if (hb_font_get_nominal_glyph(*f, uni, &glyph)) + lua_pushinteger(L, glyph); + else + lua_pushnil(L); + + return 1; +} + + +static int font_destroy(lua_State *L) { + Font *f = (Font *)luaL_checkudata(L, 1, "harfbuzz.Font"); + + hb_font_destroy(*f); + return 0; +} + +static int font_ot_color_glyph_get_png(lua_State *L) { + Font *f = (Font *)luaL_checkudata(L, 1, "harfbuzz.Font"); + hb_codepoint_t gid = (hb_codepoint_t) luaL_checkinteger(L, 2); + hb_blob_t* blob = hb_ot_color_glyph_reference_png(*f, gid); + + if (hb_blob_get_length(blob) != 0) { + Blob *b = (Blob *)lua_newuserdata(L, sizeof(*b)); + luaL_getmetatable(L, "harfbuzz.Blob"); + lua_setmetatable(L, -2); + + *b = blob; + } else { + lua_pushnil(L); + } + + return 1; +} + +static const struct luaL_Reg font_methods[] = { + { "__gc", font_destroy }, + { "set_scale", font_set_scale }, + { "get_scale", font_get_scale }, + { "get_h_extents", font_get_h_extents }, + { "get_v_extents", font_get_v_extents }, + { "get_glyph_extents", font_get_glyph_extents }, + { "get_glyph_name", font_get_glyph_name }, + { "get_glyph_from_name", font_get_glyph_from_name }, + { "get_glyph_h_advance", font_get_glyph_h_advance }, + { "get_glyph_v_advance", font_get_glyph_v_advance }, + { "get_nominal_glyph", font_get_nominal_glyph }, + { "ot_color_glyph_get_png", font_ot_color_glyph_get_png }, + { NULL, NULL } +}; + +static const struct luaL_Reg font_functions[] = { + { "new", font_new }, + { NULL, NULL } +}; + +int register_font(lua_State *L) { + return register_class(L, "harfbuzz.Font", font_methods, font_functions, NULL); +} diff --git a/luaharfbuzz/src/luaharfbuzz/language.c b/luaharfbuzz/src/luaharfbuzz/language.c new file mode 100644 index 0000000..9f397e5 --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/language.c @@ -0,0 +1,49 @@ +// harfbuzz.Feature +#include "luaharfbuzz.h" + +static int language_new(lua_State *L) { + Language *l; + + l = (Language *)lua_newuserdata(L, sizeof(*l)); + luaL_getmetatable(L, "harfbuzz.Language"); + lua_setmetatable(L, -2); + + if (lua_gettop(L) == 1 || lua_isnil(L, -2)) + *l = HB_LANGUAGE_INVALID; + else + *l = hb_language_from_string(luaL_checkstring(L, -2), -1); + + return 1; +} + +static int language_to_string(lua_State *L) { + Language* l = (Language *)luaL_checkudata(L, 1, "harfbuzz.Language"); + const char *s = hb_language_to_string(*l); + + lua_pushstring(L, s ? s : ""); + return 1; +} + +static int language_equals(lua_State *L) { + Language* lhs = (Language *)luaL_checkudata(L, 1, "harfbuzz.Language"); + Language* rhs = (Language *)luaL_checkudata(L, 2, "harfbuzz.Language"); + + lua_pushboolean(L, *lhs == *rhs); + + return 1; +} + +static const struct luaL_Reg language_methods[] = { + { "__tostring", language_to_string }, + { "__eq", language_equals }, + { NULL, NULL } +}; + +static const struct luaL_Reg language_functions[] = { + { "new", language_new }, + { NULL, NULL } +}; + +int register_language(lua_State *L) { + return register_class(L, "harfbuzz.Language", language_methods, language_functions, NULL); +} diff --git a/luaharfbuzz/src/luaharfbuzz/luaharfbuzz.c b/luaharfbuzz/src/luaharfbuzz/luaharfbuzz.c new file mode 100644 index 0000000..fcafc77 --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/luaharfbuzz.c @@ -0,0 +1,102 @@ +#include "luaharfbuzz.h" + +static int shape_full (lua_State *L) { + Font *font = (Font *)luaL_checkudata(L, 1, "harfbuzz.Font"); + Buffer *buf = (Buffer *)luaL_checkudata(L, 2, "harfbuzz.Buffer"); + luaL_checktype(L, 3, LUA_TTABLE); + luaL_checktype(L, 4, LUA_TTABLE); + + unsigned int num_features = lua_rawlen(L, 3); + Feature *features = (Feature *) malloc (num_features * sizeof(hb_feature_t)); + + for (unsigned int i = 0; i != num_features; ++i) { + lua_geti(L, 3, i + 1); + Feature* f = (Feature *)luaL_checkudata(L, -1, "harfbuzz.Feature"); + features[i] = *f; + lua_pop(L, 1); + } + + const char **shapers = NULL; + size_t num_shapers = lua_rawlen(L, 4); + if (num_shapers) { + shapers = (const char**) calloc (num_shapers + 1, sizeof(char*)); + for (unsigned int i = 0; i != num_shapers; ++i) { + lua_geti(L, 4, i + 1); + shapers[i] = luaL_checkstring(L, -1); + lua_pop(L, 1); + } + } + + // Shape text + lua_pushboolean(L, hb_shape_full(*font, *buf, features, num_features, shapers)); + + free(features); + free(shapers); + + return 1; +} + +static int version (lua_State *L) { + lua_pushstring(L, hb_version_string()); + return 1; +} + +static int list_shapers (lua_State *L) { + const char **shaper_list = hb_shape_list_shapers (); + int i = 0; + + for (; *shaper_list; shaper_list++) { + i++; + lua_pushstring(L, *shaper_list); + } + return i; +} + +static const struct luaL_Reg lib_table [] = { + {"shape_full", shape_full}, + {"version", version}, + {"shapers", list_shapers}, + {NULL, NULL} +}; + +int luaopen_luaharfbuzz (lua_State *L) { + lua_newtable(L); + + register_blob(L); + lua_setfield(L, -2, "Blob"); + + register_face(L); + lua_setfield(L, -2, "Face"); + + register_font(L); + lua_setfield(L, -2, "Font"); + + register_buffer(L); + lua_setfield(L, -2, "Buffer"); + + register_feature(L); + lua_setfield(L, -2, "Feature"); + + register_tag(L); + lua_setfield(L, -2, "Tag"); + + register_script(L); + lua_setfield(L, -2, "Script"); + + register_direction(L); + lua_setfield(L, -2, "Direction"); + + register_language(L); + lua_setfield(L, -2, "Language"); + + register_ot(L); + lua_setfield(L, -2, "ot"); + + register_unicode(L); + lua_setfield(L, -2, "unicode"); + + luaL_setfuncs(L, lib_table, 0); + + return 1; +} + diff --git a/luaharfbuzz/src/luaharfbuzz/luaharfbuzz.h b/luaharfbuzz/src/luaharfbuzz/luaharfbuzz.h new file mode 100644 index 0000000..3d9d2e0 --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/luaharfbuzz.h @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +typedef hb_blob_t* Blob; +typedef hb_face_t* Face; +typedef hb_font_t* Font; +typedef hb_buffer_t* Buffer; +typedef hb_feature_t Feature; +typedef hb_tag_t Tag; +typedef hb_script_t Script; +typedef hb_direction_t Direction; +typedef hb_language_t Language; + +typedef struct luahb_constant_t { + const char *name; + int value; +} luahb_constant_t; + +// Functions to create classes and push them onto the stack +int register_class(lua_State *L, const char *name, const luaL_Reg * methods, const luaL_Reg *functions, const luahb_constant_t* constants); +int register_blob(lua_State *L); +int register_face(lua_State *L); +int register_font(lua_State *L); +int register_buffer(lua_State *L); +int register_feature(lua_State *L); +int register_tag(lua_State *L); +int register_script(lua_State *L); +int register_direction(lua_State *L); +int register_language(lua_State *L); +int register_ot(lua_State *L); +int register_unicode(lua_State *L); diff --git a/luaharfbuzz/src/luaharfbuzz/ot.c b/luaharfbuzz/src/luaharfbuzz/ot.c new file mode 100644 index 0000000..01041cf --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/ot.c @@ -0,0 +1,47 @@ +#include "luaharfbuzz.h" + +static const struct luahb_constant_t ot_constants[] = { + { "NAME_ID_COPYRIGHT", HB_OT_NAME_ID_COPYRIGHT }, + { "NAME_ID_FONT_FAMILY", HB_OT_NAME_ID_FONT_FAMILY }, + { "NAME_ID_FONT_SUBFAMILY", HB_OT_NAME_ID_FONT_SUBFAMILY }, + { "NAME_ID_UNIQUE_ID", HB_OT_NAME_ID_UNIQUE_ID }, + { "NAME_ID_FULL_NAME", HB_OT_NAME_ID_FULL_NAME }, + { "NAME_ID_VERSION_STRING", HB_OT_NAME_ID_VERSION_STRING }, + { "NAME_ID_POSTSCRIPT_NAME", HB_OT_NAME_ID_POSTSCRIPT_NAME }, + { "NAME_ID_TRADEMARK", HB_OT_NAME_ID_TRADEMARK }, + { "NAME_ID_MANUFACTURER", HB_OT_NAME_ID_MANUFACTURER }, + { "NAME_ID_DESIGNER", HB_OT_NAME_ID_DESIGNER }, + { "NAME_ID_DESCRIPTION", HB_OT_NAME_ID_DESCRIPTION }, + { "NAME_ID_VENDOR_URL", HB_OT_NAME_ID_VENDOR_URL }, + { "NAME_ID_DESIGNER_URL", HB_OT_NAME_ID_DESIGNER_URL }, + { "NAME_ID_LICENSE", HB_OT_NAME_ID_LICENSE }, + { "NAME_ID_LICENSE_URL", HB_OT_NAME_ID_LICENSE_URL }, + { "NAME_ID_TYPOGRAPHIC_FAMILY", HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY }, + { "NAME_ID_TYPOGRAPHIC_SUBFAMILY", HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY }, + { "NAME_ID_MAC_FULL_NAME", HB_OT_NAME_ID_MAC_FULL_NAME }, + { "NAME_ID_SAMPLE_TEXT", HB_OT_NAME_ID_SAMPLE_TEXT }, + { "NAME_ID_CID_FINDFONT_NAME", HB_OT_NAME_ID_CID_FINDFONT_NAME }, + { "NAME_ID_WWS_FAMILY", HB_OT_NAME_ID_WWS_FAMILY }, + { "NAME_ID_WWS_SUBFAMILY", HB_OT_NAME_ID_WWS_SUBFAMILY }, + { "NAME_ID_LIGHT_BACKGROUND", HB_OT_NAME_ID_LIGHT_BACKGROUND }, + { "NAME_ID_DARK_BACKGROUND", HB_OT_NAME_ID_DARK_BACKGROUND }, + { "NAME_ID_VARIATIONS_PS_PREFIX", HB_OT_NAME_ID_VARIATIONS_PS_PREFIX }, + { "NAME_ID_INVALID", HB_OT_NAME_ID_INVALID }, + { "LAYOUT_NO_SCRIPT_INDEX", HB_OT_LAYOUT_NO_SCRIPT_INDEX }, + { "LAYOUT_NO_FEATURE_INDEX", HB_OT_LAYOUT_NO_FEATURE_INDEX }, + { "LAYOUT_DEFAULT_LANGUAGE_INDEX", HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX }, + { "LAYOUT_NO_VARIATIONS_INDEX", HB_OT_LAYOUT_NO_VARIATIONS_INDEX }, + { NULL, 0 } +}; + +static const struct luaL_Reg ot_methods[] = { + { NULL, NULL } +}; + +static const struct luaL_Reg ot_functions[] = { + { NULL, NULL } +}; + +int register_ot(lua_State *L) { + return register_class(L, "harfbuzz.ot", ot_methods, ot_functions, ot_constants); +} diff --git a/luaharfbuzz/src/luaharfbuzz/script.c b/luaharfbuzz/src/luaharfbuzz/script.c new file mode 100644 index 0000000..133cc6a --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/script.c @@ -0,0 +1,71 @@ +#include "luaharfbuzz.h" + +static int new_script_from_string(lua_State *L) { + Script *sp = (Script *)lua_newuserdata(L, sizeof(*sp)); + luaL_getmetatable(L, "harfbuzz.Script"); + lua_setmetatable(L, -2); + + if (lua_gettop(L) == 1 || lua_isnil(L, -2)) + *sp = HB_SCRIPT_INVALID; + else + *sp = hb_script_from_string(luaL_checkstring(L, -2), -1); + + return 1; +} + +static int new_script_from_tag(lua_State *L) { + Tag* tp = (Tag *)luaL_checkudata(L, 1, "harfbuzz.Tag"); + + Script *sp = (Script *)lua_newuserdata(L, sizeof(*sp)); + luaL_getmetatable(L, "harfbuzz.Script"); + lua_setmetatable(L, -2); + + *sp = hb_script_from_iso15924_tag(*tp); + return 1; +} + +static int script_to_string(lua_State *L) { + Script *script = (Script *)luaL_checkudata(L, 1, "harfbuzz.Script"); + char s[5]; + + hb_tag_to_string(hb_script_to_iso15924_tag(*script), s); + s[4] = '\0'; + lua_pushstring(L, s); + return 1; +} + +static int script_to_tag(lua_State *L) { + Script *script = (Script *)luaL_checkudata(L, 1, "harfbuzz.Script"); + + Tag *tp = (Tag *)lua_newuserdata(L, sizeof(*tp)); + luaL_getmetatable(L, "harfbuzz.Tag"); + lua_setmetatable(L, -2); + + *tp = hb_script_to_iso15924_tag(*script); + return 1; +} + +static int script_equals(lua_State *L) { + Script* lhs = (Script *)luaL_checkudata(L, 1, "harfbuzz.Script"); + Script* rhs = (Script *)luaL_checkudata(L, 2, "harfbuzz.Script"); + + lua_pushboolean(L, *lhs == *rhs); + return 1; +} + +static const struct luaL_Reg script_methods[] = { + { "__tostring", script_to_string }, + { "__eq", script_equals }, + { "to_iso15924_tag", script_to_tag }, + { NULL, NULL } +}; + +static const struct luaL_Reg script_functions[] = { + { "new", new_script_from_string }, + { "from_iso15924_tag", new_script_from_tag }, + { NULL, NULL } +}; + +int register_script(lua_State *L) { + return register_class(L, "harfbuzz.Script", script_methods, script_functions, NULL); +} diff --git a/luaharfbuzz/src/luaharfbuzz/tag.c b/luaharfbuzz/src/luaharfbuzz/tag.c new file mode 100644 index 0000000..ec7e4b1 --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/tag.c @@ -0,0 +1,50 @@ +// harfbuzz.Feature +#include "luaharfbuzz.h" + +static int tag_new(lua_State *L) { + Tag *t; + + t = (Tag *)lua_newuserdata(L, sizeof(*t)); + luaL_getmetatable(L, "harfbuzz.Tag"); + lua_setmetatable(L, -2); + + if (lua_gettop(L) == 1 || lua_isnil(L, -2)) + *t = HB_TAG_NONE; + else + *t = hb_tag_from_string(luaL_checkstring(L, -2), -1); + + return 1; +} + +static int tag_to_string(lua_State *L) { + Tag* t = (Tag *)luaL_checkudata(L, 1, "harfbuzz.Tag"); + char tag[5]; + + hb_tag_to_string(*t, tag); + tag[4] = '\0'; + lua_pushstring(L, tag); + return 1; +} + +static int tag_equals(lua_State *L) { + Tag* lhs = (Tag *)luaL_checkudata(L, 1, "harfbuzz.Tag"); + Tag* rhs = (Tag *)luaL_checkudata(L, 2, "harfbuzz.Tag"); + + lua_pushboolean(L, *lhs == *rhs); + return 1; +} + +static const struct luaL_Reg tag_methods[] = { + { "__tostring", tag_to_string }, + { "__eq", tag_equals }, + { NULL, NULL } +}; + +static const struct luaL_Reg tag_functions[] = { + { "new", tag_new }, + { NULL, NULL } +}; + +int register_tag(lua_State *L) { + return register_class(L, "harfbuzz.Tag", tag_methods, tag_functions, NULL); +} diff --git a/luaharfbuzz/src/luaharfbuzz/texput.log b/luaharfbuzz/src/luaharfbuzz/texput.log new file mode 100644 index 0000000..cfee0e1 --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/texput.log @@ -0,0 +1,33 @@ +This is LuaHBTeX, Version 1.11.2 (TeX Live 2020/dev) (format=luahbtex 2019.12.5) 11 DEC 2019 21:17 + restricted system commands enabled. +**\relax + +*\directlua{print(luaharfbuzz.Tag.new"ab ")} + +*\directlua{print(luaharfbuzz.Tag.new"ab " .. 'c')} +[\directlua]:1: attempt to concatenate a harfbuzz.Tag value +stack traceback: + [\directlua]:1: in main chunk. +<*> ...ctlua{print(luaharfbuzz.Tag.new"ab " .. 'c')} + +? \directlua{print(tostring(luaharfbuzz.Tag.new"ab ") .. 'c')} +Type to proceed, S to scroll future error messages, +R to run without stopping, Q to run quietly, +I to insert something, +1 or ... or 9 to ignore the next 1 to 9 tokens of input, +H for help, X to quit. +? + +*\directlua{print(tostring(luaharfbuzz.Tag.new"ab ") .. 'c')} + +*directlua{print(tostring(luaharfbuzz.Tag.new"ab") .. 'c')} + +*\directlua{print(tostring(luaharfbuzz.Tag.new"ab") .. 'c')} + +* +! Emergency stop. +<*> ...int(tostring(luaharfbuzz.Tag.new"ab") .. 'c')} + +End of file on the terminal! + +! ==> Fatal error occurred, no output PDF file produced! diff --git a/luaharfbuzz/src/luaharfbuzz/unicode.c b/luaharfbuzz/src/luaharfbuzz/unicode.c new file mode 100644 index 0000000..4e887fc --- /dev/null +++ b/luaharfbuzz/src/luaharfbuzz/unicode.c @@ -0,0 +1,22 @@ +#include "luaharfbuzz.h" + +static int script(lua_State *L) { + hb_codepoint_t c = (hb_codepoint_t) luaL_checkinteger(L, -1); + + Script *sp = (Script *)lua_newuserdata(L, sizeof(*sp)); + luaL_getmetatable(L, "harfbuzz.Script"); + lua_setmetatable(L, -2); + + *sp = hb_unicode_script(hb_unicode_funcs_get_default(), c); + return 1; +} + +static const struct luaL_Reg unicode_functions[] = { + { "script", script }, + { NULL, NULL } +}; + +int register_unicode(lua_State *L) { + luaL_newlib(L, unicode_functions); + return 1; +} diff --git a/luaharfbuzz/status/done.txt b/luaharfbuzz/status/done.txt new file mode 100644 index 0000000..fff80d6 --- /dev/null +++ b/luaharfbuzz/status/done.txt @@ -0,0 +1,42 @@ +hb_blob_create +hb_blob_destroy +hb_blob_get_length +hb_buffer_add_codepoints +hb_buffer_add_utf8 +hb_buffer_create +hb_buffer_destroy +hb_buffer_get_cluster_level +hb_buffer_get_direction +hb_buffer_get_language +hb_buffer_get_length +hb_buffer_get_script +hb_buffer_guess_segment_properties +hb_buffer_reverse +hb_buffer_set_cluster_level +hb_buffer_set_direction +hb_buffer_set_language +hb_buffer_set_script +hb_direction_from_string +hb_direction_to_string +hb_face_create +hb_face_destroy +hb_face_get_upem +hb_feature_from_string +hb_feature_to_string +hb_font_create +hb_font_destroy +hb_font_get_scale +hb_font_set_scale +hb_language_from_string +hb_language_to_string +hb_script_from_iso15924_tag +hb_script_from_string +hb_script_to_iso15924_tag +hb_shape +hb_shape_full +hb_shape_list_shapers +hb_tag_from_string +hb_tag_to_string +hb_unicode_script +hb_version + diff --git a/luaharfbuzz/status/full_api.txt b/luaharfbuzz/status/full_api.txt new file mode 100644 index 0000000..3b6728f --- /dev/null +++ b/luaharfbuzz/status/full_api.txt @@ -0,0 +1,429 @@ +hb_aat_layout_feature_type_get_name_id +hb_aat_layout_feature_type_get_selector_infos +hb_aat_layout_get_feature_types +hb_aat_layout_has_positioning +hb_aat_layout_has_substitution +hb_aat_layout_has_tracking +hb_blob_copy_writable_or_fail +hb_blob_create +hb_blob_create_from_file +hb_blob_create_sub_blob +hb_blob_destroy +hb_blob_get_data +hb_blob_get_data_writable +hb_blob_get_empty +hb_blob_get_length +hb_blob_get_user_data +hb_blob_is_immutable +hb_blob_make_immutable +hb_blob_reference +hb_blob_set_user_data +hb_buffer_add +hb_buffer_add_codepoints +hb_buffer_add_latin1 +hb_buffer_add_utf16 +hb_buffer_add_utf32 +hb_buffer_add_utf8 +hb_buffer_allocation_successful +hb_buffer_append +hb_buffer_clear_contents +hb_buffer_create +hb_buffer_deserialize_glyphs +hb_buffer_destroy +hb_buffer_diff +hb_buffer_get_cluster_level +hb_buffer_get_content_type +hb_buffer_get_direction +hb_buffer_get_empty +hb_buffer_get_flags +hb_buffer_get_glyph_infos +hb_buffer_get_glyph_positions +hb_buffer_get_invisible_glyph +hb_buffer_get_language +hb_buffer_get_length +hb_buffer_get_replacement_codepoint +hb_buffer_get_script +hb_buffer_get_segment_properties +hb_buffer_get_unicode_funcs +hb_buffer_get_user_data +hb_buffer_guess_segment_properties +hb_buffer_normalize_glyphs +hb_buffer_pre_allocate +hb_buffer_reference +hb_buffer_reset +hb_buffer_reverse +hb_buffer_reverse_clusters +hb_buffer_reverse_range +hb_buffer_serialize_format_from_string +hb_buffer_serialize_format_to_string +hb_buffer_serialize_glyphs +hb_buffer_serialize_list_formats +hb_buffer_set_cluster_level +hb_buffer_set_content_type +hb_buffer_set_direction +hb_buffer_set_flags +hb_buffer_set_invisible_glyph +hb_buffer_set_language +hb_buffer_set_length +hb_buffer_set_message_func +hb_buffer_set_replacement_codepoint +hb_buffer_set_script +hb_buffer_set_segment_properties +hb_buffer_set_unicode_funcs +hb_buffer_set_user_data +hb_color_get_alpha +hb_color_get_blue +hb_color_get_green +hb_color_get_red +hb_direction_from_string +hb_direction_to_string +hb_face_builder_add_table +hb_face_builder_create +hb_face_collect_unicodes +hb_face_collect_variation_selectors +hb_face_collect_variation_unicodes +hb_face_count +hb_face_create +hb_face_create_for_tables +hb_face_destroy +hb_face_get_empty +hb_face_get_glyph_count +hb_face_get_index +hb_face_get_table_tags +hb_face_get_upem +hb_face_get_user_data +hb_face_is_immutable +hb_face_make_immutable +hb_face_reference +hb_face_reference_blob +hb_face_reference_table +hb_face_set_glyph_count +hb_face_set_index +hb_face_set_upem +hb_face_set_user_data +hb_feature_from_string +hb_feature_to_string +hb_font_add_glyph_origin_for_direction +hb_font_create +hb_font_create_sub_font +hb_font_destroy +hb_font_funcs_create +hb_font_funcs_destroy +hb_font_funcs_get_empty +hb_font_funcs_get_user_data +hb_font_funcs_is_immutable +hb_font_funcs_make_immutable +hb_font_funcs_reference +hb_font_funcs_set_font_h_extents_func +hb_font_funcs_set_font_v_extents_func +hb_font_funcs_set_glyph_contour_point_func +hb_font_funcs_set_glyph_extents_func +hb_font_funcs_set_glyph_from_name_func +hb_font_funcs_set_glyph_func +hb_font_funcs_set_glyph_h_advance_func +hb_font_funcs_set_glyph_h_advances_func +hb_font_funcs_set_glyph_h_kerning_func +hb_font_funcs_set_glyph_h_origin_func +hb_font_funcs_set_glyph_name_func +hb_font_funcs_set_glyph_v_advance_func +hb_font_funcs_set_glyph_v_advances_func +hb_font_funcs_set_glyph_v_kerning_func +hb_font_funcs_set_glyph_v_origin_func +hb_font_funcs_set_nominal_glyph_func +hb_font_funcs_set_nominal_glyphs_func +hb_font_funcs_set_user_data +hb_font_funcs_set_variation_glyph_func +hb_font_get_empty +hb_font_get_extents_for_direction +hb_font_get_face +hb_font_get_glyph +hb_font_get_glyph_advance_for_direction +hb_font_get_glyph_advances_for_direction +hb_font_get_glyph_contour_point +hb_font_get_glyph_contour_point_for_origin +hb_font_get_glyph_extents +hb_font_get_glyph_extents_for_origin +hb_font_get_glyph_from_name +hb_font_get_glyph_h_advance +hb_font_get_glyph_h_advances +hb_font_get_glyph_h_kerning +hb_font_get_glyph_h_origin +hb_font_get_glyph_kerning_for_direction +hb_font_get_glyph_name +hb_font_get_glyph_origin_for_direction +hb_font_get_glyph_v_advance +hb_font_get_glyph_v_advances +hb_font_get_glyph_v_kerning +hb_font_get_glyph_v_origin +hb_font_get_h_extents +hb_font_get_nominal_glyph +hb_font_get_parent +hb_font_get_ppem +hb_font_get_ptem +hb_font_get_scale +hb_font_get_user_data +hb_font_get_var_coords_normalized +hb_font_get_variation_glyph +hb_font_get_v_extents +hb_font_glyph_from_string +hb_font_glyph_to_string +hb_font_is_immutable +hb_font_make_immutable +hb_font_reference +hb_font_set_face +hb_font_set_funcs +hb_font_set_funcs_data +hb_font_set_parent +hb_font_set_ppem +hb_font_set_ptem +hb_font_set_scale +hb_font_set_user_data +hb_font_set_var_coords_design +hb_font_set_var_coords_normalized +hb_font_set_variations +hb_font_subtract_glyph_origin_for_direction +hb_ft_face_create +hb_ft_face_create_cached +hb_ft_face_create_referenced +hb_ft_font_changed +hb_ft_font_create +hb_ft_font_create_referenced +hb_ft_font_get_face +hb_ft_font_get_load_flags +hb_ft_font_set_funcs +hb_ft_font_set_load_flags +hb_glib_blob_create +hb_glib_get_unicode_funcs +hb_glib_script_from_script +hb_glib_script_to_script +hb_glyph_info_get_glyph_flags +hb_gobject_aat_layout_feature_selector_get_type +hb_gobject_aat_layout_feature_type_get_type +hb_gobject_blob_get_type +hb_gobject_buffer_cluster_level_get_type +hb_gobject_buffer_content_type_get_type +hb_gobject_buffer_diff_flags_get_type +hb_gobject_buffer_flags_get_type +hb_gobject_buffer_get_type +hb_gobject_buffer_serialize_flags_get_type +hb_gobject_buffer_serialize_format_get_type +hb_gobject_direction_get_type +hb_gobject_face_get_type +hb_gobject_feature_get_type +hb_gobject_font_funcs_get_type +hb_gobject_font_get_type +hb_gobject_glyph_flags_get_type +hb_gobject_glyph_info_get_type +hb_gobject_glyph_position_get_type +hb_gobject_map_get_type +hb_gobject_memory_mode_get_type +hb_gobject_ot_color_palette_flags_get_type +hb_gobject_ot_layout_glyph_class_get_type +hb_gobject_ot_math_constant_get_type +hb_gobject_ot_math_glyph_part_flags_get_type +hb_gobject_ot_math_glyph_part_get_type +hb_gobject_ot_math_glyph_variant_get_type +hb_gobject_ot_math_kern_get_type +hb_gobject_ot_var_axis_flags_get_type +hb_gobject_script_get_type +hb_gobject_segment_properties_get_type +hb_gobject_set_get_type +hb_gobject_shape_plan_get_type +hb_gobject_unicode_combining_class_get_type +hb_gobject_unicode_funcs_get_type +hb_gobject_unicode_general_category_get_type +hb_gobject_user_data_key_get_type +hb_graphite2_face_get_gr_face +hb_graphite2_font_get_gr_font +hb_language_from_string +hb_language_get_default +hb_language_to_string +hb_map_allocation_successful +hb_map_clear +hb_map_create +hb_map_del +hb_map_destroy +hb_map_get +hb_map_get_empty +hb_map_get_population +hb_map_get_user_data +hb_map_has +hb_map_is_empty +hb_map_reference +hb_map_set +hb_map_set_user_data +hb_ot_color_glyph_get_layers +hb_ot_color_glyph_reference_png +hb_ot_color_glyph_reference_svg +hb_ot_color_has_layers +hb_ot_color_has_palettes +hb_ot_color_has_png +hb_ot_color_has_svg +hb_ot_color_palette_color_get_name_id +hb_ot_color_palette_get_colors +hb_ot_color_palette_get_count +hb_ot_color_palette_get_flags +hb_ot_color_palette_get_name_id +hb_ot_font_set_funcs +hb_ot_layout_collect_features +hb_ot_layout_collect_lookups +hb_ot_layout_feature_get_characters +hb_ot_layout_feature_get_lookups +hb_ot_layout_feature_get_name_ids +hb_ot_layout_feature_with_variations_get_lookups +hb_ot_layout_get_attach_points +hb_ot_layout_get_glyph_class +hb_ot_layout_get_glyphs_in_class +hb_ot_layout_get_ligature_carets +hb_ot_layout_get_size_params +hb_ot_layout_has_glyph_classes +hb_ot_layout_has_positioning +hb_ot_layout_has_substitution +hb_ot_layout_language_find_feature +hb_ot_layout_language_get_feature_indexes +hb_ot_layout_language_get_feature_tags +hb_ot_layout_language_get_required_feature +hb_ot_layout_language_get_required_feature_index +hb_ot_layout_lookup_collect_glyphs +hb_ot_layout_lookups_substitute_closure +hb_ot_layout_lookup_substitute_closure +hb_ot_layout_lookup_would_substitute +hb_ot_layout_script_find_language +hb_ot_layout_script_get_language_tags +hb_ot_layout_script_select_language +hb_ot_layout_table_choose_script +hb_ot_layout_table_find_feature_variations +hb_ot_layout_table_find_script +hb_ot_layout_table_get_feature_tags +hb_ot_layout_table_get_lookup_count +hb_ot_layout_table_get_script_tags +hb_ot_layout_table_select_script +hb_ot_math_get_constant +hb_ot_math_get_glyph_assembly +hb_ot_math_get_glyph_italics_correction +hb_ot_math_get_glyph_kerning +hb_ot_math_get_glyph_top_accent_attachment +hb_ot_math_get_glyph_variants +hb_ot_math_get_min_connector_overlap +hb_ot_math_has_data +hb_ot_math_is_glyph_extended_shape +hb_ot_name_get_utf16 +hb_ot_name_get_utf32 +hb_ot_name_get_utf8 +hb_ot_name_list_names +hb_ot_shape_glyphs_closure +hb_ot_shape_plan_collect_lookups +hb_ot_tag_from_language +hb_ot_tags_from_script +hb_ot_tags_from_script_and_language +hb_ot_tags_to_script_and_language +hb_ot_tag_to_language +hb_ot_tag_to_script +hb_ot_var_find_axis +hb_ot_var_find_axis_info +hb_ot_var_get_axes +hb_ot_var_get_axis_count +hb_ot_var_get_axis_infos +hb_ot_var_get_named_instance_count +hb_ot_var_has_data +hb_ot_var_named_instance_get_design_coords +hb_ot_var_named_instance_get_postscript_name_id +hb_ot_var_named_instance_get_subfamily_name_id +hb_ot_var_normalize_coords +hb_ot_var_normalize_variations +hb_script_from_iso15924_tag +hb_script_from_string +hb_script_get_horizontal_direction +hb_script_to_iso15924_tag +hb_segment_properties_equal +hb_segment_properties_hash +hb_set_add +hb_set_add_range +hb_set_allocation_successful +hb_set_clear +hb_set_create +hb_set_del +hb_set_del_range +hb_set_destroy +hb_set_get_empty +hb_set_get_max +hb_set_get_min +hb_set_get_population +hb_set_get_user_data +hb_set_has +hb_set_intersect +hb_set_invert +hb_set_is_empty +hb_set_is_equal +hb_set_is_subset +hb_set_next +hb_set_next_range +hb_set_previous +hb_set_previous_range +hb_set_reference +hb_set_set +hb_set_set_user_data +hb_set_subtract +hb_set_symmetric_difference +hb_set_union +hb_shape +hb_shape_full +hb_shape_list_shapers +hb_shape_plan_create +hb_shape_plan_create2 +hb_shape_plan_create_cached +hb_shape_plan_create_cached2 +hb_shape_plan_destroy +hb_shape_plan_execute +hb_shape_plan_get_empty +hb_shape_plan_get_shaper +hb_shape_plan_get_user_data +hb_shape_plan_reference +hb_shape_plan_set_user_data +hb_subset +hb_subset_input_create_or_fail +hb_subset_input_destroy +hb_subset_input_drop_tables_set +hb_subset_input_get_desubroutinize +hb_subset_input_get_drop_hints +hb_subset_input_get_retain_gids +hb_subset_input_glyph_set +hb_subset_input_nameid_set +hb_subset_input_reference +hb_subset_input_set_desubroutinize +hb_subset_input_set_drop_hints +hb_subset_input_set_retain_gids +hb_subset_input_unicode_set +hb_tag_from_string +hb_tag_to_string +hb_unicode_combining_class +hb_unicode_compose +hb_unicode_decompose +hb_unicode_decompose_compatibility +hb_unicode_eastasian_width +hb_unicode_funcs_create +hb_unicode_funcs_destroy +hb_unicode_funcs_get_default +hb_unicode_funcs_get_empty +hb_unicode_funcs_get_parent +hb_unicode_funcs_get_user_data +hb_unicode_funcs_is_immutable +hb_unicode_funcs_make_immutable +hb_unicode_funcs_reference +hb_unicode_funcs_set_combining_class_func +hb_unicode_funcs_set_compose_func +hb_unicode_funcs_set_decompose_compatibility_func +hb_unicode_funcs_set_decompose_func +hb_unicode_funcs_set_eastasian_width_func +hb_unicode_funcs_set_general_category_func +hb_unicode_funcs_set_mirroring_func +hb_unicode_funcs_set_script_func +hb_unicode_funcs_set_user_data +hb_unicode_general_category +hb_unicode_mirroring +hb_unicode_script +hb_variation_from_string +hb_variation_to_string +hb_version +hb_version_atleast +hb_version_string