Import Graphite shaping backend by Martin Hosken
authorBehdad Esfahbod <behdad@behdad.org>
Fri, 21 May 2010 13:02:20 +0000 (14:02 +0100)
committerBehdad Esfahbod <behdad@behdad.org>
Fri, 21 May 2010 13:15:39 +0000 (14:15 +0100)
configure.ac
src/Makefile.am
src/hb-graphite.cc [new file with mode: 0644]
src/hb-graphite.h [new file with mode: 0644]
src/hb-shape.cc

index 0cc01b7..721c912 100644 (file)
@@ -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])
index 70cbfd8..0ed02e1 100644 (file)
@@ -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 (file)
index 0000000..ba1169a
--- /dev/null
@@ -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 <graphite/GrClient.h>
+#include <graphite/ITextSource.h>
+#include <graphite/GrData.h>
+#include <graphite/GrConstants.h>
+#include <graphite/Segment.h>
+#include "hb-buffer-private.hh"
+#include "hb-font-private.hh"
+#include "hb-graphite.h"
+#include <map>
+
+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<gr::toffset, gr::toffset> propertyRange(gr::toffset ich)
+  { return std::pair<gr::toffset, gr::toffset>(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<hb_tag_t,hb_blob_t *>::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<hb_tag_t, hb_blob_t *>(font.m_blobs);
+    std::map<hb_tag_t,hb_blob_t *>::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<hb_tag_t,hb_blob_t *>::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<hb_tag_t, hb_blob_t *> 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<float>(TtfUtil::FontAscent(pOS2));
+  m_descent = static_cast<float>(TtfUtil::FontDescent(pOS2));
+  m_emsquare = static_cast<float>(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::pair<gr::GlyphIterator, gr::GlyphIterator>glyph_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<gr::toffset>(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 (file)
index 0000000..ac9ca27
--- /dev/null
@@ -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 */
index b77677c..a53f3ea 100644 (file)
@@ -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);