Initial commit

This commit is contained in:
Marcel Fabian Krüger 2021-12-28 10:18:21 +01:00
commit ca93f7224c
52 changed files with 7770 additions and 0 deletions

35
Makefile Normal file
View File

@ -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

1
libs.lua Normal file
View File

@ -0,0 +1 @@
print(require'luametalatex.kpse', require'luametalatex.harfbuzz')

9
linkscript.ld Normal file
View File

@ -0,0 +1,9 @@
luaopen_luametalatex_kpse = luaopen_kpse;
luaopen_luametalatex_harfbuzz = luaopen_luaharfbuzz;
VERSION {
{
global: luaopen_luametalatex_*;
local: *;
};
}

13
luaharfbuzz/.busted Normal file
View File

@ -0,0 +1,13 @@
return {
_all = {
coverage = true,
verbose = true,
ROOT = { "spec" }
},
default = {
["exclude-tags"] = "mac"
},
mac = {
tags = "mac"
}
}

11
luaharfbuzz/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
*.la
*.lo
*.o
*.so
.libs/*
build/*
luacov.report.out
luacov.stats.out
.luacheckcache
api
*.src.rock

13
luaharfbuzz/.luacheckrc Normal file
View File

@ -0,0 +1,13 @@
cache = true
files["spec/"].std = "+busted"
files["examples/luatex/"].globals = {
"texio",
"fontloader",
"callback",
"tex",
"node",
"read_font",
"unicode",
"font"
}

22
luaharfbuzz/LICENSE Normal file
View File

@ -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.

15
luaharfbuzz/Makefile Normal file
View File

@ -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

165
luaharfbuzz/README.md Normal file
View File

@ -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 isnt 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 <deepak.jois@gmail.com>.

2
luaharfbuzz/THANKS Normal file
View File

@ -0,0 +1,2 @@
Simon Cozens
Michal Hoftich

9
luaharfbuzz/config.ld Normal file
View File

@ -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'

View File

@ -0,0 +1,108 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>luaharfbuzz Documentation</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>luaharfbuzz</h1>
<h2>Examples</h2>
<ul class="nowrap">
<li><strong>core_types.lua</strong></li>
</ul>
<h2>Modules</h2>
<ul class="nowrap">
<li><a href="../index.html">harfbuzz</a></li>
</ul>
</div>
<div id="content">
<h2>core_types.lua</h2>
<pre>
<span class="keyword">local</span> harfbuzz = <span class="global">require</span>(<span class="string">'harfbuzz'</span>)
<span class="keyword">local</span> serpent = <span class="global">require</span>(<span class="string">'serpent'</span>) <span class="comment">-- luarocks install serpent
</span>
<span class="comment">-- Harfbuzz API Version
</span><span class="global">print</span>(<span class="string">"Harfbuzz API version"</span>, harfbuzz.version())
<span class="comment">-- Shapers available
</span><span class="global">print</span>(<span class="string">"Shapers:"</span>, serpent.line({ harfbuzz.shapers() }, {comment = <span class="keyword">false</span>}))
<span class="comment">-- harfbuzz.Face
</span><span class="keyword">local</span> face = harfbuzz.Face.new(<span class="string">'../fonts/notonastaliq.ttf'</span>)
<span class="global">print</span>(<span class="string">'\nFace upem = '</span>..face:get_upem())
<span class="comment">-- harfbuzz.Font
</span><span class="keyword">local</span> font = harfbuzz.Font.new(face)
<span class="keyword">local</span> xs, xy = font:get_scale()
<span class="global">print</span>(<span class="string">"\nDefault font scale = X: "</span>..xs..<span class="string">", Y: "</span>..xy)
<span class="comment">-- harfbuzz.Buffer
</span><span class="keyword">local</span> text = <span class="string">"یہ"</span> <span class="comment">-- U+06CC U+06C1
</span><span class="keyword">local</span> buf = harfbuzz.Buffer.new()
buf:add_utf8(text)
<span class="comment">-- harfbuzz.shape (Shapes text)
</span><span class="global">print</span>(<span class="string">"\nShaping '"</span>..text..<span class="string">"' set with Noto Nastaliq Urdu"</span>)
harfbuzz.shape(font, buf, { language = harfbuzz.Language.new(<span class="string">"urd"</span>), script = harfbuzz.Script.new(<span class="string">"Arab"</span>), direction = harfbuzz.Direction.RTL})
<span class="keyword">local</span> glyphs = buf:get_glyphs()
<span class="global">print</span>(<span class="string">"No. of glyphs"</span>, #glyphs)
<span class="global">print</span>(serpent.line(glyphs, {comment = <span class="keyword">false</span>}))
<span class="keyword">local</span> opts = { language = harfbuzz.Language.new(<span class="string">"eng"</span>), script = harfbuzz.Script.new(<span class="string">"Latn"</span>), direction = harfbuzz.Direction.LTR }
<span class="keyword">local</span> amiri_face = harfbuzz.Face.new(<span class="string">'../fonts/amiri-regular.ttf'</span>)
<span class="keyword">local</span> amiri_font = harfbuzz.Font.new(amiri_face)
<span class="comment">-- shaping '123' w/o features
</span><span class="global">print</span>(<span class="string">"\nShaping '123' set with Amiri Regular and no features"</span>)
buf= harfbuzz.Buffer.new()
buf:add_utf8(<span class="string">"123"</span>)
harfbuzz.shape(amiri_font, buf, opts)
glyphs = buf:get_glyphs()
<span class="global">print</span>(serpent.line(glyphs, {comment = <span class="keyword">false</span>}))
<span class="comment">-- shaping '123' with '+numr' (numerators)
</span><span class="global">print</span>(<span class="string">"\nShaping '123' set with Amiri Regular with 'numr' feature turned on"</span>)
buf= harfbuzz.Buffer.new()
buf:add_utf8(<span class="string">"123"</span>)
opts.features = <span class="string">"+numr"</span>
harfbuzz.shape(amiri_font, buf, opts)
glyphs = buf:get_glyphs()
<span class="global">print</span>(serpent.line(glyphs, {comment = <span class="keyword">false</span>}))</pre>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated 2019-07-31 18:39:39 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

2829
luaharfbuzz/docs/index.html Normal file

File diff suppressed because it is too large Load Diff

303
luaharfbuzz/docs/ldoc.css Normal file
View File

@ -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; }

View File

@ -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}))

View File

@ -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}]

View File

@ -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}]

View File

@ -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}]

View File

@ -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}]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

334
luaharfbuzz/inspect.lua Normal file
View File

@ -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('<table ', self:getId(t), '>')
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('<metatable> = ')
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

View File

@ -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 <deepak.jois@gmail.com>"
}
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"
}
}

View File

@ -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 <deepak.jois@gmail.com>",
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"
}
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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، خالد حسني <khaledhosny@eglug.org>.",f:get_name(harfbuzz.ot.NAME_ID_COPYRIGHT, ar))
assert.are_equal("Copyright (c) 2010-2015, Khaled Hosny <khaledhosny@eglug.org>.\nPortions copyright (c) 2010, Sebastian Kosch <sebastian@aldusleaf.org>.",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("<?xml version='1.0' encoding='UTF-8'?>",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)

View File

@ -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)

View File

@ -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

View File

@ -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 <<deepak.jois@gmail.com>>
-- @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 faces 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&nbsp;`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 thats 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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -0,0 +1,39 @@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <hb.h>
#include <hb-ot.h>
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 <return> 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!

View File

@ -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;
}

View File

@ -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

View File

@ -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