Skip to content

Commit

Permalink
Re-implement shapeWithTrace in JS
Browse files Browse the repository at this point in the history
Also fixes #83
  • Loading branch information
khaledhosny committed Mar 24, 2024
1 parent 2486aff commit 6655c17
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 163 deletions.
150 changes: 0 additions & 150 deletions hbjs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,156 +15,6 @@ HB_END_DECLS

void *free_ptr(void) { return (void *) free; }

enum {
HB_SHAPE_DONT_STOP,
HB_SHAPE_GSUB_PHASE,
HB_SHAPE_GPOS_PHASE
};

struct user_data_t {
user_data_t(char *str_,
unsigned size_,
unsigned stop_at_ = 0,
unsigned stop_phase_ = 0)
: str(str_)
, size(size_)
, stop_at(stop_at_)
, stop_phase(stop_phase_)
{}
char *str = nullptr;
unsigned size = 0;
unsigned consumed = 0;
hb_bool_t failure = false;
unsigned stop_at = 0;
unsigned stop_phase = 0;
hb_bool_t stopping = false;
unsigned current_phase = 0;
};


static void
_user_data_printf (user_data_t *data, const char *format, ...)
{
#define BUFSIZE 1000
char buf[BUFSIZE];
int len;
va_list va;

if (!data || data->failure)
return;

va_start(va, format);
len = vsnprintf(buf, BUFSIZE, format, va);
va_end(va);

if (data->consumed + len >= data->size || len < 0 || len > BUFSIZE)
{
data->failure = true;
return;
}

memcpy (data->str + data->consumed, buf, len);
data->consumed += len;
#undef BUFSIZE
}

static hb_bool_t do_trace (hb_buffer_t *buffer,
hb_font_t *font,
const char *message,
user_data_t *user_data) {
unsigned int consumed;
unsigned int num_glyphs = hb_buffer_get_length (buffer);

if (strncmp(message, "start table GSUB", 16) == 0) {
user_data->current_phase = HB_SHAPE_GSUB_PHASE;
} else if (strncmp(message, "start table GPOS", 16) == 0) {
user_data->current_phase = HB_SHAPE_GPOS_PHASE;
}


if (user_data->current_phase != user_data->stop_phase) {
user_data->stopping = false;
}

// If we overflowed, keep going anyway.
if (user_data->failure) return 1;

if (user_data->stop_phase != HB_SHAPE_DONT_STOP) {
// Do we need to start stopping?
char buf[12];
snprintf (buf, 12, "%d ", user_data->stop_at);
if ((user_data->current_phase == user_data->stop_phase) &&
(strncmp(message, "end lookup ", 11) == 0) &&
(strncmp(message + 11, buf, strlen(buf)) == 0)) {
user_data->stopping = true;
}
}

// If we need to stop, stop.
if (user_data->stopping) return 0;

_user_data_printf (user_data, "{\"m\":\"%s\",\"t\":", message);
hb_buffer_serialize_glyphs(buffer, 0, num_glyphs,
user_data->str + user_data->consumed,
user_data->size - user_data->consumed,
&consumed,
font,
HB_BUFFER_SERIALIZE_FORMAT_JSON,
HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES);
user_data->consumed += consumed;
if (hb_buffer_get_content_type(buffer) == HB_BUFFER_CONTENT_TYPE_GLYPHS) {
_user_data_printf (user_data, ", \"glyphs\": true");
} else {
_user_data_printf (user_data, ", \"glyphs\": false");
}
_user_data_printf (user_data, "},\n");

return 1;
}

unsigned
hbjs_shape_with_trace (hb_font_t *font, hb_buffer_t* buf,
char* featurestring,
unsigned int stop_at, unsigned int stop_phase,
char *outbuf, unsigned buf_size) {
user_data_t user_data(outbuf, buf_size, stop_at, stop_phase);

int num_features = 0;
hb_feature_t* features = nullptr;

if (*featurestring) {
/* count the features first, so we can allocate memory */
char* p = featurestring;
do {
num_features++;
p = strchr (p, ',');
if (p)
p++;
} while (p);

features = (hb_feature_t *) calloc (num_features, sizeof (*features));

/* now do the actual parsing */
p = featurestring;
num_features = 0;
while (p && *p) {
char *end = strchr (p, ',');
if (hb_feature_from_string (p, end ? end - p : -1, &features[num_features]))
num_features++;
p = end ? end + 1 : nullptr;
}
}

hb_buffer_set_message_func (buf, (hb_buffer_message_func_t)do_trace, &user_data, nullptr);
user_data.str[user_data.consumed++] = '[';
hb_shape(font, buf, features, num_features);

if (user_data.failure) return -1;

user_data.str[user_data.consumed-2] = ']';
user_data.str[user_data.consumed-1] = '\0';
return user_data.consumed;
}

#ifdef MAIN
#include <stdio.h>
Expand Down
87 changes: 75 additions & 12 deletions hbjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ function hbjs(Module) {

var HB_MEMORY_MODE_WRITABLE = 2;
var HB_SET_VALUE_INVALID = -1;
var HB_BUFFER_CONTENT_TYPE_GLYPHS = 2;
var DONT_STOP = 0;
var GSUB_PHASE = 1;
var GPOS_PHASE = 2;

function hb_tag(s) {
return (
Expand All @@ -21,6 +25,9 @@ function hbjs(Module) {
);
}

var HB_BUFFER_SERIALIZE_FORMAT_JSON = hb_tag('JSON');
var HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES = 4;

function _hb_untag(tag) {
return [
String.fromCharCode((tag >> 24) & 0xFF),
Expand Down Expand Up @@ -433,10 +440,25 @@ function hbjs(Module) {
* @param {object} font: A font returned from `createFont`
* @param {object} buffer: A buffer returned from `createBuffer` and suitably
* prepared.
* @param {object} features: (Currently unused).
* @param {object} features: A string of comma-separated OpenType features to apply.
*/
function shape(font, buffer, features) {
exports.hb_shape(font.ptr, buffer.ptr, 0, 0);
var featuresPtr = 0;
var featuresLen = 0;
if (features) {
features = features.split(",");
featuresPtr = exports.malloc(16 * features.length);
features.forEach(function (feature, i) {
var str = createAsciiString(feature);
if (exports.hb_feature_from_string(str.ptr, -1, featuresPtr + featuresLen * 16))
featuresLen++;
str.free();
});
}

exports.hb_shape(font.ptr, buffer.ptr, featuresPtr, featuresLen);
if (featuresPtr)
exports.free(featuresPtr);
}

/**
Expand All @@ -450,22 +472,63 @@ function hbjs(Module) {
* @param {object} font: A font returned from `createFont`
* @param {object} buffer: A buffer returned from `createBuffer` and suitably
* prepared.
* @param {object} features: A dictionary of OpenType features to apply.
* @param {object} features: A string of comma-separated OpenType features to apply.
* @param {number} stop_at: A lookup ID at which to terminate shaping.
* @param {number} stop_phase: Either 0 (don't terminate shaping), 1 (`stop_at`
refers to a lookup ID in the GSUB table), 2 (`stop_at` refers to a lookup
ID in the GPOS table).
*/

function shapeWithTrace(font, buffer, features, stop_at, stop_phase) {
var bufLen = 1024 * 1024;
var traceBuffer = exports.malloc(bufLen);
var featurestr = createAsciiString(features);
var traceLen = exports.hbjs_shape_with_trace(font.ptr, buffer.ptr, featurestr.ptr, stop_at, stop_phase, traceBuffer, bufLen);
featurestr.free();
var trace = utf8Decoder.decode(heapu8.subarray(traceBuffer, traceBuffer + traceLen - 1));
exports.free(traceBuffer);
return JSON.parse(trace);
var trace = [];
var currentPhase = DONT_STOP;
var stopping = false;
var failure = false;

var traceBufLen = 1024 * 1024;
var traceBufPtr = exports.malloc(traceBufLen);

var traceFunc = function (bufferPtr, fontPtr, messagePtr, user_data) {
var message = utf8Decoder.decode(heapu8.subarray(messagePtr, heapu8.indexOf(0, messagePtr)));
if (message.startsWith("start table GSUB"))
currentPhase = GSUB_PHASE;
else if (message.startsWith("start table GPOS"))
currentPhase = GPOS_PHASE;

if (currentPhase != stop_phase)
stopping = false;

if (failure)
return 1;

if (stop_phase != DONT_STOP && currentPhase == stop_phase && message.startsWith("end lookup " + stop_at))
stopping = true;

if (stopping)
return 0;

exports.hb_buffer_serialize_glyphs(
bufferPtr,
0, exports.hb_buffer_get_length(bufferPtr),
traceBufPtr, traceBufLen, 0,
fontPtr,
HB_BUFFER_SERIALIZE_FORMAT_JSON,
HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES);

trace.push({
m: message,
t: JSON.parse(utf8Decoder.decode(heapu8.subarray(traceBufPtr, heapu8.indexOf(0, traceBufPtr)))),
glyphs: exports.hb_buffer_get_content_type(bufferPtr) == HB_BUFFER_CONTENT_TYPE_GLYPHS,
});

return 1;
}

var traceFuncPtr = addFunction(traceFunc, 'iiiii');
exports.hb_buffer_set_message_func(buffer.ptr, traceFuncPtr, 0, 0);
shape(font, buffer, features, 0);
exports.free(traceBufPtr);

return trace;
}

return {
Expand Down
5 changes: 4 additions & 1 deletion hbjs.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ _hb_buffer_destroy
_hb_buffer_get_glyph_infos
_hb_buffer_get_glyph_positions
_hb_buffer_get_length
_hb_buffer_get_content_type
_hb_buffer_guess_segment_properties
_hb_buffer_set_cluster_level
_hb_buffer_set_direction
_hb_buffer_set_flags
_hb_buffer_set_language
_hb_buffer_set_script
_hb_buffer_set_message_func
_hb_buffer_serialize_glyphs
_hb_face_create
_hb_face_collect_unicodes
_hb_face_destroy
Expand All @@ -36,12 +39,12 @@ _hb_glyph_info_get_glyph_flags
_hb_language_from_string
_hb_ot_var_get_axis_infos
_hb_script_from_string
_hb_feature_from_string
_hb_set_create
_hb_set_destroy
_hb_set_get_population
_hb_set_next_many
_hb_shape
_hbjs_shape_with_trace
_malloc
_free
_free_ptr

0 comments on commit 6655c17

Please sign in to comment.