From c8e2fd77d9df2162ab806d3f1fe7d13c022fbc1e Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Sat, 3 Dec 2011 16:26:10 +0000 Subject: pitex (18nov11) git-svn-id: svn://tug.org/texlive/trunk@24731 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/tex/plain/pitex/base.ptxlua | 85 ++ Master/texmf-dist/tex/plain/pitex/blocks.ptx | 40 + Master/texmf-dist/tex/plain/pitex/files.ptx | 39 + Master/texmf-dist/tex/plain/pitex/fonts.ptx | 345 ++++-- Master/texmf-dist/tex/plain/pitex/fonts.ptxlua | 1211 ++++++++++++++++++++++ Master/texmf-dist/tex/plain/pitex/inserts.ptx | 113 ++ Master/texmf-dist/tex/plain/pitex/lua.ptx | 26 + Master/texmf-dist/tex/plain/pitex/output.ptx | 311 ++++++ Master/texmf-dist/tex/plain/pitex/pitex.tex | 425 +++----- Master/texmf-dist/tex/plain/pitex/references.ptx | 107 ++ Master/texmf-dist/tex/plain/pitex/sections.ptx | 435 +++++--- Master/texmf-dist/tex/plain/pitex/verbatim.ptx | 128 +++ 12 files changed, 2730 insertions(+), 535 deletions(-) create mode 100644 Master/texmf-dist/tex/plain/pitex/base.ptxlua create mode 100644 Master/texmf-dist/tex/plain/pitex/blocks.ptx create mode 100644 Master/texmf-dist/tex/plain/pitex/files.ptx create mode 100644 Master/texmf-dist/tex/plain/pitex/fonts.ptxlua create mode 100644 Master/texmf-dist/tex/plain/pitex/inserts.ptx create mode 100644 Master/texmf-dist/tex/plain/pitex/lua.ptx create mode 100644 Master/texmf-dist/tex/plain/pitex/output.ptx create mode 100644 Master/texmf-dist/tex/plain/pitex/references.ptx create mode 100644 Master/texmf-dist/tex/plain/pitex/verbatim.ptx (limited to 'Master/texmf-dist/tex/plain') diff --git a/Master/texmf-dist/tex/plain/pitex/base.ptxlua b/Master/texmf-dist/tex/plain/pitex/base.ptxlua new file mode 100644 index 00000000000..615632db0d1 --- /dev/null +++ b/Master/texmf-dist/tex/plain/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/Master/texmf-dist/tex/plain/pitex/blocks.ptx b/Master/texmf-dist/tex/plain/pitex/blocks.ptx new file mode 100644 index 00000000000..13ba4b01e16 --- /dev/null +++ b/Master/texmf-dist/tex/plain/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/Master/texmf-dist/tex/plain/pitex/files.ptx b/Master/texmf-dist/tex/plain/pitex/files.ptx new file mode 100644 index 00000000000..212b05fc076 --- /dev/null +++ b/Master/texmf-dist/tex/plain/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/Master/texmf-dist/tex/plain/pitex/fonts.ptx b/Master/texmf-dist/tex/plain/pitex/fonts.ptx index 6a3ceb019a8..a504846d6b0 100644 --- a/Master/texmf-dist/tex/plain/pitex/fonts.ptx +++ b/Master/texmf-dist/tex/plain/pitex/fonts.ptx @@ -1,44 +1,48 @@ -% I think my version of the following package might be modified. -% So no way to be sure it'll work but on my computer -% (anyway I use non-free fonts, so). -% -\input luaotfload.sty +\inputluafile fonts.ptxlua \pdfadjustspacing=2 -\directlua{% -% From otfl-font-dum.lua - local byte = string.byte - fonts.expansions.setups['threepercent'] = { - - stretch = 3, shrink = 3, step = .5, factor = 1, - - [byte('A')] = 0.5, [byte('B')] = 0.7, [byte('C')] = 0.7, [byte('D')] = 0.5, [byte('E')] = 0.7, - [byte('F')] = 0.7, [byte('G')] = 0.5, [byte('H')] = 0.7, [byte('K')] = 0.7, [byte('M')] = 0.7, - [byte('N')] = 0.7, [byte('O')] = 0.5, [byte('P')] = 0.7, [byte('Q')] = 0.5, [byte('R')] = 0.7, - [byte('S')] = 0.7, [byte('U')] = 0.7, [byte('W')] = 0.7, [byte('Z')] = 0.7, - [byte('a')] = 0.7, [byte('b')] = 0.7, [byte('c')] = 0.7, [byte('d')] = 0.7, [byte('e')] = 0.7, - [byte('g')] = 0.7, [byte('h')] = 0.7, [byte('k')] = 0.7, [byte('m')] = 0.7, [byte('n')] = 0.7, - [byte('o')] = 0.7, [byte('p')] = 0.7, [byte('q')] = 0.7, [byte('s')] = 0.7, [byte('u')] = 0.7, - [byte('w')] = 0.7, [byte('z')] = 0.7, [byte('2')] = 0.7, [byte('3')] = 0.7, [byte('6')] = 0.7, - [byte('8')] = 0.7, [byte('9')] = 0.7, - } - } + \def\currentfont{} +\def\currentsize{normal} \def\currentstyle{rm} \def\currentweight{rg} \def\currentcase{lc} \def\makecurrentfont{% - \csname\currentfont @\currentstyle @\currentweight @\currentcase\endcsname + \csname\currentfont @\currentsize @\currentstyle @\currentweight @\currentcase\endcsname } -\def\it{% - \def\currentstyle{it}% +\def\normalsize{% + \def\currentsize{normal}% \makecurrentfont } -\def\bf{% - \def\currentweight{bf}% +\def\small{% + \def\currentsize{small}% \makecurrentfont - } -\def\sc{% - \def\currentcase{sc}% + } +\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{% @@ -46,10 +50,6 @@ \makecurrentfont } \def\rmstring{rm} -\def\ital#1{{\it#1}} -\def\bold#1{{\bf#1}} -\def\scap#1{{\sc#1}} -\def\rom#1{{\rm#1}} \def\emph{% \ifx\currentstyle\rmstring \expandafter\ital @@ -57,71 +57,252 @@ \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. - type % otf, etc. The font parameter can't even load tfm's... 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.). - roman % The part of the file's name for the roman. - bold % The part of the file's name for the bold roman. - italic % The part of the file's name for the italic. - bolditalic % The part of the file's name for the bold italic. - % What happens is: it loads . at - % then the same with , 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 - features = +onum;+liga;+trep;expansion=threepercent; - type = otf % Oh yes, this is default! + bold = Bold + italic = Italic + features = "+onum; +liga; +trep; +tlig; expansion=30 20 5;" -\setparameter font: meta = metafont\par +\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{% - \passcs\font{\passvalue\commandtoname font : command @#1@#2@lc} = - "\usevalue font : name \usevalue font : #3 .\usevalue font : type :\usevalue font : features " at \usevalue font : size \relax - \passcs\font{\passvalue\commandtoname font : command @#1@#2@sc} = - "\usevalue font : name \usevalue font : #3 .\usevalue font : type :+smcp;\usevalue font : features " at \usevalue font : size \relax + \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 {% - \ifattribute #1 : command - {% - \passvaluenobraces\edef #1 : command {% - \def\noexpand\currentfont{\passvalue\commandtoname #1 : command }% - \noexpand\makecurrentfont - }% - % If #1 = name isn't defined, roman, bold, etc. are supposed to hold the - % font's full (file) name. - \ifattribute #1 : roman - {\dofont{rm}{rg}{roman}% - \ifattribute #1 : bold - {\dofont{rm}{bf}{bold}}% - {}}% - {}% - \ifattribute #1 : italic - {\dofont{it}{rg}{italic}% - \ifattribute #1 : bolditalic - {\dofont{it}{bf}{bolditalic}}% - {}}% - {}}% - {}% - \ifvalue #1 : command = {\mainfont} \mainfont{}% Calls font if it is \mainfont. - \deleteattribute #1 : command - \deleteattribute #1 : name - \deleteattribute #1 : type - \deleteattribute #1 : size - \deleteattribute #1 : features - \deleteattribute #1 : roman - \deleteattribute #1 : bold - \deleteattribute #1 : italic - \deleteattribute #1 : bolditalic + \FontLoader execute {fontloader}% } -% + + + + + + + \long\def\color#1#2{% \pdfcolorstack0 push {#1 rg #1 RG}% #2% \pdfcolorstack0 pop% - } \ No newline at end of file + } + +\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/Master/texmf-dist/tex/plain/pitex/fonts.ptxlua b/Master/texmf-dist/tex/plain/pitex/fonts.ptxlua new file mode 100644 index 00000000000..391ee9a6766 --- /dev/null +++ b/Master/texmf-dist/tex/plain/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/Master/texmf-dist/tex/plain/pitex/inserts.ptx b/Master/texmf-dist/tex/plain/pitex/inserts.ptx new file mode 100644 index 00000000000..70155f5d7eb --- /dev/null +++ b/Master/texmf-dist/tex/plain/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/Master/texmf-dist/tex/plain/pitex/lua.ptx b/Master/texmf-dist/tex/plain/pitex/lua.ptx new file mode 100644 index 00000000000..b41c5af338e --- /dev/null +++ b/Master/texmf-dist/tex/plain/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/Master/texmf-dist/tex/plain/pitex/output.ptx b/Master/texmf-dist/tex/plain/pitex/output.ptx new file mode 100644 index 00000000000..20a0dc677f2 --- /dev/null +++ b/Master/texmf-dist/tex/plain/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/Master/texmf-dist/tex/plain/pitex/pitex.tex b/Master/texmf-dist/tex/plain/pitex/pitex.tex index 5b62b8b5529..fcaa7f5132c 100644 --- a/Master/texmf-dist/tex/plain/pitex/pitex.tex +++ b/Master/texmf-dist/tex/plain/pitex/pitex.tex @@ -3,8 +3,10 @@ % archived on CTAN). % % Perhaps in the future, when this achieves some kind of -% format-like completude, there'll be a documentation and -% it'll be publicly announced. +% 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. @@ -13,333 +15,168 @@ % % The files needed are: % -% texapi.tex (an independent package \input in the following file) -% yax.tex (an independent package) -% fonts.ptx (piTeX font "management") -% luaotfload.sty (an independent package (not by me)) -% sections.ptx (piTeX sectionning commands) +% 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: July 2010. +% +% Date: November 2011. % % % User interface \input yax % which itself \input's texapi +\input gates \setcatcodes{\@\_=11} -\def\ptx@error{\senderror{PiTeX}} +\suppressoutererror=1 -% This to write in latin1. -\directlua{% - lualog = function (message) - texio.write_nl("") texio.write_nl("") - texio.write_nl(message) - texio.write_nl("") texio.write_nl("") - end - function convert(buf) - return string.gsub(buf,"(.)", function (ch) - return unicode.utf8.char(string.byte(ch)) - end) - end - callback.register('process_input_buffer',convert) - } +% 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 - \edefcs{ptx@attribute_#1}{10\the\ptx@attribute_count}% + \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}% } -\def\ptx@attribute_number#1{\usecs{ptx@attribute_#1}} -\def\ptx@attribute_set#1{\passcs\attribute{ptx@attribute_#1}=1\relax} -\def\ptx@attribute_unset#1{\passcs\attribute{ptx@attribute_#1}=-"7FFFFFFF\relax} -\newdimen\urvsize -\urvsize\vsize -\def\inputpitexfile#1 {\input #1.ptx } +\newbox\ptx@box_temp -\inputpitexfile fonts -\inputpitexfile sections -% No footnotes in documentations. -%\input footnotes.tex -% I redefine the output routine on every job. -%\input output.tex +\newif\ifmaintext +\maintexttrue -\defactiveparameter document{% - \pdfinfo{% - /Author (\usevalueor #1 : author {Unknown author}) - /Title (\usevalueor #1 : pdftitle {\usevalueor #1 : title {No title}}) - \ifattribute #1 : subject {/Subject (\usevalue #1 : subject )}{} - \ifattribute #1 : keywords {/Keywords (\usevalue #1 : keywords )}{} - }% - \pdfcatalog{% - /PageMode - \ifcasevalue #1 : display - \val outlines /UseOutlines - \val signets /UseOutlines % French! - \val thumbs /UseThumbs - \val thumbnails /UseThumbs - \val full /FullScreen - \val fullscreen /FullScreen - \elseval /UseNone - \endval - }% +% 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 } -\restrictparameter document : author title pdftitle date subject keywords display version\par -\restrictattribute document:display outlines signets thumbs thumbnails full fullscreen\par +\newcatcodetable\texcatcodes{\@\_=12} +\def\inputpitexfile#1 {\input #1.ptx } -\defactiveparameter page{% - \settovalue\pdfpagewidth #1 : pagewidth - \settovalue\pdfpageheight #1 : pageheight - \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 : pagewidth - pageheight - 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: - pageheight = 28cm - pagewidth = 21cm - hsize = 15cm - baselineskip = 12pt - lines = 42 - left = 1in - top = 1in - parskip = 0pt +\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 -% Should be used with YaX to set parameters. -\long\pdef\newblock{% - \ifnext*{\gobbleoneand\ptx@newblock_group} - {\ptx@newblock_nogroup}% - } -\def\ptx@newblock_nogroup#1#2#3{% - \def#1{\ifnext/{\gobbleoneand{#3}}{#2}}% - } -\def\ptx@newblock_group#1#2#3{% - \def#1{\ifnext/{\gobbleoneand{#3\egroup}}{\bgroup#2}}% - } -\def\antigobblespace{% - \ifcatnext a{ }{\iffnext({ }}% +\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 -% PDF links -\def\dest#1#2{% - \ifcs{ptx@link_name=#1} - {\ptx@error{Link `#1' already exists}#2} - {\letcs{ptx@link_name=#1}\relax - \bgroup - \ifvmode - \pdfdest name {#1} xyz #2% - \else - \setbox0=\hbox{#2}% - \raise\ht0\hbox{\pdfdest name {#1} xyz}#2% - \fi - \egroup - }% - } -\def\link#1#2{% - \pdfstartlink attr {/Border [0 0 0]} goto name {#1}#2\pdfendlink - } +% 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))) + }} -% Margin notes à la Bringhurst: aligned with the line they refer -% to, unless they bleed into the bottom margin, in which case they -% are shifted up so that their bottom line is aligned with the -% page's bottom line. Totally experimental. It'd be nice if I could -% do that with sections too (so that a note doesn't bleed into the -% next section). -\newattribute{note} -\def\note{\ifnext[{\ptx@note}{\ptx@note[0pt]}} -\def\ptx@note[#1]#2{% - \ptx@attribute_set{note}% - \setbox0=\hbox{\vtop{% We need the box's height minus its first line, hence the vtop. - \notefont \baselineskip=9pt - \hsize\dimexpr\pdfpagewidth-(\pdfhorigin+\hsize+4em)\relax - \rightskip=0pt plus 1fil - \it\noindent #2}}% - \ptx@attribute_unset{note}% - \ht0=0pt \dp0=0pt - \vadjust{% - \kern-\dimexpr(2pt+#1)\relax - \moveright\dimexpr\hsize+1em - \box0 - \kern\dimexpr2pt+#1}% - \vrule height 0pt width 0pt depth 2pt -% Problem: if the line where the note appears is last of its -% box, the box has no depth and vfilling doesn't work properly. - }% -\directlua{% - % - % Box255 is traversed from the last node upward. - % Each node with dimension adds to the "remaining height", - % which is used when a note is encountered: if the note's depth - % (minus its last box's depth) is larger than the remaining height, - % then it is shifted upward, so that it doesn't bleed in the bottom - % margin. When computing the remaining height, kerns and glues aren't - % taken into account until a box has been seen (and the depth of - % that first box isn't added either), so the note doesn't even - % bleed into available page space that isn't filled with text. - % - process_marginalia = function (head) - local remainingheight, first, item = 0, true, node.slide(head) - while item do - if node.has_attribute(item,\ptx@attribute_number{note}) then - local depth = item.list.depth - node.tail(item.list.list).depth - if depth > remainingheight then - lualog("A note has been shifted on page " .. tex.count[0] .. - " by an amount of " .. (depth-remainingheight)/65536 .. "pt.") - item.list.shift = remainingheight-depth - end - elseif node.has_field(item, "height") then - remainingheight = remainingheight+item.height - if first then - first = false - else - remainingheight = remainingheight + item.depth - end - elseif node.has_field(item,"kern") then - if not first then - remainingheight = remainingheight+item.kern - end - elseif node.has_field(item, "spec") then - if not first then - remainingheight = remainingheight+item.spec.width - end - end - item = item.prev - end - end - } -\def\processmarginalia{% - \directlua{process_marginalia(tex.box[255].list)}% - } -\def\stoppage{\penalty-10000\relax} -\def\clearpage{\vfil\stoppage\relax} -\def\needspace#1{% - \penalty0 - \ifdim\dimexpr\pagegoal-\pagetotal<\dimexpr#1\relax - \clearpage - \fi - } -% Verbatim facilities. -\newbox\ptx@box_temp -\def\tcode#1{{\codefont#1}} -\long\def\com#1{% - \bgroup - \codefont - \string#1% - \egroup - \antigobblespace +% TEX SETTINGS +\long\def\ptx@tex_set#1#2#3{% + \ifcs{#2} + {\usecs{#2}=#3\relax} + {\ptx@error{No TeX parameter `#2'}} } -\def\arg#1{{\codefont\it<#1>}\iffnext\spacechar{\kern.2ex }} -\def\barg#1{{\codefont\char"007B\relax{\it<#1>}\char"007D\relax}} -\def\oarg#1{{\codefont[{\it<#1>}]}} - -\def\exampleskip{\vskip\baselineskip} -\def\example{% - \exampleskip - \bgroup - \parindent0pt - \setcatcodes{\\\{\}\$\&\#\^\_\ \~\%=12,\^^M=13}% - \codefont - \readexample - }% -\let\ptx@example_original\example -\bgroup -\setcatcodes{\\=12,\|=0,\^^M=13}% -|gdef|readexample#1^^M#2\example/{#2|egroup|exampleskip}% -|restorecatcodes% -\setcatcodes{\^^M=13}% -\gdef^^M{\quitvmode\endgraf}% -\egroup% -\def\verb#1{% - \def\ptx@verb##1#1{##1\egroup}% - \bgroup - \setcatcodes{\\\{\}\$\&\#\^\_\ \~\%=12}% - \codefont - \ptx@verb +\defactiveparameter tex {% + \parameterloop #1 : \ptx@tex_set } -% -% Read and process examples. -% -\newwrite\ptx@examplewrite - -\bgroup -\setcatcodes{\%=12,\/=14} -\gdef\Example{/ - \immediate\openout\ptx@examplewrite=\jobname.pex - \immediate\write\ptx@examplewrite{% This is a scratch file used to typeset examples in \jobname.tex.}/ - \immediate\write\ptx@examplewrite{% You can delete it.}/ - \immediate\write\ptx@examplewrite{%}/ - \bgroup - \setcatcodes{\\\#\%\^^M\ =12}/ - \writeexample - }/ -\egroup - -\bgroup -\setcatcodes{\^^M=12}% -\gdef\writeexample#1^^M{% - \immediate\write\ptx@examplewrite{\noexpand\example}% - \ptx@writeexample}% -\gdef\ptx@writeexample#1^^M{% - \passexpanded{\ifstring{#1}}{\examplestring}% - {\immediate\write\ptx@examplewrite{\string\example/}% - \immediate\closeout\ptx@examplewrite\egroup% - \processexample}% - {\immediate\write\ptx@examplewrite{\primunexpanded{#1}}% - \ptx@writeexample}% - }% -{\setcatcodes{\\=12,\|=0}|gdef|examplestring{\Example/}}% -\egroup +\frenchspacing +\maxdepth=\maxdimen -\def\typesetexample{% - {\input\jobname.pex }% - } -\def\doexamplenogroup{\let\example\ptx@Example\input\jobname.pex \let\example\ptx@example_original} -\def\doexample{{\doexamplenogroup}} -\def\ptx@Example{% - \iffnext/{\gobbleoneand\endinput}% +\def\antigobblespace{% + \ifcatnext a{ }{\iffnext({ }}% } -% To be redefined for different layout. -\def\processexample{\typesetexample\doexample} -\def\verb#1{% - \def\ptx@verb##1#1{##1\egroup}% - \bgroup - \setcatcodes{\\\{\}\$\&\#\^\_\ \~\%=12}% - \codefont - \ptx@verb +\def\strut#1#2{% + \vrule height#1 depth#2 width0pt } - \newdimen\extraboxspace \newdimen\ptx@extraboxspace_top \newdimen\ptx@extraboxspace_right @@ -385,13 +222,15 @@ -\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+\dp0+\ptx@extraboxspace_bottom)\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\trace{\tracingcommands4 \tracingmacros4 } +\def\og{\char"00AB~} \def\fg{~\char"00BB\antigobblespace} + +\def\trace{\tracingcommands3 \tracingmacros2 } \def\untrace{\tracingcommands0 \tracingmacros0 } -\restorecatcodes \ No newline at end of file +\restorecatcodes diff --git a/Master/texmf-dist/tex/plain/pitex/references.ptx b/Master/texmf-dist/tex/plain/pitex/references.ptx new file mode 100644 index 00000000000..038b90bc261 --- /dev/null +++ b/Master/texmf-dist/tex/plain/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/Master/texmf-dist/tex/plain/pitex/sections.ptx b/Master/texmf-dist/tex/plain/pitex/sections.ptx index 1c56112bdac..a6d1f954319 100644 --- a/Master/texmf-dist/tex/plain/pitex/sections.ptx +++ b/Master/texmf-dist/tex/plain/pitex/sections.ptx @@ -1,144 +1,180 @@ -\def\ptx@bookmark_list{} -\def\ptx@bookmark_stack{} -\def\createbookmark{% - \ifnextnospace[{\ptx@createbookmark}{\ptx@createbookmark[]}% - } -\def\ptx@createbookmark[#1]#2#3#4{% - \passexpanded{\ptx@bookmark_add{#2}{{#3}{#4}{#1}}}\ptx@bookmark_stack - } -\bgroup -\setcatcodes{\{\}=12,<=1,>=2} -\gdef\ptx@leftbrace<{> -\gdef\ptx@rightbrace<}> -\egroup -\newfor\ptx@bookmark_add{2}#3,{% - \straightenif{ifdim}{#1pt>#3pt } - {\eaddright\ptx@bookmark_list{#2\ptx@leftbrace} - \retrieverest{\ptx@bookmark_addtostack{#1,#3,}}} - {\eaddright\ptx@bookmark_list{\ptx@rightbrace,}} - }[\eaddright\ptx@bookmark_list{#2\ptx@leftbrace} - \ptx@bookmark_addtostack{#1,}{}] -\def\ptx@bookmark_addtostack#1#2{\def\ptx@bookmark_stack{#1#2}} -\newfor\ptx@bookmark_endlist#1,{% - \eaddright\ptx@bookmark_list{\ptx@rightbrace,}% - } -\newfor\ptx@bookmark_process#1#2#3#4,{% - \ptx@bookmark_subcount{0}{#1}{#2}{#3}{#4}% - \reverse\iffemptystring{#4}{\ptx@bookmark_process{#4}}% - } -\newfor\ptx@bookmark_subcount{4}#5,{% - \passarguments{#1+1}{#2}{#3}{#4} - }[\def\ptx@bookmark_sign{-}% -% \def\ptx@bookmark_options{}% -% \ptx@option_launchloop{bookmark}{#4}% - \pdfoutline attr {#4} goto name {#3} count \ptx@bookmark_sign\the\numexpr(#1) {#2}% - ] -\def\processbookmarks{% - \reverse\iffemptycommand\ltr@bookmark_list{% - \expandafter\ptx@bookmark_endlist\expandafter{\ptx@bookmark_stack} - \bgroup - \setcatcodes{_@=11} - \scantokens\expandafter{\expandafter\gdef\expandafter\ptx@bookmark_list\expandafter{\ptx@bookmark_list}}% - \egroup - \expandafter\ptx@bookmark_process\expandafter{\ptx@bookmark_list}} +\newcount\ptx@penalty +\ptx@penalty=-10000 + +\def\newbreakpenalty#1{% + \advance\ptx@penalty-1 + \edef#1{\the\ptx@penalty\relax} } -%\let\ptx@end\end -%\def\end{\processbookmarks\ptx@end} -% -\newcount\ptx@section_link -\let\sectionaction\unbrace -\def\tosection#1#2{% - \reverse\iffcs{ptx@section_dest:#1} - {\global\advance\ptx@section_link1 - \xdefcs{ptx@section_dest:#1}{\the\ptx@section_link}}% - \sectionaction{\pdfstartlink attr {/Border [0 0 0]} goto name {ptx@section_link:\usecs{ptx@section_dest:#1}}#2\pdfendlink}% + +\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}% } -\newcount\ptx@section_skip % A count, since vertical skips are expressed in number of lines. \def\sectioncommand#1#2#3{% \ifnext[ {\ptx@sectioncommand{#1}{#2}{#3}} - {\ptx@sectioncommand{#1}{#2}{#3}[ptx@noop]}% + {\ptx@sectioncommand{#1}{#2}{#3}[]}% } \def\ptx@sectioncommand#1#2#3[#4]{% - \par\removelastskip\penalty0 - \ifvalue #1 : clear = true - {\vfil\penalty-10000}% Clear the page... - {% 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. - \ifdim\dimexpr(\pagegoal-\pagetotal) < \numexpr(\usevalueor #1 : beforeskip 0+\usevalueor #1 : afterskip 0+\usevalueor #1 : minimum 0) \baselineskip - \vfil\penalty-10000 % Not enough room. - \else - \vskip\usevalueor #1 : beforeskip 0 \baselineskip - \fi}% -% -% Sections have an internal count, incremented here. - \storevalueor\ptx@tempsectioncount #1 : internalcount {\def\ptx@tempsectioncount{0}}% - \esetattribute #1 : internalcount = {\the\numexpr(\ptx@tempsectioncount+1)} - \resetbelow{#1}% Resets all internal counts of sections which depend on the current one (i.e. "section" depends on "chapter", etc.). -% -% - \ptx@attribute_set{section}% - \ifvalue #1 : link = true {% - \ifcs{ptx@section_dest:#4} - {\edef\ptx@temp{ptx@section_link:\usecs{ptx@section_dest:#4}}} - {\global\advance\ptx@section_link1 - \edef\ptx@temp{ptx@section_link:\the\ptx@section_link}% - \reverse\iffstring{#4}{ptx@noop}{\xdefcs{ptx@section_dest:#4}{\the\ptx@section_link}}}% - \pdfdest name {\ptx@temp} xyz - \passexpanded{\createbookmark[\usevalue #1 : bookmarkstyle ]{\usevalueor #1 : bookmarklevel 0}{\detokenize{#2}}}{\ptx@temp}% - }{}% - \noindent - \hskip\usevalueor #1 : indent {0pt}% - \bgroup - \ifattribute #1 : color {\pdfcolorstack0 push {\usevalueand #1 : color { rg}{} \usevalueand #1 : color { RG}{}}}{}% - \usevalue #1 : font - \ifvalue #1 : number = none {}{% - \usevalue #1 : beforenumber - \ifcasevalue #1 : number - \val roman \makeroman - \val arabic \unbrace - \endval{\usevalue #1 : internalcount }% - \usevalue #1 : afternumber - }% - #3% - \ifattribute #1 : color {\pdfcolorstack0 pop}{} - \egroup - \ifvalue #1 : inline = true - {} - {\vskip\usevalueor #1:afterskip 0\baselineskip}% - \ptx@attribute_unset{section}% - } -\newwhile\resetbelow1{}{% - \ifattribute #1 : below - {\storevalue\tempbelow #1 : below - \esetattribute\tempbelow : internalcount = 0 - \passvalue{\changewhile} #1 : below - }{\breakwhile{}}% + \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 - minimum = 3 - inline = false - number = arabic - link = true - afternumber = "\kern.3em" + 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 : - meta = metasection +\setparameter chapter section subsection paragraph: + meta = metasection \setparameter chapter : clear = true number = arabic - below = section afterskip = 3 indent = "0pt plus 1fill" beforenumber = "chapitre " @@ -147,25 +183,66 @@ \setparameter section : beforeskip = 2 - below = subsection bookmarklevel = 2 -\setparameter subsection : +\setparameter subsection paragraph: font = \it beforeskip = 1 - beforenumber = "\usevalue section:internalcount ." + +\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{}% -\def\chapter#1{% - \def\chaptertitle{#1}% +\freedef\chapter{% \sectioncommand{chapter}{#1}{#1}% } -\def\section#1{\sectioncommand{section}{#1}{#1}} -\def\subsection#1{\sectioncommand{subsection}{#1}{#1}} +\freedef\section{\sectioncommand{section}{#1}{#1}} +\freedef\subsection{\sectioncommand{subsection}{#1}{#1}} +\freedef\paragraph{\sectioncommand{paragraph}{#1}{#1}} -\newattribute{section} +\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 @@ -177,41 +254,79 @@ % TeX-inserted (e.g. \baselineskip). % This is probably totally insufficient. % -\directlua{% - onelineskip = node.new(11) - onelineskip.kern = tex.baselineskip.width - move_pendingtitles = function (head) - local item, first = node.slide(head), true - while item do - if node.has_attribute(item,\ptx@attribute_number{section}) 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 +\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 - else end - item = nextitem else - if item.id == 0 or item.id == 1 then - item = nil - else - item = item.prev - end + 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{% - \directlua{move_pendingtitles(tex.box[255].list)}% - } \ No newline at end of file + \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/Master/texmf-dist/tex/plain/pitex/verbatim.ptx b/Master/texmf-dist/tex/plain/pitex/verbatim.ptx new file mode 100644 index 00000000000..a4e2a7ed2b3 --- /dev/null +++ b/Master/texmf-dist/tex/plain/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)}% + } -- cgit v1.2.3