From 50355309047765558ef8f5d60aefed42a7f954cc Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 21 May 2010 10:33:23 +0100 Subject: [PATCH] Add Python wrapper from Martin Hosken --- contrib/python/lib/harfbuzz.pyx | 306 ++++++++++++++++++++++++++++++ contrib/python/runpy | 2 + contrib/python/scripts/hbtestfont | 35 ++++ contrib/python/setup.py | 24 +++ 4 files changed, 367 insertions(+) create mode 100644 contrib/python/lib/harfbuzz.pyx create mode 100755 contrib/python/runpy create mode 100755 contrib/python/scripts/hbtestfont create mode 100755 contrib/python/setup.py diff --git a/contrib/python/lib/harfbuzz.pyx b/contrib/python/lib/harfbuzz.pyx new file mode 100644 index 000000000..1f254ff1f --- /dev/null +++ b/contrib/python/lib/harfbuzz.pyx @@ -0,0 +1,306 @@ +cdef extern from "stdlib.h": + ctypedef int size_t + void *malloc(size_t size) + void free(void* ptr) + +cdef extern from "ft2build.h" : + pass + +cdef extern from "freetype/freetype.h" : + ctypedef void *FT_Library + ctypedef void *FT_Face + ctypedef int FT_Error + + FT_Error FT_Init_FreeType (FT_Library *alib) + FT_Error FT_Done_FreeType (FT_Library alib) + FT_Error FT_Done_Face (FT_Face alib) + FT_Error FT_New_Face( FT_Library library, char *path, unsigned long index, FT_Face *aface) + FT_Error FT_Set_Char_Size (FT_Face aFace, unsigned int size_x, unsigned int size_y, unsigned int res_x, unsigned int res_y) + +cdef extern from "hb-common.h" : + cdef enum hb_direction_t : + HB_DIRECTION_LTR + HB_DIRECTION_RTL + HB_DIRECTION_TTB + HB_DIRECTION_BTT + ctypedef unsigned long hb_codepoint_t + ctypedef long hb_position_t + ctypedef unsigned long hb_mask_t + ctypedef unsigned long hb_tag_t + ctypedef void (*hb_destroy_func_t) (void *user_data) + +cdef extern from "hb-unicode.h" : +# there must be a better way of syncing this list with the true source + ctypedef enum hb_script_t : + HB_SCRIPT_INVALID_CODE = -1, + HB_SCRIPT_COMMON = 0, + HB_SCRIPT_INHERITED, + HB_SCRIPT_ARABIC, + HB_SCRIPT_ARMENIAN, + HB_SCRIPT_BENGALI, + HB_SCRIPT_BOPOMOFO, + HB_SCRIPT_CHEROKEE, + HB_SCRIPT_DESERET, + HB_SCRIPT_DEVANAGARI, + HB_SCRIPT_ETHIOPIC, + HB_SCRIPT_GEORGIAN, + HB_SCRIPT_GOTHIC, + HB_SCRIPT_GREEK, + HB_SCRIPT_GUJARATI, + HB_SCRIPT_GURMUKHI, + HB_SCRIPT_HAN, + HB_SCRIPT_HANGUL, + HB_SCRIPT_HEBREW, + HB_SCRIPT_HIRAGANA, + HB_SCRIPT_KANNADA, + HB_SCRIPT_KATAKANA, + HB_SCRIPT_KHMER, + HB_SCRIPT_LAO, + HB_SCRIPT_LATIN, + HB_SCRIPT_MALAYALAM, + HB_SCRIPT_MONGOLIAN, + HB_SCRIPT_MYANMAR, + HB_SCRIPT_OGHAM, + HB_SCRIPT_OLD_ITALIC, + HB_SCRIPT_ORIYA, + HB_SCRIPT_RUNIC, + HB_SCRIPT_SINHALA, + HB_SCRIPT_SYRIAC, + HB_SCRIPT_TAMIL, + HB_SCRIPT_TELUGU, + HB_SCRIPT_THAANA, + HB_SCRIPT_THAI, + HB_SCRIPT_TIBETAN, + HB_SCRIPT_CANADIAN_ABORIGINAL, + HB_SCRIPT_YI, + HB_SCRIPT_TAGALOG, + HB_SCRIPT_HANUNDO, + HB_SCRIPT_BUHID, + HB_SCRIPT_TAGBANWA, +# Unicode 4.0 + HB_SCRIPT_BRAILLE, + HB_SCRIPT_CYPRIOT, + HB_SCRIPT_LIMBU, + HB_SCRIPT_OSMANYA, + HB_SCRIPT_SHAVIAN, + HB_SCRIPT_LINEAR_B, + HB_SCRIPT_TAI_LE, + HB_SCRIPT_UGARITIC, +# Unicode 4.1 + HB_SCRIPT_NEW_TAI_LUE, + HB_SCRIPT_BUGINESE, + HB_SCRIPT_GLAGOLITIC, + HB_SCRIPT_TIFINAGH, + HB_SCRIPT_SYLOTI_NAGRI, + HB_SCRIPT_OLD_PERSIAN, + HB_SCRIPT_KHAROSHTHI, +# Unicode 5.0 + HB_SCRIPT_UNKNOWN, + HB_SCRIPT_BALINESE, + HB_SCRIPT_CUNEIFORM, + HB_SCRIPT_PHOENICIAN, + HB_SCRIPT_PHAGS_PA, + HB_SCRIPT_NKO, +# Unicode 5.1 + HB_SCRIPT_KAYAH_LI, + HB_SCRIPT_LEPCHA, + HB_SCRIPT_REJANG, + HB_SCRIPT_SUNDANESE, + HB_SCRIPT_SAURASHTRA, + HB_SCRIPT_CHAM, + HB_SCRIPT_OL_CHIKI, + HB_SCRIPT_VAI, + HB_SCRIPT_CARIAN, + HB_SCRIPT_LYCIAN, + HB_SCRIPT_LYDIAN + +cdef extern from "hb-language.h" : + ctypedef void *hb_language_t + hb_language_t hb_language_from_string(char *str) + char * hb_language_to_string(hb_language_t language) + +cdef extern from "hb-ot-tag.h" : + hb_script_t hb_ot_string_to_script (char *sname) + +cdef extern from "hb-buffer.h" : + ctypedef struct hb_buffer_t : + pass + + ctypedef struct hb_glyph_info_t : + hb_codepoint_t codepoint + hb_mask_t mask + unsigned long cluster + + ctypedef 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 + unsigned long internal + + hb_buffer_t *hb_buffer_create(unsigned int size) + hb_buffer_t *hb_buffer_reference(hb_buffer_t *buffer) + unsigned int hb_buffer_get_reference_count(hb_buffer_t *buffer) + void hb_buffer_destroy(hb_buffer_t *buffer) + void hb_buffer_set_direction(hb_buffer_t *buffer, hb_direction_t direction) + hb_direction_t hb_buffer_get_direction(hb_buffer_t *buffer) + void hb_buffer_set_script(hb_buffer_t *buffer, hb_script_t script) + hb_script_t hb_buffer_get_script(hb_buffer_t *buffer) + void hb_buffer_set_language(hb_buffer_t *buffer, hb_language_t language) + hb_language_t hb_buffer_get_language(hb_buffer_t *buffer) + void hb_buffer_clear(hb_buffer_t *) + void hb_buffer_clear_positions(hb_buffer_t *buffer) + void hb_buffer_ensure(hb_buffer_t *buffer, unsigned int size) + void hb_buffer_reverse(hb_buffer_t *buffer) + void hb_buffer_reverse_clusters(hb_buffer_t *buffer) + void hb_buffer_add_glyph(hb_buffer_t *buffer, hb_codepoint_t codepoint, hb_mask_t mask, unsigned int cluster) + void hb_buffer_add_utf8(hb_buffer_t *buffer, char *text, unsigned int text_length, unsigned int item_offset, unsigned int item_length) + unsigned int hb_buffer_get_length(hb_buffer_t *buffer) + hb_glyph_info_t *hb_buffer_get_glyph_infos(hb_buffer_t *buffer) + hb_glyph_position_t *hb_buffer_get_glyph_positions(hb_buffer_t *buffer) + +cdef extern from "hb-blob.h" : + cdef struct hb_blob_t : + pass +# do I need blob functions here? + +cdef extern from "hb-font.h" : + ctypedef struct hb_face_t : + pass + ctypedef struct hb_font_t : + pass + + ctypedef hb_blob_t * (*hb_get_table_func_t) (hb_tag_t tag, void *user_data) + hb_face_t * hb_face_create_for_data(hb_blob_t *blob, unsigned int index) + hb_face_t * hb_face_create_for_tables(hb_get_table_func_t get_table, hb_destroy_func_t destroy, void *user_data) + hb_face_t * hb_face_reference(hb_face_t *face) + unsigned int hb_face_get_reference_count(hb_face_t *face) + void hb_face_destroy(hb_face_t *face) + void hb_font_destroy(hb_font_t *font) + hb_blob_t * hb_face_get_table(hb_face_t *face, hb_tag_t tag) + void hb_font_set_tracecallback(hb_font_t *font, void (*cb)(char *type, int index, hb_buffer_t *)) + + +cdef extern from "hb-shape.h" : + ctypedef struct hb_feature_t : + int tag + unsigned int value + unsigned int start + unsigned int end + + void hb_shape (hb_font_t *font, hb_face_t *face, hb_buffer_t *buffer, hb_feature_t *features, unsigned int num_features) + +cdef extern from "hb-ft.h" : + hb_face_t *hb_ft_face_create (FT_Face ft_face, hb_destroy_func_t destroy) + hb_font_t *hb_ft_font_create (FT_Face ft_face, hb_destroy_func_t destroy) + +class glyphinfo : + + def __init__(self, gid, cluster, advance, offset, internal = 0) : + self.gid = gid + self.cluster = cluster + self.advance = advance + self.offset = offset + self.internal = internal + + def __repr__(self) : + res = "{0:d}>{1:d}@({2:.2f},{3:.2f})+({4:.2f},{5:.2f})".format(self.gid, self.cluster, self.offset[0], self.offset[1], self.advance[0], self.advance[1]) + if self.internal : res += "/i=" + str(self.internal) + return res + +gcb = None +cdef void hb_python_trace(char *aType, int i, hb_buffer_t *aBuffer) : + gcb(aType, i) + +cdef class buffer : + cdef hb_buffer_t *buffer + + def __init__(self, char *text, unsigned int length) : + """Note text must be a utf-8 string and length is number of chars""" + self.buffer = hb_buffer_create(length) + hb_buffer_add_utf8(self.buffer, text, length, 0, len(text)) + + def set_scriptlang(self, char *script, char *lang) : + cdef hb_language_t language + cdef hb_script_t scriptnum + + language = hb_language_from_string(lang) + scriptnum = hb_ot_string_to_script(script) + hb_buffer_set_script(self.buffer, scriptnum) + hb_buffer_set_language(self.buffer, language) + + def get_info(self, scale = 1) : + cdef hb_glyph_info_t *infos + cdef hb_glyph_position_t *positions + cdef unsigned int num + cdef unsigned int i + res = [] + + num = hb_buffer_get_length(self.buffer) + infos = hb_buffer_get_glyph_infos(self.buffer) + positions = hb_buffer_get_glyph_positions(self.buffer) + for 0 <= i < num : + temp = glyphinfo(infos[i].codepoint, infos[i].cluster, (positions[i].x_advance / scale, positions[i].y_advance / scale), (positions[i].x_offset / scale, positions[i].y_offset / scale), positions[i].internal) + res.append(temp) + return res + + def get_settings(self) : + cdef hb_script_t script + cdef hb_language_t lang + + script = hb_buffer_get_script(self.buffer) + lang = hb_buffer_get_language(self.buffer) + return {'script' : script, 'language' : hb_language_to_string(lang)} + + def __del__(self) : + hb_buffer_destroy(self.buffer) + +cdef class ft : + cdef FT_Library engine + cdef FT_Face face + cdef hb_face_t *hbface + cdef hb_font_t *hbfont + cdef object tracefn + + def __init__(self, char *fname, size, trace = None) : + global gcb + cdef FT_Library engine + FT_Init_FreeType(&engine) + self.engine = engine + cdef FT_Face face + FT_New_Face(engine, fname, 0, &face) + FT_Set_Char_Size(face, size << 6, size << 6, 144, 144) + self.face = face + self.hbface = hb_ft_face_create(face, hb_face_destroy) + self.hbfont = hb_ft_font_create(face, hb_font_destroy) + if trace : + self.tracefn = trace + gcb = self.trace + hb_font_set_tracecallback(self.hbfont, hb_python_trace) + + def trace(self, aType, index) : + self.tracefn(self, aType, index) + + def __del__(self) : + cdef FT_Library engine + engine = self.engine + cdef FT_Face face + face = self.face + FT_Done_Face(face) + FT_Done_FreeType(engine) + + def shape(self, buffer aBuffer, features = {}) : + cdef hb_feature_t *feats + cdef hb_feature_t *aFeat + feats = malloc(sizeof(hb_feature_t) * len(features)) + aFeat = feats + for k,v in features.items() : + k = k + " " + aFeat.tag = (ord(k[0]) << 24) + (ord(k[1]) << 16) + (ord(k[2]) << 8) + ord(k[3]) + aFeat.value = int(v) + aFeat.start = 0 + aFeat.end = -1 + aFeat += 1 + hb_shape(self.hbfont, self.hbface, aBuffer.buffer, feats, len(features)) + + diff --git a/contrib/python/runpy b/contrib/python/runpy new file mode 100755 index 000000000..b39db1b37 --- /dev/null +++ b/contrib/python/runpy @@ -0,0 +1,2 @@ +#!/bin/sh +LD_LIBRARY_PATH=../../src/.libs PYTHONPATH=build/lib.`python -c 'import distutils.util, sys; print distutils.util.get_platform()+"-"+str(sys.version_info[0])+"."+str(sys.version_info[1])'` "$@" diff --git a/contrib/python/scripts/hbtestfont b/contrib/python/scripts/hbtestfont new file mode 100755 index 000000000..4d1134d1f --- /dev/null +++ b/contrib/python/scripts/hbtestfont @@ -0,0 +1,35 @@ +#!/usr/bin/python + +import harfbuzz, optparse + +buffer = None + +def tracefn(ft, aType, index) : + print aType + "(" + str(index) + "): " + str(buffer.get_info()) + +usage = '''usage: %prog [options] fontfile "codepoints" + Generates output of glyphs and positions. Each entry is of the form: + glyphid>cluster@(offsetx,offsety)+(advancex,advancey) + + codepoints is a space separated list of hex values of Unicode codepoints''' +p = optparse.OptionParser(usage=usage) +p.add_option('-s', '--size', default=12, type="int", help="point size") +p.add_option('-l', '--lang', help="language code") +p.add_option('-c', '--script', help="script code") +p.add_option('-f', '--feature', action='append', help="define a feature key=val") +p.add_option('-d', '--debug', action='store_true', help="Output trace info") +(opts, args) = p.parse_args() + +ft = harfbuzz.ft(args[0], opts.size, trace = tracefn if opts.debug else None) +text = "".join(unichr(int(c, 16)) for c in args[1].split(" ")) +bytes = text.encode('utf_8') +buffer = harfbuzz.buffer(bytes, len(text)) +if (opts.lang or opts.script) : buffer.set_scriptlang(opts.script if opts.script else "", opts.lang if opts.lang else "") +features = {} +if opts.feature : + for f in opts.feature : + k, v = f.split("=") + features[k] = v +ft.shape(buffer, features = features) +res = buffer.get_info(64) # scale for 26.6 +print res diff --git a/contrib/python/setup.py b/contrib/python/setup.py new file mode 100755 index 000000000..ef2412b48 --- /dev/null +++ b/contrib/python/setup.py @@ -0,0 +1,24 @@ +#!/usr/bin/python + +from distutils.core import setup +from glob import glob +from Pyrex.Distutils.extension import Extension +from Pyrex.Distutils import build_ext + +setup(name='harfbuzz', + version='0.0.1', + description='Harfbuzz compatibility layer', + long_description='Harfbuzz python integration modules and supporting scripts', + maintainer='Martin Hosken', + maintainer_email='martin_hosken@sil.org', + packages=['harfbuzz'], + ext_modules = [ + Extension("harfbuzz", ["lib/harfbuzz.pyx"], libraries=["harfbuzz"], library_dirs=["../../src/.libs"], include_dirs=["/usr/include/freetype2", "../../src"]) + ], + cmdclass = {'build_ext' : build_ext}, + scripts = glob('scripts/*'), + license = 'LGPL', + platforms = ['Linux', 'Win32', 'Mac OS X'], + package_dir = {'harfbuzz' : 'lib'} +) +