/****************************************************************************\
 Part of the XeTeX typesetting system
 Copyright (c) 1994-2008 by SIL International
 Copyright (c) 2009-2012 by Jonathan Kew
 Copyright (c) 2012, 2013 by Khaled Hosny

 SIL Author(s): Jonathan Kew

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of the copyright holders
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization from the copyright holders.
\****************************************************************************/

#include <unicode/platform.h>	// We need this first
#include <unicode/ubidi.h>
#include <unicode/utext.h>

#include "XeTeXLayoutInterface.h"
#include "XeTeXFontInst.h"
#ifdef XETEX_MAC
#include "XeTeXFontInst_Mac.h"
#endif
#include "XeTeXFontMgr.h"

struct XeTeXLayoutEngine_rec
{
	XeTeXFontInst*	font;
	PlatformFontRef	fontRef;
	hb_tag_t		script;
	hb_tag_t		language;
	hb_feature_t*	features;
	char**			ShaperList;	// the requested shapers
	char*			shaper;		// the actually used shaper
	int				nFeatures;
	uint32_t		rgbValue;
	float			extend;
	float			slant;
	float			embolden;
	hb_buffer_t*	hbBuffer;
};

/*******************************************************************/
/* Glyph bounding box cache to speed up \XeTeXuseglyphmetrics mode */
/*******************************************************************/
#include <map>

// key is combined value representing (font_id << 16) + glyph
// value is glyph bounding box in TeX points
static std::map<uint32_t,GlyphBBox>	sGlyphBoxes;

int
getCachedGlyphBBox(uint16_t fontID, uint16_t glyphID, GlyphBBox* bbox)
{
	uint32_t	key = ((uint32_t)fontID << 16) + glyphID;
	std::map<uint32_t,GlyphBBox>::const_iterator i = sGlyphBoxes.find(key);
	if (i == sGlyphBoxes.end()) {
		return 0;
	}
	*bbox = i->second;
	return 1;
}

void
cacheGlyphBBox(uint16_t fontID, uint16_t glyphID, const GlyphBBox* bbox)
{
	uint32_t	key = ((uint32_t)fontID << 16) + glyphID;
	sGlyphBoxes[key] = *bbox;
}
/*******************************************************************/

void
terminatefontmanager()
{
	XeTeXFontMgr::Terminate();
}

XeTeXFont
createFont(PlatformFontRef fontRef, Fixed pointSize)
{
	int status = 0;
#ifdef XETEX_MAC
	XeTeXFontInst* font = new XeTeXFontInst_Mac(fontRef, Fix2D(pointSize), status);
#else
	FcChar8*	pathname = 0;
	FcPatternGetString(fontRef, FC_FILE, 0, &pathname);
	int			index;
	FcPatternGetInteger(fontRef, FC_INDEX, 0, &index);
	XeTeXFontInst* font = new XeTeXFontInst((const char*)pathname, index, Fix2D(pointSize), status);
#endif
	if (status != 0) {
		delete font;
		return NULL;
	}
	return (XeTeXFont)font;
}

XeTeXFont
createFontFromFile(const char* filename, int index, Fixed pointSize)
{
	int status = 0;
	XeTeXFontInst* font = new XeTeXFontInst(filename, index, Fix2D(pointSize), status);
	if (status != 0) {
		delete font;
		return NULL;
	}
	return (XeTeXFont)font;
}

void
setFontLayoutDir(XeTeXFont font, int vertical)
{
	((XeTeXFontInst*)font)->setLayoutDirVertical(vertical != 0);
}

PlatformFontRef
findFontByName(const char* name, char* var, double size)
{
	return (XeTeXFontMgr::GetFontManager()->findFont(name, var, size));
}

char
getReqEngine()
{
	return XeTeXFontMgr::GetFontManager()->getReqEngine();
}

void
setReqEngine(char reqEngine)
{
	XeTeXFontMgr::GetFontManager()->setReqEngine(reqEngine);
}

const char*
getFullName(PlatformFontRef fontRef)
{
	return XeTeXFontMgr::GetFontManager()->getFullName(fontRef);
}

double
getDesignSize(XeTeXFont font)
{
	return XeTeXFontMgr::GetFontManager()->getDesignSize(font);
}

const char*
getFontFilename(XeTeXLayoutEngine engine)
{
	return engine->font->getFilename();
}

void
getNames(PlatformFontRef fontRef, const char** psName, const char** famName, const char** styName)
{
	XeTeXFontMgr::GetFontManager()->getNames(fontRef, psName, famName, styName);
}

PlatformFontRef
getFontRef(XeTeXLayoutEngine engine)
{
	return engine->fontRef;
}

void
deleteFont(XeTeXFont font)
{
	delete (XeTeXFontInst*)font;
}

void*
getFontTablePtr(XeTeXFont font, uint32_t tableTag)
{
	return const_cast<void*>(((XeTeXFontInst*)font)->getFontTable(tableTag));
}

Fixed
getSlant(XeTeXFont font)
{
	float italAngle = ((XeTeXFontInst*)font)->getItalicAngle();
	return D2Fix(tan(-italAngle * M_PI / 180.0));
}

static unsigned int
getLargerScriptListTable(XeTeXFont font, hb_tag_t** scriptList)
{
	unsigned int rval = 0;

	hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont());

	hb_tag_t* scriptListSub = NULL;
	hb_tag_t* scriptListPos = NULL;

	unsigned int scriptCountSub = hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, NULL, NULL);
	scriptListSub = (hb_tag_t*) xmalloc(scriptCountSub * sizeof(hb_tag_t*));
	hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, &scriptCountSub, scriptListSub);

	unsigned int scriptCountPos = hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GPOS, 0, NULL, NULL);
	scriptListPos = (hb_tag_t*) xmalloc(scriptCountPos * sizeof(hb_tag_t*));
	hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, &scriptCountPos, scriptListPos);

	if (scriptCountSub > scriptCountPos) {
		if (scriptList != NULL)
			*scriptList = scriptListSub;
		rval = scriptCountSub;
	} else {
		if (scriptList != NULL)
			*scriptList = scriptListPos;
		rval = scriptCountPos;
	}

	return rval;
}

unsigned int
countScripts(XeTeXFont font)
{
	return getLargerScriptListTable(font, NULL);
}

hb_tag_t
getIndScript(XeTeXFont font, unsigned int index)
{
	hb_tag_t rval = 0;

	hb_tag_t* scriptList;

	unsigned int scriptCount = getLargerScriptListTable(font, &scriptList);
	if (scriptList != NULL) {
		if (index < scriptCount)
			rval = scriptList[index];
	}

	return rval;
}

unsigned int
countLanguages(XeTeXFont font, hb_tag_t script)
{
	unsigned int rval = 0;

	hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont());
	hb_tag_t* scriptList;

	unsigned int scriptCount = getLargerScriptListTable(font, &scriptList);
	if (scriptList != NULL) {
		for (int i = 0; i < scriptCount; i++) {
			if (scriptList[i] == script) {
				rval += hb_ot_layout_script_get_language_tags (face, HB_OT_TAG_GSUB, i, 0, NULL, NULL);
				rval += hb_ot_layout_script_get_language_tags (face, HB_OT_TAG_GPOS, i, 0, NULL, NULL);
				break;
			}
		}
	}

	return rval;
}

hb_tag_t
getIndLanguage(XeTeXFont font, hb_tag_t script, unsigned int index)
{
	hb_tag_t rval = 0;

	hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont());
	hb_tag_t* scriptList;

	unsigned int scriptCount = getLargerScriptListTable(font, &scriptList);
	if (scriptList != NULL) {
		for (int i = 0; i < scriptCount; i++) {
			if (scriptList[i] == script) {
				unsigned int langCount;
				hb_tag_t* langList;

				langCount = hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, i, 0, NULL, NULL);
				langList = (hb_tag_t*) xcalloc(langCount, sizeof(hb_tag_t*));
				hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, i, 0, &langCount, langList);

				if (index < langCount) {
					rval = langList[index];
					break;
				}

				free(langList);

				langCount = hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GPOS, i, 0, NULL, NULL);
				langList = (hb_tag_t*) xcalloc(langCount, sizeof(hb_tag_t*));
				hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GPOS, i, 0, &langCount, langList);

				if (index < langCount) {
					rval = langList[index];
					break;
				}

				free(langList);
			}
		}
	}

	return rval;
}

unsigned int
countFeatures(XeTeXFont font, hb_tag_t script, hb_tag_t language)
{
	unsigned int rval = 0;

	hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont());

	for (int i = 0; i < 2; ++i) {
		unsigned int scriptIndex, langIndex = 0;
		hb_tag_t tableTag = i == 0 ? HB_OT_TAG_GSUB : HB_OT_TAG_GPOS;
		if (hb_ot_layout_table_find_script(face, tableTag, script, &scriptIndex)) {
			if (hb_ot_layout_script_find_language(face, tableTag, scriptIndex, language, &langIndex) || language == 0) {
				rval += hb_ot_layout_language_get_feature_tags(face, tableTag, scriptIndex, langIndex, 0, NULL, NULL);
			}
		}
	}

	return rval;
}

hb_tag_t
getIndFeature(XeTeXFont font, hb_tag_t script, hb_tag_t language, unsigned int index)
{
	hb_tag_t rval = 0;

	hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont());

	for (int i = 0; i < 2; ++i) {
		unsigned int scriptIndex, langIndex = 0;
		hb_tag_t tableTag = i == 0 ? HB_OT_TAG_GSUB : HB_OT_TAG_GPOS;
		if (hb_ot_layout_table_find_script(face, tableTag, script, &scriptIndex)) {
			if (hb_ot_layout_script_find_language(face, tableTag, scriptIndex, language, &langIndex) || language == 0) {
				unsigned int featCount = hb_ot_layout_language_get_feature_tags(face, tableTag, scriptIndex, langIndex, 0, NULL, NULL);
				hb_tag_t* featList = (hb_tag_t*) xmalloc(featCount * sizeof(hb_tag_t*));
				hb_ot_layout_language_get_feature_tags(face, tableTag, scriptIndex, langIndex, 0, &featCount, featList);

				if (index < featCount) {
					rval = featList[index];
					break;
				}

				index -= featCount;
			}
		}
	}

	return rval;
}

uint32_t
countGraphiteFeatures(XeTeXLayoutEngine engine)
{
	uint32_t rval = 0;

	hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont());
	gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace);

	if (grFace != NULL)
		rval = gr_face_n_fref(grFace);

	return rval;
}

uint32_t
getGraphiteFeatureCode(XeTeXLayoutEngine engine, uint32_t index)
{
	uint32_t rval = 0;

	hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont());
	gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace);

	if (grFace != NULL) {
		const gr_feature_ref* feature = gr_face_fref(grFace, index);
		rval = gr_fref_id(feature);
	}

	return rval;
}

uint32_t
countGraphiteFeatureSettings(XeTeXLayoutEngine engine, uint32_t featureID)
{
	uint32_t rval = 0;

	hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont());
	gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace);

	if (grFace != NULL) {
		const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID);
		rval = gr_fref_n_values(feature);
	}

	return rval;
}

uint32_t
getGraphiteFeatureSettingCode(XeTeXLayoutEngine engine, uint32_t featureID, uint32_t index)
{
	uint32_t rval = 0;

	hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont());
	gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace);

	if (grFace != NULL) {
		const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID);
		rval = gr_fref_value(feature, index);
	}

	return rval;
}

uint32_t
getGraphiteFeatureDefaultSetting(XeTeXLayoutEngine engine, uint32_t featureID)
{
	uint32_t rval = 0;

	hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont());
	gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace);

	if (grFace != NULL) {
		const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID);
		gr_feature_val *featureValues = gr_face_featureval_for_lang (grFace, engine->language);

		rval = gr_fref_feature_value(feature, featureValues);
	}

	return rval;
}

char *
getGraphiteFeatureLabel(XeTeXLayoutEngine engine, uint32_t featureID)
{
	hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont());
	gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace);

	if (grFace != NULL) {
		const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID);
		uint32_t len = 0;
		uint16_t langID = 0x409;

		return (char *) gr_fref_label(feature, &langID, gr_utf8, &len);
	}

	return NULL;
}

char *
getGraphiteFeatureSettingLabel(XeTeXLayoutEngine engine, uint32_t featureID, uint32_t settingID)
{
	hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont());
	gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace);

	if (grFace != NULL) {
		const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID);
		for (int i = 0; i < gr_fref_n_values(feature); i++) {
			if (settingID == gr_fref_value(feature, i)) {
				uint32_t len = 0;
				uint16_t langID = 0x409;

				return (char *) gr_fref_value_label(feature, i, &langID, gr_utf8, &len);
			}
		}
	}

	return NULL;
}

bool
findGraphiteFeature(XeTeXLayoutEngine engine, const char* s, const char* e, hb_tag_t* f, int* v)
	/* s...e is a "feature=setting" string; look for this in the font */
{
	*f = 0;
	*v = 0;
	while (*s == ' ' || *s == '\t')
		++s;
	const char* cp = s;
	while (cp < e && *cp != '=')
		++cp;

	*f = findGraphiteFeatureNamed(engine, s, cp - s);
	if (*f == -1)
		return false;

	++cp;
	while (cp < e && (*cp == ' ' || *cp == '\t'))
		++cp;

	if (cp == e)
		/* no setting was specified */
		return false;

	*v = findGraphiteFeatureSettingNamed(engine, *f, cp, e - cp);
	if (*v == -1)
		return false;

	return true;
}

long
findGraphiteFeatureNamed(XeTeXLayoutEngine engine, const char* name, int namelength)
{
	long		rval = -1;

	hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont());
	gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace);

	if (grFace != NULL) {
		for (int i = 0; i < gr_face_n_fref(grFace); i++) {
			const gr_feature_ref* feature = gr_face_fref(grFace, i);
			uint32_t len = 0;
			uint16_t langID = 0x409;

			// the first call is to get the length of the string
			gr_fref_label(feature, &langID, gr_utf8, &len);
			char* label = (char*) xmalloc(len);
			label = (char*) gr_fref_label(feature, &langID, gr_utf8, &len);

			if (strncmp(label, name, namelength) == 0) {
				rval = gr_fref_id(feature);
				gr_label_destroy(label);
				break;
			}

			gr_label_destroy(label);
		}
	}

	return rval;
}

long
findGraphiteFeatureSettingNamed(XeTeXLayoutEngine engine, uint32_t id, const char* name, int namelength)
{
	long		rval = -1;

	hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont());
	gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace);

	if (grFace != NULL) {
		const gr_feature_ref* feature = gr_face_find_fref(grFace, id);
		for (int i = 0; i < gr_fref_n_values(feature); i++) {
			uint32_t len = 0;
			uint16_t langID = 0x409;

			// the first call is to get the length of the string
			gr_fref_value_label(feature, i, &langID, gr_utf8, &len);
			char* label = (char*) xmalloc(len);
			label = (char*) gr_fref_value_label(feature, i, &langID, gr_utf8, &len);

			if (strncmp(label, name, namelength) == 0) {
				rval = gr_fref_value(feature, i);
				gr_label_destroy(label);
				break;
			}

			gr_label_destroy(label);
		}
	}

	return rval;
}

float
getGlyphWidth(XeTeXFont font, uint32_t gid)
{
	return ((XeTeXFontInst*)font)->getGlyphWidth(gid);
}

unsigned int
countGlyphs(XeTeXFont font)
{
	return ((XeTeXFontInst*)font)->getNumGlyphs();
}

XeTeXFont
getFont(XeTeXLayoutEngine engine)
{
	return (XeTeXFont)(engine->font);
}

float
getExtendFactor(XeTeXLayoutEngine engine)
{
	return engine->extend;
}

float
getSlantFactor(XeTeXLayoutEngine engine)
{
	return engine->slant;
}

float
getEmboldenFactor(XeTeXLayoutEngine engine)
{
	return engine->embolden;
}

XeTeXLayoutEngine
createLayoutEngine(PlatformFontRef fontRef, XeTeXFont font, hb_tag_t script, hb_tag_t language,
					hb_feature_t* features, int nFeatures, char **shapers, uint32_t rgbValue,
					float extend, float slant, float embolden)
{
	XeTeXLayoutEngine result = new XeTeXLayoutEngine_rec;
	result->fontRef = fontRef;
	result->font = (XeTeXFontInst*)font;
	result->script = script;
	result->language = language;
	result->features = features;
	result->ShaperList = shapers;
	result->shaper = NULL;
	result->nFeatures = nFeatures;
	result->rgbValue = rgbValue;
	result->extend = extend;
	result->slant = slant;
	result->embolden = embolden;
	result->hbBuffer = hb_buffer_create();

	return result;
}

void
deleteLayoutEngine(XeTeXLayoutEngine engine)
{
	hb_buffer_destroy(engine->hbBuffer);
	delete engine->font;
}

int
layoutChars(XeTeXLayoutEngine engine, uint16_t chars[], int32_t offset, int32_t count, int32_t max,
						bool rightToLeft)
{
	bool res;
	hb_script_t script = HB_SCRIPT_INVALID;
	hb_language_t language = HB_LANGUAGE_INVALID;
	hb_direction_t direction = HB_DIRECTION_LTR;
	hb_segment_properties_t segment_props;
	hb_shape_plan_t *shape_plan;
	hb_font_t* hbFont = engine->font->getHbFont();
	hb_face_t* hbFace = hb_font_get_face(hbFont);

	if (engine->font->getLayoutDirVertical())
		direction = HB_DIRECTION_TTB;
	else if (rightToLeft)
		direction = HB_DIRECTION_RTL;

	script = hb_ot_tag_to_script (engine->script);
	language = hb_ot_tag_to_language (engine->language);

	hb_buffer_reset(engine->hbBuffer);
	hb_buffer_add_utf16(engine->hbBuffer, chars, max, offset, count);
	hb_buffer_set_direction(engine->hbBuffer, direction);
	hb_buffer_set_script(engine->hbBuffer, script);
	hb_buffer_set_language(engine->hbBuffer, language);

	hb_buffer_guess_segment_properties(engine->hbBuffer);
	hb_buffer_get_segment_properties(engine->hbBuffer, &segment_props);

	shape_plan = hb_shape_plan_create_cached(hbFace, &segment_props, engine->features, engine->nFeatures, engine->ShaperList);
	res = hb_shape_plan_execute(shape_plan, hbFont, engine->hbBuffer, engine->features, engine->nFeatures);

	if (res) {
		engine->shaper = strdup(hb_shape_plan_get_shaper(shape_plan));
		hb_buffer_set_content_type(engine->hbBuffer, HB_BUFFER_CONTENT_TYPE_GLYPHS);
	} else {
		// all selected shapers failed, retrying with default
		// we don't use _cached here as the cached plain will always fail.
		hb_shape_plan_destroy(shape_plan);
		shape_plan = hb_shape_plan_create(hbFace, &segment_props, engine->features, engine->nFeatures, NULL);
		res = hb_shape_plan_execute(shape_plan, hbFont, engine->hbBuffer, engine->features, engine->nFeatures);

		if (res) {
			engine->shaper = strdup(hb_shape_plan_get_shaper(shape_plan));
			hb_buffer_set_content_type(engine->hbBuffer, HB_BUFFER_CONTENT_TYPE_GLYPHS);
		} else {
			fprintf(stderr, "\nERROR: all shapers failed\n");
			exit(3);
		}
	}

	hb_shape_plan_destroy(shape_plan);

	int glyphCount = hb_buffer_get_length(engine->hbBuffer);

#ifdef DEBUG
	char buf[1024];
	unsigned int consumed;

	printf ("shaper: %s\n", engine->shaper);

	hb_buffer_serialize_flags_t flags = HB_BUFFER_SERIALIZE_FLAGS_DEFAULT;
	hb_buffer_serialize_format_t format = HB_BUFFER_SERIALIZE_FORMAT_JSON;

	hb_buffer_serialize_glyphs (engine->hbBuffer, 0, glyphCount, buf, sizeof(buf), &consumed, hbFont, format, flags);
	if (consumed)
		printf ("buffer glyphs: %s\n", buf);
#endif

	return glyphCount;
}

void
getGlyphs(XeTeXLayoutEngine engine, uint32_t glyphs[])
{
	int glyphCount = hb_buffer_get_length(engine->hbBuffer);
	hb_glyph_info_t *hbGlyphs = hb_buffer_get_glyph_infos(engine->hbBuffer, NULL);

	for (int i = 0; i < glyphCount; i++)
		glyphs[i] = hbGlyphs[i].codepoint;
}

void
getGlyphAdvances(XeTeXLayoutEngine engine, float advances[])
{
	int glyphCount = hb_buffer_get_length(engine->hbBuffer);
	hb_glyph_position_t *hbPositions = hb_buffer_get_glyph_positions(engine->hbBuffer, NULL);

	for (int i = 0; i < glyphCount; i++) {
		if (engine->font->getLayoutDirVertical())
			advances[i] = engine->font->unitsToPoints(hbPositions[i].y_advance);
		else
			advances[i] = engine->font->unitsToPoints(hbPositions[i].x_advance);
	}
}

void
getGlyphPositions(XeTeXLayoutEngine engine, float positions[])
{
	int glyphCount = hb_buffer_get_length(engine->hbBuffer);
	hb_glyph_position_t *hbPositions = hb_buffer_get_glyph_positions(engine->hbBuffer, NULL);

	int i = 0;
   	float x = 0, y = 0;

	if (engine->font->getLayoutDirVertical()) {
		for (i = 0; i < glyphCount; i++) {
			positions[2*i]   = - engine->font->unitsToPoints(x + hbPositions[i].y_offset); /* negative is forwards */
			positions[2*i+1] =   engine->font->unitsToPoints(y - hbPositions[i].x_offset);
			x += hbPositions[i].y_advance;
			y += hbPositions[i].x_advance;
		}
		positions[2*i]   = - engine->font->unitsToPoints(x);
		positions[2*i+1] =   engine->font->unitsToPoints(y);
	} else {
		for (i = 0; i < glyphCount; i++) {
			positions[2*i]   =   engine->font->unitsToPoints(x + hbPositions[i].x_offset);
			positions[2*i+1] = - engine->font->unitsToPoints(y + hbPositions[i].y_offset); /* negative is upwards */
			x += hbPositions[i].x_advance;
			y += hbPositions[i].y_advance;
		}
		positions[2*i]   =   engine->font->unitsToPoints(x);
		positions[2*i+1] = - engine->font->unitsToPoints(y);
	}

	if (engine->extend != 1.0 || engine->slant != 0.0)
		for (int i = 0; i <= glyphCount; ++i)
			positions[2*i] = positions[2*i] * engine->extend - positions[2*i+1] * engine->slant;
}

float
getPointSize(XeTeXLayoutEngine engine)
{
	return engine->font->getPointSize();
}

void
getAscentAndDescent(XeTeXLayoutEngine engine, float* ascent, float* descent)
{
	*ascent = engine->font->getAscent();
	*descent = engine->font->getDescent();
}

void
getCapAndXHeight(XeTeXLayoutEngine engine, float* capheight, float* xheight)
{
	*capheight = engine->font->getCapHeight();
	*xheight = engine->font->getXHeight();
}

int
getDefaultDirection(XeTeXLayoutEngine engine)
{
	hb_script_t script = hb_buffer_get_script(engine->hbBuffer);
	if (hb_script_get_horizontal_direction (script) == HB_DIRECTION_RTL)
		return UBIDI_DEFAULT_RTL;
	else
		return UBIDI_DEFAULT_LTR;
}

uint32_t
getRgbValue(XeTeXLayoutEngine engine)
{
	return engine->rgbValue;
}

void
getGlyphBounds(XeTeXLayoutEngine engine, uint32_t glyphID, GlyphBBox* bbox)
{
	engine->font->getGlyphBounds(glyphID, bbox);
	if (engine->extend != 0.0) {
	    bbox->xMin *= engine->extend;
	    bbox->xMax *= engine->extend;
    }
}

float
getGlyphWidthFromEngine(XeTeXLayoutEngine engine, uint32_t glyphID)
{
	return engine->extend * engine->font->getGlyphWidth(glyphID);
}

void
getGlyphHeightDepth(XeTeXLayoutEngine engine, uint32_t glyphID, float* height, float* depth)
{
	engine->font->getGlyphHeightDepth(glyphID, height, depth);
}

void
getGlyphSidebearings(XeTeXLayoutEngine engine, uint32_t glyphID, float* lsb, float* rsb)
{
	engine->font->getGlyphSidebearings(glyphID, lsb, rsb);
	if (engine->extend != 0.0) {
	    *lsb *= engine->extend;
	    *rsb *= engine->extend;
	}
}

float
getGlyphItalCorr(XeTeXLayoutEngine engine, uint32_t glyphID)
{
	return engine->extend * engine->font->getGlyphItalCorr(glyphID);
}

uint32_t
mapCharToGlyph(XeTeXLayoutEngine engine, uint32_t charCode)
{
	return engine->font->mapCharToGlyph(charCode);
}

int
getFontCharRange(XeTeXLayoutEngine engine, int reqFirst)
{
	if (reqFirst)
		return engine->font->getFirstCharCode();
	else
		return engine->font->getLastCharCode();
}

const char*
getGlyphName(XeTeXFont font, uint16_t gid, int* len)
{
	return ((XeTeXFontInst*)font)->getGlyphName(gid, *len);
}

int
mapGlyphToIndex(XeTeXLayoutEngine engine, const char* glyphName)
{
	return engine->font->mapGlyphToIndex(glyphName);
}

static gr_segment* grSegment = NULL;
static const gr_slot* grPrevSlot = NULL;
static int grTextLen;

bool
initGraphiteBreaking(XeTeXLayoutEngine engine, const uint16_t* txtPtr, int txtLen)
{
	hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont());
	gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace);
	gr_font* grFont = hb_graphite2_font_get_gr_font(engine->font->getHbFont());
	if (grFace != NULL && grFont != NULL) {
		if (grSegment != NULL) {
			gr_seg_destroy(grSegment);
			grSegment = NULL;
			grPrevSlot = NULL;
		}

		gr_feature_val *grFeatureValues = gr_face_featureval_for_lang (grFace, engine->language);

		int nFeatures = engine->nFeatures;
		hb_feature_t *features =  engine->features;
		while (nFeatures--) {
			const gr_feature_ref *fref = gr_face_find_fref (grFace, features->tag);
			if (fref)
				gr_fref_set_feature_value (fref, features->value, grFeatureValues);
			features++;
		}

		grSegment = gr_make_seg(grFont, grFace, engine->script, grFeatureValues, gr_utf16, txtPtr, txtLen, 0);
		grPrevSlot = gr_seg_first_slot(grSegment);
		grTextLen = txtLen;

		return true;
	}

	return false;
}

int
findNextGraphiteBreak(void)
{
	int ret = -1;

	if (grSegment != NULL) {
		if (grPrevSlot && grPrevSlot != gr_seg_last_slot(grSegment)) {
			for (const gr_slot* s = gr_slot_next_in_segment(grPrevSlot); s != NULL; s = gr_slot_next_in_segment(s)) {
				const gr_char_info* ci = NULL;
				int bw;

				ci = gr_seg_cinfo(grSegment, gr_slot_index(s));
				bw = gr_cinfo_break_weight(ci);
				if (bw < gr_breakNone && bw >= gr_breakBeforeWord) {
					grPrevSlot = s;
					ret = gr_cinfo_base(ci);
				} else if (bw > gr_breakNone && bw <= gr_breakWord) {
					grPrevSlot = gr_slot_next_in_segment(s);
					ret = gr_cinfo_base(ci) + 1;
				}

				if (ret != -1)
					break;
			}

			if (ret == -1) {
				grPrevSlot = gr_seg_last_slot(grSegment);
				ret = grTextLen;
			}
		}
	}

	return ret;
}

bool
usingGraphite(XeTeXLayoutEngine engine)
{
	if (engine->shaper != NULL && (strcmp("graphite2", engine->shaper) == 0))
		return true;
	else
		return false;
}

bool
usingOpenType(XeTeXLayoutEngine engine)
{
	if (engine->shaper == NULL || (strcmp("ot", engine->shaper) == 0))
		return true;
	else
		return false;
}

bool
isOpenTypeMathFont(XeTeXLayoutEngine engine)
{
	if (engine->font->getMathTable() != NULL)
		return true;
	else
		return false;
}
