From 1d3090326210c6e6f7ec5432799ded25b75bba46 Mon Sep 17 00:00:00 2001
From: Hans Hagen This module implements a couple of cleanup methods. We need these
+in order to meet the This is a prelude to integrated bibliography support. This file just loads
+bibtex files and converts them to xml so that the we access the content
+in a convenient way. Actually handling the data takes place elsewhere. This module implements some methods and creates additional datastructured
from the big character table that we use for all kind of purposes:
We assume that at this point At this point we assume that the big data table is loaded. From this
table we derive a few more. The Instead of using a Setting the lccodes is also done in a loop over the data table. The next method is used when constructing the main table, although nowadays
-we do this in one step. The index can be a string or a number.
Requesting lower and uppercase codes:
--ldx]]-- -function characters.uccode(n) return characters.data[n].uccode or n end -function characters.lccode(n) return characters.data[n].lccode or n end +function characters.uccode(n) return data[n].uccode or n end +function characters.lccode(n) return data[n].lccode or n end function characters.flush(n) - local c = characters.data[n] + local c = data[n] if c and c.contextname then - texsprint(tex.texcatcodes, "\\"..c.contextname) + texsprint(texcatcodes, "\\"..c.contextname) else - texsprint(unicode.utf8.char(n)) + texsprint(utfchar(n)) end end function characters.shape(n) - local shcode = characters.data[n].shcode + local shcode = data[n].shcode if not shcode then return n, nil elseif type(shcode) == "table" then @@ -564,43 +587,29 @@ end function characters.is_of_category(token,category) if type(token) == "string" then - return characters.data[utfbyte(token)].category == category + return data[utfbyte(token)].category == category else - return characters.data[token].category == category + return data[token].category == category end end function characters.i_is_of_category(i,category) -- by index (number) - local cd = characters.data[i] + local cd = data[i] return cd and cd.category == category end function characters.n_is_of_category(n,category) -- by name (string) - local cd = characters.data[utfbyte(n)] + local cd = data[utfbyte(n)] return cd and cd.category == category end ---[[ldx-- -The following code is kind of messy. It is used to generate the right -unicode reference tables.
---ldx]]-- - -function characters.setpdfunicodes() ---~ local tc = tex.ctxcatcodes ---~ for _,v in pairs(characters.data) do ---~ if v.adobename then ---~ texsprint(tc,format("\\pdfglyphtounicode{%s}{%04X}", v.adobename, v.unicodeslot)) ---~ end ---~ end -end - -- xml support characters.active_offset = 0x10000 xml.entities = xml.entities or { } -input.storage.register(false,"xml/entities",xml.entities,"xml.entities") -- this will move to lxml +storage.register("xml/entities",xml.entities,"xml.entities") -- this will move to lxml function characters.remapentity(chr,slot) texsprint(format("{\\catcode%s=13\\xdef%s{\\string%s}}",slot,utfchar(slot),chr)) diff --git a/tex/context/base/char-ini.tex b/tex/context/base/char-ini.tex index ba1ecf15b..b79e44857 100644 --- a/tex/context/base/char-ini.tex +++ b/tex/context/base/char-ini.tex @@ -1,8 +1,8 @@ %D \module %D [ file=char-ini, %D version=2006.08.20, -%D title=\CONTEXT\ Character Macros, -%D subtitle=Character Support (Initialization), +%D title=\CONTEXT\ Character Support, +%D subtitle=Initialization, %D author=Hans Hagen, %D date=\currentdate, %D copyright=PRAGMA] @@ -11,14 +11,12 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Character Support (initialization)} +\writestatus{loading}{ConTeXt Character Support / Initialization} \registerctxluafile{char-def}{1.001} % let's load this one first \registerctxluafile{char-ini}{1.001} \registerctxluafile{char-cmp}{1.001} % maybe we will load this someplace else -\registerctxluafile{char-tok}{1.001} % maybe we will load this someplace else -\registerctxluafile{char-map}{1.001} -\registerctxluafile{char-syn}{1.001} +\registerctxluafile{char-map}{1.001} % maybe we will load this someplace else \unprotect @@ -31,18 +29,15 @@ \def\checkedchar {\relax\ifmmode\expandafter\checkedmathchar\else\expandafter\checkedtextchar\fi} % #1#2 \def\checkedmathchar#1#2{#2} \def\checkedtextchar #1{\iffontchar\font#1 \expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments\fi{\char#1}} -\def\setcclcuc #1 #2 #3 {\global\catcode#1=11 \global\lccode #1=#2 \global\uccode #1=#3 } %D The codes are stored in the format, so we don't need to reinitialize -%D them (unless of course we have adapted the table). +%D them (unless of course we have adapted the table). It is on the agenda +%D to do this with \type {tex.lccode} cum suis once they're available. -\ctxlua{characters.setcodes()} +\def\setcclcuc#1#2#3{\global\catcode#1=11 \global\lccode #1=#2 \global\uccode #1=#3 } +\def\setcclcucself#1{\global\catcode#1=11 \global\lccode #1=#1 \global\uccode #1=#1 } -% obsolete -% -% \startruntimeluacode -% \ctxlua{characters.setpdfunicodes()}% pdftounicode mappings can only be done runtime -% \stopruntimeluacode +\ctxlua{characters.setcodes()} %D There may be a problem with the turkisch patterns. By now it's taken care of in %D ctxtools (thanks to Mojca). There seems to be a bug in the patterns (^^11 refers @@ -51,16 +46,8 @@ % \setcclcuc "201C "201C "201C % \setcclcuc "201D "201D "201D -% definitions - -\startruntimectxluacode - characters.context.rehash() -\stopruntimectxluacode - -% \ctxlua{characters.context.rehash()} - \ctxlua { - characters.context.define( + characters.define( { % letter catcodes \number\texcatcodes, \number\ctxcatcodes, @@ -85,10 +72,3 @@ } \protect \endinput - -% \ctxlua{characters.context.show(123)} -% \ctxlua{characters.context.show(0x7B)} -% \ctxlua{characters.context.show("7B")} - -% \dostepwiserecurse{`A}{`Z}{1} -% {\ctxlua{characters.context.show(\recurselevel)}} diff --git a/tex/context/base/char-map.lua b/tex/context/base/char-map.lua index e463158c5..0d8422bc2 100644 --- a/tex/context/base/char-map.lua +++ b/tex/context/base/char-map.lua @@ -1,15 +1,12 @@ --- filename : char-map.lua --- comment : companion to char-def.tex (in ConTeXt) --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files - --- remark : derived from 'specialcasing.txt', se Arthurs comments in char-map.txt - - -if not versions then versions = { } end versions['char-map'] = 1.001 -if not characters then characters = { } end +if not modules then modules = { } end modules ['char-map'] = { + version = 1.001, + comment = "companion to char-ini.tex", + author = "Hans Hagen & Arthur Reutenauer", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} +characters = characters or { } characters.casemap={ [0x0049]={ diff --git a/tex/context/base/char-syn.lua b/tex/context/base/char-syn.lua deleted file mode 100644 index a779e1a58..000000000 --- a/tex/context/base/char-syn.lua +++ /dev/null @@ -1,140 +0,0 @@ -if not modules then modules = { } end modules ['char-syn'] = { - version = 1.001, - comment = "companion to char-ini.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - --- thanks to tex4ht for these mappings - -characters.synonyms = { - angle = 0x2220, - anticlockwise = 0x21BA, - arrowaxisleft = 0x2190, - arrowaxisright = 0x2192, - arrowparrleftright = 0x21C6, - arrowparrrightleft = 0x21C4, - arrowtailleft = 0x21A2, - arrowtailright = 0x21A3, - arrowtripleleft = 0x21DA, - arrowtripleright = 0x21DB, - axisshort = 0x2212, - because = 0x2235, - between = 0x226C, - check = 0x2713, - circleasteris = 0x229B, - circleequal = 0x2257, - circleminus = 0x229D, - circleR = 0x24C7, - circlering = 0x229A, - circleS = 0x24C8, - clockwise = 0x21BB, - complement = 0x2201, - curlyleft = 0x21AB, - curlyright = 0x21AC, - dblarrowdwn = 0x21CA, - dblarrowheadleft = 0x219E, - dblarrowheadright = 0x21A0, - dblarrowleft = 0x21C7, - dblarrowright = 0x21C9, - dblarrowup = 0x21C8, - defines = 0x225C, - diamond = 0x2662, - diamondsolid = 0x2666, - difference = 0x224F, - dotplus = 0x2214, - downfall = 0x22CE, - equaldotleftright = 0x2252, - equaldotrightleft = 0x2253, - equalorfollows = 0x22DF, - equalorgreater = 0x22DD, - equalorless = 0x22DC, - equalorprecedes = 0x22DE, - equalsdots = 0x2251, - followsorcurly = 0x227D, - followsorequal = 0x227F, - forces = 0x22A9, - forcesbar = 0x22AA, - fork = 0x22D4, - frown = 0x2322, - geomequivalent = 0x224E, - greaterdbleqlless = 0x22Da, - greaterdblequal = 0x2267, - greaterlessequal = 0x22DA, - greaterorapproxeql = 0x227F, - greaterorequalslant= 0x2265, - greaterorless = 0x2277, - greaterorsimilar = 0x2273, - harpoondownleft = 0x21C3, - harpoondownright = 0x21C2, - harpoonleftright = 0x21CC, - harpoonrightleft = 0x21CB, - harpoonupleft = 0x21BF, - harpoonupright = 0x21BE, - intercal = 0x22BA, - intersectiondbl = 0x22D2, - lessdbleqlgreater = 0x22DB, - lessdblequal = 0x2266, - lessequalgreater = 0x22DB, - lessorapproxeql = 0x227E, - lessorequalslant = 0x2264, - lessorgreater = 0x2276, - lessorsimilar = 0x2272, - maltesecross = 0xFFFD, - measuredangle = 0x2221, - muchgreater = 0x22D9, - muchless = 0x22D8, - multimap = 0x22B8, - multiopenleft = 0x22CB, - multiopenright = 0x22CC, - nand = 0x22BC, - orunderscore = 0x22BB, - perpcorrespond = 0x2259, - precedesorcurly = 0x227C, - precedesorequal = 0x227E, - primereverse = 0x2035, - proportional = 0x221D, - revasymptequal = 0x2243, - revsimilar = 0x223D, - rightanglene = 0x231D, - rightanglenw = 0x231C, - rightanglese = 0x231F, - rightanglesw = 0x231E, - ringinequal = 0x2256, - satisfies = 0x22A8, - shiftleft = 0x21B0, - shiftright = 0x21B1, - smile = 0x2323, - sphericalangle = 0x2222, - square = 0x25A1, - squaredot = 0x22A1, - squareimage = 0x228F, - squareminus = 0x229F, - squaremultiply = 0x22A0, - squareoriginal = 0x2290, - squareplus = 0x229E, - squaresmallsolid = 0x25AA, - squaresolid = 0x25A0, - squiggleleftright = 0x21AD, - squiggleright = 0x21DD, - star = 0x22C6, - subsetdbl = 0x22D0, - subsetdblequal = 0x2286, - supersetdbl = 0x22D1, - supersetdblequa = 0x2287, - therefore = 0x2234, - triangle = 0x25B5, - triangledownsld = 0x25BE, - triangleinv = 0x25BF, - triangleleft = 0x25C3, - triangleleftequal = 0x22B4, - triangleleftsld = 0x25C2, - triangleright = 0x25B9, - trianglerightequal = 0x22B5, - trianglerightsld = 0x25B8, - trianglesolid = 0x25B4, - uniondbl = 0x22D3, - uprise = 0x22CF, - Yen = 0x00A5, -} diff --git a/tex/context/base/char-utf.lua b/tex/context/base/char-utf.lua index 273923c36..7dd5d914f 100644 --- a/tex/context/base/char-utf.lua +++ b/tex/context/base/char-utf.lua @@ -1,6 +1,6 @@ if not modules then modules = { } end modules ['char-utf'] = { version = 1.001, - comment = "companion to char-ini.tex", + comment = "companion to char-utf.tex", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" @@ -19,9 +19,11 @@ in special kinds of output (for instanceIt only makes sense to collapse at runtime, since we don't expect -source code to depend on collapsing:
- -We get a more efficient variant of this when we integrate @@ -186,7 +183,7 @@ function utffilters.collapse(str) -- not really tested (we could preallocate a t cf.initialize() end local tokens, first, done, n = { }, false, false, 0 - for second in str:utfcharacters() do + for second in utfcharacters(str) do if done then local crs = cr[second] if crs then @@ -208,7 +205,7 @@ function utffilters.collapse(str) -- not really tested (we could preallocate a t else local crs = cr[second] if crs then - for s in str:utfcharacters() do + for s in utfcharacters(str) do if n == 1 then break else @@ -222,7 +219,7 @@ function utffilters.collapse(str) -- not really tested (we could preallocate a t else local cgf = graphemes[first] if cgf and cgf[second] then - for s in str:utfcharacters() do + for s in utfcharacters(str) do if n == 1 then break else @@ -248,120 +245,29 @@ function utffilters.collapse(str) -- not really tested (we could preallocate a t end --[[ldx-- -
In the beginning of
The following helper functions may disappear (or become optional) -in the future. Well, they are now.
+Next we implement some commands that are used in the user interface.
--ldx]]-- ---[[obsolete-- - -characters.filters.sequences = characters.filters.sequences or { } -characters.filters.activated = false - -function characters.filters.append(name) - table.insert(characters.filters.sequences,name) -end - -function characters.filters.prepend(name) - table.insert(characters.filters.sequences,1,name) -end - -function characters.filters.remove(name) - for k,v in ipairs(characters.filters.sequences) do - if v == name then - table.remove(characters.filters.sequences,k) - end - end -end - -function characters.filters.replace(name_1,name_2) - for k,v in ipairs(characters.filters.sequences) do - if v == name_1 then - characters.filters.sequences[k] = name_2 - break - end - end -end - -function characters.filters.insert_before(name_1,name_2) - for k,v in ipairs(characters.filters.sequences) do - if v == name_1 then - table.insert(characters.filters.sequences,k,name_2) - break - end - end -end +commands = commands or { } -function characters.filters.insert_after(name_1,name_2) - for k,v in ipairs(characters.filters.sequences) do - if v == name_1 then - table.insert(characters.filters.sequences,k+1,name_2) - break - end - end +function commands.uchar(first,second) + tex.sprint(ctxcatcodes,utfchar(first*256+second)) end -function characters.filters.list(separator) - concat(characters.filters.sequences,seperator or ' ') -end - -function characters.filters.process(str) - if characters.filters.activated then - for _,v in ipairs(characters.filters.sequences) do - str = v(str) - end - return str - else - return nil -- luatex callback optimalisation - end -end - ---obsolete]]-- - --[[ldx-- -The following code is no longer needed and replaced by token -collectors somehwere else.
+A few helpers (used to be
The next code is an adaptation of code from Wolfgang Schuster +as posted on the mailing list. This version supports nested +braces and unbraced integers as scripts. We could consider +spaces as terminals for them but first let collect a bunch +of input then.
+]]-- + +-- some lpeg, maybe i'll make an syst-lpg module + +local lowercase = lpeg.R("az") +local uppercase = lpeg.R("AZ") +local backslash = lpeg.P("\\") +local csname = backslash * lpeg.P(1) * (1-backslash)^0 +local plus = lpeg.P("+") / "\\textplus " +local minus = lpeg.P("-") / "\\textminus " +local digit = lpeg.R("09") +local sign = plus + minus +local cardinal = digit^1 +local integer = sign^0 * cardinal + +local leftbrace = lpeg.P("{") +local rightbrace = lpeg.P("}") +local nobrace = 1 - (leftbrace + rightbrace) +local nested = lpeg.P { leftbrace * (csname + sign + nobrace + lpeg.V(1))^0 * rightbrace } +local any = lpeg.P(1) + +local subscript = lpeg.P("_") +local superscript = lpeg.P("^") +local somescript = subscript + superscript + +--~ local content = lpeg.Cs(nested + integer + sign + any) +local content = lpeg.Cs(csname + nested + sign + any) + +-- could be made more efficient + +local lowhigh = lpeg.Cc("\\lohi{%s}{%s}") * subscript * content * superscript * content / format +local highlow = lpeg.Cc("\\hilo{%s}{%s}") * superscript * content * subscript * content / format +local low = lpeg.Cc("\\low{%s}") * subscript * content / format +local high = lpeg.Cc("\\high{%s}") * superscript * content / format +local justtext = (1 - somescript)^1 +local parser = lpeg.Cs((csname + lowhigh + highlow + low + high + sign + any)^0) + +chemicals.moleculeparser = parser -- can be used to avoid functioncall + +function chemicals.molecule(str) + return parser:match(str) +end + +function commands.molecule(str) + if trace_molecules then + local rep = parser:match(str) + logs.report("chemistry", "molecule %s => %s",str,rep) + texsprint(ctxcatcodes,rep) + else + texsprint(ctxcatcodes,parser:match(str)) + end +end diff --git a/tex/context/base/chem-ini.mkiv b/tex/context/base/chem-ini.mkiv new file mode 100644 index 000000000..b28e73e42 --- /dev/null +++ b/tex/context/base/chem-ini.mkiv @@ -0,0 +1,42 @@ +%D \module +%D [ file=chem-ini, +%D version=2008.03.06, +%D subtitle=Chemistry, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=\PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Chemistry Macros / Initialization} % might become Inline + +\registerctxluafile{chem-ini}{1.001} + +\unprotect + +%D \macros +%D {\molecule} +%D +%D Quick and dirty: +%D +%D \starttyping +%D \def\molecule#1{$\enablesupersub\tf#1$} +%D \stoptyping +%D +%D Using \LUA: +%D +%D \startbuffer +%D \molecule{H_2SO_4^-2} +%D \molecule{H_2SO_4^{-2}} +%D \molecule{H_2SO_4^{-2{x}}} +%D \stopbuffer +%D +%D \typebuffer \getbuffer + +\def\molecule#1{\ctxlua{commands.molecule(\!!bs#1\!!es)}} + +\protect \endinput + + diff --git a/tex/context/base/chem-str-test.tex b/tex/context/base/chem-str-test.tex new file mode 100644 index 000000000..fd6a8227a --- /dev/null +++ b/tex/context/base/chem-str-test.tex @@ -0,0 +1,560 @@ +% Beware, integrated ppchtex support is incomplete and under +% construction so when you depend on the full functionality +% you need to use the module! +% +% For testing new functionality: +% +% \startMPextensions +% input "mp-chem.mp" ; +% \stopMPextensions +% \startluacode +% dofile(resolvers.find_file("chem-str.lua","tex")) +% \stopluacode +% \setbox\scratchbox\hbox{\startMPcode\stopMPcode} + +\enabletrackers[chemistry.structure] + +\starttext + +\defineprocessor[ch:r][color=red] +\defineprocessor[ch:g][color=green] +\defineprocessor[ch:b][color=blue] + +\setupchemical[frame=on,offset=3pt] + +\startbuffer[test-set] + + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,MOV1,B] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,MOV2,B] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,MOV3,B] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,MOV4,B] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,MOV5,B] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,MOV6,B] \stopchemical \quad + + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,R,AU] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,R,AD] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,EB] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,DB] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,ER] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,DR] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,BR] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,SB] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,-SB] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,+SB] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,C] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,CC] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,CD] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,CCD] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,SB,SR] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,SB,-SR] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,SB,+SR] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,R] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,RD] \stopchemical \quad + + \dontleavehmode \startchemical \chemical[\ChemicalKind,SB,Z] [a,b,c,d,e,f] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,R,RZ] [a,b,c,d,e,f] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,+R,+RZ] [a,b,c,d,e,f] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,-R,-RZ] [a,b,c,d,e,f] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,RB,RZ] [a,b,c,d,e,f] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,+RB,+RZ][a,b,c,d,e,f] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,-RB,-RZ][a,b,c,d,e,f] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,R,RT] [a,b,c,d,e,f] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,R,RTT] [a,b,c,d,e,f] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,R,RBT] [a,b,c,d,e,f] \stopchemical \quad + + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,R,RN] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,R,RTN] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,R,RBN] \stopchemical \quad + + \dontleavehmode \startchemical \chemical[\ChemicalKind,B,R,RN] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,ROT1,B,R,RN] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,ROT2,B,R,RN] \stopchemical \quad + \dontleavehmode \startchemical \chemical[\ChemicalKind,ROT3,B,R,RN] \stopchemical \quad + +\stopbuffer + +\dontcomplain + +% \startTEXpage + +\setupchemicalframed[frame=on] + +% \startTEXpage +% \noindent \startchemical \chemical[THREE, B,R,RZ][RZ_1,RZ_2,RZ_3]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT1,B,R,RZ][RZ_1,RZ_2,RZ_3]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT2,B,R,RZ][RZ_1,RZ_2,RZ_3]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT3,B,R,RZ][RZ_1,RZ_2,RZ_3]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT4,B,R,RZ][RZ_1,RZ_2,RZ_3]\stopchemical + +% \noindent \startchemical \chemical[THREE, B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT1,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT2,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT3,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT4,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3]\stopchemical + +% \noindent \startchemical \chemical[THREE, B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT1,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT2,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT3,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT4,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3]\stopchemical +% \stopTEXpage + +% \startTEXpage +% \noindent \startchemical \chemical[SIX,ROT1,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical +% \noindent \startchemical \chemical[SIX,ROT2,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical +% \noindent \startchemical \chemical[SIX,ROT3,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical +% \noindent \startchemical \chemical[SIX,ROT4,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical + +% \noindent \startchemical \chemical[SIX,ROT1,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical +% \noindent \startchemical \chemical[SIX,ROT2,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical +% \noindent \startchemical \chemical[SIX,ROT3,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical +% \noindent \startchemical \chemical[SIX,ROT4,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical + +% \noindent \startchemical \chemical[SIX,ROT1,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \noindent \startchemical \chemical[SIX,ROT2,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \noindent \startchemical \chemical[SIX,ROT3,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \noindent \startchemical \chemical[SIX,ROT4,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \stopTEXpage + +% \startTEXpage +% \noindent \startchemical \chemical[FIVE,ROT1,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical +% \noindent \startchemical \chemical[FIVE,ROT2,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical +% \noindent \startchemical \chemical[FIVE,ROT3,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical +% \noindent \startchemical \chemical[FIVE,ROT4,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical + +% \noindent \startchemical \chemical[FIVE,ROT1,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical +% \noindent \startchemical \chemical[FIVE,ROT2,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical +% \noindent \startchemical \chemical[FIVE,ROT3,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical +% \noindent \startchemical \chemical[FIVE,ROT4,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical + +% \noindent \startchemical \chemical[FIVE,ROT1,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \noindent \startchemical \chemical[FIVE,ROT2,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \noindent \startchemical \chemical[FIVE,ROT3,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \noindent \startchemical \chemical[FIVE,ROT4,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \stopTEXpage + +% \startTEXpage +% \noindent \startchemical \chemical[FOUR,ROT1,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical +% \noindent \startchemical \chemical[FOUR,ROT2,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical +% \noindent \startchemical \chemical[FOUR,ROT3,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical +% \noindent \startchemical \chemical[FOUR,ROT4,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical + +% \noindent \startchemical \chemical[FOUR,ROT1,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical +% \noindent \startchemical \chemical[FOUR,ROT2,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical +% \noindent \startchemical \chemical[FOUR,ROT3,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical +% \noindent \startchemical \chemical[FOUR,ROT4,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical + +% \noindent \startchemical \chemical[FOUR,ROT1,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \noindent \startchemical \chemical[FOUR,ROT2,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \noindent \startchemical \chemical[FOUR,ROT3,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \noindent \startchemical \chemical[FOUR,ROT4,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \stopTEXpage + +% \startTEXpage +% \noindent \startchemical \chemical[THREE,ROT1,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT2,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT3,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT4,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6]\stopchemical + +% \noindent \startchemical \chemical[THREE,ROT1,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT2,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT3,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT4,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6]\stopchemical + +% \noindent \startchemical \chemical[THREE,ROT1,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT2,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT3,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \noindent \startchemical \chemical[THREE,ROT4,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6]\stopchemical +% \stopTEXpage + +% \startTEXpage +% \noindent \startchemical \chemical[EIGHT,ROT1,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6,RZ_7,RZ_8]\stopchemical +% \noindent \startchemical \chemical[EIGHT,ROT2,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6,RZ_7,RZ_8]\stopchemical +% \noindent \startchemical \chemical[EIGHT,ROT3,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6,RZ_7,RZ_8]\stopchemical +% \noindent \startchemical \chemical[EIGHT,ROT4,B, R, RZ, AU][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6,RZ_7,RZ_8]\stopchemical + +% \noindent \startchemical \chemical[EIGHT,ROT1,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6,-RZ_7,-RZ_8]\stopchemical +% \noindent \startchemical \chemical[EIGHT,ROT2,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6,-RZ_7,-RZ_8]\stopchemical +% \noindent \startchemical \chemical[EIGHT,ROT3,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6,-RZ_7,-RZ_8]\stopchemical +% \noindent \startchemical \chemical[EIGHT,ROT4,B,-R,-RZ][-RZ_1,-RZ_2,-RZ_3,-RZ_4,-RZ_5,-RZ_6,-RZ_7,-RZ_8]\stopchemical + +% \noindent \startchemical \chemical[EIGHT,ROT1,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6,+RZ_7,+RZ_8]\stopchemical +% \noindent \startchemical \chemical[EIGHT,ROT2,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6,+RZ_7,+RZ_8]\stopchemical +% \noindent \startchemical \chemical[EIGHT,ROT3,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6,+RZ_7,+RZ_8]\stopchemical +% \noindent \startchemical \chemical[EIGHT,ROT4,B,+R,+RZ][+RZ_1,+RZ_2,+RZ_3,+RZ_4,+RZ_5,+RZ_6,+RZ_7,+RZ_8]\stopchemical +% \stopTEXpage + +% \enabletrackers[chemistry.molecules] + +% \startchemicalformula +% \chemical{S} +% \chemical{+} +% \chemical{O_2} +% \chemical{GIVES} +% \chemical{\+{4}{S}} +% \chemical{\+{4}{S}\-{2}{O_2}} +% \chemical{\-{2}{O_2}} +% \stopchemicalformula + +% \startformula +% \chemical{S} +% \chemical{+} +% \chemical{O_2} +% \chemical{GIVES} +% \chemical{\+{4}{S}} +% \chemical{\+{4}{S}\-{2}{O_2}} +% \chemical{\-{2}{O_2}} +% \stopformula + + +\startTEXpage[offset=2cm] + +\startchemical[width=fit,size=small,scale=small,frame=on] + \chemical[SIX,B] +\stopchemical + +% \startchemical[width=fit,size=small,scale=small,frame=on] +% \chemical[ONE,SB258] +% \stopchemical + +% \startchemical[width=fit,size=small,scale=small,frame=on] +% \chemical[ONE,ROT3,SB258] +% \stopchemical + +% \startchemical[width=fit,size=small,scale=small,frame=on] +% \chemical[FIVE,ROT3,SB34,+SB2,-SB5,Z345,DR35,SR4,CRZ35,SUB1,ONE,SB258,Z0,Z28][C,N,C,O,O,CH,COOC_2H_5,COOC_2H_5] +% \stopchemical + +% \startchemical[scale=small,width=8000,height=8000,frame=on] +% \chemical[SIX,SB2356,DB14,Z2346,SR36,RZ36] [C,N,C,C,H,H_2] +% \chemical[PB:Z1,ONE,Z0,DIR8,Z0,SB24,DB7,Z27,PE][C,C,CH_3,O] +% \chemical[PB:Z5,ONE,Z0,DIR6,Z0,SB24,DB7,Z47,PE][C,C,H_3C,O] +% \chemical[SR24,RZ24] [CH_3,H_3C] +% \stopchemical + +% \startchemical[scale=small,width=6000,height=6000,frame=on] +% \chemical[SIX,SB2356,DB14,Z,SR36,RZ36,SR1245,RZ24][C,C,N,C,C,C,H,H_2,CH_3,H_3C] +% \chemical[PB:RZ1,ONE,Z0,SB2,DB7,Z27,PE][C,CH_3,O] +% \chemical[PB:RZ5,ONE,Z0,SB4,DB7,Z47,PE][C,H_3C,O] +% \stopchemical + +% \startchemical[width=fit,size=small,scale=small,frame=on] +% \chemical +% [SIX,B,C,ADJ1,FIVE,ROT3,SB34,+SB2,-SB5,Z345,DR35,SR4,CRZ35,SUB1,ONE,OFF1,SB258,Z0,Z28] +% [C,N,C,O,O,CH,COOC_2H_5,COOC_2H_5] +% \stopchemical + +% \startchemical[width=fit,height=fit,frame=on,scale=small] +% \chemical +% [ONE,SB15,DB7,Z057,3OFF1,MOV1,Z0,3OFF1,MOV1, +% Z017,SB1357,MOV3,Z0,MOV3,SB1357,Z013,3OFF5, +% MOV5,Z0,3OFF5,SB5,Z5] +% [C,H_2N,NH,(CH_2)_3,C,COOH,H,\SL{NH},C,COOH,H, +% (CH_2)_2,HOOC] +% \stopchemical + +% \startchemical[width=fit,height=fit,frame=on,scale=small] +% \chemical +% [ONE,SB15,DB7,Z057,3OFF1,MOV1,Z0,3OFF1,MOV1,Z017,SB1357,MOV3,Z0,MOV3,SB1357,Z013,3OFF5,MOV5,Z0,3OFF5,SB5,Z5] +% [C,H_2N,NH,(CH_2)_3,C,COOH,H,\SL{NH},C,COOH,H,(CH_2)_2,HOOC] +% \stopchemical + +% \startchemical +% \chemical[ONE,Z0,DB,Z][C_0,C_1,C_1,C_3,C_4,C_5,C_6,C_7,C_8] +% \stopchemical + +% \startchemical +% \chemical[ONE,Z0,SB,Z][C_0,C_1,C_1,C_3,C_4,C_5,C_6,C_7,C_8] +% \stopchemical + +% \startchemical +% \chemical[ONE,Z0,DB,CZ][C_0,C_1,C_1,C_3,C_4,C_5,C_6,C_7,C_8] +% \stopchemical + +% \startchemical +% [width=fit,top=2000,bottom=2000, +% scale=small,size=small]% +% \chemical +% [ONE, +% SAVE, +% Z0,SB731,MOV1,Z0,SB1,MOV1,Z0,DB8,CZ8,SB1,Z1, +% RESTORE, +% SAVE, +% SUB4,ONE,Z0,SB3,SB1,MOV1,Z0,SB1,MOV1,Z0,DB8,CZ8,SB1,Z1, +% RESTORE, +% SUB2,ONE,Z0,SB7,SB1,MOV1,Z0,SB1,MOV1,Z0,DB8,CZ8,SB1,Z1] +% [\SR{HC},O,C,O,C_{19}H_{39}, +% \SR{H_{2}C},O,C,O,C_{17}H_{29}, +% \SR{H_{2}C},O,C,O,C_{21}H_{41}] +% \stopchemical + +% \chemical[width=fit,height=fit,frame=on,scale=small] +% [ONE,Z0,MOV7,SB1357,Z017,3OFF5,MOV5,Z0,3OFF5,MOV5,SB15,DB7,Z057,MOV0,MOV3,SB1357,Z013,MOV5,3OFF5,Z0,6OFF5,SB5,Z5] +% [\SL{NH},C,COOH,H,(CH_2)_3,C,H_2H,NH,C,COOH,H,(CH_2)_2,HOOC] +% \stopchemical + +% \chemical[width=fit,height=fit,frame=on,scale=small] +% [ONE,Z0,MOV7,SB1357,Z017,3OFF5,MOV5,Z0,3OFF5,MOV5,SB15,DB7,Z057,MOV0,MOV3,SB1357,Z013,MOV5,3OFF5,Z0,6OFF5,SB5,Z5] +% [\SL{NH},C,COOH,H,(CH_2)_3,C,H_2H,NH,C,COOH,H,(CH_2)_2,HOOC] +% \stopchemical + +% \startchemical[width=fit,top=1500,bottom=3500] +% \chemical[ONE,Z0,DB1,SB3,SB7,Z7,MOV1,Z0,SB3,SB7,Z3,Z7,MOV0,SUB2,SIX,B,R6,C][C,H,C,H,H] +% \chemical[ONE,Z0,DB1,SB3,SB7,Z7,MOV1,Z0,SB3,SB7,Z3,Z7,MOV0,SUB2,SIX,B,R6,C][C,H,C,H,H] +% \bottext{styreen} +% \stopchemical + +% \startchemical +% \chemical[SPACE,PLUS,SPACE] +% \stopchemical +% \startchemical[right=600] +% \chemical[ONE,CZ0][3CH_{3}OH] +% \stopchemical +% \startchemical +% \chemical[SPACE,GIVES,SPACE,SPACE][H^+/H_2O] +% \stopchemical +% \startchemical +% \chemical +% [ONE, +% SAVE, +% Z0,SB7,SB3,SB1,Z1, +% RESTORE, +% SAVE, +% SUB4,ONE,Z0,SB3,SB1,Z1, +% RESTORE, +% SUB2,ONE,Z0,SB7,SB1,Z1] +% [\SR{HC},OH, +% \SR{H_{2}C},OH, +% \SR{H_{2}C},OH] +% \stopchemical +% \startchemical +% \chemical[SPACE,PLUS,SPACE] +% \stopchemical + +% \startchemical +% \chemical +% [ONE, +% SAVE, +% Z0,DB8,CZ8,SB1,SB5,Z5,MOV1,Z0,SB1,Z1, +% RESTORE, +% SAVE, +% SUB4,ONE,Z0,DB8,CZ8,SB1,SB5,Z5,MOV1,Z0,SB1,Z1, +% RESTORE, +% SUB2,ONE,Z0,DB8,CZ8,SB1,SB5,Z5,MOV1,Z0,SB1,Z1] +% [C,O,C_{19}H_{39},O,CH_{3}, +% C,O,C_{17}H_{29},O,CH_{3}, +% C,O,C_{21}H_{41},O,CH_{3}] +% \stopchemical + +% \startchemical[height=4500,bottom=2500] +% \bottext{$\beta$-D-Fructopyranose} +% \chemical[SIX,FRONT,BB,B1236,+SB4,-SB5,Z5,+R12346,+RZ12346,-R12346,-RZ12346][Z_0,+R_1,+R_2,+R_3,+R_4,+R_6,-R_1,-R_2,-R_3,-R_4,-R_6] +% \stopchemical + +% \startchemical[height=4500,bottom=2500] +% \chemical[SIX,FRONT,BB,B] +% \stopchemical + +% \startchemical +% [width=fit,height=fit,frame=on] +% \chemical +% [SIX,DB135,SB246,Z,SR6,RZ6][C,C,N,\SR{HC},N,C,NH_2] +% \chemical +% [SIX,MOV1,DB1,SB23,SS6,Z1..3,SR3,RZ3][N,\SL{CH},N,H] +% \stopchemical + +% \startchemical \chemical[SIX,B,R,RZ1=a] \stopchemical +% \startchemical \chemical[SIX,B,R,RZ1..3=a] \stopchemical +% \startchemical \chemical[SIX,B,R,RZ135=a] \stopchemical +% \startchemical \chemical[SIX,B,R,RZ] [a] \stopchemical +% \startchemical \chemical[SIX,B,R,RZ] [a,b] \stopchemical +% \startchemical \chemical[SIX,B,R,RZ=a] \stopchemical + +% \definechemical[molecule] +% {\chemical +% [ONE,Z0,SB1357, +% SAVE,SUB2,SIX,B,R6,C,RESTORE, +% MOV1,Z0,SB137,MOV1,Z0,SB37,MOV1] +% [C,C,C]} + +% \startchemical[width=fit,height=fit] +% \chemical[molecule,molecule,molecule] +% \stopchemical + +% \definechemical[molecule] +% {\chemical +% [ONE,Z0,SB1357, +% SAVE,SUB2,SIX,B,R6,C,RESTORE, +% MOV1,Z0,SB137,MOV1,Z0,SB37,MOV1]} + +% \startchemical[width=fit,height=fit] +% \chemical[molecule,molecule,molecule][A,B,C,D,E,F,G,H,I] +% \stopchemical + +\stopTEXpage + +% \noindent \startchemical +% \chemical[SIX,B1..3] +% \stopchemical + +% \noindent \startchemical[width=fit,height=fit] % auto5 ipb off5 +% \chemical[SIX,B,C,R,RZ][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6] +% \stopchemical +% \noindent \startchemical[width=fit,height=fit] % auto5 ipb off5 +% \chemical[SIX,ROT1,B,C,R,RZ][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6] +% \stopchemical +% \startchemical[width=fit,height=fit] % auto5 ipb off5 +% \chemical[SIX,ROT2,B,C,R,RZ][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6] +% \stopchemical +% \startchemical[width=fit,height=fit] % auto5 ipb off5 +% \chemical[SIX,ROT3,B,C,R,RZ][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6] +% \stopchemical +% \startchemical[width=fit,height=fit] % auto5 ipb off5 +% \chemical[SIX,ROT4,B,C,R,RZ][RZ_1,RZ_2,RZ_3,RZ_4,RZ_5,RZ_6] +% \stopchemical + +% \startchemical[width=fit,height=fit,axis=on] % auto5 ipb off5 +% \chemical[SIX,B,C,R6,PB:RZ6,ONE,CZ0,OE1,SB5,MOV5,CZ0,OFF5,OE5,PE][CH,CH_2] +% \stopchemical + +% \dontleavehmode \startchemical \chemical[SIX,B,R,RZ][1,2,3,4,5,6,] \stopchemical + +% \start +% \setupchemicalframed[frame=off] +% \dontleavehmode \startchemical[scale=medium,style=slanted,color=red,rulecolor=green,left=2000,right=4000,top=2000,bottom=2000,axis=on] \chemical[SIX,B,R,RZ][1,2,3,4,5,6,] \stopchemical + +% \dontleavehmode +% \startchemical[width=fit,height=fit] +% \chemical[SIX,B][1,2,3,4,5,6] +% \start +% \setupchemical[rulecolor=red] +% \chemical[SIX,R][1,2,3,4,5,6] +% \stop +% \chemical[SIX,RZ][1,2,3,4,5,6] +% \stopchemical +% \stop + +% \stopTEXpage + +% \stoptext + +% \startTEXpage + +% \dontleavehmode \startchemical \chemical[ONE,SB,Z0,Z][0,1,2,3,4,5,6,7,8] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,DB,Z0,Z][0,1,2,3,4,5,6,7,8] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,TB,Z0,Z][0,1,2,3,4,5,6,7,8] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,EP,Z0][0] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,ES,Z0][0] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,ED,Z0][0] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,ET,Z0][0] \stopchemical \quad + + +% \dontleavehmode \startchemical \chemical[ONE,SD,Z0][0] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,LDD,Z0][0] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,RDD,Z0][0] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,HB,Z0][0] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,BB,Z0][0] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,OE,Z0][0] \stopchemical \quad + + +% \dontleavehmode \startchemical \chemical[ONE,SB,Z] [1,2,3,4,5,6,7,8] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,SB,CZ][1,2,3,4,5,6,7,8] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,SB,ZT][a,b,c,d,e,f,g,h] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,SB,ZN][1,2,3,4,5,6,7,8] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,SB,ZBT][1,2,3,4,5,6,7,8] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,SB,ZBN][1,2,3,4,5,6,7,8] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,SB,ZTT][1,2,3,4,5,6,7,8] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,SB,ZTN][1,2,3,4,5,6,7,8] \stopchemical \quad + +% \dontleavehmode \startchemical \chemical[ONE,SB,MOV1,SB] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[ONE,SB,MOV1,SB,MOV3,SB] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[SIX,B,MOV1,B] \stopchemical \quad + + +% \dontleavehmode \startchemical \chemical[ONE,SB,Z0,Z][0,1,2,3,4,5,6] \stopchemical \quad +% \stopTEXpage + + +% \dorecurse{1000}{\dontleavehmode \startchemical \chemical[SIX,B,R,RZ][a,b,c,d,e,f] \stopchemical \quad} + +% \dontleavehmode \startchemical \chemical[SIX,B,R,RT] [a,b,c,d,e,f] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[SIX,B,R,RTT] [a,b,c,d,e,f] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[SIX,B,R,RBT] [a,b,c,d,e,f] \stopchemical \quad + +% \dontleavehmode \startchemical \chemical[SIX,B,R,+R,-R] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[SIX,B1..4] \stopchemical \quad + +% \dontleavehmode \startchemical \chemical[SIX,B,ZN] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[SIX,B,ZT][A,B,C,D,E,F] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[SIX,B,R,AU] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[SIX,B,R,AD] \stopchemical \quad + +% \dontleavehmode \startchemical \chemical[SIX,B,ADJ1,SIX,B] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[SIX,B,ADJ1,FIVE,ROT1,B] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[SIX,B,ADJ1,FOUR,B] \stopchemical \quad +% \dontleavehmode \startchemical \chemical[SIX,B,ADJ1,THREE,B] \stopchemical \quad + +% \definechemical[sixring] +% {\chemical[SIX,B,R]} + +% \startchemical[frame=on,width=6000] +% \chemical[sixring,RZ][A,B,C,D,E,F] +% \stopchemical + +% \definechemical[test] +% {\chemical[SIX,SB,Z][A,B,C,D,E,F]} + +% \startchemical +% \chemical[SIX,SB,Z,ADJ1,test,ADJ1,SIX,SB,Z][a,b,c,d,e,f,g,h,j,k,l,m,P,Q,R,S,T,U,W] +% \chemical[ADJ1,SIX,SB,Z][1,2,3,4,5,6] +% \stopchemical + +% \definechemical[test]{\chemical[SIX,SB,Z]} + +% \startchemical +% \chemical[SIX,SB,Z,ADJ1,test,ADJ1,SIX,SB,Z][a,b,c,d,e,f,g,h,j,k,l,m,P,Q,R,S,T,U,W] +% \chemical[ADJ1,SIX,SB,Z][1,2,3,4,5,6] +% \stopchemical + +% \startchemical +% \chemical[ADJ1,SIX,SB,Z][a_1,a_2,a_3,a_4,a_5,\ominus] +% \stopchemical + +% \startchemical +% \chemical[SIX,SB,Z,SAVE,ADJ1,SIX,SB,Z,ADJ1,SIX,SB,Z,RESTORE,ADJ3,SIX,SB,Z][1,2,3,4,5,6,a,b,c,d,e,f,A,B,C,D,E,F,!,!,!,!,!,!] +% \stopchemical + +% $$ +% \startchemical +% \chemical[OPENCOMPLEX] +% \stopchemical +% \startchemical +% \chemical[SIX,SB,Z][1,2,3,4,5,6] +% \stopchemical +% \startchemical +% \chemical[SPACE,GIVES,SPACE][a,b] +% \stopchemical +% \startchemical +% \chemical[SIX,SB,Z][1,2,3,4,5,6] +% \stopchemical +% \startchemical +% \chemical[CLOSECOMPLEX] +% \stopchemical +% $$ + +% \stoptext + +% \page + +% \def\ChemicalKind{SIX} \getbuffer[test-set] +% \def\ChemicalKind{FIVE} \getbuffer[test-set] +% \def\ChemicalKind{FOUR} \getbuffer[test-set] +% \def\ChemicalKind{THREE} \getbuffer[test-set] + +% \startchemical +% \chemical[SIX,SB,C135,SR,Z0,Z,RZ][X,ch:r->A,ch:g->B,ch:b->C,D,E,F,a,b,c,d,e,f] +% \chemical[MOV1,SIX,SB,C135,SR,Z0,Z,RZ][X,ch:r->A,ch:g->B,ch:b->C,D,E,F,a,b,c,d,e,f] +% \chemical[MOV3,SIX,SB,C135,SR,Z0,Z,RZ][X,ch:r->A,ch:g->B,ch:b->C,D,E,F,a,b,c,d,e,f] +% \stopchemical + +\stoptext diff --git a/tex/context/base/chem-str.lua b/tex/context/base/chem-str.lua new file mode 100644 index 000000000..8ab48fca2 --- /dev/null +++ b/tex/context/base/chem-str.lua @@ -0,0 +1,488 @@ +if not modules then modules = { } end modules ['chem-str'] = { + version = 1.001, + comment = "companion to chem-str.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- This module in incomplete and experimental. + +-- We can push snippets into an mp instance. + +local trace_structure = false trackers.register("chemistry.structure", function(v) trace_structure = v end) +local trace_textstack = false trackers.register("chemistry.textstack", function(v) trace_textstack = v end) + +local format, gmatch, match, lower, gsub = string.format, string.gmatch, string.match, string.lower, string.gsub +local concat, insert, remove = table.concat, table.insert, table.remove +local apply = structure.processors.apply +local texsprint, ctxcatcodes = tex.sprint, tex.ctxcatcodes + +local variables = interfaces.variables + +chemicals = chemicals or { } + +chemicals.instance = "metafun" -- "ppchtex" +chemicals.format = "metafun" +chemicals.structures = 0 + +local remapper = { + ["+"] = "p", + ["-"] = "m", +} + +local common_keys = { + b = "line", eb = "line", db = "line", er = "line", dr = "line", br = "line", + sb = "line", msb = "line", psb = "line", + r = "line", pr = "line", mr = "line", + au = "line", ad = "line", + rb = "line", mrb = "line", prb = "line", + rd = "line", mrd = "line", prd = "line", + sr = "line", msr = "line", psr = "line", + c = "line", cc = "line", cd = "line", ccd = "line", + rn = "number", rtn = "number", rbn = "number", + s = "line", ss = "line", pss = "line", mss = "line", + mid = "fixed", mids = "fixed", midz = "text", + z = "text", rz = "text", mrz = "text", prz = "text", crz = "text", + rt = "text", rtt = "text", rbt = "text", zt = "text", zn = "number", + mov = "transform", rot = "transform", adj = "transform", dir = "transform", sub = "transform", +} + +local front_keys = { + b = "line", bb= "line", + sb = "line", msb = "line", psb = "line", + r = "line", pr = "line", mr = "line", + z = "text", mrz = "text", prz = "text", +} + +local one_keys = { + sb = "line", db = "line", tb = "line", + ep = "line", es = "line", ed = "line", et = "line", + sd = "line", ldd = "line", rdd = "line", + hb = "line", bb = "line", oe = "line", + z = "text", cz = "text", zt = "text", zn = "number", + zbt = "text", zbn = "number", ztt = "text", ztn = "number", + mov = "transform", sub = "transform", dir = "transform", off = "transform", +} + +local front_align = { + mrz = { { "b","b","b","b","b","b" } }, + prz = { { "t","t","t","t","t","t" } }, +} + +local syntax = { + one = { + n = 1, max = 8, keys = one_keys, + align = { + z = { { "r", "r_b", "b", "l_b", "l", "l_t", "t", "r_t" } }, +--~ z = { { "r", "r", "b", "l", "l", "l", "t", "r" } }, + } + }, + three = { + n = 3, max = 3, keys = common_keys, + align = { + mrz = { { "r","b","l" }, { "b","l","t" }, { "l","t","r" }, { "t","r","b" } }, + rz = { { "r","l_b","l_t" }, { "b","l_t","r_t" }, { "l","r_t","r_b" }, { "t","r_b","l_b" } }, + prz = { { "r","l","t" }, { "b","t","r" }, { "l","r","b" }, { "t","b","l" } }, + } + }, + four = { + n = 4, max = 4, keys = common_keys, + align = { + mrz = { { "t","r","b","l" }, { "r","b","l","t" }, { "b","l","t","r" }, { "l","t","r","b" } }, + rz = { { "r_t","r_b","l_b","l_t" }, { "r_b","l_b","l_t","r_t" }, { "l_b","l_t","r_t","r_b" }, { "l_t","r_t","r_b","l_b" } }, + prz = { { "r","b","l","t" }, { "b","l","t","r" }, { "l","t","r","b" }, { "t","r","b","l" } }, + } + }, + five = { + n = 5, max = 5, keys = common_keys, + align = { + mrz = { { "t","r","b","b","l" }, { "r","b","l","l","t" }, { "b","l","t","r","r" }, { "l","t","r","r","b" } }, + rz = { { "r","r","b","l","t" }, { "b","b","l","t","r" }, { "l","l","t","r","b" }, { "t","t","r","b","l" } }, + prz = { { "r","b","l","t","t" }, { "b","l","t","r","r" }, { "l","t","r","b","b" }, { "t","r","b","l","l" } }, + } + }, + six = { + n = 6, max = 6, keys = common_keys, + align = { + mrz = { { "t","t","r","b","b","l" }, { "r","b","b","l","t","t" }, { "b","b","l","t","t","r" }, { "l","t","t","r","b","b" } }, + rz = { { "r","r","b","l","l","t" }, { "b","b","l","t","t","r" }, { "l","l","t","r","r","b" }, { "t","t","r","b","b","l" } }, + prz = { { "r","b","l","l","t","r" }, { "b","l","t","t","r","b" }, { "l","t","r","r","b","l" }, { "t","r","b","b","l","t" } }, + } + }, + eight = { + n = 8, max = 8, keys = common_keys, + align = { -- todo + mrz = { { "t","r","r","b","b","l","l","t" }, { "r","b","b","l","l","t","t","r" }, { "b","l","l","t","t","r","r","b" }, { "l","t","t","r","r","b","b","l" } }, + rz = { { "r","r","b","b","l","l","t","t" }, { "b","b","l","l","t","t","r","r" }, { "l","l","t","t","r","r","b","b" }, { "t","t","r","r","b","b","l","l" } }, + prz = { { "r","b","b","l","l","t","t","r" }, { "b","l","l","t","t","r","r","b" }, { "l","t","t","r","r","b","b","l" }, { "t","r","r","b","b","l","l","t" } }, + } + }, + five_front = { + n = -5, max = 5, keys = front_keys, align = front_align, + }, + six_front = { + n = -6, max = 6, keys = front_keys, align = front_align, + }, + pb = { direct = 'chem_pb ;' }, + pe = { direct = 'chem_pe ;' }, + save = { direct = 'chem_save ;' }, + restore = { direct = 'chem_restore ;' }, + space = { direct = 'chem_symbol("\\chemicalsymbol[space]") ;' }, + plus = { direct = 'chem_symbol("\\chemicalsymbol[plus]") ;' }, + minus = { direct = 'chem_symbol("\\chemicalsymbol[minus]") ;' }, + gives = { direct = 'chem_symbol("\\chemicalsymbol[gives]{%s}{%s}") ;', arguments = 2 }, + equilibrium = { direct = 'chem_symbol("\\chemicalsymbol[equilibrium]{%s}{%s}") ;', arguments = 2 }, + mesomeric = { direct = 'chem_symbol("\\chemicalsymbol[mesomeric]{%s}{%s}") ;', arguments = 2 }, + opencomplex = { direct = 'chem_symbol("\\chemicalsymbol[opencomplex]") ;' }, + closecomplex = { direct = 'chem_symbol("\\chemicalsymbol[closecomplex]") ;' }, +} + +local definitions = { } + +function chemicals.undefine(name) + definitions[name] = nil +end + +function chemicals.define(name,spec,text) + local dn = definitions[name] + if not dn then dn = { } definitions[name] = dn end + dn[#dn+1] = { + spec = aux.settings_to_array(lower(spec)), + text = aux.settings_to_array(text), + } +end + +local metacode, kind, keys, bonds, max, txt, textsize, rot, pstack +local molecule = chemicals.molecule -- or use chemicals.moleculeparser:match(...) + +local function fetch(txt) + local st = stack[txt] + local t = st.text[st.n] +--~ st.n = st.n + 1 + while not t and txt > 1 do + txt = txt - 1 + st = stack[txt] + t = st.text[st.n] +--~ st.n = st.n + 1 + end + if t then + if trace_textstack then + logs.report("chemical", "fetching from stack %s slot %s: %s",txt,st.n,t) + end +st.n = st.n + 1 + end + return txt, t +end + +local digit = lpeg.R("09")/tonumber +local colon = lpeg.P(":") +local equal = lpeg.P("=") +local other = 1 - digit - colon - equal +local remapped = lpeg.S("+-") / remapper +local operation = lpeg.Cs((remapped^0 * other)^1) +local amount = digit +local single = digit +local special = (colon * lpeg.C(other^1)) + lpeg.Cc("") +local range = digit * lpeg.P("..") * digit +local set = lpeg.Ct(digit^2) +local text = (equal * lpeg.C(lpeg.P(1)^0)) + lpeg.Cc(false) +local pattern = + (amount + lpeg.Cc(1)) * + operation * + special * ( + range * lpeg.Cc(false) * text + + lpeg.Cc(false) * lpeg.Cc(false) * set * text + + single * lpeg.Cc(false) * lpeg.Cc(false) * text + + lpeg.Cc(false) * lpeg.Cc(false) * lpeg.Cc(false) * text + ) + +--~ local n, operation, index, upto, set, text = pattern:match("RZ1357") + +--~ print(pattern:match("RZ=x")) 1 RZ false false false x +--~ print(pattern:match("RZ1=x")) 1 RZ 1 false false x +--~ print(pattern:match("RZ1..3=x")) 1 RZ 1 3 false x +--~ print(pattern:match("RZ13=x")) 1 RZ false false table x + +local function process(spec,text,n,rulethickness,rulecolor,offset) + insert(stack,{ spec=spec, text=text, n=n }) + local txt = #stack + for i=1,#spec do + local s = spec[i] + local d = definitions[s] + if d then + for i=1,#d do + local di = d[i] + process(di.spec,di.text,1,rulethickness,rulecolor) + end + else + local rep, operation, special, index, upto, set, text = pattern:match(s) + if operation == "pb" then + insert(pstack,kind) + metacode[#metacode+1] = syntax.pb.direct + if keys[special] == "text" and index then + if keys["c"..special] == "text" then -- can be option: auto ... + metacode[#metacode+1] = format('chem_c%s(%s,%s,"");',special,bonds,index) + else + metacode[#metacode+1] = format('chem_%s(%s,%s,"");',special,bonds,index) + end + end + elseif operation == "save" then + insert(pstack,kind) + metacode[#metacode+1] = syntax.save.direct + elseif operation == "pe" or operation == "restore" then + kind = remove(pstack) + local ss = syntax[kind] + local prev = bonds or 6 + keys, bonds, max, rot = ss.keys, ss.n, ss.max, 1 + metacode[#metacode+1] = syntax[operation].direct + metacode[#metacode+1] = format("chem_set(%s,%s) ;",prev,bonds) + elseif operation == "front" then + if syntax[kind .. "_front"] then + kind = kind .. "_front" + local ss = syntax[kind] + local prev = bonds or 6 + keys, bonds, max, rot = ss.keys, ss.n, ss.max, 1 + metacode[#metacode+1] = format("chem_set(%s,%s) ;",prev,bonds) + end + elseif operation then + local ss = syntax[operation] + if ss then + local ds = ss.direct + if ds then + local sa = ss.arguments + if sa == 1 then + local one ; txt, one = fetch(txt) + metacode[#metacode+1] = format(ds,one or "") + elseif sa ==2 then + local one ; txt, one = fetch(txt) + local two ; txt, two = fetch(txt) + metacode[#metacode+1] = format(ds,one or "",two or "") + else + metacode[#metacode+1] = ds + end + elseif ss.keys then + local prev = bonds or 6 + kind, keys, bonds, max, rot = s, ss.keys, ss.n, ss.max, 1 + metacode[#metacode+1] = format("chem_set(%s,%s) ;",prev,bonds) + end + else + local what = keys[operation] + if what == "line" then + if set then + for i=1,#set do + local si = set[i] + metacode[#metacode+1] = format("chem_%s(%s,%s,%s,%s,%s);",operation,bonds,si,si,rulethickness,rulecolor) + end + elseif upto then + metacode[#metacode+1] = format("chem_%s(%s,%s,%s,%s,%s);",operation,bonds,index,upto,rulethickness,rulecolor) + elseif index then + metacode[#metacode+1] = format("chem_%s(%s,%s,%s,%s,%s);",operation,bonds,index,index,rulethickness,rulecolor) + else + metacode[#metacode+1] = format("chem_%s(%s,%s,%s,%s,%s);",operation,bonds,1,max,rulethickness,rulecolor) + end + elseif what == "number" then + if set then + for i=1,#set do + local si = set[i] + metacode[#metacode+1] = format('chem_%s(%s,%s,"\\dochemicaltext{%s}");',operation,bonds,si,si) + end + elseif upto then + for i=index,upto do + local si = set[i] + metacode[#metacode+1] = format('chem_%s(%s,%s,"\\dochemicaltext{%s}");',operation,bonds,si,si) + end + elseif index then + metacode[#metacode+1] = format('chem_%s(%s,%s,"\\dochemicaltext{%s}");',operation,bonds,index,index) + else + for i=1,max do + metacode[#metacode+1] = format('chem_%s(%s,%s,"\\dochemicaltext{%s}");',operation,bonds,i,i) + end + end + elseif what == "text" then + local align = syntax[kind].align + align = align and align[operation] + align = align and align[rot] + if set then + for i=1,#set do + local si = set[i] + local t = text + if not t then txt, t = fetch(txt) end + if t then + local a = align and align[si] + if a then a = "." .. a else a = "" end + metacode[#metacode+1] = format('chem_%s%s(%s,%s,"\\dochemicaltext{%s}");',operation,a,bonds,si,molecule(apply(t))) + end + end + elseif upto then + for i=index,upto do + local t = text + if not t then txt, t = fetch(txt) end + if t then + local s = align and align[i] + if s then s = "." .. s else s = "" end + metacode[#metacode+1] = format('chem_%s%s(%s,%s,"\\dochemicaltext{%s}");',operation,s,bonds,i,molecule(apply(t))) + end + end + elseif index == 0 then + local t = text + if not t then txt, t = fetch(txt) end + if t then + metacode[#metacode+1] = format('chem_%s_zero("\\dochemicaltext{%s}");',operation,molecule(apply(t))) + end + elseif index then + local t = text + if not t then txt, t = fetch(txt) end + if t then + local s = align and align[index] + if s then s = "." .. s else s = "" end + metacode[#metacode+1] = format('chem_%s%s(%s,%s,"\\dochemicaltext{%s}");',operation,s,bonds,index,molecule(apply(t))) + end + else + for i=1,max do + local t = text + if not t then txt, t = fetch(txt) end + if t then + local s = align and align[i] + if s then s = "." .. s else s = "" end + metacode[#metacode+1] = format('chem_%s%s(%s,%s,"\\dochemicaltext{%s}");',operation,s,bonds,i,molecule(apply(t))) + end + end + end + elseif what == "transform" then + if index then + for r=1,rep do + metacode[#metacode+1] = format('chem_%s(%s,%s);',operation,bonds,index) + end + if operation == "rot" then + rot = index + end + end + elseif what == "fixed" then + metacode[#metacode+1] = format("chem_%s(%s,%s,%s);",operation,bonds,rulethickness,rulecolor) + end + end + end + end + end + remove(stack) +end + +-- the size related values are somewhat special but we want to be +-- compatible +-- +-- maybe we should default to fit +-- +-- rulethickness in points + +function chemicals.start(settings) + chemicals.structures = chemicals.structures + 1 + local textsize, rulethickness, rulecolor = settings.size, settings.rulethickness, settings.rulecolor + local width, height, scale, offset = settings.width or 0, settings.height or 0, settings.scale or "medium", settings.offset or 0 + local l, r, t, b = settings.left or 0, settings.right or 0, settings.top or 0, settings.bottom or 0 + if scale == variables.small then + scale = 500 + elseif scale == variables.medium or scale == 0 then + scale = 625 + elseif scale == variables.big then + scale = 750 + else + scale = tonumber(scale) + if not scale or scale == 0 then + scale = 750 + elseif scale < 500 then + scale = 500 + end + end + if width == variables.fit then + width = true + else + width = tonumber(width) or 0 + if l == 0 then + if r == 0 then + l = (width == 0 and 2000) or width/2 + r = l + elseif width ~= 0 then + l = width - r + end + elseif r == 0 and width ~= 0 then + r = width - l + end + width = false + end + if height == variables.fit then + height = true + else + height = tonumber(height) or 0 + if t == 0 then + if b == 0 then + t = (height == 0 and 2000) or height/2 + b = t + elseif height ~= 0 then + t = height - b + end + elseif b == 0 and height ~= 0 then + b = height - t + end + height = false + end + scale = 0.75 * scale/625 + metacode = { format("chem_start_structure(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) ;", + chemicals.structures, + l/25, r/25, t/25, b/25, scale, + tostring(settings.axis == variables.on), tostring(width), tostring(height), tostring(offset) + ) } + kind, keys, bonds, stack, rot, pstack = "six", { }, 6, { }, 1, { } +end + +function chemicals.stop() + metacode[#metacode+1] = "chem_stop_structure ;" + local mpcode = concat(metacode,"\n") + if trace_structure then + logs.report("chemical", "metapost code:\n%s", mpcode) + end + metapost.graphic(chemicals.instance,chemicals.format,mpcode,"") + metacode = nil +end + +function chemicals.component(spec,text,settings) + rulethickness, rulecolor, offset = settings.rulethickness, settings.rulecolor + local spec = aux.settings_to_array(lower(spec)) + local text = aux.settings_to_array(text) + metacode[#metacode+1] = "chem_start_component ;" + process(spec,text,1,rulethickness,rulecolor) + metacode[#metacode+1] = "chem_stop_component ;" +end + +local inline = { + ["single"] = "\\chemicalsinglebond", ["-"] = "\\chemicalsinglebond", + ["double"] = "\\chemicaldoublebond", ["--"] = "\\chemicaldoublebond", + ["triple"] = "\\chemicaltriplebond", ["---"] = "\\chemicaltriplebond", + ["gives"] = "\\chemicalgives", ["->"] = "\\chemicalgives", + ["equilibrium"] = "\\chemicalequilibrium", ["<->"] = "\\chemicalequilibrium", + ["mesomeric"] = "\\chemicalmesomeric", ["<>"] = "\\chemicalmesomeric", + ["plus"] = "\\chemicalsplus", ["+"] = "\\chemicalsplus", + ["minus"] = "\\chemicalsminus", + ["space"] = "\\chemicalsspace", +} + +-- todo: top / bottom + +function chemicals.inline(spec) + local spec = aux.settings_to_array(spec) + for i=1,#spec do + local s = spec[i] + local inl = inline[lower(s)] + if inl then + texsprint(ctxcatcodes,inl) + else + texsprint(ctxcatcodes,format("\\chemicalinline{%s}",molecule(s))) + end + end +end + +statistics.register("chemical formulas", function() + if chemicals.structures > 0 then + return format("%s chemical structure formulas",chemicals.structures) -- no timing needed, part of metapost + end +end) diff --git a/tex/context/base/chem-str.mkiv b/tex/context/base/chem-str.mkiv new file mode 100644 index 000000000..29c6fe939 --- /dev/null +++ b/tex/context/base/chem-str.mkiv @@ -0,0 +1,526 @@ +%D \module +%D [ file=chem-ini, +%D version=2009.05.13, +%D subtitle=Chemistry, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=\PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D This module in incomplete and experimental. Eventually this code +%D will replace \PPCHTEX. + +\writestatus{loading}{ConTeXt Chemistry Macros / Structure} + +\registerctxluafile{chem-str}{1.001} + +% We have a slightly different interface. This is unchanged: +% +% \startchemical[axis=on] +% \chemical[SIX,ROT2,B,R6,SUB1,FIVE,ROT1,B][1] +% \stopchemical +% +% Here we use chemicalformula instead, so no longer a mix: +% +% \startchemicalformula +% \chemical{H_2}{top}{bottom} +% \chemical{PLUS}{top}{bottom} +% \chemical{O}{top}{bottom} +% \chemical{GIVES}{top}{bottom} +% \chemical{H_2O}{top}{bottom} +% \stopchemicalformula +% +% \startchemicalformula +% \chemical{H_2} +% \chemical{PLUS} +% \chemical{O} +% \chemical{GIVES} +% \chemical{H_2O} +% \stopchemicalformula +% +% The inline variant has only one argument: +% +% \chemical{H_2,PLUS,O,GIVES,H_2O} + +% todo: seven | eight | frontsix | fontfive | carbon | newmans | chair + +\unprotect + +\def\setupchemical + {\dosingleempty\dosetupchemical} + +\def\dosetupchemical + {\getparameters[\??cm]} + +\let\setupchemicals\setupchemical + +\def\setupchemicalframed + {\dosingleempty\dosetupchemicalframed} + +\def\dosetupchemicalframed + {\getparameters[\??cm:\c!frame]} + +\def\chemicalparameter#1{\csname\??cm#1\endcsname} + +\def\definechemical + {\dosingleargument\dodefinechemical} % global + +\def\dodefinechemical[#1]#2% + {\startnointerference + \ctxlua{chemicals.undefine("#1")}% + \def\chemical{\dodoubleempty\dostructurechemical}% + \def\dostructurechemical[##1][##2]{\ctxlua{chemicals.define("#1",\!!bs##1\!!es,\!!bs\detokenize{##2}\!!es)}}% + #2% flush + \stopnointerference} + +\def\definechemicalsymbol + {\dodoubleempty\dodefinechemicalsymbol} + +\def\dodefinechemicalsymbol[#1][#2]% + {\setvalue{\??cm::#1}{#2}} + +\def\chemicalsymbol[#1]% + {\getvalue{\??cm::#1}} + +% size (small medium big) + +\def\dosetchemicaltext + {\dosetfontattribute \??cm\c!style + \dosetcolorattribute\??cm\c!color} + +\def\dochemicaltext#1% + {\dosetchemicaltext\strut#1} % maybe also \setstrut + +\edef\chemicaltoplocation{t} +\edef\chemicalbotlocation{b} + +\def\dochemicaltext#1% in ppchtex we had a more clever alignment + {\dosetchemicaltext\strut#1} % maybe also \setstrut + +\newconditional\indisplaychemical + +\unexpanded\def\startchemical + {\dosingleempty\dostartchemical} + +\setvalue{\??cm:\c!size:\v!small }{\txx} +\setvalue{\??cm:\c!size:\v!medium}{\tx} +\setvalue{\??cm:\c!size:\v!big }{} + +\newtoks \everychemical +\newtoks \everystructurechemical +\newtoks \withchemicalbox +\newbox \chemicalbox +\newconditional\somechemicaltext +\newdimen \chemicalwidth +\newdimen \chemicalheight +\newdimen \chemicaldepth + +\def\dostartchemical[#1]% + {\ifmmode\vcenter\else\vbox\fi + \bgroup + \dontcomplain + \settrue\indisplaychemical + \forgetall + \getparameters[\??cm][#1]% + \the\everystructurechemical + \setbox\chemicalbox\hbox\bgroup + \ctxlua{chemicals.start { + width = "\chemicalparameter\c!width", + height = "\chemicalparameter\c!height", + left = \chemicalparameter\c!left, + right = \chemicalparameter\c!right, + top = \chemicalparameter\c!top, + bottom = \chemicalparameter\c!bottom, + scale = "\chemicalparameter\c!scale", + axis = "\chemicalparameter\c!axis", + offset = "\the\dimexpr.25em\relax", + } }% + \startnointerference} + +\unexpanded\def\stopchemical + {\stopnointerference + \ctxlua{chemicals.stop()}% + \egroup + \chemicalwidth \wd\chemicalbox + \chemicalheight\ht\chemicalbox + \chemicaldepth \dp\chemicalbox + \the\withchemicalbox + \doifelsenothing{\chemicalparameter\c!frame}\handlechemicalframednop\handlechemicalframedyes + \egroup} + +\def\handlechemicalframedyes + {\localframed% + [\??cm:\c!frame]% + [\c!frame=\chemicalparameter\c!frame,\c!align=\v!normal,\c!strut=\v!no]{\vbox{\box\chemicalbox\vss}}} % remove depth + +\def\handlechemicalframednop + {\localframed% + [\??cm:\c!frame]% + [\c!align=\v!normal,\c!strut=\v!no]{\vbox{\box\chemicalbox\vss}}} % remove depth + +\let\startstructurechemical\startchemical +\let\stopstructurechemical \stopchemical + +\unexpanded\def\structurechemical + {\dotripleempty\dostructurechemical} + +\appendtoks + \let\chemical\structurechemical +\to\everystructurechemical + +\def\dostructurechemical + {\ifthirdargument + \expandafter\dostructurechemicalthree + \else + \expandafter\dostructurechemicaltwo + \fi} + +\def\dostructurechemicalthree[#1][#2][#3]% + {\writestatus\m!chemicals{hyperlinked chemicals not yet supported}% todo reference, for the moment ignored + \ctxlua{chemicals.component(\!!bs#2\!!es, \!!bs\detokenize{#3}\!!es, { % maybe also pass first two args this way + rulethickness = "\the\dimexpr\chemicalparameter\c!rulethickness\relax", % todo: scaled points + rulecolor = "\MPcolor{\chemicalparameter\c!rulecolor}" % we can precalculate this for speedup + } ) }% + \ignorespaces} + +\def\dostructurechemicaltwo[#1][#2]% + {\ctxlua{chemicals.component(\!!bs#1\!!es,\!!bs\detokenize{#2}\!!es, { % maybe also pass first two args this way + rulethickness = "\the\dimexpr\chemicalparameter\c!rulethickness\relax", % todo: scaled points + rulecolor = "\MPcolor{\chemicalparameter\c!rulecolor}" % we can precalculate this for speedup + } ) }% + \ignorespaces} + +\appendtoks + \setbox\chemicalbox\hbox{\raise\MPlly\onebasepoint\box\chemicalbox}% + \chemicalwidth \wd\chemicalbox + \chemicalheight\ht\chemicalbox + \chemicaldepth \dp\chemicalbox +\to \withchemicalbox + +% kind of compatible, but text sizes instead of math sizes (i.e. tx is larger than scriptsize) + +\appendtoks + \edef\chemicalbodyfont{\chemicalparameter\c!bodyfont}% + \doifnot\chemicalbodyfont\fontbody{\switchtobodyfont[\chemicalbodyfont]}% \fontbody is not expanded (yet) + \getvalue{\??cm:\c!size:\chemicalparameter\c!size}% +% \to \everystructurechemical +\to \everychemical + +\def\chemicaltoptext#1{\global\settrue\somechemicaltext\gdef\thetoptext{#1}\ignorespaces} +\def\chemicalbottext#1{\global\settrue\somechemicaltext\gdef\thebottext{#1}\ignorespaces} +\def\chemicalmidtext#1{\global\settrue\somechemicaltext\gdef\themidtext{#1}\ignorespaces} + +\appendtoks + \let\toptext\chemicaltoptext \glet\thetoptext\empty + \let\bottext\chemicalbottext \glet\thebottext\empty + \let\midtext\chemicalmidtext \glet\themidtext\empty + \global\setfalse\somechemicaltext +\to \everystructurechemical + +\def\doaddchemicaltexts + {\setbox2\hbox to \chemicalwidth{\strut\hss\hbox{\strut\themidtext}\hss}% + \setbox4\hbox to \chemicalwidth{\strut\hss\hbox{\strut\thetoptext}\hss}% + \setbox6\hbox to \chemicalwidth{\strut\hss\hbox{\strut\thebottext}\hss}% + \setbox\chemicalbox\hbox \bgroup + \box\chemicalbox + \hskip-\chemicalwidth + \raise\chemicalheight\hbox{\lower\ht4\box4}% + \hskip-\chemicalwidth + \lower.5\dimexpr\ht2-\dp2\relax\box2% + \hskip-\chemicalwidth + \lower\chemicaldepth \hbox{\raise\dp6\box6}% + \hss + \egroup} % text on top of chemicals + +\appendtoks + \ifconditional\somechemicaltext + \doaddchemicaltexts + \chemicalwidth \wd\chemicalbox + \chemicalheight\ht\chemicalbox + \chemicaldepth \dp\chemicalbox + \fi +\to \withchemicalbox + +% todo: enspace or emspace + +\definechemicalsymbol[space] [\enspace\quad\enspace] +\definechemicalsymbol[plus] [\enspace+\enspace] +\definechemicalsymbol[minus] [\enspace-\enspace] +\definechemicalsymbol[gives] [\dochemicalarrow\xrightarrow] +\definechemicalsymbol[equilibrium] [\dochemicalarrow\xrightoverleftarrow] +\definechemicalsymbol[mesomeric] [\dochemicalarrow\xleftrightarrow] +\definechemicalsymbol[opencomplex] [\mathematics{\Bigg[}] % not yet ok +\definechemicalsymbol[closecomplex][\mathematics{\Bigg]}] % not yet ok + +\definechemicalsymbol[SPACE] [{\chemicalsymbol[space]}] +\definechemicalsymbol[PLUS] [{\chemicalsymbol[plus]}] +\definechemicalsymbol[MINUS] [{\chemicalsymbol[minus]}] +\definechemicalsymbol[GIVES] [{\chemicalsymbol[gives]}] +\definechemicalsymbol[EQUILIBRIUM] [{\chemicalsymbol[equilibrium]}] +\definechemicalsymbol[MESOMERIC] [{\chemicalsymbol[mesomeric]}] +\definechemicalsymbol[OPENCOMPLEX] [{\chemicalsymbol[opencomplex]}] +\definechemicalsymbol[CLOSECOMPLEX][{\chemicalsymbol[closecomplex]}] + +\def\dochemicalarrow#1#2#3% + {\enspace + \mathematics{#1% + {\strut\hbox \!!spread 2em{\hss\ctxlua{chemicals.inline(\!!bs#2\!!es)}\hss}}% + {\strut\hbox \!!spread 2em{\hss\ctxlua{chemicals.inline(\!!bs#3\!!es)}\hss}}}% + \enspace} + +% special macros (probably needs some more work) + +\def\dochemicaltop#1#2#3#4% + {\begingroup + \setbox0\hbox{\tx\setstrut\strut#3}% + \setbox2\hbox{\setstrut\strut\molecule{#4}}% + \setbox0\hbox{\raise\dimexpr\dp0+\ht2\relax\hbox to \wd2{#1\box0#2}}% + \smashbox0 + \hbox{\box0\box2}% + \endgroup}% + +\def\dochemicalbottom#1#2#3#4% + {\begingroup + \setbox0\hbox{\tx\setstrut\strut#3}% + \setbox2\hbox{\setstrut\strut#4}% + \setbox0\hbox{\lower\dimexpr\dp2+\ht0\relax\hbox to \wd2{#1\box0#2}}% + \smashbox0 + \hbox{\box0\box2}% + \endgroup}% + +\unexpanded\def\chemicalleft#1#2% + {\begingroup + \hbox{\llap{\tx\setstrut\strut#1}\setstrut\strut#2}% + \endgroup}% + +\unexpanded\def\chemicalright#1#2% + {\begingroup + \hbox{\setstrut\strut#2\rlap{\tx\setstrut\strut#1}}% + \endgroup}% + +\unexpanded\def\chemicaltop {\dochemicaltop \hss \hss } +\unexpanded\def\chemicallefttop {\dochemicaltop \relax \hss } +\unexpanded\def\chemicalrighttop {\dochemicaltop \hss \relax} +\unexpanded\def\chemicalbottom {\dochemicalbottom \hss \hss } +\unexpanded\def\chemicalleftbottom {\dochemicalbottom \relax \hss } +\unexpanded\def\chemicalrightbottom {\dochemicalbottom \hss \relax} + +\unexpanded\def\chemicaltopleft #1{\chemicalleft {\chemicalrighttop {#1}{}}} +\unexpanded\def\chemicalbottomleft #1{\chemicalleft {\chemicalrightbottom{#1}{}}} +\unexpanded\def\chemicaltopright #1{\chemicalright{\chemicallefttop {#1}{}}} +\unexpanded\def\chemicalbottomright #1{\chemicalright{\chemicalleftbottom {#1}{}}} + +\unexpanded\def\chemicalcentered #1{\setbox\scratchbox\hbox{C}\hbox to \wd\scratchbox{\setstrut\strut\hss#1\hss}} +\unexpanded\def\chemicalleftcentered #1{\setbox\scratchbox\hbox{C}\hbox to \wd\scratchbox{\setstrut\strut #1\hss}} +\unexpanded\def\chemicalrightcentered#1{\setbox\scratchbox\hbox{C}\hbox to \wd\scratchbox{\setstrut\strut\hss#1}} + +\let\chemicalsmashedmiddle\chemicalcentered +\let\chemicalsmashedleft \chemicalleftcentered +\let\chemicalsmashedright \chemicalrightcentered + +\unexpanded\def\chemicaloxidation#1#2#3% + {\chemicaltop{\txx\ifcase#2\relax0\else#1\uppercase\expandafter{\romannumeral#2}\fi}{#3}} + +\unexpanded\def\chemicaloxidationplus {\dotriplegroupempty\chemicaloxidation{\textplus }} % {} needed! +\unexpanded\def\chemicaloxidationminus{\dotriplegroupempty\chemicaloxidation{\textminus}} % {} needed! +\unexpanded\def\chemicalforeveropen {\dotriplegroupempty\chemicalleft {$\big[$}} % {} needed! +\unexpanded\def\chemicalforeverclose {\dotriplegroupempty\chemicalright {$\big]$}} % {} needed! +\unexpanded\def\chemicaloxidationone {\chemicaloxidation\relax1} +\unexpanded\def\chemicaloxidationtwo {\chemicaloxidation\relax2} +\unexpanded\def\chemicaloxidationthree{\chemicaloxidation\relax3} +\unexpanded\def\chemicaloxidationfour {\chemicaloxidation\relax4} +\unexpanded\def\chemicaloxidationfive {\chemicaloxidation\relax5} +\unexpanded\def\chemicaloxidationsix {\chemicaloxidation\relax6} +\unexpanded\def\chemicaloxidationseven{\chemicaloxidation\relax7} + +\appendtoks + \let \+\chemicaloxidationplus + \let \-\chemicaloxidationminus + \let \[\chemicalforeveropen + \let \]\chemicalforeverclose + \let \1\chemicaloxidationone + \let \2\chemicaloxidationtwo + \let \3\chemicaloxidationthree + \let \4\chemicaloxidationfour + \let \5\chemicaloxidationfive + \let \6\chemicaloxidationsix + \let \7\chemicaloxidationseven + \let \X\chemicaltighttext + \let \T\chemicaltop + \let \B\chemicalbottom + \let \L\chemicalleft + \let\LC\chemicalleftcentered + \let \R\chemicalright + \let\RC\chemicalrightcentered + \let\TL\chemicaltopleft + \let\BL\chemicalbottomleft + \let\TR\chemicaltopright + \let\BR\chemicalbottomright + \let\LT\chemicallefttop + \let\LB\chemicalleftbottom + \let\RT\chemicalrighttop + \let\RB\chemicalrightbottom + \let\SL\chemicalsmashedleft + \let\SM\chemicalsmashedmiddle + \let\SR\chemicalsmashedright +\to \everychemical + +\appendtoks + \the\everychemical +\to \everystructurechemical + +% inline + +\unexpanded\def\chemical + {\ifinformula + \expandafter\displaychemical + \else + \expandafter\inlinechemical + \fi} + +\def\displaychemical + {\dotriplegroupempty\dodisplaychemical} + +\def\dodisplaychemical#1#2#3% todo: + {\the\everychemical \everychemical\emptytoks + \quad + \vcenter\bgroup + \ifthirdargument + \ifsecondargument + \halign{&\hss##\hss\cr#2\cr\molecule{#1}\cr#3\cr}% + \else + \halign{&\hss##\hss\cr\molecule{#1}\cr#2\cr}% + \fi + \else + \hbox{\molecule{#1}}% + \fi + \egroup + \quad} + +\def\inlinechemical#1% + {\dontleavehmode\hbox{\ctxlua{chemicals.inline(\!!bs#1\!!es)}}} + +\def\chemicalbondrule{\hbox{\vrule\!!height.75ex\!!depth-\dimexpr.75ex-\linewidth\relax\!!width1em\relax}} + +\definechemicalsymbol[i:space] [\enspace\quad\enspace] +\definechemicalsymbol[i:plus] [\enspace\mathematics{+}\enspace] +\definechemicalsymbol[i:minus] [\enspace\mathematics{-}\enspace] +\definechemicalsymbol[i:gives] [\enspace\mathematics{\xrightarrow{}{}}\enspace] +\definechemicalsymbol[i:equilibrium] [\enspace\mathematics{\xrightpverleftarrow{}{}}\enspace] +\definechemicalsymbol[i:mesomeric] [\enspace\mathematics{\xleftrightarrow{}{}}\enspace] +\definechemicalsymbol[i:single] [\chemicalbondrule] +\definechemicalsymbol[i:tripple] [\hbox{\lower.5ex\chemicalbondrule\hskip-1em\raise.5ex\chemicalbondrule}] +\definechemicalsymbol[i:double] [\hbox{\chemicalbondrule\hskip-1em\lower.5ex\chemicalbondrule\hskip-1em\raise.5ex\chemicalbondrule}] + +\def\chemicalsinglebond {\chemicalsymbol[i:single]} +\def\chemicaldoublebond {\chemicalsymbol[i:tripple]} +\def\chemicaltriplebond {\chemicalsymbol[i:double]} +\def\chemicalgives {\chemicalsymbol[i:gives]} +\def\chemicalmesomeric {\chemicalsymbol[i:mesomeric]} +\def\chemicalequilibrium{\chemicalsymbol[i:equilibrium]} +\def\chemicalsplus {\chemicalsymbol[i:plus]} +\def\chemicalsminus {\chemicalsymbol[i:minus]} +\def\chemicalsspace {\chemicalsymbol[i:space]} +\def\chemicalinline #1{#1} + +% display + +\newconditional\formulachemicalhastop +\newconditional\formulachemicalhasbot + +\newtoks\formulachemicaltop +\newtoks\formulachemicalmid +\newtoks\formulachemicalbot + +\newif\ifinchemicalformula + +\def\startchemicalformula + {\mathortext\vcenter\vbox\bgroup + \forgetall + \inchemicalformulatrue + \the\everychemical + \everychemical\emptytoks + \formulachemicaltop\emptytoks % not needed + \formulachemicalmid\emptytoks % not needed + \formulachemicalbot\emptytoks % not needed + \let\chemical\formulachemical + \setfalse\formulachemicalhastop + \setfalse\formulachemicalhasbot } + +\def\stopchemicalformula + {\tabskip1em\relax + \nointerlineskip + \ifconditional\formulachemicalhastop + \ifconditional\formulachemicalhasbot + \halign{&\hss##\hss\cr\the\formulachemicaltop\cr\the\formulachemicalmid\cr\the\formulachemicalbot\cr}% + \else + \halign{&\hss##\hss\cr\the\formulachemicaltop\cr\the\formulachemicalmid\cr}% + \fi + \else + \ifconditional\formulachemicalhasbot + \halign{&\hss##\hss\cr\the\formulachemicalmid\cr\the\formulachemicalbot\cr}% + \else + \halign{&\hss##\hss\cr\the\formulachemicalmid\cr}% + \fi + \fi + \egroup} + +\unexpanded\def\formulachemical + {\relax\dotriplegroupempty\doformulachemical} + +\def\doformulachemical#1#2#3% + {\ifthirdargument + \doifelsenothing{#2}\noformulachemicaltop{\doformulachemicaltop{#2}}% + \doifelsenothing{#3}\noformulachemicalbot{\doformulachemicalbot{#3}}% + \else\ifsecondargument + \noformulachemicaltop + \doifelsenothing{#2}\noformulachemicalbot{\doformulachemicalbot{#2}}% + \else + \noformulachemicaltop + \noformulachemicalbot + \fi\fi + \formulachemicalmid\expandafter{\the\formulachemicalmid\dodochemicalformulamid{#1}&}} + +\def\noformulachemicaltop {\formulachemicaltop\expandafter{\the\formulachemicaltop&}} +\def\noformulachemicalbot {\formulachemicalbot\expandafter{\the\formulachemicalbot&}} +\def\doformulachemicaltop#1{\formulachemicaltop\expandafter{\the\formulachemicaltop\dodochemicalformulatop{#1}&}\settrue\formulachemicalhastop} +\def\doformulachemicalbot#1{\formulachemicalbot\expandafter{\the\formulachemicalbot\dodochemicalformulabot{#1}&}\settrue\formulachemicalhasbot} + +\def\dodochemicalformulamid#1% + {\ifcsname\??cm::\detokenize{#1}\endcsname\csname\??cm::\detokenize{#1}\expandafter\endcsname\else\molecule{#1}\fi{}{}} + +\def\dodochemicalformulatop#1{\strut#1} +\def\dodochemicalformulabot#1{\strut#1} + +% gone: state option resolution offset (now frame offset) alternative + +\setupchemicalframed + [\c!align=\v!normal, + \c!strut=\v!no, + \c!offset=\v!overlay, + \c!frame=off] + +\setupchemical + [\c!frame=, + \c!width=0, + \c!height=0, + \c!left=0, + \c!right=0, + \c!top=0, + \c!bottom=0, + \c!bodyfont=\the\bodyfontsize, + \c!scale=\v!medium, + \c!size=\v!medium, + \c!textsize=\v!big, + \c!axis=\v!off, + \c!style=\rm, + \c!location=, + \c!color=, + \c!rulethickness=\linewidth, + \c!rulecolor=, + \c!factor=1] + +\protect \endinput diff --git a/tex/context/base/colo-ext.mkii b/tex/context/base/colo-ext.mkii new file mode 100644 index 000000000..06facd34e --- /dev/null +++ b/tex/context/base/colo-ext.mkii @@ -0,0 +1,57 @@ +%D \module +%D [ file=colo-ext, % mostof thsi code used to be in colo-ini.tex +%D version=1997.04.01, +%D title=\CONTEXT\ Color Macros, +%D subtitle=Extras, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Color Macros / Extras} + +\unprotect + +%D \macros +%D {negatecolorcomponent, negativecolorbox} +%D +%D Sometimes, especially when we deal with typesetting +%D devices, we want to reverse the color scheme. Instead of +%D recalculating all those colors, we use a quick and dirty +%D approach: +%D +%D \starttyping +%D \negativecolorbox0 +%D \stoptyping +%D +%D will negate the colors in box zero. + +\def\negatecolorbox#1% + {\setbox#1\hbox + {\dostartnegative + \localstartcolor[white]\vrule\!!height\ht#1\!!depth\dp#1\!!width\wd#1\localstopcolor + \hskip-\wd#1% + \box#1% + \dostopnegative}} + +%D There are in principle two ways to handle overprint: bound to colors +%D or independent. For the moment we only support independent overprint +%D handling. Here we deal with a per-document setting. + +\setupcolors + [\c!overprint=\v!no] + +\def\starttextoverprint + {\doifelse\@@cloverprint\v!yes + {\let\stoptextoverprint\dostopoverprint\dostartoverprint} + {\let\stoptextoverprint\donothing}} + +\let\stoptextoverprint\donothing + +\appendtoks \starttextoverprint \to \everystarttextproperties +\appendtoks \stoptextoverprint \to \everystoptextproperties + +\protect \endinput diff --git a/tex/context/base/colo-ext.mkiv b/tex/context/base/colo-ext.mkiv new file mode 100644 index 000000000..06facd34e --- /dev/null +++ b/tex/context/base/colo-ext.mkiv @@ -0,0 +1,57 @@ +%D \module +%D [ file=colo-ext, % mostof thsi code used to be in colo-ini.tex +%D version=1997.04.01, +%D title=\CONTEXT\ Color Macros, +%D subtitle=Extras, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Color Macros / Extras} + +\unprotect + +%D \macros +%D {negatecolorcomponent, negativecolorbox} +%D +%D Sometimes, especially when we deal with typesetting +%D devices, we want to reverse the color scheme. Instead of +%D recalculating all those colors, we use a quick and dirty +%D approach: +%D +%D \starttyping +%D \negativecolorbox0 +%D \stoptyping +%D +%D will negate the colors in box zero. + +\def\negatecolorbox#1% + {\setbox#1\hbox + {\dostartnegative + \localstartcolor[white]\vrule\!!height\ht#1\!!depth\dp#1\!!width\wd#1\localstopcolor + \hskip-\wd#1% + \box#1% + \dostopnegative}} + +%D There are in principle two ways to handle overprint: bound to colors +%D or independent. For the moment we only support independent overprint +%D handling. Here we deal with a per-document setting. + +\setupcolors + [\c!overprint=\v!no] + +\def\starttextoverprint + {\doifelse\@@cloverprint\v!yes + {\let\stoptextoverprint\dostopoverprint\dostartoverprint} + {\let\stoptextoverprint\donothing}} + +\let\stoptextoverprint\donothing + +\appendtoks \starttextoverprint \to \everystarttextproperties +\appendtoks \stoptextoverprint \to \everystoptextproperties + +\protect \endinput diff --git a/tex/context/base/colo-ext.tex b/tex/context/base/colo-ext.tex deleted file mode 100644 index 33e87459d..000000000 --- a/tex/context/base/colo-ext.tex +++ /dev/null @@ -1,57 +0,0 @@ -%D \module -%D [ file=colo-ext, % mostof thsi code used to be in colo-ini.tex -%D version=1997.04.01, -%D title=\CONTEXT\ Color Macros, -%D subtitle=Extras, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\writestatus{loading}{Context Color Macros / extras} - -\unprotect - -%D \macros -%D {negatecolorcomponent, negativecolorbox} -%D -%D Sometimes, especially when we deal with typesetting -%D devices, we want to reverse the color scheme. Instead of -%D recalculating all those colors, we use a quick and dirty -%D approach: -%D -%D \starttyping -%D \negativecolorbox0 -%D \stoptyping -%D -%D will negate the colors in box zero. - -\def\negatecolorbox#1% - {\setbox#1\hbox - {\dostartnegative - \localstartcolor[white]\vrule\!!height\ht#1\!!depth\dp#1\!!width\wd#1\localstopcolor - \hskip-\wd#1% - \box#1% - \dostopnegative}} - -%D There are in principle two ways to handle overprint: bound to colors -%D or independent. For the moment we only support independent overprint -%D handling. Here we deal with a per-document setting. - -\setupcolors - [\c!overprint=\v!no] - -\def\starttextoverprint - {\doifelse\@@cloverprint\v!yes - {\let\stoptextoverprint\dostopoverprint\dostartoverprint} - {\let\stoptextoverprint\donothing}} - -\let\stoptextoverprint\donothing - -\appendtoks \starttextoverprint \to \everystarttextproperties -\appendtoks \stoptextoverprint \to \everystoptextproperties - -\protect \endinput diff --git a/tex/context/base/colo-hex.mkii b/tex/context/base/colo-hex.mkii new file mode 100644 index 000000000..dac2e46d0 --- /dev/null +++ b/tex/context/base/colo-hex.mkii @@ -0,0 +1,115 @@ +%D \module +%D [ file=colo-hex, +%D version=2004.06.23, +%D title=\CONTEXT\ Color Macros, +%D subtitle=Hex Colors, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\ifx\dodododefinecolor\undefined \else + \endinput +\fi + +\writestatus{loading}{ConTeXt Color Macros / Hexadecimal} + +% \edef\testcolor{\string#FFC0C0} +% \edef\testcolor{\string#55} +% +% \setupcolors[state=start] +% +% \expanded{\definecolor[thehexcolor][\hexcolorspec\testcolor]} +% +% \checkhexcolor[\testcolor] +% +% \definecolor[thehexcolor][\testcolor] +% +% \starttext +% +% test \color[thehexcolor]{rood} +% test \color[red]{rood} +% test \color[\testcolor]{rood} +% +% \stoptext + +\unprotect + +\newdimen\hexcolorfraction \hexcolorfraction=\dimexpr(1pt/256) + +\chardef\hexcolorprefix=`# + +\def\hexcolorspec #1{\expandafter\dohexcolorspec #1\empty\empty\empty\empty\relax} +\def\hexcolorpattern#1{\expandafter\dohexcolorpattern#1\empty\empty\empty\empty\relax} + +\ifx\dohexstringtonumber\undefined \def\dohexstringtonumber{"} \fi + +\def\hexcolorcomponent#1#2% + {\ifnum\dohexstringtonumber#1#2=\zerocount0\else\ifnum\dohexstringtonumber#1#2=\plusone1\else + \expandafter\withoutpt\the\dimexpr(\dohexstringtonumber#1#2\hexcolorfraction)% + \fi\fi} + +\def\dohexcolorspec#1#2#3#4#5#6#7#8\relax + {\ifx#4\empty + s=\hexcolorcomponent#2#3% + \else + r=\hexcolorcomponent#2#3,g=\hexcolorcomponent#4#5,b=\hexcolorcomponent#6#7% + \fi} + +\def\dohexcolorpattern#1#2#3#4#5#6#7#8\relax + {0\ifx#4\empty + S:\hexcolorcomponent#2#3% + \else + R:\hexcolorcomponent#2#3:\hexcolorcomponent#4#5:\hexcolorcomponent#6#7% + \fi:0:0} + +\def\doifhexcolorelse#1% + {\expandafter\dodoifhexcolorelse#10\od} % 0 is a dirty trick to catch an empty #1 + +\def\dodoifhexcolorelse#1#2\od + {\ifnum`#1=\hexcolorprefix + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\def\docheckhexcolor#1% + {\doifhexcolorelse{#1}{\doifundefined{#1}{\setxvalue{\??cr#1}{\hexcolorpattern{#1}}}}\donothing} + +\def\checkhexcolor[#1]% + {\expanded{\docheckhexcolor{#1}}} + +\def\colorHpattern{\@EA\hexcolorpattern\@EA{\@EA*\@@cl@@h}} % * == dummy placeholder + +\let\dodododefinecolor\dododefinecolor % we will overload this one + +\def\dododefinecolor#1#2#3#4[#5][#6]% + {\doifhexcolorelse{#6} + {\setxvalue{\??cr#5}{\hexcolorpattern{#6}}} + {\dodododefinecolor#1#2#3#4[#5][#6]}} + +%D For Adam Lindsay and his XeTeX special driver: + +% because we intercept the zero condition, the .23pt in 1.23pt will disappear in the +% ifcase zero part branch + +\def\colorhexcomponent#1% + {\ifdim#1\points<.005\points + 00\else\lchexnumbers{\the\dimexpr(255\dimexpr(#1\points)\relax+.5\points)\relax}% + \fi} + +% the faster one + +\newdimen\hex@color@a \hex@color@a=.005pt +\newdimen\hex@color@b \hex@color@b=.5pt +\chardef \hex@color@c =255 + +\def\colorhexcomponent#1% + {\ifdim#1\points<\hex@color@a + 00\else\lchexnumbers{\the\dimexpr(#1\points*\hex@color@c+\hex@color@b)\relax}% + \fi} + +\protect \endinput diff --git a/tex/context/base/colo-hex.mkiv b/tex/context/base/colo-hex.mkiv new file mode 100644 index 000000000..b31321b7e --- /dev/null +++ b/tex/context/base/colo-hex.mkiv @@ -0,0 +1,115 @@ +%D \module +%D [ file=colo-hex, +%D version=2004.06.23, +%D title=\CONTEXT\ Color Macros, +%D subtitle=Hex Colors, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D Not yet supported in \MKIV. + +\endinput + +\writestatus{loading}{ConTeXt Color Macros / Hexadecimal} + +% \edef\testcolor{\string#FFC0C0} +% \edef\testcolor{\string#55} +% +% \setupcolors[state=start] +% +% \expanded{\definecolor[thehexcolor][\hexcolorspec\testcolor]} +% +% \checkhexcolor[\testcolor] +% +% \definecolor[thehexcolor][\testcolor] +% +% \starttext +% +% test \color[thehexcolor]{rood} +% test \color[red]{rood} +% test \color[\testcolor]{rood} +% +% \stoptext + +\unprotect + +\newdimen\hexcolorfraction \hexcolorfraction=\dimexpr1pt/256\relax + +\chardef\hexcolorprefix=`# + +\def\hexcolorspec #1{\expandafter\dohexcolorspec #1\empty\empty\empty\empty\relax} +\def\hexcolorpattern#1{\expandafter\dohexcolorpattern#1\empty\empty\empty\empty\relax} + +\ifx\dohexstringtonumber\undefined \def\dohexstringtonumber{"} \fi + +\def\hexcolorcomponent#1#2% + {\ifnum\dohexstringtonumber#1#2=\zerocount0\else\ifnum\dohexstringtonumber#1#2=\plusone1\else + \expandafter\withoutpt\the\dimexpr(\dohexstringtonumber#1#2\hexcolorfraction)% + \fi\fi} + +\def\dohexcolorspec#1#2#3#4#5#6#7#8\relax + {\ifx#4\empty + s=\hexcolorcomponent#2#3% + \else + r=\hexcolorcomponent#2#3,g=\hexcolorcomponent#4#5,b=\hexcolorcomponent#6#7% + \fi} + +\def\dohexcolorpattern#1#2#3#4#5#6#7#8\relax + {0\ifx#4\empty + S:\hexcolorcomponent#2#3% + \else + R:\hexcolorcomponent#2#3:\hexcolorcomponent#4#5:\hexcolorcomponent#6#7% + \fi:0:0} + +\def\doifhexcolorelse#1% + {\expandafter\dodoifhexcolorelse#10\od} % 0 is a dirty trick to catch an empty #1 + +\def\dodoifhexcolorelse#1#2\od + {\ifnum`#1=\hexcolorprefix + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\def\docheckhexcolor#1% + {\doifhexcolorelse{#1}{\doifundefined{#1}{\setxvalue{\??cr#1}{\hexcolorpattern{#1}}}}\donothing} + +\def\checkhexcolor[#1]% + {\expanded{\docheckhexcolor{#1}}} + +\def\colorHpattern{\@EA\hexcolorpattern\@EA{\@EA*\@@cl@@h}} % * == dummy placeholder + +\let\dodododefinecolor\dododefinecolor % we will overload this one + +\def\dododefinecolor#1#2#3#4[#5][#6]% + {\doifhexcolorelse{#6} + {\setxvalue{\??cr#5}{\hexcolorpattern{#6}}} + {\dodododefinecolor#1#2#3#4[#5][#6]}} + +%D For Adam Lindsay and his XeTeX special driver: + +% because we intercept the zero condition, the .23pt in 1.23pt will disappear in the +% ifcase zero part branch + +\def\colorhexcomponent#1% + {\ifdim#1\points<.005\points + 00\else\lchexnumbers{\the\dimexpr(255\dimexpr(#1\points)\relax+.5\points)\relax}% + \fi} + +% the faster one + +\newdimen\hex@color@a \hex@color@a=.005pt +\newdimen\hex@color@b \hex@color@b=.5pt +\chardef \hex@color@c =255 + +\def\colorhexcomponent#1% + {\ifdim#1\points<\hex@color@a + 00\else\lchexnumbers{\the\dimexpr(#1\points*\hex@color@c+\hex@color@b)\relax}% + \fi} + +\protect \endinput diff --git a/tex/context/base/colo-hex.tex b/tex/context/base/colo-hex.tex index 8d5c3f86f..7d223c131 100644 --- a/tex/context/base/colo-hex.tex +++ b/tex/context/base/colo-hex.tex @@ -1,119 +1,3 @@ -%D \module -%D [ file=colo-hex, -%D version=2004.06.23, -%D title=\CONTEXT\ Color Macros, -%D subtitle=Hex Colors, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. +% this is just a stub -\beginLUATEX - \endinput -\endLUATEX - -\ifx\dodododefinecolor\undefined \else - \endinput -\fi - -\writestatus{loading}{Context Color Macros / hexadecimal} - -% \edef\testcolor{\string#FFC0C0} -% \edef\testcolor{\string#55} -% -% \setupcolors[state=start] -% -% \expanded{\definecolor[thehexcolor][\hexcolorspec\testcolor]} -% -% \checkhexcolor[\testcolor] -% -% \definecolor[thehexcolor][\testcolor] -% -% \starttext -% -% test \color[thehexcolor]{rood} -% test \color[red]{rood} -% test \color[\testcolor]{rood} -% -% \stoptext - -\unprotect - -\newdimen\hexcolorfraction \hexcolorfraction=\dimexpr(1pt/256) - -\chardef\hexcolorprefix=`# - -\def\hexcolorspec #1{\expandafter\dohexcolorspec #1\empty\empty\empty\empty\relax} -\def\hexcolorpattern#1{\expandafter\dohexcolorpattern#1\empty\empty\empty\empty\relax} - -\ifx\dohexstringtonumber\undefined \def\dohexstringtonumber{"} \fi - -\def\hexcolorcomponent#1#2% - {\ifnum\dohexstringtonumber#1#2=\zerocount0\else\ifnum\dohexstringtonumber#1#2=\plusone1\else - \expandafter\withoutpt\the\dimexpr(\dohexstringtonumber#1#2\hexcolorfraction)% - \fi\fi} - -\def\dohexcolorspec#1#2#3#4#5#6#7#8\relax - {\ifx#4\empty - s=\hexcolorcomponent#2#3% - \else - r=\hexcolorcomponent#2#3,g=\hexcolorcomponent#4#5,b=\hexcolorcomponent#6#7% - \fi} - -\def\dohexcolorpattern#1#2#3#4#5#6#7#8\relax - {0\ifx#4\empty - S:\hexcolorcomponent#2#3% - \else - R:\hexcolorcomponent#2#3:\hexcolorcomponent#4#5:\hexcolorcomponent#6#7% - \fi:0:0} - -\def\doifhexcolorelse#1% - {\expandafter\dodoifhexcolorelse#10\od} % 0 is a dirty trick to catch an empty #1 - -\def\dodoifhexcolorelse#1#2\od - {\ifnum`#1=\hexcolorprefix - \expandafter\firstoftwoarguments - \else - \expandafter\secondoftwoarguments - \fi} - -\def\docheckhexcolor#1% - {\doifhexcolorelse{#1}{\doifundefined{#1}{\setxvalue{\??cr#1}{\hexcolorpattern{#1}}}}\donothing} - -\def\checkhexcolor[#1]% - {\expanded{\docheckhexcolor{#1}}} - -\def\colorHpattern{\@EA\hexcolorpattern\@EA{\@EA*\@@cl@@h}} % * == dummy placeholder - -\let\dodododefinecolor\dododefinecolor % we will overload this one - -\def\dododefinecolor#1#2#3#4[#5][#6]% - {\doifhexcolorelse{#6} - {\setxvalue{\??cr#5}{\hexcolorpattern{#6}}} - {\dodododefinecolor#1#2#3#4[#5][#6]}} - -%D For Adam Lindsay and his XeTeX special driver: - -% because we intercept the zero condition, the .23pt in 1.23pt will disappear in the -% ifcase zero part branch - -\def\colorhexcomponent#1% - {\ifdim#1\points<.005\points - 00\else\lchexnumbers{\the\dimexpr(255\dimexpr(#1\points)\relax+.5\points)\relax}% - \fi} - -% the faster one - -\newdimen\hex@color@a \hex@color@a=.005pt -\newdimen\hex@color@b \hex@color@b=.5pt -\chardef \hex@color@c =255 - -\def\colorhexcomponent#1% - {\ifdim#1\points<\hex@color@a - 00\else\lchexnumbers{\the\dimexpr(#1\points*\hex@color@c+\hex@color@b)\relax}% - \fi} - -\protect \endinput +\loadmarkfile{colo-hex} diff --git a/tex/context/base/colo-ini.lua b/tex/context/base/colo-ini.lua index 777c88572..c615aad7f 100644 --- a/tex/context/base/colo-ini.lua +++ b/tex/context/base/colo-ini.lua @@ -18,38 +18,20 @@ if not modules then modules = { } end modules ['colo-ini'] = { -- todo: %s -> %f -backends = backends or { } -backends.pdf = backends.pdf or { } -backend = backends.pdf +local texsprint = tex.sprint +local concat =table.concat +local format, gmatch, gsub, lower = string.format, string.gmatch, string.gsub, string.lower -local texsprint, format, concat = tex.sprint, string.format, table.concat - -local s_template_g = "\\dodoPDFregistergrayspotcolor{%s}{%s}{%s}{%s}{%s}" -- n f d p s (p can go away) -local s_template_r = "\\dodoPDFregisterrgbspotcolor {%s}{%s}{%s}{%s}{%s}{%s}{%s}" -- n f d p r g b -local s_template_c = "\\dodoPDFregistercmykspotcolor{%s}{%s}{%s}{%s}{%s}{%s}{%s}{%s}" -- n f d p c m y k - -function backends.pdf.registergrayspotcolor(n,f,d,p,s) states.collect(s_template_g:format(n,f,d,p,s)) end -function backends.pdf.registerrgbspotcolor (n,f,d,p,r,g,b) states.collect(s_template_r:format(n,f,d,p,r,g,b)) end -function backends.pdf.registercmykspotcolor(n,f,d,p,c,m,y,k) states.collect(s_template_c:format(n,f,d,p,c,m,y,k)) end - -local m_template_g = "\\doPDFregistergrayindexcolor{%s}{%s}{%s}{%s}{%s}" -- n f d p s (p can go away) -local m_template_r = "\\doPDFregisterrgbindexcolor {%s}{%s}{%s}{%s}{%s}{%s}{%s}" -- n f d p r g b -local m_template_c = "\\doPDFregistercmykindexcolor{%s}{%s}{%s}{%s}{%s}{%s}{%s}{%s}" -- n f d p c m y k - -function backends.pdf.registergrayindexcolor(n,f,d,p,s) states.collect(m_template_g:format(n,f,d,p,s)) end -function backends.pdf.registerrgbindexcolor (n,f,d,p,r,g,b) states.collect(m_template_r:format(n,f,d,p,r,g,b)) end -function backends.pdf.registercmykindexcolor(n,f,d,p,c,m,y,k) states.collect(m_template_c:format(n,f,d,p,c,m,y,k)) end +ctx = ctx or { } +ctx.aux = ctx.aux or { } -local s_template_e = "\\doPDFregisterspotcolorname{%s}{%s}" -- name, e +local ctxcatcodes = tex.ctxcatcodes -function backends.pdf.registerspotcolorname(name,e) - if e and e ~= "" then - texsprint(tex.ctxcatcodes,format(s_template_e,name,e)) -- todo in new backend: e:gsub(" ","#20") - end -end +local registrations = backends.registrations -ctx = ctx or { } -ctx.aux = ctx.aux or { } +local a_color = attributes.private('color') +local a_transparency = attributes.private('transparency') +local a_colorspace = attributes.private('colorspace') local a_l_c_template = "\\setevalue{(ca:%s)}{%s}" .. "\\setevalue{(cs:%s)}{\\dosetattribute{color}{%s}}" @@ -59,10 +41,10 @@ local f_l_c_template = "\\setvalue {(ca:%s)}{\\doinheritca{%s}}" .. "\\setvalue {(cs:%s)}{\\doinheritcs{%s}}" local f_g_c_template = "\\setgvalue{(ca:%s)}{\\doinheritca{%s}}" .. "\\setgvalue{(cs:%s)}{\\doinheritcs{%s}}" -local r_l_c_template = "\\letbeundefined{(ca:%s)}" .. - "\\letbeundefined{(cs:%s)}" -local r_g_c_template = "\\global\\letbeundefined{(ca:%s)}" .. - "\\global\\letbeundefined{(cs:%s)}" +local r_l_c_template = "\\localundefine{(ca:%s)}" .. + "\\localundefine{(cs:%s)}" +local r_g_c_template = "\\globalundefine{(ca:%s)}" .. + "\\globalundefine{(cs:%s)}" local a_l_t_template = "\\setevalue{(ta:%s)}{%s}" .. "\\setevalue{(ts:%s)}{\\dosetattribute{transparency}{%s}}" @@ -72,68 +54,68 @@ local f_l_t_template = "\\setvalue {(ta:%s)}{\\doinheritta{%s}}" .. "\\setvalue {(ts:%s)}{\\doinheritts{%s}}" local f_g_t_template = "\\setgvalue{(ta:%s)}{\\doinheritta{%s}}" .. "\\setgvalue{(ts:%s)}{\\doinheritts{%s}}" -local r_l_t_template = "\\letbeundefined{(ta:%s)}" .. - "\\letbeundefined{(ts:%s)}" -local r_g_t_template = "\\global\\letbeundefined{(ta:%s)}" .. - "\\global\\letbeundefined{(ts:%s)}" +local r_l_t_template = "\\localundefine{(ta:%s)}" .. + "\\localundefine{(ts:%s)}" +local r_g_t_template = "\\globalundefine{(ta:%s)}" .. + "\\globalundefine{(ts:%s)}" function ctx.aux.definecolor(name, ca, global) if ca and ca > 0 then if global then - texsprint(tex.ctxcatcodes,a_g_c_template:format(name, ca, name, ca)) + texsprint(ctxcatcodes,format(a_g_c_template, name, ca, name, ca)) else - texsprint(tex.ctxcatcodes,a_l_c_template:format(name, ca, name, ca)) + texsprint(ctxcatcodes,format(a_l_c_template, name, ca, name, ca)) end else if global then - texsprint(tex.ctxcatcodes,r_g_c_template:format(name, name)) + texsprint(ctxcatcodes,format(r_g_c_template, name, name)) else - texsprint(tex.ctxcatcodes,r_l_c_template:format(name, name)) + texsprint(ctxcatcodes,format(r_l_c_template, name, name)) end end end function ctx.aux.inheritcolor(name, ca, global) if ca and ca ~= "" then if global then - texsprint(tex.ctxcatcodes,f_g_c_template:format(name, ca, name, ca)) + texsprint(ctxcatcodes,format(f_g_c_template, name, ca, name, ca)) else - texsprint(tex.ctxcatcodes,f_l_c_template:format(name, ca, name, ca)) + texsprint(ctxcatcodes,format(f_l_c_template, name, ca, name, ca)) end else if global then - texsprint(tex.ctxcatcodes,r_g_c_template:format(name, name)) + texsprint(ctxcatcodes,format(r_g_c_template, name, name)) else - texsprint(tex.ctxcatcodes,r_l_c_template:format(name, name)) + texsprint(ctxcatcodes,format(r_l_c_template, name, name)) end end end function ctx.aux.definetransparent(name, ta, global) if ta and ta > 0 then if global then - texsprint(tex.ctxcatcodes,a_g_t_template:format(name, ta, name, ta)) + texsprint(ctxcatcodes,format(a_g_t_template, name, ta, name, ta)) else - texsprint(tex.ctxcatcodes,a_l_t_template:format(name, ta, name, ta)) + texsprint(ctxcatcodes,format(a_l_t_template, name, ta, name, ta)) end else if global then - texsprint(tex.ctxcatcodes,r_g_t_template:format(name, name)) + texsprint(ctxcatcodes,format(r_g_t_template, name, name)) else - texsprint(tex.ctxcatcodes,r_l_t_template:format(name, name)) + texsprint(ctxcatcodes,format(r_l_t_template, name, name)) end end end function ctx.aux.inherittransparent(name, ta, global) if ta and ta ~= "" then if global then - texsprint(tex.ctxcatcodes,f_g_t_template:format(name, ta, name, ta)) + texsprint(ctxcatcodes,format(f_g_t_template, name, ta, name, ta)) else - texsprint(tex.ctxcatcodes,f_l_t_template:format(name, ta, name, ta)) + texsprint(ctxcatcodes,format(f_l_t_template, name, ta, name, ta)) end else if global then - texsprint(tex.ctxcatcodes,r_g_t_template:format(name, name)) + texsprint(ctxcatcodes,format(r_g_t_template, name, name)) else - texsprint(tex.ctxcatcodes,r_l_t_template:format(name, name)) + texsprint(ctxcatcodes,format(r_l_t_template, name, name)) end end end @@ -173,13 +155,15 @@ local function registerspotcolor(parent,name,parentnumber,e,f,d,p) local kind = colors.default -- else problems with shading etc if kind == 1 then kind = v[1] end if kind == 2 then -- name noffractions names p's r g b - backend.registergrayspotcolor(parent,f,d,p,v[2]) + registrations.grayspotcolor(parent,f,d,p,v[2]) elseif kind == 3 then - backend.registerrgbspotcolor (parent,f,d,p,v[3],v[4],v[5]) + registrations.rgbspotcolor (parent,f,d,p,v[3],v[4],v[5]) elseif kind == 4 then - backend.registercmykspotcolor(parent,f,d,p,v[6],v[7],v[8],v[9]) + registrations.cmykspotcolor(parent,f,d,p,v[6],v[7],v[8],v[9]) + end + if e and e ~= "" then + registrations.spotcolorname(parent,e) end - backends.pdf.registerspotcolorname(parent,e) end registered[parentnumber] = true end @@ -192,11 +176,11 @@ local function registermultitonecolor(parent,name,parentnumber,e,f,d,p) -- same local kind = colors.default -- else problems with shading etc if kind == 1 then kind = v[1] end if kind == 2 then - backend.registergrayindexcolor(parent,f,d,p,v[2]) + registrations.grayindexcolor(parent,f,d,p,v[2]) elseif kind == 3 then - backend.registerrgbindexcolor (parent,f,d,p,v[3],v[4],v[5]) + registrations.rgbindexcolor (parent,f,d,p,v[3],v[4],v[5]) elseif kind == 4 then - backend.registercmykindexcolor(parent,f,d,p,v[6],v[7],v[8],v[9]) + registrations.cmykindexcolor(parent,f,d,p,v[6],v[7],v[8],v[9]) end end registered[parentnumber] = true @@ -227,8 +211,8 @@ function ctx.defineprocesscolor(name,str,global,freeze) -- still inconsistent co ctx.aux.definetransparent(name, 0, global) -- can be sped up end elseif freeze then - local ca = attributes.list[attributes.numbers['color']] [str] - local ta = attributes.list[attributes.numbers['transparency']][str] + local ca = attributes.list[a_color] [str] + local ta = attributes.list[a_transparency][str] if ca then ctx.aux.definecolor(name, ca, global) end @@ -239,8 +223,8 @@ function ctx.defineprocesscolor(name,str,global,freeze) -- still inconsistent co ctx.aux.inheritcolor(name, str, global) ctx.aux.inherittransparent(name, str, global) -- if global and str ~= "" then -- For Peter Rolf who wants access to the numbers in Lua. (Currently only global is supported.) - -- attributes.list[attributes.numbers['color']] [name] = attributes.list[attributes.numbers['color']] [str] or -1 -- reset - -- attributes.list[attributes.numbers['transparency']][name] = attributes.list[attributes.numbers['transparency']][str] or -1 -- reset + -- attributes.list[a_color] [name] = attributes.list[a_color] [str] or attributes.unsetvalue -- reset + -- attributes.list[a_transparency][name] = attributes.list[a_transparency][str] or attributes.unsetvalue -- end end end @@ -250,20 +234,11 @@ function ctx.isblack(ca) -- maybe commands return (cv and cv[2] == 0) or false end --- function ctx.aux.colorattribute(name) --- local al = attributes.list[attributes.numbers['color']] --- return al[name] or 0 --- end --- function ctx.aux.transparencyattribute(name) --- local al = attributes.list[attributes.numbers['transparency']] --- return al[name] or 0 --- end - function ctx.definespotcolor(name,parent,str,global) if parent == "" or parent:find("=") then ctx.registerspotcolor(name, parent) elseif name ~= parent then - local cp = attributes.list[attributes.numbers['color']][parent] + local cp = attributes.list[a_color][parent] if cp then local t = str:split_settings() if t then @@ -284,7 +259,7 @@ function ctx.definespotcolor(name,parent,str,global) end function ctx.registerspotcolor(parent, str) - local cp = attributes.list[attributes.numbers['color']][parent] + local cp = attributes.list[a_color][parent] if cp then local e = "" if str then @@ -297,7 +272,7 @@ end function ctx.definemultitonecolor(name,multispec,colorspec,selfspec) local dd, pp, nn = { }, { }, { } - for k,v in multispec:gmatch("(%a+)=([^%,]*)") do + for k,v in gmatch(multispec,"(%a+)=([^%,]*)") do dd[#dd+1] = k pp[#pp+1] = v nn[#nn+1] = k @@ -307,9 +282,9 @@ function ctx.definemultitonecolor(name,multispec,colorspec,selfspec) local nof = #dd if nof > 0 then dd, pp, nn = concat(dd,','), concat(pp,','), concat(nn,'_') - local parent = (nn:lower()):gsub("[^%d%a%.]+","_") + local parent = gsub(lower(nn),"[^%d%a%.]+","_") ctx.defineprocesscolor(parent,colorspec..","..selfspec,true,true) - local cp = attributes.list[attributes.numbers['color']][parent] + local cp = attributes.list[a_color][parent] if cp then registerspotcolor (parent, name, cp, "", nof, dd, pp) registermultitonecolor(parent, name, cp, "", nof, dd, pp) @@ -362,28 +337,26 @@ end function ctx.formatcolor(ca,separator) local cv = colors.value(ca) if cv then - local model = cv[1] + local c, f, t, model = { }, 13, 13, cv[1] if model == 2 then - return tostring(cv[2]) + f, t = 2, 2 elseif model == 3 then - return concat(cv,separator,3,5) + f, t = 3, 5 elseif model == 4 then - return concat(cv,separator,6,9) - else - return tostring(cv[13]) + f, t = 6, 9 end + for i=f,t do + c[#c+1] = format("%0.3f",cv[i]) + end + return concat(c,separator) else - return tostring(0) + return format("%0.3f",0) end end function ctx.formatgray(ca,separator) local cv = colors.value(ca) - if cv then - return tostring(cv[2]) - else - return tostring(0) - end + return format("%0.3f",(cv and cv[2]) or 0) end function ctx.colorcomponents(ca) @@ -433,7 +406,7 @@ function ctx.pdfcolor(model,ca,default) -- todo: use gray when no color else local n,f,d,p = cv[10],cv[11],cv[12],cv[13] if type(p) == "string" then - p = p:gsub(","," ") -- brr misuse of spot + p = gsub(p,","," ") -- brr misuse of spot end return format("/%s cs /%s CS %s SCN %s scn",n,n,p,p) end @@ -519,23 +492,23 @@ end function ctx.resolvempgraycolor(csa,csb,model,s) local ca = colors.register('color',nil,'gray',s) - texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) - texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) + texsprint(ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) + texsprint(ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) end function ctx.resolvemprgbcolor(csa,csb,model,r,g,b) local ca = colors.register('color',nil,'rgb',r,g,b) - texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) - texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) + texsprint(ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) + texsprint(ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) end function ctx.resolvempcmykcolor(csa,csb,model,c,m,y,k) local ca = colors.register('color',nil,'cmyk',c,m,y,k) - texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) - texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) + texsprint(ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) + texsprint(ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) end function ctx.resolvempspotcolor(csa,csb,model,n,f,d,p) local ca = colors.register('color',nil,'spot',n,f,d,p) - texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) - texsprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) + texsprint(ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca))) + texsprint(ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca))) end -- literals needed to inject code in the mp stream, we cannot use attributes there @@ -544,24 +517,24 @@ end local intransparency = false function ctx.pdfrgbliteral(model,r,g,b) - texsprint(tex.ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'rgb',r,g,b)))) + texsprint(ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'rgb',r,g,b)))) end function ctx.pdfcmykliteral(model,c,m,y,k) - texsprint(tex.ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'cmyk',c,m,y,k)))) + texsprint(ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'cmyk',c,m,y,k)))) end function ctx.pdfgrayliteral(model,s) - texsprint(tex.ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'gray',s)))) + texsprint(ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'gray',s)))) end function ctx.pdfspotliteral(model,n,f,d,p) - texsprint(tex.ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'spot',n,f,d,p)))) -- incorrect + texsprint(ctxcatcodes,format("\\pdfliteral{%s}",ctx.pdfcolor(model,colors.register('color',nil,'spot',n,f,d,p)))) -- incorrect end function ctx.pdftransparencyliteral(a,t) intransparency = true - texsprint(tex.ctxcatcodes,format("\\pdfliteral{/Tr%s gs}",transparencies.register(nil,a,t))) + texsprint(ctxcatcodes,format("\\pdfliteral{/Tr%s gs}",transparencies.register(nil,a,t))) end function ctx.pdffinishtransparency() if intransparency then intransparency = false - texsprint(tex.ctxcatcodes,"\\pdfliteral{/Tr0 gs}") -- we happen to know this -) + texsprint(ctxcatcodes,"\\pdfliteral{/Tr0 gs}") -- we happen to know this -) end end diff --git a/tex/context/base/colo-ini.mkii b/tex/context/base/colo-ini.mkii index 745bb1679..2d2a7bdaa 100644 --- a/tex/context/base/colo-ini.mkii +++ b/tex/context/base/colo-ini.mkii @@ -1,6 +1,6 @@ %D \module %D [ file=colo-ini, -%D version=1997.04.01, +%D version=2007.08.08, %D title=\CONTEXT\ Color Macros, %D subtitle=Initialization, %D author=Hans Hagen, @@ -11,8 +11,872 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. +%D We need to clean this up further but first we hav eto make sure that mkiv +%D code works ok. + +\writestatus{loading}{ConTeXt Color Macros / Initialization} + +%D This module implements color. Since \MKII\ and \MKIV\ use a completely +%D different approach, this module only implements a few generic mechanisms. + \unprotect +\chardef\colorversion=1 % temp, needed for tracing purposes, mkiv transition + +%D We use a couple of local registers. That way we don't have +%D to group when converting colors. By the way, this is not +%D really faster. We can sqeeze half a second runtime for 50K +%D switches on a 1G machine, but the macros will become rather +%D ugly then. To mention one such improvement: no colon +%D after the key character (.25 sec). + +\newdimen\colordimen +\newcount\colorcount + +%D When typesetting for paper, we prefer using the \cap{CMYK} +%D color space, but for on||screen viewing we prefer \cap{RGB} +%D (the previous implementation supported only this scheme). +%D Independant of such specifications, we support some automatic +%D conversions: +%D +%D \startitemize[packed] +%D \item convert all colors to \cap{RGB} +%D \item convert all colors to \cap{CMYK} +%D \item convert all colors to gray scales +%D \stopitemize +%D +%D We also support optimization of colors to gray scales. +%D +%D \startitemize[continue] +%D \item reduce gray colors to gray scales +%D \item reduce \cap{CMY} components to \cap{K} +%D \stopitemize +%D +%D These options are communicated by means of: + +\newif\ifRGBsupported +\newif\ifCMYKsupported +\newif\ifSPOTsupported +\newif\ifpreferGRAY +\newif\ifGRAYprefered +\newif\ifreduceCMYK +\newif\ifconverttoGRAY +\newif\ifweightGRAY \weightGRAYtrue + +\newif\ifconvertMPcolors +\newif\ifreduceMPcolors +\newif\ifforcegrayMPcolors + +%D The last boolean controls reduction of \cap{CMYK} to +%D \cap{CMY} colors. When set to true, the black component +%D is added to the other ones. +%D +%D Prefering gray is not the same as converting to gray. +%D Conversion treats each color components in a different way, +%D while prefering is just a reduction and thus a +%D space||saving option. + +\newif\iffreezecolors \freezecolorsfalse +\newif\ifincolor % true if colors enabled +\newif\iflocalcolor + +\let\colorlist \empty +\let\currentspotcolor \empty +\let\allspotcolors \empty +\let\usedspotcolors \empty +\let\usedcolorchannels\empty +\let\currentpalet \empty + +%D \macros +%D {definecolor,defineglobalcolor,definenamedcolor,definespotcolor,definemultitonecolor} +%D +%D \startbuffer +%D \definecolor [blue] [c=1,m=.38,y=0,k=.64] % pantone pms 2965 uncoated m +%D \definecolor [yellow] [c=0,m=.28,y=1,k=.06] % pantone pms 124 uncoated m +%D +%D \definespotcolor [blue-100] [blue] [p=1] +%D \definespotcolor [yellow-100] [yellow] [p=1] +%D +%D \definemultitonecolor [pdftoolscolor] [blue=.12,yellow=.28] [c=.1,m=.1,y=.3,k=.1] +%D +%D \useexternalfigure[demofig][mill.png][object=no] +%D +%D \startcombination[4*1] +%D {\externalfigure[demofig]} {no color} +%D {\externalfigure[demofig][color=pdftoolscolor]} {indexed duotone} +%D {\externalfigure[demofig][color=blue-100]} {spot color} +%D {\externalfigure[demofig][color=yellow-100]} {spot color} +%D \stopcombination +%D \stopbuffer +%D +%D \getbuffer \typebuffer + +\def\definecolor {\dodoubleargument\dodefinecolor} +\def\defineglobalcolor {\dodoubleargument\dodefineglobalcolor} +\def\definenamedcolor {\dodoubleargument\dodefinenamedcolor} +\def\definespotcolor {\dotripleargument\dodefinespotcolor} +\def\definemultitonecolor{\doquadrupleempty\dodefinemultitonecolor} + +% check: registerusedspotcolors +% check: registerusedcolorchannels + +%D \macros +%D {doifcolorelse, doifcolor} +%D +%D Switching to a color is done by means of the following +%D command. Later on we will explain the use of palets. We +%D define ourselves a color conditional first. + +\ifx\doifcolorelse\undefined + \let\doifcolorelse\secondoftwoarguments + \let\doifcolor \gobbleoneargument +\fi + +%D \macros +%D {localstartcolor,localstopcolor} +%D +%D Simple color support, that is without nesting, is provided +%D by: + +\ifx\localstartcolor\undefined + \let\localstartcolor\undefined + \let\localstopcolor \undefined +\fi + +%D \macros +%D {faststartcolor,faststopcolor} +%D +%D No checking for arguments and such: + +\ifx\faststartcolor\undefined + \def\faststartcolor[#1]{} + \def\faststopcolor {} +\fi + +%D These local ones may go away in future versions. + +%D \macros +%D {startcolor,stopcolor} +%D +%D The more save method, the one that saves the current color +%D state and returns to this state afterward, is activated by: +%D +%D \showsetup{startcolor} + +\ifx\startcolor\undefined + \let\startcolor\undefined + \let\stopcolor \undefined +\fi + +%D \macros +%D {startcurrentcolor,stopcurrentcolor} + +\def\startcurrentcolor{\startcolor[\outercolorname]} +\def\stopcurrentcolor {\stopcolor} + +%D \macros +%D {color,graycolor} +%D +%D This leaves the simple color command: +%D +%D \showsetup{color} +%D \showsetup{graycolor} + +\ifx\color\undefined + \def\color [#1]{} + \def\graycolor[#1]{} + \def\gray {\graycolor} +\fi + +%D \macros +%D {localstartraster,localstopraster, +%D startraster,stopraster,raster} +%D +%D The previous conversions are not linear and treat each color +%D component according to human perception curves. Pure gray +%D (we call them rasters) has equal color components. In +%D \CONTEXT\ rasters are only used as backgrounds and these +%D don't cross page boundaries in the way color does. Therefore +%D we don't need stacks and marks. Just to be compatible with +%D color support we offer both 'global' and 'local' commands. + +\ifx\startraster\undefined + \def\startraster [#1]{} + \def\stopraster {} + \def\raster [#1]{} + \def\localstartraster[#1]{} + \def\localstopraster {} +\fi + +%D \macros +%D {colorvalue, grayvalue} +%D +%D We can typeset the color components using \type{\colorvalue} and +%D \type{\grayvalue}. The commands: +%D +%D \startbuffer +%D color value of SomeKindOfRed: \colorvalue{SomeKindOfRed} \crlf +%D gray value of SomeKindOfRed: \grayvalue{SomeKindOfRed} +%D \stopbuffer +%D +%D \typebuffer +%D +%D show us: +%D +%D \startvoorbeeld +%D \getbuffer +%D \stopvoorbeeld + +\def\colorformatseparator{ } + +\ifx\colorvalue\undefined + \let\colorvalue\gobbleoneargument + \let\grayvalue \gobbleoneargument +\fi + +% check: \currentcolorname +% check: \outercolorname + +%D \macros +%D {setupcolor} +%D +%D Color definitions can be grouped in files with the name: +%D +%D \starttyping +%D \f!colorprefix-identifier.tex +%D \stoptyping +%D +%D where \type{\f!colorprefix} is \unprotect {\tttf \f!colorprefix}. +%D Loading such a file is done by \protect +%D +%D \showsetup{setupcolor} +%D +%D Some default colors are specified in \type{colo-rgb.tex}, +%D which is loaded into the format by: +%D +%D \starttyping +%D \setupcolor[rgb] +%D \stoptyping + +\let\colorstyle\empty + +\def\setupcolor + {\dosingleargument\dosetupcolor} + +\def\dosetupcolor[#1]% + {\doifnot{#1}\colorstyle + {\def\colorstyle{#1}% + \processcommalist[#1]\dodosetupcolor}} + +\def\dodosetupcolor#1% + {\makeshortfilename[\truefilename{\f!colorprefix#1}]% + \startreadingfile + \readsysfile\shortfilename + {\showmessage\m!colors4\colorstyle} + {\showmessage\m!colors5\colorstyle}% + \stopreadingfile} + +\let\usecolors\setupcolor + +% check: \chardef\currentcolorchannel=0 +% check: \startcolormode +% check: \newif\iffilterspotcolor \filterspotcolorfalse +% check: \newif\ifdoingspotcolor \doingspotcolorfalse +% check: \registercolorchannel + +%D \macros +%D {definetransparency} +%D +%D This command numbers to names: + +\def\definetransparency + {\dodoubleargument\dodefinetransparency} + +\def\setupcolors + {\dosingleargument\dosetupcolors} + +\def\resetcolorsplitting + {\chardef\currentcolorchannel\zerocount + \let\currentspotcolor\empty + \filterspotcolorfalse} + +\def\colorsplitsuffix{\ifcase\currentcolorchannel\else-\@@clsplit\fi} +\def\colorsplitprefix{\ifcase\currentcolorchannel\else\@@clsplit-\fi} + +\def\setcolorsplitting + {\resetsystemmode{\v!color\colorsplitsuffix}% + \resetcolorsplitting + \processaction + [\@@clsplit] + [ c=>\chardef\currentcolorchannel1,% + m=>\chardef\currentcolorchannel2,% + y=>\chardef\currentcolorchannel3,% + k=>\chardef\currentcolorchannel4,% + r=>\chardef\currentcolorchannel5,% + g=>\chardef\currentcolorchannel6,% + b=>\chardef\currentcolorchannel7,% + s=>\chardef\currentcolorchannel8,% + \v!no=>,% \currentcolorchannel0,% all colors + \s!default=>,% \currentcolorchannel0,% all colors + \s!unknown=>\filterspotcolortrue + \edef\currentspotcolor{\commalistelement}]% + \setsystemmode{\v!color\colorsplitsuffix}% + \iffilterspotcolor \let\@@clrgb\v!no \fi} + +\ifx\dosetupcolormodel\undefined + \let\dosetupcolormodel\relax +\fi + +\def\dosetupcolors[#1]% some no longer make sense in MkIV + {\getparameters[\??cl][#1]% + \doifelse\@@clspot\v!yes + \SPOTsupportedtrue + \SPOTsupportedfalse + \doifelsenothing\@@clsplit + \resetcolorsplitting + \setcolorsplitting + \doifelse\@@clreduction\v!yes + \reduceCMYKtrue + \reduceCMYKfalse + \doifelse\@@clexpansion\v!yes + \freezecolorstrue + \freezecolorsfalse + \doifelse\@@clcriterium\v!all + \hidesplitcolortrue + \hidesplitcolorfalse + \doifelse\@@clrgb\v!no + {\ifRGBsupported \ifproductionrun\showmessage\m!colors {9}\v!rgb \fi\RGBsupportedfalse \fi} + {\ifRGBsupported \else\ifproductionrun\showmessage\m!colors{10}\v!rgb \fi\RGBsupportedtrue \fi}% + \doifelse\@@clcmyk\v!no + {\ifCMYKsupported \ifproductionrun\showmessage\m!colors {9}\v!cmyk \fi\CMYKsupportedfalse\fi} + {\ifCMYKsupported\else\ifproductionrun\showmessage\m!colors{10}\v!cmyk \fi\CMYKsupportedtrue \fi}% + \doifelse\@@clmpcmyk\v!no + {\ifMPcmykcolors \ifproductionrun\showmessage\m!colors {9}{\v!mp\v!cmyk}\fi\MPcmykcolorsfalse \fi} + {\ifMPcmykcolors \else\ifproductionrun\showmessage\m!colors{10}{\v!mp\v!cmyk}\fi\MPcmykcolorstrue \fi}% + \doifelse\@@clmpspot\v!no + {\ifMPspotcolors \ifproductionrun\showmessage\m!colors {9}{\v!mp\v!spot}\fi\MPspotcolorsfalse \fi} + {\ifMPspotcolors \else\ifproductionrun\showmessage\m!colors{10}{\v!mp\v!spot}\fi\MPspotcolorstrue \fi}% + \preferGRAYfalse + \processaction + [\@@clconversion] + [ \v!yes=>\preferGRAYtrue, + \v!always=>\preferGRAYtrue\RGBsupportedfalse\CMYKsupportedfalse]% + \ifRGBsupported + \converttoGRAYfalse + \forcegrayMPcolorsfalse + \else\ifCMYKsupported + \converttoGRAYfalse + \forcegrayMPcolorsfalse + \convertMPcolorstrue + \ifreduceCMYK + \reduceMPcolorstrue + \fi + \else + \ifconverttoGRAY\else\showmessage\m!colors{11}\empty\fi + \converttoGRAYtrue + \forcegrayMPcolorstrue + \convertMPcolorsfalse + \reduceMPcolorsfalse + \fi\fi + \processaction + [\@@clstate] + [ \v!global=>\ifincolor\else\showmessage\m!colors1\colorstyle\fi + \incolortrue\localcolorfalse, + \v!local=>\ifincolor\else\showmessage\m!colors2\colorstyle\fi + \incolortrue\localcolortrue, + \v!start=>\ifincolor\else\showmessage\m!colors1\colorstyle\fi + \incolortrue\localcolorfalse + \let\@@clstate\v!global, + \v!stop=>\incolorfalse\localcolorfalse + \forcegrayMPcolorstrue]% + \dosetupcolormodel + \initializemaintextcolor} + +%D \macros +%D {startregistercolor,stopregistercolor,permitcolormode} +%D +%D If you only want to register a color, the switch \type +%D {\ifpermitcolormode} can be used. That way the nested +%D colors know where to go back to. + +\ifx\startregistercolor\undefined + \def\startregistercolor[#1]{} + \def\stopregistercolor {} +\fi + +%D We use these macros for implementing text colors +%D (actually, the first application was in foreground +%D colors). +%D +%D \starttyping +%D \starttextcolor[red] +%D \dorecurse{10}{\input tufte \color[green]{oeps} \par} +%D \stoptextcolor +%D \stoptyping +%D +%D This is more efficient than the alternative: +%D +%D \starttyping +%D \setupbackgrounds[text][foregroundcolor=red] +%D \startregistercolor[red] +%D \dorecurse{10}{\input tufte \color[green]{oeps} \par} +%D \stopregistercolor +%D \stoptyping + +\def\maintextcolor {} +\def\defaulttextcolor {black} +\def\@@themaintextcolor{themaintextcolor} + +\ifx\initializemaintextcolor\undefined + \def\starttextcolor [#1]{} + \def\stoptextcolor {} + \def\initializemaintextcolor {} +\fi + +\ifx\restoretextcolor\undefined % to be redone + \let\restoretextcolor \firstofoneargument + \let\localstarttextcolor\relax + \let\localstoptextcolor \relax +\fi + +%D In this documentation we will not go into too much details +%D on palets. Curious users can find more information on this +%D topic in \from[use of color]. +%D +%D At the moment we implemented color in \CONTEXT\ color +%D printing was not yet on the desktop. In spite of this lack our +%D graphics designer made colorfull illustrations. When printed +%D on a black and white printer, distinctive colors can come +%D out equally gray. We therefore decided to use only colors +%D that were distinctive in colors as well as in black and +%D white print. +%D +%D Although none of the graphic packages we used supported +%D logical colors and global color redefition, we build this +%D support into \CONTEXT. This enabled us to experiment and +%D also prepared us for the future. + +%D \macros +%D {definepalet} +%D +%D Colors are grouped in palets. The colors in such a palet can +%D have colorful names, but best is to use names that specify +%D their use, like {\em important} or {\em danger}. As a sort +%D of example \CONTEXT\ has some palets predefined, +%D like:\footnote{At the time I wrote the palet support, I was +%D reading 'A hort history of time' of S.~Hawkins, so that's +%D why we stuck to quarks.} +%D +%D \starttyping +%D \definepalet +%D [alfa] +%D [ top=rood:7, +%D bottom=groen:6, +%D up=blauw:5, +%D down=cyaan:4, +%D strange=magenta:3, +%D charm=geel:2] +%D \stoptyping +%D +%D It's formal definition is: +%D +%D \showsetup{definepalet} +%D +%D Visualized, such a palet looks like: +%D +%D \startbuffer[palet] +%D \showpalet [alfa] [horizontal,name,number,value] +%D \stopbuffer +%D +%D \startlinecorrection +%D \getbuffer[palet] +%D \stoplinecorrection +%D +%D This bar shows both the color and gray alternatives of the +%D palet components (not visible in black and white print). +%D +%D When needed, one can copy a palet by saying: +%D +%D \starttyping +%D \definepalet [TEXcolorpretty] [colorpretty] +%D \stoptyping +%D +%D This saves us some typing in for instance the modules that +%D deal with pretty verbatim typesetting. + +\def\definepalet + {\dodoubleargument\dodefinepalet} + +\def\dodefinepalet[#1][#2]% + {\doifassignmentelse{#2} + {%\showmessage\m!colors6{#1}% + \letvalue{\??pa#1}\empty + \setevalue{\??pa\??pa#1}{#2}% + \def\dodododefinepalet[##1=##2]% + {\doifvaluesomething{\??pa#1} + {\setevalue{\??pa#1}{\csname\??pa#1\endcsname,}}% + \setevalue{\??pa#1}{\csname\??pa#1\endcsname##1}% + \dodefinepaletcolor{#1}{##1}{##2}}% + \def\dododefinepalet##1% + {\dodododefinepalet[##1]}% + \processcommalist[#2]\dododefinepalet} + {\doifdefined{\??pa#2} + {\expanded{\dodefinepalet[#1][\csname\??pa\??pa#2\endcsname]}}}} + +\ifx\dodefinepaletcolor\undefined + \let\dodefinepaletcolor\gobblethreearguments +\fi + +\let\paletsize\!!zerocount + +\def\getpaletsize[#1]% + {\getcommacommandsize[\csname\??pa\??pa#1\endcsname]% + \edef\paletsize{\number\commalistsize}} + +%D Instead of refering to colors, one can also directly specify +%D a color: +%D +%D \starttyping +%D \definepalet[test][xx=green] +%D \definepalet[test][xx={y=.4}] +%D \stoptyping + +%D \macros +%D {setuppalet} +%D +%D Colors are taken from the current palet, if defined. +%D Setting the current palet is done by: +%D +%D \showsetup{setuppalet} + +\let\currentpalet\empty + +\def\setuppalet + {\dosingleempty\dosetuppalet} + +\def\dosetuppalet[#1]% + {\edef\currentpalet{#1}% + \ifx\currentpalet\empty + % seems to be a reset + \else\ifcsname\??pa\currentpalet\endcsname + \edef\currentpalet{#1:}% + \else + \showmessage\m!colors7\currentpalet + \let\currentpalet\empty + \fi\fi} + +%D \macros +%D {showpalet} +%D +%D The previous visualization was typeset with: +%D +%D \typebuffer[palet] +%D +%D This commands is defined as: +%D +%D \showsetup{showpalet} + +\fetchruntimecommand \showpalet {\f!colorprefix\s!run} + +%D \macros +%D {showcolorcomponents} +%D +%D \starttyping +%D \showcolorcomponents[color-1,color-2] +%D \stoptyping + +\fetchruntimecommand \showcolorcomponents {\f!colorprefix\s!run} + +%D \macros +%D {definecolorgroup} +%D +%D The naming of the colors in this palet suggests some +%D ordening, which in turn is suported by color grouping. +%D +%D \starttyping +%D \definecolorgroup +%D [red] +%D [1.00:0.90:0.90, +%D 1.00:0.80:0.80, +%D 1.00:0.70:0.70, +%D 1.00:0.55:0.55, +%D 1.00:0.40:0.40, +%D 1.00:0.25:0.25, +%D 1.00:0.15:0.15, +%D 0.90:0.00:0.00] +%D \stoptyping +%D +%D In such a color group colors are numbered from~$1$ to~$n$. +%D +%D \showsetup{definecolorgroup} +%D +%D This kind of specification is not only more compact than +%D defining each color separate, it also loads faster and takes +%D less bytes. + +\def\definecolorgroup + {\dotripleempty\dodefinecolorgroup} + +\def\dododefinecolorgroupgray [#1][#2:#3]{\definecolor [#1:\the\colorcount][s=#2]} +\def\dododefinecolorgrouprgb [#1][#2:#3:#4:#5]{\definecolor [#1:\the\colorcount][r=#2,g=#3,b=#4]} +\def\dododefinecolorgroupcmyk[#1][#2:#3:#4:#5:#6]{\definecolor [#1:\the\colorcount][c=#2,m=#3=,y=#4,k=#5]} +\def\dododefinecolorgroupspot [#1][#2:#3:#4]{\definespotcolor[#1:\the\colorcount][#2][p=#3]} + +\def\dododefinecolorgroup#1#2% + {\advance\colorcount\plusone + \getvalue{dododefinecolorgroup\currentcolorspace}[#1][#2:0:0:0:0]} + +\def\dodefinecolorgroup[#1][#2][#3]% obsolete, just use palets + {\ifthirdargument + \doifelsenothing{#2}{\let\currentcolorspace\v!rgb}{\def\currentcolorspace{#2}}% + \colorcount\zerocount + \processcommalist[#3]{\dododefinecolorgroup{#1}}% + \else + \doifinstringelse{:}{#2} + {\definecolorgroup[#1][\v!rgb][#2]} + {\doloop + {\doifdefinedelse{\??cr#2:\recurselevel} + {\setevalue{\??cr#1:\recurselevel}{\csname\??cr#2:\recurselevel\endcsname}} + {\exitloop}}}% + \fi} + +%D \macros +%D {showcolorgroup} +%D +%D We can show the group by: +%D +%D \startbuffer +%D \showcolorgroup [blue] [horizontal,name,number,value] +%D \stopbuffer +%D +%D \typebuffer +%D +%D or in color: +%D +%D \startlinecorrection +%D \getbuffer +%D \stoplinecorrection +%D +%D which uses: +%D +%D \showsetup{showcolorgroup} + +\fetchruntimecommand \showcolorgroup {\f!colorprefix\s!run} + +%D There are ten predefined color groups, like +%D \color[green]{\em groen}, \color[red]{\em rood}, +%D \color[blue]{\em blauw}, \color[cyan]{\em cyaan}, +%D \color[magenta]{\em magenta} and \color[yellow]{\em geel}. +%D +%D \startlinecorrection +%D \hbox to \hsize +%D {\hss +%D \showcolorgroup [red] [vertical,name,number]\hss +%D \showcolorgroup [green] [vertical,name]\hss +%D \showcolorgroup [blue] [vertical,name]\hss +%D \showcolorgroup [cyan] [vertical,name]\hss +%D \showcolorgroup [magenta][vertical,name]\hss +%D \showcolorgroup [yellow] [vertical,name]\hss} +%D \stoplinecorrection +%D +%D These groups are used to define palets {\em alfa} upto {\em +%D zeta}. As long as we don't use colors from the same row, we +%D get ourselves distinctive palets. By activating such a palet +%D one gains access to its members {\em top} to {\em charm} (of +%D course one should use more suitable names than these). +%D +%D \startlinecorrection +%D \hbox to \hsize +%D {\showpalet [alfa] [vertical,name,number]\hss +%D \showpalet [beta] [vertical,name]\hss +%D \showpalet [gamma] [vertical,name]\hss +%D \showpalet [delta] [vertical,name]\hss +%D \showpalet [epsilon] [vertical,name]\hss +%D \showpalet [zeta] [vertical,name]} +%D \stoplinecorrection +%D +%D By using the keyword \type {value} the individual color +%D components are shown too. When printed in color, these +%D showcases show both the colors and the gray value. + +%D \macros +%D {comparepalet} +%D +%D There are some more testing macros available: +%D +%D \startbuffer +%D \comparepalet [alfa] +%D \stopbuffer +%D +%D \typebuffer +%D +%D shows the palet colors against a background: +%D +%D \startlinecorrection +%D \getbuffer +%D \stoplinecorrection +%D +%D The formal definition is: +%D +%D \showsetup{comparepalet} + +\fetchruntimecommand \comparepalet {\f!colorprefix\s!run} + +%D \macros +%D {comparecolorgroup} +%D +%D The similar command: +%D +%D \startbuffer +%D \comparecolorgroup [blue] +%D \stopbuffer +%D +%D \typebuffer +%D +%D shows color groups: +%D +%D \startlinecorrection +%D \getbuffer +%D \stoplinecorrection +%D +%D this commands are defined as: +%D +%D \showsetup{comparecolorgroup} + +\fetchruntimecommand \comparecolorgroup {\f!colorprefix\s!run} + +%D \macros +%D {showcolor} +%D +%D But let's not forget that we also have the more traditional +%D non||related colors. These show up after: +%D +%D \starttyping +%D \showcolor [name] +%D \stoptyping +%D +%D Where \type{name} for instance can be \type{rgb}. +%D +%D \showsetup{showcolor} + +\fetchruntimecommand \showcolor {\f!colorprefix\s!run} + +%D It would make sense to put the following code in \type +%D {colo-mps}, but it it rather low level. + +%D \macros +%D {negatecolorcomponent,negatedcolorcomponent} +%D +%D These speak for themselves. See \type {colo-ext} for usage. + +\def\negatecolorcomponent#1% #1 = \macro + {\scratchdimen\onepoint\advance\scratchdimen-#1\onepoint + \ifdim\scratchdimen<\zeropoint\scratchdimen\zeropoint\fi + \edef#1{\withoutpt\the\scratchdimen}} + +\let\negatedcolorcomponent\firstofoneargument + +\def\negatedcolorcomponent#1% + {\ifdim\dimexpr\onepoint-#1\onepoint\relax<\zeropoint + \!!zerocount + \else + \expandafter\withoutpt\the\dimexpr\onepoint-#1\onepoint\relax + \fi} + +\def\negatecolorcomponent#1% #1 = \macro + {\edef#1{\negatedcolorcomponent{#1}}} + +%D \macros +%D {ifMPgraphics, ifMPcmykcolors, MPcolor} +%D +%D A very special macro is \type{\MPcolor}. This one can be +%D used to pass a \CONTEXT\ color to \METAPOST. +%D +%D \starttyping +%D \MPcolor{my own red} +%D \stoptyping +%D +%D This macro returns a \METAPOST\ triplet \type{(R,G,B)}. +%D Unless \CMYK\ color support is turned on with \type +%D {MPcmyk}, only \cap{RGB} colors and gray scales are +%D supported. + +\newif\ifMPcmykcolors % \MPcmykcolorsfalse +\newif\ifMPspotcolors % \MPspotcolorsfalse + +\ifx\MPcolor\undefined + \def\MPcolor#1{(0,0,0)} +\fi + +%D \macros +%D {PDFcolor,FDFcolor} +%D +%D Similar alternatives are avaliable for \PDF: + +%D For the moment we keep the next downward compatibility +%D switch, i.e.\ expanded colors. However, predefined colors +%D and palets are no longer expanded (which is what I wanted +%D in the first place). +%D +%D Well, in case we want to do color separation and use CMYK +%D colors only, this is dangerous since unwanted remapping may +%D take place. Especially when we redefine already defined +%D colors in another color space (e.g. darkgreen is +%D predefined in RGB color space, so a redefinition in CMYK +%D coordinates before RGB mode is disabled, would give +%D unexpected results due to the already frozen color spec.) +%D +%D So, from now on, colors are not frozen any more! + +\chardef\currentcolorchannel=0 + +\newif\iffilterspotcolor \filterspotcolorfalse +\newif\ifdoingspotcolor \doingspotcolorfalse + +\def\registercolorchannel#1% + {\ifdoingspotcolor \else + \global\expandafter\chardef\csname\??cs#1\endcsname\zerocount + \fi} + +\newif\ifhidesplitcolor \hidesplitcolortrue + +%D The next macro is for instance used in figure splitting: + +\def\doifseparatingcolorselse + {\iffilterspotcolor + \@EA\firstoftwoarguments + \else\ifcase\currentcolorchannel + \@EAEAEA\secondoftwoarguments + \else + \@EAEAEA\firstoftwoarguments + \fi\fi} + +\def\doifcolorchannelelse#1% + {\doifseparatingcolorselse + {\doifelsenothing{#1} + \secondoftwoarguments + {\doifelse{#1}\@@clsplit + \firstoftwoarguments + \secondoftwoarguments}} + \secondoftwoarguments} + +\def\resetcolorseparation + {\filterspotcolorfalse + \chardef\currentcolorchannel\zerocount} + +%D These can be used in selecting specific files (like +%D figuredatabases). + +% we already have: +% +% \def\colorsplitsuffix{\ifcase\currentcolorchannel\else-\@@clsplitsen\fi} +% \def\colorsplitprefix{\ifcase\currentcolorchannel\else\@@clsplitsen-\fi} + +\def\colorchannelprefix{\doifseparatingcolorselse\@@clsplit\empty-} +\def\colorchannelsuffix{-\doifseparatingcolorselse\@@clsplit\empty} + +%D We now define the low level macros: + \chardef\colorversion=1 %D Color support is not present in \TEX. Colorful output can @@ -165,8 +1029,12 @@ \def\dodefineglobalcolor{\dododefinecolor\doglobal\setgvalue\setxvalue1} \def\dodefinenamedcolor {\dododefinecolor\doglobal\setvalue \setevalue0} +\let\colorlist\empty % not really used, only for colo-run +\setfalse\collectcolorsinlist +\def\collectcolorinlist#1{\doglobal\addtocommalist{#1}\colorlist} + \def\dododefinecolor#1#2#3#4[#5][#6]% #2==set(g)value #3==set[e|x]value - {#1\addtocommalist{#5}\colorlist % optional + {\ifconditional\collectcolorsinlist\collectcolorinlist{#5}\fi \doifassignmentelse{#6} {\@@resetcolorparameters \getparameters[\??cl @@][#6]% @@ -218,7 +1086,7 @@ \def\dodefinespotcolor[#1][#2][#3]% todo: always global {\doifnot{#1}{#2} {\@@resetcolorparameters - \doglobal\addtocommalist{#1}\colorlist % optional + \ifconditional\collectcolorsinlist\collectcolorinlist{#1}\fi \edef\@@cl@@n{#2}% \getparameters[\??cl @@][#3]% \doifnothing\@@cl@@p{\let\@@cl@@p\!!plusone}% @@ -312,7 +1180,7 @@ \def\dodefinespotcolor[#1][#2][#3]% todo: always global (REDEFINED) {\doifnot{#1}{#2} {\@@resetcolorparameters - \doglobal\addtocommalist{#1}\colorlist % optional + \ifconditional\collectcolorsinlist\collectcolorinlist{#1}\fi \edef\@@cl@@n{#2}% \getparameters[\??cl @@][#3]% \doifnothing \@@cl@@p{\let\@@cl@@p\!!plusone}% @@ -1859,4 +2727,50 @@ \appendtoks \localcolortrue \to \everyshapebox +%D \macros +%D {forcecolorhack} +%D +%D Awful \unknown + +\let\forcecolorhack\relax + +%D We default to the colors defined in \module{colo-rgb} and +%D support both \cap{RGB} and \cap{CMYK} output. As you can +%D see, color support is turned off by default. Reduction of +%D gray colors to gray scales is turned on. + +\definecolor[black][s=0] +\definecolor[white][s=1] + +\definetransparency [none] [0] +\definetransparency [normal] [1] +\definetransparency [multiply] [2] +\definetransparency [screen] [3] +\definetransparency [overlay] [4] +\definetransparency [softlight] [5] +\definetransparency [hardlight] [6] +\definetransparency [colordodge] [7] +\definetransparency [colorburn] [8] +\definetransparency [darken] [9] +\definetransparency [lighten] [10] +\definetransparency [difference] [11] +\definetransparency [exclusion] [12] + +\setupcolors + [\c!state=\v!stop, + \c!conversion=\v!yes, + \c!reduction=\v!no, + \c!rgb=\v!yes, + \c!cmyk=\v!yes, + \c!spot=\v!yes, + \c!mp\c!cmyk=\@@clcmyk, + \c!mp\c!spot=\@@clspot, + \c!expansion=\v!no, + \c!textcolor=, + \c!split=\v!no, + \c!criterium=\v!all] + +\setupcolor + [\v!rgb] + \protect \endinput diff --git a/tex/context/base/colo-ini.mkiv b/tex/context/base/colo-ini.mkiv index 7f79cdfad..cf7f2446a 100644 --- a/tex/context/base/colo-ini.mkiv +++ b/tex/context/base/colo-ini.mkiv @@ -11,8 +11,874 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. +%D We need to clean this up further but first we hav eto make sure that mkiv +%D code works ok. + +\writestatus{loading}{ConTeXt Color Macros / Initialization} + +%D This module implements color. Since \MKII\ and \MKIV\ use a completely +%D different approach, this module only implements a few generic mechanisms. + +\registerctxluafile{colo-ini}{1.000} + \unprotect +\chardef\colorversion=1 % temp, needed for tracing purposes, mkiv transition + +%D We use a couple of local registers. That way we don't have +%D to group when converting colors. By the way, this is not +%D really faster. We can sqeeze half a second runtime for 50K +%D switches on a 1G machine, but the macros will become rather +%D ugly then. To mention one such improvement: no colon +%D after the key character (.25 sec). + +\newdimen\colordimen +\newcount\colorcount + +%D When typesetting for paper, we prefer using the \cap{CMYK} +%D color space, but for on||screen viewing we prefer \cap{RGB} +%D (the previous implementation supported only this scheme). +%D Independant of such specifications, we support some automatic +%D conversions: +%D +%D \startitemize[packed] +%D \item convert all colors to \cap{RGB} +%D \item convert all colors to \cap{CMYK} +%D \item convert all colors to gray scales +%D \stopitemize +%D +%D We also support optimization of colors to gray scales. +%D +%D \startitemize[continue] +%D \item reduce gray colors to gray scales +%D \item reduce \cap{CMY} components to \cap{K} +%D \stopitemize +%D +%D These options are communicated by means of: + +\newif\ifRGBsupported +\newif\ifCMYKsupported +\newif\ifSPOTsupported +\newif\ifpreferGRAY +\newif\ifGRAYprefered +\newif\ifreduceCMYK +\newif\ifconverttoGRAY +\newif\ifweightGRAY \weightGRAYtrue + +\newif\ifconvertMPcolors +\newif\ifreduceMPcolors +\newif\ifforcegrayMPcolors + +%D The last boolean controls reduction of \cap{CMYK} to +%D \cap{CMY} colors. When set to true, the black component +%D is added to the other ones. +%D +%D Prefering gray is not the same as converting to gray. +%D Conversion treats each color components in a different way, +%D while prefering is just a reduction and thus a +%D space||saving option. + +\newif\iffreezecolors \freezecolorsfalse +\newif\ifincolor % true if colors enabled +\newif\iflocalcolor + +\let\colorlist \empty +\let\currentspotcolor \empty +\let\allspotcolors \empty +\let\usedspotcolors \empty +\let\usedcolorchannels\empty +\let\currentpalet \empty + +%D \macros +%D {definecolor,defineglobalcolor,definenamedcolor,definespotcolor,definemultitonecolor} +%D +%D \startbuffer +%D \definecolor [blue] [c=1,m=.38,y=0,k=.64] % pantone pms 2965 uncoated m +%D \definecolor [yellow] [c=0,m=.28,y=1,k=.06] % pantone pms 124 uncoated m +%D +%D \definespotcolor [blue-100] [blue] [p=1] +%D \definespotcolor [yellow-100] [yellow] [p=1] +%D +%D \definemultitonecolor [pdftoolscolor] [blue=.12,yellow=.28] [c=.1,m=.1,y=.3,k=.1] +%D +%D \useexternalfigure[demofig][mill.png][object=no] +%D +%D \startcombination[4*1] +%D {\externalfigure[demofig]} {no color} +%D {\externalfigure[demofig][color=pdftoolscolor]} {indexed duotone} +%D {\externalfigure[demofig][color=blue-100]} {spot color} +%D {\externalfigure[demofig][color=yellow-100]} {spot color} +%D \stopcombination +%D \stopbuffer +%D +%D \getbuffer \typebuffer + +\def\definecolor {\dodoubleargument\dodefinecolor} +\def\defineglobalcolor {\dodoubleargument\dodefineglobalcolor} +\def\definenamedcolor {\dodoubleargument\dodefinenamedcolor} +\def\definespotcolor {\dotripleargument\dodefinespotcolor} +\def\definemultitonecolor{\doquadrupleempty\dodefinemultitonecolor} + +% check: registerusedspotcolors +% check: registerusedcolorchannels + +%D \macros +%D {doifcolorelse, doifcolor} +%D +%D Switching to a color is done by means of the following +%D command. Later on we will explain the use of palets. We +%D define ourselves a color conditional first. + +\ifx\doifcolorelse\undefined + \let\doifcolorelse\secondoftwoarguments + \let\doifcolor \gobbleoneargument +\fi + +%D \macros +%D {localstartcolor,localstopcolor} +%D +%D Simple color support, that is without nesting, is provided +%D by: + +\ifx\localstartcolor\undefined + \let\localstartcolor\undefined + \let\localstopcolor \undefined +\fi + +%D \macros +%D {faststartcolor,faststopcolor} +%D +%D No checking for arguments and such: + +\ifx\faststartcolor\undefined + \def\faststartcolor[#1]{} + \def\faststopcolor {} +\fi + +%D These local ones may go away in future versions. + +%D \macros +%D {startcolor,stopcolor} +%D +%D The more save method, the one that saves the current color +%D state and returns to this state afterward, is activated by: +%D +%D \showsetup{startcolor} + +\ifx\startcolor\undefined + \let\startcolor\undefined + \let\stopcolor \undefined +\fi + +%D \macros +%D {startcurrentcolor,stopcurrentcolor} + +\def\startcurrentcolor{\startcolor[\outercolorname]} +\def\stopcurrentcolor {\stopcolor} + +%D \macros +%D {color,graycolor} +%D +%D This leaves the simple color command: +%D +%D \showsetup{color} +%D \showsetup{graycolor} + +\ifx\color\undefined + \def\color [#1]{} + \def\graycolor[#1]{} + \def\gray {\graycolor} +\fi + +%D \macros +%D {localstartraster,localstopraster, +%D startraster,stopraster,raster} +%D +%D The previous conversions are not linear and treat each color +%D component according to human perception curves. Pure gray +%D (we call them rasters) has equal color components. In +%D \CONTEXT\ rasters are only used as backgrounds and these +%D don't cross page boundaries in the way color does. Therefore +%D we don't need stacks and marks. Just to be compatible with +%D color support we offer both 'global' and 'local' commands. + +\ifx\startraster\undefined + \def\startraster [#1]{} + \def\stopraster {} + \def\raster [#1]{} + \def\localstartraster[#1]{} + \def\localstopraster {} +\fi + +%D \macros +%D {colorvalue, grayvalue} +%D +%D We can typeset the color components using \type{\colorvalue} and +%D \type{\grayvalue}. The commands: +%D +%D \startbuffer +%D color value of SomeKindOfRed: \colorvalue{SomeKindOfRed} \crlf +%D gray value of SomeKindOfRed: \grayvalue{SomeKindOfRed} +%D \stopbuffer +%D +%D \typebuffer +%D +%D show us: +%D +%D \startvoorbeeld +%D \getbuffer +%D \stopvoorbeeld + +\def\colorformatseparator{ } + +\ifx\colorvalue\undefined + \let\colorvalue\gobbleoneargument + \let\grayvalue \gobbleoneargument +\fi + +% check: \currentcolorname +% check: \outercolorname + +%D \macros +%D {setupcolor} +%D +%D Color definitions can be grouped in files with the name: +%D +%D \starttyping +%D \f!colorprefix-identifier.tex +%D \stoptyping +%D +%D where \type{\f!colorprefix} is \unprotect {\tttf \f!colorprefix}. +%D Loading such a file is done by \protect +%D +%D \showsetup{setupcolor} +%D +%D Some default colors are specified in \type{colo-rgb.tex}, +%D which is loaded into the format by: +%D +%D \starttyping +%D \setupcolor[rgb] +%D \stoptyping + +\let\colorstyle\empty + +\def\setupcolor + {\dosingleargument\dosetupcolor} + +\def\dosetupcolor[#1]% + {\doifnot{#1}\colorstyle + {\def\colorstyle{#1}% + \processcommalist[#1]\dodosetupcolor}} + +\def\dodosetupcolor#1% + {\makeshortfilename[\truefilename{\f!colorprefix#1}]% + \startreadingfile + \readsysfile\shortfilename + {\showmessage\m!colors4\colorstyle} + {\showmessage\m!colors5\colorstyle}% + \stopreadingfile} + +\let\usecolors\setupcolor + +% check: \chardef\currentcolorchannel=0 +% check: \startcolormode +% check: \newif\iffilterspotcolor \filterspotcolorfalse +% check: \newif\ifdoingspotcolor \doingspotcolorfalse +% check: \registercolorchannel + +%D \macros +%D {definetransparency} +%D +%D This command numbers to names: + +\def\definetransparency + {\dodoubleargument\dodefinetransparency} + +\def\setupcolors + {\dosingleargument\dosetupcolors} + +\def\resetcolorsplitting + {\chardef\currentcolorchannel\zerocount + \let\currentspotcolor\empty + \filterspotcolorfalse} + +\def\colorsplitsuffix{\ifcase\currentcolorchannel\else-\@@clsplit\fi} +\def\colorsplitprefix{\ifcase\currentcolorchannel\else\@@clsplit-\fi} + +\def\setcolorsplitting + {\resetsystemmode{\v!color\colorsplitsuffix}% + \resetcolorsplitting + \processaction + [\@@clsplit] + [ c=>\chardef\currentcolorchannel1,% + m=>\chardef\currentcolorchannel2,% + y=>\chardef\currentcolorchannel3,% + k=>\chardef\currentcolorchannel4,% + r=>\chardef\currentcolorchannel5,% + g=>\chardef\currentcolorchannel6,% + b=>\chardef\currentcolorchannel7,% + s=>\chardef\currentcolorchannel8,% + \v!no=>,% \currentcolorchannel0,% all colors + \s!default=>,% \currentcolorchannel0,% all colors + \s!unknown=>\filterspotcolortrue + \edef\currentspotcolor{\commalistelement}]% + \setsystemmode{\v!color\colorsplitsuffix}% + \iffilterspotcolor \let\@@clrgb\v!no \fi} + +\ifx\dosetupcolormodel\undefined + \let\dosetupcolormodel\relax +\fi + +\def\dosetupcolors[#1]% some no longer make sense in MkIV + {\getparameters[\??cl][#1]% + \doifelse\@@clspot\v!yes + \SPOTsupportedtrue + \SPOTsupportedfalse + \doifelsenothing\@@clsplit + \resetcolorsplitting + \setcolorsplitting + \doifelse\@@clreduction\v!yes + \reduceCMYKtrue + \reduceCMYKfalse + \doifelse\@@clexpansion\v!yes + \freezecolorstrue + \freezecolorsfalse + \doifelse\@@clcriterium\v!all + \hidesplitcolortrue + \hidesplitcolorfalse + \doifelse\@@clrgb\v!no + {\ifRGBsupported \ifproductionrun\showmessage\m!colors {9}\v!rgb \fi\RGBsupportedfalse \fi} + {\ifRGBsupported \else\ifproductionrun\showmessage\m!colors{10}\v!rgb \fi\RGBsupportedtrue \fi}% + \doifelse\@@clcmyk\v!no + {\ifCMYKsupported \ifproductionrun\showmessage\m!colors {9}\v!cmyk \fi\CMYKsupportedfalse\fi} + {\ifCMYKsupported\else\ifproductionrun\showmessage\m!colors{10}\v!cmyk \fi\CMYKsupportedtrue \fi}% + \doifelse\@@clmpcmyk\v!no + {\ifMPcmykcolors \ifproductionrun\showmessage\m!colors {9}{\v!mp\v!cmyk}\fi\MPcmykcolorsfalse \fi} + {\ifMPcmykcolors \else\ifproductionrun\showmessage\m!colors{10}{\v!mp\v!cmyk}\fi\MPcmykcolorstrue \fi}% + \doifelse\@@clmpspot\v!no + {\ifMPspotcolors \ifproductionrun\showmessage\m!colors {9}{\v!mp\v!spot}\fi\MPspotcolorsfalse \fi} + {\ifMPspotcolors \else\ifproductionrun\showmessage\m!colors{10}{\v!mp\v!spot}\fi\MPspotcolorstrue \fi}% + \preferGRAYfalse + \processaction + [\@@clconversion] + [ \v!yes=>\preferGRAYtrue, + \v!always=>\preferGRAYtrue\RGBsupportedfalse\CMYKsupportedfalse]% + \ifRGBsupported + \converttoGRAYfalse + \forcegrayMPcolorsfalse + \else\ifCMYKsupported + \converttoGRAYfalse + \forcegrayMPcolorsfalse + \convertMPcolorstrue + \ifreduceCMYK + \reduceMPcolorstrue + \fi + \else + \ifconverttoGRAY\else\showmessage\m!colors{11}\empty\fi + \converttoGRAYtrue + \forcegrayMPcolorstrue + \convertMPcolorsfalse + \reduceMPcolorsfalse + \fi\fi + \processaction + [\@@clstate] + [ \v!global=>\ifincolor\else\showmessage\m!colors1\colorstyle\fi + \incolortrue\localcolorfalse, + \v!local=>\ifincolor\else\showmessage\m!colors2\colorstyle\fi + \incolortrue\localcolortrue, + \v!start=>\ifincolor\else\showmessage\m!colors1\colorstyle\fi + \incolortrue\localcolorfalse + \let\@@clstate\v!global, + \v!stop=>\incolorfalse\localcolorfalse + \forcegrayMPcolorstrue]% + \dosetupcolormodel + \initializemaintextcolor} + +%D \macros +%D {startregistercolor,stopregistercolor,permitcolormode} +%D +%D If you only want to register a color, the switch \type +%D {\ifpermitcolormode} can be used. That way the nested +%D colors know where to go back to. + +\ifx\startregistercolor\undefined + \def\startregistercolor[#1]{} + \def\stopregistercolor {} +\fi + +%D We use these macros for implementing text colors +%D (actually, the first application was in foreground +%D colors). +%D +%D \starttyping +%D \starttextcolor[red] +%D \dorecurse{10}{\input tufte \color[green]{oeps} \par} +%D \stoptextcolor +%D \stoptyping +%D +%D This is more efficient than the alternative: +%D +%D \starttyping +%D \setupbackgrounds[text][foregroundcolor=red] +%D \startregistercolor[red] +%D \dorecurse{10}{\input tufte \color[green]{oeps} \par} +%D \stopregistercolor +%D \stoptyping + +\def\maintextcolor {} +\def\defaulttextcolor {black} +\def\@@themaintextcolor{themaintextcolor} + +\ifx\initializemaintextcolor\undefined + \def\starttextcolor [#1]{} + \def\stoptextcolor {} + \def\initializemaintextcolor {} +\fi + +\ifx\restoretextcolor\undefined % to be redone + \let\restoretextcolor \firstofoneargument + \let\localstarttextcolor\relax + \let\localstoptextcolor \relax +\fi + +%D In this documentation we will not go into too much details +%D on palets. Curious users can find more information on this +%D topic in \from[use of color]. +%D +%D At the moment we implemented color in \CONTEXT\ color +%D printing was not yet on the desktop. In spite of this lack our +%D graphics designer made colorfull illustrations. When printed +%D on a black and white printer, distinctive colors can come +%D out equally gray. We therefore decided to use only colors +%D that were distinctive in colors as well as in black and +%D white print. +%D +%D Although none of the graphic packages we used supported +%D logical colors and global color redefition, we build this +%D support into \CONTEXT. This enabled us to experiment and +%D also prepared us for the future. + +%D \macros +%D {definepalet} +%D +%D Colors are grouped in palets. The colors in such a palet can +%D have colorful names, but best is to use names that specify +%D their use, like {\em important} or {\em danger}. As a sort +%D of example \CONTEXT\ has some palets predefined, +%D like:\footnote{At the time I wrote the palet support, I was +%D reading 'A hort history of time' of S.~Hawkins, so that's +%D why we stuck to quarks.} +%D +%D \starttyping +%D \definepalet +%D [alfa] +%D [ top=rood:7, +%D bottom=groen:6, +%D up=blauw:5, +%D down=cyaan:4, +%D strange=magenta:3, +%D charm=geel:2] +%D \stoptyping +%D +%D It's formal definition is: +%D +%D \showsetup{definepalet} +%D +%D Visualized, such a palet looks like: +%D +%D \startbuffer[palet] +%D \showpalet [alfa] [horizontal,name,number,value] +%D \stopbuffer +%D +%D \startlinecorrection +%D \getbuffer[palet] +%D \stoplinecorrection +%D +%D This bar shows both the color and gray alternatives of the +%D palet components (not visible in black and white print). +%D +%D When needed, one can copy a palet by saying: +%D +%D \starttyping +%D \definepalet [TEXcolorpretty] [colorpretty] +%D \stoptyping +%D +%D This saves us some typing in for instance the modules that +%D deal with pretty verbatim typesetting. + +\def\definepalet + {\dodoubleargument\dodefinepalet} + +\def\dodefinepalet[#1][#2]% + {\doifassignmentelse{#2} + {%\showmessage\m!colors6{#1}% + \letvalue{\??pa#1}\empty + \setevalue{\??pa\??pa#1}{#2}% + \def\dodododefinepalet[##1=##2]% + {\doifvaluesomething{\??pa#1} + {\setevalue{\??pa#1}{\csname\??pa#1\endcsname,}}% + \setevalue{\??pa#1}{\csname\??pa#1\endcsname##1}% + \dodefinepaletcolor{#1}{##1}{##2}}% + \def\dododefinepalet##1% + {\dodododefinepalet[##1]}% + \processcommalist[#2]\dododefinepalet} + {\doifdefined{\??pa#2} + {\expanded{\dodefinepalet[#1][\csname\??pa\??pa#2\endcsname]}}}} + +\ifx\dodefinepaletcolor\undefined + \let\dodefinepaletcolor\gobblethreearguments +\fi + +\let\paletsize\!!zerocount + +\def\getpaletsize[#1]% + {\getcommacommandsize[\csname\??pa\??pa#1\endcsname]% + \edef\paletsize{\number\commalistsize}} + +%D Instead of refering to colors, one can also directly specify +%D a color: +%D +%D \starttyping +%D \definepalet[test][xx=green] +%D \definepalet[test][xx={y=.4}] +%D \stoptyping + +%D \macros +%D {setuppalet} +%D +%D Colors are taken from the current palet, if defined. +%D Setting the current palet is done by: +%D +%D \showsetup{setuppalet} + +\let\currentpalet\empty + +\def\setuppalet + {\dosingleempty\dosetuppalet} + +\def\dosetuppalet[#1]% + {\edef\currentpalet{#1}% + \ifx\currentpalet\empty + % seems to be a reset + \else\ifcsname\??pa\currentpalet\endcsname + \edef\currentpalet{#1:}% + \else + \showmessage\m!colors7\currentpalet + \let\currentpalet\empty + \fi\fi} + +%D \macros +%D {showpalet} +%D +%D The previous visualization was typeset with: +%D +%D \typebuffer[palet] +%D +%D This commands is defined as: +%D +%D \showsetup{showpalet} + +\fetchruntimecommand \showpalet {\f!colorprefix\s!run} + +%D \macros +%D {showcolorcomponents} +%D +%D \starttyping +%D \showcolorcomponents[color-1,color-2] +%D \stoptyping + +\fetchruntimecommand \showcolorcomponents {\f!colorprefix\s!run} + +%D \macros +%D {definecolorgroup} +%D +%D The naming of the colors in this palet suggests some +%D ordening, which in turn is suported by color grouping. +%D +%D \starttyping +%D \definecolorgroup +%D [red] +%D [1.00:0.90:0.90, +%D 1.00:0.80:0.80, +%D 1.00:0.70:0.70, +%D 1.00:0.55:0.55, +%D 1.00:0.40:0.40, +%D 1.00:0.25:0.25, +%D 1.00:0.15:0.15, +%D 0.90:0.00:0.00] +%D \stoptyping +%D +%D In such a color group colors are numbered from~$1$ to~$n$. +%D +%D \showsetup{definecolorgroup} +%D +%D This kind of specification is not only more compact than +%D defining each color separate, it also loads faster and takes +%D less bytes. + +\def\definecolorgroup + {\dotripleempty\dodefinecolorgroup} + +\def\dododefinecolorgroupgray [#1][#2:#3]{\definecolor [#1:\the\colorcount][s=#2]} +\def\dododefinecolorgrouprgb [#1][#2:#3:#4:#5]{\definecolor [#1:\the\colorcount][r=#2,g=#3,b=#4]} +\def\dododefinecolorgroupcmyk[#1][#2:#3:#4:#5:#6]{\definecolor [#1:\the\colorcount][c=#2,m=#3=,y=#4,k=#5]} +\def\dododefinecolorgroupspot [#1][#2:#3:#4]{\definespotcolor[#1:\the\colorcount][#2][p=#3]} + +\def\dododefinecolorgroup#1#2% + {\advance\colorcount\plusone + \getvalue{dododefinecolorgroup\currentcolorspace}[#1][#2:0:0:0:0]} + +\def\dodefinecolorgroup[#1][#2][#3]% obsolete, just use palets + {\ifthirdargument + \doifelsenothing{#2}{\let\currentcolorspace\v!rgb}{\def\currentcolorspace{#2}}% + \colorcount\zerocount + \processcommalist[#3]{\dododefinecolorgroup{#1}}% + \else + \doifinstringelse{:}{#2} + {\definecolorgroup[#1][\v!rgb][#2]} + {\doloop + {\doifdefinedelse{\??cr#2:\recurselevel} + {\setevalue{\??cr#1:\recurselevel}{\csname\??cr#2:\recurselevel\endcsname}} + {\exitloop}}}% + \fi} + +%D \macros +%D {showcolorgroup} +%D +%D We can show the group by: +%D +%D \startbuffer +%D \showcolorgroup [blue] [horizontal,name,number,value] +%D \stopbuffer +%D +%D \typebuffer +%D +%D or in color: +%D +%D \startlinecorrection +%D \getbuffer +%D \stoplinecorrection +%D +%D which uses: +%D +%D \showsetup{showcolorgroup} + +\fetchruntimecommand \showcolorgroup {\f!colorprefix\s!run} + +%D There are ten predefined color groups, like +%D \color[green]{\em groen}, \color[red]{\em rood}, +%D \color[blue]{\em blauw}, \color[cyan]{\em cyaan}, +%D \color[magenta]{\em magenta} and \color[yellow]{\em geel}. +%D +%D \startlinecorrection +%D \hbox to \hsize +%D {\hss +%D \showcolorgroup [red] [vertical,name,number]\hss +%D \showcolorgroup [green] [vertical,name]\hss +%D \showcolorgroup [blue] [vertical,name]\hss +%D \showcolorgroup [cyan] [vertical,name]\hss +%D \showcolorgroup [magenta][vertical,name]\hss +%D \showcolorgroup [yellow] [vertical,name]\hss} +%D \stoplinecorrection +%D +%D These groups are used to define palets {\em alfa} upto {\em +%D zeta}. As long as we don't use colors from the same row, we +%D get ourselves distinctive palets. By activating such a palet +%D one gains access to its members {\em top} to {\em charm} (of +%D course one should use more suitable names than these). +%D +%D \startlinecorrection +%D \hbox to \hsize +%D {\showpalet [alfa] [vertical,name,number]\hss +%D \showpalet [beta] [vertical,name]\hss +%D \showpalet [gamma] [vertical,name]\hss +%D \showpalet [delta] [vertical,name]\hss +%D \showpalet [epsilon] [vertical,name]\hss +%D \showpalet [zeta] [vertical,name]} +%D \stoplinecorrection +%D +%D By using the keyword \type {value} the individual color +%D components are shown too. When printed in color, these +%D showcases show both the colors and the gray value. + +%D \macros +%D {comparepalet} +%D +%D There are some more testing macros available: +%D +%D \startbuffer +%D \comparepalet [alfa] +%D \stopbuffer +%D +%D \typebuffer +%D +%D shows the palet colors against a background: +%D +%D \startlinecorrection +%D \getbuffer +%D \stoplinecorrection +%D +%D The formal definition is: +%D +%D \showsetup{comparepalet} + +\fetchruntimecommand \comparepalet {\f!colorprefix\s!run} + +%D \macros +%D {comparecolorgroup} +%D +%D The similar command: +%D +%D \startbuffer +%D \comparecolorgroup [blue] +%D \stopbuffer +%D +%D \typebuffer +%D +%D shows color groups: +%D +%D \startlinecorrection +%D \getbuffer +%D \stoplinecorrection +%D +%D this commands are defined as: +%D +%D \showsetup{comparecolorgroup} + +\fetchruntimecommand \comparecolorgroup {\f!colorprefix\s!run} + +%D \macros +%D {showcolor} +%D +%D But let's not forget that we also have the more traditional +%D non||related colors. These show up after: +%D +%D \starttyping +%D \showcolor [name] +%D \stoptyping +%D +%D Where \type{name} for instance can be \type{rgb}. +%D +%D \showsetup{showcolor} + +\fetchruntimecommand \showcolor {\f!colorprefix\s!run} + +%D It would make sense to put the following code in \type +%D {colo-mps}, but it it rather low level. + +%D \macros +%D {negatecolorcomponent,negatedcolorcomponent} +%D +%D These speak for themselves. See \type {colo-ext} for usage. + +\def\negatecolorcomponent#1% #1 = \macro + {\scratchdimen\onepoint\advance\scratchdimen-#1\onepoint + \ifdim\scratchdimen<\zeropoint\scratchdimen\zeropoint\fi + \edef#1{\withoutpt\the\scratchdimen}} + +\let\negatedcolorcomponent\firstofoneargument + +\def\negatedcolorcomponent#1% + {\ifdim\dimexpr\onepoint-#1\onepoint\relax<\zeropoint + \!!zerocount + \else + \expandafter\withoutpt\the\dimexpr\onepoint-#1\onepoint\relax + \fi} + +\def\negatecolorcomponent#1% #1 = \macro + {\edef#1{\negatedcolorcomponent{#1}}} + +%D \macros +%D {ifMPgraphics, ifMPcmykcolors, MPcolor} +%D +%D A very special macro is \type{\MPcolor}. This one can be +%D used to pass a \CONTEXT\ color to \METAPOST. +%D +%D \starttyping +%D \MPcolor{my own red} +%D \stoptyping +%D +%D This macro returns a \METAPOST\ triplet \type{(R,G,B)}. +%D Unless \CMYK\ color support is turned on with \type +%D {MPcmyk}, only \cap{RGB} colors and gray scales are +%D supported. + +\newif\ifMPcmykcolors % \MPcmykcolorsfalse +\newif\ifMPspotcolors % \MPspotcolorsfalse + +\ifx\MPcolor\undefined + \def\MPcolor#1{(0,0,0)} +\fi + +%D \macros +%D {PDFcolor,FDFcolor} +%D +%D Similar alternatives are avaliable for \PDF: + +%D For the moment we keep the next downward compatibility +%D switch, i.e.\ expanded colors. However, predefined colors +%D and palets are no longer expanded (which is what I wanted +%D in the first place). +%D +%D Well, in case we want to do color separation and use CMYK +%D colors only, this is dangerous since unwanted remapping may +%D take place. Especially when we redefine already defined +%D colors in another color space (e.g. darkgreen is +%D predefined in RGB color space, so a redefinition in CMYK +%D coordinates before RGB mode is disabled, would give +%D unexpected results due to the already frozen color spec.) +%D +%D So, from now on, colors are not frozen any more! + +\chardef\currentcolorchannel=0 + +\newif\iffilterspotcolor \filterspotcolorfalse +\newif\ifdoingspotcolor \doingspotcolorfalse + +\def\registercolorchannel#1% + {\ifdoingspotcolor \else + \global\expandafter\chardef\csname\??cs#1\endcsname\zerocount + \fi} + +\newif\ifhidesplitcolor \hidesplitcolortrue + +%D The next macro is for instance used in figure splitting: + +\def\doifseparatingcolorselse + {\iffilterspotcolor + \@EA\firstoftwoarguments + \else\ifcase\currentcolorchannel + \@EAEAEA\secondoftwoarguments + \else + \@EAEAEA\firstoftwoarguments + \fi\fi} + +\def\doifcolorchannelelse#1% + {\doifseparatingcolorselse + {\doifelsenothing{#1} + \secondoftwoarguments + {\doifelse{#1}\@@clsplit + \firstoftwoarguments + \secondoftwoarguments}} + \secondoftwoarguments} + +\def\resetcolorseparation + {\filterspotcolorfalse + \chardef\currentcolorchannel\zerocount} + +%D These can be used in selecting specific files (like +%D figuredatabases). + +% we already have: +% +% \def\colorsplitsuffix{\ifcase\currentcolorchannel\else-\@@clsplitsen\fi} +% \def\colorsplitprefix{\ifcase\currentcolorchannel\else\@@clsplitsen-\fi} + +\def\colorchannelprefix{\doifseparatingcolorselse\@@clsplit\empty-} +\def\colorchannelsuffix{-\doifseparatingcolorselse\@@clsplit\empty} + +%D We now define the low level macros: + \chardef\colorversion=2 % todo: palets in definecolor @@ -37,8 +903,6 @@ % draw btex test etex withprescript \mptexcolor{blue} ; % \stopMPpage -\registerctxluafile{colo-ini}{1.000} - \ifx\currentcolormodel\undefined \newcount\currentcolormodel \fi \def\setcolormodel#1% @@ -79,28 +943,9 @@ % Since we couple definitions, we could stick to one test. Todo. Same for mpcolor. -% \def\doactivatecolor#1% : in currentpalet, maybe not, ugly -% {\ifcsname(cs:\currentpalet#1)\endcsname -% \csname(cs:\currentpalet#1)\endcsname -% \csname(ts:\currentpalet#1)\endcsname -% \else -% \csname(cs:#1)\endcsname -% \csname(ts:#1)\endcsname -% \fi} - -% \def\doactivatecolor#1% : in currentpalet, maybe not, ugly -% {\csname(cs:\ifcsname(cs:\currentpalet#1)\endcsname\currentpalet\fi#1)\endcsname} -% \csname(ts:\ifcsname(ts:\currentpalet#1)\endcsname\currentpalet\fi#1)\endcsname} -% -% more robust test, else we get \relaxed non-colors which may confuse e.g. mpcolor - \letvalue{(cs:-}\empty \letvalue{(ts:-}\empty -% \def\doactivatecolor#1% : in currentpalet, maybe not, ugly -% {\csname(cs:\ifcsname(cs:\currentpalet#1)\endcsname\currentpalet#1\else\ifcsname(cs:#1)\endcsname#1\else-\fi\fi)\endcsname -% \csname(ts:\ifcsname(ts:\currentpalet#1)\endcsname\currentpalet#1\else\ifcsname(ts:#1)\endcsname#1\else-\fi\fi)\endcsname} - \def\doactivatecolor#1% : in currentpalet, maybe not, ugly {\ifcsname(cs:\currentpalet#1)\endcsname \csname(cs:\currentpalet#1)\endcsname @@ -131,29 +976,27 @@ \def\dodefinecolorcommand#1#2% {\unexpanded#1{#2}{\doactivatecolor{#2}}} -% todo: \allspotcolors - -\def\colorlist % not really used, only for colo-run - {\ctxlua{tex.sprint(table.concat(table.sortedkeys(attributes.list[attributes.numbers.color]),","))}} +\let\colorlist\empty % not really used, only for colo-run +\setfalse\collectcolorsinlist +\def\collectcolorinlist#1{\doglobal\addtocommalist{#1}\colorlist} \def\dodefinecolor[#1][#2]% - {%\addtocommalist{#1}\colorlist + {\ifconditional\collectcolorsinlist\collectcolorinlist{#1}\fi \ctxlua{ctx.defineprocesscolor("#1","#2",false,\iffreezecolors true\else false\fi)}% \dodefinecolorcommand\setvalue{#1}} \def\dodefineglobalcolor[#1][#2]% - {%\doglobal\addtocommalist{#1}\colorlist + {\ifconditional\collectcolorsinlist\collectcolorinlist{#1}\fi \ctxlua{ctx.defineprocesscolor("#1","#2",true,\iffreezecolors true\else false\fi)}% \dodefinecolorcommand\setgvalue{#1}} \def\dodefinenamedcolor[#1][#2]% - {%\doglobal\addtocommalist{#1}\colorlist + {\ifconditional\collectcolorsinlist\collectcolorinlist{#1}\fi \ctxlua{ctx.defineprocesscolor("#1","#2",false,\iffreezecolors true\else false\fi)}% \dodefinecolorcommand\setvalue{#1}} \def\dodefinespotcolor[#1][#2][#3]% - {%\doglobal\addtocommalist{#1}\colorlist % optional - \doglobal\addtocommalist{#2}\allspotcolors + {\ifconditional\collectcolorsinlist\collectcolorinlist{#1}\fi \ctxlua{ctx.definespotcolor("#1","#2","#3",true)}% \dodefinecolorcommand\setxvalue{#1}} @@ -293,7 +1136,7 @@ \appendtoks \initializemaintextcolor \to \everyjob -\def\localstarttextcolor{\expanded{\startcolor[\ifx\maintextcolor\empty\defaulttextcolor\else\maintextcolor\fi]}} +\def\localstarttextcolor{\normalexpanded{\noexpand\startcolor[\ifx\maintextcolor\empty\defaulttextcolor\else\maintextcolor\fi]}} \let\localstoptextcolor \stopcolor \let\restoretextcolor \firstofoneargument @@ -302,11 +1145,14 @@ {\definecolor[\??pa#1:#2][#3]% \iffreezecolors\@EA\setevalue\else\@EA\setvalue\fi{(cs:#1:#2)}{\csname(cs:\??pa#1:#2)\endcsname}% \iffreezecolors\@EA\setevalue\else\@EA\setvalue\fi{(ca:#1:#2)}{\csname(ca:\??pa#1:#2)\endcsname}} - {\doifdefinedelse{(cs:#3)}% \definepalet[test][xx=green] - {\iffreezecolors\@EA\setevalue\else\@EA\setvalue\fi{(cs:#1:#2)}{\csname(cs:#3)\endcsname}% - \iffreezecolors\@EA\setevalue\else\@EA\setvalue\fi{(ca:#1:#2)}{\csname(ca:#3)\endcsname}} - {\letvalue{(cs:#1:#2)}\undefined - \letvalue{(ca:#1:#2)}\undefined}}} + {\ifcsname(cs:#3)\endcsname % \definepalet[test][xx=green] + \iffreezecolors\@EA\setevalue\else\@EA\setvalue\fi{(cs:#1:#2)}{\csname(cs:#3)\endcsname}% + \iffreezecolors\@EA\setevalue\else\@EA\setvalue\fi{(ca:#1:#2)}{\csname(ca:#3)\endcsname}% + \else + % not entered when making format + \localundefine{(cs:#1:#2)}% \letvalue{(cs:#1:#2)}\undefined + \localundefine{(ca:#1:#2)}% \letvalue{(ca:#1:#2)}\undefined + \fi}} \setvalue{(cs:)}{} \setvalue{(ca:)}{0} \setvalue{(ts:)}{} \setvalue{(ta:)}{0} @@ -352,7 +1198,25 @@ \presetPDFtransparency{#2}{#3}% \fi} -\protect \endinput +%D \macros +%D {forcecolorhack} +%D +%D We can out this in front of (for instance) a special and so force color +%D to be applied (only glyphs, rules and leaders are handled). +%D +%D \startbuffer +%D \framed +%D [background=color,backgroundcolor=yellow,framecolor=red,corner=round] +%D {test} +%D \stopbuffer +%D +%D \typebuffer \getbuffer + +% ignores in attribute handler +% +% \def\forcecolorhack{\vrule\!!width\zeropoint\!!height\zeropoint\!!depth\zeropoint} + +\def\forcecolorhack{\leaders\hrule\hskip\zeropoint} % \setupcolors[state=start] % @@ -368,3 +1232,48 @@ % \ctxlua{tex.print(ctx.aux.colorattribute("green"))} % \ctxlua{tex.print(ctx.aux.colorattribute("black"))} % \stoptext + +%D We default to the colors defined in \module{colo-rgb} and +%D support both \cap{RGB} and \cap{CMYK} output. As you can +%D see, color support is turned off by default. Reduction of +%D gray colors to gray scales is turned on. + +\definecolor[black][s=0] +\definecolor[white][s=1] + +\definetransparency [none] [0] +\definetransparency [normal] [1] +\definetransparency [multiply] [2] +\definetransparency [screen] [3] +\definetransparency [overlay] [4] +\definetransparency [softlight] [5] +\definetransparency [hardlight] [6] +\definetransparency [colordodge] [7] +\definetransparency [colorburn] [8] +\definetransparency [darken] [9] +\definetransparency [lighten] [10] +\definetransparency [difference] [11] +\definetransparency [exclusion] [12] + +\appendtoks + \setupcolors[\c!state=\v!start]% later direct +\to \everyjob + +\setupcolors + [\c!state=\v!stop, % in mkii: \v!stop + \c!conversion=\v!yes, + \c!reduction=\v!no, + \c!rgb=\v!yes, + \c!cmyk=\v!yes, + \c!spot=\v!yes, + \c!mp\c!cmyk=\@@clcmyk, + \c!mp\c!spot=\@@clspot, + \c!expansion=\v!no, + \c!textcolor=, + \c!split=\v!no, + \c!criterium=\v!all] + +\setupcolor + [\v!rgb] + +\protect \endinput diff --git a/tex/context/base/colo-ini.tex b/tex/context/base/colo-ini.tex deleted file mode 100644 index 0136596a5..000000000 --- a/tex/context/base/colo-ini.tex +++ /dev/null @@ -1,1051 +0,0 @@ -%D \module -%D [ file=colo-ini, -%D version=2007.08.08, -%D title=\CONTEXT\ Color Macros, -%D subtitle=Initialization, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -%D We need to clean this up further but first we hav eto make sure that mkiv -%D code works ok. - -\writestatus{loading}{Context Color Macros / initialization} - -%D This module implements color. Since \MKII\ and \MKIV\ use a completely -%D different approach, this module only implements a few generic mechanisms. - -\startmessages dutch library: colors - title: kleur - 1: systeem -- is globaal actief - 2: systeem -- is lokaal actief - 3: -- is niet gedefinieerd -- - 4: systeem -- wordt geladen - 5: onbekend systeem -- - 6: palet -- is beschikbaar - 7: palet -- is niet beschikbaar - 8: specificatie -- bij -- wordt zwart - 9: -- kleurruimte wordt niet ondersteund - 10: -- kleurruimte wordt ondersteund - 11: kleur wordt vertaald in grijs - 12: -- is geregistreerd -\stopmessages - -\startmessages english library: colors - title: color - 1: system -- is global activated - 2: system -- is local activated - 3: -- is not defined -- - 4: system -- is loaded - 5: unknown system -- - 6: palette -- is available - 7: palette -- is not available - 8: specification -- at color -- becomes black - 9: -- color space is not supported - 10: -- color space is supported - 11: color is converted to gray - 12: -- is registered -\stopmessages - -\startmessages german library: colors - title: farbe - 1: system -- ist global aktiviert - 2: system -- ist lokal aktiviert - 3: -- ist undefiniert -- - 4: system -- ist geladen - 5: unbekanntes System -- - 6: palette -- ist verfuegbar - 7: palette -- ist nicht verfuegbar - 8: Spezifikation -- bei Farbe -- wird schwarz - 9: -- Farbraum wird nicht unterstuetzt - 10: -- Farbraum wird unterstuetzt - 11: Farbe wird in Grau umgewandelt - 12: -- is registered -\stopmessages - -\startmessages czech library: colors - title: barva - 1: system -- je globalne aktivovana - 2: system -- je lokalne activovana - 3: -- neni definovana -- - 4: system -- je nacten - 5: neznamy system -- - 6: palette -- je k dispozici - 7: palette -- neni k dispozici - 8: specifikace -- v barve -- bude cerna - 9: -- prostor barev neni podporovan - 10: -- prostor barev je podporovan - 11: barva je prevedena na sed - 12: -- is registered -\stopmessages - -\startmessages italian library: colors - title: colore - 1: sistema -- attivato globalmente - 2: sistema -- attivato localmente - 3: -- non definito -- - 4: sistema -- caricato - 5: sistema -- sconosciuto - 6: tavolozza -- resa disponibile - 7: tavolozza -- non disponibile - 8: specifica -- del colore -- convertita in nero - 9: spazio dei colori -- non supportato - 10: spazio dei colori -- supportato - 11: il colore ø convertito in grigio - 12: -- is registered -\stopmessages - -\startmessages norwegian library: colors - title: farge - 1: system -- er aktivert globalt - 2: system -- er aktivert lokalt - 3: -- er udefinert -- - 4: system -- er lest inn - 5: ukjent system -- - 6: palett -- er tilgjengelig - 7: palett -- er ikke tilgjengelig - 8: spesifikasjon -- for farge -- gir kun svart - 9: -- fargerom er ikke støttet - 10: -- fargerom er støttet - 11: fargen vil bli vist som grø - 12: -- is registered -\stopmessages - -\startmessages romanian library: colors - title: culori - 1: sistem -- este activata global - 2: sistem -- este activata local - 3: -- nu este definita -- - 4: sistem -- este incarcata - 5: sistem -- necunoscuta - 6: paleta -- este disponibila - 7: palette -- nu este disponibila - 8: specificatia -- la culoarea -- devine neagra - 9: spatiul de culoare -- nu este suportat - 10: spatiul de culoare -- este suportat - 11: culoarea este convertita la gri - 12: -- is registered -\stopmessages - -\startmessages french library: colors - title: couleurs - 1: le système -- est globalement activé - 2: le système -- est localement activé - 3: -- n'est pas défini -- - 4: le système -- est chargé - 5: système -- inconnu - 6: la palette -- est disponible - 7: le palette -- n'est pas disponible - 8: la spécification -- de la couleur -- devient noire - 9: l'espace de couleur -- n'est pas supporté - 10: -- l'espace de couleur est supporté - 11: la couleur est convertie en niveau de gris - 12: -- est enregistré -\stopmessages - -\unprotect - -\chardef\colorversion=1 % temp, needed for tracing purposes, mkiv transition - -%D We use a couple of local registers. That way we don't have -%D to group when converting colors. By the way, this is not -%D really faster. We can sqeeze half a second runtime for 50K -%D switches on a 1G machine, but the macros will become rather -%D ugly then. To mention one such improvement: no colon -%D after the key character (.25 sec). - -\newdimen\colordimen -\newcount\colorcount - -%D When typesetting for paper, we prefer using the \cap{CMYK} -%D color space, but for on||screen viewing we prefer \cap{RGB} -%D (the previous implementation supported only this scheme). -%D Independant of such specifications, we support some automatic -%D conversions: -%D -%D \startitemize[packed] -%D \item convert all colors to \cap{RGB} -%D \item convert all colors to \cap{CMYK} -%D \item convert all colors to gray scales -%D \stopitemize -%D -%D We also support optimization of colors to gray scales. -%D -%D \startitemize[continue] -%D \item reduce gray colors to gray scales -%D \item reduce \cap{CMY} components to \cap{K} -%D \stopitemize -%D -%D These options are communicated by means of: - -\newif\ifRGBsupported -\newif\ifCMYKsupported -\newif\ifSPOTsupported -\newif\ifpreferGRAY -\newif\ifGRAYprefered -\newif\ifreduceCMYK -\newif\ifconverttoGRAY -\newif\ifweightGRAY \weightGRAYtrue - -\newif\ifconvertMPcolors -\newif\ifreduceMPcolors -\newif\ifforcegrayMPcolors - -%D The last boolean controls reduction of \cap{CMYK} to -%D \cap{CMY} colors. When set to true, the black component -%D is added to the other ones. -%D -%D Prefering gray is not the same as converting to gray. -%D Conversion treats each color components in a different way, -%D while prefering is just a reduction and thus a -%D space||saving option. - -\newif\iffreezecolors \freezecolorsfalse -\newif\ifincolor % true if colors enabled -\newif\iflocalcolor - -\let\colorlist \empty -\let\currentspotcolor \empty -\let\allspotcolors \empty -\let\usedspotcolors \empty -\let\usedcolorchannels\empty -\let\currentpalet \empty - -%D \macros -%D {definecolor,defineglobalcolor,definenamedcolor,definespotcolor,definemultitonecolor} -%D -%D \startbuffer -%D \definecolor [blue] [c=1,m=.38,y=0,k=.64] % pantone pms 2965 uncoated m -%D \definecolor [yellow] [c=0,m=.28,y=1,k=.06] % pantone pms 124 uncoated m -%D -%D \definespotcolor [blue-100] [blue] [p=1] -%D \definespotcolor [yellow-100] [yellow] [p=1] -%D -%D \definemultitonecolor [pdftoolscolor] [blue=.12,yellow=.28] [c=.1,m=.1,y=.3,k=.1] -%D -%D \useexternalfigure[demofig][mill.png][object=no] -%D -%D \startcombination[4*1] -%D {\externalfigure[demofig]} {no color} -%D {\externalfigure[demofig][color=pdftoolscolor]} {indexed duotone} -%D {\externalfigure[demofig][color=blue-100]} {spot color} -%D {\externalfigure[demofig][color=yellow-100]} {spot color} -%D \stopcombination -%D \stopbuffer -%D -%D \getbuffer \typebuffer - -\def\definecolor {\dodoubleargument\dodefinecolor} -\def\defineglobalcolor {\dodoubleargument\dodefineglobalcolor} -\def\definenamedcolor {\dodoubleargument\dodefinenamedcolor} -\def\definespotcolor {\dotripleargument\dodefinespotcolor} -\def\definemultitonecolor{\doquadrupleempty\dodefinemultitonecolor} - -% check: registerusedspotcolors -% check: registerusedcolorchannels - -%D \macros -%D {doifcolorelse, doifcolor} -%D -%D Switching to a color is done by means of the following -%D command. Later on we will explain the use of palets. We -%D define ourselves a color conditional first. - -\ifx\doifcolorelse\undefined - \let\doifcolorelse\secondoftwoarguments - \let\doifcolor \gobbleoneargument -\fi - -%D \macros -%D {localstartcolor,localstopcolor} -%D -%D Simple color support, that is without nesting, is provided -%D by: - -\ifx\localstartcolor\undefined - \let\localstartcolor\undefined - \let\localstopcolor \undefined -\fi - -%D \macros -%D {faststartcolor,faststopcolor} -%D -%D No checking for arguments and such: - -\ifx\faststartcolor\undefined - \def\faststartcolor[#1]{} - \def\faststopcolor {} -\fi - -%D These local ones may go away in future versions. - -%D \macros -%D {startcolor,stopcolor} -%D -%D The more save method, the one that saves the current color -%D state and returns to this state afterward, is activated by: -%D -%D \showsetup{startcolor} - -\ifx\startcolor\undefined - \let\startcolor\undefined - \let\stopcolor \undefined -\fi - -%D \macros -%D {startcurrentcolor,stopcurrentcolor} - -\def\startcurrentcolor{\startcolor[\outercolorname]} -\def\stopcurrentcolor {\stopcolor} - -%D \macros -%D {color,graycolor} -%D -%D This leaves the simple color command: -%D -%D \showsetup{color} -%D \showsetup{graycolor} - -\ifx\color\undefined - \def\color [#1]{} - \def\graycolor[#1]{} - \def\gray {\graycolor} -\fi - -%D \macros -%D {localstartraster,localstopraster, -%D startraster,stopraster,raster} -%D -%D The previous conversions are not linear and treat each color -%D component according to human perception curves. Pure gray -%D (we call them rasters) has equal color components. In -%D \CONTEXT\ rasters are only used as backgrounds and these -%D don't cross page boundaries in the way color does. Therefore -%D we don't need stacks and marks. Just to be compatible with -%D color support we offer both 'global' and 'local' commands. - -\ifx\startraster\undefined - \def\startraster [#1]{} - \def\stopraster {} - \def\raster [#1]{} - \def\localstartraster[#1]{} - \def\localstopraster {} -\fi - -%D \macros -%D {colorvalue, grayvalue} -%D -%D We can typeset the color components using \type{\colorvalue} and -%D \type{\grayvalue}. The commands: -%D -%D \startbuffer -%D color value of SomeKindOfRed: \colorvalue{SomeKindOfRed} \crlf -%D gray value of SomeKindOfRed: \grayvalue{SomeKindOfRed} -%D \stopbuffer -%D -%D \typebuffer -%D -%D show us: -%D -%D \startvoorbeeld -%D \getbuffer -%D \stopvoorbeeld - -\def\colorformatseparator{ } - -\ifx\colorvalue\undefined - \let\colorvalue\gobbleoneargument - \let\grayvalue \gobbleoneargument -\fi - -% check: \currentcolorname -% check: \outercolorname - -%D \macros -%D {setupcolor} -%D -%D Color definitions can be grouped in files with the name: -%D -%D \starttyping -%D \f!colorprefix-identifier.tex -%D \stoptyping -%D -%D where \type{\f!colorprefix} is \unprotect {\tttf \f!colorprefix}. -%D Loading such a file is done by \protect -%D -%D \showsetup{setupcolor} -%D -%D Some default colors are specified in \type{colo-rgb.tex}, -%D which is loaded into the format by: -%D -%D \starttyping -%D \setupcolor[rgb] -%D \stoptyping - -\let\colorstyle\empty - -\def\setupcolor - {\dosingleargument\dosetupcolor} - -\def\dosetupcolor[#1]% - {\doifnot{#1}\colorstyle - {\def\colorstyle{#1}% - \processcommalist[#1]\dodosetupcolor}} - -\def\dodosetupcolor#1% - {\makeshortfilename[\truefilename{\f!colorprefix#1}]% - \startreadingfile - \readsysfile\shortfilename - {\showmessage\m!colors4\colorstyle} - {\showmessage\m!colors5\colorstyle}% - \stopreadingfile} - -\let\usecolors\setupcolor - -% check: \chardef\currentcolorchannel=0 -% check: \startcolormode -% check: \newif\iffilterspotcolor \filterspotcolorfalse -% check: \newif\ifdoingspotcolor \doingspotcolorfalse -% check: \registercolorchannel - -%D \macros -%D {definetransparency} -%D -%D This command numbers to names: - -\def\definetransparency - {\dodoubleargument\dodefinetransparency} - -\def\setupcolors - {\dosingleargument\dosetupcolors} - -\def\resetcolorsplitting - {\chardef\currentcolorchannel\zerocount - \let\currentspotcolor\empty - \filterspotcolorfalse} - -\def\colorsplitsuffix{\ifcase\currentcolorchannel\else-\@@clsplit\fi} -\def\colorsplitprefix{\ifcase\currentcolorchannel\else\@@clsplit-\fi} - -\def\setcolorsplitting - {\resetsystemmode{\v!color\colorsplitsuffix}% - \resetcolorsplitting - \processaction - [\@@clsplit] - [ c=>\chardef\currentcolorchannel1,% - m=>\chardef\currentcolorchannel2,% - y=>\chardef\currentcolorchannel3,% - k=>\chardef\currentcolorchannel4,% - r=>\chardef\currentcolorchannel5,% - g=>\chardef\currentcolorchannel6,% - b=>\chardef\currentcolorchannel7,% - s=>\chardef\currentcolorchannel8,% - \v!no=>,% \currentcolorchannel0,% all colors - \s!default=>,% \currentcolorchannel0,% all colors - \s!unknown=>\filterspotcolortrue - \edef\currentspotcolor{\commalistelement}]% - \setsystemmode{\v!color\colorsplitsuffix}% - \iffilterspotcolor \let\@@clrgb\v!no \fi} - -\ifx\dosetupcolormodel\undefined - \let\dosetupcolormodel\relax -\fi - -\def\dosetupcolors[#1]% some no longer make sense in MkIV - {\getparameters[\??cl][#1]% - \doifelse\@@clspot\v!yes - \SPOTsupportedtrue - \SPOTsupportedfalse - \doifelsenothing\@@clsplit - \resetcolorsplitting - \setcolorsplitting - \doifelse\@@clreduction\v!yes - \reduceCMYKtrue - \reduceCMYKfalse - \doifelse\@@clexpansion\v!yes - \freezecolorstrue - \freezecolorsfalse - \doifelse\@@clcriterium\v!all - \hidesplitcolortrue - \hidesplitcolorfalse - \doifelse\@@clrgb\v!no - {\ifRGBsupported \showmessage\m!colors {9}\v!rgb\RGBsupportedfalse\fi} - {\ifRGBsupported\else\showmessage\m!colors{10}\v!rgb\RGBsupportedtrue \fi}% - \doifelse\@@clcmyk\v!no - {\ifCMYKsupported \showmessage\m!colors {9}\v!cmyk\CMYKsupportedfalse\fi} - {\ifCMYKsupported\else\showmessage\m!colors{10}\v!cmyk\CMYKsupportedtrue \fi}% - \doifelse\@@clmpcmyk\v!no - {\ifMPcmykcolors \showmessage\m!colors {9}{\v!mp\v!cmyk}\MPcmykcolorsfalse\fi} - {\ifMPcmykcolors\else\showmessage\m!colors{10}{\v!mp\v!cmyk}\MPcmykcolorstrue \fi}% - \doifelse\@@clmpspot\v!no - {\ifMPspotcolors \showmessage\m!colors {9}{\v!mp\v!spot}\MPspotcolorsfalse\fi} - {\ifMPspotcolors\else\showmessage\m!colors{10}{\v!mp\v!spot}\MPspotcolorstrue \fi}% - \preferGRAYfalse - \processaction - [\@@clconversion] - [ \v!yes=>\preferGRAYtrue, - \v!always=>\preferGRAYtrue\RGBsupportedfalse\CMYKsupportedfalse]% - \ifRGBsupported - \converttoGRAYfalse - \forcegrayMPcolorsfalse - \else\ifCMYKsupported - \converttoGRAYfalse - \forcegrayMPcolorsfalse - \convertMPcolorstrue - \ifreduceCMYK - \reduceMPcolorstrue - \fi - \else - \ifconverttoGRAY\else\showmessage\m!colors{11}\empty\fi - \converttoGRAYtrue - \forcegrayMPcolorstrue - \convertMPcolorsfalse - \reduceMPcolorsfalse - \fi\fi - \processaction - [\@@clstate] - [ \v!global=>\ifincolor\else\showmessage\m!colors1\colorstyle\fi - \incolortrue\localcolorfalse, - \v!local=>\ifincolor\else\showmessage\m!colors2\colorstyle\fi - \incolortrue\localcolortrue, - \v!start=>\ifincolor\else\showmessage\m!colors1\colorstyle\fi - \incolortrue\localcolorfalse - \let\@@clstate\v!global, - \v!stop=>\incolorfalse\localcolorfalse - \forcegrayMPcolorstrue]% - \dosetupcolormodel - \initializemaintextcolor} - -%D \macros -%D {startregistercolor,stopregistercolor,permitcolormode} -%D -%D If you only want to register a color, the switch \type -%D {\ifpermitcolormode} can be used. That way the nested -%D colors know where to go back to. - -\ifx\startregistercolor\undefined - \def\startregistercolor[#1]{} - \def\stopregistercolor {} -\fi - -%D We use these macros for implementing text colors -%D (actually, the first application was in foreground -%D colors). -%D -%D \starttyping -%D \starttextcolor[red] -%D \dorecurse{10}{\input tufte \color[green]{oeps} \par} -%D \stoptextcolor -%D \stoptyping -%D -%D This is more efficient than the alternative: -%D -%D \starttyping -%D \setupbackgrounds[text][foregroundcolor=red] -%D \startregistercolor[red] -%D \dorecurse{10}{\input tufte \color[green]{oeps} \par} -%D \stopregistercolor -%D \stoptyping - -\def\maintextcolor {} -\def\defaulttextcolor {black} -\def\@@themaintextcolor{themaintextcolor} - -\ifx\initializemaintextcolor\undefined - \def\starttextcolor [#1]{} - \def\stoptextcolor {} - \def\initializemaintextcolor {} -\fi - -\ifx\restoretextcolor\undefined % to be redone - \let\restoretextcolor \firstofoneargument - \let\localstarttextcolor\relax - \let\localstoptextcolor \relax -\fi - -%D In this documentation we will not go into too much details -%D on palets. Curious users can find more information on this -%D topic in \from[use of color]. -%D -%D At the moment we implemented color in \CONTEXT\ color -%D printing was not yet on the desktop. In spite of this lack our -%D graphics designer made colorfull illustrations. When printed -%D on a black and white printer, distinctive colors can come -%D out equally gray. We therefore decided to use only colors -%D that were distinctive in colors as well as in black and -%D white print. -%D -%D Although none of the graphic packages we used supported -%D logical colors and global color redefition, we build this -%D support into \CONTEXT. This enabled us to experiment and -%D also prepared us for the future. - -%D \macros -%D {definepalet} -%D -%D Colors are grouped in palets. The colors in such a palet can -%D have colorful names, but best is to use names that specify -%D their use, like {\em important} or {\em danger}. As a sort -%D of example \CONTEXT\ has some palets predefined, -%D like:\footnote{At the time I wrote the palet support, I was -%D reading 'A hort history of time' of S.~Hawkins, so that's -%D why we stuck to quarks.} -%D -%D \starttyping -%D \definepalet -%D [alfa] -%D [ top=rood:7, -%D bottom=groen:6, -%D up=blauw:5, -%D down=cyaan:4, -%D strange=magenta:3, -%D charm=geel:2] -%D \stoptyping -%D -%D It's formal definition is: -%D -%D \showsetup{definepalet} -%D -%D Visualized, such a palet looks like: -%D -%D \startbuffer[palet] -%D \showpalet [alfa] [horizontal,name,number,value] -%D \stopbuffer -%D -%D \startlinecorrection -%D \getbuffer[palet] -%D \stoplinecorrection -%D -%D This bar shows both the color and gray alternatives of the -%D palet components (not visible in black and white print). -%D -%D When needed, one can copy a palet by saying: -%D -%D \starttyping -%D \definepalet [TEXcolorpretty] [colorpretty] -%D \stoptyping -%D -%D This saves us some typing in for instance the modules that -%D deal with pretty verbatim typesetting. - -\def\definepalet - {\dodoubleargument\dodefinepalet} - -\def\dodefinepalet[#1][#2]% - {\doifassignmentelse{#2} - {%\showmessage\m!colors6{#1}% - \letvalue{\??pa#1}\empty - \setevalue{\??pa\??pa#1}{#2}% - \def\dodododefinepalet[##1=##2]% - {\doifvaluesomething{\??pa#1} - {\setevalue{\??pa#1}{\csname\??pa#1\endcsname,}}% - \setevalue{\??pa#1}{\csname\??pa#1\endcsname##1}% - \dodefinepaletcolor{#1}{##1}{##2}}% - \def\dododefinepalet##1% - {\dodododefinepalet[##1]}% - \processcommalist[#2]\dododefinepalet} - {\doifdefined{\??pa#2} - {\expanded{\dodefinepalet[#1][\csname\??pa\??pa#2\endcsname]}}}} - -\ifx\dodefinepaletcolor\undefined - \let\dodefinepaletcolor\gobblethreearguments -\fi - -\let\paletsize\!!zerocount - -\def\getpaletsize[#1]% - {\getcommacommandsize[\csname\??pa\??pa#1\endcsname]% - \edef\paletsize{\number\commalistsize}} - -%D Instead of refering to colors, one can also directly specify -%D a color: -%D -%D \starttyping -%D \definepalet[test][xx=green] -%D \definepalet[test][xx={y=.4}] -%D \stoptyping - -%D \macros -%D {setuppalet} -%D -%D Colors are taken from the current palet, if defined. -%D Setting the current palet is done by: -%D -%D \showsetup{setuppalet} - -\let\currentpalet\empty - -\def\setuppalet - {\dosingleempty\dosetuppalet} - -\def\dosetuppalet[#1]% - {\edef\currentpalet{#1}% - \ifx\currentpalet\empty - % seems to be a reset - \else\ifcsname\??pa\currentpalet\endcsname - \edef\currentpalet{#1:}% - \else - \showmessage\m!colors7\currentpalet - \let\currentpalet\empty - \fi\fi} - -%D \macros -%D {showpalet} -%D -%D The previous visualization was typeset with: -%D -%D \typebuffer[palet] -%D -%D This commands is defined as: -%D -%D \showsetup{showpalet} - -\fetchruntimecommand \showpalet {\f!colorprefix\s!run} - -%D \macros -%D {showcolorcomponents} -%D -%D \starttyping -%D \showcolorcomponents[color-1,color-2] -%D \stoptyping - -\fetchruntimecommand \showcolorcomponents {\f!colorprefix\s!run} - -%D \macros -%D {definecolorgroup} -%D -%D The naming of the colors in this palet suggests some -%D ordening, which in turn is suported by color grouping. -%D -%D \starttyping -%D \definecolorgroup -%D [red] -%D [1.00:0.90:0.90, -%D 1.00:0.80:0.80, -%D 1.00:0.70:0.70, -%D 1.00:0.55:0.55, -%D 1.00:0.40:0.40, -%D 1.00:0.25:0.25, -%D 1.00:0.15:0.15, -%D 0.90:0.00:0.00] -%D \stoptyping -%D -%D In such a color group colors are numbered from~$1$ to~$n$. -%D -%D \showsetup{definecolorgroup} -%D -%D This kind of specification is not only more compact than -%D defining each color separate, it also loads faster and takes -%D less bytes. - -\def\definecolorgroup - {\dotripleempty\dodefinecolorgroup} - -\def\dododefinecolorgroupgray [#1][#2:#3]{\definecolor [#1:\the\colorcount][s=#2]} -\def\dododefinecolorgrouprgb [#1][#2:#3:#4:#5]{\definecolor [#1:\the\colorcount][r=#2,g=#3,b=#4]} -\def\dododefinecolorgroupcmyk[#1][#2:#3:#4:#5:#6]{\definecolor [#1:\the\colorcount][c=#2,m=#3=,y=#4,k=#5]} -\def\dododefinecolorgroupspot [#1][#2:#3:#4]{\definespotolor[#1:\the\colorcount][#2][p=#3]} - -\def\dododefinecolorgroup#1#2% - {\advance\colorcount\plusone - \getvalue{dododefinecolorgroup\currentcolorspace}[#1][#2:0:0:0:0]} - -\def\dodefinecolorgroup[#1][#2][#3]% obsolete, just use palets - {\ifthirdargument - \doifelsenothing{#2}{\let\currentcolorspace\v!rgb}{\def\currentcolorspace{#2}}% - \colorcount\zerocount - \processcommalist[#3]{\dododefinecolorgroup{#1}}% - \else - \doifinstringelse{:}{#2} - {\definecolorgroup[#1][\v!rgb][#2]} - {\doloop - {\doifdefinedelse{\??cr#2:\recurselevel} - {\setevalue{\??cr#1:\recurselevel}{\csname\??cr#2:\recurselevel\endcsname}} - {\exitloop}}}% - \fi} - -%D \macros -%D {showcolorgroup} -%D -%D We can show the group by: -%D -%D \startbuffer -%D \showcolorgroup [blue] [horizontal,name,number,value] -%D \stopbuffer -%D -%D \typebuffer -%D -%D or in color: -%D -%D \startlinecorrection -%D \getbuffer -%D \stoplinecorrection -%D -%D which uses: -%D -%D \showsetup{showcolorgroup} - -\fetchruntimecommand \showcolorgroup {\f!colorprefix\s!run} - -%D There are ten predefined color groups, like -%D \color[green]{\em groen}, \color[red]{\em rood}, -%D \color[blue]{\em blauw}, \color[cyan]{\em cyaan}, -%D \color[magenta]{\em magenta} and \color[yellow]{\em geel}. -%D -%D \startlinecorrection -%D \hbox to \hsize -%D {\hss -%D \showcolorgroup [red] [vertical,name,number]\hss -%D \showcolorgroup [green] [vertical,name]\hss -%D \showcolorgroup [blue] [vertical,name]\hss -%D \showcolorgroup [cyan] [vertical,name]\hss -%D \showcolorgroup [magenta][vertical,name]\hss -%D \showcolorgroup [yellow] [vertical,name]\hss} -%D \stoplinecorrection -%D -%D These groups are used to define palets {\em alfa} upto {\em -%D zeta}. As long as we don't use colors from the same row, we -%D get ourselves distinctive palets. By activating such a palet -%D one gains access to its members {\em top} to {\em charm} (of -%D course one should use more suitable names than these). -%D -%D \startlinecorrection -%D \hbox to \hsize -%D {\showpalet [alfa] [vertical,name,number]\hss -%D \showpalet [beta] [vertical,name]\hss -%D \showpalet [gamma] [vertical,name]\hss -%D \showpalet [delta] [vertical,name]\hss -%D \showpalet [epsilon] [vertical,name]\hss -%D \showpalet [zeta] [vertical,name]} -%D \stoplinecorrection -%D -%D By using the keyword \type {value} the individual color -%D components are shown too. When printed in color, these -%D showcases show both the colors and the gray value. - -%D \macros -%D {comparepalet} -%D -%D There are some more testing macros available: -%D -%D \startbuffer -%D \comparepalet [alfa] -%D \stopbuffer -%D -%D \typebuffer -%D -%D shows the palet colors against a background: -%D -%D \startlinecorrection -%D \getbuffer -%D \stoplinecorrection -%D -%D The formal definition is: -%D -%D \showsetup{comparepalet} - -\fetchruntimecommand \comparepalet {\f!colorprefix\s!run} - -%D \macros -%D {comparecolorgroup} -%D -%D The similar command: -%D -%D \startbuffer -%D \comparecolorgroup [blue] -%D \stopbuffer -%D -%D \typebuffer -%D -%D shows color groups: -%D -%D \startlinecorrection -%D \getbuffer -%D \stoplinecorrection -%D -%D this commands are defined as: -%D -%D \showsetup{comparecolorgroup} - -\fetchruntimecommand \comparecolorgroup {\f!colorprefix\s!run} - -%D \macros -%D {showcolor} -%D -%D But let's not forget that we also have the more traditional -%D non||related colors. These show up after: -%D -%D \starttyping -%D \showcolor [name] -%D \stoptyping -%D -%D Where \type{name} for instance can be \type{rgb}. -%D -%D \showsetup{showcolor} - -\fetchruntimecommand \showcolor {\f!colorprefix\s!run} - -%D It would make sense to put the following code in \type -%D {colo-mps}, but it it rather low level. - -%D \macros -%D {negatecolorcomponent,negatedcolorcomponent} -%D -%D These speak for themselves. See \type {colo-ext} for usage. - -\def\negatecolorcomponent#1% #1 = \macro - {\scratchdimen\onepoint\advance\scratchdimen-#1\onepoint - \ifdim\scratchdimen<\zeropoint\scratchdimen\zeropoint\fi - \edef#1{\withoutpt\the\scratchdimen}} - -\let\negatedcolorcomponent\firstofoneargument - -\def\negatedcolorcomponent#1% - {\ifdim\dimexpr\onepoint-#1\onepoint\relax<\zeropoint - \!!zerocount - \else - \expandafter\withoutpt\the\dimexpr\onepoint-#1\onepoint\relax - \fi} - -\def\negatecolorcomponent#1% #1 = \macro - {\edef#1{\negatedcolorcomponent{#1}}} - -%D \macros -%D {ifMPgraphics, ifMPcmykcolors, MPcolor} -%D -%D A very special macro is \type{\MPcolor}. This one can be -%D used to pass a \CONTEXT\ color to \METAPOST. -%D -%D \starttyping -%D \MPcolor{my own red} -%D \stoptyping -%D -%D This macro returns a \METAPOST\ triplet \type{(R,G,B)}. -%D Unless \CMYK\ color support is turned on with \type -%D {MPcmyk}, only \cap{RGB} colors and gray scales are -%D supported. - -\newif\ifMPcmykcolors % \MPcmykcolorsfalse -\newif\ifMPspotcolors % \MPspotcolorsfalse - -\ifx\MPcolor\undefined - \def\MPcolor#1{(0,0,0)} -\fi - -%D \macros -%D {PDFcolor,FDFcolor} -%D -%D Similar alternatives are avaliable for \PDF: - -%D For the moment we keep the next downward compatibility -%D switch, i.e.\ expanded colors. However, predefined colors -%D and palets are no longer expanded (which is what I wanted -%D in the first place). -%D -%D Well, in case we want to do color separation and use CMYK -%D colors only, this is dangerous since unwanted remapping may -%D take place. Especially when we redefine already defined -%D colors in another color space (e.g. darkgreen is -%D predefined in RGB color space, so a redefinition in CMYK -%D coordinates before RGB mode is disabled, would give -%D unexpected results due to the already frozen color spec.) -%D -%D So, from now on, colors are not frozen any more! - -% \appendtoks\setupcolors[\c!expansie=\v!ja]\to\everyjob - -\chardef\currentcolorchannel=0 - -\newif\iffilterspotcolor \filterspotcolorfalse -\newif\ifdoingspotcolor \doingspotcolorfalse - -\def\registercolorchannel#1% - {\ifdoingspotcolor \else - \global\expandafter\chardef\csname\??cs#1\endcsname\zerocount - \fi} - -\newif\ifhidesplitcolor \hidesplitcolortrue - -%D The next macro is for instance used in figure splitting: - -\def\doifseparatingcolorselse - {\iffilterspotcolor - \@EA\firstoftwoarguments - \else\ifcase\currentcolorchannel - \@EAEAEA\secondoftwoarguments - \else - \@EAEAEA\firstoftwoarguments - \fi\fi} - -\def\doifcolorchannelelse#1% - {\doifseparatingcolorselse - {\doifelsenothing{#1} - \secondoftwoarguments - {\doifelse{#1}\@@clsplit - \firstoftwoarguments - \secondoftwoarguments}} - \secondoftwoarguments} - -\def\resetcolorseparation - {\filterspotcolorfalse - \chardef\currentcolorchannel\zerocount} - -%D These can be used in selecting specific files (like -%D figuredatabases). - -% we already have: -% -% \def\colorsplitsuffix{\ifcase\currentcolorchannel\else-\@@clsplitsen\fi} -% \def\colorsplitprefix{\ifcase\currentcolorchannel\else\@@clsplitsen-\fi} - -\def\colorchannelprefix{\doifseparatingcolorselse\@@clsplit\empty-} -\def\colorchannelsuffix{-\doifseparatingcolorselse\@@clsplit\empty} - -%D We now load the low level macros: - -\loadmarkfile{colo-ini} - -%D We default to the colors defined in \module{colo-rgb} and -%D support both \cap{RGB} and \cap{CMYK} output. As you can -%D see, color support is turned off by default. Reduction of -%D gray colors to gray scales is turned on. - -\definecolor[black][s=0] -\definecolor[white][s=1] - -\definetransparency [none] [0] -\definetransparency [normal] [1] -\definetransparency [multiply] [2] -\definetransparency [screen] [3] -\definetransparency [overlay] [4] -\definetransparency [softlight] [5] -\definetransparency [hardlight] [6] -\definetransparency [colordodge] [7] -\definetransparency [colorburn] [8] -\definetransparency [darken] [9] -\definetransparency [lighten] [10] -\definetransparency [difference] [11] -\definetransparency [exclusion] [12] - -\setupcolors - [\c!state=\v!stop, - \c!conversion=\v!yes, - \c!reduction=\v!no, - \c!rgb=\v!yes, - \c!cmyk=\v!yes, - \c!spot=\v!yes, - \c!mp\c!cmyk=\@@clcmyk, - \c!mp\c!spot=\@@clspot, - \c!expansion=\v!no, - \c!textcolor=, - \c!split=\v!no, - \c!criterium=\v!all] - -\setupcolor - [\v!rgb] - -\protect \endinput diff --git a/tex/context/base/colo-run.tex b/tex/context/base/colo-run.tex index 6313255c3..d94ea9801 100644 --- a/tex/context/base/colo-run.tex +++ b/tex/context/base/colo-run.tex @@ -155,8 +155,9 @@ \gdef\doshowcolor[#1]% {\bgroup \iffirstargument - \let\colorlist\empty + \let\colorlist\empty % not really used, only for colo-run \let\colorstyle\empty + \settrue\collectcolorsinlist \setupcolor[#1]% \fi \def\rule diff --git a/tex/context/base/cont-cs.tex b/tex/context/base/cont-cs.tex index 94235a8b3..f878920aa 100644 --- a/tex/context/base/cont-cs.tex +++ b/tex/context/base/cont-cs.tex @@ -26,6 +26,14 @@ \installlanguage [\s!sk] [\c!state=\v!start] \installlanguage [\s!cs] [\c!state=\v!start] -\setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\ifnum\texengine=\luatexengine + % will be runtime option: typeface + \appendtoks + \usetypescript[modern] + \setuptypeface[modern] + \to \everyjob +\else + \setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\fi \protect \errorstopmode \dump \endinput diff --git a/tex/context/base/cont-de.tex b/tex/context/base/cont-de.tex index 95976e815..460ca7eca 100644 --- a/tex/context/base/cont-de.tex +++ b/tex/context/base/cont-de.tex @@ -31,6 +31,14 @@ \installlanguage [deo] [\c!state=\v!start] -\setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\ifnum\texengine=\luatexengine + % will be runtime option: typeface + \appendtoks + \usetypescript[modern] + \setuptypeface[modern] + \to \everyjob +\else + \setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\fi \protect \errorstopmode \dump \endinput diff --git a/tex/context/base/cont-en.tex b/tex/context/base/cont-en.tex index e3275845c..e2b09ecbe 100644 --- a/tex/context/base/cont-en.tex +++ b/tex/context/base/cont-en.tex @@ -35,6 +35,12 @@ \installlanguage [\s!sk] [\c!state=\v!start] \installlanguage [\s!pl] [\c!state=\v!start] -\setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\ifnum\texengine=\luatexengine +% \prependtoks +% \the \everysetupdocument +% \to \everyjob +\else + \setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\fi \protect \errorstopmode \dump \endinput diff --git a/tex/context/base/cont-fil.tex b/tex/context/base/cont-fil.tex index a0712a42f..28b6b6f55 100644 --- a/tex/context/base/cont-fil.tex +++ b/tex/context/base/cont-fil.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Context File Synonyms} +\writestatus{loading}{ConTeXt File Synonyms} \definefilesynonym [chemie] [chemic] \definefilesynonym [chemics] [chemic] diff --git a/tex/context/base/cont-fr.tex b/tex/context/base/cont-fr.tex index c6cf11ff1..d812b28f9 100644 --- a/tex/context/base/cont-fr.tex +++ b/tex/context/base/cont-fr.tex @@ -29,6 +29,14 @@ \installlanguage [\s!nl] [\c!state=\v!start] \installlanguage [\s!it] [\c!state=\v!start] -\setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\ifnum\texengine=\luatexengine + % will be runtime option: typeface + \appendtoks + \usetypescript[modern] + \setuptypeface[modern] + \to \everyjob +\else + \setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\fi \protect \errorstopmode \dump \endinput diff --git a/tex/context/base/cont-gb.tex b/tex/context/base/cont-gb.tex index 6e60cd1bc..99d297425 100644 --- a/tex/context/base/cont-gb.tex +++ b/tex/context/base/cont-gb.tex @@ -29,6 +29,14 @@ \installlanguage [\s!nl] [\c!state=\v!start] \installlanguage [\s!it] [\c!state=\v!start] -\setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\ifnum\texengine=\luatexengine + % will be runtime option: typeface + \appendtoks + \usetypescript[modern] + \setuptypeface[modern] + \to \everyjob +\else + \setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\fi \protect \errorstopmode \dump \endinput diff --git a/tex/context/base/cont-it.tex b/tex/context/base/cont-it.tex index d3141a4ae..2141e3bc9 100644 --- a/tex/context/base/cont-it.tex +++ b/tex/context/base/cont-it.tex @@ -28,6 +28,14 @@ \installlanguage [\s!es] [\c!state=\v!start] \installlanguage [\s!it] [\c!state=\v!start] -\setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\ifnum\texengine=\luatexengine + % will be runtime option: typeface + \appendtoks + \usetypescript[modern] + \setuptypeface[modern] + \to \everyjob +\else + \setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\fi \protect \errorstopmode \dump \endinput diff --git a/tex/context/base/cont-log.tex b/tex/context/base/cont-log.tex index fb821331d..8419394c4 100644 --- a/tex/context/base/cont-log.tex +++ b/tex/context/base/cont-log.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Context TeX Logos} +\writestatus{loading}{ConTeXt TeX Logos} %D The system that is used to typeset this text is called \TEX, %D typeset with an lowered~E. From te beginning of \TEX, @@ -228,6 +228,7 @@ \def\pdfTeX {pdf\TeX} \def\pdfeTeX {pdfe-\TeX} \def\luaTeX {lua\TeX} +\def\metaTeX {meta\TeX} \unexpanded\def\XeTeX {X\lower.5ex\hbox{\kern-.15em\mirror{E}}\kern-.1667em\TeX} % Better, since lm has a mirrored E (don't ask me why) @@ -251,41 +252,39 @@ {\setbox\scratchbox\hbox{E}% \raise\dimexpr\ht\scratchbox+\dp\scratchbox\relax\hbox{\rotate[\c!rotation=180]{\box\scratchbox}}} -\beginNEWTEX - -\unexpanded\def\XeTeX - {X\lower.5ex - \hbox - {\kern-.15em - \iffontchar\font"018E\relax - \char"018E% - \else - \ifx\fontalternative\c!bf\mirror{E}\else - \ifx\fontalternative\c!it \@XeTeX@\else - \ifx\fontalternative\c!sl \@XeTeX@\else - \ifx\fontalternative\c!bi \@XeTeX@\else - \ifx\fontalternative\c!bs \@XeTeX@\else - \mirror{E}\fi\fi\fi\fi\fi - \fi}% - \kern-.1667em \TeX} - -\endNEWTEX - -\beginOLDTEX - -\unexpanded\def\XeTeX - {X\lower.5ex - \hbox - {\kern-.15em - \ifx\fontalternative\c!bf\mirror{E}\else - \ifx\fontalternative\c!it \@XeTeX@\else - \ifx\fontalternative\c!sl \@XeTeX@\else - \ifx\fontalternative\c!bi \@XeTeX@\else - \ifx\fontalternative\c!bs \@XeTeX@\else - \mirror{E}\fi\fi\fi\fi\fi}% - \kern-.1667em \TeX} - -\endOLDTEX +\ifnum\texengine=\pdftexengine + + \unexpanded\def\XeTeX + {X\lower.5ex + \hbox + {\kern-.15em + \ifx\fontalternative\c!bf\mirror{E}\else + \ifx\fontalternative\c!it \@XeTeX@\else + \ifx\fontalternative\c!sl \@XeTeX@\else + \ifx\fontalternative\c!bi \@XeTeX@\else + \ifx\fontalternative\c!bs \@XeTeX@\else + \mirror{E}\fi\fi\fi\fi\fi}% + \kern-.1667em \TeX} + +\else + + \unexpanded\def\XeTeX + {X\lower.5ex + \hbox + {\kern-.15em + \iffontchar\font"018E\relax + \char"018E% + \else + \ifx\fontalternative\c!bf\mirror{E}\else + \ifx\fontalternative\c!it \@XeTeX@\else + \ifx\fontalternative\c!sl \@XeTeX@\else + \ifx\fontalternative\c!bi \@XeTeX@\else + \ifx\fontalternative\c!bs \@XeTeX@\else + \mirror{E}\fi\fi\fi\fi\fi + \fi}% + \kern-.1667em \TeX} + +\fi \let\ETEX \eTeX \let\PDFTEX \pdfTeX diff --git a/tex/context/base/cont-new.mkiv b/tex/context/base/cont-new.mkiv index 20813c37b..9e2ca49c0 100644 --- a/tex/context/base/cont-new.mkiv +++ b/tex/context/base/cont-new.mkiv @@ -24,8 +24,32 @@ \unprotect +% we need to figure this out (to be discussed) + +\unexpanded\def\textminus + {\char \iffontchar\font"2012 "2012 % figuredash + \else\iffontchar\font"2013 "2013 % endash + \else\iffontchar\font"2212 "2212 % math minus + "002D % hyphen + \fi\fi\fi} + +\unexpanded\def\textplus + {\char"002B } % plus + +% \def\registerviewerlayer#1#2% global ! +% {\setxvalue{(vl:#1)}{\global\dosetattribute{viewerlayer}{\ctxlua{tex.print(viewerlayers.register('#2'))}}}} + +% \setevalue{(vl:)}{\global\doresetattribute{viewerlayer}} + +\let\\=\crlf % till we fixed all styles + +% \def\pagedir{\expandafter\gobblethreearguments} +% \def\bodydir{\expandafter\gobblethreearguments} + % we have to make an mkii/mkiv core-not +\ifx\definestructurecounter\undefined + \def\dochecknote % only to be called locally, some bools will become class-ones {% for the moment no mixed text/endnotes modes, so we use % \footnoteparameter and not \noteparameter (**) @@ -79,22 +103,24 @@ \skip \currentnoteins\zeropoint \fi} -% - -\def\writestatus#1#2{\ctxlua{ctx.writestatus(\!!bs#1\!!es,\!!bs#2\!!es)}} +\fi \ifx\clearmarks\undefined \def\clearmarks {\begingroup\afterassignment\doclearmarks\scratchcounter} \def\doclearmarks{\normalmarks\scratchcounter{}\endgroup} \fi -\def\resetmark#1% we cannot use \normalmarks#1{} - {\global\@EA\chardef\csname\@@mrk\string#1\endcsname\zerocount - \@EA\clearmarks\csname\@@prk\string#1\endcsname - \global\@EA\let\csname\@@trk\string#1\endcsname\empty - \global\@EA\let\csname\@@frk\string#1\endcsname\empty - \global\@EA\let\csname\@@brk\string#1\endcsname\empty - \global\@EA\let\csname\@@crk\string#1\endcsname\empty} +\ifx\@@trk\undefined \else + + \def\resetmark#1% we cannot use \normalmarks#1{} + {\global\@EA\chardef\csname\@@mrk\string#1\endcsname\zerocount + \@EA\clearmarks\csname\@@prk\string#1\endcsname + \global\@EA\let\csname\@@trk\string#1\endcsname\empty + \global\@EA\let\csname\@@frk\string#1\endcsname\empty + \global\@EA\let\csname\@@brk\string#1\endcsname\empty + \global\@EA\let\csname\@@crk\string#1\endcsname\empty} + +\fi %D Since this can be a showstopper, we report the path at the beginning %D as well as at the end of a run. @@ -102,15 +128,26 @@ % \writestatus\m!lua{used config path - \ctxlua{tex.print(caches.configpath())}} % \writestatus\m!lua{used cache path - \ctxlua{tex.print(caches.path)}} +\startluacode + statistics.register("result saved in file", function() + return string.format( "%s.%s", "\outputfilename", (tex.pdfoutput>0 and "pdf") or "dvi") + end) +\stopluacode + %D For the moment we report some statistics. Later this will become an option, %D but for now we need this information. -\def\nomkivstatistics{\ctxlua{function ctx.show_statistics() end}} % for taco +\def\nomkivstatistics{\ctxlua{statistics.enable = false}} % for taco \def\resettimer {\ctxlua{environment.starttime = os.clock()}} \def\elapsedtime {\ctxlua{tex.sprint(os.clock()-environment.starttime)}} \let\elapsedseconds \elapsedtime +% we will have a bunch of extra tracers (--dumphash --dumpdelta) + +\def\tracersdumphash {\ctxlua{tracers.register_dump_hash(false)}} +\def\tracersdumpdelta{\ctxlua{tracers.register_dump_hash(true)}} + \resettimer %D For me. @@ -145,44 +182,33 @@ % texio.write_nl("CREATING "..pth) % os.execute("mkdir " .. pth) % end -% input.output_files = { } +% resolvers.output_files = { } % callback.register('find_write_file', function(id,name) -% input.output_files[name] = file.join(".","tmp","\jobname",name) -% texio.write_nl("REDIRECTING OUTPUT "..name.. " TO " .. input.output_files[name]) -% return input.output_files[name] +% resolvers.output_files[name] = file.join(".","tmp","\jobname",name) +% texio.write_nl("REDIRECTING OUTPUT "..name.. " TO " .. resolvers.output_files[name]) +% return resolvers.output_files[name] % end) % callback.register('find_read_file', function(id,name) % local sname = string.gsub(name,"^\letterpercent./","") -% if input.output_files[sname] then -% return input.output_files[name] +% if resolvers.output_files[sname] then +% return resolvers.output_files[name] % elseif string.find(sname,"^\jobname[\letterpercent.\letterpercent-]") then % local n = file.join(".","tmp","\jobname",sname) % local f = io.open(n) % if f then -% input.output_files[name] = n +% resolvers.output_files[name] = n % texio.write_nl("REDIRECTING INPUT "..sname.. " TO " .. n) % f:close() % return n % else -% return input.findtexfile(name) +% return resolvers.findtexfile(name) % end % else -% return input.findtexfile(name) +% return resolvers.findtexfile(name) % end % end) % } -% The following commands need to be taken care of, e.g. because there is not yet -% a mkiv module for them. (Currently they're overloaded so we need to redefine them.) - -\def\WORD {\groupedcommand{\setcharactercasing[\plusone ]}{}} -\def\word {\groupedcommand{\setcharactercasing[\plustwo ]}{}} -\def\Word {\groupedcommand{\setcharactercasing[\plusthree]}{}} -\def\Words{\groupedcommand{\setcharactercasing[\plusfour]}{}} - -\let\WORDS\WORD -\let\words\word - \definestartstop[randomized][\c!before=\dosetattribute{case}{8},\c!after=] \protect \endinput diff --git a/tex/context/base/cont-new.tex b/tex/context/base/cont-new.tex index ee047599b..b8e2a6f95 100644 --- a/tex/context/base/cont-new.tex +++ b/tex/context/base/cont-new.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2008.10.31 13:58} +\newcontextversion{2009.05.28 11:23} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new @@ -21,7 +21,7 @@ % it's about time to clean up this file ... -\writestatus{\m!systems}{beware: some patches loaded from cont-new.tex} +\writestatus\m!systems{beware: some patches loaded from cont-new.tex} % \ifx\pdfmapfile \undefined \else \pdfmapfile{ } \fi @@ -31,13 +31,16 @@ \let\then\relax % \ifnum1>2\then -) -\def\TransparencyHack % png: /CS /DeviceRGB /I true - {\appendtoks - \doPDFpageattribute{/Group << /S /Transparency /I true /K true>>}% - \to \everyPDFxform - \appendtoks - \doPDFpageattribute{/Group << /S /Transparency /I true /K true>>}% - \to \everyshipout} +\def\fastscale#1% + {\begingroup + \ifnum#1=1000\relax + \setfalse\scaleboxdone + \else + \settrue\scaleboxdone + \edef\finalscaleboxxscale{\withoutpt\the\dimexpr#1pt/1000\relax}% + \let\finalscaleboxyscale\finalscaleboxxscale + \fi + \dowithnextbox{\doscaleboxindeed\flushnextbox\endgroup}\hbox} % \setupcaption [figure] [align=flushleft] % \setupcaption [figure-1] [align=flushleft,leftmargin=10mm] @@ -96,9 +99,9 @@ % normally one does not want this to happen nested, maybe there % is more; non public vars btw, will become conditionals -\appendtoks \writetoregisterfalse \to \everybeforeutilityread -\appendtoks \writetolistfalse \to \everybeforeutilityread -\appendtoks \notesenabledfalse \to \everybeforeutilityread +\ifx\writetoregisterfalse\undefined \else \appendtoks \writetoregisterfalse \to \everybeforeutilityread \fi +\ifx\writetolistfalse \undefined \else \appendtoks \writetolistfalse \to \everybeforeutilityread \fi +\ifx\notesenabledfalse \undefined \else \appendtoks \notesenabledfalse \to \everybeforeutilityread \fi % \setuplabeltext[\s!itemcount1={{I(},{)}}] % \def\labeledcountervalue#1{\labeltexts{#1}{\countervalue{#1}}} @@ -515,69 +518,6 @@ \egroup -% todo : test low level translation (nl->en) and optimize script - -% \definestylecollection[mine] - -% \definestyleinstance[mine][default][sorry] -% \definestyleinstance[mine][tt][bs][ttbs:\rm\sl] -% \definestyleinstance[mine][tt][bf][ttbf:\rm\sl] -% \definestyleinstance[mine][bf][\sl] -% \definestyleinstance[mine][sl][\tt] - -% {\bf test \mine test \sl test \mine test \bs oeps \mine oeps {\tt test \mine \bf test}} - -\definesystemvariable{sx} - -\def\definestylecollection - {\dosingleargument\dodefinestylecollection} - -\def\dodefinestylecollection[#1]% - {\iffirstargument - \unexpanded\setvalue{#1}{\styleinstance[#1]}% - \def\docommand##1% - {\def\dodocommand####1{\letbeundefined{\??sx##1:####1:\commalistelement}}% - \processcommacommand[\alternativelist,\s!default]\dodocommand}% - \processcommacommand[\stylelist,\s!default]\docommand - \fi} - -\def\definestyleinstance - {\doquadrupleargument\dodefinestyleinstance} - -\def\dodefinestyleinstance[#1][#2][#3][#4]% [name] [rm|ss|tt|..] [sl|bf|...] [whatever] - {\iffirstargument - \doifundefined{#1}{\definestylecollection[#1]}% - \fi - \iffourthargument - \setvalue{\??sx#1:#2:#3}{#4}% - \else\ifthirdargument - \setvalue{\??sx#1::#2}{#3}% - \else\ifsecondargument - \letvalue{\??sx#1::#2}\empty - \fi\fi\fi} - -\unexpanded\def\styleinstance[#1]% will be faster - {%\begingroup\expanded{\infofont[#1:\fontstyle:\fontalternative]}\endgroup - \executeifdefined{\??sx#1:\fontstyle:\fontalternative}% - {\executeifdefined{\??sx#1:\fontstyle:\s!default}% - {\executeifdefined{\??sx#1::\fontalternative} - {\getvalue {\??sx#1::\s!default}}}}} - -% \unexpanded\def\styleinstance[#1]% -% {\csname\??sx#1% -% \ifcsname:\fontstyle:\fontalternative\endcsname -% :\fontstyle:\fontalternative -% \else\ifcsname:\fontstyle:\s!default\endcsname -% :\fontstyle:\s!default -% \else\ifcsname::\fontalternative\endcsname -% ::\fontalternative -% \else\ifcsname::\s!default\endcsname -% ::\s!default -% \else -% % nothing, \relax -% \fi\fi\fi\fi -% \endcsname} - % no, wrong! never! % % \def\tightlayer[#1]% @@ -849,16 +789,6 @@ % externfiguur -> grid =ja|hoogte|diepte|halveregel|passend -> helemaal in details % stelplaatsblokin -> zijuitlijnen=hoogte|diepte|regel|halveregel|grid -> halveregel in 'details' -% TODO: TEST FIRST, NO CORRECTION NEEDED IN GRID MODE, EVT OPTION - -\def\OTRONEsomeherefloat[#1]% spacing between two successive must be better - {\baselinecorrection % not really needed in grid mode: - %\ifgridsnapping \else \baselinecorrection \fi % ! ! ! test test test ! ! ! ! - \doplacefloatbox - \doinsertfloatinfo - \dochecknextindentation\??bk - \dorechecknextindentation} - % todo: switch koppelen aan par scheelt pos % to be documented: \startspread .. \stopspread diff --git a/tex/context/base/cont-nl.tex b/tex/context/base/cont-nl.tex index 4635d750d..32b82b01a 100644 --- a/tex/context/base/cont-nl.tex +++ b/tex/context/base/cont-nl.tex @@ -29,6 +29,14 @@ \installlanguage [\s!nl] [\c!state=\v!start] \installlanguage [\s!it] [\c!state=\v!start] -\setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\ifnum\texengine=\luatexengine + % will be runtime option: typeface + \appendtoks + \usetypescript[modern] + \setuptypeface[modern] + \to \everyjob +\else + \setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\fi \protect \errorstopmode \dump \endinput diff --git a/tex/context/base/cont-old.tex b/tex/context/base/cont-old.tex index f8b4b6062..360b5f2e6 100644 --- a/tex/context/base/cont-old.tex +++ b/tex/context/base/cont-old.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Context Old Macros} +\writestatus{loading}{ConTeXt Old Macros} \unprotect diff --git a/tex/context/base/cont-pe.tex b/tex/context/base/cont-pe.tex index ab2b30bcd..fdf47d680 100644 --- a/tex/context/base/cont-pe.tex +++ b/tex/context/base/cont-pe.tex @@ -32,6 +32,14 @@ \installlanguage [\s!nl] [\c!state=\v!start] \installlanguage [\s!pe] [\c!state=\v!start] -\setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\ifnum\texengine=\luatexengine + % will be runtime option: typeface + \appendtoks + \usetypescript[modern] + \setuptypeface[modern] + \to \everyjob +\else + \setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\fi \protect \errorstopmode \dump \endinput diff --git a/tex/context/base/cont-ro.tex b/tex/context/base/cont-ro.tex index e6b2eadf3..9be9b1162 100644 --- a/tex/context/base/cont-ro.tex +++ b/tex/context/base/cont-ro.tex @@ -25,6 +25,14 @@ \installlanguage [\s!de] [\c!state=\v!start] \installlanguage [\s!ro] [\c!state=\v!start] -\setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\ifnum\texengine=\luatexengine + % will be runtime option: typeface + \appendtoks + \usetypescript[modern] + \setuptypeface[modern] + \to \everyjob +\else + \setupencoding[default=ec] \usetypescript[fallback][\defaultencoding] \setupbodyfont[rm,12pt] +\fi \protect \errorstopmode \dump \endinput diff --git a/tex/context/base/cont-sys.ori b/tex/context/base/cont-sys.ori index 335a7d984..11c0141e7 100644 --- a/tex/context/base/cont-sys.ori +++ b/tex/context/base/cont-sys.ori @@ -14,8 +14,8 @@ \unprotect % Speed up typescript loading, but at the cost of much memory: -% -% \preloadtypescripts + +\preloadtypescripts % If you want another default font: % @@ -121,7 +121,6 @@ % When you have your own fonts installed, you may want to predefine: % % \usetypescriptfile[type-buy] -% \usetypescriptfile [type-gyr] % Some styles default to Lucida Bright. You can overload % Lucida by Times cum suis. Watch out, the pos collection @@ -158,8 +157,8 @@ % Enabling run time \METAPOST\ (also enable \write18 in % texmf.cnf): -% \runMPgraphicstrue -% \runMPTEXgraphicstrue +\runMPgraphicstrue +\runMPTEXgraphicstrue % This saves some runtime, but needs a format, which you can % make with 'texexec --make --alone metafun'. Make sure that diff --git a/tex/context/base/cont-usr.ori b/tex/context/base/cont-usr.ori index dab420e3e..5a3070362 100644 --- a/tex/context/base/cont-usr.ori +++ b/tex/context/base/cont-usr.ori @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{User Settings} +\writestatus{loading}{ConTeXt User Settings} \unprotect diff --git a/tex/context/base/context-base.lmx b/tex/context/base/context-base.lmx new file mode 100644 index 000000000..5c96b4979 --- /dev/null +++ b/tex/context/base/context-base.lmx @@ -0,0 +1,38 @@ + + + + + + + + +diff --git a/tex/context/base/context-fonttest.lmx b/tex/context/base/context-fonttest.lmx new file mode 100644 index 000000000..b90af179d --- /dev/null +++ b/tex/context/base/context-fonttest.lmx @@ -0,0 +1,47 @@ + + + + + + + + + +
Some code may move to a module in the language namespace.
--ldx]]-- -local texsprint, floor, mod, format, date, time = tex.sprint, math.floor, math.mod, string.format, os.date, os.time +local utf = unicode.utf8 + +local floor, mod, date, time, concat, format = math.floor, math.mod, os.date, os.time, table.concat, string.format +local texsprint, utfchar = tex.sprint, utf.char + +local ctxcatcodes = tex.ctxcatcodes converters = converters or { } languages = languages or { } +--~ ['arabic-digits'] = { +--~ 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, +--~ 0x0665, 0x0666, 0x0667, 0x0668, 0x0669 +--~ }, +--~ ['persian-digits'] = { +--~ 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, +--~ 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9 +--~ }, + languages.counters = { ['**'] = { 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, @@ -43,12 +57,20 @@ languages.counters = { 0x03A6, 0x03A7, 0x03A8, 0x03A9 }, ['arabic'] = { - 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, - 0x0665, 0x0666, 0x0667, 0x0668, 0x0669 + 0x0627, 0x0628, 0x062C, 0x062F, 0x0647, + 0x0648, 0x0632, 0x062D, 0x0637, 0x0649, + 0x0643, 0x0644, 0x0645, 0x0646, 0x0633, + 0x0639, 0x0641, 0x0635, 0x0642, 0x0631, + 0x0634, 0x062A, 0x062B, 0x062E, 0x0630, + 0x0636, 0x0638, 0x063A, }, ['persian'] = { - 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, - 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9 + 0x0627, 0x0628, 0x062C, 0x062F, 0x0647, + 0x0648, 0x0632, 0x062D, 0x0637, 0x0649, + 0x06A9, 0x0644, 0x0645, 0x0646, 0x0633, + 0x0639, 0x0641, 0x0635, 0x0642, 0x0631, + 0x0634, 0x062A, 0x062B, 0x062E, 0x0630, + 0x0636, 0x0638, 0x063A, }, ['thai'] = { 0xE050, 0xE051, 0xE052, 0xE053, 0xE054, @@ -69,16 +91,34 @@ languages.counters = { ['tibetan'] = { 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29 - } + }, + ['korean'] = { + 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, + 0x3142, 0x3145, 0x3147, 0x3148, 0x314A, + 0x314B, 0x314C, 0x314D, 0x314E + }, + ['korean-parent'] = { -- parenthesed + 0x3200, 0x3201, 0x3202, 0x3203, 0x3204, + 0x3205, 0x3206, 0x3207, 0x3208, 0x3209, + 0x320A, 0x320B, 0x320C, 0x320D + }, + ['korean-circle'] = { -- circled + 0x3260, 0x3261, 0x3262, 0x3263, 0x3264, + 0x3265, 0x3266, 0x3267, 0x3268, 0x3269, + 0x326A, 0x326B, 0x326C, 0x326D + }, } local counters = languages.counters -counters['gr'] = counters['greek'] -counters['g'] = counters['greek'] -counters['sl'] = counters['slovenian'] +counters['ar'] = counters['arabic'] +counters['gr'] = counters['greek'] +counters['g'] = counters['greek'] +counters['sl'] = counters['slovenian'] +counters['kr'] = counters['korean'] +counters['kr-p'] = counters['korean-parent'] +counters['kr-c'] = counters['korean-circle'] -local utfchar = utf.char local fallback = utf.byte('0') function converters.chr(n,m) @@ -92,7 +132,7 @@ function converters.maxchrs(n,m,cmd) converters.maxchrs(floor((n-1)/m),m,cmd) n = (n-1)%m + 1 end - texsprint(tex.ctxcatcodes, format("%s{%s}",cmd,n)) + texsprint(ctxcatcodes, format("%s{%s}",cmd,n)) end function converters.chrs(n,m) if n > 26 then @@ -219,3 +259,222 @@ end function converters.abjadnumerals (n) return texsprint(converters.toabjad(n,false)) end function converters.abjadnodotnumerals(n) return texsprint(converters.toabjad(n,true)) end + +local vector = { + normal = { + [0] = "○", + [1] = "一", + [2] = "二", + [3] = "三", + [4] = "四", + [5] = "五", + [6] = "六", + [7] = "七", + [8] = "八", + [9] = "九", + [10] = "十", + [100] = "百", + [1000] = "千", + [10000] = "万", + [100000000] = "亿", + }, + cap = { + [0] = "零", + [1] = "壹", + [2] = "贰", + [3] = "叁", + [4] = "肆", + [5] = "伍", + [6] = "陆", + [7] = "柒", + [8] = "捌", + [9] = "玖", + [10] = "拾", + [100] = "佰", + [1000] = "仟", + [10000] = "萬", + [100000000] = "亿", + }, + all = { + [0] = "○", + [1] = "一", + [2] = "二", + [3] = "三", + [4] = "四", + [5] = "五", + [6] = "六", + [7] = "七", + [8] = "八", + [9] = "九", + [10] = "十", + [20] = "廿", + [30] = "卅", + [100] = "百", + [1000] = "千", + [10000] = "万", + [100000000] = "亿", + } +} + +function tochinese(n,name) -- normal, caps, all + local result = { } + local vector = vector[name] or vector.normal + while true do + if n == 0 then + break + elseif n >= 100000000 then + local m = floor(n/100000000) + if m > 1 then result[#result+1] = tochinese(m) end + result[#result+1] = vector[100000000] + n = n % 100000000 + elseif n >= 10000000 then + result[#result+1] = tochinese(floor(n/10000)) + result[#result+1] = vector[10000] + n = n % 10000 + elseif n >= 1000000 then + result[#result+1] = tochinese(floor(n/10000)) + result[#result+1] = vector[10000] + n = n % 10000 + elseif n >= 100000 then + result[#result+1] = tochinese(floor(n/10000)) + result[#result+1] = vector[10000] + n = n % 10000 + elseif n >= 10000 then + local m = floor(n/10000) + if m > 1 then result[#result+1] = vector[m] end + result[#result+1] = vector[10000] + n = n % 10000 + elseif n >= 1000 then + local m = floor(n/1000) + if m > 1 then result[#result+1] = vector[m] end + result[#result+1] = vector[1000] + n = n % 1000 + elseif n >= 100 then + local m = floor(n/100) + if m > 1 then result[#result+1] = vector[m] end + result[#result+1] = vector[100] + n = n % 100 + elseif n >= 10 then + local m = floor(n/10) + if vector[m*10] then + result[#result+1] = vector[m*10] + else + result[#result+1] = vector[m] + result[#result+1] = vector[10] + end + n = n % 10 + else + result[#result+1] = vector[n] + break + end + end + return concat(result) +end + +--~ for k, v in ipairs { 1,10,15,25,35,45,11,100,111,1111,10000,11111,100000,111111,1111111,11111111,111111111,100000000,1111111111,11111111111,111111111111,1111111111111 } do +--~ print(v,tochinese(v),tochinese(v,"all"),tochinese(v,"cap")) +--~ end + +function converters.chinesenumerals (n) return texsprint(tochinese(n,"normal")) end +function converters.chinesecapnumerals(n) return texsprint(tochinese(n,"cap" )) end +function converters.chineseallnumerals(n) return texsprint(tochinese(n,"all" )) end + +--~ Well, since the one asking for this didn't test it the following code is not +--~ enabled. +--~ +--~ -- This Lua version is based on a Javascript by Behdad Esfahbod which in turn +--~ -- is based on GPL'd code by Roozbeh Pournader of the The FarsiWeb Project +--~ -- Group: http://www.farsiweb.info/jalali/jalali.js. +--~ -- +--~ -- We start tables at one, I kept it zero based in order to stay close to +--~ -- the original. +--~ -- +--~ -- Conversion by Hans Hagen +--~ +--~ local g_days_in_month = { [0]=31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } +--~ local j_days_in_month = { [0]=31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 } +--~ +--~ local function div(a,b) +--~ return math.floor(a/b) +--~ end +--~ +--~ local function remainder(a,b) +--~ return a - div(a,b)*b +--~ end +--~ +--~ function gregorian_to_jalali(gy,gm,gd) +--~ local jy, jm, jd, g_day_no, j_day_no, j_np, i +--~ gy, gm, gd = gy - 1600, gm - 1, gd - 1 +--~ g_day_no = 365*gy + div((gy+3),4) - div((gy+99),100) + div((gy+399),400) +--~ i = 0 +--~ while i < gm do +--~ g_day_no = g_day_no + g_days_in_month[i] +--~ i = i + 1 +--~ end +--~ if (gm>1 and ((gy%4==0 and gy%100~=0) or (gy%400==0))) then +--~ g_day_no = g_day_no + 1 +--~ end +--~ g_day_no = g_day_no + gd +--~ j_day_no = g_day_no - 79 +--~ j_np = div(j_day_no,12053) +--~ j_day_no = remainder(j_day_no,12053) +--~ jy = 979 + 33*j_np + 4*div(j_day_no,1461) +--~ j_day_no = remainder(j_day_no,1461) +--~ if j_day_no >= 366 then +--~ jy = jy + div((j_day_no-1),365) +--~ j_day_no = remainder((j_day_no-1),365) +--~ end +--~ i = 0 +--~ while i < 11 and j_day_no >= j_days_in_month[i] do +--~ j_day_no = j_day_no - j_days_in_month[i] +--~ i = i + 1 +--~ end +--~ jm = i + 1 +--~ jd = j_day_no + 1 +--~ return jy, jm, jd +--~ end +--~ +--~ function jalali_to_gregorian(jy,jm,jd) +--~ local gy, gm, gd, g_day_no, j_day_no, leap, i +--~ jy, jm, jd = jy - 979, jm - 1, jd - 1 +--~ j_day_no = 365*jy + div(jy,33)*8 + div((remainder(jy,33)+3),4) +--~ i = 0 +--~ while i < jm do +--~ j_day_no = j_day_no + j_days_in_month[i] +--~ i = i + 1 +--~ end +--~ j_day_no = j_day_no + jd +--~ g_day_no = j_day_no + 79 +--~ gy = 1600 + 400*div(g_day_no,146097) +--~ g_day_no = remainder (g_day_no, 146097) +--~ leap = 1 +--~ if g_day_no >= 36525 then +--~ g_day_no = g_day_no - 1 +--~ gy = gy + 100*div(g_day_no,36524) +--~ g_day_no = remainder (g_day_no, 36524) +--~ if g_day_no >= 365 then +--~ g_day_no = g_day_no + 1 +--~ else +--~ leap = 0 +--~ end +--~ end +--~ gy = gy + 4*div(g_day_no,1461) +--~ g_day_no = remainder (g_day_no, 1461) +--~ if g_day_no >= 366 then +--~ leap = 0 +--~ g_day_no = g_day_no - 1 +--~ gy = gy + div(g_day_no, 365) +--~ g_day_no = remainder(g_day_no, 365) +--~ end +--~ i = 0 +--~ while g_day_no >= g_days_in_month[i] + ((i == 1 and leap) or 0) do +--~ g_day_no = g_day_no - g_days_in_month[i] + ((i == 1 and leap) or 0) +--~ i = i + 1 +--~ end +--~ gm = i + 1 +--~ gd = g_day_no + 1 +--~ return gy, gm, gd +--~ end +--~ +--~ print(gregorian_to_jalali(2009,02,24)) +--~ print(jalali_to_gregorian(1387,12,06)) diff --git a/tex/context/base/core-con.mkii b/tex/context/base/core-con.mkii index d9347b475..c39bdd9d4 100644 --- a/tex/context/base/core-con.mkii +++ b/tex/context/base/core-con.mkii @@ -2,7 +2,7 @@ %D [ file=core-con, %D version=1997.26.08, %D title=\CONTEXT\ Core Macros, -%D subtitle=Conversion Macros, +%D subtitle=Conversion, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA / Hans Hagen \& Ton Otten}] @@ -11,8 +11,67 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. +\writestatus{loading}{ConTeXt Core Macros / Conversion} + \unprotect +\ifx\currentlanguage\undefined \let\currentlanguage\empty \fi +\ifx\labeltext \undefined \let\labeltext\firstofoneargument \fi + +%D This module deals with all kind of conversions from numbers +%D and dates. I considered splitting this module in a support +%D one and a core one, but to keep things simple as well as +%D preserve the overview, I decided against splitting. + +\let\spr\firstofoneargument % separator +\let\stp\firstofoneargument % stopper + +% cleaner, some day: +% +% \def\isolateseparators % etex only, even works with list separator overloading +% {\unexpanded\def\spr##1{{##1}}% +% \unexpanded\def\stp##1{{##1}}} + +% needed for arab : + +\def\isolateseparators % even works with list separator overloading + {\def\spr##1{{##1}}% + \def\stp##1{{##1}}} + +%D \macros +%D {numbers} +%D +%D First we deal with the dummy conversion of numbers using the +%D \TEX\ primitive \type{\number}. The uppercase alternative is +%D only there for compatibility with the other conversion +%D macros. We could do without \type{#1} but this way we get +%D rid of unwanted braces. For the savety we also define a +%D non||sence uppercase alternative. +%D +%D \showsetup{numbers} +%D +%D \starttyping +%D \def\numbers#1{\number#1} +%D \def\Numbers#1{\number#1} +%D \stoptyping +%D +%D Due to read ahead, as in \type{[\pagenumber\space]} the space will +%D disappear, unless we use: + +\def\numbers#1{\purenumber{#1}} +\def\Numbers#1{\purenumber{#1}} + +%D \macros +%D {romannumerals,Romannumerals} +%D +%D \TEX\ the program uses a rather tricky conversion from +%D numbers to their roman counterparts. This conversion could +%D of course be programmed in \TEX\ itself, but I guess Knuth +%D found the programming trick worth presenting. +%D +%D \showsetup{romannumerals} +%D \showsetup{Romannumerals} + %D When upcasing the result, we just follow the text book rules %D of expansion. Later on we'll see some more uppercase tricks. @@ -50,6 +109,24 @@ \uppercase\expandafter{\romannumeral#1#2}% \fi\fi\fi\fi} +%D \macros +%D {character,Character} +%D +%D Converting a number into a character can of course only +%D be done with numbers less or equal to~26. At the cost of +%D much more macros a faster conversion is possible, using: +%D +%D \starttyping +%D \setvalue{char1}{a} \def\character#1{\getvalue{char#1}} +%D \stoptyping +%D +%D But we prefer a simpel \type{\case}. +%D +%D \showsetup{character} +%D \showsetup{Character} + +\def\unknowncharacter{-} % else in lists \relax + %D Big case statements but pretty fast: \def\character#1% @@ -68,6 +145,38 @@ \unknowncharacter \fi} +%D \macros +%D {characters,Characters} +%D +%D Converting large numbers is supported by the next two +%D macros. This time we just count on: $\cdots$~x, y, z, aa, +%D ab, ac~$\cdots$. +%D +%D \showsetup{characters} +%D \showsetup{Characters} + +%D The fully expandable alternative: + +\def\dodoconvertcharacters#1#2#3% + {\ifcase#3\else + \ifnum#3>#1 + \expandafter\doconvertcharacters\expandafter#2\expandafter{\the\numexpr(#3+12)/#1-1\relax}% + \expandafter#2\expandafter{\the\numexpr#3-((#3+12)/#1-1)*#1\relax}% + \else + \expandafter#2\expandafter{\number#3}% + \fi + \fi} + +\def\doconvertcharacters{\dodoconvertcharacters{26}} + +\def\characters{\doconvertcharacters\character} +\def\Characters{\doconvertcharacters\Character} + +%D \macros +%D {greeknumerals,Greeknumerals} +%D +%D Why should we only honour the romans, and not the greek? + \def\greeknumerals#1% {% no longer needed: \mathematics {\ifcase#1\unknowncharacter\or @@ -94,22 +203,115 @@ \unknowncharacter \fi}} -%D The fully expandable alternative: +%D \macros +%D {oldstylenumerals,oldstyleromannumerals} +%D +%D These conversions are dedicated to Frans Goddijn. -\def\dodoconvertcharacters#1#2#3% - {\ifcase#3\else - \ifnum#3>#1 - \expandafter\doconvertcharacters\expandafter#2\expandafter{\the\numexpr(#3+12)/#1-1\relax}% - \expandafter#2\expandafter{\the\numexpr#3-((#3+12)/#1-1)*#1\relax}% - \else - \expandafter#2\expandafter{\number#3}% - \fi +\unexpanded\def\oldstylenumerals#1% + {{\os\number#1}} + +\unexpanded\def\oldstyleromannumerals#1% + {{\leftrulefalse\rightrulefalse\ss\txx\boxrulewidth.15ex + \ruledhbox spread .15em{\hss\uppercased{\romannumerals{#1}}\hss}}} + +%D \macros +%D {protectconversion} +%D +%D The previous two commands are not robust enough to be +%D passed to \type{\write} en \type{\message}. That's why we +%D introduce: + +\def\protectconversion + {\def\doconvertcharacters##1{##1}} % was \relax + %{\def\doconvertcharacters##1{\ifcase0##1 0\else##1\fi}} more save + +%D \macros +%D {normaltime,normalyear,normalmonth,normalday} +%D +%D The last part of this module is dedicated to converting +%D dates. Because we want to use as meaningful commands as +%D possible, and because \TEX\ already uses up some of those, +%D we save the original meanings. + +\savenormalmeaning\time +\savenormalmeaning\year +\savenormalmeaning\month +\savenormalmeaning\day + +%D \macros +%D {month,MONTH} +%D +%D Converting the month number into a month name is done +%D using a case statement, abstact values and the label +%D mechanism. This way users can easily redefine a label from +%D for instance german into austrian. +%D +%D \starttyping +%D \setuplabeltext [de] [january=J\"anner] +%D \stoptyping +%D +%D Anyhow, the conversion looks like: + +\def\domonthtag#1% + {\ifcase#1% + \or \v!january \or \v!february \or \v!march \or \v!april + \or \v!may \or \v!june \or \v!july \or \v!august + \or \v!september \or \v!october \or \v!november \or \v!december + \else + \v!unknown \fi} -\def\doconvertcharacters{\dodoconvertcharacters{26}} +\def\doconvertmonthlong #1{\labeltext{\domonthtag{#1}}} +\def\doconvertmonthshort#1{\labeltext{\domonthtag{#1}:\s!mnem}} -\def\characters{\doconvertcharacters\character} -\def\Characters{\doconvertcharacters\Character} +\let\doconvertmonth\doconvertmonthlong + +%D We redefine the \TEX\ primitive \type{\month} as: +%D +%D \showsetup{month} +%D \showsetup{MONTH} + +\def\monthlong {\doconvertmonthlong} +\def\monthshort{\doconvertmonthshort} +\def\month {\doconvertmonth} + +\def\MONTH #1{{\let\labeltext\LABELTEXT\month {#1}}} +\def\MONTHLONG #1{{\let\labeltext\LABELTEXT\monthlong {#1}}} +\def\MONTHSHORT#1{{\let\labeltext\LABELTEXT\monthshort{#1}}} + +%D We never explicitly needed this, but Tobias Burnus pointed +%D out that it would be handy to convert to the day of the +%D week. In doing so, we have to calculate the total number of +%D days, taking leapyears into account. For those who are +%D curious: +%D +%D \startitemize[packed] +%D \item years that can be divided by 4 are leapyears +%D \item exept years that can be divided by 100 +%D \item unless years can be divided by 400 +%D \stopitemize +%D +%D This makes the year 1900 into a normal year and 1996 and +%D 2000 into leap years, right? Well, converting to string +%D looks familiar: + +\def\doconvertday#1% + {\labeltext + {\ifcase#1 + \or \v!sunday \or \v!monday \or \v!tuesday \or \v!wednesday + \or \v!thursday \or \v!friday \or \v!saturday \fi}} + +%D \macros +%D {getdayoftheweek, dayoftheweek} +%D +%D The conversion algoritm is an old one and a translation from +%D a procedure written in MODULA~2 back in the 80's. I finaly +%D found the 4--100-400 rules in some enclopedia. Look at this +%D messy low level routine that takes the day, month and year +%D as arguments: + +\newcount\normalweekday \def\getdayoftheweek#1#2#3% {\bgroup @@ -140,6 +342,77 @@ \def\dayoftheweek#1#2#3% {\getdayoftheweek{#1}{#2}{#3}\doconvertday{\normalweekday}} +%D Using this macro in +%D +%D \startbuffer +%D monday: \dayoftheweek {4} {5} {1992} +%D friday: \dayoftheweek {16} {6} {1995} +%D monday: \dayoftheweek {25} {8} {1997} +%D saturday: \dayoftheweek {30} {8} {1997} +%D tuesday: \dayoftheweek {2} {1} {1996} +%D tuesday: \dayoftheweek {7} {1} {1997} +%D tuesday: \dayoftheweek {13} {1} {1998} +%D friday: \dayoftheweek {1} {1} {2000} +%D \stopbuffer +%D +%D \typebuffer +%D +%D gives +%D +%D \startvoorbeeld +%D \startlines +%D \getbuffer +%D \stoplines +%D \stopvoorbeeld +%D +%D The macro \type {\getdayoftheweek} can be used to calculate +%D the number \type {\normalweekday}. + +%D \macros +%D {weekday,WEEKDAY} +%D +%D The first one is sort of redundant. It takes the day +%D number argument. +%D +%D \showsetup{weekday} +%D \showsetup{WEEKDAY} + +\def\weekday + {\doconvertday} + +\def\WEEKDAY#1% + {{\let\labeltext\LABELTEXT\doconvertday{#1}}} + +%D \macros +%D {weekoftheday} +%D +%D {\em not yet implemented:} +%D +%D \starttyping +%D \def\weekoftheday#1#2#3% +%D {} +%D \stoptyping + +%D \macros +%D {doifleapyearelse, +%D getdayspermonth} +%D +%D Sometimes we need to know if we're dealing with a +%D leapyear, so here is a testmacro: +%D +%D \starttyping +%D \doifleapyearelse{year}{yes}{no} +%D \stoptyping +%D +%D An example of its use can be seen in the macro +%D +%D \starttyping +%D \getdayspermonth{year}{month} +%D \stoptyping +%D +%D The number of days is available in the macro \type +%D {\numberofdays}. + \def\doifleapyearelse#1% #2#3% {\bgroup \!!doneafalse @@ -185,12 +458,450 @@ {\ifcase#2 \or31\or\numberofdays\or31\or30\or 31\or30\or31\or31\or30\or31\or30\or31\fi}} +%D \macros +%D {currentdate, date} +%D +%D We use these conversion macros in the date formatting +%D macro: +%D +%D \showsetup{currentdate} +%D +%D This macro takes care of proper spacing and delivers for +%D instance: +%D +%D \startbuffer +%D \currentdate[weekday,day,month,year] % still dutch example +%D \currentdate[WEEKDAY,day,MONTH,year] % still dutch example +%D \stopbuffer +%D +%D \startvoorbeeld +%D \startlines +%D \getbuffer +%D \stoplines +%D \stopvoorbeeld +%D +%D depending of course on the keywords. Here we gave: +%D +%D \typebuffer +%D +%D If needed one can also add non||keywords, like in +%D +%D \startbuffer +%D \currentdate[dd,--,mm,--,yy] +%D \stopbuffer +%D +%D \typebuffer +%D +%D or typeset: \getbuffer. +%D +%D When no argument is passed, the current date is given as +%D specified per language (using \type{\installlanguage}). +%D +%D \showsetup{currentdate} +%D +%D \startbuffer +%D \date +%D \date[d=12,m=12,y=1998][weekday] +%D \date[d=12,m=12,y=1998] +%D \stopbuffer +%D +%D We can also typeset arbitrary dates, using the previous +%D command. +%D +%D \typebuffer +%D +%D The date is specified by one character keys. When no date +%D is given, we get the current date. +%D +%D \startlines +%D \getbuffer +%D \stoplines + +\def\kenmerkdatumpatroon{j,mm,dd} % jj,mm,dd changed at januari 1-1-2000 + +\newsignal\datesignal + +\def\dobetweendates + {\ifdim\lastskip=\datesignal\relax\else + \unskip\space + \hskip\datesignal\relax + \fi} + +\newtoks \everycurrentdate + +\def\complexcurrentdate[#1]% + {\bgroup + \the\everycurrentdate + \def\betweendates{\let\betweendates\dobetweendates}% + % was \processcommacommandp[#1]\docomplexcurrentdate + \safeedef\ascii{\empty#1}% keep encoded chars + \@EA\processcommalist\@EA[\ascii]\docomplexcurrentdate + \ifdim\lastskip=\datesignal\relax + \unskip + \fi + \egroup} + +\def\docomplexcurrentdate#1% + {\lowercase{\edef\!!stringa{#1}}% permits usage in \smallcapped + \expanded{\processaction[\!!stringa]}% [#1] + [ \v!day=>\betweendates\the\normalday, + %\v!day+=>\betweendates\ordinaldaynumber\normalday, + \v!day+=>\betweendates\convertnumber{\v!day+}\normalday, + \v!month=>\betweendates\month\normalmonth, + \v!year=>\betweendates\the\normalyear, + \v!space=>\unskip\ \hskip\datesignal,% optimization -) + \ =>\unskip\ \hskip\datesignal,% optimization -) + d=>\convertnumber\v!day\normalday, + %d+=>\ordinaldaynumber\normalday, + d+=>\convertnumber{\v!day+}\normalday, + m=>\convertnumber\v!month\normalmonth, + j=>\convertnumber\v!year\normalyear, + y=>\convertnumber\v!year\normalyear, + w=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear, + dd=>\ifnum\normalday >9 \else0\fi\the\normalday, + %dd+=>\ordinaldaynumber{\ifnum\normalday >9 \else0\fi\the\normalday}, + dd+=>\convertnumber{\v!day+}{\ifnum\normalday >9 \else0\fi\the\normalday}, + mm=>\ifnum\normalmonth>9 \else0\fi\the\normalmonth, + jj=>\expandafter\gobbletwoarguments\the\normalyear, + yy=>\expandafter\gobbletwoarguments\the\normalyear, + \v!weekday=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear, + \v!referral=>\expanded{\complexcurrentdate[\kenmerkdatumpatroon]}, + \s!unknown=>\unskip + % #1 and not the lowercased \commalistelement, vietnamese has text + % {} because #1 can have comma, like: {\ ,} + {#1}% + \hskip\datesignal + \def\betweendates{\let\betweendates\dobetweendates}]} + +\def\simplecurrentdate + {\expanded{\complexcurrentdate[\currentdatespecification]}} + +\definecomplexorsimple\currentdate + +\def\dodate[#1][#2]% + {\bgroup + \iffirstargument + \getparameters[\??da][d=\normalday,m=\normalmonth,y=\normalyear,#1]% + \normalday \@@dad\relax + \normalmonth\@@dam\relax + \normalyear \@@day\relax + \ifsecondargument + \currentdate[#2]% + \else + \currentdate + \fi + \else + \currentdate + \fi + \egroup} + +\def\date + {\dodoubleempty\dodate} + +%D \macros +%D {currenttime} +%D +%D The currenttime is actually the jobtime. You can specify +%D a pattern similar to the previous date macro using the +%D keys \type {h}, \type {m} and a separator. + \def\calculatecurrenttime {\dosetdivision\time{60}\scratchcounter \edef\currenthour {\ifnum\scratchcounter<10 0\fi \the\scratchcounter}% \dosetmodulo \time{60}\scratchcounter \edef\currentminute{\ifnum\scratchcounter<10 0\fi \the\scratchcounter}} +\let\currenthour \!!plusone +\let\currentminute\!!plusone + +\def\currenttimespecification{h,:,m} + +\def\complexcurrenttime[#1]% + {\calculatecurrenttime + \processallactionsinset[#1] + [h=>\currenthour,m=>\currentminute,\s!unknown=>\commalistelement]} + +\def\simplecurrenttime + {\expanded{\complexcurrenttime[\currenttimespecification]}} + +\definecomplexorsimple\currenttime + +%D Because we're dealing with dates, we also introduce a few +%D day loops: +%D +%D \starttyping +%D \processmonth{year}{month}{command} +%D \processyear{year}{command}{before}{after} +%D \stoptyping +%D +%D The counters \type {\normalyear}, \type {\normalmonth} and +%D \type{\normalday} can be used for for date manipulations. + +\long\def\processmonth#1#2#3% year month command + {\bgroup + \getdayspermonth{#1}{#2}% + \dostepwiserecurse1\numberofdays1% + {\normalyear #1\relax + \normalmonth#2\relax + \normalday \recurselevel\relax + #3}% + \egroup} + +\def\lastmonth{12} % can be set to e.g. 1 when testing + +\long\def\processyear#1#2#3#4% year command before after + {\bgroup + \dorecurse\lastmonth + {\normalyear #1\relax + \normalmonth\recurselevel\relax + #3\processmonth\normalyear\normalmonth{#2}#4}% + \egroup} + +%D \macros +%D {defineconversion, convertnumber} +%D +%D Conversion involves the macros that we implemented earlier +%D in this module. +%D +%D \showsetup{defineconversion} +%D \showsetup{convertnumber} +%D +%D We can feed this command with conversion macros as well as +%D a set of conversion symbols. Both need a bit different +%D treatment. +%D +%D \starttyping +%D \defineconversion [roman] [\romannumerals] +%D \defineconversion [set 1] [$\star$,$\bullet$,$\ast$] +%D \stoptyping +%D +%D You can define a language dependent conversion with: +%D +%D \starttyping +%D \defineconversion [en] [whatever] [\something] +%D \stoptyping + +% \def\dodefineconversion[#1][#2]% +% {\ConvertConstantAfter\doifinstringelse{,}{#2} +% {\scratchcounter=0 +% \def\docommand##1% +% {\advance\scratchcounter 1 +% \setvalue{\??cv#1\the\scratchcounter}{##1}}% +% \processcommalist[#2]\docommand +% \setvalue{\??cv#1}##1{\csname\??cv#1##1\endcsname}} +% {\setvalue{\??cv#1}{#2}}} +% +% \def\defineconversion% +% {\dodoubleargument\dodefineconversion} + +\def\defineconversion + {\dotripleempty\dodefineconversion} + +\def\dodefineconversion[#1][#2][#3]% + {\ifthirdargument + \dododefineconversion[#1][#2][#3]% + \else + \dododefineconversion[][#1][#2]% + \fi} + +%D \starttyping +%D \def\dododefineconversion[#1][#2][#3]% +%D {\ConvertConstantAfter\doifinstringelse{,}{#3} +%D {\scratchcounter\zerocount +%D \def\docommand##1% +%D {\advance\scratchcounter \plusone +%D \setvalue{\??cv#1#2\the\scratchcounter}{##1}}% +%D \processcommalist[#3]\docommand +%D \setvalue{\??cv#1#2}##1{\executeifdefined{\??cv#1#2##1}\unknown}} % catch out-of-range numbers +%D {\setvalue{\??cv#1#2}{#3}}} +%D \stoptyping + +%D This approach has the disadvantage that when you run out of +%D symbols you get unknown results. The following implementation +%D permits overloading of the converter: + +\def\dododefineconversion[#1][#2][#3]% + {\ConvertConstantAfter\doifinstringelse{,}{#3} + {\scratchcounter\zerocount + \def\docommand##1% + {\advance\scratchcounter \plusone + \setvalue{\??cv#1#2\the\scratchcounter}{##1}}% + \processcommalist[#3]\docommand + \setevalue{\??cv#1#2}##1% + {\noexpand\docheckedconversion{#1#2}{\the\scratchcounter}{##1}}} + {\setvalue{\??cv#1#2}{#3}}} + +\def\docheckedconversion#1#2#3% class maxnumber number + {\executeifdefined{\??cv#1#3}\unknown} + +%D When Gerben reported problems with footnote numbering per page, +%D Taco came with the following wrap around solution. So, let's +%D overload the checked conversion macro: + +\def\docheckedconversion#1#2#3% class maxnumber number + {\executeifdefined{\??cv#1\modulatednumber{#2}{#3}}\unknown} + +%D Taco's modulo code is implemented in the system module +%D \type {syst-con}. + +%D If a conversion is just a font switch then we need to make sure +%D that the number is indeed end up as number in the input, so we +%D need to handle the second argument. + +\def\convertnumber#1#2% + {\csname\??cv + \ifcsname\??cv\currentlanguage#1\endcsname + \currentlanguage#1% + \else\ifcsname\??cv#1\endcsname + #1% + \else + \s!default + \fi\fi + \endcsname{\number#2}} + +\def\doifconversiondefinedelse#1% + {\ifcsname\??cv\currentlanguage#1\endcsname + \@EA\firstoftwoarguments + \else\ifcsname\??cv#1\endcsname + \@EAEAEA\firstoftwoarguments + \else + \@EAEAEA\secondoftwoarguments + \fi\fi} + +\def\doifelseconversionnumber#1#2% slow but seldom used + {\doifdefinedelse{\??cv#1#2}} + +%D Handy. + +\setvalue{\??cv:\c!n:\v!one }{1} +\setvalue{\??cv:\c!n:\v!two }{2} +\setvalue{\??cv:\c!n:\v!three}{3} +\setvalue{\??cv:\c!n:\v!four }{4} +\setvalue{\??cv:\c!n:\v!five }{5} + +\def\wordtonumber#1#2{\ifcsname\??cv:\c!n:#1\endcsname\csname\??cv:\c!n:#1\endcsname\else#2\fi} + +% \defineconversion[ctx][c,o,n,t,e,x,t] +% +% \doloop{\doifelseconversionnumber{ctx}{\recurselevel}{[\recurselevel]}{\exitloop}} + +\defineconversion [\s!default] [\numbers] + +%D As longs as symbols are linked to levels or numbers, we can +%D also use the conversion mechanism, but in for instance the +%D itemization macros, we prefer symbols because they can more +%D easier be (partially) redefined. Symbols are implemented +%D in another module. + +\defineconversion [] [\numbers] % the default conversion + +\defineconversion [a] [\characters] +\defineconversion [A] [\Characters] +\defineconversion [AK] [\smallcapped\characters] +\defineconversion [KA] [\smallcapped\characters] + +\defineconversion [n] [\numbers] +\defineconversion [N] [\Numbers] +\defineconversion [m] [\mediaeval] + +\defineconversion [i] [\romannumerals] +\defineconversion [I] [\Romannumerals] +\defineconversion [r] [\romannumerals] +\defineconversion [R] [\Romannumerals] +\defineconversion [KR] [\smallcapped\romannumerals] +\defineconversion [RK] [\smallcapped\romannumerals] + +\defineconversion [g] [\greeknumerals] +\defineconversion [G] [\Greeknumerals] + +\defineconversion [o] [\oldstylenumerals] +\defineconversion [O] [\oldstylenumerals] +\defineconversion [or] [\oldstyleromannumerals] + +\defineconversion [\v!character] [\character] +\defineconversion [\v!Character] [\Character] + +\defineconversion [\v!characters] [\characters] +\defineconversion [\v!Characters] [\Characters] + +\defineconversion [\v!numbers] [\numbers] +\defineconversion [\v!Numbers] [\Numbers] +\defineconversion [\v!mediaeval] [\mediaeval] + +\defineconversion [\v!romannumerals] [\romannumerals] +\defineconversion [\v!Romannumerals] [\Romannumerals] + +\defineconversion [\v!greek] [\greeknumerals] +\defineconversion [\v!Greek] [\Greeknumerals] + +\defineconversion [arabicnumerals] [\arabicnumerals] +\defineconversion [persiannumerals] [\arabicnumerals] + +\defineconversion [month] [\doconvertmonthlong] +\defineconversion [month:mnem] [\doconvertmonthshort] + +% Some bonus ones: + +\defineconversion [\v!empty] [\gobbleoneargument] +\defineconversion [\v!none] [\numbers] + +\ifx\symbol\undefined \def\symbol[#1]{#1} \fi % todo + +\defineconversion + [set 0] + [{\symbol[bullet]}, + {\symbol[dash]}, + {\symbol[star]}, + {\symbol[triangle]}, + {\symbol[circle]}, + {\symbol[medcircle]}, + {\symbol[bigcircle]}, + {\symbol[square]}] + +\defineconversion + [set 1] + [\mathematics{\star}, + \mathematics{\star\star}, + \mathematics{\star\star\star}, + \mathematics{\ddagger}, + \mathematics{\ddagger\ddagger}, + \mathematics{\ddagger\ddagger\ddagger}, + \mathematics{\ast}, + \mathematics{\ast\ast}, + \mathematics{\ast\ast\ast}] + +\defineconversion + [set 2] + [\mathematics{*}, + \mathematics{\dag}, + \mathematics{\ddag}, + \mathematics{**}, + \mathematics{\dag\dag}, + \mathematics{\ddag\ddag}, + \mathematics{***}, + \mathematics{\dag\dag\dag}, + \mathematics{\ddag\ddag\ddag}, + \mathematics{****}, + \mathematics{\dag\dag\dag\dag}, + \mathematics{\ddag\ddag\ddag\ddag}] + +\defineconversion + [set 3] + [\mathematics{\star}, + \mathematics{\star\star}, + \mathematics{\star\star\star}, + \mathematics{\ddagger}, + \mathematics{\ddagger\ddagger}, + \mathematics{\ddagger\ddagger\ddagger}, + \mathematics{\P}, + \mathematics{\P\P}, + \mathematics{\P\P\P}, + \mathematics{\S}, + \mathematics{\S\S}, + \mathematics{\S\S\S}, + \mathematics{\ast}, + \mathematics{\ast\ast}, + \mathematics{\ast\ast\ast}] %D \macros %D {defineconversionvector,conversionnumber} % bad names so no danger for clash @@ -235,24 +946,24 @@ % actually mkiii code -\beginXETEX - -\defineconversionvector{arabicnumerals} {"0660} -\defineconversionvector{persiannumerals} {"06F0} -\defineconversionvector{thainumerals} {"0E50} -\defineconversionvector{devanagarinumerals}{"0966} -\defineconversionvector{gurmurkhinumerals} {"0A66} -\defineconversionvector{gujaratinumerals} {"0AE6} -\defineconversionvector{tibetannumerals} {"0F20} % also "half numerals?" - -\defineconversion[arabicnumerals] [\conversionnumber{arabicnumerals}] -\defineconversion[persiannumerals] [\conversionnumber{persiannumerals}] -\defineconversion[thainumerals] [\conversionnumber{thainumerals}] -\defineconversion[devanagarinumerals][\conversionnumber{devanagarinumerals}] -\defineconversion[gurmurkhinumerals] [\conversionnumber{gurmurkhinumerals}] -\defineconversion[gujaratinumerals] [\conversionnumber{gujaratinumerals}] -\defineconversion[tibetannumerals] [\conversionnumber{tibetannumerals}] - -\endXETEX +\ifnum\texengine=\xetexengine + + \defineconversionvector{arabicnumerals} {"0660} + \defineconversionvector{persiannumerals} {"06F0} + \defineconversionvector{thainumerals} {"0E50} + \defineconversionvector{devanagarinumerals}{"0966} + \defineconversionvector{gurmurkhinumerals} {"0A66} + \defineconversionvector{gujaratinumerals} {"0AE6} + \defineconversionvector{tibetannumerals} {"0F20} % also "half numerals?" + + \defineconversion[arabicnumerals] [\conversionnumber{arabicnumerals}] + \defineconversion[persiannumerals] [\conversionnumber{persiannumerals}] + \defineconversion[thainumerals] [\conversionnumber{thainumerals}] + \defineconversion[devanagarinumerals][\conversionnumber{devanagarinumerals}] + \defineconversion[gurmurkhinumerals] [\conversionnumber{gurmurkhinumerals}] + \defineconversion[gujaratinumerals] [\conversionnumber{gujaratinumerals}] + \defineconversion[tibetannumerals] [\conversionnumber{tibetannumerals}] + +\fi \protect \endinput diff --git a/tex/context/base/core-con.mkiv b/tex/context/base/core-con.mkiv index 70ddc6991..5568fc78c 100644 --- a/tex/context/base/core-con.mkiv +++ b/tex/context/base/core-con.mkiv @@ -1,8 +1,8 @@ %D \module %D [ file=core-con, -%D version=2006.09.16, +%D version=1997.26.08, %D title=\CONTEXT\ Core Macros, -%D subtitle=Conversion Macros, +%D subtitle=Conversion, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA / Hans Hagen \& Ton Otten}] @@ -11,33 +11,331 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\unprotect +\writestatus{loading}{ConTeXt Core Macros / Conversion} \registerctxluafile{core-con}{1.001} -\def\romannumerals #1{\ctxlua{converters.romannumerals(\number#1)}} -\def\Romannumerals #1{\ctxlua{converters.Romannumerals(\number#1)}} -\def\abjadnumerals #1{\ctxlua{converters.arabicnumerals(\number#1)}} -\def\abjadnodotnumerals#1{\ctxlua{converters.arabicnodotnumerals(\number#1)}} -\def\abjadnaivenumerals#1{\ctxlua{converters.arabicnaivenumerals(\number#1)}} +\unprotect -\defineconversion [romannumerals] [\romannumerals] -\defineconversion [Romannumerals] [\Romannumerals] -\defineconversion [abjadnumerals] [\abjadnumerals] -\defineconversion [abjadnodotnumerals] [\adjadnodotnumerals] -\defineconversion [abjadnaivenumerals] [\adjadnaivenumerals] +\ifx\currentlanguage\undefined \let\currentlanguage\empty \fi +\ifx\labeltext \undefined \let\labeltext\firstofoneargument \fi -\def\character #1{\ctxlua{converters.character (\number#1)}} -\def\Character #1{\ctxlua{converters.Character (\number#1)}} -\def\characters#1{\ctxlua{converters.characters(\number#1)}} -\def\Characters#1{\ctxlua{converters.Characters(\number#1)}} +%D This module deals with all kind of conversions from numbers +%D and dates. I considered splitting this module in a support +%D one and a core one, but to keep things simple as well as +%D preserve the overview, I decided against splitting. + +\let\spr\firstofoneargument % separator +\let\stp\firstofoneargument % stopper + +% cleaner, some day: +% +% \def\isolateseparators % etex only, even works with list separator overloading +% {\unexpanded\def\spr##1{{##1}}% +% \unexpanded\def\stp##1{{##1}}} + +% needed for arab : + +\def\isolateseparators % even works with list separator overloading + {\def\spr##1{{##1}}% + \def\stp##1{{##1}}} + +%D \macros +%D {numbers} +%D +%D First we deal with the dummy conversion of numbers using the +%D \TEX\ primitive \type{\number}. The uppercase alternative is +%D only there for compatibility with the other conversion +%D macros. We could do without \type{#1} but this way we get +%D rid of unwanted braces. For the savety we also define a +%D non||sence uppercase alternative. +%D +%D \showsetup{numbers} +%D +%D \starttyping +%D \def\numbers#1{\number#1} +%D \def\Numbers#1{\number#1} +%D \stoptyping +%D +%D Due to read ahead, as in \type{[\pagenumber\space]} the space will +%D disappear, unless we use: + +\def\numbers#1{\purenumber{#1}} +\def\Numbers#1{\purenumber{#1}} + +%D \macros +%D {romannumerals,Romannumerals} +%D +%D \TEX\ the program uses a rather tricky conversion from +%D numbers to their roman counterparts. This conversion could +%D of course be programmed in \TEX\ itself, but I guess Knuth +%D found the programming trick worth presenting. +%D +%D \showsetup{romannumerals} +%D \showsetup{Romannumerals} + +\def\romannumerals#1{\ctxlua{converters.romannumerals(\number#1)}} +\def\Romannumerals#1{\ctxlua{converters.Romannumerals(\number#1)}} + +%D Arabic etc: + +\def\abjadnumerals #1{\ctxlua{converters.abjadnumerals (\number#1)}} +\def\abjadnodotnumerals#1{\ctxlua{converters.abjadnodotnumerals(\number#1)}} +\def\abjadnaivenumerals#1{\ctxlua{converters.arabicnumerals (\number#1)}} \def\languagecharacters#1{\ctxlua{converters.alphabetic(\number#1,"\currentlanguage")}} % new \def\languageCharacters#1{\ctxlua{converters.Alphabetic(\number#1,"\currentlanguage")}} % new +% we could use an auxiliary macro to save some bytes in the format +% +% \def\dolanguagecharacters#1#2{\ctxlua{converters.alphabetic(\number#2,"#1")}} + +\def\thainumerals #1{\ctxlua{converters.alphabetic(\number#1,"thai")}} +\def\devanagarinumerals#1{\ctxlua{converters.alphabetic(\number#1,"devanagari")}} +\def\gurmurkhinumerals #1{\ctxlua{converters.alphabetic(\number#1,"gurmurkhi")}} +\def\gujaratinumerals #1{\ctxlua{converters.alphabetic(\number#1,"gujarati")}} +\def\tibetannumerals #1{\ctxlua{converters.alphabetic(\number#1,"tibetan")}} +\def\greeknumerals #1{\ctxlua{converters.alphabetic(\number#1,"greek")}} +\def\Greeknumerals #1{\ctxlua{converters.Alphabetic(\number#1,"greek")}} +\def\arabicnumerals #1{\ctxlua{converters.alphabetic(\number#1,"arabic")}} +\def\persiannumerals #1{\ctxlua{converters.alphabetic(\number#1,"persian")}} + +\let\arabicexnumerals \persiannumerals + +\def\koreannumerals #1{\ctxlua{converters.alphabetic(\number#1,"korean")}} +\def\koreannumeralsp#1{\ctxlua{converters.alphabetic(\number#1,"korean-parent")}} +\def\koreannumeralsc#1{\ctxlua{converters.alphabetic(\number#1,"korean-circle")}} + +\def\chinesenumerals #1{\ctxlua{converters.chinesenumerals (\number#1)}} +\def\chinesecapnumerals#1{\ctxlua{converters.chinesecapnumerals(\number#1,"cap")}} +\def\chineseallnumerals#1{\ctxlua{converters.chineseallnumerals(\number#1,"all")}} + +%D \macros +%D {character,Character} +%D +%D Converting a number into a character can of course only +%D be done with numbers less or equal to~26. At the cost of +%D much more macros a faster conversion is possible, using: +%D +%D \starttyping +%D \setvalue{char1}{a} \def\character#1{\getvalue{char#1}} +%D \stoptyping +%D +%D But we prefer a simpel \type{\case}. +%D +%D \showsetup{character} +%D \showsetup{Character} + +\def\unknowncharacter{-} % else in lists \relax + +\def\character#1{\ctxlua{converters.character (\number#1)}} +\def\Character#1{\ctxlua{converters.Character (\number#1)}} + +%D \macros +%D {characters,Characters} +%D +%D Converting large numbers is supported by the next two +%D macros. This time we just count on: $\cdots$~x, y, z, aa, +%D ab, ac~$\cdots$. +%D +%D \showsetup{characters} +%D \showsetup{Characters} + +\def\characters#1{\ctxlua{converters.characters(\number#1)}} +\def\Characters#1{\ctxlua{converters.Characters(\number#1)}} + +%D \macros +%D {greeknumerals,Greeknumerals} +%D +%D Why should we only honour the romans, and not the greek? + +\let\greeknumerals\gobbleoneargument +\let\Greeknumerals\gobbleoneargument + +%D \macros +%D {oldstylenumerals,oldstyleromannumerals} +%D +%D These conversions are dedicated to Frans Goddijn. + +\unexpanded\def\oldstylenumerals#1% + {{\os\number#1}} + +\unexpanded\def\oldstyleromannumerals#1% + {{\leftrulefalse\rightrulefalse\ss\txx\boxrulewidth.15ex + \ruledhbox spread .15em{\hss\uppercased{\romannumerals{#1}}\hss}}} + +%D \macros +%D {protectconversion} +%D +%D The previous two commands are not robust enough to be +%D passed to \type{\write} en \type{\message}. That's why we +%D introduce: + +\def\protectconversion + {\def\doconvertcharacters##1{##1}} % was \relax + %{\def\doconvertcharacters##1{\ifcase0##1 0\else##1\fi}} more save + +%D \macros +%D {normaltime,normalyear,normalmonth,normalday} +%D +%D The last part of this module is dedicated to converting +%D dates. Because we want to use as meaningful commands as +%D possible, and because \TEX\ already uses up some of those, +%D we save the original meanings. + +\savenormalmeaning\time +\savenormalmeaning\year +\savenormalmeaning\month +\savenormalmeaning\day + +%D \macros +%D {month,MONTH} +%D +%D Converting the month number into a month name is done +%D using a case statement, abstact values and the label +%D mechanism. This way users can easily redefine a label from +%D for instance german into austrian. +%D +%D \starttyping +%D \setuplabeltext [de] [january=J\"anner] +%D \stoptyping +%D +%D Anyhow, the conversion looks like: + +\def\domonthtag#1% + {\ifcase#1% + \or \v!january \or \v!february \or \v!march \or \v!april + \or \v!may \or \v!june \or \v!july \or \v!august + \or \v!september \or \v!october \or \v!november \or \v!december + \else + \v!unknown + \fi} + +\def\doconvertmonthlong #1{\labeltext{\domonthtag{#1}}} +\def\doconvertmonthshort#1{\labeltext{\domonthtag{#1}:\s!mnem}} + +\let\doconvertmonth\doconvertmonthlong + +%D We redefine the \TEX\ primitive \type{\month} as: +%D +%D \showsetup{month} +%D \showsetup{MONTH} + +\def\monthlong {\doconvertmonthlong} +\def\monthshort{\doconvertmonthshort} +\def\month {\doconvertmonth} + +\def\MONTH #1{{\let\labeltext\LABELTEXT\month {#1}}} +\def\MONTHLONG #1{{\let\labeltext\LABELTEXT\monthlong {#1}}} +\def\MONTHSHORT#1{{\let\labeltext\LABELTEXT\monthshort{#1}}} + +%D We never explicitly needed this, but Tobias Burnus pointed +%D out that it would be handy to convert to the day of the +%D week. In doing so, we have to calculate the total number of +%D days, taking leapyears into account. For those who are +%D curious: +%D +%D \startitemize[packed] +%D \item years that can be divided by 4 are leapyears +%D \item exept years that can be divided by 100 +%D \item unless years can be divided by 400 +%D \stopitemize +%D +%D This makes the year 1900 into a normal year and 1996 and +%D 2000 into leap years, right? Well, converting to string +%D looks familiar: + +\def\doconvertday#1% + {\labeltext + {\ifcase#1 + \or \v!sunday \or \v!monday \or \v!tuesday \or \v!wednesday + \or \v!thursday \or \v!friday \or \v!saturday \fi}} + +%D \macros +%D {getdayoftheweek, dayoftheweek} +%D +%D The conversion algoritm is an old one and a translation from +%D a procedure written in MODULA~2 back in the 80's. I finaly +%D found the 4--100-400 rules in some enclopedia. Look at this +%D messy low level routine that takes the day, month and year +%D as arguments: + +\newcount\normalweekday + \def\getdayoftheweek#1#2#3{\normalweekday\ctxlua{converters.weekday(\number#1,\number#2,\number#3)}} \def\dayoftheweek #1#2#3{\doconvertday{\ctxlua{converters.weekday(\number#1,\number#2,\number#3)}}} +%D Using this macro in +%D +%D \startbuffer +%D monday: \dayoftheweek {4} {5} {1992} +%D friday: \dayoftheweek {16} {6} {1995} +%D monday: \dayoftheweek {25} {8} {1997} +%D saturday: \dayoftheweek {30} {8} {1997} +%D tuesday: \dayoftheweek {2} {1} {1996} +%D tuesday: \dayoftheweek {7} {1} {1997} +%D tuesday: \dayoftheweek {13} {1} {1998} +%D friday: \dayoftheweek {1} {1} {2000} +%D \stopbuffer +%D +%D \typebuffer +%D +%D gives +%D +%D \startvoorbeeld +%D \startlines +%D \getbuffer +%D \stoplines +%D \stopvoorbeeld +%D +%D The macro \type {\getdayoftheweek} can be used to calculate +%D the number \type {\normalweekday}. + +%D \macros +%D {weekday,WEEKDAY} +%D +%D The first one is sort of redundant. It takes the day +%D number argument. +%D +%D \showsetup{weekday} +%D \showsetup{WEEKDAY} + +\def\weekday + {\doconvertday} + +\def\WEEKDAY#1% + {{\let\labeltext\LABELTEXT\doconvertday{#1}}} + +%D \macros +%D {weekoftheday} +%D +%D {\em not yet implemented:} +%D +%D \starttyping +%D \def\weekoftheday#1#2#3% +%D {} +%D \stoptyping + +%D \macros +%D {doifleapyearelse, +%D getdayspermonth} +%D +%D Sometimes we need to know if we're dealing with a +%D leapyear, so here is a testmacro: +%D +%D \starttyping +%D \doifleapyearelse{year}{yes}{no} +%D \stoptyping +%D +%D An example of its use can be seen in the macro +%D +%D \starttyping +%D \getdayspermonth{year}{month} +%D \stoptyping +%D +%D The number of days is available in the macro \type +%D {\numberofdays}. + \def\doifleapyearelse#1% {\ifcase\ctxlua{converters.leapyear(\number#1)} \@EA\secondoftwoarguments @@ -51,11 +349,6 @@ \def\dayspermonth#1#2% {\ctxlua{converters.nofdays(\number#1,\number#2)}} -\def\calculatecurrenttime - {\edef\currenthour {\ctxlua{converters.hour ()}}% - \edef\currentminute{\ctxlua{converters.minute()}}% - \edef\currentsecond{\ctxlua{converters.second()}}} - % problem is that we calculate with those numbers % % \def\time {\numexpr\ctxlua{converters.textime()}\relax} @@ -71,33 +364,481 @@ % \dayspermonth{2000}{2} % [\the\normaltime=\the\time] -% we could use an auxiliary macro to save some bytes in the format +%D \macros +%D {currentdate, date} +%D +%D We use these conversion macros in the date formatting +%D macro: +%D +%D \showsetup{currentdate} +%D +%D This macro takes care of proper spacing and delivers for +%D instance: +%D +%D \startbuffer +%D \currentdate[weekday,day,month,year] % still dutch example +%D \currentdate[WEEKDAY,day,MONTH,year] % still dutch example +%D \stopbuffer +%D +%D \startvoorbeeld +%D \startlines +%D \getbuffer +%D \stoplines +%D \stopvoorbeeld +%D +%D depending of course on the keywords. Here we gave: +%D +%D \typebuffer +%D +%D If needed one can also add non||keywords, like in +%D +%D \startbuffer +%D \currentdate[dd,--,mm,--,yy] +%D \stopbuffer +%D +%D \typebuffer +%D +%D or typeset: \getbuffer. +%D +%D When no argument is passed, the current date is given as +%D specified per language (using \type{\installlanguage}). +%D +%D \showsetup{currentdate} +%D +%D \startbuffer +%D \date +%D \date[d=12,m=12,y=1998][weekday] +%D \date[d=12,m=12,y=1998] +%D \stopbuffer +%D +%D We can also typeset arbitrary dates, using the previous +%D command. +%D +%D \typebuffer +%D +%D The date is specified by one character keys. When no date +%D is given, we get the current date. +%D +%D \startlines +%D \getbuffer +%D \stoplines + +\def\kenmerkdatumpatroon{j,mm,dd} % jj,mm,dd changed at januari 1-1-2000 + +\newsignal\datesignal + +\def\dobetweendates + {\ifdim\lastskip=\datesignal\relax\else + \unskip\space + \hskip\datesignal\relax + \fi} + +\newtoks \everycurrentdate + +\def\complexcurrentdate[#1]% + {\bgroup + \the\everycurrentdate + \def\betweendates{\let\betweendates\dobetweendates}% + % was \processcommacommandp[#1]\docomplexcurrentdate + \safeedef\ascii{\empty#1}% keep encoded chars + \@EA\processcommalist\@EA[\ascii]\docomplexcurrentdate + \ifdim\lastskip=\datesignal\relax + \unskip + \fi + \egroup} + +\def\docomplexcurrentdate#1% + {\lowercase{\edef\!!stringa{#1}}% permits usage in \smallcapped + \expanded{\processaction[\!!stringa]}% [#1] + [ \v!day=>\betweendates\the\normalday, + %\v!day+=>\betweendates\ordinaldaynumber\normalday, + \v!day+=>\betweendates\convertnumber{\v!day+}\normalday, + \v!month=>\betweendates\month\normalmonth, + \v!year=>\betweendates\the\normalyear, + \v!space=>\unskip\ \hskip\datesignal,% optimization -) + \ =>\unskip\ \hskip\datesignal,% optimization -) + d=>\convertnumber\v!day\normalday, + %d+=>\ordinaldaynumber\normalday, + d+=>\convertnumber{\v!day+}\normalday, + m=>\convertnumber\v!month\normalmonth, + j=>\convertnumber\v!year\normalyear, + y=>\convertnumber\v!year\normalyear, + w=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear, + dd=>\ifnum\normalday >9 \else0\fi\the\normalday, + %dd+=>\ordinaldaynumber{\ifnum\normalday >9 \else0\fi\the\normalday}, + dd+=>\convertnumber{\v!day+}{\ifnum\normalday >9 \else0\fi\the\normalday}, + mm=>\ifnum\normalmonth>9 \else0\fi\the\normalmonth, + jj=>\expandafter\gobbletwoarguments\the\normalyear, + yy=>\expandafter\gobbletwoarguments\the\normalyear, + \v!weekday=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear, + \v!referral=>\expanded{\complexcurrentdate[\kenmerkdatumpatroon]}, + \s!unknown=>\unskip + % #1 and not the lowercased \commalistelement, vietnamese has text + % {} because #1 can have comma, like: {\ ,} + {#1}% + \hskip\datesignal + \def\betweendates{\let\betweendates\dobetweendates}]} + +\def\simplecurrentdate + {\expanded{\complexcurrentdate[\currentdatespecification]}} + +\definecomplexorsimple\currentdate + +\def\dodate[#1][#2]% + {\bgroup + \iffirstargument + \getparameters[\??da][d=\normalday,m=\normalmonth,y=\normalyear,#1]% + \normalday \@@dad\relax + \normalmonth\@@dam\relax + \normalyear \@@day\relax + \ifsecondargument + \currentdate[#2]% + \else + \currentdate + \fi + \else + \currentdate + \fi + \egroup} + +\def\date + {\dodoubleempty\dodate} + +%D \macros +%D {currenttime} +%D +%D The currenttime is actually the jobtime. You can specify +%D a pattern similar to the previous date macro using the +%D keys \type {h}, \type {m} and a separator. + +\def\calculatecurrenttime + {\edef\currenthour {\ctxlua{converters.hour ()}}% + \edef\currentminute{\ctxlua{converters.minute()}}% + \edef\currentsecond{\ctxlua{converters.second()}}} + +\let\currenthour \!!plusone +\let\currentminute\!!plusone + +\def\currenttimespecification{h,:,m} + +\def\complexcurrenttime[#1]% + {\calculatecurrenttime + \processallactionsinset[#1] + [h=>\currenthour,m=>\currentminute,\s!unknown=>\commalistelement]} + +\def\simplecurrenttime + {\expanded{\complexcurrenttime[\currenttimespecification]}} + +\definecomplexorsimple\currenttime + +%D Because we're dealing with dates, we also introduce a few +%D day loops: +%D +%D \starttyping +%D \processmonth{year}{month}{command} +%D \processyear{year}{command}{before}{after} +%D \stoptyping +%D +%D The counters \type {\normalyear}, \type {\normalmonth} and +%D \type{\normalday} can be used for for date manipulations. + +\long\def\processmonth#1#2#3% year month command + {\bgroup + \getdayspermonth{#1}{#2}% + \dostepwiserecurse1\numberofdays1% + {\normalyear #1\relax + \normalmonth#2\relax + \normalday \recurselevel\relax + #3}% + \egroup} + +\def\lastmonth{12} % can be set to e.g. 1 when testing + +\long\def\processyear#1#2#3#4% year command before after + {\bgroup + \dorecurse\lastmonth + {\normalyear #1\relax + \normalmonth\recurselevel\relax + #3\processmonth\normalyear\normalmonth{#2}#4}% + \egroup} + +%D \macros +%D {defineconversion, convertnumber} +%D +%D Conversion involves the macros that we implemented earlier +%D in this module. +%D +%D \showsetup{defineconversion} +%D \showsetup{convertnumber} +%D +%D We can feed this command with conversion macros as well as +%D a set of conversion symbols. Both need a bit different +%D treatment. +%D +%D \starttyping +%D \defineconversion [roman] [\romannumerals] +%D \defineconversion [set 1] [$\star$,$\bullet$,$\ast$] +%D \stoptyping +%D +%D You can define a language dependent conversion with: +%D +%D \starttyping +%D \defineconversion [en] [whatever] [\something] +%D \stoptyping + +% \def\dodefineconversion[#1][#2]% +% {\ConvertConstantAfter\doifinstringelse{,}{#2} +% {\scratchcounter=0 +% \def\docommand##1% +% {\advance\scratchcounter 1 +% \setvalue{\??cv#1\the\scratchcounter}{##1}}% +% \processcommalist[#2]\docommand +% \setvalue{\??cv#1}##1{\csname\??cv#1##1\endcsname}} +% {\setvalue{\??cv#1}{#2}}} % -% \def\dolanguagecharacters#1#2{\ctxlua{converters.alphabetic(\number#2,"#1")}} +% \def\defineconversion% +% {\dodoubleargument\dodefineconversion} -% this does not belong here, but in a lang-module +\def\defineconversion + {\dotripleempty\dodefineconversion} -\def\thainumerals #1{\ctxlua{converters.alphabetic(\number#1,"thai")}} -\def\devanagarinumerals#1{\ctxlua{converters.alphabetic(\number#1,"devanagari")}} -\def\gurmurkhinumerals #1{\ctxlua{converters.alphabetic(\number#1,"gurmurkhi")}} -\def\gujaratinumerals #1{\ctxlua{converters.alphabetic(\number#1,"gujarati")}} -\def\tibetannumerals #1{\ctxlua{converters.alphabetic(\number#1,"tibetan")}} -\def\greeknumerals #1{\ctxlua{converters.alphabetic(\number#1,"greek")}} -\def\Greeknumerals #1{\ctxlua{converters.Alphabetic(\number#1,"greek")}} -\def\arabicnumerals #1{\ctxlua{converters.alphabetic(\number#1,"arabic")}} -\def\persiannumerals #1{\ctxlua{converters.alphabetic(\number#1,"persian")}} +\def\dodefineconversion[#1][#2][#3]% + {\ifthirdargument + \dododefineconversion[#1][#2][#3]% + \else + \dododefineconversion[][#1][#2]% + \fi} -\let\arabicexnumerals \persiannumerals +%D \starttyping +%D \def\dododefineconversion[#1][#2][#3]% +%D {\ConvertConstantAfter\doifinstringelse{,}{#3} +%D {\scratchcounter\zerocount +%D \def\docommand##1% +%D {\advance\scratchcounter \plusone +%D \setvalue{\??cv#1#2\the\scratchcounter}{##1}}% +%D \processcommalist[#3]\docommand +%D \setvalue{\??cv#1#2}##1{\executeifdefined{\??cv#1#2##1}\unknown}} % catch out-of-range numbers +%D {\setvalue{\??cv#1#2}{#3}}} +%D \stoptyping + +%D This approach has the disadvantage that when you run out of +%D symbols you get unknown results. The following implementation +%D permits overloading of the converter: + +\def\dododefineconversion[#1][#2][#3]% + {\ConvertConstantAfter\doifinstringelse{,}{#3} + {\scratchcounter\zerocount + \def\docommand##1% + {\advance\scratchcounter \plusone + \setvalue{\??cv#1#2\the\scratchcounter}{##1}}% + \processcommalist[#3]\docommand + \setevalue{\??cv#1#2}##1% + {\noexpand\docheckedconversion{#1#2}{\the\scratchcounter}{##1}}} + {\setvalue{\??cv#1#2}{#3}}} + +\def\docheckedconversion#1#2#3% class maxnumber number + {\executeifdefined{\??cv#1#3}\unknown} + +%D When Gerben reported problems with footnote numbering per page, +%D Taco came with the following wrap around solution. So, let's +%D overload the checked conversion macro: + +\def\docheckedconversion#1#2#3% class maxnumber number + {\executeifdefined{\??cv#1\modulatednumber{#2}{#3}}\unknown} + +%D Taco's modulo code is implemented in the system module +%D \type {syst-con}. + +%D If a conversion is just a font switch then we need to make sure +%D that the number is indeed end up as number in the input, so we +%D need to handle the second argument. + +\def\convertnumber#1#2% + {\csname\??cv + \ifcsname\??cv\currentlanguage#1\endcsname + \currentlanguage#1% + \else\ifcsname\??cv#1\endcsname + #1% + \else + \s!default + \fi\fi + \endcsname{\number#2}} + +\def\doifconversiondefinedelse#1% + {\ifcsname\??cv\currentlanguage#1\endcsname + \@EA\firstoftwoarguments + \else\ifcsname\??cv#1\endcsname + \@EAEAEA\firstoftwoarguments + \else + \@EAEAEA\secondoftwoarguments + \fi\fi} + +\def\doifelseconversionnumber#1#2% slow but seldom used + {\doifdefinedelse{\??cv#1#2}} + +%D Handy. + +\setvalue{\??cv:\c!n:\v!one }{1} +\setvalue{\??cv:\c!n:\v!two }{2} +\setvalue{\??cv:\c!n:\v!three}{3} +\setvalue{\??cv:\c!n:\v!four }{4} +\setvalue{\??cv:\c!n:\v!five }{5} + +\def\wordtonumber#1#2{\ifcsname\??cv:\c!n:#1\endcsname\csname\??cv:\c!n:#1\endcsname\else#2\fi} + +% \defineconversion[ctx][c,o,n,t,e,x,t] +% +% \doloop{\doifelseconversionnumber{ctx}{\recurselevel}{[\recurselevel]}{\exitloop}} + +%D As longs as symbols are linked to levels or numbers, we can +%D also use the conversion mechanism, but in for instance the +%D itemization macros, we prefer symbols because they can more +%D easier be (partially) redefined. Symbols are implemented +%D in another module. + +\def\smallcappedromannumerals#1{\smallcapped{\romannumerals{#1}}} +\def\smallcappedcharacters #1{\smallcapped{\characters {#1}}} + +\defineconversion [] [\numbers] % the default conversion +\defineconversion [\v!empty] [\gobbleoneargument] +\defineconversion [\v!none] [\numbers] +\defineconversion [\s!default] [\numbers] + +\defineconversion [month] [\doconvertmonthlong] +\defineconversion [month:mnem] [\doconvertmonthshort] + +\defineconversion [\v!character] [\character] +\defineconversion [\v!Character] [\Character] + +\defineconversion [\v!characters] [\characters] +\defineconversion [\v!Characters] [\Characters] + +\defineconversion [a] [\characters] +\defineconversion [A] [\Characters] +\defineconversion [AK] [\smallcappedcharacters] +\defineconversion [KA] [\smallcappedcharacters] + +\defineconversion [\v!numbers] [\numbers] +\defineconversion [\v!Numbers] [\Numbers] +\defineconversion [\v!mediaeval] [\mediaeval] + +\defineconversion [n] [\numbers] +\defineconversion [N] [\Numbers] +\defineconversion [m] [\mediaeval] +\defineconversion [o] [\oldstylenumerals] +\defineconversion [O] [\oldstylenumerals] +\defineconversion [or] [\oldstyleromannumerals] + +\defineconversion [\v!romannumerals] [\romannumerals] +\defineconversion [\v!Romannumerals] [\Romannumerals] + +\defineconversion [i] [\romannumerals] +\defineconversion [I] [\Romannumerals] +\defineconversion [r] [\romannumerals] +\defineconversion [R] [\Romannumerals] + +\defineconversion [KR] [\smallcappedromannumerals] +\defineconversion [RK] [\smallcappedromannumerals] + +\defineconversion [\v!greek] [\greeknumerals] +\defineconversion [\v!Greek] [\Greeknumerals] + +\defineconversion [g] [\greeknumerals] +\defineconversion [G] [\Greeknumerals] + +\defineconversion [arabicnumerals] [\arabicnumerals] +\defineconversion [persiannumerals] [\persiannumerals] + +\defineconversion [abjadnumerals] [\abjadnumerals] +\defineconversion [abjadnodotnumerals] [\adjadnodotnumerals] +\defineconversion [abjadnaivenumerals] [\adjadnaivenumerals] + +\defineconversion [thainumerals] [\thainumerals] +\defineconversion [devanagarinumerals] [\devanagarinumerals] +\defineconversion [gurmurkhinumerals] [\gurmurkhinumerals] +\defineconversion [gujaratinumerals] [\gujaratinumerals] +\defineconversion [tibetannumerals] [\tibetannumerals] +\defineconversion [greeknumerals] [\greeknumerals] +\defineconversion [Greeknumerals] [\Greeknumerals] +\defineconversion [arabicnumerals] [\arabicnumerals] +\defineconversion [persiannumerals] [\persiannumerals] +\defineconversion [arabicexnumerals] [\arabicexnumerals] + + +\defineconversion [koreannumerals] [\koreannumerals] +\defineconversion [koreanparentnumerals] [\koreanparentnumerals] +\defineconversion [koreancirclenumerals] [\koreancirclenumerals] + +\defineconversion [kr] [\koreannumerals] +\defineconversion [kr-p] [\koreanparentnumerals] +\defineconversion [kr-c] [\koreancirclenumerals] + +\defineconversion [chinesenumerals] [\chinesenumerals] +\defineconversion [chinesecapnumeralscn] [\chinesecapnumerals] +\defineconversion [chineseallnumeralscn] [\chineseallnumerals] + +\defineconversion [cn] [\chinesenumerals] +\defineconversion [cn-c] [\chinesecapnumerals] +\defineconversion [cn-a] [\chineseallnumerals] + +%D Symbol sets: + +\ifx\symbol\undefined \def\symbol[#1]{#1} \fi % todo + +\defineconversion + [set 0] + [{\symbol[bullet]}, + {\symbol[dash]}, + {\symbol[star]}, + {\symbol[triangle]}, + {\symbol[circle]}, + {\symbol[medcircle]}, + {\symbol[bigcircle]}, + {\symbol[square]}] + +\defineconversion + [set 1] + [\mathematics{\star}, + \mathematics{\star\star}, + \mathematics{\star\star\star}, + \mathematics{\ddagger}, + \mathematics{\ddagger\ddagger}, + \mathematics{\ddagger\ddagger\ddagger}, + \mathematics{\ast}, + \mathematics{\ast\ast}, + \mathematics{\ast\ast\ast}] + +\defineconversion + [set 2] + [\mathematics{*}, + \mathematics{\dag}, + \mathematics{\ddag}, + \mathematics{**}, + \mathematics{\dag\dag}, + \mathematics{\ddag\ddag}, + \mathematics{***}, + \mathematics{\dag\dag\dag}, + \mathematics{\ddag\ddag\ddag}, + \mathematics{****}, + \mathematics{\dag\dag\dag\dag}, + \mathematics{\ddag\ddag\ddag\ddag}] -\defineconversion [thainumerals] [\thainumerals] -\defineconversion [devanagarinumerals] [\devanagarinumerals] -\defineconversion [gurmurkhinumerals] [\gurmurkhinumerals] -\defineconversion [gujaratinumerals] [\gujaratinumerals] -\defineconversion [tibetannumerals] [\tibetannumerals] -\defineconversion [greeknumerals] [\greeknumerals] -\defineconversion [Greeknumerals] [\Greeknumerals] -\defineconversion [arabicnumerals] [\arabicnumerals] -\defineconversion [persiannumerals] [\persiannumerals] -\defineconversion [arabicexnumerals] [\arabicexnumerals] +\defineconversion + [set 3] + [\mathematics{\star}, + \mathematics{\star\star}, + \mathematics{\star\star\star}, + \mathematics{\ddagger}, + \mathematics{\ddagger\ddagger}, + \mathematics{\ddagger\ddagger\ddagger}, + \mathematics{\P}, + \mathematics{\P\P}, + \mathematics{\P\P\P}, + \mathematics{\S}, + \mathematics{\S\S}, + \mathematics{\S\S\S}, + \mathematics{\ast}, + \mathematics{\ast\ast}, + \mathematics{\ast\ast\ast}] \protect \endinput diff --git a/tex/context/base/core-con.tex b/tex/context/base/core-con.tex deleted file mode 100644 index 13d59ecc6..000000000 --- a/tex/context/base/core-con.tex +++ /dev/null @@ -1,744 +0,0 @@ -%D \module -%D [ file=core-con, -%D version=1997.26.08, -%D title=\CONTEXT\ Core Macros, -%D subtitle=Conversion Macros, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\writestatus{loading}{Context Core Macros / Conversion Macros} - -\unprotect - -\ifx\currentlanguage\undefined \let\currentlanguage\empty \fi -\ifx\labeltext \undefined \let\labeltext\firstofoneargument \fi - -%D This module deals with all kind of conversions from numbers -%D and dates. I considered splitting this module in a support -%D one and a core one, but to keep things simple as well as -%D preserve the overview, I decided against splitting. - -\let\spr\firstofoneargument % separator -\let\stp\firstofoneargument % stopper - -% cleaner, some day: -% -% \def\isolateseparators % etex only, even works with list separator overloading -% {\unexpanded\def\spr##1{{##1}}% -% \unexpanded\def\stp##1{{##1}}} - -% needed for arab : - -\def\isolateseparators % even works with list separator overloading - {\def\spr##1{{##1}}% - \def\stp##1{{##1}}} - -%D \macros -%D {numbers} -%D -%D First we deal with the dummy conversion of numbers using the -%D \TEX\ primitive \type{\number}. The uppercase alternative is -%D only there for compatibility with the other conversion -%D macros. We could do without \type{#1} but this way we get -%D rid of unwanted braces. For the savety we also define a -%D non||sence uppercase alternative. -%D -%D \showsetup{numbers} -%D -%D \starttyping -%D \def\numbers#1{\number#1} -%D \def\Numbers#1{\number#1} -%D \stoptyping -%D -%D Due to read ahead, as in \type{[\pagenumber\space]} the space will -%D disappear, unless we use: - -\def\numbers#1{\purenumber{#1}} -\def\Numbers#1{\purenumber{#1}} - -%D \macros -%D {romannumerals,Romannumerals} -%D -%D \TEX\ the program uses a rather tricky conversion from -%D numbers to their roman counterparts. This conversion could -%D of course be programmed in \TEX\ itself, but I guess Knuth -%D found the programming trick worth presenting. -%D -%D \showsetup{romannumerals} -%D \showsetup{Romannumerals} - -\let\romannumerals\gobbleoneargument -\let\Romannumerals\gobbleoneargument - -%D \macros -%D {character,Character} -%D -%D Converting a number into a character can of course only -%D be done with numbers less or equal to~26. At the cost of -%D much more macros a faster conversion is possible, using: -%D -%D \starttyping -%D \setvalue{char1}{a} \def\character#1{\getvalue{char#1}} -%D \stoptyping -%D -%D But we prefer a simpel \type{\case}. -%D -%D \showsetup{character} -%D \showsetup{Character} - -\def\unknowncharacter{-} % else in lists \relax - -\let\character\gobbleoneargument -\let\Character\gobbleoneargument - -%D \macros -%D {characters,Characters} -%D -%D Converting large numbers is supported by the next two -%D macros. This time we just count on: $\cdots$~x, y, z, aa, -%D ab, ac~$\cdots$. -%D -%D \showsetup{characters} -%D \showsetup{Characters} - -\let\characters\gobbleoneargument -\let\Characters\gobbleoneargument - -%D \macros -%D {greeknumerals,Greeknumerals} -%D -%D Why should we only honour the romans, and not the greek? - -\let\greeknumerals\gobbleoneargument -\let\Greeknumerals\gobbleoneargument - -%D \macros -%D {oldstylenumerals,oldstyleromannumerals} -%D -%D These conversions are dedicated to Frans Goddijn. - -\unexpanded\def\oldstylenumerals#1% - {{\os\number#1}} - -\unexpanded\def\oldstyleromannumerals#1% - {{\leftrulefalse\rightrulefalse\ss\txx\boxrulewidth.15ex - \ruledhbox spread .15em{\hss\uppercased{\romannumerals{#1}}\hss}}} - -%D \macros -%D {protectconversion} -%D -%D The previous two commands are not robust enough to be -%D passed to \type{\write} en \type{\message}. That's why we -%D introduce: - -\def\protectconversion - {\def\doconvertcharacters##1{##1}} % was \relax - %{\def\doconvertcharacters##1{\ifcase0##1 0\else##1\fi}} more save - -%D \macros -%D {normaltime,normalyear,normalmonth,normalday} -%D -%D The last part of this module is dedicated to converting -%D dates. Because we want to use as meaningful commands as -%D possible, and because \TEX\ already uses up some of those, -%D we save the original meanings. - -\savenormalmeaning\time -\savenormalmeaning\year -\savenormalmeaning\month -\savenormalmeaning\day - -%D \macros -%D {month,MONTH} -%D -%D Converting the month number into a month name is done -%D using a case statement, abstact values and the label -%D mechanism. This way users can easily redefine a label from -%D for instance german into austrian. -%D -%D \starttyping -%D \setuplabeltext [de] [january=J\"anner] -%D \stoptyping -%D -%D Anyhow, the conversion looks like: - -\def\domonthtag#1% - {\ifcase#1% - \or \v!january \or \v!february \or \v!march \or \v!april - \or \v!may \or \v!june \or \v!july \or \v!august - \or \v!september \or \v!october \or \v!november \or \v!december - \else - \v!unknown - \fi} - -\def\doconvertmonthlong #1{\labeltext{\domonthtag{#1}}} -\def\doconvertmonthshort#1{\labeltext{\domonthtag{#1}:\s!mnem}} - -\let\doconvertmonth\doconvertmonthlong - -%D We redefine the \TEX\ primitive \type{\month} as: -%D -%D \showsetup{month} -%D \showsetup{MONTH} - -\def\monthlong {\doconvertmonthlong} -\def\monthshort{\doconvertmonthshort} -\def\month {\doconvertmonth} - -\def\MONTH #1{{\let\labeltext\LABELTEXT\month {#1}}} -\def\MONTHLONG #1{{\let\labeltext\LABELTEXT\monthlong {#1}}} -\def\MONTHSHORT#1{{\let\labeltext\LABELTEXT\monthshort{#1}}} - -%D We never explicitly needed this, but Tobias Burnus pointed -%D out that it would be handy to convert to the day of the -%D week. In doing so, we have to calculate the total number of -%D days, taking leapyears into account. For those who are -%D curious: -%D -%D \startitemize[packed] -%D \item years that can be divided by 4 are leapyears -%D \item exept years that can be divided by 100 -%D \item unless years can be divided by 400 -%D \stopitemize -%D -%D This makes the year 1900 into a normal year and 1996 and -%D 2000 into leap years, right? Well, converting to string -%D looks familiar: - -\def\doconvertday#1% - {\labeltext - {\ifcase#1 - \or \v!sunday \or \v!monday \or \v!tuesday \or \v!wednesday - \or \v!thursday \or \v!friday \or \v!saturday \fi}} - -%D \macros -%D {getdayoftheweek, dayoftheweek} -%D -%D The conversion algoritm is an old one and a translation from -%D a procedure written in MODULA~2 back in the 80's. I finaly -%D found the 4--100-400 rules in some enclopedia. Look at this -%D messy low level routine that takes the day, month and year -%D as arguments: - -\newcount\normalweekday - -\let\getdayoftheweek\gobblethreearguments -\let\dayoftheweek \gobblethreearguments - -%D Using this macro in -%D -%D \startbuffer -%D monday: \dayoftheweek {4} {5} {1992} -%D friday: \dayoftheweek {16} {6} {1995} -%D monday: \dayoftheweek {25} {8} {1997} -%D saturday: \dayoftheweek {30} {8} {1997} -%D tuesday: \dayoftheweek {2} {1} {1996} -%D tuesday: \dayoftheweek {7} {1} {1997} -%D tuesday: \dayoftheweek {13} {1} {1998} -%D friday: \dayoftheweek {1} {1} {2000} -%D \stopbuffer -%D -%D \typebuffer -%D -%D gives -%D -%D \startvoorbeeld -%D \startlines -%D \getbuffer -%D \stoplines -%D \stopvoorbeeld -%D -%D The macro \type {\getdayoftheweek} can be used to calculate -%D the number \type {\normalweekday}. - -%D \macros -%D {weekday,WEEKDAY} -%D -%D The first one is sort of redundant. It takes the day -%D number argument. -%D -%D \showsetup{weekday} -%D \showsetup{WEEKDAY} - -\def\weekday - {\doconvertday} - -\def\WEEKDAY#1% - {{\let\labeltext\LABELTEXT\doconvertday{#1}}} - -%D \macros -%D {weekoftheday} -%D -%D {\em not yet implemented:} -%D -%D \starttyping -%D \def\weekoftheday#1#2#3% -%D {} -%D \stoptyping - -%D \macros -%D {doifleapyearelse, -%D getdayspermonth} -%D -%D Sometimes we need to know if we're dealing with a -%D leapyear, so here is a testmacro: -%D -%D \starttyping -%D \doifleapyearelse{year}{yes}{no} -%D \stoptyping -%D -%D An example of its use can be seen in the macro -%D -%D \starttyping -%D \getdayspermonth{year}{month} -%D \stoptyping -%D -%D The number of days is available in the macro \type -%D {\numberofdays}. - -\def\doifleapyearelse #1{\firstoftwoarguments} -\def\getdayspermonth#1#2{\let\numberofdays\!!zerocount} - -%D \macros -%D {currentdate, date} -%D -%D We use these conversion macros in the date formatting -%D macro: -%D -%D \showsetup{currentdate} -%D -%D This macro takes care of proper spacing and delivers for -%D instance: -%D -%D \startbuffer -%D \currentdate[weekday,day,month,year] % still dutch example -%D \currentdate[WEEKDAY,day,MONTH,year] % still dutch example -%D \stopbuffer -%D -%D \startvoorbeeld -%D \startlines -%D \getbuffer -%D \stoplines -%D \stopvoorbeeld -%D -%D depending of course on the keywords. Here we gave: -%D -%D \typebuffer -%D -%D If needed one can also add non||keywords, like in -%D -%D \startbuffer -%D \currentdate[dd,--,mm,--,yy] -%D \stopbuffer -%D -%D \typebuffer -%D -%D or typeset: \getbuffer. -%D -%D When no argument is passed, the current date is given as -%D specified per language (using \type{\installlanguage}). -%D -%D \showsetup{currentdate} -%D -%D \startbuffer -%D \date -%D \date[d=12,m=12,y=1998][weekday] -%D \date[d=12,m=12,y=1998] -%D \stopbuffer -%D -%D We can also typeset arbitrary dates, using the previous -%D command. -%D -%D \typebuffer -%D -%D The date is specified by one character keys. When no date -%D is given, we get the current date. -%D -%D \startlines -%D \getbuffer -%D \stoplines - -\def\kenmerkdatumpatroon{j,mm,dd} % jj,mm,dd changed at januari 1-1-2000 - -\newsignal\datesignal - -\def\dobetweendates - {\ifdim\lastskip=\datesignal\relax\else - \unskip\space - \hskip\datesignal\relax - \fi} - -\newtoks \everycurrentdate - -\def\complexcurrentdate[#1]% - {\bgroup - \the\everycurrentdate - \def\betweendates{\let\betweendates\dobetweendates}% - % was \processcommacommandp[#1]\docomplexcurrentdate - \safeedef\ascii{\empty#1}% keep encoded chars - \@EA\processcommalist\@EA[\ascii]\docomplexcurrentdate - \ifdim\lastskip=\datesignal\relax - \unskip - \fi - \egroup} - -\def\docomplexcurrentdate#1% - {\lowercase{\edef\!!stringa{#1}}% permits usage in \smallcapped - \expanded{\processaction[\!!stringa]}% [#1] - [ \v!day=>\betweendates\the\normalday, - %\v!day+=>\betweendates\ordinaldaynumber\normalday, - \v!day+=>\betweendates\convertnumber{\v!day+}\normalday, - \v!month=>\betweendates\month\normalmonth, - \v!year=>\betweendates\the\normalyear, - \v!space=>\unskip\ \hskip\datesignal,% optimization -) - \ =>\unskip\ \hskip\datesignal,% optimization -) - d=>\convertnumber\v!day\normalday, - %d+=>\ordinaldaynumber\normalday, - d+=>\convertnumber{\v!day+}\normalday, - m=>\convertnumber\v!month\normalmonth, - j=>\convertnumber\v!year\normalyear, - y=>\convertnumber\v!year\normalyear, - w=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear, - dd=>\ifnum\normalday >9 \else0\fi\the\normalday, - %dd+=>\ordinaldaynumber{\ifnum\normalday >9 \else0\fi\the\normalday}, - dd+=>\convertnumber{\v!day+}{\ifnum\normalday >9 \else0\fi\the\normalday}, - mm=>\ifnum\normalmonth>9 \else0\fi\the\normalmonth, - jj=>\expandafter\gobbletwoarguments\the\normalyear, - yy=>\expandafter\gobbletwoarguments\the\normalyear, - \v!weekday=>\betweendates\dayoftheweek\normalday\normalmonth\normalyear, - \v!referral=>\expanded{\complexcurrentdate[\kenmerkdatumpatroon]}, - \s!unknown=>\unskip - % #1 and not the lowercased \commalistelement, vietnamese has text - % {} because #1 can have comma, like: {\ ,} - {#1}% - \hskip\datesignal - \def\betweendates{\let\betweendates\dobetweendates}]} - -\def\simplecurrentdate - {\expanded{\complexcurrentdate[\currentdatespecification]}} - -\definecomplexorsimple\currentdate - -\def\dodate[#1][#2]% - {\bgroup - \iffirstargument - \getparameters[\??da][d=\normalday,m=\normalmonth,y=\normalyear,#1]% - \normalday \@@dad\relax - \normalmonth\@@dam\relax - \normalyear \@@day\relax - \ifsecondargument - \currentdate[#2]% - \else - \currentdate - \fi - \else - \currentdate - \fi - \egroup} - -\def\date - {\dodoubleempty\dodate} - -%D \macros -%D {currenttime} -%D -%D The currenttime is actually the jobtime. You can specify -%D a pattern similar to the previous date macro using the -%D keys \type {h}, \type {m} and a separator. - -\let\calculatecurrenttime\relax - -\let\currenthour \!!plusone -\let\currentminute\!!plusone - -\appendtoks \calculatecurrenttime \to \everyjob - -\def\currenttimespecification{h,:,m} - -\def\complexcurrenttime[#1]% - {\calculatecurrenttime - \processallactionsinset[#1] - [h=>\currenthour,m=>\currentminute,\s!unknown=>\commalistelement]} - -\def\simplecurrenttime - {\expanded{\complexcurrenttime[\currenttimespecification]}} - -\definecomplexorsimple\currenttime - -%D Because we're dealing with dates, we also introduce a few -%D day loops: -%D -%D \starttyping -%D \processmonth{year}{month}{command} -%D \processyear{year}{command}{before}{after} -%D \stoptyping -%D -%D The counters \type {\normalyear}, \type {\normalmonth} and -%D \type{\normalday} can be used for for date manipulations. - -\long\def\processmonth#1#2#3% year month command - {\bgroup - \getdayspermonth{#1}{#2}% - \dostepwiserecurse1\numberofdays1% - {\normalyear #1\relax - \normalmonth#2\relax - \normalday \recurselevel\relax - #3}% - \egroup} - -\def\lastmonth{12} % can be set to e.g. 1 when testing - -\long\def\processyear#1#2#3#4% year command before after - {\bgroup - \dorecurse\lastmonth - {\normalyear #1\relax - \normalmonth\recurselevel\relax - #3\processmonth\normalyear\normalmonth{#2}#4}% - \egroup} - -%D \macros -%D {defineconversion, convertnumber} -%D -%D Conversion involves the macros that we implemented earlier -%D in this module. -%D -%D \showsetup{defineconversion} -%D \showsetup{convertnumber} -%D -%D We can feed this command with conversion macros as well as -%D a set of conversion symbols. Both need a bit different -%D treatment. -%D -%D \starttyping -%D \defineconversion [roman] [\romannumerals] -%D \defineconversion [set 1] [$\star$,$\bullet$,$\ast$] -%D \stoptyping -%D -%D You can define a language dependent conversion with: -%D -%D \starttyping -%D \defineconversion [en] [whatever] [\something] -%D \stoptyping - -% \def\dodefineconversion[#1][#2]% -% {\ConvertConstantAfter\doifinstringelse{,}{#2} -% {\scratchcounter=0 -% \def\docommand##1% -% {\advance\scratchcounter 1 -% \setvalue{\??cv#1\the\scratchcounter}{##1}}% -% \processcommalist[#2]\docommand -% \setvalue{\??cv#1}##1{\csname\??cv#1##1\endcsname}} -% {\setvalue{\??cv#1}{#2}}} -% -% \def\defineconversion% -% {\dodoubleargument\dodefineconversion} - -\def\defineconversion - {\dotripleempty\dodefineconversion} - -\def\dodefineconversion[#1][#2][#3]% - {\ifthirdargument - \dododefineconversion[#1][#2][#3]% - \else - \dododefineconversion[][#1][#2]% - \fi} - -%D \starttyping -%D \def\dododefineconversion[#1][#2][#3]% -%D {\ConvertConstantAfter\doifinstringelse{,}{#3} -%D {\scratchcounter\zerocount -%D \def\docommand##1% -%D {\advance\scratchcounter \plusone -%D \setvalue{\??cv#1#2\the\scratchcounter}{##1}}% -%D \processcommalist[#3]\docommand -%D \setvalue{\??cv#1#2}##1{\executeifdefined{\??cv#1#2##1}\unknown}} % catch out-of-range numbers -%D {\setvalue{\??cv#1#2}{#3}}} -%D \stoptyping - -%D This approach has the disadvantage that when you run out of -%D symbols you get unknown results. The following implementation -%D permits overloading of the converter: - -\def\dododefineconversion[#1][#2][#3]% - {\ConvertConstantAfter\doifinstringelse{,}{#3} - {\scratchcounter\zerocount - \def\docommand##1% - {\advance\scratchcounter \plusone - \setvalue{\??cv#1#2\the\scratchcounter}{##1}}% - \processcommalist[#3]\docommand - \setevalue{\??cv#1#2}##1% - {\noexpand\docheckedconversion{#1#2}{\the\scratchcounter}{##1}}} - {\setvalue{\??cv#1#2}{#3}}} - -\def\docheckedconversion#1#2#3% class maxnumber number - {\executeifdefined{\??cv#1#3}\unknown} - -%D When Gerben reported problems with footnote numbering per page, -%D Taco came with the following wrap around solution. So, let's -%D overload the checked conversion macro: - -\def\docheckedconversion#1#2#3% class maxnumber number - {\executeifdefined{\??cv#1\modulatednumber{#2}{#3}}\unknown} - -%D Taco's modulo code is implemented in the system module -%D \type {syst-con}. - -%D If a conversion is just a font switch then we need to make sure -%D that the number is indeed end up as number in the input, so we -%D need to handle the second argument. - -\def\convertnumber#1#2% - {\csname\??cv - \ifcsname\??cv\currentlanguage#1\endcsname - \currentlanguage#1% - \else\ifcsname\??cv#1\endcsname - #1% - \else - \s!default - \fi\fi - \endcsname{\number#2}} - -\def\doifconversiondefinedelse#1% - {\ifcsname\??cv\currentlanguage#1\endcsname - \@EA\firstoftwoarguments - \else\ifcsname\??cv#1\endcsname - \@EAEAEA\firstoftwoarguments - \else - \@EAEAEA\secondoftwoarguments - \fi\fi} - -\def\doifelseconversionnumber#1#2% slow but seldom used - {\doifdefinedelse{\??cv#1#2}} - -% \defineconversion[ctx][c,o,n,t,e,x,t] -% -% \doloop{\doifelseconversionnumber{ctx}{\recurselevel}{[\recurselevel]}{\exitloop}} - -\defineconversion [\s!default] [\numbers] - -%D As longs as symbols are linked to levels or numbers, we can -%D also use the conversion mechanism, but in for instance the -%D itemization macros, we prefer symbols because they can more -%D easier be (partially) redefined. Symbols are implemented -%D in another module. - -\defineconversion [] [\numbers] % the default conversion - -\defineconversion [a] [\characters] -\defineconversion [A] [\Characters] -\defineconversion [AK] [\smallcapped\characters] -\defineconversion [KA] [\smallcapped\characters] - -\defineconversion [n] [\numbers] -\defineconversion [N] [\Numbers] -\defineconversion [m] [\mediaeval] - -\defineconversion [i] [\romannumerals] -\defineconversion [I] [\Romannumerals] -\defineconversion [r] [\romannumerals] -\defineconversion [R] [\Romannumerals] -\defineconversion [KR] [\smallcapped\romannumerals] -\defineconversion [RK] [\smallcapped\romannumerals] - -\defineconversion [g] [\greeknumerals] -\defineconversion [G] [\Greeknumerals] - -\defineconversion [o] [\oldstylenumerals] -\defineconversion [O] [\oldstylenumerals] -\defineconversion [or] [\oldstyleromannumerals] - -\defineconversion [\v!character] [\character] -\defineconversion [\v!Character] [\Character] - -\defineconversion [\v!characters] [\characters] -\defineconversion [\v!Characters] [\Characters] - -\defineconversion [\v!numbers] [\numbers] -\defineconversion [\v!Numbers] [\Numbers] -\defineconversion [\v!mediaeval] [\mediaeval] - -\defineconversion [\v!romannumerals] [\romannumerals] -\defineconversion [\v!Romannumerals] [\Romannumerals] - -\defineconversion [\v!greek] [\greeknumerals] -\defineconversion [\v!Greek] [\Greeknumerals] - -\defineconversion [arabicnumerals] [\arabicnumerals] -\defineconversion [persiannumerals] [\arabicnumerals] - -\defineconversion [month] [\doconvertmonthlong] -\defineconversion [month:mnem] [\doconvertmonthshort] - -% Some bonus ones: - -\defineconversion [\v!empty] [\gobbleoneargument] -\defineconversion [\v!none] [\numbers] - -\ifx\symbol\undefined \def\symbol[#1]{#1} \fi % todo - -\defineconversion - [set 0] - [{\symbol[bullet]}, - {\symbol[dash]}, - {\symbol[star]}, - {\symbol[triangle]}, - {\symbol[circle]}, - {\symbol[medcircle]}, - {\symbol[bigcircle]}, - {\symbol[square]}] - -\defineconversion - [set 1] - [\mathematics{\star}, - \mathematics{\star\star}, - \mathematics{\star\star\star}, - \mathematics{\ddagger}, - \mathematics{\ddagger\ddagger}, - \mathematics{\ddagger\ddagger\ddagger}, - \mathematics{\ast}, - \mathematics{\ast\ast}, - \mathematics{\ast\ast\ast}] - -\defineconversion - [set 2] - [\mathematics{*}, - \mathematics{\dag}, - \mathematics{\ddag}, - \mathematics{**}, - \mathematics{\dag\dag}, - \mathematics{\ddag\ddag}, - \mathematics{***}, - \mathematics{\dag\dag\dag}, - \mathematics{\ddag\ddag\ddag}, - \mathematics{****}, - \mathematics{\dag\dag\dag\dag}, - \mathematics{\ddag\ddag\ddag\ddag}] - -\defineconversion - [set 3] - [\mathematics{\star}, - \mathematics{\star\star}, - \mathematics{\star\star\star}, - \mathematics{\ddagger}, - \mathematics{\ddagger\ddagger}, - \mathematics{\ddagger\ddagger\ddagger}, - \mathematics{\P}, - \mathematics{\P\P}, - \mathematics{\P\P\P}, - \mathematics{\S}, - \mathematics{\S\S}, - \mathematics{\S\S\S}, - \mathematics{\ast}, - \mathematics{\ast\ast}, - \mathematics{\ast\ast\ast}] - -%D Plugins: - -\loadmarkfile{core-con} - -\protect \endinput diff --git a/tex/context/base/core-ctx.lua b/tex/context/base/core-ctx.lua index 90cd4cb3b..eb9003bf1 100644 --- a/tex/context/base/core-ctx.lua +++ b/tex/context/base/core-ctx.lua @@ -6,8 +6,9 @@ if not modules then modules = { } end modules ['supp-fil'] = { license = "see context related readme files" } -commands = commands or { } -commands.trace_prepfiles = false +local trace_prepfiles = false trackers.register("resolvers.prepfiles", function(v) trace_prepfiles = v end) + +commands = commands or { } local list, suffix, islocal, found = { }, "prep", false, false @@ -17,11 +18,11 @@ function commands.loadctxpreplist() local x = xml.load(ctlname) if x then islocal = xml.found(x,"ctx:preplist[@local=='yes']") - if commands.trace_prepfiles then + if trace_prepfiles then if islocal then - ctx.writestatus("systems","loading ctx log file (local)") -- todo: m!systems + commands.writestatus("systems","loading ctx log file (local)") -- todo: m!systems else - ctx.writestatus("systems","loading ctx log file (specified)") -- todo: m!systems + commands.writestatus("systems","loading ctx log file (specified)") -- todo: m!systems end end for r, d, k in xml.elements(x,"ctx:prepfile") do @@ -31,8 +32,8 @@ function commands.loadctxpreplist() name = file.basename(name) end local done = dk.at['done'] or 'no' - if commands.trace_prepfiles then - ctx.writestatus("systems","registering %s -> %s",done) + if trace_prepfiles then + commands.writestatus("systems","registering %s -> %s",done) end found = true list[name] = done -- 'yes' or 'no' @@ -41,26 +42,26 @@ function commands.loadctxpreplist() end end -local function resolve(name) - local function found(name) - local prepname = name .. "." .. suffix - local done = list[name] - if done then - if lfs.isfile(prepname) then - if commands.trace_prepfiles then - ctx.writestatus("systems", "preprocessing: using %s",prepname) - end - return prepname - end +-- -- -- + +local function found(name) -- used in resolve + local prepname = name .. "." .. suffix + if list[name] and lfs.isfile(prepname) then + if trace_prepfiles then + commands.writestatus("systems", "preprocessing: using %s",prepname) end - return false + return prepname end + return false +end + +local function resolve(name) -- used a few times later on local filename = file.collapse_path(name) local prepname = islocal and found(file.basename(name)) if prepname then return prepname end - local prepname = found(filename) + prepname = found(filename) if prepname then return prepname end diff --git a/tex/context/base/core-ctx.mkii b/tex/context/base/core-ctx.mkii index 673d69c09..93cf8b4be 100644 --- a/tex/context/base/core-ctx.mkii +++ b/tex/context/base/core-ctx.mkii @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Context Core Macros / Ctx Job Files} +\writestatus{loading}{ConTeXt Core Macros / Job Control} \unprotect diff --git a/tex/context/base/core-ctx.mkiv b/tex/context/base/core-ctx.mkiv index f2447ffd0..c401b09f0 100644 --- a/tex/context/base/core-ctx.mkiv +++ b/tex/context/base/core-ctx.mkiv @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Context Core Macros / Ctx Job Files} +\writestatus{loading}{ConTeXt Core Macros / Job Control} \unprotect @@ -23,5 +23,4 @@ \appendtoks\loadctxpreplist\to\everystarttext % will become: \prependtoks\loadctxpreplist\to\everyjob - \protect \endinput diff --git a/tex/context/base/core-ctx.tex b/tex/context/base/core-ctx.tex deleted file mode 100644 index 6eb70f029..000000000 --- a/tex/context/base/core-ctx.tex +++ /dev/null @@ -1,22 +0,0 @@ -%D \module -%D [ file=core-ctx, -%D version=2006.08.16, % old stuff -%D title=\CONTEXT\ Core Macros, -%D subtitle=Job Control, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\writestatus{loading}{Context Core Macros / Ctx Job Files} - -%D After some experimenting this code moved into the core. It -%D overloades a few file reading macros and permits runtime -%D conversion and job control. - -\loadmarkfile{core-ctx} - -\endinput diff --git a/tex/context/base/core-dat.tex b/tex/context/base/core-dat.tex index dc39f979f..44a82e1f3 100644 --- a/tex/context/base/core-dat.tex +++ b/tex/context/base/core-dat.tex @@ -13,73 +13,25 @@ % THIS WILL DISAPPEAR, I.E. BE MOVED TO A MODULE -\writestatus{loading}{Context Database Support} +\writestatus{loading}{ConTeXt Core Macros / Database Support} -\startmessages dutch library: databases - title: database - 1: -- - 2: lokaal bestand -- - 3: globaal bestand -- - 4: onbekend bestand -- -\stopmessages +% messages moved -\startmessages english library: databases - title: databases - 1: -- - 2: local file -- - 3: global file -- - 4: unknown file -- -\stopmessages +% messages moved -\startmessages german library: databases - title: Datenbank - 1: -- - 2: lokale Datei -- - 3: globale Datei -- - 4: unbekannte Datei -- -\stopmessages +% messages moved % TOM : -\startmessages czech library: databases - title: databases - 1: -- - 2: local file -- - 3: global file -- - 4: unknown file -- -\stopmessages +% messages moved -\startmessages italian library: databases - title: database - 1: -- - 2: file locale -- - 3: file globale -- - 4: file sconosciuto -- -\stopmessages +% messages moved -\startmessages norwegian library: databases - title: databaser - 1: -- - 2: lokal fil -- - 3: global fil -- - 4: ukjent fil -- -\stopmessages +% messages moved -\startmessages romanian library: databases - title: baze de date - 1: -- - 2: fisier local -- - 3: fisier global -- - 4: fisier necunoscut -- -\stopmessages +% messages moved -\startmessages french library: databases - title: bases de données - 1: -- - 2: fichier local -- - 3: fichier global -- - 4: fichier inconnu -- -\stopmessages +% messages moved \unprotect diff --git a/tex/context/base/core-def.mkii b/tex/context/base/core-def.mkii new file mode 100644 index 000000000..ea2d0ff15 --- /dev/null +++ b/tex/context/base/core-def.mkii @@ -0,0 +1,77 @@ +%D \module +%D [ file=core-def, +%D version=2002.05.07, +%D title=\CONTEXT\ Core Macros, +%D subtitle=Defaults, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Core Macros / Defaults} + +%D Here we collect settings that cannot be done earlier due to +%D depedencies. More code will moved to this module later. + +\unprotect + +\usesymbols[mis,mvs] % 'glm' no longer needed due to lm + +\usesymbols[nav] \setupsymbolset[navigation 1] + +\setupinteraction[\c!symbolset=navigation 1] + +% initialization order: + +%appendtoks \initializeluainstances \to \everyjob +\appendtoks \showcontextbanner \to \everyjob +\appendtoks \initializenewlinechar \to \everyjob +\appendtoks \checksystemcommandmode \to \everyjob +\appendtoks \calculatecurrenttime \to \everyjob +\appendtoks \loadsystemfiles \to \everyjob + +\appendtoks \loadoptionfile \to \everyjob % can load files ! + +\appendtoks \preloadfonts \to \everyjob +\appendtoks \settopskip \to \everyjob +\appendtoks \preloadlanguages \to \everyjob +\appendtoks \preloadspecials \to \everyjob +\appendtoks \openspecialfile \to \everyjob +\appendtoks \openutilities \to \everyjob +\appendtoks \splitjobfilename \to \everyjob +\appendtoks \checknotes \to \everyjob % depends on bodyfont +\appendtoks \initializeMPgraphics \to \everyjob % after loading system files +\appendtoks \reportsystemcommandmode \to \everyjob +\appendtoks \initializemainlanguage \to \everyjob +\appendtoks \settrue\trackfilenames \to \everyjob +\appendtoks \newbackgroundfalse \to \everyjob % global + +\ifdefined\initializepagecounters + \appendtoks \initializepagecounters \to \everyjob +\fi + +\appendtoks \directsetup{*runtime:options} \to \everyjob % we could erase them afterwards % order can change +\appendtoks \directsetup{*runtime:modules} \to \everyjob % we could erase them afterwards % order can change + +\appendtoks \checkpreprocessor \to \everyjob + +%appendtoks \page[\v!last] \page \to \everybye % moved to core-job, we need to do this cleaner +\appendtoks \ifarrangingpages\poparrangedpages\fi \to \everybye +\appendtoks \registerfileinfo[end]\jobname \to \everybye +\appendtoks \savenofpages \to \everybye +\appendtoks \savenofsubpages \to \everybye + +\appendtoks \closeutilities \to \everygoodbye +\appendtoks \stopcopyingblocks \to \everygoodbye +\appendtoks \closespecialfile \to \everygoodbye + +\prependtoks \resetutilities \to \everystarttext % moved 28-02-2002 +\prependtoks \loadtwopassdata \to \everystarttext % moved 28-02-2002 +\appendtoks \checkreferences \to \everystarttext % new 04-12-1999 + +% \appendtoks\everyjob\expandafter{\the\everyjob\checkpreprocessor}\to\everydump + +\protect \endinput diff --git a/tex/context/base/core-def.mkiv b/tex/context/base/core-def.mkiv new file mode 100644 index 000000000..380b733bc --- /dev/null +++ b/tex/context/base/core-def.mkiv @@ -0,0 +1,74 @@ +%D \module +%D [ file=core-def, +%D version=2002.05.07, +%D title=\CONTEXT\ Core Macros, +%D subtitle=Defaults, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Core Macros / Defaults} + +%D Here we collect settings that cannot be done earlier due to +%D depedencies. More code will moved to this module later. + +\unprotect + +\usesymbols[mis,mvs,nav] + +\setupsymbolset[navigation 1] + +\setupinteraction[\c!symbolset=navigation 1] + +% initialization order: + +\appendtoks \showcontextbanner \to \everyjob +\appendtoks \initializenewlinechar \to \everyjob +\appendtoks \checksystemcommandmode \to \everyjob +\appendtoks \calculatecurrenttime \to \everyjob +\appendtoks \loadsystemfiles \to \everyjob +\appendtoks \loadoptionfile \to \everyjob % can load files ! +\appendtoks \preloadfonts \to \everyjob +\appendtoks \settopskip \to \everyjob +\appendtoks \preloadlanguages \to \everyjob +\appendtoks \preloadspecials \to \everyjob +\appendtoks \splitjobfilename \to \everyjob +\appendtoks \checknotes \to \everyjob % depends on bodyfont +\appendtoks \initializeMPgraphics \to \everyjob % after loading system files +\appendtoks \reportsystemcommandmode \to \everyjob +\appendtoks \initializemainlanguage \to \everyjob +\appendtoks \MPLIBregister \to \everyjob +\appendtoks \xmlinitialize \to \everyjob +\appendtoks \settrue\trackfilenames \to \everyjob +\appendtoks \newbackgroundfalse \to \everyjob % global +\appendtoks \initializepagecounters \to \everyjob +\appendtoks \directsetup{*runtime:options} \to \everyjob % we could erase them afterwards % order can change +\appendtoks \directsetup{*runtime:modules} \to \everyjob % we could erase them afterwards % order can change +\appendtoks \checkpreprocessor \to \everyjob + +%appendtoks \page[\v!last] \page \to \everybye % moved to core-job, we need to do this cleaner +\appendtoks \ifarrangingpages\poparrangedpages\fi \to \everybye +\appendtoks \registerfileinfo[end]\jobname \to \everybye + +\prependtoks \resetutilities \to \everystarttext % moved 28-02-2002 + +\appendtoks \MPLIBallocate{1000} \to \everydump + +\prependtoks \resetallattributes \to \everybeforeoutput + +\appendtoks \the\everybackendshipout \to \everyshipout +\prependtoks \the\everylastbackendshipout \to \everylastshipout + +% temporary here: + +\ifx\in \undefined\else \let\normalmathin \in \unexpanded\def\in {\mathortext\normalmathin \dospecialin } \fi +\ifx\at \undefined\else \let\normalmathat \at \unexpanded\def\at {\mathortext\normalmathat \dospecialat } \fi +\ifx\about\undefined\else \let\normalmathabout\about \unexpanded\def\about{\mathortext\normalmathabout\dospecialabout} \fi +\ifx\from \undefined\else \let\normalmathfrom \from \unexpanded\def\from {\mathortext\normalmathfrom \dospecialfrom } \fi +\ifx\over \undefined\else \let\normalmathover \over \unexpanded\def\over {\mathortext\normalmathover \dospecialabout} \fi + +\protect \endinput diff --git a/tex/context/base/core-def.tex b/tex/context/base/core-def.tex deleted file mode 100644 index c7c49858e..000000000 --- a/tex/context/base/core-def.tex +++ /dev/null @@ -1,27 +0,0 @@ -%D \module -%D [ file=core-def, -%D version=2002.05.07, -%D title=\CONTEXT\ Core Macros, -%D subtitle=Defaults, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\writestatus{loading}{Context Core Macros / Defaults} - -%D Here we collect settings that cannot be done earlier due to -%D depedencies. More code will moved to this module later. - -\unprotect - -\usesymbols[mis,mvs] % 'glm' no longer needed due to lm - -\usesymbols[nav] \setupsymbolset[navigation 1] - -\setupinteraction[\c!symbolset=navigation 1] - -\protect \endinput diff --git a/tex/context/base/core-des.tex b/tex/context/base/core-des.tex index 1794800a4..dc7136c40 100644 --- a/tex/context/base/core-des.tex +++ b/tex/context/base/core-des.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Context Core Macros / Descriptions} +\writestatus{loading}{ConTeXt Core Macros / Descriptions} %D In order to be more flexible with theorems Aditya Mahajan added %D support for titles and endsymbols. At the same time we some more @@ -394,7 +394,7 @@ % which calls: \def\@@makedescription#1% - {\postponefootnotes % new, assumes grouping + {\postponenotes % new, assumes grouping \def\currentdescription{#1}% \executeifdefined {@@description\descriptionparameter\c!location} @@ -829,8 +829,7 @@ \def\do@@label[#1][#2]% {\numberparameter{#1}\c!before - \numberparameter{#1}\c!command - {\doattributes{\@@thenumber{#1}}\c!headstyle\c!headcolor{\getvalue{\e!next#1}[#2]}}% + \numberparameter{#1}\c!command{\doattributes{\@@thenumber{#1}}\c!headstyle\c!headcolor{\getvalue{\e!next#1}[#2]}}% \numberparameter{#1}\c!after}% \def\do@@nextlabel[#1][#2]% diff --git a/tex/context/base/core-env.mkii b/tex/context/base/core-env.mkii new file mode 100644 index 000000000..a22594b27 --- /dev/null +++ b/tex/context/base/core-env.mkii @@ -0,0 +1,543 @@ +%D \module +%D [ file=core-env, % was core-new +%D version=1995.01.01, % wrong +%D title=\CONTEXT\ Core Macros, +%D subtitle=New ones, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Core Macros / Environments} + +\unprotect + +% Clean labels: + +\bgroup % some day this will go away / we could use detokenize as well + +% actually we should handle all discretionaries here + +\catcode`:=\@@active + +\gdef\cleanuplabel#1% + {\begingroup + \let:\lettercolon + \xdef\cleanlabel{#1}% + \endgroup} + +\gdef\cleanupprefixedlabel#1#2% + {\begingroup + \let:\lettercolon + \xdef\cleanprefix{#1}% + \xdef\cleanlabel {#2}% + \endgroup} + +\gdef\protectlabels + {\let:\lettercolon} + +\global\def\blabelgroup {\begingroup \let:\lettercolon} +\global\let\elabelgroup \endgroup + +\gdef\labelcsname + {\begingroup\let:\lettercolon + \expandafter\endgroup\csname} + +\gdef\labelvalue#1% + {\labelcsname#1\endcsname} + +\egroup + +%D Modes: +%D +%D \starttyping +%D \enablemode[screen,paper,bound] +%D +%D \doifmodeelse {paper} {this} {that} +%D \doifmode {paper,screen} {this} +%D \doifnotmode {paper,bound} {that} +%D +%D \startmode [list] +%D \stopmode +%D +%D \startnotmode [list] +%D \stopnotmode +%D \stoptyping +%D +%D system modes have a * as prefix +%D +%D Sometimes, we want to prevent a mode for being set. Think +%D of situations where a style enables a mode, but an outer +%D level style does not want that. Preventing can be +%D considered a permanent disabling on forehand. + +\def\@mode@{@md@} + +\def\systemmodeprefix{*} + +\def\disabledmode {0} +\def\enabledmode {1} +\def\preventedmode {2} + +% fast internal ones + +\def\setmode #1{\@EA\let\csname\@mode@#1\endcsname\enabledmode } +\def\resetmode#1{\@EA\let\csname\@mode@#1\endcsname\disabledmode} + +\def\setsystemmode #1{\@EA\let\csname\@mode@\systemmodeprefix#1\endcsname\enabledmode } +\def\resetsystemmode#1{\@EA\let\csname\@mode@\systemmodeprefix#1\endcsname\disabledmode} + +% user ones + +\def\preventmode{\unprotect\dopreventmode} +\def\enablemode {\unprotect\doenablemode } +\def\disablemode{\unprotect\dodisablemode} + +\def\dopreventmode[#1]{\protect\cleanuplabel{#1}\rawprocesscommalist[\cleanlabel]\dodopreventmode} +\def\doenablemode [#1]{\protect\cleanuplabel{#1}\rawprocesscommalist[\cleanlabel]\dodoenablemode } +\def\dodisablemode[#1]{\protect\cleanuplabel{#1}\rawprocesscommalist[\cleanlabel]\dododisablemode} + +\def\dodopreventmode#1% + {\@EA\let\csname\@mode@#1\endcsname\preventedmode} + +\def\dodoenablemode#1% mode can be relax + {\ifcase0\csname\@mode@#1\endcsname\relax + \@EA\let\csname\@mode@#1\endcsname\enabledmode + \fi} + +\def\dododisablemode#1% + {\ifcase0\csname\@mode@#1\endcsname\or + \@EA\let\csname\@mode@#1\endcsname\disabledmode + \fi} + +% handy for mp + +\def\booleanmodevalue#1% can be \relax + {\expandafter\ifx\csname\@mode@#1\endcsname\relax + fals% + \else\ifnum0\csname\@mode@#1\endcsname=0 + fals% + \else + tru% + \fi\fi e} + +% check macros + +\newif\ifcheckedmode + +\def\dodocheckformode#1% + {\ifcase0\csname\@mode@#1\endcsname\or\checkedmodetrue\fi} + +\def\docheckformode#1#2#3% will be sped up with a quit + {\cleanuplabel{#3}% + \protect\checkedmodefalse\rawprocesscommacommand[\cleanlabel]\dodocheckformode + \ifcheckedmode\@EA#1\else\@EA#2\fi} + +\def\dodocheckforallmodes#1% + {\ifcase0\csname\@mode@#1\endcsname\relax\checkedmodefalse\or\or\checkedmodefalse\fi} + +\def\docheckforallmodes#1#2#3% will be sped up with a quit + {\cleanuplabel{#3}% + \protect\checkedmodetrue\rawprocesscommacommand[\cleanlabel]\dodocheckforallmodes + \ifcheckedmode\@EA#1\else\@EA#2\fi} + +% simple ones + +\def\doifmodeelse{\unprotect\dodoifmodeelse} +\def\doifmode {\unprotect\dodoifmode} +\def\doifnotmode {\unprotect\dodoifnotmode} +\def\startmode {\unprotect\dostartmode} +\def\startnotmode{\unprotect\dostartnotmode} + +\def\dodoifmodeelse + {\docheckformode\firstoftwoarguments\secondoftwoarguments} + +\def\dodoifmode + {\docheckformode\firstofoneargument\gobbleoneargument} + +\def\dodoifnotmode + {\docheckformode\gobbleoneargument\firstofoneargument} + +\long\def\dostartmode[#1]% + {\docheckformode\donothing\dostopmode{#1}} + +\long\def\dostartnotmode[#1]% + {\docheckformode\dostopnotmode\donothing{#1}} + +\let\stopmode \donothing +\let\stopnotmode\donothing + +\long\def\dostopmode #1\stopmode {} +\long\def\dostopnotmode#1\stopnotmode{} + +\def\doifallmodeselse{\unprotect\dodoifallmodeselse} +\def\doifallmodes {\unprotect\dodoifallmodes} +\def\doifnotallmodes {\unprotect\dodoifnotallmodes} +\def\startallmodes {\unprotect\dostartallmodes} +\def\startnotallmodes{\unprotect\dostartnotallmodes} + +\def\dodoifallmodeselse + {\docheckforallmodes\firstoftwoarguments\secondoftwoarguments} + +\def\dodoifallmodes + {\docheckforallmodes\firstofoneargument\gobbleoneargument} + +\def\dodoifnotallmodes + {\docheckforallmodes\gobbleoneargument\firstofoneargument} + +\long\def\dostartallmodes[#1]% + {\docheckforallmodes\donothing\dostopallmodes{#1}} + +\long\def\dostartnotallmodes[#1]% + {\docheckforallmodes\dostopnotallmodes\donothing{#1}} + +\let\stopallmodes \donothing +\let\stopnotallmodes\donothing + +\long\def\dostopallmodes #1\stopallmodes {} +\long\def\dostopnotallmodes#1\stopnotallmodes{} + +% Setups + +\let\startsetups\relax % to please dep checker +\let\stopsetups \relax % to please dep checker + +\expanded + {\long\def\@EA\noexpand\csname\e!start\v!setups\endcsname + {\begingroup\noexpand\doifnextoptionalelse + {\noexpand\startsetupsA\@EA\noexpand\csname\e!stop\v!setups\endcsname} + {\noexpand\startsetupsB\@EA\noexpand\csname\e!stop\v!setups\endcsname}}} + +\letvalue{\e!stop\v!setups}\relax + +\unexpanded \def\setups{\doifnextbgroupelse\dosetupsA\dosetupsB} % {..} or [..] +\unexpanded \def\setup {\doifnextbgroupelse\dosetups \dosetupsC} % {..} or [..] + +\def\dosetupsA #1{\cleanuplabel{#1}\processcommacommand[\cleanlabel]\dosetups} % {..} +\def\dosetupsB[#1]{\cleanuplabel{#1}\processcommacommand[\cleanlabel]\dosetups} % [..] +\def\dosetupsC[#1]{\cleanuplabel{#1}\dosetups\cleanlabel} % [..] + +% \def\dosetups#1% the grid option will be extended to other main modes +% {\executeifdefined{\??su\ifgridsnapping\v!grid\fi:#1} +% {\executeifdefined{\??su :#1}\gobbleoneargument}\empty} % takes one argument +% +% \def\setupwithargument#1% the grid option will be extended to other main modes +% {\executeifdefined{\??su:#1}\gobbleoneargument} + +% better: + +% \def\dosetups#1% the grid option will be extended to other main modes +% {\executeifdefined{\??su\ifgridsnapping\v!grid\fi:#1} +% {\executeifdefined{\??su :#1}\gobbleoneargument}\empty} % takes one argument +% +% \def\setupwithargument#1% the grid option will be extended to other main modes +% {\executeifdefined{\??su:#1}\gobbleoneargument} + +% faster: + +\letvalue{\??su:\letterpercent}\gobbleoneargument + +\def\dosetups#1% the grid option will be extended to other main modes + {\csname\??su + \ifgridsnapping + \ifcsname\??su\v!grid:#1\endcsname\v!grid:#1\else\ifcsname\??su:#1\endcsname:#1\else:\letterpercent\fi\fi + \else + \ifcsname\??su:#1\endcsname:#1\else:\letterpercent\fi + \fi + \endcsname\empty} % takes one argument + +\def\setupwithargument#1% the grid option will be extended to other main modes + {\csname\??su:\ifcsname\??su:#1\endcsname#1\else\letterpercent\fi\endcsname} + +\let\directsetup\dosetups + +% somehow fails ... +% +% \letvalue{\??su:..}\gobbleoneargument +% +% \def\dosetups#1% the grid option will be extended to other main modes +% {\csname \??su +% \ifcsname\??su\ifgridsnapping\v!grid\fi:#1\endcsname\v!grid:#1\else +% \ifcsname\??su :#1\endcsname :#1\else +% :..\fi\fi +% \endcsname\empty} % takes one argument +% +% \def\setupwithargument#1% the grid option will be extended to other main modes +% {\csname\??su:\ifcsname\??su:#1\endcsname#1\else..\fi\endcsname} + +\let\directsetup\dosetups + +\def\doifsetupselse#1% to be done: grid + {\doifdefinedelse{\??su:#1}} + +\chardef\setupseolmode\plusone + +\def\startsetups {\xxstartsetups\plusone \stopsetups } \let\stopsetups \relax +\def\startlocalsetups{\xxstartsetups\plusone \stoplocalsetups} \let\stoplocalsetups\relax +\def\startrawsetups {\xxstartsetups\zerocount\stoprawsetups } \let\stoprawsetups \relax +\def\startxmlsetups {\xxstartsetups\plustwo \stopxmlsetups } \let\stopxmlsetups \relax + +\def\xxstartsetups#1#2% + {\begingroup\chardef\setupseolmode#1\doifnextoptionalelse{\startsetupsA#2}{\startsetupsB#2}} + +\def\startsetupsA#1% [ ] delimited + {\ifcase\setupseolmode\or\catcode`\^^M\@@ignore\or\catcode`\^^M\@@ignore\catcode`\|\@@other\fi + \dotripleempty\dostartsetups[#1]} + +\def\startsetupsB#1#2 % space delimited + {\ifcase\setupseolmode\or\catcode`\^^M\@@ignore\or\catcode`\^^M\@@ignore\catcode`\|\@@other\fi + \dodostartsetups#1\empty{#2}} + +\def\startsetupsC[#1][#2][#3]{\dodostartsetups#1{#2}{#3}} % [..] [..] +\def\startsetupsD[#1][#2][#3]{\dodostartsetups#1\empty{#2}} % [..] + +\def\dostartsetups + {\ifthirdargument\@EA\startsetupsC\else\@EA\startsetupsD\fi} + +% \long\def\dodostartsetups#1#2#3% watch out: not \grabuntil +% {\dograbuntil#1{\endgroup\dodoglobal\long\setvalue{\??su#2:#3}}} % \doglobal +% +% better: + +% \long\def\dodostartsetups#1#2#3% watch out: not \grabuntil +% {\cleanuplabel{\??su#2:#3}\dograbuntil#1{\endgroup\dodoglobal\long\setvalue\cleanlabel}} % \doglobal + +% \long\def\dodostartsetups#1#2#3% +% {\cleanuplabel{\??su#2:#3}% +% \long\def\dododostartsetups##1#1{\endgroup\dodoglobal\long\setvalue\cleanlabel####1{##1}}\dododostartsetups} + +\long\def\dodostartsetups#1#2#3% + {\cleanuplabel{\??su#2:#3}% + \long\def\dododostartsetups##1#1% + {\endgroup + \dodoglobal % bah + \long\expandafter\setvalue\expandafter\cleanlabel\expandafter####\expandafter1\expandafter{##1}}% + \dododostartsetups\empty} % the empty trick prevents the { } in {arg} from being eaten up + +\def\systemsetupsprefix{*} + +\def\systemsetups#1{\dosetups{\systemsetupsprefix#1}} + +\def\resetsetups[#1]% see x-fo for usage + {\ifundefined{\??su\ifgridsnapping\v!grid\fi:#1}% + \dodoglobal\letbeundefined{\??su:#1}% + \else + \dodoglobal\letbeundefined{\??su\ifgridsnapping\v!grid\fi:#1}% + \fi} + +% or +% +% \def\resetsetups[#1]% +% {\letbeundefined +% {\??su:% +% \ifundefined{\??su\ifgridsnapping\v!grid\fi:#1}#1\else\ifgridsnapping\v!grid\fi% +% #1}} + +%D new and beta and will become a module instead + +\def\defineshortcut + {\dotripleargument\dodefineshortcut} + +\def\dodefineshortcut[#1][#2][#3]% + {\ifthirdargument + \doifelsenothing{#1} + {\dododefineshortcut[<>][#2][#3]} + {\dododefineshortcut[#1][#2][#3]}% + \else\ifsecondargument + \dododefineshortcut[<>][#1][#2]% + \else + \dododefineshortcut[<>][][#1]% + \fi\fi} + +\def\dododefineshortcut[#1#2][#3][#4]% #1 is the trigger, #2 the delimiter/tag + {\doifundefined{\??te\??te\string#2}{\letvalue{\??te\??te\string#2}=#1}% + \defineactivecharacter #1 {\@EA\doshortcut\string#2} % + \getparameters + [\??te\string#2#3] + [\c!commands=,\c!command=,\c!style=,\c!color=,#4]} + +\def\doshortcut#1% + {\ifmmode + \getvalue{\??te\??te#1}% + \else + \bgroup + \catcode`#1=\@@other + \def\dodoshortcut##1#1% + {\def\shorttag{\??te#1}% + \def\shortcut{##1}% + \dododoshortcut##1:\end}% + \@EA\dodoshortcut + \fi} + +\def\dododoshortcut#1:#2\end + {\doifelsenothing{#2} + {\doifundefinedelse{\shorttag\c!commands} + {\shortcut} + {\@EA\dodododoshortcut\@EA\shorttag\@EA:\shortcut:\end}} + {\doifundefinedelse{\shorttag#1\c!commands} + {\shortcut} + {\dodododoshortcut\shorttag#1:#2\end}}% + \egroup} + +\def\dodododoshortcut#1:#2:\end + {\getvalue{#1\c!commands}% + \doattributes{#1}\c!style\c!color{\getvalue{#1\c!command}{#2}}} + +%D \defineshortcut [style=type] +%D \defineshortcut [b] [style=bold] +%D \defineshortcut [e] [style=\em] +%D \defineshortcut [t] [style=type] +%D \defineshortcut [c] [style=cap] +%D \defineshortcut [k] [style=cap] +%D \defineshortcut [u] [style=type,command=\hyphenatedurl] +%D +%D \startlines +%D testVariables are saved using in the previously defined table and passed
@@ -32,39 +33,206 @@ directly access the variable using a
..
) -\long\def\dostartallmodes[#1]% - {\docheckforallmodes\donothing\dostopallmodes{#1}} +\newtoks \everybeginofpar +\newtoks \everyendofpar +%newtoks \everyparflush -\long\def\dostartnotallmodes[#1]% - {\docheckforallmodes\dostopnotallmodes\donothing{#1}} +\def\bpar{\the\everybeginofpar\ignorespaces} % may interfere with \everypar +\def\epar{\ifhmode\removeunwantedspaces\the\everyendofpar\fi} % test prevents problems with \bpar\epar -\let\stopallmodes \donothing -\let\stopnotallmodes\donothing +%D Lists: -\long\def\dostopallmodes #1\stopallmodes {} -\long\def\dostopnotallmodes#1\stopnotallmodes{} +\newtoks \everylistentry +\newtoks \everysavesortkeys -%D \macros -%D {every...} -%D -%D A few every's. - -\newevery \everyshipout \relax -\newevery \everybeforeshipout \relax -\newevery \everyaftershipout \relax -\newevery \everyfirstshipout \relax -\newevery \everylastshipout \relax -\newevery \everybye \relax -\newevery \everygoodbye \relax -\newevery \everystarttext \relax -\newevery \everystoptext \relax -\newevery \everyforgetall \relax -\newevery \everybeforepagebody \relax -\newevery \everyafterpagebody \relax -\newevery \everybeforeutilityread \relax -\newevery \everyafterutilityread \relax - -\let \everypagebody \everybeforepagebody % backward compatible - -%newevery \everybeforeutilitywrite \relax - -\newevery \everycleanupfeatures \relax -\newevery \everyinsidefloat \relax -\newevery \everyheadstart \relax -\newevery \everyendoftextbody \relax -\newevery \everybeginofpar \relax -\newevery \everyendofpar \relax -\newevery \everylistentry \relax -\newevery \everymarking \relax -\newevery \everysavesortkeys \relax - -\newevery \everyfont \relax -\newevery \everybodyfont \EveryBodyFont -\newevery \everyglobalbodyfont \relax -\newevery \everyfontswitch \EveryFontSwitch -\newevery \everydefinedfont \relax - -\newevery \everybeforeoutput \relax -\newevery \everyafteroutput \relax - -\newevery \everybeforedisplayformula \relax +%D Marks: -\def\cleanupfeatures{\the\everycleanupfeatures} -\def\forgetall {\the\everyforgetall} +\newtoks \everymarking -%D State mess: +%D Fonts: -\newtoks \everypushsomestate -\newtoks \everypopsomestate +\newtoks \everyfont +\newtoks \everyglobalbodyfont +\newtoks \everydefinedfont -\def\pushsomestates{\the\everypushsomestate} -\def\popsomestates {\the\everypopsomestate } +\newevery \everybodyfont \EveryBodyFont +\newevery \everyfontswitch \EveryFontSwitch -%D For shared \type {\everymath} and \type {\everydisplay}: +%D Math: -\newevery \everymathematics \relax +\newtoks \everybeforedisplayformula +\newtoks \everymathematics \prependtoks \the\everymathematics \to \everymath \prependtoks \the\everymathematics \to \everydisplay -% \newevery \everyparflush \relax % collected nodes +%D Tables -%D Experimental (used in xml..
+\newtoks \everytable -\def\bpar{\the\everybeginofpar\ignorespaces} % may interfere with \everypar -\def\epar{\ifhmode\removeunwantedspaces\the\everyendofpar\fi} % test prevents problems with \bpar\epar +%D State mess: + +\newtoks \everypushsomestate +\newtoks \everypopsomestate + +\def\pushsomestates{\the\everypushsomestate} +\def\popsomestates {\the\everypopsomestate } %D More generic (used to be pushcolor etc) @@ -337,8 +154,9 @@ %D %D New. Some work needs to be done. +% not in mkiv + \def\defineinputmode[#1]{\@EA\newtoks\csname every#1inputmode\endcsname} -%def\setinputmode [#1]{\the \csname every#1inputmode\endcsname} \def\setinputmode [#1]{\the\executeifdefined{every#1inputmode}\emptytoks} \defineinputmode [TEX] @@ -352,7 +170,7 @@ %D We disable trial typesetting in the output routine, %D just to be sure. -% defined in syst-ext +\newif\iftrialtypesetting \prependtoks \trialtypesettingfalse \to \everybeforepagebody @@ -372,7 +190,7 @@ %D %D We need this one even if no \XML\ is supported. -\newif\ifprocessingXML +\newif\ifprocessingXML % old way %D \macros %D {ifproductionrun} @@ -382,7 +200,9 @@ \ifx\protectionlevel\undefined \newcount\protectionlevel \fi -\newif\ifproductionrun \appendtoks \productionruntrue \to \everydump +\newif\ifproductionrun + +\appendtoks \productionruntrue \to \everydump \appendtoks \ifcase\protectionlevel\else\reportunprotection\fi \to \everydump @@ -393,8 +213,8 @@ %D This one is relatively new and will be used as a more %D robust test for inner situations. -\newif \ifboxedcontent -\newevery \everyboxedcontent \relax +\newif \ifboxedcontent +\newtoks\everyboxedcontent \appendtoks \boxedcontenttrue \to \everyboxedcontent @@ -402,145 +222,12 @@ \let\stopboxedcontent \egroup %D \macros -%D {fastmode} -%D -%D The command \type {\fastmode} disables some time consuming -%D typesetting. - -\newevery \everyfastmode \relax - -\newif\iffastmode - -\def\fastmode - {\fastmodetrue - \the\everyfastmode} - -\def\silentmode % ook hier \everysilentmode net als \fastmode - {\showmessagesfalse - \showwarningsfalse - \let\writestatus\gobbletwoarguments} - -%D \macros -%D {pdfoutput} -%D -%D There are some fundamental differences between producing -%D \DVI\ and \PDF\ output, especially when we use \PDFTEX, like -%D object reuse, one pass graphic inclusion and the lack of a -%D postprocessing stage. Because we must make sure that -%D \CONTEXT\ knows what it's up to, we always default to \DVI\ -%D mode, even when users explicitly ask for \PDF\ output in the -%D \PDFTEX\ configuration file. - -% we assume no pdfcontext or whatever -% -% \ifx\pdfoutput\undefined \else -% \prependtoks \pdfoutput=0 \to \everyjob -% \fi - -%D \macros -%D {setvariables,getvariable,getvariabledefault} +%D {fastmode,silentmode} %D -%D \starttyping -%D \setvariables[xx][title=] -%D \setvariables[xx][title=test test] -%D \setvariables[xx][title=test $x=1$ test] % fatal error reported -%D \setvariables[xx][title=test {$x=1$} test] -%D \setvariables[xx][title] % fatal error reported -%D \setvariables[xx][titletitel=e] -%D \stoptyping - -\def\??vars{@@vars} - -\def\setvariables {\dotripleargument\dosetvariables[\getrawparameters ]} -\def\setevariables{\dotripleargument\dosetvariables[\getraweparameters]} -\def\setgvariables{\dotripleargument\dosetvariables[\getrawgparameters]} -\def\setxvariables{\dotripleargument\dosetvariables[\getrawxparameters]} - -\def\globalsetvariables % obsolete - {\dotripleargument\dosetvariables[\globalgetrawparameters]} - -% \long\def\dosetvariables[#1][#2][#3]% -% {\errorisfataltrue -% \def\currentvariableclass{#2}% -% \getvariable{#2}\s!reset -% #1[\??vars:#2:][#3]% -% \getvariable{#2}\s!set -% \errorisfatalfalse} -% -% permit nested definitions while preventing nested set/reset -% -% wrong: -% -% \long\def\dosetvariables[#1][#2][#3]% -% {\errorisfataltrue -% \getrawparameters[\??vars:*:][\s!reset=*,\s!set=*,#3]% -% \doifelse{\getvalue{\??vars:*:\s!reset}\getvalue{\??vars:*:\s!set}}{**} -% {\doifelse{#2}\currentvariableclass -% {#1[\??vars:#2:][#3]} -% {\pushmacro\currentvariableclass -% \def\currentvariableclass{#2}% -% \getvariable{#2}\s!reset -% #1[\??vars:#2:][#3]% -% \getvariable{#2}\s!set -% \popmacro\currentvariableclass}}% -% {#1[\??vars:#2:][#3]}% -% \errorisfatalfalse} - -\long\def\dosetvariables[#1][#2][#3]% tricky, test on s-pre-60 - {\errorisfataltrue - \doifelse{#2}\currentvariableclass - {#1[\??vars:#2:][#3]}% - {\pushmacro\currentvariableclass - \def\currentvariableclass{#2}% - \getvariable{#2}\s!reset - #1[\??vars:#2:][#3]% - \getvariable{#2}\s!set - \popmacro\currentvariableclass}% - \errorisfatalfalse} - -\long\def\setvariable #1#2#3{\long\setvalue {\??vars:#1:#2}{#3}} -\long\def\setevariable#1#2#3{\long\setevalue{\??vars:#1:#2}{#3}} -\long\def\setgvariable#1#2#3{\long\setgvalue{\??vars:#1:#2}{#3}} -\long\def\setxvariable#1#2#3{\long\setxvalue{\??vars:#1:#2}{#3}} - -\def\getvariable#1#2% to be sped up - {\csname - \ifcsname\??vars:#1:#2\endcsname\??vars:#1:#2\else\s!empty\fi - \endcsname} - -\def\showvariable#1#2% - {\showvalue{\ifcsname\??vars:#1:#2\endcsname\??vars:#1:#2\else\s!empty\fi}} - -\let\currentvariableclass\empty - -%D \macros -%D {doifelsevariable,doifvariable,doifnotvariable} -%D -%D A few trivial macros: - -\def\doifelsevariable#1#2% - {\ifcsname\??vars:#1:#2\endcsname - \expandafter\firstoftwoarguments - \else - \expandafter\secondoftwoarguments - \fi} - -\def\doifvariable#1#2% - {\ifcsname\??vars:#1:#2\endcsname - \expandafter\firstofoneargument - \else - \expandafter\gobbleoneargument - \fi} - -\def\doifnotvariable#1#2% - {\ifcsname\??vars:#1:#2\endcsname - \expandafter\gobbleoneargument - \else - \expandafter\firstofoneargument - \fi} +%D These commands are obsolete. -\def\getvariabledefault#1#2% #3% can be command, so no ifcsname here - {\executeifdefined{\??vars:#1:#2}}% {#3} +\let\fastmode \relax +\let\silentmode\relax %D \macros %D {defineselector,setupselector} @@ -571,95 +258,20 @@ {\executeifdefined{\??sx#1\c!max}1} {\executeifdefined{\??sx#1\c!n }1}} -%D \macros -%D {checkvariables} -%D -%D I'll probably forget that this on exists. - -\def\checkvariables - {\dodoubleargument\docheckvariables} - -\def\docheckvariables - {\dogetparameters\docheckrawvalue} - -\def\docheckrawvalue#1#2#3% - {\doifundefined {\??vars:#1:#2}{\setvalue{\??vars:#1:#2}{#3}} - {\doifvaluenothing{\??vars:#1:#2}{\setvalue{\??vars:#1:#2}{#3}}}} - %D We store some original meanings, maybe in \type %D {math-ini}. -\let\normalat \at -\let\normalin \in -\let\normalfrom \from -\let\normalover \over -\let\normalabout \about - -\let\normalabove \above -\let\normalatop \atop - -\let\normaloverwithdelims \overwithdelims -\let\normalabovewithdelims\abovewithdelims -\let\normalatopwithdelims \atopwithdelims +\let\normalat \at +\let\normalin \in +\let\normalfrom \from +%let\normalover \over +\let\normalabout\about %D Add-ons: \let\startlayoutcomponent\gobbletwoarguments \let\stoplayoutcomponent \relax - -%D Label cleanup: -\bgroup % some day this will go away / we could use detokenize as well - -% actually we should handle all discretionaries here - -\catcode`:=\@@active - -\gdef\cleanuplabel#1% - {\begingroup - \let:\lettercolon - \xdef\cleanlabel{#1}% - \endgroup} - -\gdef\cleanupprefixedlabel#1#2% - {\begingroup - \let:\lettercolon - \xdef\cleanprefix{#1}% - \xdef\cleanlabel {#2}% - \endgroup} - -\gdef\protectlabels - {\let:\lettercolon} - -\global\def\blabelgroup {\begingroup \let:\lettercolon} -\global\let\elabelgroup \endgroup - -\gdef\labelcsname - {\begingroup\let:\lettercolon - \expandafter\endgroup\csname} - -\gdef\labelvalue#1% - {\labelcsname#1\endcsname} - -\egroup - -%D TO BE TESTED FIRST (needs changes in usage too) - -% \def\cleanuplabel#1% -% {\edef\cleanlabel{\detokenize{#1}}} -% -% \def\cleanupprefixedlabel#1#2% -% {\edef\cleanprefix{\detokenize{#1}}% -% \edef\cleanlabel {\detokenize{#2}}} -% -% \def\labelvalue#1% -% {\csname\detokenize{#1}\endcsname} -% -% \let\protectlabels\donothing -% -% \def\blabelgroup {\begingroup} % why no \let ? -% \let\elabelgroup \endgroup - %D Concepts: \chardef\conceptmode\zerocount diff --git a/tex/context/base/core-ver.mkii b/tex/context/base/core-ver.mkii index 4e51c934c..dd8f5f84f 100644 --- a/tex/context/base/core-ver.mkii +++ b/tex/context/base/core-ver.mkii @@ -11,12 +11,51 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. +\writestatus{loading}{ConTeXt Core Macros / Verbatim} + \unprotect -% uses \prettyidentifier and sets \setupprettytype +\ifx\startlinenumbering\undefined \let\startlinenumbering\relax \fi +\ifx\stoplinenumbering \undefined \let\stoplinenumbering\relax \fi +\ifx\setuplinenumbering\undefined \def\setuplinenumbering[#1]{} \fi + +% \type{Once we found ourselves defining similar cache constructs +several times, containers were introduced. Containers are used +to collect tables in memory and reuse them when possible based +on (unique) hashes (to be provided by the calling function).
+ +Caching to disk is disabled by default. Version numbers are +stored in the saved table which makes it possible to change the +table structures without bothering about the disk cache.
+ +Examples of usage can be found in the font related code.
+--ldx]]-- + +containers = containers or { } + +containers.usecache = true + +local function report(container,tag,name) + if trace_cache or trace_containers then + logs.report(format("%s cache",container.subcategory),"%s: %s",tag,name or 'invalid') + end +end + +local allocated = { } + +-- tracing + +function containers.define(category, subcategory, version, enabled) + return function() + if category and subcategory then + local c = allocated[category] + if not c then + c = { } + allocated[category] = c + end + local s = c[subcategory] + if not s then + s = { + category = category, + subcategory = subcategory, + storage = { }, + enabled = enabled, + version = version or 1.000, + trace = false, + path = caches and caches.setpath and caches.setpath(category,subcategory), + } + c[subcategory] = s + end + return s + else + return nil + end + end +end + +function containers.is_usable(container, name) + return container.enabled and caches and caches.iswritable(container.path, name) +end + +function containers.is_valid(container, name) + if name and name ~= "" then + local storage = container.storage[name] + return storage and not table.is_empty(storage) and storage.cache_version == container.version + else + return false + end +end + +function containers.read(container,name) + if container.enabled and caches and not container.storage[name] and containers.usecache then + container.storage[name] = caches.loaddata(container.path,name) + if containers.is_valid(container,name) then + report(container,"loaded",name) + else + container.storage[name] = nil + end + end + if container.storage[name] then + report(container,"reusing",name) + end + return container.storage[name] +end + +function containers.write(container, name, data) + if data then + data.cache_version = container.version + if container.enabled and caches then + local unique, shared = data.unique, data.shared + data.unique, data.shared = nil, nil + caches.savedata(container.path, name, data) + report(container,"saved",name) + data.unique, data.shared = unique, shared + end + report(container,"stored",name) + container.storage[name] = data + end + return data +end + +function containers.content(container,name) + return container.storage[name] +end + +function containers.cleanname(name) + return (gsub(lower(name),"[^%w%d]+","-")) +end diff --git a/tex/context/base/data-crl.lua b/tex/context/base/data-crl.lua new file mode 100644 index 000000000..5cad241a6 --- /dev/null +++ b/tex/context/base/data-crl.lua @@ -0,0 +1,58 @@ +if not modules then modules = { } end modules ['data-crl'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +curl = curl or { } + +curl.cached = { } +curl.cachepath = caches.definepath("curl") + +local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders + +function curl.fetch(protocol, name) + local cachename = curl.cachepath() .. "/" .. name:gsub("[^%a%d%.]+","-") +-- cachename = cachename:gsub("[\\/]", io.fileseparator) + cachename = cachename:gsub("[\\]", "/") -- cleanup + if not curl.cached[name] then + if not io.exists(cachename) then + curl.cached[name] = cachename + local command = "curl --silent --create-dirs --output " .. cachename .. " " .. name -- no protocol .. "://" + os.spawn(command) + end + if io.exists(cachename) then + curl.cached[name] = cachename + else + curl.cached[name] = "" + end + end + return curl.cached[name] +end + +function finders.curl(protocol,filename) + local foundname = curl.fetch(protocol, filename) + return finders.generic(protocol,foundname,filetype) +end + +function openers.curl(protocol,filename) + return openers.generic(protocol,filename) +end + +function loaders.curl(protocol,filename) + return loaders.generic(protocol,filename) +end + +-- todo: metamethod + +function curl.install(protocol) + finders[protocol] = function (filename,filetype) return finders.curl(protocol,filename) end + openers[protocol] = function (filename) return openers.curl(protocol,filename) end + loaders[protocol] = function (filename) return loaders.curl(protocol,filename) end +end + +curl.install('http') +curl.install('https') +curl.install('ftp') diff --git a/tex/context/base/data-ctx.lua b/tex/context/base/data-ctx.lua new file mode 100644 index 000000000..00d307b6d --- /dev/null +++ b/tex/context/base/data-ctx.lua @@ -0,0 +1,29 @@ +if not modules then modules = { } end modules ['data-ctx'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local format = string.format + +function resolvers.save_used_files_in_trees(filename,jobname) + if not filename then filename = 'luatex.jlg' end + local found = instance.foundintrees + local f = io.open(filename,'w') + if f then + f:write("\n") + f:write("This file is used when we want the input handlers to behave like
+
How about just forgetting about them?
+--ldx]]-- + +local suffixes = resolvers.suffixes +local formats = resolvers.formats + +suffixes['gf'] = { 'If you wondered abou tsome of the previous mappings, how about +the next bunch:
+--ldx]]-- + +formats['bib'] = '' +formats['bst'] = '' +formats['mft'] = '' +formats['ist'] = '' +formats['web'] = '' +formats['cweb'] = '' +formats['MetaPost support'] = '' +formats['TeX system documentation'] = '' +formats['TeX system sources'] = '' +formats['Troff fonts'] = '' +formats['dvips config'] = '' +formats['graphic/figure'] = '' +formats['ls-R'] = '' +formats['other text files'] = '' +formats['other binary files'] = '' + +formats['gf'] = '' +formats['pk'] = '' +formats['base'] = 'MFBASES' +formats['cnf'] = '' +formats['mem'] = 'MPMEMS' +formats['mf'] = 'MFINPUTS' +formats['mfpool'] = 'MFPOOL' +formats['mppool'] = 'MPPOOL' +formats['texpool'] = 'TEXPOOL' +formats['PostScript header'] = 'TEXPSHEADERS' +formats['cmap files'] = 'CMAPFONTS' +formats['type42 fonts'] = 'T42FONTS' +formats['web2c files'] = 'WEB2C' +formats['pdftex config'] = 'PDFTEXCONFIG' +formats['texmfscripts'] = 'TEXMFSCRIPTS' +formats['bitmap font'] = '' +formats['lig files'] = 'LIGFONTS' diff --git a/tex/context/base/data-lst.lua b/tex/context/base/data-lst.lua new file mode 100644 index 000000000..10d3ea479 --- /dev/null +++ b/tex/context/base/data-lst.lua @@ -0,0 +1,58 @@ +if not modules then modules = { } end modules ['data-lst'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- used in mtxrun + +local find, concat, upper, format = string.find, table.concat, string.upper, string.format + +resolvers.listers = resolvers.listers or { } + +local function tabstr(str) + if type(str) == 'table' then + return concat(str," | ") + else + return str + end +end + +local function list(list,report) + local instance = resolvers.instance + local pat = upper(pattern or "","") + local report = report or texio.write_nl + for _,key in pairs(table.sortedkeys(list)) do + if instance.pattern == "" or find(upper(key),pat) then + if instance.kpseonly then + if instance.kpsevars[key] then + report(format("%s=%s",key,tabstr(list[key]))) + end + else + report(format('%s %s=%s',(instance.kpsevars[key] and 'K') or 'E',key,tabstr(list[key]))) + end + end + end +end + +function resolvers.listers.variables () list(resolvers.instance.variables ) end +function resolvers.listers.expansions() list(resolvers.instance.expansions) end + +function resolvers.listers.configurations(report) + local report = report or texio.write_nl + local instance = resolvers.instance + for _,key in ipairs(table.sortedkeys(instance.kpsevars)) do + if not instance.pattern or (instance.pattern=="") or find(key,instance.pattern) then + report(format("%s\n",key)) + for i,c in ipairs(instance.order) do + local str = c[key] + if str then + report(format("\t%s\t%s",i,str)) + end + end + report("") + end + end +end diff --git a/tex/context/base/data-lua.lua b/tex/context/base/data-lua.lua new file mode 100644 index 000000000..86231b3a3 --- /dev/null +++ b/tex/context/base/data-lua.lua @@ -0,0 +1,55 @@ +if not modules then modules = { } end modules ['data-lua'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- some loading stuff ... we might move this one to slot 1 depending +-- on the developments (the loaders must not trigger kpse); we could +-- of course use a more extensive lib path spec + +local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end) + +local gsub = string.gsub + +local libformats = { 'luatexlibs', 'tex', 'texmfscripts', 'othertextfiles' } +local libpaths = file.split_path(package.path) + +package.loaders[#package.loaders+1] = function(name) + for i=1,#libformats do + local format = libformats[i] + local resolved = resolvers.find_file(name,format) or "" + if resolved ~= "" then + if trace_locating then + logs.report("fileio","! lib '%s' located via environment: '%s'",name,resolved) + end + return function() return dofile(resolved) end + end + end + local simple = file.removesuffix(name) + for i=1,#libpaths do + local resolved = gsub(libpaths[i],"?",simple) + if resolvers.isreadable.file(resolved) then + if trace_locating then + logs.report("fileio","! lib '%s' located via 'package.path': '%s'",name,resolved) + end + return function() return dofile(resolved) end + end + end + -- just in case the distribution is messed up + local resolved = resolvers.find_file(file.basename(name),'luatexlibs') or "" + if resolved ~= "" then + if trace_locating then + logs.report("fileio","! lib '%s' located by basename via environment: '%s'",name,resolved) + end + return function() return dofile(resolved) end + end + if trace_locating then + logs.report("fileio",'? unable to locate lib: %s',name) + end + return "unable to locate " .. name +end + +resolvers.loadlualib = require diff --git a/tex/context/base/data-out.lua b/tex/context/base/data-out.lua new file mode 100644 index 000000000..b774e25fc --- /dev/null +++ b/tex/context/base/data-out.lua @@ -0,0 +1,10 @@ +if not modules then modules = { } end modules ['data-out'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +outputs = outputs or { } + diff --git a/tex/context/base/data-pre.lua b/tex/context/base/data-pre.lua new file mode 100644 index 000000000..deee9ebf4 --- /dev/null +++ b/tex/context/base/data-pre.lua @@ -0,0 +1,90 @@ +if not modules then modules = { } end modules ['data-res'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +--~ print(resolvers.resolve("abc env:tmp file:cont-en.tex path:cont-en.tex full:cont-en.tex rel:zapf/one/p-chars.tex")) + +local upper, lower, gsub = string.upper, string.lower, string.gsub + +local prefixes = { } + +prefixes.environment = function(str) + return resolvers.clean_path(os.getenv(str) or os.getenv(upper(str)) or os.getenv(lower(str)) or "") +end + +prefixes.relative = function(str,n) + if io.exists(str) then + -- nothing + elseif io.exists("./" .. str) then + str = "./" .. str + else + local p = "../" + for i=1,n or 2 do + if io.exists(p .. str) then + str = p .. str + break + else + p = p .. "../" + end + end + end + return resolvers.clean_path(str) +end + +prefixes.locate = function(str) + local fullname = resolvers.find_given_file(str) or "" + return resolvers.clean_path((fullname ~= "" and fullname) or str) +end + +prefixes.filename = function(str) + local fullname = resolvers.find_given_file(str) or "" + return resolvers.clean_path(file.basename((fullname ~= "" and fullname) or str)) +end + +prefixes.pathname = function(str) + local fullname = resolvers.find_given_file(str) or "" + return resolvers.clean_path(file.dirname((fullname ~= "" and fullname) or str)) +end + +prefixes.env = prefixes.environment +prefixes.rel = prefixes.relative +prefixes.loc = prefixes.locate +prefixes.kpse = prefixes.locate +prefixes.full = prefixes.locate +prefixes.file = prefixes.filename +prefixes.path = prefixes.pathname + +local function _resolve_(method,target) + if prefixes[method] then + return prefixes[method](target) + else + return method .. ":" .. target + end +end + +local function resolve(str) + if type(str) == "table" then + for k, v in pairs(str) do -- ipairs + str[k] = resolve(v) or v + end + elseif str and str ~= "" then + str = gsub(str,"([a-z]+):([^ \"\']*)",_resolve_) + end + return str +end + +resolvers.resolve = resolve + +if os.uname then + + for k, v in pairs(os.uname()) do + if not prefixes[k] then + prefixes[k] = function() return v end + end + end + +end diff --git a/tex/context/base/data-res.lua b/tex/context/base/data-res.lua new file mode 100644 index 000000000..0981881a2 --- /dev/null +++ b/tex/context/base/data-res.lua @@ -0,0 +1,2029 @@ +if not modules then modules = { } end modules ['data-inp'] = { + version = 1.001, + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files", + comment = "companion to luat-lib.tex", +} + +-- After a few years using the code the large luat-inp.lua file +-- has been split up a bit. In the process some functionality was +-- dropped: +-- +-- * support for reading lsr files +-- * selective scanning (subtrees) +-- * some public auxiliary functions were made private +-- +-- TODO: os.getenv -> os.env[] +-- TODO: instances.[hashes,cnffiles,configurations,522] -> ipairs (alles check, sneller) +-- TODO: check escaping in find etc, too much, too slow + +-- This lib is multi-purpose and can be loaded again later on so that +-- additional functionality becomes available. We will split thislogs.report("fileio", +-- module in components once we're done with prototyping. This is the +-- first code I wrote for LuaTeX, so it needs some cleanup. Before changing +-- something in this module one can best check with Taco or Hans first; there +-- is some nasty trickery going on that relates to traditional kpse support. + +-- To be considered: hash key lowercase, first entry in table filename +-- (any case), rest paths (so no need for optimization). Or maybe a +-- separate table that matches lowercase names to mixed case when +-- present. In that case the lower() cases can go away. I will do that +-- only when we run into problems with names ... well ... Iwona-Regular. + +-- Beware, loading and saving is overloaded in luat-tmp! + +local format, gsub, find, lower, upper, match, gmatch = string.format, string.gsub, string.find, string.lower, string.upper, string.match, string.gmatch +local concat, insert, sortedkeys = table.concat, table.insert, table.sortedkeys +local next, type = next, type + +local trace_locating, trace_detail, trace_verbose = false, false, false + +trackers.register("resolvers.verbose", function(v) trace_verbose = v end) +trackers.register("resolvers.locating", function(v) trace_locating = v trackers.enable("resolvers.verbose") end) +trackers.register("resolvers.detail", function(v) trace_detail = v trackers.enable("resolvers.verbose,resolvers.detail") end) + +if not resolvers then + resolvers = { + suffixes = { }, + formats = { }, + dangerous = { }, + suffixmap = { }, + alternatives = { }, + locators = { }, -- locate databases + hashers = { }, -- load databases + generators = { }, -- generate databases + } +end + +local resolvers = resolvers + +resolvers.locators .notfound = { nil } +resolvers.hashers .notfound = { nil } +resolvers.generators.notfound = { nil } + +resolvers.cacheversion = '1.0.1' +resolvers.cnfname = 'texmf.cnf' +resolvers.luaname = 'texmfcnf.lua' +resolvers.homedir = os.env[os.platform == "windows" and 'USERPROFILE'] or os.env['HOME'] or '~' +resolvers.cnfdefault = '{$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,.local,}/web2c}' + +local dummy_path_expr = "^!*unset/*$" + +local formats = resolvers.formats +local suffixes = resolvers.suffixes +local dangerous = resolvers.dangerous +local suffixmap = resolvers.suffixmap +local alternatives = resolvers.alternatives + +formats['afm'] = 'AFMFONTS' suffixes['afm'] = { 'afm' } +formats['enc'] = 'ENCFONTS' suffixes['enc'] = { 'enc' } +formats['fmt'] = 'TEXFORMATS' suffixes['fmt'] = { 'fmt' } +formats['map'] = 'TEXFONTMAPS' suffixes['map'] = { 'map' } +formats['mp'] = 'MPINPUTS' suffixes['mp'] = { 'mp' } +formats['ocp'] = 'OCPINPUTS' suffixes['ocp'] = { 'ocp' } +formats['ofm'] = 'OFMFONTS' suffixes['ofm'] = { 'ofm', 'tfm' } +formats['otf'] = 'OPENTYPEFONTS' suffixes['otf'] = { 'otf' } -- 'ttf' +formats['opl'] = 'OPLFONTS' suffixes['opl'] = { 'opl' } +formats['otp'] = 'OTPINPUTS' suffixes['otp'] = { 'otp' } +formats['ovf'] = 'OVFFONTS' suffixes['ovf'] = { 'ovf', 'vf' } +formats['ovp'] = 'OVPFONTS' suffixes['ovp'] = { 'ovp' } +formats['tex'] = 'TEXINPUTS' suffixes['tex'] = { 'tex' } +formats['tfm'] = 'TFMFONTS' suffixes['tfm'] = { 'tfm' } +formats['ttf'] = 'TTFONTS' suffixes['ttf'] = { 'ttf', 'ttc' } +formats['pfb'] = 'T1FONTS' suffixes['pfb'] = { 'pfb', 'pfa' } +formats['vf'] = 'VFFONTS' suffixes['vf'] = { 'vf' } + +formats['fea'] = 'FONTFEATURES' suffixes['fea'] = { 'fea' } +formats['cid'] = 'FONTCIDMAPS' suffixes['cid'] = { 'cid', 'cidmap' } + +formats ['texmfscripts'] = 'TEXMFSCRIPTS' -- new +suffixes['texmfscripts'] = { 'rb', 'pl', 'py' } -- 'lua' + +formats ['lua'] = 'LUAINPUTS' -- new +suffixes['lua'] = { 'lua', 'luc', 'tma', 'tmc' } + +-- backward compatible ones + +alternatives['map files'] = 'map' +alternatives['enc files'] = 'enc' +alternatives['cid files'] = 'cid' +alternatives['fea files'] = 'fea' +alternatives['opentype fonts'] = 'otf' +alternatives['truetype fonts'] = 'ttf' +alternatives['truetype collections'] = 'ttc' +alternatives['type1 fonts'] = 'pfb' + +-- obscure ones + +formats ['misc fonts'] = '' +suffixes['misc fonts'] = { } + +formats ['sfd'] = 'SFDFONTS' +suffixes ['sfd'] = { 'sfd' } +alternatives['subfont definition files'] = 'sfd' + +-- In practice we will work within one tds tree, but i want to keep +-- the option open to build tools that look at multiple trees, which is +-- why we keep the tree specific data in a table. We used to pass the +-- instance but for practical pusposes we now avoid this and use a +-- instance variable. + +-- here we catch a few new thingies (todo: add these paths to context.tmf) +-- +-- FONTFEATURES = .;$TEXMF/fonts/fea// +-- FONTCIDMAPS = .;$TEXMF/fonts/cid// + +-- we always have one instance active + +resolvers.instance = resolvers.instance or nil -- the current one (slow access) +local instance = resolvers.instance or nil -- the current one (fast access) + +function resolvers.newinstance() + + -- store once, freeze and faster (once reset we can best use + -- instance.environment) maybe better have a register suffix + -- function + + for k, v in next, suffixes do + for i=1,#v do + local vi = v[i] + if vi then + suffixmap[vi] = k + end + end + end + + -- because vf searching is somewhat dangerous, we want to prevent + -- too liberal searching esp because we do a lookup on the current + -- path anyway; only tex (or any) is safe + + for k, v in next, formats do + dangerous[k] = true + end + dangerous.tex = nil + + -- the instance + + local newinstance = { + rootpath = '', + treepath = '', + progname = 'context', + engine = 'luatex', + format = '', + environment = { }, + variables = { }, + expansions = { }, + files = { }, + remap = { }, + configuration = { }, + setup = { }, + order = { }, + found = { }, + foundintrees = { }, + kpsevars = { }, + hashes = { }, + cnffiles = { }, + luafiles = { }, + lists = { }, + remember = true, + diskcache = true, + renewcache = false, + scandisk = true, + cachepath = nil, + loaderror = false, + sortdata = false, + savelists = true, + cleanuppaths = true, + allresults = false, + pattern = nil, -- lists + data = { }, -- only for loading + force_suffixes = true, + fakepaths = { }, + } + + local ne = newinstance.environment + + for k,v in next, os.env do + ne[k] = resolvers.bare_variable(v) + end + + return newinstance + +end + +function resolvers.setinstance(someinstance) + instance = someinstance + resolvers.instance = someinstance + return someinstance +end + +function resolvers.reset() + return resolvers.setinstance(resolvers.newinstance()) +end + +local function reset_hashes() + instance.lists = { } + instance.found = { } +end + +local function check_configuration() -- not yet ok, no time for debugging now + local ie = instance.environment + local function fix(varname,default) + local proname = varname .. "." .. instance.progname or "crap" + local p, v = ie[proname], ie[varname] + if not ((p and p ~= "") or (v and v ~= "")) then + instance.variables[varname] = default -- or environment? + end + end + local name = os.name + if name == "windows" then + fix("OSFONTDIR", "c:/windows/fonts//") + elseif name == "macosx" then + fix("OSFONTDIR", "$HOME/Library/Fonts//;/Library/Fonts//;/System/Library/Fonts//") + else + -- bad luck + end + fix("LUAINPUTS" , ".;$TEXINPUTS;$TEXMFSCRIPTS") -- no progname, hm + fix("FONTFEATURES", ".;$TEXMF/fonts/fea//;$OPENTYPEFONTS;$TTFONTS;$T1FONTS;$AFMFONTS") + fix("FONTCIDMAPS" , ".;$TEXMF/fonts/cid//;$OPENTYPEFONTS;$TTFONTS;$T1FONTS;$AFMFONTS") + fix("LUATEXLIBS" , ".;$TEXMF/luatex/lua//") +end + +function resolvers.bare_variable(str) -- assumes str is a string + return (gsub(str,"\s*([\"\']?)(.+)%1\s*", "%2")) +end + +function resolvers.settrace(n) -- no longer number but: 'locating' or 'detail' + if n then + trackers.disable("resolvers.*") + trackers.enable("resolvers."..n) + end +end + +resolvers.settrace(os.getenv("MTX.resolvers.TRACE") or os.getenv("MTX_INPUT_TRACE")) + +function resolvers.osenv(key) + local ie = instance.environment + local value = ie[key] + if value == nil then + -- local e = os.getenv(key) + local e = os.env[key] + if e == nil then + -- value = "" -- false + else + value = resolvers.bare_variable(e) + end + ie[key] = value + end + return value or "" +end + +function resolvers.env(key) + return instance.environment[key] or resolvers.osenv(key) +end + +-- + +local function expand_vars(lst) -- simple vars + local variables, env = instance.variables, resolvers.env + local function resolve(a) + return variables[a] or env(a) + end + for k=1,#lst do + lst[k] = gsub(lst[k],"%$([%a%d%_%-]+)",resolve) + end +end + +local function expanded_var(var) -- simple vars + local function resolve(a) + return instance.variables[a] or resolvers.env(a) + end + return (gsub(var,"%$([%a%d%_%-]+)",resolve)) +end + +local function entry(entries,name) + if name and (name ~= "") then + name = gsub(name,'%$','') + local result = entries[name..'.'..instance.progname] or entries[name] + if result then + return result + else + result = resolvers.env(name) + if result then + instance.variables[name] = result + resolvers.expand_variables() + return instance.expansions[name] or "" + end + end + end + return "" +end + +local function is_entry(entries,name) + if name and name ~= "" then + name = gsub(name,'%$','') + return (entries[name..'.'..instance.progname] or entries[name]) ~= nil + else + return false + end +end + +-- {a,b,c,d} +-- a,b,c/{p,q,r},d +-- a,b,c/{p,q,r}/d/{x,y,z}// +-- a,b,c/{p,q/{x,y,z},r},d/{p,q,r} +-- a,b,c/{p,q/{x,y,z},r},d/{p,q,r} +-- a{b,c}{d,e}f +-- {a,b,c,d} +-- {a,b,c/{p,q,r},d} +-- {a,b,c/{p,q,r}/d/{x,y,z}//} +-- {a,b,c/{p,q/{x,y,z}},d/{p,q,r}} +-- {a,b,c/{p,q/{x,y,z},w}v,d/{p,q,r}} +-- {$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,.local,}/web2c} + +-- this one is better and faster, but it took me a while to realize +-- that this kind of replacement is cleaner than messy parsing and +-- fuzzy concatenating we can probably gain a bit with selectively +-- applying lpeg, but experiments with lpeg parsing this proved not to +-- work that well; the parsing is ok, but dealing with the resulting +-- table is a pain because we need to work inside-out recursively + +local function splitpathexpr(str, t, validate) + -- no need for further optimization as it is only called a + -- few times, we can use lpeg for the sub; we could move + -- the local functions outside the body + t = t or { } + str = gsub(str,",}",",@}") + str = gsub(str,"{,","{@,") + -- str = "@" .. str .. "@" + local ok, done + local function do_first(a,b) + local t = { } + for s in gmatch(b,"[^,]+") do t[#t+1] = a .. s end + return "{" .. concat(t,",") .. "}" + end + local function do_second(a,b) + local t = { } + for s in gmatch(a,"[^,]+") do t[#t+1] = s .. b end + return "{" .. concat(t,",") .. "}" + end + local function do_both(a,b) + local t = { } + for sa in gmatch(a,"[^,]+") do + for sb in gmatch(b,"[^,]+") do + t[#t+1] = sa .. sb + end + end + return "{" .. concat(t,",") .. "}" + end + local function do_three(a,b,c) + return a .. b.. c + end + while true do + done = false + while true do + str, ok = gsub(str,"([^{},]+){([^{}]+)}",do_first) + if ok > 0 then done = true else break end + end + while true do + str, ok = gsub(str,"{([^{}]+)}([^{},]+)",do_second) + if ok > 0 then done = true else break end + end + while true do + str, ok = gsub(str,"{([^{}]+)}{([^{}]+)}",do_both) + if ok > 0 then done = true else break end + end + str, ok = gsub(str,"({[^{}]*){([^{}]+)}([^{}]*})",do_three) + if ok > 0 then done = true end + if not done then break end + end + str = gsub(str,"[{}]", "") + str = gsub(str,"@","") + if validate then + for s in gmatch(str,"[^,]+") do + s = validate(s) + if s then t[#t+1] = s end + end + else + for s in gmatch(str,"[^,]+") do + t[#t+1] = s + end + end + return t +end + +local function expanded_path_from_list(pathlist) -- maybe not a list, just a path + -- a previous version fed back into pathlist + local newlist, ok = { }, false + for k=1,#pathlist do + if find(pathlist[k],"[{}]") then + ok = true + break + end + end + if ok then + local function validate(s) + s = file.collapse_path(s) + return s ~= "" and not find(s,dummy_path_expr) and s + end + for k=1,#pathlist do + splitpathexpr(pathlist[k],newlist,validate) + end + else + for k=1,#pathlist do + for p in gmatch(pathlist[k],"([^,]+)") do + p = file.collapse_path(p) + if p ~= "" then newlist[#newlist+1] = p end + end + end + end + return newlist +end + +-- we follow a rather traditional approach: +-- +-- (1) texmf.cnf given in TEXMFCNF +-- (2) texmf.cnf searched in default variable +-- +-- also we now follow the stupid route: if not set then just assume *one* +-- cnf file under texmf (i.e. distribution) + +resolvers.ownpath = resolvers.ownpath or nil +resolvers.ownbin = resolvers.ownbin or arg[-2] or arg[-1] or arg[0] or "luatex" +resolvers.autoselfdir = true -- false may be handy for debugging + +function resolvers.getownpath() + if not resolvers.ownpath then + if resolvers.autoselfdir and os.selfdir then + resolvers.ownpath = os.selfdir + else + local binary = resolvers.ownbin + if os.platform == "windows" then + binary = file.replacesuffix(binary,"exe") + end + for p in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do + local b = file.join(p,binary) + if lfs.isfile(b) then + -- we assume that after changing to the path the currentdir function + -- resolves to the real location and use this side effect here; this + -- trick is needed because on the mac installations use symlinks in the + -- path instead of real locations + local olddir = lfs.currentdir() + if lfs.chdir(p) then + local pp = lfs.currentdir() + if trace_verbose and p ~= pp then + logs.report("fileio","following symlink %s to %s",p,pp) + end + resolvers.ownpath = pp + lfs.chdir(olddir) + else + if trace_verbose then + logs.report("fileio","unable to check path %s",p) + end + resolvers.ownpath = p + end + break + end + end + end + if not resolvers.ownpath then resolvers.ownpath = '.' end + end + return resolvers.ownpath +end + +local own_places = { "SELFAUTOLOC", "SELFAUTODIR", "SELFAUTOPARENT", "TEXMFCNF" } + +local function identify_own() + local ownpath = resolvers.getownpath() or lfs.currentdir() + local ie = instance.environment + if ownpath then + if resolvers.env('SELFAUTOLOC') == "" then os.env['SELFAUTOLOC'] = file.collapse_path(ownpath) end + if resolvers.env('SELFAUTODIR') == "" then os.env['SELFAUTODIR'] = file.collapse_path(ownpath .. "/..") end + if resolvers.env('SELFAUTOPARENT') == "" then os.env['SELFAUTOPARENT'] = file.collapse_path(ownpath .. "/../..") end + else + logs.report("fileio","error: unable to locate ownpath") + os.exit() + end + if resolvers.env('TEXMFCNF') == "" then os.env['TEXMFCNF'] = resolvers.cnfdefault end + if resolvers.env('TEXOS') == "" then os.env['TEXOS'] = resolvers.env('SELFAUTODIR') end + if resolvers.env('TEXROOT') == "" then os.env['TEXROOT'] = resolvers.env('SELFAUTOPARENT') end + if trace_verbose then + for i=1,#own_places do + local v = own_places[i] + logs.report("fileio","variable %s set to %s",v,resolvers.env(v) or "unknown") + end + end + identify_own = function() end +end + +function resolvers.identify_cnf() + if #instance.cnffiles == 0 then + -- fallback + identify_own() + -- the real search + resolvers.expand_variables() + local t = resolvers.split_path(resolvers.env('TEXMFCNF')) + t = expanded_path_from_list(t) + expand_vars(t) -- redundant + local function locate(filename,list) + for i=1,#t do + local ti = t[i] + local texmfcnf = file.collapse_path(file.join(ti,filename)) + if lfs.isfile(texmfcnf) then + list[#list+1] = texmfcnf + end + end + end + locate(resolvers.luaname,instance.luafiles) + locate(resolvers.cnfname,instance.cnffiles) + end +end + +local function load_cnf_file(fname) + fname = resolvers.clean_path(fname) + local lname = file.replacesuffix(fname,'lua') + local f = io.open(lname) + if f then -- this will go + f:close() + local dname = file.dirname(fname) + if not instance.configuration[dname] then + resolvers.load_data(dname,'configuration',lname and file.basename(lname)) + instance.order[#instance.order+1] = instance.configuration[dname] + end + else + f = io.open(fname) + if f then + if trace_verbose then + logs.report("fileio","loading %s", fname) + end + local line, data, n, k, v + local dname = file.dirname(fname) + if not instance.configuration[dname] then + instance.configuration[dname] = { } + instance.order[#instance.order+1] = instance.configuration[dname] + end + local data = instance.configuration[dname] + while true do + local line, n = f:read(), 0 + if line then + while true do -- join lines + line, n = gsub(line,"\\%s*$", "") + if n > 0 then + line = line .. f:read() + else + break + end + end + if not find(line,"^[%%#]") then + local l = gsub(line,"%s*%%.*$","") + local k, v = match(l,"%s*(.-)%s*=%s*(.-)%s*$") + if k and v and not data[k] then + v = gsub(v,"[%%#].*",'') + data[k] = gsub(v,"~","$HOME") + instance.kpsevars[k] = true + end + end + else + break + end + end + f:close() + elseif trace_verbose then + logs.report("fileio","skipping %s", fname) + end + end +end + +local function collapse_cnf_data() -- potential optimization: pass start index (setup and configuration are shared) + for _,c in ipairs(instance.order) do + for k,v in next, c do + if not instance.variables[k] then + if instance.environment[k] then + instance.variables[k] = instance.environment[k] + else + instance.kpsevars[k] = true + instance.variables[k] = resolvers.bare_variable(v) + end + end + end + end +end + +function resolvers.load_cnf() + local function loadoldconfigdata() + for _, fname in ipairs(instance.cnffiles) do + load_cnf_file(fname) + end + end + -- instance.cnffiles contain complete names now ! + if #instance.cnffiles == 0 then + if trace_verbose then + logs.report("fileio","no cnf files found (TEXMFCNF may not be set/known)") + end + else + instance.rootpath = instance.cnffiles[1] + for k,fname in ipairs(instance.cnffiles) do + instance.cnffiles[k] = file.collapse_path(gsub(fname,"\\",'/')) + end + for i=1,3 do + instance.rootpath = file.dirname(instance.rootpath) + end + instance.rootpath = file.collapse_path(instance.rootpath) + if instance.diskcache and not instance.renewcache then + resolvers.loadoldconfig(instance.cnffiles) + if instance.loaderror then + loadoldconfigdata() + resolvers.saveoldconfig() + end + else + loadoldconfigdata() + if instance.renewcache then + resolvers.saveoldconfig() + end + end + collapse_cnf_data() + end + check_configuration() +end + +function resolvers.load_lua() + if #instance.luafiles == 0 then + -- yet harmless + else + instance.rootpath = instance.luafiles[1] + for k,fname in ipairs(instance.luafiles) do + instance.luafiles[k] = file.collapse_path(gsub(fname,"\\",'/')) + end + for i=1,3 do + instance.rootpath = file.dirname(instance.rootpath) + end + instance.rootpath = file.collapse_path(instance.rootpath) + resolvers.loadnewconfig() + collapse_cnf_data() + end + check_configuration() +end + +-- database loading + +function resolvers.load_hash() + resolvers.locatelists() + if instance.diskcache and not instance.renewcache then + resolvers.loadfiles() + if instance.loaderror then + resolvers.loadlists() + resolvers.savefiles() + end + else + resolvers.loadlists() + if instance.renewcache then + resolvers.savefiles() + end + end +end + +function resolvers.append_hash(type,tag,name) + if trace_locating then + logs.report("fileio","= hash append: %s",tag) + end + insert(instance.hashes, { ['type']=type, ['tag']=tag, ['name']=name } ) +end + +function resolvers.prepend_hash(type,tag,name) + if trace_locating then + logs.report("fileio","= hash prepend: %s",tag) + end + insert(instance.hashes, 1, { ['type']=type, ['tag']=tag, ['name']=name } ) +end + +function resolvers.extend_texmf_var(specification) -- crap, we could better prepend the hash +-- local t = resolvers.expanded_path_list('TEXMF') -- full expansion + local t = resolvers.split_path(resolvers.env('TEXMF')) + insert(t,1,specification) + local newspec = concat(t,";") + if instance.environment["TEXMF"] then + instance.environment["TEXMF"] = newspec + elseif instance.variables["TEXMF"] then + instance.variables["TEXMF"] = newspec + else + -- weird + end + resolvers.expand_variables() + reset_hashes() +end + +-- locators + +function resolvers.locatelists() + for _, path in ipairs(resolvers.clean_path_list('TEXMF')) do + if trace_verbose then + logs.report("fileio","locating list of %s",path) + end + resolvers.locatedatabase(file.collapse_path(path)) + end +end + +function resolvers.locatedatabase(specification) + return resolvers.methodhandler('locators', specification) +end + +function resolvers.locators.tex(specification) + if specification and specification ~= '' and lfs.isdir(specification) then + if trace_locating then + logs.report("fileio",'! tex locator found: %s',specification) + end + resolvers.append_hash('file',specification,filename) + elseif trace_locating then + logs.report("fileio",'? tex locator not found: %s',specification) + end +end + +-- hashers + +function resolvers.hashdatabase(tag,name) + return resolvers.methodhandler('hashers',tag,name) +end + +function resolvers.loadfiles() + instance.loaderror = false + instance.files = { } + if not instance.renewcache then + for _, hash in ipairs(instance.hashes) do + resolvers.hashdatabase(hash.tag,hash.name) + if instance.loaderror then break end + end + end +end + +function resolvers.hashers.tex(tag,name) + resolvers.load_data(tag,'files') +end + +-- generators: + +function resolvers.loadlists() + for _, hash in ipairs(instance.hashes) do + resolvers.generatedatabase(hash.tag) + end +end + +function resolvers.generatedatabase(specification) + return resolvers.methodhandler('generators', specification) +end + +-- starting with . or .. etc or funny char + +local weird = lpeg.P(".")^1 + lpeg.anywhere(lpeg.S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t")) + +function resolvers.generators.tex(specification) + local tag = specification + if trace_verbose then + logs.report("fileio","scanning path %s",specification) + end + instance.files[tag] = { } + local files = instance.files[tag] + local n, m, r = 0, 0, 0 + local spec = specification .. '/' + local attributes = lfs.attributes + local directory = lfs.dir + local function action(path) + local full + if path then + full = spec .. path .. '/' + else + full = spec + end + for name in directory(full) do + if not weird:match(name) then + local mode = attributes(full..name,'mode') + if mode == 'file' then + if path then + n = n + 1 + local f = files[name] + if f then + if type(f) == 'string' then + files[name] = { f, path } + else + f[#f+1] = path + end + else -- probably unique anyway + files[name] = path + local lower = lower(name) + if name ~= lower then + files["remap:"..lower] = name + r = r + 1 + end + end + end + elseif mode == 'directory' then + m = m + 1 + if path then + action(path..'/'..name) + else + action(name) + end + end + end + end + end + action() + if trace_verbose then + logs.report("fileio","%s files found on %s directories with %s uppercase remappings",n,m,r) + end +end + +-- savers, todo + +function resolvers.savefiles() + resolvers.save_data('files') +end + +-- A config (optionally) has the paths split in tables. Internally +-- we join them and split them after the expansion has taken place. This +-- is more convenient. + +function resolvers.splitconfig() + for i,c in ipairs(instance) do + for k,v in pairs(c) do + if type(v) == 'string' then + local t = file.split_path(v) + if #t > 1 then + c[k] = t + end + end + end + end +end + +function resolvers.joinconfig() + for i,c in ipairs(instance.order) do + for k,v in pairs(c) do -- ipairs? + if type(v) == 'table' then + c[k] = file.join_path(v) + end + end + end +end +function resolvers.split_path(str) + if type(str) == 'table' then + return str + else + return file.split_path(str) + end +end +function resolvers.join_path(str) + if type(str) == 'table' then + return file.join_path(str) + else + return str + end +end + +function resolvers.splitexpansions() + local ie = instance.expansions + for k,v in next, ie do + local t, h = { }, { } + for _,vv in ipairs(file.split_path(v)) do + if vv ~= "" and not h[vv] then + t[#t+1] = vv + h[vv] = true + end + end + if #t > 1 then + ie[k] = t + else + ie[k] = t[1] + end + end +end + +-- end of split/join code + +function resolvers.saveoldconfig() + resolvers.splitconfig() + resolvers.save_data('configuration') + resolvers.joinconfig() +end + +resolvers.configbanner = [[ +-- This is a Luatex configuration file created by 'luatools.lua' or +-- 'luatex.exe' directly. For comment, suggestions and questions you can +-- contact the ConTeXt Development Team. This configuration file is +-- not copyrighted. [HH & TH] +]] + +function resolvers.serialize(files) + -- This version is somewhat optimized for the kind of + -- tables that we deal with, so it's much faster than + -- the generic serializer. This makes sense because + -- luatools and mtxtools are called frequently. Okay, + -- we pay a small price for properly tabbed tables. + local t = { } + local function dump(k,v,m) -- could be moved inline + if type(v) == 'string' then + return m .. "['" .. k .. "']='" .. v .. "'," + elseif #v == 1 then + return m .. "['" .. k .. "']='" .. v[1] .. "'," + else + return m .. "['" .. k .. "']={'" .. concat(v,"','").. "'}," + end + end + t[#t+1] = "return {" + if instance.sortdata then + for _, k in pairs(sortedkeys(files)) do -- ipairs + local fk = files[k] + if type(fk) == 'table' then + t[#t+1] = "\t['" .. k .. "']={" + for _, kk in pairs(sortedkeys(fk)) do -- ipairs + t[#t+1] = dump(kk,fk[kk],"\t\t") + end + t[#t+1] = "\t}," + else + t[#t+1] = dump(k,fk,"\t") + end + end + else + for k, v in next, files do + if type(v) == 'table' then + t[#t+1] = "\t['" .. k .. "']={" + for kk,vv in next, v do + t[#t+1] = dump(kk,vv,"\t\t") + end + t[#t+1] = "\t}," + else + t[#t+1] = dump(k,v,"\t") + end + end + end + t[#t+1] = "}" + return concat(t,"\n") +end + +function resolvers.save_data(dataname, makename) -- untested without cache overload + for cachename, files in next, instance[dataname] do + local name = (makename or file.join)(cachename,dataname) + local luaname, lucname = name .. ".lua", name .. ".luc" + if trace_verbose then + logs.report("fileio","preparing %s for %s",dataname,cachename) + end + for k, v in next, files do + if type(v) == "table" and #v == 1 then + files[k] = v[1] + end + end + local data = { + type = dataname, + root = cachename, + version = resolvers.cacheversion, + date = os.date("%Y-%m-%d"), + time = os.date("%H:%M:%S"), + content = files, + } + local ok = io.savedata(luaname,resolvers.serialize(data)) + if ok then + if trace_verbose then + logs.report("fileio","%s saved in %s",dataname,luaname) + end + if utils.lua.compile(luaname,lucname,false,true) then -- no cleanup but strip + if trace_verbose then + logs.report("fileio","%s compiled to %s",dataname,lucname) + end + else + if trace_verbose then + logs.report("fileio","compiling failed for %s, deleting file %s",dataname,lucname) + end + os.remove(lucname) + end + elseif trace_verbose then + logs.report("fileio","unable to save %s in %s (access error)",dataname,luaname) + end + end +end + +function resolvers.load_data(pathname,dataname,filename,makename) -- untested without cache overload + filename = ((not filename or (filename == "")) and dataname) or filename + filename = (makename and makename(dataname,filename)) or file.join(pathname,filename) + local blob = loadfile(filename .. ".luc") or loadfile(filename .. ".lua") + if blob then + local data = blob() + if data and data.content and data.type == dataname and data.version == resolvers.cacheversion then + if trace_verbose then + logs.report("fileio","loading %s for %s from %s",dataname,pathname,filename) + end + instance[dataname][pathname] = data.content + else + if trace_verbose then + logs.report("fileio","skipping %s for %s from %s",dataname,pathname,filename) + end + instance[dataname][pathname] = { } + instance.loaderror = true + end + elseif trace_verbose then + logs.report("fileio","skipping %s for %s from %s",dataname,pathname,filename) + end +end + +-- some day i'll use the nested approach, but not yet (actually we even drop +-- engine/progname support since we have only luatex now) +-- +-- first texmfcnf.lua files are located, next the cached texmf.cnf files +-- +-- return { +-- TEXMFBOGUS = 'effe checken of dit werkt', +-- } + +function resolvers.resetconfig() + identify_own() + instance.configuration, instance.setup, instance.order, instance.loaderror = { }, { }, { }, false +end + +function resolvers.loadnewconfig() + for _, cnf in ipairs(instance.luafiles) do + local pathname = file.dirname(cnf) + local filename = file.join(pathname,resolvers.luaname) + local blob = loadfile(filename) + if blob then + local data = blob() + if data then + if trace_verbose then + logs.report("fileio","loading configuration file %s",filename) + end + if true then + -- flatten to variable.progname + local t = { } + for k, v in next, data do -- v = progname + if type(v) == "string" then + t[k] = v + else + for kk, vv in next, v do -- vv = variable + if type(vv) == "string" then + t[vv.."."..v] = kk + end + end + end + end + instance['setup'][pathname] = t + else + instance['setup'][pathname] = data + end + else + if trace_verbose then + logs.report("fileio","skipping configuration file %s",filename) + end + instance['setup'][pathname] = { } + instance.loaderror = true + end + elseif trace_verbose then + logs.report("fileio","skipping configuration file %s",filename) + end + instance.order[#instance.order+1] = instance.setup[pathname] + if instance.loaderror then break end + end +end + +function resolvers.loadoldconfig() + if not instance.renewcache then + for _, cnf in ipairs(instance.cnffiles) do + local dname = file.dirname(cnf) + resolvers.load_data(dname,'configuration') + instance.order[#instance.order+1] = instance.configuration[dname] + if instance.loaderror then break end + end + end + resolvers.joinconfig() +end + +function resolvers.expand_variables() + local expansions, environment, variables = { }, instance.environment, instance.variables + local env = resolvers.env + instance.expansions = expansions + if instance.engine ~= "" then environment['engine'] = instance.engine end + if instance.progname ~= "" then environment['progname'] = instance.progname end + for k,v in next, environment do + local a, b = match(k,"^(%a+)%_(.*)%s*$") + if a and b then + expansions[a..'.'..b] = v + else + expansions[k] = v + end + end + for k,v in next, environment do -- move environment to expansions + if not expansions[k] then expansions[k] = v end + end + for k,v in next, variables do -- move variables to expansions + if not expansions[k] then expansions[k] = v end + end + local busy = false + local function resolve(a) + busy = true + return expansions[a] or env(a) + end + while true do + busy = false + for k,v in next, expansions do + local s, n = gsub(v,"%$([%a%d%_%-]+)",resolve) + local s, m = gsub(s,"%$%{([%a%d%_%-]+)%}",resolve) + if n > 0 or m > 0 then + expansions[k]= s + end + end + if not busy then break end + end + for k,v in next, expansions do + expansions[k] = gsub(v,"\\", '/') + end +end + +function resolvers.variable(name) + return entry(instance.variables,name) +end + +function resolvers.expansion(name) + return entry(instance.expansions,name) +end + +function resolvers.is_variable(name) + return is_entry(instance.variables,name) +end + +function resolvers.is_expansion(name) + return is_entry(instance.expansions,name) +end + +function resolvers.unexpanded_path_list(str) + local pth = resolvers.variable(str) + local lst = resolvers.split_path(pth) + return expanded_path_from_list(lst) +end + +function resolvers.unexpanded_path(str) + return file.join_path(resolvers.unexpanded_path_list(str)) +end + +do -- no longer needed + + local done = { } + + function resolvers.reset_extra_path() + local ep = instance.extra_paths + if not ep then + ep, done = { }, { } + instance.extra_paths = ep + elseif #ep > 0 then + instance.lists, done = { }, { } + end + end + + function resolvers.register_extra_path(paths,subpaths) + local ep = instance.extra_paths or { } + local n = #ep + if paths and paths ~= "" then + if subpaths and subpaths ~= "" then + for p in gmatch(paths,"[^,]+") do + -- we gmatch each step again, not that fast, but used seldom + for s in gmatch(subpaths,"[^,]+") do + local ps = p .. "/" .. s + if not done[ps] then + ep[#ep+1] = resolvers.clean_path(ps) + done[ps] = true + end + end + end + else + for p in gmatch(paths,"[^,]+") do + if not done[p] then + ep[#ep+1] = resolvers.clean_path(p) + done[p] = true + end + end + end + elseif subpaths and subpaths ~= "" then + for i=1,n do + -- we gmatch each step again, not that fast, but used seldom + for s in gmatch(subpaths,"[^,]+") do + local ps = ep[i] .. "/" .. s + if not done[ps] then + ep[#ep+1] = resolvers.clean_path(ps) + done[ps] = true + end + end + end + end + if #ep > 0 then + instance.extra_paths = ep -- register paths + end + if #ep > n then + instance.lists = { } -- erase the cache + end + end + +end + +local function made_list(instance,list) + local ep = instance.extra_paths + if not ep or #ep == 0 then + return list + else + local done, new = { }, { } + -- honour . .. ../.. but only when at the start + for k=1,#list do + local v = list[k] + if not done[v] then + if find(v,"^[%.%/]$") then + done[v] = true + new[#new+1] = v + else + break + end + end + end + -- first the extra paths + for k=1,#ep do + local v = ep[k] + if not done[v] then + done[v] = true + new[#new+1] = v + end + end + -- next the formal paths + for k=1,#list do + local v = list[k] + if not done[v] then + done[v] = true + new[#new+1] = v + end + end + return new + end +end + +function resolvers.clean_path_list(str) + local t = resolvers.expanded_path_list(str) + if t then + for i=1,#t do + t[i] = file.collapse_path(resolvers.clean_path(t[i])) + end + end + return t +end + +function resolvers.expand_path(str) + return file.join_path(resolvers.expanded_path_list(str)) +end + +function resolvers.expanded_path_list(str) + if not str then + return ep or { } + elseif instance.savelists then + -- engine+progname hash + str = gsub(str,"%$","") + if not instance.lists[str] then -- cached + local lst = made_list(instance,resolvers.split_path(resolvers.expansion(str))) + instance.lists[str] = expanded_path_from_list(lst) + end + return instance.lists[str] + else + local lst = resolvers.split_path(resolvers.expansion(str)) + return made_list(instance,expanded_path_from_list(lst)) + end +end + +function resolvers.expanded_path_list_from_var(str) -- brrr + local tmp = resolvers.var_of_format_or_suffix(gsub(str,"%$","")) + if tmp ~= "" then + return resolvers.expanded_path_list(str) + else + return resolvers.expanded_path_list(tmp) + end +end + +function resolvers.expand_path_from_var(str) + return file.join_path(resolvers.expanded_path_list_from_var(str)) +end + +function resolvers.format_of_var(str) + return formats[str] or formats[alternatives[str]] or '' +end +function resolvers.format_of_suffix(str) + return suffixmap[file.extname(str)] or 'tex' +end + +function resolvers.variable_of_format(str) + return formats[str] or formats[alternatives[str]] or '' +end + +function resolvers.var_of_format_or_suffix(str) + local v = formats[str] + if v then + return v + end + v = formats[alternatives[str]] + if v then + return v + end + v = suffixmap[file.extname(str)] + if v then + return formats[isf] + end + return '' +end + +function resolvers.expand_braces(str) -- output variable and brace expansion of STRING + local ori = resolvers.variable(str) + local pth = expanded_path_from_list(resolvers.split_path(ori)) + return file.join_path(pth) +end + +resolvers.isreadable = { } + +function resolvers.isreadable.file(name) + local readable = lfs.isfile(name) -- brrr + if trace_detail then + if readable then + logs.report("fileio","+ readable: %s",name) + else + logs.report("fileio","- readable: %s", name) + end + end + return readable +end + +resolvers.isreadable.tex = resolvers.isreadable.file + +-- name +-- name/name + +local function collect_files(names) + local filelist = { } + for k=1,#names do + local fname = names[k] + if trace_detail then + logs.report("fileio","? blobpath asked: %s",fname) + end + local bname = file.basename(fname) + local dname = file.dirname(fname) + if dname == "" or find(dname,"^%.") then + dname = false + else + dname = "/" .. dname .. "$" + end + local hashes = instance.hashes + for h=1,#hashes do + local hash = hashes[h] + local blobpath = hash.tag + local files = blobpath and instance.files[blobpath] + if files then + if trace_detail then + logs.report("fileio",'? blobpath do: %s (%s)',blobpath,bname) + end + local blobfile = files[bname] + if not blobfile then + local rname = "remap:"..bname + blobfile = files[rname] + if blobfile then + bname = files[rname] + blobfile = files[bname] + end + end + if blobfile then + if type(blobfile) == 'string' then + if not dname or find(blobfile,dname) then + filelist[#filelist+1] = { + hash.type, + file.join(blobpath,blobfile,bname), -- search + resolvers.concatinators[hash.type](blobpath,blobfile,bname) -- result + } + end + else + for kk=1,#blobfile do + local vv = blobfile[kk] + if not dname or find(vv,dname) then + filelist[#filelist+1] = { + hash.type, + file.join(blobpath,vv,bname), -- search + resolvers.concatinators[hash.type](blobpath,vv,bname) -- result + } + end + end + end + end + elseif trace_locating then + logs.report("fileio",'! blobpath no: %s (%s)',blobpath,bname) + end + end + end + if #filelist > 0 then + return filelist + else + return nil + end +end + +function resolvers.suffix_of_format(str) + if suffixes[str] then + return suffixes[str][1] + else + return "" + end +end + +function resolvers.suffixes_of_format(str) + if suffixes[str] then + return suffixes[str] + else + return {} + end +end + +function resolvers.register_in_trees(name) + if not find(name,"^%.") then + instance.foundintrees[name] = (instance.foundintrees[name] or 0) + 1 -- maybe only one + end +end + +-- split the next one up for readability (bu this module needs a cleanup anyway) + +local function can_be_dir(name) -- can become local + local fakepaths = instance.fakepaths + if not fakepaths[name] then + if lfs.isdir(name) then + fakepaths[name] = 1 -- directory + else + fakepaths[name] = 2 -- no directory + end + end + return (fakepaths[name] == 1) +end + +local function collect_instance_files(filename,collected) -- todo : plugin (scanners, checkers etc) + local result = collected or { } + local stamp = nil + filename = file.collapse_path(filename) -- elsewhere + filename = file.collapse_path(gsub(filename,"\\","/")) -- elsewhere + -- speed up / beware: format problem + if instance.remember then + stamp = filename .. "--" .. instance.engine .. "--" .. instance.progname .. "--" .. instance.format + if instance.found[stamp] then + if trace_locating then + logs.report("fileio",'! remembered: %s',filename) + end + return instance.found[stamp] + end + end + if not dangerous[instance.format or "?"] then + if resolvers.isreadable.file(filename) then + if trace_detail then + logs.report("fileio",'= found directly: %s',filename) + end + instance.found[stamp] = { filename } + return { filename } + end + end + if find(filename,'%*') then + if trace_locating then + logs.report("fileio",'! wildcard: %s', filename) + end + result = resolvers.find_wildcard_files(filename) + elseif file.is_qualified_path(filename) then + if resolvers.isreadable.file(filename) then + if trace_locating then + logs.report("fileio",'! qualified: %s', filename) + end + result = { filename } + else + local forcedname, ok, suffix = "", false, file.extname(filename) + if suffix == "" then -- why + if instance.format == "" then + forcedname = filename .. ".tex" + if resolvers.isreadable.file(forcedname) then + if trace_locating then + logs.report("fileio",'! no suffix, forcing standard filetype: tex') + end + result, ok = { forcedname }, true + end + else + local suffixes = resolvers.suffixes_of_format(instance.format) + for _, s in next, suffixes do + forcedname = filename .. "." .. s + if resolvers.isreadable.file(forcedname) then + if trace_locating then + logs.report("fileio",'! no suffix, forcing format filetype: %s', s) + end + result, ok = { forcedname }, true + break + end + end + end + end + if not ok and suffix ~= "" then + -- try to find in tree (no suffix manipulation), here we search for the + -- matching last part of the name + local basename = file.basename(filename) + local pattern = (filename .. "$"):gsub("([%.%-])","%%%1") + local savedformat = instance.format + local format = savedformat or "" + if format == "" then + instance.format = resolvers.format_of_suffix(suffix) + end + if not format then + instance.format = "othertextfiles" -- kind of everything, maybe texinput is better + end + -- + local resolved = collect_instance_files(basename) + if #result == 0 then + local lowered = lower(basename) + if filename ~= lowered then + resolved = collect_instance_files(lowered) + end + end + resolvers.format = savedformat + -- + for r=1,#resolved do + local rr = resolved[r] + if rr:find(pattern) then + result[#result+1], ok = rr, true + end + end + -- a real wildcard: + -- + -- if not ok then + -- local filelist = collect_files({basename}) + -- for f=1,#filelist do + -- local ff = filelist[f][3] or "" + -- if ff:find(pattern) then + -- result[#result+1], ok = ff, true + -- end + -- end + -- end + end + if not ok and trace_locating then + logs.report("fileio",'? qualified: %s', filename) + end + end + else + -- search spec + local filetype, extra, done, wantedfiles, ext = '', nil, false, { }, file.extname(filename) + if ext == "" then + if not instance.force_suffixes then + wantedfiles[#wantedfiles+1] = filename + end + else + wantedfiles[#wantedfiles+1] = filename + end + if instance.format == "" then + if ext == "" then + local forcedname = filename .. '.tex' + wantedfiles[#wantedfiles+1] = forcedname + filetype = resolvers.format_of_suffix(forcedname) + if trace_locating then + logs.report("fileio",'! forcing filetype: %s',filetype) + end + else + filetype = resolvers.format_of_suffix(filename) + if trace_locating then + logs.report("fileio",'! using suffix based filetype: %s',filetype) + end + end + else + if ext == "" then + local suffixes = resolvers.suffixes_of_format(instance.format) + for _, s in next, suffixes do + wantedfiles[#wantedfiles+1] = filename .. "." .. s + end + end + filetype = instance.format + if trace_locating then + logs.report("fileio",'! using given filetype: %s',filetype) + end + end + local typespec = resolvers.variable_of_format(filetype) + local pathlist = resolvers.expanded_path_list(typespec) + if not pathlist or #pathlist == 0 then + -- no pathlist, access check only / todo == wildcard + if trace_detail then + logs.report("fileio",'? filename: %s',filename) + logs.report("fileio",'? filetype: %s',filetype or '?') + logs.report("fileio",'? wanted files: %s',concat(wantedfiles," | ")) + end + for k=1,#wantedfiles do + local fname = wantedfiles[k] + if fname and resolvers.isreadable.file(fname) then + filename, done = fname, true + result[#result+1] = file.join('.',fname) + break + end + end + -- this is actually 'other text files' or 'any' or 'whatever' + local filelist = collect_files(wantedfiles) + local fl = filelist and filelist[1] + if fl then + filename = fl[3] + result[#result+1] = filename + done = true + end + else + -- list search + local filelist = collect_files(wantedfiles) + local doscan, recurse + if trace_detail then + logs.report("fileio",'? filename: %s',filename) + end + -- a bit messy ... esp the doscan setting here + for k=1,#pathlist do + local path = pathlist[k] + if find(path,"^!!") then doscan = false else doscan = true end + if find(path,"//$") then recurse = true else recurse = false end + local pathname = gsub(path,"^!+", '') + done = false + -- using file list + if filelist and not (done and not instance.allresults) and recurse then + -- compare list entries with permitted pattern + pathname = gsub(pathname,"([%-%.])","%%%1") -- this also influences + pathname = gsub(pathname,"/+$", '/.*') -- later usage of pathname + pathname = gsub(pathname,"//", '/.-/') -- not ok for /// but harmless + local expr = "^" .. pathname + for k=1,#filelist do + local fl = filelist[k] + local f = fl[2] + if find(f,expr) then + if trace_detail then + logs.report("fileio",'= found in hash: %s',f) + end + --- todo, test for readable + result[#result+1] = fl[3] + resolvers.register_in_trees(f) -- for tracing used files + done = true + if not instance.allresults then break end + end + end + end + if not done and doscan then + -- check if on disk / unchecked / does not work at all / also zips + if resolvers.splitmethod(pathname).scheme == 'file' then -- ? + local pname = gsub(pathname,"%.%*$",'') + if not find(pname,"%*") then + local ppname = gsub(pname,"/+$","") + if can_be_dir(ppname) then + for k=1,#wantedfiles do + local w = wantedfiles[k] + local fname = file.join(ppname,w) + if resolvers.isreadable.file(fname) then + if trace_detail then + logs.report("fileio",'= found by scanning: %s',fname) + end + result[#result+1] = fname + done = true + if not instance.allresults then break end + end + end + else + -- no access needed for non existing path, speedup (esp in large tree with lots of fake) + end + end + end + end + if not done and doscan then + -- todo: slow path scanning + end + if done and not instance.allresults then break end + end + end + end + for k=1,#result do + result[k] = file.collapse_path(result[k]) + end + if instance.remember then + instance.found[stamp] = result + end + return result +end + +if not resolvers.concatinators then resolvers.concatinators = { } end + +resolvers.concatinators.tex = file.join +resolvers.concatinators.file = resolvers.concatinators.tex + +function resolvers.find_files(filename,filetype,mustexist) + if type(mustexist) == boolean then + -- all set + elseif type(filetype) == 'boolean' then + filetype, mustexist = nil, false + elseif type(filetype) ~= 'string' then + filetype, mustexist = nil, false + end + instance.format = filetype or '' + local result = collect_instance_files(filename) + if #result == 0 then + local lowered = lower(filename) + if filename ~= lowered then + return collect_instance_files(lowered) + end + end + instance.format = '' + return result +end + +function resolvers.find_file(filename,filetype,mustexist) + return (resolvers.find_files(filename,filetype,mustexist)[1] or "") +end + +function resolvers.find_given_files(filename) + local bname, result = file.basename(filename), { } + local hashes = instance.hashes + for k=1,#hashes do + local hash = hashes[k] + local files = instance.files[hash.tag] + local blist = files[bname] + if not blist then + local rname = "remap:"..bname + blist = files[rname] + if blist then + bname = files[rname] + blist = files[bname] + end + end + if blist then + if type(blist) == 'string' then + result[#result+1] = resolvers.concatinators[hash.type](hash.tag,blist,bname) or "" + if not instance.allresults then break end + else + for kk=1,#blist do + local vv = blist[kk] + result[#result+1] = resolvers.concatinators[hash.type](hash.tag,vv,bname) or "" + if not instance.allresults then break end + end + end + end + end + return result +end + +function resolvers.find_given_file(filename) + return (resolvers.find_given_files(filename)[1] or "") +end + +local function doit(path,blist,bname,tag,kind,result,allresults) + local done = false + if blist and kind then + if type(blist) == 'string' then + -- make function and share code + if find(lower(blist),path) then + result[#result+1] = resolvers.concatinators[kind](tag,blist,bname) or "" + done = true + end + else + for kk=1,#blist do + local vv = blist[kk] + if find(lower(vv),path) then + result[#result+1] = resolvers.concatinators[kind](tag,vv,bname) or "" + done = true + if not allresults then break end + end + end + end + end + return done +end + +function resolvers.find_wildcard_files(filename) -- todo: remap: + local result = { } + local bname, dname = file.basename(filename), file.dirname(filename) + local path = gsub(dname,"^*/","") + path = gsub(path,"*",".*") + path = gsub(path,"-","%%-") + if dname == "" then + path = ".*" + end + local name = bname + name = gsub(name,"*",".*") + name = gsub(name,"-","%%-") + path = lower(path) + name = lower(name) + local files, allresults, done = instance.files, instance.allresults, false + if find(name,"%*") then + local hashes = instance.hashes + for k=1,#hashes do + local hash = hashes[k] + local tag, kind = hash.tag, hash.type + for kk, hh in next, files[hash.tag] do + if not find(kk,"^remap:") then + if find(lower(kk),name) then + if doit(path,hh,kk,tag,kind,result,allresults) then done = true end + if done and not allresults then break end + end + end + end + end + else + local hashes = instance.hashes + for k=1,#hashes do + local hash = hashes[k] + local tag, kind = hash.tag, hash.type + if doit(path,files[tag][bname],bname,tag,kind,result,allresults) then done = true end + if done and not allresults then break end + end + end + -- we can consider also searching the paths not in the database, but then + -- we end up with a messy search (all // in all path specs) + return result +end + +function resolvers.find_wildcard_file(filename) + return (resolvers.find_wildcard_files(filename)[1] or "") +end + +-- main user functions + +function resolvers.automount() + -- implemented later +end + +function resolvers.load(option) + statistics.starttiming(instance) + resolvers.resetconfig() + resolvers.identify_cnf() + resolvers.load_lua() + resolvers.expand_variables() + resolvers.load_cnf() + resolvers.expand_variables() + if option ~= "nofiles" then + resolvers.load_hash() + resolvers.automount() + end + statistics.stoptiming(instance) +end + +function resolvers.for_files(command, files, filetype, mustexist) + if files and #files > 0 then + local function report(str) + if trace_verbose then + logs.report("fileio",str) -- has already verbose + else + print(str) + end + end + if trace_verbose then + report('') + end + for _, file in ipairs(files) do + local result = command(file,filetype,mustexist) + if type(result) == 'string' then + report(result) + else + for _,v in ipairs(result) do + report(v) + end + end + end + end +end + +-- strtab + +resolvers.var_value = resolvers.variable -- output the value of variable $STRING. +resolvers.expand_var = resolvers.expansion -- output variable expansion of STRING. + +function resolvers.show_path(str) -- output search path for file type NAME + return file.join_path(resolvers.expanded_path_list(resolvers.format_of_var(str))) +end + +-- resolvers.find_file(filename) +-- resolvers.find_file(filename, filetype, mustexist) +-- resolvers.find_file(filename, mustexist) +-- resolvers.find_file(filename, filetype) + +function resolvers.register_file(files, name, path) + if files[name] then + if type(files[name]) == 'string' then + files[name] = { files[name], path } + else + files[name] = path + end + else + files[name] = path + end +end + +function resolvers.splitmethod(filename) + if not filename then + return { } -- safeguard + elseif type(filename) == "table" then + return filename -- already split + elseif not find(filename,"://") then + return { scheme="file", path = filename, original=filename } -- quick hack + else + return url.hashed(filename) + end +end + +function table.sequenced(t,sep) -- temp here + local s = { } + for k, v in pairs(t) do -- pairs? + s[#s+1] = k .. "=" .. v + end + return concat(s, sep or " | ") +end + +function resolvers.methodhandler(what, filename, filetype) -- ... + local specification = (type(filename) == "string" and resolvers.splitmethod(filename)) or filename -- no or { }, let it bomb + local scheme = specification.scheme + if resolvers[what][scheme] then + if trace_locating then + logs.report("fileio",'= handler: %s -> %s -> %s',specification.original,what,table.sequenced(specification)) + end + return resolvers[what][scheme](filename,filetype) -- todo: specification + else + return resolvers[what].tex(filename,filetype) -- todo: specification + end +end + +function resolvers.clean_path(str) + if str then + str = gsub(str,"\\","/") + str = gsub(str,"^!+","") + str = gsub(str,"^~",resolvers.homedir) + return str + else + return nil + end +end + +function resolvers.do_with_path(name,func) + for _, v in pairs(resolvers.expanded_path_list(name)) do -- pairs? + func("^"..resolvers.clean_path(v)) + end +end + +function resolvers.do_with_var(name,func) + func(expanded_var(name)) +end + +function resolvers.with_files(pattern,handle) + for _, hash in ipairs(instance.hashes) do + local blobpath = hash.tag + local blobtype = hash.type + if blobpath then + local files = instance.files[blobpath] + if files then + for k,v in next, files do + if find(k,"^remap:") then + k = files[k] + v = files[k] -- chained + end + if find(k,pattern) then + if type(v) == "string" then + handle(blobtype,blobpath,v,k) + else + for _,vv in pairs(v) do -- ipairs? + handle(blobtype,blobpath,vv,k) + end + end + end + end + end + end + end +end + +function resolvers.locate_format(name) + local barename, fmtname = name:gsub("%.%a+$",""), "" + if resolvers.usecache then + local path = file.join(caches.setpath("formats")) -- maybe platform + fmtname = file.join(path,barename..".fmt") or "" + end + if fmtname == "" then + fmtname = resolvers.find_files(barename..".fmt")[1] or "" + end + fmtname = resolvers.clean_path(fmtname) + if fmtname ~= "" then + local barename = file.removesuffix(fmtname) + local luaname, lucname, luiname = barename .. ".lua", barename .. ".luc", barename .. ".lui" + if lfs.isfile(luiname) then + return barename, luiname + elseif lfs.isfile(lucname) then + return barename, lucname + elseif lfs.isfile(luaname) then + return barename, luaname + end + end + return nil, nil +end + +function resolvers.boolean_variable(str,default) + local b = resolvers.expansion(str) + if b == "" then + return default + else + b = toboolean(b) + return (b == nil and default) or b + end +end + +texconfig.kpse_init = false + +kpse = { original = kpse } setmetatable(kpse, { __index = function(k,v) return resolvers[v] end } ) + +-- for a while + +input = resolvers diff --git a/tex/context/base/data-tex.lua b/tex/context/base/data-tex.lua new file mode 100644 index 000000000..792c48fec --- /dev/null +++ b/tex/context/base/data-tex.lua @@ -0,0 +1,220 @@ +if not modules then modules = { } end modules ['data-tex'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- special functions that deal with io + +local format, lower = string.format, string.lower + +local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end) + +local texiowrite_nl = (texio and texio.write_nl) or print +local texiowrite = (texio and texio.write) or print + +local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders + +function finders.generic(tag,filename,filetype) + local foundname = resolvers.find_file(filename,filetype) + if foundname and foundname ~= "" then + if trace_locating then + logs.report("fileio",'+ finder: %s, file: %s', tag,filename) + end + return foundname + else + if trace_locating then + logs.report("fileio",'- finder: %s, file: %s', tag,filename) + end + return unpack(finders.notfound) + end +end + +--~ local getlines = lpeg.Ct(lpeg.linebyline) + +local input_translator, utf_translator, user_translator = nil, nil, nil + +function resolvers.install_text_filter(name,func) + if name == "input" then input_translator = func + elseif name == "utf" then utf_translator = func + elseif name == "user" then user_translator = func end +end + +function openers.text_opener(filename,file_handle,tag) + local u = unicode.utftype(file_handle) + local t = { } + if u > 0 then + if trace_locating then + logs.report("fileio",'+ opener: %s (%s), file: %s',tag,unicode.utfname[u],filename) + end + local l + if u > 2 then + l = unicode.utf32_to_utf8(file_handle:read("*a"),u==4) + else + l = unicode.utf16_to_utf8(file_handle:read("*a"),u==2) + end + file_handle:close() + t = { + utftype = u, -- may go away + lines = l, + current = 0, -- line number, not really needed + handle = nil, + noflines = #l, + close = function() + if trace_locating then + logs.report("fileio",'= closer: %s (%s), file: %s',tag,unicode.utfname[u],filename) + end + logs.show_close(filename) + t = nil + end, + reader = function(self) + self = self or t + local current, lines = self.current, self.lines + if current >= #lines then + return nil + else + current = current + 1 + self.current = current + local line = lines[current] + if not line then + return nil + elseif line == "" then + return "" + else + if input_translator then + line = input_translator(line) + end + if utf_translator then + line = utf_translator(line) + end + if user_translator then + line = user_translator(line) + end + return line + end + end + end + } + else + if trace_locating then + logs.report("fileio",'+ opener: %s, file: %s',tag,filename) + end + -- todo: file;name -> freeze / eerste regel scannen -> freeze + --~ local data = getlines:match(file_handle:read("*a")) + --~ local n = 0 + t = { + reader = function() -- self + local line = file_handle:read() + --~ n = n + 1 + --~ local line = data[n] + --~ print(line) + if not line then + return nil + elseif line == "" then + return "" + else + if input_translator then + line = input_translator(line) + end + if utf_translator then + line = utf_translator(line) + end + if user_translator then + line = user_translator(line) + end + return line + end + end, + close = function() + if trace_locating then + logs.report("fileio",'= closer: %s, file: %s',tag,filename) + end + logs.show_close(filename) + file_handle:close() + t = nil + end, + handle = function() + return file_handle + end, + noflines = function() + t.noflines = io.noflines(file_handle) + return t.noflines + end + } + end + return t +end + +function openers.generic(tag,filename) + if filename and filename ~= "" then + local f = io.open(filename,"r") + if f then + logs.show_open(filename) + return openers.text_opener(filename,f,tag) + end + end + if trace_locating then + logs.report("fileio",'- opener: %s, file: %s',tag,filename) + end + return unpack(openers.notfound) +end + +function loaders.generic(tag,filename) + if filename and filename ~= "" then + local f = io.open(filename,"rb") + if f then + logs.show_load(filename) + if trace_locating then + logs.report("fileio",'+ loader: %s, file: %s',tag,filename) + end + local s = f:read("*a") + if garbagecollector and garbagecollector.check then garbagecollector.check(#s) end + f:close() + if s then + return true, s, #s + end + end + end + if trace_locating then + logs.report("fileio",'- loader: %s, file: %s',tag,filename) + end + return unpack(loaders.notfound) +end + +function finders.tex(filename,filetype) + return finders.generic('tex',filename,filetype) +end + +function openers.tex(filename) + return openers.generic('tex',filename) +end + +function loaders.tex(filename) + return loaders.generic('tex',filename) +end + +function resolvers.findtexfile(filename, filetype) + return resolvers.methodhandler('finders',file.collapse_path(filename), filetype) +end + +function resolvers.opentexfile(filename) + return resolvers.methodhandler('openers',file.collapse_path(filename)) +end + +function resolvers.openfile(filename) + local fullname = resolvers.findtexfile(filename) + if fullname and (fullname ~= "") then + return resolvers.opentexfile(fullname) + else + return nil + end +end + +function resolvers.texdatablob(filename, filetype) + local ok, data, size = resolvers.loadbinfile(filename, filetype) + return data or "" +end + +resolvers.loadtexfile = resolvers.texdatablob diff --git a/tex/context/base/data-tmf.lua b/tex/context/base/data-tmf.lua new file mode 100644 index 000000000..302841a65 --- /dev/null +++ b/tex/context/base/data-tmf.lua @@ -0,0 +1,72 @@ +if not modules then modules = { } end modules ['data-tmf'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- loads *.tmf files in minimal tree roots (to be optimized and documented) + +function resolvers.check_environment(tree) + logs.simpleline() + os.setenv('TMP', os.getenv('TMP') or os.getenv('TEMP') or os.getenv('TMPDIR') or os.getenv('HOME')) + os.setenv('TEXOS', os.getenv('TEXOS') or ("texmf-" .. os.currentplatform())) + os.setenv('TEXPATH', (tree or "tex"):gsub("\/+$",'')) + os.setenv('TEXMFOS', os.getenv('TEXPATH') .. "/" .. os.getenv('TEXOS')) + logs.simpleline() + logs.simple("preset : TEXPATH => %s", os.getenv('TEXPATH')) + logs.simple("preset : TEXOS => %s", os.getenv('TEXOS')) + logs.simple("preset : TEXMFOS => %s", os.getenv('TEXMFOS')) + logs.simple("preset : TMP => %s", os.getenv('TMP')) + logs.simple('') +end + +function resolvers.load_environment(name) -- todo: key=value as well as lua + local f = io.open(name) + if f then + for line in f:lines() do + if line:find("^[%%%#]") then + -- skip comment + else + local key, how, value = line:match("^(.-)%s*([<=>%?]+)%s*(.*)%s*$") + if how then + value = value:gsub("%%(.-)%%", function(v) return os.getenv(v) or "" end) + if how == "=" or how == "<<" then + os.setenv(key,value) + elseif how == "?" or how == "??" then + os.setenv(key,os.getenv(key) or value) + elseif how == "<" or how == "+=" then + if os.getenv(key) then + os.setenv(key,os.getenv(key) .. io.fileseparator .. value) + else + os.setenv(key,value) + end + elseif how == ">" or how == "=+" then + if os.getenv(key) then + os.setenv(key,value .. io.pathseparator .. os.getenv(key)) + else + os.setenv(key,value) + end + end + end + end + end + f:close() + end +end + +function resolvers.load_tree(tree) + if tree and tree ~= "" then + local setuptex = 'setuptex.tmf' + if lfs.attributes(tree, "mode") == "directory" then -- check if not nil + setuptex = tree .. "/" .. setuptex + else + setuptex = tree + end + if io.exists(setuptex) then + resolvers.check_environment(tree) + resolvers.load_environment(setuptex) + end + end +end diff --git a/tex/context/base/data-tmp.lua b/tex/context/base/data-tmp.lua new file mode 100644 index 000000000..31d0147ea --- /dev/null +++ b/tex/context/base/data-tmp.lua @@ -0,0 +1,174 @@ +if not modules then modules = { } end modules ['data-tmp'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +--[[ldx-- +This module deals with caching data. It sets up the paths and +implements loaders and savers for tables. Best is to set the +following variable. When not set, the usual paths will be +checked. Personally I prefer the (users) temporary path.
+ + +TEXMFCACHE=$TMP;$TEMP;$TMPDIR;$TEMPDIR;$HOME;$TEXMFVAR;$VARTEXMF;. + + +Currently we do no locking when we write files. This is no real +problem because most caching involves fonts and the chance of them +being written at the same time is small. We also need to extend +luatools with a recache feature.
+--ldx]]-- + +local format, lower, gsub = string.format, string.lower, string.gsub + +local trace_cache = false trackers.register("resolvers.cache", function(v) trace_cache = v end) + +caches = caches or { } + +caches.path = caches.path or nil +caches.base = caches.base or "luatex-cache" +caches.more = caches.more or "context" +caches.direct = false -- true is faster but may need huge amounts of memory +caches.tree = false +caches.paths = caches.paths or nil +caches.force = false +caches.defaults = { "TEXMFCACHE", "TMPDIR", "TEMPDIR", "TMP", "TEMP", "HOME", "HOMEPATH" } + +function caches.temp() + local cachepath = nil + local function check(list,isenv) + if not cachepath then + for k=1,#list do + local v = list[k] + cachepath = (isenv and (os.env[v] or "")) or v or "" + if cachepath == "" then + -- next + else + cachepath = resolvers.clean_path(cachepath) + if lfs.isdir(cachepath) and file.iswritable(cachepath) then -- lfs.attributes(cachepath,"mode") == "directory" + break + elseif caches.force or io.ask(format("\nShould I create the cache path %s?",cachepath), "no", { "yes", "no" }) == "yes" then + dir.mkdirs(cachepath) + if lfs.isdir(cachepath) and file.iswritable(cachepath) then + break + end + end + end + cachepath = nil + end + end + end + check(resolvers.clean_path_list("TEXMFCACHE") or { }) + check(caches.defaults,true) + if not cachepath then + print("\nfatal error: there is no valid (writable) cache path defined\n") + os.exit() + elseif not lfs.isdir(cachepath) then -- lfs.attributes(cachepath,"mode") ~= "directory" + print(format("\nfatal error: cache path %s is not a directory\n",cachepath)) + os.exit() + end + cachepath = file.collapse_path(cachepath) + function caches.temp() + return cachepath + end + return cachepath +end + +function caches.configpath() + return table.concat(resolvers.instance.cnffiles,";") +end + +function caches.hashed(tree) + return md5.hex(gsub(lower(tree),"[\\\/]+","/")) +end + +function caches.treehash() + local tree = caches.configpath() + if not tree or tree == "" then + return false + else + return caches.hashed(tree) + end +end + +function caches.setpath(...) + if not caches.path then + if not caches.path then + caches.path = caches.temp() + end + caches.path = resolvers.clean_path(caches.path) -- to be sure + caches.tree = caches.tree or caches.treehash() + if caches.tree then + caches.path = dir.mkdirs(caches.path,caches.base,caches.more,caches.tree) + else + caches.path = dir.mkdirs(caches.path,caches.base,caches.more) + end + end + if not caches.path then + caches.path = '.' + end + caches.path = resolvers.clean_path(caches.path) + if not table.is_empty({...}) then + local pth = dir.mkdirs(caches.path,...) + return pth + end + caches.path = dir.expand_name(caches.path) + return caches.path +end + +function caches.definepath(category,subcategory) + return function() + return caches.setpath(category,subcategory) + end +end + +function caches.setluanames(path,name) + return path .. "/" .. name .. ".tma", path .. "/" .. name .. ".tmc" +end + +function caches.loaddata(path,name) + local tmaname, tmcname = caches.setluanames(path,name) + local loader = loadfile(tmcname) or loadfile(tmaname) + if loader then + return loader() + else + return false + end +end + +--~ function caches.loaddata(path,name) +--~ local tmaname, tmcname = caches.setluanames(path,name) +--~ return dofile(tmcname) or dofile(tmaname) +--~ end + +function caches.iswritable(filepath,filename) + local tmaname, tmcname = caches.setluanames(filepath,filename) + return file.iswritable(tmaname) +end + +function caches.savedata(filepath,filename,data,raw) + local tmaname, tmcname = caches.setluanames(filepath,filename) + local reduce, simplify = true, true + if raw then + reduce, simplify = false, false + end + if caches.direct then + file.savedata(tmaname, table.serialize(data,'return',false,true,false)) -- no hex + else + table.tofile(tmaname, data,'return',false,true,false) -- maybe not the last true + end + local cleanup = resolvers.boolean_variable("PURGECACHE", false) + local strip = resolvers.boolean_variable("LUACSTRIP", true) + utils.lua.compile(tmaname, tmcname, cleanup, strip) +end + +-- here we use the cache for format loading (texconfig.[formatname|jobname]) + +--~ if tex and texconfig and texconfig.formatname and texconfig.formatname == "" then +if tex and texconfig and (not texconfig.formatname or texconfig.formatname == "") and input and resolvers.instance then + if not texconfig.luaname then texconfig.luaname = "cont-en.lua" end -- or luc + texconfig.formatname = caches.setpath("formats") .. "/" .. gsub(texconfig.luaname,"%.lu.$",".fmt") +end diff --git a/tex/context/base/data-tre.lua b/tex/context/base/data-tre.lua new file mode 100644 index 000000000..9cac73b8e --- /dev/null +++ b/tex/context/base/data-tre.lua @@ -0,0 +1,43 @@ +if not modules then modules = { } end modules ['data-tre'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- \input tree://oeps1/**/oeps.tex + +local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders + +local done, found = { }, { } + +function finders.tree(specification,filetype) + local fnd = found[specification] + if not fnd then + local spec = resolvers.splitmethod(specification).path or "" + if spec ~= "" then + local path, name = file.dirname(spec), file.basename(spec) + if path == "" then path = "." end + local hash = done[path] + if not hash then + local pattern = path .. "/*" -- we will use the proper splitter + hash = dir.glob(pattern) + done[path] = hash + end + local pattern = "/" .. name:gsub("([%.%-%+])", "%%%1") .. "$" + for k, v in pairs(hash) do + if v:find(pattern) then + found[specification] = v + return v + end + end + end + fnd = unpack(finders.notfound) + found[specification] = fnd + end + return fnd +end + +openers.tree = openers.generic +loaders.tree = loaders.generic diff --git a/tex/context/base/data-use.lua b/tex/context/base/data-use.lua new file mode 100644 index 000000000..609ffd88f --- /dev/null +++ b/tex/context/base/data-use.lua @@ -0,0 +1,127 @@ +if not modules then modules = { } end modules ['data-use'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local format, lower, gsub = string.format, string.lower, string.gsub + +local trace_verbose = false trackers.register("resolvers.verbose", function(v) trace_verbose = v end) +local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v trackers.enable("resolvers.verbose") end) + +-- since we want to use the cache instead of the tree, we will now +-- reimplement the saver. + +local save_data = resolvers.save_data +local load_data = resolvers.load_data + +resolvers.cachepath = nil -- public, for tracing +resolvers.usecache = true -- public, for tracing + +function resolvers.save_data(dataname) + save_data(dataname, function(cachename,dataname) + resolvers.usecache = not toboolean(resolvers.expansion("CACHEINTDS") or "false",true) + if resolvers.usecache then + resolvers.cachepath = resolvers.cachepath or caches.definepath("trees") + return file.join(resolvers.cachepath(),caches.hashed(cachename)) + else + return file.join(cachename,dataname) + end + end) +end + +function resolvers.load_data(pathname,dataname,filename) + load_data(pathname,dataname,filename,function(dataname,filename) + resolvers.usecache = not toboolean(resolvers.expansion("CACHEINTDS") or "false",true) + if resolvers.usecache then + resolvers.cachepath = resolvers.cachepath or caches.definepath("trees") + return file.join(resolvers.cachepath(),caches.hashed(pathname)) + else + if not filename or (filename == "") then + filename = dataname + end + return file.join(pathname,filename) + end + end) +end + +-- we will make a better format, maybe something xml or just text or lua + +resolvers.automounted = resolvers.automounted or { } + +function resolvers.automount(usecache) + local mountpaths = resolvers.clean_path_list(resolvers.expansion('TEXMFMOUNT')) + if table.is_empty(mountpaths) and usecache then + mountpaths = { caches.setpath("mount") } + end + if not table.is_empty(mountpaths) then + statistics.starttiming(resolvers.instance) + for k, root in pairs(mountpaths) do + local f = io.open(root.."/url.tmi") + if f then + for line in f:lines() do + if line then + if line:find("^[%%#%-]") then -- or %W + -- skip + elseif line:find("^zip://") then + if trace_locating then + logs.report("fileio","mounting %s",line) + end + table.insert(resolvers.automounted,line) + resolvers.usezipfile(line) + end + end + end + f:close() + end + end + statistics.stoptiming(resolvers.instance) + end +end + +-- status info + +statistics.register("used config path", function() return caches.configpath() end) +statistics.register("used cache path", function() return caches.temp() or "?" end) + +-- experiment (code will move) + +function statistics.save_fmt_status(texname,formatbanner,sourcefile) -- texname == formatname + local enginebanner = status.list().banner + if formatbanner and enginebanner and sourcefile then + local luvname = file.replacesuffix(texname,"luv") + local luvdata = { + enginebanner = enginebanner, + formatbanner = formatbanner, + sourcehash = md5.hex(io.loaddata(resolvers.find_file(sourcefile)) or "unknown"), + sourcefile = sourcefile, + } + io.savedata(luvname,table.serialize(luvdata,true)) + end +end + +function statistics.check_fmt_status(texname) + local enginebanner = status.list().banner + if enginebanner and texname then + local luvname = file.replacesuffix(texname,"luv") + if lfs.isfile(luvname) then + local luv = dofile(luvname) + if luv and luv.sourcefile then + local sourcehash = md5.hex(io.loaddata(resolvers.find_file(luv.sourcefile)) or "unknown") + if luv.enginebanner and luv.enginebanner ~= enginebanner then + return "engine mismatch" + end + if luv.sourcehash and luv.sourcehash ~= sourcehash then + return "source mismatch" + end + else + return "invalid status file" + end + else + return "missing status file" + end + end + return true +end diff --git a/tex/context/base/data-zip.lua b/tex/context/base/data-zip.lua new file mode 100644 index 000000000..dcb6b170a --- /dev/null +++ b/tex/context/base/data-zip.lua @@ -0,0 +1,241 @@ +if not modules then modules = { } end modules ['data-zip'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local format, find = string.format, string.find + +local trace_locating, trace_verbose = false, false + +trackers.register("resolvers.verbose", function(v) trace_verbose = v end) +trackers.register("resolvers.locating", function(v) trace_locating = v trace_verbose = v end) + +zip = zip or { } +zip.archives = zip.archives or { } +zip.registeredfiles = zip.registeredfiles or { } + +local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders +local locators, hashers, concatinators = resolvers.locators, resolvers.hashers, resolvers.concatinators + +local archives = zip.archives + +-- zip:///oeps.zip?name=bla/bla.tex +-- zip:///oeps.zip?tree=tex/texmf-local + +local function validzip(str) -- todo: use url splitter + if not find(str,"^zip://") then + return "zip:///" .. str + else + return str + end +end + +function zip.openarchive(name) + if not name or name == "" then + return nil + else + local arch = archives[name] + if not arch then + local full = resolvers.find_file(name) or "" + arch = (full ~= "" and zip.open(full)) or false + archives[name] = arch + end + return arch + end +end + +function zip.closearchive(name) + if not name or (name == "" and archives[name]) then + zip.close(archives[name]) + archives[name] = nil + end +end + +-- zip:///texmf.zip?tree=/tex/texmf +-- zip:///texmf.zip?tree=/tex/texmf-local +-- zip:///texmf-mine.zip?tree=/tex/texmf-projects + +function locators.zip(specification) -- where is this used? startup zips (untested) + specification = resolvers.splitmethod(specification) + local zipfile = specification.path + local zfile = zip.openarchive(name) -- tricky, could be in to be initialized tree + if trace_locating then + if zfile then + logs.report("fileio",'! zip locator, found: %s',specification.original) + else + logs.report("fileio",'? zip locator, not found: %s',specification.original) + end + end +end + +function hashers.zip(tag,name) + if trace_verbose then + logs.report("fileio","loading zip file %s as %s",name,tag) + end + resolvers.usezipfile(format("%s?tree=%s",tag,name)) +end + +function concatinators.zip(tag,path,name) + if not path or path == "" then + return format('%s?name=%s',tag,name) + else + return format('%s?name=%s/%s',tag,path,name) + end +end + +function resolvers.isreadable.zip(name) + return true +end + +function finders.zip(specification,filetype) + specification = resolvers.splitmethod(specification) + if specification.path then + local q = url.query(specification.query) + if q.name then + local zfile = zip.openarchive(specification.path) + if zfile then + if trace_locating then + logs.report("fileio",'! zip finder, path: %s',specification.path) + end + local dfile = zfile:open(q.name) + if dfile then + dfile = zfile:close() + if trace_locating then + logs.report("fileio",'+ zip finder, name: %s',q.name) + end + return specification.original + end + elseif trace_locating then + logs.report("fileio",'? zip finder, path %s',specification.path) + end + end + end + if trace_locating then + logs.report("fileio",'- zip finder, name: %s',filename) + end + return unpack(finders.notfound) +end + +function openers.zip(specification) + local zipspecification = resolvers.splitmethod(specification) + if zipspecification.path then + local q = url.query(zipspecification.query) + if q.name then + local zfile = zip.openarchive(zipspecification.path) + if zfile then + if trace_locating then + logs.report("fileio",'+ zip starter, path: %s',zipspecification.path) + end + local dfile = zfile:open(q.name) + if dfile then + logs.show_open(specification) + return openers.text_opener(specification,dfile,'zip') + end + elseif trace_locating then + logs.report("fileio",'- zip starter, path %s',zipspecification.path) + end + end + end + if trace_locating then + logs.report("fileio",'- zip opener, name: %s',filename) + end + return unpack(openers.notfound) +end + +function loaders.zip(specification) + specification = resolvers.splitmethod(specification) + if specification.path then + local q = url.query(specification.query) + if q.name then + local zfile = zip.openarchive(specification.path) + if zfile then + if trace_locating then + logs.report("fileio",'+ zip starter, path: %s',specification.path) + end + local dfile = zfile:open(q.name) + if dfile then + logs.show_load(filename) + if trace_locating then + logs.report("fileio",'+ zip loader, name: %s',filename) + end + local s = dfile:read("*all") + dfile:close() + return true, s, #s + end + elseif trace_locating then + logs.report("fileio",'- zip starter, path: %s',specification.path) + end + end + end + if trace_locating then + logs.report("fileio",'- zip loader, name: %s',filename) + end + return unpack(openers.notfound) +end + +-- zip:///somefile.zip +-- zip:///somefile.zip?tree=texmf-local -> mount + +function resolvers.usezipfile(zipname) + zipname = validzip(zipname) + if trace_locating then + logs.report("fileio",'! zip use, file: %s',zipname) + end + local specification = resolvers.splitmethod(zipname) + local zipfile = specification.path + if zipfile and not zip.registeredfiles[zipname] then + local tree = url.query(specification.query).tree or "" + if trace_locating then + logs.report("fileio",'! zip register, file: %s',zipname) + end + local z = zip.openarchive(zipfile) + if z then + local instance = resolvers.instance + if trace_locating then + logs.report("fileio","= zipfile, registering: %s",zipname) + end + statistics.starttiming(instance) + resolvers.prepend_hash('zip',zipname,zipfile) + resolvers.extend_texmf_var(zipname) -- resets hashes too + zip.registeredfiles[zipname] = z + instance.files[zipname] = resolvers.register_zip_file(z,tree or "") + statistics.stoptiming(instance) + elseif trace_locating then + logs.report("fileio","? zipfile, unknown: %s",zipname) + end + elseif trace_locating then + logs.report("fileio",'! zip register, no file: %s',zipname) + end +end + +function resolvers.register_zip_file(z,tree) + local files, filter = { }, "" + if tree == "" then + filter = "^(.+)/(.-)$" + else + filter = format("^%s/(.+)/(.-)$",tree) + end + if trace_locating then + logs.report("fileio",'= zip filter: %s',filter) + end + local register, n = resolvers.register_file, 0 + for i in z:files() do + local path, name = i.filename:match(filter) + if path then + if name and name ~= '' then + register(files, name, path) + n = n + 1 + else + -- directory + end + else + register(files, i.filename, '') + n = n + 1 + end + end + logs.report("fileio",'= zip entries: %s',n) + return files +end diff --git a/tex/context/base/enco-cyr.tex b/tex/context/base/enco-cyr.tex index 0ac82207f..36bca82b5 100644 --- a/tex/context/base/enco-cyr.tex +++ b/tex/context/base/enco-cyr.tex @@ -256,6 +256,8 @@ % \definecharacter textperthousand {\%\char 24 } % \definecharacter textpertenthousand {\%\char 24\char 24 } +\definecharacter cyrillicgheupturn 160 % to satisfy the patterns + \stopencoding \startmapping[t2b] diff --git a/tex/context/base/enco-def.tex b/tex/context/base/enco-def.tex index c319c0065..a0631ac25 100644 --- a/tex/context/base/enco-def.tex +++ b/tex/context/base/enco-def.tex @@ -509,6 +509,7 @@ \definecharacter greekzeta {\zeta} \definecharacter greeketa {\eta} \definecharacter greektheta {\theta} +\definecharacter greekthetaalt {\vartheta} \definecharacter greekiota {\iota} \definecharacter greekkappa {\kappa} \definecharacter greeklambda {\lambda} @@ -522,7 +523,8 @@ \definecharacter greeksigma {\sigma} \definecharacter greektau {\tau} \definecharacter greekupsilon {\upsilon} -\definecharacter greekphi {\phi} +\definecharacter greekphi {\varphi} +\definecharacter greekphialt {\phi} \definecharacter greekchi {\chi} \definecharacter greekpsi {\psi} \definecharacter greekomega {\omega} @@ -893,7 +895,7 @@ \startencoding[\s!default] -\definecharacter texthorizontalbar {{--\kern\zeropoint--}} +\definecharacter texthorizontalbar {{\endash\kern\zeropoint\endash}} \definecharacter textdong {\underbar{\dstroke}} \stopencoding diff --git a/tex/context/base/enco-fpl.tex b/tex/context/base/enco-fpl.tex index ee9d98dc8..14d102ff1 100644 --- a/tex/context/base/enco-fpl.tex +++ b/tex/context/base/enco-fpl.tex @@ -21,7 +21,7 @@ \startlanguagespecifics[\s!pl] - \appendtoks \makecharacteractive / \to \everynormalcatcodes +% \appendtoks \makecharacteractive / \to \everynormalcatcodes % obsolete \installcompoundcharacter /a {\aogonek} \installcompoundcharacter /c {\cacute} diff --git a/tex/context/base/enco-ini.mkii b/tex/context/base/enco-ini.mkii index d39a64fca..9379c3a7f 100644 --- a/tex/context/base/enco-ini.mkii +++ b/tex/context/base/enco-ini.mkii @@ -1,34 +1,1137 @@ %D \module %D [ file=enco-ini, -%D version=2007.02.19, +%D version=2007.02.19, % 2000.12.27, % 1998.12.03, %D title=\CONTEXT\ Encoding Macros, %D subtitle=Initialization, %D author=Hans Hagen, %D date=\currentdate, -%D copyright=\PRAGMA] +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -%D In the end we will cleanup enco-ini.tex! +%D Quite some code will be moved to the mk files once we're ready +%D for it. + +%D This module is a reimplementation of the module that handled +%D composed characters and non \ASCII\ characters. The changed +%D are not that fundamental, and mainly concerns moving +%D definitions of specific glyphs and accents to other files as +%D well as moving plain handling of accents to this module +%D instead of overloading plain \TEX\ commands. + +%D Patterns are kind of mixed with font encodings and +%D mappings. Alas. + +\ifx\synchronizepatterns\undefined \let\synchronizepatterns\relax \fi + +%D While dealing with input (the text source) and output (the +%D glyphs), encoding comes into view. To summarize a few: +%D +%D \startitemize +%D \item Bytes in the input file are mapped to an internal +%D representation. An~\type {a} often stays an~\type {a}, +%D but~\type {\"e} can become either one code or become +%D two codes (ending in overlapping glyphs). +%D \item Characters can be made active and mapped onto another +%D character. +%D \item When changing case, characters are mapped onto +%D themselves, their case||counterpart or a reasonable +%D alternative, like~\"e onto~e. +%D \item Single character representations in a \DVI\ file can +%D be mapped onto one or more characters, either of not +%D in more than one font file (virtual fonts). +%D \item In the final format, fonts collections can be +%D partially embedded, thereby losing the one||to||one +%D relation between several instances of one font. +%D \item For special purposes, individual characters should be +%D mapped onto a dedicated encoding vector, for instance +%D \PDF\ document encoding. +%D \stopitemize +%D +%D These and other kind of mappings are to be dealt with, and +%D the exact way of dealing often depends on the language to be +%D typeset. + +\writestatus{loading}{ConTeXt Encoding Macros / Initialization} \unprotect -\beginOLDTEX +%D First we define a few local or not yet initialized constants. - \useencoding[ans,il2,ec,tbo,pdf,pol,qx,t5,cyr,agr] % pol and il2 will go away, not needed in mkiv, uc removed +\def\@map@{@m@ap@} % mapping prefix +\def\@fha@{@f@ha@} % font prefix +\def\@cas@{@c@as@} % casecom prefix - \useencoding[032,033,037] % fallbacks for some unicode chars +\ifx\currentlanguage\undefined \let\currentlanguage\s!en \fi - \setupencoding[\s!default=ec] % was: [\s!default=\s!default] +%D \macros +%D {setupencoding} +%D +%D The following setup command is used to tune encoding +%D handling. -\endOLDTEX +\def\setupencoding + {\dosingleargument\dosetupencoding} -\beginXETEX +\def\dosetupencoding[#1]% + {\getparameters[\??ec][#1]% + \edef\defaultencoding + {\ifx\@@ecdefault\empty\s!default\else\@@ecdefault\fi}} - \setupencoding[\s!default=\s!default] +%D \macros +%D {useencoding} +%D +%D Encodings things are defined in separate files and are +%D loaded only once, using: +%D +%D \showsetup{useencoding} + +\def\douseencoding#1% + {\doifundefined{\c!file\f!encodingprefix#1}% + {\letvalue{\c!file\f!encodingprefix#1}\empty + \makeshortfilename[\truefilename{\f!encodingprefix#1}]% + \startreadingfile + \readsysfile\shortfilename + {\showmessage\m!encodings2{#1}} + {\showmessage\m!encodings3{#1}}% + \stopreadingfile}} + +\def\useencoding[#1]% + {\processcommalist[#1]\douseencoding} + +%D \macros +%D {startmapping,enablemapping} +%D +%D In order to process patterns, convert from lower to +%D uppercase and vise versa and some more, we provide a +%D mechanism to define mappings. The first real application +%D of this command was: +%D +%D \starttyping +%D \startmapping [something] +%D \definecasemap 165 181 165 +%D \definecasemap 171 187 171 +%D ... +%D \defineuppercasecom \i {I} +%D \defineuppercasecom \l \L +%D \definelowercasecom \AE \ae +%D ... +%D \stopmapping +%D \stoptyping +%D +%D So, character 165 becomes 181 in uppercase and 165 in +%D lowercase. A mapping is activated with \type {\enablemapping}. + +\def\startsavingmappingtoks#1% + {\bgroup + \edef\charactermapping{@#1@}% + \checkmappingtoks + \setmappingtoks + \the\mappingtoks} + +\def\stopsavingmappingtoks + {\global\mappingtoks\emptytoks + \dostepwiserecurse{0}{255}\plusone + {\edef\@@expanded + {\the\mappingtoks + \ifnum\recurselevel>127 + \noexpand\settoletterunlessactive{\recurselevel}% + \fi + \lccode\recurselevel\ifnum\lccode\recurselevel=\zerocount\zerocount\else\space\the\lccode\recurselevel\space\fi + \uccode\recurselevel\ifnum\uccode\recurselevel=\zerocount\zerocount\else\space\the\uccode\recurselevel\space\fi + \ifnum\sfcode\recurselevel=\plusthousand\else\sfcode\recurselevel=\the\sfcode\recurselevel\space\fi + }% + \global\mappingtoks\expandafter{\@@expanded}}% + \egroup + \let\enabledmapping\empty + \enablemapping[\currentmapping]} + +\def\startmapping[#1]% + {\startsavingmappingtoks{#1}} + +\def\stopmapping + {\stopsavingmappingtoks} + +\def\optimizemapping[#1]% + {\startsavingmappingtoks{#1}% + % nothing, just an automatic cleanup + \stopsavingmappingtoks + % we need to resync + %\let\enabledmapping\relax + }%\enablemapping[\currentmapping]} + +\def\setmappingtoks + {\@EA\let\@EA\mappingtoks\csname\@map@\charactermapping\endcsname + \@EA\let\@EA\casecomtoks\csname\@cas@\charactermapping\endcsname} + +\def\checkmappingtoks + {\ifundefined{\@map@\charactermapping}% + \expandafter\newtoks\csname\@map@\charactermapping\endcsname + \fi + \ifundefined{\@cas@\charactermapping}% + \expandafter\newtoks\csname\@cas@\charactermapping\endcsname + \fi} + +\def\definecasemap #1 #2 #3 % code lower upper + {\doifelse{#2}{to} + {\presetcaserange{#1}{#3}} + {\lccode#1=#2\relax + \uccode#1=#3\relax}% + \ignorespaces} + +%D Saves a few tokens + +\def\definecaseswap #1 #2 % lower upper + {\lccode#1=#1\relax + \uccode#2=#2\relax + \lccode#2=#1\relax + \uccode#1=#2\relax + \ignorespaces} + +\def\definecaseself #1 % lower=upper=self + {\lccode#1=#1\relax + \uccode#1=#1\relax + \ignorespaces} + +%D Watch the \type {\definecasemap 127 to 255} option! +%D Dedicated to Taco there is also: + +\def\definecasemaps #1 to #2 lc #3 uc #4 % from to lc+ uc+ + {\dostepwiserecurse{#1}{#2}\plusone + {\scratchcounter\recurselevel\advance\scratchcounter#3\lccode\recurselevel=\scratchcounter + \scratchcounter\recurselevel\advance\scratchcounter#4\uccode\recurselevel=\scratchcounter}% + \ignorespaces} + +%D This can be used like: +%D +%D \starttyping +%D \definecasemaps 128 to 156 lc 32 uc 0 +%D \definecasemaps 160 to 188 lc -32 uc 0 +%D \definecasemaps 160 to 188 lc -32 uc 0 +%D \definecasemaps 192 to 255 lc 32 uc 0 +%D \stoptyping +%D +%D and saves a lot of typing (copying). + +\def\resetcaserange #1 to #2 + {\dostepwiserecurse{#1}{#2}\plusone + {\lccode\recurselevel\zerocount + \uccode\recurselevel\zerocount}% + \ignorespaces} + +\def\presetcaserange#1#2% could be pre-expanded + {\dostepwiserecurse{#1}{#2}\plusone + {\lccode\recurselevel=\recurselevel + \uccode\recurselevel=\recurselevel}% + \ignorespaces} + +\def\setcasemap #1 #2 #3 % + {\settoletterunlessactive{#1}% + \lccode #1=#2 + \uccode #1=#3 } + +\def\setcaseswap #1 #2 % + {\settoletterunlessactive{#1}% + \settoletterunlessactive{#2}% + \lccode #1=#1 + \uccode #2=#2 + \lccode #2=#1 + \uccode #1=#2 } + +\def\setcaseself #1 % + {\settoletterunlessactive{#1}% + \lccode #1=#1 + \uccode #1=#1 } + +\def\definespacemap #1 #2 % code sfcode + {\sfcode#1=#2% + \ignorespaces} + +\def\setspacemap #1 #2 % + {\settootherunlessactive{#1}% + %\lccode #1=\zerocount + %\uccode #1=\zerocount + \sfcode #1=#2 } + +\def\defineuppercasecom#1#2% + {\global\casecomtoks\expandafter{\the\casecomtoks\setuppercasecom#1{#2}}% + \ignorespaces} + +\def\definelowercasecom#1#2% + {\global\casecomtoks\expandafter{\the\casecomtoks\setlowercasecom#1{#2}}% + \ignorespaces} + +\let\setuppercasecom\gobbletwoarguments +\let\setlowercasecom\gobbletwoarguments + +\def\setcasecom#1#2{\def#1{#2}} + +\let\enabledmapping\empty % indirect, needed to handle default too + +\def\enablemapping[#1]% + {\edef\charactermapping{@#1@}% + \ifx\enabledmapping\charactermapping \else + \doifdefined{\@map@\charactermapping} + {%\expandafter\showthe\csname\@map@\charactermapping\endcsname\endcsname + \the\csname\@map@\charactermapping\endcsname}% + % == \the\executeifdefined{\@map@\charactermapping}\emptytoks + \edef\enabledmapping{\charactermapping}% + \enablelanguagespecifics[\currentlanguage]% new + % \edef\enabledmapping{\charactermapping\currentlanguage}% can be comma list + \fi + \synchronizepatterns} + +% on behalf of font switching: + +\def\fastenablemapping#1% + {\edef\charactermapping{@#1@}% + \ifx\enabledmapping\charactermapping \else + \@EA\ifx\csname\@map@\charactermapping\endcsname\relax\else + \the\csname\@map@\charactermapping\endcsname + \fi + % == \the\executeifdefined{\@map@\charactermapping}\emptytoks + \let\enabledmapping\charactermapping + \enablelanguagespecifics[\currentlanguage]% to faster + \fi} + +%D This macro wil be implemented in \type {lang-ini.tex}. + +\ifx\enablelanguagespecifics\undefined + \def\enablelanguagespecifics[#1]{} +\fi + +%D Further on we have to take some precautions when dealing +%D with special characters like~\type{~}, \type{_} +%D and~\type{^}, so let us define ourselve some handy macros +%D first. + +\def\protectfontcharacters + {\edef\unprotectfontcharacters + {\catcode`\noexpand ~=\the\catcode`~\relax + \catcode`\noexpand _=\the\catcode`_\relax + \catcode`\noexpand ^=\the\catcode`^\relax}% + \catcode`~=\@@letter + \catcode`_=\@@letter + \catcode`^=\@@letter\relax} + +%D The completeness of the Computer Modern Roman typefaces +%D makes clear how incomplete other faces are. To honour 7~bit +%D \ASCII, these fonts were designed using only the first 127 +%D values of the 256 ones that can be presented by one byte. +%D Nowadays 8~bit character codings are more common, mainly +%D because they permit us to predefine some composed +%D characters, which are needed in most european languages. +%D +%D Supporting more than the standard \TEX\ encoding vector +%D |<|which in itself is far from standard and differs per +%D font|>| puts a burden on the fonts mechanism. The \CONTEXT\ +%D mechanism is far from complete, but can handle several +%D schemes at once. The main problem lays in the accented +%D characters and ligatures like~ff, although handling +%D ligatures is not the responsibility of this module. +%D +%D By default, we use \PLAIN\ \TEX's approach of placing +%D accents. All other schemes sooner or later give problems +%D when we distribute \DVI||files are distributed across +%D machines and platforms. Nevertheless, we have to take care +%D of different encoding vectors, which tell us where to find +%D the characters we need. This means that all kind of +%D character placement macro's like \type{\"} and \type{\ae} +%D have to be implemented and adapted in a way that suits +%D these vectors. +%D +%D The main difference between different vector is the way +%D accents are ordered and/or the availability of prebuilt +%D accented characters. Accented characters can for instance be +%D called for by sequences like \type{\"e}. Here the \type{\"} +%D is defined as: +%D +%D \starttyping +%D \def\"#1{{\accent"7F #1}} +%D \stoptyping +%D +%D This macro places the accent \accent"7F {} on top of an~e +%D gives \"e. Some fonts however can have prebuild accents and +%D use a more direct approach like +%D +%D \starttyping +%D \def\"#1{\if#1e\char 235\else ... \fi} +%D \stoptyping +%D +%D The latter approach is not used in \CONTEXT, because we +%D store relevant combinations of accents and characters in +%D individual macros. + +%D We define character substitutes and commands with definition +%D commands like: +%D +%D \starttyping +%D \startcoding[texnansi] +%D +%D \defineaccent " a 228 +%D \defineaccent ^ e 234 +%D \defineaccent ' {\dotlessi} 237 +%D +%D \definecharacter ae 230 +%D \definecharacter oe 156 +%D +%D \definecommand b \texnansiencodedb +%D \definecommand c \texnansiencodedc +%D +%D \stopcoding +%D \stoptyping +%D +%D The last argument of \type{\defineaccent} and +%D \type{\definecharacter} tells \TEX\ the position of the +%D accented character in the encoding vector. In order to +%D complish this, we tag each implementation with the character +%D coding identifier. We therefore need two auxiliary variables +%D \type{\characterencoding} and \type{\nocharacterencoding}. These +%D contain the current and default encoding vectors and both +%D default to the \PLAIN\ one. + +\edef\characterencoding {@\s!default @} +\edef\nocharacterencoding {@\s!default @} +\edef\charactermapping {@\s!default @} + +% todo, else \d j == \dj, print file and check + +\def\accentprefix {}%{*} +\def\commandprefix {}%{=} +\def\characterprefix{}%{-} + +%D \macros +%D {startcoding, reducetocoding} +%D +%D Before we can redefine accents and special characters, we +%D have to tell \CONTEXT\ what encoding is in force. The next +%D command is responsible for doing this and also takes care of +%D the definition of the recoding commands. We use the \type +%D {\start}||\type {\stop}||commands for definitions and the +%D \type {\reduceto}||command for local switching to +%D simplified commands. + +% etex : \ifcsname + +\def\justhandleaccent#1#2% \empty makes #2={} save % no \unexpanded + {\ifundefined{\accentprefix\characterencoding#1\string#2\empty}% + #2% + \else + \csname\accentprefix\characterencoding#1\string#2\empty\endcsname + \fi} + +\def\justhandlecommand#1% % no \unexpanded, otherwise pdfdoc will fail + {\ifundefined{\commandprefix\characterencoding#1}% as well as hyph patterns + #1% + \else + \csname\commandprefix\characterencoding#1\endcsname + \fi} + +\def\enableencoding + {\dodoubleempty\doenableencoding} + +\def\doenableencoding[#1][#2]% main fallback + {\iffirstargument\edef\characterencoding{@#1@}\fi + \edef\nocharacterencoding{@\ifsecondargument#2\else\s!default\fi @}% + \synchronizepatterns} + +\edef\xnocharacterencoding{@\s!default @} + +\def\fastenableencoding#1% + {\edef\characterencoding{@#1@}% + \let\nocharacterencoding\xnocharacterencoding} + +\def\startencoding + {\dodoubleempty\dostartencoding} + +\def\dostartencoding[#1][#2]% encoding regime + {%\showmessage\m!encodings1{#1}% + \pushmacro\characterencoding + \pushmacro\currentregime + \pushmacro\dohandleaccent % still needed? + \pushmacro\dohandlecommand % still needed? + \pushmacro\doautosetregime + \let\dohandleaccent\donthandleaccent % still needed? + \let\dohandlecommand\donthandlecommand % still needed? + %let\definesortkey\savesortkey + \edef\characterencoding{@#1@}% + \doifelsenothing{#2}% + {\let\doautosetregime\gobbletwoarguments} + {\def\currentregime{#2}}} + +\def\stopencoding + {\popmacro\doautosetregime + \popmacro\dohandlecommand % still needed? + \popmacro\dohandleaccent % still needed? + \popmacro\currentregime + \popmacro\characterencoding} + +% probably obsolete (hm, not yet) + +\def\reducetocoding[#1]% use grouped! + {\doifsomething{#1} + {\let\dohandleaccent \justhandleaccent + \let\dohandlecommand\justhandlecommand + \enableencoding[#1]% + \enablelanguagespecifics[\currentlanguage]}} + +\let\startcoding \startencoding +\def\stopcoding {\stopencoding} +\let\enablecoding \enableencoding + +%D The use of these macros are not limited to font +%D definition files, but may also be used when loading +%D patterns. + +%D \macros +%D {definesortkey,flushsortkeys,flushsortkey} +%D +%D Yet another definition concerns sorting of indexes and +%D lists. +%D +%D \starttyping +%D \definesortkey {\'e} {e} {a} {\'e} +%D \stoptyping +%D +%D The first argument denotes the string to be treated. The +%D second argument is the raw replacement, while the third +%D argument determines the sort order given the replacement. +%D The last argument is used as entry in the index (a, b, etc). +%D +%D The keys can be flushed using \type {\flushsortkeys} +%D which in turn results in a sequence of calls to \type +%D {\flushsortkey}, a macro taking 4~arguments. +%D +%D This mechanism is currently being tested and subjected to +%D changes! Obsolete: + +\let\definesortkey\gobblefourarguments +\let\savesortkey \gobblefourarguments +\let\flushsortkeys\relax +\let\flushsortkey \relax + +%D \macros +%D {defineaccent, definecharacter, definecommand} +%D +%D The actual definition of accents, special characters and +%D commands is done with the next three commands. + +\def\defineaccent + {\protectfontcharacters + \dodefineaccent} + +\def\dodefineaccent#1 #2 % + {\unprotectfontcharacters + \dododefineaccent#1 #2 } + +\def\dododefineaccent#1 #2 #3 % + {\setvalue{#1}{\dohandleaccent{#1}}% + \doifnumberelse{\string#3} + {\setvalue{\accentprefix\characterencoding#1\string#2}{\char#3 }} % space added + {\setvalue{\accentprefix\characterencoding#1\string#2}{#3}}} + +\def\dohandleaccent#1#2% + {\ifcsname\accentprefix\characterencoding#1\string#2\empty\endcsname + \csname\accentprefix\characterencoding#1\string#2\empty\endcsname + \else\ifcsname\accentprefix\nocharacterencoding#1\string#2\empty\endcsname + \csname\accentprefix\nocharacterencoding#1\string#2\empty\endcsname + \else\ifcsname\accentprefix\characterencoding#1\endcsname + \csname\accentprefix\characterencoding#1\endcsname{#2}% + \else%\ifcsname\accentprefix\nocharacterencoding#1\endcsname + \csname\accentprefix\nocharacterencoding#1\endcsname{#2}% +% \else +% \donormaltextaccent{#1}{#2}% + \fi\fi\fi}%\fi} + +\def\patternchar#1 {\rawcharacter{#1}} % space is part of character definition ! + +% \ifx \enablepatterntokens\undefined +% \def\handlepatterntoken#1]{\csname#1\endcsname} +% \fi + +% we need to postpone catcode changes, e.g. hr patterns +% have \catcode" -> which fails when " is letter + +\def\pathypsettings + {\ifx \enablepatterntokens\undefined + \defineactivecharacter [ {\handlepatterntoken}% + \else + \enablepatterntokens + \fi + \let\dochar\thechr + \lccode16=16 % brrr, extra quote in ec (turkish) + \lccode17=17 % brrr, extra quote in ec (turkish) + \lccode`\-=`\- + \lccode`\'=`\' + \lccode`\"=`\" + \relax} + +\def\patterns {\pathypsettings\normalpatterns } +\def\hyphenation{\pathypsettings\normalhyphenation} + +%D Because we don't want to use the second command grouped, we +%D (re)define it as follows: + +\def\hyphenation + {\begingroup\def\hyphenation{\normalhyphenation{\the\scratchtoks}\endgroup}% + \pathypsettings\afterassignment\hyphenation\scratchtoks=} + +%D This is not needed for patterns because they are loaded grouped +%D anyway and it saves us an assignment. Can go ... no longer +%D shared patterns. + +\def\startpatternloading#1#2#3% % we should use \everypatternloading + {\startreadingfile + \bgroup + % let's get rid of interfering stuff + \let\everyjob\scratchtoks + \let\message \gobbleoneargument + % we want direct characters + \let\char\patternchar + \doifelsenothing{#2}{\enableencoding[ec]}{\enableencoding[#2]}% + \doifelsenothing{#3}{\enablemapping [ec]}{\enablemapping [#3]}% + \expanded{\doifinstring{\f!languageprefix}{#1}} + {\ifx \enablepatternxml\undefined \else + \enablepatternxml + \fi}% + \let\dohandleaccent\normaldohandleaccent} + +\def\stoppatternloading + {\egroup + \stopreadingfile} + + \def\thechr#1{\char#1 } % just in case \relax interferes +\unexpanded\def\numchr#1{\char#1\relax} +\unexpanded\def\strchr#1{\csname#1\endcsname} + +\let\dochar\numchr + +\def\startdirectcharacters {\pushmacro\dochar \let\dochar\thechr} +\def\stopdirectcharacters {\popmacro \dochar} + +\def\definecharacter#1 #2 % + {\ifundefined{#1}\setvalue{#1}{\dohandlecharacter{#1}}\fi + \doifnumberelse{\string#2} + {\setvalue{\characterprefix\characterencoding\string#1}{\dochar{#2}}% + \doautosetregime{#1}{#2}} + {\setvalue{\characterprefix\characterencoding\string#1}{#2}}} + +\def\dohandlecharacter#1% + {\csname\characterprefix\ifcsname\characterprefix\characterencoding#1\endcsname + \characterencoding\else\nocharacterencoding\fi#1\endcsname} + +% \def\fallbackpatternchar{x} % makes no sense, duplicate patterns + +\def\defaultcharacter#1% + {\csname\characterprefix\nocharacterencoding\strippedcsname#1\endcsname} + +%D Instead of numbers, a command may be entered. + +\def\definecommand#1 #2 % + {\setvalue{\string#1}{\dohandlecommand{#1}}% + %\redefinecommand #1 % just to be sure + \setvalue{\commandprefix\characterencoding\string#1}{#2}} + +%D Here we see that redefining accents is characters is more +%D or less the same as redefining commands. We also could have +%D said: +%D +%D \starttyping +%D \def\defineaccent#1 #2 {\definecommand#1\string#2 \char} +%D \def\definecharacter#1 {\definecommand#1 \char} +%D \stoptyping + +%D \macros +%D {defineaccentcommand} +%D +%D When needed, one can overload the default positions of the +%D accents. The \PLAIN\ \TEX\ defaults are: +%D +%D \starttyping +%D \defineaccentcommand ` 18 +%D \defineaccentcommand ' 19 +%D \defineaccentcommand v 20 +%D \defineaccentcommand u 21 +%D \defineaccentcommand = 22 +%D \defineaccentcommand ^ 94 +%D \defineaccentcommand . 95 +%D \defineaccentcommand H 125 % "7D +%D \defineaccentcommand ~ 126 % "7E +%D \defineaccentcommand " 127 % "7F +%D \stoptyping -\endXETEX +\def\defineaccentcommand + {\protectfontcharacters + \dodefineaccentcommand} + +\def\dodefineaccentcommand#1 #2 % \string toegevoegd + {\doifnumberelse{\string#2} + {\setvalue{\accentprefix\characterencoding\string#1}##1{{\accent#2 ##1}}} + {\setvalue{\accentprefix\characterencoding\string#1}##1{{#2##1}}}% + \unprotectfontcharacters} + +%D We don't have to define them for the default \PLAIN\ case. +%D Commands may be used instead of character codes. + +%D \macros +%D {redefinecommand} +%D +%D Redefinition of encoding dependant commands like \type{\b} +%D and \type{\c} can be triggered by: +%D +%D \starttyping +%D \redefinecommand b % something math +%D \redefinecommand c % something math +%D \stoptyping +%D +%D Handling of characters is easier than handling accents +%D because here we don't have to take care of arguments. We +%D just call for the right glyph in the right place. +%D +%D The \type{\next} construction permits handling of commands +%D that take arguments. This means that we can use this +%D command to redefine accent handling commands too +%D (although today the next is not needed any longer in test +%D macros). + +\def\redefinecommand#1 % + {% no \unexpanded, else pdfdoc fails + \setvalue{\string#1}{\dohandlecommand{#1}}}% + +\def\dohandlecommand#1% + {\csname\commandprefix + \ifcsname\commandprefix\characterencoding#1\endcsname + \characterencoding + \else + \nocharacterencoding + \fi + #1\endcsname} + +%D \macros +%D {currentencoding, currentmapping} +%D +%D When we show 'm, we don't want to see the protection +%D measures. + +\def\currentencoding{\@EA\dopureencodingname\characterencoding} +\def\currentmapping {\@EA\dopureencodingname\charactermapping } + +\def\dopureencodingname @#1@{#1} + +\def\pureencodingname#1{\@EA\dopureencodingname#1} + +%D \macros +%D {showaccents, showcharacters, +%D showcharacterbounds, showhyphenations} +%D +%D Encoding is a tricky business. Therefore we provide a +%D a few macros that show most of the characters involved. The +%D next two tables show the result of \type {\showaccents}. +%D +%D \placetable +%D {The special glyphs in default encoding.} +%D {\showaccents} +%D +%D \placetable +%D {The special glyphs in texnansi encoding.} +%D {\switchtobodyfont[lbr]\showaccents} +%D +%D The command +%D +%D \starttyping +%D \showhyphenations{doordefini\"eren} +%D \stoptyping +%D +%D can be used to check the correct loading of hyphenation +%D patterns. + +\fetchruntimecommand \showaccents {\f!encodingprefix\s!run} +\fetchruntimecommand \showcharacters {\f!encodingprefix\s!run} +\fetchruntimecommand \showcharacterbounds {\f!encodingprefix\s!run} +\fetchruntimecommand \showhyphenations {\f!encodingprefix\s!run} +\fetchruntimecommand \showmapping {\f!encodingprefix\s!run} + +%D \macros +%D {everyuppercase, EveryUppercase, +%D everyuppercase, EveryUppercase} +%D +%D When we want to uppercase strings of characters, we have to +%D take care of those characters that have a special meaning or +%D are only accessible by means of macros. The next hack was +%D introduced when Tobias Burnus started translating head and +%D label texts into spanish and italian. The first application +%D of this token register therefore can be found in the module +%D that deals with these texts. + +\newevery \everyuppercase \EveryUppercase +\newevery \everylowercase \EveryLowercase + +%D This magic trick maps takes care of mapping from lower to +%D upper case and reverse. + +\def\reloadmapping{\the\executeifdefined{\@cas@\charactermapping}\emptytoks} + +\appendtoks\let\setuppercasecom\setcasecom\to\everyuppercase +\appendtoks\let\setlowercasecom\setcasecom\to\everylowercase + +\appendtoks\reloadmapping\to\everyuppercase % slow, will be sped up +\appendtoks\reloadmapping\to\everylowercase % slow, will be sped up + +\newtoks\everyULmap + +\appendtoks\let\remapcase\remapuppercase\the\everyULmap\to\everyuppercase +\appendtoks\let\remapcase\remaplowercase\the\everyULmap\to\everylowercase + +\let\remapcase\gobbletwoarguments + +\def\remapuppercase#1#2{\let#2#1} % more efficient: +\def\remaplowercase#1#2{\let#1#2} \let\remaplowercase\let + +\def\defineLCcharacter #1 #2 % + {\appendtoks\let\to\everylowercase + \@EA\appendtoks\csname#1\endcsname\to\everylowercase + \@EA\appendtoks\csname#2\endcsname\to\everylowercase} + +\def\defineUCcharacter #1 #2 % + {\appendtoks\let\to\everyuppercase + \@EA\appendtoks\csname#1\endcsname\to\everyuppercase + \@EA\appendtoks\csname#2\endcsname\to\everyuppercase} + +\def\defineULcharacter #1 #2 % + {\appendtoks\remapcase\to\everyULmap + \@EA\appendtoks\csname#1\endcsname\to\everyULmap + \@EA\appendtoks\csname#2\endcsname\to\everyULmap} + +% slightly faster with \smallcapped's but far more hash and stringspace +% +% \newif\ifuppercase \appendtoks\uppercasetrue\to\everyuppercase +% \newif\iflowercase \appendtoks\lowercasetrue\to\everylowercase +% +% \def\defineULcharacter #1 #2 % +% {\def\!!stringa{@#1}\@EA\letvalue\@EA\!!stringa\csname#1\endcsname +% \def\!!stringa{@#2}\@EA\letvalue\@EA\!!stringa\csname#2\endcsname +% \setvalue{#1}{\getvalue{@\ifuppercase#2\else#1\fi}}% +% \setvalue{#2}{\getvalue{@\iflowercase#1\else#2\fi}}} + +% 2 = tricky, since expanding \definedfont[lowcasename] ... goes wrong + +\chardef\uppercasemode\plusthree % 0=ignore 1=normal 2=expand 3=auto +\chardef\casecommode \plusone % 0=noexpand 1=expand + +\def\setcasecom #1#2{\def#1{\ifcase\casecommode\noexpand#1\else#2\fi}} + +% \def\OEPS{whatever} +% +% \startmapping[ec] +% \defineuppercasecom \oeps {\getvalue{OEPS}} +% \stopmapping +% +% \WORD{xx \oeps} + +\def\douppercase#1% + {\bgroup + \let\douppercase\firstofoneargument + \the\everyuppercase % currently also checks uppercasemode + \let\dochar\rawcharacter + \ifcase\uppercasemode + #1% + \or % No expansion here, otherwise \getvalue problems! Default!!! + %\edef\next{#1}% keep this to prevent roll back + %\uppercase\expandafter{\next}% keep this to prevent roll back + \uppercase{#1}% + \or + \chardef\casecommode\zerocount + \let\docasecom\firstoftwoarguments + \edef\ascii{#1}% + \edef\ascii{\expandafter\uppercase\expandafter{\ascii}}% needed when in regime + \chardef\casecommode\plusone + \ascii + \else + % mode three may trigger setting 2 elsewhere (e.g. regime test) + \uppercase{#1}% + \fi + \egroup} + +\prependtoksonce + \doifnot\currentregime\s!default + {\ifnum\uppercasemode=\plusthree \chardef\uppercasemode\plustwo \fi}% +\to \everyuppercase + +%D \macros +%D {everysanitize, EverySanitize} +%D +%D Whenever we are sanitizing strings, like we sometimes do +%D when we deal with specials, the next token register can be +%D called. + +\newevery \everysanitize \EverySanitize + +%D \macros +%D {defineuclass,defineudigit,udigit} +%D +%D The next few macros are experimental and needed for unicoded +%D chinese characters. + +\def\defineuclass #1 #2 #3 % + {\setvalue{uc\the\numexpr#2*256+#3\relax}{#1}} + +\def\defineudigit #1 #2 #3 {\setvalue{\characterencoding uc#1}{\uchar{#2}{#3}}} + +%D It may look strange, but for the moment, we want the encoding +%D to be part of the digit specification. This may change! + +\unexpanded\def\udigit#1#2{\getvalue{@#1@uc\number#2}} + +%D \macros +%D {uchar, octuchar, hexuchar} + +\ifx\uchar\undefined \def\uchar#1#2{(\number#1,\number#2)} \fi + +\def\octuchar#1#2{\uchar{`#1}{`#2}} +\def\hexuchar#1#2{\uchar{"#1}{"#2}} + +%D Basics and fallbacks. + +\newif\ifignoreaccent + +\let\textaccent \accent +\let\normaltextaccent\textaccent + +% ** we will explicitly embrace the two arguments, since in definitions +% this may not be the case, and we don't want faulty expansions like +% "\dobuildtextaccent \char 18 a" but "\dobuildtextaccent {\char 18}{a}" +% instead + +\def\buildmathaccent#1% + {\mathaccent#1 } + +\def\buildtextaccent#1#2% ** + {\ifignoreaccent + \expandafter\nobuildtextaccent + \else + \expandafter\dobuildtextaccent + \fi{#1}{#2}} + +\unexpanded\def\nobuildtextaccent#1#2% + {#2} + +\unexpanded\def\dobuildtextaccent#1#2% + {{\let\char\normalaccent#1\let\char\normalchar#2}} + +% some fake ones, name will change into build + +\unexpanded\def\bottomaccent#1#2#3#4#5% down right slantcorrection accent char + {\dontleavehmode % why this align mess + \vtop + {\forgetall + \baselineskip\zeropoint + \lineskip#1% + \everycr\emptytoks + \tabskip\zeropoint + \lineskiplimit\zeropoint + \setbox0\hbox{#4}% + \halign + {##\crcr\hbox{#5}\crcr + \hidewidth + \hskip#2\wd0 + \hskip-#3\slantperpoint % in plain 1ex * dimenless value + \vbox to .2ex{\box0\vss}\hidewidth + \crcr}}} + +\def\buildtextmacron {\bottomaccent{.25ex}{0}{15}{\textmacron}} +\def\buildtextbottomdot{\bottomaccent{.25ex}{0}{5}{\textbottomdot}} +\def\buildtextcedilla {\bottomaccent{0ex}{0}{5}{\textcedilla}} +\def\buildtextogonek {\bottomaccent{-.1ex}{.5}{0}{\textogonek}} + +%D A collectors item: + +\def\buildtextbottomcomma{\bottomaccent{.15ex}{0}{5}{\tx,}} + +%D Rarely needed but there: + +\unexpanded\def\topaccent#1#2#3#4#5% down right slantcorrection accent char + {\dontleavehmode + \bgroup + \setbox0\hbox{#4}% + \setbox2\hbox{#5}% + \hbox to \wd2 \bgroup + \hss\copy2\hss + \hskip-\wd2 + \hss\hskip#2\wd0\hskip-#3\slantperpoint\raise#1\hbox{#4}\hss + \egroup + \egroup} + +\def\buildtextgrave{\topaccent{0pt}{0}{15}{\textgrave}} % e.g. + +% \definecharacter schwa {\hbox{\rotate[rotation=180,location=high]{\hbox{e}}}} +% \definecharacter schwagrave {\buildtextgrave\schwa} + +% math stuff, will change + +\def\definemathaccent#1 #2% + {\setvalue{\string#1}{#2}% + \setvalue{normalmathaccent\string#1}{#2}} + +\def\donormalmathaccent#1% + {\getvalue{normalmathaccent\string#1}} + +%D Some precautions: + +\ifx\usepdffontresource\undefined + \def\usepdffontresource #1 {} % this will be defined elsewhere +\fi + +\def\donthandleaccent #1{\expandafter\string\csname#1\endcsname\space} +\def\donthandlecommand #1{\expandafter\string\csname#1\endcsname\space} +\def\donthandlecharacter #1{\expandafter\string\csname#1\endcsname\space} + +\def\stringifyhandleaccent #1{\strchr{#1}} +\def\stringifyhandlecommand #1{\strchr{#1}} +\def\stringifyhandlecharacter#1{\strchr{#1}} + +\def\keephandleaccent #1{\expandafter\noexpand\csname#1\endcsname} +\def\keephandlecommand #1{\expandafter\noexpand\csname#1\endcsname} +\def\keephandlecharacter #1{\expandafter\noexpand\csname#1\endcsname} + +\def\handleaccent #1{\csname#1\endcsname} +\def\handlecommand #1{\csname#1\endcsname} +\def\handlecharacter #1{\csname#1\endcsname} + +\def\dontexpandencoding + {\let\dohandleaccent \donthandleaccent + \let\dohandlecommand \donthandlecommand + \let\dohandlecharacter\donthandlecharacter} + +\def\keepencodedtokens + {\let\dohandleaccent \keephandleaccent + \let\dohandlecommand \keephandlecommand + \let\dohandlecharacter\keephandlecharacter} + +\def\literateencodedtokens + {% \let\dohandleaccent \keephandleaccent + % \let\dohandlecommand \keephandlecommand + \let\dohandlecharacter\keephandlecharacter} + +\def\stringifyencodedtokens + {% \let\dohandleaccent \stringifyhandleaccent + % \let\dohandlecommand \stringifyhandlecommand + \let\dohandlecharacter\stringifyhandlecharacter} + +\unexpanded\def\uhandleaccent #1{\csname#1\endcsname} +\unexpanded\def\uhandlecommand #1{\csname#1\endcsname} +\unexpanded\def\uhandlecharacter#1{\csname#1\endcsname} + +\def\dontexpandencodedtokens + {\def\dohandleaccent {\uhandleaccent}% + \def\dohandlecommand {\uhandlecommand}% + \def\dohandlecharacter{\uhandlecharacter}} + +% no longer: \def\convertencodedtokens{\dontexpandencoding} but: + +\def\convertencodedtokens{\stringifyencodedtokens} + +% test case: +% +% \enableregime[cp1250] +% \mainlanguage[cz] +% +% \starttext +% +% \title{Ϭuޯu餭 kon졺p +% \placelist[chapter][criterium=all] +% +% \startbuffer +%So far we haven't really dealt with features (or whatever we want +to pass along with the font definition. We distinguish the following +situations:
+situations: + +
+name:xetex like specs
+name@virtual font spec
+name*context specification
+
+--ldx]]--
+
+function specify.predefined(specification)
+ local detail = specification.detail
+ if detail ~= "" then
+ -- detail = detail:gsub("["..define.splitsymbols.."].*$","") -- get rid of *whatever specs and such
+ if define.methods[detail] then -- since these may be appended at the
+ specification.features.vtf = { preset = detail } -- tex end by default
+ end
+ end
+ return specification
+end
+
+define.register_split("@", specify.predefined)
+
+storage.register("fonts/setups" , define.specify.context_setups , "fonts.define.specify.context_setups" )
+storage.register("fonts/numbers", define.specify.context_numbers, "fonts.define.specify.context_numbers")
+storage.register("fonts/merged", define.specify.context_merged, "fonts.define.specify.context_merged")
+storage.register("fonts/synonyms", define.specify.synonyms, "fonts.define.specify.synonyms")
+
+local normalize_meanings = fonts.otf.meanings.normalize
+local settings_to_hash = aux.settings_to_hash
+local default_features = fonts.otf.features.default
+
+local function preset_context(name,parent,features) -- currently otf only
+ if features == "" then
+ if find(parent,"=") then
+ features = parent
+ parent = ""
+ end
+ end
+ local number = (setups[name] and setups[name].number) or 0
+ local t = (features == "" and { }) or normalize_meanings(settings_to_hash(features))
+ -- todo: synonyms, and not otf bound
+ if parent ~= "" then
+ for p in gmatch(parent,"[^, ]+") do
+ local s = setups[p]
+ if s then
+ for k,v in next, s do
+ if t[k] == nil then
+ t[k] = v
+ end
+ end
+ end
+ end
+ end
+ -- these are auto set so in order to prevent redundant definitions
+ -- we need to preset them (we hash the features and adding a default
+ -- setting during initialization may result in a different hash)
+ for k,v in next, triggers do
+ if type(t[v]) == "nil" then
+ local vv = default_features[v]
+ if vv then t[v] = vv end
+ end
+ end
+ -- sparse 'm so that we get a better hash and less test (experimental
+ -- optimization)
+ local tt = { } -- maybe avoid tt
+ for k,v in next, t do
+ if v then tt[k] = v end
+ end
+ -- needed for dynamic features
+ if number == 0 then
+ number = #numbers + 1
+ numbers[number] = name
+ end
+ tt.number = number
+ setups[name] = tt
+ return number
+end
+
+local function context_number(name) -- will be replaced
+ local t = setups[name]
+ if not t then
+ return 0
+ elseif t.auto then
+ local lng = tonumber(tex.language)
+ local tag = name .. ":" .. lng
+ local s = setups[tag]
+ if s then
+ return s.number or 0
+ else
+ local script, language = languages.association(lng)
+ if t.script ~= script or t.language ~= language then
+ local s = table.fastcopy(t)
+ local n = #numbers + 1
+ setups[tag] = s
+ numbers[n] = tag
+ s.number = n
+ s.script = script
+ s.language = language
+ return n
+ else
+ setups[tag] = t
+ return t.number or 0
+ end
+ end
+ else
+ return t.number or 0
+ end
+end
+
+local function merge_context(currentnumber,extraname,option)
+ local current = setups[numbers[currentnumber]]
+ local extra = setups[extraname]
+ if extra then
+ local mergedfeatures, mergedname = { }, nil
+ if option < 0 then
+ if current then
+ for k, v in next, current do
+ if not extra[k] then
+ mergedfeatures[k] = v
+ end
+ end
+ end
+ mergedname = currentnumber .. "-" .. extraname
+ else
+ if current then
+ for k, v in next, current do
+ mergedfeatures[k] = v
+ end
+ end
+ for k, v in next, extra do
+ mergedfeatures[k] = v
+ end
+ mergedname = currentnumber .. "+" .. extraname
+ end
+ local number = #numbers + 1
+ mergedfeatures.number = number
+ numbers[number] = mergedname
+ merged[number] = option
+ setups[mergedname] = mergedfeatures
+ return number -- context_number(mergedname)
+ else
+ return currentnumber
+ end
+end
+
+local function register_context(fontnumber,extraname,option)
+ local extra = setups[extraname]
+ if extra then
+ local mergedfeatures, mergedname = { }, nil
+ if option < 0 then
+ mergedname = fontnumber .. "-" .. extraname
+ else
+ mergedname = fontnumber .. "+" .. extraname
+ end
+ for k, v in next, extra do
+ mergedfeatures[k] = v
+ end
+ local number = #numbers + 1
+ mergedfeatures.number = number
+ numbers[number] = mergedname
+ merged[number] = option
+ setups[mergedname] = mergedfeatures
+ return number -- context_number(mergedname)
+ else
+ return 0
+ end
+end
+
+specify.preset_context = preset_context
+specify.context_number = context_number
+specify.merge_context = merge_context
+specify.register_context = register_context
+
+local current_font = font.current
+local tex_attribute = tex.attribute
+
+local cache = { } -- concat might be less efficient than nested tables
+
+function fonts.withset(name,what)
+ local zero = tex_attribute[0]
+ local hash = zero .. "+" .. name .. "*" .. what
+ local done = cache[hash]
+ if not done then
+ done = merge_context(zero,name,what)
+ cache[hash] = done
+ end
+ tex_attribute[0] = done
+end
+function fonts.withfnt(name,what)
+ local font = current_font()
+ local hash = font .. "*" .. name .. "*" .. what
+ local done = cache[hash]
+ if not done then
+ done = register_context(font,name,what)
+ cache[hash] = done
+ end
+ tex_attribute[0] = done
+end
+
+function specify.show_context(name)
+ return setups[name] or setups[numbers[name]] or setups[numbers[tonumber(name)]] or { }
+end
+
+local function split_context(features)
+ return setups[features] or (preset_context(features,"","") and setups[features])
+end
+
+specify.split_context = split_context
+
+function specify.context_tostring(name,kind,separator,yes,no,strict,omit) -- not used
+ return aux.hash_to_string(table.merged(fonts[kind].features.default or {},setups[name] or {}),separator,yes,no,strict,omit)
+end
+
+local splitter = lpeg.splitat(",")
+
+function specify.starred(features) -- no longer fallbacks here
+ local detail = features.detail
+ if detail and detail ~= "" then
+ features.features.normal = split_context(detail)
+ else
+ features.features.normal = { }
+ end
+ return features
+end
+
+define.register_split('*',specify.starred)
+
+-- define (two steps)
+
+local P, C, Cc = lpeg.P, lpeg.C, lpeg.Cc
+
+local space = P(" ")
+local spaces = space^0
+local value = C((1-space)^1)
+local rest = C(P(1)^0)
+local scale_none = Cc(0)
+local scale_at = P("at") * Cc(1) * spaces * value
+local scale_sa = P("sa") * Cc(2) * spaces * value
+local scale_mo = P("mo") * Cc(3) * spaces * value
+local scale_scaled = P("scaled") * Cc(4) * spaces * value
+
+local sizepattern = spaces * (scale_at + scale_sa + scale_mo + scale_scaled + scale_none)
+local splitpattern = spaces * value * spaces * rest
+
+local specification --
+
+local get_specification = define.get_specification
+
+function define.command_1(str)
+ statistics.starttiming(fonts)
+ local fullname, size = splitpattern:match(str)
+ local lookup, name, sub, method, detail = get_specification(fullname)
+ if not name then
+ logs.report("define font","strange definition '%s'",str)
+ texsprint(ctxcatcodes,"\\glet\\somefontname\\defaultfontfile")
+ elseif name == "unknown" then
+ texsprint(ctxcatcodes,"\\glet\\somefontname\\defaultfontfile")
+ else
+ texsprint(ctxcatcodes,format("\\xdef\\somefontname{%s}",name))
+ end
+ -- we can also use a count for the size
+ if size and size ~= "" then
+ local mode, size = sizepattern:match(size)
+ if size and mode then
+ count.scaledfontmode = mode
+ texsprint(ctxcatcodes,format("\\def\\somefontsize{%s}",size))
+ else
+ count.scaledfontmode = 0
+ texsprint(ctxcatcodes,format("\\let\\somefontsize\\empty",size))
+ end
+ elseif true then
+ -- so we don't need to check in tex
+ count.scaledfontmode = 2
+--~ texsprint(ctxcatcodes,format("\\def\\somefontsize{*}",size))
+ texsprint(ctxcatcodes,format("\\let\\somefontsize\\empty",size))
+ else
+ count.scaledfontmode = 0
+ texsprint(ctxcatcodes,format("\\let\\somefontsize\\empty",size))
+ end
+ specification = define.makespecification(str,lookup,name,sub,method,detail,size)
+end
+
+function define.command_2(global,cs,str,size,classfeatures,fontfeatures,classfallbacks,fontfallbacks,mathsize,textsize)
+ -- name is now resolved and size is scaled cf sa/mo
+ local lookup, name, sub, method, detail = get_specification(str or "")
+ -- asome settings can be overloaded
+ if lookup and lookup ~= "" then specification.lookup = lookup end
+ specification.name = name
+ specification.size = size
+ specification.sub = sub
+ specification.mathsize = mathsize
+ specification.textsize = textsize
+ if detail and detail ~= "" then
+ specification.method, specification.detail = method or "*", detail
+ elseif specification.detail and specification.detail ~= "" then
+ -- already set
+ elseif fontfeatures and fontfeatures ~= "" then
+ specification.method, specification.detail = "*", fontfeatures
+ elseif classfeatures and classfeatures ~= "" then
+ specification.method, specification.detail = "*", classfeatures
+ end
+ if trace_defining then
+ logs.report("define font","memory usage before: %s",statistics.memused())
+ end
+ if fontfallbacks and fontfallbacks ~= "" then
+ specification.fallbacks = fontfallbacks
+ elseif classfallbacks and classfallbacks ~= "" then
+ specification.fallbacks = classfallbacks
+ end
+ local tfmdata = define.read(specification,size) -- id not yet known
+ if not tfmdata then
+ logs.report("define font","unable to define %s as \\%s",name,cs)
+ elseif type(tfmdata) == "number" then
+ if trace_defining then
+ logs.report("define font","reusing %s with id %s as \\%s (features: %s/%s, fallbacks: %s/%s)",name,tfmdata,cs,classfeatures,fontfeatures,classfallbacks,fontfallbacks)
+ end
+ tex.definefont(global,cs,tfmdata)
+ -- resolved (when designsize is used):
+ texsprint(ctxcatcodes,format("\\def\\somefontsize{%isp}",fontdata[tfmdata].size))
+ else
+ -- local t = os.clock(t)
+ local id = font.define(tfmdata)
+ -- print(name,os.clock()-t)
+ tfmdata.id = id
+ define.register(tfmdata,id)
+ tex.definefont(global,cs,id)
+ tfm.cleanup_table(tfmdata)
+ if trace_defining then
+ logs.report("define font","defining %s with id %s as \\%s (features: %s/%s, fallbacks: %s/%s)",name,id,cs,classfeatures,fontfeatures,classfallbacks,fontfallbacks)
+ end
+ -- resolved (when designsize is used):
+ texsprint(ctxcatcodes,format("\\def\\somefontsize{%isp}",tfmdata.size))
+ --~ if specification.fallbacks then
+ --~ fonts.collections.prepare(specification.fallbacks)
+ --~ end
+ end
+ if trace_defining then
+ logs.report("define font","memory usage after: %s",statistics.memused())
+ end
+ statistics.stoptiming(fonts)
+end
+
+--~ table.insert(readers.sequence,1,'vtf')
+
+--~ function readers.vtf(specification)
+--~ if specification.features.vtf and specification.features.vtf.preset then
+--~ return tfm.make(specification)
+--~ else
+--~ return nil
+--~ end
+--~ end
diff --git a/tex/context/base/font-def.lua b/tex/context/base/font-def.lua
index 474cde41d..8c367e148 100644
--- a/tex/context/base/font-def.lua
+++ b/tex/context/base/font-def.lua
@@ -6,9 +6,13 @@ if not modules then modules = { } end modules ['font-def'] = {
license = "see context related readme files"
}
--- check reuse of lmroman1o-regular vs lmr10
+local format, concat, gmatch, match, find, lower = string.format, table.concat, string.gmatch, string.match, string.find, string.lower
+local tostring, next = tostring, next
-local texsprint, count, dimen, format, concat = tex.sprint, tex.count, tex.dimen, string.format, table.concat
+local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
+
+trackers.register("fonts.loading", "fonts.defining", "otf.loading", "afm.loading", "tfm.loading")
+trackers.register("fonts.all", "fonts.*", "otf.*", "afm.*", "tfm.*")
--[[ldx--
Here we deal with defining fonts. We do so by intercepting the
@@ -18,43 +22,30 @@ default loader that only handles
Choosing a font by name and specififying its size is only part of the
-game. In order to prevent complex commands,
For the sake of users who have defined fonts using that syntax, we -will support it, but we will provide additional methods as well. -Normally users will not use this direct way, but use a more abstract -interface.
- --ldx]]-- - ---~ name, kind, features = fonts.features.split_xetex("blabla / B : + lnum ; foo = bar ; - whatever ; whow ; + hans ; test = yes") - -fonts.define.method = 3 -- 1: tfm 2: tfm and if not then afm 3: afm and if not then tfm -fonts.define.auto_afm = true -fonts.define.auto_otf = true -fonts.define.specify = fonts.define.specify or { } -fonts.define.methods = fonts.define.methods or { } +define.method = "afm or tfm" -- afm, tfm, afm or tfm, tfm or afm +define.specify = fonts.define.specify or { } +define.methods = fonts.define.methods or { } tfm.fonts = tfm.fonts or { } tfm.readers = tfm.readers or { } tfm.internalized = tfm.internalized or { } -- internal tex numbers -tfm.id = tfm.id or { } -- font data, maybe use just fonts.ids (faster lookup) tfm.readers.sequence = { 'otf', 'ttf', 'afm', 'tfm' } +local readers = tfm.readers +local sequence = readers.sequence + --[[ldx--We hardly gain anything when we cache the final (pre scaled)
A unique hash value is generated by:
--ldx]]-- +local sortedhashkeys = table.sortedhashkeys + function tfm.hash_features(specification) local features = specification.features if features then local t = { } local normal = features.normal if normal and next(normal) then - local f = table.sortedhashkeys(normal) + local f = sortedhashkeys(normal) for i=1,#f do local v = f[i] - if v ~= "number" then + if v ~= "number" and v ~= "features" then -- i need to figure this out, features t[#t+1] = v .. '=' .. tostring(normal[v]) end end end local vtf = features.vtf if vtf and next(vtf) then - local f = table.sortedhashkeys(vtf) + local f = sortedhashkeys(vtf) for i=1,#f do local v = f[i] t[#t+1] = v .. '=' .. tostring(vtf[v]) end end +--~ if specification.mathsize then +--~ t[#t] = "mathsize=" .. specification.mathsize +--~ end if #t > 0 then return concat(t,"+") end @@ -189,18 +191,28 @@ function tfm.hash_instance(specification,force) size = math.round(tfm.scaled(size, fonts.designsizes[hash])) specification.size = size end - if fallbacks then - return hash .. ' @ ' .. tostring(size) .. ' @ ' .. fallbacks - else - return hash .. ' @ ' .. tostring(size) - end +--~ local mathsize = specification.mathsize or 0 +--~ if mathsize > 0 then +--~ local textsize = specification.textsize +--~ if fallbacks then +--~ return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ] @ ' .. fallbacks +--~ else +--~ return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ]' +--~ end +--~ else + if fallbacks then + return hash .. ' @ ' .. tostring(size) .. ' @ ' .. fallbacks + else + return hash .. ' @ ' .. tostring(size) + end +--~ end end --[[ldx--We can resolve the filename using the next function:
--ldx]]-- -function fonts.define.resolve(specification) +function define.resolve(specification) if not specification.resolved or specification.resolved == "" then -- resolved itself not per se in mapping hash if specification.lookup == 'name' then specification.resolved, specification.sub = fonts.names.resolve(specification.name,specification.sub) @@ -218,7 +230,8 @@ function fonts.define.resolve(specification) else specification.forced = specification.forced end - specification.hash = specification.name .. ' @ ' .. tfm.hash_features(specification) +--~ specification.hash = specification.name .. ' @ ' .. tfm.hash_features(specification) + specification.hash = lower(specification.name .. ' @ ' .. tfm.hash_features(specification)) if specification.sub and specification.sub ~= "" then specification.hash = specification.sub .. ' @ ' .. specification.hash end @@ -242,22 +255,23 @@ specification yet. --ldx]]-- function tfm.read(specification) ---~ input.starttiming(fonts) local hash = tfm.hash_instance(specification) local tfmtable = tfm.fonts[hash] -- hashes by size ! if not tfmtable then - if specification.forced and specification.forced ~= "" then - tfmtable = tfm.readers[specification.forced:lower()](specification) + local forced = specification.forced or "" + if forced ~= "" then + tfmtable = readers[lower(forced)](specification) if not tfmtable then - logs.report("define font","forced type %s of %s not found",specification.forced,specification.name) + logs.report("define font","forced type %s of %s not found",forced,specification.name) end else - for _, reader in ipairs(tfm.readers.sequence) do - if tfm.readers[reader] then -- not really needed - if fonts.trace then + for s=1,#sequence do -- reader sequence + local reader = sequence[s] + if readers[reader] then -- not really needed + if trace_defining then logs.report("define font","trying type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") end - tfmtable = tfm.readers[reader](specification) + tfmtable = readers[reader](specification) if tfmtable then break end end end @@ -273,7 +287,6 @@ function tfm.read(specification) --~ tfmtable.mode = specification.features.normal.mode or "base" end end ---~ input.stoptiming(fonts) if not tfmtable then logs.report("define font","font with name %s is not found",specification.name) end @@ -285,26 +298,26 @@ end --ldx]]-- function tfm.read_and_define(name,size) -- no id - local specification = fonts.define.analyze(name,size) + local specification = define.analyze(name,size) local method = specification.method - if method and fonts.define.specify[method] then - specification = fonts.define.specify[method](specification) + if method and define.specify[method] then + specification = define.specify[method](specification) end - specification = fonts.define.resolve(specification) + specification = define.resolve(specification) local hash = tfm.hash_instance(specification) - local id = fonts.define.registered(hash) + local id = define.registered(hash) if not id then local fontdata = tfm.read(specification) if fontdata then fontdata.hash = hash id = font.define(fontdata) - fonts.define.register(fontdata,id) -tfm.cleanup_table(fontdata) + define.register(fontdata,id) + tfm.cleanup_table(fontdata) else id = 0 -- signal end end - return tfm.id[id], id + return fonts.ids[id], id end --[[ldx-- @@ -312,275 +325,113 @@ end evolved. Each one has its own way of dealing with its format. --ldx]]-- -function tfm.readers.opentype(specification,suffix,what) - if fonts.define.auto_otf then - local fullname, tfmtable = nil, nil - fullname = input.findbinfile(specification.name,suffix) or "" - if fullname == "" then - local fb = fonts.names.old_to_new[specification.name] - if fb then - fullname = input.findbinfile(fb,suffix) or "" - end - end - if fullname == "" then - local fb = fonts.names.new_to_old[specification.name] - if fb then - fullname = input.findbinfile(fb,suffix) or "" - end - end - if fullname ~= "" then - specification.filename, specification.format = fullname, what -- hm, so we do set the filename, then - tfmtable = tfm.read_from_open_type(specification) -- we need to do it for all matches / todo - end - return tfmtable - else - return nil +local function check_tfm(specification,fullname) + -- ofm directive blocks local path search unless set + fullname = resolvers.findbinfile(fullname, 'tfm') or "" -- just to be sure + if fullname ~= "" then + specification.filename, specification.format = fullname, "ofm" + return tfm.read_from_tfm(specification) end end -function tfm.readers.otf(specification) return tfm.readers.opentype(specification,"otf","opentype") end -function tfm.readers.ttf(specification) return tfm.readers.opentype(specification,"ttf","truetype") end -function tfm.readers.ttc(specification) return tfm.readers.opentype(specification,"ttf","truetype") end -- !! - -function tfm.readers.afm(specification,method) - local fullname, tfmtable = nil, nil - method = method or fonts.define.method - if method == 2 then - fullname = input.findbinfile(specification.name,"ofm") or "" - if fullname == "" then - tfmtable = tfm.read_from_afm(specification) - else -- redundant - specification.filename = fullname - tfmtable = tfm.read_from_tfm(specification) - end - elseif method == 3 then -- maybe also findbinfile here - if fonts.define.auto_afm then - tfmtable = tfm.read_from_afm(specification) - end - elseif method == 4 then -- maybe also findbinfile here - tfmtable = tfm.read_from_afm(specification) +local function check_afm(specification,fullname) + fullname = resolvers.findbinfile(fullname, 'afm') or "" -- just to be sure + if fullname ~= "" then + specification.filename, specification.format = fullname, "afm" + return tfm.read_from_afm(specification) end - return tfmtable end -function tfm.readers.tfm(specification) - local fullname, tfmtable = nil, nil - tfmtable = tfm.read_from_tfm(specification) - return tfmtable -end - ---[[ldx-- -So far we haven't really dealt with features (or whatever we want -to pass along with the font definition. We distinguish the following -situations:
-situations: - -
-name:xetex like specs
-name@virtual font spec
-name*context specification
-
-
-Of course one can always define more.
---ldx]]-- - -function fonts.define.specify.predefined(specification) - local detail = specification.detail - if detail ~= "" then - -- detail = detail:gsub("["..fonts.define.splitsymbols.."].*$","") -- get rid of *whatever specs and such - if fonts.define.methods[detail] then -- since these may be appended at the - specification.features.vtf = { preset = detail } -- tex end by default +function readers.tfm(specification) + local fullname, tfmtable = specification.filename or "", nil + if fullname == "" then + local forced = specification.forced or "" + if forced ~= "" then + tfmtable = check_tfm(specification,specification.name .. "." .. forced) end - end - return specification -end - -fonts.define.register_split("@", fonts.define.specify.predefined) - -function fonts.define.specify.colonized(specification) -- xetex mode - local list = { } - if specification.detail and specification.detail ~= "" then - local expanded_features = { } - local function expand(features) - for _,v in pairs(features:split(";")) do --just gmatch - expanded_features[#expanded_features+1] = v - end - end - expand(specification.detail) - for _,v in pairs(expanded_features) do - local a, b = v:match("^%s*(%S+)%s*=%s*(%S+)%s*$") - if a and b then - list[a] = b:is_boolean() - if type(list[a]) == "nil" then - list[a] = b - end - else - local a, b = v:match("^%s*([%+%-]?)%s*(%S+)%s*$") - if a and b then - list[b] = a ~= "-" - end - end + if not tfmtable then + tfmtable = check_tfm(specification,specification.name) end - end - specification.features.normal = list - return specification -end - -function tfm.make(specification) - -- currently fonts are scaled while constructing the font, so we - -- have to do scaling of commands in the vf at that point using - -- e.g. "local scale = g.factor or 1" after all, we need to work - -- with copies anyway and scaling needs to be done at some point; - -- however, when virtual tricks are used as feature (makes more - -- sense) we scale the commands in fonts.tfm.scale (and set the - -- factor there) - local fvm = fonts.define.methods[specification.features.vtf.preset] - if fvm then - return fvm(specification) else - return nil + tfmtable = check_tfm(specification,fullname) end + return tfmtable end -fonts.define.register_split(":", fonts.define.specify.colonized) - -fonts.define.specify.context_setups = fonts.define.specify.context_setups or { } -fonts.define.specify.context_numbers = fonts.define.specify.context_numbers or { } -fonts.define.specify.synonyms = fonts.define.specify.synonyms or { } - -input.storage.register(false,"fonts/setups" , fonts.define.specify.context_setups , "fonts.define.specify.context_setups" ) -input.storage.register(false,"fonts/numbers", fonts.define.specify.context_numbers, "fonts.define.specify.context_numbers") - -fonts.triggers = fonts.triggers or { } - -function fonts.define.specify.preset_context(name,parent,features) - if features == "" then - if parent:find("=") then - features = parent - parent = "" +function readers.afm(specification,method) + local fullname, tfmtable = specification.filename or "", nil + if fullname == "" then + local forced = specification.forced or "" + if forced ~= "" then + tfmtable = check_afm(specification,specification.name .. "." .. forced) end - end - local fds = fonts.define.specify - local setups, numbers, synonyms = fds.context_setups, fds.context_numbers, fds.synonyms - local number = (setups[name] and setups[name].number) or 0 - local t = (features == "" and { }) or fonts.otf.meanings.normalize(aux.settings_to_hash(features)) - -- todo: synonyms, and not otf bound - if parent ~= "" then - for p in parent:gmatch("[^, ]+") do - local s = setups[p] - if s then - for k,v in pairs(s) do - if t[k] == nil then - t[k] = v - end - end + if not tfmtable then + method = method or define.method or "afm or tfm" + if method == "tfm" then + tfmtable = check_tfm(specification,specification.name) + elseif method == "afm" then + tfmtable = check_afm(specification,specification.name) + elseif method == "tfm or afm" then + tfmtable = check_tfm(specification,specification.name) or check_afm(specification,specification.name) + else -- method == "afm or tfm" or method == "" then + tfmtable = check_afm(specification,specification.name) or check_tfm(specification,specification.name) end end - end - -- these are auto set so in order to prevent redundant definitions - -- we need to preset them (we hash the features and adding a default - -- setting during initialization may result in a different hash) - local default = fonts.otf.features.default - for k,v in pairs(fonts.triggers) do - if type(t[v]) == "nil" then - local vv = default[v] - if vv then t[v] = vv end - end - end - -- sparse 'm so that we get a better hash and less test (experimental - -- optimization) - local tt = { } - for k,v in pairs(t) do - if v then tt[k] = v end - end - -- needed for dynamic features - if number == 0 then - numbers[#numbers+1] = name - tt.number = #numbers else - tt.number = number + tfmtable = check_afm(specification,fullname) end - setups[name] = tt + return tfmtable end -do - - -- here we clone features according to languages - - local default = 0 - local setups = fonts.define.specify.context_setups - local numbers = fonts.define.specify.context_numbers - - function fonts.define.specify.context_number(name) - local t = setups[name] - if not t then - return default - elseif t.auto then - local lng = tonumber(tex.language) - local tag = name .. ":" .. lng - local s = setups[tag] - if s then - return s.number or default - else - local script, language = languages.association(lng) - if t.script ~= script or t.language ~= language then - local s = table.fastcopy(t) - local n = #numbers + 1 - setups[tag] = s - numbers[n] = tag - s.number = n - s.script = script - s.language = language - return n - else - setups[tag] = t - return t.number or default - end - end - else - return t.number or default +local function check_otf(specification,suffix,what) + local fullname, tfmtable = resolvers.findbinfile(specification.name,suffix) or "", nil + if fullname == "" then + local fb = fonts.names.old_to_new[specification.name] + if fb then + fullname = resolvers.findbinfile(fb,suffix) or "" end end - -end - -function fonts.define.specify.context_tostring(name,kind,separator,yes,no,strict,omit) -- not used - return aux.hash_to_string(table.merged(fonts[kind].features.default or {},fonts.define.specify.context_setups[name] or {}),separator,yes,no,strict,omit) -end - -function fonts.define.specify.split_context(features) - if fonts.define.specify.context_setups[features] then - return fonts.define.specify.context_setups[features] - else -- ? ? ? - return fonts.define.specify.preset_context("***",features) + if fullname == "" then + local fb = fonts.names.new_to_old[specification.name] + if fb then + fullname = resolvers.findbinfile(fb,suffix) or "" + end + end + if fullname ~= "" then + specification.filename, specification.format = fullname, what -- hm, so we do set the filename, then + tfmtable = tfm.read_from_open_type(specification) -- we need to do it for all matches / todo end + return tfmtable end -local splitter = lpeg.splitat(",") - -function fonts.define.specify.starred(features) -- no longer fallbacks here - local detail = features.detail - if detail and detail ~= "" then - features.features.normal = fonts.define.specify.split_context(detail) +function readers.opentype(specification,suffix,what) + local forced = specification.forced or "" + if forced == "otf" then + return check_otf(specification,forced,"opentype") + elseif forced == "ttf" then + return check_otf(specification,forced,"truetype") + elseif forced == "ttf" then + return check_otf(specification,forced,"truetype") else - features.features.normal = { } + return check_otf(specification,suffix,what) end - return features end -fonts.define.register_split('*',fonts.define.specify.starred) +function readers.otf(specification) return readers.opentype(specification,"otf","opentype") end +function readers.ttf(specification) return readers.opentype(specification,"ttf","truetype") end +function readers.ttc(specification) return readers.opentype(specification,"ttf","truetype") end -- !! --[[ldx--We need to check for default features. For this we provide a helper function.
--ldx]]-- -function fonts.define.check(features,defaults) -- nb adapts features ! +function define.check(features,defaults) -- nb adapts features ! local done = false if table.is_empty(features) then features, done = table.fastcopy(defaults), true else - for k,v in pairs(defaults) do + for k,v in next, defaults do if features[k] == nil then features[k], done = v, true end @@ -601,43 +452,59 @@ not gain much. By the way, passing id's back to in the callback was introduced later in the development. --ldx]]-- -fonts.define.last = nil +define.last = nil -function fonts.define.register(fontdata,id) +function define.register(fontdata,id) if fontdata and id then local hash = fontdata.hash if not tfm.internalized[hash] then - if fonts.trace then + if trace_defining then logs.report("define font","loading at 2 id %s, hash: %s",id or "?",hash or "?") end - tfm.id[id] = fontdata + fonts.ids[id] = fontdata tfm.internalized[hash] = id end end end -function fonts.define.registered(hash) +function define.registered(hash) local id = tfm.internalized[hash] - return id, id and tfm.id[id] + return id, id and fonts.ids[id] end local cache_them = false -function fonts.define.read(specification,size,id) -- id can be optional, name can already be table - input.starttiming(fonts) +function tfm.make(specification) + -- currently fonts are scaled while constructing the font, so we + -- have to do scaling of commands in the vf at that point using + -- e.g. "local scale = g.factor or 1" after all, we need to work + -- with copies anyway and scaling needs to be done at some point; + -- however, when virtual tricks are used as feature (makes more + -- sense) we scale the commands in fonts.tfm.scale (and set the + -- factor there) + local fvm = define.methods[specification.features.vtf.preset] + if fvm then + return fvm(specification) + else + return nil + end +end + +function define.read(specification,size,id) -- id can be optional, name can already be table + statistics.starttiming(fonts) if type(specification) == "string" then - specification = fonts.define.analyze(specification,size) + specification = define.analyze(specification,size) end local method = specification.method - if method and fonts.define.specify[method] then - specification = fonts.define.specify[method](specification) + if method and define.specify[method] then + specification = define.specify[method](specification) end - specification = fonts.define.resolve(specification) + specification = define.resolve(specification) local hash = tfm.hash_instance(specification) if cache_them then local fontdata = containers.read(fonts.cache(),hash) -- for tracing purposes end - local fontdata = fonts.define.registered(hash) -- id + local fontdata = define.registered(hash) -- id if not fontdata then if specification.features.vtf and specification.features.vtf.preset then fontdata = tfm.make(specification) @@ -652,15 +519,16 @@ function fonts.define.read(specification,size,id) -- id can be optional, name ca end if fontdata then fontdata.hash = hash + fontdata.cache = "no" if id then - fonts.define.register(fontdata,id) + define.register(fontdata,id) end end end - fonts.define.last = fontdata or id -- todo ! ! ! ! ! + define.last = fontdata or id -- todo ! ! ! ! ! if not fontdata then logs.report("define font", "unknown font %s, loading aborted",specification.name) - elseif fonts.trace and type(fontdata) == "table" then + elseif trace_defining and type(fontdata) == "table" then logs.report("define font","using %s font with id %s, n:%s s:%s b:%s e:%s p:%s f:%s", fontdata.type or "unknown", id or "?", @@ -671,148 +539,30 @@ function fonts.define.read(specification,size,id) -- id can be optional, name ca fontdata.fullname or "?", file.basename(fontdata.filename or "?")) end - input.stoptiming(fonts) + statistics.stoptiming(fonts) return fontdata end --- define (two steps) - -local P, C, Cc = lpeg.P, lpeg.C, lpeg.Cc - -local space = P(" ") -local spaces = space^0 -local value = C((1-space)^1) -local rest = C(P(1)^0) -local scale_none = Cc(0) -local scale_at = P("at") * Cc(1) * spaces * value -local scale_sa = P("sa") * Cc(2) * spaces * value -local scale_mo = P("mo") * Cc(3) * spaces * value -local scale_scaled = P("scaled") * Cc(4) * spaces * value - -local sizepattern = spaces * (scale_at + scale_sa + scale_mo + scale_scaled + scale_none) -local splitpattern = spaces * value * spaces * rest - -local specification -- - -function fonts.define.command_1(str) - input.starttiming(fonts) - local fullname, size = splitpattern:match(str) - local lookup, name, sub, method, detail = fonts.define.get_specification(fullname) - if not name then - logs.report("define font","strange definition '%s'",str) - texsprint(tex.ctxcatcodes,"\\glet\\somefontname\\defaultfontfile") - elseif name == "unknown" then - texsprint(tex.ctxcatcodes,"\\glet\\somefontname\\defaultfontfile") - else - texsprint(tex.ctxcatcodes,format("\\xdef\\somefontname{%s}",name)) - end - -- we can also use a count for the size - if size and size ~= "" then - local mode, size = sizepattern:match(size) - if size and mode then - count.scaledfontmode = mode - texsprint(tex.ctxcatcodes,format("\\def\\somefontsize{%s}",size)) - else - count.scaledfontmode = 0 - texsprint(tex.ctxcatcodes,format("\\let\\somefontsize\\empty",size)) - end - else - count.scaledfontmode = 0 - texsprint(tex.ctxcatcodes,format("\\let\\somefontsize\\empty",size)) - end - specification = fonts.define.makespecification(str,lookup,name,sub,method,detail,size) -end - -function fonts.define.command_2(global,cs,name,size,classfeatures,fontfeatures,classfallbacks,fontfallbacks) - local trace = fonts.trace - -- name is now resolved and size is scaled cf sa/mo - local lookup, name, sub, method, detail = fonts.define.get_specification(name or "") - -- asome settings can be overloaded - if lookup and lookup ~= "" then specification.lookup = lookup end - specification.name = name - specification.size = size - specification.sub = sub - if detail and detail ~= "" then - specification.method, specification.detail = method or "*", detail - elseif specification.detail and specification.detail ~= "" then - -- already set - elseif fontfeatures and fontfeatures ~= "" then - specification.method, specification.detail = "*", fontfeatures - elseif classfeatures and classfeatures ~= "" then - specification.method, specification.detail = "*", classfeatures - end - if trace then - logs.report("define font","memory usage before: %s",ctx.memused()) - end -if fontfallbacks and fontfallbacks ~= "" then - specification.fallbacks = fontfallbacks -elseif classfallbacks and classfallbacks ~= "" then - specification.fallbacks = classfallbacks -end - local tfmdata = fonts.define.read(specification,size) -- id not yet known - if not tfmdata then - logs.report("define font","unable to define %s as \\%s",name,cs) - elseif type(tfmdata) == "number" then - if trace then - logs.report("define font","reusing %s with id %s as \\%s (features: %s/%s, fallbacks: %s/%s)",name,tfmdata,cs,classfeatures,fontfeatures,classfallbacks,fontfallbacks) - end - tex.definefont(global,cs,tfmdata) - -- resolved (when designsize is used): - texsprint(tex.ctxcatcodes,format("\\def\\somefontsize{%isp}",tfm.id[tfmdata].size)) - else - -- local t = os.clock(t) - local id = font.define(tfmdata) - -- print(name,os.clock()-t) - tfmdata.id = id - fonts.define.register(tfmdata,id) - tex.definefont(global,cs,id) - tfm.cleanup_table(tfmdata) - if fonts.trace then - logs.report("define font","defining %s with id %s as \\%s (features: %s/%s, fallbacks: %s/%s)",name,id,cs,classfeatures,fontfeatures,classfallbacks,fontfallbacks) - end - -- resolved (when designsize is used): - texsprint(tex.ctxcatcodes,format("\\def\\somefontsize{%isp}",tfmdata.size)) - --~ if specification.fallbacks then - --~ fonts.collections.prepare(specification.fallbacks) - --~ end - end - if trace then - logs.report("define font","memory usage after: %s",ctx.memused()) - end - input.stoptiming(fonts) -end - - ---~ table.insert(tfm.readers.sequence,1,'vtf') - ---~ function tfm.readers.vtf(specification) ---~ if specification.features.vtf and specification.features.vtf.preset then ---~ return tfm.make(specification) ---~ else ---~ return nil ---~ end ---~ end - -function fonts.vf.find(name) +function vf.find(name) name = file.removesuffix(file.basename(name)) if tfm.resolve_vf then local format = fonts.logger.format(name) if format == 'tfm' or format == 'ofm' then - if fonts.trace then + if trace_defining then logs.report("define font","locating vf for %s",name) end - return input.findbinfile(name,"ovf") + return resolvers.findbinfile(name,"ovf") else - if fonts.trace then + if trace_defining then logs.report("define font","vf for %s is already taken care of",name) end return nil -- "" end else - if fonts.trace then + if trace_defining then logs.report("define font","locating vf for %s",name) end - return input.findbinfile(name,"ovf") + return resolvers.findbinfile(name,"ovf") end end @@ -820,5 +570,5 @@ endWe overload both the
Because encodings are going to disappear, we don't bother defining them in tables. But we may do so some day, for consistency.
@@ -62,15 +64,15 @@ function fonts.enc.load(filename) return data end local vector, tag, hash, unicodes = { }, "", { }, { } - local foundname = input.find_file(filename,'enc') + local foundname = resolvers.find_file(filename,'enc') if foundname and foundname ~= "" then - local ok, encoding, size = input.loadbinfile(foundname) + local ok, encoding, size = resolvers.loadbinfile(foundname) if ok and encoding then local enccodes = characters.enccodes - encoding = encoding:gsub("%%(.-)\n","") - local tag, vec = encoding:match("/(%w+)%s*%[(.*)%]%s*def") + encoding = gsub(encoding,"%%(.-)\n","") + local tag, vec = match(encoding,"/(%w+)%s*%[(.*)%]%s*def") local i = 0 - for ch in vec:gmatch("/([%a%d%.]+)") do + for ch in gmatch(vec,"/([%a%d%.]+)") do if ch ~= ".notdef" then vector[i] = ch if not hash[ch] then @@ -105,7 +107,7 @@ one. function fonts.enc.make_unicode_vector() local vector, hash = { }, { } - for code, v in pairs(characters.data) do + for code, v in next, characters.data do local name = v.adobename if name then vector[code], hash[name] = name, code @@ -113,7 +115,7 @@ function fonts.enc.make_unicode_vector() vector[code] = '.notdef' end end - for name, code in pairs(characters.synonyms) do + for name, code in next, characters.synonyms do vector[code], hash[name] = name, code end return containers.write(fonts.enc.cache(), 'unicode', { name='unicode', tag='unicode', vector=vector, hash=hash }) diff --git a/tex/context/base/font-ext.lua b/tex/context/base/font-ext.lua index c3979fad6..17c302c53 100644 --- a/tex/context/base/font-ext.lua +++ b/tex/context/base/font-ext.lua @@ -6,7 +6,191 @@ if not modules then modules = { } end modules ['font-ext'] = { license = "see context related readme files" } -local byte = string.byte +local next, type, byte = next, type, string.byte + +--[[ldx-- +When we implement functions that deal with features, most of them +will depend of the font format. Here we define the few that are kind +of neutral.
+--ldx]]-- + +fonts.triggers = fonts.triggers or { } +fonts.initializers = fonts.initializers or { } +fonts.initializers.common = fonts.initializers.common or { } + +local initializers = fonts.initializers + +--[[ldx-- +This feature will remove inter-digit kerns.
+--ldx]]-- + +table.insert(fonts.triggers,"equaldigits") + +function initializers.common.equaldigits(tfmdata,value) + if value then + local chr = tfmdata.characters + for i = utfbyte('0'), utfbyte('9') do + local c = chr[i] + if c then + c.kerns = nil + end + end + end +end + +--[[ldx-- +This feature will give all glyphs an equal height and/or depth. Valid
+values are
It does not make sense any more to support messed up encoding vectors
+so we stick to those that implement oldstyle and small caps. After all,
+we move on. We can extend the next function on demand. This features is
+only used with
This is very experimental code!
--ldx]]-- -fonts.fallbacks = fonts.fallbacks or { } -fonts.vf.aux.combine.trace = false +fonts.fallbacks = fonts.fallbacks or { } local vf = fonts.vf local tfm = fonts.tfm vf.aux.combine.commands["enable-tracing"] = function(g,v) - vf.aux.combine.trace = true + trace_combining = true end vf.aux.combine.commands["disable-tracing"] = function(g,v) - vf.aux.combine.trace = false + trace_combining = false end vf.aux.combine.commands["set-tracing"] = function(g,v) if v[2] == nil then - vf.aux.combine.trace = true + trace_combining = true else - vf.aux.combine.trace = v[2] + trace_combining = v[2] end end function vf.aux.combine.initialize_trace() - if vf.aux.combine.trace then + if trace_combining then return "special", "pdf: .8 0 0 rg .8 0 0 RG", "pdf: 0 .8 0 rg 0 .8 0 RG", "pdf: 0 0 .8 rg 0 0 .8 RG", "pdf: 0 g 0 G" else return "comment", "", "", "", "" @@ -54,7 +57,7 @@ end fonts.fallbacks['textcent'] = function (g) local c = ("c"):byte() local t = table.fastcopy(g.characters[c]) - local a = - math.tan(math.rad(g.italicangle or 0)) + local a = - tan(rad(g.italicangle or 0)) local special, red, green, blue, black = vf.aux.combine.initialize_trace() local quad = g.parameters.quad if a == 0 then @@ -71,7 +74,7 @@ fonts.fallbacks['textcent'] = function (g) {"push"}, {"right", .5*t.width-.025*quad}, {"down", .2*t.height}, - {"special",("pdf: q 1 0 %s 1 0 0 cm"):format(a)}, + {"special",format("pdf: q 1 0 %s 1 0 0 cm",a)}, {special, green}, {"rule", 1.4*t.height, .025*quad}, {special, black}, @@ -84,6 +87,7 @@ fonts.fallbacks['textcent'] = function (g) -- todo: set height t.height = 1.2*t.height t.depth = 0.2*t.height + g.virtualized = true local d = g.descriptions return t, d and d[c] end @@ -91,7 +95,7 @@ end fonts.fallbacks['texteuro'] = function (g) local c = ("C"):byte() local t = table.fastcopy(g.characters[c]) - local d = math.cos(math.rad(90+(g.italicangle))) + local d = cos(rad(90+(g.italicangle))) local special, red, green, blue, black = vf.aux.combine.initialize_trace() local quad = g.parameters.quad t.width = 1.05*t.width @@ -104,6 +108,7 @@ fonts.fallbacks['texteuro'] = function (g) {"rule", .05*quad, .4*quad}, {special, black}, } + g.virtualized = true return t, g.descriptions[c] end @@ -111,6 +116,10 @@ end vf.aux.combine.force_composed = false +local push, pop = { "push" }, { "pop" } + +local cache = { } -- we could make these weak + function vf.aux.compose_characters(g) -- todo: scaling depends on call location -- this assumes that slot 1 is self, there will be a proper self some day local chars, descs = g.characters, g.descriptions @@ -120,14 +129,16 @@ function vf.aux.compose_characters(g) -- todo: scaling depends on call location if xchar and xdesc then local scale = g.factor or 1 local cap_lly = scale*xdesc.boundingbox[4] - local ita_cor = math.cos(math.rad(90+(g.italicangle or 0))) + local ita_cor = cos(rad(90+(g.italicangle or 0))) local force = vf.aux.combine.force_composed local fallbacks = characters.fallbacks - local special, red, green, blue, black = vf.aux.combine.initialize_trace() - red, green, blue, black = { special, red }, { special, green }, { special, blue }, { special, black } - local push, pop = { "push" }, { "pop" } - local trace = vf.aux.combine.trace -- saves mem - for i,c in pairs(characters.data) do + local special, red, green, blue, black + if trace_combining then + special, red, green, blue, black = vf.aux.combine.initialize_trace() + red, green, blue, black = { special, red }, { special, green }, { special, blue }, { special, black } + end + local done = false + for i,c in next, characters.data do if force or not chars[i] then local s = c.specials if s and s[1] == 'char' then @@ -138,17 +149,35 @@ function vf.aux.compose_characters(g) -- todo: scaling depends on call location if cc == 'll' or cc == 'lu' or cc == 'lt' then local acc = s[3] local t = { } - for k, v in pairs(charschr) do + for k, v in next, charschr do if k ~= "commands" then t[k] = v end end local charsacc = chars[acc] +--~ local ca = charsacc.category +--~ if ca == "mn" then +--~ -- mark nonspacing +--~ elseif ca == "ms" then +--~ -- mark spacing combining +--~ elseif ca == "me" then +--~ -- mark enclosing +--~ else if not charsacc then acc = fallbacks[acc] charsacc = acc and chars[acc] end if charsacc then + local chr_t = cache[chr] + if not cht_t then + chr_t = {"slot", 1, chr} + cache[chr] = chr_t + end + local acc_t = cache[acc] + if not acc_t then + acc_t = {"slot", 1, acc} + cache[acc] = acc_t + end local cb = descs[chr].boundingbox local ab = descs[acc].boundingbox if cb and ab then @@ -158,77 +187,75 @@ function vf.aux.compose_characters(g) -- todo: scaling depends on call location local dx = (c_urx - a_urx - a_llx + c_llx)/2 local dd = (c_urx - c_llx)*ita_cor if a_ury < 0 then - -- local dy = cap_lly-a_lly - if trace then + if trace_combining then t.commands = { push, {"right", dx-dd}, - -- {"down", -dy}, -- added red, - {"slot", 1, acc}, + acc_t, black, pop, - {"slot", 1, chr}, + chr_t, } else t.commands = { push, {"right", dx-dd}, - -- {"down", -dy}, -- added - {"slot", 1, acc}, + acc_t, pop, - {"slot", 1, chr}, + chr_t, } end elseif c_ury > a_lly then local dy = cap_lly-a_lly - if trace then + if trace_combining then t.commands = { push, {"right", dx+dd}, {"down", -dy}, green, - {"slot", 1, acc}, + acc_t, black, pop, - {"slot", 1, chr}, + chr_t, } else t.commands = { push, {"right", dx+dd}, {"down", -dy}, - {"slot", 1, acc}, + acc_t, pop, - {"slot", 1, chr}, + chr_t, } end else - if trace then + if trace_combining then t.commands = { {"push"}, {"right", dx+dd}, blue, - {"slot", 1, acc}, + acc_t, black, {"pop"}, - {"slot", 1, chr}, + chr_t, } else t.commands = { {"push"}, {"right", dx+dd}, - {"slot", 1, acc}, + acc_t, {"pop"}, - {"slot", 1, chr}, + chr_t, } end end + done = true end end chars[i] = t local d = { } - for k, v in pairs(descs[chr]) do + for k, v in next, descs[chr] do d[k] = v end d.name = c.adobename or "unknown" @@ -239,6 +266,9 @@ function vf.aux.compose_characters(g) -- todo: scaling depends on call location end end end + if done then + g.virtualized = true + end end end diff --git a/tex/context/base/font-ini.lua b/tex/context/base/font-ini.lua index 5db2973a1..248a2baca 100644 --- a/tex/context/base/font-ini.lua +++ b/tex/context/base/font-ini.lua @@ -10,34 +10,44 @@ if not modules then modules = { } end modules ['font-ini'] = {Not much is happening here.
--ldx]]-- +local utf = unicode.utf8 + +if not fontloader then fontloader = fontforge end + +fontloader.totable = fontloader.to_table + -- vtf comes first -- fix comes last -fonts = fonts or { } +fonts = fonts or { } +fonts.ids = fonts.ids or { } -- aka fontdata +fonts.tfm = fonts.tfm or { } -fonts.trace = false -- true fonts.mode = 'base' -fonts.private = 0xE000 +fonts.private = 0xF0000 -- 0x10FFFF fonts.verbose = false -- more verbose cache tables -fonts.methods = { +fonts.methods = fonts.methods or { base = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, node = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, } -fonts.initializers = { +fonts.initializers = fonts.initializers or { base = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, node = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } } } -fonts.triggers = { +fonts.triggers = fonts.triggers or { 'mode', 'language', 'script', 'strategy', } -fonts.manipulators = { +fonts.processors = fonts.processors or { +} + +fonts.manipulators = fonts.manipulators or { } fonts.define = fonts.define or { } @@ -48,18 +58,35 @@ fonts.define.specify.synonyms = fonts.define.specify.synonyms or { } fonts.color = fonts.color or { } -fonts.color.trace = false - -local attribute = attributes.numbers['color'] or 7 -- we happen to know this -) -local mapping = attributes.list[attribute] +local attribute = attributes.private('color') +local mapping = (attributes and attributes.list[attribute]) or { } local set_attribute = node.set_attribute local unset_attribute = node.unset_attribute function fonts.color.set(n,c) --- local mc = mapping[c] if mc then unset_attribute((n,attribute) else set_attribute(n,attribute,mc) end - set_attribute(n,attribute,mapping[c] or -1) -- also handles -1 now + local mc = mapping[c] + if not mc then + unset_attribute(n,attribute) + else + set_attribute(n,attribute,mc) + end end function fonts.color.reset(n) unset_attribute(n,attribute) end + +-- this will change ... + +function fonts.show_char_data(n) + local tfmdata = fonts.ids[font.current()] + if tfmdata then + if type(n) == "string" then + n = utf.byte(n) + end + local chr = tfmdata.characters[n] + if chr then + texio.write_nl(table.serialize(chr,string.format("U_%04X",n))) + end + end +end diff --git a/tex/context/base/font-ini.mkii b/tex/context/base/font-ini.mkii index 9b9f5ac83..658d06f70 100644 --- a/tex/context/base/font-ini.mkii +++ b/tex/context/base/font-ini.mkii @@ -12,7 +12,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Context Font Macros (ini)} +\writestatus{loading}{ConTeXt Font Macros / Initialization} \unprotect @@ -121,125 +121,21 @@ %%% message 14 added -\startmessages dutch library: fonts - title: korps - 1: codering -- - 2: variant -- wordt geladen - 3: onbekende variant -- - 4: korps -- is niet gedefinieerd - 5: stijl -- is niet gedefinieerd - 6: -- wordt geladen - 7: onbekend formaat -- - 8: stijl -- gedefinieerd -% 9: mapping -- is geladen - 10: onbekende font file -- - 14: korps -- is gedefinieerd (kan beter globaal plaatsvinden) -\stopmessages - -\startmessages english library: fonts - title: bodyfont - 1: coding -- - 2: variant -- is loaded - 3: unknown variant -- - 4: bodyfont -- is not defined - 5: style -- is not defined - 6: -- is loaded - 7: unknown format -- - 8: style -- defined -% 9: mapping -- is loaded - 10: unknown font file -- - 14: bodyfont -- is defined (can better be done global) -\stopmessages - -\startmessages german library: fonts - title: Fliesstext - 1: Kodierung -- - 2: Variante -- ist geladen - 3: Unbekannte Variante -- - 4: Fliesstext -- ist nicht definiert - 5: Stil -- ist nicht definiert - 6: -- ist geladen - 7: unbekanntes Format -- - 8: Stil -- definiert -% 9: Map -- ist geladen - 10: unbekanntes Font -- - 14: Fliesstext -- wurde definiert (besser waere globale Definition) -\stopmessages - -\startmessages czech library: fonts - title: zakladnifont - 1: kodovani -- - 2: varianta -- je nactena - 3: neznama varianta -- - 4: zakladni font -- neni definovan - 5: styl -- neni definovan - 6: -- je nacten - 7: neznamy format -- - 8: styl -- definovan -% 9: mapovani -- je nacteno - 10: neznamy font -- - 14: bodyfont -- is defined (can better be done global) -\stopmessages - -\startmessages italian library: fonts - title: font del corpo - 1: codifica -- - 2: variante -- caricata - 3: variante sconosciuta -- - 4: corpo del testo -- non definito - 5: stile -- non definito - 6: -- caricato - 7: formato sconosciuto -- - 8: stile -- definito -% 9: mappatura -- caricata - 10: file di font sconosciuto -- - 14: corpo del testo -- definito (sarebbe meglio globale) -\stopmessages - -\startmessages norwegian library: fonts - title: hovedfont - 1: koding -- - 2: variant -- er lest inn - 3: ukjent variant -- - 4: hovedfont -- er ikke definert - 5: stil -- er ikke definert - 6: -- er lest inn - 7: ukjent format -- - 8: stil -- definert -% 9: avbildning -- er lest inn - 10: ukjent fontfil -- - 14: bodyfont -- is defined (can better be done global) -\stopmessages - -\startmessages romanian library: fonts - title: corp de litere - 1: codificarea -- - 2: varianta -- este incarcata - 3: varianta necunoscuta -- - 4: corpul de litere -- nu este definit - 5: stilul -- nu este definit - 6: -- este incarcat - 7: format necunoscut -- - 8: stilul -- definit -% 9: maparea -- este incarcat - 10: fisier font necunoscut -- - 14: bodyfont -- is defined (can better be done global) -\stopmessages - -\startmessages french library: fonts - title: corps de texte - 1: encodage -- - 2: la variante -- est chargée - 3: variante -- inconnue - 4: policecorps -- n'est pas définie - 5: le style -- n'est pas défini - 6: -- est chargé - 7: format -- inconnu - 8: style -- défini -% 9: mapping -- is loaded - 10: fichier de police -- inconnu - 14: policecorps -- est défini (une définition globale pourrait être plus adéquat) -\stopmessages +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved %D This module is one of the oldest modules of \CONTEXT. The %D macros below evolved out of the \PLAIN\ \TEX\ macros and @@ -682,8 +578,6 @@ %D %D We can fix this by defining -\let\normalmathop\mathop - \unexpanded\def\mathop {\normalmathop \bgroup @@ -694,8 +588,6 @@ %D \TEX\ primitive \type{\hbox}: %D %D \starttyping -%D \let\normalhbox=\hbox -%D %D \def\hbox{\ifmmode\mbox\else\normalhbox\fi} %D \stoptyping %D @@ -875,6 +767,7 @@ \def\@shortstyle@ {@f@sh@} % short style prefix (rm etc) \def\@letter@ {@f@le@} % first alternative typeface \def\@noletter@ {@f@no@} % second alternative typeface +\def\@fontclass@ {@f@cl@} % fontclass %D The families can be grouped into math specific ones and %D more text related families, although text ones can be @@ -1060,9 +953,7 @@ %D separated list. Appart from practical limitations one can %D define as many styles as needed. -\let\stylelist=\empty - -\def\fontsizelist{\s!text,\s!script,\s!scriptscript,\c!x,\c!xx,\c!big,\c!small} +\def\fontrelativesizelist{\s!text,\s!script,\s!scriptscript,\c!x,\c!xx,\c!big,\c!small} %D \macros %D {magfactor,magfactorhalf} @@ -1099,7 +990,7 @@ %D \stoptable \def\magstep#1% \relax removed, otherwise space after it sticks, else added - {\ifcase#1 \@m\or1200\or1440\or1728\or2074\or2488\or\@m\fi} + {\ifcase#1 1000\or1200\or1440\or1728\or2074\or2488\or1000\fi} \def\magstephalf {1095} @@ -1287,25 +1178,21 @@ % [encoding=ec] % \definedfont[blabla] test \currentencoding/\fontfile \par -\beginOLDTEX - - \def\checkfontfilename - {\expandafter\docheckfontfilename\fontfile:\empty:\empty\relax} - - \def\docheckfontfilename#1:#2:#3#4\relax - {\edef\!!stringa{#1}% - \edef\!!stringb{#2}% - \ifx\!!stringb\empty - \edef\checkedfontfile{\!!stringa}% - \else\ifx\!!stringa\v!file - \edef\checkedfontfile{"\!!stringb"}% - \else\ifx\!!stringa\v!name - \edef\checkedfontfile{"\!!stringb"}% - \else - \edef\checkedfontfile{\!!stringb}% - \fi\fi\fi} - -\endOLDTEX +\def\checkfontfilename + {\expandafter\docheckfontfilename\fontfile:\empty:\empty\relax} + +\def\docheckfontfilename#1:#2:#3#4\relax + {\edef\!!stringa{#1}% + \edef\!!stringb{#2}% + \ifx\!!stringb\empty + \edef\checkedfontfile{\!!stringa}% + \else\ifx\!!stringa\v!file + \edef\checkedfontfile{"\!!stringb"}% + \else\ifx\!!stringa\v!name + \edef\checkedfontfile{"\!!stringb"}% + \else + \edef\checkedfontfile{\!!stringb}% + \fi\fi\fi} % \definefontfeature[default] [liga=yes,texligatures=yes,texquotes=yes] % \definefontfeature[default-caps][liga=yes,texligatures=yes,texquotes=yes,smcp=yes,script=latn] @@ -1389,156 +1276,6 @@ % % \stoptext -% xetex / todo: disable default features ! file:, name:, [], "" etc etc - -\beginXETEX - - % for some reason xetex does not support [filename] for tfm files and - % quotes also behave kind of strange " vs ' vs [ vs ... - - % we need to use the specs, - % - % \font\myfont = msam7 % ok - % \font\myfont = "msam7" % also ok - % \font\myfont = "msam7" at 8pt % error - - \ifx\suppressfontnotfounderror\undefined - - \newcount\xetexsavedinteractionmode - \newbox \xetexcrappyhackbox - - \def\doiffoundxetexfontelse#1#2% - {\xetexsavedinteractionmode\interactionmode - \batchmode - \setbox\xetexcrappyhackbox\vbox{\par}% resets error count - \font\xetextempfont=#2\somefontspec\relax - \edef\xetextempfont{\fontname\xetextempfont}% - \ifx\xetextempfont\nullfontname - \interactionmode\xetexsavedinteractionmode - %\writestatus\m!fonts{fails #1: #2 (\xetextempfont)}% - \expandafter\secondoftwoarguments - \else - \interactionmode\xetexsavedinteractionmode - %\writestatus\m!fonts{succeeds #1: #2 (\xetextempfont)}% - \expandafter\firstoftwoarguments - \fi} - - \else - - \def\doiffoundxetexfontelse#1#2% - {\suppressfontnotfounderror\plusone - \font\xetextempfont=#2\somefontspec\relax - \suppressfontnotfounderror\zerocount - \edef\xetextempfont{\fontname\xetextempfont}% - \ifx\xetextempfont\nullfontname - %\writestatus\m!fonts{fails #1: #2 (\xetextempfont)}% - \expandafter\secondoftwoarguments - \else - %\writestatus\m!fonts{succeeds #1: #2 (\xetextempfont)}% - \expandafter\firstoftwoarguments - \fi} - - \fi - - \def\docheckfontfilenameprefix#1:#2:#3#4\relax - {\edef\!!stringa{#1}% - \edef\!!stringb{#2}% - \ifx\!!stringb\empty - % no prefix - \let\checkedfontfile\!!stringa - \doiffoundxetexfontelse{1a}{\checkedfontfile\checkedfontfeatures} - {\edef\checkedfontfile{\checkedfontfile\checkedfontfeatures}} - {\doiffoundxetexfontelse{1b}{"\checkedfontfile\checkedfontfeatures"} - {\edef\checkedfontfile{"\checkedfontfile\checkedfontfeatures"}} - {\doiffoundxetexfontelse{1c}{"[\checkedfontfile]\checkedfontfeatures"} - {\edef\checkedfontfile{"[\checkedfontfile]\checkedfontfeatures"}} - {}}}% - \else\ifx\!!stringa\v!file - % force file, only file check when no spaces - \let\checkedfontfile\!!stringb - \doiffoundxetexfontelse{2b}{"[\checkedfontfile]\checkedfontfeatures"} - {\edef\checkedfontfile{"[\checkedfontfile]\checkedfontfeatures"}} - {\doiffoundxetexfontelse{2c}{"\checkedfontfile\checkedfontfeatures"} - {\edef\checkedfontfile{"\checkedfontfile\checkedfontfeatures"}} - {}}% - \else\ifx\!!stringa\v!name - % force name, always lookup by xetex itself, "" forces otf/ttf/type1 - \edef\checkedfontfile{"\!!stringb\checkedfontfeatures"}% - \else - % whatever, maybe even xetex spec, forget about features - \edef\checkedfontfile{"\!!stringa\!!stringb"}% - \fi\fi\fi} - - \def\checkfontfilename% -- todo: integrate so that we call do.. directly - {\expandafter\docheckfontfilename\fontfile*\empty*\relax} - - \def\docheckfontfilename#1*#2#3*#4\relax % class overrules file - {\edef\checkedfontfeatures - {\expandafter\ifx\csname\fontclass\s!features\endcsname\empty - \ifx\@@fontfeatures\empty\ifx#2\empty\else#2#3\fi\else\@@fontfeatures\fi - \else\expandafter\ifx\csname\fontclass\s!features\endcsname\relax % redundant, will go away - \ifx\@@fontfeatures\empty\ifx#2\empty\else#2#3\fi\else\@@fontfeatures\fi - \else - \csname\fontclass\s!features\endcsname - \fi\fi}% - \ifx\checkedfontfeatures\empty - % done - \else - \edef\checkedfontfeatures{\executeifdefined{\??fa\checkedfontfeatures}\empty}% - \ifx\checkedfontfeatures\empty - % done - \else - \let\convertedfontfeatures\empty - \processcommacommand[\checkedfontfeatures]\doconvertfontfeatures % raw - \ifx\convertedfontfeatures\empty - \let\checkedfontfeatures\empty - \else - \edef\checkedfontfeatures{:\convertedfontfeatures}% - \fi - \fi - \fi - \docheckfontfilenameprefix#1:\empty:\empty\relax - \doshowcheckedfontfeatures} - - \def\dodoconvertfontfeatures#1=#2#3=#4\relax - {\ifx#2\empty - % invalid feature - \else\ifcsname @xtx@#1@#2#3\endcsname - \expandafter\ifx\csname @xtx@#1@#2#3\endcsname\empty\else - \edef\convertedfontfeatures{\convertedfontfeatures\csname @xtx@#1@#2#3\endcsname;}% - \fi - \else - \edef\!!stringa{#1}% - \edef\!!stringb{#2#3}% - \edef\convertedfontfeatures - {\convertedfontfeatures - \ifx\!!stringb\v!yes - +\!!stringa - \else\ifx\!!stringb\v!no - -\!!stringa - \else - \!!stringa=\!!stringb - \fi\fi;}% - \fi\fi} - - \def\doconvertfontfeatures#1% - {\dodoconvertfontfeatures#1=\empty=\relax} - - \def\remapfontfeature #1 #2 #3 {\setevalue{@xtx@#1@#2}{#3}} - - % this may move to another file, maybe font-xtx - - \remapfontfeature tlig yes mapping=tlig - %remapfontfeature tlig no mapping= - \remapfontfeature trep yes {} - \remapfontfeature trep no {} - \remapfontfeature texligatures yes mapping=tlig - %remapfontfeature texligatures no mapping= - %remapfontfeature texquotes yes mapping=tex-text - %remapfontfeature texquotes no mapping= - -\endXETEX - \let\doshowcheckedfontfeatures\relax \def\showcheckedfontfeatures @@ -1550,7 +1287,7 @@ \def\donoparsefontspec % #1 == \cs {\edef\fontfile{\truefontname\somefontname}% - \ifx\fontfile\s!unknown \let\fontfile\defaultfontfile \fi + \ifx\fontfile\s!unknown \let\fontfile\defaultfontfile \fi % can for instance happen with MathGamma \updatefontparameters \checkfontfilename \edef\lastfontname{\checkedfontfile\somefontspec}% @@ -1606,10 +1343,6 @@ \edef\nullfontname {\fontname\nullfont} \edef\dummyfontname {font\strippedcsname\\} -\beginXETEX - \def\defaultfontfile{lmtypewriter10-regular} -\endXETEX - %D \macros %D {everyfont,everyfontswitch} %D @@ -1688,12 +1421,10 @@ \def\classfont#1#2{#1#2} % \definefont[whatever][\classfont{xx}{yy} at 10pt] -\beginOLDTEX - \def\definefontsynonym[#1]#2[#3]% {\edef\@@fontfile{#3}% \@EA\let\csname\??ff\fontclass#1\endcsname\@@fontfile - \doifnextcharelse[\dodefinefontsynonym\donothing} + \doifnextoptionalelse\dodefinefontsynonym\donothing} \def\dodefinefontsynonym[#1]% {\edef\@@fontdata{#1}% @@ -1703,48 +1434,6 @@ \getglobalfontparameters \fi \fi} -\endOLDTEX - -% We need to move the feature into the filename else it may be -% overloaded by another reference. For instance the definition of -% a regular and caps variant can use the same font. - -% We could use an indirect method ... store in 'array' and refer to -% slot. - -\beginNEWTEX - -\def\definefontsynonym[#1]#2[#3]% - {\edef\@@fontname{#1}% - \edef\@@fontfile{#3}% - \doifnextcharelse[\dodefinefontsynonym\nodefinefontsynonym} - -\def\nodefinefontsynonym - {\@EA\let\csname\??ff\fontclass\@@fontname\endcsname\@@fontfile} - -\def\dodefinefontsynonym[#1]% - {\edef\@@fontdata{#1}% - \ifx\@@fontdata\empty - \nodefinefontsynonym - \else - \ifx\fontclass\empty - \getfontparameters - \else - \getglobalfontparameters - \fi - \ifcsname\??ff\@@fontfile\s!features\endcsname - \@EA\edef\csname\??ff\fontclass\@@fontname\endcsname{\@@fontfile*\csname\??ff\@@fontfile\s!features\endcsname}% - \@EA\let\csname\??ff\@@fontfile\s!features\endcsname\undefined - \else - \nodefinefontsynonym - \fi - \fi} - -\endNEWTEX - -% \def\resetfontsynonym[#1]% fails -% {\letbeundefined{\??ff\fontclass#1}\letbeundefined{\??ff#1}} - \let\definefontfile\definefontsynonym % dedicated to Taco Hoekwater \def\setupfontsynonym @@ -1776,8 +1465,6 @@ \csname\??ff#2\endcsname \fi\fi\fi\fi} -\beginOLDTEX - \def\truefontname#1% {\ifcsname\??ff\fontclass#1\endcsname \@EA\truefontname\csname\??ff\fontclass#1\endcsname @@ -1787,86 +1474,6 @@ #1% \fi\fi} -\endOLDTEX - -\beginNEWTEX - -% simple version -% -% \def\truefontname#1% -% {\@EA\dotruefontname#1*\relax} -% -% \def\dotruefontname#1*#2\relax -% {\ifcsname\??ff\fontclass#1\endcsname -% \@EA\truefontname\csname\??ff\fontclass#1\endcsname -% \else\ifcsname\??ff#1\endcsname -% \@EA\truefontname\csname\??ff#1\endcsname -% \else -% #1% -% \fi\fi} -% -% last counts -% -% \def\truefontname#1% -% {\@EA\dotruefontname#1*\empty*\relax} -% -% \def\dotruefontname#1*#2#3*#4\relax -% {\ifcsname\??ff\fontclass#1\endcsname -% \ifx#2\empty -% \@EA\truefontname\csname\??ff\fontclass#1\endcsname -% \else -% \@EA\truefontname\csname\??ff\fontclass#1\endcsname*#2#3% -% \fi -% \else\ifcsname\??ff#1\endcsname -% \ifx#2\empty -% \@EA\truefontname\csname\??ff#1\endcsname -% \else -% \@EA\truefontname\csname\??ff#1\endcsname*#2#3% -% \fi -% \else -% \ifx#2\empty -% #1% -% \else -% #1*#2#3% -% \fi -% \fi\fi} -% -% first counts - -\def\truefontname#1% - {\@EA\dotruefontname#1*\empty*\relax} - -\def\dotruefontname#1*#2#3*#4\relax - {\ifcsname\??ff\fontclass#1\endcsname - \ifx#2\empty - \@EA\truefontname\csname\??ff\fontclass#1\endcsname - \else - \@EA\redotruefontname\csname\??ff\fontclass#1\endcsname*#2#3% - \fi - \else\ifcsname\??ff#1\endcsname - \ifx#2\empty - \@EA\truefontname\csname\??ff#1\endcsname - \else - \@EA\redotruefontname\csname\??ff#1\endcsname*#2#3% - \fi - \else - #1\ifx#2\empty\else*#2#3\fi - \fi\fi} - -\def\redotruefontname#1% - {\@EA\dodotruefontname#1*\relax} - -\def\dodotruefontname#1*#2\relax - {\ifcsname\??ff\fontclass#1\endcsname - \@EA\redotruefontname\csname\??ff\fontclass#1\endcsname - \else\ifcsname\??ff#1\endcsname - \@EA\redotruefontname\csname\??ff#1\endcsname - \else - #1% - \fi\fi} - -\endNEWTEX - \def\expandfontsynonym#1#2% #2 := onelevelexpansion(#1) {\ifcsname\??ff\fontclass#2\endcsname \expandafter\def\expandafter#1\expandafter{\csname\??ff\fontclass#2\endcsname}% @@ -2109,26 +1716,24 @@ %D To be documented. -\let\sizelist\empty +\let\fontsizelist\empty +\let\fontstylelist\empty \def\definefontsize[#1]% sneller met toks - {\addtocommalist{#1}\sizelist + {\addtocommalist{#1}\fontsizelist \def\docommand##1% {\def\dodocommand####1% {\def\dododocommand########1% %{\checkbodyfont{}{########1}{####1}{##1}}% {\checkbodyfont{########1}{####1}{##1}}% - \processcommacommand[\stylelist]\dododocommand}% - \processcommacommand[\alternativelist]\dodocommand}% - \processcommacommand[\sizelist]\docommand} - -\def\alternativetextlist{\c!tf,\c!bf,\c!it,\c!sl,\c!bs,\c!bi,\c!sc} -\def\alternativemathlist{\c!mr,\c!mi,\c!sy,\c!ex,\c!ma,\c!mb} + \processcommacommand[\fontstylelist]\dododocommand}% + \processcommacommand[\fontalternativelist]\dodocommand}% + \processcommacommand[\fontsizelist]\docommand} -\let\alternativelist\alternativetextlist % upward compatible +\def\fontalternativetextlist{\c!tf,\c!bf,\c!it,\c!sl,\c!bs,\c!bi,\c!sc} +\def\fontalternativemathlist{\c!mr,\c!mi,\c!sy,\c!ex,\c!ma,\c!mb} -%\definefontsize[\c!a] \definefontsize[\c!b] -%\definefontsize[\c!c] \definefontsize[\c!d] +\let\fontalternativelist\fontalternativetextlist % upward compatible %D \macros %D {currentfontscale,currentfontbodyscale} @@ -2268,7 +1873,7 @@ \scratchdimen\csname\??ft\s!default##1\endcsname\scratchdimen \normalizebodyfontsize\scratchdimen\to\tempbodyfontsize \setevalue{\??ft#2#1##1}{\tempbodyfontsize}}% - \processcommacommand[\fontsizelist]\docommand + \processcommacommand[\fontrelativesizelist]\docommand \copyparameters [\??ft#2#1][\??ft\s!default] [\c!interlinespace,\c!em]}% @@ -2595,7 +2200,7 @@ % \scratchdimen\csname\??ft\s!default##1\endcsname\scratchdimen % \normalizebodyfontsize\scratchdimen\to\!!stringa % \letvalue{\??ft#1##1}\!!stringa}}% -% \processcommacommand[\fontsizelist]\docommand +% \processcommacommand[\fontrelativesizelist]\docommand % \let\c!text\c!savedtext % \ifdone % \donefalse @@ -2605,7 +2210,7 @@ % {\doifdefined{\s!default\s!default##1} % {\donetrue\getvalue{\s!default\s!default##1}{#1}{##1}}}% % \processcommacommand -% [\stylelist] +% [\fontstylelist] % \defineunknownbodyfont % \ifdone % \setvalue{\@size@#1}{\docompletefontswitch[#1]}% @@ -2614,7 +2219,7 @@ % \def\defineunknownsubfont##1% % {\doifundefined{\@size@\getvalue{\??ft#1##1}} % {\defineunknownfont{\getvalue{\??ft#1##1}}}}% -% \processcommacommand[\fontsizelist]\defineunknownsubfont +% \processcommacommand[\fontrelativesizelist]\defineunknownsubfont % \definingunknownfontfalse % \fi % \fi @@ -2659,19 +2264,19 @@ {\let\c!savedtext\c!text \let\c!text\s!text \donefalse - \processcommacommand[\fontsizelist]{\dodefineunknownfont{#1}}% + \processcommacommand[\fontrelativesizelist]{\dodefineunknownfont{#1}}% \let\c!text\c!savedtext \ifdone \donefalse \processcommacommand - [\stylelist] + [\fontstylelist] {\dodefineunknownbodyfont{#1}}% \ifdone \donefalse \setvalue{\@size@#1}{\docompletefontswitch[#1]}% \ifdefiningunknownfont \else \definingunknownfonttrue - \processcommacommand[\fontsizelist]{\dodefineunknownsubfont{#1}}% + \processcommacommand[\fontrelativesizelist]{\dodefineunknownsubfont{#1}}% \definingunknownfontfalse \fi \fi @@ -2683,7 +2288,7 @@ % \def\defineunknownfontstyles#1% % {\def\defineunknownbodyfont##1% see *** % {\executeifdefined{\s!default\s!default##1}\gobbletwoarguments{#1}{##1}}% -% \rawprocesscommacommand[\stylelist]\defineunknownbodyfont} +% \rawprocesscommacommand[\fontstylelist]\defineunknownbodyfont} %D These macros show that quite some definitions take place. %D Fonts are not loaded yet! This means that at format @@ -2741,8 +2346,8 @@ %D size and the local (sometimes in the textflow) size. We %D store these dimensions in two \DIMENSION\ registers. -\newdimen\globalbodyfontsize \globalbodyfontsize=12pt -\newdimen\localbodyfontsize \localbodyfontsize =\globalbodyfontsize +\ifdefined\globalbodyfontsize\else \newdimen\globalbodyfontsize \fi \globalbodyfontsize=12pt +\ifdefined\localbodyfontsize \else \newdimen\localbodyfontsize \fi \localbodyfontsize =\globalbodyfontsize %D \macros %D {bodyfontsize} @@ -2776,27 +2381,37 @@ %D often not the way users specify the bodyfont size. Therefore %D we also store the normalized value. -\chardef\fontdigits=1 +\chardef\fontdigits=2 % was 1 + +% \def\normalizebodyfontsize#1\to#2% +% {\scratchdimen#1\relax +% \ifcase\fontdigits\advance\scratchdimen.5\points\fi +% \@EA\@EA\@EA\donormalizedbodyfontsize\@EA\WITHOUTPT\the\scratchdimen00\to#2} +% +% \def\donormalizedbodyfontsize#1.#2#3#4\to#5% \points ? +% {\edef#5% +% {#1% +% \ifcase\fontdigits\or +% \ifcase#2 \else.#2\fi % and not: \ifcase#2\else ... +% \else +% \ifcase#2#3 \else.#2\ifcase#3 \else#3\fi\fi % not: \ifcase#2#3\else ... +% \fi +% \s!pt}} \def\normalizebodyfontsize#1\to#2% - {\scratchdimen#1\relax - \ifcase\fontdigits\advance\scratchdimen.5\points\fi - \@EA\@EA\@EA\donormalizedbodyfontsize\@EA\WITHOUTPT\the\scratchdimen00\to#2} + {\scratchdimen\dimexpr#1+\ifcase\fontdigits.5\or.05\or.005\fi\points\relax + \@EA\@EA\@EA\donormalizedbodyfontsize\@EA\WITHOUTPT\the\scratchdimen000\to#2} -\def\donormalizedbodyfontsize#1.#2#3#4\to#5% \points ? - {\edef#5% +\def\donormalizedbodyfontsize#1.#2#3#4#5\to#6% \points ? + {\edef#6% not \ifcase#2\else due to \relax adding {#1% - \ifcase\fontdigits\or - \ifcase#2 \else.#2\fi % and not: \ifcase#2\else ... - \else - \ifcase#2#3 \else.#2\ifcase#3 \else#3\fi\fi % not: \ifcase#2#3\else ... + \ifcase\fontdigits + \or \ifcase#2 \else .#2\fi % 1 + \or \ifcase#2#3 \else .#2\ifcase#3 \else #3\fi\fi % 2 + \else \ifcase#2#3#4 \else .#2\ifcase#4 \ifcase#3 \else#3\fi \else#3#4\fi\fi % 3 \fi \s!pt}} -\normalizebodyfontsize\bodyfontsize\to\normalizedglobalbodyfontsize -\normalizebodyfontsize\bodyfontsize\to\normalizedlocalbodyfontsize -\normalizebodyfontsize\bodyfontsize\to\normalizedbodyfontsize - %D To be internationalized: \def\korpsgrootte {\bodyfontsize} @@ -3018,8 +2633,16 @@ \let\fontclass\empty \let\globalfontclass\fontclass +% \def\setcurrentfontclass#1% +% {\edef\fontclass{#1}} + +\def\registerfontclass#1% + {\letgvalue{\@fontclass@#1}\v!yes} % global ? + \def\setcurrentfontclass#1% - {\edef\fontclass{#1}} + {\ifcsname\@fontclass@#1\endcsname + \edef\fontclass{#1}% + \fi} \let\defaultfontstyle \c!rm \let\defaultfontalternative \c!tf @@ -3317,9 +2940,9 @@ %D \stoptyping \def\dodefinefontstyle[#1][#2]% - {\rawdoifinsetelse{#2}{\stylelist} + {\rawdoifinsetelse{#2}{\fontstylelist} {}%\debuggerinfo\m!fonts{unknown style #2}} - {\addtocommalist{#2}\stylelist + {\addtocommalist{#2}\fontstylelist \showmessage\m!fonts8{#2\space (#1)}}% % check kan hier \def\docommand##1% @@ -4389,7 +4012,7 @@ %D {bordermatrix} %D %D In \PLAIN\ \TEX\ the width of a parenthesis is stored in -%D the \DIMENSION\ \type{\p@renwd}. This value is derived from +%D the \DIMENSION\ \type{\mathparentwd}. This value is derived from %D the width of \type{\tenrm B}, so let's take care of it now: \let\normalbordermatrix=\bordermatrix @@ -4397,7 +4020,7 @@ \def\bordermatrix% {\bgroup \setbox0\hbox{\getvalue{\textface\c!mm\c!ex}B}% - \global\p@renwd\wd0\relax + \global\mathparentwd\wd0\relax \egroup \normalbordermatrix} @@ -4678,6 +4301,7 @@ %D So far. +\definefontstyle [\c!mm] [\c!mm] \definefontstyle [\c!rm,\v!roman,\v!serif,\v!regular] [\c!rm] \definefontstyle [\c!ss,\v!sansserif,\v!sans,\v!support] [\c!ss] \definefontstyle [\c!tt,\v!teletype,\v!type,\v!mono] [\c!tt] @@ -4800,24 +4424,6 @@ \def\fontvariant#1#2{\executeifdefined{\??fv#1#2}\empty} -% original: -% -% \def\variant[#1]% -% {\expanded{\definedfont -% [\truefontname{\fontstringA\fontstylesuffix\fontvariant\fontstringA{#1}} -% at \currentfontscale\bodyfontsize]}} -% -% \beginXETEX \font -% -% \def\variant[#1]% -% {\font\variantfont\truefontname{\fontstringA\fontstylesuffix\fontvariant\fontstringA{#1}} -% at \currentfontscale\bodyfontsize -% \variantfont} -% -% \endXETEX -% -% better - \def\dosetscaledfont {\checkrelativefontsize\fontstyle \scaledfont\currentfontscale\bodyfontsize @@ -4830,16 +4436,6 @@ at \scaledfont]}% \ignoreimplicitspaces} -\beginXETEX \font - - \unexpanded\def\variant[#1]% - {\dosetscaledfont - \font\variantfont\truefontname{\fontstringA\fontstylesuffix\fontvariant\fontstringA{#1}} - at \scaledfont - \variantfont} - -\endXETEX - \ifx\Var\undefined \let\Var\variant \fi %D By default we load the Computer Modern Roman fonts (but @@ -4847,7 +4443,7 @@ %D bodyfont. Sans serif and teletype are also available and %D can be called for by \type{\ss} and \type{\tt}. -\setupbodyfont [unk, rm] +% \setupbodyfont [unk, rm] %D Also needed is: @@ -4889,4 +4485,83 @@ \egroup\expandafter\firstoftwoarguments \fi} +%D New commands (not yet interfaced): + +\def\style[#1]% for inline usage, like \color + {\groupedcommand{\ifcsname#1\endcsname\csname#1\endcsname\else\definedfont[#1]\fi}{}} + +\def\startstyle[#1]% + {\begingroup + \ifcsname#1\endcsname\csname#1\endcsname\else\definedfont[#1]\fi} + +\def\stopstyle + {\endgroup} + +%D Still experimental (might even go away). + +% \definestylecollection[mine] + +% \definestyleinstance[mine][default][sorry] +% \definestyleinstance[mine][tt][bs][ttbs:\rm\sl] +% \definestyleinstance[mine][tt][bf][ttbf:\rm\sl] +% \definestyleinstance[mine][bf][\sl] +% \definestyleinstance[mine][sl][\tt] + +% {\bf test \mine test \sl test \mine test \bs oeps \mine oeps {\tt test \mine \bf test}} + +\definesystemvariable{sx} + +\def\definestylecollection + {\dosingleargument\dodefinestylecollection} + +\def\dodefinestylecollection[#1]% + {\iffirstargument + \unexpanded\setvalue{#1}{\styleinstance[#1]}% + \def\docommand##1% + {\def\dodocommand####1{\letbeundefined{\??sx##1:####1:\commalistelement}}% + \processcommacommand[\fontalternativelist,\s!default]\dodocommand}% + \processcommacommand[\fontstylelist,\s!default]\docommand + \fi} + +\def\definestyleinstance + {\doquadrupleargument\dodefinestyleinstance} + +\def\dodefinestyleinstance[#1][#2][#3][#4]% [name] [rm|ss|tt|..] [sl|bf|...] [whatever] + {\iffirstargument + \doifundefined{#1}{\definestylecollection[#1]}% + \fi + \iffourthargument + \setvalue{\??sx#1:#2:#3}{#4}% + \else\ifthirdargument + \setvalue{\??sx#1::#2}{#3}% + \else\ifsecondargument + \letvalue{\??sx#1::#2}\empty + \fi\fi\fi} + +\unexpanded\def\styleinstance[#1]% will be faster + {%\begingroup\expanded{\infofont[#1:\fontstyle:\fontalternative]}\endgroup + \executeifdefined{\??sx#1:\fontstyle:\fontalternative}% + {\executeifdefined{\??sx#1:\fontstyle:\s!default}% + {\executeifdefined{\??sx#1::\fontalternative} + {\getvalue {\??sx#1::\s!default}}}}} + +% \unexpanded\def\styleinstance[#1]% +% {\csname\??sx#1% +% \ifcsname:\fontstyle:\fontalternative\endcsname +% :\fontstyle:\fontalternative +% \else\ifcsname:\fontstyle:\s!default\endcsname +% :\fontstyle:\s!default +% \else\ifcsname::\fontalternative\endcsname +% ::\fontalternative +% \else\ifcsname::\s!default\endcsname +% ::\s!default +% \else +% % nothing, \relax +% \fi\fi\fi\fi +% \endcsname} + +%D \Compatibility with \MKIV: + +\def\somefontsize{\scaledfont} + \protect \endinput diff --git a/tex/context/base/font-ini.mkiv b/tex/context/base/font-ini.mkiv index 3e2e57145..4d0d92fc5 100644 --- a/tex/context/base/font-ini.mkiv +++ b/tex/context/base/font-ini.mkiv @@ -12,26 +12,64 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. +% simplification ... we no longer deal with specific mmtfa specifications -% \rm\bf --> \song -% \rm\it --> \kai -% \ss\it --> \kai -% \tt\bf --> \hei +% todo: always fontclass, then less testing -\writestatus{loading}{Context Font Macros (ini)} +% \starttext +% \definefontfeature[basekerned][default][mode=base] +% \definefontfeature[nodekerned][default][mode=node] +% \definefontfeature[nonekerned][default][mode=base,kern=no] +% \setupcolors[state=start] +% \startoverlay +% {\vbox{\red \definedfont[Serif*nonekerned at 12pt]\input tufte }} +% {\vbox{\blue \definedfont[Serif*basekerned at 12pt]\input tufte }} +% {\vbox{\green\definedfont[Serif*nodekerned at 12pt]\input tufte }} +% \stopoverlay +% \stoptext + +% \enabletrackers[otf.kerns] +% +% \definefontfeature[withkern][default][mode=node] +% \definefontfeature[nokern] [default][mode=node,kern=no] +% \definefontfeature[single] [default][mode=node,cpsp=yes] +% \definefontfeature[simple] [default][mode=node,cpsp=yes,kern=no] +% +% {\definedfont[Serif*default] [FGFGFGFGFGFGFGFGFGFGFGFGFG ABCDEFGHIJKLMNOPQRSTUVWXYZ] \par} +% {\definedfont[Serif*nokern] [FGFGFGFGFGFGFGFGFGFGFGFGFG ABCDEFGHIJKLMNOPQRSTUVWXYZ] \par} +% {\definedfont[Serif*single] [FGFGFGFGFGFGFGFGFGFGFGFGFG ABCDEFGHIJKLMNOPQRSTUVWXYZ] \par} +% {\definedfont[Serif*simple] [FGFGFGFGFGFGFGFGFGFGFGFGFG ABCDEFGHIJKLMNOPQRSTUVWXYZ] \par} + +% figure out why \fontbody is not expanded + +\writestatus{loading}{ConTeXt Font Macros / Initialization} \registerctxluafile{font-ini}{1.001} +\registerctxluafile{node-fnt}{1.001} % here \registerctxluafile{font-enc}{1.001} \registerctxluafile{font-map}{1.001} \registerctxluafile{font-syn}{1.001} +\registerctxluafile{font-log}{1.001} \registerctxluafile{font-tfm}{1.001} \registerctxluafile{font-afm}{1.001} -\registerctxluafile{font-otf}{1.001} +\registerctxluafile{font-cid}{1.001} % cid maps +\registerctxluafile{font-ott}{1.001} % otf tables +\registerctxluafile{font-otf}{1.001} % otf main +\registerctxluafile{font-otd}{1.001} % otf dynamics +\registerctxluafile{font-oti}{1.001} % otf initialization +\registerctxluafile{font-otb}{1.001} % otf main base +\registerctxluafile{font-otn}{1.001} % otf main node +\registerctxluafile{font-ota}{1.001} % otf analyzers +\registerctxluafile{font-otp}{1.001} % otf pack +\registerctxluafile{font-otc}{1.001} % otf context \registerctxluafile{font-vf} {1.001} \registerctxluafile{font-def}{1.001} +\registerctxluafile{font-ctx}{1.001} +\registerctxluafile{font-xtx}{1.001} \registerctxluafile{font-fbk}{1.001} \registerctxluafile{font-ext}{1.001} \registerctxluafile{font-pat}{1.001} +\registerctxluafile{font-chk}{1.001} \unprotect @@ -107,10 +145,10 @@ %D %D A couple of relatively new macros: -\newevery \everydefinedfont \relax % not ot be confused with \everydefinefont +% \newtoks \everydefinedfont % not ot be confused with \everydefinefont \def\dodefinedfont[#1]% - {\iffirstargument\definefont[thedefinedfont][#1]\fi + {\iffirstargument\definefont[thedefinedfont][#1]\fi % we can speed this one up \csname thedefinedfont\endcsname \the\everydefinedfont} @@ -132,158 +170,10 @@ \egroup\expandafter\secondoftwoarguments \fi} -%%% message 14 added - -\startmessages dutch library: fonts - title: korps - 1: codering -- - 2: variant -- wordt geladen - 3: onbekende variant -- - 4: korps -- is niet gedefinieerd - 5: stijl -- is niet gedefinieerd - 6: -- wordt geladen - 7: onbekend formaat -- - 8: stijl -- gedefinieerd -% 9: mapping -- is geladen - 10: onbekende font file -- - 14: korps -- is gedefinieerd (kan beter globaal plaatsvinden) -\stopmessages - -\startmessages english library: fonts - title: bodyfont - 1: coding -- - 2: variant -- is loaded - 3: unknown variant -- - 4: bodyfont -- is not defined - 5: style -- is not defined - 6: -- is loaded - 7: unknown format -- - 8: style -- defined -% 9: mapping -- is loaded - 10: unknown font file -- - 14: bodyfont -- is defined (can better be done global) -\stopmessages - -\startmessages german library: fonts - title: Fliesstext - 1: Kodierung -- - 2: Variante -- ist geladen - 3: Unbekannte Variante -- - 4: Fliesstext -- ist nicht definiert - 5: Stil -- ist nicht definiert - 6: -- ist geladen - 7: unbekanntes Format -- - 8: Stil -- definiert -% 9: Map -- ist geladen - 10: unbekanntes Font -- - 14: Fliesstext -- wurde definiert (besser waere globale Definition) -\stopmessages - -\startmessages czech library: fonts - title: zakladnifont - 1: kodovani -- - 2: varianta -- je nactena - 3: neznama varianta -- - 4: zakladni font -- neni definovan - 5: styl -- neni definovan - 6: -- je nacten - 7: neznamy format -- - 8: styl -- definovan -% 9: mapovani -- je nacteno - 10: neznamy font -- - 14: bodyfont -- is defined (can better be done global) -\stopmessages - -\startmessages italian library: fonts - title: font del corpo - 1: codifica -- - 2: variante -- caricata - 3: variante sconosciuta -- - 4: corpo del testo -- non definito - 5: stile -- non definito - 6: -- caricato - 7: formato sconosciuto -- - 8: stile -- definito -% 9: mappatura -- caricata - 10: file di font sconosciuto -- - 14: corpo del testo -- definito (sarebbe meglio globale) -\stopmessages - -\startmessages norwegian library: fonts - title: hovedfont - 1: koding -- - 2: variant -- er lest inn - 3: ukjent variant -- - 4: hovedfont -- er ikke definert - 5: stil -- er ikke definert - 6: -- er lest inn - 7: ukjent format -- - 8: stil -- definert -% 9: avbildning -- er lest inn - 10: ukjent fontfil -- - 14: bodyfont -- is defined (can better be done global) -\stopmessages - -\startmessages romanian library: fonts - title: corp de litere - 1: codificarea -- - 2: varianta -- este incarcata - 3: varianta necunoscuta -- - 4: corpul de litere -- nu este definit - 5: stilul -- nu este definit - 6: -- este incarcat - 7: format necunoscut -- - 8: stilul -- definit -% 9: maparea -- este incarcat - 10: fisier font necunoscut -- - 14: bodyfont -- is defined (can better be done global) -\stopmessages - -\startmessages french library: fonts - title: corps de texte - 1: encodage -- - 2: la variante -- est chargée - 3: variante -- inconnue - 4: policecorps -- n'est pas définie - 5: le style -- n'est pas défini - 6: -- est chargé - 7: format -- inconnu - 8: style -- défini -% 9: mapping -- is loaded - 10: fichier de police -- inconnu - 14: policecorps -- est défini (une définition globale pourrait être plus adéquat) -\stopmessages - -%D This module is one of the oldest modules of \CONTEXT. The -%D macros below evolved out of the \PLAIN\ \TEX\ macros and -%D therefore use a similar naming scheme (\type{\rm}, -%D \type{\bf}, etc). This module grew out of our needs. We -%D started with the \PLAIN\ \TEX\ definitions, generalized the -%D underlaying macros, and extended those to a level at which -%D probably no one will ever recognize them. -%D -%D In 2001 we ran into a couple of projects where more than -%D one combined set of fonts was involved in a document. To -%D make definitions more readable, as well as to overcome the -%D problem of ever growing file name lists, and also because -%D we needed to scale fonts relative to each other, the low -%D level implementation was partly rewritten. Global -%D font assignments, relative scaling, font classes and alike -%D were added then. At the same time some macros were made a -%D bit more readable, and math support was extended to the -%D larger sizes. -%D -%D One important characteristic of the font mechanism presented -%D here is the postponing of font loading. This makes it -%D possible to distribute \type{fmt} files without bothering -%D about the specific breed of \type{tfm} files. -%D -%D Another feature implemented here is the massive switching -%D from roman to {\ss sans serif}, {\tt teletype} or else. This -%D means one doesn't have to take care of all kind of relations -%D between fonts. -%D -%D \page[bigpreference] +%D For more detailed (and historic information) we refer to the file +%D \type {font-ini.mkii}. Here we have a much simplified lower level +%D implementation due to a different approach to math. Also the chapter +%D on fonts in the reference manual explains a lot. %D \macros %D {rm,ss,tt,hw,cg} @@ -314,56 +204,6 @@ %D \stoptable %D \stoplinecorrection %D -%D Anyone who feels the need, can define additional ones, like -%D -%D \startlinecorrection -%D \starttable[|l||] -%D \HL -%D \NC faxfont \NC \type{\ff} \NC\FR -%D \NC blackboard \NC \type{\bb} \NC\LR -%D \HL -%D \stoptable -%D \stoplinecorrection -%D -%D Or even -%D -%D \startlinecorrection -%D \starttable[|l||] -%D \HL -%D \NC hebrew \NC \type{\hb} \NC\SR -%D \HL -%D \stoptable -%D \stoplinecorrection -%D -%D Styles are grouped in font sets. At the moment there are -%D three main sets defined: -%D -%D \startlinecorrection -%D \starttable[|l|l||] -%D \HL -%D \NC Computer Modern Roman \NC Knuth \NC \type{cmr} \NC\FR -%D \NC Lucida Bright \NC Bigelow \& Holmes \NC \type{lbr} \NC\MR -%D \NC Standard Postscript Fonts \NC Adobe \NC \type{pos} \NC\LR -%D \HL -%D \stoptable -%D \stoplinecorrection -%D -%D There are also some Computer Modern Roman alternatives: -%D -%D \startlinecorrection -%D \starttable[|l|l||] -%D \HL -%D \NC Computer Modern Roman \NC Knuth \& Sauter \NC \type{sau} \NC\FR -%D \NC Euler fonts \NC Zapf \NC \type{eul} \NC\MR -%D \NC Computer Modern Concrete \NC Knuth \& Zapf \NC \type{con} \NC\LR -%D \HL -%D \stoptable -%D \stoplinecorrection -%D -%D All these definitions are ordered in files with names like -%D \type{font-cmr} and \type{font-pos}, where the last three -%D characters specify the name as known to \CONTEXT. -%D %D Within such a font set (\type{cmr}) and style (\type{\rm}) %D we can define a number of text font alternatives: %D @@ -380,43 +220,6 @@ %D \HL %D \stoptable %D \stoplinecorrection - -%D For old stylish Frans Goddijn we have: -%D -%D \startlinecorrection -%D \starttable[|l||] -%D \HL -%D \NC oldstyle \NC \type{\os} \NC\SR -%D \HL -%D \stoptable -%D \stoplinecorrection -%D -%D The availability of these alternatives depends on the -%D completeness of a font family and of course the definitions -%D in the font files. -%D -%D But let's not forget math. In addition to the previous \TEX\ -%D families (the mysterious \type{\fam}'s) we've got some more: -%D -%D \startlinecorrection -%D \starttable[|l||] -%D \HL -%D \NC Math Roman \NC \type{\mr} \NC\FR -%D \NC Math Italic \NC \type{\mi} \NC\MR -%D \NC Math Symbol \NC \type{\sy} \NC\MR -%D \NC Math Extra \NC \type{\ex} \NC\MR -%D \NC Math A \NC \type{\ma} \NC\MR -%D \NC Math B \NC \type{\mb} \NC\MR -%D \NC Math C \NC \type{\mc} \NC\LR -%D \HL -%D \stoptable -%D \stoplinecorrection -%D -%D Users can call for specific fonts in many ways. Switches to -%D other typefaces, like the switch from normal to bold, are as -%D intuitive as possible, which means that all dependant fonts -%D also switch. One can imagine that this takes quite some -%D processing time. %D %D Internally fonts are stored as combination of size, style %D and alternative, e.g. \type{12pt}+\type{\ss}+\type{\bf}. @@ -514,7 +317,7 @@ %D And compare $\rm \scriptstyle THIS$ with the slightly larger %D \cap{THIS}: \ruledhbox{$\rm \scriptstyle scriptstyle: THIS$} %D or \ruledhbox{\cap{x style: THIS}} makes a big difference. - +%D %D The \type{x..d} sizes should be used grouped. If you %D don't group them, i.e. call them in a row, \CONTEXT\ will %D not be able to sort out your intention (\type {x} inside @@ -592,282 +395,8 @@ %D \NC \NR %D \HL %D \stoptabulate - -%D \macros -%D {mf} -%D -%D Math fonts are a species in their own. They are tightly -%D hooked into smaller and even smaller ones of similar breed -%D to form a tight family. Let's first see how these are -%D related: -%D -%D \startbuffer -%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+ \bi x^2 =\rm 6x^2$ -%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+{\bi x^2}=\rm 6x^2$ -%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+ \bi x^2 =\tf 6x^2$ -%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+{\bi x^2}=\tf 6x^2$ -%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+ \bi x^2 =\bf 6x^2$ -%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+{\bi x^2}=\bf 6x^2$ -%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+ \bi x^2 =\sl 6x^2$ -%D $\tf x^2+\bf x^2+\sl x^2+\it x^2+\bs x^2+{\bi x^2}=\sl 6x^2$ -%D \stopbuffer -%D -%D \typebuffer -%D -%D Gives both an expected and unexpected result: -%D -%D \startvoorbeeld -%D \startlines -%D \getbuffer -%D \stoplines -%D \stopvoorbeeld -%D -%D We see here that the character shapes change accordingly to -%D the current family, but that the symbols are always typeset -%D in the font assigned to \type{\fam0}. -%D -%D \startbuffer -%D $\tf\mf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = 6x^2$ -%D $\bf\mf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = 6x^2$ -%D $\sl\mf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = 6x^2$ -%D $\bs\mf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = 6x^2$ -%D $\it\mf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = 6x^2$ -%D $\bi\mf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = 6x^2$ -%D \stopbuffer -%D -%D \startvoorbeeld -%D \startlines -%D \getbuffer -%D \stoplines -%D \stopvoorbeeld -%D -%D In this example we see a new command \type{\mf} surface -%D which means as much as {\em math font}. This commands -%D reactivates the last font alternative and therefore equals -%D \type{\bf}, \type{\sl} etc. but by default it equals -%D \type{\tf}: - -\unexpanded\def\mf - {\dodosetmathfont\fontalternative - \csname\fontalternative\endcsname} - -%D The previous example was typeset saying: -%D -%D \typebuffer -%D -%D Beware: the exact location of \type{\mf} is not that -%D important, we could as well has said -%D -%D \startbuffer -%D $\bf x^2 + x^2 + x^2 + x^2 + x^2 + x^2 = \mf 6x^2$ -%D \stopbuffer -%D -%D \typebuffer -%D -%D This is due to the way \TEX\ handles fonts in math mode. %D -%D Of course we'll have to redefine \type{\mf} every time we -%D change the current \type{\fam}. - -%D \macros -%D {mbox,enablembox,mathop} -%D -%D Now how can we put this to use? Will the next sequence -%D give the desired result? -%D -%D \startbuffer -%D $\bf x^2 + \hbox{\mf whatever} + \sin(2x)$ -%D \stopbuffer -%D -%D \typebuffer -%D -%D It won't! -%D -%D \startvoorbeeld -%D \let\mathop=\normalmathop \getbuffer -%D \stopvoorbeeld -%D -%D The reason for this is that \type{\sin} is defined as: -%D -%D \starttyping -%D \def\sin{\mathop{\rm sin}\nolimits} -%D \stoptyping -%D -%D We can fix this by defining - -\let\normalmathop\mathop - -\unexpanded\def\mathop - {\normalmathop - \bgroup - \let\rm\mf - \let\next=} - -%D We can fix arbitrary horizontal boxes by redefining the -%D \TEX\ primitive \type{\hbox}: -%D -%D \starttyping -%D \let\normalhbox=\hbox -%D -%D \def\hbox{\ifmmode\mbox\else\normalhbox\fi} -%D \stoptyping -%D -%D with -%D -%D \starttyping -%D \def\mbox#1#% -%D {\normalhbox#1\bgroup\mf\let\next=} -%D \stoptyping -%D -%D or more robust, that is, also accepting \type{\hbox\bgroup}: -%D -%D \starttyping -%D \def\mbox% -%D {\normalhbox\bgroup\mf -%D \dowithnextbox{\flushnextbox\egroup}% -%D \normalhbox} -%D \stoptyping -%D -%D And now: -%D -%D \startbuffer -%D $\bf x^2 + \hbox{whatever} + \sin(2x)$ -%D \stopbuffer -%D -%D \typebuffer -%D -%D Indeed gives: -%D -%D \startvoorbeeld -%D \enablembox\getbuffer -%D \stopvoorbeeld -%D -%D But, do we want this kind of trickery to be activated? No, -%D simply because we cannot be sure of incompatibilities, -%D although for instance unboxing goes ok. Therefore we -%D introduce: - -% best can go to math-ini and make \mf a hook then - -% better use \dowithnextboxcontent - -\def\normalmbox - {\normalhbox\bgroup\mf - \dowithnextbox{\flushnextbox\egroup}\normalhbox} - -% to test: -% -% \def\normalmbox -% {\dowithnextboxcontent\mf\flushnextbox\normalhbox} - -\def\mbox - {\ifmmode\normalmbox\else\normalhbox\fi} - -\def\enablembox - {\appendtoks - \ifx\normalhbox\undefined\let\normalhbox\hbox\fi - \let\hbox\mbox - \to\everymathematics} - -%D So in fact one can enable this feature if needed. I would say: -%D go along, but use grouping if needed! - -%D \macros -%D {mrfam,mifam,syfam,exfam, -%D bsfam,bifam,scfam,tffam, -%D mafam,mbfam,msfam} -%D -%D After this short mathematical excursion, we enter the world -%D of fonts and fontswitching. We start with something very -%D \TEX: \type{\fam} specified font families. \TEX\ uses -%D families for managing fonts in math mode. Such a family has -%D three members: text, script and scriptscript: $x^{y^z}$. In -%D \CONTEXT\ we take a bit different approach than \PLAIN\ -%D \TEX\ does. \PLAIN\ \TEX\ needs at least four families for -%D typesetting math. We use those but give them symbolic names. - -\chardef\mrfam = 0 % (Plain TeX) Math Roman -\chardef\mifam = 1 % (Plain TeX) Math Italic -\chardef\syfam = 2 % (Plain TeX) Math Symbol -\chardef\exfam = 3 % (Plain TeX) Math Extra - -%D \PLAIN\ \TEX\ also defines families for {\it italic}, {\sl -%D slanted} and {\bf bold} typefaces, so we don't have to -%D define them here. - -\ifx\itfam\undefined - -\chardef\itfam = 4 % (Plain TeX) Italic -\chardef\slfam = 5 % (Plain TeX) Slanted -\chardef\bffam = 6 % (Plain TeX) Boldface - -\fi - -%D Family~7 in \PLAIN\ \TEX\ is not used in \CONTEXT, because -%D we do massive switches from roman to sans serif, teletype or -%D other faces. - -\ifx\ttfam\undefined - \chardef\ttfam = 7 % (Plain TeX) can be reused! -\fi - -%D We define ourselves some more families for {\bs bold -%D slanted}, {\bi bold italic} and {\sc Small Caps}, so -%D we can use them in math mode too. Instead of separate -%D families for {\ss sans serif} and \type{teletype} we use the -%D more general \type{\tffam}, which stands for typeface. - -\chardef\bsfam = 8 % (ConTeXt) BoldSlanted -\chardef\bifam = 9 % (ConTeXt) BoldItalic -\chardef\scfam = 10 % (ConTeXt) SmallCaps -\chardef\tffam = 11 % (ConTeXt) TypeFace - -%D Because Taco needs a few more math families, we reuse -%D family~7 for all those typefaces that have no related -%D family, and therefore are grouped into one. - -\chardef\nnfam = 7 % (ReUsed) NoName - -%D Normally \type{\mrfam} equals \type{\tffam}, but a more -%D distinctive alternatives are possible, for instance the -%D Euler and Concrete Typefaces. -%D -%D After having defined all those in nature non||mathematical -%D families, we define ourselves some real math ones. These are -%D needed for the \AMS\ Symbol Fonts and Extended Lucida -%D Bright. - -\chardef\mafam = 12 % (ConTeXt) Math A Fam (AmsTeX A) -\chardef\mbfam = 13 % (ConTeXt) Math B Fam (AmsTeX B) -\chardef\mcfam = 14 % (ConTeXt) Math C Fam (MathTime) -\chardef\mdfam = 15 % (ConTeXt) Math D Fam (MathTime) - -%D Because there are 16~families and because \type{\ttfam} -%D is reused, at the moment we have no so many families -%D left. By default, we map any newly defined family on the -%D last one (F). - -\def\newfam#1{\chardef#1=15 } - -%D This hack is also needed because in \ETEX\ we are going -%D to reuse the \type {\newfam} allocation counter. - -%D To ease the support of font packages, we als define -%D shortcuts to these familynames. This is necessary because -%D the family names are in fact \type{\chardef}'s, which means -%D that we're dealing with numbers (one can check this by -%D applying \type{\showthe} and \type{\show}). In the -%D specification of math symbols however we need hexadecimal -%D numbers, so we have to convert the \type{\fam}'s value. - -\edef\hexmrfam {\hexnumber\mrfam} \edef\hexbsfam {\hexnumber\bsfam} -\edef\hexmifam {\hexnumber\mifam} \edef\hexbifam {\hexnumber\bifam} -\edef\hexsyfam {\hexnumber\syfam} \edef\hexscfam {\hexnumber\scfam} -\edef\hexexfam {\hexnumber\exfam} \edef\hextffam {\hexnumber\tffam} -\edef\hexitfam {\hexnumber\itfam} \edef\hexmafam {\hexnumber\mafam} -\edef\hexslfam {\hexnumber\slfam} \edef\hexmbfam {\hexnumber\mbfam} -\edef\hexbffam {\hexnumber\bffam} \edef\hexmcfam {\hexnumber\mcfam} -\edef\hexnnfam {\hexnumber\nnfam} \edef\hexmdfam {\hexnumber\mdfam} +%D Remark: math support has changed a bit. %D \macros %D {uchar} @@ -886,244 +415,122 @@ \def\@shortstyle@ {@f@sh@} % short style prefix (rm etc) \def\@letter@ {@f@le@} % first alternative typeface \def\@noletter@ {@f@no@} % second alternative typeface +\def\@fontclass@ {@f@cl@} % fontclass -%D The families can be grouped into math specific ones and -%D more text related families, although text ones can be -%D mapped onto the math ones to get for instance bold math. +%D \macros +%D {fontclass, defaultfontclass} %D -%D Both groups of families are handles by a couple of token -%D list tagged as strategies. This implementation makes -%D implementing extensions more comfortable. +%D The fontclass model was introduced a while after we implement +%D the basic font model and at that time we still defaulted to +%D no model at all. Nowadays we default to the \type {modern} +%D fontclass. + +\let\fontclass \empty +\let\defaultfontclass\empty + +%D \macros +%D {textonly} +%D +%D Traditionally math has a big impact on font definitions, mainly +%D because we need to define alphabet variants using families and +%D fonts. This means that one can easily get 10 fonts loaded per +%D math size. In \MKIV\ we use a different approach: one family +%D which has either a virtual font made of traditional fonts, or +%D an \OPENTYPE\ font that has it all. +%D +%D We currently use only one math family but in the future we +%D might consider using a second one for bold math. For the +%D moment we keep the \MKII\ method of using a token register +%D for definitions but we already dropped the text and symbols +%D ones since they now live in the same family. -\newtoks \textstrategies \newtoks \mathstrategies -\newtoks \symbstrategies \newif\ifsynchronizemathfonts \synchronizemathfontstrue -\def\synchronizetext % stylish text in mmode - {\ifsynchronizemathfonts\the\textstrategies\fi} % \if...\fam\minusone\fi} - \def\synchronizemath % math stuff in mmode - {\ifsynchronizemathfonts\the\mathstrategies\fi} % \if...\fam\minusone\fi} + {\ifsynchronizemathfonts\the\mathstrategies\fi} -\def\synchronizesymb % stylish math stuff in mmode - {\ifsynchronizemathfonts\the\symbstrategies\fi} % \if...\fam\minusone\fi} +\def\textonly{\synchronizemathfontsfalse} % document this -%D By not setting the family we can append a font switch to \type -%D {\everymath}. On the other hand, one never knows in what family -%D state the strategies brought us. -%D -%D \starttyping -%D {\bfa $\the\fam$} {\bfa \everymath{} $\the\fam$} -%D \stoptyping +%D The main math font definer. We have removed some optimized +%D code simply because we now always have a fontclass. We could +%D check for fontclass being default or empty and save a few +%D tests but it does not help us when no math is defined. -%D \macros -%D {textonly} -%D -%D We can inhibit this slow||downer with: +\chardef\mrfam\zerocount % math regular +\chardef\mbfam\zerocount % math bold -\def\textonly{\synchronizemathfontsfalse} % document this +\def\mathtextsuffix {-text} +\def\mathscriptsuffix {-script} +\def\mathscriptscriptsuffix{-scriptscript} -\appendtoks - \dosettextfamily\c!tf - \dosettextfamily\c!bf - \dosettextfamily\c!sl - \dosettextfamily\c!it - \dosettextfamily\c!bs - \dosettextfamily\c!bi - \dosettextfamily\c!sc -\to \textstrategies - -\def\dosettextfamily#1% better pass fontbody to dodoset - {\let\savedfontbody\fontbody - \let\fontfamily#1% - \let\fontbody\scriptscriptface\dodosettextfamily\scriptscriptfont - \let\fontbody\scriptface \dodosettextfamily \scriptfont - \let\fontbody\textface \dodosettextfamily \textfont - \let\fontbody\savedfontbody} - -% \def\s!nullfont{nullfont} - -\def\dodosettextfamily - {\ifx\fontclass\empty - \@EA\dodosettextfamilyA - \else - \@EA\dodosettextfamilyB - \fi} +% \let\mathsizesuffix\empty -\def\dodosettextfamilyA#1% - {\ifcsname \fontbody\c!mm\fontfamily\fontsize\endcsname \autofontsizefalse - \csname \fontbody\c!mm\fontfamily\fontsize\endcsname \else - \ifcsname \fontbody\c!mm\fontfamily\endcsname \autofontsizetrue - \csname \fontbody\c!mm\fontfamily\endcsname \else - \ifcsname \fontbody\c!rm\fontfamily\fontsize\endcsname \autofontsizefalse - \csname \fontbody\c!rm\fontfamily\fontsize\endcsname \else - \ifcsname \fontbody\c!rm\fontfamily\endcsname \autofontsizetrue - \csname \fontbody\c!rm\fontfamily\endcsname \else - \nullfont \autofontsizetrue - \fi\fi\fi\fi - #1\csname\fontfamily\s!fam\endcsname\font} - -\def\dodosettextfamilyB#1% - {\ifcsname\fontclass\fontbody\c!mm\fontfamily\fontsize\endcsname \autofontsizefalse - \csname\fontclass\fontbody\c!mm\fontfamily\fontsize\endcsname \else - \ifcsname\fontclass\fontbody\c!mm\fontfamily\endcsname \autofontsizetrue - \csname\fontclass\fontbody\c!mm\fontfamily\endcsname \else - \ifcsname\fontclass\fontbody\c!rm\fontfamily\fontsize\endcsname \autofontsizefalse - \csname\fontclass\fontbody\c!rm\fontfamily\fontsize\endcsname \else - \ifcsname\fontclass\fontbody\c!rm\fontfamily\endcsname \autofontsizetrue - \csname\fontclass\fontbody\c!rm\fontfamily\endcsname \else - \dodosettextfamilyA#1% - \fi\fi\fi\fi - #1\csname\fontfamily\s!fam\endcsname\font} - -\def\mrfallback{\c!rm\c!tf} +\chardef\currentmathsize\zerocount -\appendtoks - \dosetmathfamily\mrfam\textface\scriptface\scriptscriptface\c!mr\mrfallback - \dosetmathfamily\mifam\textface\scriptface\scriptscriptface\c!mi\empty - \dosetmathfamily\syfam\textface\scriptface\scriptscriptface\c!sy\empty - \dosetmathfamily\exfam\textface\textface \textface \c!ex\empty - \dosetmathfamily\mafam\textface\scriptface\scriptscriptface\c!ma\empty - \dosetmathfamily\mbfam\textface\scriptface\scriptscriptface\c!mb\empty - \dosetmathfamily\mcfam\textface\scriptface\scriptscriptface\c!mc\empty -% \dosetmathfamily\mdfam\textface\scriptface\scriptscriptface\c!md\empty - \dosetmathfamily\nnfam\textface\scriptface\scriptscriptface\c!nn\empty -\to \mathstrategies +\def\mathsizesuffix{\ifcase\currentmathsize\or\mathtextsuffix\or\mathscriptscriptsuffix\or\mathscriptsuffix\fi} -\appendtoks - \dosetskewchar\mifam\defaultskewcharmi % implemented later on - \dosetskewchar\syfam\defaultskewcharsy % implemented later on -\to \mathstrategies +\def\dodosetmathfamily#1#2% + {\ifcsname\fontclass \fontbody\c!mm\fontfamily\fontsize\the\currentmathsize\endcsname \autofontsizefalse + \csname\fontclass \fontbody\c!mm\fontfamily\fontsize\the\currentmathsize\endcsname \else + \ifcsname\fontclass \fontbody\c!mm\fontfamily \the\currentmathsize\endcsname \autofontsizetrue + \csname\fontclass \fontbody\c!mm\fontfamily \the\currentmathsize\endcsname \else + \dodosetmathfamilyx#1#2% + \fi\fi + #1#2\font} -\def\dosetmathfamily#1#2#3#4#5#6% - {\let\savedfontbody\fontbody % op hoger plan - \let\fontfamily#5% - \let\backfamily#6% - \let\fontbody #4\dodosetmathfamily\scriptscriptfont#1% - \let\fontbody #3\dodosetmathfamily \scriptfont#1% - \let\fontbody #2\dodosetmathfamily \textfont#1% - \let\fontbody\savedfontbody} - -\def\dodosetmathfamily - {\ifx\fontclass\empty - \@EA\dodosetmathfamilyA - \else - \@EA\dodosetmathfamilyB - \fi} +\def\dodosetmathfamilyx#1#2% + {\ifcsname\defaultfontclass\fontbody\c!mm\fontfamily\fontsize\the\currentmathsize\endcsname \autofontsizefalse + \csname\defaultfontclass\fontbody\c!mm\fontfamily\fontsize\the\currentmathsize\endcsname \else + \ifcsname\defaultfontclass\fontbody\c!mm\fontfamily \the\currentmathsize\endcsname \autofontsizetrue + \csname\defaultfontclass\fontbody\c!mm\fontfamily \the\currentmathsize\endcsname \else + \dodosetmathfamilyxx#1#2% + \fi\fi} -\def\dodosetmathfamilyA#1#2% - {\ifcsname \fontbody\c!mm\fontfamily\fontsize\endcsname \autofontsizefalse - \csname \fontbody\c!mm\fontfamily\fontsize\endcsname \else - \ifcsname \fontbody\c!mm\fontfamily \endcsname \autofontsizetrue - \csname \fontbody\c!mm\fontfamily \endcsname \else - \ifcsname \fontbody \backfamily\fontsize\endcsname \autofontsizefalse - \csname \fontbody \backfamily\fontsize\endcsname \else - \ifcsname \fontbody \backfamily \endcsname \autofontsizetrue - \csname \fontbody \backfamily \endcsname \else - \nullfont \autofontsizetrue - \fi\fi\fi\fi - #1#2\font} +\def\dodosetmathfamilyxx#1#2% + {\ifcsname \fontbody\c!mm\fontfamily\fontsize\the\currentmathsize\endcsname \autofontsizefalse + \csname \fontbody\c!mm\fontfamily\fontsize\the\currentmathsize\endcsname \else + \ifcsname \fontbody\c!mm\fontfamily \the\currentmathsize\endcsname \autofontsizetrue + \csname \fontbody\c!mm\fontfamily \the\currentmathsize\endcsname \else + \nullfont \autofontsizetrue + \fi\fi} -\def\dodosetmathfamilyB#1#2% - {\ifcsname\fontclass\fontbody\c!mm\fontfamily\fontsize\endcsname \autofontsizefalse - \csname\fontclass\fontbody\c!mm\fontfamily\fontsize\endcsname \else - \ifcsname\fontclass\fontbody\c!mm\fontfamily \endcsname \autofontsizetrue - \csname\fontclass\fontbody\c!mm\fontfamily \endcsname \else - \ifcsname\fontclass\fontbody \backfamily\fontsize\endcsname \autofontsizefalse - \csname\fontclass\fontbody \backfamily\fontsize\endcsname \else - \ifcsname\fontclass\fontbody \backfamily \endcsname \autofontsizetrue - \csname\fontclass\fontbody \backfamily \endcsname \else - \dodosetmathfamilyA#1#2% - \fi\fi\fi\fi - #1#2\font} +\def\dosetmathfamily#1#2% + {\let\savedfontbody\fontbody % op hoger plan + \let\fontfamily#2% + \let\currentmathsize\plusthree\let\fontbody\scriptscriptface\dodosetmathfamily\scriptscriptfont#1% + \let\currentmathsize\plustwo \let\fontbody\scriptface \dodosetmathfamily\scriptfont #1% + \let\currentmathsize\plusone \let\fontbody\textface \dodosetmathfamily\textfont #1% + \let\currentmathsize\zerocount + \let\fontbody\savedfontbody + \autofontsizefalse} \appendtoks - \dosetsymbfamily\mrfam\textface\scriptface\scriptscriptface\c!mr - \dosetsymbfamily\mifam\textface\scriptface\scriptscriptface\c!mi - \dosetsymbfamily\syfam\textface\scriptface\scriptscriptface\c!sy - \dosetsymbfamily\exfam\textface\textface \textface \c!ex - \dosetsymbfamily\mafam\textface\scriptface\scriptscriptface\c!ma - \dosetsymbfamily\mbfam\textface\scriptface\scriptscriptface\c!mb - \dosetsymbfamily\mcfam\textface\scriptface\scriptscriptface\c!mc -% \dosetsymbfamily\mdfam\textface\scriptface\scriptscriptface\c!md % also ? -\to \symbstrategies - -\def\dosetsymbfamily#1#2#3#4#5% - {\let\savedfontbody\fontbody - \let\fontfamily#5% - \let\fontbody #4\dodosetsymbfamily\scriptscriptfont#1% - \let\fontbody #3\dodosetsymbfamily \scriptfont#1% - \let\fontbody #2\dodosetsymbfamily \textfont#1% - \let\fontbody\savedfontbody} - -\def\dodosetsymbfamily#1#2% - {\ifcsname\fontclass\fontbody\c!mm\fontfamily\fontalternative\fontsize\endcsname - \csname\fontclass\fontbody\c!mm\fontfamily\fontalternative\fontsize\endcsname - #1#2\font - \else\ifcsname\fontbody\c!mm\fontfamily\fontalternative\fontsize\endcsname - \csname\fontbody\c!mm\fontfamily\fontalternative\fontsize\endcsname - #1#2\font - \fi\fi} + \dosetmathfamily\mrfam\c!mr + %\dosetmathfamily\mbfam\c!mb % some day, only when defined, else equivalent to 0 +\to \mathstrategies %D All used styles, like rm, ss and tt, are saved in a comma %D separated list. Appart from practical limitations one can %D define as many styles as needed. -\let\stylelist=\empty - -\def\fontsizelist{\s!text,\s!script,\s!scriptscript,\c!x,\c!xx,\c!big,\c!small} +\def\fontrelativesizelist{\s!text,\s!script,\s!scriptscript,\c!x,\c!xx,\c!big,\c!small} -%D \macros -%D {magfactor,magfactorhalf} -%D %D There are several ways to specify a font. Three of them are %D pure \TeX\ ones, the fourth one is new: %D %D \starttyping %D \font\name=cmr12 %D \font\name=cmr12 at 10pt -%D \font\name=cmr12 scaled \magstep2 +%D \font\name=cmr12 scaled 2 %D \font\name=cmr12 sa 1.440 %D \stoptyping %D %D The non||\TEX\ alternative \type{sa} stands for {\em scaled %D at}. This means as much as: scale the bodyfontsize with this -%D factor. The value 1.440 in this example is derived -%D from the \type{\magstep}'s as mentioned in -%D \in{table}[tab:magstep]. We therefore introduce -%D \type{\magfactor} as an alternative for \type{\magstep}. -%D -%D \placetable[here][tab:magstep] -%D {Factors to be used with \type{sa.}} -%D \starttable[|c|c|c|] -%D \HL -%D \NC \bf magstep \NC \bf equivalent \NC \bf factor \NC\SR -%D \HL -%D \NC 1 \NC \type{\magfactor1} \NC 1.200 \NC\FR -%D \NC 2 \NC \type{\magfactor2} \NC 1.440 \NC\MR -%D \NC 3 \NC \type{\magfactor3} \NC 1.728 \NC\MR -%D \NC 4 \NC \type{\magfactor4} \NC 2.074 \NC\MR -%D \NC 5 \NC \type{\magfactor5} \NC 2.488 \NC\LR -%D \HL -%D \stoptable - -\def\magstep#1% \relax removed, otherwise space after it sticks, else added - {\ifcase#1 \@m\or1200\or1440\or1728\or2074\or2488\or\@m\fi} - -\def\magstephalf - {1095} - -\def\magfactor#1% - {\ifcase#1 1.000\or1.200\or1.440\or1.728\or2.074\or2.488\or1\fi} - -\def\magfactorhalf - {1.095} - -%D These macros enable the use of definitions like \type{sa -%D \magfactor3} which saves us both (mis|)|calculations and -%D potential mistypings. +%D factor. The scaled option is not that useful as one needs to +%D know the design size. %D %D Because \type {sa} (scaled at) and \type {mo} (mapped on) %D are not low level \TEX\ supported alternatives, we have to @@ -1146,9 +553,25 @@ {\edef\relativefontsize {\ifcsname\fontclass#1\s!rscale\endcsname \csname\fontclass#1\s!rscale\endcsname + \else\ifcsname\defaultfontclass#1\s!rscale\endcsname + \csname\defaultfontclass#1\s!rscale\endcsname \else \defaultrelativefontsize - \fi}} + \fi\fi}} + +% \letvalue{\s!default\s!rscale}\defaultrelativefontsize +% +% \def\checkrelativefontsize#1% +% {\edef\relativefontsize +% {\csname +% \ifcsname\fontclass#1\s!rscale\endcsname +% \fontclass#1% +% \else\ifcsname\defaultfontclass#1\s!rscale\endcsname +% \defaultfontclass#1% +% \else +% \s!default +% \fi\fi +% \s!rscale\endcsname}} %D We also save: @@ -1159,6 +582,38 @@ {\executeifdefined{\fontclass\c!mm\s!text}\empty} %D Scaling macros: +%D +%D This system is somewhat complicated by two (possible conflicting) +%D demands: +%D +%D \startitemize +%D \item We support wildcards like \type {sa *} which will adapt +%D to the current size. This is also the default specification. +%D \item We support named scales like \type {sa d}; beware: \type +%D {x} and \type {xx} are valid scales but they are not alway +%D the same as the ones used in for instance \type {\bfx} because +%D there the sized come from the bodyfont environment. In the +%D future there maybe a switch that also honors the environment +%D in named scales. +%D \stopitemize + +%D Keep in mind that the smaller sizes are just for text super and +%D subscripts while larger sizes can be used in titles where for +%D instance math follows the size. + +% b:x{\definedfont[SerifBold sa b]x}{\bfb x $x^x$}\par +% 1:x{\definedfont[SerifBold sa 1]x}{\bf x $x^x$}\par +% x:x{\definedfont[SerifBold sa x]x}{\bfx x $x^x$}\par +% xx:x{\definedfont[SerifBold sa xx]x}{\bfxx x $x^x$}\par +% +% *:x{\definedfont[Serif sa *]x}\par +% 1:x{\definedfont[Serif sa 1]x}\par +% 2:x{\definedfont[Serif sa 2]x}\par +% 3:x{\definedfont[Serif sa 3]x}\par +% 4:x{\definedfont[Serif sa 4]x}\par +% 5:x{\definedfont[Serif sa 5]x}\par +% +% {\definedfont[cmbx10 at 10pt]x\definedfont[cmbx8 at 10pt]x} \def\safontscale{\number\dimexpr\localabsolutefontsize\relax} \def\mofontscale{\number\dimexpr\setmappedfontsize\localabsolutefontsize\relax} @@ -1171,29 +626,39 @@ \newdimen\scaledfontsize \newtoks\everydefinefont +\def\currentfontbodysize + {\ifcsname\??ft\s!default\somefontsize\endcsname + \csname\??ft\s!default\somefontsize\endcsname + \else + \somefontsize + \fi} + \def\lowleveldefinefont#1#2% #2 = cs - {\ctxlua{fonts.define.command_1("\luaescapestring{#1}")}% the escapestring catches at \somedimen + {% + \ctxlua{fonts.define.command_1("\luaescapestring{#1}")}% the escapestring catches at \somedimen % sets \scaledfontmode and \somefontname and \somefontsize \ifcase\scaledfontmode\relax - % none + % none, avoid the designsize if possible \scaledfontsize-1000\scaledpoint \or % at \scaledfontsize\somefontsize \or % sa - \scaledfontsize\localabsolutefontsize - \scaledfontsize\ifcsname\??ft\s!default\somefontsize\endcsname\csname\??ft\s!default\somefontsize\endcsname\else\somefontsize\fi\scaledfontsize + \scaledfontsize\localabsolutefontsize\relax + \scaledfontsize\currentfontbodysize\scaledfontsize \or % mo \scaledfontsize\setmappedfontsize\localabsolutefontsize - \scaledfontsize\ifcsname\??ft\s!default\somefontsize\endcsname\csname\??ft\s!default\somefontsize\endcsname\else\somefontsize\fi\scaledfontsize + \scaledfontsize\currentfontbodysize\scaledfontsize \or - % scaled + % scaled, don't use this one as it's unpredictable \scaledfontsize-\somefontsize\scaledpoint \fi \scaledfontsize\localrelativefontsize\scaledfontsize - \ifautofontsize\scaledfontsize\currentfontbodyscale\scaledfontsize\fi + \ifautofontsize + \scaledfontsize\currentfontbodyscale\scaledfontsize + \fi \edef\somefontspec{at \number\scaledfontsize sp}% \edef\somefontfile{\truefontname\somefontname}% \ifx\somefontfile\s!unknown @@ -1209,7 +674,9 @@ "\@@fontclassfeatures", "\@@fontfeatures", "\@@fontclassfallbacks", - "\@@fontfallbacks" + "\@@fontfallbacks", + \number\currentmathsize, + \number\dimexpr\textface\relax )}% \edef\somefontspec{at \somefontsize}% we need the resolved designsize (for fallbacks) \expandafter\let\expandafter\lastrawfontcall\csname#2\endcsname @@ -1219,12 +686,48 @@ {\edef\@@fontclassfeatures {\ifcsname\fontclass\s!features \endcsname\csname\fontclass\s!features \endcsname\fi}% \edef\@@fontclassfallbacks{\ifcsname\fontclass\s!fallbacks\endcsname\csname\fontclass\s!fallbacks\endcsname\fi}} +% resolve + +\def\@@thefeaturesyes#1% + {\ifcsname\??ff\fontclass#1\s!features \endcsname\@EA\let\@EA\@@fontfeatures \csname\??ff\fontclass#1\s!features \endcsname\else + \ifcsname\??ff #1\s!features \endcsname\@EA\let\@EA\@@fontfeatures \csname\??ff #1\s!features \endcsname\else + \ifcsname\??ff\fontclass #1\endcsname\@EA \@@thefeaturesyes \csname\??ff\fontclass #1\endcsname\else + \ifcsname\??ff #1\endcsname\@EA \@@thefeaturesyes \csname\??ff #1\endcsname\else + \let \@@fontfeatures \empty \fi\fi\fi\fi} + +\def\@@thefallbacksyes#1% + {\ifcsname\??ff\fontclass#1\s!fallbacks\endcsname\@EA\let\@EA\@@fontfallbacks \csname\??ff\fontclass#1\s!fallbacks\endcsname\else + \ifcsname\??ff #1\s!fallbacks\endcsname\@EA\let\@EA\@@fontfallbacks \csname\??ff #1\s!fallbacks\endcsname\else + \ifcsname\??ff\fontclass #1\endcsname\@EA \@@thefallbacksyes\csname\??ff\fontclass #1\endcsname\else + \ifcsname\??ff #1\endcsname\@EA \@@thefallbacksyes\csname\??ff #1\endcsname\else + \let \@@fontfallbacks \empty \fi\fi\fi\fi} + +\def\@@thefeaturesnop#1% + {\ifcsname\??ff#1\s!features \endcsname\@EA\let\@EA\@@fontfeatures \csname\??ff#1\s!features \endcsname\else + \ifcsname\??ff #1\endcsname\@EA \@@thefeaturesnop \csname\??ff #1\endcsname\else + \let \@@fontfeatures \empty \fi\fi} + +\def\@@thefallbacksnop#1% + {\ifcsname\??ff#1\s!fallbacks\endcsname\@EA\let\@EA\@@fontfallbacks \csname\??ff#1\s!fallbacks\endcsname\else + \ifcsname\??ff #1\endcsname\@EA \@@thefallbacksnop\csname\??ff #1\endcsname\else + \let \@@fontfallbacks \empty \fi\fi} + +\def\updatefontparametersyes + {\@@thefeaturesyes \somefontname + \@@thefallbacksyes\somefontname} + +\def\updatefontparametersnop + {\@@thefeaturesnop \somefontname + \@@thefallbacksnop\somefontname} + +\def\updatefontparameters + {\ifx\fontclass\empty\updatefontparametersnop\else\updatefontparametersyes\fi} + \let\@@fontclassfeatures \empty \let\@@fontclassfallbacks\empty \let\@@fontfallbacks\empty \let\@@fontfeatures \empty -\let\@@skewchar \empty \let\@@hyphenchar \empty % todo, will go to encoding %D This brings down maps processing from 466 to 309 seconds @@ -1334,14 +837,6 @@ %D We also accept \type{sa a}||\type{sa d} as specification. -%D The duplicate font definition, using the ever the same dummy -%D font name, results in less fuzzy error messages. In the log -%D file, for instance when overfull boxes are reported, the -%D simple keyword `font' replaces the \TEX\ ordinated name. The -%D latter can be too misleading, due to the fact that \TEX\ has -%D a rather optimized font memory management. Thanks to Taco -%D for helping me sort this out. - %D \macros %D {definefontsynonym, doifelsefontsynonym, %D expandfontsynonym, truefontname, truefontdata} @@ -1366,47 +861,64 @@ \def\definefontsynonym[#1]#2[#3]% {\edef\@@fontname{#1}% \edef\@@fontfile{#3}% - \doifnextcharelse[\dodefinefontsynonym\nodefinefontsynonym} - -\def\nodefinefontsynonym - {\@EA\let\csname\??ff\fontclass\@@fontname\endcsname\@@fontfile} - -\def\dodefinefontsynonym[#1]% - {\edef\@@fontdata{#1}% - \ifx\@@fontdata\empty - \nodefinefontsynonym + \ifx\fontclass\empty + \expandafter\dodefinefontsynonymnop \else - \ifx\fontclass\empty - \getfontparameters - \else - \getglobalfontparameters - \fi - \ifcsname\??ff\@@fontfile\s!features\endcsname - \@EA\edef\csname\??ff\fontclass\@@fontname\endcsname{\@@fontfile*\csname\??ff\@@fontfile\s!features\endcsname}% - \@EA\let\csname\??ff\@@fontfile\s!features\endcsname\undefined - \else - \nodefinefontsynonym - \fi + \expandafter\dodefinefontsynonymyes \fi} -\def\getfontparameters - {\expandafter\dogetfontparameter\@@fontdata,]=,} +\def\dodefinefontsynonymnop + {\@EA\let\csname\??ff\@@fontname\endcsname\@@fontfile % maybe just #1 #3, saves expansion + \doifnextoptionalelse\dododefinefontsynonymnop\nonodefinefontsynonymnop} + +\def\dodefinefontsynonymyes + {\@EA\let\csname\??ff\fontclass\@@fontname\endcsname\@@fontfile % maybe just #1 #3, saves expansion + \doifnextoptionalelse\dododefinefontsynonymyes\nonodefinefontsynonymyes} -\def\getglobalfontparameters - {\expandafter\dogetglobalfontparameter\@@fontdata,]=,} +\def\dododefinefontsynonymnop[#1]% + {\let\@@ff@@features \undefined + \let\@@ff@@fallbacks\undefined + \expandafter\dogetfontparameternop#1,]=,} -\def\dogetfontparameter#1=#2,% - {\if]#1\else - \expandafter\def\csname\??ff\@@fontfile#1\endcsname{#2}% - \expandafter\dogetfontparameter +\def\dododefinefontsynonymyes[#1]% + {\let\@@ff@@features \undefined + \let\@@ff@@fallbacks\undefined + \expandafter\dogetfontparameteryes#1,]=,} + +\def\dogetfontparameternop#1=#2,% + {\if]#1% + \dodododefinefontsynonymnop + \else + \expandafter\def\csname @@ff@@#1\endcsname{#2}% + \expandafter\dogetfontparameternop \fi} -\def\dogetglobalfontparameter#1=#2,% - {\if]#1\else - \expandafter\gdef\csname\??ff\@@fontfile#1\endcsname{#2}% - \expandafter\dogetglobalfontparameter +\def\dogetfontparameteryes#1=#2,% + {\if]#1% + \dodododefinefontsynonymyes + \else + \expandafter\def\csname @@ff@@#1\endcsname{#2}% + \expandafter\dogetfontparameteryes \fi} +% hm, was wrong, class/global reversed + +\def\nonodefinefontsynonymnop + {\@EA\let\csname\??ff\@@fontname\s!features \endcsname\undefined + \@EA\let\csname\??ff\@@fontname\s!fallbacks\endcsname\undefined} + +\def\nonodefinefontsynonymyes + {\global\@EA\let\csname\??ff\fontclass\@@fontname\s!features \endcsname\undefined + \global\@EA\let\csname\??ff\fontclass\@@fontname\s!fallbacks\endcsname\undefined} + +\def\dodododefinefontsynonymnop + {\@EA\let\csname\??ff\@@fontname\s!features \endcsname\@@ff@@features + \@EA\let\csname\??ff\@@fontname\s!fallbacks\endcsname\@@ff@@fallbacks} + +\def\dodododefinefontsynonymyes + {\global\@EA\let\csname\??ff\fontclass\@@fontname\s!features \endcsname\@@ff@@features + \global\@EA\let\csname\??ff\fontclass\@@fontname\s!fallbacks\endcsname\@@ff@@fallbacks} + \let\definefontfile\definefontsynonym % dedicated to Taco Hoekwater \def\setupfontsynonym @@ -1423,20 +935,51 @@ \fi \fi} -\def\truefontdata#1#2% - {\ifcsname\??ff#1#2\endcsname - % raw(Regular) raw(key) - \csname\??ff#1#2\endcsname - \else\ifcsname\??ff\fontclass#1\endcsname - % exp(palatino Regular) raw(key) - \expandafter\truefontdata\csname\??ff\fontclass#1\endcsname#2% - \else\ifcsname\??ff#1\endcsname - % exp(Regular) raw(key) - \expandafter\truefontdata\csname\??ff#1\endcsname#2% - \else\ifcsname\??ff#2\endcsname - % raw(key) - \csname\??ff#2\endcsname - \fi\fi\fi\fi} +% \def\truefontname#1% +% {\@EA\dotruefontname#1*\empty*\relax} +% +% \def\dotruefontname#1*#2#3*#4\relax +% {\ifcsname\??ff\fontclass#1\endcsname +% \ifx#2\empty +% \@EA\truefontname\csname\??ff\fontclass#1\endcsname +% \else +% \@EA\redotruefontname\csname\??ff\fontclass#1\endcsname*#2#3% +% \fi +% \else\ifcsname\??ff#1\endcsname +% \ifx#2\empty +% \@EA\truefontname\csname\??ff#1\endcsname +% \else +% \@EA\redotruefontname\csname\??ff#1\endcsname*#2#3% +% \fi +% \else +% #1\ifx#2\empty\else*#2#3\fi +% \fi\fi} +% +% \def\redotruefontname#1% +% {\@EA\dodotruefontname#1*\relax} +% +% \def\dodotruefontname#1*#2\relax +% {\ifcsname\??ff\fontclass#1\endcsname +% \@EA\redotruefontname\csname\??ff\fontclass#1\endcsname +% \else\ifcsname\??ff#1\endcsname +% \@EA\redotruefontname\csname\??ff#1\endcsname +% \else +% #1% +% \fi\fi} +% +% \def\expandfontsynonym#1#2% #2 := onelevelexpansion(#1) +% {\ifcsname\??ff\fontclass#2\endcsname +% \expandafter\def\expandafter#1\expandafter{\csname\??ff\fontclass#2\endcsname}% +% \fi} +% +% \def\doifelsefontsynonym#1% +% {\ifcsname\??ff\fontclass#1\endcsname +% \expandafter\firstoftwoarguments +% \else +% \expandafter\secondoftwoarguments +% \fi} + +% maybe we need to stick in one branch \def\truefontname#1% {\@EA\dotruefontname#1*\empty*\relax} @@ -1448,6 +991,12 @@ \else \@EA\redotruefontname\csname\??ff\fontclass#1\endcsname*#2#3% \fi + \else\ifcsname\??ff\defaultfontclass#1\endcsname + \ifx#2\empty + \@EA\truefontname\csname\??ff\defaultfontclass#1\endcsname + \else + \@EA\redotruefontname\csname\??ff\defaultfontclass#1\endcsname*#2#3% + \fi \else\ifcsname\??ff#1\endcsname \ifx#2\empty \@EA\truefontname\csname\??ff#1\endcsname @@ -1456,7 +1005,7 @@ \fi \else #1\ifx#2\empty\else*#2#3\fi - \fi\fi} + \fi\fi\fi} \def\redotruefontname#1% {\@EA\dodotruefontname#1*\relax} @@ -1464,23 +1013,29 @@ \def\dodotruefontname#1*#2\relax {\ifcsname\??ff\fontclass#1\endcsname \@EA\redotruefontname\csname\??ff\fontclass#1\endcsname + \else\ifcsname\??ff\defaultfontclass#1\endcsname + \@EA\redotruefontname\csname\??ff\defaultfontclass#1\endcsname \else\ifcsname\??ff#1\endcsname \@EA\redotruefontname\csname\??ff#1\endcsname \else #1% - \fi\fi} + \fi\fi\fi} \def\expandfontsynonym#1#2% #2 := onelevelexpansion(#1) {\ifcsname\??ff\fontclass#2\endcsname \expandafter\def\expandafter#1\expandafter{\csname\??ff\fontclass#2\endcsname}% - \fi} + \else\ifcsname\??ff\defaultfontclass#2\endcsname + \expandafter\def\expandafter#1\expandafter{\csname\??ff\defaultfontclass#2\endcsname}% + \fi\fi} \def\doifelsefontsynonym#1% {\ifcsname\??ff\fontclass#1\endcsname - \expandafter\firstoftwoarguments + \@EA\firstoftwoarguments + \else\ifcsname\??ff\defaultfontclass#1\endcsname + \@EAEAEA\firstoftwoarguments \else - \expandafter\secondoftwoarguments - \fi} + \@EAEAEA\secondoftwoarguments + \fi\fi} % \definetypeface[palatino][rm][serif][palatino,allbold][default] % @@ -1498,7 +1053,7 @@ \def\dostartfontclass[#1]% {\pushmacro\fontclass - \doifelse{#1}{\v!each} + \doifelse{#1}\v!each {\let\fontclass\empty} {\doifsomething{#1}{\def\fontclass{#1}}}} @@ -1510,46 +1065,13 @@ %D %D A goody: -\def\tracedfontencoding#1% - {\ifcsname\??ff#1\s!encoding\endcsname - \space[\csname\??ff#1\s!encoding\endcsname]% - \fi} - \def\tracedfontname#1% - {\ifcsname\??ff\fontclass#1\endcsname - #1\tracedfontencoding{\fontclass#1}\space->\space - \@EA\tracedfontname\csname\??ff\fontclass#1\endcsname + {#1\ifcsname\??ff\fontclass#1\endcsname + \@EA\tracedfontname\csname\??ff\fontclass#1\endcsname \else\ifcsname\??ff#1\endcsname - #1\tracedfontencoding{#1}\space->\space - \@EA\tracedfontname\csname\??ff#1\endcsname - \else - #1% + \@EA\tracedfontname\csname\??ff#1\endcsname \fi\fi} -%D \macros -%D {getfontfileparameters} -%D -%D For special purposes, one can use the next macro to -%D access font file characteristics, for instance: -%D -%D \starttyping -%D \getfontfileparameters{Regular} -%D \stoptyping -%D -%D can result in: -%D -%D \starttyping -%D \def\currentfontfileencoding{texnansi} -%D \stoptyping - -% \let\currentfontfileencoding\s!unknown -% \let\currentfontfilemapping \s!unknown -% \let\currentfontfilehandling\s!unknown - -% \def\getfontfileparameters#1% -% {\edef\@@truefontname{\truefontname{#1}}% -% \edef\currentfontfilefeatures{\truefontdata\@@truefontname\s!features}} - %D \macros %D {definefont} %D @@ -1568,14 +1090,23 @@ \def\definefont {\dotripleempty\dodefinefont} +% \def\dodefinefont[#1][#2][#3]% [name][spec][1.6 | line=10pt | setup_id] +% {\doifinstringelse{ }{#2} +% {\ifthirdargument +% \unexpanded\setvalue{#1}{\redodefinefont{#1}{#2}{#3}}% +% \else +% \unexpanded\setvalue{#1}{\dododefinefont{#1}{#2}}% +% \fi} +% {\definefont[#1][#2 sa *][#3]}} + +% we moved the unspecified size check to lua + \def\dodefinefont[#1][#2][#3]% [name][spec][1.6 | line=10pt | setup_id] - {\doifinstringelse{ }{#2} - {\ifthirdargument - \unexpanded\setvalue{#1}{\redodefinefont{#1}{#2}{#3}}% - \else - \unexpanded\setvalue{#1}{\dododefinefont{#1}{#2}}% - \fi} - {\definefont[#1][#2 sa *][#3]}} + {\ifthirdargument + \unexpanded\setvalue{#1}{\redodefinefont{#1}{#2}{#3}}% + \else + \unexpanded\setvalue{#1}{\dododefinefont{#1}{#2}}% + \fi} \def\redodefinefont#1#2#3% {\dododefinefont{#1}{#2}% @@ -1636,11 +1167,6 @@ \the\everyfontswitch \fi} -%D I considered checking for mistakenly use of \PLAIN's -%D \type{\magstep}'s but although it would take only a few -%D lines of code, this would not add to consistent use. I -%D therefore removed this check. - %D \macros %D {mapfontsize} %D @@ -1673,8 +1199,7 @@ {\dodoubleargument\domapfontsize} \def\domapfontsize[#1][#2]% - {\scratchdimen#1\relax % \relax is really needed here - \setvalue{\??ft*\the\scratchdimen}{#2}} + {\setvalue{\??ft*\the\dimexpr#1\relax}{#2}} \def\setmappedfontsize#1% {\ifcsname\??ft*#1\endcsname @@ -1695,26 +1220,25 @@ %D To be documented. -\let\sizelist\empty +\let\fontsizelist \empty +\let\fontalternativelist\empty +\let\fontstylelist \empty -\def\definefontsize[#1]% sneller met toks - {\addtocommalist{#1}\sizelist - \def\docommand##1% +\def\checkfontnamecombinations + {\def\docommand##1% {\def\dodocommand####1% - {\def\dododocommand########1% - %{\checkbodyfont{}{########1}{####1}{##1}}% - {\checkbodyfont{########1}{####1}{##1}}% - \processcommacommand[\stylelist]\dododocommand}% - \processcommacommand[\alternativelist]\dodocommand}% - \processcommacommand[\sizelist]\docommand} + {\def\dododocommand########1{\checkbodyfont{########1}{####1}{##1}}% + \processcommacommand[\fontstylelist]\dododocommand}% + \processcommacommand[\fontalternativelist]\dodocommand}% + \processcommacommand[\fontsizelist]\docommand} -\def\alternativetextlist{\c!tf,\c!bf,\c!it,\c!sl,\c!bs,\c!bi,\c!sc} -\def\alternativemathlist{\c!mr,\c!mi,\c!sy,\c!ex,\c!ma,\c!mb} - -\let\alternativelist\alternativetextlist % upward compatible +\def\definefontsize[#1]% sneller met toks + {\addtocommalist{#1}\fontsizelist + \checkfontnamecombinations} -%\definefontsize[\c!a] \definefontsize[\c!b] -%\definefontsize[\c!c] \definefontsize[\c!d] +\def\definefontalternative[#1]% + {\addtocommalist{#1}\fontalternativelist + \checkfontnamecombinations} %D \macros %D {currentfontscale,currentfontbodyscale} @@ -1810,7 +1334,7 @@ \let\bodyfontenvironmentlist\empty -\newcount\@@fontdefhack +\newcount\@@fontdefhack % check if this is still needed \def\@@beginfontdef {\ifcase\@@fontdefhack @@ -1843,29 +1367,44 @@ \@EA\dododefinebodyfontenvironment\@EA[\tempbodyfontsize][#1][#3]}% \@@endfontdef \else + \ifx\fontclass\empty\else + \writestatus\m!fonts{beware: fontclass ignored (if needed use: [fontclass][size][settings])}% + \fi + \pushmacro\fontclass + \let\fontclass\empty \definebodyfontenvironment[\fontclass][#1][#2]% change */* + \popmacro\fontclass \fi} \def\dododefinebodyfontenvironment[#1][#2][#3]% size class settings - {\@@beginfontdef - \doifundefined{\??ft#2#1\c!em} % \s!text goes wrong in testing because - {\def\docommand##1% % the 12pt alternative will called when - {\scratchdimen#1\relax % typesetting the test (or so) - \scratchdimen\csname\??ft\s!default##1\endcsname\scratchdimen + {\@@beginfontdef % \s!text goes wrong in testing because the 12pt alternative will called when typesetting the test (or so) + \ifcsname\??ft#2#1\c!em\endcsname + % we test for em as we assume it to be set + \else + \def\docommand##1% +% fails: \def\checkbodyfontenvironment[#1]{! #1 ! \definebodyfontenvironment[\fontclass][#1][]} \setupbodyfont[8.5pt] +% {\normalizebodyfontsize\csname\??ft\s!default##1\endcsname\dimexpr#1\relax\to\tempbodyfontsize +% \letvalue{\??ft#2#1##1}\tempbodyfontsize}% + {\scratchdimen\csname\??ft\s!default##1\endcsname\dimexpr#1\relax \normalizebodyfontsize\scratchdimen\to\tempbodyfontsize - \setevalue{\??ft#2#1##1}{\tempbodyfontsize}}% - \processcommacommand[\fontsizelist]\docommand + \letvalue{\??ft#2#1##1}\tempbodyfontsize}% + \processcommacommand[\fontrelativesizelist]\docommand \copyparameters [\??ft#2#1][\??ft\s!default] - [\c!interlinespace,\c!em]}% + [\c!interlinespace,\c!em]% + \fi \getparameters[\??ft#2#1][#3]% \@@endfontdef % new code, see remark - \ifloadingfonts \else % only runtime - \doifundefined{\@size@#1} % only once - {\letvalue{\@size@#1}\empty % prevent loop - \defineunknownfont{#1}}% % safeguard - \fi + \ifloadingfonts + % only runtime + \else\ifcsname\@size@#1\endcsname + % only once + \else + % prevent loop (hence \empty) + \letvalue{\@size@#1}\empty + \defineunknownfont{#1}% + \fi\fi % so far \setvalue{\@size@#1}{\docompletefontswitch[#1]}} @@ -1891,28 +1430,18 @@ \def\checkbodyfontenvironment[#1]% {\definebodyfontenvironment[\fontclass][#1][]} + +\def\checkbodyfontenvironment[#1]% + {\ifcsname\??ft\fontclass#1\c!em\endcsname + % we test for em as we assume it to be set + \else + \definebodyfontenvironment[\fontclass][#1][]% + \fi} % this one already catches both define/setup \def\setupbodyfontenvironment{\definebodyfontenvironment} -% officially, but not needed (yet): -% -% \def\dosetupbodyfontenvironment[#1][#2][#3]% class size settings -% {\ifthirdargument -% \localbodyfontsize#2\relax -% \normalizebodyfontsize\localbodyfontsize\to\normalizedbodyfontsize -% \doifundefinedelse{\??ft#1\normalizedbodyfontsize\c!em} -% {\definebodyfontenvironment[#1][#2][#3]}% -% {\getparameters[\??ft#1\normalizedbodyfontsize][#3]}% -% \else -% \localbodyfontsize#1\relax -% \normalizebodyfontsize\localbodyfontsize\to\normalizedbodyfontsize -% \doifundefinedelse{\??ft\normalizedbodyfontsize\c!em} -% {\definebodyfontenvironment[#1][#2]}% -% {\getparameters[\??ft\normalizedbodyfontsize][#2]}% -% \fi} - %D Just a couple of interface macros: \def\bodyfontvariable#1% @@ -1952,7 +1481,8 @@ %D We show two examples, that show all the alternative %D scaling options. The \type{\tfa} alternatives can be %D extended with \type{\bfa}, \type{\slb}, etc. or even -%D \type{e} and higher alternatives. +%D \type{e} and higher alternatives. The magic scaled +%D values are derived from plain \TEX's \type {\magstep}: %D %D \starttyping %D \definebodyfont [12pt] [rm] @@ -1962,10 +1492,10 @@ %D sl=cmsl12, %D bi=cmbxti10 at 12pt, %D bs=cmbxsl10 at 12pt, -%D tfa=cmr12 scaled \magstep1, -%D tfb=cmr12 scaled \magstep2, -%D tfc=cmr12 scaled \magstep3, -%D tfd=cmr12 scaled \magstep4, +%D tfa=cmr12 scaled 1.200, +%D tfb=cmr12 scaled 1.440, +%D tfc=cmr12 scaled 1.728, +%D tfd=cmr12 scaled 2.074, %D sc=cmcsc10 at 12pt] %D %D \definebodyfont [12pt,11pt,10pt,9pt,8pt] [rm] @@ -2026,12 +1556,12 @@ \doifnumberelse{#1} {\doifassignmentelse{#3} {% [12pt] [style] [settings] - \doifundefined{#2}{\expanded{\definefontstyle[#2][#2]}}% new + \doifundefined{#2}{\normalexpanded{\noexpand\definefontstyle[#2][#2]}}% new \processcommalist[#1]{\dododefinebodyfont{#2}{#3}}} {% [12pt] [style] [identifier] \dodefinedefaultbodyfont[#1][#2][#3]}} % body style identifier {% [identifier] [style] [settings] % see *** - \setvalue{\s!default#1#2}##1##2{\expanded{\xdodefinebodyfont[##1][##2][#3]}}}% + \setvalue{\s!default#1#2}##1##2{\normalexpanded{\noexpand\xdodefinebodyfont[##1][##2][#3]}}}% \else\ifsecondargument \definebodyfont[#1][\c!rm][#2]% \else @@ -2042,9 +1572,8 @@ \fi\fi} \def\xdodefinebodyfont[#1][#2][#3]% body|identifier style defs|identifier - {%\writestatus{[#1]}{[#2][#3]}% - \checkrelativefontsize{#2}% rather new, inherit from other defs - \ifundefined{#2}\expanded{\definefontstyle[#2][#2]}\fi % new + {\checkrelativefontsize{#2}% rather new, inherit from other defs + \ifcsname#2\endcsname\else\normalexpanded{\noexpand\definefontstyle[#2][#2]}\fi % new \processcommalist[#1]{\dododefinebodyfont{#2}{#3}}% \let\relativefontsize\defaultrelativefontsize} @@ -2055,47 +1584,56 @@ \def\dodododefinebodyfont#1#2#3% style body def {\dododododefinebodyfont{#1}{#2}[#3]} -\def\iflocalclassfonts{\ifx\fontclass\empty} - -\def\dododododefinebodyfont#1#2[#3#4#5=#6]% style body def - {\ifundefined{#1#3#4#5}% - %\checkbodyfont{#2}{#1}{#3#4}{#5}% not \definefontsize[#5] - \checkbodyfont{#1}{#3#4}{#5}% not \definefontsize[#5] - \fi - \iflocalclassfonts - \letbeundefined{*\fontclass#2#1#3#4#5*}% - \scratchtoks{#6}% - \expanded{\unexpanded\noexpand\setvalue{#2#1#3#4#5}% - {\noexpand\xxdododefinefont{\relativefontsize}{#2}% - {#2#1#3#4#5}{\the\scratchtoks}}}% +\def\dododododefinebodyfont + {\ifx\fontclass\empty + \expandafter\dododododefinebodyfontnop \else - %\expanded{\writestatus{defining}{[\fontclass][#2#1#3#4#5] \resolvefontname#6 }}% - \global\letbeundefined{*\fontclass#2#1#3#4#5*}% - \scratchtoks{#6}% - \expanded{\unexpanded\noexpand\setgvalue{\fontclass#2#1#3#4#5}% - {\noexpand\xxdododefinefont{\relativefontsize}{#2}% - {#2#1#3#4#5}{\the\scratchtoks}}}% + \expandafter\dododododefinebodyfontyes \fi} -% \def\checkbodyfont#1#2#3#4% body style alt size / gdef % #4 can be empty -% {\def\c!!mm{#2}% -% \ifx\c!!mm\c!mm % prevents \max and alike (re)defs -% \unexpanded\setgvalue {#2}{\setcurrentfontstyle {#2}}% \rm -% \unexpanded\setgvalue {#3}{\setcurrentfontalternative {#3}}% \sl -% \else -% \unexpanded\setgvalue {#2#4}{\setcurrentfontstylesize {#2}{#4}}% \rma -% \unexpanded\setgvalue {#3#4}{\setcurrentfontalternativesize {#3}{#4}}% \sla -% \unexpanded\setgvalue {#2#3#4}{\setcurrentfontstylealternativesize{#2}{#3}{#4}}% \rmsla -% \unexpanded\setgvalue {#2}{\setcurrentfontstyle {#2}}% \rm -% \unexpanded\setgvalue {#3}{\setcurrentfontalternative {#3}}% \sl -% \unexpanded\setgvalue {#2\c!x}{\setcurrentfontxstylealternative {#2}}% \rmx -% \unexpanded\setgvalue{#2\c!xx}{\setcurrentfontxxstylealternative {#2}}% \rmxx -% \unexpanded\setgvalue {#3\c!x}{\setcurrentfontxalternative {#3}}% \slx -% \unexpanded\setgvalue{#3\c!xx}{\setcurrentfontxxalternative {#3}}% \slxx -% \unexpanded\setgvalue {#2#3}{\setcurrentfontstylealternative {#2}{#3}}% \rmsl -% \fi} -% -% leaner +\def\dododododefinebodyfontyes#1% style body def + {\edef\askedbodyfontstyle{#1}% + \ifx\askedbodyfontstyle\c!mm + \expandafter\dodefinebodyfontyesmm + \else + \expandafter\dodefinebodyfontyesxx + \fi\askedbodyfontstyle} % we can get rid of #1 + +\def\dododododefinebodyfontnop#1% style body def + {\edef\askedbodyfontstyle{#1}% + \ifx\askedbodyfontstyle\c!mm + \expandafter\dodefinebodyfontnopmm + \else + \expandafter\dodefinebodyfontnopxx + \fi\askedbodyfontstyle} % we can get rid of #1 + +\def\dodefinebodyfontnopxx#1#2[#3#4#5=#6]% style body def + {\ifcsname#1#3#4#5\endcsname\else\checkbodyfont{#1}{#3#4}{#5}\fi% not \definefontsize[#5] + \@EA\let\csname*#2#1#3#4#5*\endcsname\undefined + \normalprotected\@EA\edef\csname#2#1#3#4#5\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#5}{\normalunexpanded{#6}}}} + +\def\dodefinebodyfontyesxx#1#2[#3#4#5=#6]% style body def + {\ifcsname#1#3#4#5\endcsname\else\checkbodyfont{#1}{#3#4}{#5}\fi% not \definefontsize[#5] + \global\@EA\let\csname*\fontclass#2#1#3#4#5*\endcsname\undefined + \normalprotected\@EA\xdef\csname\fontclass#2#1#3#4#5\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#5}{\normalunexpanded{#6}}}} + +\def\dodefinebodyfontnopmm#1#2[#3#4#5=#6]% style body def + {\ifcsname#1#3#4#5\endcsname\else\checkbodyfont{#1}{#3#4}{#5}\fi% not \definefontsize[#5] + \@EA\let\csname*#2#1#3#4#51*\endcsname\undefined + \@EA\let\csname*#2#1#3#4#52*\endcsname\undefined + \@EA\let\csname*#2#1#3#4#53*\endcsname\undefined + \normalprotected\@EA\edef\csname#2#1#3#4#51\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#51}{\normalunexpanded{#6}}}% + \normalprotected\@EA\edef\csname#2#1#3#4#52\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#52}{\normalunexpanded{#6}}}% + \normalprotected\@EA\edef\csname#2#1#3#4#53\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#53}{\normalunexpanded{#6}}}} + +\def\dodefinebodyfontyesmm#1#2[#3#4#5=#6]% style body def + {\ifcsname#1#3#4#5\endcsname\else\checkbodyfont{#1}{#3#4}{#5}\fi% not \definefontsize[#5] + \global\@EA\let\csname*\fontclass#2#1#3#4#51*\endcsname\undefined + \global\@EA\let\csname*\fontclass#2#1#3#4#52*\endcsname\undefined + \global\@EA\let\csname*\fontclass#2#1#3#4#53*\endcsname\undefined + \normalprotected\@EA\xdef\csname\fontclass#2#1#3#4#51\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#51}{\normalunexpanded{#6}}}% + \normalprotected\@EA\xdef\csname\fontclass#2#1#3#4#52\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#52}{\normalunexpanded{#6}}}% + \normalprotected\@EA\xdef\csname\fontclass#2#1#3#4#53\endcsname{\noexpand\xxdododefinefont{\number\relativefontsize}{#2}{#2#1#3#4#53}{\normalunexpanded{#6}}}} \def\checkbodyfont#1% tests for ttsl mmbf {\def\c!!mm{#1}% @@ -2108,9 +1646,9 @@ \def\checkmathbodyfont#1#2#3% style alt size / gdef % #3 can be empty {%\message{!m #1 #2 #3!}% % #1 #2 #3 = signal - \unexpanded\setgvalue {#1#2#3}{\setcurrentfontstylealternativesize{#1}{#2}{#3}}% \mmsla - \unexpanded\setgvalue {#1}{\setcurrentfontstyle {#1}}% \mm - \unexpanded\setgvalue {#2}{\setcurrentfontalternative {#2}}}% \sl + %unexpanded\setgvalue {#1#2#3}{\setcurrentfontstylealternativesize{#1}{#2}{#3}}% \mmsla + \unexpanded\setgvalue {#2}{\setcurrentfontalternative {#2}}% \sl + \unexpanded\setgvalue {#1}{\setcurrentfontstyle {#1}}}% \mm \def\checktextbodyfont#1#2#3% style alt size / gdef % #3 can be empty {%\message{!t #1 #2 #3!}% @@ -2144,40 +1682,42 @@ \newif\ifdefiningunknownfont \def\dodefineunknownfont#1#2% - {\doifdefined{\??ft\s!default#2} - {\donetrue - \scratchdimen#1\relax - \scratchdimen\csname\??ft\s!default#2\endcsname\scratchdimen - \normalizebodyfontsize\scratchdimen\to\!!stringa - \letvalue{\??ft#1#2}\!!stringa}} + {\ifcsname\??ft\s!default#2\endcsname + \donetrue + \normalizebodyfontsize\csname\??ft\s!default#2\endcsname\dimexpr#1\relax\to\tempbodyfontsize + \letvalue{\??ft#1#2}\tempbodyfontsize + \fi} \def\dodefineunknownbodyfont#1#2% see *** - {\doifdefined{\s!default\s!default#2}% somehow related to */* - {\donetrue - \getvalue{\s!default\s!default#2}{#1}{#2}}} + {\ifcsname\s!default\s!default#2\endcsname % somehow related to */* + \donetrue + \getvalue{\s!default\s!default#2}{#1}{#2}% + \fi} \def\dodefineunknownsubfont#1#2% - {\doifundefined{\@size@\getvalue{\??ft#1#2}} - {\donetrue - \defineunknownfont{\getvalue{\??ft#1#2}}}} + {\ifcsname\@size@\getvalue{\??ft#1#2}\endcsname + \else + \donetrue + \defineunknownfont{\getvalue{\??ft#1#2}}% + \fi} \def\defineunknownfont#1% {\let\c!savedtext\c!text \let\c!text\s!text \donefalse - \processcommacommand[\fontsizelist]{\dodefineunknownfont{#1}}% + \processcommacommand[\fontrelativesizelist]{\dodefineunknownfont{#1}}% \let\c!text\c!savedtext \ifdone \donefalse \processcommacommand - [\stylelist] + [\fontstylelist] {\dodefineunknownbodyfont{#1}}% \ifdone \donefalse \setvalue{\@size@#1}{\docompletefontswitch[#1]}% \ifdefiningunknownfont \else \definingunknownfonttrue - \processcommacommand[\fontsizelist]{\dodefineunknownsubfont{#1}}% + \processcommacommand[\fontrelativesizelist]{\dodefineunknownsubfont{#1}}% \definingunknownfontfalse \fi \fi @@ -2238,8 +1778,8 @@ %D size and the local (sometimes in the textflow) size. We %D store these dimensions in two \DIMENSION\ registers. -\newdimen\globalbodyfontsize \globalbodyfontsize=12pt -\newdimen\localbodyfontsize \localbodyfontsize =\globalbodyfontsize +\ifdefined\globalbodyfontsize\else \newdimen\globalbodyfontsize \fi \globalbodyfontsize=12pt +\ifdefined\localbodyfontsize \else \newdimen\localbodyfontsize \fi \localbodyfontsize =\globalbodyfontsize %D \macros %D {bodyfontsize} @@ -2273,34 +1813,41 @@ %D often not the way users specify the bodyfont size. Therefore %D we also store the normalized value. -\chardef\fontdigits=1 +\chardef\fontdigits=2 % was 1 \def\normalizebodyfontsize#1\to#2% - {\scratchdimen#1\relax - \ifcase\fontdigits\advance\scratchdimen.5\points\fi - \@EA\@EA\@EA\donormalizedbodyfontsize\@EA\WITHOUTPT\the\scratchdimen00\to#2} + {\@EA\@EA\@EA\donormalizedbodyfontsize\@EA\WITHOUTPT\the\dimexpr#1+\ifcase\fontdigits.5\or.05\or.005\fi\points\relax000\to#2} -\def\donormalizedbodyfontsize#1.#2#3#4\to#5% \points ? - {\edef#5% +\def\donormalizedbodyfontsize#1.#2#3#4#5\to#6% \points ? + {\edef#6% not \ifcase#2\else due to \relax adding {#1% - \ifcase\fontdigits\or - \ifcase#2 \else.#2\fi % and not: \ifcase#2\else ... - \else - \ifcase#2#3 \else.#2\ifcase#3 \else#3\fi\fi % not: \ifcase#2#3\else ... + \ifcase\fontdigits + \or \ifcase#2 \else .#2\fi % 1 + \or \ifcase#2#3 \else .#2\ifcase#3 \else #3\fi\fi % 2 + \else \ifcase#2#3#4 \else .#2\ifcase#4 \ifcase#3 \else#3\fi \else#3#4\fi\fi % 3 \fi \s!pt}} +% not faster, just less tracing +% +% \def\setfontdigits#1% +% {\chardef\fontdigits\ifnum#1>\plusthree\plusthree\else#1\fi\relax +% \@EA\let\@EA\normalizedbfs\csname normalizedbfs\number\fontdigits\endcsname} +% +% \def\normalizebodyfontsize#1\to#2% +% {\@EA\@EA\@EA\normalizedbfs\@EA\WITHOUTPT\the\dimexpr#1+\ifcase\fontdigits.5\or.05\or.005\fi\points\relax000\to#2} +% +% \setvalue{normalizedbfs0}#1.#2\to #3{\edef#3{#1\s!pt}} +% \setvalue{normalizedbfs1}#1.#2#3\to #4{\edef#4{#1\ifcase#2 \else.#2\fi\s!pt}} +% \setvalue{normalizedbfs2}#1.#2#3#4\to #5{\edef#5{#1\ifcase#2#3 \else.#2\ifcase#3 \else#3\fi\fi\s!pt}} +% \setvalue{normalizedbfs3}#1.#2#3#4#5\to#6{\edef#6{#1\ifcase#2#3#4 \else.#2\ifcase#4 \ifcase#3 \else#3\fi\else#3#4\fi\fi\s!pt}} +% +% \setfontdigits2 + \normalizebodyfontsize\bodyfontsize\to\normalizedglobalbodyfontsize \normalizebodyfontsize\bodyfontsize\to\normalizedlocalbodyfontsize \normalizebodyfontsize\bodyfontsize\to\normalizedbodyfontsize -%D To be internationalized: - -\def\korpsgrootte {\bodyfontsize} -\def\korpspunten {\bodyfontpoints} - -%D some day. - %D \macros %D {fontstyle,fontalternative,fontsize} %D @@ -2320,29 +1867,7 @@ \let\fontalternative = \defaultfontalternative \let\fontstyle = \defaultfontstyle -\let\fontsize = \defaultfontsize - -%D {\em The following approach is obsolete.} -%D -%D All things related to fonts are grouped into files with -%D names like \type{font-cmr}. These files are loaded by: - -\def\resetfontdefinitionfile[#1]% - {\letbeundefined{\c!file\f!fontprefix#1}} - -\newif\ifloadfontfileonce - -\def\doreadfontdefinitionfile#1#2% #1 = set/switch state - {\doifundefined{\c!file\f!fontprefix#2}% - {\ifloadfontfileonce - \letvalue{\c!file\f!fontprefix#2}\empty - \fi - \makeshortfilename[\truefilename{\f!fontprefix#2}]% - \startreadingfile - \readsysfile\shortfilename - {\showmessage\m!fonts2{#2}} - {\showmessage\m!fonts3{#2}}% - \stopreadingfile}} +\let\fontsize = \defaultfontsize %D When \type {\loadfontfileoncetrue}, such files are %D only loaded once! This permits redundant loading, but at @@ -2352,8 +1877,10 @@ %D needed to prevent problems with loading files that use this %D character in numbers. +% can be made faster (only used internally now) + \def\doswitchpoints[#1]% - {\expanded{\dodoswitchpoints{#1}}} + {\normalexpanded{\noexpand\dodoswitchpoints{#1}}} \def\dodoswitchpoints#1% {\doifundefined{\@size@#1} @@ -2363,6 +1890,7 @@ {\getvalue{\@size@#1}% \localbodyfontsize#1\relax \normalizebodyfontsize\localbodyfontsize\to\normalizedbodyfontsize +% \edef\fontbody{\fontbody}% to be tested but we can clean up mkiv further \checkbodyfontenvironment[\normalizedbodyfontsize]} {\showmessage\m!fonts4{#1}}} @@ -2425,17 +1953,29 @@ %D sequence of a session. After the loading job is done, the %D macro relaxes itself and reset the signal. +% \appendtoks +% \to \everysetupdocument + +\newconditional\fontsareloaded + \def\preloadfonts % never called, needs a clean up - {\showmessage\m!fonts6{\normalizedbodyfontsize\normalspace\fontstyle}% - \global\loadingfontsfalse - \doswitchpoints[\normalizedbodyfontsize]% - \doswitchstyle[\fontstyle]% - \the\everybodyfont - \the\everyglobalbodyfont - \saveinterlinespace + {\global\loadingfontsfalse + \ifconditional\fontsareloaded \else + \doifmodeelse {*nofonts} + {\writestatus\m!fonts{latin modern fonts are not preloaded}} + {\writestatus\m!fonts{preloading latin modern fonts}% + \usetypescript[modern]% + \setuptypeface[modern]% + \showmessage\m!fonts6{\normalizedbodyfontsize\normalspace\fontstyle}}% + \fi \global\let\preloadfonts\relax} -% \prependtoks \preloadfonts \to \everydump % saves .1 s on a DELL P60 - 2GHZ +% maybe add this to \everystarttext +% +% \ifconditional\fontsareloaded\else +% \usetypescript[modern]% +% \setuptypeface[modern]% +% \fi %D Here comes the main font switching macros. These macros %D handle changes in size as well as returning to the global @@ -2446,36 +1986,68 @@ {\restoreglobalbodyfont} {\processcommacommand[#2]{\dodosetfont{#1}}% ##1 get also passed \ifloadingfonts\else + \global\settrue\fontsareloaded \doswitchpoints[\normalizedbodyfontsize]% \doswitchstyle[\fontstyle]% + \ifx\defaultfontclass\empty + \let\defaultfontclass\fontclass + \fi \fi}% \chardef\currentxfontsize\zerocount} \def\dodosetfont#1#2% #1 = set/switch state | check fo rempty, else space {\doifsomething{#2}{\dododosetfont{#1}{#2}{\showmessage\m!fonts4{#2}}}} -\def\dododosetfont#1#2#3% #1 = set/switch state - {\doifnumberelse{#2} - {\dodododosetfont{#1}{#2}{#3}} - {\doifdefinedelse{\??ft\normalizedbodyfontsize\interfaced{#2}} - {\edef\fontstep{\csname\bodyfontvariable\normalizedbodyfontsize\interfaced{#2}\endcsname}% - \expanded{\dodododosetfont{#1}{\fontstep}}{#3}} - {\doifelse{#2}\v!reset - {\let\fontstyle\empty % new 31/7/2006 - \let\fontsize \empty} - {\doifdefinedelse{\@style@#2} - {\edef\fontstyle{#2}} - {\doreadfontdefinitionfile{#1}{#2}}}}}} +% % % this can be retrofitted in mkii code % % % + +% \def\normalizebodyfontsize#1\to#2% +% {\@EA\@EA\@EA\donormalizedbodyfontsize\@EA\WITHOUTPT\the\dimexpr#1+\ifcase\fontdigits.5\or.05\or.005\fi\points\relax000\to#2} + +\def\dododosetfont#1#2#3% #1 = set/switch state ! ! ! !could also be used for mkii + {\doifnumberelse{#2}\dodododosetfont\redododosetfont{#1}{#2}{#3}} + +\def\redododosetfont#1#2#3% #1 = set/switch state ! ! ! !could also be used for mkii + {\edef\expandedfontthing{#2}% + \def\interfacedfontsize{\normalizedbodyfontsize\interfaced\expandedfontthing}% + \ifcsname\??ft\interfacedfontsize\endcsname + \edef\fontstep{\csname\bodyfontvariable\interfacedfontsize\endcsname}% + \normalexpanded{\noexpand\dodododosetfont{#1}{\fontstep}}{#3}% + \else\ifx\expandedfontthing\v!reset + \let\fontstyle\empty % new 31/7/2006 + \let\fontsize \empty + \else + \ifcsname\@style@\expandedfontthing\endcsname + \let\fontstyle\expandedfontthing + \else + \setcurrentfontclass\expandedfontthing + \ifcase#1\relax + \let\globalfontclass\globalfontclass + \else + \let\globalfontclass\fontclass + \fi + \ifx\fontclass\empty + \let\fontstyle\c!rm + \else\ifcsname\??tf\fontclass\s!default\endcsname + \edef\fontstyle{\csname\??tf\fontclass\s!default\endcsname}% + \else + \let\fontstyle\c!rm + \fi\fi + \fi + \fi\fi} \def\dodododosetfont#1#2#3% #1 = set/switch state - {\scratchdimen#2\relax - \normalizebodyfontsize\scratchdimen\to\normalizedsetfont - \doifundefined{\@size@\normalizedsetfont} - {\defineunknownfont{#2}}% - \doifdefinedelse{\@size@\normalizedsetfont} - {\localbodyfontsize\normalizedsetfont - \let\normalizedbodyfontsize\normalizedsetfont} - {#3\dosetsubstitutefont{#1}{#2}}} + {\normalizebodyfontsize#2\to\normalizedsetfont + \ifcsname\@size@\normalizedsetfont\endcsname \else + \defineunknownfont{#2}% + \fi + \ifcsname\@size@\normalizedsetfont\endcsname + \localbodyfontsize\normalizedsetfont + \let\normalizedbodyfontsize\normalizedsetfont + \else + #3\dosetsubstitutefont{#1}{#2}% + \fi} + +% % % %D In the previous macros we use \type{\currentxfontsize} to %D hold the current x||size of the font. This enables us to @@ -2515,8 +2087,15 @@ \let\fontclass\empty \let\globalfontclass\fontclass +% we need to check the fontclass + +\def\registerfontclass#1% + {\letgvalue{\@fontclass@#1}\v!yes} % global ? + \def\setcurrentfontclass#1% - {\edef\fontclass{#1}} + {\ifcsname\@fontclass@#1\endcsname + \edef\fontclass{#1}% + \fi} \let\defaultfontstyle \c!rm \let\defaultfontalternative \c!tf @@ -2553,7 +2132,7 @@ % already in sync \else \let\bigmathfontsize\fontsize - \synchronizemath \synchronizetext + \synchronizemath \fi} \def\checkbigmathsynchronization @@ -2572,10 +2151,7 @@ \checkbigmathsynchronization} \def\dosetcurrentfontalternative#1% - {\edef\fontalternative{#1}% - \ifmmode % maybe no test, or actually, an option - \fam\csname\fontalternative\s!fam\endcsname - \fi} + {\edef\fontalternative{#1}} \def\setcurrentfont#1#2#3#4% {%\message{[1 #1 #2 #3 #4]}% @@ -2660,6 +2236,7 @@ \global\let\fontstrategy\dofontstrategy \the\fontstrategies \relax % \relax still needed ? \fi + \autofontsizefalse \ifskipfontcharacteristics \setfontcharacteristics \the\everyfontswitch @@ -2675,6 +2252,8 @@ #1\csname\fontclass#2#3#4#5\endcsname \tryingfontfalse \fi} +% old sequence + \appendtoks \iftryingfont \fontstrategy \autofontsizefalse % --- --- --- --- % pt tt bf a \fontbody \fontstyle \fontalternative \fontsize \fi \to \fontstrategies @@ -2706,7 +2285,6 @@ \prependtoks \ifsynchronizefonts \synchronizemath - \synchronizetext \synchronizefont % problem: syncs last font \fi \to \everybodyfont @@ -2757,9 +2335,6 @@ % This alterative is not really needed, but for old time's sake % we keep it there. We can speed it up when needed. -% \def\setcurrentfontxstylealternative #1{\csname#1\endcsname\tfx} -% \def\setcurrentfontxxstylealternative#1{\csname#1\endcsname\tfxx} - \def\setcurrentfontxstylealternative #1{\csname#1\endcsname\tx} \def\setcurrentfontxxstylealternative#1{\csname#1\endcsname\txx} @@ -2808,10 +2383,11 @@ %D \stoptyping \def\dodefinefontstyle[#1][#2]% - {\rawdoifinsetelse{#2}{\stylelist} - {}%\debuggerinfo\m!fonts{unknown style #2}} - {\addtocommalist{#2}\stylelist - \showmessage\m!fonts8{#2\space (#1)}}% + {\rawdoifinsetelse{#2}{\fontstylelist} + {%\debuggerinfo\m!fonts{unknown style #2}% + } + {%\debuggerinfo\m!fonts8{#2\space (#1)}% + \addtocommalist{#2}\fontstylelist}% % check kan hier \def\docommand##1% {\setvalue{\@shortstyle@##1}{#2}% @@ -2823,25 +2399,9 @@ \def\setfontstyle#1#2% #1:name (roman, romaan) #2:style (rm) {\edef\fontstyle{#1}% + \checkfontnamecombinations \setcurrentfontstyle\normalizedbodyfontsize} -\chardef\defaultskewcharmi=127 % '177 -\chardef\defaultskewcharsy= 48 % '60 - -% \def\dosetskewchar#1% -% {\skewchar\font\ifx\@@fontskewchar\empty#1\else\@@fontskewchar\fi} - -\def\dosetskewchar#1#2% - {\ifx\@@fontskewchar\empty - \skewchar\textfont #1#2% - \skewchar\scriptfont #1#2% - \skewchar\scriptscriptfont#1#2% - \else - \skewchar\textfont #1\@@fontskewchar - \skewchar\scriptfont #1\@@fontskewchar - \skewchar\scriptscriptfont#1\@@fontskewchar - \fi} - %D The previous macros show that it's is not always %D neccessary to define the whole bunch of fonts, take for %D instance the sequence: @@ -2878,7 +2438,7 @@ \def\docompletefontswitch[#1]% {\bodyfontsize#1\relax - \dimensiontocount\bodyfontsize\bodyfontpoints + \dimensiontocount\bodyfontsize\bodyfontpoints % rounded, still used in m-chart \edef\bodyfontfactor{\withoutpt\the\bodyfontsize}% \normalizebodyfontsize\bodyfontsize\to\normalizedbodyfontsize \dosetbodyfontface \textface \s!text @@ -2948,8 +2508,7 @@ \def\fastswitchtobodyfont#1% {\ifcsname\??ft\normalizedbodyfontsize#1\endcsname - \edef\futurebodyfontsize - {\csname\??ft\normalizedbodyfontsize#1\endcsname}% + \edef\futurebodyfontsize{\csname\??ft\normalizedbodyfontsize#1\endcsname}% \ifcsname\@size@\futurebodyfontsize\endcsname \csname\@size@\futurebodyfontsize\endcsname \localbodyfontsize\futurebodyfontsize\relax @@ -2958,46 +2517,10 @@ \csname\@style@\fontstyle\endcsname \the\everybodyfont} -%D Because the last macro can appear in arguments or be assigned -%D to parameters, we protect this one for unwanted expansion. - -\def\dodosetmathfont#1% - {\setcurrentfontalternative{#1}% - % \doifdefinedelse{#1\s!fam} % adapted - % {\edef\mffam{\getvalue{#1\s!fam}}} - % {\edef\mffam{\getvalue{\c!nn\s!fam}}}% - \textfont \mrfam\textfont \mffam - \scriptfont \mrfam\scriptfont \mffam - \scriptscriptfont\mrfam\scriptscriptfont\mffam} - -\def\domffam#1% - {\csname\ifcsname#1\s!fam\endcsname#1\else\c!nn\fi\s!fam\endcsname} - -\def\mffam - {\domffam\fontalternative} - -\def\dosetmathfont - {\def\rm{\fam\mrfam}\dodosetmathfont} - -\def\enableencodinginmath - {\appendtoks - \everyhbox{\mr\everyhbox\emptytoks}% - \everyvbox{\mr\everyvbox\emptytoks}% - \to \everymathematics} % was \everymath - -% \enableencodinginmath % too untested to enable by default - %D \starttyping %D $\cases{& \ccaron}$ $x=\hbox{\ccaron $x=\hbox{\ccaron}$}$ %D \stoptyping -%D The font specific features are bound to the filename. - -\def\updatefontparameters - {\edef\@@fontfeatures {\truefontdata\somefontfile\s!features}% - \edef\@@fontfallbacks{\truefontdata\somefontname\s!fallbacks}% - \edef\@@fontskewchar {\truefontdata\somefontfile\s!skewchar}} % will be replaced - \def\setfontcharacteristics {\the\everyfont} @@ -3020,27 +2543,53 @@ {\dotripleargument\dodefinefontfeature} \def\dodefinefontfeature[#1][#2][#3]% - {\ctxlua{fonts.define.specify.preset_context("#1","#2","#3")}} + {\global\expandafter\chardef\csname\??fq=#1\endcsname + \ctxlua{tex.write(fonts.define.specify.preset_context("#1","#2","#3"))}\relax} \definefontfeature [default] - [liga=yes,kern=yes,tlig=yes,trep=yes] % texligatures=yes,texquotes=yes + [%mode=node,% + liga=yes,kern=yes,tlig=yes,trep=yes] % texligatures=yes,texquotes=yes \definefontfeature [smallcaps] - [liga=yes,kern=yes,tlig=yes,trep=yes,smcp=yes] % texligatures=yes,texquotes=yes + [%mode=node,% + liga=yes,kern=yes,tlig=yes,trep=yes,smcp=yes] % texligatures=yes,texquotes=yes \definefontfeature [oldstyle] - [liga=yes,kern=yes,tlig=yes,trep=yes,onum=yes] % texligatures=yes,texquotes=yes + [%mode=node,% + liga=yes,kern=yes,tlig=yes,trep=yes,onum=yes] % texligatures=yes,texquotes=yes -\definefontfeature % no calt +\definefontfeature [arabic] - [mode=node,language=dflt,script=arab, + [mode=node,language=dflt,script=arab,ccmp=yes, init=yes,medi=yes,fina=yes,isol=yes, - liga=yes,dlig=yes,rlig=yes,clig=yes, + liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes, mark=yes,mkmk=yes,kern=yes,curs=yes] +\definefontfeature + [none] + [mode=none,features=off] + +\definefontfeature + [virtualmath] + [mode=base,liga=yes,kern=yes,tlig=yes,trep=yes] + +% for the moment here, this will change but we need it for mk.tex + +\definefontfeature[math-text] [virtualmath][ssty=no] +\definefontfeature[math-script] [virtualmath][ssty=1,mathsize=yes] +\definefontfeature[math-scriptscript][virtualmath][ssty=2,mathsize=yes] + +\definefontfeature [math-nostack-text] [math-text] [nostackmath=yes] +\definefontfeature [math-nostack-script] [math-script] [nostackmath=yes] +\definefontfeature [math-nostack-scriptscript][math-scriptscript][nostackmath=yes] + +% \definefontfeature[mathtext] [math-text] +% \definefontfeature[mathscript] [math-script] +% \definefontfeature[mathscriptscript] [math-scriptscript] + %D Also new: % handy for manuals @@ -3052,32 +2601,9 @@ \definecolor[font:init][r=.75] \definecolor[font:medi][g=.75] \definecolor[font:fina][b=.75] -\definecolor[font:isol][y=.75] -\definecolor[font:mark][m=.75] -\definecolor[font:rest][c=.75] - -%D goodies: -%D -%D \starttyping -%D \showinstalledfonts[officinasans.*][all] -%D \showinstalledfonts[officinaserif.*][all] -%D \showinstalledfonts[officina.*itc.*][all] -%D -%D \showinstalledfonts[officina.*itc.*][all,new] -%D \stoptyping - -\def\showinstalledfonts - {\dodoubleempty\doshowinstalledfonts} - -\def\doshowinstalledfonts[#1][#2]% - {\bgroup - \def\pattern{#1}% - \def\all{false}% - \def\reload{false}% - \doifnothing\pattern{\def\pattern{.*}}% - \processallactionsinset[#2][\v!new=>\def\reload{true},\v!all=>\def\all{true}]% - \ctxlua{fonts.names.table("#1",\reload,\all)}% - \egroup} +\definecolor[font:isol][r=.75,g=.75] % [y=.75] +\definecolor[font:mark][r=.75,b=.75] % [m=.75] +\definecolor[font:rest][g=.75,b=.75] % [c=.75] %D Experimental! @@ -3085,7 +2611,7 @@ {\dodoubleargument\doinstallfontfeature} \def\doinstallfontfeature[#1][#2]% - {\ctxlua{fonts.install_feature("#1","#2")}} + {\writestatus\m!fonts{installing font features was experimental}} % \ctxlua{fonts.install_feature("#1","#2")}} %D Not yet in \MKII. @@ -3099,13 +2625,35 @@ \let\currentfeature\empty +% ! ! ! very experimental, some test code for idris advanced features ! ! ! +% +% \startbuffer +% \definefontfeature[smallcaps][smallcaps][script=latn] +% \definefontfeature[oldstyle] [oldstyle] [script=latn] +% +% \definedfont[name:cambria at 15pt] +% +% Hello there {\setff{smallcaps}capped 123 \setff{oldstyle}123!} \blank +% Hello there {\addff{smallcaps}capped 123 \addff{oldstyle}123!} \blank +% Hello there {\addff{smallcaps}capped \subff{smallcaps}normal} \blank +% \stopbuffer +% +% \typebuffer \getbuffer + \def\featureattribute#1{\ctxlua{tex.sprint(fonts.define.specify.context_number("#1"))}} \def\setfontfeature #1{\edef\currentfeature{#1}\attribute\zerocount\featureattribute{#1}\relax} -\def\resetfontfeature#1{\let\currentfeature\empty\attribute\zerocount\zerocount} +\def\resetfontfeature#1{\let\currentfeature\empty\attribute\zerocount\zerocount} % initial value -\appendtoks - \setfontfeature\currentfeature -\to \everylanguage +\def\addfontfeaturetoset #1{\ctxlua{fonts.withset("#1", 1)}} +\def\subtractfontfeaturefromset #1{\ctxlua{fonts.withset("#1",-1)}} +\def\addfontfeaturetofont #1{\ctxlua{fonts.withfnt("#1", 2)}} +\def\subtractfontfeaturefromfont#1{\ctxlua{fonts.withfnt("#1",-2)}} + +\let\setff\setfontfeature +\let\addfs\addfontfeaturetoset +\let\subfs\subtractfontfeaturefromset +\let\addff\addfontfeaturetofont +\let\subff\subtractfontfeaturefromfont %D The next auxilliary macro is an alternative to \type %D {\fontname}. @@ -3140,75 +2688,23 @@ \the\everybodyfont} % needed ? %D \macros -%D {os,frak, goth, cal} -%D -%D Old style numerals can be typeset with \type{\os} and look -%D like {\os 1234567890} instead of the more common looking -%D 1234567890. +%D {os} %D -%D On behalf of {\frac Tobias Burnus}, we define some more of -%D these. Later we will link these names to real file names. +%D In good old \TEX, the old style numerals were often taken +%D from the math fonts. No longer. -% older -% -% \definefont [os] [OldStyle sa *] -% \definefont [frak] [Fraktur sa *] -% \definefont [goth] [Gothic sa *] -% \definefont [cal] [Calligraphic sa *] -% \definefont [bbd] [Blackboard sa *] -% -% newer - -\def\os {\mathortext{\fam\purefamily {oldstyle}}{\symbolicfont {OldStyle}}} -\def\frak{\mathortext{\fam\purefamily {fraktur}}{\symbolicfont {Fraktur}}} -\def\goth{\mathortext{\fam\purefamily {gothic}}{\symbolicfont {Gothic}}} -\def\cal {\mathortext{\fam\purefamily{calligraphic}}{\symbolicfont{Calligraphic}}} -\def\bbd {\mathortext{\fam\purefamily {blackboard}}{\symbolicfont {Blackboard}}} +\definefontfeature + [just-os] + [mode=node,onum=yes] -\definefontsynonym [OldStyle] [Serif] -\definefontsynonym [Fraktur] [Serif] -\definefontsynonym [Gothic] [Serif] -\definefontsynonym [Calligraphic] [Serif] -\definefontsynonym [Blackboard] [Serif] +% \def\sc{\setfontfeature{smallcaps}} +\def\os{\setfontfeature{just-os}} -%D \macros -%D {fraktur, gothic, calligraphic, blackboard} -%D -%D These macros assume that we use text fonts, and not math -%D families. +%D Code for swithcing to fraktur and script has also been +%D changed. We now have an alphabet switcher. \ifx\mathtext\undefined \let\mathtext\hbox \fi -\def\fraktur #1{\mathortext\domathtext\donothing{\frak#1}} -\def\gothic #1{\mathortext\domathtext\donothing{\goth#1}} -\def\calligraphic#1{\mathortext\domathtext\donothing{\cal #1}} -\def\blackboard #1{\mathortext\domathtext\donothing{\bbd#1}} - -%D Torture test: -%D -%D \starttyping -%D \usetypescript[modern] [texnansi] -%D \usetypescript[lucida] [texnansi] -%D \usetypescript[palatino][texnansi] -%D \usetypescript[times] [texnansi] -%D \usetypescript[fourier] [ec] -%D -%D \startbuffer -%D \section{\blackboard{T\high{\blackboard{T}}} \blackboard{E}\high{\blackboard{E}} \blackboard{X}\high{\blackboard{X}}} -%D -%D {\fontclass: 123 \os123 \cal TEX $\os 123$} -%D -%D $\blackboard{T}^{\blackboard{T}} \blackboard{E}^{\blackboard{E}} \blackboard{X}^{\blackboard{X}}$ -%D \blackboard{T}\high{\blackboard{T}} \blackboard{E}\high{\blackboard{E}} \blackboard{X}\high{\blackboard{X}} -%D \stopbuffer -%D -%D {\setupbodyfont[lucida] \getbuffer} -%D {\setupbodyfont[modern] \getbuffer} -%D {\setupbodyfont[palatino] \getbuffer} -%D {\setupbodyfont[times] \getbuffer} -%D {\setupbodyfont[fourier] \getbuffer} -%D \stoptyping - %D \macros %D {definebodyfontswitch} %D @@ -3264,7 +2760,7 @@ %D in the main bodyfont and style of the document. Returning to %D the global state can be done with the next macro: -\let\mainfontclass\empty +\let\globalfontstyle\c!rm \def\fullrestoreglobalbodyfont {\let\fontsize\defaultfontsize @@ -3420,7 +2916,7 @@ %D \stoptyping %D %D We deliberately pass an argument. This enables us to -%D assign converters that handle one agrument, like +%D assign converters that handle one argument, like %D \type{\cap}. %D %D By default the first specification is used to set the style, @@ -3429,43 +2925,11 @@ %D \type{\noconvertfont}. In nested calls, we can restore the %D conversion by saying \type{\redoconvertfont}. -% \def\@@dodoconvertfont#1{\csname\@letter@ #1\endcsname} -% \def\@@donoconvertfont#1{\csname\@noletter@#1\endcsname} -% -% \unexpanded\def\dodoconvertfont#1% #2% we need the protection -% {\doifdefinedelse{\@letter@#1} % in testing -% {\doifelsenothing{#1}\gobbleoneargument\@@dodoconvertfont} -% {\doifdefinedelse{#1}\getvalue \firstofoneargument}% -% {#1}} % {#2}} -% -% \let\doconvertfont\dodoconvertfont -% -% \def\noconvertfont#1% #2% -% {\doifdefinedelse{\@noletter@#1} -% {\doifelsenothing{#1}\gobbleoneargument\@@donoconvertfont}\gobbleoneargument -% {#1}} % {#2}} - -% \def\@@dodoconvertfont{\csname\@letter@ \p!defined\endcsname} -% \def\@@donoconvertfont{\csname\@noletter@\p!defined\endcsname} -% \def\@@redoconvertfont{\csname \p!defined\endcsname} -% -% \unexpanded\def\dodoconvertfont#1% #2% we need the protection -% {\edef\p!defined{#1}% -% \ifcsname\@letter@\detokenize\@EA{\p!defined}\endcsname -% \ifx\p!defined\empty\else\@EAEAEA\@@dodoconvertfont\fi -% \else -% \ifcsname\detokenize\@EA{\p!defined}\endcsname\@EAEAEA\@@redoconvertfont\else\@EAEAEA\p!defined\fi -% \fi} % {#2}} -% -% \unexpanded\def\noconvertfont#1% #2% -% {\edef\p!defined{#1}% -% \ifcsname\@noletter@\detokenize\@EA{\p!defined}\endcsname -% \ifx\p!defined\empty\else\@EAEAEA\@@donoconvertfont\fi -% \fi} % {#2}} +% subtle ... \expandafter is needed else problems with lookahead caps -\def\@@dodoconvertfont{\csname\@letter@ \p!defined\endcsname\gobbleoneargument} +\def\@@dodoconvertfont{\csname\@letter@ \p!defined\expandafter\endcsname\gobbleoneargument} \def\@@donoconvertfont{\csname\@noletter@\p!defined\endcsname} -\def\@@redoconvertfont{\csname \p!defined\endcsname\gobbleoneargument} +\def\@@redoconvertfont{\csname \p!defined\expandafter\endcsname\gobbleoneargument} % beware: p!defined can contain crap like \edef crap {...} and such % so we need to pass #1 as well @@ -3501,11 +2965,8 @@ %D Extras: -\unexpanded\def\dontconvertfont - {\let\doconvertfont\noconvertfont} - -\unexpanded\def\redoconvertfont - {\let\doconvertfont\dodoconvertfont} +\unexpanded\def\dontconvertfont{\let\doconvertfont\noconvertfont} +\unexpanded\def\redoconvertfont{\let\doconvertfont\dodoconvertfont} %D These commands are not grouped! Grouping is most probably %D done by the calling macro's and would lead to unnecessary @@ -3534,13 +2995,7 @@ %D or even better: -% \def\doemphasistypeface#1#2% -% {\doifelsevalue{\??ft\fontclass\normalizedbodyfontsize\c!em}\v!slanted#1% -% {\doifelsevalue{\??ft\fontclass\normalizedbodyfontsize\c!em}\v!italic #2% -% {\doifelsevalue{\??ft \normalizedbodyfontsize\c!em}\v!slanted#1% -% {\doifvalue {\??ft \normalizedbodyfontsize\c!em}\v!italic #2}}}} - -\def\doemphasistypeface#1#2% +\def\doemphasistypeface#1#2% slow {\doifelsevalue{\??ft\fontclass\normalizedbodyfontsize\c!em}\v!slanted {#1}% {\doifelsevalue{\??ft\fontclass\normalizedbodyfontsize\c!em}\v!italic @@ -3553,6 +3008,25 @@ {\getvalue{\??ft\normalizedbodyfontsize\c!em}}}} {\getvalue{\??ft\fontclass\normalizedbodyfontsize\c!em}}}}} +% \def\doemphasistypeface#1#2% +% {\edef\emphasizedtypeface{\csname\??ft\fontclass\normalizedbodyfontsize\c!em\endcsname}% +% \ifx\emphasizedtypeface\v!slanted +% #1% +% \else\ifx\emphasizedtypeface\v!italic +% #2% +% \else\ifx\emphasizedtypeface\v!empty +% \edef\emphasizedtypeface{\csname\??ft\normalizedbodyfontsize\c!em\endcsname}% +% \ifx\emphasizedtypeface\v!slanted +% #1% +% \else\ifx\emphasizedtypeface\v!italic +% #2% +% \else +% \getvalue\emphasizedtypeface +% \fi\fi +% \else +% \getvalue\emphasizedtypeface +% \fi\fi\fi} + \def\emphasistypeface{\doemphasistypeface\sl\it} \def\emphasisboldface{\doemphasistypeface\bs\bi} @@ -3571,15 +3045,15 @@ \setfalse\emneeded \fi \setemphasisboldface % new - \ifx\fontalternative\c!it % \ifnum\fam=\itfam + \ifx\fontalternative\c!it \def\emphasistypeface{\it}\tf - \else\ifx\fontalternative\c!sl % \ifnum\fam=\slfam + \else\ifx\fontalternative\c!sl \def\emphasistypeface{\sl}\tf - \else\ifx\fontalternative\c!bf % \ifnum\fam=\bffam + \else\ifx\fontalternative\c!bf \emphasisboldface - \else\ifx\fontalternative\c!bs % \ifnum\fam=\bsfam + \else\ifx\fontalternative\c!bs \def\emphasisboldface{\bs}\bf - \else\ifx\fontalternative\c!bi % \ifnum\fam=\bifam + \else\ifx\fontalternative\c!bi \def\emphasisboldface{\bi}\bf \else \emphasistypeface @@ -3605,9 +3079,9 @@ \unexpanded\def\bf {%\relax \let\bf\relax % new - \ifx\fontalternative\c!it % \ifnum\fam=\itfam + \ifx\fontalternative\c!it \bi - \else\ifx\fontalternative\c!sl % \ifnum\fam=\slfam + \else\ifx\fontalternative\c!sl \bs \else \normalbf @@ -3619,24 +3093,35 @@ %D look for something that looks like a dash, in which case we %D don't correct. -\let\italiccorrection=\/ +\let\italiccorrection=\/ % tex primitive \def\emphasiscorrection {\ifhmode \expandafter\emphasislook \fi} +% \def\emphasislook +% {\begingroup +% \beginrobusttest +% \futurelet\next\emphasistest} + +% \def\emphasistest +% {\normalifcat\noexpand\next,% +% \endrobusttest\expandafter\doemphasiscorrection +% \normalelse +% \endrobusttest\expandafter\dododoemphasiscorrection +% \normalfi} + \def\emphasislook {\begingroup - \beginrobusttest \futurelet\next\emphasistest} \def\emphasistest - {\normalifcat\noexpand\next,% - \endrobusttest\expandafter\doemphasiscorrection - \normalelse - \endrobusttest\expandafter\dododoemphasiscorrection - \normalfi} + {\ifcat\noexpand\next,% still ok? + \expandafter\doemphasiscorrection + \else + \expandafter\dododoemphasiscorrection + \fi} \def\doemphasiscorrection {\futurelet\next\dodoemphasiscorrection} @@ -3792,11 +3277,6 @@ %D \stopbuffer %D %D \typebuffer -%D -%D Below the table the name, encoding, mapping and handling are -%D shown. Special characters like the \type {\skewchar} and -%D \type {\hyphenchar} als marked. -%D %D \getbuffer % to be internationalized @@ -3954,49 +3434,6 @@ %D The shape as well as the size is adapted to the current %D environment. -%D Fonts can only be used when loaded. In \CONTEXT\ we -%D postpone the loading of fonts, even when we load \PLAIN. -%D This means that we have to redefine one of the \PLAIN\ -%D macros. Let's tell that to the user first: - -\writestatus{loading}{Postponed Plain TeX Font Definitions} - -%D \macros -%D {bordermatrix} -%D -%D In \PLAIN\ \TEX\ the width of a parenthesis is stored in -%D the \DIMENSION\ \type{\p@renwd}. This value is derived from -%D the width of \type{\tenrm B}, so let's take care of it now: - -\let\normalbordermatrix=\bordermatrix - -\def\bordermatrix% - {\bgroup - \setbox0\hbox{\getvalue{\textface\c!mm\c!ex}B}% - \global\p@renwd\wd0\relax - \egroup - \normalbordermatrix} - -%D Because we want to be as \PLAIN\ compatible as possible, we -%D make most of \PLAIN's font mechanisme available to the -%D \CONTEXT\ user. - -\def\setplainfonts#1#2% - {\setvalue {ten#1}{\getvalue{\!!tenpoint #2}}% - \setvalue{seven#1}{\getvalue{\!!sevenpoint#2}}% - \setvalue {five#1}{\getvalue{\!!fivepoint #2}}} - -\setplainfonts {\c!rm} {\c!rm\c!tf} -\setplainfonts {\c!bf} {\c!rm\c!bf} -\setplainfonts {\c!sl} {\c!rm\c!sl} -\setplainfonts {\c!it} {\c!rm\c!it} -\setplainfonts {\c!tt} {\c!rm\c!tt} -\setplainfonts {\c!sy} {\c!mm\c!sy} -\setplainfonts {\c!ex} {\c!mm\c!ex} -\setplainfonts {\c!i} {\c!mm\c!mi} - -\let\setplainfonts=\undefined - %D \macros %D {ss, SS, sz} %D @@ -4008,81 +3445,11 @@ \ifx\undefined\SS \let\SS=\ss \fi \ifx\undefined\sz \let\sz=\ss \fi -%D \macros -%D {xi} -%D -%D We are going to redefine \type{\xi}, but fortunately this -%D is a math mode character, so we can just say: - -\let\normalxi=\xi - -%D \macros -%D {smashaccent} -%D -%D When we let \TEX\ put an accent on top of a character, such -%D composed characters can get more height that height of a -%D standard \type{\strut}. The next macro takes care of such -%D unwanted compositions. -%D -%D We need to reach over the number that specifies the accent, -%D and in doing so we use \type{\scratchcounter} as a placeholder -%D because it accepts 8 bit numbers in octal, decimal or -%D hexadecimal format. Next we set the height of the accented -%D character to the natural height of the character. - -\unexpanded\def\smashaccent#1% - {\dontleavehmode - \bgroup - \setbox\scratchbox\hbox{#1}% - \ifdim\ht\scratchbox>\strutheight\relax\ht\scratchbox\strutheight\fi - \ifdim\dp\scratchbox>\strutdepth \relax\dp\scratchbox\strutdepth \fi - \box\scratchbox - \egroup} - -%D For instance we can say: -%D -%D \starttyping -%D \smashaccent{\"Uberhaupt} -%D \stoptyping -%D -%D But normally one will use it as a prefix in definitions. -%D The difference is in the height: -%D -%D \leavevmode\ruledhbox -%D {\ruledhbox{\smashaccent{\"U}berhaupt}\quad -%D oder\quad -%D \ruledhbox{\"Uberhaupt}} - -%D \macros -%D {moveaccent} -%D -%D Exact positioning of accents can be realized by saying: -%D -%D \starttyping -%D \moveaccent{-.1ex}{\"u}berhaupt -%D \stoptyping -%D -%D Again, this one will mostly used as a prefix in definitions. -%D Here the difference is in the position: -%D -%D \leavevmode\ruledhbox -%D {\ruledhbox{\moveaccent{-.1ex}{\"}Uberhaupt}\quad -%D oder\quad -%D \ruledhbox{\"Uberhaupt}} - -\unexpanded\def\moveaccent#1#2% - {\smashaccent - {\dimen0\exheight - \dimen2\dimen0 - \advance\dimen2 -#1% - \exheight\dimen2 - #2\relax - \exheight\dimen0}} - -%D Personally I think that using \TEX\ is complicated by the -%D way fonts are handled. Apart from the many encodings, we -%D also deal with different naming schemes. Confronted with -%D this problem, I decided to change the definitions into: +%D Personally I think that using \TEX\ macro packages is +%D complicated by the way fonts are handled. Apart from the +%D many encodings, we also deal with different naming schemes. +%D Confronted with this problem, I decided to change the +%D definitions into: %D %D \starttyping %D \definebodyfont [12pt] [rm] [tf=Times-Roman at 12pt] @@ -4244,22 +3611,31 @@ \definebodyfontswitch [fivepoint] [\!!fivepoint] \definebodyfontswitch [fourpoint] [\!!fourpoint] -\definebodyfontswitch [xii] [\!!twelvepoint] -\definebodyfontswitch [xi] [\!!elevenpoint] -\definebodyfontswitch [x] [\!!tenpoint] -\definebodyfontswitch [ix] [\!!ninepoint] -\definebodyfontswitch [viii] [\!!eightpoint] -\definebodyfontswitch [vii] [\!!sevenpoint] -\definebodyfontswitch [vi] [\!!sixpoint] +% \definebodyfontswitch [xii] [\!!twelvepoint] +% \definebodyfontswitch [xi] [\!!elevenpoint] +% \definebodyfontswitch [x] [\!!tenpoint] +% \definebodyfontswitch [ix] [\!!ninepoint] +% \definebodyfontswitch [viii] [\!!eightpoint] +% \definebodyfontswitch [vii] [\!!sevenpoint] +% \definebodyfontswitch [vi] [\!!sixpoint] %D So far. +\definefontstyle [\c!mm] [\c!mm] \definefontstyle [\c!rm,\v!roman,\v!serif,\v!regular] [\c!rm] \definefontstyle [\c!ss,\v!sansserif,\v!sans,\v!support] [\c!ss] \definefontstyle [\c!tt,\v!teletype,\v!type,\v!mono] [\c!tt] \definefontstyle [\c!hw,\v!handwritten] [\c!hw] \definefontstyle [\c!cg,\v!calligraphic] [\c!cg] +\definefontalternative[\c!tf] +\definefontalternative[\c!bf] +\definefontalternative[\c!it] +\definefontalternative[\c!sl] +\definefontalternative[\c!bs] +\definefontalternative[\c!bi] +\definefontalternative[\c!sc] + \definefontsize[\c!a] \definefontsize[\c!b] \definefontsize[\c!c] \definefontsize[\c!d] @@ -4294,42 +3670,12 @@ \definealternativestyle [\v!smallcaps] [\sc] [\sc] \definealternativestyle [\v!WORD] [\WORD] [\WORD] -%D \macros -%D {...math} -%D -%D New or old? - -% tzt proper \define... -% -% watch out: \synchronizesymb resets the family so we need a second -% \mf (or maybe \mr): messy and to be sorted out - -\def\tfmath{\tf\mf\synchronizesymb\mf} -\def\bfmath{\bf\mf\synchronizesymb\mf} -\def\slmath{\sl\mf\synchronizesymb\mf} -\def\itmath{\it\mf\synchronizesymb\mf} -\def\bsmath{\bs\mf\synchronizesymb\mf} -\def\bimath{\bi\mf\synchronizesymb\mf} -\def\scmath{\sc\mf\synchronizesymb\mf} -\def\nnmath{\nn\mf\synchronizesymb\mf} - -\def\textmath {\synchronizesymb} - %D \macros %D {fontstylesuffix} %D %D The next macro is used to map non latin fontnames on %D fonts. See \type {font-uni} for an example of its use. -%\def\fontstylesuffix% -% {\ifnum\fam=\tffam \s!Regular \else -% \ifnum\fam=\bffam \s!Bold \else -% \ifnum\fam=\slfam \s!Slanted \else -% \ifnum\fam=\itfam \s!Italic \else -% \ifnum\fam=\bsfam \s!BoldSlanted \else -% \ifnum\fam=\bifam \s!BoldItalic \else -% \s!Regular \fi\fi\fi\fi\fi\fi}% - \def\fontstylesuffix% why the \s!Regular ? see \getglyph {\ifx\fontalternative\c!tf \s!Regular \else \ifx\fontalternative\c!bf \s!Bold \else @@ -4340,10 +3686,6 @@ \ifx\fontalternative\c!sc \s!Caps \else \s!Regular \fi\fi\fi\fi\fi\fi\fi}% -%D We still have to take care of \type{\xi}, so: - -\def\xi{\ifmmode\normalxi\else\elevenpoint\fi} - %D \macros %D {definefontvariant,fontvariant,variant} %D @@ -4383,7 +3725,7 @@ \unexpanded\def\variant[#1]% slow {\dosetscaledfont - \expanded{\definedfont[\fontstringA\fontstylesuffix\fontvariant\fontstringA{#1} at \the\dimexpr\scaledfontsize\relax]}% + \normalexpanded{\noexpand\definedfont[\fontstringA\fontstylesuffix\fontvariant\fontstringA{#1} at \the\dimexpr\scaledfontsize\relax]}% \ignoreimplicitspaces} \ifx\Var\undefined \let\Var\variant \fi @@ -4393,262 +3735,113 @@ %D bodyfont. Sans serif and teletype are also available and %D can be called for by \type{\ss} and \type{\tt}. -\setupbodyfont [unk, rm] +% \setupbodyfont [unk, rm] +% \setupbodyfont [rm] %D Also needed is: \definefont[tinyfont][Mono at 1ex] -%D \macros -%D {doiffontpresentelse} -%D -%D \starttyping -%D \doiffontpresentelse{texnansi-lmr10}{YES}{NO} -%D \doiffontpresentelse{adam-lindsay-modern-serif}{YES}{NO} -%D \stoptyping +% \tracinglostchars=1 -\def\doiffontpresentelse#1{\ctxlua{commands.doifelse(fonts.names.exists("#1"))}} +% this needs some interfacing +% +% \setupfonts[check=...] -%D OPTIMIZATIONS +\def\checkcharactersinfont {\ctxlua{fonts.checkers.enabled=true}} +\def\removemissingcharacters{\ctxlua{fonts.checkers.enabled=true fonts.checkers.delete=true}} -\def\definefontsynonym[#1]#2[#3]% - {\edef\@@fontname{#1}% - \edef\@@fontfile{#3}% - \@EA\let\csname\??ff\fontclass\@@fontname\endcsname\@@fontfile % maybe just #1 #3, saves expansion - \doifnextcharelse[\dodefinefontsynonym\nodefinefontsynonym} +%D New commands (not yet interfaced): -\def\dodefinefontsynonym[#1]% - {\let\@@ff@@features \undefined - \let\@@ff@@fallbacks\undefined - \let\@@ff@@skewchar \undefined - \expandafter\dogetfontparameter#1,]=,} +\def\style[#1]% for inline usage, like \color + {\groupedcommand{\ifcsname#1\endcsname\csname#1\endcsname\else\definedfont[#1]\fi}{}} -\def\dogetfontparameter#1=#2,% - {\if]#1% - \dododefinefontsynonym - \else - \expandafter\def\csname @@ff@@#1\endcsname{#2}% - \expandafter\dogetfontparameter - \fi} +\def\startstyle[#1]% + {\begingroup + \ifcsname#1\endcsname\csname#1\endcsname\else\definedfont[#1]\fi} -\def\nodefinefontsynonym - {\ifx\fontclass\empty - \@EA\let\csname\??ff\@@fontname\s!features \endcsname\undefined - \@EA\let\csname\??ff\@@fontname\s!fallbacks\endcsname\undefined - \@EA\let\csname\??ff\@@fontfile\s!skewchar \endcsname\undefined - \else - \global\@EA\let\csname\??ff\fontclass\@@fontname\s!features \endcsname\undefined - \global\@EA\let\csname\??ff\fontclass\@@fontname\s!fallbacks\endcsname\undefined - \global\@EA\let\csname\??ff\fontclass\@@fontfile\s!skewchar \endcsname\undefined - \fi} +\def\stopstyle + {\endgroup} -\def\dododefinefontsynonym - {\ifx\fontclass\empty - \@EA\let\csname\??ff\@@fontname\s!features \endcsname\@@ff@@features - \@EA\let\csname\??ff\@@fontname\s!fallbacks\endcsname\@@ff@@fallbacks - \@EA\let\csname\??ff\@@fontfile\s!skewchar \endcsname\@@ff@@skewchar - \else - \global\@EA\let\csname\??ff\fontclass\@@fontname\s!features \endcsname\@@ff@@features - \global\@EA\let\csname\??ff\fontclass\@@fontname\s!fallbacks\endcsname\@@ff@@fallbacks - \global\@EA\let\csname\??ff\fontclass\@@fontfile\s!skewchar \endcsname\@@ff@@skewchar - \fi} +%D Still experimental (might even go away). -\def\truefontname#1% - {\ifcsname\??ff\fontclass#1\endcsname - \@EA\truefontname\csname\??ff\fontclass#1\endcsname - \else\ifcsname\??ff#1\endcsname - \@EA\truefontname\csname\??ff#1\endcsname - \else - #1% - \fi\fi} +% \definestylecollection[mine] -\def\updatefontparameters - {\edef\@@fontfeatures {\@@thefeatures \somefontname}% - \edef\@@fontfallbacks{\@@thefallbacks\somefontname}% - \edef\@@fontskewchar {\@@theskewchar \somefontname}} - -\def\@@thefeatures#1% - {\ifcsname\??ff\fontclass#1\s!features\endcsname \csname\??ff\fontclass#1\s!features\endcsname\else % class + symbolic_name - \ifcsname\??ff #1\s!features\endcsname \csname\??ff #1\s!features\endcsname\else % symbolic_name - \ifcsname\??ff\fontclass#1\endcsname \@EA\@@thefeatures\csname\??ff\fontclass#1\endcsname \else % fontclass + parent_name - \ifcsname\??ff #1\endcsname \@EA\@@thefeatures\csname\??ff #1\endcsname \fi\fi\fi\fi} % parent_name - -\def\@@thefallbacks#1% - {\ifcsname\??ff\fontclass#1\s!fallbacks\endcsname \csname\??ff\fontclass#1\s!fallbacks\endcsname\else % class + symbolic_name - \ifcsname\??ff #1\s!fallbacks\endcsname \csname\??ff #1\s!fallbacks\endcsname\else % symbolic_name - \ifcsname\??ff\fontclass#1\endcsname \@EA\@@thefallbacks\csname\??ff\fontclass#1\endcsname \else % fontclass + parent_name - \ifcsname\??ff #1\endcsname \@EA\@@thefallbacks\csname\??ff #1\endcsname \fi\fi\fi\fi} % parent_name - -\def\@@theskewchar#1% skew chars will be done differently (just a hash with registered skewchars) - {\ifcsname\??ff\fontclass#1\s!skewchar\endcsname \csname\??ff\fontclass#1\s!skewchar\endcsname\else % class + symbolic_name - \ifcsname\??ff #1\s!skewchar\endcsname \csname\??ff #1\s!skewchar\endcsname\else % symbolic_name - \ifcsname\??ff\fontclass#1\endcsname \@EA\@@theskewchar\csname\??ff\fontclass#1\endcsname \else % fontclass + parent_name - \ifcsname\??ff #1\endcsname \@EA\@@theskewchar\csname\??ff #1\endcsname \fi\fi\fi\fi} % parent_name - -% more efficient ? +% \definestyleinstance[mine][default][sorry] +% \definestyleinstance[mine][tt][bs][ttbs:\rm\sl] +% \definestyleinstance[mine][tt][bf][ttbf:\rm\sl] +% \definestyleinstance[mine][bf][\sl] +% \definestyleinstance[mine][sl][\tt] -\def\definefontsynonym[#1]#2[#3]% - {\edef\@@fontname{#1}% - \edef\@@fontfile{#3}% - \ifx\fontclass\empty - \expandafter\dodefinefontsynonymnop - \else - \expandafter\dodefinefontsynonymyes - \fi} +% {\bf test \mine test \sl test \mine test \bs oeps \mine oeps {\tt test \mine \bf test}} -\def\dodefinefontsynonymyes - {\@EA\let\csname\??ff\fontclass\@@fontname\endcsname\@@fontfile % maybe just #1 #3, saves expansion - \doifnextcharelse[\dododefinefontsynonymyes\nonodefinefontsynonymyes} -\def\dodefinefontsynonymnop - {\@EA\let\csname\??ff\@@fontname\endcsname\@@fontfile % maybe just #1 #3, saves expansion - \doifnextcharelse[\dododefinefontsynonymnop\nonodefinefontsynonymnop} +\definesystemvariable{sx} -\def\dododefinefontsynonymyes[#1]% - {\let\@@ff@@features \undefined - \let\@@ff@@fallbacks\undefined - \let\@@ff@@skewchar \undefined - \expandafter\dogetfontparameteryes#1,]=,} -\def\dododefinefontsynonymnop[#1]% - {\let\@@ff@@features \undefined - \let\@@ff@@fallbacks\undefined - \let\@@ff@@skewchar \undefined - \expandafter\dogetfontparameternop#1,]=,} +\def\definestylecollection + {\dosingleargument\dodefinestylecollection} -\def\dogetfontparameteryes#1=#2,% - {\if]#1% - \dodododefinefontsynonymyes - \else - \expandafter\def\csname @@ff@@#1\endcsname{#2}% - \expandafter\dogetfontparameteryes - \fi} -\def\dogetfontparameternop#1=#2,% - {\if]#1% - \dodododefinefontsynonymnop - \else - \expandafter\def\csname @@ff@@#1\endcsname{#2}% - \expandafter\dogetfontparameternop +\def\dodefinestylecollection[#1]% + {\iffirstargument + \unexpanded\setvalue{#1}{\styleinstance[#1]}% + \def\docommand##1% + {\def\dodocommand####1{\letbeundefined{\??sx##1:####1:\commalistelement}}% + \processcommacommand[\fontalternativelist,\s!default]\dodocommand}% + \processcommacommand[\fontstylelist,\s!default]\docommand \fi} -\def\nonodefinefontsynonymyes - {\global\@EA\let\csname\??ff\@@fontname\s!features \endcsname\undefined - \global\@EA\let\csname\??ff\@@fontname\s!fallbacks\endcsname\undefined - \global\@EA\let\csname\??ff\@@fontfile\s!skewchar \endcsname\undefined} -\def\nonodefinefontsynonymnop - {\@EA\let\csname\??ff\fontclass\@@fontname\s!features \endcsname\undefined - \@EA\let\csname\??ff\fontclass\@@fontname\s!fallbacks\endcsname\undefined - \@EA\let\csname\??ff\fontclass\@@fontfile\s!skewchar \endcsname\undefined} - -\def\dodododefinefontsynonymyes - {\global\@EA\let\csname\??ff\@@fontname\s!features \endcsname\@@ff@@features - \global\@EA\let\csname\??ff\@@fontname\s!fallbacks\endcsname\@@ff@@fallbacks - \global\@EA\let\csname\??ff\@@fontfile\s!skewchar \endcsname\@@ff@@skewchar} -\def\dodododefinefontsynonymnop - {\@EA\let\csname\??ff\fontclass\@@fontname\s!features \endcsname\@@ff@@features - \@EA\let\csname\??ff\fontclass\@@fontname\s!fallbacks\endcsname\@@ff@@fallbacks - \@EA\let\csname\??ff\fontclass\@@fontfile\s!skewchar \endcsname\@@ff@@skewchar} - -% resolve - -\def\@@thefeaturesyes#1% - {\ifcsname\??ff\fontclass#1\s!features \endcsname\@EA\let\@EA\@@fontfeatures \csname\??ff\fontclass#1\s!features \endcsname\else - \ifcsname\??ff #1\s!features \endcsname\@EA\let\@EA\@@fontfeatures \csname\??ff #1\s!features \endcsname\else - \ifcsname\??ff\fontclass #1\endcsname\@EA \@@thefeaturesyes \csname\??ff\fontclass #1\endcsname\else - \ifcsname\??ff #1\endcsname\@EA \@@thefeaturesyes \csname\??ff #1\endcsname\else - \let \@@fontfeatures \empty \fi\fi\fi\fi} -\def\@@thefallbacksyes#1% - {\ifcsname\??ff\fontclass#1\s!fallbacks\endcsname\@EA\let\@EA\@@fontfallbacks \csname\??ff\fontclass#1\s!fallbacks\endcsname\else - \ifcsname\??ff #1\s!fallbacks\endcsname\@EA\let\@EA\@@fontfallbacks \csname\??ff #1\s!fallbacks\endcsname\else - \ifcsname\??ff\fontclass #1\endcsname\@EA \@@thefallbacksyes\csname\??ff\fontclass #1\endcsname\else - \ifcsname\??ff #1\endcsname\@EA \@@thefallbacksyes\csname\??ff #1\endcsname\else - \let \@@fontfallbacks \empty \fi\fi\fi\fi} -\def\@@theskewcharyes#1% - {\ifcsname\??ff\fontclass#1\s!skewchar \endcsname\@EA\let\@EA\@@fontskewchar \csname\??ff\fontclass#1\s!skewchar \endcsname\else - \ifcsname\??ff #1\s!skewchar \endcsname\@EA\let\@EA\@@fontskewchar \csname\??ff #1\s!skewchar \endcsname\else - \ifcsname\??ff\fontclass #1\endcsname\@EA \@@theskewcharyes \csname\??ff\fontclass #1\endcsname\else - \ifcsname\??ff #1\endcsname\@EA \@@theskewcharyes \csname\??ff #1\endcsname\else - \let \@@fontskewchar \empty \fi\fi\fi\fi} - -\def\@@thefeaturesnop#1% - {\ifcsname\??ff#1\s!features \endcsname\@EA\let\@EA\@@fontfeatures \csname\??ff#1\s!features \endcsname\else - \ifcsname\??ff #1\endcsname\@EA \@@thefeaturesnop \csname\??ff #1\endcsname\else - \let \@@fontfeatures \empty \fi\fi} -\def\@@thefallbacksnop#1% - {\ifcsname\??ff#1\s!fallbacks\endcsname\@EA\let\@EA\@@fontfallbacks \csname\??ff#1\s!fallbacks\endcsname\else - \ifcsname\??ff #1\endcsname\@EA \@@thefallbacksnop\csname\??ff #1\endcsname\else - \let \@@fontfallbacks \empty \fi\fi} -\def\@@theskewcharnop#1% - {\ifcsname\??ff#1\s!skewchar \endcsname\@EA\let\@EA\@@fontskewchar \csname\??ff#1\s!skewchar \endcsname\else - \ifcsname\??ff #1\endcsname\@EA \@@theskewcharnop \csname\??ff #1\endcsname\else - \let \@@fontskewchar \empty \fi\fi} +\def\definestyleinstance + {\doquadrupleargument\dodefinestyleinstance} -\def\updatefontparametersyes - {\@@thefeaturesyes \somefontname - \@@thefallbacksyes\somefontname - \@@theskewcharyes \somefontname} -\def\updatefontparametersnop - {\@@thefeaturesnop \somefontname - \@@thefallbacksnop\somefontname - \@@theskewcharnop \somefontname} - -\def\updatefontparameters - {\ifx\fontclass\empty\updatefontparametersnop\else\updatefontparametersyes\fi} +\def\dodefinestyleinstance[#1][#2][#3][#4]% [name] [rm|ss|tt|..] [sl|bf|...] [whatever] + {\iffirstargument + \doifundefined{#1}{\definestylecollection[#1]}% + \fi + \iffourthargument + \setvalue{\??sx#1:#2:#3}{#4}% + \else\ifthirdargument + \setvalue{\??sx#1::#2}{#3}% + \else\ifsecondargument + \letvalue{\??sx#1::#2}\empty + \fi\fi\fi} + +\unexpanded\def\styleinstance[#1]% will be faster + {%\begingroup\normalexpanded{\noexpand\infofont[#1:\fontstyle:\fontalternative]}\endgroup + \executeifdefined{\??sx#1:\fontstyle:\fontalternative}% + {\executeifdefined{\??sx#1:\fontstyle:\s!default}% + {\executeifdefined{\??sx#1::\fontalternative} + {\getvalue {\??sx#1::\s!default}}}}} + +% \unexpanded\def\styleinstance[#1]% +% {\csname\??sx#1% +% \ifcsname:\fontstyle:\fontalternative\endcsname +% :\fontstyle:\fontalternative +% \else\ifcsname:\fontstyle:\s!default\endcsname +% :\fontstyle:\s!default +% \else\ifcsname::\fontalternative\endcsname +% ::\fontalternative +% \else\ifcsname::\s!default\endcsname +% ::\s!default +% \else +% % nothing, \relax +% \fi\fi\fi\fi +% \endcsname} \protect \endinput -% bewaren -% -% \def\truefontdata#1#2% -% {\ifcsname\??ff\fontclass#1#2\endcsname -% % raw(Regular) raw(key) -% \csname\??ff\fontclass#1#2\endcsname -% \else\ifcsname\??ff\fontclass#1\endcsname -% % exp(palatino Regular) raw(key) -% \expandafter\truefontdata\csname\??ff\fontclass#1\endcsname#2% -% \else\ifcsname\??ff#1\endcsname -% % exp(Regular) raw(key) -% \expandafter\truefontdata\csname\??ff#1\endcsname#2% -% \else\ifcsname\??ff#2\endcsname -% % raw(key) -% \csname\??ff#2\endcsname -% \fi\fi\fi\fi} - -% test file -% -% \starttypescript[serif][mine-1] -% \definefontsynonym[Serif] [TeXGyrePagella-Regular] -% \definefontsynonym[TeXGyrePagella-Regular][file:texgyrepagella-regular] -% \stoptypescript -% -% \starttypescript[serif][mine-2] -% \definefontsynonym[Serif] [TeXGyrePagella-Regular] [features=default] -% \definefontsynonym[TeXGyrePagella-Regular][file:texgyrepagella-regular] [features=oldstyle] -% \stoptypescript -% -% \starttypescript[serif][mine-3] -% \definefontsynonym[Serif] [TeXGyrePagella-Regular] [features=oldstyle] -% \definefontsynonym[TeXGyrePagella-Regular][file:texgyrepagella-regular] [features=default] -% \stoptypescript -% -% \starttypescript[serif][mine-4] -% \definefontsynonym[Serif] [TeXGyrePagella-Regular] [features=default] -% \definefontsynonym[TeXGyrePagella-Regular][file:texgyrepagella-regular] [features=default] -% \stoptypescript -% -% \starttypescript[serif][mine-5] -% \definefontsynonym[Serif] [TeXGyrePagella-Regular] [features=oldstyle] -% \definefontsynonym[TeXGyrePagella-Regular][file:texgyrepagella-regular] [features=oldstyle] -% \stoptypescript -% -% \starttext -% \dorecurse {5} { -% \expanded{\definetypeface[mine-\recurselevel][rm][serif][mine-\recurselevel][default]} -% \expanded{\setupbodyfont [mine-\recurselevel] mine-\recurselevel: text 1234567890 done} -% \par -% } -% \blank -% \dorecurse {5} { -% \expanded{\definetypeface[more-\recurselevel][rm][serif][mine-\recurselevel][default][features=oldstyle]} -% \expanded{\setupbodyfont [more-\recurselevel] mine-\recurselevel: text 1234567890 done} -% \par -% } -% \stoptext +% \startluacode +% function commands.doifelsecurrentfonthasfeature(name) +% local f = fonts.ids[font.current()] +% f = f and f.shared +% f = f and f.otfdata +% f = f and f.luatex +% f = f and f.features +% commands.doifelse(f and (f.gpos[name] or f.gsub[name])) +% end +% \stopluacode + +% \def\doifelsecurrentfonthasfeature#1% +% {\ctxlua{commands.doifelsecurrentfonthasfeature("#1")}} + +% \doifelsecurrentfonthasfeature{smcp}{YES}{NO} +% \doifelsecurrentfonthasfeature{crap}{YES}{NO} +% \doifelsecurrentfonthasfeature{kern}{YES}{NO} diff --git a/tex/context/base/font-jap.tex b/tex/context/base/font-jap.tex index 6bb813ccc..42480df43 100644 --- a/tex/context/base/font-jap.tex +++ b/tex/context/base/font-jap.tex @@ -15,7 +15,7 @@ \ifx\handlejapaneseunicodeglyph\undefined \else \endinput \fi \ifx\handlechineseunicodeglyph \undefined \input font-chi.tex \fi -\writestatus{loading}{Context Font Macros / Japanese} +\writestatus{loading}{ConTeXt Font Macros / Japanese} \unprotect diff --git a/tex/context/base/font-log.lua b/tex/context/base/font-log.lua new file mode 100644 index 000000000..499bd4304 --- /dev/null +++ b/tex/context/base/font-log.lua @@ -0,0 +1,53 @@ +if not modules then modules = { } end modules ['font-log'] = { + version = 1.001, + comment = "companion to font-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local next, format, lower, concat = next, string.format, string.lower, table.concat + +local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) + +fonts.logger = fonts.logger or { } + +--[[ldx-- +The following functions are used for reporting about the fonts +used. The message itself is not that useful in regular runs but since +we now have several readers it may be handy to know what reader is +used for which font.
+--ldx]]-- + +function fonts.logger.save(tfmtable,source,specification) -- save file name in spec here ! ! ! ! ! ! + if tfmtable and specification and specification.specification then + local name = lower(specification.name) + if trace_defining and not fonts.used[name] then + logs.report("define font","registering %s as %s",file.basename(specification.name),source) + end + specification.source = source + fonts.loaded[lower(specification.specification)] = specification + fonts.used[name] = source + end +end + +function fonts.logger.report() + local t = { } + for name, used in table.sortedpairs(fonts.used) do + t[#t+1] = file.basename(name) .. ":" .. used + end + return t +end + +function fonts.logger.format(name) + return fonts.used[name] or "unknown" +end + +statistics.register("loaded fonts", function() + if next(fonts.used) then + local t = fonts.logger.report(separator) + return (#t > 0 and format("%s files: %s",#t,concat(t,separator or " "))) or "none" + else + return nil + end +end) diff --git a/tex/context/base/font-map.lua b/tex/context/base/font-map.lua index 64ff268fb..35cfaf32f 100644 --- a/tex/context/base/font-map.lua +++ b/tex/context/base/font-map.lua @@ -6,6 +6,10 @@ if not modules then modules = { } end modules ['font-map'] = { license = "see context related readme files" } +local match, format, find = string.match, string.format, string.find + +local ctxcatcodes = tex.ctxcatcodes + --[[ldx--Eventually this code will disappear because map files are kind of obsolete. Some code may move to runtime or auxiliary modules.
@@ -29,21 +33,21 @@ function fonts.map.line.pdftex(e) -- so far no combination of slant and stretch local fullname = e.fullname or "" if e.slant and e.slant ~= 0 then if e.encoding then - return fonts.map.line.pdfmapline("=",string.format('%s %s "%g SlantFont" <%s <%s',e.name,fullname,e.slant,e.encoding,e.fontfile)) + return fonts.map.line.pdfmapline("=",format('%s %s "%g SlantFont" <%s <%s',e.name,fullname,e.slant,e.encoding,e.fontfile)) else - return fonts.map.line.pdfmapline("=",string.format('%s %s "%g SlantFont" <%s',e.name,fullname,e.slant,e.fontfile)) + return fonts.map.line.pdfmapline("=",format('%s %s "%g SlantFont" <%s',e.name,fullname,e.slant,e.fontfile)) end elseif e.stretch and e.stretch ~= 1 and e.stretch ~= 0 then if e.encoding then - return fonts.map.line.pdfmapline("=",string.format('%s %s "%g ExtendFont" <%s <%s',e.name,fullname,e.stretch,e.encoding,e.fontfile)) + return fonts.map.line.pdfmapline("=",format('%s %s "%g ExtendFont" <%s <%s',e.name,fullname,e.stretch,e.encoding,e.fontfile)) else - return fonts.map.line.pdfmapline("=",string.format('%s %s "%g ExtendFont" <%s',e.name,fullname,e.stretch,e.fontfile)) + return fonts.map.line.pdfmapline("=",format('%s %s "%g ExtendFont" <%s',e.name,fullname,e.stretch,e.fontfile)) end else if e.encoding then - return fonts.map.line.pdfmapline("=",string.format('%s %s <%s <%s',e.name,fullname,e.encoding,e.fontfile)) + return fonts.map.line.pdfmapline("=",format('%s %s <%s <%s',e.name,fullname,e.encoding,e.fontfile)) else - return fonts.map.line.pdfmapline("=",string.format('%s %s <%s',e.name,fullname,e.fontfile)) + return fonts.map.line.pdfmapline("=",format('%s %s <%s',e.name,fullname,e.fontfile)) end end else @@ -54,7 +58,7 @@ end function fonts.map.flush(backend) -- will also erase the accumulated data local flushline = fonts.map.line[backend or "pdftex"] or fonts.map.line.pdftex for _, e in pairs(fonts.map.data) do - tex.sprint(tex.ctxcatcodes,flushline(e)) + tex.sprint(ctxcatcodes,flushline(e)) end fonts.map.data = { } end @@ -76,27 +80,27 @@ function fonts.map.load_file(filename, entries, encodings) if f then local data = f:read("*a") if data then - for line in data:gmatch("(.-)[\n\t]") do - if line:find("^[%#%%%s]") then + for line in gmatch(data,"(.-)[\n\t]") do + if find(line,"^[%#%%%s]") then -- print(line) else local stretch, slant, name, fullname, fontfile, encoding line = line:gsub('"(.+)"', function(s) - stretch = s:find('"([^"]+) ExtendFont"') - slant = s:find('"([^"]+) SlantFont"') + stretch = find(s,'"([^"]+) ExtendFont"') + slant = find(s,'"([^"]+) SlantFont"') return "" end) if not name then -- name fullname encoding fontfile - name, fullname, encoding, fontfile = line:match("^(%S+)%s+(%S*)[%s<]+(%S*)[%s<]+(%S*)%s*$") + name, fullname, encoding, fontfile = match(line,"^(%S+)%s+(%S*)[%s<]+(%S*)[%s<]+(%S*)%s*$") end if not name then -- name fullname (flag) fontfile encoding - name, fullname, fontfile, encoding = line:match("^(%S+)%s+(%S*)[%d%s<]+(%S*)[%s<]+(%S*)%s*$") + name, fullname, fontfile, encoding = match(line,"^(%S+)%s+(%S*)[%d%s<]+(%S*)[%s<]+(%S*)%s*$") end if not name then -- name fontfile - name, fontfile = line:match("^(%S+)%s+[%d%s<]+(%S*)%s*$") + name, fontfile = match(line,"^(%S+)%s+[%d%s<]+(%S*)%s*$") end if name then if encoding == "" then encoding = nil end diff --git a/tex/context/base/font-mis.lua b/tex/context/base/font-mis.lua new file mode 100644 index 000000000..520f9e7a6 --- /dev/null +++ b/tex/context/base/font-mis.lua @@ -0,0 +1,91 @@ +if not modules then modules = { } end modules ['font-mis'] = { + version = 1.001, + comment = "companion to luatex-fonts.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local next, pairs, ipairs = next, pairs, ipairs +local lower, strip = string.lower, string.strip + +fonts.otf = fonts.otf or { } + +fonts.otf.version = fonts.otf.version or 2.626 +fonts.otf.pack = true +fonts.otf.cache = containers.define("fonts", "otf", fonts.otf.version, true) + +function fonts.otf.loadcached(filename,format,sub) + -- no recache when version mismatch + local name = file.basename(file.removesuffix(filename)) + if sub == "" then sub = false end + local hash = name + if sub then + hash = hash .. "-" .. sub + end + hash = containers.cleanname(hash) + local data = containers.read(fonts.otf.cache(), hash) + if data and not data.verbose then + fonts.otf.enhancers.unpack(data) + return data + else + return nil + end +end + +function fonts.get_features(name,t,script,language) + local t = lower(t or (name and file.extname(name)) or "") + if t == "otf" or t == "ttf" or t == "ttc" then + local filename = resolvers.find_file(name,t) or "" + if filename ~= "" then + local data = fonts.otf.loadcached(filename) + if data and data.luatex and data.luatex.features then + return data.luatex.features + else + local ff = fontloader.open(filename) + if ff then + local data = fontloader.to_table(ff) + fontloader.close(ff) + local features = { } + for k, what in pairs { "gsub", "gpos" } do + local dw = data[what] + if dw then + local f = { } + features[what] = f + for _, d in ipairs(dw) do + if d.features then + for _, df in ipairs(d.features) do + local tag = strip(lower(df.tag)) + local ft = f[tag] if not ft then ft = {} f[tag] = ft end + for _, ds in ipairs(df.scripts) do + local scri = strip(lower(ds.script)) + local fts = ft[scri] if not fts then fts = {} ft[scri] = fts end + for _, lang in ipairs(ds.langs) do + lang = strip(lower(lang)) + if scri == script then + if lang == language then + fts[lang] = 'sl' + else + fts[lang] = 's' + end + else + if lang == language then + fts[lang] = 'l' + else + fts[lang] = true + end + end + end + end + end + end + end + end + end + return features + end + end + end + end + return nil, nil +end diff --git a/tex/context/base/font-ota.lua b/tex/context/base/font-ota.lua new file mode 100644 index 000000000..72e7414c8 --- /dev/null +++ b/tex/context/base/font-ota.lua @@ -0,0 +1,320 @@ +if not modules then modules = { } end modules ['font-ota'] = { + version = 1.001, + comment = "companion to font-otf.lua (analysing)", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this might become scrp-*.lua + +local type, tostring, match, format, concat = type, tostring, string.match, string.format, table.concat + +if not trackers then trackers = { register = function() end } end + +local trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v end) +local trace_cjk = false trackers.register("cjk.injections", function(v) trace_cjk = v end) + +trackers.register("cjk.analyzing","otf.analyzing") + +fonts = fonts or { } +fonts.analyzers = fonts.analyzers or { } +fonts.analyzers.initializers = fonts.analyzers.initializers or { node = { otf = { } } } +fonts.analyzers.methods = fonts.analyzers.methods or { node = { otf = { } } } + +local otf = fonts.otf +local tfm = fonts.tfm + +local initializers = fonts.analyzers.initializers +local methods = fonts.analyzers.methods + +local glyph = node.id('glyph') +local glue = node.id('glue') +local penalty = node.id('penalty') + +local set_attribute = node.set_attribute +local has_attribute = node.has_attribute +local traverse_id = node.traverse_id +local delete_node = nodes.delete +local replace_node = nodes.replace +local insert_node_after = node.insert_after +local insert_node_before = node.insert_before +local traverse_node_list = node.traverse + +local fontdata = fonts.ids +local state = attributes.private('state') + +local fcs = (fonts.color and fonts.color.set) or function() end +local fcr = (fonts.color and fonts.color.reset) or function() end + +local a_to_script = otf.a_to_script +local a_to_language = otf.a_to_language + +-- in the future we will use language/script attributes instead of the +-- font related value, but then we also need dynamic features which is +-- somewhat slower; and .. we need a chain of them + + +function fonts.initializers.node.otf.analyze(tfmdata,value,attr) + if attr and attr > 0 then + script, language = a_to_script[attr], a_to_language[attr] + else + script, language = tfmdata.script, tfmdata.language + end + local action = initializers[script] + if action then + if type(action) == "function" then + return action(tfmdata,value) + else + local action = action[language] + if action then + return action(tfmdata,value) + end + end + end + return nil +end + +function fonts.methods.node.otf.analyze(head,font,attr) + local tfmdata = fontdata[font] + local script, language + if attr and attr > 0 then + script, language = a_to_script[attr], a_to_language[attr] + else + script, language = tfmdata.script, tfmdata.language + end + local action = methods[script] + if action then + if type(action) == "function" then + return action(head,font,attr) + else + action = action[language] + if action then + return action(head,font,attr) + end + end + end + return head, false +end + +otf.features.register("analyze",true) -- we always analyze +table.insert(fonts.triggers,"analyze") -- we need a proper function for doing this + +-- latin + +fonts.analyzers.methods.latn = fonts.analyzers.aux.setstate + +-- this info eventually will go into char-def + +local zwnj = 0x200C +local zwj = 0x200D + +local isol = { + [0x0600] = true, [0x0601] = true, [0x0602] = true, [0x0603] = true, + [0x0608] = true, [0x060B] = true, [0x0621] = true, [0x0674] = true, + [0x06DD] = true, [zwnj] = true, +} + +local isol_fina = { + [0x0622] = true, [0x0623] = true, [0x0624] = true, [0x0625] = true, + [0x0627] = true, [0x0629] = true, [0x062F] = true, [0x0630] = true, + [0x0631] = true, [0x0632] = true, [0x0648] = true, [0x0671] = true, + [0x0672] = true, [0x0673] = true, [0x0675] = true, [0x0676] = true, + [0x0677] = true, [0x0688] = true, [0x0689] = true, [0x068A] = true, + [0x068B] = true, [0x068C] = true, [0x068D] = true, [0x068E] = true, + [0x068F] = true, [0x0690] = true, [0x0691] = true, [0x0692] = true, + [0x0693] = true, [0x0694] = true, [0x0695] = true, [0x0696] = true, + [0x0697] = true, [0x0698] = true, [0x0699] = true, [0x06C0] = true, + [0x06C3] = true, [0x06C4] = true, [0x06C5] = true, [0x06C6] = true, + [0x06C7] = true, [0x06C8] = true, [0x06C9] = true, [0x06CA] = true, + [0x06CB] = true, [0x06CD] = true, [0x06CF] = true, [0x06D2] = true, + [0x06D3] = true, [0x06D5] = true, [0x06EE] = true, [0x06EF] = true, + [0x0759] = true, [0x075A] = true, [0x075B] = true, [0x076B] = true, + [0x076C] = true, [0x0771] = true, [0x0773] = true, [0x0774] = true, + [0x0778] = true, [0x0779] = true, +} + +local isol_fina_medi_init = { + [0x0626] = true, [0x0628] = true, [0x062A] = true, [0x062B] = true, + [0x062C] = true, [0x062D] = true, [0x062E] = true, [0x0633] = true, + [0x0634] = true, [0x0635] = true, [0x0636] = true, [0x0637] = true, + [0x0638] = true, [0x0639] = true, [0x063A] = true, [0x063B] = true, + [0x063C] = true, [0x063D] = true, [0x063E] = true, [0x063F] = true, + [0x0640] = true, [0x0641] = true, [0x0642] = true, [0x0643] = true, + [0x0644] = true, [0x0645] = true, [0x0646] = true, [0x0647] = true, + [0x0649] = true, [0x064A] = true, [0x066E] = true, [0x066F] = true, + [0x0678] = true, [0x0679] = true, [0x067A] = true, [0x067B] = true, + [0x067C] = true, [0x067D] = true, [0x067E] = true, [0x067F] = true, + [0x0680] = true, [0x0681] = true, [0x0682] = true, [0x0683] = true, + [0x0684] = true, [0x0685] = true, [0x0686] = true, [0x0687] = true, + [0x069A] = true, [0x069B] = true, [0x069C] = true, [0x069D] = true, + [0x069E] = true, [0x069F] = true, [0x06A0] = true, [0x06A1] = true, + [0x06A2] = true, [0x06A3] = true, [0x06A4] = true, [0x06A5] = true, + [0x06A6] = true, [0x06A7] = true, [0x06A8] = true, [0x06A9] = true, + [0x06AA] = true, [0x06AB] = true, [0x06AC] = true, [0x06AD] = true, + [0x06AE] = true, [0x06AF] = true, [0x06B0] = true, [0x06B1] = true, + [0x06B2] = true, [0x06B3] = true, [0x06B4] = true, [0x06B5] = true, + [0x06B6] = true, [0x06B7] = true, [0x06B8] = true, [0x06B9] = true, + [0x06BA] = true, [0x06BB] = true, [0x06BC] = true, [0x06BD] = true, + [0x06BE] = true, [0x06BF] = true, [0x06C1] = true, [0x06C2] = true, + [0x06CC] = true, [0x06CE] = true, [0x06D0] = true, [0x06D1] = true, + [0x06FA] = true, [0x06FB] = true, [0x06FC] = true, [0x06FF] = true, + [0x0750] = true, [0x0751] = true, [0x0752] = true, [0x0753] = true, + [0x0754] = true, [0x0755] = true, [0x0756] = true, [0x0757] = true, + [0x0758] = true, [0x075C] = true, [0x075D] = true, [0x075E] = true, + [0x075F] = true, [0x0760] = true, [0x0761] = true, [0x0762] = true, + [0x0763] = true, [0x0764] = true, [0x0765] = true, [0x0766] = true, + [0x0767] = true, [0x0768] = true, [0x0769] = true, [0x076A] = true, + [0x076D] = true, [0x076E] = true, [0x076F] = true, [0x0770] = true, + [0x0772] = true, [0x0775] = true, [0x0776] = true, [0x0777] = true, + [0x077A] = true, [0x077B] = true, [0x077C] = true, [0x077D] = true, + [0x077E] = true, [0x077F] = true, [zwj] = true, +} + +local arab_warned = { } + +-- todo: gref + +local function warning(current,what) + local char = current.char + if not arab_warned[char] then + log.report("analyze","arab: character %s (U+%04X) has no %s class", char, char, what) + arab_warned[char] = true + end +end + +function fonts.analyzers.methods.nocolor(head,font,attr) + for n in traverse_node_list(head,glyph) do + if not font or n.font == font then + fcr(n) + end + end + return head, true +end + +otf.remove_joiners = false -- true -- for idris who want it as option + +local function finish(first,last) + if last then + if first == last then + local fc = first.char + if isol_fina_medi_init[fc] or isol_fina[fc] then + set_attribute(first,state,4) -- isol + if trace_analyzing then fcs(first,"font:isol") end + else + warning(first,"isol") + set_attribute(first,state,0) -- error + if trace_analyzing then fcr(first) end + end + else + local lc = last.char + if isol_fina_medi_init[lc] or isol_fina[lc] then -- why isol here ? + -- if laststate == 1 or laststate == 2 or laststate == 4 then + set_attribute(last,state,3) -- fina + if trace_analyzing then fcs(last,"font:fina") end + else + warning(last,"fina") + set_attribute(last,state,0) -- error + if trace_analyzing then fcr(last) end + end + end + first, last = nil, nil + elseif first then + -- first and last are either both set so we never com here + local fc = first.char + if isol_fina_medi_init[fc] or isol_fina[fc] then + set_attribute(first,state,4) -- isol + if trace_analyzing then fcs(first,"font:isol") end + else + warning(first,"isol") + set_attribute(first,state,0) -- error + if trace_analyzing then fcr(first) end + end + first = nil + end + return first, last +end + +function fonts.analyzers.methods.arab(head,font,attr) -- maybe make a special version with no trace + local tfmdata = fontdata[font] + local marks = tfmdata.marks + local first, last, current, done = nil, nil, head, false + local joiners, nonjoiners + local removejoiners = tfmdata.remove_joiners -- or otf.remove_joiners + if removejoiners then + joiners, nonjoiners = { }, { } + end + while current do + if current.id == glyph and current.subtype<256 and current.font == font and not has_attribute(current,state) then + done = true + local char = current.char + if removejoiners then + if char == zwj then + joiners[#joiners+1] = current + elseif char == zwnj then + nonjoiners[#nonjoiners+1] = current + end + end + if marks[char] then + set_attribute(current,state,5) -- mark + if trace_analyzing then fcs(current,"font:mark") end + elseif isol[char] then -- can be zwj or zwnj too + first, last = finish(first,last) + set_attribute(current,state,4) -- isol + if trace_analyzing then fcs(current,"font:isol") end + first, last = nil, nil + elseif not first then + if isol_fina_medi_init[char] then + set_attribute(current,state,1) -- init + if trace_analyzing then fcs(current,"font:init") end + first, last = first or current, current + elseif isol_fina[char] then + set_attribute(current,state,4) -- isol + if trace_analyzing then fcs(current,"font:isol") end + first, last = nil, nil + else -- no arab + first, last = finish(first,last) + end + elseif isol_fina_medi_init[char] then + first, last = first or current, current + set_attribute(current,state,2) -- medi + if trace_analyzing then fcs(current,"font:medi") end + elseif isol_fina[char] then + if not has_attribute(last,state,1) then + -- tricky, we need to check what last may be ! + set_attribute(last,state,2) -- medi + if trace_analyzing then fcs(last,"font:medi") end + end + set_attribute(current,state,3) -- fina + if trace_analyzing then fcs(current,"font:fina") end + first, last = nil, nil + elseif char >= 0x0600 and char <= 0x06FF then + if trace_analyzing then fcs(current,"font:rest") end + first, last = finish(first,last) + else --no + first, last = finish(first,last) + end + else + first, last = finish(first,last) + end + current = current.next + end + first, last = finish(first,last) + if removejoiners then + for i=1,#joiners do + head = delete_node(head,joiners[i]) + end + for i=1,#nonjoiners do + head = replace_node(head,nonjoiners[i],nodes.glue(0)) -- or maybe a kern + end + end + return head, done +end + +table.insert(fonts.manipulators,"joiners") + +function fonts.initializers.node.otf.joiners(tfmdata,value) + if value == "strip" then + tfmdata.remove_joiners = true + end +end diff --git a/tex/context/base/font-otb.lua b/tex/context/base/font-otb.lua new file mode 100644 index 000000000..2a14085d6 --- /dev/null +++ b/tex/context/base/font-otb.lua @@ -0,0 +1,364 @@ +if not modules then modules = { } end modules ['font-otb'] = { + version = 1.001, + comment = "companion to font-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local concat = table.concat +local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip +local type, next, tonumber, tostring = type, next, tonumber, tostring + +local otf = fonts.otf +local tfm = fonts.tfm + +local trace_baseinit = false trackers.register("otf.baseinit", function(v) trace_baseinit = v end) +local trace_singles = false trackers.register("otf.singles", function(v) trace_singles = v end) +local trace_multiples = false trackers.register("otf.multiples", function(v) trace_multiples = v end) +local trace_alternatives = false trackers.register("otf.alternatives", function(v) trace_alternatives = v end) +local trace_ligatures = false trackers.register("otf.ligatures", function(v) trace_ligatures = v end) +local trace_kerns = false trackers.register("otf.kerns", function(v) trace_kerns = v end) +local trace_preparing = false trackers.register("otf.preparing", function(v) trace_preparing = v end) + +local wildcard = "*" +local default = "dflt" + +local split_at_space = lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway + +local pcache, fcache = { }, { } -- could be weak + +local function gref(descriptions,n) + if type(n) == "number" then + local name = descriptions[n].name + if name then + return format("U+%04X (%s)",n,name) + else + return format("U+%04X") + end + elseif n then + local num, nam = { }, { } + for i=1,#n do + local ni = n[i] + num[i] = format("U+%04X",ni) + nam[i] = descriptions[ni].name or "?" + end + return format("%s (%s)",concat(num," "), concat(nam," ")) + else + return "?" + end +end + +local function cref(kind,lookupname) + if lookupname then + return format("feature %s, lookup %s",kind,lookupname) + else + return format("feature %s",kind) + end +end + +local function resolve_ligatures(tfmdata,ligatures,kind) + kind = kind or "unknown" + local unicodes = tfmdata.unicodes + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local changed = tfmdata.changed + local done = { } + while true do + local ok = false + for k,v in next, ligatures do + local lig = v[1] + if not done[lig] then + local ligs = split_at_space:match(lig) + if #ligs == 2 then + local uc = v[2] + local c, f, s = characters[uc], ligs[1], ligs[2] + local uft, ust = unicodes[f] or 0, unicodes[s] or 0 + if not uft or not ust then + logs.report("define otf","%s: unicode problem with base ligature %s = %s + %s",cref(kind),gref(descriptions,uc),gref(descriptions,uft),gref(descriptions,ust)) + -- some kind of error + else + if type(uft) == "number" then uft = { uft } end + if type(ust) == "number" then ust = { ust } end + for ufi=1,#uft do + local uf = uft[ufi] + for usi=1,#ust do + local us = ust[usi] + if changed[uf] or changed[us] then + if trace_baseinit and trace_ligatures then + logs.report("define otf","%s: base ligature %s + %s ignored",cref(kind),gref(descriptions,uf),gref(descriptions,us)) + end + else + local first, second = characters[uf], us + if first and second then + local t = first.ligatures + if not t then + t = { } + first.ligatures = t + end + if type(uc) == "number" then + t[second] = { type = 0, char = uc } + else + t[second] = { type = 0, char = uc[1] } -- can this still happen? + end + if trace_baseinit and trace_ligatures then + logs.report("define otf","%s: base ligature %s + %s => %s",cref(kind),gref(descriptions,uf),gref(descriptions,us),gref(descriptions,uc)) + end + end + end + end + end + end + ok, done[lig] = true, descriptions[uc].name + end + end + end + if ok then + -- done has "a b c" = "a_b_c" and ligatures the already set ligatures: "a b" = 123 + -- and here we add extras (f i i = fi + i and alike) + -- + -- we could use a hash for fnc and pattern + -- + -- this might be interfering ! + for d,n in next, done do + local pattern = pcache[d] if not pattern then pattern = "^(" .. d .. ") " pcache[d] = pattern end + local fnc = fcache[n] if not fnc then fnc = function() return n .. " " end fcache[n] = fnc end + for k,v in next, ligatures do + v[1] = gsub(v[1],pattern,fnc) + end + end + else + break + end + end +end + +local function collect_lookups(otfdata,kind,script,language) + -- maybe store this in the font + local sequences = otfdata.luatex.sequences + if sequences then + local featuremap, featurelist = { }, { } + for s=1,#sequences do + local sequence = sequences[s] + local features = sequence.features + features = features and features[kind] + features = features and (features[script] or features[default] or features[wildcard]) + features = features and (features[language] or features[default] or features[wildcard]) + if features then + local subtables = sequence.subtables + if subtables then + for s=1,#subtables do + local ss = subtables[s] + if not featuremap[s] then + featuremap[ss] = true + featurelist[#featurelist+1] = ss + end + end + end + end + end + if #featurelist > 0 then + return featuremap, featurelist + end + end + return nil, nil +end + +local splitter = lpeg.splitat(" ") + +function prepare_base_substitutions(tfmdata,kind,value) -- we can share some code with the node features + if value then + local otfdata = tfmdata.shared.otfdata + local validlookups, lookuplist = collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) + if validlookups then + local ligatures = { } + local unicodes = tfmdata.unicodes -- names to unicodes + local indices = tfmdata.indices + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local changed = tfmdata.changed + for k,c in next, characters do + local glyph = descriptions[k] + local lookups = glyph.lookups + if lookups then + for l=1,#lookuplist do + local lookup = lookuplist[l] + local ps = lookups[lookup] + if ps then + for i=1,#ps do + local p = ps[i] + local t = p[1] + if t == 'substitution' then + local pv = p[2] -- p.variant + if pv then + local upv = unicodes[pv] + if upv then + if type(upv) == "table" then + upv = upv[1] + end + if characters[upv] then + if trace_baseinit and trace_singles then + logs.report("define otf","%s: base substitution %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upv)) + end + changed[k] = upv + end + end + end + elseif t == 'alternate' then + local pc = p[2] -- p.components + if pc then + -- a bit optimized ugliness + if value == 1 then + pc = splitter:match(pc) + elseif value == 2 then + local a, b = splitter:match(pc) + pc = b or a + else + pc = { splitter:match(pc) } + pc = pc[value] or pc[#pc] + end + if pc then + local upc = unicodes[pc] + if upc then + if type(upc) == "table" then + upc = upc[1] + end + if characters[upc] then + if trace_baseinit and trace_alternatives then + logs.report("define otf","%s: base alternate %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upc)) + end + changed[k] = upc + end + end + end + end + elseif t == 'ligature' and not changed[k] then + local pc = p[2] + if pc then + if trace_baseinit and trace_ligatures then + local upc = { splitter:match(pc) } + for i=1,#upc do upc[i] = unicodes[upc[i]] end + -- we assume that it's no table + logs.report("define otf","%s: base ligature %s => %s",cref(kind,lookup),gref(descriptions,upc),gref(descriptions,k)) + end + ligatures[#ligatures+1] = { pc, k } + end + end + end + end + end + end + end + resolve_ligatures(tfmdata,ligatures,kind) + end + else + tfmdata.ligatures = tfmdata.ligatures or { } -- left over from what ? + end +end + +local function prepare_base_kerns(tfmdata,kind,value) -- todo what kind of kerns, currently all + if value then + local otfdata = tfmdata.shared.otfdata + local validlookups, lookuplist = collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) + if validlookups then + local unicodes = tfmdata.unicodes -- names to unicodes + local indices = tfmdata.indices + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + for u, chr in next, characters do + local d = descriptions[u] + if d then + local dk = d.mykerns + if dk then + local t, done = chr.kerns or { }, false + for l=1,#lookuplist do + local lookup = lookuplist[l] + local kerns = dk[lookup] + if kerns then + for k, v in next, kerns do + if v ~= 0 and not t[k] then -- maybe no 0 test here + t[k], done = v, true + if trace_baseinit and trace_kerns then + logs.report("define otf","%s: base kern %s + %s => %s",cref(kind,lookup),gref(descriptions,u),gref(descriptions,k),v) + end + end + end + end + end + if done then + chr.kerns = t -- no empty assignments + end + -- elseif d.kerns then + -- logs.report("define otf","%s: invalid mykerns for %s",cref(kind),gref(descriptions,u)) + end + end + end + end + end +end + +-- In principle we could register each feature individually which was +-- what we did in earlier versions. However, after the rewrite it +-- made more sense to collect them in an overall features initializer +-- just as with the node variant. There it was needed because we need +-- to do complete mixed runs and not run featurewise (as we did before). + +local supported_gsub = { + 'liga','dlig','rlig','hlig', + 'pnum','onum','tnum','lnum', + 'zero', + 'smcp','cpsp','c2sc','ornm','aalt', + 'hwid','fwid', + 'ssty', -- math +} + +local supported_gpos = { + 'kern' +} + +function otf.features.register_base_substitution(tag) + supported_gsub[#supported_gsub+1] = tag +end +function otf.features.register_base_kern(tag) + supported_gsub[#supported_gpos+1] = tag +end + +local basehash, basehashes = { }, 1 + +function fonts.initializers.base.otf.features(tfmdata,value) + if true then -- value then + -- not shared + local t = trace_preparing and os.clock() + local features = tfmdata.shared.features + if features then + local h = { } + for f=1,#supported_gsub do + local feature = supported_gsub[f] + prepare_base_substitutions(tfmdata,feature,features[feature]) + h[#h+1] = feature + end + for f=1,#supported_gpos do + local feature = supported_gpos[f] + prepare_base_kerns(tfmdata,feature,features[feature]) + h[#h+1] = feature + end + local hash = concat(h," ") + local base = basehash[hash] + if not base then + basehashes = basehashes + 1 + base = basehashes + basehash[hash] = base + end + -- We need to make sure that luatex sees the difference between + -- base fonts that have different glyphs in the same slots in fonts + -- that have the same fullname (or filename). LuaTeX will merge fonts + -- eventually (and subset later on). If needed we can use a more + -- verbose name as long as we don't use <()<>[]{}/%> and the length + -- is < 128. + tfmdata.fullname = tfmdata.fullname .. base + end + if trace_preparing then + logs.report("otf define","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") + end + end +end diff --git a/tex/context/base/font-otc.lua b/tex/context/base/font-otc.lua new file mode 100644 index 000000000..f75da39cd --- /dev/null +++ b/tex/context/base/font-otc.lua @@ -0,0 +1,238 @@ +if not modules then modules = { } end modules ['font-otc'] = { + version = 1.001, + comment = "companion to font-otf.lua (context)", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local format, insert = string.format, table.insert +local type, next = type, next + +local ctxcatcodes = tex.ctxcatcodes + +-- we assume that the other otf stuff is loaded already + +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) + +local otf = fonts.otf +local tfm = fonts.tfm + +-- instead of "script = "DFLT", langs = { 'dflt' }" we now use wildcards (we used to +-- have always); some day we can write a "force always when true" trick for other +-- features as well + +local extra_lists = { + tlig = { + { + endash = "hyphen hyphen", + emdash = "hyphen hyphen hyphen", + -- quotedblleft = "quoteleft quoteleft", + -- quotedblright = "quoteright quoteright", + -- quotedblleft = "grave grave", + -- quotedblright = "quotesingle quotesingle", + -- quotedblbase = "comma comma", + }, + }, + trep = { + { + -- [0x0022] = 0x201D, + [0x0027] = 0x2019, + -- [0x0060] = 0x2018, + }, + }, + anum = { + { -- arabic + [0x0030] = 0x0660, + [0x0031] = 0x0661, + [0x0032] = 0x0662, + [0x0033] = 0x0663, + [0x0034] = 0x0664, + [0x0035] = 0x0665, + [0x0036] = 0x0666, + [0x0037] = 0x0667, + [0x0038] = 0x0668, + [0x0039] = 0x0669, + }, + { -- persian + [0x0030] = 0x06F0, + [0x0031] = 0x06F1, + [0x0032] = 0x06F2, + [0x0033] = 0x06F3, + [0x0034] = 0x06F4, + [0x0035] = 0x06F5, + [0x0036] = 0x06F6, + [0x0037] = 0x06F7, + [0x0038] = 0x06F8, + [0x0039] = 0x06F9, + }, + }, +} + +local extra_features = { -- maybe just 1..n so that we prescribe order + tlig = { + { + features = { { scripts = { { script = "*", langs = { "*" }, } }, tag = "tlig", comment = "added bij mkiv" }, }, + name = "ctx_tlig_1", + subtables = { { name = "ctx_tlig_1_s" } }, + type = "gsub_ligature", + flags = { }, + }, + }, + trep = { + { + features = { { scripts = { { script = "*", langs = { "*" }, } }, tag = "trep", comment = "added bij mkiv" }, }, + name = "ctx_trep_1", + subtables = { { name = "ctx_trep_1_s" } }, + type = "gsub_single", + flags = { }, + }, + }, + anum = { + { + features = { { scripts = { { script = "arab", langs = { "dflt", "FAR" }, } }, tag = "anum", comment = "added bij mkiv" }, }, + name = "ctx_anum_1", + subtables = { { name = "ctx_anum_1_s" } }, + type = "gsub_single", + flags = { }, + }, + { + features = { { scripts = { { script = "arab", langs = { "URD" }, } }, tag = "anum", comment = "added bij mkiv" }, }, + name = "ctx_anum_2", + subtables = { { name = "ctx_anum_2_s" } }, + type = "gsub_single", + flags = { }, + }, + }, +} + +fonts.otf.enhancers["add some missing characters"] = function(data,filename) + -- todo +end + +fonts.otf.enhancers["enrich with features"] = function(data,filename) + -- could be done elsewhere (true can be #) + local used = { } + for i=1,#otf.glists do + local g = data[otf.glists[i]] + if g then + for i=1,#g do + local f = g[i].features + if f then + for i=1,#f do + local t = f[i].tag + if t then used[t] = true end + end + end + end + end + end + -- + local glyphs = data.glyphs + local indices = data.map.map + data.gsub = data.gsub or { } + for kind, specifications in next, extra_features do + if not used[kind] then + local done = 0 + for s=1,#specifications do + local added = false + local specification = specifications[s] + local list = extra_lists[kind][s] + local name = specification.name .. "_s" + if specification.type == "gsub_ligature" then + for unicode, index in next, indices do + local glyph = glyphs[index] + local ligature = list[glyph.name] + if ligature then + local o = glyph.lookups or { } + -- o[name] = { "ligature", ligature, glyph.name } + o[name] = { + { + ["type"] = "ligature", + ["specification"] = { + char = glyph.name, + components = ligature, + } + } + } + glyph.lookups, done, added = o, done+1, true + end + end + elseif specification.type == "gsub_single" then + for unicode, index in next, indices do + local glyph = glyphs[index] + local r = list[unicode] + if r then + local replacement = indices[r] + if replacement and glyphs[replacement] then + local o = glyph.lookups or { } + -- o[name] = { { "substitution", glyphs[replacement].name } } + o[name] = { + { + ["type"] = "substitution", + ["specification"] = { + variant = glyphs[replacement].name, + } + } + } + glyph.lookups, done, added = o, done+1, true + end + end + end + end + if added then + insert(data.gsub,s,table.fastcopy(specification)) -- right order + end + end + if done > 0 then + if trace_loading then + logs.report("load otf","enhance: registering %s feature (%s glyphs affected)",kind,done) + end + end + end + end +end + +otf.tables.features['tlig'] = 'TeX Ligatures' +otf.tables.features['trep'] = 'TeX Replacements' +otf.tables.features['anum'] = 'Arabic Digits' + +otf.features.register_base_substitution('tlig') +otf.features.register_base_substitution('trep') +otf.features.register_base_substitution('anum') + +-- the functionality is defined elsewhere + +fonts.initializers.base.otf.equaldigits = fonts.initializers.common.equaldigits +fonts.initializers.node.otf.equaldigits = fonts.initializers.common.equaldigits + +fonts.initializers.base.otf.lineheight = fonts.initializers.common.lineheight +fonts.initializers.node.otf.lineheight = fonts.initializers.common.lineheight + +fonts.initializers.base.otf.compose = fonts.initializers.common.compose +fonts.initializers.node.otf.compose = fonts.initializers.common.compose + +-- bonus function + +function otf.name_to_slot(name) -- todo: afm en tfm + local tfmdata = fonts.ids[font.current()] + if tfmdata and tfmdata.shared then + local otfdata = tfmdata.shared.otfdata + local unicode = otfdata.luatex.unicodes[name] + if type(unicode) == "number" then + return unicode + else + return unicode[1] + end + end + return nil +end + +function otf.char(n) -- todo: afm en tfm + if type(n) == "string" then + n = otf.name_to_slot(n) + end + if n then + tex.sprint(ctxcatcodes,format("\\char%s ",n)) + end +end diff --git a/tex/context/base/font-otd.lua b/tex/context/base/font-otd.lua new file mode 100644 index 000000000..78a828146 --- /dev/null +++ b/tex/context/base/font-otd.lua @@ -0,0 +1,78 @@ +if not modules then modules = { } end modules ['font-otd'] = { + version = 1.001, + comment = "companion to font-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) + +fonts = fonts or { } +fonts.otf = fonts.otf or { } + +local otf = fonts.otf +local fontdata = fonts.ids + +otf.features = otf.features or { } +otf.features.default = otf.features.default or { } + +local context_setups = fonts.define.specify.context_setups +local context_numbers = fonts.define.specify.context_numbers + +local a_to_script = { } otf.a_to_script = a_to_script +local a_to_language = { } otf.a_to_language = a_to_language + +function otf.set_dynamics(font,dynamics,attribute) + features = context_setups[context_numbers[attribute]] -- can be moved to caller + if features then + local script = features.script or 'dflt' + local language = features.language or 'dflt' + local ds = dynamics[script] + if not ds then + ds = { } + dynamics[script] = ds + end + local dsl = ds[language] + if not dsl then + dsl = { } + ds[language] = dsl + end + local dsla = dsl[attribute] + if dsla then + -- if trace_dynamics then + -- logs.report("otf define","using dynamics %s: attribute %s, script %s, language %s",context_numbers[attribute],attribute,script,language) + -- end + return dsla + else + local tfmdata = fontdata[font] + a_to_script [attribute] = script + a_to_language[attribute] = language + -- we need to save some values + local saved = { + script = tfmdata.script, + language = tfmdata.language, + mode = tfmdata.mode, + features = tfmdata.shared.features + } + tfmdata.mode = "node" + tfmdata.language = language + tfmdata.script = script + tfmdata.shared.features = { } + -- end of save + dsla = otf.set_features(tfmdata,fonts.define.check(features,otf.features.default)) + if trace_dynamics then + logs.report("otf define","setting dynamics %s: attribute %s, script %s, language %s",context_numbers[attribute],attribute,script,language) + end + -- we need to restore some values + tfmdata.script = saved.script + tfmdata.language = saved.language + tfmdata.mode = saved.mode + tfmdata.shared.features = saved.features + -- end of restore + dynamics[script][language][attribute] = dsla -- cache + return dsla + end + end + return nil -- { } +end diff --git a/tex/context/base/font-otf.lua b/tex/context/base/font-otf.lua index fffd4eeda..20273f8f5 100644 --- a/tex/context/base/font-otf.lua +++ b/tex/context/base/font-otf.lua @@ -6,829 +6,210 @@ if not modules then modules = { } end modules ['font-otf'] = { license = "see context related readme files" } -local format, concat, getn = string.format, table.concat, table.getn -local type, pairs, ipairs, next, tonumber, tostring = type, pairs, ipairs, next, tonumber, tostring +local utf = unicode.utf8 -local space = lpeg.P(" ") -local nospaces = (1-space)^1 -local optionalspace = space^0 +local concat, getn, utfbyte = table.concat, table.getn, utf.byte +local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip +local type, next, tonumber, tostring = type, next, tonumber, tostring -local split_at_space = lpeg.Ct((lpeg.C(nospaces) * optionalspace)^0) -- table ! +local trace_private = false trackers.register("otf.private", function(v) trace_private = v end) +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) +local trace_features = false trackers.register("otf.features", function(v) trace_features = v end) +local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) +local trace_sequences = false trackers.register("otf.sequences", function(v) trace_sequences = v end) +local trace_math = false trackers.register("otf.math", function(v) trace_math = v end) --- we can use more lpegs when lpeg is extended with function args and so +--~ trackers.enable("otf.loading") --- the flattening code is a prelude to a more compact table format (so, we're now --- at the fourth version); maybe we will go unicode, although that will mean that we --- miss some glyphs (unicode -1) - --- todo: featuredata is now indexed by kind,lookup but probably lookup is okay too - --- todo: now that we pack ... resolve strings to unicode points --- todo: unpack already in tmc file, i.e. save tables and return ref''d version --- todo: dependents etc resolve too, maybe even reorder glyphs to unicode --- todo: pack ignoreflags - --- abvf abvs blwf blws dist falt half halt jalt lfbd ljmo --- mset opbd palt pwid qwid rand rtbd ruby size tjmo twid valt vatu vert --- vhal vjmo vkna vkrn vpal vrt2 - --- The specification of OpenType is vague, very vague. Apart from a lack of proper --- specifications (a free one) there's also the problem that Microsoft and Adobe --- may have their own rules. Anyhow, the following is from Adobe's feature file --- specification: --- --- http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html#6.h --- --- The following is a reference summary of the algorithm used by an OpenType layout --- (OTL) engine to perform substitutions and positionings. The important aspect of --- this for a feature file editor is that each lookup corresponds to one "pass" over --- the glyph run (see step 4 below). Thus, each lookup has as input the accumulated --- result of all previous lookups in the LookupList (whether in the same feature or --- in other features). --- 1. All glyphs in the client's glyph run must belong to the same language --- system. Glyph sequence matching may not occur across language --- systems. Do the following first for the GSUB and then for the GPOS: --- 2. Assemble all features (including any required feature) for the glyph --- run's language system. --- 3. Assemble all lookups in these features, in LookupList order, removing --- any duplicates. All features and thus all lookups needn't be applied to --- every glyph in the run. --- 4. For each lookup: --- 5. For each glyph in the glyph run: --- 6. If the lookup is applied to that glyph and the lookupflag doesn't --- indicate that that glyph is to be ignored: --- 7. For each subtable in the lookup: --- 8. If the subtable's target context is matched: --- 9. Do the glyph substitution or positioning, --- --- OR: --- --- If this is a (chain) contextual lookup do the following --- [(10)-(11)] in the subtable's Subst/PosLookupRecord order: --- 10. For each (sequenceIndex, lookupListIndex) pair: --- 11. Apply lookup[lookupListIndex] at input sequence[sequenceIndex] --- [steps (7)-(11)] --- 12. Goto the glyph after the input sequence matched in (8) --- (i.e. skip any remaining subtables in the lookup). --- The "target context" in step 8 above comprises the input sequence and any --- backtrack and lookahead sequences. --- The input sequence must be matched entirely within the lookup's "application --- range" at that glyph (that contiguous subrun of glyphs including and around --- the current glyph on which the lookup is applied). There is no such restriction --- on the backtrack and lookahead sequences. --- "Matching" includes matching any glyphs designated to be skipped in the --- lookup's LookupFlag. +local zwnj = 0x200C +local zwj = 0x200D --[[ldx-- -This module is sparsely documented because it is a moving target. -The table format of the reader changes and we experiment a lot with -different methods for supporting features.
+The fontforge table has organized lookups in a certain way. A first implementation +of this code was organized featurewise: information related to features was +collected and processing boiled down to a run over the features. The current +implementation honors the order in the main feature table. Since we can reorder this +table as we want, we can eventually support several models of processing. We kept +the static as well as dynamic feature processing, because it had proved to be +rather useful. The formerly three loop variants have beem discarded but will +reapear at some time.
+ +In
We process the whole list and then consult the glyph nodes. An alternative approach +is to collect strings of characters using the same font including spaces (because some +lookups involve spaces). However, we then need to reconstruct the list which is no fun. +Also, we need to carry quite some information, like attributes, so eventually we don't +gain much (if we gain something at all).
+ +Another consideration has been to operate on sublists (subhead, subtail) but again +this would complicate matters as we then neext to keep track of a changing subhead +and subtail. On the other hand, this might save some runtime. The number of changes +involved is not that large. This only makes sense when we have many fonts in a list +and don't change to frequently.
+--ldx]]-- -As with the
Incrementing the version number will force a re-cache. We jump the
-number by one when there's a fix in the
We start with a lot of tables and related functions.
--ldx]]-- -otf.tables.scripts = { - ['dflt'] = 'Default', - - ['arab'] = 'Arabic', - ['armn'] = 'Armenian', - ['bali'] = 'Balinese', - ['beng'] = 'Bengali', - ['bopo'] = 'Bopomofo', - ['brai'] = 'Braille', - ['bugi'] = 'Buginese', - ['buhd'] = 'Buhid', - ['byzm'] = 'Byzantine Music', - ['cans'] = 'Canadian Syllabics', - ['cher'] = 'Cherokee', - ['copt'] = 'Coptic', - ['cprt'] = 'Cypriot Syllabary', - ['cyrl'] = 'Cyrillic', - ['deva'] = 'Devanagari', - ['dsrt'] = 'Deseret', - ['ethi'] = 'Ethiopic', - ['geor'] = 'Georgian', - ['glag'] = 'Glagolitic', - ['goth'] = 'Gothic', - ['grek'] = 'Greek', - ['gujr'] = 'Gujarati', - ['guru'] = 'Gurmukhi', - ['hang'] = 'Hangul', - ['hani'] = 'CJK Ideographic', - ['hano'] = 'Hanunoo', - ['hebr'] = 'Hebrew', - ['ital'] = 'Old Italic', - ['jamo'] = 'Hangul Jamo', - ['java'] = 'Javanese', - ['kana'] = 'Hiragana and Katakana', - ['khar'] = 'Kharosthi', - ['khmr'] = 'Khmer', - ['knda'] = 'Kannada', - ['lao' ] = 'Lao', - ['latn'] = 'Latin', - ['limb'] = 'Limbu', - ['linb'] = 'Linear B', - ['math'] = 'Mathematical Alphanumeric Symbols', - ['mlym'] = 'Malayalam', - ['mong'] = 'Mongolian', - ['musc'] = 'Musical Symbols', - ['mymr'] = 'Myanmar', - ['nko' ] = "N'ko", - ['ogam'] = 'Ogham', - ['orya'] = 'Oriya', - ['osma'] = 'Osmanya', - ['phag'] = 'Phags-pa', - ['phnx'] = 'Phoenician', - ['runr'] = 'Runic', - ['shaw'] = 'Shavian', - ['sinh'] = 'Sinhala', - ['sylo'] = 'Syloti Nagri', - ['syrc'] = 'Syriac', - ['tagb'] = 'Tagbanwa', - ['tale'] = 'Tai Le', - ['talu'] = 'Tai Lu', - ['taml'] = 'Tamil', - ['telu'] = 'Telugu', - ['tfng'] = 'Tifinagh', - ['tglg'] = 'Tagalog', - ['thaa'] = 'Thaana', - ['thai'] = 'Thai', - ['tibt'] = 'Tibetan', - ['ugar'] = 'Ugaritic Cuneiform', - ['xpeo'] = 'Old Persian Cuneiform', - ['xsux'] = 'Sumero-Akkadian Cuneiform', - ['yi' ] = 'Yi' +otf.tables.global_fields = table.tohash { + "lookups", + "glyphs", + "subfonts", + "luatex", + "pfminfo", + "cidinfo", + "tables", + "names", + "unicodes", + "names", + "anchor_classes", + "kern_classes", + "gpos", + "gsub" } -otf.tables.languages = { - ['dflt'] = 'Default', - - ['aba'] = 'Abaza', - ['abk'] = 'Abkhazian', - ['ady'] = 'Adyghe', - ['afk'] = 'Afrikaans', - ['afr'] = 'Afar', - ['agw'] = 'Agaw', - ['als'] = 'Alsatian', - ['alt'] = 'Altai', - ['amh'] = 'Amharic', - ['ara'] = 'Arabic', - ['ari'] = 'Aari', - ['ark'] = 'Arakanese', - ['asm'] = 'Assamese', - ['ath'] = 'Athapaskan', - ['avr'] = 'Avar', - ['awa'] = 'Awadhi', - ['aym'] = 'Aymara', - ['aze'] = 'Azeri', - ['bad'] = 'Badaga', - ['bag'] = 'Baghelkhandi', - ['bal'] = 'Balkar', - ['bau'] = 'Baule', - ['bbr'] = 'Berber', - ['bch'] = 'Bench', - ['bcr'] = 'Bible Cree', - ['bel'] = 'Belarussian', - ['bem'] = 'Bemba', - ['ben'] = 'Bengali', - ['bgr'] = 'Bulgarian', - ['bhi'] = 'Bhili', - ['bho'] = 'Bhojpuri', - ['bik'] = 'Bikol', - ['bil'] = 'Bilen', - ['bkf'] = 'Blackfoot', - ['bli'] = 'Balochi', - ['bln'] = 'Balante', - ['blt'] = 'Balti', - ['bmb'] = 'Bambara', - ['bml'] = 'Bamileke', - ['bos'] = 'Bosnian', - ['bre'] = 'Breton', - ['brh'] = 'Brahui', - ['bri'] = 'Braj Bhasha', - ['brm'] = 'Burmese', - ['bsh'] = 'Bashkir', - ['bti'] = 'Beti', - ['cat'] = 'Catalan', - ['ceb'] = 'Cebuano', - ['che'] = 'Chechen', - ['chg'] = 'Chaha Gurage', - ['chh'] = 'Chattisgarhi', - ['chi'] = 'Chichewa', - ['chk'] = 'Chukchi', - ['chp'] = 'Chipewyan', - ['chr'] = 'Cherokee', - ['chu'] = 'Chuvash', - ['cmr'] = 'Comorian', - ['cop'] = 'Coptic', - ['cos'] = 'Corsican', - ['cre'] = 'Cree', - ['crr'] = 'Carrier', - ['crt'] = 'Crimean Tatar', - ['csl'] = 'Church Slavonic', - ['csy'] = 'Czech', - ['dan'] = 'Danish', - ['dar'] = 'Dargwa', - ['dcr'] = 'Woods Cree', - ['deu'] = 'German', - ['dgr'] = 'Dogri', - ['div'] = 'Divehi', - ['djr'] = 'Djerma', - ['dng'] = 'Dangme', - ['dnk'] = 'Dinka', - ['dri'] = 'Dari', - ['dun'] = 'Dungan', - ['dzn'] = 'Dzongkha', - ['ebi'] = 'Ebira', - ['ecr'] = 'Eastern Cree', - ['edo'] = 'Edo', - ['efi'] = 'Efik', - ['ell'] = 'Greek', - ['eng'] = 'English', - ['erz'] = 'Erzya', - ['esp'] = 'Spanish', - ['eti'] = 'Estonian', - ['euq'] = 'Basque', - ['evk'] = 'Evenki', - ['evn'] = 'Even', - ['ewe'] = 'Ewe', - ['fan'] = 'French Antillean', - ['far'] = 'Farsi', - ['fin'] = 'Finnish', - ['fji'] = 'Fijian', - ['fle'] = 'Flemish', - ['fne'] = 'Forest Nenets', - ['fon'] = 'Fon', - ['fos'] = 'Faroese', - ['fra'] = 'French', - ['fri'] = 'Frisian', - ['frl'] = 'Friulian', - ['fta'] = 'Futa', - ['ful'] = 'Fulani', - ['gad'] = 'Ga', - ['gae'] = 'Gaelic', - ['gag'] = 'Gagauz', - ['gal'] = 'Galician', - ['gar'] = 'Garshuni', - ['gaw'] = 'Garhwali', - ['gez'] = "Ge'ez", - ['gil'] = 'Gilyak', - ['gmz'] = 'Gumuz', - ['gon'] = 'Gondi', - ['grn'] = 'Greenlandic', - ['gro'] = 'Garo', - ['gua'] = 'Guarani', - ['guj'] = 'Gujarati', - ['hai'] = 'Haitian', - ['hal'] = 'Halam', - ['har'] = 'Harauti', - ['hau'] = 'Hausa', - ['haw'] = 'Hawaiin', - ['hbn'] = 'Hammer-Banna', - ['hil'] = 'Hiligaynon', - ['hin'] = 'Hindi', - ['hma'] = 'High Mari', - ['hnd'] = 'Hindko', - ['ho'] = 'Ho', - ['hri'] = 'Harari', - ['hrv'] = 'Croatian', - ['hun'] = 'Hungarian', - ['hye'] = 'Armenian', - ['ibo'] = 'Igbo', - ['ijo'] = 'Ijo', - ['ilo'] = 'Ilokano', - ['ind'] = 'Indonesian', - ['ing'] = 'Ingush', - ['inu'] = 'Inuktitut', - ['iri'] = 'Irish', - ['irt'] = 'Irish Traditional', - ['isl'] = 'Icelandic', - ['ism'] = 'Inari Sami', - ['ita'] = 'Italian', - ['iwr'] = 'Hebrew', - ['jan'] = 'Japanese', - ['jav'] = 'Javanese', - ['jii'] = 'Yiddish', - ['jud'] = 'Judezmo', - ['jul'] = 'Jula', - ['kab'] = 'Kabardian', - ['kac'] = 'Kachchi', - ['kal'] = 'Kalenjin', - ['kan'] = 'Kannada', - ['kar'] = 'Karachay', - ['kat'] = 'Georgian', - ['kaz'] = 'Kazakh', - ['keb'] = 'Kebena', - ['kge'] = 'Khutsuri Georgian', - ['kha'] = 'Khakass', - ['khk'] = 'Khanty-Kazim', - ['khm'] = 'Khmer', - ['khs'] = 'Khanty-Shurishkar', - ['khv'] = 'Khanty-Vakhi', - ['khw'] = 'Khowar', - ['kik'] = 'Kikuyu', - ['kir'] = 'Kirghiz', - ['kis'] = 'Kisii', - ['kkn'] = 'Kokni', - ['klm'] = 'Kalmyk', - ['kmb'] = 'Kamba', - ['kmn'] = 'Kumaoni', - ['kmo'] = 'Komo', - ['kms'] = 'Komso', - ['knr'] = 'Kanuri', - ['kod'] = 'Kodagu', - ['koh'] = 'Korean Old Hangul', - ['kok'] = 'Konkani', - ['kon'] = 'Kikongo', - ['kop'] = 'Komi-Permyak', - ['kor'] = 'Korean', - ['koz'] = 'Komi-Zyrian', - ['kpl'] = 'Kpelle', - ['kri'] = 'Krio', - ['krk'] = 'Karakalpak', - ['krl'] = 'Karelian', - ['krm'] = 'Karaim', - ['krn'] = 'Karen', - ['krt'] = 'Koorete', - ['ksh'] = 'Kashmiri', - ['ksi'] = 'Khasi', - ['ksm'] = 'Kildin Sami', - ['kui'] = 'Kui', - ['kul'] = 'Kulvi', - ['kum'] = 'Kumyk', - ['kur'] = 'Kurdish', - ['kuu'] = 'Kurukh', - ['kuy'] = 'Kuy', - ['kyk'] = 'Koryak', - ['lad'] = 'Ladin', - ['lah'] = 'Lahuli', - ['lak'] = 'Lak', - ['lam'] = 'Lambani', - ['lao'] = 'Lao', - ['lat'] = 'Latin', - ['laz'] = 'Laz', - ['lcr'] = 'L-Cree', - ['ldk'] = 'Ladakhi', - ['lez'] = 'Lezgi', - ['lin'] = 'Lingala', - ['lma'] = 'Low Mari', - ['lmb'] = 'Limbu', - ['lmw'] = 'Lomwe', - ['lsb'] = 'Lower Sorbian', - ['lsm'] = 'Lule Sami', - ['lth'] = 'Lithuanian', - ['ltz'] = 'Luxembourgish', - ['lub'] = 'Luba', - ['lug'] = 'Luganda', - ['luh'] = 'Luhya', - ['luo'] = 'Luo', - ['lvi'] = 'Latvian', - ['maj'] = 'Majang', - ['mak'] = 'Makua', - ['mal'] = 'Malayalam Traditional', - ['man'] = 'Mansi', - ['map'] = 'Mapudungun', - ['mar'] = 'Marathi', - ['maw'] = 'Marwari', - ['mbn'] = 'Mbundu', - ['mch'] = 'Manchu', - ['mcr'] = 'Moose Cree', - ['mde'] = 'Mende', - ['men'] = "Me'en", - ['miz'] = 'Mizo', - ['mkd'] = 'Macedonian', - ['mle'] = 'Male', - ['mlg'] = 'Malagasy', - ['mln'] = 'Malinke', - ['mlr'] = 'Malayalam Reformed', - ['mly'] = 'Malay', - ['mnd'] = 'Mandinka', - ['mng'] = 'Mongolian', - ['mni'] = 'Manipuri', - ['mnk'] = 'Maninka', - ['mnx'] = 'Manx Gaelic', - ['moh'] = 'Mohawk', - ['mok'] = 'Moksha', - ['mol'] = 'Moldavian', - ['mon'] = 'Mon', - ['mor'] = 'Moroccan', - ['mri'] = 'Maori', - ['mth'] = 'Maithili', - ['mts'] = 'Maltese', - ['mun'] = 'Mundari', - ['nag'] = 'Naga-Assamese', - ['nan'] = 'Nanai', - ['nas'] = 'Naskapi', - ['ncr'] = 'N-Cree', - ['ndb'] = 'Ndebele', - ['ndg'] = 'Ndonga', - ['nep'] = 'Nepali', - ['new'] = 'Newari', - ['ngr'] = 'Nagari', - ['nhc'] = 'Norway House Cree', - ['nis'] = 'Nisi', - ['niu'] = 'Niuean', - ['nkl'] = 'Nkole', - ['nko'] = "N'ko", - ['nld'] = 'Dutch', - ['nog'] = 'Nogai', - ['nor'] = 'Norwegian', - ['nsm'] = 'Northern Sami', - ['nta'] = 'Northern Tai', - ['nto'] = 'Esperanto', - ['nyn'] = 'Nynorsk', - ['oci'] = 'Occitan', - ['ocr'] = 'Oji-Cree', - ['ojb'] = 'Ojibway', - ['ori'] = 'Oriya', - ['oro'] = 'Oromo', - ['oss'] = 'Ossetian', - ['paa'] = 'Palestinian Aramaic', - ['pal'] = 'Pali', - ['pan'] = 'Punjabi', - ['pap'] = 'Palpa', - ['pas'] = 'Pashto', - ['pgr'] = 'Polytonic Greek', - ['pil'] = 'Pilipino', - ['plg'] = 'Palaung', - ['plk'] = 'Polish', - ['pro'] = 'Provencal', - ['ptg'] = 'Portuguese', - ['qin'] = 'Chin', - ['raj'] = 'Rajasthani', - ['rbu'] = 'Russian Buriat', - ['rcr'] = 'R-Cree', - ['ria'] = 'Riang', - ['rms'] = 'Rhaeto-Romanic', - ['rom'] = 'Romanian', - ['roy'] = 'Romany', - ['rsy'] = 'Rusyn', - ['rua'] = 'Ruanda', - ['rus'] = 'Russian', - ['sad'] = 'Sadri', - ['san'] = 'Sanskrit', - ['sat'] = 'Santali', - ['say'] = 'Sayisi', - ['sek'] = 'Sekota', - ['sel'] = 'Selkup', - ['sgo'] = 'Sango', - ['shn'] = 'Shan', - ['sib'] = 'Sibe', - ['sid'] = 'Sidamo', - ['sig'] = 'Silte Gurage', - ['sks'] = 'Skolt Sami', - ['sky'] = 'Slovak', - ['sla'] = 'Slavey', - ['slv'] = 'Slovenian', - ['sml'] = 'Somali', - ['smo'] = 'Samoan', - ['sna'] = 'Sena', - ['snd'] = 'Sindhi', - ['snh'] = 'Sinhalese', - ['snk'] = 'Soninke', - ['sog'] = 'Sodo Gurage', - ['sot'] = 'Sotho', - ['sqi'] = 'Albanian', - ['srb'] = 'Serbian', - ['srk'] = 'Saraiki', - ['srr'] = 'Serer', - ['ssl'] = 'South Slavey', - ['ssm'] = 'Southern Sami', - ['sur'] = 'Suri', - ['sva'] = 'Svan', - ['sve'] = 'Swedish', - ['swa'] = 'Swadaya Aramaic', - ['swk'] = 'Swahili', - ['swz'] = 'Swazi', - ['sxt'] = 'Sutu', - ['syr'] = 'Syriac', - ['tab'] = 'Tabasaran', - ['taj'] = 'Tajiki', - ['tam'] = 'Tamil', - ['tat'] = 'Tatar', - ['tcr'] = 'TH-Cree', - ['tel'] = 'Telugu', - ['tgn'] = 'Tongan', - ['tgr'] = 'Tigre', - ['tgy'] = 'Tigrinya', - ['tha'] = 'Thai', - ['tht'] = 'Tahitian', - ['tib'] = 'Tibetan', - ['tkm'] = 'Turkmen', - ['tmn'] = 'Temne', - ['tna'] = 'Tswana', - ['tne'] = 'Tundra Nenets', - ['tng'] = 'Tonga', - ['tod'] = 'Todo', - ['trk'] = 'Turkish', - ['tsg'] = 'Tsonga', - ['tua'] = 'Turoyo Aramaic', - ['tul'] = 'Tulu', - ['tuv'] = 'Tuvin', - ['twi'] = 'Twi', - ['udm'] = 'Udmurt', - ['ukr'] = 'Ukrainian', - ['urd'] = 'Urdu', - ['usb'] = 'Upper Sorbian', - ['uyg'] = 'Uyghur', - ['uzb'] = 'Uzbek', - ['ven'] = 'Venda', - ['vit'] = 'Vietnamese', - ['wa' ] = 'Wa', - ['wag'] = 'Wagdi', - ['wcr'] = 'West-Cree', - ['wel'] = 'Welsh', - ['wlf'] = 'Wolof', - ['xbd'] = 'Tai Lue', - ['xhs'] = 'Xhosa', - ['yak'] = 'Yakut', - ['yba'] = 'Yoruba', - ['ycr'] = 'Y-Cree', - ['yic'] = 'Yi Classic', - ['yim'] = 'Yi Modern', - ['zhh'] = 'Chinese Hong Kong', - ['zhp'] = 'Chinese Phonetic', - ['zhs'] = 'Chinese Simplified', - ['zht'] = 'Chinese Traditional', - ['znd'] = 'Zande', - ['zul'] = 'Zulu' -} - -otf.tables.features = { - ['aalt'] = 'Access All Alternates', - ['abvf'] = 'Above-Base Forms', - ['abvm'] = 'Above-Base Mark Positioning', - ['abvs'] = 'Above-Base Substitutions', - ['afrc'] = 'Alternative Fractions', - ['akhn'] = 'Akhands', - ['blwf'] = 'Below-Base Forms', - ['blwm'] = 'Below-Base Mark Positioning', - ['blws'] = 'Below-Base Substitutions', - ['c2pc'] = 'Petite Capitals From Capitals', - ['c2sc'] = 'Small Capitals From Capitals', - ['calt'] = 'Contextual Alternates', - ['case'] = 'Case-Sensitive Forms', - ['ccmp'] = 'Glyph Composition/Decomposition', - ['cjct'] = 'Conjunct Forms', - ['clig'] = 'Contextual Ligatures', - ['cpsp'] = 'Capital Spacing', - ['cswh'] = 'Contextual Swash', - ['curs'] = 'Cursive Positioning', - ['dflt'] = 'Default Processing', - ['dist'] = 'Distances', - ['dlig'] = 'Discretionary Ligatures', - ['dnom'] = 'Denominators', - ['expt'] = 'Expert Forms', - ['falt'] = 'Final glyph Alternates', - ['fin2'] = 'Terminal Forms #2', - ['fin3'] = 'Terminal Forms #3', - ['fina'] = 'Terminal Forms', - ['frac'] = 'Fractions', - ['fwid'] = 'Full Width', - ['half'] = 'Half Forms', - ['haln'] = 'Halant Forms', - ['halt'] = 'Alternate Half Width', - ['hist'] = 'Historical Forms', - ['hkna'] = 'Horizontal Kana Alternates', - ['hlig'] = 'Historical Ligatures', - ['hngl'] = 'Hangul', - ['hojo'] = 'Hojo Kanji Forms', - ['hwid'] = 'Half Width', - ['init'] = 'Initial Forms', - ['isol'] = 'Isolated Forms', - ['ital'] = 'Italics', - ['jalt'] = 'Justification Alternatives', - ['jp04'] = 'JIS2004 Forms', - ['jp78'] = 'JIS78 Forms', - ['jp83'] = 'JIS83 Forms', - ['jp90'] = 'JIS90 Forms', - ['kern'] = 'Kerning', - ['lfbd'] = 'Left Bounds', - ['liga'] = 'Standard Ligatures', - ['ljmo'] = 'Leading Jamo Forms', - ['lnum'] = 'Lining Figures', - ['locl'] = 'Localized Forms', - ['mark'] = 'Mark Positioning', - ['med2'] = 'Medial Forms #2', - ['medi'] = 'Medial Forms', - ['mgrk'] = 'Mathematical Greek', - ['mkmk'] = 'Mark to Mark Positioning', - ['mset'] = 'Mark Positioning via Substitution', - ['nalt'] = 'Alternate Annotation Forms', - ['nlck'] = 'NLC Kanji Forms', - ['nukt'] = 'Nukta Forms', - ['numr'] = 'Numerators', - ['onum'] = 'Old Style Figures', - ['opbd'] = 'Optical Bounds', - ['ordn'] = 'Ordinals', - ['ornm'] = 'Ornaments', - ['palt'] = 'Proportional Alternate Width', - ['pcap'] = 'Petite Capitals', - ['pnum'] = 'Proportional Figures', - ['pref'] = 'Pre-base Forms', - ['pres'] = 'Pre-base Substitutions', - ['pstf'] = 'Post-base Forms', - ['psts'] = 'Post-base Substitutions', - ['pwid'] = 'Proportional Widths', - ['qwid'] = 'Quarter Widths', - ['rand'] = 'Randomize', - ['rkrf'] = 'Rakar Forms', - ['rlig'] = 'Required Ligatures', - ['rphf'] = 'Reph Form', - ['rtbd'] = 'Right Bounds', - ['rtla'] = 'Right-To-Left Alternates', - ['ruby'] = 'Ruby Notation Forms', - ['salt'] = 'Stylistic Alternates', - ['sinf'] = 'Scientific Inferiors', - ['size'] = 'Optical Size', - ['smcp'] = 'Small Capitals', - ['smpl'] = 'Simplified Forms', - ['ss01'] = 'Stylistic Set 1', - ['ss02'] = 'Stylistic Set 2', - ['ss03'] = 'Stylistic Set 3', - ['ss04'] = 'Stylistic Set 4', - ['ss05'] = 'Stylistic Set 5', - ['ss06'] = 'Stylistic Set 6', - ['ss07'] = 'Stylistic Set 7', - ['ss08'] = 'Stylistic Set 8', - ['ss09'] = 'Stylistic Set 9', - ['ss10'] = 'Stylistic Set 10', - ['ss11'] = 'Stylistic Set 11', - ['ss12'] = 'Stylistic Set 12', - ['ss13'] = 'Stylistic Set 13', - ['ss14'] = 'Stylistic Set 14', - ['ss15'] = 'Stylistic Set 15', - ['ss16'] = 'Stylistic Set 16', - ['ss17'] = 'Stylistic Set 17', - ['ss18'] = 'Stylistic Set 18', - ['ss19'] = 'Stylistic Set 19', - ['ss20'] = 'Stylistic Set 20', - ['subs'] = 'Subscript', - ['sups'] = 'Superscript', - ['swsh'] = 'Swash', - ['titl'] = 'Titling', - ['tjmo'] = 'Trailing Jamo Forms', - ['tnam'] = 'Traditional Name Forms', - ['tnum'] = 'Tabular Figures', - ['trad'] = 'Traditional Forms', - ['twid'] = 'Third Widths', - ['unic'] = 'Unicase', - ['valt'] = 'Alternate Vertical Metrics', - ['vatu'] = 'Vattu Variants', - ['vert'] = 'Vertical Writing', - ['vhal'] = 'Alternate Vertical Half Metrics', - ['vjmo'] = 'Vowel Jamo Forms', - ['vkna'] = 'Vertical Kana Alternates', - ['vkrn'] = 'Vertical Kerning', - ['vpal'] = 'Proportional Alternate Vertical Metrics', - ['vrt2'] = 'Vertical Rotation', - ['zero'] = 'Slashed Zero' -} - -otf.tables.baselines = { - ['hang'] = 'Hanging baseline', - ['icfb'] = 'Ideographic character face bottom edge baseline', - ['icft'] = 'Ideographic character face tope edige baseline', - ['ideo'] = 'Ideographic em-box bottom edge baseline', - ['idtp'] = 'Ideographic em-box top edge baseline', - ['math'] = 'Mathmatical centered baseline', - ['romn'] = 'Roman baseline' +otf.tables.valid_fields = { + "anchor_classes", + "ascent", + "cache_version", + "cidinfo", + "copyright", + "creationtime", + "descent", + "design_range_bottom", + "design_range_top", + "design_size", + "encodingchanged", + "extrema_bound", + "familyname", + "fontname", + "fontstyle_id", + "fontstyle_name", + "fullname", + "glyphs", + "hasvmetrics", + "head_optimized_for_cleartype", + "horiz_base", + "issans", + "isserif", + "italicangle", + "kerns", + "lookups", + -- "luatex", + "macstyle", + "modificationtime", + "onlybitmaps", + "origname", + "os2_version", + "pfminfo", + "private", + "serifcheck", + "sfd_version", + -- "size", + "strokedfont", + "strokewidth", + "subfonts", + "table_version", + -- "tables", + -- "ttf_tab_saved", + "ttf_tables", + "uni_interp", + "uniqueid", + "units_per_em", + "upos", + "use_typo_metrics", + "uwidth", + "validation_state", + "verbose", + "version", + "vert_base", + "weight", + "weight_width_slope_only", + "xuid", } -function otf.tables.to_tag(id) - return stringformat("%4s",id:lower()) -end +--[[ldx-- +Here we go.
+--ldx]]-- -function otf.meanings.resolve(tab,id) - if tab and id then - id = id:lower() - return tab[id] or tab[id:gsub(" ","")] or tab['dflt'] or '' - else - return "unknown" +local function load_featurefile(ff,featurefile) + if featurefile then + featurefile = resolvers.find_file(file.addsuffix(featurefile,'fea')) -- "FONTFEATURES" + if featurefile and featurefile ~= "" then + if trace_loading then + logs.report("load otf", "featurefile: %s", featurefile) + end + fontloader.apply_featurefile(ff, featurefile) + end end end -function otf.meanings.script(id) - return otf.meanings.resolve(otf.tables.scripts,id) -end -function otf.meanings.language(id) - return otf.meanings.resolve(otf.tables.languages,id) -end -function otf.meanings.feature(id) - return otf.meanings.resolve(otf.tables.features,id) -end -function otf.meanings.baseline(id) - return otf.meanings.resolve(otf.tables.baselines,id) -end - -otf.tables.to_scripts = table.reverse_hash(otf.tables.scripts ) -otf.tables.to_languages = table.reverse_hash(otf.tables.languages) -otf.tables.to_features = table.reverse_hash(otf.tables.features ) - -do - - local scripts = otf.tables.scripts - local languages = otf.tables.languages - local features = otf.tables.features - - local to_scripts = otf.tables.to_scripts - local to_languages = otf.tables.to_languages - local to_features = otf.tables.to_features - - function otf.meanings.normalize(features) - local h = { } - for k,v in pairs(features) do - k = k:lower() -- :gsub("[^a-z0-9%-%.]" -- not needed - if k == "language" or k == "lang" then - v = (v:lower()):gsub("[^a-z0-9%-]","") - k = language - if not languages[v] then - h.language = to_languages[v] or "dflt" - else - h.language = v - end - elseif k == "script" then - v = (v:lower()):gsub("[^a-z0-9%-]","") - if not scripts[v] then - h.script = to_scripts[v] or "dflt" - else - h.script = v - end - else - if type(v) == "string" then - local b = v:is_boolean() - if type(b) == "nil" then - v = tonumber(v) or v:lower() -- gsub("[^a-z0-9%-]") -- too dangerous, e.g. featurefiles - else - v = b - end - end - h[to_features[k] or k] = v - end +function otf.enhance(name,data,filename,verbose) + local enhancer = otf.enhancers[name] + if enhancer then + if (verbose ~= nil and verbose) or trace_loading then + logs.report("load otf","enhance: %s",name) end - return h + enhancer(data,filename) end - end ---[[ldx-- -Here we go.
---ldx]]-- - -otf.enhance = otf.enhance or { } -otf.enhance.add_kerns = true - -otf.featurefiles = { ---~ "texhistoric.fea" +local enhancers = { + -- pack and unpack are handled separately; they might even be moved + -- away from the enhancers namespace + "patch bugs", + "merge cid fonts", "prepare unicode", "cleanup ttf tables", "compact glyphs", "reverse coverage", + "cleanup aat", "enrich with features", "add some missing characters", + "reorganize kerns", -- moved here + "flatten glyph lookups", "flatten anchor tables", "flatten feature tables", + "prepare luatex tables", + "analyse features", "rehash features", + "analyse anchors", "analyse marks", "analyse unicodes", "analyse subtables", + "check italic correction","check math", + "share widths", + "strip not needed data", + "migrate metadata", } function otf.load(filename,format,sub,featurefile) @@ -838,72 +219,45 @@ function otf.load(filename,format,sub,featurefile) end if sub == "" then sub = false end local hash = name - if sub then -- name cleanup will move to cache code + if sub then hash = hash .. "-" .. sub - hash = hash:lower() - hash = hash:gsub("[^%w%d]+","-") end + hash = containers.cleanname(hash) local data = containers.read(otf.cache(), hash) - if data and data.verbose ~= fonts.verbose then - data = nil - end local size = lfs.attributes(filename,"size") or 0 - if data and data.size ~= size then - data = nil - end - if not data then + if not data or data.verbose ~= fonts.verbose or data.size ~= size then logs.report("load otf","loading: %s",filename) local ff, messages if sub then - ff, messages = fontforge.open(filename,sub) + ff, messages = fontloader.open(filename,sub) else - ff, messages = fontforge.open(filename) + ff, messages = fontloader.open(filename) end - if messages and #messages > 0 then - for _, m in ipairs(messages) do - logs.report("load otf","warning: %s",m) + if trace_loading and messages and #messages > 0 then + for m=1,#messages do + logs.report("load otf","warning: %s",messages[m]) end end if ff then - local function load_featurefile(featurefile) - if featurefile then - featurefile = input.find_file(file.addsuffix(featurefile,'fea')) -- "FONTFEATURES" - if featurefile and featurefile ~= "" then - logs.report("load otf", "featurefile: %s", featurefile) - fontforge.apply_featurefile(ff, featurefile) - end - end - end - -- for _, featurefile in pairs(otf.featurefiles) do - -- load_featurefile(featurefile) - -- end - load_featurefile(featurefile) - data = fontforge.to_table(ff) - fontforge.close(ff) + load_featurefile(ff,featurefile) + data = fontloader.to_table(ff) + fontloader.close(ff) if data then - logs.report("load otf","enhance: patch") - otf.enhance.patch(data,filename) - logs.report("load otf","enhance: before") - otf.enhance.before(data,filename) - logs.report("load otf","enhance: enrich") - otf.enhance.enrich(data,filename) - logs.report("load otf","enhance: flatten") - otf.enhance.flatten(data,filename) - logs.report("load otf","enhance: analyze") - otf.enhance.analyze(data,filename) - logs.report("load otf","enhance: after") - otf.enhance.after(data,filename) - logs.report("load otf","enhance: strip") - otf.enhance.strip(data,filename) + logs.report("load otf","file size: %s", size) + logs.report("load otf","enhancing ...") + for e=1,#enhancers do + otf.enhance(enhancers[e],data,filename) + end if otf.pack and not fonts.verbose then - logs.report("load otf","enhance: pack") - otf.enhance.pack(data) + otf.enhance("pack",data,filename) end - logs.report("load otf","file size: %s", size) data.size = size data.verbose = fonts.verbose - logs.report("load otf","saving: in cache") + logs.report("load otf","saving in cache: %s",filename) data = containers.write(otf.cache(), hash, data) + collectgarbage("collect") + data = containers.read(otf.cache(), hash) -- this frees the old table and load the sparse one + collectgarbage("collect") else logs.report("load otf","loading failed (table conversion error)") end @@ -911,375 +265,357 @@ function otf.load(filename,format,sub,featurefile) logs.report("load otf","loading failed (file read error)") end end - otf.enhance.unpack(data) + if data then + otf.enhance("unpack",data,filename,false) -- no message here + otf.add_dimensions(data) + if trace_sequences then + otf.show_feature_order(data,filename) + end + end return data end --- memory saver .. - -local criterium, threshold = 1, 0 - -function otf.enhance.pack(data) +function otf.add_dimensions(data) + -- todo: forget about the width if it's the defaultwidth (saves mem) + -- we could also build the marks hash here (instead of storing it) if data then - local h, t, c = { }, { }, { } - local hh, tt, cc = { }, { }, { } - local function tabstr(t) - for i=1,#t do - -- tricky, was if type(t[i]) == "boolean" then, but if no [1] then error - local ti = type(t[i]) - if ti ~= "string" or ti ~= "number" then - local s = tostring(t[1]) - for i=2,#t do - s = s .. ",".. tostring(t[i]) - end - return s - end - end - return concat(t,",") - end - for pass=1,2 do - local pack - if pass == 1 then - pack = function(v) - -- v == table - local tag = tabstr(v,",") - local ht = h[tag] - if not ht then - ht = #t+1 - t[ht] = v - h[tag] = ht - c[ht] = 1 - else - c[ht] = c[ht] + 1 - end - return ht - end - else - pack = function(v) - -- v == number - if c[v] <= criterium then - return t[v] - else - -- compact hash - local hv = hh[v] - if not hv then - hv = #tt+1 - tt[hv] = t[v] - hh[v] = hv - cc[hv] = c[v] - end - return hv - end - end - end - for k, v in pairs(data.glyphs) do - v.boundingbox = pack(v.boundingbox) - if v.lookups then - for k,v in pairs(v.lookups) do - for kk=1,#v do - v[kk] = pack(v[kk]) - end - end - end - local a = v.anchors - if a then - for k,v in pairs(a) do - if k == "baselig" then - for kk, vv in pairs(v) do - for kkk=1,#vv do - vv[kkk] = pack(vv[kkk]) - end - end - else - for kk, vv in pairs(v) do - v[kk] = pack(vv) - end - end - end - end + local force = otf.notdef + local luatex = data.luatex + local defaultwidth = luatex.defaultwidth or 0 + local defaultheight = luatex.defaultheight or 0 + local defaultdepth = luatex.defaultdepth or 0 + for _, d in next, data.glyphs do + local bb, wd = d.boundingbox, d.width + if not wd then + d.width = defaultwidth + elseif wd ~= 0 and d.class == "mark" then + d.width = -wd end - if data.lookups then - for k, v in pairs(data.lookups) do - if v.rules then - for kk, vv in pairs(v.rules) do - local l = vv.lookups - if l then - vv.lookups = pack(l) - end - local c = vv.coverage - if c then - c.before = c.before and pack(c.before ) - c.after = c.after and pack(c.after ) - c.current = c.current and pack(c.current) - end - end - end - end + if force and not d.name then + d.name = ".notdef" end - if data.luatex then - local li = data.luatex.ignore_flags - if li then - for k, v in pairs(li) do - li[k] = pack(v) - end + if bb then + local ht, dp = bb[4], -bb[2] + if ht == 0 or ht < 0 then + -- no need to set it and no negative heights, nil == 0 + else + d.height = ht end - end - if #t == 0 then - logs.report("load otf","pack quality: nothing to pack") - break - elseif #t >= threshold then - local one, two, rest = 0, 0, 0 - if pass == 1 then - for k,v in pairs(c) do - if v == 1 then - one = one + 1 - elseif v == 2 then - two = two + 1 - else - rest = rest + 1 - end - end + if dp == 0 or dp < 0 then + -- no negative depths and no negative depths, nil == 0 else - for k,v in pairs(cc) do - if v >20 then - rest = rest + 1 - elseif v >10 then - two = two + 1 - else - one = one + 1 - end - end - data.tables = tt + d.depth = dp end - logs.report("load otf","pack quality: pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)", pass, one+two+rest, one, two, rest, criterium) - else - logs.report("load otf","pack quality: pass 1, %s packed, aborting pack (threshold: %s)", #t, threshold) - break end end end end -function otf.enhance.unpack(data) - if data then - local t = data.tables - if t then - for k, v in pairs(data.glyphs) do - local tv = t[v.boundingbox] if tv then v.boundingbox = tv end - local l = v.lookups - if l then - for k,v in pairs(l) do - for i=1,#v do - local tv = t[v[i]] if tv then v[i] = tv end - end - end - end - local a = v.anchors - if a then - for k,v in pairs(a) do - if k == "baselig" then - for kk, vv in pairs(v) do - for kkk=1,#vv do - local tv = t[vv[kkk]] if tv then vv[kkk] = tv end - end - end - else - for kk, vv in pairs(v) do - local tv = t[vv] if tv then v[kk] = tv end - end - end - end - end +function otf.show_feature_order(otfdata,filename) + local sequences = otfdata.luatex.sequences + if sequences and #sequences > 0 then + if trace_loading then + logs.report("otf check","font %s has %s sequences",filename,#sequences) + logs.report("otf check"," ") + end + for nos=1,#sequences do + local sequence = sequences[nos] + local typ = sequence.type or "no-type" + local name = sequence.name or "no-name" + local subtables = sequence.subtables or { "no-subtables" } + local features = sequence.features + if trace_loading then + logs.report("otf check","%3i %-15s %-20s [%s]",nos,name,typ,concat(subtables,",")) end - if data.lookups then - for k, v in pairs(data.lookups) do - local r = v.rules - if r then - for kk, vv in pairs(r) do - local l = vv.lookups - if l then - local tv = t[l] if tv then vv.lookups = tv end - end - local c = vv.coverage - if c then - local cc = c.before if cc then local tv = t[cc] if tv then c.before = tv end end - cc = c.after if cc then local tv = t[cc] if tv then c.after = tv end end - cc = c.current if cc then local tv = t[cc] if tv then c.current = tv end end - end + if features then + for feature, scripts in next, features do + local tt = { } + for script, languages in next, scripts do + local ttt = { } + for language, _ in next, languages do + ttt[#ttt+1] = language end + tt[#tt+1] = format("[%s: %s]",script,concat(ttt," ")) end - end - end - if data.luatex then - local li = data.luatex.ignore_flags - if li then - for k, v in pairs(li) do - local tv = t[v] if tv then li[k] = tv end + if trace_loading then + logs.report("otf check"," %s: %s",feature,concat(tt," ")) end end end - data.tables = nil end + if trace_loading then + logs.report("otf check","\n") + end + elseif trace_loading then + logs.report("otf check","font %s has no sequences",filename) end end -- todo: normalize, design_size => designsize -function otf.enhance.analyze(data,filename) - local t = { ---~ filename = file.basename(filename), - filename = filename, - version = otf.version, - creator = "context mkiv", - unicodes = otf.analyze_unicodes(data), - gposfeatures = otf.analyze_features(data.gpos), - gsubfeatures = otf.analyze_features(data.gsub), - marks = otf.analyze_class(data,'mark'), - } - t.subtables, t.name_to_type, t.internals, t.always_valid, t.ignore_flags, t.ctx_always = otf.analyze_subtables(data) - data.luatex = t +otf.enhancers["prepare luatex tables"] = function(data,filename) + data.luatex = data.luatex or { } + local luatex = data.luatex + luatex.filename = filename + luatex.version = otf.version + luatex.creator = "context mkiv" end -do - -- original string parser: 0.109, lpeg parser: 0.036 seconds for Adobe-CNS1-4.cidmap - -- - -- 18964 18964 (leader) - -- 0 /.notdef - -- 1..95 0020 - -- 99 3000 - - local number = lpeg.C(lpeg.R("09","af","AF")^1) - local space = lpeg.S(" \n\r\t") - local spaces = space^0 - local period = lpeg.P(".") - local periods = period * period - local name = lpeg.P("/") * lpeg.C((1-space)^1) - - local unicodes, names = { }, {} - - local function do_one(a,b) - unicodes[tonumber(a)] = tonumber(b,16) +otf.enhancers["cleanup aat"] = function(data,filename) + if otf.cleanup_aat then end - local function do_range(a,b,c) - c = tonumber(c,16) - for i=tonumber(a),tonumber(b) do - unicodes[i] = c - c = c + 1 - end - end - local function do_name(a,b) - names[tonumber(a)] = b - end - - local grammar = lpeg.P { "start", - start = number * spaces * number * lpeg.V("series"), - series = (spaces * (lpeg.V("one") + lpeg.V("range") + lpeg.V("named")) )^1, - one = (number * spaces * number) / do_one, - range = (number * periods * number * spaces * number) / do_range, - named = (number * spaces * name) / do_name - } +end - function otf.load_cidmap(filename) -- lpeg - local data = io.loaddata(filename) - if data then - unicodes, names = { }, { } - grammar:match(data) - local supplement, registry, ordering = filename:match("^(.-)%-(.-)%-()%.(.-)$") - return { - supplement = supplement, - registry = registry, - ordering = ordering, - filename = filename, - unicodes = unicodes, - names = names - } - else - return nil +local function analyze_features(g, features) + if g then + local t, done = { }, { } + for k=1,#g do + local f = features or g[k].features + if f then + for k=1,#f do + -- scripts and tag + local tag = f[k].tag + if not done[tag] then + t[#t+1] = tag + done[tag] = true + end + end + end + end + if #t > 0 then + return t end end - + return nil end -otf.cidmaps = { } -otf.cidmax = 10 +otf.enhancers["analyse features"] = function(data,filename) + -- local luatex = data.luatex + -- luatex.gposfeatures = analyze_features(data.gpos) + -- luatex.gsubfeatures = analyze_features(data.gsub) +end -function otf.cidmap(registry,ordering,supplement) - -- cf Arthur R. we can safely scan upwards since cids are downward compatible - local template = "%s-%s-%s.cidmap" - local supplement = tonumber(supplement) - logs.report("load otf","needed cidmap, registry: %s, ordering: %s, supplement: %s",registry,ordering,supplement) - local function locate(registry,ordering,supplement) - local filename = format(template,registry,ordering,supplement) - local cidmap = otf.cidmaps[filename] - if not cidmap then - logs.report("load otf","checking cidmap, registry: %s, ordering: %s, supplement: %s, filename: %s",registry,ordering,supplement,filename) - local fullname = input.find_file(filename,'cid') or "" - if fullname ~= "" then - cidmap = otf.load_cidmap(fullname) - if cidmap then - logs.report("load otf","using cidmap file %s",filename) - otf.cidmaps[filename] = cidmap - return cidmap +otf.enhancers["rehash features"] = function(data,filename) + local features = { } + data.luatex.features = features + for k, what in next, otf.glists do + local dw = data[what] + if dw then + local f = { } + features[what] = f + for i=1,#dw do + local d= dw[i] + local dfeatures = d.features + if dfeatures then + for i=1,#dfeatures do + local df = dfeatures[i] + local tag = strip(lower(df.tag)) + local ft = f[tag] if not ft then ft = {} f[tag] = ft end + local dscripts = df.scripts + for script, languages in next, dscripts do + script = strip(lower(script)) + local fts = ft[script] if not fts then fts = {} ft[script] = fts end + for i=1,#languages do + fts[strip(lower(languages[i]))] = true + end + end + end end end end - return cidmap end - local cidmap = locate(registry,ordering,supplement) - if not cidmap then - local cidnum = nil - -- next highest (alternatively we could start high) - if supplement < otf.cidmax then - for supplement=supplement+1,otf.cidmax do - local c = locate(registry,ordering,supplement) - if c then - cidmap, cidnum = c, supplement - break - end +end + +otf.enhancers["analyse anchors"] = function(data,filename) + local classes = data.anchor_classes + local luatex = data.luatex + local anchor_to_lookup, lookup_to_anchor = { }, { } + luatex.anchor_to_lookup, luatex.lookup_to_anchor = anchor_to_lookup, lookup_to_anchor + if classes then + for c=1,#classes do + local class = classes[c] + local anchor = class.name + local lookups = class.lookup + if type(lookups) ~= "table" then + lookups = { lookups } + end + local a = anchor_to_lookup[anchor] + if not a then a = { } anchor_to_lookup[anchor] = a end + for l=1,#lookups do + local lookup = lookups[l] + local l = lookup_to_anchor[lookup] + if not l then l = { } lookup_to_anchor[lookup] = l end + l[anchor] = true + a[lookup] = true end end - -- next lowest (least worse fit) - if not cidmap and supplement > 0 then - for supplement=supplement-1,0,-1 do - local c = locate(registry,ordering,supplement) - if c then - cidmap, cidnum = c, supplement - break - end - end + end +end + +otf.enhancers["analyse marks"] = function(data,filename) + local glyphs = data.glyphs + local marks = { } + data.luatex.marks = marks + for unicode, index in next, data.luatex.indices do + local glyph = glyphs[index] + if glyph.class == "mark" then + marks[unicode] = true end - -- prevent further lookups - if cidmap and cidnum > 0 then - for s=0,cidnum-1 do - filename = format(template,registry,ordering,s) - if not otf.cidmaps[filename] then - otf.cidmaps[filename] = cidmap -- copy of ref + end +end + +local other = lpeg.C((1 - lpeg.S("_."))^0) +local ligsplitter = lpeg.Ct(other * (lpeg.P("_") * other)^0) + +--~ print(splitter:match("this")) +--~ print(splitter:match("this.that")) +--~ print(splitter:match("such_so_more")) +--~ print(splitter:match("such_so_more.that")) + +otf.enhancers["analyse unicodes"] = function(data,filename) + local unicodes = data.luatex.unicodes + -- we need to move this code + unicodes['space'] = unicodes['space'] or 32 -- handly later on + unicodes['hyphen'] = unicodes['hyphen'] or 45 -- handly later on + unicodes['zwj'] = unicodes['zwj'] or zwj -- handly later on + unicodes['zwnj'] = unicodes['zwnj'] or zwnj -- handly later on + -- the tounicode mapping is sparse and only needed for alternatives + local tounicode, originals, ns, nl, private, unknown = { }, { }, 0, 0, fonts.private, format("%04X",utfbyte("?")) + data.luatex.tounicode, data.luatex.originals = tounicode, originals + for index, glyph in next, data.glyphs do + local name, unic = glyph.name, glyph.unicode or -1 -- play safe + if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then + -- a.whatever or a_b_c.whatever or a_b_c + local split = ligsplitter:match(name) + if #split == 0 then + -- skip + elseif #split == 1 then + local u = unicodes[split[1]] + if u then + if type(u) == "table" then + u = u[1] + end + if u < 0x10000 then + originals[index], tounicode[index] = u, format("%04X",u) + else + originals[index], tounicode[index] = u, format("%04X%04X",u/1024+0xD800,u%1024+0xDC00) + end + ns = ns + 1 + else + originals[index], tounicode[index] = 0xFFFD, "FFFD" + end + else + local as = { } + for l=1,#split do + local u = unicodes[split[l]] + if not u then + as[l], split[l] = 0xFFFD, "FFFD" + else + if type(u) == "table" then + u = u[1] + end + if u < 0x10000 then + as[l], split[l] = u, format("%04X",u) + else + as[l], split[l] = u, format("%04X%04X",u/1024+0xD800,u%1024+0xDC00) + end + end + end + split = concat(split) + if split ~= "" then + originals[index], tounicode[index] = as, split + nl = nl + 1 + else + originals[index], tounicode[index] = 0xFFFD, "FFFD" end end end end - return cidmap + if trace_loading and (ns > 0 or nl > 0) then + logs.report("load otf","enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns) + end end ---~ ["cidinfo"]={ ---~ ["ordering"]="Japan1", ---~ ["registry"]="Adobe", ---~ ["supplement"]=6, ---~ ["version"]=6, ---~ }, +otf.enhancers["analyse subtables"] = function(data,filename) + data.luatex = data.luatex or { } + local luatex = data.luatex + local sequences = { } + local lookups = { } + luatex.sequences = sequences + luatex.lookups = lookups + for _, g in next, { data.gsub, data.gpos } do + for k=1,#g do + local gk = g[k] -function otf.enhance.before(data,filename) - local private = fonts.private +local typ = gk.type +if typ == "gsub_contextchain" or typ == "gpos_contextchain" then + gk.chain = 1 +elseif typ == "gsub_reversecontextchain" or typ == "gpos_reversecontextchain" then + gk.chain = -1 +else + gk.chain = 0 +end + + local features = gk.features + if features then + sequences[#sequences+1] = gk + -- scripts, tag, ismac + local t = { } + for f=1,#features do + local feature = features[f] + local hash = { } + -- only script and langs matter + for s, languages in next, feature.scripts do + s = lower(s) + local h = hash[s] + if not h then h = { } hash[s] = h end + for l=1,#languages do + h[strip(lower(languages[l]))] = true + end + end + t[feature.tag] = hash + end + gk.features = t + else + lookups[gk.name] = gk + gk.name = nil + end + local subtables = gk.subtables + if subtables then + local t = { } + for s=1,#subtables do + local subtable = subtables[s] + local name = subtable.name + t[#t+1] = name + end + gk.subtables = t + end + local flags = gk.flags + if flags then + gk.flags = { -- forcing false packs nicer + (flags.ignorecombiningmarks and "mark") or false, + (flags.ignoreligatures and "ligature") or false, + (flags.ignorebaseglyphs and "base") or false, + flags.r2l or false + } + end + end + end +end + +otf.enhancers["merge cid fonts"] = function(data,filename) + -- we can also move the names to data.luatex.names which might + -- save us some more memory (at the cost of harder tracing) if data.subfonts and table.is_empty(data.glyphs) then local cidinfo = data.cidinfo local verbose = fonts.verbose if cidinfo.registry then - local cidmap = otf.cidmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) + local cidmap = fonts.cid.getmap and fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) if cidmap then local glyphs, uni_to_int, int_to_uni, nofnames, nofunicodes = { }, { }, { }, 0, 0 local unicodes, names = cidmap.unicodes, cidmap.names - for n, subfont in pairs(data.subfonts) do - for index, g in pairs(subfont.glyphs) do + for n, subfont in next, data.subfonts do + for index, g in next, subfont.glyphs do if not next(g) then -- dummy entry else @@ -1288,90 +624,143 @@ function otf.enhance.before(data,filename) g.boundingbox = g.boundingbox -- or zerobox g.name = g.name or name or "unknown" if unicode then --- g.unicode = unicode uni_to_int[unicode] = index int_to_uni[index] = unicode nofunicodes = nofunicodes + 1 + g.unicode = unicode elseif name then --- g.unicode = -1 nofnames = nofnames + 1 + g.unicode = -1 end glyphs[index] = g end end subfont.glyphs = nil end - logs.report("load otf","cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames) + if trace_loading then + logs.report("load otf","cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames) + end data.glyphs = glyphs data.map = data.map or { } data.map.map = uni_to_int data.map.backmap = int_to_uni - else + elseif trace_loading then logs.report("load otf","unable to remap cid font, missing cid file for %s",filename) end - else + elseif trace_loading then logs.report("load otf","font %s has no glyphs",filename) end end - if data.map then - local uni_to_int = data.map.map -- [unic] = slot - local int_to_uni = data.map.backmap -- { [0|1] = unic, ... } - for index, glyph in pairs(data.glyphs) do - if glyph.name then --- local unic = glyph.unicode or glyph.unicodeenc or -1 -local unic = int_to_uni[index] or -1 - if index > 0 and (unic == -1 or unic >= 0x110000) then - while uni_to_int[private] do - private = private + 1 - end - uni_to_int[private] = index - int_to_uni[index] = private --- glyph.unicode = private - if fonts.trace then - logs.report("load otf","enhance: glyph %s at index %s is moved to private unicode slot %s",glyph.name,index,private) - end +end + +otf.enhancers["prepare unicode"] = function(data,filename) + local luatex = data.luatex + if not luatex then luatex = { } data.luatex = luatex end + local indices, unicodes, multiples, internals = { }, { }, { }, { } + local glyphs = data.glyphs + local mapmap = data.map + if not mapmap then + logs.report("load otf","no map in %s",filename) + mapmap = { } + data.map = { map = mapmap } + elseif not mapmap.map then + logs.report("load otf","no unicode map in %s",filename) + mapmap = { } + data.map.map = mapmap + else + mapmap = mapmap.map + end + local criterium = fonts.private + local private = fonts.private + for index, glyph in next, glyphs do + if index > 0 then + local name = glyph.name + if name then + local unicode = glyph.unicode + if unicode == -1 or unicode >= criterium then + glyph.unicode = private + indices[private] = index + unicodes[name] = private + internals[index] = true + if trace_private then + logs.report("load otf","enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",name,index,private) + end + private = private + 1 else - glyph.unicode = unic -- safeguard for older version + indices[unicode] = index + unicodes[name] = unicode end end end - local n = 0 - for k,v in pairs(int_to_uni) do - if v == -1 or v >= 0x110000 then - int_to_uni[k], n = nil, n+1 - end - end - if fonts.trace then - logs.report("load otf","enhance: %s entries removed from map.backmap",n) - end - local n = 0 - for k,v in pairs(uni_to_int) do - if k == -1 or k >= 0x110000 then - uni_to_int[k], n = nil, n+1 + end + -- beware: the indeces table is used to initialize the tfm table + for unicode, index in next, mapmap do + if not internals[index] then + local name = glyphs[index].name + if name then + local un = unicodes[name] + if not un then + unicodes[name] = unicode -- or 0 + elseif type(un) == "number" then + if un ~= unicode then + multiples[#multiples+1] = name + unicodes[name] = { un, unicode } + indices[unicode] = index + end + else + local ok = false + for u=1,#un do + if un[u] == unicode then + ok = true + break + end + end + if not ok then + multiples[#multiples+1] = name + un[#un+1] = unicode + indices[unicode] = index + end + end end end - if fonts.trace then - logs.report("load otf","enhance: %s entries removed from map.mapmap",n) + end + if trace_loading then + if #multiples > 0 then + logs.report("load otf","%s glyph are reused: %s",#multiples, concat(multiples," ")) + else + logs.report("load otf","no glyph are reused") end - else - data.map = { map = {}, backmap = {} } end - if data.ttf_tables then - for _, v in ipairs(data.ttf_tables) do - if v.data then v.data = "deleted" end + luatex.indices = indices + luatex.unicodes = unicodes + luatex.private = private +end + +otf.enhancers["cleanup ttf tables"] = function(data,filename) + local ttf_tables = data.ttf_tables + if ttf_tables then + for k=1,#ttf_tables do + if ttf_tables[k].data then ttf_tables[k].data = "deleted" end end end - table.compact(data.glyphs) + data.ttf_tab_saved = nil +end + +otf.enhancers["compact glyphs"] = function(data,filename) + table.compact(data.glyphs) -- needed? if data.subfonts then - for _, subfont in pairs(data.subfonts) do - table.compact(subfont.glyphs) + for _, subfont in next, data.subfonts do + table.compact(subfont.glyphs) -- needed? end end +end + +otf.enhancers["reverse coverage"] = function(data,filename) -- we prefer the before lookups in a normal order if data.lookups then - for _, v in pairs(data.lookups) do + for _, v in next, data.lookups do if v.rules then - for _, vv in pairs(v.rules) do + for _, vv in next, v.rules do local c = vv.coverage if c and c.before then c.before = table.reverse(c.before) @@ -1382,91 +771,239 @@ local unic = int_to_uni[index] or -1 end end -function otf.enhance.after(data,filename) -- to be split - if otf.enhance.add_kerns then - local glyphs, mapmap, unicodes = data.glyphs, data.map.map, data.luatex.unicodes - local mkdone = false - for index, glyph in pairs(data.glyphs) do - if glyph.kerns then - local mykerns = { } -- unicode indexed ! - for k,v in pairs(glyph.kerns) do - local vc, vo, vl = v.char, v.off, v.lookup - if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones - local uvc = unicodes[vc] - if not uvc then - logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index) - else - local mkl = mykerns[vl] - if not mkl then - mkl = { } - mykerns[v.lookup] = mkl +otf.enhancers["check italic correction"] = function(data,filename) + local glyphs = data.glyphs + local ok = false + for index, glyph in next, glyphs do + local ic = glyph.italic_correction + if ic then + if ic ~= 0 then + glyph.italic = ic + end + glyph.italic_correction = nil + ok = true + end + end + -- we can use this to avoid calculations + otf.tables.valid_fields[#otf.tables.valid_fields+1] = "has_italic" + data.has_italic = true +end + +otf.enhancers["check math"] = function(data,filename) + if data.math then + -- we move the math stuff into a math subtable because we then can + -- test faster in the tfm copy + local glyphs = data.glyphs + local unicodes = data.luatex.unicodes + for index, glyph in next, glyphs do + local mk = glyph.mathkern + local hv = glyph.horiz_variants + local vv = glyph.vert_variants + if mk or hv or vv then + local math = { } + glyph.math = math + if mk then + for k, v in next, mk do + if not next(v) then + mk[k] = nil + end + end + math.kerns = mk + glyph.mathkern = nil + end + if hv then + math.horiz_variants = hv.variants + local p = hv.parts + if p then + if #p>0 then + for i=1,#p do + local pi = p[i] + pi.glyph = unicodes[pi.component] or 0 end - if type(uvc) == "table" then - for u=1,#uvc do - mkl[uvc[u]] = vo - end - else - mkl[uvc] = vo + math.horiz_parts = p + end + end + local ic = hv.italic_correction + if ic and ic ~= 0 then + math.horiz_italic_correction = ic + end + glyph.horiz_variants = nil + end + if vv then + local uc = unicodes[index] + math.vert_variants = vv.variants + local p = vv.parts + if p then + if #p>0 then + for i=1,#p do + local pi = p[i] + pi.glyph = unicodes[pi.component] or 0 end + math.vert_parts = p end end + local ic = vv.italic_correction + if ic and ic ~= 0 then + math.vert_italic_correction = ic + end + glyph.vert_variants = nil + end + local ic = glyph.italic_correction + if ic then + if ic ~= 0 then + math.italic_correction = ic + end + glyph.italic_correction = nil end - glyph.mykerns = mykerns - glyph.kerns = nil -- saves space and time - mkdone = true end end - if mkdone then - logs.report("load otf", "replacing 'kerns' tables by 'mykerns' tables") + end +end + +otf.enhancers["share widths"] = function(data,filename) + local glyphs = data.glyphs + local widths = { } + for index, glyph in next, glyphs do + local width = glyph.width + widths[width] = (widths[width] or 0) + 1 + end + -- share width for cjk fonts + local wd, most = 0, 1 + for k,v in next, widths do + if v > most then + wd, most = k, v end - if data.gpos then - for _, gpos in ipairs(data.gpos) do - if gpos.subtables then - for _, subtable in ipairs(gpos.subtables) do - local kernclass = subtable.kernclass - if kernclass then - for _, kcl in ipairs(kernclass) do - local firsts, seconds, offsets, lookup = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup + end + if most > 1000 then + if trace_loading then + logs.report("load otf", "most common width: %s (%s times), sharing (cjk font)",wd,most) + end + for k, v in next, glyphs do + if v.width == wd then + v.width = nil + end + end + data.luatex.defaultwidth = wd + end +end + +-- kern: ttf has a table with kerns + +otf.enhancers["reorganize kerns"] = function(data,filename) + local glyphs, mapmap, unicodes = data.glyphs, data.luatex.indices, data.luatex.unicodes + local mkdone = false + for index, glyph in next, data.glyphs do + if glyph.kerns then + local mykerns = { } + for k,v in next, glyph.kerns do + local vc, vo, vl = v.char, v.off, v.lookup + if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones + local uvc = unicodes[vc] + if not uvc then + if trace_loading then + logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index) + end + else + if type(vl) ~= "table" then + vl = { vl } + end + for l=1,#vl do + local vll = vl[l] + local mkl = mykerns[vll] + if not mkl then + mkl = { } + mykerns[vll] = mkl + end + if type(uvc) == "table" then + for u=1,#uvc do + mkl[uvc[u]] = vo + end + else + mkl[uvc] = vo + end + end + end + end + end + glyph.mykerns = mykerns + glyph.kerns = nil -- saves space and time + mkdone = true + end + end + if trace_loading and mkdone then + logs.report("load otf", "replacing 'kerns' tables by 'mykerns' tables") + end + if data.kerns then + if trace_loading then + logs.report("load otf", "removing global 'kern' table") + end + data.kerns = nil + end + local dgpos = data.gpos + if dgpos then + for gp=1,#dgpos do + local gpos = dgpos[gp] + local subtables = gpos.subtables + if subtables then + for s=1,#subtables do + local subtable = subtables[s] + local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes + if kernclass then + for k=1,#kernclass do + local kcl = kernclass[k] + local firsts, seconds, offsets, lookups = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup -- singular + if type(lookups) ~= "table" then + lookups = { lookups } + end + for l=1,#lookups do + local lookup = lookups[l] local maxfirsts, maxseconds = getn(firsts), getn(seconds) - logs.report("load otf", "adding kernclass %s with %s times %s pairs)",lookup, maxfirsts, maxseconds) - for fk, fv in pairs(firsts) do - for first in fv:gmatch("[^ ]+") do + if trace_loading then + logs.report("load otf", "adding kernclass %s with %s times %s pairs",lookup, maxfirsts, maxseconds) + end + for fk, fv in next, firsts do + for first in gmatch(fv,"[^ ]+") do local first_unicode = unicodes[first] if type(first_unicode) == "number" then first_unicode = { first_unicode } end for f=1,#first_unicode do local glyph = glyphs[mapmap[first_unicode[f]]] - local mykerns = glyph.mykerns - if not mykerns then - mykerns = { } -- unicode indexed ! - glyph.mykerns = mykerns - end - local lookupkerns = mykerns[lookup] - if not lookupkerns then - lookupkerns = { } - mykerns[lookup] = lookupkerns - end - for sk, sv in pairs(seconds) do - local offset = offsets[(fk-1) * maxseconds + sk] - for second in sv:gmatch("[^ ]+") do - local second_unicode = unicodes[second] - if type(second_unicode) == "number" then - lookupkerns[second_unicode] = offset - else - for s=1,#second_unicode do - lookupkerns[second_unicode[s]] = offset + if glyph then + local mykerns = glyph.mykerns + if not mykerns then + mykerns = { } -- unicode indexed ! + glyph.mykerns = mykerns + end + local lookupkerns = mykerns[lookup] + if not lookupkerns then + lookupkerns = { } + mykerns[lookup] = lookupkerns + end + for sk, sv in next, seconds do + local offset = offsets[(fk-1) * maxseconds + sk] + --~ local offset = offsets[sk] -- (fk-1) * maxseconds + sk] + for second in gmatch(sv,"[^ ]+") do + local second_unicode = unicodes[second] + if type(second_unicode) == "number" then + lookupkerns[second_unicode] = offset + else + for s=1,#second_unicode do + lookupkerns[second_unicode[s]] = offset + end end end end + elseif trace_loading then + logs.report("load otf", "no glyph data for U+%04X", first_unicode[f]) end end end end end - subtable.comment = "The kernclass table is merged into mykerns in the indexed glyph tables." - subtable.kernclass = { } end + subtable.comment = "The kernclass table is merged into mykerns in the indexed glyph tables." + subtable.kernclass = { } end end end @@ -1474,21 +1011,28 @@ function otf.enhance.after(data,filename) -- to be split end end -function otf.enhance.strip(data) +otf.enhancers["strip not needed data"] = function(data,filename) local verbose = fonts.verbose - local int_to_uni = data.map.backmap - for k, v in pairs(data.glyphs) do + local int_to_uni = data.luatex.unicodes + for k, v in next, data.glyphs do local d = v.dependents if d then v.dependents = nil end + local a = v.altuni + if a then v.altuni = nil end if verbose then local code = int_to_uni[k] + -- looks like this is done twice ... bug? if code then local vu = v.unicode if not vu then v.unicode = code elseif type(vu) == "table" then - vu[#bu+1] = code - else + if vu[#vu] == code then + -- weird + else + vu[#vu+1] = code + end + elseif vu ~= code then v.unicode = { vu, code } end end @@ -1498,19 +1042,39 @@ function otf.enhance.strip(data) end end data.luatex.comment = "Glyph tables have their original index. When present, mykern tables are indexed by unicode." - data.luatex.indices = data.map.map -- needed for shared glyphs data.map = nil - data.names = nil + data.names = nil -- funny names for editors data.glyphcnt = nil data.glyphmax = nil + if true then + data.gpos = nil + data.gsub = nil + data.anchor_classes = nil + end +end + +otf.enhancers["migrate metadata"] = function(data,filename) + local global_fields = otf.tables.global_fields + local metadata = { } + for k,v in next, data do + if not global_fields[k] then + metadata[k] = v + data[k] = nil + end + end + data.metadata = metadata + -- goodies + local pfminfo = data.pfminfo + metadata.isfixedpitch = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose["proportion"] == "Monospaced") + metadata.charwidth = pfminfo and pfminfo.avgwidth end -function otf.enhance.flatten(data,filename) -- to be split - logs.report("load otf", "flattening 'specifications' tables") - for k, v in pairs(data.glyphs) do +otf.enhancers["flatten glyph lookups"] = function(data,filename) + for k, v in next, data.glyphs do if v.lookups then - for kk, vv in pairs(v.lookups) do - for kkk, vvv in ipairs(vv) do + for kk, vv in next, v.lookups do + for kkk=1,#vv do + local vvv = vv[kkk] local s = vvv.specification if s then local t = vvv.type @@ -1523,27 +1087,29 @@ function otf.enhance.flatten(data,filename) -- to be split elseif t == "multiple" then vv[kkk] = { "multiple", s.components } elseif t == "position" then - vv[kkk] = { "position", s.x or 0, s.y or 0, s.h or 0, s.v or 0 } + vv[kkk] = { "position", { s.x or 0, s.y or 0, s.h or 0, s.v or 0 } } elseif t == "pair" then local one, two, paired = s.offsets[1], s.offsets[2], s.paired or "" if one then if two then - vv[kkk] = { "pair", paired, one.x or 0, one.y or 0, one.h or 0, one.v or 0, two.x or 0, two.y or 0, two.h or 0, two.v or 0 } + vv[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0 } } else - vv[kkk] = { "pair", paired, one.x or 0, one.y or 0, one.h or 0 } + vv[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } } end else if two then - vv[kkk] = { "pair", paired, 0, 0, 0, 0, two.x or 0, two.y or 0, two.h or 0, two.v or 0 } + vv[kkk] = { "pair", paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { } else vv[kkk] = { "pair", paired } end end else - logs.report("load otf", "flattening needed, warn Hans and/or Taco") - for a, b in pairs(s) do - if vvv[a] then - logs.report("load otf", "flattening conflict, warn Hans and/or Taco") + if trace_loading then + logs.report("load otf", "flattening needed, report to context list") + end + for a, b in next, s do + if trace_loading and vvv[a] then + logs.report("load otf", "flattening conflict, report to context list") end vvv[a] = b end @@ -1554,15 +1120,18 @@ function otf.enhance.flatten(data,filename) -- to be split end end end - logs.report("load otf", "flattening 'anchor' tables") - for k, v in pairs(data.glyphs) do +end + +otf.enhancers["flatten anchor tables"] = function(data,filename) + for k, v in next, data.glyphs do if v.anchors then - for kk, vv in pairs(v.anchors) do - for kkk, vvv in pairs(vv) do - if vvv.x or vvv.y then -- kkk == "centry" + for kk, vv in next, v.anchors do + for kkk, vvv in next, vv do + if vvv.x or vvv.y then vv[kkk] = { vvv.x or 0, vvv.y or 0 } else - for kkkk, vvvv in ipairs(vvv) do + for kkkk=1,#vvv do + local vvvv = vvv[kkkk] vvv[kkkk] = { vvvv.x or 0, vvvv.y or 0 } end end @@ -1570,14 +1139,24 @@ function otf.enhance.flatten(data,filename) -- to be split end end end - for _, tag in pairs({"gpos","gsub"}) do +end + +otf.enhancers["flatten feature tables"] = function(data,filename) + -- is this needed? do we still use them at all? + for _, tag in next, otf.glists do if data[tag] then - logs.report("load otf", "flattening '%s' tables", tag) - for k, v in pairs(data[tag]) do - if v.features then - for kk, vv in ipairs(v.features) do + if trace_loading then + logs.report("load otf", "flattening %s table", tag) + end + for k, v in next, data[tag] do + local features = v.features + if features then + for kk=1,#features do + local vv = features[kk] local t = { } - for kkk, vvv in ipairs(vv.scripts) do + local scripts = vv.scripts + for kkk=1,#scripts do + local vvv = scripts[kkk] t[vvv.script] = vvv.langs end vv.scripts = t @@ -1588,12 +1167,12 @@ function otf.enhance.flatten(data,filename) -- to be split end end -otf.enhance.patches = { } +otf.enhancers.patches = otf.enhancers.patches or { } -function otf.enhance.patch(data,filename) - local basename = file.basename(filename:lower()) - for pattern, action in pairs(otf.enhance.patches) do - if basename:find(pattern) then +otf.enhancers["patch bugs"] = function(data,filename) + local basename = file.basename(lower(filename)) + for pattern, action in next, otf.enhancers.patches do + if find(basename,pattern) then action(data,filename) end end @@ -1601,160 +1180,8 @@ end -- tex features -function otf.enhance.enrich(data,filename) - -- later -end - -function otf.analyze_class(data,class) - local classes = { } - local glyphs = data.glyphs - for unicode, index in pairs(data.map.map) do - local glyph = glyphs[index] - if glyph.class == class then - classes[unicode] = true - end - end - return classes -end - -function otf.analyze_subtables(data) - local subtables, name_to_type, internals, always_valid, ignore_flags, ctx_always = { }, { }, { }, { }, { }, { } - local function collect(g) - if g then - for k,v in ipairs(g) do - if v.features then - local ignored = { false, false, false } - local flags = v.flags - if flags.ignorecombiningmarks then ignored[1] = 'mark' end - if flags.ignorebasechars then ignored[2] = 'base' end - if flags.ignoreligatures then ignored[3] = 'ligature' end - if v.subtables then - local type = v.type - for _, feature in ipairs(v.features) do - local ft = feature.tag:lower() - subtables[ft] = subtables[ft] or { } - ctx_always[ft] = v.always - for script, languages in pairs(feature.scripts) do - script = script:lower() - script = script:strip() - sft = subtables[ft] - local sfts = sft[script] - if not sfts then - sfts = { } - sft[script] = sfts - end - for _, language in ipairs(languages) do - language = language:lower() - language = language:strip() - local sftsl = sfts[language] - if not sftsl then - sftsl = sfts[language] or { } - sfts[language] = sftsl - end - local lookups, valid = sftsl.lookups or { }, sftsl.valid or { } - for n, subtable in ipairs(v.subtables) do - local stl = subtable.name - if stl then - lookups[#lookups+1] = stl - valid[stl] = true - name_to_type[stl] = type - ignore_flags[stl] = ignored - end - end - sftsl.lookups, sftsl.valid = lookups, valid - end - end - end - end - else - -- we have an internal feature, say ss_l_83 that resolves to - -- subfeatures like ss_l_83_s which we find in the glyphs - name_to_type[v.name] = v.type - local lookups, valid = { }, { } - for n, subtable in ipairs(v.subtables) do - local stl = subtable.name - if stl then - lookups[#lookups+1] = stl - valid[stl] = true - always_valid[stl] = true - end - end - internals[v.name] = { - lookups = lookups, - valid = valid - } - always_valid[v.name] = true -- bonus - end - end - end - end - collect(data.gsub) - collect(data.gpos) - return subtables, name_to_type, internals, always_valid, ignore_flags, ctx_always -end - -function otf.analyze_unicodes(data) - local unicodes = { } - local indices = data.map.map - local glyphs = data.glyphs - local multiples = { } - for unicode, index in pairs(indices) do - local name = glyphs[index].name - if name then - local un = unicodes[name] - if not un then - unicodes[name] = unicode -- or 0 - elseif type(un) == "number" then - multiples[#multiples+1] = name - unicodes[name] = { un, unicode } - else - un[#un+1] = unicode - end - end - end - if #multiples > 0 then - logs.report("load otf","%s glyph are reused: %s",#multiples, concat(multiples," ")) - end - unicodes['space'] = unicodes['space'] or 32 -- handly later on - unicodes['hyphen'] = unicodes['hyphen'] or 45 -- handly later on - return unicodes -end - -function otf.analyze_features(g, features) - if g then - local t, done = { }, { } - for k=1,#g do - local f = features or g[k].features - if f then - for k=1,#f do - -- scripts and tag - local tag = f[k].tag - if not done[tag] then - t[#t+1] = tag - done[tag] = true - end - end - end - end - if #t > 0 then - return t - end - end - return nil -end - -function otf.valid_subtable(otfdata,kind,script,language) - local tk = otfdata.luatex.subtables[kind] - if tk then - local tks = tk[script] or tk.dflt - if tks then - local tksl = tks[language] or tks.dflt - if tksl then - return tksl.lookups - end - end - end - return false +fonts.otf.enhancers["enrich with features"] = function(data,filename) + -- later, ctx only end function otf.features.register(name,default) @@ -1762,75 +1189,70 @@ function otf.features.register(name,default) otf.features.default[name] = default end -function otf.set_features(tfmdata) -- node and base, simple mapping - local shared = tfmdata.shared - local otfdata = shared.otfdata - shared.features = fonts.define.check(shared.features,otf.features.default) - local features = shared.features - local trace = otf.trace_features or otf.trace_set_features - if not tfmdata.language then tfmdata.language = 'dflt' end - if not tfmdata.script then tfmdata.script = 'dflt' end +function otf.set_features(tfmdata,features) + local processes = { } if not table.is_empty(features) then - local gposlist = otfdata.luatex.gposfeatures - local gsublist = otfdata.luatex.gsubfeatures - local mode = tfmdata.mode or fonts.mode + local lists = { + fonts.triggers, + fonts.processors, + fonts.manipulators, + } + local mode = tfmdata.mode or fonts.mode -- or features.mode local initializers = fonts.initializers local fi = initializers[mode] - if fi then -- todo: delay initilization for mode 'node' + if fi then local fiotf = fi.otf if fiotf then local done = { } - local function initialize(list) -- using tex lig and kerning + for l=1,4 do + local list = lists[l] if list then for i=1,#list do local f = list[i] local value = features[f] if value and fiotf[f] then -- brr if not done[f] then -- so, we can move some to triggers - if trace then + if trace_features then logs.report("define otf","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown', tfmdata.fullname or 'unknown') end fiotf[f](tfmdata,value) -- can set mode (no need to pass otf) mode = tfmdata.mode or fonts.mode -- keep this, mode can be set local ! - local fi = initializers[mode] - fiotf = fi.otf + local im = initializers[mode] + if im then + fiotf = initializers[mode].otf + end done[f] = true end end end end end - initialize(fonts.triggers) - initialize(gsublist) - initialize(gposlist) - initialize(fonts.manipulators) end end local fm = fonts.methods[mode] if fm then local fmotf = fm.otf - local sp = shared.processors if fmotf then - local function register(list) -- node manipulations + for l=1,4 do + local list = lists[l] if list then for i=1,#list do local f = list[i] - if features[f] and fmotf[f] then -- brr - if trace then + if fmotf[f] then -- brr + if trace_features then logs.report("define otf","installing feature handler %s for mode %s for font %s",f,mode or 'unknown', tfmdata.fullname or 'unknown') end - sp[#sp+1] = fmotf[f] + processes[#processes+1] = fmotf[f] end end end end - register(fonts.triggers) - register(gsublist) - register(gposlist) - register(fonts.manipulators) end + else + -- message end end + return processes, features end function otf.otf_to_tfm(specification) @@ -1841,38 +1263,37 @@ function otf.otf_to_tfm(specification) local features = specification.features.normal local cache_id = specification.hash local tfmdata = containers.read(tfm.cache(),cache_id) +--~ print(cache_id) if not tfmdata then local otfdata = otf.load(filename,format,sub,features and features.featurefile) if not table.is_empty(otfdata) then - otf.add_dimensions(otfdata) - if true then - otfdata._shared_ = otfdata._shared_ or { -- aggressive sharing - processes = { }, - lookuptable = { }, - featuredata = { }, - featurecache = { }, - } - end - tfmdata = otf.copy_to_tfm(otfdata) + otfdata.shared = otfdata.shared or { + featuredata = { }, + anchorhash = { }, + initialized = false, + } + tfmdata = otf.copy_to_tfm(otfdata,cache_id) if not table.is_empty(tfmdata) then tfmdata.unique = tfmdata.unique or { } tfmdata.shared = tfmdata.shared or { } -- combine local shared = tfmdata.shared shared.otfdata = otfdata - shared.features = features - shared.processors = { } + shared.features = features -- default shared.dynamics = { } shared.processes = { } - shared.lookuptable = { } - shared.featuredata = { } - shared.featurecache = { } - if otfdata._shared_ then - shared.processes = otfdata._shared_.processes - shared.lookuptable = otfdata._shared_.lookuptable - shared.featuredata = otfdata._shared_.featuredata - shared.featurecache = otfdata._shared_.featurecache - end - otf.set_features(tfmdata) + shared.set_dynamics = otf.set_dynamics -- fast access and makes other modules independent + -- this will be done later anyway, but it's convenient to have + -- them already for fast access + tfmdata.luatex = otfdata.luatex + tfmdata.indices = otfdata.luatex.indices + tfmdata.unicodes = otfdata.luatex.unicodes + tfmdata.marks = otfdata.luatex.marks + tfmdata.originals = otfdata.luatex.originals + tfmdata.changed = { } + tfmdata.has_italic = otfdata.metadata.has_italic + if not tfmdata.language then tfmdata.language = 'dflt' end + if not tfmdata.script then tfmdata.script = 'dflt' end + shared.processes, shared.features = otf.set_features(tfmdata,fonts.define.check(features,otf.features.default)) end end containers.write(tfm.cache(),cache_id,tfmdata) @@ -1880,108 +1301,106 @@ function otf.otf_to_tfm(specification) return tfmdata end -function otf.features.prepare_base_kerns(tfmdata,kind,value) -- todo what kind of kerns, currently all - if value then - local otfdata = tfmdata.shared.otfdata - local glyphs = otfdata.glyphs - local unicodes = otfdata.luatex.unicodes -- names to unicodes - local somevalid = otf.some_valid_feature(otfdata,kind,tfmdata.script,tfmdata.language) - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - for u, chr in pairs(characters) do - -- hm, maybe just use descriptions, and why still index? font is already in - -- unicode with private slots, so: d = glyphs[u] should work ok - local d = glyphs[descriptions[u].index] - if d then - local dk = d.mykerns - if dk then - local t, done = chr.kerns or { }, false - for lookup,kerns in pairs(dk) do - if somevalid[lookup] then - for k, v in pairs(kerns) do - if v ~= 0 then - t[k], done = v, true +--~ { +--~ ['boundingbox']={ 95, -458, 733, 1449 }, +--~ ['class']="base", +--~ ['name']="braceleft", +--~ ['unicode']=123, +--~ ['vert_variants']={ +--~ ['italic_correction']=0, +--~ ['parts']={ +--~ { ['component']="uni23A9", ['endConnectorLength']=1000, ['fullAdvance']=2546, ['is_extender']=0, ['startConnectorLength']=0, }, -- bot +--~ { ['component']="uni23AA", ['endConnectorLength']=2500, ['fullAdvance']=2501, ['is_extender']=1, ['startConnectorLength']=2500, }, -- rep +--~ { ['component']="uni23A8", ['endConnectorLength']=1000, ['fullAdvance']=4688, ['is_extender']=0, ['startConnectorLength']=1000, }, -- mid +--~ { ['component']="uni23AA", ['endConnectorLength']=2500, ['fullAdvance']=2501, ['is_extender']=1, ['startConnectorLength']=2500, }, -- rep +--~ { ['component']="uni23A7", ['endConnectorLength']=0, ['fullAdvance']=2546, ['is_extender']=0, ['startConnectorLength']=1000, }, -- top +--~ }, +--~ ['variants']="braceleft braceleft.vsize1 braceleft.vsize2 braceleft.vsize3 braceleft.vsize4 braceleft.vsize5 braceleft.vsize6 braceleft.vsize7", +--~ }, +--~ ['width']=793, +--~ }, + +-- the first version made a top/mid/not extensible table, now we just pass on the variants data +-- and deal with it in the tfm scaler (there is no longer an extensible table anyway) + +-- we cannot share descriptions as virtual fonts might extend them (ok, we could +-- use a cache with a hash + +function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder the tma to unicode (nasty due to one->many) + if data then + local glyphs, pfminfo, metadata = data.glyphs or { }, data.pfminfo or { }, data.metadata or { } + local luatex = data.luatex + local unicodes = luatex.unicodes -- names to unicodes + local indices = luatex.indices + local characters, parameters, math_parameters, descriptions = { }, { }, { }, { } + local tfm = { + characters = characters, + parameters = parameters, + math_parameters = math_parameters, + descriptions = descriptions, + indices = indices, + unicodes = unicodes, + } + -- indices maps from unicodes to indices + for u, i in next, indices do + characters[u] = { } -- we need this because for instance we add protruding info + descriptions[u] = glyphs[i] + end + -- math + if metadata.math then + -- parameters + for name, value in next, metadata.math do + math_parameters[name] = value + end + -- we could use a subset + for u, char in next, characters do + local d = descriptions[u] + local m = d.math + -- we have them shared because that packs nicer + -- we could prepare the variants and keep 'm in descriptions + if m then + local variants = m.horiz_variants + if variants then + local c = char + for n in variants:gmatch("[^ ]+") do + local un = unicodes[n] + if un and u ~= un then + c.next = un + c = characters[un] + end + end + c.horiz_variants = m.horiz_parts + else + local variants = m.vert_variants + if variants then + local c = char + for n in variants:gmatch("[^ ]+") do + local un = unicodes[n] + if un and u ~= un then + c.next = un + c = characters[un] end end + c.vert_variants = m.vert_parts end end - if done then - chr.kerns = t -- no empty assignments - end - else - dk = d.kerns - if dk then - local t, done = chr.kerns or { }, false - for _, v in pairs(dk) do - if somevalid[v.lookup] then - local k = unicodes[v.char] - local o = v.off - if type(k) == "number" then - if k > 0 then - t[k], done = o, true - end - else - for i=1,#k do - local ki = k[i] - if ki > 0 then - t[ki], done = o, true - end - end - end - end - end - if done then - chr.kerns = t -- no empty assignments - end + local kerns = m.kerns + if kerns then + char.mathkerns = kerns end end end end - end -end - -function otf.add_dimensions(data) - if data then - local force = otf.notdef - for k, d in pairs(data.glyphs) do - local bb, wd = d.boundingbox, d.width or 0 - if force and not d.name then - d.name = ".notdef" - end - if wd ~= 0 and d.class == "mark" then - d.width = -wd - end - if bb then - local ht, dp = bb[4], -bb[2] - if ht ~= 0 then d.height = ht end - if dp ~= 0 then d.depth = dp end - end - d.index = k - end - end -end - -function otf.copy_to_tfm(data) -- we can save a copy when we reorder the tma to unicode - if data then - local characters, parameters, descriptions = { }, { }, { } - local tfm = { characters = characters, parameters = parameters, descriptions = descriptions } - local luatex = data.luatex - local indices = luatex.indices -- unicodes to indices - local glyphs = data.glyphs - for u, i in pairs(indices) do - local d = glyphs[i] - characters[u] = { } -- not needed - descriptions[u] = d - end - local designsize = data.designsize or data.design_size or 100 + -- end math + local designsize = metadata.designsize or metadata.design_size or 100 if designsize == 0 then designsize = 100 end local spaceunits = 500 - tfm.units = data.units_per_em or 1000 + tfm.units = metadata.units_per_em or 1000 -- we need a runtime lookup because of running from cdrom or zip, brrr - tfm.filename = input.findbinfile(data.luatex.filename,"") or data.luatex.filename - tfm.fullname = data.fontname or data.fullname + tfm.filename = resolvers.findbinfile(luatex.filename,"") or luatex.filename + tfm.fullname = metadata.fontname or metadata.fullname tfm.encodingbytes = 2 tfm.cidinfo = data.cidinfo tfm.cidinfo.registry = tfm.cidinfo.registry or "" @@ -1993,21 +1412,16 @@ function otf.copy_to_tfm(data) -- we can save a copy when we reorder the tma to tfm.boundarychar = 65536 tfm.designsize = (designsize/10)*65536 tfm.spacer = "500 units" - data.isfixedpitch = data.pfminfo and data.pfminfo.panose and data.pfminfo.panose["proportion"] == "Monospaced" - data.charwidth = nil - if data.pfminfo then - data.charwidth = data.pfminfo.avgwidth - end local endash, emdash = 0x20, 0x2014 -- unicodes['space'], unicodes['emdash'] - if data.isfixedpitch then + if metadata.isfixedpitch then if descriptions[endash] then spaceunits, tfm.spacer = descriptions[endash].width, "space" end if not spaceunits and descriptions[emdash] then spaceunits, tfm.spacer = descriptions[emdash].width, "emdash" end - if not spaceunits and data.charwidth then - spaceunits, tfm.spacer = data.charwidth, "charwidth" + if not spaceunits and metadata.charwidth then + spaceunits, tfm.spacer = metadata.charwidth, "charwidth" end else if descriptions[endash] then @@ -2016,36 +1430,37 @@ function otf.copy_to_tfm(data) -- we can save a copy when we reorder the tma to if not spaceunits and descriptions[emdash] then spaceunits, tfm.spacer = descriptions[emdash].width/2, "emdash/2" end - if not spaceunits and data.charwidth then - spaceunits, tfm.spacer = data.charwidth, "charwidth" + if not spaceunits and metadata.charwidth then + spaceunits, tfm.spacer = metadata.charwidth, "charwidth" end end spaceunits = tonumber(spaceunits) or tfm.units/2 -- 500 -- brrr parameters.slant = 0 - parameters.space = spaceunits - parameters.space_stretch = tfm.units/2 -- 500 - parameters.space_shrink = 2*tfm.units/3 -- 333 - parameters.x_height = 4*tfm.units/5 -- 400 + parameters.space = spaceunits -- 3.333 (cmr10) + parameters.space_stretch = tfm.units/2 -- 500 -- 1.666 (cmr10) + parameters.space_shrink = 1*tfm.units/3 -- 333 -- 1.111 (cmr10) + parameters.x_height = 2*tfm.units/5 -- 400 parameters.quad = tfm.units -- 1000 - parameters.extra_space = 0 if spaceunits < 2*tfm.units/5 then -- todo: warning end - tfm.italicangle = data.italicangle - tfm.ascender = math.abs(data.ascent or 0) - tfm.descender = math.abs(data.descent or 0) - if data.italicangle then -- maybe also in afm _ - parameters.slant = parameters.slant - math.round(math.tan(data.italicangle*math.pi/180)) + local italicangle = metadata.italicangle + tfm.ascender = math.abs(metadata.ascent or 0) + tfm.descender = math.abs(metadata.descent or 0) + if italicangle then -- maybe also in afm _ + tfm.italicangle = italicangle + parameters.slant = parameters.slant - math.round(math.tan(italicangle*math.pi/180)) end - if data.isfixedpitch then + if metadata.isfixedpitch then parameters.space_stretch = 0 parameters.space_shrink = 0 elseif otf.syncspace then -- parameters.space_stretch = spaceunits/2 parameters.space_shrink = spaceunits/3 end - if data.pfminfo and data.pfminfo.os2_xheight and data.pfminfo.os2_xheight > 0 then - parameters.x_height = data.pfminfo.os2_xheight + parameters.extra_space = parameters.space_shrink -- 1.111 (cmr10) + if pfminfo.os2_xheight and pfminfo.os2_xheight > 0 then + parameters.x_height = pfminfo.os2_xheight else local x = 0x78 -- unicodes['x'] if x then @@ -2062,23 +1477,55 @@ function otf.copy_to_tfm(data) -- we can save a copy when we reorder the tma to end end +otf.features.register('mathsize') + function tfm.read_from_open_type(specification) local tfmtable = otf.otf_to_tfm(specification) if tfmtable then + local otfdata = tfmtable.shared.otfdata tfmtable.name = specification.name tfmtable.sub = specification.sub - tfmtable = tfm.scale(tfmtable, specification.size) + local s = specification.size + local m = otfdata.metadata.math + if m then + local f = specification.features + if f then + local f = f.normal + if f and f.mathsize then + local mathsize = specification.mathsize or 0 + if mathsize == 2 then + local p = m.ScriptPercentScaleDown + if p then + local ps = p * specification.textsize / 100 + if trace_math then + logs.report("define font","asked script size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) + end + s = ps + end + elseif mathsize == 3 then + local p = m.ScriptScriptPercentScaleDown + if p then + local ps = p * specification.textsize / 100 + if trace_math then + logs.report("define font","asked scriptscript size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) + end + s = ps + end + end + end + end + end + tfmtable = tfm.scale(tfmtable,s) -- here we resolve the name; file can be relocated, so this info is not in the cache - local otfdata = tfmtable.shared.otfdata local filename = (otfdata and otfdata.luatex and otfdata.luatex.filename) or specification.filename if not filename then -- try to locate anyway and set otfdata.luatex.filename end if filename then tfmtable.encodingbytes = 2 - tfmtable.filename = input.findbinfile(filename,"") or filename - tfmtable.fullname = otfdata.fontname or otfdata.fullname - local order = otfdata and otfdata.order2 + tfmtable.filename = resolvers.findbinfile(filename,"") or filename + tfmtable.fullname = otfdata.metadata.fontname or otfdata.metadata.fullname + local order = otfdata and otfdata.metadata.order2 if order == 0 then tfmtable.format = 'opentype' elseif order == 1 then @@ -2092,3295 +1539,3 @@ function tfm.read_from_open_type(specification) end return tfmtable end - -function otf.analyze_only(otfdata) - local analyze = otf.analyze_features - return analyze(otfdata.gpos), analyze(otfdata.gsub) -end - -local a_to_script = { } -local a_to_language = { } - -do - - local context_setups = fonts.define.specify.context_setups - local context_numbers = fonts.define.specify.context_numbers - - function otf.set_dynamics(tfmdata,attribute,features) --currently experimental and slow / hackery - local shared = tfmdata.shared - if shared then - local dynamics = shared.dynamics - if dynamics then - features = features or context_setups[context_numbers[attribute]] - if features then - local script = features.script or 'dflt' - local language = features.language or 'dflt' - local ds = dynamics[script] - if not ds then - ds = { } - dynamics[script] = ds - end - local dsl = ds[language] - if not dsl then - dsl = { } - ds[language] = dsl - end - local dsla = dsl[attribute] - if dsla then - return dsla - else - a_to_script [attribute] = script - a_to_language[attribute] = language - dsla = { } - local otfdata = shared.otfdata - local methods = fonts.methods.node.otf - local initializers = fonts.initializers.node.otf - local gposfeatures, gsubfeatures = otf.analyze_only(otfdata,features) - local default = otf.features.default - local function register(list) - if list then - for i=1,#list do - local f = list[i] - local value = features[f] or default[f] - if value then - local i, m = initializers[f], methods[f] - if i then - i(tfmdata,value) - end - if m then - dsla[#dsla+1] = m - end - end - end - end - end - register(fonts.triggers) - register(gsubfeatures) - register(gposfeatures) - dynamics[script][language][attribute] = dsla - return dsla - end - end - end - end - return { } -- todo: false - end - -end - --- scripts - -otf.default_language = 'latn' -otf.default_script = 'dflt' - -function otf.valid_feature(otfdata,kind,script,language) -- return hash is faster - local luatex = otfdata.luatex - if luatex.ctx_always[kind] then - script, language = 'dflt', 'dflt' - else - script = script or otf.default_script - language = language or otf.default_language - end - script, language = script:lower(), language:lower() -- will go away, we will lowercase values - local ft = luatex.subtables[kind] - local st = ft[script] or ft.dflt - local lt = st and (st[language] or st.dflt) - return false, luatex.always_valid, lt.valid -end - -function otf.some_valid_feature(otfdata,kind,script,language) - local luatex = otfdata.luatex - if luatex.ctx_always[kind] then - script, language = 'dflt', 'dflt' - else - script = script or otf.default_script - language = language or otf.default_language - script, language = script:lower(), language:lower() -- will go away, we will lowercase values - end - local t = luatex.subtables[kind] - if t then - local ts = t[script] or t.dflt - if ts then - local tsl = ts[language] or ts.dflt - return (tsl and tsl.valid) or { } - end - end - return { } -end - -function otf.features.aux.resolve_ligatures(tfmdata,ligatures,kind) - local otfdata = tfmdata.shared.otfdata - local unicodes = otfdata.luatex.unicodes - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local changed = tfmdata.changed or { } - local done = { } - kind = kind or "unknown" - local trace = otf.trace_features - while true do - local ok = false - for k,v in pairs(ligatures) do - local lig = v[1] - if not done[lig] then - local ligs = split_at_space:match(lig) - if #ligs == 2 then - local uc = v[2] - local c, f, s = characters[uc], ligs[1], ligs[2] ---~ local uf, us = unicodes[f], unicodes[s] - -local uft, ust = unicodes[f], unicodes[s] -if not uft or not ust then - logs.report("define otf","%s: unicode problem with ligature (%s->%s=%s->%s+%s->%s)",kind,descriptions[uc].name or "?",uc,f,uft or "?",s,ust or "?") - -- some kind of error -else - if type(uft) == "number" then uft = { uft } end - if type(ust) == "number" then ust = { ust } end - for ufi=1,#uft do - local uf = uft[ufi] - for usi=1,#ust do - local us = ust[usi] - - if changed[uf] or changed[us] then - if trace then - logs.report("define otf","%s: %s (%s) + %s (%s) ignored",kind,f,uf,s,us) - end - else - local first, second = characters[uf], us - if first and second then - local t = first.ligatures - if not t then - t = { } - first.ligatures = t - end - local uuc = unicodes[descriptions[uc].name] - if type(uuc) == "number" then - t[second] = { type = 0, char = uuc } - else - t[second] = { type = 0, char = uuc[1] } - end - if trace then - logs.report("define otf","%s: %s (%s) + %s (%s) = %s (%s)",kind,f,uf,s,us,descriptions[uc].name,unicodes[descriptions[uc].name]) - end - end - end - - end - end -end - - ok, done[lig] = true, descriptions[uc].name - end - end - end - if ok then - for d,n in pairs(done) do - local pattern = "^(" .. d .. ") " - for k,v in pairs(ligatures) do - v[1] = v[1]:gsub(pattern, function(str) - return n .. " " - end) - end - end - else - break - end - end -end - -function otf.features.prepare_base_substitutions(tfmdata,kind,value) -- we can share some code with the node features - if value then - local ligatures = { } - local otfdata = tfmdata.shared.otfdata - local unicodes = otfdata.luatex.unicodes - local trace = otf.trace_features - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local somevalid = otf.some_valid_feature(otfdata,kind,tfmdata.script,tfmdata.language) - if not table.is_empty(somevalid) then - tfmdata.changed = tfmdata.changed or { } - local changed = tfmdata.changed - local glyphs = otfdata.glyphs - for k,c in pairs(characters) do - local o = glyphs[descriptions[k].index] - if o and o.lookups then - for lookup,ps in pairs(o.lookups) do - if somevalid[lookup] then - for i=1,#ps do - local p = ps[i] - local t = p[1] - if t == 'substitution' then - local pv = p[2] -- p.variant - if pv then - local upv = unicodes[pv] - if upv then - if type(upv) == "number" then - if characters[upv] then - if trace then - logs.report("define otf","%s: %s (%s) => %s (%s)",kind,descriptions[k].name,k,descriptions[upv].name,upv) - end - characters[k] = characters[upv] - descriptions[k] = descriptions[upv] - changed[k] = true - end - else - for i=1,#upv do - local upv = upv[i] - if characters[upv] then - if trace then - logs.report("define otf","%s: %s (%s) => %s (%s)",kind,descriptions[k].name,k,descriptions[upv].name,upv) - end - characters[k] = characters[upv] - descriptions[k] = descriptions[upv] - changed[k] = true - end - end - end - end - end - elseif t == 'alternate' then - local pc = p[2] -- p.components - if pc then - pc = pa.components:match("([^ ]+)") -- todo: selector - if pc then - local upc = unicodes[pc] - if upc then - if type(upc) == "number" then - if chars[upc] then - if trace then - logs.report("define otf","%s: %s (%s) => %s (%s)",kind,descriptions[k].name,k,descriptions[upc].name,upc) - end - characters[k] = characters[upc] - descriptions[k] = descriptions[upc] - changed[k] = true - end - else - for i=1,#upc do - local upc = upc[i] - if chars[upc] then - if trace then - logs.report("define otf","%s: %s (%s) => %s (%s)",kind,descriptions[k].name,k,descriptions[upc].name,upc) - end - characters[k] = characters[upc] - descriptions[k] = descriptions[upc] - changed[k] = true - end - end - end - end - end - end - elseif t == 'ligature' and not changed[k] then - local pc = p[2] - if pc then - if trace then - logs.report("define otf","%s: %s => %s (%s)",kind,pc,descriptions[k].name,k) - end - ligatures[#ligatures+1] = { pc, k } - end - end - end - end - end - end - end - otf.features.aux.resolve_ligatures(tfmdata,ligatures,kind) - end - else - tfmdata.ligatures = tfmdata.ligatures or { } - end -end - -function fonts.initializers.base.otf.liga(tfm,value) otf.features.prepare_base_substitutions(tfm,'liga',value) end -function fonts.initializers.base.otf.dlig(tfm,value) otf.features.prepare_base_substitutions(tfm,'dlig',value) end -function fonts.initializers.base.otf.rlig(tfm,value) otf.features.prepare_base_substitutions(tfm,'rlig',value) end -function fonts.initializers.base.otf.hlig(tfm,value) otf.features.prepare_base_substitutions(tfm,'hlig',value) end -function fonts.initializers.base.otf.pnum(tfm,value) otf.features.prepare_base_substitutions(tfm,'pnum',value) end -function fonts.initializers.base.otf.onum(tfm,value) otf.features.prepare_base_substitutions(tfm,'onum',value) end -function fonts.initializers.base.otf.tnum(tfm,value) otf.features.prepare_base_substitutions(tfm,'tnum',value) end -function fonts.initializers.base.otf.lnum(tfm,value) otf.features.prepare_base_substitutions(tfm,'lnum',value) end -function fonts.initializers.base.otf.zero(tfm,value) otf.features.prepare_base_substitutions(tfm,'zero',value) end -function fonts.initializers.base.otf.smcp(tfm,value) otf.features.prepare_base_substitutions(tfm,'smcp',value) end -function fonts.initializers.base.otf.cpsp(tfm,value) otf.features.prepare_base_substitutions(tfm,'cpsp',value) end -function fonts.initializers.base.otf.c2sc(tfm,value) otf.features.prepare_base_substitutions(tfm,'c2sc',value) end -function fonts.initializers.base.otf.ornm(tfm,value) otf.features.prepare_base_substitutions(tfm,'ornm',value) end -function fonts.initializers.base.otf.aalt(tfm,value) otf.features.prepare_base_substitutions(tfm,'aalt',value) end - -function fonts.initializers.base.otf.hwid(tfm,value) otf.features.prepare_base_substitutions(tfm,'hwid',value) end -function fonts.initializers.base.otf.fwid(tfm,value) otf.features.prepare_base_substitutions(tfm,'fwid',value) end - --- Here comes the real thing ... node processing! The next session prepares --- things. The main features (unchained by rules) have their own caches, --- while the private ones cache locally. - -do - - otf.features.prepare = { } - - local falsetable = { false, false, false } - - function otf.features.prepare.feature(tfmdata,kind,value) - if value then - local language, script = tfmdata.language or "dflt", tfmdata.script or "dflt" - local shared = tfmdata.shared - local otfdata = shared.otfdata - local lookuptable = otf.valid_subtable(otfdata,kind,script,language) - if lookuptable then - local fullkind = kind .. script .. language - if not shared.lookuptable [fullkind] then - --~ print(tfmdata,file.basename(tfmdata.fullname or ""),kind,script,language,lookuptable,fullkind) - local processes = { } - -- featuredata and featurecache are indexed by lookup so we can share them - shared.featuredata [kind] = shared.featuredata [kind] or { } - shared.featurecache[kind] = shared.featurecache[kind] or false -- signal - shared.lookuptable [fullkind] = lookuptable - shared.processes [fullkind] = processes - local luatex = otfdata.luatex - local types = luatex.name_to_type - local flags = luatex.ignore_flags - local preparers = otf.features.prepare - local process = otf.features.process - for i=1,#lookuptable do - local lookupname = lookuptable[i] - local lookuptype = types[lookupname] - local prepare = preparers[lookuptype] - if prepare then - local processdata = prepare(tfmdata,kind,lookupname) - if processdata then - local processflags = flags[lookupname] or falsetable --- share false table - -- local chain = (lookuptype == "gsub_contextchain") or (lookuptype == "gpos_contextchain") - local chain = lookuptype:find("context") ~= nil - processes[#processes+1] = { process[lookuptype], lookupname, processdata, processflags, chain } - end - end - end - end - end - end - end - - -- helper: todo, we don't need to store non local ones for chains so we can pass the - -- validator as parameter - - function otf.features.collect_ligatures(tfmdata,kind) -- ligs are spread all over the place - local otfdata = tfmdata.shared.otfdata - local glyphs = otfdata.glyphs - local luatex = otfdata.luatex - local unicodes = luatex.unicodes -- names to unicode - local indices = luatex.indices -- unicode to index - local trace = otf.trace_features - local ligatures = { } - local function collect(lookup,unicode,glyph,ps) - for i=1,#ps do - local p = ps[i] - if p[1] == 'ligature' then - if trace then - logs.report("define otf","feature %s lookup %s ligature %s => %s",kind,lookup,p[2],glyph.name) - end - local t = ligatures[lookup] - if not t then - t = { } - ligatures[lookup] = t - end - -- this table is kind of special: - -- unicode -> tree of names/indices -> unicode - -- this way we can handle multiple unicode to one glyph cases - local first = true - for s in p[2]:gmatch("[^ ]+") do - if first then - local u = unicodes[s] - if not u then - logs.report("define otf","feature %s lookup %s ligature %s => %s ignored due to invalid unicode",kind,lookup,p[2],glyph.name) - elseif type(u) == "number" then - if not t[u] then - t[u] = { { } } - end - t = t[u] - else - local tt = t - local tu - for i=1,#u do - local u = u[i] - if i==1 then - if not t[u] then - t[u] = { { } } - end - tu = t[u] - t = tu - else - if not t[u] then - tt[u] = tu - end - end - end - end - first = false - else - -- beware, we mix unicodes and indices, we can comment these - -- lines when testing, see (*lig*) - s = unicodes[s] - if type(s) == "number" then - s = indices[s] - else - s = indices[s[1]] - end - -- maybe we will introduce a names table some day - local t1 = t[1] - if not t1[s] then - t1[s] = { { } } - end - t = t1[s] - end - end - t[2] = unicode - end - end - end - local forced, always, okay = otf.valid_feature(otfdata,kind,tfmdata.script,tfmdata.language) - for unicode, index in pairs(indices) do - local glyph = glyphs[index] - local lookups = glyph.lookups - if lookups then - if forced then - for lookup, ps in pairs(lookups) do collect(lookup,unicode,glyph,ps) end - elseif okay then - for lookup, ps in pairs(lookups) do if always[lookup] or okay[lookup] then collect(lookup,unicode,glyph,ps) end end - else - for lookup, ps in pairs(lookups) do if always[lookup] then collect(lookup,unicode,glyph,ps) end end - end - end - end - return ligatures - end - - -- gsub_single -> done - -- gsub_multiple -> done - -- gsub_alternate -> done - -- gsub_ligature -> done - -- gsub_context -> todo - -- gsub_contextchain -> done - -- gsub_reversecontextchain -> todo - - -- we used to share code in the following functions but that was relatively - -- due to extensive calls to functions (easily hundreds of thousands per - -- document) - - function otf.features.prepare.gsub_single(tfmdata,kind,lookupname) - local featuredata = tfmdata.shared.featuredata[kind] - local substitutions = featuredata[lookupname] - if not substitutions then - substitutions = { } - featuredata[lookupname] = substitutions - local otfdata = tfmdata.shared.otfdata - local glyphs = otfdata.glyphs - local luatex = otfdata.luatex - local unicodes = luatex.unicodes -- names to unicode - local indices = luatex.indices -- unicode to index - local trace = otf.trace_features - for unicode, index in pairs(indices) do - local glyph = glyphs[index] - local lookups = glyph.lookups - if lookups then - for lookup,ps in pairs(lookups) do - if lookup == lookupname then - for i=1,#ps do - local p = ps[i] - if p[1] == 'substitution' then - local old, new = unicode, unicodes[p[2]] - if type(new) == "table" then - new = new[1] - end - substitutions[old] = new - if trace then - logs.report("define otf","%s:%s substitution %s => %s",kind,lookupname,old,new) - end - end - end - end - end - end - end - end - return substitutions - end - - function otf.features.prepare.gsub_multiple(tfmdata,kind,lookupname) - local featuredata = tfmdata.shared.featuredata[kind] - local substitutions = featuredata[lookupname] - if not substitutions then - substitutions = { } - featuredata[lookupname] = substitutions - local otfdata = tfmdata.shared.otfdata - local glyphs = otfdata.glyphs - local luatex = otfdata.luatex - local unicodes = luatex.unicodes -- names to unicode - local indices = luatex.indices -- unicode to index - local trace = otf.trace_features - for unicode, index in pairs(indices) do - local glyph = glyphs[index] - local lookups = glyph.lookups - if lookups then - for lookup,ps in pairs(lookups) do - if lookup == lookupname then - for i=1,#ps do - local p = ps[i] - if p[1] == 'multiple' then - local old, new = unicode, { } - substitutions[old] = new - for pc in p[2]:gmatch("[^ ]+") do - local upc = unicodes[pc] - if type(upc) == "number" then - new[#new+1] = upc - else - new[#new+1] = upc[1] - end - end - if trace then - logs.report("define otf","%s:%s multiple %s => %s",kind,lookupname,old,concat(new," ")) - end - end - end - end - end - end - end - end - return substitutions - end - - function otf.features.prepare.gsub_alternate(tfmdata,kind,lookupname) - -- todo: configurable preference list - local featuredata = tfmdata.shared.featuredata[kind] - local substitutions = featuredata[lookupname] - if not substitutions then - featuredata[lookupname] = { } - substitutions = featuredata[lookupname] - local otfdata = tfmdata.shared.otfdata - local glyphs = otfdata.glyphs - local luatex = otfdata.luatex - local unicodes = luatex.unicodes -- names to unicode - local indices = luatex.indices -- unicode to index - local trace = otf.trace_features - for unicode, index in pairs(indices) do - local glyph = glyphs[index] - local lookups = glyph.lookups - if lookups then - for lookup,ps in pairs(lookups) do - if lookup == lookupname then - for i=1,#ps do - local p = ps[i] - if p[1] == 'alternate' then - local old = unicode - local t = { } - for pc in p[2]:gmatch("[^ ]+") do - local upc = unicodes[pc] - if type(upc) == "number" then - t[#t+1] = upc - else - t[#t+1] = upc[1] - end - end - substitutions[old] = t - if trace then - logs.report("define otf","%s:%s alternate %s => %s",kind,lookupname,old,concat(substitutions,"|")) - end - end - end - end - end - end - end - end - return substitutions - end - - function otf.features.prepare.gsub_ligature(tfmdata,kind,lookupname) - -- we collect them for all lookups, this saves loops, we only use the - -- lookupname for testing, we need to check if this leads to redundant - -- collections - local ligatures = tfmdata.shared.featuredata[kind] - if not ligatures[lookupname] then - ligatures = otf.features.collect_ligatures(tfmdata,kind) - tfmdata.shared.featuredata[kind] = ligatures - end - return ligatures[lookupname] - end - - function otf.features.prepare.contextchain(tfmdata,kind,lookupname) - local featuredata = tfmdata.shared.featuredata[kind] - local contexts = featuredata[lookupname] - if not contexts then - contexts = { } - featuredata[lookupname] = contexts - local characters = tfmdata.characters - local otfdata = tfmdata.shared.otfdata - local luatex = otfdata.luatex - local unicodes = luatex.unicodes - local internals = luatex.internals - local flags = luatex.ignore_flags - local types = luatex.name_to_type - local cache = luatex.covers - if not cache then - cache = { } - luatex.covers = cache - end - local function uncover(covers,result) - -- lpeg hardly faster (.005 sec on mk) - for n=1,#covers do - local c = covers[n] - local cc = cache[c] - if not cc then - local t = { } - for s in c:gmatch("[^ ]+") do - local us = unicodes[s] - if type(us) == "number" then - t[us] = true - else - for i=1,#us do - t[us[i]] = true - end - end - end - cache[c] = t - result[#result+1] = t - else - result[#result+1] = cc - end - end - end - local lookupdata = otfdata.lookups[lookupname] - if not lookupdata then - logs.report("otf process","missing lookupdata table %s",lookupname) - elseif lookupdata.rules then - local rules = lookupdata.rules - local center_match = otf.center_match - for nofrules=1,#rules do - local rule = rules[nofrules] - local coverage = rule.coverage - if coverage and coverage.current then - local current, before, after, sequence = coverage.current, coverage.before, coverage.after, { } - if before then - uncover(before,sequence) - end - local start = #sequence + 1 - uncover(current,sequence) - local stop = #sequence - if after then - uncover(after,sequence) - end - if sequence[1] then - local lookups, lookuptype = rule.lookups, 'self' - -- for the moment only lookup index 1 - if lookups then - if #lookups > 1 then - logs.report("otf process","WARNING: more than one lookup in rule") - end - lookuptype = types[lookups[1]] - end - -- this may be wrong; we cannot copy inside the for loop (out of memory with hz); - -- so we may end up with a different usage of sequence in the chainproc handlers - sequence = table.copy(sequence) - -- we trigger on the first character in current - for unic, _ in pairs(sequence[start]) do - local t = contexts[unic] - if not t then - contexts[unic] = { lookups={}, flags=flags[lookupname] } - t = contexts[unic].lookups - end - t[#t+1] = { nofrules, lookuptype, sequence, start, stop, lookups } - end - end - end - end - end - end - return contexts - end - - otf.features.prepare.gsub_context = otf.features.prepare.contextchain - otf.features.prepare.gsub_contextchain = otf.features.prepare.contextchain - otf.features.prepare.gsub_reversecontextchain = otf.features.prepare.contextchain - - -- ruled->lookup=ks_latn_l_27_c_4 => internal[ls_l_84] => valid[ls_l_84_s] - - -- gpos_mark2base -> done - -- gpos_mark2ligature -> done - -- gpos_mark2mark -> done - -- gpos_single -> not done - -- gpos_pair -> not done - -- gpos_cursive -> not done - -- gpos_context -> not done - -- gpos_reversecontextchain -> not done - - function otf.features.prepare.anchors(tfmdata,kind,lookupname) -- tracing - local featuredata = tfmdata.shared.featuredata[kind] - local anchors = featuredata[lookupname] - if not anchors then - anchors = { } - featuredata[lookupname] = anchors - local otfdata = tfmdata.shared.otfdata - local glyphs = otfdata.glyphs - local luatex = otfdata.luatex - local unicodes = luatex.unicodes - local indices = luatex.indices - local validanchors = { } - local trace = otf.trace_features - local classes = otfdata.anchor_classes - if classes then - for k=1,#classes do - local class = classes[k] - if class.lookup == lookupname then - if trace then - logs.report("define otf","%s:%s anchor -> %s",kind,lookupname,class.name) - end - validanchors[class.name] = true - end - end - end - for unicode, index in pairs(indices) do - local glyph = glyphs[index] - local oanchor = glyph.anchors - if oanchor then - local t, ok = { }, false - for type, anchors in pairs(oanchor) do -- types - local tt = false - for name, anchor in pairs(anchors) do - if validanchors[name] then - if not tt then - tt = { [name] = anchor } - t[type] = tt - ok = true - else - tt[name] = anchor - end - end - end - end - if ok then - anchors[unicode] = t - end - end - end - end ---~ if kind == "mkmk" then print(lookupname,table.serialize(anchors)) end - return anchors - end - - otf.features.prepare.gpos_mark2base = otf.features.prepare.anchors - otf.features.prepare.gpos_mark2ligature = otf.features.prepare.anchors - otf.features.prepare.gpos_mark2mark = otf.features.prepare.anchors - otf.features.prepare.gpos_cursive = otf.features.prepare.anchors - otf.features.prepare.gpos_context = otf.features.prepare.contextchain - otf.features.prepare.gpos_contextchain = otf.features.prepare.contextchain - - function otf.features.prepare.gpos_single(tfmdata,kind,lookupname) - logs.report("otf define","gpos_single not yet supported") - end - - -- ["kerns"]={ { ["char"]="ytilde", ["lookup"]="pp_l_1_s", ["off"]=-83, ... - -- ["mykerns"] = { ["pp_l_1_s"] = { [67] = -28, ... - - function otf.features.prepare.gpos_pair(tfmdata,kind,lookupname) - local featuredata = tfmdata.shared.featuredata[kind] - local kerns = featuredata[lookupname] - if not kerns then - local trace = otf.trace_features - featuredata[lookupname] = { } - kerns = featuredata[lookupname] - local otfdata = tfmdata.shared.otfdata - local glyphs = otfdata.glyphs - local luatex = otfdata.luatex - local unicodes = luatex.unicodes - local indices = luatex.indices - -- ff has isolated kerns in a separate table - for unicode, index in pairs(indices) do - local glyph = glyphs[index] - local list = glyph.mykerns - if list then - local omk = list[lookupname] - if omk then - local krn = kerns[unicode] - for other, off in pairs(omk) do - if not krn then - krn = { } - kerns[unicode] = krn - end - krn[other] = off - if trace then - logs.report("define otf","feature %s kern pair %s - %s",kind,unicode,other) - end - end - end - else - list = glyph.kerns - if list then - local krn - for ok=1,#list do - local k = list[ok] - if k.lookup == lookupname then - local char = k.char - if char then - if not krn then - krn = kerns[unicode] - if not krn then - krn = { } - kerns[unicode] = krn - end - end - local second = unicodes[char] - local off = k.off - if type(second) == "number" then - krn[second] = off - if trace then - logs.report("define otf","feature %s kern pair %s - %s",kind,unicode,second) - end - else - for i=1,#second do - local second = second[i] - krn[second] = off - if trace then - logs.report("define otf","feature %s kern pair %s - %s",kind,unicode,second) - end - end - end - end - end - end - end - end - list = glyphs.lookups - if list then - for lookup,ps in pairs(list) do - if lookup == lookupname then - local krn - for i=1,#ps do - local p = ps[i] - if p[1] == 'pair' then - if not krn then - krn = kerns[unicode] - if not krn then - krn = { } - kerns[unicode] = krn - end - end - local second = unicodes[p[2]] - if type(second) == "number" then - krn[second] = p - if trace then - logs.report("define otf","feature %s kern pair %s - %s",kind,unicode,second) - end - else - for i=1,#second do - local second = second[i] - krn[second] = p - if trace then - logs.report("define otf","feature %s kern pair %s - %s",kind,unicode,second) - end - end - end - end - end - end - end - end - end - end - return kerns - end - - otf.features.prepare.gpos_contextchain = otf.features.prepare.contextchain - -end - --- can be generalized: one loop in main - -do - - local prepare = otf.features.prepare.feature - - function fonts.initializers.node.otf.aalt(tfm,value) return prepare(tfm,'aalt',value) end - function fonts.initializers.node.otf.abvm(tfm,value) return prepare(tfm,'abvm',value) end - function fonts.initializers.node.otf.afrc(tfm,value) return prepare(tfm,'afrc',value) end - function fonts.initializers.node.otf.akhn(tfm,value) return prepare(tfm,'akhn',value) end - function fonts.initializers.node.otf.blwm(tfm,value) return prepare(tfm,'blwm',value) end - function fonts.initializers.node.otf.c2pc(tfm,value) return prepare(tfm,'c2pc',value) end - function fonts.initializers.node.otf.c2sc(tfm,value) return prepare(tfm,'c2sc',value) end - function fonts.initializers.node.otf.calt(tfm,value) return prepare(tfm,'calt',value) end - function fonts.initializers.node.otf.case(tfm,value) return prepare(tfm,'case',value) end - function fonts.initializers.node.otf.ccmp(tfm,value) return prepare(tfm,'ccmp',value) end - function fonts.initializers.node.otf.clig(tfm,value) return prepare(tfm,'clig',value) end - function fonts.initializers.node.otf.cpsp(tfm,value) return prepare(tfm,'cpsp',value) end - function fonts.initializers.node.otf.cswh(tfm,value) return prepare(tfm,'cswh',value) end - function fonts.initializers.node.otf.curs(tfm,value) return prepare(tfm,'curs',value) end - function fonts.initializers.node.otf.dlig(tfm,value) return prepare(tfm,'dlig',value) end - function fonts.initializers.node.otf.dnom(tfm,value) return prepare(tfm,'dnom',value) end - function fonts.initializers.node.otf.expt(tfm,value) return prepare(tfm,'expt',value) end - function fonts.initializers.node.otf.fin2(tfm,value) return prepare(tfm,'fin2',value) end - function fonts.initializers.node.otf.fin3(tfm,value) return prepare(tfm,'fin3',value) end - function fonts.initializers.node.otf.fina(tfm,value) return prepare(tfm,'fina',value) end - function fonts.initializers.node.otf.frac(tfm,value) return prepare(tfm,'frac',value) end - function fonts.initializers.node.otf.fwid(tfm,value) return prepare(tfm,'fwid',value) end - function fonts.initializers.node.otf.haln(tfm,value) return prepare(tfm,'haln',value) end - function fonts.initializers.node.otf.hist(tfm,value) return prepare(tfm,'hist',value) end - function fonts.initializers.node.otf.hkna(tfm,value) return prepare(tfm,'hkna',value) end - function fonts.initializers.node.otf.hlig(tfm,value) return prepare(tfm,'hlig',value) end - function fonts.initializers.node.otf.hngl(tfm,value) return prepare(tfm,'hngl',value) end - function fonts.initializers.node.otf.hwid(tfm,value) return prepare(tfm,'hwid',value) end - function fonts.initializers.node.otf.init(tfm,value) return prepare(tfm,'init',value) end - function fonts.initializers.node.otf.isol(tfm,value) return prepare(tfm,'isol',value) end - function fonts.initializers.node.otf.ital(tfm,value) return prepare(tfm,'ital',value) end - function fonts.initializers.node.otf.jp78(tfm,value) return prepare(tfm,'jp78',value) end - function fonts.initializers.node.otf.jp83(tfm,value) return prepare(tfm,'jp83',value) end - function fonts.initializers.node.otf.jp90(tfm,value) return prepare(tfm,'jp90',value) end - function fonts.initializers.node.otf.kern(tfm,value) return prepare(tfm,'kern',value) end - function fonts.initializers.node.otf.liga(tfm,value) return prepare(tfm,'liga',value) end - function fonts.initializers.node.otf.lnum(tfm,value) return prepare(tfm,'lnum',value) end - function fonts.initializers.node.otf.locl(tfm,value) return prepare(tfm,'locl',value) end - function fonts.initializers.node.otf.mark(tfm,value) return prepare(tfm,'mark',value) end - function fonts.initializers.node.otf.med2(tfm,value) return prepare(tfm,'med2',value) end - function fonts.initializers.node.otf.medi(tfm,value) return prepare(tfm,'medi',value) end - function fonts.initializers.node.otf.mgrk(tfm,value) return prepare(tfm,'mgrk',value) end - function fonts.initializers.node.otf.mkmk(tfm,value) return prepare(tfm,'mkmk',value) end - function fonts.initializers.node.otf.nalt(tfm,value) return prepare(tfm,'nalt',value) end - function fonts.initializers.node.otf.nlck(tfm,value) return prepare(tfm,'nlck',value) end - function fonts.initializers.node.otf.nukt(tfm,value) return prepare(tfm,'nukt',value) end - function fonts.initializers.node.otf.numr(tfm,value) return prepare(tfm,'numr',value) end - function fonts.initializers.node.otf.onum(tfm,value) return prepare(tfm,'onum',value) end - function fonts.initializers.node.otf.ordn(tfm,value) return prepare(tfm,'ordn',value) end - function fonts.initializers.node.otf.ornm(tfm,value) return prepare(tfm,'ornm',value) end - function fonts.initializers.node.otf.pnum(tfm,value) return prepare(tfm,'pnum',value) end - function fonts.initializers.node.otf.pref(tfm,value) return prepare(tfm,'pref',value) end - function fonts.initializers.node.otf.pres(tfm,value) return prepare(tfm,'pres',value) end - function fonts.initializers.node.otf.pstf(tfm,value) return prepare(tfm,'pstf',value) end - function fonts.initializers.node.otf.rlig(tfm,value) return prepare(tfm,'rlig',value) end - function fonts.initializers.node.otf.rphf(tfm,value) return prepare(tfm,'rphf',value) end - function fonts.initializers.node.otf.rtla(tfm,value) return prepare(tfm,'rtla',value) end - function fonts.initializers.node.otf.salt(tfm,value) return prepare(tfm,'salt',value) end - function fonts.initializers.node.otf.sinf(tfm,value) return prepare(tfm,'sinf',value) end - function fonts.initializers.node.otf.smcp(tfm,value) return prepare(tfm,'smcp',value) end - function fonts.initializers.node.otf.smpl(tfm,value) return prepare(tfm,'smpl',value) end - function fonts.initializers.node.otf.ss01(tfm,value) return prepare(tfm,'ss01',value) end - function fonts.initializers.node.otf.ss02(tfm,value) return prepare(tfm,'ss02',value) end - function fonts.initializers.node.otf.ss03(tfm,value) return prepare(tfm,'ss03',value) end - function fonts.initializers.node.otf.ss04(tfm,value) return prepare(tfm,'ss04',value) end - function fonts.initializers.node.otf.ss05(tfm,value) return prepare(tfm,'ss05',value) end - function fonts.initializers.node.otf.ss06(tfm,value) return prepare(tfm,'ss06',value) end - function fonts.initializers.node.otf.ss07(tfm,value) return prepare(tfm,'ss07',value) end - function fonts.initializers.node.otf.ss08(tfm,value) return prepare(tfm,'ss08',value) end - function fonts.initializers.node.otf.ss09(tfm,value) return prepare(tfm,'ss09',value) end - function fonts.initializers.node.otf.subs(tfm,value) return prepare(tfm,'subs',value) end - function fonts.initializers.node.otf.sups(tfm,value) return prepare(tfm,'sups',value) end - function fonts.initializers.node.otf.swsh(tfm,value) return prepare(tfm,'swsh',value) end - function fonts.initializers.node.otf.titl(tfm,value) return prepare(tfm,'titl',value) end - function fonts.initializers.node.otf.tnam(tfm,value) return prepare(tfm,'tnam',value) end - function fonts.initializers.node.otf.tnum(tfm,value) return prepare(tfm,'tnum',value) end - function fonts.initializers.node.otf.trad(tfm,value) return prepare(tfm,'trad',value) end - function fonts.initializers.node.otf.unic(tfm,value) return prepare(tfm,'unic',value) end - function fonts.initializers.node.otf.zero(tfm,value) return prepare(tfm,'zero',value) end - -end - -do - - -- todo: use nodes helpers - - local glyph = node.id('glyph') - local glue = node.id('glue') - local kern = node.id('kern') - local disc = node.id('disc') - local whatsit = node.id('whatsit') - - local fontdata = tfm.id - local has_attribute = node.has_attribute - local set_attribute = node.set_attribute - local state = attributes.numbers['state'] or 100 - local marknumber = attributes.numbers['mark'] or 200 - local report = logs.report - local scale = tex.scale - - otf.features.process = { } - - -- we share some vars here, after all, we have no nested lookups and - -- less code - - local tfmdata = false - local otfdata = false - local characters = false - local descriptions = false - local marks = false - local indices = false - local glyphs = false - local currentfont = false - local rlmode = 0 - - -- we cheat a bit and assume that a font,attr combination are kind of ranged - - local context_setups = fonts.define.specify.context_setups - local context_numbers = fonts.define.specify.context_numbers - - -- 1 loop over glyphs loop over lookups, quit at match - -- 2 loop over glyphs loop over lookups, continue at match - -- 3 loop over lookups loop over glyphs - - otf.strategy = 2 - - function otf.features.process.feature(head,font,attr,kind,attribute) - tfmdata = fontdata[font] - local shared = tfmdata.shared - otfdata = shared.otfdata - characters = tfmdata.characters - descriptions = tfmdata.descriptions - glyphs = otfdata.glyphs - local luatex = otfdata.luatex - marks = luatex.marks - indices = luatex.indices - currentfont = font - rlmode = 0 - local script, language, strategy - if attr and attr > 0 then - local features = context_setups[context_numbers[attr]] - language, script, strategy = features.language or "dflt", features.script or "dflt", features.strategy or otf.strategy - else - language, script, strategy = tfmdata.language or "dflt", tfmdata.script or "dflt", tfmdata.strategy or otf.strategy - end - local fullkind = kind .. script .. language - local lookuptable = shared.lookuptable[fullkind] - if lookuptable then - -- local strategy = otf.strategy - local types = otfdata.luatex.name_to_type - local start, done, ok = head, false, false - local processes = shared.processes[fullkind] - if #processes == 1 then - local p = processes[1] - while start do -- evt splitsen - local id = start.id - if id == glyph then - if start.subtype<256 and start.font == font and - (not attr or has_attribute(start,0,attr)) and -- dynamic feature - (not attribute or has_attribute(start,state,attribute)) then - -- we can make the p vars also global to this closure - local pp = p[3] -- all lookups - local pc = pp[start.char] - if pc then - start, ok = p[1](start,kind,p[2],pc,pp,p[4]) - done = done or ok - if start then start = start.next end - else - start = start.next - end - else - start = start.next - end - elseif id == glue and p[5] then - local pp = p[3] -- all lookups - local pc = pp[32] -- space, todo: more generic spacing - if pc then - start, ok = p[1](start,kind,p[2],pc,pp,p[4]) - done = done or ok - if start then start = start.next end - else - start = start.next - end - elseif id == whatsit then - local subtype = start.subtype - if subtype == 7 then - local dir = start.dir - if dir == "+TRT" then - rlmode = -1 - elseif dir == "+TLT" then - rlmode = 1 - else - rlmode = 0 - end - elseif subtype == 6 then - local dir = start.dir - if dir == "TRT" then - rlmode = -1 - elseif dir == "TLT" then - rlmode = 1 - else - rlmode = 0 - end - end - start = start.next - else - start = start.next - end - end - elseif strategy == 3 then - for i=1,#processes do local p = processes[i] - local pp = p[3] - start = head - while start do - local id = start.id - if id == glyph then - if start.subtype<256 and start.font == font and - (not attr or has_attribute(start,0,attr)) and -- dynamic feature - (not attribute or has_attribute(start,state,attribute)) then - local pc = pp[start.char] - if pc then - start, ok = p[1](start,kind,p[2],pc,pp,p[4]) - if ok then - done = true - end -- else - if start then start = start.next end - else - start = start.next - end - else - start = start.next - end - elseif id == glue then - if p[5] then -- chain - local pc = pp[32] - if pc then - start, ok = p[1](start,kind,p[2],pc,p[3],p[4]) - if ok then - done = true - end - if start then start = start.next end - else - start = start.next - end - else - start = start.next - end - elseif id == whatsit then - local subtype = start.subtype - if subtype == 7 then - local dir = start.dir - if dir == "+TRT" then - rlmode = -1 - elseif dir == "+TLT" then - rlmode = 1 - else - rlmode = 0 - end - elseif subtype == 6 then - local dir = start.dir - if dir == "TRT" then - rlmode = -1 - elseif dir == "TLT" then - rlmode = 1 - else - rlmode = 0 - end - end - start = start.next - else - start = start.next - end - end - end - else - while start do - local id = start.id - if id == glyph then - if start.subtype<256 and start.font == font and - (not attr or has_attribute(start,0,attr)) and -- dynamic feature - (not attribute or has_attribute(start,state,attribute)) then - local chr = start.char -- used ? - for i=1,#processes do local p = processes[i] - local pp = p[3] ---~ local pc = pp[chr] - local pc = pp[start.char] - if pc then - start, ok = p[1](start,kind,p[2],pc,pp,p[4]) - if ok then - done = true - if strategy == 1 then - break - end - end -- else - if not start then - break - end - end - end - if start then start = start.next end - else - start = start.next - end - elseif id == glue then - for i=1,#processes do local p = processes[i] - if p[5] then -- chain - local pp = p[3] - local pc = pp[32] - if pc then - start, ok = p[1](start,kind,p[2],pc,pp,p[4]) - if ok then - done = true - if strategy == 1 then - break - end - end - if not start then - break - end - end - end - end - if start then start = start.next end - elseif id == whatsit then - local subtype = start.subtype - if subtype == 7 then - local dir = start.dir - if dir == "+TRT" then - rlmode = -1 - elseif dir == "+TLT" then - rlmode = 1 - else - rlmode = 0 - end - elseif subtype == 6 then - local dir = start.dir - if dir == "TRT" then - rlmode = -1 - elseif dir == "TLT" then - rlmode = 1 - else - rlmode = 0 - end - end - start = start.next - else - start = start.next - end - end - end - return head, done - else - return head, false - end - end - - -- we can assume that languages that use marks are not hyphenated - -- we can also assume that at most one discretionary is present - - local copy_list, slide, free = node.copy_list, node.slide, node.free - - local function toligature(start,stop,char,markflag,discfound) -- brr head - if start ~= stop then - if discfound then - local lignode = node.copy(start) - lignode.font = start.font - lignode.char = char - lignode.subtype = 2 - start = node.do_ligature_n(start, stop, lignode) - if start.id == disc then - local prev = start.prev - start = start.next - end - else -- start is the ligature - -- to be checked: this marknum mess (sensitive for looping) - local deletemarks = markflag ~= "mark" ---~ deletemarks = false - start.components = copy_list(start,stop) - local last = slide(start.components) - start.components.prev, last.next = nil, nil - start.char, start.subtype = char, 2 - local next, done, marknum = start.next, false, 1 - local after = stop.next - while not done do - done = next == stop - if not deletemarks and marks[next.char] then - set_attribute(next,marknumber,marknum) - next = next.next - --~ marknum = marknum + 1 - else - marknum = marknum + 1 - start, next = nodes.remove(start,next,true) - end - end - while after and after.id == glyph and after.font == currentfont and marks[after.char] do - if deletemarks then - start, after = nodes.remove(start,after,true) - else - set_attribute(after,marknumber,marknum) - after = after.next - --~ marknum = marknum + 1 - end - end - - end - end - return start - end - - function otf.features.process.gsub_single(start,kind,lookupname,replacements) - if replacements then - if otf.trace_replacements then - report("otf process","%s:%s replacing 0x%04X by 0x%04X",kind,lookupname,start.char,replacements) - end - start.char = replacements - return start, true - else - return start, false - end - end - - function otf.features.process.gsub_alternate(start,kind,lookupname,alternatives) - if alternatives then - if otf.trace_replacements then - report("otf process","%s:%s alternative 0x%04X => %s",kind,lookupname,start.char,table.hexed(alternatives)) - end - start.char = alternatives[1] -- will be preference - return start, true - else - return start, false - end - end - - function otf.features.process.gsub_multiple(start,kind,lookupname,multiples) - if multiples then - if otf.trace_replacements then - report("otf process","%s:%s multiple 0x%04X => %s",kind,lookupname,start.char,table.hexed(multiples)) - end - start.char = multiples[1] - if #multiples > 1 then - for k=2,#multiples do - local n = node.copy(start) - local sn = start.next - n.char = multiples[k] - n.next = sn - n.prev = start - if sn then - sn.prev = n - end - start.next = n - start = n - end - end - return start, true - else - return start, false - end - end - - function otf.features.process.gsub_ligature(start,kind,lookupname,ligatures,alldata,flags) - local s, stop, discfound = start.next, nil, false - while s do - local id = s.id - if id == glyph and s.subtype<256 then - if s.font == currentfont then - local char = s.char - if marks[char] then - s = s.next - else - -- we use indices, which saves a lookup, but we can use - -- names when we comment the line after (*lig*) - -- local lg = ligatures[1][glyphs[indices[char]].name] - local lg = ligatures[1][indices[char]] - -- mayb esome day we introduce a more efficient method - if not lg then - break - else - stop = s - ligatures = lg - s = s.next - end - end - else - break - end - elseif id == disc then - discfound = true - s = s.next - else - break - end - end - if stop and ligatures[2] then - start = toligature(start,stop,ligatures[2],flags[1],discfound) - if otf.trace_ligatures then - report("otf process","%s: inserting ligature 0x%04X (%s)",kind,start.char,utf.char(start.char)) - end - return start, true - end - return start, false - end - - function otf.features.process.gpos_mark2base(start,kind,lookupname,m_anchors,b_anchors) - local markchar = start.char - if marks[markchar] then - local markanchors = m_anchors['mark'] - if markanchors then - local component = start.prev - while component and component.id == glyph and component.subtype<256 and component.font == currentfont do - local basechar = component.char - if marks[basechar] then - component = component.prev - else - local baseanchors = b_anchors[basechar] - if baseanchors then - baseanchors = baseanchors['basechar'] - if baseanchors then - for anchor, ma in pairs(markanchors) do - local ba = baseanchors[anchor] - if ba then - local factor = tfmdata.factor - local dx, dy = scale(ba[1]-ma[1],factor), scale(ba[2]-ma[2],factor) - start.xoffset, start.yoffset = component.xoffset - dx, component.yoffset + dy - if otf.trace_anchors then - report("otf process","%s: anchoring mark 0x%04X to basechar 0x%04X => (%s,%s) => (%s,%s)", - kind,markchar,basechar,dx,dy,start.xoffset,start.yoffset) - end - return start, true - end - end - end - end - break - end - end - end - end - return start, false - end - - function otf.features.process.gpos_mark2ligature(start,kind,lookupname,m_anchors,b_anchors) -- maybe use copies - local markchar = start.char - if marks[markchar] then - local markanchors = m_anchors['mark'] - if markanchors then - local component = start.prev - while component and component.id == glyph and component.subtype<256 and component.font == currentfont do - local basechar = component.char - if marks[basechar] then - component = component.prev - else - local baseanchors = b_anchors[basechar] - if baseanchors then - baseanchors = baseanchors['baselig'] - if baseanchors then - for anchor, ma in pairs(markanchors) do - local ba = baseanchors[anchor] - if ba then - local n = has_attribute(start,marknumber) - ba = ba[n] - if ba then - local factor = tfmdata.factor - local dx, dy = scale(ba[1]-ma[1],factor), scale(ba[2]-ma[2],factor) - start.xoffset, start.yoffset = component.xoffset - dx, component.yoffset + dy - if otf.trace_anchors then - report("otf process","%s: anchoring mark 0x%04X to baseligature 0x%04X => (%s,%s) => (%s,%s)", - kind,markchar,basechar,dx,dy,component.xoffset,component.yoffset) - end - return start, true - end - end - end - end - end - break - end - end - return start, done - end - end - return start, false - end - - -- hm which one is the correct one? chainprocs.gpos_mark2mark ot the next; the next one - -- had more tracing so might be the best - - function otf.features.process.gpos_mark2mark(start,kind,lookupname,b_anchors,m_anchors) - local basemarkchar = start.char - if marks[basemarkchar] then - local baseanchors = b_anchors['basemark'] - if baseanchors then - local component = start.next - while component and component.id == glyph and component.subtype<256 and component.font == currentfont do - local markchar = component.char - if not marks[markchar] then - break - else - local basemarkattr = has_attribute(start,marknumber) or 1 - local markattr = has_attribute(component,marknumber) or 1 - if basemarkattr == markattr then -- still needed? - local markanchors = m_anchors[markchar] - if markanchors then - local markanchor = markanchors['mark'] - if markanchor then - for anchor,ma in pairs(markanchor) do - local ba = baseanchors[anchor] - if ba then - local factor = tfmdata.factor - local dx, dy = scale(ba[1]-ma[1],factor), scale(ba[2]-ma[2],factor) - component.xoffset, component.yoffset = start.xoffset - dx, start.yoffset + dy - if otf.trace_anchors then - report("otf process","%s:%s:%s anchoring mark 0x%04X to basemark 0x%04X => (%s,%s) => (%s,%s)", - kind,anchor,markattr,markchar,basemarkchar,dx,dy,component.xoffset,component.yoffset) - end - return start, true - end - end - end - end - -- weird, was here - end - component = component.next - end - end - end - end - return start, false - end - - function otf.features.process.gpos_cursive(start,kind,lookupname,exitanchors,anchors) - local trace = otf.trace_cursive - if rlmode >= 0 then - local prev, done = start.prev, false - while prev do - if prev.id == glyph and prev.subtype<256 and prev.font == currentfont then - local prevchar = prev.char - if marks[prevchar] then - -- what do do with marks, give them the offset of the previous glyph? - prev = prev.prev - else - local startchar = start.char - local entryanchors, exitanchors = anchors[startchar], anchors[prevchar] - if entryanchors and exitanchors then - local centry, cexit = entryanchors['centry'], exitanchors['cexit'] - if centry and cexit then - for anchor, entry in pairs(centry) do - local exit = cexit[anchor] - if exit then - local factor = tfmdata.factor - local dx = -(descriptions[prevchar].width-exit[1]) - entry[1] - local dy = -(entry[2]-exit[2]) - start.yoffset = prev.yoffset + scale(dy, factor) - -- start.xoffset = scale(tx[i], factor) - node.insert_before(prev,start,nodes.kern(scale(dx,factor))) - if trace then - report("otf process","%s:%s move 0x%04X cursive (%s,%s)",kind,lookupname,startchar,dx,dy) - end - done = true - end - end - end - end - break - end - else - break - end - end - else - local trace, factor = fonts.otf.trace_anchors, tfmdata.factor - local next, done, total_x, total_y, tx, ty, stack = start.next, false, 0, 0, { }, { }, { } - local function finish() - done = true - for i=1,#stack do - local s = stack[i] - s.yoffset = scale(total_y, factor) - node.insert_before(s.prev,s,nodes.kern(scale(tx[i],factor))) - if fonts.otf.trace_cursive then - report("otf process",format("%s:%s move 0x%04X cursive (%s,%s)",kind,lookupname,s.char,tx[i],total_y)) - end - total_y = total_y - (ty[i] or 0) - end - total_x, total_y, tx, ty, stack = 0, 0, { }, { }, { } - end - while next do - if next.id == glyph and next.subtype<256 and next.font == currentfont then - local nextchar = next.char - if marks[nextchar] then - next = next.next - else - local entryanchors, exitanchors = anchors[nextchar], anchors[start.char] - if entryanchors and exitanchors then - local centry, cexit = entryanchors['centry'], exitanchors['cexit'] - if centry and cexit then - for anchor, entry in pairs(centry) do - local exit = cexit[anchor] - if exit then - local dy = -exit[2] + entry[2] - local dx = -(descriptions[nextchar].width-entry[1]) - exit[1] -- often width == entry 1 - tx[#tx+1], ty[#ty+1] = dx, dy - total_x, total_y = total_x + dx, total_y + dy - stack[#stack+1] = start - break - end - end - else - finish() - end - else - finish() - end - start = next - next = start.next - end - else - finish() - break - end - end - return start, done - end - return start, done - end - - function otf.features.process.gpos_single(start,kind,lookupname,basekerns,kerns) - report("otf process","gpos_single not yet supported") - return start, false - end - - function otf.features.process.gpos_pair(start,kind,lookupname,basekerns,kerns) - -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too - -- todo: kerns in components of ligatures - local next = start.next - if not next then - return start, false - else - local prev, done = start, false - local trace = otf.trace_kerns - local factor = tfmdata.factor - while next and next.id == glyph and next.subtype<256 and next.font == currentfont do - local cn = descriptions[next.char] - if not cn or cn.class == 'mark' then - prev = next - next = next.next - else - local krn = basekerns[next.char] - if not krn then - -- skip - elseif type(krn) == "table" then - local a, b = krn[3], krn[7] - if a and a ~= 0 then - local k = nodes.kern(scale(a,factor)) - k.next = next - k.prev = prev - prev.next = k - next.prev = k - if trace then - -- todo - end - end - if b and b ~= 0 then - report("otf process","we need to do something with the second kern xoff %s",b) - end - else - -- todo, just start, next = node.insert_before(head,next,nodes.kern(scale(kern,factor))) - if otf.trace_kerns then - report("otf process","%s: inserting kern %s between 0x%04X and 0x%04X",kind,krn,prev.char,next.char) - end - local k = nodes.kern(scale(krn,factor)) - k.next = next - k.prev = prev - prev.next = k - next.prev = k - end - break - end - end - return start, done - end - end - --- -- -- temp here, needs to be tested first -- -- -- - ---~ function do_gpos_pair(start,kind,lookupname,basekerns,kerns) ---~ local trace = otf.trace_kerns ---~ local factor = tfmdata.factor ---~ local next, prev, middle = start.next, start, nil ---~ -- to be optimized, we can consider using basemode for fonts without lookups ---~ -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too ---~ -- todo: kerns in components of ligatures ---~ -- ---~ -- find valid next ---~ while next do ---~ local id = next.id ---~ if id == glyph and next.subtype<256 and next.font == currentfont then ---~ local cn = characters[next.char] ---~ if not cn or cn.description.class == 'mark' then ---~ prev = next ---~ next = next.next ---~ else ---~ break ---~ end ---~ elseif id == disc then -- assume same font ---~ middle = next ---~ else ---~ return start, false ---~ end ---~ end ---~ local function inject(head, prevkern, nextkern) ---~ if head then ---~ -- kern between prevchar and head ---~ local tail = node.slide(head) -- tail ---~ if head.id == glyph then ---~ local c = head.char ---~ local pc = prevkern[c] ---~ if pc then ---~ local k = nodes.kern(scale(pc,factor)) ---~ k.next = head ---~ head = k ---~ end ---~ end ---~ -- kern between prevchar and tail ---~ if tail.id == glyph then ---~ local c = tail.char ---~ local nc = nextkern[c] ---~ if nc then ---~ tail.next = nodes.kern(scale(nc,factor)) ---~ end ---~ end ---~ -- kern between head .. tail ---~ local c = head ---~ while c do do_gpos_pair(c,kind,lookupname,basekerns,kerns) ; c = c.next end ---~ end ---~ return head ---~ end ---~ if middle then ---~ -- prev middle next - assumes same lookup ---~ local prevkern, nextkern = kerns[prev.char], kerns[next.char] ---~ local m = middle.pre ; if m then middle.pre = inject(m, prevkern, nextkern) end ---~ local m = middle.post ; if m then middle.post = inject(m, prevkern, nextkern) end ---~ local m = middle.replace ; if m then middle.replace = inject(m, prevkern, nextkern) end ---~ elseif next then ---~ local prevchar, nextchar = prev.char, next.char ---~ if prev.components then ---~ local prevkern, nextkern = kerns[prev.char], kerns[next.char] ---~ local p = prev.components ; if p then prev.components = inject(p, prevkern, nextkern) end ---~ end ---~ local krn = basekerns[nextchar] ---~ if not krn then ---~ return start, false ---~ elseif type(krn) == "table" then ---~ local a, b = krn[3], krn[7] ---~ if a and a ~= 0 then ---~ start, next = node.insert_before(start,next,nodes.kern(scale(a,factor))) ---~ if trace then ---~ report("otf process","%s: inserting kern %s between 0x%04X and 0x%04X",kind,a,prevchar,nextchar) ---~ end ---~ end ---~ if b and b ~= 0 then ---~ report("otf process","we need to do something with the second kern xoff %s",b) ---~ end ---~ return start, true -- could be next ---~ else ---~ if otf.trace_kerns then ---~ report("otf process","%s: inserting kern %s between 0x%04X and 0x%04X",kind,krn,prevchar,nextchar) ---~ end ---~ start, next = node.insert_before(start,next,nodes.kern(scale(krn,factor))) ---~ return start, true -- could be next ---~ end ---~ end ---~ return start, false ---~ end - ---~ otf.features.process.gpos_pair = do_gpos_pair - --- -- -- temp here, needs to be tested -- -- -- - - - local chainprocs = { } -- we can probably optimize this because they're all internal lookups - - -- For the moment we save each looked up glyph in the sequence, which is ok because - -- each lookup in the chain has its own sequence. This saves memory. Only ligatures - -- are stored in the featurecache, because we don't want to loop over all characters - -- in order to locate them. - - function chainprocs.gsub_single(start,stop,kind,lookupname,sequence,f,l,lookups) - local trace = otf.trace_replacements - local c, r = trace and { }, trace and { } - local lookup, index, current = 1, f, start - while current ~= nil do - if current.id == glyph then -- test for more ? - local char = current.char - local cacheslot = sequence[index] - local replacement = cacheslot[char] - if replacement == true then - if lookups then - -- didn't we have the arrays available? - local looks = glyphs[descriptions[char].index].lookups -- SLOW, USE OTFDATA - if looks then - local luatex = otfdata.luatex - local glyphlookups = luatex.internals[lookups[lookup]].lookups - local unicodes = luatex.unicodes - for gl=1,#glyphlookups do - local lv = looks[glyphlookups[gl]] - if lv then - local ulv = unicodes[lv[1][2]] - if not ulv then - replacement = char - elseif type(ulv) == "number" then - replacement = ulv - else - replacement = ulv[1] - end - cacheslot[char] = replacement - break - end - end - else - replacement, cacheslot[char] = char, char - end - else - replacement, cacheslot[char] = char, char - end - end - if trace then - c[#c+1], r[#r+1] = char, replacement - end - current.char = replacement - if current == stop then - break - else - current, lookup, index = current.next, lookup + 1, index + 1 - end - elseif current == stop then - break - else - current = current.next - end - end - if trace then - report("otf chain","%s: single replacement %s by %s",kind,table.hexed(c),table.hexed(r)) - end - return start - end - - function chainprocs.gsub_multiple(start,stop,kind,lookupname,sequence,f,l,lookups) - local char = start.char - local cacheslot = sequence[f] -- [1] - local replacement = cacheslot[char] - if replacement == true then - if lookups then - local looks = glyphs[descriptions[char].index].lookups - if looks then - local luatex = otfdata.luatex - local lookups = luatex.internals[lookups[1]].lookups - local unicodes = luatex.unicodes - for l=1,#lookups do - local lv = looks[lookups[l]] - if lv then - replacement = { } - for c in lv[1][2]:gmatch("[^ ]+") do - local uc = unicodes[c] - if type(uc) == "number" then - replacement[#replacement+1] = uc - else - replacement[#replacement+1] = uc[1] - end - end - cacheslot[char] = replacement - break - end - end - else - replacement = { char } - cacheslot[char] = replacement - end - else - replacement = { char } - cacheslot[char] = replacement - end - end - if otf.trace_replacements then - report("otf chain","%s: replacing character 0x%04X by multiple 0x%04X",kind,char,table.hexed(replacement)) - end - start.char = replacement[1] - if #replacement > 1 then - for k=2,#replacement do - local n = node.copy(start) - local sn = start.next - n.char = replacement[k] - n.next = sn - n.prev = start - if sn then - sn.prev = n - end - start.next = n - start = n - end - end - return start - end - - function chainprocs.gsub_alternate(start,stop,kind,lookupname,sequence,f,l,lookups) - local char = start.char - local cacheslot = sequence[f] -- [1] - local replacement = cacheslot[char] - if replacement == true then - if lookups then - local looks = glyphs[descriptions[char].index].lookups - if looks then - local luatex = otfdata.luatex - local lookups = luatex.internals[lookups[1]].lookups - local unicodes = luatex.unicodes - for l=1,#lookups do - local lv = looks[lookups[l]] - if lv then - replacement = { } - for c in lv[1][2]:gmatch("[^ ]+") do - local uc = unicodes[c] - if type(uc) == "number" then - replacement[#replacement+1] = uc - else - replacement[#replacement+1] = uc[1] - end - end - cacheslot[char] = replacement - break - end - end - else - replacement = { char } - cacheslot[char] = replacement - end - else - replacement = { char } - cacheslot[char] = replacement - end - end - if otf.trace_replacements then - report("otf chain","%s: replacing character 0x%04X by alternate",kind,char) - end - start.char = replacement[1] - return start - end - - function chainprocs.gsub_ligature(start,stop,kind,lookupname,sequence,f,l,lookups,flags) - if lookups then - if start == stop then - -- print("todo: optimize") - end - local featurecache = fontdata[currentfont].shared.featurecache - local ligaturecache = featurecache[kind] - if not ligaturecache then - ligaturecache = otf.features.collect_ligatures(tfmdata,kind) -- double cached ? - featurecache[kind] = ligaturecache - end - local lookups = otfdata.luatex.internals[lookups[1]].lookups - local trace = otf.trace_ligatures - for i=1,#lookups do - local ligatures = ligaturecache[lookups[i]] - if ligatures and ligatures[start.char] then - ligatures = ligatures[start.char] - local s, discfound = start.next, false - while s do - local id = s.id - if id == disc then - s = s.next - discfound = true - elseif descriptions[s.char].class == 'mark' then -- marks - s = s.next - else - local lg = ligatures[1][s.char] - if not lg then - break - else - ligatures = lg - if s == stop then - break - else - s = s.next - end - end - end - end - if ligatures[2] then - if trace then - if start == stop then - report("otf chain","%s: replacing character 0x%04X by ligature 0x%04X",kind,start.char,ligatures[2]) - else - report("otf chain","%s: replacing character 0x%04X upto 0x%04X by ligature 0x%04X",kind,start.char,stop.char,ligatures[2]) - end - end - return toligature(start,stop,ligatures[2],flags[1],discfound) - end - break - end - end - end - return stop - end - - -- weird, mkmk can have a mark2base, in idris font - - function chainprocs.gpos_mark2base(start,stop,kind,lookupname,sequence,f,l,lookups,flags) - -- dynamic resolver - local markchar = start.char - if marks[markchar] then - local anchortag = sequence[f][markchar] - if anchortag == true then - local ok = false - local classes = otfdata.anchor_classes - local lookups = otfdata.luatex.internals[lookups[1]].lookups - for k=1,#classes do - local v = classes[k] - if v.lookup == lookups[1] then -- let's gamble for uniqueness: and v.type == kind then - anchortag = v.name - sequence[f][markchar] = anchortag - ok = true - break - end - end - if not ok and otf.trace_anchors then - report("otf chain","%s: no matching mark2base anchor class for 0x%04X, lookup %s",kind,markchar,lookups[1]) - end - end - if anchortag ~= true then - local component = start.prev - while component and component.id == glyph and component.subtype<256 and component.font == currentfont do - local basechar = component.char - if marks[basechar] then - component = component.prev - else - local bglyph = glyphs[descriptions[basechar].index] -- startchar - local baseanchors = bglyph.anchors['basechar'] - if baseanchors then - local ba = baseanchors[anchortag] - if ba then - local mglyph = glyphs[descriptions[markchar].index] - local markanchors = mglyph.anchors['mark'] - if markanchors then - local ma = markanchors[anchortag] - if ma then - local factor = tfmdata.factor - local dx, dy = scale(ba[1]-ma[1],factor), scale(ba[2]-ma[2],factor) - start.xoffset, start.yoffset = component.xoffset - dx, component.yoffset + dy - if otf.trace_anchors then - report("otf chain","%s: anchoring mark 0x%04X to basechar 0x%04X => (%s,%s) => (%s,%s)", - kind,markchar,basechar,dx,dy,start.xoffset,start.yoffset) - end - return start, true - end - end - end - end - break - end - end - end - end - return start, false - end - - function chainprocs.gpos_mark2ligature(start,stop,kind,lookupname,sequence,f,l,lookups,flags) - -- dynamic resolver - local markchar = start.char - if marks[markchar] then - local anchortag = sequence[f][markchar] - if anchortag == true then - local classes = otfdata.anchor_classes - local lookups = otfdata.luatex.internals[lookups[1]].lookups - local ok = false - for k=1,#classes do - local v = classes[k] - if v.lookup == lookups[1] then -- and v.type == kind then - anchortag = v.name - sequence[f][markchar] = anchortag - ok = true - break - end - end - if not ok and otf.trace_anchors then - report("otf chain","%s: no matching mark2ligature anchor class for 0x%04X, lookup %s",kind,markchar,lookups[1]) - end - end - if anchortag ~= true then - local component = start.prev - while component and component.id == glyph and component.subtype<256 and component.font == currentfont do - local basechar = component.char - if marks[basechar] then - component = component.prev - else - local bglyph = glyphs[descriptions[basechar].index] -- startchar - local baseanchors = bglyph.anchors['baselig'] - if baseanchors then - local ba = baseanchors[anchortag] - if ba then - local n = has_attribute(start,marknumber) - ba = ba[n] -- ok ? - if ba then - local mglyph = glyphs[descriptions[markchar].index] - local markanchors = mglyph.anchors['mark'] - if markanchors then - local ma = markanchors[anchortag] - if ma then - local factor = tfmdata.factor - local dx, dy = scale(ba[1]-ma[1],factor), scale(ba[2]-ma[2],factor) - start.xoffset, start.yoffset = component.xoffset - dx, component.yoffset + dy - if otf.trace_anchors then - report("otf chain","%s: anchoring mark 0x%04X to baseligature 0x%04X => (%s,%s) => (%s,%s)", - kind,basechar,markchar,dx,dy,start.xoffset,start.yoffset) - end - return start, true - end - end - end - end - end - break - end - end - end - end - return start, false - end - - -- to be checked (see previous generic mark2mark) - - function chainprocs.gpos_mark2mark(start,stop,kind,lookupname,sequence,f,l,lookups) - local component = start.next - if component and component.id == glyph and component.subtype<256 and component.font == currentfont and marks[component.char] then - local markchar = start.char - local anchortag = sequence[f][markchar] -- [1][char] - if anchortag == true then - local classes = otfdata.anchor_classes - local ok = false - for k=1,#classes do - local v = classes[k] - if v.lookup == lookupname then -- and v.type == kind then - anchortag = v.name - sequence[f][markchar] = anchortag - ok = true - break - end - end - if not ok and otf.trace_anchors then - report("otf chain","%s: no matching mark2mark anchor class for 0x%04X, lookup %s",kind,markchar,lookups[1]) - end - end - if anchortag ~= true then - -- the following may have been be spoiled while idrising the other ones - local markattr = has_attribute(start, marknumber) or 1 -- i need to check this ! 1 is new ! - local baseattr = has_attribute(component,marknumber) or 1 -- i need to check this ! 1 is new ! - if baseattr == markattr then - local glyph = glyphs[descriptions[markchar].index] - if glyph.anchors and glyph.anchors[anchortag] then - local trace = otf.trace_anchors - local done = false - local baseanchors = glyph.anchors['basemark'][anchortag] - while component do - local basechar = component.char - local markanchors = glyphs[descriptions[basechar].index].anchors['mark'][anchortag] - if markanchors then - for anchor,data in pairs(markanchors) do - local ba = baseanchors[anchor] - if ba then - local factor = tfmdata.factor - local dx, dy = scale(ba[1]-ma[1],factor), scale(ba[2]-ma[2],factor) - start.xoffset, start.yoffset = component.xoffset - dx, component.yoffset + dy - if otf.trace_anchors then - report("otf chain","%s: anchoring mark 0x%04X to basemark 0x%04X => (%s,%s) => (%s,%s)", - kind,markchar,basechar,dx,dy,component.xoffset,component.yoffset) - end - done = true - break - end - end - end - component = component.next - if component and component.id == glyph and component.subtype<256 and component.font == currentfont and marks[component.char] then - markattr = has_attribute(component,marknumber) - if baseattr ~= markattr then - break - end - else - break - end - end - return start, done - end - end - end - end - return start, false - end - - function chainprocs.gpos_cursive(start,stop,kind,lookupname,sequence,f,l,lookups) - report("otf chain","chainproc gpos_cursive not yet supported") - return start - end - function chainprocs.gpos_single(start,stop,kind,lookupname,sequence,f,l,lookups) - report("otf process","chainproc gpos_single not yet supported") - return start - end - function chainprocs.gpos_pair(start,stop,kind,lookupname,sequence,f,l,lookups) - report("otf process","chainproc gpos_pair not yet supported") - return start - end - - function chainprocs.self(start,stop,kind,lookupname,sequence,f,l,lookups) - report("otf process","self refering lookup cannot happen") - return stop - end - - local zwnj = 0x200C - local zwj = 0x200D - - -- what pointer to return, spec says stop - - -- to be discussed ... is bidi changer a space? - - function otf.features.process.contextchain(start,kind,lookupname,contextdata) - local contexts, flags, done = contextdata.lookups, contextdata.flags, false - local skipmark, skipligature, skipbase = unpack(flags) -- unpack slower than assignment - for k=1,#contexts do - local match, next, last = true, start, start - local rule, lookuptype, sequence, f, l, lookups = unpack(contexts[k]) -- unpack is slow - local s = #sequence - if s == 1 then - match = next.id == glyph and next.subtype<256 and next.font == currentfont and sequence[1][next.char] - else - -- todo: better space check (maybe check for glue) - local n = f - while n <= l do - if last then - local id = last.id - if id == glyph and last.subtype<256 and last.font == currentfont then - local char = last.char - local cc = characters[char] - if cc then - local ccd = descriptions[char] - if ccd then - local class = ccd.class - if class == skipmark or class == skipligature or class == skipbase then - -- skip 'm - last = last.next - elseif sequence[n][char] then - if n < l then - last = last.next - end - n = n + 1 - else - match = false break - end - else - match = false break - end - else -- play safe - match = false break - end - elseif id == disc then -- what to do with kerns? - last = last.next - else - match = false break - end - else - match = false break - end - end - if match and f > 1 then - local prev = start.prev - if prev then - -- removed optimiziation for f == 2, we have to deal with marks anyway - local n = f-1 - while n >= 1 do - if prev then - local id = prev.id - if id == glyph and prev.subtype<256 and prev.font == currentfont then -- normal char - local char = prev.char - local cc = characters[char] - if cc then - local ccd = descriptions[char] - if ccd then - local class = ccd.class - if class == skipmark or class == skipligature or class == skipbase then - -- skip 'm - elseif sequence[n][char] then - n = n -1 - else - match = false break - end - else - match = false break - end - else - match = false break - end - elseif id == disc then - -- skip 'm - elseif sequence[n][32] then - n = n -1 - else - match = false break - end - prev = prev.prev - elseif sequence[n][32] then - n = n -1 - else - match = false break - end - end - elseif f == 2 then - match = sequence[1][32] - else - for n=f-1,1 do - if not sequence[n][32] then - match = false break - end - end - end - end - if match and s > l then - local next = last.next - if next then - -- removed optimiziation for s-l == 1, we have to deal with marks anyway - local n = l+ 1 - while n <= s do - if next then - local id = next.id - if id == glyph and next.subtype<256 and next.font == currentfont then -- normal char - local char = next.char - local cc = characters[char] - if cc then - local ccd = descriptions[char] - if ccd then - local class = ccd.class - if class == skipmark or class == skipligature or class == skipbase then - -- skip 'm - elseif sequence[n][char] then - n = n + 1 - else - match = false break - end - else - match = false break - end - else - match = false break - end - elseif id == disc then - -- skip 'm - elseif sequence[n][32] then -- brrr - n = n + 1 - else - match = false break - end - next = next.next - elseif sequence[n][32] then - n = n + 1 - else - match = false break - end - end - elseif s-l == 1 then - match = sequence[s][32] - else - for n=l+1,s do - if not sequence[n][32] then - match = false break - end - end - end - end - end - if match then - local trace = otf.trace_contexts - if trace then - local char = start.char - report("otf chain","%s: rule %s of %s matches at char 0x%04X (%s) for (%s,%s,%s) chars, lookuptype %s",kind,rule,lookupname,char,utf.char(char),f-1,l-f+1,s-l,lookuptype) - end - if lookups then - local cp = chainprocs[lookuptype] - if cp then - start = cp(start,last,kind,lookupname,sequence,f,l,lookups,flags) - else - report("otf chain","%s: lookuptype %s not supported yet for %s",kind,lookuptype,lookupname) - end - elseif trace then - report("otf chain","%s: skipping match for %s",kind,lookupname) - end - done = true - break - end - end - return start, done - end - ---~ if true then ---~ if n < f then ---~ texio.write_nl(format("%s before %s %04x %s %s %s",lookupname,n,char,class,skipmark or "?",tostring(sequence[n][char]))) ---~ elseif n > l then ---~ texio.write_nl(format("%s after %s %04x %s %s %s",lookupname,n,char,class,skipmark or "?",tostring(sequence[n][char]))) ---~ else ---~ texio.write_nl(format("%s current %s %04x %s %s %s",lookupname,n,char,class,skipmark or "?",tostring(sequence[n][char]))) ---~ end ---~ end - ---~ elseif char == zwnj and sequence[n][32] then -- brrr - - -- this needs to be fixed ! ! ! ! ! ! ! ! - - function otf.features.process.reversecontextchain(start,kind,lookupname,contextdata) - -- PROBABLY WRONG, WE NEED TO WALK BACK OVER THE LIST - local done = false - local contexts = contextdata.lookups - local flags = contextdata.flags - local skipmark, skipligature, skipbase = unpack(flags) - for k=1,#contexts do - local match, next, first, last = true, start, start, start - local rule, lookuptype, sequence, f, l, lookups = unpack(contexts[k]) -- unpack is slow - if #sequence == 1 then - match = next.id == glyph and next.subtype<256 and next.font == currentfont and sequence[1][next.char] - else - local n, s = #sequence, 1 - while n > 0 do - if next then - local id = next.id - if id == glyph and next.subtype<256 and next.font == currentfont then -- normal char - local char = next.char - local class = descriptions[char].class - if class == skipmark or class == skipligature or class == skipbase then - -- skip - elseif sequence[n][char] then - if n == f then - first = next -- ok ? - end - if n == l then - last = next -- ok ? - end - n = n - 1 - else - match = false break - end - elseif id == disc then - -- skip - elseif not sequence[n][32] then -- brrr - match = false break - end - next = next.next - elseif sequence[n][32] then - n = n - 1 - else - match = false break - end - end - end - if match then - local trace = otf.trace_contexts - if trace then - local char = first.char - report("otf reverse chain","%s: rule %s of %s matches, replacing starts at char 0x%04X (%s) lookuptype %s",kind,rule,lookupname,char,utf.char(char),lookuptype) - end - if lookups then - local cp = chainprocs[lookuptype] - if cp then - if start == first then - start = cp(first,last,kind,lookupname,sequence,f,l,lookups,flags) - else - first = cp(first,last,kind,lookupname,sequence,f,l,lookups,flags) - end - else - report("otf reverse chain","%s: lookuptype %s not supported yet for %s",kind,lookuptype,lookupname) - end - elseif trace then - report("otf reverse chain","%s: skipping match for %s",kind,lookupname) - end - done = true - break - end - end - return start, done - end - - otf.features.process.gsub_context = otf.features.process.contextchain - otf.features.process.gsub_contextchain = otf.features.process.contextchain - otf.features.process.gsub_reversecontextchain = otf.features.process.reversecontextchain - - otf.features.process.gpos_contextchain = otf.features.process.contextchain - otf.features.process.gpos_context = otf.features.process.contextchain - -end - -do - - local process = otf.features.process.feature - - function fonts.methods.node.otf.aalt(head,font,attr) return process(head,font,attr,'aalt') end - function fonts.methods.node.otf.abvm(head,font,attr) return process(head,font,attr,'abvm') end - function fonts.methods.node.otf.afrc(head,font,attr) return process(head,font,attr,'afrc') end - function fonts.methods.node.otf.akhn(head,font,attr) return process(head,font,attr,'akhn') end - function fonts.methods.node.otf.blwm(head,font,attr) return process(head,font,attr,'blwm') end - function fonts.methods.node.otf.c2pc(head,font,attr) return process(head,font,attr,'c2pc') end - function fonts.methods.node.otf.c2sc(head,font,attr) return process(head,font,attr,'c2sc') end - function fonts.methods.node.otf.calt(head,font,attr) return process(head,font,attr,'calt') end - function fonts.methods.node.otf.case(head,font,attr) return process(head,font,attr,'case') end - function fonts.methods.node.otf.ccmp(head,font,attr) return process(head,font,attr,'ccmp') end - function fonts.methods.node.otf.clig(head,font,attr) return process(head,font,attr,'clig') end - function fonts.methods.node.otf.cpsp(head,font,attr) return process(head,font,attr,'cpsp') end - function fonts.methods.node.otf.cswh(head,font,attr) return process(head,font,attr,'cswh') end - function fonts.methods.node.otf.curs(head,font,attr) return process(head,font,attr,'curs') end - function fonts.methods.node.otf.dlig(head,font,attr) return process(head,font,attr,'dlig') end - function fonts.methods.node.otf.dnom(head,font,attr) return process(head,font,attr,'dnom') end - function fonts.methods.node.otf.expt(head,font,attr) return process(head,font,attr,'expt') end - function fonts.methods.node.otf.fin2(head,font,attr) return process(head,font,attr,'fin2') end - function fonts.methods.node.otf.fin3(head,font,attr) return process(head,font,attr,'fin3') end - function fonts.methods.node.otf.fina(head,font,attr) return process(head,font,attr,'fina',3) end - function fonts.methods.node.otf.frac(head,font,attr) return process(head,font,attr,'frac') end - function fonts.methods.node.otf.fwid(head,font,attr) return process(head,font,attr,'fwid') end - function fonts.methods.node.otf.haln(head,font,attr) return process(head,font,attr,'haln') end - function fonts.methods.node.otf.hist(head,font,attr) return process(head,font,attr,'hist') end - function fonts.methods.node.otf.hkna(head,font,attr) return process(head,font,attr,'hkna') end - function fonts.methods.node.otf.hlig(head,font,attr) return process(head,font,attr,'hlig') end - function fonts.methods.node.otf.hngl(head,font,attr) return process(head,font,attr,'hngl') end - function fonts.methods.node.otf.hwid(head,font,attr) return process(head,font,attr,'hwid') end - function fonts.methods.node.otf.init(head,font,attr) return process(head,font,attr,'init',1) end - function fonts.methods.node.otf.isol(head,font,attr) return process(head,font,attr,'isol',4) end - function fonts.methods.node.otf.ital(head,font,attr) return process(head,font,attr,'ital') end - function fonts.methods.node.otf.jp78(head,font,attr) return process(head,font,attr,'jp78') end - function fonts.methods.node.otf.jp83(head,font,attr) return process(head,font,attr,'jp83') end - function fonts.methods.node.otf.jp90(head,font,attr) return process(head,font,attr,'jp90') end - function fonts.methods.node.otf.kern(head,font,attr) return process(head,font,attr,'kern') end - function fonts.methods.node.otf.liga(head,font,attr) return process(head,font,attr,'liga') end - function fonts.methods.node.otf.lnum(head,font,attr) return process(head,font,attr,'lnum') end - function fonts.methods.node.otf.locl(head,font,attr) return process(head,font,attr,'locl') end - function fonts.methods.node.otf.mark(head,font,attr) return process(head,font,attr,'mark') end - function fonts.methods.node.otf.med2(head,font,attr) return process(head,font,attr,'med2') end - function fonts.methods.node.otf.medi(head,font,attr) return process(head,font,attr,'medi',2) end - function fonts.methods.node.otf.mgrk(head,font,attr) return process(head,font,attr,'mgrk') end - function fonts.methods.node.otf.mkmk(head,font,attr) return process(head,font,attr,'mkmk') end - function fonts.methods.node.otf.nalt(head,font,attr) return process(head,font,attr,'nalt') end - function fonts.methods.node.otf.nlck(head,font,attr) return process(head,font,attr,'nlck') end - function fonts.methods.node.otf.nukt(head,font,attr) return process(head,font,attr,'nukt') end - function fonts.methods.node.otf.numr(head,font,attr) return process(head,font,attr,'numr') end - function fonts.methods.node.otf.onum(head,font,attr) return process(head,font,attr,'onum') end - function fonts.methods.node.otf.ordn(head,font,attr) return process(head,font,attr,'ordn') end - function fonts.methods.node.otf.ornm(head,font,attr) return process(head,font,attr,'ornm') end - function fonts.methods.node.otf.pnum(head,font,attr) return process(head,font,attr,'pnum') end - function fonts.methods.node.otf.pref(head,font,attr) return process(head,font,attr,'pref') end - function fonts.methods.node.otf.pres(head,font,attr) return process(head,font,attr,'pres') end - function fonts.methods.node.otf.pstf(head,font,attr) return process(head,font,attr,'pstf') end - function fonts.methods.node.otf.rlig(head,font,attr) return process(head,font,attr,'rlig') end - function fonts.methods.node.otf.rphf(head,font,attr) return process(head,font,attr,'rphf') end - function fonts.methods.node.otf.rtla(head,font,attr) return process(head,font,attr,'rtla') end - function fonts.methods.node.otf.salt(head,font,attr) return process(head,font,attr,'calt') end - function fonts.methods.node.otf.sinf(head,font,attr) return process(head,font,attr,'sinf') end - function fonts.methods.node.otf.smcp(head,font,attr) return process(head,font,attr,'smcp') end - function fonts.methods.node.otf.smpl(head,font,attr) return process(head,font,attr,'smpl') end - function fonts.methods.node.otf.ss01(head,font,attr) return process(head,font,attr,'ss01') end - function fonts.methods.node.otf.ss02(head,font,attr) return process(head,font,attr,'ss02') end - function fonts.methods.node.otf.ss03(head,font,attr) return process(head,font,attr,'ss03') end - function fonts.methods.node.otf.ss04(head,font,attr) return process(head,font,attr,'ss04') end - function fonts.methods.node.otf.ss05(head,font,attr) return process(head,font,attr,'ss05') end - function fonts.methods.node.otf.ss06(head,font,attr) return process(head,font,attr,'ss06') end - function fonts.methods.node.otf.ss07(head,font,attr) return process(head,font,attr,'ss07') end - function fonts.methods.node.otf.ss08(head,font,attr) return process(head,font,attr,'ss08') end - function fonts.methods.node.otf.ss09(head,font,attr) return process(head,font,attr,'ss09') end - function fonts.methods.node.otf.subs(head,font,attr) return process(head,font,attr,'subs') end - function fonts.methods.node.otf.sups(head,font,attr) return process(head,font,attr,'sups') end - function fonts.methods.node.otf.swsh(head,font,attr) return process(head,font,attr,'swsh') end - function fonts.methods.node.otf.titl(head,font,attr) return process(head,font,attr,'titl') end - function fonts.methods.node.otf.tnam(head,font,attr) return process(head,font,attr,'tnam') end - function fonts.methods.node.otf.tnum(head,font,attr) return process(head,font,attr,'tnum') end - function fonts.methods.node.otf.trad(head,font,attr) return process(head,font,attr,'trad') end - function fonts.methods.node.otf.unic(head,font,attr) return process(head,font,attr,'unic') end - function fonts.methods.node.otf.zero(head,font,attr) return process(head,font,attr,'zero') end - -end - --- common stuff - -function otf.features.language(tfmdata,value) - if value then - value = value:lower() - if otf.tables.languages[value] then - tfmdata.language = value - end - end -end - -function otf.features.script(tfmdata,value) - if value then - value = value:lower() - if otf.tables.scripts[value] then - tfmdata.script = value - end - end -end - -function otf.features.mode(tfmdata,value) - if value then - tfmdata.mode = value:lower() - end -end - -function otf.features.strategy(tfmdata,value) - if value then - tfmdata.strategy = tonumber(value) or otf.strategy - end -end - -fonts.initializers.base.otf.language = otf.features.language -fonts.initializers.base.otf.script = otf.features.script -fonts.initializers.base.otf.mode = otf.features.mode -fonts.initializers.base.otf.method = otf.features.mode -fonts.initializers.base.otf.strategy = otf.features.strategy -- not needed - -fonts.initializers.node.otf.language = otf.features.language -fonts.initializers.node.otf.script = otf.features.script -fonts.initializers.node.otf.mode = otf.features.mode -fonts.initializers.node.otf.method = otf.features.mode -fonts.initializers.node.otf.strategy = otf.features.strategy - -do - - local tlig_list = { - endash = "hyphen hyphen", - emdash = "hyphen hyphen hyphen", - --~ quotedblleft = "quoteleft quoteleft", - --~ quotedblright = "quoteright quoteright", - --~ quotedblleft = "grave grave", - --~ quotedblright = "quotesingle quotesingle", - --~ quotedblbase = "comma comma", - } - local trep_list = { - --~ [0x0022] = 0x201D, - [0x0027] = 0x2019, - --~ [0x0060] = 0x2018, - } - - local tlig_feature = { - features = { { scripts = { { script = "DFLT", langs = { "dflt" }, } }, tag = "tlig", comment = "added bij mkiv" }, }, - name = "ctx_tlig", - subtables = { { name = "ctx_tlig_1" } }, - type = "gsub_ligature", - flags = { }, - always = true - } - local trep_feature = { - features = { { scripts = { { script = "DFLT", langs = { "dflt" }, } }, tag = "trep", comment = "added bij mkiv" }, }, - name = "ctx_trep", - subtables = { { name = "ctx_trep_1" } }, - type = "gsub_single", - flags = { }, - always = true - } - - function otf.enhance.enrich(data,filename) - local glyphs = data.glyphs - local indices = data.map.map - for unicode, index in pairs(indices) do - local glyph = glyphs[index] - local l = tlig_list[glyph.name] - if l then - local o = glyph.lookups or { } - o["ctx_tlig_1"] = { { "ligature", l, glyph.name } } - glyph.lookups = o - end - local r = trep_list[unicode] - if r then - local replacement = indices[r] - if replacement then - local o = glyph.lookups or { } - o["ctx_trep_1"] = { { "substitution", glyphs[replacement].name } } --- - glyph.lookups = o - end - end - end - data.gsub = data.gsub or { } - logs.report("load otf","enhance: registering tlig feature") - table.insert(data.gsub,1,table.fastcopy(tlig_feature)) - logs.report("load otf","enhance: registering trep feature") - table.insert(data.gsub,1,table.fastcopy(trep_feature)) - end - - local prepare = otf.features.prepare.feature - local process = otf.features.process.feature - - otf.tables.features['tlig'] = 'TeX Ligatures' - otf.tables.features['trep'] = 'TeX Replacements' - - function fonts.initializers.node.otf.tlig(tfm,value) return prepare(tfm,'tlig',value) end - function fonts.initializers.node.otf.trep(tfm,value) return prepare(tfm,'trep',value) end - - function fonts.methods.node.otf.tlig(head,font,attr) return process(head,font,attr,'tlig') end - function fonts.methods.node.otf.trep(head,font,attr) return process(head,font,attr,'trep') end - - function fonts.initializers.base.otf.tlig(tfm,value) otf.features.prepare_base_substitutions(tfm,'tlig',value) end - function fonts.initializers.base.otf.trep(tfm,value) otf.features.prepare_base_substitutions(tfm,'trep',value) end - -end - --- we need this because fonts can be bugged - --- \definefontfeature[calt][language=nld,script=latn,mode=node,calt=yes,clig=yes,rlig=yes] --- \definefontfeature[dflt][language=nld,script=latn,mode=node,calt=no, clig=yes,rlig=yes] --- \definefontfeature[fixd][language=nld,script=latn,mode=node,calt=no, clig=yes,rlig=yes,ignoredrules={44,45,47}] - --- \starttext - --- {\type{dflt:}\font\test=ZapfinoExtraLTPro*dflt at 24pt \test \char57777\char57812 c/o} \endgraf --- {\type{calt:}\font\test=ZapfinoExtraLTPro*calt at 24pt \test \char57777\char57812 c/o} \endgraf --- {\type{fixd:}\font\test=ZapfinoExtraLTPro*fixd at 24pt \test \char57777\char57812 c/o} \endgraf - --- \stoptext - ---~ table.insert(fonts.triggers,"ignoredrules") - ---~ function fonts.initializers.node.otf.ignoredrules(tfmdata,value) ---~ if value then ---~ -- these tests must move ! ---~ tfmdata.unique = tfmdata.unique or { } ---~ tfmdata.unique.ignoredrules = tfmdata.unique.ignoredrules or { } ---~ local ignored = tfmdata.unique.ignoredrules ---~ -- value is already ok now ---~ for s in string.gmatch(value:gsub("[{}]","")..",", "%s*(.-),") do ---~ ignored[tonumber(s)] = true ---~ end ---~ end ---~ end - -fonts.initializers.base.otf.equaldigits = fonts.initializers.common.equaldigits -fonts.initializers.node.otf.equaldigits = fonts.initializers.common.equaldigits - -fonts.initializers.base.otf.lineheight = fonts.initializers.common.lineheight -fonts.initializers.node.otf.lineheight = fonts.initializers.common.lineheight - -fonts.initializers.base.otf.compose = fonts.initializers.common.compose -fonts.initializers.node.otf.compose = fonts.initializers.common.compose - --- temp hack, may change - -function fonts.initializers.base.otf.kern(tfmdata,value) - otf.features.prepare_base_kerns(tfmdata,'kern',value) -end - --- bonus function - -function otf.name_to_slot(name) -- todo: afm en tfm - local tfmdata = tfm.id[font.current()] - if tfmdata and tfmdata.shared then - local otfdata = tfmdata.shared.otfdata - if otfdata and otfdata.luatex then - local unicode = otfdata.luatex.unicodes[name] - if type(unicode) == "number" then - return unicode - else - return unicode[1] - end - end - end - return nil -end - -function otf.char(n) -- todo: afm en tfm - if type(n) == "string" then - n = otf.name_to_slot(n) - end - if n then - tex.sprint(tex.ctxcatcodes,format("\\char%s ",n)) - end -end - --- Here we plug in some analyzing code (will move to font-tfm). - -do - - local glyph = node.id('glyph') - local glue = node.id('glue') - local penalty = node.id('penalty') - - local fontdata = tfm.id - local set_attribute = node.set_attribute - local has_attribute = node.has_attribute - local state = attributes.numbers['state'] or 100 - - local fcs = fonts.color.set - local fcr = fonts.color.reset - local remove = node.remove - - -- in the future we will use language/script attributes instead of the - -- font related value, but then we also need dynamic features which is - -- somewhat slower; and .. we need a chain of them - - local type = type - - local initializers, methods = fonts.analyzers.initializers, fonts.analyzers.methods - - function fonts.initializers.node.otf.analyze(tfmdata,value,attr) - if attr and attr > 0 then - script, language = a_to_script[attr], a_to_language[attr] - else - script, language = tfmdata.script, tfmdata.language - end - local action = initializers[script] - if action then - if type(action) == "function" then - return action(tfmdata,value) - else - local action = action[language] - if action then - return action(tfmdata,value) - end - end - end - return nil - end - - function fonts.methods.node.otf.analyze(head,font,attr) - local tfmdata = fontdata[font] - local script, language - if attr and attr > 0 then - script, language = a_to_script[attr], a_to_language[attr] - else - script, language = tfmdata.script, tfmdata.language - end - local action = methods[script] - if action then - if type(action) == "function" then - return action(head,font,attr) - else - action = action[language] - if action then - return action(head,font,attr) - end - end - end - return head, false - end - - otf.features.register("analyze",true) -- we always analyze - table.insert(fonts.triggers,"analyze") -- we need a proper function for doing this - - -- latin - - fonts.analyzers.methods.latn = fonts.analyzers.aux.setstate - - -- this info eventually will go into char-def - - local zwnj = 0x200C - local zwj = 0x200D - - local isol = { - [0x0621] = true, [zwnj] = true, - } - - local isol_fina = { - [0x0622] = true, [0x0623] = true, [0x0624] = true, [0x0625] = true, [0x0627] = true, [0x062F] = true, - [0x0630] = true, [0x0631] = true, [0x0632] = true, - [0x0648] = true, [0x0698] = true, - [0xFEF5] = true, [0xFEF7] = true, [0xFEF9] = true, [0xFEFB] = true, - } - - local isol_fina_medi_init = { - [0x0626] = true, [0x0628] = true, [0x0629] = true, [0x062A] = true, [0x062B] = true, [0x062C] = true, [0x062D] = true, [0x062E] = true, - [0x0633] = true, [0x0634] = true, [0x0635] = true, [0x0636] = true, [0x0637] = true, [0x0638] = true, [0x0639] = true, [0x063A] = true, - [0x0640] = true, -- tadwil - [0x0641] = true, [0x0642] = true, [0x0643] = true, [0x0644] = true, [0x0645] = true, [0x0646] = true, [0x0647] = true, [0x0649] = true, [0x064A] = true, - [0x067E] = true, [0x0686] = true, [0x06AF] = true, [0x06A9] = true, [0x06CC] = true, - [zwj] = true, - } - - local arab_warned = { } - - local function warning(current,what) - local char = current.char - if not arab_warned[char] then - log.report("analyze","arab: character %s (0x%04X) has no %s class", char, char, what) - arab_warned[char] = true - end - end - - function fonts.analyzers.methods.nocolor(head,font,attr) - for n in node.traverse(head,glyph) do - if not font or n.font == font then - fcr(n) - end - end - return head, true - end - - otf.remove_joiners = true -- for idris who want it as option - - function fonts.analyzers.methods.arab(head,font,attr) -- maybe make a special version with no trace - local tfmdata = fontdata[font] - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local first, last, current, done = nil, nil, head, false - local trace, removejoiners = fonts.color.trace, otf.remove_joiners - --~ local laststate = 0 - local joiners = { } - local function finish() - if last then - if first == last then - local fc = first.char - if isol_fina_medi_init[fc] or isol_fina[fc] then - set_attribute(first,state,4) -- isol - if trace then fcs(first,"font:isol") end - else - warning(first,"isol") - set_attribute(first,state,0) -- error - if trace then fcr(first) end - end - else - local lc = last.char - if isol_fina_medi_init[lc] or isol_fina[lc] then -- why isol here ? - -- if laststate == 1 or laststate == 2 or laststate == 4 then - set_attribute(last,state,3) -- fina - if trace then fcs(last,"font:fina") end - else - warning(last,"fina") - set_attribute(last,state,0) -- error - if trace then fcr(last) end - end - end - first, last = nil, nil - elseif first then - -- first and last are either both set so we never com here - local fc = first.char - if isol_fina_medi_init[fc] or isol_fina[fc] then - set_attribute(first,state,4) -- isol - if trace then fcs(first,"font:isol") end - else - warning(first,"isol") - set_attribute(first,state,0) -- error - if trace then fcr(first) end - end - first = nil - end - --~ laststate = 0 - end - while current do - if current.id == glyph and current.subtype<256 and current.font == font and not has_attribute(current,state) then - done = true - -- some day we will make a characters.marks hash - -- this is also more efficient since it's shared - local char = current.char - local descriptions = descriptions[char] - if removejoiners and char == zwj or char == zwnj then - joiners[#joiners+1] = current - end - if descriptions and descriptions.class == "mark" then - set_attribute(current,state,5) -- mark - if trace then fcs(current,"font:mark") end - elseif isol[char] then -- can be zwj or zwnj too - finish() - set_attribute(current,state,4) -- isol - if trace then fcs(current,"font:isol") end - first, last = nil, nil - --~ laststate = 0 - elseif not first then - if isol_fina_medi_init[char] then - set_attribute(current,state,1) -- init - if trace then fcs(current,"font:init") end - first, last = first or current, current - --~ laststate = 1 - elseif isol_fina[char] then - set_attribute(current,state,4) -- isol - if trace then fcs(current,"font:isol") end - first, last = nil, nil - --~ laststate = 0 - else -- no arab - finish() - end - elseif isol_fina_medi_init[char] then - first, last = first or current, current - set_attribute(current,state,2) -- medi - if trace then fcs(current,"font:medi") end - --~ laststate = 2 - elseif isol_fina[char] then - -- if not laststate == 1 then - if not has_attribute(last,state,1) then - -- tricky, we need to check what last may be ! - set_attribute(last,state,2) -- medi - if trace then fcs(last,"font:medi") end - end - set_attribute(current,state,3) -- fina - if trace then fcs(current,"font:fina") end - first, last = nil, nil - --~ laststate = 0 - elseif char >= 0x0600 and char <= 0x06FF then - if trace then fcs(current,"font:rest") end - finish() - else --no - finish() - end - else - finish() - end - current = current.next - end - finish() - if removejoiners then - for i=1,#joiners do - head = remove(head,joiners[i]) - end - end - return head, done - end - - -- han (chinese) (unfinished) - - -- this info eventually will go into char-def - - -- in the future we will use language/script attributes instead of the - -- font related value, but then we also need dynamic features which is - -- somewhat slower; and .. we need a chain of them - - local type = type - - local opening_parenthesis_hw = table.tohash { -- half width - 0x0028, - 0x005B, - 0x007B, - 0x2018, -- ‘ - 0x201C, -- “ - } - - local opening_parenthesis_fw = table.tohash { -- full width - 0x3008, -- 〈 Left book quote - 0x300A, -- 《 Left double book quote - 0x300C, -- 「 left quote - 0x300E, -- 『 left double quote - 0x3010, -- 【 left double book quote - 0x3014, -- 〔 left book quote - 0x3016, --〖 left double book quote - 0x3018, -- left tortoise bracket - 0x301A, -- left square bracket - 0x301D, -- reverse double prime qm - 0xFF08, -- ( left parenthesis - 0xFF3B, -- [ left square brackets - 0xFF5B, -- { left curve bracket - 0xFF62, -- left corner bracket - } - - local closing_parenthesis_hw = table.tohash { -- half width - 0x0029, - 0x005D, - 0x007D, - 0x2019, -- ’ right quote, right - 0x201D, -- ” right double quote - } - - local closing_parenthesis_fw = table.tohash { -- full width - 0x3009, -- 〉 book quote - 0x300B, -- 》 double book quote - 0x300D, -- 」 right quote, right - 0x300F, -- 』 right double quote - 0x3011, -- 】 right double book quote - 0x3015, -- 〕 right book quote - 0x3017, -- 〗 right double book quote - 0x3019, -- right tortoise bracket - 0x301B, -- right square bracket - 0x301E, -- double prime qm - 0x301F, -- low double prime qm - 0xFF09, -- ) right parenthesis - 0xFF3D, -- ] right square brackets - 0xFF5D, -- } right curve brackets - 0xFF63, -- right corner bracket - } - - local opening_vertical = table.tohash { - 0xFE35, 0xFE37, 0xFE39, 0xFE3B, 0xFE3D, 0xFE3F, 0xFE41, 0xFE43, 0xFE47, - } - - local closing_vertical = table.tohash { - 0xFE36, 0xFE38, 0xFE3A, 0xFE3C, 0xFE3E, 0xFE40, 0xFE42, 0xFE44, 0xFE48, - } - - local opening_punctuation_hw = table.tohash { -- half width - } - - local opening_punctuation_fw = table.tohash { - -- 0x2236, -- ∶ - -- 0xFF0C, -- , - } - - local closing_punctuation_hw = table.tohash { -- half width - 0x0021, -- ! - 0x002C, -- , - 0x002E, -- . - 0x003A, -- : - 0x003B, -- ; - 0x003F, -- ? - 0xFF61, -- hw full stop - } - - local closing_punctuation_fw = table.tohash { -- full width - 0x3001, -- 、 - 0x3002, -- 。 - 0xFF01, -- ! - 0xFF0C, -- , - 0xFF0E, -- . - 0xFF1A, -- : - 0xFF1B, -- ; - 0xFF1F, -- ? - } - - local non_starter = table.tohash { -- japanese - 0x3005, 0x3041, 0x3043, 0x3045, 0x3047, - 0x3049, 0x3063, 0x3083, 0x3085, 0x3087, - 0x308E, 0x3095, 0x3096, 0x309B, 0x309C, - 0x309D, 0x309E, 0x30A0, 0x30A1, 0x30A3, - 0x30A5, 0x30A7, 0x30A9, 0x30C3, 0x30E3, - 0x30E5, 0x30E7, 0x30EE, 0x30F5, 0x30F6, - 0x30FC, 0x30FD, 0x30FE, 0x31F0, 0x31F1, - 0x30F2, 0x30F3, 0x30F4, 0x31F5, 0x31F6, - 0x30F7, 0x30F8, 0x30F9, 0x31FA, 0x31FB, - 0x30FC, 0x30FD, 0x30FE, 0x31FF, - } - - -- the characters below are always appear in a double form, so there - -- will be two Chinese ellipsis characters together that denote - -- ellipsis marks and it is not allowed to break between them - - local hyphenation = table.tohash { - 0x2026, -- … ellipsis - 0x2014, -- — hyphen - } - - local function is_han_character(char) - -- we might add such info to char-def - return - (char>=0x03040 and char<=0x0309F) or - (char>=0x030A0 and char<=0x030FF) or - (char>=0x031F0 and char<=0x031FF) or - (char>=0x03400 and char<=0x04DFF) or - (char>=0x04E00 and char<=0x09FFF) or - (char>=0x0F900 and char<=0x0FAFF) or - (char>=0x0FF00 and char<=0x0FFEF) or - (char>=0x20000 and char<=0x2A6DF) or - (char>=0x2F800 and char<=0x2FA1F) - end - -- maybe an entry in the character table: hanclass - - --~ opening_parenthesis_hw / closing_parenthesis_hw - --~ opening_parenthesis_fw / closing_parenthesis_fw - --~ opening_punctuation_hw / closing_punctuation_hw - --~ opening_punctuation_fw / closing_punctuation_fw - - --~ non_starter - --~ hyphenation - - --~ opening_vertical / closing_vertical - - fonts.analyzers.methods.stretch_hang = true - - fonts.analyzers.methods.hang_data = { - inter_char_stretch_factor = 2.00, -- we started with 0.5, then 1.0 - inter_char_half_factor = 0.50, -- normally there is no reason to change this - inter_char_half_shrink_factor = 0.25, -- normally there is no reason to change this - } - - local hang_data = fonts.analyzers.methods.hang_data - - local insert_after, insert_before, delete = node.insert_after, node.insert_before, nodes.delete - - local function nobreak_before(head,current) - local p = current.prev - if p then - p = p.prev - if p and p.id == penalty then - p.penalty = 10000 - return head, current - end - end - return insert_before(head,current,nodes.penalty(10000)) - end - - function fonts.analyzers.methods.hani(head,font,attr) - -- maybe make a special version with no trace - local tfmdata = fontdata[font] - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local current, done, stretch, prevclass = head, false, 0, 0 - if fonts.analyzers.methods.stretch_hang then - stretch = fontdata[font].parameters.quad - end - -- penalty before break - local interspecialskip = - stretch * hang_data.inter_char_half_factor - local interspecialshrink = stretch * hang_data.inter_char_half_shrink_factor - local internormalstretch = stretch * hang_data.inter_char_stretch_factor - local trace = fonts.color.trace - -- todo: check for first and last - -- maybe it's better to look back --- we need to backtrack a glyph (also other font) - while current do - if current.id == glyph and current.subtype<256 then - if current.font == font then - local char = current.char - if false then - -- don't ask -) - elseif opening_punctuation_fw[char] or opening_parenthesis_fw[char] then - if trace then fcs(current,"font:init") end - if head ~= current then - head, _ = insert_before(head,current,nodes.glue(interspecialskip,0,interspecialshrink)) - end - head, current = insert_after(head,current,nodes.penalty(10000)) - head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0)) - prevclass, done = 1, true - elseif closing_punctuation_fw[char] or closing_parenthesis_fw[char] then - if trace then fcs(current,"font:fina") end - if prevclass > 0 then - head, current = nobreak_before(head,current) - head, current = insert_after(head,current,nodes.penalty(10000)) - head, current = insert_after(head,current,nodes.glue(interspecialskip,0,interspecialshrink)) - head, current = insert_after(head,current,nodes.penalty(0)) - head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0)) - end - prevclass, done = 2, true - elseif opening_punctuation_hw[char] or opening_parenthesis_hw[char] then - if trace then fcs(current,"font:init") end - head, current = insert_after(head,current,nodes.penalty(10000)) - head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0)) - prevclass, done = 3, true - elseif closing_punctuation_hw[char] or closing_parenthesis_hw[char] then - if trace then fcs(current,"font:fina") end - if prevclass > 0 then - head, current = nobreak_before(head,current) - head, current = insert_after(head,current,nodes.penalty(0)) - head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0)) - end - prevclass, done = 4, true - elseif hyphenation[char] then - if trace then fcs(current,"font:medi") end - if prevclass > 0 then - head, current = nobreak_before(head,current) - head, current = insert_after(head,current,nodes.penalty(0)) - head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0)) - end - prevclass, done = 5, true - elseif non_starter[char] then - if trace then fcs(current,"font:isol") end - head, current = insert_after(head,current,nodes.penalty(10000)) - head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0)) - prevclass, done = 6, true - elseif is_han_character(char) then - -- if trace then fcs(current,"font:isol") end - prevclass, done = 7, true - head, current = insert_after(head,current,nodes.penalty(0)) - head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0)) - end - else --- here we might have a mixed font - prevclass = 0 - end - elseif prevclass > 0 and current.id == glue and current.spec and current.spec.width > 0 then - -- hack, it might be better to look back and flush (we need to delete end-of-line spaces) - local next = current.next - if next.id == glyph and next.font == font then - head, current = delete(head,current) - end - end - if current then - current = current.next - end - end - return head, done - end - - fonts.analyzers.methods.hang = fonts.analyzers.methods.hani - -end - --- experimental and will probably change - -do - local process = otf.features.process.feature - local prepare = otf.features.prepare.feature - function fonts.install_feature(type,...) - if fonts[type] and fonts[type].install_feature then - fonts[type].install_feature(...) - end - end - function otf.install_feature(tag) - fonts.methods.node.otf [tag] = function(head,font,attr) return process(head,font,attr,tag) end - fonts.initializers.node.otf[tag] = function(tfm,value) return prepare(tfm,tag,value) end - end -end diff --git a/tex/context/base/font-oti.lua b/tex/context/base/font-oti.lua new file mode 100644 index 000000000..cbac6d36a --- /dev/null +++ b/tex/context/base/font-oti.lua @@ -0,0 +1,57 @@ +if not modules then modules = { } end modules ['font-oti'] = { + version = 1.001, + comment = "companion to font-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- i need to check features=yes|no also in relation to hashing + +local lower = string.lower + +local otf = fonts.otf + +otf.default_language = 'latn' +otf.default_script = 'dflt' + +local languages = otf.tables.languages +local scripts = otf.tables.scripts + +function otf.features.language(tfmdata,value) + if value then + value = lower(value) + if languages[value] then + tfmdata.language = value + end + end +end + +function otf.features.script(tfmdata,value) + if value then + value = lower(value) + if scripts[value] then + tfmdata.script = value + end + end +end + +function otf.features.mode(tfmdata,value) + if value then + tfmdata.mode = lower(value) + end +end + +fonts.initializers.base.otf.language = otf.features.language +fonts.initializers.base.otf.script = otf.features.script +fonts.initializers.base.otf.mode = otf.features.mode +fonts.initializers.base.otf.method = otf.features.mode + +fonts.initializers.node.otf.language = otf.features.language +fonts.initializers.node.otf.script = otf.features.script +fonts.initializers.node.otf.mode = otf.features.mode +fonts.initializers.node.otf.method = otf.features.mode + +otf.features.register("features",true) -- we always do features +table.insert(fonts.processors,"features") -- we need a proper function for doing this + diff --git a/tex/context/base/font-otn.lua b/tex/context/base/font-otn.lua new file mode 100644 index 000000000..113f90470 --- /dev/null +++ b/tex/context/base/font-otn.lua @@ -0,0 +1,2496 @@ +if not modules then modules = { } end modules ['font-otn'] = { + version = 1.001, + comment = "companion to font-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this is still somewhat preliminary and it will get better in due time; +-- much functionality could only be implemented thanks to the husayni font +-- of Idris Samawi Hamid to who we dedicate this module. + +-- we can use more lpegs when lpeg is extended with function args and so +-- resolving to unicode does not gain much + +-- in retrospect it always looks easy but believe it or not, it took a lot +-- of work to get proper open type support done: buggy fonts, fuzzy specs, +-- special made testfonts, many skype sessions between taco, idris and me, +-- torture tests etc etc ... unfortunately the code does not show how much +-- time it took ... + +-- todo: +-- +-- kerning is probably not yet ok for latin around dics nodes +-- extension infrastructure (for usage out of context) +-- sorting features according to vendors/renderers +-- alternative loop quitters +-- check cursive and r2l +-- find out where ignore-mark-classes went +-- remove unused tables +-- slide tail (always glue at the end so only needed once +-- default features (per language, script) +-- cleanup kern(class) code, remove double info +-- handle positions (we need example fonts) +-- handle gpos_single (we might want an extra width field in glyph nodes because adding kerns might interfere) + +--[[ldx-- +This module is a bit more split up that I'd like but since we also want to test
+with plain
The specification of OpenType is kind of vague. Apart from a lack of a proper +free specifications there's also the problem that Microsoft and Adobe +may have their own interpretation of how and in what order to apply features. +In general the Microsoft website has more detailed specifications and is a +better reference. There is also some information in the FontForge help files.
+ +Because there is so much possible, fonts might contain bugs and/or be made to +work with certain rederers. These may evolve over time which may have the side +effect that suddenly fonts behave differently.
+ +After a lot of experiments (mostly by Taco, me and Idris) we're now at yet another
+implementation. Of course all errors are mine and of course the code can be
+improved. There are quite some optimizations going on here and processing speed
+is currently acceptable. Not all functions are implemented yet, often because I
+lack the fonts for testing. Many scripts are not yet supported either, but I will
+look into them as soon as
Because there are different interpretations possible, I will extend the code +with more (configureable) variants. I can also add hooks for users so that they can +write their own extensions.
+ +Glyphs are indexed not by unicode but in their own way. This is because there is no
+relationship with unicode at all, apart from the fact that a font might cover certain
+ranges of characters. One character can have multiple shapes. However, at the
+
The raw table as it coms from
This module is sparsely documented because it is a moving target. The table format +of the reader changes and we experiment a lot with different methods for supporting +features.
+ +As with the
Incrementing the version number will force a re-cache. We jump the number by one
+when there's a fix in the
We get hits on a mark, but we're not sure if the it has to be applied so +we need to explicitly test for basechar, baselig and basemark entries.
+--ldx]]-- + +function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) + local markchar = start.char + if marks[markchar] then + local base = start.prev -- [glyph] [start=mark] + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + local basechar = base.char + if marks[basechar] then + while true do + base = base.prev + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + basechar = base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return start, false + end + end + end + local baseanchors = descriptions[basechar] + if baseanchors then + baseanchors = baseanchors.anchors + end + if baseanchors then + local baseanchors = baseanchors['basechar'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", + pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return start, true + end + end + end + if trace_bugs then + logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + else -- if trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) + fonts.register_message(currentfont,basechar,"no base anchors") + end + elseif trace_bugs then + logwarning("%s: prev node is no char",pref(kind,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return start, false +end + +function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) + -- check chainpos variant + local markchar = start.char + if marks[markchar] then + local base = start.prev -- [glyph] [optional marks] [start=mark] + local index = 1 + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + local basechar = base.char + if marks[basechar] then + index = index + 1 + while true do + base = base.prev + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + basechar = base.char + if marks[basechar] then + index = index + 1 + else + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return start, false + end + end + end + local i = has_attribute(start,markdone) + if i then index = i end + local baseanchors = descriptions[basechar] + if baseanchors then + baseanchors = baseanchors.anchors + if baseanchors then + local baseanchors = baseanchors['baselig'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + ba = ba[index] + if ba then + local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index) + if trace_marks then + logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", + pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) + end + return start, true + end + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + end + else -- if trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) + fonts.register_message(currentfont,basechar,"no base anchors") + end + elseif trace_bugs then + logwarning("%s: prev node is no char",pref(kind,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return start, false +end + +function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) + local markchar = start.char + if marks[markchar] then +--~ local alreadydone = markonce and has_attribute(start,markmark) +--~ if not alreadydone then + local base = start.prev -- [glyph] [basemark] [start=mark] + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then -- subtype test can go + local basechar = base.char + local baseanchors = descriptions[basechar] + if baseanchors then + baseanchors = baseanchors.anchors + if baseanchors then + baseanchors = baseanchors['basemark'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", + pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return start,true + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar)) + end + end + end + else -- if trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) + fonts.register_message(currentfont,basechar,"no base anchors") + end + elseif trace_bugs then + logwarning("%s: prev node is no mark",pref(kind,lookupname)) + end +--~ elseif trace_marks and trace_details then +--~ logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) +--~ end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) + end + return start,false +end + +function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to be checked + local alreadydone = cursonce and has_attribute(start,cursbase) + if not alreadydone then + local done = false + local startchar = start.char + if marks[startchar] then + if trace_cursive then + logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) + end + else + local nxt = start.next + while not done and nxt and nxt.id == glyph and nxt.subtype<256 and nxt.font == currentfont do + local nextchar = nxt.char + if marks[nextchar] then + -- should not happen (maybe warning) + nxt = nxt.next + else + local entryanchors = descriptions[nextchar] + if entryanchors then + entryanchors = entryanchors.anchors + if entryanchors then + entryanchors = entryanchors['centry'] + if entryanchors then + local al = anchorlookups[lookupname] + for anchor, entry in next, entryanchors do + if al[anchor] then + local exit = exitanchors[anchor] + if exit then + local dx, dy, bound = set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound) + end + done = true + break + end + end + end + end + end + else -- if trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) + fonts.register_message(currentfont,startchar,"no entry anchors") + end + break + end + end + end + return start, done + else + if trace_cursive and trace_details then + logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) + end + return start, false + end +end + +function handlers.gpos_single(start,kind,lookupname,kerns,sequence) + local startchar = start.char + local dx, dy = set_pair(start,tfmdata.factor,rlmode,kerns,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting single %s by (%s,%s)",pref(kind,lookupname),gref(startchar),dx,dy) + end + return start, false +end + +function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) + -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too + -- todo: kerns in components of ligatures + local snext = start.next + if not snext then + return start, false + else + local prev, done = start, false + local factor = tfmdata.factor + while snext and snext.id == glyph and snext.subtype<256 and snext.font == currentfont do + local nextchar = snext.char +local krn = kerns[nextchar] + if not krn and marks[nextchar] then + prev = snext + snext = snext.next + else + local krn = kerns[nextchar] + if not krn then + -- skip + elseif type(krn) == "table" then + if krn[1] == "pair" then + local a, b = krn[3], krn[4] + if a and #a > 0 then + local startchar = start.char + local x, y, w, h = set_pair(start,factor,rlmode,a,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + if b and #b > 0 then + local startchar = start.char + local x, y, w, h = set_pair(snext,factor,rlmode,b,characters[nextchar]) + if trace_kerns then + logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + else + logs.report("%s: check this out (old kern stuff)",pref(kind,lookupname)) + local a, b = krn[3], krn[7] + if a and a ~= 0 then + local k = set_kern(snext,factor,rlmode,a) + if trace_kerns then + logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) + end + end + if b and b ~= 0 then + logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) + end + end + done = true + elseif krn ~= 0 then + local k = set_kern(snext,factor,rlmode,krn) + if trace_kerns then + logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) + end + done = true + end + break + end + end + return start, done + end +end + +--[[ldx-- +I will implement multiple chain replacements once I run into a font that uses +it. It's not that complex to handle.
+--ldx]]-- + +local chainmores = { } +local chainprocs = { } + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + logs.report("otf subchain",...) +end +local function logwarning(...) + logs.report("otf subchain",...) +end + +-- ['coverage']={ +-- ['after']={ "r" }, +-- ['before']={ "q" }, +-- ['current']={ "a", "b", "c" }, +-- }, +-- ['lookups']={ "ls_l_1", "ls_l_1", "ls_l_1" }, + +function chainmores.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname,n) + logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) + return start, false +end + +-- handled later: +-- +-- function chainmores.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +-- return chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +-- end + +function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) + logprocess("%s: gsub_multiple not yet supported",cref(kind,chainname,chainlookupname)) + return start, false +end +function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) + logprocess("%s: gsub_alternate not yet supported",cref(kind,chainname,chainlookupname)) + return start, false +end + +-- handled later: +-- +-- function chainmores.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +-- return chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +-- end + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + logs.report("otf chain",...) +end +local function logwarning(...) + logs.report("otf chain",...) +end + +-- We could share functions but that would lead to extra function calls with many +-- arguments, redundant tests and confusing messages. + +function chainprocs.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname) + logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) + return start, false +end + +-- The reversesub is a special case, which is why we need to store the replacements +-- in a bit weird way. There is no lookup and the replacement comes from the lookup +-- itself. It is meant mostly for dealing with Urdu. + +function chainprocs.reversesub(start,stop,kind,chainname,currentcontext,cache,replacements) + local char = start.char + local replacement = replacements[char] + if replacement then + if trace_singles then + logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement)) + end + start.char = replacement + return start, true + else + return start, false + end +end + +--[[ldx-- +This chain stuff is somewhat tricky since we can have a sequence of actions to be +applied: single, alternate, multiple or ligature where ligature can be an invalid +one in the sense that it will replace multiple by one but not neccessary one that +looks like the combination (i.e. it is the counterpart of multiple then). For +example, the following is valid:
+ +Therefore we we don't really do the replacement here already unless we have the +single lookup case. The efficiency of the replacements can be improved by deleting +as less as needed but that would also mke the code even more messy.
+--ldx]]-- + +local function delete_till_stop(start,stop,ignoremarks) + if start ~= stop then + -- todo keep marks + local done = false + while not done do + done = start == stop + delete_node(start,start.next) + end + end +end + +--[[ldx-- +Here we replace start by a single variant, First we delete the rest of the +match.
+--ldx]]-- + +function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex) + -- todo: marks ? + if not chainindex then + delete_till_stop(start,stop) -- ,currentlookup.flags[1]) + end + local current = start + local subtables = currentlookup.subtables + while current do + if current.id == glyph then + local currentchar = current.char + local lookupname = subtables[1] + local replacement = cache.gsub_single[lookupname] + if not replacement then + if trace_bugs then + logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) + end + else + replacement = replacement[currentchar] + if not replacement then + if trace_bugs then + logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar)) + end + else + if trace_singles then + logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement)) + end + current.char = replacement + end + end + return start, true + elseif current == stop then + break + else + current = current.next + end + end + return start, false +end + +chainmores.gsub_single = chainprocs.gsub_single + +--[[ldx-- +Here we replace start by a sequence of new glyphs. First we delete the rest of +the match.
+--ldx]]-- + +function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) + delete_till_stop(start,stop) + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local replacements = cache.gsub_multiple[lookupname] + if not replacements then + if trace_bugs then + logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname)) + end + else + replacements = replacements[startchar] + if not replacements then + if trace_bugs then + logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar)) + end + else + if trace_multiples then + logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) + end + local sn = start.next + for k=1,#replacements do + if k == 1 then + start.char = replacements[k] + else + local n = copy_node(start) -- maybe delete the components and such + n.char = replacements[k] + n.next, n.prev = sn, start + if sn then + sn.prev = n + end + start.next, start = n, n + end + end + return start, true + end + end + return start, false +end + +--[[ldx-- +Here we replace start by new glyph. First we delete the rest of the match.
+--ldx]]-- + +function chainprocs.gsub_alternate(start,stop,kind,lookupname,currentcontext,cache,currentlookup) + -- todo: marks ? + delete_till_stop(start,stop) + local current = start + local subtables = currentlookup.subtables + while current do + if current.id == glyph then + local currentchar = current.char + local lookupname = subtables[1] + local alternatives = cache.gsub_alternate[lookupname] + if not alternatives then + if trace_bugs then + logwarning("%s: no alternative hits",cref(kind,chainname,chainlookupname,lookupname)) + end + else + alternatives = alternatives[currentchar] + if not alternatives then + if trace_bugs then + logwarning("%s: no alternative for %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar)) + end + else + local choice, index = alternative_glyph(current,alternatives,kind,chainname,chainlookupname,lookupname) + current.char = choice + if trace_alternatives then + logprocess("%s: replacing single %s by alternative %s (%s)",cref(kind,chainname,chainlookupname,lookupname),index,gref(currentchar),gref(choice),index) + end + end + end + return start, true + elseif current == stop then + break + else + current = current.next + end + end + return start, false +end + +--[[ldx-- +When we replace ligatures we use a helper that handles the marks. I might change +this function (move code inline and handle the marks by a separate function). We +assume rather stupid ligatures (no complex disc nodes).
+--ldx]]-- + +function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex) + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local ligatures = cache.gsub_ligature[lookupname] + if not ligatures then + if trace_bugs then + logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) + end + else + ligatures = ligatures[startchar] + if not ligatures then + if trace_bugs then + logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) + end + else + local s, discfound, last, nofreplacements = start.next, false, stop, 0 + while s do + local id = s.id + if id == disc then + s = s.next + discfound = true + else + local schar = s.char + if marks[schar] then -- marks + s = s.next + else + local lg = ligatures[1][schar] + if not lg then + break + else + ligatures, last, nofreplacements = lg, s, nofreplacements + 1 + if s == stop then + break + else + s = s.next + end + end + end + end + end + local l2 = ligatures[2] + if l2 then + if chainindex then + stop = last + end + if trace_ligatures then + if start == stop then + logprocess("%s: replacing character %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) + else + logprocess("%s: replacing character %s upto %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) + end + end + start = toligature(kind,lookup,start,stop,l2,currentlookup.flags[1],discfound) + return start, true, nofreplacements + elseif trace_bugs then + if start == stop then + logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) + else + logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char)) + end + end + end + end + return start, false, 0 +end + +chainmores.gsub_ligature = chainprocs.gsub_ligature + +function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) + local markchar = start.char + if marks[markchar] then + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local markanchors = cache.gpos_mark2base[lookupname] + if markanchors then + markanchors = markanchors[markchar] + end + if markanchors then + local base = start.prev -- [glyph] [start=mark] + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + local basechar = base.char + if marks[basechar] then + while true do + base = base.prev + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + basechar = base.char + if not marks[basechar] then + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) + end + return start, false + end + end + end + local baseanchors = descriptions[basechar].anchors + if baseanchors then + local baseanchors = baseanchors['basechar'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", + cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return start, true + end + end + end + if trace_bugs then + logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return start, false +end + +function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) + local markchar = start.char + if marks[markchar] then + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local markanchors = cache.gpos_mark2ligature[lookupname] + if markanchors then + markanchors = markanchors[markchar] + end + if markanchors then + local base = start.prev -- [glyph] [optional marks] [start=mark] + local index = 1 + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + local basechar = base.char + if marks[basechar] then + index = index + 1 + while true do + base = base.prev + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + basechar = base.char + if marks[basechar] then + index = index + 1 + else + break + end + else + if trace_bugs then + logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar) + end + return start, false + end + end + end + -- todo: like marks a ligatures hash + local i = has_attribute(start,markdone) + if i then index = i end + local baseanchors = descriptions[basechar].anchors + if baseanchors then + local baseanchors = baseanchors['baselig'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + ba = ba[index] + if ba then + local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", + cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) + end + return start, true + end + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return start, false +end + +function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) + local markchar = start.char + if marks[markchar] then +--~ local alreadydone = markonce and has_attribute(start,markmark) +--~ if not alreadydone then + -- local markanchors = descriptions[markchar].anchors markanchors = markanchors and markanchors.mark + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local markanchors = cache.gpos_mark2mark[lookupname] + if markanchors then + markanchors = markanchors[markchar] + end + if markanchors then + local base = start.prev -- [glyph] [basemark] [start=mark] + if base and base.id == glyph and base.subtype<256 and base.font == currentfont then -- subtype test can go + local basechar = base.char + local baseanchors = descriptions[basechar].anchors + if baseanchors then + baseanchors = baseanchors['basemark'] + if baseanchors then + local al = anchorlookups[lookupname] + for anchor,ba in next, baseanchors do + if al[anchor] then + local ma = markanchors[anchor] + if ma then + local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma) + if trace_marks then + logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", + cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) + end + return start, true + end + end + end + if trace_bugs then + logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) + end + end + end + elseif trace_bugs then + logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname)) + end + elseif trace_bugs then + logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) + end +--~ elseif trace_marks and trace_details then +--~ logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) +--~ end + elseif trace_bugs then + logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) + end + return start, false +end + +-- ! ! ! untested ! ! ! + +function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) + local alreadydone = cursonce and has_attribute(start,cursbase) + if not alreadydone then + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local exitanchors = cache.gpos_cursive[lookupname] + if exitanchors then + exitanchors = exitanchors[startchar] + end + if exitanchors then + local done = false + if marks[startchar] then + if trace_cursive then + logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) + end + else + local nxt = start.next + while not done and nxt and nxt.id == glyph and nxt.subtype<256 and nxt.font == currentfont do + local nextchar = nxt.char + if marks[nextchar] then + -- should not happen (maybe warning) + nxt = nxt.next + else + local entryanchors = descriptions[nextchar] + if entryanchors then + entryanchors = entryanchors.anchors + if entryanchors then + entryanchors = entryanchors['centry'] + if entryanchors then + local al = anchorlookups[lookupname] + for anchor, entry in next, entryanchors do + if al[anchor] then + local exit = exitanchors[anchor] + if exit then + local dx, dy, bound = set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound) + end + done = true + break + end + end + end + end + end + else -- if trace_bugs then + -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) + fonts.register_message(currentfont,startchar,"no entry anchors") + end + break + end + end + end + return start, done + else + if trace_cursive and trace_details then + logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) + end + return start, false + end + end + return start, false +end + +function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) + -- untested + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local kerns = cache.gpos_single[lookupname] + if kerns then + kerns = kerns[startchar] + if kerns then + local dx, dy = set_pair(start,tfmdata.factor,rlmode,kerns,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting single %s by (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy) + end + end + end + return start, false +end + +-- when machines become faster i will make a shared function + +function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +-- logwarning("%s: gpos_pair not yet supported",cref(kind,chainname,chainlookupname)) + local snext = start.next + if snext then + local startchar = start.char + local subtables = currentlookup.subtables + local lookupname = subtables[1] + local kerns = cache.gpos_pair[lookupname] + if kerns then + kerns = kerns[startchar] + if kerns then + local prev, done = start, false + local factor = tfmdata.factor + while snext and snext.id == glyph and snext.subtype<256 and snext.font == currentfont do + local nextchar = snext.char +local krn = kerns[nextchar] + if not krn and marks[nextchar] then + prev = snext + snext = snext.next + else +--~ local krn = kerns[nextchar] + if not krn then + -- skip + elseif type(krn) == "table" then + if krn[1] == "pair" then + local a, b = krn[3], krn[4] + if a and #a > 0 then + local startchar = start.char + local x, y, w, h = set_pair(start,factor,rlmode,a,characters[startchar]) + if trace_kerns then + logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + if b and #b > 0 then + local startchar = start.char + local x, y, w, h = set_pair(snext,factor,rlmode,b,characters[nextchar]) + if trace_kerns then + logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) + end + end + else + logs.report("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) + local a, b = krn[3], krn[7] + if a and a ~= 0 then + local k = set_kern(snext,factor,rlmode,a) + if trace_kerns then + logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) + end + end + if b and b ~= 0 then + logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) + end + end + done = true + elseif krn ~= 0 then + local k = set_kern(snext,factor,rlmode,krn) + if trace_kerns then + logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) + end + done = true + end + break + end + end + return start, done + end + end + end + return start, false +end + +-- what pointer to return, spec says stop +-- to be discussed ... is bidi changer a space? +-- elseif char == zwnj and sequence[n][32] then -- brrr + +-- somehow l or f is global +-- we don't need to pass the currentcontext, saves a bit +-- make a slow variant then can be activated but with more tracing + +local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,cache) + -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] + local flags, done = sequence.flags, false + local skipmark, skipligature, skipbase = flags[1], flags[2], flags[3] + local someskip = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) + for k=1,#contexts do + local match, current, last = true, start, start + local ck = contexts[k] + local sequence = ck[3] + local s = #sequence + if s == 1 then + -- never happens + match = current.id == glyph and current.subtype<256 and current.font == currentfont and sequence[1][current.char] + else + -- todo: better space check (maybe check for glue) + local f, l = ck[4], ck[5] + if f == l then + -- already a hit + match = true + else + -- no need to test first hit (to be optimized) + local n = f + 1 + last = last.next + -- we cannot optimize for n=2 because there can be disc nodes + -- if not someskip and n == l then + -- -- n=2 and no skips then faster loop + -- match = last and last.id == glyph and last.subtype<256 and last.font == currentfont and sequence[n][last.char] + -- else + while n <= l do + if last then + local id = last.id + if id == glyph then + if last.subtype<256 and last.font == currentfont then + local char = last.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase then +--~ if someskip and class == skipmark or class == skipligature or class == skipbase then + -- skip 'm + last = last.next + elseif sequence[n][char] then + if n < l then + last = last.next + end + n = n + 1 + else + match = false break + end + else + match = false break + end + else + match = false break + end + elseif id == disc then -- what to do with kerns? + last = last.next + else + match = false break + end + else + match = false break + end + end + -- end + end + if match and f > 1 then + local prev = start.prev + if prev then + local n = f-1 + while n >= 1 do + if prev then + local id = prev.id + if id == glyph then + if prev.subtype<256 and prev.font == currentfont then -- normal char + local char = prev.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase then +--~ if someskip and class == skipmark or class == skipligature or class == skipbase then + -- skip 'm + elseif sequence[n][char] then + n = n -1 + else + match = false break + end + else + match = false break + end + else + match = false break + end + elseif id == disc then + -- skip 'm + elseif sequence[n][32] then + n = n -1 + else + match = false break + end + prev = prev.prev + elseif sequence[n][32] then + n = n -1 + else + match = false break + end + end + elseif f == 2 then + match = sequence[1][32] + else + for n=f-1,1 do + if not sequence[n][32] then + match = false break + end + end + end + end + if match and s > l then + local current = last.next + if current then + -- removed optimiziation for s-l == 1, we have to deal with marks anyway + local n = l + 1 + while n <= s do + if current then + local id = current.id + if id == glyph then + if current.subtype<256 and current.font == currentfont then -- normal char + local char = current.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase then +--~ if someskip and class == skipmark or class == skipligature or class == skipbase then + -- skip 'm + elseif sequence[n][char] then + n = n + 1 + else + match = false break + end + else + match = false break + end + else + match = false break + end + elseif id == disc then + -- skip 'm + elseif sequence[n][32] then -- brrr + n = n + 1 + else + match = false break + end + current = current.next + elseif sequence[n][32] then + n = n + 1 + else + match = false break + end + end + elseif s-l == 1 then + match = sequence[s][32] + else + for n=l+1,s do + if not sequence[n][32] then + match = false break + end + end + end + end + end + if match then + -- ck == currentcontext + if trace_contexts then + local rule, lookuptype, sequence, f, l = ck[1], ck[2] ,ck[3], ck[4], ck[5] + local char = start.char + if ck[9] then + logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s (%s=>%s)",cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) + else + logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s",cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype) + end + end + local chainlookups = ck[6] + if chainlookups then + local nofchainlookups = #chainlookups + -- we can speed this up if needed + if nofchainlookups == 1 then + local chainlookupname = chainlookups[1] + local chainlookup = lookuptable[chainlookupname] + local cp = chainprocs[chainlookup.type] + if cp then + start, done = cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname) + else + logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + end + else + -- actually this needs a more complex treatment for which we will use chainmores + local i = 1 + repeat + local chainlookupname = chainlookups[i] + local chainlookup = lookuptable[chainlookupname] + local cp = chainmores[chainlookup.type] + if cp then + local ok, n + start, ok, n = cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,i) + -- messy since last can be changed ! + if ok then + done = true + start = start.next + if n then + -- skip next one(s) if ligature + i = i + n - 1 + end + end + else + logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + end + i = i + 1 + until i > nofchainlookups + end + else + local replacements = ck[7] + if replacements then + start, done = chainprocs.reversesub(start,last,kind,chainname,ck,cache,replacements) + else + done = true -- can be meant to be skipped + if trace_contexts then + logprocess("%s: skipping match",cref(kind,chainname)) + end + end + end + end + end + return start, done +end + +-- Because we want to keep this elsewhere (an because speed is less an issue) we +-- pass the font id so that the verbose variant can access the relevant helper tables. + +local verbose_handle_contextchain = function(font,...) + logwarning("no verbose handler installed, reverting to 'normal'") + otf.setcontextchain() + return normal_handle_contextchain(...) +end + +otf.chainhandlers = { + normal = normal_handle_contextchain, + verbose = verbose_handle_contextchain, +} + +function otf.setcontextchain(method) + if not method or method == "normal" or not otf.chainhandlers[method] then + if handlers.contextchain then -- no need for a message while making the format + logwarning("installing normal contextchain handler") + end + handlers.contextchain = normal_handle_contextchain + else + logwarning("installing contextchain handler '%s'",method) + local handler = otf.chainhandlers[method] + handlers.contextchain = function(...) + return handler(currentfont,...) + end + end + handlers.gsub_context = handlers.contextchain + handlers.gsub_contextchain = handlers.contextchain + handlers.gsub_reversecontextchain = handlers.contextchain + handlers.gpos_contextchain = handlers.contextchain + handlers.gpos_context = handlers.contextchain +end + +otf.setcontextchain() + +local missing = { } -- we only report once + +local function logprocess(...) + if trace_steps then + registermessage(...) + end + logs.report("otf process",...) +end +local function logwarning(...) + logs.report("otf process",...) +end + +local function report_missing_cache(typ,lookup) + local f = missing[currentfont] if not f then f = { } missing[currentfont] = f end + local t = f[typ] if not t then t = { } f[typ] = t end + if not t[lookup] then + t[lookup] = true + logwarning("missing cache for lookup %s of type %s in font %s (%s)",lookup,typ,currentfont,tfmdata.fullname) + end +end + +local resolved = { } -- we only resolve a font,script,language pair once + +function fonts.methods.node.otf.features(head,font,attr) + if trace_steps then + checkstep(head) + end + tfmdata = fontdata[font] + local shared = tfmdata.shared + otfdata = shared.otfdata + local luatex = otfdata.luatex + descriptions = tfmdata.descriptions + characters = tfmdata.characters + indices = tfmdata.indices + unicodes = tfmdata.unicodes + marks = tfmdata.marks + anchorlookups = luatex.lookup_to_anchor + currentfont = font + rlmode = 0 + local featuredata = otfdata.shared.featuredata -- can be made local to closure + local sequences = luatex.sequences + lookuptable = luatex.lookups + local done = false + local script, language, s_enabled, a_enabled, dyn + local attribute_driven = attr and attr ~= 0 + if attribute_driven then + local features = context_setups[context_numbers[attr]] -- could be a direct list + dyn = context_merged[attr] or 0 + language, script = features.language or "dflt", features.script or "dflt" + a_enabled = features -- shared.features -- can be made local to the resolver + if dyn == 2 or dyn == -2 then + -- font based + s_enabled = shared.features + end + else + language, script = tfmdata.language or "dflt", tfmdata.script or "dflt" + s_enabled = shared.features -- can be made local to the resolver + dyn = 0 + end + -- we can save some runtime by caching feature tests + local res = resolved[font] if not res then res = { } resolved[font] = res end + local rs = res [script] if not rs then rs = { } res [script] = rs end + local rl = rs [language] if not rl then rl = { } rs [language] = rl end + local ra = rl [attr] if ra == nil then ra = { } rl [attr] = ra end -- attr can be false + -- sequences always > 1 so no need for optimization + for s=1,#sequences do + local success = false + local sequence = sequences[s] + local r = ra[s] -- cache + if r == nil then + -- + -- this bit will move to font-ctx and become a function + --- + local chain = sequence.chain or 0 + local features = sequence.features + if not features then + -- indirect lookup, part of chain (todo: make this a separate table) + r = false -- { false, false, chain } + else + local valid, attribute, kind, what = false, false + for k,v in next, features do + -- we can quit earlier but for the moment we want the tracing + local s_e = s_enabled and s_enabled[k] + local a_e = a_enabled and a_enabled[k] + if s_e or a_e then + local l = v[script] or v[wildcard] + if l then + -- not l[language] or l[default] or l[wildcard] because we want tracing + -- only first attribute match check, so we assume simple fina's + -- default can become a font feature itself + if l[language] then +--~ valid, what = true, language + valid, what = s_e or a_e, language + -- elseif l[default] then + -- valid, what = true, default + elseif l[wildcard] then +--~ valid, what = true, wildcard + valid, what = s_e or a_e, wildcard + end + if valid then + kind, attribute = k, special_attributes[k] or false + if a_e and dyn < 0 then + valid = false + end + if trace_applied then + local typ, action = match(sequence.type,"(.*)_(.*)") + logs.report("otf node mode", + "%s font: %03i, dynamic: %03i, kind: %s, lookup: %3i, script: %-4s, language: %-4s (%-4s), type: %s, action: %s, name: %s", + (valid and "+") or "-",font,attr or 0,kind,s,script,language,what,typ,action,sequence.name) + end + break + end + end + end + end + if valid then + r = { valid, attribute, chain, kind } + else + r = false -- { valid, attribute, chain, "generic" } -- false anyway, could be flag instead of table + end + end + ra[s] = r + end +featurevalue = r and r[1] -- toto: pass to function instead + if featurevalue then + local attribute, chain, typ, subtables = r[2], r[3], sequence.type, sequence.subtables + if chain < 0 then + -- this is a limited case, no special treatments like 'init' etc + local handler = handlers[typ] + local thecache = featuredata[typ] or { } + -- we need to get rid of this slide ! + start = slide_node_list(head) -- slow (we can store tail because there's always a skip at the end): todo + while start do + local id = start.id + if id == glyph then +--~ if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) then + if start.subtype<256 and start.font == font and has_attribute(start,0,attr) then + for i=1,#subtables do + local lookupname = subtables[i] + local lookupcache = thecache[lookupname] + if lookupcache then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + start, success = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) + if success then + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + if start then start = start.prev end + else + start = start.prev + end + else + start = start.prev + end + end + else + local handler = handlers[typ] + local ns = #subtables + local thecache = featuredata[typ] or { } + start = head -- local ? + rlmode = 0 + if ns == 1 then + local lookupname = subtables[1] + local lookupcache = thecache[lookupname] + if not lookupcache then + report_missing_cache(typ,lookupname) + else + while start do + local id = start.id + if id == glyph then +--~ if start.font == font and start.subtype<256 and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then + if start.font == font and start.subtype<256 and has_attribute(start,0,attr) and (not attribute or has_attribute(start,state,attribute)) then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + -- sequence kan weg + local ok + start, ok = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,1) + if ok then + success = true + end + end + if start then start = start.next end + else + start = start.next + end + -- elseif id == glue then + -- if p[5] then -- chain + -- local pc = pp[32] + -- if pc then + -- start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) + -- if ok then + -- done = true + -- end + -- if start then start = start.next end + -- else + -- start = start.next + -- end + -- else + -- start = start.next + -- end + elseif id == whatsit then + local subtype = start.subtype + if subtype == 7 then + local dir = start.dir + if dir == "+TRT" then + rlmode = -1 + elseif dir == "+TLT" then + rlmode = 1 + else + rlmode = 0 + end + elseif subtype == 6 then + local dir = start.dir + if dir == "TRT" then + rlmode = -1 + elseif dir == "TLT" then + rlmode = 1 + else + rlmode = 0 + end + end + start = start.next + else + start = start.next + end + end + + end + else + while start do + local id = start.id + if id == glyph then +--~ if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then + if start.subtype<256 and start.font == font and has_attribute(start,0,attr) and (not attribute or has_attribute(start,state,attribute)) then + for i=1,ns do + local lookupname = subtables[i] + local lookupcache = thecache[lookupname] + if lookupcache then + local lookupmatch = lookupcache[start.char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local ok + start, ok = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) + if ok then + success = true + break + end + end + else + report_missing_cache(typ,lookupname) + end + end + if start then start = start.next end + else + start = start.next + end + -- elseif id == glue then + -- if p[5] then -- chain + -- local pc = pp[32] + -- if pc then + -- start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) + -- if ok then + -- done = true + -- end + -- if start then start = start.next end + -- else + -- start = start.next + -- end + -- else + -- start = start.next + -- end + elseif id == whatsit then + local subtype = start.subtype + if subtype == 7 then + local dir = start.dir + if dir == "+TRT" then + rlmode = -1 + elseif dir == "+TLT" then + rlmode = 1 + else + rlmode = 0 + end + elseif subtype == 6 then + local dir = start.dir + if dir == "TRT" then + rlmode = -1 + elseif dir == "TLT" then + rlmode = 1 + else + rlmode = 0 + end + end + start = start.next + else + start = start.next + end + end + end + end + if success then + done = true + end + if trace_steps then -- ? + registerstep(head) + end + end + end + return head, done +end + +otf.features.prepare = { } + +-- we used to share code in the following functions but that costs a lot of +-- memory due to extensive calls to functions (easily hundreds of thousands per +-- document) + +local function split(replacement,original,cache,unicodes) + -- we can cache this too, but not the same + local o, t, n = { }, { }, 0 + for s in gmatch(original,"[^ ]+") do + local us = unicodes[s] + if type(us) == "number" then + o[#o+1] = us + else + o[#o+1] = us[1] + end + end + for s in gmatch(replacement,"[^ ]+") do + n = n + 1 + local us = unicodes[s] + if type(us) == "number" then + t[o[n]] = us + else + t[o[n]] = us[1] + end + end + return t +end + +local function uncover(covers,result,cache,unicodes) + -- lpeg hardly faster (.005 sec on mk) + for n=1,#covers do + local c = covers[n] + local cc = cache[c] + if not cc then + local t = { } + for s in gmatch(c,"[^ ]+") do + local us = unicodes[s] + if type(us) == "number" then + t[us] = true + else + for i=1,#us do + t[us[i]] = true + end + end + end + cache[c] = t + result[#result+1] = t + else + result[#result+1] = cc + end + end +end + +local function prepare_lookups(tfmdata) + local otfdata = tfmdata.shared.otfdata + local featuredata = otfdata.shared.featuredata + local anchor_to_lookup = otfdata.luatex.anchor_to_lookup + local lookup_to_anchor = otfdata.luatex.lookup_to_anchor + -- + local multiple = featuredata.gsub_multiple + local alternate = featuredata.gsub_alternate + local single = featuredata.gsub_single + local ligature = featuredata.gsub_ligature + local pair = featuredata.gpos_pair + local position = featuredata.gpos_single + local kerns = featuredata.gpos_pair + local mark = featuredata.gpos_mark2mark + local cursive = featuredata.gpos_cursive + -- + local unicodes = tfmdata.unicodes -- names to unicodes + local indices = tfmdata.indices + local descriptions = tfmdata.descriptions + -- + -- we can change the otf table after loading but then we need to adapt base mode + -- as well (no big deal) + -- + for unicode, glyph in next, descriptions do + local lookups = glyph.lookups + if lookups then + for lookup, whatever in next, lookups do + for i=1,#whatever do -- normaly one + local p = whatever[i] + local what = p[1] + if what == 'substitution' then + local old, new = unicode, unicodes[p[2]] + if type(new) == "table" then + new = new[1] + end + local s = single[lookup] + if not s then s = { } single[lookup] = s end + s[old] = new +--~ if trace_lookups then +--~ logs.report("define otf","lookup %s: substitution %s => %s",lookup,old,new) +--~ end + break + elseif what == 'multiple' then + local old, new = unicode, { } + local m = multiple[lookup] + if not m then m = { } multiple[lookup] = m end + m[old] = new + for pc in gmatch(p[2],"[^ ]+") do + local upc = unicodes[pc] + if type(upc) == "number" then + new[#new+1] = upc + else + new[#new+1] = upc[1] + end + end +--~ if trace_lookups then +--~ logs.report("define otf","lookup %s: multiple %s => %s",lookup,old,concat(new," ")) +--~ end + break + elseif what == 'alternate' then + local old, new = unicode, { } + local a = alternate[lookup] + if not a then a = { } alternate[lookup] = a end + a[old] = new + for pc in gmatch(p[2],"[^ ]+") do + local upc = unicodes[pc] + if type(upc) == "number" then + new[#new+1] = upc + else + new[#new+1] = upc[1] + end + end +--~ if trace_lookups then +--~ logs.report("define otf","lookup %s: alternate %s => %s",lookup,old,concat(new,"|")) +--~ end + break + elseif what == "ligature" then +--~ if trace_lookups then +--~ logs.report("define otf","lookup %s: ligature %s => %s",lookup,p[2],glyph.name) +--~ end + local first = true + local t = ligature[lookup] + if not t then t = { } ligature[lookup] = t end + for s in gmatch(p[2],"[^ ]+") do + if first then + local u = unicodes[s] + if not u then + logs.report("define otf","lookup %s: ligature %s => %s ignored due to invalid unicode",lookup,p[2],glyph.name) + break + elseif type(u) == "number" then + if not t[u] then + t[u] = { { } } + end + t = t[u] + else + local tt = t + local tu + for i=1,#u do + local u = u[i] + if i==1 then + if not t[u] then + t[u] = { { } } + end + tu = t[u] + t = tu + else + if not t[u] then + tt[u] = tu + end + end + end + end + first = false + else + s = unicodes[s] + local t1 = t[1] + if not t1[s] then + t1[s] = { { } } + end + t = t1[s] + end + end + t[2] = unicode + elseif what == 'position' then + -- not used + local s = position[lookup] + if not s then s = { } position[lookup] = s end + s[unicode] = p[2] -- direct pointer to kern spec + elseif what == 'pair' then + local s = pair[lookup] + if not s then s = { } pair[lookup] = s end + local others = s[unicode] + if not others then others = { } s[unicode] = others end + -- todo: fast check for space + local two = p[2] + local upc = unicodes[two] + if not upc then + for pc in gmatch(two,"[^ ]+") do + local upc = unicodes[pc] + if type(upc) == "number" then + others[upc] = p -- direct pointer to main table + else + for i=1,#upc do + others[upc[i]] = p -- direct pointer to main table + end + end + end + elseif type(upc) == "number" then + others[upc] = p -- direct pointer to main table + else + for i=1,#upc do + others[upc[i]] = p -- direct pointer to main table + end + end +--~ if trace_lookups then +--~ logs.report("define otf","lookup %s: pair for U+%04X",lookup,unicode) +--~ end + end + end + end + end + local list = glyph.mykerns + if list then + for lookup, krn in next, list do + local k = kerns[lookup] + if not k then k = { } kerns[lookup] = k end + k[unicode] = krn -- ref to glyph, saves lookup +--~ if trace_lookups then +--~ logs.report("define otf","lookup %s: kern for U+%04X",lookup,unicode) +--~ end + end + end + local oanchor = glyph.anchors + if oanchor then + for typ, anchors in next, oanchor do -- types + if typ == "mark" then + for name, anchor in next, anchors do + local lookups = anchor_to_lookup[name] + if lookups then + for lookup, _ in next, lookups do + local f = mark[lookup] + if not f then f = { } mark[lookup] = f end + f[unicode] = anchors -- ref to glyph, saves lookup +--~ if trace_lookups then +--~ logs.report("define otf","lookup %s: mark anchor %s for U+%04X",lookup,name,unicode) +--~ end + end + end + end + elseif typ == "cexit" then -- or entry? + for name, anchor in next, anchors do + local lookups = anchor_to_lookup[name] + if lookups then + for lookup, _ in next, lookups do + local f = cursive[lookup] + if not f then f = { } cursive[lookup] = f end + f[unicode] = anchors -- ref to glyph, saves lookup +--~ if trace_lookups then +--~ logs.report("define otf","lookup %s: exit anchor %s for U+%04X",lookup,name,unicode) +--~ end + end + end + end + end + end + end + end +end + +-- local cache = { } +luatex = luatex or {} -- this has to change ... we need a better one + +function prepare_contextchains(tfmdata) + local otfdata = tfmdata.shared.otfdata + local lookups = otfdata.lookups + if lookups then + local featuredata = otfdata.shared.featuredata + local contextchain = featuredata.gsub_contextchain -- shared with gpos + local reversecontextchain = featuredata.gsub_reversecontextchain -- shared with gpos + local characters = tfmdata.characters + local unicodes = tfmdata.unicodes + local indices = tfmdata.indices + local cache = luatex.covers + if not cache then + cache = { } + luatex.covers = cache + end + -- + for lookupname, lookupdata in next, otfdata.lookups do + local lookuptype = lookupdata.type + if not lookuptype then + logs.report("otf process","missing lookuptype for %s",lookupname) + else + local rules = lookupdata.rules + if rules then + local fmt = lookupdata.format + -- contextchain[lookupname][unicode] + if fmt == "coverage" then + if lookuptype ~= "chainsub" and lookuptype ~= "chainpos" then + logs.report("otf process","unsupported coverage %s for %s",lookuptype,lookupname) + else + local contexts = contextchain[lookupname] + if not contexts then + contexts = { } + contextchain[lookupname] = contexts + end + local t = { } + for nofrules=1,#rules do -- does #rules>1 happen often? + local rule = rules[nofrules] + local coverage = rule.coverage + if coverage and coverage.current then + local current, before, after, sequence = coverage.current, coverage.before, coverage.after, { } + if before then + uncover(before,sequence,cache,unicodes) + end + local start = #sequence + 1 + uncover(current,sequence,cache,unicodes) + local stop = #sequence + if after then + uncover(after,sequence,cache,unicodes) + end + if sequence[1] then + t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } + for unic, _ in next, sequence[start] do + local cu = contexts[unic] + if not cu then + contexts[unic] = t + end + end + end + end + end + end + elseif fmt == "reversecoverage" then + if lookuptype ~= "reversesub" then + logs.report("otf process","unsupported reverse coverage %s for %s",lookuptype,lookupname) + else + local contexts = reversecontextchain[lookupname] + if not contexts then + contexts = { } + reversecontextchain[lookupname] = contexts + end + local t = { } + for nofrules=1,#rules do + local rule = rules[nofrules] + local reversecoverage = rule.reversecoverage + if reversecoverage and reversecoverage.current then + local current, before, after, replacements, sequence = reversecoverage.current, reversecoverage.before, reversecoverage.after, reversecoverage.replacements, { } + if before then + uncover(before,sequence,cache,unicodes) + end + local start = #sequence + 1 + uncover(current,sequence,cache,unicodes) + local stop = #sequence + if after then + uncover(after,sequence,cache,unicodes) + end + if replacements then + replacements = split(replacements,current[1],cache,unicodes) + end + if sequence[1] then + -- this is different from normal coverage, we assume only replacements + t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } + for unic, _ in next, sequence[start] do + local cu = contexts[unic] + if not cu then + contexts[unic] = t + end + end + end + end + end + end + end + end + end + end + end +end + +function fonts.initializers.node.otf.features(tfmdata,value) + if true then -- value then + if not tfmdata.shared.otfdata.shared.initialized then + local t = trace_preparing and os.clock() + local otfdata = tfmdata.shared.otfdata + local featuredata = otfdata.shared.featuredata + -- caches + featuredata.gsub_multiple = { } + featuredata.gsub_alternate = { } + featuredata.gsub_single = { } + featuredata.gsub_ligature = { } + featuredata.gsub_contextchain = { } + featuredata.gsub_reversecontextchain = { } + featuredata.gpos_pair = { } + featuredata.gpos_single = { } + featuredata.gpos_mark2base = { } + featuredata.gpos_mark2ligature = featuredata.gpos_mark2base + featuredata.gpos_mark2mark = featuredata.gpos_mark2base + featuredata.gpos_cursive = { } + featuredata.gpos_contextchain = featuredata.gsub_contextchain + featuredata.gpos_reversecontextchain = featuredata.gsub_reversecontextchain + -- + prepare_contextchains(tfmdata) + prepare_lookups(tfmdata) + otfdata.shared.initialized = true + if trace_preparing then + logs.report("otf process","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") + end + end + end +end diff --git a/tex/context/base/font-otp.lua b/tex/context/base/font-otp.lua new file mode 100644 index 000000000..3cdc80737 --- /dev/null +++ b/tex/context/base/font-otp.lua @@ -0,0 +1,420 @@ +if not modules then modules = { } end modules ['font-otp'] = { + version = 1.001, + comment = "companion to font-otf.lua (packing)", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- todo: pack math (but not that much to share) + +local next = next + +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) + +fonts = fonts or { } +fonts.otf = fonts.otf or { } +fonts.otf.enhancers = fonts.otf.enhancers or { } +fonts.otf.glists = fonts.otf.glists or { "gsub", "gpos" } + +local criterium, threshold, tabstr = 1, 0, table.serialize + +function fonts.otf.enhancers.pack(data) + if data then + local h, t, c = { }, { }, { } + local hh, tt, cc = { }, { }, { } + local function pack_1(v) + -- v == table + local tag = tabstr(v) + local ht = h[tag] + if not ht then + ht = #t+1 + t[ht] = v + h[tag] = ht + c[ht] = 1 + else + c[ht] = c[ht] + 1 + end + return ht + end + local function pack_2(v) + -- v == number + if c[v] <= criterium then + return t[v] + else + -- compact hash + local hv = hh[v] + if not hv then + hv = #tt+1 + tt[hv] = t[v] + hh[v] = hv + cc[hv] = c[v] + end + return hv + end + end + local function success(stage,pass) + if #t == 0 then + if trace_loading then + logs.report("load otf","pack quality: nothing to pack") + end + return false + elseif #t >= threshold then + local one, two, rest = 0, 0, 0 + if pass == 1 then + for k,v in next, c do + if v == 1 then + one = one + 1 + elseif v == 2 then + two = two + 1 + else + rest = rest + 1 + end + end + else + for k,v in next, cc do + if v >20 then + rest = rest + 1 + elseif v >10 then + two = two + 1 + else + one = one + 1 + end + end + data.tables = tt + end + if trace_loading then + logs.report("load otf","pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)", stage, pass, one+two+rest, one, two, rest, criterium) + end + return true + else + if trace_loading then + logs.report("load otf","pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)", stage, pass, #t, threshold) + end + return false + end + end + for pass=1,2 do + local pack = (pass == 1 and pack_1) or pack_2 + for k, v in next, data.glyphs do + v.boundingbox = pack(v.boundingbox) + local l = v.lookups + if l then + for k,v in next, l do + for kk=1,#v do + local vkk = v[kk] + local what = vkk[1] + if what == "pair" then + local t = vkk[3] if t then vkk[3] = pack(t) end + local t = vkk[4] if t then vkk[4] = pack(t) end + elseif what == "position" then + local t = vkk[2] if t then vkk[2] = pack(t) end + end + -- v[kk] = pack(vkk) + end + end + end + local m = v.mykerns + if m then + for k,v in next, m do + m[k] = pack(v) + end + end + local m = v.math + if m then + local mk = m.kerns + if mk then + for k,v in next, mk do + mk[k] = pack(v) + end + end + end + local a = v.anchors + if a then + for k,v in next, a do + if k == "baselig" then + for kk, vv in next, v do + for kkk=1,#vv do + vv[kkk] = pack(vv[kkk]) + end + end + else + for kk, vv in next, v do + v[kk] = pack(vv) + end + end + end + end + end + if data.lookups then + for k, v in next, data.lookups do + if v.rules then + for kk, vv in next, v.rules do + local l = vv.lookups + if l then + vv.lookups = pack(l) + end + local c = vv.coverage + if c then + c.before = c.before and pack(c.before ) + c.after = c.after and pack(c.after ) + c.current = c.current and pack(c.current) + end + local c = vv.reversecoverage + if c then + c.before = c.before and pack(c.before ) + c.after = c.after and pack(c.after ) + c.current = c.current and pack(c.current) + end + end + end + end + end + if data.luatex then + local la = data.luatex.anchor_to_lookup + if la then + for lookup, ldata in next, la do + la[lookup] = pack(ldata) + end + end + local la = data.luatex.lookup_to_anchor + if la then + for lookup, ldata in next, la do + la[lookup] = pack(ldata) + end + end + local ls = data.luatex.sequences + if ls then + for feature, fdata in next, ls do + local flags = fdata.flags + if flags then + fdata.flags = pack(flags) + end + local subtables = fdata.subtables + if subtables then + fdata.subtables = pack(subtables) + end + local features = fdata.features + if features then + for script, sdata in next, features do + features[script] = pack(sdata) + end + end + end + end + local ls = data.luatex.lookups + if ls then + for lookup, fdata in next, ls do + local flags = fdata.flags + if flags then + fdata.flags = pack(flags) + end + local subtables = fdata.subtables + if subtables then + fdata.subtables = pack(subtables) + end + end + end + local lf = data.luatex.features + if lf then + for _, g in next, fonts.otf.glists do + local gl = lf[g] + if gl then + for feature, spec in next, gl do + gl[feature] = pack(spec) + end + end + end + end + end + if not success(1,pass) then + return + end + end + if #t > 0 then + for pass=1,2 do + local pack = (pass == 1 and pack_1) or pack_2 + for k, v in next, data.glyphs do + local m = v.mykerns + if m then + v.mykerns = pack(m) + end + local m = v.math + if m then + local mk = m.kerns + if mk then + m.kerns = pack(mk) + end + end + local a = v.anchors + if a then + v.anchors = pack(a) + end + local l = v.lookups + if l then + for k,v in next, l do + for kk=1,#v do + v[kk] = pack(v[kk]) + end + end + end + end + local ls = data.luatex.sequences + if ls then + for feature, fdata in next, ls do + fdata.features = pack(fdata.features) + end + end + if not success(2,pass) then +--~ return + end + end + end + end +end + +function fonts.otf.enhancers.unpack(data) + if data then + local t = data.tables + if t then + for k, v in next, data.glyphs do + local tv = t[v.boundingbox] if tv then v.boundingbox = tv end + local l = v.lookups + if l then + for k,v in next, l do + for i=1,#v do + local tv = t[v[i]] if tv then v[i] = tv end + local vi = v[i] + local what = vi[1] + if what == "pair" then + local tv = t[vi[3]] if tv then vi[3] = tv end + local tv = t[vi[4]] if tv then vi[4] = tv end + elseif what == "position" then + local tv = t[vi[2]] if tv then vi[2] = tv end + end + end + end + end + local m = v.mykerns + if m then + local tv = t[m] if tv then m = tv v.mykerns = m end -- secondary optimization + for k,v in next, m do + local tv = t[v] if tv then m[k] = tv end + end + end + local m = v.math + if m then + local mk = m.kerns + if mk then + local tv = t[mk] if tv then mk = tv m.kerns = mk end -- secondary optimization + for k,v in next, mk do + local tv = t[v] if tv then mk[k] = tv end + end + end + end + local a = v.anchors + if a then + local tv = t[a] if tv then a = tv v.anchors = a end -- secondary optimization + for k,v in next, a do + if k == "baselig" then + for kk, vv in next, v do + for kkk=1,#vv do + local tv = t[vv[kkk]] if tv then vv[kkk] = tv end + end + end + else + for kk, vv in next, v do + local tv = t[vv] if tv then v[kk] = tv end + end + end + end + end + end + if data.lookups then + for k, v in next, data.lookups do + local r = v.rules + if r then + for kk, vv in next, r do + local l = vv.lookups + if l then + local tv = t[l] if tv then vv.lookups = tv end + end + local c = vv.coverage + if c then + local cc = c.before if cc then local tv = t[cc] if tv then c.before = tv end end + cc = c.after if cc then local tv = t[cc] if tv then c.after = tv end end + cc = c.current if cc then local tv = t[cc] if tv then c.current = tv end end + end + local c = vv.reversecoverage + if c then + local cc = c.before if cc then local tv = t[cc] if tv then c.before = tv end end + cc = c.after if cc then local tv = t[cc] if tv then c.after = tv end end + cc = c.current if cc then local tv = t[cc] if tv then c.current = tv end end + end + end + end + end + end + local luatex = data.luatex + if luatex then + local la = luatex.anchor_to_lookup + if la then + for lookup, ldata in next, la do + local tv = t[ldata] if tv then la[lookup] = tv end + end + end + local la = luatex.lookup_to_anchor + if la then + for lookup, ldata in next, la do + local tv = t[ldata] if tv then la[lookup] = tv end + end + end + local ls = luatex.sequences + if ls then + for feature, fdata in next, ls do + local flags = fdata.flags + if flags then + local tv = t[flags] if tv then fdata.flags = tv end + end + local subtables = fdata.subtables + if subtables then + local tv = t[subtables] if tv then fdata.subtables = tv end + end + local features = fdata.features + if features then + local tv = t[features] if tv then fdata.features = tv features = tv end -- secondary pack + for script, sdata in next, features do + local tv = t[sdata] if tv then features[script] = tv end + end + end + end + end + local ls = luatex.lookups + if ls then + for lookups, fdata in next, ls do + local flags = fdata.flags + if flags then + local tv = t[flags] if tv then fdata.flags = tv end + end + local subtables = fdata.subtables + if subtables then + local tv = t[subtables] if tv then fdata.subtables = tv end + end + end + end + local lf = luatex.features + if lf then + for _, g in next, fonts.otf.glists do + local gl = lf[g] + if gl then + for feature, spec in next, gl do + local tv = t[spec] if tv then gl[feature] = tv end + end + end + end + end + end + data.tables = nil + end + end +end diff --git a/tex/context/base/font-ott.lua b/tex/context/base/font-ott.lua new file mode 100644 index 000000000..6676ff64b --- /dev/null +++ b/tex/context/base/font-ott.lua @@ -0,0 +1,935 @@ +if not modules then modules = { } end modules ['font-otf'] = { + version = 1.001, + comment = "companion to font-otf.lua (tables)", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local type, next, tonumber, tostring = type, next, tonumber, tostring +local gsub, lower = string.gsub, string.lower + +fonts = fonts or { } +fonts.otf = fonts.otf or { } + +local otf = fonts.otf + +otf.tables = otf.tables or { } +otf.meanings = otf.meanings or { } + +otf.tables.scripts = { + ['dflt'] = 'Default', + + ['arab'] = 'Arabic', + ['armn'] = 'Armenian', + ['bali'] = 'Balinese', + ['beng'] = 'Bengali', + ['bopo'] = 'Bopomofo', + ['brai'] = 'Braille', + ['bugi'] = 'Buginese', + ['buhd'] = 'Buhid', + ['byzm'] = 'Byzantine Music', + ['cans'] = 'Canadian Syllabics', + ['cher'] = 'Cherokee', + ['copt'] = 'Coptic', + ['cprt'] = 'Cypriot Syllabary', + ['cyrl'] = 'Cyrillic', + ['deva'] = 'Devanagari', + ['dsrt'] = 'Deseret', + ['ethi'] = 'Ethiopic', + ['geor'] = 'Georgian', + ['glag'] = 'Glagolitic', + ['goth'] = 'Gothic', + ['grek'] = 'Greek', + ['gujr'] = 'Gujarati', + ['guru'] = 'Gurmukhi', + ['hang'] = 'Hangul', + ['hani'] = 'CJK Ideographic', + ['hano'] = 'Hanunoo', + ['hebr'] = 'Hebrew', + ['ital'] = 'Old Italic', + ['jamo'] = 'Hangul Jamo', + ['java'] = 'Javanese', + ['kana'] = 'Hiragana and Katakana', + ['khar'] = 'Kharosthi', + ['khmr'] = 'Khmer', + ['knda'] = 'Kannada', + ['lao' ] = 'Lao', + ['latn'] = 'Latin', + ['limb'] = 'Limbu', + ['linb'] = 'Linear B', + ['math'] = 'Mathematical Alphanumeric Symbols', + ['mlym'] = 'Malayalam', + ['mong'] = 'Mongolian', + ['musc'] = 'Musical Symbols', + ['mymr'] = 'Myanmar', + ['nko' ] = "N'ko", + ['ogam'] = 'Ogham', + ['orya'] = 'Oriya', + ['osma'] = 'Osmanya', + ['phag'] = 'Phags-pa', + ['phnx'] = 'Phoenician', + ['runr'] = 'Runic', + ['shaw'] = 'Shavian', + ['sinh'] = 'Sinhala', + ['sylo'] = 'Syloti Nagri', + ['syrc'] = 'Syriac', + ['tagb'] = 'Tagbanwa', + ['tale'] = 'Tai Le', + ['talu'] = 'Tai Lu', + ['taml'] = 'Tamil', + ['telu'] = 'Telugu', + ['tfng'] = 'Tifinagh', + ['tglg'] = 'Tagalog', + ['thaa'] = 'Thaana', + ['thai'] = 'Thai', + ['tibt'] = 'Tibetan', + ['ugar'] = 'Ugaritic Cuneiform', + ['xpeo'] = 'Old Persian Cuneiform', + ['xsux'] = 'Sumero-Akkadian Cuneiform', + ['yi' ] = 'Yi' +} + +otf.tables.languages = { + ['dflt'] = 'Default', + + ['aba'] = 'Abaza', + ['abk'] = 'Abkhazian', + ['ady'] = 'Adyghe', + ['afk'] = 'Afrikaans', + ['afr'] = 'Afar', + ['agw'] = 'Agaw', + ['als'] = 'Alsatian', + ['alt'] = 'Altai', + ['amh'] = 'Amharic', + ['ara'] = 'Arabic', + ['ari'] = 'Aari', + ['ark'] = 'Arakanese', + ['asm'] = 'Assamese', + ['ath'] = 'Athapaskan', + ['avr'] = 'Avar', + ['awa'] = 'Awadhi', + ['aym'] = 'Aymara', + ['aze'] = 'Azeri', + ['bad'] = 'Badaga', + ['bag'] = 'Baghelkhandi', + ['bal'] = 'Balkar', + ['bau'] = 'Baule', + ['bbr'] = 'Berber', + ['bch'] = 'Bench', + ['bcr'] = 'Bible Cree', + ['bel'] = 'Belarussian', + ['bem'] = 'Bemba', + ['ben'] = 'Bengali', + ['bgr'] = 'Bulgarian', + ['bhi'] = 'Bhili', + ['bho'] = 'Bhojpuri', + ['bik'] = 'Bikol', + ['bil'] = 'Bilen', + ['bkf'] = 'Blackfoot', + ['bli'] = 'Balochi', + ['bln'] = 'Balante', + ['blt'] = 'Balti', + ['bmb'] = 'Bambara', + ['bml'] = 'Bamileke', + ['bos'] = 'Bosnian', + ['bre'] = 'Breton', + ['brh'] = 'Brahui', + ['bri'] = 'Braj Bhasha', + ['brm'] = 'Burmese', + ['bsh'] = 'Bashkir', + ['bti'] = 'Beti', + ['cat'] = 'Catalan', + ['ceb'] = 'Cebuano', + ['che'] = 'Chechen', + ['chg'] = 'Chaha Gurage', + ['chh'] = 'Chattisgarhi', + ['chi'] = 'Chichewa', + ['chk'] = 'Chukchi', + ['chp'] = 'Chipewyan', + ['chr'] = 'Cherokee', + ['chu'] = 'Chuvash', + ['cmr'] = 'Comorian', + ['cop'] = 'Coptic', + ['cos'] = 'Corsican', + ['cre'] = 'Cree', + ['crr'] = 'Carrier', + ['crt'] = 'Crimean Tatar', + ['csl'] = 'Church Slavonic', + ['csy'] = 'Czech', + ['dan'] = 'Danish', + ['dar'] = 'Dargwa', + ['dcr'] = 'Woods Cree', + ['deu'] = 'German', + ['dgr'] = 'Dogri', + ['div'] = 'Divehi', + ['djr'] = 'Djerma', + ['dng'] = 'Dangme', + ['dnk'] = 'Dinka', + ['dri'] = 'Dari', + ['dun'] = 'Dungan', + ['dzn'] = 'Dzongkha', + ['ebi'] = 'Ebira', + ['ecr'] = 'Eastern Cree', + ['edo'] = 'Edo', + ['efi'] = 'Efik', + ['ell'] = 'Greek', + ['eng'] = 'English', + ['erz'] = 'Erzya', + ['esp'] = 'Spanish', + ['eti'] = 'Estonian', + ['euq'] = 'Basque', + ['evk'] = 'Evenki', + ['evn'] = 'Even', + ['ewe'] = 'Ewe', + ['fan'] = 'French Antillean', + ['far'] = 'Farsi', + ['fin'] = 'Finnish', + ['fji'] = 'Fijian', + ['fle'] = 'Flemish', + ['fne'] = 'Forest Nenets', + ['fon'] = 'Fon', + ['fos'] = 'Faroese', + ['fra'] = 'French', + ['fri'] = 'Frisian', + ['frl'] = 'Friulian', + ['fta'] = 'Futa', + ['ful'] = 'Fulani', + ['gad'] = 'Ga', + ['gae'] = 'Gaelic', + ['gag'] = 'Gagauz', + ['gal'] = 'Galician', + ['gar'] = 'Garshuni', + ['gaw'] = 'Garhwali', + ['gez'] = "Ge'ez", + ['gil'] = 'Gilyak', + ['gmz'] = 'Gumuz', + ['gon'] = 'Gondi', + ['grn'] = 'Greenlandic', + ['gro'] = 'Garo', + ['gua'] = 'Guarani', + ['guj'] = 'Gujarati', + ['hai'] = 'Haitian', + ['hal'] = 'Halam', + ['har'] = 'Harauti', + ['hau'] = 'Hausa', + ['haw'] = 'Hawaiin', + ['hbn'] = 'Hammer-Banna', + ['hil'] = 'Hiligaynon', + ['hin'] = 'Hindi', + ['hma'] = 'High Mari', + ['hnd'] = 'Hindko', + ['ho'] = 'Ho', + ['hri'] = 'Harari', + ['hrv'] = 'Croatian', + ['hun'] = 'Hungarian', + ['hye'] = 'Armenian', + ['ibo'] = 'Igbo', + ['ijo'] = 'Ijo', + ['ilo'] = 'Ilokano', + ['ind'] = 'Indonesian', + ['ing'] = 'Ingush', + ['inu'] = 'Inuktitut', + ['iri'] = 'Irish', + ['irt'] = 'Irish Traditional', + ['isl'] = 'Icelandic', + ['ism'] = 'Inari Sami', + ['ita'] = 'Italian', + ['iwr'] = 'Hebrew', + ['jan'] = 'Japanese', + ['jav'] = 'Javanese', + ['jii'] = 'Yiddish', + ['jud'] = 'Judezmo', + ['jul'] = 'Jula', + ['kab'] = 'Kabardian', + ['kac'] = 'Kachchi', + ['kal'] = 'Kalenjin', + ['kan'] = 'Kannada', + ['kar'] = 'Karachay', + ['kat'] = 'Georgian', + ['kaz'] = 'Kazakh', + ['keb'] = 'Kebena', + ['kge'] = 'Khutsuri Georgian', + ['kha'] = 'Khakass', + ['khk'] = 'Khanty-Kazim', + ['khm'] = 'Khmer', + ['khs'] = 'Khanty-Shurishkar', + ['khv'] = 'Khanty-Vakhi', + ['khw'] = 'Khowar', + ['kik'] = 'Kikuyu', + ['kir'] = 'Kirghiz', + ['kis'] = 'Kisii', + ['kkn'] = 'Kokni', + ['klm'] = 'Kalmyk', + ['kmb'] = 'Kamba', + ['kmn'] = 'Kumaoni', + ['kmo'] = 'Komo', + ['kms'] = 'Komso', + ['knr'] = 'Kanuri', + ['kod'] = 'Kodagu', + ['koh'] = 'Korean Old Hangul', + ['kok'] = 'Konkani', + ['kon'] = 'Kikongo', + ['kop'] = 'Komi-Permyak', + ['kor'] = 'Korean', + ['koz'] = 'Komi-Zyrian', + ['kpl'] = 'Kpelle', + ['kri'] = 'Krio', + ['krk'] = 'Karakalpak', + ['krl'] = 'Karelian', + ['krm'] = 'Karaim', + ['krn'] = 'Karen', + ['krt'] = 'Koorete', + ['ksh'] = 'Kashmiri', + ['ksi'] = 'Khasi', + ['ksm'] = 'Kildin Sami', + ['kui'] = 'Kui', + ['kul'] = 'Kulvi', + ['kum'] = 'Kumyk', + ['kur'] = 'Kurdish', + ['kuu'] = 'Kurukh', + ['kuy'] = 'Kuy', + ['kyk'] = 'Koryak', + ['lad'] = 'Ladin', + ['lah'] = 'Lahuli', + ['lak'] = 'Lak', + ['lam'] = 'Lambani', + ['lao'] = 'Lao', + ['lat'] = 'Latin', + ['laz'] = 'Laz', + ['lcr'] = 'L-Cree', + ['ldk'] = 'Ladakhi', + ['lez'] = 'Lezgi', + ['lin'] = 'Lingala', + ['lma'] = 'Low Mari', + ['lmb'] = 'Limbu', + ['lmw'] = 'Lomwe', + ['lsb'] = 'Lower Sorbian', + ['lsm'] = 'Lule Sami', + ['lth'] = 'Lithuanian', + ['ltz'] = 'Luxembourgish', + ['lub'] = 'Luba', + ['lug'] = 'Luganda', + ['luh'] = 'Luhya', + ['luo'] = 'Luo', + ['lvi'] = 'Latvian', + ['maj'] = 'Majang', + ['mak'] = 'Makua', + ['mal'] = 'Malayalam Traditional', + ['man'] = 'Mansi', + ['map'] = 'Mapudungun', + ['mar'] = 'Marathi', + ['maw'] = 'Marwari', + ['mbn'] = 'Mbundu', + ['mch'] = 'Manchu', + ['mcr'] = 'Moose Cree', + ['mde'] = 'Mende', + ['men'] = "Me'en", + ['miz'] = 'Mizo', + ['mkd'] = 'Macedonian', + ['mle'] = 'Male', + ['mlg'] = 'Malagasy', + ['mln'] = 'Malinke', + ['mlr'] = 'Malayalam Reformed', + ['mly'] = 'Malay', + ['mnd'] = 'Mandinka', + ['mng'] = 'Mongolian', + ['mni'] = 'Manipuri', + ['mnk'] = 'Maninka', + ['mnx'] = 'Manx Gaelic', + ['moh'] = 'Mohawk', + ['mok'] = 'Moksha', + ['mol'] = 'Moldavian', + ['mon'] = 'Mon', + ['mor'] = 'Moroccan', + ['mri'] = 'Maori', + ['mth'] = 'Maithili', + ['mts'] = 'Maltese', + ['mun'] = 'Mundari', + ['nag'] = 'Naga-Assamese', + ['nan'] = 'Nanai', + ['nas'] = 'Naskapi', + ['ncr'] = 'N-Cree', + ['ndb'] = 'Ndebele', + ['ndg'] = 'Ndonga', + ['nep'] = 'Nepali', + ['new'] = 'Newari', + ['ngr'] = 'Nagari', + ['nhc'] = 'Norway House Cree', + ['nis'] = 'Nisi', + ['niu'] = 'Niuean', + ['nkl'] = 'Nkole', + ['nko'] = "N'ko", + ['nld'] = 'Dutch', + ['nog'] = 'Nogai', + ['nor'] = 'Norwegian', + ['nsm'] = 'Northern Sami', + ['nta'] = 'Northern Tai', + ['nto'] = 'Esperanto', + ['nyn'] = 'Nynorsk', + ['oci'] = 'Occitan', + ['ocr'] = 'Oji-Cree', + ['ojb'] = 'Ojibway', + ['ori'] = 'Oriya', + ['oro'] = 'Oromo', + ['oss'] = 'Ossetian', + ['paa'] = 'Palestinian Aramaic', + ['pal'] = 'Pali', + ['pan'] = 'Punjabi', + ['pap'] = 'Palpa', + ['pas'] = 'Pashto', + ['pgr'] = 'Polytonic Greek', + ['pil'] = 'Pilipino', + ['plg'] = 'Palaung', + ['plk'] = 'Polish', + ['pro'] = 'Provencal', + ['ptg'] = 'Portuguese', + ['qin'] = 'Chin', + ['raj'] = 'Rajasthani', + ['rbu'] = 'Russian Buriat', + ['rcr'] = 'R-Cree', + ['ria'] = 'Riang', + ['rms'] = 'Rhaeto-Romanic', + ['rom'] = 'Romanian', + ['roy'] = 'Romany', + ['rsy'] = 'Rusyn', + ['rua'] = 'Ruanda', + ['rus'] = 'Russian', + ['sad'] = 'Sadri', + ['san'] = 'Sanskrit', + ['sat'] = 'Santali', + ['say'] = 'Sayisi', + ['sek'] = 'Sekota', + ['sel'] = 'Selkup', + ['sgo'] = 'Sango', + ['shn'] = 'Shan', + ['sib'] = 'Sibe', + ['sid'] = 'Sidamo', + ['sig'] = 'Silte Gurage', + ['sks'] = 'Skolt Sami', + ['sky'] = 'Slovak', + ['sla'] = 'Slavey', + ['slv'] = 'Slovenian', + ['sml'] = 'Somali', + ['smo'] = 'Samoan', + ['sna'] = 'Sena', + ['snd'] = 'Sindhi', + ['snh'] = 'Sinhalese', + ['snk'] = 'Soninke', + ['sog'] = 'Sodo Gurage', + ['sot'] = 'Sotho', + ['sqi'] = 'Albanian', + ['srb'] = 'Serbian', + ['srk'] = 'Saraiki', + ['srr'] = 'Serer', + ['ssl'] = 'South Slavey', + ['ssm'] = 'Southern Sami', + ['sur'] = 'Suri', + ['sva'] = 'Svan', + ['sve'] = 'Swedish', + ['swa'] = 'Swadaya Aramaic', + ['swk'] = 'Swahili', + ['swz'] = 'Swazi', + ['sxt'] = 'Sutu', + ['syr'] = 'Syriac', + ['tab'] = 'Tabasaran', + ['taj'] = 'Tajiki', + ['tam'] = 'Tamil', + ['tat'] = 'Tatar', + ['tcr'] = 'TH-Cree', + ['tel'] = 'Telugu', + ['tgn'] = 'Tongan', + ['tgr'] = 'Tigre', + ['tgy'] = 'Tigrinya', + ['tha'] = 'Thai', + ['tht'] = 'Tahitian', + ['tib'] = 'Tibetan', + ['tkm'] = 'Turkmen', + ['tmn'] = 'Temne', + ['tna'] = 'Tswana', + ['tne'] = 'Tundra Nenets', + ['tng'] = 'Tonga', + ['tod'] = 'Todo', + ['trk'] = 'Turkish', + ['tsg'] = 'Tsonga', + ['tua'] = 'Turoyo Aramaic', + ['tul'] = 'Tulu', + ['tuv'] = 'Tuvin', + ['twi'] = 'Twi', + ['udm'] = 'Udmurt', + ['ukr'] = 'Ukrainian', + ['urd'] = 'Urdu', + ['usb'] = 'Upper Sorbian', + ['uyg'] = 'Uyghur', + ['uzb'] = 'Uzbek', + ['ven'] = 'Venda', + ['vit'] = 'Vietnamese', + ['wa' ] = 'Wa', + ['wag'] = 'Wagdi', + ['wcr'] = 'West-Cree', + ['wel'] = 'Welsh', + ['wlf'] = 'Wolof', + ['xbd'] = 'Tai Lue', + ['xhs'] = 'Xhosa', + ['yak'] = 'Yakut', + ['yba'] = 'Yoruba', + ['ycr'] = 'Y-Cree', + ['yic'] = 'Yi Classic', + ['yim'] = 'Yi Modern', + ['zhh'] = 'Chinese Hong Kong', + ['zhp'] = 'Chinese Phonetic', + ['zhs'] = 'Chinese Simplified', + ['zht'] = 'Chinese Traditional', + ['znd'] = 'Zande', + ['zul'] = 'Zulu' +} + +otf.tables.features = { + ['aalt'] = 'Access All Alternates', + ['abvf'] = 'Above-Base Forms', + ['abvm'] = 'Above-Base Mark Positioning', + ['abvs'] = 'Above-Base Substitutions', + ['afrc'] = 'Alternative Fractions', + ['akhn'] = 'Akhands', + ['blwf'] = 'Below-Base Forms', + ['blwm'] = 'Below-Base Mark Positioning', + ['blws'] = 'Below-Base Substitutions', + ['c2pc'] = 'Petite Capitals From Capitals', + ['c2sc'] = 'Small Capitals From Capitals', + ['calt'] = 'Contextual Alternates', + ['case'] = 'Case-Sensitive Forms', + ['ccmp'] = 'Glyph Composition/Decomposition', + ['cjct'] = 'Conjunct Forms', + ['clig'] = 'Contextual Ligatures', + ['cpsp'] = 'Capital Spacing', + ['cswh'] = 'Contextual Swash', + ['curs'] = 'Cursive Positioning', + ['dflt'] = 'Default Processing', + ['dist'] = 'Distances', + ['dlig'] = 'Discretionary Ligatures', + ['dnom'] = 'Denominators', + ['dtls'] = 'Dotless Forms', -- math + ['expt'] = 'Expert Forms', + ['falt'] = 'Final glyph Alternates', + ['fin2'] = 'Terminal Forms #2', + ['fin3'] = 'Terminal Forms #3', + ['fina'] = 'Terminal Forms', + ['flac'] = 'Flattened Accents Over Capitals', -- math + ['frac'] = 'Fractions', + ['fwid'] = 'Full Width', + ['half'] = 'Half Forms', + ['haln'] = 'Halant Forms', + ['halt'] = 'Alternate Half Width', + ['hist'] = 'Historical Forms', + ['hkna'] = 'Horizontal Kana Alternates', + ['hlig'] = 'Historical Ligatures', + ['hngl'] = 'Hangul', + ['hojo'] = 'Hojo Kanji Forms', + ['hwid'] = 'Half Width', + ['init'] = 'Initial Forms', + ['isol'] = 'Isolated Forms', + ['ital'] = 'Italics', + ['jalt'] = 'Justification Alternatives', + ['jp04'] = 'JIS2004 Forms', + ['jp78'] = 'JIS78 Forms', + ['jp83'] = 'JIS83 Forms', + ['jp90'] = 'JIS90 Forms', + ['kern'] = 'Kerning', + ['lfbd'] = 'Left Bounds', + ['liga'] = 'Standard Ligatures', + ['ljmo'] = 'Leading Jamo Forms', + ['lnum'] = 'Lining Figures', + ['locl'] = 'Localized Forms', + ['mark'] = 'Mark Positioning', + ['med2'] = 'Medial Forms #2', + ['medi'] = 'Medial Forms', + ['mgrk'] = 'Mathematical Greek', + ['mkmk'] = 'Mark to Mark Positioning', + ['mset'] = 'Mark Positioning via Substitution', + ['nalt'] = 'Alternate Annotation Forms', + ['nlck'] = 'NLC Kanji Forms', + ['nukt'] = 'Nukta Forms', + ['numr'] = 'Numerators', + ['onum'] = 'Old Style Figures', + ['opbd'] = 'Optical Bounds', + ['ordn'] = 'Ordinals', + ['ornm'] = 'Ornaments', + ['palt'] = 'Proportional Alternate Width', + ['pcap'] = 'Petite Capitals', + ['pnum'] = 'Proportional Figures', + ['pref'] = 'Pre-base Forms', + ['pres'] = 'Pre-base Substitutions', + ['pstf'] = 'Post-base Forms', + ['psts'] = 'Post-base Substitutions', + ['pwid'] = 'Proportional Widths', + ['qwid'] = 'Quarter Widths', + ['rand'] = 'Randomize', + ['rkrf'] = 'Rakar Forms', + ['rlig'] = 'Required Ligatures', + ['rphf'] = 'Reph Form', + ['rtbd'] = 'Right Bounds', + ['rtla'] = 'Right-To-Left Alternates', + ['ruby'] = 'Ruby Notation Forms', + ['salt'] = 'Stylistic Alternates', + ['sinf'] = 'Scientific Inferiors', + ['size'] = 'Optical Size', + ['smcp'] = 'Small Capitals', + ['smpl'] = 'Simplified Forms', + ['ss01'] = 'Stylistic Set 1', + ['ss02'] = 'Stylistic Set 2', + ['ss03'] = 'Stylistic Set 3', + ['ss04'] = 'Stylistic Set 4', + ['ss05'] = 'Stylistic Set 5', + ['ss06'] = 'Stylistic Set 6', + ['ss07'] = 'Stylistic Set 7', + ['ss08'] = 'Stylistic Set 8', + ['ss09'] = 'Stylistic Set 9', + ['ss10'] = 'Stylistic Set 10', + ['ss11'] = 'Stylistic Set 11', + ['ss12'] = 'Stylistic Set 12', + ['ss13'] = 'Stylistic Set 13', + ['ss14'] = 'Stylistic Set 14', + ['ss15'] = 'Stylistic Set 15', + ['ss16'] = 'Stylistic Set 16', + ['ss17'] = 'Stylistic Set 17', + ['ss18'] = 'Stylistic Set 18', + ['ss19'] = 'Stylistic Set 19', + ['ss20'] = 'Stylistic Set 20', + ['ssty'] = 'Script Style', -- math + ['subs'] = 'Subscript', + ['sups'] = 'Superscript', + ['swsh'] = 'Swash', + ['titl'] = 'Titling', + ['tjmo'] = 'Trailing Jamo Forms', + ['tnam'] = 'Traditional Name Forms', + ['tnum'] = 'Tabular Figures', + ['trad'] = 'Traditional Forms', + ['twid'] = 'Third Widths', + ['unic'] = 'Unicase', + ['valt'] = 'Alternate Vertical Metrics', + ['vatu'] = 'Vattu Variants', + ['vert'] = 'Vertical Writing', + ['vhal'] = 'Alternate Vertical Half Metrics', + ['vjmo'] = 'Vowel Jamo Forms', + ['vkna'] = 'Vertical Kana Alternates', + ['vkrn'] = 'Vertical Kerning', + ['vpal'] = 'Proportional Alternate Vertical Metrics', + ['vrt2'] = 'Vertical Rotation', + ['zero'] = 'Slashed Zero', + + ['trep'] = 'Traditional TeX Replacements', + ['tlig'] = 'Traditional TeX Ligatures', +} + +otf.tables.baselines = { + ['hang'] = 'Hanging baseline', + ['icfb'] = 'Ideographic character face bottom edge baseline', + ['icft'] = 'Ideographic character face tope edige baseline', + ['ideo'] = 'Ideographic em-box bottom edge baseline', + ['idtp'] = 'Ideographic em-box top edge baseline', + ['math'] = 'Mathmatical centered baseline', + ['romn'] = 'Roman baseline' +} + +-- can be sped up by local tables + +function otf.tables.to_tag(id) + return stringformat("%4s",lower(id)) +end + +local function resolve(tab,id) + if tab and id then + id = lower(id) + return tab[id] or tab[gsub(id," ","")] or tab['dflt'] or '' + else + return "unknown" + end +end + +function otf.meanings.script(id) + return resolve(otf.tables.scripts,id) +end +function otf.meanings.language(id) + return resolve(otf.tables.languages,id) +end +function otf.meanings.feature(id) + return resolve(otf.tables.features,id) +end +function otf.meanings.baseline(id) + return resolve(otf.tables.baselines,id) +end + +otf.tables.to_scripts = table.reverse_hash(otf.tables.scripts ) +otf.tables.to_languages = table.reverse_hash(otf.tables.languages) +otf.tables.to_features = table.reverse_hash(otf.tables.features ) + +local scripts = otf.tables.scripts +local languages = otf.tables.languages +local features = otf.tables.features + +local to_scripts = otf.tables.to_scripts +local to_languages = otf.tables.to_languages +local to_features = otf.tables.to_features + +function otf.meanings.normalize(features) + local h = { } + for k,v in next, features do + k = lower(k) + if k == "language" or k == "lang" then + v = gsub(lower(v),"[^a-z0-9%-]","") + k = language + if not languages[v] then + h.language = to_languages[v] or "dflt" + else + h.language = v + end + elseif k == "script" then + v = gsub(lower(v),"[^a-z0-9%-]","") + if not scripts[v] then + h.script = to_scripts[v] or "dflt" + else + h.script = v + end + else + if type(v) == "string" then + local b = v:is_boolean() + if type(b) == "nil" then + v = tonumber(v) or lower(v) + else + v = b + end + end + h[to_features[k] or k] = v + end + end + return h +end + +-- When I feel the need ... + +--~ otf.tables.aat = { +--~ [ 0] = { +--~ name = "allTypographicFeaturesType", +--~ [ 0] = "allTypeFeaturesOnSelector", +--~ [ 1] = "allTypeFeaturesOffSelector", +--~ }, +--~ [ 1] = { +--~ name = "ligaturesType", +--~ [0 ] = "requiredLigaturesOnSelector", +--~ [1 ] = "requiredLigaturesOffSelector", +--~ [2 ] = "commonLigaturesOnSelector", +--~ [3 ] = "commonLigaturesOffSelector", +--~ [4 ] = "rareLigaturesOnSelector", +--~ [5 ] = "rareLigaturesOffSelector", +--~ [6 ] = "logosOnSelector ", +--~ [7 ] = "logosOffSelector ", +--~ [8 ] = "rebusPicturesOnSelector", +--~ [9 ] = "rebusPicturesOffSelector", +--~ [10] = "diphthongLigaturesOnSelector", +--~ [11] = "diphthongLigaturesOffSelector", +--~ [12] = "squaredLigaturesOnSelector", +--~ [13] = "squaredLigaturesOffSelector", +--~ [14] = "abbrevSquaredLigaturesOnSelector", +--~ [15] = "abbrevSquaredLigaturesOffSelector", +--~ }, +--~ [ 2] = { +--~ name = "cursiveConnectionType", +--~ [ 0] = "unconnectedSelector", +--~ [ 1] = "partiallyConnectedSelector", +--~ [ 2] = "cursiveSelector ", +--~ }, +--~ [ 3] = { +--~ name = "letterCaseType", +--~ [ 0] = "upperAndLowerCaseSelector", +--~ [ 1] = "allCapsSelector ", +--~ [ 2] = "allLowerCaseSelector", +--~ [ 3] = "smallCapsSelector ", +--~ [ 4] = "initialCapsSelector", +--~ [ 5] = "initialCapsAndSmallCapsSelector", +--~ }, +--~ [ 4] = { +--~ name = "verticalSubstitutionType", +--~ [ 0] = "substituteVerticalFormsOnSelector", +--~ [ 1] = "substituteVerticalFormsOffSelector", +--~ }, +--~ [ 5] = { +--~ name = "linguisticRearrangementType", +--~ [ 0] = "linguisticRearrangementOnSelector", +--~ [ 1] = "linguisticRearrangementOffSelector", +--~ }, +--~ [ 6] = { +--~ name = "numberSpacingType", +--~ [ 0] = "monospacedNumbersSelector", +--~ [ 1] = "proportionalNumbersSelector", +--~ }, +--~ [ 7] = { +--~ name = "appleReserved1Type", +--~ }, +--~ [ 8] = { +--~ name = "smartSwashType", +--~ [ 0] = "wordInitialSwashesOnSelector", +--~ [ 1] = "wordInitialSwashesOffSelector", +--~ [ 2] = "wordFinalSwashesOnSelector", +--~ [ 3] = "wordFinalSwashesOffSelector", +--~ [ 4] = "lineInitialSwashesOnSelector", +--~ [ 5] = "lineInitialSwashesOffSelector", +--~ [ 6] = "lineFinalSwashesOnSelector", +--~ [ 7] = "lineFinalSwashesOffSelector", +--~ [ 8] = "nonFinalSwashesOnSelector", +--~ [ 9] = "nonFinalSwashesOffSelector", +--~ }, +--~ [ 9] = { +--~ name = "diacriticsType", +--~ [ 0] = "showDiacriticsSelector", +--~ [ 1] = "hideDiacriticsSelector", +--~ [ 2] = "decomposeDiacriticsSelector", +--~ }, +--~ [10] = { +--~ name = "verticalPositionType", +--~ [ 0] = "normalPositionSelector", +--~ [ 1] = "superiorsSelector ", +--~ [ 2] = "inferiorsSelector ", +--~ [ 3] = "ordinalsSelector ", +--~ }, +--~ [11] = { +--~ name = "fractionsType", +--~ [ 0] = "noFractionsSelector", +--~ [ 1] = "verticalFractionsSelector", +--~ [ 2] = "diagonalFractionsSelector", +--~ }, +--~ [12] = { +--~ name = "appleReserved2Type", +--~ }, +--~ [13] = { +--~ name = "overlappingCharactersType", +--~ [ 0] = "preventOverlapOnSelector", +--~ [ 1] = "preventOverlapOffSelector", +--~ }, +--~ [14] = { +--~ name = "typographicExtrasType", +--~ [0 ] = "hyphensToEmDashOnSelector", +--~ [1 ] = "hyphensToEmDashOffSelector", +--~ [2 ] = "hyphenToEnDashOnSelector", +--~ [3 ] = "hyphenToEnDashOffSelector", +--~ [4 ] = "unslashedZeroOnSelector", +--~ [5 ] = "unslashedZeroOffSelector", +--~ [6 ] = "formInterrobangOnSelector", +--~ [7 ] = "formInterrobangOffSelector", +--~ [8 ] = "smartQuotesOnSelector", +--~ [9 ] = "smartQuotesOffSelector", +--~ [10] = "periodsToEllipsisOnSelector", +--~ [11] = "periodsToEllipsisOffSelector", +--~ }, +--~ [15] = { +--~ name = "mathematicalExtrasType", +--~ [ 0] = "hyphenToMinusOnSelector", +--~ [ 1] = "hyphenToMinusOffSelector", +--~ [ 2] = "asteriskToMultiplyOnSelector", +--~ [ 3] = "asteriskToMultiplyOffSelector", +--~ [ 4] = "slashToDivideOnSelector", +--~ [ 5] = "slashToDivideOffSelector", +--~ [ 6] = "inequalityLigaturesOnSelector", +--~ [ 7] = "inequalityLigaturesOffSelector", +--~ [ 8] = "exponentsOnSelector", +--~ [ 9] = "exponentsOffSelector", +--~ }, +--~ [16] = { +--~ name = "ornamentSetsType", +--~ [ 0] = "noOrnamentsSelector", +--~ [ 1] = "dingbatsSelector ", +--~ [ 2] = "piCharactersSelector", +--~ [ 3] = "fleuronsSelector ", +--~ [ 4] = "decorativeBordersSelector", +--~ [ 5] = "internationalSymbolsSelector", +--~ [ 6] = "mathSymbolsSelector", +--~ }, +--~ [17] = { +--~ name = "characterAlternativesType", +--~ [ 0] = "noAlternatesSelector", +--~ }, +--~ [18] = { +--~ name = "designComplexityType", +--~ [ 0] = "designLevel1Selector", +--~ [ 1] = "designLevel2Selector", +--~ [ 2] = "designLevel3Selector", +--~ [ 3] = "designLevel4Selector", +--~ [ 4] = "designLevel5Selector", +--~ }, +--~ [19] = { +--~ name = "styleOptionsType", +--~ [ 0] = "noStyleOptionsSelector", +--~ [ 1] = "displayTextSelector", +--~ [ 2] = "engravedTextSelector", +--~ [ 3] = "illuminatedCapsSelector", +--~ [ 4] = "titlingCapsSelector", +--~ [ 5] = "tallCapsSelector ", +--~ }, +--~ [20] = { +--~ name = "characterShapeType", +--~ [0 ] = "traditionalCharactersSelector", +--~ [1 ] = "simplifiedCharactersSelector", +--~ [2 ] = "jis1978CharactersSelector", +--~ [3 ] = "jis1983CharactersSelector", +--~ [4 ] = "jis1990CharactersSelector", +--~ [5 ] = "traditionalAltOneSelector", +--~ [6 ] = "traditionalAltTwoSelector", +--~ [7 ] = "traditionalAltThreeSelector", +--~ [8 ] = "traditionalAltFourSelector", +--~ [9 ] = "traditionalAltFiveSelector", +--~ [10] = "expertCharactersSelector", +--~ }, +--~ [21] = { +--~ name = "numberCaseType", +--~ [ 0] = "lowerCaseNumbersSelector", +--~ [ 1] = "upperCaseNumbersSelector", +--~ }, +--~ [22] = { +--~ name = "textSpacingType", +--~ [ 0] = "proportionalTextSelector", +--~ [ 1] = "monospacedTextSelector", +--~ [ 2] = "halfWidthTextSelector", +--~ [ 3] = "normallySpacedTextSelector", +--~ }, +--~ [23] = { +--~ name = "transliterationType", +--~ [ 0] = "noTransliterationSelector", +--~ [ 1] = "hanjaToHangulSelector", +--~ [ 2] = "hiraganaToKatakanaSelector", +--~ [ 3] = "katakanaToHiraganaSelector", +--~ [ 4] = "kanaToRomanizationSelector", +--~ [ 5] = "romanizationToHiraganaSelector", +--~ [ 6] = "romanizationToKatakanaSelector", +--~ [ 7] = "hanjaToHangulAltOneSelector", +--~ [ 8] = "hanjaToHangulAltTwoSelector", +--~ [ 9] = "hanjaToHangulAltThreeSelector", +--~ }, +--~ [24] = { +--~ name = "annotationType", +--~ [ 0] = "noAnnotationSelector", +--~ [ 1] = "boxAnnotationSelector", +--~ [ 2] = "roundedBoxAnnotationSelector", +--~ [ 3] = "circleAnnotationSelector", +--~ [ 4] = "invertedCircleAnnotationSelector", +--~ [ 5] = "parenthesisAnnotationSelector", +--~ [ 6] = "periodAnnotationSelector", +--~ [ 7] = "romanNumeralAnnotationSelector", +--~ [ 8] = "diamondAnnotationSelector", +--~ }, +--~ [25] = { +--~ name = "kanaSpacingType", +--~ [ 0] = "fullWidthKanaSelector", +--~ [ 1] = "proportionalKanaSelector", +--~ }, +--~ [26] = { +--~ name = "ideographicSpacingType", +--~ [ 0] = "fullWidthIdeographsSelector", +--~ [ 1] = "proportionalIdeographsSelector", +--~ }, +--~ [103] = { +--~ name = "cjkRomanSpacingType", +--~ [ 0] = "halfWidthCJKRomanSelector", +--~ [ 1] = "proportionalCJKRomanSelector", +--~ [ 2] = "defaultCJKRomanSelector", +--~ [ 3] = "fullWidthCJKRomanSelector", +--~ }, +--~ } diff --git a/tex/context/base/font-pat.lua b/tex/context/base/font-pat.lua index 8f1817ec2..ae91700a9 100644 --- a/tex/context/base/font-pat.lua +++ b/tex/context/base/font-pat.lua @@ -6,19 +6,56 @@ if not modules then modules = { } end modules ['font-pat'] = { license = "see context related readme files" } +local match, lower = string.match, string.lower + +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) + -- older versions of latin modern didn't have the designsize set -- so for them we get it from the name -local patches = fonts.otf.enhance.patches +local patches = fonts.otf.enhancers.patches local function patch(data,filename) if data.design_size == 0 then - local ds = (file.basename(filename:lower())):match("(%d+)") + local ds = match(file.basename(lower(filename)),"(%d+)") if ds then - logs.report("load otf","patching design size (%s)",ds) + if trace_loading then + logs.report("load otf","patching design size (%s)",ds) + end data.design_size = tonumber(ds) * 10 end end + local uni_to_ind = data.map.map + if not uni_to_ind[0x391] then + -- beware, this is a hack, features for latin often don't apply to greek + -- but lm has not much features anyway (and only greek for math) + if trace_loading then + logs.report("load otf","adding 13 greek capitals") + end + uni_to_ind[0x391] = uni_to_ind[0x41] + uni_to_ind[0x392] = uni_to_ind[0x42] + uni_to_ind[0x395] = uni_to_ind[0x45] + uni_to_ind[0x397] = uni_to_ind[0x48] + uni_to_ind[0x399] = uni_to_ind[0x49] + uni_to_ind[0x39A] = uni_to_ind[0x4B] + uni_to_ind[0x39C] = uni_to_ind[0x4D] + uni_to_ind[0x39D] = uni_to_ind[0x4E] + uni_to_ind[0x39F] = uni_to_ind[0x4F] + uni_to_ind[0x3A1] = uni_to_ind[0x52] + uni_to_ind[0x3A4] = uni_to_ind[0x54] + uni_to_ind[0x3A7] = uni_to_ind[0x58] + uni_to_ind[0x396] = uni_to_ind[0x5A] + end + -- better make this into a feature + -- + -- local glyphs = data.glyphs + -- for i=0x300,0x36F do + -- local c = glyphs[uni_to_ind[i]] + -- if c and c.width == 0 then + -- local boundingbox = c.boundingbox + -- c.width = boundingbox[3] - boundingbox[1] + -- end + -- end end patches["^lmroman"] = patch @@ -30,10 +67,14 @@ patches["^lmtypewriter"] = patch -- have the mkmk features properly set up local function patch(data,filename) - if data.gpos then - for _, v in ipairs(data.gpos) do + local gpos = data.gpos + if gpos then + for k=1,#gpos do + local v = gpos[k] if not v.features and v.type == "gpos_mark2mark" then - logs.report("load otf","patching mkmk feature (name: %s)", v.name or "?") + if trace_loading then + logs.report("load otf","patching mkmk feature (name: %s)", v.name or "?") + end v.features = { { scripts = { diff --git a/tex/context/base/font-run.tex b/tex/context/base/font-run.tex index 83da04b62..0a0ddd057 100644 --- a/tex/context/base/font-run.tex +++ b/tex/context/base/font-run.tex @@ -213,8 +213,7 @@ \processcommalist[#2]\docommand \egroup \else\ifsecondargument - \showfontstyle[#1][#2][\alternativetextlist]% - \doif{#2}{\c!mm}{\showfontstyle[#1][#2][\alternativemathlist]}% + \showfontstyle[#1][#2][\fontalternativelist]% math is gone \else \showfontstyle[#1][\c!rm]\showfontstyle[#1][\c!ss] \showfontstyle[#1][\c!tt]\showfontstyle[#1][\c!mm] diff --git a/tex/context/base/font-syn.lua b/tex/context/base/font-syn.lua index 70f859cde..2f4bebbaf 100644 --- a/tex/context/base/font-syn.lua +++ b/tex/context/base/font-syn.lua @@ -6,53 +6,62 @@ if not modules then modules = { } end modules ['font-syn'] = { license = "see context related readme files" } +local next = next +local gsub, lower, match, find, lower, upper = string.gsub, string.lower, string.match, string.find, string.lower, string.upper + +local trace_names = false trackers.register("fonts.names", function(v) trace_names = v end) + --[[ldx--This module implements a name to filename resolver. Names are resolved using a table that has keys filtered from the font related files.
--ldx]]-- -local texsprint = tex.sprint +local texsprint = (tex and tex.sprint) or print fonts = fonts or { } input = input or { } texmf = texmf or { } -fonts.names = { } -fonts.names.filters = { } -fonts.names.data = { } -fonts.names.version = 1.07 -fonts.names.saved = false -fonts.names.loaded = false -fonts.names.be_clever = true -fonts.names.enabled = true -fonts.names.autoreload = toboolean(os.env['MTX.FONTS.AUTOLOAD'] or os.env['MTX_FONTS_AUTOLOAD'] or "no") -fonts.names.cache = containers.define("fonts","data",fonts.names.version,true) -fonts.names.trace = false +fonts.names = fonts.names or { } +fonts.names.filters = fonts.names.filters or { } +fonts.names.data = fonts.names.data or { } + +local names = fonts.names +local filters = fonts.names.filters + +names.version = 1.08 -- when adapting this, also changed font-dum.lua +names.basename = "names" +names.saved = false +names.loaded = false +names.be_clever = true +names.enabled = true +names.autoreload = toboolean(os.env['MTX.FONTS.AUTOLOAD'] or os.env['MTX_FONTS_AUTOLOAD'] or "no") +names.cache = containers.define("fonts","data",names.version,true) --[[ldx--It would make sense to implement the filters in the related modules, but to keep the overview, we define them here.
--ldx]]-- -fonts.names.filters.otf = fontforge.info -fonts.names.filters.ttf = fontforge.info -fonts.names.filters.ttc = fontforge.info +filters.otf = fontloader.info +filters.ttf = fontloader.info +filters.ttc = fontloader.info -function fonts.names.filters.afm(name) - local pfbname = input.find_file(file.removesuffix(name)..".pfb","pfb") or "" +function filters.afm(name) + local pfbname = resolvers.find_file(file.removesuffix(name)..".pfb","pfb") or "" if pfbname == "" then - pfbname = input.find_file(file.removesuffix(file.basename(name))..".pfb","pfb") or "" + pfbname = resolvers.find_file(file.removesuffix(file.basename(name))..".pfb","pfb") or "" end if pfbname ~= "" then local f = io.open(name) if f then local hash = { } for line in f:lines() do - local key, value = line:match("^(.+)%s+(.+)%s*$") + local key, value = match(line,"^(.+)%s+(.+)%s*$") if key and #key > 0 then - hash[key:lower()] = value + hash[lower(key)] = value end - if line:find("StartCharMetrics") then + if find(line,"StartCharMetrics") then break end end @@ -63,8 +72,8 @@ function fonts.names.filters.afm(name) return nil end -function fonts.names.filters.pfb(name) - return fontforge.info(name) +function filters.pfb(name) + return fontloader.info(name) end --[[ldx-- @@ -73,101 +82,103 @@ the file databases. Watch how we check not only for the names, but also for combination with the weight of a font. --ldx]]-- -fonts.names.filters.list = { +filters.list = { "otf", "ttf", "ttc", "afm", } -fonts.names.filters.fixes = { +filters.fixes = { { "reg$", "regular", }, { "ita$", "italic", }, { "ital$", "italic", }, { "cond$", "condensed", }, + { "book$", "", }, } -fonts.names.xml_configuration_file = "fonts.conf" -- a bit weird format, bonus feature -fonts.names.environment_path_variable = "OSFONTDIR" -- the official way, in minimals etc +names.xml_configuration_file = "fonts.conf" -- a bit weird format, bonus feature +names.environment_path_variable = "OSFONTDIR" -- the official way, in minimals etc -fonts.names.filters.paths = { } -fonts.names.filters.names = { } +filters.paths = { } +filters.names = { } -function fonts.names.getpaths() +function names.getpaths() local hash, result = { }, { } local function collect(t) for i=1, #t do - local v = input.clean_path(t[i]) - v = v:gsub("/+$","") - local key = v:lower() + local v = resolvers.clean_path(t[i]) + v = gsub(v,"/+$","") + local key = lower(v) if not hash[key] then hash[key], result[#result+1] = true, v end end end - local path = fonts.names.environment_path_variable or "" + local path = names.environment_path_variable or "" if path ~= "" then - collect(input.expanded_path_list(path)) + collect(resolvers.expanded_path_list(path)) end - local name = fonts.names.xml_configuration_file or "" - if name ~= "" then - local name = input.find_file(name,"other") + if xml then + local name = names.xml_configuration_file or "" if name ~= "" then - collect(xml.collect_texts(xml.load(name),"dir",true)) + local name = resolvers.find_file(name,"other") + if name ~= "" then + collect(xml.collect_texts(xml.load(name),"dir",true)) + end end end - function fonts.names.getpaths() + function names.getpaths() return result end return result end -function fonts.names.cleanname(name) - return ((name:lower()):gsub("[^%a%d]","")) +function names.cleanname(name) + return (gsub(lower(name),"[^%a%d]","")) end -function fonts.names.identify(verbose) - fonts.names.data = { - version = fonts.names.version, +function names.identify(verbose) -- lsr is for kpse + names.data = { + version = names.version, mapping = { }, -- sorted = { }, fallback_mapping = { }, -- fallback_sorted = { }, } - local done, mapping, fallback_mapping, nofread, nofok = { }, fonts.names.data.mapping, fonts.names.data.fallback_mapping, 0, 0 - local cleanname = fonts.names.cleanname + local done, mapping, fallback_mapping, nofread, nofok = { }, names.data.mapping, names.data.fallback_mapping, 0, 0 + local cleanname = names.cleanname local function check(result, filename, suffix, is_sub) local fontname = result.fullname if fontname then local n = cleanname(result.fullname) if not mapping[n] then - mapping[n], nofok = { suffix, fontname, filename, is_sub }, nofok + 1 + mapping[n], nofok = { lower(suffix), fontname, filename, is_sub }, nofok + 1 end end if result.fontname then fontname = fontname or result.fontname local n = cleanname(result.fontname) if not mapping[n] then - mapping[n], nofok = { suffix, fontname, filename, is_sub }, nofok + 1 + mapping[n], nofok = { lower(suffix), fontname, filename, is_sub }, nofok + 1 end end if result.familyname and result.weight and result.italicangle == 0 then local madename = result.familyname .. " " .. result.weight fontname = fontname or madename - local n = cleanname(madename) + local n = cleanname(fontname) if not mapping[n] and not fallback_mapping[n] then - fallback_mapping[n], nofok = { suffix, fontname, filename, is_sub }, nofok + 1 + fallback_mapping[n], nofok = { lower(suffix), fontname, filename, is_sub }, nofok + 1 end end end - local trace = verbose or fonts.names.trace - local filters = fonts.names.filters - local skip_paths = fonts.names.filters.paths - local skip_names = fonts.names.filters.names + local trace = verbose or trace_names + local skip_paths = filters.paths + local skip_names = filters.names local function identify(completename,name,suffix,storedname) if not done[name] and io.exists(completename) then nofread = nofread + 1 if #skip_paths > 0 then local path = file.dirname(completename) for i=1,#skip_paths do - if path:find(skip_paths[i]) then + if find(path,skip_paths[i]) then if trace then logs.report("fontnames","rejecting path of %s font %s",suffix,completename) logs.push() @@ -179,7 +190,7 @@ function fonts.names.identify(verbose) if #skip_names > 0 then local base = file.basename(completename) for i=1,#skip_paths do - if base:find(skip_names[i]) then + if find(base,skip_names[i]) then done[name] = true if trace then logs.report("fontnames","rejecting name of %s font %s",suffix,completename) @@ -189,51 +200,46 @@ function fonts.names.identify(verbose) end end end - if trace then + if trace_names then logs.report("fontnames","identifying %s font %s",suffix,completename) logs.push() end - local result = filters[suffix:lower()](completename) + local result = filters[lower(suffix)](completename) if trace then logs.pop() end if result then if not result[1] then check(result,storedname,suffix,false) -- was name - else for _, r in ipairs(result) do - check(r,storedname,suffix,true) -- was name - end end + else + for r=1,#result do + check(result[r],storedname,suffix,true) -- was name + end + end end done[name] = true end end + local totalread, totalok = 0, 0 local function traverse(what, method) - for n, suffix in ipairs(fonts.names.filters.list) do + for n, suffix in ipairs(filters.list) do nofread, nofok = 0, 0 local t = os.gettimeofday() -- use elapser - suffix = suffix:lower() + suffix = lower(suffix) logs.report("fontnames", "identifying %s font files with suffix %s",what,suffix) method(suffix) - suffix = suffix:upper() + suffix = upper(suffix) logs.report("fontnames", "identifying %s font files with suffix %s",what,suffix) method(suffix) logs.report("fontnames", "%s %s files identified, %s hash entries added, runtime %0.3f seconds",nofread,what,nofok,os.gettimeofday()-t) + totalread, totalok = totalread + nofread, totalok + nofok end end - traverse("tree", function(suffix) -- TEXTREE only - input.with_files(".*%." .. suffix .. "$", function(method,root,path,name) - if method == "file" then - local completename = root .."/" .. path .. "/" .. name - identify(completename,name,suffix,name,name) - end - end) - end) - traverse("system", function(suffix) -- OSFONTDIR cum suis - local pathlist = fonts.names.getpaths() + local function walk_tree(pathlist,suffix) if pathlist then for _, path in ipairs(pathlist) do - path = input.clean_path(path .. "/") - path = path:gsub("/+","/") + path = resolvers.clean_path(path .. "/") + path = gsub(path,"/+","/") local pattern = path .. "**." .. suffix -- ** forces recurse logs.report("fontnames", "globbing path %s",pattern) local t = dir.glob(pattern) @@ -242,67 +248,115 @@ function fonts.names.identify(verbose) end end end + end + traverse("tree", function(suffix) -- TEXTREE only + resolvers.with_files(".*%." .. suffix .. "$", function(method,root,path,name) + if method == "file" then + local completename = root .."/" .. path .. "/" .. name + identify(completename,name,suffix,name,name) + end + end) end) + if texconfig.kpse_init then + -- we do this only for a stupid names run, not used for context itself, + -- using the vars is to clumsy so we just stick to a full scan instead + traverse("lsr", function(suffix) -- all trees + local pathlist = resolvers.split_path(resolvers.show_path("ls-R") or "") + walk_tree(pathlist,suffix) + end) + else + traverse("system", function(suffix) -- OSFONTDIR cum suis + walk_tree(names.getpaths(),suffix) + end) + end local t = { } - for _, f in ipairs(fonts.names.filters.fixes) do + for _, f in ipairs(filters.fixes) do local expression, replacement = f[1], f[2] - for k,v in pairs(mapping) do - local fix, pos = k:gsub(expression,replacement) + for k,v in next, mapping do + local fix, pos = gsub(k,expression,replacement) if pos > 0 and not mapping[fix] then t[fix] = v end end end - for k,v in pairs(t) do + local n = 0 + for k,v in next, t do mapping[k] = v + n = n + 1 end + local rejected = 0 + for k, v in next, mapping do + local kind, filename = v[1], v[3] + if not file.is_qualified_path(filename) and resolvers.find_file(filename,kind) == "" then + mapping[k] = nil + rejected = rejected + 1 + end + end + if n > 0 then + logs.report("fontnames", "%s files read, %s normal and %s extra entries added, %s rejected, %s valid",totalread,totalok,n,rejected,totalok+n-rejected) + end +end + +function names.is_permitted(name) + return containers.is_usable(names.cache(), name) +end +function names.write_data(name,data) + containers.write(names.cache(),name,data) +end +function names.read_data(name) + return containers.read(names.cache(),name) end -function fonts.names.load(reload,verbose) - if not fonts.names.loaded then +function names.load(reload,verbose) + if not names.loaded then if reload then - if containers.is_usable(fonts.names.cache(), "names") then - fonts.names.identify(verbose) - containers.write(fonts.names.cache(), "names", fonts.names.data) + if names.is_permitted(names.basename) then + names.identify(verbose) + names.write_data(names.basename,names.data) + else + logs.report("font table", "unable to access database cache") end - fonts.names.saved = true + names.saved = true else - fonts.names.data = containers.read(fonts.names.cache(), "names") - if not fonts.names.saved then - if table.is_empty(fonts.names.data) or table.is_empty(fonts.names.data.mapping) then - fonts.names.load(true) + names.data = names.read_data(names.basename) + if not names.saved then + if table.is_empty(names.data) or table.is_empty(names.data.mapping) then + names.load(true) end - fonts.names.saved = true + names.saved = true end end - local data = fonts.names.data + local data = names.data if data then data.sorted = table.sortedkeys(data.mapping or { }) or { } data.fallback_sorted = table.sortedkeys(data.fallback_mapping or { }) or { } else logs.report("font table", "accessing the data table failed") end - fonts.names.loaded = true + names.loaded = true end end -function fonts.names.list(pattern,reload) - fonts.names.load(reload) - if fonts.names.loaded then +function names.list(pattern,reload) + names.load(reload) + if names.loaded then local t = { } local function list_them(mapping,sorted) if mapping[pattern] then t[pattern] = mapping[pattern] else for k,v in ipairs(sorted) do - if v:find(pattern) then + if find(v,pattern) then t[v] = mapping[v] end end end end - list_them(fonts.names.data.mapping,fonts.names.data.sorted) - list_them(fonts.names.data.fallback_mapping,fonts.names.data.fallback_sorted) + local data = names.data + if data then + list_them(data.mapping,data.sorted) + list_them(data.fallback_mapping,data.fallback_sorted) + end return t else return nil @@ -315,110 +369,81 @@ here is for testing purposes only (it deals with names prefixed by an encoding name). --ldx]]-- -do - - local function found(name) - if fonts.names.data then - name = fonts.names.cleanname(name) - local function found_indeed(mapping,sorted) - local mn = mapping[name] - if mn then - return mn[2], mn[3], mn[4] - end - if fonts.names.be_clever then -- this will become obsolete - local encoding, tag = name:match("^(.-)[%-%:](.+)$") - local mt = mapping[tag] - if tag and fonts.enc.is_known(encoding) and mt then - return mt[1], encoding .. "-" .. mt[3], mt[4] - end - end - -- name, type, file - for k,v in pairs(mapping) do - if k:find(name) then - return v[2], v[3], v[4] - end - end - local condensed = name:gsub("[^%a%d]","") - local mc = mapping[condensed] - if mc then - return mc[2], mc[3], mc[4] - end - for k,v in ipairs(sorted) do - if v:find(condensed) then - v = mapping[v] - return v[2], v[3], v[4] - end - end - return nil, nil, nil - end - local data = fonts.names.data - local fontname, filename, is_sub = found_indeed(data.mapping, data.sorted) - if not fontname or not filename then - fontname, filename, is_sub = found_indeed(data.fallback_mapping, data.fallback_sorted) - end - return fontname, filename, is_sub - else - return nil, nil, nil +local function found_indeed(mapping,sorted,name) + local mn = mapping[name] + if mn then + return mn[2], mn[3], mn[4] + end + if names.be_clever then -- this will become obsolete + local encoding, tag = match(name,"^(.-)[%-%:](.+)$") + local mt = mapping[tag] + if tag and fonts.enc.is_known(encoding) and mt then + return mt[1], encoding .. "-" .. mt[3], mt[4] end end - - local reloaded = false - - function fonts.names.resolve(askedname, sub) - if not askedname then - return nil, nil - elseif fonts.names.enabled then - askedname = askedname:lower() - fonts.names.load() - local name, filename, is_sub = found(askedname) - if not filename and not reloaded and fonts.names.autoreload then - fonts.names.loaded = false - reloaded = true - io.flush() - fonts.names.load(true) - name, filename, is_sub = found(askedname) - end - if is_sub then - return filename, name - else - return filename, sub - end - else - return filename, sub + -- name, type, file + for k,v in next, mapping do + if find(k,name) then + return v[2], v[3], v[4] end end + local condensed = gsub(name,"[^%a%d]","") + local mc = mapping[condensed] + if mc then + return mc[2], mc[3], mc[4] + end + for k=1,#sorted do + local v = sorted[k] + if find(v,condensed) then + v = mapping[v] + return v[2], v[3], v[4] + end + end + return nil, nil, nil +end +local function found(name) + if name and name ~= "" and names.data then + name = names.cleanname(name) + local data = names.data + local fontname, filename, is_sub = found_indeed(data.mapping, data.sorted, name) + if not fontname or not filename then + fontname, filename, is_sub = found_indeed(data.fallback_mapping, data.fallback_sorted, name) + end + return fontname, filename, is_sub + else + return nil, nil, nil + end end ---[[ldx-- -A handy helper.
---ldx]]-- +local reloaded = false -function fonts.names.table(pattern,reload,all) - local t = fonts.names.list(pattern,reload) - if t then - texsprint(tex.ctxcatcodes,"\\start\\nonknuthmode\\starttabulate[|T|T|T|T|T|]") - texsprint(tex.ctxcatcodes,"\\NC hashname\\NC type\\NC fontname\\NC filename\\NC\\NR\\HL") - for k,v in pairs(table.sortedkeys(t)) do - if all or v == t[v][2]:lower() then - local type, name, file = unpack(t[v]) - if type and name and file then - texsprint(tex.ctxcatcodes,string.format("\\NC %s\\NC %s\\NC %s\\NC %s\\NC\\NR",v,type, name, file)) - else - logs.report("font table", "skipping %s", v) - end - end +function names.specification(askedname, sub) + if askedname and askedname ~= "" and names.enabled then + askedname = lower(askedname) + names.load() + local name, filename, is_sub = found(askedname) + if not filename and not reloaded and names.autoreload then + names.loaded = false + reloaded = true + io.flush() + names.load(true) + name, filename, is_sub = found(askedname) end - texsprint(tex.ctxcatcodes,"\\stoptabulate\\stop") + return name, filename, is_sub end end +function names.resolve(askedname, sub) + local name, filename, is_sub = names.specification(askedname, sub) + return filename, (is_sub and name) or sub +end --[[ldx--Fallbacks, not permanent but a transition thing.
--ldx]]-- -fonts.names.new_to_old = { +names.new_to_old = { ["lmroman10-capsregular"] = "lmromancaps10-oblique", ["lmroman10-capsoblique"] = "lmromancaps10-regular", ["lmroman10-demi"] = "lmromandemi10-oblique", @@ -460,19 +485,19 @@ fonts.names.new_to_old = { ["lmtypewritervarwd10-darkoblique"] = "lmmonoproplt10-boldoblique", } -fonts.names.old_to_new = table.swapped(fonts.names.new_to_old) +names.old_to_new = table.swapped(names.new_to_old) -function fonts.names.exists(name) - local fna, found = fonts.names.autoreload, false - fonts.names.autoreload = false - for k,v in ipairs(fonts.names.filters.list) do - found = (input.find_file(name,v) or "") ~= "" +function names.exists(name) + local fna, found = names.autoreload, false + names.autoreload = false + for k,v in ipairs(filters.list) do + found = (resolvers.find_file(name,v) or "") ~= "" if found then break end end - found = found or (input.find_file(name,"tfm") or "") ~= "" - found = found or (fonts.names.resolve(name) or "") ~= "" - fonts.names.autoreload = fna + found = found or (resolvers.find_file(name,"tfm") or "") ~= "" + found = found or (names.resolve(name) or "") ~= "" + names.autoreload = fna return found end diff --git a/tex/context/base/font-tfm.lua b/tex/context/base/font-tfm.lua index 1955b58bc..2ee633b77 100644 --- a/tex/context/base/font-tfm.lua +++ b/tex/context/base/font-tfm.lua @@ -6,31 +6,51 @@ if not modules then modules = { } end modules ['font-tfm'] = { license = "see context related readme files" } +local utf = unicode.utf8 + +local next, format, match, lower = next, string.format, string.match, string.lower +local concat, sortedkeys, utfbyte, serialize = table.concat, table.sortedkeys, utf.byte, table.serialize + +local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) +local trace_scaling = false trackers.register("fonts.scaling" , function(v) trace_scaling = v end) + +-- tfmdata has also fast access to indices and unicodes +-- to be checked: otf -> tfm -> tfmscaled +-- +-- watch out: no negative depths and negative eights permitted in regular fonts + --[[ldx--Here we only implement a few helper functions.
--ldx]]-- fonts = fonts or { } fonts.tfm = fonts.tfm or { } +fonts.ids = fonts.ids or { } local tfm = fonts.tfm -fonts.loaded = fonts.loaded or { } -fonts.dontembed = fonts.dontembed or { } -fonts.logger = fonts.logger or { } -fonts.loadtime = 0 -fonts.triggers = fonts.triggers or { } -- brrr +fonts.loaded = fonts.loaded or { } +fonts.dontembed = fonts.dontembed or { } +fonts.triggers = fonts.triggers or { } -- brrr +fonts.initializers = fonts.initializers or { } +fonts.initializers.common = fonts.initializers.common or { } + +local fontdata = fonts.ids +local glyph = node.id('glyph') +local set_attribute = node.set_attribute --[[ldx--The next function encapsulates the standard
The following functions are used for reporting about the fonts -used. The message itself is not that useful in regular runs but since -we now have several readers it may be handy to know what reader is -used for which font.
---ldx]]-- - -function fonts.logger.save(tfmtable,source,specification) -- save file name in spec here ! ! ! ! ! ! - if tfmtable and specification and specification.specification then - if fonts.trace then - logs.report("define font","registering %s as %s",specification.name,source) - end - specification.source = source - fonts.loaded[specification.specification] = specification - fonts.used[specification.name] = source - end -end - ---~ function fonts.logger.report(separator) ---~ local s = table.sortedkeys(fonts.loaded) ---~ if #s > 0 then ---~ local t = { } ---~ for _,v in ipairs(s) do ---~ t[#t+1] = v .. ":" .. fonts.loaded[v].source ---~ end ---~ return table.concat(t,separator or " ") ---~ else ---~ return "none" ---~ end ---~ end - -function fonts.logger.report(separator) - local s = table.sortedkeys(fonts.used) - if #s > 0 then - local t = { } - for _,v in ipairs(s) do - t[#t+1] = v .. ":" .. fonts.used[v] - end - return table.concat(t,separator or " ") - else - return "none" - end -end - -function fonts.logger.format(name) - return fonts.used[name] or "unknown" -end - ---[[ldx-- -When we implement functions that deal with features, most of them -will depend of the font format. Here we define the few that are kind -of neutral.
---ldx]]-- - -fonts.initializers = fonts.initializers or { } -fonts.initializers.common = fonts.initializers.common or { } - ---[[ldx-- -This feature will remove inter-digit kerns.
---ldx]]-- - -table.insert(fonts.triggers,"equaldigits") - -function fonts.initializers.common.equaldigits(tfmdata,value) - if value then - local chr = tfmdata.characters - for i = utf.byte('0'), utf.byte('9') do - local c = chr[i] - if c then - c.kerns = nil - end - end - end -end - ---[[ldx-- -This feature will give all glyphs an equal height and/or depth. Valid
-values are
It does not make sense any more to support messed up encoding vectors
-so we stick to those that implement oldstyle and small caps. After all,
-we move on. We can extend the next function on demand. This features is
-only used with
Analyzers run per script and/or language and are needed in order to process features right.
@@ -587,47 +603,31 @@ fonts.analyzers.aux = fonts.analyzers.aux or { } fonts.analyzers.methods = fonts.analyzers.methods or { } fonts.analyzers.initializers = fonts.analyzers.initializers or { } -do - - local glyph = node.id('glyph') - local fontdata = tfm.id - local set_attribute = node.set_attribute --- local unset_attribute = node.unset_attribute --- local has_attribute = node.has_attribute - - local state = attributes.numbers['state'] or 100 - - -- todo: analyzers per script/lang, cross font, so we need an font id hash -> script - -- e.g. latin -> hyphenate, arab -> 1/2/3 analyze - - -- an example analyzer - - function fonts.analyzers.aux.setstate(head,font) - local tfmdata = fontdata[font] - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean - while current do - if current.id == glyph and current.font == font then - local d = descriptions[current.char] - if d then - if d.class == "mark" then - done = true - set_attribute(current,state,5) -- mark - elseif n == 0 then - first, last, n = current, current, 1 - set_attribute(current,state,1) -- init - else - last, n = current, n+1 - set_attribute(current,state,2) -- medi - end - else -- finish - if first and first == last then - set_attribute(last,state,4) -- isol - elseif last then - set_attribute(last,state,3) -- fina - end - first, last, n = nil, nil, 0 +-- todo: analyzers per script/lang, cross font, so we need an font id hash -> script +-- e.g. latin -> hyphenate, arab -> 1/2/3 analyze + +-- an example analyzer (should move to font-ota.lua) + +local state = attributes.private('state') + +function fonts.analyzers.aux.setstate(head,font) + local tfmdata = fontdata[font] + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean + while current do + if current.id == glyph and current.font == font then + local d = descriptions[current.char] + if d then + if d.class == "mark" then + done = true + set_attribute(current,state,5) -- mark + elseif n == 0 then + first, last, n = current, current, 1 + set_attribute(current,state,1) -- init + else + last, n = current, n+1 + set_attribute(current,state,2) -- medi end else -- finish if first and first == last then @@ -637,132 +637,22 @@ do end first, last, n = nil, nil, 0 end - current = current.next - end - if first and first == last then - set_attribute(last,state,4) -- isol - elseif last then - set_attribute(last,state,3) -- fina - end - return head, done - end - -end - ---[[ldx-- -We move marks into the components list. This saves much nasty testing later on.
---ldx]]-- - -do - - local glyph = node.id('glyph') - local fontdata = tfm.id - local marknumber = attributes.numbers['mark'] or 200 - local set_attribute = node.set_attribute - - function fonts.pushmarks(head,font) - local tfmdata = fontdata[font] - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local current, last, done, n = head, nil, false, 0 - while current do - if current.id == glyph and current.font == font then - local d = descriptions[current.char] - if d and d.class == "mark" then - -- check if head - if last and not last.components then - last.components = current - current.prev = nil -- last.components.prev = nil - done = true - n = 1 - else - n = n + 1 - end - set_attribute(current,marknumber,n) - current = current.next - elseif last and last.components then - -- finish 'm - current.prev.next = nil - current.prev = last - last.next = current - last = current - last = nil - else - last = current - current = current.next - end - elseif last and last.components then - current.prev.next = nil - current.prev = last - last.next = current - last = nil - else - last = nil - current = current.next - end - end - if last and last.components then - last.next = nil - end - tfmdata.shared.markspushed = done - return head, done - end - - function fonts.removemarks(head,font) - local current, done, characters, descriptions = head, false, tfmdata.characters, tfmdata.descriptions - while current do - if current.id == glyph and current.font == font and descriptions[current.char].class == "mark" then - local next, prev = current.next, current.prev - if next then - next.prev = prev - end - if prev then - prev.next = next - else - head = next - end - node.free(current) - current = next - done = true - else - current = current.next + else -- finish + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina end + first, last, n = nil, nil, 0 end - return head, done + current = current.next end - - function fonts.popmarks(head,font) - local tfmdata = fontdata[font] - if tfmdata.shared.markspushed then - local current, done, characters = head, false, tfmdata.characters - while current do - if current.id == glyph and current.font == font then - local components = current.components - if components then - local last, next = components, current.next - while last.next do last = last.next end - if next then - next.prev = last - end - last.next= next - current.next = components - components.prev = current - current.components = nil - current = last.next - done = true - else - current = current.next - end - else - current = current.next - end - end - return head, done - else - return head, false - end + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina end - + return head, done end function tfm.replacements(tfm,value) @@ -800,7 +690,7 @@ function tfm.enhance(tfmdata,specification) tfmdata.filename = specification.name if not features.encoding then local name, size = specification.name, specification.size - local encoding, filename = name:match("^(.-)%-(.*)$") -- context: encoding-name.* + local encoding, filename = match(name,"^(.-)%-(.*)$") -- context: encoding-name.* if filename and encoding and fonts.enc.known[encoding] then features.encoding = encoding end @@ -809,6 +699,7 @@ tfmdata.filename = specification.name end function tfm.set_features(tfmdata) + -- todo: no local functions local shared = tfmdata.shared -- local tfmdata = shared.tfmdata local features = shared.features @@ -818,7 +709,8 @@ function tfm.set_features(tfmdata) if fi and fi.tfm then local function initialize(list) -- using tex lig and kerning if list then - for _, f in ipairs(list) do + for i=1,#list do + local f = list[i] local value = features[f] if value and fi.tfm[f] then -- brr if tfm.trace_features then @@ -839,7 +731,8 @@ function tfm.set_features(tfmdata) if fm and fm.tfm then local function register(list) -- node manipulations if list then - for _, f in ipairs(list) do + for i=1,#list do + local f = list[i] if features[f] and fm.tfm[f] then -- brr if not shared.processors then -- maybe also predefine shared.processors = { fm.tfm[f] } @@ -866,13 +759,13 @@ function tfm.reencode(tfmdata,encoding) if data then local characters, original, vector = tfmdata.characters, { }, data.vector tfmdata.encoding = encoding -- not needed - for k, v in pairs(characters) do + for k, v in next, characters do v.name, v.index, original[k] = vector[k], k, v end - for k,v in pairs(data.unicodes) do + for k,v in next, data.unicodes do if k ~= v then - if fonts.trace then - logs.report("define font","reencoding %04X to %04X",k,v) + if trace_defining then + logs.report("define font","reencoding U+%04X to U+%04X",k,v) end characters[k] = original[v] end @@ -893,13 +786,13 @@ function tfm.remap(tfmdata,remapping) local vector = remapping and fonts.enc.remappings[remapping] if vector then local characters, original = tfmdata.characters, { } - for k, v in pairs(characters) do + for k, v in next, characters do original[k], characters[k] = v, nil end - for k,v in pairs(vector) do + for k,v in next, vector do if k ~= v then - if fonts.trace then - logs.report("define font","remapping %04X to %04X",k,v) + if trace_defining then + logs.report("define font","remapping U+%04X to U+%04X",k,v) end local c = original[k] characters[v] = c @@ -915,3 +808,11 @@ tfm.features.register('remap') fonts.initializers.base.tfm.remap = tfm.remap fonts.initializers.node.tfm.remap = tfm.remap + +-- status info + +statistics.register("fonts load time", function() + if statistics.elapsedindeed(fonts) then + return format("%s seconds",statistics.elapsedtime(fonts)) + end +end) diff --git a/tex/context/base/font-tra.mkiv b/tex/context/base/font-tra.mkiv new file mode 100644 index 000000000..c45e1394d --- /dev/null +++ b/tex/context/base/font-tra.mkiv @@ -0,0 +1,113 @@ +%D \module +%D [ file=font-tra, +%D version=2009.01.02, % or so +%D title=\CONTEXT\ Font Macros, +%D subtitle=Tracing, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\unprotect + +%D \macros +%D {doiffontpresentelse} +%D +%D \starttyping +%D \doiffontpresentelse{texnansi-lmr10}{YES}{NO} +%D \doiffontpresentelse{adam-lindsay-modern-serif}{YES}{NO} +%D \stoptyping + +\def\doiffontpresentelse#1{\ctxlua{commands.doifelse(fonts.names.exists("#1"))}} + +% experimental, maybe this becomes a module + +\newbox\otfcollector + +\def\startotfcollecting{\ctxlua{nodes.tracers.steppers.start()}} +\def\stopotfcollecting {\ctxlua{nodes.tracers.steppers.stop()}} +\def\resetotfcollecting{\ctxlua{nodes.tracers.steppers.reset()}} + +% Rather experimental: +% +% \page \showotfcomposition{arabtype*arab-default at 48pt}{-1}{الضَّرَّ} \page +% \page \showotfcomposition{arabtype*arab-default at 48pt}{-1}{لِلّٰهِ} \page + +\def\showotfstepglyphs#1% + {\ctxlua{nodes.tracers.steppers.glyphs(\number\otfcollector,#1)}% + \unhbox\otfcollector} + +\def\otfstepcharcommand#1#2% font char + {\removeunwantedspaces + \hskip.5em plus .125em\relax + U+\hexnumber{#2}:\ruledhbox{\ctxlua{nodes.tracers.fontchar(#1,#2)}}% + \hskip.5em plus .125em\relax} + +\def\otfstepmessagecommand#1#2% + {\begingroup + \tttf\language\minusone + \veryraggedright + \hangindent1em + \hangafter\plusone + \dontleavehmode\hbox{\detokenize{#1}}\removeunwantedspaces + \doifsomething{#2}{\break\detokenize{#2}}\endgraf + \endgroup + \blank} + +\def\showotfstepchars#1% + {\ctxlua{nodes.tracers.steppers.codes(#1,\!!bs\detokenize{\otfstepcharcommand}\!!es)}} + +\def\showotfstepmessages#1% + {\ctxlua{nodes.tracers.steppers.messages(#1,\!!bs\detokenize{\otfstepmessagecommand}\!!es,true)}} + +\def\showotfstepfeatures + {\ctxlua{nodes.tracers.steppers.features()}} + +\def\showotfsteps + {\dontleavehmode\bgroup\tttf \language\minusone features: \showotfstepfeatures\egroup + \blank + \dontleavehmode\bgroup\tttf result:\egroup + \blank + \startlinecorrection + \ruledhbox\bgroup\box\otfcompositionbox\egroup + \stoplinecorrection + \dorecurse{\ctxlua{nodes.tracers.steppers.nofsteps()}} + {\blank + \showotfstepmessages\recurselevel + \blank + \startlinecorrection + \dontleavehmode\bgroup\resetallattributes\pardir TLT\textdir TLT\relax\tttf\recurselevel: \showotfstepchars\recurselevel\egroup + \stoplinecorrection + \blank + \startlinecorrection + \ruledhbox % can be mode + \bgroup\resetallattributes\showotfstepglyphs\recurselevel\egroup % reset is new, we don't want additional processing + \stoplinecorrection + \blank}} + +\def\startotfsample + {\enabletrackers[*otf.sample]% beware, kind of global + \startotfcollecting + \begingroup} + +\def\stopotfsample + {\endgroup + \stopotfcollecting + \disabletrackers[*otf.sample]% beware, kind of global: otf.sample + \showotfsteps + \resetotfcollecting} + +\newbox\otfcompositionbox + +\def\showotfcomposition#1#2#3% {font*features at size}, rl=-1, text + {\begingroup + \setupcolors[\c!state=\v!start]% can be option + \startotfsample + \global\setbox\otfcompositionbox\hbox{\definedfont[#1]\ifnum#2<0 \textdir TRT\else\ifnum#2>0 \textdir TLT\fi\fi\relax#3}% + \stopotfsample + \endgroup} + +\protect \endinput diff --git a/tex/context/base/font-uni.mkii b/tex/context/base/font-uni.mkii new file mode 100644 index 000000000..1b8ce8e43 --- /dev/null +++ b/tex/context/base/font-uni.mkii @@ -0,0 +1,444 @@ +%D \module +%D [ file=font-uni, +%D version=1999.10.10, +%D title=\CONTEXT\ Font Macros, +%D subtitle=\UNICODE, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Font Macros / Unicode} + +%D In \XETEX, unicode support is straightforward, so we +%D simply output a \type {\char} with a 16||bit number. + +\ifnum\texengine=\xetexengine + \unexpanded\def\uchar#1#2{\char\numexpr#2+#1*\pluscclvi\relax} + \let\uc\uchar + \endinput +\fi + +%D Now comes the more traditional 8 bit \TEX\ hackery. + +%D I wrote this module when Wang Lei asked me how to use +%D Chinese in \CONTEXT. From the samples he sent me, I deduced +%D that some mixture of one and two byte encoding was used, +%D which he confirmed. Since \TEX\ normally does not use the +%D characters $>127$, so as long as the two byte characters +%D have a first character with code $>127$, we can use active +%D characters to handle them. In an optimistic mood, I called +%D this module the \UNICODE\ font module. In the module that +%D handles Chinese, we will see that some more interpretation +%D is involved, which is why the macros handling those +%D characters look ahead. + +\unprotect + +%D \macros +%D {handleunicodeflowglyph, uchar, +%D handleunicodeglyph, insertunicodeglyph, +%D unicodeposition, unicodeone, unicodetwo} +%D +%D For the moment \UNICODE\ support is rather primitive but +%D nevertheless effective. The reference to \UNICODE\ is not +%D entirely correct, since in many cases one will use \quote +%D {older} mappings, but in principle, \UNICODE\ can be +%D supported. +%D +%D We expect each character to come as two eight bit +%D characters. Those doubles are handled by making all +%D characters in the range $>127$ active, so that they can +%D pick up the next one, and act upon both their values. +%D Internally only numbers are used. A first implementation +%D simply internally prefixed the second part of the \UNICODE\ +%D pair with \type {\string} or \type {\char}, but this was +%D not that handy when it came to testing those values. +%D Because in principle we are dealing with an encoding, the +%D making active is handled in \type {enco-uni}. +%D +%D There are two commands to handle unicode characters: +%D +%D \starttyping +%D \handleunicodeflowglyph{number}{character} +%D \uchar{number}{number} +%D \stoptyping +%D +%D The first one can be assigned to an active character, the +%D second one can be used to directly access a glyph. Both +%D command call \type {\handleunicodeglyph} that in turn +%D calls \type {\insertunicodeglyph}. Both can be overruled +%D in specialized modules. The low level command \type +%D {\unicodeglyph} can best be left untouched, which is not +%D so much a problem because there is a hook into this macro: +%D \type {\unicodecharcommand}. +%D +%D In most cases one will redefine \type {\handleunicodeglyph} +%D in such a way that it identifies special situations first, +%D takes some actions next, calls \type {\insertunicodeglyph}, +%D if needed with \type {\unicodecharcommand} changed, and +%D finally does some finishing: +%D +%D \starttyping +%D \def\handleunicodeglyph +%D {take actions based on \unicodeone-two-position cq. \nextutoken +%D redefine \unicodecharcommand if needed +%D expand \insertunicodeglyph +%D take some final actions} +%D \stoptyping + +\newcount\unicodeposition + +%D The multistep approach is needed to pick up the second +%D token, since this token can have any value and any +%D catcode. + +% the \relax trick prevents eating up the space (needed for +% korean + +\def\handleunicodeflowglyph#1#2% + {\begingroup + \edef\unicodeone{#1}% + \@EA\afterassignment\@EA\dohandleunicodeflowglyph % two redundant ea's + \@EA\chardef\@EA\nexttoken\@EA`\string#2\relax} + +\def\dohandleunicodeflowglyph\relax + {\futurelet\nextutoken\dodohandleunicodeflowglyph} + +\def\dodohandleunicodeflowglyph % todo tex (or maybe no longer) + {\edef\unicodetwo{\the\nexttoken}% + \unicodeposition\numexpr\unicodeone*256+\unicodetwo\relax + \handleunicodeglyph + \endgroup} + +\unexpanded\def\uchar#1#2% use as standalone glyph + {\begingroup + \edef\unicodeone{#1}% + \edef\unicodetwo{#2}% + \unicodeposition\numexpr\unicodeone*256+\unicodetwo\relax + \handleunicodeglyph + \endgroup} + +\let\nextutoken\relax + +\unexpanded\def\lookaheaduchar#1#2% + {\def\dolookaheaduchar{\uchar{#1}{#2}\let\nextutoken\relax}% + \futurelet\nextutoken\dolookaheaduchar} + +\def\dohandleucflowglyph + {\unicodeposition\numexpr\unicodeone*256+\unicodetwo\relax + \handleunicodeglyph + \endgroup} + +\unexpanded\def\uc#1#2% used in tricky situations + {\begingroup + \edef\unicodeone{#1}% + \edef\unicodetwo{#2}% + \futurelet\nextutoken\dohandleucflowglyph} + +\def\insertunicodeglyph + {\unicodeglyph\unicodeone\unicodetwo} + +\let\handleunicodeglyph\insertunicodeglyph + +%D One can use the \type {\unicodeposition} in the macros +%D that handle pre and post material. + +%D \macros +%D {unicodestyle, unicodecharcommand} +%D +%D Each character pair will become one glyph. Because \TEX\ +%D cannot handle fonts with more that 256 characters, we use +%D \TFM\ files for each range. The first character of the pair +%D is appended to the name of a font, and the second is used to +%D access the glyph in that font. This means that a particular +%D font is split up in subfonts with names in the range: +%D +%D \starttyping +%DChoosing a font by name and specififying its size is only part of the
+game. In order to prevent complex commands,
For the sake of users who have defined fonts using that syntax, we +will support it, but we will provide additional methods as well. +Normally users will not use this direct way, but use a more abstract +interface.
+ +The next one is the official one. However, in the plain +variant we need to support the crappy [] specification as +well and that does not work too well with the general design +of the specifier.
+--ldx]]-- + +--~ function fonts.define.specify.colonized(specification) -- xetex mode +--~ local list = { } +--~ if specification.detail and specification.detail ~= "" then +--~ for v in gmatch(specification.detail,"%s*([^;]+)%s*") do +--~ local a, b = match(v,"^(%S*)%s*=%s*(%S*)$") +--~ if a and b then +--~ list[a] = b:is_boolean() +--~ if type(list[a]) == "nil" then +--~ list[a] = b +--~ end +--~ else +--~ local a, b = match(v,"^([%+%-]?)%s*(%S+)$") +--~ if a and b then +--~ list[b] = a ~= "-" +--~ end +--~ end +--~ end +--~ end +--~ specification.features.normal = list +--~ return specification +--~ end + +--~ check("oeps/BI:+a;-b;c=d") +--~ check("[oeps]/BI:+a;-b;c=d") +--~ check("file:oeps/BI:+a;-b;c=d") +--~ check("name:oeps/BI:+a;-b;c=d") + +local list = { } + +fonts.define.specify.colonized_default_lookup = "file" + +local function issome () list.lookup = fonts.define.specify.colonized_default_lookup end +local function isfile () list.lookup = 'file' end +local function isname () list.lookup = 'name' end +local function thename(s) list.name = s end +local function issub (v) list.sub = v end +local function iscrap (s) list.crap = string.lower(s) end +local function istrue (s) list[s] = 'yes' end +local function isfalse(s) list[s] = 'no' end +local function iskey (k,v) list[k] = v end + +local spaces = lpeg.P(" ")^0 +local namespec = (1-lpeg.S("/: ("))^0 +local crapspec = spaces * lpeg.P("/") * (((1-lpeg.P(":"))^0)/iscrap) * spaces +local filename = (lpeg.P("file:")/isfile * (namespec/thename)) + (lpeg.P("[") * lpeg.P(true)/isname * (((1-lpeg.P("]"))^0)/thename) * lpeg.P("]")) +local fontname = (lpeg.P("name:")/isname * (namespec/thename)) + lpeg.P(true)/issome * (namespec/thename) +local sometext = (lpeg.R("az") + lpeg.R("AZ") + lpeg.R("09"))^1 +local truevalue = lpeg.P("+") * spaces * (sometext/istrue) +local falsevalue = lpeg.P("-") * spaces * (sometext/isfalse) +local keyvalue = (lpeg.C(sometext) * spaces * lpeg.P("=") * spaces * lpeg.C(sometext))/iskey +local somevalue = sometext/istrue +local subvalue = lpeg.P("(") * (lpeg.C(lpeg.P(1-lpeg.S("()"))^1)/issub) * lpeg.P(")") -- for Kim +local option = spaces * (keyvalue + falsevalue + truevalue + somevalue) * spaces +local options = lpeg.P(":") * spaces * (lpeg.P(";")^0 * option)^0 +local pattern = (filename + fontname) * subvalue^0 * crapspec^0 * options^0 + +function fonts.define.specify.colonized(specification) -- xetex mode + list = { } + pattern:match(specification.specification) + for k, v in next, list do + list[k] = v:is_boolean() + if type(list[a]) == "nil" then + list[k] = v + end + end + list.crap = nil -- style not supported, maybe some day + if list.name then + specification.name = list.name + list.name = nil + end + if list.lookup then + specification.lookup = list.lookup + list.lookup = nil + end + if list.sub then + specification.sub = list.sub + list.sub = nil + end + specification.features.normal = list + return specification +end + +fonts.define.register_split(":", fonts.define.specify.colonized) diff --git a/tex/context/base/font-xtx.tex b/tex/context/base/font-xtx.tex new file mode 100644 index 000000000..5f4b85879 --- /dev/null +++ b/tex/context/base/font-xtx.tex @@ -0,0 +1,357 @@ +%D \module +%D [ file=font-xtx, +%D version=2004.09.11, +%D title=\CONTEXT\ Font Macros, +%D subtitle=\XETEX\ Hacks, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=\PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\ifnum\texengine=\xetexengine + \writestatus{loading}{ConTeXt Font Macros / XeTeX Hacks} +\else + \endinput +\fi + +\unprotect + +%D Loading: + +%D for some reason xetex does not support [filename] for tfm files and +%D quotes also behave kind of strange " vs ' vs [ vs ... +%D +%D \starttyping +%D \font\myfont = msam7 % ok +%D \font\myfont = "msam7" % also ok +%D \font\myfont = "msam7" at 8pt % error +%D \stoptyping + +\newconditional\tracexetexfonts + +%D Because \XETEX\ is not that fast on locating fonts we cache lookups so +%D that we minimize the test. It saves a little bit of runtime, depending +%D on the number of fonts loaded (which is normally not that much). + +\def\doiffoundXTXfontelse#1#2% + {\ifcsname xtx@fnt@#2\somefontspec\endcsname + \ifconditional\tracexetexfonts + \writestatus\m!fonts{already checked #1: #2\somefontspec\space (state: \number\csname xtx@fnt@#2\somefontspec\endcsname)}% + \fi + \else + \suppressfontnotfounderror\plusone + \font\xetextempfont=#2\somefontspec\relax + \suppressfontnotfounderror\zerocount + \edef\xetextempfont{\fontname\xetextempfont}% + \global\expandafter\chardef\csname xtx@fnt@#2\somefontspec\endcsname + \ifx\xetextempfont\nullfontname + \zerocount \ifconditional\tracexetexfonts + \writestatus\m!fonts{not found #1: #2\somefontspec}% + \fi + \else + \plusone \ifconditional\tracexetexfonts + \writestatus\m!fonts{found #1: #2\somefontspec}% + \fi + \fi + \fi + \ifcase\csname xtx@fnt@#2\somefontspec\endcsname + \expandafter\secondoftwoarguments + \else + \expandafter\firstoftwoarguments + \fi} + +\def\docheckfontfilenameprefix#1:#2:#3#4\relax + {\edef\!!stringa{#1}% + \edef\!!stringb{#2}% + \ifx\!!stringb\empty + % no prefix + \let\checkedfontfile\!!stringa + \doiffoundXTXfontelse{1a}{\checkedfontfile\checkedfontfeatures} + {\edef\checkedfontfile{\checkedfontfile\checkedfontfeatures}} + {\doiffoundXTXfontelse{1b}{"\checkedfontfile\checkedfontfeatures"} + {\edef\checkedfontfile{"\checkedfontfile\checkedfontfeatures"}} + {\doiffoundXTXfontelse{1c}{"[\checkedfontfile]\checkedfontfeatures"} + {\edef\checkedfontfile{"[\checkedfontfile]\checkedfontfeatures"}} + {}}}% + \else\ifx\!!stringa\v!file + % force file, only file check when no spaces + \let\checkedfontfile\!!stringb + \doiffoundXTXfontelse{2a}{"[\checkedfontfile]\checkedfontfeatures"} + {\edef\checkedfontfile{"[\checkedfontfile]\checkedfontfeatures"}} + {\doiffoundXTXfontelse{2b}{"\checkedfontfile\checkedfontfeatures"} + {\edef\checkedfontfile{"\checkedfontfile\checkedfontfeatures"}} + {}}% + \else\ifx\!!stringa\v!name + % force name, always lookup by xetex itself, "" forces otf/ttf/type1 + \edef\checkedfontfile{"\!!stringb\checkedfontfeatures"}% + \ifconditional\tracexetexfonts + \writestatus\m!fonts{no checking 3a: \checkedfontfile}% + \fi + \else + % whatever, maybe even xetex spec, forget about features + \edef\checkedfontfile{"\!!stringa\!!stringb"}% + \ifconditional\tracexetexfonts + \writestatus\m!fonts{no checking 3b: \checkedfontfile}% + \fi + \fi\fi\fi} + +\def\checkfontfilename% -- todo: integrate so that we call do.. directly + {\expandafter\docheckfontfilename\fontfile*\empty*\relax} + +\def\docheckfontfilename#1*#2#3*#4\relax % class overrules file + {\edef\checkedfontfeatures + {\expandafter\ifx\csname\fontclass\s!features\endcsname\empty + \ifx\@@fontfeatures\empty\ifx#2\empty\else#2#3\fi\else\@@fontfeatures\fi + \else\expandafter\ifx\csname\fontclass\s!features\endcsname\relax % redundant, will go away + \ifx\@@fontfeatures\empty\ifx#2\empty\else#2#3\fi\else\@@fontfeatures\fi + \else + \csname\fontclass\s!features\endcsname + \fi\fi}% + \ifx\checkedfontfeatures\empty + % done + \else + \edef\checkedfontfeatures{\executeifdefined{\??fa\checkedfontfeatures}\empty}% + \ifx\checkedfontfeatures\empty + % done + \else + \let\convertedfontfeatures\empty + \processcommacommand[\checkedfontfeatures]\doconvertfontfeatures % raw + \ifx\convertedfontfeatures\empty + \let\checkedfontfeatures\empty + \else + \edef\checkedfontfeatures{:\convertedfontfeatures}% + \fi + \fi + \fi + \docheckfontfilenameprefix#1:\empty:\empty\relax + \doshowcheckedfontfeatures} + +\def\dodoconvertfontfeatures#1=#2#3=#4\relax + {\ifx#2\empty + % invalid feature + \else\ifcsname @xtx@#1@#2#3\endcsname + \expandafter\ifx\csname @xtx@#1@#2#3\endcsname\empty\else + \edef\convertedfontfeatures{\convertedfontfeatures\csname @xtx@#1@#2#3\endcsname;}% + \fi + \else + \edef\!!stringa{#1}% + \edef\!!stringb{#2#3}% + \edef\convertedfontfeatures + {\convertedfontfeatures + \ifx\!!stringb\v!yes + +\!!stringa + \else\ifx\!!stringb\v!no + -\!!stringa + \else + \!!stringa=\!!stringb + \fi\fi;}% + \fi\fi} + +\def\doconvertfontfeatures#1% + {\dodoconvertfontfeatures#1=\empty=\relax} + +\def\remapfontfeature #1 #2 #3 {\setevalue{@xtx@#1@#2}{#3}} + +% this may move to another file, maybe font-xtx + +\remapfontfeature tlig yes mapping=tlig +%remapfontfeature tlig no mapping= +\remapfontfeature trep yes {} +\remapfontfeature trep no {} +\remapfontfeature texligatures yes mapping=tlig +%remapfontfeature texligatures no mapping= +%remapfontfeature texquotes yes mapping=tex-text +%remapfontfeature texquotes no mapping= + +%D Variants: + +\unexpanded\def\variant[#1]% + {\dosetscaledfont + \font\variantfont\truefontname{\fontstringA\fontstylesuffix\fontvariant\fontstringA{#1}} at \scaledfont + \variantfont} + +%D Possible optimizations: + +% \def\updatefontparameters +% {\edef\@@fontfeatures{\truefontdata\fontfile\s!features}% +% \edef\@@fontskewchar{\truefontdata\fontfile\s!skewchar}} + +% \def\setfontcharacteristics +% {\updatefontparameters % redundant, will go away, faster too +% \the\everyfont} + +% \let\synchronizepatternswithfont\relax + +%D Names: + +% We need to move the feature into the filename else it may be +% overloaded by another reference. For instance the definition of +% a regular and caps variant can use the same font. + +% We could use an indirect method ... store in 'array' and refer to +% slot. + +\def\definefontsynonym[#1]#2[#3]% + {\edef\@@fontname{#1}% + \edef\@@fontfile{#3}% + \doifnextoptionalelse\dodefinefontsynonym\nodefinefontsynonym} + +\def\nodefinefontsynonym + {\@EA\let\csname\??ff\fontclass\@@fontname\endcsname\@@fontfile} + +\def\dodefinefontsynonym[#1]% + {\edef\@@fontdata{#1}% + \ifx\@@fontdata\empty + \nodefinefontsynonym + \else + \ifx\fontclass\empty + \getfontparameters + \else + \getglobalfontparameters + \fi + \ifcsname\??ff\@@fontfile\s!features\endcsname + \@EA\edef\csname\??ff\fontclass\@@fontname\endcsname{\@@fontfile*\csname\??ff\@@fontfile\s!features\endcsname}% + \@EA\let\csname\??ff\@@fontfile\s!features\endcsname\undefined + \else + \nodefinefontsynonym + \fi + \fi} + +\let\definefontfile\definefontsynonym % dedicated to Taco Hoekwater + +% simple version +% +% \def\truefontname#1% +% {\@EA\dotruefontname#1*\relax} +% +% \def\dotruefontname#1*#2\relax +% {\ifcsname\??ff\fontclass#1\endcsname +% \@EA\truefontname\csname\??ff\fontclass#1\endcsname +% \else\ifcsname\??ff#1\endcsname +% \@EA\truefontname\csname\??ff#1\endcsname +% \else +% #1% +% \fi\fi} +% +% last counts +% +% \def\truefontname#1% +% {\@EA\dotruefontname#1*\empty*\relax} +% +% \def\dotruefontname#1*#2#3*#4\relax +% {\ifcsname\??ff\fontclass#1\endcsname +% \ifx#2\empty +% \@EA\truefontname\csname\??ff\fontclass#1\endcsname +% \else +% \@EA\truefontname\csname\??ff\fontclass#1\endcsname*#2#3% +% \fi +% \else\ifcsname\??ff#1\endcsname +% \ifx#2\empty +% \@EA\truefontname\csname\??ff#1\endcsname +% \else +% \@EA\truefontname\csname\??ff#1\endcsname*#2#3% +% \fi +% \else +% \ifx#2\empty +% #1% +% \else +% #1*#2#3% +% \fi +% \fi\fi} +% +% first counts + +\def\truefontname#1% + {\@EA\dotruefontname#1*\empty*\relax} + +\def\dotruefontname#1*#2#3*#4\relax + {\ifcsname\??ff\fontclass#1\endcsname + \ifx#2\empty + \@EA\truefontname\csname\??ff\fontclass#1\endcsname + \else + \@EA\redotruefontname\csname\??ff\fontclass#1\endcsname*#2#3% + \fi + \else\ifcsname\??ff#1\endcsname + \ifx#2\empty + \@EA\truefontname\csname\??ff#1\endcsname + \else + \@EA\redotruefontname\csname\??ff#1\endcsname*#2#3% + \fi + \else + #1\ifx#2\empty\else*#2#3\fi + \fi\fi} + +\def\redotruefontname#1% + {\@EA\dodotruefontname#1*\relax} + +\def\dodotruefontname#1*#2\relax + {\ifcsname\??ff\fontclass#1\endcsname + \@EA\redotruefontname\csname\??ff\fontclass#1\endcsname + \else\ifcsname\??ff#1\endcsname + \@EA\redotruefontname\csname\??ff#1\endcsname + \else + #1% + \fi\fi} + +%D Default: + +\def\defaultfontfile{file:lmmono10-regular} + +%D Maybe: + +% \def\updatefontparameters +% {\edef\@@fontfeatures{\truefontdata\fontfile \s!features}% +% \edef\@@fontskewchar{\truefontdata\fontfile \s!skewchar}} + +% \def\setfontcharacteristics +% {%\updatefontparameters % redundant, will go away, faster too +% \the\everyfont +% \synchronizepatternswithfont} + +\protect \endinput + +% \starttypescript[serif] [myzhfont] +% \definefontsynonym [Serif] [file:SimSun] +% \definefontsynonym [SerifBold] [file:SimSun] +% \definefontsynonym [SerifItalic] [file:SimSun] +% \definefontsynonym [SerifBoldItalic] [file:SimSun] +% \stoptypescript +% \starttypescript[sans] [myzhfont] +% \definefontsynonym [Sans] [file:SimSun] +% \definefontsynonym [SansBold] [file:SimSun] +% \definefontsynonym [SansItalic] [file:SimSun] +% \definefontsynonym [SansBoldItalic] [file:SimSun] +% \stoptypescript +% \starttypescript[mono] [myzhfont] +% \definefontsynonym [Mono] [file:SimSun] +% \definefontsynonym [MonoBold] [file:SimSun] +% \definefontsynonym [MonoItalic] [file:SimSun] +% \definefontsynonym [MonoBoldItalic] [file:SimSun] +% \stoptypescript +% \definetypeface [myzhfont] [rm] [serif][myzhfont] [default] +% \definetypeface [myzhfont] [ss] [sans] [myzhfont] [default] +% \definetypeface [myzhfont] [tt] [mono] [myzhfont] [default] + +% \starttext +% % on windows: make sure fonts.conf has no cache mentioned +% % +% % 64 sec xetex, 11 sec luatex (56 sec xetex when \nobigmath) +% % +% \setupbodyfont[myzhfont] \dorecurse{10000}{{hello {\switchtobodyfont[myzhfont] 你好}}\par} +% % +% % 67 sec xetex, 11.5 sec luatex +% % +% % \dorecurse{10000}{{hello {\switchtobodyfont[myzhfont] 你好}}\par} +% % +% % 5 sec xetex, 7 sec luatex +% % +% % \setupbodyfont[myzhfont] \dorecurse{10000}{{hello {你好}}\par} +% % +% % 5 sec xetex, 7 sec luatex +% % +% % \setupbodyfont[myzhfont] \dorecurse{10000}{{\bf hello {你好}}\par} +% \stoptext + diff --git a/tex/context/base/hand-ini.mkii b/tex/context/base/hand-ini.mkii index 59c98fa06..42d248df6 100644 --- a/tex/context/base/hand-ini.mkii +++ b/tex/context/base/hand-ini.mkii @@ -16,61 +16,21 @@ \unprotect -\startmessages dutch library: handlings - title: handling - 1: font afhandeling -- - 2: font afhandeling -- wordt geladen - 3: onbekende font afhandeling -- -\stopmessages - -\startmessages english library: handlings - title: handling - 1: font handling -- - 2: font handling -- is loaded - 3: unknown font handling -- -\stopmessages - -\startmessages german library: handlings % to do - title: handling - 1: Font Verarbeitung -- - 2: Font Verarbeitung -- ist geladen - 3: unknown font handling -- -\stopmessages - -\startmessages czech library: handlings % to do - title: handling - 1: font handling -- - 2: font handling -- is loaded - 3: unknown font handling -- -\stopmessages - -\startmessages italian library: handlings % to do - title: handling - 1: font handling -- - 2: font handling -- is loaded - 3: unknown font handling -- -\stopmessages - -\startmessages norwegian library: handlings % to do - title: handling - 1: font handling -- - 2: font handling -- is loaded - 3: unknown font handling -- -\stopmessages - -\startmessages romanian library: handlings % to do - title: handling - 1: font handling -- - 2: font handling -- is loaded - 3: unknown font handling -- -\stopmessages - -\startmessages french library: handlings - title: manipulation - 1: manipulation -- de police - 2: la manipulation -- de police est chargée - 3: manipulation -- inconnue de police -\stopmessages +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved \newif\iftracefonthandling % \tracefonthandlingtrue @@ -81,8 +41,6 @@ % much in common with hz/protruding defs % todo: fix others -\let\normalchar\char % also done in enco-ini - \def\dosetsomehandling#1#2#3 #4 % no define since directly set {\ifskiphandlingdef \else \doifnumberelse{\string#2} @@ -105,17 +63,6 @@ \let\char\normalchar}}% \fi} -% \def\dosettriplethandling#1#2#3 #4 #5 #6 % no define since directly set -% {\ifskiphandlingdef \else -% \doifnumberelse{\string#2} -% {#1{#2#3}{#4}{#5}{#6}} -% {\doifelsenothing{#3} -% {#1{`#2}{#4}{#5}{#6}} -% {\let\char\empty -% \doifnumberelse{\csname#2#3\endcsname}{#1{\csname#2#3\endcsname}{#4}{#5}{#6}}\donothing -% \let\char\normalchar}}% -% \fi} - \def\dosetquartethandling#1#2#3 #4 #5 #6 #7 % no define since directly set {\ifskiphandlingdef \else \doifnumberelse{\string#2} @@ -127,14 +74,6 @@ \let\char\normalchar}}% \fi} - -% \def\doinhsomehandling#1#2 #3 % -% {\ifskiphandlingdef \else -% \let\char\empty -% \doifnumberelse{\csname#2\endcsname}{#1{\csname#2\endcsname}{`#3}}\donothing -% \let\char\normalchar -% \fi} - \def\doinhsomehandling#1#2#3 #4 % to be checked {\ifskiphandlingdef \else \if#3\relax\relax diff --git a/tex/context/base/hand-ini.mkiv b/tex/context/base/hand-ini.mkiv index 527c32da7..41e9db415 100644 --- a/tex/context/base/hand-ini.mkiv +++ b/tex/context/base/hand-ini.mkiv @@ -36,7 +36,7 @@ \appendtoks \disableadjusting \to \everyforgetall % Here or not here? \appendtoks \disableprotruding \to \everyforgetall % Here or not here? -\def\startfonthandling #1{\fonthandlingerror\gobbleuntil\stopfonthandling} % can't happen +\def\startfonthandling #1{\fonthandlingerror\fonthandlingerror\gobbleuntil\stopfonthandling} % can't happen \def\definefonthandling {\dotripleempty\dodefinefonthandling} \def\setupfonthandling {\dodoubleempty\dosetupfonthandling } \def\dodefinefonthandling[#1][#2][#3]{\fonthandlingerror} diff --git a/tex/context/base/hand-ini.tex b/tex/context/base/hand-ini.tex deleted file mode 100644 index 4d19b5284..000000000 --- a/tex/context/base/hand-ini.tex +++ /dev/null @@ -1,18 +0,0 @@ -%D \module -%D [ file=hand-ini, % moved from enco-ini / pro -%D version=2000.12.27, % 1998.12.03, -%D title=\CONTEXT\ Handling Macros, -%D subtitle=Initialization, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\writestatus{loading}{Context Handling Macros (ini)} - -\loadmarkfile{hand-ini} - -\endinput diff --git a/tex/context/base/java-ini.mkii b/tex/context/base/java-ini.mkii new file mode 100644 index 000000000..e929da108 --- /dev/null +++ b/tex/context/base/java-ini.mkii @@ -0,0 +1,713 @@ +%D \module +%D [ file=java-ini, +%D version=1998.01.30, +%D title=\CONTEXT\ JavaScript Macros, +%D subtitle=Initialization, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt JavaScript Macros / Initialization} + +% BUG: preamble zonder used/used en split + +% todo: lua sanitizer + +% JavaScript support is under development. In the near future +% a slightly different model will be used. The JScode stuff +% will probably become just auto function inclusion and the +% JS_* things will disappear. First I have to find a way to +% deal with global variables so the 'uses' thing will remain. + +% ook p{ref} +% documentation should be corrected to JS( + +% Also, obeylines will be supported. + +\unprotect + +%D \JAVA\ support is not implemented as a generic support +%D module. The main reason for this is that passing system +%D variables to a \JAVASCRIPT\ is closely related to other core +%D macros. First some messages: + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + + +%D \TEX\ is not the right tool to check the \JAVA\ code; the +%D most we can do is reporting some passed variables: + +\newif\iftraceJScode \traceJScodefalse + +\let\traceJScode\traceJScodetrue + +%D A bit out of place, but not dangerous: + +\bgroup +\catcode127=\@@letter +\gdef\delcharacter{^^7f} +\egroup + +%D The number of passed variables is minimalized by setting the +%D next switch. + +\newif\ifminimalizeJScode \minimalizeJScodetrue + +%D \macros +%D {JS*} +%D +%D Because \JAVASCRIPT's are activated by the user, for +%D instance by activating on a button, their support is closely +%D related to the referencing mechanism. Integration takes +%D place by +%D +%D \starttyping +%D \goto{calculate total}[Sum()] +%D \stoptyping +%D +%D The \type{()} classify this as a script. If they are absent, +%D the keyword is treated as a normal reference. +%D +%D One can pass arguments to such a script by saying: +%D +%D \starttyping +%D \goto{calculate total}[Sum(1.5,2.3)] +%D \stoptyping +%D +%D References are passed by using the \type{R{}} classifier. +%D +%D \starttyping +%D \goto{calculate total}[Sum(1.5,2.3,R{overflow})] +%D \stoptyping +%D +%D The last call calls the script \type{Sum} and passes the +%D next set of variables: +%D +%D \starttyping +%D JS_S_1="1.5"; +%D JS_S_2="2.3"; +%D JS_R_3="overflow"; +%D JS_P_3=3; +%D \stoptyping +%D +%D The first two parameters are just strings, the third one +%D however is treated as a reference and results in passing the +%D reference (if needed this references is prefixed) and the +%D (real) page number. The alternative: +%D +%D \starttyping +%D \goto{calculate total}[JS(Sum{V{1.5},V{2.3},R{overflow}})] +%D \stoptyping +%D +%D does a verbose passing: +%D +%D \starttyping +%D JS_V_1=1.5; +%D JS_V_2=2.3; +%D JS_R_3="overflow"; +%D JS_P_3=3; +%D \stoptyping +% %D +% %D Finally we have a counter that tells\JAVA\ how many +% %D arguments were passed, +% %D +% %D \starttyping +% %D JS_N +% %D \stoptyping + +%D We will also support direct function calls. In that case +%D no intermediate variables are used. + +%D \macros +%D {startJScode} +%D +%D A piece of \JAVASCRIPT\ code is defined by saying: +%D +%D \starttyping +%D \startJScode{SomeScript} +%D var Item=this.getField("item"); +%D N=Item.getArray(); +%D Total=this.getField("total"); +%D Total.value=0; +%D for (j=0; jWe use a metatable to intercept errors. When no key is found in @@ -96,6 +98,7 @@ function string:todimen() return self else local value, unit = pattern:match(self) + print(value,unit) return value/unit end end @@ -260,7 +263,7 @@ yet be available.
--ldx]]-- function dimensions.texify() - local fti, fc = fonts and fonts.tfm and fonts.tfm.id, font and font.current + local fti, fc = fonts and fonts.ids and fonts.ids, font and font.current if fti and fc then dimenfactors["ex"] = function() return fti[fc()].ex_height end dimenfactors["em"] = function() return fti[fc()].quad end diff --git a/tex/context/base/l-dir.lua b/tex/context/base/l-dir.lua index 0a174e18a..5a726303f 100644 --- a/tex/context/base/l-dir.lua +++ b/tex/context/base/l-dir.lua @@ -1,202 +1,203 @@ --- filename : l-dir.lua --- comment : split off from luat-lib --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files +if not modules then modules = { } end modules ['l-dir'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -if not versions then versions = { } end versions['l-dir'] = 1.001 +local type = type +local find, gmatch = string.find, string.gmatch -dir = { } +dir = dir or { } -- optimizing for no string.find (*) does not save time -if lfs then do +local attributes = lfs.attributes +local walkdir = lfs.dir - local attributes = lfs.attributes - local walkdir = lfs.dir - - local function glob_pattern(path,patt,recurse,action) - local ok, scanner - if path == "/" then - ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe - else - ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe - end - if ok and type(scanner) == "function" then - if not path:find("/$") then path = path .. '/' end - for name in scanner do - local full = path .. name - local mode = attributes(full,'mode') - if mode == 'file' then - if full:find(patt) then - action(full) - end - elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then - glob_pattern(full,patt,recurse,action) +local function glob_pattern(path,patt,recurse,action) + local ok, scanner + if path == "/" then + ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe + else + ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe + end + if ok and type(scanner) == "function" then + if not find(path,"/$") then path = path .. '/' end + for name in scanner do + local full = path .. name + local mode = attributes(full,'mode') + if mode == 'file' then + if find(full,patt) then + action(full) end + elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then + glob_pattern(full,patt,recurse,action) end end end +end - dir.glob_pattern = glob_pattern +dir.glob_pattern = glob_pattern - local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V +local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V - local pattern = Ct { - [1] = (C(P(".") + P("/")^1) + C(R("az","AZ") * P(":") * P("/")^0) + Cc("./")) * V(2) * V(3), - [2] = C(((1-S("*?/"))^0 * P("/"))^0), - [3] = C(P(1)^0) - } +local pattern = Ct { + [1] = (C(P(".") + P("/")^1) + C(R("az","AZ") * P(":") * P("/")^0) + Cc("./")) * V(2) * V(3), + [2] = C(((1-S("*?/"))^0 * P("/"))^0), + [3] = C(P(1)^0) +} - local filter = Cs ( ( - P("**") / ".*" + - P("*") / "[^/]*" + - P("?") / "[^/]" + - P(".") / "%%." + - P("+") / "%%+" + - P("-") / "%%-" + - P(1) - )^0 ) +local filter = Cs ( ( + P("**") / ".*" + + P("*") / "[^/]*" + + P("?") / "[^/]" + + P(".") / "%%." + + P("+") / "%%+" + + P("-") / "%%-" + + P(1) +)^0 ) - local function glob(str,t) - if type(str) == "table" then - local t = t or { } - for _, s in ipairs(str) do - glob(s,t) - end - return t - elseif lfs.isfile(str) then +local function glob(str,t) + if type(str) == "table" then + local t = t or { } + for s=1,#str do + glob(str[s],t) + end + return t + elseif lfs.isfile(str) then + local t = t or { } + t[#t+1] = str + return t + else + local split = pattern:match(str) + if split then local t = t or { } - t[#t+1] = str + local action = action or function(name) t[#t+1] = name end + local root, path, base = split[1], split[2], split[3] + local recurse = find(base,"%*%*") + local start = root .. path + local result = filter:match(start .. base) + glob_pattern(start,result,recurse,action) return t else - local split = pattern:match(str) - if split then - local t = t or { } - local action = action or function(name) t[#t+1] = name end - local root, path, base = split[1], split[2], split[3] - local recurse = base:find("%*%*") - local start = root .. path - local result = filter:match(start .. base) - glob_pattern(start,result,recurse,action) - return t - else - return { } - end + return { } end end +end - dir.glob = glob +dir.glob = glob - --~ list = dir.glob("**/*.tif") - --~ list = dir.glob("/**/*.tif") - --~ list = dir.glob("./**/*.tif") - --~ list = dir.glob("oeps/**/*.tif") - --~ list = dir.glob("/oeps/**/*.tif") +--~ list = dir.glob("**/*.tif") +--~ list = dir.glob("/**/*.tif") +--~ list = dir.glob("./**/*.tif") +--~ list = dir.glob("oeps/**/*.tif") +--~ list = dir.glob("/oeps/**/*.tif") - local function globfiles(path,recurse,func,files) -- func == pattern or function - if type(func) == "string" then - local s = func -- alas, we need this indirect way - func = function(name) return name:find(s) end - end - files = files or { } - for name in walkdir(path) do - if name:find("^%.") then - --- skip - else - local mode = attributes(name,'mode') - if mode == "directory" then - if recurse then - globfiles(path .. "/" .. name,recurse,func,files) - end - elseif mode == "file" then - if func then - if func(name) then - files[#files+1] = path .. "/" .. name - end - else +local function globfiles(path,recurse,func,files) -- func == pattern or function + if type(func) == "string" then + local s = func -- alas, we need this indirect way + func = function(name) return find(name,s) end + end + files = files or { } + for name in walkdir(path) do + if find(name,"^%.") then + --- skip + else + local mode = attributes(name,'mode') + if mode == "directory" then + if recurse then + globfiles(path .. "/" .. name,recurse,func,files) + end + elseif mode == "file" then + if func then + if func(name) then files[#files+1] = path .. "/" .. name end + else + files[#files+1] = path .. "/" .. name end end end - return files end + return files +end - dir.globfiles = globfiles +dir.globfiles = globfiles - -- t = dir.glob("c:/data/develop/context/sources/**/????-*.tex") - -- t = dir.glob("c:/data/develop/tex/texmf/**/*.tex") - -- t = dir.glob("c:/data/develop/context/texmf/**/*.tex") - -- t = dir.glob("f:/minimal/tex/**/*") - -- print(dir.ls("f:/minimal/tex/**/*")) - -- print(dir.ls("*.tex")) +-- t = dir.glob("c:/data/develop/context/sources/**/????-*.tex") +-- t = dir.glob("c:/data/develop/tex/texmf/**/*.tex") +-- t = dir.glob("c:/data/develop/context/texmf/**/*.tex") +-- t = dir.glob("f:/minimal/tex/**/*") +-- print(dir.ls("f:/minimal/tex/**/*")) +-- print(dir.ls("*.tex")) - function dir.ls(pattern) - return table.concat(glob(pattern),"\n") - end +function dir.ls(pattern) + return table.concat(glob(pattern),"\n") +end - --~ mkdirs("temp") - --~ mkdirs("a/b/c") - --~ mkdirs(".","/a/b/c") - --~ mkdirs("a","b","c") +--~ mkdirs("temp") +--~ mkdirs("a/b/c") +--~ mkdirs(".","/a/b/c") +--~ mkdirs("a","b","c") - local make_indeed = true -- false +local make_indeed = true -- false - if string.find(os.getenv("PATH"),";") then +if string.find(os.getenv("PATH"),";") then - function dir.mkdirs(...) - local str, pth = "", "" - for _, s in ipairs({...}) do - if s ~= "" then - if str ~= "" then - str = str .. "/" .. s - else - str = s - end + function dir.mkdirs(...) + local str, pth = "", "" + for _, s in ipairs({...}) do + if s ~= "" then + if str ~= "" then + str = str .. "/" .. s + else + str = s end end - local first, middle, last - local drive = false - first, middle, last = str:match("^(//)(//*)(.*)$") + end + local first, middle, last + local drive = false + first, middle, last = str:match("^(//)(//*)(.*)$") + if first then + -- empty network path == local path + else + first, last = str:match("^(//)/*(.-)$") if first then - -- empty network path == local path + middle, last = str:match("([^/]+)/+(.-)$") + if middle then + pth = "//" .. middle + else + pth = "//" .. last + last = "" + end else - first, last = str:match("^(//)/*(.-)$") + first, middle, last = str:match("^([a-zA-Z]:)(/*)(.-)$") if first then - middle, last = str:match("([^/]+)/+(.-)$") - if middle then - pth = "//" .. middle - else - pth = "//" .. last - last = "" - end + pth, drive = first .. middle, true else - first, middle, last = str:match("^([a-zA-Z]:)(/*)(.-)$") - if first then - pth, drive = first .. middle, true - else - middle, last = str:match("^(/*)(.-)$") - if not middle then - last = str - end + middle, last = str:match("^(/*)(.-)$") + if not middle then + last = str end end end - for s in last:gmatch("[^/]+") do - if pth == "" then - pth = s - elseif drive then - pth, drive = pth .. s, false - else - pth = pth .. "/" .. s - end - if make_indeed and not lfs.isdir(pth) then - lfs.mkdir(pth) - end + end + for s in gmatch(last,"[^/]+") do + if pth == "" then + pth = s + elseif drive then + pth, drive = pth .. s, false + else + pth = pth .. "/" .. s + end + if make_indeed and not lfs.isdir(pth) then + lfs.mkdir(pth) end - return pth, (lfs.isdir(pth) == true) end + return pth, (lfs.isdir(pth) == true) + end --~ print(dir.mkdirs("","","a","c")) --~ print(dir.mkdirs("a")) @@ -210,79 +211,79 @@ if lfs then do --~ print(dir.mkdirs("///a/b/c")) --~ print(dir.mkdirs("a/bbb//ccc/")) - function dir.expand_name(str) - local first, nothing, last = str:match("^(//)(//*)(.*)$") - if first then - first = lfs.currentdir() .. "/" - first = first:gsub("\\","/") - end - if not first then - first, last = str:match("^(//)/*(.*)$") - end - if not first then - first, last = str:match("^([a-zA-Z]:)(.*)$") - if first and not last:find("^/") then - local d = lfs.currentdir() - if lfs.chdir(first) then - first = lfs.currentdir() - first = first:gsub("\\","/") - end - lfs.chdir(d) + function dir.expand_name(str) + local first, nothing, last = str:match("^(//)(//*)(.*)$") + if first then + first = lfs.currentdir() .. "/" + first = first:gsub("\\","/") + end + if not first then + first, last = str:match("^(//)/*(.*)$") + end + if not first then + first, last = str:match("^([a-zA-Z]:)(.*)$") + if first and not find(last,"^/") then + local d = lfs.currentdir() + if lfs.chdir(first) then + first = lfs.currentdir() + first = first:gsub("\\","/") end + lfs.chdir(d) end - if not first then - first, last = lfs.currentdir(), str - first = first:gsub("\\","/") - end - last = last:gsub("//","/") - last = last:gsub("/%./","/") - last = last:gsub("^/*","") - first = first:gsub("/*$","") - if last == "" then - return first - else - return first .. "/" .. last - end end + if not first then + first, last = lfs.currentdir(), str + first = first:gsub("\\","/") + end + last = last:gsub("//","/") + last = last:gsub("/%./","/") + last = last:gsub("^/*","") + first = first:gsub("/*$","") + if last == "" then + return first + else + return first .. "/" .. last + end + end - else +else - function dir.mkdirs(...) - local str, pth = "", "" - for _, s in ipairs({...}) do - if s ~= "" then - if str ~= "" then - str = str .. "/" .. s - else - str = s - end + function dir.mkdirs(...) + local str, pth = "", "" + for _, s in ipairs({...}) do + if s ~= "" then + if str ~= "" then + str = str .. "/" .. s + else + str = s end end - str = str:gsub("/+","/") - if str:find("^/") then - pth = "/" - for s in str:gmatch("[^/]+") do - local first = (pth == "/") - if first then - pth = pth .. s - else - pth = pth .. "/" .. s - end - if make_indeed and not first and not lfs.isdir(pth) then - lfs.mkdir(pth) - end - end - else - pth = "." - for s in str:gmatch("[^/]+") do + end + str = str:gsub("/+","/") + if find(str,"^/") then + pth = "/" + for s in gmatch(str,"[^/]+") do + local first = (pth == "/") + if first then + pth = pth .. s + else pth = pth .. "/" .. s - if make_indeed and not lfs.isdir(pth) then - lfs.mkdir(pth) - end + end + if make_indeed and not first and not lfs.isdir(pth) then + lfs.mkdir(pth) + end + end + else + pth = "." + for s in gmatch(str,"[^/]+") do + pth = pth .. "/" .. s + if make_indeed and not lfs.isdir(pth) then + lfs.mkdir(pth) end end - return pth, (lfs.isdir(pth) == true) end + return pth, (lfs.isdir(pth) == true) + end --~ print(dir.mkdirs("","","a","c")) --~ print(dir.mkdirs("a")) @@ -292,17 +293,15 @@ if lfs then do --~ print(dir.mkdirs("///a/b/c")) --~ print(dir.mkdirs("a/bbb//ccc/")) - function dir.expand_name(str) - if not str:find("^/") then - str = lfs.currentdir() .. "/" .. str - end - str = str:gsub("//","/") - str = str:gsub("/%./","/") - return str + function dir.expand_name(str) + if not find(str,"^/") then + str = lfs.currentdir() .. "/" .. str end - + str = str:gsub("//","/") + str = str:gsub("/%./","/") + return str end - dir.makedirs = dir.mkdirs +end -end end +dir.makedirs = dir.mkdirs diff --git a/tex/context/base/l-file.lua b/tex/context/base/l-file.lua index ae4cd426a..0782ac676 100644 --- a/tex/context/base/l-file.lua +++ b/tex/context/base/l-file.lua @@ -1,23 +1,24 @@ --- filename : l-file.lua --- comment : split off from luat-lib --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files +if not modules then modules = { } end modules ['l-file'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -if not versions then versions = { } end versions['l-file'] = 1.001 +-- needs a cleanup -if not file then file = { } end +file = file or { } local concat = table.concat +local find, gmatch, match, gsub = string.find, string.gmatch, string.match, string.gsub function file.removesuffix(filename) - return (filename:gsub("%.[%a%d]+$","")) + return (gsub(filename,"%.[%a%d]+$","")) end -file.stripsuffix = file.removesuffix - function file.addsuffix(filename, suffix) - if not filename:find("%.[%a%d]+$") then + if not find(filename,"%.[%a%d]+$") then return filename .. "." .. suffix else return filename @@ -25,23 +26,23 @@ function file.addsuffix(filename, suffix) end function file.replacesuffix(filename, suffix) - return (filename:gsub("%.[%a%d]+$","")) .. "." .. suffix + return (gsub(filename,"%.[%a%d]+$","")) .. "." .. suffix end -function file.dirname(name) - return name:match("^(.+)[/\\].-$") or "" +function file.dirname(name,default) + return match(name,"^(.+)[/\\].-$") or (default or "") end function file.basename(name) - return name:match("^.+[/\\](.-)$") or name + return match(name,"^.+[/\\](.-)$") or name end function file.nameonly(name) - return ((name:match("^.+[/\\](.-)$") or name):gsub("%..*$","")) + return (gsub(match(name,"^.+[/\\](.-)$") or name,"%..*$","")) end function file.extname(name) - return name:match("^.+%.([^/\\]-)$") or "" + return match(name,"^.+%.([^/\\]-)$") or "" end file.suffix = file.extname @@ -54,40 +55,20 @@ file.suffix = file.extname function file.join(...) local pth = concat({...},"/") - pth = pth:gsub("\\","/") - local a, b = pth:match("^(.*://)(.*)$") + pth = gsub(pth,"\\","/") + local a, b = match(pth,"^(.*://)(.*)$") if a and b then - return a .. b:gsub("//+","/") + return a .. gsub(b,"//+","/") end - a, b = pth:match("^(//)(.*)$") + a, b = match(pth,"^(//)(.*)$") if a and b then - return a .. b:gsub("//+","/") - end - return (pth:gsub("//+","/")) -end - -function file.is_writable(name) - local f = io.open(name, 'w') - if f then - f:close() - return true - else - return false - end -end - -function file.is_readable(name) - local f = io.open(name,'r') - if f then - f:close() - return true - else - return false + return a .. gsub(b,"//+","/") end + return (gsub(pth,"//+","/")) end function file.iswritable(name) - local a = lfs.attributes(name) + local a = lfs.attributes(name) or lfs.attributes(file.dirname(name,".")) return a and a.permissions:sub(2,2) == "w" end @@ -96,24 +77,18 @@ function file.isreadable(name) return a and a.permissions:sub(1,1) == "r" end ---~ function file.split_path(str) ---~ if str:find(';') then ---~ return str:splitchr(";") ---~ else ---~ return str:splitchr(io.pathseparator) ---~ end ---~ end +file.is_readable = file.isreadable +file.is_writable = file.iswritable -- todo: lpeg function file.split_path(str) local t = { } - str = str:gsub("\\", "/") - str = str:gsub("(%a):([;/])", "%1\001%2") - for name in str:gmatch("([^;:]+)") do + str = gsub(str,"\\", "/") + str = gsub(str,"(%a):([;/])", "%1\001%2") + for name in gmatch(str,"([^;:]+)") do if name ~= "" then - name = name:gsub("\001",":") - t[#t+1] = name + t[#t+1] = gsub(name,"\001",":") end end return t @@ -124,15 +99,15 @@ function file.join_path(tab) end function file.collapse_path(str) - str = str:gsub("/%./","/") + str = gsub(str,"/%./","/") local n, m = 1, 1 while n > 0 or m > 0 do - str, n = str:gsub("[^/%.]+/%.%.$","") - str, m = str:gsub("[^/%.]+/%.%./","") + str, n = gsub(str,"[^/%.]+/%.%.$","") + str, m = gsub(str,"[^/%.]+/%.%./","") end - str = str:gsub("([^/])/$","%1") - str = str:gsub("^%./","") - str = str:gsub("/%.$","") + str = gsub(str,"([^/])/$","%1") + str = gsub(str,"^%./","") + str = gsub(str,"/%.$","") if str == "" then str = "." end return str end @@ -145,7 +120,7 @@ end --~ print(file.collapse_path("a/b/c/../..")) function file.robustname(str) - return (str:gsub("[^%a%d%/%-%.\\]+","-")) + return (gsub(str,"[^%a%d%/%-%.\\]+","-")) end file.readdata = io.loaddata @@ -175,8 +150,6 @@ end --~ return pattern:match(name) --~ end ---~ file.stripsuffix = file.removesuffix - --~ local pattern = (noslashes^0 * slashes)^1 * lpeg.C(noslashes^1) * -1 --~ function file.basename(name) @@ -230,7 +203,6 @@ end --~ end --~ local test = file.extname ---~ local test = file.stripsuffix --~ local test = file.basename --~ local test = file.dirname --~ local test = file.addsuffix @@ -246,3 +218,41 @@ end --~ print(7,test("def.xxx","!!!")) --~ local tim = os.clock() for i=1,250000 do local ext = test("abd.def.xxx","!!!") end print(os.clock()-tim) + +-- also rewrite previous + +local letter = lpeg.R("az","AZ") + lpeg.S("_-+") +local separator = lpeg.P("://") + +local qualified = lpeg.P(".")^0 * lpeg.P("/") + letter*lpeg.P(":") + letter^1*separator + letter^1 * lpeg.P("/") +local rootbased = lpeg.P("/") + letter*lpeg.P(":") + +-- ./name ../name /name c: :// name/name + +function file.is_qualified_path(filename) + return qualified:match(filename) +end + +function file.is_rootbased_path(filename) + return rootbased:match(filename) +end + +local slash = lpeg.S("\\/") +local period = lpeg.P(".") +local drive = lpeg.C(lpeg.R("az","AZ")) * lpeg.P(":") +local path = lpeg.C(((1-slash)^0 * slash)^0) +local suffix = period * lpeg.C(lpeg.P(1-period)^0 * lpeg.P(-1)) +local base = lpeg.C((1-suffix)^0) + +local pattern = (drive + lpeg.Cc("")) * (path + lpeg.Cc("")) * (base + lpeg.Cc("")) * (suffix + lpeg.Cc("")) + +function file.splitname(str) -- returns drive, path, base, suffix + return pattern:match(str) +end + +-- function test(t) for k, v in pairs(t) do print(v, "=>", file.splitname(v)) end end +-- +-- test { "c:", "c:/aa", "c:/aa/bb", "c:/aa/bb/cc", "c:/aa/bb/cc.dd", "c:/aa/bb/cc.dd.ee" } +-- test { "c:", "c:aa", "c:aa/bb", "c:aa/bb/cc", "c:aa/bb/cc.dd", "c:aa/bb/cc.dd.ee" } +-- test { "/aa", "/aa/bb", "/aa/bb/cc", "/aa/bb/cc.dd", "/aa/bb/cc.dd.ee" } +-- test { "aa", "aa/bb", "aa/bb/cc", "aa/bb/cc.dd", "aa/bb/cc.dd.ee" } diff --git a/tex/context/base/l-io.lua b/tex/context/base/l-io.lua index 6d773c582..4b937a322 100644 --- a/tex/context/base/l-io.lua +++ b/tex/context/base/l-io.lua @@ -1,10 +1,12 @@ --- filename : l-io.lua --- comment : split off from luat-lib --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files +if not modules then modules = { } end modules ['l-io'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -if not versions then versions = { } end versions['l-io'] = 1.001 +local byte = string.byte if string.find(os.getenv("PATH"),";") then io.fileseparator, io.pathseparator = "\\", ";" @@ -12,8 +14,8 @@ else io.fileseparator, io.pathseparator = "/" , ":" end -function io.loaddata(filename) - local f = io.open(filename,'rb') +function io.loaddata(filename,textmode) + local f = io.open(filename,(textmode and 'r') or 'rb') if f then local data = f:read('*all') -- garbagecollector.check(data) @@ -71,146 +73,83 @@ function io.noflines(f) return n end -do +local nextchar = { + [ 4] = function(f) + return f:read(1,1,1,1) + end, + [ 2] = function(f) + return f:read(1,1) + end, + [ 1] = function(f) + return f:read(1) + end, + [-2] = function(f) + local a, b = f:read(1,1) + return b, a + end, + [-4] = function(f) + local a, b, c, d = f:read(1,1,1,1) + return d, c, b, a + end +} - local sb = string.byte +function io.characters(f,n) + if f then + return nextchar[n or 1], f + else + return nil, nil + end +end - local nextchar = { - [ 4] = function(f) - return f:read(1,1,1,1) - end, - [ 2] = function(f) - return f:read(1,1) - end, - [ 1] = function(f) - return f:read(1) - end, - [-2] = function(f) - local a, b = f:read(1,1) - return b, a - end, - [-4] = function(f) - local a, b, c, d = f:read(1,1,1,1) - return d, c, b, a +local nextbyte = { + [4] = function(f) + local a, b, c, d = f:read(1,1,1,1) + if d then + return byte(a), byte(b), byte(c), byte(d) + else + return nil, nil, nil, nil end - } - - function io.characters(f,n) - if f then - return nextchar[n or 1], f + end, + [2] = function(f) + local a, b = f:read(1,1) + if b then + return byte(a), byte(b) else return nil, nil end - end - -end - -do - - local sb = string.byte - ---~ local nextbyte = { ---~ [4] = function(f) ---~ local a = f:read(1) ---~ local b = f:read(1) ---~ local c = f:read(1) ---~ local d = f:read(1) ---~ if d then ---~ return sb(a), sb(b), sb(c), sb(d) ---~ else ---~ return nil, nil, nil, nil ---~ end ---~ end, ---~ [2] = function(f) ---~ local a = f:read(1) ---~ local b = f:read(1) ---~ if b then ---~ return sb(a), sb(b) ---~ else ---~ return nil, nil ---~ end ---~ end, ---~ [1] = function (f) ---~ local a = f:read(1) ---~ if a then ---~ return sb(a) ---~ else ---~ return nil ---~ end ---~ end, ---~ [-2] = function (f) ---~ local a = f:read(1) ---~ local b = f:read(1) ---~ if b then ---~ return sb(b), sb(a) ---~ else ---~ return nil, nil ---~ end ---~ end, ---~ [-4] = function(f) ---~ local a = f:read(1) ---~ local b = f:read(1) ---~ local c = f:read(1) ---~ local d = f:read(1) ---~ if d then ---~ return sb(d), sb(c), sb(b), sb(a) ---~ else ---~ return nil, nil, nil, nil ---~ end ---~ end ---~ } - - local nextbyte = { - [4] = function(f) - local a, b, c, d = f:read(1,1,1,1) - if d then - return sb(a), sb(b), sb(c), sb(d) - else - return nil, nil, nil, nil - end - end, - [2] = function(f) - local a, b = f:read(1,1) - if b then - return sb(a), sb(b) - else - return nil, nil - end - end, - [1] = function (f) - local a = f:read(1) - if a then - return sb(a) - else - return nil - end - end, - [-2] = function (f) - local a, b = f:read(1,1) - if b then - return sb(b), sb(a) - else - return nil, nil - end - end, - [-4] = function(f) - local a, b, c, d = f:read(1,1,1,1) - if d then - return sb(d), sb(c), sb(b), sb(a) - else - return nil, nil, nil, nil - end + end, + [1] = function (f) + local a = f:read(1) + if a then + return byte(a) + else + return nil end - } - - function io.bytes(f,n) - if f then - return nextbyte[n or 1], f + end, + [-2] = function (f) + local a, b = f:read(1,1) + if b then + return byte(b), byte(a) else return nil, nil end + end, + [-4] = function(f) + local a, b, c, d = f:read(1,1,1,1) + if d then + return byte(d), byte(c), byte(b), byte(a) + else + return nil, nil, nil, nil + end end +} +function io.bytes(f,n) + if f then + return nextbyte[n or 1], f + else + return nil, nil + end end function io.ask(question,default,options) diff --git a/tex/context/base/l-lpeg.lua b/tex/context/base/l-lpeg.lua index cd61dc926..88b445717 100644 --- a/tex/context/base/l-lpeg.lua +++ b/tex/context/base/l-lpeg.lua @@ -1,9 +1,12 @@ --- filename : l-lpeg.lua --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files +if not modules then modules = { } end modules ['l-lpeg'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -if not versions then versions = { } end versions['l-lpeg'] = 1.001 +local P, S, Ct, C, Cs, Cc = lpeg.P, lpeg.S, lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc --~ l-lpeg.lua : @@ -27,35 +30,33 @@ if not versions then versions = { } end versions['l-lpeg'] = 1.001 local hash = { } function lpeg.anywhere(pattern) --slightly adapted from website - return lpeg.P { lpeg.P(pattern) + 1 * lpeg.V(1) } + return P { P(pattern) + 1 * lpeg.V(1) } end function lpeg.startswith(pattern) --slightly adapted - return lpeg.P(pattern) + return P(pattern) end ---~ g = lpeg.splitter(" ",function(s) ... end) -- gmatch:lpeg = 3:2 - function lpeg.splitter(pattern, action) - return (((1-lpeg.P(pattern))^1)/action+1)^0 + return (((1-P(pattern))^1)/action+1)^0 end -- variant: --~ local parser = lpeg.Ct(lpeg.splitat(newline)) -local crlf = lpeg.P("\r\n") -local cr = lpeg.P("\r") -local lf = lpeg.P("\n") -local space = lpeg.S(" \t\f\v") -- + string.char(0xc2, 0xa0) if we want utf (cf mail roberto) +local crlf = P("\r\n") +local cr = P("\r") +local lf = P("\n") +local space = S(" \t\f\v") -- + string.char(0xc2, 0xa0) if we want utf (cf mail roberto) local newline = crlf + cr + lf local spacing = space^0 * newline -local empty = spacing * lpeg.Cc("") -local nonempty = lpeg.Cs((1-spacing)^1) * spacing^-1 +local empty = spacing * Cc("") +local nonempty = Cs((1-spacing)^1) * spacing^-1 local content = (empty + nonempty)^1 -local capture = lpeg.Ct(content^0) +local capture = Ct(content^0) function string:splitlines() return capture:match(self) @@ -70,19 +71,32 @@ lpeg.linebyline = content -- better make a sublibrary local splitters_s, splitters_m = { }, { } -function lpeg.splitat(separator,single) +local function splitat(separator,single) local splitter = (single and splitters_s[separator]) or splitters_m[separator] if not splitter then - separator = lpeg.P(separator) + separator = P(separator) if single then - local other, any = lpeg.C((1 - separator)^0), lpeg.P(1) - splitter = other * (separator * lpeg.C(any^0) + "") + local other, any = C((1 - separator)^0), P(1) + splitter = other * (separator * C(any^0) + "") splitters_s[separator] = splitter else - local other = lpeg.C((1 - separator)^0) + local other = C((1 - separator)^0) splitter = other * (separator * other)^0 splitters_m[separator] = splitter end end return splitter end + +lpeg.splitat = splitat + +local cache = { } + +function string:split(separator) + local c = cache[separator] + if not c then + c = Ct(splitat(separator)) + cache[separator] = c + end + return c:match(self) +end diff --git a/tex/context/base/l-math.lua b/tex/context/base/l-math.lua index 00b72dba5..bfb3d506b 100644 --- a/tex/context/base/l-math.lua +++ b/tex/context/base/l-math.lua @@ -1,12 +1,12 @@ --- filename : l-math.lua --- comment : split off from luat-lib --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files +if not modules then modules = { } end modules ['l-math'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -if not versions then versions = { } end versions['l-math'] = 1.001 - -local floor = math.floor +local floor, sin, cos, tan = math.floor, math.sin, math.cos, math.tan if not math.round then function math.round(x) @@ -25,3 +25,17 @@ if not math.mod then return n % m end end + +local pipi = 2*math.pi/360 + +function math.sind(d) + return sin(d*pipi) +end + +function math.cosd(d) + return cos(d*pipi) +end + +function math.tand(d) + return tan(d*pipi) +end diff --git a/tex/context/base/l-md5.lua b/tex/context/base/l-md5.lua index 4deb9bd74..8640ad54e 100644 --- a/tex/context/base/l-md5.lua +++ b/tex/context/base/l-md5.lua @@ -1,18 +1,72 @@ --- filename : l-md5.lua --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files +if not modules then modules = { } end modules ['l-md5'] = { + version = 1.001, + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -if not versions then versions = { } end versions['l-md5'] = 1.001 +-- This also provides file checksums and checkers. -if md5 then do +local gsub, format, byte = string.gsub, string.format, string.byte - local function convert(str,fmt) - return (string.gsub(md5.sum(str),".",function(chr) return string.format(fmt,string.byte(chr)) end)) +local function convert(str,fmt) + return (gsub(md5.sum(str),".",function(chr) return format(fmt,byte(chr)) end)) +end + +if not md5.HEX then function md5.HEX(str) return convert(str,"%02X") end end +if not md5.hex then function md5.hex(str) return convert(str,"%02x") end end +if not md5.dec then function md5.dec(str) return convert(str,"%03i") end end + +--~ if not md5.HEX then +--~ local function remap(chr) return format("%02X",byte(chr)) end +--~ function md5.HEX(str) return (gsub(md5.sum(str),".",remap)) end +--~ end +--~ if not md5.hex then +--~ local function remap(chr) return format("%02x",byte(chr)) end +--~ function md5.hex(str) return (gsub(md5.sum(str),".",remap)) end +--~ end +--~ if not md5.dec then +--~ local function remap(chr) return format("%03i",byte(chr)) end +--~ function md5.dec(str) return (gsub(md5.sum(str),".",remap)) end +--~ end + +file.needs_updating_threshold = 1 + +function file.needs_updating(oldname,newname) -- size modification access change + local oldtime = lfs.attributes(oldname, modification) + local newtime = lfs.attributes(newname, modification) + if newtime >= oldtime then + return false + elseif oldtime - newtime < file.needs_updating_threshold then + return false + else + return true end +end - if not md5.HEX then function md5.HEX(str) return convert(str,"%02X") end end - if not md5.hex then function md5.hex(str) return convert(str,"%02x") end end - if not md5.dec then function md5.dec(str) return convert(str,"%03i") end end +function file.checksum(name) + if md5 then + local data = io.loaddata(name) + if data then + return md5.HEX(data) + end + end + return nil +end + +function file.loadchecksum(name) + if md5 then + local data = io.loaddata(name .. ".md5") + return data and data:gsub("%s","") + end + return nil +end -end end +function file.savechecksum(name, checksum) + if not checksum then checksum = file.checksum(name) end + if checksum then + io.savedata(name .. ".md5",checksum) + return checksum + end + return nil +end diff --git a/tex/context/base/l-number.lua b/tex/context/base/l-number.lua index 180b4c544..18d488a1a 100644 --- a/tex/context/base/l-number.lua +++ b/tex/context/base/l-number.lua @@ -1,12 +1,14 @@ --- filename : l-number.lua --- comment : split off from luat-lib --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files +if not modules then modules = { } end modules ['l-number'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -if not versions then versions = { } end versions['l-number'] = 1.001 +local format = string.format -if not number then number = { } end +number = number or { } -- a,b,c,d,e,f = number.toset(100101) @@ -14,8 +16,6 @@ function number.toset(n) return (tostring(n)):match("(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?)") end -local format = string.format - function number.toevenhex(n) local s = format("%X",n) if #s % 2 == 0 then @@ -36,11 +36,10 @@ end -- -- of course dedicated "(.)(.)(.)(.)" matches are even faster -do - local one = lpeg.C(1-lpeg.S(''))^1 +local one = lpeg.C(1-lpeg.S(''))^1 - function number.toset(n) - return one:match(tostring(n)) - end +function number.toset(n) + return one:match(tostring(n)) end + diff --git a/tex/context/base/l-os.lua b/tex/context/base/l-os.lua index 1dba5262f..47b47fa4f 100644 --- a/tex/context/base/l-os.lua +++ b/tex/context/base/l-os.lua @@ -1,13 +1,12 @@ --- filename : l-os.lua --- comment : split off from luat-lib --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files +if not modules then modules = { } end modules ['l-os'] = { + version = 1.001, + comment = "companion to luat-lub.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} - ---~ print(table.serialize(os.uname())) - -if not versions then versions = { } end versions['l-os'] = 1.001 +local find = string.find function os.resultof(command) return io.popen(command,"r"):read("*all") @@ -20,7 +19,7 @@ if not os.spawn then os.spawn = os.execute end --~ os.name : windows | msdos | linux | macosx | solaris | .. | generic (new) if not io.fileseparator then - if string.find(os.getenv("PATH"),";") then + if find(os.getenv("PATH"),";") then io.fileseparator, io.pathseparator, os.platform = "\\", ";", os.type or "windows" else io.fileseparator, io.pathseparator, os.platform = "/" , ":", os.type or "unix" @@ -58,11 +57,10 @@ end os.gettimeofday = os.gettimeofday or os.clock -do - local startuptime = os.gettimeofday() - function os.runtime() - return os.gettimeofday() - startuptime - end +local startuptime = os.gettimeofday() + +function os.runtime() + return os.gettimeofday() - startuptime end --~ print(os.gettimeofday()-os.time()) @@ -70,3 +68,63 @@ end --~ print (">>",os.runtime()) --~ print(os.date("%H:%M:%S",os.gettimeofday())) --~ print(os.date("%H:%M:%S",os.time())) + +os.arch = os.arch or function() + local a = os.resultof("uname -m") or "linux" + os.arch = function() + return a + end + return a +end + +local platform + +function os.currentplatform(name,default) + if not platform then + local name = os.name or os.platform or name -- os.name is built in, os.platform is mine + if not name then + platform = default or "linux" + elseif name == "windows" or name == "mswin" or name == "win32" or name == "msdos" then + if os.getenv("PROCESSOR_ARCHITECTURE") == "AMD64" then + platform = "mswin-64" + else + platform = "mswin" + end + else + local architecture = os.arch() + if name == "linux" then + if find(architecture,"x86_64") then + platform = "linux-64" + elseif find(architecture,"ppc") then + platform = "linux-ppc" + else + platform = "linux" + end + elseif name == "macosx" then + if find(architecture,"i386") then + platform = "osx-intel" + else + platform = "osx-ppc" + end + elseif name == "sunos" then + if find(architecture,"sparc") then + platform = "solaris-sparc" + else -- if architecture == 'i86pc' + platform = "solaris-intel" + end + elseif name == "freebsd" then + if find(architecture,"amd64") then + platform = "freebsd-amd64" + else + platform = "freebsd" + end + else + platform = default or name + end + end + function os.currentplatform() + return platform + end + end + return platform +end diff --git a/tex/context/base/l-set.lua b/tex/context/base/l-set.lua index 2bcf664f8..199253ee2 100644 --- a/tex/context/base/l-set.lua +++ b/tex/context/base/l-set.lua @@ -1,59 +1,56 @@ --- filename : l-set.lua --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files +if not modules then modules = { } end modules ['l-set'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -if not versions then versions = { } end versions['l-set'] = 1.001 +set = set or { } -if not set then set = { } end +local nums = { } +local tabs = { } +local concat = table.concat -do +set.create = table.tohash - local nums = { } - local tabs = { } - local concat = table.concat - - set.create = table.tohash - - function set.tonumber(t) - if next(t) then - local s = "" - -- we could save mem by sorting, but it slows down - for k, v in pairs(t) do - if v then - -- why bother about the leading space - s = s .. " " .. k - end - end - if not nums[s] then - tabs[#tabs+1] = t - nums[s] = #tabs +function set.tonumber(t) + if next(t) then + local s = "" + -- we could save mem by sorting, but it slows down + for k, v in pairs(t) do + if v then + -- why bother about the leading space + s = s .. " " .. k end - return nums[s] - else - return 0 end - end - - function set.totable(n) - if n == 0 then - return { } - else - return tabs[n] or { } + if not nums[s] then + tabs[#tabs+1] = t + nums[s] = #tabs end + return nums[s] + else + return 0 end +end - function set.contains(n,s) - if type(n) == "table" then - return n[s] - elseif n == 0 then - return false - else - local t = tabs[n] - return t and t[s] - end +function set.totable(n) + if n == 0 then + return { } + else + return tabs[n] or { } end +end +function set.contains(n,s) + if type(n) == "table" then + return n[s] + elseif n == 0 then + return false + else + local t = tabs[n] + return t and t[s] + end end --~ local c = set.create{'aap','noot','mies'} diff --git a/tex/context/base/l-string.lua b/tex/context/base/l-string.lua index 90af72c87..ab7d314e4 100644 --- a/tex/context/base/l-string.lua +++ b/tex/context/base/l-string.lua @@ -1,137 +1,31 @@ --- filename : l-string.lua --- comment : split off from luat-lib --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files - -if not versions then versions = { } end versions['l-string'] = 1.001 - ---~ function string.split(str, pat) -- taken from the lua wiki ---~ local t = {n = 0} -- so this table has a length field, traverse with ipairs then! ---~ local fpat = "(.-)"..pat ---~ local last_end = 1 ---~ local s, e, cap = string.find(str, fpat, 1) ---~ while s ~= nil do ---~ if s~=1 or cap~="" then ---~ table.insert(t,cap) ---~ end ---~ last_end = e+1 ---~ s, e, cap = string.find(str, fpat, last_end) ---~ end ---~ if last_end<=string.len(str) then ---~ table.insert(t,(string.sub(str,last_end))) ---~ end ---~ return t ---~ end - ---~ function string:split(pat) -- taken from the lua wiki but adapted ---~ local t = { } -- self and colon usage (faster) ---~ local fpat = "(.-)"..pat ---~ local last_end = 1 ---~ local s, e, cap = self:find(fpat, 1) ---~ while s ~= nil do ---~ if s~=1 or cap~="" then ---~ t[#t+1] = cap ---~ end ---~ last_end = e+1 ---~ s, e, cap = self:find(fpat, last_end) ---~ end ---~ if last_end <= #self then ---~ t[#t+1] = self:sub(last_end) ---~ end ---~ return t ---~ end - ---~ a piece of brilliant code by Rici Lake (posted on lua list) -- only names changed ---~ ---~ function string:splitter(pat) ---~ local st, g = 1, self:gmatch("()"..pat.."()") ---~ local function splitter(self) ---~ if st then ---~ local s, f = g() ---~ local rv = self:sub(st, (s or 0)-1) ---~ st = f ---~ return rv ---~ end ---~ end ---~ return splitter, self ---~ end - -function string:splitter(pat) - -- by Rici Lake (posted on lua list) -- only names changed - -- p 79 ref man: () returns position of match - local st, g = 1, self:gmatch("()("..pat..")") - local function strgetter(self, segs, seps, sep, cap1, ...) - st = sep and seps + #sep - return self:sub(segs, (seps or 0) - 1), cap1 or sep, ... - end - local function strsplitter(self) - if st then return strgetter(self, st, g()) end - end - return strsplitter, self -end +if not modules then modules = { } end modules ['l-string'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -function string:split(separator) - local t = {} - for k in self:splitter(separator) do t[#t+1] = k end - return t -end +local sub, gsub, find, match, gmatch, format, char, byte, rep = string.sub, string.gsub, string.find, string.match, string.gmatch, string.format, string.char, string.byte, string.rep --- faster than a string:split: +if not string.split then -function string:splitchr(chr) - if #self > 0 then - local t = { } - for s in (self..chr):gmatch("(.-)"..chr) do - t[#t+1] = s + -- this will be overloaded by a faster lpeg variant + + function string:split(pattern) + if #self > 0 then + local t = { } + for s in gmatch(self..pattern,"(.-)"..pattern) do + t[#t+1] = s + end + return t + else + return { } end - return t - else - return { } end -end -function string.piecewise(str, pat, fnc) -- variant of split - for k in string.splitter(str,pat) do fnc(k) end end ---~ function string.piecewise(str, pat, fnc) -- variant of split ---~ for k in str:splitter(pat) do fnc(k) end ---~ end - ---~ do if lpeg then - ---~ -- this alternative is 30% faster esp when we cache them ---~ -- problem: no expressions - ---~ splitters = { } - ---~ function string:split(separator) ---~ if #self > 0 then ---~ local split = splitters[separator] ---~ if not split then ---~ -- based on code by Roberto ---~ local p = lpeg.P(separator) ---~ local c = lpeg.C((1-p)^0) ---~ split = lpeg.Ct(c*(p*c)^0) ---~ splitters[separator] = split ---~ end ---~ return split:match(self) ---~ else ---~ return { } ---~ end ---~ end - ---~ string.splitchr = string.split - ---~ function string:piecewise(separator,fnc) ---~ for _,v in pairs(self:split(separator)) do ---~ fnc(v) ---~ end ---~ end - ---~ end end - local chr_to_esc = { ["%"] = "%%", ["."] = "%.", @@ -145,20 +39,20 @@ local chr_to_esc = { string.chr_to_esc = chr_to_esc function string:esc() -- variant 2 - return (self:gsub("(.)",chr_to_esc)) + return (gsub(self,"(.)",chr_to_esc)) end function string:unquote() - return (self:gsub("^([\"\'])(.*)%1$","%2")) + return (gsub(self,"^([\"\'])(.*)%1$","%2")) end -function string:quote() +function string:quote() -- we could use format("%q") return '"' .. self:unquote() .. '"' end function string:count(pattern) -- variant 3 local n = 0 - for _ in self:gmatch(pattern) do + for _ in gmatch(self,pattern) do n = n + 1 end return n @@ -167,29 +61,25 @@ end function string:limit(n,sentinel) if #self > n then sentinel = sentinel or " ..." - return self:sub(1,(n-#sentinel)) .. sentinel + return sub(self,1,(n-#sentinel)) .. sentinel else return self end end function string:strip() - return (self:gsub("^%s*(.-)%s*$", "%1")) + return (gsub(self,"^%s*(.-)%s*$", "%1")) end ---~ function string.strip(str) -- slightly different ---~ return (string.gsub(string.gsub(str,"^%s*(.-)%s*$","%1"),"%s+"," ")) ---~ end - function string:is_empty() - return not self:find("%S") + return not find(find,"%S") end function string:enhance(pattern,action) local ok, n = true, 0 while ok do ok = false - self = self:gsub(pattern, function(...) + self = gsub(self,pattern, function(...) ok, n = true, n + 1 return action(...) end) @@ -197,59 +87,19 @@ function string:enhance(pattern,action) return self, n end ---~ function string:enhance(pattern,action) ---~ local ok, n = 0, 0 ---~ repeat ---~ self, ok = self:gsub(pattern, function(...) ---~ n = n + 1 ---~ return action(...) ---~ end) ---~ until ok == 0 ---~ return self, n ---~ end - ---~ function string:to_hex() ---~ if self then ---~ return (self:gsub("(.)",function(c) ---~ return string.format("%02X",c:byte()) ---~ end)) ---~ else ---~ return "" ---~ end ---~ end - ---~ function string:from_hex() ---~ if self then ---~ return (self:gsub("(..)",function(c) ---~ return string.char(tonumber(c,16)) ---~ end)) ---~ else ---~ return "" ---~ end ---~ end - -string.chr_to_hex = { } -string.hex_to_chr = { } +local chr_to_hex, hex_to_chr = { }, { } for i=0,255 do - local c, h = string.char(i), string.format("%02X",i) - string.chr_to_hex[c], string.hex_to_chr[h] = h, c + local c, h = char(i), format("%02X",i) + chr_to_hex[c], hex_to_chr[h] = h, c end ---~ function string:to_hex() ---~ if self then return (self:gsub("(.)",string.chr_to_hex)) else return "" end ---~ end - ---~ function string:from_hex() ---~ if self then return (self:gsub("(..)",string.hex_to_chr)) else return "" end ---~ end - function string:to_hex() - return ((self or ""):gsub("(.)",string.chr_to_hex)) + return (gsub(self or "","(.)",chr_to_hex)) end function string:from_hex() - return ((self or ""):gsub("(..)",string.hex_to_chr)) + return (gsub(self or "","(..)",hex_to_chr)) end if not string.characters then @@ -263,7 +113,7 @@ if not string.characters then end local function nextbyte(str, index) index = index + 1 - return (index <= #str) and index or nil, string.byte(str:sub(index,index)) + return (index <= #str) and index or nil, byte(str:sub(index,index)) end function string:bytes() return nextbyte, self, 0 @@ -271,9 +121,7 @@ if not string.characters then end ---~ function string:padd(n,chr) ---~ return self .. self.rep(chr or " ",n-#self) ---~ end +-- we can use format for this (neg n) function string:rpadd(n,chr) local m = n-#self @@ -295,8 +143,8 @@ end string.padd = string.rpadd -function is_number(str) - return str:find("^[%-%+]?[%d]-%.?[%d+]$") == 1 +function is_number(str) -- tonumber + return find(str,"^[%-%+]?[%d]-%.?[%d+]$") == 1 end --~ print(is_number("1")) @@ -308,9 +156,9 @@ end --~ print(is_number("+.1")) function string:split_settings() -- no {} handling, see l-aux for lpeg variant - if self:find("=") then + if find(self,"=") then local t = { } - for k,v in self:gmatch("(%a+)=([^%,]*)") do + for k,v in gmatch(self,"(%a+)=([^%,]*)") do t[k] = v end return t @@ -332,13 +180,49 @@ local patterns_escapes = { } function string:pattesc() - return (self:gsub(".",patterns_escapes)) + return (gsub(self,".",patterns_escapes)) end function string:tohash() local t = { } - for s in self:gmatch("([^, ]+)") do -- lpeg + for s in gmatch(self,"([^, ]+)") do -- lpeg t[s] = true end return t end + +local pattern = lpeg.Ct(lpeg.C(1)^0) + +function string:totable() + return pattern:match(self) +end + +--~ for _, str in ipairs { +--~ "1234567123456712345671234567", +--~ "a\tb\tc", +--~ "aa\tbb\tcc", +--~ "aaa\tbbb\tccc", +--~ "aaaa\tbbbb\tcccc", +--~ "aaaaa\tbbbbb\tccccc", +--~ "aaaaaa\tbbbbbb\tcccccc", +--~ } do print(string.tabtospace(str)) end + +function string.tabtospace(str,tab) + -- we don't handle embedded newlines + while true do + local s = find(str,"\t") + if s then + if not tab then tab = 7 end -- only when found + local d = tab-(s-1)%tab + if d > 0 then + str = gsub(str,"\t",rep(" ",d),1) + else + str = gsub(str,"\t","",1) + end + else + break + end + end + return str +end + diff --git a/tex/context/base/l-table.lua b/tex/context/base/l-table.lua index 23d4bed63..bfe33ff85 100644 --- a/tex/context/base/l-table.lua +++ b/tex/context/base/l-table.lua @@ -1,22 +1,22 @@ --- filename : l-table.lua --- comment : split off from luat-lib --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files - -if not versions then versions = { } end versions['l-table'] = 1.001 +if not modules then modules = { } end modules ['l-table'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} table.join = table.concat local concat, sort, insert, remove = table.concat, table.sort, table.insert, table.remove -local format = string.format +local format, find, gsub, lower, dump = string.format, string.find, string.gsub, string.lower, string.dump local getmetatable, setmetatable = getmetatable, setmetatable -local pairs, ipairs, type, next, tostring = pairs, ipairs, type, next, tostring +local type, next, tostring, ipairs = type, next, tostring, ipairs function table.strip(tab) local lst = { } for i=1,#tab do - local s = tab[i]:gsub("^%s*(.-)%s*$","%1") + local s = gsub(tab[i],"^%s*(.-)%s*$","%1") if s == "" then -- skip this one else @@ -28,7 +28,7 @@ end local function sortedkeys(tab) local srt, kind = { }, 0 -- 0=unknown 1=string, 2=number 3=mixed - for key,_ in pairs(tab) do + for key,_ in next, tab do srt[#srt+1] = key if kind == 3 then -- no further check @@ -55,7 +55,7 @@ end local function sortedhashkeys(tab) -- fast one local srt = { } - for key,_ in pairs(tab) do + for key,_ in next, tab do srt[#srt+1] = key end sort(srt) @@ -65,14 +65,25 @@ end table.sortedkeys = sortedkeys table.sortedhashkeys = sortedhashkeys +function table.sortedpairs(t) + local s = sortedhashkeys(t) -- maybe just sortedkeys + local n = 0 + local function kv(s) + n = n + 1 + local k = s[n] + return k, t[k] + end + return kv, s +end + function table.append(t, list) - for _,v in pairs(list) do + for _,v in next, list do insert(t,v) end end function table.prepend(t, list) - for k,v in pairs(list) do + for k,v in next, list do insert(t,k,v) end end @@ -81,7 +92,7 @@ function table.merge(t, ...) -- first one is target t = t or {} local lst = {...} for i=1,#lst do - for k, v in pairs(lst[i]) do + for k, v in next, lst[i] do t[k] = v end end @@ -91,7 +102,7 @@ end function table.merged(...) local tmp, lst = { }, {...} for i=1,#lst do - for k, v in pairs(lst[i]) do + for k, v in next, lst[i] do tmp[k] = v end end @@ -123,13 +134,14 @@ end local function fastcopy(old) -- fast one if old then local new = { } - for k,v in pairs(old) do + for k,v in next, old do if type(v) == "table" then new[k] = fastcopy(v) -- was just table.copy else new[k] = v end end + -- optional second arg local mt = getmetatable(old) if mt then setmetatable(new,mt) @@ -146,7 +158,7 @@ local function copy(t, tables) -- taken from lua wiki, slightly adapted if not tables[t] then tables[t] = tcopy end - for i,v in pairs(t) do -- brrr, what happens with sparse indexed + for i,v in next, t do -- brrr, what happens with sparse indexed if type(i) == "table" then if tables[i] then i = tables[i] @@ -179,7 +191,7 @@ function table.sub(t,i,j) end function table.replace(a,b) - for k,v in pairs(b) do + for k,v in next, b do a[k] = v end end @@ -201,16 +213,18 @@ end function table.tohash(t,value) local h = { } - if value == nil then value = true end - for _, v in pairs(t) do -- no ipairs here - h[v] = value + if t then + if value == nil then value = true end + for _, v in next, t do -- no ipairs here + h[v] = value + end end return h end function table.fromhash(t) local h = { } - for k, v in pairs(t) do -- no ipairs here + for k, v in next, t do -- no ipairs here if v then h[#h+1] = k end end return h @@ -234,24 +248,10 @@ local reserved = table.tohash { -- intercept a language flaw, no reserved words 'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while', } -local function key(k) - if type(k) == "number" then -- or k:find("^%d+$") then - if hexify then - return ("[0x%04X]"):format(k) - else - return "["..k.."]" - end - elseif noquotes and not reserved[k] and k:find("^%a[%a%d%_]*$") then - return k - else - return '["'..k..'"]' - end -end - local function simple_table(t) if #t > 0 then local n = 0 - for _,v in pairs(t) do + for _,v in next, t do n = n + 1 end if n == #t then @@ -261,14 +261,14 @@ local function simple_table(t) local tv = type(v) if tv == "number" then if hexify then - tt[#tt+1] = ("0x%04X"):format(v) + tt[#tt+1] = format("0x%04X",v) else - tt[#tt+1] = tostring(v) + tt[#tt+1] = tostring(v) -- tostring not needed end elseif tv == "boolean" then tt[#tt+1] = tostring(v) elseif tv == "string" then - tt[#tt+1] = ("%q"):format(v) + tt[#tt+1] = format("%q",v) else tt = nil break @@ -280,21 +280,40 @@ local function simple_table(t) return nil end +-- Because this is a core function of mkiv I moved some function calls +-- inline. +-- +-- twice as fast in a test: +-- +-- local propername = lpeg.P(lpeg.R("AZ","az","__") * lpeg.R("09","AZ","az", "__")^0 * lpeg.P(-1) ) + local function do_serialize(root,name,depth,level,indexed) if level > 0 then depth = depth .. " " if indexed then - handle(("%s{"):format(depth)) + handle(format("%s{",depth)) elseif name then - handle(("%s%s={"):format(depth,key(name))) + --~ handle(format("%s%s={",depth,key(name))) + if type(name) == "number" then -- or find(k,"^%d+$") then + if hexify then + handle(format("%s[0x%04X]={",depth,name)) + else + handle(format("%s[%s]={",depth,name)) + end + elseif noquotes and not reserved[name] and find(name,"^%a[%w%_]*$") then + handle(format("%s%s={",depth,name)) + else + handle(format("%s[%q]={",depth,name)) + end else - handle(("%s{"):format(depth)) + handle(format("%s{",depth)) end end if root and next(root) then local first, last = nil, 0 -- #root cannot be trusted here if compact then - for k,v in ipairs(root) do -- NOT: for k=1,#root do (we need to quit at nil) + -- NOT: for k=1,#root do (we need to quit at nil) + for k,v in ipairs(root) do -- can we use next? if not first then first = k end last = last + 1 end @@ -303,30 +322,30 @@ local function do_serialize(root,name,depth,level,indexed) for i=1,#sk do local k = sk[i] local v = root[k] ---~ if v == root then - -- circular ---~ else + --~ if v == root then + -- circular + --~ else local t = type(v) if compact and first and type(k) == "number" and k >= first and k <= last then if t == "number" then if hexify then - handle(("%s 0x%04X,"):format(depth,v)) + handle(format("%s 0x%04X,",depth,v)) else - handle(("%s %s,"):format(depth,v)) + handle(format("%s %s,",depth,v)) end elseif t == "string" then - if reduce and (v:find("^[%-%+]?[%d]-%.?[%d+]$") == 1) then - handle(("%s %s,"):format(depth,v)) + if reduce and (find(v,"^[%-%+]?[%d]-%.?[%d+]$") == 1) then + handle(format("%s %s,",depth,v)) else - handle(("%s %q,"):format(depth,v)) + handle(format("%s %q,",depth,v)) end elseif t == "table" then if not next(v) then - handle(("%s {},"):format(depth)) - elseif inline then + handle(format("%s {},",depth)) + elseif inline then -- and #t > 0 local st = simple_table(v) if st then - handle(("%s { %s },"):format(depth,concat(st,", "))) + handle(format("%s { %s },",depth,concat(st,", "))) else do_serialize(v,k,depth,level+1,true) end @@ -334,39 +353,102 @@ local function do_serialize(root,name,depth,level,indexed) do_serialize(v,k,depth,level+1,true) end elseif t == "boolean" then - handle(("%s %s,"):format(depth,tostring(v))) + handle(format("%s %s,",depth,tostring(v))) elseif t == "function" then if functions then - handle(('%s loadstring(%q),'):format(depth,v:dump())) + handle(format('%s loadstring(%q),',depth,dump(v))) else - handle(('%s "function",'):format(depth)) + handle(format('%s "function",',depth)) end else - handle(("%s %q,"):format(depth,tostring(v))) + handle(format("%s %q,",depth,tostring(v))) end elseif k == "__p__" then -- parent if false then - handle(("%s __p__=nil,"):format(depth)) + handle(format("%s __p__=nil,",depth)) end elseif t == "number" then - if hexify then - handle(("%s %s=0x%04X,"):format(depth,key(k),v)) + --~ if hexify then + --~ handle(format("%s %s=0x%04X,",depth,key(k),v)) + --~ else + --~ handle(format("%s %s=%s,",depth,key(k),v)) + --~ end + if type(k) == "number" then -- or find(k,"^%d+$") then + if hexify then + handle(format("%s [0x%04X]=0x%04X,",depth,k,v)) + else + handle(format("%s [%s]=%s,",depth,k,v)) + end + elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then + if hexify then + handle(format("%s %s=0x%04X,",depth,k,v)) + else + handle(format("%s %s=%s,",depth,k,v)) + end else - handle(("%s %s=%s,"):format(depth,key(k),v)) + if hexify then + handle(format("%s [%q]=0x%04X,",depth,k,v)) + else + handle(format("%s [%q]=%s,",depth,k,v)) + end end elseif t == "string" then - if reduce and (v:find("^[%-%+]?[%d]-%.?[%d+]$") == 1) then - handle(("%s %s=%s,"):format(depth,key(k),v)) + if reduce and (find(v,"^[%-%+]?[%d]-%.?[%d+]$") == 1) then + --~ handle(format("%s %s=%s,",depth,key(k),v)) + if type(k) == "number" then -- or find(k,"^%d+$") then + if hexify then + handle(format("%s [0x%04X]=%s,",depth,k,v)) + else + handle(format("%s [%s]=%s,",depth,k,v)) + end + elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then + handle(format("%s %s=%s,",depth,k,v)) + else + handle(format("%s [%q]=%s,",depth,k,v)) + end else - handle(("%s %s=%q,"):format(depth,key(k),v)) + --~ handle(format("%s %s=%q,",depth,key(k),v)) + if type(k) == "number" then -- or find(k,"^%d+$") then + if hexify then + handle(format("%s [0x%04X]=%q,",depth,k,v)) + else + handle(format("%s [%s]=%q,",depth,k,v)) + end + elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then + handle(format("%s %s=%q,",depth,k,v)) + else + handle(format("%s [%q]=%q,",depth,k,v)) + end end elseif t == "table" then if not next(v) then - handle(("%s %s={},"):format(depth,key(k))) + --~ handle(format("%s %s={},",depth,key(k))) + if type(k) == "number" then -- or find(k,"^%d+$") then + if hexify then + handle(format("%s [0x%04X]={},",depth,k)) + else + handle(format("%s [%s]={},",depth,k)) + end + elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then + handle(format("%s %s={},",depth,k)) + else + handle(format("%s [%q]={},",depth,k)) + end elseif inline then local st = simple_table(v) if st then - handle(("%s %s={ %s },"):format(depth,key(k),concat(st,", "))) + --~ handle(format("%s %s={ %s },",depth,key(k),concat(st,", "))) + if type(k) == "number" then -- or find(k,"^%d+$") then + if hexify then + handle(format("%s [0x%04X]={ %s },",depth,k,concat(st,", "))) + else + handle(format("%s [%s]={ %s },",depth,k,concat(st,", "))) + end + elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then + handle(format("%s %s={ %s },",depth,k,concat(st,", "))) + else + handle(format("%s [%q]={ %s },",depth,k,concat(st,", "))) + end else do_serialize(v,k,depth,level+1) end @@ -374,25 +456,58 @@ local function do_serialize(root,name,depth,level,indexed) do_serialize(v,k,depth,level+1) end elseif t == "boolean" then - handle(("%s %s=%s,"):format(depth,key(k),tostring(v))) + --~ handle(format("%s %s=%s,",depth,key(k),tostring(v))) + if type(k) == "number" then -- or find(k,"^%d+$") then + if hexify then + handle(format("%s [0x%04X]=%s,",depth,k,tostring(v))) + else + handle(format("%s [%s]=%s,",depth,k,tostring(v))) + end + elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then + handle(format("%s %s=%s,",depth,k,tostring(v))) + else + handle(format("%s [%q]=%s,",depth,k,tostring(v))) + end elseif t == "function" then if functions then - handle(('%s %s=loadstring(%q),'):format(depth,key(k),v:dump())) - else - handle(('%s %s="function",'):format(depth,key(k))) + --~ handle(format('%s %s=loadstring(%q),',depth,key(k),dump(v))) + if type(k) == "number" then -- or find(k,"^%d+$") then + if hexify then + handle(format("%s [0x%04X]=loadstring(%q),",depth,k,dump(v))) + else + handle(format("%s [%s]=loadstring(%q),",depth,k,dump(v))) + end + elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then + handle(format("%s %s=loadstring(%q),",depth,k,dump(v))) + else + handle(format("%s [%q]=loadstring(%q),",depth,k,dump(v))) + end end else - handle(("%s %s=%q,"):format(depth,key(k),tostring(v))) - -- handle(('%s %s=loadstring(%q),'):format(depth,key(k),string.dump(function() return v end))) + --~ handle(format("%s %s=%q,",depth,key(k),tostring(v))) + if type(k) == "number" then -- or find(k,"^%d+$") then + if hexify then + handle(format("%s [0x%04X]=%q,",depth,k,tostring(v))) + else + handle(format("%s [%s]=%q,",depth,k,tostring(v))) + end + elseif noquotes and not reserved[k] and find(k,"^%a[%w%_]*$") then + handle(format("%s %s=%q,",depth,k,tostring(v))) + else + handle(format("%s [%q]=%q,",depth,k,tostring(v))) + end end ---~ end + --~ end end end if level > 0 then - handle(("%s},"):format(depth)) + handle(format("%s},",depth)) end end +-- replacing handle by a direct t[#t+1] = ... (plus test) is not much +-- faster (0.03 on 1.00 for zapfino.tma) + local function serialize(root,name,_handle,_reduce,_noquotes,_hexify) noquotes = _noquotes hexify = _hexify @@ -410,7 +525,7 @@ local function serialize(root,name,_handle,_reduce,_noquotes,_hexify) end elseif tname == "number" then if hexify then - handle(("[0x%04X]={"):format(name)) + handle(format("[0x%04X]={",name)) else handle("[" .. name .. "]={") end @@ -561,14 +676,18 @@ function table.insert_after_value(t,value,str) end end -function table.are_equal(a,b,n,m) +local function are_equal(a,b,n,m) -- indexed if #a == #b then n = n or 1 m = m or #a for i=n,m do local ai, bi = a[i], b[i] - if (ai==bi) or (type(ai)=="table" and type(bi)=="table" and table.are_equal(ai,bi)) then - -- continue + if ai==bi then + -- same + elseif type(ai)=="table" and type(bi)=="table" then + if not are_equal(ai,bi) then + return false + end else return false end @@ -579,9 +698,30 @@ function table.are_equal(a,b,n,m) end end +local function identical(a,b) -- assumes same structure + for ka, va in next, a do + local vb = b[k] + if va == vb then + -- same + elseif type(va) == "table" and type(vb) == "table" then + if not identical(va,vb) then + return false + end + else + return false + end + end + return true +end + +table.are_equal = are_equal +table.identical = identical + +-- maybe also make a combined one + function table.compact(t) if t then - for k,v in pairs(t) do + for k,v in next, t do if not next(v) then t[k] = nil end @@ -610,7 +750,7 @@ end function table.swapped(t) local s = { } - for k, v in pairs(t) do + for k, v in next, t do s[v] = k end return s @@ -632,14 +772,14 @@ end function table.hexed(t,seperator) local tt = { } - for i=1,#t do tt[i] = ("0x%04X"):format(t[i]) end + for i=1,#t do tt[i] = format("0x%04X",t[i]) end return concat(tt,seperator or " ") end function table.reverse_hash(h) local r = { } - for k,v in pairs(h) do - r[v] = (k:gsub(" ","")):lower() + for k,v in next, h do + r[v] = lower(gsub(k," ","")) end return r end @@ -653,3 +793,19 @@ function table.reverse(t) end return tt end + +--~ function table.keys(t) +--~ local k = { } +--~ for k,_ in next, t do +--~ k[#k+1] = k +--~ end +--~ return k +--~ end + +--~ function table.keys_as_string(t) +--~ local k = { } +--~ for k,_ in next, t do +--~ k[#k+1] = k +--~ end +--~ return concat(k,"") +--~ end diff --git a/tex/context/base/l-unicode.lua b/tex/context/base/l-unicode.lua index ebd67db1c..124a1e240 100644 --- a/tex/context/base/l-unicode.lua +++ b/tex/context/base/l-unicode.lua @@ -1,14 +1,17 @@ --- filename : l-unicode.lua --- comment : split off from luat-inp --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files +if not modules then modules = { } end modules ['l-unicode'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +utf = utf or unicode.utf8 -if not versions then versions = { } end versions['l-unicode'] = 1.001 -if not unicode then unicode = { } end +local concat, utfchar, utfgsub = table.concat, utf.char, utf.gsub +local char, byte, find, bytepairs = string.char, string.byte, string.find, string.bytepairs -local concat, utfchar, utfgsub = table.concat, unicode.utf8.char, unicode.utf8.gsub -local char, byte = string.char, string.byte +unicode = unicode or { } -- 0 EF BB BF UTF-8 -- 1 FF FE UTF-16-little-endian @@ -29,17 +32,17 @@ function unicode.utftype(f) -- \000 fails ! if not str then f:seek('set') return 0 - elseif str:find("^%z%z\254\255") then + elseif find(str,"^%z%z\254\255") then return 4 - elseif str:find("^\255\254%z%z") then + elseif find(str,"^\255\254%z%z") then return 3 - elseif str:find("^\254\255") then + elseif find(str,"^\254\255") then f:seek('set',2) return 2 - elseif str:find("^\255\254") then + elseif find(str,"^\255\254") then f:seek('set',2) return 1 - elseif str:find("^\239\187\191") then + elseif find(str,"^\239\187\191") then f:seek('set',3) return 0 else @@ -67,7 +70,7 @@ function unicode.utf16_to_utf8(str, endian) -- maybe a gsub is faster or an lpeg p = 0 end end - for l,r in str:bytepairs() do + for l,r in bytepairs(str) do if r then if endian then n = l*256 + r @@ -111,7 +114,7 @@ function unicode.utf32_to_utf8(str, endian) p = 0 end end - for a,b in str:bytepairs() do + for a,b in bytepairs(str) do if a and b then if m < 0 then if endian then @@ -138,28 +141,32 @@ function unicode.utf32_to_utf8(str, endian) return result end +local function little(c) + local b = byte(c) -- b = c:byte() + if b < 0x10000 then + return char(b%256,b/256) + else + b = b - 0x10000 + local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00 + return char(b1%256,b1/256,b2%256,b2/256) + end +end + +local function big(c) + local b = byte(c) + if b < 0x10000 then + return char(b/256,b%256) + else + b = b - 0x10000 + local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00 + return char(b1/256,b1%256,b2/256,b2%256) + end +end + function unicode.utf8_to_utf16(str,littleendian) if littleendian then - return char(255,254) .. utfgsub(str,".",function(c) - local b = byte(c) -- b = c:byte() - if b < 0x10000 then - return char(b%256,b/256) - else - b = b - 0x10000 - local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00 - return char(b1%256,b1/256,b2%256,b2/256) - end - end) + return char(255,254) .. utfgsub(str,".",little) else - return char(254,255) .. utfgsub(str,".",function(c) - local b = byte(c) - if b < 0x10000 then - return char(b/256,b%256) - else - b = b - 0x10000 - local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00 - return char(b1/256,b1%256,b2/256,b2%256) - end - end) + return char(254,255) .. utfgsub(str,".",big) end end diff --git a/tex/context/base/l-url.lua b/tex/context/base/l-url.lua index 3bb2b1f11..097c94467 100644 --- a/tex/context/base/l-url.lua +++ b/tex/context/base/l-url.lua @@ -1,10 +1,13 @@ --- filename : l-url.lua --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files +if not modules then modules = { } end modules ['l-url'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -if not versions then versions = { } end versions['l-url'] = 1.001 -if not url then url = { } end +local char, gmatch = string.char, string.gmatch +local tonumber, type = tonumber, type -- from the spec (on the web): -- @@ -16,29 +19,28 @@ if not url then url = { } end -- / \ / \ -- urn:example:animal:ferret:nose -do +url = url or { } - local function tochar(s) - return string.char(tonumber(s,16)) - end +local function tochar(s) + return char(tonumber(s,16)) +end - local colon, qmark, hash, slash, percent, endofstring = lpeg.P(":"), lpeg.P("?"), lpeg.P("#"), lpeg.P("/"), lpeg.P("%"), lpeg.P(-1) +local colon, qmark, hash, slash, percent, endofstring = lpeg.P(":"), lpeg.P("?"), lpeg.P("#"), lpeg.P("/"), lpeg.P("%"), lpeg.P(-1) - local hexdigit = lpeg.R("09","AF","af") - local escaped = percent * lpeg.C(hexdigit * hexdigit) / tochar +local hexdigit = lpeg.R("09","AF","af") +local plus = lpeg.P("+") +local escaped = (plus / " ") + (percent * lpeg.C(hexdigit * hexdigit) / tochar) - local scheme = lpeg.Cs((escaped+(1-colon-slash-qmark-hash))^0) * colon + lpeg.Cc("") - local authority = slash * slash * lpeg.Cs((escaped+(1- slash-qmark-hash))^0) + lpeg.Cc("") - local path = slash * lpeg.Cs((escaped+(1- qmark-hash))^0) + lpeg.Cc("") - local query = qmark * lpeg.Cs((escaped+(1- hash))^0) + lpeg.Cc("") - local fragment = hash * lpeg.Cs((escaped+(1- endofstring))^0) + lpeg.Cc("") +local scheme = lpeg.Cs((escaped+(1-colon-slash-qmark-hash))^0) * colon + lpeg.Cc("") +local authority = slash * slash * lpeg.Cs((escaped+(1- slash-qmark-hash))^0) + lpeg.Cc("") +local path = slash * lpeg.Cs((escaped+(1- qmark-hash))^0) + lpeg.Cc("") +local query = qmark * lpeg.Cs((escaped+(1- hash))^0) + lpeg.Cc("") +local fragment = hash * lpeg.Cs((escaped+(1- endofstring))^0) + lpeg.Cc("") - local parser = lpeg.Ct(scheme * authority * path * query * fragment) - - function url.split(str) - return (type(str) == "string" and parser:match(str)) or str - end +local parser = lpeg.Ct(scheme * authority * path * query * fragment) +function url.split(str) + return (type(str) == "string" and parser:match(str)) or str end function url.hashed(str) @@ -61,7 +63,7 @@ end function url.query(str) if type(str) == "string" then local t = { } - for k, v in str:gmatch("([^&=]*)=([^&=]*)") do + for k, v in gmatch(str,"([^&=]*)=([^&=]*)") do t[k] = v end return t @@ -76,12 +78,12 @@ end --~ print(url.filename("file:///etc/test.txt")) --~ print(url.filename("/oeps.txt")) --- from the spec on the web (sort of): +--~ from the spec on the web (sort of): --~ --~ function test(str) --~ print(table.serialize(url.hashed(str))) --~ end ----~ +--~ --~ test("%56pass%20words") --~ test("file:///c:/oeps.txt") --~ test("file:///c|/oeps.txt") diff --git a/tex/context/base/l-utils.lua b/tex/context/base/l-utils.lua index fa8e31ba8..8d531711f 100644 --- a/tex/context/base/l-utils.lua +++ b/tex/context/base/l-utils.lua @@ -1,10 +1,12 @@ --- filename : l-utils.lua --- comment : split off from luat-lib --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files +if not modules then modules = { } end modules ['l-utils'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -if not versions then versions = { } end versions['l-utils'] = 1.001 +-- hm, quite unreadable if not utils then utils = { } end if not utils.merger then utils.merger = { } end @@ -69,24 +71,52 @@ function utils.merger._self_swap_(data,code) end end +--~ stripper: +--~ +--~ data = string.gsub(data,"%-%-~[^\n]*\n","") +--~ data = string.gsub(data,"\n\n+","\n") + function utils.merger._self_libs_(libs,list) - local result, f = { }, nil + local result, f, frozen = { }, nil, false + result[#result+1] = "\n" if type(libs) == 'string' then libs = { libs } end if type(list) == 'string' then list = { list } end + local foundpath = nil for _, lib in ipairs(libs) do for _, pth in ipairs(list) do - local name = string.gsub(pth .. "/" .. lib,"\\","/") - f = io.open(name) - if f then - utils.report("merging library %s",name) - result[#result+1] = f:read("*all") - f:close() - list = { pth } -- speed up the search - break + pth = string.gsub(pth,"\\","/") -- file.clean_path + utils.report("checking library path %s",pth) + local name = pth .. "/" .. lib + if lfs.isfile(name) then + foundpath = pth + end + end + if foundpath then break end + end + if foundpath then + utils.report("using library path %s",foundpath) + local right, wrong = { }, { } + for _, lib in ipairs(libs) do + local fullname = foundpath .. "/" .. lib + if lfs.isfile(fullname) then + -- right[#right+1] = lib + utils.report("merging library %s",fullname) + result[#result+1] = "do -- create closure to overcome 200 locals limit" + result[#result+1] = io.loaddata(fullname,true) + result[#result+1] = "end -- of closure" else - utils.report("no library %s",name) + -- wrong[#wrong+1] = lib + utils.report("no library %s",fullname) end end + if #right > 0 then + utils.report("merged libraries: %s",table.concat(right," ")) + end + if #wrong > 0 then + utils.report("skipped libraries: %s",table.concat(wrong," ")) + end + else + utils.report("no valid library path found") end return table.concat(result, "\n\n") end diff --git a/tex/context/base/l-xml.lua b/tex/context/base/l-xml.lua index cdb9dacc5..d6c48fe7b 100644 --- a/tex/context/base/l-xml.lua +++ b/tex/context/base/l-xml.lua @@ -10,6 +10,7 @@ if not modules then modules = { } end modules ['l-xml'] = { -- some code may move to l-xmlext -- some day we will really compile the lpaths (just construct functions) +-- todo: some things per xml file, like namespace remapping --[[ldx--The parser used here is inspired by the variant discussed in the lua book, but @@ -38,15 +39,30 @@ optimize the code.
xml = xml or { } tex = tex or { } -xml.trace_lpath = false -xml.trace_print = false -xml.trace_remap = false +local concat, remove, insert = table.concat, table.remove, table.insert +local type, next, tonumber, tostring, setmetatable, loadstring = type, next, tonumber, tostring, setmetatable, loadstring +local format, lower, gmatch, gsub, find = string.format, string.lower, string.gmatch, string.gsub, string.find -local format, concat, remove, insert, type, next = string.format, table.concat, table.remove, table.insert, type, next +--[[ldx-- +This module can be used stand alone but also inside
First a hack to enable namespace resolving. A namespace is characterized by @@ -73,7 +89,7 @@ do --ldx]]-- function xml.registerns(namespace, pattern) -- pattern can be an lpeg - check = check + lpeg.C(lpeg.P(pattern:lower())) / namespace + check = check + lpeg.C(lpeg.P(lower(pattern))) / namespace parse = lpeg.P { lpeg.P(check) + 1 * lpeg.V(1) } end @@ -88,7 +104,7 @@ do --ldx]]-- function xml.checkns(namespace,url) - local ns = parse:match(url:lower()) + local ns = parse:match(lower(url)) if ns and namespace ~= ns then xml.xmlns[namespace] = ns end @@ -106,7 +122,7 @@ do --ldx]]-- function xml.resolvens(url) - return parse:match(url:lower()) or "" + return parse:match(lower(url)) or "" end --[[ldx-- @@ -173,6 +189,9 @@ do end local function add_attribute(namespace,tag,value) + if cleanup and #value > 0 then + value = cleanup(value) -- new + end if tag == "xmlns" then xmlns[#xmlns+1] = resolvens(value) at[tag] = value @@ -245,7 +264,7 @@ dt[0] = top end end local function set_message(txt) - errorstr = "garbage at the end of the file: " .. txt:gsub("([ \n\r\t]*)","") + errorstr = "garbage at the end of the file: " .. gsub(txt,"([ \n\r\t]*)","") end local P, S, R, C, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V @@ -293,7 +312,7 @@ dt[0] = top local somecomment = C((1 - endcomment )^0) local somecdata = C((1 - endcdata )^0) - function entity(k,v) entities[k] = v end + local function entity(k,v) entities[k] = v end local begindoctype = open * P("!DOCTYPE") local enddoctype = close @@ -389,7 +408,7 @@ dt[0] = top return root and not root.error end - xml.error_handler = (logs and logs.report) or (input and input.report) or print + xml.error_handler = (logs and logs.report) or (input and logs.report) or print end @@ -535,16 +554,16 @@ do local ats = eat and next(eat) and { } -- type test maybe faster if ats then if attributeconverter then - for k,v in pairs(eat) do + for k,v in next, eat do ats[#ats+1] = format('%s=%q',k,attributeconverter(v)) end else - for k,v in pairs(eat) do + for k,v in next, eat do ats[#ats+1] = format('%s=%q',k,v) end end end - if ern and xml.trace_remap and ern ~= ens then + if ern and trace_remap and ern ~= ens then ens = ern end if ens ~= "" then @@ -640,7 +659,8 @@ do function xml.checkbom(root) -- can be made faster if root.ri then local dt, found = root.dt, false - for k,v in ipairs(dt) do + for k=1,#dt do + local v = dt[k] if type(v) == "table" and v.special and v.tg == "@pi" and v.dt:find("xml.*version=") then found = true break @@ -913,8 +933,6 @@ do local bar = P('|') local hat = P('^') local valid = R('az', 'AZ', '09') + S('_-') ---~ local name_yes = C(valid^1 + star) * colon * C(valid^1 + star) -- permits ns:* *:tg *:* ---~ local name_nop = C(P(true)) * C(valid^1) local name_yes = C(valid^1 + star) * colon * C(valid^1 + star) -- permits ns:* *:tg *:* local name_nop = Cc("*") * C(valid^1) local name = name_yes + name_nop @@ -1051,7 +1069,7 @@ do if m ~= 11 and m ~= 12 and m ~= 13 and m ~= 14 and m ~= 15 and m ~= 16 then insert(map, 1, { 16 }) end - -- print((table.serialize(map)):gsub("[ \n]+"," ")) + -- print(gsub(table.serialize(map),"[ \n]+"," ")) return map end end @@ -1068,7 +1086,7 @@ do cache[pattern] = result lpathcached = lpathcached + 1 end - if trace or xml.trace_lpath then + if trace or trace_lpath then xml.lshow(result) end return result @@ -1077,14 +1095,17 @@ do end end - function lpath_cached_patterns() + function xml.cached_patterns() return cache end - local fallbackreport = (texio and texio.write) or io.write +-- we run out of locals (limited to 200) +-- +-- local fallbackreport = (texio and texio.write) or io.write function xml.lshow(pattern,report) - report = report or fallbackreport +-- report = report or fallbackreport + report = report or (texio and texio.write) or io.write local lp = xml.lpath(pattern) if lp == false then report(" -: root\n") @@ -1116,7 +1137,8 @@ do function xml.xshow(e,...) -- also handy when report is given, use () to isolate first e local t = { ... } - local report = (type(t[#t]) == "function" and t[#t]) or fallbackreport +-- local report = (type(t[#t]) == "function" and t[#t]) or fallbackreport + local report = (type(t[#t]) == "function" and t[#t]) or (texio and texio.write) or io.write if e == nil then report("\n") elseif type(e) ~= "table" then @@ -1636,7 +1658,7 @@ do local rt, dt, dk traverse(root, lpath(pattern), function(r,d,k) rt, dt, dk = r, d, k return true end) local ekat = (dt and dt[dk] and dt[dk].at) or (rt and rt.at) - return (ekat and (ekat[arguments] or ekat[arguments:gsub("^([\"\'])(.*)%1$","%2")])) or "" + return (ekat and (ekat[arguments] or ekat[gsub(arguments,"^([\"\'])(.*)%1$","%2")])) or "" end function xml.filters.text(root,pattern,arguments) -- ?? why index, tostring slow local dtk, rt, dt, dk = xml.filters.index(root,pattern,arguments) @@ -1698,9 +1720,6 @@ do function xml.filter(root,pattern) local kind, a, b, c = parser:match(pattern) ---~ if xml.trace_lpath then ---~ print(pattern,kind,a,b,c) ---~ end if kind == 1 or kind == 3 then return (filters[b] or default_filter)(root,a,c) elseif kind == 2 then @@ -2013,7 +2032,7 @@ do end if not name then if ek.at then - for a in (attribute or "href"):gmatch("([^|]+)") do + for a in gmatch(attribute or "href","([^|]+)") do name = ek.at[a] if name then break end end @@ -2052,7 +2071,7 @@ do -- stripped else if nolines then - str = str:gsub("[ \n\r\t]+"," ") + str = gsub(str,"[ \n\r\t]+"," ") end if str == "" then -- stripped @@ -2069,9 +2088,8 @@ do end) end - function xml.rename_space(root, oldspace, newspace) -- fast variant + local function rename_space(root, oldspace, newspace) -- fast variant local ndt = #root.dt - local rename = xml.rename_space for i=1,ndt or 0 do local e = root[i] if type(e) == "table" then @@ -2083,12 +2101,14 @@ do end local edt = e.dt if edt then - rename(edt, oldspace, newspace) + rename_space(edt, oldspace, newspace) end end end end + xml.rename_space = rename_space + function xml.remap_tag(root, pattern, newtg) traverse(root, lpath(pattern), function(r,d,k) d[k].tg = newtg @@ -2167,10 +2187,12 @@ put them here instead of loading mode modules there then needed.
--ldx]]-- function xml.gsub(t,old,new) - if t.dt then - for k,v in ipairs(t.dt) do + local dt = t.dt + if dt then + for k=1,#dt do + local v = dt[k] if type(v) == "string" then - t.dt[k] = v:gsub(old,new) + dt[k] = gsub(v,old,new) else xml.gsub(v,old,new) end @@ -2195,9 +2217,9 @@ end --~ xml.escapes = { ['&'] = '&', ['<'] = '<', ['>'] = '>', ['"'] = '"' } --~ xml.unescapes = { } for k,v in pairs(xml.escapes) do xml.unescapes[v] = k end ---~ function xml.escaped (str) return str:gsub("(.)" , xml.escapes ) end ---~ function xml.unescaped(str) return str:gsub("(&.-;)", xml.unescapes) end ---~ function xml.cleansed (str) return str:gsub("<.->" , '' ) end -- "%b<>" +--~ function xml.escaped (str) return (gsub(str,"(.)" , xml.escapes )) end +--~ function xml.unescaped(str) return (gsub(str,"(&.-;)", xml.unescapes)) end +--~ function xml.cleansed (str) return (gsub(str,"<.->" , '' )) end -- "%b<>" do @@ -2229,6 +2251,10 @@ do local cleansed = Cs(((P("<") * (1-P(">"))^0 * P(">"))/"" + 1)^0) + xml.escaped_pattern = escaped + xml.unescaped_pattern = unescaped + xml.cleansed_pattern = cleansed + function xml.escaped (str) return escaped :match(str) end function xml.unescaped(str) return unescaped:match(str) end function xml.cleansed (str) return cleansed :match(str) end @@ -2273,14 +2299,14 @@ do if unicode and unicode.utf8 then return char(tonumber(s,16)) end - function utfize(root) + local function utfize(root) local d = root.dt for k=1,#d do local dk = d[k] if type(dk) == "string" then -- test prevents copying if no match - if dk:find(".-;") then - d[k] = dk:gsub("(.-);",toutf) + if find(dk,".-;") then + d[k] = gsub(dk,"(.-);",toutf) end else utfize(dk) @@ -2291,8 +2317,10 @@ do if unicode and unicode.utf8 then xml.utfize = utfize local function resolve(e) -- hex encoded always first, just to avoid mkii fallbacks - if e:find("#x") then + if find(e,"^#x") then return char(tonumber(e:sub(3),16)) + elseif find(e,"^#") then + return char(tonumber(e:sub(2))) else local ee = xml.entities[e] -- we cannot shortcut this one (is reloaded) if ee then @@ -2310,8 +2338,8 @@ do if unicode and unicode.utf8 then for k=1,#d do local dk = d[k] if type(dk) == "string" then - if dk:find("&.-;") then - d[k] = dk:gsub("&(.-);",resolve) + if find(dk,"&.-;") then + d[k] = gsub(dk,"&(.-);",resolve) end else resolve_entities(dk) @@ -2323,24 +2351,24 @@ do if unicode and unicode.utf8 then xml.resolve_entities = resolve_entities function xml.utfize_text(str) - if str:find("") then - return (str:gsub("(.-);",toutf)) + if find(str,"") then + return (gsub(str,"(.-);",toutf)) else return str end end function xml.resolve_text_entities(str) -- maybe an lpeg. maybe resolve inline - if str:find("&") then - return (str:gsub("&(.-);",resolve)) + if find(str,"&") then + return (gsub(str,"&(.-);",resolve)) else return str end end function xml.show_text_entities(str) - if str:find("&") then - return (str:gsub("&(.-);","[%1]")) + if find(str,"&") then + return (gsub(str,"&(.-);","[%1]")) else return str end @@ -2352,7 +2380,7 @@ do if unicode and unicode.utf8 then local documententities = root.entities local allentities = xml.entities if documententities then - for k, v in pairs(documententities) do + for k, v in next, documententities do allentities[k] = v end end @@ -2386,7 +2414,7 @@ end --~ --~ ]]) ---~ xml.trace_lpath = true +--~ xml.settrace("lpath",true) --~ xml.xshow(xml.first(x,"b[position() > 2 and position() < 5 and text() == 'ok']")) --~ xml.xshow(xml.first(x,"b[position() > 2 and position() < 5 and text() == upper('ok')]")) diff --git a/tex/context/base/lang-alt.tex b/tex/context/base/lang-alt.tex index d59df78bd..e45748ead 100644 --- a/tex/context/base/lang-alt.tex +++ b/tex/context/base/lang-alt.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Altaic Languages} +\writestatus{loading}{ConTeXt Language Macros / Altaic Languages} %D The framework of this module is set up by Hans Hagen while %D many of the first translations were done by Tobias. Later @@ -37,8 +37,7 @@ \c!rightquote=\upperrightsingleninequote, \c!leftquotation=\upperleftdoublesixquote, \c!rightquotation=\upperrightdoubleninequote, - \c!date={\v!year,\ ,\v!month,\ ,\v!day}, - \c!state=\v!stop] + \c!date={\v!year,\ ,\v!month,\ ,\v!day}] \installlanguage [turkish] [\s!tr] diff --git a/tex/context/base/lang-ana.tex b/tex/context/base/lang-ana.tex index 336be50f2..c108655c4 100644 --- a/tex/context/base/lang-ana.tex +++ b/tex/context/base/lang-ana.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Anatolian Languages} +\writestatus{loading}{ConTeXt Language Macros / Anatolian Languages} %D The framework of this module is set up by Hans Hagen while %D many of the first translations were done by Tobias. Later @@ -21,6 +21,4 @@ \unprotect -\protect - -\endinput +\protect \endinput diff --git a/tex/context/base/lang-ara.tex b/tex/context/base/lang-ara.tex index 91bd6ae38..3c4d3c522 100644 --- a/tex/context/base/lang-ara.tex +++ b/tex/context/base/lang-ara.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Arabic Languages} +\writestatus{loading}{ConTeXt Language Macros / Arabic Languages} \unprotect @@ -29,8 +29,7 @@ \c!rightquote=\upperrightsingleninequote, \c!leftquotation=\upperleftdoublesixquote, \c!rightquotation=\upperrightdoubleninequote, - \c!date={\v!day,\ ,\v!month,{،\ },\v!year}, - \c!state=\v!stop] % elders always preloaded! + \c!date={\v!day,\ ,\v!month,{،\ },\v!year}] \installlanguage [\s!arabic] [\s!ar] diff --git a/tex/context/base/lang-art.tex b/tex/context/base/lang-art.tex index 3f857e11e..e8be91630 100644 --- a/tex/context/base/lang-art.tex +++ b/tex/context/base/lang-art.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Artificial Languages} +\writestatus{loading}{ConTeXt Language Macros / Artificial Languages} %D The framework of this module is set up by Hans Hagen while %D many of the first translations were done by Tobias. Later @@ -23,6 +23,4 @@ \unprotect -\protect - -\endinput +\protect \endinput diff --git a/tex/context/base/lang-bal.tex b/tex/context/base/lang-bal.tex index c4e0f31f7..9b0528a27 100644 --- a/tex/context/base/lang-bal.tex +++ b/tex/context/base/lang-bal.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Baltic Languages} +\writestatus{loading}{ConTeXt Language Macros / Baltic Languages} %D The framework of this module is set up by Hans Hagen while %D many of the first translations were done by Tobias. Later @@ -23,6 +23,4 @@ \unprotect -\protect - -\endinput +\protect \endinput diff --git a/tex/context/base/lang-cel.tex b/tex/context/base/lang-cel.tex index abbeb10c6..4d93957f1 100644 --- a/tex/context/base/lang-cel.tex +++ b/tex/context/base/lang-cel.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Celtic Languages} +\writestatus{loading}{ConTeXt Language Macros / Celtic Languages} %D The framework of this module is set up by Hans Hagen while %D many of the first translations were done by Tobias. Later @@ -23,6 +23,4 @@ \unprotect -\protect - -\endinput +\protect \endinput diff --git a/tex/context/base/lang-chi.tex b/tex/context/base/lang-chi.tex index 7458268f7..278e10745 100644 --- a/tex/context/base/lang-chi.tex +++ b/tex/context/base/lang-chi.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Context Language Macros / Chinese} +\writestatus{loading}{ConTeXt Language Macros / Chinese} %D This module is coded using the \UNICODE\ support built in %D \CONTEXT. Therefore, \type {\uchar} is used instead of latin @@ -31,9 +31,7 @@ \c!rightquote=\cnencoding\cnupperrightsinglequote, \c!leftquotation=\cnencoding\cnupperleftdoublequote, \c!rightquotation=\cnencoding\cnupperrightdoublequote, - \c!date={\v!year,\cnyear,\ ,\v!month,\v!day,\cnday}, - \c!state=\v!stop] - + \c!date={\v!year,\cnyear,\ ,\v!month,\v!day,\cnday}] \setupheadtext [\s!cn] [\v!content={\cnencoding\cnencodedcontents}] \setupheadtext [\s!cn] [\v!tables={\cnencoding\cnencodedtables}] diff --git a/tex/context/base/lang-cjk.tex b/tex/context/base/lang-cjk.tex new file mode 100644 index 000000000..138f6d263 --- /dev/null +++ b/tex/context/base/lang-cjk.tex @@ -0,0 +1,328 @@ +%D \module +%D [ file=lang-chi, +%D version=2009.03.02, +%D title=\CONTEXT\ Language Macros, +%D subtitle=Chinese, +%D author={Hans Hagen \& Wang Lei}, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D Derived from \MKII\ files. + +\writestatus{loading}{ConTeXt Language Macros / CJK} + +\definesystemconstant {chinese} \definesystemconstant {cn} +\definesystemconstant {japanese} \definesystemconstant {ja} +\definesystemconstant {korean} \definesystemconstant {kr} + +\unprotect + +% Chinese + +\installlanguage + [\s!cn] + [\c!leftsentence=——, + \c!rightsentence=——, + \c!leftsubsentence=——, + \c!rightsubsentence=——, + \c!leftquote=‘, + \c!rightquote=’, + \c!leftquotation=“, + \c!rightquotation=”, + \c!date={\v!year,年,\ ,\v!month,\v!day,日}] + +\setupheadtext [\s!cn] [\v!content=目录] +\setupheadtext [\s!cn] [\v!tables=表格] +\setupheadtext [\s!cn] [\v!figures=图形] +\setupheadtext [\s!cn] [\v!graphics=图] +\setupheadtext [\s!cn] [\v!intermezzi=퉣] +\setupheadtext [\s!cn] [\v!index=索引] +\setupheadtext [\s!cn] [\v!abbreviations=缩略语] +\setupheadtext [\s!cn] [\v!logos=徽贬] +\setupheadtext [\s!cn] [\v!units=计量单位] + +\setuplabeltext [\s!cn] [\v!table=表] +\setuplabeltext [\s!cn] [\v!figure=图] +\setuplabeltext [\s!cn] [\v!intermezzo=퉣] +\setuplabeltext [\s!cn] [\v!graphic=插图] +\setuplabeltext [\s!cn] [\v!appendix=附录] +\setuplabeltext [\s!cn] [\v!part={第,部分}] +\setuplabeltext [\s!cn] [\v!chapter={第,章}] +\setuplabeltext [\s!cn] [\v!section={第,节}] +\setuplabeltext [\s!cn] [\v!line=行] +\setuplabeltext [\s!cn] [\v!lines=行] + +\setuplabeltext [\s!cn] [\v!subsection=] +\setuplabeltext [\s!cn] [\v!subsubsection=] +\setuplabeltext [\s!cn] [\v!subsubsubsection=] + +\setuplabeltext [\s!cn] [\v!january=一月] +\setuplabeltext [\s!cn] [\v!february=二月] +\setuplabeltext [\s!cn] [\v!march=三月] +\setuplabeltext [\s!cn] [\v!april=四月] +\setuplabeltext [\s!cn] [\v!may=五月] +\setuplabeltext [\s!cn] [\v!june=六月] +\setuplabeltext [\s!cn] [\v!july=七月] +\setuplabeltext [\s!cn] [\v!august=八月] +\setuplabeltext [\s!cn] [\v!september=九月] +\setuplabeltext [\s!cn] [\v!october=十月] +\setuplabeltext [\s!cn] [\v!november=十一月] +\setuplabeltext [\s!cn] [\v!december=十二月] + +\setuplabeltext [\s!cn] [\v!sunday=星期日] +\setuplabeltext [\s!cn] [\v!monday=星期一] +\setuplabeltext [\s!cn] [\v!tuesday=星期二] +\setuplabeltext [\s!cn] [\v!wednesday=星期三] +\setuplabeltext [\s!cn] [\v!thursday=星期四] +\setuplabeltext [\s!cn] [\v!friday=星期五] +\setuplabeltext [\s!cn] [\v!saturday=星期六] + +%D Japanese + +\installlanguage + [\s!ja] + [\c!leftsentence=——, + \c!rightsentence=——, + \c!leftsubsentence=——, + \c!rightsubsentence=——, + \c!leftquote=‘, + \c!rightquote=’, + \c!leftquotation=「, + \c!rightquotation=」, + \c!date={西暦,\v!year,年,\v!month,月,\v!day,日}] + +\setupheadtext [\s!ja] [\v!content=目次] +\setupheadtext [\s!ja] [\v!tables=机] +\setupheadtext [\s!ja] [\v!figures=図] +\setupheadtext [\s!ja] [\v!graphics=グラフ] +\setupheadtext [\s!ja] [\v!intermezzi=間奏曲] +\setupheadtext [\s!ja] [\v!index=目次] +\setupheadtext [\s!ja] [\v!abbreviations=略語] +\setupheadtext [\s!ja] [\v!logos=理性] +\setupheadtext [\s!ja] [\v!units=ユニッツ] + +\setuplabeltext [\s!ja] [\v!table=表] +\setuplabeltext [\s!ja] [\v!figure=図] +\setuplabeltext [\s!ja] [\v!intermezzo=間奏曲] +\setuplabeltext [\s!ja] [\v!graphic=イラスト] +\setuplabeltext [\s!ja] [\v!appendix=付録] +\setuplabeltext [\s!ja] [\v!part={第,パート}] +\setuplabeltext [\s!ja] [\v!chapter={第,章}] +\setuplabeltext [\s!ja] [\v!section={第,項}] +\setuplabeltext [\s!ja] [\v!line=線] +\setuplabeltext [\s!ja] [\v!lines=線] + +\setuplabeltext [\s!ja] [\v!subsection=] +\setuplabeltext [\s!ja] [\v!subsubsection=] +\setuplabeltext [\s!ja] [\v!subsubsubsection=] + +\setuplabeltext [\s!ja] [\v!january=1] +\setuplabeltext [\s!ja] [\v!february=2] +\setuplabeltext [\s!ja] [\v!march=3] +\setuplabeltext [\s!ja] [\v!april=4] +\setuplabeltext [\s!ja] [\v!may=5] +\setuplabeltext [\s!ja] [\v!june=6] +\setuplabeltext [\s!ja] [\v!july=7] +\setuplabeltext [\s!ja] [\v!august=8] +\setuplabeltext [\s!ja] [\v!september=9] +\setuplabeltext [\s!ja] [\v!october=10] +\setuplabeltext [\s!ja] [\v!november=11] +\setuplabeltext [\s!ja] [\v!december=12] + +\setuplabeltext [\s!ja] [\v!sunday=月曜日] +\setuplabeltext [\s!ja] [\v!monday=火曜日] +\setuplabeltext [\s!ja] [\v!tuesday=水曜日] +\setuplabeltext [\s!ja] [\v!wednesday=木曜日] +\setuplabeltext [\s!ja] [\v!thursday=金曜日] +\setuplabeltext [\s!ja] [\v!friday=土曜日] +\setuplabeltext [\s!ja] [\v!saturday=日曜日] + +%D Korean + +% todo + +\protect \endinput + +cn={ + ["abbreviations"]="缩略语", + ["appendix"]="附录", + ["april"]="四月", + ["august"]="八月", + ["chapter"]="章", + ["contents"]="目录", + ["day"]="日", + ["december"]="十二月", + ["febrary"]="二月", + ["figure"]="图", + ["figures"]="图形", + ["friday"]="星期五", + ["graphics"]="图", + ["illustration"]="插图", + ["index"]="索引", + ["intermezzo"]="퉣", + ["intro"]="第", + ["january"]="一月", + ["july"]="七月", + ["june"]="六月", + ["leftsentence"]="——", + ["leftsubsentence"]="——", + ["line"]="行", + ["logos"]="徽贬", + ["march"]="三月", + ["may"]="五月", + ["monday"]="星期一", + ["month"]="月", + ["november"]="十一月", + ["october"]="十月", + ["part"]="部分", + ["rightsentence"]="——", + ["rightsubsentence"]="——", + ["saturday"]="星期六", + ["section"]="节", + ["september"]="九月", + ["sunday"]="星期日", + ["table"]="表", + ["tables"]="表格", + ["thursday"]="星期四", + ["tuesday"]="星期二", + ["units"]="计量单位", + ["upperleftdoublequote"]="“", + ["upperleftdoublequote-v"]="『", + ["upperleftsinglequote"]="‘", + ["upperleftsinglequote-v"]="「", + ["upperrightdoublequote"]="”", + ["upperrightdoublequote-v"]="』", + ["upperrightsinglequote"]="’", + ["upperrightsinglequote-v"]="」", + ["wednesday"]="星期三", + ["year"]="年", +} + +ja={ + ["abbreviations"]="略語", + ["abstract"]="概要", + ["and"]="、", + ["answer"]="答:", + ["appendix"]="付録", + ["april"]="四月", + ["article"]="項目", + ["august"]="八月", + ["bibliography"]="参考文献", + ["book"]="ブック", + ["bridgehead"]="項", + ["bullet"]="●", + ["by"]=":", + ["caution"]="注意", + ["chapter"]="章", + ["christiandate"]="西暦", + ["colophon"]="奥付", + ["copyright"]="製作著作", + ["day"]="日", + ["december"]="十二月", + ["dedication"]="謝辞", + ["edited"]="編者", + ["editedby"]="編者:", + ["edition"]="編集", + ["endquote"]="」", + ["equation"]="式", + ["example"]="例", + ["february"]="二月", + ["figure"]="図", + ["figures"]="図", + ["friday"]="土曜日", + ["glossary"]="用語集", + ["glosssee"]="参照", + ["glossseealso"]="参照", + ["graphics"]="グラフ", + ["illustration"]="イラスト", + ["important"]="重要項目", + ["index"]="目次", + ["indexsymbols"]="シンボル", + ["intermezzo"]="間奏曲", + ["intermezzos"]="間奏曲", + ["intro"]="第", + ["january"]="一月", + ["july"]="七月", + ["june"]="六月", + ["leftsentence"]="——", + ["leftsubsentence"]="——", + ["line"]="線", + ["lines"]="線", + ["listofequations"]="式目次", + ["listofexamples"]="例目次", + ["listoffigures"]="図目次", + ["listoftables"]="表目次", + ["listofunknown"]="不明目次", + ["logos"]="理性", + ["march"]="三月", + ["may"]="五月", + ["monday"]="火曜日", + ["month"]="月", + ["msgaud"]="対象者", + ["msglevel"]="レベル", + ["msgorig"]="発信元", + ["navhome"]="ホーム", + ["navnext"]="次のページ", + ["navnextsibling"]="早送り", + ["navprev"]="前のページ", + ["navprevsibling"]="巻戻し", + ["navup"]="上に戻る", + ["nestedendquote"]="』", + ["nestedstartquote"]="『", + ["nonexistantelement"]="要素が存在しません", + ["note"]="注意", + ["notes"]="注意", + ["november"]="十一月", + ["october"]="十月", + ["pages"]="偧献", + ["part"]="パート", + ["preface"]="序文", + ["procedure"]="手順", + ["procedureformal"]="手順", + ["productionset"]="プロダクション", + ["productionsetformal"]="プロダクション", + ["published"]="発行", + ["qandadiv"]="問:、答:", + ["qandaentry"]="問:", + ["question"]="問:", + ["reference"]="参照", + ["refname"]="名前", + ["refsection"]="項", + ["refsynopsisdiv"]="概要", + ["revhistory"]="改訂履歴", + ["revision"]="改訂", + ["rightsentence"]="——", + ["rightsubsentence"]="——", + ["saturday"]="日曜日", + ["section"]="項", + ["see"]="参照", + ["seealso"]="参照", + ["separator"]="、", + ["september"]="九月", + ["set"]="設定", + ["setindex"]="目次設定", + ["sidebar"]="サイドバー", + ["simplesect"]="項", + ["singleendquote"]="’", + ["singlestartquote"]="‘", + ["startquote"]="「", + ["step"]="ステップ", + ["sunday"]="月曜日", + ["table"]="表", + ["tablenotes"]="注意", + ["tableofcontents"]="目次", + ["tables"]="机", + ["thursday"]="金曜日", + ["tip"]="ティップ", + ["tuesday"]="水曜日", + ["unexpectedelementname"]="不明な要素名", + ["units"]="ユニッツ", + ["unsupported"]="サポートしません", + ["warning"]="警告", + ["wednesday"]="木曜日", + ["year"]="年", +} diff --git a/tex/context/base/lang-ctx.tex b/tex/context/base/lang-ctx.tex index 5364c1af6..09f28dda1 100644 --- a/tex/context/base/lang-ctx.tex +++ b/tex/context/base/lang-ctx.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Context Language Macros / Generic Patterns} +\writestatus{loading}{ConTeXt Language Macros / Generic Patterns} \unprotect @@ -28,31 +28,26 @@ %D than one font encoding is in use. I can add more defaults here %D if users let me know what encoding they use. -\installlanguage [\s!nl] [\s!mapping={texnansi,ec},\s!encoding={texnansi,ec}] -\installlanguage [\s!fr] [\s!mapping={texnansi,ec},\s!encoding={texnansi,ec}] -\installlanguage [\s!de] [\s!mapping={texnansi,ec},\s!encoding={texnansi,ec}] -\installlanguage [\s!it] [\s!mapping={texnansi,ec},\s!encoding={texnansi,ec}] - -\installlanguage [\s!pt] [\s!mapping={texnansi,ec},\s!encoding={texnansi,ec}] - -\installlanguage [\s!hr] [\s!mapping=ec,\s!encoding=ec] % no il2, misses cacute characters - -\installlanguage [\s!pl] [\s!mapping={pl0,ec,qx},\s!encoding={pl0,ec,qx}] % pl0 may go -\installlanguage [\s!cs] [\s!mapping={il2,ec},\s!encoding={il2,ec}] % il2 may go -\installlanguage [\s!sk] [\s!mapping={il2,ec},\s!encoding={il2,ec}] % il2 may go -\installlanguage [\s!sl] [\s!mapping=ec,\s!encoding=ec] % il2 has gone - -\installlanguage [\s!vn] [\s!mapping=t5,\s!encoding=t5] - -\installlanguage [\s!ru] [\s!mapping=t2a,\s!encoding=t2a] +% \installlanguage [\s!nl] [\s!mapping={texnansi,ec},\s!encoding={texnansi,ec}] +% \installlanguage [\s!fr] [\s!mapping={texnansi,ec},\s!encoding={texnansi,ec}] +% \installlanguage [\s!de] [\s!mapping={texnansi,ec},\s!encoding={texnansi,ec}] +% \installlanguage [\s!it] [\s!mapping={texnansi,ec},\s!encoding={texnansi,ec}] +% \installlanguage [\s!pt] [\s!mapping={texnansi,ec},\s!encoding={texnansi,ec}] +% \installlanguage [\s!hr] [\s!mapping=ec,\s!encoding=ec] % no il2, misses cacute characters +% \installlanguage [\s!pl] [\s!mapping={pl0,ec,qx},\s!encoding={pl0,ec,qx}] % pl0 may go +% \installlanguage [\s!cs] [\s!mapping={il2,ec},\s!encoding={il2,ec}] % il2 may go +% \installlanguage [\s!sk] [\s!mapping={il2,ec},\s!encoding={il2,ec}] % il2 may go +% \installlanguage [\s!sl] [\s!mapping=ec,\s!encoding=ec] % il2 has gone +% \installlanguage [\s!vi] [\s!mapping=t5,\s!encoding=t5] +% \installlanguage [\s!ru] [\s!mapping=t2a,\s!encoding=t2a] % beware, don't use \setuplanguage here -\installlanguage[\s!gb][\s!lefthyphenmin=3,\s!righthyphenmin=3] % patterns can only handle this -\installlanguage[\s!us][\s!lefthyphenmin=2,\s!righthyphenmin=3] % patterns can only handle this +% \installlanguage[\s!gb][\s!lefthyphenmin=3,\s!righthyphenmin=3] % patterns can only handle this +% \installlanguage[\s!us][\s!lefthyphenmin=2,\s!righthyphenmin=3] % patterns can only handle this % greek -\installlanguage[\s!agr][\s!mapping=\s!agr,\s!encoding=\s!agr] +% \installlanguage[\s!agr][\s!mapping=\s!agr,\s!encoding=\s!agr] \protect \endinput diff --git a/tex/context/base/lang-cyr.tex b/tex/context/base/lang-cyr.tex index 34b5e78c3..470402bb1 100644 --- a/tex/context/base/lang-cyr.tex +++ b/tex/context/base/lang-cyr.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Cyrillic Languages} +\writestatus{loading}{ConTeXt Language Macros / Cyrillic Languages} %D The cyrillic languages always use a dedicated input regime. %D Therefore we define the labels using symbolic names. @@ -37,7 +37,8 @@ \c!leftquotation=\leftguillemot, \c!rightquotation=\rightguillemot, \c!date={\v!day,\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] + \s!mapping=t2a, + \s!encoding=t2a] \installlanguage [\s!ua] @@ -53,11 +54,12 @@ \c!leftquotation=\leftguillemot, \c!rightquotation=\rightguillemot, \c!date={\v!day,\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] - -\installlanguage [russian] [\s!ru] -\installlanguage [ukrainian] [\s!ua] + \s!patterns=\s!uk, + \s!mapping=t2a, + \s!encoding=t2a] +\installlanguage [russian] [\s!ru] +\installlanguage [ukrainian] [\s!ua] %D Labels and header texts. diff --git a/tex/context/base/lang-dis.tex b/tex/context/base/lang-dis.tex index db932d68a..f081bf4a9 100644 --- a/tex/context/base/lang-dis.tex +++ b/tex/context/base/lang-dis.tex @@ -15,7 +15,7 @@ %D use more generic pattern files, we decided to isolate these %D mappings. -\writestatus{loading}{Context Language Macros / Distribution Patterns} +\writestatus{loading}{ConTeXt Language Macros / Distribution Patterns} %D Hyphenation patterns are normally sought in filed named %D \type {lang-xx.pat}. When present on the system, those @@ -52,8 +52,8 @@ % \definefilefallback [lang-sk.pat] [skhyphen.tex,skhyph.pat] % \definefilefallback [lang-deo.pat] [dehypht.tex] -\definefilesynonym [lang-af.pat] [lang-nl.pat] -\definefilesynonym [lang-en.pat] [lang-us.pat] -\definefilesynonym [lang-en.hyp] [lang-us.hyp] +% \definefilesynonym [lang-af.pat] [lang-nl.pat] +% \definefilesynonym [lang-en.pat] [lang-us.pat] +% \definefilesynonym [lang-en.hyp] [lang-us.hyp] \protect \endinput diff --git a/tex/context/base/lang-frq.tex b/tex/context/base/lang-frq.tex index 372813f70..773230e6c 100644 --- a/tex/context/base/lang-frq.tex +++ b/tex/context/base/lang-frq.tex @@ -2,7 +2,7 @@ %D [ file=lang-frq, %D version=2004.01.15, %D title=\CONTEXT\ Language Macros, -%D subtitle=Language Frequency Table Support, +%D subtitle=Frequency Tables, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA / Hans Hagen \& Ton Otten}] @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Language Frequency Table Support} +\writestatus{loading}{ConTeXt Language Macros / Frequency Tables} \unprotect diff --git a/tex/context/base/lang-ger.tex b/tex/context/base/lang-ger.tex index bdcaa9cb3..b9717ce9a 100644 --- a/tex/context/base/lang-ger.tex +++ b/tex/context/base/lang-ger.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Germanic Languages} +\writestatus{loading}{ConTeXt Language Macros / Germanic Languages} %D The framework of this module is set up by Hans Hagen while %D many of the first translations were done by Tobias. Later @@ -42,7 +42,8 @@ \c!leftquotation=\lowerleftdoubleninequote, \c!rightquotation=\upperrightdoubleninequote, \c!date={\v!day,\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] + \s!mapping={texnansi,ec}, + \s!encoding={texnansi,ec}] \installlanguage [\s!en] @@ -57,11 +58,14 @@ \c!rightquotation=\upperrightdoubleninequote, \c!date={\v!month,\ ,\v!day,{,\ },\v!year}, \s!patterns=\s!us, - \c!state=\v!stop] % elders always preloaded! + \s!lefthyphenmin=2, + \s!righthyphenmin=3] \installlanguage [\s!de] [\c!spacing=\v!packed, + \s!lefthyphenmin=3, + \s!righthyphenmin=3, \c!leftsentence={\hbox{--~}}, \c!rightsentence={\hbox{~--}}, \c!leftsubsentence={--}, @@ -71,7 +75,8 @@ \c!leftquotation=\lowerleftdoubleninequote, \c!rightquotation=\upperrightdoublesixquote, \c!date={\v!day,{.},\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] + \s!mapping={texnansi,ec}, + \s!encoding={texnansi,ec}] \installlanguage [\s!da] @@ -84,8 +89,7 @@ \c!rightquote=\upperrightsinglesixquote, \c!leftquotation=\lowerleftdoubleninequote, \c!rightquotation=\upperrightdoublesixquote, - \c!date={\v!day,{.},\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] + \c!date={\v!day,{.},\ ,\v!month,\ ,\v!year}] \installlanguage [\s!sv] @@ -98,8 +102,7 @@ \c!rightquote=\upperrightsingleninequote, \c!leftquotation=\upperrightdoubleninequote, \c!rightquotation=\upperrightdoubleninequote, - \c!date={\v!day,\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] + \c!date={\v!day,\ ,\v!month,\ ,\v!year}] \installlanguage [\s!af] @@ -113,7 +116,7 @@ \c!leftquotation=\upperleftdoublesixquote, \c!rightquotation=\upperrightdoubleninequote, \c!date={\v!year,\ ,\v!month,\ ,\v!day}, - \c!state=\v!stop] + \s!patterns=\s!nl] \installlanguage [\s!nb] @@ -180,14 +183,12 @@ \installlanguage % old german [deo] [\c!spacing=\v!packed, - \c!default=\s!de, - \c!state=\v!stop] + \c!default=\s!de] \installlanguage [de-de] [\c!spacing=\v!packed, - \c!default=\s!de, - \c!state=\v!stop] + \c!default=\s!de] \installlanguage [de-at] @@ -196,14 +197,12 @@ \c!leftquote=\leftguillemot, \c!rightquote=\rightguillemot, \c!leftquotation=\leftguillemot, - \c!rightquotation=\rightguillemot, - \c!state=\v!stop] + \c!rightquotation=\rightguillemot] \installlanguage [de-ch] [\c!spacing=\v!packed, - \c!default=\s!de, - \c!state=\v!stop] + \c!default=\s!de] %D And some alternative (but very real) english patterns: @@ -211,24 +210,23 @@ [en-gb] [\c!default=\s!en, \s!patterns=\s!gb, - \c!state=\v!stop] + \s!lefthyphenmin=3, + \s!righthyphenmin=3] \installlanguage [en-us] - [\c!default=\s!en, - %\s!patterns=\s!us, - \c!state=\v!stop] + [\c!default=\s!en] \installlanguage [\s!uk] [en-gb] \installlanguage [\s!us] [en-us] %D For compatibility reasons we also define: -\installlanguage [du] [\s!de] % old times context +%installlanguage [du] [\s!de] % old times context %installlanguage [sp] [\s!es] % old times context /lang-ita \installlanguage [usenglish] [en-us] -\installlanguage [ukenglish] [en-uk] +\installlanguage [ukenglish] [en-gb] \installlanguage [english] [en-us] \installlanguage [dutch] [\s!nl] \installlanguage [german] [\s!de] diff --git a/tex/context/base/lang-grk.tex b/tex/context/base/lang-grk.tex index 13cebb207..e4ba781eb 100644 --- a/tex/context/base/lang-grk.tex +++ b/tex/context/base/lang-grk.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Greek Language} +\writestatus{loading}{ConTeXt Language Macros / Greek} %D The framework of this module is set up by Hans Hagen while %D all the translations have been done by Apostolos Syropoulos @@ -29,8 +29,7 @@ \c!rightquote=\greekrightquot, \c!leftquotation=\greekleftquot, \c!rightquotation=\greekrightquot, - \c!date={\v!day\ \v!month\ \v!year}, - \c!state=\v!stop] + \c!date={\v!day\ \v!month\ \v!year}] \installlanguage [greek] [\s!gr] @@ -86,7 +85,9 @@ \installlanguage [\s!agr] [\s!default=\s!gr, - \c!state=\v!stop] + \s!patterns=\s!agr, + \s!mapping=\s!agr, + \s!encoding=\s!agr] \installlanguage [ancientgreek] [\s!agr] diff --git a/tex/context/base/lang-ind.tex b/tex/context/base/lang-ind.tex index f9bbad0d7..9b6e5ff1d 100644 --- a/tex/context/base/lang-ind.tex +++ b/tex/context/base/lang-ind.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Indo-Iranian Languages} +\writestatus{loading}{ConTeXt Language Macros / Indo-Iranian Languages} %D The framework of this module is set up by Hans Hagen while %D many of the first translations were done by Tobias. Later diff --git a/tex/context/base/lang-ini.lua b/tex/context/base/lang-ini.lua index e9e9af1b6..e188ad36c 100644 --- a/tex/context/base/lang-ini.lua +++ b/tex/context/base/lang-ini.lua @@ -6,6 +6,13 @@ if not modules then modules = { } end modules ['lang-ini'] = { license = "see context related readme files" } +-- needs a cleanup (share locals) + +local utf = unicode.utf8 + +local find, lower, format, utfchar = string.find, string.lower, string.format, utf.char +local concat = table.concat + if lang.use_new then lang.use_new(true) end languages = languages or {} @@ -13,6 +20,8 @@ languages.version = 1.009 languages.hyphenation = languages.hyphenation or { } languages.hyphenation.data = languages.hyphenation.data or { } +local langdata = languages.hyphenation.data + -- 002D : hyphen-minus (ascii) -- 2010 : hyphen -- 2011 : nonbreakable hyphen @@ -26,7 +35,7 @@ languages.hyphenation.data = languages.hyphenation.data or { } -- we can consider hiding data (faster access too) --~ local function filter(filename,what) ---~ local data = io.loaddata(input.find_file(filename)) +--~ local data = io.loaddata(resolvers.find_file(filename)) --~ local data = data:match(string.format("\\%s%%s*(%%b{})",what or "patterns")) --~ return data:match("{%s*(.-)%s*}") or "" --~ end @@ -47,29 +56,29 @@ local command = lpeg.P("\\patterns") local parser = (1-command)^0 * command * content local function filterpatterns(filename) - if filename:find("%.rpl") then - return io.loaddata(input.find_file(filename)) or "" + if find(filename,"%.rpl") then + return io.loaddata(resolvers.find_file(filename)) or "" else - return parser:match(io.loaddata(input.find_file(filename)) or "") + return parser:match(io.loaddata(resolvers.find_file(filename)) or "") end end -local command = lpeg.P("\\hyphenation") -local parser = (1-command)^0 * command * content +local command = lpeg.P("\\hyphenation") +local parser = (1-command)^0 * command * content local function filterexceptions(filename) - if filename:find("%.rhl") then - return io.loaddata(input.find_file(filename)) or "" + if find(filename,"%.rhl") then + return io.loaddata(resolvers.find_file(filename)) or "" else - return parser:match(io.loaddata(input.find_file(filename)) or {}) -- "" ? + return parser:match(io.loaddata(resolvers.find_file(filename)) or {}) -- "" ? end end local function record(tag) - local data = languages.hyphenation.data[tag] + local data = langdata[tag] if not data then data = lang.new() - languages.hyphenation.data[tag] = data or 0 + langdata[tag] = data or 0 end return data end @@ -82,31 +91,31 @@ function languages.hyphenation.define(tag) end function languages.hyphenation.number(tag) - local d = languages.hyphenation.data[tag] + local d = langdata[tag] return (d and d:id()) or 0 end -function languages.hyphenation.load(tag, filename, filter, target) - input.starttiming(languages) +local function loadthem(tag, filename, filter, target) + statistics.starttiming(languages) local data = record(tag) - filename = (filename and filename ~= "" and input.find_file(filename)) or "" + filename = (filename and filename ~= "" and resolvers.find_file(filename)) or "" local ok = filename ~= "" if ok then lang[target](data,filterpatterns(filename)) else lang[target](data,"") end - languages.hyphenation.data[tag] = data - input.stoptiming(languages) + langdata[tag] = data + statistics.stoptiming(languages) return ok end function languages.hyphenation.loadpatterns(tag, patterns) - return languages.hyphenation.load(tag, patterns, filterpatterns, "patterns") + return loadthem(tag, patterns, filterpatterns, "patterns") end function languages.hyphenation.loadexceptions(tag, exceptions) - return languages.hyphenation.load(tag, patterns, filterexceptions, "hyphenation") + return loadthem(tag, patterns, filterexceptions, "hyphenation") end function languages.hyphenation.exceptions(tag, ...) @@ -130,16 +139,17 @@ function languages.hyphenation.righthyphenmin(tag, value) end function languages.hyphenation.n() - return table.count(languages.hyphenation.data) + return table.count(langdata) end -- we can speed this one up with locals if needed local function tolang(what) - if type(what) == "number" then - return languages.hyphenation.data[languages.numbers[what]] - elseif type(what) == "string" then - return languages.hyphenation.data[what] + local kind = type(what) + if kind == "number" then + return langdata[languages.numbers[what]] + elseif kind == "string" then + return langdata[what] else return what end @@ -158,15 +168,15 @@ languages.registered = languages.registered or { } languages.associated = languages.associated or { } languages.numbers = languages.numbers or { } -input.storage.register(false,"languages/registered",languages.registered,"languages.registered") -input.storage.register(false,"languages/associated",languages.associated,"languages.associated") +storage.register("languages/registered",languages.registered,"languages.registered") +storage.register("languages/associated",languages.associated,"languages.associated") function languages.register(tag,parent,patterns,exceptions) parent = parent or tag languages.registered[tag] = { parent = parent, - patterns = patterns or string.format("lang-%s.pat",parent), - exceptions = exceptions or string.format("lang-%s.hyp",parent), + patterns = patterns or format("lang-%s.pat",parent), + exceptions = exceptions or format("lang-%s.hyp",parent), loaded = false, number = 0, } @@ -190,7 +200,7 @@ end function languages.loadable(tag) local l = languages.registered[tag] - if l and l.patterns and input.find_file(patterns) then + if l and l.patterns and resolvers.find_file(patterns) then return true else return false @@ -237,10 +247,10 @@ function languages.hyphenation.loadwords(tag, filename) local id = languages.hyphenation.number(tag) if id > 0 then local l = lang.new(id) or 0 - input.starttiming(languages) + statistics.starttiming(languages) local data = io.loaddata(filename) or "" l:hyphenation(data) - input.stoptiming(languages) + statistics.stoptiming(languages) end end @@ -257,10 +267,10 @@ function languages.logger.report() if l.loaded then local p = (l.patterns and "pat") or '-' local e = (l.exceptions and "exc") or '-' - result[#result+1] = string.format("%s:%s:%s:%s:%s", tag, l.parent, p, e, l.number) + result[#result+1] = format("%s:%s:%s:%s:%s", tag, l.parent, p, e, l.number) end end - return (#result > 0 and table.concat(result," ")) or "none" + return (#result > 0 and concat(result," ")) or "none" end languages.words = languages.words or {} @@ -283,15 +293,15 @@ do local word = lpeg.Cs((markup/"" + disc/"" + (1-spacing))^1) function languages.words.load(tag, filename) - local filename = input.find_file(filename,'other text file') or "" + local filename = resolvers.find_file(filename,'other text file') or "" if filename ~= "" then - input.starttiming(languages) + statistics.starttiming(languages) local data = io.loaddata(filename) or "" local words = languages.words.data[tag] or {} parser = (spacing + word/function(s) words[s] = true end)^0 parser:match(data) languages.words.data[tag] = words - input.stoptiming(languages) + statistics.stoptiming(languages) end end @@ -301,7 +311,7 @@ function languages.words.found(id, str) local tag = languages.numbers[id] if tag then local data = languages.words.data[tag] - return data and (data[str] or data[str:lower()]) + return data and (data[str] or data[lower(str)]) else return false end @@ -314,11 +324,10 @@ do local glyph, disc, kern = node.id('glyph'), node.id('disc'), node.id('kern') - local bynode = node.traverse + local bynode = node.traverse + local chardata = characters.data local function mark_words(head,found) -- can be optimized - local cd = characters.data - local uc = utf.char local current, start, str, language, n = head, nil, "", nil, 0 local function action() if #str > 0 then @@ -347,25 +356,25 @@ do action() language = a end - if current.subtype > 0 then + local components = current.components + if components then start = start or current n = n + 1 - for g in bynode(current.components) do - str = str .. uc(g.char) + for g in bynode(components) do + str = str .. utfchar(g.char) end else local code = current.char - if cd[code].uccode or cd[code].lccode then + if chardata[code].uccode or chardata[code].lccode then start = start or current n = n + 1 - str = str .. uc(code) - else - if start then - action() - end + str = str .. utfchar(code) + elseif start then + action() end end elseif id == disc then + if n > 0 then n = n + 1 end -- ok elseif id == kern and current.subtype == 0 and start then -- ok @@ -383,18 +392,20 @@ do languages.words.methods = { } languages.words.method = 1 + local lw = languages.words + languages.words.methods[1] = function(head, attribute, yes, nop) local set = node.set_attribute local unset = node.unset_attribute - local wrong, right = false, false - if nop then wrong = function(n) set(n,attribute,nop) end end + local right, wrong = false, false if yes then right = function(n) set(n,attribute,yes) end end + if nop then wrong = function(n) set(n,attribute,nop) end end for n in node.traverse(head) do unset(n,attribute) -- hm end local found, done = languages.words.found, false mark_words(head, function(language,str) - if #str < languages.words.threshold then + if #str < lw.threshold then return false elseif found(language,str) then done = true @@ -407,11 +418,10 @@ do return head, done end - local lw = languages.words + local color = attributes.private('color') function languages.words.check(head) if lw.enable and head.next then - local color = attributes.numbers['color'] local colors = lw.colors local alc = attributes.list[color] return lw.methods[lw.method](head, color, alc[colors.known], alc[colors.unknown]) @@ -443,3 +453,16 @@ languages.associate('uk','latn','eng') languages.associate('nl','latn','nld') languages.associate('de','latn','deu') languages.associate('fr','latn','fra') + +statistics.register("loaded patterns", function() + local result = languages.logger.report() + if result ~= "none" then + return result + end +end) + +statistics.register("language load time", function() + if statistics.elapsedindeed(languages) then + return format("%s seconds, n=%s", statistics.elapsedtime(languages), languages.hyphenation.n()) + end +end) diff --git a/tex/context/base/lang-ini.mkii b/tex/context/base/lang-ini.mkii index 46b9f51ce..e5759bc84 100644 --- a/tex/context/base/lang-ini.mkii +++ b/tex/context/base/lang-ini.mkii @@ -11,13 +11,159 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. +%D This module needs a further cleanup (real split between ii/iv). + +%D This module implements the (for the moment still simple) +%D multi||language support of \CONTEXT, which should not be +%D confused with the multi||lingual interface. This support +%D will be extended when needed. + +\writestatus{loading}{ConTeXt Language Macros / Initialization} + \unprotect +\ifx\nonfrenchspacing\undefined \let\nonfrenchspacing\relax \fi +\ifx\frenchspacing \undefined \let\frenchspacing \relax \fi + +%D When loading hyphenation patterns, \TEX\ assign a number to +%D each loaded table, starting with~0. Switching to a specific +%D table is done by assigning the relevant number to the +%D predefined \COUNTER\ \type{\language}. + +%D We keep track of the last loaded patterns by means of a +%D pseudo \COUNTER. This just one of those situations in which +%D we don't want to spent a real one. Language zero has no +%D patterns, first of all because I like to start numbering +%D at one. It may come in handy for special purposes as well. + +\normallanguage\zerocount \def\loadedlanguage{1} + +%D \macros +%D {currentlanguage, setupcurrentlanguage} +%D +%D Instead of numbers,we are going to use symbolic names for +%D the languages. The current langage is saved in the macro +%D \type {\currentlanguage}. The setup macro is mainly used +%D for cosmetic purposes. +%D +%D \starttyping +%D \dorecurse{3} +%D {\language[nl] +%D \startmode[*en] english \stopmode +%D \startmode[*nl] dutch \stopmode +%D \language[en] +%D \startmode[*en] english \stopmode +%D \startmode[*nl] dutch \stopmode} +%D \stoptyping + +\let\currentlanguage \empty +\let\currentmainlanguage\empty + +\def\setupcurrentlanguage[#1]{\setcurrentlanguage\currentmainlanguage{#1}} + +\def\setcurrentlanguage#1#2% sets modes: **id (currentmain) *id (current) + {\doifsomething{#1} + {\ifx\currentmainlanguage\empty\else\resetsystemmode{\systemmodeprefix\currentmainlanguage}\fi + \edef\currentmainlanguage{#1}% + \setsystemmode{\systemmodeprefix\currentmainlanguage}}% + \doifsomething{#2} + {\ifx\currentlanguage\empty\else\resetsystemmode\currentlanguage\fi + \edef\currentlanguage{#2}% + \setsystemmode\currentlanguage}} + +%D The internal macros will be defined later. + +%D \macros +%D {installlanguage} +%D +%D Hyphenation patterns can only be loaded when the format file +%D is prepared. The next macro takes care of this loading. A +%D language is specified with +%D +%D \showsetup{installlanguage} +%D +%D When \type {state} equals \type {start}, both patterns +%D and additional hyphenation specifications are loaded. These +%D files are seached for on the system path and are to be +%D named: +%D +%D \starttyping +%D \f!languageprefix-identifier.\f!patternsextension +%D \f!languageprefix-identifier.\f!hyhensextension +%D \stoptyping +%D +%D The \type{spacing} variable specifies how the spaces after +%D punctuation has to be handled. English is by tradition more +%D tolerant to inter||sentence spacing than other languages. +%D +%D This macro also defines \type {\identifier} as a shortcut +%D switch to the language. Furthermore the command defined as +%D being language specific, are executed. With +%D \type {default} we can default to another language +%D (patterns) at format generation time. This default language +%D is overruled when the appropriate patterns are loaded (some +%D implementations support run time addition of patterns to a +%D preloaded format). + +\def\dodoinstalllanguage#1#2% #2 added + {\doifundefined{#1}{\setvalue{#1}{\complexlanguage[#2]}}% + \expanded{\noexpand\uppercase{\noexpand\edef\noexpand\ascii{#1}}}% + \doifundefined\ascii{\setvalue\ascii{\complexlanguage[#2]}}} + +%D \macros +%D {preloadlanguages} +%D +%D We first try to load the files defined as file synonym +%D for \type {lang-*.pat} and \type {lang-*.hyp}. After that we +%D fall back on those files. The macro \type {\preloadpatterns} +%D reports which patterns are loaded and what hyphenmin +%D values are set. + +\let\installedlanguages\empty + +\def\doiflanguageelse#1{\doifdefinedelse{\??la#1\c!state}} + +\def\doloadlanguagefiles#1% + {\doifelsevalue{\??la#1\c!state}\v!start + {\edef\languagesuffix{\specificlanguageparameter{#1}\s!patterns}% + \ifx\languagesuffix\empty + \edef\languagesuffix{\defaultlanguage{#1}}% + \else\ifx\languagesuffix\relax + \edef\languagesuffix{\defaultlanguage{#1}}% + \fi\fi + \ifx\languagesuffix\empty + \edef\languagesuffix{#1}% + \fi + \doifundefinedelse{\??la\??la:\currentencoding:\currentmapping:\languagesuffix} + {\doloadpatterns{#1}\languagesuffix} + {\bgroup + \edef\loadedlanguage{\getvalue{\??la\??la:\currentencoding:\currentmapping:\languagesuffix}}% + %\showmessage\m!linguals1{\languagesuffix,#1,\loadedlanguage,*,*}% + %\showmessage\m!linguals3{\languagesuffix,#1,\loadedlanguage,*,*}% + \egroup}} + {\showmessage\m!linguals5{#1}}} + +\def\doinstalllanguage[#1][#2]% + {\doifassignmentelse{#2} + {\doiflanguageelse{#1} + {\getparameters[\??la#1][#2]} + {\setvalue{\l!prefix!#1}{#1}% + \addtocommalist{#1}\installedlanguages + \dodoinstalllanguage{#1}{#1}% + \getparameters[\??la#1][\c!state=\v!start,#2]}% + \doloadlanguagefiles{#1}} + {\setvalue{\l!prefix!#1}{#2}% + \getparameters[\??la#1][\s!default=#2]% + \dodoinstalllanguage{#1}{#2}}} + +\def\reallanguagetag#1% + {\ifcsname\l!prefix!#1\endcsname\csname\l!prefix!#1\endcsname\else#1\fi} + \let\preloadedpatterns\empty \let\preloadedpmessage\empty \def\doshowpatterns#1#2#3#4% language number encoding mapping - {#1->#3:#4->#2->\xxlanguageparameter{#1}\s!lefthyphenmin:\xxlanguageparameter{#1}\s!righthyphenmin\space} + {#1->#3:#4->#2->\specificlanguageparameter{#1}\s!lefthyphenmin:\specificlanguageparameter{#1}\s!righthyphenmin\space} \def\preloadlanguages {\doifsomething\preloadedpmessage{\showmessage\m!linguals{10}\preloadedpmessage}} @@ -27,16 +173,33 @@ \processcommacommand[\installedlanguages]\preloadallpatterns \global\let\preloadallpatterns\relax} -\fetchruntimecommand \showpatterns {\f!languageprefix\s!run} +% ^^ \language[#1] gave unwanted side effect of loading language specifics + +\def\installlanguage + {\dodoubleargument\doinstalllanguage} + +%D When the second argument is a language identifier, a +%D synonym is created. This feature is present because we +%D used dutch mnemonics in the dutch version, but nowadays +%D conform a standard. + +\let \patternencoding \s!default +\let \patternmapping \s!default -\def\mkdoloadpatterns#1#2% - {\expanded{\getcommacommandsize[\getvalue{\??la#2\s!encoding}]}% +\def\doifpatternselse#1% + {\expanded{\doifinsetelse{#1}{\preloadedpatterns}}} + +\def\doloadpatterns#1#2% + {\edef\askedlanguageencoding{\specificlanguageparameter{#1}\s!encoding}% + \edef\askedlanguagemapping {\specificlanguageparameter{#1}\s!mapping}% + \expanded{\getcommacommandsize[\askedlanguageencoding]}% + % slightly faster: \let\unicodechar\utfunihashglyph \ifnum\commalistsize>0 - %\message{[nofpatterns #2: \commalistsize/\getvalue{\??la#2\s!encoding}]}% + %\message{[nofpatterns #2: \commalistsize/\askedlanguageencoding]}% \dorecurse\commalistsize - {\expanded{\getfromcommacommand[\getvalue{\??la#2\s!encoding}][\recurselevel]}% + {\expanded{\getfromcommacommand[\askedlanguageencoding][\recurselevel]}% \let\patternencoding\commalistelement - \expanded{\getfromcommacommand[\getvalue{\??la#2\s!mapping }][\recurselevel]}% + \expanded{\getfromcommacommand[\askedlanguagemapping][\recurselevel]}% \let\patternmapping \commalistelement %\message{[patterns: #1/#2/\patternencoding/\patternmapping]}% \dodoloadpatterns{#1}{#2}\patternencoding\patternmapping}% @@ -45,14 +208,14 @@ \dodoloadpatterns{#1}{#2}{}{}% \fi} -\beginXETEX +\ifnum\texengine=\xetexengine -\def\mkdoloadpatterns#1#2% - {\letvalue{\??la#2\s!encoding}\empty - \letvalue{\??la#2\s!mapping }\empty - \dodoloadpatterns{#1}{#2}{}{}} + \def\doloadpatterns#1#2% + {%\letvalue{\??la#2\s!encoding}\empty + %\letvalue{\??la#2\s!mapping }\empty + \dodoloadpatterns{#1}{#2}{}{}} -\endXETEX +\fi \def\setuphyppatencoding {\pathypsettings @@ -102,6 +265,8 @@ \fi \egroup} +\fetchruntimecommand \showpatterns {\f!languageprefix\s!run} + %D Since we can only load patterns in ini\TeX, we nil the %D loading before dumping (which saves a bit of memory, but %D strangely enough not in the format). @@ -111,30 +276,130 @@ \globallet\dodoloadpatterns\gobblefourarguments \to \everydump -\def\mkdoifpatternselse#1% - {\expanded{\doifinsetelse{#1}{\preloadedpatterns}}} +%D \macros +%D {setuplanguage} +%D +%D Quick and dirty, but useful: +%D +%D \showsetup{setuplanguage} +%D +%D Beware, this command can only be used when a language is installed. -\def\mkloadlanguagefiles#1% - {\doifelsevalue{\??la#1\c!state}\v!start - {\doifelsevaluenothing{\??la#1\s!patterns} - {\edef\languagesuffix{#1}} - {\edef\languagesuffix{\getvalue{\??la#1\s!patterns}}}% - \doifundefinedelse{\??la\??la:\currentencoding:\currentmapping:\languagesuffix} - {\mkdoloadpatterns{#1}\languagesuffix} - {\bgroup - \edef\loadedlanguage{\getvalue{\??la\??la:\currentencoding:\currentmapping:\languagesuffix}}% - \showmessage\m!linguals1{\languagesuffix,#1,\loadedlanguage,*,*}% - \showmessage\m!linguals3{\languagesuffix,#1,\loadedlanguage,*,*}% - \egroup}} - {\showmessage\m!linguals5{#1}}} +\unprotected \def\setuplanguage + {\dodoubleempty\dosetuplanguage} + +\def\dosetuplanguage[#1][#2]% handy patch for testing + {\ifsecondargument + \getparameters[\??la#1][#2]% + \doif{#1}\currentlanguage\docomplexlanguage + \else + \getparameters[\??la\currentlanguage][#1]% + \docomplexlanguage + \fi} + +\setuplanguage + [\s!default] + [\s!lefthyphenmin=2, + \s!righthyphenmin=2, + \s!patterns=, + \c!spacing=\v!packed, + \s!encoding=, + \s!mapping=, + \c!lefthyphen=, + \c!righthyphen=-, + \c!hyphen=-, + \c!midsentence=---, + \c!leftsentence=---, + \c!rightsentence=---, + \c!leftsubsentence=---, + \c!rightsubsentence=---, + \c!leftquote=\upperleftsinglesixquote, + \c!rightquote=\upperrightsingleninequote, + \c!leftquotation=\upperleftdoublesixquote, + \c!rightquotation=\upperrightdoubleninequote, + \c!leftspeech=\languageparameter\c!leftquotation, + \c!middlespeech=, + \c!rightspeech=\languageparameter\c!rightquotation, + \c!limittext=\unknown, + \c!date={\v!year,\ ,\v!month,\ ,\v!day}, + \c!text=Ag] + +% rather new, split and per language + +\setuplanguage + [\s!default] + [\c!compoundhyphen=\compoundhyphen, + \c!leftcompoundhyphen=\compoundhyphen, + \c!rightcompoundhyphen=] -\def\mksetnormallanguage#1#2% current default +%D The values \type {leftsentence} and \type +%D {rightsentence} can be (and are) used to implement +%D automatic subsentence boundary glyphs, like in {\fr +%D |<|french guillemots|>|} or {\de |<|german guillemots|>|} or +%D {\nl |<|dutch dashes|>|} like situations. Furthermore \type +%D {leftquotation} and \type {leftquote} come into view +%D \quotation {when we quote} or \quote {quote} something. + +%D \macros +%D {currentdatespecification} +%D +%D Just to make things easy we can ask for the current date +%D specification by saying: + +\def\currentdatespecification{\languageparameter\c!date} + +%D This command is not meant for users. + +%D Carefull reading of these macros shows that it's legal to +%D say +%D +%D \starttyping +%D \installlanguage [du] [de] +%D \stoptyping + +%D \macros +%D {language,mainlanguage} +%D +%D Switching to another language (actually another hyphenation +%D pattern) is done with: +%D +%D \starttyping +%D \language[identifier] +%D \stoptyping +%D +%D or with \type{\identifier}. Just to be compatible with +%D \PLAIN\ \TEX, we still support the original meaning, so +%D +%D \starttyping +%D \language=1 +%D \stoptyping +%D +%D is a valid operation, where the relation between number +%D and language depends on the order in installing languages. +%D +%D \showsetup{language} +%D \showsetup{mainlanguage} +%D +%D Both commands take a predefined language identifier as +%D argument. We can use \type{\mainlanguage[identifier]} for +%D setting the (indeed) main language. This is the language +%D used for translating labels like {\em figure} and {\em +%D table}. The main language defaults to the current language. +%D +%D We take care of local as well as standardized language +%D switching (fr and fa, de and du, but nl and nl). + +\ifx\synchronizepatterns \undefined \let\synchronizepatterns\relax \fi +\ifx\synchronizepatternswithfont\undefined \def\synchronizepatternswithfont{\synchronizepatterns} \fi + +\def\setnormallanguage#1#2% current default {% called quite often, so we use \csname % \def\synchronizepatterns{\setnormallanguage % {\csname\??la\currentlanguage\s!patterns\endcsname}}% called often % of even better pre-expand in an ugly way: - \@EA\def\@EA\synchronizepatterns\@EA{\@EA\dosetnormallanguage - \csname\??la\currentlanguage\s!patterns\endcsname}% +% \@EA\def\@EA\synchronizepatterns\@EA{\@EA\dosetnormallanguage +% \csname\??la\currentlanguage\s!patterns\endcsname}% +\edef\synchronizepatterns{\noexpand\dosetnormallanguage{\languageparameter\s!patterns}}% \donefalse \synchronizepatterns \ifdone\else @@ -142,8 +407,9 @@ \synchronizepatterns \ifdone\else \ifx\currentdefaultlanguage\empty\else - \@EA\def\@EA\synchronizepatterns\@EA{\@EA\dosetnormallanguage - \csname\??la\currentdefaultlanguage\s!patterns\endcsname}% +% \@EA\def\@EA\synchronizepatterns\@EA{\@EA\dosetnormallanguage +% \csname\??la\currentdefaultlanguage\s!patterns\endcsname}% +\edef\synchronizepatterns{\noexpand\dosetnormallanguage{\specificlanguageparameter\currentdefaultlanguage\s!patterns}}% \synchronizepatterns \ifdone\else \dosetnormallanguage\currentdefaultlanguage @@ -153,11 +419,11 @@ \fi \fi} -\def\dosetnormallanguage#1% #1 == \cs - {\dodosetnormallanguage{:\currentencoding:\currentmapping:}#1{% - \dodosetnormallanguage{:\currentencoding:\s!default :}#1{% - \dodosetnormallanguage{:\s!default :\currentmapping:}#1{% - \dodosetnormallanguage{:\s!default :\s!default :}#1\empty}}}} +\def\dosetnormallanguage#1% #1 == \cs (no longer) + {\dodosetnormallanguage{:\currentencoding:\currentmapping:}{#1}{% + \dodosetnormallanguage{:\currentencoding:\s!default :}{#1}{% + \dodosetnormallanguage{:\s!default :\currentmapping:}{#1}{% + \dodosetnormallanguage{:\s!default :\s!default :}{#1}\empty}}}} \def\dodosetnormallanguage#1#2% {\ifcsname\??la\??la#1#2\endcsname @@ -174,11 +440,251 @@ \@EA\firstofoneargument \fi} -\beginXETEX +\newevery \everylanguage \relax + +\def\disablelanguagespecifics + {\ignorecompoundcharacter} + +\def\sethyphenationvariables + {\lefthyphenmin 0\languageparameter\s!lefthyphenmin \relax + \righthyphenmin0\languageparameter\s!righthyphenmin\relax + \lefthyphenmin \numexpr\lefthyphenmin +\hyphenminoffset\relax + \righthyphenmin\numexpr\righthyphenmin+\hyphenminoffset\relax} + +\def\docomplexlanguage% assumes that \currentlanguage is set + {\edef\currentdefaultlanguage{\defaultlanguage\currentlanguage}% + \setnormallanguage\currentlanguage\currentdefaultlanguage + \the\everylanguage + \enablelanguagespecifics[\currentlanguage]% + \sethyphenationvariables + \relax + % will be definable and move to core-spa ! + \doifelse{\languageparameter\c!spacing}\v!broad\nonfrenchspacing\frenchspacing} + +\ifx\enablelanguagespecifics\undefined \def\enablelanguagespecifics[#1]{} \fi + +% The following may be a solution for the fact that one cannot +% change catcodes of characters like : and ; inside an environment. + +\appendtoks + \enablelanguagespecifics[\currentlanguage]% +\to \everystarttext + +\def\complexlanguage[#1]% + {\edef\askedlanguage{#1}% + \ifx\askedlanguage\empty \else + \ifcsname\l!prefix!\askedlanguage\endcsname + \edef\askedlanguage{\csname\l!prefix!\askedlanguage\endcsname}% + \ifx\currentlanguage\askedlanguage \else + \setcurrentlanguage\currentmainlanguage\askedlanguage + \docomplexlanguage + \fi + \else + \showmessage\m!linguals6{#1}% + \fi + \fi} + +\let\simplelanguage\normallanguage + +\definecomplexorsimple\language + +\def\mainlanguage[#1]% + {\edef\askedlanguage{#1}% + \ifx\askedlanguage\empty \else + \ifcsname\l!prefix!\askedlanguage\endcsname + \edef\askedlanguage{\csname\l!prefix!\askedlanguage\endcsname}% + \ifx\currentlanguage\askedlanguage + \ifx\currentmainlanguage\askedlanguage + \else + \setcurrentlanguage\askedlanguage\askedlanguage + \docomplexlanguage + \fi + \else + \setcurrentlanguage\askedlanguage\askedlanguage + \docomplexlanguage + \fi + \fi + \fi} + +%D \macros +%D {defaultlanguage,languageparameter,specificlanguageparameter} + +\def\defaultlanguage#1% + {\ifcsname\??la#1\s!default\endcsname + \expandafter\defaultlanguage\csname\??la#1\s!default\endcsname + \else + #1% + \fi} + +\def\languageparameter#1% + {\ifcsname\??la\currentlanguage#1\endcsname + \csname\??la\currentlanguage#1\endcsname + \else\ifcsname\??la\currentlanguage\s!default\endcsname + \expandafter\specificlanguageparameter\csname\??la\currentlanguage\s!default\endcsname{#1}% + \else\ifcsname\??la\s!default#1\endcsname + \csname\??la\s!default#1\endcsname + \fi\fi\fi} + +\def\specificlanguageparameter#1#2% + {\ifcsname\??la#1#2\endcsname + \csname\??la#1#2\endcsname + \else\ifcsname\??la#1\s!default\endcsname + \expandafter\specificlanguageparameter\csname\??la#1\s!default\endcsname{#2}% + \else\ifcsname\??la\s!default#2\endcsname + \csname\??la\s!default#2\endcsname + \fi\fi\fi} + +%D New (see nomarking and nolist): + +\def\splitsequence#1#2% + {\doifelse{#1}\v!no{#2}{\doifelse{#1}\v!yes{\languageparameter\c!limittext}{#1}}} + +\def\splitsymbol#1% + {\splitsequence{#1}{\languageparameter\c!limittext}} + +%D Just like with subsentence boundary symbols, quotes +%D placement depends on the current language, therefore we show +%D the defaults here. +%D +%D \def\ShowLanguageValues [#1] [#2] #3 #4 +%D {\blank +%D \startlinecorrection +%D \vbox\bgroup +%D \language[#1]% +%D \setbox0=\hbox to \hsize{\hss\bf#2 subsentence symbol and quotes\hss} +%D \dp0=0pt +%D \box0 +%D \vskip.5em +%D \hrule +%D \vskip.5em +%D \let\normalbar=| +%D \hbox to \hsize +%D {\hfil\quotation{#3 #4}\hfil\quote{#2}\hfil +%D \let|=\normalbar\strut|<||<|#3|>|#4|>|\hfil} +%D \vskip.5em +%D \hrule +%D \egroup +%D \stoplinecorrection +%D \blank} +%D +%D \ShowLanguageValues [af] [afrikaans] afrikaanse ... +%D \ShowLanguageValues [ca] [catalan] catalan ... +%D \ShowLanguageValues [cs] [czech] tjechisch tex +%D \ShowLanguageValues [cs] [slovak] slowaakse ... +%D \ShowLanguageValues [da] [danish] deense ... +%D \ShowLanguageValues [de] [german] duitse degelijkheid +%D \ShowLanguageValues [en] [english] engelse humor +%D \ShowLanguageValues [fi] [finnish] finse ... +%D \ShowLanguageValues [fr] [french] franse slag +%D \ShowLanguageValues [it] [italian] italiaanse ... +%D \ShowLanguageValues [la] [latin] latijnse missen +%D \ShowLanguageValues [nl] [dutch] nederlandse zuinigheid +%D \ShowLanguageValues [nb] [bokmal] noorse zalm +%D \ShowLanguageValues [nn] [nnynorsk] noorse zalm +%D \ShowLanguageValues [pl] [polish] poolse vlag +%D \ShowLanguageValues [pt] [portuguese] portugese ... +%D \ShowLanguageValues [es] [spanish] spaans benauwd +%D \ShowLanguageValues [sv] [swedish] zweedse ... +%D \ShowLanguageValues [tr] [turkish] turks fruit + +%D We support a lot of languages. These are specified and +%D loaded in separate files, according to their roots. Here +%D we only take care of (postponed) setting of the current +%D language. +%D +%D \unprotect +%D \placetable{The germanic languages (\type{lang-ger})} +%D \starttable[||||] +%D \HL +%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR +%D \HL +%D \NC \s!nl \NC dutch \NC germanic \NC\FR +%D \NC \s!en \NC english \NC germanic \NC\MR +%D \NC \s!de \NC german \NC germanic \NC\MR +%D \NC \s!da \NC danish \NC germanic \NC\MR +%D \NC \s!sv \NC swedish \NC germanic \NC\MR +%D \NC \s!af \NC afrikaans \NC germanic \NC\MR +%D \NC \s!nb \NC bokmal \NC germanic \NC\LR +%D \NC \s!nn \NC nynorsk \NC germanic \NC\LR +%D \HL +%D \stoptable +%D \protect +%D +%D \unprotect +%D \placetable{The italic languages (\type{lang-ita})} +%D \starttable[||||] +%D \HL +%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR +%D \HL +%D \NC \s!fr \NC french \NC italic \NC\FR +%D \NC \s!ca \NC catalan \NC italic \NC\MR +%D \NC \s!es \NC spanish \NC italic \NC\MR +%D \NC \s!it \NC italian \NC italic \NC\MR +%D \NC \s!la \NC latin \NC italic \NC\MR +%D \NC \s!pt \NC portuguese \NC italic \NC\LR +%D \HL +%D \stoptable +%D \protect +%D +%D \unprotect +%D \placetable{The slavic languages (\type{lang-sla})} +%D \starttable[||||] +%D \HL +%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR +%D \HL +%D \NC \s!pl \NC polish \NC slavic \NC\FR +%D \NC \s!cs \NC czech \NC slavic \NC\MR +%D \NC \s!sk \NC slavik \NC slavic \NC\LR +%D \HL +%D \stoptable +%D \protect +%D \unprotect +%D +%D \placetable{The altaic languages (\type{lang-alt})} +%D \starttable[||||] +%D \HL +%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR +%D \HL +%D \NC \s!tr \NC turkish \NC altaic \NC\SR +%D \HL +%D \stoptable +%D +%D \placetable{The uralic languages (\type{lang-ura})} +%D \starttable[||||] +%D \HL +%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR +%D \HL +%D \NC \s!fi \NC finnish \NC uralic \NC\SR +%D \HL +%D \stoptable +%D \protect + +% \bgroup \normallanguage255 \patterns{} \egroup +% \def\nopatterns{\normallanguage255 } + +\def\nopatterns{\normallanguage\minusone} + +%D \XETEX\ is \UNICODE: + +\ifnum\texengine=\xetexengine + \def\synchronizepatternswithfont{} \def\doloadpatterns #1#2{\dodoloadpatterns{#1}{#2}\s!default\s!default} - \def\setnormallanguage #1{\dosetnormallanguage{:\s!default:\s!default:}#1\empty} + \def\dosetnormallanguage #1{\dodosetnormallanguage{:\s!default:\s!default:}{#1}\empty} \def\setuphyppatencoding {\pathypsettings} -\endXETEX + +\fi + +%D We default to the language belonging to the interface. This +%D is one of the few places outside the interface modules where +%D \type{\startinterface} is used. + +%D We default to english: + +\setupcurrentlanguage[\s!en] + +\def\initializemainlanguage + {\mainlanguage[\currentlanguage]% + \showmessage\m!linguals9\currentlanguage} \protect \endinput diff --git a/tex/context/base/lang-ini.mkiv b/tex/context/base/lang-ini.mkiv index ce82b5a47..7cb945ef9 100644 --- a/tex/context/base/lang-ini.mkiv +++ b/tex/context/base/lang-ini.mkiv @@ -11,16 +11,133 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\unprotect +%D This module needs a further cleanup (real split between ii/iv). + +%D This module implements the (for the moment still simple) +%D multi||language support of \CONTEXT, which should not be +%D confused with the multi||lingual interface. This support +%D will be extended when needed. + +\writestatus{loading}{ConTeXt Language Macros / Initialization} \registerctxluafile{lang-ini}{1.001} -\let\synchronizepatterns \relax % todo: cleanup -\let\synchronizepatternswithfont\relax % todo: cleanup -\let\preloadallpatterns \relax % just for old times sake -\let\preloadlanguages \relax % just for old times sake +\unprotect + +% \def\testlanguage[#1]% +% {\start +% \language[#1] +% \number\normallanguage/\the\lefthyphenmin/\the\righthyphenmin: +% \input tufte +% \hyphenatedword{effetestenofditwerkt} +% \par +% \stop} +% +% \testlanguage[de] \testlanguage[de-de] \testlanguage[de-at] \testlanguage[de-ch] \page +% \testlanguage[en] \testlanguage[us] \testlanguage[en-us] \testlanguage[uk] \testlanguage[en-gb] \page + +\ifx\nonfrenchspacing\undefined \let\nonfrenchspacing\relax \fi +\ifx\frenchspacing \undefined \let\frenchspacing \relax \fi + +%D When loading hyphenation patterns, \TEX\ assign a number to +%D each loaded table, starting with~0. Switching to a specific +%D table is done by assigning the relevant number to the +%D predefined \COUNTER\ \type{\language}. + +%D We keep track of the last loaded patterns by means of a +%D pseudo \COUNTER. This just one of those situations in which +%D we don't want to spent a real one. Language zero has no +%D patterns, first of all because I like to start numbering +%D at one. It may come in handy for special purposes as well. + +\normallanguage\zerocount \def\loadedlanguage{1} + +%D \macros +%D {currentlanguage, setupcurrentlanguage} +%D +%D Instead of numbers,we are going to use symbolic names for +%D the languages. The current langage is saved in the macro +%D \type {\currentlanguage}. The setup macro is mainly used +%D for cosmetic purposes. +%D +%D \starttyping +%D \dorecurse{3} +%D {\language[nl] +%D \startmode[*en] english \stopmode +%D \startmode[*nl] dutch \stopmode +%D \language[en] +%D \startmode[*en] english \stopmode +%D \startmode[*nl] dutch \stopmode} +%D \stoptyping + +\let\currentlanguage \empty +\let\currentmainlanguage\empty + +\def\setupcurrentlanguage[#1]{\setcurrentlanguage\currentmainlanguage{#1}} + +\def\setcurrentlanguage#1#2% sets modes: **id (currentmain) *id (current) + {\doifsomething{#1} + {\ifx\currentmainlanguage\empty\else\resetsystemmode{\systemmodeprefix\currentmainlanguage}\fi + \edef\currentmainlanguage{#1}% + \setsystemmode{\systemmodeprefix\currentmainlanguage}}% + \doifsomething{#2} + {\ifx\currentlanguage\empty\else\resetsystemmode\currentlanguage\fi + \edef\currentlanguage{#2}% + \setsystemmode\currentlanguage}} + +%D The internal macros will be defined later. + +%D \macros +%D {installlanguage} +%D +%D Hyphenation patterns can only be loaded when the format file +%D is prepared. The next macro takes care of this loading. A +%D language is specified with +%D +%D \showsetup{installlanguage} +%D +%D When \type {state} equals \type {start}, both patterns +%D and additional hyphenation specifications are loaded. These +%D files are seached for on the system path and are to be +%D named: +%D +%D \starttyping +%D \f!languageprefix-identifier.\f!patternsextension +%D \f!languageprefix-identifier.\f!hyhensextension +%D \stoptyping +%D +%D The \type{spacing} variable specifies how the spaces after +%D punctuation has to be handled. English is by tradition more +%D tolerant to inter||sentence spacing than other languages. +%D +%D This macro also defines \type {\identifier} as a shortcut +%D switch to the language. Furthermore the command defined as +%D being language specific, are executed. With +%D \type {default} we can default to another language +%D (patterns) at format generation time. This default language +%D is overruled when the appropriate patterns are loaded (some +%D implementations support run time addition of patterns to a +%D preloaded format). + +\def\dodoinstalllanguage#1#2% #2 added + {\doifundefined{#1}{\setvalue{#1}{\complexlanguage[#2]}}% + \expanded{\noexpand\uppercase{\noexpand\edef\noexpand\ascii{#1}}}% + \doifundefined\ascii{\setvalue\ascii{\complexlanguage[#2]}}} + +%D \macros +%D {preloadlanguages} +%D +%D We first try to load the files defined as file synonym +%D for \type {lang-*.pat} and \type {lang-*.hyp}. After that we +%D fall back on those files. The macro \type {\preloadpatterns} +%D reports which patterns are loaded and what hyphenmin +%D values are set. -\def\mkdoloadpatterns#1#2% +\let\installedlanguages\empty + +\def\doiflanguageelse#1{\doifdefinedelse{\??la#1\c!state}} + +\def\doloadpatterns#1#2% {\ctxlua{languages.register( "#1", "#2", @@ -28,43 +145,431 @@ "\truefilename{\f!languageprefix#2.\f!hyphensextension }") }} -\def\mkdoifpatternselse#1% +% \def\doloadlanguagefiles#1% +% {\doifelsevaluenothing{\??la#1\s!patterns} +% {\doloadpatterns{#1}{#1}} +% {\doloadpatterns{#1}{\getvalue{\??la#1\s!patterns}}}} + +\def\doloadlanguagefiles#1% + {\edef\languagesuffix{\specificlanguageparameter{#1}\s!patterns}% + \ifx\languagesuffix\empty + \edef\languagesuffix{\defaultlanguage{#1}}% + \else\ifx\languagesuffix\relax + \edef\languagesuffix{\defaultlanguage{#1}}% + \fi\fi + \ifx\languagesuffix\empty + \edef\languagesuffix{#1}% + \fi + \doloadpatterns{#1}\languagesuffix} + +\def\doinstalllanguage[#1][#2]% + {\doifassignmentelse{#2} + {\doiflanguageelse{#1} + {\getparameters[\??la#1][#2]} + {\setvalue{\l!prefix!#1}{#1}% + \addtocommalist{#1}\installedlanguages + \dodoinstalllanguage{#1}{#1}% + \getparameters[\??la#1][\c!state=\v!start,#2]}% + \doloadlanguagefiles{#1}} + {\setvalue{\l!prefix!#1}{#2}% + \getparameters[\??la#1][\s!default=#2]% + \dodoinstalllanguage{#1}{#2}}} + +\def\reallanguagetag#1% + {\ifcsname\l!prefix!#1\endcsname\csname\l!prefix!#1\endcsname\else#1\fi} + +% ^^ \language[#1] gave unwanted side effect of loading language specifics + +\def\installlanguage + {\dodoubleargument\doinstalllanguage} + +%D When the second argument is a language identifier, a +%D synonym is created. This feature is present because we +%D used dutch mnemonics in the dutch version, but nowadays +%D conform a standard. + +\def\doifpatternselse#1% {\ctxlua{cs.testcase(languages.loadable("#1"))}} -\def\mkloadlanguagefiles#1% - {\doifelsevaluenothing{\??la#1\s!patterns} - {\mkdoloadpatterns{#1}{#1}} - {\mkdoloadpatterns{#1}{\getvalue{\??la#1\s!patterns}}}} +%D \macros +%D {setuplanguage} +%D +%D Quick and dirty, but useful: +%D +%D \showsetup{setuplanguage} +%D +%D Beware, this command can only be used when a language is installed. -\def\mksetnormallanguage#1#2% current default / we can freeze the number here - {\normallanguage=\ctxlua{tex.sprint(languages.enable({ - "\csname\??la#1\s!patterns\endcsname","#1", - "\csname\??la#2\s!patterns\endcsname","#2", - }))}\relax} +\unprotected \def\setuplanguage + {\dodoubleempty\dosetuplanguage} -% to be tested -% -% \def\mkdosetnormallanguage#1#2% current default -% {\normallanguage=\ctxlua{tex.sprint(languages.enable({ -% "\csname\??la#1\s!patterns\endcsname","#1", -% "\csname\??la#2\s!patterns\endcsname","#2", -% }))}}% -% \setxvalue{\??la\??la#1#2}{\number\normallanguage}} -% -% \def\mksetnormallanguage#1#2% current default / we can freeze the number here -% {\normallanguage\executeifdefined{\??la\??la#1#2}{\mkdosetnormallanguage{#1}{#2}}} +\def\dosetuplanguage[#1][#2]% handy patch for testing + {\ifsecondargument + \getparameters[\??la#1][#2]% + \doif{#1}\currentlanguage\docomplexlanguage + \else + \getparameters[\??la\currentlanguage][#1]% + \docomplexlanguage + \fi} +\setuplanguage + [\s!default] + [\s!lefthyphenmin=2, + \s!righthyphenmin=2, + \s!patterns=, + \c!spacing=\v!packed, + \c!lefthyphen=, + \c!righthyphen=-, + \c!hyphen=-, + \c!midsentence=---, + \c!leftsentence=---, + \c!rightsentence=---, + \c!leftsubsentence=---, + \c!rightsubsentence=---, + \c!leftquote=\upperleftsinglesixquote, + \c!rightquote=\upperrightsingleninequote, + \c!leftquotation=\upperleftdoublesixquote, + \c!rightquotation=\upperrightdoubleninequote, + \c!leftspeech=\languageparameter\c!leftquotation, + \c!middlespeech=, + \c!rightspeech=\languageparameter\c!rightquotation, + \c!limittext=\unknown, + \c!date={\v!year,\ ,\v!month,\ ,\v!day}, + \c!text=Ag] -\def\loadspellchecklist - {\dodoubleempty\doloadspellchecklist} +% rather new, split and per language + +\setuplanguage + [\s!default] + [\c!compoundhyphen=\compoundhyphen, + \c!leftcompoundhyphen=\compoundhyphen, + \c!rightcompoundhyphen=] + +%D The values \type {leftsentence} and \type +%D {rightsentence} can be (and are) used to implement +%D automatic subsentence boundary glyphs, like in {\fr +%D |<|french guillemots|>|} or {\de |<|german guillemots|>|} or +%D {\nl |<|dutch dashes|>|} like situations. Furthermore \type +%D {leftquotation} and \type {leftquote} come into view +%D \quotation {when we quote} or \quote {quote} something. + +%D \macros +%D {currentdatespecification} +%D +%D Just to make things easy we can ask for the current date +%D specification by saying: + +\def\currentdatespecification{\languageparameter\c!date} + +%D This command is not meant for users. + +%D Carefull reading of these macros shows that it's legal to +%D say +%D +%D \starttyping +%D \installlanguage [du] [de] +%D \stoptyping + +%D \macros +%D {language,mainlanguage} +%D +%D Switching to another language (actually another hyphenation +%D pattern) is done with: +%D +%D \starttyping +%D \language[identifier] +%D \stoptyping +%D +%D or with \type{\identifier}. Just to be compatible with +%D \PLAIN\ \TEX, we still support the original meaning, so +%D +%D \starttyping +%D \language=1 +%D \stoptyping +%D +%D is a valid operation, where the relation between number +%D and language depends on the order in installing languages. +%D +%D \showsetup{language} +%D \showsetup{mainlanguage} +%D +%D Both commands take a predefined language identifier as +%D argument. We can use \type{\mainlanguage[identifier]} for +%D setting the (indeed) main language. This is the language +%D used for translating labels like {\em figure} and {\em +%D table}. The main language defaults to the current language. +%D +%D We take care of local as well as standardized language +%D switching (fr and fa, de and du, but nl and nl). + +\def\dosetnormallanguage#1#2% current default + {\edef\askedlanguagepatterns{\specificlanguageparameter{#1}\s!patterns}% + \normallanguage=\ctxlua{tex.sprint(languages.enable({"\askedlanguagepatterns","#1","\askedlanguagepatterns","#2"}))}% + \ifproductionrun + \setxvalue{\??la\??la#1#2}{\number\normallanguage}% + \fi} + +\def\setnormallanguage#1#2% current default / we can freeze the number here + {\ifcsname\??la\??la#1#2\endcsname + \normallanguage\csname\??la\??la#1#2\endcsname % todo: we can set language at the lua end now + \else + \dosetnormallanguage{#1}{#2}% + \fi} + +\newtoks \everylanguage + +\def\disablelanguagespecifics + {\ignorecompoundcharacter} + +\def\sethyphenationvariables + {\lefthyphenmin 0\languageparameter\s!lefthyphenmin \relax + \righthyphenmin0\languageparameter\s!righthyphenmin\relax + \lefthyphenmin \numexpr\lefthyphenmin +\hyphenminoffset\relax + \righthyphenmin\numexpr\righthyphenmin+\hyphenminoffset\relax} + +\def\docomplexlanguage% assumes that \currentlanguage is set + {\edef\currentdefaultlanguage{\defaultlanguage\currentlanguage}% + \setnormallanguage\currentlanguage\currentdefaultlanguage + \the\everylanguage + \enablelanguagespecifics[\currentlanguage]% + \sethyphenationvariables + \relax + % will be definable and move to core-spa ! + \doifelse{\languageparameter\c!spacing}\v!broad\nonfrenchspacing\frenchspacing} + +\ifx\enablelanguagespecifics\undefined \def\enablelanguagespecifics[#1]{} \fi + +% The following may be a solution for the fact that one cannot +% change catcodes of characters like : and ; inside an environment. + +\appendtoks + \enablelanguagespecifics[\currentlanguage]% +\to \everystarttext + +\def\complexlanguage[#1]% + {\edef\askedlanguage{#1}% + \ifx\askedlanguage\empty \else + \ifcsname\l!prefix!\askedlanguage\endcsname + \edef\askedlanguage{\csname\l!prefix!\askedlanguage\endcsname}% + \ifx\currentlanguage\askedlanguage \else + \setcurrentlanguage\currentmainlanguage\askedlanguage + \docomplexlanguage + \fi + \else + \showmessage\m!linguals6{#1}% + \fi + \fi} + +\let\simplelanguage\normallanguage + +\definecomplexorsimple\language + +\def\mainlanguage[#1]% + {\edef\askedlanguage{#1}% + \ifx\askedlanguage\empty \else + \ifcsname\l!prefix!\askedlanguage\endcsname + \edef\askedlanguage{\csname\l!prefix!\askedlanguage\endcsname}% + \ifx\currentlanguage\askedlanguage + \ifx\currentmainlanguage\askedlanguage + \else + \setcurrentlanguage\askedlanguage\askedlanguage + \docomplexlanguage + \fi + \else + \setcurrentlanguage\askedlanguage\askedlanguage + \docomplexlanguage + \fi + \fi + \fi} + +%D \macros +%D {defaultlanguage,languageparameter,specificlanguageparameter} + +\def\defaultlanguage#1% + {\ifcsname\??la#1\s!default\endcsname + \expandafter\defaultlanguage\csname\??la#1\s!default\endcsname + \else + #1% + \fi} + +\def\languageparameter#1% + {\ifcsname\??la\currentlanguage#1\endcsname + \csname\??la\currentlanguage#1\endcsname + \else\ifcsname\??la\currentlanguage\s!default\endcsname + \expandafter\specificlanguageparameter\csname\??la\currentlanguage\s!default\endcsname{#1}% + \else\ifcsname\??la\s!default#1\endcsname + \csname\??la\s!default#1\endcsname + \fi\fi\fi} + +\def\specificlanguageparameter#1#2% + {\ifcsname\??la#1#2\endcsname + \csname\??la#1#2\endcsname + \else\ifcsname\??la#1\s!default\endcsname + \expandafter\specificlanguageparameter\csname\??la#1\s!default\endcsname{#2}% + \else\ifcsname\??la\s!default#2\endcsname + \csname\??la\s!default#2\endcsname + \fi\fi\fi} + +%D New (see nomarking and nolist): + +\def\splitsequence#1#2% + {\doifelse{#1}\v!no{#2}{\doifelse{#1}\v!yes{\languageparameter\c!limittext}{#1}}} + +\def\splitsymbol#1% + {\splitsequence{#1}{\languageparameter\c!limittext}} + +%D Just like with subsentence boundary symbols, quotes +%D placement depends on the current language, therefore we show +%D the defaults here. +%D +%D \def\ShowLanguageValues [#1] [#2] #3 #4 +%D {\blank +%D \startlinecorrection +%D \vbox\bgroup +%D \language[#1]% +%D \setbox0=\hbox to \hsize{\hss\bf#2 subsentence symbol and quotes\hss} +%D \dp0=0pt +%D \box0 +%D \vskip.5em +%D \hrule +%D \vskip.5em +%D \let\normalbar=| +%D \hbox to \hsize +%D {\hfil\quotation{#3 #4}\hfil\quote{#2}\hfil +%D \let|=\normalbar\strut|<||<|#3|>|#4|>|\hfil} +%D \vskip.5em +%D \hrule +%D \egroup +%D \stoplinecorrection +%D \blank} +%D +%D \ShowLanguageValues [af] [afrikaans] afrikaanse ... +%D \ShowLanguageValues [ca] [catalan] catalan ... +%D \ShowLanguageValues [cs] [czech] tjechisch tex +%D \ShowLanguageValues [cs] [slovak] slowaakse ... +%D \ShowLanguageValues [da] [danish] deense ... +%D \ShowLanguageValues [de] [german] duitse degelijkheid +%D \ShowLanguageValues [en] [english] engelse humor +%D \ShowLanguageValues [fi] [finnish] finse ... +%D \ShowLanguageValues [fr] [french] franse slag +%D \ShowLanguageValues [it] [italian] italiaanse ... +%D \ShowLanguageValues [la] [latin] latijnse missen +%D \ShowLanguageValues [nl] [dutch] nederlandse zuinigheid +%D \ShowLanguageValues [nb] [bokmal] noorse zalm +%D \ShowLanguageValues [nn] [nnynorsk] noorse zalm +%D \ShowLanguageValues [pl] [polish] poolse vlag +%D \ShowLanguageValues [pt] [portuguese] portugese ... +%D \ShowLanguageValues [es] [spanish] spaans benauwd +%D \ShowLanguageValues [sv] [swedish] zweedse ... +%D \ShowLanguageValues [tr] [turkish] turks fruit + +%D We support a lot of languages. These are specified and +%D loaded in separate files, according to their roots. Here +%D we only take care of (postponed) setting of the current +%D language. +%D +%D \unprotect +%D \placetable{The germanic languages (\type{lang-ger})} +%D \starttable[||||] +%D \HL +%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR +%D \HL +%D \NC \s!nl \NC dutch \NC germanic \NC\FR +%D \NC \s!en \NC english \NC germanic \NC\MR +%D \NC \s!de \NC german \NC germanic \NC\MR +%D \NC \s!da \NC danish \NC germanic \NC\MR +%D \NC \s!sv \NC swedish \NC germanic \NC\MR +%D \NC \s!af \NC afrikaans \NC germanic \NC\MR +%D \NC \s!nb \NC bokmal \NC germanic \NC\LR +%D \NC \s!nn \NC nynorsk \NC germanic \NC\LR +%D \HL +%D \stoptable +%D \protect +%D +%D \unprotect +%D \placetable{The italic languages (\type{lang-ita})} +%D \starttable[||||] +%D \HL +%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR +%D \HL +%D \NC \s!fr \NC french \NC italic \NC\FR +%D \NC \s!ca \NC catalan \NC italic \NC\MR +%D \NC \s!es \NC spanish \NC italic \NC\MR +%D \NC \s!it \NC italian \NC italic \NC\MR +%D \NC \s!la \NC latin \NC italic \NC\MR +%D \NC \s!pt \NC portuguese \NC italic \NC\LR +%D \HL +%D \stoptable +%D \protect +%D +%D \unprotect +%D \placetable{The slavic languages (\type{lang-sla})} +%D \starttable[||||] +%D \HL +%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR +%D \HL +%D \NC \s!pl \NC polish \NC slavic \NC\FR +%D \NC \s!cs \NC czech \NC slavic \NC\MR +%D \NC \s!sk \NC slavik \NC slavic \NC\LR +%D \HL +%D \stoptable +%D \protect +%D \unprotect +%D +%D \placetable{The altaic languages (\type{lang-alt})} +%D \starttable[||||] +%D \HL +%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR +%D \HL +%D \NC \s!tr \NC turkish \NC altaic \NC\SR +%D \HL +%D \stoptable +%D +%D \placetable{The uralic languages (\type{lang-ura})} +%D \starttable[||||] +%D \HL +%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR +%D \HL +%D \NC \s!fi \NC finnish \NC uralic \NC\SR +%D \HL +%D \stoptable +%D \protect + +% \bgroup \normallanguage255 \patterns{} \egroup +% \def\nopatterns{\normallanguage255 } + +\def\nopatterns{\normallanguage\minusone} + +%D We default to the language belonging to the interface. This +%D is one of the few places outside the interface modules where +%D \type{\startinterface} is used. + +%D We default to english: + +\setupcurrentlanguage[\s!en] + +\def\initializemainlanguage + {\mainlanguage[\currentlanguage]% + \showmessage\m!linguals9\currentlanguage} + +%D Might be in use: + +\let\preloadallpatterns\relax % just for old times sake +\let\preloadlanguages \relax % just for old times sake + +%D This might bexcome a seperate file: % mkiv only -- todo: internationalize command names % \loadspellchecklist[en][words-en.txt] +% \loadspellchecklist[us][words-en.txt] % \loadspellchecklist[nl][words-nl.txt] % \setupspellchecking[state=start] -\def\loadspellchecklist[#1][#2]% +\def\loadspellchecklist + {\dodoubleempty\doloadspellchecklist} + +\def\doloadspellchecklist[#1][#2]% {\ctxlua{languages.words.load("#1","#2")}} \def\setupspellchecking diff --git a/tex/context/base/lang-ini.tex b/tex/context/base/lang-ini.tex deleted file mode 100644 index 17393da33..000000000 --- a/tex/context/base/lang-ini.tex +++ /dev/null @@ -1,692 +0,0 @@ -%D \module -%D [ file=lang-ini, -%D version=1996.01.25, -%D title=\CONTEXT\ Language Macros, -%D subtitle=Initialization, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -%D This module needs a further cleanup (real split between ii/iv). - -%D This module implements the (for the moment still simple) -%D multi||language support of \CONTEXT, which should not be -%D confused with the multi||lingual interface. This support -%D will be extended when needed. - -\writestatus{loading}{Context Language Macros / Initialization} - -\startmessages dutch library: linguals - title: taal - 1: afbreekpatronen -- voor -- geladen (n=--,e=--,m=--) - 2: geen afbreekpatronen -- voor -- (n=--,e=--,m=--) (--,--) - 3: afbreekdefinities -- voor -- geladen (n=--,e=--,m=--) - 4: geen afbreekdefinities -- voor -- (n=--,e=--,m=--) - 5: afbreekpatronen voor -- niet geladen - 6: taal -- is niet gedefinieerd - 7: taal specifieke opties [--] introduceren een skip van -- - 8: taal specifieke opties [--] naadloos toegevoegd - 9: taal -- is actief - 10: patronen --geladen -\stopmessages - -\startmessages english library: linguals - title: language - 1: patterns -- for -- loaded (n=--,e=--,m=--) - 2: no patterns -- for -- (n=--,e=--,m=--) (--,--) - 3: hyphenations -- for -- loaded (n=--,e=--,m=--) - 4: no hyphenations -- for -- (n=--,e=--,m=--) - 5: patterns for -- not loaded - 6: language -- is undefined - 7: language specific options [--] introduce a -- skip - 8: language specific options [--] seamless appended - 9: language -- is active - 10: patterns --loaded -\stopmessages - -\startmessages german library: linguals - title: Sprache - 1: Trennmuster -- fuer -- geladen (n=--,e=--,m=--) - 2: Keine Trennmuster -- fuer -- (n=--,e=--,m=--) (--,--) - 3: Trenndefinitionen -- fuer -- geladen (n=--,e=--,m=--) - 4: Keine Trenndefinitionen -- fuer -- (n=--,e=--,m=--) - 5: Trennmuster fuer -- nicht geladen - 6: Sprache -- ist undefiniert - 7: Sprachenspezifische Option [--] fuegt eine Luecke von -- ein - 8: Sprachenspezifische Option [--] nahtlos hinzugefuegt - 9: Sprache -- ist aktiv - 10: Trennmuster --geladen -\stopmessages - -% TOM: 9 and 10 - -\startmessages czech library: linguals - title: jazyky - 1: vzory -- pro -- nacteny (n=--,e=--,m=--) - 2: zadne vzory -- pro -- (n=--,e=--,m=--) (--,--) - 3: deleni slov -- pro -- nacteno (n=--,e=--,m=--) - 4: zadne deleni slov -- pro -- (n=--,e=--,m=--) - 5: vzory pro -- nenacteny - 6: jazyk -- neni definovan - 7: specificke volby jazyka [--] zavadeji -- (zavlecenou) mezeru - 8: specificke volby jazyka [--] bez mezer pripojeny - 9: language -- is active - 10: vzory --nacteny -\stopmessages - -\startmessages italian library: linguals - title: lingua - 1: schemi -- per -- caricati (n=--,e=--,m=--) - 2: niente schemi -- per -- (n=--,e=--,m=--) (--,--) - 3: sillabazione -- per -- caricata (n=--,e=--,m=--) - 4: niente sillabazione -- per -- (n=--,e=--,m=--) - 5: schemi per -- non caricati - 6: lingua -- non definita - 7: opzioni specifiche per la lingua [--] introducono un salto -- - 8: opzioni specifiche per la lingua [--] aggiunte trasparentemente - 9: lingua -- attiva - 10: schemi -- caricati -\stopmessages - -\startmessages norwegian library: linguals - title: sprøk - 1: orddelingsmønster -- for -- er lest inn (n=--,e=--,m=--) - 2: ingen orddelingsmønster -- for -- (n=--,e=--,m=--) (--,--) - 3: orddelingsdefinisjon -- for -- er lest inn (n=--,e=--,m=--) - 4: ingen orddelingsdefinisjon -- for -- (n=--,e=--,m=--) - 5: orddelingsmønster for -- er ikke lest inn - 6: spràk -- er udefinert - 7: spràk spesifikk opsjon [--] introduserer et -- hopp - 8: spràk spesifikk opsjon [--] problemfritt tilføyd - 9: spràk -- er aktivt - 10: orddelingsmønster -- er lest inn -\stopmessages - -\startmessages romanian library: linguals - title: limbi - 1: sablonul -- pentru -- s-a incarcat (n=--,e=--,m=--) - 2: nu exista sabloane -- pentru -- (n=--,e=--,m=--) (--,--) - 3: despartirea in silabe -- pentru -- s-a incarcat (n=--,e=--,m=--) - 4: nu exista despartire in silabe -- pentru -- (n=--,e=--,m=--) - 5: sabloanele pentru -- nu sunt incarcate - 6: limba -- nu este definita - 7: optiunile specifice ale limbii [--] introduc un spatiu -- - 8: optiunile specifice ale limbii [--] adaugate - 9: limba -- este activa - 10: sabloanele -- incarcate -\stopmessages - -\startmessages french library: linguals - title: langue - 1: les motifs -- pour -- sont chargés (n=--,e=--,m=--) - 2: pas de motifs -- pour -- (n=--,e=--,m=--) (--,--) - 3: hyphenations -- pour -- chargés (n=--,e=--,m=--) - 4: pas d'hyphenations -- pour -- (n=--,e=--,m=--) - 5: les motifs pour -- ne sont pas chargés - 6: langue -- non définie - 7: les options spécifiques de langue [--] introduisent un -- saut - 8: les options spécifiques de langue [--] sont ajoutés en douceur - 9: la langue -- est active - 10: motifs -- chargés -\stopmessages - -\unprotect - -\ifx\nonfrenchspacing\undefined \let\nonfrenchspacing\relax \fi -\ifx\frenchspacing \undefined \let\frenchspacing \relax \fi - -%D When loading hyphenation patterns, \TEX\ assign a number to -%D each loaded table, starting with~0. Switching to a specific -%D table is done by assigning the relevant number to the -%D predefined \COUNTER\ \type{\language}. Unfortunately the -%D name of this command suits very well the name of the -%D language switching command we are to define, so let's save -%D this primitive under another name: - -\let\normallanguage\language - -%D We keep track of the last loaded patterns by means of a -%D pseudo \COUNTER. This just one of those situations in which -%D we don't want to spent a real one. Language zero has no -%D patterns, first of all because I like to start numbering -%D at one. It may come in handy for special purposes as well. - -\normallanguage\zerocount \def\loadedlanguage{1} - -%D \macros -%D {currentlanguage, setupcurrentlanguage} -%D -%D Instead of numbers,we are going to use symbolic names for -%D the languages. The current langage is saved in the macro -%D \type {\currentlanguage}. The setup macro is mainly used -%D for cosmetic purposes. -%D -%D \starttyping -%D \dorecurse{3} -%D {\language[nl] -%D \startmode[*en] english \stopmode -%D \startmode[*nl] dutch \stopmode -%D \language[en] -%D \startmode[*en] english \stopmode -%D \startmode[*nl] dutch \stopmode} -%D \stoptyping - -\let\currentlanguage \empty -\let\currentmainlanguage\empty - -\def\setupcurrentlanguage[#1]{\setcurrentlanguage\currentmainlanguage{#1}} - -\def\setcurrentlanguage#1#2% sets modes: **id (currentmain) *id (current) - {\doifsomething{#1} - {\ifx\currentmainlanguage\empty\else\resetsystemmode{\systemmodeprefix\currentmainlanguage}\fi - \edef\currentmainlanguage{#1}% - \setsystemmode{\systemmodeprefix\currentmainlanguage}}% - \doifsomething{#2} - {\ifx\currentlanguage\empty\else\resetsystemmode\currentlanguage\fi - \edef\currentlanguage{#2}% - \setsystemmode\currentlanguage}} - -%D The internal macros will be defined later. - -%D \macros -%D {installlanguage} -%D -%D Hyphenation patterns can only be loaded when the format file -%D is prepared. The next macro takes care of this loading. A -%D language is specified with -%D -%D \showsetup{installlanguage} -%D -%D When \type {state} equals \type {start}, both patterns -%D and additional hyphenation specifications are loaded. These -%D files are seached for on the system path and are to be -%D named: -%D -%D \starttyping -%D \f!languageprefix-identifier.\f!patternsextension -%D \f!languageprefix-identifier.\f!hyhensextension -%D \stoptyping -%D -%D The \type{spacing} variable specifies how the spaces after -%D punctuation has to be handled. English is by tradition more -%D tolerant to inter||sentence spacing than other languages. -%D -%D This macro also defines \type {\identifier} as a shortcut -%D switch to the language. Furthermore the command defined as -%D being language specific, are executed. With -%D \type {default} we can default to another language -%D (patterns) at format generation time. This default language -%D is overruled when the appropriate patterns are loaded (some -%D implementations support run time addition of patterns to a -%D preloaded format). - -\def\dodoinstalllanguage#1#2% #2 added - {\doifundefined{#1}{\setvalue{#1}{\complexlanguage[#2]}}% - \expanded{\noexpand\uppercase{\noexpand\edef\noexpand\ascii{#1}}}% - \doifundefined\ascii{\setvalue\ascii{\complexlanguage[#2]}}} - -%D \macros -%D {preloadlanguages} -%D -%D We first try to load the files defined as file synonym -%D for \type {lang-*.pat} and \type {lang-*.hyp}. After that we -%D fall back on those files. The macro \type {\preloadpatterns} -%D reports which patterns are loaded and what hyphenmin -%D values are set. - -\let\installedlanguages\empty - -\def\doiflanguageelse#1{\doifdefinedelse{\??la#1\c!state}} - -\ifx\mkloadlanguagefiles\undefined \let\mkloadlanguagefiles\gobbleoneargument \fi - -\def\doinstalllanguage[#1][#2]% some day we will make one for mkii and mkiv - {\doifassignmentelse{#2} - {\doiflanguageelse{#1} - {\getparameters[\??la#1][#2]} - {\setvalue{\l!prefix!#1}{#1}% - \addtocommalist{#1}\installedlanguages - \dodoinstalllanguage{#1}{#1}% - \getparameters - [\??la#1] - [\c!state=\v!stop, - \c!default=, - \s!patterns=, - \s!mapping=, - \s!encoding=, - \s!lefthyphenmin=\defaultlanguageparameter\s!lefthyphenmin, - \s!righthyphenmin=\defaultlanguageparameter\s!righthyphenmin, - #2]}% - \doifvalue{\??la#1\c!default}{#1}{\letvalue{\??la#1\c!default}\empty}% - % loop in deo: \doifvalue{\??la#1\s!patterns}{#1}{\letvalue{\??la#1\c!default}\empty}% - \mkloadlanguagefiles{#1}} - {\setvalue{\l!prefix!#1}{#2}% - \dodoinstalllanguage{#1}{#2}}} - -\def\reallanguagetag#1% - {\ifcsname\l!prefix!#1\endcsname - %\expandafter\reallanguagetag\csname\l!prefix!#1\endcsname % evt undefined en dan wel - \csname\l!prefix!#1\endcsname - \else - #1% - \fi} - -% ^^ \language[#1] gave unwanted side effect of loading language specifics - -\def\installlanguage - {\dodoubleargument\doinstalllanguage} - -%D When the second argument is a language identifier, a -%D synonym is created. This feature is present because we -%D used dutch mnemonics in the dutch version, but nowadays -%D conform a standard. - -\let \patternencoding \s!default -\let \patternmapping \s!default - -\ifx\mkloadpatterns \undefined \let\mkloadpatterns \gobbletwoarguments \fi -\ifx\mkdoifpatternselse\undefined \let\mkdoifpatternselse\gobbletwoarguments \fi - -\def\doloadpatterns {\mkdoloadpatterns} -\def\doifpatternselse{\mkdoifpatternselse} - -%D \macros -%D {setuplanguage} -%D -%D Quick and dirty, but useful: -%D -%D \showsetup{setuplanguage} -%D -%D Beware, this command can only be used when a language is installed. - -\unprotected \def\setuplanguage - {\dodoubleempty\dosetuplanguage} - -\def\dosetuplanguage[#1][#2]% handy patch for testing - {\ifsecondargument - \getparameters[\??la#1][#2]% - \doif{#1}\currentlanguage\docomplexlanguage - \else - \getparameters[\??la\currentlanguage][#1]% - \docomplexlanguage - \fi} - -\setuplanguage - [\s!default] - [\s!lefthyphenmin=2, - \s!righthyphenmin=2, - \c!spacing=\v!packed, - \c!lefthyphen=, - \c!righthyphen=-, - \c!hyphen=-, - \c!midsentence=---, - \c!leftsentence=---, - \c!rightsentence=---, - \c!leftsubsentence=---, - \c!rightsubsentence=---, - \c!leftquote=\upperleftsinglesixquote, - \c!rightquote=\upperrightsingleninequote, - \c!leftquotation=\upperleftdoublesixquote, - \c!rightquotation=\upperrightdoubleninequote, - \c!leftspeech=\languageparameter\c!leftquotation, - \c!middlespeech=, - \c!rightspeech=\languageparameter\c!rightquotation, - \c!limittext=\unknown, - \c!date={\v!year,\ ,\v!month,\ ,\v!day}, - \c!text=Ag] - -% rather new, split and per language - -\setuplanguage - [\s!default] - [\c!compoundhyphen=\compoundhyphen, - \c!leftcompoundhyphen=\compoundhyphen, - \c!rightcompoundhyphen=] - -%D The values \type {leftsentence} and \type -%D {rightsentence} can be (and are) used to implement -%D automatic subsentence boundary glyphs, like in {\fr -%D |<|french guillemots|>|} or {\de |<|german guillemots|>|} or -%D {\nl |<|dutch dashes|>|} like situations. Furthermore \type -%D {leftquotation} and \type {leftquote} come into view -%D \quotation {when we quote} or \quote {quote} something. - -%D \macros -%D {currentdatespecification} -%D -%D Just to make things easy we can ask for the current date -%D specification by saying: - -\def\currentdatespecification{\languageparameter\c!date} - -%D This command is not meant for users. - -%D Carefull reading of these macros shows that it's legal to -%D say -%D -%D \starttyping -%D \installlanguage [du] [de] -%D \stoptyping - -%D \macros -%D {language,mainlanguage} -%D -%D Switching to another language (actually another hyphenation -%D pattern) is done with: -%D -%D \starttyping -%D \language[identifier] -%D \stoptyping -%D -%D or with \type{\identifier}. Just to be compatible with -%D \PLAIN\ \TEX, we still support the original meaning, so -%D -%D \starttyping -%D \language=1 -%D \stoptyping -%D -%D is a valid operation, where the relation between number -%D and language depends on the order in installing languages. -%D -%D \showsetup{language} -%D \showsetup{mainlanguage} -%D -%D Both commands take a predefined language identifier as -%D argument. We can use \type{\mainlanguage[identifier]} for -%D setting the (indeed) main language. This is the language -%D used for translating labels like {\em figure} and {\em -%D table}. The main language defaults to the current language. -%D -%D We take care of local as well as standardized language -%D switching (fr and fa, de and du, but nl and nl). - -\ifx\synchronizepatterns \undefined \let\synchronizepatterns\relax \fi -\ifx\synchronizepatternswithfont\undefined \def\synchronizepatternswithfont{\synchronizepatterns} \fi - -\ifx\mksetnormallanguage\undefined \let\mksetnormallanguage\gobbletwoarguments \fi - -\def\setnormallanguage{\mksetnormallanguage} - -\newevery \everylanguage \relax -\newevery \everyresetlanguagespecifics \relax - -\def\disablelanguagespecifics - {\ignorecompoundcharacter} - -\def\sethyphenationvariables - {\lefthyphenmin 0\languageparameter\s!lefthyphenmin \relax - \righthyphenmin0\languageparameter\s!righthyphenmin\relax - \lefthyphenmin \numexpr\lefthyphenmin +\hyphenminoffset\relax - \righthyphenmin\numexpr\righthyphenmin+\hyphenminoffset\relax} - -\def\docomplexlanguage% assumes that \currentlanguage is set - {\edef\currentdefaultlanguage{\defaultlanguage\currentlanguage}% - \mksetnormallanguage\currentlanguage\currentdefaultlanguage - \the\everylanguage - \enablelanguagespecifics[\currentlanguage]% - \sethyphenationvariables - \relax - % will be definable and move to core-spa ! - \doifelse{\languageparameter\c!spacing}\v!broad - \nonfrenchspacing\frenchspacing} - -\ifx\enablelanguagespecifics\undefined \def\enablelanguagespecifics[#1]{} \fi - -% The following may be a solution for the fact that one cannot -% change catcodes of characters like : and ; inside an environment. - -\appendtoks - \enablelanguagespecifics[\currentlanguage]% -\to \everystarttext - -\def\complexlanguage[#1]% - {\edef\askedlanguage{#1}% - \ifx\askedlanguage\empty \else - \ifcsname\l!prefix!\askedlanguage\endcsname - \edef\askedlanguage{\csname\l!prefix!\askedlanguage\endcsname}% - \ifx\currentlanguage\askedlanguage \else - \setcurrentlanguage\currentmainlanguage\askedlanguage - \docomplexlanguage - \fi - \else - \showmessage\m!linguals6{#1}% - \fi - \fi} - -\let\simplelanguage\normallanguage - -\definecomplexorsimple\language - -% \def\mainlanguage[#1]% -% {\edef\askedlanguage{#1}% -% \ifx\askedlanguage\empty \else -% \ifcsname\l!prefix!\askedlanguage\endcsname -% \edef\askedlanguage{\csname\l!prefix!\askedlanguage\endcsname}% -% \ifx\currentmainlanguage\askedlanguage \else -% \setcurrentlanguage\askedlanguage\askedlanguage -% \docomplexlanguage -% \fi -% \fi -% \fi} - -\def\mainlanguage[#1]% - {\edef\askedlanguage{#1}% - \ifx\askedlanguage\empty \else - \ifcsname\l!prefix!\askedlanguage\endcsname - \edef\askedlanguage{\csname\l!prefix!\askedlanguage\endcsname}% - \ifx\currentlanguage\askedlanguage - \ifx\currentmainlanguage\askedlanguage - \else - \setcurrentlanguage\askedlanguage\askedlanguage - \docomplexlanguage - \fi - \else - \setcurrentlanguage\askedlanguage\askedlanguage - \docomplexlanguage - \fi - \fi - \fi} - -%D \macros -%D {defaultlanguage,languagedefault} -%D -%D The macro \type {\defaultlanguage{id}} expands into the -%D default language, when defined, while \type -%D {\languagedefault{id}\c!parameter} returns the default's -%D parameter. - -\def\defaultlanguage#1% - {\@EA\ifx\csname\??la#1\c!default\endcsname\empty - #1% - \else - \@EA\defaultlanguage\csname\??la#1\c!default\endcsname - \fi} - -\def\languagedefault#1#2% - {\csname\??la\defaultlanguage{#1}#2\endcsname} - -\def\languageparameter % @EA = speedup - {\@EA\dolanguageparameter\@EA{\defaultlanguage\currentlanguage}} - -\def\specificlanguageparameter#1% @EA = speedup - {\@EA\dospecificlanguageparameter\@EA{\defaultlanguage{#1}}{#1}} - -\def\xxlanguageparameter#1% @EA = speedup - {\@EA\dolanguageparameter\@EA{\defaultlanguage{#1}}} - -\def\defaultlanguageparameter#1% - {\csname\??la\s!default#1\endcsname} - -\def\dolanguageparameter#1#2% - {\csname\??la - \ifcsname\??la\currentlanguage#2\endcsname - \currentlanguage - \else\ifcsname\??la#1#2\endcsname - \@EA\ifx\csname\??la#1#2\endcsname\empty\s!default\else#1\fi - \else - \s!default - \fi\fi - #2\endcsname} - -\def\dospecificlanguageparameter#1#2#3% - {\csname\??la - \ifcsname\??la#2#3\endcsname - \@EA\ifx\csname\??la#2#3\endcsname\empty\s!default\else#2\fi - \else\ifcsname\??la#1#3\endcsname - \@EA\ifx\csname\??la#1#3\endcsname\empty\s!default\else#1\fi - \else - \s!default - \fi\fi - #3\endcsname} - -%D New (see nomarking and nolist): - -\def\splitsequence#1#2% - {\doifelse{#1}\v!no{#2}{\doifelse{#1}\v!yes{\languageparameter\c!limittext}{#1}}} - -\def\splitsymbol#1% - {\splitsequence{#1}{\languageparameter\c!limittext}} - -%D Just like with subsentence boundary symbols, quotes -%D placement depends on the current language, therefore we show -%D the defaults here. -%D -%D \def\ShowLanguageValues [#1] [#2] #3 #4 -%D {\blank -%D \startlinecorrection -%D \vbox\bgroup -%D \language[#1]% -%D \setbox0=\hbox to \hsize{\hss\bf#2 subsentence symbol and quotes\hss} -%D \dp0=0pt -%D \box0 -%D \vskip.5em -%D \hrule -%D \vskip.5em -%D \let\normalbar=| -%D \hbox to \hsize -%D {\hfil\quotation{#3 #4}\hfil\quote{#2}\hfil -%D \let|=\normalbar\strut|<||<|#3|>|#4|>|\hfil} -%D \vskip.5em -%D \hrule -%D \egroup -%D \stoplinecorrection -%D \blank} -%D -%D \ShowLanguageValues [af] [afrikaans] afrikaanse ... -%D \ShowLanguageValues [ca] [catalan] catalan ... -%D \ShowLanguageValues [cs] [czech] tjechisch tex -%D \ShowLanguageValues [cs] [slovak] slowaakse ... -%D \ShowLanguageValues [da] [danish] deense ... -%D \ShowLanguageValues [de] [german] duitse degelijkheid -%D \ShowLanguageValues [en] [english] engelse humor -%D \ShowLanguageValues [fi] [finnish] finse ... -%D \ShowLanguageValues [fr] [french] franse slag -%D \ShowLanguageValues [it] [italian] italiaanse ... -%D \ShowLanguageValues [la] [latin] latijnse missen -%D \ShowLanguageValues [nl] [dutch] nederlandse zuinigheid -%D \ShowLanguageValues [nb] [bokmal] noorse zalm -%D \ShowLanguageValues [nn] [nnynorsk] noorse zalm -%D \ShowLanguageValues [pl] [polish] poolse vlag -%D \ShowLanguageValues [pt] [portuguese] portugese ... -%D \ShowLanguageValues [es] [spanish] spaans benauwd -%D \ShowLanguageValues [sv] [swedish] zweedse ... -%D \ShowLanguageValues [tr] [turkish] turks fruit - -%D We support a lot of languages. These are specified and -%D loaded in separate files, according to their roots. Here -%D we only take care of (postponed) setting of the current -%D language. -%D -%D \unprotect -%D \placetable{The germanic languages (\type{lang-ger})} -%D \starttable[||||] -%D \HL -%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR -%D \HL -%D \NC \s!nl \NC dutch \NC germanic \NC\FR -%D \NC \s!en \NC english \NC germanic \NC\MR -%D \NC \s!de \NC german \NC germanic \NC\MR -%D \NC \s!da \NC danish \NC germanic \NC\MR -%D \NC \s!sv \NC swedish \NC germanic \NC\MR -%D \NC \s!af \NC afrikaans \NC germanic \NC\MR -%D \NC \s!nb \NC bokmal \NC germanic \NC\LR -%D \NC \s!nn \NC nynorsk \NC germanic \NC\LR -%D \HL -%D \stoptable -%D \protect -%D -%D \unprotect -%D \placetable{The italic languages (\type{lang-ita})} -%D \starttable[||||] -%D \HL -%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR -%D \HL -%D \NC \s!fr \NC french \NC italic \NC\FR -%D \NC \s!ca \NC catalan \NC italic \NC\MR -%D \NC \s!es \NC spanish \NC italic \NC\MR -%D \NC \s!it \NC italian \NC italic \NC\MR -%D \NC \s!la \NC latin \NC italic \NC\MR -%D \NC \s!pt \NC portuguese \NC italic \NC\LR -%D \HL -%D \stoptable -%D \protect -%D -%D \unprotect -%D \placetable{The slavic languages (\type{lang-sla})} -%D \starttable[||||] -%D \HL -%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR -%D \HL -%D \NC \s!pl \NC polish \NC slavic \NC\FR -%D \NC \s!cs \NC czech \NC slavic \NC\MR -%D \NC \s!sk \NC slavik \NC slavic \NC\LR -%D \HL -%D \stoptable -%D \protect -%D \unprotect -%D -%D \placetable{The altaic languages (\type{lang-alt})} -%D \starttable[||||] -%D \HL -%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR -%D \HL -%D \NC \s!tr \NC turkish \NC altaic \NC\SR -%D \HL -%D \stoptable -%D -%D \placetable{The uralic languages (\type{lang-ura})} -%D \starttable[||||] -%D \HL -%D \NC \bf mnemonic \NC \bf language \NC \bf group \NC\SR -%D \HL -%D \NC \s!fi \NC finnish \NC uralic \NC\SR -%D \HL -%D \stoptable -%D \protect - -% \bgroup \normallanguage255 \patterns{} \egroup -% \def\nopatterns{\normallanguage255 } - -\def\nopatterns{\normallanguage\minusone} - -%D Mark plugin: - -\loadmarkfile{lang-ini} % not yet - -%D We default to the language belonging to the interface. This -%D is one of the few places outside the interface modules where -%D \type{\startinterface} is used. - -%D We default to english: - -\setupcurrentlanguage[\s!en] - -\appendtoks\mainlanguage[\currentlanguage]\to\everyjob - -\appendtoks\showmessage\m!linguals9\currentlanguage\to\everyjob - -\protect \endinput diff --git a/tex/context/base/lang-ita.tex b/tex/context/base/lang-ita.tex index 93a169112..ae3b7a514 100644 --- a/tex/context/base/lang-ita.tex +++ b/tex/context/base/lang-ita.tex @@ -13,7 +13,7 @@ % Todo: replace \'.. by \namedglyph -\writestatus{loading}{Italic Languages} +\writestatus{loading}{ConTeXt Language Macros / Italic Languages} %D The framework of this module is set up by Hans Hagen while %D many of the first translations were done by Tobias. Later @@ -47,7 +47,8 @@ \c!leftquotation=\leftguillemot, \c!rightquotation=\rightguillemot, \c!date={\v!day+,\v!space,\v!month,\v!space,\v!year}, - \c!state=\v!stop] + \s!mapping={texnansi,ec}, + \s!encoding={texnansi,ec}] \installlanguage [\s!es] @@ -60,8 +61,7 @@ \c!rightquote=\upperrightsingleninequote, \c!leftquotation=\upperleftdoublesixquote, \c!rightquotation=\upperrightdoubleninequote, - \c!date={\v!day,\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] + \c!date={\v!day,\ ,\v!month,\ ,\v!year}] \installlanguage [sp] [\s!es] % old times context @@ -76,8 +76,7 @@ \c!rightquote=\upperrightsingleninequote, \c!leftquotation=\upperleftdoublesixquote, \c!rightquotation=\upperrightdoubleninequote, - \c!date={\v!day,\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] + \c!date={\v!day,\ ,\v!month,\ ,\v!year}] % Note GB left|/|right (sub)sentences are for \quote {incisi}. @@ -96,7 +95,8 @@ \c!middlespeech=\leftguillemot, \c!rightspeech=\rightguillemot, \c!date={\v!day,\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] + \s!mapping={texnansi,ec}, + \s!encoding={texnansi,ec}] \installlanguage % the same as italian [\s!la] @@ -109,8 +109,7 @@ \c!rightquote=\lowerrightsingleninequote, \c!leftquotation=\upperleftdoublesixquote, \c!rightquotation=\lowerrightdoubleninequote, - \c!date={\v!day,\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] + \c!date={\v!day,\ ,\v!month,\ ,\v!year}] \installlanguage [\s!pt] @@ -124,7 +123,8 @@ \c!leftquotation=\upperleftdoublesixquote, \c!rightquotation=\upperrightdoubleninequote, \c!date={\v!year,\ ,\v!month,\ ,\v!day}, - \c!state=\v!stop] + \s!mapping={texnansi,ec}, + \s!encoding={texnansi,ec}] \installlanguage [\s!ro] @@ -137,8 +137,7 @@ \c!rightquote=\rightguillemot, \c!leftquotation=\lowerrightdoubleninequote, \c!rightquotation=\upperleftdoublesixquote, - \c!date={\v!day,\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] + \c!date={\v!day,\ ,\v!month,\ ,\v!year}] %D For compatibility reasons we also define: @@ -227,8 +226,8 @@ \setupheadtext [\s!ro] [\v!units=Unit\u{a}\c{t}i] \setuplabeltext [\s!fr] [\v!table=Tableau ] -\setuplabeltext [\s!es] [\v!table=Tablas ] -\setuplabeltext [\s!ca] [\v!table=Taules ] +\setuplabeltext [\s!es] [\v!table=Tabla ] +\setuplabeltext [\s!ca] [\v!table=Taula ] \setuplabeltext [\s!it] [\v!table=Tabella ] \setuplabeltext [\s!la] [\v!table=Tabula ] \setuplabeltext [\s!pt] [\v!table=Tabela ] @@ -259,48 +258,48 @@ \setuplabeltext [\s!ro] [\v!graphic=Graficul ] \setuplabeltext [\s!fr] [\v!chapter=] -\setuplabeltext [\s!es] [\v!chapter=] -\setuplabeltext [\s!ca] [\v!chapter=] +\setuplabeltext [\s!es] [\v!chapter=Cap\'\itulo] +\setuplabeltext [\s!ca] [\v!chapter=Cap\'\itol] \setuplabeltext [\s!it] [\v!chapter=] \setuplabeltext [\s!la] [\v!chapter=] \setuplabeltext [\s!pt] [\v!chapter=] \setuplabeltext [\s!ro] [\v!chapter=] \setuplabeltext [\s!fr] [\v!section=] -\setuplabeltext [\s!es] [\v!section=] -\setuplabeltext [\s!ca] [\v!section=] +\setuplabeltext [\s!es] [\v!section=Secci\'on] +\setuplabeltext [\s!ca] [\v!section=Secci\'o] \setuplabeltext [\s!it] [\v!section=] \setuplabeltext [\s!la] [\v!section=] \setuplabeltext [\s!pt] [\v!section=] \setuplabeltext [\s!ro] [\v!section=] \setuplabeltext [\s!fr] [\v!subsection=] -\setuplabeltext [\s!es] [\v!subsection=] -\setuplabeltext [\s!ca] [\v!subsection=] +\setuplabeltext [\s!es] [\v!subsection=Subsecci\'on] +\setuplabeltext [\s!ca] [\v!subsection=Subsecci\'o] \setuplabeltext [\s!it] [\v!subsection=] \setuplabeltext [\s!la] [\v!subsection=] \setuplabeltext [\s!pt] [\v!subsection=] \setuplabeltext [\s!ro] [\v!subsection=] \setuplabeltext [\s!fr] [\v!subsubsection=] -\setuplabeltext [\s!es] [\v!subsubsection=] -\setuplabeltext [\s!ca] [\v!subsubsection=] +\setuplabeltext [\s!es] [\v!subsubsection=Subsubsecci\'on] +\setuplabeltext [\s!ca] [\v!subsubsection=Subsubsecci\'o] \setuplabeltext [\s!it] [\v!subsubsection=] \setuplabeltext [\s!la] [\v!subsubsection=] \setuplabeltext [\s!pt] [\v!subsubsection=] \setuplabeltext [\s!ro] [\v!subsubsection=] \setuplabeltext [\s!fr] [\v!subsubsubsection=] -\setuplabeltext [\s!es] [\v!subsubsubsection=] -\setuplabeltext [\s!ca] [\v!subsubsubsection=] +\setuplabeltext [\s!es] [\v!subsubsubsection=Subsubsubsecci\'on] +\setuplabeltext [\s!ca] [\v!subsubsubsection=Subsubsubsecci\'o] \setuplabeltext [\s!it] [\v!subsubsubsection=] \setuplabeltext [\s!la] [\v!subsubsubsection=] \setuplabeltext [\s!pt] [\v!subsubsubsection=] \setuplabeltext [\s!ro] [\v!subsubsubsection=] \setuplabeltext [\s!fr] [\v!appendix=] -\setuplabeltext [\s!es] [\v!appendix=] -\setuplabeltext [\s!ca] [\v!appendix=] +\setuplabeltext [\s!es] [\v!appendix=Ap\'endice] +\setuplabeltext [\s!ca] [\v!appendix=Ap\`endix] \setuplabeltext [\s!it] [\v!appendix=] \setuplabeltext [\s!la] [\v!appendix=] \setuplabeltext [\s!pt] [\v!appendix=] @@ -479,14 +478,12 @@ %D Rather new \unknown -\setuplabeltext [\s!it] [\v!page=pagina ] -\setuplabeltext [\s!it] [\v!atpage=a pagina ] +\setuplabeltext [\s!it] [\v!page=pagina ] +\setuplabeltext [\s!it] [\v!atpage=a pagina ] \setuplabeltext [\s!it] [\v!hencefore=come mostrato sopra] \setuplabeltext [\s!it] [\v!hereafter=come mostrato sotto] \setuplabeltext [\s!it] [\v!see=cf. ] -\setuplabeltext[\s!fr] [\v!see=voir ] - %D Ordinal converters: \def\frordinaldaynumber#1% date is masculine diff --git a/tex/context/base/lang-jap.tex b/tex/context/base/lang-jap.tex index ffb53ea70..05c9b1d41 100644 --- a/tex/context/base/lang-jap.tex +++ b/tex/context/base/lang-jap.tex @@ -13,7 +13,7 @@ % rgabriel@kerio.com -\writestatus{loading}{Context Language Macros / Japanese} +\writestatus{loading}{ConTeXt Language Macros / Japanese} \unprotect @@ -29,8 +29,7 @@ \c!rightquote=\jaencoding\jaencodedsingleendquote, \c!leftquotation=\jaencoding\jaencodedstartquote, \c!rightquotation=\jaencoding\jaencodedendquote, - \c!date={\jaencodedchristiandate,\v!year,\jaencodedyear,\v!month,\jaencodedmonth,\v!day,\jaencodedday}, - \c!state=\v!stop] + \c!date={\jaencodedchristiandate,\v!year,\jaencodedyear,\v!month,\jaencodedmonth,\v!day,\jaencodedday}] \setupheadtext [\s!ja] [\v!content={\jaencoding\jaencodedtableofcontents}] \setupheadtext [\s!ja] [\v!tables={\jaencoding\jaencodedtables}] diff --git a/tex/context/base/lang-lab.mkii b/tex/context/base/lang-lab.mkii new file mode 100644 index 000000000..269ac249b --- /dev/null +++ b/tex/context/base/lang-lab.mkii @@ -0,0 +1,295 @@ +%D \module +%D [ file=lang-lab, +%D version=1997.08.27, +%D title=\CONTEXT\ Language Macros, +%D subtitle=Labels, +%D author=Hans Hagen / Tobias Burnus, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\unprotect + +%D In this module we deal with language dependant labels and +%D prefixes, like in {\em Figure~12} and {\em Chapter 1}. In +%D this file we set the default values. Users can easily +%D overrule these. +%D +%D This module is dedicated to the grandfather of Tobias +%D Burnus, who's extensive languages oriented library helped us +%D a lot in finding the right translations. All those labels +%D are collected in files that reflect their common ancestor. +%D +%D Not all languages can be satisfied with the labeling +%D mechanism as provided here. Chinese for instance put a label +%D in front as well as after a part number. This is why the +%D current implementation of labels supports two labels too. + +%D \macros +%D {setupheadtext, setuplabeltext} +%D +%D First we present some macros that deal with what we will +%D call head and label texts. Such texts are defines by: +%D +%D \showsetup{setupheadtext} +%D \showsetup{setuplabeltext} +%D +%D In a few paragraphs we'll show quite a lot of examples +%D of its use. + +\let\handletextprefix\relax + +\def\setupheadtext {\dosetupsometextprefix[\c!title]} +\def\setuplabeltext{\dosetupsometextprefix[\c!label]} + +\def\dosetupsometextprefix + {\let\dodocommand\xdosetupsometextprefix + \dotripleempty\dodosetupsometextprefix} + +% \def\dodosetupsometextprefix[#1][#2][#3]% +% {\ifthirdargument +% \def\docommand##1{\dodocommand[#1#2][##1]}% +% \processcommalist[#3]\docommand +% \else +% \def\docommand##1{\dodocommand[#1\currentmainlanguage][##1]}% +% \processcommalist[#2]\docommand +% \fi} + +\def\dodosetupsometextprefix[#1][#2][#3]% + {\ifthirdargument + \def\docommand##1{\expanded{\dodocommand[#1\reallanguagetag{#2}]}[##1]}% + \processcommalist[#3]\docommand + \else + \def\docommand##1{\expanded{\dodocommand[#1\reallanguagetag\currentmainlanguage]}[##1]}% + \processcommalist[#2]\docommand + \fi} + +\def\doassignsometextprefix[#1][#2,#3,#4]% + {\setvalue{#1}{\handletextprefix{#2}{#3}}} + +\def\xdosetupsometextprefix[#1][#2=#3]% + {\doassignsometextprefix[#1#2][#3,,]} + +%D By changing the meaning of \type {\handletextprefix} we +%D can filter the left and right labeltext as well as convert +%D labels to uppercase. +%D +%D These commands accept all kind of inputs: +%D +%D \starttyping +%D \setuplabeltext [language] [labellabel=text] +%D \setuplabeltext [language] [labellabel=text,labellabel=text,...] +%D \setuplabeltext [labellabel=text] +%D \setuplabeltext [labellabel=text,labellabel=text,...] +%D \stoptyping +%D +%D The last two cases concern the current language. + +%D \macros +%D {headtext, +%D labeltext, leftlabeltext, rightlabeltext, labeltexts, +%D LABELTEXT, LEFTLABELTEXT, RIGHTLABELTEXT, LABELTEXTS} +%D +%D Once defined, head and label texts can be called upon using: +%D +%D \showsetup{headtext} +%D \showsetup{labeltext} +%D +%D The latter one has an upcased alternative \type{\LABELTEXT}. + +% \def\labellanguage{\currentmainlanguage} +% \def\headlanguage {\currentmainlanguage} + +% \def\labellanguage{\defaultlanguage\currentmainlanguage} +% \def\headlanguage {\defaultlanguage\currentmainlanguage} + +\def\labellanguage{\reallanguagetag{\defaultlanguage\currentmainlanguage}} +\def\headlanguage {\reallanguagetag{\defaultlanguage\currentmainlanguage}} + +\appendtoks \let\labellanguage\currentlanguage \to \everycurrentdate + +\unexpanded\def\headtext + {\let\handletextprefix\firstoftwoarguments + \let\reporttextprefixerror\doreporttextprefixerror + \global\labeltextdonetrue + \dogetupsometextprefix\headlanguage\c!title} + +\unexpanded\def\leftlabeltext + {\let\handletextprefix\firstoftwoarguments + \let\reporttextprefixerror\doreporttextprefixerror + \global\labeltextdonetrue + \dogetupsometextprefix\labellanguage\c!label} + +\unexpanded\def\rightlabeltext + {\let\handletextprefix\secondoftwoarguments + \let\reporttextprefixerror\doreporttextprefixerror + \global\labeltextdonetrue + \dogetupsometextprefix\labellanguage\c!label} + +\unexpanded\def\LEFTLABELTEXT + {\def\handletextprefix##1##2{\uppercase{##1}}\DOLABELTEXT} + +\unexpanded\def\RIGHTLABELTEXT + {\def\handletextprefix##1##2{\uppercase{##2}}\DOLABELTEXT} + +\def\DOLABELTEXT#1% + {\bgroup + \the\everyuppercase + \let\reporttextprefixerror\doreporttextprefixerror + \global\labeltextdonetrue + \dogetupsometextprefix\labellanguage\c!label{#1}% not \labeltext (see \MONTH) + \egroup} + +\let\labeltext \leftlabeltext +\let\LABELTEXT \LEFTLABELTEXT + +\unexpanded\def\labeltexts#1#2{\leftlabeltext{#1}#2\rightlabeltext{#1}} +\unexpanded\def\LABELTEXTS#1#2{\LEFTLABELTEXT{#1}#2\RIGHTLABELTEXT{#1}} + +\newif\iflabeltextdone % needs to be reset elsewhere +\newif\iftracelabels % shows missing labels + +\def\doreporttextprefixerror#1#2#3% + {\iftracelabels{\tttf[#2:~#3/#1]~}\fi} + +\def\dosetexpandedheadlabeltext#1#2#3% + {\bgroup + \let\handletextprefix\firstoftwoarguments + \let\reporttextprefixerror\gobblethreearguments + \keepencodedtokens % test on multilingual pascal, ok in stretched + %\dontexpandencodedtokens % not usable in token handler + \expanded + {\egroup\noexpand\def\noexpand#2% watch out, no \edef + {\dogetupsometextprefix{\headlanguage}{#1}{#3}}}} + +\def\setexpandedheadtext {\dosetexpandedheadlabeltext\c!title} +\def\setexpandedlabeltext{\dosetexpandedheadlabeltext\c!label} + +% \def\dogetupsometextprefix#1#2#3% +% {\ifcsname#2#1#3\endcsname +% \csname#2#1#3\endcsname \else +% \ifcsname#2#3\endcsname +% \csname#2#3\endcsname \else +% \ifcsname#2\defaultlanguage#1#3\endcsname +% \csname#2\defaultlanguage#1#3\endcsname \else +% \ifcsname#2\s!en#3\endcsname +% \csname#2\s!en#3\endcsname \else +% \ifcsname#2\s!nl#3\endcsname +% \csname#2\s!nl#3\endcsname \else +% \reporttextprefixerror{#1}{#2}{#3}% +% \fi\fi\fi\fi\fi} +% +% \def\dogetupsometextprefix#1#2#3% must be expandable ! +% {\ifcsname#2#1#3\endcsname +% \csname#2#1#3\endcsname +% \else\@EA\ifx\csname\??la#1\c!default\endcsname\empty +% \ifcsname#2#3\endcsname +% \csname#2#3\endcsname +% \else\ifcsname#2\s!en#3\endcsname +% \csname#2\s!en#3\endcsname +% \else +% \reporttextprefixerror{#1}{#2}{#3}% +% \fi\fi +% \else +% \dogetupsometextprefix{\csname\??la#1\c!default\endcsname}{#2}{#3}% +% \fi\fi} + +\def\dogetupsometextprefix#1#2#3% must be expandable ! #1 == language + {\ifcsname#2#1#3\endcsname + \csname#2#1#3\endcsname + \else\ifcsname\??la#1\s!default\endcsname + \expandafter\dogetupsometextprefix\csname\??la#1\s!default\endcsname{#2}{#3}% + \else\ifcsname#2#3\endcsname + \csname#2#3\endcsname + \else\ifcsname#1\s!en#3\endcsname + \csname#2\s!en#3\endcsname + \else + \reporttextprefixerror{#1}{#2}{#3}% + \fi\fi\fi\fi} + +\ifx\simplifiedcommands\undefined \newtoks\simplifiedcommands \fi + +\appendtoks + \let \headtext \firstofoneargument + \let \labeltext \firstofoneargument + \let \leftlabeltext \firstofoneargument + \let \rightlabeltext \firstofoneargument + \let \HEADTEXT \firstofoneargument + \let \LABELTEXT \firstofoneargument + \let \LEFTLABELTEXT \firstofoneargument + \let \RIGHTLABELTEXT \firstofoneargument +\to \simplifiedcommands + +%D \macros +%D {presetheadtext,presetlabeltext} +%D +%D The next two macros enable us to automatically define +%D head and label texts without replacing predefined ones. +%D These are internal macros. + +\def\xdopresetsometextprefix[#1][#2=#3]% + {\ifundefined{#1#2}\doassignsometextprefix[#1\reallanguagetag{#2}][#3,,]\fi} + +\def\dopresetsometextprefix + {\let\dodocommand\xdopresetsometextprefix + \dotripleempty\dodosetupsometextprefix} + +\def\presetheadtext {\dopresetsometextprefix[\c!title]} +\def\presetlabeltext{\dopresetsometextprefix[\c!label]} + +%D \macros +%D {translate} +%D +%D Sometismes macros contain language specific words that are to +%D be typeset. Such macros can be made (more) language +%D independant by using: +%D +%D \showsetup{translate} +%D +%D like for instance: +%D +%D \starttyping +%D \translate[en=something,nl=iets] +%D \stoptyping +%D +%D which expands to {\em something} or {\em iets}, depending on +%D de current language. + +\def\dotranslate[#1]% + {\getparameters[\??lg][#1]% + \ifcsname\??lg\currentlanguage\endcsname + \csname\??lg\currentlanguage\endcsname + \else\ifcsname\??lg\s!en\endcsname + \csname\??lg\s!en\endcsname + \else + [translation #1]% + \fi\fi} + +\unexpanded\def\translate + {\dosingleempty\dotranslate} + +%D When used without argument, the last defined values are +%D used. This enables repetitive use like +%D +%D \starttyping +%D \en \translate\ means \nl \translate +%D \stoptyping + +%D \macros +%D {assigntranslation} +%D +%D This macro is a system macro, and can be used to assign a +%D translation to a macro. Its form is: +%D +%D \starttyping +%D \assigntranslation[en=something,nl=iets]\to\command +%D \stoptyping + +\def\assigntranslation[#1]\to#2% + {\getparameters[\??lg][#1]% + \edef#2{\csname\??lg\currentlanguage\endcsname}} + +\protect \endinput diff --git a/tex/context/base/lang-lab.mkiv b/tex/context/base/lang-lab.mkiv new file mode 100644 index 000000000..60408f787 --- /dev/null +++ b/tex/context/base/lang-lab.mkiv @@ -0,0 +1,266 @@ +%D \module +%D [ file=lang-lab, +%D version=1997.08.27, +%D title=\CONTEXT\ Language Macros, +%D subtitle=Labels, +%D author=Hans Hagen / Tobias Burnus, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\unprotect + +%D In this module we deal with language dependant labels and +%D prefixes, like in {\em Figure~12} and {\em Chapter 1}. In +%D this file we set the default values. Users can easily +%D overrule these. +%D +%D This module is dedicated to the grandfather of Tobias +%D Burnus, who's extensive languages oriented library helped us +%D a lot in finding the right translations. All those labels +%D are collected in files that reflect their common ancestor. +%D +%D Not all languages can be satisfied with the labeling +%D mechanism as provided here. Chinese for instance put a label +%D in front as well as after a part number. This is why the +%D current implementation of labels supports two labels too. + +%D \macros +%D {setupheadtext, setuplabeltext} +%D +%D First we present some macros that deal with what we will +%D call head and label texts. Such texts are defines by: +%D +%D \showsetup{setupheadtext} +%D \showsetup{setuplabeltext} +%D +%D In a few paragraphs we'll show quite a lot of examples +%D of its use. + +\let\handletextprefix\relax + +\def\setupheadtext {\dosetupsometextprefix[\c!title]} +\def\setuplabeltext{\dosetupsometextprefix[\c!label]} + +\def\dosetupsometextprefix + {\let\dodocommand\xdosetupsometextprefix + \dotripleempty\dodosetupsometextprefix} + +% \def\dodosetupsometextprefix[#1][#2][#3]% +% {\ifthirdargument +% \def\docommand##1{\dodocommand[#1#2][##1]}% +% \processcommalist[#3]\docommand +% \else +% \def\docommand##1{\dodocommand[#1\currentmainlanguage][##1]}% +% \processcommalist[#2]\docommand +% \fi} + +\def\dodosetupsometextprefix[#1][#2][#3]% + {\ifthirdargument + \def\docommand##1{\expanded{\dodocommand[#1\reallanguagetag{#2}]}[##1]}% + \processcommalist[#3]\docommand + \else + \def\docommand##1{\expanded{\dodocommand[#1\reallanguagetag\currentmainlanguage]}[##1]}% + \processcommalist[#2]\docommand + \fi} + +\def\doassignsometextprefix[#1][#2,#3,#4]% + {\setvalue{#1}{\handletextprefix{#2}{#3}}} + +\def\xdosetupsometextprefix[#1][#2=#3]% + {\doassignsometextprefix[#1#2][#3,,]} + +%D By changing the meaning of \type {\handletextprefix} we +%D can filter the left and right labeltext as well as convert +%D labels to uppercase. +%D +%D These commands accept all kind of inputs: +%D +%D \starttyping +%D \setuplabeltext [language] [labellabel=text] +%D \setuplabeltext [language] [labellabel=text,labellabel=text,...] +%D \setuplabeltext [labellabel=text] +%D \setuplabeltext [labellabel=text,labellabel=text,...] +%D \stoptyping +%D +%D The last two cases concern the current language. + +%D \macros +%D {headtext, +%D labeltext, leftlabeltext, rightlabeltext, labeltexts, +%D LABELTEXT, LEFTLABELTEXT, RIGHTLABELTEXT, LABELTEXTS} +%D +%D Once defined, head and label texts can be called upon using: +%D +%D \showsetup{headtext} +%D \showsetup{labeltext} +%D +%D The latter one has an upcased alternative \type{\LABELTEXT}. + +% \def\labellanguage{\currentmainlanguage} +% \def\headlanguage {\currentmainlanguage} + +% \def\labellanguage{\defaultlanguage\currentmainlanguage} +% \def\headlanguage {\defaultlanguage\currentmainlanguage} + +\def\labellanguage{\reallanguagetag{\defaultlanguage\currentmainlanguage}} +\def\headlanguage {\reallanguagetag{\defaultlanguage\currentmainlanguage}} + +\appendtoks \let\labellanguage\currentlanguage \to \everycurrentdate + +\unexpanded\def\headtext + {\let\handletextprefix\firstoftwoarguments + \let\reporttextprefixerror\doreporttextprefixerror + \global\labeltextdonetrue + \dogetupsometextprefix\headlanguage\c!title} + +\unexpanded\def\leftlabeltext + {\let\handletextprefix\firstoftwoarguments + \let\reporttextprefixerror\doreporttextprefixerror + \global\labeltextdonetrue + \dogetupsometextprefix\labellanguage\c!label} + +\unexpanded\def\rightlabeltext + {\let\handletextprefix\secondoftwoarguments + \let\reporttextprefixerror\doreporttextprefixerror + \global\labeltextdonetrue + \dogetupsometextprefix\labellanguage\c!label} + +\unexpanded\def\LEFTLABELTEXT + {\def\handletextprefix##1##2{\uppercase{##1}}\DOLABELTEXT} + +\unexpanded\def\RIGHTLABELTEXT + {\def\handletextprefix##1##2{\uppercase{##2}}\DOLABELTEXT} + +\def\DOLABELTEXT#1% + {\bgroup + \the\everyuppercase + \let\reporttextprefixerror\doreporttextprefixerror + \global\labeltextdonetrue + \dogetupsometextprefix\labellanguage\c!label{#1}% not \labeltext (see \MONTH) + \egroup} + +\let\labeltext \leftlabeltext +\let\LABELTEXT \LEFTLABELTEXT + +\unexpanded\def\labeltexts#1#2{\leftlabeltext{#1}#2\rightlabeltext{#1}} +\unexpanded\def\LABELTEXTS#1#2{\LEFTLABELTEXT{#1}#2\RIGHTLABELTEXT{#1}} + +\newif\iflabeltextdone % needs to be reset elsewhere +\newif\iftracelabels % shows missing labels + +\def\doreporttextprefixerror#1#2#3% + {\iftracelabels{\tttf[#2:~#3/#1]~}\fi} + +\def\dosetexpandedheadlabeltext#1#2#3% + {\bgroup + \let\handletextprefix\firstoftwoarguments + \let\reporttextprefixerror\gobblethreearguments + \keepencodedtokens % test on multilingual pascal, ok in stretched + %\dontexpandencodedtokens % not usable in token handler + \expanded + {\egroup\noexpand\def\noexpand#2% watch out, no \edef + {\dogetupsometextprefix{\headlanguage}{#1}{#3}}}} + +\def\setexpandedheadtext {\dosetexpandedheadlabeltext\c!title} +\def\setexpandedlabeltext{\dosetexpandedheadlabeltext\c!label} + +\def\dogetupsometextprefix#1#2#3% must be expandable ! #1 == language + {\ifcsname#2#1#3\endcsname + \csname#2#1#3\endcsname + \else\ifcsname\??la#1\s!default\endcsname + \expandafter\dogetupsometextprefix\csname\??la#1\s!default\endcsname{#2}{#3}% + \else\ifcsname#2#3\endcsname + \csname#2#3\endcsname + \else\ifcsname#1\s!en#3\endcsname + \csname#2\s!en#3\endcsname + \else + % \doreporttextprefixerror{#1}{#2}{#3}% + \fi\fi\fi\fi} + +\ifx\simplifiedcommands\undefined \newtoks\simplifiedcommands \fi + +\appendtoks + \let \headtext \firstofoneargument + \let \labeltext \firstofoneargument + \let \leftlabeltext \firstofoneargument + \let \rightlabeltext \firstofoneargument + \let \HEADTEXT \firstofoneargument + \let \LABELTEXT \firstofoneargument + \let \LEFTLABELTEXT \firstofoneargument + \let \RIGHTLABELTEXT \firstofoneargument +\to \simplifiedcommands + +%D \macros +%D {presetheadtext,presetlabeltext} +%D +%D The next two macros enable us to automatically define +%D head and label texts without replacing predefined ones. +%D These are internal macros. + +\def\xdopresetsometextprefix[#1][#2=#3]% + {\ifundefined{#1#2}\doassignsometextprefix[#1\reallanguagetag{#2}][#3,,]\fi} + +\def\dopresetsometextprefix + {\let\dodocommand\xdopresetsometextprefix + \dotripleempty\dodosetupsometextprefix} + +\def\presetheadtext {\dopresetsometextprefix[\c!title]} +\def\presetlabeltext{\dopresetsometextprefix[\c!label]} + +%D \macros +%D {translate} +%D +%D Sometismes macros contain language specific words that are to +%D be typeset. Such macros can be made (more) language +%D independant by using: +%D +%D \showsetup{translate} +%D +%D like for instance: +%D +%D \starttyping +%D \translate[en=something,nl=iets] +%D \stoptyping +%D +%D which expands to {\em something} or {\em iets}, depending on +%D de current language. + +\def\dotranslate[#1]% + {\getparameters[\??lg][#1]% + \ifcsname\??lg\currentlanguage\endcsname + \csname\??lg\currentlanguage\endcsname + \else\ifcsname\??lg\s!en\endcsname + \csname\??lg\s!en\endcsname + \else + [translation #1]% + \fi\fi} + +\unexpanded\def\translate + {\dosingleempty\dotranslate} + +%D When used without argument, the last defined values are +%D used. This enables repetitive use like +%D +%D \starttyping +%D \en \translate\ means \nl \translate +%D \stoptyping + +%D \macros +%D {assigntranslation} +%D +%D This macro is a system macro, and can be used to assign a +%D translation to a macro. Its form is: +%D +%D \starttyping +%D \assigntranslation[en=something,nl=iets]\to\command +%D \stoptyping + +\def\assigntranslation[#1]\to#2% + {\getparameters[\??lg][#1]% + \edef#2{\csname\??lg\currentlanguage\endcsname}} + +\protect \endinput diff --git a/tex/context/base/lang-lab.tex b/tex/context/base/lang-lab.tex deleted file mode 100644 index 664460129..000000000 --- a/tex/context/base/lang-lab.tex +++ /dev/null @@ -1,284 +0,0 @@ -%D \module -%D [ file=lang-lab, -%D version=1997.08.27, -%D title=\CONTEXT\ Language Macros, -%D subtitle=Language Head and Label Texts, -%D author=Hans Hagen / Tobias Burnus, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\writestatus{loading}{Language Head and Label Texts} - -\unprotect - -%D In this module we deal with language dependant labels and -%D prefixes, like in {\em Figure~12} and {\em Chapter 1}. In -%D this file we set the default values. Users can easily -%D overrule these. -%D -%D This module is dedicated to the grandfather of Tobias -%D Burnus, who's extensive languages oriented library helped us -%D a lot in finding the right translations. All those labels -%D are collected in files that reflect their common ancestor. -%D -%D Not all languages can be satisfied with the labeling -%D mechanism as provided here. Chinese for instance put a label -%D in front as well as after a part number. This is why the -%D current implementation of labels supports two labels too. - -%D \macros -%D {setupheadtext, setuplabeltext} -%D -%D First we present some macros that deal with what we will -%D call head and label texts. Such texts are defines by: -%D -%D \showsetup{setupheadtext} -%D \showsetup{setuplabeltext} -%D -%D In a few paragraphs we'll show quite a lot of examples -%D of its use. - -\let\handletextprefix\relax - -\def\setupheadtext {\dosetupsometextprefix[\c!title]} -\def\setuplabeltext{\dosetupsometextprefix[\c!label]} - -\def\dosetupsometextprefix - {\let\dodocommand\xdosetupsometextprefix - \dotripleempty\dodosetupsometextprefix} - -% \def\dodosetupsometextprefix[#1][#2][#3]% -% {\ifthirdargument -% \def\docommand##1{\dodocommand[#1#2][##1]}% -% \processcommalist[#3]\docommand -% \else -% \def\docommand##1{\dodocommand[#1\currentmainlanguage][##1]}% -% \processcommalist[#2]\docommand -% \fi} - -\def\dodosetupsometextprefix[#1][#2][#3]% - {\ifthirdargument - \def\docommand##1{\expanded{\dodocommand[#1\reallanguagetag{#2}]}[##1]}% - \processcommalist[#3]\docommand - \else - \def\docommand##1{\expanded{\dodocommand[#1\reallanguagetag\currentmainlanguage]}[##1]}% - \processcommalist[#2]\docommand - \fi} - -\def\doassignsometextprefix[#1][#2,#3,#4]% - {\setvalue{#1}{\handletextprefix{#2}{#3}}} - -\def\xdosetupsometextprefix[#1][#2=#3]% - {\doassignsometextprefix[#1#2][#3,,]} - -%D By changing the meaning of \type {\handletextprefix} we -%D can filter the left and right labeltext as well as convert -%D labels to uppercase. -%D -%D These commands accept all kind of inputs: -%D -%D \starttyping -%D \setuplabeltext [language] [labellabel=text] -%D \setuplabeltext [language] [labellabel=text,labellabel=text,...] -%D \setuplabeltext [labellabel=text] -%D \setuplabeltext [labellabel=text,labellabel=text,...] -%D \stoptyping -%D -%D The last two cases concern the current language. - -%D \macros -%D {headtext, -%D labeltext, leftlabeltext, rightlabeltext, labeltexts, -%D LABELTEXT, LEFTLABELTEXT, RIGHTLABELTEXT, LABELTEXTS} -%D -%D Once defined, head and label texts can be called upon using: -%D -%D \showsetup{headtext} -%D \showsetup{labeltext} -%D -%D The latter one has an upcased alternative \type{\LABELTEXT}. - -% \def\labellanguage{\currentmainlanguage} -% \def\headlanguage {\currentmainlanguage} - -% \def\labellanguage{\defaultlanguage\currentmainlanguage} -% \def\headlanguage {\defaultlanguage\currentmainlanguage} - -\def\labellanguage{\reallanguagetag{\defaultlanguage\currentmainlanguage}} -\def\headlanguage {\reallanguagetag{\defaultlanguage\currentmainlanguage}} - -\appendtoks \let\labellanguage\currentlanguage \to \everycurrentdate - -\unexpanded\def\headtext - {\let\handletextprefix\firstoftwoarguments - \let\reporttextprefixerror\doreporttextprefixerror - \global\labeltextdonetrue - \dogetupsometextprefix\headlanguage\c!title} - -\unexpanded\def\leftlabeltext - {\let\handletextprefix\firstoftwoarguments - \let\reporttextprefixerror\doreporttextprefixerror - \global\labeltextdonetrue - \dogetupsometextprefix\labellanguage\c!label} - -\unexpanded\def\rightlabeltext - {\let\handletextprefix\secondoftwoarguments - \let\reporttextprefixerror\doreporttextprefixerror - \global\labeltextdonetrue - \dogetupsometextprefix\labellanguage\c!label} - -\unexpanded\def\LEFTLABELTEXT - {\def\handletextprefix##1##2{\uppercase{##1}}\DOLABELTEXT} - -\unexpanded\def\RIGHTLABELTEXT - {\def\handletextprefix##1##2{\uppercase{##2}}\DOLABELTEXT} - -\def\DOLABELTEXT#1% - {\bgroup - \the\everyuppercase - \let\reporttextprefixerror\doreporttextprefixerror - \global\labeltextdonetrue - \dogetupsometextprefix\labellanguage\c!label{#1}% not \labeltext (see \MONTH) - \egroup} - -\let\labeltext \leftlabeltext -\let\LABELTEXT \LEFTLABELTEXT - -\unexpanded\def\labeltexts#1#2{\leftlabeltext{#1}#2\rightlabeltext{#1}} -\unexpanded\def\LABELTEXTS#1#2{\LEFTLABELTEXT{#1}#2\RIGHTLABELTEXT{#1}} - -\newif\iflabeltextdone % needs to be reset elsewhere -\newif\iftracelabels % shows missing labels - -\def\doreporttextprefixerror#1#2#3% - {\iftracelabels{\tttf[#2:~#3/#1]~}\fi} - -\def\dosetexpandedheadlabeltext#1#2#3% - {\bgroup - \let\handletextprefix\firstoftwoarguments - \let\reporttextprefixerror\gobblethreearguments - \keepencodedtokens % test on multilingual pascal, ok in stretched - %\dontexpandencodedtokens % not usable in token handler - \expanded - {\egroup\noexpand\def\noexpand#2% watch out, no \edef - {\dogetupsometextprefix{\headlanguage}{#1}{#3}}}} - -\def\setexpandedheadtext {\dosetexpandedheadlabeltext\c!title} -\def\setexpandedlabeltext{\dosetexpandedheadlabeltext\c!label} - -% \def\dogetupsometextprefix#1#2#3% -% {\ifcsname#2#1#3\endcsname -% \csname#2#1#3\endcsname \else -% \ifcsname#2#3\endcsname -% \csname#2#3\endcsname \else -% \ifcsname#2\defaultlanguage#1#3\endcsname -% \csname#2\defaultlanguage#1#3\endcsname \else -% \ifcsname#2\s!en#3\endcsname -% \csname#2\s!en#3\endcsname \else -% \ifcsname#2\s!nl#3\endcsname -% \csname#2\s!nl#3\endcsname \else -% \reporttextprefixerror{#1}{#2}{#3}% -% \fi\fi\fi\fi\fi} - -\def\dogetupsometextprefix#1#2#3% must be expandable ! - {\ifcsname#2#1#3\endcsname - \csname#2#1#3\endcsname - \else\@EA\ifx\csname\??la#1\c!default\endcsname\empty - \ifcsname#2#3\endcsname - \csname#2#3\endcsname - \else\ifcsname#2\s!en#3\endcsname - \csname#2\s!en#3\endcsname - \else - \reporttextprefixerror{#1}{#2}{#3}% - \fi\fi - \else - \dogetupsometextprefix{\csname\??la#1\c!default\endcsname}{#2}{#3}% - \fi\fi} - -\ifx\simplifiedcommands\undefined \newtoks\simplifiedcommands \fi - -\appendtoks - \let \headtext \firstofoneargument - \let \labeltext \firstofoneargument - \let \leftlabeltext \firstofoneargument - \let \rightlabeltext \firstofoneargument - \let \HEADTEXT \firstofoneargument - \let \LABELTEXT \firstofoneargument - \let \LEFTLABELTEXT \firstofoneargument - \let \RIGHTLABELTEXT \firstofoneargument -\to \simplifiedcommands - -%D \macros -%D {presetheadtext,presetlabeltext} -%D -%D The next two macros enable us to automatically define -%D head and label texts without replacing predefined ones. -%D These are internal macros. - -\def\xdopresetsometextprefix[#1][#2=#3]% - {\ifundefined{#1#2}\doassignsometextprefix[#1\reallanguagetag{#2}][#3,,]\fi} - -\def\dopresetsometextprefix - {\let\dodocommand\xdopresetsometextprefix - \dotripleempty\dodosetupsometextprefix} - -\def\presetheadtext {\dopresetsometextprefix[\c!title]} -\def\presetlabeltext{\dopresetsometextprefix[\c!label]} - -%D \macros -%D {translate} -%D -%D Sometismes macros contain language specific words that are to -%D be typeset. Such macros can be made (more) language -%D independant by using: -%D -%D \showsetup{translate} -%D -%D like for instance: -%D -%D \starttyping -%D \translate[en=something,nl=iets] -%D \stoptyping -%D -%D which expands to {\em something} or {\em iets}, depending on -%D de current language. - -\def\dotranslate[#1]% don't group! SLOW if really used: speed up - {\getparameters[\??lg][#1]% - \doifdefinedelse{\??lg\currentlanguage}% - {\getvalue{\??lg\currentlanguage}} - {\doifdefinedelse{\??lg\s!en} - {\getvalue{\??lg\s!en}} - {\doifdefinedelse{\??lg\s!nl} - {\getvalue{\??lg\s!nl}} - {[translation #1]}}}} - -\unexpanded\def\translate - {\dosingleempty\dotranslate} - -%D When used without argument, the last defined values are -%D used. This enables repetitive use like -%D -%D \starttyping -%D \en \translate\ means \nl \translate -%D \stoptyping - -%D \macros -%D {assigntranslation} -%D -%D This macro is a system macro, and can be used to assign a -%D translation to a macro. Its form is: -%D -%D \starttyping -%D \assigntranslation[en=something,nl=iets]\to\command -%D \stoptyping - -\def\assigntranslation[#1]\to#2% - {\getparameters[\??lg][#1]% - \edef#2{\csname\??lg\currentlanguage\endcsname}} - -\protect \endinput diff --git a/tex/context/base/lang-mis.tex b/tex/context/base/lang-mis.tex index 41f370974..eb7bb1a04 100644 --- a/tex/context/base/lang-mis.tex +++ b/tex/context/base/lang-mis.tex @@ -2,7 +2,7 @@ %D [ file=lang-mis, %D version=1997.03.20, % used to be supp-lan.tex %D title=\CONTEXT\ Language Macros, -%D subtitle=Language Options, +%D subtitle=Compounds, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA / Hans Hagen \& Ton Otten}] @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Context Language Macros / Compounds} +\writestatus{loading}{ConTeXt Language Macros / Compounds} %D \gdef\starttest %D {\blank @@ -181,7 +181,7 @@ %ifx\postwordbreak \undefined \def\postwordbreak{\penalty\zerocount \prewordbreak } \fi \ifx\postwordbreak \undefined \def\postwordbreak{\penalty\zerocount \hskip\zeropoint\relax} \fi -\ifx\hspaceamount \undefined \def\hspaceamount#1#2{\kern.16667em} \fi % language specific +\ifx\hspaceamount \undefined \def\hspaceamount#1#2{.16667em} \fi % language specific %D \macros %D {beginofsubsentencespacing,endofsubsentencespacing} diff --git a/tex/context/base/lang-sla.tex b/tex/context/base/lang-sla.tex index 50ebed127..0832e3f46 100644 --- a/tex/context/base/lang-sla.tex +++ b/tex/context/base/lang-sla.tex @@ -32,7 +32,7 @@ % Lusatian/Sorbian/Wendish, Polish, Slovak, Albanian, % Illyrian, Armenian -\writestatus{loading}{Slavic Languages} +\writestatus{loading}{ConTeXt Language Macros / Slavic Languages} \unprotect @@ -77,7 +77,8 @@ \c!leftquotation=\lowerleftdoubleninequote, \c!rightquotation=\upperrightdoubleninequote, \c!date={\v!day,{.},\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] + \s!mapping={pl0,ec,qx}, + \s!encoding={pl0,ec,qx}] \installlanguage [\s!cs] @@ -91,7 +92,8 @@ \c!leftquotation=\lowerleftdoubleninequote, \c!rightquotation=\upperrightdoublesixquote, \c!date={\v!day,{.\,},\v!month,\ ,\v!year}, - \c!state=\v!stop] + \s!mapping={il2,ec}, + \s!encoding={il2,ec}] \installlanguage [\s!sk] @@ -105,7 +107,8 @@ \c!leftquotation=\upperleftdoublesixquote, \c!rightquotation=\upperrightdoubleninequote, \c!date={\v!day,{.\,},\v!month,\ ,\v!year}, - \c!state=\v!stop] + \s!mapping={il2,ec}, + \s!encoding={il2,ec}] \installlanguage [\s!hr] @@ -119,7 +122,8 @@ \c!leftquotation=\upperleftdoublesixquote, \c!rightquotation=\upperrightdoubleninequote, \c!date={\v!day,\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] + \s!mapping=ec, + \s!encoding=ec] %D The default quotation marks for Slovenian were chosen as %D \lowerleftdoubleninequote these ones\upperrightdoublesixquote\ @@ -168,7 +172,8 @@ \c!leftquotation=\rightguillemot, \c!rightquotation=\leftguillemot, \c!date={\v!day,{.},\ ,\v!month,\ ,\v!year}, - \c!state=\v!stop] + \s!mapping=ec, + \s!encoding=ec] \installlanguage [polish] [\s!pl] \installlanguage [czech] [\s!cs] @@ -177,6 +182,8 @@ \installlanguage [slovenian] [\s!sl] \installlanguage [slovene] [\s!sl] % both possible (mojca: still needed?) +\installlanguage [cz] [\s!cs] + % If this is really needed we should make an enco-fhr. % % \startlanguagespecifics[\s!hr] @@ -443,9 +450,7 @@ % \c!rightquote=\upperrightsinglesixquote, % \c!leftquotation=\lowerleftdoubleninequote, % \c!rightquotation=\upperrightdoublesixquote, -% \c!date={\v!day,\ ,\v!month,\ ,\v!year}, -% \c!state=\v!stop] - +% \c!date={\v!day,\ ,\v!month,\ ,\v!year}] \setuplabeltext [\s!sl] [\v!page=stran ] \setuplabeltext [\s!sl] [\v!atpage=na strani ] diff --git a/tex/context/base/lang-spa.tex b/tex/context/base/lang-spa.tex index 1ec45cd69..f6e22aa51 100644 --- a/tex/context/base/lang-spa.tex +++ b/tex/context/base/lang-spa.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Context Language Macros / Spacing} +\writestatus{loading}{ConTeXt Language Macros / Spacing} %D This module was created in the process of enhancing %D support for French (with the help of Daniel Flipo). diff --git a/tex/context/base/lang-spe.mkii b/tex/context/base/lang-spe.mkii new file mode 100644 index 000000000..7911b0c95 --- /dev/null +++ b/tex/context/base/lang-spe.mkii @@ -0,0 +1,244 @@ +%D \module +%D [ file=lang-spe, +%D version=2002.05.07, % 1996.01.25, +%D title=\CONTEXT\ Language Macros, +%D subtitle=Specifics, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D This code was originally placed in the language +%D initialization module, but isolating it is clearer. Language +%D specifics evolved out of user demands for special features, +%D like the german active quote. After a while I decided to +%D associate them to languages in a more general way so that we +%D could associate all kind of things with language switching. +%D +%D This is a typical example of functionality that occasionally +%D gets improved based on user input and experience. Much of the +%D code is pretty old and could probabbly be done in better ways. +%D It's probably also the kind of code that has been and will be +%D written over and over again by \TEX\ users around the world, +%D so there are probably better implementations of similar +%D functionality around. Therefore, users are invited to pop in +%D their own handling as long as it does not interfere with +%D existing code. Writing the more obscure macros that deal with +%D this is a good learning experience (catcodes, lccodes, token +%D lists, expansion, \unknown). + +\writestatus{loading}{ConTeXt Language Macros / Specifics} + +\unprotect + +%D \macros +%D {everyresetlanguagespecifics,resetlanguagespecifics} +%D +%D Cleanup macros. + +\newevery \everyresetlanguagespecifics \relax + +\def\resetlanguagespecifics + {\ifcase\protectionlevel + \the\everyresetlanguagespecifics + \else % to be translated + % \writestatus\m!systems{don't change language in unprotected mode!}% + \fi} + +\appendtoks + \resetlanguagespecifics +\to \everycleanupfeatures + +%D \macros +%D {startlanguagespecifics,enablelanguagespecifics} +%D +%D Each language has its own typographic pecularities. Some of +%D those can be influenced by parameters, others are handled by +%D the interface, but as soon as specific commands come into +%D view we need another mechanism. In the macro that activates +%D a language, we call \type{\enablelanguagespecifics}. This +%D macro in return calls for the setup of language specific +%D macros. Such specifics are defined as: +%D +%D \starttyping +%D \startlanguagespecifics[de] +%D \installcompoundcharacter "a {\"a} +%D \installcompoundcharacter "e {\"e} +%D \installcompoundcharacter "s {\SS} +%D \stoplanguagespecifics +%D \stoptyping +%D +%D Instead of \type{[du]} we can pass a comma separated +%D list, like \type{[du,nl]}. Next calls to this macro add the +%D specifics to the current list. +%D +%D Before we actually read the specifics, we first take some +%D precautions that will prevent spurious spaces to creep into +%D the list. + +% We should use token registers, but alas, we run out of them and +% \ETEX\ has a bug. Well, let's use a token register now (2006). + +\def\startlanguagespecifics% % we use double to + {\bgroup + \catcode`\^^I=\@@ignore + \catcode`\^^M=\@@ignore + \catcode`\^^L=\@@ignore + \dodoubleempty\dostartlanguagespecifics} % get rid of spaces + +%D The main macro looks quite complicated but actually does +%D nothing special. By embedding \type{\do} we can easily +%D append to the lists and also execute them at will. Just to +%D be sure, we check on spurious spaces. The second dummy +%D argument gobbles spaces. + +\def\languageencoding + {\ifx\characterencoding\nocharacterencoding \else + \characterencoding-% + \fi} + +\long\def\dostartlanguagespecifics[#1][#2]#3\stoplanguagespecifics + {\egroup + \processcommalist[#1]{\dosetlanguagespecifics{#3}}} + +% \long\def\dosetlanguagespecifics#1#2% +% {\ifundefined{\??la\languageencoding#2\??la}\forgetlanguagespecifics[#2]\fi +% % the next line catches the case that specifics are enabled *before* they are defined +% \expandafter\ifx\csname\??la\languageencoding#2\??la\endcsname\relax\forgetlanguagespecifics[#2]\fi +% \appendvalue{\??la\languageencoding#2\??la}{#1}% +% \bgroup +% \setbox\scratchbox\hbox{\enablelanguagespecifics[#2]}% +% \ifdim\wd\scratchbox>\zeropoint +% \showmessage\m!linguals7{\currentencoding-#2,\the\wd\scratchbox\space}\wait +% \else +% \showmessage\m!linguals8{\currentencoding-#2}% +% \fi +% \egroup +% \doif{#2}\currentmainlanguage{\enablelanguagespecifics[#2]}} + +\def\languagespectag#1{\??la\languageencoding#1\??la} + +\long\def\dosetlanguagespecifics#1#2% + {\edef\askedlanguagespecificstag{\languagespectag{#2}}% + \ifcsname\askedlanguagespecificstag\endcsname \else + \expandafter\newtoks\csname\askedlanguagespecificstag\endcsname + \fi + \csname\askedlanguagespecificstag\endcsname\@EA{\the\csname\askedlanguagespecificstag\endcsname#1}% + \bgroup + \setbox\scratchbox\hbox{\enablelanguagespecifics[#2]}% + \ifdim\wd\scratchbox>\zeropoint + \showmessage\m!linguals7{\currentencoding-#2,\the\wd\scratchbox\space}\wait + \else + \showmessage\m!linguals8{\currentencoding-#2}% + \fi + \egroup + \doif{#2}\currentmainlanguage{\enablelanguagespecifics[#2]}} + +\def\forgetlanguagespecifics[#1]% + {\csname\languagespectag{#1}\endcsname\emptytoks} + +%D Enabling them is rather straightforward. We only have to +%D define \type{\do} in such a way that \type{{ }} is removed +%D and the language key is gobbled. + +% \def\enablelanguagespecifics[#1]% +% {\the\executeifdefined{\??la +% \@EA\ifx\csname\??la#1\c!default\endcsname\relax +% \languageencoding +% \else +% \csname\??la#1\c!default\endcsname +% \fi +% \??la}\emptytoks +% \the\executeifdefined{\??la#1\??la}\emptytoks +% \the\executeifdefined{\??la\languageencoding#1\??la}\emptytoks} % dup ? + +\def\enablelanguagespecifics[#1]% + {\edef\askedlanguagespecificslanguage{\defaultlanguage{#1}}% + \ifcsname\??la\askedlanguagespecificslanguage\??la\endcsname + \the\csname\??la\askedlanguagespecificslanguage\??la\endcsname + \fi + \ifx\languageencoding\empty\else + \ifcsname\??la\languageencoding\askedlanguagespecificslanguage\??la\endcsname + \the\csname\??la\languageencoding\askedlanguagespecificslanguage\??la\endcsname + \fi + \fi} + +%D \macros +%D {deactivatelanguagespecific} +%D +%D The next code makes it possible to disable the specifics. + +% \def\deactivatelanguagespecific#1% +% {\ifundefined{l g s \string#1}% +% \letgvalueempty{l g s \string#1}% signal to prevent dup def +% \bgroup +% \catcode`#1=\@@active +% \uccode`~=`#1 +% \uppercase{\doglobal\appendtoks\dodeactivatetoken{~}\to\everyresetlanguagespecifics}% +% \egroup +% \expanded{\doglobal\noexpand\appendtoks{#1}{\the\catcode`#1}}\to\everyresetlanguagespecifics +% \fi} + +% \def\dodeactivatetoken#1#2#3% test needed to avoid clash with \unprotect +% {\def#1{#2}\ifnum\catcode`#2=\@@active\catcode`#2=#3\relax\fi} + +%D We cannot hook this into the installer since language +%D specifics can be anything. So far, we have the following +%D potentially active characters. + +%D Beware, this should happen under an unprotected regime; +%D thanks to Giuseppe Oblomov Bilotta, who first noticed +%D that something was wrong. + +\protect + +% \deactivatelanguagespecific " +% \deactivatelanguagespecific / +% \deactivatelanguagespecific : +% \deactivatelanguagespecific ; +% \deactivatelanguagespecific ? +% \deactivatelanguagespecific ! + +\unprotect + +% yes or no (taco wins: no) + +% \startlanguagespecifics[nl,cs,sk,fr] +% \lccode`\'=`\' +% \stoplanguagespecifics + +%D \macros +%D {ordinaldaynumber, highordinalstr, ordinalstr} +%D +%D Efficient general ordinal number converters are sometimes +%D difficult to implement. Fortunately dates never exceed the +%D number~31. + +\ifx\high \undefined \let\high \firstofoneargument \fi % todo +\ifx\notsmallcapped\undefined \let\notsmallcapped\firstofoneargument \fi % todo + +\def\highordinalstr#1{\high{\notsmallcapped{#1}}} +\def\ordinalstr #1{\notsmallcapped{#1}} + +\def\ordinaldaynumber#1% \strippedcsname\ordinaldaynumber + {\expanded{\executeifdefined{\currentlanguage ordinaldaynumber}% + \noexpand\firstofoneargument{\number#1}}} + +%D Language specific converters have definitions like: +%D +%D \starttyping +%D \def\enordinaldaynumber#1{...} +%D \stoptyping +%D +%D Examples can be found in the other \type {lang} modules. + +% \ifprocessingXML is a nasty dependency + +\appendtoks + \ifprocessingXML \else \resetlanguagespecifics \fi +\to \everylanguage + +\protect \endinput diff --git a/tex/context/base/lang-spe.mkiv b/tex/context/base/lang-spe.mkiv new file mode 100644 index 000000000..6f32888e6 --- /dev/null +++ b/tex/context/base/lang-spe.mkiv @@ -0,0 +1,111 @@ +%D \module +%D [ file=lang-spe, +%D version=2002.05.07, % 1996.01.25, +%D title=\CONTEXT\ Language Macros, +%D subtitle=Specifics, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Language Macros / Specifics} + +%D In \MKIV\ we will get away from this feature. See \MKII\ file +%D for comments. So, consider this a temporary feature. + +\unprotect + +%D \macros +%D {everyresetlanguagespecifics,resetlanguagespecifics} +%D +%D Cleanup macros. + +\newevery \everyresetlanguagespecifics \relax + +\def\resetlanguagespecifics + {\ifcase\protectionlevel + \the\everyresetlanguagespecifics + \fi} + +\appendtoks + \resetlanguagespecifics +\to \everycleanupfeatures + +%D \macros +%D {startlanguagespecifics,enablelanguagespecifics} + +\def\startlanguagespecifics + {\bgroup + \catcode`\^^I=\@@ignore + \catcode`\^^M=\@@ignore + \catcode`\^^L=\@@ignore + \dodoubleempty\dostartlanguagespecifics} % get rid of spaces + +\long\def\dostartlanguagespecifics[#1][#2]#3\stoplanguagespecifics + {\egroup + \processcommalist[#1]{\dosetlanguagespecifics{#3}}} + +\long\def\dosetlanguagespecifics#1#2% specifics language + {\ifcsname\??la#2\??la\endcsname \else + \expandafter\newtoks\csname\??la#2\??la\endcsname + \fi + \csname\??la#2\??la\endcsname\@EA{\the\csname\??la#2\??la\endcsname#1}% + \bgroup + \setbox\scratchbox\hbox{\enablelanguagespecifics[#2]}% + \ifdim\wd\scratchbox>\zeropoint + \showmessage\m!linguals7{#2,\the\wd\scratchbox\space}\wait + \else + \showmessage\m!linguals8{#2}% + \fi + \egroup + \doif{#2}\currentmainlanguage{\enablelanguagespecifics[#2]}} + +\def\forgetlanguagespecifics[#1]% + {\ifcsname\??la#1\??la\endcsname + \csname\??la#1\??la\endcsname\emptytoks + \fi} + +% \def\enablelanguagespecifics[#1]% no default language fallback (yet) +% {\ifcsname\??la#1\??la\endcsname +% \the\csname\??la#1\??la\endcsname\relax +% \fi} + +\def\enablelanguagespecifics[#1]% + {\edef\askedlanguagespecificslanguage{\defaultlanguage{#1}}% + \ifcsname\??la\askedlanguagespecificslanguage\??la\endcsname + \the\csname\??la\askedlanguagespecificslanguage\??la\endcsname + \fi} + +%D \macros +%D {ordinaldaynumber, highordinalstr, ordinalstr} +%D +%D Efficient general ordinal number converters are sometimes +%D difficult to implement. Fortunately dates never exceed the +%D number~31. + +\ifx\high \undefined \let\high \firstofoneargument \fi % todo +\ifx\notsmallcapped\undefined \let\notsmallcapped\firstofoneargument \fi % todo + +\def\highordinalstr#1{\high{\notsmallcapped{#1}}} +\def\ordinalstr #1{\notsmallcapped{#1}} + +\def\ordinaldaynumber#1% \strippedcsname\ordinaldaynumber + {\expanded{\executeifdefined{\currentlanguage ordinaldaynumber}% + \noexpand\firstofoneargument{\number#1}}} + +%D Language specific converters have definitions like: +%D +%D \starttyping +%D \def\enordinaldaynumber#1{...} +%D \stoptyping +%D +%D Examples can be found in the other \type {lang} modules. + +\appendtoks + \ifprocessingXML \else \resetlanguagespecifics \fi +\to \everylanguage + +\protect \endinput diff --git a/tex/context/base/lang-spe.tex b/tex/context/base/lang-spe.tex deleted file mode 100644 index 00b514be2..000000000 --- a/tex/context/base/lang-spe.tex +++ /dev/null @@ -1,242 +0,0 @@ -%D \module -%D [ file=lang-spe, -%D version=2002.05.07, % 1996.01.25, -%D title=\CONTEXT\ Language Macros, -%D subtitle=Specifics, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -%D This code was originally placed in the language -%D initialization module, but isolating it is clearer. Language -%D specifics evolved out of user demands for special features, -%D like the german active quote. After a while I decided to -%D associate them to languages in a more general way so that we -%D could associate all kind of things with language switching. -%D -%D This is a typical example of functionality that occasionally -%D gets improved based on user input and experience. Much of the -%D code is pretty old and could probabbly be done in better ways. -%D It's probably also the kind of code that has been and will be -%D written over and over again by \TEX\ users around the world, -%D so there are probably better implementations of similar -%D functionality around. Therefore, users are invited to pop in -%D their own handling as long as it does not interfere with -%D existing code. Writing the more obscure macros that deal with -%D this is a good learning experience (catcodes, lccodes, token -%D lists, expansion, \unknown). - -\writestatus{loading}{Context Language Macros / Specifics} - -\unprotect - -%D \macros -%D {everyresetlanguagespecifics,resetlanguagespecifics} -%D -%D Cleanup macros. - -\newevery \everyresetlanguagespecifics \relax - -\def\resetlanguagespecifics - {\ifcase\protectionlevel - \the\everyresetlanguagespecifics - \else % to be translated - % \writestatus\m!systems{don't change language in unprotected mode!}% - \fi} - -\appendtoks - \resetlanguagespecifics -\to \everycleanupfeatures - -%D \macros -%D {startlanguagespecifics,enablelanguagespecifics} -%D -%D Each language has its own typographic pecularities. Some of -%D those can be influenced by parameters, others are handled by -%D the interface, but as soon as specific commands come into -%D view we need another mechanism. In the macro that activates -%D a language, we call \type{\enablelanguagespecifics}. This -%D macro in return calls for the setup of language specific -%D macros. Such specifics are defined as: -%D -%D \starttyping -%D \startlanguagespecifics[de] -%D \installcompoundcharacter "a {\"a} -%D \installcompoundcharacter "e {\"e} -%D \installcompoundcharacter "s {\SS} -%D \stoplanguagespecifics -%D \stoptyping -%D -%D Instead of \type{[du]} we can pass a comma separated -%D list, like \type{[du,nl]}. Next calls to this macro add the -%D specifics to the current list. -%D -%D Before we actually read the specifics, we first take some -%D precautions that will prevent spurious spaces to creep into -%D the list. - -% We should use token registers, but alas, we run out of them and -% \ETEX\ has a bug. Well, let's use a token register now (2006). - -\def\startlanguagespecifics% % we use double to - {\bgroup - \catcode`\^^I=\@@ignore - \catcode`\^^M=\@@ignore - \catcode`\^^L=\@@ignore - \dodoubleempty\dostartlanguagespecifics} % get rid of spaces - -%D The main macro looks quite complicated but actually does -%D nothing special. By embedding \type{\do} we can easily -%D append to the lists and also execute them at will. Just to -%D be sure, we check on spurious spaces. The second dummy -%D argument gobbles spaces. - -\def\languageencoding - {\ifx\characterencoding\nocharacterencoding \else - \characterencoding-% - \fi} - -\long\def\dostartlanguagespecifics[#1][#2]#3\stoplanguagespecifics - {\egroup - \processcommalist[#1]{\dosetlanguagespecifics{#3}}} - -% \long\def\dosetlanguagespecifics#1#2% -% {\ifundefined{\??la\languageencoding#2\??la}\forgetlanguagespecifics[#2]\fi -% % the next line catches the case that specifics are enabled *before* they are defined -% \expandafter\ifx\csname\??la\languageencoding#2\??la\endcsname\relax\forgetlanguagespecifics[#2]\fi -% \appendvalue{\??la\languageencoding#2\??la}{#1}% -% \bgroup -% \setbox\scratchbox\hbox{\enablelanguagespecifics[#2]}% -% \ifdim\wd\scratchbox>\zeropoint -% \showmessage\m!linguals7{\currentencoding-#2,\the\wd\scratchbox\space}\wait -% \else -% \showmessage\m!linguals8{\currentencoding-#2}% -% \fi -% \egroup -% \doif{#2}\currentmainlanguage{\enablelanguagespecifics[#2]}} - -\def\languagespectag#1{\??la\languageencoding#1\??la} - -\long\def\dosetlanguagespecifics#1#2% - {\ifcsname\languagespectag{#2}\endcsname \else - \expandafter\newtoks\csname\languagespectag{#2}\endcsname - \fi - \csname\languagespectag{#2}\endcsname\@EA{\the\csname\languagespectag{#2}\endcsname#1}% - \bgroup - \setbox\scratchbox\hbox{\enablelanguagespecifics[#2]}% - \ifdim\wd\scratchbox>\zeropoint - \showmessage\m!linguals7{\currentencoding-#2,\the\wd\scratchbox\space}\wait - \else - \showmessage\m!linguals8{\currentencoding-#2}% - \fi - \egroup - \doif{#2}\currentmainlanguage{\enablelanguagespecifics[#2]}} - -% \def\forgetlanguagespecifics[#1]% -% {\letvalue{\??la\languageencoding#1\??la}\empty} - -\def\forgetlanguagespecifics[#1]% - {\csname\languagespectag{#1}\endcsname\emptytoks} - -%D Enabling them is rather straightforward. We only have to -%D define \type{\do} in such a way that \type{{ }} is removed -%D and the language key is gobbled. - -\def\enablelanguagespecifics[#1]% - {\the\executeifdefined{\??la - \@EA\ifx\csname\??la#1\c!default\endcsname\relax - \languageencoding - \else - \csname\??la#1\c!default\endcsname - \fi - \??la}\emptytoks - \the\executeifdefined{\??la#1\??la}\emptytoks - \the\executeifdefined{\??la\languageencoding#1\??la}\emptytoks} % dup ? - -% check: - -% \def\enablelanguagespecifics[#1]% -% {\the\executeifdefined{\??la\executeifdefined{\??la#1\c!default}\languageencoding\??la}\emptytoks -% \the\executeifdefined{\??la#1\??la}\emptytoks -% \the\executeifdefined{\??la\languageencoding#1\??la}\emptytoks} % dup ? - -%D \macros -%D {deactivatelanguagespecific} -%D -%D The next code makes it possible to disable the specifics. - -% \def\deactivatelanguagespecific#1% -% {\ifundefined{l g s \string#1}% -% \letgvalueempty{l g s \string#1}% signal to prevent dup def -% \bgroup -% \catcode`#1=\@@active -% \uccode`~=`#1 -% \uppercase{\doglobal\appendtoks\dodeactivatetoken{~}\to\everyresetlanguagespecifics}% -% \egroup -% \expanded{\doglobal\noexpand\appendtoks{#1}{\the\catcode`#1}}\to\everyresetlanguagespecifics -% \fi} - -% \def\dodeactivatetoken#1#2#3% test needed to avoid clash with \unprotect -% {\def#1{#2}\ifnum\catcode`#2=\@@active\catcode`#2=#3\relax\fi} - -%D We cannot hook this into the installer since language -%D specifics can be anything. So far, we have the following -%D potentially active characters. - -%D Beware, this should happen under an unprotected regime; -%D thanks to Giuseppe Oblomov Bilotta, who first noticed -%D that something was wrong. - -\protect - -% \deactivatelanguagespecific " -% \deactivatelanguagespecific / -% \deactivatelanguagespecific : -% \deactivatelanguagespecific ; -% \deactivatelanguagespecific ? -% \deactivatelanguagespecific ! - -\unprotect - -% yes or no (taco wins: no) - -% \startlanguagespecifics[nl,cs,sk,fr] -% \lccode`\'=`\' -% \stoplanguagespecifics - -%D \macros -%D {ordinaldaynumber, highordinalstr, ordinalstr} -%D -%D Efficient general ordinal number converters are sometimes -%D difficult to implement. Fortunately dates never exceed the -%D number~31. - -\ifx\high \undefined \let\high \firstofoneargument \fi % todo -\ifx\notsmallcapped\undefined \let\notsmallcapped\firstofoneargument \fi % todo - -\def\highordinalstr#1{\high{\notsmallcapped{#1}}} -\def\ordinalstr #1{\notsmallcapped{#1}} - -\def\ordinaldaynumber#1% \strippedcsname\ordinaldaynumber - {\expanded{\executeifdefined{\currentlanguage ordinaldaynumber}% - \noexpand\firstofoneargument{\number#1}}} - -%D Language specific converters have definitions like: -%D -%D \starttyping -%D \def\enordinaldaynumber#1{...} -%D \stoptyping -%D -%D Examples can be found in the other \type {lang} modules. - -% \ifprocessingXML is a nasty dependency - -\appendtoks - \ifprocessingXML \else \resetlanguagespecifics \fi -\to \everylanguage - -\protect \endinput diff --git a/tex/context/base/lang-ura.tex b/tex/context/base/lang-ura.tex index 2ecb31e6b..a2bcd3d2b 100644 --- a/tex/context/base/lang-ura.tex +++ b/tex/context/base/lang-ura.tex @@ -13,7 +13,7 @@ % Todo: replace \'.. by \namedglyph -\writestatus{loading}{Uralic Languages} +\writestatus{loading}{ConTeXt Language Macros / Uralic Languages} %D The framework of this module is set up by Hans Hagen while %D many of the first translations were done by Tobias. Later @@ -42,8 +42,7 @@ \c!rightquote=\upperrightsingleninequote, \c!leftquotation=\upperleftdoublesixquote, \c!rightquotation=\upperrightdoubleninequote, - \c!date={\v!year,\ ,\v!month,\ ,\v!day}, - \c!state=\v!stop] + \c!date={\v!year,\ ,\v!month,\ ,\v!day}] \installlanguage [\s!hu] @@ -57,7 +56,6 @@ \c!leftquotation=\lowerleftdoubleninequote, \c!rightquotation=\upperrightdoubleninequote, \c!date={\v!year,.,\ ,\v!month,\ ,\v!day,.}, - \c!state=\v!stop, \s!mapping=ec, \s!encoding=ec] diff --git a/tex/context/base/lang-url.lua b/tex/context/base/lang-url.lua index b732dcef0..1524878cf 100644 --- a/tex/context/base/lang-url.lua +++ b/tex/context/base/lang-url.lua @@ -6,6 +6,12 @@ if not modules then modules = { } end modules ['lang-url'] = { license = "see context related readme files" } +local utf = unicode.utf8 + +local utfcharacters, utfvalues = string.utfcharacters, string.utfvalues + +local ctxcatcodes = tex.ctxcatcodes + commands = commands or { } --[[ @@ -76,13 +82,13 @@ do return "\\a{" .. u(s) .. "}" end end ) - tex.sprint(tex.ctxcatcodes,str) + tex.sprint(ctxcatcodes,str) end -- todo, no interface in mkiv yet function commands.hyphenatedurl.setcharacters(str,value) -- 1, 2 == before, after - for s in str:utfcharacters() do + for s in utfcharacters(str) do chars[s] = value or 1 end end diff --git a/tex/context/base/lang-url.mkii b/tex/context/base/lang-url.mkii index f3310cceb..fdf530b45 100644 --- a/tex/context/base/lang-url.mkii +++ b/tex/context/base/lang-url.mkii @@ -18,6 +18,29 @@ \ifx\\\undefined \let\\\crlf \fi +%D \macros +%D {hyphenatedurl} +%D +%D For those who want to put full \URL's in a text, we offer +%D +%D \startbuffer +%D \hyphenatedurl{http://optimist.optimist/optimist/optimist.optimist#optimist} +%D \stopbuffer +%D +%D \typebuffer +%D +%D which breaks at the appropriate places. Watch the \type{#} +%D hack. +%D +%D When passed as argument, like in \type {\goto}, one needs +%D to substitute a \type {\\} for each \type{#}. +%D +%D \startbuffer +%D \hyphenatedurl{http://this.is.a.rather/strange/reference#indeed} +%D \stopbuffer +%D +%D \typebuffer + \ifx\urlsplitmode\undefined \chardef\urlsplitmode\plusone \fi % 0 => don't split @@ -229,4 +252,55 @@ % % {\hsize1cm\hyphenatedstring{ABXXXXXXXXXXC-12345-12345}} +%D \macros +%D {hyphenatedfilename} +%D +%D For the moment we treat filenames in a similar way, +%D +%D \starttyping +%D \hyphenatedfilename{here/there/filename.suffix} +%D \stoptyping + +\ifx\hyphenatedfilename\undefined \let\hyphenatedfilename\hyphenatedurl \fi + +% \def\test#1% +% {\dontleavehmode +% \begingroup +% \tttf +% \hyphenatedurl {% +% \letterampersand #1\letterampersand #1\letterampersand #1\letterampersand #1\letterampersand +% \letterhash #1\letterhash #1\letterpercent #1\letterslash #1\letterampersand +% }% +% \endgroup} + +% \dorecurse{100}{\test{a} \test{ab} \test{abc} \test{abcd} \test{abcde} \test{abcdef}} + \protect \endinput + +% \bgroup + +% \gdef\lettercolon{:} + +% \catcode`\:=\active +% \catcode`\^=\active +% \catcode`\/=\active +% \catcode`\~=\active + +% \gdef\theurlcolon {\nobreak\hbox{\lettercolon}\allowbreak} +% \gdef\theurlslash#1{\nobreak\hbox{\letterslash}\ifx#1\relax\else\ifnum`/=\expandafter`\string#1\else\allowbreak\fi#1\fi} +% \gdef\theurlhat {\allowbreak\hbox{\letterhat}\nobreak} +% \gdef\theurltilde {\allowbreak\hbox{\lettertilde}\nobreak} + +% \gdef\ForMojcaWhoLikesHacks#1% +% {\dontleavehmode +% \begingroup +% \mathcode`\:="8000 \let:\theurlcolon +% \mathcode`\^="8000 \let^\theurlhat +% \mathcode`\/="8000 \let/\theurlslash +% \mathcode`\~="8000 \let~\theurltilde +% \everymath\emptytoks +% \mathsurround\zeropoint$\tf#1\relax$% +% \endgroup} +% \egroup + +% \hsize 1mm \ForMojcaWhoLikesHacks{http://www.sil.org//silesr/} diff --git a/tex/context/base/lang-url.mkiv b/tex/context/base/lang-url.mkiv index 7479fed68..c22f9f420 100644 --- a/tex/context/base/lang-url.mkiv +++ b/tex/context/base/lang-url.mkiv @@ -15,7 +15,30 @@ \unprotect -% \urlsplitmode is not (yet) supported (not that much needed) +%D \macros +%D {hyphenatedurl} +%D +%D For those who want to put full \URL's in a text, we offer +%D +%D \startbuffer +%D \hyphenatedurl{http://optimist.optimist/optimist/optimist.optimist#optimist} +%D \stopbuffer +%D +%D \typebuffer +%D +%D which breaks at the appropriate places. Watch the \type{#} +%D hack. +%D +%D When passed as argument, like in \type {\goto}, one needs +%D to substitute a \type {\\} for each \type{#}. +%D +%D \startbuffer +%D \hyphenatedurl{http://this.is.a.rather/strange/reference#indeed} +%D \stopbuffer +%D +%D \typebuffer + +\ifx\urlsplitmode\undefined \chardef\urlsplitmode\zerocount \fi % not supported in mkiv \newtoks\everyhyphenatedurl @@ -54,11 +77,34 @@ \let\n\dohyphenatedurlnormal \let\b\dohyphenatedurlbefore \let\a\dohyphenatedurlafter - \expanded{\ctxlua{commands.hyphenatedurl.action( + \normalexpanded{\noexpand\ctxlua{commands.hyphenatedurl.action( \!!bs\noexpand\detokenize{#1}\!!es, \number\hyphenatedurllefthyphenmin, \number\hyphenatedurlrighthyphenmin )}}% \endgroup} +%D \macros +%D {hyphenatedfilename} +%D +%D For the moment we treat filenames in a similar way, +%D +%D \starttyping +%D \hyphenatedfilename{here/there/filename.suffix} +%D \stoptyping + +\ifx\hyphenatedfilename\undefined \let\hyphenatedfilename\hyphenatedurl \fi + +% \def\test#1% +% {\dontleavehmode +% \begingroup +% \tttf +% \hyphenatedurl {% +% \letterampersand #1\letterampersand #1\letterampersand #1\letterampersand #1\letterampersand +% \letterhash #1\letterhash #1\letterpercent #1\letterslash #1\letterampersand +% }% +% \endgroup} + +% \dorecurse{100}{\test{a} \test{ab} \test{abc} \test{abcd} \test{abcde} \test{abcdef}} + \protect \endinput diff --git a/tex/context/base/lang-url.tex b/tex/context/base/lang-url.tex deleted file mode 100644 index 3eb891914..000000000 --- a/tex/context/base/lang-url.tex +++ /dev/null @@ -1,70 +0,0 @@ -%D \module -%D [ file=lang-url, -%D version=2008.01.22, % used to be lang-mis -%D title=\CONTEXT\ Language Macros, -%D subtitle=Language Options, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\writestatus{loading}{Context Language Macros / URL} - -\loadmarkfile{lang-url} - -\unprotect - -\ifx\urlsplitmode\undefined \chardef\urlsplitmode\zerocount \fi % not supported in mkiv - -%D \macros -%D {hyphenatedurl} -%D -%D For those who want to put full \URL's in a text, we offer -%D -%D \startbuffer -%D \hyphenatedurl{http://optimist.optimist/optimist/optimist.optimist#optimist} -%D \stopbuffer -%D -%D \typebuffer -%D -%D which breaks at the appropriate places. Watch the \type{#} -%D hack. -%D -%D When passed as argument, like in \type {\goto}, one needs -%D to substitute a \type {\\} for each \type{#}. -%D -%D \startbuffer -%D \hyphenatedurl{http://this.is.a.rather/strange/reference#indeed} -%D \stopbuffer -%D -%D \typebuffer - -\ifx\hyphenatedurl\undefined \let\hyphenatedurl\firstofoneargument \fi - -%D \macros -%D {hyphenatedfilename} -%D -%D For the moment we treat filenames in a similar way, -%D -%D \starttyping -%D \hyphenatedfilename{here/there/filename.suffix} -%D \stoptyping - -\ifx\hyphenatedfilename\undefined \let\hyphenatedfilename\hyphenatedurl \fi - -% \def\test#1% -% {\dontleavehmode -% \begingroup -% \tttf -% \hyphenatedurl {% -% \letterampersand #1\letterampersand #1\letterampersand #1\letterampersand #1\letterampersand -% \letterhash #1\letterhash #1\letterpercent #1\letterslash #1\letterampersand -% }% -% \endgroup} - -% \dorecurse{100}{\test{a} \test{ab} \test{abc} \test{abcd} \test{abcde} \test{abcdef}} - -\protect \endinput diff --git a/tex/context/base/lang-vn.tex b/tex/context/base/lang-vn.tex index 22bbe9ff6..27d2a48a1 100644 --- a/tex/context/base/lang-vn.tex +++ b/tex/context/base/lang-vn.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Vietnamese Language} +\writestatus{loading}{ConTeXt Language Macros / Vietnamese Language} %D The framework of this module is set up by Hans Hagen while %D many of the first translations were done by Tobias. Later @@ -37,7 +37,8 @@ \c!leftquotation=\quotedblleft, \c!rightquotation=\quotedblright, \c!date={{ },dd,{/},mm,{/},yy}, - \c!state=\v!stop] + \s!mapping=t5, + \s!encoding=t5] \installlanguage [vietnamese] [\s!vi] diff --git a/tex/context/base/luat-bas.tex b/tex/context/base/luat-bas.tex new file mode 100644 index 000000000..a78455173 --- /dev/null +++ b/tex/context/base/luat-bas.tex @@ -0,0 +1,64 @@ +%D \module +%D [ file=luat-bas, % moved from luat-lib, +%D version=2006.09.11, +%D title=\CONTEXT\ Lua Macros, +%D subtitle=Basic \LUA\ Libraries, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% \writestatus{loading}{ConTeXt Lua Macros / Basic Lua Libraries} + +%D This will move cq. become configurable. The XML like output is just +%D an example. + +% todo \let\normaleverytoks\everytoks \newtoks\everytoke \normaleverytoks{\the\everytoks} + +\chardef\statuswidth=15 +\chardef\statuswrite=16 + +\newtoks\everywritestring + +\def\writedirect {\immediate\write\statuswrite} +\def\writeline {\writedirect{}} +\def\writestring#1{\begingroup\the\everywritestring\writedirect{#1}\endgroup} + +\ifx\normalwritestatus\undefined \def\normalwritestatus#1#2{\writedirect{#1 : #2}} \fi + +% Because all libs are also on bytecodes we can start without stub. However, +% some initializations need to take place before the \TEX\ engine itself +% kicks in, especially memory settings and so. In due time we might make the +% stub smaller and just create a configuration startup file. + +\registerctxluafile{l-string} {1.001} +\registerctxluafile{l-lpeg} {1.001} +\registerctxluafile{l-boolean}{1.001} +\registerctxluafile{l-number} {1.001} +\registerctxluafile{l-math} {1.001} +\registerctxluafile{l-table} {1.001} +\registerctxluafile{l-aux} {1.001} +\registerctxluafile{l-io} {1.001} +\registerctxluafile{l-os} {1.001} +\registerctxluafile{l-file} {1.001} +\registerctxluafile{l-md5} {1.001} +\registerctxluafile{l-dir} {1.001} +\registerctxluafile{l-unicode}{1.001} +\registerctxluafile{l-utils} {1.001} +\registerctxluafile{l-dimen} {1.001} +\registerctxluafile{l-url} {1.001} +\registerctxluafile{l-set} {1.001} + +% \registerctxluafile{socket.lua}{} +% \registerctxluafile{ltn12.lua} {} +% \registerctxluafile{mime.lua} {} +% \registerctxluafile{http.lua} {} +% \registerctxluafile{url.lua} {} +% \registerctxluafile{tp.lua} {} +% \registerctxluafile{ftp.lua} {} +% %registerctxluafile{smtp.lua} {} + +\endinput diff --git a/tex/context/base/luat-cbk.lua b/tex/context/base/luat-cbk.lua index 4069fe61f..d8b508c13 100644 --- a/tex/context/base/luat-cbk.lua +++ b/tex/context/base/luat-cbk.lua @@ -6,6 +6,8 @@ if not modules then modules = { } end modules ['luat-cbk'] = { license = "see context related readme files" } +local trace_checking = false trackers.register("memory.checking", function(v) trace_checking = v end) + --[[ldx--Callbacks are the real asset of
This does a one-shot.
--ldx]]-- - --[[ldx--Callbacks may result in
We cannot load anything yet. However what we will do us reserve a fewtables. These can be used for runtime user data or third party modules and will not be cluttered by macro package code.
--ldx]]-- -userdata = userdata or { } -thirddata = thirddata or { } -document = document or { } +userdata = userdata or { } -- might be used +thirddata = thirddata or { } -- might be used +moduledata = moduledata or { } -- might be used +document = document or { } + +--[[ldx-- +These can be used/set by the caller program;
Please create a namespace within these tables before using them!
@@ -24,3 +34,116 @@ userdata ['my.name'] = { } thirddata['tricks' ] = { } --ldx]]-- + +--[[ldx-- +We could cook up a readonly model for global tables but it +makes more sense to invite users to use one of the predefined +namespaces. One can redefine the protector. After all, it's +just a lightweight suggestive system, not a watertight +one.
+--ldx]]-- + +local string, table, lpeg, math, io, system = string, table, lpeg, math, io, system +local next, setfenv = next, setfenv +local format = string.format + +local global = _G + +global.global = global + +local dummy = function() end + +local protected = { + -- global table + global = global, + -- user tables + userdata = userdata, + moduledata = moduledata, + thirddata = thirddata, + document = document, + -- reserved + protect = dummy, + unprotect = dummy, + -- luatex + tex = tex, + -- lua + string = string, + table = table, + lpeg = lpeg, + math = math, + io = io, + system = system, +} + +userdata, thirddata, moduledata = nil, nil, nil + +function protect(name) + if name == "isolateddata" then + local t = { } + for k, v in next, protected do + t[k] = v + end + setfenv(2,t) + else + if not name then + name = "shareddata" + end + local t = global[name] + if not t then + t = { } + for k, v in next, protected do + t[k] = v + end + global[name] = t + end + setfenv(2,t) + end +end + +lua.numbers = { } +lua.messages = { } + +function lua.registername(name,message) + local lnn = lua.numbers[name] + if not lnn then + lnn = #lua.messages + 1 + lua.messages[lnn] = message + lua.numbers[name] = lnn + end + lua.name[lnn] = message + tex.write(lnn) +end + +--~ function lua.checknames() +--~ lua.name[0] = "ctx" +--~ for k, v in next, lua.messages do +--~ lua.name[k] = v +--~ end +--~ end + +storage.register("lua/numbers", lua.numbers, "lua.numbers") +storage.register("lua/messages", lua.messages, "lua.messages") + +function document.setargument(key,value) + document.arguments[key] = value +end + +function document.setdefaultargument(key,default) + local v = document.arguments[key] + if v == nil or v == "" then + document.arguments[key] = default + end +end + +function document.getargument(key,default) + local v = document.arguments[key] + if type(v) == "boolean" then + v = (v and "yes") or "no" + document.arguments[key] = v + end + tex.sprint(tex.ctxcatcodes,v or default or "") +end + +function document.getfilename(i) + tex.sprint(tex.ctxcatcodes,document.files[i] or "") +end diff --git a/tex/context/base/luat-ini.tex b/tex/context/base/luat-ini.tex index 1e1e20ebe..265f1b643 100644 --- a/tex/context/base/luat-ini.tex +++ b/tex/context/base/luat-ini.tex @@ -11,15 +11,10 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Lua Support Macros (initialization)} +\writestatus{loading}{ConTeXt Lua Macros / Initialization} \unprotect -%D We have to load this module in a very early stage. Therefore we -%D cannot rely on support macros being available. - -% \long\def\rescan#1{\expanded{\scantextokens{#1}}} - %D Loading lua code can be done using \type {startup.lua}. The following %D method uses the \TEX\ input file locator of kpse. At least we need to %D use that way of loading when we haven't yet define our own code, which @@ -30,36 +25,27 @@ \ifx\obeylualines \undefined \let\obeylualines \relax \fi \ifx\obeyluatokens \undefined \let\obeyluatokens \relax \fi -% \def\loadluacode#1#2% instance filename -% {\bgroup -% \everyeof{\noexpand}% hack to make \input nicely expandable -% \setnaturalcatcodes -% \obeylualines -% %message{[Lua Load: #2]}% -% \directlua#1\expandafter{\normalinput#2\space}\relax -% \egroup} - %D A few more goodies: -\long\def\dostartlua#1% +\long\def\dostartlua {\begingroup \obeylualines - \dodostartlua{#1}} + \dodostartlua} -\long\def\dodostartlua#1#2\stoplua - {\expanded{\endgroup\noexpand\directlua#1{#2}}} +\long\def\dodostartlua#1\stoplua + {\normalexpanded{\endgroup\noexpand\directlua\zerocount{#1}}} -\long\def\dostartluacode#1% +\long\def\dostartluacode {\begingroup \obeylualines \obeyluatokens - \dodostartluacode{#1}} + \dodostartluacode} -\long\def\dodostartluacode#1#2\stopluacode - {\expanded{\endgroup\noexpand\directlua#1{#2}}} +\long\def\dodostartluacode#1\stopluacode + {\normalexpanded{\endgroup\noexpand\directlua\zerocount{#1}}} -\def\startlua {\dostartlua \zerocount} -\def\startluacode{\dostartluacode\zerocount} +\def\startlua {\dostartlua } % tex catcodes +\def\startluacode{\dostartluacode} % lua catcodes %D Some delayed definitions: @@ -69,40 +55,164 @@ \ifx\obeyedspace \undefined \let\obeyedspace \relax \fi \ifx\outputnewlinechar\undefined \let\outputnewlinechar\relax \fi -\def\obeylualines - {\obeylines \let\obeyedline \outputnewlinechar - \obeyspaces \let\obeyedspace\space} - -\def\obeyluatokens % todo: make this a proper catcode table, use let's - {\catcode`\%=12 \catcode`\#=12 - \catcode`\_=12 \catcode`\^=12 - \catcode`\&=12 \catcode`\|=12 - \catcode`\{=12 \catcode`\}=12 - \catcode`\~=12 \catcode`\$=12 - \def\\{\string\\}\def\|{\string\|}\def\-{\string\-}% - \def\({\string\(}\def\){\string\)}\def\{{\string\{}\def\}{\string\}}% - \def\'{\string\'}\def\"{\string\"}% - \def\n{\string\n}\def\r{\string\r}\def\f{\string\f}\def\t{\string\t}% - \def\a{\string\a}\def\b{\string\b}\def\v{\string\v}\def\s{\string\s}% - \def\1{\string1}\def\2{\string2}\def\3{\string3}\def\4{\string\4}\def\5{\string\5}% - \def\6{\string6}\def\7{\string7}\def\8{\string8}\def\9{\string\9}\def\0{\string\0}} - +%D A previous version used a bit less code and no catcode table, +%D simply becaus ethey were not around at the time of writing. +% +% we keep it around for archival purposes +% +% \def\obeylualines +% {\obeylines \let\obeyedline \outputnewlinechar +% \obeyspaces \let\obeyedspace\space} +% +% \def\obeyluatokens % todo: make this a proper catcode table, use let's +% {\catcode`\%=12 \catcode`\#=12 +% \catcode`\_=12 \catcode`\^=12 +% \catcode`\&=12 \catcode`\|=12 +% \catcode`\{=12 \catcode`\}=12 +% \catcode`\~=12 \catcode`\$=12 +% \def\\{\string\\}\def\|{\string\|}\def\-{\string\-}% +% \def\({\string\(}\def\){\string\)}\def\{{\string\{}\def\}{\string\}}% +% \def\'{\string\'}\def\"{\string\"}% +% \def\n{\string\n}\def\r{\string\r}\def\f{\string\f}\def\t{\string\t}% +% \def\a{\string\a}\def\b{\string\b}\def\v{\string\v}\def\s{\string\s}% +% \def\1{\string\1}\def\2{\string\2}\def\3{\string\3}\def\4{\string\4}\def\5{\string\5}% +% \def\6{\string\6}\def\7{\string\7}\def\8{\string\8}\def\9{\string\9}\def\0{\string\0}} + +\let\obeylualines\relax + +\newtoks\everyluacode + +\edef\lualetterbackslash{\string\\} +\edef\lualetterbar {\string\|} \edef\lualetterdash {\string\-} +\edef\lualetterlparent {\string\(} \edef\lualetterrparent {\string\)} +\edef\lualetterlbrace {\string\{} \edef\lualetterrbrace {\string\}} +\edef\lualettersquote {\string\'} \edef\lualetterdquote {\string\"} +\edef\lualettern {\string\n} \edef\lualetterr {\string\r} +\edef\lualetterf {\string\f} \edef\lualettert {\string\t} +\edef\lualettera {\string\a} \edef\lualetterb {\string\b} +\edef\lualetterv {\string\v} \edef\lualetters {\string\s} +\edef\lualetterone {\string\1} \edef\lualettertwo {\string\2} +\edef\lualetterthree {\string\3} \edef\lualetterfour {\string\4} +\edef\lualetterfive {\string\5} \edef\lualettersix {\string\6} +\edef\lualetterseven {\string\7} \edef\lualettereight {\string\8} +\edef\lualetternine {\string\9} \edef\lualetterzero {\string\0} + +\appendtoks + \let\\\lualetterbackslash + \let\|\lualetterbar \let\-\lualetterdash + \let\(\lualetterlparent \let\)\lualetterrparent + \let\{\lualetterlbrace \let\}\lualetterrbrace + \let\'\lualettersquote \let\"\lualetterdquote + \let\n\lualettern \let\r\lualetterr + \let\f\lualetterf \let\t\lualettert + \let\a\lualettera \let\b\lualetterb + \let\v\lualetterv \let\s\lualetters + \let\1\lualetterone \let\2\lualettertwo + \let\3\lualetterthree \let\4\lualetterfour + \let\5\lualetterfive \let\6\lualettersix + \let\7\lualetterseven \let\8\lualettereight + \let\9\lualetternine \let\0\lualetterzero +\to \everyluacode + +\def\obeyluatokens + {\setcatcodetable \luacatcodes + \the\everyluacode} + +%D \macros +%D {definenamedlua} +%D %D We provide an interface for defining instances: -\def\s!lua{lua} \def\v!code{code} \let\@EA\expandafter +\def\s!lua{lua} \def\v!code{code} \def\!!name{name} \def\s!data{data} -\def\setluainstancename#1#2% - {\ifproductionrun\else\appendtoks\setluainstancename{#1}{#2}\to\everyjob\fi - \directlua0{if lua.instancename then lua.instancename[\number#1]="#2" end}} +%D Beware: because \type {\expanded} is een convert command, the error +%D message will show \type{This file is used when we want the input handlers to behave like
-
How about just forgetting abou them?
---ldx]]-- - -input = input or { } -input.suffixes = input.suffixes or { } -input.formats = input.formats or { } - -input.suffixes['gf'] = { 'If you wondered abou tsome of the previous mappings, how about -the next bunch:
---ldx]]-- - -input.formats['bib'] = '' -input.formats['bst'] = '' -input.formats['mft'] = '' -input.formats['ist'] = '' -input.formats['web'] = '' -input.formats['cweb'] = '' -input.formats['MetaPost support'] = '' -input.formats['TeX system documentation'] = '' -input.formats['TeX system sources'] = '' -input.formats['Troff fonts'] = '' -input.formats['dvips config'] = '' -input.formats['graphic/figure'] = '' -input.formats['ls-R'] = '' -input.formats['other text files'] = '' -input.formats['other binary files'] = '' - -input.formats['gf'] = '' -input.formats['pk'] = '' -input.formats['base'] = 'MFBASES' -input.formats['cnf'] = '' -input.formats['mem'] = 'MPMEMS' -input.formats['mf'] = 'MFINPUTS' -input.formats['mfpool'] = 'MFPOOL' -input.formats['mppool'] = 'MPPOOL' -input.formats['texpool'] = 'TEXPOOL' -input.formats['PostScript header'] = 'TEXPSHEADERS' -input.formats['cmap files'] = 'CMAPFONTS' -input.formats['type42 fonts'] = 'T42FONTS' -input.formats['web2c files'] = 'WEB2C' -input.formats['pdftex config'] = 'PDFTEXCONFIG' -input.formats['texmfscripts'] = 'TEXMFSCRIPTS' -input.formats['bitmap font'] = '' -input.formats['lig files'] = 'LIGFONTS' diff --git a/tex/context/base/luat-lib.lua b/tex/context/base/luat-lib.lua deleted file mode 100644 index 06d00e778..000000000 --- a/tex/context/base/luat-lib.lua +++ /dev/null @@ -1,174 +0,0 @@ -if not modules then modules = { } end modules ['luat-lib'] = { - version = 1.001, - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files", - comment = "companion to luat-lib.tex", -} - --- most code already moved to the l-*.lua and other luat-*.lua files - -os.setlocale(nil,nil) -- useless feature and even dangerous in luatex - -function os.setlocale() - -- no way you can mess with it -end - -if arg and (arg[0] == 'luatex' or arg[0] == 'luatex.exe') and arg[1] == "--luaonly" then - arg[-1]=arg[0] arg[0]=arg[2] for k=3,#arg do arg[k-2]=arg[k] end arg[#arg]=nil arg[#arg]=nil -end - -environment = environment or { } -environment.arguments = { } -environment.files = { } -environment.sortedflags = nil - -function environment.initialize_arguments(arg) - local arguments, files = { }, { } - environment.arguments, environment.files, environment.sortedflags = arguments, files, nil - for index, argument in pairs(arg) do - if index > 0 then - local flag, value = argument:match("^%-+(.+)=(.-)$") - if flag then - arguments[flag] = string.unquote(value or "") - else - flag = argument:match("^%-+(.+)") - if flag then - arguments[flag] = true - else - files[#files+1] = argument - end - end - end - end - environment.ownname = environment.ownname or arg[0] or 'unknown.lua' -end - -function environment.showarguments() - for k,v in pairs(environment.arguments) do - print(k .. " : " .. tostring(v)) - end - if #environment.files > 0 then - print("files : " .. table.concat(environment.files, " ")) - end -end - -function environment.setargument(name,value) - environment.arguments[name] = value -end - -function environment.argument(name) -- todo: default (plus typecheck on default) - local arguments, sortedflags = environment.arguments, environment.sortedflags - if arguments[name] then - return arguments[name] - else - if not sortedflags then - sortedflags = { } - for _,v in pairs(table.sortedkeys(arguments)) do - sortedflags[#sortedflags+1] = "^" .. v - end - environment.sortedflags = sortedflags - end - for _,v in ipairs(sortedflags) do - if name:find(v) then - return arguments[v:sub(2,#v)] - end - end - end - return nil -end - -function environment.split_arguments(separator) -- rather special, cut-off before separator - local done, before, after = false, { }, { } - for _,v in ipairs(environment.original_arguments) do - if not done and v == separator then - done = true - elseif done then - after[#after+1] = v - else - before[#before+1] = v - end - end - return before, after -end - ---~ function environment.reconstruct_commandline(arg) ---~ if not arg then arg = environment.original_arguments end ---~ local result = { } ---~ for _,a in ipairs(arg) do -- ipairs 1 .. #n ---~ local kk, vv = a:match("^(%-+.-)=(.+)$") ---~ if kk and vv then ---~ if vv:find(" ") then ---~ vv = vv:unquote() ---~ vv = vv:gsub('"','\\"') ---~ result[#result+1] = kk .. "=" .. vv:quote() ---~ else ---~ a = a:unquote() ---~ a = a:gsub('"','\\"') ---~ result[#result+1] = a ---~ end ---~ elseif a:find(" ") then ---~ a = a:unquote() ---~ a = a:gsub('"','\\"') ---~ result[#result+1] = a:quote() ---~ else ---~ result[#result+1] = a ---~ end ---~ end ---~ return table.join(result," ") ---~ end - -function environment.reconstruct_commandline(arg,noquote) - if not arg then arg = environment.original_arguments end - if noquote and #arg == 1 then - local a = arg[1] - a = input.resolve(a) - a = a:unquote() - return a - elseif #arg == 1 then - local result = { } - for _,a in ipairs(arg) do -- ipairs 1 .. #n - a = input.resolve(a) - a = a:unquote() - a = a:gsub('"','\\"') -- tricky - if a:find(" ") then - result[#result+1] = a:quote() - else - result[#result+1] = a - end - end - return table.join(result," ") - end -end - -if arg then - - -- new, reconstruct quoted snippets (maybe better just remnove the " then and add them later) - local newarg, instring = { }, false - - for index, argument in ipairs(arg) do - if argument:find("^\"") then - newarg[#newarg+1] = argument:gsub("^\"","") - if not argument:find("\"$") then - instring = true - end - elseif argument:find("\"$") then - newarg[#newarg] = newarg[#newarg] .. " " .. argument:gsub("\"$","") - instring = false - elseif instring then - newarg[#newarg] = newarg[#newarg] .. " " .. argument - else - newarg[#newarg+1] = argument - end - end - for i=1,-5,-1 do - newarg[i] = arg[i] - end - - environment.initialize_arguments(newarg) - environment.original_arguments = newarg - environment.raw_arguments = arg - - arg = { } -- prevent duplicate handling - -end diff --git a/tex/context/base/luat-lib.tex b/tex/context/base/luat-lib.tex index 9693595b2..ec781f3cf 100644 --- a/tex/context/base/luat-lib.tex +++ b/tex/context/base/luat-lib.tex @@ -2,7 +2,7 @@ %D [ file=luat-lib, %D version=2006.09.11, %D title=\CONTEXT\ Lua Macros, -%D subtitle=Unicode Support, +%D subtitle=Libraries, %D author=Hans Hagen, %D date=\currentdate, %D copyright=PRAGMA] @@ -11,60 +11,41 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -% \writestatus{loading}{Lua Support Macros (libs)} +% \writestatus{loading}{ConTeXt Lua Macros / Libraries} -%D For the moment we only load this lib. +\registerctxluafile{trac-inf} {1.001} +\registerctxluafile{trac-tra} {1.001} +\registerctxluafile{trac-log} {1.001} -%D This will move cq. become configurable. The XML like output is just -%D an example. - -% todo \let\normaleverytoks\everytoks \newtoks\everytoke \normaleverytoks{\the\everytoks} - -\chardef\statuswidth=15 -\chardef\statuswrite=16 - -\newtoks\everywritestring - -\def\writedirect {\immediate\write\statuswrite} -\def\writeline {\writedirect{}} -\def\writestring#1{\begingroup\the\everywritestring\writedirect{#1}\endgroup} - -\ifx\normalmessage \undefined \let\normalmessage \message \fi -\ifx\normalwritestatus\undefined \def\normalwritestatus#1#2{\writedirect{#1 : #2}} \fi - -% this will change once we have proper write overloads +\registerctxluafile{luat-cbk} {1.001} -\registerctxluafile{l-string} {1.001} -\registerctxluafile{l-lpeg} {1.001} -\registerctxluafile{l-boolean}{1.001} -\registerctxluafile{l-number} {1.001} -\registerctxluafile{l-set} {1.001} -\registerctxluafile{l-math} {1.001} -\registerctxluafile{l-table} {1.001} -\registerctxluafile{l-md5} {1.001} -\registerctxluafile{l-aux} {1.001} -\registerctxluafile{l-io} {1.001} -\registerctxluafile{l-os} {1.001} -\registerctxluafile{l-file} {1.001} -\registerctxluafile{l-dir} {1.001} -\registerctxluafile{l-unicode}{1.001} -\registerctxluafile{l-utils} {1.001} -\registerctxluafile{l-dimen} {1.001} -\registerctxluafile{l-url} {1.001} -\registerctxluafile{l-xml} {1.001} -%registerctxluafile{l-xmlctx} {1.001} +\registerctxluafile{data-res} {1.001} +\registerctxluafile{data-tmp} {1.001} +\registerctxluafile{data-pre} {1.001} +\registerctxluafile{data-inp} {1.001} +\registerctxluafile{data-out} {1.001} +\registerctxluafile{data-tex} {1.001} +\registerctxluafile{data-bin} {1.001} +\registerctxluafile{data-zip} {1.001} +\registerctxluafile{data-crl} {1.001} +\registerctxluafile{data-tre} {1.001} +\registerctxluafile{data-lua} {1.001} +\registerctxluafile{data-ctx} {1.001} +\registerctxluafile{data-con} {1.001} +\registerctxluafile{data-use} {1.001} -\registerctxluafile{luat-cbk} {1.001} -\registerctxluafile{luat-lib} {1.001} -\registerctxluafile{luat-inp} {1.001} -\registerctxluafile{luat-log} {1.001} -\registerctxluafile{luat-zip} {1.001} -\registerctxluafile{luat-tex} {1.001} +\registerctxluafile{luat-run} {1.001} +\registerctxluafile{luat-fio} {1.001} % not needed, part of startup file +\registerctxluafile{luat-cnf} {1.001} % not needed, part of startup file \registerctxluafile{luat-lua} {1.001} -\registerctxluafile{luat-tre} {1.001} +\registerctxluafile{luat-sto} {1.001} +\registerctxluafile{luat-ini} {1.001} +\registerctxluafile{luat-env} {1.001} + +\registerctxluafile{l-xml} {1.001} % we want tracking \startruntimeluacode - \edef\asciia{\ctxlua{tex.sprint(input.logmode())}} + \edef\asciia{\ctxlua{tex.sprint(logs.mode)}} \edef\asciib{xml} \ifx\asciia\asciib % brrr \long\def\writebanner #1{\writestring {This is a prelude to a more extensive logging module. For the sake
-of parsing log files, in addition to the standard logging we will
-provide an
This looks pretty ugly but we need to speed things up a bit.
---ldx]]-- - -logs.levels = { - ['error'] = 1, - ['warning'] = 2, - ['info'] = 3, - ['debug'] = 4 -} - -logs.functions = { - 'report', 'start', 'stop', 'push', 'pop', 'line', 'direct' -} - -logs.callbacks = { - 'start_page_number', - 'stop_page_number', - 'report_output_pages', - 'report_output_log' -} - -logs.tracers = { -} - -logs.xml = logs.xml or { } -logs.tex = logs.tex or { } - -logs.level = 0 - -local write_nl, write, format = texio.write_nl or print, texio.write or io.write, string.format - -if texlua then - write_nl = print - write = io.write -end - -function logs.xml.report(category,fmt,...) -- new - write_nl(format("This module deals with caching data. It sets up the paths and -implements loaders and savers for tables. Best is to set the -following variable. When not set, the usual paths will be -checked. Personally I prefer the (users) temporary path.
- - -TEXMFCACHE=$TMP;$TEMP;$TMPDIR;$TEMPDIR;$HOME;$TEXMFVAR;$VARTEXMF;. - - -Currently we do no locking when we write files. This is no real -problem because most caching involves fonts and the chance of them -being written at the same time is small. We also need to extend -luatools with a recache feature.
---ldx]]-- - -local format = string.format - -caches = caches or { } -dir = dir or { } -texmf = texmf or { } - -caches.path = caches.path or nil -caches.base = caches.base or "luatex-cache" -caches.more = caches.more or "context" -caches.direct = false -- true is faster but may need huge amounts of memory -caches.trace = false -caches.tree = false -caches.paths = caches.paths or nil -caches.force = false -caches.defaults = { "TEXMFCACHE", "TMPDIR", "TEMPDIR", "TMP", "TEMP", "HOME", "HOMEPATH" } - -function caches.temp() - local cachepath = nil - local function check(list,isenv) - if not cachepath then - for _, v in ipairs(list) do - cachepath = (isenv and (os.env[v] or "")) or v or "" - if cachepath == "" then - -- next - else - cachepath = input.clean_path(cachepath) - if lfs.isdir(cachepath) and file.iswritable(cachepath) then -- lfs.attributes(cachepath,"mode") == "directory" - break - elseif caches.force or io.ask(format("\nShould I create the cache path %s?",cachepath), "no", { "yes", "no" }) == "yes" then - dir.mkdirs(cachepath) - if lfs.isdir(cachepath) and file.iswritable(cachepath) then - break - end - end - end - cachepath = nil - end - end - end - check(input.clean_path_list("TEXMFCACHE") or { }) - check(caches.defaults,true) - if not cachepath then - print("\nfatal error: there is no valid (writable) cache path defined\n") - os.exit() - elseif not lfs.isdir(cachepath) then -- lfs.attributes(cachepath,"mode") ~= "directory" - print(format("\nfatal error: cache path %s is not a directory\n",cachepath)) - os.exit() - end - cachepath = input.normalize_name(cachepath) - function caches.temp() - return cachepath - end - return cachepath -end - -function caches.configpath() - return table.concat(input.instance.cnffiles,";") -end - -function caches.hashed(tree) - return md5.hex((tree:lower()):gsub("[\\\/]+","/")) -end - ---~ tracing: - ---~ function caches.hashed(tree) ---~ tree = (tree:lower()):gsub("[\\\/]+","/") ---~ local hash = md5.hex(tree) ---~ if input.verbose then -- temp message ---~ input.report("hashing %s => %s",tree,hash) ---~ end ---~ return hash ---~ end - -function caches.treehash() - local tree = caches.configpath() - if not tree or tree == "" then - return false - else - return caches.hashed(tree) - end -end - -function caches.setpath(...) - if not caches.path then - if not caches.path then - caches.path = caches.temp() - end - caches.path = input.clean_path(caches.path) -- to be sure - if lfs then - caches.tree = caches.tree or caches.treehash() - if caches.tree then - caches.path = dir.mkdirs(caches.path,caches.base,caches.more,caches.tree) - else - caches.path = dir.mkdirs(caches.path,caches.base,caches.more) - end - end - end - if not caches.path then - caches.path = '.' - end - caches.path = input.clean_path(caches.path) - if lfs and not table.is_empty({...}) then - local pth = dir.mkdirs(caches.path,...) - return pth - end - caches.path = dir.expand_name(caches.path) - return caches.path -end - -function caches.definepath(category,subcategory) - return function() - return caches.setpath(category,subcategory) - end -end - -function caches.setluanames(path,name) - return path .. "/" .. name .. ".tma", path .. "/" .. name .. ".tmc" -end - -function caches.loaddata(path,name) - local tmaname, tmcname = caches.setluanames(path,name) - local loader = loadfile(tmcname) or loadfile(tmaname) - if loader then - return loader() - else - return false - end -end - -function caches.is_writable(filepath,filename) - local tmaname, tmcname = caches.setluanames(filepath,filename) - return file.is_writable(tmaname) -end - -function caches.savedata(filepath,filename,data,raw) - local tmaname, tmcname = caches.setluanames(filepath,filename) - local reduce, simplify = true, true - if raw then - reduce, simplify = false, false - end - if caches.direct then - file.savedata(tmaname, table.serialize(data,'return',true,true,false)) -- no hex - else - table.tofile(tmaname, data,'return',true,true,false) -- maybe not the last true - end - local cleanup = input.boolean_variable("PURGECACHE", false) - local strip = input.boolean_variable("LUACSTRIP", true) - utils.lua.compile(tmaname, tmcname, cleanup, strip) -end - --- here we use the cache for format loading (texconfig.[formatname|jobname]) - ---~ if tex and texconfig and texconfig.formatname and texconfig.formatname == "" then -if tex and texconfig and (not texconfig.formatname or texconfig.formatname == "") and input and input.instance then - if not texconfig.luaname then texconfig.luaname = "cont-en.lua" end -- or luc - texconfig.formatname = caches.setpath("formats") .. "/" .. texconfig.luaname:gsub("%.lu.$",".fmt") -end - ---[[ldx-- -Once we found ourselves defining similar cache constructs -several times, containers were introduced. Containers are used -to collect tables in memory and reuse them when possible based -on (unique) hashes (to be provided by the calling function).
- -Caching to disk is disabled by default. Version numbers are -stored in the saved table which makes it possible to change the -table structures without bothering about the disk cache.
- -Examples of usage can be found in the font related code.
---ldx]]-- - -containers = { } -containers.trace = false - -do -- local report - - local function report(container,tag,name) - if caches.trace or containers.trace or container.trace then - logs.report(format("%s cache",container.subcategory),"%s: %s",tag,name or 'invalid') - end - end - - local allocated = { } - - -- tracing - - function containers.define(category, subcategory, version, enabled) - return function() - if category and subcategory then - local c = allocated[category] - if not c then - c = { } - allocated[category] = c - end - local s = c[subcategory] - if not s then - s = { - category = category, - subcategory = subcategory, - storage = { }, - enabled = enabled, - version = version or 1.000, - trace = false, - path = caches.setpath(category,subcategory), - } - c[subcategory] = s - end - return s - else - return nil - end - end - end - - function containers.is_usable(container, name) - return container.enabled and caches.is_writable(container.path, name) - end - - function containers.is_valid(container, name) - if name and name ~= "" then - local storage = container.storage[name] - return storage and not table.is_empty(storage) and storage.cache_version == container.version - else - return false - end - end - - function containers.read(container,name) - if container.enabled and not container.storage[name] then - container.storage[name] = caches.loaddata(container.path,name) - if containers.is_valid(container,name) then - report(container,"loaded",name) - else - container.storage[name] = nil - end - end - if container.storage[name] then - report(container,"reusing",name) - end - return container.storage[name] - end - - function containers.write(container, name, data) - if data then - data.cache_version = container.version - if container.enabled then - local unique, shared = data.unique, data.shared - data.unique, data.shared = nil, nil - caches.savedata(container.path, name, data) - report(container,"saved",name) - data.unique, data.shared = unique, shared - end - report(container,"stored",name) - container.storage[name] = data - end - return data - end - - function containers.content(container,name) - return container.storage[name] - end - -end - --- since we want to use the cache instead of the tree, we will now --- reimplement the saver. - -local save_data = input.aux.save_data -local load_data = input.aux.load_data - -input.cachepath = nil -- public, for tracing -input.usecache = true -- public, for tracing - -function input.aux.save_data(dataname, check) - save_data(dataname, check, function(cachename,dataname) - input.usecache = not toboolean(input.expansion("CACHEINTDS") or "false",true) - if input.usecache then - input.cachepath = input.cachepath or caches.definepath("trees") - return file.join(input.cachepath(),caches.hashed(cachename)) - else - return file.join(cachename,dataname) - end - end) -end - -function input.aux.load_data(pathname,dataname,filename) - load_data(pathname,dataname,filename,function(dataname,filename) - input.usecache = not toboolean(input.expansion("CACHEINTDS") or "false",true) - if input.usecache then - input.cachepath = input.cachepath or caches.definepath("trees") - return file.join(input.cachepath(),caches.hashed(pathname)) - else - if not filename or (filename == "") then - filename = dataname - end - return file.join(pathname,filename) - end - end) -end - --- we will make a better format, maybe something xml or just text or lua - -input.automounted = input.automounted or { } - -function input.automount(usecache) - local mountpaths = input.clean_path_list(input.expansion('TEXMFMOUNT')) - if table.is_empty(mountpaths) and usecache then - mountpaths = { caches.setpath("mount") } - end - if not table.is_empty(mountpaths) then - input.starttiming(input.instance) - for k, root in pairs(mountpaths) do - local f = io.open(root.."/url.tmi") - if f then - for line in f:lines() do - if line then - if line:find("^[%%#%-]") then -- or %W - -- skip - elseif line:find("^zip://") then - input.report("mounting %s",line) - table.insert(input.automounted,line) - input.usezipfile(line) - end - end - end - f:close() - end - end - input.stoptiming(input.instance) - end -end - --- store info in format - -input.storage = { } -input.storage.data = { } -input.storage.min = 0 -- 500 -input.storage.max = input.storage.min - 1 -input.storage.trace = false -- true -input.storage.done = input.storage.done or 0 -input.storage.evaluators = { } --- (evaluate,message,names) - -function input.storage.register(...) - input.storage.data[#input.storage.data+1] = { ... } -end - -function input.storage.evaluate(name) - input.storage.evaluators[#input.storage.evaluators+1] = name -end - -function input.storage.finalize() -- we can prepend the string with "evaluate:" - for _, t in ipairs(input.storage.evaluators) do - for i, v in pairs(t) do - if type(v) == "string" then - t[i] = loadstring(v)() - elseif type(v) == "table" then - for _, vv in pairs(v) do - if type(vv) == "string" then - t[i] = loadstring(vv)() - end - end - end - end - end -end - -function input.storage.dump() - for name, data in ipairs(input.storage.data) do - local evaluate, message, original, target = data[1], data[2], data[3] ,data[4] - local name, initialize, finalize, code = nil, "", "", "" - for str in target:gmatch("([^%.]+)") do - if name then - name = name .. "." .. str - else - name = str - end - initialize = format("%s %s = %s or {} ", initialize, name, name) - end - if evaluate then - finalize = "input.storage.evaluate(" .. name .. ")" - end - input.storage.max = input.storage.max + 1 - if input.storage.trace then - logs.report('storage','saving %s in slot %s',message,input.storage.max) - code = - initialize .. - format("logs.report('storage','restoring %s from slot %s') ",message,input.storage.max) .. - table.serialize(original,name) .. - finalize - else - code = initialize .. table.serialize(original,name) .. finalize - end - lua.bytecode[input.storage.max] = loadstring(code) - end -end - --- we also need to count at generation time (nicer for message) - -if lua.bytecode then -- from 0 upwards - local i = input.storage.min - while lua.bytecode[i] do - lua.bytecode[i]() - lua.bytecode[i] = nil - i = i + 1 - end - input.storage.done = i -end diff --git a/tex/context/base/luat-tra.lua b/tex/context/base/luat-tra.lua deleted file mode 100644 index 5314b48c6..000000000 --- a/tex/context/base/luat-tra.lua +++ /dev/null @@ -1,145 +0,0 @@ --- filename : luat-tra.lua --- comment : companion to luat-lib.tex --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files - --- theWe provide (at least here) two entity handlers. The more extensive
+resolver consults a hash first, tries to convert to
The following helper functions best belong to the
This module can be used stand alone but also inside
We've now arrived at an intersting part: accessing the tree using a subset
+of
An
Access to the root and data table makes it possible to construct insert and delete +functions.
+--ldx]]-- + +local functions = xml.functions +local expressions = xml.expressions + +expressions.contains = string.find +expressions.find = string.find +expressions.upper = string.upper +expressions.lower = string.lower +expressions.number = tonumber +expressions.boolean = toboolean + +expressions.oneof = function(s,...) -- slow + local t = {...} for i=1,#t do if s == t[i] then return true end end return false +end + +expressions.error = function(str) + xml.error_handler("unknown function in lpath expression",str or "?") + return false +end + +functions.text = function(root,k,n) -- unchecked, maybe one deeper + local t = type(t) + if t == "string" then + return t + else -- todo n + local rdt = root.dt + return (rdt and rdt[k]) or root[k] or "" + end +end + +functions.name = function(d,k,n) -- ns + tg + local found = false + n = n or 0 + if not k then + -- not found + elseif n == 0 then + local dk = d[k] + found = dk and (type(dk) == "table") and dk + elseif n < 0 then + for i=k-1,1,-1 do + local di = d[i] + if type(di) == "table" then + if n == -1 then + found = di + break + else + n = n + 1 + end + end + end + else + for i=k+1,#d,1 do + local di = d[i] + if type(di) == "table" then + if n == 1 then + found = di + break + else + n = n - 1 + end + end + end + end + if found then + local ns, tg = found.rn or found.ns or "", found.tg + if ns ~= "" then + return ns .. ":" .. tg + else + return tg + end + else + return "" + end +end + +functions.tag = function(d,k,n) -- only tg + local found = false + n = n or 0 + if not k then + -- not found + elseif n == 0 then + local dk = d[k] + found = dk and (type(dk) == "table") and dk + elseif n < 0 then + for i=k-1,1,-1 do + local di = d[i] + if type(di) == "table" then + if n == -1 then + found = di + break + else + n = n + 1 + end + end + end + else + for i=k+1,#d,1 do + local di = d[i] + if type(di) == "table" then + if n == 1 then + found = di + break + else + n = n - 1 + end + end + end + end + return (found and found.tg) or "" +end + +expressions.text = functions.text +expressions.name = functions.name +expressions.tag = functions.tag + +local function traverse(root,pattern,handle,reverse,index,parent,wildcard) -- multiple only for tags, not for namespaces + if not root then -- error + return false + elseif pattern == false then -- root + handle(root,root.dt,root.ri) + return false + elseif pattern == true then -- wildcard + local rootdt = root.dt + if rootdt then + local start, stop, step = 1, #rootdt, 1 + if reverse then + start, stop, step = stop, start, -1 + end + for k=start,stop,step do + if handle(root,rootdt,root.ri or k) then return false end + if not traverse(rootdt[k],true,handle,reverse) then return false end + end + end + return false + elseif root.dt then + index = index or 1 + local action = pattern[index] + local command = action[1] + if command == 29 then -- fast case /oeps + local rootdt = root.dt + for k=1,#rootdt do + local e = rootdt[k] + local tg = e.tg + if e.tg then + local ns = e.rn or e.ns + local ns_a, tg_a = action[3], action[4] + local matched = (ns_a == "*" or ns == ns_a) and (tg_a == "*" or tg == tg_a) + if not action[2] then matched = not matched end + if matched then + if handle(root,rootdt,k) then return false end + end + end + end + elseif command == 11 then -- parent + local ep = root.__p__ or parent + if index < #pattern then + if not traverse(ep,pattern,handle,reverse,index+1,root) then return false end + elseif handle(root,rootdt,k) then + return false + end + else + if (command == 16 or command == 12) and index == 1 then -- initial + -- wildcard = true + wildcard = command == 16 -- ok? + index = index + 1 + action = pattern[index] + command = action and action[1] or 0 -- something is wrong + end + if command == 11 then -- parent + local ep = root.__p__ or parent + if index < #pattern then + if not traverse(ep,pattern,handle,reverse,index+1,root) then return false end + elseif handle(root,rootdt,k) then + return false + end + else + local rootdt = root.dt + local start, stop, step, n, dn = 1, #rootdt, 1, 0, 1 + if command == 30 then + if action[5] < 0 then + start, stop, step = stop, start, -1 + dn = -1 + end + elseif reverse and index == #pattern then + start, stop, step = stop, start, -1 + end + local idx = 0 + local hsh = { } -- this will slooow down the lot + for k=start,stop,step do -- we used to have functions for all but a case is faster + local e = rootdt[k] + local ns, tg = e.rn or e.ns, e.tg + if tg then + -- we can optimize this for simple searches, but it probably does not pay off + hsh[tg] = (hsh[tg] or 0) + 1 + idx = idx + 1 + if command == 30 then + local ns_a, tg_a = action[3], action[4] + if tg == tg_a then + matched = ns_a == "*" or ns == ns_a + elseif tg_a == '*' then + matched, multiple = ns_a == "*" or ns == ns_a, true + else + matched = false + end + if not action[2] then matched = not matched end + if matched then + n = n + dn + if n == action[5] then + if index == #pattern then + if handle(root,rootdt,root.ri or k) then return false end + else + if not traverse(e,pattern,handle,reverse,index+1,root) then return false end + end + break + end + elseif wildcard then + if not traverse(e,pattern,handle,reverse,index,root,true) then return false end + end + else + local matched, multiple = false, false + if command == 20 then -- match + local ns_a, tg_a = action[3], action[4] + if tg == tg_a then + matched = ns_a == "*" or ns == ns_a + elseif tg_a == '*' then + matched, multiple = ns_a == "*" or ns == ns_a, true + else + matched = false + end + if not action[2] then matched = not matched end + elseif command == 21 then -- match one of + multiple = true + for i=3,#action,2 do + local ns_a, tg_a = action[i], action[i+1] + if (ns_a == "*" or ns == ns_a) and (tg == "*" or tg == tg_a) then + matched = true + break + end + end + if not action[2] then matched = not matched end + elseif command == 22 then -- eq + local ns_a, tg_a = action[3], action[4] + if tg == tg_a then + matched = ns_a == "*" or ns == ns_a + elseif tg_a == '*' then + matched, multiple = ns_a == "*" or ns == ns_a, true + else + matched = false + end + matched = matched and e.at[action[6]] == action[7] + elseif command == 23 then -- ne + local ns_a, tg_a = action[3], action[4] + if tg == tg_a then + matched = ns_a == "*" or ns == ns_a + elseif tg_a == '*' then + matched, multiple = ns_a == "*" or ns == ns_a, true + else + matched = false + end + if not action[2] then matched = not matched end + matched = mached and e.at[action[6]] ~= action[7] + elseif command == 24 then -- one of eq + multiple = true + for i=3,#action-2,2 do + local ns_a, tg_a = action[i], action[i+1] + if (ns_a == "*" or ns == ns_a) and (tg == "*" or tg == tg_a) then + matched = true + break + end + end + if not action[2] then matched = not matched end + matched = matched and e.at[action[#action-1]] == action[#action] + elseif command == 25 then -- one of ne + multiple = true + for i=3,#action-2,2 do + local ns_a, tg_a = action[i], action[i+1] + if (ns_a == "*" or ns == ns_a) and (tg == "*" or tg == tg_a) then + matched = true + break + end + end + if not action[2] then matched = not matched end + matched = matched and e.at[action[#action-1]] ~= action[#action] + elseif command == 27 then -- has attribute + local ns_a, tg_a = action[3], action[4] + if tg == tg_a then + matched = ns_a == "*" or ns == ns_a + elseif tg_a == '*' then + matched, multiple = ns_a == "*" or ns == ns_a, true + else + matched = false + end + if not action[2] then matched = not matched end + matched = matched and e.at[action[5]] + elseif command == 28 then -- has value + local edt, ns_a, tg_a = e.dt, action[3], action[4] + if tg == tg_a then + matched = ns_a == "*" or ns == ns_a + elseif tg_a == '*' then + matched, multiple = ns_a == "*" or ns == ns_a, true + else + matched = false + end + if not action[2] then matched = not matched end + matched = matched and edt and edt[1] == action[5] + elseif command == 31 then + local edt, ns_a, tg_a = e.dt, action[3], action[4] + if tg == tg_a then + matched = ns_a == "*" or ns == ns_a + elseif tg_a == '*' then + matched, multiple = ns_a == "*" or ns == ns_a, true + else + matched = false + end + if not action[2] then matched = not matched end + if matched then + matched = action[6](expressions,root,rootdt,k,e,edt,ns,tg,idx,hsh[tg] or 1) + end + end + if matched then -- combine tg test and at test + if index == #pattern then + if handle(root,rootdt,root.ri or k) then return false end + if wildcard then + if multiple then + if not traverse(e,pattern,handle,reverse,index,root,true) then return false end + else + -- maybe or multiple; anyhow, check on (section|title) vs just section and title in example in lxml + if not traverse(e,pattern,handle,reverse,index,root) then return false end + end + end + else + if not traverse(e,pattern,handle,reverse,index+1,root) then return false end + end + elseif command == 14 then -- any + if index == #pattern then + if handle(root,rootdt,root.ri or k) then return false end + else + if not traverse(e,pattern,handle,reverse,index+1,root) then return false end + end + elseif command == 15 then -- many + if index == #pattern then + if handle(root,rootdt,root.ri or k) then return false end + else + if not traverse(e,pattern,handle,reverse,index+1,root,true) then return false end + end + -- not here : 11 + elseif command == 11 then -- parent + local ep = e.__p__ or parent + if index < #pattern then + if not traverse(ep,pattern,handle,reverse,root,index+1) then return false end + elseif handle(root,rootdt,k) then + return false + end + elseif command == 40 and e.special and tg == "@pi@" then -- pi + local pi = action[2] + if pi ~= "" then + local pt = e.dt[1] + if pt and pt:find(pi) then + if handle(root,rootdt,k) then + return false + end + end + elseif handle(root,rootdt,k) then + return false + end + elseif wildcard then + if not traverse(e,pattern,handle,reverse,index,root,true) then return false end + end + end + else + -- not here : 11 + if command == 11 then -- parent + local ep = e.__p__ or parent + if index < #pattern then + if not traverse(ep,pattern,handle,reverse,index+1,root) then return false end + elseif handle(root,rootdt,k) then + return false + end + break -- else loop + end + end + end + end + end + end + return true +end + +xml.traverse = traverse + +--[[ldx-- +Next come all kind of locators and manipulators. The most generic function here
+is
For splitting the filter function from the path specification, we can +use string matching or lpeg matching. Here the difference in speed is +neglectable but the lpeg variant is more robust.
+--ldx]]-- + +-- not faster but hipper ... although ... i can't get rid of the trailing / in the path + +local P, S, R, C, V, Cc = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc + +local slash = P('/') +local name = (R("az","AZ","--","__"))^1 +local path = C(((1-slash)^0 * slash)^1) +local argument = P { "(" * C(((1 - S("()")) + V(1))^0) * ")" } +local action = Cc(1) * path * C(name) * argument +local attribute = Cc(2) * path * P('@') * C(name) +local direct = Cc(3) * Cc("../*") * slash^0 * C(name) * argument + +local parser = direct + action + attribute + +local filters = xml.filters +local attribute_filter = xml.filters.attributes +local default_filter = xml.filters.default + +-- todo: also hash, could be gc'd + +function xml.filter(root,pattern) + local kind, a, b, c = parser:match(pattern) + if kind == 1 or kind == 3 then + return (filters[b] or default_filter)(root,a,c) + elseif kind == 2 then + return attribute_filter(root,a,b) + else + return default_filter(root,pattern) + end +end + +--~ slightly faster, but first we need a proper test file +--~ +--~ local hash = { } +--~ +--~ function xml.filter(root,pattern) +--~ local h = hash[pattern] +--~ if not h then +--~ local kind, a, b, c = parser:match(pattern) +--~ if kind == 1 then +--~ h = { kind, filters[b] or default_filter, a, b, c } +--~ elseif kind == 2 then +--~ h = { kind, attribute_filter, a, b, c } +--~ else +--~ h = { kind, default_filter, a, b, c } +--~ end +--~ hash[pattern] = h +--~ end +--~ local kind = h[1] +--~ if kind == 1 then +--~ return h[2](root,h[2],h[4]) +--~ elseif kind == 2 then +--~ return h[2](root,h[2],h[3]) +--~ else +--~ return h[2](root,pattern) +--~ end +--~ end + +--[[ldx-- +The following functions collect elements and texts.
+--ldx]]-- + +-- still somewhat bugged + +function xml.collect_elements(root, pattern, ignorespaces) + local rr, dd = { }, { } + traverse(root, lpath(pattern), function(r,d,k) + local dk = d and d[k] + if dk then + if ignorespaces and type(dk) == "string" and dk:find("[^%S]") then + -- ignore + else + local n = #rr+1 + rr[n], dd[n] = r, dk + end + end + end) + return dd, rr +end + +function xml.collect_texts(root, pattern, flatten) + local t = { } -- no r collector + traverse(root, lpath(pattern), function(r,d,k) + if d then + local ek = d[k] + local tx = ek and ek.dt + if flatten then + if tx then + t[#t+1] = xml.tostring(tx) or "" + else + t[#t+1] = "" + end + else + t[#t+1] = tx or "" + end + else + t[#t+1] = "" + end + end) + return t +end + +function xml.collect_tags(root, pattern, nonamespace) + local t = { } + xml.traverse(root, xml.lpath(pattern), function(r,d,k) + local dk = d and d[k] + if dk and type(dk) == "table" then + local ns, tg = e.ns, e.tg + if nonamespace then + t[#t+1] = tg -- if needed we can return an extra table + elseif ns == "" then + t[#t+1] = tg + else + t[#t+1] = ns .. ":" .. tg + end + end + end) + return #t > 0 and {} +end + +--[[ldx-- +Often using an iterators looks nicer in the code than passing handler
+functions. The
Which will print all the titles in the document. The iterator variant takes +1.5 times the runtime of the function variant which is due to the overhead in +creating the wrapper. So, instead of:
+ +We use the function variants in the filters.
+--ldx]]-- + +local wrap, yield = coroutine.wrap, coroutine.yield + +function xml.elements(root,pattern,reverse) + return wrap(function() traverse(root, lpath(pattern), yield, reverse) end) +end + +function xml.elements_only(root,pattern,reverse) + return wrap(function() traverse(root, lpath(pattern), function(r,d,k) yield(d[k]) end, reverse) end) +end + +function xml.each_element(root, pattern, handle, reverse) + local ok + traverse(root, lpath(pattern), function(r,d,k) ok = true handle(r,d,k) end, reverse) + return ok +end + +function xml.process_elements(root, pattern, handle) + traverse(root, lpath(pattern), function(r,d,k) + local dkdt = d[k].dt + if dkdt then + for i=1,#dkdt do + local v = dkdt[i] + if v.tg then handle(v) end + end + end + end) +end + +function xml.process_attributes(root, pattern, handle) + traverse(root, lpath(pattern), function(r,d,k) + local ek = d[k] + local a = ek.at or { } + handle(a) + if next(a) then -- next is faster than type (and >0 test) + ek.at = a + else + ek.at = nil + end + end) +end + +--[[ldx-- +We've now arrives at the functions that manipulate the tree.
+--ldx]]-- + +function xml.inject_element(root, pattern, element, prepend) + if root and element then + local matches, collect = { }, nil + if type(element) == "string" then + element = convert(element,true) + end + if element then + collect = function(r,d,k) matches[#matches+1] = { r, d, k, element } end + traverse(root, lpath(pattern), collect) + for i=1,#matches do + local m = matches[i] + local r, d, k, element, edt = m[1], m[2], m[3], m[4], nil + if element.ri then + element = element.dt[element.ri].dt + else + element = element.dt + end + if r.ri then + edt = r.dt[r.ri].dt + else + edt = d and d[k] and d[k].dt + end + if edt then + local be, af + if prepend then + be, af = xml.copy(element), edt + else + be, af = edt, xml.copy(element) + end + for i=1,#af do + be[#be+1] = af[i] + end + if r.ri then + r.dt[r.ri].dt = be + else + d[k].dt = be + end + else + -- r.dt = element.dt -- todo + end + end + end + end +end + +-- todo: copy ! + +function xml.insert_element(root, pattern, element, before) -- todo: element als functie + if root and element then + if pattern == "/" then + xml.inject_element(root, pattern, element, before) + else + local matches, collect = { }, nil + if type(element) == "string" then + element = convert(element,true) + end + if element and element.ri then + element = element.dt[element.ri] + end + if element then + collect = function(r,d,k) matches[#matches+1] = { r, d, k, element } end + traverse(root, lpath(pattern), collect) + for i=#matches,1,-1 do + local m = matches[i] + local r, d, k, element = m[1], m[2], m[3], m[4] + if not before then k = k + 1 end + if element.tg then + insert(d,k,element) -- untested +--~ elseif element.dt then +--~ for _,v in ipairs(element.dt) do -- i added +--~ insert(d,k,v) +--~ k = k + 1 +--~ end +--~ end + else + local edt = element.dt + if edt then + for i=1,#edt do + insert(d,k,edt[i]) + k = k + 1 + end + end + end + end + end + end + end +end + +xml.insert_element_after = xml.insert_element +xml.insert_element_before = function(r,p,e) xml.insert_element(r,p,e,true) end +xml.inject_element_after = xml.inject_element +xml.inject_element_before = function(r,p,e) xml.inject_element(r,p,e,true) end + +function xml.delete_element(root, pattern) + local matches, deleted = { }, { } + local collect = function(r,d,k) matches[#matches+1] = { r, d, k } end + traverse(root, lpath(pattern), collect) + for i=#matches,1,-1 do + local m = matches[i] + deleted[#deleted+1] = remove(m[2],m[3]) + end + return deleted +end + +function xml.replace_element(root, pattern, element) + if type(element) == "string" then + element = convert(element,true) + end + if element and element.ri then + element = element.dt[element.ri] + end + if element then + traverse(root, lpath(pattern), function(rm, d, k) + d[k] = element.dt -- maybe not clever enough + end) + end +end + +local function load_data(name) -- == io.loaddata + local f, data = io.open(name), "" + if f then + data = f:read("*all",'b') -- 'b' ? + f:close() + end + return data +end + +function xml.include(xmldata,pattern,attribute,recursive,loaddata) + -- parse="text" (default: xml), encoding="" (todo) + -- attribute = attribute or 'href' + pattern = pattern or 'include' + loaddata = loaddata or load_data + local function include(r,d,k) + local ek, name = d[k], nil + if not attribute or attribute == "" then + local ekdt = ek.dt + name = (type(ekdt) == "table" and ekdt[1]) or ekdt + end + if not name then + if ek.at then + for a in gmatch(attribute or "href","([^|]+)") do + name = ek.at[a] + if name then break end + end + end + end + local data = (name and name ~= "" and loaddata(name)) or "" + if data == "" then + xml.empty(d,k) + elseif ek.at["parse"] == "text" then -- for the moment hard coded + d[k] = xml.escaped(data) + else + local xi = xml.convert(data) + if not xi then + xml.empty(d,k) + else + if recursive then + xml.include(xi,pattern,attribute,recursive,loaddata) + end + xml.assign(d,k,xi) + end + end + end + xml.each_element(xmldata, pattern, include) +end + +function xml.strip_whitespace(root, pattern, nolines) -- strips all leading and trailing space ! + traverse(root, lpath(pattern), function(r,d,k) + local dkdt = d[k].dt + if dkdt then -- can be optimized + local t = { } + for i=1,#dkdt do + local str = dkdt[i] + if type(str) == "string" then + + if str == "" then + -- stripped + else + if nolines then + str = gsub(str,"[ \n\r\t]+"," ") + end + if str == "" then + -- stripped + else + t[#t+1] = str + end + end + else + t[#t+1] = str + end + end + d[k].dt = t + end + end) +end + +local function rename_space(root, oldspace, newspace) -- fast variant + local ndt = #root.dt + for i=1,ndt or 0 do + local e = root[i] + if type(e) == "table" then + if e.ns == oldspace then + e.ns = newspace + if e.rn then + e.rn = newspace + end + end + local edt = e.dt + if edt then + rename_space(edt, oldspace, newspace) + end + end + end +end + +xml.rename_space = rename_space + +function xml.remap_tag(root, pattern, newtg) + traverse(root, lpath(pattern), function(r,d,k) + d[k].tg = newtg + end) +end +function xml.remap_namespace(root, pattern, newns) + traverse(root, lpath(pattern), function(r,d,k) + d[k].ns = newns + end) +end +function xml.check_namespace(root, pattern, newns) + traverse(root, lpath(pattern), function(r,d,k) + local dk = d[k] + if (not dk.rn or dk.rn == "") and dk.ns == "" then + dk.rn = newns + end + end) +end +function xml.remap_name(root, pattern, newtg, newns, newrn) + traverse(root, lpath(pattern), function(r,d,k) + local dk = d[k] + dk.tg = newtg + dk.ns = newns + dk.rn = newrn + end) +end + +function xml.filters.found(root,pattern,check_content) + local found = false + traverse(root, lpath(pattern), function(r,d,k) + if check_content then + local dk = d and d[k] + found = dk and dk.dt and next(dk.dt) and true + else + found = true + end + return true + end) + return found +end + +--[[ldx-- +Here are a few synonyms.
+--ldx]]-- + +xml.filters.position = xml.filters.index + +xml.count = xml.filters.count +xml.index = xml.filters.index +xml.position = xml.filters.index +xml.first = xml.filters.first +xml.last = xml.filters.last +xml.found = xml.filters.found + +xml.each = xml.each_element +xml.process = xml.process_element +xml.strip = xml.strip_whitespace +xml.collect = xml.collect_elements +xml.all = xml.collect_elements + +xml.insert = xml.insert_element_after +xml.inject = xml.inject_element_after +xml.after = xml.insert_element_after +xml.before = xml.insert_element_before +xml.delete = xml.delete_element +xml.replace = xml.replace_element + +--[[ldx-- +The following helper functions best belong to the
The parser used here is inspired by the variant discussed in the lua book, but
+handles comment and processing instructions, has a different structure, provides
+parent access; a first version used different trickery but was less optimized to we
+went this route. First we had a find based parser, now we have an
Expecially the lpath code is experimental, we will support some of xpath, but
+only things that make sense for us; as compensation it is possible to hook in your
+own functions. Apart from preprocessing content for
Beware, the interface may change. For instance at, ns, tg, dt may get more +verbose names. Once the code is stable we will also remove some tracing and +optimize the code.
+--ldx]]-- + +xml = xml or { } + +--~ local xml = xml + +local concat, remove, insert = table.concat, table.remove, table.insert +local type, next, setmetatable = type, next, setmetatable +local format, lower, find = string.format, string.lower, string.find + +--[[ldx-- +This module can be used stand alone but also inside
First a hack to enable namespace resolving. A namespace is characterized by
+a
The next function associates a namespace prefix with an
The next function also registers a namespace, but this time we map a
+given namespace prefix onto a registered one, using the given
+
Next we provide a way to turn an
A namespace in an element can be remapped onto the registered
+one efficiently by using the
This version uses
Next comes the parser. The rather messy doctype definition comes in many
+disguises so it is no surprice that later on have to dedicate quite some
+
The code may look a bit complex but this is mostly due to the fact that we +resolve namespaces and attach metatables. There is only one public function:
+ +An optional second boolean argument tells this function not to create a root +element.
+--ldx]]-- + +xml.strip_cm_and_dt = false -- an extra global flag, in case we have many includes + +-- not just one big nested table capture (lpeg overflow) + +local nsremap, resolvens = xml.xmlns, xml.resolvens + +local stack, top, dt, at, xmlns, errorstr, entities = {}, {}, {}, {}, {}, nil, {} + +local mt = { __tostring = xml.text } + +function xml.check_error(top,toclose) + return "" +end + +local strip = false +local cleanup = false + +function xml.set_text_cleanup(fnc) + cleanup = fnc +end + +local function add_attribute(namespace,tag,value) + if cleanup and #value > 0 then + value = cleanup(value) -- new + end + if tag == "xmlns" then + xmlns[#xmlns+1] = resolvens(value) + at[tag] = value + elseif namespace == "xmlns" then + xml.checkns(tag,value) + at["xmlns:" .. tag] = value + else + at[tag] = value + end +end + +local function add_begin(spacing, namespace, tag) + if #spacing > 0 then + dt[#dt+1] = spacing + end + local resolved = (namespace == "" and xmlns[#xmlns]) or nsremap[namespace] or namespace + top = { ns=namespace or "", rn=resolved, tg=tag, at=at, dt={}, __p__ = stack[#stack] } + setmetatable(top, mt) + dt = top.dt + stack[#stack+1] = top + at = { } +end + +local function add_end(spacing, namespace, tag) + if #spacing > 0 then + dt[#dt+1] = spacing + end + local toclose = remove(stack) + top = stack[#stack] + if #stack < 1 then + errorstr = format("nothing to close with %s %s", tag, xml.check_error(top,toclose) or "") + elseif toclose.tg ~= tag then -- no namespace check + errorstr = format("unable to close %s with %s %s", toclose.tg, tag, xml.check_error(top,toclose) or "") + end + dt = top.dt + dt[#dt+1] = toclose + dt[0] = top + if toclose.at.xmlns then + remove(xmlns) + end +end + +local function add_empty(spacing, namespace, tag) + if #spacing > 0 then + dt[#dt+1] = spacing + end + local resolved = (namespace == "" and xmlns[#xmlns]) or nsremap[namespace] or namespace + top = stack[#stack] + dt = top.dt + local t = { ns=namespace or "", rn=resolved, tg=tag, at=at, dt={}, __p__ = top } + dt[#dt+1] = t + setmetatable(t, mt) + if at.xmlns then + remove(xmlns) + end + at = { } +end + +local function add_text(text) + if cleanup and #text > 0 then + dt[#dt+1] = cleanup(text) + else + dt[#dt+1] = text + end +end + +local function add_special(what, spacing, text) + if #spacing > 0 then + dt[#dt+1] = spacing + end + if strip and (what == "@cm@" or what == "@dt@") then + -- forget it + else + dt[#dt+1] = { special=true, ns="", tg=what, dt={text} } + end +end + +local function set_message(txt) + errorstr = "garbage at the end of the file: " .. gsub(txt,"([ \n\r\t]*)","") +end + +local P, S, R, C, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V + +local space = S(' \r\n\t') +local open = P('<') +local close = P('>') +local squote = S("'") +local dquote = S('"') +local equal = P('=') +local slash = P('/') +local colon = P(':') +local valid = R('az', 'AZ', '09') + S('_-.') +local name_yes = C(valid^1) * colon * C(valid^1) +local name_nop = C(P(true)) * C(valid^1) +local name = name_yes + name_nop + +local utfbom = P('\000\000\254\255') + P('\255\254\000\000') + + P('\255\254') + P('\254\255') + P('\239\187\191') -- no capture + +local spacing = C(space^0) +local justtext = C((1-open)^1) +local somespace = space^1 +local optionalspace = space^0 + +local value = (squote * C((1 - squote)^0) * squote) + (dquote * C((1 - dquote)^0) * dquote) +local attribute = (somespace * name * optionalspace * equal * optionalspace * value) / add_attribute +local attributes = attribute^0 + +local text = justtext / add_text +local balanced = P { "[" * ((1 - S"[]") + V(1))^0 * "]" } -- taken from lpeg manual, () example + +local emptyelement = (spacing * open * name * attributes * optionalspace * slash * close) / add_empty +local beginelement = (spacing * open * name * attributes * optionalspace * close) / add_begin +local endelement = (spacing * open * slash * name * optionalspace * close) / add_end + +local begincomment = open * P("!--") +local endcomment = P("--") * close +local begininstruction = open * P("?") +local endinstruction = P("?") * close +local begincdata = open * P("![CDATA[") +local endcdata = P("]]") * close + +local someinstruction = C((1 - endinstruction)^0) +local somecomment = C((1 - endcomment )^0) +local somecdata = C((1 - endcdata )^0) + +local function entity(k,v) entities[k] = v end + +local begindoctype = open * P("!DOCTYPE") +local enddoctype = close +local beginset = P("[") +local endset = P("]") +local doctypename = C((1-somespace)^0) +local elementdoctype = optionalspace * P("Packaging data in an xml like table is done with the following +function. Maybe it will go away (when not used). +--ldx]]-- + +function xml.is_valid(root) + return root and root.dt and root.dt[1] and type(root.dt[1]) == "table" and not root.dt[1].er +end + +function xml.package(tag,attributes,data) + local ns, tg = tag:match("^(.-):?([^:]+)$") + local t = { ns = ns, tg = tg, dt = data or "", at = attributes or {} } + setmetatable(t, mt) + return t +end + +function xml.is_valid(root) + return root and not root.error +end + +xml.error_handler = (logs and logs.report) or (input and logs.report) or print + +--[[ldx-- +We cannot load an
When we inject new elements, we need to convert strings to +valid trees, which is what the next function does.
+--ldx]]-- + +function xml.toxml(data) + if type(data) == "string" then + local root = { xml.convert(data,true) } + return (#root > 1 and root) or root[1] + else + return data + end +end + +--[[ldx-- +For copying a tree we use a dedicated function instead of the +generic table copier. Since we know what we're dealing with we +can speed up things a bit. The second argument is not to be used!
+--ldx]]-- + +function copy(old,tables) + if old then + tables = tables or { } + local new = { } + if not tables[old] then + tables[old] = new + end + for k,v in pairs(old) do + new[k] = (type(v) == "table" and (tables[v] or copy(v, tables))) or v + end + local mt = getmetatable(old) + if mt then + setmetatable(new,mt) + end + return new + else + return { } + end +end + +xml.copy = copy + +--[[ldx-- +In
At the cost of some 25% runtime overhead you can first convert the tree to a string +and then handle the lot.
+--ldx]]-- + +function xml.tostring(root) -- 25% overhead due to collecting + if root then + if type(root) == 'string' then + return root + elseif next(root) then -- next is faster than type (and >0 test) + local result = { } + serialize(root,function(s) result[#result+1] = s end) + return concat(result,"") + end + end + return "" +end + +--[[ldx-- +The next function operated on the content only and needs a handle function +that accepts a string.
+--ldx]]-- + +function xml.string(e,handle) + if not handle or (e.special and e.tg ~= "@rt@") then + -- nothing + elseif e.tg then + local edt = e.dt + if edt then + for i=1,#edt do + xml.string(edt[i],handle) + end + end + else + handle(e) + end +end + +--[[ldx-- +How you deal with saving data depends on your preferences. For a 40 MB database +file the timing on a 2.3 Core Duo are as follows (time in seconds):
+ +The save function is given below.
+--ldx]]-- + +function xml.save(root,name) + local f = io.open(name,"w") + if f then + xml.serialize(root,function(s) f:write(s) end) + f:close() + end +end + +--[[ldx-- +A few helpers:
+--ldx]]-- + +function xml.body(root) + return (root.ri and root.dt[root.ri]) or root +end + +function xml.text(root) + return (root and xml.tostring(root)) or "" +end + +function xml.content(root) -- bugged + return (root and root.dt and xml.tostring(root.dt)) or "" +end + +function xml.isempty(root, pattern) + if pattern == "" or pattern == "*" then + pattern = nil + end + if pattern then + -- todo + return false + else + return not root or not root.dt or #root.dt == 0 or root.dt == "" + end +end + +--[[ldx-- +The next helper erases an element but keeps the table as it is, +and since empty strings are not serialized (effectively) it does +not harm. Copying the table would take more time. Usage:
+ +The next helper assigns a tree (or string). Usage:
+ +Math definitions. This code may move.
---ldx]]-- - -- if needed we can use the info here to set up xetex definition files -- the "8000 hackery influences direct characters (utf) as indirect \char's +local utf = unicode.utf8 + local texsprint, format, utfchar, utfbyte = tex.sprint, string.format, utf.char, utf.byte -mathematics = mathematics or { } -mathematics.data = mathematics.data or { } -mathematics.slots = mathematics.slots or { } +local trace_defining = false trackers.register("math.defining", function(v) trace_defining = v end) -mathematics.classes = { - ord = 0, -- mathordcomm mathord - op = 1, -- mathopcomm mathop - bin = 2, -- mathbincomm mathbin - rel = 3, -- mathrelcomm mathrel - open = 4, -- mathopencomm mathopen - close = 5, -- mathclosecomm mathclose - punct = 6, -- mathpunctcomm mathpunct - alpha = 7, -- mathalphacomm firstofoneargument - accent = 8, - radical = 9, - inner = 0, -- mathinnercomm mathinner - nothing = 0, -- mathnothingcomm firstofoneargument - choice = 0, -- mathchoicecomm @@mathchoicecomm - box = 0, -- mathboxcomm @@mathboxcomm - limop = 1, -- mathlimopcomm @@mathlimopcomm - nolop = 1, -- mathnolopcomm @@mathnolopcomm -} +mathematics = mathematics or { } -mathematics.classes.alphabetic = mathematics.classes.alpha -mathematics.classes.unknown = mathematics.classes.nothing -mathematics.classes.punctuation = mathematics.classes.punct -mathematics.classes.normal = mathematics.classes.nothing -mathematics.classes.opening = mathematics.classes.open -mathematics.classes.closing = mathematics.classes.close -mathematics.classes.binary = mathematics.classes.bin -mathematics.classes.relation = mathematics.classes.rel -mathematics.classes.fence = mathematics.classes.unknown -mathematics.classes.diacritic = mathematics.classes.accent -mathematics.classes.large = mathematics.classes.op -mathematics.classes.variable = mathematics.classes.alphabetic -mathematics.classes.number = mathematics.classes.nothing +mathematics.extrabase = 0xFE000 -- here we push some virtuals +mathematics.privatebase = 0xFF000 -- here we push the ex -mathematics.families = { - mr = 0, bs = 8, - mi = 1, bi = 9, - sy = 2, sc = 10, - ex = 3, tf = 11, - it = 4, ma = 12, - sl = 5, mb = 13, - bf = 6, mc = 14, - nn = 7, md = 15, +local families = { + tf = 0, it = 1, sl = 2, bf = 3, bi = 4, bs = 5, -- virtual fonts or unicode otf } -mathematics.families.letters = mathematics.families.mr -mathematics.families.numbers = mathematics.families.mr -mathematics.families.variables = mathematics.families.mi -mathematics.families.operators = mathematics.families.sy -mathematics.families.lcgreek = mathematics.families.mi -mathematics.families.ucgreek = mathematics.families.mr -mathematics.families.vargreek = mathematics.families.mi -mathematics.families.mitfamily = mathematics.families.mi -mathematics.families.calfamily = mathematics.families.sy - -mathematics.families[0] = mathematics.families.mr -mathematics.families[1] = mathematics.families.mi -mathematics.families[2] = mathematics.families.sy -mathematics.families[3] = mathematics.families.ex +local classes = { + ord = 0, -- mathordcomm mathord + op = 1, -- mathopcomm mathop + bin = 2, -- mathbincomm mathbin + rel = 3, -- mathrelcomm mathrel + open = 4, -- mathopencomm mathopen + close = 5, -- mathclosecomm mathclose + punct = 6, -- mathpunctcomm mathpunct + alpha = 7, -- mathalphacomm firstofoneargument + accent = 8, -- class 0 + radical = 9, + xaccent = 10, -- class 3 + topaccent = 11, -- class 0 + botaccent = 12, -- class 0 + under = 13, + over = 14, + delimiter = 15, + inner = 0, -- mathinnercomm mathinner + nothing = 0, -- mathnothingcomm firstofoneargument + choice = 0, -- mathchoicecomm @@mathchoicecomm + box = 0, -- mathboxcomm @@mathboxcomm + limop = 1, -- mathlimopcomm @@mathlimopcomm + nolop = 1, -- mathnolopcomm @@mathnolopcomm +} -function mathematics.mathcode(target,class,family,slot) - if class <= 7 then - return ("\\omathcode%s=\"%X%02X%04X "):format(target,class,family,slot) - end +mathematics.families = families +mathematics.classes = classes + +classes.alphabetic = classes.alpha +classes.unknown = classes.nothing +classes.default = classes.nothing +classes.punctuation = classes.punct +classes.normal = classes.nothing +classes.opening = classes.open +classes.closing = classes.close +classes.binary = classes.bin +classes.relation = classes.rel +classes.fence = classes.unknown +classes.diacritic = classes.accent +classes.large = classes.op +classes.variable = classes.alphabetic +classes.number = classes.alphabetic + +-- there will be proper functions soon (and we will move this code in-line) + +local function delcode(target,family,slot) + return format('\\Udelcode%s="%X "%X ',target,family,slot) +end +local function mathchar(class,family,slot) + return format('\\Umathchar "%X "%X "%X ',class,family,slot) +end +local function mathaccent(class,family,slot) + return format('\\Umathaccent "%X "%X "%X ',0,family,slot) -- no class +end +local function delimiter(class,family,slot) + return format('\\Udelimiter "%X "%X "%X ',class,family,slot) end -function mathematics.delcode(target,small_family,small_slot,large_family,large_slot) - return ("\\odelcode%s=\"%02X%04X\"%02X%04X "):format(target,small_family,small_slot,large_family,large_slot) +local function radical(family,slot) + return format('\\Uradical "%X "%X ',family,slot) end -function mathematics.radical(small_family,small_slot,large_family,large_slot) - return ("\\radical%s=\"%02X%04X%\"02X%04X "):format(target,small_family,small_slot,large_family,large_slot) +local function mathchardef(name,class,family,slot) + return format('\\Umathchardef\\%s "%X "%X "%X ',name,class,family,slot) end -function mathematics.mathchar(class,family,slot) - return ("\\omathchar\"%X%02X%04X "):format(class,family,slot) +local function mathcode(target,class,family,slot) + return format('\\Umathcode%s="%X "%X "%X ',target,class,family,slot) end -function mathematics.mathaccent(class,family,slot) - return ("\\omathaccent\"%X%02X%04X "):format(class,family,slot) +local function mathtopaccent(class,family,slot) + return format('\\Umathaccent "%X "%X "%X ',0,family,slot) -- no class end -function mathematics.delimiter(class,family,slot,largefamily,largeslot) - return ("\\odelimiter\"%X%02X%04X\"%02X%04X "):format(class,family,slot,largefamily,largeslot) +local function mathbotaccent(class,family,slot) + return format('\\Umathbotaccent "%X "%X "%X ',0,family,slot) -- no class end -function mathematics.mathchardef(name,class,family,slot) -- we can avoid this one - return ("\\omathchardef\\%s\"%X%02X%04X "):format(name,class,family,slot) +local function mathtopdelimiter(class,family,slot) + return format('\\Uoverdelimiter "%X "%X ',0,family,slot) -- no class +end +local function mathbotdelimiter(class,family,slot) + return format('\\Uunderdelimiter "%X "%X ',0,family,slot) -- no class end -function mathematics.setmathsymbol(name,class,family,slot,largefamily,largeslot,unicode) - class = mathematics.classes[class] or class -- no real checks needed - family = mathematics.families[family] or family - -- \unexpanded ? \relax needed for the codes? - local classes = mathematics.classes - if largefamily and largeslot then - largefamily = mathematics.families[largefamily] or largefamily - if class == classes.radical then - texsprint(("\\unexpanded\\xdef\\%s{%s }"):format(name,mathematics.radical(class,family,slot,largefamily,largeslot))) - elseif class == classes.open or class == classes.close then - texsprint(("\\unexpanded\\xdef\\%s{%s}"):format(name,mathematics.delimiter(class,family,slot,largefamily,largeslot))) - end - elseif class == classes.accent then - texsprint(("\\unexpanded\\xdef\\%s{%s }"):format(name,mathematics.mathaccent(class,family,slot))) - elseif unicode then - -- beware, open/close and other specials should not end up here - local ch = utfchar(unicode) - if characters.filters.utf.private.escapes[ch] then - texsprint(("\\xdef\\%s{\\char%s }"):format(name,unicode)) - else - texsprint(("\\xdef\\%s{%s}"):format(name,ch)) - end +local escapes = characters.filters.utf.private.escapes + +local function setmathsymbol(name,class,family,slot) + if class == classes.accent then + texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,mathaccent(class,family,slot))) + elseif class == classes.topaccent then + texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,mathtopaccent(class,family,slot))) + elseif class == classes.botaccent then + texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,mathbotaccent(class,family,slot))) + elseif class == classes.over then + texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,mathtopdelimiter(class,family,slot))) + elseif class == classes.under then + texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,mathbotdelimiter(class,family,slot))) + elseif class == classes.open or class == classes.close then + texsprint(delcode(slot,family,slot)) + texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,delimiter(class,family,slot))) + elseif class == classes.delimiter then + texsprint(delcode(slot,family,slot)) + texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,delimiter(0,family,slot))) + elseif class == classes.radical then + texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,radical(family,slot))) else - texsprint(mathematics.mathchardef(name,class,family,slot)) + -- beware, open/close and other specials should not end up here +--~ local ch = utfchar(slot) +--~ if escapes[ch] then +--~ texsprint(format("\\xdef\\%s{\\char%s }",name,slot)) +--~ else + texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,mathchar(class,family,slot))) +--~ end end end --- direct sub call - -function mathematics.setmathcharacter(target,class,family,slot,largefamily,largeslot) - class = mathematics.classes[class] or class -- no real checks needed - family = mathematics.families[family] or family - if largefamily and largeslot then - largefamily = mathematics.families[largefamily] or largefamily - texsprint(mathematics.delcode(target,family,slot,largefamily,largeslot)) - else - texsprint(mathematics.mathcode(target,class,family,slot)) +local function setmathcharacter(class,family,slot,unicode,firsttime) + if not firsttime and class <= 7 then + texsprint(mathcode(slot,class,family,unicode or slot)) end end --- definitions (todo: expand commands to utf instead of codes) - -mathematics.trace = false -- false +local function setmathsynonym(class,family,slot,unicode,firsttime) + if not firsttime and class <= 7 then + texsprint(mathcode(slot,class,family,unicode)) + end + if class == classes.open or class == classes.close then + texsprint(delcode(slot,family,unicode)) + end +end -function mathematics.define(slots) - local slots = slots or mathematics.slots.current - local setmathcharacter = mathematics.setmathcharacter - local setmathsymbol = mathematics.setmathsymbol - local trace = mathematics.trace - local function report(k,m,c,f,i,fe,ie) - local mc = mathematics.classes[m] or m - if fe then - logs.report("mathematics","a - %s:%s 0x%05X -> %s -> %s %s (%s %s) -> %s",mc,m,k,c,f,i,fe,ie,utfchar(k)) - elseif c then - logs.report("mathematics","b - %s:%s 0x%05X -> %s -> %s %s -> %s",mc,m,k,c,f,i,utfchar(k)) - else - logs.report("mathematics","c - %s:%s 0x%05X -> %s %s -> %s",mc,m,k,f,i,utfchar(k)) - end +local function report(class,family,unicode,name) + local nametype = type(name) + if nametype == "string" then + logs.report("mathematics","%s:%s %s U+%05X (%s) => %s",classname,class,family,unicode,utfchar(unicode),name) + elseif nametype == "number" then + logs.report("mathematics","%s:%s %s U+%05X (%s) => U+%05X",classname,class,family,unicode,utfchar(unicode),name) + else + logs.report("mathematics","%s:%s %s U+%05X (%s)", classname,class,family,unicode,utfchar(unicode)) end - for k,v in pairs(characters.data) do - local m = v.mathclass - -- i need to clean this up a bit - if m then - local c = v.mathname - if c == false then - -- no command - local s = slots[k] - if s then - local f, i, fe, ie = s[1], s[2], s[3], s[4] - if trace then - report(k,m,c,f,i,fe,ie) - end - setmathcharacter(k,m,f,i,fe,ie) +end + +-- there will be a combined \(math)chardef + +function mathematics.define(slots,family) + family = family or 0 + family = families[family] or family + local data = characters.data + for unicode, character in next, data do + local symbol = character.mathsymbol + if symbol then + local other = data[symbol] + local class = other.mathclass + if class then + class = classes[class] or class -- no real checks needed + if trace_defining then + report(class,family,unicode,symbol) end - elseif c then - local s = slots[k] - if s then - local f, i, fe, ie = s[1], s[2], s[3], s[4] - if trace then - report(k,m,c,f,i,fe,ie) + setmathsynonym(class,family,unicode,symbol) + end + local spec = other.mathspec + if spec then + for i, m in next, spec do + local class = m.class + if class then + class = classes[class] or class -- no real checks needed + setmathsynonym(class,family,unicode,symbol,i) end - setmathsymbol(c,m,f,i,fe,ie,k) - setmathcharacter(k,m,f,i,fe,ie) end - elseif v.contextname then - local s = slots[k] - local c = v.contextname - if s then - local f, i, fe, ie = s[1], s[2], s[3], s[4] - if trace then - report(k,m,c,f,i,fe,ie) + end + end + local mathclass = character.mathclass + local mathspec = character.mathspec + if mathspec then + for i, m in next, mathspec do + local name = m.name + local class = m.class + if not class then + class = mathclass + elseif not mathclass then + mathclass = class + end + if class then + class = classes[class] or class -- no real checks needed + if name then + if trace_defining then + report(class,family,unicode,name) + end + setmathsymbol(name,class,family,unicode) + -- setmathcharacter(class,family,unicode,unicode,i) + else + name = class == classes.variable or class == classes.number and character.adobename + if name then + if trace_defining then + report(class,family,unicode,name) + end + -- setmathcharacter(class,family,unicode,unicode,i) + end end - -- todo: mathortext - setmathsymbol(c,m,f,i,fe,ie,k) - setmathcharacter(k,m,f,i,fe,ie) + setmathcharacter(class,family,unicode,unicode,i) end + end + end + if mathclass then + local name = character.mathname + local class = classes[mathclass] or mathclass -- no real checks needed + if name == false then + if trace_defining then + report(class,family,unicode,name) + end + setmathcharacter(class,family,unicode) else - local a = v.adobename - if a and m then - local s, f, i, fe, ie = slots[k], nil, nil, nil, nil - if s then - f, i, fe, ie = s[1], s[2], s[3], s[4] - elseif m == "variable" then - f, i = mathematics.families.variables, k - elseif m == "number" then - f, i = mathematics.families.numbers, k + name = name or character.contextname + if name then + if trace_defining then + report(class,family,unicode,name) end - if f and i then - if trace then - report(k,m,a,f,i,fe,ie) - end - setmathcharacter(k,m,f,i,fe,ie) + setmathsymbol(name,class,family,unicode) + else + if trace_defining then + report(class,family,unicode,character.adobename) end end + setmathcharacter(class,family,unicode,unicode) end end end end --- temporary here: will become separate - --- maybe we should define a nice virtual font so that we have --- just the base n families repeated for different styles - -mathematics.slots.traditional = { - - [0x03B1] = { "lcgreek", 0x0B }, -- alpha - [0x03B2] = { "lcgreek", 0x0C }, -- beta - [0x03B3] = { "lcgreek", 0x0D }, -- gamma - [0x03B4] = { "lcgreek", 0x0E }, -- delta - [0x03B5] = { "lcgreek", 0x0F }, -- epsilon - [0x03B6] = { "lcgreek", 0x10 }, -- zeta - [0x03B7] = { "lcgreek", 0x11 }, -- eta - [0x03B8] = { "lcgreek", 0x12 }, -- theta - [0x03B9] = { "lcgreek", 0x13 }, -- iota - [0x03BA] = { "lcgreek", 0x14 }, -- kappa - [0x03BB] = { "lcgreek", 0x15 }, -- lambda - [0x03BC] = { "lcgreek", 0x16 }, -- mu - [0x03BD] = { "lcgreek", 0x17 }, -- nu - [0x03BE] = { "lcgreek", 0x18 }, -- xi - [0x03BF] = { "lcgreek", 0x6F }, -- omicron - [0x03C0] = { "lcgreek", 0x19 }, -- pi - [0x03C1] = { "lcgreek", 0x1A }, -- rho --- [0x03C2] = { "lcgreek", 0x00 }, -- varsigma - [0x03C3] = { "lcgreek", 0x1B }, -- sigma - [0x03C4] = { "lcgreek", 0x1C }, -- tau - [0x03C5] = { "lcgreek", 0x1D }, -- upsilon --- [0x03C6] = { "lcgreek", 0x1E }, -- varphi - [0x03C7] = { "lcgreek", 0x1F }, -- chi - [0x03C8] = { "lcgreek", 0x20 }, -- psi - [0x03C9] = { "lcgreek", 0x21 }, -- omega - - [0x0391] = { "ucgreek", 0x41 }, -- Alpha - [0x0392] = { "ucgreek", 0x42 }, -- Beta - [0x0393] = { "ucgreek", 0x00 }, -- Gamma - [0x0394] = { "ucgreek", 0x01 }, -- Delta - [0x0395] = { "ucgreek", 0x45 }, -- Epsilon - [0x0396] = { "ucgreek", 0x5A }, -- Zeta - [0x0397] = { "ucgreek", 0x48 }, -- Eta - [0x0398] = { "ucgreek", 0x02 }, -- Theta - [0x0399] = { "ucgreek", 0x49 }, -- Iota - [0x039A] = { "ucgreek", 0x4B }, -- Kappa - [0x039B] = { "ucgreek", 0x03 }, -- Lambda - [0x039C] = { "ucgreek", 0x4D }, -- Mu - [0x039D] = { "ucgreek", 0x4E }, -- Nu - [0x039E] = { "ucgreek", 0x04 }, -- Xi - [0x039F] = { "ucgreek", 0x4F }, -- Omicron - [0x03A0] = { "ucgreek", 0x05 }, -- Pi - [0x03A1] = { "ucgreek", 0x52 }, -- Rho - [0x03A3] = { "ucgreek", 0x06 }, -- Sigma - [0x03A4] = { "ucgreek", 0x54 }, -- Tau - [0x03A5] = { "ucgreek", 0x07 }, -- Upsilon - [0x03A6] = { "ucgreek", 0x08 }, -- Phi - [0x03A7] = { "ucgreek", 0x58 }, -- Chi - [0x03A8] = { "ucgreek", 0x09 }, -- Psi - [0x03A9] = { "ucgreek", 0x0A }, -- Omega - - [0x03F5] = { "vargreek", 0x22 }, -- varepsilon - [0x03D1] = { "vargreek", 0x23 }, -- vartheta - [0x03D6] = { "vargreek", 0x24 }, -- varpi - [0x03F1] = { "vargreek", 0x25 }, -- varrho - [0x03C2] = { "vargreek", 0x26 }, -- varsigma - - -- varphi is part of the alphabet, contrary to the other var*s' - - [0x03C6] = { "vargreek", 0x27 }, -- varphi - [0x03D5] = { "lcgreek", 0x1E }, -- phi - - [0x03F0] = { "lcgreek", 0x14 }, -- varkappa, not in tex fonts - - [0x0021] = { "mr", 0x21 }, -- ! - [0x0028] = { "mr", 0x28 }, -- ( - [0x0029] = { "mr", 0x29 }, -- ) - [0x002A] = { "sy", 0x03 }, -- * - [0x002B] = { "mr", 0x2B }, -- + - [0x002C] = { "mi", 0x3B }, -- , - [0x002D] = { "sy", 0x00 }, -- - - [0x2212] = { "sy", 0x00 }, -- - - [0x002E] = { "mi", 0x3A }, -- . - [0x002F] = { "mi", 0x3D }, -- / - [0x003A] = { "mr", 0x3A }, -- : - [0x003B] = { "mr", 0x3B }, -- ; - [0x003C] = { "mi", 0x3C }, -- < - [0x003D] = { "mr", 0x3D }, -- = - [0x003E] = { "mi", 0x3E }, -- > - [0x003F] = { "mr", 0x3F }, -- ? - [0x005C] = { "sy", 0x6E }, -- \ - [0x007B] = { "sy", 0x66 }, -- { - [0x007C] = { "sy", 0x6A }, -- | - [0x007D] = { "sy", 0x67 }, -- } - [0x00AC] = { "sy", 0x3A }, -- lnot - [0x00B1] = { "sy", 0x06 }, -- pm - [0x00B7] = { "sy", 0x01 }, -- cdot - [0x00D7] = { "sy", 0x02 }, -- times - [0x00F7] = { "sy", 0x04 }, -- div - [0x2022] = { "sy", 0x0F }, -- bullet - [0x2111] = { "sy", 0x3D }, -- Im - [0x2118] = { "mi", 0x7D }, -- wp - [0x211C] = { "sy", 0x3C }, -- Re - [0x2190] = { "sy", 0x20 }, -- leftarrow - [0x2191] = { "sy", 0x22, "ex", 0x78 }, -- uparrow - [0x2192] = { "sy", 0x21 }, -- rightarrow - [0x2193] = { "sy", 0x23, "ex", 0x79 }, -- downarrow - [0x2194] = { "sy", 0x24 }, -- leftrightarrow - [0x2195] = { "sy", 0x6C, "ex", 0x3F }, -- updownarrow - [0x2196] = { "sy", 0x2D }, -- nwarrow - [0x2197] = { "sy", 0x25 }, -- nearrow - [0x2198] = { "sy", 0x2E }, -- swarrow - [0x2199] = { "sy", 0x26 }, -- searrow - [0x21D0] = { "sy", 0x28 }, -- Leftarrow - [0x21D1] = { "sy", 0x6C, "ex", 0x7E }, -- Uparrow - [0x21D2] = { "sy", 0x29 }, -- Rightarrow - [0x21D3] = { "sy", 0x2B, "ex", 0x7F }, -- Downarrow - [0x21D4] = { "sy", 0x2C }, -- Leftrightarrow - [0x21D5] = { "sy", 0x6D, "ex", 0x77 }, -- Updownarrow - [0x2135] = { "sy", 0x40 }, -- aleph - [0x2113] = { "mi", 0x60 }, -- ell --- ... - [0x2200] = { "sy", 0x38 }, -- forall --- [0x2201] = { "sy", 0x00 }, -- complement - [0x2202] = { "mi", 0x40 }, -- partial - [0x2203] = { "sy", 0x39 }, -- exists --- [0x2204] = { "sy", 0x00 }, -- not exists - [0x2205] = { "sy", 0x3B }, -- empty set --- [0x2206] = { "sy", 0x00 }, -- increment - [0x2207] = { "sy", 0x72 }, -- nabla - [0x2208] = { "sy", 0x32 }, -- in - [0x2209] = { "sy", 0x33 }, -- ni - [0x220F] = { "ex", 0x51 }, -- prod - [0x2210] = { "ex", 0x60 }, -- coprod - [0x2211] = { "ex", 0x50 }, -- sum --- [0x2212] = { "sy", 0x00 }, -- - - [0x2213] = { "sy", 0x07 }, -- mp - [0x2215] = { "sy", 0x3D }, -- / AM: Not sure - [0x2216] = { "sy", 0x6E }, -- setminus - [0x2217] = { "sy", 0x03 }, -- * - [0x2218] = { "sy", 0x0E }, -- circ - [0x2219] = { "sy", 0x0F }, -- bullet --- [0x221A] = { "sy", 0x70, "ex", 0x70 }, -- sqrt. AM: Check surd?? --- ... - [0x221D] = { "sy", 0x2F }, -- propto - [0x221E] = { "sy", 0x31 }, -- infty - [0x2225] = { "sy", 0x6B }, -- parallel - [0x2227] = { "sy", 0x5E }, -- wedge - [0x2228] = { "sy", 0x5F }, -- vee - [0x2229] = { "sy", 0x5C }, -- cap - [0x222A] = { "sy", 0x5B }, -- cup - [0x222B] = { "ex", 0x52 }, -- intop --- ... other integrals - [0x2236] = { "mr", 0x3A }, -- colon - [0x223C] = { "sy", 0x18 }, -- sim - [0x2243] = { "sy", 0x27 }, -- simeq - [0x2248] = { "sy", 0x19 }, -- approx - [0x225C] = { "ma", 0x2C }, -- triangleq - [0x2261] = { "sy", 0x11 }, -- equiv - [0x2264] = { "sy", 0x14 }, -- leq - [0x2265] = { "sy", 0x15 }, -- geq - [0x226A] = { "sy", 0x1C }, -- ll - [0x226B] = { "sy", 0x1D }, -- gg - [0x227A] = { "sy", 0x1E }, -- prec - [0x227B] = { "sy", 0x1F }, -- succ --- [0x227C] = { "sy", 0x16 }, -- preceq, AM:No see 2AAF --- [0x227D] = { "sy", 0x17 }, -- succeq, AM:No see 2AB0 - [0x2282] = { "sy", 0x1A }, -- subset - [0x2283] = { "sy", 0x1B }, -- supset - [0x2286] = { "sy", 0x12 }, -- subseteq - [0x2287] = { "sy", 0x13 }, -- supseteq - [0x2293] = { "sy", 0x75 }, -- sqcap - [0x2294] = { "sy", 0x74 }, -- sqcup - [0x2295] = { "sy", 0x08 }, -- oplus - [0x2296] = { "sy", 0x09 }, -- ominus - [0x2297] = { "sy", 0x0A }, -- otimes - [0x2298] = { "sy", 0x0B }, -- oslash - [0x2299] = { "sy", 0x0C }, -- odot - [0x22A4] = { "sy", 0x3E }, -- top - [0x22A5] = { "sy", 0x3F }, -- bop - [0x22C0] = { "ex", 0x56 }, -- bigwedge - [0x22C1] = { "ex", 0x57 }, -- bigvee - [0x22C2] = { "ex", 0x54 }, -- bigcap - [0x22C3] = { "ex", 0x53 }, -- bigcup - [0x22C4] = { "sy", 0x05 }, -- diamond - [0x22C5] = { "sy", 0x01 }, -- cdot - [0x22C6] = { "mi", 0x3F }, -- star - [0x25B3] = { "sy", 0x34 }, -- triangle up - - [0x2220] = { "ma", 0x5C }, -- angle - [0x2221] = { "ma", 0x5D }, -- measuredangle - [0x2222] = { "ma", 0x5E }, -- sphericalangle - - [0x2245] = { "ma", 0x75 }, -- aproxeq - - [0x1D6A4] = { "mi", 0x7B }, -- imath - [0x1D6A5] = { "mi", 0x7C }, -- jmath - - [0x0028] = { "mr", 0x28, "ex", 0x00 }, -- ( - [0x0029] = { "mr", 0x29, "ex", 0x01 }, -- ) - [0x002F] = { "mr", 0x2F, "ex", 0x0E }, -- / - [0x003C] = { "sy", 0x3C, "ex", 0x0A }, -- < - [0x003E] = { "sy", 0x3E, "ex", 0x0B }, -- > - [0x005B] = { "mr", 0x5B, "ex", 0x02 }, -- [ - [0x005D] = { "mr", 0x5D, "ex", 0x03 }, -- ] - [0x007C] = { "sy", 0x6A, "ex", 0x0C }, -- | - [0x005C] = { "sy", 0x6E, "ex", 0x0F }, -- \ - [0x007B] = { "sy", 0x66, "ex", 0x08 }, -- { - [0x007D] = { "sy", 0x67, "ex", 0x09 }, -- } - - [0x005E] = { "mr", 0x5E, "ex", 0x62 }, -- widehat - [0x007E] = { "mr", 0x7E, "ex", 0x65 }, -- widetilde - - [0x2AAF] = { "sy", 0x16 }, -- preceq - [0x2AB0] = { "sy", 0x17 }, -- succeq - - [0x2145] = { "mr", 0x44 }, - [0x2146] = { "mr", 0x64 }, - [0x2147] = { "mr", 0x65 }, - - -- please let lm/gypre math show up soon - -} - -mathematics.slots.current = mathematics.slots.traditional +-- needed for mathml analysis function mathematics.utfmathclass(chr, default) local cd = characters.data[utfbyte(chr)] @@ -473,3 +279,42 @@ function mathematics.register_xml_entities() end end end + +-- helpers + +function mathematics.big(tfmdata,unicode,n) + local t = tfmdata.characters + local c = t[unicode] + if c then + local next = c.next + while next do + if n <= 1 then + return next + else + n = n - 1 + next = t[next].next + end + end + end + return unicode +end + +-- plugins + +function mathematics.scaleparameters(t,tfmtable,delta) + local math_parameters = tfmtable.math_parameters + if math_parameters and next(math_parameters) then + delta = delta or 1 + local _, mp = mathematics.dimensions(math_parameters) + for name, value in next, mp do + if name ~= "RadicalDegreeBottomRaisePercent" then + mp[name] = delta*value + else + mp[name] = value + end + end + t.MathConstants = mp + end +end + +table.insert(fonts.tfm.mathactions,mathematics.scaleparameters) diff --git a/tex/context/base/math-ini.mkii b/tex/context/base/math-ini.mkii index 6b0cd71d7..7d87fb365 100644 --- a/tex/context/base/math-ini.mkii +++ b/tex/context/base/math-ini.mkii @@ -1,14 +1,681 @@ %D \module %D [ file=math-ini, -%D version=2008.01.02, -%D title=\CONTEXT\ Lua Macros, -%D subtitle=Math Initializations, -%D author=Hans Hagen, +%D version=2001.04.12, +%D title=\CONTEXT\ Math Macros, +%D subtitle=Initializations, +%D author={Hans Hagen \& Taco Hoekwater}, %D date=\currentdate, -%D copyright=PRAGMA] +%D copyright=\PRAGMA] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\endinput +\writestatus{loading}{ConTeXt Math Macros / Initializations} + +% todo: make all definitions global since file loaded only once + +%D This module provides namespaces for math fonts, thereby +%D permitting mixed usage of math fonts. Although not strictly +%D needed, we also provide a family name mapping mechanism as +%D used in the (original) AMS math definition files, but here +%D these names can recursively be remapped and if needed, +%D dynamically be changed. We've tried to minimize the number +%D of definition commands and use plain \TEX\ definitions as +%D fallback. We've tried to follow a couple of conventions +%D from plain and AMS math in order to achieve backward +%D compatinility. We also kept an eye on future usage of these +%D modules in the perspective of MathML and unicode fonts. + +\unprotect + +\def\@ml@{@ml@} % math list (used for collection) +\def\@mf@{@mf@} % math family +%def\@mh@{@mh@} % math handler (not used) +\def\@mt@{@mt@} % math token +\def\@mc@{@mc@} % math collection + +\def\@@mathlimopcomm#1{\mathop{#1}} %no \limits +\def\@@mathnolopcomm#1{\mathop{#1}\nolimits} +\def\@@mathboxcomm #1{\dontleavehmode\hbox{$\mathsurround\zeropoint#1$}} + +\chardef\mathordcode = 0 \let\mathordcomm \mathord +\chardef\mathopcode = 1 \let\mathopcomm \mathop +\chardef\mathbincode = 2 \let\mathbincomm \mathbin +\chardef\mathrelcode = 3 \let\mathrelcomm \mathrel +\chardef\mathopencode = 4 \let\mathopencomm \mathopen +\chardef\mathclosecode = 5 \let\mathclosecomm \mathclose +\chardef\mathpunctcode = 6 \let\mathpunctcomm \mathpunct +\chardef\mathalphacode = 7 \let\mathalphacomm \firstofoneargument +\chardef\mathinnercode = 0 \let\mathinnercomm \mathinner +\chardef\mathnothingcode= 0 \let\mathnothingcomm \firstofoneargument +\chardef\mathlimopcode = 1 \let\mathlimopcomm \@@mathlimopcomm +\chardef\mathnolopcode = 1 \let\mathnolopcomm \@@mathnolopcomm +\chardef\mathchoicecode = 0 \let\mathchoicecomm \@@mathchoicecomm +\chardef\mathboxcode = 0 \let\mathboxcomm \@@mathboxcomm + +\chardef\mathaccentcode = 8 +\chardef\mathradicalcode= 9 + +\def\@@mathchoicecomm#1{[todo #1]} + +\def\puremathcode#1{\the\csname math#1code\endcsname} +\def\puremathcomm#1{\csname math#1comm\endcsname} + +\newif\iftracemathcollection + +% Simple variant: +% +% \def\dohandlemathtoken#1% +% {\csname\@mt@ +% \ifcsname\@mt@\mathcollection#1\endcsname +% \mathcollection +% \else\ifcsname\@mt@\nomathcollection#1\endcsname +% \nomathcollection +% \fi\fi +% #1\endcsname} + +%D Because a command can have a different meaning in math +%D and in text mode, we provide a selector. We also provide +%D the pure alternatives as \type {\mathcharacter} and \type +%D {\textcharacter}. + +% \ifx\dohandlecommand\undefined \wait \fi % troubles ! but not in mkiv so ... + +\let\mathcharacter\dohandlemathtoken +\let\textcharacter\dohandlecommand % better \dohandletexttoken + +% More clever layout: +% +% \def\dohandlemathtoken#1% +% {\csname +% \ifmmode +% \ifcsname\@mt@\mathcollection#1\endcsname +% \@mt@\mathcollection +% \else\ifcsname\@mt@\nomathcollection#1\endcsname +% \@mt@\nomathcollection +% \else\ifcsname\characterencoding#1\endcsname +% \characterencoding +% \else +% \nocharacterencoding +% \fi\fi\fi +% \else +% \ifcsname\characterencoding#1\endcsname +% \characterencoding +% \else +% \nocharacterencoding +% \fi +% \fi +% #1\endcsname} +% +% fallback to math when in text mode (handy for unicode vectors) + +\def\dohandlemathtoken#1% + {\csname + \ifmmode + \ifcsname\@mt@\mathcollection#1\endcsname + \@mt@\mathcollection + \else\ifcsname\@mt@\nomathcollection#1\endcsname + \@mt@\nomathcollection + \else\ifcsname\characterencoding#1\endcsname + \characterencoding + \else + \nocharacterencoding + \fi\fi\fi + \else + \ifcsname\characterencoding#1\endcsname + \characterencoding + \else\ifcsname\nocharacterencoding#1\endcsname + \nocharacterencoding + \else\ifcsname\@mt@\mathcollection#1\endcsname + \strippedcsname\mathematics\expandafter\endcsname\csname\@mt@\mathcollection + \else\ifcsname\@mt@\nomathcollection#1\endcsname + \strippedcsname\mathematics\expandafter\endcsname\csname\@mt@\nomathcollection + \else + \nocharacterencoding + \fi\fi\fi\fi + \fi + #1\endcsname} + +%D Now we redefine the text encoding handler. + +%D A better fallback: + +% Just ETEX which is the default nowadays. + +\def\dohandlemathtoken#1% + {\csname + \ifmmode + \ifcsname\@mt@\mathcollection:\outerencoding#1\endcsname + \@mt@\mathcollection:\outerencoding + \else\ifcsname\@mt@\mathcollection#1\endcsname + \@mt@\mathcollection + \else\ifcsname\@mt@\nomathcollection#1\endcsname + \@mt@\nomathcollection + \else\ifcsname\characterencoding#1\endcsname + \characterencoding + \else + \nocharacterencoding + \fi\fi\fi\fi + \else + \ifcsname\characterencoding#1\endcsname + \characterencoding + \else\ifcsname\nocharacterencoding#1\endcsname + \nocharacterencoding + \else\ifcsname\@mt@\mathcollection:\outerencoding#1\endcsname + \@mt@\mathcollection:\outerencoding + \else\ifcsname\@mt@\mathcollection#1\endcsname + \strippedcsname\mathematics\expandafter\endcsname\csname\@mt@\mathcollection + \else\ifcsname\@mt@\nomathcollection#1\endcsname + \strippedcsname\mathematics\expandafter\endcsname\csname\@mt@\nomathcollection + \else + \nocharacterencoding + \fi\fi\fi\fi\fi + \fi + #1\endcsname} + +\let\dohandlecommand\dohandlemathtoken + +\def\definefamilysynonym + {\dotripleempty\dodefinefamilysynonym} + +\def\dodefinefamilysynonym[#1][#2][#3]% [mathcollection] [] [] + {\ifthirdargument + \setvalue{\@mf@#1#2}{#3}% + \else + \setvalue{\@mf@ #1}{#2}% + \fi} + +\let\mathsubfamily\empty + +\def\purefamily #1{\csname \truefamily{#1}\mathsubfamily\s!fam\endcsname} +\def\purefamilyhex#1{\csname hex\truefamily{#1}\mathsubfamily\s!fam\endcsname} + +\def\truefamily#1% + {\ifcsname\@mf@\mathcollection#1\endcsname + \@EA\truefamily\csname\@mf@\mathcollection#1\endcsname + \else\ifcsname\@mf@#1\endcsname + \@EA\truefamily\csname\@mf@#1\endcsname + \else\ifcsname\@mf@\nomathcollection#1\endcsname + \@EA\truefamily\csname\@mf@\nomathcollection#1\endcsname + \else + #1% + \fi\fi\fi} + +\newif\ifdynamicmathfamilies \dynamicmathfamiliestrue % true per 2003.11.25; needed for mixed bold math + +\let\normalpurefamilyhex\purefamilyhex + +% todo: reset collection (tok legen) en opnieuw laden met true + +\def\definemathsymbol + {\dosixtupleempty\dodefinemathsymbol} + +\def\dodefinemathsymbol[#1][#2][#3][#4][#5][#6]% + {\unexpanded\setgvalue{#1}{\dohandlemathtoken{#1}}% + \ifdynamicmathfamilies \let\purefamilyhex\relax \fi + \setevalue{\@mt@\mathcollection#1}% + {\ifsixthargument + \ifnum\puremathcode{#2}=\mathradicalcode + \radical"% + \else + \delimiter"% + \ifnum\puremathcode{#2}>7 0\else\puremathcode{#2}\fi + \fi + \purefamilyhex{#3}\uchexnumbers{#4}% + \purefamilyhex{#5}\uchexnumbers{#6}\space + \else\iffourthargument + \ifnum\puremathcode{#2}=\mathaccentcode + \mathaccent\else\mathchar + \fi + "\ifnum\puremathcode{#2}>7 0\else\puremathcode{#2}\fi + \purefamilyhex{#3}\uchexnumbers{#4}\space + \fi\fi}% + \let\purefamilyhex\normalpurefamilyhex + \tracemathsymbol{#1}} + +\def\tracemathsymbol#1% + {\iftracemathcollection + {\endgraf + \hbox{\tex{#1}~:~{\mathematics{\getvalue{#1}{}}}} + \endgraf}% + \fi} + +\def\definemathcharacter + {\dosixtupleempty\dodefinemathcharacter} + +% \def\dodefinemathcharacter[#1][#2][#3][#4][#5][#6]% +% {\setmathtoks +% \ifdynamicmathfamilies \let\purefamilyhex\relax \fi +% \doifnumberelse{#1} +% {\scratchcounter#1} +% {\scratchcounter\@EA`\string#1}% +% \appendetoks +% \ifsixthargument +% \delcode\the\scratchcounter="% +% \ifnum\puremathcode{#2}>7 0\else\puremathcode{#2}\fi +% \purefamilyhex{#3}\uchexnumbers{#4}% +% \purefamilyhex{#5}\uchexnumbers{#6}\space +% \else\iffourthargument +% \mathcode\the\scratchcounter="% +% \ifnum\puremathcode{#2}>7 0\else\puremathcode{#2}\fi +% \purefamilyhex{#3}\uchexnumbers{#4}\space +% \fi\fi\to\mathtoks +% \let\purefamilyhex\normalpurefamilyhex +% \tracemathcharacter{#1}} + +\newtoks\mathscratchtoks + +\def\definemathcharacter + {\chardef\mathcharactermode\zerocount + \dosixtupleempty\dodefinemathcharacter} + +\def\redefinemathcharacter + {\chardef\mathcharactermode\plusone + \dosixtupleempty\dodefinemathcharacter} + +\def\dodefinemathcharacter[#1][#2][#3][#4][#5][#6]% + {\ifcase\mathcharactermode + \setmathtoks + \or + \let\mathtoks\mathscratchtoks \mathtoks\emptytoks + \fi + \ifdynamicmathfamilies \let\purefamilyhex\relax \fi + \doifnumberelse{#1} + {\scratchcounter#1} + {\scratchcounter\@EA`\string#1}% + \appendetoks + \ifsixthargument + \delcode\the\scratchcounter="% + \ifnum\puremathcode{#2}>7 0\else\puremathcode{#2}\fi + \purefamilyhex{#3}\uchexnumbers{#4}% + \purefamilyhex{#5}\uchexnumbers{#6}\space + \else\iffourthargument + \mathcode\the\scratchcounter="% + \ifnum\puremathcode{#2}>7 0\else\puremathcode{#2}\fi + \purefamilyhex{#3}\uchexnumbers{#4}\space + \fi\fi + \to \mathtoks + \let\purefamilyhex\normalpurefamilyhex + \ifcase\mathcharactermode + \expandafter\tracemathcharacter + \or + \the\mathtoks + \mathtoks\emptytoks + \expandafter\gobbleoneargument + \fi{#1}} % maybe lookahead + +\def\tracemathcharacter#1% + {\iftracemathcollection + {\endgraf + \doifnumberelse{#1} + {\hbox{\tttf\rawcharacter{#1}~:~{\mathematics{\rawcharacter{#1}}}}} + {\hbox{\type{#1}~:~{\mathematics{#1}}}} + \endgraf}% + \fi} + +\def\definemathcommand + {\dotripleempty\dodefinemathcommand} + +\def\dodefinemathcommand[#1][#2][#3]#4% command class args meaning + {\unexpanded\setgvalue{#1}{\dohandlemathtoken{#1}}% + \ifthirdargument + \processaction + [#3] + [one=>\setvalue{\@mt@\mathcollection#1}##1{\puremathcomm{#2}{#4{##1}}}, + two=>\setvalue{\@mt@\mathcollection#1}##1##2{\puremathcomm{#2}{#4{##1}{##2}}}]% + \else\ifsecondargument + \setvalue{\@mt@\mathcollection#1}{\puremathcomm{#2}{#4}}% + \else + \setvalue{\@mt@\mathcollection#1}{\puremathcomm{nothing}{#4}}% + \fi\fi + \tracemathcommand{#1}} + +\def\tracemathcommand#1% + {\iftracemathcollection + \endgraf\hbox{\tex{#1}~:~{\mathematics{\getvalue{#1}{}}}}\endgraf + \fi} + +\def\startmathcollection[#1]% + {\pushmacro\mathcollection + \setmathcollection{#1}} + +\def\setmathcollection#1% + {\edef\mathcollection{#1}% + \doifundefined{\@ml@\mathcollection} + {\expandafter\newtoks\csname\@ml@\mathcollection\endcsname}} + +\def\stopmathcollection + {\popmacro\mathcollection} + +\def\startrawmathcollection + {\startmathcollection} + +\def\stoprawmathcollection + {\stopmathcollection} + +\newtoks\mathtoks + +\def\setmathtoks + {\@EA\let\@EA\mathtoks\csname\@ml@\mathcollection\endcsname} + +\def\currentmathcollection{\mathcollection} + +\let\nomathcollection\s!default + +\def\enablemathcollection[#1]% + {\doifnot{#1}\s!default + {\setmathcollection\s!default + \the\csname\@ml@\mathcollection\endcsname}% + \setmathcollection{#1}% + \the\csname\@ml@\mathcollection\endcsname} + +% hook 'm into the font mechanism + +\definefilesynonym[\f!mathprefix\s!default][\f!mathprefix tex] + +\def\usemathcollection + {\dodoubleempty\dousemathcollection} + +\def\dousemathcollection[#1][#2]% + {\pushmacro\fontclass + \pushmacro\mathclass + \ifsecondargument + \edef\fontclass{#1}% + \edef\mathclass{#2}% + \else + \edef\mathclass{#1}% + \fi + \doinputonce{\truefilename{\f!mathprefix\mathclass}}% + \doifsomething\fontclass{\setevalue{\@mc@\fontclass\@mc@}{\mathclass}}% + \popmacro\mathclass + \popmacro\fontclass} + +\let\mathclass\nomathcollection + +\letvalue{\@mc@\@mc@}\nomathcollection + +% \def\autoenablemathcollection +% {\doifdefinedelse{\@mc@\fontclass\@mc@} +% {\enablemathcollection[\getvalue{\@mc@\fontclass\@mc@}]} +% {\enablemathcollection[\s!default]}} % ? ? ? + +\def\autoenablemathcollection + {\expanded{\enablemathcollection[\executeifdefined{\@mc@\fontclass\@mc@}\nomathcollection]}} + +\appendtoks\autoenablemathcollection\to\mathstrategies + +\fetchruntimecommand \showmathcharacters {\f!mathprefix\s!run.mkii} +\fetchruntimecommand \showmathtoken {\f!mathprefix\s!run.mkii} + +\def\resetmathcollection[#1]% + {\def\mathcollection{#1}% + \forgetdoingonce{\f!mathprefix\mathcollection}% + \setmathtoks + \ifx\mathtoks\relax\else\mathtoks\emptytoks\fi} + +%D \macros +%D {ifmathpunctuation, enablemathpunctuation, +%D definemathpunctuation} +%D +%D This will replace periods by comma's: +%D +%D \starttyping +%D \definemathpunctuation . textcomma textperiod +%D \definemathpunctuation , textcomma textcomma +%D +%D \appendtoks +%D \redefinemathcharacter [.] [ord] [mi] ["3B]% +%D \to \everymathpunctuation +%D \stoptyping + +% \newif\ifmathpunctuation +% +% \def\enablemathpunctuation{\mathpunctuationtrue} +% +% \def\definemathpunctuation #1 #2 #3 % +% {\appendtoks +% \initializemathpunctuation{#1}{#2}{#3}% +% \to\everymathematics} +% +% \def\initializemathpunctuation#1#2#3% sloowww +% {\ifmathpunctuation % hm move this test to everymath, or better a separate token list +% \mathcode`#1="8000 +% \defineactivecharacter #1 {\dohandlemathpunctuation{#2}{#3}}% +% \fi} +% +% \unexpanded\def\dohandlemathpunctuation#1#2% \if fails in mathml interval +% {\def\next{\csname\ifx\space\nexttoken#2\else#1\fi\endcsname}% +% \futurelet\nexttoken\next} + +\newtoks\everymathpunctuation + +\def\enablemathpunctuation % can be called inside math, so after \everymathematics + {\the\everymathpunctuation + \appendtoksonce + \the\everymathpunctuation + \to\everymathematics} + +\def\definemathpunctuation #1 #2 #3 % + {\appendtoks + \initializemathpunctuation{#1}{#2}{#3}% + \to\everymathpunctuation} + +\def\initializemathpunctuation#1#2#3% sloowww + {\mathcode`#1="8000 + \defineactivecharacter #1 {\dohandlemathpunctuation{#2}{#3}}} + +\unexpanded\def\dohandlemathpunctuation#1#2% \if fails in mathml interval + {\def\next{\csname\ifx\space\nexttoken#2\else#1\fi\endcsname}% + \futurelet\nexttoken\next} + +%D \startbuffer +%D \enablemathpunctuation$(1,2) (1, 2) (1{,}2) \hbox{foo, not bar}$ +%D \stopbuffer +%D +%D \typebuffer +%D +%D \blank{\getbuffer}\blank + +%D needed for sin, cos etc + +\def\mfunction #1{{\mr#1}} + +% \def\mlimitsfunction #1{\mathlimopcomm{{\mr#1}} +% \def\mnolimitsfunction#1{\mathnolopcomm{{\mr#1}} + +%D Taco posted this solution as response to a mail by Olivier, so +%D let's integrate it here. + +% \def\setmathfunctionstyle#1% rm ss tt +% {\def\mfunction##1% no families, just scaling a la text +% {\mathchoice +% {\hbox{\csname#1\endcsname\tf ##1}} +% {\hbox{\csname#1\endcsname\tf ##1}} +% {\hbox{\csname#1\endcsname\tfx ##1}} +% {\hbox{\csname#1\endcsname\tfxx##1}}}} + +\def\currentmscaledstyle{rm} % will be plugged into the typeface text=ss option + +\def\setmathfunctionstyle#1% rm ss tt + {\doifsomething{#1} + {\def\currentmscaledstyle{#1}% + \def\mathopnolimits##1{\mathop{\mscaledtext{##1}}\nolimits}% + \def\mfunction##1{\mscaledtext{##1}}}} + +\def\mscaledtext#1% + {\mathchoice + {\hbox{\csname\currentmscaledstyle\endcsname\tf #1}} + {\hbox{\csname\currentmscaledstyle\endcsname\tf #1}} + {\hbox{\csname\currentmscaledstyle\endcsname\tfx #1}} + {\hbox{\csname\currentmscaledstyle\endcsname\tfxx#1}}} + +%D We can force the way functions are typeset by manipulating the text +%D option: +%D +%D \starttyping +%D \definetypeface[iwona][ss][sans][iwona][default][encoding=texnansi] +%D \definetypeface[iwona][mm][math][iwona][default][encoding=texnansi,text=ss] +%D \stoptyping +%D +%D This hooks into the math handler with: + +\appendtoks + \setmathfunctionstyle\currentmathtextstyle +\to \everybodyfont + +%D Usage: +%D +%D \starttyping +%D \setmathfunctionstyle\fontstyle % or {rm} or {ss} or .. +%D \rm test $\sin{(x^{\sin(x^{\sin(x)})})}$ test +%D \ss test $\sin{(x^{\sin(x^{\sin(x)})})}$ test +%D \tt test $\sin{(x^{\sin(x^{\sin(x)})})}$ test +%D \stoptyping + +\edef\hexmrfam {0} \edef\hexbsfam {8} +\edef\hexmifam {1} \edef\hexbifam {9} +\edef\hexsyfam {2} \edef\hexscfam {A} +\edef\hexexfam {3} \edef\hextffam {B} +\edef\hexitfam {4} \edef\hexmafam {C} +\edef\hexslfam {5} \edef\hexmbfam {D} +\edef\hexbffam {6} \edef\hexmcfam {E} +\edef\hexnnfam {7} \edef\hexmdfam {F} + +\definefamilysynonym [default] [letters] [mr] +\definefamilysynonym [default] [operators] [sy] +\definefamilysynonym [default] [lcgreek] [mi] +\definefamilysynonym [default] [ucgreek] [mr] +\definefamilysynonym [default] [vargreek] [mi] +\definefamilysynonym [default] [mitfamily] [mi] +\definefamilysynonym [default] [calfamily] [sy] + +\definefamilysynonym [default] [0] [mr] +\definefamilysynonym [default] [1] [mi] +\definefamilysynonym [default] [2] [sy] +\definefamilysynonym [default] [3] [ex] + +\enablemathcollection[default] + +\usemathcollection [default] [tex] +\usemathcollection [default] [ams] +\usemathcollection [default] [uni] + +\enablemathcollection[default] + +%D Some goodies: + +\def\Angstrom{\nomathematics{\Aring}} + +%D Bold math: +%D +%D \starttyping +%D \usetypescript [lucida] [texnansi] +%D +%D \definetypeface [boldmath] [rm] [serif] +%D [lucida] [default] [encoding=texnansi] +%D \definetypeface [boldmath] [tt] [mono] +%D [lucida] [default] [encoding=texnansi] +%D \definetypeface [boldmath] [ss] [sans] +%D [lucida] [default] [encoding=texnansi] +%D \definetypeface [boldmath] [mm] [boldmath] +%D [lucida] [default] [encoding=texnansi] +%D +%D \switchtobodyfont[lucida,10pt] +%D +%D \showmathtoken{Gamma} $\Gamma \Delta \alpha \delta \zeta$ +%D +%D \switchtobodyfont[boldmath,10pt] +%D +%D \showmathtoken{Gamma} $\Gamma \Delta \alpha \delta \zeta$ +%D \stoptyping + +%D \macros +%D {nonknuthmode, donknuthmode} +%D +%D The underscore is frequently used in manuals but unfortunately \TEX\ prefers +%D it to be a math specific character. And since computer modern fonts didn't +%D have an underscore, one had to use commands to fake one. Nowadays we do +%D have underscores in latin modern, and since all other fonts have them, we +%D decided to get away from the restriction to use the underscore character in +%D text mode. +%D +%D \starttyping +%D \def\test#1{#1} +%D +%D \nonknuthmode $x_2$ x_2 \test{$x_2$} \test{x_2} +%D +%D \donknuthmode $x_2$ x_2 \test{$x_2$} \test{x_2} +%D \stoptyping +%D +%D The result is as expected: the first line typesets ok, while the second +%D one triggers an error message. + +\bgroup + + \ifx\normalsuber\undefined \def\normalsuber{_} \fi + \ifx\normalsuper\undefined \def\normalsuper{^} \fi + + \catcode`_=\active + \catcode`^=\active + + \gdef\nonknuthmode + {\appendtoks\let_\normalsuber\let^\normalsuper\to\everymathematics + \mathcode`_="8000 + \mathcode`^="8000 + \catcode`_=\@@other + \catcode`^=\@@other + \let\nonknuthmode\relax} + + \gdef\donknuthmode + {\catcode`_=\@@subscript + \catcode`^=\@@superscript} + +\egroup + +%D \macros +%D {checkdelimiters, fakeleftdelimiter, fakerightdelimiter} +%D +%D Handy for non matching situations (as with mathml): +%D +%D \starttyping +%D \checkdelimiters{... bla bla ...} +%D \fakeleftdelimiter +%D ... bla bla ... +%D \fakerightdelimiter +%D \stoptyping + +\newcount\delimitercount + +\def\leftfakedelimiter {\advance\delimitercount\minusone\gobbleoneargument}% +\def\rightfakedelimiter{\advance\delimitercount\plusone \gobbleoneargument}% + +\def\checkdelimiters#1% + {\delimitercount\zerocount + \setbox\scratchbox\hbox\bgroup + \let\left \leftfakedelimiter + \let\right\rightfakedelimiter + $#1\expandafter$\expandafter + \egroup + \expandafter\delimitercount\the\delimitercount\relax} + +\def\fakeleftdelimiter {\ifnum\delimitercount>\zerocount\left .\fi} +\def\fakerightdelimiter{\ifnum\delimitercount<\zerocount\right.\fi} + +%D Needed for unicode: + +\def\nulloperator{\mathortext{\mathop{\null}}{\null}} + +%D To be dealt with ... + +\mathcode`\ ="8000 % \space +\mathcode`\'="8000 % ^\prime +\mathcode`\_="8000 % \_ + +\protect \endinput + +\tracemathcollectiontrue + \input math-tex \page +\setupbodyfont[ams] \enablemathcollection[default] \input math-ams \page +\setupbodyfont[lbr] \enablemathcollection[lbr] \input math-lbr \page +\setupbodyfont[eul] \enablemathcollection[eul] \input math-eul \stoptext diff --git a/tex/context/base/math-ini.mkiv b/tex/context/base/math-ini.mkiv index 062631b39..b87096661 100644 --- a/tex/context/base/math-ini.mkiv +++ b/tex/context/base/math-ini.mkiv @@ -1,8 +1,8 @@ %D \module %D [ file=math-ini, %D version=2008.01.02, -%D title=\CONTEXT\ Lua Macros, -%D subtitle=Math Initializations, +%D title=\CONTEXT\ Math Macros, +%D subtitle=Initializations, %D author=Hans Hagen, %D date=\currentdate, %D copyright=PRAGMA] @@ -11,39 +11,549 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. +\writestatus{loading}{ConTeXt Math Macros / Initializations} + +%D This module provides namespaces for math fonts, thereby +%D permitting mixed usage of math fonts. Although not strictly +%D needed, we also provide a family name mapping mechanism as +%D used in the (original) AMS math definition files, but here +%D these names can recursively be remapped and if needed, +%D dynamically be changed. We've tried to minimize the number +%D of definition commands and use plain \TEX\ definitions as +%D fallback. We've tried to follow a couple of conventions +%D from plain and AMS math in order to achieve backward +%D compatinility. We also kept an eye on future usage of these +%D modules in the perspective of MathML and unicode fonts. + \unprotect +\ifx\v!compact\undefined \def\v!compact{compact} \fi + %D We move these definitions into the format: % test [[\char948 \ctxlua{tex.sprint(utf.char(948))}]] % test $[[\char948 \ctxlua{tex.sprint(utf.char(948))}]]$ \registerctxluafile{math-ini}{1.001} +\registerctxluafile{math-dim}{1.001} \registerctxluafile{math-ent}{1.001} +\registerctxluafile{math-ext}{1.001} +\registerctxluafile{math-vfu}{1.001} +\registerctxluafile{math-map}{1.001} +\registerctxluafile{math-noa}{1.001} + +\definesystemattribute[mathalph] +\definesystemattribute[mathsize] +\definesystemattribute[mathpunc] + +% todo: only in mmode + +% \def\setmathattribute#1#2{\dosetattribute{mathalph}{\ctxlua{tex.sprint(mathematics.sync_a_both (\number\dogetattribute{mathalph},"#1","#2"))}}} +% \def\setmathalphabet #1{\dosetattribute{mathalph}{\ctxlua{tex.sprint(mathematics.sync_a_name (\number\dogetattribute{mathalph},"#1"))}}} +% \def\setmathstyle #1{\dosetattribute{mathalph}{\ctxlua{tex.sprint(mathematics.sync_a_style(\number\dogetattribute{mathalph},"#1"))}}} + +\def\setmathattribute#1#2{\ctxlua{mathematics.sync_a_both ("#1","#2")}} +\def\setmathalphabet #1{\ctxlua{mathematics.sync_a_name ("#1")}} +\def\setmathstyle #1{\ctxlua{mathematics.sync_a_style("#1")}} + +\unexpanded\def\mr {\setmathattribute{regular}{tf}} + +\unexpanded\def\mathdefault {\setmathattribute{regular}{it}} +\unexpanded\def\mathscript {\setmathalphabet{script}} +\unexpanded\def\mathfraktur {\setmathalphabet{fraktur}} +\unexpanded\def\mathblackboard{\setmathalphabet{blackboard}} + +\unexpanded\def\mathrm{\setmathattribute{rm}{tf}} +\unexpanded\def\mathss{\setmathattribute{ss}{tf}} +\unexpanded\def\mathtt{\setmathattribute{tt}{tf}} + +\unexpanded\def\mathtf{\setmathstyle{tf}} +\unexpanded\def\mathbf{\setmathstyle{bf}} +\unexpanded\def\mathsl{\setmathstyle{sl}} +\unexpanded\def\mathit{\setmathstyle{it}} +\unexpanded\def\mathbs{\setmathstyle{bs}} +\unexpanded\def\mathbi{\setmathstyle{bi}} + +\let\tfmath\mathtf % maybe a grouped command +\let\bfmath\mathbf +\let\slmath\mathsl +\let\itmath\mathit +\let\bsmath\mathbs +\let\bimath\mathbi -% \registerctxluafile{math-def}{1.001} -% \ctxlua{mathematics.traditional()} +\let\Bbb\mathblackboard -\ctxlua{mathematics.define()} -\ctxlua{mathematics.register_xml_entities()} +\unexpanded\def\frak {\ifmmode\expandafter\mathfraktur \fi} +\unexpanded\def\cal {\ifmmode\expandafter\mathscript \fi} +\unexpanded\def\bbd {\ifmmode\expandafter\mathblackboard\fi} +\unexpanded\def\blackboard{\ifmmode\expandafter\mathblackboard\fi} +\unexpanded\def\fraktur {\ifmmode\expandafter\mathfraktur \fi} +\unexpanded\def\gothic {\ifmmode\expandafter\mathfraktur \fi} + +\unexpanded\def\mathcal #1{{\setmathalphabet{script}#1}} % for AMS compatibility +\unexpanded\def\mathfrak#1{{\setmathalphabet{fraktur}#1}} % for AMS compatibility +\unexpanded\def\mathbb #1{{\setmathalphabet{blackboard}#1}} % for AMS compatibility + +\let\normalmr\mr + +\prependtoks + \let\mr\normalmr + \let\rm\mathrm \let\ss\mathss \let\tt\mathtt + \let\tf\mathtf \let\bf\mathbf \let\it\mathit \let\sl\mathsl \let\bi\mathbi \let\bs\mathbs + \let\frak\mathfraktur \let\cal\mathscript \let\bbd\mathblackboard + \mathdefault +\to \everymathematics + +%D \macros +%D {boldsymbol} +%D +%D The math definition is inspired by amsmath. +%D +%D \startbuffer +%D \definetypeface [boldmath] [mm] [boldmath] [latin-modern] [modern] [encoding=texnansi] +%D +%D $a \times b$ $a \boldsymbol{\times} b$ +%D \stopbuffer +%D +%D \typebuffer \start \getbuffer \stop + +\let\mathboldsymbol\relax % yet unsupported, will be + +\def\boldsymbol + {\mathortext\mathboldsymbol\bold} + +%D Helpers: \def\utfmathclass #1{\ctxlua{tex.sprint(mathematics.utfmathclass ("#1"))}} \def\utfmathstretch#1{\ctxlua{tex.sprint(mathematics.utfmathstretch("#1"))}} \def\utfmathcommand#1{\ctxlua{tex.sprint(mathematics.utfmathcommand("#1"))}} \def\utfmathfiller #1{\ctxlua{tex.sprint(mathematics.utfmathfiller ("#1"))}} -\def\utfmathclassdefault #1#2{\ctxlua{ - tex.sprint(mathematics.utfmathclass("#1","#2")) -}} +% \def\utfmathclassdefault #1#2{\ctxlua{ +% tex.sprint(mathematics.utfmathclass("#1","#2")) +% }} +% +% \def\utfmathcommanddefault#1#2#3{\ctxlua{ +% local cmd = mathematics.utfmathcommand("#1","") or "" +% if cmd == "" then +% commands.cs("#2","#3") +% else +% commands.cs(cmd) +% end}} + +% % % + +\def\@@mathlimopcomm #1{\mathop{#1}} %no \limits +\def\@@mathnolopcomm #1{\mathop{#1}\nolimits} +\def\@@mathboxcomm #1{\dontleavehmode\hbox{$\mathsurround\zeropoint#1$}} +\def\@@mathchoicecomm#1{[todo #1]} + +\chardef\mathordcode = 0 \let\mathordcomm \mathord +\chardef\mathopcode = 1 \let\mathopcomm \mathop +\chardef\mathbincode = 2 \let\mathbincomm \mathbin +\chardef\mathrelcode = 3 \let\mathrelcomm \mathrel +\chardef\mathopencode = 4 \let\mathopencomm \mathopen +\chardef\mathclosecode = 5 \let\mathclosecomm \mathclose +\chardef\mathpunctcode = 6 \let\mathpunctcomm \mathpunct +\chardef\mathalphacode = 7 \let\mathalphacomm \firstofoneargument +\chardef\mathinnercode = 0 \let\mathinnercomm \mathinner +\chardef\mathnothingcode= 0 \let\mathnothingcomm \firstofoneargument +\chardef\mathlimopcode = 1 \let\mathlimopcomm \@@mathlimopcomm +\chardef\mathnolopcode = 1 \let\mathnolopcomm \@@mathnolopcomm +\chardef\mathchoicecode = 0 \let\mathchoicecomm \@@mathchoicecomm +\chardef\mathboxcode = 0 \let\mathboxcomm \@@mathboxcomm + +\chardef\mathaccentcode = 8 +\chardef\mathradicalcode= 9 + +\def\puremathcode#1{\the\csname math#1code\endcsname} +\def\puremathcomm#1{\csname math#1comm\endcsname} + +% \startlines +% $\mathopnolimits{\rm d}x$ +% $\mathopnolimits{\kern\zeropoint \rm d}x$ +% $\puremathcomm{nolop}{\rm d}x$ +% $\puremathcomm{nolop}{\kern\zeropoint\rm d}x$ +% \blank +% $\puremathcomm{nolop}{\mr d}x$ +% $\puremathcomm{nolop}{\kern\zeropoint\mr d}x$ +% $\mathop{\kern\zeropoint\mr d}x$ +% $\mathopnolimits{\kern\zeropoint d}x$ +% \stoplines + +\newif\iftracemathcollection + +% this will be sorted out: + +\let\mathcharacter \getvalue +\let\textcharacter \getvalue +\def\definefamilysynonym {\dotripleempty\dodefinefamilysynonym} +\def\dodefinefamilysynonym [#1][#2][#3]{} +\def\definemathsymbol {\dosixtupleempty\dodefinemathsymbol} +\def\dodefinemathsymbol [#1][#2][#3][#4][#5][#6]{} +\def\definemathcharacter {\dosixtupleempty\dodefinemathcharacter} +\def\redefinemathcharacter {\dosixtupleempty\dodefinemathcharacter} +\def\dodefinemathcharacter [#1][#2][#3][#4][#5][#6]{} +\def\startmathcollection [#1]{} +\def\setmathcollection #1{} +\def\stopmathcollection {} +\def\startrawmathcollection {} +\def\stoprawmathcollection {} +\def\setmathtoks {} +\let\currentmathcollection \s!default +\let\nomathcollection \s!default +\let\mathcollection \s!default +\def\enablemathcollection [#1]{} +\def\usemathcollection {\dodoubleempty\dousemathcollection} +\def\dousemathcollection [#1][#2]{} +\let\mathclass \nomathcollection +\let\autoenablemathcollection\relax +\def\resetmathcollection [#1]{} + +\def\definemathcommand + {\dotripleempty\dodefinemathcommand} + +\def\dodefinemathcommand[#1][#2][#3]#4% command class args meaning + {\ifthirdargument + \processaction + [#3] + [one=>\unexpanded\setvalue{#1}##1{\puremathcomm{#2}{#4{##1}}}, + two=>\unexpanded\setvalue{#1}##1##2{\puremathcomm{#2}{#4{##1}{##2}}}]% + \else\ifsecondargument + \unexpanded\setvalue{#1}{\puremathcomm{#2}{#4}}% + \else + \unexpanded\setvalue{#1}{\puremathcomm{nothing}{#4}}% + \fi\fi} + +%D Moved from font-ini.mkiv: +%D +%D \macros +%D {mf,mbox,enablembox,mathop} +%D +%D Todo: + +\unexpanded\def\mf + {\csname\fontalternative\endcsname} + +\let\normalmathop\mathop + +\unexpanded\def\mathop + {\normalmathop + \bgroup + \let\rm\mf + \let\next=} + +\def\normalmbox + {\normalhbox\bgroup\mf + \dowithnextbox{\flushnextbox\egroup}\normalhbox} + +\def\mbox + {\ifmmode\normalmbox\else\normalhbox\fi} + +\def\enablembox + {\appendtoks + \ifx\normalhbox\undefined\let\normalhbox\hbox\fi + \let\hbox\mbox + \to\everymathematics} + +%D needed for sin, cos etc + +\def\mfunction#1{{\mr#1}} + +% \def\mlimitsfunction #1{\mathlimopcomm{{\mr#1}} +% \def\mnolimitsfunction#1{\mathnolopcomm{{\mr#1}} + +%D Taco posted this solution as response to a mail by Olivier, so +%D let's integrate it here. + +\def\currentmscaledstyle{rm} % will be plugged into the typeface text=ss option + +\def\setmathfunctionstyle#1% rm ss tt + {\doifsomething{#1} + {\def\currentmscaledstyle{#1}% + \def\mathopnolimits##1{\mathop{\mscaledtext{##1}}\nolimits}% + \def\mfunction##1{\mscaledtext{##1}}}} + +\def\mscaledtext#1% + {\mathchoice + {\hbox{\csname\currentmscaledstyle\endcsname\tf #1}} + {\hbox{\csname\currentmscaledstyle\endcsname\tf #1}} + {\hbox{\csname\currentmscaledstyle\endcsname\tfx #1}} + {\hbox{\csname\currentmscaledstyle\endcsname\tfxx#1}}} + +%D We can force the way functions are typeset by manipulating the text +%D option: +%D +%D \starttyping +%D \definetypeface[iwona][ss][sans][iwona][default][encoding=texnansi] +%D \definetypeface[iwona][mm][math][iwona][default][encoding=texnansi,text=ss] +%D \stoptyping +%D +%D This hooks into the math handler with: + +\appendtoks + \setmathfunctionstyle\currentmathtextstyle +\to \everybodyfont + +%D Usage: +%D +%D \starttyping +%D \setmathfunctionstyle\fontstyle % or {rm} or {ss} or .. +%D \rm test $\sin{(x^{\sin(x^{\sin(x)})})}$ test +%D \ss test $\sin{(x^{\sin(x^{\sin(x)})})}$ test +%D \tt test $\sin{(x^{\sin(x^{\sin(x)})})}$ test +%D \stoptyping + +%D Some goodies: + +\def\Angstrom{\nomathematics{\Aring}} + +%D \macros +%D {nonknuthmode, donknuthmode} +%D +%D The underscore is frequently used in manuals but unfortunately \TEX\ prefers +%D it to be a math specific character. And since computer modern fonts didn't +%D have an underscore, one had to use commands to fake one. Nowadays we do +%D have underscores in latin modern, and since all other fonts have them, we +%D decided to get away from the restriction to use the underscore character in +%D text mode. +%D +%D \starttyping +%D \def\test#1{#1} +%D +%D \nonknuthmode $x_2$ x_2 \test{$x_2$} \test{x_2} +%D +%D \donknuthmode $x_2$ x_2 \test{$x_2$} \test{x_2} +%D \stoptyping +%D +%D The result is as expected: the first line typesets ok, while the second +%D one triggers an error message. + +\bgroup + + \ifx\normalsuber\undefined \def\normalsuber{_} \fi + \ifx\normalsuper\undefined \def\normalsuper{^} \fi + + \catcode`_=\active + \catcode`^=\active + + \gdef\nonknuthmode + {\appendtoks\let_\normalsuber\let^\normalsuper\to\everymathematics + \mathcode`_="8000 + \mathcode`^="8000 + \catcode`_=\@@other + \catcode`^=\@@other + \let\nonknuthmode\relax} + + \gdef\donknuthmode + {\catcode`_=\@@subscript + \catcode`^=\@@superscript} + +\egroup + +%D Needed for unicode: + +\def\nulloperator{\mathortext{\mathop{\null}}{\null}} + +%D To be dealt with ... + +\mathcode`\ ="8000 % \space +\mathcode`\'="8000 % ^\prime +\mathcode`\_="8000 % \_ + +%D \macros +%D {\setupmathematics} +%D +%D Configuration for integrals. (If needed we can speed this up and make it +%D installable; no processaction is needed then). + +\newtoks\everysetupmathematics + +\def\setupmathematics + {\dosingleargument\dosetupmathematics} + +\def\dosetupmathematics[#1]% + {\getparameters[\??mo][#1]% + \the\everysetupmathematics} + +\def\mathematicsparameter#1{\ifcsname\??mo#1\endcsname\csname\??mo#1\endcsname\fi} + +%D Memory saver: + +\appendtoks + \doifelse{\mathematicsparameter\v!compact}\v!yes + {\ctxlua{fonts.vf.math.optional=true}} + {\ctxlua{fonts.vf.math.optional=false}}% +\to \everysetupmathematics + +\setupmathematics + [\v!compact=no] + +%D \macros +%D {enablemathpunctuation,disablemathpunctuation} +%D +%D \startbuffer +%D \enablemathpunctuation$(1,2) (1, 2) (1{,}2) \hbox{foo, not bar}$ +%D \stopbuffer +%D +%D \typebuffer +%D +%D \blank{\getbuffer}\blank + +\setfalse \automathpunctuation + +\def\enablemathpunctuation {\settrue \automathpunctuation} +\def\disablemathpunctuation{\setfalse\automathpunctuation} + +\ifx\v!autopunctuation\undefined \def\v!autopunctuation{autopunctuation} \fi + +\appendtoks + \doifelse{\mathematicsparameter\v!autopunctuation}\v!yes\enablemathpunctuation\disablemathpunctuation +\to \everysetupmathematics + +\appendtoks + \ifconditional\automathpunctuation\dosetattribute{mathpunc}\plusone\fi +\to \everymathematics + +\setupmathematics + [\v!autopunctuation=\v!no] + +%D \macros +%D {mathstyle} +%D +%D If one want to be sure that something is typeset in the +%D appropriate style, \type {\mathstyle} can be used: +%D +%D \starttyping +%D \mathstyle{something} +%D \stoptyping + +% \def\mathstyle#1% +% {\mathchoice +% {\displaystyle #1}% +% {\textstyle #1}% +% {\scriptstyle #1}% +% {\scriptscriptstyle#1}} +% +% We now have a primitive operation for this. As the +% macro overloads a new primitive introduced in \LUATEX, +% we need to use \type {\normalmathstyle} when we consult +% the current math style. +% +% \let \mathstyle \Ustack % spoils cramped +% +% \let \mathstyle \firstofoneargument +% +% 0 = display +% 1 = crampeddisplay +% 2 = text +% 3 = crampedtext +% 4 = script +% 5 = crampedscript +% 6 = scriptscript +% 7 = crampedscriptscript + +\def\uncramped#1% + {{\ifcase\normalmathstyle + \or \displaystyle \or + \or \textstyle \or + \or \scriptstyle \or + \or \scriptscriptstyle \fi + #1}} + +\def\cramped#1% + {{\ifcase\normalmathstyle + \crampeddisplaystyle \or \or % 0 -> 1 + \crampedtextstyle \or \or % 2 -> 3 + \crampedscriptstyle \or \or % 4 -> 5 + \crampedscriptscriptstyle \fi % 6 -> 7 + #1}} + +\def\triggermathstyle#1% #1 is number + {\ifcase#1\relax + \displaystyle \or + \crampeddisplaystyle \or + \textstyle \or + \crampedtextstyle \or + \scriptstyle \or + \crampedscriptstyle \or + \scriptscriptstyle \or + \crampedscriptscriptstyle \else + % error + \fi} + +\def\cramped#1% + {{\ifcase\normalmathstyle + \crampeddisplaystyle \or \or % 0 -> 1 + \crampedtextstyle \or \or % 2 -> 3 + \crampedscriptstyle \or \or % 4 -> 5 + \crampedscriptscriptstyle \fi % 6 -> 7 + #1}} + +%D Something similar can be used in the (re|)|definition +%D of \type {\text}. This version is a variation on the one +%D in the math module (see \type{m-math} and|/|or \type +%D {m-newmat}). + +\unexpanded\def\mathtext + {\mathortext\domathtext\hbox} + +\def\domathtext#1% + {\mathchoice + {\dodomathtext\displaystyle\textface {#1}}% + {\dodomathtext\textstyle \textface {#1}}% + {\dodomathtext\textstyle \scriptface {#1}}% + {\dodomathtext\textstyle \scriptscriptface{#1}}} + +\def\dodomathtext#1#2#3% no \everymath ! + %{\hbox{\everymath{#1}\switchtobodyfont [#2]#3}} % 15 sec + {\hbox{\everymath{#1}\setcurrentfontbody{#2}#3}} % 3 sec (no math) + +%D Because we may overload \type {\text} in other (structuring) +%D macros, we say: + +\appendtoks \let\text\mathtext \to \everymathematics + +%D The next code is derived from plain \TEX. + +\newcount\interdisplaylinepenalty \interdisplaylinepenalty=100 + +\newif\ifdt@p + +\def\displ@y + {\global\dt@ptrue + \openup\displayopenupvalue % was \openup\jot + \everycr + {\noalign + {\ifdt@p + \global\dt@pfalse + \ifdim\prevdepth>-\thousandpoint + \vskip-\lineskiplimit + \vskip\normallineskiplimit + \fi + \else + \penalty\interdisplaylinepenalty + \fi}}} + +\let\normaldispl@y\displ@y + +\def\displ@y{\resetdisplaymatheq\normaldispl@y} + +\def\m@th{\mathsurround\zeropoint} % obsolete + +%D Text in math: + +\def\mathortext + {\ifmmode + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +% \defineactivecharacter _ {\mathortext{_}{\_}} text_text $a^2$ -\def\utfmathcommanddefault#1#2#3{\ctxlua{ - local cmd = mathematics.utfmathcommand("#1","") or "" - if cmd == "" then - commands.cs("#2","#3") - else - commands.cs(cmd) - end}} +% force text mode, will be overloaded later -% \let\math@normal@int\int \def\int{\math@normal@int\intlimits} +\ifx\text\undefined \let\text\hbox \fi \protect \endinput diff --git a/tex/context/base/math-ini.tex b/tex/context/base/math-ini.tex deleted file mode 100644 index 98738e500..000000000 --- a/tex/context/base/math-ini.tex +++ /dev/null @@ -1,688 +0,0 @@ -%D \module -%D [ file=math-ini, -%D version=2001.04.12, -%D title=\CONTEXT\ Math Macros, -%D subtitle=Basic Macros, -%D author={Hans Hagen \& Taco Hoekwater}, -%D date=\currentdate, -%D copyright=\PRAGMA] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -% todo: make all definitions global since file loaded only once - -%D This module provides namespaces for math fonts, thereby -%D permitting mixed usage of math fonts. Although not strictly -%D needed, we also provide a family name mapping mechanism as -%D used in the (original) AMS math definition files, but here -%D these names can recursively be remapped and if needed, -%D dynamically be changed. We've tried to minimize the number -%D of definition commands and use plain \TEX\ definitions as -%D fallback. We've tried to follow a couple of conventions -%D from plain and AMS math in order to achieve backward -%D compatinility. We also kept an eye on future usage of these -%D modules in the perspective of MathML and unicode fonts. - -\unprotect - -\def\@ml@{@ml@} % math list (used for collection) -\def\@mf@{@mf@} % math family -%def\@mh@{@mh@} % math handler (not used) -\def\@mt@{@mt@} % math token -\def\@mc@{@mc@} % math collection - -\def\@@mathlimopcomm#1{\mathop{#1}} %no \limits -\def\@@mathnolopcomm#1{\mathop{#1}\nolimits} -\def\@@mathboxcomm #1{\dontleavehmode\hbox{$\m@th#1$}} - -\chardef\mathordcode = 0 \let\mathordcomm \mathord -\chardef\mathopcode = 1 \let\mathopcomm \mathop -\chardef\mathbincode = 2 \let\mathbincomm \mathbin -\chardef\mathrelcode = 3 \let\mathrelcomm \mathrel -\chardef\mathopencode = 4 \let\mathopencomm \mathopen -\chardef\mathclosecode = 5 \let\mathclosecomm \mathclose -\chardef\mathpunctcode = 6 \let\mathpunctcomm \mathpunct -\chardef\mathalphacode = 7 \let\mathalphacomm \firstofoneargument -\chardef\mathinnercode = 0 \let\mathinnercomm \mathinner -\chardef\mathnothingcode= 0 \let\mathnothingcomm \firstofoneargument -\chardef\mathlimopcode = 1 \let\mathlimopcomm \@@mathlimopcomm -\chardef\mathnolopcode = 1 \let\mathnolopcomm \@@mathnolopcomm -\chardef\mathchoicecode = 0 \let\mathchoicecomm \@@mathchoicecomm -\chardef\mathboxcode = 0 \let\mathboxcomm \@@mathboxcomm - -\chardef\mathaccentcode = 8 -\chardef\mathradicalcode= 9 - -\def\@@mathchoicecomm#1{[todo #1]} - -\def\puremathcode#1{\the\csname math#1code\endcsname} -\def\puremathcomm#1{\csname math#1comm\endcsname} - -\newif\iftracemathcollection - -% Simple variant: -% -% \def\dohandlemathtoken#1% -% {\csname\@mt@ -% \ifcsname\@mt@\mathcollection#1\endcsname -% \mathcollection -% \else\ifcsname\@mt@\nomathcollection#1\endcsname -% \nomathcollection -% \fi\fi -% #1\endcsname} - -%D Because a command can have a different meaning in math -%D and in text mode, we provide a selector. We also provide -%D the pure alternatives as \type {\mathcharacter} and \type -%D {\textcharacter}. - -\ifx\dohandlecommand\undefined \wait \fi % troubles ! - -\def\mathcharacter\dohandlemathtoken -\def\textcharacter\dohandlecommand % better \dohandletexttoken - -% More clever layout: -% -% \def\dohandlemathtoken#1% -% {\csname -% \ifmmode -% \ifcsname\@mt@\mathcollection#1\endcsname -% \@mt@\mathcollection -% \else\ifcsname\@mt@\nomathcollection#1\endcsname -% \@mt@\nomathcollection -% \else\ifcsname\characterencoding#1\endcsname -% \characterencoding -% \else -% \nocharacterencoding -% \fi\fi\fi -% \else -% \ifcsname\characterencoding#1\endcsname -% \characterencoding -% \else -% \nocharacterencoding -% \fi -% \fi -% #1\endcsname} -% -% fallback to math when in text mode (handy for unicode vectors) - -\def\dohandlemathtoken#1% - {\csname - \ifmmode - \ifcsname\@mt@\mathcollection#1\endcsname - \@mt@\mathcollection - \else\ifcsname\@mt@\nomathcollection#1\endcsname - \@mt@\nomathcollection - \else\ifcsname\characterencoding#1\endcsname - \characterencoding - \else - \nocharacterencoding - \fi\fi\fi - \else - \ifcsname\characterencoding#1\endcsname - \characterencoding - \else\ifcsname\nocharacterencoding#1\endcsname - \nocharacterencoding - \else\ifcsname\@mt@\mathcollection#1\endcsname - \strippedcsname\mathematics\expandafter\endcsname\csname\@mt@\mathcollection - \else\ifcsname\@mt@\nomathcollection#1\endcsname - \strippedcsname\mathematics\expandafter\endcsname\csname\@mt@\nomathcollection - \else - \nocharacterencoding - \fi\fi\fi\fi - \fi - #1\endcsname} - -%D Now we redefine the text encoding handler. - -%D A better fallback: - -% Just ETEX which is the default nowadays. - -\def\dohandlemathtoken#1% - {\csname - \ifmmode - \ifcsname\@mt@\mathcollection:\outerencoding#1\endcsname - \@mt@\mathcollection:\outerencoding - \else\ifcsname\@mt@\mathcollection#1\endcsname - \@mt@\mathcollection - \else\ifcsname\@mt@\nomathcollection#1\endcsname - \@mt@\nomathcollection - \else\ifcsname\characterencoding#1\endcsname - \characterencoding - \else - \nocharacterencoding - \fi\fi\fi\fi - \else - \ifcsname\characterencoding#1\endcsname - \characterencoding - \else\ifcsname\nocharacterencoding#1\endcsname - \nocharacterencoding - \else\ifcsname\@mt@\mathcollection:\outerencoding#1\endcsname - \@mt@\mathcollection:\outerencoding - \else\ifcsname\@mt@\mathcollection#1\endcsname - \strippedcsname\mathematics\expandafter\endcsname\csname\@mt@\mathcollection - \else\ifcsname\@mt@\nomathcollection#1\endcsname - \strippedcsname\mathematics\expandafter\endcsname\csname\@mt@\nomathcollection - \else - \nocharacterencoding - \fi\fi\fi\fi\fi - \fi - #1\endcsname} - -\let\dohandlecommand\dohandlemathtoken - -\def\definefamilysynonym - {\dotripleempty\dodefinefamilysynonym} - -\def\dodefinefamilysynonym[#1][#2][#3]% [mathcollection] [] [] - {\ifthirdargument - \setvalue{\@mf@#1#2}{#3}% - \else - \setvalue{\@mf@ #1}{#2}% - \fi} - -\let\mathsubfamily\empty - -\def\purefamily #1{\csname \truefamily{#1}\mathsubfamily\s!fam\endcsname} -\def\purefamilyhex#1{\csname hex\truefamily{#1}\mathsubfamily\s!fam\endcsname} - -\def\truefamily#1% - {\ifcsname\@mf@\mathcollection#1\endcsname - \@EA\truefamily\csname\@mf@\mathcollection#1\endcsname - \else\ifcsname\@mf@#1\endcsname - \@EA\truefamily\csname\@mf@#1\endcsname - \else\ifcsname\@mf@\nomathcollection#1\endcsname - \@EA\truefamily\csname\@mf@\nomathcollection#1\endcsname - \else - #1% - \fi\fi\fi} - -\newif\ifdynamicmathfamilies \dynamicmathfamiliestrue % true per 2003.11.25; needed for mixed bold math - -\let\normalpurefamilyhex\purefamilyhex - -% todo: reset collection (tok legen) en opnieuw laden met true - -\def\definemathsymbol - {\dosixtupleempty\dodefinemathsymbol} - -\def\dodefinemathsymbol[#1][#2][#3][#4][#5][#6]% - {\unexpanded\setgvalue{#1}{\dohandlemathtoken{#1}}% - \ifdynamicmathfamilies \let\purefamilyhex\relax \fi - \setevalue{\@mt@\mathcollection#1}% - {\ifsixthargument - \ifnum\puremathcode{#2}=\mathradicalcode - \radical"% - \else - \delimiter"% - \ifnum\puremathcode{#2}>7 0\else\puremathcode{#2}\fi - \fi - \purefamilyhex{#3}\uchexnumbers{#4}% - \purefamilyhex{#5}\uchexnumbers{#6}\space - \else\iffourthargument - \ifnum\puremathcode{#2}=\mathaccentcode - \mathaccent\else\mathchar - \fi - "\ifnum\puremathcode{#2}>7 0\else\puremathcode{#2}\fi - \purefamilyhex{#3}\uchexnumbers{#4}\space - \fi\fi}% - \let\purefamilyhex\normalpurefamilyhex - \tracemathsymbol{#1}} - -\def\tracemathsymbol#1% - {\iftracemathcollection - {\endgraf - \hbox{\tex{#1}~:~{\mathematics{\getvalue{#1}{}}}} - \endgraf}% - \fi} - -\def\definemathcharacter - {\dosixtupleempty\dodefinemathcharacter} - -% \def\dodefinemathcharacter[#1][#2][#3][#4][#5][#6]% -% {\setmathtoks -% \ifdynamicmathfamilies \let\purefamilyhex\relax \fi -% \doifnumberelse{#1} -% {\scratchcounter#1} -% {\scratchcounter\@EA`\string#1}% -% \appendetoks -% \ifsixthargument -% \delcode\the\scratchcounter="% -% \ifnum\puremathcode{#2}>7 0\else\puremathcode{#2}\fi -% \purefamilyhex{#3}\uchexnumbers{#4}% -% \purefamilyhex{#5}\uchexnumbers{#6}\space -% \else\iffourthargument -% \mathcode\the\scratchcounter="% -% \ifnum\puremathcode{#2}>7 0\else\puremathcode{#2}\fi -% \purefamilyhex{#3}\uchexnumbers{#4}\space -% \fi\fi\to\mathtoks -% \let\purefamilyhex\normalpurefamilyhex -% \tracemathcharacter{#1}} - -\newtoks\mathscratchtoks - -\def\definemathcharacter - {\chardef\mathcharactermode\zerocount - \dosixtupleempty\dodefinemathcharacter} - -\def\redefinemathcharacter - {\chardef\mathcharactermode\plusone - \dosixtupleempty\dodefinemathcharacter} - -\def\dodefinemathcharacter[#1][#2][#3][#4][#5][#6]% - {\ifcase\mathcharactermode - \setmathtoks - \or - \let\mathtoks\mathscratchtoks \mathtoks\emptytoks - \fi - \ifdynamicmathfamilies \let\purefamilyhex\relax \fi - \doifnumberelse{#1} - {\scratchcounter#1} - {\scratchcounter\@EA`\string#1}% - \appendetoks - \ifsixthargument - \delcode\the\scratchcounter="% - \ifnum\puremathcode{#2}>7 0\else\puremathcode{#2}\fi - \purefamilyhex{#3}\uchexnumbers{#4}% - \purefamilyhex{#5}\uchexnumbers{#6}\space - \else\iffourthargument - \mathcode\the\scratchcounter="% - \ifnum\puremathcode{#2}>7 0\else\puremathcode{#2}\fi - \purefamilyhex{#3}\uchexnumbers{#4}\space - \fi\fi - \to \mathtoks - \let\purefamilyhex\normalpurefamilyhex - \ifcase\mathcharactermode - \expandafter\tracemathcharacter - \or - \the\mathtoks - \mathtoks\emptytoks - \expandafter\gobbleoneargument - \fi{#1}} % maybe lookahead - -\def\tracemathcharacter#1% - {\iftracemathcollection - {\endgraf - \doifnumberelse{#1} - {\hbox{\tttf\rawcharacter{#1}~:~{\mathematics{\rawcharacter{#1}}}}} - {\hbox{\type{#1}~:~{\mathematics{#1}}}} - \endgraf}% - \fi} - -\def\definemathcommand - {\dotripleempty\dodefinemathcommand} - -\def\dodefinemathcommand[#1][#2][#3]#4% command class args meaning - {\unexpanded\setgvalue{#1}{\dohandlemathtoken{#1}}% - \ifthirdargument - \processaction - [#3] - [one=>\setvalue{\@mt@\mathcollection#1}##1{\puremathcomm{#2}{#4{##1}}}, - two=>\setvalue{\@mt@\mathcollection#1}##1##2{\puremathcomm{#2}{#4{##1}{##2}}}]% - \else\ifsecondargument - \setvalue{\@mt@\mathcollection#1}{\puremathcomm{#2}{#4}}% - \else - \setvalue{\@mt@\mathcollection#1}{\puremathcomm{nothing}{#4}}% - \fi\fi - \tracemathcommand{#1}} - -\def\tracemathcommand#1% - {\iftracemathcollection - \endgraf\hbox{\tex{#1}~:~{\mathematics{\getvalue{#1}{}}}}\endgraf - \fi} - -\def\startmathcollection[#1]% - {\pushmacro\mathcollection - \setmathcollection{#1}} - -\def\setmathcollection#1% - {\edef\mathcollection{#1}% - \doifundefined{\@ml@\mathcollection} - {\expandafter\newtoks\csname\@ml@\mathcollection\endcsname}} - -\def\stopmathcollection - {\popmacro\mathcollection} - -\def\startrawmathcollection - {\startmathcollection} - -\def\stoprawmathcollection - {\stopmathcollection} - -\newtoks\mathtoks - -\def\setmathtoks - {\@EA\let\@EA\mathtoks\csname\@ml@\mathcollection\endcsname} - -\def\currentmathcollection{\mathcollection} - -\let\nomathcollection\s!default - -\def\enablemathcollection[#1]% - {\doifnot{#1}\s!default - {\setmathcollection\s!default - \the\csname\@ml@\mathcollection\endcsname}% - \setmathcollection{#1}% - \the\csname\@ml@\mathcollection\endcsname} - -% hook 'm into the font mechanism - -\definefilesynonym[\f!mathprefix\s!default][\f!mathprefix tex] - -\def\usemathcollection - {\dodoubleempty\dousemathcollection} - -\def\dousemathcollection[#1][#2]% - {\pushmacro\fontclass - \pushmacro\mathclass - \ifsecondargument - \edef\fontclass{#1}% - \edef\mathclass{#2}% - \else - \edef\mathclass{#1}% - \fi - \doinputonce{\truefilename{\f!mathprefix\mathclass}}% - \doifsomething\fontclass{\setevalue{\@mc@\fontclass\@mc@}{\mathclass}}% - \popmacro\mathclass - \popmacro\fontclass} - -\let\mathclass\nomathcollection - -\letvalue{\@mc@\@mc@}\nomathcollection - -% \def\autoenablemathcollection -% {\doifdefinedelse{\@mc@\fontclass\@mc@} -% {\enablemathcollection[\getvalue{\@mc@\fontclass\@mc@}]} -% {\enablemathcollection[\s!default]}} % ? ? ? - -\def\autoenablemathcollection - {\expanded{\enablemathcollection[\executeifdefined{\@mc@\fontclass\@mc@}\nomathcollection]}} - -\appendtoks\autoenablemathcollection\to\mathstrategies - -\fetchruntimecommand \showmathcharacters {\f!mathprefix\s!run} -\fetchruntimecommand \showmathtoken {\f!mathprefix\s!run} - -\def\resetmathcollection[#1]% - {\def\mathcollection{#1}% - \forgetdoingonce{\f!mathprefix\mathcollection}% - \setmathtoks - \ifx\mathtoks\relax\else\mathtoks\emptytoks\fi} - -%D \macros -%D {ifmathpunctuation, enablemathpunctuation, -%D definemathpunctuation} -%D -%D This will replace periods by comma's: -%D -%D \starttyping -%D \definemathpunctuation . textcomma textperiod -%D \definemathpunctuation , textcomma textcomma -%D -%D \appendtoks -%D \redefinemathcharacter [.] [ord] [mi] ["3B]% -%D \to \everymathpunctuation -%D \stoptyping - -% \newif\ifmathpunctuation -% -% \def\enablemathpunctuation{\mathpunctuationtrue} -% -% \def\definemathpunctuation #1 #2 #3 % -% {\appendtoks -% \initializemathpunctuation{#1}{#2}{#3}% -% \to\everymathematics} -% -% \def\initializemathpunctuation#1#2#3% sloowww -% {\ifmathpunctuation % hm move this test to everymath, or better a separate token list -% \mathcode`#1="8000 -% \defineactivecharacter #1 {\dohandlemathpunctuation{#2}{#3}}% -% \fi} -% -% \unexpanded\def\dohandlemathpunctuation#1#2% \if fails in mathml interval -% {\def\next{\csname\ifx\space\nexttoken#2\else#1\fi\endcsname}% -% \futurelet\nexttoken\next} - -\newtoks\everymathpunctuation - -\def\enablemathpunctuation % can be called inside math, so after \everymathematics - {\the\everymathpunctuation - \appendtoksonce - \the\everymathpunctuation - \to\everymathematics} - -\def\definemathpunctuation #1 #2 #3 % - {\appendtoks - \initializemathpunctuation{#1}{#2}{#3}% - \to\everymathpunctuation} - -\def\initializemathpunctuation#1#2#3% sloowww - {\mathcode`#1="8000 - \defineactivecharacter #1 {\dohandlemathpunctuation{#2}{#3}}} - -\unexpanded\def\dohandlemathpunctuation#1#2% \if fails in mathml interval - {\def\next{\csname\ifx\space\nexttoken#2\else#1\fi\endcsname}% - \futurelet\nexttoken\next} - -%D \startbuffer -%D \enablemathpunctuation$(1,2) (1, 2) (1{,}2) \hbox{foo, not bar}$ -%D \stopbuffer -%D -%D \typebuffer -%D -%D \blank{\getbuffer}\blank - -%D needed for sin, cos etc - -\def\mfunction #1{{\mr#1}} - -% \def\mlimitsfunction #1{\mathlimopcomm{{\mr#1}} -% \def\mnolimitsfunction#1{\mathnolopcomm{{\mr#1}} - -%D Taco posted this solution as response to a mail by Olivier, so -%D let's integrate it here. - -% \def\setmathfunctionstyle#1% rm ss tt -% {\def\mfunction##1% no families, just scaling a la text -% {\mathchoice -% {\hbox{\csname#1\endcsname\tf ##1}} -% {\hbox{\csname#1\endcsname\tf ##1}} -% {\hbox{\csname#1\endcsname\tfx ##1}} -% {\hbox{\csname#1\endcsname\tfxx##1}}}} - -\def\currentmscaledstyle{rm} % will be plugged into the typeface text=ss option - -\def\setmathfunctionstyle#1% rm ss tt - {\doifsomething{#1} - {\def\currentmscaledstyle{#1}% - \def\mathopnolimits##1{\mathop{\mscaledtext{##1}}\nolimits}% - \def\mfunction##1{\mscaledtext{##1}}}} - -\def\mscaledtext#1% - {\mathchoice - {\hbox{\csname\currentmscaledstyle\endcsname\tf #1}} - {\hbox{\csname\currentmscaledstyle\endcsname\tf #1}} - {\hbox{\csname\currentmscaledstyle\endcsname\tfx #1}} - {\hbox{\csname\currentmscaledstyle\endcsname\tfxx#1}}} - -%D We can force the way functions are typeset by manipulating the text -%D option: -%D -%D \starttyping -%D \definetypeface[iwona][ss][sans][iwona][default][encoding=texnansi] -%D \definetypeface[iwona][mm][math][iwona][default][encoding=texnansi,text=ss] -%D \stoptyping -%D -%D This hooks into the math handler with: - -\appendtoks - \setmathfunctionstyle\currentmathtextstyle -\to \everybodyfont - -%D Usage: -%D -%D \starttyping -%D \setmathfunctionstyle\fontstyle % or {rm} or {ss} or .. -%D \rm test $\sin{(x^{\sin(x^{\sin(x)})})}$ test -%D \ss test $\sin{(x^{\sin(x^{\sin(x)})})}$ test -%D \tt test $\sin{(x^{\sin(x^{\sin(x)})})}$ test -%D \stoptyping - -\edef\hexmrfam {0} \edef\hexbsfam {8} -\edef\hexmifam {1} \edef\hexbifam {9} -\edef\hexsyfam {2} \edef\hexscfam {A} -\edef\hexexfam {3} \edef\hextffam {B} -\edef\hexitfam {4} \edef\hexmafam {C} -\edef\hexslfam {5} \edef\hexmbfam {D} -\edef\hexbffam {6} \edef\hexmcfam {E} -\edef\hexnnfam {7} \edef\hexmdfam {F} - -\definefamilysynonym [default] [letters] [mr] -\definefamilysynonym [default] [operators] [sy] -\definefamilysynonym [default] [lcgreek] [mi] -\definefamilysynonym [default] [ucgreek] [mr] -\definefamilysynonym [default] [vargreek] [mi] -\definefamilysynonym [default] [mitfamily] [mi] -\definefamilysynonym [default] [calfamily] [sy] - -\definefamilysynonym [default] [0] [mr] -\definefamilysynonym [default] [1] [mi] -\definefamilysynonym [default] [2] [sy] -\definefamilysynonym [default] [3] [ex] - -\enablemathcollection[default] - -\usemathcollection [default] [tex] -\usemathcollection [default] [ams] -\usemathcollection [default] [uni] - -\enablemathcollection[default] - -%D Some goodies: - -\def\Angstrom{\nomathematics{\Aring}} - -%D Bold math: -%D -%D \starttyping -%D \usetypescript [lucida] [texnansi] -%D -%D \definetypeface [boldmath] [rm] [serif] -%D [lucida] [default] [encoding=texnansi] -%D \definetypeface [boldmath] [tt] [mono] -%D [lucida] [default] [encoding=texnansi] -%D \definetypeface [boldmath] [ss] [sans] -%D [lucida] [default] [encoding=texnansi] -%D \definetypeface [boldmath] [mm] [boldmath] -%D [lucida] [default] [encoding=texnansi] -%D -%D \switchtobodyfont[lucida,10pt] -%D -%D \showmathtoken{Gamma} $\Gamma \Delta \alpha \delta \zeta$ -%D -%D \switchtobodyfont[boldmath,10pt] -%D -%D \showmathtoken{Gamma} $\Gamma \Delta \alpha \delta \zeta$ -%D \stoptyping - -%D \macros -%D {nonknuthmode, donknuthmode} -%D -%D The underscore is frequently used in manuals but unfortunately \TEX\ prefers -%D it to be a math specific character. And since computer modern fonts didn't -%D have an underscore, one had to use commands to fake one. Nowadays we do -%D have underscores in latin modern, and since all other fonts have them, we -%D decided to get away from the restriction to use the underscore character in -%D text mode. -%D -%D \starttyping -%D \def\test#1{#1} -%D -%D \nonknuthmode $x_2$ x_2 \test{$x_2$} \test{x_2} -%D -%D \donknuthmode $x_2$ x_2 \test{$x_2$} \test{x_2} -%D \stoptyping -%D -%D The result is as expected: the first line typesets ok, while the second -%D one triggers an error message. - -\bgroup - - \ifx\normalsuber\undefined \def\normalsuber{_} \fi - \ifx\normalsuper\undefined \def\normalsuper{^} \fi - - \catcode`_=\active - \catcode`^=\active - - \gdef\nonknuthmode - {\appendtoks\let_\normalsuber\let^\normalsuper\to\everymathematics - \mathcode`_="8000 - \mathcode`^="8000 - \catcode`_=\@@other - \catcode`^=\@@other - \let\nonknuthmode\relax} - - \gdef\donknuthmode - {\catcode`_=\@@subscript - \catcode`^=\@@superscript} - -\egroup - -%D \macros -%D {checkdelimiters, fakeleftdelimiter, fakerightdelimiter} -%D -%D Handy for non matching situations (as with mathml): -%D -%D \starttyping -%D \checkdelimiters{... bla bla ...} -%D \fakeleftdelimiter -%D ... bla bla ... -%D \fakerightdelimiter -%D \stoptyping - -\newcount\delimitercount - -\def\leftfakedelimiter {\advance\delimitercount\minusone\gobbleoneargument}% -\def\rightfakedelimiter{\advance\delimitercount\plusone \gobbleoneargument}% - -\def\checkdelimiters#1% - {\delimitercount\zerocount - \setbox\scratchbox\hbox\bgroup - \let\left \leftfakedelimiter - \let\right\rightfakedelimiter - $#1\expandafter$\expandafter - \egroup - \expandafter\delimitercount\the\delimitercount\relax} - -\def\fakeleftdelimiter {\ifnum\delimitercount>\zerocount\left .\fi} -\def\fakerightdelimiter{\ifnum\delimitercount<\zerocount\right.\fi} - -% \def\scaledmathdelimiter#1#2% -% {\begingroup -% \scratchdimen\lineheight -% \hbox{$\left#2\vbox\!!to#1\scratchdimen{}\right.\n@space$}% -% \endgroup} -% -% \let\scaledmathdelimiter\@@dobig -% -% \def\scaledmathopen #1#2{\mathopen {\scaledmathdelimiter{#1}{#2}}} -% \def\scaledmathclose#1#2{\mathclose{\scaledmathdelimiter{#1}{#2}}} - -%D Needed for unicode: - -\def\nulloperator{\mathortext{\mathop{\null}}{\null}} - -%D Plugins. - -\loadmarkfile{math-ini} - -\protect \endinput - -\tracemathcollectiontrue - \input math-tex \page -\setupbodyfont[ams] \enablemathcollection[default] \input math-ams \page -\setupbodyfont[lbr] \enablemathcollection[lbr] \input math-lbr \page -\setupbodyfont[eul] \enablemathcollection[eul] \input math-eul \stoptext diff --git a/tex/context/base/math-inl.mkiv b/tex/context/base/math-inl.mkiv new file mode 100644 index 000000000..acbf02de7 --- /dev/null +++ b/tex/context/base/math-inl.mkiv @@ -0,0 +1,357 @@ +%D \module +%D [ file=math-inl, +%D version=2008.10.20, +%D title=\CONTEXT\ Math Macros, +%D subtitle=Inline, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA-ADE / Hans Hagen] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Math Macros / Inline} + +\unprotect + +%D \macros +%D {...} +%D +%D New and experimental: snapping big inline math! + +\newconditional\halfcrazymathlines % \settrue\halfcrazymathlines +\newconditional\crazymathsnapping % \settrue\crazymathsnapping + +\appendtoks + \doifelse\@@mtgrid\v!yes \settrue\setfalse\crazymathsnapping + \doifelse\@@mtstep\v!halfline\settrue\setfalse\halfcrazymathlines +\to \everysetuptextformulas + +\setuptextformulas + [\c!grid=\v!yes, + \c!step=\v!line] + +\newcount\crazymathhack + +\let\lastcrazymathline \!!zeropoint +\let\lastcrazymathpage \!!zerocount +\let\lastcrazymathprelines \!!zerocount +\let\lastcrazymathpostlines\!!zerocount + +\def\crazymathtag{amh:\the\crazymathhack} +\def\crazytexttag{\v!text:\lastcrazymathpage} + +\def\crazymathindent{\hskip\MPx\crazymathtag\hskip-\MPx\crazytexttag} + +\def\flushcrazymathbox + {\nextboxht\strutheight + \nextboxdp\strutdepth + \hbox{\iftracegridsnapping\ruledhbox\fi{\flushnextbox}}} + +\def\snappedinlineformula + {\dosingleempty\dosnappedinlineformula} + +%D \starttabulate[|Tl|l|] +%D \NC - \NC half lines \NC \NR +%D \NC + \NC full lines \NC \NR +%D \NC = \NC force \NC \NR +%D \NC < \NC force, minus pre \NC \NR +%D \NC > \NC force, minus post \NC \NR +%D \stoptabulate + +\def\inlinemathmargin{1pt} + +\settrue\autocrazymathsnapping + +% FROM NOW ON, CHANGES AS OPTIONS + +% TODO: SKYLINE (PREV LINE POS SCAN) + +% we can rewrite this in lua but maybe we don't need it +% any more when we have proper snapping anyway + +\def\dosnappedinlineformula[#1]#2% + {\ifvmode\dontleavehmode\fi % tricky + \strut % prevents funny space at line break + \begingroup % interesting: \bgroup can make \vadjust disappear + \ifconditional\crazymathsnapping + \ifgridsnapping + \checktextbackgrounds % we need pos tracking, to be made less redundant + \donetrue + \else + \donefalse + \fi + \else + \donefalse + \fi + \!!doneafalse % forced or not auto + \!!donebfalse % too heigh + \!!donecfalse % too low + \!!donedfalse % less before + \!!doneefalse % less after + \ifdone + \setbox\nextbox\hbox{$#2$}% + \iftracegridsnapping + \setbox\nextbox\ruledhbox + {\incolortrue\localcolortrue + \backgroundline[gray]{\showstruts\strut\flushnextbox}}% + \fi + \def\docommand##1% + {\doif{##1}-{\settrue \halfcrazymathlines}% + \doif{##1}+{\setfalse\halfcrazymathlines}% + \doif{##1}={\!!doneatrue}% + \doif{##1}<{\!!donedtrue}% + \doif{##1}>{\!!doneetrue}}% + \processcommalist[#1]\docommand + \if!!doneb + \if!!donec \else + \setfalse\halfcrazymathlines + \fi + \else + \if!!donec + \setfalse\halfcrazymathlines + \fi + \fi + \donefalse + \if!!donea + \donetrue + \scratchdimen \nextboxht + \advance\scratchdimen .5\lineheight + \nextboxht\scratchdimen + \scratchdimen \nextboxdp + \advance\scratchdimen .5\lineheight + \nextboxdp\scratchdimen + \else\ifdim\nextboxht>\strutht + \donetrue + \else\ifdim\nextboxdp>\strutdp + \donetrue + \fi\fi\fi + \ifconditional\autocrazymathsnapping \else \if!!donea \else + % don't compensate, just snap to strut + \donefalse + % signal for next else, snap line to strut + \!!doneatrue + \fi \fi + \fi + \ifdone + % analyze height + \scratchdimen\inlinemathmargin + \advance\scratchdimen \strutht + \ifdim\nextboxht<\scratchdimen \else \!!donebtrue \fi + % analyze depth + \scratchdimen\inlinemathmargin + \advance\scratchdimen \strutdp + \ifdim\nextboxdp<\scratchdimen \else \!!donectrue \fi + % analyzed or forced + \ifdone + \global\advance\crazymathhack\plusone + \donefalse + \ifnum\MPp\crazymathtag=\lastcrazymathpage\relax + \ifdim\MPy\crazymathtag=\lastcrazymathline\relax + \donetrue + \fi + \fi + \ifnum\MPp\crazymathtag=\zerocount \donefalse \fi + \ifdim\MPy\crazymathtag=\zeropoint \donefalse \fi + \ifdone + % same page and same line + \else + \global\let\lastcrazymathprelines \!!zerocount + \global\let\lastcrazymathpostlines\!!zerocount + \xdef\lastcrazymathpage{\MPp\crazymathtag}% + \xdef\lastcrazymathline{\MPy\crazymathtag}% + \fi + \if!!doneb + % \getrawnoflines\nextboxht + \scratchdimen\nextboxht + \advance\scratchdimen-\strutht + \getnoflines\scratchdimen + \if!!doned \advance\noflines\minusone \fi + \scratchcounter\noflines + \advance\noflines-\lastcrazymathprelines\relax + \ifnum\noflines>\zerocount + \xdef\lastcrazymathprelines{\the\scratchcounter}% + \scratchdimen\noflines\lineheight + \ifconditional\halfcrazymathlines + \advance\scratchdimen-.5\lineheight + \fi + \advance\scratchdimen-\strutdepth + \setbox\scratchbox\null + \wd\scratchbox2\bodyfontsize + \ht\scratchbox\scratchdimen + \dp\scratchbox\strutdepth + %%% top correction code (see below) + \normalvadjust pre + {%\allowbreak % sometimes breaks spacing + \forgetall + \crazymathindent + \iftracegridsnapping + \setbox\scratchbox\hbox + {\incolortrue\localcolortrue\green + \ruledhbox{\box\scratchbox}}% + \fi + \box\scratchbox + \endgraf + \nobreak}% + \else\ifnum\scratchcounter>\zerocount + \normalvadjust pre + {\nobreak}% + \fi\fi + \fi + \if!!donec + % \getrawnoflines\nextboxdp + \scratchdimen\nextboxdp + \advance\scratchdimen-\strutdp + \getnoflines\scratchdimen + \if!!donee \advance\noflines\minusone \fi + \scratchcounter\noflines + \advance\noflines-\lastcrazymathpostlines\relax + \ifnum\noflines>\zerocount + \donetrue + \else\ifnum\lastcrazymathpostlines=\zerocount + \donetrue + \else + \donefalse + \fi\fi + \else + \donefalse + \fi + \ifdone + \xdef\lastcrazymathpostlines{\the\scratchcounter}% + \ifnum\lastcrazymathpostlines=\zerocount + \global\let\lastcrazymathpostlines\!!plusone + \fi + \hbox{\setposition\crazymathtag\flushcrazymathbox}% + \scratchdimen\noflines\lineheight + \advance\scratchdimen-\lineheight + \advance\scratchdimen+\strutheight + \ifdim\scratchdimen>\zeropoint \else + \scratchdimen\strutheight % todo : test for half lines + \fi + \ifconditional\halfcrazymathlines + \advance\scratchdimen-.5\lineheight + \fi + \setbox\scratchbox\null + \wd\scratchbox2\bodyfontsize + \ht\scratchbox\scratchdimen + \dp\scratchbox\strutdepth + \normalvadjust + {\forgetall + \crazymathindent + \iftracegridsnapping + \setbox\scratchbox\hbox + {\incolortrue\localcolortrue\color[blue]{\ruledhbox{\box\scratchbox}}}% + \fi + \box\scratchbox + \endgraf + % precaution: else we stick below the text bottom + \ifconditional\halfcrazymathlines + \allowbreak + \else + \vskip-\lineheight + \vskip \lineheight + \fi}% + \else + \hbox{\setposition\crazymathtag\flushcrazymathbox}% + \fi + \else + \flushcrazymathbox + \fi + \else\if!!donea + \flushcrazymathbox + \else + \mathematics{#2}% + \fi\fi + \endgroup} + +\let\tform\mathematics +\let\gform\snappedinlineformula + +% test set: +% +% \startbuffer +% Crazy math \gform {1+x} or \gform {\dorecurse {100} {1+} 1 = +% 101} and even gore crazy \gform {2^{2^2}_{1_1}} +% again\dorecurse {20} { and again} \gform {\sqrt {\frac +% {x^{5^5}} {\frac {1} {2}}}} even gore\dorecurse {50} { and +% gore} \tform {\dorecurse {12} {\gform {\sqrt {\frac +% {x^{5^5}} {3}}}+\gform {\sqrt {\frac {x^{5^5}} {\frac {1} +% {2}}}}+}x=10}\dorecurse{20} { super crazy math}: \tform +% {\dorecurse {30} {\gform {\sqrt {\frac {x^{5^5}} {3}}}+ +% \gform {\sqrt {\frac {x^{5^5}} {\frac {1} {2}}}}+ }x = 10}, +% and we're\dorecurse {20} { done}! +% \stopbuffer +% +% \setupcolors[state=start] \setuppapersize[S6][S6] +% +% \showgrid \tracegridsnappingtrue \showstruts +% +% \starttext +% \setuplayout[grid=yes,lines=15]\getbuffer \page +% \setuplayout[grid=yes,lines=16]\getbuffer \page +% \setuplayout[grid=yes,lines=17]\getbuffer \page +% \setuplayout[grid=yes,lines=18]\getbuffer \page +% \setuplayout[grid=yes,lines=19]\getbuffer \page +% \stoptext +% +% test +% +% \startregels +% \gform[<]{35 \cdot p^{\frac{3}{4}} = 70} +% \gform{12{,}4 \cdot d^3 = 200} +% \gform{a \cdot x^b}. +% \gform{12x^6 \cdot \negative 3x^4} +% \gform{\frac{12x^6}{\negative 3x^4}} +% \gform{(4x^2)^3} +% \gform{4x \sqrt{x} \cdot 3x^2} +% \gform{\frac{2x^4}{4x \sqrt{x}}} +% \gform{y = a \cdot x^b}. +% \gform{y_1 = \frac{15x^2}{x}} +% \gform{y_2 = x \cdot \sqrt{x}} +% \gform{y_3 = \frac{6x^3}{x^2}} +% \gform[<]{y_4 = \left(2x^2\right)^{\frac{1}{2}}} +% \gform{y_1 = \frac{4x^5}{x^2}} +% \gform{y_2 = 4 \cdot \sqrt{x}} +% \gform{y_3 = 4x^3} +% \gform{y_4 = \frac{100x}{\sqrt{x}}} +% \gform[<]{y_5 = 4 \cdot x^{\frac{1}{2}}} +% \gform{y_6 = \frac{1}{2} x \cdot 4x^2} +% \gform{y_7 = 2 \cdot x^3} +% \gform{y_8 = 100 \cdot x^{\frac{1}{2}}} +% \gform{4x^8 \cdot 8x^3} +% \gform{\frac{4x^8}{8x^3}} +% \gform{\left(\negative3x^4\right)^3} +% \gform{x^3 \sqrt{x} \cdot 3x^2} +% \gform{\frac{6x^3}{x^2 \sqrt{x}}} +% \gform{\frac{6}{2x^4}} +% \gform{\frac{1}{3x^6}} +% \gform{\frac{12x^8}{4x^{10}}} +% \gform{\frac{4}{\sqrt{x}}} +% \gform{\frac{1}{2x \sqrt{x}}} +% \gform{\frac{2{,}25}{p} = 0{,}35} +% \gform{4{,}50 + \frac{300}{k} = 4{,}70} +% \gform{\frac{1200}{k+12} - 42 = 6} +% \stopregels + +%D \macros +%D {enableautomath} +%D +%D The next one can be dangerous, but handy in controlled +%D situations. + +\bgroup \catcode`\$=\active + +\gdef\enableautomath + {\catcode`\$=\active + \def$##1${\snappedinlineformula{##1}}} + +% \gdef\enableautomath +% {\catcode`\$=\active +% \def${\doifnextcharelse$\doautodmath\doautoimath}% +% \def\doautoimath##1${\snappedinlineformula{##1}}% +% \def\doautodmath$##1$${\startformula##1\stopformula}} + +\egroup + +\protect \endinput diff --git a/tex/context/base/math-int.mkiv b/tex/context/base/math-int.mkiv new file mode 100644 index 000000000..8ac2d4776 --- /dev/null +++ b/tex/context/base/math-int.mkiv @@ -0,0 +1,87 @@ +%D \module +%D [ file=math-int, +%D version=2007.07.19, +%D title=\CONTEXT\ Math Macros, +%D subtitle=Scripts, +%D author={Hans Hagen \& Taco Hoekwater \& Aditya Mahajan}, +%D date=\currentdate, +%D copyright=\PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Math Macros / Integrals} + +\unprotect + +%D \startbuffer +%D $\int_a^b f(x) dx$ and also +%D $\iint_a^b f(x,y) dxdy$, $\iiint_a^b f(x,y) dxdy$, +%D $\iiiint_a^b f(x) dx$ +%D \startformula +%D \int_a^b f(x) dx \quad +%D \iint_a^b f(x) dx \quad +%D \iiint_a^b f(x) dx \quad +%D \iiiint_a^b f(x) dx \quad +%D \stopformula +%D \stopbuffer +%D +%D Default: \getbuffer +%D +%D Displaylimits: \setupmathematics[integral=displaylimits] \getbuffer +%D +%D Limits: \setupmathematics[integral=limits] \getbuffer + +\chardef\intlimitcode\zerocount % 0 nolimits 1 displaylimits 2 limits + +\def\intlimits + {\ifcase\intlimitcode \nolimits \or \displaylimits \or \limits \fi} + +\ifx\v!integral\undefined \def\v!integral{integral} \fi + +\appendtoks + \processaction + [\mathematicsparameter\v!integral] + [ nolimits=>\chardef\intlimitcode\zerocount, + displaylimits=>\chardef\intlimitcode\plusone, + limits=>\chardef\intlimitcode\plustwo]% +\to \everysetupmathematics + +\setupmathematics + [\v!integral=nolimits] + +%D More integrals (AM): + +\definemathcommand [iint] {\repeatintegral\plusone } +\definemathcommand [iiint] {\repeatintegral\plustwo } +\definemathcommand [iiiint] {\repeatintegral\plusthree} + +\def\repeatintegral#1% + {\scratchtoks\emptytoks + \let\dointlimits\donothing + \let\dodointlimits\intlimits + \dorecurse{#1}{\appendtoks \intop \dointkern \to \scratchtoks} + \appendtoks \intop \dointlimits \dodointlimits \to \scratchtoks + \edef\dodorepeatintegral{\the\scratchtoks}% + \futurelet\next\dorepeatintegral} + +%D If the \type{\limits} option is used after \type{\iint}, use +%D \type{\mathop} and fudge the left hand space a bit to make the +%D subscript visually centered. + +\def\dointkern + {\mkern-6mu\mathchoice{\mkern-3mu}{}{}{}} + +\def\dorepeatintegral + {\ifx\next\limits \dointlimitcorrection \else + \ifx\next\displaylimits \dointlimitcorrection \else + \ifx\next\nolimits \donothing \else + \ifcase\intlimitcode\else \dointlimitcorrection \fi\fi\fi\fi + \dodorepeatintegral} + +\def\dointlimitcorrection + {\mkern-7mu\mathchoice{\mkern-2mu}{}{}{}% + \mathop\bgroup\mkern7mu\mathchoice{\mkern2mu}{}{}{}\let\dointlimits\egroup} + +\protect \endinput diff --git a/tex/context/base/math-lbr.tex b/tex/context/base/math-lbr.tex index ecc3632b1..7ac7c3aff 100644 --- a/tex/context/base/math-lbr.tex +++ b/tex/context/base/math-lbr.tex @@ -394,12 +394,12 @@ \stopmathcollection \def\LBRroot#1#2% - {\setbox\z@\hbox{$\m@th#1\sqrt{#2}$} - \dimen@\ht\z@ \advance\dimen@-\dp\z@ - \mkern5mu\raise.6\dimen@\copy\rootbox \mkern-7.5mu \box\z@} + {\setbox\zerocount\hbox{$\mathsurround\zeropoint#1\sqrt{#2}$} + \dimen@\ht\zerocount \advance\dimen@-\dp\zerocount + \mkern5mu\raise.6\dimen@\copy\rootbox \mkern-7.5mu \box\zerocount} \def\LBRmatrix#1% - {\null\,\vcenter{\normalbaselines\m@th + {\null\,\vcenter{\normalbaselines\mathsurround\zeropoint \ialign{\hfil$##$\hfil&&\quad\hfil$##$\hfil\crcr \mathstrut\crcr\noalign{\kern-0.9\baselineskip} #1\crcr\mathstrut\crcr\noalign{\kern-0.9\baselineskip}}}\,} diff --git a/tex/context/base/math-map.lua b/tex/context/base/math-map.lua new file mode 100644 index 000000000..0229790c2 --- /dev/null +++ b/tex/context/base/math-map.lua @@ -0,0 +1,365 @@ +if not modules then modules = { } end modules ['math-map'] = { + version = 1.001, + comment = "companion to math-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +--[[ldx-- +Remapping mathematics alphabets.
+--ldx]]-- + +-- oldstyle: not really mathematics but happened to be part of +-- the mathematics fonts in cmr +-- +-- persian: we will also provide mappers for other +-- scripts + +-- todo: alphabets namespace +-- maybe: script/scriptscript dynamic, + +local type, next = type, next + +mathematics = mathematics or { } + +-- we could use one level less and have tf etc be tables directly but the +-- following approach permits easier remapping of a-a, A-Z and 0-9 to +-- fallbacks; symbols is currently mostly greek + +mathematics.alphabets = { + regular = { + tf = { + digits = 0x00030, + ucletters = 0x00041, + lcletters = 0x00061, + symbols = { + [0x0391]=0x0391, [0x0392]=0x0392, [0x0393]=0x0393, [0x0394]=0x0394, [0x0395]=0x0395, + [0x0396]=0x0396, [0x0397]=0x0397, [0x0398]=0x0398, [0x0399]=0x0399, [0x039A]=0x039A, + [0x039B]=0x039B, [0x039C]=0x039C, [0x039D]=0x039D, [0x039E]=0x039E, [0x039F]=0x039F, + [0x03A0]=0x03A0, [0x03A1]=0x03A1, [0x03A3]=0x03A3, [0x03A4]=0x03A4, [0x03A5]=0x03A5, + [0x03A6]=0x03A6, [0x03A7]=0x03A7, [0x03A8]=0x03A8, [0x03A9]=0x03A9, [0x03B1]=0x03B1, + [0x03B2]=0x03B2, [0x03B3]=0x03B3, [0x03B4]=0x03B4, [0x03B5]=0x03B5, [0x03B6]=0x03B6, + [0x03B7]=0x03B7, [0x03B8]=0x03B8, [0x03B9]=0x03B9, [0x03BA]=0x03BA, [0x03BB]=0x03BB, + [0x03BC]=0x03BC, [0x03BD]=0x03BD, [0x03BE]=0x03BE, [0x03BF]=0x03BF, [0x03C0]=0x03C0, + [0x03C1]=0x03C1, [0x03C2]=0x03C2, [0x03C3]=0x03C3, [0x03C4]=0x03C4, [0x03C5]=0x03C5, + [0x03C6]=0x03C6, [0x03C7]=0x03C7, [0x03C8]=0x03C8, [0x03C9]=0x03C9, [0x03D1]=0x03D1, + [0x03D5]=0x03D5, [0x03D6]=0x03D6, [0x03F0]=0x03F0, [0x03F1]=0x03F1, [0x03F4]=0x03F4, + [0x03F5]=0x03F5, [0x2202]=0x2202, [0x2207]=0x2207, + }, + }, + it = { + ucletters = 0x1D434, + lcletters = { -- H + [0x00061]=0x1D44E, [0x00062]=0x1D44F, [0x00063]=0x1D450, [0x00064]=0x1D451, [0x00065]=0x1D452, + [0x00066]=0x1D453, [0x00067]=0x1D454, [0x00068]=0x0210E, [0x00069]=0x1D456, [0x0006A]=0x1D457, + [0x0006B]=0x1D458, [0x0006C]=0x1D459, [0x0006D]=0x1D45A, [0x0006E]=0x1D45B, [0x0006F]=0x1D45C, + [0x00070]=0x1D45D, [0x00071]=0x1D45E, [0x00072]=0x1D45F, [0x00073]=0x1D460, [0x00074]=0x1D461, + [0x00075]=0x1D462, [0x00076]=0x1D463, [0x00077]=0x1D464, [0x00078]=0x1D465, [0x00079]=0x1D466, + [0x0007A]=0x1D467, + }, + symbols = { + [0x0391]=0x1D6E2, [0x0392]=0x1D6E3, [0x0393]=0x1D6E4, [0x0394]=0x1D6E5, [0x0395]=0x1D6E6, + [0x0396]=0x1D6E7, [0x0397]=0x1D6E8, [0x0398]=0x1D6E9, [0x0399]=0x1D6EA, [0x039A]=0x1D6EB, + [0x039B]=0x1D6EC, [0x039C]=0x1D6ED, [0x039D]=0x1D6EE, [0x039E]=0x1D6EF, [0x039F]=0x1D6F0, + [0x03A0]=0x1D6F1, [0x03A1]=0x1D6F2, [0x03A3]=0x1D6F4, [0x03A4]=0x1D6F5, [0x03A5]=0x1D6F6, + [0x03A6]=0x1D6F7, [0x03A7]=0x1D6F8, [0x03A8]=0x1D6F9, [0x03A9]=0x1D6FA, [0x03B1]=0x1D6FC, + [0x03B2]=0x1D6FD, [0x03B3]=0x1D6FE, [0x03B4]=0x1D6FF, [0x03B5]=0x1D700, [0x03B6]=0x1D701, + [0x03B7]=0x1D702, [0x03B8]=0x1D703, [0x03B9]=0x1D704, [0x03BA]=0x1D705, [0x03BB]=0x1D706, + [0x03BC]=0x1D707, [0x03BD]=0x1D708, [0x03BE]=0x1D709, [0x03BF]=0x1D70A, [0x03C0]=0x1D70B, + [0x03C1]=0x1D70C, [0x03C2]=0x1D70D, [0x03C3]=0x1D70E, [0x03C4]=0x1D70F, [0x03C5]=0x1D710, + [0x03C6]=0x1D711, [0x03C7]=0x1D712, [0x03C8]=0x1D713, [0x03C9]=0x1D714, [0x03D1]=0x1D717, + [0x03D5]=0x1D719, [0x03D6]=0x1D71B, [0x03F0]=0x1D718, [0x03F1]=0x1D71A, [0x03F4]=0x1D6F3, + [0x03F5]=0x1D716, [0x2202]=0x1D715, [0x2207]=0x1D6FB, + }, + }, + bf= { + digits = 0x1D7CE, + ucletters = 0x1D400, + lcletters = 0x1D41A, + symbols = { + [0x0391]=0x1D6A8, [0x0392]=0x1D6A9, [0x0393]=0x1D6AA, [0x0394]=0x1D6AB, [0x0395]=0x1D6AC, + [0x0396]=0x1D6AD, [0x0397]=0x1D6AE, [0x0398]=0x1D6AF, [0x0399]=0x1D6B0, [0x039A]=0x1D6B1, + [0x039B]=0x1D6B2, [0x039C]=0x1D6B3, [0x039D]=0x1D6B4, [0x039E]=0x1D6B5, [0x039F]=0x1D6B6, + [0x03A0]=0x1D6B7, [0x03A1]=0x1D6B8, [0x03A3]=0x1D6BA, [0x03A4]=0x1D6BB, [0x03A5]=0x1D6BC, + [0x03A6]=0x1D6BD, [0x03A7]=0x1D6BE, [0x03A8]=0x1D6BF, [0x03A9]=0x1D6C0, [0x03B1]=0x1D6C2, + [0x03B2]=0x1D6C3, [0x03B3]=0x1D6C4, [0x03B4]=0x1D6C5, [0x03B5]=0x1D6C6, [0x03B6]=0x1D6C7, + [0x03B7]=0x1D6C8, [0x03B8]=0x1D6C9, [0x03B9]=0x1D6CA, [0x03BA]=0x1D6CB, [0x03BB]=0x1D6CC, + [0x03BC]=0x1D6CD, [0x03BD]=0x1D6CE, [0x03BE]=0x1D6CF, [0x03BF]=0x1D6D0, [0x03C0]=0x1D6D1, + [0x03C1]=0x1D6D2, [0x03C2]=0x1D6D3, [0x03C3]=0x1D6D4, [0x03C4]=0x1D6D5, [0x03C5]=0x1D6D6, + [0x03C6]=0x1D6D7, [0x03C7]=0x1D6D8, [0x03C8]=0x1D6D9, [0x03C9]=0x1D6DA, [0x03D1]=0x1D6DD, + [0x03D5]=0x1D6DF, [0x03D6]=0x1D6E1, [0x03F0]=0x1D6DE, [0x03F1]=0x1D6E0, [0x03F4]=0x1D6B9, + [0x03F5]=0x1D6DC, [0x2202]=0x1D6DB, [0x2207]=0x1D6C1, + }, + }, + bi = { + ucletters = 0x1D468, + lcletters = 0x1D482, + symbols = { + [0x0391]=0x1D71C, [0x0392]=0x1D71D, [0x0393]=0x1D71E, [0x0394]=0x1D71F, [0x0395]=0x1D720, + [0x0396]=0x1D721, [0x0397]=0x1D722, [0x0398]=0x1D723, [0x0399]=0x1D724, [0x039A]=0x1D725, + [0x039B]=0x1D726, [0x039C]=0x1D727, [0x039D]=0x1D728, [0x039E]=0x1D729, [0x039F]=0x1D72A, + [0x03A0]=0x1D72B, [0x03A1]=0x1D72C, [0x03A3]=0x1D72E, [0x03A4]=0x1D72F, [0x03A5]=0x1D730, + [0x03A6]=0x1D731, [0x03A7]=0x1D732, [0x03A8]=0x1D733, [0x03A9]=0x1D734, [0x03B1]=0x1D736, + [0x03B2]=0x1D737, [0x03B3]=0x1D738, [0x03B4]=0x1D739, [0x03B5]=0x1D73A, [0x03B6]=0x1D73B, + [0x03B7]=0x1D73C, [0x03B8]=0x1D73D, [0x03B9]=0x1D73E, [0x03BA]=0x1D73F, [0x03BB]=0x1D740, + [0x03BC]=0x1D741, [0x03BD]=0x1D742, [0x03BE]=0x1D743, [0x03BF]=0x1D744, [0x03C0]=0x1D745, + [0x03C1]=0x1D746, [0x03C2]=0x1D747, [0x03C3]=0x1D748, [0x03C4]=0x1D749, [0x03C5]=0x1D74A, + [0x03C6]=0x1D74B, [0x03C7]=0x1D74C, [0x03C8]=0x1D74D, [0x03C9]=0x1D74E, [0x03D1]=0x1D751, + [0x03D5]=0x1D753, [0x03D6]=0x1D755, [0x03F0]=0x1D752, [0x03F1]=0x1D754, [0x03F4]=0x1D72D, + [0x03F5]=0x1D750, [0x2202]=0x1D74F, [0x2207]=0x1D735, + }, + }, + }, + sansserif = { + tf = { + digits = 0x1D7E2, + ucletters = 0x1D5A0, + lcletters = 0x1D5BA, + }, + it = { + ucletters = 0x1D608, + lcletters = 0x1D622, + }, + bf = { + digits = 0x1D7EC, + ucletters = 0x1D5D4, + lcletters = 0x1D5EE, + symbols = { + [0x0391]=0x1D756, [0x0392]=0x1D757, [0x0393]=0x1D758, [0x0394]=0x1D759, [0x0395]=0x1D75A, + [0x0396]=0x1D75B, [0x0397]=0x1D75C, [0x0398]=0x1D75D, [0x0399]=0x1D75E, [0x039A]=0x1D75F, + [0x039B]=0x1D760, [0x039C]=0x1D761, [0x039D]=0x1D762, [0x039E]=0x1D763, [0x039F]=0x1D764, + [0x03A0]=0x1D765, [0x03A1]=0x1D766, [0x03A3]=0x1D768, [0x03A4]=0x1D769, [0x03A5]=0x1D76A, + [0x03A6]=0x1D76B, [0x03A7]=0x1D76C, [0x03A8]=0x1D76D, [0x03A9]=0x1D76E, [0x03B1]=0x1D770, + [0x03B2]=0x1D771, [0x03B3]=0x1D772, [0x03B4]=0x1D773, [0x03B5]=0x1D774, [0x03B6]=0x1D775, + [0x03B7]=0x1D776, [0x03B8]=0x1D777, [0x03B9]=0x1D778, [0x03BA]=0x1D779, [0x03BB]=0x1D77A, + [0x03BC]=0x1D77B, [0x03BD]=0x1D77C, [0x03BE]=0x1D77D, [0x03BF]=0x1D77E, [0x03C0]=0x1D77F, + [0x03C1]=0x1D780, [0x03C2]=0x1D781, [0x03C3]=0x1D782, [0x03C4]=0x1D783, [0x03C5]=0x1D784, + [0x03C6]=0x1D785, [0x03C7]=0x1D786, [0x03C8]=0x1D787, [0x03C9]=0x1D788, [0x03D1]=0x1D78B, + [0x03D5]=0x1D78D, [0x03D6]=0x1D78F, [0x03F0]=0x1D78C, [0x03F1]=0x1D78E, [0x03F4]=0x1D767, + [0x03F5]=0x1D78A, [0x2202]=0x1D789, [0x2207]=0x1D76F, + }, + }, + bi = { + ucletters = 0x1D63C, + lcletters = 0x1D656, + symbols = { + [0x0391]=0x1D790, [0x0392]=0x1D791, [0x0393]=0x1D792, [0x0394]=0x1D793, [0x0395]=0x1D794, + [0x0396]=0x1D795, [0x0397]=0x1D796, [0x0398]=0x1D797, [0x0399]=0x1D798, [0x039A]=0x1D799, + [0x039B]=0x1D79A, [0x039C]=0x1D79B, [0x039D]=0x1D79C, [0x039E]=0x1D79D, [0x039F]=0x1D79E, + [0x03A0]=0x1D79F, [0x03A1]=0x1D7A0, [0x03A3]=0x1D7A2, [0x03A4]=0x1D7A3, [0x03A5]=0x1D7A4, + [0x03A6]=0x1D7A5, [0x03A7]=0x1D7A6, [0x03A8]=0x1D7A7, [0x03A9]=0x1D7A8, [0x03B1]=0x1D7AA, + [0x03B2]=0x1D7AB, [0x03B3]=0x1D7AC, [0x03B4]=0x1D7AD, [0x03B5]=0x1D7AE, [0x03B6]=0x1D7AF, + [0x03B7]=0x1D7B0, [0x03B8]=0x1D7B1, [0x03B9]=0x1D7B2, [0x03BA]=0x1D7B3, [0x03BB]=0x1D7B4, + [0x03BC]=0x1D7B5, [0x03BD]=0x1D7B6, [0x03BE]=0x1D7B7, [0x03BF]=0x1D7B8, [0x03C0]=0x1D7B9, + [0x03C1]=0x1D7BA, [0x03C2]=0x1D7BB, [0x03C3]=0x1D7BC, [0x03C4]=0x1D7BD, [0x03C5]=0x1D7BE, + [0x03C6]=0x1D7BF, [0x03C7]=0x1D7C0, [0x03C8]=0x1D7C1, [0x03C9]=0x1D7C2, [0x03D1]=0x1D7C5, + [0x03D5]=0x1D7C7, [0x03D6]=0x1D7C9, [0x03F0]=0x1D7C6, [0x03F1]=0x1D7C8, [0x03F4]=0x1D7A1, + [0x03F5]=0x1D7C4, [0x2202]=0x1D7C3, [0x2207]=0x1D7A9, + }, + }, + }, + monospaced = { + tf = { + digits = 0x1D7F6, + ucletters = 0x1D670, + lcletters = 0x1D68A, + }, + }, + blackboard = { -- ok + tf = { + digits = 0x1D7D8, + ucletters = { -- C H N P Q R Z + [0x00041]=0x1D538, [0x00042]=0x1D539, [0x00043]=0x02102, [0x00044]=0x1D53B, [0x00045]=0x1D53C, + [0x00046]=0x1D53D, [0x00047]=0x1D53E, [0x00048]=0x0210D, [0x00049]=0x1D540, [0x0004A]=0x1D541, + [0x0004B]=0x1D542, [0x0004C]=0x1D543, [0x0004D]=0x1D544, [0x0004E]=0x02115, [0x0004F]=0x1D546, + [0x00050]=0x02119, [0x00051]=0x0211A, [0x00052]=0x0211D, [0x00053]=0x1D54A, [0x00054]=0x1D54B, + [0x00055]=0x1D54C, [0x00056]=0x1D54D, [0x00057]=0x1D54E, [0x00058]=0x1D54F, [0x00059]=0x1D550, + [0x0005A]=0x02124, + }, + lcletters = 0x1D552, + }, + }, + fraktur = { -- ok + tf= { + ucletters = { -- C H I R Z + [0x00041]=0x1D504, [0x00042]=0x1D505, [0x00043]=0x0212D, [0x00044]=0x1D507, [0x00045]=0x1D508, + [0x00046]=0x1D509, [0x00047]=0x1D50A, [0x00048]=0x0210C, [0x00049]=0x02111, [0x0004A]=0x1D50D, + [0x0004B]=0x1D50E, [0x0004C]=0x1D50F, [0x0004D]=0x1D510, [0x0004E]=0x1D511, [0x0004F]=0x1D512, + [0x00050]=0x1D513, [0x00051]=0x1D514, [0x00052]=0x0211C, [0x00053]=0x1D516, [0x00054]=0x1D517, + [0x00055]=0x1D518, [0x00056]=0x1D519, [0x00057]=0x1D51A, [0x00058]=0x1D51B, [0x00059]=0x1D51C, + [0x0005A]=0x02128, + }, + lcletters = 0x1D51E, + }, + bf = { + ucletters = 0x1D56C, + lcletters = 0x1D586, + }, + }, + script = { + tf= { + ucletters = { -- B E F H I L M R -- P 2118 + [0x00041]=0x1D49C, [0x00042]=0x0212C, [0x00043]=0x1D49E, [0x00044]=0x1D49F, [0x00045]=0x02130, + [0x00046]=0x02131, [0x00047]=0x1D4A2, [0x00048]=0x0210B, [0x00049]=0x02110, [0x0004A]=0x1D4A5, + [0x0004B]=0x1D4A6, [0x0004C]=0x02112, [0x0004D]=0x02133, [0x0004E]=0x1D4A9, [0x0004F]=0x1D4AA, + [0x00050]=0x1D4AB, [0x00051]=0x1D4AC, [0x00052]=0x0211B, [0x00053]=0x1D4AE, [0x00054]=0x1D4AF, + [0x00055]=0x1D4B0, [0x00056]=0x1D4B1, [0x00057]=0x1D4B2, [0x00058]=0x1D4B3, [0x00059]=0x1D4B4, + [0x0005A]=0x1D4B5, + }, + lcletters = { -- E G O -- L 2113 + [0x00061]=0x1D4B6, [0x00062]=0x1D4B7, [0x00063]=0x1D4B8, [0x00064]=0x1D4B9, [0x00065]=0x0212F, + [0x00066]=0x1D4BB, [0x00067]=0x0210A, [0x00068]=0x1D4BD, [0x00069]=0x1D4BE, [0x0006A]=0x1D4BF, + [0x0006B]=0x1D4C0, [0x0006C]=0x1D4C1, [0x0006D]=0x1D4C2, [0x0006E]=0x1D4C3, [0x0006F]=0x02134, + [0x00070]=0x1D4C5, [0x00071]=0x1D4C6, [0x00072]=0x1D4C7, [0x00073]=0x1D4C8, [0x00074]=0x1D4C9, + [0x00075]=0x1D4CA, [0x00076]=0x1D4CB, [0x00077]=0x1D4CC, [0x00078]=0x1D4CD, [0x00079]=0x1D4CE, + [0x0007A]=0x1D4CF, + } + }, + bf = { + ucletters = 0x1D4D0, + lcletters = 0x1D4EA, + }, + }, +} + +local alphabets = mathematics.alphabets +local attribs = { } + +for alphabet, styles in next, alphabets do + for style, data in next, styles do + -- let's keep the long names (for tracing) + local n = #attribs+1 + data.attribute = n + data.alphabet = alphabet + data.style = style + attribs[n] = data + end +end + +-- beware, these are shared tables (no problem since they're not +-- in unicode) + +alphabets.regular.it.digits = alphabets.regular.tf.digits +alphabets.regular.bi.digits = alphabets.regular.bf.digits + +alphabets.sansserif.tf.symbols = alphabets.regular.tf.symbols +alphabets.sansserif.tf.digits = alphabets.regular.tf.digits +alphabets.sansserif.it.symbols = alphabets.regular.tf.symbols +alphabets.sansserif.bi.digits = alphabets.regular.bf.digits + +alphabets.monospaced.tf.symbols = alphabets.sansserif.tf.symbols +alphabets.monospaced.it = alphabets.sansserif.tf +alphabets.monospaced.bf = alphabets.sansserif.tf +alphabets.monospaced.bi = alphabets.sansserif.bf + +alphabets.blackboard.tf.symbols = alphabets.regular.tf.symbols +alphabets.blackboard.it = alphabets.blackboard.tf +alphabets.blackboard.bf = alphabets.blackboard.tf +alphabets.blackboard.bi = alphabets.blackboard.bf + +alphabets.fraktur.tf.digits = alphabets.regular.tf.digits +alphabets.fraktur.tf.symbols = alphabets.regular.tf.symbols +alphabets.fraktur.bf.digits = alphabets.regular.bf.digits +alphabets.fraktur.bf.symbols = alphabets.regular.bf.symbols +alphabets.fraktur.it = alphabets.fraktur.tf +alphabets.fraktur.bi = alphabets.fraktur.bf + +alphabets.script.tf.digits = alphabets.regular.tf.digits +alphabets.script.tf.symbols = alphabets.regular.tf.symbols +alphabets.script.bf.digits = alphabets.regular.bf.digits +alphabets.script.bf.symbols = alphabets.regular.bf.symbols +alphabets.script.it = alphabets.script.tf +alphabets.script.bi = alphabets.script.bf + +alphabets.tt = alphabets.monospaced +alphabets.ss = alphabets.sansserif +alphabets.rm = alphabets.regular +alphabets.bb = alphabets.blackboard +alphabets.fr = alphabets.fraktur +alphabets.sr = alphabets.script + +alphabets.serif = alphabets.regular +alphabets.type = alphabets.monospaced +alphabets.teletype = alphabets.monospaced + +function mathematics.to_a_style(attribute) + local r = attribs[attribute] + return r and r.style or "tf" +end + +function mathematics.to_a_name(attribute) + local r = attribs[attribute] + return r and r.alphabet or "regular" +end + +-- of course we could do some div/mod trickery instead + +--~ function mathematics.sync_a_both(attribute,alphabet,style) +--~ local data = alphabets[alphabet or "regular"] or alphabets.regular +--~ data = data[style or "tf"] or data.tf +--~ return data and data.attribute or attribute +--~ end + +--~ function mathematics.sync_a_style(attribute,style) +--~ local r = attribs[attribute] +--~ local alphabet = r and r.alphabet or "regular" +--~ local data = alphabets[alphabet][style] +--~ return data and data.attribute or attribute +--~ end + +--~ function mathematics.sync_a_name(attribute,alphabet) +--~ local r = attribs[attribute] +--~ local style = r and r.style or "tf" +--~ local data = alphabets[alphabet][style] +--~ return data and data.attribute or attribute +--~ end + +local mathalph = attributes.private("mathalph") + +local texattribute = tex.attribute + +function mathematics.sync_a_both(alphabet,style) + local data = alphabets[alphabet or "regular"] or alphabets.regular + data = data[style or "tf"] or data.tf + texattribute[mathalph] = data and data.attribute or texattribute[mathalph] +end + +function mathematics.sync_a_style(style) + local r = attribs[attribute] + local alphabet = r and r.alphabet or "regular" + local data = alphabets[alphabet][style] + texattribute[mathalph] = data and data.attribute or texattribute[mathalph] +end + +function mathematics.sync_a_name(alphabet) + local r = attribs[attribute] + local style = r and r.style or "tf" + local data = alphabets[alphabet][style] + texattribute[mathalph] = data and data.attribute or texattribute[mathalph] +end + +local issymbol = mathematics.alphabets.regular.tf.symbols + +function mathematics.remap_alphabets(attribute,char) + -- we could use a map[attribute][char] => newchar but first we have + -- to finish the table + local offset = attribs[attribute] + if offset then + local newchar + if char >= 0x030 and char <= 0x039 then + local o = offset.digits + newchar = (type(o) == "table" and (o[char] or char)) or (char - 0x030 + o) + elseif char >= 0x041 and char <= 0x05A then + local o = offset.ucletters + newchar = (type(o) == "table" and (o[char] or char)) or (char - 0x041 + o) + elseif char >= 0x061 and char <= 0x07A then + local o = offset.lcletters + newchar = (type(o) == "table" and (o[char] or char)) or (char - 0x061 + o) + elseif issymbol[char] then + newchar = offset.symbols[char] + end + return newchar ~= char and newchar + end + return nil +end diff --git a/tex/context/base/math-mis.tex b/tex/context/base/math-mis.tex deleted file mode 100644 index 1b1193fd4..000000000 --- a/tex/context/base/math-mis.tex +++ /dev/null @@ -1,49 +0,0 @@ -%D \module -%D [ file=math-mis, -%D version=2001.04.12, -%D title=\CONTEXT\ Math Macros, -%D subtitle=Miscelaneous Symbols, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright=\PRAGMA] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\unprotect - -%D \starttyping -%D \usemathcollection[mis] -%D \stoptyping - -\def\styledmathcommand#1% - {\mathchoice - {\let\currentmathstyle\displaystyle#1}% - {\let\currentmathstyle\textstyle#1}% - {\let\currentmathstyle\scriptstyle#1}% - {\let\currentmathstyle\scriptscriptstyle#1}} - -%D For Hong Feng: - -\def\geneq - {\styledmathcommand\dogeneq} - -\def\dogeneq - {\begingroup - \setbox\scratchbox\hbox{$\currentmathstyle=$}% - \hbox to \wd\scratchbox - {\copy\scratchbox - \hskip-\wd\scratchbox - \hss\incolortrue\localcolortrue - \color[white]{\vrule\!!height.6\ht\scratchbox\!!depth\zeropoint\!!width.2\wd\scratchbox}% - \hss}% - \endgroup} - -%D \startbuffer -%D $a\string\geneq b^{a\string\geneq b^{a\string\geneq b}}$ -%D \stopbuffer -%D -%D \typebuffer \getbuffer - -\protect \endinput diff --git a/tex/context/base/math-noa.lua b/tex/context/base/math-noa.lua new file mode 100644 index 000000000..6cdcc0114 --- /dev/null +++ b/tex/context/base/math-noa.lua @@ -0,0 +1,336 @@ +if not modules then modules = { } end modules ['math-noa'] = { + version = 1.001, + comment = "companion to math-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- beware: this is experimental code and there will be a more +-- generic (attribute value driven) interface too but for the +-- moment this is ok + +local utf = unicode.utf8 + +local set_attribute = node.set_attribute +local has_attribute = node.has_attribute +local mlist_to_hlist = node.mlist_to_hlist +local font_of_family = node.family_font +local fontdata = fonts.ids + +local format, rep = string.format, string.rep +local utfchar, utfbyte = utf.char, utf.byte + +noads = noads or { } + +local trace_remapping = false trackers.register("math.remapping", function(v) trace_remapping = v end) +local trace_processing = false trackers.register("math.processing", function(v) trace_processing = v end) +local trace_analyzing = false trackers.register("math.analyzing", function(v) trace_analyzing = v end) + +local noad_ord = 0 +local noad_op_displaylimits = 1 +local noad_op_limits = 2 +local noad_op_nolimits = 3 +local noad_bin = 4 +local noad_rel = 5 +local noad_open = 6 +local noad_close = 7 +local noad_punct = 8 +local noad_inner = 9 +local noad_under = 10 +local noad_over = 11 +local noad_vcenter = 12 + +-- obsolete: +-- +-- math_ord = node.id("ord") -- attr nucleus sub sup +-- math_op = node.id("op") -- attr nucleus sub sup subtype +-- math_bin = node.id("bin") -- attr nucleus sub sup +-- math_rel = node.id("rel") -- attr nucleus sub sup +-- math_punct = node.id("punct") -- attr nucleus sub sup +-- +-- math_open = node.id("open") -- attr nucleus sub sup +-- math_close = node.id("close") -- attr nucleus sub sup +-- +-- math_inner = node.id("inner") -- attr nucleus sub sup +-- math_vcenter = node.id("vcenter") -- attr nucleus sub sup +-- math_under = node.id("under") -- attr nucleus sub sup +-- math_over = node.id("over") -- attr nucleus sub sup + +local math_noad = node.id("noad") -- attr nucleus sub sup + +local math_accent = node.id("accent") -- attr nucleus sub sup accent +local math_radical = node.id("radical") -- attr nucleus sub sup left degree +local math_fraction = node.id("fraction") -- attr nucleus sub sup left right + +local math_box = node.id("sub_box") -- attr list +local math_sub = node.id("sub_mlist") -- attr list +local math_char = node.id("math_char") -- attr fam char +local math_text_char = node.id("math_text_char") -- attr fam char +local math_delim = node.id("delim") -- attr small_fam small_char large_fam large_char +local math_style = node.id("style") -- attr style +local math_choice = node.id("choice") -- attr display text script scriptscript +local math_fence = node.id("fence") -- attr subtype + +local simple_noads = table.tohash { + math_noad, +} + +local all_noads = { + math_noad, + math_box, math_sub, + math_char, math_text_char, math_delim, math_style, + math_accent, math_radical, math_fraction, math_choice, math_fence, +} + +noads.processors = noads.processors or { } + +local function process(start,what,n) + if n then n = n + 1 else n = 0 end + while start do + if trace_processing then + texio.write_nl(format("%s%s",rep(" ",n or 0),tostring(start))) + end + local id = start.id + local proc = what[id] + if proc then + proc(start,what,n) + elseif id == math_char or id == math_text_char or id == math_delim then + break + elseif id == math_style then + -- has a next + elseif id == math_noad then + local noad = start.nucleus if noad then process(noad,what,n) end -- list + noad = start.sup if noad then process(noad,what,n) end -- list + noad = start.sub if noad then process(noad,what,n) end -- list + elseif id == math_box or id == math_sub then + local noad = start.list if noad then process(noad,what,n) end -- list + elseif id == math_fraction then + local noad = start.num if noad then process(noad,what,n) end -- list + noad = start.denom if noad then process(noad,what,n) end -- list + noad = start.left if noad then process(noad,what,n) end -- delimiter + noad = start.right if noad then process(noad,what,n) end -- delimiter + elseif id == math_choice then + local noad = start.display if noad then process(noad,what,n) end -- list + noad = start.text if noad then process(noad,what,n) end -- list + noad = start.script if noad then process(noad,what,n) end -- list + noad = start.scriptscript if noad then process(noad,what,n) end -- list + elseif id == math_fence then + local noad = start.delim if noad then process(noad,what,n) end -- delimiter + elseif id == math_radical then + local noad = start.nucleus if noad then process(noad,what,n) end -- list + noad = start.sup if noad then process(noad,what,n) end -- list + noad = start.sub if noad then process(noad,what,n) end -- list + noad = start.left if noad then process(noad,what,n) end -- delimiter + noad = start.degree if noad then process(noad,what,n) end -- list + elseif id == math_accent then + local noad = start.nucleus if noad then process(noad,what,n) end -- list + noad = start.sup if noad then process(noad,what,n) end -- list + noad = start.sub if noad then process(noad,what,n) end -- list + noad = start.accent if noad then process(noad,what,n) end -- list + noad = start.bot_accent if noad then process(noad,what,n) end -- list + else + -- glue, penalty, etc + end + start = start.next + end +end + +noads.process = process + +-- character remapping + +local attribute = attributes.private("mathalph") + +noads.processors.relocate = { } + +local function report_remap(tag,id,old,new,extra) + logs.report("math","remapping %s in font %s from U+%04X (%s) to U+%04X (%s)%s",tag,id,old,utfchar(old),new,utfchar(new),extra or "") +end + +local remap_alphabets = mathematics.remap_alphabets +local fcs = fonts.color.set + +noads.processors.relocate[math_char] = function(pointer) + local a = has_attribute(pointer,attribute) + if a and a > 0 then + local fam = pointer.fam + set_attribute(pointer,attribute,0) + local char = pointer.char + local newchar = remap_alphabets(a,char) + if newchar then + local id = font_of_family(fam) + local tfmdata = fontdata[id] + if tfmdata and tfmdata.characters[newchar] then -- we could probably speed this up + if trace_remapping then + report_remap("char",id,char,newchar) + end + if trace_analyzing then + fcs(pointer,"font:isol") + end + pointer.char = newchar + return + elseif trace_remapping then + report_remap("char",id,char,newchar," fails") + end + end + end + if trace_analyzing then + fcs(pointer,"font:medi") + end +end + +noads.processors.relocate[math_text_char] = function(pointer) + if trace_analyzing then + fcs(pointer,"font:init") + end +end + +noads.processors.relocate[math_delim] = function(pointer) + if trace_analyzing then + fcs(pointer,"font:fina") + end +end + +function noads.relocate_characters(head,tail,style,penalties) + process(head,noads.processors.relocate) + return true +end + +-- some resize options (this works ok because the content is +-- empty and no larger next will be forced) +-- +-- beware: we don't use \delcode but \Udelcode and as such have +-- no large_fam; also, we need to check for subtype and/or +-- small_fam not being 0 because \. sits in 0,0 by default +-- +-- todo: just replace the character by an ord noad +-- and remove the right delimiter as well + +local attribute = attributes.private("mathsize") + +noads.processors.resize = { } + +noads.processors.resize[math_fence] = function(pointer) + if pointer.subtype == 1 then -- left + local a = has_attribute(pointer,attribute) + if a and a > 0 then + set_attribute(pointer,attribute,0) + local d = pointer.delim + local df = d.small_fam + local id = font_of_family(df) + if id > 0 then + local ch = d.small_char + d.small_char = mathematics.big(fontdata[id],ch,a) + end + end + end +end + +function noads.resize_characters(head,tail,style,penalties) + process(head,noads.processors.resize) + return true +end + +-- respacing + +local attribute = attributes.private("mathpunc") + +noads.processors.respace = { } + +local chardata = characters.data + +-- only [nd,ll,ul][po][nd,ll,ul] + +noads.processors.respace[math_noad] = function(pointer) + if pointer.subtype == noad_ord then + local a = has_attribute(pointer,attribute) + if a and a > 0 then + set_attribute(pointer,attribute,0) + local current_nucleus = pointer.nucleus + if current_nucleus.id == math_char then + local current_char = current_nucleus.char + local fc = chardata[current_char] + fc = fc and fc.category + if fc == "nd" or fc == "ll" or fc == "lu" then + local next_noad = pointer.next + if next_noad and next_noad.id == math_noad and next_noad.subtype == noad_punct then + local next_nucleus = next_noad.nucleus + if next_nucleus.id == math_char then + local next_char = next_nucleus.char + local nc = chardata[next_char] + nc = nc and nc.category + if nc == "po" then + local last_noad = next_noad.next + if last_noad and last_noad.id == math_noad and last_noad.subtype == noad_ord then + local last_nucleus = last_noad.nucleus + if last_nucleus.id == math_char then + local last_char = last_nucleus.char + local lc = chardata[last_char] + lc = lc and lc.category + if lc == "nd" or lc == "ll" or lc == "lu" then + local ord = node.new(math_noad) -- todo: pool + ord.subtype, ord.nucleus, ord.sub, ord.sup, ord.attr = noad_ord, next_noad.nucleus, next_noad.sub, next_noad.sup, next_noad.attr + -- next_noad.nucleus, next_noad.sub, next_noad.sup, next_noad.attr = nil, nil, nil, nil + next_noad.nucleus, next_noad.sub, next_noad.sup = nil, nil, nil -- else crash with attributes ref count + --~ next_noad.attr = nil + ord.next = last_noad + pointer.next = ord + node.free(next_noad) + end + end + end + end + end + end + end + end + end + end +end + + +function noads.respace_characters(head,tail,style,penalties) + noads.process(head,noads.processors.respace) + return true +end + +-- the normal builder + +function noads.mlist_to_hlist(head,tail,style,penalties) + return mlist_to_hlist(head,style,penalties), true +end + +tasks.new ( + "math", + { + "normalizers", + "builders", + } +) + +--~ tasks.appendaction("math", "normalizers", "noads.relocate_characters", nil, "nohead") +--~ tasks.appendaction("math", "normalizers", "noads.resize_characters", nil, "nohead") +--~ tasks.appendaction("math", "normalizers", "noads.respace_characters", nil, "nohead") +--~ tasks.appendaction("math", "builders", "noads.mlist_to_hlist", nil, "notail") + +local actions = tasks.actions("math") + +local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming + +function nodes.processors.mlist_to_hlist(head,style,penalties) + starttiming(noads) + local head, done = actions(head,nil,style,penalties) + stoptiming(noads) + return head, done +end + +callback.register('mlist_to_hlist',nodes.processors.mlist_to_hlist) + +-- tracing + +statistics.register("math processing time", function() + if statistics.elapsedindeed(noads) then + return format("%s seconds", statistics.elapsedtime(noads)) + end +end) diff --git a/tex/context/base/math-pln.mkii b/tex/context/base/math-pln.mkii new file mode 100644 index 000000000..0bacc40a2 --- /dev/null +++ b/tex/context/base/math-pln.mkii @@ -0,0 +1,360 @@ +%D \module +%D [ file=math-pln, +%D version=2001.11.16, +%D title=\CONTEXT\ Math Macros, +%D subtitle=Plain Helpers, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% \points should become \bodyfontsize + +%D This is a temporary module, some of this code will move to +%D the other math modules. + +\writestatus{loading}{ConTeXt Math Macros / Plain Helpers} + +\unprotect + +\ifx\displ@y\undefined \let\displ@y\relax\fi + +\newbox\rootbox + +\def\root#1\of + {\setbox\rootbox\hbox{$\mathsurround\zeropoint\scriptscriptstyle{#1}$}% + \mathpalette\r@@t} + +\def\r@@t#1#2% will be overloaded + {\setbox\zerocount\hbox{$\mathsurround\zeropoint#1\sqrt{#2}$}\dimen@\ht\zerocount + \advance\dimen@-\dp\zerocount + \mkern5mu\raise.6\dimen@\copy\rootbox + \mkern-10mu\box\zerocount} + +\def\mathhexbox#1#2#3% + {\leavevmode + \hbox{$\mathsurround\zeropoint\mathchar"#1#2#3$}} + +\def\oalign#1% + {\leavevmode + \vtop + {\baselineskip\zeroskip \lineskip.25ex% + \ialign{##\crcr#1\crcr}}} + +\def\o@lign + {\lineskiplimit\zeropoint \oalign} + +\def\ooalign % chars over each other + {\lineskiplimit-\maxdimen + \oalign} + +\def\sh@ft#1% kern by #1 times the current slant + {\dimen@#1% + \kern\expandafter\withoutpt\the\slantperpoint + \dimen@} + +\def\dots + {\relax\ifmmode\ldots\else$\mathsurround\zeropoint\ldots\,$\fi} + +\def\hrulefill + {\leaders\hrule\hfill} + +\def\dotfill + {\cleaders\hbox{$\mathsurround\zeropoint \mkern1.5mu.\mkern1.5mu$}\hfill} + +\def\rightarrowfill + {$\mathsurround\zeropoint\smash-\mkern-7mu% + \cleaders\hbox{$\mkern-2mu\smash-\mkern-2mu$}\hfill + \mkern-7mu\mathord\rightarrow$} + +\def\leftarrowfill + {$\mathsurround\zeropoint\mathord\leftarrow\mkern-7mu% + \cleaders\hbox{$\mkern-2mu\smash-\mkern-2mu$}\hfill + \mkern-7mu\smash-$} + +% must go to math-tex + +\ifx\braceld\undefined + % mkii values + \mathchardef\braceld="37A + \mathchardef\bracerd="37B + \mathchardef\bracelu="37C + \mathchardef\braceru="37D +\fi + +\def\downbracefill + {$\mathsurround\zeropoint\setbox\zerocount\hbox{$\braceld$}% + \braceld\leaders\vrule\!!height\ht\zerocount\!!depth\zeropoint\hfill\braceru + \bracelu\leaders\vrule\!!height\ht\zerocount\!!depth\zeropoint\hfill\bracerd$} + +\def\upbracefill + {$\mathsurround\zeropoint\setbox\zerocount\hbox{$\braceld$}% + \bracelu\leaders\vrule\!!height\ht\zerocount\!!depth\zeropoint\hfill\bracerd + \braceld\leaders\vrule\!!height\ht\zerocount\!!depth\zeropoint\hfill\braceru$} + +% hm, shouldn't that be \kern3\bodyfontsize + +\def\overbrace#1% + {\mathop{\vbox{\mathsurround\zeropoint\ialign{##\crcr\noalign{\kern3\points} + \downbracefill\crcr\noalign{\kern3\points\nointerlineskip} + $\hfil\displaystyle{#1}\hfil$\crcr}}}\limits} + +\def\underbrace#1% + {\mathop{\vtop{\mathsurround\zeropoint\ialign{##\crcr + $\hfil\displaystyle{#1}\hfil$\crcr\noalign{\kern3\points\nointerlineskip} + \upbracefill\crcr\noalign{\kern3\points}}}}\limits} + +\let\sp=^ % will become obsolete +\let\sb=_ % will become obsolete + +\ifx\,\undefined \def\,{\mskip \thinmuskip } \fi +\ifx\>\undefined \def\>{\mskip \medmuskip } \fi +\ifx\;\undefined \def\;{\mskip \thickmuskip} \fi +\ifx\!\undefined \def\!{\mskip-\thinmuskip } \fi +\ifx\*\undefined \def\*{\discretionary{\thinspace\the\textfont2\char2}{}{}} \fi + +% {\catcode`\'=\active \gdef'{^\bgroup\prim@s}} + +\def\activemathquote{^\bgroup\prim@s} + +\def\prim@s + {\prime\futurelet\next\pr@m@s} + +\def\pr@m@s + {\ifx'\next + \@EA\pr@@@s + \else\ifx^\next + \@EAEAEA\pr@@@t + \else + \@EAEAEA\egroup + \fi\fi} + +\def\pr@@@s#1% + {\prim@s} + +\def\pr@@@t#1#2% + {#2\egroup} + +% {\catcode`\_=\active \global\let_=\_} % _ in math is either subscript or \_ + +\let\activemathunderscore\_ + +\def\relbar {\mathrel{\smash-}} % - has the same height as + +\def\Relbar {\mathrel=} + +\def\Longrightarrow {\Relbar\joinrel\Rightarrow} +\def\longrightarrow {\relbar\joinrel\rightarrow} +\def\longleftarrow {\leftarrow\joinrel\relbar} +\def\Longleftarrow {\Leftarrow\joinrel\Relbar} +\def\longmapsto {\mapstochar\longrightarrow} +\def\longleftrightarrow{\leftarrow\joinrel\rightarrow} +\def\Longleftrightarrow{\Leftarrow\joinrel\Rightarrow} + +\def\overrightarrow#1% + {\vbox{\mathsurround\zeropoint\ialign{##\crcr + \rightarrowfill\crcr\noalign{\kern-\onepoint\nointerlineskip} + $\hfil\displaystyle{#1}\hfil$\crcr}}} + +\def\overleftarrow#1% + {\vbox{\mathsurround\zeropoint\ialign{##\crcr + \leftarrowfill\crcr\noalign{\kern-\onepoint\nointerlineskip} + $\hfil\displaystyle{#1}\hfil$\crcr}}} + +\def\skew#1#2#3% + {{\muskip\zerocount#1mu\divide\muskip\zerocount\plustwo \mkern\muskip\zerocount + #2{\mkern-\muskip\zerocount{#3}\mkern\muskip\zerocount}\mkern-\muskip\zerocount}{}} + +\def\choose{\atopwithdelims()} +\def\brack {\atopwithdelims[]} +\def\brace {\atopwithdelims\{\}} + +\def\mathpalette#1#2% + {\mathchoice + {#1\displaystyle {#2}}% + {#1\textstyle {#2}}% + {#1\scriptstyle {#2}}% + {#1\scriptscriptstyle{#2}}} + +\def\cong + {\mathrel{\mathpalette\@vereq\sim}} % congruence sign + +\def\@vereq#1#2% + {\lower.5\points\vbox{\lineskiplimit\maxdimen\lineskip-.5\points + \ialign{$\mathsurround\zeropoint#1\hfil##\hfil$\crcr#2\crcr=\crcr}}} + +\def\notin% can be mkiv'd + {\mathrel{\mathpalette\c@ncel\in}} + +\def\c@ncel#1#2% + {\mathsurround\zeropoint\ooalign{$\hfil#1\mkern1mu/\hfil$\crcr$#1#2$}} + +\def\rightleftharpoons% + {\mathrel{\mathpalette\rlh@{}}} + +\def\rlh@#1% + {\vcenter + {\mathsurround\zeropoint + \hbox + {\ooalign + {\raise2pt\hbox{$#1\rightharpoonup$}\crcr + $#1\leftharpoondown$}}}} + +\def\buildrel#1\over#2% + {\mathrel{\mathop{\kern\zerocount#2}\limits^{#1}}} + +\def\doteq + {\buildrel\textstyle.\over=} + +\ifx\mfunction\undefined \def\mfunction#1{\mathbin{\rm#1}} \fi + +\def\bmod + {\nonscript + \mskip-\medmuskip + \mkern5mu + \mfunction{mod}% + \penalty900 + \mkern5mu + \nonscript + \mskip-\medmuskip} + +\def\pmod#1% + {\allowbreak + \mkern18mu + (\mfunction{mod}\,\,#1)} + +\def\cases#1% + {\left\{% + \,% + \vcenter + {\normalbaselines + \mathsurround\zeropoint + \ialign{$##\hfil$&\quad##\hfil\crcr#1\crcr}}% + \right.} + +\def\matrix#1% + {\null + \,% + \vcenter + {\normalbaselines\mathsurround\zeropoint + \ialign{\hfil$##$\hfil&&\quad\hfil$##$\hfil\crcr + \mathstrut\crcr\noalign{\kern-\baselineskip} + #1\crcr\mathstrut\crcr\noalign{\kern-\baselineskip}}}% + \,} + +\def\pmatrix#1% + {\left(\matrix{#1}\right)} + +\newdimen\mathparentwd + +% \setbox0=\hbox{\tenex B} \mathparentwd=\wd0 % width of the big left ( + +\def\bordermatrix#1% + {\begingroup + \mathsurround\zeropoint + \setbox\zerocount\vbox + {\def\cr{\crcr\noalign{\kern2\points\global\let\cr\endline}}% + \ialign{$##$\hfil\kern2\points\kern\mathparentwd&\thinspace\hfil$##$\hfil + &&\quad\hfil$##$\hfil\crcr + \omit\strut\hfil\crcr\noalign{\kern-\baselineskip}% + #1\crcr\omit\strut\cr}}% + \setbox\plustwo\vbox + {\unvcopy\zerocount\global\setbox\plusone\lastbox}% + \setbox\plustwo\hbox + {\unhbox\plusone\unskip\global\setbox\plusone\lastbox}% + \setbox\plustwo\hbox + {$\kern\wd\plusone\kern-\mathparentwd\left(\kern-\wd\plusone + \global\setbox\plusone\vbox{\box\plusone\kern2\points}% + \vcenter{\kern-\ht\plusone\unvbox\zerocount\kern-\baselineskip}\,\right)$}% + \null + \;% + \vbox{\kern\ht\plusone\box\plustwo}% + \endgroup} + +% \def\openup{\afterassignment\@penup\dimen@=} +% +% \def\@penup{\advance\lineskip\dimen@ +% \advance\baselineskip\dimen@ +% \advance\lineskiplimit\dimen@} + +\def\openup + {\afterassignment\doopenup\scratchdimen=} + +\def\doopenup + {\advance\lineskip \scratchdimen + \advance\baselineskip \scratchdimen + \advance\lineskiplimit\scratchdimen} + +% \def\jot{.25\bodyfontsize} % plain tex: 3 pt (todo: better name and configurable) + +\def\displayopenupvalue{.25\bodyfontsize} + +\def\eqalign#1% + {\null + \,% + \vcenter + {\openup\displayopenupvalue % was \openup\jot + \mathsurround\zeropoint + \ialign + {\strut\hfil$\displaystyle{##}$&$\displaystyle{{}##}$\hfil\crcr + #1\crcr}}% + \,} + +\def\@lign % restore inside \displ@y + {\tabskip\zeroskip + \everycr{}} + +\def\displaylines#1% + {\displ@y + \tabskip\zeroskip + \halign + {\hbox to \displaywidth{$\@lign\hfil\displaystyle##\hfil$}\crcr + #1\crcr}} + +\def\eqalignno#1% + {\displ@y + \tabskip\centering + \halign to \displaywidth + {\hfil$\@lign\displaystyle{##}$\tabskip\zeroskip + &$\@lign\displaystyle{{}##}$\hfil\tabskip\centering + &\llap{$\@lign##$}\tabskip\zeroskip\crcr + #1\crcr}} + +\def\leqalignno#1% + {\displ@y + \tabskip\centering + \halign to \displaywidth + {\hfil$\@lign\displaystyle{##}$\tabskip\zeroskip + &$\@lign\displaystyle{{}##}$\hfil\tabskip\centering + &\kern-\displaywidth\rlap{$\@lign##$}\tabskip\displaywidth\crcr + #1\crcr}} + +% temporary here + +% \startcatcodetable \mthcatcodes +% \setcatcodetable\ctxcatcodes +% \catcode`\_ = 13 +% \catcode`\' = 13 +% \stopcatcodetable +% +% \letcatcodecommand \mthcatcodes `\_ \activemathunderscore +% \letcatcodecommand \mthcatcodes `\' \activemathquote + +% \appendtoks \setcatcodetable\mthcatcodes \to \everymath : spoils xml + +% tricky, but some day we will reimplement math + +\bgroup + \catcode`\_ = 13 + \catcode`\' = 13 + \doglobal\appendtoks + \let_\activemathunderscore + \let'\activemathquote + \to \everymathematics +\egroup + +% so far + +\protect \endinput diff --git a/tex/context/base/math-pln.mkiv b/tex/context/base/math-pln.mkiv new file mode 100644 index 000000000..23d7d935c --- /dev/null +++ b/tex/context/base/math-pln.mkiv @@ -0,0 +1,298 @@ +%D \module +%D [ file=math-pln, +%D version=2001.11.16, +%D title=\CONTEXT\ Math Macros, +%D subtitle=Plain Helpers, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=\PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D This is a temporary module, some of this code will move to +%D the other math modules. Much is copied from Plain \TEX. + +% \points should become \bodyfontsize + +\writestatus{loading}{ConTeXt Math Macros / Plain Helpers} + +\unprotect + +\ifx\displ@y\undefined \let\displ@y\relax\fi + +\def\oalign#1% + {\leavevmode + \vtop + {\baselineskip\zeroskip \lineskip.25ex% + \ialign{##\crcr#1\crcr}}} + +\def\o@lign + {\lineskiplimit\zeropoint \oalign} + +\def\ooalign % chars over each other + {\lineskiplimit-\maxdimen + \oalign} + +\def\sh@ft#1% kern by #1 times the current slant + {\dimen@#1% + \kern\expandafter\withoutpt\the\slantperpoint + \dimen@} + +\def\dots + {\relax\ifmmode\ldots\else$\mathsurround\zeropoint\ldots\,$\fi} + +\def\hrulefill + {\leaders\hrule\hfill} + +\def\dotfill + {\cleaders\hbox{$\mathsurround\zeropoint \mkern1.5mu.\mkern1.5mu$}\hfill} + +\def\rightarrowfill + {$\mathsurround\zeropoint\smash-\mkern-7mu% + \cleaders\hbox{$\mkern-2mu\smash-\mkern-2mu$}\hfill + \mkern-7mu\mathord\rightarrow$} + +\def\leftarrowfill + {$\mathsurround\zeropoint\mathord\leftarrow\mkern-7mu% + \cleaders\hbox{$\mkern-2mu\smash-\mkern-2mu$}\hfill + \mkern-7mu\smash-$} + +\let\sp=^ % will become obsolete +\let\sb=_ % will become obsolete + +\ifx\,\undefined \def\,{\mskip \thinmuskip } \fi +\ifx\>\undefined \def\>{\mskip \medmuskip } \fi +\ifx\;\undefined \def\;{\mskip \thickmuskip} \fi +\ifx\!\undefined \def\!{\mskip-\thinmuskip } \fi +\ifx\*\undefined \def\*{\discretionary{\thinspace\the\textfont2\char2}{}{}} \fi + +% {\catcode`\'=\active \gdef'{^\bgroup\prim@s}} + +\def\activemathquote{^\bgroup\prim@s} + +\def\prim@s + {\prime\futurelet\next\pr@m@s} + +\def\pr@m@s + {\ifx'\next + \@EA\pr@@@s + \else\ifx^\next + \@EAEAEA\pr@@@t + \else + \@EAEAEA\egroup + \fi\fi} + +\def\pr@@@s#1% + {\prim@s} + +\def\pr@@@t#1#2% + {#2\egroup} + +% {\catcode`\_=\active \global\let_=\_} % _ in math is either subscript or \_ + +\let\activemathunderscore\_ + +\def\relbar {\mathrel{\smash-}} % - has the same height as + +\def\Relbar {\mathrel=} + +\def\Longrightarrow {\Relbar\joinrel\Rightarrow} +\def\longrightarrow {\relbar\joinrel\rightarrow} +\def\longleftarrow {\leftarrow\joinrel\relbar} +\def\Longleftarrow {\Leftarrow\joinrel\Relbar} +\def\longmapsto {\mapstochar\longrightarrow} +\def\longleftrightarrow{\leftarrow\joinrel\rightarrow} +\def\Longleftrightarrow{\Leftarrow\joinrel\Rightarrow} + +\def\choose{\atopwithdelims()} +\def\brack {\atopwithdelims[]} +\def\brace {\atopwithdelims\{\}} + +\def\mathpalette#1#2% + {\mathchoice + {#1\displaystyle {#2}}% + {#1\textstyle {#2}}% + {#1\scriptstyle {#2}}% + {#1\scriptscriptstyle{#2}}} + +\def\cong + {\mathrel{\mathpalette\@vereq\sim}} % congruence sign + +\def\@vereq#1#2% + {\lower.5\points\vbox{\lineskiplimit\maxdimen\lineskip-.5\points + \ialign{$\mathsurround\zeropoint#1\hfil##\hfil$\crcr#2\crcr=\crcr}}} + +\def\notin + {\mathrel{\mathpalette\c@ncel\in}} + +\def\c@ncel#1#2% + {\mathsurround\zeropoint\ooalign{$\hfil#1\mkern1mu/\hfil$\crcr$#1#2$}} + +\def\rightleftharpoons + {\mathrel{\mathpalette\rlh@{}}} + +\def\rlh@#1% + {\vcenter + {\mathsurround\zeropoint + \hbox + {\ooalign + {\raise2pt\hbox{$#1\rightharpoonup$}\crcr + $#1\leftharpoondown$}}}} + +\def\buildrel#1\over#2% + {\mathrel{\mathop{\kern\zeropoint#2}\limits^{#1}}} + +\def\doteq + {\buildrel\textstyle.\over=} + +\ifx\mfunction\undefined \def\mfunction#1{\mathbin{\rm#1}} \fi + +\def\bmod + {\nonscript + \mskip-\medmuskip + \mkern5mu + \mfunction{mod}% + \penalty900 + \mkern5mu + \nonscript + \mskip-\medmuskip} + +\def\pmod#1% + {\allowbreak + \mkern18mu + (\mfunction{mod}\,\,#1)} + +\def\cases#1% + {\left\{% + \,% + \vcenter + {\normalbaselines + \mathsurround\zeropoint + \ialign{$##\hfil$&\quad##\hfil\crcr#1\crcr}}% + \right.} + +\def\matrix#1% + {\null + \,% + \vcenter + {\normalbaselines\mathsurround\zeropoint + \ialign{\hfil$##$\hfil&&\quad\hfil$##$\hfil\crcr + \mathstrut\crcr\noalign{\kern-\baselineskip} + #1\crcr\mathstrut\crcr\noalign{\kern-\baselineskip}}}% + \,} + +\def\pmatrix#1% + {\left(\matrix{#1}\right)} + +\newdimen\mathparentwd + +% \setbox0=\hbox{\tenex B} \mathparentwd=\wd0 % width of the big left ( + +\def\bordermatrix#1% + {\begingroup + \mathsurround\zeropoint + \setbox\zerocount\vbox + {\def\cr{\crcr\noalign{\kern2\points\global\let\cr\endline}}% + \ialign{$##$\hfil\kern2\points\kern\mathparentwd&\thinspace\hfil$##$\hfil + &&\quad\hfil$##$\hfil\crcr + \omit\strut\hfil\crcr\noalign{\kern-\baselineskip}% + #1\crcr\omit\strut\cr}}% + \setbox\plustwo\vbox + {\unvcopy\zerocount\global\setbox\plusone\lastbox}% + \setbox\plustwo\hbox + {\unhbox\plusone\unskip\global\setbox\plusone\lastbox}% + \setbox\plustwo\hbox + {$\kern\wd\plusone\kern-\mathparentwd\left(\kern-\wd\plusone + \global\setbox\plusone\vbox{\box\plusone\kern2\points}% + \vcenter{\kern-\ht\plusone\unvbox\zerocount\kern-\baselineskip}\,\right)$}% + \null + \;% + \vbox{\kern\ht\plusone\box\plustwo}% + \endgroup} + +% \def\openup{\afterassignment\@penup\dimen@=} +% +% \def\@penup{\advance\lineskip\dimen@ +% \advance\baselineskip\dimen@ +% \advance\lineskiplimit\dimen@} + +\def\openup + {\afterassignment\doopenup\scratchdimen=} + +\def\doopenup + {\advance\lineskip \scratchdimen + \advance\baselineskip \scratchdimen + \advance\lineskiplimit\scratchdimen} + +% \def\jot{.25\bodyfontsize} % plain tex: 3 pt (todo: better name and configurable) + +\def\displayopenupvalue{.25\bodyfontsize} + +\def\eqalign#1% + {\null + \,% + \vcenter + {\openup\displayopenupvalue % was \openup\jot + \mathsurround\zeropoint + \ialign + {\strut\hfil$\displaystyle{##}$&$\displaystyle{{}##}$\hfil\crcr + #1\crcr}}% + \,} + +\def\@lign % restore inside \displ@y + {\tabskip\zeroskip + \everycr{}} + +\def\displaylines#1% + {\displ@y + \tabskip\zeroskip + \halign + {\hbox to \displaywidth{$\@lign\hfil\displaystyle##\hfil$}\crcr + #1\crcr}} + +\def\eqalignno#1% + {\displ@y + \tabskip\centering + \halign to \displaywidth + {\hfil$\@lign\displaystyle{##}$\tabskip\zeroskip + &$\@lign\displaystyle{{}##}$\hfil\tabskip\centering + &\llap{$\@lign##$}\tabskip\zeroskip\crcr + #1\crcr}} + +\def\leqalignno#1% + {\displ@y + \tabskip\centering + \halign to \displaywidth + {\hfil$\@lign\displaystyle{##}$\tabskip\zeroskip + &$\@lign\displaystyle{{}##}$\hfil\tabskip\centering + &\kern-\displaywidth\rlap{$\@lign##$}\tabskip\displaywidth\crcr + #1\crcr}} + +% temporary here + +% \startcatcodetable \mthcatcodes +% \setcatcodetable\ctxcatcodes +% \catcode`\_ = 13 +% \catcode`\' = 13 +% \stopcatcodetable +% +% \letcatcodecommand \mthcatcodes `\_ \activemathunderscore +% \letcatcodecommand \mthcatcodes `\' \activemathquote + +% \appendtoks \setcatcodetable\mthcatcodes \to \everymath : spoils xml + +% tricky, but some day we will reimplement math + +\bgroup + \catcode`\_ = 13 + \catcode`\' = 13 + \doglobal\appendtoks + \let_\activemathunderscore + \let'\activemathquote + \to \everymathematics +\egroup + +% so far + +\protect \endinput diff --git a/tex/context/base/math-pln.tex b/tex/context/base/math-pln.tex deleted file mode 100644 index ffa16c8f5..000000000 --- a/tex/context/base/math-pln.tex +++ /dev/null @@ -1,355 +0,0 @@ -%D \module -%D [ file=math-pln, -%D version=2001.11.16, -%D title=\CONTEXT\ System Macros, -%D subtitle=Efficient \PLAIN\ \TEX\ loading, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -%D This is a temporary module, some of this code will move to -%D the other math modules. - -\unprotect - -\ifx\displ@y\unefined \let\displ@y\relax\fi -\ifx\m@th \unefined \let\m@th \relax\fi - -\newbox\rootbox - -\def\root#1\of% - {\setbox\rootbox\hbox{$\m@th\scriptscriptstyle{#1}$}% - \mathpalette\r@@t} - -\def\r@@t#1#2% will be overloaded - {\setbox\z@\hbox{$\m@th#1\sqrt{#2}$}\dimen@\ht\z@ - \advance\dimen@-\dp\z@ - \mkern5mu\raise.6\dimen@\copy\rootbox - \mkern-10mu\box\z@} - -\def\mathhexbox#1#2#3% - {\leavevmode - \hbox{$\m@th\mathchar"#1#2#3$}} - -\def\oalign#1% - {\leavevmode - \vtop - {\baselineskip\z@skip \lineskip.25ex% - \ialign{##\crcr#1\crcr}}} - -\def\o@lign - {\lineskiplimit\z@ \oalign} - -\def\ooalign % chars over each other - {\lineskiplimit-\maxdimen - \oalign} - -\def\sh@ft#1% kern by #1 times the current slant - {\dimen@#1% - \kern\expandafter\withoutpt\the\slantperpoint - \dimen@} - -\def\dots - {\relax\ifmmode\ldots\else$\m@th\ldots\,$\fi} - -\def\hrulefill - {\leaders\hrule\hfill} - -\def\dotfill - {\cleaders\hbox{$\m@th \mkern1.5mu.\mkern1.5mu$}\hfill} - -\def\rightarrowfill - {$\m@th\smash-\mkern-7mu% - \cleaders\hbox{$\mkern-2mu\smash-\mkern-2mu$}\hfill - \mkern-7mu\mathord\rightarrow$} - -\def\leftarrowfill - {$\m@th\mathord\leftarrow\mkern-7mu% - \cleaders\hbox{$\mkern-2mu\smash-\mkern-2mu$}\hfill - \mkern-7mu\smash-$} - -% must go to math-tex - -\mathchardef\braceld="37A -\mathchardef\bracerd="37B -\mathchardef\bracelu="37C -\mathchardef\braceru="37D - -\def\downbracefill - {$\m@th\setbox\z@\hbox{$\braceld$}% - \braceld\leaders\vrule\!!height\ht\z@\!!depth\z@\hfill\braceru - \bracelu\leaders\vrule\!!height\ht\z@\!!depth\z@\hfill\bracerd$} - -\def\upbracefill - {$\m@th\setbox\z@\hbox{$\braceld$}% - \bracelu\leaders\vrule\!!height\ht\z@\!!depth\z@\hfill\bracerd - \braceld\leaders\vrule\!!height\ht\z@\!!depth\z@\hfill\braceru$} - -\let\sp=^ % will become obsolete -\let\sb=_ % will become obsolete - -\ifx\,\undefined \def\,{\mskip \thinmuskip } \fi -\ifx\>\undefined \def\>{\mskip \medmuskip } \fi -\ifx\;\undefined \def\;{\mskip \thickmuskip} \fi -\ifx\!\undefined \def\!{\mskip-\thinmuskip } \fi -\ifx\*\undefined \def\*{\discretionary{\thinspace\the\textfont2\char2}{}{}} \fi - -% {\catcode`\'=\active \gdef'{^\bgroup\prim@s}} - -\def\activemathquote{^\bgroup\prim@s} - -\def\prim@s - {\prime\futurelet\next\pr@m@s} - -\def\pr@m@s - {\ifx'\next - \@EA\pr@@@s - \else\ifx^\next - \@EAEAEA\pr@@@t - \else - \@EAEAEA\egroup - \fi\fi} - -\def\pr@@@s#1% - {\prim@s} - -\def\pr@@@t#1#2% - {#2\egroup} - -% {\catcode`\_=\active \global\let_=\_} % _ in math is either subscript or \_ - -\let\activemathunderscore\_ - -\def\relbar {\mathrel{\smash-}} % - has the same height as + -\def\Relbar {\mathrel=} - -\def\Longrightarrow {\Relbar\joinrel\Rightarrow} -\def\longrightarrow {\relbar\joinrel\rightarrow} -\def\longleftarrow {\leftarrow\joinrel\relbar} -\def\Longleftarrow {\Leftarrow\joinrel\Relbar} -\def\longmapsto {\mapstochar\longrightarrow} -\def\longleftrightarrow{\leftarrow\joinrel\rightarrow} -\def\Longleftrightarrow{\Leftarrow\joinrel\Rightarrow} - -\def\overrightarrow#1% - {\vbox{\m@th\ialign{##\crcr - \rightarrowfill\crcr\noalign{\kern-\p@\nointerlineskip} - $\hfil\displaystyle{#1}\hfil$\crcr}}} - -\def\overleftarrow#1% - {\vbox{\m@th\ialign{##\crcr - \leftarrowfill\crcr\noalign{\kern-\p@\nointerlineskip} - $\hfil\displaystyle{#1}\hfil$\crcr}}} - -\def\overbrace#1% - {\mathop{\vbox{\m@th\ialign{##\crcr\noalign{\kern3\p@} - \downbracefill\crcr\noalign{\kern3\p@\nointerlineskip} - $\hfil\displaystyle{#1}\hfil$\crcr}}}\limits} - -\def\underbrace#1% - {\mathop{\vtop{\m@th\ialign{##\crcr - $\hfil\displaystyle{#1}\hfil$\crcr\noalign{\kern3\p@\nointerlineskip} - \upbracefill\crcr\noalign{\kern3\p@}}}}\limits} - -\def\skew#1#2#3% - {{\muskip\z@#1mu\divide\muskip\z@\tw@ \mkern\muskip\z@ - #2{\mkern-\muskip\z@{#3}\mkern\muskip\z@}\mkern-\muskip\z@}{}} - -\def\n@space - {\nulldelimiterspace\z@ \m@th} - -\def\choose{\atopwithdelims()} -\def\brack {\atopwithdelims[]} -\def\brace {\atopwithdelims\{\}} - -\def\mathpalette#1#2% - {\mathchoice - {#1\displaystyle {#2}}% - {#1\textstyle {#2}}% - {#1\scriptstyle {#2}}% - {#1\scriptscriptstyle{#2}}} - -\def\cong% - {\mathrel{\mathpalette\@vereq\sim}} % congruence sign - -\def\@vereq#1#2% - {\lower.5\p@\vbox{\lineskiplimit\maxdimen\lineskip-.5\p@ - \ialign{$\m@th#1\hfil##\hfil$\crcr#2\crcr=\crcr}}} - -\def\notin% - {\mathrel{\mathpalette\c@ncel\in}} - -\def\c@ncel#1#2% - {\m@th\ooalign{$\hfil#1\mkern1mu/\hfil$\crcr$#1#2$}} - -\def\rightleftharpoons% - {\mathrel{\mathpalette\rlh@{}}} - -\def\rlh@#1% - {\vcenter - {\m@th - \hbox - {\ooalign - {\raise2pt\hbox{$#1\rightharpoonup$}\crcr - $#1\leftharpoondown$}}}} - -\def\buildrel#1\over#2% - {\mathrel{\mathop{\kern\z@#2}\limits^{#1}}} - -\def\doteq - {\buildrel\textstyle.\over=} - -\ifx\mfunction\undefined \def\mfunction#1{\mathbin{\rm#1}} \fi - -\def\bmod - {\nonscript - \mskip-\medmuskip - \mkern5mu - \mfunction{mod}% - \penalty900 - \mkern5mu - \nonscript - \mskip-\medmuskip} - -\def\pmod#1% - {\allowbreak - \mkern18mu - (\mfunction{mod}\,\,#1)} - -\def\cases#1% - {\left\{% - \,% - \vcenter - {\normalbaselines - \m@th - \ialign{$##\hfil$&\quad##\hfil\crcr#1\crcr}}% - \right.} - -\def\matrix#1% - {\null - \,% - \vcenter - {\normalbaselines\m@th - \ialign{\hfil$##$\hfil&&\quad\hfil$##$\hfil\crcr - \mathstrut\crcr\noalign{\kern-\baselineskip} - #1\crcr\mathstrut\crcr\noalign{\kern-\baselineskip}}}% - \,} - -\def\pmatrix#1% - {\left(\matrix{#1}\right)} - -\newdimen\p@renwd - -% \setbox0=\hbox{\tenex B} \p@renwd=\wd0 % width of the big left ( - -\def\bordermatrix#1% - {\begingroup - \m@th - \setbox\z@\vbox - {\def\cr{\crcr\noalign{\kern2\p@\global\let\cr\endline}}% - \ialign{$##$\hfil\kern2\p@\kern\p@renwd&\thinspace\hfil$##$\hfil - &&\quad\hfil$##$\hfil\crcr - \omit\strut\hfil\crcr\noalign{\kern-\baselineskip}% - #1\crcr\omit\strut\cr}}% - \setbox\tw@\vbox - {\unvcopy\z@\global\setbox\@ne\lastbox}% - \setbox\tw@\hbox - {\unhbox\@ne\unskip\global\setbox\@ne\lastbox}% - \setbox\tw@\hbox - {$\kern\wd\@ne\kern-\p@renwd\left(\kern-\wd\@ne - \global\setbox\@ne\vbox{\box\@ne\kern2\p@}% - \vcenter{\kern-\ht\@ne\unvbox\z@\kern-\baselineskip}\,\right)$}% - \null - \;% - \vbox{\kern\ht\@ne\box\tw@}% - \endgroup} - -% \def\openup{\afterassignment\@penup\dimen@=} -% -% \def\@penup{\advance\lineskip\dimen@ -% \advance\baselineskip\dimen@ -% \advance\lineskiplimit\dimen@} - -\def\openup - {\afterassignment\doopenup\scratchdimen=} - -\def\doopenup - {\advance\lineskip \scratchdimen - \advance\baselineskip \scratchdimen - \advance\lineskiplimit\scratchdimen} - -% \def\jot{.25\bodyfontsize} % plain tex: 3 pt (todo: better name and configurable) - -\def\displayopenupvalue{.25\bodyfontsize} - -\def\eqalign#1% - {\null - \,% - \vcenter - {\openup\displayopenupvalue % was \openup\jot - \m@th - \ialign - {\strut\hfil$\displaystyle{##}$&$\displaystyle{{}##}$\hfil\crcr - #1\crcr}}% - \,} - -\def\@lign % restore inside \displ@y - {\tabskip\z@skip - \everycr{}} - -\def\displaylines#1% - {\displ@y - \tabskip\z@skip - \halign - {\hbox to \displaywidth{$\@lign\hfil\displaystyle##\hfil$}\crcr - #1\crcr}} - -\def\eqalignno#1% - {\displ@y - \tabskip\centering - \halign to \displaywidth - {\hfil$\@lign\displaystyle{##}$\tabskip\z@skip - &$\@lign\displaystyle{{}##}$\hfil\tabskip\centering - &\llap{$\@lign##$}\tabskip\z@skip\crcr - #1\crcr}} - -\def\leqalignno#1% - {\displ@y - \tabskip\centering - \halign to \displaywidth - {\hfil$\@lign\displaystyle{##}$\tabskip\z@skip - &$\@lign\displaystyle{{}##}$\hfil\tabskip\centering - &\kern-\displaywidth\rlap{$\@lign##$}\tabskip\displaywidth\crcr - #1\crcr}} - -% temporary here - -% \startcatcodetable \mthcatcodes -% \setcatcodetable\ctxcatcodes -% \catcode`\_ = 13 -% \catcode`\' = 13 -% \stopcatcodetable -% -% \letcatcodecommand \mthcatcodes `\_ \activemathunderscore -% \letcatcodecommand \mthcatcodes `\' \activemathquote - -% \appendtoks \setcatcodetable\mthcatcodes \to \everymath : spoils xml - -% tricky, but some day we will reimplement math - -\bgroup - \catcode`\_ = 13 - \catcode`\' = 13 - \doglobal\appendtoks - \let_\activemathunderscore - \let'\activemathquote - \to \everymathematics -\egroup - -% so far - -\protect \endinput diff --git a/tex/context/base/math-run.mkii b/tex/context/base/math-run.mkii new file mode 100644 index 000000000..afe5b18b4 --- /dev/null +++ b/tex/context/base/math-run.mkii @@ -0,0 +1,97 @@ +%D \module +%D [ file=math-run, +%D version=2001.23.04, +%D title=\CONTEXT\ Math Macros, +%D subtitle=Runtime Macros, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=Hans Hagen \& Ton Otten] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Math Macros / Runtime Macros} + +\unprotect + +\ifx\showmathmodern\undefined \global\chardef\showmathmodern\zerocount \fi + +\gdef\showmathcharacters% nearly \showcharacters + {\par + \bgroup + \ifcase\showmathmodern\or\ifx\modern\undefined\chardef\showmathmodern\zerocount\fi\fi + \setuptextrules[\c!bodyfont=,\c!style=] + \starttextrule{math characters -- \currentmathcollection} + \whitespace + \dontcomplain + \forgetall + \def\startmathcollection[##1]{} + \let\stopmathcollection\relax + \dimen0\zeropoint + \dimen2\zeropoint + \def\definemathsymbol {\dosixtupleempty\dodefinemathsymbol} + \def\definemathcharacter{\dosixtupleempty\dodefinemathcharacter} + \def\definemathcommand {\dotripleempty \dodefinemathcommand} + %\newcounter\mathcolor + \def\dodefinemathsymbol[##1][##2][##3][##4][##5][##6]% + {%\doifcolorelse{math \purefamilyhex{##3}}{} + % {\increment\mathcolor + % \definecolor[math \purefamilyhex{##3}][\mathcolor]}% + \setbox0\hbox spread 1em{\mathematics{\getvalue{##1}{}{}{}}}% + \ifdim\wd0>\dimen0 \dimen0=\wd0 \fi + \setbox2\hbox spread 1em{\hbox to 1em{\tttf\purefamilyhex{##3}\hss}\box0 ##1}% + \ifdim\wd2>\dimen2 \dimen2=\wd2 \fi} + \def\dodefinemathcharacter[##1][##2][##3][##4][##5][##6]{} + \def\dodefinemathcommand [##1][##2][##3]##4{} + \readsysfile{\f!mathprefix tex}\donothing\donothing + \readsysfile{\f!mathprefix ams}\donothing\donothing + \edef\encwidth{\the\dimen0} + \dimen0=\hsize + \advance\dimen0 2em + \advance\dimen2 2em + \ifcase\showmathmodern\or\advance\dimen2 4em\fi + \divide \dimen0 by \dimen2 \advance\dimen0 1sp + \edef\enccols{\number\dimen0} + \startcolumns[\c!n=\enccols,\c!distance=2em] + \def\dodefinemathsymbol[##1][##2][##3][##4][##5][##6]% + {%\localcolortrue + %\color + % [math \purefamilyhex{##3}] + {\hbox + {\ifcase\showmathmodern\or + \hbox to \encwidth{\modern\let\mathcollection\nomathcollection\mathematics{\getvalue{##1}{}{}{}}\hss}% + \fi + \hbox to \encwidth{\mathematics{\getvalue{##1}{}{}{}}\hss}% + \hbox to 1em{\tttf\purefamilyhex{##3}\hss}##1}\par}} + \readsysfile{\f!mathprefix tex}\donothing\donothing + \readsysfile{\f!mathprefix ams}\donothing\donothing + \stopcolumns + \stoptextrule + \egroup} + +% \definecolor[math \purefamilyhex{mr}] [darkred] +% \definecolor[math \purefamilyhex{mi}] [darkgreen] +% \definecolor[math \purefamilyhex{sy}] [darkblue] +% \definecolor[math \purefamilyhex{ex}] [darkmagenta] +% \definecolor[math \purefamilyhex{nn}] [darkyellow] +% \definecolor[math \purefamilyhex{ma}] [lightred] +% \definecolor[math \purefamilyhex{mb}] [lightgreen] +% \definecolor[math \purefamilyhex{mc}] [lightblue] +% \definecolor[math \purefamilyhex{md}] [lightmagenta] + +\gdef\showmathtoken#1% + {\starttabulate[|lT|lT|lT|l|] + \NC token \NC #1 \NC \NR + \NC collection \NC \ifcsname\@mt@\mathcollection#1\endcsname + \mathcollection + \else\ifcsname\@mt@\nomathcollection#1\endcsname + \nomathcollection + \else + ?% + \fi\fi \NC \NR + \NC visualization \NC \mathematics{\getvalue{#1}} \NC \NR + \NC definition \NC \tttf \@EA\defconvertedcommand\@EA\ascii\csname\@mt@\mathcollection#1\endcsname \ascii \NC \NR + \stoptabulate} + +\protect \endinput diff --git a/tex/context/base/math-run.tex b/tex/context/base/math-run.tex deleted file mode 100644 index affa8d5af..000000000 --- a/tex/context/base/math-run.tex +++ /dev/null @@ -1,95 +0,0 @@ -%D \module -%D [ file=math-run, -%D version=2001.23.04, -%D title=\CONTEXT\ Math Macros, -%D subtitle=Runtime Macros, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright=Hans Hagen \& Ton Otten] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\unprotect - -\ifx\showmathmodern\undefined \global\chardef\showmathmodern\zerocount \fi - -\gdef\showmathcharacters% nearly \showcharacters - {\par - \bgroup - \ifcase\showmathmodern\or\ifx\modern\undefined\chardef\showmathmodern\zerocount\fi\fi - \setuptextrules[\c!bodyfont=,\c!style=] - \starttextrule{math characters -- \currentmathcollection} - \whitespace - \dontcomplain - \forgetall - \def\startmathcollection[##1]{} - \let\stopmathcollection\relax - \dimen0\zeropoint - \dimen2\zeropoint - \def\definemathsymbol {\dosixtupleempty\dodefinemathsymbol} - \def\definemathcharacter{\dosixtupleempty\dodefinemathcharacter} - \def\definemathcommand {\dotripleempty \dodefinemathcommand} - %\newcounter\mathcolor - \def\dodefinemathsymbol[##1][##2][##3][##4][##5][##6]% - {%\doifcolorelse{math \purefamilyhex{##3}}{} - % {\increment\mathcolor - % \definecolor[math \purefamilyhex{##3}][\mathcolor]}% - \setbox0\hbox spread 1em{\mathematics{\getvalue{##1}{}{}{}}}% - \ifdim\wd0>\dimen0 \dimen0=\wd0 \fi - \setbox2\hbox spread 1em{\hbox to 1em{\tttf\purefamilyhex{##3}\hss}\box0 ##1}% - \ifdim\wd2>\dimen2 \dimen2=\wd2 \fi} - \def\dodefinemathcharacter[##1][##2][##3][##4][##5][##6]{} - \def\dodefinemathcommand [##1][##2][##3]##4{} - \readsysfile{\f!mathprefix tex}\donothing\donothing - \readsysfile{\f!mathprefix ams}\donothing\donothing - \edef\encwidth{\the\dimen0} - \dimen0=\hsize - \advance\dimen0 2em - \advance\dimen2 2em - \ifcase\showmathmodern\or\advance\dimen2 4em\fi - \divide \dimen0 by \dimen2 \advance\dimen0 1sp - \edef\enccols{\number\dimen0} - \startcolumns[\c!n=\enccols,\c!distance=2em] - \def\dodefinemathsymbol[##1][##2][##3][##4][##5][##6]% - {%\localcolortrue - %\color - % [math \purefamilyhex{##3}] - {\hbox - {\ifcase\showmathmodern\or - \hbox to \encwidth{\modern\let\mathcollection\nomathcollection\mathematics{\getvalue{##1}{}{}{}}\hss}% - \fi - \hbox to \encwidth{\mathematics{\getvalue{##1}{}{}{}}\hss}% - \hbox to 1em{\tttf\purefamilyhex{##3}\hss}##1}\par}} - \readsysfile{\f!mathprefix tex}\donothing\donothing - \readsysfile{\f!mathprefix ams}\donothing\donothing - \stopcolumns - \stoptextrule - \egroup} - -% \definecolor[math \purefamilyhex{mr}] [darkred] -% \definecolor[math \purefamilyhex{mi}] [darkgreen] -% \definecolor[math \purefamilyhex{sy}] [darkblue] -% \definecolor[math \purefamilyhex{ex}] [darkmagenta] -% \definecolor[math \purefamilyhex{nn}] [darkyellow] -% \definecolor[math \purefamilyhex{ma}] [lightred] -% \definecolor[math \purefamilyhex{mb}] [lightgreen] -% \definecolor[math \purefamilyhex{mc}] [lightblue] -% \definecolor[math \purefamilyhex{md}] [lightmagenta] - -\gdef\showmathtoken#1% - {\starttabulate[|lT|lT|lT|l|] - \NC token \NC #1 \NC \NR - \NC collection \NC \ifcsname\@mt@\mathcollection#1\endcsname - \mathcollection - \else\ifcsname\@mt@\nomathcollection#1\endcsname - \nomathcollection - \else - ?% - \fi\fi \NC \NR - \NC visualization \NC \mathematics{\getvalue{#1}} \NC \NR - \NC definition \NC \tttf \@EA\defconvertedcommand\@EA\ascii\csname\@mt@\mathcollection#1\endcsname \ascii \NC \NR - \stoptabulate} - -\protect \endinput diff --git a/tex/context/base/math-scr.mkiv b/tex/context/base/math-scr.mkiv new file mode 100644 index 000000000..43355679f --- /dev/null +++ b/tex/context/base/math-scr.mkiv @@ -0,0 +1,215 @@ +%D \module +%D [ file=math-scr, +%D version=2007.07.19, +%D title=\CONTEXT\ Math Macros, +%D subtitle=Scripts, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=\PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Math Macros / Scripts} + +\unprotect + +%D \macros +%D {super, sub} +%D +%D \TEX\ uses \type{^} and \type{_} for entering super- and +%D subscript mode. We want however a bit more control than +%D normally provided, and therefore provide \type {\super} +%D and \type{sub}. + +\global\let\normalsuper=^ +\global\let\normalsuber=_ + +\newcount\supersubmode + +\newevery\everysupersub \EverySuperSub + +\appendtoks \advance\supersubmode \plusone \to \everysupersub + +\appendtoks + \gridsupsubstyle +\to \everysupersub + +\appendtoks + \doifelse\@@mtsize\v!small + {\let\gridsupsubstyle \scriptscriptstyle + \let\gridsupsubbodyfont \setsmallbodyfont}% + {\let\gridsupsubstyle \scriptstyle + \let\gridsupsubbodyfont \relax}% +\to \everysetuptextformulas + +\setuptextformulas + [\c!size=\v!normal] + +\def\dogridsupsub#1#2% + {\begingroup + \setbox\nextbox\iftracegridsnapping\ruledhbox\else\hbox\fi + {\gridsupsubbodyfont + $\strut^{\the\everysupersub#1}_{\the\everysupersub#2}$}% + \nextboxht\strutheight + \nextboxdp\strutdepth + \flushnextbox + \endgroup} + +\def\gridsupsub + {\ifconditional\crazymathsnapping + \ifgridsnapping + \@EAEAEA\dogridsupsub + \else + \@EAEAEA\normalsupsub + \fi + \else + \@EA\normalsupsub + \fi} + +\def\normalsupsub#1#2% + {^{\the\everysupersub#1}_{\the\everysupersub#2}} + +\appendtoks + \let\gridsupsubstyle \relax + \let\gridsupsubbodyfont\relax + \let\gridsupsub \normalsupsub +\to \everydisplay + +\def\super#1{^{\the\everysupersub#1}} +\def\suber#1{_{\the\everysupersub#1}} +\def\supsub#1#2{\super{#1}\suber{#2}} +\def\subsup#1#2{\suber{#1}\super{#2}} + +%\def\super#1{\gridsupsub{#1}{}} % +%\def\suber#1{\gridsupsub{}{#1}} % +% +%\def\supsub#1#2{\gridsupsub{#1}{#2}} +%\def\subsup#1#2{\gridsupsub{#2}{#1}} + +\def\gridsuper#1{\gridsupsub{#1}{}} +\def\gridsuber#1{\gridsupsub{}{#1}} + +% \let\sup\super % math char +% \let\sub\suber + +% test set: +% +% \startbuffer +% \sform{x\frac{1}{2}} +% \sform{x\sup{\frac{1}{2}} + x\sup{2} + 2} +% \sform{x\supsub{\frac{1}{2}}{\frac{1}{2}} + x\sup{2} + 2} +% \stopbuffer +% +% \typebuffer +% +% \startlines +% \getbuffer +% \stoplines +% +% \startbuffer +% $x\frac{1}{2}$ +% $x\sup{\frac{1}{2}} + x^2 + 2$ +% $x\supsub{\frac{1}{2}}{\frac{1}{2}} + x^2 + 2$ +% \stopbuffer +% +% \typebuffer +% +% \start +% \enablesupersub +% \enableautomath +% \startlines +% \getbuffer +% \stoplines +% \stop + +%D \macros +%D {enablesupersub,enablesimplesupersub} +%D +%D We can let \type {^} and \type {_} act like \type {\super} +%D and \type {\sub} by saying \type {\enablesupersub}. + +\bgroup +\catcode`\^=\@@active +\catcode`\_=\@@active +\gdef\enablesupersub + {\catcode`\^=\@@active + \def^{\ifmmode\expandafter\super\else\expandafter\normalsuper\fi}% + \catcode`\_=\@@active + \def_{\ifmmode\expandafter\suber\else\expandafter\normalsuber\fi}} +\egroup + +%D \macros +%D {restoremathstyle} +%D +%D We can pick up the current math style by calling \type +%D {\restoremathstyle}. + +\def\restoremathstyle + {\ifmmode + \ifcase\supersubmode + \textstyle + \or + \scriptstyle + \else + \scriptscriptstyle + \fi + \fi} + +%D These macros were first needed by Frits Spijker (also +%D known as Gajes) for typesetting the minus sign that is +%D keyed into scientific calculators. + +% This is the first alternative, which works okay for the +% minus, but less for the plus. +% +% \def\dodoraisedmathord#1#2#3% +% {\mathord{{#2\raise.#1ex\hbox{#2#3}}}} +% +% \def\doraisedmathord#1% +% {\mathchoice +% {\dodoraisedmathord5\tf #1}% +% {\dodoraisedmathord5\tf #1}% +% {\dodoraisedmathord4\tfx #1}% +% {\dodoraisedmathord3\tfxx#1}} +% +% \def\negative{\doraisedmathord-} +% \def\positive{\doraisedmathord+} +% +% So, now we use the monospaced signs, that we also +% define as symbol, so that they can be overloaded. + +\def\dodoraisedmathord#1#2#3% + {\mathord{{#2\raise.#1ex\hbox{#2\symbol[#3]}}}} + +\def\doraisedmathord#1% + {\mathchoice + {\dodoraisedmathord5\tf {#1}}% + {\dodoraisedmathord5\tf {#1}}% + {\dodoraisedmathord4\tx {#1}}% + {\dodoraisedmathord3\txx{#1}}} + +\def\dodonumbermathord#1#2% + {\setbox\scratchbox\hbox{0}% + \mathord{\hbox to \wd\scratchbox{\hss#1\symbol[#2]\hss}}} + +\def\donumbermathord#1% + {\mathchoice + {\dodonumbermathord\tf {#1}}% + {\dodonumbermathord\tf {#1}}% + {\dodonumbermathord\tx {#1}}% + {\dodonumbermathord\txx{#1}}} + +\definesymbol[positive] [\getglyph{Mono}{+}] +\definesymbol[negative] [\getglyph{Mono}{-}] +\definesymbol[zeroamount][\getglyph{Mono}{-}] + +\def\negative {\doraisedmathord{negative}} +\def\positive {\doraisedmathord{positive}} +\def\zeroamount{\donumbermathord{zeroamount}} + +%D How negative such a symbol looks is demonstrated in: +%D $\negative 10^{\negative 10^{\negative 10}}$. + +\protect \endinput diff --git a/tex/context/base/math-tex.tex b/tex/context/base/math-tex.tex index 752f113b7..c833db956 100644 --- a/tex/context/base/math-tex.tex +++ b/tex/context/base/math-tex.tex @@ -232,7 +232,7 @@ \stopmathcollection \def\PLAINangle - {{\vbox{\ialign{$\m@th\scriptstyle##$\crcr + {{\vbox{\ialign{$\mathsurround\zeropoint\scriptstyle##$\crcr \not\mathrel{\mkern14mu}\crcr \noalign{\nointerlineskip} \mkern2.5mu\leaders\hrule height.34pt\hfill\mkern2.5mu\crcr}}}} @@ -424,12 +424,12 @@ {\cdotp\cdotp\cdotp} \def\PLAINvdots - {\vbox{\baselineskip4\p@ \lineskiplimit\z@ - \kern6\p@\hbox{.}\hbox{.}\hbox{.}}} + {\vbox{\baselineskip.4\bodyfontsize\lineskiplimit\zeropoint + \kern.6\bodyfontsize\hbox{.}\hbox{.}\hbox{.}}} \def\PLAINddots - {\mkern1mu\raise7\p@\vbox{\kern7\p@\hbox{.}}\mkern2mu - \raise4\p@\hbox{.}\mkern2mu\raise\p@\hbox{.}\mkern1mu} + {\mkern1mu\raise.7\bodyfontsize\vbox{\kern.7\bodyfontsize\hbox{.}}\mkern2mu + \raise.4\bodyfontsize\hbox{.}\mkern2mu\raise.1\bodyfontsize\hbox{.}\mkern1mu} \startmathcollection[default] @@ -521,7 +521,7 @@ \def\notsosqrt[#1]{\root#1\of} -\unexpanded\def\sqrt{\doifnextcharelse[\notsosqrt\normalsqrt} +\unexpanded\def\sqrt{\doifnextoptionalelse\notsosqrt\normalsqrt} \def\PLAINbig {\@@dobig{0.85}} \def\PLAINBig {\@@dobig{1.15}} @@ -561,12 +561,12 @@ \stopmathcollection \def\PLAINroot#1#2% - {\setbox\z@\hbox{$\m@th#1\sqrt{#2}$}\dimen@\ht\z@ - \advance\dimen@-\dp\z@ - \mkern5mu\raise.6\dimen@\copy\rootbox \mkern-10mu\box\z@} + {\setbox\zerocount\hbox{$\mathsurround\zeropoint#1\sqrt{#2}$}\dimen@\ht\zerocount + \advance\dimen@-\dp\zerocount + \mkern5mu\raise.6\dimen@\copy\rootbox \mkern-10mu\box\zerocount} \def\PLAINmatrix#1% - {\null\,\vcenter{\normalbaselines\m@th + {\null\,\vcenter{\normalbaselines\mathsurround\zeropoint \ialign{\hfil$##$\hfil&&\quad\hfil$##$\hfil\crcr \mathstrut\crcr\noalign{\kern-\baselineskip} #1\crcr\mathstrut\crcr\noalign{\kern-\baselineskip}}}\,} @@ -651,7 +651,7 @@ %D The next macro vertically centeres its contents. \def\@center@math#1% - {\vcenter{\hbox{$\m@th#1$}}} + {\vcenter{\hbox{$\mathsurround\zeropoint#1$}}} \def\@center@colon {\mathpalette\@center@math{\colon}} @@ -712,7 +712,6 @@ \mkern7mu\mathchoice{\mkern2mu}{}{}{}% \let\dointlimits\egroup} - \setupmathematics [integral=nolimits] diff --git a/tex/context/base/math-tim.tex b/tex/context/base/math-tim.tex index de6561ba7..3b9aea103 100644 --- a/tex/context/base/math-tim.tex +++ b/tex/context/base/math-tim.tex @@ -1,6 +1,6 @@ %D \module %D [ file=math-tim, -%D version=2001.04.12, +%D version=2001.04.12, %D title=\CONTEXT\ Math Macros, %D subtitle=Mathtime Specials, %D author={Hans Hagen \& Taco Hoekwater}, @@ -13,24 +13,24 @@ \endinput % i will clean this up after taco has gone over it -%D With thanks to Berthold Horn from YandY for providing me -%D evaluation copies of the MathTimePlus fonts. +%D With thanks to Berthold Horn from YandY for providing me +%D evaluation copies of the MathTimePlus fonts. % version 0 : Michael Spivak % version 1 : Taco Hoekwater % version 2 : Hans Hagen -% version 3 : etc etc etc +% version 3 : etc etc etc \unprotect %D We use the predefined spare families \type {\mcfam} and -%D \type {\mdfam}. +%D \type {\mdfam}. \let\cafam\mcfam \let\hexcafam\hexmcfam \let\gbfam\mdfam \let\hexgbfam\hexmdfam \let\gkfam\mdfam \let\hexgkfam\hexmdfam -% Why is this needed? +% Why is this needed? % \font\tenmd =mtgu at 10pt % \font\sevenmd=mtgu at 7.6pt @@ -38,7 +38,7 @@ % \font\tenmc =mtms at 10pt % \font\sevenmc=mtms at 7.6pt % \font\fivemc =mtms at 6pt -% +% % \textfont \mcfam\tenmc \textfont \mdfam\tenmd % \scriptfont \mcfam\sevenmc \scriptfont \mdfam\sevenmd % \scriptscriptfont\mcfam\fivemc \scriptscriptfont\mdfam\fivemd @@ -52,7 +52,7 @@ % \definealternativestyle[script] [\ca][\ca] % \definealternativestyle[greek] [\gk][\gk] -% \definealternativestyle[boldgreek][\gb][\gb] +% \definealternativestyle[boldgreek][\gb][\gb] % \definebodyfont % [5pt,6pt,7pt,8pt,9pt,10pt,11pt,12pt,14.4pt] [rm] @@ -60,9 +60,9 @@ % gk=mtgu sa 1, % gb=mtgub sa 1] -%D Since a font size is a rather fuzzy thing, it will be no -%D surprise that the Math Times fonts have different specs -%D than the Computer Modern Roman fonts. +%D Since a font size is a rather fuzzy thing, it will be no +%D surprise that the Math Times fonts have different specs +%D than the Computer Modern Roman fonts. %D %D \starttabulate[|Bl|c|c|c|c|c|c|c|c|c|c|] %D \NC Computer Modern\NC @@ -71,9 +71,9 @@ %D 6.0\NC6.8\NC7.6\NC8.4\NC9.2\NC10.0\NC10.8\NC11.6\NC13.2\NC--\NC\NR %D \stoptabulate %D -%D The following definitions presume the existence of \type -%D {tio} and \type {tibio} font alternatives. Definitions for -%D \type {\tf.} etc and \type {\sc} are left as they are. +%D The following definitions presume the existence of \type +%D {tio} and \type {tibio} font alternatives. Definitions for +%D \type {\tf.} etc and \type {\sc} are left as they are. %D moved code @@ -100,10 +100,10 @@ \def\tildehex{7E} \def\ddothex {7F} -%D The \type {mtex} fonts need a recalculation of \type +%D The \type {mtex} fonts need a recalculation of \type %D {\p@renwd}, which in \CONTEXT\ is done automatically. -%D The following definitions are mostly copied from the file +%D The following definitions are mostly copied from the file %D \type {mtmacs.tex}, which banner said: %D %D \starttyping @@ -112,9 +112,9 @@ %D ALL RIGHTS RESERVED %D \stoptyping %D -%D We reformatted the macros and changed a few bits and -%D pieces. A further cleanup with regards to the scratch -%D registers will be done later. +%D We reformatted the macros and changed a few bits and +%D pieces. A further cleanup with regards to the scratch +%D registers will be done later. \mathchardef\Gamma = "0130 \mathchardef\Delta = "0131 @@ -171,7 +171,7 @@ % like \rm (cf. the texbook page 290) \def\ifdefaultfamelse#1#2% - {\ifnum\fam=\m@ne\mathaccent#1\else\mathaccent#2\fi} + {\ifnum\fam=\minusone\mathaccent#1\else\mathaccent#2\fi} \let\noaccents@\relax @@ -190,10 +190,10 @@ \def\mathhexbox@#1#2#3% {\relax \ifmmode - \mathpalette{}{\m@th\rm\mathchar"#1#2#3}% + \mathpalette{}{\mathsurround\zeropoint\rm\mathchar"#1#2#3}% \else \leavevmode - \hbox{$\m@th\rm\mathchar"#1#2#3$}% + \hbox{$\mathsurround\zeropoint\rm\mathchar"#1#2#3$}% \fi} \def\dag {\edef\next@{0\daghex }\expandafter\mathhexbox@\next@} @@ -204,16 +204,16 @@ \def\vdots% {\vbox - {\baselineskip4\p@ - \lineskiplimit\z@ - \kern6\p@\hbox{$\m@th.$}\hbox{$\m@th.$}\hbox{$\m@th.$}}} + {\baselineskip4\points + \lineskiplimit\zeropoint + \kern6\points\hbox{$\mathsurround\zeropoint.$}\hbox{$\mathsurround\zeropoint.$}\hbox{$\mathsurround\zeropoint.$}}} \def\ddots% {\mathinner {\mkern1mu - \raise7\p@\vbox{\kern 7\p@\hbox{$\m@th.$}}\mkern2mu - \raise4\p@\hbox{$\m@th.$}\mkern2mu - \raise \p@\hbox{$\m@th.$}\mkern1mu}} + \raise7\points\vbox{\kern 7\points\hbox{$\mathsurround\zeropoint.$}}\mkern2mu + \raise4\points\hbox{$\mathsurround\zeropoint.$}\mkern2mu + \raise \points\hbox{$\mathsurround\zeropoint.$}\mkern1mu}} \def\hbar {{\mathchoice @@ -224,10 +224,10 @@ \mkern-6.3muh}} \def\angle% - {{\vbox{\ialign{$\m@th\scriptstyle##$\crcr + {{\vbox{\ialign{$\mathsurround\zeropoint\scriptstyle##$\crcr \not\mathrel{\mkern14mu}\crcr \noalign{\nointerlineskip} - \mkern2.5mu\leaders\hrule height.48\p@\hfill\mkern2.5mu\crcr}}}} + \mkern2.5mu\leaders\hrule height.48\points\hfill\mkern2.5mu\crcr}}}} \newdimen\amstexex @@ -235,25 +235,25 @@ \def\varinjlim% {\mathop{\vtop{\ialign{##\crcr - \hfil\the\textfont\z@ lim\hfil\crcr + \hfil\the\textfont\zerocount lim\hfil\crcr \noalign{\nointerlineskip}\rightarrowfill\crcr \noalign{\nointerlineskip\kern-\amstexex}\crcr}}}} \def\varprojlim% {\mathop{\vtop{\ialign{##\crcr - \hfil\the\textfont\z@ lim\hfil\crcr + \hfil\the\textfont\zerocount lim\hfil\crcr \noalign{\nointerlineskip}\leftarrowfill\crcr \noalign{\nointerlineskip\kern-\amstexex}\crcr}}}} \def\varliminf{\mathop{\underbar {lim}}} % context-ified \def\varlimsup{\mathop{\overstrike{lim}}} % context-ified -\def\spdot {^{\hbox{\raise\amstexex\hbox{\the\textfont\z@ .}}}} -\def\spddot {^{\hbox{\raise\amstexex\hbox{\the\textfont\z@ ..}}}} -\def\spdddot {^{\hbox{\raise\amstexex\hbox{\the\textfont\z@ ...}}}} -\def\spddddot{^{\hbox{\raise\amstexex\hbox{\the\textfont\z@....}}}} +\def\spdot {^{\hbox{\raise\amstexex\hbox{\the\textfont\zerocount .}}}} +\def\spddot {^{\hbox{\raise\amstexex\hbox{\the\textfont\zerocount ..}}}} +\def\spdddot {^{\hbox{\raise\amstexex\hbox{\the\textfont\zerocount ...}}}} +\def\spddddot{^{\hbox{\raise\amstexex\hbox{\the\textfont\zerocount....}}}} -%D Here some code is merged in order to save strings. +%D Here some code is merged in order to save strings. \def\domultidot#1#2% {\setbox0\hbox{$#1#2$}% @@ -303,29 +303,29 @@ \fi} \def\root#1\of#2% - {\setbox\rootbox=\hbox{$\m@th\scriptscriptstyle{#1}$}% + {\setbox\rootbox=\hbox{$\mathsurround\zeropoint\scriptscriptstyle{#1}$}% \mathpalette\r@@t{#2}} \def\r@@t#1#2% - {\setbox\z@=\hbox{$\uproot@\z@\leftroot\z@\m@th#1\sqrt{#2}$}% - \dimen@\ht\z@\advance\dimen@-\dp\z@ + {\setbox\zerocount\hbox{$\uproot@\zerocount\leftroot\zerocount\mathsurround\zeropoint#1\sqrt{#2}$}% + \dimen@\ht\zerocount\advance\dimen@-\dp\zerocount \dimen@ii\dimen@ - \ifdim\dimen@>30\p@ \advance\dimen@ii-16\p@ \else - \ifdim\dimen@>24\p@ \advance\dimen@ii -8\p@ \else - \ifdim\dimen@>18\p@ \advance\dimen@ii -6\p@ \else - \ifdim\dimen@>12\p@ \advance\dimen@ii -4\p@ \else - \ifdim\dimen@>10\p@ \advance\dimen@ii -2\p@ \fi\fi\fi\fi\fi - \setbox\tw@=\hbox{$\m@th#1\mskip\uproot@ mu$}% - \advance\dimen@ii by1.667\wd\tw@ + \ifdim\dimen@>30\points \advance\dimen@ii-16\points \else + \ifdim\dimen@>24\points \advance\dimen@ii -8\points \else + \ifdim\dimen@>18\points \advance\dimen@ii -6\points \else + \ifdim\dimen@>12\points \advance\dimen@ii -4\points \else + \ifdim\dimen@>10\points \advance\dimen@ii -2\points \fi\fi\fi\fi\fi + \setbox\plustwo=\hbox{$\mathsurround\zeropoint#1\mskip\uproot@ mu$}% + \advance\dimen@ii by1.667\wd\plustwo \mkern-\leftroot@ mu\mkern5mu\raise.6\dimen@ii\copy\rootbox - \mkern-8mu\mkern\leftroot@ mu\box\z@\leftroot\z@\uproot\z@} + \mkern-8mu\mkern\leftroot@ mu\box\zerocount\leftroot\zerocount\uproot\zerocount} \def\space@.{\futurelet\space@\relax} \space@. % really needed ? \def\jadjust% - {\mkern-\tw@ mu} + {\mkern-\plustwo mu} -%D For the moment the following code is left unchanged. It is +%D For the moment the following code is left unchanged. It is %D not used anyway. \newif\ifsubscriptcorrection \subscriptcorrectionfalse @@ -358,11 +358,11 @@ \else \def\next@.% {\ifx\next j% - \mkern-\tw@ mu\else + \mkern-\plustwo mu\else \ifx\next f% - \mkern-\tw@ mu\else + \mkern-\plustwo mu\else \ifx\next p% - \mkern-\@ne mu\fi\fi\fi}% + \mkern-\plusone mu\fi\fi\fi}% \fi \next@.} diff --git a/tex/context/base/math-vfu.lua b/tex/context/base/math-vfu.lua new file mode 100644 index 000000000..35d18d77a --- /dev/null +++ b/tex/context/base/math-vfu.lua @@ -0,0 +1,1534 @@ +if not modules then modules = { } end modules ['math-vfu'] = { + version = 1.001, + comment = "companion to math-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- All these math vectors .. thanks to Aditya and Mojca they become +-- better and better. + +local type, next = type, next + +local trace_virtual = false trackers.register("math.virtual", function(v) trace_virtual = v end) +local trace_timings = false trackers.register("math.timings", function(v) trace_timings = v end) + +fonts.enc.math = fonts.enc.math or { } + +local shared = { } + +fonts.vf.math = fonts.vf.math or { } +fonts.vf.math.optional = false + +local push, pop, back = { "push" }, { "pop" }, { "slot", 1, 0x2215 } + +local function negate(main,unicode,basecode) + local characters = main.characters + if not characters[unicode] then + local basechar = characters[basecode] + if basechar then + local ht, wd = basechar.height, basechar.width + characters[unicode] = { + width = wd, + height = ht, + depth = basechar.depth, + italic = basechar.italic, + kerns = basechar.kerns, + commands = { + { "slot", 1, basecode }, + push, + { "down", ht/5}, + { "right", - wd/2}, + back, + push, + } + } + end + end +end + +--~ \Umathchardef\braceld="0 "1 "FF07A +--~ \Umathchardef\bracerd="0 "1 "FF07B +--~ \Umathchardef\bracelu="0 "1 "FF07C +--~ \Umathchardef\braceru="0 "1 "FF07D + +local function brace(main,unicode,first,rule,left,right,rule,last) + local characters = main.characters + if not characters[unicode] then + characters[unicode] = { + horiz_variants = { + { extender = 0, glyph = first }, + { extender = 1, glyph = rule }, + { extender = 0, glyph = left }, + { extender = 0, glyph = right }, + { extender = 1, glyph = rule }, + { extender = 0, glyph = last }, + } + } + end +end + +local function arrow(main,unicode,arrow,minus,isleft) + if isleft then + t = { + { extender = 0, glyph = arrow }, + { extender = 1, glyph = minus }, + } + else + t = { + { extender = 0, glyph = minus }, + { extender = 1, glyph = arrow }, + } + end +--~ main.characters[unicode] = { horiz_variants = t } + main.characters[unicode].horiz_variants = t +end + +local function parent(main,unicode,first,rule,last) + local characters = main.characters + if not characters[unicode] then + characters[unicode] = { + horiz_variants = { + { extender = 0, glyph = first }, + { extender = 1, glyph = rule }, + { extender = 0, glyph = last }, + } + } + end +end + +local push, pop, step = { "push" }, { "pop" }, 0.2 -- 0.1 is nicer but gives larger files + +local function make(main,id,size,n,m) + local characters = main.characters + local xu = main.parameters.x_height + 0.3*size + local xd = 0.3*size + local old, upslot, dnslot, uprule, dnrule = 0xFF000+n, 0xFF100+n, 0xFF200+n, 0xFF300+m, 0xFF400+m + local c = characters[old] + if c then + local w, h, d = c.width, c.height, c.depth + local thickness = h - d + local rulewidth = step*size -- we could use an overlap + local slot = { "slot", id, old } + local rule = { "rule", thickness, rulewidth } + local up = { "down", -xu } + local dn = { "down", xd } + local ht, dp = xu + 3*thickness, 0 + if not characters[uprule] then + characters[uprule] = { width = rulewidth, height = ht, depth = dp, commands = { push, up, rule, pop } } + end + characters[upslot] = { width = w, height = ht, depth = dp, commands = { push, up, slot, pop } } + local ht, dp = 0, xd + 3*thickness + if not characters[dnrule] then + characters[dnrule] = { width = rulewidth, height = ht, depth = dp, commands = { push, dn, rule, pop } } + end + characters[dnslot] = { width = w, height = ht, depth = dp, commands = { push, dn, slot, pop } } + end +end + +local function minus(main,id,size,unicode) + local characters = main.characters + local mu = size/18 + local minus = characters[0x002D] + local width = minus.width - 5*mu + characters[unicode] = { + width = width, height = minus.height, depth = minus.depth, + commands = { push, { "right", -3*mu }, { "slot", id, 0x002D }, pop } + } +end + +local function dots(main,id,size,unicode) + local characters = main.characters + local c = characters[0x002E] + local w, h, d = c.width, c.height, c.depth + local mu = size/18 + local right3mu = { "right", 3*mu } + local right1mu = { "right", 1*mu } + local up1size = { "down", -.1*size } + local up4size = { "down", -.4*size } + local up7size = { "down", -.7*size } + local right2muw = { "right", 2*mu + w } + local slot = { "slot", id, 0x002E } + if unicode == 0x22EF then + local c = characters[0x022C5] + if c then + local w, h, d = c.width, c.height, c.depth + local slot = { "slot", id, 0x022C5 } + characters[unicode] = { + width = 3*w + 2*3*mu, height = h, depth = d, + commands = { push, slot, right3mu, slot, right3mu, slot, pop } + } + end + elseif unicode == 0x22EE then + -- weird height ! + characters[unicode] = { + width = w, height = h+(1.4)*size, depth = 0, + commands = { push, push, slot, pop, up4size, push, slot, pop, up4size, slot, pop } + } + elseif unicode == 0x22F1 then + characters[unicode] = { + width = 3*w + 6*size/18, height = 1.5*size, depth = 0, + commands = { + push, + right1mu, + push, up7size, slot, pop, + right2muw, + push, up4size, slot, pop, + right2muw, + push, up1size, slot, pop, + right1mu, + pop + } + } + elseif unicode == 0x22F0 then + characters[unicode] = { + width = 3*w + 6*size/18, height = 1.5*size, depth = 0, + commands = { + push, + right1mu, + push, up1size, slot, pop, + right2muw, + push, up4size, slot, pop, + right2muw, + push, up7size, slot, pop, + right1mu, + pop + } + } + else + characters[unicode] = { + width = 3*w + 2*3*mu, height = h, depth = d, + commands = { push, slot, right3mu, slot, right3mu, slot, pop } + } + end +end + +function fonts.vf.math.alas(main,id,size) + for i=0x7A,0x7D do + make(main,id,size,i,1) + end + brace (main,0x23DE,0xFF17A,0xFF301,0xFF17D,0xFF17C,0xFF301,0xFF17B) + brace (main,0x23DF,0xFF27C,0xFF401,0xFF27B,0xFF27A,0xFF401,0xFF27D) + parent(main,0x23DC,0xFF17A,0xFF301,0xFF17B) + parent(main,0x23DD,0xFF27C,0xFF401,0xFF27D) + negate(main,0x2260,0x003D) + dots(main,id,size,0x2026) -- ldots + dots(main,id,size,0x22EE) -- vdots + dots(main,id,size,0x22EF) -- cdots + dots(main,id,size,0x22F1) -- ddots + dots(main,id,size,0x22F0) -- udots + minus(main,id,size,0xFF501) + arrow(main,0x2190,0xFE190,0xFF501,true) -- left + arrow(main,0x2192,0xFE192,0xFF501,false) -- right +end + +local reverse -- index -> unicode + +function fonts.basecopy(tfmtable) + local t, c, p = { }, { }, { } + for k, v in next, tfmtable do + t[k] = v + end + for k, v in next, tfmtable.characters do + c[k] = v + end + for k, v in next, tfmtable.parameters do + p[k] = v + end + t.characters, t.parameters = c, p + return t +end + +function fonts.vf.math.define(specification,set) + if not reverse then + reverse = { } + for k, v in next, fonts.enc.math do + local r = { } + for u, i in next, v do + r[i] = u + end + reverse[k] = r + end + end + local name = specification.name -- symbolic name + local size = specification.size -- given size + local fnt, lst, main = { }, { }, nil + local start = (trace_virtual or trace_timings) and os.clock() +--~ texio.write_nl("defining font " .. name .. " " .. size) + local okset, n = { }, 0 + for s=1,#set do + local ss = set[s] + local ssname = ss.name + if ss.optional and fonts.vf.math.optional then + if trace_virtual then + logs.report("math virtual","loading font %s subfont %s with name %s at %s is skipped",name,s,ssname,size) + end + else + if ss.features then ssname = ssname .. "*" .. ss.features end + if ss.main then main = s end + local f, id = fonts.tfm.read_and_define(ssname,size) + if not f then + logs.report("math virtual","loading font %s subfont %s with name %s at %s is skipped, not found",name,s,ssname,size) + else + n = n + 1 + okset[n] = ss + fnt[n] = f + lst[n] = { id = id, size = size } + if not shared[s] then shared[n] = { } end + if trace_virtual then + logs.report("math virtual","loading font %s subfont %s with name %s at %s as id %s using encoding %s",name,s,ssname,size,id,ss.vector or "none") + end + end + end + end + -- beware, fnt[1] is already passed to tex (we need to make a simple copy then .. todo) + main = fonts.basecopy(fnt[1]) + main.name, main.fonts, main.virtualized, main.math_parameters = name, lst, true, { } + local characters, descriptions = main.characters, main.descriptions + main.parameters.x_height = main.parameters.x_height or 0 + for s=1,n do + local ss, fs = okset[s], fnt[s] + if not fs then + -- skip, error + elseif ss.optional and fonts.vf.math.optional then + -- skip, redundant + else + local mm, fp = main.math_parameters, fs.parameters + if ss.extension then + mm.math_x_height = fp.x_height or 0 -- math_x_height height of x + mm.default_rule_thickness = fp[ 8] or 0 -- default_rule_thickness thickness of \over bars + mm.big_op_spacing1 = fp[ 9] or 0 -- big_op_spacing1 minimum clearance above a displayed op + mm.big_op_spacing2 = fp[10] or 0 -- big_op_spacing2 minimum clearance below a displayed op + mm.big_op_spacing3 = fp[11] or 0 -- big_op_spacing3 minimum baselineskip above displayed op + mm.big_op_spacing4 = fp[12] or 0 -- big_op_spacing4 minimum baselineskip below displayed op + mm.big_op_spacing5 = fp[13] or 0 -- big_op_spacing5 padding above and below displayed limits + -- logs.report("math virtual","loading and virtualizing font %s at size %s, setting ex parameters",name,size) + elseif ss.parameters then + main.parameters.x_height = fp.x_height or main.parameters.x_height + mm.x_height = mm.x_height or fp.x_height or 0 -- x_height height of x + mm.num1 = fp[ 8] or 0 -- num1 numerator shift-up in display styles + mm.num2 = fp[ 9] or 0 -- num2 numerator shift-up in non-display, non-\atop + mm.num3 = fp[10] or 0 -- num3 numerator shift-up in non-display \atop + mm.denom1 = fp[11] or 0 -- denom1 denominator shift-down in display styles + mm.denom2 = fp[12] or 0 -- denom2 denominator shift-down in non-display styles + mm.sup1 = fp[13] or 0 -- sup1 superscript shift-up in uncramped display style + mm.sup2 = fp[14] or 0 -- sup2 superscript shift-up in uncramped non-display + mm.sup3 = fp[15] or 0 -- sup3 superscript shift-up in cramped styles + mm.sub1 = fp[16] or 0 -- sub1 subscript shift-down if superscript is absent + mm.sub2 = fp[17] or 0 -- sub2 subscript shift-down if superscript is present + mm.sup_drop = fp[18] or 0 -- sup_drop superscript baseline below top of large box + mm.sub_drop = fp[19] or 0 -- sub_drop subscript baseline below bottom of large box + mm.delim1 = fp[20] or 0 -- delim1 size of \atopwithdelims delimiters in display styles + mm.delim2 = fp[21] or 0 -- delim2 size of \atopwithdelims delimiters in non-displays + mm.axis_height = fp[22] or 0 -- axis_height height of fraction lines above the baseline + -- logs.report("math virtual","loading and virtualizing font %s at size %s, setting sy parameters",name,size) + end + local vectorname = ss.vector + if vectorname then + local offset = 0xFF000 + local vector = fonts.enc.math[vectorname] + local rotcev = reverse[vectorname] + if vector then + local fc, fd, si = fs.characters, fs.descriptions, shared[s] + local skewchar = ss.skewchar + for unicode, index in next, vector do + local fci = fc[index] + if not fci then + -- if trace_virtual then + logs.report("math virtual", "unicode point U+%04X has no index %04X in %s",unicode,index,vectorname) + -- end + else + local ref = si[index] + if not ref then + ref = { { 'slot', s, index } } + si[index] = ref + end + local kerns = fci.kerns + if kerns then + local width = fci.width + local krn = { } + for k=1,#kerns do + local rk = rotcev[k] + if rk then + krn[rk] = kerns[k] + end + end + if not next(krn) then + krn = nil + end + local t = { + width = width, + height = fci.height, + depth = fci.depth, + italic = fci.italic, + kerns = krn, + commands = ref, + } + if skewchar and kerns then + local k = kerns[skewchar] + if k then + t.top_accent = width/2 + k + end + end + characters[unicode] = t + else + characters[unicode] = { + width = fci.width, + height = fci.height, + depth = fci.depth, + italic = fci.italic, + commands = ref, + } + end + end + end + if ss.extension then + -- todo: if multiple ex, then 256 offsets per instance + local extension = fonts.enc.math["large-to-small"] + local variants_done = fs.variants_done + for index, fci in next, fc do -- the raw ex file + if type(index) == "number" then + local ref = si[index] + if not ref then + ref = { { 'slot', s, index } } + si[index] = ref + end + local t = { + width = fci.width, + height = fci.height, + depth = fci.depth, + italic = fci.italic, + commands = ref, + } + local n = fci.next + if n then + t.next = offset + n + elseif variants_done then + local vv = fci.vert_variants + if vv then + t.vert_variants = vv + end + local hv = fci.horiz_variants + if hv then + t.horiz_variants = hv + end + else + local vv = fci.vert_variants + if vv then + for i=1,#vv do + local vvi = vv[i] + vvi.glyph = vvi.glyph + offset + end + t.vert_variants = vv + end + local hv = fci.horiz_variants + if hv then + for i=1,#hv do + local hvi = hv[i] + hvi.glyph = hvi.glyph + offset + end + t.horiz_variants = hv + end + end + characters[offset + index] = t + end + end + fs.variants_done = true + for unicode, index in next, extension do + local cu = characters[unicode] + if cu then + cu.next = offset + index + --~ local n, c, d = unicode, cu, { } + --~ print("START", unicode) + --~ while n do + --~ n = c.next + --~ if n then + --~ print("NEXT", n) + --~ c = characters[n] + --~ if not c then + --~ print("EXIT") + --~ elseif d[n] then + --~ print("LOOP") + --~ break + --~ end + --~ d[n] = true + --~ end + --~ end + else + local fci = fc[index] + local ref = si[index] + if not ref then + ref = { { 'slot', s, index } } + si[index] = ref + end + local kerns = fci.kerns + if kerns then + local krn = { } + for k=1,#kerns do + krn[offset + k] = kerns[k] + end + characters[unicode] = { + width = fci.width, + height = fci.height, + depth = fci.depth, + italic = fci.italic, + commands = ref, + kerns = krn, + next = offset + index, + } + else + characters[unicode] = { + width = fci.width, + height = fci.height, + depth = fci.depth, + italic = fci.italic, + commands = ref, + next = offset + index, + } + end + end + end + end + end + end + mathematics.extras.copy(main) --not needed here (yet) + end + end + lst[#lst+1] = { id = font.nextid(), size = size } + fonts.vf.math.alas(main,#lst,size) + if trace_virtual or trace_timings then + logs.report("math virtual","loading and virtualizing font %s at size %s took %0.3f seconds",name,size,os.clock()-start) + end + main.has_italic = true + main.type = "virtual" -- not needed + mathematics.scaleparameters(main,main,1) + return main +end + +function mathematics.make_font(name, set) + fonts.define.methods[name] = function(specification) + return fonts.vf.math.define(specification,set) + end +end + +-- varphi is part of the alphabet, contrary to the other var*s' + +fonts.enc.math["large-to-small"] = { + [0x00028] = 0x00, -- ( + [0x00029] = 0x01, -- ) + [0x0005B] = 0x02, -- [ + [0x0005D] = 0x03, -- ] + [0x0230A] = 0x04, -- lfloor + [0x0230B] = 0x05, -- rfloor + [0x02308] = 0x06, -- lceil + [0x02309] = 0x07, -- rceil + [0x0007B] = 0x08, -- { + [0x0007D] = 0x09, -- } + [0x027E8] = 0x0A, -- < + [0x027E9] = 0x0B, -- > + [0x0007C] = 0x0C, -- | +--~ [0x0] = 0x0D, -- lVert rVert Vert +-- [0x0002F] = 0x0E, -- / + [0x0005C] = 0x0F, -- \ +--~ [0x0] = 0x3A, -- lgroup +--~ [0x0] = 0x3B, -- rgroup +--~ [0x0] = 0x3C, -- arrowvert +--~ [0x0] = 0x3D, -- Arrowvert + [0x02195] = 0x3F, -- updownarrow +--~ [0x0] = 0x40, -- lmoustache +--~ [0x0] = 0x41, -- rmoustache + [0x0221A] = 0x70, -- sqrt + [0x021D5] = 0x77, -- Updownarrow + [0x02191] = 0x78, -- uparrow + [0x02193] = 0x79, -- downarrow + [0x021D1] = 0x7E, -- Uparrow + [0x021D3] = 0x7F, -- Downarrow + [0x0220F] = 0x59, -- prod + [0x02210] = 0x61, -- coprod + [0x02211] = 0x58, -- sum + [0x0222B] = 0x5A, -- intop + [0x0222E] = 0x49, -- ointop + [0xFE302] = 0x62, -- widehat + [0xFE303] = 0x65, -- widetilde + [0x022C0] = 0x5E, -- bigwedge + [0x022C1] = 0x5F, -- bigvee + [0x022C2] = 0x5C, -- bigcap + [0x022C3] = 0x5B, -- bigcup + [0x02044] = 0x0E, -- / +} + +fonts.enc.math["tex-ex"] = { + [0x0220F] = 0x51, -- prod + [0x0222B] = 0x52, -- intop + [0x02210] = 0x60, -- coprod + [0x02211] = 0x50, -- sum + [0x022C0] = 0x56, -- bigwedge + [0x022C1] = 0x57, -- bigvee + [0x022C2] = 0x54, -- bigcap + [0x022C3] = 0x53, -- bigcup + [0x02A04] = 0x55, -- biguplus + [0x02A02] = 0x4E, -- bigotimes + [0x02A01] = 0x4C, -- bigoplus + [0x02A03] = 0x4A, -- bigodot + [0x0222E] = 0x48, -- ointop + [0x02A06] = 0x46, -- bigsqcup +} + +-- only math stuff is needed, since we always use an lm or gyre +-- font as main font + +fonts.enc.math["tex-mr"] = { + [0x00393] = 0x00, -- Gamma + [0x00394] = 0x01, -- Delta + [0x00398] = 0x02, -- Theta + [0x0039B] = 0x03, -- Lambda + [0x0039E] = 0x04, -- Xi + [0x003A0] = 0x05, -- Pi + [0x003A3] = 0x06, -- Sigma + [0x003A5] = 0x07, -- Upsilon + [0x003A6] = 0x08, -- Phi + [0x003A8] = 0x09, -- Psi + [0x003A9] = 0x0A, -- Omega +-- [0x00060] = 0x12, -- [math]grave +-- [0x000B4] = 0x13, -- [math]acute +-- [0x002C7] = 0x14, -- [math]check +-- [0x002D8] = 0x15, -- [math]breve +-- [0x000AF] = 0x16, -- [math]bar +-- [0x00021] = 0x21, -- ! +-- [0x00028] = 0x28, -- ( +-- [0x00029] = 0x29, -- ) +-- [0x0002B] = 0x2B, -- + +-- [0x0002F] = 0x2F, -- / +-- [0x0003A] = 0x3A, -- : +-- [0x02236] = 0x3A, -- colon +-- [0x0003B] = 0x3B, -- ; +-- [0x0003C] = 0x3C, -- < +-- [0x0003D] = 0x3D, -- = +-- [0x0003E] = 0x3E, -- > +-- [0x0003F] = 0x3F, -- ? + [0x00391] = 0x41, -- Alpha + [0x00392] = 0x42, -- Beta + [0x02145] = 0x44, + [0x00395] = 0x45, -- Epsilon + [0x00397] = 0x48, -- Eta + [0x00399] = 0x49, -- Iota + [0x0039A] = 0x4B, -- Kappa + [0x0039C] = 0x4D, -- Mu + [0x0039D] = 0x4E, -- Nu + [0x0039F] = 0x4F, -- Omicron + [0x003A1] = 0x52, -- Rho + [0x003A4] = 0x54, -- Tau + [0x003A7] = 0x58, -- Chi + [0x00396] = 0x5A, -- Zeta +-- [0x0005B] = 0x5B, -- [ +-- [0x0005D] = 0x5D, -- ] +-- [0x0005E] = 0x5E, -- [math]hat -- the text one + [0x00302] = 0x5E, -- [math]hat -- the real math one +-- [0x002D9] = 0x5F, -- [math]dot + [0x02146] = 0x64, + [0x02147] = 0x65, +-- [0x002DC] = 0x7E, -- [math]tilde -- the text one + [0x00303] = 0x7E, -- [math]tilde -- the real one +-- [0x000A8] = 0x7F, -- [math]ddot +} + +fonts.enc.math["tex-mi"] = { + [0x1D6E4] = 0x00, -- Gamma + [0x1D6E5] = 0x01, -- Delta + [0x1D6E9] = 0x02, -- Theta + [0x1D6F3] = 0x02, -- varTheta (not present in TeX) + [0x1D6EC] = 0x03, -- Lambda + [0x1D6EF] = 0x04, -- Xi + [0x1D6F1] = 0x05, -- Pi + [0x1D6F4] = 0x06, -- Sigma + [0x1D6F6] = 0x07, -- Upsilon + [0x1D6F7] = 0x08, -- Phi + [0x1D6F9] = 0x09, -- Psi + [0x1D6FA] = 0x0A, -- Omega + [0x1D6FC] = 0x0B, -- alpha + [0x1D6FD] = 0x0C, -- beta + [0x1D6FE] = 0x0D, -- gamma + [0x1D6FF] = 0x0E, -- delta + [0x1D716] = 0x0F, -- epsilon TODO: 1D716 + [0x1D701] = 0x10, -- zeta + [0x1D702] = 0x11, -- eta + [0x1D703] = 0x12, -- theta TODO: 1D703 + [0x1D704] = 0x13, -- iota + [0x1D705] = 0x14, -- kappa + [0x1D718] = 0x14, -- varkappa, not in tex fonts + [0x1D706] = 0x15, -- lambda + [0x1D707] = 0x16, -- mu + [0x1D708] = 0x17, -- nu + [0x1D709] = 0x18, -- xi + [0x1D70B] = 0x19, -- pi + [0x1D70C] = 0x1A, -- rho + [0x1D70E] = 0x1B, -- sigma + [0x1D70F] = 0x1C, -- tau + [0x1D710] = 0x1D, -- upsilon + [0x1D719] = 0x1E, -- phi + [0x1D712] = 0x1F, -- chi + [0x1D713] = 0x20, -- psi + [0x1D714] = 0x21, -- omega + [0x1D700] = 0x22, -- varepsilon (the other way around) + [0x1D717] = 0x23, -- vartheta + [0x1D71B] = 0x24, -- varpi + [0x1D71A] = 0x25, -- varrho + [0x1D70D] = 0x26, -- varsigma + [0x1D711] = 0x27, -- varphi (the other way around) + [0x021BC] = 0x28, -- leftharpoonup + [0x021BD] = 0x29, -- leftharpoondown + [0x021C0] = 0x2A, -- righttharpoonup + [0x021C1] = 0x2B, -- rightharpoondown + -- 0x2C, -- lhook (hook for combining arrows) + -- 0x2D, -- rhook (hook for combining arrows) + [0x022B3] = 0x2E, -- triangleright (TODO: which one is right?) + [0x022B2] = 0x2F, -- triangleleft (TODO: which one is right?) +-- [0x00041] = 0x30, -- 0 +-- [0x00041] = 0x31, -- 1 +-- [0x00041] = 0x32, -- 2 +-- [0x00041] = 0x33, -- 3 +-- [0x00041] = 0x34, -- 4 +-- [0x00041] = 0x35, -- 5 +-- [0x00041] = 0x36, -- 6 +-- [0x00041] = 0x37, -- 7 +-- [0x00041] = 0x38, -- 8 +-- [0x00041] = 0x39, -- 9 +--~ [0x0002E] = 0x3A, -- . + [0x0002C] = 0x3B, -- , + [0x0003C] = 0x3C, -- < +-- [0x0002F] = 0x3D, -- /, slash, solidus + [0x02044] = 0x3D, -- / AM: Not sure + [0x0003E] = 0x3E, -- > + [0x022C6] = 0x3F, -- star + [0x02202] = 0x40, -- partial +-- [0x00041] = 0x41, -- A + [0x1D6E2] = 0x41, -- Alpha +-- [0x00042] = 0x42, -- B + [0x1D6E3] = 0x42, -- Beta +-- [0x00043] = 0x43, -- C +-- [0x00044] = 0x44, -- D +-- [0x00045] = 0x45, -- E + [0x1D6E6] = 0x45, -- Epsilon +-- [0x00046] = 0x46, -- F +-- [0x00047] = 0x47, -- G +-- [0x00048] = 0x48, -- H + [0x1D6E8] = 0x48, -- Eta +-- [0x00049] = 0x49, -- I + [0x1D6EA] = 0x49, -- Iota +-- [0x0004A] = 0x4A, -- J +-- [0x0004B] = 0x4B, -- K + [0x1D6EB] = 0x4B, -- Kappa +-- [0x0004C] = 0x4C, -- L +-- [0x0004D] = 0x4D, -- M + [0x1D6ED] = 0x4D, -- Mu +-- [0x0004E] = 0x4E, -- N + [0x1D6EE] = 0x4E, -- Nu +-- [0x0004F] = 0x4F, -- O + [0x1D6F0] = 0x4F, -- Omicron +-- [0x00050] = 0x50, -- P + [0x1D6F2] = 0x50, -- Rho +-- [0x00051] = 0x51, -- Q +-- [0x00052] = 0x52, -- R +-- [0x00053] = 0x53, -- S +-- [0x00054] = 0x54, -- T + [0x1D6F5] = 0x54, -- Tau +-- [0x00055] = 0x55, -- U +-- [0x00056] = 0x56, -- V +-- [0x00057] = 0x57, -- W +-- [0x00058] = 0x58, -- X + [0x1D6F8] = 0x58, -- Chi +-- [0x00059] = 0x59, -- Y +-- [0x0005A] = 0x5A, -- Z + [0x1D6E7] = 0x5A, -- Zeta + [0x0266D] = 0x5B, -- flat + [0x0266E] = 0x5C, -- natural + [0x0266F] = 0x5D, -- sharp + [0x02323] = 0x5E, -- smile + [0x02322] = 0x5F, -- frown + [0x02113] = 0x60, -- ell +-- [0x00061] = 0x61, -- a +-- [0x00062] = 0x62, -- b +-- [0x00063] = 0x63, -- c +-- [0x00064] = 0x64, -- d +-- [0x00065] = 0x65, -- e +-- [0x00066] = 0x66, -- f +-- [0x00067] = 0x67, -- g +-- [0x00068] = 0x68, -- h + [0x0210E] = 0x68, -- plant constant +-- [0x00069] = 0x69, -- i +-- [0x0006A] = 0x6A, -- j +-- [0x0006B] = 0x6B, -- k +-- [0x0006C] = 0x6C, -- l +-- [0x0006D] = 0x6D, -- m +-- [0x0006E] = 0x6E, -- n +-- [0x0006F] = 0x6F, -- o + [0x1D70A] = 0x6F, -- omicron +-- [0x00070] = 0x70, -- p +-- [0x00071] = 0x71, -- q +-- [0x00072] = 0x72, -- r +-- [0x00073] = 0x73, -- s +-- [0x00074] = 0x74, -- t +-- [0x00075] = 0x75, -- u +-- [0x00076] = 0x76, -- v +-- [0x00077] = 0x77, -- w +-- [0x00078] = 0x78, -- x +-- [0x00079] = 0x79, -- y +-- [0x0007A] = 0x7A, -- z + [0x1D6A4] = 0x7B, -- imath (TODO: also 0131) + [0x1D6A5] = 0x7C, -- jmath (TODO: also 0237) + [0x02118] = 0x7D, -- wp + [0x020D7] = 0x7E, -- vec (TODO: not sure) +-- 0x7F, -- (no idea what that could be) +} + +fonts.enc.math["tex-ss"] = { } +fonts.enc.math["tex-tt"] = { } +fonts.enc.math["tex-bf"] = { } +fonts.enc.math["tex-bi"] = { } +fonts.enc.math["tex-fraktur"] = { } +fonts.enc.math["tex-fraktur-bold"] = { } + +function fonts.vf.math.set_letters(font_encoding, name, uppercase, lowercase) + local enc = font_encoding[name] + for i = 0,25 do + enc[uppercase+i] = i + 0x41 + enc[lowercase+i] = i + 0x61 + end +end + +function fonts.vf.math.set_digits(font_encoding, name, digits) + local enc = font_encoding[name] + for i = 0,9 do + enc[digits+i] = i + 0x30 + end +end + +fonts.enc.math["tex-sy"] = { + [0x0002D] = 0x00, -- - + [0x02212] = 0x00, -- - +-- [0x02201] = 0x00, -- complement +-- [0x02206] = 0x00, -- increment +-- [0x02204] = 0x00, -- not exists +--~ [0x000B7] = 0x01, -- cdot + [0x022C5] = 0x01, -- cdot + [0x000D7] = 0x02, -- times + [0x0002A] = 0x03, -- * + [0x02217] = 0x03, -- * + [0x000F7] = 0x04, -- div + [0x022C4] = 0x05, -- diamond + [0x000B1] = 0x06, -- pm + [0x02213] = 0x07, -- mp + [0x02295] = 0x08, -- oplus + [0x02296] = 0x09, -- ominus + [0x02297] = 0x0A, -- otimes + [0x02298] = 0x0B, -- oslash + [0x02299] = 0x0C, -- odot + [0x025EF] = 0x0D, -- bigcirc, Orb (either 25EF or 25CB) -- todo + [0x02218] = 0x0E, -- circ + [0x02219] = 0x0F, -- bullet + [0x02022] = 0x0F, -- bullet + [0x0224D] = 0x10, -- asymp + [0x02261] = 0x11, -- equiv + [0x02286] = 0x12, -- subseteq + [0x02287] = 0x13, -- supseteq + [0x02264] = 0x14, -- leq + [0x02265] = 0x15, -- geq + [0x02AAF] = 0x16, -- preceq +-- [0x0227C] = 0x16, -- preceq, AM:No see 2AAF + [0x02AB0] = 0x17, -- succeq +-- [0x0227D] = 0x17, -- succeq, AM:No see 2AB0 + [0x0223C] = 0x18, -- sim + [0x02248] = 0x19, -- approx + [0x02282] = 0x1A, -- subset + [0x02283] = 0x1B, -- supset + [0x0226A] = 0x1C, -- ll + [0x0226B] = 0x1D, -- gg + [0x0227A] = 0x1E, -- prec + [0x0227B] = 0x1F, -- succ + [0x02190] = 0x20, -- leftarrow + [0x02192] = 0x21, -- rightarrow +--~ [0xFE190] = 0x20, -- leftarrow +--~ [0xFE192] = 0x21, -- rightarrow + [0x02191] = 0x22, -- uparrow + [0x02193] = 0x23, -- downarrow + [0x02194] = 0x24, -- leftrightarrow + [0x02197] = 0x25, -- nearrow + [0x02198] = 0x26, -- searrow + [0x02243] = 0x27, -- simeq + [0x021D0] = 0x28, -- Leftarrow + [0x021D2] = 0x29, -- Rightarrow + [0x021D1] = 0x2A, -- Uparrow + [0x021D3] = 0x2B, -- Downarrow + [0x021D4] = 0x2C, -- Leftrightarrow + [0x02196] = 0x2D, -- nwarrow + [0x02199] = 0x2E, -- swarrow + [0x0221D] = 0x2F, -- propto + [0x02032] = 0x30, -- prime + [0x0221E] = 0x31, -- infty + [0x02208] = 0x32, -- in + [0x0220B] = 0x33, -- ni + [0x025B3] = 0x34, -- triangle, bigtriangleup + [0x025BD] = 0x35, -- bigtriangledown + [0x00338] = 0x36, -- not +-- 0x37, -- (beginning of arrow) + [0x02200] = 0x38, -- forall + [0x02203] = 0x39, -- exists + [0x000AC] = 0x3A, -- neg, lnot + [0x02205] = 0x3B, -- empty set + [0x0211C] = 0x3C, -- Re + [0x02111] = 0x3D, -- Im + [0x022A4] = 0x3E, -- top + [0x022A5] = 0x3F, -- bot, perp + [0x02135] = 0x40, -- aleph + [0x1D49C] = 0x41, -- script A + [0x0212C] = 0x42, -- script B + [0x1D49E] = 0x43, -- script C + [0x1D49F] = 0x44, -- script D + [0x02130] = 0x45, -- script E + [0x02131] = 0x46, -- script F + [0x1D4A2] = 0x47, -- script G + [0x0210B] = 0x48, -- script H + [0x02110] = 0x49, -- script I + [0x1D4A5] = 0x4A, -- script J + [0x1D4A6] = 0x4B, -- script K + [0x02112] = 0x4C, -- script L + [0x02133] = 0x4D, -- script M + [0x1D4A9] = 0x4E, -- script N + [0x1D4AA] = 0x4F, -- script O + [0x1D4AB] = 0x50, -- script P + [0x1D4AC] = 0x51, -- script Q + [0x0211B] = 0x52, -- script R + [0x1D4AE] = 0x53, -- script S + [0x1D4AF] = 0x54, -- script T + [0x1D4B0] = 0x55, -- script U + [0x1D4B1] = 0x56, -- script V + [0x1D4B2] = 0x57, -- script W + [0x1D4B3] = 0x58, -- script X + [0x1D4B4] = 0x59, -- script Y + [0x1D4B5] = 0x5A, -- script Z + [0x0222A] = 0x5B, -- cup + [0x02229] = 0x5C, -- cap + [0x0228E] = 0x5D, -- uplus + [0x02227] = 0x5E, -- wedge, land + [0x02228] = 0x5F, -- vee, lor + [0x022A2] = 0x60, -- vdash + [0x022A3] = 0x61, -- dashv + [0x0230A] = 0x62, -- lfloor + [0x0230B] = 0x63, -- rfloor + [0x02308] = 0x64, -- lceil + [0x02309] = 0x65, -- rceil + [0x0007B] = 0x66, -- {, lbrace + [0x0007D] = 0x67, -- }, rbrace + [0x027E8] = 0x68, -- <, langle + [0x027E9] = 0x69, -- >, rangle + [0x0007C] = 0x6A, -- |, mid, lvert, rvert + [0x02225] = 0x6B, -- parallel, Vert, lVert, rVert, arrowvert + [0x02195] = 0x6C, -- updownarrow + [0x021D5] = 0x6D, -- Updownarrow + [0x0005C] = 0x6E, -- \, backslash, setminus + [0x02216] = 0x6E, -- setminus + [0x02240] = 0x6F, -- wr + [0x0221A] = 0x70, -- sqrt. AM: Check surd?? + [0x02A3F] = 0x71, -- amalg + [0x1D6FB] = 0x72, -- nabla +-- [0x0222B] = 0x73, -- smallint (TODO: what about intop?) + [0x02294] = 0x74, -- sqcup + [0x02293] = 0x75, -- sqcap + [0x02291] = 0x76, -- sqsubseteq + [0x02292] = 0x77, -- sqsupseteq + [0x000A7] = 0x78, -- S + [0x02020] = 0x79, -- dagger, dag + [0x02021] = 0x7A, -- ddagger, ddag + [0x000B6] = 0x7B, -- P + [0x02663] = 0x7C, -- clubsuit + [0x02662] = 0x7D, -- diamondsuit + [0x02661] = 0x7E, -- heartsuit + [0x02660] = 0x7F, -- spadesuit + [0xFE321] = 0x37, -- mapstochar +} + +-- The names in masm10.enc can be trusted best and are shown in the first +-- column, while in the second column we show the tex/ams names. As usual +-- it costs hours to figure out such a table. + +fonts.enc.math["tex-ma"] = { + [0x022A1] = 0x00, -- squaredot \boxdot + [0x0229E] = 0x01, -- squareplus \boxplus + [0x022A0] = 0x02, -- squaremultiply \boxtimes + [0x025A1] = 0x03, -- square \square \Box + [0x025A0] = 0x04, -- squaresolid \blacksquare + [0x000B7] = 0x05, -- squaresmallsolid \centerdot + [0x022C4] = 0x06, -- diamond \Diamond \lozenge + [0x029EB] = 0x07, -- diamondsolid \blacklozenge + [0x021BA] = 0x08, -- clockwise \circlearrowright + [0x021BB] = 0x09, -- anticlockwise \circlearrowleft + [0x021CC] = 0x0A, -- harpoonleftright \rightleftharpoons + [0x021CB] = 0x0B, -- harpoonrightleft \leftrightharpoons + [0x0229F] = 0x0C, -- squareminus \boxminus + [0x022A9] = 0x0D, -- forces \Vdash + [0x022AA] = 0x0E, -- forcesbar \Vvdash + [0x022A8] = 0x0F, -- satisfies \vDash + [0x021A0] = 0x10, -- dblarrowheadright \twoheadrightarrow + [0x0219E] = 0x11, -- dblarrowheadleft \twoheadleftarrow + [0x021C7] = 0x12, -- dblarrowleft \leftleftarrows + [0x021C9] = 0x13, -- dblarrowright \rightrightarrows + [0x021C8] = 0x14, -- dblarrowup \upuparrows + [0x021CA] = 0x15, -- dblarrowdwn \downdownarrows + [0x021BE] = 0x16, -- harpoonupright \upharpoonright \restriction + [0x021C2] = 0x17, -- harpoondownright \downharpoonright + [0x021BF] = 0x18, -- harpoonupleft \upharpoonleft + [0x021C3] = 0x19, -- harpoondownleft \downharpoonleft + [0x021A3] = 0x1A, -- arrowtailright \rightarrowtail + [0x021A2] = 0x1B, -- arrowtailleft \leftarrowtail + [0x021C6] = 0x1C, -- arrowparrleftright \leftrightarrows +-- [0x021C5] = 0x00, -- \updownarrows (missing in lm) + [0x021C4] = 0x1D, -- arrowparrrightleft \rightleftarrows + [0x021B0] = 0x1E, -- shiftleft \Lsh + [0x021B1] = 0x1F, -- shiftright \Rsh + [0x021DD] = 0x20, -- squiggleright \leadsto \rightsquigarrow + [0x021AD] = 0x21, -- squiggleleftright \leftrightsquigarrow + [0x021AB] = 0x22, -- curlyleft \looparrowleft + [0x021AC] = 0x23, -- curlyright \looparrowright + [0x02257] = 0x24, -- circleequal \circeq + [0x0227F] = 0x25, -- followsorequal \succsim + [0x02273] = 0x26, -- greaterorsimilar \gtrsim + [0x02A86] = 0x27, -- greaterorapproxeql \gtrapprox + [0x022B8] = 0x28, -- multimap \multimap + [0x02234] = 0x29, -- therefore \therefore + [0x02235] = 0x2A, -- because \because + [0x02251] = 0x2B, -- equalsdots \Doteq \doteqdot + [0x0225C] = 0x2C, -- defines \triangleq + [0x0227E] = 0x2D, -- precedesorequal \precsim + [0x02272] = 0x2E, -- lessorsimilar \lesssim + [0x02A85] = 0x2F, -- lessorapproxeql \lessapprox + [0x02A95] = 0x30, -- equalorless \eqslantless + [0x02A96] = 0x31, -- equalorgreater \eqslantgtr + [0x022DE] = 0x32, -- equalorprecedes \curlyeqprec + [0x022DF] = 0x33, -- equalorfollows \curlyeqsucc + [0x0227C] = 0x34, -- precedesorcurly \preccurlyeq + [0x02266] = 0x35, -- lessdblequal \leqq + [0x02A7D] = 0x36, -- lessorequalslant \leqslant + [0x02276] = 0x37, -- lessorgreater \lessgtr + [0x02035] = 0x38, -- primereverse \backprime + -- [0x0] = 0x39, -- axisshort \dabar + [0x02253] = 0x3A, -- equaldotrightleft \risingdotseq + [0x02252] = 0x3B, -- equaldotleftright \fallingdotseq + [0x0227D] = 0x3C, -- followsorcurly \succcurlyeq + [0x02267] = 0x3D, -- greaterdblequal \geqq + [0x02A7E] = 0x3E, -- greaterorequalslant \geqslant + [0x02277] = 0x3F, -- greaterorless \gtrless + [0x0228F] = 0x40, -- squareimage \sqsubset + [0x02290] = 0x41, -- squareoriginal \sqsupset + -- wrong: + [0x022B3] = 0x42, -- triangleright \rhd \vartriangleright + [0x022B2] = 0x43, -- triangleleft \lhd \vartriangleleft + [0x022B5] = 0x44, -- trianglerightequal \unrhd \trianglerighteq + [0x022B4] = 0x45, -- triangleleftequal \unlhd \trianglelefteq + -- + [0x02605] = 0x46, -- star \bigstar + [0x0226C] = 0x47, -- between \between + [0x025BC] = 0x48, -- triangledownsld \blacktriangledown + [0x025B6] = 0x49, -- trianglerightsld \blacktriangleright + [0x025C0] = 0x4A, -- triangleleftsld \blacktriangleleft + -- [0x0] = 0x4B, -- arrowaxisright + -- [0x0] = 0x4C, -- arrowaxisleft + [0x025B2] = 0x4D, -- triangle \triangleup \vartriangle + [0x025B2] = 0x4E, -- trianglesolid \blacktriangle + [0x025BC] = 0x4F, -- triangleinv \triangledown + [0x02256] = 0x50, -- ringinequal \eqcirc + [0x022DA] = 0x51, -- lessequalgreater \lesseqgtr + [0x022DB] = 0x52, -- greaterlessequal \gtreqless + [0x02A8B] = 0x53, -- lessdbleqlgreater \lesseqqgtr + [0x02A8C] = 0x54, -- greaterdbleqlless \gtreqqless + [0x000A5] = 0x55, -- Yen \yen + [0x021DB] = 0x56, -- arrowtripleright \Rrightarrow + [0x021DA] = 0x57, -- arrowtripleleft \Lleftarrow + [0x02713] = 0x58, -- check \checkmark + [0x022BB] = 0x59, -- orunderscore \veebar + [0x022BC] = 0x5A, -- nand \barwedge + [0x02306] = 0x5B, -- perpcorrespond \doublebarwedge + [0x02220] = 0x5C, -- angle \angle + [0x02221] = 0x5D, -- measuredangle \measuredangle + [0x02222] = 0x5E, -- sphericalangle \sphericalangle + -- [0x0] = 0x5F, -- proportional \varpropto + -- [0x0] = 0x60, -- smile \smallsmile + -- [0x0] = 0x61, -- frown \smallfrown + [0x022D0] = 0x62, -- subsetdbl \Subset + [0x022D1] = 0x63, -- supersetdbl \Supset + [0x022D3] = 0x64, -- uniondbl \doublecup \Cup + [0x00100] = 0x65, -- intersectiondbl \doublecap \Cap + [0x022CF] = 0x66, -- uprise \curlywedge + [0x022CE] = 0x67, -- downfall \curlyvee + [0x022CB] = 0x68, -- multiopenleft \leftthreetimes + [0x022CC] = 0x69, -- multiopenright \rightthreetimes + [0x02AC5] = 0x6A, -- subsetdblequal \subseteqq + [0x02AC6] = 0x6B, -- supersetdblequal \supseteqq + [0x0224F] = 0x6C, -- difference \bumpeq + [0x0224E] = 0x6D, -- geomequivalent \Bumpeq + [0x022D8] = 0x6E, -- muchless \lll \llless + [0x022D9] = 0x6F, -- muchgreater \ggg \gggtr + [0x0231C] = 0x70, -- rightanglenw \ulcorner + [0x0231D] = 0x71, -- rightanglene \urcorner + [0x024C7] = 0x72, -- circleR \circledR + [0x024C8] = 0x73, -- circleS \circledS + [0x022D4] = 0x74, -- fork \pitchfork + [0x02245] = 0x75, -- dotplus \dotplus + [0x0223D] = 0x76, -- revsimilar \backsim + [0x022CD] = 0x77, -- revasymptequal \backsimeq -- AM: Check this! I mapped it to simeq. + [0x0231E] = 0x78, -- rightanglesw \llcorner + [0x0231F] = 0x79, -- rightanglese \lrcorner + [0x02720] = 0x7A, -- maltesecross \maltese + [0x02201] = 0x7B, -- complement \complement + [0x022BA] = 0x7C, -- intercal \intercal + [0x0229A] = 0x7D, -- circlering \circledcirc + [0x0229B] = 0x7E, -- circleasterisk \circledast + [0x0229D] = 0x7F, -- circleminus \circleddash +} + +fonts.enc.math["tex-mb"] = { + -- [0x0] = 0x00, -- lessornotequal \lvertneqq + -- [0x0] = 0x01, -- greaterornotequal \gvertneqq + [0x02270] = 0x02, -- notlessequal \nleq + [0x02271] = 0x03, -- notgreaterequal \ngeq + [0x0226E] = 0x04, -- notless \nless + [0x0226F] = 0x05, -- notgreater \ngtr + [0x02280] = 0x06, -- notprecedes \nprec + [0x02281] = 0x07, -- notfollows \nsucc + [0x02268] = 0x08, -- lessornotdbleql \lneqq + [0x02269] = 0x09, -- greaterornotdbleql \gneqq + -- [0x0] = 0x0A, -- notlessorslnteql \nleqslant + -- [0x0] = 0x0B, -- notgreaterorslnteql \ngeqslant + [0x02A87] = 0x0C, -- lessnotequal \lneq + [0x02A88] = 0x0D, -- greaternotequal \gneq + -- [0x0] = 0x0E, -- notprecedesoreql \npreceq + -- [0x0] = 0x0F, -- notfollowsoreql \nsucceq + [0x022E8] = 0x10, -- precedeornoteqvlnt \precnsim + [0x022E9] = 0x11, -- followornoteqvlnt \succnsim + [0x022E6] = 0x12, -- lessornotsimilar \lnsim + [0x022E7] = 0x13, -- greaterornotsimilar \gnsim + -- [0x0] = 0x14, -- notlessdblequal \nleqq + -- [0x0] = 0x15, -- notgreaterdblequal \ngeqq + [0x02AB5] = 0x16, -- precedenotslnteql \precneqq + [0x02AB6] = 0x17, -- follownotslnteql \succneqq + [0x02AB9] = 0x18, -- precedenotdbleqv \precnapprox + [0x02ABA] = 0x19, -- follownotdbleqv \succnapprox + [0x02A89] = 0x1A, -- lessnotdblequal \lnapprox + [0x02A8A] = 0x1B, -- greaternotdblequal \gnapprox + [0x02241] = 0x1C, -- notsimilar \nsim + [0x02247] = 0x1D, -- notapproxequal \ncong + -- [0x0] = 0x1E, -- upslope \diagup + -- [0x0] = 0x1F, -- downslope \diagdown + -- [0x0] = 0x20, -- notsubsetoreql \varsubsetneq + -- [0x0] = 0x21, -- notsupersetoreql \varsupsetneq + -- [0x0] = 0x22, -- notsubsetordbleql \nsubseteqq + -- [0x0] = 0x23, -- notsupersetordbleql \nsupseteqq + [0x02ACB] = 0x24, -- subsetornotdbleql \subsetneqq + [0x02ACC] = 0x25, -- supersetornotdbleql \supsetneqq + -- [0x0] = 0x26, -- subsetornoteql \varsubsetneqq + -- [0x0] = 0x27, -- supersetornoteql \varsupsetneqq + [0x0228A] = 0x28, -- subsetnoteql \subsetneq + [0x0228B] = 0x29, -- supersetnoteql \supsetneq + [0x02288] = 0x2A, -- notsubseteql \nsubseteq + [0x02289] = 0x2B, -- notsuperseteql \nsupseteq + [0x02226] = 0x2C, -- notparallel \nparallel + [0x02224] = 0x2D, -- notbar \nmid \ndivides + -- [0x0] = 0x2E, -- notshortbar \nshortmid + -- [0x0] = 0x2F, -- notshortparallel \nshortparallel + [0x022AC] = 0x30, -- notturnstile \nvdash + [0x022AE] = 0x31, -- notforces \nVdash + [0x022AD] = 0x32, -- notsatisfies \nvDash + [0x022AF] = 0x33, -- notforcesextra \nVDash + [0x022ED] = 0x34, -- nottriangeqlright \ntrianglerighteq + [0x022EC] = 0x35, -- nottriangeqlleft \ntrianglelefteq + [0x022EA] = 0x36, -- nottriangleleft \ntriangleleft + [0x022EB] = 0x37, -- nottriangleright \ntriangleright + [0x0219A] = 0x38, -- notarrowleft \nleftarrow + [0x0219B] = 0x39, -- notarrowright \nrightarrow + [0x021CD] = 0x3A, -- notdblarrowleft \nLeftarrow + [0x021CF] = 0x3B, -- notdblarrowright \nRightarrow + [0x021CE] = 0x3C, -- notdblarrowboth \nLeftrightarrow + [0x021AE] = 0x3D, -- notarrowboth \nleftrightarrow + [0x022C7] = 0x3E, -- dividemultiply \divideontimes + [0x02300] = 0x3F, -- diametersign \varnothing + [0x02204] = 0x40, -- notexistential \nexists + [0x1D538] = 0x41, -- A (blackboard A) + [0x1D539] = 0x42, -- B + [0x02102] = 0x43, -- C + [0x1D53B] = 0x44, -- D + [0x1D53C] = 0x45, -- E + [0x1D53D] = 0x46, -- F + [0x1D53E] = 0x47, -- G + [0x0210D] = 0x48, -- H + [0x1D540] = 0x49, -- I + [0x1D541] = 0x4A, -- J + [0x1D542] = 0x4B, -- K + [0x1D543] = 0x4C, -- L + [0x1D544] = 0x4D, -- M + [0x02115] = 0x4E, -- N + [0x1D546] = 0x4F, -- O + [0x02119] = 0x50, -- P + [0x0211A] = 0x51, -- Q + [0x0211D] = 0x52, -- R + [0x1D54A] = 0x53, -- S + [0x1D54B] = 0x54, -- T + [0x1D54C] = 0x55, -- U + [0x1D54D] = 0x56, -- V + [0x1D54E] = 0x57, -- W + [0x1D54F] = 0x58, -- X + [0x1D550] = 0x59, -- Y + [0x02124] = 0x5A, -- Z (blackboard Z) + [0x02132] = 0x60, -- hatwide \Finv + [0x02141] = 0x61, -- hatwider \Game + -- [0x0] = 0x62, tildewide + -- [0x0] = 0x63, tildewider + -- [0x0] = 0x64, Finv + -- [0x0] = 0x65, Gmir + [0x02127] = 0x66, -- Omegainv \mho + [0x000F0] = 0x67, -- eth \eth + [0x02242] = 0x68, -- equalorsimilar \eqsim + [0x02136] = 0x69, -- beth \beth + [0x02137] = 0x6A, -- gimel \gimel + [0x02138] = 0x6B, -- daleth \daleth + [0x022D6] = 0x6C, -- lessdot \lessdot + [0x022D7] = 0x6D, -- greaterdot \gtrdot + [0x022C9] = 0x6E, -- multicloseleft \ltimes + [0x022CA] = 0x6F, -- multicloseright \rtimes + -- [0x0] = 0x70, -- barshort \shortmid + -- [0x0] = 0x71, -- parallelshort \shortparallel + -- [0x02216] = 0x72, -- integerdivide \smallsetminus (2216 already part of tex-sy + -- [0x0] = 0x73, -- similar \thicksim + -- [0x0] = 0x74, -- approxequal \thickapprox + [0x0224A] = 0x75, -- approxorequal \approxeq + [0x02AB8] = 0x76, -- followsorequal \succapprox + [0x02AB7] = 0x77, -- precedesorequal \precapprox + [0x021B6] = 0x78, -- archleftdown \curvearrowleft + [0x021B7] = 0x79, -- archrightdown \curvearrowright + [0x003DC] = 0x7A, -- Digamma \digamma + [0x003F0] = 0x7B, -- kappa \varkappa + [0x1D55C] = 0x7C, -- k \Bbbk (blackboard k) + [0x0210F] = 0x7D, -- planckover2pi \hslash + [0x00127] = 0x7E, -- planckover2pi1 \hbar + [0x003F6] = 0x7F, -- epsiloninv \backepsilon +} + +fonts.enc.math["tex-fraktur"] = { +-- [0x1D504] = 0x41, -- A (fraktur A) +-- [0x1D505] = 0x42, -- B + [0x0212D] = 0x43, -- C +-- [0x1D507] = 0x44, -- D +-- [0x1D508] = 0x45, -- E +-- [0x1D509] = 0x46, -- F +-- [0x1D50A] = 0x47, -- G + [0x0210C] = 0x48, -- H + [0x02111] = 0x49, -- I +-- [0x1D50D] = 0x4A, -- J +-- [0x1D50E] = 0x4B, -- K +-- [0x1D50F] = 0x4C, -- L +-- [0x1D510] = 0x4D, -- M +-- [0x1D511] = 0x4E, -- N +-- [0x1D512] = 0x4F, -- O +-- [0x1D513] = 0x50, -- P +-- [0x1D514] = 0x51, -- Q + [0x0211C] = 0x52, -- R +-- [0x1D516] = 0x53, -- S +-- [0x1D517] = 0x54, -- T +-- [0x1D518] = 0x55, -- U +-- [0x1D519] = 0x56, -- V +-- [0x1D51A] = 0x57, -- W +-- [0x1D51B] = 0x58, -- X +-- [0x1D51C] = 0x59, -- Y + [0x02128] = 0x5A, -- Z (fraktur Z) +-- [0x1D51E] = 0x61, -- a (fraktur a) +-- [0x1D51F] = 0x62, -- b +-- [0x1D520] = 0x63, -- c +-- [0x1D521] = 0x64, -- d +-- [0x1D522] = 0x65, -- e +-- [0x1D523] = 0x66, -- f +-- [0x1D524] = 0x67, -- g +-- [0x1D525] = 0x68, -- h +-- [0x1D526] = 0x69, -- i +-- [0x1D527] = 0x6A, -- j +-- [0x1D528] = 0x6B, -- k +-- [0x1D529] = 0x6C, -- l +-- [0x1D52A] = 0x6D, -- m +-- [0x1D52B] = 0x6E, -- n +-- [0x1D52C] = 0x6F, -- o +-- [0x1D52D] = 0x70, -- p +-- [0x1D52E] = 0x71, -- q +-- [0x1D52F] = 0x72, -- r +-- [0x1D530] = 0x73, -- s +-- [0x1D531] = 0x74, -- t +-- [0x1D532] = 0x75, -- u +-- [0x1D533] = 0x76, -- v +-- [0x1D534] = 0x77, -- w +-- [0x1D535] = 0x78, -- x +-- [0x1D536] = 0x79, -- y +-- [0x1D537] = 0x7A, -- z +} + +-- now that all other vectors are defined ... + +fonts.vf.math.set_letters(fonts.enc.math, "tex-mi", 0x1D434, 0x1D44E) +fonts.vf.math.set_letters(fonts.enc.math, "tex-ss", 0x1D5A0, 0x1D5BA) +fonts.vf.math.set_letters(fonts.enc.math, "tex-tt", 0x1D670, 0x1D68A) +fonts.vf.math.set_letters(fonts.enc.math, "tex-bf", 0x1D400, 0x1D41A) +fonts.vf.math.set_letters(fonts.enc.math, "tex-bi", 0x1D468, 0x1D482) +fonts.vf.math.set_letters(fonts.enc.math, "tex-fraktur", 0x1D504, 0x1D51E) +fonts.vf.math.set_letters(fonts.enc.math, "tex-fraktur-bold", 0x1D56C, 0x1D586) + +fonts.vf.math.set_digits (fonts.enc.math, "tex-ss", 0x1D7E2) +fonts.vf.math.set_digits (fonts.enc.math, "tex-tt", 0x1D7F6) +fonts.vf.math.set_digits (fonts.enc.math, "tex-bf", 0x1D7CE) + +-- fonts.vf.math.set_digits (fonts.enc.math, "tex-bi", 0x1D7CE) + +-- todo: add ss, tt, bf etc vectors +-- we can make ss tt etc an option + +-- rm-lmr5 : LMMathRoman5-Regular +-- rm-lmbx5 : LMMathRoman5-Bold ] +-- lmbsy5 : LMMathSymbols5-BoldItalic +-- lmsy5 : LMMathSymbols5-Italic +-- lmmi5 : LMMathItalic5-Italic +-- lmmib5 : LMMathItalic5-BoldItalic + +mathematics.make_font ( "lmroman5-math", { + { name = "lmroman5-regular.otf", features = "virtualmath", main = true }, + -- { name = "rm-lmr5.tfm", vector = "tex-mr" } , + { name = "lmmi5.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "lmsy5.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "lmex10.tfm", vector = "tex-ex", extension = true } , + { name = "msam5.tfm", vector = "tex-ma" }, + { name = "msbm5.tfm", vector = "tex-mb" }, + -- { name = "rm-lmbx5.tfm", vector = "tex-bf" } , + { name = "lmroman5-bold", "tex-bf" } , + { name = "lmmib5.tfm", vector = "tex-bi", skewchar=0x7F } , + { name = "lmsans8-regular.otf", vector = "tex-ss", optional=true }, + { name = "lmmono8-regular.otf", vector = "tex-tt", optional=true }, + { name = "eufm5.tfm", vector = "tex-fraktur", optional=true }, +} ) + +-- rm-lmr6 : LMMathRoman6-Regular +-- rm-lmbx6 : LMMathRoman6-Bold +-- lmsy6 : LMMathSymbols6-Italic +-- lmmi6 : LMMathItalic6-Italic + +mathematics.make_font ( "lmroman6-math", { + { name = "lmroman6-regular.otf", features = "virtualmath", main = true }, + -- { name = "rm-lmr6.tfm", vector = "tex-mr" } , + { name = "lmmi6.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "lmsy6.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "lmex10.tfm", vector = "tex-ex", extension = true } , + { name = "msam5.tfm", vector = "tex-ma" }, + { name = "msbm5.tfm", vector = "tex-mb" }, + -- { name = "rm-lmbx6.tfm", vector = "tex-bf" } , + { name = "lmroman6-bold.otf", "tex-bf" } , + { name = "lmmib5.tfm", vector = "tex-bi", skewchar=0x7F } , + { name = "lmsans8-regular.otf", vector = "tex-ss", optional=true }, + { name = "lmmono8-regular.otf", vector = "tex-tt", optional=true }, + { name = "eufm6.tfm", vector = "tex-fraktur", optional=true }, + { name = "eufb6.tfm", vector = "tex-fraktur-bold", optional=true }, +} ) + +-- rm-lmr7 : LMMathRoman7-Regular +-- rm-lmbx7 : LMMathRoman7-Bold +-- lmbsy7 : LMMathSymbols7-BoldItalic +-- lmsy7 : LMMathSymbols7-Italic +-- lmmi7 : LMMathItalic7-Italic +-- lmmib7 : LMMathItalic7-BoldItalic + +mathematics.make_font ( "lmroman7-math", { + { name = "lmroman7-regular.otf", features = "virtualmath", main = true }, + -- { name = "rm-lmr7.tfm", vector = "tex-mr" } , + { name = "lmmi7.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "lmsy7.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "lmex10.tfm", vector = "tex-ex", extension = true } , + { name = "msam7.tfm", vector = "tex-ma" }, + { name = "msbm7.tfm", vector = "tex-mb" }, + -- { name = "rm-lmbx7.tfm", vector = "tex-bf" } , + { name = "lmroman7-bold.otf", "tex-bf" } , + { name = "lmmib7.tfm", vector = "tex-bi", skewchar=0x7F } , + { name = "lmsans8-regular.otf", vector = "tex-ss", optional=true }, + { name = "lmmono8-regular.otf", vector = "tex-tt", optional=true }, + { name = "eufm7.tfm", vector = "tex-fraktur", optional=true }, + { name = "eufb7.tfm", vector = "tex-fraktur-bold", optional=true }, +} ) + +-- rm-lmr8 : LMMathRoman8-Regular +-- rm-lmbx8 : LMMathRoman8-Bold +-- lmsy8 : LMMathSymbols8-Italic +-- lmmi8 : LMMathItalic8-Italic + +mathematics.make_font ( "lmroman8-math", { + { name = "lmroman8-regular.otf", features = "virtualmath", main = true }, + -- { name = "rm-lmr8.tfm", vector = "tex-mr" } , + { name = "lmmi8.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "lmsy8.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "lmex10.tfm", vector = "tex-ex", extension = true } , + { name = "msam7.tfm", vector = "tex-ma" }, + { name = "msbm7.tfm", vector = "tex-mb" }, + -- { name = "rm-lmbx8.tfm", vector = "tex-bf" } , + { name = "lmroman8-bold.otf", "tex-bf" } , + { name = "lmmib7.tfm", vector = "tex-bi", skewchar=0x7F } , + { name = "lmsans8-regular.otf", vector = "tex-ss", optional=true }, + { name = "lmmono8-regular.otf", vector = "tex-tt", optional=true }, + { name = "eufm8.tfm", vector = "tex-fraktur", optional=true }, + { name = "eufb8.tfm", vector = "tex-fraktur-bold", optional=true }, +} ) + +-- rm-lmr9 : LMMathRoman9-Regular +-- rm-lmbx9 : LMMathRoman9-Bold +-- lmsy9 : LMMathSymbols9-Italic +-- lmmi9 : LMMathItalic9-Italic + +mathematics.make_font ( "lmroman9-math", { + { name = "lmroman9-regular.otf", features = "virtualmath", main = true }, + -- { name = "rm-lmr9.tfm", vector = "tex-mr" } , + { name = "lmmi9.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "lmsy9.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "lmex10.tfm", vector = "tex-ex", extension = true } , + { name = "msam10.tfm", vector = "tex-ma" }, + { name = "msbm10.tfm", vector = "tex-mb" }, + -- { name = "rm-lmbx9.tfm", vector = "tex-bf" } , + { name = "lmroman9-bold.otf", "tex-bf" } , + { name = "lmmib10.tfm", vector = "tex-bi", skewchar=0x7F } , + { name = "lmsans9-regular.otf", vector = "tex-ss", optional=true }, + { name = "lmmono9-regular.otf", vector = "tex-tt", optional=true }, + { name = "eufm9.tfm", vector = "tex-fraktur", optional=true }, + { name = "eufb9.tfm", vector = "tex-fraktur-bold", optional=true }, +} ) + +-- rm-lmr10 : LMMathRoman10-Regular +-- rm-lmbx10 : LMMathRoman10-Bold +-- lmbsy10 : LMMathSymbols10-BoldItalic +-- lmsy10 : LMMathSymbols10-Italic +-- lmex10 : LMMathExtension10-Regular +-- lmmi10 : LMMathItalic10-Italic +-- lmmib10 : LMMathItalic10-BoldItalic + +mathematics.make_font ( "lmroman10-math", { + { name = "lmroman10-regular.otf", features = "virtualmath", main = true }, + -- { name = "rm-lmr10.tfm", vector = "tex-mr" } , + { name = "lmmi10.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "lmsy10.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "lmex10.tfm", vector = "tex-ex", extension = true } , + { name = "msam10.tfm", vector = "tex-ma" }, + { name = "msbm10.tfm", vector = "tex-mb" }, + -- { name = "rm-lmbx10.tfm", vector = "tex-bf" } , + { name = "lmroman10-bold.otf", "tex-bf" } , + { name = "lmmib10.tfm", vector = "tex-bi", skewchar=0x7F } , + { name = "lmsans10-regular.otf", vector = "tex-ss", optional=true }, + { name = "lmmono10-regular.otf", vector = "tex-tt", optional=true }, + { name = "eufm10.tfm", vector = "tex-fraktur", optional=true }, + { name = "eufb10.tfm", vector = "tex-fraktur-bold", optional=true }, +} ) + +-- rm-lmr12 : LMMathRoman12-Regular +-- rm-lmbx12 : LMMathRoman12-Bold +-- lmmi12 : LMMathItalic12-Italic + +mathematics.make_font ( "lmroman12-math", { + { name = "lmroman12-regular.otf", features = "virtualmath", main = true }, + -- { name = "rm-lmr12.tfm", vector = "tex-mr" } , + { name = "lmmi12.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "lmsy10.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "lmex10.tfm", vector = "tex-ex", extension = true } , + { name = "msam10.tfm", vector = "tex-ma" }, + { name = "msbm10.tfm", vector = "tex-mb" }, + -- { name = "rm-lmbx12.tfm", vector = "tex-bf" } , + { name = "lmroman12-bold.otf", "tex-bf" } , + { name = "lmmib10.tfm", vector = "tex-bi", skewchar=0x7F } , + { name = "lmsans12-regular.otf", vector = "tex-ss", optional=true }, + { name = "lmmono12-regular.otf", vector = "tex-tt", optional=true }, + { name = "eufm10.tfm", vector = "tex-fraktur", optional=true }, + { name = "eufb10.tfm", vector = "tex-fraktur-bold", optional=true }, +} ) + +-- rm-lmr17 : LMMathRoman17-Regular + +mathematics.make_font ( "lmroman17-math", { + { name = "lmroman17-regular.otf", features = "virtualmath", main = true }, + -- { name = "rm-lmr12.tfm", vector = "tex-mr" } , + { name = "lmmi12.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "lmsy10.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "lmex10.tfm", vector = "tex-ex", extension = true } , + { name = "msam10.tfm", vector = "tex-ma" }, + { name = "msbm10.tfm", vector = "tex-mb" }, + -- { name = "rm-lmbx12.tfm", vector = "tex-bf" } , + { name = "lmroman12-bold.otf", "tex-bf" } , + { name = "lmmib10.tfm", vector = "tex-bi", skewchar=0x7F } , + { name = "lmsans17-regular.otf", vector = "tex-ss", optional=true }, + { name = "lmmono17-regular.otf", vector = "tex-tt", optional=true }, + { name = "eufm10.tfm", vector = "tex-fraktur", optional=true }, + { name = "eufb10.tfm", vector = "tex-fraktur-bold", optional=true }, +} ) + +-- pxr/txr messes up the accents + +mathematics.make_font ( "px-math", { + { name = "texgyrepagella-regular.otf", features = "virtualmath", main = true }, + { name = "pxr.tfm", vector = "tex-mr" } , + { name = "pxmi.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "pxsy.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "pxex.tfm", vector = "tex-ex", extension = true } , + { name = "pxsya.tfm", vector = "tex-ma" }, + { name = "pxsyb.tfm", vector = "tex-mb" }, +} ) + +mathematics.make_font ( "tx-math", { + { name = "texgyretermes-regular.otf", features = "virtualmath", main = true }, + { name = "txr.tfm", vector = "tex-mr" } , + { name = "txmi.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "txsy.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "txex.tfm", vector = "tex-ex", extension = true } , + { name = "txsya.tfm", vector = "tex-ma" }, + { name = "txsyb.tfm", vector = "tex-mb" }, +} ) + +mathematics.make_font ( "iwona-math", { + { name = "file:Iwona-Regular", features = "virtualmath", main = true }, + { name = "mi-iwonari.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "sy-iwonarz.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "ex-iwonar.tfm", vector = "tex-ex", extension = true } , + { name = "msam10.tfm", vector = "tex-ma" }, + { name = "msbm10.tfm", vector = "tex-mb" }, +} ) + +mathematics.make_font ( "iwona-light-math", { + { name = "file:IwonaLight-Regular", features = "virtualmath", main = true }, + { name = "mi-iwonali.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "sy-iwonalz.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "ex-iwonal.tfm", vector = "tex-ex", extension = true } , + { name = "msam10.tfm", vector = "tex-ma" }, + { name = "msbm10.tfm", vector = "tex-mb" }, +} ) + +mathematics.make_font ( "iwona-medium-math", { + { name = "file:IwonaMedium-Regular", features = "virtualmath", main = true }, + { name = "mi-iwonami.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "sy-iwonamz.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "ex-iwonam.tfm", vector = "tex-ex", extension = true } , + { name = "msam10.tfm", vector = "tex-ma" }, + { name = "msbm10.tfm", vector = "tex-mb" }, +} ) + +mathematics.make_font ( "iwona-heavy-math", { + { name = "file:IwonaHeavy-Regular", features = "virtualmath", main = true }, + { name = "mi-iwonahi.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "sy-iwonahz.tfm", vector = "tex-sy", skewchar=0x30, parameters = true } , + { name = "ex-iwonah.tfm", vector = "tex-ex", extension = true } , + { name = "msam10.tfm", vector = "tex-ma" }, + { name = "msbm10.tfm", vector = "tex-mb" }, +} ) + +-- not ok, we need adapted vectors ! + +mathematics.make_font ( "mathtimes-math", { + { name = "file:texgyretermes-regular.otf", features = "virtualmath", main = true }, + { name = "mtmiz.tfm", vector = "tex-mi", skewchar=0x7F }, + { name = "mtsyn.tfm", vector = "tex-sy", skewchar=0x30, parameters = true }, + { name = "mtex.tfm", vector = "tex-ex", extension = true }, + { name = "msam10.tfm", vector = "tex-ma" }, + { name = "msbm10.tfm", vector = "tex-mb" }, +} ) diff --git a/tex/context/base/meta-ini.mkii b/tex/context/base/meta-ini.mkii index cb59ed44b..ee7e8a38b 100644 --- a/tex/context/base/meta-ini.mkii +++ b/tex/context/base/meta-ini.mkii @@ -20,45 +20,21 @@ \unprotect -\startmessages dutch library: metapost - title: metapost - 1: metapost bibliotheek -- wordt geladen -\stopmessages - -\startmessages english library: metapost - title: metapost - 1: loading metapost library -- -\stopmessages - -\startmessages german library: metapost - title: metapost - 1: Lade metapost Bibliothek -- -\stopmessages - -\startmessages czech library: metapost - title: metapost - 1: loading metapost library -- -\stopmessages - -\startmessages italian library: metapost - title: metapost - 1: caricamento della libreria metapost -- -\stopmessages - -\startmessages norwegian library: metapost - title: metapost - 1: metapost bibliotek -- blir lest inn -\stopmessages - -\startmessages romanian library: metapost - title: metapost - 1: se incarca biblioteca metapost -- -\stopmessages - -\startmessages french library: metapost - title: metapost - 1: chargement de la bibliothèque metapost -- -\stopmessages +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved + +% messages moved %D This module extends the functionality of the support module %D \type {supp-mps}, the module that is responsible for @@ -69,49 +45,48 @@ \maxnofMPgraphics = 4000 % metafun disables the 4K boundary -\appendtoks \runMPgraphicsfalse \to \everyfastmode -\appendtoks \insertMPgraphicsfalse \to \everyfastmode -\appendtoks \flushMPgraphics \to \everygoodbye % \everylastshipout +\appendtoks \flushMPgraphics \to \everygoodbye % \everylastshipout \def\@@MPG{@MPG@} \startMPextensions - if unknown context_tool: input mp-tool; fi; - if unknown context_spec: input mp-spec; fi; - if unknown context_grph: input mp-grph; fi; + if unknown context_tool: input mp-tool; fi; + if unknown context_spec: input mp-spec; fi; + if unknown context_grph: input mp-grph; fi; \stopMPextensions %D Since we want lables to follow the document settings, we %D also set the font related variables. -\startMPinitializations % scale is not yet ok - defaultfont:="\truefontname{Regular}"; - defaultscale:=\the\bodyfontsize/10pt; -\stopMPinitializations - -\beginNEWTEX +\ifnum\texengine=\xetexengine \startMPinitializations % scale is not yet ok - defaultfont:="rm-lmtt10"; + defaultfont:="rm-lmtt10"; + defaultscale:=\the\bodyfontsize/10pt; \stopMPinitializations -\endNEWTEX +\else + \startMPinitializations % scale is not yet ok + defaultfont:="\truefontname{Regular}"; + defaultscale:=\the\bodyfontsize/10pt; + \stopMPinitializations +\fi %D In order to support fancy text features (like outline %D fonts), we set: \startMPextensions - graphictextformat:="context"; - graphictextdirective "\the\everyMPTEXgraphic"; + graphictextformat:="context"; + graphictextdirective "\the\everyMPTEXgraphic"; \stopMPextensions % \startMPextensions -% textextdirective "\the\everyMPTEXgraphic"; +% textextdirective "\the\everyMPTEXgraphic"; % \stopMPextensions %D A signal that we're in combines \CONTEXT||\METAFUN mode: \startMPextensions - string contextversion; - contextversion:="\contextversion"; + string contextversion; + contextversion:="\contextversion"; \stopMPextensions %D Some safeguards: diff --git a/tex/context/base/meta-ini.mkiv b/tex/context/base/meta-ini.mkiv index 8d2f7a724..bcd82c4ed 100644 --- a/tex/context/base/meta-ini.mkiv +++ b/tex/context/base/meta-ini.mkiv @@ -15,46 +15,6 @@ \unprotect -\startmessages dutch library: metapost - title: metapost - 1: metapost bibliotheek -- wordt geladen -\stopmessages - -\startmessages english library: metapost - title: metapost - 1: loading metapost library -- -\stopmessages - -\startmessages german library: metapost - title: metapost - 1: Lade metapost Bibliothek -- -\stopmessages - -\startmessages czech library: metapost - title: metapost - 1: loading metapost library -- -\stopmessages - -\startmessages italian library: metapost - title: metapost - 1: caricamento della libreria metapost -- -\stopmessages - -\startmessages norwegian library: metapost - title: metapost - 1: metapost bibliotek -- blir lest inn -\stopmessages - -\startmessages romanian library: metapost - title: metapost - 1: se incarca biblioteca metapost -- -\stopmessages - -\startmessages french library: metapost - title: metapost - 1: chargement de la bibliothèque metapost -- -\stopmessages - %D Instead of sharing code with \MKII, I decided to copy %D the code. Otherwise maintainance becomes a pain and after all, %D the \MKII\ code will not change. @@ -77,6 +37,20 @@ \newtoks \everyMPgraphic % mp \newtoks \everyMPTEXgraphic % tex +% The next command is, of course, dedicated to Mojca, who +% needs it for gnuplot. Anyway, the whole multiple engine +% mechanism is to keep her gnuplot from interfering. + +\def\startMPdefinitions + {\dosinglegroupempty\dostartMPdefinitions} + +\long\def\dostartMPdefinitions#1#2\stopMPdefinitions + {\edef\currentMPgraphicinstance{#1}% + \ifx\currentMPgraphicinstance\empty + \let\currentMPgraphicinstance\defaultMPgraphicinstance + \fi + \global\MPinstancetoks\expandafter{\the\MPinstancetoks#2}} + \long\def\startMPextensions#1\stopMPextensions {\global\MPextensions\expandafter{\the\MPextensions#1}} @@ -110,31 +84,163 @@ \def\currentMPformat{metafun} +% todo: +% +% \splitMPgraphicname[a::b] (\currentMPgraphicformat,\currentMPgraphicname) +% \splitMPgraphicname[a] (\currentMPgraphicformat,\currentMPgraphicname) +% \splitMPgraphicname[a::b::c] (\currentMPgraphicformat,\currentMPgraphicname) +% +% \resetMPformat[extrafun] +% +% MPinclusions etc only for metafun, randomseed for all +% +% todo: \resetMPformat[instance] -> unload and nil +% todo: geen page stats +% todo: textext in plain mp + +% test: +% +% \let\processMPgraphic\extendedprocessMPgraphic \setupcolors[state=start] +% +% \startMPdefinitions{metafun} +% color MyColor ; MyColor = red ; +% \stopMPdefinitions +% \startuseMPgraphic{test1} +% fill fullcircle scaled 1cm withcolor MyColor ; +% \stopuseMPgraphic +% \startuseMPgraphic{test2} +% color MyColor ; MyColor = green ; +% fill fullcircle scaled 1cm withcolor MyColor ; +% \stopuseMPgraphic +% \startuseMPgraphic{test3} +% fill fullcircle scaled 1cm withcolor MyColor ; +% \stopuseMPgraphic +% \startuseMPgraphic{test4} +% color MyColor ; MyColor = blue ; +% \stopuseMPgraphic +% +% \useMPgraphic{metafun::test1} +% \useMPgraphic{metafun::test2} +% \useMPgraphic{metafun::test3} +% \useMPgraphic{extrafun::test4} +% \useMPgraphic{extrafun::test3} +% \useMPgraphic{metafun::test3} +% \useMPgraphic{nofun::test4} +% \useMPgraphic{nofun::test3} +% +% \startMPcode +% fill fullsquare scaled 1cm ; +% \stopMPcode +% \startMPcode{metafun} +% fill fullsquare scaled 1cm withcolor MyColor ; +% \stopMPcode + +\def\@@MPF{@MPF@} + +\def\MPinstancetoks{\csname\@@MPF::\currentMPgraphicinstance\endcsname} + +\def\defineMPinstance + {\dodoubleargument\dodefineMPinstance} + +\def\dodefineMPinstance[#1][#2]% + {\ifcsname\@@MPF::#1\endcsname\else\expandafter\newtoks\csname\@@MPF::#1\endcsname\fi + \MPinstancetoks\emptytoks % in case we redefine + \getparameters[\@@MPF#1][\s!format=mpost,\s!extensions=\v!no,\s!initializations=\v!no,#2]} + +\def\resetMPinstance[#1]% + {\writestatus\m!metapost{reset will be implemented when needed}} + +\def\defaultMPgraphicinstance{metafun} + +\def\splitMPgraphicname[#1]% + {\dosplitMPgraphicname[#1::::]} + +\def\dosplitMPgraphicname[#1::#2::#3]% instance :: + {\edef\currentMPgraphicname{#2}% + \ifx\currentMPgraphicname\empty + \edef\currentMPgraphicname{#1}% + \let\currentMPgraphicinstance\defaultMPgraphicinstance + \else + \edef\currentMPgraphicinstance{#1}% + \fi + \edef\currentMPgraphicformat{\csname\@@MPF\currentMPgraphicinstance\s!format\endcsname}} + +\def\currentMPgraphicinstance{\defaultMPgraphicinstance} +\def\currentMPgraphicformat {\currentMPgraphicinstance} + +\defineMPinstance[metafun] [\s!format=metafun,\s!extensions=\v!yes,\s!initializations=\v!yes] +\defineMPinstance[extrafun][\s!format=metafun,\s!extensions=\v!yes,\s!initializations=\v!yes] +\defineMPinstance[metapost][\s!format=mpost] +\defineMPinstance[nofun] [\s!format=mpost] + +\def\beginMPgraphicgroup#1% + {\begingroup + \splitMPgraphicname[#1]} + +\def\endMPgraphicgroup + {\endgroup} + +% + \newconditional \METAFUNinitialized +% maybe we need to force black, i.e. fake nodes + \long\def\processMPgraphic#1% todo: extensions and inclusions outside beginfig - {\blabelgroup + {\begingroup \enableincludeMPgraphics \the\everyMPgraphic \presetMPdefinitions \setMPrandomseed % this has to change % we need to preexpand the token lists \setbox\MPgraphicbox\hbox\bgroup - \ifconditional\METAFUNinitialized - \ctxlua { metapost.graphic( - "\currentMPformat", \@EA\!!bs\the\MPinitializations;#1;\!!es, - "" - ) }% - \else - \ctxlua { metapost.graphic( - "\currentMPformat", \@EA\!!bs\the\MPinitializations;\theMPrandomseed;#1;\!!es, % code - \@EA\@EA\@EA\!!bs\@EA\the\@EA\MPextensions\@EA;\the\MPuserinclusions;\!!es % optional preamble - ) }% - \global\settrue\METAFUNinitialized - \fi + \ctxlua{metapost.graphic("\currentMPformat", "\currentMPformat", + \@EA\!!bs\the\MPinitializations;\theMPrandomseed;#1;\!!es, % code + \@EA\@EA\@EA\!!bs\@EA\the\@EA\MPextensions\@EA;\the\MPuserinclusions;\!!es % optional preamble + )}% + \global\settrue\METAFUNinitialized + \global\MPextensions\emptytoks + \global\MPuserinclusions\emptytoks \egroup \placeMPgraphic - \elabelgroup} + \endgroup} + +% ! ! ! ! begin temporary ! ! ! ! + +\let\normalprocessMPgraphic\processMPgraphic + +\long\def\processMPgraphic#1% todo: extensions and inclusions outside beginfig + {\begingroup % needed? + \enableincludeMPgraphics + \the\everyMPgraphic + \presetMPdefinitions + \setMPrandomseed % this has to change + % we need to preexpand the token lists + \doifelsevalue{\@@MPF\currentMPgraphicinstance\s!extensions}\v!yes + {\settrue\includeMPextensions\letgvalue{\@@MPF\currentMPgraphicinstance\s!extensions}\v!no} + {\setfalse\includeMPextensions}% + \doifelsevalue{\@@MPF\currentMPgraphicinstance\s!initializations}\v!yes + {\settrue\includeMPinitializations\letgvalue{\@@MPF\currentMPgraphicinstance\s!initializations}\v!no} + {\setfalse\includeMPinitializations}% + \setbox\MPgraphicbox\hbox\bgroup + \normalexpanded{\noexpand\ctxlua{metapost.graphic("\currentMPgraphicinstance", "\currentMPgraphicformat", + \!!bs\ifconditional\includeMPinitializations\the\MPinitializations;\fi\theMPrandomseed;#1;\!!es, + \!!bs\ifconditional\includeMPextensions\the\MPextensions;\the\MPuserinclusions;\fi\the\MPinstancetoks;\!!es + )}}% + \egroup + \global\MPinstancetoks\emptytoks + \global\settrue\METAFUNinitialized % becomes obsolete + %\global\MPextensions\emptytoks % multipls instances + %\global\MPuserinclusions\emptytoks % multipls instances + \placeMPgraphic + \endgroup} + +\let\extendedprocessMPgraphic\processMPgraphic + +\let\processMPgraphic\normalprocessMPgraphic +% \let\processMPgraphic\extendedprocessMPgraphic + +% ! ! ! ! end temporary ! ! ! ! \newif\ifsetMPrandomseed \setMPrandomseedtrue % false by default @@ -150,10 +256,7 @@ \def\@@MPG{@MPG@} \def\doifMPgraphicelse#1% - {\blabelgroup - \doifdefinedelse{\@@MPG#1}% - {\elabelgroup\firstoftwoarguments} - {\elabelgroup\secondoftwoarguments}} + {\ifcsname\@@MPG#1\endcsname\expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments\fi} \def\includeMPgraphic#1% {\executeifdefined{\@@MPG#1};} % ; if not found @@ -165,7 +268,6 @@ \let\MPdrawingdata\empty \newif\ifMPdrawingdone \MPdrawingdonefalse -\newif\ifMPshiftdrawing \MPshiftdrawingfalse \def\resetMPdrawing {\globallet\MPdrawingdata\empty @@ -178,9 +280,25 @@ \def\popMPdrawing {\globalpopmacro\MPdrawingdata} -\def\getMPdrawing +\def\getMPdrawing{\dosinglegroupempty\dogetMPdrawing} + +\def\nodogetMPdrawing#1% + {\ifMPdrawingdone + \expandafter\processMPgraphic\expandafter{\MPdrawingdata}% + \fi} + +\def\dostartMPcode + {\iffirstargument + \expandafter\dodogetMPdrawing + \else + \expandafter\nodogetMPdrawing + \fi} + +\def\dodogetMPdrawing#1% {\ifMPdrawingdone + \beginMPgraphicgroup{#1::\s!dummy}% name does not matter \expandafter\processMPgraphic\expandafter{\MPdrawingdata}% + \endMPgraphicgroup \fi} \def\startMPdrawing @@ -199,7 +317,6 @@ \let\MPdrawingdata\empty -\newif\ifMPdrawingdone \MPdrawingdonefalse \newif\ifMPshiftdrawing \MPshiftdrawingfalse \def\resetMPdrawing @@ -233,24 +350,24 @@ \let\stopMPdrawing\relax \long\def\startMPclip#1#2\stopMPclip - {\blabelgroup - \long\setgvalue{MPC:#1}{\ctxlua{metapost.getclippath(\!!bs#2\!!es)}}% - \elabelgroup} + {\long\setgvalue{MPC:#1}{\ctxlua{metapost.getclippath(\!!bs#2\!!es)}}} \let\stopMPclip\relax \def\grabMPclippath#1#2#3#4#5% #5 is alternative - {\blabelgroup + {\begingroup \edef\width {#3\space}\let\overlaywidth \width \edef\height{#4\space}\let\overlayheight\height - \doifdefinedelse{MPC:#1} - {\xdef\MPclippath{\getvalue{MPC:#1}}% - \ifx\MPclippath\empty\xdef\MPclippath{#5}\fi - \setxvalue{MPC:#1}{\MPclippath}} - {\xdef\MPclippath{#5}}% + \ifcsname MPC:#1\endcsname + \xdef\MPclippath{\getvalue{MPC:#1}}% + \ifx\MPclippath\empty\xdef\MPclippath{#5}\fi + \setxvalue{MPC:#1}{\MPclippath}% + \else + \xdef\MPclippath{#5}% + \fi % #2 : method is obsolete, only pdf now, we can always % gsub the result to ps - \elabelgroup} + \endgroup} %D Next we will use these support macros. @@ -268,6 +385,8 @@ defaultscale:=\the\bodyfontsize/10pt; \stopMPinitializations +% watch out, this is a type1 font because mp can only handle 8 bit fonts + \startMPinitializations % scale is not yet ok defaultfont:="rm-lmtt10"; \stopMPinitializations @@ -386,18 +505,36 @@ %D \stoptyping \newcount\MPobjectcounter -\newif \ifMPshiftdrawing \MPshiftdrawingfalse \newbox \MPgraphicbox +%newif \ifMPshiftdrawing \MPshiftdrawingfalse + +\chardef\MPboxmode\zerocount + +\def\doobeyMPboxdepth % mode = 1 + {\setbox\MPgraphicbox\hbox{\hskip\MPllx\onebasepoint\raise\MPlly\onebasepoint\box\MPgraphicbox}} + +\def\doignoreMPboxdepth % mode = 2 + {\normalexpanded + {\noexpand\doobeyMPboxdepth + \wd\MPgraphicbox\the\wd\MPgraphicbox + \ht\MPgraphicbox\the\ht\MPgraphicbox + \dp\MPgraphicbox\the\dp\MPgraphicbox}} + +\def\obeyMPboxdepth {\chardef\MPboxmode\plusone} +\def\ignoreMPboxdepth{\chardef\MPboxmode\plustwo} +\def\normalMPboxdepth{\chardef\MPboxmode\zerocount} + +% compatibility hack: + +\let\MPshiftdrawingtrue \ignoreMPboxdepth +\let\MPshiftdrawingfalse\normalMPboxdepth \def\placeMPgraphic - {\ifMPshiftdrawing - \edef\next - {\wd\MPgraphicbox\the\wd\MPgraphicbox - \ht\MPgraphicbox\the\ht\MPgraphicbox - \dp\MPgraphicbox\the\dp\MPgraphicbox}% - \setbox\MPgraphicbox\hbox - {\hskip\MPllx\onebasepoint\raise\MPlly\onebasepoint\box\MPgraphicbox}% - \next + {\ifcase\MPboxmode + \or % 1 + \doobeyMPboxdepth + \or % 2 + \doignoreMPboxdepth \fi \box\MPgraphicbox} @@ -409,10 +546,10 @@ \getobject{MP}{#1}} \long\def\handleuniqueMPgraphic#1#2#3% - {\blabelgroup + {\begingroup \def\@@meta{#1:}% \extendMPoverlaystamp{#2}% incl prepare - \ifundefined{\@@MPG\overlaystamp:#1}% + \ifcsname\@@MPG\overlaystamp:#1\endcsname\else \enableincludeMPgraphics \forgetall \global\advance\MPobjectcounter\plusone @@ -420,57 +557,51 @@ \setxvalue{\@@MPG\overlaystamp:#1}{\noexpand\reuseMPbox{\number\MPobjectcounter}{\MPllx}{\MPlly}{\MPurx}{\MPury}}% \fi \getvalue{\@@MPG\overlaystamp:#1}% - \elabelgroup} + \endgroup} \long\def\startuniqueMPgraphic - {\blabelgroup - \dodoublegroupempty\dostartuniqueMPgraphic} + {\dodoublegroupempty\dostartuniqueMPgraphic} \long\def\dostartuniqueMPgraphic#1#2#3\stopuniqueMPgraphic% - {\long\setgvalue{\@@MPG#1}{\handleuniqueMPgraphic{#1}{#2}{#3}}% - \elabelgroup} + {\long\setgvalue{\@@MPG#1}{\handleuniqueMPgraphic{#1}{#2}{#3}}} \unexpanded\def\uniqueMPgraphic {\dodoublegroupempty\douniqueMPgraphic} \def\douniqueMPgraphic#1#2% - {\blabelgroup - \setupMPvariables[#1][#2]% - \getvalue{\@@MPG#1}\empty - \elabelgroup} + {\beginMPgraphicgroup{#1}% + \setupMPvariables[\currentMPgraphicname][#2]% + \getvalue{\@@MPG\currentMPgraphicname}\empty + \endMPgraphicgroup} \let\stopuniqueMPcode \relax % so that we can use it in \expanded \long\def\handleuseMPgraphic#1#2#3% - {\blabelgroup + {\begingroup \forgetall % check this \def\@@meta{#1:}% \prepareMPvariables{#2}% \enableincludeMPgraphics \processMPgraphic{#3}% - \elabelgroup} + \endgroup} \long\def\startuseMPgraphic - {\blabelgroup - \dodoublegroupempty\dostartuseMPgraphic} + {\dodoublegroupempty\dostartuseMPgraphic} \long\def\dostartuseMPgraphic#1#2#3\stopuseMPgraphic - {\long\setgvalue{\@@MPG#1}{\handleuseMPgraphic{#1}{#2}{#3}}% - \elabelgroup} + {\long\setgvalue{\@@MPG#1}{\handleuseMPgraphic{#1}{#2}{#3}}} \long\def\startusableMPgraphic % redundant but handy - {\blabelgroup - \dodoublegroupempty\dostartusableMPgraphic} + {\dodoublegroupempty\dostartusableMPgraphic} \long\def\dostartusableMPgraphic#1#2#3\stopusableMPgraphic - {\long\setgvalue{\@@MPG#1}{\handleuseMPgraphic{#1}{#2}{#3}}% - \elabelgroup} + {\long\setgvalue{\@@MPG#1}{\handleuseMPgraphic{#1}{#2}{#3}}} \let\stopuseMPgraphic \relax % so that we can use it in \expanded \let\stopusableMPgraphic \relax % so that we can use it in \expanded \long\def\handlereusableMPgraphic#1#2#3% - {\blabelgroup + {\begingroup \def\@@meta{#1:}% \prepareMPvariables{#2}% \enableincludeMPgraphics @@ -478,15 +609,13 @@ \setobject{MP}{\number\MPobjectcounter}\hbox{\processMPgraphic{#3}}% was vbox, graphic must end up as hbox \setxvalue{\@@MPG#1}{\noexpand\reuseMPbox{\number\MPobjectcounter}{\MPllx}{\MPlly}{\MPurx}{\MPury}}% \getvalue{\@@MPG#1}% - \elabelgroup} + \endgroup} \long\def\startreusableMPgraphic - {\blabelgroup - \dodoublegroupempty\dostartreusableMPgraphic} + {\dodoublegroupempty\dostartreusableMPgraphic} \long\def\dostartreusableMPgraphic#1#2#3\stopreusableMPgraphic - {\long\setgvalue{\@@MPG#1}{\handlereusableMPgraphic{#1}{#2}{#3}}% - \elabelgroup} + {\long\setgvalue{\@@MPG#1}{\handlereusableMPgraphic{#1}{#2}{#3}}} \let\stopreusableMPgraphic \relax % so that we can use it in \expanded @@ -494,10 +623,10 @@ {\dodoublegroupempty\douseMPgraphic} \def\douseMPgraphic#1#2% - {\blabelgroup - \doifsomething{#2}{\setupMPvariables[#1][#2]}% - \getvalue{\@@MPG#1}\empty - \elabelgroup} + {\beginMPgraphicgroup{#1}% + \doifsomething{#2}{\setupMPvariables[\currentMPgraphicname][#2]}% + \getvalue{\@@MPG\currentMPgraphicname}\empty + \endMPgraphicgroup} \let\reuseMPgraphic \useMPgraphic % we can save a setup here if needed \let\reusableMPgraphic\reuseMPgraphic % we can save a setup here if needed @@ -522,23 +651,21 @@ {\MPpageprefix\overlaywidth:\overlayheight:\overlaydepth:\MPcolor\overlaycolor:\MPcolor\overlaylinecolor} \long\def\startuniqueMPpagegraphic - {\blabelgroup - \dodoublegroupempty\dostartuniqueMPpagegraphic} + {\dodoublegroupempty\dostartuniqueMPpagegraphic} \long\def\dostartuniqueMPpagegraphic#1#2#3\stopuniqueMPpagegraphic {\long\setgvalue{\@@MPG o:#1}{\handleuniqueMPgraphic{o:#1}{#2}{#3}}% - \long\setgvalue{\@@MPG e:#1}{\handleuniqueMPgraphic{e:#1}{#2}{#3}}% - \elabelgroup} + \long\setgvalue{\@@MPG e:#1}{\handleuniqueMPgraphic{e:#1}{#2}{#3}}} \unexpanded\def\uniqueMPpagegraphic {\dodoublegroupempty\douniqueMPpagegraphic} \def\douniqueMPpagegraphic#1#2% - {\blabelgroup + {\beginMPgraphicgroup{#1}% \let\overlaystamp\overlaypagestamp - \setupMPvariables[\MPpageprefix#1][#2]% prefix is new here - \getvalue{\@@MPG\MPpageprefix#1}{}% - \elabelgroup} + \setupMPvariables[\MPpageprefix\currentMPgraphicname][#2]% prefix is new here + \getvalue{\@@MPG\MPpageprefix\currentMPgraphicname}{}% + \endMPgraphicgroup} %D One way of defining a stamp is: %D @@ -553,10 +680,10 @@ %D we introduce a dedicated expansion engine. \def\prepareMPvariable#1% - {\ifundefined{\@@framed\@@meta#1}% - \doprepareMPvariable{\@@meta#1}% - \else + {\ifcsname\@@framed\@@meta#1\endcsname \doprepareMPvariable{\@@framed\@@meta#1}% + \else + \doprepareMPvariable{\@@meta#1}% \fi} % \startlines @@ -637,15 +764,35 @@ %D For the moment, the next one is a private macro: -% TODO ! ! ! ! ! ! - \def\processMPbuffer {\dosingleempty\doprocessMPbuffer} +% this fails (keep): +% +% \def\doprocessMPbuffer[#1]% +% {\doifelsenothing{#1} +% {\doprocessMPbuffer[\jobname]} +% {\processMPgraphic{\ctxlua{tex.sprint(tex.ctxcatcodes,buffers.collect("#1"))}}}} % "\\n" +% +% this works (keep): +% +% \def\doprocessMPbuffer[#1]% +% {\doifelsenothing{#1} +% {\doprocessMPbuffer[\jobname]} % #1 can be a list of buffers, otherwise we could use: +% {\processMPgraphic{\ctxlua{tex.sprint(tex.ctxcatcodes,unpack(buffers.data["#1"]))}}}} +% +% this we use: + +\newtoks\mpbuffertoks + \def\doprocessMPbuffer[#1]% {\doifelsenothing{#1} {\doprocessMPbuffer[\jobname]} - {\processMPgraphic{\ctxlua{tex.sprint(tex.ctxcatcodes,buffers.collect(string.split("#1",",")))}}}} + {\beginMPgraphicgroup{#1}% + % we need this trick because tex.sprint does not interprets newlines + \ctxlua{tex.toks.mpbuffertoks=buffers.collect("\currentMPgraphicname")}% + \processMPgraphic{\the\mpbuffertoks}% + \endMPgraphicgroup}} \def\runMPbuffer {\dosingleempty\dorunMPbuffer} @@ -718,8 +865,22 @@ %D %D The most simple case: -\long\def\startMPcode#1\stopMPcode - {\processMPgraphic{#1}} +\def\startMPcode{\dosinglegroupempty\dostartMPcode} + +\def\dostartMPcode + {\iffirstargument + \expandafter\dodostartMPcode + \else + \expandafter\nodostartMPcode + \fi} + +\def\dodostartMPcode#1#2\stopMPcode + {\beginMPgraphicgroup{#1::\s!dummy}% name does not matter + \processMPgraphic{#2}% + \endMPgraphicgroup} + +\def\nodostartMPcode#1#2\stopMPcode + {\processMPgraphic{#2}} \let\stopMPcode\relax @@ -749,7 +910,7 @@ %D accomplished by: \def\douseMPlibrary#1% - {\ifundefined{\c!file\f!metapostprefix#1}% + {\ifcsname\c!file\f!metapostprefix#1\endcsname\else \letvalueempty{\c!file\f!metapostprefix#1}% \makeshortfilename[\truefilename{\f!metapostprefix#1}]% \startreadingfile @@ -839,7 +1000,7 @@ \to \everyMPgraphic \appendtoks - \expanded{\definecolor[currentcolor][\currentcolorname]}% + \normalexpanded{\noexpand\definecolor[currentcolor][\currentcolorname]}% \to \everyMPgraphic \appendtoks @@ -1003,9 +1164,9 @@ \long\def\dostartMPcolor[#1][#2]#3\stopMPcolor % slow but sometimes handy {\startnointerference - \def\handleMPgraycolor{\expanded{\defineglobalcolor[#1][s=\!MPgMPa1,#2]}}% - \def\handleMPrgbcolor {\expanded{\defineglobalcolor[#1][r=\!MPgMPa1,g=\!MPgMPa2,b=\!MPgMPa3,#2]}}% - \def\handleMPcmykcolor{\expanded{\defineglobalcolor[#1][c=\!MPgMPa1,m=\!MPgMPa2,y=\!MPgMPa3,k=\!MPgMPa4,#2]}}% + \def\handleMPgraycolor{\normalexpanded{\noexpand\defineglobalcolor[#1][s=\!MPgMPa1,#2]}}% + \def\handleMPrgbcolor {\normalexpanded{\noexpand\defineglobalcolor[#1][r=\!MPgMPa1,g=\!MPgMPa2,b=\!MPgMPa3,#2]}}% + \def\handleMPcmykcolor{\normalexpanded{\noexpand\defineglobalcolor[#1][c=\!MPgMPa1,m=\!MPgMPa2,y=\!MPgMPa3,k=\!MPgMPa4,#2]}}% \processMPgraphic{#3}% \stopnointerference} @@ -1095,12 +1256,10 @@ {\startreusableMPgraphic{\@@MPG#1@S@}#2\stopreusableMPgraphic} \long\def\startstaticMPgraphic - {\blabelgroup - \dodoublegroupempty\dostartstaticMPgraphic} + {\dodoublegroupempty\dostartstaticMPgraphic} \long\def\dostartstaticMPgraphic#1#2#3\stopstaticMPgraphic - {\long\setgvalue{\@@MPG#1@S@}{\handlereusableMPgraphic{#1}{#2}{#3}}% - \elabelgroup} + {\long\setgvalue{\@@MPG#1@S@}{\handlereusableMPgraphic{#1}{#2}{#3}}} %D New: diff --git a/tex/context/base/meta-pdf.lua b/tex/context/base/meta-pdf.lua index 39f24aa5b..240778bfa 100644 --- a/tex/context/base/meta-pdf.lua +++ b/tex/context/base/meta-pdf.lua @@ -1,228 +1,94 @@ --- filename : meta-pdf.lua --- comment : companion to meta-pdf.tex --- author : Hans Hagen, PRAGMA-ADE, Hasselt NL --- copyright: PRAGMA ADE / ConTeXt Development Team --- license : see context related readme files - --- This is the third version. Version 1 converted to Lua code, --- version 2 gsubbed the file into TeX code, and version 3 uses --- the new lpeg functionality and streams the result into TeX. - --- We will move old stuff to edu. - ---~ old lpeg 0.4 lpeg 0.5 ---~ 100 times test graphic 2.45 (T:1.07) 0.72 (T:0.24) 0.580 (0.560 no table) -- 0.54 optimized for one space (T:0.19) ---~ 100 times big graphic 10.44 4.30/3.35 nogb 2.914 (2.050 no table) -- 1.99 optimized for one space (T:0.85) ---~ 500 times test graphic T:1.29 T:1.16 (T:1.10 no table) -- T:1.10 - -if not versions then versions = { } end versions['meta-pdf'] = 1.003 - -mptopdf = { } -mptopdf.parsers = { } -mptopdf.parser = 'none' - -function mptopdf.reset() - mptopdf.data = "" - mptopdf.path = { } - mptopdf.stack = { } - mptopdf.texts = { } - mptopdf.version = 0 - mptopdf.shortcuts = false - mptopdf.resetpath() -end - -function mptopdf.resetpath() - mptopdf.stack.close = false - mptopdf.stack.path = { } - mptopdf.stack.concat = nil - mptopdf.stack.special = false -end +if not modules then modules = { } end modules ['meta-pdf'] = { + version = 1.001, + comment = "companion to meta-pdf.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -mptopdf.reset() +-- Finally we used an optimized version. The test code can be found in +-- meta-pdh.lua but since we no longer want to overload functione we +-- use more locals now. -function mptopdf.parsers.none() - -- no parser set -end +local concat, format, gsub, find = table.concat, string.format, string.gsub, string.find +local byte = string.byte +local texsprint = tex.sprint -function mptopdf.parse() - mptopdf.parsers[mptopdf.parser]() -end +local ctxcatcodes = tex.ctxcatcodes --- old code +mptopdf = { } +mptopdf.n = 0 -mptopdf.steps = { } +local m_path, m_stack, m_texts, m_version, m_date, m_shortcuts = { }, { }, { }, 0, 0, false -mptopdf.descapes = { - ['('] = "\\\\char40 ", - [')'] = "\\\\char41 ", - ['"'] = "\\\\char92 " -} +local m_stack_close, m_stack_path, m_stack_concat = false, { }, nil -function mptopdf.descape(str) - str = str:gsub("\\(%d%d%d)",function(n) - return "\\char" .. tonumber(n,8) .. " " - end) - return str:gsub("\\([%(%)\\])",mptopdf.descapes) +local function resetpath() + m_stack_close = false + m_stack_path = { } + m_stack_concat = nil end -function mptopdf.steps.descape(str) - str = str:gsub("\\(%d%d%d)",function(n) - return "\\\\char" .. tonumber(n,8) .. " " - end) - return str:gsub("\\([%(%)\\])",mptopdf.descapes) +local function resetall() + m_path, m_stack, m_texts, m_version, m_shortcuts = { }, { }, { }, 0, false + resetpath() end -function mptopdf.steps.strip() -- .3 per expr - mptopdf.data = mptopdf.data:gsub("^(.-)%%+Page:.-%c+(.*)%s+%a+%s+%%+EOF.*$", function(preamble, graphic) - local bbox = "0 0 0 0" - for b in preamble:gmatch("%%%%%a+oundingBox: +(.-)%c+") do - bbox = b - end - local name, version = preamble:gmatch("%%%%Creator: +(.-) +(.-) ") - mptopdf.version = tostring(version or "0") - if preamble:find("/hlw{0 dtransform") then - mptopdf.shortcuts = true - end - -- the boundingbox specification needs to come before data, well, not really - return bbox .. " boundingbox\n" .. "\nbegindata\n" .. graphic .. "\nenddata\n" - end, 1) - mptopdf.data = mptopdf.data:gsub("%%%%MetaPostSpecials: +(.-)%c+", "%1 specials\n", 1) - mptopdf.data = mptopdf.data:gsub("%%%%MetaPostSpecial: +(.-)%c+", "%1 special\n") - mptopdf.data = mptopdf.data:gsub("%%.-%c+", "") -end - -function mptopdf.steps.cleanup() - if not mptopdf.shortcuts then - mptopdf.data = mptopdf.data:gsub("gsave%s+fill%s+grestore%s+stroke", "both") - mptopdf.data = mptopdf.data:gsub("([%d%.]+)%s+([%d%.]+)%s+dtransform%s+exch%s+truncate%s+exch%s+idtransform%s+pop%s+setlinewidth", function(wx,wy) - if tonumber(wx) > 0 then return wx .. " setlinewidth" else return wy .. " setlinewidth" end - end) - mptopdf.data = mptopdf.data:gsub("([%d%.]+)%s+([%d%.]+)%s+dtransform%s+truncate%s+idtransform%s+setlinewidth%s+pop", function(wx,wy) - if tonumber(wx) > 0 then return wx .. " setlinewidth" else return wy .. " setlinewidth" end - end) - end -end +resetall() -function mptopdf.steps.convert() - mptopdf.data = mptopdf.data:gsub("%c%((.-)%) (.-) (.-) fshow", function(str,font,scale) - mptopdf.texts[mptopdf.texts+1] = {mptopdf.steps.descape(str), font, scale} - return "\n" .. #mptopdf.texts .. " textext" - end) - mptopdf.data = mptopdf.data:gsub("%[%s*(.-)%s*%]", function(str) - return str:gsub("%s+"," ") - end) - local t - mptopdf.data = mptopdf.data:gsub("%s*([^%a]-)%s*(%a+)", function(args,cmd) - if cmd == "textext" then - t = mptopdf.texts[tonumber(args)] - return "mps.textext(" .. "\"" .. t[2] .. "\"," .. t[3] .. ",\"" .. t[1] .. "\")\n" - else - return "mps." .. cmd .. "(" .. args:gsub(" +",",") .. ")\n" - end - end) -end +-- code injection, todo: collect and flush packed using node injection -function mptopdf.steps.process() - assert(loadstring(mptopdf.data))() -- () runs the loaded chunk +local function pdfcode(str) -- not used + texsprint(ctxcatcodes,"\\MPScode{",str,"}") end - -function mptopdf.parsers.gsub() - mptopdf.steps.strip() - mptopdf.steps.cleanup() - mptopdf.steps.convert() - mptopdf.steps.process() -end - --- end of old code - --- from lua to tex - -function mptopdf.pdfcode(str) - tex.sprint(tex.ctxcatcodes,"\\PDFcode{" .. str .. "}") -end - -function mptopdf.texcode(str) - tex.sprint(tex.ctxcatcodes,str) +local function texcode(str) + texsprint(ctxcatcodes,str) end -- auxiliary functions -function mptopdf.flushconcat() - if mptopdf.stack.concat then - mptopdf.pdfcode(table.concat(mptopdf.stack.concat," ") .. " cm") - mptopdf.stack.concat = nil +local function flushconcat() + if m_stack_concat then + texsprint(ctxcatcodes,"\\MPScode{",concat(m_stack_concat," ")," cm}") + m_stack_concat = nil end end -function mptopdf.flushpath(cmd) - if #mptopdf.stack.path > 0 then +local function flushpath(cmd) + -- faster: no local function + if #m_stack_path > 0 then local path = { } - if mptopdf.stack.concat then - local sx, sy = mptopdf.stack.concat[1], mptopdf.stack.concat[4] - local rx, ry = mptopdf.stack.concat[2], mptopdf.stack.concat[3] - local tx, ty = mptopdf.stack.concat[5], mptopdf.stack.concat[6] + if m_stack_concat then + local sx, sy = m_stack_concat[1], m_stack_concat[4] + local rx, ry = m_stack_concat[2], m_stack_concat[3] + local tx, ty = m_stack_concat[5], m_stack_concat[6] local d = (sx*sy) - (rx*ry) - local function concat(px, py) - return (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d - end - for _,v in ipairs(mptopdf.stack.path) do - v[1],v[2] = concat(v[1],v[2]) + -- local function mpconcat(px, py) -- move this inline + -- return (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d + -- end + for k=1,#m_stack_path do + local v = m_stack_path[k] + local px, py = v[1], v[2] ; v[1], v[2] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[1],v[2]) if #v == 7 then - v[3],v[4] = concat(v[3],v[4]) - v[5],v[6] = concat(v[5],v[6]) + local px, py = v[3], v[4] ; v[3], v[4] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[3],v[4]) + local px, py = v[5], v[6] ; v[5], v[6] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[5],v[6]) end - path[#path+1] = table.concat(v," ") + path[#path+1] = concat(v," ") end else - for _,v in ipairs(mptopdf.stack.path) do - path[#path+1] = table.concat(v," ") + for k=1,#m_stack_path do + path[#path+1] = concat(m_stack_path[k]," ") end end - mptopdf.flushconcat() - mptopdf.texcode("\\MPSpath{" .. table.concat(path," ") .. "}") - if mptopdf.stack.close then - mptopdf.texcode("\\MPScode{h " .. cmd .. "}") + flushconcat() + texcode("\\MPSpath{" .. concat(path," ") .. "}") + if m_stack_close then + texcode("\\MPScode{h " .. cmd .. "}") else - mptopdf.texcode("\\MPScode{" .. cmd .."}") + texcode("\\MPScode{" .. cmd .."}") end end - mptopdf.resetpath() -end - -if input and input.instance then - function mptopdf.loaded(name) - local ok, n - mptopdf.reset() - ok, mptopdf.data, n = input.loadbinfile(name, 'tex') -- we need a binary load ! - return ok - end -else - function mptopdf.loaded(name) - local f = io.open(name, 'rb') - if f then - mptopdf.reset() - mptopdf.data = f:read('*all') - f:close() - return true - else - return false - end - end -end - -if not mptopdf.parse then - function mptopdf.parse() end -- forward declaration -end - -function mptopdf.convertmpstopdf(name) - if mptopdf.loaded(name) then - input.starttiming(mptopdf) - mptopdf.parse() - mptopdf.reset() - input.stoptiming(mptopdf) - else - tex.print("file " .. name .. " not found") - end + resetpath() end -- mp interface @@ -230,171 +96,133 @@ end mps = mps or { } function mps.creator(a, b, c) - mptopdf.version = tonumber(b) + m_version = tonumber(b) end function mps.creationdate(a) - mptopdf.date= a + m_date = a end function mps.newpath() - mptopdf.stack.path = { } + m_stack_path = { } end function mps.boundingbox(llx, lly, urx, ury) - mptopdf.texcode("\\MPSboundingbox{" .. llx .. "}{" .. lly .. "}{" .. urx .. "}{" .. ury .. "}") + texcode("\\MPSboundingbox{" .. llx .. "}{" .. lly .. "}{" .. urx .. "}{" .. ury .. "}") end function mps.moveto(x,y) - mptopdf.stack.path[#mptopdf.stack.path+1] = {x,y,"m"} + m_stack_path[#m_stack_path+1] = {x,y,"m"} end function mps.curveto(ax, ay, bx, by, cx, cy) - mptopdf.stack.path[#mptopdf.stack.path+1] = {ax,ay,bx,by,cx,cy,"c"} + m_stack_path[#m_stack_path+1] = {ax,ay,bx,by,cx,cy,"c"} end function mps.lineto(x,y) - mptopdf.stack.path[#mptopdf.stack.path+1] = {x,y,"l"} + m_stack_path[#m_stack_path+1] = {x,y,"l"} end function mps.rlineto(x,y) local dx, dy = 0, 0 - if #mptopdf.stack.path > 0 then - dx, dy = mptopdf.stack.path[#mptopdf.stack.path][1], mptopdf.stack.path[#mptopdf.stack.path][2] + if #m_stack_path > 0 then + dx, dy = m_stack_path[#m_stack_path][1], m_stack_path[#m_stack_path][2] end - mptopdf.stack.path[#mptopdf.stack.path+1] = {dx,dy,"l"} + m_stack_path[#m_stack_path+1] = {dx,dy,"l"} end function mps.translate(tx,ty) - mptopdf.pdfcode("1 0 0 0 1 " .. tx .. " " .. ty .. " cm") + texsprint(ctxcatcodes,"\\MPScode{1 0 0 0 1 ",tx," ",ty," cm}") end function mps.scale(sx,sy) - mptopdf.stack.concat = {sx,0,0,sy,0,0} + m_stack_concat = {sx,0,0,sy,0,0} end function mps.concat(sx, rx, ry, sy, tx, ty) - mptopdf.stack.concat = {sx,rx,ry,sy,tx,ty} + m_stack_concat = {sx,rx,ry,sy,tx,ty} end function mps.setlinejoin(d) - mptopdf.pdfcode(d .. " j") + texsprint(ctxcatcodes,"\\MPScode{",d," j}") end function mps.setlinecap(d) - mptopdf.pdfcode(d .. " J") + texsprint(ctxcatcodes,"\\MPScode{",d," J}") end function mps.setmiterlimit(d) - mptopdf.pdfcode(d .. " M") + texsprint(ctxcatcodes,"\\MPScode{",d," M}") end function mps.gsave() - mptopdf.pdfcode("q") + texsprint(ctxcatcodes,"\\MPScode{q}") end function mps.grestore() - mptopdf.pdfcode("Q") + texsprint(ctxcatcodes,"\\MPScode{Q}") end -function mps.setdash(...) +function mps.setdash(...) -- can be made faster, operate on t = { ... } local n = select("#",...) - mptopdf.pdfcode("[" .. table.concat({...}," ",1,n-1) .. "] " .. select(n,...) .. " d") + texsprint(ctxcatcodes,"\\MPScode{","[",concat({...}," ",1,n-1),"] ",select(n,...)," d}") end function mps.resetdash() - mptopdf.pdfcode("[ ] 0 d") + texsprint(ctxcatcodes,"\\MPScode{[ ] 0 d}") end function mps.setlinewidth(d) - mptopdf.pdfcode(d .. " w") + texsprint(ctxcatcodes,"\\MPScode{",d," w}") end function mps.closepath() - mptopdf.stack.close = true + m_stack_close = true end function mps.fill() - mptopdf.flushpath('f') + flushpath('f') end function mps.stroke() - mptopdf.flushpath('S') + flushpath('S') end function mps.both() - mptopdf.flushpath('B') + flushpath('B') end function mps.clip() - mptopdf.flushpath('W n') + flushpath('W n') end function mps.textext(font, scale, str) -- old parser local dx, dy = 0, 0 - if #mptopdf.stack.path > 0 then - dx, dy = mptopdf.stack.path[1][1], mptopdf.stack.path[1][2] + if #m_stack_path > 0 then + dx, dy = m_stack_path[1][1], m_stack_path[1][2] end - mptopdf.flushconcat() - mptopdf.texcode("\\MPStextext{"..font.."}{"..scale.."}{"..str.."}{"..dx.."}{"..dy.."}") - mptopdf.resetpath() + flushconcat() + texcode("\\MPStextext{"..font.."}{"..scale.."}{"..str.."}{"..dx.."}{"..dy.."}") + resetpath() end ---~ function mps.handletext(font,scale.str,dx,dy) ---~ local one, two = string.match(str, "^(%d+)::::(%d+)") ---~ if one and two then ---~ mptopdf.texcode("\\MPTOPDFtextext{"..font.."}{"..scale.."}{"..one.."}{"..two.."}{"..dx.."}{"..dy.."}") ---~ else ---~ mptopdf.texcode("\\MPTOPDFtexcode{"..font.."}{"..scale.."}{"..str.."}{"..dx.."}{"..dy.."}") ---~ end ---~ end - -if false and ctx and ctx.aux and ctx.aux.definecolor then - - logs.report("mptopdf", "using attribute based mps colors") - - -- does not work due to Q-q mess-up - - function mps.setrgbcolor(r,g,b) -- extra check - r, g, b = tonumber(r), tonumber(g), tonumber(b) -- needed when we use lpeg - if r == 0.0123 and g < 0.1 then -- g is extra check - mptopdf.texcode("\\doresetattribute{transparency}\\MPSspecial{" .. g*10000 .. "}{" .. b*10000 .. "}") - elseif r == 0.123 and g < 0.1 then -- g is extra check - mptopdf.texcode("\\doresetattribute{transparency}\\MPSspecial{" .. g* 1000 .. "}{" .. b* 1000 .. "}") - else - mptopdf.texcode("\\doresetattribute{transparency}\\dosetattribute{color}{" .. colors.register('color',nil,'rgb',r,g,b) .. "}") - end - end - - function mps.setcmykcolor(c,m,y,k) - mptopdf.texcode("\\doresetattribute{transparency}\\dosetattribute{color}{" .. colors.register('color',nil,'cmyk',tonumber(c),tonumber(m),tonumber(y),tonumber(k)) .. "}") - end - - function mps.setgray(s) - mptopdf.texcode("\\doresetattribute{transparency}\\dosetattribute{color}{" .. colors.register('color',nil,'gray',tonumber(s)) .. "}") - end - -else - - function mps.setrgbcolor(r,g,b) -- extra check - r, g = tonumber(r), tonumber(g) -- needed when we use lpeg - if r == 0.0123 and g < 0.1 then - mptopdf.texcode("\\MPSspecial{" .. g*10000 .. "}{" .. b*10000 .. "}") - elseif r == 0.123 and g < 0.1 then - mptopdf.texcode("\\MPSspecial{" .. g* 1000 .. "}{" .. b* 1000 .. "}") - else - mptopdf.texcode("\\MPSrgb{" .. r .. "}{" .. g .. "}{" .. b .. "}") - end - end - - function mps.setcmykcolor(c,m,y,k) - mptopdf.texcode("\\MPScmyk{" .. c .. "}{" .. m .. "}{" .. y .. "}{" .. k .. "}") +function mps.setrgbcolor(r,g,b) -- extra check + r, g = tonumber(r), tonumber(g) -- needed when we use lpeg + if r == 0.0123 and g < 0.1 then + texcode("\\MPSspecial{" .. g*10000 .. "}{" .. b*10000 .. "}") + elseif r == 0.123 and g < 0.1 then + texcode("\\MPSspecial{" .. g* 1000 .. "}{" .. b* 1000 .. "}") + else + texcode("\\MPSrgb{" .. r .. "}{" .. g .. "}{" .. b .. "}") end +end - function mps.setgray(s) - mptopdf.texcode("\\MPSgray{" .. s .. "}") - end +function mps.setcmykcolor(c,m,y,k) + texcode("\\MPScmyk{" .. c .. "}{" .. m .. "}{" .. y .. "}{" .. k .. "}") +end +function mps.setgray(s) + texcode("\\MPSgray{" .. s .. "}") end function mps.specials(version,signal,factor) -- 2.0 123 1000 @@ -402,7 +230,7 @@ end function mps.special(...) -- 7 1 0.5 1 0 0 1 3 local n = select("#",...) - mptopdf.texcode("\\MPSbegin\\MPSset{" .. table.concat({...},"}\\MPSset{",2,n) .. "}\\MPSend") + texcode("\\MPSbegin\\MPSset{" .. concat({...},"}\\MPSset{",2,n) .. "}\\MPSend") end function mps.begindata() @@ -414,43 +242,8 @@ end function mps.showpage() end -mps.n = mps.newpath -- n -mps.p = mps.closepath -- h -mps.l = mps.lineto -- l -mps.r = mps.rlineto -- r -mps.m = mps.moveto -- m -mps.c = mps.curveto -- c -mps.hlw = mps.setlinewidth -mps.vlw = mps.setlinewidth - -mps.C = mps.setcmykcolor -- k -mps.G = mps.setgray -- g -mps.R = mps.setrgbcolor -- rg - -mps.lj = mps.setlinejoin -- j -mps.ml = mps.setmiterlimit -- M -mps.lc = mps.setlinecap -- J -mps.sd = mps.setdash -- d -mps.rd = mps.resetdash - -mps.S = mps.stroke -- S -mps.F = mps.fill -- f -mps.B = mps.both -- B -mps.W = mps.clip -- W - -mps.q = mps.gsave -- q -mps.Q = mps.grestore -- Q - -mps.s = mps.scale -- (not in pdf) -mps.t = mps.concat -- (not the same as pdf anyway) - -mps.P = mps.showpage - --- experimental - function mps.attribute(id,value) - mptopdf.texcode("\\attribute " .. id .. "=" .. value .. " ") --- mptopdf.texcode("\\dompattribute{" .. id .. "}{" .. value .. "}") + texcode("\\attribute " .. id .. "=" .. value .. " ") end -- lpeg parser @@ -459,160 +252,178 @@ end -- that MetaPost produces. It's my first real lpeg code, which may -- show. Because the parser binds to functions, we define it last. -do -- assumes \let\c\char +local lpegP, lpegR, lpegS, lpegC, lpegCc, lpegCs = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cs + +local digit = lpegR("09") +local eol = lpegS('\r\n')^1 +local sp = lpegP(' ')^1 +local space = lpegS(' \r\n')^1 +local number = lpegS('0123456789.-+')^1 +local nonspace = lpegP(1-lpegS(' \r\n'))^1 + +local spec = digit^2 * lpegP("::::") * digit^2 +local text = lpegCc("{") * ( + lpegP("\\") * ( (digit * digit * digit) / function(n) return "c" .. tonumber(n,8) end) + + lpegP(" ") / function(n) return "\\c32" end + -- never in new mp + lpegP(1) / function(n) return "\\c" .. byte(n) end + ) * lpegCc("}") +local package = lpegCs(spec + text^0) + +function mps.fshow(str,font,scale) -- lpeg parser + mps.textext(font,scale,package:match(str)) +end + +local cnumber = lpegC(number) +local cstring = lpegC(nonspace) + +local specials = (lpegP("%%MetaPostSpecials:") * sp * (cstring * sp^0)^0 * eol) / mps.specials +local special = (lpegP("%%MetaPostSpecial:") * sp * (cstring * sp^0)^0 * eol) / mps.special +local boundingbox = (lpegP("%%BoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox +local highresboundingbox = (lpegP("%%HiResBoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox + +local setup = lpegP("%%BeginSetup") * (1 - lpegP("%%EndSetup") )^1 +local prolog = lpegP("%%BeginProlog") * (1 - lpegP("%%EndProlog"))^1 +local comment = lpegP('%')^1 * (1 - eol)^1 + +local curveto = ((cnumber * sp)^6 * lpegP("curveto") ) / mps.curveto +local lineto = ((cnumber * sp)^2 * lpegP("lineto") ) / mps.lineto +local rlineto = ((cnumber * sp)^2 * lpegP("rlineto") ) / mps.rlineto +local moveto = ((cnumber * sp)^2 * lpegP("moveto") ) / mps.moveto +local setrgbcolor = ((cnumber * sp)^3 * lpegP("setrgbcolor") ) / mps.setrgbcolor +local setcmykcolor = ((cnumber * sp)^4 * lpegP("setcmykcolor") ) / mps.setcmykcolor +local setgray = ((cnumber * sp)^1 * lpegP("setgray") ) / mps.setgray +local newpath = ( lpegP("newpath") ) / mps.newpath +local closepath = ( lpegP("closepath") ) / mps.closepath +local fill = ( lpegP("fill") ) / mps.fill +local stroke = ( lpegP("stroke") ) / mps.stroke +local clip = ( lpegP("clip") ) / mps.clip +local both = ( lpegP("gsave fill grestore")) / mps.both +local showpage = ( lpegP("showpage") ) +local setlinejoin = ((cnumber * sp)^1 * lpegP("setlinejoin") ) / mps.setlinejoin +local setlinecap = ((cnumber * sp)^1 * lpegP("setlinecap") ) / mps.setlinecap +local setmiterlimit = ((cnumber * sp)^1 * lpegP("setmiterlimit") ) / mps.setmiterlimit +local gsave = ( lpegP("gsave") ) / mps.gsave +local grestore = ( lpegP("grestore") ) / mps.grestore + +local setdash = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("setdash")) / mps.setdash +local concat = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("concat") ) / mps.concat +local scale = ( (cnumber * sp^0)^6 * sp * lpegP("concat") ) / mps.concat + +local fshow = (lpegP("(") * lpegC((1-lpegP(")"))^1) * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow +local fshow = (lpegP("(") * lpegCs( ( lpegP("\\(")/"\\050" + lpegP("\\)")/"\\051" + (1-lpegP(")")) )^1 ) + * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow + +local setlinewidth_x = (lpegP("0") * sp * cnumber * sp * lpegP("dtransform truncate idtransform setlinewidth pop")) / mps.setlinewidth +local setlinewidth_y = (cnumber * sp * lpegP("0 dtransform exch truncate exch idtransform pop setlinewidth") ) / mps.setlinewidth + +local c = ((cnumber * sp)^6 * lpegP("c") ) / mps.curveto -- ^6 very inefficient, ^1 ok too +local l = ((cnumber * sp)^2 * lpegP("l") ) / mps.lineto +local r = ((cnumber * sp)^2 * lpegP("r") ) / mps.rlineto +local m = ((cnumber * sp)^2 * lpegP("m") ) / mps.moveto +local vlw = ((cnumber * sp)^1 * lpegP("vlw")) / mps.setlinewidth +local hlw = ((cnumber * sp)^1 * lpegP("hlw")) / mps.setlinewidth + +local R = ((cnumber * sp)^3 * lpegP("R") ) / mps.setrgbcolor +local C = ((cnumber * sp)^4 * lpegP("C") ) / mps.setcmykcolor +local G = ((cnumber * sp)^1 * lpegP("G") ) / mps.setgray + +local lj = ((cnumber * sp)^1 * lpegP("lj") ) / mps.setlinejoin +local ml = ((cnumber * sp)^1 * lpegP("ml") ) / mps.setmiterlimit +local lc = ((cnumber * sp)^1 * lpegP("lc") ) / mps.setlinecap + +local n = lpegP("n") / mps.newpath +local p = lpegP("p") / mps.closepath +local S = lpegP("S") / mps.stroke +local F = lpegP("F") / mps.fill +local B = lpegP("B") / mps.both +local W = lpegP("W") / mps.clip +local P = lpegP("P") / mps.showpage + +local q = lpegP("q") / mps.gsave +local Q = lpegP("Q") / mps.grestore + +local sd = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("sd")) / mps.setdash +local rd = ( lpegP("rd")) / mps.resetdash + +local s = ( (cnumber * sp^0)^2 * lpegP("s") ) / mps.scale +local t = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("t") ) / mps.concat - local byte = string.byte - local digit = lpeg.R("09") - local spec = digit^2 * lpeg.P("::::") * digit^2 - local text = lpeg.Cc("{") * ( - lpeg.P("\\") * ( (digit * digit * digit) / function(n) return "c" .. tonumber(n,8) end) + - lpeg.P(" ") / function(n) return "\\c32" end + -- never in new mp - lpeg.P(1) / function(n) return "\\c" .. byte(n) end - ) * lpeg.Cc("}") - local package = lpeg.Cs(spec + text^0) +-- experimental - function mps.fshow(str,font,scale) -- lpeg parser - mps.textext(font,scale,package:match(str)) +local attribute = ((cnumber * sp)^2 * lpegP("attribute")) / mps.attribute +local A = ((cnumber * sp)^2 * lpegP("A")) / mps.attribute + +local preamble = ( + prolog + setup + + boundingbox + highresboundingbox + specials + special + + comment +) + +local procset = ( + lj + ml + lc + + c + l + m + n + p + r + + A + + R + C + G + + S + F + B + W + + vlw + hlw + + Q + q + + sd + rd + + t + s + + fshow + + P +) + +local verbose = ( + curveto + lineto + moveto + newpath + closepath + rlineto + + setrgbcolor + setcmykcolor + setgray + + attribute + + setlinejoin + setmiterlimit + setlinecap + + stroke + fill + clip + both + + setlinewidth_x + setlinewidth_y + + gsave + grestore + + concat + scale + + fshow + + setdash + -- no resetdash + showpage +) + +-- order matters in terms of speed / we could check for procset first + +local captures_old = ( space + verbose + preamble )^0 +local captures_new = ( space + procset + preamble + verbose )^0 + +local function parse(m_data) + if find(m_data,"%%%%BeginResource: procset mpost") then + captures_new:match(m_data) + else + captures_old:match(m_data) end - end -do - - local eol = lpeg.S('\r\n')^1 - local sp = lpeg.P(' ')^1 - local space = lpeg.S(' \r\n')^1 - local number = lpeg.S('0123456789.-+')^1 - local nonspace = lpeg.P(1-lpeg.S(' \r\n'))^1 - - local cnumber = lpeg.C(number) - local cstring = lpeg.C(nonspace) - - local specials = (lpeg.P("%%MetaPostSpecials:") * sp * (cstring * sp^0)^0 * eol) / mps.specials - local special = (lpeg.P("%%MetaPostSpecial:") * sp * (cstring * sp^0)^0 * eol) / mps.special - local boundingbox = (lpeg.P("%%BoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox - local highresboundingbox = (lpeg.P("%%HiResBoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox - - local setup = lpeg.P("%%BeginSetup") * (1 - lpeg.P("%%EndSetup") )^1 - local prolog = lpeg.P("%%BeginProlog") * (1 - lpeg.P("%%EndProlog"))^1 - local comment = lpeg.P('%')^1 * (1 - eol)^1 - - local curveto = ((cnumber * sp)^6 * lpeg.P("curveto") ) / mps.curveto - local lineto = ((cnumber * sp)^2 * lpeg.P("lineto") ) / mps.lineto - local rlineto = ((cnumber * sp)^2 * lpeg.P("rlineto") ) / mps.rlineto - local moveto = ((cnumber * sp)^2 * lpeg.P("moveto") ) / mps.moveto - local setrgbcolor = ((cnumber * sp)^3 * lpeg.P("setrgbcolor") ) / mps.setrgbcolor - local setcmykcolor = ((cnumber * sp)^4 * lpeg.P("setcmykcolor") ) / mps.setcmykcolor - local setgray = ((cnumber * sp)^1 * lpeg.P("setgray") ) / mps.setgray - local newpath = ( lpeg.P("newpath") ) / mps.newpath - local closepath = ( lpeg.P("closepath") ) / mps.closepath - local fill = ( lpeg.P("fill") ) / mps.fill - local stroke = ( lpeg.P("stroke") ) / mps.stroke - local clip = ( lpeg.P("clip") ) / mps.clip - local both = ( lpeg.P("gsave fill grestore")) / mps.both - local showpage = ( lpeg.P("showpage") ) - local setlinejoin = ((cnumber * sp)^1 * lpeg.P("setlinejoin") ) / mps.setlinejoin - local setlinecap = ((cnumber * sp)^1 * lpeg.P("setlinecap") ) / mps.setlinecap - local setmiterlimit = ((cnumber * sp)^1 * lpeg.P("setmiterlimit") ) / mps.setmiterlimit - local gsave = ( lpeg.P("gsave") ) / mps.gsave - local grestore = ( lpeg.P("grestore") ) / mps.grestore - - local setdash = (lpeg.P("[") * (cnumber * sp^0)^0 * lpeg.P("]") * sp * cnumber * sp * lpeg.P("setdash")) / mps.setdash - local concat = (lpeg.P("[") * (cnumber * sp^0)^6 * lpeg.P("]") * sp * lpeg.P("concat") ) / mps.concat - local scale = ( (cnumber * sp^0)^6 * sp * lpeg.P("concat") ) / mps.concat - - local fshow = (lpeg.P("(") * lpeg.C((1-lpeg.P(")"))^1) * lpeg.P(")") * space * cstring * space * cnumber * space * lpeg.P("fshow")) / mps.fshow - local fshow = (lpeg.P("(") * - lpeg.Cs( ( lpeg.P("\\(")/"\\050" + lpeg.P("\\)")/"\\051" + (1-lpeg.P(")")) )^1 ) - * lpeg.P(")") * space * cstring * space * cnumber * space * lpeg.P("fshow")) / mps.fshow - - local setlinewidth_x = (lpeg.P("0") * sp * cnumber * sp * lpeg.P("dtransform truncate idtransform setlinewidth pop")) / mps.setlinewidth - local setlinewidth_y = (cnumber * sp * lpeg.P("0 dtransform exch truncate exch idtransform pop setlinewidth") ) / mps.setlinewidth - - local c = ((cnumber * sp)^6 * lpeg.P("c") ) / mps.curveto -- ^6 very inefficient, ^1 ok too - local l = ((cnumber * sp)^2 * lpeg.P("l") ) / mps.lineto - local r = ((cnumber * sp)^2 * lpeg.P("r") ) / mps.rlineto - local m = ((cnumber * sp)^2 * lpeg.P("m") ) / mps.moveto - local vlw = ((cnumber * sp)^1 * lpeg.P("vlw")) / mps.setlinewidth - local hlw = ((cnumber * sp)^1 * lpeg.P("hlw")) / mps.setlinewidth - - local R = ((cnumber * sp)^3 * lpeg.P("R") ) / mps.setrgbcolor - local C = ((cnumber * sp)^4 * lpeg.P("C") ) / mps.setcmykcolor - local G = ((cnumber * sp)^1 * lpeg.P("G") ) / mps.setgray - - local lj = ((cnumber * sp)^1 * lpeg.P("lj") ) / mps.setlinejoin - local ml = ((cnumber * sp)^1 * lpeg.P("ml") ) / mps.setmiterlimit - local lc = ((cnumber * sp)^1 * lpeg.P("lc") ) / mps.setlinecap - - local n = lpeg.P("n") / mps.newpath - local p = lpeg.P("p") / mps.closepath - local S = lpeg.P("S") / mps.stroke - local F = lpeg.P("F") / mps.fill - local B = lpeg.P("B") / mps.both - local W = lpeg.P("W") / mps.clip - local P = lpeg.P("P") / mps.showpage - - local q = lpeg.P("q") / mps.gsave - local Q = lpeg.P("Q") / mps.grestore - - local sd = (lpeg.P("[") * (cnumber * sp^0)^0 * lpeg.P("]") * sp * cnumber * sp * lpeg.P("sd")) / mps.setdash - local rd = ( lpeg.P("rd")) / mps.resetdash - - local s = ( (cnumber * sp^0)^2 * lpeg.P("s") ) / mps.scale - local t = (lpeg.P("[") * (cnumber * sp^0)^6 * lpeg.P("]") * sp * lpeg.P("t") ) / mps.concat - - -- experimental - - local attribute = ((cnumber * sp)^2 * lpeg.P("attribute")) / mps.attribute - local A = ((cnumber * sp)^2 * lpeg.P("A")) / mps.attribute - - local preamble = ( - prolog + setup + - boundingbox + highresboundingbox + specials + special + - comment - ) - - local procset = ( - lj + ml + lc + - c + l + m + n + p + r + - A + - R + C + G + - S + F + B + W + - vlw + hlw + - Q + q + - sd + rd + - t + s + - fshow + - P - ) - - local verbose = ( - curveto + lineto + moveto + newpath + closepath + rlineto + - setrgbcolor + setcmykcolor + setgray + - attribute + - setlinejoin + setmiterlimit + setlinecap + - stroke + fill + clip + both + - setlinewidth_x + setlinewidth_y + - gsave + grestore + - concat + scale + - fshow + - setdash + -- no resetdash - showpage - ) - - -- order matters in terms of speed / we could check for procset first - - local captures_old = ( space + verbose + preamble )^0 - local captures_new = ( space + procset + preamble + verbose )^0 - - function mptopdf.parsers.lpeg() - if mptopdf.data:find("%%%%BeginResource: procset mpost") then - captures_new:match(mptopdf.data) - else - captures_old:match(mptopdf.data) - end - end +-- main converter +function mptopdf.convertmpstopdf(name) + resetall() + local ok, m_data, n = resolvers.loadbinfile(name, 'tex') -- we need a binary load ! + if ok then + statistics.starttiming(mptopdf) + mptopdf.n = mptopdf.n + 1 + parse(m_data) + resetall() + statistics.stoptiming(mptopdf) + else + tex.print("file " .. name .. " not found") + end end -mptopdf.parser = 'lpeg' + +-- status info + +statistics.register("mps conversion time",function() + local n = mptopdf.n + if n > 0 then + return format("%s seconds, %s conversions", statistics.elapsedtime(mptopdf),n) + else + return nil + end +end) diff --git a/tex/context/base/meta-pdf.mkii b/tex/context/base/meta-pdf.mkii index d1a803604..2099b0d37 100644 --- a/tex/context/base/meta-pdf.mkii +++ b/tex/context/base/meta-pdf.mkii @@ -1,8 +1,8 @@ %D \module %D [ file=meta-pdf, %D version=2006.06.07, -%D title=\CONTEXT\ Support Macros, -%D subtitle=\METAPOST\ to \PDF\ conversion, +%D title=\METAPOST\ Graphics, +%D subtitle=Conversion to \PDF, %D author=Hans Hagen \& others (see text), %D date=\currentdate, %D copyright=\PRAGMA] @@ -11,12 +11,171 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. +%D Formerly known as supp-pdf.tex and supp-mpe.tex. + +%D We will clean up the color mess later. + +%D These macros are written as generic as possible. Some +%D general support macro's are loaded from a small module +%D especially made for non \CONTEXT\ use. In this module I +%D use a matrix transformation macro written by Tanmoy +%D Bhattacharya. Thanks to extensive testing by Sebastian +%D Ratz I was able to complete this module within reasonable +%D time. This module has support for \METAPOST\ extensions +%D built in. +%D +%D Daniel H. Luecking came up with a better (more precise) +%D transformation method. You can recognize his comment by +%D his initials. (We keep the old code around because it's a +%D nice illustration on how a module like this evolves.) + +% Beware, we cannot use 0pt here by defaukt since it may be +% defined in the range \dimen 0 - 20 which we happen to use +% as scratch registers; for this reason we start allocating +% scratch registers > 20 + +%D This module handles some \PDF\ conversion and insertions +%D topics. By default, the macros use the \PDFTEX\ primitive +%D \type{\pdfliteral} when available. Since \PDFTEX\ is now the +%D default engine for \TEX\ distributions, we need a more complex +%D test. + +\writestatus{loading}{MetaPost Graphics / MPS to PDF} + \unprotect -%D These are the main macros. +\ifx\PDFcode \undefined \let\PDFcode \gobbleoneargument \fi +\ifx\PDFcomment\undefined \def\PDFcomment#1{\PDFcode{\letterpercent\space#1}} \fi + +%D First we define a handy constant: + +\bgroup \catcode`\%=\@@other \xdef\letterpercent{\string%} \egroup + +%D \macros +%D {pdfimage,pdfimages,pdfclippedimage} +%D +%D Starting with pdftex version 14, images are included more +%D natural to the form embedding. This enables alternative +%D images to be embedded. +%D +%D \starttyping +%D \pdfimageThe directional helpers and pen analysis are more or less translated from the
Serializing nodes can be handy for tracing. Also, saving and +loading node lists can come in handy as soon we are going to +use external applications to process node lists.
+--ldx]]-- + +function nodes.show(stack) +-- texio.write_nl(table.serialize(stack)) +end + +function nodes.save(stack,name) -- *.ltn : luatex node file +-- if name then +-- file.savedata(name,table.serialize(stack)) +-- else +-- texio.write_nl('log',table.serialize(stack)) +-- end +end + +function nodes.load(name) +-- return file.loaddata(name) +-- -- todo +end diff --git a/tex/context/base/node-fin.lua b/tex/context/base/node-fin.lua new file mode 100644 index 000000000..3810b7a85 --- /dev/null +++ b/tex/context/base/node-fin.lua @@ -0,0 +1,363 @@ +if not modules then modules = { } end modules ['node-fin'] = { + version = 1.001, + comment = "companion to node-fin.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this module is being reconstructed + +local next, type, format = next, type, string.format +local texsprint = tex.sprint + +local ctxcatcodes = tex.ctxcatcodes + +local glyph = node.id('glyph') +local glue = node.id('glue') +local rule = node.id('rule') +local whatsit = node.id('whatsit') +local hlist = node.id('hlist') +local vlist = node.id('vlist') + +local has_attribute = node.has_attribute +local copy_node = node.copy + +local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming + +states = states or { } +shipouts = shipouts or { } + +local numbers = attributes.numbers +local trigger = attributes.private('trigger') +local triggering = false + +-- these two will be like trackers + +function states.enabletriggering() + triggering = true +end +function states.disabletriggering() + triggering = false +end + +-- + +states.collected = states.collected or { } + +storage.register("states/collected", states.collected, "states.collected") + +local collected = states.collected + +function states.collect(str) + collected[#collected+1] = str +end + +function states.flush() + if #collected > 0 then + for i=1,#collected do + texsprint(ctxcatcodes,collected[i]) -- we're in context mode anyway + end + collected = { } + states.collected = collected + end +end + +function states.check() + texio.write_nl(concat(collected,"\n")) +end + +-- we used to do the main processor loop here and call processor for each node +-- but eventually this was too much a slow down (1 sec on 23 for 120 pages mk) +-- so that we moved looping to the processor itself; this may lead to a bit of +-- duplicate code once that we have more state handlers + +local function process_attribute(head,plugin) -- head,attribute,enabled,initializer,resolver,processor,finalizer + starttiming(attributes) + local done, used, ok = false, nil, false + local attribute = numbers[plugin.name] -- todo: plugin.attribute + local namespace = plugin.namespace + if namespace.enabled then + local processor = plugin.processor + if processor then + local initializer = plugin.initializer + local resolver = plugin.resolver + local inheritance = (resolver and resolver()) or -0x7FFFFFFF -- we can best use nil and skip ! + if initializer then + initializer(namespace,attribute,head) + end + head, ok = processor(namespace,attribute,head,inheritance) + if ok then + local finalizer = plugin.finalizer + if finalizer then + head, ok, used = finalizer(namespace,attribute,head) + if used then + local flusher = plugin.flusher + if flusher then + local h, d = flusher(namespace,attribute,head,used) + head = h + end + end + end + done = true + end + end + end + stoptiming(attributes) + return head, done +end + +nodes.process_attribute = process_attribute + +function nodes.install_attribute_handler(plugin) + return function(head) + return process_attribute(head,plugin) + end +end + +-- a few handlers + +local current, current_selector, used, done = 0, 0, { }, false + +local function insert(n,stack,previous,head) -- there is a helper, we need previous because we are not slided + if n then + if type(n) == "function" then + n = n() + end + if n then + n = copy_node(n) + n.next = stack + if previous then + previous.next = n + else + head = n + end + previous = n -- ? + else + -- weird + end + end + return stack, head +end + +function states.initialize(what, attribute, stack) + current, current_selector, used, done = 0, 0, { }, false +end + +function states.finalize(namespace,attribute,head) -- is this one ok? + if current > 0 then + local nn = namespace.none + if nn then + local id = head.id + if id == hlist or id == vlist then + local list = head.list + if list then + local _, h = insert(nn,list,nil,list) + head.list = h + end + else + stack, head = insert(nn,head,nil,head) + end + return head, true, true + end + end + return head, false, false +end + +local function process(namespace,attribute,head,inheritance,default) -- one attribute + local trigger = triggering and namespace.triggering and trigger + local stack, previous, done = head, nil, false + local nsdata, nsreviver, nsnone = namespace.data, namespace.reviver, namespace.none + while stack do + local id = stack.id + -- we need to deal with literals too (reset as well as oval) +--~ if id == glyph or (id == whatsit and stack.subtype == 8) or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + if id == glyph or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + local c = has_attribute(stack,attribute) + if c then + if default and c == inheritance then + if current ~= default then + local data = nsdata[default] or nsreviver(default) + stack, head = insert(data,stack,previous,head) + current, done, used[default] = default, true, true + end + elseif current ~= c then + local data = nsdata[c] or nsreviver(c) + stack, head = insert(data,stack,previous,head) + current, done, used[c] = c, true, true + end + -- here ? compare selective + if id == glue then --leader + -- same as *list + local content = stack.leader + if content then + local savedcurrent = current + local ci = content.id + if ci == hlist or ci == vlist then + -- else we reset inside a box unneeded, okay, the downside is + -- that we trigger color in each repeated box, so there is room + -- for improvement here + current = 0 + end + local ok = false + if trigger and has_attribute(stack,trigger) then + local outer = has_attribute(stack,attribute) + if outer ~= inheritance then + stack.leader, ok = process(namespace,attribute,content,inheritance,outer) + else + stack.leader, ok = process(namespace,attribute,content,inheritance,default) + end + else + stack.leader, ok = process(namespace,attribute,content,inheritance,default) + end + current = savedcurrent + done = done or ok + end + end + elseif default and inheritance then + if current ~= default then + local data = nsdata[default] or nsreviver(default) + stack, head = insert(data,stack,previous,head) + current, done, used[default] = default, true, true + end + elseif current > 0 then + stack, head = insert(nsnone,stack,previous,head) + current, done, used[0] = 0, true, true + end + elseif id == hlist or id == vlist then + local content = stack.list + if content then + local ok = false + if trigger and has_attribute(stack,trigger) then + local outer = has_attribute(stack,attribute) + if outer ~= inheritance then + stack.list, ok = process(namespace,attribute,content,inheritance,outer) + else + stack.list, ok = process(namespace,attribute,content,inheritance,default) + end + else + stack.list, ok = process(namespace,attribute,content,inheritance,default) + end + done = done or ok + end + end + previous = stack + stack = stack.next + end + -- we need to play safe +-- i need a proper test set for this, maybe controlled per feature +--~ if current > 0 then +--~ stack, head = insert(nsnone,stack,previous,head) +--~ current, current_selector, done, used[0] = 0, 0, true, true +--~ end + return head, done +end + +states.process = process + +-- we can force a selector, e.g. document wide color spaces, saves a little +-- watch out, we need to check both the selector state (like colorspace) and +-- the main state (like color), otherwise we get into troubles when a selector +-- state changes while the main state stays the same (like two glyphs following +-- each other with the same color but different color spaces e.g. \showcolor) + +local function selective(namespace,attribute,head,inheritance,default) -- two attributes + local trigger = triggering and namespace.triggering and trigger + local stack, previous, done = head, nil, false + local nsforced, nsselector = namespace.forced, namespace.selector + local nsdata, nsreviver, nsnone = namespace.data, namespace.reviver, namespace.none + while stack do + local id = stack.id + -- we need to deal with literals too (reset as well as oval) +--~ if id == glyph or (id == whatsit and stack.subtype == 8) or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + if id == glyph or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + local c = has_attribute(stack,attribute) + if c then + if default and c == inheritance then + if current ~= default then + local data = nsdata[default] or nsreviver(default) + stack, head = insert(data[nsforced or has_attribute(stack,nsselector) or nsselector],stack,previous,head) + current, done, used[default] = default, true, true + end + else + local s = has_attribute(stack,nsselector) + if current ~= c or current_selector ~= s then + local data = nsdata[c] or nsreviver(c) + stack, head = insert(data[nsforced or has_attribute(stack,nsselector) or nsselector],stack,previous,head) + current, current_selector, done, used[c] = c, s, true, true + end + end + elseif default and inheritance then + if current ~= default then + local data = nsdata[default] or nsreviver(default) + stack, head = insert(data[nsforced or has_attribute(stack,nsselector) or nsselector],stack,previous,head) + current, done, used[default] = default, true, true + end + elseif current > 0 then + stack, head = insert(nsnone,stack,previous,head) + current, current_selector, done, used[0] = 0, 0, true, true + end + if id == glue then -- leader + -- same as *list + local content = stack.leader + if content then + local savedcurrent = current + local ci = content.id + if ci == hlist or ci == vlist then + -- else we reset inside a box unneeded, okay, the downside is + -- that we trigger color in each repeated box, so there is room + -- for improvement here + current = 0 + end + local ok = false + if trigger and has_attribute(stack,trigger) then + local outer = has_attribute(stack,attribute) + if outer ~= inheritance then + stack.leader, ok = selective(namespace,attribute,content,inheritance,outer) + else + stack.leader, ok = selective(namespace,attribute,content,inheritance,default) + end + else + stack.leader, ok = selective(namespace,attribute,content,inheritance,default) + end + current = savedcurrent + done = done or ok + end + end + elseif id == hlist or id == vlist then + local content = stack.list + if content then + local ok = false + if trigger and has_attribute(stack,trigger) then + local outer = has_attribute(stack,attribute) + if outer ~= inheritance then + stack.list, ok = selective(namespace,attribute,content,inheritance,outer) + else + stack.list, ok = selective(namespace,attribute,content,inheritance,default) + end + else + stack.list, ok = selective(namespace,attribute,content,inheritance,default) + end + done = done or ok + end + end + previous = stack + stack = stack.next + end + -- we need to play safe, this is subptimal since now we end each box + -- even if it's not needed +-- i need a proper test set for this, maybe controlled per feature +--~ if current > 0 then +--~ stack, head = insert(nsnone,stack,previous,head) +--~ current, current_selector, done, used[0] = 0, 0, true, true +--~ end + return head, done +end + +states.selective = selective + +statistics.register("attribute processing time", function() + if statistics.elapsedindeed(attributes) then + return format("%s seconds",statistics.elapsedtime(attributes)) + end +end) diff --git a/tex/context/base/node-fin.tex b/tex/context/base/node-fin.tex new file mode 100644 index 000000000..787706ff2 --- /dev/null +++ b/tex/context/base/node-fin.tex @@ -0,0 +1,78 @@ +%D \module +%D [ file=attr-ini, +%D version=2007.06.06, % probably a bit older +%D title=\CONTEXT\ Node Macros, +%D subtitle=Finalizing, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA-ADE] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Node Support / Finalizing} + +% Objects are processed indepently \unknown\ actually we may +% need a proper callback. + +\unprotect + +\registerctxluafile{node-fin}{1.001} % we might generalize this one + +\definesystemattribute[trigger] % feature inheritance + +\newbox\finalizedshipoutbox + +\def\finalizeobjectbox#1{\ctxlua{nodes.process_page(tex.box[\number#1])}} + +\def\finalizeshipoutbox#1% % hack till we have access to pdf backend + {\global\setbox\finalizedshipoutbox\hbox{#1}% + \finalizeobjectbox\finalizedshipoutbox + \hbox{\ctxlua{states.flush()}\box\finalizedshipoutbox}} + +% tricky stuff: + +\newcount\attributeboxcount + +\edef\startinheritattributes{\dosetattribute {trigger}{1}} +\edef\stopinheritattributes {\doresetattribute{trigger}} + +\def\doattributedcopy {\afterassignment\dodoattributedcopy\attributeboxcount} +\def\doattributedbox {\afterassignment\dodoattributedbox \attributeboxcount} + +\def\dodoattributedcopy + {\startinheritattributes + \ifvbox\attributeboxcount + \vbox{\unvcopy\attributeboxcount}% + \else + \hbox{\unhcopy\attributeboxcount}% + \fi + \stopinheritattributes} + +\def\dodoattributedbox + {\startinheritattributes + \ifvbox\attributeboxcount + \vbox{\unvbox\attributeboxcount}% + \else + \hbox{\unhbox\attributeboxcount}% + \fi + \stopinheritattributes} + +\def\enableattributeinheritance + {\ctxlua{states.enabletriggering()}% + \let\attributedcopy\doattributedcopy + \let\attributedbox \doattributedbox} + +\def\disableattributeinheritance + {\ctxlua{states.disabletriggering()}% + \let\attributedcopy\copy + \let\attributedbox \box} + +\disableattributeinheritance + +% \appendtoks +% \enableattributeinheritance % will become default +% \to\everyjob + +\protect \endinput diff --git a/tex/context/base/node-fnt.lua b/tex/context/base/node-fnt.lua new file mode 100644 index 000000000..3ad9060c3 --- /dev/null +++ b/tex/context/base/node-fnt.lua @@ -0,0 +1,206 @@ +if not modules then modules = { } end modules ['node-fnt'] = { + version = 1.001, + comment = "companion to font-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local next, type = next, type + +local trace_characters = false trackers.register("nodes.characters", function(v) trace_characters = v end) + +local glyph = node.id('glyph') + +local traverse_id = node.traverse_id +local has_attribute = node.has_attribute + +local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming + +fonts = fonts or { } +fonts.tfm = fonts.tfm or { } +fonts.ids = fonts.ids or { } + +local fontdata = fonts.ids + +-- some tests with using an array of dynamics[id] and processes[id] demonstrated +-- that there was nothing to gain (unless we also optimize other parts) +-- +-- maybe getting rid of the intermediate shared can save some time + +-- potential speedup: check for subtype < 256 so that we can remove that test +-- elsewhere, danger: injected nodes will not be dealt with but that does not +-- happen often; we could consider processing sublists but that might need mor +-- checking later on; the current approach also permits variants + +if tex.attribute[0] < 0 then + + texio.write_nl("log","!") + texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be") + texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special") + texio.write_nl("log","! purposed so setting them at the TeX end might break the font handler.") + texio.write_nl("log","!") + + tex.attribute[0] = 0 -- else no features + +end + +function nodes.process_characters(head) + -- either next or not, but definitely no already processed list + starttiming(nodes) + local usedfonts, attrfonts, done = { }, { }, false + local a, u, prevfont, prevattr = 0, 0, nil, 0 + for n in traverse_id(glyph,head) do + local font, attr = n.font, has_attribute(n,0) -- zero attribute is reserved for fonts, preset to 0 is faster (first match) + if attr and attr > 0 then + if font ~= prevfont or attr ~= prevattr then + local used = attrfonts[font] + if not used then + used = { } + attrfonts[font] = used + end + if not used[attr] then + -- we do some testing outside the function + local tfmdata = fontdata[font] + local shared = tfmdata.shared + if shared then + local dynamics = shared.dynamics + if dynamics then + local d = shared.set_dynamics(font,dynamics,attr) -- still valid? + if d then + used[attr] = d + a = a + 1 + end + end + end + end + prevfont, prevattr = font, attr + end + elseif font ~= prevfont then + prevfont, prevattr = font, 0 + local used = usedfonts[font] + if not used then + local tfmdata = fontdata[font] + if tfmdata then + local shared = tfmdata.shared -- we need to check shared, only when same features + if shared then + local processors = shared.processes + if processors and #processors > 0 then + usedfonts[font] = processors + u = u + 1 + end + end + else + -- probably nullfont + end + end + else + prevattr = attr + end + end + -- we could combine these and just make the attribute nil + if u == 1 then + local font, processors = next(usedfonts) + local n = #processors + if n > 0 then + local h, d = processors[1](head,font,false) + head, done = h or head, done or d + if n > 1 then + for i=2,n do + local h, d = processors[i](head,font,0) -- false) + head, done = h or head, done or d + end + end + end + elseif u > 0 then + for font, processors in next, usedfonts do + local n = #processors + local h, d = processors[1](head,font,false) + head, done = h or head, done or d + if n > 1 then + for i=2,n do + local h, d = processors[i](head,font,0) -- false) + head, done = h or head, done or d + end + end + end + end + if a == 1 then + local font, dynamics = next(attrfonts) + for attribute, processors in next, dynamics do -- attr can switch in between + local n = #processors + local h, d = processors[1](head,font,attribute) + head, done = h or head, done or d + if n > 1 then + for i=2,n do + local h, d = processors[i](head,font,attribute) + head, done = h or head, done or d + end + end + end + elseif a > 0 then + for font, dynamics in next, attrfonts do + for attribute, processors in next, dynamics do -- attr can switch in between + local n = #processors + local h, d = processors[1](head,font,attribute) + head, done = h or head, done or d + if n > 1 then + for i=2,n do + local h, d = processors[i](head,font,attribute) + head, done = h or head, done or d + end + end + end + end + end + stoptiming(nodes) + if trace_characters then + nodes.report(head,done) + end + return head, true +end + +if node.protect_glyphs then + + nodes.protect_glyphs = node.protect_glyphs + nodes.unprotect_glyphs = node.unprotect_glyphs + +else do + + -- initial value subtype : X000 0001 = 1 = 0x01 = char + -- + -- expected before linebreak : X000 0000 = 0 = 0x00 = glyph + -- X000 0010 = 2 = 0x02 = ligature + -- X000 0100 = 4 = 0x04 = ghost + -- X000 1010 = 10 = 0x0A = leftboundary lig + -- X001 0010 = 18 = 0x12 = rightboundary lig + -- X001 1010 = 26 = 0x1A = both boundaries lig + -- X000 1100 = 12 = 0x1C = leftghost + -- X001 0100 = 20 = 0x14 = rightghost + + + function nodes.protect_glyphs(head) + local done = false + for g in traverse_id(glyph,head) do + local s = g.subtype + if s == 1 then + done, g.subtype = true, 256 + elseif s <= 256 then + done, g.subtype = true, 256 + s + end + end + return done + end + + function nodes.unprotect_glyphs(head) + local done = false + for g in traverse_id(glyph,head) do + local s = g.subtype + if s > 256 then + done, g.subtype = true, s - 256 + end + end + return done + end + +end end diff --git a/tex/context/base/node-ini.lua b/tex/context/base/node-ini.lua index 8b451124e..8185e3033 100644 --- a/tex/context/base/node-ini.lua +++ b/tex/context/base/node-ini.lua @@ -7,225 +7,68 @@ if not modules then modules = { } end modules ['node-ini'] = { } --[[ldx-- -Access to nodes is what gives
Most of the code that had accumulated here is now separated in +modules.
--ldx]]-- -local format = string.format - -nodes = nodes or { } -nodes.trace = false -nodes.ignore = nodes.ignore or false - -local hlist = node.id('vlist') -local vlist = node.id('hlist') -local glyph = node.id('glyph') -local disc = node.id('disc') -local mark = node.id('mark') -local glue = node.id('glue') -local whatsit = node.id('whatsit') - --- handy helpers - -if node.protect_glyphs then - - nodes.protect_glyphs = node.protect_glyphs - nodes.unprotect_glyphs = node.unprotect_glyphs +-- this module is being reconstructed -else do +local utf = unicode.utf8 +local next, type = next, type +local format, concat, match, utfchar = string.format, table.concat, string.match, utf.char - -- initial value subtype : X000 0001 = 1 = 0x01 = char - -- - -- expected before linebreak : X000 0000 = 0 = 0x00 = glyph - -- X000 0010 = 2 = 0x02 = ligature - -- X000 0100 = 4 = 0x04 = ghost - -- X000 1010 = 10 = 0x0A = leftboundary lig - -- X001 0010 = 18 = 0x12 = rightboundary lig - -- X001 1010 = 26 = 0x1A = both boundaries lig - -- X000 1100 = 12 = 0x1C = leftghost - -- X001 0100 = 20 = 0x14 = rightghost +local chardata = characters and characters.data +--[[ldx-- +We start with a registration system for atributes so that we can use the +symbolic names later on.
+--ldx]]-- - local traverse_id = node.traverse_id - - function nodes.protect_glyphs(head) - local done = false - for g in traverse_id(glyph,head) do - local s = g.subtype - if s == 1 then - done, g.subtype = true, 256 - elseif s <= 256 then - done, g.subtype = true, 256 + s - end - end - return done - end - - function nodes.unprotect_glyphs(head) - local done = false - for g in traverse_id(glyph,head) do - local s = g.subtype - if s > 256 then - done, g.subtype = true, s - 256 - end - end - return done - end - -end end - -do - - local remove, free = node.remove, node.free - - function nodes.remove(head, current, free_too) - local t = current - head, current = remove(head,current) - if t then - if free_too then - free(t) - t = nil - else - t.next, t.prev = nil, nil - end - end - return head, current, t - end - ---~ function nodes.remove(head, current, delete) ---~ local t = current ---~ if current == head then ---~ current = current.next ---~ if current then ---~ current.prev = nil ---~ end ---~ head = current ---~ else ---~ local prev, next = current.prev, current.next ---~ if prev then ---~ prev.next = next ---~ end ---~ if next then ---~ next.prev = prev ---~ end ---~ current = next -- not: or next ---~ end ---~ if t then ---~ if free_too then ---~ free(t) ---~ t = nil ---~ else ---~ t.next, t.prev = nil, nil ---~ end ---~ end ---~ return head, current, t ---~ end - +attributes = attributes or { } - function nodes.delete(head,current) - return nodes.remove(head,current,true) - end +attributes.names = attributes.names or { } +attributes.numbers = attributes.numbers or { } +attributes.list = attributes.list or { } +attributes.unsetvalue = -0x7FFFFFFF - nodes.before = node.insert_before -- broken - nodes.after = node.insert_after +storage.register("attributes/names", attributes.names, "attributes.names") +storage.register("attributes/numbers", attributes.numbers, "attributes.numbers") +storage.register("attributes/list", attributes.list, "attributes.list") - function nodes.before(h,c,n) - if c then - if c == h then - n.next = h - n.prev = nil - h.prev = n - else - local cp = c.prev - n.next = c - n.prev = cp - if cp then - cp.next = n - end - c.prev = n - return h, n - end - end - return n, n - end +local names, numbers, list = attributes.names, attributes.numbers, attributes.list - function nodes.after(h,c,n) - if c then - local cn = c.next - if cn then - n.next = cn - cn.prev = n - else - n.next = nil - end - c.next = n - n.prev = c ---~ if c ~= h then - return h, n ---~ end - end - return n, n +function attributes.define(name,number) -- at the tex end + if not numbers[name] then + numbers[name], names[number], list[number] = number, name, { } end - - function nodes.show_list(head, message) - if message then - texio.write_nl(message) - end - for n in node.traverse(head) do - texio.write_nl(tostring(n)) - end - end - end --- will move +--[[ldx-- +We can use the attributes in the range 127-255 (outside user space). These
+are only used when no attribute is set at the \TEX\ end which normally
+happens in
Access to nodes is what gives
When manipulating node lists in
Serializing nodes can be handy for tracing. Also, saving and -loading node lists can come in handy as soon we are going to -use external applications to process node lists.
---ldx]]-- - -function nodes.show(stack) ---~ texio.write_nl(table.serialize(stack)) -end - -function nodes.save(stack,name) -- *.ltn : luatex node file ---~ if name then ---~ file.savedata(name,table.serialize(stack)) ---~ else ---~ texio.write_nl('log',table.serialize(stack)) ---~ end -end - -function nodes.load(name) ---~ return file.loaddata(name) -end - --- node-cap.lua - ---~ nodes.capture = { } -- somehow fails - ---~ function nodes.capture.start(cbk) ---~ local head, tail = nil, nil ---~ callbacks.push(cbk, function(t) ---~ if tail then ---~ tail.next = t ---~ else ---~ head, tail = t, t ---~ end ---~ while tail.next do ---~ tail = tail.next ---~ end ---~ return false ---~ end) ---~ function nodes.capture.stop() ---~ function nodes.capture.stop() end ---~ function nodes.capture.get() ---~ function nodes.capture.get() end ---~ return head ---~ end ---~ callbacks.pop(cbk) ---~ end ---~ function nodes.capture.get() end -- error ---~ end - ---~ nodes.capture.stop = function() end ---~ nodes.capture.get = function() end - --- node-gly.lua - -fonts = fonts or { } -fonts.otf = fonts.otf or { } -fonts.tfm = fonts.tfm or { } -fonts.tfm.id = fonts.tfm.id or { } - -local tfm = fonts.tfm -local otf = fonts.otf -local tfmid = fonts.tfm.id - -do - - local has_attribute = node.has_attribute - local traverse_id = node.traverse_id - - local pairs = pairs - - local starttiming, stoptiming = input.starttiming, input.stoptiming +local hlist = node.id('hlist') +local vlist = node.id('vlist') +local glyph = node.id('glyph') +local glue = node.id('glue') +local penalty = node.id('penalty') +local kern = node.id('kern') +local whatsit = node.id('whatsit') - function nodes.process_characters(head) - -- not ok yet; we need a generic blocker - -- if status.output_active then - if false then -- status.output_active then - return head, false -- true +local traverse_id = node.traverse_id +local traverse = node.traverse +local slide_nodes = node.slide +local free_node = node.free +local remove_node = node.remove + +function nodes.remove(head, current, free_too) + local t = current + head, current = remove_node(head,current) + if t then + if free_too then + free_node(t) + t = nil else - -- either next or not, but definitely no already processed list - starttiming(nodes) - local usedfonts, attrfonts, done = { }, { }, false - -- todo: should be independent of otf - local set_dynamics = otf.set_dynamics -- todo: font-var.lua so that we can global this one - local a, u, prevfont, prevattr = 0, 0, nil, 0 - for n in traverse_id(glyph,head) do - local font, attr = n.font, has_attribute(n,0) -- zero attribute is reserved for fonts, preset to 0 is faster (first match) - if attr and attr > 0 then - if font ~= prevfont or attr ~= prevattr then - local used = attrfonts[font] - if not used then - used = { } - attrfonts[font] = used - end - if not used[attr] then - local d = set_dynamics(tfmid[font],attr) -- todo, script, language -> n.language also axis - if d then - used[attr] = d - a = a + 1 - end - end - prevfont, prevattr = font, attr - end - elseif font ~= prevfont then - prevfont, prevattr = font, 0 - local used = usedfonts[font] - if not used then - local data = tfmid[font] - if data then - local shared = data.shared -- we need to check shared, only when same features - if shared then - local processors = shared.processors - if processors and #processors > 0 then - usedfonts[font] = processors - u = u + 1 - end - end - else - -- probably nullfont - end - end - else - prevattr = attr - end - end - -- we could combine these and just make the attribute nil - if u > 0 then - for font, processors in pairs(usedfonts) do - local n = #processors - if n == 1 then - local h, d = processors[1](head,font,false) - head, done = h or head, done or d - else - for i=1,#processors do - local h, d = processors[i](head,font,false) - head, done = h or head, done or d - end - end - end - end - if a > 0 then -- we need to get rid of a loop here - for font, dynamics in pairs(attrfonts) do - for attribute, processors in pairs(dynamics) do -- attr can switch in between - local n = #processors - if n == 1 then - local h, d = processors[1](head,font,attribute) - head, done = h or head, done or d - else - for i=1,n do - local h, d = processors[i](head,font,attribute) - head, done = h or head, done or d - end - end - end - end - end - stoptiming(nodes) - if nodes.trace then - nodes.report(head,done) - end - return head, true - end - end - -end - --- vbox: grouptype: vbox vtop output split_off split_keep | box_type: exactly|aditional --- hbox: grouptype: hbox adjusted_hbox(=hbox_in_vmode) | box_type: exactly|aditional - -do - - local has_attribute, set, attribute = node.has_attribute, node.set_attribute, tex.attribute - - function nodes.inherit_attributes(n) -- still ok ? - if n then - local i = 1 - while true do - local a = attribute[i] - if a < 0 then - break - else - local ai = has_attribute(n,i) - if not ai then - set(n,i,a) - end - i = i + 1 - end - end + t.next, t.prev = nil, nil end - end - + end + return head, current, t end -function nodes.length(head) - if head then - local m = 0 - for n in node.traverse(head) do - m = m + 1 - end - return m - else - return 0 - end +function nodes.delete(head,current) + return nodes.remove(head,current,true) end -lists = lists or { } -chars = chars or { } -words = words or { } -- not used yet - -callbacks.trace = false - -do +nodes.before = node.insert_before -- broken +nodes.after = node.insert_after - kernel = kernel or { } +-- we need to test this, as it might be fixed - local starttiming, stoptiming = input.starttiming, input.stoptiming - local hyphenate, ligaturing, kerning = lang.hyphenate, node.ligaturing, node.kerning - - function kernel.hyphenation(head,tail) -- lang.hyphenate returns done - if head == tail then - return head, tail, false - else - starttiming(kernel) - local done = head ~= tail and hyphenate(head,tail) - stoptiming(kernel) - return head, tail, done - end - end - function kernel.ligaturing(head,tail) -- node.ligaturing returns head,tail,done - if head == tail then - return head, tail, false +function nodes.before(h,c,n) + if c then + if c == h then + n.next = h + n.prev = nil + h.prev = n else - starttiming(kernel) - local head, tail, done = ligaturing(head,tail) - stoptiming(kernel) - return head, tail, done - end - end - function kernel.kerning(head,tail) -- node.kerning returns head,tail,done - if head == tail then - return head, tail, false - else - starttiming(kernel) - local head, tail, done = kerning(head,tail) - stoptiming(kernel) - return head, tail, done - end - end - -end - -callback.register('hyphenate' , function(head,tail) return tail end) -callback.register('ligaturing', function(head,tail) return tail end) -callback.register('kerning' , function(head,tail) return tail end) - -nodes.tasks = nodes.tasks or { } -nodes.tasks.data = nodes.tasks.data or { } - -function nodes.tasks.new(name,list) - local tasklist = sequencer.reset() - nodes.tasks.data[name] = { list = tasklist, runner = false } - for _, task in ipairs(list) do - sequencer.appendgroup(tasklist,task) - end -end - -function nodes.tasks.appendaction(name,group,action,where,kind) - local data = nodes.tasks.data[name] - sequencer.appendaction(data.list,group,action,where,kind) - data.runner = false -end - -function nodes.tasks.prependaction(name,group,action,where,kind) - local data = nodes.tasks.data[name] - sequencer.prependaction(data.list,group,action,where,kind) - data.runner = false -end - -function nodes.tasks.removeaction(name,group,action) - local data = nodes.tasks.data[name] - sequencer.removeaction(data.list,group,action) - data.runner = false -end - -function nodes.tasks.showactions(name,group,action,where,kind) - local data = nodes.tasks.data[name] - logs.report("nodes","task %s, list:\n%s",name,sequencer.nodeprocessor(data.list)) -end - -function nodes.tasks.actions(name) - local data = nodes.tasks.data[name] - return function(head,tail) - local runner = data.runner - if not runner then - if nodes.trace_tasks then - logs.report("nodes","creating task runner '%s'",name) + local cp = c.prev + n.next = c + n.prev = cp + if cp then + cp.next = n end - runner = sequencer.compile(data.list,sequencer.nodeprocessor) - data.runner = runner + c.prev = n + return h, n end - return runner(head,tail) end + return n, n end -nodes.tasks.new ( - "processors", - { - "before", -- for users - "normalizers", - "characters", - "words", - "fonts", - "lists", - "after", -- for users - } -) - --- these definitions will move - -nodes.tasks.appendaction("processors", "normalizers", "nodes.normalize_fonts", nil) -nodes.tasks.appendaction("processors", "characters", "chars.handle_mirroring", nil, "notail") -nodes.tasks.appendaction("processors", "characters", "chars.handle_casing", nil, "notail") -nodes.tasks.appendaction("processors", "characters", "chars.handle_breakpoints", nil, "notail") -nodes.tasks.appendaction("processors", "words", "kernel.hyphenation", nil) -nodes.tasks.appendaction("processors", "words", "languages.words.check", nil, "notail") -nodes.tasks.appendaction("processors", "fonts", "nodes.process_characters", nil, "notail") -nodes.tasks.appendaction("processors", "fonts", "nodes.protect_glyphs", nil, "nohead") -nodes.tasks.appendaction("processors", "fonts", "kernel.ligaturing", nil) -nodes.tasks.appendaction("processors", "fonts", "kernel.kerning", nil) -nodes.tasks.appendaction("processors", "lists", "lists.handle_spacing", nil, "notail") -nodes.tasks.appendaction("processors", "lists", "lists.handle_kerning", nil, "notail") - - -local free = node.free - -local function cleanup_page(head) -- rough - local prev, start = nil, head - while start do - local id, nx = start.id, start.next - if id == disc or id == mark then - if prev then - prev.next = nx - end - if start == head then - head = nx - end - local tmp = start - start = nx - free(tmp) - elseif id == hlist or id == vlist then - local sl = start.list - if sl then - start.list = cleanup_page(sl) - end - prev, start = start, nx +function nodes.after(h,c,n) + if c then + local cn = c.next + if cn then + n.next = cn + cn.prev = n else - prev, start = start, nx + n.next = nil end + c.next = n + n.prev = c + return h, n end - return head -end - -nodes.cleanup_page_first = false - -function nodes.cleanup_page(head) - if nodes.cleanup_page_first then - head = cleanup_page(head) - end - return head, false -end - -nodes.tasks.new ( - "shipouts", - { - "before", -- for users - "normalizers", - "finishers", - "after", -- for users - } -) - -nodes.tasks.appendaction("shipouts", "normalizers", "nodes.cleanup_page", nil, "notail") -nodes.tasks.appendaction("shipouts", "finishers", "shipouts.handle_color", nil, "notail") -nodes.tasks.appendaction("shipouts", "finishers", "shipouts.handle_transparency", nil, "notail") -nodes.tasks.appendaction("shipouts", "finishers", "shipouts.handle_overprint", nil, "notail") -nodes.tasks.appendaction("shipouts", "finishers", "shipouts.handle_negative", nil, "notail") -nodes.tasks.appendaction("shipouts", "finishers", "shipouts.handle_effect", nil, "notail") -nodes.tasks.appendaction("shipouts", "finishers", "shipouts.handle_viewerlayer", nil, "notail") - -local actions = nodes.tasks.actions("shipouts") - -function nodes.process_page(head) -- problem, attr loaded before node, todo ... - return actions(head) -- no tail + return n, n end --- or just: nodes.process_page = nodes.tasks.actions("shipouts") - - -do -- remove these - - local actions = nodes.tasks.actions("processors") - local first_character = node.first_character - local slide = node.slide - - local n = 0 - - local function reconstruct(head) - local t = { } - local h = head - while h do - local id = h.id - if id == glyph then - t[#t+1] = utf.char(h.char) - else - t[#t+1] = "[]" - end - h = h.next - end - return table.concat(t) - end - - local function tracer(what,state,head,groupcode,before,after,show) - if not groupcode then - groupcode = "unknown" - elseif groupcode == "" then - groupcode = "mvl" - end - n = n + 1 - if show then - texio.write_nl(format("%s %s: %s, group: %s, nodes: %s -> %s, string: %s",what,n,state,groupcode,before,after,reconstruct(head))) +function nodes.replace(head,current,new) + if current and next then + local p, n = current.prev, current.next + new.prev, new.next = p, n + if p then + p.next = new else - texio.write_nl(format("%s %s: %s, group: %s, nodes: %s -> %s",what,n,state,groupcode,before,after)) - end - end - - function nodes.processors.pre_linebreak_filter(head,groupcode) -- todo: tail - local first, found = first_character(head) - if found then - if callbacks.trace then - local before = nodes.count(head,true) - local head, tail, done = actions(head,slide(head)) - local after = nodes.count(head,true) - if done then - tracer("pre_linebreak","changed",head,groupcode,before,after,true) - else - tracer("pre_linebreak","unchanged",head,groupcode,before,after,true) - end - return (done and head) or true - else - local head, tail, done = actions(head,slide(head)) - return (done and head) or true - end - else - if callbacks.trace then - local n = nodes.count(head,false) - tracer("pre_linebreak","no chars",head,groupcode,n,n) - end - return true - end - end - - function nodes.processors.hpack_filter(head,groupcode) -- todo: tail - local first, found = first_character(head) - if found then - if callbacks.trace then - local before = nodes.count(head,true) - local head, tail, done = actions(head,slide(head)) - local after = nodes.count(head,true) - if done then - tracer("hpack","changed",head,groupcode,before,after,true) - else - tracer("hpack","unchanged",head,groupcode,before,after,true) - end - return (done and head) or true - else - local head, tail, done = actions(head,slide(head)) - return (done and head) or true - end - end - if callbacks.trace then - local n = nodes.count(head,false) - tracer("hpack","no chars",head,groupcode,n,n) - end - return true - end - -end - -callback.register('pre_linebreak_filter', nodes.processors.pre_linebreak_filter) -callback.register('hpack_filter' , nodes.processors.hpack_filter) - -do - - -- beware, some field names will change in a next release of luatex - - local expand = table.tohash { - "list", -- list_ptr & ins_ptr & adjust_ptr - "pre", -- - "post", -- - "spec", -- glue_ptr - "top_skip", -- - "attr", -- - "replace", -- nobreak - "components", -- lig_ptr - "box_left", -- - "box_right", -- - "glyph", -- margin_char - "leader", -- leader_ptr - "action", -- action_ptr - "value", -- user_defined nodes with subtype 'a' en 'n' - } - - -- page_insert: "height", "last_ins_ptr", "best_ins_ptr" - -- split_insert: "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins" - - local ignore = table.tohash { - "page_insert", - "split_insert", - "ref_count", - } - - local dimension = table.tohash { - "width", "height", "depth", "shift", - "stretch", "shrink", - "xoffset", "yoffset", - "surround", - "kern", - "box_left_width", "box_right_width" - } - - -- flat: don't use next, but indexes - -- verbose: also add type - -- can be sped up - - nodes.dimensionfields = dimension - nodes.listablefields = expand - nodes.ignorablefields = ignore - - -- not ok yet: - - function nodes.astable(n,sparse) -- not yet ok - local f, t = node.fields(n.id,n.subtype), { } - for i=1,#f do - local v = f[i] - local d = n[v] - if d then - if ignore[v] or v == "id" then - -- skip - elseif expand[v] then -- or: type(n[v]) ~= "string" or type(n[v]) ~= "number" or type(n[v]) ~= "table" - t[v] = "pointer to list" - elseif sparse then - if (type(d) == "number" and d ~= 0) or (type(d) == "string" and d ~= "") then - t[v] = d - end - else - t[v] = d - end - end - end - t.type = node.type(n.id) - return t - end - - local nodefields = node.fields - local nodetype = node.type - - -- under construction: - - local function totable(n,flat,verbose) - local function to_table(n) - local f = nodefields(n.id,n.subtype) - local tt = { } - for k=1,#f do - local v = f[k] - local nv = n[v] - if nv then - if ignore[v] then - -- skip - elseif expand[v] then - if type(nv) == "number" or type(nv) == "string" then - tt[v] = nv - else - tt[v] = totable(nv,flat,verbose) - end - elseif type(nv) == "table" then - tt[v] = nv -- totable(nv,flat,verbose) -- data - else - tt[v] = nv - end - end - end - if verbose then - tt.type = nodetype(tt.id) - end - return tt + head = new end if n then - if flat then - local t = { } - while n do - t[#t+1] = to_table(n) - n = n.next - end - return t - else - local t = to_table(n) - if n.next then - t.next = totable(n.next,flat,verbose) - end - return t - end - else - return { } + n.prev = new end + free_node(current) end + return head, current +end - nodes.totable = totable - - local function key(k) - return ((type(k) == "number") and "["..k.."]") or k - end - - -- not ok yet; this will become a module +-- will move - local function serialize(root,name,handle,depth,m) - handle = handle or print - if depth then - depth = depth .. " " - handle(("%s%s={"):format(depth,key(name))) - else - depth = "" - local tname = type(name) - if tname == "string" then - if name == "return" then - handle("return {") - else - handle(name .. "={") - end - elseif tname == "number"then - handle("[" .. name .. "]={") - else - handle("t={") - end - end - if root then - local fld - if root.id then - fld = nodefields(root.id,root.subtype) -- we can cache these (todo) +local function count(stack,flat) + local n = 0 + while stack do + local id = stack.id + if not flat and id == hlist or id == vlist then + local list = stack.list + if list then + n = n + 1 + count(list) -- self counts too else - fld = table.sortedkeys(root) - end - if type(root) == 'table' and root['type'] then -- userdata or table - handle(("%s %s=%q,"):format(depth,'type',root['type'])) - end - for _,k in ipairs(fld) do - if k == "ref_count" then - -- skip - elseif k then - local v = root[k] - local t = type(v) - if t == "number" then - if v == 0 then - -- skip - else - handle(("%s %s=%s,"):format(depth,key(k),v)) - end - elseif t == "string" then - if v == "" then - -- skip - else - handle(("%s %s=%q,"):format(depth,key(k),v)) - end - elseif v then -- userdata or table - serialize(v,k,handle,depth,m+1) - end - end - end - if root['next'] then -- userdata or table - serialize(root['next'],'next',handle,depth,m+1) + n = n + 1 end - end - if m and m > 0 then - handle(("%s},"):format(depth)) else - handle(("%s}"):format(depth)) + n = n + 1 end + stack = stack.next end - - function nodes.serialize(root,name) - local t = { } - local function flush(s) - t[#t+1] = s - end - serialize(root, name, flush, nil, 0) - return table.concat(t,"\n") - end - - function nodes.serializebox(n,flat,verbose) - return nodes.serialize(nodes.totable(tex.box[n],flat,verbose)) - -- return nodes.serialize(tex.box[n]) - end - - function nodes.visualizebox(...) - -- tex.sprint(tex.ctxcatcodes,"\\starttyping\n" .. nodes.serializebox(...) .. "\n\\stoptyping\n") - tex.print(tex.ctxcatcodes,"\\starttyping") - tex.print(nodes.serializebox(...)) - tex.print("\\stoptyping") - end - - function nodes.list(head,n) -- name might change to nodes.type - if not n then - tex.print(tex.ctxcatcodes,"\\starttyping") - end - while head do - local id = head.id - tex.print(string.rep(" ",n or 0) .. tostring(head) .. "\n") - if id == hlist or id == vlist then - nodes.list(head.list,(n or 0)+1) - end - head = head.next - end - if not n then - tex.print("\\stoptyping") - end - end - - function nodes.print(head,n) - while head do - local id = head.id - texio.write_nl(string.rep(" ",n or 0) .. tostring(head)) - if id == hlist or id == vlist then - nodes.print(head.list,(n or 0)+1) - end - head = head.next - end - end - - function nodes.check_for_leaks(sparse) - local l = { } - local q = node.usedlist() - for p in node.traverse(q) do - local s = table.serialize(nodes.astable(p,sparse),node.type(p.id)) - l[s] = (l[s] or 0) + 1 - end - node.flush_list(q) - for k, v in pairs(l) do - texio.write_nl(format("%s * %s", v, k)) - end - end - -end - -if not node.list_has_attribute then -- no longer needed - - function node.list_has_attribute(list,attribute) - if list and attribute then - for n in node.traverse(list) do - local a = has_attribute(n,attribute) - if a then return a end - end - end - return false - end - + return n end -function nodes.pack_list(head) - local t = { } - for n in node.traverse(head) do - t[#t+1] = tostring(n) - end - return t -end +nodes.count = count -do +-- new - function nodes.leftskip(n) - while n do - local id = n.id - if id == glue then - if n.subtype == 8 then -- 7 in c/web source - return (n.spec and n.spec.width) or 0 - else - return 0 - end - elseif id == whatsit then - n = n.next - elseif id == hlist then - return n.width - else - break - end - end - return 0 - end - function nodes.rightskip(n) - if n then - n = node.slide(n) - while n do - local id = n.id - if id == glue then - if n.subtype == 9 then -- 8 in the c/web source - return (n.spec and n.spec.width) or 0 - else - return 0 - end - elseif id == whatsit then - n = n.prev - else - break - end - end +function attributes.ofnode(n) + local a = n.attr + if a then + local names = attributes.names + a = a.next + while a do + local number, value = a.number, a.value + texio.write_nl(format("%s : attribute %3i, value %4i, name %s",tostring(n),number,value,names[number] or '?')) + a = a.next end - return false - end - + end end --- goodie --- --- if node.valid(tex.box[0]) then print("valid node") end - ---~ do ---~ local n = node.new(0,0) ---~ local m = getmetatable(n) ---~ m.__metatable = 'node' ---~ node.free(n) - ---~ function node.valid(n) ---~ return n and getmetatable(n) == 'node' ---~ end ---~ end +local left, space = lpeg.P("<"), lpeg.P(" ") --- for the moment we put this here: - -do - - nodes.tracers = { } - nodes.tracers.characters = { } - - local function collect(head,list,tag,n) - n = n or 0 - local ok, fn = false, nil - while head do - local id = head.id - if id == glyph then - local f = head.font - if f ~= fn then - ok, fn = false, f - end - local c = head.char - local d = tfmid[f].descriptions[c] - local i = (d and d.index) or -1 - if not ok then - ok = true - n = n + 1 - list[n] = list[n] or { } - list[n][tag] = { } - end - local l = list[n][tag] - l[#l+1] = { c, f, i } - elseif id == disc then - -- skip - else - ok = false - end - head = head.next - end - end - - function nodes.tracers.characters.equal(ta, tb) - if #ta ~= #tb then - return false - else - for i=1,#ta do - local a, b = ta[i], tb[i] - if a[1] ~= b[1] or a[2] ~= b[2] or a[3] ~= b[3] then - return false - end - end - end - return true - end - function nodes.tracers.characters.string(t) - local tt = { } - for i=1,#t do - tt[i] = utf.char(t[i][1]) - end - return table.concat(tt,"") - end - function nodes.tracers.characters.unicodes(t,decimal) - local tt = { } - for i=1,#t do - if decimal then - tt[i] = t[i][1] - else - tt[i] = format("%04X",t[i][1]) - end - end - return table.concat(tt," ") - end - function nodes.tracers.characters.indices(t,decimal) - local tt = { } - for i=1,#t do - if decimal then - tt[i] = t[i][3] - else - tt[i] = format("%04X",t[i][3]) - end - end - return table.concat(tt," ") - end - function nodes.tracers.characters.fonts(t) - local f = t[1] and t[1][2] - return (f and file.basename(tfmid[f].filename or "unknown")) or "unknown" - end - - function nodes.tracers.characters.start() - local npc = nodes.process_characters - local list = { } - function nodes.process_characters(head) - local n = #list - collect(head,list,'before',n) - local h, d = npc(head) - collect(head,list,'after',n) - if #list > n then - list[#list+1] = { } - end - return h, d - end - function nodes.tracers.characters.stop() - tracers.list['characters'] = list - lmx.set('title', 'ConTeXt Character Processing Information') - lmx.set('color-background-one', lmx.get('color-background-yellow')) - lmx.set('color-background-two', lmx.get('color-background-purple')) - lmx.show('context-characters.lmx') - lmx.restore() - nodes.process_characters = npc - end - end - - local stack = { } - - function nodes.tracers.start(tag) - stack[#stack+1] = tag - local tracer = nodes.tracers[tag] - if tracer and tracer.start then - tracer.start() - end - end - function nodes.tracers.stop() - local tracer = stack[#stack] - if tracer and tracer.stop then - tracer.stop() - end - stack[#stack] = nil - end - -end +nodes.filterkey = left * (1-left)^0 * left * space^0 * lpeg.C((1-space)^0) diff --git a/tex/context/base/node-ini.tex b/tex/context/base/node-ini.tex index c033a1f7b..210f21229 100644 --- a/tex/context/base/node-ini.tex +++ b/tex/context/base/node-ini.tex @@ -1,8 +1,8 @@ %D \module %D [ file=node-ini, %D version=2006.08.20, -%D title=\CONTEXT\ Character Macros, -%D subtitle=Node Support (Initialization), +%D title=\CONTEXT\ Node Macros, +%D subtitle=Initialization, %D author=Hans Hagen, %D date=\currentdate, %D copyright=PRAGMA] @@ -11,10 +11,59 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Node Support (initialization)} +\writestatus{loading}{ConTeXt Node Support / Initialization} + +\unprotect + +\newcount\filterstate \filterstate\plusone -\registerctxluafile{node-seq}{1.001} \registerctxluafile{node-ini}{1.001} +\registerctxluafile{node-tst}{1.001} +\registerctxluafile{node-tra}{1.001} % we might split it off (module) +\registerctxluafile{node-seq}{1.001} % we might generalize this one +\registerctxluafile{node-tsk}{1.001} +\registerctxluafile{node-tex}{1.001} +\registerctxluafile{node-res}{1.001} +\registerctxluafile{node-pro}{1.001} +\registerctxluafile{node-shp}{1.001} +\registerctxluafile{node-ser}{1.001} +\registerctxluafile{node-ext}{1.001} +\registerctxluafile{node-inj}{1.001} % we might split it off + +\newtoks \attributesresetlist + +\ifdefined \v!global \else \def\v!global{global} \fi % for metatex + +\def\defineattribute + {\dodoubleempty\dodefineattribute} + +\def\dodefineattribute[#1][#2]% alternatively we can let lua do the housekeeping + {\expandafter\newattribute\csname @attr@#1\endcsname + \expandafter \xdef\csname :attr:#1\endcsname{\number\lastallocatedattribute}% + \ctxlua{attributes.define("#1",\number\lastallocatedattribute)}% + %\writestatus\m!systems{defining attribute #1 with number \number\lastallocatedattribute}% + \doifnotinset\v!global{#2}{\appendetoks\csname @attr@#1\endcsname\attributeunsetvalue\to\attributesresetlist}} + +\def\definesystemattribute + {\dodoubleempty\dodefinesystemattribute} + +\def\dodefinesystemattribute[#1][#2]% alternatively we can let lua do the housekeeping + {\scratchcounter\ctxlua{tex.print(attributes.private("#1"))}\relax + \global\expandafter\attributedef\csname @attr@#1\endcsname\scratchcounter + \expandafter \xdef\csname :attr:#1\endcsname{\number\scratchcounter}% + %\writestatus\m!systems{defining system attribute #1 with number \number\scratchcounter}% + \doifnotinset\v!global{#2}{\appendetoks\csname @attr@#1\endcsname\attributeunsetvalue\to\attributesresetlist}} + +% expandable so we can \edef them for speed + +\def\dosetattribute#1#2{\csname @attr@#1\endcsname#2\relax} +\def\doresetattribute#1{\csname @attr@#1\endcsname\attributeunsetvalue} +\def\dogetattribute #1{\number\csname @attr@#1\endcsname} +\def\dogetattributeid#1{\csname :attr:#1\endcsname} + +\let\dompattribute\gobbletwoarguments + +\def\resetallattributes{\the\attributesresetlist} % \appendtoks % \ctxlua { @@ -54,4 +103,4 @@ % \stoptracingnodes % \stoptext -\endinput +\protect \endinput diff --git a/tex/context/base/node-inj.lua b/tex/context/base/node-inj.lua new file mode 100644 index 000000000..6ba21b39d --- /dev/null +++ b/tex/context/base/node-inj.lua @@ -0,0 +1,608 @@ +if not modules then modules = { } end modules ['node-inj'] = { + version = 1.001, + comment = "companion to node-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- tricky ... fonts.ids is not yet defined .. to be solved (maybe general tex ini) + +-- This is very experimental (this will change when we have luatex > .50 and +-- a few pending thingies are available. Also, Idris needs to make a few more +-- test fonts. + +local next = next + +local trace_injections = false trackers.register("nodes.injections", function(v) trace_injections = v end) + +fonts = fonts or { } +fonts.tfm = fonts.tfm or { } +fonts.ids = fonts.ids or { } + +local fontdata = fonts.ids + +local glyph = node.id('glyph') +local kern = node.id('kern') + +local traverse_id = node.traverse_id +local has_attribute = node.has_attribute +local set_attribute = node.set_attribute +local insert_node_before = node.insert_before +local insert_node_after = node.insert_after + +local newkern = nodes.kern + +local markbase = attributes.private('markbase') +local markmark = attributes.private('markmark') +local markdone = attributes.private('markdone') +local cursbase = attributes.private('cursbase') +local curscurs = attributes.private('curscurs') +local cursdone = attributes.private('cursdone') +local kernpair = attributes.private('kernpair') + +local cursives = { } +local marks = { } +local kerns = { } + +-- currently we do gpos/kern in a bit inofficial way but when we +-- have the extra fields in glyphnodes to manipulate ht/dp/wd +-- explicitly i will provide an alternative; also, we can share +-- tables + +function nodes.set_cursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) + local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2]) + local ws, wn = tfmstart.width, tfmnext.width + local bound = #cursives + 1 + set_attribute(start,cursbase,bound) + set_attribute(nxt,curscurs,bound) + cursives[bound] = { rlmode, dx, dy, ws, wn } + return dx, dy, bound +end + +function nodes.set_pair(current,factor,rlmode,spec,tfmchr) + local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4] + -- dy = y - h + if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then + local bound = has_attribute(current,kernpair) + if bound then + local kb = kerns[bound] + kb[2], kb[3], kb[4], kb[5] = kb[2] + x, kb[3] + y, kb[4] + w, kb[5] + h + else + bound = #kerns + 1 + set_attribute(current,kernpair,bound) + kerns[bound] = { rlmode, x, y, w, h } + end + return x, y, w, h, bound + end + return x, y, w, h -- no bound +end + +function nodes.set_kern(current,factor,rlmode,x,tfmchr) + local dx = factor*x + if dx ~= 0 then + local bound = #kerns + 1 + set_attribute(current,kernpair,bound) + kerns[bound] = { rlmode, dx } + end + return dx, bound +end + +function nodes.set_mark(start,base,factor,rlmode,ba,ma,index) --ba=baseanchor, ma=markanchor + local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) + local bound = has_attribute(base,markbase) + if bound then + local mb = marks[bound] + if mb then + if not index then index = #mb + 1 end + mb[index] = { dx, dy } + set_attribute(start,markmark,bound) + set_attribute(start,markdone,index) + return dx, dy, bound + else + logs.report("nodes mark", "possible problem, U+%04X is base without data (id: %s)",base.char,bound) + end + end + index = index or 1 + bound = #marks + 1 + set_attribute(base,markbase,bound) + set_attribute(start,markmark,bound) + set_attribute(start,markdone,index) + marks[bound] = { [index] = { dx, dy } } + return dx, dy, bound +end + +function nodes.trace_injection(head) + local function dir(n) + return (n<0 and "r-to-l") or (n>0 and "l-to-r") or ("unset") + end + local function report(...) + logs.report("nodes finisher",...) + end + report("begin run") + for n in traverse_id(glyph,head) do + if n.subtype < 256 then + local kp = has_attribute(n,kernpair) + local mb = has_attribute(n,markbase) + local mm = has_attribute(n,markmark) + local md = has_attribute(n,markdone) + local cb = has_attribute(n,cursbase) + local cc = has_attribute(n,curscurs) + report("char U+%05X, font=%s",n.char,n.font) + if kp then + local k = kerns[kp] + if k[3] then + report(" pairkern: dir=%s, x=%s, y=%s, w=%s, h=%s",dir(k[1]),k[2],k[3],k[4],k[5]) + else + report(" kern: dir=%s, dx=%s",dir(k[1]),k[2]) + end + end + if mb then + report(" markbase: bound=%s",mb) + end + if mm then + local m = marks[mm] + if mb then + local m = m[mb] + if m then + report(" markmark: bound=%s, index=%s, dx=%s, dy=%s",mm,j,m[1],m[2]) + else + report(" markmark: bound=%s, missing index",mm) + end + else + m = m[1] + report(" markmark: bound=%s, dx=%s, dy=%s",mm,m[1],m[2]) + end + end + if cb then + report(" cursbase: bound=%s",cb) + end + if cc then + local c = cursives[cc] + report(" curscurs: bound=%s, dir=%s, dx=%s, dy=%s",cc,dir(c[1]),c[2],c[3]) + end + end + end + report("end run") +end + +-- todo: reuse tables (i.e. no collection), but will be extra fields anyway + +function nodes.inject_kerns(head,tail,keep) + if trace_injections then + nodes.trace_injection(head) + end + local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns) + if has_marks or has_cursives then + -- in the future variant we will not copy items but refs to tables + local done, ky, rl, valid, cx, wx = false, { }, { }, { }, { }, { } + for n in traverse_id(glyph,head) do + if n.subtype < 256 then + valid[#valid+1] = n + if has_kerns then -- move outside loop + local k = has_attribute(n,kernpair) + if k then + local kk = kerns[k] + if kk then + local x, y, w, h = kk[2], kk[3], kk[4], kk[5] + local dy = y - h + if dy ~= 0 then + ky[n] = dy + end + if w ~= 0 or x ~= 0 then + wx[n] = kk + end + rl[n] = kk[1] -- could move in test + end + end + end + end + end + if #valid > 0 then + -- we can assume done == true because we have cursives and marks + local cx = { } + if has_kerns and next(ky) then + for n, k in next, ky do + n.yoffset = k + end + end + -- todo: reuse t and use maxt + if has_cursives then + local n_cursbase, n_curscurs, p_cursbase, n, p, nf, tm = nil, nil, nil, nil, nil, nil, nil + -- since we need valid[n+1] we can also use a "while true do" + local t, d, maxt = { }, { }, 0 + for i=1,#valid do -- valid == glyphs + n = valid[i] + if n.font ~= nf then + nf = n.font + tm = fontdata[nf].marks + -- maybe flush + maxt = 0 + end + if not tm[n.char] then + n_cursbase = has_attribute(n,cursbase) + n_curscurs = has_attribute(n,curscurs) + if p_cursbase then + if p_cursbase == n_curscurs then + local c = cursives[n_curscurs] + if c then + local rlmode, dx, dy, ws, wn = c[1], c[2], c[3], c[4], c[5] + if rlmode >= 0 then + dx = dx - ws + else + dx = dx + wn + end + if dx ~= 0 then + cx[n] = dx + rl[n] = rlmode + end + -- if rlmode and rlmode < 0 then + dy = -dy + -- end + maxt = maxt + 1 + t[maxt] = p + d[maxt] = dy + else + maxt = 0 + end + end + elseif maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + t[i].yoffset = t[i].yoffset + ny + end + maxt = 0 + end + if not n_cursbase and maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + t[i].yoffset = ny + end + maxt = 0 + end + p_cursbase, p = n_cursbase, n + end + end + if maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + t[i].yoffset = ny + end + maxt = 0 + end + if not keep then + cursives = { } + end + end + if has_marks then + local p_markbase, n_markmark = nil, nil + for i=1,#valid do + local p = valid[i] + p_markbase = has_attribute(p,markbase) + if p_markbase then + local mrks = marks[p_markbase] + for n in traverse_id(glyph,p.next) do + n_markmark = has_attribute(n,markmark) + if p_markbase == n_markmark then + local index = has_attribute(n,markdone) or 1 + local d = mrks[index] + if d then + -- local rlmode = d[3] -- not used + -- if rlmode and rlmode < 0 then + -- n.xoffset = p.xoffset + d[1] + -- else + n.xoffset = p.xoffset - d[1] + -- end + n.yoffset = p.yoffset + d[2] + end + else + break + end + end + end + end + if not keep then + marks = { } + end + end + -- todo : combine + if next(wx) then + for n, k in next, wx do + -- only w can be nil, can be sped up when w == nil + local rl, x, w = k[1], k[2] or 0, k[4] or 0 + local wx = w - x + if rl < 0 then + if wx ~= 0 then + insert_node_before(head,n,newkern(wx)) + end + if x ~= 0 then + insert_node_after (head,n,newkern(x)) + end + else + -- if wx ~= 0 then + -- insert_node_after(head,n,newkern(wx)) + -- end + if x ~= 0 then + insert_node_before(head,n,newkern(x)) + end + end + end + end + if next(cx) then + for n, k in next, cx do + if k ~= 0 then + local rln = rl[n] + if rln and rln < 0 then + insert_node_before(head,n,newkern(-k)) + else + insert_node_before(head,n,newkern(k)) + end + end + end + end + if not keep then + kerns = { } + end + return head, true + elseif not keep then + kerns, cursives, marks = { }, { }, { } + end + elseif has_kerns then + -- we assume done is true because there are kerns + for n in traverse_id(glyph,head) do + local k = has_attribute(n,kernpair) + if k then + local kk = kerns[k] + if kk then + -- only w can be nil, can be sped up when w == nil + local rl, x, y, w = kk[1], kk[2] or 0, kk[3] or 0, kk[4] or 0 + if y ~= 0 then + n.yoffset = y -- todo: h ? + end + local wx = w - x + if rl < 0 then + if wx ~= 0 then + insert_node_before(head,n,newkern(wx)) + end + if x ~= 0 then + insert_node_after (head,n,newkern(x)) + end + else + -- if wx ~= 0 then + -- insert_node_after(head,n,newkern(wx)) + -- end + if x ~= 0 then + insert_node_before(head,n,newkern(x)) + end + end + end + end + end + if not keep then + kerns = { } + end + return head, true + end + return head, false +end + +-- -- -- KEEP OLD ONE, THE NEXT IS JUST OPTIMIZED -- -- -- + +function nodes.XXXXXXXxinject_kerns(head,tail,keep) + if trace_injections then + nodes.trace_injection(head) + end + local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns) + if has_marks or has_cursives then + -- in the future variant we will not copy items but refs to tables + local done, ky, valid, cx, wx = false, { }, { }, { }, { } + for n in traverse_id(glyph,head) do + if n.subtype < 256 then + valid[#valid+1] = n + if has_kerns then -- move outside loop + local k = has_attribute(n,kernpair) + if k then + local kk = kerns[k] + if kk then + local x, y, w, h = kk[2], kk[3], kk[4], kk[5] + local dy = y - h + if dy ~= 0 then + ky[n] = dy + end + if w ~= 0 or x ~= 0 then + wx[n] = kk + end + end + end + end + end + end + if #valid > 0 then + -- we can assume done == true because we have cursives and marks + local cx = { } + if has_kerns and next(ky) then + for n, k in next, ky do + n.yoffset = k + end + end + -- todo: reuse t and use maxt + if has_cursives then + local n_cursbase, n_curscurs, p_cursbase, n, p, nf, tm = nil, nil, nil, nil, nil, nil, nil + -- since we need valid[n+1] we can also use a "while true do" + local t, d, maxt = { }, { }, 0 + for i=1,#valid do -- valid == glyphs + n = valid[i] + if n.font ~= nf then + nf = n.font + tm = fontdata[nf].marks + -- maybe flush + maxt = 0 + end + if not tm[n.char] then + n_cursbase = has_attribute(n,cursbase) + n_curscurs = has_attribute(n,curscurs) + if p_cursbase then + if p_cursbase == n_curscurs then + local c = cursives[n_curscurs] + if c then + local rlmode, dx, dy, ws, wn = c[1], c[2], c[3], c[4], c[5] + if rlmode >= 0 then + dx = dx - ws + else + dx = dx + wn + end + if dx ~= 0 then +if rlmode < 0 then + cx[n] = -dx +else + cx[n] = dx +end + end + -- if rlmode and rlmode < 0 then + dy = -dy + -- end + maxt = maxt + 1 + t[maxt] = p + d[maxt] = dy + else + maxt = 0 + end + end + elseif maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + t[i].yoffset = t[i].yoffset + ny + end + maxt = 0 + end + if not n_cursbase and maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + t[i].yoffset = ny + end + maxt = 0 + end + p_cursbase, p = n_cursbase, n + end + end + if maxt > 0 then + local ny = n.yoffset + for i=maxt,1,-1 do + ny = ny + d[i] + t[i].yoffset = ny + end + maxt = 0 + end + if not keep then + cursives = { } + end + end + if has_marks then + local p_markbase, n_markmark = nil, nil + for i=1,#valid do + local p = valid[i] + p_markbase = has_attribute(p,markbase) + if p_markbase then + local mrks = marks[p_markbase] + for n in traverse_id(glyph,p.next) do + n_markmark = has_attribute(n,markmark) + if p_markbase == n_markmark then + local index = has_attribute(n,markdone) or 1 + local d = mrks[index] + if d then + local d1, d2 = d[1], d[2] + if d1 ~= 0 then + n.xoffset = p.xoffset - d[1] + end + if d2 ~= 0 then + n.yoffset = p.yoffset + d[2] + end + end + else + break + end + end + end + end + if not keep then + marks = { } + end + end + -- todo : combine + if next(wx) then + for n, k in next, wx do + -- only w can be nil, can be sped up when w == nil + local rl, x, w = k[1], k[2] or 0, k[4] or 0 + local wx = w - x + if rl < 0 then + if wx ~= 0 then + insert_node_before(head,n,newkern(wx)) + end + if x ~= 0 then + insert_node_after (head,n,newkern(x)) + end + else + -- if wx ~= 0 then + -- insert_node_after(head,n,newkern(wx)) + -- end + if x ~= 0 then + insert_node_before(head,n,newkern(x)) + end + end + end + end + if next(cx) then + for n, k in next, cx do + insert_node_before(head,n,newkern(k)) + end + end + if not keep then + kerns = { } + end + return head, true + elseif not keep then + kerns, cursives, marks = { }, { }, { } + end + elseif has_kerns then + -- we assume done is true because there are kerns + for n in traverse_id(glyph,head) do + local k = has_attribute(n,kernpair) + if k then + local kk = kerns[k] + if kk then + -- only w can be nil, can be sped up when w == nil + local rl, x, y, w = kk[1], kk[2] or 0, kk[3] or 0, kk[4] or 0 + if y ~= 0 then + n.yoffset = y -- todo: h ? + end + local wx = w - x + if rl < 0 then + if wx ~= 0 then + insert_node_before(head,n,newkern(wx)) + end + if x ~= 0 then + insert_node_after (head,n,newkern(x)) + end + else + -- if wx ~= 0 then + -- insert_node_after(head,n,newkern(wx)) + -- end + if x ~= 0 then + insert_node_before(head,n,newkern(x)) + end + end + end + end + end + if not keep then + kerns = { } + end + return head, true + end + return head, false +end diff --git a/tex/context/base/node-par.lua b/tex/context/base/node-par.lua index 7dd95ea5d..f275a1035 100644 --- a/tex/context/base/node-par.lua +++ b/tex/context/base/node-par.lua @@ -11,7 +11,7 @@ parbuilders.constructors = parbuilders.constructors or { } parbuilders.names = parbuilders.names or { } parbuilders.attribute = attributes.numbers['parbuilder'] or 999 -input.storage.register(false, "parbuilders.names", parbuilders.names, "parbuilders.names") +storage.register("parbuilders.names", parbuilders.names, "parbuilders.names") -- store parbuilders.names diff --git a/tex/context/base/node-par.tex b/tex/context/base/node-par.tex index 2e628c066..7f7ca9977 100644 --- a/tex/context/base/node-par.tex +++ b/tex/context/base/node-par.tex @@ -1,5 +1,5 @@ %D \module -%D [ file=core-spa, +%D [ file=node-par, %D version=2008.09.30, %D title=\CONTEXT\ Node Macros, %D subtitle=Paragraph Building, @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Context Core Macros / Paragraph Building} +\writestatus{loading}{ConTeXt Node Macros / Paragraph Building} %D This is very experimental, undocumented, subjected to changes, etc. just as %D the underlying interfaces. @@ -30,7 +30,7 @@ \registerctxluafile{node-par}{1.001} -\defineattribute[parbuilder] +\definesystemattribute[parbuilder] \newcount\nofparbuilders diff --git a/tex/context/base/node-pro.lua b/tex/context/base/node-pro.lua new file mode 100644 index 000000000..575941fe5 --- /dev/null +++ b/tex/context/base/node-pro.lua @@ -0,0 +1,155 @@ +if not modules then modules = { } end modules ['node-pro'] = { + version = 1.001, + comment = "companion to node-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local utf = unicode.utf8 +local format, concat = string.format, table.concat + +local trace_callbacks = false trackers.register("nodes.callbacks", function(v) trace_callbacks = v end) + +local hlist = node.id('hlist') +local vlist = node.id('vlist') +local glyph = node.id('glyph') +local disc = node.id('disc') +local mark = node.id('mark') + +local slide_nodes = node.slide +local free_node = node.free +local first_character = node.first_character + +nodes.processors = nodes.processors or { } + +-- vbox: grouptype: vbox vtop output split_off split_keep | box_type: exactly|aditional +-- hbox: grouptype: hbox adjusted_hbox(=hbox_in_vmode) | box_type: exactly|aditional + +lists = lists or { } +chars = chars or { } +words = words or { } -- not used yet + +-- or just: +-- +-- nodes.process_page = tasks.actions("shipouts") + +local actions = tasks.actions("processors") + +local n = 0 + +local function reconstruct(head) + local t = { } + local h = head + while h do + local id = h.id + if id == glyph then + t[#t+1] = utf.char(h.char) + else + t[#t+1] = "[]" + end + h = h.next + end + return concat(t) +end + +local function tracer(what,state,head,groupcode,before,after,show) + if not groupcode then + groupcode = "unknown" + elseif groupcode == "" then + groupcode = "mvl" + end + n = n + 1 + if show then + texio.write_nl(format("%s %s: %s, group: %s, nodes: %s -> %s, string: %s",what,n,state,groupcode,before,after,reconstruct(head))) + else + texio.write_nl(format("%s %s: %s, group: %s, nodes: %s -> %s",what,n,state,groupcode,before,after)) + end +end + +nodes.processors.enabled = true -- thsi will become a proper state (like trackers) + +function nodes.processors.pre_linebreak_filter(head,groupcode) -- todo: tail + local first, found = first_character(head) + if found then + if trace_callbacks then + local before = nodes.count(head,true) + local head, tail, done = actions(head,slide_nodes(head)) + local after = nodes.count(head,true) + if done then + tracer("pre_linebreak","changed",head,groupcode,before,after,true) + else + tracer("pre_linebreak","unchanged",head,groupcode,before,after,true) + end + return (done and head) or true + else + local head, tail, done = actions(head,slide_nodes(head)) + return (done and head) or true + end + elseif trace_callbacks then + local n = nodes.count(head,false) + tracer("pre_linebreak","no chars",head,groupcode,n,n) + end + return true +end + +function nodes.processors.hpack_filter(head,groupcode) -- todo: tail + local first, found = first_character(head) + if found then + if trace_callbacks then + local before = nodes.count(head,true) + local head, tail, done = actions(head,slide_nodes(head)) + local after = nodes.count(head,true) + if done then + tracer("hpack","changed",head,groupcode,before,after,true) + else + tracer("hpack","unchanged",head,groupcode,before,after,true) + end + return (done and head) or true + else + local head, tail, done = actions(head,slide_nodes(head)) + return (done and head) or true + end + elseif trace_callbacks then + local n = nodes.count(head,false) + tracer("hpack","no chars",head,groupcode,n,n) + end + return true +end + +callback.register('pre_linebreak_filter', nodes.processors.pre_linebreak_filter) +callback.register('hpack_filter' , nodes.processors.hpack_filter) + +local actions = tasks.actions("finalizers") + +function nodes.processors.post_linebreak_filter(head,groupcode) -- todo: tail + local first, found = first_character(head) + if found then + if trace_callbacks then + local before = nodes.count(head,true) + local head, tail, done = actions(head,slide_nodes(head)) + local after = nodes.count(head,true) + if done then + tracer("finalizer","changed",head,groupcode,before,after,true) + else + tracer("finalizer","unchanged",head,groupcode,before,after,true) + end + return (done and head) or true + else + local head, tail, done = actions(head,slide_nodes(head)) + return (done and head) or true + end + elseif trace_callbacks then + local n = nodes.count(head,false) + tracer("finalizer","no chars",head,groupcode,n,n) + end + return true +end + +callback.register('post_linebreak_filter', nodes.processors.post_linebreak_filter) + +statistics.register("h-node processing time", function() + if statistics.elapsedindeed(nodes) then + return format("%s seconds including kernel", statistics.elapsedtime(nodes)) + end +end) diff --git a/tex/context/base/node-res.lua b/tex/context/base/node-res.lua new file mode 100644 index 000000000..c8d815be4 --- /dev/null +++ b/tex/context/base/node-res.lua @@ -0,0 +1,110 @@ +if not modules then modules = { } end modules ['node-res'] = { + version = 1.001, + comment = "companion to node-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local gmatch, format = string.gmatch, string.format +local copy_node, free_node, new_node = node.copy, node.free, node.new + +--[[ldx-- +The next function is not that much needed but in
Here we implement a mechanism for chaining the special functions
+that we use in
This is rather experimental. We need more control and some of this +might become a runtime module instead.
+--ldx]]-- + +local utf = unicode.utf8 +local format, match, concat, utfchar = string.format, string.match, table.concat, utf.char + +local ctxcatcodes = tex.ctxcatcodes + +fonts = fonts or { } +fonts.tfm = fonts.tfm or { } +fonts.ids = fonts.ids or { } + +nodes = nodes or { } +nodes.tracers = nodes.tracers or { } +nodes.tracers.characters = nodes.tracers.characters or { } +nodes.tracers.steppers = nodes.tracers.steppers or { } + +local glyph = node.id('glyph') +local disc = node.id('disc') +local glue = node.id('glue') +local kern = node.id('kern') +local whatsit = node.id('whatsit') + +local copy_node_list = node.copy_list +local hpack_node_list = node.hpack +local free_node_list = node.flush_list +local first_character = node.first_character +local node_type = node.type +local traverse_nodes = node.traverse + +local texsprint = tex.sprint +local fontdata = fonts.ids + +function nodes.tracers.characters.collect(head,list,tag,n) + n = n or 0 + local ok, fn = false, nil + while head do + local id = head.id + if id == glyph then + local f = head.font + if f ~= fn then + ok, fn = false, f + end + local c = head.char + local i = fontdata[f].indices[c] or 0 + if not ok then + ok = true + n = n + 1 + list[n] = list[n] or { } + list[n][tag] = { } + end + local l = list[n][tag] + l[#l+1] = { c, f, i } + elseif id == disc then + -- skip + else + ok = false + end + head = head.next + end +end + +function nodes.tracers.characters.equal(ta, tb) + if #ta ~= #tb then + return false + else + for i=1,#ta do + local a, b = ta[i], tb[i] + if a[1] ~= b[1] or a[2] ~= b[2] or a[3] ~= b[3] then + return false + end + end + end + return true +end + +function nodes.tracers.characters.string(t) + local tt = { } + for i=1,#t do + tt[i] = utfchar(t[i][1]) + end + return concat(tt,"") +end + +function nodes.tracers.characters.unicodes(t,decimal) + local tt = { } + for i=1,#t do + local n = t[i][1] + if n == 0 then + tt[i] = "-" + elseif decimal then + tt[i] = n + else + tt[i] = format("U+%04X",n) + end + end + return concat(tt," ") +end + +function nodes.tracers.characters.indices(t,decimal) + local tt = { } + for i=1,#t do + local n = t[i][3] + if n == 0 then + tt[i] = "-" + elseif decimal then + tt[i] = n + else + tt[i] = format("U+%04X",n) + end + end + return concat(tt," ") +end + +function nodes.tracers.characters.start() + local npc = nodes.process_characters + local list = { } + function nodes.process_characters(head) + local n = #list + nodes.tracers.characters.collect(head,list,'before',n) + local h, d = npc(head) + nodes.tracers.characters.collect(head,list,'after',n) + if #list > n then + list[#list+1] = { } + end + return h, d + end + function nodes.tracers.characters.stop() + tracers.list['characters'] = list + lmx.set('title', 'ConTeXt Character Processing Information') + lmx.set('color-background-one', lmx.get('color-background-yellow')) + lmx.set('color-background-two', lmx.get('color-background-purple')) + lmx.show('context-characters.lmx') + lmx.restore() + nodes.process_characters = npc + tasks.restart("processors", "characters") + end + tasks.restart("processors", "characters") +end + +local stack = { } + +function nodes.tracers.start(tag) + stack[#stack+1] = tag + local tracer = nodes.tracers[tag] + if tracer and tracer.start then + tracer.start() + end +end +function nodes.tracers.stop() + local tracer = stack[#stack] + if tracer and tracer.stop then + tracer.stop() + end + stack[#stack] = nil +end + +-- experimental + +local collection, collecting, messages = { }, false, { } + +function nodes.tracers.steppers.start() + collecting = true +end + +function nodes.tracers.steppers.stop() + collecting = false +end + +function nodes.tracers.steppers.reset() + for i=1,#collection do + local c = collection[i] + if c then + free_node_list(c) + end + end + collection, messages = { }, { } +end + +function nodes.tracers.steppers.nofsteps() + return tex.write(#collection) +end + +function nodes.tracers.steppers.glyphs(n,i) + local c = collection[i] + if c then + tex.box[n] = hpack_node_list(copy_node_list(c)) + end +end + +function nodes.tracers.steppers.features() +-- local f = first_character(collection[1]) +-- if f then -- something fishy with first_character + local f = collection[1] + while f do + if f.id == glyph then + local tfmdata, t = fontdata[f.font], { } + for feature, value in table.sortedpairs(tfmdata.shared.features) do + if feature == "number" or feature == "features" then + -- private + elseif type(value) == "boolean" then + if value then + t[#t+1] = format("%s=yes",feature) + else + -- skip + end + else + t[#t+1] = format("%s=%s",feature,value) + end + end + if #t > 0 then + texsprint(ctxcatcodes,concat(t,", ")) + else + texsprint(ctxcatcodes,"no features") + end + return + end + f = f.next + end +end + +function nodes.tracers.fontchar(font,char) + local n = nodes.glyph() + n.font, n.char, n.subtype = font, char, 256 + node.write(n) +end + +function nodes.tracers.steppers.codes(i,command) + local c = collection[i] + while c do + local id = c.id + if id == glyph then + if command then + texsprint(ctxcatcodes,format("%s{%s}{%s}",command,c.font,c.char)) + else + texsprint(ctxcatcodes,format("[%s:U+%04X]",c.font,c.char)) + end + elseif id == whatsit and (c.subtype == 6 or c.subtype == 7) then + texsprint(ctxcatcodes,format("[%s]",c.dir)) + else + texsprint(ctxcatcodes,format("[%s]",node_type(id))) + end + c = c.next + end +end + +function nodes.tracers.steppers.messages(i,command,split) + local list = messages[i] -- or { "no messages" } + if list then + for i=1,#list do + local l = list[i] + if split then + local a, b = match(l,"^(.-)%s*:%s*(.*)$") + texsprint(ctxcatcodes,format("%s{%s}{%s}",command,a or l,b or "")) + else + texsprint(ctxcatcodes,format("%s{%s}",command,l)) + end + end + end +end + +-- hooks into the node list processor (see otf) + +function nodes.tracers.steppers.check(head) + if collecting then + nodes.tracers.steppers.reset() + local n = copy_node_list(head) + nodes.inject_kerns(n,nil,true) + nodes.protect_glyphs(n) -- can be option + collection[1] = n + end +end + +function nodes.tracers.steppers.register(head) + if collecting then + local nc = #collection+1 + if messages[nc] then + local n = copy_node_list(head) + nodes.inject_kerns(n,nil,true) + nodes.protect_glyphs(n) -- can be option + collection[nc] = n + end + end +end + +function nodes.tracers.steppers.message(str,...) + str = format(str,...) + if collecting then + local n = #collection + 1 + local m = messages[n] + if not m then m = { } messages[n] = m end + m[#m+1] = str + end + return str -- saves an intermediate var in the caller +end + +-- this will be reorganized: + +function nodes.show_list(head, message) + if message then + texio.write_nl(message) + end + for n in traverse(head) do + texio.write_nl(tostring(n)) + end +end + +function nodes.check_glyphs(head,message) + local t = { } + for g in traverse_id(glyph,head) do + t[#t+1] = format("U+%04X:%s",g.char,g.subtype) + end + if #t > 0 then + logs.report(message or "nodes","%s glyphs: %s",#t,concat(t," ")) + end + return false +end + +function nodes.tosequence(start,stop) + if start then + local t = { } + while start do + if start.id == glyph then + t[#t+1] = format("U+%04X:%s",start.char,utfchar(start.char)) + else + t[#t+1] = match(tostring(start),": (%S+)") + end + if start == stop then + break + else + start = start.next + end + end + return concat(t," ") + else + return "Regimes take care of converting the input characters into
We will hook regime handling code into the input methods.
--ldx]]-- -input = input or { } -input.filters = input.filters or { } - function regimes.number(n) if type(n) == "string" then return tonumber(n,16) else return n end end -function regimes.define(c) -- is this used at all? - local r, u, s = c.regime, c.unicodeslot, c.slot - regimes.data[r] = regimes.data[r] or { } - if s then - if u then - regimes.data[r][regimes.number(s)] = regimes.number(u) - else - regimes.data[r][regimes.number(s)] = 0 - end - else - logs.report("regime","unknown vector %s/%s",r,s) -- ctx.statusmessage - end +function regimes.setsynonym(synonym,target) + regimes.synonyms[synonym] = target +end + +function regimes.truename(regime) + texsprint(ctxcatcodes,(regime and regimes.synonyms[synonym] or regime) or regimes.currentregime) end function regimes.load(regime) - environment.loadluafile("regi-"..regime, 1.001) - if regimes.data[regime] then - regimes.utf[regime] = { } - for k,v in pairs(regimes.data[regime]) do - regimes.utf[regime][char(k)] = utfchar(v) + regime = regimes.synonyms[regime] or regime + if not regimes.data[regime] then + environment.loadluafile("regi-"..regime, 1.001) + if regimes.data[regime] then + regimes.utf[regime] = { } + for k,v in pairs(regimes.data[regime]) do + regimes.utf[regime][char(k)] = utfchar(v) + end end end end function regimes.translate(line,regime) + regime = regimes.synonyms[regime] or regime if regime and line then local rur = regimes.utf[regime] if rur then @@ -69,44 +70,19 @@ function regimes.translate(line,regime) end function regimes.enable(regime) + regime = regimes.synonyms[regime] or regime if regimes.data[regime] then regimes.currentregime = regime local translate = regimes.translate - input.filters.dynamic_translator = function(s) + resolvers.install_text_filter('input',function(s) return translate(s,regime) - end + end) else regimes.disable() end end function regimes.disable() - regimes.currentregime = "" - input.filters.dynamic_translator = nil -end - -function input.filters.frozen_translator(regime) - return function(s) - return regimes.translate(s,regime) - end -end - ---[[ldx-- -The following code is rather
The following code is rather
This module implements a couple of cleanup methods. We need these
-in order to meet the
a paragraph of text
+another paragraph of text
+a paragraph of text
+another paragraph of text
+This code is experimental.
+This code is experimental and needs a cleanup. The visualizers will move to +a module.
--ldx]]-- - -- 1 = command, 2 = modifier (char), 3 = controlsequence id -- -- callback.register('token_filter', token.get_next) @@ -196,14 +197,13 @@ function collectors.trace() end collectors.show_methods.a = function(data) -- no need to store the table, just pass directly - local ct = tex.ctxcatcodes local template = "\\NC %s\\NC %s\\NC %s\\NC %s\\NC %s\\NC\\NR " - texsprint(ct, "\\starttabulate[|T|Tr|cT|Tr|T|]") - texsprint(ct, template:format("cmd","chr","","id","name")) - texsprint(ct, "\\HL") + texsprint(ctxcatcodes, "\\starttabulate[|T|Tr|cT|Tr|T|]") + texsprint(ctxcatcodes, format(template,"cmd","chr","","id","name")) + texsprint(ctxcatcodes, "\\HL") for _,v in pairs(data) do local cmd, chr, id, cs, sym = v[1], v[2], v[3], "", "" - local name = (token.command_name(v) or ""):gsub("_","\\_") + local name = gsub(token.command_name(v) or "","_","\\_") if id > 0 then cs = token.csname_name(v) or "" if cs ~= "" then cs = "\\string " .. cs end @@ -214,27 +214,26 @@ collectors.show_methods.a = function(data) -- no need to store the table, just p sym = "\\char " .. chr end if tonumber(chr) < 0 then - texsprint(ct, template:format(name, "", sym, id, cs)) + texsprint(ctxcatcodes, format(template, name, "", sym, id, cs)) else - texsprint(ct, template:format(name, chr, sym, id, cs)) + texsprint(ctxcatcodes, format(template, name, chr, sym, id, cs)) end end - texsprint(ct, "\\stoptabulate") + texsprint(ctxcatcodes, "\\stoptabulate") end collectors.show_methods.b_c = function(data,swap) -- no need to store the table, just pass directly - local ct = tex.ctxcatcodes local template = "\\NC %s\\NC %s\\NC %s\\NC\\NR" if swap then - texsprint(ct, "\\starttabulate[|Tl|Tl|Tr|]") + texsprint(ctxcatcodes, "\\starttabulate[|Tl|Tl|Tr|]") else - texsprint(ct, "\\starttabulate[|Tl|Tr|Tl|]") + texsprint(ctxcatcodes, "\\starttabulate[|Tl|Tr|Tl|]") end - texsprint(ct, template:format("cmd","chr","name")) - texsprint(ct, "\\HL") + texsprint(ctxcatcodes, format(template,"cmd","chr","name")) + texsprint(ctxcatcodes, "\\HL") for _,v in pairs(data) do local cmd, chr, id, cs, sym = v[1], v[2], v[3], "", "" - local name = (token.command_name(v) or ""):gsub("_","\\_") + local name = gsub(token.command_name(v) or "","_","\\_") if id > 0 then cs = token.csname_name(v) or "" end @@ -248,14 +247,14 @@ collectors.show_methods.b_c = function(data,swap) -- no need to store the table, end end if swap then - texsprint(ct, template:format(name, sym, chr)) + texsprint(ctxcatcodes, format(template, name, sym, chr)) elseif tonumber(chr) < 0 then - texsprint(ct, template:format(name, "", sym)) + texsprint(ctxcatcodes, format(template, name, "", sym)) else - texsprint(ct, template:format(name, chr, sym)) + texsprint(ctxcatcodes, format(template, name, chr, sym)) end end - texsprint(ct, "\\stoptabulate") + texsprint(ctxcatcodes, "\\stoptabulate") end -- Even more experimental ... diff --git a/tex/context/base/toks-ini.tex b/tex/context/base/toks-ini.tex index 90311eb13..932c05f32 100644 --- a/tex/context/base/toks-ini.tex +++ b/tex/context/base/toks-ini.tex @@ -1,8 +1,8 @@ %D \module %D [ file=toks-ini, %D version=2007.03.03, -%D title=\CONTEXT\ Character Macros, -%D subtitle=Token Support (Initialization), +%D title=\CONTEXT\ Token Support, +%D subtitle=Initialization, %D author=Hans Hagen, %D date=\currentdate, %D copyright=PRAGMA] @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\writestatus{loading}{Token Support (initialization)} +\writestatus{loading}{ConTeXt Token Support / Initialization} \registerctxluafile{toks-ini}{1.001} diff --git a/tex/context/base/trac-deb.lua b/tex/context/base/trac-deb.lua new file mode 100644 index 000000000..f476169c3 --- /dev/null +++ b/tex/context/base/trac-deb.lua @@ -0,0 +1,206 @@ +if not modules then modules = { } end modules ['trac-deb'] = { + version = 1.001, + comment = "companion to luat-lib.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if not lmx then lmx = { } end +if not lmx.variables then lmx.variables = { } end + +lmx.variables['color-background-green'] = '#4F6F6F' +lmx.variables['color-background-blue'] = '#6F6F8F' +lmx.variables['color-background-yellow'] = '#8F8F6F' +lmx.variables['color-background-purple'] = '#8F6F8F' + +lmx.variables['color-background-body'] = '#808080' +lmx.variables['color-background-main'] = '#3F3F3F' +lmx.variables['color-background-one'] = lmx.variables['color-background-green'] +lmx.variables['color-background-two'] = lmx.variables['color-background-blue'] + +lmx.variables['title-default'] = 'ConTeXt Status Information' +lmx.variables['title'] = lmx.variables['title-default'] + +lmx.htmfile = function(name) return environment.jobname .. "-status.html" end +lmx.lmxfile = function(name) return resolvers.find_file(name,'tex') end + +if not tracers then tracers = { } end +if not tracers.list then tracers.list = { } end +if not tracers.strings then tracers.strings = { } end + +tracers.strings.undefined = "undefined" + +function tracers.split(csname) + return csname:match("^(.+):(.+)$") +end + +function tracers.type(csname) + tag, name = tracers.split(csname) + if tag then return tag else return nil end +end + +function tracers.name(csname) + tag, name = tracers.split(csname) + if tag then return name else return csname end +end + +function tracers.cs(csname) + tag, name = tracers.split(csname) + if tracers.types[tag] then + return tracers.types[tag](name) + else + return tracers.primitive(csname) + end +end + +function tracers.dimen(name) + return (tex.dimen[name] and number.topoints(tex.dimen[name])) or tracers.strings.undefined +end + +function tracers.count(name) + return tex.count[name] or tracers.strings.undefined +end + +function tracers.toks(name) + return (tex.toks[name] and string.limit(tex.toks[name],40)) or tracers.strings.undefined +end + +function tracers.primitive(name) + return tex[name] or tracers.strings.undefined +end + +tracers.types = { + ['d'] = tracers.dimen, + ['c'] = tracers.count, + ['t'] = tracers.toks, + ['p'] = tracers.primitive +} + +function tracers.knownlist(name) + return tracers.list[name] and #tracers.list[name] > 0 +end + +function tracers.showdebuginfo() + lmx.set('title', 'ConTeXt Debug Information') + lmx.set('color-background-one', lmx.get('color-background-green')) + lmx.set('color-background-two', lmx.get('color-background-blue')) + lmx.show('context-debug.lmx') + lmx.restore() +end + +function tracers.showerror() + lmx.set('title', 'ConTeXt Error Information') + lmx.set('errormessage', status.lasterrorstring) + lmx.set('linenumber', status.linenumber) + lmx.set('color-background-one', lmx.get('color-background-yellow')) + lmx.set('color-background-two', lmx.get('color-background-purple')) + local filename = status.filename + local linenumber = tonumber(status.linenumber or "0") + if not filename then + lmx.set('filename', 'unknown') + lmx.set('errorcontext', 'error in filename') + elseif type(filename) == "number" then + lmx.set('filename', "This is a prelude to a more extensive logging module. For the sake
+of parsing log files, in addition to the standard logging we will
+provide an
This looks pretty ugly but we need to speed things up a bit.
+--ldx]]-- + +logs.moreinfo = [[ +more information about ConTeXt and the tools that come with it can be found at: + +maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context +webpage : http://www.pragma-ade.nl / http://tex.aanhet.net +wiki : http://contextgarden.net +]] + +logs.levels = { + ['error'] = 1, + ['warning'] = 2, + ['info'] = 3, + ['debug'] = 4, +} + +logs.functions = { + 'report', 'start', 'stop', 'push', 'pop', 'line', 'direct', + 'start_run', 'stop_run', + 'start_page_number', 'stop_page_number', + 'report_output_pages', 'report_output_log', + 'report_tex_stat', 'report_job_stat', + 'show_open', 'show_close', 'show_load', +} + +logs.tracers = { +} + +logs.level = 0 +logs.mode = string.lower((os.getenv("MTX.LOG.MODE") or os.getenv("MTX_LOG_MODE") or "tex")) + +function logs.set_level(level) + logs.level = logs.levels[level] or level +end + +function logs.set_method(method) + for _, v in next, logs.functions do + logs[v] = logs[method][v] or function() end + end +end + +-- tex logging + +function logs.tex.report(category,fmt,...) -- new + if fmt then + write_nl(category .. " | " .. format(fmt,...)) + else + write_nl(category .. " |") + end +end + +function logs.tex.line(fmt,...) -- new + if fmt then + write_nl(format(fmt,...)) + else + write_nl("") + end +end + +local texcount = tex and tex.count + +function logs.tex.start_page_number() + local real, user, sub = texcount[0], texcount[1], texcount[2] + if real > 0 then + if user > 0 then + if sub > 0 then + write(format("[%s.%s.%s",real,user,sub)) + else + write(format("[%s.%s",real,user)) + end + else + write(format("[%s",real)) + end + else + write("[-") + end +end + +function logs.tex.stop_page_number() + write("]") +end + +logs.tex.report_job_stat = statistics.show_job_stat + +-- xml logging + +function logs.xml.report(category,fmt,...) -- new + if fmt then + write_nl(format("