summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/luatexdir/font/luafont.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/font/luafont.c')
-rw-r--r--Build/source/texk/web2c/luatexdir/font/luafont.c1733
1 files changed, 1733 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/font/luafont.c b/Build/source/texk/web2c/luatexdir/font/luafont.c
new file mode 100644
index 00000000000..be32e39de9f
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/font/luafont.c
@@ -0,0 +1,1733 @@
+
+#include "luatex-api.h"
+#include <ptexlib.h>
+
+#include "nodes.h"
+
+#define noVERBOSE
+
+#define SAVE_REF 1
+
+char *font_type_strings[] = {"unknown","virtual","real", NULL};
+char *font_format_strings[] = {"unknown","type1","type3","truetype", "opentype", NULL};
+char *font_embedding_strings[] = {"unknown","no","subset", "full", NULL};
+char *ligature_type_strings[] = {"=:", "=:|", "|=:", "|=:|", "", "=:|>", "|=:>", "|=:|>", "", "", "", "|=:|>>", NULL };
+
+void
+font_char_to_lua (lua_State *L, internalfontnumber f, charinfo *co) {
+ int i;
+ liginfo *l;
+ kerninfo *ki;
+
+ lua_createtable(L,0,10);
+
+ lua_pushstring(L,"width");
+ lua_pushnumber(L,get_charinfo_width(co));
+ lua_rawset(L,-3);
+
+ lua_pushstring(L,"height");
+ lua_pushnumber(L,get_charinfo_height(co));
+ lua_rawset(L,-3);
+
+ lua_pushstring(L,"depth");
+ lua_pushnumber(L,get_charinfo_depth(co));
+ lua_rawset(L,-3);
+
+ lua_pushstring(L,"italic");
+ lua_pushnumber(L,get_charinfo_italic(co));
+ lua_rawset(L,-3);
+
+ if (get_charinfo_ef(co)!=0) {
+ lua_pushstring(L,"expansion_factor");
+ lua_pushnumber(L,get_charinfo_ef(co));
+ lua_rawset(L,-3);
+ }
+
+ if (get_charinfo_lp(co)!=0) {
+ lua_pushstring(L,"left_protruding");
+ lua_pushnumber(L,get_charinfo_lp(co));
+ lua_rawset(L,-3);
+ }
+
+ if (get_charinfo_lp(co)!=0) {
+ lua_pushstring(L,"right_protruding");
+ lua_pushnumber(L,get_charinfo_rp(co));
+ lua_rawset(L,-3);
+ }
+
+
+ if (font_encodingbytes(f) == 2 ) {
+ lua_pushstring(L,"index");
+ lua_pushnumber(L,get_charinfo_index(co));
+ lua_rawset(L,-3);
+ }
+
+ if (get_charinfo_name(co)!=NULL) {
+ lua_pushstring(L,"name");
+ lua_pushstring(L,get_charinfo_name(co));
+ lua_rawset(L,-3);
+ }
+
+ if (get_charinfo_tounicode(co)!=NULL) {
+ lua_pushstring(L,"tounicode");
+ lua_pushstring(L,get_charinfo_tounicode(co));
+ lua_rawset(L,-3);
+ }
+
+ if (get_charinfo_tag(co) == list_tag) {
+ lua_pushstring(L,"next");
+ lua_pushnumber(L,get_charinfo_remainder(co));
+ lua_rawset(L,-3);
+ }
+
+ lua_pushstring(L,"used");
+ lua_pushboolean(L,(get_charinfo_used(co) ? true : false));
+ lua_rawset(L,-3);
+
+ if (get_charinfo_tag(co) == ext_tag) {
+ lua_pushstring(L,"extensible");
+ lua_createtable(L,0,4);
+ lua_pushnumber(L,get_charinfo_extensible(co,EXT_TOP));
+ lua_setfield(L,-2,"top");
+ lua_pushnumber(L,get_charinfo_extensible(co,EXT_BOT));
+ lua_setfield(L,-2,"bot");
+ lua_pushnumber(L,get_charinfo_extensible(co,EXT_MID));
+ lua_setfield(L,-2,"mid");
+ lua_pushnumber(L,get_charinfo_extensible(co,EXT_REP));
+ lua_setfield(L,-2,"rep");
+ lua_rawset(L,-3);
+ }
+ ki = get_charinfo_kerns(co);
+ if (ki != NULL) {
+ lua_pushstring(L,"kerns");
+ lua_createtable(L,10,1);
+ for (i=0;!kern_end(ki[i]);i++) {
+ if (kern_char(ki[i]) == right_boundarychar) {
+ lua_pushstring(L,"right_boundary");
+ } else {
+ lua_pushnumber(L,kern_char(ki[i]));
+ }
+ lua_pushnumber(L,kern_kern(ki[i]));
+ lua_rawset(L,-3);
+ }
+ lua_rawset(L,-3);
+ }
+ l = get_charinfo_ligatures(co);
+ if (l!=NULL) {
+ lua_pushstring(L,"ligatures");
+ lua_createtable(L,10,1);
+ for (i=0;!lig_end(l[i]);i++) {
+ if (lig_char(l[i]) == right_boundarychar) {
+ lua_pushstring(L,"right_boundary");
+ } else {
+ lua_pushnumber(L,lig_char(l[i]));
+ }
+ lua_createtable(L,0,2);
+ lua_pushstring(L,"type");
+ lua_pushnumber(L,lig_type(l[i]));
+ lua_rawset(L,-3);
+ lua_pushstring(L,"char");
+ lua_pushnumber(L,lig_replacement(l[i]));
+ lua_rawset(L,-3);
+ lua_rawset(L,-3);
+ }
+ lua_rawset(L,-3);
+ }
+}
+
+static void
+write_lua_parameters (lua_State *L, int f) {
+ int k;
+ lua_newtable(L);
+ for (k=1;k<=font_params(f);k++) {
+ lua_pushnumber(L,font_param(f,k));
+ switch (k) {
+ case slant_code: lua_setfield(L,-2,"slant"); break;
+ case space_code: lua_setfield(L,-2,"space"); break;
+ case space_stretch_code: lua_setfield(L,-2,"space_stretch"); break;
+ case space_shrink_code: lua_setfield(L,-2,"space_shrink"); break;
+ case x_height_code: lua_setfield(L,-2,"x_height"); break;
+ case quad_code: lua_setfield(L,-2,"quad"); break;
+ case extra_space_code: lua_setfield(L,-2,"extra_space"); break;
+ default:
+ lua_rawseti(L,-2,k);
+ }
+ }
+ lua_setfield(L,-2,"parameters");
+}
+
+
+int
+font_to_lua (lua_State *L, int f) {
+ int k;
+ charinfo *co;
+ if (font_cache_id(f)) {
+ /* fetch the table from the registry if it was
+ saved there by font_from_lua() */
+ lua_rawgeti(L,LUA_REGISTRYINDEX,font_cache_id(f));
+ /* fontdimens can be changed from tex code */
+ write_lua_parameters(L,f);
+ return 1;
+ }
+
+ lua_newtable(L);
+ lua_pushstring(L,font_name(f));
+ lua_setfield(L,-2,"name");
+ if(font_area(f)!=NULL) {
+ lua_pushstring(L,font_area(f));
+ lua_setfield(L,-2,"area");
+ }
+ if(font_filename(f)!=NULL) {
+ lua_pushstring(L,font_filename(f));
+ lua_setfield(L,-2,"filename");
+ }
+ if(font_fullname(f)!=NULL) {
+ lua_pushstring(L,font_fullname(f));
+ lua_setfield(L,-2,"fullname");
+ }
+ if(font_encodingname(f)!=NULL) {
+ lua_pushstring(L,font_encodingname(f));
+ lua_setfield(L,-2,"encodingname");
+ }
+
+ lua_pushboolean(L,(font_used(f) ? true : false));
+ lua_setfield(L,-2,"used");
+
+
+ lua_pushstring(L,font_type_strings[font_type(f)]);
+ lua_setfield(L,-2,"type");
+ lua_pushstring(L,font_format_strings[font_format(f)]);
+ lua_setfield(L,-2,"format");
+ lua_pushstring(L,font_embedding_strings[font_embedding(f)]);
+ lua_setfield(L,-2,"embedding");
+
+ lua_pushnumber(L,font_size(f));
+ lua_setfield(L,-2,"size");
+ lua_pushnumber(L,font_dsize(f));
+ lua_setfield(L,-2,"designsize");
+ lua_pushnumber(L,font_checksum(f));
+ lua_setfield(L,-2,"checksum");
+ lua_pushnumber(L,font_slant(f));
+ lua_setfield(L,-2,"slant");
+ lua_pushnumber(L,font_extend(f));
+ lua_setfield(L,-2,"extend");
+ lua_pushnumber(L,font_natural_dir(f));
+ lua_setfield(L,-2,"direction");
+ lua_pushnumber(L,font_encodingbytes(f));
+ lua_setfield(L,-2,"encodingbytes");
+ lua_pushnumber(L,font_tounicode(f));
+ lua_setfield(L,-2,"tounicode");
+
+ /* pdf parameters */
+ /* skip the first four for now, that are very much interal */
+ /*
+ if (pdf_font_size(f) != 0) {
+ lua_pushnumber(L,pdf_font_size(f));
+ lua_setfield(L,-2,"pdf_size");
+ }
+ if (pdf_font_num(f) != 0) {
+ lua_pushnumber(L,pdf_font_num(f));
+ lua_setfield(L,-2,"pdf_num");
+ }
+ if (pdf_font_blink(f) != 0) {
+ lua_pushnumber(L,pdf_font_blink(f));
+ lua_setfield(L,-2,"pdf_blink");
+ }
+ if (pdf_font_elink(f) != 0) {
+ lua_pushnumber(L,pdf_font_elink(f));
+ lua_setfield(L,-2,"pdf_elink");
+ }
+ */
+ /* the next one is read only */
+ if (pdf_font_expand_ratio(f) != 0) {
+ lua_pushnumber(L,pdf_font_expand_ratio(f));
+ lua_setfield(L,-2,"expand_ratio");
+ }
+ if (pdf_font_shrink(f) != 0) {
+ lua_pushnumber(L,pdf_font_shrink(f));
+ lua_setfield(L,-2,"shrink");
+ }
+ if (pdf_font_stretch(f) != 0) {
+ lua_pushnumber(L,pdf_font_stretch(f));
+ lua_setfield(L,-2,"stretch");
+ }
+ if (pdf_font_step(f) != 0) {
+ lua_pushnumber(L,pdf_font_step(f));
+ lua_setfield(L,-2,"step");
+ }
+ if (pdf_font_auto_expand(f) != 0) {
+ lua_pushboolean(L,pdf_font_auto_expand(f));
+ lua_setfield(L,-2,"auto_expand");
+ }
+ if (pdf_font_attr(f) != 0) {
+ lua_pushstring(L,makecstring(pdf_font_attr(f)));
+ lua_setfield(L,-2,"attributes");
+ }
+
+ /* params */
+ write_lua_parameters(L,f);
+
+ /* chars */
+ lua_createtable(L,font_tables[f]->charinfo_size,0); /* all characters */
+
+ if (has_left_boundary(f)) {
+ co = get_charinfo(f,left_boundarychar);
+ font_char_to_lua(L,f,co);
+ lua_setfield(L,-2,"left_boundary");
+ }
+ if (has_right_boundary(f)) {
+ co = get_charinfo(f,right_boundarychar);
+ font_char_to_lua(L,f,co);
+ lua_setfield(L,-2,"right_boundary");
+ }
+
+ for (k=font_bc(f);k<=font_ec(f);k++) {
+ if (char_exists(f,k)) {
+ lua_pushnumber(L,k);
+ co = get_charinfo(f,k);
+ font_char_to_lua(L,f,co);
+ lua_rawset(L,-3);
+ }
+ }
+ lua_setfield(L,-2,"characters");
+ return 1;
+}
+
+static int
+count_hash_items (lua_State *L, int name_index){
+ int n = -1;
+ lua_rawgeti(L,LUA_REGISTRYINDEX,name_index);
+ lua_rawget(L,-2);
+ if (!lua_isnil(L,-1)) {
+ if (lua_istable(L,-1)) {
+ n = 0;
+ /* now find the number */
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, -2) != 0) {
+ n++;
+ lua_pop(L,1);
+ }
+ }
+ }
+ lua_pop(L,1);
+ return n;
+}
+
+#define streq(a,b) (strcmp(a,b)==0)
+
+#define append_packet(k) { cpackets[np++] = k; }
+
+#define do_store_four(l) { \
+ append_packet((l&0xFF000000)>>24); \
+ append_packet((l&0x00FF0000)>>16); \
+ append_packet((l&0x0000FF00)>>8); \
+ append_packet((l&0x000000FF)); }
+
+/*
+*/
+
+#define lua_roundnumber(a,b) (int)floor((double)lua_tonumber(L,-1)+0.5)
+
+static int
+numeric_field (lua_State *L, char *name, int dflt) {
+ int i = dflt;
+ lua_pushstring(L,name);
+ lua_rawget(L,-2);
+ if (lua_isnumber(L,-1)) {
+ i = lua_roundnumber(L,-1);
+ }
+ lua_pop(L,1);
+ return i;
+}
+
+static int
+n_numeric_field (lua_State *L, int name_index, int dflt) {
+ register int i = dflt;
+ lua_rawgeti(L,LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */
+ lua_rawget(L,-2);
+ if (lua_type(L,-1)==LUA_TNUMBER) {
+ i = lua_roundnumber(L,-1);
+ }
+ lua_pop(L,1);
+ return i;
+}
+
+
+static int
+enum_field (lua_State *L, char *name, int dflt, char **values) {
+ int k;
+ char *s;
+ int i = dflt;
+ lua_pushstring(L,name);
+ lua_rawget(L,-2);
+ if (lua_isnumber(L,-1)) {
+ i = lua_tonumber(L,-1);
+ } else if (lua_isstring(L,-1)) {
+ s = (char *)lua_tostring(L,-1);
+ k = 0;
+ while (values[k] != NULL) {
+ if (strcmp(values[k],s) == 0) {
+ i = k;
+ break;
+ }
+ k++;
+ }
+ }
+ lua_pop(L,1);
+ return i;
+}
+
+static int
+boolean_field (lua_State *L, char *name, int dflt) {
+ int i = dflt;
+ lua_pushstring(L,name);
+ lua_rawget(L,-2);
+ if (lua_isboolean(L,-1)) {
+ i = lua_toboolean(L,-1);
+ }
+ lua_pop(L,1);
+ return i;
+}
+
+static int
+n_boolean_field (lua_State *L, int name_index, int dflt) {
+ int i = dflt;
+ lua_rawgeti(L,LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */
+ lua_rawget(L,-2);
+ if (lua_isboolean(L,-1)) {
+ i = lua_toboolean(L,-1);
+ }
+ lua_pop(L,1);
+ return i;
+}
+
+
+static char *
+string_field (lua_State *L, char *name, char *dflt) {
+ char *i;
+ lua_pushstring(L,name);
+ lua_rawget(L,-2);
+ if (lua_isstring(L,-1)) {
+ i = xstrdup(lua_tostring(L,-1));
+ } else if (dflt==NULL) {
+ i = NULL;
+ } else {
+ i = xstrdup(dflt);
+ }
+ lua_pop(L,1);
+ return i;
+}
+
+static char *
+n_string_field (lua_State *L, int name_index, char *dflt) {
+ char *i;
+ lua_rawgeti(L,LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */
+ lua_rawget(L,-2);
+ if (lua_isstring(L,-1)) {
+ i = xstrdup(lua_tostring(L,-1));
+ } else if (dflt==NULL) {
+ i = NULL;
+ } else {
+ i = xstrdup(dflt);
+ }
+ lua_pop(L,1);
+ return i;
+}
+
+#define init_luaS_index(a) do { \
+ lua_pushliteral(L,#a); \
+ luaS_##a##_ptr = (char *)lua_tostring(L,-1); \
+ luaS_##a##_index = luaL_ref (L,LUA_REGISTRYINDEX); \
+ } while (0)
+
+#define make_luaS_index(a) \
+ static int luaS_##a##_index = 0; \
+ static char * luaS_##a##_ptr = NULL
+
+#define luaS_index(a) luaS_##a##_index
+
+#define luaS_ptr_eq(a,b) (a==luaS_##b##_ptr)
+
+make_luaS_index(width);
+make_luaS_index(height);
+make_luaS_index(depth);
+make_luaS_index(italic);
+make_luaS_index(index);
+make_luaS_index(left_protruding);
+make_luaS_index(right_protruding);
+make_luaS_index(expansion_factor);
+make_luaS_index(top);
+make_luaS_index(bot);
+make_luaS_index(rep);
+make_luaS_index(mid);
+make_luaS_index(next);
+make_luaS_index(used);
+make_luaS_index(name);
+make_luaS_index(tounicode);
+make_luaS_index(font);
+make_luaS_index(char);
+make_luaS_index(slot);
+make_luaS_index(comment);
+make_luaS_index(push);
+make_luaS_index(pop);
+make_luaS_index(rule);
+make_luaS_index(right);
+make_luaS_index(node);
+make_luaS_index(down);
+make_luaS_index(special);
+make_luaS_index(slant);
+make_luaS_index(space);
+make_luaS_index(space_stretch);
+make_luaS_index(space_shrink);
+make_luaS_index(x_height);
+make_luaS_index(quad);
+make_luaS_index(extra_space);
+make_luaS_index(left_boundary);
+make_luaS_index(right_boundary);
+make_luaS_index(kerns);
+make_luaS_index(ligatures);
+make_luaS_index(fonts);
+
+void init_font_string_pointers (lua_State *L) {
+ init_luaS_index(width);
+ init_luaS_index(height);
+ init_luaS_index(depth);
+ init_luaS_index(italic);
+ init_luaS_index(index);
+ init_luaS_index(left_protruding);
+ init_luaS_index(right_protruding);
+ init_luaS_index(expansion_factor);
+ init_luaS_index(top);
+ init_luaS_index(bot);
+ init_luaS_index(rep);
+ init_luaS_index(mid);
+ init_luaS_index(next);
+ init_luaS_index(used);
+ init_luaS_index(name);
+ init_luaS_index(tounicode);
+ init_luaS_index(font);
+ init_luaS_index(char);
+ init_luaS_index(slot);
+ init_luaS_index(comment);
+ init_luaS_index(push);
+ init_luaS_index(pop);
+ init_luaS_index(rule);
+ init_luaS_index(right);
+ init_luaS_index(node);
+ init_luaS_index(down);
+ init_luaS_index(special);
+
+ init_luaS_index(slant);
+ init_luaS_index(space);
+ init_luaS_index(space_stretch);
+ init_luaS_index(space_shrink);
+ init_luaS_index(x_height);
+ init_luaS_index(quad);
+ init_luaS_index(extra_space);
+
+ init_luaS_index(left_boundary);
+ init_luaS_index(right_boundary);
+ init_luaS_index(kerns);
+ init_luaS_index(ligatures);
+ init_luaS_index(fonts);
+}
+
+static int
+count_char_packet_bytes (lua_State *L) {
+ register int i;
+ register int l = 0;
+ int ff = 0;
+ for (i=1;i<=lua_objlen(L,-1);i++) {
+ lua_rawgeti(L,-1,i);
+ if (lua_istable(L,-1)) {
+ lua_rawgeti(L,-1,1);
+ if (lua_isstring(L,-1)) {
+ char *s = (char *)lua_tostring(L,-1);
+ if (luaS_ptr_eq(s,font)) { l+= 5; ff =1; }
+ else if (luaS_ptr_eq(s,char)) { if (ff==0) { l+=5; } l += 5; ff = 1; }
+ else if (luaS_ptr_eq(s,slot)) { l += 10; ff = 1;}
+ else if (luaS_ptr_eq(s,comment)) { ; }
+ else if (luaS_ptr_eq(s,push) ||
+ luaS_ptr_eq(s,pop)) { l++; }
+ else if (luaS_ptr_eq(s,rule)) { l+=9; }
+ else if (luaS_ptr_eq(s,right) ||
+ luaS_ptr_eq(s,node) ||
+ luaS_ptr_eq(s,down)) { l+=5; }
+ else if (luaS_ptr_eq(s,special)) {
+ size_t len;
+ lua_rawgeti(L,-2,2);
+ (void)lua_tolstring(L,-1,&len);
+ lua_pop(L,1);
+ if (len>0) { l = l + 5 + len; }
+ }
+ else { fprintf(stdout,"unknown packet command %s!\n",s); }
+ } else {
+ fprintf(stdout,"no packet command!\n");
+ }
+ lua_pop(L,1); /* command name */
+ }
+ lua_pop(L,1); /* item */
+ }
+ return l;
+}
+
+
+
+scaled
+sp_to_dvi (halfword sp, halfword atsize) {
+ double result, mult;
+ mult = (double)(atsize>>16);
+ result = (sp << 4);
+ return floor (result/mult );
+}
+
+
+static void
+read_char_packets (lua_State *L, integer *l_fonts, charinfo *co, int atsize) {
+ int i, n, m;
+ size_t l;
+ int cmd;
+ char *s;
+ real_eight_bits *cpackets;
+ int ff = 0;
+ int np = 0;
+ int max_f = 0;
+ int pc = count_char_packet_bytes (L);
+ if (pc<=0)
+ return;
+ assert(l_fonts != NULL);
+ assert(l_fonts[1] != 0);
+ while (l_fonts[(max_f+1)]!=0)
+ max_f++;
+
+ cpackets = xmalloc(pc+1);
+ for (i=1;i<=lua_objlen(L,-1);i++) {
+ lua_rawgeti(L,-1,i);
+ if (lua_istable(L,-1)) {
+ /* fetch the command code */
+ lua_rawgeti(L,-1,1);
+ if (lua_isstring(L,-1)) {
+ s = (char *)lua_tostring(L,-1);
+ cmd = 0;
+ if (luaS_ptr_eq(s,font)) {
+ cmd = packet_font_code;
+ } else if (luaS_ptr_eq(s,char)) {
+ cmd = packet_char_code;
+ if (ff==0) {
+ append_packet(packet_font_code);
+ ff = l_fonts[1];
+ do_store_four(ff);
+ }
+ } else if (luaS_ptr_eq(s,slot)) {
+ cmd = packet_nop_code;
+ lua_rawgeti(L,-2,2); n = lua_tointeger(L,-1);
+ ff = (n>max_f ? l_fonts[1] : l_fonts[n]);
+ lua_rawgeti(L,-3,3); n = lua_tointeger(L,-1);
+ lua_pop(L,2);
+ append_packet(packet_font_code);
+ do_store_four(ff);
+ append_packet(packet_char_code);
+ do_store_four(n);
+ }
+ else if (luaS_ptr_eq(s,comment)) { cmd = packet_nop_code; }
+ else if (luaS_ptr_eq(s,node)) { cmd = packet_node_code; }
+ else if (luaS_ptr_eq(s,push)) { cmd = packet_push_code; }
+ else if (luaS_ptr_eq(s,pop)) { cmd = packet_pop_code; }
+ else if (luaS_ptr_eq(s,rule)) { cmd = packet_rule_code; }
+ else if (luaS_ptr_eq(s,right)) { cmd = packet_right_code; }
+ else if (luaS_ptr_eq(s,down)) { cmd = packet_down_code; }
+ else if (luaS_ptr_eq(s,special)) { cmd = packet_special_code; }
+
+ switch(cmd) {
+ case packet_push_code:
+ case packet_pop_code:
+ append_packet(cmd);
+ break;
+ case packet_font_code:
+ append_packet(cmd);
+ lua_rawgeti(L,-2,2);
+ n = lua_tointeger(L,-1);
+ ff = (n>max_f ? l_fonts[1] : l_fonts[n]);
+ do_store_four(ff);
+ lua_pop(L,1);
+ break;
+ case packet_node_code:
+ append_packet(cmd);
+ lua_rawgeti(L,-2,2);
+ n = copy_node_list(nodelist_from_lua(L));
+ do_store_four(n);
+ lua_pop(L,1);
+ break;
+ case packet_char_code:
+ append_packet(cmd);
+ lua_rawgeti(L,-2,2);
+ n = lua_tointeger(L,-1);
+ do_store_four(n);
+ lua_pop(L,1);
+ break;
+ case packet_right_code:
+ case packet_down_code:
+ append_packet(cmd);
+ lua_rawgeti(L,-2,2);
+ n = lua_tointeger(L,-1);
+ do_store_four(sp_to_dvi(n,atsize));
+ lua_pop(L,1);
+ break;
+ case packet_rule_code:
+ append_packet(cmd);
+ lua_rawgeti(L,-2,2);
+ n = lua_tointeger(L,-1);
+ do_store_four(sp_to_dvi(n,atsize));
+ lua_rawgeti(L,-3,3);
+ n = lua_tointeger(L,-1);
+ do_store_four(sp_to_dvi(n,atsize));
+ lua_pop(L,2);
+ break;
+ case packet_special_code:
+ append_packet(cmd);
+ lua_rawgeti(L,-2,2);
+ s = (char *)lua_tolstring(L,-1,&l);
+ if (l>0) {
+ do_store_four(l);
+ m = (int)l;
+ while(m>0) {
+ n = *s++;
+ m--;
+ append_packet(n);
+ }
+ }
+ lua_pop(L,1);
+ break;
+ case packet_nop_code:
+ break;
+ default:
+ fprintf(stdout,"Unknown char packet code %s (char %d in font %s)\n",s,(int)c,font_name(f));
+ }
+ }
+ lua_pop(L,1); /* command code */
+ } else {
+ fprintf(stdout,"Found a `commands' item that is not a table (char %d in font %s)\n",(int)c,font_name(f));
+ }
+ lua_pop(L,1); /* command table */
+ }
+ append_packet(packet_end_code);
+ set_charinfo_packets(co,cpackets);
+ return;
+}
+
+
+static void
+read_lua_cidinfo (lua_State *L, int f) {
+ int i;
+ char *s;
+ lua_getfield(L,-1,"cidinfo");
+ if (lua_istable(L,-1)) {
+ i = numeric_field(L,"version",0);
+ set_font_cidversion(f,i);
+ i = numeric_field(L,"supplement",0);
+ set_font_cidsupplement(f,i);
+ s = string_field(L,"registry","Adobe"); /* Adobe-Identity-0 */
+ set_font_cidregistry(f,s);
+ s = string_field(L,"ordering","Identity");
+ set_font_cidordering(f,s);
+ }
+ lua_pop(L,1);
+}
+
+
+static void
+read_lua_parameters (lua_State *L, int f) {
+ int i, n;
+ char *s;
+ lua_getfield(L,-1,"parameters");
+ if (lua_istable(L,-1)) {
+ /* the number of parameters is the max(IntegerKeys(L)),7) */
+ n = 7;
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, -2) != 0) {
+ if (lua_isnumber(L,-2)) {
+ i = lua_tonumber(L,-2);
+ if (i > n) n = i;
+ }
+ lua_pop(L,1); /* pop value */
+ }
+ if (n>7) set_font_params(f,n);
+ /* sometimes it is handy to have all integer keys */
+ for (i=1;i<=7;i++) {
+ lua_rawgeti(L,-1,i);
+ if (lua_isnumber(L,-1)) {
+ n = lua_roundnumber(L,-1);
+ set_font_param(f,i, n);
+ }
+ lua_pop(L,1);
+ }
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, -2) != 0) {
+ if (lua_isnumber(L,-2)) {
+ i = lua_tointeger(L,-2);
+ if (i>=8) {
+ n = (lua_isnumber(L,-1) ? lua_roundnumber(L,-1) : 0);
+ set_font_param(f,i, n);
+ }
+ } else if (lua_isstring(L,-2)) {
+ s = (char *)lua_tostring(L,-2);
+ n = (lua_isnumber(L,-1) ? lua_roundnumber(L,-1) : 0);
+ if (luaS_ptr_eq(s,slant)) { set_font_param(f,slant_code,n); }
+ else if (luaS_ptr_eq(s,space)) { set_font_param(f,space_code,n); }
+ else if (luaS_ptr_eq(s,space_stretch)) { set_font_param(f,space_stretch_code,n); }
+ else if (luaS_ptr_eq(s,space_shrink)) { set_font_param(f,space_shrink_code,n); }
+ else if (luaS_ptr_eq(s,x_height)) { set_font_param(f,x_height_code,n); }
+ else if (luaS_ptr_eq(s,quad)) { set_font_param(f,quad_code,n); }
+ else if (luaS_ptr_eq(s,extra_space)) { set_font_param(f,extra_space_code,n); }
+ }
+ lua_pop(L,1);
+ }
+ }
+ lua_pop(L,1);
+
+}
+
+void
+font_char_from_lua (lua_State *L, internal_font_number f, integer i, integer *l_fonts) {
+ int k,r,t;
+ charinfo *co;
+ kerninfo *ckerns;
+ liginfo *cligs;
+ scaled j;
+ char *s;
+ int nl = 0; /* number of ligature table items */
+ int nk = 0; /* number of kern table items */
+ int ctr = 0;
+ int atsize = font_size(f);
+ if (lua_istable(L,-1)) {
+ co = get_charinfo(f,i);
+ set_charinfo_tag (co,0);
+ j = n_numeric_field(L,luaS_width_index,0); set_charinfo_width (co,j);
+ j = n_numeric_field(L,luaS_height_index,0); set_charinfo_height (co,j);
+ j = n_numeric_field(L,luaS_depth_index,0); set_charinfo_depth (co,j);
+ j = n_numeric_field(L,luaS_italic_index,0); set_charinfo_italic (co,j);
+ j = n_numeric_field(L,luaS_index_index,0); set_charinfo_index(co,j);
+ j = n_numeric_field(L,luaS_expansion_factor_index,0); set_charinfo_ef(co,j);
+ j = n_numeric_field(L,luaS_left_protruding_index,0); set_charinfo_lp(co,j);
+ j = n_numeric_field(L,luaS_right_protruding_index,0); set_charinfo_rp(co,j);
+ k = n_boolean_field(L,luaS_used_index,0); set_charinfo_used(co,k);
+ s = n_string_field (L,luaS_name_index,NULL); set_charinfo_name(co,s);
+ s = n_string_field (L,luaS_tounicode_index,NULL); set_charinfo_tounicode(co,s);
+ k = n_numeric_field(L,luaS_next_index,-1);
+ if (k>=0) {
+ set_charinfo_tag (co,list_tag);
+ set_charinfo_remainder (co,k);
+ }
+ lua_getfield(L,-1,"extensible");
+ if (lua_istable(L,-1)){
+ int top, bot,mid, rep;
+ top = n_numeric_field(L,luaS_top_index,0);
+ bot = n_numeric_field(L,luaS_bot_index,0);
+ mid = n_numeric_field(L,luaS_mid_index,0);
+ rep = n_numeric_field(L,luaS_rep_index,0);
+ if (top != 0 || bot != 0 || mid != 0 || rep != 0) {
+ set_charinfo_tag (co,ext_tag);
+ set_charinfo_extensible (co,top,bot,mid,rep);
+ } else {
+ pdftex_warn("lua-loaded font %s char [%d] has an invalid extensible field!",font_name(f),(int)i);
+ }
+ }
+ lua_pop(L,1);
+
+ nk = count_hash_items(L,luaS_index(kerns));
+ if (nk>0) {
+ ckerns = xcalloc((nk+1),sizeof(kerninfo));
+ lua_rawgeti(L,LUA_REGISTRYINDEX,luaS_index(kerns));
+ lua_rawget(L,-2);
+ if (lua_istable(L,-1)) { /* there are kerns */
+ ctr = 0;
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0) {
+ k = non_boundarychar;
+ if (lua_isnumber(L,-2)) {
+ k = lua_tonumber(L,-2); /* adjacent char */
+ if (k<0)
+ k = non_boundarychar;
+ } else if (lua_isstring(L,-2)) {
+ s = (char *)lua_tostring(L,-2);
+ if (luaS_ptr_eq(s,right_boundary)) {
+ k = right_boundarychar;
+ if (!has_right_boundary(f))
+ set_right_boundary(f,get_charinfo(f,right_boundarychar));
+ }
+ }
+ j = lua_roundnumber(L,-1); /* movement */
+ if (k!=non_boundarychar) {
+ set_kern_item(ckerns[ctr],k,j);
+ ctr++;
+ } else {
+ pdftex_warn("lua-loaded font %s char [%d] has an invalid kern field!",font_name(f),(int)i);
+ }
+ lua_pop(L,1);
+ }
+ /* guard against empty tables */
+ if (ctr>0) {
+ set_kern_item(ckerns[ctr],end_kern,0);
+ set_charinfo_kerns(co,ckerns);
+ } else {
+ pdftex_warn("lua-loaded font %s char [%d] has an invalid kerns field!",font_name(f),(int)i);
+ }
+ }
+ lua_pop(L,1);
+ }
+
+ /* packet commands */
+ lua_getfield(L,-1,"commands");
+ if (lua_istable(L,-1)){
+ lua_pushnil(L); /* first key */
+ if (lua_next(L, -2) != 0) {
+ lua_pop(L,2);
+ read_char_packets(L,(integer *)l_fonts,co,atsize);
+ }
+ }
+ lua_pop(L,1);
+
+ /* ligatures */
+ nl = count_hash_items(L,luaS_index(ligatures));
+
+ if (nl>0) {
+ cligs = xcalloc((nl+1),sizeof(liginfo));
+ lua_rawgeti(L,LUA_REGISTRYINDEX,luaS_index(ligatures));
+ lua_rawget(L,-2);
+ if (lua_istable(L,-1)){/* do ligs */
+ ctr = 0;
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0) {
+ k = non_boundarychar;
+ if (lua_isnumber(L,-2)) {
+ k = lua_tonumber(L,-2); /* adjacent char */
+ if (k<0) {
+ k = non_boundarychar;
+ }
+ } else if (lua_isstring(L,-2)) {
+ s = (char *)lua_tostring(L,-2);
+ if (luaS_ptr_eq(s,right_boundary)) {
+ k = right_boundarychar;
+ if (!has_right_boundary(f))
+ set_right_boundary(f,get_charinfo(f,right_boundarychar));
+ }
+ }
+ r = -1;
+ if (lua_istable(L,-1)) {
+ r = n_numeric_field(L,luaS_char_index,-1); /* ligature */
+ }
+ if (r != -1 && k != non_boundarychar) {
+ t = enum_field(L,"type",0,ligature_type_strings);
+ set_ligature_item(cligs[ctr],(t*2)+1,k,r);
+ ctr++;
+ } else {
+ pdftex_warn("lua-loaded font %s char [%d] has an invalid ligature field!",font_name(f),(int)i);
+ }
+ lua_pop(L,1); /* iterator value */
+ }
+ /* guard against empty tables */
+ if (ctr>0) {
+ set_ligature_item(cligs[ctr],0,end_ligature,0);
+ set_charinfo_ligatures(co,cligs);
+ } else {
+ pdftex_warn("lua-loaded font %s char [%d] has an invalid ligatures field!",font_name(f),(int)i);
+ }
+ }
+ lua_pop(L,1); /* ligatures table */
+ }
+ }
+}
+
+
+
+/* The caller has to fix the state of the lua stack when there is an error! */
+
+
+int
+font_from_lua (lua_State *L, int f) {
+ int i,n,r,t;
+ int s_top; /* lua stack top */
+ int bc; /* first char index */
+ int ec; /* last char index */
+ char *s;
+ integer *l_fonts = NULL;
+ /* the table is at stack index -1 */
+
+ if (luaS_width_index==0)
+ init_font_string_pointers(L);
+
+ s = string_field(L,"area",""); set_font_area(f,s);
+ s = string_field(L,"filename",NULL); set_font_filename(f,s);
+ s = string_field(L,"encodingname",NULL); set_font_encodingname(f,s);
+
+ s = string_field(L,"name",NULL); set_font_name(f,s);
+ s = string_field(L,"fullname",font_name(f)); set_font_fullname(f,s);
+
+ if (s==NULL) {
+ pdftex_fail("lua-loaded font [%d] has no name!",f);
+ return false;
+ }
+
+ i = numeric_field(L,"designsize",655360); set_font_dsize(f,i);
+ i = numeric_field(L,"size",font_dsize(f)); set_font_size(f,i);
+ i = numeric_field(L,"checksum",0); set_font_checksum(f,i);
+ i = numeric_field(L,"direction",0); set_font_natural_dir(f,i);
+ i = numeric_field(L,"encodingbytes",0); set_font_encodingbytes(f,i);
+ i = numeric_field(L,"tounicode",0); set_font_tounicode(f,i);
+
+ i = numeric_field(L,"extend",0);
+ if (i<-2000) i = -2000; if (i>2000) i = 2000; if (i==1000) i = 0;
+ set_font_extend(f,i);
+ i = numeric_field(L,"slant",0);
+ if (i<-1000) i = -1000; if (i>1000) i = 1000;
+ set_font_slant(f,i);
+
+ i = numeric_field(L,"hyphenchar",get_default_hyphen_char()); set_hyphen_char(f,i);
+ i = numeric_field(L,"skewchar",get_default_skew_char()); set_skew_char(f,i);
+ i = boolean_field(L,"used",0); set_font_used(f,i);
+
+ s = string_field (L,"attributes",NULL);
+ if (s!=NULL && strlen(s)>0) {
+ i = maketexstring(s);
+ set_pdf_font_attr(f,i);
+ }
+
+ i = enum_field(L,"type", unknown_font_type,font_type_strings); set_font_type(f,i);
+ i = enum_field(L,"format", unknown_format, font_format_strings); set_font_format(f,i);
+ i = enum_field(L,"embedding",unknown_embedding,font_embedding_strings); set_font_embedding(f,i);
+ if (font_encodingbytes(f)==0 &&
+ (font_format(f)==opentype_format || font_format(f)==truetype_format)) {
+ set_font_encodingbytes(f,2);
+ }
+
+ /* now fetch the base fonts, if needed */
+ n = count_hash_items(L,luaS_index(fonts));
+ if (n>0) {
+ l_fonts = xmalloc((n+2)*sizeof(integer));
+ memset (l_fonts,0,(n+2)*sizeof(integer));
+ lua_rawgeti(L,LUA_REGISTRYINDEX,luaS_index(fonts));
+ lua_rawget(L,-2);
+ for (i=1;i<=n;i++) {
+ lua_rawgeti(L,-1,i);
+ if (lua_istable(L,-1)) {
+ lua_getfield(L,-1,"id");
+ if (lua_isnumber(L,-1)) {
+ l_fonts[i] = lua_tonumber(L,-1);
+ lua_pop(L,2); /* pop id and entry */
+ continue;
+ }
+ lua_pop(L,1); /* pop id */
+ };
+ s = NULL;
+ if (lua_istable(L,-1)) {
+ lua_getfield(L,-1,"name");
+ if (lua_isstring(L,-1)) {
+ s = (char *)lua_tostring(L,-1);
+ }
+ lua_pop(L,1); /* pop name */
+ }
+ if (s!= NULL) {
+ lua_getfield(L,-1,"size");
+ t = (lua_isnumber(L,-1) ? lua_roundnumber(L,-1) : -1000);
+ lua_pop(L,1);
+
+ /* TODO: the stack is messed up, otherwise this
+ * explicit resizing would not be needed
+ */
+ s_top = lua_gettop(L);
+ l_fonts[i] = find_font_id(s,"",t);
+ lua_settop(L,s_top);
+ } else {
+ pdftex_fail("Invalid local font in font %s!\n", font_name(f));
+ }
+ lua_pop(L,1); /* pop list entry */
+ }
+ lua_pop(L,1); /* pop list entry */
+ } else {
+ if(font_type(f) == virtual_font_type) {
+ pdftex_fail("Invalid local fonts in font %s!\n", font_name(f));
+ } else {
+ l_fonts = xmalloc(3*sizeof(integer));
+ l_fonts[0] = 0;
+ l_fonts[1] = f;
+ l_fonts[2] = 0;
+ }
+ }
+
+ /* parameters */
+ read_lua_parameters(L,f);
+ read_lua_cidinfo(L,f);
+
+ /* characters */
+ lua_getfield(L,-1,"characters");
+ if (lua_istable(L,-1)) {
+ /* find the array size values */
+ ec = 0; bc = -1;
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, -2) != 0) {
+ if (lua_isnumber(L,-2)) {
+ i = lua_tointeger(L,-2);
+ if (i>=0) {
+ if (lua_istable(L,-1)) {
+ if (i>ec) ec = i;
+ if (bc<0) bc = i;
+ if (bc>=0 && i<bc) bc = i;
+ }
+ }
+ }
+ lua_pop(L, 1);
+ }
+
+ if (bc != -1) {
+ /* fprintf(stdout,"defined a font at %d with %d-%d\n",f,bc,ec); */
+ set_font_bc(f,bc);
+ set_font_ec(f,ec);
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, -2) != 0) {
+ if (lua_isnumber(L,-2)) {
+ i = lua_tonumber(L,-2);
+ if (i>=0) {
+ font_char_from_lua(L,f, i, l_fonts);
+ }
+ } else if (lua_isstring(L,-2)) {
+ s = (char *)lua_tostring(L,-2);
+ if (luaS_ptr_eq(s,left_boundary)) {
+ font_char_from_lua(L,f, left_boundarychar, l_fonts);
+ } else if (luaS_ptr_eq(s,right_boundary)) {
+ font_char_from_lua(L,f, right_boundarychar, l_fonts);
+ }
+ }
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
+
+ /* handle font expansion last: the |copy_font| routine is called eventually,
+ and that needs to know |bc| and |ec|. */
+ if (font_type(f)!=virtual_font_type) {
+ int fstep = numeric_field(L,"step",0);
+ if (fstep<0) fstep = 0;
+ if (fstep>100) fstep = 100;
+ if (fstep!=0) {
+ int fshrink = numeric_field(L,"shrink",0);
+ int fstretch = numeric_field(L,"stretch",0);
+ int fexpand = boolean_field(L,"auto_expand",0);
+ if (fshrink<0) fshrink = 0;
+ if (fshrink>500) fshrink = 500;
+ fshrink -= (fshrink % fstep);
+ if (fshrink<0) fshrink = 0;
+ if (fstretch<0) fstretch = 0;
+ if (fstretch>1000) fstretch = 1000;
+ fstretch -= (fstretch % fstep);
+ if (fstretch<0) fstretch = 0;
+ set_expand_params(f, fexpand, fstretch, fshrink, fstep, 0);
+ }
+ }
+
+ } else { /* jikes, no characters */
+ pdftex_warn("lua-loaded font [%d] (%s) has no characters!",f, font_name(f));
+ }
+
+#if SAVE_REF
+ r = luaL_ref(Luas[0],LUA_REGISTRYINDEX); /* pops the table */
+ set_font_cache_id(f,r);
+#else
+ lua_pop(Luas[0],1);
+#endif
+ } else { /* jikes, no characters */
+ pdftex_warn("lua-loaded font [%d] (%s) has no character table!",f, font_name(f));
+ }
+
+ if (l_fonts!=NULL)
+ free(l_fonts);
+ return true;
+}
+
+/* ====================================================================
+ *
+ * L I G A T U R I N G
+ *
+ * ==================================================================== */
+
+
+#define assert_disc(a) \
+ assert(pre_break(a)!=null); /* expect head_node */ \
+ assert(type(pre_break(a))==nesting_node); \
+ assert((vlink_pre_break(a)==null && tlink_pre_break(a)==null) || tail_of_list(vlink_pre_break(a))==tlink_pre_break(a)); \
+ assert(post_break(a)!=null); /* expect head_node */ \
+ assert(type(post_break(a))==nesting_node); \
+ assert((vlink_post_break(a)==null && tlink_post_break(a)==null) || tail_of_list(vlink_post_break(a))==tlink_post_break(a)); \
+ assert(no_break(a)!=null); /* expect head_node */ \
+ assert(type(no_break(a))==nesting_node); \
+ assert((vlink_no_break(a)==null && tlink_no_break(a)==null) || tail_of_list(vlink_no_break(a))==tlink_no_break(a));
+
+static void
+nesting_append (halfword nest, halfword newn) {
+ halfword tail = tlink(nest);
+ assert(alink(nest)==null);
+ assert(vlink(newn)==null);
+ assert(alink(newn)==null);
+ if (tail==null) {
+ assert(vlink(nest)==null);
+ couple_nodes(nest,newn);
+ } else {
+ assert(vlink(tail)==null);
+ assert(tail_of_list(vlink(nest))==tail);
+ couple_nodes(tail,newn);
+ }
+ tlink(nest) = newn;
+}
+
+
+static void
+nesting_prepend (halfword nest, halfword newn) {
+ halfword head = vlink(nest);
+ assert(alink(nest)==null);
+ assert(vlink(newn)==null);
+ assert(alink(newn)==null);
+ couple_nodes(nest,newn);
+ if (head==null) {
+ assert(tlink(nest)==null);
+ tlink(nest) = newn;
+ } else {
+ assert(alink(head)==nest);
+ assert(tail_of_list(head)==tlink(nest));
+ couple_nodes(newn,head);
+ }
+}
+
+
+static int
+test_ligature( liginfo *lig, halfword left, halfword right ) {
+ if (type(left)!=glyph_node)
+ return 0;
+ assert(type(right)==glyph_node);
+ if (font(left)!=font(right)) return 0;
+ if (is_ghost(left) || is_ghost(right)) return 0;
+ *lig = get_ligature(font(left),character(left),character(right));
+ if (is_valid_ligature(*lig)) {
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+try_ligature(halfword *frst, halfword fwd) {
+ halfword cur = *frst;
+ liginfo lig;
+ if (test_ligature(&lig,cur,fwd)) {
+ int move_after = (lig_type(lig) & 0x0C)>>2;
+ int keep_right = ((lig_type(lig) & 0x01) != 0);
+ int keep_left = ((lig_type(lig) & 0x02) != 0);
+ halfword newgl = raw_glyph_node();
+ font(newgl) = font(cur);
+ character(newgl) = lig_replacement(lig);
+ set_is_ligature(newgl);
+
+ /* below might not be correct in contrived border case.
+ * but we use it only for debugging, so ... */
+ if (character(cur)<0) {
+ set_is_leftboundary(newgl);
+ }
+ if (character(fwd)<0) {
+ set_is_rightboundary(newgl);
+ }
+ if (character(cur)<0) {
+ if (character(fwd)<0) {
+ build_attribute_list(newgl);
+ } else {
+ add_node_attr_ref(node_attr(fwd));
+ node_attr(newgl) = node_attr(fwd);
+ }
+ } else {
+ add_node_attr_ref(node_attr(cur));
+ node_attr(newgl) = node_attr(cur);
+ }
+
+ /* TODO/FIXME if this ligature is consists of another ligature
+ * we should add it's lig_ptr to the new glyphs lig_ptr (and
+ * cleanup the no longer needed node) LOW PRIORITY */
+ /* left side */
+ if (keep_left) {
+ halfword new_first = copy_node(cur);
+ lig_ptr(newgl) = new_first;
+ couple_nodes(cur,newgl);
+ if (move_after) {
+ move_after--;
+ cur = newgl;
+ }
+ } else {
+ halfword prev = alink(cur);
+ uncouple_node(cur);
+ lig_ptr(newgl) = cur;
+ assert(prev!=null);
+ couple_nodes(prev,newgl);
+ cur = newgl; /* as cur has disappeared */
+ }
+ /* right side */
+ if (keep_right) {
+ halfword new_second = copy_node(fwd);
+ /* correct, because we _know_ lig_ptr points to _one_ node */
+ couple_nodes(lig_ptr(newgl),new_second);
+ couple_nodes(newgl,fwd);
+ if (move_after) {
+ move_after--;
+ cur = fwd;
+ }
+ } else {
+ halfword next = vlink(fwd);
+ uncouple_node(fwd);
+ /* correct, because we _know_ lig_ptr points to _one_ node */
+ couple_nodes(lig_ptr(newgl),fwd);
+ if (next!=null) {couple_nodes(newgl,next);}
+ }
+
+ /* check and return */
+ /* assert(move_after==0);*/
+ *frst = cur;
+ return 1;
+ }
+ return 0;
+}
+
+
+/* there shouldn't be any ligatures here - we only add them at the end of
+ * xxx_break in a DISC-1 - DISC-2 situation and we stop processing DISC-1
+ * (we continue with DISC-1's post_ and no_break */
+static halfword
+handle_lig_nest(halfword root, halfword cur) {
+ if (cur==null) return root;
+ while (vlink(cur)!=null) {
+ halfword fwd = vlink(cur);
+ if (type(cur)==glyph_node && type(fwd)==glyph_node &&
+ font(cur)==font(fwd) && try_ligature(&cur,fwd)) continue;
+ cur = vlink(cur);
+ assert(vlink(alink(cur))==cur);
+ }
+ tlink(root) = cur;
+ return root;
+}
+
+
+static halfword
+handle_lig_word(halfword cur) {
+ halfword right=null;
+
+ if (type(cur)==whatsit_node && subtype(cur)==cancel_boundary_node) {
+ halfword prev= alink(cur);
+ halfword fwd = vlink(cur);
+/* uncouple_node(cur); */ /* not needed, it is freed */
+ flush_node(cur);
+ if (fwd==null) {
+ vlink(prev) = fwd;
+ return prev;
+ }
+ couple_nodes(prev,fwd);
+ if (type(fwd)!=glyph_node) return prev;
+ cur = fwd;
+ } else if (has_left_boundary(font(cur))) {
+ halfword prev= alink(cur);
+ halfword p = new_glyph(font(cur),left_boundarychar);
+ couple_nodes(prev,p);
+ couple_nodes(p,cur);
+ cur = p;
+ }
+ if (has_right_boundary(font(cur))) {
+ right = new_glyph(font(cur),right_boundarychar);
+ }
+
+ while (1) {
+ /* A glyph followed by ... */
+ if (type(cur)==glyph_node) {
+ halfword fwd = vlink(cur);
+ if (fwd==null) { /* last character of paragraph */
+ if (right==null) break;
+ couple_nodes(cur,right);
+ right = null;
+ continue;
+ }
+ assert(alink(fwd)==cur);
+ if (type(fwd)==glyph_node) { /* GLYPH - GLYPH */
+ if (font(cur)!=font(fwd)) break;
+ if (try_ligature(&cur,fwd)) continue;
+ } else if (type(fwd)==disc_node) { /* GLYPH - DISC */
+
+ /* if a{bx}{}{y} and a+b=>B convert to {Bx}{}{ay} */
+ halfword pre = vlink_pre_break(fwd);
+ halfword nob = vlink_no_break(fwd);
+ liginfo lig;
+ assert_disc(fwd);
+ /* Check on: a{b?}{?}{?} and a+b=>B : {B?}{?}{a?}*/
+ /* Check on: a{?}{?}{b?} and a+b=>B : {a?}{?}{B?} */
+ if ( (pre!=null && type(pre)==glyph_node && test_ligature(&lig,cur,pre))
+ || (nob!=null && type(nob)==glyph_node && test_ligature(&lig,cur,nob))) {
+ /* move cur from before disc, to skipped part */
+ halfword prev = alink(cur);
+ assert(vlink(prev)==cur);
+ uncouple_node(cur);
+ couple_nodes(prev,fwd);
+ nesting_prepend(no_break(fwd),cur);
+ /* now ligature the pre_break */
+ nesting_prepend(pre_break(fwd),copy_node(cur));
+ /* As we have removed cur, we need to start again ... */
+ cur = prev;
+ }
+ /* Check on: a{?}{?}{}b and a+b=>B : {a?}{?b}{B}*/
+ halfword next = vlink(fwd);
+ if (nob==null && next != null && type(next)==glyph_node
+ && test_ligature(&lig,cur,next)) {
+ /* move cur from before disc to no_break part */
+ halfword prev = alink(cur);
+ assert(alink(next)==fwd);
+ assert(vlink(prev)==cur);
+ uncouple_node(cur);
+ couple_nodes(prev,fwd);
+ couple_nodes(no_break(fwd),cur); /* we _know_ it's empty */
+ /* now copy cur the pre_break */
+ nesting_prepend(pre_break(fwd),copy_node(cur));
+ /* move next from after disc to no_break part */
+ halfword tail = vlink(next);
+ uncouple_node(next);
+ try_couple_nodes(fwd,tail);
+ couple_nodes(cur,next); /* we _know_ this works */
+ tlink(no_break(fwd)) = next; /* and make sure the list is correct */
+ /* now copy next to the post_break */
+ nesting_append(post_break(fwd),copy_node(next));
+ /* As we have removed cur, we need to start again ... */
+ cur = prev;
+ }
+ /* we are finished with the pre_break */
+ handle_lig_nest(pre_break(fwd),vlink_pre_break(fwd));
+ } else if (type(fwd)==whatsit_node && subtype(fwd)==cancel_boundary_node) {
+ halfword next = vlink(fwd);
+ try_couple_nodes(cur,next);
+ flush_node(fwd);
+ if (right!=null) {
+ flush_node(right); /* Shame, didn't need it */
+ /* right = null; */ /* no need, we're going to leave the loop anyway */
+ }
+ break;
+ } else { /* fwd is something unknown */
+ if (right==null) break;
+ couple_nodes(cur,right);
+ couple_nodes(right,fwd);
+ right = null;
+ continue;
+ }
+ /* A discretionary followed by ... */
+ } else if (type(cur)==disc_node) {
+ assert_disc(cur);
+ /* If {?}{x}{?} or {?}{?}{y} then ... */
+ if (vlink_no_break(cur)!=null || vlink_post_break(cur)!=null) {
+ halfword fwd;
+ halfword lists[511]; /* max 8 levels */
+
+ int i, max_depth=0;
+ lists[max_depth++] = handle_lig_nest(post_break(cur),vlink_post_break(cur));
+ lists[max_depth++] = handle_lig_nest(no_break(cur),vlink_no_break(cur));
+ while (1) {
+ if ((fwd = vlink(cur))==null) return cur;
+ if ( type(fwd)==glyph_node) {
+ for (i=0; i<max_depth; i++) {
+ liginfo lig;
+ halfword tail = tlink(lists[i]);
+ if ( tail!=null && test_ligature(&lig,tail,fwd))
+ goto add_glyph_to_all;
+ }
+ /* if we get here, nothing had a ligature, so we stop */
+ break;
+add_glyph_to_all:
+ for (i=0; i<max_depth; i++) {
+ halfword copy = copy_node(fwd);
+ halfword tail = tlink(lists[i]);
+ nesting_append(lists[i],copy);
+ if (tail==null) continue; /* first character - never a ligature */
+ handle_lig_nest(lists[i],tail);
+ }
+ halfword next = vlink(fwd);
+ uncouple_node(fwd);
+ try_couple_nodes(cur,next);
+ flush_node(fwd);
+ } else if ( type(fwd)==disc_node) {
+ /* MAGIC WARNING
+ * A disc followed by a disc can have different kernings
+ * depending on which path is choosen, and it is impossible to
+ * store the possible kernings: fe {}{A}{W} {V}{}{A}
+ * So we _always_ add discs so only a simple path remains. */
+ int m=max_depth; /* as max_depth changes in this loop */
+ for (i=0; i<m; i++) {
+ halfword copy = copy_node(fwd);
+ halfword tail = tlink(lists[i]);
+ if (tail!=null) {
+ halfword prev = alink(tail);
+ assert(alink(tail)!=null);
+ uncouple_node(tail);
+ vlink(prev) = null;
+ if (prev==lists[i]) {
+ tlink(prev) = null;
+ } else {
+ tlink(lists[i]) = prev;
+ }
+ nesting_prepend(pre_break(copy),tail);
+ nesting_prepend(no_break(copy),copy_node(tail));
+ }
+ nesting_append(lists[i],copy);
+ handle_lig_nest(pre_break(copy),vlink_pre_break(copy));
+ assert(max_depth<256);
+ lists[max_depth++] = handle_lig_nest(no_break(copy),vlink_no_break(copy));
+ lists[i] = handle_lig_nest(post_break(copy),vlink_post_break(copy));
+ }
+ halfword next = vlink(fwd);
+ uncouple_node(fwd);
+ try_couple_nodes(cur,next);
+ flush_node(fwd);
+ } else {
+ return cur;
+ }
+ }
+ }
+ } else { /* NO GLYPH NOR DISC */
+ /* fprintf(stdout,"This is a %d node\n",type(cur));*/
+ /* assert(0);*/ /* TODO howcome there can be a glue here? */
+ return cur;
+ }
+ /* step-to-next-node */
+ {
+ halfword prev = cur;
+ cur = vlink(cur);
+ assert(cur!=null);
+ assert(alink(cur)==prev);
+ /* alink(cur) = prev;*/
+ }
+ }
+
+ return cur;
+}
+
+/* return = new tail, head should be a dummy */
+
+halfword
+handle_ligaturing(halfword head, halfword tail) {
+ halfword save_tail ; /* trick to allow explicit node==null tests */
+ halfword cur, prev;
+
+ if (vlink(head)==null)
+ return tail;
+ save_tail = vlink(tail);
+ vlink(tail) = null;
+
+ /* if (fix_node_lists) */
+ fix_node_list(head);
+
+ prev = head;
+ cur = vlink(prev);
+
+ while (cur!=null) {
+ if ( type(cur)==glyph_node ||
+ (type(cur)==whatsit_node && subtype(cur)==cancel_boundary_node)) {
+ cur = handle_lig_word(cur);
+ }
+ prev = cur;
+ cur = vlink(cur);
+ assert(cur==null||alink(cur) == prev);
+ }
+ if (valid_node(save_tail)) {
+ try_couple_nodes(prev,save_tail);
+ }
+ return prev;
+}
+
+
+/* ====================================================================
+ *
+ * K E R N I N G
+ *
+ * ==================================================================== */
+
+static void
+add_kern_before(halfword left, halfword right) {
+ if ((!is_rightghost(right)) &&
+ font(left)==font(right) &&
+ has_kern(font(left),character(left))) {
+ int k = get_kern(font(left),character(left),character(right));
+ if (k!=0) {
+ halfword kern = new_kern(k);
+ halfword prev = alink(right);
+ assert(vlink(prev)==right);
+ couple_nodes(prev,kern);
+ couple_nodes(kern,right);
+ }
+ }
+}
+
+
+static void
+add_kern_after(halfword left, halfword right, halfword aft) {
+ if ((!is_rightghost(right)) &&
+ font(left)==font(right) &&
+ has_kern(font(left),character(left))) {
+ int k = get_kern(font(left),character(left),character(right));
+ if (k!=0) {
+ halfword kern = new_kern(k);
+ halfword next = vlink(aft);
+ assert(next==null||alink(next)==aft);
+ couple_nodes(aft,kern);
+ try_couple_nodes(kern,next);
+ }
+ }
+}
+
+
+static void
+do_handle_kerning (halfword root, halfword init_left, halfword init_right) {
+ halfword cur = vlink(root);
+ halfword left = null;
+ assert(init_left==null || type(init_left)==glyph_node);
+ assert(init_right==null || type(init_right)==glyph_node);
+ if (cur==null) {
+ if (init_left!=null && init_right!=null) {
+ add_kern_after(init_left,init_right,root);
+ tlink(root)=vlink(root);
+ }
+ return;
+ }
+ if (type(cur)==glyph_node) {
+ set_is_glyph(cur);
+ if (init_left!=null)
+ add_kern_before(init_left,cur);
+ left = cur;
+ }
+ while ((cur=vlink(cur))!=null) {
+ if (type(cur)==glyph_node) {
+ set_is_glyph(cur);
+ if (left != null) {
+ add_kern_before(left,cur);
+ if (character(left)<0 || is_ghost(left)) {
+ halfword prev = alink(left);
+ couple_nodes(prev,cur);
+ flush_node(left);
+ }
+ }
+ left = cur;
+ } else {
+ if (type(cur)==disc_node) {
+ halfword right= type(vlink(cur))==glyph_node ? vlink(cur) : null;
+ do_handle_kerning(pre_break(cur),left,null);
+ do_handle_kerning(post_break(cur),null,right);
+ do_handle_kerning(no_break(cur),left,right);
+ }
+ if (left != null) {
+ if (character(left)<0 || is_ghost(left)) {
+ halfword prev = alink(left);
+ couple_nodes(prev,cur);
+ flush_node(left);
+ }
+ left = null;
+ }
+ }
+ }
+ if (left!=null) {
+ if (init_right!=null)
+ add_kern_after(left,init_right,left);
+ if (character(left)<0 || is_ghost(left)) {
+ halfword prev = alink(left);
+ halfword next = vlink(left);
+ if (next!=null) {
+ couple_nodes(prev,next);
+ tlink(root) = next;
+ assert(vlink(next)==null);
+ assert(type(next)==kern_node);
+ } else if (prev!=root) {
+ vlink(prev) = null;
+ tlink(root) = prev;
+ } else {
+ vlink(root) = null;
+ tlink(root) = null;
+ }
+ flush_node(left);
+ }
+ }
+}
+
+
+halfword
+handle_kerning (halfword head, halfword tail) {
+ halfword save_link;
+ save_link = vlink(tail);
+ vlink(tail) = null;
+ tlink(head) = tail;
+ do_handle_kerning(head,null,null);
+ tail = tlink(head);
+ if (valid_node(save_link)) {
+ try_couple_nodes(tail,save_link);
+ }
+ return tail;
+}
+
+/* ====================================================================
+ *
+ * L I G A T U R I N G and K E R N I N G : L U A - I N T E R F A C E
+ *
+ * ==================================================================== */
+
+static halfword
+run_lua_ligkern_callback (halfword head, halfword tail, int callback_id){
+ lua_State *L = Luas[0];
+ lua_rawgeti(L,LUA_REGISTRYINDEX,callback_callbacks_id);
+ lua_rawgeti(L,-1, callback_id);
+ if (!lua_isfunction(L,-1)) {
+ lua_pop(L,2);
+ return tail;
+ }
+ nodelist_to_lua(L,head);
+ nodelist_to_lua(L,tail);
+ if (lua_pcall(L,2,1,0) != 0) {
+ fprintf(stdout,"error: %s\n",lua_tostring(L,-1));
+ lua_pop(L,2);
+ lua_error(L);
+ return tail;
+ }
+ tail = nodelist_from_lua(L);
+ if (fix_node_lists)
+ fix_node_list(head);
+ lua_pop(L,2);
+ return tail;
+}
+
+
+halfword
+new_ligkern(halfword head, halfword tail) {
+ int callback_id = 0;
+
+ assert(head!=null);
+ if (vlink(head)==null)
+ return tail;
+
+ callback_id = callback_defined(ligaturing_callback);
+ if (callback_id>0) {
+ tail = run_lua_ligkern_callback(head,tail,callback_id);
+ if (tail==null) tail = tail_of_list(head);
+ } else {
+ tail = handle_ligaturing(head,tail);
+ }
+
+ callback_id = callback_defined(kerning_callback);
+ if (callback_id>0) {
+ tail = run_lua_ligkern_callback(head,tail,callback_id);
+ if (tail==null) tail = tail_of_list(head);
+ } else {
+ halfword nest = new_node(nesting_node,1);
+ halfword cur = vlink(head);
+ halfword aft = vlink(tail);
+ couple_nodes(nest,cur);
+ tlink(nest)=tail;
+ vlink(tail)=null;
+ do_handle_kerning(nest,null,null);
+ couple_nodes(head,vlink(nest));
+ tail = tlink(nest);
+ try_couple_nodes(tail,aft);
+ flush_node(nest);
+ }
+ return tail;
+}
+