summaryrefslogtreecommitdiff
path: root/source/luametatex/source/luaoptional/lmthb.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/luaoptional/lmthb.c')
-rw-r--r--source/luametatex/source/luaoptional/lmthb.c761
1 files changed, 761 insertions, 0 deletions
diff --git a/source/luametatex/source/luaoptional/lmthb.c b/source/luametatex/source/luaoptional/lmthb.c
new file mode 100644
index 000000000..d853256cf
--- /dev/null
+++ b/source/luametatex/source/luaoptional/lmthb.c
@@ -0,0 +1,761 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# include "luametatex.h"
+
+/*tex
+
+ This is using similar c-lua-interfacing code as that Kai Eigner wrote for ffi. I cleaned it up
+ a bit but the principles remain because that way we are downward compatible. Don't expect
+ miracles here. We use function pointers as we delay binding to the module. We use the system
+ library as it comes, because after all, that is what is expected: shaping conform what the
+ system offers.
+
+ This interface is only for testing. We load the font in \LUA\ anyway, so we don't need to
+ collect information here. The code runs on top of the \CONTEXT\ font plugin interface that
+ itself was written for testing purposes. When we wanted to test uniscribe the hb command
+ line program could be used for that, but more direct support was also added. The tests that
+ were done at that time (irr it was when xetex switched to hb and folks used that as reference)
+ ended up in articles. Later we used this mechanism to check how Idris advanced Arabic font
+ behaves in different shapers (context, uniscribe, hb, ...) as we try to follow uniscribe when
+ possible and this gave the glue to that. It showed interesting differences and overlap in
+ interpretation (and made us wonder when bugs - in whatever program or standard - get turned
+ into features, but that's another matter, and we can always adapt and provide variants and
+ overloads if needed).
+
+ The following code is not dependent on h files so we don't need to install a whole bunch of
+ dependencies. Also, because we delay loading, there is no default overhead in startup. The
+ loading of the library happens (as usual) at the \LUA\ end, but in order for it to work okay
+ the initializer needs to be called, so that the functions get resolved. So, it works a bit
+ like the ffi interface: delayed loading, but (maybe) with a bit less overhead. I should
+ probably look at the latest api to see if things can be done with less code but on the other
+ hand there is no real reason to change something that already worked okay some years ago.
+
+ I guess that the script enumeration is no longer right but it probably doesn't matter as
+ numbers are passed anyway. We can probably make that into an integer (I need to test that
+ some day) as these enumerations are just that: integers, and the less hard-coding we have
+ here the better.
+
+ When this module is used (which is triggered via loading an optional module and setting the
+ mode in a font definition) other features of the \CONTEXT\ font handler are lost for that
+ specific font instance, simply because these mechanism operate independently. But that is
+ probably what a user expects anyway: no interference from other code, just the results from
+ a library. It makes no sense to complicate the machinery even more. This is comparable with
+ basemode and nodemode that are also seperated code paths.
+
+ So, we could probably simplify the following typedefs, but this is what Kai started with so
+ I stick to it. From the enumerations only the direction constant is used.
+
+*/
+
+typedef struct hb_blob_t hb_blob_t;
+
+/* typedef int hb_memory_mode_t; */
+
+typedef enum hb_memory_mode_t {
+ HB_MEMORY_MODE_DUPLICATE,
+ HB_MEMORY_MODE_READONLY,
+ HB_MEMORY_MODE_WRITABLE,
+ HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE
+} hb_memory_mode_t;
+
+typedef void (*hb_destroy_func_t) (
+ void *user_data
+);
+
+typedef struct hb_face_t hb_face_t;
+typedef const struct hb_language_impl_t *hb_language_t;
+typedef struct hb_buffer_t hb_buffer_t;
+
+/* typedef int hb_script_t; */
+/* typedef int hb_direction_t; */
+
+/*
+ The content of this enum doesn't really matter here because we don't use it. Integers are
+ passed around. So, even if the following is not up to date we're okay.
+*/
+
+typedef enum hb_script_t {
+ HB_SCRIPT_COMMON, HB_SCRIPT_INHERITED, HB_SCRIPT_UNKNOWN,
+
+ HB_SCRIPT_ARABIC, HB_SCRIPT_ARMENIAN, HB_SCRIPT_BENGALI, HB_SCRIPT_CYRILLIC,
+ HB_SCRIPT_DEVANAGARI, HB_SCRIPT_GEORGIAN, HB_SCRIPT_GREEK,
+ HB_SCRIPT_GUJARATI, HB_SCRIPT_GURMUKHI, HB_SCRIPT_HANGUL, HB_SCRIPT_HAN,
+ HB_SCRIPT_HEBREW, HB_SCRIPT_HIRAGANA, HB_SCRIPT_KANNADA, HB_SCRIPT_KATAKANA,
+ HB_SCRIPT_LAO, HB_SCRIPT_LATIN, HB_SCRIPT_MALAYALAM, HB_SCRIPT_ORIYA,
+ HB_SCRIPT_TAMIL, HB_SCRIPT_TELUGU, HB_SCRIPT_THAI, HB_SCRIPT_TIBETAN,
+ HB_SCRIPT_BOPOMOFO, HB_SCRIPT_BRAILLE, HB_SCRIPT_CANADIAN_SYLLABICS,
+ HB_SCRIPT_CHEROKEE, HB_SCRIPT_ETHIOPIC, HB_SCRIPT_KHMER, HB_SCRIPT_MONGOLIAN,
+ HB_SCRIPT_MYANMAR, HB_SCRIPT_OGHAM, HB_SCRIPT_RUNIC, HB_SCRIPT_SINHALA,
+ HB_SCRIPT_SYRIAC, HB_SCRIPT_THAANA, HB_SCRIPT_YI, HB_SCRIPT_DESERET,
+ HB_SCRIPT_GOTHIC, HB_SCRIPT_OLD_ITALIC, HB_SCRIPT_BUHID, HB_SCRIPT_HANUNOO,
+ HB_SCRIPT_TAGALOG, HB_SCRIPT_TAGBANWA, HB_SCRIPT_CYPRIOT, HB_SCRIPT_LIMBU,
+ HB_SCRIPT_LINEAR_B, HB_SCRIPT_OSMANYA, HB_SCRIPT_SHAVIAN, HB_SCRIPT_TAI_LE,
+ HB_SCRIPT_UGARITIC, HB_SCRIPT_BUGINESE, HB_SCRIPT_COPTIC,
+ HB_SCRIPT_GLAGOLITIC, HB_SCRIPT_KHAROSHTHI, HB_SCRIPT_NEW_TAI_LUE,
+ HB_SCRIPT_OLD_PERSIAN, HB_SCRIPT_SYLOTI_NAGRI, HB_SCRIPT_TIFINAGH,
+ HB_SCRIPT_BALINESE, HB_SCRIPT_CUNEIFORM, HB_SCRIPT_NKO, HB_SCRIPT_PHAGS_PA,
+ HB_SCRIPT_PHOENICIAN, HB_SCRIPT_CARIAN, HB_SCRIPT_CHAM, HB_SCRIPT_KAYAH_LI,
+ HB_SCRIPT_LEPCHA, HB_SCRIPT_LYCIAN, HB_SCRIPT_LYDIAN, HB_SCRIPT_OL_CHIKI,
+ HB_SCRIPT_REJANG, HB_SCRIPT_SAURASHTRA, HB_SCRIPT_SUNDANESE, HB_SCRIPT_VAI,
+ HB_SCRIPT_AVESTAN, HB_SCRIPT_BAMUM, HB_SCRIPT_EGYPTIAN_HIEROGLYPHS,
+ HB_SCRIPT_IMPERIAL_ARAMAIC, HB_SCRIPT_INSCRIPTIONAL_PAHLAVI,
+ HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, HB_SCRIPT_JAVANESE, HB_SCRIPT_KAITHI,
+ HB_SCRIPT_LISU, HB_SCRIPT_MEETEI_MAYEK, HB_SCRIPT_OLD_SOUTH_ARABIAN,
+ HB_SCRIPT_OLD_TURKIC, HB_SCRIPT_SAMARITAN, HB_SCRIPT_TAI_THAM,
+ HB_SCRIPT_TAI_VIET, HB_SCRIPT_BATAK, HB_SCRIPT_BRAHMI, HB_SCRIPT_MANDAIC,
+ HB_SCRIPT_CHAKMA, HB_SCRIPT_MEROITIC_CURSIVE, HB_SCRIPT_MEROITIC_HIEROGLYPHS,
+ HB_SCRIPT_MIAO, HB_SCRIPT_SHARADA, HB_SCRIPT_SORA_SOMPENG, HB_SCRIPT_TAKRI,
+ HB_SCRIPT_BASSA_VAH, HB_SCRIPT_CAUCASIAN_ALBANIAN, HB_SCRIPT_DUPLOYAN,
+ HB_SCRIPT_ELBASAN, HB_SCRIPT_GRANTHA, HB_SCRIPT_KHOJKI, HB_SCRIPT_KHUDAWADI,
+ HB_SCRIPT_LINEAR_A, HB_SCRIPT_MAHAJANI, HB_SCRIPT_MANICHAEAN,
+ HB_SCRIPT_MENDE_KIKAKUI, HB_SCRIPT_MODI, HB_SCRIPT_MRO, HB_SCRIPT_NABATAEAN,
+ HB_SCRIPT_OLD_NORTH_ARABIAN, HB_SCRIPT_OLD_PERMIC, HB_SCRIPT_PAHAWH_HMONG,
+ HB_SCRIPT_PALMYRENE, HB_SCRIPT_PAU_CIN_HAU, HB_SCRIPT_PSALTER_PAHLAVI,
+ HB_SCRIPT_SIDDHAM, HB_SCRIPT_TIRHUTA, HB_SCRIPT_WARANG_CITI, HB_SCRIPT_AHOM,
+ HB_SCRIPT_ANATOLIAN_HIEROGLYPHS, HB_SCRIPT_HATRAN, HB_SCRIPT_MULTANI,
+ HB_SCRIPT_OLD_HUNGARIAN, HB_SCRIPT_SIGNWRITING, HB_SCRIPT_ADLAM,
+ HB_SCRIPT_BHAIKSUKI, HB_SCRIPT_MARCHEN, HB_SCRIPT_OSAGE, HB_SCRIPT_TANGUT,
+ HB_SCRIPT_NEWA, HB_SCRIPT_MASARAM_GONDI, HB_SCRIPT_NUSHU, HB_SCRIPT_SOYOMBO,
+ HB_SCRIPT_ZANABAZAR_SQUARE, HB_SCRIPT_DOGRA, HB_SCRIPT_GUNJALA_GONDI,
+ HB_SCRIPT_HANIFI_ROHINGYA, HB_SCRIPT_MAKASAR, HB_SCRIPT_MEDEFAIDRIN,
+ HB_SCRIPT_OLD_SOGDIAN, HB_SCRIPT_SOGDIAN, HB_SCRIPT_ELYMAIC,
+ HB_SCRIPT_NANDINAGARI, HB_SCRIPT_NYIAKENG_PUACHUE_HMONG, HB_SCRIPT_WANCHO,
+
+ HB_SCRIPT_INVALID, _HB_SCRIPT_MAX_VALUE, _HB_SCRIPT_MAX_VALUE_SIGNED,
+} hb_script_t;
+
+typedef enum hb_direction_t {
+ HB_DIRECTION_INVALID,
+ HB_DIRECTION_LTR,
+ HB_DIRECTION_RTL,
+ HB_DIRECTION_TTB,
+ HB_DIRECTION_BTT
+} hb_direction_t;
+
+typedef int hb_bool_t;
+
+typedef uint32_t hb_tag_t;
+
+typedef struct hb_feature_t {
+ hb_tag_t tag;
+ uint32_t value;
+ unsigned int start;
+ unsigned int end;
+} hb_feature_t;
+
+typedef struct hb_font_t hb_font_t;
+
+typedef uint32_t hb_codepoint_t;
+typedef int32_t hb_position_t;
+typedef uint32_t hb_mask_t;
+
+typedef union _hb_var_int_t {
+ uint32_t u32;
+ int32_t i32;
+ uint16_t u16[2];
+ int16_t i16[2];
+ uint8_t u8[4];
+ int8_t i8[4];
+} hb_var_int_t;
+
+typedef struct hb_glyph_info_t {
+ hb_codepoint_t codepoint;
+ hb_mask_t mask;
+ uint32_t cluster;
+ /* private */
+ hb_var_int_t var1;
+ hb_var_int_t var2;
+} hb_glyph_info_t;
+
+typedef struct hb_glyph_position_t {
+ hb_position_t x_advance;
+ hb_position_t y_advance;
+ hb_position_t x_offset;
+ hb_position_t y_offset;
+ /* private */
+ hb_var_int_t var;
+} hb_glyph_position_t;
+
+/*tex
+
+ We only need to initialize the font and call a shaper. There is no need to interface more as we
+ won't use those features. I never compiled this library myself and just took it from the system
+ (e.g from inkscape). Keep in mind that names can be different on windows and linux.
+
+ If needed we can reuse buffers and cache a bit more but it probably doesn't make much difference
+ performance wise. Also, in a bit more complex document font handling is not the most time
+ critical and when you use specific scripts in \TEX\ that are not supported otherwise and
+ therefore demand a library run time is probably the least of your problems. So best is that we
+ keep it all abstract.
+
+*/
+
+# define HBLIB_METATABLE "optional.hblib"
+
+typedef struct hblib_data {
+ hb_font_t *font;
+} hblib_data;
+
+typedef struct hblib_state_info {
+
+ int initialized;
+ int padding;
+
+ const char * (*hb_version_string) (
+ void
+ );
+
+ hb_blob_t * (*hb_blob_create) (
+ const char *data,
+ unsigned int length,
+ hb_memory_mode_t mode, /* Could be int I guess. */
+ void *user_data,
+ hb_destroy_func_t destroy
+ );
+
+ void (*hb_blob_destroy) (
+ hb_blob_t *blob
+ );
+
+ hb_face_t * (*hb_face_create) (
+ hb_blob_t *blob,
+ unsigned int index
+ );
+
+ void (*hb_face_destroy) (
+ hb_face_t *face
+ );
+
+ hb_language_t (*hb_language_from_string) (
+ const char *str,
+ int len
+ );
+
+ void (*hb_buffer_set_language) (
+ hb_buffer_t *buffer,
+ hb_language_t language
+ );
+
+ hb_script_t (*hb_script_from_string) (
+ const char *s,
+ int len
+ );
+
+ void (*hb_buffer_set_script) (
+ hb_buffer_t *buffer,
+ hb_script_t script
+ );
+
+ hb_direction_t (*hb_direction_from_string) (
+ const char *str,
+ int len
+ );
+
+ void (*hb_buffer_set_direction) (
+ hb_buffer_t *buffer,
+ hb_direction_t direction
+ );
+
+ hb_bool_t (*hb_feature_from_string) (
+ const char *str,
+ int len,
+ hb_feature_t *feature
+ );
+
+ hb_bool_t (*hb_shape_full) (
+ hb_font_t *font,
+ hb_buffer_t *buffer,
+ const hb_feature_t *features,
+ unsigned int num_features,
+ const char * const *shaper_list
+ );
+
+ hb_buffer_t * (*hb_buffer_create )(
+ void
+ );
+
+ void (*hb_buffer_destroy)(
+ hb_buffer_t *buffer
+ );
+
+ void (*hb_buffer_add_utf8) (
+ hb_buffer_t *buffer,
+ const char *text,
+ int text_length,
+ unsigned int item_offset,
+ int item_length
+ );
+
+ void (*hb_buffer_add_utf32) (
+ hb_buffer_t *buffer,
+ const char *text,
+ int text_length,
+ unsigned int item_offset,
+ int item_length
+ );
+
+ /* void (*hb_buffer_add) (
+ hb_buffer_t *buffer,
+ hb_codepoint_t codepoint,
+ unsigned int cluster
+ ); */
+
+ unsigned int (*hb_buffer_get_length) (
+ hb_buffer_t *buffer
+ );
+
+ hb_glyph_info_t * (*hb_buffer_get_glyph_infos) (
+ hb_buffer_t *buffer,
+ unsigned int *length
+ );
+
+ hb_glyph_position_t * (*hb_buffer_get_glyph_positions) (
+ hb_buffer_t *buffer,
+ unsigned int *length
+ );
+
+ void (*hb_buffer_reverse) (
+ hb_buffer_t *buffer
+ );
+
+ void (*hb_buffer_reset) (
+ hb_buffer_t *buffer
+ );
+
+ void (*hb_buffer_guess_segment_properties) (
+ hb_buffer_t *buffer
+ );
+
+ hb_font_t * (*hb_font_create) (
+ hb_face_t *face
+ );
+
+ void (*hb_font_destroy) (
+ hb_font_t *font
+ );
+
+ void (*hb_font_set_scale) (
+ hb_font_t *font,
+ int x_scale,
+ int y_scale
+ );
+
+ void (*hb_ot_font_set_funcs) (
+ hb_font_t *font
+ );
+
+ unsigned int (*hb_face_get_upem) (
+ hb_face_t *face
+ );
+
+ const char ** (*hb_shape_list_shapers) (
+ void
+ );
+
+} hblib_state_info;
+
+static hblib_state_info hblib_state = {
+
+ .initialized = 0,
+ .padding = 0,
+
+ .hb_version_string = NULL,
+ .hb_blob_create = NULL,
+ .hb_blob_destroy = NULL,
+ .hb_face_create = NULL,
+ .hb_face_destroy = NULL,
+ .hb_language_from_string = NULL,
+ .hb_buffer_set_language = NULL,
+ .hb_script_from_string = NULL,
+ .hb_buffer_set_script = NULL,
+ .hb_direction_from_string = NULL,
+ .hb_buffer_set_direction = NULL,
+ .hb_feature_from_string = NULL,
+ .hb_shape_full = NULL,
+ .hb_buffer_create = NULL,
+ .hb_buffer_destroy = NULL,
+ .hb_buffer_add_utf8 = NULL,
+ .hb_buffer_add_utf32 = NULL,
+ /* .hb_buffer_add = NULL, */
+ .hb_buffer_get_length = NULL,
+ .hb_buffer_get_glyph_infos = NULL,
+ .hb_buffer_get_glyph_positions = NULL,
+ .hb_buffer_reverse = NULL,
+ .hb_buffer_reset = NULL,
+ .hb_buffer_guess_segment_properties = NULL,
+ .hb_font_create = NULL,
+ .hb_font_destroy = NULL,
+ .hb_font_set_scale = NULL,
+ .hb_ot_font_set_funcs = NULL,
+ .hb_face_get_upem = NULL,
+ .hb_shape_list_shapers = NULL,
+
+};
+
+/* <boolean> = initialize(full_path_of_library) */
+
+static int hblib_initialize(lua_State * L)
+{
+ if (! hblib_state.initialized) {
+ const char *filename = lua_tostring(L, 1);
+ if (filename) {
+
+ lmt_library lib = lmt_library_load(filename);
+
+ hblib_state.hb_version_string = lmt_library_find(lib, "hb_version_string");
+ hblib_state.hb_language_from_string = lmt_library_find(lib, "hb_language_from_string");
+ hblib_state.hb_script_from_string = lmt_library_find(lib, "hb_script_from_string");
+ hblib_state.hb_direction_from_string = lmt_library_find(lib, "hb_direction_from_string");
+ hblib_state.hb_feature_from_string = lmt_library_find(lib, "hb_feature_from_string");
+
+ hblib_state.hb_buffer_set_language = lmt_library_find(lib, "hb_buffer_set_language");
+ hblib_state.hb_buffer_set_script = lmt_library_find(lib, "hb_buffer_set_script");
+ hblib_state.hb_buffer_set_direction = lmt_library_find(lib, "hb_buffer_set_direction");
+
+ hblib_state.hb_buffer_create = lmt_library_find(lib, "hb_buffer_create");
+ hblib_state.hb_buffer_destroy = lmt_library_find(lib, "hb_buffer_destroy");
+ hblib_state.hb_buffer_reverse = lmt_library_find(lib, "hb_buffer_reverse");
+ hblib_state.hb_buffer_get_length = lmt_library_find(lib, "hb_buffer_get_length");
+ hblib_state.hb_buffer_reset = lmt_library_find(lib, "hb_buffer_reset");
+ hblib_state.hb_buffer_add_utf8 = lmt_library_find(lib, "hb_buffer_add_utf8");
+ hblib_state.hb_buffer_add_utf32 = lmt_library_find(lib, "hb_buffer_add_utf32");
+
+ hblib_state.hb_blob_create = lmt_library_find(lib, "hb_blob_create");
+ hblib_state.hb_blob_destroy = lmt_library_find(lib, "hb_blob_destroy");
+
+ hblib_state.hb_face_create = lmt_library_find(lib, "hb_face_create");
+ hblib_state.hb_face_destroy = lmt_library_find(lib, "hb_face_destroy");
+ hblib_state.hb_face_get_upem = lmt_library_find(lib, "hb_face_get_upem");
+
+ hblib_state.hb_font_create = lmt_library_find(lib, "hb_font_create");
+ hblib_state.hb_font_destroy = lmt_library_find(lib, "hb_font_destroy");
+ hblib_state.hb_font_set_scale = lmt_library_find(lib, "hb_font_set_scale");
+
+ hblib_state.hb_shape_list_shapers = lmt_library_find(lib, "hb_shape_list_shapers");
+ hblib_state.hb_shape_full = lmt_library_find(lib, "hb_shape_full");
+
+ hblib_state.hb_ot_font_set_funcs = lmt_library_find(lib, "hb_ot_font_set_funcs");
+
+ hblib_state.hb_buffer_guess_segment_properties = lmt_library_find(lib, "hb_buffer_guess_segment_properties");
+ hblib_state.hb_buffer_get_glyph_positions = lmt_library_find(lib, "hb_buffer_get_glyph_positions");
+ hblib_state.hb_buffer_get_glyph_infos = lmt_library_find(lib, "hb_buffer_get_glyph_infos");
+
+ hblib_state.initialized = lmt_library_okay(lib);
+ }
+ }
+ lua_pushboolean(L, hblib_state.initialized);
+ return 1;
+}
+
+/* <string> = getversion() */
+
+static int hblib_get_version(lua_State * L)
+{
+ if (hblib_state.initialized) {
+ lua_pushstring(L, hblib_state.hb_version_string());
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* <instance> = loadfont(identifier, fontdata) */
+
+static int hblib_load_font(lua_State * L)
+{
+ if (hblib_state.initialized) {
+ int id = (int) lua_tointeger(L, 1);
+ const char *str= lua_tostring(L, 2);
+ int size = (int) lua_rawlen(L, 2);
+ hb_blob_t *blob = hblib_state.hb_blob_create(str, size, 0, NULL, NULL);
+ hb_face_t *face = hblib_state.hb_face_create(blob, id);
+ unsigned int scale = hblib_state.hb_face_get_upem(face);
+ hb_font_t *font = hblib_state.hb_font_create(face);
+ hblib_state.hb_font_set_scale(font, scale, scale);
+ hblib_state.hb_ot_font_set_funcs(font);
+ hblib_data *data = lua_newuserdatauv(L, sizeof(data), 0);
+ data->font = font;
+ luaL_getmetatable(L, HBLIB_METATABLE);
+ lua_setmetatable(L, -2);
+ hblib_state.hb_blob_destroy(blob);
+ hblib_state.hb_face_destroy(face);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* <table> = shapestring(instance, script, language, direction, { shapers }, { features }, text, reverse) */
+
+static int hblib_utf8len(const char *text, size_t size) /* todo: take from utilities */
+{
+ size_t ls = size;
+ int ind = 0;
+ int num = 0;
+ while (ind < (int) ls) {
+ unsigned char i = (unsigned char) *(text + ind);
+ if (i < 0x80) {
+ ind += 1;
+ } else if (i >= 0xF0) {
+ ind += 4;
+ } else if (i >= 0xE0) {
+ ind += 3;
+ } else if (i >= 0xC0) {
+ ind += 2;
+ } else {
+ ind += 1;
+ }
+ num += 1;
+ }
+ return num;
+}
+
+static int hblib_utf32len(const char *text, size_t size)
+{
+ /* not okay, hb doesn't stop at \0 */
+ /* (void) s; */
+ /* return (int) size / 4; */
+ /* so we do this instead */
+ size_t ls = size;
+ int ind = 0;
+ int num = 0;
+ while (ind < (int) ls) {
+ unsigned char i = (unsigned char) *(text + ind);
+ if (i) {
+ ind += 4;
+ } else {
+ break;
+ }
+ num += 1;
+ }
+ return num;
+}
+
+/*tex
+
+ Maybe with |utfbits == 0| take a table with code points, but then we might also need cluster
+ stuff, so there is no gain here.
+
+ I remember some issues with passing features (maybe because some defaults are always set) but
+ it's not really that important because one actually expects the library to handle them that way
+ (read: only enable additional ones). But I will look into it when needed.
+
+*/
+
+static int hblib_shape_string(lua_State * L)
+{
+ if (hblib_state.initialized) {
+ hblib_data *data = luaL_checkudata(L, 1, HBLIB_METATABLE);
+ if (data == NULL) {
+ lua_pushnil(L);
+ } else {
+ /* Maybe we can better take a table, so it's a yet undecided api. */
+ size_t nofscript = 0;
+ const char *script = lua_tolstring(L, 2, &nofscript);
+ size_t noflanguage = 0;
+ const char *language = lua_tolstring(L, 3, &noflanguage);
+ size_t nofdirection = 0;
+ const char *direction = lua_tolstring(L, 4, &nofdirection);
+ int nofshapers = 0;
+ const char * *shapers = NULL; /* slot 5 */
+ int noffeatures = 0;
+ hb_feature_t *features = NULL; /* slot 6 */
+ size_t noftext = 0;
+ const char *text = lua_tolstring(L, 7, &noftext);
+ int reverse = lua_toboolean(L, 8);
+ int utfbits = (int) luaL_optinteger(L, 9, 8);
+ hb_buffer_t *buffer = NULL;
+ /*
+ Shapers are passed as a table; why not pass the length here too ... simpler in
+ ffi -) Maybe I'll make this more static: a general setshaper or so, which is
+ more natural than having it as argument to the shape function.
+
+ MSVC wants |char**| for the shapers and gcc wants |const char**| i.e.\ doesn't
+ like a cast so we just accept the less annoying MSVC warning.
+ */
+ if (lua_istable(L,5)) {
+ lua_Unsigned n = lua_rawlen(L, 5);
+ if (n > 0) {
+ shapers = malloc((size_t) (n + 1) * sizeof(char *));
+ if (shapers) {
+ for (lua_Unsigned i = 0; i < n; i++) {
+ lua_rawgeti(L, 5, i + 1);
+ if (lua_isstring(L, -1)) {
+ shapers[nofshapers] = lua_tostring(L, -1);
+ nofshapers += 1;
+ }
+ lua_pop(L, 1);
+ }
+ } else {
+ luaL_error(L, "optional hblib: unable to allocate shaper memory");
+ }
+ /* sentinal */
+ shapers[nofshapers] = NULL;
+ }
+ }
+ /*
+ Features need to be converted to a table of features (manual work); simpler in
+ ffi -) Maybe I'll move this to the loadfont function.
+ */
+ if (lua_istable(L, 6)) {
+ lua_Unsigned n = lua_rawlen(L, 6);
+ if (n > 0) {
+ features = malloc((size_t) n * sizeof(hb_feature_t));
+ if (features) {
+ for (lua_Unsigned i = 0; i < n; i++) {
+ lua_rawgeti(L, 6, i + 1);
+ if (lua_isstring(L, -1)) {
+ size_t l = 0;
+ const char *s = lua_tolstring(L, -1, &l);
+ hblib_state.hb_feature_from_string(s, (int) l, &features[noffeatures]);
+ noffeatures += 1;
+ }
+ lua_pop(L, 1);
+ }
+ } else {
+ luaL_error(L, "optional hblib: unable to allocate feature memory");
+ }
+ }
+ }
+ /* Some preparations (see original ffi variant). */
+ buffer =hblib_state. hb_buffer_create(); /* we could put this in the data blob */
+ /*
+ When using ffi we used to use utf32 plus some slack because utf8 crashed. It would
+ be more handy if we could pass an array of integers (maybe we can).
+ */
+ if (utfbits == 32) {
+ hblib_state.hb_buffer_add_utf32(buffer, text, (int) noftext, 0, hblib_utf32len(text, noftext));
+ } else { /* 8 */
+ hblib_state.hb_buffer_add_utf8(buffer, text, (int) noftext, 0, hblib_utf8len(text, noftext));
+ }
+ hblib_state.hb_buffer_set_language(buffer, hblib_state.hb_language_from_string(language, (int) noflanguage));
+ hblib_state.hb_buffer_set_script(buffer, hblib_state.hb_script_from_string(script, (int) nofscript));
+ hblib_state.hb_buffer_set_direction(buffer, hblib_state.hb_direction_from_string(direction, (int) nofdirection));
+ hblib_state.hb_buffer_guess_segment_properties(buffer);
+ /* Do it! */
+ hblib_state.hb_shape_full(data->font, buffer, features, noffeatures, shapers);
+ /* Fixup. */
+ if (reverse) {
+ hblib_state.hb_buffer_reverse(buffer);
+ }
+ /* Convert the result: plain and simple.*/
+ {
+ unsigned length = hblib_state.hb_buffer_get_length(buffer);
+ hb_glyph_info_t *infos = hblib_state.hb_buffer_get_glyph_infos(buffer, NULL);
+ hb_glyph_position_t *positions = hblib_state.hb_buffer_get_glyph_positions(buffer, NULL);
+ lua_createtable(L, length, 0);
+ for (unsigned i = 0; i < length; i++) {
+ lua_createtable(L, 6, 0);
+ lua_pushinteger(L, infos[i].codepoint);
+ lua_rawseti(L, -2, 1);
+ lua_pushinteger(L, infos[i].cluster);
+ lua_rawseti(L, -2, 2);
+ lua_pushinteger(L, positions[i].x_offset);
+ lua_rawseti(L, -2, 3);
+ lua_pushinteger(L, positions[i].y_offset);
+ lua_rawseti(L, -2, 4);
+ lua_pushinteger(L, positions[i].x_advance);
+ lua_rawseti(L, -2, 5);
+ lua_pushinteger(L, positions[i].y_advance);
+ lua_rawseti(L, -2, 6);
+ lua_rawseti(L, -2, i + 1);
+ }
+ }
+ hblib_state.hb_buffer_destroy(buffer);
+ free((void *) shapers); /* we didn't make copies of the lua strings, ms compiler gives warning */
+ free((void *) features);
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* <table> = getshapers() */
+
+static int hblib_get_shapers(lua_State * L)
+{
+ if (hblib_state.initialized) {
+ const char * *shapers = hblib_state.hb_shape_list_shapers();
+ if (shapers) {
+ int nofshapers = 0;
+ lua_createtable(L, 1, 0);
+ while (1) {
+ const char *s = shapers[nofshapers];
+ if (s) {
+ nofshapers++;
+ lua_pushstring(L, s);
+ lua_rawseti(L, -2, nofshapers);
+ } else {
+ break;
+ }
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* private */
+
+static int hblib_free(lua_State * L)
+{
+ if (hblib_state.initialized) {
+ hblib_data *data = luaL_checkudata(L, 1, HBLIB_METATABLE);
+ if (data) {
+ hblib_state.hb_font_destroy(data->font);
+ }
+ }
+ return 0;
+}
+
+/* <string> = tostring(instance) */
+
+static int hblib_tostring(lua_State * L)
+{
+ if (hblib_state.initialized) {
+ hblib_data *data = luaL_checkudata(L, 1, HBLIB_METATABLE);
+ if (data) {
+ lua_pushfstring(L, "<optional.hblib.instance %p>", data);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*tex We can do with a rather mimimal user data object. */
+
+static const struct luaL_Reg hblib_metatable[] = {
+ { "__tostring", hblib_tostring },
+ { "__gc", hblib_free },
+ { NULL, NULL },
+};
+
+/*tex
+
+ Idem, just the collected calls of the ffi variant. The less the better because that way there
+ is no tricky code needed at the \LUA\ end.
+
+*/
+
+static struct luaL_Reg hblib_function_list[] = {
+ { "initialize", hblib_initialize },
+ { "getversion", hblib_get_version },
+ { "getshapers", hblib_get_shapers },
+ { "loadfont", hblib_load_font },
+ { "shapestring", hblib_shape_string },
+ { NULL, NULL },
+};
+
+int luaopen_hb(lua_State *L)
+{
+ luaL_newmetatable(L, HBLIB_METATABLE);
+ luaL_setfuncs(L, hblib_metatable, 0);
+ lmt_library_register(L, "hb", hblib_function_list);
+ return 0;
+}