summaryrefslogtreecommitdiff
path: root/macros/plain/contrib/pitex
diff options
context:
space:
mode:
Diffstat (limited to 'macros/plain/contrib/pitex')
-rw-r--r--macros/plain/contrib/pitex/README38
-rw-r--r--macros/plain/contrib/pitex/base.ptxlua85
-rw-r--r--macros/plain/contrib/pitex/blocks.ptx40
-rw-r--r--macros/plain/contrib/pitex/files.ptx39
-rw-r--r--macros/plain/contrib/pitex/fonts.ptx308
-rw-r--r--macros/plain/contrib/pitex/fonts.ptxlua1211
-rw-r--r--macros/plain/contrib/pitex/foundry-settings.lua19
-rw-r--r--macros/plain/contrib/pitex/i-pitex.lua163
-rw-r--r--macros/plain/contrib/pitex/inserts.ptx113
-rw-r--r--macros/plain/contrib/pitex/lua.ptx26
-rw-r--r--macros/plain/contrib/pitex/output.ptx311
-rw-r--r--macros/plain/contrib/pitex/pitex-doc.pdfbin0 -> 96941 bytes
-rw-r--r--macros/plain/contrib/pitex/pitex-doc.tex106
-rw-r--r--macros/plain/contrib/pitex/pitex-doc.txt1057
-rw-r--r--macros/plain/contrib/pitex/pitex.tex236
-rw-r--r--macros/plain/contrib/pitex/references.ptx107
-rw-r--r--macros/plain/contrib/pitex/sections.ptx332
-rw-r--r--macros/plain/contrib/pitex/verbatim.ptx128
18 files changed, 4319 insertions, 0 deletions
diff --git a/macros/plain/contrib/pitex/README b/macros/plain/contrib/pitex/README
new file mode 100644
index 0000000000..13fd5c7192
--- /dev/null
+++ b/macros/plain/contrib/pitex/README
@@ -0,0 +1,38 @@
+This is piTeX, a set of macros I (Paul Isambert) use to
+typeset documentations for my packages (that's why it is
+archived on CTAN).
+
+Perhaps in the future, when this achieves some kind of
+format-like completude, it'll be publicly announced. In the
+meanwhile, a documentation exists (pitex-doc.pdf, also readable
+in a text editor as pitex-doc.txt).
+
+You can of course use those macros, but you are on your
+own, and the files will probably be modified without announcement.
+The file is supposed to be \input on plain TeX with LuaTeX, at least v.0.7.
+
+The files needed are:
+
+texapi.tex (an independent package for programming)
+yax.tex (an independent package for key=value interface)
+gates.tex and gates.lua (an independant package for overall architecture)
+navigator.tex (an independant package for PDF features)
+lua.ptx and base.ptxlua (Lua side)
+files.ptx (file management)
+fonts.ptx, fonts.ptxlua and foundry-settings.lua
+ (fonts, should be independant some day; actually
+ fonts.ptxlua can be used independantly, but there is
+ no doc)
+sections.ptx (sectionning commands)
+blocks.ptx (text blocks)
+references.ptx (labels and references)
+verbatim.ptx (typesetting verbatim)
+inserts.ptx (footnotes and figures, a mess, like most files here anyway)
+output.ptx (output routine)
+
+The file i-pitex.lua is needed only to typeset the documentation
+with the Interpreter package.
+
+Date: November 2011.
+
+Licensing of these files is covered by LPPL.
diff --git a/macros/plain/contrib/pitex/base.ptxlua b/macros/plain/contrib/pitex/base.ptxlua
new file mode 100644
index 0000000000..615632db0d
--- /dev/null
+++ b/macros/plain/contrib/pitex/base.ptxlua
@@ -0,0 +1,85 @@
+require"gates.lua"
+
+pitex = gates.new("pitex")
+pitex.misc = gates.new("pitex.misc")
+
+function pitex.log (message, ...)
+ texio.write_nl(string.format("\n" .. message .. "\n", ...))
+end
+function pitex.error (...)
+ tex.error ("! PiTeX error: " .. string.format(...) .. ".")
+end
+
+pitex.callback = gates.new("pitex.callback")
+
+-- Creates a gate with a callback's name and put it in that callback, if not
+-- already there. Adds the subgate(s).
+function pitex.callback.register (c, f)
+ if pitex.callback.type(c) == 2 then
+ pitex.callback.add(f, c)
+ else
+ pitex.callback.list{c}
+ pitex.callback.add(f, c)
+ callback.register(c, pitex.callback.execute[c])
+ end
+end
+
+-- Latin1 to UTF-8.
+local char = unicode.utf8.char
+local function convert_char (ch)
+ return char(string.byte(ch))
+end
+function pitex.callback.convert (buf)
+ return string.gsub(buf,".",convert_char)
+end
+
+pitex.callback.register("process_input_buffer", "convert")
+
+function remove_conversion ()
+ pitex.callback.close("convert", "process_input_buffer")
+end
+function restore_conversion ()
+ pitex.callback.open("convert", "process_input_buffer")
+end
+
+require("nodeinspector")
+new_inspection = nodeinspector.new_inspection
+
+local french_highmarks = {
+ string.byte("?"),
+ string.byte("!"),
+ string.byte(":"),
+ string.byte(";"),
+ }
+local french_marks = {
+ string.byte("?"),
+ string.byte("!"),
+ string.byte(":"),
+ string.byte(";"),
+ string.byte(","),
+ string.byte("."),
+ string.byte("("),
+ string.byte("["),
+ string.byte("{"),
+ }
+
+local function french_punctuation (head, ...)
+ for _, glue in ipairs(arg) do
+ if glue.id == 10 then
+ head = node.remove(head, glue)
+ node.free(glue)
+ end
+ end
+ local kern = node.new(11, 1)
+ kern.kern = tex.sp(".15em")
+ node.insert_after(head, arg[1], kern)
+end
+
+pitex.callback.french_punctuation = new_inspection(
+ french_punctuation,
+ {{id = 37, _char = french_marks}, {true, id = 37, char = french_highmarks}},
+ {{id = 10, subtype = 0}, {id = 11, subtype = 0}})
+
+pitex.callback.register("kerning", "french_punctuation")
+pitex.callback.original_kerning = node.kerning
+pitex.callback.register("kerning", "original_kerning")
diff --git a/macros/plain/contrib/pitex/blocks.ptx b/macros/plain/contrib/pitex/blocks.ptx
new file mode 100644
index 0000000000..13ba4b01e1
--- /dev/null
+++ b/macros/plain/contrib/pitex/blocks.ptx
@@ -0,0 +1,40 @@
+\long\def\newblocktype#1#2#3#4{%
+ \def#1{\ptx@newblock_pattern{#2}{#3}{#4}}%
+ }
+\newife\ifptx@newblock_group
+\long\def\ptx@newblock_pattern#1#2#3{%
+ \ifnext*
+ {\ptx@newblock_grouptrue\gobbleoneand{\ptx@newblock{#1}{#2}{#3}}}
+ {\ptx@newblock_groupfalse\ptx@newblock{#1}{#2}{#3}}%
+ }
+\long\def\ptx@newblock#1#2#3#4#5{%
+ \ifnextnospace*
+ {\gobbleoneand{\ptx@newblock_do#4{#1#5}{#2}{#3}}}
+ {\ptx@newblock_do#4{#1#5}{#2}{#3}{}}%
+ }
+\long\def\ptx@newblock_do#1#2#3#4#5#6{%
+ \ifptx@newblock_group
+ {\defcs{ptx@inner_\commandtoname#1:start}{\bgroup#2}%
+ \defcs{ptx@inner_\commandtoname#1:stop}{#4#6\egroup}}
+ {\defcs{ptx@inner_\commandtoname#1:start}{#2}%
+ \defcs{ptx@inner_\commandtoname#1:stop}{#4#6}}%
+ \defcs{ptx@inner_\commandtoname#1:continue}{#3#5}%
+ \def#1##1{%
+ \ifelseif{%
+ {\ifstring{##1}{|}} {\skipspace{\usecs{ptx@inner_\commandtoname#1:continue}}}
+ {\ifstring{##1}{/}} {\usecs{ptx@inner_\commandtoname#1:stop}}
+ {\ifstring{##1}{>}} {\skipspace{\usecs{ptx@inner_\commandtoname#1:start}}}
+ \iftrue {\usecs{ptx@inner_\commandtoname#1:start}##1}}%
+ }%
+ }
+
+\newblocktype\newblock{}{}{}
+
+\def\Indent{\quitvmode\kern\parindent}
+
+\gates new \Everypar {Everypar}
+\Everypar list {everypar} [0]
+ [noindent] ?{status = close} {{\setbox0=\lastbox}}
+\Everypar close {noindent}{everypar}
+\everypar={\Everypar execute {everypar}}
+\def\removenextindent{\Everypar ajar {noindent}{everypar}}
diff --git a/macros/plain/contrib/pitex/files.ptx b/macros/plain/contrib/pitex/files.ptx
new file mode 100644
index 0000000000..212b05fc07
--- /dev/null
+++ b/macros/plain/contrib/pitex/files.ptx
@@ -0,0 +1,39 @@
+\newread\ptx@temp_read
+\def\iffile{%
+ \ifnext[
+ {\ptx@iffile}
+ {\ptx@iffile[]}%
+ }
+\def\ptx@iffile[#1]#2{%
+ \directlua{
+ local f = kpse.find_file("\luaescapestring{#2}"\reverse\iffemptystring{#1}{, "#1"})
+ local x = f and "firstoftwo" or "secondoftwo"
+ tex.print(\the\texcatcodes, "\noexpand\\" .. x)
+ }%
+ }
+\long\def\ifffile{%
+ \ifnext[
+ {\ptx@ifffile}
+ {\ptx@ifffile[]}%
+ }
+\long\def\ptx@ifffile[#1]#2#3{%
+ \iffile[#1]{#2}{#3}{}%
+ }
+
+\long\def\inputfileor{%
+ \ifnext[
+ {\ptx@inputfileor}
+ {\ptx@inputfileor[]}%
+ }
+\long\def\ptx@inputfileor[#1]#2{%
+ \iffile[#1]{#2}{\input{#2}\relax}%
+ }
+
+\newwrite\ptx@auxfile
+\def\ptx@write_toaux{%
+ \ifnext*
+ {\gobbleoneand{\write\ptx@auxfile}}
+ {\immediate\write\ptx@auxfile}%
+ }
+
+\let\writeout\ptx@write_toaux
diff --git a/macros/plain/contrib/pitex/fonts.ptx b/macros/plain/contrib/pitex/fonts.ptx
new file mode 100644
index 0000000000..a504846d6b
--- /dev/null
+++ b/macros/plain/contrib/pitex/fonts.ptx
@@ -0,0 +1,308 @@
+\inputluafile fonts.ptxlua
+\pdfadjustspacing=2
+
+\def\currentfont{}
+\def\currentsize{normal}
+\def\currentstyle{rm}
+\def\currentweight{rg}
+\def\currentcase{lc}
+\def\makecurrentfont{%
+ \csname\currentfont @\currentsize @\currentstyle @\currentweight @\currentcase\endcsname
+ }
+\def\normalsize{%
+ \def\currentsize{normal}%
+ \makecurrentfont
+ }
+\def\small{%
+ \def\currentsize{small}%
+ \makecurrentfont
+ }
+\def\verysmall{%
+ \def\currentsize{verysmall}%
+ \makecurrentfont
+ }
+\def\smaller{%
+ \passexpanded\ifstring\currentsize{normal}%
+ {\def\currentsize{small}}
+ {\def\currentsize{verysmall}}
+ \makecurrentfont
+ }
+\def\big{%
+ \def\currentsize{big}%
+ \makecurrentfont
+ }
+\def\verybig{%
+ \def\currentsize{verybig}%
+ \makecurrentfont
+ }
+\def\bigger{%
+ \passexpanded\ifstring\currentsize{normal}%
+ {\def\currentsize{big}}
+ {\def\currentsize{verybig}}
+ \makecurrentfont
+ }
+\def\it{%
+ \def\currentstyle{it}%
+ \makecurrentfont
+ }
+\def\rm{%
+ \def\currentstyle{rm}%
+ \makecurrentfont
+ }
+\def\rmstring{rm}
+\def\emph{%
+ \ifx\currentstyle\rmstring
+ \expandafter\ital
+ \else
+ \expandafter\rom
+ \fi
+ }
+\def\bf{%
+ \def\currentweight{bf}%
+ \makecurrentfont
+ }
+%\def\normalweight{%
+\def\rg{%
+ \def\currentweight{rg}%
+ \makecurrentfont
+ }
+\def\sc{%
+ \def\currentcase{sc}%
+ \makecurrentfont
+ }
+%\def\normalcase{%
+\def\lc{%
+ \def\currentcase{lc}%
+ \makecurrentfont
+ }
+\freedef\ital{{\it#1}}
+\freedef\bold{{\bf#1}}
+\freedef\scap{{\sc#1}}
+\freedef\rom{{\rm#1}}
+
+
+\restrictparameter font :
+ command % The command to be used to switch to that font.
+ name % Actually the part common to all files' names for that font.
+ size % Well, the size.
+ small % Smaller size.
+ verysmall % Smaller yet.
+ big % Bigger.
+ verybig % Insanely big.
+ bold % Modifier for bold font.
+ italic % Modifier for italic font.
+ math % If set to "true", then used for math font.
+ % small, verysmall and italic should be specified
+ % (which shows I don't use it very often).
+ features % Font features (e.g. +onum, etc.).
+ slant % A number representing an angle, loading an italic
+ % font if no real italic is a available.
+ slantsc % The same, for small caps.
+
+% The font parameter should disappear some day, it's clumsy.
+\def\setfont#1:{%
+ \setparameter font : command = #1%
+ }
+
+\setparameter metafont : % No relation :)
+ size = 10pt
+ bold = Bold
+ italic = Italic
+ features = "+onum; +liga; +trep; +tlig; expansion=30 20 5;"
+
+\setparameter font :
+ meta = metafont
+
+\newdimen\ptx@fontsize
+\luacode
+ptx_fonts = {}
+function ptx_getfont(Font, ...)
+ if ptx_fonts[Font] then
+ local tempfont = ptx_fonts[Font]
+ for _, style in ipairs(arg) do
+ if ptx_getstyle[style] then
+ tempfont = tempfont:gsub("(%w+)@(%w+)@(%w+)@(%w+)@(%w+)",ptx_getstyle[style])
+ else
+ tempfont = nil
+ texio.write_nl("! PiTeX error: Unknown font style `style'.")
+ return Font
+ end
+ end
+ return font.id(tempfont)
+ else
+ texio.write_nl("! PiTeX error: " .. Font .. " is not a PiTeX font. It can't be changed.")
+ end
+end
+ptx_getstyle = {
+ normal = "%1@normal@%3@%4@%5",
+ small = "%1@small@%3@%4@%5",
+ verysmall = "%1@verysmall@%3@%4@%5",
+ big = "%1@big@%3@%4@%5",
+ verybig = "%1@verybig@%3@%4@%5",
+ rm = "%1@%2@rm@%4@%5",
+ it = "%1@%2@it@%4@%5",
+ rg = "%1@%2@%3@rg@%5",
+ bf = "%1@%2@%3@bf@%5",
+ lc = "%1@%2@%3@%4@lc",
+ sc = "%1@%2@%3@%4@sc"
+ }
+\luacode/
+
+\newstring{+} \newstring{-}
+\def\ptx@dofont#1#2#3#4#5{%
+ \edef\ptx@temp{\passvaluenobraces\commandtoname font : command }%
+ \ifexpression{ -\ifstring{#4}{normal} & { \ifprefix-{#5} | \ifprefix+{#5} } }
+ {\def\ptx@dofont_size{\dimexpr\usevalue font : size + #5\relax}}
+ {\def\ptx@dofont_size{#5}}%
+ \ptx@dofont_load{\ptx@temp @#4@#1@#2@lc}{#3}{}{\ptx@dofont_size}%
+ \ifstring{#1}{rm}
+ {\ptx@dofont_load{\ptx@temp @#4@#1@#2@sc}{#3}{+smcp;}{\ptx@dofont_size}%
+ \ifattribute font : slant % Loading a slanted version of the font if no italic was given
+ {\ptx@dofont_load{\ptx@temp @#4@it@#2@lc}{#3}{slant=\usevalue font: slant ;}{\ptx@dofont_size}
+ \ifattribute font : slantsc {}
+ {\ptx@dofont_load{\ptx@temp @#4@it@#2@sc}{#3}{+smcp;slant=\usevalue font: slant ;}{\ptx@dofont_size}}}{}%
+ \ifattribute font : slantsc
+ {\ptx@dofont_load{\ptx@temp @#4@it@#2@sc}{#3}{+smcp;slant=\usevalue font: slantsc ;}{\ptx@dofont_size}}{}}%
+ {\ifattribute font : slantsc {}{\ptx@dofont_load{\ptx@temp @#4@it@#2@sc}{#3}{+smcp;}{\ptx@dofont_size}}}
+ }
+
+\def\ptx@dofont_load#1#2#3#4{%
+ \passcs\font{#1}="\usevalue font : name #2:#3\usevalue font : features " at #4\relax
+ \ptx@lua{ptx_fonts[font.id("#1")] = "#1"}%
+ }
+
+
+
+\def\dofont#1#2#3{%
+ \passvalue{\ptx@dofont{#1}{#2}{#3}{normal}} font : size
+ \passvalue{\ptx@dofont{#1}{#2}{#3}{small}} font : small
+ \passvalue{\ptx@dofont{#1}{#2}{#3}{verysmall}} font : verysmall
+ \passvalue{\ptx@dofont{#1}{#2}{#3}{big}} font : big
+ \passvalue{\ptx@dofont{#1}{#2}{#3}{verybig}} font : verybig
+%
+ \ifvalue font : math = true
+ {\ifstring{#1#2}{rmrg}
+ {\passcs{\textfont0=}{\passvalue\commandtoname font : command @normal@#1@#2@lc}%
+ \passcs{\scriptfont0=}{\passvalue\commandtoname font : command @small@#1@#2@lc}%
+ \passcs{\scriptscriptfont0=}{\passvalue\commandtoname font : command @verysmall@#1@#2@lc}
+ \passcs{\textfont2=}{\passvalue\commandtoname font : command @normal@#1@#2@lc}%
+ \passcs{\scriptfont2=}{\passvalue\commandtoname font : command @small@#1@#2@lc}%
+ \passcs{\scriptscriptfont2=}{\passvalue\commandtoname font : command @verysmall@#1@#2@lc}}
+ {\iffstring{#1#2}{itrg}
+ {\passcs{\textfont1=}{\passvalue\commandtoname font : command @normal@#1@#2@lc}%
+ \passcs{\scriptfont1=}{\passvalue\commandtoname font : command @verysmall@#1@#2@lc}%
+ \passcs{\scriptscriptfont1=}{\passvalue\commandtoname font : command @verysmall@#1@#2@lc}}}%
+ }{}%
+ }
+
+\gates new \FontLoader {FontLoader}
+\FontLoader list {fontloader}
+ [fl_command]
+ {%
+ \passvaluenobraces\edef font : command {%
+ \def\noexpand\currentfont{\passvalue\commandtoname font : command }%
+ \noexpand\makecurrentfont}}
+ (fl_load)
+ . ( fl_roman )
+ . . [ fl_doromanregular ]
+ {\dofont{rm}{rg}{}}
+ . . [ fl_doromanbold ] ? {conditional = -\ifvalue font : bold = none }
+ {\dofont{rm}{bf}{/\usevalue font : bold }}
+ . ( fl_italic ) ? {conditional = -\ifvalue font : italic = none }
+ . . [ fl_doitalicregular ]
+ {\dofont{it}{rg}{/\usevalue font : italic }}
+ . . [ fl_doitalicbold ] ? {conditional = -\ifvalue font : bold = none }
+ {\dofont{it}{bf}{/\usevalue font : italic /\usevalue font : bold }}
+ [fl_use] ? {conditional = \ifvalue font : command = {\mainfont} }
+ {\mainfont}
+ (fl_post)
+ . [fl_delete]
+ {\deleteparameter font:}
+ . [fl_meta]
+ {\setattribute font : meta = metafont }
+
+\defactiveparameter font {%
+ \FontLoader execute {fontloader}%
+ }
+
+
+
+
+
+
+
+\long\def\color#1#2{%
+ \pdfcolorstack0 push {#1 rg #1 RG}%
+ #2%
+ \pdfcolorstack0 pop%
+ }
+
+\newattribute\ptx@underline_attribute
+\luacode
+local do_underline = function (head,order,ratio,sign)
+ local item, leader = head, false
+ while item do
+ leader = false
+ if node.has_attribute(item,\attributenumber\ptx@underline_attribute)
+ and not (item.id == 10 and (item.subtype == 8 or item.subtype == 9)) then
+ local item_line = node.new(2)
+ item_line.depth = tex.sp("1.4pt")
+ item_line.height = tex.sp("-1pt")
+ if node.has_field(item,"spec") then
+ item_line.width = item.spec.width
+ if order == 0 then
+ if sign == 1 then
+ item_line.width = item_line.width + ratio * item.spec.stretch
+ else
+ item_line.width = item_line.width - ratio * item.spec.shrink
+ end
+ else
+ if item.spec.stretch_order > 0 or item.spec.shrink_order > 0 then
+ lualog(item.spec.stretch_order)
+ item_line.width = 1
+ item.subtype = 100
+ item.leader = item_line
+ leader = true
+ end
+ end
+ elseif node.has_field(item,"width") then
+ item_line.width = item.width
+ elseif node.has_field(item,"kern") then
+ item_line.width = item.kern
+ end
+ if leader then
+ item = item.next
+ else
+ local item_kern = node.new(11)
+ item_kern.kern = -item_line.width
+ node.insert_after(head,item,item_line)
+ node.insert_after(head,item,item_kern)
+ item = item_line.next
+ end
+ else
+ item = item.next
+ end
+ end
+end
+pitex.misc.underline = function (head)
+ for line in node.traverse_id(0,head) do
+ do_underline(line.list,line.glue_order,line.glue_set,line.glue_sign)
+ end
+ return head
+end
+pitex.callback.register("post_linebreak_filter", "pitex.misc:underline")
+pitex.callback.close("pitex.misc:underline", "post_linebreak_filter")
+\luacode/
+
+\freedef\underline{%
+ \ptx@lua{%
+ pitex.callback.ajar("pitex:underline", "post_linebreak_filter")
+ }
+% local plf = callback.list_functions("post_linebreak_filter")
+% if not plf or not plf["underline"] then
+% callback.add_function(underline, "underline", "post_linebreak_filter")
+% end}%
+ \quitvmode % Otherwise it might also underline the indentation box.
+ \ptx@underline_attribute=0\relax
+ #1\unsetattribute\ptx@underline_attribute
+ }
diff --git a/macros/plain/contrib/pitex/fonts.ptxlua b/macros/plain/contrib/pitex/fonts.ptxlua
new file mode 100644
index 0000000000..391ee9a676
--- /dev/null
+++ b/macros/plain/contrib/pitex/fonts.ptxlua
@@ -0,0 +1,1211 @@
+-- *** UTILITY FUNCTIONS ***
+local function get_locals (tab)
+ local tb = {}
+ for lib, keys in pairs(tab) do
+ keys = string.explode(keys)
+ for _, k in ipairs(keys) do
+ tb[k] = _G[lib][k]
+ end
+ end
+ return tb
+end
+
+-- str
+-- string manipulation
+local str = get_locals {string = "explode gsub match format lower upper"}
+
+-- Removes space at the beginning and end and conflates multiple spaces.
+function str.trim (s)
+ s = str.match(s, "^%s*(.-)%s*$")
+ s = str.gsub(s, "%s+", " ")
+ return s
+end
+
+-- Extracts a pattern from a string, i.e. removes it and returns it.
+-- If "full", then the entire pattern is removed, not only the captures.
+function str.extract (s, pat, full)
+ local cap = str.match(s, pat)
+ if cap then
+ if full then
+ s = str.gsub(s, pat, "")
+ else
+ s = str.gsub(s, cap, "")
+ end
+ end
+ return cap, s
+end
+-- /str
+
+
+-- lp
+-- advanced string manipulation
+lpeg.locale(lpeg)
+local lp = get_locals {lpeg = "match P C S Ct V alnum"}
+lp.space = lpeg.space^0
+-- /lp
+
+
+-- tab
+-- table manipulation
+local tab = get_locals {table = "insert remove sort"}
+
+-- Adds val to tab, creating it if necessary.
+function tab.update (tb, val)
+ tb = tb or {}
+ tab.insert(tb, val)
+ return tb
+end
+
+-- Adds subtable (empty, or val) to tb at entry key, unless it already exists.
+function tab.subtable (tb, key, val)
+ tb[key] = tb[key] or val or {}
+end
+
+-- Writes a table to an extenal file.
+local function write_key (key, ind)
+ return ind .. '["' .. key .. '"] = '
+end
+
+function tab.write (tb, f, ind)
+ for a, b in pairs (tb) do
+ if type(a) == "string" then
+ a = '"' .. a .. '"'
+ end
+ a = "[" .. a .. "]"
+ if type(b) == "table" then
+ f:write(ind, a, " = {")
+ tab.write(b, f, ind .. " ")
+ f:write(ind, "},")
+ else
+ if type(b) == "boolean" then
+ b = b and "true" or "false"
+ elseif type(b) == "string" then
+ b = '"' .. b .. '"'
+ end
+ f:write(ind, a, " = ", b, ",")
+ end
+ end
+end
+
+-- Returns a full copy of a table. Not copying of metatables necessary for the
+-- moment.
+function tab.copy (tb)
+ local t = {}
+ for k, v in pairs (tb) do
+ if type(v) == "table" then
+ v = tab.copy(v)
+ end
+ t[k] = v
+ end
+ return t
+end
+
+-- Sorts two tables containing modifiers (Italic, etc.).
+function tab.sortmods (a, b)
+ local A, B = "", ""
+ for _, x in ipairs (a) do
+ A = A .. " " .. x
+ end
+ for _, x in ipairs (b) do
+ B = B .. " " .. x
+ end
+ return A < B
+end
+
+-- Turns an array into a hash.
+function tab.tohash(tb)
+ local t = {}
+ for _, k in ipairs(tb) do
+ t[k] = true
+ end
+ return t
+end
+-- /tab
+
+-- lfs
+-- files etc.
+local lfs = get_locals {lfs = "dir isdir isfile mkdir", kpse = "expand_var show_path find_file"}
+
+-- Returns anything after the last dot, i.e. an extension.
+function lfs.extension (s)
+ return str.lower(str.match(s, "%.([^%.]*)$"))
+end
+
+local extensions = {
+ otf = "opentype",
+ ttf = "truetype",
+ ttc = "truetype",
+}
+function lfs.type (s)
+ return extensions[lfs.extension(s)]
+end
+
+local kpse_extensions = {
+ otf = "opentype fonts",
+ ttf = "truetype fonts",
+ ttc = "truetype fonts",
+}
+function lfs.kpse (s)
+ return kpse_extensions[lfs.extension(s)]
+end
+
+-- Returns anything after the last slash, i.e. a pathless file.
+function lfs.nopath (f)
+ return str.match(f, "[^/]*$")
+end
+
+-- Creates a directory; the arguments are the successive subdirectories.
+function lfs.ensure_dir (...)
+ local arg, path = {...}
+ for _, d in ipairs(arg) do
+ if path then
+ path = path .. "/" .. d
+ else
+ path = d
+ end
+ path = str.gsub(path, "//", "/")
+ if not lfs.isdir(path) then
+ lfs.mkdir(path)
+ end
+ end
+ return path
+end
+
+-- Turns "foo/blahblah/../" into "foo/" (such going into and leaving
+-- directories happens with kpse). Also puts everything to lowercase.
+function lfs.smooth_file (f)
+ f = str.gsub(f, "/.-/%.%./", "/")
+ f = str.gsub(f, "^%a", str.lower)
+ return f
+end
+-- /lfs
+
+
+-- wri
+-- messages
+local wri, write_nl = {}, texio.write_nl
+
+function wri.report (s, ...)
+ write_nl(str.format(s, unpack(arg)))
+end
+
+function wri.error (s, ...)
+ tex.error(str.format(s, unpack(arg)))
+end
+-- /wri
+
+
+-- various
+-- the last one is, of course, not the least
+local io = get_locals {io = "open lines"}
+local os = get_locals {os = "date"}
+local num = get_locals {math = "abs tan rad floor pi", tex = "sp"}
+local fl = get_locals {fontloader = "open close to_table info", font = "read_tfm"}
+-- /various
+
+
+-- *** END OF UTILITY FUNCTIONS ***
+
+
+
+--- *** CREATING THE LIBRARY *** ---
+local settings
+if lfs.find_file"foundry-settings.lua" then
+ settings = require"foundry-settings.lua"
+else
+ settings = {normal = {}, features = {}}
+end
+local font_families = {}
+local normal_names = {}
+for _, name in ipairs(settings.normal) do
+ normal_names[name] = true
+end
+local local_path = lfs.expand_var("$TEXMFLOCAL")
+local foundry_path = lfs.ensure_dir (local_path, "tex", "luatex", "foundry")
+local library_file = foundry_path .. "/" .. "readable.txt"
+--local library_file = "c:/texlive/texmf-local/tex/plain/pitex/readable.txt"
+--local library_file = "readable.txt"
+
+-- Analyze a font file and return a name and a table
+-- with modifiers.
+local function extract_font (file, names)
+ local fi, subname
+ -- Trying to open a font in ttc, using the names returned by fontloader.info
+ if names then
+ local name = names.fullname
+ if name then
+ fi = fl.open (file, name)
+ end
+ if not fi then
+ name = names.fontname
+ if name then
+ fi = fl.open
+ end
+ if not fi then
+ fl.error("Can't open %s", file)
+ return
+ end
+ end
+ subname = name
+ else
+ fi = fl.open(file)
+ end
+ -- Getting the most precise information. Not necessarily the best
+ -- solution, but since the user can modify the library, it's not so bad.
+ local fam, name = fi.names[1].names.preffamilyname or fi.names[1].names.family or fi.familyname, fi.fontname
+ local spec = fi.names[1].names.prefmodifiers or fi.names[1].names.subfamily or ""
+ local subfam, _spec
+ local t = { [0] = file }
+ -- Removing mods like Regular, Book, etc.
+ for name in pairs(normal_names) do
+ spec = str.gsub(spec, name, "")
+ end
+ if subname then
+ tab.insert(t, "[font = " .. subname .. "]")
+ end
+ if spec ~= "" then
+ spec = str.explode(spec)
+ for _, s in ipairs(spec) do
+ tab.insert(t, s)
+ end
+ end
+ fl.close(fi)
+ return fam, t
+end
+
+-- Searches directories for font files, and pass them to
+-- extract_font. The fonts are collected in a table.
+-- The fonts_done table is updated when the library is read,
+-- so when a font is missing and one needs to recheck files,
+-- only those that arent in the libraries are considered.
+local fonts_done = {}
+local function check_fonts (rep, tb)
+ for f in lfs.dir (rep) do
+ if f ~= "." and f ~= ".." then
+ f = str.gsub(rep, "/$", "") .. "/" .. f
+ if lfs.isdir(f) then
+ check_fonts(f, tb)
+ elseif lfs.isfile(f) and not fonts_done[lfs.nopath(f)] then
+ local e = lfs.extension(f)
+ if e == "ttf" or e == "otf" then
+ local fam, file = extract_font(f)
+ if fam then
+ tab.subtable(tb, fam)
+ tab.insert(tb[fam], file)
+ end
+ elseif e == "ttc" then
+ local info = fl.info(f)
+ for _, i in ipairs(info) do
+ local fam, file = extract_font(f, i)
+ if fam then
+ tab.subtable(tb, fam)
+ tab.insert(tb[fam], file)
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+-- Writes the library to an external file.
+-- Type is "a" if what's going on is recheck_fonts.
+local function write_lib (fams, file, type)
+ local read_table = {}
+ for fam, tb in pairs(fams) do
+ tab.insert(read_table, fam)
+ for _, ttb in ipairs(tb) do
+ tab.sort(ttb)
+ end
+ tab.sort(tb, tab.sortmods)
+ end
+ tab.sort(read_table)
+ local readable = io.open(file, type)
+ for n, fam in ipairs(read_table) do
+ local log
+ if type == "a" then
+ log = true
+ if n == 1 then
+ wri.report("\nAdding new font(s):")
+ readable:write("\n\n% Added automatically " .. os.date() .. "\n\n")
+ end
+ wri.report(fam)
+ end
+ readable:write(fam .. " :")
+ for n, f in ipairs(fams[fam]) do
+ log = log and " "
+ readable:write("\n ")
+ for _, t in ipairs(f) do
+ log = log and log .. " " .. t
+ readable:write(" " .. t)
+ end
+ log = log and log .. " " .. '"' .. f[0] .. '"'
+ readable:write(" " .. '"' .. lfs.nopath(f[0]) .. '",')
+ if log then wri.report(log) end
+ if n == #fams[fam] then
+ readable:write("\n\n")
+ end
+ end
+ end
+ readable:close()
+end
+
+
+-- If there is no library, we create it.
+local font_paths = lfs.show_path("opentype fonts")
+font_paths = str.gsub(font_paths, "\\", "/")
+font_paths = str.gsub(font_paths, "/+", "/")
+font_paths = str.gsub(font_paths, "!!", "")
+font_paths = str.explode(font_paths, ";+")
+
+if not lfs.find_file(library_file) then
+ wri.report("I must create the library; please wait, that can take some time.")
+ for _, rep in ipairs(font_paths) do
+ check_fonts(rep, font_families)
+ end
+ write_lib(font_families, library_file, "w")
+end
+
+-- Reads the library file, turning it into a table.
+local explode_semicolon = lp.P{
+ lp.Ct(lp.V"data"^1),
+ data = lp.C(((1 - lp.S";[") + (lp.S"[" * (1 - lp.S"]")^0 * lp.S"]" ))^1) / str.trim * (lp.S";" + -1),
+ }
+local explode_comma = lp.P{
+ lp.Ct(lp.V"data"^1),
+ data = lp.C(((1 - lp.S",[") + (lp.S"[" * (1 - lp.S"]")^0 * lp.S"]" ))^1) / str.trim * (lp.S"," + -1),
+ }
+
+local function load_library (lib)
+ local LIB = ""
+ local lib_file = lfs.find_file(lib)
+ if not lib_file then
+ wri.error("I can't find library %s.", lib)
+ return
+ end
+
+ for l in io.lines(lib_file) do
+ if not str.match(l, "^%s*%%") then
+ if str.match(l, "^%s*$") then
+ LIB = LIB .. ";"
+ else
+ LIB = LIB .. " " .. l
+ end
+ end
+ end
+ LIB = str.gsub(LIB, ";%s*;", ";;")
+ LIB = str.gsub(LIB, ";+", ";")
+ LIB = str.gsub(LIB, "^;", "")
+ LIB = str.gsub(LIB, "%s+", " ")
+
+ LIB = lp.match(explode_semicolon, LIB)
+
+ local newlib = {}
+ for _, t in ipairs(LIB) do
+ local fam, files = str.match(t, "(.-):(.*)")
+ local current_mods
+ if files then
+ files = lp.match(explode_comma, files)
+ fam = str.explode(fam, ",")
+ local root
+ for n, f in ipairs (fam) do
+ f = str.trim(f)
+ if n == 1 then
+ root = f
+ if type(newlib[f]) == "string" then
+ wri.error("Name `%s' is already used as an alias for `%s'; it is now overwritten to denote a family", f, newlib[f])
+ newlib[f] = {}
+ else
+ newlib[f] = newlib[f] or {}
+ end
+ else
+ if newlib[f] then
+ wri.error("The name `%s' is already used. I ignore it as an alias for `%s'", f, root)
+ else
+ newlib[f] = root
+ end
+ end
+ end
+ for _, f in ipairs(files) do
+ local reset
+ reset, f = str.extract(f, "^%.%.", true)
+ if reset then current_mods = nil end
+ local mods, file, feats = str.match(f, '([^"]*)"(.*)"')
+ if mods then
+ fonts_done[lfs.nopath(file)] = true
+ feats, mods = str.extract(mods, "%[([^%]]-)]", true)
+ mods = str.explode(mods)
+ if current_mods then
+ for _, t in ipairs(current_mods) do
+ tab.insert(mods, t)
+ end
+ if current_mods.feats then
+ feats = feats or ""
+ feats = current_mods.feats .. "," .. feats
+ end
+ end
+ local sizes, real_mods = {}, {}
+ for n, s in ipairs(mods) do
+ if tonumber(s) then
+ tab.insert(sizes, tonumber(s))
+ else
+ tab.insert(real_mods, s)
+ end
+ end
+ sizes = #sizes > 0 and sizes or {0}
+ tab.sort(real_mods)
+ local T = newlib[root]
+ for _, t in ipairs(real_mods) do
+ t = str.trim(t)
+ if t ~= "" then
+ T[t] = T[t] or {}
+ T = T[t]
+ end
+ end
+ T.__files = T.__files or {}
+ for _, s in ipairs(sizes) do
+ T.__files[s] = {str.trim(file), feats}
+ end
+ else
+ feats, mods = str.extract(f, "%[([^%]]-)]", true)
+ if current_mods then
+ for _, mod in ipairs(str.explode(mods)) do
+ tab.insert(current_mods, mod)
+ end
+ if feats then
+ current_mods.feats = current_mods.feats and current_mods.feats .. "," .. feats or feats
+ end
+ else
+ current_mods = str.explode(mods)
+ current_mods.feats = feats
+ end
+ end
+ end
+ end
+ end
+ return newlib
+end
+
+local library = {}
+library.default = load_library(library_file)
+
+-- Same as above, but used when rechecking (if a font isn't found in libraries).
+local function recheck_fonts ()
+ local tb = {}
+ for _, rep in ipairs(font_paths) do
+ check_fonts(rep, tb)
+ end
+ write_lib(tb, library_file, "a")
+ library.default = load_library(library_file)
+end
+
+
+-- This is public.
+function new_library (lib)
+ local l = load_library(lib)
+ if l then
+ tab.insert(library, l)
+ end
+end
+
+--- *** END OF LIBRARY MANAGEMENT *** ---
+
+
+
+--- *** FONT CREATION *** ---
+
+
+-- Creates a new substitution for trep.
+local function add_sub (f, num, sub)
+ num, sub = f.map.map[num], f.map.map[sub]
+ if f.glyphs[num] and f.glyphs[sub] then
+ local x = f.glyphs[num]
+ tab.subtable(x, "lookups")
+ x.lookups.tex_trep = { { type = "substitution", specification = {variant = f.glyphs[sub].name} } }
+ end
+end
+
+-- Creates a new ligature for tlig.
+local function add_lig (f, lig, ...)
+ lig = f.map.map[lig]
+ if lig then
+ arg = {...}
+ local components
+ for _, c in ipairs(arg) do
+ c = f.map.map[c]
+ c = c and f.glyphs[c]
+ if c then
+ c = c.name
+ components = components and components .. " " .. c or c
+ else
+ components = nil
+ break
+ end
+ end
+ if components then
+ local x = f.glyphs[lig]
+ tab.subtable(x, "lookups")
+ tab.subtable(x.lookups, "tex_tlig")
+ tab.insert(x.lookups.tex_tlig, { type = "ligature",
+ specification = {char = x.name, components = components} })
+ end
+ end
+end
+
+-- Loops over all the constitutents of ligatures, creating intermediary
+-- ligatures if necessary. E.g. "f f i" is broken into:
+-- f + f = ff.lig
+-- ff.lig + i = ffi.lig
+-- Then when loading the font if the intermediary ligatures do not exist
+-- (e.g. "1/" in "1 / 4") a phantom character is added to the font; which might
+-- be dangerous (e.g. "1/" will create a node without character if no "4" follows).
+-- The ".lig" suffix is arbitrary but all glyphs marked as ligatures are also registered
+-- with such a name, so if there's an "f f" ligature in a font, no matter its name, "ff.lig"
+-- will point to it.
+local function ligature (comp, tb, phantoms)
+ local i = str.gsub(comp[1], "%.lig$", "") .. comp[2] .. ".lig"
+ phantoms[i] = true
+ tab.insert(tb.all_ligs, i)
+ tab.subtable(tb, comp[1])
+ tb[comp[1]][comp[2]] = { char = i, type = 0 } -- The type could be something else.
+ tab.remove(comp, 1)
+ tab.remove(comp, 1)
+ if #comp > 0 then
+ tab.insert(comp, 1, i)
+ ligature(comp, tb, phantoms)
+ end
+end
+
+local function get_lookups (t, lookup_table)
+ if t then
+ for _, tb in pairs(t) do
+ local _tb = { tags = {} }
+ if tb.features then
+ for _, feats in pairs(tb.features) do
+ local _tag = {}
+ if feats.scripts then
+ for _, scr in pairs(feats.scripts) do
+ _tag[scr.script] = {}
+ for _, lang in pairs(scr.langs) do
+ tab.insert (_tag[scr.script], str.trim(lang))
+ end
+ end
+ end
+ for _, sub in pairs(tb.subtables) do
+ tab.insert(_tb, sub.name)
+ end
+ _tb.tags[feats.tag] = _tag
+ end
+ end
+ if tb.name then
+ local tp = tb.type or "no_type"
+ tab.subtable(lookup_table, tp)
+ lookup_table[tp][tb.name] = _tb
+ end
+ end
+ end
+end
+
+function create_font (filename, extension, path, subfont, write)
+
+ local data = fl.open(filename, subfont)
+ fontfile = fl.to_table(data)
+ fl.close(data)
+
+ local lookups = {}
+
+ local name_touni = { }
+ local max_char = 0
+ for chr, gly in pairs(fontfile.map.map) do
+ max_char = chr > max_char and chr or max_char
+ -- Some glyphs have the same name in some fonts,
+ -- e.g. the several hyphens.
+ local name = fontfile.glyphs[gly].name
+ while name_touni[name] do
+ name = name .. "_"
+ end
+ name_touni[name] = chr
+ end
+
+ if fontfile.gsub then
+ tab.insert(fontfile.gsub,
+ { type = "gsub_single",
+ name = "tex_trep",
+ subtables = { {name = "tex_trep"} },
+ features = { { tag = "trep"} } })
+ tab.insert(fontfile.gsub,
+ { type = "gsub_ligature",
+ name = "tex_tlig",
+ subtables = { {name = "tex_tlig"} },
+ features = { { tag = "tlig"} } })
+ for _, tb in ipairs(fontfile.gsub) do
+ for __, ttb in ipairs(tb.subtables) do
+ if tb.type == "gsub_contextchain" then
+ lookups[tb.name] = lookups[tb.name] or { type = tb.type }
+ tab.insert(lookups[tb.name], ttb.name)
+ end
+ lookups[ttb.name] = { type = tb.type }
+ lookups["-" .. ttb.name] = { type = tb.type }
+ end
+ end
+ end
+
+ if fontfile.gpos then
+ for _, tb in ipairs(fontfile.gpos) do
+ for __, ttb in ipairs(tb.subtables) do
+ lookups[ttb.name] = { type = tb.type }
+ lookups["-" .. ttb.name] = { type = tb.type }
+ end
+ end
+ end
+
+ if fontfile.kerns then
+ for _, class in ipairs(fontfile.kerns) do
+ local max = 0
+ for a in pairs (class.seconds) do
+ max = max < a and a or max
+ end
+ if type(class.lookup) == "string" then
+ lookups[class.lookup] = { type = "gpos_pair", firsts = class.firsts, seconds = class.seconds, offsets = class.offsets, max = max}
+ else
+ for _, lk in ipairs(class.lookup) do
+ lookups[lk] = { type = "gpos_pair", firsts = class.firsts, seconds = class.seconds, offsets = class.offsets, max = max}
+ end
+ end
+ end
+ end
+
+ add_sub(fontfile, 96, 8216) -- ` to quoteleft
+ add_sub(fontfile, 39, 8217) -- ' to apostrophe (quoteright)
+
+ add_lig(fontfile, 8220, 8216, 8216) -- quoteleft + quoteleft to quotedblleft
+ add_lig(fontfile, 8221, 8217, 8217) -- quoteright + quoteright to quotedblright
+ add_lig(fontfile, 8211, 45, 45) -- -- to endash
+ add_lig(fontfile, 8212, 8211, 45) -- --- (i.e. endash + -) to emdash
+ add_lig(fontfile, 161, 63, 96) -- ?` to inverted question mark
+ add_lig(fontfile, 161, 63, 8216) -- The same, with `turned to quoteleft.
+ add_lig(fontfile, 191, 33, 96) -- !` to inverted exclamation mark
+ add_lig(fontfile, 191, 33, 8216) -- Idem.
+
+ local characters, phantom_ligatures = {}, {}
+ for chr, gly in pairs(fontfile.map.map) do
+ local glyph, char = fontfile.glyphs[gly], {}
+
+ char.index = gly
+ char.name = glyph.name
+ char.width = glyph.width
+ if glyph.boundingbox then
+ char.depth = -glyph.boundingbox[2]
+ char.height = glyph.boundingbox[4]
+ end
+ if glyph.italic_correction then
+ char.italic = glyph.italic_correction
+ elseif glyph.width and glyph.boundingbox then
+ char.italic = glyph.boundingbox[3] - glyph.width
+ end
+ tab.subtable(characters, chr)
+ characters[chr] = char
+
+ if glyph.lookups then
+ for lk, tb in pairs(glyph.lookups) do
+ local _lk = "-" .. lk
+ if lookups[lk] and lookups[_lk] then
+ for _, l in ipairs(tb) do
+ if l.type == "substitution" then
+
+ tab.subtable(lookups[lk], "pairs")
+ lookups[lk].pairs[glyph.name] = l.specification.variant
+
+ tab.subtable(lookups[_lk], "pairs")
+ lookups[_lk].pairs[l.specification.variant] = glyph.name
+
+ elseif l.type == "ligature" then
+
+ local comp, lig = str.explode(l.specification.components), l.specification.char
+ local lig = ""
+ for _, c in ipairs(comp) do
+ lig = lig .. c
+ end
+
+ tab.subtable(lookups, lk)
+ tab.subtable(lookups[lk], "ligs", {all_ligs = {}})
+ ligature(comp, lookups[lk].ligs, phantom_ligatures)
+ name_touni[lig .. ".lig"] = chr
+
+ end
+ end
+ end
+ end
+ end
+
+ if glyph.kerns then
+ for _, kern in pairs(glyph.kerns) do
+ local lks = type(kern.lookup) == "table" and kern.lookup or {kern.lookup}
+ for _, lk in ipairs(lks) do
+ tab.subtable(lookups[lk], "kerns")
+ tab.subtable(lookups[lk].kerns, glyph.name)
+ lookups[lk].kerns[glyph.name][kern.char] = kern.off
+ end
+ end
+ end
+
+ end
+
+ for lig in pairs(phantom_ligatures) do
+ if not name_touni[lig] then
+ max_char = max_char + 1
+ name_touni[lig] = max_char
+ characters[max_char] = {name = lig}
+ end
+ end
+
+ local lookup_table = {}
+ get_lookups(fontfile.gsub, lookup_table)
+ get_lookups(fontfile.gpos, lookup_table)
+ get_lookups(fontfile.lookups, lookup_table, true)
+
+ if fontfile.lookups then
+ for name, lk in pairs(fontfile.lookups) do
+ local tb, format = {}, lk.format
+ for _, rule in ipairs(lk.rules) do
+ local ttb = { lookups = rule.lookups }
+ for pos, seq in pairs(rule[format]) do
+ ttb[pos] = {}
+ for _, glyfs in ipairs(seq) do
+ glyfs = str.explode(glyfs)
+ glyfs = tab.tohash(glyfs)
+ tab.insert(ttb[pos], glyfs)
+ end
+ end
+ tab.insert(tb, ttb)
+ end
+ lookups[name] = tb
+ end
+ end
+
+ local loaded_font = {
+
+ direction = 0,
+ filename = filename,
+ format = extension,
+ fullname = fontfile.names[1].names.fullname,
+ name = fontfile.fontname,
+ psname = fontfile.fontname,
+ type = "real",
+ units_per_em = fontfile.units_per_em,
+
+ auto_expand = true,
+
+ cidinfo = fontfile.cidinfo,
+
+ -- Used only to adjust absoluteslant.
+ italicangle = -fontfile.italicangle,
+ name_to_unicode = name_touni,
+ max_char = max_char,
+ lookups = lookups,
+ lookup_table = lookup_table,
+ characters = characters
+ }
+
+ if write then
+ local f = io.open(path, "w")
+ f:write("return {")
+ tab.write(loaded_font, f, "\n")
+ f:write("}")
+ f:close()
+ end
+
+ return loaded_font
+
+end
+
+-- GETTING A FONT
+
+-- Finds a font file, and returns the original
+-- file and the Lua version.
+local name_luafile, get_features
+local function is_font (name, mods, size)
+
+ local lib, library_filename
+ for l, t in ipairs(library) do
+ if t[name] then
+ library_filename = t[name]
+ lib = t
+ break
+ end
+ end
+ if not library_filename then
+ library_filename = library.default[name]
+ lib = library.default
+ end
+
+ if library_filename then
+ if type(library_filename) == "string" then
+ library_filename = lib[library_filename]
+ end
+ tab.sort(mods)
+ local T = library_filename
+ for _, t in ipairs(mods) do
+ local found
+ for tag in pairs(T) do
+ if str.match(tag, "^" .. t) then
+ T = T[tag]
+ found = true
+ break
+ end
+ end
+ if not found then
+ T = nil
+ break
+ end
+ end
+
+ if T and T.__files then
+ local file, feats
+ local diff = 10000
+ for s, f in pairs(T.__files) do
+ if num.abs(s - size) < diff then
+ diff = num.abs(s - size)
+ file, feats = f[1], f[2]
+ end
+ end
+ file, _file = lfs.find_file(file, lfs.kpse(file)), file
+ if file then
+ file = str.gsub(file, "\\", "/")
+ else
+ return 1, _file
+ end
+ local features = {}
+ if feats then
+ get_features(feats, features)
+ end
+ local lua = name_luafile(file, features.font)
+ lua = lfs.isfile(lua) and lua
+ return file, lua, feats
+ end
+ end
+end
+
+-- Returns the full path to the Lua version of the font.
+-- "sub" is a font in ttc.
+function name_luafile (file, sub)
+ local lua
+ sub = sub and "_" .. sub or ""
+ sub = str.gsub(sub, " ", "_")
+ if str.match(file, "/") then
+ lua = str.match(file, ".*/(.-)%....$")
+ else
+ lua = str.match(file, "(.-)%....$")
+ end
+ lua = lua .. sub
+ return foundry_path .. "/" .. lua .. ".lua"
+end
+
+local function apply_size (font, size, letterspacing, parameters)
+ if (size < 0) then size = (- 655.36) * size end
+ local to_size = size / font.units_per_em
+ font.size = size
+ font.designsize = size
+ font.to_size = to_size
+ local italic = font.italicangle or 0
+ local space, stretch, shrink, extra
+ if parameters == "mono" then -- Creates a monospaced font with space equal to the
+ -- width of an "m" and no stretch or shrink.
+ space = font.characters[109].width * to_size
+ stretch, shrink, extra = 0, 0, 0
+ else
+ parameters = parameters and str.explode(parameters) or {}
+ space = (parameters[1] or 0.25) * size
+ stretch = (parameters[2] or 0.166666) * size
+ shrink = (parameters[3] or 0.111111) * size
+ extra = (parameters[4] or 0.111111) * size
+ end
+ font.parameters = {
+ slant = size * num.floor(num.tan(italic * num.pi/180)), -- \fontdimen 1
+ space = space, -- \fontdimen 2
+ space_stretch = stretch, -- \fontdimen 3
+ space_shrink = shrink, -- \fontdimen 4
+ x_height = size * 0.4, -- \fontdimen 5
+ quad = size, -- \fontdimen 6
+ extra_space = extra -- \fontdimen 7
+ }
+
+ letterspacing = letterspacing or 1
+ for c, t in pairs(font.characters) do
+ if t.width then
+ t.width, t.height, t.depth = t.width * to_size * letterspacing, t.height * to_size, t.depth * to_size
+ t.expansion_factor = 1000
+ if t.italic then
+ t.italic = t.italic * to_size
+ end
+ end
+ end
+
+ return font
+end
+
+
+local get_mods = lp.Ct((lp.space * lp.S"/" * lp.C(lp.alnum^1))^0)
+local get_feats = lp.Ct((lp.C((1 - lp.S",;")^1) * (lp.S",;" + -1))^1)
+function get_features (features, tb)
+ features = lp.match(get_feats, features) or {}
+ for _, f in ipairs(features) do
+ if str.match(f, "=") then
+ local key, val = str.match(f, "%s*(.-)%s*=%s*(.*)")
+ val = str.trim(val)
+ if val == "false" then
+ tb[key] = nil
+ else
+ tb[key] = val
+ end
+ else
+ f = str.trim(f)
+ neg, f = str.extract(f, "^%-")
+ if neg then
+ tb[f] = nil
+ else
+ _, f = str.extract(f, "^%+")
+ tb[f] = true
+ end
+ end
+ end
+end
+
+local lookup_functions = {}
+
+function lookup_functions.gsub_single (tb, f)
+ local name_touni = f.name_to_unicode
+ for a, b in pairs(tb.pairs) do
+ local _a, _b = name_touni[a], name_touni[b]
+ f.max_char = f.max_char + 1
+ f.characters[f.max_char] = f.characters[_a]
+ f.characters[_a] = f.characters[_b]
+ name_touni[a], name_touni[b] = f.max_char, _a
+ end
+ return f
+end
+
+function lookup_functions.gsub_ligature (tb, f)
+ local name_touni = f.name_to_unicode
+ tb.ligs.all_ligs = nil
+ for a, tb in pairs(tb.ligs) do
+ a = name_touni[a]
+ tab.subtable(f.characters[a], "ligatures")
+ for b, ttb in pairs(tb) do
+ b, c = name_touni[b], name_touni[ttb.char]
+ f.characters[a].ligatures[b] = {char = c, type = ttb.type}
+ end
+ end
+ return f
+end
+
+local function kern_pairs (tb, firsts, seconds, offset)
+ for _, c1 in ipairs(str.explode(firsts)) do
+ for __, c2 in ipairs(str.explode(seconds)) do
+ tab.subtable(tb, c1)
+ tb[c1][c2] = offset
+ end
+ end
+end
+
+local function kern_classes (firsts, seconds, offsets, max)
+ local kerns = {}
+ for f, F in pairs (firsts) do
+ for s, S in pairs (seconds) do
+ local off = offsets[(f-1) * max + s]
+ if off then
+ kern_pairs (kerns, F, S, off)
+ end
+ end
+ end
+ return kerns
+end
+
+local function apply_kerns (f, kerns, to_size)
+ local name_touni = f.name_to_unicode
+ to_size = to_size or 1
+ for c1, ttb in pairs(kerns) do
+ for c2, off in pairs(ttb) do
+ tab.subtable(f.characters[name_touni[c1]], "kerns")
+ f.characters[name_touni[c1]].kerns[name_touni[c2]] = off * to_size
+ end
+ end
+end
+
+function lookup_functions.gpos_pair (tb, f)
+ -- These are the big kern classes.
+ if tb.offsets then
+ local name_touni = f.name_to_unicode
+ for n, off in pairs(tb.offsets) do
+ tb.offsets[n] = off * f.to_size
+ end
+ local kerns = kern_classes(tb.firsts, tb.seconds, tb.offsets, tb.max)
+ apply_kerns(f, kerns)
+ end
+ -- These are the ones retrieved from individual glyphs.
+ if tb.kerns then
+ apply_kerns(f, tb.kerns, f.to_size)
+ end
+ return f
+end
+
+function lookup_functions.gsub_contextchain (tb, f)
+ local name_touni = f.name_to_unicode
+ local T = f.contextchain or {}
+ for _, llk in ipairs(tb) do
+ if fontfile.lookups then
+ local sub, Sub = fontfile.lookups[llk].rules[1].lookups
+ local chain = fontfile.lookups[llk].rules[1].coverage
+ local cur, current = str.explode(chain.current[1]), {}
+ local aft, after = chain.after and str.explode(chain.after[1]) or {}, {}
+ for _, c in ipairs(cur) do
+ c = name_touni[c]
+ if sub then
+ Sub = {}
+ for n, x in ipairs(sub) do
+ Sub[n] = name_touni[fontfile.glyphs[f.characters[c].index].lookups[x .. "_s"][1].specification.variant]
+ end
+ end
+ local t = { lookup = Sub}
+ tab.subtable(T, C)
+ for __, a in ipairs(aft) do
+ tab.subtable(t, "after")
+ t.after[name_touni[a]] = true
+ end
+ tab.insert(T[c], t)
+ end
+ end
+ end
+ f.contextchain = T
+ return f
+end
+
+local function _isactive (tb, ft, sc, lg)
+ for t in pairs(tb.tags) do
+ if ft[t] then
+ if t[sc] then
+ for _, lang in pairs(tb[sc]) do
+ if lang == lg then
+ return true
+ end
+ end
+ else
+ return true
+ end
+ end
+ end
+end
+
+
+local lookup_types = {
+ "gsub_single",
+ "gsub_ligature",
+ "gpos_pair"
+ }
+
+local function activate_lookups (font, features, script, lang)
+ for _, type in ipairs(lookup_types) do
+ if font.lookup_table and font.lookup_table[type] then
+ for l, tb in pairs(font.lookup_table[type]) do
+ if _isactive(tb, features, script, lang) then
+ for _, lk in ipairs(tb) do
+ local lt = font.lookups[lk]
+ if lt then
+ font = lookup_functions[type](lt, font)
+ end
+ end
+ end
+ end
+ end
+ end
+ return font
+end
+
+local function load_font (name, size, id, done)
+ local loaded_font = lfs.find_file(name, "tfm")
+ if loaded_font then
+ loaded_font = fl.read_tfm(loaded_font, size)
+ else
+ local original = str.trim(str.match(name, "[^:]*"))
+ local family, mods, feats
+ family, name = str.extract(name, "([^/:]*)")
+ family = str.trim(family)
+ mods, name = str.extract(name, "[^:]*")
+ mods = lp.match(get_mods, mods) or {}
+ feats = str.extract(name, ":(.*)") or ""
+ local features = tab.copy(settings.features)
+ get_features(feats, features)
+
+ local at_size
+ if features.size then
+ at_size = features.size
+ else
+ at_size = size
+ at_size = at_size > 0 and at_size or at_size * - 655.36
+ at_size = at_size / 65536
+ end
+ local source, lua, add_feats = is_font(family, mods, at_size)
+ if add_feats then get_features(add_feats, features) end
+
+ if type(source) == "string" then
+ local cache = features.cache or "yes"
+ if lua then
+ if cache == "no" or cache == "rewrite" then
+ loaded_font = create_font(source, lfs.type(source), lua, features.font, cache == "rewrite")
+ else
+ loaded_font = dofile(lua)
+ end
+ else
+ lua = name_luafile(source, features.font)
+ loaded_font = create_font(source, lfs.type(source), lua, features.font, cache ~= "no")
+ end
+ else
+ if not done then
+ if type(source) == "number" then
+ wri.error("The library says `%s' matches `%s', but I can't find that file anywhere. Clean up your library!", original, lua)
+ else
+ recheck_fonts()
+ return load_font(original, size, id, true)
+ end
+ else
+ wri.error("I can't find `%s'. I return a default font to avoid further errors.", original)
+ end
+ end
+
+ if loaded_font then
+
+ local expansion = features.expansion and str.explode(features.expansion) or {}
+ loaded_font.stretch = expansion[1] or 0
+ loaded_font.shrink = expansion[2] or 0
+ loaded_font.step = expansion[3] or 0
+
+ local extend = features.extend or 1
+ loaded_font.extend = extend * 1000
+ local slant
+ if features.absoluteslant then
+ local italic = loaded_font.italicangle or 0
+ slant = features.absoluteslant - italic
+ else
+ slant = features.slant or 0
+ end
+ loaded_font.slant = num.tan(num.rad(slant)) * 1000
+
+ loaded_font = apply_size(loaded_font, size, features.letterspacing, features.space)
+ loaded_font = activate_lookups(loaded_font, features, features.script, features.lang)
+
+ loaded_font.name = loaded_font.name .. id
+ loaded_font.fullname = loaded_font.fullname .. id
+ local embedding = features.embedding or "subset"
+ if embedding ~= "no" and embedding ~= "subset" and embedding ~= "full" then
+ wri.error("Invalid value `%s' for the `embedding' feature. Value should be `no', `subset' or `full'.", embedding)
+ embedding = "subset"
+ end
+ loaded_font.embedding = embedding
+ else
+ loaded_font = fl.read_tfm(lfs.find_file("cmr10", "tfm"), size)
+ end
+ end
+ return loaded_font
+end
+
+callback.register("define_font", load_font)
diff --git a/macros/plain/contrib/pitex/foundry-settings.lua b/macros/plain/contrib/pitex/foundry-settings.lua
new file mode 100644
index 0000000000..5c24caa937
--- /dev/null
+++ b/macros/plain/contrib/pitex/foundry-settings.lua
@@ -0,0 +1,19 @@
+return {
+
+ features = {
+ kern = true,
+ liga = true,
+ trep = true,
+ tlig = true,
+ script = "latn",
+ lang = "dflt",
+ --stretch = 30,
+ --shrink = 20,
+ --step = 10,
+ },
+
+ normal = {
+ "Normal", "Regular", "Book",
+ },
+
+}
diff --git a/macros/plain/contrib/pitex/i-pitex.lua b/macros/plain/contrib/pitex/i-pitex.lua
new file mode 100644
index 0000000000..4559a1901d
--- /dev/null
+++ b/macros/plain/contrib/pitex/i-pitex.lua
@@ -0,0 +1,163 @@
+local gsub, match, gmatch = string.gsub, string.match, string.gmatch
+local insert = table.insert
+local add_pattern = interpreter.add_pattern
+
+-- CLASS 1: things that prevent other things, thanks to protection.
+interpreter.default_class = 1
+
+-- "> foo ..." as the first line of a paragraph signals a description
+-- of foo.
+local function describe (buffer, num)
+ if num == 1 then
+ local line = buffer[num]
+ line = line .. " "
+ line = gsub(line, "^> ", "")
+ line = gsub(line, "{<(.-)>}", "\\hskip1pt\\hbox{\\barg{%1}}")
+ line = gsub(line, "^(.-)%s+", "\\describe{%1}{")
+ line = gsub(line, "%[<(.-)>%]", "\\hskip1pt\\hbox{\\oarg{%1}}")
+ line = gsub(line, "<(.-)>", "\\hskip1pt\\hbox{\\arg{%1}}")
+ line = gsub(line, "\\hfil", "", 1)
+ line = gsub(line, "%s*$", "}", 1)
+ buffer[num] = line
+ interpreter.protect(num)
+ else
+ return 2
+ end
+end
+add_pattern{pattern = "^[\\>]", call = describe}
+
+-- Verbatim material is marked by a 10-space indent.
+local function verbatim (buffer)
+ for n, l in ipairs(buffer) do
+ buffer[n] = gsub(l, "%s%s%s%s%s%s%s%s%s%s","", 1)
+ end
+ insert(buffer, 1, [[\verbatim]])
+ if gsub(buffer[#buffer],
+ interpreter.paragraph, "") == "" then
+ buffer[#buffer] = [[\verbatim/]]
+ else
+ insert(buffer, [[\verbatim/]])
+ end
+ interpreter.protect()
+end
+add_pattern{pattern = "^%s%s%s%s%s%s%s%s%s%s", call = verbatim}
+
+-- CLASS 2: protecting control sequences, so they're verbatim.
+interpreter.default_class = 2
+add_pattern {
+ pattern = "(\\[%a@]+)",
+ replace = '"%1"'}
+
+
+-- CLASS 3: things that precede other things.
+interpreter.default_class = 3
+
+-- Tables.
+local function table (buffer)
+ buffer[1] = "PROTECT\\tablePROTECT"
+ if buffer[#buffer] == "" then
+ buffer[#buffer-1] = "PROTECT\\table/PROTECT"
+ else
+ buffer[#buffer] = "PROTECT\\table/PROTECT"
+ end
+ local cells = 0
+ for i, l in ipairs(buffer) do
+ if match(l, "^%-%-%-") then
+ l = "%"
+ else
+ l = gsub(l, "^|", "")
+ l = gsub(l, "|%s*$", "PROTECT\\crPROTECT")
+ local c = 0
+ l = gsub(l, "|", function () c = c + 1 return "&" end)
+ cells = c > cells and c or cells
+ end
+ buffer[i] = l
+ end
+ for i, l in ipairs(buffer) do
+ if match(l, "\\cr") then
+ local c = 0
+ for tab in gmatch(l, "&") do
+ c = c + 1
+ end
+ for x = 1, cells - c do
+ buffer[i] = gsub(l, "\\cr", "\\span\\cr")
+ end
+ end
+ end
+end
+add_pattern{pattern = "^%-%-%-", call = table}
+
+-- Sections.
+local function make_header(buffer, num, _, pattern)
+ if num + 1 == #buffer then
+ local l = buffer[1]
+ l = gsub(l, "^%s*", "")
+ l = "\\" .. pattern.type .. "{" .. l
+ if match(l, ":([%S]+)%s*$") then
+ l = gsub(l, "%s*:([%a_]+)%s*$", "}PROTECT[%1]PROTECT")
+ else
+ l = l .. "}"
+ end
+ buffer[1] = l
+ buffer[num] = ""
+ end
+end
+add_pattern {pattern = "^%s*===", call = make_header, type = "section"}
+add_pattern {pattern = "^%s*~~~", call = make_header, type = "subsection"}
+add_pattern {pattern = "^%s%s%s*%-%-%s+(.-)%s+%-%-",
+ call = function (buffer, line, _, pattern)
+ buffer[line] = gsub(buffer[line], pattern.pattern, "\\paragraph{%1}")
+ buffer[line] = gsub(buffer[line], "%s*:(%S+)", "PROTECT[%1]PROTECT")
+ end}
+
+
+-- CLASS 4: things with no order of preference.
+interpreter.default_class = 4
+
+add_pattern {pattern = "%*(.-)%*", replace = "\\emph{%1}"}
+add_pattern {pattern = "%*%*(.-)%*%*", replace = "\\bold{%1}"}
+add_pattern {pattern = "%?([a-zA-Z]+)", replace = "\\attr{%1}"}
+add_pattern {pattern = "!([a-zA-Z]+)", replace = "\\param{%1}"}
+add_pattern {pattern = "=([a-zA-Z]+)", replace = "\\value{%1}"}
+add_pattern {pattern = "|(.-)|", replace = "\\gateaction{%1}"}
+add_pattern {pattern = "<(.-)>", replace = "\\arg{%1}"}
+add_pattern {pattern = "{<(.-)>}", replace = "\\barg{%1}", offset = 7}
+add_pattern {pattern = "%[<(.-)>%]", replace = "\\oarg{%1}", offset = 7}
+add_pattern {pattern = "TeX", replace = "\\TeX{}", offset = 2}
+
+add_pattern {pattern = "(texapi)(.?)", offset = 7,
+ replace = function (a, b)
+ a = "\\emph{texapi}"
+ if b == "'" then
+ a = a .. "\\kern.2em'"
+ else
+ a = a .. b
+ end
+ return a
+ end}
+
+-- Links.
+add_pattern {pattern = "%s*%[see%s+([%a_]+)%]",
+ call = function (buffer, line, index, pattern)
+ local l = buffer[line]
+ local pre, post = l:sub(1, index-1), l:sub(index)
+ local text
+ if pre:match("{.-}$") then
+ pre, text = match(pre, "(.*){(.-)}$")
+ else
+ pre, text = match(pre, "(.-)(%a+)$")
+ end
+ post = gsub(post, pattern.pattern, "\\jumplink{PROTECT%1PROTECT}{" .. text .. "}", 1)
+ buffer[line] = pre .. post
+ end}
+add_pattern {pattern = ":([%a_]+)", replace = "\\anchor{PROTECT%1PROTECT}"}
+
+
+-- CLASS 0: the broom wagon
+interpreter.default_class = 0
+
+interpreter.protector('"')
+add_pattern {pattern = '"(.-)"', replace = "\\verb`%1`", class = 0}
+
+interpreter.protector("PROTECT")
+add_pattern {pattern = "PROTECT(.-)PROTECT", replace = "%1", class = 0}
diff --git a/macros/plain/contrib/pitex/inserts.ptx b/macros/plain/contrib/pitex/inserts.ptx
new file mode 100644
index 0000000000..70155f5d7e
--- /dev/null
+++ b/macros/plain/contrib/pitex/inserts.ptx
@@ -0,0 +1,113 @@
+% This is a mess.
+\newinsert\ptx@insert_footnotes
+\dimen\ptx@insert_footnotes20\baselineskip
+\skip\ptx@insert_footnotes12pt
+\count\ptx@insert_footnotes1000
+
+\newcount\ptx@footnote_count
+\newbox\ptx@footnote_box
+\newif\iffootnote
+
+\def\footnote{%
+ \global\advance\ptx@footnote_count1
+ \maintextfalse \footnotetrue
+ \ifnext[
+ {\ptx@label_withand{\the\ptx@footnote_count}\ptx@footnote}
+ {\ptx@footnote}%
+ }
+\long\def\ptx@footnote#1{%
+ {\verysmall
+ \unskip\kern.1em
+ \raise.53em\hbox{\the\ptx@footnote_count}%
+ \global\setbox\ptx@footnote_box\hbox{\the\ptx@footnote_count}%
+ }%
+ \insert\ptx@insert_footnotes\bgroup
+ \verysmall
+ \baselineskip12pt
+ \leftskip=0pt \rightskip=\leftskip
+ \floatingpenalty20000
+ \noindent\vrule width0pt depth0pt height9pt
+ \hbox to 0pt{\hss\the\ptx@footnote_count\hskip.5em\hfill}#1%
+ \egroup
+ \futurelet\temp\getpunc
+ }
+
+\def\getpunc{%
+ \bgroup
+ \ifx\temp\virgule
+ \kern-.4\wd\ptx@footnote_box,%
+ \egroup
+ \expandafter\gobble
+ \else
+ \ifx\temp\point
+ \kern-.4\wd\ptx@footnote_box.%
+ \egroup
+ \expandafter\expandafter\expandafter\gobble
+ \else
+ \egroup
+ \fi
+ \fi
+ \maintexttrue \footnotefalse
+ }
+
+
+\newinsert\ptx@insert_figures
+\dimen\ptx@insert_figures\vsize
+\count\ptx@insert_figures1000
+\newcount\ptx@figure_count
+\newcount\ptx@table_count
+\newbox\ptx@figure_captionbox
+\newif\iffigure
+
+\newblock*\figure
+ {\ifnextnospace*
+ {\def\ptx@figure_type{table}\def\ptx@figure_word{Table}\gobbleoneand\ptx@figure_do}
+ {\def\ptx@figure_type{figure}\def\ptx@figure_word{Fig.}\ptx@figure_do}}
+ {\egroup
+ \insert\ptx@insert_figures{%
+ \penalty0
+ \floatingpenalty0
+ \vbox to \ptx@figure_measure{0}{\ht\ptx@box_temp+\dp\ptx@box_temp+\ht\ptx@figure_captionbox}{2\baselineskip}{%
+ \box\ptx@box_temp
+ \vfill
+ \vskip\baselineskip
+ \box\ptx@figure_captionbox
+ \vskip\baselineskip}}}
+\def\ptx@figure_do{%
+ \maintextfalse
+ \figuretrue
+ \global\advance\usecs{ptx@\ptx@figure_type _count}1
+ \def\ptx@label{\usevalue chapter:internalcount .\the\usecs{ptx@\ptx@figure_type _count}}%
+ \ifnextnospace[
+ {\ptx@figure_getcaption}
+ {\ptx@figure_getcaption[]}}
+
+\def\ptx@figure_getcaption[#1]{%
+ \setbox\ptx@figure_captionbox\vbox{%
+ \leftskip3em \rightskip\leftskip \parindent0pt
+ \ifdefined\figurefont
+ \figurefont
+ \else
+ \verysmall
+ \fi
+ \baselineskip16pt
+ \leavevmode
+ \llap{\bf \ptx@figure_word~\usevalueand chapter:internalcount {.}{}\the\usecs{ptx@\ptx@figure_type _count}\hskip.5em}#1}%
+ \setbox\ptx@box_temp=\vbox\bgroup
+ \leftskip0pt plus1fill \rightskip\leftskip \parindent0pt
+ }
+
+\newwhile\ptx@figure_measure3{#1+1}{#2}{#3}{%
+ \reverse\straighteniff{ifdim}
+ {\dimexpr\baselineskip*\numexpr(#1)<\dimexpr(#2)\relax}
+ {\breakwhile{\dimexpr(\numexpr(#1)\baselineskip\reverse\iffemptystring{#3}{+#3})\relax}}
+ }
+
+\newblock\infigure
+ {\par \setbox\ptx@box_temp=\hbox\bgroup
+ \leftskip0pt plus1fill \rightskip\leftskip \parindent0pt }
+ {\egroup
+ \kern-\prevdepth \nointerlineskip
+ \vbox to \ptx@figure_measure{0}{\ht\ptx@box_temp+\dp\ptx@box_temp}{}
+ {\vfil\box\ptx@box_temp\vfil}%
+ }
diff --git a/macros/plain/contrib/pitex/lua.ptx b/macros/plain/contrib/pitex/lua.ptx
new file mode 100644
index 0000000000..b41c5af338
--- /dev/null
+++ b/macros/plain/contrib/pitex/lua.ptx
@@ -0,0 +1,26 @@
+\def\inputluafile#1 {\directlua{dofile(kpse.find_file("#1"))}}
+
+\inputluafile base.ptxlua
+
+\def\ptx@lua{\directlua name {Internal PiTeX chunk}}
+
+\newcatcodetable\luacatcodes{\#\%\^^M\~=12}
+\newtoks\ptx@luacode_list
+\newif\ifptx@luacode_store
+% Mimicks a block (\newblock isn't defined yet
+% and anyway it wouldn't be very useful).
+\def\luacode{%
+ \begingroup
+ \catcodetable\luacatcodes
+ \ifnext[
+ {\ptx@luacode_store}
+ {\ptx@luacode_store[]}}
+\bgroup
+\setcatcodes{\^^M=12}%
+\long\gdef\ptx@luacode_store[#1]^^M#2\luacode/{% So line count is right.
+ \endgroup%
+ \ifemptystring{#1}%
+ {\ptx@lua{#2}}%
+ {\def#1{#2}}%
+ }%
+\egroup
diff --git a/macros/plain/contrib/pitex/output.ptx b/macros/plain/contrib/pitex/output.ptx
new file mode 100644
index 0000000000..20a0dc677f
--- /dev/null
+++ b/macros/plain/contrib/pitex/output.ptx
@@ -0,0 +1,311 @@
+\defactiveparameter page {%
+ \settovalue\pdfpagewidth #1 : width
+ \settovalue\pdfpageheight #1 : height
+ \settovalue\baselineskip #1 : baselineskip
+ \settovalueor\topskip #1 : topskip {\topskip=\baselineskip}%
+ \settovalue\pdfhorigin #1 : left
+ \ifattribute #1 : right
+ {\hsize = \dimexpr(\pdfpagewidth-\pdfhorigin-\usevalue #1 : right )\relax}
+ {\settovalue\hsize #1 : hsize }%
+ \settovalue\pdfvorigin #1 : top
+ \settovalue\parindent #1 : parindent
+ \settovalue\parskip #1 : parskip
+ \ifattribute #1 : lines {\vsize=\usevalue #1 : lines \baselineskip}{}%
+ }
+
+\restrictparameter page :
+ width
+ height
+ hsize
+ baselineskip
+ topskip
+ left
+ right
+ top
+ lines
+ parindent
+ parskip
+\par
+
+% Defaults... they don't produce anything beautiful.
+% I redefine them on every job.
+\setparameter page:
+ height = 28cm
+ width = 21cm
+ hsize = 15cm
+ baselineskip = 12pt
+ lines = 42
+ left = 1in
+ top = 1in
+ parskip = 0pt
+
+\widowpenalty151
+\clubpenalty0
+\holdinginserts1
+\newdimen\outputsize
+\newif\ifheader
+
+\gates new \OutputRoutine {OutputRoutine}%
+
+\OutputRoutine list {output}
+ [precheck]
+ {%
+ \global\holdinginserts0
+ \OutputRoutine skip {precheck, shipout}{output}%
+ \ifnum\outputpenalty<\widowpenalty
+ \global\outputsize=\vsize
+ \else
+ \ifnum\outputpenalty=10000
+ \global\outputsize=\vsize
+ \else
+ \ifdim\pagegoal<\vsize
+ \global\advance\vsize-\baselineskip
+ \global\outputsize=\dimexpr\vsize+\baselineskip\relax
+ \else
+ \global\advance\vsize\baselineskip
+ \global\outputsize=\dimexpr\vsize-\baselineskip\relax
+ \fi
+ \fi
+ \fi
+ \unvbox\outputbox
+ \ifnum\outputpenalty=10000
+ \penalty0
+ \else
+ \penalty\outputpenalty
+ \fi}
+%
+ (shipout)
+ . (inserts)
+ . . [inserts_figures] ?{conditional = \unless\ifvoid\ptx@insert_figures} {%
+ \setbox\outputbox=\vbox to \outputsize{%
+ \box\ptx@insert_figures
+ \unvbox\outputbox}}
+ . . [inserts_footnotes] ?{conditional = \unless\ifvoid\ptx@insert_footnotes} {%
+ \setbox\outputbox=\vbox to \outputsize{%
+ \unvbox\outputbox
+ \vfil
+ \vskip.5\skip\ptx@insert_footnotes
+ \vbox to0pt{\vss\hrule width .3\hsize}%
+ \vskip.5\skip\ptx@insert_footnotes
+ \unvbox\ptx@insert_footnotes}}
+ %
+ . [headers] {%
+ \setbox\outputbox=\vbox{%
+ \kern-3\baselineskip
+ \vbox to3\baselineskip{%
+ \hbox to\hsize{\strut\baselineskip {0pt}%
+ \normalsize\rm\sc
+ \ifodd\pageno
+ \hfill\usevalue chapter : inner_title \rlap{\kern1em \the\pageno}\kern-3em
+ \else
+ \kern-3em\llap{\the\pageno\kern1em }\usevalue chapter : inner_title \hfill
+ \fi}
+ \vfill}
+ \nointerlineskip
+ \box\outputbox}}
+ %
+ . [ship] {%
+ \settovalue\pdfhorigin page : left
+ \shipout\box\outputbox}
+ %
+ . [postship] {%
+ \global\holdinginserts1
+ \global\vsize\outputsize
+ \global\advance\count0 1
+ \ifnum\insertpenalties>0
+ \ifnum\outputpenalty=\clearpagepenalty
+ \hbox{}\vfil\penalty\outputpenalty
+ \fi
+ \fi}
+
+
+\output{%
+ \OutputRoutine execute {output}%
+ }
+
+
+
+% MARGIN NOTES
+\setparameter marginnote :
+ hsize = 3cm
+ hpos = fr
+ font = \it
+ parindent = 0pt
+ side = right
+ gap = 1em
+
+\newcount\notecount
+\newattribute\ptx@marginnote_attribute
+\def\marginnote{%
+ \ifnext[
+ {\ptx@marginnote}
+ {\ptx@marginnote[]}%
+ }
+
+\long\def\ptx@marginnote[#1]#2{%
+ \setparameterlist{marginnote@temp}{meta = marginnote, #1}%
+ \global\advance\notecount 1
+ \global\expandafter\newbox\csname marginnote_\the\notecount\endcsname
+ \global\expandafter\setbox\csname marginnote_\the\notecount\endcsname=
+ \vtop{%
+ \settovalue\hsize marginnote@temp : hsize
+ \settovalue{\advance\hsize} marginnote@temp : gap
+ \leftskip=0pt \rightskip=0pt plus 1 fil \parfillskip=0pt\relax
+ \ifcasevalue marginnote@temp : hpos
+ \val ff \rightskip=0pt \parfillskip=0pt plus 1 fil
+ \val rf \leftskip=0pt plus 1 fil \rightskip=1pt
+ \val rr \leftskip=0pt plus 1 fil
+ \endval
+ \ifvalue marginnote@temp : side = left
+ {\settovalue{\advance\rightskip} marginnote@temp : gap }
+ {\settovalue{\advance\leftskip} marginnote@temp : gap }%
+ \settovalue\baselineskip marginnote@temp : baselineskip
+ \settovalue\parindent marginnote@temp : parindent
+ \usevalue marginnote@temp : font
+ #2}%
+ \quitvmode % Must be outside the attribute's scope, otherwise the
+ % paragraphs DIR whatsit might get in the way.
+ \bgroup
+ \ifvalue marginnote@temp : side = left
+ {\ptx@marginnote_attribute=-\expandafter\the\csname marginnote_\the\notecount\endcsname}
+ {\ptx@marginnote_attribute=\expandafter\the\csname marginnote_\the\notecount\endcsname}\relax
+ \pdfliteral{}%
+ \egroup
+ \deleteparameter marginnote@temp:%
+ }
+
+\luacode
+local WHAT, HLIST, KERN = node.id("whatsit"), node.id("hlist"), node.id("kern")
+local GLUE, SPEC = node.id("glue"), node.id("glue_spec")
+local marginnote_table = {}
+function pitex.misc.mark_lines (head)
+ for line in node.traverse_id(HLIST, head) do
+ local t = {left = {}, right = {}}
+ local item = line.head
+ while item do
+ local next = item.next
+ if item.id == WHAT then
+ local attr = node.has_attribute(item, \attributenumber\ptx@marginnote_attribute)
+ if attr then
+ if attr < 0 then
+ table.insert(t.left, -attr)
+ else
+ table.insert(t.right, attr)
+ end
+ line.head = node.remove(line.head, item)
+ end
+ end
+ item = next
+ end
+ if #t.left > 0 or #t.right > 0 then
+ table.insert(marginnote_table, t)
+ node.set_attribute(line, \attributenumber\ptx@marginnote_attribute, #marginnote_table)
+ end
+ end
+ return head
+end
+
+pitex.callback.register("post_linebreak_filter", "pitex.misc:mark_lines")
+
+local lheight, rheight = 0, 0
+local function update_height (h)
+ lheight = lheight + h
+ rheight = rheight + h
+end
+
+local function first_dim (n, t)
+ while n do
+ if node.has_field(n, "kern") then
+ return n.kern
+ elseif node.has_field(n, "spec") then
+ return n.spec.width
+ elseif node.has_field(n, "height") then
+ return t == "height" and n.height or n.depth
+ else
+ n = t == "height" and n.next or n.prev
+ end
+ end
+ return 0
+end
+
+function process_marginalia (head)
+ lheight, rheight = 0, 0
+ local item, first = node.slide(head), true
+ while item do
+ if node.has_field(item, "kern") then
+ if not first then
+ update_height(item.kern)
+ end
+ elseif node.has_field(item, "spec") then
+ if not first then
+ update_height(item.spec.width)
+ end
+ elseif node.has_field(item, "height") then
+ if first then
+ first = false
+ else
+ update_height(item.depth)
+ end
+ local attr = node.has_attribute(item, \attributenumber\ptx@marginnote_attribute)
+ if attr then
+ for side, notes in pairs (marginnote_table[attr]) do
+ if #notes > 0 then
+ local note
+ for _, n in ipairs(notes) do
+ if note then
+ local n, g, s = tex.box[n], node.new(GLUE), node.new(SPEC)
+ local d = first_dim(node.tail(note.head), "depth")
+ local h = first_dim(n.head, "height")
+ s.width = tex.baselineskip.width - (d + h)
+ g.spec = s
+ note.depth = note.depth + s.width + n.height + n.depth
+ node.insert_after(note.head, node.tail(note.head), g)
+ g.next, n.head.prev = n.head, g
+ else
+ note = tex.box[n]
+ end
+ end
+ first = true
+ local upward = note.depth - first_dim(node.tail(note.head), "depth")
+ local remainingheight = side == "left" and lheight or rheight
+ if upward > remainingheight then
+ upward = remainingheight - upward
+ else
+ upward = 0
+ end
+ local kern = node.new(KERN, 1)
+ kern.kern = upward - note.height - item.depth
+ node.insert_before(note.list, note.list, kern)
+ note.list = kern
+ if side == "left" then
+ note.shift = -note.width
+ lheight = upward
+ else
+ note.shift = item.width
+ rheight = upward
+ end
+ note.height, note.depth = 0, 0
+ node.insert_after(head, item, note)
+ else
+ if side == "left" then
+ lheight = lheight + item.height
+ else
+ rheight = rheight + item.height
+ end
+ end
+ end
+ else
+ update_height(item.height)
+ end
+ end
+ item = item.prev
+ end
+end
+\luacode/
+
+\OutputRoutine def {processmarginalia}{%
+ \ptx@lua{process_marginalia(tex.box[255].list)}%
+ }
+
+\OutputRoutine add {processmarginalia}[first]{shipout}
+
diff --git a/macros/plain/contrib/pitex/pitex-doc.pdf b/macros/plain/contrib/pitex/pitex-doc.pdf
new file mode 100644
index 0000000000..dbcbfa7e96
--- /dev/null
+++ b/macros/plain/contrib/pitex/pitex-doc.pdf
Binary files differ
diff --git a/macros/plain/contrib/pitex/pitex-doc.tex b/macros/plain/contrib/pitex/pitex-doc.tex
new file mode 100644
index 0000000000..63c6281717
--- /dev/null
+++ b/macros/plain/contrib/pitex/pitex-doc.tex
@@ -0,0 +1,106 @@
+\input pitex
+
+\setparameter document :
+ author = "Paul Isambert"
+ title = "The PiTeX documentation"
+ date = "November 2011"
+ layout = onecolumn
+ mode = bookmarks
+
+\setparameter page:
+ height = 24cm
+ left = 4cm
+ hsize = 32pc
+
+\setfont\mainfont :
+ name = "Chaparral Pro"
+ size = 10pt
+ bold = Semi
+ big = 50pt
+
+\setfont\codefont :
+ name = "Lucida Console"
+ size = 8pt
+ slant = .15
+ features = "space = mono"
+ bold italic = none
+
+\setparameter marginnote:
+ baselineskip = 9pt
+ font = "\small\it"
+
+\setparameter section:
+ font = \sc
+
+
+\OutputRoutine remove {headers, inserts}{shipout}
+
+\newblock\table
+ {\ifdim\lastskip=\baselineskip
+ \needspace{2\baselineskip}%
+ \else
+ \needspace{3\baselineskip}%
+ \vskip\baselineskip
+ \fi
+ \halign\bgroup##\unskip\hfil&&\kern1em##\unskip\hfil\cr}
+ {\egroup
+ % This is a horrible hack to circumvent a bug in LuaTeX (\prevdepth not
+ % updated after \halign) (rev 4277).
+ \directlua{%
+ local n = tex.lists.page_head
+ while n.next do
+ n = n.next
+ end
+ tex.nest[tex.nest.ptr].prevdepth = n.depth}%
+ \vskip\baselineskip
+ }%
+
+
+\def\describe#1#2{%
+ \needspace{2\baselineskip}%
+ \bgroup\codefont
+ \noindent\color{.8 0 0}{\llap{\string#1\kern.3em}%
+ #2\par\egroup}\removenextindent
+ \ignorespaces}
+
+\def\comarg{\arg{command}\antigobblespace}
+\def\textarg{\arg{text}}
+
+\def\filesection#1{\section{File: \tcode{#1}}}
+\pdfdef\verb`#1`{#1}
+
+\def\attr#1{{\codefont\color{0 .5 0}{#1}}}
+\def\param#1{{\codefont\color{.8 0 0}{#1}}}
+\def\value#1{{\codefont\color{.3 .3 .6}{#1}}}
+
+\def\texapi{\emph{texapi}}
+\def\navigator{\emph{Navigator}}
+\def\tex{\TeX\antigobblespace}
+
+%%% Redefining \TeX to look better with Chaparral.
+\def\TeX{%
+ T\kern-.13em
+ \lower.5ex \hbox {E}%
+ \kern-.03em X%
+ }
+
+
+
+
+
+\vbox to 50pt{%
+ \vfil
+ \hbox{%
+ \hbox{\big\sc Pi\kern-.08em\TeX}%
+ \kern1cm
+ \vbox{%
+ \hbox{Paul Isambert}%
+ \hbox{\codefont zappathustra@free.fr}%
+ \hbox{November 2011}}}%
+ \vfil
+ }
+
+\input interpreter
+\interpretfile{pitex}{pitex-doc.txt}
+
+\bye
diff --git a/macros/plain/contrib/pitex/pitex-doc.txt b/macros/plain/contrib/pitex/pitex-doc.txt
new file mode 100644
index 0000000000..168e7e7b18
--- /dev/null
+++ b/macros/plain/contrib/pitex/pitex-doc.txt
@@ -0,0 +1,1057 @@
+Introduction
+============
+
+If you're reading this, either you saw "\input pitex" at the beginning
+of the documentation of one of my packages, or you spend desperate hours
+browsing CTAN, or you're Arnaud Schmittbuhl. In any case, you're welcome
+to use the PiTeX macros, provided that you don't forget that: nothing
+is guaranteed; changes might occur without warning nor retrocompatibility;
+the documentation isn't necessarily up-to-date; and, if you still want
+to, you must load PiTeX with "\input pitex" on top of plain TeX, using
+LuaTeX.
+
+What is PiTeX? Originally, it was a set of files I loaded with plain TeX
+to typeset documentation for my packages. But it's not just a few macros
+anymore, but rather a format in progress. The format might never see the
+public light, but if it does, its originality (compared to existing
+format) will be an organization based on the Gates package: everything
+will be highly customizable, not because there are tons of options
+(although that can be the case too), but because big operations are
+divided into gates, i.e. macros with a handle that you can control without
+having to rewrite (nor understand) the big operation you're modifying;
+which big operation also keeps its integrity, because removing or adding
+something is neatly done. It also means that PiTeX can be changed
+piecewise, and made into something that bears little resemblance to the
+original code. In other words, there is nothing private, nothing forbidden.
+For the moment, only sectionning commands and the output routine, plus
+callback management in Lua and \everypar, are written with gates. See
+the Gates documentation for further information.
+
+Another thing with PiTeX is that it will rely heavily on external packages.
+There will be as little PiTeX-only code as possible. Rather, in line
+with Gates, each area will be independant code able to work with other
+formats. That is no simple task, though, and far from complete. For
+instance, the user's interface is made with YaX, which can be used (and
+is used) elsewhere.
+
+PiTeX uses three types of files: mandatory external packages, i.e.
+independant code that PiTeX can't do without, optional external packages,
+i.e. independant code that can be used, but is not automatically loaded,
+and mandatory PiTeX-only files. All mandatory files aren't necessary to
+the same degree, though, and in the future switches might be available
+to load only some of them. Currently, the files are:
+
+------------------------------------------------------------------
+| **Mandatory external packages** |
+------------------------------------------------------------------
+| texapi | Macros to write independant code. |
+| YaX | User's interface (and convenient programming tool) |
+| | with "key = value" style). |
+| Gates | Overall architecture for modular code. |
+| Navigator | PDF features (links, bookmarks...) |
+| | Currently used by sections, but might become |
+| | non-mandatory (although strongly recommended). |
+------------------------------------------------------------------
+
+---------------------------------------------------------------------------
+| **Mandatory PiTeX files** |
+---------------------------------------------------------------------------
+| pitex.tex | The main file that inputs the other, |
+| | and contains a few macros. |
+| lua.ptx | Lua-related macros. |
+| base.ptxlua | Lua functions, input in the previous file. |
+| files.ptx | Dealing with files. |
+| fonts.ptx | Interface for fonts; relies on the next file. |
+| fonts.ptxlua | The Lua fontloader; should become independant |
+| | some day. |
+| foundry-settings.lua | Default settings for the fontloader. |
+| sections.ptx | Sectionning commands. |
+| blocks.ptx | Blocks (delimited text with special formatting). |
+| references.ptx | Labels and references. |
+| verbatim.ptx | Typesetting verbatim material. |
+| inserts.ptx | Footnotes and figures. |
+| output.ptx | Page layout and output routine. |
+---------------------------------------------------------------------------
+
+The following can be used with PiTeX; actually I only list the packages
+I've written, but anything working with plain TeX (e.g. TikZ) works with
+PiTeX.
+
+----------------------------------------------------------------------------
+| **Optional external packages** |
+----------------------------------------------------------------------------
+| Librarian | To create bibliographies without BibTeX. |
+| Lecturer | For screen presentations. |
+| Interpreter | To write text with non-TeX markup (as this documentation); |
+| | Interpreter does the conversion on the fly. |
+----------------------------------------------------------------------------
+
+The PiTeX distribution also contains "i-pitex.lua", an interpretation
+file for Interpreter used to typeset documentations, like the one you
+are currently reading. Which is why you can read it quite comfortably
+as a plain text file in a text editor (see "pitex-doc.txt").
+
+The rest of this document is a terse description of existing commands,
+parameters, and of course, gates.
+
+
+
+
+
+Fonts (fonts.ptx and fonts.ptxlua)
+==================================
+
+The fontloader uses gates, but only superficially. They won't be
+documented here.
+
+\setfont <command>:<attributes>
+Sets <command> to call the font described in <attributes>; all defaults
+to the values of the !metafont parameter. If <command> is \mainfont, the
+font is called at once. Also, \codefont is used in some places (e.g.
+verbatim).
+
+> name
+The family name of the font; e.g. Chaparral Pro for the main text of
+this document.
+
+> size (dimension)
+The size of the font.
+
+> small (dimension)
+The size of the font when \small is called. Can be a relative value by
+prefixing it with "-" or "+", in which case it is set relative to ?size.
+
+> verysmall (dimension)
+The size (possibly relative) for \verysmall.
+
+> big (dimension)
+The size (possibly relative) for \big.
+
+> verybig (dimension)
+The size (possibly relative) for \verybig.
+
+> bold (font modifier)
+The modifier used for the bold version of the font, without the leading slash;
+!metafont sets it to "Bold".
+
+> italic (font modifier)
+Same as !bold for the italic version; set to "Italic" by !metafont.
+
+> math (true or false)
+If true, math fonts will be created.
+
+> features
+Well, err, font features...
+
+> slant (angle)
+The slant applied to the font to create a fake italic.
+
+> slantsc (angle)
+The slant applied to the font to create fake italic smallcaps; if not
+given, defaults to ?slant.
+
+There's actually much more going under the hood, but "font.ptxlua" (the
+font loader itself) is a work in progress, and undocumented.
+
+The same macros as in plain TeX can be used, except they're cumulative,
+i.e. "\it\bf" switches to a bold italic.
+
+\it
+Switches to italics.
+
+\rm
+Switches to roman.
+
+\bf
+Switches to bold.
+
+\rg
+Switches to regular weight.
+
+\sc
+Switches to small capitals.
+
+\lc
+Switches to lower case (i.e. not small caps).
+
+\ital <text>
+Typesets <text> in italics.
+
+\bold <text>
+Typesets <text> in bold.
+
+\scap <text>
+Typesets <text> in small caps.
+
+\rom <text>
+Typesets <text> in roman.
+
+\emph <text>
+Typesets <text> in italics or roman, depending on whether the current
+font is roman or italics, respectively.
+
+\underline <text>
+Underlines <text>. Wow.
+
+\small
+Switches to small font.
+
+\verysmall
+Switches to very small font.
+
+\big
+Switches to big font.
+
+\verybig
+Got it?
+
+\normalsize
+Switches to default size.
+
+\smaller
+Switches to the font smaller than the current one (e.g. \normalsize if
+you're currently using \big).
+
+\bigger
+Same as \smaller, the other way around.
+
+\color <color><text>
+Typesets <text> with <color>, which should be a triplet "R G B" with
+each value between 0 and 1.
+
+
+
+
+
+Sections (sections.ptx)
+=======================
+
+Sections are among the victims of my fanaticism for grid-typesetting.
+
+
+
+ Main sectioning commands
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+\declaresection <type><level>
+Creates a new section type with <level>. This is not necessary to make
+\sectioncommand work with <type>, but with it all declared sections of
+level larger than <level> are reset (i.e. their counters are set to 0).
+Sections with <type> chapter, section, subsection and paragraph are
+already declared.
+
+\incrementsection <type>
+Increments the counter of section <type>. If <type> hasn't been declared,
+a new <type> is created, but without a level.
+
+\getsectioncounter <type>
+Returns the value of section <type>'s counter, or -1 if there is no
+section of that type.
+
+\sectioncommand <type><title><alternate title>[<label>]
+Creates a section heading of type <type> with <title>. See the details
+of the gates involved below. The <type> refers to the parameter of the
+same name. The <alternate title> is very likely to disappear.
+
+\chapter <title>[<label>]
+Equivalent to \sectioncommand{chapter}{<title>}{<title>}[<label>].
+
+\section <title>[<label>]
+Equivalent to \sectioncommand{section}{<title>}{<title>}[<label>].
+
+\subsection <title>[<label>]
+Equivalent to \sectioncommand{subsection}{<title>}{<title>}[<label>].
+
+\paragraph <title>[<label>]
+Equivalent to \sectioncommand{paragraph}{<title>}{<title>}[<label>].
+
+\ifsectiontitle
+A conditional that is true when the section title is being typeset (sets by
+the section_pre gate below.
+
+\sectioninfile <optional star><title><space><type><space><file><space>
+Creates a section with the contents of a file, unless there's an optional
+star (useful to typeset only parts of a big document); <title> is a
+\freedef argument, hence it can be given between braces, double quotes
+or slashes (but the <space> is nonetheless mandatory); <type> is a section
+type, and <file> is a file to be input: it shouldn't have an extension
+(tex files are searched), but it can be a path with "/" as a separator.
+A \label is also created, with the tail of <file> as its argument. In
+other words, the following:
+
+ \sectioninfile "A chapter" chapter mydir/myfile
+
+is equivalent to:
+
+ \chapter{A chapter}
+ \label{myfile}
+ \input mydir/myfile.tex
+
+The \sectioncommand macro only contains a list gate, "section", itself
+containing the gates typesetting a section heading; all the gates belong
+to the "Section" family associated with the \Section command. Here are
+all the gates involved; the first number between parentheses indicates
+how many arguments the gate should receive, the second how many it
+returns.
+
+-------------------------------------------------------------------------------
+| section | (4, 0) | section_break | (1, 0) | section_vmode | (1, 0) |
+| | | | | section_clearpage | (1, 0) |
+| | | | | section_beforeskip | (1, 0) |
+| | | section_advance | (1, 0) | | |
+| | | section_advance | (1, 0) | | |
+| | | section_bookmark | (4, 0) | | |
+| | | section_toc | (3, 0) | | |
+| | | section_pre | (0, 0) | | |
+| | | section_typeset | (2, 2) | section_number | (2, 3) |
+| | | | | section_heading | (3, 2) |
+| | | | | section_addfont | (2, 2) |
+| | | | | section_addcolor | (2, 2) |
+| | | | | section_do | (2, 0) |
+| | | section_post | (0, 0) | | |
+| | | section_afterskip | (1, 0) | | |
+-------------------------------------------------------------------------------
+
+Here's how the gates work:
+
+> section <type><title><alternate title><label>
+The main list gate that contains all sections. In what follows, when I
+mention an attribute, I mean the attribute of the parameter <type>.
+
+> section_break <type>
+An l-gate managing whatever must happen before the section title is
+typeset.
+
+> section_vmode <type>
+Inserts a \par and removes last skip. (Conditional: ?vmode is =true.)
+
+> section_clear <type>
+Inserts a \clearpage. (Conditional: ?clear is =true.)
+
+> section_beforeskip <type>
+Creates a vertical skip before the heading. If the current page cannot
+accommodate ?beforeskip + ?minimum + ?afterskip worth of lines, then
+the section heading is typeset on the next page (using \breakpage). If
+the current page can accommodate the section, a skip of ?beforeskip
+lines is inserted. The gate doesn't return anything. (Conditional:
+?clear is not =true.)
+
+> section_advance <type>
+Increments the section counter, and resets the counters of those sections
+whose level is larger than <type>'s level (provided <type> has been
+declared with \declaresection and thus given a level). The gate doesn't
+return anything.
+
+> section_bookmark <type><title><alternate title><label>
+The bookmark is created with Navigator's \outline command as follows:
+
+ \outline[meta = <type>bookmark]{<bookmarklevel>}[<label>]{<alternate title>}
+
+only if ?bookmarklevel is defined. For types chapter, section, subsection
+and paragraph, the related !chapterbookmark, !sectionbookmark,
+!subsectionbookmark and !paragraphbookmark parameters are predefined,
+with simply ?meta set to =navigator. The <alternate title> is likely
+to disappear, since Navigator can handle things correctly. The gate
+doesn't return anything. (Conditional: ?link is =true.)
+
+> section_toc <type><title><alternate title>
+Writes what should be written to the auxiliary file for the next run
+to produce a table of contents. The gate doesn't return anything.
+(Conditional: ?toc is =true.)
+
+> section_pre
+Prepares the typesetting: open a group, sets \maintextfalse and
+\sectiontitletrue, and sets a LuaTeX attribute to 0 (so the section title
+is marked and can be spotted in the output routine). The gates doesn't
+return anything.
+
+> section_typeset <type><title>
+A list gate containing the gates used to typeset the section heading.
+It returns its final two arguments if only because list gate automatically
+return. See description below.
+
+> section_post
+Closes the group opened in section_pre. The gate doesn't return anything.
+
+> section_afterskip <type>
+Creates a vertical skip of ?afterskip lines. Also, calls \removenextindent
+if ?removenextindent is =true. The gate doesn't return anything.
+(Conditional: ?inline is not =true.)
+
+Here are the gates contained in section_pre. Beware, there the nature
+of the passed arguments slightly changes.
+
+> section_indent <type>
+Goes into horizontal mode and inserts an indent of width ?indent. The
+gate doesn't return anything.
+
+> section_number <type><title>
+Sets the section number, if ?number isn't =none. The number is surrounded
+by ?beforenumber and ?afternumber, converted to roman or arabic number
+according to the value of ?number, and the whole is passed to ?numbercommand
+(if it exists). The gate returns the following three arguments:
+<type><number><title>, where <number> is what's just been described.
+
+> section_heading <type><number><title>
+Sets the <title>: it is prefixed with <number>, passed to ?function if
+it exists, and suffixed with ?aftertitle if any. The gate returns <type>
+and <title> as just described.
+
+> section_addfont <type><title>
+Prefixes <title> with the value of font and returns its two arguments.
+
+> section_addcolor <type><title>
+Adds color to the title and returns its two arguments. (Conditional:
+?color is =true.)
+
+> section_do <type><title>
+At last! Inserts an horizontal skip of width ?indent, typesets <title>,
+and if ?inline isn't =true, sets \rightskip to ?ragged. Oh, yes, this
+could be divided into smaller gates. The gates doesn't return anything.
+
+The relevant parameters are the one corresponding to the type of the
+section, i.e. !chapter, !section, !subsection, !paragraph, which all
+have !metasection as their meta-parameter. The relevant attributes are:
+
+> vmode (true or false)
+If =true, goes into vertical mode before typesetting the heading.
+
+> clear (true or false)
+If =true, the section starts on a new page.
+
+> beforeskip (glue)
+The skip added before the heading.
+
+> afterskip (glue)
+The skip added after the heading.
+
+> minimum (number)
+The minimum number of lines that should be present on the page after the
+section heading. The "section_skip" gate above starts a new page if
+?beforeskip + ?afterskip + ?minimum can't be accommodated.
+
+> inline (true or false)
+If =true, the section heading is inserted at the beginning of the following
+paragraph.
+
+> number (arabic, roman or none)
+The way the section number should be typeset; =none means the number
+isn't typeset.
+
+> beforenumber
+Material to be added before the section number.
+
+> afternumber
+Material to be added after the section number.
+
+> numbercommand (control sequence)
+A macro to which the section number (surrounded by ?beforenumber and
+?afternumber) is passed.
+
+> function (control sequence)
+A macro to which the section title is passed.
+
+> aftertitle
+Material added after the section title.
+
+> font
+Font for the heading.
+
+> color (a triplet of values)
+Color for the heading.
+
+> indent (glue)
+The value of the glue added before the section title.
+
+> ragged (glue)
+The value of \rightskip for the heading.
+
+> toc (true or false)
+Sets whether the section should be added to the table of contents or
+not.
+
+> removenextindent (true or false)
+Sets whether the next paragraph should be unindented.
+
+> link (true or false)
+Sets whether a bookmark should be created with the section's title.
+
+> bookmarklevel (number)
+The level of the bookmark created for the section (how surprising).
+Further specification of the bookmark is done with !chapterbookmark,
+!sectionbookmark, !subsectionbookmark, !paragraphbookmark, whose only
+specification is that ?meta is set to =navigator. New "<type>bookmark"'s
+can be created, of course. See the documentation of Navigator for advanced
+use.
+
+
+
+ Various commands
+ ~~~~~~~~~~~~~~~~
+
+\tableofcontents
+Writes the table of contents (needs two runs). Not customizable for the
+time being!
+
+\newbreakpenalty <command>
+Defines <command> as a number below "-10000", suffixed with a \relax.
+The idea is to use it to break a page and check it in the output routine.
+
+\clearpage
+Fills the rest of the page with white space.
+
+\clearpagepenalty
+Penalty associated with \clearpage.
+
+\breakpage
+Same as \clearpage. They shouldn't be used for the same reasons. I use
+\clearpage at the end of a chapter, and \breakpage elsewhere (e.g. when
+a section heading would be orphaned and must move to the next page). The
+latter triggers nothing special, but the former can be identified in the
+output routine and for instance suppress footers.
+
+\breakpagepenalty
+Penalty associated with \breakpage.
+
+\needspace <dimen>
+Moves to the next page if there's less than <dimen>.
+
+\iflines <number><true><false>
+Executes <true> if there's at least <number> lines left on the page, and
+<false> otherwise.
+
+\ignorepars <material>
+Ignores incoming \par commands (and spaces too) and executes <material>.
+Useful when a blank line looks good in the source but you don't want it
+to signal a paragraph's end. The command is used by sectionning commands,
+so that if the section's title is supposed to be inserted at the beginning
+of the next paragraph (e.g. if ?inline is =true), you can nonetheless
+leave a blank line after the command.
+
+
+
+References (references.ptx)
+===========================
+
+There is a nice reference system, but it is a mess and should be rewritten
+in Lua. So it isn't fully described here.
+
+\label <name>
+Sets a label with <name>.
+
+\ref [<pre>][<post>] <reference type> {<name>}
+Makes a reference. Beware of the syntax: label should be enclosed between
+braces, because the left brace is the delimiter for <reference name>,
+which in turn should be enclosed in braces. E.g. a call is:
+
+ \ref page {mylabel}
+
+What is returned depends on <reference type>. If it is empty, then what
+is returned is the value of \ptx@label when \label{<name>} was issued.
+I think some commands define \ptx@label (nice in blocks, for instance).
+Otherwise, <reference type> can be page, chapter, section, subsection,
+paragraph or footnote (the latter if and only if \label was issued in a
+footnote). The returned text is prefixed with <pre> and suffixed with
+<post>.
+
+Also, if <reference type> is page, it may take three runs to make things
+work, because it is checked whether the returned value is the current
+page, in which case nothing is printed (it's stupid to refer to the
+current page). As mentioned above, this is a mess.
+
+There also are commands like "\sref{label}" and the like, which are
+shorthands for e.g. "\ref[ section~][] section {label}".
+
+
+
+
+
+Blocks (blocks.ptx)
+===================
+
+
+Blocks are what are called environments elsewhere: they mark up a section
+of the document, and generally apply some special operations. Given a
+block myblock, it is launched with "\myblock", closed with "\myblock/"
+and continued with "\myblock|". As you might imagine, this implies poking
+at the next token, which in some rare case might be troublesome; hence,
+\myblock can be followed by an optional ">" whose only goal is to protect
+the next token. (You can also use a \relax, of course.)
+
+\newblock <optional star><command><pre><optional start><optional middle><post>
+Defines <command> as a block. If the first optional star is present, the
+block is executed inside a group. If the second optional star is present,
+then the <middle> argument should be present too. The block is defined
+as follow: <pre> is executed at the beginning (i.e. "\myblock" or
+"\myblock>"), <middle> is executed when the block is continued (i.e.
+"\myblock|"), and <post> is executed when the block is closed (i.e.
+"\myblock/"). If <middle> is not given, then "\myblock|" does nothing.
+For instance, the following defines a grouped block (so the \rightskip
+setting doesn't affect the rest of the document); note the \par at the
+end, so the paragraph is built before the group is closed:
+
+ \newblock*\raggedblock{\rightskip=0pt plus 1fil\relax}{\par}
+
+And here's a simple example with a middle part:
+
+ \newblock\listblock{\vskip\baselineskip-- }
+ *{\par-- }
+ {\vskip\baselineskip}
+
+The continuation part can be used as a partial block opening: some markers
+are repeated (the dash) others are not (the vertical space).
+
+\newblocktype <command><pre><middle><post>
+Defines <command> as a block definition command like \newblock with
+<pre>, <middle> and <post> to be executed by default before the user-supplied
+versions. The \newblock command itself has been thus defined, with empty
+arguments. Arguments, after:
+
+ \newblocktype\newlist{\vskip\baselineskip--}
+ {\par--}
+ {\vskip\baselineskip}
+
+Then \newlist\listblock{}{} will have the same definition as in the
+previous example (no need to supply a <middle> part, it's in the default),
+and a variation can be created.
+
+\removenextindent
+Removes the indentation box of the next paragraph (used by section
+macros). Technically, it sets ajar the "noindent" gate in the "everypar"
+gate list (itself registered in the \everypar token list, which shouldn't
+be handled otherwise if flexibility is to be ensured). Those two gates
+belong to the "Everypar" family associated with the \Everypar command.
+
+\Indent
+Indents the next paragraph even if it \removenextindent has been issued
+(a \kern is added).
+
+
+
+
+
+
+Dealing with files (files.ptx)
+==============================
+
+\iffile [<format>]<file><true><false>
+Executes <true> if "kpse.find_file" (from the LuaTeX "kpse" library,
+implementing "kpathsea") with file type <format> (default: "tex"), and
+<false> otherwise.
+
+\ifffile [<format>]<file><true>
+Same as \iffile, except nothing happens when the file isn't found. Yes,
+three *f*'s.
+
+\inputfileor [<format>]<file><no file>
+Reads file <file> or executes <no file>.
+
+\writeout <optional star><general text>
+Writes <general text> to the auxilary file that is read at the beginning
+of each job. Without a star, writing happens at once (it's \immediate),
+with it writing is delayed until the current page is shipped out.
+
+
+
+
+
+Verbatim (verbatim.ptx)
+=======================
+
+\verbcatcodes
+A catcode table with usual verbatim catcodes: special characters have
+catcode 12, except space and end-of-line, which have catcode 13 and are
+defined to \quitvmode\spacecs and \quitvmode\par by default.
+
+\newverbatim <command>[<catcode table>]<pre><post>
+Defines a new block <command> with <catcode table>, <pre> at the beginning
+and <post> at the end. If <catcode table> is missing, \verbcatcodes is
+used. Verbatim blocks work as follows: first, there is no continuation
+command, i.e. only "\myverbatimblock" and "\myverbatimblock/" are allowed,
+not "\myverbatimblock|" (it might exist somewhere in the future). Second,
+the block opening takes one optional argument between brackets, which
+is the name of the verbatim block. Third, <pre> is executed at the
+beginning, and <post> at the end, as defined with \newverbatim. Fourth,
+the end statement "\myverbatimblock/" should be on a line of its own.
+What a verbatim block does is the following (not taking into account
+what <pre> and <post> execute): it stores its contents as is, along with
+the <catcode table> the block was declared with, and that's it. Then
+come the following two functions.
+
+\doverbatim [<name>]
+Executes the contents of <name> (with the current catcode regime). If
+name is missing, last is used, a special name which refers to the last
+verbatim block.
+
+\printverbatim [<name>]
+Executes the contents of <name> (or last if <name> is missing) with the
+catcode table associated with the block <name> was stored with. Since
+that catcode table is \verbcatcodes by default, it generally results in
+the contents being typeset.
+
+As an example:
+
+ \newverbatim\myverbatim{\vskip\baselineskip}
+ {\printverbatim\vskip\baselineskip}
+ \myverbatim[example]
+ \def\foo{hello !}%
+ \foo
+ \myverbatim/
+ And now we are going to print: \doverbatim[example].
+
+\verbatim
+A predefined verbatim block, designed as follows:
+
+ \newverbatim\verbatim{\codefont\parindent0pt}
+ {\vskip\baselineskip\printverbatim\relax
+ \vskip\baselineskip\removenextindent}
+
+I.e. it switches to the console-like font, sets the paragraph indentation
+to nothing, prints its contents between two blank lines and removes the
+indentation of the paragraph to follow.
+
+Each verbatim block adds a table to the Lua table "pitex.verbatims" (yes,
+with an *s*); the key is the block's name, and the value is a table with
+lines as values, indexed by numbers, plus a "regime" key which returns
+the catcode table's number of the block. For instance, the core operation
+performed by "\printverbatim[<name>]" is:
+
+ tex.print(pitex.verbatims[<name>].regime, pitex.verbatims[<name>])
+
+
+
+
+
+Insertions (inserts.ptx)
+========================
+
+Insertions are still a mess, and not related to parameters. Yet you can
+use:
+
+\footnote <text>
+Typesets <text> in a footnote. How astounding.
+
+\figure [<title>]
+A block creating a figure with title <title>.
+
+\table [<title>]
+The same as <figure>, except "Table" will be used instead of "Fig" in
+the caption.
+
+\infigure
+Ablock creating a figure in the main text, i.e. between paragraphs.
+
+
+
+
+Layout and output routine (output.ptx)
+=====================================
+
+The page layout can be specified with the !page parameter, whose attributes
+are:
+
+> width (dimension)
+The width of the page.
+
+> height (dimension)
+The height of the page.
+
+> baselineskip (glue)
+The baseline distance.
+
+> topskip (glue)
+The distance between the top of the textblock and the first baseline.
+
+> top (dimension)
+The height of the upper margin.
+
+> lines
+The number of lines on a page.
+
+> hsize (dimension)
+The width of the textblock.
+
+> left (dimension)
+Width of the left margin.
+
+> right (dimension)
+Width of the right margin. If specified, ?hsize is ignored and the
+texblock's width is set to ?width - ?left - ?right.
+
+> parindent (dimension)
+The width of the indentation.
+
+> parskip (glue)
+The glue between paragraphs.
+
+The output routine holds nothing very interesting for the moment. I
+used to redefine it for each job. Now it is set up with gates, but I
+haven't taken the time yet to make it really powerful. Plus I should
+rewrite everything in Lua as much as possible. Anyway, \output contains
+the "output" gate, from the "OutputRoutine" family associated with the
+\OutputRoutine command; the gates work as follows (passed arguments
+aren't indicated, because there isn't any; although someday perhaps the
+gates will pass a box between them, to be less dependant on \outputbox,
+which is used, by the way, instead of box 255, so any box register can
+be used):
+
+---------------------------------------------------------------
+| output | precheck | | |
+| | shipout | processmarginalia | |
+| | | inserts | inserts_figures |
+| | | | inserts_footnotes |
+| | | headers | |
+| | | ship | |
+| | | postship | |
+---------------------------------------------------------------
+
+> output
+The main list gate, holding the following.
+
+> precheck
+Checks whether \outputpenalty is smaller than \widowpenalty. If not,
+\vsize is increased or decreased (if there are inserts) by \baselineskip,
+so that the widow is accommodated or a line is given to the next page.
+In any case, the output box is repassed to TeX with "\holdinginserts=0".
+The gate is then set to "skip", so it isn't executed again.
+
+> shipout
+A list gate containing gates to write the page. By default it is skipped,
+so it isn't executed when the previous gate is, and vice-versa.
+
+> processmarginalia
+Insert the marginal notes (see \marginnote below). Can be obviously
+removed if there are no such notes.
+
+> inserts
+An l-gate containing the following two m-gates.
+
+> inserts_figures
+Adds the figures. (Conditional: the box "\ptx@insert_figures" isn't
+empty.)
+
+> inserts_footnotes
+Adds the footnotes. (Conditional: the box "\ptx@insert_footnotes" isn't
+empty.)
+
+> headers
+Inserts headers or footers, i.e. page number, running title, etc.
+
+> ship
+Ships out the page.
+
+> postship
+Resets some stuff (set "output_shipout" back to "skip"), and increments
+the page number.
+
+And now a lonely command:
+
+\marginnote [options]<text>
+Produces a marginal note with <text>. Uses the attributes (font,
+baselineskip, hsize) of the !marginnote parameter, with the following
+attributes:
+
+> hsize (dimension)
+Width of the textblock in the note.
+
+> hpos (ff, fr, rf, rr)
+Justification of the text: flushed on both sides, ragged on the right,
+ragged on the left, ragged on both sides.
+
+> font
+Font used to typeset the note.
+
+> parindent (dimension)
+Paragraph indentation for the note.
+
+> side (left or right)
+Side of the note relative to the textblock. That should depend on whether
+the note is on an odd or even side, but for the moment that is not the
+case.
+
+> gap (dimension)
+Distance between the textblock and the note.
+
+
+
+
+
+Lua facilities (lua.ptx and base.ptxlua)
+========================================
+
+\inputluafile <file>
+Shorthand for \directlua{dofile(kpse.find_file(<file>))}.
+
+\luacatcodes
+A catcode table with Lua-convenient catcodes: "#", "~", "%" and the end
+of file "^^M" are set to catcode 12.
+
+\luacode
+A block to write Lua code with the catcodes above.
+
+Lua code in PiTeX is organized mostly in gates; "pitex" is a gate table
+associated with family "pitex", "pitex.callback" is another table
+associated with family "pitex.callback", and "pitex.misc" is a third
+table associated (how surprising) with family "pitex.misc". The
+division of labour isn't perfectly defined, to say the least.
+
+The "pitex" family holds general commands, namely:
+
+> pitex.log (<message>, ...)
+Writes a <message> formatted as "string.format(<message>, ...)"
+
+> pitex.error (<message>, ...)
+Same as the previous function, but less friendly.
+
+The "pitex.callback" family is concerned with callback management. It
+has one interesting function (well, a gate) devoted to handling functions
+in callbacks as gates:
+
+> pitex.callback.register (<callback>, <gate>)
+An l-gate is registered in the callback, with subgates added to it; the
+name of the l-gate is the same as the callback where it is registered
+(with the family prefix "pitex.callback" added when necessary). For
+instance the list gate containing functions to used in "process_input_buffer"
+is called "process_input_buffer". Ordinary you would add a subgate to
+such a callback with the "add" action:
+
+ pitex.callback.add ("mygate", "process_input_buffer")
+
+However, the l-gate associated with the callback isn't created by
+default, nor registered in the callback. This means that "add" above
+will fail miserably if "process_input_buffer" hasn't been created
+beforehand. This function is meant to circumvent that: if the l-gate
+exists, it boils down to "add"; otherwise it creates it and registers
+it in the callback, and then add the gate. Note that the syntax follows
+the original "callback.register" function, with the callback first and
+the function second, even though you're adding gates to l-gates, with
+the syntax of "add" being subgates first, l-gate second. To manage the
+gates, thus created, you can then rely on the original gate actions.
+
+So, when I say `gates "X" and "Y" are registered in callback "Z"', it
+means `gates "X" and "Z" are subgates of l-gate "pitex.callback:Z",
+itself registered in callback "Z"'; unless otherwise indicated, "X" and
+"Z" belong to the "pitex.callback" family.
+
+And here are the gates registered in callbacks: "process_input_buffer"
+contains "convert", which turns latin1 into UTF-8. Verbatim blocks also
+register "process_verbatim", which is removed when the block ends. The
+"kerning" callback contains "french_punctuation", meant to add thin
+space before some punctuation mark, and "original_kerning", which is
+just a gate version of the "node.kerning" function. In "post_linebreak_filter"
+you'll find "pitex.misc:underline", which deals with material to be
+underline and "pitex.misc:mark_lines", which marks lines where a margin
+note is to be added. Those last two gates should be rewritten as complex
+l-gates (they're just big functions for the moment) some day. If you
+neither underline nor use marginal notes, you can remove them.
+
+
+
+
+Things that didn't make it elsewhere (pitex.tex)
+================================================
+
+General properties of the document can be set with the !document parameter,
+with the following attributes (the !navigator parameter has a ?meta
+attribute set to !document, which is why you'll find attributes here
+used by Navigator):
+
+> author
+The author of the document.
+
+> title
+The title of the document.
+
+> pdftitle
+The title that Navigator will use for the document's properties (defaults
+to ?title).
+
+> date
+The date of the document
+
+> pdfdate
+The date that Navigator will use for the document's properties; should
+be a properly formatted PDF date (this corresponds to the ?date attribute
+in the !navigator parameter; note that ?pdfdate doesn't defaults to
+?date, because the latter is supposed to hold a readable date).
+
+> subject
+For the document's properties.
+
+> keywords
+Again, the properties.
+
+> mode (outlines, bookmarks, thumbs, thumbnails, attachments, files, oc)
+What should be displayed in the navigation bar when the document is
+opened. See the documentation to Navigator.
+
+> layout (onepage, onecolumn, twopage, twocolumn, twopage*, twocolumn*)
+How the document is displayed when opened. See the documentation of
+Navigator.
+
+\newattribute <command>
+Defines <command> as an attribute register.
+
+\unsetattribute <command>
+Unsets attribute <command>.
+
+\attributenumber <command>
+Returns the number of attribute register <command> (not its value; you
+get the value with \the; this is to pass to Lua).
+
+\freedef <command>{<definition>}
+Same as "\def\foo#1{...}", except <command> can take its argument between
+double quotes (°") or slahes (/), and of course as single token or
+brace-delimited.
+
+\ifmaintext
+Conditional that is true when in main text; inserts, section headings,
+etc., should turn it to false, and sets their own to true.
+
+\newcatcodetable <command><catcode settings>
+Defines <command> as a catcode table with <catcode settings>; the latter
+are "<list of characters> = catcode" pairs, separated by commas, like
+the argument of texapi's \setcatcodes.
+
+\texcatcodes
+A code catcode table with the traditional catcodes.
+
+\inputpitexfile <file><space>
+Inputs <file> unless the initialization script says otherwise. If <file>
+has no extension, ".ptx" is used.
+
+\antigobblespace
+Adds a space if the next character has catcode 11 or is an opening
+parenthesis. For instance, after "\def\tex{\TeX\antigobblespace}", you
+can type "\tex is typesetting program" without worrying for gobbled
+space. Note that only ASCII letters have catcode 11 by default (not
+accented characters).
+
+\strut <height><depth>
+Produces an invisible vertical rule with the specified dimensions.
+
+\colorbox [<dimensions>]<RGB color><text>
+Puts <text> in a colored box with background color <RGB color> (e.g.
+three space-separated numbers between 0 and 1) and with padding <dimensions>.
+If <dimensions> is missing, padding is done according to the \extraboxspace
+length. Otherwise, if <dimensions> contains one value, it is used on all
+side; if there are two values (separated by commas), the first is used
+for top and bottom padding, and the second for left and right padding;
+with three value, the third specifies bottom padding, and a fourth
+specifies left padding. Very unlikely to remain in its present state or
+to remain at all.
+
+\extraboxspace
+Default padding for the previous command.
+
+\og
+Produces an opening guillemet: "«" (character "0x00AB").
+
+\fg
+Produces a closing guillemet: "»" (character "0x00BB"). Uses \antigobblespace.
+
+\trace
+Sets \tracingcommands to 3 and \tracingmacros to 2.
+
+\untrace
+Sets \tracingcommands and \tracingmacros to 0.
diff --git a/macros/plain/contrib/pitex/pitex.tex b/macros/plain/contrib/pitex/pitex.tex
new file mode 100644
index 0000000000..fcaa7f5132
--- /dev/null
+++ b/macros/plain/contrib/pitex/pitex.tex
@@ -0,0 +1,236 @@
+% This is piTeX, a set of macros I (Paul Isambert) use to
+% typeset documentations for my packages (that's why it is
+% archived on CTAN).
+%
+% Perhaps in the future, when this achieves some kind of
+% format-like completude, it'll be publicly announced. In the
+% meanwhile, a documentation exists (pitex-doc.pdf, also readable
+% in a text editor as pitex-doc.txt).
+%
+%
+% You can of course use those macros, but you are on your
+% own, and the files will probably be modified without announcement.
+% The file is supposed to be \input on plain TeX with LuaTeX, at least v.0.6.
+%
+%
+% The files needed are:
+%
+% texapi.tex (an independent package for programming)
+% yax.tex (an independent package for key=value interface)
+% gates.tex and gates.lua (an independant package for overall architecture)
+% navigator.tex (an independant package for PDF features)
+% lua.ptx and base.ptxlua (Lua side)
+% files.ptx (file management)
+% fonts.ptx, fonts.ptxlua and foundry-settings.lua
+% (fonts, should be independant some day; actually
+% fonts.ptxlua can be used independantly, but there is
+% no doc)
+% sections.ptx (sectionning commands)
+% blocks.ptx (text blocks)
+% references.ptx (labels and references)
+% verbatim.ptx (typesetting verbatim)
+% inserts.ptx (footnotes and figures, a mess)
+% output.ptx (output routine)
+%
+% The file i-pitex.lua is needed only to typeset the documentation with the
+% Interpreter package.
+%
+%
+% Date: November 2011.
+%
+%
+% User interface
+\input yax % which itself \input's texapi
+\input gates
+
+\setcatcodes{\@\_=11}
+\suppressoutererror=1
+
+% MESSAGES
+\def\ptx@error{\senderror{PiTeX}}
+\def\ptx@log#1{%
+ \immediate\write17{^^J#1^^J}%
+ }
+\def\ptx@warn#1{%
+ \ptx@log{PiTeX warning: #1}%
+ }
+
+% ATTRIBUTES
+\newcount\ptx@attribute_count
+\ptx@attribute_count=100 % The first 100 attributes are scratch.
+\def\newattribute#1{%
+ \advance\ptx@attribute_count1
+ \attributedef#1=\ptx@attribute_count
+ \xdefcs{ptx@attribute:\commandtoname#1}{\the\ptx@attribute_count}%
+ }
+\def\unsetattribute#1{#1=-"7FFFFFFF\relax}
+\def\attributenumber#1{\usecs{ptx@attribute:\commandtoname#1}}
+
+% FREEDEF
+\def\freedef#1{%
+ \def#1{%
+ \ifnextnospace"
+ {\ptx@freedef_quote#1}
+ {\ifnextnospace/
+ {\ptx@freedef_slash#1}
+ {\usecs{ptx@freedef_user:\commandtoname#1}}}%
+ }%
+ \defcs{ptx@freedef_user:\commandtoname#1}##1%
+ }
+\def\ptx@freedef_quote#1"#2"{%
+ \usecs{ptx@freedef_user:\commandtoname#1}{#2}%
+ }
+\def\ptx@freedef_slash#1/#2/{%
+ \usecs{ptx@freedef_user:\commandtoname#1}{#2}%
+ }
+
+
+\newbox\ptx@box_temp
+
+\newif\ifmaintext
+\maintexttrue
+
+% CATCODE TABLES
+\newcount\ptx@catcodetable_count
+\ptx@catcodetable_count=100 % First 100 are scratch.
+\def\newcatcodetable#1#2{%
+ \global\advance\ptx@catcodetable_count1
+ \chardef#1=\ptx@catcodetable_count
+ \begingroup
+ \setcatcodes{#2}%
+ \savecatcodetable#1%
+ \endgroup
+ }
+
+\newcatcodetable\texcatcodes{\@\_=12}
+
+\def\inputpitexfile#1 {\input #1.ptx }
+
+\inputpitexfile lua
+\inputpitexfile files
+\inputpitexfile fonts
+\inputpitexfile sections
+\inputpitexfile blocks
+\inputpitexfile references
+\inputpitexfile verbatim
+\inputpitexfile inserts
+\inputpitexfile output
+\input navigator.tex
+
+% AUXILIARY FILE
+\iffile{\jobname.aux}{%
+ \ptx@lua{%
+ remove_conversion()
+ tex.print("\luaescapestring{\noexpand\input\noexpand\jobname.aux}")
+ }%
+ \directlua{restore_conversion()}}
+\immediate\openout\ptx@auxfile=\jobname.aux
+
+% PDF SETTINGS
+\restrictparameter document : author title pdftitle date pdfdate subject keywords mode layout version\par
+\restrictattribute document : mode outlines bookmarks thumbs thumbnails attachments files oc\par
+\restrictattribute document : layout onepage onecolumn twopage twocolumn twopage* twocolumn*\par
+
+\suppressoutererror=1
+\let\ptx@bye\bye
+\def\bye{%
+ \passvalueand{\setattribute navigator : title = } document : pdftitle { }{}
+ % The "date" attribute in the "document" parameter isn't supposed to hold a
+ % PDF-date, as navigator expects.
+ \deleteattribute document : date
+ \passvalueand{\setattribute navigator : date = } document : pdfdate { }{}
+ \finishpdffile\ptx@bye
+ }
+
+\setattribute navigator : meta = document
+
+% Turns a dimen into PostScript points, without the unit (as wanted by PDF).
+\def\pdfpoint#1{%
+ \directlua{%
+ local d = "\the\dimexpr#1"
+ d = string.gsub(d, "pt", "")
+ tex.print(tostring(d * (72/72.27)))
+ }}
+
+
+
+
+% TEX SETTINGS
+\long\def\ptx@tex_set#1#2#3{%
+ \ifcs{#2}
+ {\usecs{#2}=#3\relax}
+ {\ptx@error{No TeX parameter `#2'}}
+ }
+
+\defactiveparameter tex {%
+ \parameterloop #1 : \ptx@tex_set
+ }
+
+\frenchspacing
+\maxdepth=\maxdimen
+
+\def\antigobblespace{%
+ \ifcatnext a{ }{\iffnext({ }}%
+ }
+
+\def\strut#1#2{%
+ \vrule height#1 depth#2 width0pt
+ }
+
+\newdimen\extraboxspace
+\newdimen\ptx@extraboxspace_top
+\newdimen\ptx@extraboxspace_right
+\newdimen\ptx@extraboxspace_bottom
+\newdimen\ptx@extraboxspace_left
+
+\newfornoempty\ptx@colorbox_loop{1}#2,{%
+ \ifcase#1
+ \ptx@extraboxspace_top =#2
+ \ptx@extraboxspace_right =#2
+ \ptx@extraboxspace_bottom =#2
+ \ptx@extraboxspace_left =#2
+ \or
+ \ptx@extraboxspace_right =#2
+ \ptx@extraboxspace_left =#2
+ \or
+ \ptx@extraboxspace_bottom =#2
+ \or
+ \ptx@extraboxspace_left =#2
+ \fi
+ \passarguments{\numexpr(#1+1)}%
+ }
+\def\colorbox{%
+ \ifnextnospace[\ptx@colorbox_setborders
+ {\ptx@extraboxspace_top =\extraboxspace
+ \ptx@extraboxspace_right =\extraboxspace
+ \ptx@extraboxspace_bottom =\extraboxspace
+ \ptx@extraboxspace_left =\extraboxspace
+ \ptx@colorbox_do}%
+ }
+\def\ptx@colorbox_setborders[#1]{%
+ \ptx@colorbox_loop{0}{#1,}%
+ \ptx@colorbox_do
+ }
+{\setcatcodes{pt=12}
+\gdef\noPT#1pt{#1 }}
+\def\ptx@colorbox_do#1#2{%
+ \bgroup
+ \setbox\ptx@box_temp=\hbox{#2}%
+ \hbox{%
+ \pdfliteral{
+ q #1 rg #1 RG
+ -\expandafter\noPT\the\ptx@extraboxspace_left
+ \expandafter\noPT\the\dimexpr(\ht\ptx@box_temp+\ptx@extraboxspace_top)\relax
+ \expandafter\noPT\the\dimexpr(\wd\ptx@box_temp+\ptx@extraboxspace_left+\ptx@extraboxspace_right)\relax
+ -\expandafter\noPT\the\dimexpr(\ht\ptx@box_temp+\ptx@extraboxspace_top+\dp\ptx@box_temp+\ptx@extraboxspace_bottom)\relax
+ re f Q}%
+ #2}%
+ \egroup
+ }
+
+\def\og{\char"00AB~} \def\fg{~\char"00BB\antigobblespace}
+
+\def\trace{\tracingcommands3 \tracingmacros2 }
+\def\untrace{\tracingcommands0 \tracingmacros0 }
+
+\restorecatcodes
diff --git a/macros/plain/contrib/pitex/references.ptx b/macros/plain/contrib/pitex/references.ptx
new file mode 100644
index 0000000000..038b90bc26
--- /dev/null
+++ b/macros/plain/contrib/pitex/references.ptx
@@ -0,0 +1,107 @@
+% This is a mess.
+\def\ptx@label{}%
+\def\label#1{%
+ \ifcs{ptx@label_user:#1}
+ {\ptx@error{Label `#1' already exists. Find another one}}
+ {\global\letcs{ptx@label_user:#1}\relax
+ \ptx@label_write{ptx@ref_user:#1}\ptx@label
+ \ptx@label_write{ptx@ref_chapter:#1}{\usevalue chapter : internalcount }%
+ \ptx@label_write{ptx@ref_section:#1}{\usevalue section : beforenumber \usevalue section : internalcount }%
+ \ptx@label_write{ptx@ref_subsection:#1}{\usevalue subsection : beforenumber \usevalue subsection : internalcount }%
+ \ptx@label_write{ptx@ref_paragraph:#1}{\usevalue paragraph : beforenumber \usevalue paragraph : internalcount }%
+ \iffootnote
+ \ptx@label_write{ptx@ref_footnote:#1}{\the\ptx@footnote_count}%
+ \fi
+ \ptx@write_toaux*{\defcs{ptx@ref_page:#1}{\the\pageno}}}%
+ }
+\def\ptx@label_with#1[#2]{%
+ \def\ptx@label{#1}%
+ \label{#2}%
+ \ignorespaces
+ }
+\def\ptx@label_and#1[#2]{%
+ \label{#2}%
+ #1%
+ }
+\def\ptx@label_withand#1#2[#3]{%
+ \ptx@label_with#1[#3]%
+ #2%
+ }
+\newif\ifptx@rerun
+\def\ptx@rerun{%
+ \unless\ifptx@rerun
+ \global\ptx@reruntrue
+ \ptx@warn{You need a rerun.}%
+ \fi
+ }
+\def\ptx@label_write#1#2{%
+ \unless\ifptx@rerun
+ \edef\ptx@temp{#2}%
+ \ifcs{#1}
+ {\passexpanded{\passexpandedcs{\reverse\iffstring}{#1}}\ptx@temp\ptx@rerun}
+ {\ptx@rerun}%
+ \fi
+ \ptx@write_toaux{\defcs{#1}{#2}}%
+ }
+
+
+
+\def\ref{%
+ \ifnextnospace[
+ {\ptx@ref_getpre}
+ {\ptx@ref_getpre[]}%
+ }
+\def\ptx@ref_getpre[#1]{%
+ \ifnextnospace[
+ {\ptx@ref_getpost{#1}}
+ {\ptx@ref_getpost{#1}[]}%
+ }
+\def\ptx@ref_getpost#1[#2]#3#{%
+ \passtrim{#3}{\ptx@ref_do{#1}{#2}}%
+ }
+\def\ptx@ref_do#1#2#3#4{%
+ \ifcs{ptx@ref_user:#4}
+ {\ifemptystring{#3}
+ {\ptx@ref_typeset{\usecs{ptx@ref_user:#4}}}
+ {\ifstring{#3}{page}\ptx@refpage_typeset\ptx@ref_typeset{\usecs{ptx@ref_#3:#4}}}
+ {#1}{#2}}
+ {??\ptx@rerun}%
+ }
+\protected\def\ptx@ref_typeset#1#2#3{#2#1#3}
+\newcount\ptx@ref_pagerefcount
+\protected\def\ptx@refpage_typeset#1#2#3{%
+ \global\advance\ptx@ref_pagerefcount1
+ \ifcs{ptx@pageref_number:\the\ptx@ref_pagerefcount}
+ {\unless\ifnum\usecs{ptx@pageref_number:\the\ptx@ref_pagerefcount}=#1\relax
+ #2#1#3%
+ \fi}
+ {??\ptx@rerun}%
+ \expandafter\ptx@pageref_write\expandafter{\the\ptx@ref_pagerefcount}%
+ }
+\def\ptx@pageref_write#1{%
+ \ptx@write_toaux*{\defcs{ptx@pageref_number:#1}{\the\pageno}}%
+ }
+
+\let\xref\ref
+\def\sref{\unskip\ref[ section~][] section}
+\def\Sref{\unskip\ref[ section~][] subsection}
+\def\cref{\unskip\ref[ chapitre~][] chapter}
+\def\pref{\unskip\ref[ (p.~][)] page}
+\def\Pref{\unskip\ref[ p.~][] page}
+\def\fref{\unskip\ref[ figure~][]}
+\def\Fref#1{\fref{#1}\pref{#1}}
+\def\tref{\unskip\ref[ tableau~][]}
+\def\Tref#1{\tref{#1}\pref{#1}}
+\def\fnref{\unskip\ref[ note~][] footnote}
+\def\FNref#1{\fnref{#1}\pref{#1}}
+\def\multiref#1"#2"{%
+ \passtrim{#1}{\ptx@multiref_loop{first}{#2}}%
+ }
+\def\ptx@multiref#1#2#3{%
+ \ptx@multiref_loop{#2}{#3}{#1}%
+ }
+\newfor\ptx@multiref_loop{2}#3{%
+ \reverse\iffstring{#1}{first}{, }%
+ \usecs{#3ref}{#2}%
+ \passarguments{}{#2}
+ }
diff --git a/macros/plain/contrib/pitex/sections.ptx b/macros/plain/contrib/pitex/sections.ptx
new file mode 100644
index 0000000000..a6d1f95431
--- /dev/null
+++ b/macros/plain/contrib/pitex/sections.ptx
@@ -0,0 +1,332 @@
+\newcount\ptx@penalty
+\ptx@penalty=-10000
+
+\def\newbreakpenalty#1{%
+ \advance\ptx@penalty-1
+ \edef#1{\the\ptx@penalty\relax}
+ }
+
+\newbreakpenalty\clearpagepenalty
+\def\clearpage{\vfil\penalty\clearpagepenalty}
+
+\newbreakpenalty\breakpagepenalty
+\def\breakpage{\vfil\penalty\breakpagepenalty}
+
+\def\whitepage{\clearpage\shipout\hbox{}\advancepageno}
+
+% TODO: Rewrite in Lua.
+\def\needspace#1{%
+ \par\penalty0
+ \ifdim\dimexpr\pagegoal-\pagetotal<\dimexpr#1\relax
+ \breakpage
+ \fi
+ }
+
+\def\iflines#1{%
+ \par\penalty0
+ \ifdim\dimexpr(\pagegoal-\pagetotal) < \numexpr(#1)\baselineskip
+ \expandafter\secondoftwo
+ \else
+ \expandafter\firstoftwo
+ \fi}
+
+\long\def\ignorepars#1{%
+ \ifxnextnospace\par
+ {\gobbleoneand{\ignorepars{#1}}}
+ {#1}%
+ }
+
+\def\sectioncommand#1#2#3{%
+ \ifnext[
+ {\ptx@sectioncommand{#1}{#2}{#3}}
+ {\ptx@sectioncommand{#1}{#2}{#3}[]}%
+ }
+
+\def\ptx@sectioncommand#1#2#3[#4]{%
+ \Section execute {section}{#1}{#2}{#3}{#4}%
+ \ignorepars{}%
+ }
+\def\makeroman#1{%
+ \uppercase\expandafter{\romannumeral#1\relax}%
+ }
+
+\newif\ifsectiontitle
+
+\luacode
+pitex.sections = { sections = {} }
+function pitex.sections.increment (type)
+ local n = type
+ type = pitex.sections.sections[type] or { num = 0 }
+ type.num = type.num + 1
+ local level = type.level
+ if level then
+ for name, tb in pairs(pitex.sections.sections) do
+ tb.num = tb.level > level and 0 or tb.num
+ end
+ end
+end
+function pitex.sections.counter (type)
+ type = pitex.sections.sections[type]
+ return type and type.num or -1
+end
+\luacode/
+
+\def\declaresection#1#2{%
+ \ptx@lua{pitex.sections.sections.#1 = {num = 0, level = #2}}%
+ }
+
+\def\incrementsection#1{%
+ \ptx@lua{pitex.sections.increment("#1")}%
+ }
+
+\def\getsectioncounter#1{%
+ \ptx@lua{tex.print(pitex.sections.counter ("#1"))}%
+ }
+
+%%% Gates in section %%%
+
+\gates new \Section {Section}
+\Section list {section} [4]
+ (section_break) [2]
+ . [section_vmode] [1] ?{conditional = \ifvalue #1 : vmode = true } {\par\removelastskip\penalty0 }
+ . [section_clearpage] [1] ?{conditional = \ifvalue #1 : clear = true } {\clearpage}
+ . [section_beforeskip] [2] ?{conditional = -\ifvalue #1 : clear = true }
+ {% Or skip some lines. The "beforeskip" attribute is
+ % the number of blank lines one wants before a section
+ % title, afterskip is the same thing after,
+ % and "minimum" is the minimum number of lines
+ % one wants after the section title.
+ \ifexpression{%
+ -\ifdim\dimexpr(\pagegoal-\pagetotal) < 0pt &
+ \ifdim\dimexpr(\pagegoal-\pagetotal) < \numexpr(\usevalueor #1 : beforeskip 0+\usevalueor #1 : afterskip 0+\usevalueor #1 : minimum 0) \baselineskip}
+ {\breakpage} % Not enough room.
+ {\passvaluenobracesand\vskip #1 : beforeskip \baselineskip{}}}
+%
+ [section_advance] [1] {\incrementsection{#1}}
+%
+ [section_bookmark] [4] ?{conditional = \ifvalue #1 : link = true } {%
+ \passvalueand{\outline[meta = #1bookmark]} #1 : bookmarklevel {[#4]{#3}}{}}
+%
+ [section_toc] [3] ?{conditional = \ifvalue #1 : toc = true } {%
+ \edef\ptx@temp{{#1}{\ifvalue #1 : number = none {}{\getsectioncounter{#1}}}{\unexpanded{\unexpanded{#3}}}{\the\pageno}}%
+ \expandafter\writeout\expandafter*\expandafter{\expandafter\noexpand\expandafter\ptx@toc\ptx@temp}}
+%
+ [section_pre] {%
+ \bgroup
+ \maintextfalse
+ \sectiontitletrue
+ \ptx@section_attribute=0\relax}
+%
+ (section_typeset) [2]
+ . [section_number] [2] {%
+ \ifvalue #1 : number = none
+ {\Section return3 {#1}{}{#2}}
+ {\Section return3
+ {#1}
+ {\usevalueor #1 : numbercommand \unbrace
+ {\usevalue #1 : beforenumber
+ \ifcasevalue #1 : number
+ \val roman \makeroman
+ \val arabic \unbrace
+ \endval{\getsectioncounter{#1}}%
+ \usevalue #1 : afternumber }}
+ {#2}}}
+%
+ . [section_heading] [3] {%
+ \Section return2 {#1}{#2\usevalueor #1 : function \unbrace{#3}\usevalue #1 : aftertitle }}
+%
+ . [section_addfont] [2] {\Section return {#1}{\usevalue #1 : font #2}}
+%
+ . [section_addcolor] [2] ?{conditional = \ifattribute #1 : color } {%
+ \Section return {#1}{\passvalueand\color #1 : color {{#2}}{}}}
+%
+ . [section_do] [2] {%
+ \noindent
+ \settovalue\hskip #1 : indent
+ #2%
+ \ifvalue #1 : inline = true {}{\settovalue\rightskip #1 :ragged \endgraf}}
+%
+ [section_post] {\egroup}
+%
+ [section_afterskip] [1] ?{conditional = -\ifvalue #1 : inline = true } {%
+ \vskip\usevalueor #1 : afterskip 0\baselineskip
+ \ifvalue #1 : removenextindent = true {\removenextindent}{}}
+
+\setparameter metasection :
+ clear = false
+ vmode = true
+ minimum = 3
+ inline = false
+ number = arabic
+ afternumber = "\kern.3em"
+ link = true
+ ragged = 0pt
+ toc = true
+ removenextindent = true
+
+\declaresection {chapter} 1
+\declaresection {section} 2
+\declaresection {subsection} 3
+\declaresection {paragraph} 4
+
+\setparameter chapter section subsection paragraph:
+ meta = metasection
+
+\setparameter chapter :
+ clear = true
+ number = arabic
+ afterskip = 3
+ indent = "0pt plus 1fill"
+ beforenumber = "chapitre "
+ afternumber = {\par\hfill}
+ bookmarklevel = 1
+
+\setparameter section :
+ beforeskip = 2
+ bookmarklevel = 2
+
+\setparameter subsection paragraph:
+ font = \it
+ beforeskip = 1
+
+\setparameter subsection:
+ beforenumber = "\getsectioncounter{section}."
+ minimum = 3
+ bookmarklevel = 3
+
+\setparameter paragraph:
+ beforenumber = "\getsectioncounter{section}.\getsectioncounter{subsection}."
+ minimum = 2
+ inline = true
+ aftertitle = ".\hskip.333em"
+ bookmarklevel = 4
+
+\setparameter chapterbookmark sectionbookmark subsectionbookmark paragraphbookmark:
+ meta = navigator
+
+\def\chaptertitle{}%
+\freedef\chapter{%
+ \sectioncommand{chapter}{#1}{#1}%
+ }
+\freedef\section{\sectioncommand{section}{#1}{#1}}
+\freedef\subsection{\sectioncommand{subsection}{#1}{#1}}
+\freedef\paragraph{\sectioncommand{paragraph}{#1}{#1}}
+
+\newif\ifptx@sectioninfile
+\def\sectioninfile{%
+ \ifnextnospace*
+ {\ptx@sectioninfilefalse
+ \gobbleoneand\ptx@sectioninfile}
+ {\ptx@sectioninfiletrue
+ \ptx@sectioninfile}%
+ }
+
+\freedef\ptx@sectioninfile{%
+ \ptx@sectioninfile_do{#1}%
+ }
+\def\ptx@sectioninfile_do#1 #2 #3 {%
+ \sectioncommand{#2}{#1}{#1}%
+ \ptx@section_getlabel{#3}%
+ \ifptx@sectioninfile
+ \input #3\relax
+ \fi
+ }
+\newstring/
+\newwhile\ptx@section_getlabel1{#1}{%
+ \ifcontains/{#1}%
+ {\splitstringat/{#1}{\gobbleoneand\changewhile}}
+ {\label{#1}\breakwhile}%
+ }
+\freedef\ptx@sectioninfile_gobble{%
+ \ptx@sectioninfile_gobblerest
+ }
+\def\ptx@sectioninfile_gobblerest#1 #2 #3 {}
+
+\newattribute\ptx@section_attribute
+%
+% Move pending titles if they happen. They shouldn't by themselves,
+% but the next paragraph might want more room than available
+% and clear the page. Hence this.
+% It reads box 255 backwards and move everything with set
+% section attribute to the next page. If the first such material
+% is a line, a (totally arbitrary) one-line skip is added, unless
+% there's already a skip on top of the next page that isn't
+% TeX-inserted (e.g. \baselineskip).
+% This is probably totally insufficient.
+%
+\luacode
+onelineskip = node.new(11)
+onelineskip.kern = tex.baselineskip.width
+remove_pendingtitles = function (head)
+ local item, first = node.slide(head), true
+ while item do
+ if node.has_attribute(item,\attributenumber\ptx@section_attribute) then
+ local nextitem = item.prev
+ node.remove(head,item)
+ if not ((item.id == 10 or item.id == 11) and first) then
+ node.insert_before(tex.lists.contrib_head, tex.lists.contrib_head, item)
+ tex.lists.contrib_head = item
+ end
+ if item.id == 0 then
+ if first then
+ first = false
+ if not (tex.lists.contrib_head.next.id == 10 and tex.lists.contrib_head.next.subtype == 0) then
+ node.insert_after(tex.lists.contrib_head, item, node.copy(onelineskip))
+ lualog("A title has been moved from page " .. tex.count[0] ..
+ " to page " .. tex.count[0]+1 .. ".")
+ end
+ end
+ else
+ end
+ item = nextitem
+ else
+ if item.id == 0 or item.id == 1 then
+ item = nil
+ else
+ item = item.prev
+ end
+ end
+ end
+end
+\luacode/
+
+\def\removependingtitles{%
+ \ptx@lua{remove_pendingtitles(tex.box[255].list)}%
+ }
+
+
+% ToC: this is an inefficient mess.
+
+\def\ptx@toc_tok{}
+\def\ptx@toc#1#2#3#4{%
+ \addright\ptx@toc_tok{%
+ \usecs{ptx@toc_item:#1}{#2}{#3}{#4}}%
+ }
+
+\def\tableofcontents{%
+ \bgroup
+ \parindent=0pt
+ \ifemptycommand\ptx@toc_tok
+ {\ptx@warn{No table of contents.}}
+ {\ptx@toc_tok}%
+ \egroup
+ }
+
+\long\def\ptx@def_tocitem#1#2#3#4{%
+ \defcs{ptx@toc_item:#1}##1##2##3{%
+ \edefcs{ptx@toc_current#1}{##1}%
+ \bgroup\par\quitvmode
+ \leftskip#2\relax
+ \rightskip=0pt plus 1fil
+ #3\reverse\iffemptystring{##1}{\llap{#4##1\kern1em}}%
+ ##2\leaders\hbox{. }\hfill\hbox to .8cm{\hfil##3}\par
+ \egroup}%
+ }
+
+\ptx@def_tocitem{chapter}{0pt}{\big\bf\sc}{}
+\ptx@def_tocitem{section}{1cm}{\sc}{}
+\ptx@def_tocitem{subsection}{2cm}{\it}
+ {\reverse\iffemptycs{ptx@toc_currentsection}{\usecs{ptx@toc_currentsection}.}}
+\ptx@def_tocitem{paragraph}{3cm}{}
+ {\reverse\iffemptycs{ptx@toc_currentsection}{\usecs{ptx@toc_currentsection}.}%
+ \reverse\iffemptycs{ptx@toc_currentsubsection}{\usecs{ptx@toc_currentsection}}.}
diff --git a/macros/plain/contrib/pitex/verbatim.ptx b/macros/plain/contrib/pitex/verbatim.ptx
new file mode 100644
index 0000000000..a4e2a7ed2b
--- /dev/null
+++ b/macros/plain/contrib/pitex/verbatim.ptx
@@ -0,0 +1,128 @@
+% Verbatim facilities.
+\newcatcodetable\verbcatcodes{\\\{\}\$\&\#\^\_\~\%=12,\ \^^M=13}
+
+\def\verb#1{%
+ \def\ptx@verb##1#1{\ptx@verb_punc{}{##1}\egroup}%
+ \bgroup
+ \catcodetable\verbcatcodes
+ \iffcommand\codefont\codefont
+ \ptx@verb
+ }
+
+% Hack to prevent French spacing before punctuation. I should find
+% something better (attributes marking material to be left alone when
+% the French punctuation kicks in in the "kerning" callback).
+\def\ptx@verb_punc#1#2{%
+ \ifelseif{%
+ {\ifcontains:{#2}} {\splitstringat:{#2}{\ptx@verb_glue{#1}:}}
+ {\ifcontains;{#2}} {\splitstringat;{#2}{\ptx@verb_glue{#1};}}
+ {\ifcontains!{#2}} {\splitstringat!{#2}{\ptx@verb_glue{#1}!}}
+ {\ifcontains?{#2}} {\splitstringat?{#2}{\ptx@verb_glue{#1}?}}
+ \iftrue {#1#2}}%
+ }
+\def\ptx@verb_glue#1#2#3#4{%
+ \ptx@verb_punc{#1#3\kern0pt#2}{#4}%
+ }
+
+\bgroup
+\setcatcodes{\ \^^M=13}%
+\gdef {\quitvmode\spacecs}%
+\gdef^^M{\quitvmode\par}%
+\egroup
+
+\def\tcode#1{{\codefont#1}}
+\long\def\com#1{%
+ \bgroup
+ \codefont
+ \string#1%
+ \egroup
+ \antigobblespace
+ }
+\freedef\arg{{\codefont\it<#1>}\iffnext\spacechar{\kern.2ex }}
+\freedef\barg{{\codefont\char"007B\relax\arg{#1}\char"007D\relax}}
+\freedef\oarg{{\codefont[\arg{#1}]}}
+
+\luacode
+pitex.verbatims = {}
+local function prepare_verbatim (chunk, name)
+ return function (str)
+ if not str:match("^%s*\noexpand\\" .. chunk .. "/") then
+ table.insert(pitex.verbatims[name], str)
+ return "%"
+ else
+ pitex.callback.remove("process_verbatim", "process_input_buffer")
+ return "\noexpand\\usecs{ptx@inner_verbatimstop:" .. chunk .. "}"
+ end
+ end
+end
+
+function install_verbatim (chunk, regime, name)
+ pitex.verbatims[name] = { regime = regime }
+ pitex.callback.process_verbatim = prepare_verbatim(chunk, name)
+ pitex.callback.register("process_input_buffer", "process_verbatim")
+end
+
+function do_verbatim(name, exec)
+ if exec then
+ tex.print(pitex.verbatims[name])
+ else
+ tex.print(pitex.verbatims[name].regime, pitex.verbatims[name])
+ end
+end
+\luacode/
+
+\def\ptx@verbatim_last{}
+
+\def\newverbatim#1{%
+ \ifnext[
+ {\ptx@newverbatim#1}
+ {\ptx@newverbatim#1[\verbcatcodes]}%
+ }
+
+\long\def\ptx@newverbatim#1[#2]#3#4{%
+ \edef#1{%
+ \bgroup\primunexpanded{#3}%
+ \bgroup\primunexpanded{\setcatcodes{\^^M=12}}%
+ \noexpand\ptx@verbatim{\commandtoname#1}{#2}}%
+ \defcs{ptx@inner_verbatimstop:\commandtoname#1}{#4\egroup}%
+ }
+
+\bgroup
+\setcatcodes{\^^M=12}%
+\gdef\ptx@verbatim#1^^M{%
+ \egroup%
+ \ptx@verbatim_prepare#1\relax%
+ }%
+\egroup
+
+\def\ptx@verbatim_prepare#1#2{%
+ \ifnext[
+ {\ptx@verbatim_do{#1}{#2}}
+ {\ptx@verbatim_do{#1}{#2}[last]}%
+ }
+
+\def\ptx@verbatim_do#1#2[#3]{%
+ \gdef\ptx@verbatim_last{#3}%
+ \directlua{install_verbatim("#1", \the#2, "#3")}%
+ }
+
+\newverbatim\verbatim{}
+ {\codefont\parindent0pt %\hsize=\maxdimen
+ \directlua{pitex.callback.close("french_punctuation", "kerning")}\relax
+ \vskip\baselineskip\printverbatim\relax
+ \directlua{pitex.callback.open("french_punctuation", "kerning")}
+ \vskip\baselineskip\removenextindent}
+
+\def\doverbatim{%
+ \ifnext[
+ {\ptx@doverbatim{true}}
+ {\ptx@doverbatim{true}[\ptx@verbatim_last]}%
+ }
+\def\printverbatim{%
+ \ifnext[
+ {\ptx@doverbatim{nil}}
+ {\ptx@doverbatim{nil}[\ptx@verbatim_last]}%
+ }
+\def\ptx@doverbatim#1[#2]{%
+ \directlua{do_verbatim("#2", #1)}%
+ }