diff options
30 files changed, 12087 insertions, 4804 deletions
diff --git a/Build/source/libs/luafontforge/src/luafflib.c b/Build/source/libs/luafontforge/src/luafflib.c index cf252e682ea..8c67e0b2697 100644 --- a/Build/source/libs/luafontforge/src/luafflib.c +++ b/Build/source/libs/luafontforge/src/luafflib.c @@ -255,6 +255,14 @@ static char *make_tag_string (unsigned int field) { return (char *)tag_string; } +static char featbuf[32] = {0}; + +static char *make_mactag_string (unsigned int field) { + sprintf( featbuf, "<%d,%d>", field>>16, field&0xffff ); + return (char *)featbuf; +} + + static void dump_tag (lua_State *L, char *name, unsigned int field) { lua_checkstack(L,2); @@ -263,6 +271,14 @@ dump_tag (lua_State *L, char *name, unsigned int field) { lua_rawset(L,-3); } +static void +dump_mactag (lua_State *L, char *name, unsigned int field) { + lua_checkstack(L,2); + lua_pushstring(L,name); + lua_pushstring(L,make_mactag_string(field)); + lua_rawset(L,-3); +} + #define NESTED_TABLE(a,b,c) { \ int k = 1; \ next = b; \ @@ -308,7 +324,11 @@ handle_scriptlanglist (lua_State *L, struct scriptlanglist *sll) { void do_handle_featurescriptlanglist (lua_State *L, struct featurescriptlanglist *features) { - dump_tag (L,"tag",features->featuretag); + if (features->ismac) { + dump_mactag (L,"tag",features->featuretag); + } else { + dump_tag (L,"tag",features->featuretag); + } lua_newtable(L); handle_scriptlanglist(L, features->scripts); lua_setfield(L,-2,"scripts"); diff --git a/Build/source/libs/luapeg/lpeg.c b/Build/source/libs/luapeg/lpeg.c index 2f2bbb706be..969ef9d1433 100644 --- a/Build/source/libs/luapeg/lpeg.c +++ b/Build/source/libs/luapeg/lpeg.c @@ -1,23 +1,10 @@ /* -** $Id: lpeg.c,v 1.75 2007/10/08 14:10:53 roberto Exp $ +** $Id: lpeg.c,v 1.86 2008/03/07 17:20:19 roberto Exp $ ** LPeg - PEG pattern matching for Lua -** Copyright 2007, Lua.org & PUC-Rio (see documentation for license) +** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license) ** written by Roberto Ierusalimschy */ -/* - PEG rules: - - e1 | e2 -> choice L1; e1; commit L2; L1: e2; L2: - e* -> L2: choice L1; e; commit L2; L1: - or e* -> choice L1; L2: e; partialcommit L2; L1: - e? -> choice L1; e; commit L1; L1: - !e -> choice L1; e; commit L2; L2: fail; L1: - or !e -> choice L1; e; failtwice; L1: - &e -> choice L1; choice L2; e; L2: commit L3; L3: fail; L1: - or &e -> choice L1; e; backcommit L2; L1: fail; L2: -*/ - #include <assert.h> #include <limits.h> @@ -28,6 +15,9 @@ #include "lauxlib.h" +#define VERSION "0.8" +#define PATTERN_T "pattern" + /* maximum call/backtrack levels */ #define MAXBACK 400 @@ -38,14 +28,17 @@ /* index, on Lua stack, for subject */ #define SUBJIDX 2 +/* number of fixed arguments to 'match' (before capture arguments) */ +#define FIXEDARGS 3 + /* index, on Lua stack, for substitution value cache */ -#define SUBSCACHE 3 +#define subscache(ptop) ((ptop) + 1) /* index, on Lua stack, for capture list */ -#define CAPLISTIDX (SUBSCACHE + 1) +#define caplistidx(ptop) ((ptop) + 2) /* index, on Lua stack, for pattern's fenv */ -#define PENVIDX (CAPLISTIDX + 1) +#define penvidx(ptop) ((ptop) + 3) @@ -72,8 +65,9 @@ typedef enum Opcode { IRet, IEnd, IChoice, IJmp, ICall, IOpenCall, ICommit, IPartialCommit, IBackCommit, IFailTwice, IFail, IGiveup, - IFunc, ILFunc, - IFullCapture, IEmptyCapture, IOpenCapture, ICloseCapture + IFunc, + IFullCapture, IEmptyCapture, IEmptyCaptureIdx, + IOpenCapture, ICloseCapture, ICloseRunTime } Opcode; @@ -110,11 +104,12 @@ static const byte opproperties[] = { /* IFail */ 0, /* IGiveup */ 0, /* IFunc */ 0, - /* ILFunc */ ISFENVOFF, /* IFullCapture */ ISCAPTURE | ISNOFAIL | ISFENVOFF, - /* IEmptyCapture */ ISCAPTURE | ISNOFAIL | ISMOVABLE | ISFENVOFF, + /* IEmptyCapture */ ISCAPTURE | ISNOFAIL | ISMOVABLE, + /* IEmptyCaptureIdx */ISCAPTURE | ISNOFAIL | ISMOVABLE | ISFENVOFF, /* IOpenCapture */ ISCAPTURE | ISNOFAIL | ISMOVABLE | ISFENVOFF, - /* ICloseCapture */ ISCAPTURE | ISNOFAIL | ISMOVABLE | ISFENVOFF + /* ICloseCapture */ ISCAPTURE | ISNOFAIL | ISMOVABLE | ISFENVOFF, + /* ICloseRunTime */ ISCAPTURE | ISFENVOFF }; @@ -150,8 +145,8 @@ static const Instruction giveup = {{IGiveup, 0, 0}}; /* kinds of captures */ typedef enum CapKind { - Cclose, Cposition, Cconst, Csimple, Ctable, Cfunction, - Cquery, Cstring, Csubst, Caccum + Cclose, Cposition, Cconst, Cbackref, Carg, Csimple, Ctable, Cfunction, + Cquery, Cstring, Csubst, Caccum, Cruntime } CapKind; #define iscapnosize(k) ((k) == Cposition || (k) == Cconst) @@ -210,7 +205,7 @@ static int getposition (lua_State *L, int t, int i) { res = lua_tointeger(L, -1); if (res == 0) { /* key has no registered position? */ lua_rawgeti(L, -2, i); /* get key again */ - luaL_error(L, "%s is not defined in given grammar", val2str(L, -1)); + return luaL_error(L, "%s is not defined in given grammar", val2str(L, -1)); } lua_pop(L, 2); /* remove environment and position */ return res; @@ -242,12 +237,19 @@ static void printcharset (const Charset st) { static void printcapkind (int kind) { const char *const modes[] = { - "close", "position", "constant", "simple", "table", "function", - "query", "string", "substitution", "accumulator"}; + "close", "position", "constant", "backref", + "argument", "simple", "table", "function", + "query", "string", "substitution", "accumulator", + "runtime"}; printf("%s", modes[kind]); } +static void printjmp (const Instruction *op, const Instruction *p) { + printf("-> %ld", (long)(dest(0, p) - op)); +} + + static void printinst (const Instruction *op, const Instruction *p) { const char *const names[] = { "any", "char", "set", "zset", @@ -256,10 +258,11 @@ static void printinst (const Instruction *op, const Instruction *p) { "ret", "end", "choice", "jmp", "call", "open_call", "commit", "partial_commit", "back_commit", "failtwice", "fail", "giveup", - "func", "Luafunc", - "fullcapture", "emptycapture", "opencapture", "closecapture" + "func", + "fullcapture", "emptycapture", "emptycaptureidx", "opencapture", + "closecapture", "closeruntime" }; - printf("%02d: %s ", p - op, names[p->i.code]); + printf("%02ld: %s ", (long)(p - op), names[p->i.code]); switch ((Opcode)p->i.code) { case IChar: { printf("'%c'", p->i.aux); @@ -267,7 +270,7 @@ static void printinst (const Instruction *op, const Instruction *p) { } case ITestChar: { printf("'%c'", p->i.aux); - printf("-> %d", dest(0, p) - op); + printjmp(op, p); break; } case IAny: { @@ -276,17 +279,14 @@ static void printinst (const Instruction *op, const Instruction *p) { } case ITestAny: { printf("* %d", p->i.aux); - printf("-> %d", dest(0, p) - op); + printjmp(op, p); break; } case IFullCapture: case IOpenCapture: - case IEmptyCapture: case ICloseCapture: { + case IEmptyCapture: case IEmptyCaptureIdx: + case ICloseCapture: case ICloseRunTime: { printcapkind(getkind(p)); - printf("(n = %d)", getoff(p)); - /* go through */ - } - case ILFunc: { - printf(" (%d)", p->i.offset); + printf("(n = %d) (off = %d)", getoff(p), p->i.offset); break; } case ISet: case IZSet: case ISpan: case ISpanZ: { @@ -295,7 +295,7 @@ static void printinst (const Instruction *op, const Instruction *p) { } case ITestSet: case ITestZSet: { printcharset((p+1)->buff); - printf("-> %d", dest(0, p) - op); + printjmp(op, p); break; } case IOpenCall: { @@ -303,12 +303,13 @@ static void printinst (const Instruction *op, const Instruction *p) { break; } case IChoice: { - printf("-> %d (%d)", dest(0, p) - op, p->i.aux); + printjmp(op, p); + printf(" (%d)", p->i.aux); break; } case IJmp: case ICall: case ICommit: case IPartialCommit: case IBackCommit: { - printf("-> %d", dest(0, p) - op); + printjmp(op, p); break; } default: break; @@ -328,10 +329,13 @@ static void printpatt (Instruction *p) { static void printcap (Capture *cap) { - for (; cap->s; cap++) { - printcapkind(cap->kind); - printf(" (idx: %d - size: %d) -> %p\n", cap->idx, cap->siz, cap->s); - } + printcapkind(cap->kind); + printf(" (idx: %d - size: %d) -> %p\n", cap->idx, cap->siz, cap->s); +} + + +static void printcaplist (Capture *cap) { + for (; cap->s; cap++) printcap(cap); } /* }====================================================== */ @@ -353,19 +357,40 @@ typedef struct Stack { } Stack; -static Capture *doublecap (lua_State *L, Capture *cap, int captop) { +static int runtimecap (lua_State *L, Capture *close, Capture *ocap, + const char *o, const char *s, int ptop); + + +static Capture *doublecap (lua_State *L, Capture *cap, int captop, int ptop) { Capture *newc; if (captop >= INT_MAX/((int)sizeof(Capture) * 2)) luaL_error(L, "too many captures"); newc = (Capture *)lua_newuserdata(L, captop * 2 * sizeof(Capture)); memcpy(newc, cap, captop * sizeof(Capture)); - lua_replace(L, CAPLISTIDX); + lua_replace(L, caplistidx(ptop)); return newc; } -static const char *match (lua_State *L, const char *o, const char *s, - const char *e, Instruction *op, Capture *capture) { +static void adddyncaptures (const char *s, Capture *base, int n, int fd) { + int i; + assert(base[0].kind == Cruntime && base[0].siz == 0); + base[0].idx = fd; /* first returned capture */ + for (i = 1; i < n; i++) { /* add extra captures */ + base[i].siz = 1; /* mark it as closed */ + base[i].s = s; + base[i].kind = Cruntime; + base[i].idx = fd + i; /* stack index */ + } + base[n].kind = Cclose; /* add closing entry */ + base[n].siz = 1; + base[n].s = s; +} + + +static const char *match (lua_State *L, + const char *o, const char *s, const char *e, + Instruction *op, Capture *capture, int ptop) { Stack stackbase[MAXBACK]; Stack *stacklimit = stackbase + MAXBACK; Stack *stack = stackbase; /* point to first empty slot in stack */ @@ -463,19 +488,6 @@ static const char *match (lua_State *L, const char *o, const char *s, p += p->i.offset; continue; } - case ILFunc: { - lua_Integer res; - lua_rawgeti(L, PENVIDX, p->i.offset); /* push function */ - lua_pushvalue(L, SUBJIDX); /* push original subject */ - lua_pushinteger(L, s - o + 1); /* current position */ - lua_call(L, 2, 1); - res = lua_tointeger(L, -1) - 1; - lua_pop(L, 1); - if (res < s - o || res > e - o) goto fail; - s = o + res; - p++; - continue; - } case IJmp: { p += p->i.offset; continue; @@ -515,7 +527,6 @@ static const char *match (lua_State *L, const char *o, const char *s, case IBackCommit: { assert(stack > stackbase && (stack - 1)->s != NULL); s = (--stack)->s; - captop = stack->caplevel; p += p->i.offset; continue; } @@ -533,6 +544,34 @@ static const char *match (lua_State *L, const char *o, const char *s, p = stack->p; continue; } + case ICloseRunTime: { + int fr = lua_gettop(L) + 1; /* stack index of first result */ + int ncap = runtimecap(L, capture + captop, capture, o, s, ptop); + lua_Integer res = lua_tointeger(L, fr) - 1; /* offset */ + int n = lua_gettop(L) - fr; /* number of new captures */ + if (res == -1) { /* may not be a number */ + if (!lua_toboolean(L, fr)) { /* false value? */ + lua_settop(L, fr - 1); /* remove results */ + goto fail; /* and fail */ + } + else if (lua_isboolean(L, fr)) /* true? */ + res = s - o; /* keep current position */ + } + if (res < s - o || res > e - o) + luaL_error(L, "invalid position returned by match-time capture"); + s = o + res; /* update current position */ + captop -= ncap; /* remove nested captures */ + lua_remove(L, fr); /* remove first result (offset) */ + if (n > 0) { /* captures? */ + if ((captop += n + 1) >= capsize) { + capture = doublecap(L, capture, captop, ptop); + capsize = 2 * captop; + } + adddyncaptures(s, capture + captop - n - 1, n, fr); + } + p++; + continue; + } case ICloseCapture: { const char *s1 = s - getoff(p); assert(captop > 0); @@ -547,7 +586,7 @@ static const char *match (lua_State *L, const char *o, const char *s, goto capture; } } - case IEmptyCapture: + case IEmptyCapture: case IEmptyCaptureIdx: capture[captop].siz = 1; /* mark entry as closed */ goto capture; case IOpenCapture: @@ -560,14 +599,16 @@ static const char *match (lua_State *L, const char *o, const char *s, capture[captop].idx = p->i.offset; capture[captop].kind = getkind(p); if (++captop >= capsize) { - capture = doublecap(L, capture, captop); + capture = doublecap(L, capture, captop, ptop); capsize = 2 * captop; } p++; continue; } - case IOpenCall: - luaL_error(L, "reference to unknown rule #%d", p->i.offset); + case IOpenCall: { + lua_rawgeti(L, penvidx(ptop), p->i.offset); + luaL_error(L, "reference to %s outside a grammar", val2str(L, -1)); + } default: assert(0); return NULL; } } @@ -673,22 +714,21 @@ static int verify (lua_State *L, Instruction *op, const Instruction *p, continue; } case ISpan: case ISpanZ: - case IOpenCapture: - case ICloseCapture: - case IEmptyCapture: + case IOpenCapture: case ICloseCapture: + case IEmptyCapture: case IEmptyCaptureIdx: case IFullCapture: { p += sizei(p); continue; } + case ICloseRunTime: { + goto fail; /* be liberal in this case */ + } case IFunc: { const char *r = (p+1)->f((p+2)->buff, dummy, dummy, dummy); if (r == NULL) goto fail; p += p->i.offset; continue; } - case ILFunc: { - goto fail; /* be liberal in this case */ - } case IEnd: /* cannot happen (should stop before it) */ default: assert(0); return 0; } @@ -860,7 +900,7 @@ static int isheadfail (Instruction *p) { } -#define checkpattern(L, idx) ((Instruction *)luaL_checkudata(L, idx, "pattern")) +#define checkpattern(L, idx) ((Instruction *)luaL_checkudata(L, idx, PATTERN_T)) static int jointable (lua_State *L, int p1) { @@ -923,6 +963,9 @@ static void setinstaux (Instruction *i, Opcode op, int offset, int aux) { #define setinst(i,op,off) setinstaux(i,op,off,0) +#define setinstcap(i,op,idx,k,n) setinstaux(i,op,idx,((k) | ((n) << 4))) + + static int value2fenv (lua_State *L, int vidx) { lua_createtable(L, 1, 0); lua_pushvalue(L, vidx); @@ -937,7 +980,7 @@ static Instruction *newpatt (lua_State *L, size_t n) { if (n >= MAXPATTSIZE - 1) luaL_error(L, "pattern too big"); p = (Instruction *)lua_newuserdata(L, (n + 1) * sizeof(Instruction)); - luaL_getmetatable(L, "pattern"); + luaL_getmetatable(L, PATTERN_T); lua_setmetatable(L, -2); setinst(p + n, IEnd, 0); return p; @@ -1054,18 +1097,17 @@ static int nter_l (lua_State *L) { -static void checkfield (lua_State *L) { - Instruction *p = (Instruction *)lua_touserdata(L, -1); - if (p != NULL) { /* value is a userdata? */ - if (lua_getmetatable(L, -1)) { /* does it have a metatable? */ - lua_getfield(L, LUA_REGISTRYINDEX, "pattern"); +static int testpattern (lua_State *L, int idx) { + if (lua_touserdata(L, idx)) { /* value is a userdata? */ + if (lua_getmetatable(L, idx)) { /* does it have a metatable? */ + luaL_getmetatable(L, PATTERN_T); if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ lua_pop(L, 2); /* remove both metatables */ - return; + return 1; } } } - luaL_error(L, "invalid field in grammar"); + return 0; } @@ -1085,7 +1127,8 @@ static Instruction *fix_l (lua_State *L, int t) { lua_replace(L, base + 2); /* use this value as initial rule */ continue; } - checkfield(L); + if (!testpattern(L, -1)) + luaL_error(L, "invalid field in grammar"); l = pattsize(L, -1) + 1; /* space for pattern + ret */ if (totalsize >= MAXPATTSIZE - l) luaL_error(L, "grammar too large"); @@ -1195,8 +1238,9 @@ static Instruction *getpatt (lua_State *L, int idx, int *size) { break; } case LUA_TFUNCTION: { - p = newpatt(L, 1); - setinst(p, ILFunc, value2fenv(L, idx)); + p = newpatt(L, 2); + setinstcap(p, IOpenCapture, value2fenv(L, idx), Cruntime, 0); + setinstcap(p + 1, ICloseRunTime, 0, Cclose, 0); lua_replace(L, idx); break; } @@ -1528,9 +1572,6 @@ static int getlabel (lua_State *L, int labelidx) { } -#define setinstcap(i,op,idx,k,n) setinstaux(i,op,idx,((k) | ((n) << 4))) - - static int capture_aux (lua_State *L, int kind, int labelidx) { int l1, n; Instruction *p1 = getpatt(L, 1, &l1); @@ -1576,6 +1617,38 @@ static int position_l (lua_State *L) { } +static int emptycap_aux (lua_State *L, int kind, const char *msg) { + int n = luaL_checkint(L, 1); + Instruction *p = newpatt(L, 1); + luaL_argcheck(L, 0 < n && n <= SHRT_MAX, 1, msg); + setinstcap(p, IEmptyCapture, n, kind, 0); + return 1; +} + + +static int backref_l (lua_State *L) { + return emptycap_aux(L, Cbackref, "invalid back reference"); +} + + +static int argcap_l (lua_State *L) { + return emptycap_aux(L, Carg, "invalid argument index"); +} + + +static int matchtime_l (lua_State *L) { + int l1 = getpattl(L, 1); + Instruction *op = newpatt(L, 1 + l1 + 1); + Instruction *p = op; + luaL_checktype(L, 2, LUA_TFUNCTION); + setinstcap(p++, IOpenCapture, value2fenv(L, 2), Cruntime, 0); + p += addpatt(L, p, 1); + setinstcap(p, ICloseRunTime, 0, Cclose, 0); + optimizecaptures(op); + return 1; +} + + static int capconst_l (lua_State *L) { int i, j; int n = lua_gettop(L); @@ -1583,9 +1656,9 @@ static int capconst_l (lua_State *L) { lua_createtable(L, n, 0); /* new environment for the new pattern */ for (i = j = 1; i <= n; i++) { if (lua_isnil(L, i)) - setinstcap(p++, IEmptyCapture, 0, Cconst, 0); + setinstcap(p++, IEmptyCaptureIdx, 0, Cconst, 0); else { - setinstcap(p++, IEmptyCapture, j, Cconst, 0); + setinstcap(p++, IEmptyCaptureIdx, j, Cconst, 0); lua_pushvalue(L, i); lua_rawseti(L, -2, j++); } @@ -1645,7 +1718,9 @@ static int span_l (lua_State *L) { typedef struct CapState { Capture *cap; /* current capture */ + Capture *ocap; /* (original) capture list */ lua_State *L; + int ptop; /* index of last argument to 'match' */ const char *s; /* original string */ int valuecached; /* value stored in cache slot */ } CapState; @@ -1659,7 +1734,7 @@ typedef struct CapState { #define isfullcap(cap) ((cap)->siz != 0) -#define pushluaval(cs) lua_rawgeti((cs)->L, PENVIDX, (cs)->cap->idx) +#define pushluaval(cs) lua_rawgeti((cs)->L, penvidx((cs)->ptop), (cs)->cap->idx) #define pushsubject(cs, c) lua_pushlstring((cs)->L, (c)->s, (c)->siz - 1) @@ -1668,8 +1743,8 @@ typedef struct CapState { static void updatecache_ (CapState *cs, int v) { - lua_rawgeti(cs->L, PENVIDX, v); - lua_replace(cs->L, SUBSCACHE); + lua_rawgeti(cs->L, penvidx(cs->ptop), v); + lua_replace(cs->L, subscache(cs->ptop)); cs->valuecached = v; } @@ -1677,6 +1752,33 @@ static void updatecache_ (CapState *cs, int v) { static int pushcapture (CapState *cs); +static Capture *findopen (Capture *cap) { + int n = 0; + for (;;) { + cap--; + if (isclosecap(cap)) n++; + else if (!isfullcap(cap)) + if (n-- == 0) return cap; + } +} + + +static Capture *findback (CapState *cs, Capture *cap, int n) { + int i; + for (i = 0; i < n; i++) { + if (cap == cs->ocap) + luaL_error(cs->L, "invalid back reference (%d)", n); + cap--; + if (isclosecap(cap)) + cap = findopen(cap); + else if (!isfullcap(cap)) + i--; /* does not count enclosing captures */ + } + assert(!isclosecap(cap)); + return cap; +} + + static int pushallcaptures (CapState *cs, int addextra) { Capture *co = cs->cap; int n = 0; @@ -1704,6 +1806,17 @@ static int simplecap (CapState *cs) { } +static int backrefcap (CapState *cs) { + int n = (cs->cap)->idx; + Capture *curr = cs->cap; + Capture *backref = findback(cs, curr, n); + cs->cap = backref; + n = pushcapture(cs); + cs->cap = curr + 1; + return n; +} + + static int tablecap (CapState *cs) { int n = 0; lua_newtable(cs->L); @@ -1727,7 +1840,7 @@ static int querycap (CapState *cs) { if (n > 1) /* extra captures? */ lua_pop(cs->L, n - 1); /* throw them away */ updatecache(cs, idx); - lua_gettable(cs->L, SUBSCACHE); + lua_gettable(cs->L, subscache(cs->ptop)); if (!lua_isnil(cs->L, -1)) return 1; else { @@ -1740,11 +1853,11 @@ static int querycap (CapState *cs) { static int accumcap (CapState *cs) { lua_State *L = cs->L; if (isfullcap(cs->cap++) || isclosecap(cs->cap) || pushcapture(cs) != 1) - luaL_error(L, "no initial value for accumulator capture"); + return luaL_error(L, "no initial value for accumulator capture"); while (!isclosecap(cs->cap)) { int n; if (captype(cs->cap) != Cfunction) - luaL_error(L, "invalid (non function) capture to accumulate"); + return luaL_error(L, "invalid (non function) capture to accumulate"); pushluaval(cs); lua_insert(L, -2); /* put function before previous capture */ n = pushallcaptures(cs, 0); @@ -1765,6 +1878,27 @@ static int functioncap (CapState *cs) { } +static int runtimecap (lua_State *L, Capture *close, Capture *ocap, + const char *o, const char *s, int ptop) { + CapState cs; + int n; + Capture *open = findopen(close); + assert(captype(open) == Cruntime); + close->kind = Cclose; + close->s = s; + cs.ocap = ocap; cs.cap = open; cs.L = L; + cs.s = o; cs.valuecached = 0; cs.ptop = ptop; + luaL_checkstack(L, 4, "too many runtime captures"); + pushluaval(&cs); + lua_pushvalue(L, SUBJIDX); /* push original subject */ + lua_pushinteger(L, s - o + 1); /* current position */ + n = pushallcaptures(&cs, 0); + lua_call(L, n + 2, LUA_MULTRET); + return close - open; +} + + + typedef struct StrAux { const char *s; const char *e; @@ -1778,7 +1912,8 @@ static int getstrcaps (CapState *cs, StrAux *cps, int n) { if (!isfullcap(cs->cap++)) { while (!isclosecap(cs->cap)) { if (captype(cs->cap) != Csimple) - luaL_error(cs->L, "invalid capture #%d in replacement pattern", n); + return luaL_error(cs->L, + "invalid capture #%d in replacement pattern", n); n = getstrcaps(cs, cps, n); } cs->cap++; /* skip close */ @@ -1794,7 +1929,7 @@ static void stringcap (luaL_Buffer *b, CapState *cs) { size_t len, i; const char *c; updatecache(cs, cs->cap->idx); - c = lua_tolstring(cs->L, SUBSCACHE, &len); + c = lua_tolstring(cs->L, subscache(cs->ptop), &len); n = getstrcaps(cs, cps, 0) - 1; for (i = 0; i < len; i++) { if (c[i] != '%' || c[++i] < '0' || c[i] > '9') @@ -1852,6 +1987,13 @@ static int pushcapture (CapState *cs) { cs->cap++; return 1; } + case Carg: { + int arg = (cs->cap++)->idx; + if (arg + FIXEDARGS > cs->ptop) + return luaL_error(cs->L, "reference to absent argument #%d", arg); + lua_pushvalue(cs->L, arg + FIXEDARGS); + return 1; + } case Csimple: { if (isfullcap(cs->cap)) { pushsubject(cs, cs->cap); @@ -1860,6 +2002,15 @@ static int pushcapture (CapState *cs) { } else return simplecap(cs); } + case Cruntime: { + int i = 0; + while (!isclosecap(cs->cap++)) { + luaL_checkstack(cs->L, 4, "too many unstored captures"); + lua_pushvalue(cs->L, (cs->cap - 1)->idx); + i++; + } + return i; + } case Cstring: { luaL_Buffer b; luaL_buffinit(cs->L, &b); @@ -1874,6 +2025,7 @@ static int pushcapture (CapState *cs) { substcap(cs); return 1; } + case Cbackref: return backrefcap(cs); case Ctable: return tablecap(cs); case Cfunction: return functioncap(cs); case Cquery: return querycap(cs); @@ -1883,14 +2035,18 @@ static int pushcapture (CapState *cs) { } -static int getcaptures (lua_State *L, const char *s, const char *r) { - Capture *capture = (Capture *)lua_touserdata(L, CAPLISTIDX); - CapState cs; +static int getcaptures (lua_State *L, const char *s, const char *r, int ptop) { + Capture *capture = (Capture *)lua_touserdata(L, caplistidx(ptop)); int n = 0; - cs.cap = capture; cs.L = L; cs.s = s; cs.valuecached = 0; - while (!isclosecap(cs.cap)) - n += pushcapture(&cs); - if (n == 0) { /* no captures? */ + if (!isclosecap(capture)) { /* is there any capture? */ + CapState cs; + cs.ocap = cs.cap = capture; cs.L = L; + cs.s = s; cs.valuecached = 0; cs.ptop = ptop; + do { /* collect their values */ + n += pushcapture(&cs); + } while (!isclosecap(cs.cap)); + } + if (n == 0) { /* no capture values? */ lua_pushinteger(L, r - s + 1); /* return only end position */ n = 1; } @@ -1900,6 +2056,21 @@ static int getcaptures (lua_State *L, const char *s, const char *r) { /* }====================================================== */ +static int version_l (lua_State *L) { + lua_pushstring(L, VERSION); + return 1; +} + + +static int type_l (lua_State *L) { + if (testpattern(L, 1)) + lua_pushliteral(L, "pattern"); + else + lua_pushnil(L); + return 1; +} + + static int printpat_l (lua_State *L) { Instruction *p = getpatt(L, 1, NULL); int n, i; @@ -1927,20 +2098,20 @@ static int matchl (lua_State *L) { size_t l; Instruction *p = getpatt(L, 1, NULL); const char *s = luaL_checklstring(L, SUBJIDX, &l); - lua_Integer i = luaL_optinteger(L, 3, 1); - i = (i > 0) ? - ((i <= (lua_Integer)l) ? i - 1 : (lua_Integer)l) : - (((lua_Integer)l + i >= 0) ? (lua_Integer)l + i : 0); - lua_settop(L, CAPLISTIDX - 1); - lua_pushlightuserdata(L, capture); - lua_getfenv(L, 1); - r = match(L, s, s + i, s + l, p, capture); + int ptop = lua_gettop(L); + lua_Integer ii = luaL_optinteger(L, 3, 1); + size_t i = (ii > 0) ? + (((size_t)ii <= l) ? (size_t)ii - 1 : l) : + (((size_t)-ii <= l) ? l - ((size_t)-ii) : 0); + lua_pushnil(L); /* subscache */ + lua_pushlightuserdata(L, capture); /* caplistidx */ + lua_getfenv(L, 1); /* penvidx */ + r = match(L, s, s + i, s + l, p, capture, ptop); if (r == NULL) { lua_pushnil(L); return 1; } - assert(lua_gettop(L) == PENVIDX); - return getcaptures(L, s, r); + return getcaptures(L, s, r, ptop); } @@ -1951,6 +2122,9 @@ static struct luaL_reg pattreg[] = { {"Ca", capaccum_l}, {"Cc", capconst_l}, {"Cp", position_l}, + {"Cb", backref_l}, + {"Carg", argcap_l}, + {"Cmt", matchtime_l}, {"Cs", capsubst_l}, {"Ct", tcapture_l}, {"P", pattern_l}, @@ -1958,6 +2132,8 @@ static struct luaL_reg pattreg[] = { {"S", set_l}, {"V", nter_l}, {"span", span_l}, + {"type", type_l}, + {"version", version_l}, {NULL, NULL} }; @@ -1978,7 +2154,7 @@ int luaopen_lpeg (lua_State *L); int luaopen_lpeg (lua_State *L) { lua_newtable(L); lua_replace(L, LUA_ENVIRONINDEX); /* empty env for new patterns */ - luaL_newmetatable(L, "pattern"); + luaL_newmetatable(L, PATTERN_T); luaL_register(L, NULL, metapattreg); luaL_register(L, "lpeg", pattreg); lua_pushliteral(L, "__index"); diff --git a/Build/source/texk/web2c/luatexdir/font/vfovf.c b/Build/source/texk/web2c/luatexdir/font/vfovf.c index 0f23cb07b94..61a0b76aaa4 100644 --- a/Build/source/texk/web2c/luatexdir/font/vfovf.c +++ b/Build/source/texk/web2c/luatexdir/font/vfovf.c @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with LuaTeX; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -$Id: vfovf.c 1022 2008-02-14 11:49:30Z taco $ +$Id: vfovf.c 1169 2008-04-15 14:57:56Z oneiros $ */ #include "ptexlib.h" @@ -167,7 +167,7 @@ pdf_check_vf_cur_val (void) { internal_font_number f; f = cur_val; if (font_type(f) == virtual_font_type) - pdf_error("font", "command cannot be used with virtual font"); + pdf_error(maketexstring("font"), maketexstring("command cannot be used with virtual font")); } static void diff --git a/Build/source/texk/web2c/luatexdir/font/vfpacket.c b/Build/source/texk/web2c/luatexdir/font/vfpacket.c index 4d8445f2227..3ce10dd10ce 100644 --- a/Build/source/texk/web2c/luatexdir/font/vfpacket.c +++ b/Build/source/texk/web2c/luatexdir/font/vfpacket.c @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with pdfTeX; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -$Id: vfpacket.c 1013 2008-02-14 00:09:02Z oneiros $ +$Id: vfpacket.c 1169 2008-04-15 14:57:56Z oneiros $ */ #include "ptexlib.h" @@ -102,7 +102,7 @@ vf_packet_bytes (charinfo *co) { case packet_nop_code: break; default: - pdf_error("vf", "invalid DVI command"); + pdf_error(maketexstring("vf"), maketexstring("invalid DVI command")); } }; return (cur_packet_byte +1); @@ -211,7 +211,7 @@ do_vf_packet (internal_font_number vf_f, integer c) { case packet_nop_code: break; default: - pdf_error("vf", "invalid DVI command"); + pdf_error(maketexstring("vf"), maketexstring("invalid DVI command")); } }; cur_h = save_cur_h; @@ -268,7 +268,7 @@ integer *packet_local_fonts(internal_font_number f, integer *num) { (void)do_packet_byte(); break; default: - pdf_error("vf", "invalid DVI command"); + pdf_error(maketexstring("vf"), maketexstring("invalid DVI command")); } } } @@ -334,7 +334,7 @@ replace_packet_fonts(internal_font_number f, integer *old_fontid, integer *new_f (void)do_packet_byte(); break; default: - pdf_error("vf", "invalid DVI command"); + pdf_error(maketexstring("vf"), maketexstring("invalid DVI command")); } } } diff --git a/Build/source/texk/web2c/luatexdir/font/writefont.c b/Build/source/texk/web2c/luatexdir/font/writefont.c index 05257746889..679929e32e4 100644 --- a/Build/source/texk/web2c/luatexdir/font/writefont.c +++ b/Build/source/texk/web2c/luatexdir/font/writefont.c @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with pdfTeX; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -$Id: writefont.c 1112 2008-03-20 14:46:32Z taco $ +$Id: writefont.c 1139 2008-04-09 14:44:20Z taco $ */ #include "ptexlib.h" @@ -596,6 +596,7 @@ void create_fontdictionary(fm_entry * fm, integer font_objnum, { fo_entry *fo = new_fo_entry(); get_char_range(fo, f); /* set fo->first_char and fo->last_char from f */ + if (fo->last_char>255) fo->last_char=255; /* added 9-4-2008, mantis #25 */ assert(fo->last_char >= fo->first_char); fo->fm = fm; fo->fo_objnum = font_objnum; diff --git a/Build/source/texk/web2c/luatexdir/image/epdf.h b/Build/source/texk/web2c/luatexdir/image/epdf.h index 2359d53d3db..93a9e1c45eb 100644 --- a/Build/source/texk/web2c/luatexdir/image/epdf.h +++ b/Build/source/texk/web2c/luatexdir/image/epdf.h @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with pdfTeX; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -$Id: epdf.h 1031 2008-02-14 23:03:20Z hhenkel $ +$Id: epdf.h 1125 2008-03-25 19:36:52Z hhenkel $ */ extern "C" { @@ -45,6 +45,7 @@ extern "C" { #include <web2c/luatexdir/ptexmac.h> #include "openbsd-compat.h" #include "image.h" +#include "../avlstuff.h" /* #-define pdfbufsize pdfbufmax */ @@ -78,7 +79,6 @@ extern "C" { extern void read_pdf_info(image_dict *, integer, integer); extern void embed_whole_font(struct fd_entry *); extern void epdf_check_mem(void); - extern void epdf_delete(image_dict *); extern void epdf_free(void); __attribute__ ((format(printf, 1, 2))) extern void pdf_printf(const char *fmt, ...); @@ -109,9 +109,6 @@ extern "C" { /* write_enc.c */ extern void epdf_write_enc(char **, integer); -/* write_enc.c */ - extern pdf_img_struct *new_pdf_img_struct(void); - /* utils.c */ extern char *convertStringToPDFString(char *in, int len); extern char *stripzeros(char *a); diff --git a/Build/source/texk/web2c/luatexdir/image/image.h b/Build/source/texk/web2c/luatexdir/image/image.h index cf7d4f0dd88..e239e617f37 100644 --- a/Build/source/texk/web2c/luatexdir/image/image.h +++ b/Build/source/texk/web2c/luatexdir/image/image.h @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with pdfTeX; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -$Id: image.h 1100 2008-03-09 13:40:48Z hhenkel $ +$Id: image.h 1125 2008-03-25 19:36:52Z hhenkel $ */ #ifndef IMAGE_H @@ -43,14 +43,6 @@ extern integer zround(double); /* from zround.c */ /**********************************************************************/ typedef struct { - float width; - float height; - float orig_x; - float orig_y; - void *doc; -} pdf_img_struct; - -typedef struct { png_structp png_ptr; png_infop info_ptr; } png_img_struct; @@ -89,6 +81,8 @@ typedef struct { integer index; /* /Im1, /Im2, ... */ integer x_size; /* dimensions in pixel counts as in JPG/PNG/JBIG2 file */ integer y_size; + integer x_orig; /* origin in sp for PDF files */ + integer y_orig; integer x_res; /* pixel resolution as in JPG/PNG/JBIG2 file */ integer y_res; integer colorspace; /* number of /ColorSpace object */ @@ -105,7 +99,6 @@ typedef struct { pdfboxspec_e page_box_spec; /* PDF page box spec.: media/crop/bleed/trim/art */ dict_state state; union { - pdf_img_struct *pdf; png_img_struct *png; jpg_img_struct *jpg; jb2_img_struct *jb2; @@ -116,6 +109,8 @@ typedef struct { # define img_index(N) ((N)->index) # define img_xsize(N) ((N)->x_size) # define img_ysize(N) ((N)->y_size) +# define img_xorig(N) ((N)->x_orig) +# define img_yorig(N) ((N)->y_orig) # define img_xres(N) ((N)->x_res) # define img_yres(N) ((N)->y_res) # define img_colorspace(N) ((N)->colorspace) @@ -132,13 +127,6 @@ typedef struct { # define img_pagebox(N) ((N)->page_box_spec) # define img_state(N) ((N)->state) -# define img_pdf_ptr(N) ((N)->img_struct.pdf) -# define img_pdf_width(N) ((N)->img_struct.pdf->width) -# define img_pdf_height(N) ((N)->img_struct.pdf->height) -# define img_pdf_orig_x(N) ((N)->img_struct.pdf->orig_x) -# define img_pdf_orig_y(N) ((N)->img_struct.pdf->orig_y) -# define img_pdf_doc(N) ((N)->img_struct.pdf->doc) - # define img_png_ptr(N) ((N)->img_struct.png) # define img_png_png_ptr(N) ((N)->img_struct.png->png_ptr) # define img_png_info_ptr(N) ((N)->img_struct.png->info_ptr) @@ -218,6 +206,6 @@ void write_epdf(image_dict *); /* pdftoepdf.cc */ -void epdf_delete(image_dict * idict); +void unrefPdfDocument(char *); #endif diff --git a/Build/source/texk/web2c/luatexdir/image/pdftoepdf.cc b/Build/source/texk/web2c/luatexdir/image/pdftoepdf.cc index c5270eb58bd..21ded8e0b0f 100644 --- a/Build/source/texk/web2c/luatexdir/image/pdftoepdf.cc +++ b/Build/source/texk/web2c/luatexdir/image/pdftoepdf.cc @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with pdfTeX; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -$Id: pdftoepdf.cc 1058 2008-02-19 21:13:58Z hhenkel $ +$Id: pdftoepdf.cc 1129 2008-03-27 19:43:44Z hhenkel $ */ #include <stdlib.h> @@ -79,7 +79,8 @@ $Id: pdftoepdf.cc 1058 2008-02-19 21:13:58Z hhenkel $ class PdfObject { public: PdfObject() { // nothing - } ~PdfObject() { + } + ~PdfObject() { iObject.free(); } Object *operator->() { @@ -101,20 +102,16 @@ class PdfObject { // appended into a linked list. Duplicates are checked and removed from the // list of indirect objects during appending. -enum InObjType { - objFont, - objFontDesc, - objOther -}; +enum InObjType { objFont, objFontDesc, objOther }; struct InObj { Ref ref; // ref in original PDF InObjType type; // object type - InObj *next; // next entry in list of indirect objects integer num; // new object number in output PDF fd_entry *fd; // pointer to /FontDescriptor object structure integer enc_objnum; // Encoding for objFont int written; // has it been written to output PDF? + InObj *next; // next entry in list of indirect objects }; struct UsedEncoding { @@ -123,89 +120,104 @@ struct UsedEncoding { UsedEncoding *next; }; -static InObj *inObjList; +static XRef *xref = NULL; +static InObj *inObjList = NULL; static UsedEncoding *encodingList; static GBool isInit = gFalse; -// -------------------------------------------------------------------- -// Maintain list of open embedded PDF files -// -------------------------------------------------------------------- +//********************************************************************** +// Maintain AVL tree of open embedded PDF files + +static avl_table *PdfDocumentTree = NULL; struct PdfDocument { - char *file_name; + char *file_path; // full file name including path PDFDoc *doc; XRef *xref; InObj *inObjList; - int occurences; // number of references to the document; the doc can be - // deleted when this is negative - PdfDocument *next; + int occurences; // number of references to the PdfDocument; it can be deleted when occurences == 0 }; -static PdfDocument *pdfDocuments = 0; +/* AVL sort PdfDocument into PdfDocumentTree by file_path */ -static XRef *xref = 0; +static int CompPdfDocument(const void *pa, const void *pb, void *p) +{ + return strcmp(((const PdfDocument *) pa)->file_path, + ((const PdfDocument *) pb)->file_path); +} -// Returns pointer to PdfDocument record for PDF file. -// Creates a new record if it doesn't exist yet. -// xref is made current for the document. +// Returns pointer to PdfDocument structure for PDF file. -static PdfDocument *find_add_document(char *file_name) +static PdfDocument *findPdfDocument(char *file_path) { - PdfDocument *p = pdfDocuments; - while (p && strcmp(p->file_name, file_name) != 0) - p = p->next; - if (p) { - xref = p->xref; - (p->occurences)++; + PdfDocument *pdf_doc, tmp; + assert(file_path != NULL); + if (PdfDocumentTree == NULL) + PdfDocumentTree = avl_create(CompPdfDocument, NULL, &avl_xallocator); + assert(PdfDocumentTree != NULL); + tmp.file_path = file_path; + pdf_doc = (PdfDocument *) avl_find(PdfDocumentTree, &tmp); + return pdf_doc; +} + +// Returns pointer to PdfDocument structure for PDF file. +// Creates a new structure if it doesn't exist yet. + +static PdfDocument *refPdfDocument(char *file_path) +{ + PdfDocument *pdf_doc = findPdfDocument(file_path); + if (pdf_doc == NULL) { + pdf_doc = new PdfDocument; + pdf_doc->file_path = xstrdup(file_path); + void **aa = avl_probe(PdfDocumentTree, pdf_doc); + assert(aa != NULL); + GString *docName = new GString(pdf_doc->file_path); + pdf_doc->doc = new PDFDoc(docName); // takes ownership of docName + if (!pdf_doc->doc->isOk() || !pdf_doc->doc->okToPrint()) + pdftex_fail("xpdf: reading PDF image failed"); + pdf_doc->xref = NULL; + pdf_doc->inObjList = NULL; + pdf_doc->occurences = 0; // 0 = unreferenced #ifdef DEBUG - fprintf(stderr, "\npdfTeX Debug: Incrementing %s (%d)\n", p->file_name, - p->occurences); + fprintf(stderr, "\nluatex Debug: Creating %s (%d)\n", + pdf_doc->file_path, pdf_doc->occurences); #endif - return p; } - p = new PdfDocument; - p->file_name = xstrdup(file_name); - p->xref = xref = 0; - p->occurences = 0; + pdf_doc->occurences++; #ifdef DEBUG - fprintf(stderr, "\npdfTeX Debug: Creating %s (%d)\n", p->file_name, - p->occurences); + fprintf(stderr, "\nluatex Debug: Incrementing %s (%d)\n", + pdf_doc->file_path, pdf_doc->occurences); #endif - GString *docName = new GString(p->file_name); - p->doc = new PDFDoc(docName); // takes ownership of docName - if (!p->doc->isOk() || !p->doc->okToPrint()) { - pdftex_fail("xpdf: reading PDF image failed"); - } - p->inObjList = 0; - p->next = pdfDocuments; - pdfDocuments = p; - return p; + return pdf_doc; } -// Deallocate a PdfDocument with all its resources +static void deletePdfDocument(PdfDocument *); -static void delete_document(PdfDocument * pdf_doc) +// Called when an image has been written and its resources in image_tab are +// freed and it's not referenced anymore. + +void unrefPdfDocument(char *file_path) { - PdfDocument **p = &pdfDocuments; - while (*p && *p != pdf_doc) - p = &((*p)->next); - // should not happen: - if (!*p) - return; - // unlink from list - *p = pdf_doc->next; - // free pdf_doc's resources - InObj *r, *n; - for (r = pdf_doc->inObjList; r != 0; r = n) { - n = r->next; - delete r; + PdfDocument *pdf_doc = findPdfDocument(file_path); + assert(pdf_doc != NULL); + assert(pdf_doc->occurences > 0); // aim for point landing + pdf_doc->occurences--; +#ifdef DEBUG + fprintf(stderr, "\nluatex Debug: Decrementing %s (%d)\n", + pdf_doc->file_path, pdf_doc->occurences); +#endif + if (pdf_doc->occurences == 0) { +#ifdef DEBUG + fprintf(stderr, "\nluatex Debug: Deleting %s\n", pdf_doc->file_path); +#endif + void *a = avl_delete(PdfDocumentTree, pdf_doc); + assert((PdfDocument *) a == pdf_doc); + deletePdfDocument(pdf_doc); } - xref = pdf_doc->xref; - delete pdf_doc->doc; - xfree(pdf_doc->file_name); - delete pdf_doc; } +//********************************************************************** + // Replacement for // Object *initDict(Dict *dict1){ initObj(objDict); dict = dict1; return this; } @@ -218,8 +230,6 @@ static void initDictFromDict(PdfObject & obj, Dict * dict) } } -// -------------------------------------------------------------------- - static int addEncoding(GfxFont * gfont) { UsedEncoding *n; @@ -241,7 +251,7 @@ static int addEncoding(GfxFont * gfont) addInObj(objFontDesc, ref, fd, 0) #define addOther(ref) \ - addInObj(objOther, ref, 0, 0) + addInObj(objOther, ref, NULL, 0) static int addInObj(InObjType type, Ref ref, fd_entry * fd, integer e) { @@ -250,14 +260,14 @@ static int addInObj(InObjType type, Ref ref, fd_entry * fd, integer e) pdftex_fail("PDF inclusion: invalid reference"); n->ref = ref; n->type = type; - n->next = 0; + n->next = NULL; n->fd = fd; n->enc_objnum = e; n->written = 0; - if (inObjList == 0) + if (inObjList == NULL) inObjList = n; else { - for (p = inObjList; p != 0; p = p->next) { + for (p = inObjList; p != NULL; p = p->next) { if (p->ref.num == ref.num && p->ref.gen == ref.gen) { delete n; return p->num; @@ -266,7 +276,7 @@ static int addInObj(InObjType type, Ref ref, fd_entry * fd, integer e) } // it is important to add new objects at the end of the list, // because new objects are being added while the list is being - // written out. + // written out by writeRefs(). q->next = n; } if (type == objFontDesc) @@ -276,8 +286,6 @@ static int addInObj(InObjType type, Ref ref, fd_entry * fd, integer e) return n->num; } -static void copyObject(Object *); - static void copyName(char *s) { pdf_puts("/"); @@ -290,6 +298,8 @@ static void copyName(char *s) } } +static void copyObject(Object *); + static void copyDictEntry(Object * obj, int i) { PdfObject obj1; @@ -502,7 +512,7 @@ static char *convertNumToPDF(double n) fval /= 10; } } else - buf[bindex++] = 0; + buf[bindex++] = '\0'; } return (char *) buf; } @@ -591,7 +601,7 @@ static void copyObject(Object * obj) static void writeRefs() { InObj *r; - for (r = inObjList; r != 0; r = r->next) { + for (r = inObjList; r != NULL; r = r->next) { if (!r->written) { Object obj1; r->written = 1; @@ -621,20 +631,20 @@ static void writeEncodings() UsedEncoding *r, *n; char *glyphNames[256], *s; int i; - for (r = encodingList; r != 0; r = r->next) { + for (r = encodingList; r != NULL; r = r->next) { for (i = 0; i < 256; i++) { if (r->font->isCIDFont()) { pdftex_warn ("PDF inclusion: CID font included, encoding maybe wrong"); } - if ((s = ((Gfx8BitFont *) r->font)->getCharName(i)) != 0) + if ((s = ((Gfx8BitFont *) r->font)->getCharName(i)) != NULL) glyphNames[i] = s; else glyphNames[i] = notdef; } epdf_write_enc(glyphNames, r->enc_objnum); } - for (r = encodingList; r != 0; r = n) { + for (r = encodingList; r != NULL; r = n) { n = r->next; delete r->font; delete r; @@ -661,7 +671,6 @@ static PDFRectangle *get_pagebox(Page * page, integer pagebox_spec) return page->getMediaBox(); // to make the compiler happy } - // Reads various information about the PDF and sets it up for later inclusion. // This will fail if the PDF version of the PDF is higher than // minor_pdf_version_wanted or page_name is given and can not be found. @@ -676,28 +685,25 @@ read_pdf_info(image_dict * idict, integer minor_pdf_version_wanted, Page *page; int rotate; PDFRectangle *pagebox; - float pdf_version_found, pdf_version_wanted; + float pdf_version_found, pdf_version_wanted, pdf_width, pdf_height, + pdf_xorig, pdf_yorig; assert(idict != NULL); assert(img_type(idict) == IMAGE_TYPE_PDF); - assert(img_pdf_ptr(idict) == NULL); - img_pdf_ptr(idict) = new_pdf_img_struct(); // initialize - if (!isInit) { + if (isInit == gFalse) { globalParams = new GlobalParams(); globalParams->setErrQuiet(gFalse); isInit = gTrue; } // open PDF file - pdf_doc = find_add_document(img_filepath(idict)); - img_pdf_doc(idict) = pdf_doc; - + pdf_doc = refPdfDocument(img_filepath(idict)); // check PDF version // this works only for PDF 1.x -- but since any versions of PDF newer // than 1.x will not be backwards compatible to PDF 1.x, pdfTeX will // then have to changed drastically anyway. pdf_version_found = pdf_doc->doc->getPDFVersion(); pdf_version_wanted = 1 + (minor_pdf_version_wanted * 0.1); - if (pdf_version_found > pdf_version_wanted) { + if (pdf_version_found > pdf_version_wanted + 0.01) { char msg[] = "PDF inclusion: found PDF version <%.1f>, but at most version <%.1f> allowed"; if (pdf_inclusion_errorlevel > 0) { @@ -711,7 +717,7 @@ read_pdf_info(image_dict * idict, integer minor_pdf_version_wanted, // get page by name GString name(img_pagename(idict)); LinkDest *link = pdf_doc->doc->findDest(&name); - if (link == 0 || !link->isOk()) + if (link == NULL || !link->isOk()) pdftex_fail("PDF inclusion: invalid destination <%s>", img_pagename(idict)); Ref ref = link->getPageRef(); @@ -734,53 +740,50 @@ read_pdf_info(image_dict * idict, integer minor_pdf_version_wanted, // get the pagebox (media, crop...) to use. pagebox = get_pagebox(page, img_pagebox(idict)); if (pagebox->x2 > pagebox->x1) { - img_pdf_orig_x(idict) = pagebox->x1; - img_pdf_width(idict) = pagebox->x2 - pagebox->x1; + pdf_xorig = pagebox->x1; + pdf_width = pagebox->x2 - pagebox->x1; } else { - img_pdf_orig_x(idict) = pagebox->x2; - img_pdf_width(idict) = pagebox->x1 - pagebox->x2; + pdf_xorig = pagebox->x2; + pdf_width = pagebox->x1 - pagebox->x2; } if (pagebox->y2 > pagebox->y1) { - img_pdf_orig_y(idict) = pagebox->y1; - img_pdf_height(idict) = pagebox->y2 - pagebox->y1; + pdf_yorig = pagebox->y1; + pdf_height = pagebox->y2 - pagebox->y1; } else { - img_pdf_orig_y(idict) = pagebox->y2; - img_pdf_height(idict) = pagebox->y1 - pagebox->y2; + pdf_yorig = pagebox->y2; + pdf_height = pagebox->y1 - pagebox->y2; } rotate = page->getRotate(); // handle page rotation and adjust dimens as needed if (rotate != 0) { - if (rotate % 90 == 0) { - // handle only the simple case: multiple of 90s. - // these are the only values allowed according to the - // reference (v1.3, p. 78). - // 180 needs no special treatment here - register float f; - switch (rotate) { - case 90: - f = img_pdf_height(idict); - img_pdf_height(idict) = img_pdf_width(idict); - img_pdf_width(idict) = f; - break; - case 270: - f = img_pdf_height(idict); - img_pdf_height(idict) = img_pdf_width(idict); - img_pdf_width(idict) = f; - break; - } + // handle only the simple case: multiple of 90s. + // these are the only values allowed according to the + // reference (v1.3, p. 78). + // 180 needs no special treatment here + register float f; + switch (rotate % 360) { + case 90: + case 270: + f = pdf_height; + pdf_height = pdf_width; + pdf_width = f; + break; + default:; } } pdf_doc->xref = pdf_doc->doc->getXRef(); - img_xsize(idict) = bp2int(img_pdf_width(idict)); - img_ysize(idict) = bp2int(img_pdf_height(idict)); + img_xsize(idict) = bp2int(pdf_width); + img_ysize(idict) = bp2int(pdf_height); + img_xorig(idict) = bp2int(pdf_xorig); + img_yorig(idict) = bp2int(pdf_yorig); } -// writes the current epf_doc. +// Writes the current epf_doc. // Here the included PDF is copied, so most errors that can happen during PDF // inclusion will arise here. -void write_epdf(image_dict * idict) +static void write_epdf1(image_dict * idict) { Page *page; PdfObject contents, obj1, obj2; @@ -792,15 +795,11 @@ void write_epdf(image_dict * idict) int rotate; double scale[6] = { 0, 0, 0, 0, 0, 0 }; bool writematrix = false; - PdfDocument *pdf_doc = (PdfDocument *) img_pdf_doc(idict); - (pdf_doc->occurences)--; -#ifdef DEBUG - fprintf(stderr, "\npdfTeX Debug: Decrementing %s (%d)\n", - pdf_doc->file_name, pdf_doc->occurences); -#endif + PdfDocument *pdf_doc = (PdfDocument *) findPdfDocument(img_filepath(idict)); + assert(pdf_doc != NULL); xref = pdf_doc->xref; inObjList = pdf_doc->inObjList; - encodingList = 0; + encodingList = NULL; page = pdf_doc->doc->getCatalog()->getPage(img_pagenum(idict)); rotate = page->getRotate(); PDFRectangle *pagebox; @@ -812,8 +811,8 @@ void write_epdf(image_dict * idict) // write additional information pdf_printf("/%s.FileName (%s)\n", pdfkeyprefix, - convertStringToPDFString(pdf_doc->file_name, - strlen(pdf_doc->file_name))); + convertStringToPDFString(pdf_doc->file_path, + strlen(pdf_doc->file_path))); pdf_printf("/%s.PageNumber %i\n", pdfkeyprefix, (int) img_pagenum(idict)); pdf_doc->doc->getDocInfoNF(&info); if (info.isRef()) { @@ -826,42 +825,40 @@ void write_epdf(image_dict * idict) // handle page rotation if (rotate != 0) { - if (rotate % 90 == 0) { - // this handles only the simple case: multiple of 90s but these - // are the only values allowed according to the reference - // (v1.3, p. 78). - // the image is rotated around its center. - // the /Rotate key is clockwise while the matrix is - // counterclockwise :-% - tex_printf(", page is rotated %d degrees", rotate); - switch (rotate) { - case 90: - scale[1] = -1; - scale[2] = 1; - scale[4] = pagebox->x1 - pagebox->y1; - scale[5] = pagebox->y1 + pagebox->x2; - writematrix = true; - break; - case 180: - scale[0] = scale[3] = -1; - scale[4] = pagebox->x1 + pagebox->x2; - scale[5] = pagebox->y1 + pagebox->y2; - writematrix = true; - break; // width and height are exchanged - case 270: - scale[1] = 1; - scale[2] = -1; - scale[4] = pagebox->x1 + pagebox->y2; - scale[5] = pagebox->y1 - pagebox->x1; - writematrix = true; - break; - } - if (writematrix) { // The matrix is only written if the image is rotated. - sprintf(s, "/Matrix [%.8f %.8f %.8f %.8f %.8f %.8f]\n", - scale[0], - scale[1], scale[2], scale[3], scale[4], scale[5]); - pdf_printf(stripzeros(s)); - } + // this handles only the simple case: multiple of 90s but these + // are the only values allowed according to the reference + // (v1.3, p. 78). + // the image is rotated around its center. + // the /Rotate key is clockwise while the matrix is + // counterclockwise :-% + tex_printf(", page is rotated %d degrees", rotate); + switch (rotate % 360) { + case 90: + scale[1] = -1; + scale[2] = 1; + scale[4] = pagebox->x1 - pagebox->y1; + scale[5] = pagebox->y1 + pagebox->x2; + writematrix = true; + break; + case 180: + scale[0] = scale[3] = -1; + scale[4] = pagebox->x1 + pagebox->x2; + scale[5] = pagebox->y1 + pagebox->y2; + writematrix = true; + break; // width and height are exchanged + case 270: + scale[1] = 1; + scale[2] = -1; + scale[4] = pagebox->x1 + pagebox->y2; + scale[5] = pagebox->y1 - pagebox->x1; + writematrix = true; + break; + default:; + } + if (writematrix) { // The matrix is only written if the image is rotated. + sprintf(s, "/Matrix [%.8f %.8f %.8f %.8f %.8f %.8f]\n", + scale[0], scale[1], scale[2], scale[3], scale[4], scale[5]); + pdf_printf(stripzeros(s)); } } @@ -883,12 +880,12 @@ void write_epdf(image_dict * idict) # else // FIXME: currently we don't know how to handle Page Groups so we abort gracefully :-( pdftex_fail - ("PDF inclusion: Page Group detected which pdfTeX can't handle. Sorry."); + ("PDF inclusion: Page Group detected which luatex can't handle. Sorry."); # endif #else // FIXME: currently we don't know how to handle Page Groups so we at least give a warning :-( pdftex_warn - ("PDF inclusion: Page Group detected which pdfTeX can't handle. Ignoring it."); + ("PDF inclusion: Page Group detected which luatex can't handle. Ignoring it."); #endif } // write the page Metadata if it's there @@ -970,25 +967,39 @@ void write_epdf(image_dict * idict) writeRefs(); // write out all used encodings (and delete list) writeEncodings(); - // save object list, xref + // save object list pdf_doc->inObjList = inObjList; - pdf_doc->xref = xref; + assert(xref == pdf_doc->xref); // xref should be unchanged } -// Called when an image has been written and it's resources in image_tab are -// freed and it's not referenced anymore. +void write_epdf(image_dict * idict) +{ + assert(idict != NULL); + write_epdf1(idict); + unrefPdfDocument(img_filepath(idict)); +} -void epdf_delete(image_dict * idict) +//********************************************************************** +// Deallocate a PdfDocument with all its resources + +static void deletePdfDocument(PdfDocument * pdf_doc) { - PdfDocument *pdf_doc = (PdfDocument *) img_pdf_doc(idict); - xref = pdf_doc->xref; - if (pdf_doc->occurences < 0) { -#ifdef DEBUG - fprintf(stderr, "\npdfTeX Debug: Deleting %s\n", pdf_doc->file_name); -#endif - delete_document(pdf_doc); + assert(pdf_doc != NULL); + // free PdfDocument's resources + InObj *r, *n; + for (r = pdf_doc->inObjList; r != NULL; r = n) { + n = r->next; + delete r; } - img_pdf_doc(idict) = NULL; + delete pdf_doc->doc; + xfree(pdf_doc->file_path); + delete pdf_doc; +} + +static void destroyPdfDocument(void *pa, void *pb) +{ + PdfDocument *pdf_doc = (PdfDocument *) pa; + deletePdfDocument(pdf_doc); } // Called when PDF embedding system is finalized. @@ -996,13 +1007,10 @@ void epdf_delete(image_dict * idict) void epdf_check_mem() { - if (isInit) { - PdfDocument *p, *n; - for (p = pdfDocuments; p; p = n) { - n = p->next; - delete_document(p); - } - // see above for globalParams + if (PdfDocumentTree != NULL) + avl_destroy(PdfDocumentTree, destroyPdfDocument); + PdfDocumentTree = NULL; + if (isInit == gTrue) delete globalParams; - } + isInit = gFalse; } diff --git a/Build/source/texk/web2c/luatexdir/image/writeimg.c b/Build/source/texk/web2c/luatexdir/image/writeimg.c index 66eb459bc0c..c49ef9a28d9 100644 --- a/Build/source/texk/web2c/luatexdir/image/writeimg.c +++ b/Build/source/texk/web2c/luatexdir/image/writeimg.c @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with pdfTeX; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -$Id: writeimg.c 1119 2008-03-22 11:27:02Z hhenkel $ +$Id: writeimg.c 1152 2008-04-13 22:29:59Z hhenkel $ */ #include <assert.h> @@ -168,18 +168,6 @@ static void check_type_by_extension(image_dict * idict) /**********************************************************************/ -pdf_img_struct *new_pdf_img_struct() -{ - pdf_img_struct *p; - p = xtalloc(1, pdf_img_struct); - p->width = 0; - p->height = 0; - p->orig_x = 0; - p->orig_y = 0; - p->doc = NULL; - return p; -} - void init_image(image * p) { assert(p != NULL); @@ -208,6 +196,8 @@ void init_image_dict(image_dict * p) img_index(p) = 0; img_xsize(p) = 0; img_ysize(p) = 0; + img_xorig(p) = 0; + img_yorig(p) = 0; img_xres(p) = 0; img_yres(p) = 0; img_colorspace(p) = 0; @@ -223,7 +213,7 @@ void init_image_dict(image_dict * p) img_colordepth(p) = 0; img_pagebox(p) = PDF_BOX_SPEC_MEDIA; img_state(p) = DICT_NEW; - img_pdf_ptr(p) = NULL; /* union */ + img_png_ptr(p) = NULL; /* union */ } image_dict *new_image_dict() @@ -250,10 +240,26 @@ void free_dict_strings(image_dict * p) } void free_image_dict(image_dict * p) -{ +{ /* called from limglib.c */ + assert(img_state(p) < DICT_REFERED); + switch (img_type(p)) { + case IMAGE_TYPE_PDF: + unrefPdfDocument(img_filepath(p)); + break; + case IMAGE_TYPE_PNG: /* assuming IMG_CLOSEINBETWEEN */ + assert(img_png_ptr(p) == NULL); + break; + case IMAGE_TYPE_JPG: /* assuming IMG_CLOSEINBETWEEN */ + assert(img_jpg_ptr(p) == NULL); + break; + case IMAGE_TYPE_JBIG2: /* todo: writejbig2.c cleanup */ + break; + case IMAGE_TYPE_NONE: + break; + default: + assert(0); + } free_dict_strings(p); - if (img_type(p) == IMAGE_TYPE_PDF) - epdf_delete(p); assert(img_file(p) == NULL); xfree(p); } @@ -405,7 +411,8 @@ void scale_img(image * img) void out_img(image * img, scaled hpos, scaled vpos) { - float a[7]; /* transformation matrix (todo: indices should be reduced!) */ + float a[6]; /* transformation matrix */ + float xoff, yoff, tmp; int r; /* number of digits after the decimal point */ assert(img != 0); image_dict *idict = img_dict(img); @@ -413,92 +420,94 @@ void out_img(image * img, scaled hpos, scaled vpos) scaled wd = img_width(img); scaled ht = img_height(img); scaled dp = img_depth(img); + if ((img_transform(img) & 1) == 1) { /* 90 deg. or 270 deg. rotated */ + if (ht == -dp) + pdftex_fail("image transform: division by zero (height == -depth)"); + if (wd == 0) + pdftex_fail("image transform: division by zero (width == 0)"); + } + a[1] = a[2] = 0; if (img_type(idict) == IMAGE_TYPE_PDF) { - a[1] = wd * 1.0e6 / img_xsize(idict); - a[2] = 0; - a[3] = 0; - a[4] = (ht + dp) * 1.0e6 / img_ysize(idict); - a[5] = hpos - - (float) wd *bp2int(img_pdf_orig_x(idict)) / img_xsize(idict); - a[6] = vpos - - (float) ht *bp2int(img_pdf_orig_y(idict)) / img_ysize(idict); + a[0] = 1.0e6 / img_xsize(idict); + a[3] = 1.0e6 / img_ysize(idict); r = 6; } else { - a[1] = wd * 1.0e6 / one_hundred_bp; - a[2] = 0; - a[3] = 0; - a[4] = (ht + dp) * 1.0e6 / one_hundred_bp; - a[5] = hpos; - a[6] = vpos; + a[3] = a[0] = 1.0e6 / one_hundred_bp; r = 4; } - if ((img_transform(img) & 1) == 1) { - if (ht == -dp) - pdftex_fail("image transform: division by zero (height == -depth)"); - if (wd == 0) - pdftex_fail("image transform: division by zero (width == 0)"); + xoff = (float) img_xorig(idict) / img_xsize(idict); + yoff = (float) img_yorig(idict) / img_ysize(idict); + if ((img_transform(img) & 7) > 3) { /* mirrored */ + a[0] *= -1; + xoff *= -1; } - switch (img_transform(img) & 7) { - case 0: /* unrotated */ + switch (img_transform(img) & 3) { + case 0: /* no transform */ break; case 1: /* rot. 90 deg. (counterclockwise) */ - a[2] = a[1] * (ht + dp) / wd; - a[1] = 0; - a[3] = -a[4] * wd / (ht + dp); - a[4] = 0; - a[5] += wd; + a[1] = a[0]; + a[2] = -a[3]; + a[3] = a[0] = 0; + tmp = yoff; + yoff = xoff; + xoff = -tmp; break; - case 2: /* rot. 180 deg. */ - a[1] = -a[1]; - a[4] = -a[4]; - a[5] += wd; - a[6] += ht + dp; + case 2: /* rot. 180 deg. (counterclockwise) */ + a[0] *= -1; + a[3] *= -1; + xoff *= -1; + yoff *= -1; break; - case 3: /* rot. 270 deg. */ - a[2] = -a[1] * (ht + dp) / wd; - a[1] = 0; - a[3] = a[4] * wd / (ht + dp); - a[4] = 0; - a[6] += ht + dp; + case 3: /* rot. 270 deg. (counterclockwise) */ + a[1] = -a[0]; + a[2] = a[3]; + a[3] = a[0] = 0; + tmp = yoff; + yoff = -xoff; + xoff = tmp; break; + default:; + } + xoff *= wd; + yoff *= ht + dp; + a[0] *= wd; + a[1] *= ht + dp; + a[2] *= wd; + a[3] *= ht + dp; + a[4] = hpos - xoff; + a[5] = vpos - yoff; + switch (img_transform(img) & 7) { + case 0: /* no transform */ + case 7: /* mirrored, then rot. 270 deg. */ + break; + case 1: /* rot. 90 deg. (counterclockwise) */ case 4: /* mirrored, unrotated */ - a[1] = -a[1]; - a[5] += wd; + a[4] += wd; break; + case 2: /* rot. 180 deg. */ case 5: /* mirrored, then rot. 90 deg. */ - a[2] = -a[1] * (ht + dp) / wd; - a[1] = 0; - a[3] = -a[4] * wd / (ht + dp); - a[4] = 0; - a[5] += wd; - a[6] += ht + dp; + a[4] += wd; + a[5] += ht + dp; break; + case 3: /* rot. 270 deg. */ case 6: /* mirrored, then rot. 180 deg. */ - a[4] = -a[4]; - a[6] += ht + dp; + a[5] += ht + dp; break; - case 7: /* mirrored, then rot. 270 deg. */ - a[2] = a[1] * (ht + dp) / wd; - a[1] = 0; - a[3] = a[4] * wd / (ht + dp); - a[4] = 0; - break; - default: - assert(0); + default:; } pdf_end_text(); pdf_printf("q\n"); + pdf_print_real((integer) a[0], r); + pdfout(' '); pdf_print_real((integer) a[1], r); pdfout(' '); pdf_print_real((integer) a[2], r); pdfout(' '); pdf_print_real((integer) a[3], r); pdfout(' '); - pdf_print_real((integer) a[4], r); + pdf_print_bp((integer) a[4]); pdfout(' '); pdf_print_bp((integer) a[5]); - pdfout(' '); - pdf_print_bp((integer) a[6]); pdf_printf(" cm\n/Im"); pdf_print_int(img_index(idict)); pdf_print_resname_prefix(); @@ -621,12 +630,12 @@ integer image_colordepth(integer ref) integer epdf_orig_x(integer ref) { - return bp2int(img_pdf_orig_x(img_dict(img_array[ref]))); + return img_xorig(img_dict(img_array[ref])); } integer epdf_orig_y(integer ref) { - return bp2int(img_pdf_orig_y(img_dict(img_array[ref]))); + return img_yorig(img_dict(img_array[ref])); } integer image_objnum(integer ref) @@ -659,8 +668,6 @@ void update_image_procset(integer ref) pdf_image_procset |= img_color(img_dict(img_array[ref])); } -/**********************************************************************/ - boolean check_image_b(integer procset) { return procset & IMAGE_COLOR_B; diff --git a/Build/source/texk/web2c/luatexdir/image/writejbig2.c b/Build/source/texk/web2c/luatexdir/image/writejbig2.c index d50ed88c485..2d7d4560548 100644 --- a/Build/source/texk/web2c/luatexdir/image/writejbig2.c +++ b/Build/source/texk/web2c/luatexdir/image/writejbig2.c @@ -75,7 +75,7 @@ object exists, reference it. Else create fresh one. 09 Dec. 2002: JBIG2 seg. page numbers > 0 are now set to 1, see PDF Ref. -$Id: writejbig2.c 1027 2008-02-14 21:01:03Z hhenkel $ +$Id: writejbig2.c 1127 2008-03-25 21:32:54Z hhenkel $ ***********************************************************************/ #include "writejbig2.h" @@ -705,6 +705,7 @@ void read_jbig2_info(image_dict * idict) if (pip == NULL) pdftex_fail("read_jbig2_info(): page %d not found in JBIG2 image file", (int) img_pagenum(idict)); + img_totalpages(idict) = fip->numofpages; img_xsize(idict) = pip->width; img_ysize(idict) = pip->height; img_xres(idict) = (int) (pip->xres * 0.0254 + 0.5); diff --git a/Build/source/texk/web2c/luatexdir/lang/hyphen.c b/Build/source/texk/web2c/luatexdir/lang/hyphen.c index 140cde8d768..1f75b352bca 100644 --- a/Build/source/texk/web2c/luatexdir/lang/hyphen.c +++ b/Build/source/texk/web2c/luatexdir/lang/hyphen.c @@ -623,7 +623,7 @@ void hnj_hyphen_load( repl = hnj_strdup(repl + 1); } */ - for (i=0,j=0,e=0; i<l; i++) { + for (i=0,j=0,e=0; (unsigned)i<l; i++) { if (format[i]>='0'&&format[i]<='9') j++; if (is_utf8_follow(format[i])) e++; } @@ -633,7 +633,7 @@ void hnj_hyphen_load( char *org = ( char*) malloc(2+l-e-j); /* remove hyphenation encoders (digits) from pat*/ org[0] = '0'; - for (i=0,j=0,e=0; i<l; i++) { + for (i=0,j=0,e=0; (unsigned)i<l; i++) { unsigned char c = format[i]; if (is_utf8_follow(c)) { pat[j+e++] = c; @@ -779,7 +779,7 @@ void hnj_hyphen_hyphenate( halfword here; for (char_num=0, here=begin_point; here!=end_point; here=get_vlink(here)) { - int ch = get_character(here); + int ch = get_lc_code(get_character(here)); while (state!=-1) { /* printf("%*s%s%c",char_num-strlen(get_state_str(state)),"",get_state_str(state),(char)ch);*/ diff --git a/Build/source/texk/web2c/luatexdir/lang/texlang.c b/Build/source/texk/web2c/luatexdir/lang/texlang.c index 12390ae2fcf..0504805a55e 100644 --- a/Build/source/texk/web2c/luatexdir/lang/texlang.c +++ b/Build/source/texk/web2c/luatexdir/lang/texlang.c @@ -60,7 +60,7 @@ new_language (void) { struct tex_language * get_language (int n) { - if (n>=0 && n<=MAX_TEX_LANGUAGES ) { + if (n>=0 && n<MAX_TEX_LANGUAGES ) { if (tex_languages[n]!=NULL) { return tex_languages[n]; } else { @@ -74,24 +74,28 @@ get_language (int n) { void set_pre_hyphen_char (integer n, integer v) { struct tex_language *l = get_language((int)n); - l->pre_hyphen_char = (int)v; + if (l!=NULL) + l->pre_hyphen_char = (int)v; } void set_post_hyphen_char (integer n, integer v) { struct tex_language *l = get_language((int)n); - l->post_hyphen_char = (int)v; + if (l!=NULL) + l->post_hyphen_char = (int)v; } integer get_pre_hyphen_char (integer n) { struct tex_language *l = get_language((int)n); + if (l==NULL) return -1; return (integer)l->pre_hyphen_char; } integer get_post_hyphen_char (integer n) { struct tex_language *l = get_language((int)n); + if (l==NULL) return -1; return (integer)l->post_hyphen_char; } @@ -352,7 +356,7 @@ char *hyphenation_exception(int exceptions, char *w) { char *exception_strings(struct tex_language *lang) { char *value; - int size = 0, current =0; + size_t size = 0, current =0; size_t l =0; char *ret = NULL; lua_State *L = Luas[0]; @@ -382,7 +386,7 @@ char *exception_strings(struct tex_language *lang) { /* the sequence from |wordstart| to |r| can contain only normal characters */ /* it could be faster to modify a halfword pointer and return an integer */ -halfword find_exception_part(int *j, int *uword, int len) { +halfword find_exception_part(unsigned int *j, int *uword, int len) { halfword g = null, gg = null; register int i = *j; i++; /* this puts uword[i] on the '{' */ @@ -401,7 +405,7 @@ halfword find_exception_part(int *j, int *uword, int len) { return gg; } -int count_exception_part(int *j, int *uword, int len) { +int count_exception_part(unsigned int *j, int *uword, int len) { int ret=0; register int i = *j; i++; /* this puts uword[i] on the '{' */ @@ -420,7 +424,7 @@ static char *PAT_ERROR[] = { NULL }; void do_exception (halfword wordstart, halfword r, char *replacement) { - int i; + unsigned i; halfword t; unsigned len; int clang; @@ -812,8 +816,7 @@ void undump_one_language (int i) { } void undump_language_data (void) { - int i; - unsigned x, numlangs; + unsigned i, x, numlangs; undump_int(numlangs); for (i=0;i<numlangs;i++) { undump_int(x); diff --git a/Build/source/texk/web2c/luatexdir/lua/lfontlib.c b/Build/source/texk/web2c/luatexdir/lua/lfontlib.c index 13632527082..4bb5252daa7 100644 --- a/Build/source/texk/web2c/luatexdir/lua/lfontlib.c +++ b/Build/source/texk/web2c/luatexdir/lua/lfontlib.c @@ -1,4 +1,4 @@ -/* $Id: lfontlib.c 1013 2008-02-14 00:09:02Z oneiros $ */ +/* $Id: lfontlib.c 1171 2008-04-16 13:56:53Z taco $ */ #include "luatex-api.h" #include <ptexlib.h> @@ -181,6 +181,16 @@ deffont (lua_State *L) { return 0; /* not reached */ } +/* this returns the expected (!) next fontid. */ +static int +nextfontid (lua_State *L) { + int i = new_font(); + lua_pushnumber(L,i); + delete_font(i); + return 1; +} + + static int getfont (lua_State *L) { int i; @@ -201,6 +211,7 @@ static const struct luaL_reg fontlib [] = { {"getfont", getfont}, {"setfont", setfont}, {"define", deffont}, + {"nextid", nextfontid}, {"frozen", frozenfont}, {NULL, NULL} /* sentinel */ }; diff --git a/Build/source/texk/web2c/luatexdir/lua/llanglib.c b/Build/source/texk/web2c/luatexdir/lua/llanglib.c index 63e8c45dec8..a5227afa1a0 100644 --- a/Build/source/texk/web2c/luatexdir/lua/llanglib.c +++ b/Build/source/texk/web2c/luatexdir/lua/llanglib.c @@ -29,6 +29,9 @@ This is llanglib.c #define check_islang(L,b) (struct tex_language **)luaL_checkudata(L,b,LANG_METATABLE) +extern halfword *check_isnode (lua_State *L, int ud) ; + + static int lang_new (lua_State *L) { struct tex_language **lang; diff --git a/Build/source/texk/web2c/luatexdir/lua/llualib.c b/Build/source/texk/web2c/luatexdir/lua/llualib.c index 0d13500a9b7..90b1687f3b1 100644 --- a/Build/source/texk/web2c/luatexdir/lua/llualib.c +++ b/Build/source/texk/web2c/luatexdir/lua/llualib.c @@ -1,5 +1,5 @@ -/* $Id: llualib.c 1013 2008-02-14 00:09:02Z oneiros $ */ +/* $Id: llualib.c 1169 2008-04-15 14:57:56Z oneiros $ */ #include "luatex-api.h" #include <ptexlib.h> @@ -49,17 +49,17 @@ void undump_luac_registers (void) { if (luabytecode_max>=0) { i = (luabytecode_max+1); if ((int)(UINT_MAX32/sizeof(bytecode)+1)<=i) { - lua_fatal_error("Corrupt format file"); + lua_fatal_error(maketexstring("Corrupt format file")); } lua_bytecode_registers = xmalloc(i*sizeof(bytecode)); luabytecode_bytes = i*sizeof(bytecode); - for (i=0;i<=luabytecode_max;i++) { + for (i=0;i<=(unsigned)luabytecode_max;i++) { lua_bytecode_registers[i].done = 0; lua_bytecode_registers[i].size = 0; lua_bytecode_registers[i].buf = NULL; } undump_int(n); - for (i=0;i<n;i++) { + for (i=0;i<(unsigned)n;i++) { undump_int(k); undump_int(b.size); b.buf=xmalloc(b.size); @@ -107,7 +107,7 @@ bytecode_register_shadow_get (lua_State* L, int k){ int writer(lua_State* L, const void* b, size_t size, void* B) { bytecode* buf = (bytecode*)B; - if (buf->size + size > buf->alloc) { + if ((int)(buf->size + size) > buf->alloc) { buf->buf = xrealloc(buf->buf,buf->alloc+size+LOAD_BUF_SIZE); buf->alloc = buf->alloc+size+LOAD_BUF_SIZE; } @@ -176,7 +176,7 @@ int set_bytecode (lua_State *L) { } else { luabytecode_bytes += sizeof(bytecode)*(k+1-luabytecode_max); } - for (i=(luabytecode_max+1);i<=k;i++) { + for (i=(luabytecode_max+1);i<=(unsigned)k;i++) { lua_bytecode_registers[i].buf=NULL; lua_bytecode_registers[i].size=0; lua_bytecode_registers[i].done=0; diff --git a/Build/source/texk/web2c/luatexdir/lua/lmplib.c b/Build/source/texk/web2c/luatexdir/lua/lmplib.c index a194f9a55d8..b435618e97b 100644 --- a/Build/source/texk/web2c/luatexdir/lua/lmplib.c +++ b/Build/source/texk/web2c/luatexdir/lua/lmplib.c @@ -430,7 +430,7 @@ void mplib_write_ascii_file (MP mp, void *ff, const char *s) { } else if (f==mplib_data->ps_file_ptr) { APPEND_STRING(ps_out,s); } else { - fprintf((FILE *)f,s); + fprintf((FILE *)f,"%s",s); } } } @@ -883,8 +883,7 @@ mplib_gr_tostring (lua_State *L) { static int mplib_gr_fields (lua_State *L) { const char **fields; - const char *f; - int i = 1; + int i ; struct mp_graphic_object **hh = is_gr_object(L,1); if (*hh) { switch ((*hh)->_type_field) { @@ -899,9 +898,9 @@ mplib_gr_fields (lua_State *L) { default: fields = no_fields; } lua_newtable(L); - for (f = *fields; f != NULL; f++) { - lua_pushstring(L,f); - lua_rawseti(L,-2,i); i++; + for (i=0;fields[i]!=NULL;i++) { + lua_pushstring(L,fields[i]); + lua_rawseti(L,-2,(i+1)); } } else { lua_pushnil(L); @@ -926,16 +925,16 @@ mplib_push_path (lua_State *L, struct mp_knot *h, int is_pen) { lua_createtable(L,0,6); if (!is_pen) { if (p->left_type_field != mp_explicit) { - mplib_push_S(left_type); - lua_pushstring(L,knot_type_enum[p->left_type_field]); - lua_rawset(L,-3); - } - if (p->right_type_field != mp_explicit) { - mplib_push_S(right_type); - lua_pushstring(L,knot_type_enum[p->right_type_field]); - lua_rawset(L,-3); - } - } + mplib_push_S(left_type); + lua_pushstring(L,knot_type_enum[p->left_type_field]); + lua_rawset(L,-3); + } + if (p->right_type_field != mp_explicit) { + mplib_push_S(right_type); + lua_pushstring(L,knot_type_enum[p->right_type_field]); + lua_rawset(L,-3); + } + } mplib_push_S(x_coord); mplib_push_number(L,p->x_coord_field); lua_rawset(L,-3); @@ -965,6 +964,23 @@ mplib_push_path (lua_State *L, struct mp_knot *h, int is_pen) { } } +/* this assumes that the top of the stack is a table + or nil already in the case + */ +static void +mplib_push_pentype (lua_State *L, struct mp_knot *h) { + struct mp_knot *p; /* for scanning the path */ + p=h; + if (p==NULL) { + /* do nothing */ + } else if (p==p->next_field) { + mplib_push_S(type); + lua_pushstring(L,"elliptical"); + lua_rawset(L,-3); + } else { + } +} + #define set_color_objects(pq) \ object_color_model = pq->color_model_field; \ object_color_a = pq->color_field._a_val; \ @@ -1012,6 +1028,7 @@ mplib_push_color (lua_State *L, struct mp_graphic_object *p ) { static void mplib_push_dash (lua_State *L, struct mp_stroked_object *h ) { mp_dash_object *d; + double ds; if (h!=NULL && h->dash_p_field != NULL) { d = h->dash_p_field; lua_newtable(L); @@ -1021,7 +1038,8 @@ mplib_push_dash (lua_State *L, struct mp_stroked_object *h ) { int i = 0; lua_newtable(L); while (*(d->array_field+i) != -1) { - mplib_push_number(L, *(d->array_field+1)); + ds = *(d->array_field+1) / 65536.0; + lua_pushnumber(L, ds); i++; lua_rawseti(L,-2,i); } @@ -1064,6 +1082,7 @@ mplib_fill_field (lua_State *L, struct mp_fill_object *h) { mplib_push_path(L, h->htap_p_field, MPLIB_PATH); } else if (FIELD(pen)) { mplib_push_path(L, h->pen_p_field, MPLIB_PEN); + mplib_push_pentype(L, h->pen_p_field); } else if (FIELD(color)) { mplib_push_color(L,(mp_graphic_object *)h); } else if (FIELD(linejoin)) { @@ -1085,6 +1104,7 @@ mplib_stroked_field (lua_State *L, struct mp_stroked_object *h) { mplib_push_path(L, h->path_p_field, MPLIB_PATH); } else if (FIELD(pen)) { mplib_push_path(L, h->pen_p_field, MPLIB_PEN); + mplib_push_pentype(L, h->pen_p_field); } else if (FIELD(color)) { mplib_push_color(L, (mp_graphic_object *)h); } else if (FIELD(dash)) { @@ -1163,6 +1183,7 @@ mplib_gr_index (lua_State *L) { struct mp_graphic_object **hh = is_gr_object(L,1); if (*hh) { struct mp_graphic_object *h = *hh; + if (mplib_is_S(type,2)) { lua_rawgeti(L,LUA_REGISTRYINDEX,mplib_type_Ses[h->_type_field]); } else { @@ -1206,7 +1227,6 @@ static const struct luaL_reg mplib_gr_meta[] = { {"__gc", mplib_gr_collect }, {"__tostring", mplib_gr_tostring }, {"__index", mplib_gr_index }, - {"fields", mplib_gr_fields }, {NULL, NULL} /* sentinel */ }; @@ -1220,7 +1240,8 @@ static const struct luaL_reg mplib_d [] = { static const struct luaL_reg mplib_m[] = { - {"new", mplib_new}, + {"new", mplib_new }, + {"fields", mplib_gr_fields }, {NULL, NULL} /* sentinel */ }; @@ -1228,6 +1249,7 @@ static const struct luaL_reg mplib_m[] = { int luaopen_mp (lua_State *L) { mplib_init_Ses(L); + luaL_newmetatable(L,MPLIB_GR_METATABLE); lua_pushvalue(L, -1); /* push metatable */ lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ diff --git a/Build/source/texk/web2c/luatexdir/lua/lpdflib.c b/Build/source/texk/web2c/luatexdir/lua/lpdflib.c index 9cf847c84c8..393409b0729 100644 --- a/Build/source/texk/web2c/luatexdir/lua/lpdflib.c +++ b/Build/source/texk/web2c/luatexdir/lua/lpdflib.c @@ -1,23 +1,23 @@ -/* $Id: lpdflib.c 1013 2008-02-14 00:09:02Z oneiros $ */ +/* $Id: lpdflib.c 1154 2008-04-13 22:36:03Z oneiros $ */ #include "luatex-api.h" #include <ptexlib.h> -static int -findcurv (lua_State *L) { - int j; - j = get_cur_v(); - lua_pushnumber(L, j); - return 1; +static int findcurv(lua_State * L) +{ + int j; + j = get_cur_v(); + lua_pushnumber(L, j); + return 1; } -static int -findcurh (lua_State *L) { - int j; - j = get_cur_h(); - lua_pushnumber(L, j); - return 1; +static int findcurh(lua_State * L) +{ + int j; + j = get_cur_h(); + lua_pushnumber(L, j); + return 1; } @@ -26,7 +26,8 @@ typedef enum { set_origin, direct_page, direct_always } pdf_lit_mode; int luapdfprint(lua_State * L) { int n; - unsigned i, len; + unsigned i; + size_t len; const char *outputstr, *st; pdf_lit_mode literal_mode; n = lua_gettop(L); @@ -46,13 +47,12 @@ int luapdfprint(lua_State * L) else if (strcmp(outputstr, "page") == 0) literal_mode = direct_page; else { - lua_pushstring(L, - "invalid argument for print literal mode"); + lua_pushstring(L, "invalid argument for print literal mode"); lua_error(L); } } } else { - if (n != 1) { + if (n != 1) { lua_pushstring(L, "invalid number of arguments"); lua_error(L); } @@ -71,57 +71,72 @@ int luapdfprint(lua_State * L) default: assert(0); } - st = lua_tolstring(L, n,&len); + st = lua_tolstring(L, n, &len); for (i = 0; i < len; i++) { - if (i%16 == 0) - pdfroom(16); - pdf_buf[pdf_ptr++] = st[i]; + if (i % 16 == 0) + pdfroom(16); + pdf_buf[pdf_ptr++] = st[i]; } return 0; } -static int -getpdf (lua_State *L) { - char *st; - if (lua_isstring(L,2)) { - st = (char *)lua_tostring(L,2); - if (st && *st) { - if (*st == 'h') - return findcurh(L); - else if (*st == 'v') - return findcurv(L); +#define obj_type_others 0 + +static int l_immediateobj(lua_State * L) +{ + if (!lua_isstring(L, -1)) + luaL_error(L, "pdf.immediateobj needs string value"); + pdf_create_obj(obj_type_others, 0); + pdf_begin_obj(obj_ptr, 1); + pdf_printf("%s\n", lua_tostring(L, -1)); + pdf_end_obj(); + lua_pop(L, 1); + lua_pushinteger(L, obj_ptr); + return 1; +} + +static int getpdf(lua_State * L) +{ + char *st; + if (lua_isstring(L, 2)) { + st = (char *) lua_tostring(L, 2); + if (st && *st) { + if (*st == 'h') + return findcurh(L); + else if (*st == 'v') + return findcurv(L); + } } - } - lua_pushnil(L); - return 1; + lua_pushnil(L); + return 1; } -static int -setpdf (lua_State *L) { - return (L==NULL ? 0 : 0); /* for -Wall */ +static int setpdf(lua_State * L) +{ + return (L == NULL ? 0 : 0); /* for -Wall */ } static const struct luaL_reg pdflib[] = { {"print", luapdfprint}, + {"immediateobj", l_immediateobj}, {NULL, NULL} /* sentinel */ }; -int -luaopen_pdf (lua_State *L) { - luaL_register(L, "pdf", pdflib); - /* build meta table */ - luaL_newmetatable(L,"pdf_meta"); - lua_pushstring(L, "__index"); - lua_pushcfunction(L, getpdf); - /* do these later, NYI */ - if (0) { +int luaopen_pdf(lua_State * L) +{ + luaL_register(L, "pdf", pdflib); + /* build meta table */ + luaL_newmetatable(L, "pdf_meta"); + lua_pushstring(L, "__index"); + lua_pushcfunction(L, getpdf); + /* do these later, NYI */ + if (0) { + lua_settable(L, -3); + lua_pushstring(L, "__newindex"); + lua_pushcfunction(L, setpdf); + } lua_settable(L, -3); - lua_pushstring(L, "__newindex"); - lua_pushcfunction(L, setpdf); - } - lua_settable(L, -3); - lua_setmetatable(L,-2); /* meta to itself */ - return 1; + lua_setmetatable(L, -2); /* meta to itself */ + return 1; } - diff --git a/Build/source/texk/web2c/luatexdir/lua/ltexlib.c b/Build/source/texk/web2c/luatexdir/lua/ltexlib.c index f2258010136..937c802727a 100644 --- a/Build/source/texk/web2c/luatexdir/lua/ltexlib.c +++ b/Build/source/texk/web2c/luatexdir/lua/ltexlib.c @@ -1,4 +1,4 @@ -/* $Id: ltexlib.c 1087 2008-03-07 23:52:35Z hhenkel $ */ +/* $Id: ltexlib.c 1168 2008-04-15 13:43:34Z taco $ */ #include "luatex-api.h" #include <ptexlib.h> @@ -135,6 +135,8 @@ luacstring_input (void) { last = first; ret = last; check_buffer_overflow (last + t->tsize); + /* make sure it fits in the pool as well (for show_token_list c.s) */ + check_pool_overflow(pool_ptr + t->tsize); while (t->tsize-->0) buffer[last++] = *st++; if (!t->partial) { @@ -193,7 +195,7 @@ luacstring_close (int n) { #define height_offset 3 #define check_index_range(j) \ - if (j<0 || j > 65535) { \ + if (j > 65535) { \ lua_pushstring(L, "incorrect index value"); \ lua_error(L); } @@ -449,9 +451,38 @@ int gettoks (lua_State *L) { return 1; } +/* UGLY hack */ + +#define char_given 70 +#define math_given 71 +#define omath_given 72 + +static int get_box_id (lua_State *L, int i) { + const char *s; + integer cur_cs, cur_cmd; + str_number texstr; + size_t k = 0; + int j = -1; + if (lua_type(L,i)==LUA_TSTRING) { + s = (char *)lua_tolstring(L,i, &k); + texstr = maketexlstring(s,k); + cur_cs = string_lookup(texstr); + cur_cmd = zget_eq_type(cur_cs); + flush_str(texstr); + if (cur_cmd==char_given || + cur_cmd==math_given || + cur_cmd==omath_given) { + j = zget_equiv(cur_cs); + } + } else { + j = (int)lua_tonumber(L,(i)); + } + return j; +} + int getbox (lua_State *L) { int k, t; - k = (int)luaL_checkinteger(L,-1); + k = get_box_id(L,-1); check_index_range(k); t = get_tex_box_register(k); nodelist_to_lua(L,t); @@ -460,10 +491,7 @@ int getbox (lua_State *L) { int setbox (lua_State *L) { int i,j,k; - - - - k = (int)luaL_checkinteger(L,-2); + k = get_box_id(L,-2); check_index_range(k); i = get_tex_box_register(k); if (lua_isboolean(L,-1)) { @@ -482,11 +510,10 @@ int setbox (lua_State *L) { return 0; } - static int getboxdim (lua_State *L, int whichdim) { int i, j; i = lua_gettop(L); - j = (int)lua_tonumber(L,(i)); + j = get_box_id(L,i); lua_settop(L,(i-2)); /* table at -1 */ if (j<0 || j > 65535) { lua_pushstring(L, "incorrect index"); @@ -525,7 +552,7 @@ static int setboxdim (lua_State *L, int whichdim) { } else { j = (int)lua_tonumber(L,i); } - k = (int)lua_tonumber(L,(i-1)); + k = get_box_id(L,(i-1)); lua_settop(L,(i-3)); /* table at -2 */ if (k<0 || k > 65535) { lua_pushstring(L, "incorrect index"); @@ -704,6 +731,7 @@ getlist (lua_State *L) { int setlist (lua_State *L) { + assert(L); return 0; } diff --git a/Build/source/texk/web2c/luatexdir/lua/luatoken.c b/Build/source/texk/web2c/luatexdir/lua/luatoken.c index 23d9ca82150..bf434b286bb 100644 --- a/Build/source/texk/web2c/luatexdir/lua/luatoken.c +++ b/Build/source/texk/web2c/luatexdir/lua/luatoken.c @@ -1,4 +1,4 @@ -/* $Id: luatoken.c 1013 2008-02-14 00:09:02Z oneiros $ */ +/* $Id: luatoken.c 1168 2008-04-15 13:43:34Z taco $ */ #include "luatex-api.h" #include <ptexlib.h> @@ -222,7 +222,7 @@ get_cur_cs (lua_State *L) { if (lua_isstring(L,-1)) { s = (char *)lua_tolstring(L,-1,&l); if (l>0) { - if (last+l>buf_size) + if ((int)(last+l)>buf_size) check_buffer_overflow(last+l); for (j=0;j<l;j++) { buffer[last+1+j]=*s++; @@ -330,7 +330,7 @@ tokenlist_to_cstring ( int p , int inhibit_par, int *siz) { Print_uchar(q-active_base); } } - } else if ((q>=undefined_control_sequence)&&((q<=eqtb_size))||(q>eqtb_size+hash_extra)) { + } else if ((q>=undefined_control_sequence)&&((q<=eqtb_size)||(q>eqtb_size+hash_extra))) { Print_esc("IMPOSSIBLE."); } else if ((zget_cs_text(q)<0)||(zget_cs_text(q)>=str_ptr)) { Print_esc("NONEXISTENT."); @@ -439,8 +439,8 @@ tokenlist_to_luastring(lua_State *L, int p) { int tokenlist_from_lua(lua_State *L) { char *s; - int tok,i; - size_t j; + int tok; + size_t i,j; halfword p,q,r; r = get_avail(); info(r)=0; /* ref count */ diff --git a/Build/source/texk/web2c/luatexdir/lua/mp.w b/Build/source/texk/web2c/luatexdir/lua/mp.w index a459cc48fe1..4894a54e9aa 100644 --- a/Build/source/texk/web2c/luatexdir/lua/mp.w +++ b/Build/source/texk/web2c/luatexdir/lua/mp.w @@ -75,8 +75,8 @@ undergoes any modifications, so that it will be clear which version of @d banner "This is MetaPost, Version 1.003" /* printed when \MP\ starts */ @d metapost_version "1.003" -@d mplib_version "0.30" -@d version_string " (Cweb version 0.30)" +@d mplib_version "0.40" +@d version_string " (Cweb version 0.40)" @d true 1 @d false 0 @@ -659,6 +659,8 @@ char *mp_read_ascii_file (MP mp, void *ff, size_t *size) { FILE *f = (FILE *)ff; *size = 0; (void) mp; /* for -Wunused */ + if (f==NULL) + return NULL; #if NOTTESTING c = fgetc(f); if (c==EOF) @@ -700,7 +702,8 @@ void mp_read_binary_file (MP mp, void *f, void **data, size_t *size) { size_t len = 0; (void) mp; #if NOTTESTING - len = fread(*data,1,*size,(FILE *)f); + if (f!=NULL) + len = fread(*data,1,*size,(FILE *)f); #endif *size = len; } @@ -719,7 +722,8 @@ void mp_write_binary_file (MP mp, void *f, void *s, size_t size) { void mp_close_file (MP mp, void *f) { (void) mp; #if NOTTESTING - fclose((FILE *)f); + if (f!=NULL) + fclose((FILE *)f); #endif } @@ -727,7 +731,10 @@ void mp_close_file (MP mp, void *f) { int mp_eof_file (MP mp, void *f) { (void) mp; #if NOTTESTING - return feof((FILE *)f); + if (f!=NULL) + return feof((FILE *)f); + else + return 1; #else return 0; #endif @@ -737,7 +744,8 @@ int mp_eof_file (MP mp, void *f) { void mp_flush_file (MP mp, void *f) { (void) mp; #if NOTTESTING - fflush((FILE *)f); + if (f!=NULL) + fflush((FILE *)f); #endif } @@ -9517,27 +9525,39 @@ dash_y(hh)=dash_y(h) @ |h| is an edge structure @c -mp_dash_object *mp_export_dashes (MP mp, pointer h) { +mp_dash_object *mp_export_dashes (MP mp, pointer q, scaled *w) { mp_dash_object *d; - pointer p; + pointer p, h; + scaled scf; /* scale factor */ scaled *dashes = NULL; int num_dashes = 1; + h = dash_p(q); if (h==null || dash_list(h)==null_dash) return NULL; p = dash_list(h); + scf=mp_get_pen_scale(mp, pen_p(q)); + if (scf==0) { + if (*w==0) scf = dash_scale(q); else return NULL; + } else { + scf=mp_make_scaled(mp, *w,scf); + scf=mp_take_scaled(mp, scf,dash_scale(q)); + } + *w = scf; d = mp_xmalloc(mp,1,sizeof(mp_dash_object)); start_x(null_dash)=start_x(p)+dash_y(h); while (p != null_dash) { dashes = mp_xrealloc(mp, dashes, num_dashes+2, sizeof(scaled)); - dashes[(num_dashes-1)] = (stop_x(p)-start_x(p)); - dashes[(num_dashes)] = (start_x(link(p))-stop_x(p)); + dashes[(num_dashes-1)] = + mp_take_scaled(mp,(stop_x(p)-start_x(p)),scf); + dashes[(num_dashes)] = + mp_take_scaled(mp,(start_x(link(p))-stop_x(p)),scf); dashes[(num_dashes+1)] = -1; /* terminus */ num_dashes+=2; p=link(p); } d->array_field = dashes; - d->offset_field = mp_dash_offset(mp, h); - d->scale_field = dash_scale(h); + d->offset_field = + mp_take_scaled(mp,mp_dash_offset(mp, h),scf); return d; } @@ -9590,8 +9610,6 @@ pointer mp_copy_objects (MP mp, pointer p, pointer q) { } @ @<Fix anything in graphical object |pp| that should differ from the...@>= -if ( pre_script(p)!=null ) add_str_ref(pre_script(p)); -if ( post_script(p)!=null ) add_str_ref(post_script(p)); switch (type(p)) { case mp_start_clip_code: case mp_start_bounds_code: @@ -9599,14 +9617,20 @@ case mp_start_bounds_code: break; case mp_fill_code: path_p(pp)=mp_copy_path(mp, path_p(p)); + if ( pre_script(p)!=null ) add_str_ref(pre_script(p)); + if ( post_script(p)!=null ) add_str_ref(post_script(p)); if ( pen_p(p)!=null ) pen_p(pp)=copy_pen(pen_p(p)); break; case mp_stroked_code: + if ( pre_script(p)!=null ) add_str_ref(pre_script(p)); + if ( post_script(p)!=null ) add_str_ref(post_script(p)); path_p(pp)=mp_copy_path(mp, path_p(p)); pen_p(pp)=copy_pen(pen_p(p)); if ( dash_p(p)!=null ) add_edge_ref(dash_p(pp)); break; case mp_text_code: + if ( pre_script(p)!=null ) add_str_ref(pre_script(p)); + if ( post_script(p)!=null ) add_str_ref(post_script(p)); add_str_ref(text_p(pp)); break; case mp_stop_clip_code: @@ -10376,7 +10400,7 @@ integer spec_offset; /* number of pen edges between |h| and the initial offset * @ @c @<Declare subroutines needed by |offset_prep|@> pointer mp_offset_prep (MP mp,pointer c, pointer h) { halfword n; /* the number of vertices in the pen polygon */ - pointer p,q,q0,r,w, ww; /* for list manipulation */ + pointer c0,p,q,q0,r,w, ww; /* for list manipulation */ integer k_needed; /* amount to be added to |info(p)| when it is computed */ pointer w0; /* a pointer to pen offset to use just before |p| */ scaled dxin,dyin; /* the direction into knot |p| */ @@ -10385,7 +10409,7 @@ pointer mp_offset_prep (MP mp,pointer c, pointer h) { dx0=0; dy0=0; @<Initialize the pen size~|n|@>; @<Initialize the incoming direction and pen offset at |c|@>; - p=c; k_needed=0; + p=c; c0=c; k_needed=0; do { q=link(p); @<Split the cubic between |p| and |q|, if necessary, into cubics @@ -10456,7 +10480,7 @@ do { p=r; } while (p!=q); /* Check if we removed too much */ -if(q!=q0) +if ((q!=q0)&&(q!=c||c==c0)) q = link(q) @ @<Remove the cubic following |p| and update the data structures...@>= @@ -23247,7 +23271,6 @@ void mp_do_write (MP mp) ; @ @<Record the end of file on |wr_file[n]|@>= { (mp->close_file)(mp,mp->wr_file[n]); xfree(mp->wr_fname[n]); - mp->wr_fname[n]=NULL; if ( n==mp->write_files-1 ) mp->write_files=n; } @@ -25218,6 +25241,7 @@ void mp_ship_out (MP mp, pointer h) ; struct mp_edge_object *mp_gr_export(MP mp, pointer h) { pointer p; /* the current graphical object */ integer t; /* a temporary value */ + scaled d_width; /* the current pen width */ mp_edge_object *hh; /* the first graphical object */ struct mp_graphic_object *hq; /* something |hp| points to */ struct mp_text_object *tt; @@ -25244,6 +25268,7 @@ struct mp_edge_object *mp_gr_export(MP mp, pointer h) { case mp_fill_code: tf = (mp_fill_object *)hq; gr_pen_p(tf) = mp_export_knot_list(mp,pen_p(p)); + d_width = mp_get_pen_scale(mp, pen_p(p)); if ((pen_p(p)==null) || pen_is_elliptical(pen_p(p))) { gr_path_p(tf) = mp_export_knot_list(mp,path_p(p)); } else { @@ -25265,6 +25290,7 @@ struct mp_edge_object *mp_gr_export(MP mp, pointer h) { case mp_stroked_code: ts = (mp_stroked_object *)hq; gr_pen_p(ts) = mp_export_knot_list(mp,pen_p(p)); + d_width = mp_get_pen_scale(mp, pen_p(p)); if (pen_is_elliptical(pen_p(p))) { gr_path_p(ts) = mp_export_knot_list(mp,path_p(p)); } else { @@ -25286,7 +25312,7 @@ struct mp_edge_object *mp_gr_export(MP mp, pointer h) { gr_ljoin_val(ts) = ljoin_val(p); gr_miterlim_val(ts) = miterlim_val(p); gr_lcap_val(ts) = lcap_val(p); - gr_dash_p(ts) = mp_export_dashes(mp,dash_p(p)); + gr_dash_p(ts) = mp_export_dashes(mp,p,&d_width); break; case mp_text_code: tt = (mp_text_object *)hq; @@ -25666,6 +25692,7 @@ for (k=p;k<=mp->lo_mem_max;k++ ) undump_wd(mp->mem[k]); undump(mp->lo_mem_max+1,hi_mem_stat_min,mp->hi_mem_min); undump(null,mp->mem_top,mp->avail); mp->mem_end=mp->mem_top; +mp->last_pending=spec_head; for (k=mp->hi_mem_min;k<= mp->mem_end;k++) undump_wd(mp->mem[k]); undump_int(mp->var_used); undump_int(mp->dyn_used) @@ -25841,6 +25868,7 @@ if (mp->rd_fname!=NULL) { for (k=0;k<=(int)mp->read_files-1;k++ ) { if ( mp->rd_fname[k]!=NULL ) { (mp->close_file)(mp,mp->rd_file[k]); + xfree(mp->rd_fname[k]); } } } @@ -25848,6 +25876,7 @@ if (mp->wr_fname!=NULL) { for (k=0;k<=(int)mp->write_files-1;k++) { if ( mp->wr_fname[k]!=NULL ) { (mp->close_file)(mp,mp->wr_file[k]); + xfree(mp->wr_fname[k]); } } } @@ -25856,19 +25885,19 @@ if (mp->wr_fname!=NULL) { for (k=0;k<(int)mp->max_read_files;k++ ) { if ( mp->rd_fname[k]!=NULL ) { (mp->close_file)(mp,mp->rd_file[k]); - mp_xfree(mp->rd_fname[k]); + xfree(mp->rd_fname[k]); } } -mp_xfree(mp->rd_file); -mp_xfree(mp->rd_fname); +xfree(mp->rd_file); +xfree(mp->rd_fname); for (k=0;k<(int)mp->max_write_files;k++) { if ( mp->wr_fname[k]!=NULL ) { (mp->close_file)(mp,mp->wr_file[k]); - mp_xfree(mp->wr_fname[k]); + xfree(mp->wr_fname[k]); } } -mp_xfree(mp->wr_file); -mp_xfree(mp->wr_fname); +xfree(mp->wr_file); +xfree(mp->wr_fname); @ We want to produce a \.{TFM} file if and only if |mp_fontmaking| is positive. diff --git a/Build/source/texk/web2c/luatexdir/lua/psout.w b/Build/source/texk/web2c/luatexdir/lua/psout.w index 4a3f588ef7f..90cf6923e01 100644 --- a/Build/source/texk/web2c/luatexdir/lua/psout.w +++ b/Build/source/texk/web2c/luatexdir/lua/psout.w @@ -4405,12 +4405,9 @@ format, it is closely modelled to the PostScript model. The array of dashes is ended by a single negative value, because this is not allowed in PostScript. -@d gr_dash_scale(A) (gr_dash_p(A))->scale_field - @<Types...@>= typedef struct { scaled offset_field; - scaled scale_field; scaled *array_field; } mp_dash_object ; @@ -4707,7 +4704,6 @@ void mp_gr_fix_graphics_state (MP mp, mp_graphic_object *p) { scaled wx,wy,ww; /* dimensions of pen bounding box */ boolean adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */ integer tx,ty; /* temporaries for computing |adj_wx| */ - scaled scf; /* a scale factor for the dash pattern */ if ( gr_has_color(p) ) @<Make sure \ps\ will use the right color for object~|p|@>; if ( (gr_type(p)==mp_fill_code)||(gr_type(p)==mp_stroked_code) ) { @@ -4980,13 +4976,6 @@ if ( gr_type(p)==mp_fill_code || gr_dash_p(p) == NULL) { hh=NULL; } else { hh=gr_dash_p(p); - scf=mp_gr_get_pen_scale(mp, gr_pen_p((mp_fill_object *)p)); - if ( scf==0 ) { - if ( gs_width==0 ) scf=hh->scale_field; else hh=NULL; - } else { - scf=mp_make_scaled(mp, gs_width,scf); - scf=mp_take_scaled(mp, scf, (hh == NULL ? unity : gr_dash_scale(p))); - } } if ( hh==NULL ) { if ( gs_dash_p!=NULL || gs_dash_init_done == false) { @@ -5038,9 +5027,6 @@ boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) ; @ This function test if |h| and |hh| represent the same dash pattern. -The |scale_field| is ignored in this test because it is not really -a property of the PostScript format of a dash pattern. - @c boolean mp_gr_same_dashes (mp_dash_object *h, mp_dash_object *hh) { boolean ret=false; @@ -5496,9 +5482,12 @@ void mp_gr_ship_out (mp_edge_object *hh, int prologues, int procset) { case mp_start_bounds_code: case mp_stop_bounds_code: break; - case mp_special_code: - mp_ps_print_nl (mp, gr_pre_script((mp_special_object *)p)); - mp_ps_print_ln (mp); + case mp_special_code: + { + mp_special_object *ps = (mp_special_object *)p; + mp_ps_print_nl (mp, gr_pre_script(ps)); + mp_ps_print_ln (mp); + } break; } /* all cases are enumerated */ p=gr_link(p); diff --git a/Build/source/texk/web2c/luatexdir/luatex.web b/Build/source/texk/web2c/luatexdir/luatex.web index b1c977706ff..99fe08e72a9 100644 --- a/Build/source/texk/web2c/luatexdir/luatex.web +++ b/Build/source/texk/web2c/luatexdir/luatex.web @@ -243,8 +243,8 @@ known as `\eTeX'. @d pdftex_version_string=='-2.00.0' {current \pdfTeX\ version} @# @d luatex_version==25 { \.{\\luatexversion} } -@d luatex_revision=="1" { \.{\\luatexrevision} } -@d luatex_version_string=='snapshot-0.25.1' {current \LuaTeX\ version} +@d luatex_revision=="3" { \.{\\luatexrevision} } +@d luatex_version_string=='snapshot-0.25.3' {current \LuaTeX\ version} @d luatex_date_info==-extra_version_info { the compile date is negated } @# @d luaTeX_banner=='This is LuaTeX, Version ',luatex_version_string,extra_version_info @@ -10908,8 +10908,8 @@ at the value |p| that is returned. (If |p=temp_token_head|, the list is empty.) |lua_str_toks| is almost identical, but it also escapes the three symbols that |lua| considers special while scanning a literal string -@d unicode_incr(#)==if str_pool[#]>@"F0 then #:=#+4 else if str_pool[#]>@"E0 - then #:=#+3 else if str_pool[#]>@"C0 then #:=#+2 else incr(#) +@d unicode_incr(#)==if str_pool[#]>=@"F0 then #:=#+4 else if str_pool[#]>=@"E0 + then #:=#+3 else if str_pool[#]>=@"C0 then #:=#+2 else incr(#) @p @t\4@>@<Declare \eTeX\ procedures for token lists@>@;@/ @@ -16318,15 +16318,15 @@ var w, s_out: scaled; begin w := char_width(f,c); if hasfmentry(f) then begin - if pdf_cur_Tm_a = 0 then begin - call_func(divide_scaled_n(w, pdf_font_size(f),10000.0)); + if pdf_cur_Tm_a = 1000 then begin + call_func(divide_scaled_n(w, pdf_font_size(f), 10000.0)); pdf_delta_h := pdf_delta_h + scaled_out; end else begin - s := divide_scaled_n(round_xn_over_d(w, 1000, 1000 + pdf_cur_Tm_a), - pdf_font_size(f),10000.0); + s := divide_scaled_n(round_xn_over_d(w, 1000, pdf_cur_Tm_a), + pdf_font_size(f), 10000.0); s_out := round_xn_over_d(round_xn_over_d(pdf_font_size(f), abs(s), 10000), - 1000 + pdf_cur_Tm_a, 1000); + pdf_cur_Tm_a, 1000); if s < 0 then s_out := -s_out; pdf_delta_h := pdf_delta_h + s_out; @@ -16378,12 +16378,12 @@ end; @!pdf_output_value: integer; @!pdf_draftmode_option: integer; @!pdf_draftmode_value: integer; -@!pdf_cur_Tm_a: integer; {|a| value of the current text matrix, ie the current - horizontal scaling factor} +@!pdf_cur_Tm_a: integer; {value |a| of the current text matrix, i.e., + the current horizontal scaling factor} +@!pdf_cur_Tm_c: integer; {value |c| of the current text matrix, i.e., slant (skew, obliqueness)} @!pdf_last_f: internal_font_number; {last font in PDF output page} @!pdf_last_fs: internal_font_number; {last font size in PDF output page} - @ Following procedures implement low-level subroutines to convert \TeX{} internal structures to PDF page description. @@ -16434,19 +16434,26 @@ end; procedure pdf_set_textmatrix(v, v_out: scaled; f: internal_font_number); {set the next starting point to |cur_h|, |cur_v|} -var pdf_new_Tm_a: integer; {|a| value of the new text matrix} +var pdf_new_Tm_a, pdf_new_Tm_c: integer; {values |a| and |c| of the new text matrix} begin pdf_out(" "); - if f = pdf_f then - pdf_new_Tm_a := pdf_cur_Tm_a - else if not pdf_font_auto_expand(f) then - pdf_new_Tm_a := 0 - else - pdf_new_Tm_a := pdf_font_expand_ratio(f); - if (pdf_new_Tm_a <> 0) or - ((pdf_new_Tm_a = 0) and (pdf_cur_Tm_a <> 0)) then begin - pdf_print_real(1000 + pdf_new_Tm_a, 3); - pdf_print(" 0 0 1 "); + if f = pdf_f then begin + pdf_new_Tm_a := pdf_cur_Tm_a; + pdf_new_Tm_c := pdf_cur_Tm_c; + end else begin + if pdf_font_auto_expand(f) then + pdf_new_Tm_a := 1000 + pdf_font_expand_ratio(f) + else + pdf_new_Tm_a := 1000; + pdf_new_Tm_c := 0 * font_slant(f); {don't activate it yet, needs fixing writefont.c etc.} + end; + if (pdf_new_Tm_a <> 1000) or (pdf_cur_Tm_a <> 1000) + or (pdf_new_Tm_c <> 0) or (pdf_cur_Tm_c <> 0) + then begin + pdf_print_real(pdf_new_Tm_a, 3); + pdf_print(" 0 "); + pdf_print_real(pdf_new_Tm_c, 3); + pdf_print(" 1 "); pdf_print_bp(cur_h - pdf_origin_h); pdf_h := pdf_origin_h + scaled_out; pdf_out(" "); @@ -16454,7 +16461,8 @@ begin pdf_v := pdf_origin_v - scaled_out; pdf_print(" Tm"); pdf_cur_Tm_a := pdf_new_Tm_a; - pdfassert(pdf_cur_Tm_a > -1000); + pdf_cur_Tm_c := pdf_new_Tm_c; + pdfassert(pdf_cur_Tm_a > 0); end else begin pdf_print_bp(cur_h - pdf_tj_start_h); {works only for unexpanded fonts} pdf_h := pdf_tj_start_h + scaled_out; @@ -16582,7 +16590,8 @@ begin pdf_last_f := null_font; pdf_last_fs := 0; pdf_doing_string := false; - pdf_cur_Tm_a := 0; + pdf_cur_Tm_a := 1000; + pdf_cur_Tm_c := 0; end; @ @p @@ -16596,18 +16605,16 @@ begin s_out:=0; pdf_end_string; pdf_set_font(f); end; - if pdf_cur_Tm_a = 0 then begin - s := divide_scaled_n(cur_h - (pdf_tj_start_h + pdf_delta_h), pdf_font_size(f),1000.0); + if pdf_cur_Tm_a = 1000 then begin + s := divide_scaled_n(cur_h - (pdf_tj_start_h + pdf_delta_h), pdf_font_size(f), 1000.0); s_out := scaled_out; end else begin s := divide_scaled_n(round_xn_over_d(cur_h - (pdf_tj_start_h + pdf_delta_h), 1000, - 1000 + pdf_cur_Tm_a), - pdf_font_size(f), - 1000.0); + pdf_cur_Tm_a), pdf_font_size(f), 1000.0); if abs(s) < @'100000 then begin s_out := round_xn_over_d(round_xn_over_d(pdf_font_size(f), abs(s), 1000), - 1000 + pdf_cur_Tm_a, 1000); + pdf_cur_Tm_a, 1000); if s < 0 then s_out := -s_out; end; @@ -27630,7 +27637,7 @@ by causing its width to be zero. begin if (e<>0)and((w-total_shrink[normal]+q<=z)or@| (total_shrink[sfi]<>0)or(total_shrink[fil]<>0)or (total_shrink[fill]<>0)or(total_shrink[filll]<>0)) then - begin flush_node(b); + begin list_ptr(b):=null; flush_node(b); b:=hpack(p,z-q,exactly); end else begin e:=0; @@ -34782,7 +34789,7 @@ var q:pointer; begin q := box(j); get_tex_box_width := 0; - if q <> min_halfword then + if q <> null then get_tex_box_width := width(q); end; @@ -34791,7 +34798,7 @@ var q:pointer; begin q := box(j); set_tex_box_width := 0; - if q <> min_halfword then + if q <> null then width(q) := v else set_tex_box_width := 1; @@ -34802,7 +34809,7 @@ var q:pointer; begin q := box(j); get_tex_box_height := 0; - if q <> min_halfword then + if q <> null then get_tex_box_height := height(q); end; @@ -34811,7 +34818,7 @@ var q:pointer; begin q := box(j); set_tex_box_height := 0; - if q <> min_halfword then + if q <> null then height(q) := v else set_tex_box_height := 1; @@ -34822,7 +34829,7 @@ var q:pointer; begin q := box(j); get_tex_box_depth := 0; - if q <> min_halfword then + if q <> null then get_tex_box_depth := depth(q); end; @@ -34831,7 +34838,7 @@ var q:pointer; begin q := box(j); set_tex_box_depth := 0; - if q <> min_halfword then + if q <> null then depth(q) := v else set_tex_box_depth := 1; diff --git a/Build/source/texk/web2c/luatexdir/tex/linebreak.c b/Build/source/texk/web2c/luatexdir/tex/linebreak.c index 910d26b9f27..768d1999af8 100644 --- a/Build/source/texk/web2c/luatexdir/tex/linebreak.c +++ b/Build/source/texk/web2c/luatexdir/tex/linebreak.c @@ -1,4 +1,4 @@ -/* $Id: linebreak.c 1102 2008-03-11 09:52:54Z taco $ */ +/* $Id: linebreak.c 1168 2008-04-15 13:43:34Z taco $ */ #include "luatex-api.h" #include <ptexlib.h> @@ -12,29 +12,31 @@ prevents this error message from appearing more than once per paragraph. */ -#define check_shrinkage(a) \ - if ((shrink_order((a))!=normal)&&(shrink((a))!=0)) \ +#define check_shrinkage(a) \ + if ((shrink_order((a))!=normal)&&(shrink((a))!=0)) \ a=finite_shrink((a)) -static boolean no_shrink_error_yet; /*have we complained about infinite shrinkage?*/ - -halfword -finite_shrink(halfword p) { /* recovers from infinite shrinkage */ - halfword q; /*new glue specification*/ - char *hlp[] = { - "The paragraph just ended includes some glue that has", - "infinite shrinkability, e.g., `\\hskip 0pt minus 1fil'.", - "Such glue doesn't belong there---it allows a paragraph", - "of any length to fit on one line. But it's safe to proceed,", - "since the offensive shrinkability has been made finite.", - NULL }; - if (no_shrink_error_yet) { - no_shrink_error_yet=false; - tex_error("Infinite glue shrinkage found in a paragraph",hlp); - } - q=new_spec(p); shrink_order(q)=normal; - delete_glue_ref(p); - return q; +static boolean no_shrink_error_yet; /*have we complained about infinite shrinkage? */ + +halfword finite_shrink(halfword p) +{ /* recovers from infinite shrinkage */ + halfword q; /*new glue specification */ + char *hlp[] = { + "The paragraph just ended includes some glue that has", + "infinite shrinkability, e.g., `\\hskip 0pt minus 1fil'.", + "Such glue doesn't belong there---it allows a paragraph", + "of any length to fit on one line. But it's safe to proceed,", + "since the offensive shrinkability has been made finite.", + NULL + }; + if (no_shrink_error_yet) { + no_shrink_error_yet = false; + tex_error("Infinite glue shrinkage found in a paragraph", hlp); + } + q = new_spec(p); + shrink_order(q) = normal; + delete_glue_ref(p); + return q; } /* A pointer variable |cur_p| runs through the given horizontal list as we look @@ -60,168 +62,177 @@ finite_shrink(halfword p) { /* recovers from infinite shrinkage */ stretchability and set |final_pass=true|. */ -static boolean second_pass; /* is this our second attempt to break this paragraph? */ -static boolean final_pass; /*is this our final attempt to break this paragraph? */ -static integer threshold; /* maximum badness on feasible lines */ +static boolean second_pass; /* is this our second attempt to break this paragraph? */ +static boolean final_pass; /*is this our final attempt to break this paragraph? */ +static integer threshold; /* maximum badness on feasible lines */ /* skipable nodes at the margins during character protrusion */ -#define cp_skipable(a) ((! is_char_node((a))) && \ - ((type((a)) == ins_node) \ - || (type((a)) == mark_node) \ - || (type((a)) == adjust_node) \ - || (type((a)) == penalty_node) \ - || ((type((a)) == whatsit_node) && \ - (subtype((a)) != pdf_refximage_node) && \ - (subtype((a)) != pdf_refxform_node)) \ - /* reference to an image or XObject form */ \ - || ((type((a)) == disc_node) && \ - (vlink_pre_break(a) == null) && \ - (vlink_post_break(a) == null) && \ - (vlink_no_break(a) == null)) \ - /* an empty |disc_node| */ \ - || ((type((a)) == math_node) && \ - (surround((a)) == 0)) \ - || ((type((a)) == kern_node) && \ - ((width((a)) == 0) || \ - (subtype((a)) == normal))) \ - || ((type((a)) == glue_node) && \ - (glue_ptr((a)) == zero_glue)) \ - || ((type((a)) == hlist_node) && \ - (width((a)) == 0) && \ - (height((a)) == 0) && \ - (depth((a)) == 0) && \ - (list_ptr((a)) == null)) \ - )) +#define cp_skipable(a) ((! is_char_node((a))) && \ + ((type((a)) == ins_node) \ + || (type((a)) == mark_node) \ + || (type((a)) == adjust_node) \ + || (type((a)) == penalty_node) \ + || ((type((a)) == whatsit_node) && \ + (subtype((a)) != pdf_refximage_node) && \ + (subtype((a)) != pdf_refxform_node)) \ + /* reference to an image or XObject form */ \ + || ((type((a)) == disc_node) && \ + (vlink_pre_break(a) == null) && \ + (vlink_post_break(a) == null) && \ + (vlink_no_break(a) == null)) \ + /* an empty |disc_node| */ \ + || ((type((a)) == math_node) && \ + (surround((a)) == 0)) \ + || ((type((a)) == kern_node) && \ + ((width((a)) == 0) || \ + (subtype((a)) == normal))) \ + || ((type((a)) == glue_node) && \ + (glue_ptr((a)) == zero_glue)) \ + || ((type((a)) == hlist_node) && \ + (width((a)) == 0) && \ + (height((a)) == 0) && \ + (depth((a)) == 0) && \ + (list_ptr((a)) == null)) \ + )) /* maximum fill level for |hlist_stack|*/ -#define max_hlist_stack 512 /* maybe good if larger than |2 * - max_quarterword|, so that box nesting - level would overflow first */ +#define max_hlist_stack 512 /* maybe good if larger than |2 * + max_quarterword|, so that box nesting + level would overflow first */ /* stack for |find_protchar_left()| and |find_protchar_right()| */ -static halfword hlist_stack[max_hlist_stack]; +static halfword hlist_stack[max_hlist_stack]; /* fill level for |hlist_stack| */ -static short hlist_stack_level = 0; +static short hlist_stack_level = 0; -void push_node(halfword p) { - if (hlist_stack_level >= max_hlist_stack) - pdf_error("push_node", "stack overflow"); - hlist_stack[hlist_stack_level++] = p; +void push_node(halfword p) +{ + if (hlist_stack_level >= max_hlist_stack) + pdf_error(maketexstring("push_node"), maketexstring("stack overflow")); + hlist_stack[hlist_stack_level++] = p; } -halfword pop_node (void) { - if (hlist_stack_level <= 0) /* would point to some bug */ - pdf_error("pop_node", "stack underflow (internal error)"); - return hlist_stack[--hlist_stack_level]; +halfword pop_node(void) +{ + if (hlist_stack_level <= 0) /* would point to some bug */ + pdf_error(maketexstring("pop_node"), + maketexstring("stack underflow (internal error)")); + return hlist_stack[--hlist_stack_level]; } #define null_font 0 -static integer max_stretch_ratio=0; /*maximal stretch ratio of expanded fonts*/ -static integer max_shrink_ratio=0; /*maximal shrink ratio of expanded fonts*/ -static integer cur_font_step=0; /*the current step of expanded fonts*/ - - -boolean check_expand_pars(internal_font_number f) { - internal_font_number k; - - if ((pdf_font_step(f) == 0) || ((pdf_font_stretch(f) == null_font) && - (pdf_font_shrink(f) == null_font))) - return false; - if (cur_font_step < 0) - cur_font_step = pdf_font_step(f); - else if (cur_font_step != pdf_font_step(f)) - pdf_error("font expansion", - "using fonts with different step of expansion in one paragraph is not allowed"); - k = pdf_font_stretch(f); - if( k != null_font) { - if (max_stretch_ratio < 0) - max_stretch_ratio = pdf_font_expand_ratio(k); - else if (max_stretch_ratio != pdf_font_expand_ratio(k)) - pdf_error("font expansion", - "using fonts with different limit of expansion in one paragraph is not allowed"); - } - k = pdf_font_shrink(f); - if( k != null_font) { - if (max_shrink_ratio < 0) - max_shrink_ratio = -pdf_font_expand_ratio(k); - else if (max_shrink_ratio != -pdf_font_expand_ratio(k)) - pdf_error("font expansion", - "using fonts with different limit of expansion in one paragraph is not allowed"); - } - return true; +static integer max_stretch_ratio = 0; /*maximal stretch ratio of expanded fonts */ +static integer max_shrink_ratio = 0; /*maximal shrink ratio of expanded fonts */ +static integer cur_font_step = 0; /*the current step of expanded fonts */ + + +boolean check_expand_pars(internal_font_number f) +{ + internal_font_number k; + + if ((pdf_font_step(f) == 0) || ((pdf_font_stretch(f) == null_font) && + (pdf_font_shrink(f) == null_font))) + return false; + if (cur_font_step < 0) + cur_font_step = pdf_font_step(f); + else if (cur_font_step != pdf_font_step(f)) + pdf_error(maketexstring("font expansion"), + maketexstring + ("using fonts with different step of expansion in one paragraph is not allowed")); + k = pdf_font_stretch(f); + if (k != null_font) { + if (max_stretch_ratio < 0) + max_stretch_ratio = pdf_font_expand_ratio(k); + else if (max_stretch_ratio != pdf_font_expand_ratio(k)) + pdf_error(maketexstring("font expansion"), + maketexstring + ("using fonts with different limit of expansion in one paragraph is not allowed")); + } + k = pdf_font_shrink(f); + if (k != null_font) { + if (max_shrink_ratio < 0) + max_shrink_ratio = -pdf_font_expand_ratio(k); + else if (max_shrink_ratio != -pdf_font_expand_ratio(k)) + pdf_error(maketexstring("font expansion"), + maketexstring + ("using fonts with different limit of expansion in one paragraph is not allowed")); + } + return true; } /* searches left to right from list head |l|, returns 1st non-skipable item*/ -/*public*/ halfword find_protchar_left(halfword l, boolean d) { - halfword t; - boolean run; - if ((vlink(l) != null) && (type(l) == hlist_node) && (width(l) == 0) - && (height(l) == 0) && (depth(l) == 0) && (list_ptr(l) == null)) { - l = vlink(l); /*for paragraph start with \.{\\parindent = 0pt*/ - } else if (d) { - while ((vlink(l) != null) && (!(is_char_node(l) || non_discardable(l)))) { - l = vlink(l); /* std.\ discardables at line break, \TeX book, p 95 */ - } - } - hlist_stack_level = 0; - run = true; - do { - t = l; - while (run && (type(l) == hlist_node) && (list_ptr(l) != null)) { - push_node(l); - l = list_ptr(l); - } - while (run && cp_skipable(l)) { - while ((vlink(l) == null) && (hlist_stack_level > 0)) { - l = pop_node(); /* don't visit this node again */ - } - if (vlink(l) != null) - l = vlink(l); - else if (hlist_stack_level == 0) - run = false; +/*public*/ halfword find_protchar_left(halfword l, boolean d) +{ + halfword t; + boolean run; + if ((vlink(l) != null) && (type(l) == hlist_node) && (width(l) == 0) + && (height(l) == 0) && (depth(l) == 0) && (list_ptr(l) == null)) { + l = vlink(l); /*for paragraph start with \.{\\parindent = 0pt */ + } else if (d) { + while ((vlink(l) != null) && (!(is_char_node(l) || non_discardable(l)))) { + l = vlink(l); /* std.\ discardables at line break, \TeX book, p 95 */ + } } - } while (t != l); - return l; + hlist_stack_level = 0; + run = true; + do { + t = l; + while (run && (type(l) == hlist_node) && (list_ptr(l) != null)) { + push_node(l); + l = list_ptr(l); + } + while (run && cp_skipable(l)) { + while ((vlink(l) == null) && (hlist_stack_level > 0)) { + l = pop_node(); /* don't visit this node again */ + } + if (vlink(l) != null) + l = vlink(l); + else if (hlist_stack_level == 0) + run = false; + } + } while (t != l); + return l; } /* searches right to left from list tail |r| to head |l|, returns 1st non-skipable item */ -/*public*/ halfword find_protchar_right(halfword l, halfword r) { - halfword t; - boolean run; - if (r == null) - return null; - hlist_stack_level = 0; - run = true; - do { - t = r; - while (run && (type(r) == hlist_node) && (list_ptr(r) != null)) { - push_node(l); - push_node(r); - l = list_ptr(r); - r = l; - while (vlink(r) != null) { - halfword s = r; - r = vlink(r); - alink(r) = s; - } - } - while (run && cp_skipable(r)) { - while ((r == l) && (hlist_stack_level > 0)) { - r = pop_node(); /* don't visit this node again */ - l = pop_node(); - } - if ((r != l) && (r != null) ) { - assert(vlink(alink(r))==r); - r = alink(r); - } else if ((r == l) && (hlist_stack_level == 0)) - run = false; - } - } while (t != r); - return r; +/*public*/ halfword find_protchar_right(halfword l, halfword r) +{ + halfword t; + boolean run; + if (r == null) + return null; + hlist_stack_level = 0; + run = true; + do { + t = r; + while (run && (type(r) == hlist_node) && (list_ptr(r) != null)) { + push_node(l); + push_node(r); + l = list_ptr(r); + r = l; + while (vlink(r) != null) { + halfword s = r; + r = vlink(r); + alink(r) = s; + } + } + while (run && cp_skipable(r)) { + while ((r == l) && (hlist_stack_level > 0)) { + r = pop_node(); /* don't visit this node again */ + l = pop_node(); + } + if ((r != l) && (r != null)) { + assert(vlink(alink(r)) == r); + r = alink(r); + } else if ((r == l) && (hlist_stack_level == 0)) + run = false; + } + } while (t != r); + return r; } #define left_pw(a) char_pw((a), left_side) @@ -232,30 +243,31 @@ boolean check_expand_pars(internal_font_number f) { |cur_break(break_node(q))| and |p| is the leftmost resp. rightmost node in the horizontal list representing the actual line */ -scaled total_pw (halfword first_p, halfword q, halfword p) { - halfword l, r; - l = (break_node(q) == null) ? first_p : cur_break(break_node(q)); - assert(vlink(alink(p))==p); - r = alink(r); - /* let's look at the right margin first */ - if ((p != null) && (type(p) == disc_node) && (vlink(pre_break(p)) != null)) { - /* a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */ - r = vlink(pre_break(p)); - while (vlink(r) != null) - r = vlink(r); - } else { - r = find_protchar_right(l, r); - } - /* now the left margin */ - if ((l != null) && (type(l) == disc_node)) { - if (vlink(post_break(l)) != null) { - l = vlink(post_break(l)); /* protrude the first char */ - goto DONE; +scaled total_pw(halfword first_p, halfword q, halfword p) +{ + halfword l, r; + l = (break_node(q) == null) ? first_p : cur_break(break_node(q)); + assert(vlink(alink(p)) == p); + r = alink(r); + /* let's look at the right margin first */ + if ((p != null) && (type(p) == disc_node) && (vlink(pre_break(p)) != null)) { + /* a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */ + r = vlink(pre_break(p)); + while (vlink(r) != null) + r = vlink(r); + } else { + r = find_protchar_right(l, r); } - } - l = find_protchar_left(l, true); -DONE: - return (left_pw(l) + right_pw(r)); + /* now the left margin */ + if ((l != null) && (type(l) == disc_node)) { + if (vlink(post_break(l)) != null) { + l = vlink(post_break(l)); /* protrude the first char */ + goto DONE; + } + } + l = find_protchar_left(l, true); + DONE: + return (left_pw(l) + right_pw(r)); } #endif @@ -271,14 +283,14 @@ DONE: */ typedef enum { - very_loose_fit = 0, /* fitness classification for lines stretching more than - their stretchability */ - loose_fit, /* fitness classification for lines stretching 0.5 to 1.0 of their - stretchability */ - decent_fit, /* fitness classification for all other lines*/ - tight_fit /* fitness classification for lines shrinking 0.5 to 1.0 of their - shrinkability */ -} fitness_value ; + very_loose_fit = 0, /* fitness classification for lines stretching more than + their stretchability */ + loose_fit, /* fitness classification for lines stretching 0.5 to 1.0 of their + stretchability */ + decent_fit, /* fitness classification for all other lines */ + tight_fit /* fitness classification for lines shrinking 0.5 to 1.0 of their + shrinkability */ +} fitness_value; /* The algorithm essentially determines the best possible way to achieve @@ -324,11 +336,11 @@ except that nodes with |line_number>easy_line| may be in any order relative to each other. */ -void -initialize_active (void) { - type(active)=hyphenated_node; - line_number(active)=max_halfword; - subtype(active)=0; /* the |subtype| is never examined */ +void initialize_active(void) +{ + type(active) = hyphenated_node; + line_number(active) = max_halfword; + subtype(active) = 0; /* the |subtype| is never examined */ } /* The passive node for a given breakpoint contains EIGHT fields: @@ -356,9 +368,9 @@ is used to help print out the paragraph when detailed information about the line-breaking computation is being displayed. */ -static halfword passive; /* most recent node on passive list */ -static halfword printed_node; /*most recent node that has been printed*/ -static halfword pass_number; /*the number of passive nodes allocated on this pass*/ +static halfword passive; /* most recent node on passive list */ +static halfword printed_node; /*most recent node that has been printed */ +static halfword pass_number; /*the number of passive nodes allocated on this pass */ /* @ The active list also contains ``delta'' nodes that help the algorithm @@ -398,15 +410,15 @@ macro makes such six-tuples convenient. #define save_active_width(a) prev_active_width[(a)] = active_width[(a)] #define restore_active_width(a) active_width[(a)] = prev_active_width[(a)] -static scaled active_width[10] = {0}; /*distance from first active node to~|cur_p|*/ -static scaled background[10] = {0}; /*length of an ``empty'' line*/ -static scaled break_width[10] = {0}; /*length being computed after current break*/ +static scaled active_width[10] = { 0 }; /*distance from first active node to~|cur_p| */ +static scaled background[10] = { 0 }; /*length of an ``empty'' line */ +static scaled break_width[10] = { 0 }; /*length being computed after current break */ -static boolean auto_breaking; /*make |auto_breaking| accessible out of |line_break|*/ -static boolean try_prev_break; /*force break at the previous legal breakpoint?*/ -static halfword prev_legal; /*the previous legal breakpoint*/ -static halfword rejected_cur_p; /*the last |cur_p| that has been rejected*/ -static boolean before_rejected_cur_p; /*|cur_p| is still before |rejected_cur_p|?*/ +static boolean auto_breaking; /*make |auto_breaking| accessible out of |line_break| */ +static boolean try_prev_break; /*force break at the previous legal breakpoint? */ +static halfword prev_legal; /*the previous legal breakpoint */ +static halfword rejected_cur_p; /*the last |cur_p| that has been rejected */ +static boolean before_rejected_cur_p; /*|cur_p| is still before |rejected_cur_p|? */ /* Let's state the principles of the delta nodes more precisely and concisely, so that the following programs will be less obscure. For each legal @@ -467,16 +479,16 @@ static boolean before_rejected_cur_p; /*|cur_p| is still before |rejected_cur_p| -static integer internal_pen_inter; /* running \.{\\localinterlinepenalty} */ -static integer internal_pen_broken; /* running \.{\\localbrokenpenalty} */ -static halfword internal_left_box; /* running \.{\\localleftbox} */ +static integer internal_pen_inter; /* running \.{\\localinterlinepenalty} */ +static integer internal_pen_broken; /* running \.{\\localbrokenpenalty} */ +static halfword internal_left_box; /* running \.{\\localleftbox} */ static integer internal_left_box_width; /* running \.{\\localleftbox} width */ static halfword init_internal_left_box; /* running \.{\\localleftbox} */ -static integer init_internal_left_box_width; /* running \.{\\localleftbox} width */ -static halfword internal_right_box; /* running \.{\\localrightbox} */ -static integer internal_right_box_width; /* running \.{\\localrightbox} width */ +static integer init_internal_left_box_width; /* running \.{\\localleftbox} width */ +static halfword internal_right_box; /* running \.{\\localrightbox} */ +static integer internal_right_box_width; /* running \.{\\localrightbox} width */ -static scaled disc_width [10] = {0}; /* the length of discretionary material preceding a break*/ +static scaled disc_width[10] = { 0 }; /* the length of discretionary material preceding a break */ /* As we consider various ways to end a line at |cur_p|, in a given line number class, we keep track of the best total demerits known, in an array with @@ -492,12 +504,12 @@ static scaled disc_width [10] = {0}; /* the length of discretionary material pre */ -static integer minimal_demerits[4]; /* best total demerits known for current - line class and position, given the fitness */ -static integer minimum_demerits; /* best total demerits known for current line class - and position */ -static halfword best_place[4]; /* how to achieve |minimal_demerits| */ -static halfword best_pl_line[4]; /*corresponding line number*/ +static integer minimal_demerits[4]; /* best total demerits known for current + line class and position, given the fitness */ +static integer minimum_demerits; /* best total demerits known for current line class + and position */ +static halfword best_place[4]; /* how to achieve |minimal_demerits| */ +static halfword best_pl_line[4]; /*corresponding line number */ /* @@ -518,21 +530,21 @@ Note that if |hang_indent=0|, the value of |hang_after| is irrelevant. @^length of lines@> @^hanging indentation@> */ -static halfword easy_line; /*line numbers |>easy_line| are equivalent in break nodes*/ -static halfword last_special_line; /*line numbers |>last_special_line| all have the same width */ -static scaled first_width; /*the width of all lines |<=last_special_line|, if - no \.{\\parshape} has been specified*/ -static scaled second_width; /*the width of all lines |>last_special_line|*/ -static scaled first_indent; /*left margin to go with |first_width|*/ -static scaled second_indent; /*left margin to go with |second_width|*/ +static halfword easy_line; /*line numbers |>easy_line| are equivalent in break nodes */ +static halfword last_special_line; /*line numbers |>last_special_line| all have the same width */ +static scaled first_width; /*the width of all lines |<=last_special_line|, if + no \.{\\parshape} has been specified */ +static scaled second_width; /*the width of all lines |>last_special_line| */ +static scaled first_indent; /*left margin to go with |first_width| */ +static scaled second_indent; /*left margin to go with |second_width| */ -static halfword best_bet; /*use this passive node and its predecessors */ -static integer fewest_demerits; /*the demerits associated with |best_bet|*/ -static halfword best_line; /*line number following the last line of the new paragraph*/ -static integer actual_looseness; /*the difference between |line_number(best_bet)| - and the optimum |best_line|*/ -static integer line_diff; /*the difference between the current line number and - the optimum |best_line|*/ +static halfword best_bet; /*use this passive node and its predecessors */ +static integer fewest_demerits; /*the demerits associated with |best_bet| */ +static halfword best_line; /*line number following the last line of the new paragraph */ +static integer actual_looseness; /*the difference between |line_number(best_bet)| + and the optimum |best_line| */ +static integer line_diff; /*the difference between the current line number and + the optimum |best_line| */ @@ -551,7 +563,7 @@ static integer line_diff; /*the difference between the current line number and #define store_background(a) {active_width[a]=background[a];} -#define act_width active_width[1] /*length from first active node to current node*/ +#define act_width active_width[1] /*length from first active node to current node */ #define kern_break() { \ if ((!is_char_node(vlink(cur_p))) && auto_breaking) \ @@ -581,17 +593,17 @@ static integer line_diff; /*the difference between the current line number and } \ } -#define inf_bad 10000 /* infinitely bad value */ +#define inf_bad 10000 /* infinitely bad value */ -static boolean do_last_line_fit; /* special algorithm for last line of paragraph? */ -static scaled fill_width[4]; /* infinite stretch components of |par_fill_skip| */ +static boolean do_last_line_fit; /* special algorithm for last line of paragraph? */ +static scaled fill_width[4]; /* infinite stretch components of |par_fill_skip| */ static scaled best_pl_short[4]; /* |shortfall| corresponding to |minimal_demerits| */ -static scaled best_pl_glue[4]; /*corresponding glue stretch or shrink */ +static scaled best_pl_glue[4]; /*corresponding glue stretch or shrink */ -#define awful_bad 07777777777 /* more than a billion demerits */ +#define awful_bad 07777777777 /* more than a billion demerits */ -#define before 0 /* |subtype| for math node that introduces a formula */ -#define after 1 /* |subtype| for math node that winds up a formula */ +#define before 0 /* |subtype| for math node that introduces a formula */ +#define after 1 /* |subtype| for math node that winds up a formula */ #define reset_disc_width(a) disc_width[(a)] = 0 @@ -636,241 +648,250 @@ static scaled best_pl_glue[4]; /*corresponding glue stretch or shrink */ disc_width[] line_break_dir */ -static void -compute_break_width (int break_type, int pdf_adjust_spacing, halfword p - /*, halfword s*/) { - halfword s; /* glue and other 'whitespace' to be skipped after a break - * used if unhyphenated, or post_break==empty */ - s=p; - if (break_type>unhyphenated_node && p!=null) { - /*@<Compute the discretionary |break_width| values@>;*/ - /* When |p| is a discretionary break, the length of a line - ``from |p| to |p|'' has to be defined properly so - that the other calculations work out. Suppose that the - pre-break text at |p| has length $l_0$, the post-break - text has length $l_1$, and the replacement text has length - |l|. Suppose also that |q| is the node following the - replacement text. Then length of a line from |p| to |q| - will be computed as $\gamma+\beta(q)-\alpha(|p|)$, where - $\beta(q)=\beta(|p|)-l_0+l$. The actual length will be - the background plus $l_1$, so the length from |p| to - |p| should be $\gamma+l_0+l_1-l$. If the post-break text - of the discretionary is empty, a break may also discard~|q|; - in that unusual case we subtract the length of~|q| and any - other nodes that will be discarded after the discretionary - break. - - TH: I don't quite understand the above remarks. - - The value of $l_0$ need not be computed, since |line_break| - will put it into the global variable |disc_width| before - calling |try_break|. - */ - /* In case of nested discretionaries, we always follow the no_break - path, as we are talking about the breaking on _this_ position. - */ - - halfword v; - for (v=vlink_no_break(p); v!=null; v=vlink(v)) { - /* @<Subtract the width of node |v| from |break_width|@>; */ - /* Replacement texts and discretionary texts are supposed to contain - only character nodes, kern nodes, and box or rule nodes.*/ - if (is_char_node(v)) { - if (is_rotated(line_break_dir)) { - break_width[1] -= (glyph_height(v)+glyph_depth(v)); - } else { - break_width[1] -= glyph_width(v); - } - if ((pdf_adjust_spacing > 1) && check_expand_pars(font(v))) { - set_prev_char_p(v); - sub_char_stretch(break_width[8],v); - sub_char_shrink(break_width[9],v); +static void +compute_break_width(int break_type, int pdf_adjust_spacing, halfword p + /*, halfword s */ ) +{ + halfword s; /* glue and other 'whitespace' to be skipped after a break + * used if unhyphenated, or post_break==empty */ + s = p; + if (break_type > unhyphenated_node && p != null) { + /*@<Compute the discretionary |break_width| values@>; */ + /* When |p| is a discretionary break, the length of a line + ``from |p| to |p|'' has to be defined properly so + that the other calculations work out. Suppose that the + pre-break text at |p| has length $l_0$, the post-break + text has length $l_1$, and the replacement text has length + |l|. Suppose also that |q| is the node following the + replacement text. Then length of a line from |p| to |q| + will be computed as $\gamma+\beta(q)-\alpha(|p|)$, where + $\beta(q)=\beta(|p|)-l_0+l$. The actual length will be + the background plus $l_1$, so the length from |p| to + |p| should be $\gamma+l_0+l_1-l$. If the post-break text + of the discretionary is empty, a break may also discard~|q|; + in that unusual case we subtract the length of~|q| and any + other nodes that will be discarded after the discretionary + break. + + TH: I don't quite understand the above remarks. + + The value of $l_0$ need not be computed, since |line_break| + will put it into the global variable |disc_width| before + calling |try_break|. + */ + /* In case of nested discretionaries, we always follow the no_break + path, as we are talking about the breaking on _this_ position. + */ + + halfword v; + for (v = vlink_no_break(p); v != null; v = vlink(v)) { + /* @<Subtract the width of node |v| from |break_width|@>; */ + /* Replacement texts and discretionary texts are supposed to contain + only character nodes, kern nodes, and box or rule nodes. */ + if (is_char_node(v)) { + if (is_rotated(line_break_dir)) { + break_width[1] -= (glyph_height(v) + glyph_depth(v)); + } else { + break_width[1] -= glyph_width(v); + } + if ((pdf_adjust_spacing > 1) && check_expand_pars(font(v))) { + set_prev_char_p(v); + sub_char_stretch(break_width[8], v); + sub_char_shrink(break_width[9], v); + } + } else { + switch (type(v)) { + case hlist_node: + case vlist_node: + if (!(dir_orthogonal(dir_primary[box_dir(v)], + dir_primary[line_break_dir]))) + break_width[1] -= width(v); + else + break_width[1] -= (depth(v) + height(v)); + break; + case kern_node: + if ((pdf_adjust_spacing > 1) && (subtype(v) == normal)) { + sub_kern_stretch(break_width[8], v); + sub_kern_shrink(break_width[9], v); + } + /* fall through */ + case rule_node: + break_width[1] -= width(v); + break; + case disc_node: + assert(vlink(v) == null); /* discs are _always_ last */ + v = no_break(v); + break; + default: + tconfusion("disc1"); + break; + } + } } - } else { - switch (type(v)) { - case hlist_node: - case vlist_node: - if (!(dir_orthogonal(dir_primary[box_dir(v)], - dir_primary[line_break_dir]))) - break_width[1] -= width(v); - else - break_width[1] -= (depth(v)+height(v)); - break; - case kern_node: - if ((pdf_adjust_spacing > 1) && (subtype(v) == normal)) { - sub_kern_stretch(break_width[8],v); - sub_kern_shrink(break_width[9],v); - } - /* fall through */ - case rule_node: - break_width[1] -= width(v); - break; - case disc_node: - assert(vlink(v)==null); /* discs are _always_ last */ - v = no_break(v); - break; - default: - tconfusion("disc1"); - break; + + for (v = vlink_post_break(p); v != null; v = vlink(v)) { + /* @<Add the width of node |v| to |break_width|@>; */ + if (is_char_node(v)) { + if (is_rotated(line_break_dir)) + break_width[1] += (glyph_height(v) + glyph_depth(v)); + else + break_width[1] += glyph_width(v); + if ((pdf_adjust_spacing > 1) && check_expand_pars(font(v))) { + set_prev_char_p(v); + add_char_stretch(break_width[8], v); + add_char_shrink(break_width[9], v); + } + } else { + switch (type(v)) { + case hlist_node: + case vlist_node: + if (!(dir_orthogonal(dir_primary[box_dir(v)], + dir_primary[line_break_dir]))) + break_width[1] += width(v); + else + break_width[1] += (depth(v) + height(v)); + break; + case kern_node: + if ((pdf_adjust_spacing > 1) && (subtype(v) == normal)) { + add_kern_stretch(break_width[8], v); + add_kern_shrink(break_width[9], v); + } + /* fall through */ + case rule_node: + break_width[1] += width(v); + break; + case disc_node: + assert(vlink(v) == null); + v = no_break(v); + break; + default: + tconfusion("disc2"); + } + } } - } - } - for (v=vlink_post_break(p); v!=null; v=vlink(v)) { - /* @<Add the width of node |v| to |break_width|@>; */ - if (is_char_node(v)) { - if (is_rotated(line_break_dir)) - break_width[1] += (glyph_height(v)+glyph_depth(v)); - else - break_width[1] += glyph_width(v); - if ((pdf_adjust_spacing > 1) && check_expand_pars(font(v))) { - set_prev_char_p(v); - add_char_stretch(break_width[8],v); - add_char_shrink(break_width[9],v); + do_one_seven_eight(add_disc_width_to_break_width); + if (vlink_post_break(p) == null) { + s = vlink(p); /* no post_break: 'skip' any 'whitespace' following */ + } else { + s = null; } - } else { - switch (type(v)) { - case hlist_node: - case vlist_node: - if (!(dir_orthogonal(dir_primary[box_dir(v)], - dir_primary[line_break_dir]))) - break_width[1] += width(v); - else - break_width[1] += (depth(v)+height(v)); - break; + } else { + s = p; /* unhyphenated: we need to 'skip' any 'whitespace' following */ + } + while (s != null) { + switch (type(s)) { + case glue_node: + /*@<Subtract glue from |break_width|@>; */ + { + halfword v = glue_ptr(s); + break_width[1] -= width(v); + break_width[2 + stretch_order(v)] -= stretch(v); + break_width[7] -= shrink(v); + } + break; + case penalty_node: + break; + case math_node: + break_width[1] -= surround(s); + break; case kern_node: - if ((pdf_adjust_spacing > 1) && (subtype(v) == normal)) { - add_kern_stretch(break_width[8],v); - add_kern_shrink(break_width[9],v); - } - /* fall through */ - case rule_node: - break_width[1] += width(v); - break; - case disc_node: - assert(vlink(v)==null); - v = no_break(v); - break; + if (subtype(s) != explicit) + return; + else + break_width[1] -= width(s); + break; default: - tconfusion("disc2"); - } - } + return; + }; + s = vlink(s); } - - do_one_seven_eight(add_disc_width_to_break_width); - if (vlink_post_break(p)==null) { - s=vlink(p); /* no post_break: 'skip' any 'whitespace' following */ - } else { - s=null; - } - } else { - s = p; /* unhyphenated: we need to 'skip' any 'whitespace' following */ - } - while (s!=null) { - switch (type(s)) { - case glue_node: - /*@<Subtract glue from |break_width|@>;*/ - {halfword v=glue_ptr(s); - break_width[1] -= width(v); - break_width[2+stretch_order(v)] -= stretch(v); - break_width[7] -= shrink(v); - }break; - case penalty_node: - break; - case math_node: - break_width[1] -= surround(s); - break; - case kern_node: - if (subtype(s)!=explicit) - return; - else - break_width[1] -= width(s); - break; - default: - return; - }; - s=vlink(s); - } } static void print_break_node(halfword q, fitness_value fit_class, - quarterword break_type, halfword cur_p) { - /* @<Print a symbolic description of the new break node@> */ - tprint_nl("@@"); - print_int(serial(passive)); - tprint(": line "); - print_int(line_number(q)-1); - print_char('.'); - print_int(fit_class); - if (break_type==hyphenated_node) - print_char('-'); - tprint(" t="); - print_int(total_demerits(q)); - if (do_last_line_fit) { - /*@<Print additional data in the new active node@>; */ - tprint(" s="); - print_scaled(active_short(q)); - if (cur_p==null) - tprint(" a="); - else - tprint(" g="); - print_scaled(active_glue(q)); - } - tprint(" -> @@"); - if (prev_break(passive)==null) - print_char('0'); - else - print_int(serial(prev_break(passive))); + quarterword break_type, halfword cur_p) +{ + /* @<Print a symbolic description of the new break node@> */ + tprint_nl("@@"); + print_int(serial(passive)); + tprint(": line "); + print_int(line_number(q) - 1); + print_char('.'); + print_int(fit_class); + if (break_type == hyphenated_node) + print_char('-'); + tprint(" t="); + print_int(total_demerits(q)); + if (do_last_line_fit) { + /*@<Print additional data in the new active node@>; */ + tprint(" s="); + print_scaled(active_short(q)); + if (cur_p == null) + tprint(" a="); + else + tprint(" g="); + print_scaled(active_glue(q)); + } + tprint(" -> @@"); + if (prev_break(passive) == null) + print_char('0'); + else + print_int(serial(prev_break(passive))); } -static void +static void print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi, - integer d, boolean artificial_demerits) { - /* @<Print a symbolic description of this feasible break@>;*/ - if (printed_node!=cur_p) { - /* @<Print the list between |printed_node| and |cur_p|, then - set |printed_node:=cur_p|@>;*/ - tprint_nl(""); - if (cur_p==null) { - short_display(vlink(printed_node)); - } else { - halfword save_link=vlink(cur_p); - vlink(cur_p)=null; - tprint_nl(""); - short_display(vlink(printed_node)); - vlink(cur_p)=save_link; + integer d, boolean artificial_demerits) +{ + /* @<Print a symbolic description of this feasible break@>; */ + if (printed_node != cur_p) { + /* @<Print the list between |printed_node| and |cur_p|, then + set |printed_node:=cur_p|@>; */ + tprint_nl(""); + if (cur_p == null) { + short_display(vlink(printed_node)); + } else { + halfword save_link = vlink(cur_p); + vlink(cur_p) = null; + tprint_nl(""); + short_display(vlink(printed_node)); + vlink(cur_p) = save_link; + } + printed_node = cur_p; } - printed_node=cur_p; - } - tprint_nl("@"); - if (cur_p==null) { - tprint_esc("par"); - } else if (type(cur_p)!=glue_node) { - if (type(cur_p)==penalty_node) tprint_esc("penalty"); - else if (type(cur_p)==disc_node) tprint_esc("discretionary"); - else if (type(cur_p)==kern_node) tprint_esc("kern"); - else tprint_esc("math"); - } - tprint(" via @@"); - if (break_node(r)==null) - print_char('0'); - else - print_int(serial(break_node(r))); - tprint(" b="); - if (b>inf_bad) - print_char('*'); - else - print_int(b); - tprint(" p="); - print_int(pi); - tprint(" d="); - if (artificial_demerits) - print_char('*'); - else - print_int(d); + tprint_nl("@"); + if (cur_p == null) { + tprint_esc("par"); + } else if (type(cur_p) != glue_node) { + if (type(cur_p) == penalty_node) + tprint_esc("penalty"); + else if (type(cur_p) == disc_node) + tprint_esc("discretionary"); + else if (type(cur_p) == kern_node) + tprint_esc("kern"); + else + tprint_esc("math"); + } + tprint(" via @@"); + if (break_node(r) == null) + print_char('0'); + else + print_int(serial(break_node(r))); + tprint(" b="); + if (b > inf_bad) + print_char('*'); + else + print_int(b); + tprint(" p="); + print_int(pi); + tprint(" d="); + if (artificial_demerits) + print_char('*'); + else + print_int(d); } #define add_disc_width_to_active_width(a) active_width[a] += disc_width[a] @@ -898,7 +919,7 @@ print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi, #define total_font_stretch cur_active_width[8] #define total_font_shrink cur_active_width[9] -#define max_dimen 07777777777 /* $2^{30}-1$ */ +#define max_dimen 07777777777 /* $2^{30}-1$ */ #define left_side 0 #define right_side 1 @@ -909,1187 +930,1238 @@ print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi, font(cp) = font((a)); \ do_subst_font(cp, 1000); \ if (font(cp) != font((a))) \ - margin_kern_stretch += (left_pw((a)) - left_pw(cp)); \ + margin_kern_stretch += (left_pw((a)) - left_pw(cp)); \ font(cp) = font((a)); \ do_subst_font(cp, -1000); \ if (font(cp) != font((a))) \ - margin_kern_shrink += (left_pw(cp) - left_pw((a))); \ + margin_kern_shrink += (left_pw(cp) - left_pw((a))); \ } -static void +static void ext_try_break(integer pi, quarterword break_type, int pdf_adjust_spacing, - int par_shape_ptr, - int adj_demerits, - int tracing_paragraphs, + int par_shape_ptr, + int adj_demerits, + int tracing_paragraphs, int pdf_protrude_chars, int line_penalty, - int last_line_fit, - int double_hyphen_demerits, - int final_hyphen_demerits, - halfword first_p, - halfword cur_p) { - /* CONTINUE,DEACTIVATE,FOUND,NOT_FOUND; */ - pointer r; /* runs through the active list */ - scaled margin_kern_stretch; - scaled margin_kern_shrink; - halfword lp, rp, cp; - halfword prev_r; /* stays a step behind |r| */ - halfword prev_prev_r; /*a step behind |prev_r|, if |type(prev_r)=delta_node|*/ - halfword old_l; /* maximum line number in current equivalence class of lines */ - boolean no_break_yet; /* have we found a feasible break at |cur_p|? */ - halfword q; /*points to a new node being created*/ - halfword l; /*line number of current active node*/ - boolean node_r_stays_active; /*should node |r| remain in the active list?*/ - scaled line_width; /*the current line will be justified to this width*/ - fitness_value fit_class; /*possible fitness class of test line*/ - halfword b; /*badness of test line*/ - integer d; /*demerits of test line*/ - boolean artificial_demerits; /*has |d| been forced to zero?*/ - - scaled shortfall; /*used in badness calculations*/ - scaled g; /*glue stretch or shrink of test line, adjustment for last line*/ - scaled cur_active_width[10] = {0}; /*distance from current active node*/ - - line_width=0; g=0; prev_prev_r=null; - /*@<Make sure that |pi| is in the proper range@>;*/ - if (pi>=inf_penalty) { - return; /* this breakpoint is inhibited by infinite penalty */ - } else if (pi<=-inf_penalty) { - pi=eject_penalty; /*this breakpoint will be forced*/ - } - - no_break_yet=true; - prev_r=active; - old_l=0; - do_all_eight(copy_to_cur_active); - - while (1) { - r=vlink(prev_r); - /* @<If node |r| is of type |delta_node|, update |cur_active_width|, - set |prev_r| and |prev_prev_r|, then |goto continue|@>; */ - /* The following code uses the fact that |type(active)<>delta_node| */ - if (type(r)==delta_node) { - do_all_eight(update_width); /* IMPLICIT ,r */ - prev_prev_r=prev_r; - prev_r=r; - continue; + int last_line_fit, + int double_hyphen_demerits, + int final_hyphen_demerits, halfword first_p, halfword cur_p) +{ + /* CONTINUE,DEACTIVATE,FOUND,NOT_FOUND; */ + pointer r; /* runs through the active list */ + scaled margin_kern_stretch; + scaled margin_kern_shrink; + halfword lp, rp, cp; + halfword prev_r; /* stays a step behind |r| */ + halfword prev_prev_r; /*a step behind |prev_r|, if |type(prev_r)=delta_node| */ + halfword old_l; /* maximum line number in current equivalence class of lines */ + boolean no_break_yet; /* have we found a feasible break at |cur_p|? */ + halfword q; /*points to a new node being created */ + halfword l; /*line number of current active node */ + boolean node_r_stays_active; /*should node |r| remain in the active list? */ + scaled line_width; /*the current line will be justified to this width */ + fitness_value fit_class; /*possible fitness class of test line */ + halfword b; /*badness of test line */ + integer d; /*demerits of test line */ + boolean artificial_demerits; /*has |d| been forced to zero? */ + + scaled shortfall; /*used in badness calculations */ + scaled g; /*glue stretch or shrink of test line, adjustment for last line */ + scaled cur_active_width[10] = { 0 }; /*distance from current active node */ + + line_width = 0; + g = 0; + prev_prev_r = null; + /*@<Make sure that |pi| is in the proper range@>; */ + if (pi >= inf_penalty) { + return; /* this breakpoint is inhibited by infinite penalty */ + } else if (pi <= -inf_penalty) { + pi = eject_penalty; /*this breakpoint will be forced */ } - /* @<If a line number class has ended, create new active nodes for - the best feasible breaks in that class; then |return| - if |r=active|, otherwise compute the new |line_width|@>; */ - /* The first part of the following code is part of \TeX's inner loop, so - we don't want to waste any time. The current active node, namely node |r|, - contains the line number that will be considered next. At the end of the - list we have arranged the data structure so that |r=active| and - |line_number(active)>old_l|. - */ - l=line_number(r); - if (l>old_l) { /* now we are no longer in the inner loop */ - if ((minimum_demerits<awful_bad)&&((old_l!=easy_line)||(r==active))) { - /*@<Create new active nodes for the best feasible breaks just found@>*/ - /* It is not necessary to create new active nodes having |minimal_demerits| - greater than - |minimum_demerits+abs(adj_demerits)|, since such active nodes will never - be chosen in the final paragraph breaks. This observation allows us to - omit a substantial number of feasible breakpoints from further consideration. - */ - if (no_break_yet) { - no_break_yet=false; - do_all_eight(set_break_width_to_background); - compute_break_width(break_type, pdf_adjust_spacing, cur_p); - } - /* @<Insert a delta node to prepare for breaks at |cur_p|@>;*/ - /* We use the fact that |type(active)<>delta_node|.*/ - if (type(prev_r)==delta_node) {/* modify an existing delta node */ - do_all_eight(convert_to_break_width); /* IMPLICIT prev_r */ - } else if (prev_r==active) { /* no delta node needed at the beginning */ - do_all_eight(store_break_width); - } else { - q=new_node(delta_node,0); - vlink(q)=r; - do_all_eight(new_delta_to_break_width); /* IMPLICIT q */ - vlink(prev_r)=q; - prev_prev_r=prev_r; - prev_r=q; + + no_break_yet = true; + prev_r = active; + old_l = 0; + do_all_eight(copy_to_cur_active); + + while (1) { + r = vlink(prev_r); + /* @<If node |r| is of type |delta_node|, update |cur_active_width|, + set |prev_r| and |prev_prev_r|, then |goto continue|@>; */ + /* The following code uses the fact that |type(active)<>delta_node| */ + if (type(r) == delta_node) { + do_all_eight(update_width); /* IMPLICIT ,r */ + prev_prev_r = prev_r; + prev_r = r; + continue; } + /* @<If a line number class has ended, create new active nodes for + the best feasible breaks in that class; then |return| + if |r=active|, otherwise compute the new |line_width|@>; */ + /* The first part of the following code is part of \TeX's inner loop, so + we don't want to waste any time. The current active node, namely node |r|, + contains the line number that will be considered next. At the end of the + list we have arranged the data structure so that |r=active| and + |line_number(active)>old_l|. + */ + l = line_number(r); + if (l > old_l) { /* now we are no longer in the inner loop */ + if ((minimum_demerits < awful_bad) + && ((old_l != easy_line) || (r == active))) { + /*@<Create new active nodes for the best feasible breaks just found@> */ + /* It is not necessary to create new active nodes having |minimal_demerits| + greater than + |minimum_demerits+abs(adj_demerits)|, since such active nodes will never + be chosen in the final paragraph breaks. This observation allows us to + omit a substantial number of feasible breakpoints from further consideration. + */ + if (no_break_yet) { + no_break_yet = false; + do_all_eight(set_break_width_to_background); + compute_break_width(break_type, pdf_adjust_spacing, cur_p); + } + /* @<Insert a delta node to prepare for breaks at |cur_p|@>; */ + /* We use the fact that |type(active)<>delta_node|. */ + if (type(prev_r) == delta_node) { /* modify an existing delta node */ + do_all_eight(convert_to_break_width); /* IMPLICIT prev_r */ + } else if (prev_r == active) { /* no delta node needed at the beginning */ + do_all_eight(store_break_width); + } else { + q = new_node(delta_node, 0); + vlink(q) = r; + do_all_eight(new_delta_to_break_width); /* IMPLICIT q */ + vlink(prev_r) = q; + prev_prev_r = prev_r; + prev_r = q; + } - if (abs(adj_demerits)>=awful_bad-minimum_demerits) - minimum_demerits=awful_bad-1; - else - minimum_demerits += abs(adj_demerits); - for (fit_class=very_loose_fit;fit_class<=tight_fit;fit_class++) { - if (minimal_demerits[fit_class]<=minimum_demerits) { - /* @<Insert a new active node from |best_place[fit_class]| - to |cur_p|@>; */ - /* When we create an active node, we also create the corresponding - passive node. - */ - q=new_node(passive_node,0); - vlink(q)=passive; - passive=q; - cur_break(q)=cur_p; - incr(pass_number); - serial(q)=pass_number; - prev_break(q)=best_place[fit_class]; - /*Here we keep track of the subparagraph penalties in the break nodes*/ - passive_pen_inter(q)=internal_pen_inter; - passive_pen_broken(q)=internal_pen_broken; - passive_last_left_box(q)=internal_left_box; - passive_last_left_box_width(q)=internal_left_box_width; - if (prev_break(q)!=null) { - passive_left_box(q)=passive_last_left_box(prev_break(q)); - passive_left_box_width(q)=passive_last_left_box_width(prev_break(q)); + if (abs(adj_demerits) >= awful_bad - minimum_demerits) + minimum_demerits = awful_bad - 1; + else + minimum_demerits += abs(adj_demerits); + for (fit_class = very_loose_fit; fit_class <= tight_fit; + fit_class++) { + if (minimal_demerits[fit_class] <= minimum_demerits) { + /* @<Insert a new active node from |best_place[fit_class]| + to |cur_p|@>; */ + /* When we create an active node, we also create the corresponding + passive node. + */ + q = new_node(passive_node, 0); + vlink(q) = passive; + passive = q; + cur_break(q) = cur_p; + incr(pass_number); + serial(q) = pass_number; + prev_break(q) = best_place[fit_class]; + /*Here we keep track of the subparagraph penalties in the break nodes */ + passive_pen_inter(q) = internal_pen_inter; + passive_pen_broken(q) = internal_pen_broken; + passive_last_left_box(q) = internal_left_box; + passive_last_left_box_width(q) = + internal_left_box_width; + if (prev_break(q) != null) { + passive_left_box(q) = + passive_last_left_box(prev_break(q)); + passive_left_box_width(q) = + passive_last_left_box_width(prev_break(q)); + } else { + passive_left_box(q) = init_internal_left_box; + passive_left_box_width(q) = + init_internal_left_box_width; + } + passive_right_box(q) = internal_right_box; + passive_right_box_width(q) = internal_right_box_width; + q = new_node(break_type, fit_class); + break_node(q) = passive; + line_number(q) = best_pl_line[fit_class] + 1; + total_demerits(q) = minimal_demerits[fit_class]; + if (do_last_line_fit) { + /*@<Store \(a)additional data in the new active node@> */ + /* Here we save these data in the active node + representing a potential line break. */ + active_short(q) = best_pl_short[fit_class]; + active_glue(q) = best_pl_glue[fit_class]; + } + vlink(q) = r; + vlink(prev_r) = q; + prev_r = q; + if (tracing_paragraphs > 0) + print_break_node(q, fit_class, break_type, cur_p); + } + minimal_demerits[fit_class] = awful_bad; + } + minimum_demerits = awful_bad; + /* @<Insert a delta node to prepare for the next active node@>; */ + /* When the following code is performed, we will have just inserted at + least one active node before |r|, so |type(prev_r)<>delta_node|. + */ + if (r != active) { + q = new_node(delta_node, 0); + vlink(q) = r; + do_all_eight(new_delta_from_break_width); /* IMPLICIT q */ + vlink(prev_r) = q; + prev_prev_r = prev_r; + prev_r = q; + } + } + if (r == active) + return; + /*@<Compute the new line width@>; */ + /* When we come to the following code, we have just encountered + the first active node~|r| whose |line_number| field contains + |l|. Thus we want to compute the length of the $l\mskip1mu$th + line of the current paragraph. Furthermore, we want to set + |old_l| to the last number in the class of line numbers + equivalent to~|l|. + */ + if (l > easy_line) { + old_l = max_halfword - 1; + line_width = second_width; } else { - passive_left_box(q)=init_internal_left_box; - passive_left_box_width(q)=init_internal_left_box_width; + old_l = l; + if (l > last_special_line) { + line_width = second_width; + } else if (par_shape_ptr == null) { + line_width = first_width; + } else { + line_width = varmem[(par_shape_ptr + 2 * l + 1)].cint; + } } - passive_right_box(q)=internal_right_box; - passive_right_box_width(q)=internal_right_box_width; - q=new_node(break_type,fit_class); - break_node(q)=passive; - line_number(q)=best_pl_line[fit_class]+1; - total_demerits(q)=minimal_demerits[fit_class]; - if (do_last_line_fit) { - /*@<Store \(a)additional data in the new active node@>*/ - /* Here we save these data in the active node - representing a potential line break.*/ - active_short(q)=best_pl_short[fit_class]; - active_glue(q)=best_pl_glue[fit_class]; + } + /* /If a line number class has ended, create new active nodes for + the best feasible breaks in that class; then |return| + if |r=active|, otherwise compute the new |line_width|@>; */ + + /* @<Consider the demerits for a line from |r| to |cur_p|; + deactivate node |r| if it should no longer be active; + then |goto continue| if a line from |r| to |cur_p| is infeasible, + otherwise record a new feasible break@>; */ + artificial_demerits = false; + shortfall = line_width - cur_active_width[1] + - ((break_node(r) == null) + ? init_internal_left_box_width + : passive_last_left_box_width(break_node(r))) + - internal_right_box_width; + if (pdf_protrude_chars > 1) { + halfword l, o; + l = (break_node(r) == null) ? first_p : cur_break(break_node(r)); + if (cur_p == null) { + o = null; + } else { /* TODO if (is_character_node(alink(cur_p))) */ + o = alink(cur_p); + assert(vlink(o) == cur_p); + } + /* let's look at the right margin first */ + if ((cur_p != null) && (type(cur_p) == disc_node) + && (vlink_pre_break(cur_p) != null)) { + /* a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */ + o = tlink_pre_break(cur_p); + } else { + o = find_protchar_right(l, o); + } + /* now the left margin */ + if ((l != null) && (type(l) == disc_node) + && (vlink_post_break(l) != null)) { + /* FIXME: first 'char' could be a disc! */ + l = vlink_post_break(l); /* protrude the first char */ + } else { + l = find_protchar_left(l, true); } - vlink(q)=r; - vlink(prev_r)=q; - prev_r=q; - if (tracing_paragraphs>0) - print_break_node(q,fit_class,break_type,cur_p); - } - minimal_demerits[fit_class]=awful_bad; + shortfall += (left_pw(l) + right_pw(o)); } - minimum_demerits=awful_bad; - /* @<Insert a delta node to prepare for the next active node@>;*/ - /* When the following code is performed, we will have just inserted at - least one active node before |r|, so |type(prev_r)<>delta_node|. - */ - if (r!=active) { - q=new_node(delta_node,0); - vlink(q)=r; - do_all_eight(new_delta_from_break_width); /* IMPLICIT q */ - vlink(prev_r)=q; - prev_prev_r=prev_r; - prev_r=q; + if ((shortfall != 0) && (pdf_adjust_spacing > 1)) { + margin_kern_stretch = 0; + margin_kern_shrink = 0; + if (pdf_protrude_chars > 1) { + /* @<Calculate variations of marginal kerns@>; */ + lp = last_leftmost_char; + rp = last_rightmost_char; + cp = raw_glyph_node(); + if (lp != null) { + cal_margin_kern_var(lp); + } + if (rp != null) { + cal_margin_kern_var(rp); + } + flush_node(cp); + } + if ((shortfall > 0) + && ((total_font_stretch + margin_kern_stretch) > 0)) { + if ((total_font_stretch + margin_kern_stretch) > shortfall) + shortfall = ((total_font_stretch + margin_kern_stretch) / + (max_stretch_ratio / cur_font_step)) / 2; + else + shortfall -= (total_font_stretch + margin_kern_stretch); + } else if ((shortfall < 0) + && ((total_font_shrink + margin_kern_shrink) > 0)) { + if ((total_font_shrink + margin_kern_shrink) > -shortfall) + shortfall = -((total_font_shrink + margin_kern_shrink) / + (max_shrink_ratio / cur_font_step)) / 2; + else + shortfall += (total_font_shrink + margin_kern_shrink); + } } - } - if (r==active) - return; - /*@<Compute the new line width@>;*/ - /* When we come to the following code, we have just encountered - the first active node~|r| whose |line_number| field contains - |l|. Thus we want to compute the length of the $l\mskip1mu$th - line of the current paragraph. Furthermore, we want to set - |old_l| to the last number in the class of line numbers - equivalent to~|l|. - */ - if (l>easy_line) { - old_l=max_halfword-1; - line_width=second_width; - } else { - old_l=l; - if (l>last_special_line) { - line_width=second_width; - } else if (par_shape_ptr==null) { - line_width=first_width; + if (shortfall > 0) { + /* @<Set the value of |b| to the badness for stretching the line, + and compute the corresponding |fit_class|@> */ + + /* When a line must stretch, the available stretchability can be + found in the subarray |cur_active_width[2..6]|, in units of + points, sfi, fil, fill and filll. + + The present section is part of \TeX's inner loop, and it is + most often performed when the badness is infinite; therefore + it is worth while to make a quick test for large width excess + and small stretchability, before calling the |badness| + subroutine. @^inner loop@> */ + + if ((cur_active_width[3] != 0) || (cur_active_width[4] != 0) || + (cur_active_width[5] != 0) || (cur_active_width[6] != 0)) { + if (do_last_line_fit) { + if (cur_p == null) { /* the last line of a paragraph */ + /* @<Perform computations for last line and |goto found|@>; */ + + /* Here we compute the adjustment |g| and badness |b| for + a line from |r| to the end of the paragraph. When any + of the criteria for adjustment is violated we fall + through to the normal algorithm. + + The last line must be too short, and have infinite + stretch entirely due to |par_fill_skip|. */ + if ((active_short(r) == 0) || (active_glue(r) <= 0)) + /* previous line was neither stretched nor shrunk, or + was infinitely bad */ + goto NOT_FOUND; + if ((cur_active_width[3] != fill_width[0]) || + (cur_active_width[4] != fill_width[1]) || + (cur_active_width[5] != fill_width[2]) || + (cur_active_width[6] != fill_width[3])) + /* infinite stretch of this line not entirely due to + |par_fill_skip| */ + goto NOT_FOUND; + if (active_short(r) > 0) + g = cur_active_width[2]; + else + g = cur_active_width[7]; + if (g <= 0) + /*no finite stretch resp.\ no shrink */ + goto NOT_FOUND; + arith_error = false; + g = fract(g, active_short(r), active_glue(r), + max_dimen); + if (last_line_fit < 1000) + g = fract(g, last_line_fit, 1000, max_dimen); + if (arith_error) { + if (active_short(r) > 0) + g = max_dimen; + else + g = -max_dimen; + } + if (g > 0) { + /*@<Set the value of |b| to the badness of the last line + for stretching, compute the corresponding |fit_class, + and |goto found||@> */ + /* These badness computations are rather similar to + those of the standard algorithm, with the adjustment + amount |g| replacing the |shortfall|. */ + if (g > shortfall) + g = shortfall; + if (g > 7230584) { + if (cur_active_width[2] < 1663497) { + b = inf_bad; + fit_class = very_loose_fit; + goto FOUND; + } + } + b = badness(g, cur_active_width[2]); + if (b > 99) { + fit_class = very_loose_fit; + } else if (b > 12) { + fit_class = loose_fit; + } else { + fit_class = decent_fit; + } + goto FOUND; + } else if (g < 0) { + /*@<Set the value of |b| to the badness of the last line + for shrinking, compute the corresponding |fit_class, + and |goto found||@>; */ + if (-g > cur_active_width[7]) + g = -cur_active_width[7]; + b = badness(-g, cur_active_width[7]); + if (b > 12) + fit_class = tight_fit; + else + fit_class = decent_fit; + goto FOUND; + } + } + NOT_FOUND: + shortfall = 0; + } + b = 0; + fit_class = decent_fit; /* infinite stretch */ + } else { + if (shortfall > 7230584 && cur_active_width[2] < 1663497) { + b = inf_bad; + fit_class = very_loose_fit; + } else { + b = badness(shortfall, cur_active_width[2]); + if (b > 99) { + fit_class = very_loose_fit; + } else if (b > 12) { + fit_class = loose_fit; + } else { + fit_class = decent_fit; + } + } + } } else { - line_width=varmem[(par_shape_ptr+2*l+1)].cint; + /* Set the value of |b| to the badness for shrinking the line, + and compute the corresponding |fit_class|@>; */ + /* Shrinkability is never infinite in a paragraph; we can shrink + the line from |r| to |cur_p| by at most + |cur_active_width[7]|. */ + if (-shortfall > cur_active_width[7]) + b = inf_bad + 1; + else + b = badness(-shortfall, cur_active_width[7]); + if (b > 12) + fit_class = tight_fit; + else + fit_class = decent_fit; } - } - } - /* /If a line number class has ended, create new active nodes for - the best feasible breaks in that class; then |return| - if |r=active|, otherwise compute the new |line_width|@>; */ - - /* @<Consider the demerits for a line from |r| to |cur_p|; - deactivate node |r| if it should no longer be active; - then |goto continue| if a line from |r| to |cur_p| is infeasible, - otherwise record a new feasible break@>; */ - artificial_demerits=false; - shortfall = line_width - - cur_active_width[1] - -(( break_node(r)==null) - ? init_internal_left_box_width - : passive_last_left_box_width(break_node(r))) - - internal_right_box_width; - if (pdf_protrude_chars > 1) { - halfword l, o; - l = (break_node(r) == null) ? first_p : cur_break(break_node(r)); - if (cur_p==null) { - o = null; - } else { /* TODO if (is_character_node(alink(cur_p))) */ - o = alink(cur_p); - assert(vlink(o)==cur_p); - } - /* let's look at the right margin first */ - if ((cur_p != null) && (type(cur_p) == disc_node) && (vlink_pre_break(cur_p) != null)) { - /* a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */ - o = tlink_pre_break(cur_p); - } else { - o = find_protchar_right(l, o); - } - /* now the left margin */ - if ((l != null) && (type(l) == disc_node) && (vlink_post_break(l) != null)) { - /* FIXME: first 'char' could be a disc! */ - l = vlink_post_break(l); /* protrude the first char */ - } else { - l = find_protchar_left(l, true); - } - shortfall += (left_pw(l) + right_pw(o)); - } - if ((shortfall != 0) && (pdf_adjust_spacing > 1)) { - margin_kern_stretch = 0; - margin_kern_shrink = 0; - if (pdf_protrude_chars > 1) { - /* @<Calculate variations of marginal kerns@>;*/ - lp = last_leftmost_char; - rp = last_rightmost_char; - cp = raw_glyph_node(); - if (lp != null) { - cal_margin_kern_var(lp); - } - if (rp != null) { - cal_margin_kern_var(rp); - } - flush_node(cp); - } - if ((shortfall > 0) && ((total_font_stretch + margin_kern_stretch) > 0)) { - if ((total_font_stretch + margin_kern_stretch) > shortfall) - shortfall = ((total_font_stretch + margin_kern_stretch) / - (max_stretch_ratio / cur_font_step)) / 2; - else - shortfall -= (total_font_stretch + margin_kern_stretch); - } else if ((shortfall < 0) && ((total_font_shrink + margin_kern_shrink) > 0)) { - if ((total_font_shrink + margin_kern_shrink) > -shortfall) - shortfall = -((total_font_shrink + margin_kern_shrink) / - (max_shrink_ratio / cur_font_step)) / 2; - else - shortfall += (total_font_shrink + margin_kern_shrink); - } - } - if (shortfall>0) { - /* @<Set the value of |b| to the badness for stretching the line, - and compute the corresponding |fit_class|@> */ - - /* When a line must stretch, the available stretchability can be - found in the subarray |cur_active_width[2..6]|, in units of - points, sfi, fil, fill and filll. - - The present section is part of \TeX's inner loop, and it is - most often performed when the badness is infinite; therefore - it is worth while to make a quick test for large width excess - and small stretchability, before calling the |badness| - subroutine. @^inner loop@> */ - - if ((cur_active_width[3]!=0)||(cur_active_width[4]!=0)|| - (cur_active_width[5]!=0)||(cur_active_width[6]!=0)) { if (do_last_line_fit) { - if (cur_p==null) { /* the last line of a paragraph */ - /* @<Perform computations for last line and |goto found|@>;*/ - - /* Here we compute the adjustment |g| and badness |b| for - a line from |r| to the end of the paragraph. When any - of the criteria for adjustment is violated we fall - through to the normal algorithm. - - The last line must be too short, and have infinite - stretch entirely due to |par_fill_skip|.*/ - if ((active_short(r)==0)||(active_glue(r)<=0)) - /* previous line was neither stretched nor shrunk, or - was infinitely bad*/ - goto NOT_FOUND; - if ((cur_active_width[3]!=fill_width[0])|| - (cur_active_width[4]!=fill_width[1])|| - (cur_active_width[5]!=fill_width[2])|| - (cur_active_width[6]!=fill_width[3])) - /* infinite stretch of this line not entirely due to - |par_fill_skip| */ - goto NOT_FOUND; - if (active_short(r)>0) - g=cur_active_width[2]; - else - g=cur_active_width[7]; - if (g<=0) - /*no finite stretch resp.\ no shrink*/ - goto NOT_FOUND; - arith_error=false; - g=fract(g,active_short(r),active_glue(r),max_dimen); - if (last_line_fit<1000) - g=fract(g,last_line_fit,1000,max_dimen); - if (arith_error) { - if (active_short(r)>0) - g=max_dimen; - else - g=-max_dimen; + /* @<Adjust \(t)the additional data for last line@>; */ + if (cur_p == null) + shortfall = 0; + if (shortfall > 0) { + g = cur_active_width[2]; + } else if (shortfall < 0) { + g = cur_active_width[7]; + } else { + g = 0; } - if (g>0) { - /*@<Set the value of |b| to the badness of the last line - for stretching, compute the corresponding |fit_class, - and |goto found||@>*/ - /* These badness computations are rather similar to - those of the standard algorithm, with the adjustment - amount |g| replacing the |shortfall|.*/ - if (g>shortfall) - g=shortfall; - if (g>7230584) { - if (cur_active_width[2]<1663497) { - b=inf_bad; - fit_class=very_loose_fit; - goto FOUND; - } - } - b=badness(g,cur_active_width[2]); - if (b>99) { fit_class=very_loose_fit; } - else if (b>12) { fit_class=loose_fit; } - else { fit_class=decent_fit; } - goto FOUND; - } else if (g<0) { - /*@<Set the value of |b| to the badness of the last line - for shrinking, compute the corresponding |fit_class, - and |goto found||@>;*/ - if (-g>cur_active_width[7]) - g=-cur_active_width[7]; - b=badness(-g,cur_active_width[7]); - if (b>12) fit_class=tight_fit; - else fit_class=decent_fit; - goto FOUND; + } + FOUND: + if ((b > inf_bad) || (pi == eject_penalty)) { + /* @<Prepare to deactivate node~|r|, and |goto deactivate| unless + there is a reason to consider lines of text from |r| to |cur_p|@> */ + /* During the final pass, we dare not lose all active nodes, lest we lose + touch with the line breaks already found. The code shown here makes + sure that such a catastrophe does not happen, by permitting overfull + boxes as a last resort. This particular part of \TeX\ was a source of + several subtle bugs before the correct program logic was finally + discovered; readers who seek to ``improve'' \TeX\ should therefore + think thrice before daring to make any changes here. + @^overfull boxes@> + */ + if (final_pass && (minimum_demerits == awful_bad) && + (vlink(r) == active) && (prev_r == active)) { + artificial_demerits = true; /* set demerits zero, this break is forced */ + } else if (b > threshold) { + goto DEACTIVATE; } - } - NOT_FOUND: - shortfall=0; + node_r_stays_active = false; + } else { + prev_r = r; + if (b > threshold) + continue; + node_r_stays_active = true; } - b=0; - fit_class=decent_fit; /* infinite stretch */ - } else { - if (shortfall>7230584 && cur_active_width[2]<1663497) { - b=inf_bad; - fit_class=very_loose_fit; + /* @<Record a new feasible break@>; */ + /* When we get to this part of the code, the line from |r| to |cur_p| is + feasible, its badness is~|b|, and its fitness classification is + |fit_class|. We don't want to make an active node for this break yet, + but we will compute the total demerits and record them in the + |minimal_demerits| array, if such a break is the current champion among + all ways to get to |cur_p| in a given line-number class and fitness + class. + */ + if (artificial_demerits) { + d = 0; } else { - b=badness(shortfall,cur_active_width[2]); - if (b>99) { fit_class=very_loose_fit; } - else if (b>12) { fit_class=loose_fit; } - else { fit_class=decent_fit; } + /* @<Compute the demerits, |d|, from |r| to |cur_p|@>; */ + d = line_penalty + b; + if (abs(d) >= 10000) + d = 100000000; + else + d = d * d; + if (pi != 0) { + if (pi > 0) { + d += (pi * pi); + } else if (pi > eject_penalty) { + d -= (pi * pi); + } + } + if ((break_type == hyphenated_node) && (type(r) == hyphenated_node)) { + if (cur_p != null) + d += double_hyphen_demerits; + else + d += final_hyphen_demerits; + } + if (abs(fit_class - fitness(r)) > 1) + d = d + adj_demerits; } - } - } else { - /* Set the value of |b| to the badness for shrinking the line, - and compute the corresponding |fit_class|@>; */ - /* Shrinkability is never infinite in a paragraph; we can shrink - the line from |r| to |cur_p| by at most - |cur_active_width[7]|.*/ - if (-shortfall>cur_active_width[7]) - b=inf_bad+1; - else - b=badness(-shortfall,cur_active_width[7]); - if (b>12) - fit_class=tight_fit; - else - fit_class=decent_fit; - } - if (do_last_line_fit) { - /* @<Adjust \(t)the additional data for last line@>; */ - if (cur_p==null) shortfall=0; - if (shortfall>0) { g=cur_active_width[2]; } - else if (shortfall<0) { g=cur_active_width[7]; } - else { g=0;} - } - FOUND: - if ((b>inf_bad)||(pi==eject_penalty)) { - /* @<Prepare to deactivate node~|r|, and |goto deactivate| unless - there is a reason to consider lines of text from |r| to |cur_p|@> */ - /* During the final pass, we dare not lose all active nodes, lest we lose - touch with the line breaks already found. The code shown here makes - sure that such a catastrophe does not happen, by permitting overfull - boxes as a last resort. This particular part of \TeX\ was a source of - several subtle bugs before the correct program logic was finally - discovered; readers who seek to ``improve'' \TeX\ should therefore - think thrice before daring to make any changes here. - @^overfull boxes@> - */ - if (final_pass && (minimum_demerits==awful_bad) && - (vlink(r)==active) && (prev_r==active)) { - artificial_demerits=true; /* set demerits zero, this break is forced */ - } else if (b>threshold) { - goto DEACTIVATE; - } - node_r_stays_active=false; - } else { - prev_r=r; - if (b>threshold) - continue; - node_r_stays_active=true; - } - /* @<Record a new feasible break@>;*/ - /* When we get to this part of the code, the line from |r| to |cur_p| is - feasible, its badness is~|b|, and its fitness classification is - |fit_class|. We don't want to make an active node for this break yet, - but we will compute the total demerits and record them in the - |minimal_demerits| array, if such a break is the current champion among - all ways to get to |cur_p| in a given line-number class and fitness - class. - */ - if (artificial_demerits) { - d=0; - } else { - /* @<Compute the demerits, |d|, from |r| to |cur_p|@>;*/ - d=line_penalty+b; - if (abs(d)>=10000) d=100000000; else d=d*d; - if (pi!=0) { - if (pi>0) { - d += (pi*pi); - } else if (pi>eject_penalty) { - d -= (pi*pi); + if (tracing_paragraphs > 0) + print_feasible_break(cur_p, r, b, pi, d, artificial_demerits); + d += total_demerits(r); /*this is the minimum total demerits + from the beginning to |cur_p| via |r| */ + if (d <= minimal_demerits[fit_class]) { + minimal_demerits[fit_class] = d; + best_place[fit_class] = break_node(r); + best_pl_line[fit_class] = l; + if (do_last_line_fit) { + /* Store \(a)additional data for this feasible break@>; */ + /* For each feasible break we record the shortfall and glue stretch or + shrink (or adjustment). */ + best_pl_short[fit_class] = shortfall; + best_pl_glue[fit_class] = g; + } + if (d < minimum_demerits) + minimum_demerits = d; } - } - if ((break_type==hyphenated_node)&&(type(r)==hyphenated_node)) { - if (cur_p!=null) - d += double_hyphen_demerits; - else - d += final_hyphen_demerits; - } - if (abs(fit_class-fitness(r))>1) d=d+adj_demerits; - } - if (tracing_paragraphs>0) - print_feasible_break(cur_p, r, b, pi, d, artificial_demerits); - d += total_demerits(r); /*this is the minimum total demerits - from the beginning to |cur_p| via |r| */ - if (d<=minimal_demerits[fit_class]) { - minimal_demerits[fit_class]=d; - best_place[fit_class]=break_node(r); - best_pl_line[fit_class]=l; - if (do_last_line_fit) { - /* Store \(a)additional data for this feasible break@>; */ - /* For each feasible break we record the shortfall and glue stretch or - shrink (or adjustment).*/ - best_pl_short[fit_class]=shortfall; - best_pl_glue[fit_class]=g; - } - if (d<minimum_demerits) - minimum_demerits=d; - } - /* /Record a new feasible break */ - if (node_r_stays_active) - continue; /*|prev_r| has been set to |r| */ - DEACTIVATE: - /* @<Deactivate node |r|@>; */ - /* When an active node disappears, we must delete an adjacent delta node if - the active node was at the beginning or the end of the active list, or - if it was surrounded by delta nodes. We also must preserve the property - that |cur_active_width| represents the length of material from - |vlink(prev_r)| to~|cur_p|.*/ - - vlink(prev_r)=vlink(r); - flush_node(r); - if (prev_r==active) { - /*@<Update the active widths, since the first active node has been - deleted@>*/ - /* The following code uses the fact that |type(active)<>delta_node|. - If the active list has just become empty, we do not need to update the - |active_width| array, since it will be initialized when an active - node is next inserted. - */ - r=vlink(active); - if (type(r)==delta_node) { - do_all_eight(update_active); /* IMPLICIT r */ - do_all_eight(copy_to_cur_active); - vlink(active)=vlink(r); + /* /Record a new feasible break */ + if (node_r_stays_active) + continue; /*|prev_r| has been set to |r| */ + DEACTIVATE: + /* @<Deactivate node |r|@>; */ + /* When an active node disappears, we must delete an adjacent delta node if + the active node was at the beginning or the end of the active list, or + if it was surrounded by delta nodes. We also must preserve the property + that |cur_active_width| represents the length of material from + |vlink(prev_r)| to~|cur_p|. */ + + vlink(prev_r) = vlink(r); flush_node(r); - } - } else if (type(prev_r)==delta_node){ - r=vlink(prev_r); - if (r==active) { - do_all_eight(downdate_width); /* IMPLICIT prev_r */ - vlink(prev_prev_r)=active; - flush_node(prev_r); - prev_r=prev_prev_r; - } else if (type(r)==delta_node) { - do_all_eight(update_width); /* IMPLICIT ,r */ - do_all_eight(combine_two_deltas); /* IMPLICIT r prev_r */ - vlink(prev_r)=vlink(r); - flush_node(r); - } - } - } + if (prev_r == active) { + /*@<Update the active widths, since the first active node has been + deleted@> */ + /* The following code uses the fact that |type(active)<>delta_node|. + If the active list has just become empty, we do not need to update the + |active_width| array, since it will be initialized when an active + node is next inserted. + */ + r = vlink(active); + if (type(r) == delta_node) { + do_all_eight(update_active); /* IMPLICIT r */ + do_all_eight(copy_to_cur_active); + vlink(active) = vlink(r); + flush_node(r); + } + } else if (type(prev_r) == delta_node) { + r = vlink(prev_r); + if (r == active) { + do_all_eight(downdate_width); /* IMPLICIT prev_r */ + vlink(prev_prev_r) = active; + flush_node(prev_r); + prev_r = prev_prev_r; + } else if (type(r) == delta_node) { + do_all_eight(update_width); /* IMPLICIT ,r */ + do_all_eight(combine_two_deltas); /* IMPLICIT r prev_r */ + vlink(prev_r) = vlink(r); + flush_node(r); + } + } + } } void -ext_do_line_break (boolean d, - int pretolerance, - int tracing_paragraphs, - int tolerance, - scaled emergency_stretch, - int looseness, - int hyphen_penalty, - int ex_hyphen_penalty, - int pdf_adjust_spacing, - halfword par_shape_ptr, - int adj_demerits, - int pdf_protrude_chars, - int line_penalty, - int last_line_fit, - int double_hyphen_demerits, - int final_hyphen_demerits, - int hang_indent, - int hsize, - int hang_after, - halfword left_skip, - halfword right_skip, - int pdf_each_line_height, - int pdf_each_line_depth, - int pdf_first_line_height, - int pdf_last_line_depth, - halfword inter_line_penalties_ptr, - int inter_line_penalty, - int club_penalty, - halfword club_penalties_ptr, - halfword display_widow_penalties_ptr, - halfword widow_penalties_ptr, - int display_widow_penalty, - int widow_penalty, - int broken_penalty, - halfword final_par_glue) { - /* DONE,DONE1,DONE2,DONE3,DONE4,DONE5,CONTINUE;*/ - halfword cur_p,q,r,s; /* miscellaneous nodes of temporary interest */ - - /* Get ready to start ... */ - minimum_demerits=awful_bad; - minimal_demerits[tight_fit]=awful_bad; - minimal_demerits[decent_fit]=awful_bad; - minimal_demerits[loose_fit]=awful_bad; - minimal_demerits[very_loose_fit]=awful_bad; - - /* We compute the values of |easy_line| and the other local variables relating - to line length when the |line_break| procedure is initializing itself. */ - if (par_shape_ptr==null) { - if (hang_indent==0) { - last_special_line=0; - second_width=hsize; - second_indent=0; - } else { - /* @<Set line length parameters in preparation for hanging indentation@>*/ - /* We compute the values of |easy_line| and the other local variables relating - to line length when the |line_break| procedure is initializing itself. */ - last_special_line=abs(hang_after); - if (hang_after<0) { - first_width=hsize-abs(hang_indent); - if (hang_indent>=0) - first_indent=hang_indent; - else - first_indent=0; - second_width=hsize; - second_indent=0; - } else { - first_width=hsize; - first_indent=0; - second_width=hsize-abs(hang_indent); - if (hang_indent>=0) - second_indent=hang_indent; - else - second_indent=0; - } +ext_do_line_break(boolean d, + int pretolerance, + int tracing_paragraphs, + int tolerance, + scaled emergency_stretch, + int looseness, + int hyphen_penalty, + int ex_hyphen_penalty, + int pdf_adjust_spacing, + halfword par_shape_ptr, + int adj_demerits, + int pdf_protrude_chars, + int line_penalty, + int last_line_fit, + int double_hyphen_demerits, + int final_hyphen_demerits, + int hang_indent, + int hsize, + int hang_after, + halfword left_skip, + halfword right_skip, + int pdf_each_line_height, + int pdf_each_line_depth, + int pdf_first_line_height, + int pdf_last_line_depth, + halfword inter_line_penalties_ptr, + int inter_line_penalty, + int club_penalty, + halfword club_penalties_ptr, + halfword display_widow_penalties_ptr, + halfword widow_penalties_ptr, + int display_widow_penalty, + int widow_penalty, + int broken_penalty, halfword final_par_glue) +{ + /* DONE,DONE1,DONE2,DONE3,DONE4,DONE5,CONTINUE; */ + halfword cur_p, q, r, s; /* miscellaneous nodes of temporary interest */ + + /* Get ready to start ... */ + minimum_demerits = awful_bad; + minimal_demerits[tight_fit] = awful_bad; + minimal_demerits[decent_fit] = awful_bad; + minimal_demerits[loose_fit] = awful_bad; + minimal_demerits[very_loose_fit] = awful_bad; + + /* We compute the values of |easy_line| and the other local variables relating + to line length when the |line_break| procedure is initializing itself. */ + if (par_shape_ptr == null) { + if (hang_indent == 0) { + last_special_line = 0; + second_width = hsize; + second_indent = 0; + } else { + /* @<Set line length parameters in preparation for hanging indentation@> */ + /* We compute the values of |easy_line| and the other local variables relating + to line length when the |line_break| procedure is initializing itself. */ + last_special_line = abs(hang_after); + if (hang_after < 0) { + first_width = hsize - abs(hang_indent); + if (hang_indent >= 0) + first_indent = hang_indent; + else + first_indent = 0; + second_width = hsize; + second_indent = 0; + } else { + first_width = hsize; + first_indent = 0; + second_width = hsize - abs(hang_indent); + if (hang_indent >= 0) + second_indent = hang_indent; + else + second_indent = 0; + } + } + } else { + last_special_line = vinfo(par_shape_ptr + 1) - 1; + second_indent = varmem[(par_shape_ptr + (2 * last_special_line))].cint; + second_width = + varmem[(par_shape_ptr + 2 * (last_special_line + 1) + 1)].cint; } - } else { - last_special_line=vinfo(par_shape_ptr+1)-1; - second_indent=varmem[(par_shape_ptr+(2*last_special_line))].cint; - second_width=varmem[(par_shape_ptr+2*(last_special_line+1)+1)].cint; - } - if (looseness==0) - easy_line=last_special_line; - else - easy_line=max_halfword; - - no_shrink_error_yet=true; - check_shrinkage(left_skip); - check_shrinkage(right_skip); - q=left_skip; - r=right_skip; - background[1]=width(q)+width(r); - background[2]=0; - background[3]=0; - background[4]=0; - background[5]=0; - background[6]=0; - background[2+stretch_order(q)] = stretch(q); - background[2+stretch_order(r)] += stretch(r); - background[7]=shrink(q)+shrink(r); - if (pdf_adjust_spacing > 1) { - background[8] = 0; - background[9] = 0; - max_stretch_ratio = -1; - max_shrink_ratio = -1; - cur_font_step = -1; - set_prev_char_p(null); - } - /* @<Check for special treatment of last line of paragraph@>;*/ - /* The new algorithm for the last line requires that the stretchability - |par_fill_skip| is infinite and the stretchability of |left_skip| plus - |right_skip| is finite. - */ - do_last_line_fit=false; - if (last_line_fit>0) { - q=glue_ptr(last_line_fill); - if ((stretch(q)>0)&&(stretch_order(q)>normal)) { - if ((background[3]==0)&&(background[4]==0)&& - (background[5]==0)&&(background[6]==0)) { - do_last_line_fit=true; - fill_width[0]=0; - fill_width[1]=0; - fill_width[2]=0; - fill_width[3]=0; - fill_width[stretch_order(q)-1]=stretch(q); - } + if (looseness == 0) + easy_line = last_special_line; + else + easy_line = max_halfword; + + no_shrink_error_yet = true; + check_shrinkage(left_skip); + check_shrinkage(right_skip); + q = left_skip; + r = right_skip; + background[1] = width(q) + width(r); + background[2] = 0; + background[3] = 0; + background[4] = 0; + background[5] = 0; + background[6] = 0; + background[2 + stretch_order(q)] = stretch(q); + background[2 + stretch_order(r)] += stretch(r); + background[7] = shrink(q) + shrink(r); + if (pdf_adjust_spacing > 1) { + background[8] = 0; + background[9] = 0; + max_stretch_ratio = -1; + max_shrink_ratio = -1; + cur_font_step = -1; + set_prev_char_p(null); } - } - /* @<DIR: Initialize |dir_ptr| for |line_break|@> */ - if (dir_ptr!=null) { - fprintf(stdout,"-dir_node s %d\n",dir_ptr); - flush_node_list(dir_ptr); - dir_ptr=null; - } - /*push_dir(paragraph_dir);*/ /* TODO what was the point of this? */ - - /* @<Find optimal breakpoints@>;*/ - threshold=pretolerance; - if (threshold>=0) { - if (tracing_paragraphs>0) { - begin_diagnostic(); - tprint_nl("@firstpass"); + /* @<Check for special treatment of last line of paragraph@>; */ + /* The new algorithm for the last line requires that the stretchability + |par_fill_skip| is infinite and the stretchability of |left_skip| plus + |right_skip| is finite. + */ + do_last_line_fit = false; + if (last_line_fit > 0) { + q = glue_ptr(last_line_fill); + if ((stretch(q) > 0) && (stretch_order(q) > normal)) { + if ((background[3] == 0) && (background[4] == 0) && + (background[5] == 0) && (background[6] == 0)) { + do_last_line_fit = true; + fill_width[0] = 0; + fill_width[1] = 0; + fill_width[2] = 0; + fill_width[3] = 0; + fill_width[stretch_order(q) - 1] = stretch(q); + } + } } - second_pass=false; - final_pass=false; - } else { - threshold=tolerance; - second_pass=true; - final_pass=(emergency_stretch<=0); - if (tracing_paragraphs>0) - begin_diagnostic(); - } - - while (1) { - halfword first_p; - halfword nest_stack[10]; - int nest_index = 0; - if (threshold>inf_bad) - threshold=inf_bad; - /* Create an active breakpoint representing the beginning of the paragraph */ - q=new_node(unhyphenated_node,decent_fit); - vlink(q)=active; - break_node(q)=null; - line_number(q)=cur_list.pg_field+1; - total_demerits(q)=0; - active_short(q)=0; - active_glue(q)=0; - vlink(active)=q; - do_all_eight(store_background); - passive=null; - printed_node=temp_head; - pass_number=0; - font_in_short_display=null_font; - /* /Create an active breakpoint representing the beginning of the paragraph */ - auto_breaking=true; - cur_p=vlink(temp_head); - assert(alink(cur_p)==temp_head); - /* LOCAL: Initialize with first |local_paragraph| node */ - if ((type(cur_p)==whatsit_node)&&(subtype(cur_p)==local_par_node)) { - internal_pen_inter=local_pen_inter(cur_p); - internal_pen_broken=local_pen_broken(cur_p); - init_internal_left_box=local_box_left(cur_p); - init_internal_left_box_width=local_box_left_width(cur_p); - internal_left_box=init_internal_left_box; - internal_left_box_width=init_internal_left_box_width; - internal_right_box=local_box_right(cur_p); - internal_right_box_width=local_box_right_width(cur_p); - } else { - internal_pen_inter=0; - internal_pen_broken=0; - init_internal_left_box=null; - init_internal_left_box_width=0; - internal_left_box=init_internal_left_box; - internal_left_box_width=init_internal_left_box_width; - internal_right_box=null; - internal_right_box_width=0; + /* @<DIR: Initialize |dir_ptr| for |line_break|@> */ + if (dir_ptr != null) { + flush_node_list(dir_ptr); + dir_ptr = null; } - /* /LOCAL: Initialize with first |local_paragraph| node */ - set_prev_char_p (null); - prev_legal = null; - rejected_cur_p = null; - try_prev_break = false; - before_rejected_cur_p = false; - first_p = cur_p; - /* to access the first node of paragraph as the first active node - has |break_node=null| */ - while ((cur_p!=null)&&(vlink(active)!=active)) { - /* |try_break| if |cur_p| is a legal breakpoint; on the 2nd pass, also look at disc_nodes. */ - - while (is_char_node(cur_p)) { - /* Advance \(c)|cur_p| to the node following the present string of characters ;*/ - /* The code that passes over the characters of words in a paragraph is part of - \TeX's inner loop, so it has been streamlined for speed. We use the fact that - `\.{\\parfillskip}' glue appears at the end of each paragraph; it is therefore - unnecessary to check if |vlink(cur_p)=null| when |cur_p| is a character node. - */ - act_width += (is_rotated(line_break_dir)) - ? (glyph_height(cur_p)+glyph_depth(cur_p)) - : glyph_width(cur_p); - if ((pdf_adjust_spacing > 1) && check_expand_pars(font(cur_p))) { - set_prev_char_p (cur_p); - add_char_stretch(active_width[8],cur_p); - add_char_shrink(active_width[9],cur_p); - } - cur_p=vlink(cur_p); - while (cur_p==null && nest_index>0) { - cur_p = nest_stack[--nest_index]; - /* fprintf(stderr,"Node Pop %d [%d]\n",nest_index,(int)cur_p); */ + /*push_dir(paragraph_dir); *//* TODO what was the point of this? */ + + /* @<Find optimal breakpoints@>; */ + threshold = pretolerance; + if (threshold >= 0) { + if (tracing_paragraphs > 0) { + begin_diagnostic(); + tprint_nl("@firstpass"); } - } - if (cur_p==null) { /* TODO */ - tconfusion("linebreak_tail"); - } - /* Determine legal breaks: As we move through the hlist, we need to keep - the |active_width| array up to date, so that the badness of individual - lines is readily calculated by |try_break|. It is convenient to use the - short name |act_width| for the component of active width that represents - real width as opposed to glue. */ - - switch (type(cur_p)) { - - case hlist_node: - case vlist_node: - act_width += (dir_orthogonal(dir_primary[box_dir(cur_p)], dir_primary[line_break_dir])) - ? (depth(cur_p)+height(cur_p)) - : width(cur_p); - break; - case rule_node: - act_width += width(cur_p); - break; - case whatsit_node: - /* @<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>;*/ - switch (subtype(cur_p)) { - case local_par_node: /* @<LOCAL: Advance past a |local_paragraph| node@>;*/ - internal_pen_inter=local_pen_inter(cur_p); - internal_pen_broken=local_pen_broken(cur_p); - internal_left_box=local_box_left(cur_p); - internal_left_box_width=local_box_left_width(cur_p); - internal_right_box=local_box_right(cur_p); - internal_right_box_width=local_box_right_width(cur_p); - break; - case dir_node: /* @<DIR: Adjust the dir stack for the |line_break| routine@>;*/ - if (dir_dir(cur_p)>=0) { - line_break_dir=dir_dir(cur_p); - push_dir_node(cur_p); - } else { - pop_dir_node(); - if (dir_ptr!=null) - line_break_dir=dir_dir(dir_ptr); - } - break; - case pdf_refxform_node: - case pdf_refximage_node: - act_width += pdf_width(cur_p); - } - /* / Advance \(p)past a whatsit node in the \(l)|line_break| loop/; */ - break; - case glue_node: - /* @<If node |cur_p| is a legal breakpoint, call |try_break|; - then update the active widths by including the glue in - |glue_ptr(cur_p)|@>; */ - /* When node |cur_p| is a glue node, we look at the previous to - see whether or not a breakpoint is legal at |cur_p|, as - explained above. */ - if (auto_breaking) { - halfword prev_p = alink(cur_p); - if (prev_p!=temp_head && - (is_char_node(prev_p) || - precedes_break(prev_p)|| - ((type(prev_p)==kern_node)&&(subtype(prev_p)!=explicit)))) { - ext_try_break(0,unhyphenated_node, pdf_adjust_spacing, par_shape_ptr, - adj_demerits, tracing_paragraphs, pdf_protrude_chars, - line_penalty, last_line_fit, double_hyphen_demerits, - final_hyphen_demerits,first_p,cur_p); - } - } - check_shrinkage(glue_ptr(cur_p)); - q=glue_ptr(cur_p); - act_width += width(q); - active_width[2+stretch_order(q)] += stretch(q); - active_width[7] += shrink(q); - break; - case kern_node: - if (subtype(cur_p)==explicit) { - kern_break(); + second_pass = false; + final_pass = false; + } else { + threshold = tolerance; + second_pass = true; + final_pass = (emergency_stretch <= 0); + if (tracing_paragraphs > 0) + begin_diagnostic(); + } + + while (1) { + halfword first_p; + halfword nest_stack[10]; + int nest_index = 0; + if (threshold > inf_bad) + threshold = inf_bad; + /* Create an active breakpoint representing the beginning of the paragraph */ + q = new_node(unhyphenated_node, decent_fit); + vlink(q) = active; + break_node(q) = null; + line_number(q) = cur_list.pg_field + 1; + total_demerits(q) = 0; + active_short(q) = 0; + active_glue(q) = 0; + vlink(active) = q; + do_all_eight(store_background); + passive = null; + printed_node = temp_head; + pass_number = 0; + font_in_short_display = null_font; + /* /Create an active breakpoint representing the beginning of the paragraph */ + auto_breaking = true; + cur_p = vlink(temp_head); + assert(alink(cur_p) == temp_head); + /* LOCAL: Initialize with first |local_paragraph| node */ + if ((type(cur_p) == whatsit_node) && (subtype(cur_p) == local_par_node)) { + internal_pen_inter = local_pen_inter(cur_p); + internal_pen_broken = local_pen_broken(cur_p); + init_internal_left_box = local_box_left(cur_p); + init_internal_left_box_width = local_box_left_width(cur_p); + internal_left_box = init_internal_left_box; + internal_left_box_width = init_internal_left_box_width; + internal_right_box = local_box_right(cur_p); + internal_right_box_width = local_box_right_width(cur_p); } else { - act_width += width(cur_p); - if ((pdf_adjust_spacing > 1) && (subtype(cur_p) == normal)) { - add_kern_stretch(active_width[8],cur_p); - add_kern_shrink(active_width[9],cur_p); - } + internal_pen_inter = 0; + internal_pen_broken = 0; + init_internal_left_box = null; + init_internal_left_box_width = 0; + internal_left_box = init_internal_left_box; + internal_left_box_width = init_internal_left_box_width; + internal_right_box = null; + internal_right_box_width = 0; } - break; - case disc_node: - /* @<Try to break after a discretionary fragment, then |goto done5|@>;*/ - /* The following code knows that discretionary texts contain - only character nodes, kern nodes, box nodes, and rule - nodes. */ - if (second_pass) { - int actual_penalty = hyphen_penalty; - if (subtype(cur_p)==automatic_disc) - actual_penalty = ex_hyphen_penalty; - s=vlink_pre_break(cur_p); - do_one_seven_eight(reset_disc_width); - if (s==null) { /* trivial pre-break */ - ext_try_break(actual_penalty,hyphenated_node, pdf_adjust_spacing, - par_shape_ptr, adj_demerits, tracing_paragraphs, - pdf_protrude_chars, line_penalty, last_line_fit, - double_hyphen_demerits, final_hyphen_demerits,first_p,cur_p); - } else { - do { - /* @<Add the width of node |s| to |disc_width|@>;*/ - if (is_char_node(s)) { - if (is_rotated(line_break_dir)) { - disc_width[1] += (glyph_height(s)+glyph_depth(s)); - } else { - disc_width[1] += glyph_width(s); + /* /LOCAL: Initialize with first |local_paragraph| node */ + set_prev_char_p(null); + prev_legal = null; + rejected_cur_p = null; + try_prev_break = false; + before_rejected_cur_p = false; + first_p = cur_p; + /* to access the first node of paragraph as the first active node + has |break_node=null| */ + while ((cur_p != null) && (vlink(active) != active)) { + /* |try_break| if |cur_p| is a legal breakpoint; on the 2nd pass, also look at disc_nodes. */ + + while (is_char_node(cur_p)) { + /* Advance \(c)|cur_p| to the node following the present string of characters ; */ + /* The code that passes over the characters of words in a paragraph is part of + \TeX's inner loop, so it has been streamlined for speed. We use the fact that + `\.{\\parfillskip}' glue appears at the end of each paragraph; it is therefore + unnecessary to check if |vlink(cur_p)=null| when |cur_p| is a character node. + */ + act_width += (is_rotated(line_break_dir)) + ? (glyph_height(cur_p) + glyph_depth(cur_p)) + : glyph_width(cur_p); + if ((pdf_adjust_spacing > 1) && check_expand_pars(font(cur_p))) { + set_prev_char_p(cur_p); + add_char_stretch(active_width[8], cur_p); + add_char_shrink(active_width[9], cur_p); } - if ((pdf_adjust_spacing > 1) && check_expand_pars(font(s))) { - set_prev_char_p (s); - add_char_stretch(disc_width[8],s); - add_char_shrink(disc_width[9],s); + cur_p = vlink(cur_p); + while (cur_p == null && nest_index > 0) { + cur_p = nest_stack[--nest_index]; + /* fprintf(stderr,"Node Pop %d [%d]\n",nest_index,(int)cur_p); */ } - } else { - switch (type(s)) { - case hlist_node: - case vlist_node: - if (!(dir_orthogonal(dir_primary[box_dir(s)], - dir_primary[line_break_dir]))) { - disc_width[1] += width(s); - } else { - disc_width[1] += (depth(s)+height(s)); - } - break; - case kern_node: - if ((pdf_adjust_spacing > 1) && (subtype(s) == normal)) { - add_kern_stretch(disc_width[8],s); - add_kern_shrink(disc_width[9],s); - } - /* fall through */ - case rule_node: - disc_width[1] += width(s); - break; - case disc_node: - tconfusion("pre_break_disc"); - break; - default: - tconfusion("disc3"); - } - } - /* /Add the width of node |s| to |disc_width| */ - s=vlink(s); - } while (s!=null); - do_one_seven_eight(add_disc_width_to_active_width); - ext_try_break(actual_penalty,hyphenated_node, pdf_adjust_spacing, - par_shape_ptr, adj_demerits, tracing_paragraphs, - pdf_protrude_chars, line_penalty, last_line_fit, - double_hyphen_demerits, final_hyphen_demerits, - first_p,cur_p); - do_one_seven_eight(sub_disc_width_from_active_width); - } -#define FOO 1 -#if FOO -#if 0 - if (vlink(cur_p)!=null) nest_stack[nest_index++] = vlink(cur_p); - cur_p = no_break(cur_p); -#else - s=vlink_no_break(cur_p); - while (s!=null) { - /* @<Add the width of node |s| to |act_width|@>;*/ - if (is_char_node(s)) { - if (is_rotated(line_break_dir)) { - act_width += glyph_height(s)+glyph_depth(s); - } else { - act_width += glyph_width(s); - } - if ((pdf_adjust_spacing > 1) && check_expand_pars(font(s))) { - set_prev_char_p (s); - add_char_stretch(active_width[8],s); - add_char_shrink(active_width[9],s); - }; - } else { - switch (type(s)) { - case hlist_node: - case vlist_node: - if (!(dir_orthogonal(dir_primary[box_dir(s)], - dir_primary[line_break_dir]))) - act_width += width(s); - else - act_width += (depth(s)+height(s)); + } + if (cur_p == null) { /* TODO */ + tconfusion("linebreak_tail"); + } + /* Determine legal breaks: As we move through the hlist, we need to keep + the |active_width| array up to date, so that the badness of individual + lines is readily calculated by |try_break|. It is convenient to use the + short name |act_width| for the component of active width that represents + real width as opposed to glue. */ + + switch (type(cur_p)) { + + case hlist_node: + case vlist_node: + act_width += + (dir_orthogonal + (dir_primary[box_dir(cur_p)], dir_primary[line_break_dir])) + ? (depth(cur_p) + height(cur_p)) + : width(cur_p); break; - case kern_node: - if ((pdf_adjust_spacing > 1) && (subtype(s) == normal)) { - add_kern_stretch(active_width[8],s); - add_kern_shrink(active_width[9],s); + case rule_node: + act_width += width(cur_p); + break; + case whatsit_node: + /* @<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>; */ + switch (subtype(cur_p)) { + case local_par_node: /* @<LOCAL: Advance past a |local_paragraph| node@>; */ + internal_pen_inter = local_pen_inter(cur_p); + internal_pen_broken = local_pen_broken(cur_p); + internal_left_box = local_box_left(cur_p); + internal_left_box_width = local_box_left_width(cur_p); + internal_right_box = local_box_right(cur_p); + internal_right_box_width = local_box_right_width(cur_p); + break; + case dir_node: /* @<DIR: Adjust the dir stack for the |line_break| routine@>; */ + if (dir_dir(cur_p) >= 0) { + line_break_dir = dir_dir(cur_p); + push_dir_node(cur_p); + } else { + pop_dir_node(); + if (dir_ptr != null) + line_break_dir = dir_dir(dir_ptr); + } + break; + case pdf_refxform_node: + case pdf_refximage_node: + act_width += pdf_width(cur_p); } - /* fall through */ - case rule_node: - act_width += width(s); + /* / Advance \(p)past a whatsit node in the \(l)|line_break| loop/; */ break; - case disc_node: - /* what now */ - assert(vlink(s)==null); /* discs are _always_ last */ - /* NB: temporary solution: not good, but not bad either */ - s = no_break(s); + case glue_node: + /* @<If node |cur_p| is a legal breakpoint, call |try_break|; + then update the active widths by including the glue in + |glue_ptr(cur_p)|@>; */ + /* When node |cur_p| is a glue node, we look at the previous to + see whether or not a breakpoint is legal at |cur_p|, as + explained above. */ + /* *INDENT-OFF* */ + if (auto_breaking) { + halfword prev_p = alink(cur_p); + if (prev_p != temp_head && + (is_char_node(prev_p) || + precedes_break(prev_p) || + ((type(prev_p) == kern_node) + && (subtype(prev_p) != explicit)))) { + ext_try_break(0, unhyphenated_node, pdf_adjust_spacing, + par_shape_ptr, adj_demerits, + tracing_paragraphs, pdf_protrude_chars, + line_penalty, last_line_fit, + double_hyphen_demerits, + final_hyphen_demerits, first_p, cur_p); + } + } + /* *INDENT-ON* */ + check_shrinkage(glue_ptr(cur_p)); + q = glue_ptr(cur_p); + act_width += width(q); + active_width[2 + stretch_order(q)] += stretch(q); + active_width[7] += shrink(q); break; - default: - tconfusion("disc5"); - } - } - /* /Add the width of node |s| to |act_width|;*/ - s=vlink(s); - } -#endif - } else { /* first pass, just take the no_break path */ -#else /* FOO */ - } -#endif /* FOO */ + case kern_node: + if (subtype(cur_p) == explicit) { + kern_break(); + } else { + act_width += width(cur_p); + if ((pdf_adjust_spacing > 1) && (subtype(cur_p) == normal)) { + add_kern_stretch(active_width[8], cur_p); + add_kern_shrink(active_width[9], cur_p); + } + } + break; + case disc_node: + /* @<Try to break after a discretionary fragment, then |goto done5|@>; */ + /* The following code knows that discretionary texts contain + only character nodes, kern nodes, box nodes, and rule + nodes. */ + if (second_pass) { + int actual_penalty = hyphen_penalty; + if (subtype(cur_p) == automatic_disc) + actual_penalty = ex_hyphen_penalty; + s = vlink_pre_break(cur_p); + do_one_seven_eight(reset_disc_width); + if (s == null) { /* trivial pre-break */ + ext_try_break(actual_penalty, hyphenated_node, + pdf_adjust_spacing, par_shape_ptr, + adj_demerits, tracing_paragraphs, + pdf_protrude_chars, line_penalty, + last_line_fit, double_hyphen_demerits, + final_hyphen_demerits, first_p, cur_p); + } else { + do { + /* @<Add the width of node |s| to |disc_width|@>; */ + if (is_char_node(s)) { + if (is_rotated(line_break_dir)) { + disc_width[1] += + (glyph_height(s) + glyph_depth(s)); + } else { + disc_width[1] += glyph_width(s); + } + if ((pdf_adjust_spacing > 1) + && check_expand_pars(font(s))) { + set_prev_char_p(s); + add_char_stretch(disc_width[8], s); + add_char_shrink(disc_width[9], s); + } + } else { + switch (type(s)) { + case hlist_node: + case vlist_node: + if (! + (dir_orthogonal + (dir_primary[box_dir(s)], + dir_primary[line_break_dir]))) { + disc_width[1] += width(s); + } else { + disc_width[1] += (depth(s) + height(s)); + } + break; + case kern_node: + if ((pdf_adjust_spacing > 1) + && (subtype(s) == normal)) { + add_kern_stretch(disc_width[8], s); + add_kern_shrink(disc_width[9], s); + } + /* fall through */ + case rule_node: + disc_width[1] += width(s); + break; + case disc_node: + tconfusion("pre_break_disc"); + break; + default: + tconfusion("disc3"); + } + } + /* /Add the width of node |s| to |disc_width| */ + s = vlink(s); + } while (s != null); + do_one_seven_eight(add_disc_width_to_active_width); + ext_try_break(actual_penalty, hyphenated_node, + pdf_adjust_spacing, par_shape_ptr, + adj_demerits, tracing_paragraphs, + pdf_protrude_chars, line_penalty, + last_line_fit, double_hyphen_demerits, + final_hyphen_demerits, first_p, cur_p); + do_one_seven_eight(sub_disc_width_from_active_width); + } +#define FOO 1 +#if FOO +# if 0 + if (vlink(cur_p) != null) + nest_stack[nest_index++] = vlink(cur_p); + cur_p = no_break(cur_p); +# else + s = vlink_no_break(cur_p); + while (s != null) { + /* @<Add the width of node |s| to |act_width|@>; */ + if (is_char_node(s)) { + if (is_rotated(line_break_dir)) { + act_width += glyph_height(s) + glyph_depth(s); + } else { + act_width += glyph_width(s); + } + if ((pdf_adjust_spacing > 1) + && check_expand_pars(font(s))) { + set_prev_char_p(s); + add_char_stretch(active_width[8], s); + add_char_shrink(active_width[9], s); + }; + } else { + switch (type(s)) { + case hlist_node: + case vlist_node: + if (!(dir_orthogonal(dir_primary[box_dir(s)], + dir_primary + [line_break_dir]))) + act_width += width(s); + else + act_width += (depth(s) + height(s)); + break; + case kern_node: + if ((pdf_adjust_spacing > 1) + && (subtype(s) == normal)) { + add_kern_stretch(active_width[8], s); + add_kern_shrink(active_width[9], s); + } + /* fall through */ + case rule_node: + act_width += width(s); + break; + case disc_node: + /* what now */ + assert(vlink(s) == null); /* discs are _always_ last */ + /* NB: temporary solution: not good, but not bad either */ + s = no_break(s); + break; + default: + tconfusion("disc5"); + } + } + /* /Add the width of node |s| to |act_width|; */ + s = vlink(s); + } +# endif + } else { /* first pass, just take the no_break path */ +#else /* FOO */ + } +#endif /* FOO */ #if 0 - if (vlink_no_break(cur_p)!=null) { - if (vlink(cur_p)!=null) nest_stack[nest_index++] = vlink(cur_p); - /* fprintf(stderr,"Node Push %d [%d]->[%d] / [%d]\n",(nest_index-1),(int)cur_p,(int)vlink(cur_p),(int)vlink_no_break(cur_p));*/ - cur_p = no_break(cur_p); - } + if (vlink_no_break(cur_p) != null) { + if (vlink(cur_p) != null) + nest_stack[nest_index++] = vlink(cur_p); + /* fprintf(stderr,"Node Push %d [%d]->[%d] / [%d]\n",(nest_index-1),(int)cur_p,(int)vlink(cur_p),(int)vlink_no_break(cur_p)); */ + cur_p = no_break(cur_p); + } #else - s=vlink_no_break(cur_p); - while (s!=null) { - /* @<Add the width of node |s| to |act_width|@>;*/ - if (is_char_node(s)) { - if (is_rotated(line_break_dir)) { - act_width += glyph_height(s)+glyph_depth(s); - } else { - act_width += glyph_width(s); - } - if ((pdf_adjust_spacing > 1) && check_expand_pars(font(s))) { - set_prev_char_p (s); - add_char_stretch(active_width[8],s); - add_char_shrink(active_width[9],s); - }; - } else { - switch (type(s)) { - case hlist_node: - case vlist_node: - if (!(dir_orthogonal(dir_primary[box_dir(s)], - dir_primary[line_break_dir]))) - act_width += width(s); - else - act_width += (depth(s)+height(s)); - break; - case kern_node: - if ((pdf_adjust_spacing > 1) && (subtype(s) == normal)) { - add_kern_stretch(active_width[8],s); - add_kern_shrink(active_width[9],s); + s = vlink_no_break(cur_p); + while (s != null) { + /* @<Add the width of node |s| to |act_width|@>; */ + if (is_char_node(s)) { + if (is_rotated(line_break_dir)) { + act_width += glyph_height(s) + glyph_depth(s); + } else { + act_width += glyph_width(s); + } + if ((pdf_adjust_spacing > 1) + && check_expand_pars(font(s))) { + set_prev_char_p(s); + add_char_stretch(active_width[8], s); + add_char_shrink(active_width[9], s); + }; + } else { + switch (type(s)) { + case hlist_node: + case vlist_node: + if (!(dir_orthogonal(dir_primary[box_dir(s)], + dir_primary[line_break_dir]))) + act_width += width(s); + else + act_width += (depth(s) + height(s)); + break; + case kern_node: + if ((pdf_adjust_spacing > 1) + && (subtype(s) == normal)) { + add_kern_stretch(active_width[8], s); + add_kern_shrink(active_width[9], s); + } + /* fall through */ + case rule_node: + act_width += width(s); + break; + case disc_node: + assert(vlink(s) == null); /* discs are _always_ last */ + s = no_break(s); + break; + default: + tconfusion("disc4"); + } + } + /* /Add the width of node |s| to |act_width|; */ + s = vlink(s); } - /* fall through */ - case rule_node: - act_width += width(s); - break; - case disc_node: - assert(vlink(s)==null); /* discs are _always_ last */ - s = no_break(s); - break; - default: - tconfusion("disc4"); - } - } - /* /Add the width of node |s| to |act_width|;*/ - s=vlink(s); - } #endif #if FOO + } +#endif /* FOO */ + break; +case math_node: + auto_breaking = (subtype(cur_p) == after); + kern_break(); + break; +case penalty_node: + ext_try_break(penalty(cur_p), unhyphenated_node, pdf_adjust_spacing, + par_shape_ptr, adj_demerits, tracing_paragraphs, + pdf_protrude_chars, line_penalty, last_line_fit, + double_hyphen_demerits, final_hyphen_demerits, + first_p, cur_p); + break; +case mark_node: +case ins_node: +case adjust_node: + break; +case glue_spec_node: + fprintf(stdout, "\nfound a glue_spec in a paragraph!"); + break; +default: + fprintf(stdout, "\ntype=%d", type(cur_p)); + tconfusion("paragraph"); + } + cur_p = vlink(cur_p); + while (cur_p == null && nest_index > 0) { + cur_p = nest_stack[--nest_index]; + /* fprintf(stderr,"Node Pop %d [%d]\n",nest_index,(int)cur_p); */ } -#endif /* FOO */ - break; - case math_node: - auto_breaking=(subtype(cur_p)==after); - kern_break(); - break; - case penalty_node: - ext_try_break(penalty(cur_p),unhyphenated_node, pdf_adjust_spacing, - par_shape_ptr, adj_demerits, tracing_paragraphs, - pdf_protrude_chars, line_penalty, last_line_fit, - double_hyphen_demerits, final_hyphen_demerits,first_p,cur_p); - break; - case mark_node: - case ins_node: - case adjust_node: - break; - case glue_spec_node: - fprintf(stdout, "\nfound a glue_spec in a paragraph!"); - break; - default: - fprintf(stdout, "\ntype=%d",type(cur_p)); - tconfusion("paragraph") ; - } - cur_p=vlink(cur_p); - while (cur_p==null && nest_index>0) { - cur_p = nest_stack[--nest_index]; - /* fprintf(stderr,"Node Pop %d [%d]\n",nest_index,(int)cur_p);*/ - } } - if (cur_p==null) { - /* Try the final line break at the end of the paragraph, - and |goto done| if the desired breakpoints have been found */ - - /* The forced line break at the paragraph's end will reduce the list of - breakpoints so that all active nodes represent breaks at |cur_p=null|. - On the first pass, we insist on finding an active node that has the - correct ``looseness.'' On the final pass, there will be at least one active - node, and we will match the desired looseness as well as we can. - - The global variable |best_bet| will be set to the active node for the best - way to break the paragraph, and a few other variables are used to - help determine what is best. - */ - - ext_try_break(eject_penalty,hyphenated_node, pdf_adjust_spacing, - par_shape_ptr, adj_demerits, tracing_paragraphs, - pdf_protrude_chars,line_penalty, last_line_fit, - double_hyphen_demerits, final_hyphen_demerits,first_p,cur_p); - if (vlink(active)!=active) { - /* @<Find an active node with fewest demerits@>;*/ - r=vlink(active); - fewest_demerits=awful_bad; - do { - if (type(r)!=delta_node) { - if (total_demerits(r)<fewest_demerits) { - fewest_demerits=total_demerits(r); - best_bet=r; - } - } - r=vlink(r); - } while (r!=active); - best_line=line_number(best_bet); - - /* /Find an active node with fewest demerits;*/ - if (looseness==0) - goto DONE; - /*@<Find the best active node for the desired looseness@>;*/ - - /* The adjustment for a desired looseness is a slightly more complicated - version of the loop just considered. Note that if a paragraph is broken - into segments by displayed equations, each segment will be subject to the - looseness calculation, independently of the other segments. - */ - r=vlink(active); - actual_looseness=0; - do { - if (type(r)!=delta_node) { - line_diff=line_number(r)-best_line; - if (((line_diff<actual_looseness)&&(looseness<=line_diff))|| - ((line_diff>actual_looseness)&&(looseness>=line_diff))) { - best_bet=r; - actual_looseness=line_diff; - fewest_demerits=total_demerits(r); - } else if ((line_diff==actual_looseness)&& - (total_demerits(r)<fewest_demerits)) { - best_bet=r; - fewest_demerits=total_demerits(r); - } - } - r=vlink(r); - } while (r!=active); - best_line=line_number(best_bet); - - /* /Find the best active node for the desired looseness;*/ - if ((actual_looseness==looseness)|| final_pass) - goto DONE; - } + if (cur_p == null) { + /* Try the final line break at the end of the paragraph, + and |goto done| if the desired breakpoints have been found */ + + /* The forced line break at the paragraph's end will reduce the list of + breakpoints so that all active nodes represent breaks at |cur_p=null|. + On the first pass, we insist on finding an active node that has the + correct ``looseness.'' On the final pass, there will be at least one active + node, and we will match the desired looseness as well as we can. + + The global variable |best_bet| will be set to the active node for the best + way to break the paragraph, and a few other variables are used to + help determine what is best. + */ + + ext_try_break(eject_penalty, hyphenated_node, pdf_adjust_spacing, + par_shape_ptr, adj_demerits, tracing_paragraphs, + pdf_protrude_chars, line_penalty, last_line_fit, + double_hyphen_demerits, final_hyphen_demerits, first_p, + cur_p); + if (vlink(active) != active) { + /* @<Find an active node with fewest demerits@>; */ + r = vlink(active); + fewest_demerits = awful_bad; + do { + if (type(r) != delta_node) { + if (total_demerits(r) < fewest_demerits) { + fewest_demerits = total_demerits(r); + best_bet = r; + } + } + r = vlink(r); + } while (r != active); + best_line = line_number(best_bet); + + /* /Find an active node with fewest demerits; */ + if (looseness == 0) + goto DONE; + /*@<Find the best active node for the desired looseness@>; */ + + /* The adjustment for a desired looseness is a slightly more complicated + version of the loop just considered. Note that if a paragraph is broken + into segments by displayed equations, each segment will be subject to the + looseness calculation, independently of the other segments. + */ + r = vlink(active); + actual_looseness = 0; + do { + if (type(r) != delta_node) { + line_diff = line_number(r) - best_line; + if (((line_diff < actual_looseness) + && (looseness <= line_diff)) + || ((line_diff > actual_looseness) + && (looseness >= line_diff))) { + best_bet = r; + actual_looseness = line_diff; + fewest_demerits = total_demerits(r); + } else if ((line_diff == actual_looseness) && + (total_demerits(r) < fewest_demerits)) { + best_bet = r; + fewest_demerits = total_demerits(r); + } + } + r = vlink(r); + } while (r != active); + best_line = line_number(best_bet); + + /* /Find the best active node for the desired looseness; */ + if ((actual_looseness == looseness) || final_pass) + goto DONE; + } } - + /* Clean up the memory by removing the break nodes; */ clean_up_the_memory(); /* /Clean up the memory by removing the break nodes; */ - if (!second_pass) { - if (tracing_paragraphs>0) - tprint_nl("@secondpass"); - threshold=tolerance; - second_pass=true; - final_pass=(emergency_stretch<=0); - } else { /* if at first you do not succeed, \dots */ - if (tracing_paragraphs>0) - tprint_nl("@emergencypass"); - background[2] += emergency_stretch; - final_pass=true; + if (!second_pass) { + if (tracing_paragraphs > 0) + tprint_nl("@secondpass"); + threshold = tolerance; + second_pass = true; + final_pass = (emergency_stretch <= 0); + } else { /* if at first you do not succeed, \dots */ + if (tracing_paragraphs > 0) + tprint_nl("@emergencypass"); + background[2] += emergency_stretch; + final_pass = true; } - } - DONE: - if (tracing_paragraphs>0) { - end_diagnostic(true); - normalize_selector(); - } - if (do_last_line_fit) { - /* Adjust \(t)the final line of the paragraph;*/ +} + +DONE: +if (tracing_paragraphs > 0) { + end_diagnostic(true); + normalize_selector(); +} +if (do_last_line_fit) { + /* Adjust \(t)the final line of the paragraph; */ /* Here we either reset |do_last_line_fit| or adjust the |par_fill_skip| glue. */ - if (active_short(best_bet)==0) { - do_last_line_fit=false; - } else { - q=new_spec(glue_ptr(last_line_fill)); - delete_glue_ref(glue_ptr(last_line_fill)); - width(q) += (active_short(best_bet)-active_glue(best_bet)); - stretch(q)=0; - glue_ptr(last_line_fill)=q; + if (active_short(best_bet) == 0) { + do_last_line_fit = false; + } else { + q = new_spec(glue_ptr(last_line_fill)); + delete_glue_ref(glue_ptr(last_line_fill)); + width(q) += (active_short(best_bet) - active_glue(best_bet)); + stretch(q) = 0; + glue_ptr(last_line_fill) = q; } - /* /Adjust \(t)the final line of the paragraph;*/ - } + /* /Adjust \(t)the final line of the paragraph; */ +} /* @<Break the paragraph at the chosen...@>; */ /* Once the best sequence of breakpoints has been found (hurray), we call on the procedure |post_line_break| to finish the remainder of the work. (By introducing this subprocedure, we are able to keep |line_break| from getting extremely long.) - */ - ext_post_line_break(d, - right_skip, - left_skip, - pdf_protrude_chars, - par_shape_ptr, - pdf_adjust_spacing, - pdf_each_line_height, - pdf_each_line_depth, - pdf_first_line_height, - pdf_last_line_depth, - inter_line_penalties_ptr, - inter_line_penalty, - club_penalty, - club_penalties_ptr, - display_widow_penalties_ptr, - widow_penalties_ptr, - display_widow_penalty, - widow_penalty, - broken_penalty, - final_par_glue, - best_bet, - last_special_line, - second_width, - second_indent, - first_width, - first_indent, - best_line - ); + */ +ext_post_line_break(d, + right_skip, + left_skip, + pdf_protrude_chars, + par_shape_ptr, + pdf_adjust_spacing, + pdf_each_line_height, + pdf_each_line_depth, + pdf_first_line_height, + pdf_last_line_depth, + inter_line_penalties_ptr, + inter_line_penalty, + club_penalty, + club_penalties_ptr, + display_widow_penalties_ptr, + widow_penalties_ptr, + display_widow_penalty, + widow_penalty, + broken_penalty, + final_par_glue, + best_bet, + last_special_line, + second_width, + second_indent, first_width, first_indent, best_line); /* /Break the paragraph at the chosen... */ /* Clean up the memory by removing the break nodes; */ - clean_up_the_memory(); +clean_up_the_memory(); /* - flush_node_list(dir_ptr); - dir_ptr=null; - */ + flush_node_list(dir_ptr); + dir_ptr=null; + */ } - - diff --git a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c index 86b9562c262..21451602f8b 100644 --- a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c +++ b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c @@ -1,4 +1,4 @@ -/* $Id: postlinebreak.c 1078 2008-03-05 15:33:46Z taco $ */ +/* $Id: postlinebreak.c 1155 2008-04-14 07:53:21Z oneiros $ */ #include "luatex-api.h" #include <ptexlib.h> @@ -22,19 +22,19 @@ point to a glue node, penalty node, explicit kern node, or math node. */ -static halfword -do_push_dir_node (halfword p, halfword a ) { - halfword n; - n = copy_node(a); - vlink(n) = p; - return n; +static halfword do_push_dir_node(halfword p, halfword a) +{ + halfword n; + n = copy_node(a); + vlink(n) = p; + return n; } -static halfword -do_pop_dir_node ( halfword p ) { - halfword n = vlink(p); - flush_node(p); - return n; +static halfword do_pop_dir_node(halfword p) +{ + halfword n = vlink(p); + flush_node(p); + return n; } @@ -53,460 +53,468 @@ and begin direction instructions at the beginnings of lines. */ -#define next_break prev_break /*new name for |prev_break| after links are reversed*/ +#define next_break prev_break /*new name for |prev_break| after links are reversed */ #define append_list(a,b) \ { vlink(cur_list.tail_field)=vlink((a)); cur_list.tail_field = b; } -#define left_skip_code 7 /*glue at left of justified lines*/ -#define right_skip_code 8 /*glue at right of justified lines*/ +#define left_skip_code 7 /*glue at left of justified lines */ +#define right_skip_code 8 /*glue at right of justified lines */ /* the ints are actually halfwords */ -void ext_post_line_break(boolean d, - int right_skip, - int left_skip, - int pdf_protrude_chars, - halfword par_shape_ptr, - int pdf_adjust_spacing, - int pdf_each_line_height, - int pdf_each_line_depth, - int pdf_first_line_height, - int pdf_last_line_depth, - halfword inter_line_penalties_ptr, - int inter_line_penalty, - int club_penalty, - halfword club_penalties_ptr, - halfword display_widow_penalties_ptr, - halfword widow_penalties_ptr, - int display_widow_penalty, - int widow_penalty, - int broken_penalty, - halfword final_par_glue, - halfword best_bet, - halfword last_special_line, - scaled second_width, - scaled second_indent, - scaled first_width, - scaled first_indent, - halfword best_line ) { - - boolean have_directional = true; - halfword q,r,s; /* temporary registers for list manipulation */ - halfword p, k; - scaled w; - boolean glue_break; /* was a break at glue? */ - boolean disc_break; /*was the current break at a discretionary node?*/ - boolean post_disc_break; /*and did it have a nonempty post-break part?*/ - scaled cur_width; /*width of line number |cur_line|*/ - scaled cur_indent; /*left margin of line number |cur_line|*/ - integer pen; /*use when calculating penalties between lines */ - halfword cur_p; /* cur_p, but localized */ - halfword cur_line; /*the current line number being justified*/ - - dir_ptr = cur_list.dirs_field; - /* @<Reverse the links of the relevant passive nodes, setting |cur_p| to - the first breakpoint@>; */ - /* The job of reversing links in a list is conveniently regarded as the job - of taking items off one stack and putting them on another. In this case we - take them off a stack pointed to by |q| and having |prev_break| fields; - we put them on a stack pointed to by |cur_p| and having |next_break| fields. - Node |r| is the passive node being moved from stack to stack. - */ - q=break_node(best_bet); - /*used_discs = used_disc(best_bet);*/ - /*has_direction*/ - cur_p=null; - do { - r=q; - q=prev_break(q); - next_break(r)=cur_p; - cur_p=r; - } while (q!=null); - /* |cur_p| is now the first breakpoint; */ - - cur_line=cur_list.pg_field+1; /* prevgraf+1 */ - - do { - /* @<Justify the line ending at breakpoint |cur_p|, and append it to the - current vertical list, together with associated penalties and other - insertions@>; */ - /* The current line to be justified appears in a horizontal list starting - at |vlink(temp_head)| and ending at |cur_break(cur_p)|. If |cur_break(cur_p)| is - a glue node, we reset the glue to equal the |right_skip| glue; otherwise - we append the |right_skip| glue at the right. If |cur_break(cur_p)| is a - discretionary node, we modify the list so that the discretionary break - is compulsory, and we set |disc_break| to |true|. We also append - the |left_skip| glue at the left of the line, unless it is zero. */ +void ext_post_line_break(boolean d, + int right_skip, + int left_skip, + int pdf_protrude_chars, + halfword par_shape_ptr, + int pdf_adjust_spacing, + int pdf_each_line_height, + int pdf_each_line_depth, + int pdf_first_line_height, + int pdf_last_line_depth, + halfword inter_line_penalties_ptr, + int inter_line_penalty, + int club_penalty, + halfword club_penalties_ptr, + halfword display_widow_penalties_ptr, + halfword widow_penalties_ptr, + int display_widow_penalty, + int widow_penalty, + int broken_penalty, + halfword final_par_glue, + halfword best_bet, + halfword last_special_line, + scaled second_width, + scaled second_indent, + scaled first_width, + scaled first_indent, halfword best_line) +{ + + boolean have_directional = true; + halfword q, r, s; /* temporary registers for list manipulation */ + halfword p, k; + scaled w; + boolean glue_break; /* was a break at glue? */ + boolean disc_break; /*was the current break at a discretionary node? */ + boolean post_disc_break; /*and did it have a nonempty post-break part? */ + scaled cur_width; /*width of line number |cur_line| */ + scaled cur_indent; /*left margin of line number |cur_line| */ + integer pen; /*use when calculating penalties between lines */ + halfword cur_p; /* cur_p, but localized */ + halfword cur_line; /*the current line number being justified */ + + dir_ptr = cur_list.dirs_field; + /* @<Reverse the links of the relevant passive nodes, setting |cur_p| to + the first breakpoint@>; */ + /* The job of reversing links in a list is conveniently regarded as the job + of taking items off one stack and putting them on another. In this case we + take them off a stack pointed to by |q| and having |prev_break| fields; + we put them on a stack pointed to by |cur_p| and having |next_break| fields. + Node |r| is the passive node being moved from stack to stack. + */ + q = break_node(best_bet); + /*used_discs = used_disc(best_bet); */ + /*has_direction */ + cur_p = null; + do { + r = q; + q = prev_break(q); + next_break(r) = cur_p; + cur_p = r; + } while (q != null); + /* |cur_p| is now the first breakpoint; */ + + cur_line = cur_list.pg_field + 1; /* prevgraf+1 */ + + do { + /* @<Justify the line ending at breakpoint |cur_p|, and append it to the + current vertical list, together with associated penalties and other + insertions@>; */ + /* The current line to be justified appears in a horizontal list starting + at |vlink(temp_head)| and ending at |cur_break(cur_p)|. If |cur_break(cur_p)| is + a glue node, we reset the glue to equal the |right_skip| glue; otherwise + we append the |right_skip| glue at the right. If |cur_break(cur_p)| is a + discretionary node, we modify the list so that the discretionary break + is compulsory, and we set |disc_break| to |true|. We also append + the |left_skip| glue at the left of the line, unless it is zero. */ #if 0 - tprint("BEGIN OF LINE "); - print_int(cur_break(cur_p)); - breadth_max=100000; - depth_threshold=100000; - show_node_list(temp_head); + tprint("BEGIN OF LINE "); + print_int(cur_break(cur_p)); + breadth_max = 100000; + depth_threshold = 100000; + show_node_list(temp_head); #endif - - /* DIR: Insert dir nodes at the beginning of the current line;*/ - for (q=dir_ptr;q!=null;q=vlink(q)) { - halfword tmp = new_dir(dir_dir(q)); - halfword nxt = vlink(temp_head); - couple_nodes(temp_head,tmp); - couple_nodes(tmp,nxt); - } - if (dir_ptr!=null) { - flush_node_list(dir_ptr); dir_ptr=null; - } - - /* Modify the end of the line to reflect the nature of the break and to - include \.{\\rightskip}; also set the proper value of |disc_break|; */ - /* At the end of the following code, |q| will point to the final node on the - list about to be justified. In the meanwhile |r| will point to the - node we will use to insert end-of-line stuff after. |q==null| means - we use the final position of |r| */ - r = cur_break(cur_p); - q = null; - disc_break=false; - post_disc_break=false; - glue_break = false; - - if (r==null) { - for (r=temp_head; vlink(r)!=null; r=vlink(r)); - if (r==final_par_glue) { - /* This should almost always be true... */ - /* TODO assert ? */ - q = r; - /* |q| refers to the last node of the line (and paragraph) */ - r = alink(r); - } - /* |r| refers to the node after which the dir nodes should be closed */ - } else if (type(r)==glue_node) { - delete_glue_ref(glue_ptr(r)); - glue_ptr(r)=right_skip; - subtype(r)=right_skip_code+1; - incr(glue_ref_count(right_skip)); - glue_break = true; - /* |q| refers to the last node of the line */ - q = r; - r=alink(r); - assert(vlink(r)==q); - /* |r| refers to the node after which the dir nodes should be closed */ - } else if (type(r)==disc_node) { - halfword a = alink(r); - assert(a!=null); - halfword v = vlink(r); - if (v==null) { /* nested disc, let's unfold */ - fprintf(stderr,"Nested disc [%d]<-[%d]->null\n",(int)a,(int)r); - do { - halfword d; - while (alink(a)!=null) a = alink(a); - assert(type(a)==nesting_node); - assert(subtype(a)=no_break_head(0)); /* No_break */ - d = a - subtype(a); /* MAGIC subtype is offset of nesting with disc */ - assert(type(d)==disc_node); - v = vlink(d); - a = alink(d); - fprintf(stderr,"Up to disc [%d]<-[%d]->[%d] & link [%d]\n",(int)a,(int)d,(int)v,(int)vlink_no_break(d)); - assert(a!=null); - couple_nodes(a,vlink_no_break(d)); - vlink_no_break(d)=null; - tlink_no_break(d)=null; - flush_node(d); - } while (v==null); - couple_nodes(r,v); - fprintf(stderr,"Close list [%d]<->[%d] a=[%d]\n",(int)r,(int)v,(int)alink(r)); - a = alink(r); - } - if (vlink_no_break(r)!=null) { - flush_node_list(vlink_no_break(r)); - vlink_no_break(r)=null; - tlink_no_break(r)=null; - } - if (vlink_pre_break(r)!=null) { - couple_nodes(a,vlink_pre_break(r)); - couple_nodes(tlink_pre_break(r),r); - vlink_pre_break(r)=null; - tlink_pre_break(r)=null; - } - if (vlink_post_break(r)!=null) { - couple_nodes(r,vlink_post_break(r)); - couple_nodes(tlink_post_break(r),v); - vlink_post_break(r)=null; - tlink_post_break(r)=null; - post_disc_break = true; - } - disc_break=true; - } else if (type(r)==kern_node) { - width(r)=0; - } else if (type(r)==math_node) { - surround(r)=0; - } - - /* DIR: Adjust the dir stack based on dir nodes in this line; */ - /* TODO what about the previousparagraph ??? */ - if (have_directional) { - halfword e; - halfword p; - for(e=vlink(temp_head); e!=null && e!=cur_break(cur_p); e=vlink(e)) { - if (type(e)!=whatsit_node || subtype(e)!=dir_node) - continue; - if (dir_dir(e)>=0) { - dir_ptr = do_push_dir_node(dir_ptr,e); - } else if (dir_ptr!=null && dir_dir(dir_ptr)==(dir_dir(e)+64)) { - dir_ptr = do_pop_dir_node(dir_ptr); + + /* DIR: Insert dir nodes at the beginning of the current line; */ + for (q = dir_ptr; q != null; q = vlink(q)) { + halfword tmp = new_dir(dir_dir(q)); + halfword nxt = vlink(temp_head); + couple_nodes(temp_head, tmp); + couple_nodes(tmp, nxt); + } + if (dir_ptr != null) { + flush_node_list(dir_ptr); + dir_ptr = null; + } + + /* Modify the end of the line to reflect the nature of the break and to + include \.{\\rightskip}; also set the proper value of |disc_break|; */ + /* At the end of the following code, |q| will point to the final node on the + list about to be justified. In the meanwhile |r| will point to the + node we will use to insert end-of-line stuff after. |q==null| means + we use the final position of |r| */ + r = cur_break(cur_p); + q = null; + disc_break = false; + post_disc_break = false; + glue_break = false; + + if (r == null) { + for (r = temp_head; vlink(r) != null; r = vlink(r)); + if (r == final_par_glue) { + /* This should almost always be true... */ + /* TODO assert ? */ + q = r; + /* |q| refers to the last node of the line (and paragraph) */ + r = alink(r); + } + /* |r| refers to the node after which the dir nodes should be closed */ + } else if (type(r) == glue_node) { + delete_glue_ref(glue_ptr(r)); + glue_ptr(r) = right_skip; + subtype(r) = right_skip_code + 1; + incr(glue_ref_count(right_skip)); + glue_break = true; + /* |q| refers to the last node of the line */ + q = r; + r = alink(r); + assert(vlink(r) == q); + /* |r| refers to the node after which the dir nodes should be closed */ + } else if (type(r) == disc_node) { + halfword a = alink(r); + assert(a != null); + halfword v = vlink(r); + if (v == null) { /* nested disc, let's unfold */ + fprintf(stderr, "Nested disc [%d]<-[%d]->null\n", (int) a, + (int) r); + do { + halfword d; + while (alink(a) != null) + a = alink(a); + assert(type(a) == nesting_node); + assert(subtype(a) = no_break_head(0)); /* No_break */ + d = a - subtype(a); /* MAGIC subtype is offset of nesting with disc */ + assert(type(d) == disc_node); + v = vlink(d); + a = alink(d); + fprintf(stderr, "Up to disc [%d]<-[%d]->[%d] & link [%d]\n", + (int) a, (int) d, (int) v, (int) vlink_no_break(d)); + assert(a != null); + couple_nodes(a, vlink_no_break(d)); + vlink_no_break(d) = null; + tlink_no_break(d) = null; + flush_node(d); + } while (v == null); + couple_nodes(r, v); + fprintf(stderr, "Close list [%d]<->[%d] a=[%d]\n", (int) r, + (int) v, (int) alink(r)); + a = alink(r); + } + if (vlink_no_break(r) != null) { + flush_node_list(vlink_no_break(r)); + vlink_no_break(r) = null; + tlink_no_break(r) = null; + } + if (vlink_pre_break(r) != null) { + couple_nodes(a, vlink_pre_break(r)); + couple_nodes(tlink_pre_break(r), r); + vlink_pre_break(r) = null; + tlink_pre_break(r) = null; + } + if (vlink_post_break(r) != null) { + couple_nodes(r, vlink_post_break(r)); + couple_nodes(tlink_post_break(r), v); + vlink_post_break(r) = null; + tlink_post_break(r) = null; + post_disc_break = true; + } + disc_break = true; + } else if (type(r) == kern_node) { + width(r) = 0; + } else if (type(r) == math_node) { + surround(r) = 0; + } + + /* DIR: Adjust the dir stack based on dir nodes in this line; */ + /* TODO what about the previousparagraph ??? */ + if (have_directional) { + halfword e; + halfword p; + for (e = vlink(temp_head); e != null && e != cur_break(cur_p); + e = vlink(e)) { + if (type(e) != whatsit_node || subtype(e) != dir_node) + continue; + if (dir_dir(e) >= 0) { + dir_ptr = do_push_dir_node(dir_ptr, e); + } else if (dir_ptr != null + && dir_dir(dir_ptr) == (dir_dir(e) + 64)) { + dir_ptr = do_pop_dir_node(dir_ptr); + } + } + assert(e == cur_break(cur_p)); + + /* @<DIR: Insert dir nodes at the end of the current line@>; */ + e = vlink(r); + for (p = dir_ptr; p != null; p = vlink(p)) { + halfword s = new_dir(dir_dir(p) - 64); + couple_nodes(r, s); + try_couple_nodes(s, e); + r = s; + } + } + if (passive_right_box(cur_p) != null) { + /* TODO: CHECK has |s| below a |alink| ? */ + halfword s = copy_node_list(passive_right_box(cur_p)); + halfword e = vlink(r); + couple_nodes(r, s); + try_couple_nodes(s, e); + r = s; + } + if (q == null) { + q = r; + } + /* Now [q] refers to the last node on the line */ + + /* FIXME from this point on we no longer keep alink() valid */ + + /* at this point |q| is the rightmost breakpoint; the only exception is + the case of a discretionary break with non-empty |pre_break|, then |q| + has been changed to the last node of the |pre_break| list */ + /* If the par ends with a \break command, the last line is utterly empty. + That is the case of |q==temp_head| */ + if (q != temp_head && pdf_protrude_chars > 0) { + halfword ptmp; + if (disc_break && (is_char_node(q) || (type(q) != disc_node))) { + p = q; /* |q| has been reset to the last node of |pre_break| */ + ptmp = p; + } else { + p = alink(q); /* get |vlink(p) = q| */ + assert(vlink(p) == q); + ptmp = p; + p = find_protchar_right(vlink(temp_head), p); + } + w = char_pw(p, right_side); + if (w != 0) { /* we have found a marginal kern, append it after |ptmp| */ + k = new_margin_kern(-w, last_rightmost_char, right_side); + vlink(k) = vlink(ptmp); + vlink(ptmp) = k; + if (ptmp == q) + q = vlink(q); + } + } + /* if |q| was not a breakpoint at glue and has been reset to |rightskip| + then we append |rightskip| after |q| now */ + if (!glue_break) { + /* @<Put the \(r)\.{\\rightskip} glue after node |q|@>; */ + halfword r = new_param_glue(right_skip_code); + vlink(r) = vlink(q); + vlink(q) = r; + q = r; } - } - assert(e==cur_break(cur_p)); - - /* @<DIR: Insert dir nodes at the end of the current line@>;*/ - e = vlink(r); - for (p=dir_ptr; p!=null; p=vlink(p)) { - halfword s=new_dir(dir_dir(p)-64); - couple_nodes(r,s); - try_couple_nodes(s,e); - r=s; - } - } - if (passive_right_box(cur_p)!=null) { - /* TODO: CHECK has |s| below a |alink| ? */ - halfword s = copy_node_list(passive_right_box(cur_p)); - halfword e = vlink(r); - couple_nodes(r,s); - try_couple_nodes(s,e); - r=s; - } - if (q==null) { - q = r; - } - /* Now [q] refers to the last node on the line */ - - /* FIXME from this point on we no longer keep alink() valid */ - - /* at this point |q| is the rightmost breakpoint; the only exception is - the case of a discretionary break with non-empty |pre_break|, then |q| - has been changed to the last node of the |pre_break| list */ - /* If the par ends with a \break command, the last line is utterly empty. - That is the case of |q==temp_head| */ - if (q!=temp_head && pdf_protrude_chars > 0) { - halfword ptmp; - if (disc_break && (is_char_node(q) || (type(q) != disc_node))) { - p = q; /* |q| has been reset to the last node of |pre_break| */ - ptmp = p; - } else { - p = alink(q); /* get |vlink(p) = q| */ - assert(vlink(p)==q); - ptmp = p; - p = find_protchar_right(vlink(temp_head), p); - } - w = char_pw(p, right_side); - if (w!=0) { /* we have found a marginal kern, append it after |ptmp| */ - k = new_margin_kern(-w, last_rightmost_char, right_side); - vlink(k) = vlink(ptmp); - vlink(ptmp) = k; - if (ptmp == q) - q = vlink(q); - } - } - /* if |q| was not a breakpoint at glue and has been reset to |rightskip| - then we append |rightskip| after |q| now */ - if (!glue_break) { - /* @<Put the \(r)\.{\\rightskip} glue after node |q|@>;*/ - halfword r=new_param_glue(right_skip_code); - vlink(r)=vlink(q); - vlink(q)=r; - q=r; - } - - /* /Modify the end of the line to reflect the nature of the break and to - include \.{\\rightskip}; also set the proper value of |disc_break|; */ - - /* Put the \(l)\.{\\leftskip} glue at the left and detach this line;*/ - /* The following code begins with |q| at the end of the list to be - justified. It ends with |q| at the beginning of that list, and with - |vlink(temp_head)| pointing to the remainder of the paragraph, if any. */ - r=vlink(q); - vlink(q)=null; - - q=vlink(temp_head); - try_couple_nodes(temp_head,r); - if (passive_left_box(cur_p)!=null && passive_left_box(cur_p)!=0) { - /* omega bits: */ - r=copy_node_list(passive_left_box(cur_p)); - s=vlink(q); - vlink(r)=q; - q=r; - if ((cur_line==cur_list.pg_field+1) && (s!=null)) { - if (type(s)==hlist_node) { - if (list_ptr(s)==null) { - q=vlink(q); - vlink(r)=vlink(s); - vlink(s)=r; - } - } - } - } - /*at this point |q| is the leftmost node; all discardable nodes have been discarded */ - if (pdf_protrude_chars > 0) { - p = q; - p = find_protchar_left(p, false); /* no more discardables */ - w = char_pw(p, left_side); - if (w != 0) { - k = new_margin_kern(-w, last_leftmost_char, left_side); - vlink(k) = q; - q = k; - } - }; - if (left_skip!=zero_glue) { - r=new_param_glue(left_skip_code); - vlink(r)=q; - q=r; - } - /* /Put the \(l)\.{\\leftskip} glue at the left and detach this line;*/ - - /* Call the packaging subroutine, setting |just_box| to the justified box;*/ - /* Now |q| points to the hlist that represents the current line of the - paragraph. We need to compute the appropriate line width, pack the - line into a box of this size, and shift the box by the appropriate - amount of indentation. */ - if (cur_line>last_special_line) { - cur_width=second_width; - cur_indent=second_indent; - } else if (par_shape_ptr==null) { - cur_width=first_width; - cur_indent=first_indent; - } else { - cur_indent=varmem[(par_shape_ptr+2*cur_line)].cint; - cur_width=varmem[(par_shape_ptr+2*cur_line+1)].cint; - } - adjust_tail=adjust_head; - pack_direction=paragraph_dir; - pre_adjust_tail = pre_adjust_head; - if (pdf_adjust_spacing > 0 ) { - just_box = hpack(q, cur_width, cal_expand_ratio); - } else { - just_box = hpack(q, cur_width, exactly); - } - shift_amount(just_box)=cur_indent; - /* /Call the packaging subroutine, setting |just_box| to the justified box;*/ - - /* Append the new box to the current vertical list, followed by the list of - special nodes taken out of the box by the packager;*/ - if (pdf_each_line_height!=0) - height(just_box) = pdf_each_line_height; - if (pdf_each_line_depth != 0) - depth(just_box) = pdf_each_line_depth; - if ((pdf_first_line_height != 0) && (cur_line == cur_list.pg_field + 1)) - height(just_box) = pdf_first_line_height; - if ((pdf_last_line_depth != 0) && (cur_line + 1 == best_line)) - depth(just_box) = pdf_last_line_depth; - - if (pre_adjust_head != pre_adjust_tail) - append_list(pre_adjust_head,pre_adjust_tail); - pre_adjust_tail = null; - append_to_vlist(just_box); - if (adjust_head != adjust_tail) - append_list(adjust_head,adjust_tail); - adjust_tail = null; - - /* /Append the new box to the current vertical list, followed by the list of - special nodes taken out of the box by the packager;*/ - - /* Append a penalty node, if a nonzero penalty is appropriate */ - /* Penalties between the lines of a paragraph come from club and widow lines, - from the |inter_line_penalty| parameter, and from lines that end at - discretionary breaks. Breaking between lines of a two-line paragraph gets - both club-line and widow-line penalties. The local variable |pen| will - be set to the sum of all relevant penalties for the current line, except - that the final line is never penalized. */ - if (cur_line+1!=best_line) { - q=inter_line_penalties_ptr; - if (q!=null) { - r=cur_line; - if (r>penalty(q)) - r=penalty(q); - pen=penalty(q+r); - } else { - if (passive_pen_inter(cur_p)!=0) { - pen=passive_pen_inter(cur_p); - } else { - pen=inter_line_penalty; - } - } - q=club_penalties_ptr; - if (q!=null) { - r=cur_line-cur_list.pg_field; /* prevgraf */ - if (r>penalty(q)) - r=penalty(q); - pen += penalty(q+r); - } else if (cur_line==cur_list.pg_field+1) { /* prevgraf */ - pen += club_penalty; - } - if (d) - q = display_widow_penalties_ptr; - else - q = widow_penalties_ptr; - if (q!=null) { - r=best_line-cur_line-1; - if (r>penalty(q)) - r=penalty(q); - pen += penalty(q+r); - } else if (cur_line+2==best_line) { - if (d) - pen += display_widow_penalty; - else - pen += widow_penalty; - } - if (disc_break) { - if (passive_pen_broken(cur_p)!=0) { - pen += passive_pen_broken(cur_p); - } else { - pen += broken_penalty; - } - } - if (pen!=0) { - r=new_penalty(pen); - couple_nodes(cur_list.tail_field,r); - cur_list.tail_field=r; - } - } - /* /Append a penalty node, if a nonzero penalty is appropriate */ - - /* /Justify the line ending at breakpoint |cur_p|, and append it to the - current vertical list, together with associated penalties and other - insertions@>; */ - incr(cur_line); - cur_p=next_break(cur_p); - if (cur_p!=null && !post_disc_break) { - /* @<Prune unwanted nodes at the beginning of the next line@>; */ - /* Glue and penalty and kern and math nodes are deleted at the - beginning of a line, except in the anomalous case that the - node to be deleted is actually one of the chosen - breakpoints. Otherwise the pruning done here is designed to - match the lookahead computation in |try_break|, where the - |break_width| values are computed for non-discretionary - breakpoints. */ - r=temp_head; - while(1) { - q=vlink(r); - if (q==cur_break(cur_p) || is_char_node(q)) - break; - if (!((type(q)==whatsit_node)&&(subtype(q)==local_par_node))) { - if (non_discardable(q) - || (type(q)==kern_node && subtype(q)!=explicit )) - break; - } - r=q; - }; - if (r!=temp_head) { - vlink(r)=null; - flush_node_list(vlink(temp_head)); - try_couple_nodes(temp_head,q); - } - } - } while (cur_p!=null); - if ((cur_line!=best_line)||(vlink(temp_head)!=null)) - tconfusion("line breaking"); - cur_list.pg_field=best_line-1; /* prevgraf */ - cur_list.dirs_field=dir_ptr; /* dir_save */ - dir_ptr = null; -} + /* /Modify the end of the line to reflect the nature of the break and to + include \.{\\rightskip}; also set the proper value of |disc_break|; */ + + /* Put the \(l)\.{\\leftskip} glue at the left and detach this line; */ + /* The following code begins with |q| at the end of the list to be + justified. It ends with |q| at the beginning of that list, and with + |vlink(temp_head)| pointing to the remainder of the paragraph, if any. */ + r = vlink(q); + vlink(q) = null; + + q = vlink(temp_head); + try_couple_nodes(temp_head, r); + if (passive_left_box(cur_p) != null && passive_left_box(cur_p) != 0) { + /* omega bits: */ + r = copy_node_list(passive_left_box(cur_p)); + s = vlink(q); + vlink(r) = q; + q = r; + if ((cur_line == cur_list.pg_field + 1) && (s != null)) { + if (type(s) == hlist_node) { + if (list_ptr(s) == null) { + q = vlink(q); + vlink(r) = vlink(s); + vlink(s) = r; + } + } + } + } + /*at this point |q| is the leftmost node; all discardable nodes have been discarded */ + if (pdf_protrude_chars > 0) { + p = q; + p = find_protchar_left(p, false); /* no more discardables */ + w = char_pw(p, left_side); + if (w != 0) { + k = new_margin_kern(-w, last_leftmost_char, left_side); + vlink(k) = q; + q = k; + } + }; + if (left_skip != zero_glue) { + r = new_param_glue(left_skip_code); + vlink(r) = q; + q = r; + } + /* /Put the \(l)\.{\\leftskip} glue at the left and detach this line; */ + + /* Call the packaging subroutine, setting |just_box| to the justified box; */ + /* Now |q| points to the hlist that represents the current line of the + paragraph. We need to compute the appropriate line width, pack the + line into a box of this size, and shift the box by the appropriate + amount of indentation. */ + if (cur_line > last_special_line) { + cur_width = second_width; + cur_indent = second_indent; + } else if (par_shape_ptr == null) { + cur_width = first_width; + cur_indent = first_indent; + } else { + cur_indent = varmem[(par_shape_ptr + 2 * cur_line)].cint; + cur_width = varmem[(par_shape_ptr + 2 * cur_line + 1)].cint; + } + adjust_tail = adjust_head; + pack_direction = paragraph_dir; + pre_adjust_tail = pre_adjust_head; + if (pdf_adjust_spacing > 0) { + just_box = hpack(q, cur_width, cal_expand_ratio); + } else { + just_box = hpack(q, cur_width, exactly); + } + shift_amount(just_box) = cur_indent; + /* /Call the packaging subroutine, setting |just_box| to the justified box; */ + + /* Append the new box to the current vertical list, followed by the list of + special nodes taken out of the box by the packager; */ + if (pdf_each_line_height != 0) + height(just_box) = pdf_each_line_height; + if (pdf_each_line_depth != 0) + depth(just_box) = pdf_each_line_depth; + if ((pdf_first_line_height != 0) && (cur_line == cur_list.pg_field + 1)) + height(just_box) = pdf_first_line_height; + if ((pdf_last_line_depth != 0) && (cur_line + 1 == best_line)) + depth(just_box) = pdf_last_line_depth; + + if (pre_adjust_head != pre_adjust_tail) + append_list(pre_adjust_head, pre_adjust_tail); + pre_adjust_tail = null; + append_to_vlist(just_box); + if (adjust_head != adjust_tail) + append_list(adjust_head, adjust_tail); + adjust_tail = null; + + /* /Append the new box to the current vertical list, followed by the list of + special nodes taken out of the box by the packager; */ + + /* Append a penalty node, if a nonzero penalty is appropriate */ + /* Penalties between the lines of a paragraph come from club and widow lines, + from the |inter_line_penalty| parameter, and from lines that end at + discretionary breaks. Breaking between lines of a two-line paragraph gets + both club-line and widow-line penalties. The local variable |pen| will + be set to the sum of all relevant penalties for the current line, except + that the final line is never penalized. */ + if (cur_line + 1 != best_line) { + q = inter_line_penalties_ptr; + if (q != null) { + r = cur_line; + if (r > penalty(q)) + r = penalty(q); + pen = penalty(q + r); + } else { + if (passive_pen_inter(cur_p) != 0) { + pen = passive_pen_inter(cur_p); + } else { + pen = inter_line_penalty; + } + } + q = club_penalties_ptr; + if (q != null) { + r = cur_line - cur_list.pg_field; /* prevgraf */ + if (r > penalty(q)) + r = penalty(q); + pen += penalty(q + r); + } else if (cur_line == cur_list.pg_field + 1) { /* prevgraf */ + pen += club_penalty; + } + if (d) + q = display_widow_penalties_ptr; + else + q = widow_penalties_ptr; + if (q != null) { + r = best_line - cur_line - 1; + if (r > penalty(q)) + r = penalty(q); + pen += penalty(q + r); + } else if (cur_line + 2 == best_line) { + if (d) + pen += display_widow_penalty; + else + pen += widow_penalty; + } + if (disc_break) { + if (passive_pen_broken(cur_p) != 0) { + pen += passive_pen_broken(cur_p); + } else { + pen += broken_penalty; + } + } + if (pen != 0) { + r = new_penalty(pen); + couple_nodes(cur_list.tail_field, r); + cur_list.tail_field = r; + } + } + /* /Append a penalty node, if a nonzero penalty is appropriate */ + + /* /Justify the line ending at breakpoint |cur_p|, and append it to the + current vertical list, together with associated penalties and other + insertions@>; */ + incr(cur_line); + cur_p = next_break(cur_p); + if (cur_p != null && !post_disc_break) { + /* @<Prune unwanted nodes at the beginning of the next line@>; */ + /* Glue and penalty and kern and math nodes are deleted at the + beginning of a line, except in the anomalous case that the + node to be deleted is actually one of the chosen + breakpoints. Otherwise the pruning done here is designed to + match the lookahead computation in |try_break|, where the + |break_width| values are computed for non-discretionary + breakpoints. */ + r = temp_head; + while (1) { + q = vlink(r); + if (q == cur_break(cur_p) || is_char_node(q)) + break; + if (! + ((type(q) == whatsit_node) + && (subtype(q) == local_par_node))) { + if (non_discardable(q) + || (type(q) == kern_node && subtype(q) != explicit)) + break; + } + r = q; + }; + if (r != temp_head) { + vlink(r) = null; + flush_node_list(vlink(temp_head)); + try_couple_nodes(temp_head, q); + } + } + } while (cur_p != null); + if ((cur_line != best_line) || (vlink(temp_head) != null)) + tconfusion("line breaking"); + cur_list.pg_field = best_line - 1; /* prevgraf */ + cur_list.dirs_field = dir_ptr; /* dir_save */ + dir_ptr = null; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/texnodes.c b/Build/source/texk/web2c/luatexdir/tex/texnodes.c index 06004160503..bb477e54ffb 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texnodes.c +++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.c @@ -1,4 +1,4 @@ -/* $Id: texnodes.c 1103 2008-03-11 14:32:50Z taco $ */ +/* $Id: texnodes.c 1168 2008-04-15 13:43:34Z taco $ */ #include "luatex-api.h" #include <ptexlib.h> @@ -15,19 +15,19 @@ char *varmem_sizes = NULL; halfword var_mem_max = 0; halfword rover = 0; -halfword free_chain[MAX_CHAIN_SIZE] = {null}; +halfword free_chain[MAX_CHAIN_SIZE] = { null }; -static int prealloc=0; +static int prealloc = 0; -int fix_node_lists=1; +int fix_node_lists = 1; int free_error_seen = 0; int copy_error_seen = 0; -halfword slow_get_node (integer s) ; /* defined below */ -int copy_error (halfword p) ; /* define below */ +halfword slow_get_node(integer s); /* defined below */ +int copy_error(halfword p); /* define below */ -#undef link /* defined by cpascal.h */ +#undef link /* defined by cpascal.h */ #define info(a) fixmem[(a)].hhlh #define link(a) fixmem[(a)].hhrh @@ -38,799 +38,892 @@ int copy_error (halfword p) ; /* define below */ #define variable_node_size 2 -char * node_fields_list [] = { "attr", "width", "depth", "height", "dir", "shift", - "glue_order", "glue_sign", "glue_set" , "list", NULL }; -char * node_fields_rule [] = { "attr", "width", "depth", "height", "dir", NULL }; -char * node_fields_insert [] = { "attr", "cost", "depth", "height", "spec", "list", NULL }; -char * node_fields_mark [] = { "attr", "class", "mark", NULL }; -char * node_fields_adjust [] = { "attr", "list", NULL }; -char * node_fields_disc [] = { "attr", "pre", "post", "replace", NULL }; -char * node_fields_math [] = { "attr", "surround", NULL }; -char * node_fields_glue [] = { "attr", "spec", "leader", NULL }; -char * node_fields_kern [] = { "attr", "kern", NULL }; -char * node_fields_penalty [] = { "attr", "penalty", NULL }; -char * node_fields_unset [] = { "attr", "width", "depth", "height", "dir", "shrink", - "glue_order", "glue_sign", "stretch" , "span", "list", NULL }; -char * node_fields_margin_kern [] = { "attr", "width", "glyph", NULL }; -char * node_fields_glyph [] = { "attr", "char", "font", "lang", "left", "right", "uchyph", - "components", "xoffset", "yoffset", NULL }; -char * node_fields_style [] = { NULL }; -char * node_fields_choice [] = { NULL }; -char * node_fields_ord [] = { NULL }; -char * node_fields_op [] = { NULL }; -char * node_fields_bin [] = { NULL }; -char * node_fields_rel [] = { NULL }; -char * node_fields_open [] = { NULL }; -char * node_fields_close [] = { NULL }; -char * node_fields_punct [] = { NULL }; -char * node_fields_inner [] = { NULL }; -char * node_fields_radical [] = { NULL }; -char * node_fields_fraction [] = { NULL }; -char * node_fields_under [] = { NULL }; -char * node_fields_over [] = { NULL }; -char * node_fields_accent [] = { NULL }; -char * node_fields_vcenter [] = { NULL }; -char * node_fields_left [] = { NULL }; -char * node_fields_right [] = { NULL }; - -char * node_fields_inserting [] = { "height", "last_ins_ptr", "best_ins_ptr", NULL}; - -char * node_fields_splitup [] = { "height", "last_ins_ptr", "best_ins_ptr", - "broken_ptr", "broken_ins", NULL}; - -char * node_fields_action [] = { "action_type", "named_id", "action_id", - "file", "new_window", "data", "ref_count", NULL }; -char * node_fields_attribute [] = { "number", "value", NULL }; -char * node_fields_glue_spec [] = { "width", "stretch", "shrink", - "stretch_order", "shrink_order", "ref_count", NULL }; -char * node_fields_attribute_list [] = { NULL }; - -char * node_fields_whatsit_open [] = { "attr", "stream", "name", "area", "ext", NULL }; -char * node_fields_whatsit_write [] = { "attr", "stream", "data", NULL }; -char * node_fields_whatsit_close [] = { "attr", "stream", NULL }; -char * node_fields_whatsit_special [] = { "attr", "data", NULL }; - -char * node_fields_whatsit_local_par [] = { "attr", "pen_inter", "pen_broken", "dir", - "box_left", "box_left_width", "box_right", "box_right_width", NULL }; -char * node_fields_whatsit_dir [] = { "attr", "dir", "level", "dvi_ptr", "dvi_h", NULL }; - -char * node_fields_whatsit_pdf_literal [] = { "attr", "mode", "data", NULL }; -char * node_fields_whatsit_pdf_refobj [] = { "attr", "objnum", NULL }; -char * node_fields_whatsit_pdf_refxform [] = { "attr", "width", "height", "depth", "objnum", NULL }; -char * node_fields_whatsit_pdf_refximage [] = { "attr", "width", "height", "depth", "objnum", NULL }; -char * node_fields_whatsit_pdf_annot [] = { "attr", "width", "height", "depth", "objnum", "data", NULL }; -char * node_fields_whatsit_pdf_start_link [] = { "attr", "width", "height", "depth", - "objnum", "link_attr", "action", NULL }; -char * node_fields_whatsit_pdf_end_link [] = { "attr", NULL }; -char * node_fields_whatsit_pdf_dest [] = { "attr", "width", "height", "depth", - "named_id", "dest_id", "dest_type", "xyz_zoom", "objnum", NULL }; -char * node_fields_whatsit_pdf_thread [] = { "attr", "width", "height", "depth", - "named_id", "thread_id", "thread_attr", NULL }; -char * node_fields_whatsit_pdf_start_thread [] = { "attr", "width", "height", "depth", - "named_id", "thread_id", "thread_attr", NULL }; -char * node_fields_whatsit_pdf_end_thread [] = { "attr", NULL }; -char * node_fields_whatsit_pdf_save_pos [] = { "attr", NULL }; -char * node_fields_whatsit_late_lua [] = { "attr", "reg", "data", NULL }; -char * node_fields_whatsit_close_lua [] = { "attr", "reg", NULL }; -char * node_fields_whatsit_pdf_colorstack [] = { "attr", "stack", "cmd", "data", NULL }; -char * node_fields_whatsit_pdf_setmatrix [] = { "attr", "data", NULL }; -char * node_fields_whatsit_pdf_save [] = { "attr", NULL }; -char * node_fields_whatsit_pdf_restore [] = { "attr", NULL }; -char * node_fields_whatsit_cancel_boundary [] = { "attr", NULL }; -char * node_fields_whatsit_user_defined [] = { "attr", "user_id", "type", "value", NULL }; +char *node_fields_list[] = { "attr", "width", "depth", "height", "dir", "shift", + "glue_order", "glue_sign", "glue_set", "list", NULL +}; +char *node_fields_rule[] = { "attr", "width", "depth", "height", "dir", NULL }; +char *node_fields_insert[] = + { "attr", "cost", "depth", "height", "spec", "list", NULL }; +char *node_fields_mark[] = { "attr", "class", "mark", NULL }; +char *node_fields_adjust[] = { "attr", "list", NULL }; +char *node_fields_disc[] = { "attr", "pre", "post", "replace", NULL }; +char *node_fields_math[] = { "attr", "surround", NULL }; +char *node_fields_glue[] = { "attr", "spec", "leader", NULL }; +char *node_fields_kern[] = { "attr", "kern", NULL }; +char *node_fields_penalty[] = { "attr", "penalty", NULL }; +char *node_fields_unset[] = + { "attr", "width", "depth", "height", "dir", "shrink", + "glue_order", "glue_sign", "stretch", "span", "list", NULL +}; +char *node_fields_margin_kern[] = { "attr", "width", "glyph", NULL }; +char *node_fields_glyph[] = + { "attr", "char", "font", "lang", "left", "right", "uchyph", + "components", "xoffset", "yoffset", NULL +}; +char *node_fields_style[] = { NULL }; +char *node_fields_choice[] = { NULL }; +char *node_fields_ord[] = { NULL }; +char *node_fields_op[] = { NULL }; +char *node_fields_bin[] = { NULL }; +char *node_fields_rel[] = { NULL }; +char *node_fields_open[] = { NULL }; +char *node_fields_close[] = { NULL }; +char *node_fields_punct[] = { NULL }; +char *node_fields_inner[] = { NULL }; +char *node_fields_radical[] = { NULL }; +char *node_fields_fraction[] = { NULL }; +char *node_fields_under[] = { NULL }; +char *node_fields_over[] = { NULL }; +char *node_fields_accent[] = { NULL }; +char *node_fields_vcenter[] = { NULL }; +char *node_fields_left[] = { NULL }; +char *node_fields_right[] = { NULL }; + +char *node_fields_inserting[] = + { "height", "last_ins_ptr", "best_ins_ptr", NULL }; + +char *node_fields_splitup[] = { "height", "last_ins_ptr", "best_ins_ptr", + "broken_ptr", "broken_ins", NULL +}; + +char *node_fields_action[] = { "action_type", "named_id", "action_id", + "file", "new_window", "data", "ref_count", NULL +}; +char *node_fields_attribute[] = { "number", "value", NULL }; +char *node_fields_glue_spec[] = { "width", "stretch", "shrink", + "stretch_order", "shrink_order", "ref_count", NULL +}; +char *node_fields_attribute_list[] = { NULL }; + +char *node_fields_whatsit_open[] = + { "attr", "stream", "name", "area", "ext", NULL }; +char *node_fields_whatsit_write[] = { "attr", "stream", "data", NULL }; +char *node_fields_whatsit_close[] = { "attr", "stream", NULL }; +char *node_fields_whatsit_special[] = { "attr", "data", NULL }; + +char *node_fields_whatsit_local_par[] = + { "attr", "pen_inter", "pen_broken", "dir", + "box_left", "box_left_width", "box_right", "box_right_width", NULL +}; +char *node_fields_whatsit_dir[] = + { "attr", "dir", "level", "dvi_ptr", "dvi_h", NULL }; + +char *node_fields_whatsit_pdf_literal[] = { "attr", "mode", "data", NULL }; +char *node_fields_whatsit_pdf_refobj[] = { "attr", "objnum", NULL }; +char *node_fields_whatsit_pdf_refxform[] = + { "attr", "width", "height", "depth", "objnum", NULL }; +char *node_fields_whatsit_pdf_refximage[] = + { "attr", "width", "height", "depth", "objnum", NULL }; +char *node_fields_whatsit_pdf_annot[] = + { "attr", "width", "height", "depth", "objnum", "data", NULL }; +char *node_fields_whatsit_pdf_start_link[] = + { "attr", "width", "height", "depth", + "objnum", "link_attr", "action", NULL +}; +char *node_fields_whatsit_pdf_end_link[] = { "attr", NULL }; +char *node_fields_whatsit_pdf_dest[] = { "attr", "width", "height", "depth", + "named_id", "dest_id", "dest_type", "xyz_zoom", "objnum", NULL +}; +char *node_fields_whatsit_pdf_thread[] = { "attr", "width", "height", "depth", + "named_id", "thread_id", "thread_attr", NULL +}; +char *node_fields_whatsit_pdf_start_thread[] = + { "attr", "width", "height", "depth", + "named_id", "thread_id", "thread_attr", NULL +}; +char *node_fields_whatsit_pdf_end_thread[] = { "attr", NULL }; +char *node_fields_whatsit_pdf_save_pos[] = { "attr", NULL }; +char *node_fields_whatsit_late_lua[] = { "attr", "reg", "data", NULL }; +char *node_fields_whatsit_close_lua[] = { "attr", "reg", NULL }; +char *node_fields_whatsit_pdf_colorstack[] = + { "attr", "stack", "cmd", "data", NULL }; +char *node_fields_whatsit_pdf_setmatrix[] = { "attr", "data", NULL }; +char *node_fields_whatsit_pdf_save[] = { "attr", NULL }; +char *node_fields_whatsit_pdf_restore[] = { "attr", NULL }; +char *node_fields_whatsit_cancel_boundary[] = { "attr", NULL }; +char *node_fields_whatsit_user_defined[] = + { "attr", "user_id", "type", "value", NULL }; node_info node_data[] = { - { hlist_node, box_node_size, node_fields_list, "hlist" }, - { vlist_node, box_node_size, node_fields_list, "vlist" }, - { rule_node, rule_node_size, node_fields_rule, "rule" }, - { ins_node, ins_node_size, node_fields_insert, "ins" }, - { mark_node, mark_node_size, node_fields_mark, "mark" }, - { adjust_node, adjust_node_size, node_fields_adjust, "adjust" }, - { fake_node, fake_node_size, NULL, fake_node_name }, /* don't touch this! */ - { disc_node, disc_node_size, node_fields_disc, "disc" }, - { whatsit_node, -1, NULL, "whatsit" }, - { math_node, math_node_size, node_fields_math, "math" }, - { glue_node, glue_node_size, node_fields_glue, "glue" }, - { kern_node, kern_node_size, node_fields_kern, "kern" }, - { penalty_node, penalty_node_size, node_fields_penalty, "penalty" }, - { unset_node, box_node_size, node_fields_unset, "unset" }, - { style_node, style_node_size, node_fields_style, "style" }, - { choice_node, style_node_size, node_fields_choice, "choice" }, - { ord_noad, noad_size, node_fields_ord, "ord" }, - { op_noad, noad_size, node_fields_op, "op" }, - { bin_noad, noad_size, node_fields_bin, "bin" }, - { rel_noad, noad_size, node_fields_rel, "rel" }, - { open_noad, noad_size, node_fields_open, "open" }, - { close_noad, noad_size, node_fields_close, "close" }, - { punct_noad, noad_size, node_fields_punct, "punct" }, - { inner_noad, noad_size, node_fields_inner, "inner" }, - { radical_noad, radical_noad_size, node_fields_radical, "radical" }, - { fraction_noad, fraction_noad_size, node_fields_fraction, "fraction" }, - { under_noad, noad_size, node_fields_under, "under" }, - { over_noad, noad_size, node_fields_over, "over" }, - { accent_noad, accent_noad_size, node_fields_accent, "accent" }, - { vcenter_noad, noad_size, node_fields_vcenter, "vcenter" }, - { left_noad, noad_size, node_fields_left, "left" }, - { right_noad, noad_size, node_fields_right, "right" }, - { margin_kern_node, margin_kern_node_size, node_fields_margin_kern, "margin_kern" }, - { glyph_node, glyph_node_size, node_fields_glyph, "glyph" }, /* 33 */ - { align_record_node, box_node_size, NULL, "align_record" }, - { pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file" }, - { pseudo_line_node, variable_node_size, NULL, "pseudo_line" }, - { inserting_node, page_ins_node_size, node_fields_inserting, "page_insert" }, - { split_up_node, page_ins_node_size, node_fields_splitup, "split_insert" }, - { expr_node, expr_node_size, NULL, "expr_stack" }, - { nesting_node, nesting_node_size, NULL, "nested_list" }, /* 40 */ - { span_node, span_node_size, NULL, "span" }, - { attribute_node, attribute_node_size, node_fields_attribute, "attribute" }, - { glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec" }, - { attribute_list_node, attribute_node_size, node_fields_attribute_list,"attribute_list" }, - { action_node, pdf_action_size, node_fields_action, "action" }, - { temp_node, temp_node_size, NULL, "temp" }, - { align_stack_node, align_stack_node_size, NULL, "align_stack" }, - { movement_node, movement_node_size, NULL, "movement_stack" }, - { if_node, if_node_size, NULL, "if_stack" }, - { unhyphenated_node, active_node_size, NULL, "unhyphenated" }, /* 50 */ - { hyphenated_node, active_node_size, NULL, "hyphenated" }, - { delta_node, delta_node_size, NULL, "delta" }, - { passive_node, passive_node_size, NULL, "passive" }, - { shape_node, variable_node_size, NULL, "shape" }, - { -1, -1, NULL, NULL }}; + {hlist_node, box_node_size, node_fields_list, "hlist"}, + {vlist_node, box_node_size, node_fields_list, "vlist"}, + {rule_node, rule_node_size, node_fields_rule, "rule"}, + {ins_node, ins_node_size, node_fields_insert, "ins"}, + {mark_node, mark_node_size, node_fields_mark, "mark"}, + {adjust_node, adjust_node_size, node_fields_adjust, "adjust"}, + {fake_node, fake_node_size, NULL, fake_node_name}, /* don't touch this! */ + {disc_node, disc_node_size, node_fields_disc, "disc"}, + {whatsit_node, -1, NULL, "whatsit"}, + {math_node, math_node_size, node_fields_math, "math"}, + {glue_node, glue_node_size, node_fields_glue, "glue"}, + {kern_node, kern_node_size, node_fields_kern, "kern"}, + {penalty_node, penalty_node_size, node_fields_penalty, "penalty"}, + {unset_node, box_node_size, node_fields_unset, "unset"}, + {style_node, style_node_size, node_fields_style, "style"}, + {choice_node, style_node_size, node_fields_choice, "choice"}, + {ord_noad, noad_size, node_fields_ord, "ord"}, + {op_noad, noad_size, node_fields_op, "op"}, + {bin_noad, noad_size, node_fields_bin, "bin"}, + {rel_noad, noad_size, node_fields_rel, "rel"}, + {open_noad, noad_size, node_fields_open, "open"}, + {close_noad, noad_size, node_fields_close, "close"}, + {punct_noad, noad_size, node_fields_punct, "punct"}, + {inner_noad, noad_size, node_fields_inner, "inner"}, + {radical_noad, radical_noad_size, node_fields_radical, "radical"}, + {fraction_noad, fraction_noad_size, node_fields_fraction, "fraction"}, + {under_noad, noad_size, node_fields_under, "under"}, + {over_noad, noad_size, node_fields_over, "over"}, + {accent_noad, accent_noad_size, node_fields_accent, "accent"}, + {vcenter_noad, noad_size, node_fields_vcenter, "vcenter"}, + {left_noad, noad_size, node_fields_left, "left"}, + {right_noad, noad_size, node_fields_right, "right"}, + {margin_kern_node, margin_kern_node_size, node_fields_margin_kern, + "margin_kern"}, + {glyph_node, glyph_node_size, node_fields_glyph, "glyph"}, /* 33 */ + {align_record_node, box_node_size, NULL, "align_record"}, + {pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file"}, + {pseudo_line_node, variable_node_size, NULL, "pseudo_line"}, + {inserting_node, page_ins_node_size, node_fields_inserting, "page_insert"}, + {split_up_node, page_ins_node_size, node_fields_splitup, "split_insert"}, + {expr_node, expr_node_size, NULL, "expr_stack"}, + {nesting_node, nesting_node_size, NULL, "nested_list"}, /* 40 */ + {span_node, span_node_size, NULL, "span"}, + {attribute_node, attribute_node_size, node_fields_attribute, "attribute"}, + {glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec"}, + {attribute_list_node, attribute_node_size, node_fields_attribute_list, + "attribute_list"}, + {action_node, pdf_action_size, node_fields_action, "action"}, + {temp_node, temp_node_size, NULL, "temp"}, + {align_stack_node, align_stack_node_size, NULL, "align_stack"}, + {movement_node, movement_node_size, NULL, "movement_stack"}, + {if_node, if_node_size, NULL, "if_stack"}, + {unhyphenated_node, active_node_size, NULL, "unhyphenated"}, /* 50 */ + {hyphenated_node, active_node_size, NULL, "hyphenated"}, + {delta_node, delta_node_size, NULL, "delta"}, + {passive_node, passive_node_size, NULL, "passive"}, + {shape_node, variable_node_size, NULL, "shape"}, + {-1, -1, NULL, NULL} +}; #define last_normal_node shape_node node_info whatsit_node_data[] = { - { open_node, open_node_size, node_fields_whatsit_open, "open" }, - { write_node, write_node_size, node_fields_whatsit_write, "write" }, - { close_node, close_node_size, node_fields_whatsit_close, "close" }, - { special_node, special_node_size, node_fields_whatsit_special, "special" }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { local_par_node, local_par_size, node_fields_whatsit_local_par, "local_par" }, - { dir_node, dir_node_size, node_fields_whatsit_dir, "dir" }, - { pdf_literal_node, write_node_size, node_fields_whatsit_pdf_literal, "pdf_literal" }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { pdf_refobj_node, pdf_refobj_node_size, node_fields_whatsit_pdf_refobj, "pdf_refobj" }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { pdf_refxform_node, pdf_refxform_node_size, node_fields_whatsit_pdf_refxform, "pdf_refxform" }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { pdf_refximage_node, pdf_refximage_node_size, node_fields_whatsit_pdf_refximage, "pdf_refximage" }, - { pdf_annot_node, pdf_annot_node_size, node_fields_whatsit_pdf_annot, "pdf_annot" }, - { pdf_start_link_node, pdf_annot_node_size, node_fields_whatsit_pdf_start_link, "pdf_start_link" }, - { pdf_end_link_node, pdf_end_link_node_size, node_fields_whatsit_pdf_end_link, "pdf_end_link" }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { pdf_dest_node, pdf_dest_node_size, node_fields_whatsit_pdf_dest, "pdf_dest" }, - { pdf_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_thread, "pdf_thread" }, - { pdf_start_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_start_thread,"pdf_start_thread"}, - { pdf_end_thread_node, pdf_end_thread_node_size, node_fields_whatsit_pdf_end_thread, "pdf_end_thread" }, - { pdf_save_pos_node, pdf_save_pos_node_size, node_fields_whatsit_pdf_save_pos, "pdf_save_pos" }, - { pdf_thread_data_node, pdf_thread_node_size, NULL, "pdf_thread_data" }, - { pdf_link_data_node, pdf_annot_node_size, NULL, "pdf_link_data" }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { late_lua_node, write_node_size, node_fields_whatsit_late_lua, "late_lua" }, - { close_lua_node, write_node_size, node_fields_whatsit_close_lua, "close_lua" }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { fake_node, fake_node_size, NULL, fake_node_name }, - { pdf_colorstack_node, pdf_colorstack_node_size, node_fields_whatsit_pdf_colorstack, "pdf_colorstack" }, - { pdf_setmatrix_node, pdf_setmatrix_node_size, node_fields_whatsit_pdf_setmatrix, "pdf_setmatrix" }, - { pdf_save_node, pdf_save_node_size, node_fields_whatsit_pdf_save, "pdf_save" }, - { pdf_restore_node, pdf_restore_node_size, node_fields_whatsit_pdf_restore, "pdf_restore" }, - { cancel_boundary_node, cancel_boundary_size, node_fields_whatsit_cancel_boundary, "cancel_boundary" }, - { user_defined_node, user_defined_node_size, node_fields_whatsit_user_defined, "user_defined" }, - { -1, -1, NULL, NULL }}; + {open_node, open_node_size, node_fields_whatsit_open, "open"}, + {write_node, write_node_size, node_fields_whatsit_write, "write"}, + {close_node, close_node_size, node_fields_whatsit_close, "close"}, + {special_node, special_node_size, node_fields_whatsit_special, "special"}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {local_par_node, local_par_size, node_fields_whatsit_local_par, + "local_par"}, + {dir_node, dir_node_size, node_fields_whatsit_dir, "dir"}, + {pdf_literal_node, write_node_size, node_fields_whatsit_pdf_literal, + "pdf_literal"}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {pdf_refobj_node, pdf_refobj_node_size, node_fields_whatsit_pdf_refobj, + "pdf_refobj"}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {pdf_refxform_node, pdf_refxform_node_size, + node_fields_whatsit_pdf_refxform, "pdf_refxform"}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {pdf_refximage_node, pdf_refximage_node_size, + node_fields_whatsit_pdf_refximage, "pdf_refximage"}, + {pdf_annot_node, pdf_annot_node_size, node_fields_whatsit_pdf_annot, + "pdf_annot"}, + {pdf_start_link_node, pdf_annot_node_size, + node_fields_whatsit_pdf_start_link, "pdf_start_link"}, + {pdf_end_link_node, pdf_end_link_node_size, + node_fields_whatsit_pdf_end_link, "pdf_end_link"}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {pdf_dest_node, pdf_dest_node_size, node_fields_whatsit_pdf_dest, + "pdf_dest"}, + {pdf_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_thread, + "pdf_thread"}, + {pdf_start_thread_node, pdf_thread_node_size, + node_fields_whatsit_pdf_start_thread, "pdf_start_thread"}, + {pdf_end_thread_node, pdf_end_thread_node_size, + node_fields_whatsit_pdf_end_thread, "pdf_end_thread"}, + {pdf_save_pos_node, pdf_save_pos_node_size, + node_fields_whatsit_pdf_save_pos, "pdf_save_pos"}, + {pdf_thread_data_node, pdf_thread_node_size, NULL, "pdf_thread_data"}, + {pdf_link_data_node, pdf_annot_node_size, NULL, "pdf_link_data"}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {late_lua_node, write_node_size, node_fields_whatsit_late_lua, "late_lua"}, + {close_lua_node, write_node_size, node_fields_whatsit_close_lua, + "close_lua"}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {fake_node, fake_node_size, NULL, fake_node_name}, + {pdf_colorstack_node, pdf_colorstack_node_size, + node_fields_whatsit_pdf_colorstack, "pdf_colorstack"}, + {pdf_setmatrix_node, pdf_setmatrix_node_size, + node_fields_whatsit_pdf_setmatrix, "pdf_setmatrix"}, + {pdf_save_node, pdf_save_node_size, node_fields_whatsit_pdf_save, + "pdf_save"}, + {pdf_restore_node, pdf_restore_node_size, node_fields_whatsit_pdf_restore, + "pdf_restore"}, + {cancel_boundary_node, cancel_boundary_size, + node_fields_whatsit_cancel_boundary, "cancel_boundary"}, + {user_defined_node, user_defined_node_size, + node_fields_whatsit_user_defined, "user_defined"}, + {-1, -1, NULL, NULL} +}; #define last_whatsit_node user_defined_node #define get_node_size(i,j) (i!=whatsit_node ? node_data[i].size : whatsit_node_data[j].size) #define get_node_name(i,j) (i!=whatsit_node ? node_data[i].name : whatsit_node_data[j].name) -halfword -new_node(int i, int j) { - register int s; - register halfword n; - s = get_node_size(i,j); - n = get_node(s); - /* it should be possible to do this memset at free_node() */ - /* type() and subtype() will be set below, and vlink() is - set to null by get_node(), so we can do we clearing one - word less than |s| */ - (void)memset((void *)(varmem+n+1),0, (sizeof(memory_word)*(s-1))); - switch (i) { - case glyph_node: - init_lang_data(n); - break; - case glue_node: - case kern_node: - case glue_spec_node: - break; - case hlist_node: - case vlist_node: - box_dir(n)=-1; - break; - case whatsit_node: - if (j==open_node) { - open_name(n) = get_nullstr(); - open_area(n) = open_name(n); - open_ext(n) = open_name(n); +halfword new_node(int i, int j) +{ + register int s; + register halfword n; + s = get_node_size(i, j); + n = get_node(s); + /* it should be possible to do this memset at free_node() */ + /* type() and subtype() will be set below, and vlink() is + set to null by get_node(), so we can do we clearing one + word less than |s| */ + (void) memset((void *) (varmem + n + 1), 0, + (sizeof(memory_word) * (s - 1))); + switch (i) { + case glyph_node: + init_lang_data(n); + break; + case glue_node: + case kern_node: + case glue_spec_node: + break; + case hlist_node: + case vlist_node: + box_dir(n) = -1; + break; + case whatsit_node: + if (j == open_node) { + open_name(n) = get_nullstr(); + open_area(n) = open_name(n); + open_ext(n) = open_name(n); + } + break; + case disc_node: + pre_break(n) = pre_break_head(n); + type(pre_break(n)) = nesting_node; + subtype(pre_break(n)) = pre_break_head(0); + post_break(n) = post_break_head(n); + type(post_break(n)) = nesting_node; + subtype(post_break(n)) = post_break_head(0); + no_break(n) = no_break_head(n); + type(no_break(n)) = nesting_node; + subtype(no_break(n)) = no_break_head(0); + break; + case rule_node: + depth(n) = null_flag; + height(n) = null_flag; + rule_dir(n) = -1; + /* fall through */ + case unset_node: + width(n) = null_flag; + break; + case pseudo_line_node: + case shape_node: + /* this is a trick that makes pseudo_files slightly slower, + * but the overall allocation faster then an explicit test + * at the top of new_node(). + */ + free_node(n, variable_node_size); + n = slow_get_node(j); + (void) memset((void *) (varmem + n + 1), 0, + (sizeof(memory_word) * (j - 1))); + break; + default: + break; + } + if (nodetype_has_attributes(i)) { + build_attribute_list(n); } - break; - case disc_node: - pre_break(n)=pre_break_head(n); - type(pre_break(n))= nesting_node; - subtype(pre_break(n))=pre_break_head(0); - post_break(n)=post_break_head(n); - type(post_break(n))= nesting_node; - subtype(post_break(n))=post_break_head(0); - no_break(n)=no_break_head(n); - type(no_break(n))= nesting_node; - subtype(no_break(n))=no_break_head(0); - break; - case rule_node: - depth(n)=null_flag; - height(n)=null_flag; - rule_dir(n)=-1; - /* fall through */ - case unset_node: - width(n) = null_flag; - break; - case pseudo_line_node: - case shape_node: - /* this is a trick that makes pseudo_files slightly slower, - * but the overall allocation faster then an explicit test - * at the top of new_node(). - */ - free_node(n,variable_node_size); - n = slow_get_node(j); - (void)memset((void *)(varmem+n+1),0, (sizeof(memory_word)*(j-1))); - break; - default: - break; - } - if (nodetype_has_attributes(i)) { - build_attribute_list(n); - } - type(n)=i; - subtype(n)=j; - return n; + type(n) = i; + subtype(n) = j; + return n; } -halfword -raw_glyph_node(void) { - register halfword n; - n = get_node(glyph_node_size); - (void)memset((void *)(varmem+n+1),0, (sizeof(memory_word)*(glyph_node_size-1))); - type(n)=glyph_node; - subtype(n)=0; - return n; +halfword raw_glyph_node(void) +{ + register halfword n; + n = get_node(glyph_node_size); + (void) memset((void *) (varmem + n + 1), 0, + (sizeof(memory_word) * (glyph_node_size - 1))); + type(n) = glyph_node; + subtype(n) = 0; + return n; } -halfword -new_glyph_node(void) { - register halfword n; - n = get_node(glyph_node_size); - (void)memset((void *)(varmem+n+1),0, (sizeof(memory_word)*(glyph_node_size-1))); - type(n)=glyph_node; - subtype(n)=0; - build_attribute_list(n); - return n; +halfword new_glyph_node(void) +{ + register halfword n; + n = get_node(glyph_node_size); + (void) memset((void *) (varmem + n + 1), 0, + (sizeof(memory_word) * (glyph_node_size - 1))); + type(n) = glyph_node; + subtype(n) = 0; + build_attribute_list(n); + return n; } /* makes a duplicate of the node list that starts at |p| and returns a pointer to the new list */ -halfword -copy_node_list(halfword p) { - halfword q; /* previous position in new list */ - halfword h = null; /* head of the list */ - copy_error_seen = 0; - while (p!=null) { - register halfword s = copy_node(p); - if (h==null) { - h = s; - } else { - couple_nodes(q,s); +halfword copy_node_list(halfword p) +{ + halfword q; /* previous position in new list */ + halfword h = null; /* head of the list */ + copy_error_seen = 0; + while (p != null) { + register halfword s = copy_node(p); + if (h == null) { + h = s; + } else { + couple_nodes(q, s); + } + q = s; + p = vlink(p); } - q=s; - p=vlink(p); - } - return h; + return h; } /* make a dupe of a single node */ -halfword -copy_node(const halfword p) { - halfword r; /* current node being fabricated for new list */ - register halfword s; /* a helper variable for copying into variable mem */ - register int i; - if (copy_error(p)) { - r = new_node(temp_node,0); - return r; - } - i = get_node_size(type(p), subtype(p)); - r = get_node(i); - (void)memcpy((void *)(varmem+r),(void *)(varmem+p),(sizeof(memory_word)*i)); - - if (nodetype_has_attributes(type(p))) { - add_node_attr_ref(node_attr(p)); - alink(r)=null; /* needs checking */ - } - vlink(r)=null; - switch (type(p)) { - case glyph_node: - s=copy_node_list(lig_ptr(p)); - lig_ptr(r)=s; - break; - case glue_node: - add_glue_ref(glue_ptr(p)); - s=copy_node_list(leader_ptr(p)); - leader_ptr(r)=s; - break; - case hlist_node: - case vlist_node: - case unset_node: - s=copy_node_list(list_ptr(p)); - list_ptr(r)=s; - break; - case ins_node: - add_glue_ref(split_top_ptr(p)); - s=copy_node_list(ins_ptr(p)); - ins_ptr(r)=s; - break; - case margin_kern_node: - s=copy_node(margin_char(p)); - margin_char(r)=s; - break; - case disc_node: - pre_break(r)=pre_break_head(r); - if (vlink_pre_break(p)!=null) { - s=copy_node_list(vlink_pre_break(p)); - alink(s)=pre_break(r); - tlink_pre_break(r)=tail_of_list(s); - vlink_pre_break(r)=s; - } else { - assert(tlink(pre_break(r))==null); - } - post_break(r)=post_break_head(r); - if (vlink_post_break(p)!=null) { - s=copy_node_list(vlink_post_break(p)); - alink(s)=post_break(r); - tlink_post_break(r)=tail_of_list(s); - vlink_post_break(r)=s; - } else { - assert(tlink_post_break(r)==null); +halfword copy_node(const halfword p) +{ + halfword r; /* current node being fabricated for new list */ + register halfword s; /* a helper variable for copying into variable mem */ + register int i; + if (copy_error(p)) { + r = new_node(temp_node, 0); + return r; } - no_break(r)=no_break_head(r); - if (vlink(no_break(p))!=null) { - s=copy_node_list(vlink_no_break(p)); - alink(s)=no_break(r); - tlink_no_break(r)=tail_of_list(s); - vlink_no_break(r)=s; - } else { - assert(tlink_no_break(r)==null); + i = get_node_size(type(p), subtype(p)); + r = get_node(i); + (void) memcpy((void *) (varmem + r), (void *) (varmem + p), + (sizeof(memory_word) * i)); + + if (nodetype_has_attributes(type(p))) { + add_node_attr_ref(node_attr(p)); + alink(r) = null; /* needs checking */ } - break; - case mark_node: - add_token_ref(mark_ptr(p)); - break; - case adjust_node: - s=copy_node_list(adjust_ptr(p)); - adjust_ptr(r)=s; - break; - case glue_spec_node: - glue_ref_count(r)=null; - break; - case whatsit_node: - switch (subtype(p)) { - case dir_node: - case local_par_node: - break; - case write_node: - case special_node: - add_token_ref(write_tokens(p)); - break; - case pdf_literal_node: - add_token_ref(pdf_literal_data(p)); - break; - case pdf_colorstack_node: - if (pdf_colorstack_cmd(p) <= colorstack_data) - add_token_ref(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - add_token_ref(pdf_setmatrix_data(p)); - break; - case late_lua_node: - add_token_ref(late_lua_data(p)); - break; - case pdf_annot_node: - add_token_ref(pdf_annot_data(p)); - break; - case pdf_start_link_node: - if (pdf_link_attr(r) != null) - add_token_ref(pdf_link_attr(r)); - add_action_ref(pdf_link_action(r)); - break; - case pdf_dest_node: - if (pdf_dest_named_id(p) > 0) - add_token_ref(pdf_dest_id(p)); - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (pdf_thread_named_id(p) > 0) - add_token_ref(pdf_thread_id(p)); - if (pdf_thread_attr(p) != null) - add_token_ref(pdf_thread_attr(p)); - break; - case user_defined_node: - switch (user_node_type(p)) { - case 'a': add_node_attr_ref(user_node_value(p)); + vlink(r) = null; + switch (type(p)) { + case glyph_node: + s = copy_node_list(lig_ptr(p)); + lig_ptr(r) = s; + break; + case glue_node: + add_glue_ref(glue_ptr(p)); + s = copy_node_list(leader_ptr(p)); + leader_ptr(r) = s; break; - case 't': add_token_ref(user_node_value(p)); + case hlist_node: + case vlist_node: + case unset_node: + s = copy_node_list(list_ptr(p)); + list_ptr(r) = s; + break; + case ins_node: + add_glue_ref(split_top_ptr(p)); + s = copy_node_list(ins_ptr(p)); + ins_ptr(r) = s; + break; + case margin_kern_node: + s = copy_node(margin_char(p)); + margin_char(r) = s; + break; + case disc_node: + pre_break(r) = pre_break_head(r); + if (vlink_pre_break(p) != null) { + s = copy_node_list(vlink_pre_break(p)); + alink(s) = pre_break(r); + tlink_pre_break(r) = tail_of_list(s); + vlink_pre_break(r) = s; + } else { + assert(tlink(pre_break(r)) == null); + } + post_break(r) = post_break_head(r); + if (vlink_post_break(p) != null) { + s = copy_node_list(vlink_post_break(p)); + alink(s) = post_break(r); + tlink_post_break(r) = tail_of_list(s); + vlink_post_break(r) = s; + } else { + assert(tlink_post_break(r) == null); + } + no_break(r) = no_break_head(r); + if (vlink(no_break(p)) != null) { + s = copy_node_list(vlink_no_break(p)); + alink(s) = no_break(r); + tlink_no_break(r) = tail_of_list(s); + vlink_no_break(r) = s; + } else { + assert(tlink_no_break(r) == null); + } break; - case 'n': - s=copy_node_list(user_node_value(p)); - user_node_value(r)=s; + case mark_node: + add_token_ref(mark_ptr(p)); + break; + case adjust_node: + s = copy_node_list(adjust_ptr(p)); + adjust_ptr(r) = s; + break; + case glue_spec_node: + glue_ref_count(r) = null; + break; + case whatsit_node: + switch (subtype(p)) { + case dir_node: + case local_par_node: + break; + case write_node: + case special_node: + add_token_ref(write_tokens(p)); + break; + case pdf_literal_node: + add_token_ref(pdf_literal_data(p)); + break; + case pdf_colorstack_node: + if (pdf_colorstack_cmd(p) <= colorstack_data) + add_token_ref(pdf_colorstack_data(p)); + break; + case pdf_setmatrix_node: + add_token_ref(pdf_setmatrix_data(p)); + break; + case late_lua_node: + add_token_ref(late_lua_data(p)); + break; + case pdf_annot_node: + add_token_ref(pdf_annot_data(p)); + break; + case pdf_start_link_node: + if (pdf_link_attr(r) != null) + add_token_ref(pdf_link_attr(r)); + add_action_ref(pdf_link_action(r)); + break; + case pdf_dest_node: + if (pdf_dest_named_id(p) > 0) + add_token_ref(pdf_dest_id(p)); + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (pdf_thread_named_id(p) > 0) + add_token_ref(pdf_thread_id(p)); + if (pdf_thread_attr(p) != null) + add_token_ref(pdf_thread_attr(p)); + break; + case user_defined_node: + switch (user_node_type(p)) { + case 'a': + add_node_attr_ref(user_node_value(p)); + break; + case 't': + add_token_ref(user_node_value(p)); + break; + case 'n': + s = copy_node_list(user_node_value(p)); + user_node_value(r) = s; + break; + } + break; + default: + break; + } break; - } - break; - default: - break; } - break; - } - return r; + return r; } -int valid_node (halfword p) { - if (p>prealloc) { - if (p<var_mem_max) { - if (varmem_sizes[p]>0) - return 1; +int valid_node(halfword p) +{ + if (p > prealloc) { + if (p < var_mem_max) { + if (varmem_sizes[p] > 0) + return 1; + } + } else { + return 1; } - } else { - return 1; - } - return 0; + return 0; } -static void -do_free_error (halfword p) { - char errstr[255]= {0}; - char *errhlp[] = {"When I tried to free the node mentioned in the error message, it turned", - "out it was not (or no longer) actually in use.", - "Errors such as these are often caused by Lua node list alteration,", - "but could also point to a bug in the executable. It should be safe to continue.", - NULL}; - - check_node_mem(); - if (free_error_seen) - return; - - halfword r = null; - free_error_seen = 1; - if (type(p)==glyph_node) { - snprintf(errstr,255,"Attempt to double-free glyph (%c) node %d, ignored", (int)character (p), (int)p); - } else { - snprintf(errstr,255,"Attempt to double-free %s node %d, ignored", get_node_name(type(p),subtype(p)), (int)p); - } - tex_error(errstr,errhlp); - for (r=prealloc+1;r<var_mem_max;r++) { - if (vlink(r)==p) { - halfword s = r; - while (s>prealloc && varmem_sizes[s]==0) - s--; - if (s!=null - && s!=prealloc - && s!=var_mem_max - && (r-s)<get_node_size(type(s),subtype(s) ) - && alink(s)!=p ) { - - if (type(s)==disc_node) { - fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", - get_node_name(type(s),subtype(s)), (int)s, (int)vlink(s), (int)alink(s)); - fprintf(stdout,"pre_break(%d,%d,%d), ", (int)vlink_pre_break(s), (int)tlink(pre_break(s)), (int)alink(pre_break(s))); - fprintf(stdout,"post_break(%d,%d,%d), ", (int)vlink_post_break(s), (int)tlink(post_break(s)), (int)alink(post_break(s))); - fprintf(stdout,"no_break(%d,%d,%d)", (int)vlink_no_break(s), (int)tlink(no_break(s)), (int)alink(no_break(s))); - fprintf(stdout,"\n"); - } else { - if (vlink(s)==p - || (type(s)==glyph_node && lig_ptr(s)==p) - || (type(s)==vlist_node && list_ptr(s)==p) - || (type(s)==hlist_node && list_ptr(s)==p) - || (type(s)==unset_node && list_ptr(s)==p) - || (type(s)==ins_node && ins_ptr(s)==p) - ) { - fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", - get_node_name(type(s),subtype(s)), (int)s, (int)vlink(s), (int)alink(s)); - if (type(s)==glyph_node) { - fprintf(stdout,"lig_ptr(%d)", (int)lig_ptr(s)); - } else if (type(s)==vlist_node || type(s)==hlist_node) { - fprintf(stdout,"list_ptr(%d)", (int)list_ptr(s)); - } - fprintf(stdout,"\n"); - } else { - if ((type(s)!=penalty_node) - && (type(s)!=math_node) - && (type(s)!=kern_node) - ) { - fprintf(stdout," pointed to from %s node %d\n",get_node_name(type(s),subtype(s)), (int)s); +static void do_free_error(halfword p) +{ + char errstr[255] = { 0 }; + char *errhlp[] = + { +"When I tried to free the node mentioned in the error message, it turned", + "out it was not (or no longer) actually in use.", + "Errors such as these are often caused by Lua node list alteration,", + "but could also point to a bug in the executable. It should be safe to continue.", + NULL + }; + + check_node_mem(); + if (free_error_seen) + return; + + halfword r = null; + free_error_seen = 1; + if (type(p) == glyph_node) { + snprintf(errstr, 255, + "Attempt to double-free glyph (%c) node %d, ignored", + (int) character(p), (int) p); + } else { + snprintf(errstr, 255, "Attempt to double-free %s node %d, ignored", + get_node_name(type(p), subtype(p)), (int) p); + } + tex_error(errstr, errhlp); + for (r = prealloc + 1; r < var_mem_max; r++) { + if (vlink(r) == p) { + halfword s = r; + while (s > prealloc && varmem_sizes[s] == 0) + s--; + if (s != null + && s != prealloc + && s != var_mem_max + && (r - s) < get_node_size(type(s), subtype(s)) + && alink(s) != p) { + + if (type(s) == disc_node) { + fprintf(stdout, + " pointed to from %s node %d (vlink %d, alink %d): ", + get_node_name(type(s), subtype(s)), (int) s, + (int) vlink(s), (int) alink(s)); + fprintf(stdout, "pre_break(%d,%d,%d), ", + (int) vlink_pre_break(s), (int) tlink(pre_break(s)), + (int) alink(pre_break(s))); + fprintf(stdout, "post_break(%d,%d,%d), ", + (int) vlink_post_break(s), + (int) tlink(post_break(s)), + (int) alink(post_break(s))); + fprintf(stdout, "no_break(%d,%d,%d)", + (int) vlink_no_break(s), (int) tlink(no_break(s)), + (int) alink(no_break(s))); + fprintf(stdout, "\n"); + } else { + if (vlink(s) == p + || (type(s) == glyph_node && lig_ptr(s) == p) + || (type(s) == vlist_node && list_ptr(s) == p) + || (type(s) == hlist_node && list_ptr(s) == p) + || (type(s) == unset_node && list_ptr(s) == p) + || (type(s) == ins_node && ins_ptr(s) == p) + ) { + fprintf(stdout, + " pointed to from %s node %d (vlink %d, alink %d): ", + get_node_name(type(s), subtype(s)), (int) s, + (int) vlink(s), (int) alink(s)); + if (type(s) == glyph_node) { + fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s)); + } else if (type(s) == vlist_node + || type(s) == hlist_node) { + fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s)); + } + fprintf(stdout, "\n"); + } else { + if ((type(s) != penalty_node) + && (type(s) != math_node) + && (type(s) != kern_node) + ) { + fprintf(stdout, " pointed to from %s node %d\n", + get_node_name(type(s), subtype(s)), + (int) s); + } + } + } } - } } - } } - } } -int -free_error (halfword p) { - assert(p>prealloc); - assert(p<var_mem_max); - if (varmem_sizes[p]==0) { - do_free_error(p); - return 1; /* double free */ - } - return 0; +int free_error(halfword p) +{ + assert(p > prealloc); + assert(p < var_mem_max); + if (varmem_sizes[p] == 0) { + do_free_error(p); + return 1; /* double free */ + } + return 0; } -static void -do_copy_error (halfword p) { - char errstr[255]= {0}; - char *errhlp[] = {"When I tried to copy the node mentioned in the error message, it turned", - "out it was not (or no longer) actually in use.", - "Errors such as these are often caused by Lua node list alteration,", - "but could also point to a bug in the executable. It should be safe to continue.", - NULL}; +static void do_copy_error(halfword p) +{ + char errstr[255] = { 0 }; + char *errhlp[] = + { +"When I tried to copy the node mentioned in the error message, it turned", + "out it was not (or no longer) actually in use.", + "Errors such as these are often caused by Lua node list alteration,", + "but could also point to a bug in the executable. It should be safe to continue.", + NULL + }; + + if (copy_error_seen) + return; + + copy_error_seen = 1; + if (type(p) == glyph_node) { + snprintf(errstr, 255, + "Attempt to copy free glyph (%c) node %d, ignored", + (int) character(p), (int) p); + } else { + snprintf(errstr, 255, "Attempt to copy free %s node %d, ignored", + get_node_name(type(p), subtype(p)), (int) p); + } + tex_error(errstr, errhlp); +} - if (copy_error_seen) - return; - copy_error_seen = 1; - if (type(p)==glyph_node) { - snprintf(errstr,255,"Attempt to copy free glyph (%c) node %d, ignored", (int)character (p), (int)p); - } else { - snprintf(errstr,255,"Attempt to copy free %s node %d, ignored", get_node_name(type(p),subtype(p)), (int)p); - } - tex_error(errstr,errhlp); +int copy_error(halfword p) +{ + assert(p >= 0); + assert(p < var_mem_max); + if (p > prealloc && varmem_sizes[p] == 0) { + do_copy_error(p); + return 1; /* copy free node */ + } + return 0; } -int -copy_error (halfword p) { - assert(p>=0); - assert(p<var_mem_max); - if (p>prealloc && varmem_sizes[p]==0) { - do_copy_error(p); - return 1; /* copy free node */ - } - return 0; -} +void flush_node(halfword p) +{ + if (p == null) /* legal, but no-op */ + return; -void -flush_node (halfword p) { - - if (p==null) /* legal, but no-op */ - return; + if (free_error(p)) + return; - if (free_error(p)) - return; - - switch(type(p)) { - case glyph_node: - flush_node_list(lig_ptr(p)); - break; - case glue_node: - delete_glue_ref(glue_ptr(p)); - flush_node_list(leader_ptr(p)); - break; - - case attribute_node: - case attribute_list_node: - case temp_node: - case glue_spec_node: - case rule_node: - case kern_node: - case math_node: - case penalty_node: - break; - - case hlist_node: - case vlist_node: - case unset_node: - flush_node_list(list_ptr(p)); - break; - case whatsit_node: - switch (subtype(p)) { - - case dir_node: - break; - case open_node: - case write_node: - case close_node: - case pdf_save_node: - case pdf_restore_node: - case cancel_boundary_node: - case close_lua_node: - case pdf_refobj_node: - case pdf_refxform_node: - case pdf_refximage_node: - case pdf_end_link_node: - case pdf_end_thread_node: - case pdf_save_pos_node: - case local_par_node: - break; - - case special_node: - delete_token_ref(write_tokens(p)); - break; - case pdf_literal_node: - delete_token_ref(pdf_literal_data(p)); - break; - case pdf_colorstack_node: - if (pdf_colorstack_cmd(p) <= colorstack_data) - delete_token_ref(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - delete_token_ref(pdf_setmatrix_data(p)); - break; - case late_lua_node: - delete_token_ref(late_lua_data(p)); - break; - case pdf_annot_node: - delete_token_ref(pdf_annot_data(p)); - break; - - case pdf_link_data_node: - break; - - case pdf_start_link_node: - if (pdf_link_attr(p) != null) - delete_token_ref(pdf_link_attr(p)); - delete_action_ref(pdf_link_action(p)); - break; - case pdf_dest_node: - if (pdf_dest_named_id(p) > 0) - delete_token_ref(pdf_dest_id(p)); - break; - - case pdf_thread_data_node: - break; - - case pdf_thread_node: - case pdf_start_thread_node: - if (pdf_thread_named_id(p) > 0 ) delete_token_ref(pdf_thread_id(p)); - if (pdf_thread_attr(p) != null) delete_token_ref(pdf_thread_attr(p)); - break; - case user_defined_node: - switch( user_node_type(p)) { - case 'a': delete_attribute_ref(user_node_value(p)); break; - case 't': delete_token_ref(user_node_value(p)); break; - case 'n': flush_node_list(user_node_value(p)); break; - default: tconfusion("extuser"); break; - } - break; + switch (type(p)) { + case glyph_node: + flush_node_list(lig_ptr(p)); + break; + case glue_node: + delete_glue_ref(glue_ptr(p)); + flush_node_list(leader_ptr(p)); + break; + + case attribute_node: + case attribute_list_node: + case temp_node: + case glue_spec_node: + case rule_node: + case kern_node: + case math_node: + case penalty_node: + break; + + case hlist_node: + case vlist_node: + case unset_node: + flush_node_list(list_ptr(p)); + break; + case whatsit_node: + switch (subtype(p)) { + + case dir_node: + break; + case open_node: + case write_node: + case close_node: + case pdf_save_node: + case pdf_restore_node: + case cancel_boundary_node: + case close_lua_node: + case pdf_refobj_node: + case pdf_refxform_node: + case pdf_refximage_node: + case pdf_end_link_node: + case pdf_end_thread_node: + case pdf_save_pos_node: + case local_par_node: + break; + + case special_node: + delete_token_ref(write_tokens(p)); + break; + case pdf_literal_node: + delete_token_ref(pdf_literal_data(p)); + break; + case pdf_colorstack_node: + if (pdf_colorstack_cmd(p) <= colorstack_data) + delete_token_ref(pdf_colorstack_data(p)); + break; + case pdf_setmatrix_node: + delete_token_ref(pdf_setmatrix_data(p)); + break; + case late_lua_node: + delete_token_ref(late_lua_data(p)); + break; + case pdf_annot_node: + delete_token_ref(pdf_annot_data(p)); + break; + + case pdf_link_data_node: + break; + + case pdf_start_link_node: + if (pdf_link_attr(p) != null) + delete_token_ref(pdf_link_attr(p)); + delete_action_ref(pdf_link_action(p)); + break; + case pdf_dest_node: + if (pdf_dest_named_id(p) > 0) + delete_token_ref(pdf_dest_id(p)); + break; + + case pdf_thread_data_node: + break; + + case pdf_thread_node: + case pdf_start_thread_node: + if (pdf_thread_named_id(p) > 0) + delete_token_ref(pdf_thread_id(p)); + if (pdf_thread_attr(p) != null) + delete_token_ref(pdf_thread_attr(p)); + break; + case user_defined_node: + switch (user_node_type(p)) { + case 'a': + delete_attribute_ref(user_node_value(p)); + break; + case 't': + delete_token_ref(user_node_value(p)); + break; + case 'n': + flush_node_list(user_node_value(p)); + break; + default: + tconfusion("extuser"); + break; + } + break; + + default: + tconfusion("ext3"); + return; + + } + break; + case ins_node: + flush_node_list(ins_ptr(p)); + delete_glue_ref(split_top_ptr(p)); + break; + case margin_kern_node: + flush_node(margin_char(p)); + break; + case mark_node: + delete_token_ref(mark_ptr(p)); + break; + case disc_node: + flush_node_list(vlink(pre_break(p))); + flush_node_list(vlink(post_break(p))); + flush_node_list(vlink(no_break(p))); + break; + case adjust_node: + flush_node_list(adjust_ptr(p)); + break; + case style_node: + break; + case choice_node: + flush_node_list(display_mlist(p)); + flush_node_list(text_mlist(p)); + flush_node_list(script_mlist(p)); + flush_node_list(script_script_mlist(p)); + break; + case ord_noad: + case op_noad: + case bin_noad: + case rel_noad: + case open_noad: + case close_noad: + case punct_noad: + case inner_noad: + case radical_noad: + case over_noad: + case under_noad: + case vcenter_noad: + case accent_noad: + + /* + * if (math_type(nucleus(p))>=sub_box) + * flush_node_list(vinfo(nucleus(p))); + * if (math_type(supscr(p))>=sub_box) + * flush_node_list(vinfo(supscr(p))); + * if (math_type(subscr(p))>=sub_box) + * flush_node_list(vinfo(subscr(p))); + */ + break; + case left_noad: + case right_noad: + break; + case fraction_noad: + flush_node_list(vinfo(numerator(p))); + flush_node_list(vinfo(denominator(p))); + break; + case pseudo_file_node: + flush_node_list(pseudo_lines(p)); + break; + case pseudo_line_node: + case shape_node: + free_node(p, subtype(p)); + return; + break; + case align_stack_node: + case span_node: + case movement_node: + case if_node: + case nesting_node: + case unhyphenated_node: + case hyphenated_node: + case delta_node: + case passive_node: + case action_node: + case inserting_node: + case split_up_node: + case expr_node: + break; default: - tconfusion("ext3"); - return; + fprintf(stdout, "flush_node: type is %d\n", type(p)); + return; } - break; - case ins_node: - flush_node_list(ins_ptr(p)); - delete_glue_ref(split_top_ptr(p)); - break; - case margin_kern_node: - flush_node(margin_char(p)); - break; - case mark_node: - delete_token_ref(mark_ptr(p)); - break; - case disc_node: - flush_node_list(vlink(pre_break(p))); - flush_node_list(vlink(post_break(p))); - flush_node_list(vlink(no_break(p))); - break; - case adjust_node: - flush_node_list(adjust_ptr(p)); - break; - case style_node: - break; - case choice_node: - flush_node_list(display_mlist(p)); - flush_node_list(text_mlist(p)); - flush_node_list(script_mlist(p)); - flush_node_list(script_script_mlist(p)); - break; - case ord_noad: - case op_noad: - case bin_noad: - case rel_noad: - case open_noad: - case close_noad: - case punct_noad: - case inner_noad: - case radical_noad: - case over_noad: - case under_noad: - case vcenter_noad: - case accent_noad: - - /* - * if (math_type(nucleus(p))>=sub_box) - * flush_node_list(vinfo(nucleus(p))); - * if (math_type(supscr(p))>=sub_box) - * flush_node_list(vinfo(supscr(p))); - * if (math_type(subscr(p))>=sub_box) - * flush_node_list(vinfo(subscr(p))); - */ - - break; - case left_noad: - case right_noad: - break; - case fraction_noad: - flush_node_list(vinfo(numerator(p))); - flush_node_list(vinfo(denominator(p))); - break; - case pseudo_file_node: - flush_node_list(pseudo_lines(p)); - break; - case pseudo_line_node: - case shape_node: - free_node(p,subtype(p)); - return; - break; - case align_stack_node: - case span_node: - case movement_node: - case if_node: - case nesting_node: - case unhyphenated_node: - case hyphenated_node: - case delta_node: - case passive_node: - case action_node: - case inserting_node: - case split_up_node: - case expr_node: - break; - default: - fprintf(stdout,"flush_node: type is %d\n", type(p)); + if (nodetype_has_attributes(type(p))) + delete_attribute_ref(node_attr(p)); + free_node(p, get_node_size(type(p), subtype(p))); return; - - } - if (nodetype_has_attributes(type(p))) - delete_attribute_ref(node_attr(p)); - free_node(p,get_node_size(type(p),subtype(p))); - return; } -void -flush_node_list(halfword pp) { /* erase list of nodes starting at |p| */ - register halfword p=pp; - free_error_seen = 0; - if (p==null) /* legal, but no-op */ - return; - if (free_error(p)) - return; - - while (p!=null) { - register halfword q = vlink(p); - flush_node(p); - p=q; - } +void flush_node_list(halfword pp) +{ /* erase list of nodes starting at |p| */ + register halfword p = pp; + free_error_seen = 0; + if (p == null) /* legal, but no-op */ + return; + if (free_error(p)) + return; + + while (p != null) { + register halfword q = vlink(p); + flush_node(p); + p = q; + } } static int test_count = 1; @@ -854,841 +947,880 @@ static int test_count = 1; #define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); } #define check_token_ref(a) assert(1) -void -check_node (halfword p) { - - switch(type(p)) { - case glyph_node: - dorangetest(p,lig_ptr(p),var_mem_max); - break; - case glue_node: - check_glue_ref(glue_ptr(p)); - dorangetest(p,leader_ptr(p),var_mem_max); - break; - case hlist_node: - case vlist_node: - case unset_node: - case align_record_node: - dorangetest(p,list_ptr(p),var_mem_max); - break; - case ins_node: - dorangetest(p,ins_ptr(p),var_mem_max); - check_glue_ref(split_top_ptr(p)); - break; - case whatsit_node: - switch (subtype(p)) { - case special_node: - check_token_ref(write_tokens(p)); - break; - case pdf_literal_node: - check_token_ref(pdf_literal_data(p)); - break; - case pdf_colorstack_node: - if (pdf_colorstack_cmd(p) <= colorstack_data) - check_token_ref(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - check_token_ref(pdf_setmatrix_data(p)); - break; - case late_lua_node: - check_token_ref(late_lua_data(p)); - break; - case pdf_annot_node: - check_token_ref(pdf_annot_data(p)); - break; - case pdf_start_link_node: - if (pdf_link_attr(p) != null) - check_token_ref(pdf_link_attr(p)); - check_action_ref(pdf_link_action(p)); - break; - case pdf_dest_node: - if (pdf_dest_named_id(p) > 0) - check_token_ref(pdf_dest_id(p)); - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (pdf_thread_named_id(p) > 0 ) check_token_ref(pdf_thread_id(p)); - if (pdf_thread_attr(p) != null) check_token_ref(pdf_thread_attr(p)); - break; - case user_defined_node: - switch( user_node_type(p)) { - case 'a': check_attribute_ref(user_node_value(p)); break; - case 't': check_token_ref(user_node_value(p)); break; - case 'n': dorangetest(p,user_node_value(p),var_mem_max); break; - default: tconfusion("extuser"); break; - } - break; - case dir_node: - case open_node: - case write_node: - case close_node: - case pdf_save_node: - case pdf_restore_node: - case cancel_boundary_node: - case close_lua_node: - case pdf_refobj_node: - case pdf_refxform_node: - case pdf_refximage_node: - case pdf_end_link_node: - case pdf_end_thread_node: - case pdf_save_pos_node: - case local_par_node: - break; +void check_node(halfword p) +{ + + switch (type(p)) { + case glyph_node: + dorangetest(p, lig_ptr(p), var_mem_max); + break; + case glue_node: + check_glue_ref(glue_ptr(p)); + dorangetest(p, leader_ptr(p), var_mem_max); + break; + case hlist_node: + case vlist_node: + case unset_node: + case align_record_node: + dorangetest(p, list_ptr(p), var_mem_max); + break; + case ins_node: + dorangetest(p, ins_ptr(p), var_mem_max); + check_glue_ref(split_top_ptr(p)); + break; + case whatsit_node: + switch (subtype(p)) { + case special_node: + check_token_ref(write_tokens(p)); + break; + case pdf_literal_node: + check_token_ref(pdf_literal_data(p)); + break; + case pdf_colorstack_node: + if (pdf_colorstack_cmd(p) <= colorstack_data) + check_token_ref(pdf_colorstack_data(p)); + break; + case pdf_setmatrix_node: + check_token_ref(pdf_setmatrix_data(p)); + break; + case late_lua_node: + check_token_ref(late_lua_data(p)); + break; + case pdf_annot_node: + check_token_ref(pdf_annot_data(p)); + break; + case pdf_start_link_node: + if (pdf_link_attr(p) != null) + check_token_ref(pdf_link_attr(p)); + check_action_ref(pdf_link_action(p)); + break; + case pdf_dest_node: + if (pdf_dest_named_id(p) > 0) + check_token_ref(pdf_dest_id(p)); + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (pdf_thread_named_id(p) > 0) + check_token_ref(pdf_thread_id(p)); + if (pdf_thread_attr(p) != null) + check_token_ref(pdf_thread_attr(p)); + break; + case user_defined_node: + switch (user_node_type(p)) { + case 'a': + check_attribute_ref(user_node_value(p)); + break; + case 't': + check_token_ref(user_node_value(p)); + break; + case 'n': + dorangetest(p, user_node_value(p), var_mem_max); + break; + default: + tconfusion("extuser"); + break; + } + break; + case dir_node: + case open_node: + case write_node: + case close_node: + case pdf_save_node: + case pdf_restore_node: + case cancel_boundary_node: + case close_lua_node: + case pdf_refobj_node: + case pdf_refxform_node: + case pdf_refximage_node: + case pdf_end_link_node: + case pdf_end_thread_node: + case pdf_save_pos_node: + case local_par_node: + break; + default: + tconfusion("ext3"); + } + break; + case margin_kern_node: + check_node(margin_char(p)); + break; + case disc_node: + dorangetest(p, vlink(pre_break(p)), var_mem_max); + dorangetest(p, vlink(post_break(p)), var_mem_max); + dorangetest(p, vlink(no_break(p)), var_mem_max); + break; + case adjust_node: + dorangetest(p, adjust_ptr(p), var_mem_max); + break; + case pseudo_file_node: + dorangetest(p, pseudo_lines(p), var_mem_max); + break; + case pseudo_line_node: + case shape_node: + break; + case choice_node: + dorangetest(p, display_mlist(p), var_mem_max); + dorangetest(p, text_mlist(p), var_mem_max); + dorangetest(p, script_mlist(p), var_mem_max); + dorangetest(p, script_script_mlist(p), var_mem_max); + break; + case fraction_noad: + dorangetest(p, vinfo(numerator(p)), var_mem_max); + dorangetest(p, vinfo(denominator(p)), var_mem_max); + break; + case rule_node: + case kern_node: + case math_node: + case penalty_node: + case mark_node: + case style_node: + case ord_noad: + case op_noad: + case bin_noad: + case rel_noad: + case open_noad: + case close_noad: + case punct_noad: + case inner_noad: + case radical_noad: + case over_noad: + case under_noad: + case vcenter_noad: + case accent_noad: + case left_noad: + case right_noad: + case attribute_list_node: + case attribute_node: + case glue_spec_node: + case temp_node: + case align_stack_node: + case movement_node: + case if_node: + case nesting_node: + case span_node: + case unhyphenated_node: + case hyphenated_node: + case delta_node: + case passive_node: + case expr_node: + break; default: - tconfusion("ext3"); + fprintf(stdout, "check_node: type is %d\n", type(p)); } - break; - case margin_kern_node: - check_node(margin_char(p)); - break; - case disc_node: - dorangetest(p,vlink(pre_break(p)),var_mem_max); - dorangetest(p,vlink(post_break(p)),var_mem_max); - dorangetest(p,vlink(no_break(p)),var_mem_max); - break; - case adjust_node: - dorangetest(p,adjust_ptr(p),var_mem_max); - break; - case pseudo_file_node: - dorangetest(p,pseudo_lines(p),var_mem_max); - break; - case pseudo_line_node: - case shape_node: - break; - case choice_node: - dorangetest(p,display_mlist(p),var_mem_max); - dorangetest(p,text_mlist(p),var_mem_max); - dorangetest(p,script_mlist(p),var_mem_max); - dorangetest(p,script_script_mlist(p),var_mem_max); - break; - case fraction_noad: - dorangetest(p,vinfo(numerator(p)),var_mem_max); - dorangetest(p,vinfo(denominator(p)),var_mem_max); - break; - case rule_node: - case kern_node: - case math_node: - case penalty_node: - case mark_node: - case style_node: - case ord_noad: - case op_noad: - case bin_noad: - case rel_noad: - case open_noad: - case close_noad: - case punct_noad: - case inner_noad: - case radical_noad: - case over_noad: - case under_noad: - case vcenter_noad: - case accent_noad: - case left_noad: - case right_noad: - case attribute_list_node: - case attribute_node: - case glue_spec_node: - case temp_node: - case align_stack_node: - case movement_node: - case if_node: - case nesting_node: - case span_node: - case unhyphenated_node: - case hyphenated_node: - case delta_node: - case passive_node: - case expr_node: - break; - default: - fprintf(stdout,"check_node: type is %d\n", type(p)); - } } -void -check_static_node_mem (void) { - dotest(zero_glue,width(zero_glue),0); - dotest(zero_glue,type(zero_glue),glue_spec_node); - dotest(zero_glue,vlink(zero_glue),null); - dotest(zero_glue,stretch(zero_glue),0); - dotest(zero_glue,stretch_order(zero_glue),normal); - dotest(zero_glue,shrink(zero_glue),0); - dotest(zero_glue,shrink_order(zero_glue),normal); - - dotest(sfi_glue,width(sfi_glue),0); - dotest(sfi_glue,type(sfi_glue),glue_spec_node); - dotest(sfi_glue,vlink(sfi_glue),null); - dotest(sfi_glue,stretch(sfi_glue),0); - dotest(sfi_glue,stretch_order(sfi_glue),sfi); - dotest(sfi_glue,shrink(sfi_glue),0); - dotest(sfi_glue,shrink_order(sfi_glue),normal); - - dotest(fil_glue,width(fil_glue),0); - dotest(fil_glue,type(fil_glue),glue_spec_node); - dotest(fil_glue,vlink(fil_glue),null); - dotest(fil_glue,stretch(fil_glue),unity); - dotest(fil_glue,stretch_order(fil_glue),fil); - dotest(fil_glue,shrink(fil_glue),0); - dotest(fil_glue,shrink_order(fil_glue),normal); - - dotest(fill_glue,width(fill_glue),0); - dotest(fill_glue,type(fill_glue),glue_spec_node); - dotest(fill_glue,vlink(fill_glue),null); - dotest(fill_glue,stretch(fill_glue),unity); - dotest(fill_glue,stretch_order(fill_glue),fill); - dotest(fill_glue,shrink(fill_glue),0); - dotest(fill_glue,shrink_order(fill_glue),normal); - - dotest(ss_glue,width(ss_glue),0); - dotest(ss_glue,type(ss_glue),glue_spec_node); - dotest(ss_glue,vlink(ss_glue),null); - dotest(ss_glue,stretch(ss_glue),unity); - dotest(ss_glue,stretch_order(ss_glue),fil); - dotest(ss_glue,shrink(ss_glue),unity); - dotest(ss_glue,shrink_order(ss_glue),fil); - - dotest(fil_neg_glue,width(fil_neg_glue),0); - dotest(fil_neg_glue,type(fil_neg_glue),glue_spec_node); - dotest(fil_neg_glue,vlink(fil_neg_glue),null); - dotest(fil_neg_glue,stretch(fil_neg_glue),-unity); - dotest(fil_neg_glue,stretch_order(fil_neg_glue),fil); - dotest(fil_neg_glue,shrink(fil_neg_glue),0); - dotest(fil_neg_glue,shrink_order(fil_neg_glue),normal); +void check_static_node_mem(void) +{ + dotest(zero_glue, width(zero_glue), 0); + dotest(zero_glue, type(zero_glue), glue_spec_node); + dotest(zero_glue, vlink(zero_glue), null); + dotest(zero_glue, stretch(zero_glue), 0); + dotest(zero_glue, stretch_order(zero_glue), normal); + dotest(zero_glue, shrink(zero_glue), 0); + dotest(zero_glue, shrink_order(zero_glue), normal); + + dotest(sfi_glue, width(sfi_glue), 0); + dotest(sfi_glue, type(sfi_glue), glue_spec_node); + dotest(sfi_glue, vlink(sfi_glue), null); + dotest(sfi_glue, stretch(sfi_glue), 0); + dotest(sfi_glue, stretch_order(sfi_glue), sfi); + dotest(sfi_glue, shrink(sfi_glue), 0); + dotest(sfi_glue, shrink_order(sfi_glue), normal); + + dotest(fil_glue, width(fil_glue), 0); + dotest(fil_glue, type(fil_glue), glue_spec_node); + dotest(fil_glue, vlink(fil_glue), null); + dotest(fil_glue, stretch(fil_glue), unity); + dotest(fil_glue, stretch_order(fil_glue), fil); + dotest(fil_glue, shrink(fil_glue), 0); + dotest(fil_glue, shrink_order(fil_glue), normal); + + dotest(fill_glue, width(fill_glue), 0); + dotest(fill_glue, type(fill_glue), glue_spec_node); + dotest(fill_glue, vlink(fill_glue), null); + dotest(fill_glue, stretch(fill_glue), unity); + dotest(fill_glue, stretch_order(fill_glue), fill); + dotest(fill_glue, shrink(fill_glue), 0); + dotest(fill_glue, shrink_order(fill_glue), normal); + + dotest(ss_glue, width(ss_glue), 0); + dotest(ss_glue, type(ss_glue), glue_spec_node); + dotest(ss_glue, vlink(ss_glue), null); + dotest(ss_glue, stretch(ss_glue), unity); + dotest(ss_glue, stretch_order(ss_glue), fil); + dotest(ss_glue, shrink(ss_glue), unity); + dotest(ss_glue, shrink_order(ss_glue), fil); + + dotest(fil_neg_glue, width(fil_neg_glue), 0); + dotest(fil_neg_glue, type(fil_neg_glue), glue_spec_node); + dotest(fil_neg_glue, vlink(fil_neg_glue), null); + dotest(fil_neg_glue, stretch(fil_neg_glue), -unity); + dotest(fil_neg_glue, stretch_order(fil_neg_glue), fil); + dotest(fil_neg_glue, shrink(fil_neg_glue), 0); + dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal); } -void -check_node_mem(void ) { - int i; - check_static_node_mem(); - - for (i=(prealloc+1);i<var_mem_max;i++) { - if (varmem_sizes[i]>0) { - check_node(i); +void check_node_mem(void) +{ + int i; + check_static_node_mem(); + + for (i = (prealloc + 1); i < var_mem_max; i++) { + if (varmem_sizes[i] > 0) { + check_node(i); + } } - } - test_count++; + test_count++; } -void -fix_node_list (halfword head) { - halfword p,q; - if (head==null) - return; - p = head; - q = vlink(p); - while (q!=null) { - alink(q) = p; - p = q; +void fix_node_list(halfword head) +{ + halfword p, q; + if (head == null) + return; + p = head; q = vlink(p); - } + while (q != null) { + alink(q) = p; + p = q; + q = vlink(p); + } } -halfword -get_node (integer s) { - register halfword r; +halfword get_node(integer s) +{ + register halfword r; - /*check_static_node_mem();*/ - assert(s<MAX_CHAIN_SIZE); + /*check_static_node_mem(); */ + assert(s < MAX_CHAIN_SIZE); - r = free_chain[s]; - if (r!=null) { - free_chain[s] = vlink(r); + r = free_chain[s]; + if (r != null) { + free_chain[s] = vlink(r); #ifndef NDEBUG - varmem_sizes[r] = s; + varmem_sizes[r] = s; #endif - vlink(r)=null; - var_used += s; /* maintain usage statistics */ - return r; - } - /* this is the end of the 'inner loop' */ - return slow_get_node(s); + vlink(r) = null; + var_used += s; /* maintain usage statistics */ + return r; + } + /* this is the end of the 'inner loop' */ + return slow_get_node(s); } -void print_free_chain (int c) { - halfword p = free_chain[c]; - fprintf(stdout, "\nfree chain[%d] =\n ",c); - while (p!=null) { - fprintf(stdout, "%d,",p); - p = vlink(p); - } - fprintf(stdout, "null;\n"); +void print_free_chain(int c) +{ + halfword p = free_chain[c]; + fprintf(stdout, "\nfree chain[%d] =\n ", c); + while (p != null) { + fprintf(stdout, "%d,", (int)p); + p = vlink(p); + } + fprintf(stdout, "null;\n"); } -void -free_node (halfword p, integer s) { - - if (p<=prealloc) { - fprintf(stdout,"node %d (type %d) should not be freed!\n",(int)p, type(p)); - return; - } +void free_node(halfword p, integer s) +{ + if (p <= prealloc) { + fprintf(stdout, "node %d (type %d) should not be freed!\n", (int) p, + type(p)); + return; + } #ifndef NDEBUG - varmem_sizes[p] = 0; + varmem_sizes[p] = 0; #endif - if (s<MAX_CHAIN_SIZE) { - vlink(p) = free_chain[s]; - free_chain[s] = p; - } else { - /* todo ? it is perhaps possible to merge this node with an existing rover */ - node_size(p)=s; - vlink(p) = rover; - while (vlink(rover)!=vlink(p)){ - rover=vlink(rover); + if (s < MAX_CHAIN_SIZE) { + vlink(p) = free_chain[s]; + free_chain[s] = p; + } else { + /* todo ? it is perhaps possible to merge this node with an existing rover */ + node_size(p) = s; + vlink(p) = rover; + while (vlink(rover) != vlink(p)) { + rover = vlink(rover); + } + vlink(rover) = p; } - vlink(rover) = p; - } - var_used -= s; /* maintain statistics */ + var_used -= s; /* maintain statistics */ } -void -free_node_chain (halfword q, integer s) { - register halfword p = q; - while (vlink(p)!=null) { +void free_node_chain(halfword q, integer s) +{ + register halfword p = q; + while (vlink(p) != null) { #ifndef NDEBUG - varmem_sizes[p] = 0; + varmem_sizes[p] = 0; #endif + var_used -= s; + p = vlink(p); + } var_used -= s; - p = vlink(p); - } - var_used -= s; #ifndef NDEBUG - varmem_sizes[p] = 0; + varmem_sizes[p] = 0; #endif - vlink(p) = free_chain[s]; - free_chain[s] = q; + vlink(p) = free_chain[s]; + free_chain[s] = q; } -void -init_node_mem (halfword prealloced, halfword t) { - prealloc=prealloced; - assert(whatsit_node_data[user_defined_node].id==user_defined_node); - assert(node_data[passive_node].id==passive_node); +void init_node_mem(halfword prealloced, halfword t) +{ + prealloc = prealloced; + assert(whatsit_node_data[user_defined_node].id == user_defined_node); + assert(node_data[passive_node].id == passive_node); - varmem = (memory_word *)realloc((void *)varmem,sizeof(memory_word)*t); - if (varmem==NULL) { - overflow_string("node memory size",var_mem_max); - } - memset ((void *)(varmem),0,t*sizeof(memory_word)); + varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * t); + if (varmem == NULL) { + overflow_string("node memory size", var_mem_max); + } + memset((void *) (varmem), 0, t * sizeof(memory_word)); #ifndef NDEBUG - varmem_sizes = (char *)realloc(varmem_sizes,sizeof(char)*t); - if (varmem_sizes==NULL) { - overflow_string("node memory size",var_mem_max); - } - memset ((void *)varmem_sizes,0,sizeof(char)*t); + varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * t); + if (varmem_sizes == NULL) { + overflow_string("node memory size", var_mem_max); + } + memset((void *) varmem_sizes, 0, sizeof(char) * t); #endif - var_mem_max=t; - rover = prealloced+1; vlink(rover) = rover; - node_size(rover)=(t-rover); - var_used = 0; + var_mem_max = t; + rover = prealloced + 1; + vlink(rover) = rover; + node_size(rover) = (t - rover); + var_used = 0; } -void -dump_node_mem (void) { - dump_int(var_mem_max); - dump_int(rover); - dump_things(varmem[0],var_mem_max); +void dump_node_mem(void) +{ + dump_int(var_mem_max); + dump_int(rover); + dump_things(varmem[0], var_mem_max); #ifndef NDEBUG - dump_things(varmem_sizes[0],var_mem_max); + dump_things(varmem_sizes[0], var_mem_max); #endif - dump_things(free_chain[0],MAX_CHAIN_SIZE); - dump_int(var_used); - dump_int(prealloc); + dump_things(free_chain[0], MAX_CHAIN_SIZE); + dump_int(var_used); + dump_int(prealloc); } /* it makes sense to enlarge the varmem array immediately */ -void -undump_node_mem (void) { - int x; - undump_int(x); - undump_int(rover); - var_mem_max = (x<100000 ? 100000 : x); - varmem = xmallocarray (memory_word, var_mem_max); - /*memset ((void *)varmem,0,x*sizeof(memory_word));*/ - undump_things(varmem[0],x); +void undump_node_mem(void) +{ + int x; + undump_int(x); + undump_int(rover); + var_mem_max = (x < 100000 ? 100000 : x); + varmem = xmallocarray(memory_word, var_mem_max); + /*memset ((void *)varmem,0,x*sizeof(memory_word)); */ + undump_things(varmem[0], x); #ifndef NDEBUG - varmem_sizes = xmallocarray (char, var_mem_max); - memset ((void *)varmem_sizes,0,var_mem_max*sizeof(char)); - undump_things(varmem_sizes[0],x); + varmem_sizes = xmallocarray(char, var_mem_max); + memset((void *) varmem_sizes, 0, var_mem_max * sizeof(char)); + undump_things(varmem_sizes[0], x); #endif - undump_things(free_chain[0],MAX_CHAIN_SIZE); - undump_int(var_used); - undump_int(prealloc); - if (var_mem_max>x) { - /* todo ? it is perhaps possible to merge the new node with an existing rover */ - vlink (x) = rover; - node_size(x) = (var_mem_max-x); - while(vlink(rover)!=vlink(x)) {rover=vlink(rover);} - vlink (rover) = x; - } + undump_things(free_chain[0], MAX_CHAIN_SIZE); + undump_int(var_used); + undump_int(prealloc); + if (var_mem_max > x) { + /* todo ? it is perhaps possible to merge the new node with an existing rover */ + vlink(x) = rover; + node_size(x) = (var_mem_max - x); + while (vlink(rover) != vlink(x)) { + rover = vlink(rover); + } + vlink(rover) = x; + } } #if 0 -void test_rovers (char *s) { - int q = rover; - int r = q; - fprintf(stdout,"%s: {rover=%d,size=%d,link=%d}",s,r,node_size(r),vlink(r)); - while (vlink(r)!=q) { - r = vlink(r); - fprintf(stdout,",{rover=%d,size=%d,link=%d}",r,node_size(r),vlink(r)); - } - fprintf(stdout,"\n"); +void test_rovers(char *s) +{ + int q = rover; + int r = q; + fprintf(stdout, "%s: {rover=%d,size=%d,link=%d}", s, r, node_size(r), + vlink(r)); + while (vlink(r) != q) { + r = vlink(r); + fprintf(stdout, ",{rover=%d,size=%d,link=%d}", r, node_size(r), + vlink(r)); + } + fprintf(stdout, "\n"); } #else -#define test_rovers(a) +# define test_rovers(a) #endif -halfword -slow_get_node (integer s) { - register int t; - - RETRY: - t = node_size(rover); - assert(vlink(rover)<var_mem_max); - assert(vlink(rover)!=0); - test_rovers("entry"); - if (t>s) { - register halfword r; - /* allocating from the bottom helps decrease page faults */ - r = rover; - rover += s; - vlink(rover)=vlink(r); - node_size(rover) = node_size(r) - s; - if (vlink(rover)!=r) { /* list is longer than one */ - halfword q = r; - while (vlink(q)!=r) { q=vlink(q);} - vlink(q) += s; - } else { - vlink(rover) += s; - } - test_rovers("taken"); - assert(vlink(rover)<var_mem_max); +halfword slow_get_node(integer s) +{ + register int t; + + RETRY: + t = node_size(rover); + assert(vlink(rover) < var_mem_max); + assert(vlink(rover) != 0); + test_rovers("entry"); + if (t > s) { + register halfword r; + /* allocating from the bottom helps decrease page faults */ + r = rover; + rover += s; + vlink(rover) = vlink(r); + node_size(rover) = node_size(r) - s; + if (vlink(rover) != r) { /* list is longer than one */ + halfword q = r; + while (vlink(q) != r) { + q = vlink(q); + } + vlink(q) += s; + } else { + vlink(rover) += s; + } + test_rovers("taken"); + assert(vlink(rover) < var_mem_max); #ifndef NDEBUG - varmem_sizes[r]=(s>127 ? 127 : s); + varmem_sizes[r] = (s > 127 ? 127 : s); #endif - vlink(r)=null; - var_used += s; /* maintain usage statistics */ - return r; /* this is the only exit */ - } else { - int x; - /* attempt to keep the free list small */ - if (vlink(rover)!=rover) { - if (t<MAX_CHAIN_SIZE) { - halfword l = vlink(rover); - vlink(rover) = free_chain[t]; - free_chain[t] = rover; - rover = l; - while (vlink(l)!=free_chain[t]) { l=vlink(l); } - vlink(l) = rover; - test_rovers("outtake"); - goto RETRY; - } else { - halfword l = rover; - while (vlink(rover)!=l) { - if (node_size(rover)>s) { - goto RETRY; - } - rover=vlink(rover); - } - } - } - /* if we are still here, it was apparently impossible to get a match */ - x = (var_mem_max>>2)+s; - varmem = (memory_word *)realloc((void *)varmem,sizeof(memory_word)*(var_mem_max+x)); - if (varmem==NULL) { - overflow_string("node memory size",var_mem_max); - } - memset ((void *)(varmem+var_mem_max),0,x*sizeof(memory_word)); - + vlink(r) = null; + var_used += s; /* maintain usage statistics */ + return r; /* this is the only exit */ + } else { + int x; + /* attempt to keep the free list small */ + if (vlink(rover) != rover) { + if (t < MAX_CHAIN_SIZE) { + halfword l = vlink(rover); + vlink(rover) = free_chain[t]; + free_chain[t] = rover; + rover = l; + while (vlink(l) != free_chain[t]) { + l = vlink(l); + } + vlink(l) = rover; + test_rovers("outtake"); + goto RETRY; + } else { + halfword l = rover; + while (vlink(rover) != l) { + if (node_size(rover) > s) { + goto RETRY; + } + rover = vlink(rover); + } + } + } + /* if we are still here, it was apparently impossible to get a match */ + x = (var_mem_max >> 2) + s; + varmem = + (memory_word *) realloc((void *) varmem, + sizeof(memory_word) * (var_mem_max + x)); + if (varmem == NULL) { + overflow_string("node memory size", var_mem_max); + } + memset((void *) (varmem + var_mem_max), 0, x * sizeof(memory_word)); + #ifndef NDEBUG - varmem_sizes = (char *)realloc(varmem_sizes,sizeof(char)*(var_mem_max+x)); - if (varmem_sizes==NULL) { - overflow_string("node memory size",var_mem_max); - } - memset ((void *)(varmem_sizes+var_mem_max),0,x*sizeof(char)); + varmem_sizes = + (char *) realloc(varmem_sizes, sizeof(char) * (var_mem_max + x)); + if (varmem_sizes == NULL) { + overflow_string("node memory size", var_mem_max); + } + memset((void *) (varmem_sizes + var_mem_max), 0, x * sizeof(char)); #endif - /* todo ? it is perhaps possible to merge the new memory with an existing rover */ - vlink(var_mem_max) = rover; - node_size(var_mem_max) = x; - while(vlink(rover)!=vlink(var_mem_max)) {rover=vlink(rover);} - vlink (rover) = var_mem_max; - rover = var_mem_max; - test_rovers("realloc"); - var_mem_max += x; - goto RETRY; - } + /* todo ? it is perhaps possible to merge the new memory with an existing rover */ + vlink(var_mem_max) = rover; + node_size(var_mem_max) = x; + while (vlink(rover) != vlink(var_mem_max)) { + rover = vlink(rover); + } + vlink(rover) = var_mem_max; + rover = var_mem_max; + test_rovers("realloc"); + var_mem_max += x; + goto RETRY; + } } -char * -sprint_node_mem_usage (void) { - int i,b; - - char *s, *ss; - char msg[256]; - int node_counts[last_normal_node+last_whatsit_node+2] = {0}; - - for (i=(var_mem_max-1);i>prealloc;i--) { - if (varmem_sizes[i]>0) { - if (type(i)>last_normal_node+last_whatsit_node) { - node_counts[last_normal_node+last_whatsit_node+1] ++; - } else if (type(i)==whatsit_node){ - node_counts[(subtype(i)+last_normal_node+1)] ++; - } else { - node_counts[type(i)] ++; - } +char *sprint_node_mem_usage(void) +{ + int i, b; + + char *s, *ss; + char msg[256]; + int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 }; + + for (i = (var_mem_max - 1); i > prealloc; i--) { + if (varmem_sizes[i] > 0) { + if (type(i) > last_normal_node + last_whatsit_node) { + node_counts[last_normal_node + last_whatsit_node + 1]++; + } else if (type(i) == whatsit_node) { + node_counts[(subtype(i) + last_normal_node + 1)]++; + } else { + node_counts[type(i)]++; + } + } } - } - s = strdup(""); - b=0; - for (i=0;i<last_normal_node+last_whatsit_node+2;i++) { - if (node_counts[i]>0) { - int j = (i>(last_normal_node+1) ? (i-last_normal_node-1) : 0); - snprintf(msg,255,"%s%d %s",(b ? ", " : ""), - (int)node_counts[i], - get_node_name((i>last_normal_node ? whatsit_node : i),j)); - ss = concat(s,msg); - free (s); - s = ss; - b=1; + s = strdup(""); + b = 0; + for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) { + if (node_counts[i] > 0) { + int j = + (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0); + snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i], + get_node_name((i > last_normal_node ? whatsit_node : i), + j)); + ss = concat(s, msg); + free(s); + s = ss; + b = 1; + } } - } - return s; + return s; } -halfword -list_node_mem_usage (void) { - halfword i, j; - halfword p = null,q = null; - char *saved_varmem_sizes = xmallocarray (char, var_mem_max); - memcpy (saved_varmem_sizes,varmem_sizes,var_mem_max); - for (i=prealloc+1;i<(var_mem_max-1);i++) { - if (saved_varmem_sizes[i]>0) { - j = copy_node(i); - if (p==null) { - q = j; - } else { - vlink(p)=j; - } - p = j; +halfword list_node_mem_usage(void) +{ + halfword i, j; + halfword p = null, q = null; + char *saved_varmem_sizes = xmallocarray(char, var_mem_max); + memcpy(saved_varmem_sizes, varmem_sizes, var_mem_max); + for (i = prealloc + 1; i < (var_mem_max - 1); i++) { + if (saved_varmem_sizes[i] > 0) { + j = copy_node(i); + if (p == null) { + q = j; + } else { + vlink(p) = j; + } + p = j; + } } - } - free(saved_varmem_sizes); - return q; + free(saved_varmem_sizes); + return q; } -void -print_node_mem_stats (int num, int online) { - int i,b; - halfword j; - char msg[256]; - char *s; - integer free_chain_counts[MAX_CHAIN_SIZE] = {0}; - - snprintf(msg,255,"node memory in use: %d words out of %d", (int)(var_used+prealloc), (int)var_mem_max); - tprint_nl(msg); - tprint_nl("rapidly available: "); - b=0; - for (i=1;i<MAX_CHAIN_SIZE;i++) { - for(j = free_chain[i];j!=null;j = vlink(j)) - free_chain_counts[i] ++; - if (free_chain_counts[i]>0) { - snprintf(msg,255,"%s%d:%d",(b ? ", " : ""),i, (int)free_chain_counts[i]); - tprint(msg); - b=1; +void print_node_mem_stats(int num, int online) +{ + int i, b; + halfword j; + char msg[256]; + char *s; + integer free_chain_counts[MAX_CHAIN_SIZE] = { 0 }; + + snprintf(msg, 255, "node memory in use: %d words out of %d", + (int) (var_used + prealloc), (int) var_mem_max); + tprint_nl(msg); + tprint_nl("rapidly available: "); + b = 0; + for (i = 1; i < MAX_CHAIN_SIZE; i++) { + for (j = free_chain[i]; j != null; j = vlink(j)) + free_chain_counts[i]++; + if (free_chain_counts[i] > 0) { + snprintf(msg, 255, "%s%d:%d", (b ? ", " : ""), i, + (int) free_chain_counts[i]); + tprint(msg); + b = 1; + } } - } - tprint(" nodes"); - s = sprint_node_mem_usage(); - tprint_nl("current usage: "); - tprint(s); free(s); - tprint(" nodes"); - print_nlp(); /* newline, if needed */ + tprint(" nodes"); + s = sprint_node_mem_usage(); + tprint_nl("current usage: "); + tprint(s); + free(s); + tprint(" nodes"); + print_nlp(); /* newline, if needed */ } /* this belongs in the web but i couldn't find the correct syntactic place */ -halfword -new_span_node(halfword n, int s, scaled w) { - halfword p=new_node(span_node,0); - span_link(p)=n; - span_span(p)=s; - width(p)=w; - return p; +halfword new_span_node(halfword n, int s, scaled w) +{ + halfword p = new_node(span_node, 0); + span_link(p) = n; + span_span(p) = s; + width(p) = w; + return p; } -halfword -string_to_pseudo(integer l,integer pool_ptr, integer nl) { - halfword i, r, q = null; - four_quarters w; - int sz; - halfword h = new_node(pseudo_file_node,0); - while (l<pool_ptr) { - int m = l; - while ((l<pool_ptr)&&(str_pool[l]!=nl)) l++; - sz = (l-m+7) / 4; - if (sz==1) - sz=2; - r = new_node(pseudo_line_node,sz); - i = r; - while (--sz>1) { - w.b0=str_pool[m++]; - w.b1=str_pool[m++]; - w.b2=str_pool[m++]; - w.b3=str_pool[m++]; - varmem[++i].qqqq=w; - } - w.b0 = (l>m ? str_pool[m++] : ' '); - w.b1 = (l>m ? str_pool[m++] : ' '); - w.b2 = (l>m ? str_pool[m++] : ' '); - w.b3 = (l>m ? str_pool[m] : ' '); - varmem[++i].qqqq=w; - if (pseudo_lines(h)==null) { - pseudo_lines(h) = r; - q = r; - } else { - couple_nodes(q,r); +halfword string_to_pseudo(integer l, integer pool_ptr, integer nl) +{ + halfword i, r, q = null; + four_quarters w; + int sz; + halfword h = new_node(pseudo_file_node, 0); + while (l < pool_ptr) { + int m = l; + while ((l < pool_ptr) && (str_pool[l] != nl)) + l++; + sz = (l - m + 7) / 4; + if (sz == 1) + sz = 2; + r = new_node(pseudo_line_node, sz); + i = r; + while (--sz > 1) { + w.b0 = str_pool[m++]; + w.b1 = str_pool[m++]; + w.b2 = str_pool[m++]; + w.b3 = str_pool[m++]; + varmem[++i].qqqq = w; + } + w.b0 = (l > m ? str_pool[m++] : ' '); + w.b1 = (l > m ? str_pool[m++] : ' '); + w.b2 = (l > m ? str_pool[m++] : ' '); + w.b3 = (l > m ? str_pool[m] : ' '); + varmem[++i].qqqq = w; + if (pseudo_lines(h) == null) { + pseudo_lines(h) = r; + q = r; + } else { + couple_nodes(q, r); + } + q = vlink(q); + if (str_pool[l] == nl) + l++; } - q = vlink(q); - if (str_pool[l]==nl) l++; - } - return h; + return h; } /* attribute stuff */ -static halfword -new_attribute_node (unsigned int i, int v) { - register halfword r = get_node(attribute_node_size); - type(r) = attribute_node; - attribute_id(r)= i; - attribute_value(r)= v; - return r; +static halfword new_attribute_node(unsigned int i, int v) +{ + register halfword r = get_node(attribute_node_size); + type(r) = attribute_node; + attribute_id(r) = i; + attribute_value(r) = v; + return r; } -halfword -copy_attribute_list(halfword n) { - halfword q = get_node(attribute_node_size); - register halfword p = q; - type(p) = attribute_list_node; - attr_list_ref(p)=0; - n = vlink(n); - while (n!=null) { - register halfword r = get_node(attribute_node_size); - /* the link will be fixed automatically in the next loop */ - (void)memcpy((void *)(varmem+r),(void *)(varmem+n),(sizeof(memory_word)*attribute_node_size)); - vlink(p) = r; - p = r; +halfword copy_attribute_list(halfword n) +{ + halfword q = get_node(attribute_node_size); + register halfword p = q; + type(p) = attribute_list_node; + attr_list_ref(p) = 0; n = vlink(n); - } - return q; + while (n != null) { + register halfword r = get_node(attribute_node_size); + /* the link will be fixed automatically in the next loop */ + (void) memcpy((void *) (varmem + r), (void *) (varmem + n), + (sizeof(memory_word) * attribute_node_size)); + vlink(p) = r; + p = r; + n = vlink(n); + } + return q; } -void -update_attribute_cache(void) { - halfword p; - register int i; - attr_list_cache=get_node(attribute_node_size); - type(attr_list_cache) = attribute_list_node; - attr_list_ref(attr_list_cache)=0; - p=attr_list_cache; - for (i=0;i<=max_used_attr;i++) { - register int v = get_attribute(i); - if (v>=0) { - register halfword r = new_attribute_node(i,v); - vlink(p)=r; - p=r; - } - } - if (vlink(attr_list_cache)==null) { - free_node(attr_list_cache,attribute_node_size); - attr_list_cache = null; - } - return; +void update_attribute_cache(void) +{ + halfword p; + register int i; + attr_list_cache = get_node(attribute_node_size); + type(attr_list_cache) = attribute_list_node; + attr_list_ref(attr_list_cache) = 0; + p = attr_list_cache; + for (i = 0; i <= max_used_attr; i++) { + register int v = get_attribute(i); + if (v >= 0) { + register halfword r = new_attribute_node(i, v); + vlink(p) = r; + p = r; + } + } + if (vlink(attr_list_cache) == null) { + free_node(attr_list_cache, attribute_node_size); + attr_list_cache = null; + } + return; } -void -build_attribute_list(halfword b) { - if (max_used_attr>=0) { - if (attr_list_cache==cache_disabled) { - update_attribute_cache(); - if (attr_list_cache == null) - return; +void build_attribute_list(halfword b) +{ + if (max_used_attr >= 0) { + if (attr_list_cache == cache_disabled) { + update_attribute_cache(); + if (attr_list_cache == null) + return; + } + attr_list_ref(attr_list_cache)++; + node_attr(b) = attr_list_cache; } - attr_list_ref(attr_list_cache)++; - node_attr(b)=attr_list_cache; - } } -void -delete_attribute_ref(halfword b) { - if (b!=null){ - assert(type(b)==attribute_list_node); - attr_list_ref(b)--; - if (attr_list_ref(b)==0) { - if (b==attr_list_cache) - attr_list_cache=cache_disabled; - free_node_chain(b,attribute_node_size); +void delete_attribute_ref(halfword b) +{ + if (b != null) { + assert(type(b) == attribute_list_node); + attr_list_ref(b)--; + if (attr_list_ref(b) == 0) { + if (b == attr_list_cache) + attr_list_cache = cache_disabled; + free_node_chain(b, attribute_node_size); + } + /* maintain sanity */ + if (attr_list_ref(b) < 0) + attr_list_ref(b) = 0; } - /* maintain sanity */ - if (attr_list_ref(b)<0) - attr_list_ref(b)=0; - } } /* |p| is an attr list head, or zero */ -halfword -do_set_attribute (halfword p, int i, int val) { - register halfword q; - register int j = 0; - if (p==null) { /* add a new head & node */ - q = get_node(attribute_node_size); - type(q) = attribute_list_node; - attr_list_ref(q)=1; - p = new_attribute_node(i,val); - vlink(q)=p; +halfword do_set_attribute(halfword p, int i, int val) +{ + register halfword q; + register int j = 0; + if (p == null) { /* add a new head & node */ + q = get_node(attribute_node_size); + type(q) = attribute_list_node; + attr_list_ref(q) = 1; + p = new_attribute_node(i, val); + vlink(q) = p; + return q; + } + + assert(vlink(p) != null); + while (vlink(p) != null) { + int t = attribute_id(vlink(p)); + if (t == i && attribute_value(vlink(p)) == val) + return q; /* no need to do anything */ + if (t >= i) + break; + j++; + p = vlink(p); + } + + p = q; + while (j-- > 0) + p = vlink(p); + if (attribute_id(vlink(p)) == i) { + attribute_value(vlink(p)) = val; + } else { /* add a new node */ + halfword r = new_attribute_node(i, val); + vlink(r) = vlink(p); + vlink(p) = r; + } return q; - } - - assert(vlink(p)!=null); - while (vlink(p)!=null) { - int t = attribute_id(vlink(p)); - if (t==i && attribute_value(vlink(p))==val) - return q; /* no need to do anything */ - if (t>=i) - break; - j++; - p = vlink(p); - } - - p = q; - while (j-->0) p = vlink(p); - if (attribute_id(vlink(p))==i) { - attribute_value(vlink(p))=val; - } else { /* add a new node */ - halfword r = new_attribute_node(i,val); - vlink(r)=vlink(p); - vlink(p)=r; - } - return q; } -void -set_attribute (halfword n, int i, int val) { - register halfword p; - register int j = 0; - if (!nodetype_has_attributes(type(n))) - return; - p = node_attr(n); - if (p==null) { /* add a new head & node */ - p = get_node(attribute_node_size); - type(p) = attribute_list_node; - attr_list_ref(p)=1; - node_attr(n) = p ; - p = new_attribute_node(i,val); - vlink(node_attr(n))=p; - return; - } - assert(vlink(p)!=null); - while (vlink(p)!=null) { - int t = attribute_id(vlink(p)); - if (t==i && attribute_value(vlink(p))==val) - return; - if (t>=i) - break; - j++; - p = vlink(p); - } - p = node_attr(n); - if (attr_list_ref(p)!=1) { - if (attr_list_ref(p)>1) { - p = copy_attribute_list (p); - delete_attribute_ref(node_attr(n)); - node_attr(n) = p; - } else { - fprintf(stdout,"Node %d has an attribute list that is free already\n",(int)n); +void set_attribute(halfword n, int i, int val) +{ + register halfword p; + register int j = 0; + if (!nodetype_has_attributes(type(n))) + return; + p = node_attr(n); + if (p == null) { /* add a new head & node */ + p = get_node(attribute_node_size); + type(p) = attribute_list_node; + attr_list_ref(p) = 1; + node_attr(n) = p; + p = new_attribute_node(i, val); + vlink(node_attr(n)) = p; + return; + } + assert(vlink(p) != null); + while (vlink(p) != null) { + int t = attribute_id(vlink(p)); + if (t == i && attribute_value(vlink(p)) == val) + return; + if (t >= i) + break; + j++; + p = vlink(p); + } + p = node_attr(n); + if (attr_list_ref(p) != 1) { + if (attr_list_ref(p) > 1) { + p = copy_attribute_list(p); + delete_attribute_ref(node_attr(n)); + node_attr(n) = p; + } else { + fprintf(stdout, + "Node %d has an attribute list that is free already\n", + (int) n); + } + attr_list_ref(p) = 1; + } + while (j-- > 0) + p = vlink(p); + + if (attribute_id(vlink(p)) == i) { + attribute_value(vlink(p)) = val; + } else { /* add a new node */ + halfword r = new_attribute_node(i, val); + vlink(r) = vlink(p); + vlink(p) = r; } - attr_list_ref(p)=1; - } - while (j-->0) p = vlink(p); - - if (attribute_id(vlink(p))==i) { - attribute_value(vlink(p))=val; - } else { /* add a new node */ - halfword r = new_attribute_node(i,val); - vlink(r)=vlink(p); - vlink(p)=r; - } - return; + return; } -int -unset_attribute (halfword n, int i, int val) { - register halfword p; - register int t; - register int j = 0; - - if (!nodetype_has_attributes(type(n))) - return null; - p = node_attr(n); - if (p==null) - return -1; - assert (vlink(p)!=null); - while (vlink(p)!=null) { - t = attribute_id(vlink(p)); - if (t>i) - return -1; - if (t==i) - break; - j++; +int unset_attribute(halfword n, int i, int val) +{ + register halfword p; + register int t; + register int j = 0; + + if (!nodetype_has_attributes(type(n))) + return null; + p = node_attr(n); + if (p == null) + return -1; + assert(vlink(p) != null); + while (vlink(p) != null) { + t = attribute_id(vlink(p)); + if (t > i) + return -1; + if (t == i) + break; + j++; + p = vlink(p); + } + /* if we are still here, the attribute exists */ + p = node_attr(n); + if (attr_list_ref(p) != 1) { + if (attr_list_ref(p) > 1) { + p = copy_attribute_list(p); + delete_attribute_ref(node_attr(n)); + node_attr(n) = p; + } else { + fprintf(stdout, + "Node %d has an attribute list that is free already\n", + (int) n); + } + attr_list_ref(p) = 1; + } p = vlink(p); - } - /* if we are still here, the attribute exists */ - p = node_attr(n); - if (attr_list_ref(p)!=1) { - if (attr_list_ref(p)>1) { - p = copy_attribute_list (p); - delete_attribute_ref(node_attr(n)); - node_attr(n) = p; - } else { - fprintf(stdout,"Node %d has an attribute list that is free already\n",(int)n); + while (j-- > 0) + p = vlink(p); + t = attribute_value(p); + if (val == -1 || t == val) { + attribute_value(p) = -1; } - attr_list_ref(p)=1; - } - p = vlink(p); - while (j-->0) p = vlink(p); - t = attribute_value(p); - if (val==-1 || t==val) { - attribute_value(p)=-1; - } - return t; + return t; } -int -has_attribute (halfword n,int i, int val) { - register halfword p; - if (!nodetype_has_attributes(type(n))) - return -1; - p = node_attr(n); - if (p==null || vlink(p)==null) - return -1; - p = vlink(p); - while (p!=null) { - if (attribute_id(p)==i) { - int ret = attribute_value(p); - if (val==-1 || val==ret) - return ret; - return -1; - } else if (attribute_id(p)>i) { - return -1; +int has_attribute(halfword n, int i, int val) +{ + register halfword p; + if (!nodetype_has_attributes(type(n))) + return -1; + p = node_attr(n); + if (p == null || vlink(p) == null) + return -1; + p = vlink(p); + while (p != null) { + if (attribute_id(p) == i) { + int ret = attribute_value(p); + if (val == -1 || val == ret) + return ret; + return -1; + } else if (attribute_id(p) > i) { + return -1; + } + p = vlink(p); } - p=vlink(p); - } - return -1; + return -1; } - diff --git a/Build/source/texk/web2c/luatexdir/tex/texpdf.c b/Build/source/texk/web2c/luatexdir/tex/texpdf.c index fbb4c2b7e21..000742dd001 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texpdf.c +++ b/Build/source/texk/web2c/luatexdir/tex/texpdf.c @@ -1,4 +1,4 @@ -/* $Id: texpdf.c 1013 2008-02-14 00:09:02Z oneiros $ */ +/* $Id: texpdf.c 1158 2008-04-14 08:13:06Z oneiros $ */ #include "luatex-api.h" #include <ptexlib.h> @@ -7,7 +7,7 @@ #define number_chars 1114112 #define string_offset 2097152 #define str_start_macro(a) str_start[(a) - string_offset] -#define str_length(a) (str_start_macro((a)+1)-str_start_macro(a)) /* the number of characters in string number (a) */ +#define str_length(a) (str_start_macro((a)+1)-str_start_macro(a)) /* the number of characters in string number (a) */ #define is_hex_char isxdigit @@ -17,20 +17,20 @@ /* make sure that there are at least |n| bytes free in PDF buffer */ -#define pdf_room(a) do { \ - if ((pdf_os_mode) && ((a) + pdf_ptr > pdf_buf_size)) \ - pdf_os_get_os_buf(a); \ - else if ((!pdf_os_mode) && ((a) > pdf_buf_size) ) \ - overflow("PDF output buffer", pdf_op_buf_size); \ - else if ((!pdf_os_mode) && ((a) + pdf_ptr > pdf_buf_size)) \ - pdf_flush(); \ +#define pdf_room(a) do { \ + if ((pdf_os_mode) && ((a) + pdf_ptr > pdf_buf_size)) \ + pdf_os_get_os_buf(a); \ + else if ((!pdf_os_mode) && ((a) > pdf_buf_size) ) \ + overflow(maketexstring("PDF output buffer"), pdf_op_buf_size); \ + else if ((!pdf_os_mode) && ((a) + pdf_ptr > pdf_buf_size)) \ + pdf_flush(); \ } while (0) /* do the same as |pdf_quick_out| and flush the PDF buffer if necessary */ #define pdf_out(a) do { \ - pdf_room(1); \ - pdf_quick_out(a); \ + pdf_room(1); \ + pdf_quick_out(a); \ } while (0) @@ -39,131 +39,139 @@ * (40) and right parenthesis (41) */ -#define pdf_print_escaped(c) \ - if ((c)<=32||(c)=='\\'||(c)=='('||(c)==')'||(c)>127) { \ - pdf_room(4); \ - pdf_quick_out('\\'); \ - pdf_quick_out('0' + (((c)>>6) & 0x3)); \ - pdf_quick_out('0' + (((c)>>3) & 0x7)); \ - pdf_quick_out('0' + ( (c) & 0x7)); \ - } else { \ - pdf_out((c)); \ - } - -void -pdf_print_char(internal_font_number f, integer cc) { - register int c; - pdf_mark_char(f, cc); - if (font_encodingbytes(f)==2) { - register int chari; - chari = char_index(f, cc); - c = chari >> 8; - pdf_print_escaped(c); - c = chari & 0xFF; - } else { - if (cc>255) return; - c = cc; +#define pdf_print_escaped(c) \ + if ((c)<=32||(c)=='\\'||(c)=='('||(c)==')'||(c)>127) { \ + pdf_room(4); \ + pdf_quick_out('\\'); \ + pdf_quick_out('0' + (((c)>>6) & 0x3)); \ + pdf_quick_out('0' + (((c)>>3) & 0x7)); \ + pdf_quick_out('0' + ( (c) & 0x7)); \ + } else { \ + pdf_out((c)); \ } - pdf_print_escaped(c); + +void pdf_print_char(internal_font_number f, integer cc) +{ + register int c; + pdf_mark_char(f, cc); + if (font_encodingbytes(f) == 2) { + register int chari; + chari = char_index(f, cc); + c = chari >> 8; + pdf_print_escaped(c); + c = chari & 0xFF; + } else { + if (cc > 255) + return; + c = cc; + } + pdf_print_escaped(c); } /* print out a string to PDF buffer */ -void -pdf_print(str_number s) { - if (s < number_chars) { - assert(s<256); - pdf_out(s); - } else { - register pool_pointer j = str_start_macro(s); - while (j < str_start_macro(s + 1)) { - pdf_out(str_pool[j++]); - } - } +void pdf_print(str_number s) +{ + if (s < number_chars) { + assert(s < 256); + pdf_out(s); + } else { + register pool_pointer j = str_start_macro(s); + while (j < str_start_macro(s + 1)) { + pdf_out(str_pool[j++]); + } + } } /* print out a integer to PDF buffer */ -void -pdf_print_int(integer n) { - register integer k = 0; /* current digit; we assume that $|n|<10^{23}$ */ - if (n<0) { - pdf_out('-'); - if (n<-0x7FFFFFFF) { /* need to negate |n| more carefully */ - register integer m; - k++; - m =-1-n; n=m / 10; m= (m % 10)+1; - if (m<10) { - dig[0]=m ; - } else { - dig[0]=0; incr(n); - } - } else { - n = -n; - } - } - do { - dig[k++] = n % 10; - n = n / 10; - } while (n!=0); - pdf_room(k); - while (k-->0) { - pdf_quick_out('0'+dig[k]); - } +void pdf_print_int(integer n) +{ + register integer k = 0; /* current digit; we assume that $|n|<10^{23}$ */ + if (n < 0) { + pdf_out('-'); + if (n < -0x7FFFFFFF) { /* need to negate |n| more carefully */ + register integer m; + k++; + m = -1 - n; + n = m / 10; + m = (m % 10) + 1; + if (m < 10) { + dig[0] = m; + } else { + dig[0] = 0; + incr(n); + } + } else { + n = -n; + } + } + do { + dig[k++] = n % 10; + n = n / 10; + } while (n != 0); + pdf_room(k); + while (k-- > 0) { + pdf_quick_out('0' + dig[k]); + } } /* print $m/10^d$ as real */ -void -pdf_print_real(integer m, integer d) { - if (m < 0) { - pdf_out('-'); - m = -m; - }; - pdf_print_int(m / ten_pow[d]); - m = m % ten_pow[d]; - if (m > 0) { - pdf_out('.'); - d--; - while (m < ten_pow[d]) { - pdf_out('0'); - d--; +void pdf_print_real(integer m, integer d) +{ + if (m < 0) { + pdf_out('-'); + m = -m; + }; + pdf_print_int(m / ten_pow[d]); + m = m % ten_pow[d]; + if (m > 0) { + pdf_out('.'); + d--; + while (m < ten_pow[d]) { + pdf_out('0'); + d--; + } + while (m % 10 == 0) + m = m / 10; + pdf_print_int(m); } - while (m % 10 == 0) - m = m / 10; - pdf_print_int(m); - } } /* print out |s| as string in PDF output */ -void -pdf_print_str(str_number s) { - pool_pointer i, j; - i = str_start_macro(s); - j = str_start_macro(s+1) - 1; - if (i > j) { - pdf_room(2); - pdf_quick_out('('); - pdf_quick_out(')'); - return; - } - /* the next is not really safe, the string could be "(a)xx(b)" */ - if ((str_pool[i] == '(') && (str_pool[j] == ')')) { - pdf_print(s); - return; - } - if ((str_pool[i] != '<') || (str_pool[j] != '>') || odd(str_length(s))) { - pdf_out('('); pdf_print(s); pdf_out(')'); - return; - } - i++; - j--; - while (i < j) { - if (!is_hex_char(str_pool[i++])) { - pdf_out('('); pdf_print(s); pdf_out(')'); - return; +void pdf_print_str(str_number s) +{ + pool_pointer i, j; + i = str_start_macro(s); + j = str_start_macro(s + 1) - 1; + if (i > j) { + pdf_room(2); + pdf_quick_out('('); + pdf_quick_out(')'); + return; + } + /* the next is not really safe, the string could be "(a)xx(b)" */ + if ((str_pool[i] == '(') && (str_pool[j] == ')')) { + pdf_print(s); + return; + } + if ((str_pool[i] != '<') || (str_pool[j] != '>') || odd(str_length(s))) { + pdf_out('('); + pdf_print(s); + pdf_out(')'); + return; + } + i++; + j--; + while (i < j) { + if (!is_hex_char(str_pool[i++])) { + pdf_out('('); + pdf_print(s); + pdf_out(')'); + return; + } } - } - pdf_print(s); /* it was a hex string after all */ + pdf_print(s); /* it was a hex string after all */ } diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.c b/Build/source/texk/web2c/luatexdir/tex/textoken.c index 20c57b9479b..366fa798791 100644 --- a/Build/source/texk/web2c/luatexdir/tex/textoken.c +++ b/Build/source/texk/web2c/luatexdir/tex/textoken.c @@ -1,4 +1,4 @@ -/* $Id: textoken.c 1013 2008-02-14 00:09:02Z oneiros $ */ +/* $Id: textoken.c 1155 2008-04-14 07:53:21Z oneiros $ */ #include "luatex-api.h" #include <ptexlib.h> @@ -7,18 +7,18 @@ /* Integer parameters and other command-related defines. This needs it's own header file! */ -#define end_line_char_code 48 /* character placed at the right end of the buffer */ +#define end_line_char_code 48 /* character placed at the right end of the buffer */ #define cat_code_table_code 63 -#define tex_int_pars 66 /* total number of \.{\\TeX} + Aleph integer parameters */ +#define tex_int_pars 66 /* total number of \.{\\TeX} + Aleph integer parameters */ /* this is not what happens in the pascal code! there the values shift from bare numbers to offsets ! */ #define dir_base tex_int_pars #define dir_pars 5 -#define pdftex_first_integer_code dir_base+dir_pars /* base for \pdfTeX's integer parameters */ -#define pdf_int_pars pdftex_first_integer_code+27 /*total number of \pdfTeX's integer parameters*/ -#define etex_int_base pdf_int_pars /*base for \eTeX's integer parameters*/ -#define tracing_nesting_code etex_int_base+4 /*show incomplete groups and ifs within files*/ +#define pdftex_first_integer_code dir_base+dir_pars /* base for \pdfTeX's integer parameters */ +#define pdf_int_pars pdftex_first_integer_code+27 /*total number of \pdfTeX's integer parameters */ +#define etex_int_base pdf_int_pars /*base for \eTeX's integer parameters */ +#define tracing_nesting_code etex_int_base+4 /*show incomplete groups and ifs within files */ -#define int_par(a) zeqtb[static_int_base+a].cint /* an integer parameter */ +#define int_par(a) zeqtb[static_int_base+a].cint /* an integer parameter */ #define cat_code_table int_par(cat_code_table_code) #define tracing_nesting int_par(tracing_nesting_code) #define end_line_char int_par(end_line_char_code) @@ -26,7 +26,7 @@ #define every_eof get_every_eof() #define number_active_chars 65536+65536 -#define active_base 1 /* beginning of region 1, for active character equivalents */ +#define active_base 1 /* beginning of region 1, for active character equivalents */ #define null_cs active_base+number_active_chars /* equivalent of \.{\\csname\\endcsname} */ @@ -40,18 +40,18 @@ #define nonstop_mode 1 /* command codes */ -#define relax 0 +#define relax 0 #define out_param 5 -#define max_command 117 /* fetched from C output */ -#define dont_expand 133 /* fetched from C output */ +#define max_command 117 /* fetched from C output */ +#define dont_expand 133 /* fetched from C output */ -#define terminal_input (name==0) /* are we reading from the terminal? */ -#define special_char 1114113 /* |biggest_char+2| */ -#define cur_file input_file[index] /* the current |alpha_file| variable */ +#define terminal_input (name==0) /* are we reading from the terminal? */ +#define special_char 1114113 /* |biggest_char+2| */ +#define cur_file input_file[index] /* the current |alpha_file| variable */ -#define no_expand_flag special_char /*this characterizes a special variant of |relax|*/ +#define no_expand_flag special_char /*this characterizes a special variant of |relax| */ -extern void insert_vj_template (void); +extern void insert_vj_template(void); #define do_get_cat_code(a) do { \ if (local_catcode_table) \ @@ -61,219 +61,223 @@ extern void insert_vj_template (void); } while (0) -static void -invalid_character_error (void) { - char *hlp[] = {"A funny symbol that I can't read has just been input.", - "Continue, and I'll forget that it ever happened.", - NULL}; - deletions_allowed=false; - tex_error("Text line contains an invalid character", hlp); - deletions_allowed=true; +static void invalid_character_error(void) +{ + char *hlp[] = { "A funny symbol that I can't read has just been input.", + "Continue, and I'll forget that it ever happened.", + NULL + }; + deletions_allowed = false; + tex_error("Text line contains an invalid character", hlp); + deletions_allowed = true; } /* no longer done */ -static boolean process_sup_mark(void); /* below */ +static boolean process_sup_mark(void); /* below */ static int scan_control_sequence(void); /* below */ -typedef enum {next_line_ok, next_line_return, next_line_restart } next_line_retval ; +typedef enum { next_line_ok, next_line_return, + next_line_restart } next_line_retval; -static next_line_retval next_line(void); /* below */ +static next_line_retval next_line(void); /* below */ /* @^inner loop@>*/ -static void -utf_error (void) { - char *hlp[] = {"A funny symbol that I can't read has just been input.", - "Just continue, I'll change it to 0xFFFD.", - NULL }; - deletions_allowed=false; - tex_error("Text line contains an invalid utf-8 sequence", hlp); - deletions_allowed=true; +static void utf_error(void) +{ + char *hlp[] = { "A funny symbol that I can't read has just been input.", + "Just continue, I'll change it to 0xFFFD.", + NULL + }; + deletions_allowed = false; + tex_error("Text line contains an invalid utf-8 sequence", hlp); + deletions_allowed = true; } -static integer -qbuffer_to_unichar (integer *k) { - register int ch; - int val = 0xFFFD; - unsigned char *text = buffer+*k; - if ( (ch = *text++)<0x80 ) { - val = ch; - *k+=1; - } else if ( ch<=0xbf ) { /* error */ - *k+=1; - } else if ( ch<=0xdf ) { - if ( *text>=0x80 && *text<0xc0 ) - val = ((ch&0x1f)<<6) | (*text++&0x3f); - *k+=2; - } else if ( ch<=0xef ) { - if ( *text>=0x80 && *text<0xc0 && text[1]>=0x80 && text[1]<0xc0 ) { - val = ((ch&0xf)<<12) | ((text[0]&0x3f)<<6) | (text[1]&0x3f); - *k+=3; +static integer qbuffer_to_unichar(integer * k) +{ + register int ch; + int val = 0xFFFD; + unsigned char *text = buffer + *k; + if ((ch = *text++) < 0x80) { + val = ch; + *k += 1; + } else if (ch <= 0xbf) { /* error */ + *k += 1; + } else if (ch <= 0xdf) { + if (*text >= 0x80 && *text < 0xc0) + val = ((ch & 0x1f) << 6) | (*text++ & 0x3f); + *k += 2; + } else if (ch <= 0xef) { + if (*text >= 0x80 && *text < 0xc0 && text[1] >= 0x80 && text[1] < 0xc0) { + val = + ((ch & 0xf) << 12) | ((text[0] & 0x3f) << 6) | (text[1] & 0x3f); + *k += 3; + } + } else { + int w = (((ch & 0x7) << 2) | ((text[0] & 0x30) >> 4)) - 1, w2; + w = (w << 6) | ((text[0] & 0xf) << 2) | ((text[1] & 0x30) >> 4); + w2 = ((text[1] & 0xf) << 6) | (text[2] & 0x3f); + val = w * 0x400 + w2 + 0x10000; + if (*text < 0x80 || text[1] < 0x80 || text[2] < 0x80 || + *text >= 0xc0 || text[1] >= 0xc0 || text[2] >= 0xc0) + val = 0xFFFD; + *k += 4; } - } else { - int w = ( ((ch&0x7)<<2) | ((text[0]&0x30)>>4) )-1, w2; - w = (w<<6) | ((text[0]&0xf)<<2) | ((text[1]&0x30)>>4); - w2 = ((text[1]&0xf)<<6) | (text[2]&0x3f); - val = w*0x400 + w2 + 0x10000; - if ( *text<0x80 || text[1]<0x80 || text[2]<0x80 || - *text>=0xc0 || text[1]>=0xc0 || text[2]>=0xc0 ) - val = 0xFFFD; - *k+=4; - } - if (val==0xFFFD) - utf_error(); - return( val ); + if (val == 0xFFFD) + utf_error(); + return (val); } -static boolean -get_next_file (void) { - SWITCH: - if (loc<=limit) { /* current line not yet finished */ - cur_chr = qbuffer_to_unichar(&loc); - RESWITCH: - if (detokenized_line) { - cur_cmd = (cur_chr==' ' ? 10 : 12) ; +static boolean get_next_file(void) +{ + SWITCH: + if (loc <= limit) { /* current line not yet finished */ + cur_chr = qbuffer_to_unichar(&loc); + RESWITCH: + if (detokenized_line) { + cur_cmd = (cur_chr == ' ' ? 10 : 12); + } else { + do_get_cat_code(cur_cmd); + } + /* + @<Change state if necessary, and |goto switch| if the current + character should be ignored, or |goto reswitch| if the current + character changes to another@>; + */ + /* The following 48-way switch accomplishes the scanning quickly, assuming + that a decent \PASCAL\ compiler has translated the code. Note that the numeric + values for |mid_line|, |skip_blanks|, and |new_line| are spaced + apart from each other by |max_char_code+1|, so we can add a character's + command code to the state to get a single number that characterizes both. + */ + switch (state + cur_cmd) { + case mid_line + ignore: + case skip_blanks + ignore: + case new_line + ignore: + case skip_blanks + spacer: + case new_line + spacer: /* @<Cases where character is ignored@> */ + goto SWITCH; + break; + case mid_line + escape: + case new_line + escape: + case skip_blanks + escape: /* @<Scan a control sequence ...@>; */ + state = scan_control_sequence(); + break; + case mid_line + active_char: + case new_line + active_char: + case skip_blanks + active_char: /* @<Process an active-character */ + cur_cs = cur_chr + active_base; + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + state = mid_line; + break; + case mid_line + sup_mark: + case new_line + sup_mark: + case skip_blanks + sup_mark: /* @<If this |sup_mark| starts */ + if (process_sup_mark()) + goto RESWITCH; + else + state = mid_line; + break; + case mid_line + invalid_char: + case new_line + invalid_char: + case skip_blanks + invalid_char: /* @<Decry the invalid character and |goto restart|@>; */ + invalid_character_error(); + return false; /* because state may be token_list now */ + break; + case mid_line + spacer: /* @<Enter |skip_blanks| state, emit a space@>; */ + state = skip_blanks; + cur_chr = ' '; + break; + case mid_line + car_ret: /* @<Finish line, emit a space@>; */ + /* When a character of type |spacer| gets through, its character code is + changed to $\.{"\ "}=@'40$. This means that the ASCII codes for tab and space, + and for the space inserted at the end of a line, will + be treated alike when macro parameters are being matched. We do this + since such characters are indistinguishable on most computer terminal displays. + */ + loc = limit + 1; + cur_cmd = spacer; + cur_chr = ' '; + break; + case skip_blanks + car_ret: + case mid_line + comment: + case new_line + comment: + case skip_blanks + comment: /* @<Finish line, |goto switch|@>; */ + loc = limit + 1; + goto SWITCH; + break; + case new_line + car_ret: /* @<Finish line, emit a \.{\\par}@>; */ + loc = limit + 1; + cur_cs = par_loc; + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + break; + case skip_blanks + left_brace: + case new_line + left_brace: + state = mid_line; /* fall through */ + case mid_line + left_brace: + align_state++; + break; + case skip_blanks + right_brace: + case new_line + right_brace: + state = mid_line; /* fall through */ + case mid_line + right_brace: + align_state--; + break; + case mid_line + math_shift: + case mid_line + tab_mark: + case mid_line + mac_param: + case mid_line + sub_mark: + case mid_line + letter: + case mid_line + other_char: + break; + default: + /* + case skip_blanks + math_shift: + case skip_blanks + tab_mark: + case skip_blanks + mac_param: + case skip_blanks + sub_mark: + case skip_blanks + letter: + case skip_blanks + other_char: + case new_line + math_shift: + case new_line + tab_mark: + case new_line + mac_param: + case new_line + sub_mark: + case new_line + letter: + case new_line + other_char: + */ + state = mid_line; + break; + } } else { - do_get_cat_code(cur_cmd); + if (current_ocp_lstack > 0) { + pop_input(); + return false; + } + if (name != 21) + state = new_line; + + /* + @<Move to next line of file, + or |goto restart| if there is no next line, + or |return| if a \.{\\read} line has finished@>; + */ + do { + next_line_retval r = next_line(); + if (r == next_line_return) { + return true; + } else if (r == next_line_restart) { + return false; + } + } while (0); + check_interrupt(); + goto SWITCH; } - /* - @<Change state if necessary, and |goto switch| if the current - character should be ignored, or |goto reswitch| if the current - character changes to another@>; - */ - /* The following 48-way switch accomplishes the scanning quickly, assuming - that a decent \PASCAL\ compiler has translated the code. Note that the numeric - values for |mid_line|, |skip_blanks|, and |new_line| are spaced - apart from each other by |max_char_code+1|, so we can add a character's - command code to the state to get a single number that characterizes both. - */ - switch(state+cur_cmd) { - case mid_line + ignore: - case skip_blanks + ignore: - case new_line + ignore: - case skip_blanks + spacer: - case new_line + spacer: /* @<Cases where character is ignored@> */ - goto SWITCH; - break; - case mid_line + escape: - case new_line + escape: - case skip_blanks + escape: /* @<Scan a control sequence ...@>;*/ - state = scan_control_sequence(); - break; - case mid_line + active_char: - case new_line + active_char: - case skip_blanks + active_char: /* @<Process an active-character */ - cur_cs=cur_chr+active_base; - cur_cmd=eq_type(cur_cs); - cur_chr=equiv(cur_cs); - state=mid_line; - break; - case mid_line + sup_mark: - case new_line + sup_mark: - case skip_blanks + sup_mark: /* @<If this |sup_mark| starts */ - if (process_sup_mark()) - goto RESWITCH; - else - state=mid_line; - break; - case mid_line + invalid_char: - case new_line + invalid_char: - case skip_blanks + invalid_char: /* @<Decry the invalid character and |goto restart|@>;*/ - invalid_character_error(); - return false; /* because state may be token_list now */ - break; - case mid_line + spacer: /* @<Enter |skip_blanks| state, emit a space@>;*/ - state=skip_blanks; - cur_chr=' '; - break; - case mid_line + car_ret: /* @<Finish line, emit a space@>;*/ - /* When a character of type |spacer| gets through, its character code is - changed to $\.{"\ "}=@'40$. This means that the ASCII codes for tab and space, - and for the space inserted at the end of a line, will - be treated alike when macro parameters are being matched. We do this - since such characters are indistinguishable on most computer terminal displays. - */ - loc=limit+1; - cur_cmd=spacer; - cur_chr=' '; - break; - case skip_blanks + car_ret: - case mid_line + comment: - case new_line + comment: - case skip_blanks + comment: /* @<Finish line, |goto switch|@>;*/ - loc=limit+1; - goto SWITCH; - break; - case new_line + car_ret: /* @<Finish line, emit a \.{\\par}@>;*/ - loc=limit+1; - cur_cs=par_loc; - cur_cmd=eq_type(cur_cs); - cur_chr=equiv(cur_cs); - break; - case skip_blanks + left_brace: - case new_line + left_brace: - state=mid_line; /* fall through */ - case mid_line + left_brace: - align_state++; - break; - case skip_blanks + right_brace: - case new_line + right_brace: - state=mid_line; /* fall through */ - case mid_line + right_brace: - align_state--; - break; - case mid_line + math_shift: - case mid_line + tab_mark: - case mid_line + mac_param: - case mid_line + sub_mark: - case mid_line + letter: - case mid_line + other_char: - break; - default: - /* - case skip_blanks + math_shift: - case skip_blanks + tab_mark: - case skip_blanks + mac_param: - case skip_blanks + sub_mark: - case skip_blanks + letter: - case skip_blanks + other_char: - case new_line + math_shift: - case new_line + tab_mark: - case new_line + mac_param: - case new_line + sub_mark: - case new_line + letter: - case new_line + other_char: - */ - state=mid_line; - break; - } - } else { - if (current_ocp_lstack>0) { - pop_input(); - return false; - } - if (name!=21) - state=new_line; - - /* - @<Move to next line of file, - or |goto restart| if there is no next line, - or |return| if a \.{\\read} line has finished@>; - */ - do { - next_line_retval r = next_line(); - if (r==next_line_return) { - return true; - } else if (r==next_line_restart) { - return false; - } - } while (0); - check_interrupt(); - goto SWITCH; - } - return true; + return true; } #define is_hex(a) ((a>='0'&&a<='9')||(a>='a'&&a<='f')) @@ -307,71 +311,75 @@ get_next_file (void) { /* Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit.*/ -static boolean -process_sup_mark(void) { - if (cur_chr==buffer[loc]) { - int c,cc; - if (loc<limit) { - if ((cur_chr==buffer[loc+1]) && (cur_chr==buffer[loc+2]) - && (cur_chr==buffer[loc+3]) && (cur_chr==buffer[loc+4]) - && ((loc+10)<=limit)) { - int ccc,cccc,ccccc,cccccc; /* constituents of a possible expanded code */ - c=buffer[loc+5]; - cc=buffer[loc+6]; - ccc=buffer[loc+7]; - cccc=buffer[loc+8]; - ccccc=buffer[loc+9]; - cccccc=buffer[loc+10]; - if ((is_hex(c)) && (is_hex(cc)) && (is_hex(ccc)) && (is_hex(cccc)) - && (is_hex(ccccc)) && (is_hex(cccccc))) { - loc=loc+11; - six_hex_to_cur_chr; - return true; - } - } - if ((cur_chr==buffer[loc+1]) && (cur_chr==buffer[loc+2]) - && (cur_chr==buffer[loc+3]) && ((loc+8)<=limit)) { - int ccc,cccc,ccccc; /* constituents of a possible expanded code */ - c=buffer[loc+4]; - cc=buffer[loc+5]; - ccc=buffer[loc+6]; - cccc=buffer[loc+7]; - ccccc=buffer[loc+8]; - if ((is_hex(c)) && (is_hex(cc)) && (is_hex(ccc)) && (is_hex(cccc)) && (is_hex(ccccc))) { - loc=loc+9; - five_hex_to_cur_chr; - return true; - } - } - if ((cur_chr==buffer[loc+1]) && (cur_chr==buffer[loc+2]) && ((loc+6)<=limit)) { - int ccc,cccc; /* constituents of a possible expanded code */ - c=buffer[loc+3]; - cc=buffer[loc+4]; - ccc=buffer[loc+5]; - cccc=buffer[loc+6]; - if ((is_hex(c)) && (is_hex(cc)) && (is_hex(ccc)) && (is_hex(cccc))) { - loc=loc+7; - four_hex_to_cur_chr; - return true; - } - } - c = buffer[loc+1]; - if (c<0200) { /* yes we have an expanded char */ - loc=loc+2; - if (is_hex(c) && loc<=limit) { - cc=buffer[loc]; - if (is_hex(cc)){ - incr(loc); - hex_to_cur_chr; - return true; - } +static boolean process_sup_mark(void) +{ + if (cur_chr == buffer[loc]) { + int c, cc; + if (loc < limit) { + if ((cur_chr == buffer[loc + 1]) && (cur_chr == buffer[loc + 2]) + && (cur_chr == buffer[loc + 3]) && (cur_chr == buffer[loc + 4]) + && ((loc + 10) <= limit)) { + int ccc, cccc, ccccc, cccccc; /* constituents of a possible expanded code */ + c = buffer[loc + 5]; + cc = buffer[loc + 6]; + ccc = buffer[loc + 7]; + cccc = buffer[loc + 8]; + ccccc = buffer[loc + 9]; + cccccc = buffer[loc + 10]; + if ((is_hex(c)) && (is_hex(cc)) && (is_hex(ccc)) + && (is_hex(cccc)) + && (is_hex(ccccc)) && (is_hex(cccccc))) { + loc = loc + 11; + six_hex_to_cur_chr; + return true; + } + } + if ((cur_chr == buffer[loc + 1]) && (cur_chr == buffer[loc + 2]) + && (cur_chr == buffer[loc + 3]) && ((loc + 8) <= limit)) { + int ccc, cccc, ccccc; /* constituents of a possible expanded code */ + c = buffer[loc + 4]; + cc = buffer[loc + 5]; + ccc = buffer[loc + 6]; + cccc = buffer[loc + 7]; + ccccc = buffer[loc + 8]; + if ((is_hex(c)) && (is_hex(cc)) && (is_hex(ccc)) + && (is_hex(cccc)) && (is_hex(ccccc))) { + loc = loc + 9; + five_hex_to_cur_chr; + return true; + } + } + if ((cur_chr == buffer[loc + 1]) && (cur_chr == buffer[loc + 2]) + && ((loc + 6) <= limit)) { + int ccc, cccc; /* constituents of a possible expanded code */ + c = buffer[loc + 3]; + cc = buffer[loc + 4]; + ccc = buffer[loc + 5]; + cccc = buffer[loc + 6]; + if ((is_hex(c)) && (is_hex(cc)) && (is_hex(ccc)) + && (is_hex(cccc))) { + loc = loc + 7; + four_hex_to_cur_chr; + return true; + } + } + c = buffer[loc + 1]; + if (c < 0200) { /* yes we have an expanded char */ + loc = loc + 2; + if (is_hex(c) && loc <= limit) { + cc = buffer[loc]; + if (is_hex(cc)) { + incr(loc); + hex_to_cur_chr; + return true; + } + } + cur_chr = (c < 0100 ? c + 0100 : c - 0100); + return true; + } } - cur_chr = (c<0100 ? c+0100 : c-0100); - return true; - } } - } - return false; + return false; } /* Control sequence names are scanned only when they appear in some line of @@ -390,46 +398,50 @@ process_sup_mark(void) { buffer and the process is repeated, slowly but surely. */ -static boolean check_expanded_code (integer *kk) ; /* below */ - -static int scan_control_sequence (void ) { - int retval = mid_line; - if (loc>limit) { - cur_cs=null_cs; /* |state| is irrelevant in this case */ - } else { - register int cat; /* |cat_code(cur_chr)|, usually */ - while (1) { - integer k=loc; - cur_chr = qbuffer_to_unichar(&k); - do_get_cat_code(cat); - if (cat!=letter||k>limit) { - retval = (cat==spacer ? skip_blanks : mid_line); - if (cat==sup_mark && check_expanded_code(&k)) /* @<If an expanded...@>; */ - continue; - } else { - retval=skip_blanks; - do { - cur_chr = qbuffer_to_unichar(&k); - do_get_cat_code(cat); - } while (cat==letter&&k<=limit); - - if (cat==sup_mark && check_expanded_code(&k)) /* @<If an expanded...@>; */ - continue; - if (cat!=letter) { - decr(k); - if (cur_chr>0xFFFF) decr(k); - if (cur_chr>0x7FF) decr(k); - if (cur_chr>0x7F) decr(k); - } /* now |k| points to first nonletter */ - } - cur_cs=id_lookup(loc,k-loc); - loc=k; - break; +static boolean check_expanded_code(integer * kk); /* below */ + +static int scan_control_sequence(void) +{ + int retval = mid_line; + if (loc > limit) { + cur_cs = null_cs; /* |state| is irrelevant in this case */ + } else { + register int cat; /* |cat_code(cur_chr)|, usually */ + while (1) { + integer k = loc; + cur_chr = qbuffer_to_unichar(&k); + do_get_cat_code(cat); + if (cat != letter || k > limit) { + retval = (cat == spacer ? skip_blanks : mid_line); + if (cat == sup_mark && check_expanded_code(&k)) /* @<If an expanded...@>; */ + continue; + } else { + retval = skip_blanks; + do { + cur_chr = qbuffer_to_unichar(&k); + do_get_cat_code(cat); + } while (cat == letter && k <= limit); + + if (cat == sup_mark && check_expanded_code(&k)) /* @<If an expanded...@>; */ + continue; + if (cat != letter) { + decr(k); + if (cur_chr > 0xFFFF) + decr(k); + if (cur_chr > 0x7FF) + decr(k); + if (cur_chr > 0x7F) + decr(k); + } /* now |k| points to first nonletter */ + } + cur_cs = id_lookup(loc, k - loc); + loc = k; + break; + } } - } - cur_cmd=eq_type(cur_cs); - cur_chr=equiv(cur_cs); - return retval; + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + return retval; } /* Whenever we reach the following piece of code, we will have @@ -440,89 +452,97 @@ static int scan_control_sequence (void ) { the buffer left two or three places. */ -static boolean -check_expanded_code (integer *kk) { - int l; - int k = *kk; - int d =1; /* number of excess characters in an expanded code */ - int c,cc,ccc,cccc,ccccc,cccccc; /* constituents of a possible expanded code */ - if (buffer[k]==cur_chr && k<limit) { - if ((cur_chr==buffer[k+1]) && (cur_chr==buffer[k+2]) && ((k+6)<=limit)) { - d = 4; - if ((cur_chr==buffer[k+3]) && ((k+8)<=limit)) - d = 5; - if ((cur_chr==buffer[k+4]) && ((k+10)<=limit)) - d = 6; - c = buffer[k+d-1]; - cc = buffer[k+d]; - ccc = buffer[k+d+1]; - cccc= buffer[k+d+2]; - if (d==6) { - ccccc =buffer[k+d+3]; - cccccc=buffer[k+d+4]; - if (is_hex(c) && is_hex(cc) && is_hex(ccc) && is_hex(cccc) - && is_hex(ccccc) && is_hex(cccccc)) - six_hex_to_cur_chr; - } else if (d==5) { - ccccc=buffer[k+d+3]; - if (is_hex(c) && is_hex(cc) && is_hex(ccc) && is_hex(cccc) && is_hex(ccccc)) - five_hex_to_cur_chr; - } else { - if (is_hex(c) && is_hex(cc) && is_hex(ccc) && is_hex(cccc)) - four_hex_to_cur_chr; - } - } else { - c=buffer[k+1]; - if (c<0200) { - d=1; - if (is_hex(c) && (k+2)<=limit) { - cc=buffer[k+2]; - if (is_hex(c) && is_hex(cc)) { - d=2; - hex_to_cur_chr; - } - } else if (c<0100) { - cur_chr=c+0100; - } else { - cur_chr=c-0100; - } - } - } - if (d>2) - d=2*d-1; - else - d++; - if (cur_chr <= 0x7F) { - buffer[k-1]= cur_chr; - } else if (cur_chr<=0x7FF) { - buffer[k-1]=0xC0 + cur_chr / 0x40; - k++; d--; - buffer[k-1]=0x80 + cur_chr % 0x40; - } else if (cur_chr<=0xFFFF){ - buffer[k-1]=0xE0 + cur_chr / 0x1000; - k++; d--; - buffer[k-1]=0x80 + (cur_chr % 0x1000) / 0x40; - k++; d--; - buffer[k-1]=0x80 + (cur_chr % 0x1000) % 0x40; - } else { - buffer[k-1]=0xF0 + cur_chr / 0x40000; - k++; d--; - buffer[k-1]=0x80 + (cur_chr % 0x40000) / 0x1000; - k++; d--; - buffer[k-1]=0x80 + ((cur_chr % 0x40000) % 0x1000) / 0x40; - k++; d--; - buffer[k-1]=0x80 + ((cur_chr % 0x40000) % 0x1000) % 0x40; - } - l = k; - limit = limit - d; - while (l<=limit) { - buffer[l]=buffer[l+d]; - l++; +static boolean check_expanded_code(integer * kk) +{ + int l; + int k = *kk; + int d = 1; /* number of excess characters in an expanded code */ + int c, cc, ccc, cccc, ccccc, cccccc; /* constituents of a possible expanded code */ + if (buffer[k] == cur_chr && k < limit) { + if ((cur_chr == buffer[k + 1]) && (cur_chr == buffer[k + 2]) + && ((k + 6) <= limit)) { + d = 4; + if ((cur_chr == buffer[k + 3]) && ((k + 8) <= limit)) + d = 5; + if ((cur_chr == buffer[k + 4]) && ((k + 10) <= limit)) + d = 6; + c = buffer[k + d - 1]; + cc = buffer[k + d]; + ccc = buffer[k + d + 1]; + cccc = buffer[k + d + 2]; + if (d == 6) { + ccccc = buffer[k + d + 3]; + cccccc = buffer[k + d + 4]; + if (is_hex(c) && is_hex(cc) && is_hex(ccc) && is_hex(cccc) + && is_hex(ccccc) && is_hex(cccccc)) + six_hex_to_cur_chr; + } else if (d == 5) { + ccccc = buffer[k + d + 3]; + if (is_hex(c) && is_hex(cc) && is_hex(ccc) && is_hex(cccc) + && is_hex(ccccc)) + five_hex_to_cur_chr; + } else { + if (is_hex(c) && is_hex(cc) && is_hex(ccc) && is_hex(cccc)) + four_hex_to_cur_chr; + } + } else { + c = buffer[k + 1]; + if (c < 0200) { + d = 1; + if (is_hex(c) && (k + 2) <= limit) { + cc = buffer[k + 2]; + if (is_hex(c) && is_hex(cc)) { + d = 2; + hex_to_cur_chr; + } + } else if (c < 0100) { + cur_chr = c + 0100; + } else { + cur_chr = c - 0100; + } + } + } + if (d > 2) + d = 2 * d - 1; + else + d++; + if (cur_chr <= 0x7F) { + buffer[k - 1] = cur_chr; + } else if (cur_chr <= 0x7FF) { + buffer[k - 1] = 0xC0 + cur_chr / 0x40; + k++; + d--; + buffer[k - 1] = 0x80 + cur_chr % 0x40; + } else if (cur_chr <= 0xFFFF) { + buffer[k - 1] = 0xE0 + cur_chr / 0x1000; + k++; + d--; + buffer[k - 1] = 0x80 + (cur_chr % 0x1000) / 0x40; + k++; + d--; + buffer[k - 1] = 0x80 + (cur_chr % 0x1000) % 0x40; + } else { + buffer[k - 1] = 0xF0 + cur_chr / 0x40000; + k++; + d--; + buffer[k - 1] = 0x80 + (cur_chr % 0x40000) / 0x1000; + k++; + d--; + buffer[k - 1] = 0x80 + ((cur_chr % 0x40000) % 0x1000) / 0x40; + k++; + d--; + buffer[k - 1] = 0x80 + ((cur_chr % 0x40000) % 0x1000) % 0x40; + } + l = k; + limit = limit - d; + while (l <= limit) { + buffer[l] = buffer[l + d]; + l++; + } + *kk = k; + return true; } - *kk =k; - return true; - } - return false; + return false; } #define end_line_char_inactive ((end_line_char<0)||(end_line_char>127)) @@ -540,122 +560,126 @@ check_expanded_code (integer *kk) { There is one more branch. */ -static next_line_retval -next_line (void) { - integer tab; /* a category table */ - boolean inhibit_eol = false; /* a way to end a pseudo file without trailing space */ - detokenized_line=false; - local_catcode_table=false; - if (name>17) { - /* @<Read next line of file into |buffer|, or |goto restart| if the file has ended@> */ - incr(line); - first=start; - if (!force_eof) { - if (name<=20) { - if (pseudo_input()) { /* not end of file */ - firm_up_the_line(); /* this sets |limit| */ - if ((name==19)&&(pseudo_lines(pseudo_files)==null)) - inhibit_eol=true; - } else if ((every_eof!=null)&&!eof_seen[index]) { - limit=first-1; eof_seen[index]=true; /* fake one empty line */ - if (name!=19) - begin_token_list(every_eof,every_eof_text); - return next_line_restart; - } else { - force_eof=true; - } - } else { - if (name==21) { - if (luacstring_input()) { /* not end of strings */ - firm_up_the_line(); - if ((luacstring_penultimate())||(luacstring_simple())) - inhibit_eol=true; - if (!luacstring_simple()) - state=new_line; - if (luacstring_detokenized()) { - inhibit_eol=true; - detokenized_line=true; - } else { - if (! luacstring_defaultcattable()) { - tab=luacstring_cattable(); - if (valid_catcode_table(tab)) { - local_catcode_table=true; - line_catcode_table=tab; - } - } - } - } else { - force_eof=true; - } - } else { - if (lua_input_ln(cur_file,0,true)) { /* not end of file */ - firm_up_the_line(); /* this sets |limit| */ - } else if ((every_eof!=null)&& (!eof_seen[index])) { - limit=first-1; eof_seen[index]=true; /* fake one empty line */ - begin_token_list(every_eof,every_eof_text); - return next_line_restart; - } else { - force_eof=true; - } - } - } - } - if (force_eof) { - if (tracing_nesting>0) - if ((grp_stack[in_open]!=cur_boundary)||(if_stack[in_open]!=cond_ptr)) - if (!((name==19)||(name=21))) - file_warning(); /* give warning for some unfinished groups and/or conditionals */ - if ((name>21)||(name==20)) { - if (tracefilenames) - print_char(')'); - open_parens--; - /* update_terminal();*/ /* show user that file has been read */ - } - force_eof=false; - end_file_reading(); - return next_line_restart; - } - if (inhibit_eol||end_line_char_inactive) - limit--; - else - buffer[limit]=end_line_char; - first=limit+1; - loc=start; /* ready to read */ - } else { - if (!terminal_input) { /* \.{\\read} line has ended */ - cur_cmd=0; - cur_chr=0; - return next_line_return; /* OUTER */ - } - if (input_ptr>0) { /* text was inserted during error recovery */ - end_file_reading(); - return next_line_restart; /* resume previous level */ - } - if (selector<log_only) - open_log_file(); - if (interaction>nonstop_mode) { - if (end_line_char_inactive) - limit++; - if (limit==start) { /* previous line was empty */ - tprint_nl("(Please type a command or say `\\end')"); - } - print_ln(); - first=start; - prompt_input((str_number)'*'); /* input on-line into |buffer| */ - limit=last; - if (end_line_char_inactive) - limit--; - else - buffer[limit]=end_line_char; - first=limit+1; - loc=start; +static next_line_retval next_line(void) +{ + integer tab; /* a category table */ + boolean inhibit_eol = false; /* a way to end a pseudo file without trailing space */ + detokenized_line = false; + local_catcode_table = false; + if (name > 17) { + /* @<Read next line of file into |buffer|, or |goto restart| if the file has ended@> */ + incr(line); + first = start; + if (!force_eof) { + if (name <= 20) { + if (pseudo_input()) { /* not end of file */ + firm_up_the_line(); /* this sets |limit| */ + if ((name == 19) && (pseudo_lines(pseudo_files) == null)) + inhibit_eol = true; + } else if ((every_eof != null) && !eof_seen[index]) { + limit = first - 1; + eof_seen[index] = true; /* fake one empty line */ + if (name != 19) + begin_token_list(every_eof, every_eof_text); + return next_line_restart; + } else { + force_eof = true; + } + } else { + if (name == 21) { + if (luacstring_input()) { /* not end of strings */ + firm_up_the_line(); + if ((luacstring_penultimate()) || (luacstring_simple())) + inhibit_eol = true; + if (!luacstring_simple()) + state = new_line; + if (luacstring_detokenized()) { + inhibit_eol = true; + detokenized_line = true; + } else { + if (!luacstring_defaultcattable()) { + tab = luacstring_cattable(); + if (valid_catcode_table(tab)) { + local_catcode_table = true; + line_catcode_table = tab; + } + } + } + } else { + force_eof = true; + } + } else { + if (lua_input_ln(cur_file, 0, true)) { /* not end of file */ + firm_up_the_line(); /* this sets |limit| */ + } else if ((every_eof != null) && (!eof_seen[index])) { + limit = first - 1; + eof_seen[index] = true; /* fake one empty line */ + begin_token_list(every_eof, every_eof_text); + return next_line_restart; + } else { + force_eof = true; + } + } + } + } + if (force_eof) { + if (tracing_nesting > 0) + if ((grp_stack[in_open] != cur_boundary) + || (if_stack[in_open] != cond_ptr)) + if (!((name == 19) || (name = 21))) + file_warning(); /* give warning for some unfinished groups and/or conditionals */ + if ((name > 21) || (name == 20)) { + if (tracefilenames) + print_char(')'); + open_parens--; + /* update_terminal(); *//* show user that file has been read */ + } + force_eof = false; + end_file_reading(); + return next_line_restart; + } + if (inhibit_eol || end_line_char_inactive) + limit--; + else + buffer[limit] = end_line_char; + first = limit + 1; + loc = start; /* ready to read */ } else { - fatal_error(maketexstring("*** (job aborted, no legal \\end found)")); - /* nonstop mode, which is intended for overnight batch processing, - never waits for on-line input */ + if (!terminal_input) { /* \.{\\read} line has ended */ + cur_cmd = 0; + cur_chr = 0; + return next_line_return; /* OUTER */ + } + if (input_ptr > 0) { /* text was inserted during error recovery */ + end_file_reading(); + return next_line_restart; /* resume previous level */ + } + if (selector < log_only) + open_log_file(); + if (interaction > nonstop_mode) { + if (end_line_char_inactive) + limit++; + if (limit == start) { /* previous line was empty */ + tprint_nl("(Please type a command or say `\\end')"); + } + print_ln(); + first = start; + prompt_input((str_number) '*'); /* input on-line into |buffer| */ + limit = last; + if (end_line_char_inactive) + limit--; + else + buffer[limit] = end_line_char; + first = limit + 1; + loc = start; + } else { + fatal_error(maketexstring + ("*** (job aborted, no legal \\end found)")); + /* nonstop mode, which is intended for overnight batch processing, + never waits for on-line input */ + } } - } - return next_line_ok; + return next_line_ok; } @@ -663,46 +687,46 @@ next_line (void) { /* Let's consider now what happens when |get_next| is looking at a token list. */ -static boolean -get_next_tokenlist (void) { - register halfword t; /* a token*/ - t=info(loc); - loc=link(loc); /* move to next */ - if (t>=cs_token_flag) { /* a control sequence token */ - cur_cs=t-cs_token_flag; - cur_cmd=eq_type(cur_cs); - if (cur_cmd==dont_expand) { /* @<Get the next token, suppressing expansion@>*/ - /* The present point in the program is reached only when the |expand| - routine has inserted a special marker into the input. In this special - case, |info(loc)| is known to be a control sequence token, and |link(loc)=null|. - */ - cur_cs=info(loc)-cs_token_flag; - loc=null; - cur_cmd=eq_type(cur_cs); - if (cur_cmd>max_command) { - cur_cmd=relax; - cur_chr=no_expand_flag; - return true; - } - } - cur_chr=equiv(cur_cs); - } else { - cur_cmd=t >> string_offset_bits; /* cur_cmd=t / string_offset; */ - cur_chr=t & (string_offset-1); /* cur_chr=t % string_offset; */ - switch (cur_cmd) { - case left_brace: - align_state++; - break; - case right_brace: - align_state--; - break; - case out_param: /* @<Insert macro parameter and |goto restart|@>; */ - begin_token_list(param_stack[param_start+cur_chr-1],parameter); - return false; - break; +static boolean get_next_tokenlist(void) +{ + register halfword t; /* a token */ + t = info(loc); + loc = link(loc); /* move to next */ + if (t >= cs_token_flag) { /* a control sequence token */ + cur_cs = t - cs_token_flag; + cur_cmd = eq_type(cur_cs); + if (cur_cmd == dont_expand) { /* @<Get the next token, suppressing expansion@> */ + /* The present point in the program is reached only when the |expand| + routine has inserted a special marker into the input. In this special + case, |info(loc)| is known to be a control sequence token, and |link(loc)=null|. + */ + cur_cs = info(loc) - cs_token_flag; + loc = null; + cur_cmd = eq_type(cur_cs); + if (cur_cmd > max_command) { + cur_cmd = relax; + cur_chr = no_expand_flag; + return true; + } + } + cur_chr = equiv(cur_cs); + } else { + cur_cmd = t >> string_offset_bits; /* cur_cmd=t / string_offset; */ + cur_chr = t & (string_offset - 1); /* cur_chr=t % string_offset; */ + switch (cur_cmd) { + case left_brace: + align_state++; + break; + case right_brace: + align_state--; + break; + case out_param: /* @<Insert macro parameter and |goto restart|@>; */ + begin_token_list(param_stack[param_start + cur_chr - 1], parameter); + return false; + break; + } } - } - return true; + return true; } /* Now we're ready to take the plunge into |get_next| itself. Parts of @@ -712,42 +736,42 @@ get_next_tokenlist (void) { /* sets |cur_cmd|, |cur_chr|, |cur_cs| to next token */ -void -get_next (void) { - RESTART: - cur_cs=0; - if (state!=token_list) { - /* Input from external file, |goto restart| if no input found */ - if (!get_next_file()) - goto RESTART; - } else { - if (loc==null) { - end_token_list(); - goto RESTART; /* list exhausted, resume previous level */ - } else if (!get_next_tokenlist()) { - goto RESTART; /* parameter needs to be expanded */ +void get_next(void) +{ + RESTART: + cur_cs = 0; + if (state != token_list) { + /* Input from external file, |goto restart| if no input found */ + if (!get_next_file()) + goto RESTART; + } else { + if (loc == null) { + end_token_list(); + goto RESTART; /* list exhausted, resume previous level */ + } else if (!get_next_tokenlist()) { + goto RESTART; /* parameter needs to be expanded */ + } + } + /* @<If an alignment entry has just ended, take appropriate action@> */ + if ((cur_cmd == tab_mark || cur_cmd == car_ret) && align_state == 0) { + insert_vj_template(); + goto RESTART; } - } - /* @<If an alignment entry has just ended, take appropriate action@> */ - if ((cur_cmd==tab_mark || cur_cmd==car_ret) && align_state==0) { - insert_vj_template(); - goto RESTART; - } } -void -get_token_lua (void) { - register int callback_id ; - callback_id = callback_defined(token_filter_callback); - if (callback_id!=0) { - while (state==token_list && loc==null && index!=v_template) - end_token_list(); - /* there is some stuff we don't want to see inside the callback */ - if (!(state==token_list && - ((nofilter==true) || (index==backed_up && loc!=null)))) { - do_get_token_lua(callback_id); - return; +void get_token_lua(void) +{ + register int callback_id; + callback_id = callback_defined(token_filter_callback); + if (callback_id != 0) { + while (state == token_list && loc == null && index != v_template) + end_token_list(); + /* there is some stuff we don't want to see inside the callback */ + if (!(state == token_list && + ((nofilter == true) || (index == backed_up && loc != null)))) { + do_get_token_lua(callback_id); + return; + } } - } - get_next(); + get_next(); } diff --git a/Master/texmf-dist/doc/luatex/luatexref-env.tex b/Master/texmf-dist/doc/luatex/luatexref-env.tex new file mode 100644 index 00000000000..69ddd7496ca --- /dev/null +++ b/Master/texmf-dist/doc/luatex/luatexref-env.tex @@ -0,0 +1,312 @@ +% engine=luatex + +\startenvironment luatexref-env + +\usemodule[abr-02] + +% \tex : tex command +% \lua : native lua code +% \luatex : lua lib, function, value defined in luatex + +\catcode`\_=12 % we will make a nice attribute for this kind of break stuff + +\setuptolerance[stretch,tolerant] + +\setuptype[lines=hyphenated] \setuptyping[lines=hyphenated] + +\let\lua \type +\let\luatex \type + +\def\|{\string|} +\def\>{\string>} + +\def\showfields#1{\ctxlua + { do + local t = string.split('#1',',') + local r = { } + for _,a in pairs(node.fields(t[1],t[2])) do + if not (a == 'id' or a == 'subtype' or a =='next' or a=='prev') then + table.insert(r,'\\type{'.. a .. '}') + end + end + tex.sprint(table.concat(r, ', ')) + end + }% +} + +\def\todo#1% + {\dontleavehmode{\bf\red <TODO: #1>}} + +\setuplayout + [height=middle, + width=middle, + backspace=2cm, + topspace=2cm] + +\setuppagenumbering + [alternative=doublesided] + +\setupitemize[each][packed] + +\setupwhitespace[medium] + +\preloadtypescripts + +\definetypeface[mainfacenormal] [ss][sans] [iwona] [default] +\definetypeface[mainfacenormal] [rm][serif][palatino] [default] +\definetypeface[mainfacenormal] [tt][mono] [modern] [default][rscale=1.1] +\definetypeface[mainfacenormal] [mm][math] [iwona] [default][encoding=default] + +\definetypeface[mainfacemedium] [ss][sans] [iwona-medium][default] +\definetypeface[mainfacenormal] [rm][serif][palatino] [default] +\definetypeface[mainfacemedium] [tt][mono] [modern] [default][rscale=1.1] +\definetypeface[mainfacemedium] [mm][math] [iwona-medium][default][encoding=default] + +\definetypeface[mainfacenarrowtt][tt][mono] [modern] [default][rscale=1.1] + +\setupbodyfont[mainfacenormal,11pt] + +\definehead + [remark] + [subsubsubject] + +\setuphead [chapter] [style=\mainfacemedium\bfd,color=blue] +\setuphead [section] [style=\mainfacemedium\bfc,color=blue] +\setuphead [subsection] [style=\mainfacemedium\bfb,color=blue] +\setuphead [subsubsection][style=\mainfacemedium\bfa,color=blue] + +\setupheadertexts + [] + +\setupcolors + [state=start] + +\setuptyping + [color=blue, +]% style=\mainfacenarrowtt] + +\definetyping[functioncall] + +\setuptype + [color=blue, +]% style=\mainfacenarrowtt] + +\definecolor[blue] [b=.5] +\definecolor[red] [r=.5] +\definecolor[green][g=.5] +\runMPgraphicstrue +\startuseMPgraphic{lualogo} + color luaplanetcolor ; luaplanetcolor := .5blue ; + color luaholecolor ; luaholecolor := white ; + numeric luaextraangle ; luaextraangle := 0 ; + + vardef lualogo = image ( + % Graphic design by A. Nakonechnyj. Copyright (c) 1998, All rights reserved. + + save luaorbitcolor, d, r, p ; color luaorbitcolor ; numeric d, r, p ; + + luaorbitcolor := .5luaholecolor ; d := sqrt(2)/4 ; r := 1/4 ; p := r/8 ; + + fill fullcircle scaled 1 withcolor luaplanetcolor ; + draw fullcircle rotated 40.5 scaled (1+r) dashed evenly scaled p withpen pencircle scaled (p/2) withcolor luaorbitcolor ; + fill fullcircle scaled r shifted (d+1/8,d+1/8) rotated luaextraangle withcolor luaplanetcolor ; + fill fullcircle scaled r shifted (d-1/8,d-1/8) withcolor luaholecolor ; + ) enddef ; + +\stopuseMPgraphic + +\startuseMPgraphic{luapage} + \includeMPgraphic{lualogo} + + StartPage ; + + fill Page withcolor (white-luaplanetcolor) ; + + picture p ; p := lualogo xsized (3PaperWidth/5) ; + draw p shifted center Page shifted (0,-ypart center ulcorner p) ; + + StopPage ; +\stopuseMPgraphic + +\startuseMPgraphic{luanumber} + \includeMPgraphic{lualogo} +% luaextraangle := \realfolio*360/\lastpage ; + luaextraangle := 10 * ( (\realfolio-2)*36/(\lastpage-2) ) ; + picture p ; p := lualogo ; + setbounds p to boundingbox fullcircle ; + draw p ysized 1cm ; +\stopuseMPgraphic + +\definelayer + [page] + [width=\paperwidth, + height=\paperheight] + +\setupbackgrounds + [leftpage] + [background=page] + +\setupbackgrounds + [rightpage] + [background=page] + +\startsetups pagenumber:right + \setlayerframed + [page] + [preset=rightbottom,offset=1cm] + [frame=off,height=1cm,offset=overlay] + {\useMPgraphic{luanumber}} + \setlayerframed + [page] + [preset=rightbottom,offset=1cm,x=1.5cm] + [frame=off,height=1cm,width=1cm,offset=overlay] + {\pagenumber} + \setlayerframed + [page] + [preset=rightbottom,offset=1cm,x=2.5cm] + [frame=off,height=1cm,offset=overlay] + {\getmarking[chapter]}% Lua\TeX\ Reference Manual} +\stopsetups + +\startsetups pagenumber:left + \setlayerframed + [page] + [preset=leftbottom,offset=1cm,x=2.5cm] + [frame=off,height=1cm,offset=overlay] + {\getmarking[chapter]} + \setlayerframed + [page] + [preset=leftbottom,offset=1cm,x=1.5cm] + [frame=off,height=1cm,width=1cm,offset=overlay] + {\pagenumber} + \setlayerframed + [page] + [preset=leftbottom,offset=1cm] + [frame=off,height=1cm,offset=overlay] + {\useMPgraphic{luanumber}} +\stopsetups + +\startsetups [titlepage] + + \startstandardmakeup + + \switchtobodyfont[mainfacemedium] + \definedfont[Bold at .08\paperheight] \setupinterlinespace + + \setlayer[page]{\useMPgraphic{luapage}} + + \setlayerframed + [page] + [preset=middletop, + voffset=.05\paperheight] + [align=middle, + foregroundcolor=blue, + frame=off] + {Lua\TeX\\Reference} + + \definedfont[Bold at 24pt] \setupinterlinespace + + \setlayerframed + [page] + [preset=middletop, + voffset=.35\paperheight] + [align=middle, + foregroundcolor=blue, + frame=off] + {\doifsomething + {\getvariable{document}{snapshot}} + {snapshot \getvariable{document}{snapshot}}% + \doifsomething + {\getvariable{document}{beta}} + {beta \getvariable{document}{beta}}% + } + + \stopstandardmakeup + + \startstandardmakeup + + \raggedleft + + \definedfont[Bold at 48pt] \setupinterlinespace + + \start \blue Lua\TeX \endgraf Reference \endgraf Manual \stop + + \blank + + \definedfont[Bold at 24pt] \setupinterlinespace + + \blank + + \vfill + + \definedfont[Bold at 12pt] \setupinterlinespace + + copyright: Lua\TeX\ development team \endgraf + more info: www.luatex.org \endgraf + version: \currentdate \doifsomething{\getvariable{document}{snapshot}}{(snapshot \getvariable{document}{snapshot})} \endgraf + + \stopstandardmakeup + + \setupbackgrounds + [leftpage] + [setups=pagenumber:left] + + \setupbackgrounds + [rightpage] + [setups=pagenumber:right] + +\stopsetups + +\def\nonterminal#1>{\mathematics{\langle\hbox{{\rm #1}}\rangle}} + +\ifx\directlua\undefined + + \definetyping[syntax] + \definetype[syntax] + +\else + + % taco's brainwave -) + + \newcatcodetable\syntaxcodetable + + \def\makesyntaxcodetable + {\begingroup + \catcode`\<=13 \catcode`\|=12 + \catcode`\!= 0 \catcode`\\=12 + \savecatcodetable\syntaxcodetable + \endgroup} + + \makesyntaxcodetable + + \def\startsyntax {\begingroup\catcodetable\syntaxcodetable \dostartsyntax} + \def\syntax {\begingroup\catcodetable\syntaxcodetable \dosyntax} + + \def\syntaxenvbody#1% + {\par + \tt + \startnarrower + {\blue #1}% + \stopnarrower + \par} + + \def\syntaxbody#1% + {{\blue \tt #1}} + + \bgroup \catcodetable\syntaxcodetable + + !gdef!dostartsyntax#1\stopsyntax{!let<!nonterminal!syntaxenvbody{#1}!endgroup} + !gdef!dosyntax #1{!let<!nonterminal!syntaxbody{#1}!endgroup} + + !egroup + +\fi + +\setupinteraction[state=start] +\placebookmarks[chapter,section,subsection] + +% Hans doesn't like the bookmarks opening by default +% \setupinteractionscreen[option=bookmark] + +\stopenvironment + diff --git a/Master/texmf-dist/doc/luatex/luatexref-t.pdf b/Master/texmf-dist/doc/luatex/luatexref-t.pdf Binary files differnew file mode 100644 index 00000000000..69a6986e78c --- /dev/null +++ b/Master/texmf-dist/doc/luatex/luatexref-t.pdf diff --git a/Master/texmf-dist/doc/luatex/luatexref-t.tex b/Master/texmf-dist/doc/luatex/luatexref-t.tex new file mode 100644 index 00000000000..b89015a0d56 --- /dev/null +++ b/Master/texmf-dist/doc/luatex/luatexref-t.tex @@ -0,0 +1,6422 @@ +% engine=luatex language=uk + +% TODO: fix layout of function legend descriptions + +\environment luatexref-env + +\def\CFF{\kap{cff}} +\def\PATGEN{\kap{patgen}} +\def\MP{MetaPost} + +\dontcomplain + +\catcode`\_=12 + +\setvariables + [document] + [beta=0.25.3] + +\starttext + +\setups[titlepage] + +\title{Contents} + +\placecontent[criterium=text,level=subsection] + +\chapter{Introduction} + +\startframedtext[framecolor=red,foregroundcolor=red,width=\hsize,font=\tfa] + +This book will eventually become the reference manual of \LUATEX. At +the moment, it simply reports the behavior of the executable +matching the snapshot or beta release date in the title page. + +\blank + +Features may come and go. The current version of \LUATEX\ is not meant +for production and users cannot depend on stability, nor on +functionality staying the same. + +\blank + +Nothing is considered stable just yet. This manual +therefore simply reflects the current state of the executable. {\bs +Absolutely nothing\/} on the following pages is set in stone. When the +need arises, anything can (and will) be changed without prior notice. + +\blank + +\bf If you are not willing to deal with this situation, you should wait +for the stable version. Currently we expect the first release to be +available sometime in the summer of~2008. + +\stopframedtext + +\blank[2*line] + +\LUATEX\ consists of a number of interrelated but (still) distinguishable + parts: + +\startitemize[packed] +\item \PDFTEX\ version 1.40.3 % hahe: still 1.40.3? +\item \ALEPH\ RC4 (from the \TEXLIVE\ repository) +\item \LUA\ 5.1.2 ($+$ coco 1.1.3) % hahe: still 5.1.2? +\item Dedicated \LUA\ libraries +\item Various \TEX\ extensions +\item Parts of \FONTFORGE\ 2007.06.07 +\item Newly written compiled source code to glue it all together +\stopitemize + +Neither \ALEPH's I/O translation processes, nor tcx files, nor +\ENCTEX\ can be used, these encoding|-|related functions are +superseded by a \LUA|-|based solution (reader callbacks). Also, some +experimental \PDFTEX\ features are removed. These can be implemented +in Lua instead. + +\chapter{Basic \TEX\ enhancements} + + +\section{Version information} + +There are three new primitives to test the version of \LUATEX: + +\starttabulate[|l|p|] +\NC \bf primitive \NC \bf explanation \NC\NR +\NC \type{\luatexversion} \NC A combination of major and minor number, as in pdfTeX. + Current value: {\bf\the\luatexversion} \NC\NR +\NC \type{\luatexrevision} \NC The revision, as in pdfTeX. + Current value: {\bf\luatexrevision} \NC\NR +\NC \type{\luatexdatestamp} \NC A combination of the local date and hour when + the current executable was compiled, + the syntax is identical to \tex{luatexrevision}. + Value for the executable that generated this + document: {\bf\luatexdatestamp}. \NC\NR +\stoptabulate + +Note that the \type{\luatexdatestamp} depends on both the compilation +time and compilation place of the current executable, it is defined in +terms of the local time. The purpose of this primitive is solely to be +an aid in the development process, do not use it for anything besides +debugging. + +\section{\UNICODE\ text support} + +Text input and output is now considered to be \UNICODE\ text, so +input characters can use the full range of \UNICODE\ ($2^{20}+2^{16} = +\hbox{10FFFF} = 1114111$). % hahe: wrong +/- 1 + +Later chapters will talk of characters and glyphs. Although these are +not the interchangeable, they are closely related. During typesetting, +a character is always converted to a suitable graphic representation +of that character in a specific font. However, while processing a list +of to-be-typeset nodes, its contents may still be seen as a character. +Inside \LUATEX\ there is not yet a clear separation between the two +concepts. Until this is implemented, please do not be too harsh on +us if we make errors in the usage of the terms. + +Note: for now, it only makes sense to use values above the base plane +(\type{"0xFFFF}) for \tex{mathcode} and \tex{catcode} assignments, +since the hyphenation patterns are still limited to max.\ 16-bit +values, so the other commands will not know what to do with those high +values. + +A few primitives are affected by this, all in a similar fashion: each of +them has to accomodate for a larger range of acceptable numbers. For +instance, \tex{char} now accepts values between~0 and 1114111. This +should not be a problem for well|-|behaved input files, but it could +create incompatibilities for input that would have generated an error +when processed by older \TEX|-|based engines. The maximum number of +allocations is \type {"10FFFF} or $2^{20}+2^{16}$ (21 bits). The % hahe: wrong? +maximum value that can be assigned are: + +% hahe: all below wrong by 1? +\starttabulate[|l|l|l|l|] +\NC \bf primitive \NC \bf bits \NC \bf hex \NC \bf numeric \NC\NR +\NC \tex{char} \NC 21 \NC \tt ~10FFFF \NC $2^{20}+2^{16}$ \NC\NR +\NC \tex{chardef} \NC 21 \NC \tt ~10FFFF \NC $2^{20}+2^{16}$ \NC\NR +\NC \tex{lccode} \NC 21 \NC \tt ~10FFFF \NC $2^{20}+2^{16}$ \NC\NR +\NC \tex{uccode} \NC 21 \NC \tt ~10FFFF \NC $2^{20}+2^{16}$ \NC\NR +\NC \tex{sfcode} \NC 15 \NC \tt ~~~7FFF \NC $2^{15}$ \NC\NR +\NC \tex{catcode} \NC ~4 \NC \tt ~~~~~~F \NC $2^{4}$ \NC\NR +\stoptabulate + +As far as the core engine is concerned, all input and output to text +files is \UTF-8 encoded. Input files can be pre|-|processed using the +\luatex{reader} callback. This will be explained in a later chapter. + +Output in byte|-|sized chunks can be achieved by using characters just +outside of the valid unicode range, starting at the value 1.114.112 +(0x110000). When the times comes to print a character $c>=1.114.112$, \LUATEX\ will +actually print the single byte corresponding to $c-1.114.112$. + +Output to the terminal uses \type{^^} notation for the lower control +range ($c<32$), with the exception of \type{^^I}, \type{^^J} and +\type{^^M}. These are considered \quote{safe} and therefore printed as-is. + +Normalization of the \UNICODE\ input can be handled by a macro package +during callback processing (this will be explained in \in{section}[iocallback]). + +\section{Wide math characters} + +Text handling is now extended up to the full \UNICODE\ range, but math mode +deals mostly with glyphs in fonts directly and fonts tend to be 16-bit +at maximum. The extension from 8-bit to 16-bit was already present in +\ALEPH\ by means of a set of extra primitives. + +Therefore, the math primitives from \TEX\ and \ALEPH\ are kept mostly +as they are, except for the ones that convert from input to math +commands like \type{matcode} and \type{omathcode}. The traditional +\TEX\ primitives are unchanged, their arguments are upscaled from 8~to +16~bits internally (as in \ALEPH). + +% hahe: again wrong by 1? +\starttabulate[|l|l|l|l|] +\NC \bf primitive \NC \bf max index/bits \NC \bf hex \NC \bf numeric \NC\NR +\NC \tex{mathchardef} \NC 15 \NC \tt~~~8000 \NC $2^{3}*2^{4}*2^{8}$ \NC\NR +\NC \tex{mathcode} \NC 8=15 \NC \tt~~~~~FF = ~~~800 \NC $2^{3}*2^{4}*2^{8}$ \NC\NR +\NC \tex{delcode} \NC 8=24 \NC \tt~~~~~FF = ~FFFFF \NC $2^{4}*2^{8}*2^{4}*2^{8}$ \NC\NR +\NC \tex{mathchar} \NC 15 \NC \tt~~~7FFF \NC $2^{3}*2^{4}*2^{8}$ \NC\NR +\NC \tex{mathaccent} \NC 15 \NC \tt~~~7FFF \NC $2^{3}*2^{4}*2^{8}$ \NC\NR +\NC \tex{delimiter} \NC 27 \NC \tt7FFFFFF \NC $2^{3}*2^{4}*2^{8}*2^{4}*2^{8}$ \NC\NR +\NC \tex{radical} \NC 27 \NC \tt7FFFFFF \NC $2^{3}*2^{4}*2^{8}*2^{4}*2^{8}$ \NC\NR +\NC \tex{omathchardef}\NC 27 \NC \tt8000000 \NC $2^{3}*2^{8}*2^{16}$ \NC\NR +\NC \tex{omathcode} \NC 21=27 \NC \tt~10FFFF = 8000000 \NC $2^{3}*2^{8}*2^{16}$ \NC\NR +\NC \tex{odelcode} \NC 21=24+24 \NC \tt~10FFFF = ~FFFFFF \NC $2^{8}*2^{16}$ \NC\NR +\NC \NC \NC \tt~~~~~~~ + ~FFFFFF \NC \quad $+ 2^{8}*2^{16}$ \NC\NR +\NC \tex{omathchar} \NC 27 \NC \tt7FFFFFF \NC $2^{3}*2^{8}*2^{18}$ \NC\NR +\NC \tex{omathaccent} \NC 27 \NC \tt7FFFFFF \NC $2^{3}*2^{8}*2^{18}$ \NC\NR +\NC \tex{odelimiter} \NC 27+24 \NC \tt7FFFFFF + ~FFFFFF \NC $2^{3}*2^{8}*2^{16}+2^{8}*2^{16}$ \NC\NR +\NC \tex{oradical} \NC 27+24 \NC \tt7FFFFFF + ~FFFFFF \NC $2^{3}*2^{8}*2^{16}+2^{8}*2^{16}$ \NC\NR +\stoptabulate + +\section{Extended tables} + +All traditional \TEX\ and \ETEX\ registers can be 16 bit numbers as in \ALEPH. The affected commands +are: + +\startcolumns[n=4] +\starttyping +\count +\dimen +\skip +\muskip +\marks +\toks +\countdef +\dimendef +\skipdef +\muskipdef +\toksdef +\box +\unhbox +\unvbox +\copy +\unhcopy +\unvcopy +\wd +\ht +\dp +\setbox +\vsplit +\stoptyping +\stopcolumns + +The same is true for the font-related \PDFTEX\ tables like \type{\rpcode} etc. + +\section{Attribute registers} + +Attributes are a completely new concept in \LUATEX. Syntactically, +they behave a lot like counters: attributes obey \TEX's nesting stack +and can be used after \tex{the} etc.\ just like the normal +\tex{count} registers. + +\startsyntax +\attribute <16-bit number> <optional equals> <31-bit number>!crlf +\attributedef <csname> <optional equals> <16-bit number> +\stopsyntax + +Conceptually, an attribute is either \quote{set} or \quote{unset}. Set +attributes can only have values of~0 or more, otherwise they are +considered unset and automatically remapped to an special negative +value meaning \quote{unset} (currently that value is $-1$, but please +test on negativity, not on a specific value). All attributes start +out in the \quote{unset} state (in \INITEX). + +Attributes can be used as extra counter values, but their usefulness +comes mostly from the fact that the numbers and values of all \quote{set} +attributes are attached to all nodes created in their scope. These can +then be queried from any \LUA\ code that deals with node +processing. Future versions of +\LUATEX\ will probably be using specific negative attribute ids for +internal use. Further information about how to use attributes for node +list processing from lua is given in~\in{chapter}[nodes]. + +\subsection{Box attributes} + +Nodes typically receive the list of attributes that is in effect when +they are created. This moment can be quite asynchronous. For example: in +paragraph building, the individual line boxes are created after the +\tex{par} command has been processed, so they will receive the list of +attributes that is in effect then, not the attributes that were in +effect in, say, the first or third line of the paragraph. + +Similar situations happen in \LUATEX\ regularly. A few of the more +obvious problematic cases are dealt with: the attributes for nodes +that are created during hyphenation and ligaturing borrow their +attributes from their surrounding glyphs, and it is possible to +influence box attributes directly. + +But many other inserted nodes, like the nodes resulting from math mode +and alignments, are processed `out of order', and will have the +attributes that are in effect at the precise moment of creation (which +is often later than expected). This area needs studying, and is in +fact one of the reasons for a beta at this moment. + + +It is possible to fine-tune the list of attributes that are applied +to a \type{hbox}, \type{vbox} or \type{vtop} by the use of the +keyword \type{attr}. An example: + +\starttyping +\attribute2=5 +\setbox0=\hbox {Hello} +\setbox2=\hbox attr1=12 attr2=-1{Hello} +\stoptyping + +This will set the attribute list of the box 2 to $1=12$, and the +attributes of box 0 will be $2=5$. As you can see, assigning a +negative value causes an attribute to be ignored. + +The \type{attr} keyword(s) should come before a \type{to} or +\type{spread}, if that is also specified. + +\section{\LUA\ related primitives} + +In order to merge \LUA\ code with \TEX\ input, a few new primitives are +needed. \LUATEX\ has support for 65536 separate \LUA\ interpreter +states. States are automatically created based on the integer argument +to the primitives \tex{directlua} and \tex{latelua}. + + +\subsection{\tex{directlua}} + +The primitive \tex{directlua} is used to execute \LUA\ code immediately. +The syntax is + +\startsyntax +\directlua <16-bit number> <general text> +\stopsyntax + +The \syntax{<general text>} is expanded fully, and then fed into the +\LUA\ interpreter state indicated by the \syntax{<16-bit number>}. +If the state does not exist yet, it will be initialized +automatically. After reading and expansion has been applied to the +\syntax{<general text>}, the resulting token list is converted to a +string as if it was displayed using \type{\the\toks}. On the \LUA\ +side, each \type{\directlua} block is treated as a separate chunk. + +The conversion from and to a token list means that you normally can +not use \LUA\ line comments (starting with \type{--}) within the +argument, as there typically will be only one \quote{line}, so that comment +will then run on until the end of the input. You will either need to +use \TEX-style line comments (starting with \%), or change the \TEX\ +category codes locally. + +\startbuffer +$\pi = \directlua0{tex.print(math.pi)}$ +\stopbuffer + +The \type{\directlua} command is expandable: the results of the \LUA\ +code become effective immediately. As an example, the following +input: +\typebuffer +will result in +\getbuffer + +Because the \syntax{<general text>} is a chunk, the normal \LUA\ error +handling is triggered if there is a problem in the included code. The +\LUA\ error messages should be clear enough, but the contextual +information is still pretty bad. Typically, you will only see the line +number of the right brace at the end of the code. + +While on the subject of errors: some of the things you can do inside +\LUA\ code can break up \LUATEX\ pretty bad. If you are not careful +while working with the node list interface, you may even end up with +assertion errors from within the \TEX\ portion of the executable. + +\subsection{\tex{latelua}} + +\tex{latelua} stores \LUA\ code in a whatsit that will be processed +inside the output routine. Its intended use is very similar to +\tex{pdfliteral}. Within the \LUA\ code, you can print \PDF\ +statements directly to the \PDF\ file. + +\startsyntax +\latelua <16-bit number> <general text> +\stopsyntax + +\subsection{\tex{luaescapestring}} + +This primitive converts a \TEX\ token sequence so that it can be +safely used as the contents of a \LUA\ string: embedded backslashes, +double and single quotes, and newlines and carriage returns are +escaped. This is done by prepending an extra token consisting of a +backslash with category code~12, and for the line endings, +converting them to \type{n} and \type{r} respectively. The token +sequence is fully expanded. + +\startsyntax +\luaescapestring <general text> +\stopsyntax + +Most often, this command is not actually the best way to deal with the +differences between the \TEX\ and \LUA. In very short bits of \LUA\ +code it is often not needed, and for longer stretches of \LUA\ code it +is easier to keep the code in a separate file and load it using \LUA's +\type{dofile}: +\starttyping +\directlua0 { dofile('mysetups.lua')} +\stoptyping + +\subsection{\tex{closelua}} + +This primitive allows you to close a \LUA\ state, freeing all of its +used memory. + +\startsyntax +\closelua <16-bit number> +\stopsyntax + +You cannot close the initial \LUA\ state~(0), attempts to do so +will be silently ignored. + +States are never closed automatically except when a fatal out of +memory error occurs, at which point \LUATEX\ will exit anyway. + +Also be aware that \LUA\ states are not closed immediately, but only +when the \tex{output} routine comes into play next (because there may be +pending \tex{latelua} calls). + +\section{New \ETEX\ primitives} + +\subsection{\tex{clearmarks}} + +This primitive clears a marks class completely, resetting all three +connected mark texts to empty. + +\startsyntax +\clearmarks <16-bit number> +\stopsyntax + +\subsection{\tex{noligs} and \tex{nokerns}} + +These primitives prohibit ligature and kerning insertion at the time +when the initial node list is built by \LUATEX's main control loop. +They are part of a temporary trick and will be removed in the near +future. For now, you need to enable these primitives when you want to +do node list processing of \quote{characters}, where \TEX's normal +processing would get in the way. + +\startsyntax +\noligs <integer>!crlf +\nokerns <integer> +\stopsyntax + + +\subsection{\tex{formatname}} + +\tex{formatname}'s syntax is identical to \tex{jobname}. + +In \INITEX, the expansion is empty. Otherwise, the expansion is the +value that \tex{jobname} had during the \INITEX\ run that dumped the +currently loaded format. + +\subsection{\tex{scantextokens}} + +The syntax of \tex{scantextokens} is identical to \tex{scantokens}. + +This is a slightly adapted version of \ETEX's \tex{scantokens}. The +differences are: + +\startitemize +\item The last (and usually only) line does not have a + \tex{endlinechar} appended +\item \tex{scantextokens} never raises an EOF error, + and it does not execute \tex{everyeof} tokens. +\item The \quote{.. while end of file ..} error tests are not executed, allowing + the expansion to end on a different grouping level or while a + conditional is still incomplete. +\stopitemize + + +\subsection{Catcode tables} + +Catcode tables are a new feature that allows you to switch to a +predefined catcode regime in a single statement. You can have a +practically unlimited number of different tables. + +The subsystem is backward compatible: if you never use the following +commands, your document will not notice any difference in behavior +compared to traditional \TEX. + +The contents of each catcode table is independent from any other +catcode tables, and their contents is stored and retrieved from the +format file. + +\subsubsection{\tex{catcodetable}} + +\startsyntax +\catcodetable <28-bit number> +\stopsyntax + +The \tex{catcodetable} switches to a different catcode table. +Such a table has to be previously created using one of the two +primitives below, or it has to be zero (table zero is initialized by +\INITEX). + +\subsubsection{\tex{initcatcodetable}} + +\startsyntax +\initcatcodetable <28-bit number> +\stopsyntax + +The \tex{initcatcodetable} creates a new table with catcodes +identical to those defined by \INITEX: + +\starttabulate[|l|l|l|l|] +\NC~0\NC \type{_} \NC \NC \tt escape \NC\NR +\NC~5\NC \tt\letterhat\letterhat M \NC return \NC \tt car\_ret \NC\NR +\NC~9\NC \tt\letterhat\letterhat @ \NC null \NC \tt ignore \NC\NR +\NC10\NC \tt <space> \NC space \NC \tt spacer \NC\NR +\NC11\NC {\tt a} -- {\tt z} \NC \NC \tt letter \NC\NR +\NC11\NC {\tt A} -- {\tt Z} \NC \NC \tt letter \NC\NR +\NC12\NC everything else \NC \NC \tt other \NC\NR +\NC14\NC \tt\letterpercent \NC \NC \tt comment \NC\NR +\NC15\NC \tt\letterhat\letterhat ? \NC delete \NC \tt invalid\_char \NC\NR +\stoptabulate + +The new catcode table is allocated globally: it will not go away after +the current group has ended. If the supplied number is identical to +the currently active table, an error is raised. + +\subsubsection{\tex{savecatcodetable}} + +\startsyntax +\savecatcodetable <28-bit number> +\stopsyntax + +\tex{savecatcodetable} copies the current set of catcodes to a +new table with the requested number. The definitions in this new table +are all treated as if they were made in the outermost level. + +The new table is allocated globally: it will not go away after the +current group has ended. If the supplied number is the currently +active table, an error is raised. + +\subsection{\tex{suppressfontnotfounderror}} + +\startsyntax +\suppressfontnotfounderror = 1 +\stopsyntax + +If this new integer parameter is non-zero, then \LUATEX\ will not +complain about font metrics that are not found. Instead it will +silently skip the font assignment, making the requested csname for the +font \type{\ifx} equal to \type{\nullfont}, so that it can be tested +against that without bothering the user. + +\subsection{Font syntax} + +\LUATEX\ will accept a braced argument as a font name: + +\starttyping +\font\myfont = {cmr10} +\stoptyping + +This allows for embedded spaces, without the need for double quotes. +Macro expansion takes place inside the argument. + +\section{Debugging} + +If \tex{tracingonline} is larger than 2, the node list display will +also print the node number of the nodes. + +\chapter {\LUA\ general} + +\section[init]{Initialization} + +\subsection{\LUATEX\ as a \LUA\ interpreter} + +There are some situations that make \LUATEX\ behave like it is a \LUA\ +interpreter only: +\startitemize[packed] +\item If a \type{--luaonly} option is given on the commandline +\item If the executable is named \type{texlua} (or \type{luatexlua}) +\item if the only non|-|option argument (file) on the commandline has the extension + \type{lua} or \type{luc}. +\stopitemize + +In this mode, it will set \LUA's \type{arg[0]} to the found script +name, pushing preceding options in negative values and the rest of the +commandline in the positive values, just like the \LUA\ +interpreter. + +\LUATEX\ will exit immediately after executing the specified \LUA\ +script and is, in effect, a somewhat bulky standalone \LUA\ +interpreter with a bunch of extra preloaded libraries. + +\subsection{\LUATEX\ as a \LUA\ byte compiler} + +There are two situations that make \LUATEX\ behaves like the \LUA\ +byte compiler: +\startitemize[packed] +\item If a \type{--luaconly} option is given on the commandline +\item If the executable is named \type{texluac} +\stopitemize + +In this mode, \LUATEX\ is exactly like \type{luac} from the standalone +\LUA\ distribution, except that it does not have the \type{-l} switch, +and that it accepts (but ignores) the \type{--luaconly} switch. + +\subsection{Other commandline processing} + +When the \LUATEX\ executable starts, it looks for the \type{--lua} +commandline option. If there is no \type{--lua} option, the +commandline is interpreted in a similar fashion as in traditional +\PDFTEX\ and \ALEPH. But if the option is present, \LUATEX\ will enter an +alternative mode of commandline parsing in comparison to the standard +web2c programs. + +In this mode, a small series of actions is taken in order. At first, +it will only interpret a small subset of the commandline directly: + +\starttabulate[|l|p|] +\NC \tt --lua=s \NC load and execute a \LUA\ initialization script \NC\NR +\NC \tt --safer \NC disable easily exploitable \LUA\ commands \NC\NR +\NC \tt --help \NC display help and exit \NC\NR +\NC \tt --version \NC display version and exit \NC\NR +\stoptabulate + +Now it searches for the requested \LUA\ initialization script. If it +can not be found using the actual name given on the commandline, a +second attempt is made by prepending the value of the environment +variable \type{LUATEXDIR}, if that variable is defined. + +Then it checks the \type{--safer} switch. You can use that to disable +some \LUA\ commands that can easily be abused by a malicious document. At +the moment, this switch \type{nil}s the following functions: + +\starttabulate[|l|l|] +\NC \bf library \NC \bf functions \NC \NR +\NC \tt os \NC \tt execute exec setenv rename remove tmpdir \NC \NR +\NC \tt io \NC \tt popen output tmpfile \NC \NR +\NC \tt lfs \NC \tt rmdir mkdir chdir lock touch \NC \NR +\stoptabulate + +And it makes \lua{io.open()} fail on files that are opened for +anything besides reading. + +Next the initialization script is loaded and executed. From within the +script, the entire commandline is available in the \LUA\ table +\lua{arg}, beginning with \lua {arg[0]}, containing the name of the executable. + +Commandline processing happens very early on. So early, in fact, that +none of \TEX's initializations have taken place yet. For that reason, +the \luatex{tex}, \luatex{token}, \luatex{node} and \luatex{pdf} tables +are off|-|limits during the execution of the startup file (they are +nilled). Special care is taken that \luatex{texio.write} and +\luatex{texio.write_nl} function properly, so that you can at least +report your actions to the log file when (and if) it eventually +becomes opened (note that \TEX\ does not even know its \tex{jobname} +yet at this point). See \in{chapter}[libraries] for more information +about the \LUATEX-specific \LUA\ extension tables. + +The \LUA\ initialization script is loaded into \LUA\ state~0, and +everything you do will remain visible during the rest of the run, with +the exception of the aforementioned \luatex{tex}, \luatex{token}, +\luatex{node} and \luatex{pdf} tables: those will be initialized +to their documented state after the execution of the script. You +should not store anything in variables or within tables with these +four global names, as they will be overwritten completely. + +We recommend you use the startup file only for your own +\TEX|-|independent initializations (if you need any), to parse the +commandline, set values in the \luatex{texconfig} table, and register +the callbacks you need. \LUATEX\ will fetch some of the other +commandline options from the \luatex{texconfig} table at the end of +script execution (see the description of the \luatex{texconfig} table +later on in this document for more details on which ones exactly). + +Unless the \luatex{texconfig} table tells it not to start \KPATHSEA\ +at all (set \luatex{texconfig.kpse_init} to \type{false} for that), +\LUATEX\ acts on three more commandline options after the +initialization script is finished: + +\starttabulate[|l|l|] +\NC \bf flag \NC \bf meaning \NC \NR +\NC \tt --fmt=s \NC set the format name \NC \NR +\NC \tt --progname=s \NC set the progname (only for \KPATHSEA) \NC \NR +\NC \tt --ini \NC enable \INITEX\ mode \NC \NR +\stoptabulate + +In order to initialize the built|-|in \KPATHSEA\ library properly, +\LUATEX\ needs to know the correct \quote{progname} to use, and for that it +needs to check \type{--progname} (and \type{--ini} and \type{--fmt}, if +\type{--progname} is missing). + +\section{\LUA\ changes} + +The C coroutine (COCO) patches from luajit are applied to the Lua core, the used +version is 1.1.3. See \hyphenatedurl{http://luajit.org/coco.html} for details. + +The \lua{read("*line")} function from the io library has been +adjusted so that it is line|-|ending neutral: any of \type{LF}, \type +{CR} or \type{CR+LF} are acceptable line endings. + +The \lua{tostring()} printer for numbers has been changed so that it +returns~\type{0} instead of something like~\hbox{\type{2e-5}} (which confused \TEX\ +enormously) when the value is so small that \TEX\ cannot distinguish +it from zero. + +Dynamic loading of \type{.so} and \type{.dll} files is disabled on +all platforms. + +\lua{luafilesystem} has been extended with two extra boolean functions +(\luatex{isdir(filename)} and \luatex{isfile(filename)}) and one extra +string field in its attributes table (\type{permissions}). + +The \lua{string} library has an extra function: +\luatex{string.explode(s[,m])}. This function returns an array containing +the string argument \type{s} split into substrings based on the value +of the string argument \type{m}. The second argument is a string that +is either empty (this splits the string into characters), a single +character (this splits on each occurrence of that character, possibly +introducing empty strings), or a single character followed by the plus +sign \type{+} (this special version does not create empty +substrings). The default value for \type{m} is \quote{\type{ +}} (multiple +spaces). + +Note: \type{m} is not hidden by surrounding braces (as it would be if +this function was written in \TEX\ macros). + +The \lua{string} library also has six extra iterators that return strings +piecemeal: + +\startitemize +\item \luatex{string.utfvalues(s)} (returns an integer value in the +\UNICODE\ range) +\item \luatex{string.utfcharacters(s)} (returns a string with a single +\UTF-8 token in it) +\item \luatex{string.characters(s)} (a string containing one byte) +\item \luatex{string.characterpairs(s)} (two strings each containing one byte) will +produce an empty second string in the string length was odd. +\item \luatex{string.bytes(s)} (a single byte value) +\item \luatex{string.bytepairs(s)} (two byte values) Will produce nil instead of a +number as its second return value if the string length was odd. +\stopitemize + +The \luatex{string.characterpairs()} and \luatex{string.bytepairs()} +are useful especially in the conversion of UTF-16 encoded data into UTF-8. + + +Note: The \lua{string} library functions \luatex{find} etc. are not +Unicode-aware. In cases where this is required (i.\,e.\ because the +pattern used for searching contains characters above code point 127), +the corresponding functions from \lua{unicode.utf8} should be used. + +\blank + +The \lua{os} library has a few extra functions and variables: + +\startitemize +\item \luatex{os.exec(commandline)} is a variation on \lua{os.execute}. + + The \type{commandline} can be either a single string or a single table. + + If the argument is a table: \LUATEX\ first checks if there is a value at + integer index zero. If there is, this is the command to be executed. Otherwise, + it will use the value at integer index one. (if neither are present, nothing + at all happens). + + The set of consecutive values starting at integer 1 in the table are + the arguments that are passed on to the command (the value at index 1 + becomes \type{argv[0]}). The command is searched for in the execution path, + so there is normally no need to pass on a fully qualified pathname. + + If the argument is a string, then it is automatically converted into + a table by splitting on whitespace. In this case, it is impossible + for the command and first argument to differ from each other. + + In the string argument format, whitespace can be protected by putting (part + of) an argument inside single or double quotes. One layer of quotes is + interpreted by \LUATEX, and all occurences of \type{\"}, \type{\'} or + \type{\\} within the quoted text are un-escaped. In the table format, there + is no string handling taking place. + + This function normally does not return control back to the lua script: the + command will replace the current process. However, it will return \type{nil, 'error'} + if there was a problem while attempting to execute the command. + + On windows, the current process is actually kept in memory until after the + execution of the command has finished. This prevents crashes in situations + where \TEXLUA\ scripts are run inside integrated \TEX\ environments. + + + (the original reason for this command is that it cleans out the current + process before starting the new one, making it especially useful for + use in \TEXLUA.) + +\item \luatex{os.spawn(commandline)} is a returning version of \lua{os.exec}, + with otherwise identical calling conventions. + + If the command ran ok, then the return value is the exit status of the + command. Otherwise, it will return \type{nil, 'error'}. + +\item \luatex{os.setenv('key','value')} + This sets a variable in the environment. Passing \lua{nil} instead of a + value string will remove the variable. + +\item \luatex{os.env} + This is a hash table containing a dump of the variables and values + in the process environment at the start of the run. It is writeable, + but the actual environment is {\it not\/} updated automatically. + +\item \luatex{os.gettimeofday()} +Returns the current `unix time', but as a float. This function is +not available on the SunOS platforms, so do not use this function +for portable documents. + +\item \luatex{os.times()} +Returns the current process times cf. the unix C library `times' +call in seconds. This function is not available on the Windows and SunOS +platforms, so do not use this function for portable documents. + +\item \luatex{os.tmpdir()} This will create a directory in the `current +directory' with the name \type{luatex.XXXXXX} where the X-es are +replaced by a unique string. The function also returns this string, +so you can \type{lfs.chdir()} into it, or \type{nil} if it failed to +create the directory. The user is responsible for cleaning up at +the end of the run, it does not happen automatically. + +\item \luatex{os.type} +This is a string that gives a global indication of the class of operating +system. The possible values are currently \type{windows}, \type{unix}, and +\type{msdos} (you are unlikely to find this value `in the wild'). + +\item \luatex{os.name} +This is a string that gives a more precise indication of the operating +system. These possible values are not yet fixed, and for \type{os.type} values +\type{windows} and \type{msdos}, the \type{os.name} values are simply +\type{windows} and \type{msdos} + +The list for the type \type{unix} is more precise: \type{linux}, +\type{freebsd}, \type{openbsd}, \type{solaris}, \type{sunos} (pre-solaris), +\type{hpux}, \type{irix}, \type{macosx}, \type{bsd} (unknown, but BSD-like), +\type{sysv} (unknown, but SYSV-like), \type{generic} (unknown). + +(\type{os.version} is planned as a future extension) + +\stopitemize + +In stock Lua, many things depend on the current locale. In \LUATEX, we can't do +that, because it makes documents unportable. While \LUATEX\ is running if +forces the following locale settings: +\starttyping + LC_CTYPE=C + LC_COLLATE=C + LC_NUMERIC=C +\stoptyping + +\section {\LUA\ modules} + +Some modules that are normally external to Lua are statically linked +in with \LUATEX, because they offer useful functionality: + +\startitemize +\item \lua{slnunicode}, from the Selene libraries, \hyphenatedurl{http://luaforge.net/projects/sln}. (version 1.1) + +This library has been slightly extended so that the \type{unicode.utf8.*} +functions also accept the first 256 values of Plane~18. This is the range \LUATEX\ +uses for raw binary output, as explained above, + +\item \lua{luazip}, from the kepler project, \hyphenatedurl{http://www.keplerproject.org/luazip/}. + (version 1.2.1, but patched for compilation with lua 5.1) +\item \lua{luafilesystem}, also from the kepler project, \hyphenatedurl{http://www.keplerproject.org/luafilesystem/}. + (version 1.2, but patched for compilation with lua 5.1) +\item \lua{lpeg}, by Roberto Ierusalimschy, \hyphenatedurl{http://www.inf.puc-rio.br/~roberto/lpeg.html}. (version 0.8.1) + +Note: \lua{lpeg} is not Unicode-aware, but interprets strings on a +byte-per-byte basis. This mainly means that \luatex{lpeg.S} cannot be +used with characters above code point 127, since those characters are +encoded using two bytes, and thus \luatex{lpeg.S} will look for one +of those two bytes when matching, not the combination of the two. + +The same is true for \luatex{lpeg.R}, although the latter will display +an error message if used with characters above code point 127: I.\,e.\ +\luatex{lpeg.R('aä')} results in the message \type{bad argument #1 to +'R' (range must have two characters)}, since to \lua{lpeg}, \type{ä} +is two 'characters' (bytes), so \type{aä} totals three. + +\item \lua{lzlib}, by Tiago Dionizio, \hyphenatedurl{http://mega.ist.utl.pt/~tngd/lua/}. (version 0.2) +\item \lua{md5}, by Roberto Ierusalimschy \hyphenatedurl{http://www.inf.puc-rio.br/~roberto/md5/md5-5/md5.html}. +\stopitemize + +\chapter[libraries]{\LUATEX\ \LUA\ Libraries} + +The interfacing between \TEX\ and \LUA\ is facilitated by a set of +library modules. The \LUA\ libraries in this chapter are all defined and +initialized by the \LUATEX\ executable. Together, they allow \LUA\ +scripts to query and change a number of \TEX's internal variables, run +various internal functions \TEX, and set up \LUATEX's hooks to execute +\LUA\ code. + +\section{The \luatex{tex} library} + +The \luatex{tex} table contains a large list of virtual internal \TEX\ +parameters that are partially writable. + +The designation \quote{virtual} means that these items are not properly +defined in \LUA, but are only front\-ends that are handled by a metatable +that operates on the actual \TEX\ values. As a result, most of the \LUA\ +table operators (like \type{pairs} and \type{#}) do not work on such +items. + +At the moment, it is possible to access almost every parameter +that has these characteristics: + +\startitemize[packed] +\item You can use it after \tex{the} +\item It is a single token. +\stopitemize + +This excludes parameters that need extra arguments, like +\tex{the}\tex{scriptfont}. + +The subset comprising simple integer and dimension registers are +writable as well as readable (stuff like \tex{tracingcommands} and +\tex{parindent}). + +\subsection{Integer parameters} + +The integer parameters accept and return \LUA\ numbers. + +Read-write: + +\startcolumns[n=2] +\starttyping +tex.adjdemerits +tex.binoppenalty +tex.brokenpenalty +tex.catcodetable +tex.clubpenalty +tex.day +tex.defaulthyphenchar +tex.defaultskewchar +tex.delimiterfactor +tex.displaywidowpenalty +tex.doublehyphendemerits +tex.endlinechar +tex.errorcontextlines +tex.escapechar +tex.exhyphenpenalty +tex.fam +tex.finalhyphendemerits +tex.floatingpenalty +tex.globaldefs +tex.hangafter +tex.hbadness +tex.holdinginserts +tex.hyphenpenalty +tex.interlinepenalty +tex.language +tex.lastlinefit +tex.lefthyphenmin +tex.linepenalty +tex.localbrokenpenalty +tex.localinterlinepenalty +tex.looseness +tex.mag +tex.maxdeadcycles +tex.month +tex.newlinechar +tex.outputpenalty +tex.pausing +tex.pdfadjustinterwordglue +tex.pdfadjustspacing +tex.pdfappendkern +tex.pdfcompresslevel +tex.pdfdecimaldigits +tex.pdfgamma +tex.pdfgentounicode +tex.pdfimageapplygamma +tex.pdfimagegamma +tex.pdfimagehicolor +tex.pdfimageresolution +tex.pdfinclusionerrorlevel +tex.pdfminorversion +tex.pdfobjcompresslevel +tex.pdfoutput +tex.pdfpagebox +tex.pdfpkresolution +tex.pdfprependkern +tex.pdfprotrudechars +tex.pdftracingfonts +tex.pdfuniqueresname +tex.postdisplaypenalty +tex.predisplaydirection +tex.predisplaypenalty +tex.pretolerance +tex.relpenalty +tex.righthyphenmin +tex.savinghyphcodes +tex.savingvdiscards +tex.showboxbreadth +tex.showboxdepth +tex.time +tex.tolerance +tex.tracingassigns +tex.tracingcommands +tex.tracinggroups +tex.tracingifs +tex.tracinglostchars +tex.tracingmacros +tex.tracingnesting +tex.tracingonline +tex.tracingoutput +tex.tracingpages +tex.tracingparagraphs +tex.tracingrestores +tex.tracingscantokens +tex.tracingstats +tex.uchyph +tex.vbadness +tex.widowpenalty +tex.year +\stoptyping +\stopcolumns + +Read|-|only: + +\startcolumns[n=3] +\starttyping +tex.deadcycles +tex.insertpenalties +tex.parshape +tex.prevgraf +tex.spacefactor +\stoptyping +\stopcolumns + +\subsection{Dimension parameters} + +The dimension parameters accept \LUA\ numbers (signifying scaled points) +or strings (with included dimension). The result is always a string. + +Read-write: + +\startcolumns[n=3] +\starttyping +tex.boxmaxdepth +tex.delimitershortfall +tex.displayindent +tex.displaywidth +tex.emergencystretch +tex.hangindent +tex.hfuzz +tex.hoffset +tex.hsize +tex.lineskiplimit +tex.mathsurround +tex.maxdepth +tex.nulldelimiterspace +tex.overfullrule +tex.pagebottomoffset +tex.pageheight +tex.pagerightoffset +tex.pagewidth +tex.parindent +tex.pdfdestmargin +tex.pdfeachlinedepth +tex.pdfeachlineheight +tex.pdffirstlineheight +tex.pdfhorigin +tex.pdflastlinedepth +tex.pdflinkmargin +tex.pdfpageheight +tex.pdfpagewidth +tex.pdfpxdimen +tex.pdfthreadmargin +tex.pdfvorigin +tex.predisplaysize +tex.scriptspace +tex.splitmaxdepth +tex.vfuzz +tex.voffset +tex.vsize +\stoptyping +\stopcolumns + +Read|-|only: + +\startcolumns[n=3] +\starttyping +tex.pagedepth +tex.pagefilllstretch +tex.pagefillstretch +tex.pagefilstretch +tex.pagegoal +tex.pageshrink +tex.pagestretch +tex.pagetotal +tex.prevdepth +\stoptyping +\stopcolumns + +\subsection{Direction parameters} + +The direction parameters are read|-|only and return a \LUA\ string. + +\startcolumns[n=3] +\starttyping +tex.bodydir +tex.mathdir +tex.pagedir +tex.pardir +tex.textdir +\stoptyping +\stopcolumns + +\subsection{Glue parameters} + +All glue parameters are read|-|only and return a \LUA\ string. + +\startcolumns[n=3] +\starttyping +tex.abovedisplayshortskip +tex.abovedisplayskip +tex.baselineskip +tex.belowdisplayshortskip +tex.belowdisplayskip +tex.leftskip +tex.lineskip +tex.parfillskip +tex.parskip +tex.rightskip +tex.spaceskip +tex.splittopskip +tex.tabskip +tex.topskip +tex.xspaceskip +\stoptyping +\stopcolumns + +\subsection{Muglue parameters} + +All muglue parameters are read|-|only and return a \LUA\ string. + +\startcolumns[n=3] +\starttyping +tex.medmuskip +tex.thickmuskip +tex.thinmuskip +\stoptyping +\stopcolumns + +\subsection{Tokenlist parameters} + +All tokenlist parameters are read|-|only and return a \LUA\ string. + +\startcolumns[n=3] +\starttyping +tex.errhelp +tex.everycr +tex.everydisplay +tex.everyeof +tex.everyhbox +tex.everyjob +tex.everymath +tex.everypar +tex.everyvbox +tex.output +tex.pdfpageattr +tex.pdfpageresources +tex.pdfpagesattr +tex.pdfpkmode +\stoptyping +\stopcolumns + +\subsection{Convert commands} + +The supported commands at this moment are: + +\startcolumns[n=3] +\starttyping +tex.AlephVersion +tex.Alephrevision +tex.OmegaVersion +tex.Omegarevision +tex.eTeXVersion +tex.eTeXrevision +tex.formatname +tex.jobname +tex.luatexrevision +tex.luatexdatestamp +tex.pdfnormaldeviate +tex.pdftexbanner +tex.pdftexrevision +\stoptyping +\stopcolumns + +All \quote{convert} commands are read|-|only and return a \LUA\ string. + +If you are wondering why this list looks haphazard; these are all the +cases of the \quote{convert} internal command that do not require an +argument. + +\subsection{Attribute, count, dimension and token registers} + +\TEX's attributes (\tex{attribute}), counters (\tex{count}), +dimensions (\tex{dimen}) and token (\tex{toks}) registers can be +accessed and written to using four virtual sub|-|tables of the +\luatex{tex} table: + +\startcolumns[n=3] +\starttyping +tex.attribute +tex.count +tex.dimen +tex.toks +\stoptyping +\stopcolumns + +It is possible to use the names of relevant \tex{attributedef}, \tex{countdef}, +\tex{dimendef}, or \tex{toksdef} control sequences as indices +to these tables: + +\starttyping +tex.count.scratchcounter = 0 +enormous = tex.dimen['maxdimen'] +\stoptyping + +In this case, \LUATEX\ looks up the value for you on the fly. You have +to use a valid \tex{countdef} (or \tex{attributedef}, or +\tex{dimendef}, or \tex{toksdef}), anything else will generate an error +(the intent is to eventually also allow \type{<chardef tokens>} and even +macros that expand into a number). + +The attribute and count registers accept and return \LUA\ numbers. + +The dimension registers accept \LUA\ numbers (in scaled points) or +strings (with an included absolute dimension; \type {em} and \type {ex} and \type {px} +are forbidden). The result is always a number in scaled points. + +The token registers accept and return \LUA\ strings. \LUA\ strings are +converted to and from token lists using \tex{the}\tex{toks} style +expansion: all category codes are either space (10) or other (12). + +As an alternative to array addressing, there are also accessor +functions defined: + +\startfunctioncall +tex.setdimen(number n, string s) +tex.setdimen(string s, string s) +tex.setdimen(number n, number n) +tex.setdimen(string s, number n) +number n = tex.getdimen(number n) +number n = tex.getdimen(string s) + +tex.setcount(number n, number n) +tex.setcount(string s, number n) +number n = tex.getcount(number n) +number n = tex.getcount(string s) + +tex.settoks (number n, string s) +tex.settoks (string s, string s) +string s = tex.gettoks (number n) +string s = tex.gettoks (string s) +\stopfunctioncall + +\subsection{Box registers} + +The current dimensions of \tex{box} registers can be read and +altered using three other virtual sub|-|tables : + +\starttyping +tex.wd +tex.ht +tex.dp +\stoptyping + +These are indexed strictly by number. + +The box size registers accept \LUA\ numbers (in scaled points) +or strings (with included dimension). The result is always a number +in scaled points. + +As an alternative to array addressing, there are also accessor +functions defined: + +\startfunctioncall +tex.setboxwd(number n, number n) +number n = tex.getboxwd(number n) + +tex.setboxht(number n, number n) +number n = tex.getboxht(number n) + +tex.setboxdp(number n, number n) +number n = tex.getboxdp(number n) +\stopfunctioncall + +It is also possible to set and query actual boxes, using the node +interface as defined in the \luatex{node} library: + +\starttyping +tex.box +\stoptyping + +for array access, or + +\starttyping +tex.setbox(number n, <node> s) +<node> n = tex.getbox(number n) +\stoptyping + +for function|-|based access. + +Be warned that an assignment like + +\starttyping +tex.box[0] = tex.box[2] +\stoptyping + +does not copy the node list, it just duplicates a node pointer. If +\tex{box2} will be cleared by \TEX\ commands later on, the contents +of \tex{box0} becomes invalid as well. To prevent this from +happening, always use \luatex{node.copy_list()} unless you are +assigning to a temporary variable: + +\starttyping +tex.box[0] = node.copy_list(tex.box[2]) +\stoptyping + +\subsection{Print functions} + +The \luatex{tex} table also contains the three print functions that +are the major interface from \LUA\ scripting to \TEX. + +The arguments to these three functions are all stored in an in|-|memory +virtual file that is fed to the \TEX\ scanner as the result of the +expansion of \tex{directlua}. + +The total amount of returnable text from a \tex{directlua} command +is only limited by available system \RAM. However, each separate +printed string has to fit completely in \TEX's input buffer. + +\subsubsection{\luatex{tex.print}} + +\startfunctioncall +tex.print(string s, ...) +tex.print(number n, string s, ...) +\stopfunctioncall + +Each string argument is treated by \TEX\ as a separate input line. + +The optional parameter can be used to print the strings using the +catcode regime defined by \tex{catcodetable}~\type{n}. If \type{n} is not +a valid catcode table, then it is ignored, and the currently +active catcode regime is used instead. + +The very last string of the very last \luatex{tex.print()} command in a +\tex{directlua} will not have the \tex{endlinechar} appended, all +others do. + +\subsubsection{\luatex{tex.sprint}} + +\startfunctioncall +tex.sprint(string s, ...) +tex.sprint(number n, string s, ...) +\stopfunctioncall + +Each string argument is treated by \TEX\ as a special kind of input line +that makes it suitable for use as a partial line input mechanism: + +\startitemize[packed] +\item \TEX\ does not switch to the \quote{new line} state, so + that leading spaces are not ignored. +\item No \tex{endlinechar} is inserted. +\item Trailing spaces are not removed. + +(Note that this does not prevent \TEX\ itself from eating spaces as +result of interpreting the line. For example, in +\starttyping +before\directlua0{tex.sprint("\\relax")tex.sprint(" inbetween")}after +\stoptyping +the space before \type{inbetween} will be gobbled as a result of +the \quote{normal} scanning of \type{\relax}). +\stopitemize + + +\subsubsection{\luatex{tex.write}} + +\startfunctioncall +tex.write(string s, ...) +\stopfunctioncall + +Each string argument is treated by \TEX\ as a special kind of input +line that makes is suitable for use as a quick way to dump +information: + +\startitemize +\item All catcodes on that line are either \quote{space} (for '~') or + \quote{character} (for all others). +\item There is no \tex{endlinechar} appended. +\stopitemize + +\subsection{Helper functions} + +\subsubsection{\luatex{tex.round}} + +\startfunctioncall +number n = tex.round(number o) +\stopfunctioncall + +Rounds lua number \type{o}, and returns a number that is in the range +of a valid \TEX\ register value. If the number starts out of range, it +generates a \quote{Number to big} error as well. + +\subsubsection{\luatex{tex.scale}} + +\startfunctioncall +number n = tex.scale(number o, number delta) +table n = tex.scale(table o, number delta) +\stopfunctioncall + +Multiplies the lua numbers \type{o} and \type{delta}, and returns a +rounded number that is in the range of a valid \TEX\ register value. +In the table version, it creates a copy of the table with all numeric +top||level values scaled in that manner. If the multiplied number(s) are +of range, it generates \quote{Number to big} error(s) as well. + +\section{The \luatex{token} library} + +The \luatex{token} table contains interface functions to \TEX's +handling of tokens. These functions are most useful when combined with +the \luatex{token_filter} callback, but they could be used standalone as +well. + +A token is represented in \LUA\ as a small table. For the moment, this +table consists of three numeric entries: + +\starttabulate[|l|l|p|] +\NC \bf no.\NC \bf meaning \NC \bf description \NC \NR +\NC 1 \NC command code \NC this is a value between~$0$ and~$130$ (approximately)\NC \NR +\NC 2 \NC command modifier \NC this is a value between~$0$ and~$2^{21}$ \NC \NR +\NC 3 \NC control sequence id \NC for commands that are not the result of control + sequences, like letters and characters, it is zero, + otherwise, it is a number pointing into the \quote + {equivalence table} \NC \NR +\stoptabulate + +\subsection{\luatex{token.get_next}} + +\startfunctioncall +token t = token.get_next() +\stopfunctioncall + +This fetches the next input token from the current input source, +without expansion. + +\subsection{\luatex{token.is_expandable}} + +\startfunctioncall +boolean b = token.is_expandable(token t) +\stopfunctioncall + +This tests if the token \type{t} could be expanded. + +\subsection{\luatex{token.expand}} + +\startfunctioncall +token.expand() +\stopfunctioncall + +If a token is expandable, this will expand one level of it, so that +the first token of the expansion will now be the next token to be read +by \luatex{tex.get_next()}. + +\subsection{\luatex{token.is_activechar}} + +\startfunctioncall +boolean b = token.is_activechar(token t) +\stopfunctioncall + +This is a special test that is sometimes handy. Discovering whether +some token is the result of an active character turned out to be very +hard otherwise. + +\subsection{\luatex{token.create}} + +\startfunctioncall +token t = token.create(string csname) +token t = token.create(number charcode) +token t = token.create(number charcode, number catcode) +\stopfunctioncall + +This is the token factory. If you feed it a string, then it is the +name of a control sequence (without leading backslash), and it will be +looked up in the equivalence table. + +If you feed it number, then this is assumed to be an input character, +and an optional second number gives its category code. This means it +is possible to overrule a character's category code, with a few +exceptions: the category codes~0 (escape), 9~(ignored), 13~(active), +14~(comment), and 15 (invalid) cannot occur inside a token. The values~0, 9, 14 +and~15 are therefore illegal as input to \luatex{token.create()}, and +active characters will be resolved immediately. + +Note: unknown string sequences and never defined active characters +will result in a token representing an \quote{undefined control sequence} +with a near|-|random name. It is {\it not\/} possible to define brand +new control sequences using \luatex{token.create}! + +\subsection{\luatex{token.command_name}} + +\startfunctioncall +string commandname = token.command_name(token t) +\stopfunctioncall + +This returns the name associated with the \quote{command} value of the token +in \LUATEX. There is not always a direct connection between these names and +primitives. For instance, all \tex{ifxxx} tests are grouped under +\type {if_fest}, and the \quote{command modifier} defines which test is to be run. + +\subsection{\luatex{token.command_id}} + +\startfunctioncall +number i = token.command_idtring commandname) +\stopfunctioncall + +This returns a number that is the inverse operation of the previous +command, to be used as the first item in a token table. + +\subsection{\luatex{token.csname_name}} + +\startfunctioncall +string csname = token.csname_name(token t) +\stopfunctioncall + +This returns the name associated with the \quote{equivalence table} value of +the token in \LUATEX. It returns the string value of the command used +to create the current token, or an empty string if there is no +associated control sequence. + +\subsection{\luatex{token.csname_id}} + +\startfunctioncall +number i = token.csname_id(string csname) +\stopfunctioncall + +This returns a number that is the inverse operation of the previous +command, to be used as the third item in a token table. + +\section{The \luatex{node} library} + +The \luatex{node} library contains functions that facilitate dealing +with (lists of) nodes and their values. They allow you to create, alter, +copy, delete, and insert \LUATEX\ node objects, the core +objects within the typesetter. + +\LUATEX\ nodes are represented in \LUA\ as userdata with +the metadata type \luatex{luatex.node}. The various parts within +a node can be accessed using named fields. + +Each node has at least the three fields \type{next}, \type{id}, and +\type{subtype}: + +\startitemize[intro] + +\item The \type{next} field returns the userdata +object for the next node in a linked list of nodes, or +nil, if there is no next node. + +\item The \type{id} indicates \TEX's \quote{node type}. The field \type{id} +has a numeric value for efficiency reasons, but some of the library +functions also accept a string value instead of \type{id}. + +\item The \type{subtype} is another number. It often gives further information +about a node of a particular \type{id}, but it is most important when dealing +with \quote{whatsits}, because they are differentiated solely based on their +\type{subtype}. +\stopitemize + +The other available fields depend on the \type{id} (and for \quote{whatsits}, the +\type{subtype}) of the node. Further details on the various fields and their +meanings are given in~\in{chapter}[nodes]. + +\TEX's math nodes are not yet supported: there is not yet an interface +to the internals of the math list and it is not possible to create +them from \LUA. Support for \type{unset} (alignment) nodes is partial: +they can be queried and modified from \LUA\ code, but not created. + +Nodes can be compared to each other, but: you are actually comparing +indices into the node memory. This means that equality tests can only +be trusted under very limited conditions. It will not work correctly +in any situation where one of the two nodes has been freed and|/|or +reallocated: in that case, there will be false positives. + +At the moment, memory management of nodes should still be done +explicitly by the user. Nodes are not \quote{seen} by the \LUA\ +garbage collector, so you have to call the node free-ing functions +yourself when you are no longer in need of a node (list). Nodes form +linked lists without reference counting, so you have to be careful +that when control returns back to \LUATEX\ itself, you have not +deleted nodes that are still referenced from a \type{next} pointer +elsewhere, and that you did not create nodes that are referenced more +than once. + +\subsection{Node handling functions} + +\subsubsection{\luatex{node.types}} + +\startfunctioncall +table t = node.types() +\stopfunctioncall + +This function returns an array that maps node id numbers to node type +strings, providing an overview of the possible top|-|level \type{id} +types. + +\subsubsection{\luatex{node.whatsits}} + +\startfunctioncall +table t = node.whatsits() +\stopfunctioncall + +\TEX's \quote{whatsits} all have the same \type{id}. The various subtypes +are defined by their \type{subtype}. The function is much like +\luatex{node.types}, except that it provides an array of \type{subtype} +mappings. + +\subsubsection{\luatex{node.id}} + +\startfunctioncall +number id = node.id(string type) +\stopfunctioncall + +This converts a single type name to its internal numeric +representation. + +\subsubsection{\luatex{node.subtype}} + +\startfunctioncall +number subtype = node.subtype(string type) +\stopfunctioncall + +This converts a single whatsit name to its internal numeric +representation (\type{subtype}). + +\subsubsection{\luatex{node.type}} + +\startfunctioncall +string type = node.type(number id) +\stopfunctioncall + +This converts a internal numeric representation to an external string +representation. + +\subsubsection{\luatex{node.fields}} + +\startfunctioncall +table t = node.fields(number id) +table t = node.fields(number id, number subtype) +\stopfunctioncall + +This function returns an array of valid field names for a particular +type of node. If you want to get the valid fields for a +\quote{whatsit}, you have to supply the second argument also. In other +cases, any given second argument will be silently ignored. + +This function accepts string \type{id} and \type{subtype} values as +well. + +\subsubsection{\luatex{node.has_field}} + +\startfunctioncall +boolean t = node.has_field(<node> n, string field) +\stopfunctioncall + +This function returns a boolean that is only true if \type{n} is +actually a node, and it has the field. + + +\subsubsection{\luatex{node.new}} + +\startfunctioncall +<node> n = node.new(number id) +<node> n = node.new(number id, number subtype) +\stopfunctioncall + +Creates a new node. All of the new node's fields are initialized to +either zero or nil except for \type{id} and \type{subtype} (if +supplied). If you want to create a new whatsit, then the second +argument is required, otherwise it need not be present. As with all +node functions, this function creates a node on the \TEX\ level. + +This function accepts string \type{id} and \type{subtype} values as +well. + +\subsubsection{\luatex{node.free}} + +\startfunctioncall +node.free(<node> n) +\stopfunctioncall + +Removes the node \type{n} from \TEX's memory. Be careful: no checks +are done on whether this node is still pointed to from a register or some +\type{next} field: it is up to you to make sure that the internal data +structures remain correct. + +\subsubsection{\luatex{node.flush_list}} + +\startfunctioncall +node.flush_list(<node> n) +\stopfunctioncall + +Removes the node list \type{n} and the complete node list following +\type{n} from \TEX's memory. Be careful: no checks are done on whether +any of these nodes is still pointed to from a register or some +\type{next} field: it is up to you to make sure that the internal data +structures remain correct. + +\subsubsection{\luatex{node.copy}} + +\startfunctioncall +<node> m = node.copy(<node> n) +\stopfunctioncall + +Creates a deep copy of node \type{n}, including all nested lists as in +the case of a hlist or vlist node. Only the \type{next} field is not +copied. + +\subsubsection{\luatex{node.copy_list}} + +\startfunctioncall +<node> m = node.copy_list(<node> n) +\stopfunctioncall + +Creates a deep copy of the node list that starts at \type{n}. + +\subsubsection{\luatex{node.hpack}} + +\startfunctioncall +<node> h = node.hpack(<node> n) +<node> h = node.hpack(<node> n, number w, string info) +\stopfunctioncall + +This function creates a new hlist by packaging the list that begins at node +\type{n} into a horizontal box. With only a single argument, this box +is created using the natural width of its components. In the three +argument form, \type{info} must be either \type{additional} or +\type{exactly}, and \type{w} is the additional (\type{\hbox spread}) +or exact (\type{\hbox to}) width to be used. + +Caveat: at this moment, there can be unexpected side|-|effects to this +function, like updating some of the \type{\marks} and \type{\inserts}. + +\subsubsection{\luatex{node.slide}} + +\startfunctioncall +<node> m = node.slide(<node> n) +\stopfunctioncall + +Returns the last node of the node list that starts at \type{n}. As a +side|-|effect, it also creates a reverse chain of \type{prev} pointers +between nodes. + +\subsubsection{\luatex{node.length}} + +\startfunctioncall +number i = node.length(<node> n) +number i = node.length(<node> n, <node> m) +\stopfunctioncall + +Returns the number of nodes contained in the node list that starts at +\type{n}. If \type{m} is also supplied it stops at \type{m} instead of +at the end of the list. The node \type{m} is not counted. + +\subsubsection{\luatex{node.count}} + +\startfunctioncall +number i = node.count(number id, <node> n) +number i = node.count(number id, <node> n, <node> m) +\stopfunctioncall + +Returns the number of nodes contained in the node list that starts at +\type{n} that have an matching \type{id} field. +If \type{m} is also supplied, counting stops at \type{m} instead of at +the end of the list. The node \type{m} is not counted. + +This function also accept string \type{id}'s. + +\subsubsection{\luatex{node.traverse}} + +\startfunctioncall +<node> t = node.traverse(<node> n) +\stopfunctioncall + +This is an iterator that loops over the node list that starts at \type{n}. + +\subsubsection{\luatex{node.traverse_id}} + +\startfunctioncall +<node> t = node.traverse_id(number id, <node> n) +\stopfunctioncall + +This is an iterator that loops over all the nodes in the list that +starts at \type{n} that have a matching \type{id} field. + +\subsubsection{\luatex{node.remove}} + +\startfunctioncall +<node> head, current = node.remove(<node> head, <node> current) +\stopfunctioncall + +This function removes the node \type{current} from the list following +\type{head}. It is your responsibility to make sure it is really part +of that list. The return values are the new \type{head} and +\type{current} nodes. The returned \type{current} is the +node in the calling argument, and is only passed back as a convenience +(its \type{next} field will be cleared). The returned \type{head} is +more important, because if the function is called with \type{current} +equal to \type{head}, it will be changed. + + +\subsubsection{\luatex{node.insert_before}} + +\startfunctioncall +<node> head, new = node.insert_before(<node> head, <node> current, <node> new) +\stopfunctioncall + +This function inserts the node \type{new} before \type{current} into +the list following \type{head}. It is your responsibility to make sure +that \type{current} is really part of that list. The return values are +the (potentially mutated) \type{head} and the \type{new}, set up to +be part of the list (with correct \type{next} field). If \type{head} +is initially \type{nil}, it will become \type{new}. + + +\subsubsection{\luatex{node.insert_after}} + +\startfunctioncall +<node> head, new = node.insert_after(<node> head, <node> current, <node> new) +\stopfunctioncall + +This function inserts the node \type{new} after \type{current} into +the list following \type{head}. It is your responsibility to make sure +that \type{current} is really part of that list. The return values are +the \type{head} and the \type{new}, set up to be part of the list +(with correct \type{next} field). If \type{head} is initially +\type{nil}, it will become \type{new}. + +\subsubsection{\luatex{node.first_character}} + +\startfunctioncall +<node> n = node.first_character(<node> n) +<node> n = node.first_character(<node> n, <node> m) +\stopfunctioncall + +Returns the first node that is a glyph node with a subtype indicating +it is a character, or \type{nil}. + +\subsubsection{\luatex{node.ligaturing}} + +\startfunctioncall +<node> h, <node> t, <boolean> success = node.ligaturing(<node> n) +<node> h, <node> t, <boolean> success = node.ligaturing(<node> n, <node> m) +\stopfunctioncall + +Apply \TEX-style ligaturing to the specified nodelist. The tail node +\type{m} is optional. The two returned nodes \type{h} and \type{t} are +the new head and tail (both \type{n} and \type{m} can change into +a new ligature). + +\subsubsection{\luatex{node.kerning}} + +\startfunctioncall +<node> h, <node> t, <boolean> success = node.kerning(<node> n) +<node> h, <node> t, <boolean> success = node.kerning(<node> n, <node> m) +\stopfunctioncall + +Apply \TEX-style kerning to the specified nodelist. The tail node +\type{m} is optional. The two returned nodes \type{h} and \type{t} are +the head and tail (either one of these can be an inserted kern node, +because special kernings with word boundaries are possible). + + + + +\subsubsection{\luatex{node.unprotect_glyphs}} + +\startfunctioncall +node.unprotect_glyphs(<node> n) +\stopfunctioncall + +Substracts 256 from all glyph node subtypes. This and the next +function are helpers to convert from \type{characters} to +\type{glyphs} during node processing. + + +\subsubsection{\luatex{node.protect_glyphs}} + +\startfunctioncall +node.protect_glyphs(<node> n) +\stopfunctioncall + +Adds 256 to all glyph node subtypes in the node list starting at +\type{n}, except that if the value is 1, it adds only 255. The special +handling of 1 means that \type{characters} will become \type{glyphs} +after substraction of 256. + +\subsubsection{\luatex{node.last_node}} + +\startfunctioncall +<node> n = node.last_node() +\stopfunctioncall + +This function pops the last node from \TEX's `current list'. +It returns that node, or \type{nil} if the current list is empty. + +\subsubsection{\luatex{node.write}} + +\startfunctioncall +node.write(<node> n) +\stopfunctioncall + +This is an experimental function that will append a node list to +\TEX's `current list'. There is no error checking yet! + + + +\subsection{Attribute handling} + +Attributes appear as linked list of userdata objects in the +\type{attr} field of individual nodes. They can be handled +individually, but it is much safer and more efficient to use the +dedicated functions associated with them. + +\subsubsection{\luatex{node.has_attribute}} + +\startfunctioncall +number v = node.has_attribute(<node> n, number id) +number v = node.has_attribute(<node> n, number id, number val) +\stopfunctioncall + +Tests if a node has the attribute with number \type{id} set. If +\type{val} is also supplied, also tests if the value matches \type{val}. +It returns the value, or, if no match is found, nil. + +\subsubsection{\luatex{node.set_attribute}} + +\startfunctioncall +node.set_attribute(<node> n, number id, number val) +\stopfunctioncall + +Sets the attribute with number \type{id} to the value +\type{val}. Duplicate assignments are ignored. + +\subsubsection{\luatex{node.unset_attribute}} + +\startfunctioncall +number v = node.unset_attribute(<node> n, number id, number val) +number v = node.unset_attribute(<node> n, number id) +\stopfunctioncall + +Unsets the attribute with number \type{id}. If \type{val} is also supplied, +it will only perform this operation if the value matches \type{val}. +Missing attributes or attribute|-|value pairs are ignored. + +If the attribute was actually deleted, returns its old +value. Otherwise, returns nil. + +\section{The \luatex{texio} library} + +This library takes care of the low|-|level I/O interface. + +\subsection{Printing functions} + +\subsubsection{\luatex{texio.write}} + +\startfunctioncall +texio.write(string target, string s, ...) +texio.write(string s, ...) +\stopfunctioncall + +Without the \type{target} argument, writes all given strings to the same +location(s) \TEX\ writes messages to at this moment. If +\tex{batchmode} is in effect, it writes only to the log, +otherwise it writes to the log and the terminal. + +The optional \type{target} can be one of three possibilities: +\type{term}, \type{log} or \type {term and log}. + +Note: If several strings are given, and if the first of these strings +is or might be one of the targets above, the \type{target} must be +specified explicitely to prevent \LUA\ from interpreting the first +string as the target. + +\subsubsection{\luatex{texio.write_nl}} + +\startfunctioncall +texio.write_nl(string target, string s, ...) +texio.write_nl(string s, ...) +\stopfunctioncall + +Like \luatex{texio.write}, but make sure that the given strings will +appear at the beginning of a new line. You can pass a single empty string +if you only want to move to the next line. + +\section{The \luatex{pdf} library} + +This table contains the current \type{h} and \type{v} values that +define the location on the output page. The values can be queried +and set using scaled points as units. + +\starttyping +pdf.v +pdf.h +\stoptyping + +The associated function calls are + +\startfunctioncall +pdf.setv(number n) +number n = pdf.getv() +pdf.seth(number n) +number n = pdf.geth() +\stopfunctioncall + + +It also holds a print function to write stuff to the \PDF\ document +that can be used from within a \tex{latelua} argument. This function +is not to be used inside \tex{directlua} unless you know {\it +exactly} what you are doing. + +\subsubsubject{\luatex{pdf.print}} + +\startfunctioncall +pdf.print(string s) +pdf.print(string type, string s) +\stopfunctioncall + +The optional parameter can be used to mimic the behavior of +\tex{pdfliteral}: the \type{type} is \type{direct} or \type{page}. + +\section{The \luatex{img} library} + +The \type{img} library can be used as an alternative to +\tex{pdfximage} and \tex{pdfrefximage}, and the associated `satellite' +commands like \tex{pdfximagebbox}. + +\subsubsubject{\luatex{img.new}} + +\startfunctioncall +<image> var = img.new() +<image> var = img.new(image_spec) +\stopfunctioncall + +This function creates a userdata object of type `image'. The +\type{image_spec} argument is optional. If it is given, it must be +a table, and that table must contain a \type{filename} key. A number of +other keys can also be useful, these are explained below. + +You can either say +\starttyping +a=img.new() +\stoptyping +followed by +\starttyping +a.filename="foo.png" +\stoptyping +or you can put the file name (and some or all of the other keys) +into a table directly, like so: +\starttyping +a=img.new{filename='foo.pdf',page=1} +\stoptyping + +The generated \type{<image>} userdata object allows access to a set of +user-specified values as well as a set of values that are normally +filled in and updated automatically by \LUATEX\ itself. Some of those +are derived from the actual image file, others are updated to reflect +the PDF output status of the object. + +There is one required user-specified field: the file name +(\type{filename}). It can optionally be augmented by the requested +image dimensions (\type{width}, \type{depth}, \type{height}), +user-specified image attributes (\type{attr}), the requested PDF page +identifier (\type{page}), the requested boundingbox (\type{pagebox}) +for PDF inclusion, the requested color space object (\type{colorspace}). + +The function \type{img.new} does not access the actual image file, it +just creates the \type{<image>} userdata object and initializes some +memory structures. The \type{<image>} object and its internal +structures are automatically garbage collected. + +Once the image is scanned, all the values in the +\type{<image>} become frozen, and you cannot change them any more. + +\subsubsubject{\luatex{img.keys}} + +\startfunctioncall +<table> keys = img.keys() +\stopfunctioncall + +This function returns a list of all the possible \type{image_spec} +keys, both user-supplied and automatic ones. + +% hahe: i need to add r/w ro column... +\starttabulate[|l|l|p|] +\NC Field name \NC Type \NC Description \NC \NR +\NC depth \NC number \NC Image depth for \LUATEX\ (in scaled points)\NC \NR +\NC height \NC number \NC Image height for \LUATEX\ (in scaled points)\NC \NR +\NC width \NC number \NC Image width for \LUATEX\ (in scaled points)\NC \NR +\NC transform \NC number \NC Image transform, integer number 0..7\NC \NR +\NC attr \NC string \NC Image attributes for \LUATEX \NC \NR +\NC filename \NC string \NC Image file name \NC \NR +\NC page \NC ?? \NC Identifier for the requested image + page (type is number or string, + default is the number 1)\NC \NR +\NC pagebox \NC string \NC Requested bounding box. + One of "none", "media", "crop", "bleed", "trim", "art" \NC \NR +\NC filepath \NC string \NC Full (expanded) file name of the image \NC \NR +\NC colordepth \NC number \NC Number of bits used by the color space \NC \NR +\NC colorspace \NC number \NC Color space object number \NC \NR +\NC imagetype \NC string \NC One of "pdf", "png", "jpg", "jbig2", or \type{nil} \NC \NR +\NC objnum \NC number \NC PDF image object number \NC \NR +\NC index \NC number \NC PDF image name suffix \NC \NR +\NC pages \NC number \NC Total number of available pages \NC \NR +\NC xsize \NC number \NC Natural image width \NC \NR +\NC ysize \NC number \NC Natural image height \NC \NR +\NC xres \NC number \NC Horizontal natural image resolution (dpi) \NC \NR +\NC yres \NC number \NC Vertical natural image resolution (dpi) \NC \NR +\stoptabulate + +A running (undefined) dimension in \type{width}, \type{height}, or \type{depth} is +represented as \type{nil} in Lua, so if you want to load an image at +its `natural' size, you do not have to specify any of those three fields. + +The \type{transform} allows to mirror and rotate the image in steps of 90~deg. +The default value~0 gives an unmirrored, unrotated image. +Values 1..3 give counterclockwise rotation by 90, 180, or 270~deg., +whereas with values 4..7 the image is first mirrored +and then rotated counterclockwise by 90, 180, or 270~deg. +The \type{transform} operation gives the same visual result +as if you would externally preprocess the image by a graphics tool +and then use it by \LUATEX. + +\subsubsubject{\luatex{img.scan}} + +\startfunctioncall +<image> var = img.scan(<image> var) +<image> var = img.scan(image_spec) +\stopfunctioncall + +When you say \type{img.scan(a)} for a new image, the file is scanned, +and variables such as \type{xsize}, \type{ysize}, image \type{type}, number of +\type{pages}, and the resolution are extracted. Each of the \type{width}, +\type{height}, \type{depth} fields are set up according to the image dimensions, +if they were not given an explicit value already. +An image file will never be scanned more than once for a given image variable. +With all subsequent \type{img.scan(a)} calls only the dimensions are again +set up (if they have been changed by the user in the meantime). + +For ease of use, you can do right-away a +\starttyping +a=img.scan{filename="foo.png"} +\stoptyping +without a prior \type{img.new}. + +Nothing is written yet at this point, so you can do \type{a=img.scan}, +retrieve the available info like image width and height, and then +throw away \type{a} again by saying \type{a=nil}. In that case no +image object will be reserved in the PDF, and the used memory will be +cleaned up automatically. + +\subsubsubject{\luatex{img.copy}} + +\startfunctioncall +<image> var = img.copy(<image> var) +<image> var = img.copy(image_spec) +\stopfunctioncall + +If you say \type{a = b}, then both variables point to the same +\type{<image>} object. if you want to write out an image with +different sizes, you can do a \type{b=img.copy(a)}. + +Afterwards, \type{a} and \type{b} still reference the same actual +image dictionary, but the dimensions for \type{b} can now be changed +from their initial values that were just copies from \type{a}. + +% Hartmut, I don't know if this makes sense. An example of what +% can, and what cannot be changed would be helpful. +% -- will think about it... + +\subsubsubject{\luatex{img.write}} + +\startfunctioncall +<image> var = img.write(<image> var) +<image> var = img.write(image_spec) +\stopfunctioncall + +By \type{img.write(a)} a \PDF\ object number is allocated, +and a whatsit node of subtype \type{pdf_refximage} is generated +and put into the output list. +By this the image \type{a} is placed into the \PDF\ page stream, +and the image file is written out into an image stream object +after the shipping of the current page is finished. + +Again you can do a terse call like + +\starttyping +img.write{filename="foo.png"} +\stoptyping + +The \type{<image>} variable is returned in case you want it for later +processing. + +\subsubsubject{\luatex{img.immediatewrite}} + +\startfunctioncall +<image> var = img.immediatewrite(<image> var) +<image> var = img.immediatewrite(image_spec) +\stopfunctioncall + +By \type{img.immediatewrite(a)} a \PDF\ object number is allocated, +and the image file for image \type{a} is written out immediately +into the \PDF\ file as an image stream object +(like with \type{\immediate\pdfximage}). +The object number of the image stream dictionary is then available +by the \type{objnum} key. +No \type{pdf_refximage} whatsit node is generated. +You will need a \luatex{img.write(a)} or \luatex{img.node(a)} call +to let the image appear on the page, +or reference it by another trick; +else you will have a dangling image object in the \PDF\ file. + +Also here you can do a terse call like + +\starttyping +a = img.immediatewrite{filename="foo.png"} +\stoptyping + +The \type{<image>} variable is returned; you will most likely need it. + +\subsubsubject{\luatex{img.node}} + +\startfunctioncall +<node> n = img.node(<image> var) +<node> n = img.node(image_spec) +\stopfunctioncall + +This function allocates a \PDF\ object number +and returns a whatsit node of subtype \type{pdf_refximage}, +filled with the image parameters \type{width}, \type{height}, +\type{depth}, and \type{objnum}. +Also here you can do a terse call like: + +\starttyping +n = img.node{filename="foo.png"} +\stoptyping + +This example outputs an image: + +\starttyping +node.write(img.node{filename="foo.png"}) +\stoptyping + +\subsubsubject{\luatex{img.types}} + +\startfunctioncall +<table> types = img.types() +\stopfunctioncall + +This function returns a list with the supported image file type names, +currently these are `pdf', `png', `jpg', and `jbig2'. + +\subsubsubject{\luatex{img.boxes}} + +\startfunctioncall +<table> boxes = img.boxes() +\stopfunctioncall + +This function returns a list with the supported \PDF\ page box names, +currently these are `media', `crop', `bleed', `trim', and `art' +(all in lowercase letters). + + +\section{The \luatex{mplib} library} + +The \MP\ library interface registers itself in the table \type{mplib}. It +is based on the MPlib beta version 0.40 (based on \MP\ 1.003). + +\subsection{\luatex{mplib.new}} + +To create a new metapost instance, call + +\startfunctioncall + mp = mplib.new({...}) +\stopfunctioncall + +This creates the \type{mp} instance object. The argument hash can have a number of +different fields, as follows: + +\starttabulate[|l|l|p|p|] +\NC name \NC type \NC description \NC default \NC\NR +\NC error_line \NC number \NC line width for terminal and log \NC 79 \NC\NR +\NC half_error_line \NC number \NC ``half'' of line width \NC 50 \NC\NR +\NC max_print_line \NC number \NC line length in ps output\NC 100\NC\NR +\NC main_memory \NC number \NC total memory size \NC 5000\NC\NR +\NC hash_size \NC number \NC hash size\NC 9500 \NC\NR +\NC hash_prime \NC number \NC prime number for hashing\NC 7919\NC\NR +\NC param_size \NC number \NC max. simultaneous macro parameters\NC 150\NC\NR +\NC max_in_open \NC number \NC max. input file nestings \NC 10\NC\NR +\NC random_seed \NC number \NC the initial random seed \NC variable\NC\NR +\NC interaction \NC string \NC the interaction mode, one of +"batch", "nonstop", "scroll", "errorstop"\NC "errorstop"\NC\NR +\NC ini_version \NC boolean \NC the --ini switch\NC false \NC\NR +\NC troff_mode \NC boolean \NC the -T switch \NC false \NC\NR +\NC print_found_names \NC boolean \NC whether long file names + should be printed to the log and terminal streams\NC false \NC\NR +\NC mem_name \NC string \NC --mem\NC plain.mem\NC\NR +\NC job_name \NC string \NC --jobname\NC mpout \NC\NR +\NC find_file \NC function \NC a function to find files \NC only local files\NC\NR +\stoptabulate + +The \type{find_file} function should be of this form: + +\starttyping + string found = finder (string name, string mode, string type) +\stoptyping + +\starttabulate[|l|l|p|] +\NC name \NC the requested file \NC \NR +\NC mode \NC the file mode: 'r' or 'w' \NC \NR +\NC type \NC the kind of file, one of: + "mp", "mem", "tfm", "map", "pfb", "enc" \NC \NR +\stoptabulate + +Return either the full pathname of the found file, or \type{nil} if +the file cannot be found. + +\subsection{\luatex{mp:statistics}} + +\startfunctioncall + table stats = mp:statistics() +\stopfunctioncall + +Returns the vital statistics for an MPlib instance. There are four +fields, giving the maximum number of used items in each of the four +statically allocated object classes: + +\starttabulate[|l|l|p|] +\NC main_memory \NC number \NC memory size \NC\NR +\NC hash_size \NC number \NC hash size\NC\NR +\NC param_size \NC number \NC simultaneous macro parameters\NC\NR +\NC max_in_open \NC number \NC input file nesting levels\NC\NR +\stoptabulate + +\subsection{\luatex{mp:execute}} + +You can ask the MP interpreter to run a bit of code by calling + +\startfunctioncall + local rettable = mp:execute('metapost language chunk') +\stopfunctioncall + +for various bits of Metapost language input. + +Be sure to check the \type{rettable.status} (see below) because when a +fatal Metapost error occurs the MPlib instance will become unusable +thereafter. + +Generally speaking, it is best to keep your chunks small, but beware +that all chunks have to obey proper syntax, like each of them is a +small file. For instance, you cannot split a single statement over +multiple chunks. + +\subsection{\luatex{mp:finish}} + +\startfunctioncall + local rettable = mp:finish() +\stopfunctioncall + +If for some reason you want to stop using an MPlib instance +while processing is not yet actually done, you can call +\type{mp:finish}. Memory and files can be closed by the +Lua garbage collector, but an explicit \type{mp:finish} is +the only way to capture the final part of the output streams. + +\subsection{Result table} + +The return value of \type{mp:execute} and \type{mp:finish} is a table +with a few possible keys (only `status' is always guaranteed to be present). + +\starttabulate[|l|l|p|] +\NC log \NC string \NC output to the 'log' stream \NC \NR +\NC term \NC string \NC output to the 'term' stream \NC \NR +\NC error \NC string \NC output to the 'error' stream (only used for +`Out Of Memory')\NC \NR +\NC status \NC number \NC the return value. 0 = good, 1 = warning, 2 = errors, + 3 = fatal error \NC \NR +\NC fig \NC table \NC An array of generated figures (if any)\NC \NR +\stoptabulate + +When \type{status} equals 3, you should stop using this MPlib instance +immediately, it is no longer capable of processing input. + +If it is present, each of the entries in the \type{fig} array is a +userdata representing a figure object, and each of those has a number of +object methods you can call: + +\starttabulate[|l|l|p|] +\NC boundingbox \NC function \NC returns the bounding box, as an array of 4 values\NC \NR +\NC postscript \NC function \NC return a string that is the ps output of the \type{fig} \NC \NR +\NC objects \NC function \NC returns the actual array of graphic objects in this \type{fig} \NC \NR +\NC copy_objects \NC function \NC returns a deep copy of the array of graphic objects in this \type{fig} \NC \NR +\NC filename \NC function \NC the filename this \type{fig}'s PostScript output + would have written to in standalone mode\NC \NR +\stoptabulate + +{\bf NOTE:} you can call \type{fig:objects()} only once for any one \type{fig} object! + +When the boundingbox represents a `negated rectangle' (i.w. the first set +of coordinates is larger than the second set), the picture is empty. + +Graphical objects come in various types that each have a different list of +accessible values. The types are: 'fill', 'outline', 'text', +'start_clip', 'stop_clip', 'start_bounds', 'stop_bounds', 'special'. + +There is helper function (\type{mplib.fields(obj)}) to get the list of +accessible values for a particular object, but you can just as easily +use the tables given below. + +All graphical objects have a field \type{type} that gives the object +type as a string value, that not explicit mentioned in the tables. In +the following, \type{number}s are PostScript points represented as +a floating point number, unless stated otherwise. Field values that +are of \type{table} are explained in the next section. + +\subsubsection{fill} + +\starttabulate[|l|l|p|] +\NC path \NC table \NC the list of knots \NC \NR +\NC htap \NC table \NC the list of knots for the reversed trajectory \NC \NR +\NC pen \NC table \NC knots of the pen \NC \NR +\NC color \NC table \NC the object's color \NC \NR +\NC linejoin \NC number \NC line join style (bare number)\NC \NR +\NC miterlimit \NC number \NC miterlimit\NC \NR +\NC prescript \NC string \NC the prescript text \NC \NR +\NC postscript \NC string \NC the postscript text \NC \NR +\stoptabulate + +The entries \type{htap} and \type{pen} are optional. + +\subsubsection{outline} + +\starttabulate[|l|l|p|] +\NC path \NC table \NC the list of knots \NC \NR +\NC pen \NC table \NC knots of the pen \NC \NR +\NC color \NC table \NC the object's color \NC \NR +\NC linejoin \NC number \NC line join style (bare number)\NC \NR +\NC miterlimit \NC number \NC miterlimit \NC \NR +\NC linecap \NC number \NC line cap style (bare number)\NC \NR +\NC dash \NC table \NC representation of a dash list\NC \NR +\NC prescript \NC string \NC the prescript text \NC \NR +\NC postscript \NC string \NC the postscript text \NC \NR +\stoptabulate + +The entry \type{dash} is optional. + +\subsubsection{text} + +\starttabulate[|l|l|p|] +\NC text \NC string \NC the text \NC \NR +\NC font \NC string \NC font tfm name \NC \NR +\NC dsize \NC number \NC font size\NC \NR +\NC color \NC table \NC the object's color \NC \NR +\NC width \NC number \NC \NC \NR +\NC height \NC number \NC \NC \NR +\NC depth \NC number \NC \NC \NR +\NC transform \NC table \NC a text transformation \NC \NR +\NC prescript \NC string \NC the prescript text \NC \NR +\NC postscript \NC string \NC the postscript text \NC \NR +\stoptabulate + +\subsubsection{special} + +\starttabulate[|l|l|p|] +\NC prescript \NC string \NC special text \NC \NR +\stoptabulate + + +\subsubsection{start_bounds, start_clip} + +\starttabulate[|l|l|p|] +\NC path \NC table \NC the list of knots \NC \NR +\stoptabulate + +\subsubsection{stop_bounds, stop_clip} + +No fields available. + +\subsection{Subsidiary table formats} + +\subsubsection{Paths and pens} + +Paths and pens (that are really just a special type of paths as far as +MPlib is concerned) are represented by an array where each entry +is a table that represents a knot. + +\starttabulate[|l|l|p|] +\NC left_type \NC string \NC when present: 'endpoint', but ususally absent \NC \NR +\NC right_type \NC string \NC like \type{left_type}\NC \NR +\NC x_coord \NC number \NC X coordinate of this knot\NC \NR +\NC y_coord \NC number \NC Y coordinate of this knot\NC \NR +\NC left_x \NC number \NC X coordinate of the precontrol point of this knot\NC \NR +\NC left_y \NC number \NC Y coordinate of the precontrol point of this knot\NC \NR +\NC right_x \NC number \NC X coordinate of the postcontrol point of this knot\NC \NR +\NC right_y \NC number \NC Y coordinate of the postcontrol point of this knot\NC \NR +\stoptabulate + +There is one special case: pens that are (possibly transformed) +ellipses have an extra string-valued key \type{type} with value +\type{elliptical} besides the array part containing the knot list. + +\subsubsection{Colors} + +A color is an integer array with 0, 1, 3 or 4 values: + +\starttabulate[|l|l|p|] +\NC 0 \NC Marking only \NC No values. \NC\NR +\NC 1 \NC Greyscale \NC 0ne value in the range 0 .. 1, `black' is 0 \NC\NR +\NC 3 \NC RGB \NC 3 values in the range 0 .. 1, `black' is 0,0,0 \NC\NR +\NC 4 \NC CMYK \NC 4 values in the range 0 .. 1, `black' is 0,0,0,1 \NC\NR +\stoptabulate + +If the color model of the internal object was \type{unitialized}, then +it was initialized to the values representing `black' in the colorspace +\type{defaultcolormodel} that was in effect at the time of the \type{shipout}. + +\subsubsection{Transforms} + +Each transform is a six-item array. + +\starttabulate[|l|l|p|] +\NC 1 \NC number \NC represents x \NC\NR +\NC 2 \NC number \NC represents y \NC\NR +\NC 3 \NC number \NC represents xx \NC\NR +\NC 4 \NC number \NC represents yx \NC\NR +\NC 5 \NC number \NC represents xy \NC\NR +\NC 6 \NC number \NC represents yy \NC\NR +\stoptabulate + +Note that the translation (index 1 and 2) comes first. This differs +from the ordering in PostScript, where the translation comes last. + +\subsubsection{Dashes} + +Each \type{dash} is two-item hash, using the same model as PostScript +for the representation of the dashlist. \type{dashes} is an array of +`on' and `off', values, and \type{offset} is the phase of the pattern. + +\starttabulate[|l|l|p|] +\NC dashes \NC hash \NC an array of on-off numbers \NC\NR +\NC offset \NC number \NC the starting offset value \NC\NR +\stoptabulate + + +\section{The \luatex{callback} library} + +This library has functions that register, find and list callbacks. + +The \luatex{callback} library is only available in \LUA\ state zero (0). + +\startfunctioncall +id, error = callback.register(string callback_name,function callback_func) +id, error = callback.register(string callback_name,nil) +\stopfunctioncall + +where the \syntax{callback_name} is a predefined callback name, see +below. The function returns the internal \type{id} of the callback +or \type{nil}, if the callback could not be registered. In the latter +case, \type{error} contains an error message, otherwise it is +\type{nil}. + +\LUATEX\ internalizes the callback function in such a way that +it does not matter if you redefine a function accidentally. +% hahe: what happens then? + +Callback assignments are always global. You can use the special value +\type {nil} instead of a function for clearing the callback. + +Currently, callbacks are not dumped into the format file. + +\startfunctioncall +table info = callback.list() +\stopfunctioncall + +The keys in the table are the known callback names, the value is a +boolean where \type{true} means that the callback is currently set +(active). + +\startfunctioncall +function f = callback.find(callback_name) +\stopfunctioncall + +If the callback is not set, \luatex{callback.find} returns \type{nil}. + +\subsection{File discovery callbacks} + +\subsubsection{\luatex{find_read_file} and \luatex{find_write_file}} + +Your callback function should have the following conventions: + +\startfunctioncall +string actual_name = function (number id_number, string asked_name) +\stopfunctioncall + +Arguments: + +\startitemize + +\sym{id_number} + +This number is zero for the log or \tex {input} files. For \TEX's \tex{read} or +\tex{write} the number is incremented by one, so \tex{read0} becomes~1. + +\sym{asked_name} + +This is the user|-|supplied filename, as found by \tex{input}, \tex{openin} +or \tex{openout}. + +\stopitemize + +Return value: + +\startitemize + +\sym{actual_name} + +This is the filename used. For the very first file that is read in by +\TEX, you have to make sure you return an \type{actual_name} that has +an extension and that is suitable for use as \type{jobname}. If you +don't, you will have to manually fix the name of the log file and +output file after \LUATEX\ is finished, and an eventual format +filename will become mangled. That is because these file names depend +on the jobname. + +You have to return \type{nil} if the file cannot be found. + +\stopitemize + +\subsubsection{\luatex{find_font_file}} + +Your callback function should have the following conventions: + +\startfunctioncall +string actual_name = function (string asked_name) +\stopfunctioncall + +The \type{asked_name} is an \OTF\ or \TFM\ font metrics file. + +Return \type{nil} if the file cannot be found. + +\subsubsection{\luatex{find_output_file}} + +Your callback function should have the following conventions: + +\startfunctioncall +string actual_name = function (string asked_name) +\stopfunctioncall + +The \type{asked_name} is the \PDF\ or \DVI\ file for writing. + +\subsubsection{\luatex{find_format_file}} + +Your callback function should have the following conventions: + +\startfunctioncall +string actual_name = function (string asked_name) +\stopfunctioncall + +The \type{asked_name} is a format file for reading (the format file +for writing is always opened in the current directory). + +\subsubsection{\luatex{find_vf_file}} + +Like \luatex{find_font_file}, but for virtual fonts. This applies to +both \ALEPH's \OVF\ files and traditional Knuthian \VF\ files. + +\subsubsection{\luatex{find_ocp_file}} + +Like \luatex{find_font_file}, but for ocp files. + +\subsubsection{\luatex{find_map_file}} + +Like \luatex{find_font_file}, but for map files. + +\subsubsection{\luatex{find_enc_file}} + +Like \luatex{find_font_file}, but for enc files. + +\subsubsection{\luatex{find_sfd_file}} + +Like \luatex{find_font_file}, but for subfont definition files. + +\subsubsection{\luatex{find_pk_file}} + +Like \luatex{find_font_file}, but for pk bitmap files. The argument +\type{name} is a bit special in this case. Its form is + +\starttyping +<base res>dpi/<fontname>.<actual res>pk +\stoptyping + +So you may be asked for \type{600dpi/manfnt.720pk}. It is up to you +to find a \quote{reasonable} bitmap file to go with that specification. + +\subsubsection{\luatex{find_data_file}} + +Like \luatex{find_font_file}, but for embedded files (\tex{pdfobj file '...'}). + +\subsubsection{\luatex{find_opentype_file}} + +Like \luatex{find_font_file}, but for \OPENTYPE\ font files. + +\subsubsection{\luatex{find_truetype_file} and \luatex{find_type1_file}} + +Your callback function should have the following conventions: + +\startfunctioncall +string actual_name = function (string asked_name) +\stopfunctioncall + +The \type{asked_name} is a font file. This callback is called while +\LUATEX\ is building its internal list of needed font files, so the +actual timing may surprise you. Your return value is later fed back +into the matching \luatex{read_file} callback. + +Strangely enough, \luatex{find_type1_file} is also used for \OPENTYPE\ +(\OTF) fonts. + +\subsubsection{\luatex{find_image_file}} + +Your callback function should have the following conventions: + +\startfunctioncall +string actual_name = function (string asked_name) +\stopfunctioncall + +The \type{asked_name} is an image file. Your return value is used to +open a file from the harddisk, so make sure you return something that +is considered the name of a valid file by your operating system. + +\subsection[iocallback]{File reading callbacks} + +\subsubsection{\luatex{open_read_file}} + +Your callback function should have the following conventions: + +\startfunctioncall +table env = function (string file_name) +\stopfunctioncall + +Argument: + +\startitemize + +\sym{file_name} + +The filename returned by a previous \luatex{find_read_file} or the return +value of \luatex{kpse.find_file()} if there was no such callback defined. + +\stopitemize + +Return value: + +\startitemize + +\sym{env} + +This is a table containing at least one required and one optional +callback function for this file. The required field is +\luatex{reader} and the associated function will be called once +for each new line to be read, the optional one is \luatex{close} +that will be called once when \LUATEX\ is done with the file. + +\LUATEX\ never looks at the rest of the table, so you can use it to +store your private per|-|file data. Both the callback functions will +receive the table as their only argument. + +\stopitemize + +\subsubsubsection{\luatex{reader}} + +\LUATEX\ will run this function whenever it needs a new input line +from the file. + +\startfunctioncall +function(table env) + return string line +end +\stopfunctioncall + +Your function should return either a string or \type{nil}. The value \type{nil} +signals that the end of file has occurred, and will make \TEX\ call +the optional \luatex{close} function next. + +\subsubsubsection{\luatex{close}} + +\LUATEX\ will run this optional function when it decides to close the file. + +\startfunctioncall +function(table env) + return +end +\stopfunctioncall + +Your function should not return any value. + +\subsubsection{General file readers} + +There is a set of callbacks for the loading of binary data +files. These all use the same interface: + +\startfunctioncall +function(string name) + return boolean success, string data, number data_size +end +\stopfunctioncall + +The \type{name} will normally be a full path name as it is returned by +either one of the file discovery callbacks or the internal version of +\luatex{kpse.find_file()}. + +\startitemize + +\sym{success} + +Return false when a fatal error occured (e.\,g.\ when the file cannot be +found, after all). + +\sym{data} + +The bytes comprising the file. + +\sym{data_size} + +The length of the \type{data}, in bytes. + +\stopitemize + +Return an empty string and zero if the file was found but there was a +reading problem. + +The list of functions is: +\starttabulate[|l|p|] +\NC \luatex{read_font_file} \NC This function is called when \TEX\ needs to read a \type{ofm} or + \type{tfm} file. \NC\NR +\NC \luatex{read_vf_file} \NC for virtual fonts.\NC\NR +\NC \luatex{read_ocp_file} \NC for ocp files.\NC\NR +\NC \luatex{read_map_file} \NC for map files.\NC\NR +\NC \luatex{read_enc_file} \NC for encoding files.\NC\NR +\NC \luatex{read_sfd_file} \NC for subfont definition files.\NC\NR +\NC \luatex{read_pk_file} \NC for pk bitmap files. \NC\NR +\NC \luatex{read_data_file} \NC for embedded files (\tex{pdfobj file '...'}).\NC\NR +\NC \luatex{read_truetype_file} \NC for \TRUETYPE\ font files. \NC\NR +\NC \luatex{read_type1_file} \NC for \TYPEONE\ font files.\NC\NR +\NC \luatex{read_opentype_file} \NC for \OPENTYPE\ font files. \NC\NR +\stoptabulate + +\subsection{Data processing callbacks} + +\subsubsection{\luatex{process_input_buffer}} + + +This callback allows you to change the contents of the line input +buffer just before \LUATEX\ actually starts looking at it. + +\startfunctioncall +function(string buffer) + return string adjusted_buffer +end +\stopfunctioncall + +If you return \type{nil}, \LUATEX\ will pretend like your callback +never happened. You can gain a small amount of processing time from +that. + + +\subsubsection{\luatex{token_filter}} + +This callback allows you to replace the way \LUATEX\ fetches +lexical tokens. + +\startfunctioncall +function() + return table token +end +\stopfunctioncall + +The calling convention for this callback is a bit more complicated than +for most other callbacks. The function should either return a \LUA\ +table representing a valid to|-|be|-|processed token or tokenlist, or +something else like nil or an empty table. + +If your \LUA\ function does not return a table representing a valid +token, it will be immediately called again, until it eventually does +return a useful token or tokenlist (or until you reset the callback +value to nil). See the description of \luatex{token} for some +handy functions to be used in conjunction with this callback. + +If your function returns a single usable token, then that token will +be processed by \LUATEX\ immediately. If the function returns a token +list (a table consisting of a list of consecutive token tables), then +that list will be pushed to the input stack at a completely new token +list level, with its token type set to \quote{inserted}. In either case, +the returned token(s) will not be fed back into the callback function. + +\subsection{Node list processing callbacks} + +The description of nodes and node lists is in~\in{chapter}[nodes]. + +\subsubsection{\luatex{buildpage_filter}} + +This callback is called whenever \LUATEX\ is ready to move stuff to +the main vertical list. You can use this callback to do specialized +manipulation of the page building stage like imposition or column +balancing. + +\startfunctioncall +function(<node> head, string extrainfo) + return true | false | <node> newhead +end +\stopfunctioncall + +As for all the callbacks that deal with nodes, the return value can be +one of three things: + +\startitemize[packed] +\item \type{boolean true} signals succesful processing +\item \type{node} signals that the \quote{head} node should be +replaced by this node +\item \type{boolean false} signals that the \quote{head} node list should be +ignored and flushed from memory +\stopitemize + +The string \type{extrainfo} gives some additional information about +what \TEX's state is with respect to the \quote{current page}. The possible +values are: + +\starttabulate[|lT|p|] +\NC \bf value \NC \bf explanation \NC\NR +\NC \tt alignment \NC a (partial) alignment is being added \NC\NR +\NC \tt box \NC a typeset box is being added \NC\NR +\NC \tt begin_of_par \NC the beginning of a new paragraph \NC\NR +\NC \tt vmode_par \NC \tex{par} was found in vertical mode \NC\NR +\NC \tt hmode_par \NC \tex{par} was found in horizontal mode \NC\NR +\NC \tt insert \NC an insert is added \NC\NR +\NC \tt penalty \NC a penalty (in vertical mode) \NC\NR +\NC \tt before_display \NC immediately before a display starts \NC\NR +\NC \tt after_display \NC a display is finished \NC\NR +\stoptabulate + +\subsubsection{\luatex{pre_linebreak_filter}} + +This callback is called just before \LUATEX\ starts converting a list +of nodes into a stack of \tex{hbox}es. The removal of a possible final skip +and the subsequent insertion of \tex{parfillskip} has not happened +yet at that moment. + +\startfunctioncall +function(<node> head, string groupcode) + return true | false | <node> newhead +end +\stopfunctioncall + +The string called \type {groupcode} identifies the nodelist's context +within \TEX's processing. The range of possibilities is given in the +table below, but not all of those can actually appear in +\luatex {pre_linebreak_filter}, some are for the +\luatex {hpack_filter} and \luatex {vpack_filter} callbacks that +will be explained in the next two paragraphs. + +\starttabulate[|lT|p|] +\NC \bf value \NC \bf explanation \NC\NR +\NC \tt hbox \NC \tex{hbox} in horizontal mode \NC\NR +\NC \tt adjusted_hbox\NC \tex{hbox} in vertical mode \NC\NR +\NC \tt vbox \NC \tex{vbox} \NC\NR +\NC \tt vtop \NC \tex{vtop} \NC\NR +\NC \tt align \NC \tex{halign} or \tex{valign} \NC\NR +\NC \tt disc \NC discretionaries \NC\NR +\NC \tt insert \NC packaging an insert \NC\NR +\NC \tt vcenter \NC \tex{vcenter} \NC\NR +\NC \tt local_box \NC \tex{localleftbox} or \tex{localrightbox} \NC\NR +\NC \tt split_off \NC top of a \tex{vsplit} \NC\NR +\NC \tt split_keep \NC remainder of a \tex{vsplit} \NC\NR +\NC \tt align_set \NC alignment cell \NC\NR +\NC \tt fin_row \NC alignment row \NC\NR +\stoptabulate + +\subsubsection{\luatex{post_linebreak_filter}} + +This callback is called just after \LUATEX\ has converted a list +of nodes into a stack of \tex{hbox}es. + +\startfunctioncall +function(<node> head, string groupcode) + return true | false | <node> newhead +end +\stopfunctioncall + +\subsubsection{\luatex{hpack_filter}} + +This callback is called when \TEX\ is ready to start boxing some +horizontal mode material. Math items are ignored at the moment. + +\startfunctioncall +function(<node> head, string groupcode, number size, string packtype) + return true | false | <node> newhead +end +\stopfunctioncall + +The \type{packtype} is either \type{additional} or \type{exactly}. If +\type{additional}, then the \type{size} is a \tex{hbox spread ...} +argument. If \type{exactly}, then the \type{size} is a \tex{hbox to ...}. +In both cases, the number is in scaled points. + +\subsubsection{\luatex{vpack_filter}} + +This callback is called when \TEX\ is ready to start boxing some +vertical mode material. Math displays are ignored at the moment. + +This function is very similar to the \luatex{hpack_filter}. Besides +the fact that it is called at different moments, there is an extra +variable that matches \TEX's \tex{maxdepth} setting. + +\startfunctioncall +function(<node> head, string groupcode, number size, string packtype, number maxdepth) + return true | false | <node> newhead +end +\stopfunctioncall + +\subsubsection{\luatex{pre_output_filter}} + +This callback is called when \TEX\ is ready to start boxing the +box 255 for \tex{output}. + +\startfunctioncall +function(<node> head, string groupcode, number size, string packtype, number maxdepth) + return true | false | <node> newhead +end +\stopfunctioncall + +\subsubsection{\luatex{hyphenate}} + +\startfunctioncall +function(<node> head, <node> tail) +end +\stopfunctioncall + +No return values. This callback has to insert discretionary nodes in +the node list it receives. + + +\subsubsection{\luatex{ligaturing}} + +\startfunctioncall +function(<node> head, <node> tail) +end +\stopfunctioncall + +No return values. This callback has to apply ligaturing to the node +list it receives. + +You don't have to worry about return values because the \type{head} +node that is passed on to the callback is guaranteed not to be a +glyph_node (if need be, a temporary node will be prepended), and +therefore it cannot be affected by the mutations that take place. +After the callback, the internal value of the `tail of the list' +will be recalculated. + +The \type{next} of \type{head} is guaranteed to be non-nil. + +The \type{next} of \type{tail} is guaranteed be nil, and therefore the +second callback argument can often be ignored. It is provided for +orthogonality, and because it can sometimes be handy when special +processing has to take place. + +\subsubsection{\luatex{kerning}} + +\startfunctioncall +function(<node> head, <node> tail) end +\stopfunctioncall + +No return values. This callback has to apply kerning between the nodes +in the node list it receives. See \type{ligaturing} for calling +conventions. + + +\subsection{Information reporting callbacks} + +\subsubsection{\luatex{start_run}} + +\startfunctioncall +function() +\stopfunctioncall + +Replaces the code that prints \LUATEX's banner. + +\subsubsection{\luatex{stop_run}} + +\startfunctioncall +function() +\stopfunctioncall + +Replaces the code that prints \LUATEX's statistics and \quote{output written +to} messages. + +\subsubsection{\luatex{start_page_number}} + +\startfunctioncall +function() +\stopfunctioncall + +Replaces the code that prints the \type{[} and the page number at the +begin of \tex{shipout}. This callback will also override the +printing of box information that normally takes place when +\tex{tracingoutput} is positive. + +\subsubsection{\luatex{stop_page_number}} + +\startfunctioncall +function() +\stopfunctioncall + +Replaces the code that prints the \type{]} at the end of \tex{shipout}. + +\subsubsection{\luatex{show_error_hook}} + +\startfunctioncall +function() + return +end +\stopfunctioncall + +This callback is run from inside the \TEX\ error function, and the idea +is to allow you to do some extra reporting on top of what \TEX\ already +does (none of the normal actions are removed). You may find some of +the values in the \luatex{status} table useful. + +\startitemize + +\sym{message} + +is the formal error message \TEX\ has given to the user. +(the line after the '!'). + +\sym{indicator} + +is either a filename (when it is a string) or a location indicator (a +number) that can mean lots of different things like a token list id +or a \tex{read} number. + +\sym{lineno} + +is the current line number. +\stopitemize + +This is an investigative item for 'testing the water' only. +The final goal is the total replacement of \TEX's error handling +routines, but that needs lots of adjustments in the web source because +\TEX\ deals with errors in a somewhat haphazard fashion. This is why the +exact definition of \type{indicator} is not given here. + +\subsection{Font-related callbacks} + +\subsubsection{\luatex{define_font}} + +\startfunctioncall +function(string name, number size, number id) return table font end +\stopfunctioncall + +The string \type{name} is the filename part of the font +specification, as given by the user. + +The number \type{size} is a bit special: + +\startitemize[packed] +\item if it is positive, it specifies an \quote{at size} in scaled points. +\item if it is negative, its absolute value represents a \quote{scaled} +setting relative to the designsize of the font. +\stopitemize + +The internal structure of the \type{font} table that is to be +returned is explained in \in{chapter}[fonts]. That table is saved +internally, so you can put extra fields in the table for your +later \LUA\ code to use. + +\section{The \luatex{lua} library} + +This library contains two read|-|only items: + +\subsection{Variables} + +\starttyping +number n = lua.id +\stoptyping + +This returns the id number of the instance. + +\starttyping +string s = lua.version +\stoptyping + +This returns a \LUATEX\ version identifier string. The value is +currently \directlua0{tex.print('lua.version')}, but it is soon to be +replaced by something more elaborate. + +\subsection{\LUA\ bytecode registers} + +\LUA\ registers can be used to communicate \LUA\ functions across \LUA\ +states. The accepted values for assignments are functions and +nil. Likewise, the retrieved value is either a function or \type{nil}. + +\starttyping +lua.bytecode[n] = function () .. end +lua.bytecode[n]() +\stoptyping + +The contents of the \luatex{lua.bytecode} array is stored inside the format +file as actual \LUA\ bytecode, so it can also be used to preload \LUA\ code. + +Note: The function must not contain any upvalues. Currently, functions +containing upvalues can be stored (and their upvalues are set to +\type{nil}), but this is an artefact of the current \LUA\ +implementation and thus subject to change. + +The associated function calls are + +\startfunctioncall +function f = lua.getbytecode(number n) +lua.setbytecode(number n, function f) +\stopfunctioncall + +Note: Since a \LUA\ file loaded using \luatex{loadfile(filename)} is +essentially an anonymous function, a complete file can be stored in a +bytecode register like this: + +\startfunctioncall +lua.bytecode[n] = loadfile(filename) +\stopfunctioncall + +Now all definitions (functions, variables) contained in the file can be +created by executing this bytecode register: + +\startfunctioncall +lua.bytecode[n]() +\stopfunctioncall + +\section{The \luatex{kpse} library} + +This library provides an interface to the \KPATHSEA\ file search method. + +Before the search library can be used at all, its database has to be +initialized. When \LUATEX\ is used to typeset documents, this happens +automatically (that is, unless explicitly prohibited by the user's +startup script. See~\in{section}[init] for more details). In \TEXLUA\ +mode, the initialization has to be done explicitly via the +\luatex{kpse.set_program_name} function. + +\subsection{\luatex{kpse.set_program_name}} + +Sets the \KPATHSEA\ executable (and optionally program) name. + +\startfunctioncall +kpse.set_program_name(string name) +kpse.set_program_name(string name, string progname) +\stopfunctioncall + +The second argument controls the use of the \quote{dotted} values in the +\type{texmf.cnf} configuration file, and defaults to the first argument. + + +\subsection{\luatex{kpse.find_file}} + +The most often used function in the library is find_file: + +\startfunctioncall +string f = kpse.find_file(string filename) +string f = kpse.find_file(string filename, string ftype) +string f = kpse.find_file(string filename, boolean mustexist) +string f = kpse.find_file(string filename, string ftype, boolean mustexist) +string f = kpse.find_file(string filename, string ftype, number dpi) +\stopfunctioncall + +Arguments: +\startitemize[intro] + +\sym{filename} + +the name of the file you want to find, with or without extension. + +\sym{ftype} + +maps to the \type {-format} argument of \KPSEWHICH. The supported + \type{ftype} values are the same as the ones supported by the +standalone \type{kpsewhich} program: + +\startsimplecolumns +\starttyping +'gf' +'pk' +'bitmap font' +'tfm' +'afm' +'base' +'bib' +'bst' +'cnf' +'ls-R' +'fmt' +'map' +'mem' +'mf' +'mfpool' +'mft' +'mp' +'mppool' +'MetaPost support' +'ocp' +'ofm' +'opl' +'otp' +'ovf' +'ovp' +'graphic/figure' +'tex' +'TeX system documentation' +'texpool' +'TeX system sources' +'PostScript header' +'Troff fonts' +'type1 fonts' +'vf' +'dvips config' +'ist' +'truetype fonts' +'type42 fonts' +'web2c files' +'other text files' +'other binary files' +'misc fonts' +'web' +'cweb' +'enc files' +'cmap files' +'subfont definition files' +'opentype fonts' +'pdftex config' +'lig files' +'texmfscripts' +\stoptyping +\stopsimplecolumns + +The default type is \type{tex}. + +\sym{mustexist} + +is similar to \KPSEWHICH's \type{-must-exist}, and the default is \type{false}. +If you specify \type{true} (or a non|-|zero integer), then the \KPSE\ library +will search the disk as well as the \type {ls-R} databases. + +\sym{dpi} + +This is used for the size argument of the formats \type{pk}, \type{gf}, and \type{bitmap font}. +\stopitemize + +\subsection{\luatex{kpse.init_prog}} + +Extra initialization for programs that need to generate bitmap fonts. + +\startfunctioncall +kpse.init_prog(string prefix, number base_dpi, string mfmode) +kpse.init_prog(string prefix, number base_dpi, string mfmode, string fallback) +\stopfunctioncall + + +\subsection{\luatex{kpse.readable_file}} + +Test if an (absolute) file name is a readable file + +\startfunctioncall +string f = kpse.readable_file(string name) +\stopfunctioncall + +The return value is the actual absolute filename you should use, +because the disk name is not always the same as the requested name, +due to aliases and system|-|specific handling under e.\,g.\ \MSDOS. + +Returns \lua {nil} if the file does not exist or is not readable. + +\subsection{\luatex{kpse.expand_path}} + +Like kpsewhich's \type {-expand-path}: + +\startfunctioncall +string r = kpse.expand_path(string s) +\stopfunctioncall + +\subsection{\luatex{kpse.expand_var}} + +Like kpsewhich's \type{-expand-var}: + +\startfunctioncall +string r = kpse.expand_var(string s) +\stopfunctioncall + +\subsection{\luatex{kpse.expand_braces}} + +Like kpsewhich's \type{-expand-braces}: + +\startfunctioncall +string r = kpse.expand_braces(string s) +\stopfunctioncall + +\subsection{\luatex{kpse.show_path}} + +Like kpsewhich's \type{-show-path}: + +\startfunctioncall +string r = kpse.show_path(string ftype) +\stopfunctioncall + + +\subsection{\luatex{kpse.var_value}} + +Like kpsewhich's \type{-var-value}: + +\startfunctioncall +string r = kpse.var_value(string s) +\stopfunctioncall + +\section{The \luatex{status} library} + +This contains a number of run|-|time configuration items that +you may find useful in message reporting, as well as an iterator +function that gets all of the names and values as a table. + +\startfunctioncall +table info = status.list() +\stopfunctioncall + +The keys in the table are the known items, the value is the current +value. + +Almost all of the values in \type{status} are fetched through a +metatable at run|-|time whenever they are accessed, so you cannot use +\type{pairs} on \type{status}, but you {\it can\/} use \type{pairs} +on \type{info}, of course. + +If you do not need the full list, you can also ask for a single item +by using its name as an index into \type{status}. + +The current list is: + +\starttabulate[|lT|p|] +\NC \ssbf key \NC \bf explanation \NC\NR +\NC pdf_gone\NC written \PDF\ bytes \NC \NR +\NC pdf_ptr\NC not yet written \PDF\ bytes \NC \NR +\NC dvi_gone\NC written \DVI\ bytes \NC \NR +\NC dvi_ptr\NC not yet written \DVI\ bytes \NC \NR +\NC total_pages\NC number of written pages \NC \NR +\NC output_file_name\NC name of the \PDF\ or \DVI\ file \NC \NR +\NC log_name\NC name of the log file \NC \NR +\NC banner\NC terminal display banner \NC \NR +\NC var_used\NC variable (one|-|word) memory in use \NC \NR +\NC dyn_used\NC token (multi|-|word) memory in use \NC \NR +\NC str_ptr\NC number of strings \NC \NR +\NC init_str_ptr\NC number of \INITEX\ strings \NC \NR +\NC max_strings\NC maximum allowed strings \NC \NR +\NC pool_ptr\NC string pool index \NC \NR +\NC init_pool_ptr\NC \INITEX\ string pool index \NC \NR +\NC pool_size\NC current size allocated for string characters \NC \NR +\NC node_mem_usage\NC a string giving insight into currently used nodes\NC\NR +\NC var_mem_max\NC number of allocated words for nodes\NC \NR +\NC fix_mem_max\NC number of allocated words for tokens\NC \NR +\NC fix_mem_end\NC maximum number of used tokens\NC \NR +\NC cs_count\NC number of control sequences \NC \NR +\NC hash_size\NC size of hash \NC \NR +\NC hash_extra\NC extra allowed hash \NC \NR +\NC font_ptr\NC number of active fonts \NC \NR +\NC max_in_stack\NC max used input stack entries \NC \NR +\NC max_nest_stack\NC max used nesting stack entries \NC \NR +\NC max_param_stack\NC max used parameter stack entries \NC \NR +\NC max_buf_stack\NC max used buffer position \NC \NR +\NC max_save_stack\NC max used save stack entries \NC \NR +\NC stack_size\NC input stack size \NC \NR +\NC nest_size\NC nesting stack size \NC \NR +\NC param_size\NC parameter stack size \NC \NR +\NC buf_size\NC current allocated size of the line buffer \NC \NR +\NC save_size\NC save stack size \NC \NR +\NC obj_ptr\NC max \PDF\ object pointer \NC \NR +\NC obj_tab_size\NC \PDF\ object table size \NC \NR +\NC pdf_os_cntr\NC max \PDF\ object stream pointer \NC \NR +\NC pdf_os_objidx\NC \PDF\ object stream index \NC \NR +\NC pdf_dest_names_ptr\NC max \PDF\ destination pointer \NC \NR +\NC dest_names_size\NC \PDF\ destination table size \NC \NR +\NC pdf_mem_ptr\NC max \PDF\ memory used \NC \NR +\NC pdf_mem_size\NC \PDF\ memory size \NC \NR +\NC largest_used_mark\NC max referenced marks class \NC \NR +\NC filename\NC name of the current input file \NC \NR +\NC inputid\NC numeric id of the current input \NC \NR +\NC linenumber\NC location in the current input file\NC \NR +\NC lasterrorstring\NC last error string\NC \NR +\NC luabytecodes\NC number of active \LUA\ bytecode registers\NC \NR +\NC luabytecode_bytes\NC number of bytes in \LUA\ bytecode registers\NC \NR +\NC luastates\NC number of active \LUA\ interpreters\NC \NR +\NC luastate_bytes\NC number of bytes in use by \LUA\ interpreters\NC \NR +\NC output_active\NC \type{true} if the \tex{output} routine is active\NC \NR +\stoptabulate + +\section{The \luatex{texconfig} table} + +This is a table that is created empty. A startup \LUA\ script could +fill this table with a number of settings that are read out by +the executable after loading and executing the startup file. + +\starttabulate[|lT|l|l|p|] +\NC \ssbf key \NC \bf type \NC \bf default \NC \bf explanation \NC\NR +\NC string_vacancies \NC number\NC 75000\NC cf.\ web2c docs \NC \NR +\NC pool_free \NC number\NC 5000\NC cf.\ web2c docs \NC \NR +\NC max_strings \NC number\NC 15000\NC cf.\ web2c docs \NC \NR +\NC strings_free \NC number\NC 100\NC cf.\ web2c docs \NC \NR +\NC nest_size \NC number\NC 50\NC cf.\ web2c docs \NC \NR +\NC max_in_open \NC number\NC 15\NC cf.\ web2c docs \NC \NR +\NC param_size \NC number\NC 60\NC cf.\ web2c docs \NC \NR +\NC save_size \NC number\NC 4000\NC cf.\ web2c docs \NC \NR +\NC stack_size \NC number\NC 300\NC cf.\ web2c docs \NC \NR +\NC dvi_buf_size \NC number\NC 16384\NC cf.\ web2c docs \NC \NR +\NC error_line \NC number\NC 79\NC cf.\ web2c docs \NC \NR +\NC half_error_line \NC number\NC 50\NC cf.\ web2c docs \NC \NR +\NC max_print_line \NC number\NC 79\NC cf.\ web2c docs \NC \NR +\NC ocp_list_size \NC number\NC 1000\NC cf.\ web2c docs \NC \NR +\NC ocp_buf_size \NC number\NC 1000\NC cf.\ web2c docs \NC \NR +\NC ocp_stack_size \NC number\NC 1000\NC cf.\ web2c docs \NC \NR +\NC hash_extra \NC number\NC 0\NC cf.\ web2c docs \NC \NR +\NC pk_dpi \NC number\NC 72\NC cf.\ web2c docs \NC \NR +\NC kpse_init \NC boolean \NC true \NC \type{false} totally disables \KPATHSEA\ initialisation + (only ever unset this if you implement {\it all\/} file + find callbacks!)\NC \NR +\NC trace_file_names \NC boolean \NC true \NC \type{false} disables \TEX's normal file open|-|close + feedback (the assumption is that callbacks will take care of + that) \NC \NR +\NC src_special_auto \NC boolean \NC false \NC source specials sub|-|item \NC \NR +\NC src_special_everypar \NC boolean \NC false \NC source specials sub|-|item \NC \NR +\NC src_special_everyparend \NC boolean \NC false \NC source specials sub|-|item \NC \NR +\NC src_special_everycr \NC boolean \NC false \NC source specials sub|-|item \NC \NR +\NC src_special_everymath \NC boolean \NC false \NC source specials sub|-|item \NC \NR +\NC src_special_everyhbox \NC boolean \NC false \NC source specials sub|-|item \NC \NR +\NC src_special_everyvbox \NC boolean \NC false \NC source specials sub|-|item \NC \NR +\NC src_special_everydisplay \NC boolean \NC false \NC source specials sub|-|item \NC \NR +\NC file_line_error \NC boolean \NC false \NC do \type{file:line} style error messages\NC \NR +\NC halt_on_error \NC boolean \NC false \NC abort run on the first encountered error\NC \NR +\NC formatname \NC string \NC \NC if no format name was given + on the commandline, this key will be tested first + instead of simply quitting\NC \NR +\NC jobname \NC string \NC \NC if no input file name was given + on the commandline, this key will be tested first + instead of simply giving up\NC \NR +\stoptabulate + +\section{The \luatex{font} library} + +The font library provides the interface into the internals of the font +system, and also it contains helper functions to load traditional +\TEX\ font metrics formats. Other font loading functionality is +provided by the \luatex{fontforge} library that will be discussed in +the next section. + +\subsection{Loading a \TFM\ file} + +\startfunctioncall +table fnt = font.read_tfm(string name, number s) +\stopfunctioncall + +The number is a bit special: + +\startitemize +\item if it is positive, it specifies an \quote{at size} in scaled points. +\item if it is negative, its absolute value represents a \quote{scaled} +setting relative to the designsize of the font. +\stopitemize + +The internal structure of the metrics font table that is returned is +explained in \in{chapter}[fonts]. + +\subsection{Loading a \VF\ file} + +\startfunctioncall +table vf_fnt = font.read_vf(string name, number s) +\stopfunctioncall + +The meaning of the number \type{s}, and the format of the returned +table is similar to the one returned by the \luatex{read_tfm()} function. + +\subsection{The fonts array} + +The whole table of \TEX\ fonts is accessible from lua using a virtual array. + +\starttyping +font.fonts[n] = { ... } +table f = font.fonts[n] +\stoptyping + +See \in{chapter}[fonts] for the structure of the tables. Because this +is a virtual array, you cannot call \type{pairs} on it, but see below +for the \type{font.each} iterator. + +The two metatable functions implementing the virtual array are: + +\startfunctioncall +table f = font.getfont(number n) +font.setfont(number n, table f) +\stopfunctioncall + +Also note the following: assignments can only be made to fonts that +have already been defined in \TEX, but have not been accessed {\it at +all\/} since that definition. This limits the usability of the write +access to \type{font.fonts} quite a lot, a less stringent ruleset will +likely be implemented later. + +\subsection{Checking a font's status} + +You can test for the status of a font by calling this function: + +\startfunctioncall +boolean f = font.frozen(number n) +\stopfunctioncall + +The return value is one of true (unassignable), false (can be changed) +or nil (not a valid font at all). + +\subsection{Defining a font directly} + +You can define your own font into \luatex{font.fonts} by calling this function: + +\startfunctioncall +number i = font.define(table f) +\stopfunctioncall + +The return value is the internal id number of the defined font (the +index into \luatex{font.fonts}). If the font creation fails, an error is +raised. The table is a font structure, as explained in +\in{chapter}[fonts]. + +\subsection{Projected next font id} + +\startfunctioncall +number i = font.nextid(); +\stopfunctioncall + +This returns the font id number that would be returned by a +\type{font.define} call if it was executed at this spot in the code +flow. This is useful for virtual fonts that need to reference +themselves. + + + +\subsection{Currently active font} + +\startfunctioncall +number i = font.current(); +font.current(number i); +\stopfunctioncall + +This gets or sets the currently used font number. + +\subsection{Maximum font id} + +\startfunctioncall +number i = font.max(); +\stopfunctioncall + +This is the largest used index in \type{font.fonts}. + +\subsection{Iterating over all fonts} + +\startfunctioncall +for i,v in font.each() do + ... +end +\stopfunctioncall + +This is an iterator over each of the defined \TEX\ fonts. The first +returned value is the index in \type{font.fonts}, the second the font +itself, as a lua table. The indices are listed incrementally, but they +do not always form an array of consecutive numbers: in some cases +there can be holes in the sequence. + +\section{The \luatex{fontforge} library} + +\subsection{Getting quick information on a font} + +\startfunctioncall +local info = fontforge.info('filename') +\stopfunctioncall + +This function returns either \type{nil}, or a \type{table}, or an +array of small tables (in the case of a TrueType collection). The +returned table(s) will contain six fairly interesting information +items from the font(s) defined by the file: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC fontname \NC string \NC the \quote{PostScript} name of the font\NC\NR +\NC fullname \NC string \NC The formal name of the font\NC\NR +\NC familyname \NC string \NC The family name this font belongs to\NC\NR +\NC weight \NC string \NC A string indicating the color value of the font\NC\NR +\NC version \NC string \NC The internal font version\NC\NR +\NC italicangle \NC float \NC The slant angle\NC\NR +\stoptabulate + +Getting information through this function is (sometimes much) more +efficient than loading the font properly, and is therefore handy when +you want to create a dictionary of available fonts based on a +directory contents. + +\subsection{Loading an \OPENTYPE\ or \TRUETYPE\ file} + +If you want to use an \OPENTYPE\ font, you have to get the metric +information from somewhere. Using the \type{fontforge} library, the +basic way to get that information is thus: + + +\starttyping +function load_font (filename) + local metrics = nil + local font = fontforge.open(filename) + if font then + metrics = fontforge.to_table(font) + fontforge.close(font) + end + return metrics +end + +myfont = load_font('/opt/tex/texmf/fonts/data/arial.ttf') +\stoptyping + +The main function call is + +\startfunctioncall +f, w = fontforge.open('filename') +\stopfunctioncall + +The first return value is a table representation of the font. The +second return value is a table containing any warnings and errors +reported by fontforge while opening the font. In normal typesetting, +you would probably ignore the second argument, but it can be useful +for debugging purposes. + +For \TRUETYPE\ collections (when filename ends in 'ttc'), you have to +use a second string argument to specify which font you want from the +collection. Use one of the \type{fullname} strings that are returned +by \type{fontforge.info} for that. + +\startfunctioncall +f, w = fontforge.open('filename','fullname') +\stopfunctioncall + + +The font file is parsed and partially interpreted by the font +loading routines from \FONTFORGE. The file format can be \OPENTYPE, +\TRUETYPE, \TRUETYPE\ Collection, \CFF, or \TYPEONE. + +There are a few advantages to this approach compared to reading the +actual font file ourselves: + +\startitemize + +\item The font is automatically re|-|encoded, so that the \type{metrics} + table for \TRUETYPE\ and \OPENTYPE\ fonts is using \UNICODE\ for + the character indices. + +\item Many features are pre|-|processed into a format that is easier to handle + than just the bare tables would be. + +\item \POSTSCRIPT|-|based \OPENTYPE\ fonts do not store the character height and + depth in the font file, so the character boundingbox has to be + calculated in some way. + +\item In the future, it may be interesting to allow \LUA\ scripts access to + the font program itself, perhaps even creating or changing the font. + +\stopitemize + +\subsection{Applying a \quote{feature file}} + +You can apply a \quote{feature file} to a loaded font: + +\startfunctioncall +fontforge.apply_featurefile(f,'filename') +\stopfunctioncall + +A \quote{feature file} is a textual representation of the features in an +OpenType font. See +\hyphenatedurl{http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html} +and +\hyphenatedurl{http://fontforge.sourceforge.net/featurefile.html} +for a more detailed description of feature files. + + +\subsection{Applying an \quote{afm file}} + +You can apply an \quote{afm file} to a loaded font: + +\startfunctioncall +fontforge.apply_afmfile(f,'filename') +\stopfunctioncall + +An \quote{afm file} is a textual representation of (some of) the metainformation +in a Type~1 font. See \hyphenatedurl{http://www.adobe.com/devnet/font/pdfs/5004.AFM_Spec.pdf} +for more information about afm files. + +Note: if you \type{fontforge.open()} a PFB file named \type{font.pfb}, +the library will automatically search for and apply \type{font.afm}, +if it exists in the same directory as \type{font.pfb}. In that case, +there is no need for an explicit call to \type{apply_afmfile()}. + + +\section{Fontforge font tables} + +The top|-|level keys in the returned table are (the explanations in +this part of the documentation are not yet finished): + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC table_version \NC number \NC indicates the metrics version\NC\NR +\NC fontname \NC string \NC \POSTSCRIPT\ font name\NC\NR +\NC fullname \NC string \NC official font name\NC\NR +\NC familyname \NC string \NC family name\NC\NR +\NC weight \NC string \NC weight indicator\NC\NR +\NC copyright \NC string \NC copyright information\NC\NR +\NC filename \NC string \NC the file name\NC\NR +%\NC defbasefilename \NC string \NC \NC\NR +\NC version \NC string \NC font version\NC\NR +\NC italicangle \NC float \NC slant angle\NC\NR +\NC units_per_em \NC number \NC 1000 for \POSTSCRIPT-based fonts, usually 2048 for \TRUETYPE\NC\NR +\NC ascent \NC number \NC height of ascender in \type{units_per_em}\NC\NR +\NC descent \NC number \NC depth of descender in \type{units_per_em}\NC\NR +\NC upos \NC float \NC \NC\NR +\NC uwidth \NC float \NC \NC\NR +\NC vertical_origin \NC number \NC \NC\NR +\NC uniqueid \NC number \NC \NC\NR +\NC glyphcnt \NC number \NC number of included glyphs\NC\NR +\NC glyphs \NC array \NC \NC\NR +\NC glyphmax \NC number \NC maximum used index the glyphs array\NC\NR +%\NC changed \NC number \NC \NC\NR +\NC hasvmetrics \NC number \NC \NC\NR +\NC order2 \NC number \NC set to 1 for \TRUETYPE\ splines, 0 otherwise\NC\NR +\NC strokedfont \NC number \NC \NC\NR +\NC weight_width_slope_only \NC number \NC \NC\NR +\NC head_optimized_for_cleartype \NC number \NC \NC\NR +\NC uni_interp \NC enum \NC \type {unset}, \type {none}, \type {adobe}, + \type {greek}, \type {japanese}, \type {trad_chinese}, + \type {simp_chinese}, \type {korean}, \type {ams}\NC\NR +\NC origname \NC string \NC the file name, as supplied by the user\NC\NR +\NC map \NC table \NC \NC\NR +\NC private \NC table \NC \NC\NR +\NC xuid \NC string \NC \NC\NR +\NC pfminfo \NC table \NC \NC\NR +\NC names \NC table \NC \NC\NR +\NC cidinfo \NC table \NC \NC\NR +\NC subfonts \NC array \NC \NC\NR +\NC cidmaster \NC array \NC \NC\NR +\NC commments \NC string \NC \NC\NR +\NC anchor_classes \NC table \NC \NC\NR +\NC ttf_tables \NC table \NC \NC\NR +\NC kerns \NC table \NC \NC\NR +\NC vkerns \NC table \NC \NC\NR +\NC texdata \NC table \NC \NC\NR +\NC lookups \NC table \NC \NC\NR +\NC gpos \NC table \NC \NC\NR +\NC gsub \NC table \NC \NC\NR +\NC chosenname \NC string \NC \NC\NR +\NC macstyle \NC number \NC \NC\NR +\NC fondname \NC string \NC \NC\NR +\NC design_size \NC number \NC \NC\NR +\NC fontstyle_id \NC number \NC \NC\NR +\NC fontstyle_name \NC table \NC \NC\NR +\NC design_range_bottom \NC number \NC \NC\NR +\NC design_range_top \NC number \NC \NC\NR +\NC strokewidth \NC float \NC \NC\NR +\NC mark_classes \NC array \NC \NC\NR +\NC mark_class_names \NC array \NC \NC\NR +\NC creationtime \NC number \NC \NC\NR +\NC modificationtime \NC number \NC \NC\NR +\NC os2_version \NC number \NC \NC\NR +\stoptabulate + +\subsubsection{Glyph items} + +The \type{glyphs} is an array containing the per|-|character +information (quite a few of these are only present if nonzero). + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC name \NC string \NC the glyph name\NC\NR +\NC unicode \NC number \NC unicode code point, or -1\NC\NR +\NC boundingbox \NC array \NC array of four numbers\NC\NR +\NC width \NC number \NC (only for horizontal fonts)\NC\NR +\NC vwidth \NC number \NC (only for vertical fonts)\NC\NR +\NC lsidebearing \NC number \NC (only if nonzero)\NC\NR +\NC glyph_class \NC number \NC (only if nonzero)\NC\NR +\NC kerns \NC array \NC (only for horizontal fonts, if set)\NC\NR +\NC vkerns \NC array \NC (only for vertical fonts, if set)\NC\NR +\NC dependents \NC array \NC linear array of glyph name strings (only if nonempty)\NC\NR +\NC lookups \NC table \NC (only if nonempty)\NC\NR +\NC ligatures \NC table \NC (only if nonempty)\NC\NR +\NC anchors \NC table \NC (only if set)\NC\NR +%\NC color \NC number \NC (only if set)\NC\NR +\NC tex_height \NC number \NC (only if set)\NC\NR +\NC tex_depth \NC number \NC (only if set)\NC\NR +\NC tex_sub_pos \NC number \NC (only if set)\NC\NR +\NC tex_super_pos \NC number \NC (only if set)\NC\NR +\NC comment \NC string \NC (only if set)\NC\NR +\stoptabulate + +The \type{kerns} and \type{vkerns} are linear arrays of small hashes: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC char \NC string \NC \NC\NR +\NC off \NC number \NC \NC\NR +\NC lookup \NC string \NC \NC\NR +\stoptabulate + +The \type{lookups} is a hash, based on lookup subtable names, with +the value of each key inside that a linear array of small hashes: + +% TODO: fix this description +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC type \NC enum \NC \type {position}, \type {pair}, \type {substitution}, \type {alternate}, + \type {multiple}, \type {ligature}, \type {lcaret}, \type {kerning}, \type {vkerning}, \type {anchors}, + \type {contextpos}, \type {contextsub}, \type {chainpos}, \type {chainsub}, + \type {reversesub}, \type {max}, \type {kernback}, \type {vkernback} \NC\NR +\NC specification \NC table \NC extra data \NC\NR +\stoptabulate + +For the first seven values of \type{type}, there can be additional +sub|-|information, stored in the sub-table \type{specification}: + +\starttabulate[|lT|l|l|p|] +\NC \bf value \NC \bf type \NC \bf explanation \NC\NR +\NC position \NC table \NC a table of the \type {offset_specs} type\NC\NR +\NC pair \NC table \NC one string: \type {paired}, and an array of one or + two \type {offset_specs} tables: \type{offsets}\NC\NR +\NC substitution \NC table \NC one string: \type {variant}\NC\NR +\NC alternate \NC table \NC one string: \type {components}\NC\NR +\NC multiple \NC table \NC one string: \type {components}\NC\NR +\NC ligature \NC table \NC two strings: \type {components}, \type {char}\NC\NR +\NC lcaret \NC array \NC linear array of numbers\NC\NR +\stoptabulate + +Tables for \type{offset_specs} contain up to four number|-|valued +fields: \type{x} (a horizontal offset), \type{y} (a vertical offset), +\type{h} (an advance width correction) and \type{v} (an advance height +correction). + +The \type{ligatures} is a linear array of small hashes: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC lig \NC table \NC uses the same substructure as a single \type{possub} item\NC\NR +\NC char \NC string \NC \NC\NR +\NC components \NC array \NC linear array of named components\NC\NR +\NC ccnt \NC number \NC \NC\NR +\stoptabulate + +The \type{anchor} table is indexed by a string signifying the +anchor type, which is one of + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC mark \NC table \NC placement mark\NR +\NC basechar \NC table \NC mark for attaching combining items to a base char\NR +\NC baselig \NC table \NC mark for attaching combining items to a ligature\NR +\NC basemark \NC table \NC generic mark for attaching combining items to connect to\NR +\NC centry \NC table \NC cursive entry point\NR +\NC cexit \NC table \NC cursive exit point\NR +\stoptabulate + +The content of these is an short array of defined anchors, with the +entry keys being the anchor names. For all except \type{baselig}, the +value is a single table with this definition: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC x \NC number \NC x location\NC\NR +\NC y \NC number \NC y location\NC\NR +\NC ttf_pt_index \NC number \NC truetype point index, only if given\NC\NR +\stoptabulate + +For \type{baselig}, the value is a small array of such anchor sets +sets, one for each constituent item of the ligature. + +For clarification, an anchor table could for example look like this : +\starttyping + ['anchor']={ + ['basemark']={ + ['Anchor-7']={ ['x']=170, ['y']=1080 } + }, + ['mark']={ + ['Anchor-1'] ={ ['x']=160, ['y']=810 }, + ['Anchor-4']= { ['x']=160, ['y']=800 } + }, + ['baselig']={ + [1] = { ['Anchor-2'] ={ ['x']=160, ['y']=650 } }, + [2] = { ['Anchor-2']= { ['x']=460, ['y']=640 } } + } + }, +\stoptyping + + + +\subsubsection{map table} + +The top|-|level map is a list of encoding mappings. Each of those is a table itself. + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC enccount \NC number \NC \NC\NR +\NC encmax \NC number \NC \NC\NR +\NC backmax \NC number \NC \NC\NR +\NC remap \NC table \NC \NC\NR +\NC map \NC array \NC non|-|linear array of mappings\NC\NR +\NC backmap \NC array \NC non|-|linear array of backward mappings\NC\NR +\NC enc \NC table \NC \NC\NR +\stoptabulate + +The \type{remap} table is very small: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC firstenc \NC number \NC \NC\NR +\NC lastenc \NC number \NC \NC\NR +\NC infont \NC number \NC \NC\NR +\stoptabulate + +The \type{enc} table is a bit more verbose: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC enc_name \NC string \NC \NC\NR +\NC char_cnt \NC number \NC \NC\NR +\NC char_max \NC number \NC \NC\NR +\NC unicode \NC array \NC of \UNICODE\ position numbers\NC\NR +\NC psnames \NC array \NC of \POSTSCRIPT\ glyph names\NC\NR +\NC builtin \NC number \NC \NC\NR +\NC hidden \NC number \NC \NC\NR +\NC only_1byte \NC number \NC \NC\NR +\NC has_1byte \NC number \NC \NC\NR +\NC has_2byte \NC number \NC \NC\NR +\NC is_unicodebmp \NC number \NC (only if nonzero)\NC\NR +\NC is_unicodefull \NC number \NC (only if nonzero)\NC\NR +\NC is_custom \NC number \NC (only if nonzero)\NC\NR +\NC is_original \NC number \NC (only if nonzero)\NC\NR +\NC is_compact \NC number \NC (only if nonzero)\NC\NR +\NC is_japanese \NC number \NC (only if nonzero)\NC\NR +\NC is_korean \NC number \NC (only if nonzero)\NC\NR +\NC is_tradchinese \NC number \NC (only if nonzero)\NC\NR +\NC is_simplechinese \NC number \NC (only if nonzero)\NC\NR +\NC low_page \NC number \NC \NC\NR +\NC high_page \NC number \NC \NC\NR +\NC iconv_name \NC string \NC \NC\NR +\NC iso_2022_escape \NC string \NC \NC\NR +\stoptabulate + +\subsubsection{private table} + +This is the font's private \POSTSCRIPT\ dictionary, if any. Keys and +values are both strings. + +\subsubsection{cidinfo table} + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC registry \NC string \NC \NC\NR +\NC ordering \NC string \NC \NC\NR +\NC supplement \NC number \NC \NC\NR +\NC version \NC number \NC \NC\NR +\stoptabulate + +\subsubsection{pfminfo table} + +The \type{pfminfo} table contains most of the OS/2 information: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC pfmset \NC number \NC \NC\NR +\NC winascent_add \NC number \NC \NC\NR +\NC windescent_add \NC number \NC \NC\NR +\NC hheadascent_add \NC number \NC \NC\NR +\NC hheaddescent_add \NC number \NC \NC\NR +\NC typoascent_add \NC number \NC \NC\NR +\NC typodescent_add \NC number \NC \NC\NR +\NC subsuper_set \NC number \NC \NC\NR +\NC panose_set \NC number \NC \NC\NR +\NC hheadset \NC number \NC \NC\NR +\NC vheadset \NC number \NC \NC\NR +\NC pfmfamily \NC number \NC \NC\NR +\NC weight \NC number \NC \NC\NR +\NC width \NC number \NC \NC\NR +\NC avgwidth \NC number \NC \NC\NR +\NC firstchar \NC number \NC \NC\NR +\NC lastchar \NC number \NC \NC\NR +\NC fstype \NC number \NC \NC\NR +\NC linegap \NC number \NC \NC\NR +\NC vlinegap \NC number \NC \NC\NR +\NC hhead_ascent \NC number \NC \NC\NR +\NC hhead_descent \NC number \NC \NC\NR +\NC hhead_descent \NC number \NC \NC\NR +\NC os2_typoascent \NC number \NC \NC\NR +\NC os2_typodescent \NC number \NC \NC\NR +\NC os2_typolinegap \NC number \NC \NC\NR +\NC os2_winascent \NC number \NC \NC\NR +\NC os2_windescent \NC number \NC \NC\NR +\NC os2_subxsize \NC number \NC \NC\NR +\NC os2_subysize \NC number \NC \NC\NR +\NC os2_subxoff \NC number \NC \NC\NR +\NC os2_subyoff \NC number \NC \NC\NR +\NC os2_supxsize \NC number \NC \NC\NR +\NC os2_supysize \NC number \NC \NC\NR +\NC os2_supxoff \NC number \NC \NC\NR +\NC os2_supyoff \NC number \NC \NC\NR +\NC os2_strikeysize \NC number \NC \NC\NR +\NC os2_strikeypos \NC number \NC \NC\NR +\NC os2_family_class \NC number \NC \NC\NR +\NC os2_xheight \NC number \NC \NC\NR +\NC os2_capheight \NC number \NC \NC\NR +\NC os2_defaultchar \NC number \NC \NC\NR +\NC os2_breakchar \NC number \NC \NC\NR +\NC os2_vendor \NC string \NC \NC\NR +\NC panose \NC table \NC \NC\NR +\stoptabulate + +The \type{panose} subtable has exactly 10 string keys: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC familytype \NC string \NC Values as in the \OPENTYPE\ font specification: + \type {Any}, \type {No Fit}, \type {Text and Display}, \type {Script}, + \type {Decorative}, \type {Pictorial} \NC\NR +\NC serifstyle \NC string \NC See the \OPENTYPE\ font specification for values\NC\NR +\NC weight \NC string \NC id. \NC\NR +\NC proportion \NC string \NC id. \NC\NR +\NC contrast \NC string \NC id. \NC\NR +\NC strokevariation \NC string \NC id. \NC\NR +\NC armstyle \NC string \NC id. \NC\NR +\NC letterform \NC string \NC id. \NC\NR +\NC midline \NC string \NC id. \NC\NR +\NC xheight \NC string \NC id. \NC\NR +\stoptabulate + +\subsubsection{names table} + +Each item has two top|-|level keys: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC lang \NC string \NC language for this entry \NC\NR +\NC names \NC table \NC \NC\NR +\stoptabulate + +The \type{names} keys are the actual \TRUETYPE\ name strings. The +possible keys are: + +\starttabulate[|lT|p|] +\NC \ssbf key \NC \bf explanation \NC\NR +\NC copyright \NC \NC\NR +\NC family \NC \NC\NR +\NC subfamily \NC \NC\NR +\NC uniqueid \NC \NC\NR +\NC fullname \NC \NC\NR +\NC version \NC \NC\NR +\NC postscriptname \NC \NC\NR +\NC trademark \NC \NC\NR +\NC manufacturer \NC \NC\NR +\NC designer \NC \NC\NR +\NC descriptor \NC \NC\NR +\NC venderurl \NC \NC\NR +\NC designerurl \NC \NC\NR +\NC license \NC \NC\NR +\NC licenseurl \NC \NC\NR +\NC idontknow \NC \NC\NR +\NC preffamilyname \NC \NC\NR +\NC prefmodifiers \NC \NC\NR +\NC compatfull \NC \NC\NR +\NC sampletext \NC \NC\NR +\NC cidfindfontname \NC \NC\NR +\stoptabulate + +\subsubsection{anchor_classes table} + +The anchor_classes classes: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC name \NC string \NC A descriptive id of this anchor class\NC\NR +\NC lookup \NC string \NC \NC\NR +\NC type \NC string \NC One of 'mark', 'mkmk', 'curs', 'mklg' \NC\NR +\stoptabulate +% type is actually a lookup subtype, not a feature name. officiallu, these strings +% should be gpos_mark2mark etc. + +\subsubsection{gpos table} + +Th gpos table has one array entry for each lookup. + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC type \NC string \NC One of + 'gpos_single', 'gpos_pair', 'gpos_cursive', + 'gpos_mark2base', 'gpos_mark2ligature', 'gpos_mark2mark', 'gpos_context', + 'gpos_contextchain' % , 'kern_statemachine' +\NC\NR +\NC flags \NC table \NC \NC\NR +\NC name \NC string \NC \NC\NR +\NC features \NC array \NC \NC\NR +\NC subtables \NC array \NC \NC\NR +\stoptabulate + +The flags table has a true value for each of the lookup flags that is +actually set: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC r2l \NC boolean \NC \NC\NR +\NC ignorebaseglyphs \NC boolean \NC \NC\NR +\NC ignoreligatures \NC boolean \NC \NC\NR +\NC ignorecombiningmarks \NC boolean \NC \NC\NR +\stoptabulate + + + +The features table has: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC tag \NC string \NC \NC\NR +\NC scripts \NC table \NC \NC\NR +\NC ismax \NC number \NC (only if true)\NC\NR +\stoptabulate + +The scripts table within features has: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC script \NC string \NC \NC\NR +\NC langs \NC array of strings \NC \NC\NR +\stoptabulate + + + +The subtables table has: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC name \NC string \NC \NC\NR +\NC suffix \NC string \NC (only if used)\NC\NR % used by gpos_single to get a default +\NC anchor_classes \NC number \NC (only if used)\NC\NR +\NC vertical_kerning \NC number \NC (only if used)\NC\NR +\NC kernclass \NC table \NC (only if used)\NC\NR +\stoptabulate + + +The kernclass with subtables table has: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +%\NC first_cnt \NC number \NC \NC\NR +%\NC second_cnt \NC number \NC \NC\NR +\NC firsts \NC array of strings \NC \NC\NR +\NC seconds \NC array of strings \NC \NC\NR +\NC lookup \NC string \NC associated lookup \NC \NR +\NC offsets \NC array of numbers \NC \NC\NR +%\NC kcid \NC number \NC ? \NC\NR +\stoptabulate + +\subsubsection{gsub table} + +This has identical layout to the \type{gpos} table, except for the +type: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC type \NC string \NC One of 'gsub_single', 'gsub_multiple', 'gsub_alternate', + 'gsub_ligature', 'gsub_context', 'gsub_contextchain', 'gsub_reversecontextchain' +\NC\NR +\stoptabulate + + + +\subsubsection{ttf_tables table} + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC tag \NC string \NC \NC\NR +\NC len \NC number \NC \NC\NR +\NC maxlen \NC number \NC \NC\NR +\NC data \NC number \NC \NC\NR +\stoptabulate + +\subsubsection{kerns table} + +Substructure is identical to the per|-|glyph subtable. + +\subsubsection{vkerns table} + +Substructure is identical to the per|-|glyph subtable. + +\subsubsection{texdata table} + + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC type \NC string \NC \type {unset}, \type {text}, \type {math}, \type {mathext}\NC\NR +\NC params \NC array \NC 22 font numeric parameters\NC\NR +\stoptabulate + +\subsubsection{lookups table} + +Top|-|level \type{lookups} is quite different from the ones at +character level. The keys in this hash are strings, the values the +actual lookups, represented as dictionary tables. + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC type \NC number \NC \NC\NR +\NC format \NC enum \NC One of 'glyphs', 'class', 'coverage', 'reversecoverage' \NC\NR +\NC tag \NC string \NC \NC\NR +\NC current_class \NC array \NC \NC\NR +\NC before_class \NC array \NC \NC\NR +\NC after_class \NC array \NC \NC\NR +\NC rules \NC array \NC an array of rule items\NC\NR +\stoptabulate + +Rule items have one common item and one specialized item: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC lookups \NC array \NC A linear array of lookup names\NC\NR +\NC glyph \NC array \NC Only if the parent's format is \quote{glyph}\NC\NR +\NC class \NC array \NC Only if the parent's format is \quote{glyph}\NC\NR +\NC coverage \NC array \NC Only if the parent's format is \quote{glyph}\NC\NR +\NC reversecoverage \NC array \NC Only if the parent's format is \quote{glyph}\NC\NR +\stoptabulate + +A glyph table is: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC names \NC string \NC \NC\NR +\NC back \NC string \NC \NC\NR +\NC fore \NC string \NC \NC\NR +\stoptabulate + + + +A class table is: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC current \NC array \NC of numbers \NC\NR +\NC before \NC array \NC of numbers \NC\NR +\NC after \NC array \NC of numbers \NC\NR +\stoptabulate + +coverage: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC current \NC array \NC of strings \NC\NR +\NC before \NC array \NC of strings\NC\NR +\NC after \NC array \NC of strings \NC\NR +\stoptabulate + +reversecoverage: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC current \NC array \NC of strings \NC\NR +\NC before \NC array \NC of strings\NC\NR +\NC after \NC array \NC of strings \NC\NR +\NC replacements \NC string \NC \NC\NR +\stoptabulate + +\section{The \luatex{lang} library} + +This library provides the interface to \LUATEX's structure +representing a language, and the associated functions. + +\startfunctioncall +<language> l = lang.new() +<language> l = lang.new(number id) +\stopfunctioncall + +This function creates a new userdata object. An object of type +\type{<language>} is the first argument to most of the other functions +in the \luatex{lang} library. These functions can also be used as if +they were object methods, using the colon syntax. + +Without an argument, the next available internal id number will be +assigned to this object. With argument, an object will be created that +links to the internal language with that id number. + +\startfunctioncall +number n = lang.id(<language> l) +\stopfunctioncall + +returns the internal \tex{language} id number this object refers to. + +\startfunctioncall +string n = lang.hyphenation(<language> l) +lang.hyphenation(<language> l, string n) +\stopfunctioncall + +Either returns the current hyphenation exceptions for this language, +or adds new ones. The syntax of the string is explained in the next +chapter,~\in{section}[patternsexceptions]. + +\startfunctioncall +lang.clear_hyphenation(<language> l) +\stopfunctioncall + +Clears the exception dictionary for this language. + +\startfunctioncall +string n = lang.clean(string o) +\stopfunctioncall + +Creates a hypenation key from the supplied hyphenation value. The +syntax of the argument string is explained in the next +chapter,~\in{section}[patternsexceptions]. This function is useful if +you want to do something else based on the words in a dictionary file, +like spell-checking. + +\startfunctioncall +string n = lang.patterns(<language> l) +lang.patterns(<language> l, string n) +\stopfunctioncall + +Adds additional patterns for this language object, or returns the +current set. The syntax of this string is explained in the next +chapter,~\in{section}[patternsexceptions]. + +\startfunctioncall +lang.clear_patterns(<language> l) +\stopfunctioncall + +Clears the pattern dictionary for this language. + +\startfunctioncall +number n = lang.prehyphenchar(<language> l) +lang.prehyphenchar(<language> l, number n) +\stopfunctioncall + +Gets or sets the \quote{pre-break} hyphen character for this font +(initially the hyphen, decimal 45). + +\startfunctioncall +number n = lang.posthyphenchar(<language> l) +lang.posthyphenchar(<language> l, number n) +\stopfunctioncall + +Gets or sets the \quote{post-break} hyphen character for this font +(initially null, decimal 0). + + +\startfunctioncall +boolean success = lang.hyphenate(<node> head) +boolean success = lang.hyphenate(<node> head, <node> tail) +\stopfunctioncall + +Inserts hyphenation points (discretionary nodes) in a node list. If +\type{tail} is given as argument, processing stops on that node. +Currently, \type{succes} is always true if \type{head} (and \type{tail}, if +specified) are proper nodes, regardless of possible other errors. + +\chapter[languages]{Languages and characters, Fonts and glyphs} + +\LUATEX's internal handling of the characters and glyphs that eventually +become typeset is quite different from the way \TEX82 handles those +same objects. The easiest way to explain the difference is to focus on +unrestricted horizontal mode (i.\,e.\ paragraphs) and hyphenation first. +Later on, it will be easy to deal with the differences that occur in +horizontal and math modes. + +In \TEX82, the characters you type are converted into \type{char_node} +records when they are encountered by the main control loop. \TEX\ +attaches and processes the font information while creating those +records, so that the resulting \quote{horizontal list} contains the final +forms of ligatures and implicit kerning. + +When it becomes necessary to hyphenate words in a paragraph, \TEX\ +converts (one word at time) the \type{char_node} records into a +string array by replacing ligatures with their components and +ignoring the kerning. Then it runs the hyphenation algorithm on this +string, and converts the hyphenated result back into a +\quote{horizontal list} that is consecutively spliced back into +the paragraph stream. + +The \type{char_node} records are somewhat misnamed, as they are glyph +positions in specific fonts, and therefore not really \quote{characters} +in the linguistic sense. There is no language information inside the +\type{char_node} records. Instead, language information is passed along +using \type{language whatsit} records inside the horizontal list. + +In \LUATEX, the situation is quite different. The characters you +type are always converted into \type{glyph_node} records with a +special subtype to identify them as being intended as linguistic +characters. \LUATEX\ stores the needed language information in those +records, but does not do any font-related processing at the time of +node creation. + +When it becomes necessary to typeset a paragraph, \LUATEX\ first +inserts all hyphenation points right into the whole node list. +Next, it processes all the font information in the whole list +(creating ligatures and adjusting kerning), and finally it adjusts +all the subtype identifiers so that the records are \quote{glyph +nodes} from now on. + +That was the broad overview. The rest of this chapter will deal with the +minutiae of the new process. + +\section[charsandglyphs]{Characters and glyphs} + +\TEX82 (including \PDFTEX) differentiated between \type{char_node}s +and \type{lig_node}s. The former are simple items that contained +nothing but a \quote{character} and a \quote{font} field, and they +lived in the same memory as tokens did. The latter also contained a +list of components, and a subtype indicating whether this ligature was +the result of a word boundary, and it was stored in the same place as +other nodes like boxes and kerns and glues. + +In \LUATEX, these two types are merged into one, somewhat larger +structure called a \type{glyph_node}. Besides having the old +character, font, and component fields, and the new special fields like +\quote{attr} (see~\in{section}[glyphnodes]), these nodes also contain: + +\startitemize +\item A subtype, split into four main types: + \startitemize + \item \quote{character} -- for characters to be hyphenated + \item \quote{glyph} -- for specific font glyphs + \item \quote{ligature} -- for ligatures + \item \quote{ghost} -- for \quote{ghost objects} + \stopitemize + The latter two make further use of two extra fields: + \startitemize + \item \quote{left} -- for ligatures: created from a left word boundary. + for ghosts: created from \tex{leftghost} + \item \quote{right} -- for ligatures: created from a right word boundary. + for ghosts: created from \tex{rightghost} + \stopitemize + for ligatures, both bits can be set at the same time (in case of a single-glyph word). + +\item \type{glyph_node}s of type \quote{character} also contain language data, + split into four items that were current when the node was created: + the \tex{setlanguage} (15 bits), \tex{lefthyphenmin} (8 bits), + \tex{righthyphenmin} (8 bits), and \tex{uchyph} (1 bit). +\stopitemize + +Incidentally, \LUATEX\ allows 32768 separate languages, and words can +be 256 characters long. + +Because the \tex{uchyph} value is saved in the actual nodes, its +handling is subtly different from \TEX82: changes to \tex{uchyph} +become effective immediately, not at the end of the current partial +paragraph. + +Typeset boxes now always have their language information embedded in +the nodes themselves, so there is no longer a possible dependancy on +the surrounding language settings. In \TEX82, a mid-paragraph +statement like \type{\unhbox0} would process the box using the current +paragraph language unless there was a \tex{setlanguage} issued inside +the box. In \LUATEX, all language variables are already frozen. + + +\section{The main control loop} + +In \LUATEX's main loop, almost all input characters that are to be +typeset are converted into \type{glyph_node} records with subtype +\quote{character}, but there are a few small exceptions. + +First, the \tex{accent} primitives creates nodes with subtype \quote{glyph} +instead of \quote{character}: one for the actual accent and one for the +accentee. The primary reason for this is that \tex{accent} in \TEX82 +is explicitly dependent on the current font encoding, so it would not +make much sense to attach a new meaning to the primitive's name, as +that would invalidate many old documents and macro packages. A +secondary reason is that in \TEX82, \tex{accent} prohibits hyphenation +of the current word. Since in \LUATEX\ hyphenation only takes place on +\quote{character} nodes, it is possible to achieve the same effect. + +This change of meaning did happen with \tex{char}, that now generates +\quote{character} nodes, consistent with its changed meaning in \XETEX. +The changed status of \tex{char} is not yet finalized, but if it stays +as it is now, a new primitive \tex{glyph} should be added to directly +insert a font glyph id. + +Second, all the results of processing in math mode eventually become +nodes with \quote{glyph} subtypes. + +Third, the \ALEPH-derived commands \tex{leftghost} and +\tex{rightghost} create nodes of a third subtype: \quote{ghost}. These nodes +are ignored completely by all further processing until the stage where +inter-glyph kerning is added. + +Fourth, automatic discretionaries are handled differently. \TEX82 +inserts an empty discretionary after sensing an input character that +matches the \tex{hyphenchar} in the current font. This test is wrong, +in our opinion: wether or not hyphenation takes place should not +depend on the current font, it is a language property. + +In \LUATEX, it works like this: if \LUATEX\ senses a string of input +characters that matches the value of the new integer parameter +\tex{exhyphenchar}, it will insert an empty discretionary after that +series of nodes. Initex sets the \type{\exhyphenchar=`\-}. +Incidentally, this is a global parameter instead of a +language-specific one because it may be useful to change the value +depending on the document structure instead of the text language. + +The exact status and meaning of \tex{hyphenchar} is still under +consideration, it will probably become used in the character to glyph +conversion stage. Currently it is simply ignored. + +Fifth, \tex{setlanguage} no longer creates whatsits. The meaning of +\tex{setlanguage} is changed so that it is now an integer parameter +like all others. That integer parameter is used in \tex{glyph_node} +creation to add language information to the glyph nodes. In +conjunction, the \tex{language} primitive is extended so that it +always also updates the value of \tex{setlanguage}. + +Sixth, the \tex{noboundary} command (this command prohibits word +boundary processing where that would normally take place) now does +create whatsits. These whatsits are needed because the exact place of +the \tex{noboundary} command in the input stream has to be retained +until after the ligature and font processing stages. + +Finally, there is no longer a \type{main_loop} label in the +code. Remember that \TEX82 did quite a lot of processing while adding +\type{char_nodes} to the horizontal list? For speed reasons, it handled +that processing code outside of the \quote{main control} loop, and only the +first character of any \quote{word} was handled by that \quote{main control} loop. +In \LUATEX, there is no longer a need for that (all hard work is done +later), and the (now very small) bits of character-handling code have +been moved back inline. When \tex{tracingcommands} is on, this is +visible because the full word is reported, instead of just the initial +character. + + +\section[patternsexceptions]{Loading patterns and exceptions} + +The hyphenation algorithm in \LUATEX\ is quite different from the one +in \TEX82, although it uses essentially the same user input. + +After expansion, the argument for \tex{patterns} has to be proper +UTF-8, no \tex{char} or \tex{chardef-ed} commands are allowed. (The +current implementation is even more strict, and will reject all +non-unicode characters, but that will be changed in the future. For +now, the generated errors are a valuable tool in discovering +font-encoding specific pattern files) + +Likewise, the expanded argument for \tex{hyphenation} also has to be +proper UTF-8, but here a tiny little bit of extra syntax is provided: + +\startitemize[n] +\item three sets of arguments in curly braces (\type{{}{}{}}) + indicates a desired complex discretionary, with arguments + as in \tex{discretionary}'s command in normal document input. +\item \type{-} indicates a desired simple discretionary, cf. \type{\-} and + \type{\discretionary{-}{}{}} in normal document input. +\item Internal command names are ignored. This rule is provided + especially for \tex{discretionary}, but it also helps to deal with + \tex{relax} commands that may sneak in. +\item \type{=} indicates a hyphen in the document input (but that + is only useful in documents where \tex{exhyphenchar} is not equal to + the hyphen). +\stopitemize + +The expanded argument is first converted back to a space-separated +string while dropping the internal command names. This string is then +converted into a dictionary by a routine that creates key||value pairs +by converting the other listed items. It is important to note that the +keys in an exception dictionary can always be generated from the +values. Here are a few examples: + +\starttabulate[|l|l|l|] +\NC \ssbf value \NC \ssbf implied key (input) \NC \ssbf effect\NC\NR +\NC \type{ta-ble} \NC table \NC \type{ta\-ble} + ($=$ \type{ta\discretionary{-}{}{}ble})\NC\NR +\NC \type{ba{k-}{}{c}ken}\NC backen \NC \type{ba\discretionary{k-}{}{c}ken}\NC\NR +\stoptabulate + +The resultant patterns and exception dictionary will be stored under +the language code that is the present value of \tex{language}. + +In the last line of the table, you see there is no \tex{discretionary} +command in the value: the command is optional in the \TEX-based input +syntax. The underlying reason for that is that it is conceivable that +a whole dictionary of words is stored as a plain text file and loaded +into \LUATEX\ using one of the functions in the Lua \luatex{lang} +library. This loading method is quite a bit faster than going through +the \TEX\ language primitives, but some (most?) of that speed gain +would be lost if it had to interpret command sequences while doing so. + +The motivation behind the \ETEX\ extension \tex{savinghyphcodes} was +that hyphenation heavily depended on font encodings. This is no longer +true in \LUATEX, and the corresponding primitive is ignored pending +complete removal. The future semantics of \tex{uppercase} and +\tex{lowercase} are still under consideration, no changes have taken +place yet. + + +\section{Applying hyphenation} + +The internal structures \LUATEX\ uses for the insertion of +discretionaries in words is very different from the ones in \TEX82, +and that means there are some noticable differences in handling as +well. + +First and foremost, there is no \quote{compressed trie} involved in +hyphenation. The algorithm still reads \PATGEN-generated pattern +files, but \LUATEX\ uses a finite state hash to match the patterns +against the word to be hyphenated. This algorithm is based on the +\quote{libhnj} library used by OpenOffice. +The memory allocation for this new implementation is completely +dynamic, so the \WEBC\ setting for \type{trie_size} is ignored. + +Differences between \LUATEX\ and \TEX82 that are a direct result of that: + +\startitemize +\item \LUATEX\ happily hyphenates the full Unicode character range. +\item Pattern and exception dictionary size is limited by the + available memory only, all allocations are done dynamically. + The trie-related settings in \type{texmf.cnf} are ignored. +\item Because there is no \quote{trie preparation} stage, language patterns + never become frozen. This means that the primitive \tex{patterns} + (and its lua counterpart \luatex{lang.patterns}) can be used at any + time, not only in initex. +\item Only the string representation of \tex{patterns} and + \tex{hyphenation} is stored in the format file. At format load time, + they are simply re-evaluated. It follows that there is no real + reason to preload languages in the format file. In fact, it is + usually not a good idea to do so. It is much smarter to load + patterns no sooner than the first time they are actually needed. +\item \LUATEX\ uses the language-specific variables +\tex{prehyphenchar} and \tex{posthyphenchar} in the creation of +discretionaries, instead of \TEX82's \tex{hyphenchar}. +\stopitemize + +Previously, there were problems with changing the node attributes +mid-word, but that problem is now solved, as nodes in a word are not +converted to and from a string any more (this was required by the old +hyphenation code), they are edited in place. Inserted characters and +ligatures inherit their attributes from the nearest glyph node item +(usually the preceding one, but the following one for the items +inserted at the left-hand side of a word). + +Word boundaries are no longer implied by font switches, but by +language switches. One word can have two separate fonts and still be +hyphenated correctly (but it can not have two different languages, +the \tex{setlanguage} command forces a word boundary). + +All languages start out with \type{\prehyphenchar=`\-} and +\type{\posthyphenchar=0}. When you assign the values of +\tex{prehyphenchar} and \tex{posthyphenchar}, you are actually +changing the settings for the current \tex{language}, this behavior +is compatible with \tex{patterns} and \tex{hyphenation}. + +\LUATEX\ also hyphenates the first word in a paragraph. + +Words can be up to 256 characters long (up from 64 in \TEX82). Longer +words generate an error right now, but eventually either the +limitation will be removed or perhaps it will become possbile to +silently ignore the excess characters (this is what happens in \TEX82, +but there the behavior cannot be controlled). + +If you are using the Lua function \type{lang.hyphenate}, you should be +aware that this function expects to receive a list of \quote{character} +nodes. It will not operate properly in the presence of \quote{glyph}, +\quote{ligature}, or \quote{ghost} nodes, nor does it know how to deal with +kerning. In the near future, it will be able to skip over \quote{ghost} +nodes, and we may add a less fuzzy function you can call as well. + +The hyphenation exception dictionary is maintained as key-value +hash, and that is also dynamic, so the \type{hyph_size} setting is not +used either. + +A technical paper detailing the new algorithm will be released as a +separate document. + +\section{Applying ligatures and kerning} + +After all possible hyphenation points have been inserted in the list, +\LUATEX\ will process the list to convert the \quote{character} nodes into +\quote{glyph} and \quote{ligature} nodes. This is actually done in two stages: +first all ligatures are processed, then all kerning information is +applied to the result list. But those two stages are somewhat +dependent on each other: If the used font makes it possible to do so, +the ligaturing stage adds virtual \quote{character} nodes to the word +boundaries in the list. While doing so, it removes and interprets +\type{noboundary} nodes. The kerning stage deletes those word boundary +items after it is done with them, and it does the same for \quote{ghost} +nodes. Finally, at the end of the kerning stage, all remaining +\quote{character} nodes are converted to \quote{glyph} nodes. + +This work separation is worth mentioning because, if you overrule from +Lua only one of the two callbacks related to font handling, then you +have to make sure you perform the tasks normally done by \LUATEX\ +itself in order to make sure that the other, non-overrruled, routine +continues to function properly. + +Work in this area is not yet complete, but most of the possible cases +are handled by our rewritten ligaturing engine. We are working hard to +make sure all of the possible inputs will become supported soon. + +For example, take the word \type{office}, hyphenated \type{of-fice}, +using a \quote{normal} font with all the \type{f}-\type{i} ligatures: +\starttabulate[|l|l|] +\NC Initial: \NC \type{{o}{f}{f}{i}{c}{e}} \NC\NR +\NC After hyphenation: \NC \type{{o}{f}{{-},{},{}}{f}{i}{c}{e}}\NC\NR +\NC First ligature stage: \NC \type{{o}{{f}{-},{f},{ff}}{i}{c}{e}}\NC\NR +\NC Final result: \NC \type{{o}{{f}{-},{fi},{ffi}}{c}{e}} \NC\NR +\stoptabulate + +That's bad enough, but if there was a hyphenation point between the +\type{f} and the \type{i}: \type{of-f-ice}, the final result should be: +\starttyping + {o}{{f}{-}, + {{f}{-}, + {i}, + {fi}}, + {{ff}{-}, + {i}, + {ffi}}}{c}{e} +\stoptyping +with discretionaries in the post-break text as well as in the +replacement text of the top-level discretionary that resulted from the +first hyphenation point. And this is only a simple case. + +\section{Breaking paragraphs into lines} + +This code is still almost unchanged, but because of the +above-mentioned changes with respect to discretionaries and ligatures, +line breaking will potentially be different from traditional \TEX. +The actual line breaking code is still based on the \TEX82 algorithms, +and it does not expect there to be discretionaries inside of +discretionaries. + +But that situation is now fairly common in \LUATEX, due to the changes +to the ligaturing mechanism. And also, the \LUATEX\ discretionary +nodes are implemented slightly different from the \TEX82 nodes: the +\type{no_break} text is now embedded inside the disc node, where +previously these nodes kept their place in the horizontal list (the +discretionary node contained a counter indicating how many nodes to +skip). + +The combined effect of these two differences is that \LUATEX\ does not +always use all of the potential breakpoints in a paragraph, especially +when fonts with many ligatures are used. + +% TODO: +% Check \sfcode handling +% Implement \glyph +% +% Remove \savinghyphcodes +% Allow non-UCS characters in \patterns + +\chapter[fonts]{Font structure} + +All \TEX\ fonts are represented to \LUA\ code as tables, and +internally as C~structures. All keys in the table below are saved in +the internal font structure if they are present in the table returned +by the +\luatex{define_font} callback, or if they result from the normal \TFM|/|\VF\ +reading routines if there is no \luatex{define_font} callback defined. + +The column \quote{from \VF} means that this key will be created by the +\luatex{font.read_vf()} routine, \quote{from \TFM} means that the key will be created +by the \luatex{font.read_tfm()} routine, and \quote{used} means whether or not the +\LUATEX\ engine itself will do something with the key. + +The top|-|level keys in the table are as follows: + +\starttabulate[|l|l|l|l|l|p|] +\NC \ssbf key \NC \bf from vf \NC \bf from tfm \NC \bf used\NC \bf value type \NC \bf description \NC\NR +\NC name \NC yes \NC yes \NC yes \NC string \NC metric (file) name\NC\NR +\NC area \NC no \NC yes \NC yes \NC string \NC (directory) location, typically empty\NC\NR +\NC used \NC no \NC yes \NC yes \NC boolean\NC used already? (initial: false)\NC \NR +\NC characters \NC yes \NC yes \NC yes \NC table \NC the defined glyphs of this font \NC \NR +\NC checksum \NC yes \NC yes \NC no \NC number \NC default: 0 \NC \NR +\NC designsize \NC no \NC yes \NC yes \NC number \NC expected size (default: 655360 == 10pt) \NC \NR +\NC direction \NC no \NC yes \NC yes \NC number \NC default: 0 (LTR) \NR +\NC encodingbytes \NC no \NC no \NC yes \NC number \NC default: depends on \type {format}\NC\NR +\NC encodingname \NC no \NC no \NC yes \NC string \NC encoding name\NC\NR +\NC fonts \NC yes \NC no \NC yes \NC table \NC locally used fonts\NC \NR +\NC fullname \NC no \NC no \NC yes \NC string \NC actual (\POSTSCRIPT) name\NC\NR +\NC header \NC yes \NC no \NC no \NC string \NC header comments, if any\NC \NR +\NC hyphenchar \NC no \NC no \NC yes \NC number \NC default: TeX's \tex{hyphenchar} \NC \NR +\NC parameters \NC no \NC yes \NC yes \NC hash \NC default: 7 parameters, all zero \NC \NR +\NC size \NC no \NC yes \NC yes \NC number \NC loaded (at) size. (default: same as designsize) \NC \NR +\NC skewchar \NC no \NC no \NC yes \NC number \NC default: TeX's \tex{skewchar} \NC \NR +\NC type \NC yes \NC no \NC yes \NC string \NC basic type of this font\NC \NR +\NC format \NC no \NC no \NC yes \NC string \NC disk format type\NC \NR +\NC embedding \NC no \NC no \NC yes \NC string \NC \PDF\ inclusion\NC \NR +\NC filename \NC no \NC no \NC yes \NC string \NC disk file name\NC\NR +\NC tounicode \NC no \NC yes \NC yes \NC number \NC if 1, \LUATEX\ assumes per-glyph tounicode entries are +present in the font\NC\NR +\NC stretch \NC no \NC no \NC yes \NC number \NC the `stretch' value from \type{\pdffontexpand}\NC\NR +\NC shrink \NC no \NC no \NC yes \NC number \NC the `shrink' value from \type{\pdffontexpand}\NC\NR +\NC step \NC no \NC no \NC yes \NC number \NC the `step' value from \type{\pdffontexpand}\NC\NR +\NC auto_expand \NC no \NC no \NC yes \NC boolean\NC the `autoexpand keyword from \type{\pdffontexpand}\NC\NR +\NC expansion_factor \NC no \NC no \NC no \NC number \NC the actual expansion factor of an expanded font\NC\NR +\NC attributes \NC no \NC no \NC yes \NC string \NC the \type{\pdffontattr}\NC\NR +\stoptabulate + +The key \type{name} is always required. + +The keys \type{stretch}, \type{shrink}, \type{step} and optionally \type{auto_expand} +only have meaning when used together: they can be used to replace a post-loading +\type{\pdffontexpand} command. + +The \type{expansion_factor} is value that can be present inside a font in \type{font.fonts}. +It is the actual expansion factor (a value between \type{-shrink} and \type{stretch}, with +step \type{step}) of a font that was automatically generated by the font expansion algorithm. + +The key \type{attributes} can be used to replace \type{\pdffontattr}. + +The key \type{used} is set by the engine when a font is actively in +use, this makes sure that the font's definition is written to the +output file (\DVI\ or \PDF). The \TFM\ reader sets it to false. + +The \type{direction} is a number signalling the \quote{normal} direction for +this font. There are sixteen possibilities: + +\starttabulate[|c|c|c|c|] +\NC \bf number \NC \bf meaning \NC \bf number \NC \bf meaning \NC\NR +\NC 0 \NC LT \NC 8 \NC TT \NC\NR +\NC 1 \NC LL \NC 9 \NC TL \NC\NR +\NC 2 \NC LB \NC 10 \NC TB \NC\NR +\NC 3 \NC LR \NC 11 \NC TR \NC\NR +\NC 4 \NC RT \NC 12 \NC BT \NC\NR +\NC 5 \NC RL \NC 13 \NC BL \NC\NR +\NC 6 \NC RB \NC 14 \NC BB \NC\NR +\NC 7 \NC RR \NC 15 \NC BR \NC\NR +\stoptabulate + +These are \OMEGA|-|style direction abbreviations: the first character +indicates the \quote{first} edge of the character glyphs (the edge that is +seen first in the writing direction), the second the \quote{top} side. + +The \type{parameters} is a hash with mixed key types. There are seven +possible string keys, as well as a number of integer indices (these +start from 8 up). The seven strings are actually used instead of the +bottom seven indices, because that gives a nicer user interface. + +The names and their internal remapping: + +\starttabulate[|l|c|] +\NC \bf name \NC \bf internal remapped number \NC\NR +\NC slant \NC 1 \NC\NR +\NC space \NC 2 \NC\NR +\NC space_stretch \NC 3 \NC\NR +\NC space_shrink \NC 4 \NC\NR +\NC x_height \NC 5 \NC\NR +\NC quad \NC 6 \NC\NR +\NC extra_space \NC 7 \NC\LR +\stoptabulate + +The keys \type{type}, \type{format}, \type{embedding}, \type{fullname} and +\type{filename} are used to embed \OPENTYPE\ fonts in the result \PDF. + +The \type{characters} table is a list of character hashes indexed by +an integer number. The number is the \quote{internal code} \TEX\ knows this +character by. + +Two very special string indexes can be used also: \type{left_boundary} is a +virtual character whose ligatures and kerns are used to handle word +boundary processing. \type{right_boundary} is similar but not actually +used for anything (yet!). + +Other index keys are ignored. + +Each character hash itself is a hash. For example, here is the +character \quote{f} (decimal 102) in the font cmr10 at 10 points: + +% hahe: why this complicated notation, why not e. g. width=200250? +\starttyping +[102] = { + ['width'] = 200250, + ['height'] = 455111, + ['depth'] = 0, + ['italic'] = 50973, + ['kerns'] = { + [63] = 50973, + [93] = 50973, + [39] = 50973, + [33] = 50973, + [41] = 50973 + }, + ['ligatures'] = { + [102] = { + ['char'] = 11, + ['type'] = 0 + }, + [108] = { + ['char'] = 13, + ['type'] = 0 + }, + [105] = { + ['char'] = 12, + ['type'] = 0 + } + } +} +\stoptyping + +The following top|-|level keys can be present inside a character hash: + +\starttabulate[|l|c|c|c|l|p|] +\NC \ssbf key \NC \bf from vf \NC \bf from tfm \NC \bf used \NC \bf value type \NC \bf description \NC\NR +\NC width \NC yes \NC yes \NC yes \NC number \NC character's width, in sp (default 0) \NC\NR +\NC height \NC no \NC yes \NC yes \NC number \NC character's height, in sp (default 0) \NC\NR +\NC depth \NC no \NC yes \NC yes \NC number \NC character's depth, in sp (default 0) \NC\NR +\NC italic \NC no \NC yes \NC yes \NC number \NC character's italic correction, in sp (default zero) \NC\NR +\NC left_protruding \NC no \NC no \NC maybe \NC number \NC character's \type{\lpcode}\NC\NR +\NC right_protruding \NC no \NC no \NC maybe \NC number \NC character's \type{\rpcode}\NC\NR +\NC expansion_factor \NC no \NC no \NC maybe \NC number \NC character's \type{\efcode}\NC\NR +\NC tounicode \NC no \NC no \NC maybe \NC string \NC character's Unicode equivalent(s), in +UTF-16BE hexadecimal format\NC\NR +\NC next \NC no \NC yes \NC yes \NC number \NC the \quote{next larger} character index \NC\NR +\NC extensible \NC no \NC yes \NC yes \NC table \NC the constituent parts of an extensible recipe \NC\NR +\NC kerns \NC no \NC yes \NC yes \NC table \NC kerning information \NC\NR +\NC ligatures \NC no \NC yes \NC yes \NC table \NC ligaturing information \NC\NR +\NC commands \NC yes \NC no \NC yes \NC array \NC virtual font commands \NC\NR +\NC name \NC no \NC no \NC no \NC string \NC the character (\POSTSCRIPT) name \NC\NR +\NC index \NC no \NC no \NC yes \NC number \NC the (\OPENTYPE\ or \TRUETYPE) font glyph index \NC\NR +\NC used \NC no \NC yes \NC yes \NC boolean \NC typeset already (default: false)? \NC\NR +\stoptabulate + +The values of \type{left_protruding} and \type{right_protruding} are used only when +\type{\pdfprotrudechars} is non-zero. + +Whether or not \type{expansion_factor} is used depends on the font's global expansion +settings, as well as on the value of \type{\pdfadjustspacing}. + +The usage of \type{tounicode} is this: if this font specifies a \type{tounicode=1} at +the top level, then \LUATEX\ will construct a \type{/ToUnicode} entry for the PDF +font (or font subset) based on the character-level \type{tounicode} strings, where +they are available. If a character does not have a sensible Unicode equivalent, +do not provide a string either (no empty strings). + +If the font-level \type{tounicode} is not set, then \LUATEX\ will build up +\type{/ToUnicode} based on the \TEX\ code points you used, and any character-level +\type{tounicodes} will be ignored. {\it At the moment, the string format is exactly the +format that is expected by Adobe CMAP files (UTF-16BE in hexadecimal encoding), minus +the enclosing angle brackets. This may change in the future.} Small example: the +\type{tounicode} for a \type{fi} ligature would be \type{00660069}. + +The presence of \type{extensible} will overrule \type{next}, if that is also present. + +The \type{extensible} table is very simple: + +\starttabulate[|l|l|p|] +\NC \ssbf key \NC \bf type \NC \bf description \NC\NR +\NC top \NC number \NC \quote{top} character index \NC\NR +\NC mid \NC number \NC \quote{middle} character index \NC\NR +\NC bot \NC number \NC \quote{bottom} character index \NC\NR +\NC rep \NC number \NC \quote{repeatable} character index \NC\NR +\stoptabulate + +The \type{kerns} table is a hash indexed by character index (and +\quote{character index} is defined as either a non|-|negative integer or the +string value \type {right_boundary}), with the values the kerning to be +applied, in scaled points. + +The \type{ligatures} table is a hash indexed by character index (and +\quote{character index} is defined as either a non|-|negative integer or the +string value \type {right_boundary}), with the values being yet another small +hash, with two fields: + +\starttabulate[|l|l|p|] +\NC \ssbf key \NC \bf type \NC \bf description \NC\NR +\NC type \NC number \NC the type of this ligature command, default 0 \NC\NR +\NC char \NC number \NC the character index of the resultant ligature \NC\NR +\stoptabulate + +The \type{char} field in a ligature is required. + +The \type{type} field inside a ligature is the numerical or string value of one of the eight +possible ligature types supported by \TEX. When \TEX\ inserts a new ligature, it puts the new +glyph in the middle of the left and right glyphs. The original left and right glyphs can +optionally be retained, and when at least one of them is kept, it is also possible to move the +new \quote{insertion point} forward one or two places. The glyph that ends up to the right of the +insertion point will become the next \quote{left}. + +\starttabulate[|l|c|l|l|] +\NC \bf textual (Knuth) \NC \bf number \NC \bf string \NC result \NC\NR +\NC l + r =: n \NC 0 \NC \type{=:} \NC \|n \NC\NR +\NC l + r =:\| n \NC 1 \NC \type{=:|} \NC \|nr \NC\NR +\NC l + r \|=: n \NC 2 \NC \type{|=:} \NC \|ln \NC\NR +\NC l + r \|=:\| n \NC 3 \NC \type{|=:|} \NC \|lnr \NC\NR +\NC l + r =:\|\> n \NC 5 \NC \type{=:|>} \NC n\|r \NC\NR +\NC l + r \|=:\> n \NC 6 \NC \type{|=:>} \NC l\|n \NC\NR +\NC l + r \|=:\|\> n \NC 7 \NC \type{|=:|>} \NC l\|nr \NC\NR +\NC l + r \|=:\|\>\> n \NC 11 \NC \type{|=:|>>} \NC ln\|r \NC\NR +\stoptabulate + +The default value is~0, and can be left out. That signifies a \quote{normal} +ligature where the ligature replaces both original glyphs. In this table +the~\| indicates the final insertion point. + +The \type{commands} array is explained below. + +\section {Real fonts} + +Whether or not a \TEX\ font is a \quote{real} font that should be written to +the \PDF\ document is decided by the \type{type} value in the top|-|level +font structure. If the value is \type{real}, then this is a proper +font, and the inclusion mechanism will attempt to add the needed +font object definitions to the \PDF. + +Values for \type{type}: + +\starttabulate[|l|p|] +\NC \bf value \NC \bf description \NC\NR +\NC real \NC this is a base font \NC\NR +\NC virtual \NC this is a virtual font \NC\NR +\stoptabulate + +The actions to be taken depend on a number of different variables: + +\startitemize[packed] +\item Whether the used font fits in an 8-bit encoding scheme or not +\item The type of the disk font file +\item The level of embedding requested +\stopitemize + +A font that uses anything other than an 8-bit encoding vector has to +be written to the \PDF\ in a different way. + +The rule is: if the font table has \type {encodingbytes} set to~2, +then this is a wide font, in all other cases it isn't. The value~2 is +the default for \OPENTYPE\ and \TRUETYPE\ fonts loaded via \LUA. For +\TYPEONE\ fonts, you have to set \type {encodingbytes} to~2 +explicitly. For \PK\ bitmap fonts, wide font encoding is not +supported at all. + +If no special care is needed, \LUATEX\ currently falls back to the +mapfile|-|based solution used by \PDFTEX\ and \DVIPS. This behavior +will be removed in the future, when the existing code becomes +integrated in the new subsystem. + +But if this is a \quote{wide} font, then the new subsystem kicks in, and +some extra fields have to be present in the font structure. In this +case, \LUATEX\ does not use a map file at all. + +The extra fields are: \type{format}, \type{embedding}, \type{fullname}, +\type{cidinfo} (as explained above), \type{filename}, and the +\type{index} key in the separate characters. + +Values for \type{format} are: + +\starttabulate[|l|p|] +\NC \bf value \NC \bf description \NC\NR +\NC type1 \NC this is a \POSTSCRIPT\ \TYPEONE\ font \NC\NR +\NC type3 \NC this is a bitmapped (\PK) font \NC\NR +\NC truetype \NC this is a \TRUETYPE\ or \TRUETYPE|-|based \OPENTYPE\ font \NC\NR +\NC opentype \NC this is a \POSTSCRIPT|-|based \OPENTYPE\ font \NC\NR +\stoptabulate + +(\type{type3} fonts are provided for backward compatibility only, and do not +support the new wide encoding options.) + +Values for \type{embedding} are: + +\starttabulate[|l|p|] +\NC \bf value \NC \bf description \NC\NR +\NC no \NC don't embed the font at all \NC\NR +\NC subset \NC include and atttempt to subset the font \NC\NR +\NC full \NC include this font in its entirety \NC\NR +\stoptabulate + +It is not possible to artificially modify the transformation matrix +for the font at the moment. + +The other fields are used as follows: The \type{fullname} will be the +\POSTSCRIPT|/|\PDF\ font name. The \type{cidinfo} will be used as the +character set (the CID \type{/Ordering} and \type{/Registry} keys). The +\type{filename} points to the actual font file. If you include the +full path in the \type{filename} or if the file is in the local +directory, \LUATEX\ will run a little bit more efficient because it +will not have to re|-|run the \type{find_xxx_file} callback in that +case. + +Be careful: when mixing old and new fonts in one document, it is possible to +create \POSTSCRIPT\ name clashes that can result in printing +errors. When this happens, you have to change the \type{fullname} +of the font. + +Typeset strings are written out in a wide format using 2~bytes per +glyph, using the \type{index} key in the character information as +value. The overall effect is like having an encoding based on numbers +instead of traditional (\POSTSCRIPT) name|-|based reencoding. The way +to get the correct \type{index} numbers for \TYPEONE\ fonts is by +loading the font via \type{fontforge.open}; use the table indices as +\type{index} fields. + +This type of reencoding means that there is no longer a clear +connection between the text in your input file and the strings in the +output \PDF\ file. Dealing with this is high on the agenda. + +\section {Virtual fonts} + +You have to take the following steps if you want \LUATEX\ to treat the +returned table from \luatex{define_font} as a virtual font: + +\startitemize[packed] +\item Set the top|-|level key \type {type} to \type {virtual}. +\item Make sure there is at least one valid entry in \luatex{fonts} (see below) +\item Give a \type {commands} array to every character (see below) +\stopitemize + +The presence of the toplevel \type {type} key with the specific value +\type {virtual} will trigger handling of the rest of the special virtual +font fields in the table, but the mere existance of 'type' is enough +to prevent \LUATEX\ from looking for a virtual font on its own. + +Therefore, this also works \quote{in reverse}: if you are absolutely certain +that a font is not a virtual font, assigning the value \type{base} or +\type{real} to \type{type} will inhibit \LUATEX\ from looking for a virtual font +file, thereby saving you a disk search. + +The \luatex{fonts} is another \LUA\ array. The values are one- or two|-|key +hashes themselves, each entry indicating one of the base fonts in a +virtual font. An example makes this easy to understand + +\starttyping +fonts = { { name = 'ptmr8a', size = 655360}, + { name = 'psyr', size = 600000}, + { id = 38 } } +\stoptyping + +says that the first referenced font (index 1) in this virtual font is +\type{ptrmr8a} loaded at 10pt, and the second is \type{psyr} loaded +at a little over 9pt. The third one is previously defined font that +is known to \LUATEX\ as fontid \quote{38}. + +The array index numbers are used by the character command definitions +that are part of each character. + +The \luatex{commands} array is a hash where each item is another small array, with the first +entry representing a command and the extra items being the parameters to that command. The +allowed commands and their arguments are: + +\starttabulate[|l|l|l|p|] +\NC \bf command name \NC \bf arguments \NC \bf arg type \NC \bf description \NC\NR +\NC font \NC 1 \NC number \NC select a new font from the local \luatex{fonts} table\NC\NR +\NC char \NC 1 \NC number \NC typeset this character number from the current font, + and move right by the character's width\NC\NR +\NC node \NC 1 \NC node \NC output this node (list), and move right + by the width of this list\NC\NR +\NC slot \NC 2 \NC number \NC a shortcut for the combination of a font and char command\NC\NR +\NC push \NC 0 \NC \NC save current position\NC\NR +\NC nop \NC 0 \NC \NC do nothing \NC\NR +\NC pop \NC 0 \NC \NC pop position \NC\NR +\NC rule \NC 2 \NC 2 numbers \NC output a rule $w*h$, and move right.\NC\NR +\NC down \NC 1 \NC number \NC move down on the page\NC\NR +\NC right \NC 1 \NC number \NC move right on the page\NC\NR +\NC special \NC 1 \NC string \NC output a \tex{special} command\NC\NR +\NC comment \NC any \NC any \NC the arguments of this command are ignored\NC\NR +\stoptabulate + +Here is a rather elaborate glyph commands example: +\starttyping +... +commands = { + {'push'}, -- remember where we are + {'right', 5000}, -- move right about 0.08pt + {'font', 3}, -- select the fonts[3] entry + {'char', 97}, -- place character 97 (ASCII 'a') + {'pop'}, -- go all the way back + {'down', -200000}, -- move upwards by about 3pt + {'special', 'pdf: 1 0 0 rg'} -- switch to red color + {'rule', 500000, 20000} -- draw a bar + {'special','pdf: 0 g'} -- back to black +} +... +\stoptyping + +The default value for \type {font} is always~1 at the start of the \type{commands} array. +Therefore, if the virtual font is essentially only a re|-|encoding, then you do usually not +have create an explicit \quote{font} command in the array. + +Rules inside of \type{commands} arrays are built up using only two dimensions: +they do not have depth. For correct vertical placement, an extra \type{down} command +may be needed. + +Regardless of the amount of movement you create within the \type {commands}, +the output pointer will always move by exactly the width that was given in +the \type {width} key of the character hash. Any movements that take place +inside the \type{commands} array are ignored on the upper level. + +\subsection{Artificial fonts} + +Even in a \quote{real} font, there can be virtual characters. When \LUATEX\ encounters a \type {commands} +field inside a character when it becomes time to typeset the character, it will interpret the commands, just +like for a true virtual character. In this case, if you have created no \quote{fonts} array, then the default +(and only) \quote{base} font is taken to be the current font itself. In practice, this means that you can +create virtual duplicates of existing characters which is useful if you want to create composite characters. + +Note: this feature does {\it not\/} work the other way around. There can not be \quote{real} characters in a +virtual font! You cannot use this technique for font re-encoding either; you need a truly virtual +font for that (because characters that are already present cannot be altered). + +\subsection{Example virtual font} + +Finally, here is a plain \TEX\ input file with a virtual font demonstration: + +\startbuffer +\directlua0 { + callback.register('define_font', + function (name,size) + if name == 'cmr10-red' then + f = font.read_tfm('cmr10',size) + f.name = 'cmr10-red' + f.type = 'virtual' + f.fonts = {{ name = 'cmr10', size = size }} + for i,v in pairs(f.characters) do + if (string.char(i)):find('[tacohanshartmut]') then + v.commands = { + {'special','pdf: 1 0 0 rg'}, + {'char',i}, + {'special','pdf: 0 g'}, + } + else + v.commands = {{'char',i}} + end + end + else + f = font.read_tfm(name,size) + end + return f + end + ) +} + +\font\myfont = cmr10-red at 10pt \myfont This is a line of text \par +\font\myfontx= cmr10 at 10pt \myfontx Here is another line of text \par +\stopbuffer + +\typebuffer + +%\getbuffer + +\chapter[nodes]{Nodes} + +\section{\LUA\ node representation} + +\TEX's nodes are represented in \LUA\ as userdata object with a variable +set of fields. In the following syntax tables, such the type of such a +userdata object is represented as \syntax{<node>}. + + +The current return value of \luatex{node.types()} is: +\ctxlua { + local d = node.types() + tex.print('\\type{' .. d[0] .. '} (' .. 0 .. '), ') + for _,v in pairs(d) do + if _ > 0 then + tex.print('\\type{' .. v .. '} (' .. _ .. '), ') + end + end +} but as already mentioned, the math and alignment nodes +in this list are not supported at the moment. The useful list +is described in the next sections. + +\subsection{Auxiliary items} + +A few node|-|typed userdata objects do not occur in the \quote{normal} +list of nodes, but can be pointed to from within that list. They are +not quite the same as regular nodes, but it is easier for the library +routines to treat them as if they were. + +\subsubsection{glue_spec items} + +Skips are about the only type of data objects in traditional \TEX\ +that are not a simple value. The structure that represents the glue +components of a skip is called a \type {glue_spec}, and it has the following +accessible fields: + +\starttabulate[|lT|l|p|] +\NC \ssbf key \NC \bf type \NC \bf explanation \NC\NR +\NC width \NC number \NC \NC\NR +\NC stretch \NC number \NC \NC\NR +\NC stretch_order \NC number \NC \NC\NR +\NC shrink \NC number \NC \NC\NR +\NC shrink_order \NC number \NC \NC\NR +\stoptabulate + +These objects are reference counted, so there is actually an extra +field named \type {ref_count} as well. This item type will likely +disappear in the future, and the glue fields themselves will +become part of the nodes referencing glue items. + +\subsubsection{attribute\_list and attribute items} + +The newly introduced attribute registers are non|-|trivial, because +the value that is attached to a node is essentially a sparse array of +key|-|value pairs. + +It is generally easiest to deal with attribute lists and attributes +by using the dedicated functions in the \luatex{node} library, but +for completeness, here is the low|-|level interface. + +An \type{attribute_list} item is used as a head pointer for a list +of attribute items. It has only one user-visible field: + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC next \NC \syntax{<node>} \NC pointer to the first attribute\NC\NR +\stoptabulate + +A normal node's attribute field will point to an item of type +\type{attribute_list}, and the \type{next} field in that item will point +to the first defined \quote{attribute} item, whose \type {next} will +point to the second \quote{attribute} item, etc. + +Valid fields in \type{attribute} items: + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC next \NC \syntax{<node>} \NC pointer to the next attribute\NC\NR +\NC number \NC number \NC the attribute type id\NC\NR +\NC value \NC number \NC the attribute value\NC\NR +\stoptabulate + +\subsubsection{action item} + +Valid fields: \showfields{action} + +These are a special kind of item that only appears inside +pdf start link objects. + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC action_type \NC number \NC \NC\NR +\NC action_id \NC number or string \NC \NC\NR +\NC named_id \NC number \NC \NC\NR +\NC file \NC string \NC \NC\NR +\NC new_window \NC number \NC \NC\NR +\NC data \NC string \NC \NC\NR +\NC ref_count \NC number \NC \NC\NR +\stoptabulate + +\subsection{Main text nodes} + +These are the nodes that comprise actual typesetting commands. + +A few fields are present in all nodes regardless of their type, these are: + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC next \NC \syntax{<node>} \NC The next node in a list, or nil\NC\NR +\NC id \NC number \NC The node's type (\type{id}) number \NC\NR +\NC subtype \NC number \NC The node \type{subtype} identifier\NC\NR +\stoptabulate + +The \type{subtype} is sometimes just a stub entry. Not all nodes +actually use the \type{subtype}, but this way you can be sure that all +nodes accept it as a valid field name, and that is often handy in node +list traversal. In the following tables \type{next} and \type{id} are +not explicitly mentioned. + +Besides these three fields, almost all nodes also have an \type {attr} +field, and there is a also a field called \type{prev}. That last field +is always present, but only initialized on explicit request: when the +function \type{node.slide()} is called, it will set up the \type{prev} +fields to be a backwards pointer in the argument node list. + + +\subsubsection{hlist nodes} + +Valid fields: \showfields{hlist} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC subtype \NC number \NC unused\NC\NR +\NC attr \NC \syntax{<node>} \NC The head of the associated attribute list \NC\NR +\NC width \NC number \NC \NC\NR +\NC height \NC number \NC \NC\NR +\NC depth \NC number \NC \NC\NR +\NC shift \NC number \NC a displacement perpendicular to the + character progression direction \NC\NR +\NC glue_order \NC number \NC a number in the range 0--4, indicating + the glue order\NC\NR +\NC glue_set \NC number \NC the calculated glue ratio\NC\NR +\NC glue_sign \NC number \NC \NC\NR +\NC list \NC \syntax{<node>} \NC the body of this list\NC\NR +\NC dir \NC number \NC the direction of this box\NC\NR +\stoptabulate + +\subsubsection{vlist nodes} + +Valid fields: As for hlist, except that \quote{shift} is a displacement +perpendicular to the line progression direction. + +\subsubsection{rule nodes} + +Valid fields: \showfields{rule} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC subtype \NC number \NC unused\NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC width \NC number \NC rule size. The special value $-1073741824$ + is used for \quote{running} glue dimensions\NC\NR +\NC height \NC number \NC ' '\NC\NR +\NC depth \NC number \NC ' '\NC\NR +\NC dir \NC number \NC the direction of this rule\NC\NR +\stoptabulate + +\subsubsection{ins nodes} + +Valid fields: \showfields{ins} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC subtype \NC number \NC the insertion class\NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC cost \NC number \NC the penalty associated with this insert\NC\NR +\NC height \NC number \NC \NC\NR +\NC depth \NC number \NC \NC\NR +\NC list \NC \syntax{<node>} \NC the body of this insert\NC\NR +\NC spec \NC \syntax{<node>} \NC a pointer to the \tex{splittopskip} glue spec\NC\NR +\stoptabulate + +\subsubsection{mark nodes} + +Valid fields: \showfields{mark} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC subtype \NC number \NC unused\NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC class \NC number \NC the mark class\NC\NR +\NC mark \NC table \NC a table representing a token list\NC\NR +\stoptabulate + +\subsubsection{adjust nodes} + +Valid fields: \showfields{adjust} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC subtype \NC number \NC 0 = normal, 1 = \quote{pre}\NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC list \NC \syntax{<node>} \NC adjusted material\NC\NR +\stoptabulate + +\subsubsection{disc nodes} + +Valid fields: \showfields{disc} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC subtype \NC number \NC indicates the source of a discretionary. + 0 = the \tex{discretionary} command, + 1 = the \tex{-} command, + 2 = added automatically following a \type{-}, + 3 = added by the hyphenation algorithm\NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC pre \NC \syntax{<node>} \NC pointer to the pre|-|break text\NC\NR +\NC post \NC \syntax{<node>} \NC pointer to the post|-|break text\NC\NR +\NC replace \NC \syntax{<node>} \NC pointer to the no|-|break text\NC\NR +\stoptabulate + +\subsubsection{math nodes} + +Valid fields: \showfields{math} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC subtype \NC number \NC 0 = \quote{on}, 1 = \quote{off}\NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC surround \NC number \NC width of the \tex{mathsurround} kern\NC\NR +\stoptabulate + +\subsubsection{glue nodes} + +Valid fields: \showfields{glue} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC subtype \NC number \NC 0 = \tex{skip}, + 1--18 = internal glue parameters, + 100 = \tex{leaders}, + 101 = \tex{cleaders}, + 102 = \tex{xleaders} \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC spec \NC \syntax{<node>} \NC pointer to a glue\_spec item \NC\NR +\NC leader \NC \syntax{<node>} \NC pointer to a box or rule for leaders\NC\NR +\stoptabulate + +\subsubsection{kern nodes} + +Valid fields: \showfields{kern} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC subtype \NC number \NC 0 = from font, + 1 = from \tex{kern} or \tex{/}, + 2 = from \tex{accent}\NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC kern \NC number \NC \NC\NR +\stoptabulate + + +\subsubsection{penalty nodes} + +Valid fields: \showfields{penalty} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC subtype \NC number \NC not used\NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC penalty \NC number \NC \NC\NR +\stoptabulate + +\subsubsection[glyphnodes]{glyph nodes} + +Valid fields: \showfields{glyph} + +\newbox\mybox +\setbox\mybox=\hbox{\vbox {\hsize=3cm +\starttabulate[|l|l|] +\NC bit 0 \NC character \NC\NR +\NC bit 1 \NC glyph \NC\NR +\NC bit 2 \NC ligature \NC\NR +\NC bit 3 \NC ghost \NC\NR +\NC bit 4 \NC left \NC\NR +\NC bit 5 \NC right \NC\NR +\stoptabulate +}} + +\starttabulate[|lT|l|p|] +\NC \ssbf field\NC \ssbf type \NC \ssbf explanation \NC\NR +\NC subtype \NC number \NC bitfield, with bits:\crlf + \copy\mybox\NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC char \NC number \NC \NC\NR +\NC font \NC number \NC \NC\NR +\NC lang \NC number \NC \NC\NR +\NC left \NC number \NC \NC\NR +\NC right \NC number \NC \NC\NR +\NC uchyph \NC boolean \NC \NC\NR +\NC components \NC \syntax{<node>} \NC pointer to ligature components\NC\NR +\NC xoffset \NC number \NC \NC\NR +\NC yoffset \NC number \NC \NC\NR +\stoptabulate + +See \in{section}[charsandglyphs] for a detailed description of the +\type{subtype} field. + +\subsubsection{margin\_kern nodes} + +Valid fields: \showfields{margin_kern} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC subtype \NC number \NC 0 = left side, + 1 = right side\NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC width \NC number \NC \NC\NR +\NC glyph \NC \syntax{<node>} \NC \NC\NR +\stoptabulate + +\subsection{whatsit nodes} + +Whatsit nodes come in many subtypes that you can ask for by running +\luatex{node.whatsits()}: +\ctxlua {for _,v in pairs(node.whatsits()) do +tex.print('\\type{' .. v .. '} (' .. _ .. '), ') end } + +\subsubsection{open nodes} + +Valid fields: \showfields{whatsit,open} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC stream \NC number \NC \TEX's stream id number\NC\NR +\NC name \NC string \NC file name \NC\NR +\NC ext \NC string \NC file extension \NC\NR +\NC area \NC string \NC file area \NC\NR +\stoptabulate + +\subsubsection{write nodes} + +Valid fields: \showfields{whatsit,write} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC stream \NC number \NC \TEX's stream id number\NC\NR +\NC data \NC table \NC a table representing the token list to be written\NC\NR +\stoptabulate + +\subsubsection{close nodes} + +Valid fields: \showfields{whatsit,close} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC stream \NC number \NC \TEX's stream id number\NC\NR +\stoptabulate + +\subsubsection{special nodes} + +Valid fields: \showfields{whatsit,special} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC data \NC string \NC the \tex{special} information\NC\NR +\stoptabulate + +\subsubsection{language nodes} + + +\LUATEX\ does not have language whatsits any more. All language +information is already present inside the glyph nodes themselves. +This whatsit subtype will be removed in the next release. + + +\subsubsection{local_par nodes} + +Valid fields: \showfields{whatsit,local_par} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC pen_inter \NC number \NC interline penalty\NC\NR +\NC pen_broken\NC number \NC broken penalty\NC\NR +\NC dir \NC number \NC the direction of this par\NC\NR +\NC box_left \NC \syntax{<node>} \NC the \tex{localleftbox}\NC\NR +\NC box_left_width\NC number\NC width of the \tex{localleftbox}\NC\NR +\NC box_right \NC \syntax{<node>} \NC the \tex{localrightbox}\NC\NR +\NC box_right_width\NC number\NC width of the \tex{localrightbox}\NC\NR +\stoptabulate + +\subsubsection{dir nodes} + +Valid fields: \showfields{whatsit,dir} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC dir \NC number \NC the direction\NC\NR +\NC level \NC number \NC nesting level of this direction whatsit\NC\NR +\NC dvi_ptr \NC number \NC a saved dvi buffer byte offset\NC\NR +\NC dir_h \NC number \NC a saved dvi position\NC\NR +\stoptabulate + +\subsubsection{pdf_literal nodes} + +Valid fields: \showfields{whatsit,pdf_literal} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC mode \NC number \NC the \quote{mode} setting of this literal\NC\NR +\NC data \NC string \NC the \tex{pdfliteral} information\NC\NR +\stoptabulate + +\subsubsection{pdf_refobj nodes} + +Valid fields: \showfields{whatsit,pdf_refobj} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC objnum \NC number \NC the referenced \PDF\ object number\NC\NR +\stoptabulate + +\subsubsection{pdf_refxform nodes} + +Valid fields: \showfields{whatsit,pdf_refxform}. + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC width \NC number \NC \NC \NR +\NC height \NC number \NC \NC \NR +\NC depth \NC number \NC \NC \NR +\NC objnum \NC number \NC the referenced \PDF\ object number\NC\NR +\stoptabulate + +Be aware that \type{pdf_refxform} nodes have dimensions that are used by \LUATEX. + +\subsubsection{pdf_refximage nodes} + +Valid fields: \showfields{whatsit,pdf_refximage} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC width \NC number \NC \NC \NR +\NC height \NC number \NC \NC \NR +\NC depth \NC number \NC \NC \NR +\NC objnum \NC number \NC the referenced \PDF\ object number\NC\NR +\stoptabulate + +Be aware that \type{pdf_refximage} nodes have dimensions that are used by \LUATEX. + +\subsubsection{pdf_annot nodes} + +Valid fields: \showfields{whatsit,pdf_annot} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC width \NC number \NC \NC \NR +\NC height \NC number \NC \NC \NR +\NC depth \NC number \NC \NC \NR +\NC objnum \NC number \NC the referenced \PDF\ object number\NC\NR +\NC data \NC string \NC the annotation data\NC\NR +\stoptabulate + + +\subsubsection{pdf_start_link nodes} + +Valid fields: \showfields{whatsit,pdf_start_link} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC width \NC number \NC \NC \NR +\NC height \NC number \NC \NC \NR +\NC depth \NC number \NC \NC \NR +\NC objnum \NC number \NC the referenced \PDF\ object number\NC\NR +\NC link_attr \NC table \NC the link attribute token list\NC\NR +\NC action \NC \syntax{<node>} \NC the action to perform\NC\NR +\stoptabulate + +\subsubsection{pdf_end_link nodes} + +Valid fields: \showfields{whatsit,pdf_end_link} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\stoptabulate + +\subsubsection{pdf_dest nodes} + +Valid fields: \showfields{whatsit,pdf_dest} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC width \NC number \NC \NC \NR +\NC height \NC number \NC \NC \NR +\NC depth \NC number \NC \NC \NR +\NC named_id \NC number \NC is the dest_id a string value?\NC\NR +\NC dest_id \NC number or string \NC the destination id\NC\NR +\NC dest_type \NC number\NC type of destination\NC\NR +\NC xyz_zoom \NC number\NC \NC\NR +\NC objnum \NC number \NC the \PDF\ object number\NC\NR +\stoptabulate + +\subsubsection{pdf_thread nodes} + +Valid fields: \showfields{whatsit,pdf_thread} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC width \NC number \NC \NC \NR +\NC height \NC number \NC \NC \NR +\NC depth \NC number \NC \NC \NR +\NC named_id \NC number \NC is the tread_id a string value?\NC\NR +\NC tread_id \NC number or string \NC the thread id\NC\NR +\NC thread_attr\NC number \NC extra thread information\NC\NR +\stoptabulate + +\subsubsection{pdf_start_thread nodes} + +Valid fields: \showfields{whatsit,pdf_start_thread} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC width \NC number \NC \NC \NR +\NC height \NC number \NC \NC \NR +\NC depth \NC number \NC \NC \NR +\NC named_id \NC number \NC is the tread_id a string value?\NC\NR +\NC tread_id \NC number or string \NC the thread id\NC\NR +\NC thread_attr\NC number \NC extra thread information\NC\NR +\stoptabulate + +\subsubsection{pdf_end_thread nodes} + +Valid fields: \showfields{whatsit,pdf_end_thread} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\stoptabulate + +\subsubsection{pdf_save_pos nodes} + +Valid fields: \showfields{whatsit,pdf_save_pos} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\stoptabulate + +\subsubsection{late_lua nodes} + +Valid fields: \showfields{whatsit,late_lua} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC reg \NC number \NC \LUA\ state id number \NC\NR +\NC data \NC string \NC data to execute\NC\NR +\stoptabulate + +\subsubsection{close_lua nodes} + +Valid fields: \showfields{whatsit,close_lua} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC reg \NC number \NC \LUA\ state id number\NC\NR +\stoptabulate + +\subsubsection{pdf_colorstack nodes} + +Valid fields: \showfields{whatsit,pdf_colorstack} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC stack \NC number \NC colorstack id number\NC\NR +\NC cmd \NC number \NC command to execute\NC\NR +\NC data \NC string \NC data\NC\NR +\stoptabulate + +\subsubsection{pdf_setmatrix nodes} + +Valid fields: \showfields{whatsit,pdf_setmatrix} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC data \NC string \NC data\NC\NR +\stoptabulate + +\subsubsection{pdf_save nodes} + +Valid fields: \showfields{whatsit,pdf_save} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\stoptabulate + +\subsubsection{pdf_restore nodes} + +Valid fields: \showfields{whatsit,pdf_restore} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\stoptabulate + +\subsubsection{user_defined nodes} + +User-defined whatsit nodes can only be created and handled from Lua +code. In effect, they are an extension to the extension +mechanism. The \LUATEX\ engine will simply step over such whatsits +without ever looking at the contents. + +Valid fields: \showfields{whatsit,user_defined} + +\starttabulate[|lT|l|p|] +\NC \ssbf field \NC \bf type \NC \bf explanation \NC\NR +\NC attr \NC \syntax{<node>} \NC \NC\NR +\NC user_id \NC number \NC id number\NC\NR +\NC type \NC number \NC type of the value\NC\NR +\NC value \NC number \NC \NC\NR +\NC \NC string \NC \NC\NR +\NC \NC \syntax{<node>} \NC \NC\NR +\NC \NC table \NC \NC\NR +\stoptabulate + +The \type{type} can have one of five distinct values: + +\starttabulate[|lT|l|p|] +\NC \ssbf value \NC \bf explanation \NC\NR +\NC 97 \NC the value is an attribute node list \NC\NR +\NC 100 \NC the value is a number \NC\NR +\NC 110 \NC the value is a node list \NC\NR +\NC 115 \NC the value is a token list in string form\NC\NR +\NC 116 \NC the value is a token list in lua table form\NC\NR +\stoptabulate + + +\chapter{Modifications} + +Besides the expected changes caused by new functionality, there are a +number of not|-|so|-|expected changes. These are sometimes a side|-|effect +of a new (conflicting) feature, or, more often than not, a change +necessary to clean up the internal interfaces. + +\section{Changes from \TEX\ 3.141592} + +\startitemize + +\item See~\in{chapter}[languages] for many small changes related to paragraph + building, language handling, and hyphenation. + +\item There is no pool file, all strings are embedded during compilation. + +\item \type {plus 1 fillll} does not generate an error. The extra \quote{l} is +simply typeset. + +\item The \tex{endlinechar} can be either added (values 0 or more), or not +(negative values). If it is added, the character is always decimal 13 a/k/a +\type{^^M} a/k/a carriage return (This change may be temporary). + +\item The banner line and the statistics messages are different, as +well as many warnings and error texts. + +\stopitemize + +\section{Changes from \ETEX\ 2.2} + +\startitemize + +\item The \ETEX\ functionality is always present and enabled + (but see below about \TEXXET), so the prepended asterisk or + \type{-etex} switch for \INITEX\ is not needed. + +\item \TEXXET\ is not present, so the primitives + +\starttyping +\TeXXeTstate +\beginR +\beginL +\endR +\endL +\stoptyping + +are missing. + +\item Some of the tracing information that is output by \ETEX's \tex{tracingassigns} and + \tex{tracingrestores} is not there. + +\item Register management in \LUATEX\ uses the \ALEPH\ model, so the maximum value is 65535 + and the implementation uses a flat array instead of the mixed + flat|\&|sparse model from \ETEX. + +\item \type{savinghyphcodes} is a no-op and may possibly be removed. + See~\in{chapter}[languages] for details. + +\stopitemize + +\section{Changes from \PDFTEX\ 1.40} + +\startitemize + +\item The (experimental) support for snap nodes has been removed, because +it is much more natural to build this functionality on top of node +processing and attributes. The associated primitives that are now gone +are: \tex{pdfsnaprefpoint}, \tex{pdfsnapy}, and \tex{pdfsnapycomp}. + +\item The (experimental) support for specialized spacing around nodes +has also been removed. The associated primitives that are now gone are: +\tex{pdfadjustinterwordglue}, \tex{pdfprependkern}, and \tex{pdfappendkern}, +as well as the five supporting primitives \tex{knbscode}, \tex{stbscode}, +\tex{shbscode}, \tex{knbccode}, and \tex{knaccode}. + + + +\item A number of \quote{utility functions} is removed: + +\startcolumns[n=3] +\starttyping +\pdfelapsedtime +\pdfescapehex +\pdfescapename +\pdfescapestring +\pdffiledump +\pdffilemoddate +\pdffilesize +\pdflastmatch +\pdfmatch +\pdfmdfivesum +\pdfresettimer +\pdfshellescape +\pdfstrcmp +\pdfunescapehex +\stoptyping +\stopcolumns + +\item A few other experimental primitives are also provided without the + extra \luatex {pdf} prefix, so they can also be called like this: + +\startcolumns[n=3] +\starttyping +\primitive +\ifprimitive +\ifabsnum +\ifabsdim +\stoptyping +\stopcolumns + +\item The definitions for new didot and new cicero are patched. + +\item The \tex{pdfprimitive} is bugfixed. + +\item The \tex{pdftexversion} is set to 200. + +\stopitemize + +\section{Changes from \ALEPH\ RC4} + +\startitemize + +\item The input translations from \ALEPH\ are not implemented, the + related primitives are not available: + +\startcolumns[n=2] +\starttyping +\DefaultInputMode +\noDefaultInputMode +\noInputMode +\InputMode +\DefaultOutputMode +\noDefaultOutputMode +\noOutputMode +\OutputMode +\DefaultInputTranslation +\noDefaultInputTranslation +\noInputTranslation +\InputTranslation +\DefaultOutputTranslation +\noDefaultOutputTranslation +\noOutputTranslation +\OutputTranslation +\stoptyping +\stopcolumns + +\item A small series of bounds checking fixes to \tex{ocp} and + \tex{ocplist} has been added to prevent the system from crashing + due to array indexes running out of bounds. + +\item The \tex{hoffset} bug when \tex{pagedir TRT} is fixed, +removing the need for an explicit fix to \tex{hoffset} + +\item A bug causing \tex{fam} to fail for family numbers above + 15 is fixed. + +\item Some bits of \ALEPH\ assumed \type{0} and \type{null} were identical. +This resulted for instance in a bug that sometimes caused an eternal +loop when trying to \tex{show} a box. + +\item A fair amount of other minor bugs are fixed as well, most of these +related to \tex{tracingcommands} output. + +\item The number of possible fonts, ocps and ocplists is +smaller than their maximum \ALEPH\ value (around 5000 fonts and +30000 ocps / ocplists). + +\item The internal function \type{scan_dir()} has been renamed to +\type{scan_direction()} to prevent a naming clash. + +\item The \type{^^} notation can come in five and six item repetitions also, to +insert characters that do not fit in the BMP. + +\item Glues {\it immediately after} direction change commands are not +legal breakpoints. + +\item The \tex{ocp} and \tex{ocplist} statistics at the end of a run +are only printed if OCP's are actually used. + +\stopitemize + +\section{Changes from standard \WEBC} + +\startitemize + +\item There is no mltex + +\item There is no enctex + +\item The following commandline switches are silently ignored, even +in non|-|\LUA\ mode: + +\starttyping +-8bit +-translate-file=TCXNAME +-mltex +-enc +-etex +\stoptyping + +\item \tex{openout} whatsits are not written to the log file. + +\item Some of the so|-|called web2c extensions are hard to set up + in non|-|\KPSE\ mode because texmf.cnf is not read: \type{shell-escape} + is off (but that is not a problem because of \LUA's + \lua{os.execute}), and the paranoia checks on \type{openin} and + \type{openout} do not happen (however, it is easy for a \LUA\ script + to do this itself by overloading \lua{io.open}). + + +\stopitemize + +\chapter{Implementation notes} + +\section{Primitives overlap} + +The primitives + +\starttabulate[|l|l|] +\NC \tex{pdfpagewidth} \NC \tex{pagewidth} \NC \NR +\NC \tex{pdfpageheight}\NC \tex{pageheight} \NC \NR +\NC \tex{fontcharwd} \NC \tex{charwd} \NC \NR +\NC \tex{fontcharht} \NC \tex{charht} \NC \NR +\NC \tex{fontchardp} \NC \tex{chardp} \NC \NR +\NC \tex{fontcharic} \NC \tex{charic} \NC \NR +\stoptabulate + +are all aliases of each other. + +\section{Memory allocation} + +The single internal memory heap that traditional \TEX\ used for tokens +and nodes is split into two separate arrays. Each of these will grow +dynamically when needed. + +The \type{texmf.cnf} settings related to main memory are no longer +used (these are: \type{main_memory}, \type{mem_bot}, +\type{extra_mem_top} and \type{extra_mem_bot}). \quote{Out of main +memory} errors can still occur, but the limiting factor is now the +amount of RAM in your system, not a predefined limit. + +Also, the memory (de)allocation routines for nodes are completely +rewritten. The relevant code now lives in the C file \type{luanode.c}, +and basically uses a dozen or so avail lists instead of a +doubly|-|linked model. An extra function layer is added so that the +code can ask for nodes by type instead of directly requisitioning +a certain amount of memory words. + +Because of the split into two arrays and the resulting differences in +the data structures, some of the Pascal \WEB\ macros have been +duplicated. For instance, there are now \type{vlink} and \type{vinfo} +as well as \type{link} and \type{info}. All access to the variable +memory array is now hidden behind a macro called \type{vmem}. + +The implementation of the growth of two arrays (via reallocation) +introduces a potential pitfall: the memory arrays should never be used +as the left hand side of a statement that can modify the array in +question. + +The input line buffer and pool size are now also reallocated when +needed, and the \type{texmf.cnf} settings \type{buf_size} and +\type{pool_size} are silently ignored. + +\section{Sparse arrays} + +The \tex{mathcode}, \tex{delcode}, \tex{catcode}, +\tex{sfcode}, \tex{lccode} and \tex{uccode} tables are now +sparse arrays that are implemented in~C. They are no longer part of +the \TEX\ \quote{equivalence table} and because each had 1.1 million +entries with a few memory words each, this makes a major difference +in memory usage. + +These assignments do not yet show up when using the etex tracing +routines \tex{tracingassigns} and \tex{tracingrestores} (code +simply not written yet). + +A side|-|effect of the current implementation is that \tex{global} is +now more expensive in terms of processing than non|-|global assignments. + +See \type{mathcodes.c} and \type{textcodes.c} if you are interested in +the details. + +Also, the glyph ids within a font are now managed by means +of a sparse array and glyph ids can go up to index $2^{21}-1$. + +\section{Simple single-character csnames} + +Single|-|character commands are no longer treated specially in the +internals, they are stored in the hash just like the multiletter +csnames. + +The code that displays control sequences explicitly checks if +the length is one when it has to decide whether or not to add a +trailing space. + +\section{Compressed format} + +The format is passed through zlib, allowing it to shrink to roughly +half of the size it would have had in uncompressed form. This takes a +bit more CPU cycles but much less disk I/O, so it should still be +faster. + +\section{Binary file reading} + +All of the internal code is changed in such a way that if one of the +\type{read_xxx_file} callbacks is not set, then the file is read by +a C function using basically the same convention as the callback: a +single read into a buffer big enough to hold the entire file +contents. While this uses more memory than the previous code (that +mostly used \type{getc} calls), it can be quite a bit faster +(depending on your I/O subsystem). + +\chapter{Known bugs and limitations} + +The bugs below are going to be fixed eventually. + +The top ones will be fixed soon, but in the later items either the +actual problem is hard to find, or the code that causes the bug is +going to be replaced by a new subsystem soon anyway, or it may not +be worth the hassle and the limitations will eventually be documented. + +\startitemize + +\item The current linebreaking implementation does not yet take all +possible breakpoints into account where ligatures are involved in +the process. This means that line breaks may change in future +versions. + +\item Sometimes font loading via fontforge generates a message like this +\starttyping +Bad call to gww_iconv_open, neither arg is UCS4 (EUC-CN->UTF-8) +\stoptyping +during font loading. This is a limitation of the internal iconv +implementation. + +\item Font expansion does not work quite as it should. On the mailing +list (sep 21), Jonathan Sauer posted a very nice test file along with +an explanation. + +\item \luatex{tex.print()} and \luatex{tex.sprint()} do not work if +\tex{directlua} is used in an \OTP\ file (in the output of an +\type{expression} rule). + +\item Handling of attributes in math mode is not complete. The data +structures in math mode are quite different from those in text mode, +so this will take some extra effort to implement correctly. + +\item When used inside \type{\directlua}, \type{pdf.print()} should create a +literal node instead of flushing immediately. + +\item At the moment, only characters in plane~0 and plane~1 can be +assigned catcode~13 (i.\,e.\ turned into active characters). This is a +temporary measure to reduce the memory requirements of \LUATEX. In +general, \LUATEX's memory footprint is a bit larger that we would +like (with \type{plain.fmt} preloaded it needs about 55MB). + +\item Not all of \ALEPH's direction commands are handled properly in +\PDF\ mode, and especially the vertical scripts support is missing +almost completely (only TRT and TLT are routinely tested). + +\item Letter spacing (\tex{letterspacefont}) is currently non|-|functional +due to massive changes in the virtual font handling. This +functionality may actually be removed completely in the future, +because it is straightforward to set up letterspacing using the \LUA\ +\quote{define_font} interface. + +\item Node pointers are not always checked for validity, so if you make a +mistake in the node list processing, \LUATEX\ may terminate itself with an +assertion error or \quote{Emergency stop}. + +\item In \DVI\ generation mode, using a \type{\textdir} switch inside the +preamble of a \type{\halign} results in overprinted text in the \DVI\ file, +because the column width is not taken into account during the final +placement phase (this is a bug inherited from \ALEPH). Also, \ALEPH\ +apparently dislikes having more than one non-grouped \type{\textdir} +command in a single lined paragraph. + +\item Certain constructs in math mode leak memory nodes. + +\stopitemize + + +\chapter{TODO} + +On top of the \quote{normal} extensions that are planned, there are +some more specific small feature requests. Whether these will all be +included is not certain yet, (and new requests are welcome). + +\startitemize + +\item Implement the \TEX\ primitive \tex{dimension}, cf.\ \tex{number}. + +\item Change the \LUA\ table \type{tex.dimen} to accept and return float +values instead of strings. + +\item Do something about \tex{withoutpt} and/or a new register type \tex{real}? + +\item Create callback for the automatic creation of missing +characters in fonts. + +\item Implement the \TEX\ primitive \tex{htdp}? + +\item Do boxes with dual baselines. + +\item A way to (re?)calculate the width of a \tex{vbox}, taking only +the natural width of the included items into account. + +\item Make the number of the output box configurable. + +\item Complete the attributes in math and switch all the nodes to a double|-|linked list. + +\item Finish the interface from Lua to \TeX's internals, specially the hash and +equivalence table (a small subpart is implementing \type{\csname} +lookups for \type{tex.box} access). + +\item Use of Type1C for embedded PostScript font subsets in +traditional 8-bit encodings. + +\item Support font reencoding of 8-bit fonts via char index instead of +via map files. + +\item Attempt to parse OFM level~0 fonts that are masquerading as level~1. + +\item Add line numbers and input context information to the lua errors. +\stopitemize + +\stoptext |