Fixes to Python and Graphite from Martin

This commit is contained in:
Martin Hosken 2010-05-27 10:09:04 +01:00 committed by Behdad Esfahbod
parent b485da0b71
commit 56bef5680c
6 changed files with 159 additions and 116 deletions

10
contrib/python/README Normal file
View File

@ -0,0 +1,10 @@
This contains a wrapping of harfbuzz into python. The module is dependent on pyrex. To build, type:
python setup.py build
In addition there is a test application, hbtestfont. It has GTK based gui output and for this, python modules for gtk, gobject and cairo are needed. The application may be run without gui output using the --nogui option.
Applications may be executed in the build context, without needing to install any modules or libraries, using the runpy script from the contrib/python directory. Thus one might type:
./runpy script/hbtestfont -f "Charis SIL" 0048 0069 0303

View File

@ -0,0 +1,47 @@
cdef extern from "fontconfig/fontconfig.h" :
ctypedef struct FcPattern :
pass
ctypedef struct FcConfig :
pass
cdef enum FcResult '_FcResult' :
FcResultMatch = 0, FcResultNoMatch, FcResultTypeMismatch, FcResultNoId,
FcResultOutOfMemory
ctypedef char FcChar8
FcPattern *FcNameParse(FcChar8 *name)
FcPattern *FcFontMatch(FcConfig *config, FcPattern *match, FcResult *res)
FcResult FcPatternGetInteger(FcPattern *pattern, char *typeid, int index, int *res)
FcResult FcPatternGetString(FcPattern *pattern, char *typeid, int index, FcChar8 **res)
void FcPatternPrint(FcPattern *pattern)
void FcPatternDestroy(FcPattern *pattern)
FcConfig *FcConfigGetCurrent()
cdef class fcPattern :
cdef FcPattern *_pattern
def __init__(self, char *name) :
cdef FcPattern *temp
cdef FcResult res
temp = FcNameParse(<FcChar8 *>name)
self._pattern = FcFontMatch(FcConfigGetCurrent(), temp, &res)
if res != FcResultMatch :
print "Failed to match" + str(res)
self._pattern = <FcPattern *>0
def __destroy__(self) :
FcPatternDestroy(self._pattern)
def getInteger(self, char *typeid, int index) :
cdef int res
if self._pattern == <FcPattern *>0 or FcPatternGetInteger(self._pattern, typeid, index, &res) != FcResultMatch : return None
return res
def getString(self, char *typeid, int index) :
cdef FcChar8 *res
if self._pattern == <FcPattern *>0 or FcPatternGetString(self._pattern, typeid, index, &res) != FcResultMatch : return None
return <char *>res
def debugPrint(self) :
FcPatternPrint(self._pattern)

View File

@ -33,103 +33,7 @@ cdef extern from "hb-common.h" :
cdef extern from "hb-unicode.h" : cdef extern from "hb-unicode.h" :
# there must be a better way of syncing this list with the true source # there must be a better way of syncing this list with the true source
ctypedef enum hb_script_t : ctypedef enum hb_script_t :
HB_SCRIPT_INVALID_CODE = -1, HB_SCRIPT_COMMON = 0
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
# Unicode-5.2
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_MEITEI_MAYEK
HB_SCRIPT_OLD_SOUTH_ARABIAN
HB_SCRIPT_OLD_TURKIC
HB_SCRIPT_SAMARITAN
HB_SCRIPT_TAI_THAM
HB_SCRIPT_TAI_VIET
cdef extern from "hb-language.h" : cdef extern from "hb-language.h" :
ctypedef void *hb_language_t ctypedef void *hb_language_t
@ -137,7 +41,7 @@ cdef extern from "hb-language.h" :
char * hb_language_to_string(hb_language_t language) char * hb_language_to_string(hb_language_t language)
cdef extern from "hb-ot-tag.h" : cdef extern from "hb-ot-tag.h" :
hb_script_t hb_ot_tag_to_script (char *sname) hb_script_t hb_ot_tag_to_script (hb_tag_t tag)
cdef extern from "hb-buffer.h" : cdef extern from "hb-buffer.h" :
ctypedef struct hb_buffer_t : ctypedef struct hb_buffer_t :
@ -237,7 +141,7 @@ cdef class buffer :
cdef hb_script_t scriptnum cdef hb_script_t scriptnum
language = hb_language_from_string(lang) language = hb_language_from_string(lang)
scriptnum = hb_ot_string_to_script(script) scriptnum = hb_ot_tag_to_script(hb_tag_from_string(script))
hb_buffer_set_script(self.buffer, scriptnum) hb_buffer_set_script(self.buffer, scriptnum)
hb_buffer_set_language(self.buffer, language) hb_buffer_set_language(self.buffer, language)
@ -279,7 +183,7 @@ cdef class ft :
self.engine = engine self.engine = engine
cdef FT_Face face cdef FT_Face face
FT_New_Face(engine, fname, 0, &face) FT_New_Face(engine, fname, 0, &face)
FT_Set_Char_Size(face, size << 6, size << 6, 144, 144) FT_Set_Char_Size(face, size << 6, size << 6, 72, 72)
self.face = face self.face = face
self.hbface = hb_ft_face_create(face, <void (*)(void *)>hb_face_destroy) self.hbface = hb_ft_face_create(face, <void (*)(void *)>hb_face_destroy)
self.hbfont = hb_ft_font_create(face, <void (*)(void *)>hb_font_destroy) self.hbfont = hb_ft_font_create(face, <void (*)(void *)>hb_font_destroy)

View File

@ -1,27 +1,43 @@
#!/usr/bin/python #!/usr/bin/python
import harfbuzz, optparse import harfbuzz, optparse, sys
from fontconfig import fcPattern
buffer = None usage = '''usage: %prog [options] codepoints
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: Generates output of glyphs and positions. Each entry is of the form:
glyphid>cluster@(offsetx,offsety)+(advancex,advancey) glyphid>cluster@(offsetx,offsety)+(advancex,advancey)
codepoints is a space separated list of hex values of Unicode codepoints''' codepoints is a space separated list of hex values of Unicode codepoints'''
p = optparse.OptionParser(usage=usage) p = optparse.OptionParser(usage=usage)
p.add_option('-s', '--size', default=12, type="int", help="point size") p.add_option('-s', '--size', default=32, type="int", help="point size")
p.add_option('-l', '--lang', help="language code") p.add_option('-l', '--lang', help="language code")
p.add_option('-c', '--script', help="script 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('-F', '--feature', action='append', help="define a feature key=val")
p.add_option('-f', '--font', help='Font to use to render glyphs. My be a font file', default="verdana")
p.add_option('-b', '--bold', help='Choose bold fonts', action='store_true')
p.add_option('-i', '--italic', help='Choose italic fonts', action='store_true')
p.add_option('-d', '--debug', action='store_true', help="Output trace info") p.add_option('-d', '--debug', action='store_true', help="Output trace info")
p.add_option('--nogui', action='store_true', help="Don't display a gui")
(opts, args) = p.parse_args() (opts, args) = p.parse_args()
ft = harfbuzz.ft(args[0], opts.size, trace = tracefn if opts.debug else None) if opts.font.lower().endswith(".ttf") :
text = "".join(unichr(int(c, 16)) for c in args[1].split(" ")) fpat = ":file="
else :
fpat = ""
fpat += opts.font + ":weight="
fpat += "bold" if opts.bold else "medium"
fpat += ":slant="
fpat += "italic" if opts.italic else "roman"
pat = fcPattern(fpat)
fname = pat.getString("file", 0)
family = pat.getString("family", 0)
print "Processing: " + fname
if opts.font.lower().endswith(".ttf") and opts.font != fname :
print "Failed to find font in fontconfig. Exiting"
sys.exit(1)
ft = harfbuzz.ft(fname, opts.size)
text = "".join(unichr(int(c, 16)) for c in args)
bytes = text.encode('utf_8') bytes = text.encode('utf_8')
buffer = harfbuzz.buffer(bytes, len(text)) 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 "") if (opts.lang or opts.script) : buffer.set_scriptlang(opts.script if opts.script else "", opts.lang if opts.lang else "")
@ -33,3 +49,68 @@ if opts.feature :
ft.shape(buffer, features = features) ft.shape(buffer, features = features)
res = buffer.get_info(64) # scale for 26.6 res = buffer.get_info(64) # scale for 26.6
print res print res
if not opts.nogui :
try:
import gtk
import gobject
import cairo
from gtk import gdk
except :
raise SystemExit
import pygtk
if gtk.pygtk_version < (2, 8) :
print "PyGtk 2.8 or later required"
raise SystemExit
class GlyphsWindow (gtk.Widget) :
def __init__(self, fontname, bold, italic, size, glyphs) :
gtk.Widget.__init__(self)
self.fontname = fontname
self.size = size
self.glyphs = glyphs
self.bold = bold
self.italic = italic
def do_realize(self) :
self.set_flags(gtk.REALIZED)
self.window = gdk.Window(
self.get_parent_window(),
width = self.allocation.width,
height = self.allocation.height,
window_type = gdk.WINDOW_CHILD,
wclass = gdk.INPUT_OUTPUT,
event_mask = self.get_events() | gdk.EXPOSURE_MASK)
self.window.set_user_data(self)
self.style.attach(self.window)
self.style.set_background(self.window, gtk.STATE_NORMAL)
self.window.move_resize(*self.allocation)
def do_unrealize(self) :
self.window.destroy()
def do_expose_event(self, event) :
cr = self.window.cairo_create()
cr.set_matrix(cairo.Matrix(1, 0, 0, 1, 0, 1.5 * self.size))
cr.set_font_face(cairo.ToyFontFace(self.fontname, cairo.FONT_SLANT_ITALIC if self.italic else cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD if self.bold else cairo.FONT_WEIGHT_NORMAL))
cr.set_font_size(self.size)
cr.show_glyphs(self.glyphs) # [(gid, originx, originy)]
glyphs = []
org = [0, 0]
for g in res :
glyphs.append((g.gid, org[0] + g.offset[0], org[1] + g.offset[1]))
org[0] += g.advance[0]
org[1] += g.advance[1]
gobject.type_register(GlyphsWindow)
win = gtk.Window()
win.resize(org[0] + 10, 2 * opts.size + 40)
win.connect('delete-event', gtk.main_quit)
frame = gtk.Frame("glyphs")
win.add(frame)
w = GlyphsWindow(family, opts.bold, opts.italic, opts.size, glyphs)
frame.add(w)
win.show_all()
gtk.main()

View File

@ -13,7 +13,8 @@ setup(name='harfbuzz',
maintainer_email='martin_hosken@sil.org', maintainer_email='martin_hosken@sil.org',
packages=['harfbuzz'], packages=['harfbuzz'],
ext_modules = [ ext_modules = [
Extension("harfbuzz", ["lib/harfbuzz.pyx"], libraries=["harfbuzz"], library_dirs=["../../src/.libs"], include_dirs=["/usr/include/freetype2", "../../src"]) Extension("harfbuzz", ["lib/harfbuzz.pyx"], libraries=["harfbuzz"], library_dirs=["../../src/.libs"], include_dirs=["/usr/include/freetype2", "../../src"]),
Extension("fontconfig", ["lib/fontconfig.pyx"], libraries=["fontconfig"])
], ],
cmdclass = {'build_ext' : build_ext}, cmdclass = {'build_ext' : build_ext},
scripts = glob('scripts/*'), scripts = glob('scripts/*'),

View File

@ -264,7 +264,7 @@ hb_graphite_shape (hb_font_t *font,
hb_buffer_ensure(buffer, numGlyphs); hb_buffer_ensure(buffer, numGlyphs);
pSegment.getUniscribeClusters(firsts, numChars, NULL, flags, numGlyphs, NULL); pSegment.getUniscribeClusters(firsts, numChars, NULL, flags, numGlyphs, NULL);
glyph_range = pSegment.glyphs(); glyph_range = pSegment.glyphs();
for (pGlyph = glyph_infos, iGlyph = glyph_range.first; iGlyph < glyph_range.second; for (pGlyph = glyph_infos, iGlyph = glyph_range.first; iGlyph != glyph_range.second;
iGlyph++, pGlyph++) iGlyph++, pGlyph++)
{ *pGlyph = iGlyph->glyphID(); } { *pGlyph = iGlyph->glyphID(); }
@ -285,14 +285,14 @@ hb_graphite_shape (hb_font_t *font,
float curradvx = 0., curradvy = 0.; float curradvx = 0., curradvy = 0.;
for (pPosition = hb_buffer_get_glyph_positions(buffer), iGlyph = glyph_range.first; for (pPosition = hb_buffer_get_glyph_positions(buffer), iGlyph = glyph_range.first;
iGlyph < glyph_range.second; pPosition++, iGlyph++) iGlyph != glyph_range.second; pPosition++, iGlyph++)
{ {
pPosition->x_offset = iGlyph->origin() - curradvx; pPosition->x_offset = iGlyph->origin() - curradvx;
pPosition->y_offset = iGlyph->yOffset() - curradvy; pPosition->y_offset = iGlyph->yOffset() - curradvy;
pPosition->x_advance = pPosition->x_offset + iGlyph->advanceWidth(); pPosition->x_advance = pPosition->x_offset + iGlyph->advanceWidth();
pPosition->y_advance = pPosition->y_offset + iGlyph->advanceHeight(); pPosition->y_advance = pPosition->y_offset + iGlyph->advanceHeight();
// if (pPosition->x_advance < 0) if (pPosition->x_advance < 0 && iGlyph->logicalIndex() != iGlyph->attachedClusterBase()->logicalIndex())
// pPosition->x_advance = 0; pPosition->x_advance = 0;
curradvx += pPosition->x_advance; curradvx += pPosition->x_advance;
curradvy += pPosition->y_advance; curradvy += pPosition->y_advance;
// fprintf(stderr, "%d@(%f, %f)+(%f, %f)\n", iGlyph->glyphID(), iGlyph->origin(), iGlyph->yOffset(), iGlyph->advanceWidth(), iGlyph->advanceHeight()); // fprintf(stderr, "%d@(%f, %f)+(%f, %f)\n", iGlyph->glyphID(), iGlyph->origin(), iGlyph->yOffset(), iGlyph->advanceWidth(), iGlyph->advanceHeight());