summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Build/source/libs/luafontforge/src/luafflib.c22
-rw-r--r--Build/source/libs/luapeg/lpeg.c410
-rw-r--r--Build/source/texk/web2c/luatexdir/font/vfovf.c4
-rw-r--r--Build/source/texk/web2c/luatexdir/font/vfpacket.c10
-rw-r--r--Build/source/texk/web2c/luatexdir/font/writefont.c3
-rw-r--r--Build/source/texk/web2c/luatexdir/image/epdf.h7
-rw-r--r--Build/source/texk/web2c/luatexdir/image/image.h24
-rw-r--r--Build/source/texk/web2c/luatexdir/image/pdftoepdf.cc382
-rw-r--r--Build/source/texk/web2c/luatexdir/image/writeimg.c167
-rw-r--r--Build/source/texk/web2c/luatexdir/image/writejbig2.c3
-rw-r--r--Build/source/texk/web2c/luatexdir/lang/hyphen.c6
-rw-r--r--Build/source/texk/web2c/luatexdir/lang/texlang.c21
-rw-r--r--Build/source/texk/web2c/luatexdir/lua/lfontlib.c13
-rw-r--r--Build/source/texk/web2c/luatexdir/lua/llanglib.c3
-rw-r--r--Build/source/texk/web2c/luatexdir/lua/llualib.c12
-rw-r--r--Build/source/texk/web2c/luatexdir/lua/lmplib.c60
-rw-r--r--Build/source/texk/web2c/luatexdir/lua/lpdflib.c121
-rw-r--r--Build/source/texk/web2c/luatexdir/lua/ltexlib.c48
-rw-r--r--Build/source/texk/web2c/luatexdir/lua/luatoken.c10
-rw-r--r--Build/source/texk/web2c/luatexdir/lua/mp.w79
-rw-r--r--Build/source/texk/web2c/luatexdir/lua/psout.w23
-rw-r--r--Build/source/texk/web2c/luatexdir/luatex.web83
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/linebreak.c3220
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/postlinebreak.c924
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texnodes.c3066
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texpdf.c246
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/textoken.c1190
-rw-r--r--Master/texmf-dist/doc/luatex/luatexref-env.tex312
-rw-r--r--Master/texmf-dist/doc/luatex/luatexref-t.pdfbin0 -> 446811 bytes
-rw-r--r--Master/texmf-dist/doc/luatex/luatexref-t.tex6422
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
new file mode 100644
index 00000000000..69a6986e78c
--- /dev/null
+++ b/Master/texmf-dist/doc/luatex/luatexref-t.pdf
Binary files differ
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