diff --git a/configure.ac b/configure.ac index 0cc01b703..721c91292 100644 --- a/configure.ac +++ b/configure.ac @@ -33,6 +33,12 @@ if $have_icu; then fi AM_CONDITIONAL(HAVE_ICU, $have_icu) +PKG_CHECK_MODULES(GRAPHITE, silgraphite, have_graphite=true, have_graphite=false) +if $have_graphite; then + AC_DEFINE(HAVE_GRAPHITE, 1, [Have Graphite library]) +fi +AM_CONDITIONAL(HAVE_GRAPHITE, $have_graphite) + PKG_CHECK_MODULES(FREETYPE, freetype2, have_freetype=true, have_freetype=false) if $have_freetype; then AC_DEFINE(HAVE_FREETYPE, 1, [Have FreeType 2 library]) diff --git a/src/Makefile.am b/src/Makefile.am index 70cbfd858..0ed02e14e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -89,6 +89,17 @@ HBHEADERS += \ $(NULL) endif +if HAVE_GRAPHITE +HBCFLAGS += $(GRAPHITE_CFLAGS) +HBLIBS += $(GRAPHITE_LIBS) +HBSOURCES += \ + hb-graphite.cc \ + $(NULL) +HBHEADERS += \ + hb-graphite.h \ + $(NULL) +endif + CXXLINK = $(LINK) libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS) libharfbuzz_la_CPPFLAGS = $(HBCFLAGS) diff --git a/src/hb-graphite.cc b/src/hb-graphite.cc new file mode 100644 index 000000000..ba1169a74 --- /dev/null +++ b/src/hb-graphite.cc @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2009, Martin Hosken + * Copyright (C) 2009, SIL International + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include +#include +#include +#include +#include +#include "hb-buffer-private.hh" +#include "hb-font-private.hh" +#include "hb-graphite.h" +#include + +namespace TtfUtil +{ +extern int FontAscent(const void *pOS2); +extern int FontDescent(const void *pOS2); +extern int DesignUnits(const void *pHead); +extern bool FontOs2Style(const void *pOS2, bool &fBold, bool &fItalic); +} + +typedef struct _featureSetting { + unsigned int id; + int value; +} featureSetting; + +class GrBufferTextSrc : public gr::ITextSource +{ +public: + GrBufferTextSrc(hb_buffer_t *buff, hb_feature_t *feats, unsigned int num_features) + { + int i; + hb_feature_t *aFeat = feats; + featureSetting *aNewFeat; + + buffer = hb_buffer_reference(buff); + features = new featureSetting[num_features]; + nFeatures = num_features; + aNewFeat = features; + for (i = 0; i < num_features; i++, aFeat++, aNewFeat++) + { + aNewFeat->id = aFeat->tag; + aNewFeat->value = aFeat->value; + } + }; + ~GrBufferTextSrc() { hb_buffer_destroy(buffer); delete[] features; }; + virtual gr::UtfType utfEncodingForm() { return gr::kutf32; }; + virtual size_t getLength() { return buffer->len; }; + virtual size_t fetch(gr::toffset ichMin, size_t cch, gr::utf32 * prgchBuffer) + { + int i; + + assert(cch <= buffer->len); + if (cch > buffer->len) + return 0; + for (i = ichMin; i < ichMin + cch; i++) + prgchBuffer[i - ichMin] = buffer->info[i].codepoint; + return (cch - ichMin); + }; + virtual size_t fetch(gr::toffset ichMin, size_t cch, gr::utf16 * prgchBuffer) { return 0 ;}; + virtual size_t fetch(gr::toffset ichMin, size_t cch, gr::utf8 * prgchBuffer) { return 0; }; + virtual bool getRightToLeft(gr::toffset ich) + { return hb_buffer_get_direction(buffer) == HB_DIRECTION_RTL; }; + virtual unsigned int getDirectionDepth(gr::toffset ich) + { return hb_buffer_get_direction(buffer) == HB_DIRECTION_RTL ? 1 : 0; }; + virtual float getVerticalOffset(gr::toffset ich) { return 0; }; + virtual gr::isocode getLanguage(gr::toffset ich) + { + gr::isocode aLang; + char *p = (char *)(buffer->language); + int i; + for (i = 0; i < 4; i++) + { + if (p != NULL) + aLang.rgch[i] = *p; + else + aLang.rgch[i] = 0; + if (p && *p) + p++; + } + return aLang; + } + + virtual std::pair propertyRange(gr::toffset ich) + { return std::pair(0, buffer->len); }; + virtual size_t getFontFeatures(gr::toffset ich, gr::FeatureSetting * prgfset) + { + int i; + featureSetting *aFeat = features; + for (i = 0; i < nFeatures; i++, aFeat++, prgfset++) + { + prgfset->id = aFeat->id; + prgfset->value = aFeat->value; + } + return nFeatures; + } + virtual bool sameSegment(gr::toffset ich1, gr::toffset ich2) {return true; }; + +private: + hb_buffer_t *buffer; + featureSetting *features; + unsigned int nFeatures; +}; + +class GrHbFont : public gr::Font +{ +public: + GrHbFont(hb_font_t *font, hb_face_t *face) : gr::Font() + { m_font = hb_font_reference(font); m_face = hb_face_reference(face); initfont(); }; + ~GrHbFont() + { + std::map::iterator p = m_blobs.begin(); + while (p != m_blobs.end()) + { hb_blob_destroy((p++)->second); } + hb_font_destroy(m_font); + hb_face_destroy(m_face); + }; + GrHbFont (const GrHbFont &font) : gr::Font(font) + { + *this = font; + m_blobs = std::map(font.m_blobs); + std::map::iterator p=m_blobs.begin(); + while (p != m_blobs.end()) { hb_blob_reference((*p++).second); } + hb_font_reference(m_font); + hb_face_reference(m_face); + }; + virtual GrHbFont *copyThis() { return new GrHbFont(*this); }; + virtual bool bold() { return m_bold; }; + virtual bool italic() { return m_italic; }; + virtual float ascent() { float asc; getFontMetrics(&asc, NULL, NULL); return asc; }; + virtual float descent() { float desc; getFontMetrics(NULL, &desc, NULL); return desc; }; + virtual float height() + { float asc, desc; getFontMetrics(&asc, &desc, NULL); return (asc + desc); }; + virtual unsigned int getDPIx() { return m_font->x_ppem; }; + virtual unsigned int getDPIy() { return m_font->y_ppem; }; + virtual const void *getTable(gr::fontTableId32 tableID, size_t *pcbsize) + { + hb_blob_t *blob; + std::map::iterator p=m_blobs.find((hb_tag_t)tableID); + if (p == m_blobs.end()) + { + blob = hb_face_get_table(m_face, (hb_tag_t)tableID); + m_blobs[(hb_tag_t)tableID] = blob; + } + else + { blob = p->second; } + + const char *res = hb_blob_lock(blob); + if (pcbsize) + *pcbsize = hb_blob_get_length(blob); + hb_blob_unlock(blob); + return (const void *)res; + } + + virtual void getFontMetrics(float *pAscent, float *pDescent, float *pEmSquare) + { + if (pAscent) *pAscent = 1. * m_ascent * m_font->y_ppem / m_emsquare; + if (pDescent) *pDescent = 1. * m_descent * m_font->y_ppem / m_emsquare; + if (pEmSquare) *pEmSquare = m_font->x_scale; + } + virtual void getGlyphPoint(gr::gid16 glyphID, unsigned int pointNum, gr::Point &pointReturn) + { + hb_position_t x, y; + hb_font_get_contour_point(m_font, m_face, pointNum, glyphID, &x, &y); + pointReturn.x = (float)x; + pointReturn.y = (float)y; + } + + virtual void getGlyphMetrics(gr::gid16 glyphID, gr::Rect &boundingBox, gr::Point &advances) + { + hb_glyph_metrics_t metrics; + hb_font_get_glyph_metrics(m_font, m_face, glyphID, &metrics); + boundingBox.top = (metrics.y_offset + metrics.height); + boundingBox.bottom = metrics.y_offset; + boundingBox.left = metrics.x_offset; + boundingBox.right = (metrics.x_offset + metrics.width); + advances.x = metrics.x_advance; + advances.y = metrics.y_advance; +// fprintf (stderr, "%d: (%d, %d, %d, %d)+(%d, %d)\n", glyphID, metrics.x_offset, metrics.y_offset, metrics.width, metrics.height, metrics.x_advance, metrics.y_advance); + } + +private: + void initfont(); + + hb_font_t *m_font; + hb_face_t *m_face; + float m_ascent; + float m_descent; + float m_emsquare; + bool m_bold; + bool m_italic; + std::map m_blobs; +}; + +void GrHbFont::initfont() +{ + const void *pOS2 = getTable(gr::kttiOs2, NULL); + const void *pHead = getTable(gr::kttiHead, NULL); + TtfUtil::FontOs2Style(pOS2, m_bold, m_italic); + m_ascent = static_cast(TtfUtil::FontAscent(pOS2)); + m_descent = static_cast(TtfUtil::FontDescent(pOS2)); + m_emsquare = static_cast(TtfUtil::DesignUnits(pHead)); +} + +void +hb_graphite_shape (hb_font_t *font, + hb_face_t *face, + hb_buffer_t *buffer, + hb_feature_t *features, + unsigned int num_features) +{ + /* create text source */ + GrBufferTextSrc textSrc(buffer, features, num_features); + + /* create grfont */ + GrHbFont grfont(font, face); + + /* create segment */ + int *firsts; + bool *flags; + int numChars; + int numGlyphs; + gr::LayoutEnvironment layout; + std::pairglyph_range; + gr::GlyphIterator iGlyph; + hb_codepoint_t *glyph_infos, *pGlyph; + hb_glyph_position_t *pPosition; + int cGlyph = 0; + int cChar = 0; + + layout.setStartOfLine(0); + layout.setEndOfLine(0); + layout.setDumbFallback(true); + layout.setJustifier(NULL); + layout.setRightToLeft(false); + + gr::RangeSegment pSegment(&grfont, &textSrc, &layout, (gr::toffset)0, + static_cast(buffer->len), (gr::Segment *)NULL); + + /* fill in buffer from segment */ + _hb_buffer_clear_output(buffer); + pSegment.getUniscribeClusters(NULL, 0, &numChars, NULL, 0, &numGlyphs); + firsts = new int[numChars]; + flags = new bool[numGlyphs]; + glyph_infos = new hb_codepoint_t[numGlyphs]; + hb_buffer_ensure(buffer, numGlyphs); + pSegment.getUniscribeClusters(firsts, numChars, NULL, flags, numGlyphs, NULL); + glyph_range = pSegment.glyphs(); + for (pGlyph = glyph_infos, iGlyph = glyph_range.first; iGlyph != glyph_range.second; + iGlyph++, pGlyph++) + { *pGlyph = iGlyph->glyphID(); } + + while (cGlyph < numGlyphs) + { + if (flags[cGlyph]) + { + int oldcChar = cChar++; + int oldcGlyph = cGlyph++; + while (cChar < numChars && firsts[cChar] == firsts[oldcChar]) cChar++; + while (cGlyph < numGlyphs && !flags[cGlyph]) cGlyph++; + _hb_buffer_add_output_glyphs(buffer, cChar - oldcChar, cGlyph - oldcGlyph, + glyph_infos + oldcGlyph, 0xFFFF, 0xFFFF); + } + else + { cGlyph++; } /* This should never happen */ + } + + float curradvx = 0., curradvy = 0.; + for (pPosition = hb_buffer_get_glyph_positions(buffer), iGlyph = glyph_range.first; + iGlyph != glyph_range.second; pPosition++, iGlyph++) + { + pPosition->x_offset = iGlyph->origin() - curradvx; + pPosition->y_offset = iGlyph->yOffset() - curradvy; + pPosition->x_advance = pPosition->x_offset + iGlyph->advanceWidth(); + pPosition->y_advance = pPosition->y_offset + iGlyph->advanceHeight(); +// if (pPosition->x_advance < 0) +// pPosition->x_advance = 0; + curradvx += pPosition->x_advance; + curradvy += pPosition->y_advance; +// fprintf(stderr, "%d@(%f, %f)+(%f, %f)\n", iGlyph->glyphID(), iGlyph->origin(), iGlyph->yOffset(), iGlyph->advanceWidth(), iGlyph->advanceHeight()); + } + + delete[] glyph_infos; + delete[] firsts; + delete[] flags; +} diff --git a/src/hb-graphite.h b/src/hb-graphite.h new file mode 100644 index 000000000..ac9ca272a --- /dev/null +++ b/src/hb-graphite.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009, Martin Hosken + * Copyright (C) 2009, SIL International + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_GRAPHITE_H +#define HB_GRAPHITE_H + +#include "hb-shape.h" + + +HB_BEGIN_DECLS + +#define HB_GRAPHITE_TAG_Silf HB_TAG('S','i','l','f') + +void hb_graphite_shape (hb_font_t *font, + hb_face_t *face, + hb_buffer_t *buffer, + hb_feature_t *features, + unsigned int num_features); + +HB_END_DECLS + +#endif /* HB_GRAPHITE_H */ diff --git a/src/hb-shape.cc b/src/hb-shape.cc index b77677cef..a53f3eac1 100644 --- a/src/hb-shape.cc +++ b/src/hb-shape.cc @@ -32,6 +32,9 @@ #include "hb-ot-shape-private.hh" +#ifdef HAVE_GRAPHITE +#include "hb-graphite.h" +#endif /* Prepare */ @@ -220,6 +223,18 @@ hb_shape (hb_font_t *font, hb_direction_t original_direction; hb_bool_t substitute_fallback, position_fallback; +#ifdef HAVE_GRAPHITE + hb_blob_t *silf_blob; + silf_blob = hb_face_get_table (face, HB_GRAPHITE_TAG_Silf); + if (hb_blob_get_length(silf_blob)) + { + hb_graphite_shape(font, face, buffer, features, num_features); + hb_blob_destroy(silf_blob); + return; + } + hb_blob_destroy(silf_blob); +#endif + hb_form_clusters (buffer); hb_substitute_default (font, face, buffer, features, num_features);