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