summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Carlisle <d.p.carlisle@gmail.com>2015-11-21 21:45:03 +0000
committerDavid Carlisle <d.p.carlisle@gmail.com>2015-11-21 21:45:03 +0000
commitb46f13e0949e310b46e97e791fd4e9e38635a491 (patch)
tree415c893c82e7f55ed630a10033885f1b5781ab56
parenta472b6fd5461318ec1e5d2899af2ef57f0fb14e8 (diff)
parent6c3d70e21b100c6c7ffa11e1c6d9260b32ccc781 (diff)
downloadluaotfload-b46f13e0949e310b46e97e791fd4e9e38635a491.tar.gz
for 2.6
-rw-r--r--NEWS3
-rw-r--r--doc/Makefile4
-rw-r--r--doc/filegraph.dot175
-rw-r--r--doc/luaotfload-context.tex485
-rw-r--r--doc/luaotfload-latex.tex93
-rw-r--r--doc/luaotfload-main.tex282
-rw-r--r--doc/luaotfload.conf.rst25
-rwxr-xr-xscripts/mkcharacters12
-rwxr-xr-xscripts/mkglyphlist2
-rwxr-xr-xscripts/mkimport37
-rwxr-xr-xscripts/mktests67
-rw-r--r--src/fontloader/misc/fontloader-basics-nod.lua3
-rw-r--r--src/fontloader/misc/fontloader-font-afm.lua18
-rw-r--r--src/fontloader/misc/fontloader-font-con.lua122
-rw-r--r--src/fontloader/misc/fontloader-font-def.lua6
-rw-r--r--src/fontloader/misc/fontloader-font-map.lua473
-rw-r--r--src/fontloader/misc/fontloader-font-otb.lua10
-rw-r--r--src/fontloader/misc/fontloader-font-otf.lua609
-rw-r--r--src/fontloader/misc/fontloader-font-otp.lua5
-rw-r--r--src/fontloader/misc/fontloader-font-tfm.lua44
-rw-r--r--src/fontloader/misc/fontloader-fonts-cbk.lua33
-rw-r--r--src/fontloader/misc/fontloader-fonts-demo-vf-1.lua8
-rw-r--r--src/fontloader/misc/fontloader-fonts-inj.lua375
-rw-r--r--src/fontloader/misc/fontloader-fonts-otn.lua2163
-rw-r--r--src/fontloader/misc/fontloader-fonts.lua18
-rw-r--r--src/fontloader/misc/fontloader-l-lpeg.lua2
-rw-r--r--src/fontloader/misc/fontloader-l-lua.lua34
-rw-r--r--src/fontloader/misc/fontloader-l-string.lua7
-rw-r--r--src/fontloader/misc/fontloader-l-table.lua2
-rw-r--r--src/fontloader/misc/fontloader-mplib.lua114
-rw-r--r--src/fontloader/misc/fontloader-mplib.tex21
-rw-r--r--src/fontloader/misc/fontloader-plain.tex29
-rw-r--r--src/fontloader/misc/fontloader-test.tex26
-rw-r--r--src/fontloader/misc/fontloader-util-str.lua13
-rw-r--r--src/fontloader/runtime/fontloader-reference.lua3031
-rw-r--r--src/luaotfload-auxiliary.lua9
-rw-r--r--src/luaotfload-colors.lua130
-rw-r--r--src/luaotfload-configuration.lua107
-rw-r--r--src/luaotfload-database.lua499
-rw-r--r--src/luaotfload-diagnostics.lua32
-rw-r--r--src/luaotfload-features.lua81
-rw-r--r--src/luaotfload-init.lua198
-rw-r--r--src/luaotfload-letterspace.lua1
-rw-r--r--src/luaotfload-loaders.lua163
-rw-r--r--src/luaotfload-log.lua4
-rw-r--r--src/luaotfload-main.lua279
-rw-r--r--src/luaotfload-parsers.lua51
-rw-r--r--src/luaotfload-resolvers.lua34
-rwxr-xr-xsrc/luaotfload-tool.lua321
-rw-r--r--src/luaotfload.sty1
50 files changed, 6532 insertions, 3729 deletions
diff --git a/NEWS b/NEWS
index a1ffb1e..d5688b6 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,9 @@ Change History
* Move remaining functionality from ``luaotfload-override`` into
initialization
* Write names index if fonts were removed
+ * Separate module loading from initialization
+ * Custom fontloader package with the files from Lualibs removed
+ * Lualibs are now a dependency when used in a TeX run as well
2014/07/13, luaotfload v2.5
* Remove legacy code.
diff --git a/doc/Makefile b/doc/Makefile
index ed340a4..0355a0e 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -1,6 +1,6 @@
NAME = luaotfload
DOCPDF = $(NAME).pdf
-DOCSRC = $(NAME)-latex.tex
+DOCSRC = $(NAME)-latex.tex $(NAME)-main.tex
SCRIPTNAME = luaotfload-tool
TOOLMANSRC = $(SCRIPTNAME).rst
@@ -47,7 +47,7 @@ $(DOTPDF): $(DOT)
@echo "creating file graph ($(DOTPDF))"
$(DO_GRAPHVIZ)
-.PHONY: clean mrproper
+.PHONY: clean mrproper graph doc all manuals
clean:
@$(RM) -- *.log *.aux *.toc *.idx *.ind *.ilg *.out
diff --git a/doc/filegraph.dot b/doc/filegraph.dot
index e1a9937..af3c8ad 100644
--- a/doc/filegraph.dot
+++ b/doc/filegraph.dot
@@ -10,8 +10,8 @@ strict digraph luaotfload_files { //looks weird with circo ...
size = "21cm";
rankdir = LR;
- ranksep = 0.618;
- nodesep = 1.618;
+ ranksep = 0.618;
+ nodesep = 1.618;
edge [
arrowhead = onormal,
@@ -30,37 +30,38 @@ strict digraph luaotfload_files { //looks weird with circo ...
fontdbutil -> font_names [label="--update",
style=dashed]
- luaotfload -> otfl_fonts_merged [label="merged"]
- luaotfload -> merged_lua_libs [label="unmerged", style=solid]
- luaotfload -> merged_luatex_fonts [label="unmerged", style=solid]
- luaotfload -> merged_context_libs [label="unmerged", style=solid]
+ luaotfload -> merged_lua_libs [style=solid]
+ luaotfload -> luaotfload_init [label="main()", style=solid]
+ luaotfload -> luaotfload_libs [label="main()", style=solid]
- luaotfload -> luaotfload_libs
- luaotfload -> otfl_blacklist_cnf
-
- otfl_fonts_merged -> merged_lua_libs [label="merged",
- style=dotted,
- lhead=cluster_merged]
- otfl_fonts_merged -> merged_luatex_fonts [label="merged",
+ fontloader -> merged_luatex_fonts [label="merged",
style=dotted,
lhead=cluster_merged]
- otfl_fonts_merged -> merged_context_libs [label="merged",
+ fontloader -> merged_context_libs [label="merged",
style=dotted,
lhead=cluster_merged]
+ luaotfload_init -> luaotfload_log [label="init_early()", style=solid]
+ luaotfload_init -> luaotfload_basics_gen [label="init_early()", style=solid]
+ luaotfload_init -> fontloader [label="init_main()", style=solid]
+
+ luaotfload_init -> merged_luatex_fonts [label="unmerged", style=solid]
+ luaotfload_init -> merged_context_libs [label="unmerged", style=solid]
+
merged_luatex_fonts -> font_age [label="luatex-fonts-enc.lua",
ltail=cluster_merged]
fontdbutil -> fontdbutil_diagnostics [label="--diagnose"]
-
fontdbutil -> status [label="version information"]
- fontdbutil_diagnostics -> status [constraint=no, label="hash files"]
+ luaotfload_package -> fontloader [label="merges", style=dashed]
merged_luatex_fonts -> characters [label="luaotfload-auxiliary.lua",
ltail=cluster_merged]
- luaotfload_libs -> font_names [label="luaotfload-database.lua"]
+ luaotfload_libs -> font_names [label="luaotfload-database.lua"]
+ luaotfload_libs -> otfl_blacklist_cnf [label="luaotfload-database.lua"]
+
mkstatus -> status [label="generates from distribution files",
style=dashed]
@@ -71,13 +72,30 @@ strict digraph luaotfload_files { //looks weird with circo ...
mkcharacters -> characters [label="generates from Context’s char-def.lua",
style=dashed]
+ fontdbutil_diagnostics -> status [label="hash files"]
+
+ mkimport -> merged_luatex_fonts [label="pulls", style=dashed, constraint=no];
+ mkimport -> merged_context_libs [label="pulls", style=dashed, constraint=no];
+
subgraph { rank = same;
- mkcharacters;
- mkglyphlist;
- mkstatus;
fontdbutil;
luaotfload }
+ subgraph cluster_scripts {
+ node [style=filled, color=white];
+ style = "filled,rounded";
+ color = "#44000011:#CCCCCC77";
+ //nodesep = "3.0";
+ rank = same;
+ label = "Standalone scripts";
+ gradientangle=90;
+ mkcharacters;
+ mkglyphlist;
+ mkimport;
+ mktest;
+ mkstatus;
+ }
+
/* ····································································
* main files
* ································································· */
@@ -90,6 +108,14 @@ strict digraph luaotfload_files { //looks weird with circo ...
style = "filled,rounded",
penwidth=2]
+ luaotfload_package [label = "luaotfload-package.lua",
+ shape = rect,
+ width = "3.2cm",
+ height = "1.2cm",
+ color = "#01012222",
+ style = "filled,rounded",
+ penwidth=2]
+
fontdbutil_diagnostics [label = "luaotfload-diagnostics.lua",
shape = rect,
width = "3.2cm",
@@ -98,27 +124,43 @@ strict digraph luaotfload_files { //looks weird with circo ...
style = "filled,rounded",
penwidth=2]
+ mktest [label = "mktest",
+ shape = rect,
+ width = "3.2cm",
+ height = "0.618cm",
+ color = "#FFFFFF66",
+ style = "filled,rounded",
+ penwidth=2]
+
+ mkimport [label = "mkimport",
+ shape = rect,
+ width = "3.2cm",
+ height = "0.618cm",
+ color = "#FFFFFF66",
+ style = "filled,rounded",
+ penwidth=2]
+
mkstatus [label = "mkstatus",
shape = rect,
width = "3.2cm",
- height = "1.2cm",
- color = "#01012222",
+ height = "0.618cm",
+ color = "#FFFFFF66",
style = "filled,rounded",
penwidth=2]
mkglyphlist [label = "mkglyphlist",
shape = rect,
width = "3.2cm",
- height = "1.2cm",
- color = "#01012222",
+ height = "0.618cm",
+ color = "#FFFFFF66",
style = "filled,rounded",
penwidth=2]
mkcharacters [label = "mkcharacters",
shape = rect,
width = "3.2cm",
- height = "1.2cm",
- color = "#01012222",
+ height = "0.618cm",
+ color = "#FFFFFF66",
style = "filled,rounded",
penwidth=2]
@@ -138,13 +180,37 @@ strict digraph luaotfload_files { //looks weird with circo ...
* style = "filled,rounded",
* penwidth=2]
*/
- otfl_fonts_merged [label = "luaotfload-fontloader.lua",
- shape = rect,
- width = "3.2cm",
- height = "1.2cm",
- color = "#01012222",
- style = "filled,rounded",
- penwidth=2]
+ luaotfload_init [label = "luaotfload-init.lua",
+ shape = rect,
+ width = "3.2cm",
+ height = "1.2cm",
+ color = "#44440122",
+ style = "filled,rounded",
+ penwidth=2]
+
+ luaotfload_log [label = "luaotfload-log.lua",
+ shape = rect,
+ width = "3.2cm",
+ height = "1.2cm",
+ color = "#44440122",
+ style = "filled,rounded",
+ penwidth=2]
+
+ luaotfload_basics_gen [label = "fontloader-basics-gen.lua",
+ shape = rect,
+ width = "3.2cm",
+ height = "1.2cm",
+ color = "#FFFFFF66",
+ style = "filled,rounded",
+ penwidth=2]
+
+ fontloader [label = "fontloader-reference.lua",
+ shape = rect,
+ width = "3.2cm",
+ height = "1.2cm",
+ color = "#FFFFFF66",
+ style = "filled,rounded",
+ penwidth=2]
/* ····································································
* luaotfload files
@@ -193,15 +259,16 @@ strict digraph luaotfload_files { //looks weird with circo ...
luaotfload_libs [
shape = box,
style = "filled,rounded",
- color = "grey90:goldenrod4",
+ color = "grey90",
fontsize = 10,
label = <
<table cellborder="0" bgcolor="#FFFFFFAA">
<th> <td colspan="2"> <font point-size="12" face="Iwona Italic">Luaotfload Libraries</font> </td> </th>
- <tr> <td>luaotfload-auxiliary.lua</td> <td>luaotfload-features.lua</td> </tr>
- <tr> <td>luaotfload-loaders.lua</td> <td>luaotfload-color.lua</td> </tr>
- <tr> <td>luaotfload-log.lua</td> <td>luaotfload-letterspace.lua</td> </tr>
- <tr> <td>luaotfload-parsers.lua</td> <td>luaotfload-database.lua</td> </tr>
+ <tr> <td>luaotfload-auxiliary.lua</td> <td>luaotfload-features.lua</td> </tr>
+ <tr> <td>luaotfload-loaders.lua</td> <td>luaotfload-colors.lua</td> </tr>
+ <tr> <td>luaotfload-resolvers.lua</td> <td>luaotfload-letterspace.lua</td> </tr>
+ <tr> <td>luaotfload-parsers.lua</td> <td>luaotfload-database.lua</td> </tr>
+ <tr> <td>luaotfload-configuration.lua</td><td></td> </tr>
</table>
>,
]
@@ -213,28 +280,40 @@ strict digraph luaotfload_files { //looks weird with circo ...
subgraph cluster_merged {
node [style=filled, color=white];
style = "filled,rounded";
- color = "grey90:dodgerblue4";
+ color = "#912CEE33";
//nodesep = "3.0";
rank = same;
label = "Merged Libraries";
gradientangle=0;
- merged_lua_libs;
merged_luatex_fonts;
merged_context_libs;
}
- otfl_fonts_merged -> merged_lua_libs
- otfl_fonts_merged -> merged_luatex_fonts
- otfl_fonts_merged -> merged_context_libs
+ fontloader -> merged_luatex_fonts
+ fontloader -> merged_context_libs
+
+ subgraph cluster_fontloader {
+ node [style=filled, color=white];
+ style = "filled,rounded";
+ color = "bisque";
+ //nodesep = "3.0";
+ rank = same;
+ label = "Fontloader";
+ gradientangle=0;
+ luaotfload_basics_gen;
+ fontloader;
+ }
merged_lua_libs [
shape = box,
style = "filled,rounded",
- color = "#FFFFFFAA",
+ color = "#CCCC1166",
+ fontsize = 10,
fontsize = 10,
label = <
<table border="0">
- <th> <td colspan="3"> <font point-size="12" face="Iwona Italic">Lua Libraries from Context</font> </td> </th>
+ <th> <td colspan="3"> <font point-size="12" face="Iwona Italic">
+ Lualibs &ndash; Lua Libraries from Context</font> </td> </th>
<tr> <td>l-lua.lua</td> <td>l-lpeg.lua</td> <td>l-function.lua</td> </tr>
<tr> <td>l-string.lua</td> <td>l-table.lua</td> <td>l-io.lua</td> </tr>
<tr> <td>l-file.lua</td> <td>l-boolean.lua</td> <td>l-math.lua</td> </tr>
@@ -251,17 +330,13 @@ strict digraph luaotfload_files { //looks weird with circo ...
label = <
<table border="0">
<th> <td colspan="2"> <font point-size="12" face="Iwona Italic">Font Loader (LuaTeX-Fonts)</font> </td> </th>
- <tr> <td>luatex-basics-gen.lua</td> <td>luatex-basics-nod.lua</td> </tr>
+ <tr> <td>luatex-fonts-cbk.lua</td> <td>luatex-basics-nod.lua</td> </tr>
<tr> <td>luatex-fonts-enc.lua</td> <td>luatex-fonts-syn.lua</td> </tr>
<tr> <td>luatex-font-tfm.lua</td> <td>luatex-font-afm.lua</td> </tr>
<tr> <td>luatex-font-afk.lua</td> <td>luatex-fonts-tfm.lua</td> </tr>
<tr> <td>luatex-fonts-chr.lua</td> <td>luatex-fonts-lua.lua</td> </tr>
<tr> <td>luatex-fonts-inj.lua</td> <td>luatex-fonts-otn.lua</td> </tr>
<tr> <td>luatex-fonts-def.lua</td> <td>luatex-fonts-ext.lua</td> </tr>
- <tr> <td>luatex-fonts-cbk.lua</td> </tr>
-
-
-
</table>
>,
]
diff --git a/doc/luaotfload-context.tex b/doc/luaotfload-context.tex
deleted file mode 100644
index 6c8d4b2..0000000
--- a/doc/luaotfload-context.tex
+++ /dev/null
@@ -1,485 +0,0 @@
-% macros=mkvi
-%% Copyright (C) 2009-2014
-%%
-%% by Elie Roux <elie.roux@telecom-bretagne.eu>
-%% and Khaled Hosny <khaledhosny@eglug.org>
-%% and Philipp Gesang <philipp.gesang@alumni.uni-heidelberg.de>
-%%
-%% This file is part of Luaotfload.
-%%
-%% Home: https://github.com/lualatex/luaotfload
-%% Support: <lualatex-dev@tug.org>.
-%%
-%% Luaotfload is under the GPL v2.0 (exactly) license.
-%%
-%% ----------------------------------------------------------------------------
-%%
-%% Luaotfload is free software; you can redistribute it and/or
-%% modify it under the terms of the GNU General Public License
-%% as published by the Free Software Foundation; version 2
-%% of the License.
-%%
-%% Luaotfload is distributed in the hope that it will be useful,
-%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-%% GNU General Public License for more details.
-%%
-%% You should have received a copy of the GNU General Public License
-%% along with Luaotfload; if not, see <http://www.gnu.org/licenses/>.
-%%
-%% ----------------------------------------------------------------------------
-%%
-
-\unprotect
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% layout and paper
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\setuppapersize [A5] [A5] %% 148×210
-
-\definelayout [mainlayout] [
- backspace=15mm, %% 133
- textwidth=103mm,
- topspace=15mm,
-]
-
-\setuplayout [mainlayout]
-
-\setuppagenumbering [location=,alternative=doublesided]
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% colors
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\usecolors [x11]
-\definecolor [primarycolor] [dodgerblue4]
-\definecolor [secondarycolor] [goldenrod4]
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% interaction
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\setupinteraction [
- state=start,
- page=no,
- click=yes,
- style=italic,
- color=primarycolor,
- contrastcolor=secondarycolor,
- title={The Luaotfload package},
- subtitle={OpenType layout system for Plain TeX and LaTeX},
- author={Elie Roux & Khaled Hosny & Philipp Gesang},
- keywords={luatex, lualatex, unicode, opentype},
-]
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% fonts
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\usemodule [simplefonts]
-
-\definefontfeature [default] [default] [mode=base,liga=yes,dlig=yes,tlig=yes,onum=yes]
-\definefontfeature [monospace] [liga=no,tlig=no,onum=no]
-
-\definefontfamily [mainface] [serif] [Linux Libertine O] [features=default]
-%definefontfamily [mainface] [serif] [Liberation Serif] [feature=default]
-%definefontfamily [mainface] [sans] [Iwona] [feature=default]
-\definefontfamily [mainface] [sans] [Iwona Medium] [
- feature=default,
- it=file:IwonaMedium-Italic.otf,
- tf=file:IwonaMedium-Regular.otf,
- bf=file:Iwona-Bold.otf,
- bi=file:Iwona-BoldItalic.otf,
-]
-%definefontfamily [mainface] [sans] [DejaVu Sans] [feature=default]
-\definefontfamily [mainface] [mono] [Liberation Mono] [scale=0.85,features=monospace]
-
-\setupbodyfont [mainface,10pt]
-
-\def \LUA {Lua}
-\def \LUALATEX {Lua\LATEX}
-\def \OpenType {\identifier{Open\kern-.25ex Type}}
-
-\definealternativestyle [emphasis:texmacro] [\ss \it \letterbackslash] [\ss \it \letterbackslash]
-\definealternativestyle [emphasis:identifier] [\ss] [\ss]
-\definealternativestyle [emphasis:normal] [\sl] [\sl]
-\definealternativestyle [emphasis:abbrev] [{\feature [+][smallcaps]}] [{\feature [+][smallcaps]}]
-\definealternativestyle [emphasis:Largefont] [{\switchtobodyfont[14pt]}] [{\switchtobodyfont[14pt]}]
-\definealternativestyle [emphasis:smallcaps] [{\feature [+][smallcaps]}] [{\feature [+][smallcaps]}]
-%definealternativestyle [emphasis:nonproportional] [\mono] [\mono]
-\definealternativestyle [emphasis:nonproportional] [\tt] [\tt]
-\definealternativestyle [head:section] [{\roman\feature[+][smallcaps]}] [{\roman\feature[+][smallcaps]}]
-\definealternativestyle [head:subsection] [{\roman\feature[+][smallcaps]}] [{\roman\feature[+][smallcaps]}]
-\definealternativestyle [head:subsubsection] [{\roman\feature[+][smallcaps]}] [{\roman\feature[+][smallcaps]}]
-\definealternativestyle [typing:luafunction] [\italic] [\italic]
-\definealternativestyle [typing:fileent] [\tt] [\tt]
-
-\definehighlight [texmacro] [style=emphasis:texmacro] %% cs
-\definehighlight [identifier] [style=emphasis:identifier] %% names
-\definehighlight [abbrev] [style=emphasis:abbrev] %% acronyms
-\definehighlight [emphasis] [style=emphasis:normal] %% level 1 emph
-
-\definehighlight [Largefont] [style=emphasis:Largefont] %% font size
-\definehighlight [smallcaps] [style=emphasis:smallcaps] %% font feature
-\definehighlight [nonproportional] [style=emphasis:nonproportional] %% font switch
-
-\definetype [fileent] [style=typing:fileent]
-\definetype [luafunction] [style=typing:luafunction]
-\setuptyping [style=ttx]
-
-\definebodyfontenvironment [8pt]
-\definebodyfontenvironment [10pt]
-\definebodyfontenvironment [12pt]
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% headings
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\setuphead [section] [style=head:section, alternative=inmargin]
-\setuphead [subsection] [style=head:subsection, alternative=inmargin]
-\setuphead [subsubsection] [style=head:subsubsection,alternative=inmargin]
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% running headers
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\setupheadertexts
- [{\tfx \getmarking[section]}] [pagenumber]
- [pagenumber] [{\tfx \fileent{Luaotfload} Manual}]
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% structurals
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% section
-\def \beginsection {\dosingleempty \section_begin_indeed}
-
-\def \section_begin_indeed [#ref]#title{%
- \iffirstargument
- \startsection [reference=#ref,title=#title]%
- \else
- \startsection [title=#title]%
- \fi
-}
-
-\let \endsection \stopsection
-
-%% subsection
-\def \beginsubsection {\dosingleempty \section_begin_indeed}
-
-\def \subsection_begin_indeed [#ref]#title{%
- \iffirstargument
- \startsubsection [reference=#ref,title=#title]%
- \else
- \startsubsection [title=#title]%
- \fi
-}
-
-\let \endsubsection \stopsection
-
-%% subsubsection
-\def \beginsubsubsection {\dosingleempty \section_begin_indeed}
-
-\def \subsubsection_begin_indeed [#ref]#title{%
- \iffirstargument
- \startsubsubsection [reference=#ref,title=#title]%
- \else
- \startsubsubsection [title=#title]%
- \fi
-}
-
-\let \endsubsubsection \stopsection
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% inline verbatim
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% Context offers both \type{…} and \type<<…>>, but not an unbalanced
-%% one that we could map directly onto Latex’s \verb|…|.
-
-\definetype [inlinecode_indeed] [style=emphasis:nonproportional]
-
-%% The listings macros don’t seem to handle backslashes and braces
-%% well. We emulate this behavior by handling the escaping in Lua.
-
-\startluacode
- local lpeg = require "lpeg"
- local Cs, P, S = lpeg.Cs, lpeg.P, lpeg.S
- local lpegmatch = lpeg.match
- local unescape_char = S[[\letterbackslash\letterleftbrace\letterrightbrace]]
- local backslash = P[[\letterbackslash]]
- local unescape = Cs (((backslash / "" * unescape_char) + 1)^0)
- commands.unescape_things = function (str)
- context.type (lpegmatch (unescape, str))
- end
-\stopluacode
-
-\unexpanded \def \inlinecode #content{%
- \ctxcommand {unescape_things \!!bs \detokenize {#content}\!!es}%
-}
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% codelistings
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Now *that’s* what I call easy.
-
-\unexpanded \def \beginlisting {%
- \grabbufferdatadirect{listing}{beginlisting}{endlisting}%
-}
-
-\unexpanded \def \endlisting {\typebuffer [listing]}
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% enumerations and lists
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\definedescription [descriptionitem] [
- align=right,
- alternative=hanging,
- width=2em,
-]
-
-\def \begindescriptions {%
- \begingroup
- \def \beginnormalitem ##1\endnormalitem{%
- \startitem##1\stopitem
- }
- \let \endnormalitem \relax
- \let \beginaltitem \startdescriptionitem
- \let \endaltitem \stopdescriptionitem
-}
-
-\let \enddescriptions \endgroup
-
-
-\definedescription [definitionitem] [
- align=right,
- alternative=hanging,
-]
-
-\def \begindefinitions {%
- \begingroup
- \def \beginnormalitem ##1\endnormalitem{%
- \startitem##1\stopitem
- }
- \let \endnormalitem \relax
- \let \beginaltitem \startdefinitionitem
- \let \endaltitem \stopdefinitionitem
-}
-
-\let \enddefinitions \endgroup
-
-
-\definedescription [filelistitem] [
- align=normal,
- alternative=hanging,
- headstyle=typing:fileent,
- width=4cm,
-]
-
-\def \beginfilelist {%
- \begingroup
- \def \beginnormalitem ##1\endnormalitem{%
- \startitem##1\stopitem
- }
- \let \endnormalitem \relax
- \let \beginaltitem \startfilelistitem
- \let \endaltitem \stopfilelistitem
-}
-
-\let \endfilelist \endgroup
-
-\definedescription [functionlistitem] [
- align=normal,
- alternative=hanging,
- headstyle=typing:luafunction,
- width=4cm,
-]
-
-\def \beginfunctionlist {%
- \begingroup
- \def \beginnormalitem ##1\endnormalitem{%
- \startitem##1\stopitem
- }
- \let \endnormalitem \relax
- \let \beginaltitem \startfunctionlistitem
- \let \endaltitem \stopfunctionlistitem
-}
-
-\let \endfunctionlist \endgroup
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% columns
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\def \begindoublecolumns {\startcolumns [2]}
-\let \enddoublecolumns \stopcolumns
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% alignment
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\setupnarrower [before={\blank[line]},after={\blank[line]}]
-\let \beginnarrower \startnarrower
-\let \endnarrower \stopnarrower
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% special elements
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\definefont [lmromantenregular] [file:lmroman10-regular.otf*default]
-
-\def \meta #1{%
- {\lmromantenregular<}%
- {\italic #1}%
- {\lmromantenregular>}%
-}
-
-\def \beginabstractcontent {%
- \grabbufferdatadirect{abstractcontent}{beginabstractcontent}{endabstractcontent}%
-}
-
-\let \endabstractcontent \relax
-
-\def \setdocumenttitle #1{\setvalue {document_title}{#1}}
-\def \setdocumentdate #1{\setvalue {document_date}{#1}}
-\def \setdocumentauthor #1{\setvalue {document_author}{#1}}
-
-\let \typesetdocumenttitle \relax
-\let \beginfrontmatter \relax
-
-\def \endfrontmatter {
- \startstandardmakeup
- \vfill
- \strut \hfill
- \startframed [frame=off,align=middle,width=.5\textwidth]
- \Largefont{\getvalue {document_title}}
- \stopframed
- \hfill \strut \par
-
- \blank [2*big]
-
- \strut \hfill
- \startframed [frame=off,align=middle,width=.65\textwidth]
- \setuplocalinterlinespace [18pt]
- \getvalue {document_author}
- \stopframed
- \hfill \strut \par
-
- \vfill
- \strut \hfill \getvalue {document_date} \hfill \strut
- \blank [2*big]
-
- \strut \hfill
- \startframed [width=.7\textwidth,align=normal,style=tfx,frame=off]%
- \getbuffer [abstractcontent]
- \stopframed
- \hfill \strut
- \stopstandardmakeup
-}
-
-\let \typesetcontent \completecontent
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% floats
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% XXX we can improve on this part later
-
-\usemodule [vim]
-\definevimtyping [bnf] [syntax=bnf]
-\definefloat [syntax] [figure]
-
-\def \beginsyntaxfloat #reference#caption{%
- \begingroup
- \edef \currentreference {#reference}%
- \edef \currentcaption {#caption}%
- \grabbufferdatadirect{rawsyntaxdata}{beginsyntaxfloat}{endsyntaxfloat}%
-}
-
-\def \endsyntaxfloat {%
- \savebuffer [rawsyntaxdata] [rawsyntaxdata]
- \startplacesyntax [
- reference=\currentreference,
- title={\currentcaption},
- ]
- %% there’s no \typebnfbuffer in t-vim :(
- \typebnffile {\jobname-rawsyntaxdata.tmp}
- \stopplacesyntax
- \endgroup%
-}
-
-\def \figurefloat #reference#caption#file{%
- \startplacefigure [
- reference=#reference,
- title={#caption},
- ]
- \externalfigure [#file] [width=\textwidth]
- \stopplacefigure
-}
-
-
-\def \tablefloat #reference#caption#content{%
- \startplacetable [
- reference=#reference,
- title={#caption},
- ]
- #content
- \stopplacetable
-}
-
-
-%% tables
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% tables
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\setupxtable [frame=off,option=stretch,textwidth=\dimexpr(\textwidth/2)]
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% hyperlinks and references
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\unexpanded \def \hyperlink{%
- \dosingleempty \hyperlink_indeed%
-}
-
-\def \hyperlink_indeed [#text]#url{%
- \iffirstargument
- \useURL [temporary_url] [#url] [] [#text]%
- \else
- \useURL [temporary_url] [#url]%
- \fi%
- \from [temporary_url]%
-}
-
-
-\def \email #1{\goto{#1}[url(mailto:#1)]}
-
-\def \label #tag{\reference [#tag]\empty}
-\def \pageref #tag{\at{page}{#tag}}
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% escaped characters
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\let \charpercent \letterpercent
-\let \charbackslash \letterbackslash
-\let \chartilde \lettertilde
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% main
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\protect
-
-\newif \ifcontextmkiv \contextmkivtrue
-
-\starttext
- \input luaotfload-main.tex
-\stoptext
-
diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex
index 34c494d..e3541c5 100644
--- a/doc/luaotfload-latex.tex
+++ b/doc/luaotfload-latex.tex
@@ -1,4 +1,4 @@
-\luatexsuppresslongerror1%% sigh ...
+\suppresslongerror1%% sigh ...
%% Copyright (C) 2009-2014
%%
%% by Elie Roux <elie.roux@telecom-bretagne.eu>
@@ -129,7 +129,7 @@
\definehighlight [fileent][\ttfamily\restoreunderscore] %% files, dirs
\definehighlight [texmacro][\sffamily\itshape\textbackslash] %% cs
-\definehighlight [luafunction][\sffamily\itshape\restoreunderscore] %% lua identifiers
+\definehighlight [luaident][\sffamily\itshape\restoreunderscore] %% lua identifiers
\definehighlight [identifier][\sffamily] %% names
\definehighlight [abbrev][\rmfamily\scshape] %% acronyms
\definehighlight [emphasis][\rmfamily\slshape] %% level 1 emph
@@ -220,9 +220,9 @@
\newcount \othercatcode \othercatcode 12
\newcount \activecatcode \othercatcode 13
-\newluatexcatcodetable \vrbcatcodes
-\setluatexcatcodetable \vrbcatcodes {%
- \luatexcatcodetable \CatcodeTableIniTeX
+\newcatcodetable \vrbcatcodes
+\setcatcodetable \vrbcatcodes {%
+ \catcodetable \CatcodeTableIniTeX
\catcode 9 \othercatcode %% \tabasciicode
\catcode 13 \othercatcode %% \endoflineasciicode
\catcode 12 \othercatcode %% \formfeedasciicode
@@ -230,48 +230,85 @@
\catcode 32 \othercatcode %% \spaceasciicode
}
-\newluatexcatcodetable \literalcatcodes
-\setluatexcatcodetable \literalcatcodes {%
- \luatexcatcodetable \CatcodeTableString
+\directlua {
+ document = document or { }
+ document.vrbcatcodesidx = tonumber (\the \vrbcatcodes)
+}
+
+\newcatcodetable \literalcatcodes
+\setcatcodetable \literalcatcodes {%
+ \catcodetable \CatcodeTableString
\catcode 32 \activecatcode %% \spaceasciicode
}
+\def \listingsurroundskip {\vskip \baselineskip}
+
\def \beginlisting {%
+ \noindent
\begingroup
- \luatexcatcodetable \vrbcatcodes
+ \catcodetable \vrbcatcodes
\beginlistingindeed%
}
\directlua {
- local texprint = tex.print
- local stringsub = string.sub
- local backslash = string.byte (0x5c)
- document = document or { }
+ local texsprint = tex.sprint
+ local stringis_empty = string.is_empty
+ local stringsub = string.sub
+ local stringgsub = string.gsub
+ %local backslash = unicode.utf8.char (0x200c)
+ local backslash = unicode.utf8.char (0x5c)
+ local escaped = [[\string\string\string\]]
document.printlines = function (buffer)
- for _, line in next, string.explode (buffer, "\noexpand\n") do
- if stringsub (line, 1, 1) == " " then
- line = backslash .. line
+ local lines = string.explode (buffer, "\noexpand\n")
+ print ""
+ for i, line in next, lines do
+ local line = stringgsub (line, backslash, escaped)
+ if stringis_empty (line) then
+ print (i, "listing: <empty line />")
+ texsprint [[\string\listingpar]]
+ else
+ local line = [[\string\beginlistingline]]
+ .. line
+ .. [[\string\endlistingline]]
+ .. [[\string\listingpar]]
+ print (i, "listing: «" .. line .. "»")
+ texsprint (document.vrbcatcodesidx, line)
end
- texprint (-1, line)
- texprint (-1, "")
end
end
}
+\def \listingpar {\endgraf}
+
+\let \endlistingline \relax
+\let \endlisting \relax
+
+\protected \def \beginlistingline{%
+ \leavevmode
+ \begingroup
+ \beginlistinglineindeed%
+}
+
+\def \beginlistinglineindeed #1\endlistingline{%
+ \endgroup
+ \hbox{%
+ \addfontfeature {RawFeature=-tlig;-liga}%% So one can’t just turn them all off at once using the ``Ligatures`` key?
+ \obeyspaces
+ #1}%
+}
+
\def \beginlistingindeed#1\endlisting{%
\endgroup
\begingroup
+ \endgraf
+ \listingsurroundskip
\ttfamily
\small
- \begin {quote}
- \bgroup
- \addfontfeature {RawFeature=-tlig;-liga}%% So one can’t just turn them all off at once using the ``Ligatures`` key?
- \luatexcatcodetable \literalcatcodes
- \obeyspaces
- \obeylines
- \directlua{document.printlines ([==[\detokenize {#1}]==])}
- \egroup
- \end {quote}
+ \parindent = 0em
+ \leftskip = 2em
+ \hangindent = 2em
+ \directlua{document.printlines ([==[\detokenize {#1}]==])}%
+ \listingsurroundskip
\endgroup
}
@@ -301,7 +338,7 @@
\definelist [descriptions]{\normalitem {\textbf \first}\hfill\break}
\definelist [definitions]{\normalitem {\fileent {\first}}}
\definelist [filelist]{\normalitem {\fileent {\first}}\space--\hskip 1em}
-\definelist [functionlist]{\normalitem {\luafunction {\first}}\hfill\break}
+\definelist [functionlist]{\normalitem {\luaident {\first}}\hfill\break}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% columns
diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex
index c01beba..5ea70d8 100644
--- a/doc/luaotfload-main.tex
+++ b/doc/luaotfload-main.tex
@@ -32,7 +32,7 @@
\beginfrontmatter
\setdocumenttitle {The \identifier{luaotfload} package}
- \setdocumentdate {2015/03/29 v2.6}
+ \setdocumentdate {2015/11/05 v2.6}
\setdocumentauthor {Elie Roux · Khaled Hosny · Philipp Gesang\\
Home: \hyperlink {https://github.com/lualatex/luaotfload}\\
Support: \email {lualatex-dev@tug.org}}
@@ -43,6 +43,11 @@
This package is an adaptation of the \CONTEXT font loading system.
It allows for loading \OpenType fonts with an extended syntax and adds
support for a variety of font features.
+
+ After discussion of the font loading API, this manual gives an
+ overview of the core components of \identifier{Luaotfload}: The
+ packaged font loader code, the names database, configuration, and
+ helper functions on the \LUA\ end.
\endabstractcontent
\endfrontmatter
@@ -233,7 +238,7 @@ where \meta{prefix} is either \inlinecode{file:} or \inlinecode {name:}.\footnot
needed, for instance when supplying a customized tex distribution.
The \inlinecode {my} lookup takes this a step further: it lets you define
- a custom resolver function and hook it into the \luafunction{resolve_font}
+ a custom resolver function and hook it into the \luaident{resolve_font}
callback.
%
This ensures full control over how a file is located.
@@ -664,7 +669,7 @@ obviously, \inlinecode{random}.
Specific pairs of letters and ligatures may be exempt from
letterspacing by defining the \LUA functions
- \luafunction{keeptogether} and \luafunction{keepligature},
+ \luaident{keeptogether} and \luaident{keepligature},
respectively, inside the namespace \inlinecode {luaotfload.letterspace}.
%
Both functions are called whenever the letterspacing callback
@@ -673,10 +678,10 @@ obviously, \inlinecode{random}.
If they return a true-ish value, no extra kern is inserted at
the current position.
%
- \luafunction{keeptogether} receives a pair of consecutive
+ \luaident{keeptogether} receives a pair of consecutive
glyph nodes in order of their appearance in the node list.
%
- \luafunction{keepligature} receives a single node which can be
+ \luaident{keepligature} receives a single node which can be
analyzed into components.
%
(For details refer to the \emphasis{glyph nodes} section in the
@@ -686,7 +691,7 @@ obviously, \inlinecode{random}.
user.
\endaltitem
-\ifcontextmkiv
+\iffalse
\startbuffer [printvectors]
\directlua{inspect(fonts.protrusions.setups.default)
inspect(fonts.expansions.setups.default)}
@@ -706,7 +711,7 @@ obviously, \inlinecode{random}.
%
Alternatively and with loss of information, you can dump
those tables into your terminal by issuing
- \unless \ifcontextmkiv
+ \unless \iffalse
\beginlisting
\directlua{inspect(fonts.protrusions.setups.default)
inspect(fonts.expansions.setups.default)}
@@ -763,7 +768,7 @@ Currently (2014) there are three of them:
remapping feature.
}:
- \unless \ifcontextmkiv
+ \unless \iffalse
%% Using braced arg syntax with inline code appears to be
%% impossible within Latex tables -- just ignore the weird
%% exclamation points below.
@@ -905,7 +910,7 @@ directories.
\tablefloat {table-searchpaths}
{List of paths searched for each supported operating system.}
{%
- \unless \ifcontextmkiv
+ \unless \iffalse
\begincentered
\begintabulate [lp{.5\textwidth}]
\beginrow
@@ -1056,27 +1061,28 @@ An example with explicit paths:
\endsection
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\beginsection {Files from \CONTEXT and \LUATEX-Fonts}
+\beginsection {The Fontloader}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\identifier{luaotfload} relies on code originally written by Hans
-Hagen for the \hyperlink[\identifier{\CONTEXT}]{http://wiki.contextgarden.net}
+\beginsubsection {Overview}
+
+To a large extent, \identifier{luaotfload} relies on code originally
+written by Hans Hagen for the
+\hyperlink[\identifier{\CONTEXT}]{http://wiki.contextgarden.net}
format.
%
-It integrates the font loader as distributed in
-the \identifier{\LUATEX-Fonts} package.
+It integrates the font loader, written entirely in \LUA, as distributed
+in the \identifier{\LUATEX-Fonts} package.
%
The original \LUA source files have been combined using the
-\fileent{mtx-package} script into a single, self-contained blob.
-In this form the font loader has no further dependencies\footnote{%
- It covers, however, to some extent the functionality of the
- \identifier{lualibs} package.
-}
-and requires only minor adaptions to integrate into
+\fileent{mtx-package} script into a single, self-contained blob. In
+this form the font loader depends only on the \identifier{lualibs}
+package and requires only minor adaptions to integrate into
\identifier{luaotfload}.
-%
-The guiding principle is to let \CONTEXT/\LUATEX-Fonts take care of
-the implementation, and update the imported code from time to time.
+
+The guiding principle is to let \CONTEXT/\LUATEX-Fonts take care of the
+implementation, and update the imported code as frequently as
+necessary.
%
As maintainers, we aim at importing files from upstream essentially
\emphasis{unmodified}, except for renaming them to prevent name
@@ -1087,19 +1093,23 @@ This job has been greatly alleviated since the advent of
manually spotted and extracted from the \CONTEXT source code in a
complicated and error-prone fashion.
+\endsubsection
+
+\beginsubsection {Contents and Dependencies}
+
Below is a commented list of the files distributed with
\identifier{luaotfload} in one way or the other.
%
See figure \ref{file-graph} on page \pageref{file-graph} for a
graphical representation of the dependencies.
%
-From \LUATEX-Fonts, only the file \fileent{luatex-fonts-merged.lua}
-has been imported as \fileent{luaotfload-fontloader.lua}.
-%
-It is generated by \fileent{mtx-package}, a \LUA source code merging
-too developed by Hans Hagen.\footnote{%
- \fileent{mtx-package} is
- \hyperlink [part of \CONTEXT]{http://repo.or.cz/w/context.git/blob_plain/refs/heads/origin:/scripts/context/lua/mtx-package.lua}
+Through the script \fileent{luaotfload-package.lua} the \CONTEXT
+utility \fileent{mtx-package} is invoked to create the
+\identifier{luaotfload} fontloader as a merged (amalgamated) source
+file.\footnote{%
+ \fileent{mtx-package}, a \LUA source code merging tool developed by
+ Hans Hagen, is
+ \hyperlink [part of \CONTEXT]{https://bitbucket.org/phg/context-mirror/src/726a663be481042003566d4614266b940b5a0c91/scripts/context/lua/mtx-package.lua?at=beta}
and requires \fileent{mtxrun}.
Run
\inlinecode {mtxrun --script package --help}
@@ -1107,31 +1117,46 @@ too developed by Hans Hagen.\footnote{%
For the actual merging code see the file
\fileent{util-mrg.lua} that is part of \CONTEXT.
}
-It houses several \LUA files that can be classed in three
-categories.
-
-\begindefinitions
- \beginnormalitem
- \emphasis{\LUA utility libraries}, a subset
- of what is provided by the \identifier{lualibs}
- package.
+%
+This file constitutes the “reference fontloader” and is part of the
+\identifier{luaotfload} package as \fileent{fontloader-reference.lua}.
+A companion to it, \fileent{luatex-basics-gen.lua} must be loaded
+beforehand to set up parts of the environment required by the \CONTEXT
+libraries.
+%
+During a \TEX\ run, the fontloader initialization and injection happens
+in the module \fileent{luaotfload-init.lua}.
+
+A number of \emphasis{\LUA utility libraries} are not part of the
+\identifier{luaotfload} fontloader, contrary to its equivalent in
+\LUATEX-Fonts. These are already provided by the \identifier{lualibs}
+and have thus been omitted from the merge.\footnote{%
+ Faithful listeners will remember the pre-2.6 era when the fontloader
+ used to be integrated as-is which caused all kinds of code
+ duplication with the pervasive \identifier{lualibs} package.
+ This conceptual glitch has since been amended by tightening the
+ coupling with the excellent \CONTEXT\ toolchain.
+}
- \begindoublecolumns
- \begindefinitions
- \beginaltitem {l-lua.lua} \endaltitem
- \beginaltitem {l-lpeg.lua} \endaltitem
- \beginaltitem {l-function.lua} \endaltitem
- \beginaltitem {l-string.lua} \endaltitem
- \beginaltitem {l-table.lua} \endaltitem
- \beginaltitem {l-io.lua} \endaltitem
- \beginaltitem {l-file.lua} \endaltitem
- \beginaltitem {l-boolean.lua} \endaltitem
- \beginaltitem {l-math.lua} \endaltitem
- \beginaltitem {util-str.lua} \endaltitem
- \enddefinitions
- \enddoublecolumns
- \endnormalitem
+\begindoublecolumns
+ \begindefinitions
+ \beginaltitem {l-lua.lua} \endaltitem
+ \beginaltitem {l-lpeg.lua} \endaltitem
+ \beginaltitem {l-function.lua} \endaltitem
+ \beginaltitem {l-string.lua} \endaltitem
+ \beginaltitem {l-table.lua} \endaltitem
+ \beginaltitem {l-io.lua} \endaltitem
+ \beginaltitem {l-file.lua} \endaltitem
+ \beginaltitem {l-boolean.lua} \endaltitem
+ \beginaltitem {l-math.lua} \endaltitem
+ \beginaltitem {util-str.lua} \endaltitem
+ \enddefinitions
+\enddoublecolumns
+
+The reference fontloader is home to several \LUA files that can be
+grouped twofold as below:
+\begindefinitions
\beginnormalitem
The \emphasis{font loader} itself.
These files have been written for
@@ -1139,7 +1164,6 @@ categories.
with \identifier{luaotfload}.
\begindoublecolumns
\begindefinitions
- \beginaltitem{luatex-basics-gen.lua} \endaltitem
\beginaltitem{luatex-basics-nod.lua} \endaltitem
\beginaltitem{luatex-fonts-enc.lua} \endaltitem
\beginaltitem{luatex-fonts-syn.lua} \endaltitem
@@ -1177,16 +1201,17 @@ categories.
\endnormalitem
\enddefinitions
-Note that if \identifier{luaotfload} cannot locate the
-merged file, it will load the individual \LUA libraries
-instead.
+As an alternative to the merged file, \identifier {Luaotfload} may load
+individual unpackaged \LUA libraries that come with the source, or even
+use the files from Context directly.
+%
+Thus if you prefer running bleeding edge code from the \CONTEXT beta,
+all you have to do is to choose the \inlinecode {context} fontloader
+via the configuration file (see section \ref{sec:conf} below).
%
-Their names remain the same as in \CONTEXT (without the
-\inlinecode {otfl}-prefix) since we imported the relevant section of
-\fileent{luatex-fonts.lua} unmodified into \fileent{luaotfload-main.lua}.
-Thus if you prefer running bleeding edge code from the
-\CONTEXT beta, all you have to do is remove
-\fileent{luaotfload-merged.lua} from the search path.
+This will make \identifier {Luaotfload} locate the \CONTEXT source via
+\identifier{kpathsea} lookups and use those instead of the fontloader
+%% TODO allow choosing the context path
Also, the merged file at some point loads the Adobe Glyph List from a
\LUA table that is contained in \fileent{luaotfload-glyphlist.lua},
@@ -1194,8 +1219,9 @@ which is automatically generated by the script
\fileent{mkglyphlist}.\footnote{%
See \fileent{luaotfload-font-enc.lua}.
The hard-coded file name is why we have to replace the procedure
- that loads the file in \fileent{luaotfload-override.lua}.
+ that loads the file in \fileent{luaotfload-init.lua}.
}
+%
There is a make target \identifier{glyphs} that will create a fresh
glyph list so we don’t need to import it from \CONTEXT any longer.
@@ -1209,7 +1235,10 @@ files not contained in the merge. Some of these have no equivalent in
font feature handling; incorporates some of the code from
\fileent{font-otc} from \CONTEXT;
\endaltitem
- \beginaltitem {luaotfload-override.lua}
+ \beginaltitem {luaotfload-configuration.lua}
+ handling of \fileent{luaotfload.conf(5)}.
+ \endaltitem
+ \beginaltitem {luaotfload-log.lua}
overrides the \CONTEXT logging functionality.
\endaltitem
\beginaltitem {luaotfload-loaders.lua}
@@ -1222,6 +1251,9 @@ files not contained in the merge. Some of these have no equivalent in
\beginaltitem {luaotfload-database.lua}
font names database.
\endaltitem
+ \beginaltitem {luaotfload-resolvers.lua}
+ file name resolvers.
+ \endaltitem
\beginaltitem {luaotfload-colors.lua}
color handling.
\endaltitem
@@ -1239,13 +1271,93 @@ files not contained in the merge. Some of these have no equivalent in
{Schematic of the files in \identifier{Luaotfload}}
{filegraph.pdf}
+\endsubsection
+
+\beginsubsection {Packaging}
+
+The fontloader code is integrated as an isolated component that can be
+switched out on demand.
+To specify the fontloader you wish to use, the configuration file
+(described in section \ref{sec:conf}) provides the option
+\inlinecode{fontloader}. Its value can be one of the identifiers
+\inlinecode{default} or \inlinecode{reference} or the name of a file
+somewhere in the search path of \LUATEX.
+
+\endsubsection
\endsection
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\beginsection {Configuration Files}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\beginnarrower
+ \emphasis{Caution}: For the authoritative documentation, consult the
+ manpage for \fileent{luaotfload.conf(5)}.
+\endnarrower
+
+\label{sec:conf}
+The runtime behavior of \identifier{Luaotfload} can be customized by
+means of a configuration file.
+% location
+At startup, it attempts to locate a file called \fileent
+{luaotfload.conf} or \fileent {luaotfloadrc} at a number of candidate
+locations:
+
+\begincentered
+ \begindefinitions
+ \beginnormalitem \fileent{./luaotfload.conf} \endnormalitem
+ \beginnormalitem \fileent{./luaotfloadrc} \endnormalitem
+ \beginnormalitem \fileent{\$XDG_CONFIG_HOME/luaotfload/luaotfload.conf} \endnormalitem
+ \beginnormalitem \fileent{\$XDG_CONFIG_HOME/luaotfload/luaotfload.rc} \endnormalitem
+ \beginnormalitem \fileent{~/.luaotfloadrc} \endnormalitem
+ \enddefinitions
+\endcentered
+
+\beginnarrower
+ \emphasis{Caution}: The configuration potentially modifies the final
+ document. A project-local file belongs under version control along
+ with the rest of the document. This is to ensure that everybody who
+ builds the project also receives the same customizations as the
+ author.
+\endnarrower
+
+% syntax
+The syntax is fairly close to the format used by
+\fileent{git-config(1)} which in turn was derived from the popular
+\identifier{.INI} format: Lines of key-value pairs are grouped under
+different configuration “sections”.\footnote{%
+ The configuration parser in \fileent {luoatfload-parsers.lua} might
+ be employed by other packages for similar purposes.
+}
+% example settings
+An example for customization via \fileent {luaotfload.conf} might look
+as below:
+
+\beginlisting
+; Example luaotfload.conf containing a rudimentary configuration
+[db]
+ update-live = false
+[run]
+ color-callback = pre_linebreak_filter
+ definer = info_patch
+ log-level = 5
+[default-features]
+ global = mode=base
+\endlisting
+
+This specifies that for the given project, \identifier{Luaotfload}
+shall not attempt to automatically scan for fonts if it can’t resolve a
+request. The font-based colorization will happen during \LUATEX’s
+pre-linebreak filter. The fontloader will output verbose information
+about the fonts at definition time along with globally increased
+verbosity. Lastly, the fontloader defaults to the less expensive
+\luaident{base} mode like it does in \CONTEXT.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\beginsection {Auxiliary Functions}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-With release version 2.2, \identifier{luaotfload} received
+With release version 2.2, \identifier{Luaotfload} received
additional functions for package authors to call from outside
(see the file \fileent{luaotfload-auxiliary.lua} for details).
%
@@ -1254,7 +1366,7 @@ The purpose of this addition twofold.
Firstly, \identifier{luaotfload} failed to provide a stable interface
to internals in the past which resulted in an unmanageable situation
of different packages abusing the raw access to font objects by means
-of the \luafunction{patch_font} callback.
+of the \luaident{patch_font} callback.
%
When the structure of the font object changed due to an update, all
of these imploded and several packages had to be fixed while
@@ -1275,7 +1387,7 @@ additions.
\beginsubsection {Callback Functions}
-The \luafunction{patch_font} callback is inserted in the wrapper
+The \luaident{patch_font} callback is inserted in the wrapper
\identifier{luaotfload} provides for the font definition callback.
%
At this place it allows manipulating the font object immediately after
@@ -1367,8 +1479,8 @@ are defined for which scripts.
\beginfunctionlist
\beginaltitem {aux.font_has_glyph (id : int, index : int)}
- Predicate that returns true if the font \luafunction{id}
- has glyph \luafunction{index}.
+ Predicate that returns true if the font \luaident{id}
+ has glyph \luaident{index}.
\endaltitem
\beginaltitem {aux.slot_of_name(name : string)}
@@ -1377,33 +1489,33 @@ are defined for which scripts.
\endaltitem
\beginaltitem {aux.name_of_slot(slot : int)}
- The inverse of \luafunction{slot_of_name}; note that this
+ The inverse of \luaident{slot_of_name}; note that this
might be incomplete as multiple glyph names may map to the
same codepoint, only one of which is returned by
- \luafunction{name_of_slot}.
+ \luaident{name_of_slot}.
\endaltitem
\beginaltitem {aux.provides_script(id : int, script : string)}
- Test if a font supports \luafunction{script}.
+ Test if a font supports \luaident{script}.
\endaltitem
\beginaltitem {aux.provides_language(id : int, script : string, language : string)}
- Test if a font defines \luafunction{language} for a given
- \luafunction{script}.
+ Test if a font defines \luaident{language} for a given
+ \luaident{script}.
\endaltitem
\beginaltitem {aux.provides_feature(id : int, script : string,
language : string, feature : string)}
- Test if a font defines \luafunction{feature} for
- \luafunction{language} for a given \luafunction{script}.
+ Test if a font defines \luaident{feature} for
+ \luaident{language} for a given \luaident{script}.
\endaltitem
\beginaltitem {aux.get_math_dimension(id : int, dimension : string)}
- Get the dimension \luafunction{dimension} of font \luafunction{id}.
+ Get the dimension \luaident{dimension} of font \luaident{id}.
\endaltitem
\beginaltitem {aux.sprint_math_dimension(id : int, dimension : string)}
- Same as \luafunction{get_math_dimension()}, but output the value
+ Same as \luaident{get_math_dimension()}, but output the value
in scaled points at the \TEX end.
\endaltitem
@@ -1416,7 +1528,7 @@ are defined for which scripts.
%% not implemented, may come back later
\beginfunctionlist
% \beginaltitem {aux.scan_external_dir(dir : string)}
-% Include fonts in directory \luafunction{dir} in font lookups without
+% Include fonts in directory \luaident{dir} in font lookups without
% adding them to the database.
%
\beginaltitem {aux.read_font_index (void)}
@@ -1527,9 +1639,9 @@ Another strategy that helps avoiding problems is to not access raw
Some of them, even though they are dangerous to access, have not been
overridden or disabled.
%
-Thus, whenever possible prefer the functions in the \luafunction{aux}
+Thus, whenever possible prefer the functions in the \luaident{aux}
namespace over direct manipulation of font objects. For example, raw
-access to the \luafunction{font.fonts} table like:
+access to the \luaident{font.fonts} table like:
\beginlisting
local somefont = font.fonts[2]
@@ -1537,16 +1649,16 @@ access to the \luafunction{font.fonts} table like:
\noindent can render already defined fonts unusable.
%
-Instead, the function \luafunction{font.getfont()} should be used
+Instead, the function \luaident{font.getfont()} should be used
because it has been replaced by a safe variant.
-However, \luafunction{font.getfont()} only covers fonts handled by the
+However, \luaident{font.getfont()} only covers fonts handled by the
font loader, e.~g. \identifier{OpenType} and \identifier{TrueType}
fonts, but not \abbrev{tfm} or \abbrev{ofm}.
%
Should you absolutely require access to all fonts known to \LUATEX,
including the virtual and autogenerated ones, then you need to query
-both \luafunction{font.getfont()} and \luafunction{font.fonts}.
+both \luaident{font.getfont()} and \luaident{font.fonts}.
%
In this case, best define you own accessor:
diff --git a/doc/luaotfload.conf.rst b/doc/luaotfload.conf.rst
index 2a339ce..12dd887 100644
--- a/doc/luaotfload.conf.rst
+++ b/doc/luaotfload.conf.rst
@@ -276,6 +276,8 @@ Section ``run``
+------------------+--------+------------------------------+
| resolver | s | ``"cached"`` |
+------------------+--------+------------------------------+
+| fontloader | s | ``"default"`` |
++------------------+--------+------------------------------+
The ``color-callback`` option determines the stage at which fonts that
defined with a ``color=xxyyzz`` feature will be colorized. By default
@@ -294,6 +296,29 @@ one that comes with the vanilla fontloader. Beware that this might
break tools like Fontspect that rely on the ``patch_font`` callback
provided by Luaotfload to perform important corrections on font data.
+The fontloader backend can be selected by setting the value of
+``fontloader``. Other than the default, which selects the packaged
+``reference`` loader as shipped with Luaotfload, a file name accessible
+by kpathsea can be specified. Alternatively, the individual files that
+constitute the fontloader can be loaded directly. While less efficient,
+this greatly aids debugging since error messages will reference the
+actual line numbers of the source files and explanatory comments are
+not stripped. Currently, three distinct loading strategies are
+available: ``unpackaged`` will load the batch that is part of
+Luaotfload. These contain the identical source code that the reference
+fontloader has been compiled from. Another option, ``context`` will
+attempt to load the same files by their names in the Context format
+from the search path. Consequently this option allows to use the
+version of Context that comes with the TeX distribution. Distros tend
+to prefer the stable version (“current” in Context jargon) of those
+files so certain bugs encountered in the more bleeding edge Luaotfload
+can be avoided this way. A third option is to use ``context`` with a
+colon to specify a directory prefix where the *TEXMF* is located that
+the files should be loaded from, e. g. ``context:~/context/tex/texmf-context``.
+This can be used when referencing another distribution like the Context
+minimals that is installed under a different path not indexed by
+kpathsea.
+
The value of ``log-level`` sets the default verbosity of messages
printed by Luaotfload. Only messages defined with a verbosity of less
than or equal to the supplied value will be output on the terminal.
diff --git a/scripts/mkcharacters b/scripts/mkcharacters
index abed2c9..59582f2 100755
--- a/scripts/mkcharacters
+++ b/scripts/mkcharacters
@@ -5,8 +5,6 @@
-- DESCRIPTION: import parts of char-def.lua
-- REQUIREMENTS: lua, ConTeXt, the lualibs package
-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
--- VERSION: 2.5
--- MODIFIED: 2014-02-11 07:24:25+0100
-----------------------------------------------------------------------
-- we create a stripped-down version of char-def.lua
-----------------------------------------------------------------------
@@ -15,7 +13,7 @@
-- config
-----------------------------------------------------------------------
local charfile = "./build/luaotfload-characters.lua"
-local chardef = "/home/phg/base/char-def.lua"
+local chardef = arg[1]
--- for every code point char-def.lua provides a set of fields. they
--- are:
@@ -65,6 +63,11 @@ for _, lib in next, { "lualibs-lua.lua",
require(found)
end
+if not chardef then
+ chardef = kpse.expand_path("~/context/tex/texmf-context/tex/context/base/")
+ .. "/char-def.lua"
+end
+
if not (chardef and lfs.isfile(chardef)) then
--- we could grab the file from contextgarden but as Context is part
--- of TL it’s not worth bothering
@@ -72,6 +75,9 @@ if not (chardef and lfs.isfile(chardef)) then
"Could not find ConTeXt.")
end
+io.write(string.format("extracting data from char-def.lua at %s\n",
+ chardef))
+
-----------------------------------------------------------------------
-- functionality
-----------------------------------------------------------------------
diff --git a/scripts/mkglyphlist b/scripts/mkglyphlist
index 8fde098..f66a686 100755
--- a/scripts/mkglyphlist
+++ b/scripts/mkglyphlist
@@ -5,8 +5,6 @@
-- DESCRIPTION: part of the luaotfload package
-- REQUIREMENTS: lua, lpeg, luasocket, the lualibs package
-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
--- VERSION: 2.5
--- MODIFIED: 2014-02-11 06:44:50+0100
-----------------------------------------------------------------------
-- interesting thread on the Context list:
-- http://www.ntg.nl/pipermail/ntg-context/2008/029057.html
diff --git a/scripts/mkimport b/scripts/mkimport
index 0833ccb..9ea224a 100755
--- a/scripts/mkimport
+++ b/scripts/mkimport
@@ -5,8 +5,6 @@
-- DESCRIPTION: check luaotfload imports against Context
-- REQUIREMENTS: luatex, the lualibs package, Context MkIV
-- AUTHOR: Philipp Gesang (Phg), <phg@phi-gamma.net>
--- VERSION: 42
--- CREATED: 2014-12-08 22:36:15+0100
-------------------------------------------------------------------------------
--
@@ -53,7 +51,7 @@ local tableconcat = table.concat
-- config
-------------------------------------------------------------------------------
-local context_root = "/home/phg/context/tex/texmf-context"
+local parms = { }
local our_prefix = "fontloader"
local luatex_fonts_prefix = "luatex"
local fontloader_subdir = "src/fontloader"
@@ -71,7 +69,6 @@ local subdirs = {
local searchdirs = {
--- order is important!
fontloader_subdir,
- context_root
}
local prefixes = {
@@ -261,7 +258,7 @@ end
local derive_category_path = function (cat)
local subpath = origin_paths[cat] or die ("category " .. cat .. " unknown")
- local location = file.join (context_root, subpath)
+ local location = file.join (parms.context_root, subpath)
if not lfsisdir (location) then
die ("invalid base path defined for category "
.. cat .. " at " .. location)
@@ -484,7 +481,8 @@ end --[[ [local import_file = function (name, kind)] ]]
local import = function (arg)
if #arg > 1 then
- local name = arg[2] or die ("invalid filename " .. tostring (arg[2]))
+ local tgt = parms.target
+ local name = tgt or die ("invalid filename " .. tostring (tgt))
local stat = import_file (name)
if stat == import_failed then
die ("failed to import file " .. name)
@@ -528,10 +526,10 @@ local search_paths = function (target)
end
- local found = find_in_path (context_root, origin_paths.context, target)
+ local found = find_in_path (parms.context_root, origin_paths.context, target)
if found then return found end
- local found = find_in_path (context_root, origin_paths.fontloader, target)
+ local found = find_in_path (parms.context_root, origin_paths.fontloader, target)
if found then return found end
return false
end
@@ -658,7 +656,7 @@ local describe = function (target, location)
end
local tell = function (arg)
- local target = arg[2]
+ local target = parms.target
if not target then die "no filename given" end
local location = search (target)
@@ -848,12 +846,31 @@ local check_job = function (j)
return job_kind[j] or die ("invalid job type “%s”.", j)
end
+local parse_argv = function (argv)
+ local job
+ local tgt
+ local pth
+
+ local argc = #arg
+ if argc < 1 or argc > 3 then return "help" end
+ job = arg[1] or "help"
+ if argc > 1 then
+ tgt = arg[2]
+ if argc == 3 then pth = arg[3] end
+ end
+ if not pth then pth = "~/context/tex/texmf-context" end
+ parms.context_root = kpse.expand_path (pth)
+ parms.target = tgt
+ searchdirs [#searchdirs + 1] = pth
+ return job
+end
+
-------------------------------------------------------------------------------
-- entry point
-------------------------------------------------------------------------------
local main = function ()
- local job = arg[1] or "help"
+ local job = parse_argv (arg)
local runner = check_job (job)
return runner(arg)
end
diff --git a/scripts/mktests b/scripts/mktests
index b36b6dd..ad8c4f5 100755
--- a/scripts/mktests
+++ b/scripts/mktests
@@ -5,8 +5,6 @@
-- DESCRIPTION: test the behavior of Luaotfload
-- REQUIREMENTS: Luatex > 0.76, Luaotfload
-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
--- VERSION: 2.4
--- MODIFIED: 2014-05-15 22:16:47+0200
-----------------------------------------------------------------------
--
--===================================================================--
@@ -20,14 +18,68 @@ local tests = { }
local lpeg = require "lpeg"
local lpegmatch = lpeg.match
+config = { luaotfload = { } }
+luatexbase = { }
+
kpse.set_program_name "luatex"
require "lualibs"
require "luaotfload-basics-gen.lua"
require "luaotfload-log.lua"
-require "luaotfload-parsers"
-require "luaotfload-configuration"
-require "luaotfload-database"
+
+fonts = { names = { } } -- for db; normally provided by the fontloaders
+
+local require_init = { }
+
+local loadmodule = function (name)
+ local v = require ("luaotfload-" .. name)
+ if v then
+ local mod = { }
+ local tv = type (v)
+ if tv == "table" then
+ mod.name = name
+ mod.init = v.init
+ require_init [#require_init + 1] = mod
+ elseif tv == "function" then
+ mod.name = name
+ mod.init = v
+ require_init [#require_init + 1] = mod
+ end
+ end
+end
+
+require "alt_getopt"
+
+loadmodule "log.lua" --- this populates the luaotfload.log.* namespace
+loadmodule "parsers" --- fonts.conf, configuration, and request syntax
+loadmodule "configuration" --- configuration file handling
+loadmodule "database"
+loadmodule "resolvers" --- Font lookup
+
+do --- init_modules
+ --- NB we don’t command the logger at this point.
+ local todo = #require_init
+ local ret = true
+ for i = 1, todo do
+ local mod = require_init[i]
+ local name = mod.name
+ local init = mod.init
+ if type (init) ~= "function" then
+ error ("luaotfload broken; module "
+ .. name .. " missing initializers!")
+ end
+ local v = mod.init ()
+ if v == true then
+ --- evaluated well
+ elseif type (v) == "table" then
+ luaotfload[name] = v
+ else
+ error ("luaotfload broken; initialization of module "
+ .. name .. " returned " .. tostring (v) .. ".")
+ return false
+ end
+ end
+end
local names = fonts.names
@@ -236,7 +288,6 @@ local default_spec = {
local resolve_font_name = function ()
local failed, total = 0, 0
- local resolve_name = names.resolve_name
for nset = 1, #font_name_tests do
local set = font_name_tests[nset]
@@ -248,7 +299,7 @@ local resolve_font_name = function ()
local input_spec = table.copy (default_spec)
input_spec.name = input
input_spec.specification = input_spec.lookup .. ":" .. input
- local result = resolve_name (input_spec) == output
+ local result = fonts.names.lookup_font_name (input_spec) == output
total = total + 1
if not result then
failed = failed + 1
@@ -262,7 +313,7 @@ local resolve_font_name = function ()
.. ":" .. input_spec.name
input_spec.optsize = input_spec.optsize or default_spec.optsize
input_spec.style = translate_style [input_spec.style]
- local result = resolve_name (input_spec) == output
+ local result = fonts.names.lookup_font_name (input_spec) == output
total = total + 1
if not result then
failed = failed + 1
diff --git a/src/fontloader/misc/fontloader-basics-nod.lua b/src/fontloader/misc/fontloader-basics-nod.lua
index 1ec2895..39400a3 100644
--- a/src/fontloader/misc/fontloader-basics-nod.lua
+++ b/src/fontloader/misc/fontloader-basics-nod.lua
@@ -56,6 +56,9 @@ local whatcodes = { } for k,v in next, node.whatsits() do whatcodes[string.gs
local glyphcodes = { [0] = "character", "glyph", "ligature", "ghost", "left", "right" }
local disccodes = { [0] = "discretionary", "explicit", "automatic", "regular", "first", "second" }
+for i=0,#glyphcodes do glyphcodes[glyphcodes[i]] = i end
+for i=0,#disccodes do disccodes [disccodes [i]] = i end
+
nodes.nodecodes = nodecodes
nodes.whatcodes = whatcodes
nodes.whatsitcodes = whatcodes
diff --git a/src/fontloader/misc/fontloader-font-afm.lua b/src/fontloader/misc/fontloader-font-afm.lua
index a96c668..329639b 100644
--- a/src/fontloader/misc/fontloader-font-afm.lua
+++ b/src/fontloader/misc/fontloader-font-afm.lua
@@ -152,14 +152,14 @@ end
local keys = { }
-function keys.FontName (data,line) data.metadata.fontname = strip (line) -- get rid of spaces
- data.metadata.fullname = strip (line) end
-function keys.ItalicAngle (data,line) data.metadata.italicangle = tonumber (line) end
-function keys.IsFixedPitch(data,line) data.metadata.isfixedpitch = toboolean(line,true) end
-function keys.CharWidth (data,line) data.metadata.charwidth = tonumber (line) end
-function keys.XHeight (data,line) data.metadata.xheight = tonumber (line) end
-function keys.Descender (data,line) data.metadata.descender = tonumber (line) end
-function keys.Ascender (data,line) data.metadata.ascender = tonumber (line) end
+function keys.FontName (data,line) data.metadata.fontname = strip (line) -- get rid of spaces
+ data.metadata.fullname = strip (line) end
+function keys.ItalicAngle (data,line) data.metadata.italicangle = tonumber (line) end
+function keys.IsFixedPitch(data,line) data.metadata.monospaced = toboolean(line,true) end
+function keys.CharWidth (data,line) data.metadata.charwidth = tonumber (line) end
+function keys.XHeight (data,line) data.metadata.xheight = tonumber (line) end
+function keys.Descender (data,line) data.metadata.descender = tonumber (line) end
+function keys.Ascender (data,line) data.metadata.ascender = tonumber (line) end
function keys.Comment (data,line)
-- Comment DesignSize 12 (pts)
-- Comment TFM designsize: 12 (in points)
@@ -640,7 +640,7 @@ local function copytotfm(data)
local spacer = "space"
local spaceunits = 500
--
- local monospaced = metadata.isfixedpitch
+ local monospaced = metadata.monospaced
local charwidth = metadata.charwidth
local italicangle = metadata.italicangle
local charxheight = metadata.xheight and metadata.xheight > 0 and metadata.xheight
diff --git a/src/fontloader/misc/fontloader-font-con.lua b/src/fontloader/misc/fontloader-font-con.lua
index 72fbb5c..55d7793 100644
--- a/src/fontloader/misc/fontloader-font-con.lua
+++ b/src/fontloader/misc/fontloader-font-con.lua
@@ -170,8 +170,8 @@ constructors.setfactor()
function constructors.scaled(scaledpoints, designsize) -- handles designsize in sp as well
if scaledpoints < 0 then
+ local factor = constructors.factor
if designsize then
- local factor = constructors.factor
if designsize > factor then -- or just 1000 / when? mp?
return (- scaledpoints/1000) * designsize -- sp's
else
@@ -191,10 +191,9 @@ in the process; numbers are of course copies. Here 65536 equals 1pt. (Due to
excessive memory usage in CJK fonts, we no longer pass the boundingbox.)</p>
--ldx]]--
--- The scaler is only used for otf and afm and virtual fonts. If
--- a virtual font has italic correction make sure to set the
--- hasitalics flag. Some more flags will be added in
--- the future.
+-- The scaler is only used for otf and afm and virtual fonts. If a virtual font has italic
+-- correction make sure to set the hasitalics flag. Some more flags will be added in the
+-- future.
--[[ldx--
<p>The reason why the scaler was originally split, is that for a while we experimented
@@ -426,6 +425,7 @@ function constructors.scale(tfmdata,specification)
local vdelta = delta
--
target.designsize = parameters.designsize -- not really needed so it might become obsolete
+ target.units = units
target.units_per_em = units -- just a trigger for the backend
--
local direction = properties.direction or tfmdata.direction or 0 -- pointless, as we don't use omf fonts at all
@@ -562,26 +562,27 @@ function constructors.scale(tfmdata,specification)
target.mathparameters = nil -- nop
end
--
- local italickey = "italic"
- local useitalics = true -- something context
- --
- -- some context specific trickery (this will move to a plugin)
+ -- Here we support some context specific trickery (this might move to a plugin). During the
+ -- transition to opentype the engine had troubles with italics so we had some additional code
+ -- for fixing that. In node mode (text) we don't care much if italics gets passed because
+ -- the engine does nothign with them then.
--
if hasmath then
- -- the latest luatex can deal with it itself so we now disable this
- -- mechanism here
- --
- -- if properties.mathitalics then
- -- italickey = "italic_correction"
- -- if trace_defining then
- -- report_defining("math italics disabled for font %a, fullname %a, filename %a",name,fullname,filename)
- -- end
- -- end
- autoitalicamount = false -- new
- elseif properties.textitalics then
- italickey = "italic_correction"
- useitalics = false
- if properties.delaytextitalics then
+ local mathitalics = properties.mathitalics
+ if mathitalics == false then
+ if trace_defining then
+ report_defining("%s italics %s for font %a, fullname %a, filename %a","math",hasitalics and "ignored" or "disabled",name,fullname,filename)
+ end
+ hasitalics = false
+ autoitalicamount = false
+ end
+ else
+ local textitalics = properties.textitalics
+ if textitalics == false then
+ if trace_defining then
+ report_defining("%s italics %s for font %a, fullname %a, filename %a","text",hasitalics and "ignored" or "disabled",name,fullname,filename)
+ end
+ hasitalics = false
autoitalicamount = false
end
end
@@ -590,8 +591,7 @@ function constructors.scale(tfmdata,specification)
--
if trace_defining then
report_defining("defining tfm, name %a, fullname %a, filename %a, hscale %a, vscale %a, math %a, italics %a",
- name,fullname,filename,hdelta,vdelta,
- hasmath and "enabled" or "disabled",useitalics and "enabled" or "disabled")
+ name,fullname,filename,hdelta,vdelta,hasmath and "enabled" or "disabled",hasitalics and "enabled" or "disabled")
end
--
constructors.beforecopyingcharacters(target,tfmdata)
@@ -606,15 +606,15 @@ function constructors.scale(tfmdata,specification)
local c = changed[unicode]
if c then
description = descriptions[c] or descriptions[unicode] or character
- character = characters[c] or character
- index = description.index or c
+ character = characters[c] or character
+ index = description.index or c
else
description = descriptions[unicode] or character
- index = description.index or unicode
+ index = description.index or unicode
end
else
description = descriptions[unicode] or character
- index = description.index or unicode
+ index = description.index or unicode
end
local width = description.width
local height = description.height
@@ -699,24 +699,8 @@ function constructors.scale(tfmdata,specification)
end
end
--
- if autoitalicamount then
- local vi = description.italic
- if not vi then
- local vi = description.boundingbox[3] - description.width + autoitalicamount
- if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic
- chr[italickey] = vi*hdelta
- end
- elseif vi ~= 0 then
- chr[italickey] = vi*hdelta
- end
- elseif hasitalics then
- local vi = description.italic
- if vi and vi ~= 0 then
- chr[italickey] = vi*hdelta
- end
- end
- -- to be tested
if hasmath then
+ --
-- todo, just operate on descriptions.math
local vn = character.next
if vn then
@@ -753,8 +737,13 @@ function constructors.scale(tfmdata,specification)
chr.horiz_variants = t
end
end
+ -- todo also check mathitalics (or that one can go away)
end
- local va = character.top_accent
+ local vi = character.vert_italic
+ if vi and vi ~= 0 then
+ chr.vert_italic = vi*hdelta
+ end
+ local va = character.accent
if va then
chr.top_accent = vdelta*va
end
@@ -777,6 +766,27 @@ function constructors.scale(tfmdata,specification)
chr.mathkern = kerns -- singular -> should be patched in luatex !
end
end
+ if hasitalics then
+ local vi = character.italic
+ if vi and vi ~= 0 then
+ chr.italic = vi*hdelta
+ end
+ end
+ elseif autoitalicamount then -- itlc feature
+ local vi = description.italic
+ if not vi then
+ local vi = description.boundingbox[3] - description.width + autoitalicamount
+ if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic
+ chr.italic = vi*hdelta
+ end
+ elseif vi ~= 0 then
+ chr.italic = vi*hdelta
+ end
+ elseif hasitalics then -- unlikely
+ local vi = character.italic
+ if vi and vi ~= 0 then
+ chr.italic = vi*hdelta
+ end
end
if haskerns then
local vk = character.kerns
@@ -843,6 +853,8 @@ function constructors.scale(tfmdata,specification)
targetcharacters[unicode] = chr
end
--
+ properties.setitalics = hasitalics -- for postprocessing
+ --
constructors.aftercopyingcharacters(target,tfmdata)
--
constructors.trytosharefont(target,tfmdata)
@@ -895,12 +907,21 @@ function constructors.finalize(tfmdata)
parameters.slantfactor = tfmdata.slant or 0
end
--
- if not parameters.designsize then
- parameters.designsize = tfmdata.designsize or (factors.pt * 10)
+ local designsize = parameters.designsize
+ if designsize then
+ parameters.minsize = tfmdata.minsize or designsize
+ parameters.maxsize = tfmdata.maxsize or designsize
+ else
+ designsize = factors.pt * 10
+ parameters.designsize = designsize
+ parameters.minsize = designsize
+ parameters.maxsize = designsize
end
+ parameters.minsize = tfmdata.minsize or parameters.designsize
+ parameters.maxsize = tfmdata.maxsize or parameters.designsize
--
if not parameters.units then
- parameters.units = tfmdata.units_per_em or 1000
+ parameters.units = tfmdata.units or tfmdata.units_per_em or 1000
end
--
if not tfmdata.descriptions then
@@ -976,6 +997,7 @@ function constructors.finalize(tfmdata)
tfmdata.auto_protrude = nil
tfmdata.extend = nil
tfmdata.slant = nil
+ tfmdata.units = nil
tfmdata.units_per_em = nil
--
tfmdata.cache = nil
diff --git a/src/fontloader/misc/fontloader-font-def.lua b/src/fontloader/misc/fontloader-font-def.lua
index fdded3c..add42ee 100644
--- a/src/fontloader/misc/fontloader-font-def.lua
+++ b/src/fontloader/misc/fontloader-font-def.lua
@@ -183,10 +183,11 @@ end
function resolvers.name(specification)
local resolve = fonts.names.resolve
if resolve then
- local resolved, sub = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
+ local resolved, sub, subindex = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
if resolved then
specification.resolved = resolved
specification.sub = sub
+ specification.subindex = subindex
local suffix = lower(suffixonly(resolved))
if fonts.formats[suffix] then
specification.forced = suffix
@@ -204,10 +205,11 @@ end
function resolvers.spec(specification)
local resolvespec = fonts.names.resolvespec
if resolvespec then
- local resolved, sub = resolvespec(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
+ local resolved, sub, subindex = resolvespec(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
if resolved then
specification.resolved = resolved
specification.sub = sub
+ specification.subindex = subindex
specification.forced = lower(suffixonly(resolved))
specification.forcedname = resolved
specification.name = removesuffix(resolved)
diff --git a/src/fontloader/misc/fontloader-font-map.lua b/src/fontloader/misc/fontloader-font-map.lua
index 69474ba..b645d9a 100644
--- a/src/fontloader/misc/fontloader-font-map.lua
+++ b/src/fontloader/misc/fontloader-font-map.lua
@@ -31,25 +31,27 @@ of obsolete. Some code may move to runtime or auxiliary modules.</p>
<p>The name to unciode related code will stay of course.</p>
--ldx]]--
-local function loadlumtable(filename) -- will move to font goodies
- local lumname = file.replacesuffix(file.basename(filename),"lum")
- local lumfile = resolvers.findfile(lumname,"map") or ""
- if lumfile ~= "" and lfs.isfile(lumfile) then
- if trace_loading or trace_mapping then
- report_fonts("loading map table %a",lumfile)
- end
- lumunic = dofile(lumfile)
- return lumunic, lumfile
- end
-end
+-- local function loadlumtable(filename) -- will move to font goodies
+-- local lumname = file.replacesuffix(file.basename(filename),"lum")
+-- local lumfile = resolvers.findfile(lumname,"map") or ""
+-- if lumfile ~= "" and lfs.isfile(lumfile) then
+-- if trace_loading or trace_mapping then
+-- report_fonts("loading map table %a",lumfile)
+-- end
+-- lumunic = dofile(lumfile)
+-- return lumunic, lumfile
+-- end
+-- end
local hex = R("AF","09")
-local hexfour = (hex*hex*hex*hex) / function(s) return tonumber(s,16) end
-local hexsix = (hex*hex*hex*hex*hex*hex) / function(s) return tonumber(s,16) end
+----- hexfour = (hex*hex*hex*hex) / function(s) return tonumber(s,16) end
+----- hexsix = (hex*hex*hex*hex*hex*hex) / function(s) return tonumber(s,16) end
+local hexfour = (hex*hex*hex^-2) / function(s) return tonumber(s,16) end
+local hexsix = (hex*hex*hex^-4) / function(s) return tonumber(s,16) end
local dec = (R("09")^1) / tonumber
local period = P(".")
-local unicode = P("uni") * (hexfour * (period + P(-1)) * Cc(false) + Ct(hexfour^1) * Cc(true))
-local ucode = P("u") * (hexsix * (period + P(-1)) * Cc(false) + Ct(hexsix ^1) * Cc(true))
+local unicode = (P("uni") + P("UNI")) * (hexfour * (period + P(-1)) * Cc(false) + Ct(hexfour^1) * Cc(true)) -- base planes
+local ucode = (P("u") + P("U") ) * (hexsix * (period + P(-1)) * Cc(false) + Ct(hexsix ^1) * Cc(true)) -- extended
local index = P("index") * dec * Cc(false)
local parser = unicode + ucode + index
@@ -168,7 +170,6 @@ end
-- return s
-- end
-mappings.loadlumtable = loadlumtable
mappings.makenameparser = makenameparser
mappings.tounicode = tounicode
mappings.tounicode16 = tounicode16
@@ -179,13 +180,13 @@ local ligseparator = P("_")
local varseparator = P(".")
local namesplitter = Ct(C((1 - ligseparator - varseparator)^1) * (ligseparator * C((1 - ligseparator - varseparator)^1))^0)
+-- maybe: ff fi fl ffi ffl => f_f f_i f_l f_f_i f_f_l
+
-- local function test(name)
-- local split = lpegmatch(namesplitter,name)
-- print(string.formatters["%s: [% t]"](name,split))
-- end
--- maybe: ff fi fl ffi ffl => f_f f_i f_l f_f_i f_f_l
-
-- test("i.f_")
-- test("this")
-- test("this.that")
@@ -221,332 +222,184 @@ end
mappings.overloads = overloads
-function mappings.addtounicode(data,filename)
- local resources = data.resources
- local properties = data.properties
- local descriptions = data.descriptions
- local unicodes = resources.unicodes
- local lookuptypes = resources.lookuptypes
+function mappings.addtounicode(data,filename,checklookups)
+ local resources = data.resources
+ local unicodes = resources.unicodes
if not unicodes then
return
end
+ local properties = data.properties
+ local descriptions = data.descriptions
-- we need to move this code
unicodes['space'] = unicodes['space'] or 32
unicodes['hyphen'] = unicodes['hyphen'] or 45
unicodes['zwj'] = unicodes['zwj'] or 0x200D
unicodes['zwnj'] = unicodes['zwnj'] or 0x200C
- local private = fonts.constructors.privateoffset
- local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context
- ----- namevector = fonts.encodings.agl.names -- loaded runtime in context
- local missing = { }
- local lumunic, uparser, oparser
- local cidinfo, cidnames, cidcodes, usedmap
- --
- cidinfo = properties.cidinfo
- usedmap = cidinfo and fonts.cid.getmap(cidinfo)
--
+ local private = fonts.constructors and fonts.constructors.privateoffset or 0xF0000 -- 0x10FFFF
+ local unicodevector = fonts.encodings.agl.unicodes or { } -- loaded runtime in context
+ local contextvector = fonts.encodings.agl.ctxcodes or { } -- loaded runtime in context
+ local missing = { }
+ local nofmissing = 0
+ local oparser = nil
+ local cidnames = nil
+ local cidcodes = nil
+ local cidinfo = properties.cidinfo
+ local usedmap = cidinfo and fonts.cid.getmap(cidinfo)
+ local uparser = makenameparser() -- hm, every time?
if usedmap then
- oparser = usedmap and makenameparser(cidinfo.ordering)
- cidnames = usedmap.names
- cidcodes = usedmap.unicodes
+ oparser = usedmap and makenameparser(cidinfo.ordering)
+ cidnames = usedmap.names
+ cidcodes = usedmap.unicodes
end
- uparser = makenameparser()
- local ns, nl = 0, 0
+ local ns = 0
+ local nl = 0
+ --
for unic, glyph in next, descriptions do
- local index = glyph.index
- local name = glyph.name
- local r = overloads[name]
- if r then
- -- get rid of weird ligatures
- -- glyph.name = r.name
- glyph.unicode = r.unicode
- elseif unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then
- local unicode = lumunic and lumunic[name] or unicodevector[name]
- if unicode then
- glyph.unicode = unicode
- ns = ns + 1
- end
- -- cidmap heuristics, beware, there is no guarantee for a match unless
- -- the chain resolves
- if (not unicode) and usedmap then
- local foundindex = lpegmatch(oparser,name)
- if foundindex then
- unicode = cidcodes[foundindex] -- name to number
- if unicode then
- glyph.unicode = unicode
- ns = ns + 1
- else
- local reference = cidnames[foundindex] -- number to name
- if reference then
- local foundindex = lpegmatch(oparser,reference)
- if foundindex then
- unicode = cidcodes[foundindex]
- if unicode then
- glyph.unicode = unicode
- ns = ns + 1
+ local name = glyph.name
+ if name then
+ local index = glyph.index
+ local r = overloads[name]
+ if r then
+ -- get rid of weird ligatures
+ -- glyph.name = r.name
+ glyph.unicode = r.unicode
+ elseif not unic or unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then
+ local unicode = unicodevector[name] or contextvector[name]
+ if unicode then
+ glyph.unicode = unicode
+ ns = ns + 1
+ end
+ -- cidmap heuristics, beware, there is no guarantee for a match unless
+ -- the chain resolves
+ if (not unicode) and usedmap then
+ local foundindex = lpegmatch(oparser,name)
+ if foundindex then
+ unicode = cidcodes[foundindex] -- name to number
+ if unicode then
+ glyph.unicode = unicode
+ ns = ns + 1
+ else
+ local reference = cidnames[foundindex] -- number to name
+ if reference then
+ local foundindex = lpegmatch(oparser,reference)
+ if foundindex then
+ unicode = cidcodes[foundindex]
+ if unicode then
+ glyph.unicode = unicode
+ ns = ns + 1
+ end
end
- end
- if not unicode or unicode == "" then
- local foundcodes, multiple = lpegmatch(uparser,reference)
- if foundcodes then
- glyph.unicode = foundcodes
- if multiple then
- nl = nl + 1
- unicode = true
- else
- ns = ns + 1
- unicode = foundcodes
+ if not unicode or unicode == "" then
+ local foundcodes, multiple = lpegmatch(uparser,reference)
+ if foundcodes then
+ glyph.unicode = foundcodes
+ if multiple then
+ nl = nl + 1
+ unicode = true
+ else
+ ns = ns + 1
+ unicode = foundcodes
+ end
end
end
end
end
end
end
- end
- -- a.whatever or a_b_c.whatever or a_b_c (no numbers) a.b_
- --
- -- It is not trivial to find a solution that suits all fonts. We tried several alternatives
- -- and this one seems to work reasonable also with fonts that use less standardized naming
- -- schemes. The extra private test is tested by KE and seems to work okay with non-typical
- -- fonts as well.
- --
- -- The next time I look into this, I'll add an extra analysis step to the otf loader (we can
- -- resolve some tounicodes by looking into the gsub data tables that are bound to glyphs.
- --
--- a real tricky last resort:
---
--- local lookups = glyph.lookups
--- if lookups then
--- for _, lookup in next, lookups do -- assume consistency else we need to sort
--- for i=1,#lookup do
--- local l = lookup[i]
--- if l.type == "ligature" then
--- local s = l.specification
--- if s.char == glyph.name then
--- local components = s.components
--- if components then
--- local t, n = { }, 0
--- unicode = true
--- for l=1,#components do
--- local base = components[l]
--- local u = unicodes[base] or unicodevector[base]
--- if not u then
--- break
--- elseif type(u) == "table" then
--- if u[1] >= private then
--- unicode = false
--- break
--- end
--- n = n + 1
--- t[n] = u[1]
--- else
--- if u >= private then
--- unicode = false
--- break
--- end
--- n = n + 1
--- t[n] = u
--- end
--- end
--- if n == 0 then -- done then
--- -- nothing
--- elseif n == 1 then
--- glyph.unicode = t[1]
--- else
--- glyph.unicode = t
--- end
--- nl = nl + 1
--- break
--- end
--- end
--- end
--- end
--- if unicode then
--- break
--- end
--- end
--- end
- if not unicode or unicode == "" then
- local split = lpegmatch(namesplitter,name)
- local nsplit = split and #split or 0
- local t, n = { }, 0
- unicode = true
- for l=1,nsplit do
- local base = split[l]
- local u = unicodes[base] or unicodevector[base]
- if not u then
- break
- elseif type(u) == "table" then
- if u[1] >= private then
- unicode = false
- break
+ -- a.whatever or a_b_c.whatever or a_b_c (no numbers) a.b_
+ --
+ -- It is not trivial to find a solution that suits all fonts. We tried several alternatives
+ -- and this one seems to work reasonable also with fonts that use less standardized naming
+ -- schemes. The extra private test is tested by KE and seems to work okay with non-typical
+ -- fonts as well.
+ --
+ if not unicode or unicode == "" then
+ local split = lpegmatch(namesplitter,name)
+ local nsplit = split and #split or 0 -- add if
+ if nsplit == 0 then
+ -- skip
+ elseif nsplit == 1 then
+ local base = split[1]
+ local u = unicodes[base] or unicodevector[base] or contextvector[name]
+ if not u then
+ -- skip
+ elseif type(u) == "table" then
+ -- unlikely
+ if u[1] < private then
+ unicode = u
+ glyph.unicode = unicode
+ end
+ elseif u < private then
+ unicode = u
+ glyph.unicode = unicode
end
- n = n + 1
- t[n] = u[1]
else
- if u >= private then
- unicode = false
- break
- end
- n = n + 1
- t[n] = u
- end
- end
- if n == 0 then -- done then
- -- nothing
- elseif n == 1 then
- glyph.unicode = t[1]
- else
- glyph.unicode = t
- end
- nl = nl + 1
- end
- -- last resort (we might need to catch private here as well)
- if not unicode or unicode == "" then
- local foundcodes, multiple = lpegmatch(uparser,name)
- if foundcodes then
- glyph.unicode = foundcodes
- if multiple then
- nl = nl + 1
- unicode = true
- else
- ns = ns + 1
- unicode = foundcodes
- end
- end
- end
- -- check using substitutes and alternates
- local r = overloads[unicode]
- if r then
- unicode = r.unicode
- glyph.unicode = unicode
- end
- --
- if not unicode then
- missing[name] = true
- end
- end
- end
- if next(missing) then
- local guess = { }
- -- helper
- local function check(gname,code,unicode)
- local description = descriptions[code]
- -- no need to add a self reference
- local variant = description.name
- if variant == gname then
- return
- end
- -- the variant already has a unicode (normally that resultrs in a default tounicode to self)
- local unic = unicodes[variant]
- if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then
- -- no default mapping and therefore maybe no tounicode yet
- else
- return
- end
- -- the variant already has a tounicode
- if descriptions[code].unicode then
- return
- end
- -- add to the list
- local g = guess[variant]
- -- local r = overloads[unicode]
- -- if r then
- -- unicode = r.unicode
- -- end
- if g then
- g[gname] = unicode
- else
- guess[variant] = { [gname] = unicode }
- end
- end
- --
- for unicode, description in next, descriptions do
- local slookups = description.slookups
- if slookups then
- local gname = description.name
- for tag, data in next, slookups do
- local lookuptype = lookuptypes[tag]
- if lookuptype == "alternate" then
- for i=1,#data do
- check(gname,data[i],unicode)
- end
- elseif lookuptype == "substitution" then
- check(gname,data,unicode)
- end
- end
- end
- local mlookups = description.mlookups
- if mlookups then
- local gname = description.name
- for tag, list in next, mlookups do
- local lookuptype = lookuptypes[tag]
- if lookuptype == "alternate" then
- for i=1,#list do
- local data = list[i]
- for i=1,#data do
- check(gname,data[i],unicode)
+ local t, n = { }, 0
+ for l=1,nsplit do
+ local base = split[l]
+ local u = unicodes[base] or unicodevector[base] or contextvector[name]
+ if not u then
+ break
+ elseif type(u) == "table" then
+ if u[1] >= private then
+ break
+ end
+ n = n + 1
+ t[n] = u[1]
+ else
+ if u >= private then
+ break
+ end
+ n = n + 1
+ t[n] = u
end
end
- elseif lookuptype == "substitution" then
- for i=1,#list do
- check(gname,list[i],unicode)
+ if n > 0 then
+ if n == 1 then
+ unicode = t[1]
+ else
+ unicode = t
+ end
+ glyph.unicode = unicode
end
end
+ nl = nl + 1
end
- end
- end
- -- resolve references
- local done = true
- while done do
- done = false
- for k, v in next, guess do
- if type(v) ~= "number" then
- for kk, vv in next, v do
- if vv == -1 or vv >= private or (vv >= 0xE000 and vv <= 0xF8FF) or vv == 0xFFFE or vv == 0xFFFF then
- local uu = guess[kk]
- if type(uu) == "number" then
- guess[k] = uu
- done = true
- end
+ -- last resort (we might need to catch private here as well)
+ if not unicode or unicode == "" then
+ local foundcodes, multiple = lpegmatch(uparser,name)
+ if foundcodes then
+ glyph.unicode = foundcodes
+ if multiple then
+ nl = nl + 1
+ unicode = true
else
- guess[k] = vv
- done = true
+ ns = ns + 1
+ unicode = foundcodes
end
end
end
- end
- end
- -- wrap up
- local orphans = 0
- local guessed = 0
- for k, v in next, guess do
- if type(v) == "number" then
- descriptions[unicodes[k]].unicode = descriptions[v].unicode or v -- can also be a table
- guessed = guessed + 1
- else
- local t = nil
- local l = lower(k)
- local u = unicodes[l]
- if not u then
- orphans = orphans + 1
- elseif u == -1 or u >= private or (u >= 0xE000 and u <= 0xF8FF) or u == 0xFFFE or u == 0xFFFF then
- local unicode = descriptions[u].unicode
- if unicode then
- descriptions[unicodes[k]].unicode = unicode
- guessed = guessed + 1
- else
- orphans = orphans + 1
- end
- else
- orphans = orphans + 1
+ -- check using substitutes and alternates
+ local r = overloads[unicode]
+ if r then
+ unicode = r.unicode
+ glyph.unicode = unicode
+ end
+ --
+ if not unicode then
+ missing[unic] = true
+ nofmissing = nofmissing + 1
end
end
+ else
+ -- no name
end
- if trace_loading and orphans > 0 or guessed > 0 then
- report_fonts("%s glyphs with no related unicode, %s guessed, %s orphans",guessed+orphans,guessed,orphans)
- end
end
+ if type(checklookups) == "function" then
+ checklookups(data,missing,nofmissing)
+ end
+ -- todo: go lowercase
if trace_mapping then
for unic, glyph in table.sortedhash(descriptions) do
local name = glyph.name
diff --git a/src/fontloader/misc/fontloader-font-otb.lua b/src/fontloader/misc/fontloader-font-otb.lua
index 4e955a1..c9f5d4a 100644
--- a/src/fontloader/misc/fontloader-font-otb.lua
+++ b/src/fontloader/misc/fontloader-font-otb.lua
@@ -321,14 +321,14 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis
for unicode, character in next, characters do
local description = descriptions[unicode]
- local lookups = description.slookups
+ local lookups = description.slookups
if lookups then
for l=1,#lookuplist do
local lookupname = lookuplist[l]
local lookupdata = lookups[lookupname]
if lookupdata then
local lookuptype = lookuptypes[lookupname]
- local action = actions[lookuptype]
+ local action = actions[lookuptype]
if action then
action(lookupdata,lookuptags,lookupname,description,unicode)
end
@@ -342,7 +342,7 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis
local lookuplist = lookups[lookupname]
if lookuplist then
local lookuptype = lookuptypes[lookupname]
- local action = actions[lookuptype]
+ local action = actions[lookuptype]
if action then
for i=1,#lookuplist do
action(lookuplist[i],lookuptags,lookupname,description,unicode)
@@ -614,8 +614,8 @@ local function featuresinitializer(tfmdata,value)
local collectlookups = otf.collectlookups
local rawdata = tfmdata.shared.rawdata
local properties = tfmdata.properties
- local script = properties.script
- local language = properties.language
+ local script = properties.script -- or "dflt" -- can be nil
+ local language = properties.language -- or "dflt" -- can be nil
local basesubstitutions = rawdata.resources.features.gsub
local basepositionings = rawdata.resources.features.gpos
--
diff --git a/src/fontloader/misc/fontloader-font-otf.lua b/src/fontloader/misc/fontloader-font-otf.lua
index e7a97c6..f709e70 100644
--- a/src/fontloader/misc/fontloader-font-otf.lua
+++ b/src/fontloader/misc/fontloader-font-otf.lua
@@ -12,15 +12,19 @@ if not modules then modules = { } end modules ['font-otf'] = {
-- to_table -> totable
-- ascent descent
+-- to be checked: combinations like:
+--
+-- current="ABCD" with [A]=nothing, [BC]=ligature, [D]=single (applied to result of BC so funny index)
+--
+-- unlikely but possible
+
-- more checking against low level calls of functions
local utfbyte = 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 gmatch, gsub, find, match, lower, strip = string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip
local type, next, tonumber, tostring = type, next, tonumber, tostring
local abs = math.abs
-local insert = table.insert
-local lpegmatch = lpeg.match
-local reversed, concat, remove, sortedkeys = table.reversed, table.concat, table.remove, table.sortedkeys
+local reversed, concat, insert, remove, sortedkeys = table.reversed, table.concat, table.insert, table.remove, table.sortedkeys
local ioflush = io.flush
local fastcopy, tohash, derivetable = table.fastcopy, table.tohash, table.derive
local formatters = string.formatters
@@ -54,7 +58,7 @@ local otf = fonts.handlers.otf
otf.glists = { "gsub", "gpos" }
-otf.version = 2.812 -- beware: also sync font-mis.lua
+otf.version = 2.819 -- beware: also sync font-mis.lua and in mtx-fonts
otf.cache = containers.define("fonts", "otf", otf.version, true)
local hashes = fonts.hashes
@@ -283,13 +287,17 @@ local ordered_enhancers = {
"check glyphs",
"check metadata",
- "check extra features", -- after metadata
+-- "check extra features", -- after metadata
"prepare tounicode",
"check encoding", -- moved
"add duplicates",
+ "expand lookups", -- a temp hack awaiting the lua loader
+
+-- "check extra features", -- after metadata and duplicates
+
"cleanup tables",
"compact lookups",
@@ -386,6 +394,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
if featurefile then
name = name .. "@" .. file.removesuffix(file.basename(featurefile))
end
+ -- or: sub = tonumber(sub)
if sub == "" then
sub = false
end
@@ -442,6 +451,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
end
end
if reload then
+ starttiming("fontloader")
report_otf("loading %a, hash %a",filename,hash)
local fontdata, messages
if sub then
@@ -476,6 +486,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
data = {
size = size,
time = time,
+ subfont = sub,
format = otf_format(filename),
featuredata = featurefiles,
resources = {
@@ -512,7 +523,6 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
tounicodetable = Ct(splitter),
},
}
- starttiming(data)
report_otf("file size: %s", size)
enhancers.apply(data,filename,fontdata)
local packtime = { }
@@ -529,10 +539,10 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
if cleanup > 1 then
collectgarbage("collect")
end
- stoptiming(data)
+ stoptiming("fontloader")
if elapsedtime then -- not in generic
- report_otf("preprocessing and caching time %s, packtime %s",
- elapsedtime(data),packdata and elapsedtime(packtime) or 0)
+ report_otf("loading, optimizing, packing and caching time %s, pack time %s",
+ elapsedtime("fontloader"),packdata and elapsedtime(packtime) or 0)
end
close_font(fontdata) -- free memory
if cleanup > 3 then
@@ -543,6 +553,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
collectgarbage("collect")
end
else
+ stoptiming("fontloader")
data = nil
report_otf("loading failed due to read error")
end
@@ -589,6 +600,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
applyruntimefixes(filename,data)
end
enhance("add dimensions",data,filename,nil,false)
+enhance("check extra features",data,filename)
if trace_sequences then
showfeatureorder(data,filename)
end
@@ -697,7 +709,7 @@ local function somecopy(old) -- fast one
end
end
--- not setting hasitalics and class (when nil) during table cronstruction can save some mem
+-- not setting hasitalics and class (when nil) during table construction can save some mem
actions["prepare glyphs"] = function(data,filename,raw)
local tableversion = tonumber(raw.table_version) or 0
@@ -780,7 +792,7 @@ actions["prepare glyphs"] = function(data,filename,raw)
end
if not unicode or unicode == -1 then -- or unicode >= criterium then
if not name then
- name = format("u%06X.ctx",private)
+ name = formatters["u%06X.ctx"](private)
end
unicode = private
unicodes[name] = private
@@ -803,7 +815,7 @@ actions["prepare glyphs"] = function(data,filename,raw)
-- end
-- end
if not name then
- name = format("u%06X.ctx",unicode)
+ name = formatters["u%06X.ctx"](unicode)
end
unicodes[name] = unicode
nofunicodes = nofunicodes + 1
@@ -819,35 +831,35 @@ actions["prepare glyphs"] = function(data,filename,raw)
glyph = glyph,
}
descriptions[unicode] = description
-local altuni = glyph.altuni
-if altuni then
- -- local d
- for i=1,#altuni do
- local a = altuni[i]
- local u = a.unicode
- if u ~= unicode then
- local v = a.variant
- if v then
- -- tricky: no addition to d? needs checking but in practice such dups are either very simple
- -- shapes or e.g cjk with not that many features
- local vv = variants[v]
- if vv then
- vv[u] = unicode
- else -- xits-math has some:
- vv = { [u] = unicode }
- variants[v] = vv
- end
- -- elseif d then
- -- d[#d+1] = u
- -- else
- -- d = { u }
- end
- end
- end
- -- if d then
- -- duplicates[unicode] = d -- is this needed ?
- -- end
-end
+ local altuni = glyph.altuni
+ if altuni then
+ -- local d
+ for i=1,#altuni do
+ local a = altuni[i]
+ local u = a.unicode
+ if u ~= unicode then
+ local v = a.variant
+ if v then
+ -- tricky: no addition to d? needs checking but in practice such dups are either very simple
+ -- shapes or e.g cjk with not that many features
+ local vv = variants[v]
+ if vv then
+ vv[u] = unicode
+ else -- xits-math has some:
+ vv = { [u] = unicode }
+ variants[v] = vv
+ end
+ -- elseif d then
+ -- d[#d+1] = u
+ -- else
+ -- d = { u }
+ end
+ end
+ end
+ -- if d then
+ -- duplicates[unicode] = d -- is this needed ?
+ -- end
+ end
end
end
else
@@ -916,7 +928,7 @@ end
end
indices[index] = unicode
-- if not name then
- -- name = format("u%06X",unicode) -- u%06X.ctx
+ -- name = formatters["u%06X"](unicode) -- u%06X.ctx
-- end
descriptions[unicode] = {
-- width = glyph.width,
@@ -1089,7 +1101,7 @@ actions["add duplicates"] = function(data,filename,raw)
end
if u > 0 then -- and
local duplicate = table.copy(description) -- else packing problem
- duplicate.comment = format("copy of U+%05X", unicode)
+ duplicate.comment = formatters["copy of %U"](unicode)
descriptions[u] = duplicate
-- validduplicates[#validduplicates+1] = u
if trace_loading then
@@ -1107,16 +1119,16 @@ end
-- boundingbox: split into ht/dp takes more memory (larger tables and less sharing)
actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this in the previous
- local descriptions = data.descriptions
- local resources = data.resources
- local metadata = data.metadata
- local properties = data.properties
- local hasitalics = false
- local widths = { }
- local marks = { } -- always present (saves checking)
+ local descriptions = data.descriptions
+ local resources = data.resources
+ local metadata = data.metadata
+ local properties = data.properties
+ local hasitalics = false
+ local widths = { }
+ local marks = { } -- always present (saves checking)
for unicode, description in next, descriptions do
- local glyph = description.glyph
- local italic = glyph.italic_correction
+ local glyph = description.glyph
+ local italic = glyph.italic_correction -- only in a math font (we also have vert/horiz)
if not italic then
-- skip
elseif italic == 0 then
@@ -1185,7 +1197,8 @@ end
actions["reorganize features"] = function(data,filename,raw) -- combine with other
local features = { }
data.resources.features = features
- for k, what in next, otf.glists do
+ for k=1,#otf.glists do
+ local what = otf.glists[k]
local dw = raw[what]
if dw then
local f = { }
@@ -1254,6 +1267,140 @@ actions["reorganize anchor classes"] = function(data,filename,raw)
end
end
+-- local function checklookups(data,missing,nofmissing)
+-- local resources = data.resources
+-- local unicodes = resources.unicodes
+-- local lookuptypes = resources.lookuptypes
+-- if not unicodes or not lookuptypes then
+-- return
+-- elseif nofmissing <= 0 then
+-- return
+-- end
+-- local descriptions = data.descriptions
+-- local private = fonts.constructors and fonts.constructors.privateoffset or 0xF0000 -- 0x10FFFF
+-- --
+-- local ns, nl = 0, 0
+
+-- local guess = { }
+-- -- helper
+-- local function check(gname,code,unicode)
+-- local description = descriptions[code]
+-- -- no need to add a self reference
+-- local variant = description.name
+-- if variant == gname then
+-- return
+-- end
+-- -- the variant already has a unicode (normally that results in a default tounicode to self)
+-- local unic = unicodes[variant]
+-- if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then
+-- -- no default mapping and therefore maybe no tounicode yet
+-- else
+-- return
+-- end
+-- -- the variant already has a tounicode
+-- if descriptions[code].unicode then
+-- return
+-- end
+-- -- add to the list
+-- local g = guess[variant]
+-- -- local r = overloads[unicode]
+-- -- if r then
+-- -- unicode = r.unicode
+-- -- end
+-- if g then
+-- g[gname] = unicode
+-- else
+-- guess[variant] = { [gname] = unicode }
+-- end
+-- end
+-- --
+-- for unicode, description in next, descriptions do
+-- local slookups = description.slookups
+-- if slookups then
+-- local gname = description.name
+-- for tag, data in next, slookups do
+-- local lookuptype = lookuptypes[tag]
+-- if lookuptype == "alternate" then
+-- for i=1,#data do
+-- check(gname,data[i],unicode)
+-- end
+-- elseif lookuptype == "substitution" then
+-- check(gname,data,unicode)
+-- end
+-- end
+-- end
+-- local mlookups = description.mlookups
+-- if mlookups then
+-- local gname = description.name
+-- for tag, list in next, mlookups do
+-- local lookuptype = lookuptypes[tag]
+-- if lookuptype == "alternate" then
+-- for i=1,#list do
+-- local data = list[i]
+-- for i=1,#data do
+-- check(gname,data[i],unicode)
+-- end
+-- end
+-- elseif lookuptype == "substitution" then
+-- for i=1,#list do
+-- check(gname,list[i],unicode)
+-- end
+-- end
+-- end
+-- end
+-- end
+-- -- resolve references
+-- local done = true
+-- while done do
+-- done = false
+-- for k, v in next, guess do
+-- if type(v) ~= "number" then
+-- for kk, vv in next, v do
+-- if vv == -1 or vv >= private or (vv >= 0xE000 and vv <= 0xF8FF) or vv == 0xFFFE or vv == 0xFFFF then
+-- local uu = guess[kk]
+-- if type(uu) == "number" then
+-- guess[k] = uu
+-- done = true
+-- end
+-- else
+-- guess[k] = vv
+-- done = true
+-- end
+-- end
+-- end
+-- end
+-- end
+-- -- wrap up
+-- local orphans = 0
+-- local guessed = 0
+-- for k, v in next, guess do
+-- if type(v) == "number" then
+-- descriptions[unicodes[k]].unicode = descriptions[v].unicode or v -- can also be a table
+-- guessed = guessed + 1
+-- else
+-- local t = nil
+-- local l = lower(k)
+-- local u = unicodes[l]
+-- if not u then
+-- orphans = orphans + 1
+-- elseif u == -1 or u >= private or (u >= 0xE000 and u <= 0xF8FF) or u == 0xFFFE or u == 0xFFFF then
+-- local unicode = descriptions[u].unicode
+-- if unicode then
+-- descriptions[unicodes[k]].unicode = unicode
+-- guessed = guessed + 1
+-- else
+-- orphans = orphans + 1
+-- end
+-- else
+-- orphans = orphans + 1
+-- end
+-- end
+-- end
+-- if trace_loading and orphans > 0 or guessed > 0 then
+-- report_otf("%s glyphs with no related unicode, %s guessed, %s orphans",guessed+orphans,guessed,orphans)
+-- end
+-- end
+
actions["prepare tounicode"] = function(data,filename,raw)
fonts.mappings.addtounicode(data,filename)
end
@@ -1288,8 +1435,9 @@ actions["reorganize subtables"] = function(data,filename,raw)
local lookups = { }
local chainedfeatures = { }
resources.sequences = sequences
- resources.lookups = lookups
- for _, what in next, otf.glists do
+ resources.lookups = lookups -- we also have lookups in data itself
+ for k=1,#otf.glists do
+ local what = otf.glists[k]
local dw = raw[what]
if dw then
for k=1,#dw do
@@ -1375,12 +1523,6 @@ actions["reorganize subtables"] = function(data,filename,raw)
end
end
--- test this:
---
--- for _, what in next, otf.glists do
--- raw[what] = nil
--- end
-
actions["prepare lookups"] = function(data,filename,raw)
local lookups = raw.lookups
if lookups then
@@ -1494,12 +1636,16 @@ end
actions["reorganize lookups"] = function(data,filename,raw) -- we could check for "" and n == 0
-- we prefer the before lookups in a normal order
if data.lookups then
- local splitter = data.helpers.tounicodetable
- local t_u_cache = { }
- local s_u_cache = t_u_cache -- string keys
- local t_h_cache = { }
- local s_h_cache = t_h_cache -- table keys (so we could use one cache)
- local r_u_cache = { } -- maybe shared
+ local helpers = data.helpers
+ local duplicates = data.resources.duplicates
+ local splitter = helpers.tounicodetable
+ local t_u_cache = { }
+ local s_u_cache = t_u_cache -- string keys
+ local t_h_cache = { }
+ local s_h_cache = t_h_cache -- table keys (so we could use one cache)
+ local r_u_cache = { } -- maybe shared
+ helpers.matchcache = t_h_cache -- so that we can add duplicates
+ --
for _, lookup in next, data.lookups do
local rules = lookup.rules
if rules then
@@ -1653,6 +1799,50 @@ actions["reorganize lookups"] = function(data,filename,raw) -- we could check fo
end
end
+actions["expand lookups"] = function(data,filename,raw) -- we could check for "" and n == 0
+ if data.lookups then
+ local cache = data.helpers.matchcache
+ if cache then
+ local duplicates = data.resources.duplicates
+ for key, hash in next, cache do
+ local done = nil
+ for key in next, hash do
+ local unicode = duplicates[key]
+ if not unicode then
+ -- no duplicate
+ elseif type(unicode) == "table" then
+ -- multiple duplicates
+ for i=1,#unicode do
+ local u = unicode[i]
+ if hash[u] then
+ -- already in set
+ elseif done then
+ done[u] = key
+ else
+ done = { [u] = key }
+ end
+ end
+ else
+ -- one duplicate
+ if hash[unicode] then
+ -- already in set
+ elseif done then
+ done[unicode] = key
+ else
+ done = { [unicode] = key }
+ end
+ end
+ end
+ if done then
+ for u in next, done do
+ hash[u] = true
+ end
+ end
+ end
+ end
+ end
+end
+
local function check_variants(unicode,the_variants,splitter,unicodes)
local variants = the_variants.variants
if variants then -- use splitter
@@ -1693,11 +1883,11 @@ local function check_variants(unicode,the_variants,splitter,unicodes)
parts = nil
end
end
- local italic_correction = the_variants.italic_correction
- if italic_correction and italic_correction == 0 then
- italic_correction = nil
+ local italic = the_variants.italic
+ if italic and italic == 0 then
+ italic = nil
end
- return variants, parts, italic_correction
+ return variants, parts, italic
end
actions["analyze math"] = function(data,filename,raw)
@@ -1706,15 +1896,16 @@ actions["analyze math"] = function(data,filename,raw)
local unicodes = data.resources.unicodes
local splitter = data.helpers.tounicodetable
for unicode, description in next, data.descriptions do
- local glyph = description.glyph
- local mathkerns = glyph.mathkern -- singular
- local horiz_variants = glyph.horiz_variants
- local vert_variants = glyph.vert_variants
- local top_accent = glyph.top_accent
- if mathkerns or horiz_variants or vert_variants or top_accent then
+ local glyph = description.glyph
+ local mathkerns = glyph.mathkern -- singular
+ local hvariants = glyph.horiz_variants
+ local vvariants = glyph.vert_variants
+ local accent = glyph.top_accent
+ local italic = glyph.italic_correction
+ if mathkerns or hvariants or vvariants or accent or italic then
local math = { }
- if top_accent then
- math.top_accent = top_accent
+ if accent then
+ math.accent = accent
end
if mathkerns then
for k, v in next, mathkerns do
@@ -1730,15 +1921,14 @@ actions["analyze math"] = function(data,filename,raw)
end
math.kerns = mathkerns
end
- if horiz_variants then
- math.horiz_variants, math.horiz_parts, math.horiz_italic_correction = check_variants(unicode,horiz_variants,splitter,unicodes)
+ if hvariants then
+ math.hvariants, math.hparts, math.hitalic = check_variants(unicode,hvariants,splitter,unicodes)
end
- if vert_variants then
- math.vert_variants, math.vert_parts, math.vert_italic_correction = check_variants(unicode,vert_variants,splitter,unicodes)
+ if vvariants then
+ math.vvariants, math.vparts, math.vitalic = check_variants(unicode,vvariants,splitter,unicodes)
end
- local italic_correction = description.italic
- if italic_correction and italic_correction ~= 0 then
- math.italic_correction = italic_correction
+ if italic and italic ~= 0 then
+ math.italic = italic
end
description.math = math
end
@@ -1809,9 +1999,10 @@ actions["merge kern classes"] = function(data,filename,raw)
local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes
local lookup = subtable.lookup or subtable.name
if kernclass then -- the next one is quite slow
+ -- as fas as i can see the kernclass is a table with one entry and offsets
+ -- have no [1] so we could remov eon elevel (kernclass) and start offsets
+ -- at 1 but we're too far down the road now to fix that
if #kernclass > 0 then
- -- it's a table with one entry .. a future luatex can just
- -- omit that level
kernclass = kernclass[1]
lookup = type(kernclass.lookup) == "string" and kernclass.lookup or lookup
report_otf("fixing kernclass table of lookup %a",lookup)
@@ -1838,15 +2029,17 @@ actions["merge kern classes"] = function(data,filename,raw)
if splt then
local extrakerns = { }
local baseoffset = (fk-1) * maxseconds
- for sk=2,maxseconds do -- will become 1 based in future luatex
- local sv = seconds[sk]
-- for sk, sv in next, seconds do
- local splt = split[sv]
- if splt then -- redundant test
- local offset = offsets[baseoffset + sk]
- if offset then
- for i=1,#splt do
- extrakerns[splt[i]] = offset
+ for sk=2,maxseconds do
+ local sv = seconds[sk]
+ if sv then
+ local splt = split[sv]
+ if splt then -- redundant test
+ local offset = offsets[baseoffset + sk]
+ if offset then
+ for i=1,#splt do
+ extrakerns[splt[i]] = offset
+ end
end
end
end
@@ -1903,7 +2096,7 @@ actions["merge kern classes"] = function(data,filename,raw)
report_otf("%s kern overloads ignored",ignored)
end
if blocked > 0 then
- report_otf("%s succesive kerns blocked",blocked)
+ report_otf("%s successive kerns blocked",blocked)
end
end
end
@@ -1940,18 +2133,21 @@ actions["check metadata"] = function(data,filename,raw)
end
end
--
+ local names = raw.names
+ --
if metadata.validation_state and table.contains(metadata.validation_state,"bad_ps_fontname") then
-- the ff library does a bit too much (and wrong) checking ... so we need to catch this
-- at least for now
local function valid(what)
- local names = raw.names
- for i=1,#names do
- local list = names[i]
- local names = list.names
- if names then
- local name = names[what]
- if name and valid_ps_name(name) then
- return name
+ if names then
+ for i=1,#names do
+ local list = names[i]
+ local names = list.names
+ if names then
+ local name = names[what]
+ if name and valid_ps_name(name) then
+ return name
+ end
end
end
end
@@ -1975,6 +2171,33 @@ actions["check metadata"] = function(data,filename,raw)
check("fullname")
end
--
+ if names then
+ local psname = metadata.psname
+ if not psname or psname == "" then
+ for i=1,#names do
+ local name = names[i]
+ -- Currently we use the same restricted search as in the new context (specific) font loader
+ -- but we might add more lang checks (it worked ok in the new loaded so now we're in sync)
+ -- This check here is also because there are (esp) cjk fonts out there with psnames different
+ -- from fontnames (gives a bad lookup in backend).
+ if lower(name.lang) == "english (us)" then
+ local specification = name.names
+ if specification then
+ local postscriptname = specification.postscriptname
+ if postscriptname then
+ psname = postscriptname
+ end
+ end
+ end
+ break
+ end
+ end
+ if psname ~= metadata.fontname then
+ report_otf("fontname %a, fullname %a, psname %a",metadata.fontname,metadata.fullname,psname)
+ end
+ metadata.psname = psname
+ end
+ --
end
actions["cleanup tables"] = function(data,filename,raw)
@@ -2000,7 +2223,9 @@ end
-- we can share { } as it is never set
---- ligatures have an extra specification.char entry that we don't use
+-- ligatures have an extra specification.char entry that we don't use
+
+-- mlookups only with pairs and ligatures
actions["reorganize glyph lookups"] = function(data,filename,raw)
local resources = data.resources
@@ -2081,14 +2306,14 @@ actions["reorganize glyph lookups"] = function(data,filename,raw)
if mlookups then
description.mlookups = mlookups
end
+ -- description.lookups = nil
end
end
-
end
local zero = { 0, 0 }
-actions["reorganize glyph anchors"] = function(data,filename,raw) -- when we replace inplace we safe entries
+actions["reorganize glyph anchors"] = function(data,filename,raw)
local descriptions = data.descriptions
for unicode, description in next, descriptions do
local anchors = description.glyph.anchors
@@ -2311,7 +2536,7 @@ end
-- we cannot share descriptions as virtual fonts might extend them (ok,
-- we could use a cache with a hash
--
--- we already assing an empty tabel to characters as we can add for
+-- we already assign an empty tabel to characters as we can add for
-- instance protruding info and loop over characters; one is not supposed
-- to change descriptions and if one does so one should make a copy!
@@ -2334,10 +2559,14 @@ local function copytotfm(data,cache_id)
local spaceunits = 500
local spacer = "space"
local designsize = metadata.designsize or metadata.design_size or 100
+ local minsize = metadata.minsize or metadata.design_range_bottom or designsize
+ local maxsize = metadata.maxsize or metadata.design_range_top or designsize
local mathspecs = metadata.math
--
if designsize == 0 then
designsize = 100
+ minsize = 100
+ maxsize = 100
end
if mathspecs then
for name, value in next, mathspecs do
@@ -2355,8 +2584,12 @@ local function copytotfm(data,cache_id)
local m = d.math
if m then
-- watch out: luatex uses horiz_variants for the parts
- local variants = m.horiz_variants
- local parts = m.horiz_parts
+ --
+ local italic = m.italic
+ local vitalic = m.vitalic
+ --
+ local variants = m.hvariants
+ local parts = m.hparts
-- local done = { [unicode] = true }
if variants then
local c = character
@@ -2373,9 +2606,11 @@ local function copytotfm(data,cache_id)
c.horiz_variants = parts
elseif parts then
character.horiz_variants = parts
+ italic = m.hitalic
end
- local variants = m.vert_variants
- local parts = m.vert_parts
+ --
+ local variants = m.vvariants
+ local parts = m.vparts
-- local done = { [unicode] = true }
if variants then
local c = character
@@ -2393,14 +2628,19 @@ local function copytotfm(data,cache_id)
elseif parts then
character.vert_variants = parts
end
- local italic_correction = m.vert_italic_correction
- if italic_correction then
- character.vert_italic_correction = italic_correction -- was c.
+ --
+ if italic and italic ~= 0 then
+ character.italic = italic -- overload
end
- local top_accent = m.top_accent
- if top_accent then
- character.top_accent = top_accent
+ if vitalic and vitalic ~= 0 then
+ character.vert_italic = vitalic
end
+ --
+ local accent = m.accent
+ if accent then
+ character.accent = accent
+ end
+ --
local kerns = m.kerns
if kerns then
character.mathkerns = kerns
@@ -2413,16 +2653,16 @@ local function copytotfm(data,cache_id)
local filename = constructors.checkedfilename(resources)
local fontname = metadata.fontname
local fullname = metadata.fullname or fontname
- local psname = fontname or fullname
- local units = metadata.units_per_em or 1000
+ local psname = metadata.psname or fontname or fullname
+ local units = metadata.units or metadata.units_per_em or 1000
--
if units == 0 then -- catch bugs in fonts
units = 1000 -- maybe 2000 when ttf
- metadata.units_per_em = 1000
+ metadata.units = 1000
report_otf("changing %a units to %a",0,units)
end
--
- local monospaced = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced")
+ local monospaced = metadata.monospaced or metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced")
local charwidth = pfminfo.avgwidth -- or unset
local charxheight = pfminfo.os2_xheight and pfminfo.os2_xheight > 0 and pfminfo.os2_xheight
-- charwidth = charwidth * units/1000
@@ -2492,17 +2732,16 @@ local function copytotfm(data,cache_id)
end
end
--
- parameters.designsize = (designsize/10)*65536
- parameters.ascender = abs(metadata.ascent or 0)
- parameters.descender = abs(metadata.descent or 0)
- parameters.units = units
+ parameters.designsize = (designsize/10)*65536
+ parameters.minsize = (minsize /10)*65536
+ parameters.maxsize = (maxsize /10)*65536
+ parameters.ascender = abs(metadata.ascender or metadata.ascent or 0)
+ parameters.descender = abs(metadata.descender or metadata.descent or 0)
+ parameters.units = units
--
properties.space = spacer
properties.encodingbytes = 2
properties.format = data.format or otf_format(filename) or formats.otf
--- if units ~= 1000 and format ~= "truetype" then
--- properties.format = "truetype"
--- end
properties.noglyphnames = true
properties.filename = filename
properties.fontname = fontname
@@ -2703,3 +2942,111 @@ function otf.scriptandlanguage(tfmdata,attr)
local properties = tfmdata.properties
return properties.script or "dflt", properties.language or "dflt"
end
+
+-- a little bit of abstraction
+
+local function justset(coverage,unicode,replacement)
+ coverage[unicode] = replacement
+end
+
+otf.coverup = {
+ stepkey = "subtables",
+ actions = {
+ substitution = justset,
+ alternate = justset,
+ multiple = justset,
+ ligature = justset,
+ kern = justset,
+ },
+ register = function(coverage,lookuptype,format,feature,n,descriptions,resources)
+ local name = formatters["ctx_%s_%s"](feature,n)
+ if lookuptype == "kern" then
+ resources.lookuptypes[name] = "position"
+ else
+ resources.lookuptypes[name] = lookuptype
+ end
+ for u, c in next, coverage do
+ local description = descriptions[u]
+ local slookups = description.slookups
+ if slookups then
+ slookups[name] = c
+ else
+ description.slookups = { [name] = c }
+ end
+-- inspect(feature,description)
+ end
+ return name
+ end
+}
+
+-- moved from font-oth.lua
+
+local function getgsub(tfmdata,k,kind)
+ local description = tfmdata.descriptions[k]
+ if description then
+ local slookups = description.slookups -- we assume only slookups (we can always extend)
+ if slookups then
+ local shared = tfmdata.shared
+ local rawdata = shared and shared.rawdata
+ if rawdata then
+ local lookuptypes = rawdata.resources.lookuptypes
+ if lookuptypes then
+ local properties = tfmdata.properties
+ -- we could cache these
+ local validlookups, lookuplist = otf.collectlookups(rawdata,kind,properties.script,properties.language)
+ if validlookups then
+ for l=1,#lookuplist do
+ local lookup = lookuplist[l]
+ local found = slookups[lookup]
+ if found then
+ return found, lookuptypes[lookup]
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+otf.getgsub = getgsub -- returns value, gsub_kind
+
+function otf.getsubstitution(tfmdata,k,kind,value)
+ local found, kind = getgsub(tfmdata,k,kind)
+ if not found then
+ --
+ elseif kind == "substitution" then
+ return found
+ elseif kind == "alternate" then
+ local choice = tonumber(value) or 1 -- no random here (yet)
+ return found[choice] or found[1] or k
+ end
+ return k
+end
+
+otf.getalternate = otf.getsubstitution
+
+function otf.getmultiple(tfmdata,k,kind)
+ local found, kind = getgsub(tfmdata,k,kind)
+ if found and kind == "multiple" then
+ return found
+ end
+ return { k }
+end
+
+function otf.getkern(tfmdata,left,right,kind)
+ local kerns = getgsub(tfmdata,left,kind or "kern",true) -- for now we use getsub
+ if kerns then
+ local found = kerns[right]
+ local kind = type(found)
+ if kind == "table" then
+ found = found[1][3] -- can be more clever
+ elseif kind ~= "number" then
+ found = false
+ end
+ if found then
+ return found * tfmdata.parameters.factor
+ end
+ end
+ return 0
+end
diff --git a/src/fontloader/misc/fontloader-font-otp.lua b/src/fontloader/misc/fontloader-font-otp.lua
index ebf36ed..91bd05b 100644
--- a/src/fontloader/misc/fontloader-font-otp.lua
+++ b/src/fontloader/misc/fontloader-font-otp.lua
@@ -12,9 +12,8 @@ if not modules then modules = { } end modules ['font-otp'] = {
--
-- unless we sort all hashes we can get a different pack order (no big deal but size can differ)
-local next, type = next, type
+local next, type, tostring = next, type, tostring
local sort, concat = table.sort, table.concat
-local sortedhash = table.sortedhash
local trace_packing = false trackers.register("otf.packing", function(v) trace_packing = v end)
local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end)
@@ -148,6 +147,7 @@ end
-- we then need to sort more thanks to random hashing
local function packdata(data)
+
if data then
-- stripdata(data)
local h, t, c = { }, { }, { }
@@ -537,6 +537,7 @@ local unpacked_mt = {
}
local function unpackdata(data)
+
if data then
local tables = data.tables
if tables then
diff --git a/src/fontloader/misc/fontloader-font-tfm.lua b/src/fontloader/misc/fontloader-font-tfm.lua
index ab03788..2dd5768 100644
--- a/src/fontloader/misc/fontloader-font-tfm.lua
+++ b/src/fontloader/misc/fontloader-font-tfm.lua
@@ -24,6 +24,9 @@ local constructors = fonts.constructors
local encodings = fonts.encodings
local tfm = constructors.newhandler("tfm")
+tfm.version = 1.000
+tfm.maxnestingdepth = 5
+tfm.maxnestingsize = 65536*1024
local tfmfeatures = constructors.newfeatures("tfm")
local registertfmfeature = tfmfeatures.register
@@ -44,6 +47,18 @@ supplied by <l n='luatex'/>.</p>
-- ofm directive blocks local path search unless set; btw, in context we
-- don't support ofm files anyway as this format is obsolete
+-- we need to deal with nested virtual fonts, but because we load in the
+-- frontend we also need to make sure we don't nest too deep (esp when sizes
+-- get large)
+--
+-- (VTITLE Example of a recursion)
+-- (MAPFONT D 0 (FONTNAME recurse)(FONTAT D 2))
+-- (CHARACTER C A (CHARWD D 1)(CHARHT D 1)(MAP (SETRULE D 1 D 1)))
+-- (CHARACTER C B (CHARWD D 2)(CHARHT D 2)(MAP (SETCHAR C A)))
+-- (CHARACTER C C (CHARWD D 4)(CHARHT D 4)(MAP (SETCHAR C B)))
+--
+-- we added the same checks as below to the luatex engine
+
function tfm.setfeatures(tfmdata,features)
local okay = constructors.initializefeatures("tfm",tfmdata,features,trace_features,report_tfm)
if okay then
@@ -53,9 +68,12 @@ function tfm.setfeatures(tfmdata,features)
end
end
+local depth = { } -- table.setmetatableindex("number")
+
local function read_from_tfm(specification)
- local filename = specification.filename
- local size = specification.size
+ local filename = specification.filename
+ local size = specification.size
+ depth[filename] = (depth[filename] or 0) + 1
if trace_defining then
report_defining("loading tfm file %a at size %s",filename,size)
end
@@ -103,6 +121,25 @@ local function read_from_tfm(specification)
end
properties.virtualized = true
tfmdata.fonts = vfdata.fonts
+ tfmdata.type = "virtual" -- else nested calls with cummulative scaling
+ local fontlist = vfdata.fonts
+ local name = file.nameonly(filename)
+ for i=1,#fontlist do
+ local n = fontlist[i].name
+ local s = fontlist[i].size
+ local d = depth[filename]
+ s = constructors.scaled(s,vfdata.designsize)
+ if d > tfm.maxnestingdepth then
+ report_defining("too deeply nested virtual font %a with size %a, max nesting depth %s",n,s,tfm.maxnestingdepth)
+ fontlist[i] = { id = 0 }
+ elseif (d > 1) and (s > tfm.maxnestingsize) then
+ report_defining("virtual font %a exceeds size %s",n,s)
+ fontlist[i] = { id = 0 }
+ else
+ local t, id = fonts.constructors.readanddefine(n,s)
+ fontlist[i] = { id = id }
+ end
+ end
end
end
end
@@ -121,7 +158,10 @@ local function read_from_tfm(specification)
resources.unicodes = { }
resources.lookuptags = { }
--
+ depth[filename] = depth[filename] - 1
return tfmdata
+ else
+ depth[filename] = depth[filename] - 1
end
end
diff --git a/src/fontloader/misc/fontloader-fonts-cbk.lua b/src/fontloader/misc/fontloader-fonts-cbk.lua
index 81b5b6e..9da8151 100644
--- a/src/fontloader/misc/fontloader-fonts-cbk.lua
+++ b/src/fontloader/misc/fontloader-fonts-cbk.lua
@@ -160,12 +160,31 @@ function nodes.handlers.nodepass(head)
local range = basefonts[i]
local start = range[1]
local stop = range[2]
- if stop then
- start, stop = ligaturing(start,stop)
- start, stop = kerning(start,stop)
- elseif start then
- start = ligaturing(start)
- start = kerning(start)
+ -- maybe even: if start and start ~= stop then
+ if start or stop then
+ local prev = nil
+ local next = nil
+ local front = start == head
+ if stop then
+ next = stop.next
+ start, stop = ligaturing(start,stop)
+ start, stop = kerning(start,stop)
+ elseif start then
+ prev = start.prev
+ start = ligaturing(start)
+ start = kerning(start)
+ end
+ if prev then
+ start.prev = prev
+ prev.next = start
+ end
+ if next then
+ stop.next = next
+ next.prev = stop
+ end
+ if front then
+ head = start
+ end
end
end
end
@@ -176,7 +195,7 @@ function nodes.handlers.nodepass(head)
end
function nodes.handlers.basepass(head)
- if not basepass then
+ if basepass then
head = ligaturing(head)
head = kerning(head)
end
diff --git a/src/fontloader/misc/fontloader-fonts-demo-vf-1.lua b/src/fontloader/misc/fontloader-fonts-demo-vf-1.lua
index 3878ae6..13acd16 100644
--- a/src/fontloader/misc/fontloader-fonts-demo-vf-1.lua
+++ b/src/fontloader/misc/fontloader-fonts-demo-vf-1.lua
@@ -17,6 +17,7 @@ return function(specification)
{ "special", "pdf:1 0 0 rg" },
{ "special", "pdf:0 1 0 rg" },
{ "special", "pdf:0 0 1 rg" },
+ { "special", "pdf:0 0 1 rg" },
}
local chars = {
identifiers[id1].characters,
@@ -26,7 +27,12 @@ return function(specification)
for u, v in next, f1.characters do
local n = math.floor(math.random(1,3)+0.5)
local c = chars[n][u] or v
- v.commands = { color[n], { 'slot', n, u }, color[0] }
+ v.commands = {
+ color[n],
+ { 'slot', n, u },
+ color[0],
+ { 'nop' }
+ }
v.kerns = nil
v.width = c.width
v.height = c.height
diff --git a/src/fontloader/misc/fontloader-fonts-inj.lua b/src/fontloader/misc/fontloader-fonts-inj.lua
index 332e920..36781f7 100644
--- a/src/fontloader/misc/fontloader-fonts-inj.lua
+++ b/src/fontloader/misc/fontloader-fonts-inj.lua
@@ -8,7 +8,16 @@ if not modules then modules = { } end modules ['font-inj'] = {
-- This property based variant is not faster but looks nicer than the attribute one. We
-- need to use rawget (which is apbout 4 times slower than a direct access but we cannot
--- get/set that one for our purpose!
+-- get/set that one for our purpose! This version does a bit more with discretionaries
+-- (and Kai has tested it with his collection of weird fonts.)
+
+-- There is some duplicate code here (especially in the the pre/post/replace branches) but
+-- we go for speed. We could store a list of glyph and mark nodes when registering but it's
+-- cleaner to have an identification pass here. Also, I need to keep tracing in mind so
+-- being too clever here is dangerous.
+
+-- The subtype test is not needed as there will be no (new) properties set, given that we
+-- reset the properties.
if not nodes.properties then return end
@@ -80,8 +89,7 @@ function injections.resetcounts()
keepregisteredcounts = false
end
--- We need to make sure that a possible metatable will not kick in
--- unexpectedly.
+-- We need to make sure that a possible metatable will not kick in unexpectedly.
function injections.reset(n)
local p = rawget(properties,n)
@@ -146,7 +154,8 @@ end
function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) -- hm: nuts or nodes
local dx = factor*(exit[1]-entry[1])
local dy = -factor*(exit[2]-entry[2])
- local ws, wn = tfmstart.width, tfmnext.width
+ local ws = tfmstart.width
+ local wn = tfmnext.width
nofregisteredcursives = nofregisteredcursives + 1
if rlmode < 0 then
dx = -(dx + wn)
@@ -195,7 +204,10 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne
end
function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2lflag & tfmchr not used
- local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4]
+ local x = factor*spec[1]
+ local y = factor*spec[2]
+ local w = factor*spec[3]
+ local h = factor*spec[4]
if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay?
local yoffset = y - h
local leftkern = x -- both kerns are set in a pair kern compared
@@ -205,9 +217,12 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l
if rlmode and rlmode < 0 then
leftkern, rightkern = rightkern, leftkern
end
+ if not injection then
+ injection = "injections"
+ end
local p = rawget(properties,current)
if p then
- local i = rawget(p,"injections")
+ local i = rawget(p,injection)
if i then
if leftkern ~= 0 then
i.leftkern = (i.leftkern or 0) + leftkern
@@ -219,19 +234,19 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l
i.yoffset = (i.yoffset or 0) + yoffset
end
elseif leftkern ~= 0 or rightkern ~= 0 then
- p.injections = {
+ p[injection] = {
leftkern = leftkern,
rightkern = rightkern,
yoffset = yoffset,
}
else
- p.injections = {
+ p[injection] = {
yoffset = yoffset,
}
end
elseif leftkern ~= 0 or rightkern ~= 0 then
properties[current] = {
- injections = {
+ [injection] = {
leftkern = leftkern,
rightkern = rightkern,
yoffset = yoffset,
@@ -239,7 +254,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l
}
else
properties[current] = {
- injections = {
+ [injection] = {
yoffset = yoffset,
},
}
@@ -250,10 +265,9 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l
return x, y, w, h -- no bound
end
--- this needs checking for rl < 0 but it is unlikely that a r2l script
--- uses kernclasses between glyphs so we're probably safe (KE has a
--- problematic font where marks interfere with rl < 0 in the previous
--- case)
+-- This needs checking for rl < 0 but it is unlikely that a r2l script uses kernclasses between
+-- glyphs so we're probably safe (KE has a problematic font where marks interfere with rl < 0 in
+-- the previous case)
function injections.setkern(current,factor,rlmode,x,injection)
local dx = factor * x
@@ -285,7 +299,7 @@ function injections.setkern(current,factor,rlmode,x,injection)
end
end
-function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=baseanchor, ma=markanchor
+function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) -- ba=baseanchor, ma=markanchor
local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2])
nofregisteredmarks = nofregisteredmarks + 1
-- markanchors[nofregisteredmarks] = base
@@ -293,14 +307,20 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=basean
dx = tfmbase.width - dx -- see later commented ox
end
local p = rawget(properties,start)
+ -- hm, dejavu serif does a sloppy mark2mark before mark2base
if p then
local i = rawget(p,"injections")
if i then
- i.markx = dx
- i.marky = dy
- i.markdir = rlmode or 0
- i.markbase = nofregisteredmarks
- i.markbasenode = base
+ if i.markmark then
+ -- out of order mkmk: yes or no or option
+ else
+ i.markx = dx
+ i.marky = dy
+ i.markdir = rlmode or 0
+ i.markbase = nofregisteredmarks
+ i.markbasenode = base
+ i.markmark = mkmk
+ end
else
p.injections = {
markx = dx,
@@ -308,6 +328,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=basean
markdir = rlmode or 0,
markbase = nofregisteredmarks,
markbasenode = base,
+ markmark = mkmk,
}
end
else
@@ -318,6 +339,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=basean
markdir = rlmode or 0,
markbase = nofregisteredmarks,
markbasenode = base,
+ markmark = mkmk,
},
}
end
@@ -430,30 +452,36 @@ local function show_result(head)
end
end
--- we could also check for marks here but maybe not all are registered (needs checking)
-
-local function collect_glyphs_1(head)
- local glyphs, nofglyphs = { }, 0
- local marks, nofmarks = { }, 0
+local function collect_glyphs(head,offsets)
+ local glyphs, glyphi, nofglyphs = { }, { }, 0
+ local marks, marki, nofmarks = { }, { }, 0
local nf, tm = nil, nil
- for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts
- if getsubtype(n) < 256 then
- local f = getfont(n)
- if f ~= nf then
- nf = f
- tm = fontdata[nf].resources.marks -- other hash in ctx
- end
- if tm and tm[getchar(n)] then
- nofmarks = nofmarks + 1
- marks[nofmarks] = n
- else
- nofglyphs = nofglyphs + 1
- glyphs[nofglyphs] = n
+ local n = head
+
+ local function identify(n,what)
+ local f = getfont(n)
+ if f ~= nf then
+ nf = f
+ -- other hash in ctx:
+ tm = fontdata[nf].resources
+ if tm then
+ tm = tm.marks
end
+ end
+ if tm and tm[getchar(n)] then
+ nofmarks = nofmarks + 1
+ marks[nofmarks] = n
+ marki[nofmarks] = "injections"
+ else
+ nofglyphs = nofglyphs + 1
+ glyphs[nofglyphs] = n
+ glyphi[nofglyphs] = what
+ end
+ if offsets then
-- yoffsets can influence curs steps
local p = rawget(properties,n)
if p then
- local i = rawget(p,"injections")
+ local i = rawget(p,what)
if i then
local yoffset = i.yoffset
if yoffset and yoffset ~= 0 then
@@ -463,38 +491,50 @@ local function collect_glyphs_1(head)
end
end
end
- return glyphs, nofglyphs, marks, nofmarks
-end
-local function collect_glyphs_2(head)
- local glyphs, nofglyphs = { }, 0
- local marks, nofmarks = { }, 0
- local nf, tm = nil, nil
- for n in traverse_id(glyph_code,head) do
- if getsubtype(n) < 256 then
- local f = getfont(n)
- if f ~= nf then
- nf = f
- tm = fontdata[nf].resources.marks -- other hash in ctx
- end
- if tm and tm[getchar(n)] then
- nofmarks = nofmarks + 1
- marks[nofmarks] = n
- else
- nofglyphs = nofglyphs + 1
- glyphs[nofglyphs] = n
- end
+ while n do -- only needed for relevant fonts
+ local id = getid(n)
+ if id == glyph_code then
+ identify(n,"injections")
+ elseif id == disc_code then
+ local d = getfield(n,"pre")
+ if d then
+ for n in traverse_id(glyph_code,d) do
+ if getsubtype(n) < 256 then
+ identify(n,"preinjections")
+ end
+ end
+ end
+ local d = getfield(n,"post")
+ if d then
+ for n in traverse_id(glyph_code,d) do
+ if getsubtype(n) < 256 then
+ identify(n,"postinjections")
+ end
+ end
+ end
+ local d = getfield(n,"replace")
+ if d then
+ for n in traverse_id(glyph_code,d) do
+ if getsubtype(n) < 256 then
+ identify(n,"replaceinjections")
+ end
+ end
+ end
end
+ n = getnext(n)
end
- return glyphs, nofglyphs, marks, nofmarks
+
+ return glyphs, glyphi, nofglyphs, marks, marki, nofmarks
end
-local function inject_marks(marks,nofmarks)
+local function inject_marks(marks,marki,nofmarks)
for i=1,nofmarks do
- local n = marks[i]
+ local n = marks[i]
local pn = rawget(properties,n)
if pn then
- pn = rawget(pn,"injections")
+ local ni = marki[i]
+ local pn = rawget(pn,ni)
if pn then
local p = pn.markbasenode
if p then
@@ -503,7 +543,7 @@ local function inject_marks(marks,nofmarks)
local rightkern = nil
local pp = rawget(properties,p)
if pp then
- pp = rawget(pp,"injections")
+ pp = rawget(pp,ni)
if pp then
rightkern = pp.rightkern
end
@@ -516,13 +556,22 @@ local function inject_marks(marks,nofmarks)
else
-- kern(x) glyph(p) kern(w-x) mark(n)
-- ox = px - getfield(p,"width") + pn.markx - pp.leftkern
- local leftkern = pp.leftkern
- if leftkern then
- ox = px - pn.markx
+ --
+ -- According to Kai we don't need to handle leftkern here but I'm
+ -- pretty sure I've run into a case where it was needed so maybe
+ -- some day we need something more clever here.
+ --
+ if false then
+ -- a mark with kerning
+ local leftkern = pp.leftkern
+ if leftkern then
+ ox = px - pn.markx - leftkern
+ else
+ ox = px - pn.markx
+ end
else
- ox = px - pn.markx - leftkern
+ ox = px - pn.markx
end
--- report_injections("l2r case 1: %p",ox)
end
else
-- we need to deal with fonts that have marks with width
@@ -548,12 +597,13 @@ local function inject_marks(marks,nofmarks)
setfield(n,"xoffset",ox)
--
local py = getfield(p,"yoffset")
- local oy = 0
- if marks[p] then
- oy = py + pn.marky
- else
- oy = getfield(n,"yoffset") + py + pn.marky
- end
+-- local oy = 0
+-- if marks[p] then
+-- oy = py + pn.marky
+-- else
+-- oy = getfield(n,"yoffset") + py + pn.marky
+-- end
+ local oy = getfield(n,"yoffset") + py + pn.marky
setfield(n,"yoffset",oy)
else
-- normally this can't happen (only when in trace mode which is a special case anyway)
@@ -564,14 +614,14 @@ local function inject_marks(marks,nofmarks)
end
end
-local function inject_cursives(glyphs,nofglyphs)
+local function inject_cursives(glyphs,glyphi,nofglyphs)
local cursiveanchor, lastanchor = nil, nil
local minc, maxc, last = 0, 0, nil
for i=1,nofglyphs do
- local n = glyphs[i]
+ local n = glyphs[i]
local pn = rawget(properties,n)
if pn then
- pn = rawget(pn,"injections")
+ pn = rawget(pn,glyphi[i])
end
if pn then
local cursivex = pn.cursivex
@@ -630,7 +680,7 @@ local function inject_cursives(glyphs,nofglyphs)
-- if maxc > 0 and not cursiveanchor then
-- local ny = getfield(n,"yoffset")
-- for i=maxc,minc,-1 do
- -- local ti = glyphs[i]
+ -- local ti = glyphs[i][1]
-- ny = ny + properties[ti].cursivedy
-- setfield(ti,"yoffset",ny) -- why not add ?
-- end
@@ -647,23 +697,67 @@ local function inject_cursives(glyphs,nofglyphs)
end
end
-local function inject_kerns(head,list,length)
- -- todo: pre/post/replace
+-- G +D-pre G
+-- D-post+
+-- +D-replace+
+--
+-- G +D-pre +D-pre
+-- D-post +D-post
+-- +D-replace +D-replace
+
+local function inject_kerns(head,glist,ilist,length) -- not complete ! compare with inject_kerns_only (but unlikely disc here)
for i=1,length do
- local n = list[i]
+ local n = glist[i]
local pn = rawget(properties,n)
if pn then
- local i = rawget(pn,"injections")
- if i then
- local leftkern = i.leftkern
- if leftkern and leftkern ~= 0 then
- insert_node_before(head,n,newkern(leftkern)) -- type 0/2
- end
- local rightkern = i.rightkern
- if rightkern and rightkern ~= 0 then
- insert_node_after(head,n,newkern(rightkern)) -- type 0/2
- end
- end
+ local dp = nil
+ local dr = nil
+ local ni = ilist[i]
+ local p = nil
+ if ni == "injections" then
+ p = getprev(n)
+ if p then
+ local id = getid(p)
+ if id == disc_code then
+ dp = getfield(p,"post")
+ dr = getfield(p,"replace")
+ end
+ end
+ end
+ if dp then
+ local i = rawget(pn,"postinjections")
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ local t = find_tail(dp)
+ insert_node_after(dp,t,newkern(leftkern))
+ setfield(p,"post",dp) -- currently we need to force a tail refresh
+ end
+ end
+ end
+ if dr then
+ local i = rawget(pn,"replaceinjections")
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ local t = find_tail(dr)
+ insert_node_after(dr,t,newkern(leftkern))
+ setfield(p,"replace",dr) -- currently we need to force a tail refresh
+ end
+ end
+ else
+ local i = rawget(pn,ni)
+ if i then
+ local leftkern = i.leftkern
+ if leftkern and leftkern ~= 0 then
+ insert_node_before(head,n,newkern(leftkern)) -- type 0/2
+ end
+ local rightkern = i.rightkern
+ if rightkern and rightkern ~= 0 then
+ insert_node_after(head,n,newkern(rightkern)) -- type 0/2
+ end
+ end
+ end
end
end
end
@@ -673,23 +767,18 @@ local function inject_everything(head,where)
if trace_injections then
trace(head,"everything")
end
- local glyphs, nofglyphs, marks, nofmarks
- if nofregisteredpairs > 0 then
- glyphs, nofglyphs, marks, nofmarks = collect_glyphs_1(head)
- else
- glyphs, nofglyphs, marks, nofmarks = collect_glyphs_2(head)
- end
+ local glyphs, glyphi, nofglyphs, marks, marki, nofmarks = collect_glyphs(head,nofregisteredpairs > 0)
if nofglyphs > 0 then
if nofregisteredcursives > 0 then
- inject_cursives(glyphs,nofglyphs)
+ inject_cursives(glyphs,glyphi,nofglyphs)
end
if nofregisteredmarks > 0 then -- and nofmarks > 0
- inject_marks(marks,nofmarks)
+ inject_marks(marks,marki,nofmarks)
end
- inject_kerns(head,glyphs,nofglyphs)
+ inject_kerns(head,glyphs,glyphi,nofglyphs)
end
if nofmarks > 0 then
- inject_kerns(head,marks,nofmarks)
+ inject_kerns(head,marks,marki,nofmarks)
end
if keepregisteredcounts then
keepregisteredcounts = false
@@ -702,13 +791,21 @@ local function inject_everything(head,where)
return tonode(head), true
end
+-- G +D-pre G
+-- D-post+
+-- +D-replace+
+--
+-- G +D-pre +D-pre
+-- D-post +D-post
+-- +D-replace +D-replace
+
local function inject_kerns_only(head,where)
head = tonut(head)
if trace_injections then
trace(head,"kerns")
end
local n = head
- local p = nil
+ local p = nil -- disc node when non-nil
while n do
local id = getid(n)
if id == glyph_code then
@@ -724,6 +821,7 @@ local function inject_kerns_only(head,where)
if leftkern and leftkern ~= 0 then
local t = find_tail(d)
insert_node_after(d,t,newkern(leftkern))
+ setfield(p,"post",d) -- currently we need to force a tail refresh
end
end
end
@@ -735,6 +833,7 @@ local function inject_kerns_only(head,where)
if leftkern and leftkern ~= 0 then
local t = find_tail(d)
insert_node_after(d,t,newkern(leftkern))
+ setfield(p,"replace",d) -- currently we need to force a tail refresh
end
end
else
@@ -747,6 +846,7 @@ local function inject_kerns_only(head,where)
end
end
else
+ -- this is the most common case
local i = rawget(pn,"injections")
if i then
local leftkern = i.leftkern
@@ -756,8 +856,6 @@ local function inject_kerns_only(head,where)
end
end
end
- else
- break
end
p = nil
elseif id == disc_code then
@@ -812,7 +910,7 @@ local function inject_kerns_only(head,where)
local h = d
for n in traverse_id(glyph_code,d) do
if getsubtype(n) < 256 then
- local pn = rawget(properties,n) -- why can it be empty { }
+ local pn = rawget(properties,n)
if pn then
local i = rawget(pn,"replaceinjections")
if i then
@@ -850,9 +948,8 @@ local function inject_pairs_only(head,where)
if trace_injections then
trace(head,"pairs")
end
- --
local n = head
- local p = nil
+ local p = nil -- disc node when non-nil
while n do
local id = getid(n)
if id == glyph_code then
@@ -868,6 +965,7 @@ local function inject_pairs_only(head,where)
if leftkern and leftkern ~= 0 then
local t = find_tail(d)
insert_node_after(d,t,newkern(leftkern))
+ setfield(p,"post",d) -- currently we need to force a tail refresh
end
-- local rightkern = i.rightkern
-- if rightkern and rightkern ~= 0 then
@@ -884,6 +982,7 @@ local function inject_pairs_only(head,where)
if leftkern and leftkern ~= 0 then
local t = find_tail(d)
insert_node_after(d,t,newkern(leftkern))
+ setfield(p,"replace",d) -- currently we need to force a tail refresh
end
-- local rightkern = i.rightkern
-- if rightkern and rightkern ~= 0 then
@@ -909,24 +1008,22 @@ local function inject_pairs_only(head,where)
-- this is the most common case
local i = rawget(pn,"injections")
if i then
- local yoffset = i.yoffset
- if yoffset and yoffset ~= 0 then
- setfield(n,"yoffset",yoffset)
- end
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
- insert_node_before(head,n,newkern(leftkern))
+ head = insert_node_before(head,n,newkern(leftkern))
end
local rightkern = i.rightkern
if rightkern and rightkern ~= 0 then
insert_node_after(head,n,newkern(rightkern))
n = getnext(n) -- to be checked
end
+ local yoffset = i.yoffset
+ if yoffset and yoffset ~= 0 then
+ setfield(n,"yoffset",yoffset)
+ end
end
end
end
- else
- break
end
p = nil
elseif id == disc_code then
@@ -935,16 +1032,12 @@ local function inject_pairs_only(head,where)
local h = d
for n in traverse_id(glyph_code,d) do
if getsubtype(n) < 256 then
- local p = rawget(properties,n)
- if p then
- local i = rawget(p,"preinjections")
+ local pn = rawget(properties,n)
+ if pn then
+ local i = rawget(pn,"preinjections")
if i then
- local yoffset = i.yoffset
- if yoffset and yoffset ~= 0 then
- setfield(n,"yoffset",yoffset)
- end
local leftkern = i.leftkern
- if leftkern ~= 0 then
+ if leftkern and leftkern ~= 0 then
h = insert_node_before(h,n,newkern(leftkern))
end
local rightkern = i.rightkern
@@ -952,6 +1045,10 @@ local function inject_pairs_only(head,where)
insert_node_after(head,n,newkern(rightkern))
n = getnext(n) -- to be checked
end
+ local yoffset = i.yoffset
+ if yoffset and yoffset ~= 0 then
+ setfield(n,"yoffset",yoffset)
+ end
end
end
else
@@ -967,14 +1064,10 @@ local function inject_pairs_only(head,where)
local h = d
for n in traverse_id(glyph_code,d) do
if getsubtype(n) < 256 then
- local p = rawget(properties,n)
- if p then
- local i = rawget(p,"postinjections")
+ local pn = rawget(properties,n)
+ if pn then
+ local i = rawget(pn,"postinjections")
if i then
- local yoffset = i.yoffset
- if yoffset and yoffset ~= 0 then
- setfield(n,"yoffset",yoffset)
- end
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
h = insert_node_before(h,n,newkern(leftkern))
@@ -984,6 +1077,10 @@ local function inject_pairs_only(head,where)
insert_node_after(head,n,newkern(rightkern))
n = getnext(n) -- to be checked
end
+ local yoffset = i.yoffset
+ if yoffset and yoffset ~= 0 then
+ setfield(n,"yoffset",yoffset)
+ end
end
end
else
@@ -999,14 +1096,10 @@ local function inject_pairs_only(head,where)
local h = d
for n in traverse_id(glyph_code,d) do
if getsubtype(n) < 256 then
- local p = rawget(properties,n)
- if p then
- local i = rawget(p,"replaceinjections")
+ local pn = rawget(properties,n)
+ if pn then
+ local i = rawget(pn,"replaceinjections")
if i then
- local yoffset = i.yoffset
- if yoffset and yoffset ~= 0 then
- setfield(n,"yoffset",yoffset)
- end
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
h = insert_node_before(h,n,newkern(leftkern))
@@ -1016,6 +1109,10 @@ local function inject_pairs_only(head,where)
insert_node_after(head,n,newkern(rightkern))
n = getnext(n) -- to be checked
end
+ local yoffset = i.yoffset
+ if yoffset and yoffset ~= 0 then
+ setfield(n,"yoffset",yoffset)
+ end
end
end
else
@@ -1042,7 +1139,7 @@ local function inject_pairs_only(head,where)
return tonode(head), true
end
-function injections.handler(head,where) -- optimize for n=1 ?
+function injections.handler(head,where)
if nofregisteredmarks > 0 or nofregisteredcursives > 0 then
return inject_everything(head,where)
elseif nofregisteredpairs > 0 then
diff --git a/src/fontloader/misc/fontloader-fonts-otn.lua b/src/fontloader/misc/fontloader-fonts-otn.lua
index dd3aa61..7fafadb 100644
--- a/src/fontloader/misc/fontloader-fonts-otn.lua
+++ b/src/fontloader/misc/fontloader-fonts-otn.lua
@@ -6,12 +6,16 @@ if not modules then modules = { } end modules ['font-otn'] = {
license = "see context related readme files",
}
--- todo: looks like we have a leak somewhere (probably in ligatures)
--- todo: copy attributes to disc
-
-- this is a context version which can contain experimental code, but when we
-- have serious patches we also need to change the other two font-otn files
+-- at some point i might decide to convert the whole list into a table and then
+-- run over that instead (but it has some drawbacks as we also need to deal with
+-- attributes and such so we need to keep a lot of track - which is why i rejected
+-- that method - although it has become a bit easier in the meantime so it might
+-- become an alternative (by that time i probably have gone completely lua) .. the
+-- usual chicken-egg issues ... maybe mkix as it's no real tex any more then
+
-- preprocessors = { "nodes" }
-- anchor class : mark, mkmk, curs, mklg (todo)
@@ -40,7 +44,18 @@ if not modules then modules = { } end modules ['font-otn'] = {
-- mark (to mark) code is still not what it should be (too messy but we need some more extreem husayni tests)
-- remove some optimizations (when I have a faster machine)
--
--- maybe redo the lot some way (more context specific)
+-- beware:
+--
+-- we do some disc jugling where we need to keep in mind that the
+-- pre, post and replace fields can have prev pointers to a nesting
+-- node ... i wonder if that is still needed
+--
+-- not possible:
+--
+-- \discretionary {alpha-} {betagammadelta}
+-- {\discretionary {alphabeta-} {gammadelta}
+-- {\discretionary {alphabetagamma-} {delta}
+-- {alphabetagammadelta}}}
--[[ldx--
<p>This module is a bit more split up that I'd like but since we also want to test
@@ -65,9 +80,12 @@ 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 <l n='context'/> users ask for it.</p>
-<p>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.</p>
+<p>The specification leaves room for interpretation. In case of doubt the microsoft
+implementation is the reference as it is the most complete one. As they deal with
+lots of scripts and fonts, Kai and Ivo did a lot of testing of the generic code and
+their suggestions help improve the code. I'm aware that not all border cases can be
+taken care of, unless we accept excessive runtime, and even then the interference
+with other mechanisms (like hyphenation) are not trivial.</p>
<p>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
@@ -94,12 +112,12 @@ when there's a fix in the <l n='fontforge'/> library or <l n='lua'/> code that
results in different tables.</p>
--ldx]]--
--- action handler chainproc chainmore comment
+-- action handler chainproc
--
--- gsub_single ok ok ok
--- gsub_multiple ok ok not implemented yet
--- gsub_alternate ok ok not implemented yet
--- gsub_ligature ok ok ok
+-- gsub_single ok ok
+-- gsub_multiple ok ok
+-- gsub_alternate ok ok
+-- gsub_ligature ok ok
-- gsub_context ok --
-- gsub_contextchain ok --
-- gsub_reversecontextchain ok --
@@ -123,7 +141,6 @@ results in different tables.</p>
-- chainmore : multiple substitutions triggered by contextual lookup (e.g. fij -> f + ij)
--
-- remark: the 'not implemented yet' variants will be done when we have fonts that use them
--- remark: we need to check what to do with discretionaries
-- We used to have independent hashes for lookups but as the tags are unique
-- we now use only one hash. If needed we can have multiple again but in that
@@ -131,16 +148,14 @@ results in different tables.</p>
-- Todo: make plugin feature that operates on char/glyphnode arrays
-local concat, insert, remove = table.concat, table.insert, table.remove
-local gmatch, gsub, find, match, lower, strip = string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip
-local type, next, tonumber, tostring = type, next, tonumber, tostring
-local lpegmatch = lpeg.match
+local type, next, tonumber = type, next, tonumber
local random = math.random
local formatters = string.formatters
local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes
-local registertracker = trackers.register
+local registertracker = trackers.register
+local registerdirective = directives.register
local fonts = fonts
local otf = fonts.handlers.otf
@@ -162,6 +177,16 @@ local trace_steps = false registertracker("otf.steps", function(v
local trace_skips = false registertracker("otf.skips", function(v) trace_skips = v end)
local trace_directions = false registertracker("otf.directions", function(v) trace_directions = v end)
+local trace_kernruns = false registertracker("otf.kernruns", function(v) trace_kernruns = v end)
+local trace_discruns = false registertracker("otf.discruns", function(v) trace_discruns = v end)
+local trace_compruns = false registertracker("otf.compruns", function(v) trace_compruns = v end)
+
+local quit_on_no_replacement = true -- maybe per font
+local zwnjruns = true
+
+registerdirective("otf.zwnjruns", function(v) zwnjruns = v end)
+registerdirective("otf.chain.quitonnoreplacement",function(value) quit_on_no_replacement = value end)
+
local report_direct = logs.reporter("fonts","otf direct")
local report_subchain = logs.reporter("fonts","otf subchain")
local report_chain = logs.reporter("fonts","otf chain")
@@ -225,16 +250,11 @@ local disccodes = nodes.disccodes
local glyph_code = nodecodes.glyph
local glue_code = nodecodes.glue
local disc_code = nodecodes.disc
-local whatsit_code = nodecodes.whatsit
local math_code = nodecodes.math
local dir_code = whatcodes.dir
local localpar_code = whatcodes.localpar
-
local discretionary_code = disccodes.discretionary
-local regular_code = disccodes.regular
-local automatic_code = disccodes.automatic
-
local ligature_code = glyphcodes.ligature
local privateattribute = attributes.private
@@ -286,7 +306,14 @@ local handlers = { }
local rlmode = 0
local featurevalue = false
--- head is always a whatsit so we can safely assume that head is not changed
+local sweephead = { }
+local sweepnode = nil
+local sweepprev = nil
+local sweepnext = nil
+
+local notmatchpre = { }
+local notmatchpost = { }
+local notmatchreplace = { }
-- we use this for special testing and documentation
@@ -376,8 +403,66 @@ local function copy_glyph(g) -- next and prev are untouched !
end
end
---
+local function flattendisk(head,disc)
+ local replace = getfield(disc,"replace")
+ setfield(disc,"replace",nil)
+ free_node(disc)
+ if head == disc then
+ local next = getnext(disc)
+ if replace then
+ if next then
+ local tail = find_node_tail(replace)
+ setfield(tail,"next",next)
+ setfield(next,"prev",tail)
+ end
+ return replace, replace
+ elseif next then
+ return next, next
+ else
+ return -- maybe warning
+ end
+ else
+ local next = getnext(disc)
+ local prev = getprev(disc)
+ if replace then
+ local tail = find_node_tail(replace)
+ if next then
+ setfield(tail,"next",next)
+ setfield(next,"prev",tail)
+ end
+ setfield(prev,"next",replace)
+ setfield(replace,"prev",prev)
+ return head, replace
+ else
+ if next then
+ setfield(next,"prev",prev)
+ end
+ setfield(prev,"next",next)
+ return head, next
+ end
+ end
+end
+local function appenddisc(disc,list)
+ local post = getfield(disc,"post")
+ local replace = getfield(disc,"replace")
+ local phead = list
+ local rhead = copy_node_list(list)
+ local ptail = find_node_tail(post)
+ local rtail = find_node_tail(replace)
+ if post then
+ setfield(ptail,"next",phead)
+ setfield(phead,"prev",ptail)
+ else
+ setfield(disc,"post",phead)
+ end
+ if replace then
+ setfield(rtail,"next",rhead)
+ setfield(rhead,"prev",rtail)
+ else
+ setfield(disc,"replace",rhead)
+ end
+end
-- start is a mark and we need to keep that one
@@ -416,8 +501,8 @@ end
-- iteration this becomes a KAF-LAM-ALEF with a SHADDA on the second and a FATHA on the
-- third component.
-local function getcomponentindex(start)
- if getid(start) ~= glyph_code then
+local function getcomponentindex(start) -- we could store this offset in the glyph (nofcomponents)
+ if getid(start) ~= glyph_code then -- and then get rid of all components
return 0
elseif getsubtype(start) == ligature_code then
local i = 0
@@ -434,16 +519,28 @@ local function getcomponentindex(start)
end
end
--- eventually we will do positioning in an other way (needs addional w/h/d fields)
+local a_noligature = attributes.private("noligature")
local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) -- brr head
+ if getattr(start,a_noligature) == 1 then
+ -- so we can do: e\noligature{ff}e e\noligature{f}fie (we only look at the first)
+ return head, start
+ end
if start == stop and getchar(start) == char then
resetinjection(start)
setfield(start,"char",char)
return head, start
end
+ -- needs testing (side effects):
+ local components = getfield(start,"components")
+ if components then
+ -- we get a double free .. needs checking
+ -- flush_node_list(components)
+ end
+ --
local prev = getprev(start)
local next = getnext(stop)
+ local comp = start
setfield(start,"prev",nil)
setfield(stop,"next",nil)
local base = copy_glyph(start)
@@ -453,15 +550,15 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
resetinjection(base)
setfield(base,"char",char)
setfield(base,"subtype",ligature_code)
- setfield(base,"components",start) -- start can have components
+ setfield(base,"components",comp) -- start can have components ... do we need to flush?
if prev then
setfield(prev,"next",base)
end
if next then
setfield(next,"prev",base)
end
- setfield(base,"next",next)
setfield(base,"prev",prev)
+ setfield(base,"next",next)
if not discfound then
local deletemarks = markflag ~= "mark"
local components = start
@@ -480,7 +577,9 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
if trace_marks then
logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getligaindex(start))
end
- head, current = insert_node_after(head,current,copy_node(start)) -- unlikely that mark has components
+ local n = copy_node(start)
+ copyinjection(n,start)
+ head, current = insert_node_after(head,current,n) -- unlikely that mark has components
elseif trace_marks then
logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))
end
@@ -501,17 +600,85 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
end
start = getnext(start)
end
+ else
+ -- discfound ... forget about marks .. probably no scripts that hyphenate and have marks
+ local discprev = getfield(discfound,"prev")
+ local discnext = getfield(discfound,"next")
+ if discprev and discnext then
+ -- we assume normalization in context, and don't care about generic ... especially
+ -- \- can give problems as there we can have a negative char but that won't match
+ -- anyway
+ local pre = getfield(discfound,"pre")
+ local post = getfield(discfound,"post")
+ local replace = getfield(discfound,"replace")
+ if not replace then -- todo: signal simple hyphen
+ local prev = getfield(base,"prev")
+ local copied = copy_node_list(comp)
+ setfield(discnext,"prev",nil) -- also blocks funny assignments
+ setfield(discprev,"next",nil) -- also blocks funny assignments
+ if pre then
+ setfield(discprev,"next",pre)
+ setfield(pre,"prev",discprev)
+ end
+ pre = comp
+ if post then
+ local tail = find_node_tail(post)
+ setfield(tail,"next",discnext)
+ setfield(discnext,"prev",tail)
+ setfield(post,"prev",nil)
+ else
+ post = discnext
+ end
+ setfield(prev,"next",discfound)
+ setfield(discfound,"prev",prev)
+ setfield(discfound,"next",next)
+ setfield(next,"prev",discfound)
+ setfield(base,"next",nil)
+ setfield(base,"prev",nil)
+ setfield(base,"components",copied)
+ setfield(discfound,"pre",pre)
+ setfield(discfound,"post",post)
+ setfield(discfound,"replace",base)
+ setfield(discfound,"subtype",discretionary_code)
+ base = prev -- restart
+ end
+ end
end
return head, base
end
-function handlers.gsub_single(head,start,kind,lookupname,replacement)
- if trace_singles then
- logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement))
+local function multiple_glyphs(head,start,multiple,ignoremarks)
+ local nofmultiples = #multiple
+ if nofmultiples > 0 then
+ resetinjection(start)
+ setfield(start,"char",multiple[1])
+ if nofmultiples > 1 then
+ local sn = getnext(start)
+ for k=2,nofmultiples do -- todo: use insert_node
+-- untested:
+--
+-- while ignoremarks and marks[getchar(sn)] then
+-- local sn = getnext(sn)
+-- end
+ local n = copy_node(start) -- ignore components
+ resetinjection(n)
+ setfield(n,"char",multiple[k])
+ setfield(n,"prev",start)
+ setfield(n,"next",sn)
+ if sn then
+ setfield(sn,"prev",n)
+ end
+ setfield(start,"next",n)
+ start = n
+ end
+ end
+ return head, start, true
+ else
+ if trace_multiples then
+ logprocess("no multiple for %s",gref(getchar(start)))
+ end
+ return head, start, false
end
- resetinjection(start)
- setfield(start,"char",replacement)
- return head, start, true
end
local function get_alternative_glyph(start,alternatives,value,trace_alternatives)
@@ -546,38 +713,15 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives
end
end
-local function multiple_glyphs(head,start,multiple,ignoremarks)
- local nofmultiples = #multiple
- if nofmultiples > 0 then
- resetinjection(start)
- setfield(start,"char",multiple[1])
- if nofmultiples > 1 then
- local sn = getnext(start)
- for k=2,nofmultiples do -- todo: use insert_node
--- untested:
---
--- while ignoremarks and marks[getchar(sn)] then
--- local sn = getnext(sn)
--- end
- local n = copy_node(start) -- ignore components
- resetinjection(n)
- setfield(n,"char",multiple[k])
- setfield(n,"next",sn)
- setfield(n,"prev",start)
- if sn then
- setfield(sn,"prev",n)
- end
- setfield(start,"next",n)
- start = n
- end
- end
- return head, start, true
- else
- if trace_multiples then
- logprocess("no multiple for %s",gref(getchar(start)))
- end
- return head, start, false
+-- handlers
+
+function handlers.gsub_single(head,start,kind,lookupname,replacement)
+ if trace_singles then
+ logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement))
end
+ resetinjection(start)
+ setfield(start,"char",replacement)
+ return head, start, true
end
function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence)
@@ -605,7 +749,7 @@ function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence)
end
function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
- local s, stop, discfound = getnext(start), nil, false
+ local s, stop = getnext(start), nil
local startchar = getchar(start)
if marks[startchar] then
while s do
@@ -633,24 +777,30 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
else
head, start = markstoligature(kind,lookupname,head,start,stop,lig)
end
- return head, start, true
+ return head, start, true, false
else
-- ok, goto next lookup
end
end
else
- local skipmark = sequence.flags[1]
+ local skipmark = sequence.flags[1]
+ local discfound = false
+ local lastdisc = nil
while s do
local id = getid(s)
- if id == glyph_code and getsubtype(s)<256 then
- if getfont(s) == currentfont then
+ if id == glyph_code and getsubtype(s)<256 then -- not needed
+ if getfont(s) == currentfont then -- also not needed only when mark
local char = getchar(s)
if skipmark and marks[char] then
s = getnext(s)
- else
- local lg = ligature[char]
+ else -- ligature is a tree
+ local lg = ligature[char] -- can there be multiple in a row? maybe in a bad font
if lg then
- stop = s
+ if not discfound and lastdisc then
+ discfound = lastdisc
+ lastdisc = nil
+ end
+ stop = s -- needed for fake so outside then
ligature = lg
s = getnext(s)
else
@@ -661,13 +811,13 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
break
end
elseif id == disc_code then
- discfound = true
+ lastdisc = s
s = getnext(s)
else
break
end
end
- local lig = ligature.ligature
+ local lig = ligature.ligature -- can't we get rid of this .ligature?
if lig then
if stop then
if trace_ligatures then
@@ -685,14 +835,88 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig))
end
end
- return head, start, true
+ return head, start, true, discfound
else
- -- weird but happens
+ -- weird but happens, pseudo ligatures ... just the components
end
end
+ return head, start, false, discfound
+end
+
+function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence,injection)
+ local startchar = getchar(start)
+ local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,injection) -- ,characters[startchar])
+ if trace_kerns then
+ logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h)
+ end
return head, start, false
end
+function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence,lookuphash,i,injection)
+ -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too
+ -- todo: kerns in components of ligatures
+ local snext = getnext(start)
+ if not snext then
+ return head, start, false
+ else
+ local prev = start
+ local done = false
+ local factor = tfmdata.parameters.factor
+ local lookuptype = lookuptypes[lookupname]
+ while snext and getid(snext) == glyph_code and getfont(snext) == currentfont and getsubtype(snext)<256 do
+ local nextchar = getchar(snext)
+ local krn = kerns[nextchar]
+ if not krn and marks[nextchar] then
+ prev = snext
+ snext = getnext(snext)
+ else
+ if not krn then
+ -- skip
+ elseif type(krn) == "table" then
+ if lookuptype == "pair" then -- probably not needed
+ local a, b = krn[2], krn[3]
+ if a and #a > 0 then
+ local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,injection) -- characters[startchar])
+ if trace_kerns then
+ local startchar = getchar(start)
+ logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ if b and #b > 0 then
+ local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,injection) -- characters[nextchar])
+ if trace_kerns then
+ local startchar = getchar(start)
+ logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ else -- wrong ... position has different entries
+ report_process("%s: check this out (old kern stuff)",pref(kind,lookupname))
+ -- local a, b = krn[2], krn[6]
+ -- if a and a ~= 0 then
+ -- local k = setkern(snext,factor,rlmode,a)
+ -- if trace_kerns then
+ -- logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),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 = setkern(snext,factor,rlmode,krn,injection)
+ if trace_kerns then
+ logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) -- prev?
+ end
+ done = true
+ end
+ break
+ end
+ end
+ return head, start, done
+ end
+end
+
--[[ldx--
<p>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.</p>
@@ -855,7 +1079,7 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence
if al[anchor] then
local ma = markanchors[anchor]
if ma then
- local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar])
+ local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)
if trace_marks then
logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -938,85 +1162,11 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)
end
end
-function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence)
- local startchar = getchar(start)
- local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h)
- end
- return head, start, false
-end
-
-function handlers.gpos_pair(head,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 = getnext(start)
- if not snext then
- return head, start, false
- else
- local prev, done = start, false
- local factor = tfmdata.parameters.factor
- local lookuptype = lookuptypes[lookupname]
- while snext and getid(snext) == glyph_code and getfont(snext) == currentfont and getsubtype(snext)<256 do
- local nextchar = getchar(snext)
- local krn = kerns[nextchar]
- if not krn and marks[nextchar] then
- prev = snext
- snext = getnext(snext)
- else
- if not krn then
- -- skip
- elseif type(krn) == "table" then
- if lookuptype == "pair" then -- probably not needed
- local a, b = krn[2], krn[3]
- if a and #a > 0 then
- local startchar = getchar(start)
- local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- if b and #b > 0 then
- local startchar = getchar(start)
- local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
- if trace_kerns then
- logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- else -- wrong ... position has different entries
- report_process("%s: check this out (old kern stuff)",pref(kind,lookupname))
- -- local a, b = krn[2], krn[6]
- -- if a and a ~= 0 then
- -- local k = setkern(snext,factor,rlmode,a)
- -- if trace_kerns then
- -- logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),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 = setkern(snext,factor,rlmode,krn)
- if trace_kerns then
- logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar))
- end
- done = true
- end
- break
- end
- end
- return head, start, done
- end
-end
-
--[[ldx--
<p>I will implement multiple chain replacements once I run into a font that uses
it. It's not that complex to handle.</p>
--ldx]]--
-local chainmores = { }
local chainprocs = { }
local function logprocess(...)
@@ -1045,11 +1195,6 @@ function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,looku
return head, start, false
end
-function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n)
- logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
- return head, 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.
@@ -1116,8 +1261,7 @@ as less as needed but that would also make the code even more messy.</p>
-- end
--[[ldx--
-<p>Here we replace start by a single variant, First we delete the rest of the
-match.</p>
+<p>Here we replace start by a single variant.</p>
--ldx]]--
function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex)
@@ -1125,7 +1269,7 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
local current = start
local subtables = currentlookup.subtables
if #subtables > 1 then
- logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," "))
+ logwarning("todo: check if we need to loop over the replacements: % t",subtables)
end
while current do
if getid(current) == glyph_code then
@@ -1160,11 +1304,8 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
return head, start, false
end
-chainmores.gsub_single = chainprocs.gsub_single
-
--[[ldx--
-<p>Here we replace start by a sequence of new glyphs. First we delete the rest of
-the match.</p>
+<p>Here we replace start by a sequence of new glyphs.</p>
--ldx]]--
function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
@@ -1193,8 +1334,6 @@ function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,
return head, start, false
end
-chainmores.gsub_multiple = chainprocs.gsub_multiple
-
--[[ldx--
<p>Here we replace start by new glyph. First we delete the rest of the match.</p>
--ldx]]--
@@ -1249,8 +1388,6 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext
return head, start, false
end
-chainmores.gsub_alternate = chainprocs.gsub_alternate
-
--[[ldx--
<p>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
@@ -1276,13 +1413,19 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
local s = getnext(start)
local discfound = false
local last = stop
- local nofreplacements = 0
+ local nofreplacements = 1
local skipmark = currentlookup.flags[1]
while s do
local id = getid(s)
if id == disc_code then
- s = getnext(s)
- discfound = true
+ if not discfound then
+ discfound = s
+ end
+ if s == stop then
+ break -- okay? or before the disc
+ else
+ s = getnext(s)
+ end
else
local schar = getchar(s)
if skipmark and marks[schar] then -- marks
@@ -1315,7 +1458,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
end
end
head, start = toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound)
- return head, start, true, nofreplacements
+ return head, start, true, nofreplacements, discfound
elseif trace_bugs then
if start == stop then
logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
@@ -1325,10 +1468,96 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
end
end
end
- return head, start, false, 0
+ return head, start, false, 0, false
end
-chainmores.gsub_ligature = chainprocs.gsub_ligature
+function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
+ -- untested .. needs checking for the new model
+ local startchar = getchar(start)
+ local subtables = currentlookup.subtables
+ local lookupname = subtables[1]
+ local kerns = lookuphash[lookupname]
+ if kerns then
+ kerns = kerns[startchar] -- needed ?
+ if kerns then
+ local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns) -- ,characters[startchar])
+ if trace_kerns then
+ logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h)
+ end
+ end
+ end
+ return head, start, false
+end
+
+function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
+ local snext = getnext(start)
+ if snext then
+ local startchar = getchar(start)
+ local subtables = currentlookup.subtables
+ local lookupname = subtables[1]
+ local kerns = lookuphash[lookupname]
+ if kerns then
+ kerns = kerns[startchar]
+ if kerns then
+ local lookuptype = lookuptypes[lookupname]
+ local prev, done = start, false
+ local factor = tfmdata.parameters.factor
+ while snext and getid(snext) == glyph_code and getfont(snext) == currentfont and getsubtype(snext)<256 do
+ local nextchar = getchar(snext)
+ local krn = kerns[nextchar]
+ if not krn and marks[nextchar] then
+ prev = snext
+ snext = getnext(snext)
+ else
+ if not krn then
+ -- skip
+ elseif type(krn) == "table" then
+ if lookuptype == "pair" then
+ local a, b = krn[2], krn[3]
+ if a and #a > 0 then
+ local startchar = getchar(start)
+ local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a) -- ,characters[startchar])
+ if trace_kerns then
+ logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ if b and #b > 0 then
+ local startchar = getchar(start)
+ local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b) -- ,characters[nextchar])
+ if trace_kerns then
+ logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ else
+ report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname))
+ -- local a, b = krn[2], krn[6]
+ -- if a and a ~= 0 then
+ -- local k = setkern(snext,factor,rlmode,a)
+ -- if trace_kerns then
+ -- logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),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 = setkern(snext,factor,rlmode,krn)
+ if trace_kerns then
+ logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar))
+ end
+ done = true
+ end
+ break
+ end
+ end
+ return head, start, done
+ end
+ end
+ end
+ return head, start, false
+end
function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
local markchar = getchar(start)
@@ -1497,7 +1726,7 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext
if al[anchor] then
local ma = markanchors[anchor]
if ma then
- local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar])
+ local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)
if trace_marks then
logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -1588,133 +1817,346 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l
return head, start, false
end
-function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
- -- untested .. needs checking for the new model
- local startchar = getchar(start)
- local subtables = currentlookup.subtables
- local lookupname = subtables[1]
- local kerns = lookuphash[lookupname]
- if kerns then
- kerns = kerns[startchar] -- needed ?
- if kerns then
- local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h)
+-- 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 show_skip(kind,chainname,char,ck,class)
+ if ck[9] then
+ logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
+ else
+ logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])
+ end
+end
+
+-- A previous version had disc collapsing code in the (single sub) handler plus some
+-- checking in the main loop, but that left the pre/post sequences undone. The best
+-- solution is to add some checking there and backtrack when a replace/post matches
+-- but it takes a bit of work to figure out an efficient way (this is what the sweep*
+-- names refer to). I might look into that variant one day again as it can replace
+-- some other code too. In that approach we can have a special version for gub and pos
+-- which gains some speed. This method does the test and passes info to the handlers
+-- (sweepnode, sweepmode, sweepprev, sweepnext, etc). Here collapsing is handled in the
+-- main loop which also makes code elsewhere simpler (i.e. no need for the other special
+-- runners and disc code in ligature building). I also experimented with pushing preceding
+-- glyphs sequences in the replace/pre fields beforehand which saves checking afterwards
+-- but at the cost of duplicate glyphs (memory) but it's too much overhead (runtime).
+--
+-- In the meantime Kai had moved the code from the single chain into a more general handler
+-- and this one (renamed to chaindisk) is used now. I optimized the code a bit and brought
+-- it in sycn with the other code. Hopefully I didn't introduce errors. Note: this somewhat
+-- complex approach is meant for fonts that implement (for instance) ligatures by character
+-- replacement which to some extend is not that suitable for hyphenation. I also use some
+-- helpers. This method passes some states but reparses the list. There is room for a bit of
+-- speed up but that will be done in the context version. (In fact a partial rewrite of all
+-- code can bring some more efficientry.)
+--
+-- I didn't test it with extremes but successive disc nodes still can give issues but in
+-- order to handle that we need more complex code which also slows down even more. The main
+-- loop variant could deal with that: test, collapse, backtrack.
+
+local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,chainindex,sequence,chainproc)
+
+ if not start then
+ return head, start, false
+ end
+
+ local startishead = start == head
+ local seq = ck[3]
+ local f = ck[4]
+ local l = ck[5]
+ local s = #seq
+ local done = false
+ local sweepnode = sweepnode
+ local sweeptype = sweeptype
+ local sweepoverflow = false
+ local checkdisc = getprev(head) -- hm bad name head
+ local keepdisc = not sweepnode
+ local lookaheaddisc = nil
+ local backtrackdisc = nil
+ local current = start
+ local last = start
+ local prev = getprev(start)
+
+ -- fishy: so we can overflow and then go on in the sweep?
+
+ local i = f
+ while i <= l do
+ local id = getid(current)
+ if id == glyph_code then
+ i = i + 1
+ last = current
+ current = getnext(current)
+ elseif id == disc_code then
+ if keepdisc then
+ keepdisc = false
+ if notmatchpre[current] ~= notmatchreplace[current] then
+ lookaheaddisc = current
+ end
+ local replace = getfield(current,"replace")
+ while replace and i <= l do
+ if getid(replace) == glyph_code then
+ i = i + 1
+ end
+ replace = getnext(replace)
+ end
+ last = current
+ current = getnext(c)
+ else
+ head, current = flattendisk(head,current)
+ end
+ else
+ last = current
+ current = getnext(current)
+ end
+ if current then
+ -- go on
+ elseif sweepoverflow then
+ -- we already are folling up on sweepnode
+ break
+ elseif sweeptype == "post" or sweeptype == "replace" then
+ current = getnext(sweepnode)
+ if current then
+ sweeptype = nil
+ sweepoverflow = true
+ else
+ break
end
end
end
- return head, start, false
-end
-chainmores.gpos_single = chainprocs.gpos_single -- okay?
+ if sweepoverflow then
+ local prev = current and getprev(current)
+ if not current or prev ~= sweepnode then
+ local head = getnext(sweepnode)
+ local tail = nil
+ if prev then
+ tail = prev
+ setfield(current,"prev",sweepnode)
+ else
+ tail = find_node_tail(head)
+ end
+ setfield(sweepnode,"next",current)
+ setfield(head,"prev",nil)
+ setfield(tail,"next",nil)
+ appenddisc(sweepnode,head)
+ end
+ end
--- when machines become faster i will make a shared function
+ if l < s then
+ local i = l
+ local t = sweeptype == "post" or sweeptype == "replace"
+ while current and i < s do
+ local id = getid(current)
+ if id == glyph_code then
+ i = i + 1
+ current = getnext(current)
+ elseif id == disc_code then
+ if keepdisc then
+ keepdisc = false
+ if notmatchpre[current] ~= notmatchreplace[current] then
+ lookaheaddisc = current
+ end
+ local replace = getfield(c,"replace")
+ while replace and i < s do
+ if getid(replace) == glyph_code then
+ i = i + 1
+ end
+ replace = getnext(replace)
+ end
+ current = getnext(current)
+ elseif notmatchpre[current] ~= notmatchreplace[current] then
+ head, current = flattendisk(head,current)
+ else
+ current = getnext(current) -- HH
+ end
+ else
+ current = getnext(current)
+ end
+ if not current and t then
+ current = getnext(sweepnode)
+ if current then
+ sweeptype = nil
+ end
+ end
+ end
+ end
-function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
- local snext = getnext(start)
- if snext then
- local startchar = getchar(start)
- local subtables = currentlookup.subtables
- local lookupname = subtables[1]
- local kerns = lookuphash[lookupname]
- if kerns then
- kerns = kerns[startchar]
- if kerns then
- local lookuptype = lookuptypes[lookupname]
- local prev, done = start, false
- local factor = tfmdata.parameters.factor
- while snext and getid(snext) == glyph_code and getfont(snext) == currentfont and getsubtype(snext)<256 do
- local nextchar = getchar(snext)
- local krn = kerns[nextchar]
- if not krn and marks[nextchar] then
- prev = snext
- snext = getnext(snext)
- else
- if not krn then
- -- skip
- elseif type(krn) == "table" then
- if lookuptype == "pair" then
- local a, b = krn[2], krn[3]
- if a and #a > 0 then
- local startchar = getchar(start)
- local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- if b and #b > 0 then
- local startchar = getchar(start)
- local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
- if trace_kerns then
- logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- else
- report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname))
- local a, b = krn[2], krn[6]
- if a and a ~= 0 then
- local k = setkern(snext,factor,rlmode,a)
- if trace_kerns then
- logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),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 = setkern(snext,factor,rlmode,krn)
- if trace_kerns then
- logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar))
- end
- done = true
+ if f > 1 then
+ local current = prev
+ local i = f
+ local t = sweeptype == "pre" or sweeptype == "replace"
+ if not current and t and current == checkdisk then
+ current = getprev(sweepnode)
+ end
+ while current and i > 1 do -- missing getprev added / moved outside
+ local id = getid(current)
+ if id == glyph_code then
+ i = i - 1
+ elseif id == disc_code then
+ if keepdisc then
+ keepdisc = false
+ if notmatchpost[current] ~= notmatchreplace[current] then
+ backtrackdisc = current
+ end
+ local replace = getfield(current,"replace")
+ while replace and i > 1 do
+ if getid(replace) == glyph_code then
+ i = i - 1
end
- break
+ replace = getnext(replace)
end
+ elseif notmatchpost[current] ~= notmatchreplace[current] then
+ head, current = flattendisk(head,current)
end
- return head, start, done
+ end
+ current = getprev(current)
+ if t and current == checkdisk then
+ current = getprev(sweepnode)
end
end
end
- return head, start, false
-end
-chainmores.gpos_pair = chainprocs.gpos_pair -- okay?
+ local ok = false
+ if lookaheaddisc then
--- what pointer to return, spec says stop
--- to be discussed ... is bidi changer a space?
--- elseif char == zwnj and sequence[n][32] then -- brrr
+ local cf = start
+ local cl = getprev(lookaheaddisc)
+ local cprev = getprev(start)
+ local insertedmarks = 0
--- 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
+ while cprev and getid(cf) == glyph_code and getfont(cf) == currentfont and getsubtype(cf) < 256 and marks[getchar(cf)] do
+ insertedmarks = insertedmarks + 1
+ cf = cprev
+ startishead = cf == head
+ cprev = getprev(cprev)
+ end
+
+ setfield(lookaheaddisc,"prev",cprev)
+ if cprev then
+ setfield(cprev,"next",lookaheaddisc)
+ end
+ setfield(cf,"prev",nil)
+ setfield(cl,"next",nil)
+ if startishead then
+ head = lookaheaddisc
+ end
+
+ local replace = getfield(lookaheaddisc,"replace")
+ local pre = getfield(lookaheaddisc,"pre")
+ local new = copy_node_list(cf)
+ local cnew = new
+ for i=1,insertedmarks do
+ cnew = getnext(cnew)
+ end
+ local clast = cnew
+ for i=f,l do
+ clast = getnext(clast)
+ end
+ if not notmatchpre[lookaheaddisc] then
+ cf, start, ok = chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ end
+ if not notmatchreplace[lookaheaddisc] then
+ new, cnew, ok = chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ end
+ if pre then
+ setfield(cl,"next",pre)
+ setfield(pre,"prev",cl)
+ end
+ if replace then
+ local tail = find_node_tail(new)
+ setfield(tail,"next",replace)
+ setfield(replace,"prev",tail)
+ end
+ setfield(lookaheaddisc,"pre",cf) -- also updates tail
+ setfield(lookaheaddisc,"replace",new) -- also updates tail
+
+ start = getprev(lookaheaddisc)
+ sweephead[cf] = getnext(clast)
+ sweephead[new] = getnext(last)
+
+ elseif backtrackdisc then
+
+ local cf = getnext(backtrackdisc)
+ local cl = start
+ local cnext = getnext(start)
+ local insertedmarks = 0
+
+ while cnext and getid(cnext) == glyph_code and getfont(cnext) == currentfont and getsubtype(cnext) < 256 and marks[getchar(cnext)] do
+ insertedmarks = insertedmarks + 1
+ cl = cnext
+ cnext = getnext(cnext)
+ end
+ if cnext then
+ setfield(cnext,"prev",backtrackdisc)
+ end
+ setfield(backtrackdisc,"next",cnext)
+ setfield(cf,"prev",nil)
+ setfield(cl,"next",nil)
+ local replace = getfield(backtrackdisc,"replace")
+ local post = getfield(backtrackdisc,"post")
+ local new = copy_node_list(cf)
+ local cnew = find_node_tail(new)
+ for i=1,insertedmarks do
+ cnew = getprev(cnew)
+ end
+ local clast = cnew
+ for i=f,l do
+ clast = getnext(clast)
+ end
+ if not notmatchpost[backtrackdisc] then
+ cf, start, ok = chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ end
+ if not notmatchreplace[backtrackdisc] then
+ new, cnew, ok = chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ end
+ if post then
+ local tail = find_node_tail(post)
+ setfield(tail,"next",cf)
+ setfield(cf,"prev",tail)
+ else
+ post = cf
+ end
+ if replace then
+ local tail = find_node_tail(replace)
+ setfield(tail,"next",new)
+ setfield(new,"prev",tail)
+ else
+ replace = new
+ end
+ setfield(backtrackdisc,"post",post) -- also updates tail
+ setfield(backtrackdisc,"replace",replace) -- also updates tail
+ start = getprev(backtrackdisc)
+ sweephead[post] = getnext(clast)
+ sweephead[replace] = getnext(last)
-local function show_skip(kind,chainname,char,ck,class)
- if ck[9] then
- logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
else
- logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])
- end
-end
-local quit_on_no_replacement = true
+ head, start, ok = chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
-directives.register("otf.chain.quitonnoreplacement",function(value) -- maybe per font
- quit_on_no_replacement = value
-end)
+ end
+
+ return head, start, ok
+end
local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash)
- -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6]
+ local sweepnode = sweepnode
+ local sweeptype = sweeptype
+ local diskseen = false
+ local checkdisc = getprev(head)
local flags = sequence.flags
local done = false
local skipmark = flags[1]
local skipligature = flags[2]
local skipbase = flags[3]
- local someskip = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !)
- local markclass = sequence.markclass -- todo, first we need a proper test
+ local markclass = sequence.markclass
local skipped = false
- for k=1,#contexts do
+
+ for k=1,#contexts do -- i've only seen ccmp having > 1 (e.g. dejavu)
local match = true
local current = start
local last = start
@@ -1728,7 +2170,8 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
else
-- maybe we need a better space check (maybe check for glue or category or combination)
-- we cannot optimize for n=2 because there can be disc nodes
- local f, l = ck[4], ck[5]
+ local f = ck[4]
+ local l = ck[5]
-- current match
if f == 1 and f == l then -- current only
-- already a hit
@@ -1738,9 +2181,14 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
if f == l then -- new, else last out of sync (f is > 1)
-- match = true
else
+ local discfound = nil
local n = f + 1
last = getnext(last)
while n <= l do
+ if not last and (sweeptype == "post" or sweeptype == "replace") then
+ last = getnext(sweepnode)
+ sweeptype = nil
+ end
if last then
local id = getid(last)
if id == glyph_code then
@@ -1748,7 +2196,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
local char = getchar(last)
local ccd = descriptions[char]
if ccd then
- local class = ccd.class
+ local class = ccd.class or "base"
if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then
skipped = true
if trace_skips then
@@ -1761,18 +2209,77 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
n = n + 1
else
- match = false
+ if discfound then
+ notmatchreplace[discfound] = true
+ match = not notmatchpre[discfound]
+ else
+ match = false
+ end
break
end
else
- match = false
+ if discfound then
+ notmatchreplace[discfound] = true
+ match = not notmatchpre[discfound]
+ else
+ match = false
+ end
break
end
else
- match = false
+ if discfound then
+ notmatchreplace[discfound] = true
+ match = not notmatchpre[discfound]
+ else
+ match = false
+ end
break
end
elseif id == disc_code then
+ diskseen = true
+ discfound = last
+ notmatchpre[last] = nil
+ notmatchpost[last] = true
+ notmatchreplace[last] = nil
+ local pre = getfield(last,"pre")
+ local replace = getfield(last,"replace")
+ if pre then
+ local n = n
+ while pre do
+ if seq[n][getchar(pre)] then
+ n = n + 1
+ pre = getnext(pre)
+ if n > l then
+ break
+ end
+ else
+ notmatchpre[last] = true
+ break
+ end
+ end
+ if n <= l then
+ notmatchpre[last] = true
+ end
+ else
+ notmatchpre[last] = true
+ end
+ if replace then
+ -- so far we never entered this branch
+ while replace do
+ if seq[n][getchar(replace)] then
+ n = n + 1
+ replace = getnext(replace)
+ if n > l then
+ break
+ end
+ else
+ notmatchreplace[last] = true
+ match = not notmatchpre[last]
+ break
+ end
+ end
+ match = not notmatchpre[last]
+ end
last = getnext(last)
else
match = false
@@ -1789,50 +2296,137 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
if match and f > 1 then
local prev = getprev(start)
if prev then
- local n = f-1
- while n >= 1 do
- if prev then
- local id = getid(prev)
- if id == glyph_code then
- if getfont(prev) == currentfont and getsubtype(prev)<256 then -- normal char
- local char = getchar(prev)
- local ccd = descriptions[char]
- if ccd then
- local class = ccd.class
- if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then
- skipped = true
- if trace_skips then
- show_skip(kind,chainname,char,ck,class)
+ if prev == checkdisc and (sweeptype == "pre" or sweeptype == "replace") then
+ prev = getprev(sweepnode)
+ -- sweeptype = nil
+ end
+ if prev then
+ local discfound = nil
+ local n = f - 1
+ while n >= 1 do
+ if prev then
+ local id = getid(prev)
+ if id == glyph_code then
+ if getfont(prev) == currentfont and getsubtype(prev)<256 then -- normal char
+ local char = getchar(prev)
+ local ccd = descriptions[char]
+ if ccd then
+ local class = ccd.class
+ if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then
+ skipped = true
+ if trace_skips then
+ show_skip(kind,chainname,char,ck,class)
+ end
+ elseif seq[n][char] then
+ n = n -1
+ else
+ if discfound then
+ notmatchreplace[discfound] = true
+ match = not notmatchpost[discfound]
+ else
+ match = false
+ end
+ break
end
- elseif seq[n][char] then
- n = n -1
else
- match = false
+ if discfound then
+ notmatchreplace[discfound] = true
+ match = not notmatchpost[discfound]
+ else
+ match = false
+ end
break
end
else
- match = false
+ if discfound then
+ notmatchreplace[discfound] = true
+ match = not notmatchpost[discfound]
+ else
+ match = false
+ end
break
end
+ elseif id == disc_code then
+ -- the special case: f i where i becomes dottless i ..
+ diskseen = true
+ discfound = prev
+ notmatchpre[prev] = true
+ notmatchpost[prev] = nil
+ notmatchreplace[prev] = nil
+ local pre = getfield(prev,"pre")
+ local post = getfield(prev,"post")
+ local replace = getfield(prev,"replace")
+ if pre ~= start and post ~= start and replace ~= start then
+ if post then
+ local n = n
+ local posttail = find_node_tail(post)
+ while posttail do
+ if seq[n][getchar(posttail)] then
+ n = n - 1
+ if posttail == post then
+ break
+ else
+ posttail = getprev(posttail)
+ if n < 1 then
+ break
+ end
+ end
+ else
+ notmatchpost[prev] = true
+ break
+ end
+ end
+ if n >= 1 then
+ notmatchpost[prev] = true
+ end
+ else
+ notmatchpost[prev] = true
+ end
+ if replace then
+ -- we seldom enter this branch (e.g. on brill efficient)
+ local replacetail = find_node_tail(replace)
+ while replacetail do
+ if seq[n][getchar(replacetail)] then
+ n = n - 1
+ if replacetail == replace then
+ break
+ else
+ replacetail = getprev(replacetail)
+ if n < 1 then
+ break
+ end
+ end
+ else
+ notmatchreplace[prev] = true
+ match = not notmatchpost[prev]
+ break
+ end
+ end
+ if not match then
+ break
+ end
+ else
+ -- skip 'm
+ end
+ else
+ -- skip 'm
+ end
+ elseif seq[n][32] then
+ n = n -1
else
match = false
break
end
- elseif id == disc_code then
- -- skip 'm
- elseif seq[n][32] then
- n = n -1
+ prev = getprev(prev)
+ elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces
+ n = n - 1
else
match = false
break
end
- prev = getprev(prev)
- elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces
- n = n -1
- else
- match = false
- break
end
+ else
+ match = false
end
else
match = false
@@ -1841,7 +2435,14 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
-- after
if match and s > l then
local current = last and getnext(last)
+ if not current then
+ if sweeptype == "post" or sweeptype == "replace" then
+ current = getnext(sweepnode)
+ -- sweeptype = nil
+ end
+ end
if current then
+ local discfound = nil
-- removed optimization for s-l == 1, we have to deal with marks anyway
local n = l + 1
while n <= s do
@@ -1861,19 +2462,81 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
elseif seq[n][char] then
n = n + 1
else
- match = false
+ if discfound then
+ notmatchreplace[discfound] = true
+ match = not notmatchpre[discfound]
+ else
+ match = false
+ end
break
end
else
- match = false
+ if discfound then
+ notmatchreplace[discfound] = true
+ match = not notmatchpre[discfound]
+ else
+ match = false
+ end
break
end
else
- match = false
+ if discfound then
+ notmatchreplace[discfound] = true
+ match = not notmatchpre[discfound]
+ else
+ match = false
+ end
break
end
elseif id == disc_code then
- -- skip 'm
+ diskseen = true
+ discfound = current
+ notmatchpre[current] = nil
+ notmatchpost[current] = true
+ notmatchreplace[current] = nil
+ local pre = getfield(current,"pre")
+ local replace = getfield(current,"replace")
+ if pre then
+ local n = n
+ while pre do
+ if seq[n][getchar(pre)] then
+ n = n + 1
+ pre = getnext(pre)
+ if n > s then
+ break
+ end
+ else
+ notmatchpre[current] = true
+ break
+ end
+ end
+ if n <= s then
+ notmatchpre[current] = true
+ end
+ else
+ notmatchpre[current] = true
+ end
+ if replace then
+ -- so far we never entered this branch
+ while replace do
+ if seq[n][getchar(replace)] then
+ n = n + 1
+ replace = getnext(replace)
+ if n > s then
+ break
+ end
+ else
+ notmatchreplace[current] = true
+ match = notmatchpre[current]
+ break
+ end
+ end
+ if not match then
+ break
+ end
+ else
+ -- skip 'm
+ end
elseif seq[n][32] then -- brrr
n = n + 1
else
@@ -1894,7 +2557,8 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
end
if match then
- -- ck == currentcontext
+ -- can lookups be of a different type ?
+ local diskchain = diskseen or sweepnode
if trace_contexts then
local rule, lookuptype, f, l = ck[1], ck[2], ck[4], ck[5]
local char = getchar(start)
@@ -1914,10 +2578,14 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
local chainlookupname = chainlookups[1]
local chainlookup = lookuptable[chainlookupname]
if chainlookup then
- local cp = chainprocs[chainlookup.type]
- if cp then
+ local chainproc = chainprocs[chainlookup.type]
+ if chainproc then
local ok
- head, start, ok = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ if diskchain then
+ head, start, ok = chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc)
+ else
+ head, start, ok = chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ end
if ok then
done = true
end
@@ -1929,13 +2597,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
else
local i = 1
- while true do
+ while start and true do
if skipped then
- while true do
+ while true do -- todo: use properties
local char = getchar(start)
local ccd = descriptions[char]
if ccd then
- local class = ccd.class
+ local class = ccd.class or "base"
if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then
start = getnext(start)
else
@@ -1946,36 +2614,51 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
end
end
+ -- see remark in ms standard under : LookupType 5: Contextual Substitution Subtable
local chainlookupname = chainlookups[i]
local chainlookup = lookuptable[chainlookupname]
if not chainlookup then
- -- okay, n matches, < n replacements
+ -- we just advance
i = i + 1
else
- local cp = chainmores[chainlookup.type]
- if not cp then
+ local chainproc = chainprocs[chainlookup.type]
+ if not chainproc then
-- actually an error
logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
i = i + 1
else
local ok, n
- head, start, ok, n = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence)
+ if diskchain then
+ head, start, ok = chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc)
+ else
+ head, start, ok, n = chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence)
+ end
-- messy since last can be changed !
if ok then
done = true
- -- skip next one(s) if ligature
- i = i + (n or 1)
- else
- i = i + 1
+ if n and n > 1 then
+ -- we have a ligature (cf the spec we advance one but we really need to test it
+ -- as there are fonts out there that are fuzzy and have too many lookups:
+ --
+ -- U+1105 U+119E U+1105 U+119E : sourcehansansklight: script=hang ccmp=yes
+ --
+ if i + n > nofchainlookups then
+ -- if trace_contexts then
+ -- logprocess("%s: quitting lookups",cref(kind,chainname))
+ -- end
+ break
+ else
+ -- we need to carry one
+ end
+ end
end
+ i = i + 1
end
end
- if i > nofchainlookups then
+ if i > nofchainlookups or not start then
break
elseif start then
start = getnext(start)
- else
- -- weird
end
end
end
@@ -1990,8 +2673,16 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
end
end
+ if done then
+ break -- out of contexts (new, needs checking)
+ end
end
end
+ if diskseen then -- maybe move up so that we can turn checking on/off
+ notmatchpre = { }
+ notmatchpost = { }
+ notmatchreplace = { }
+ end
return head, start, done
end
@@ -2076,13 +2767,13 @@ local function initialize(sequence,script,language,enabled)
local order = sequence.order
if order then
for i=1,#order do --
- local kind = order[i] --
+ local kind = order[i] --
local valid = enabled[kind]
if valid then
local scripts = features[kind] --
local languages = scripts[script] or scripts[wildcard]
if languages and (languages[language] or languages[wildcard]) then
- return { valid, autofeatures[kind] or false, sequence.chain or 0, kind, sequence }
+ return { valid, autofeatures[kind] or false, sequence, kind }
end
end
end
@@ -2126,32 +2817,216 @@ function otf.dataset(tfmdata,font) -- generic variant, overloaded in context
return rl
end
--- elseif id == glue_code 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 = getnext(start) end
--- else
--- start = getnext(start)
--- end
--- else
--- start = getnext(start)
--- end
+-- assumptions:
+--
+-- * languages that use complex disc nodes
+
+local function kernrun(disc,run)
+ --
+ -- we catch <font 1><disc font 2>
+ --
+ if trace_kernruns then
+ report_run("kern") -- will be more detailed
+ end
+ --
+ local prev = getprev(disc) -- todo, keep these in the main loop
+ local next = getnext(disc) -- todo, keep these in the main loop
+ --
+ local pre = getfield(disc,"pre")
+ local post = getfield(disc,"post")
+ local replace = getfield(disc,"replace")
+ --
+ local prevmarks = prev
+ --
+ -- can be optional, because why on earth do we get a disc after a mark (okay, maybe when a ccmp
+ -- has happened but then it should be in the disc so basically this test indicates an error)
+ --
+ while prevmarks and getid(prevmarks) == glyph_code and marks[getchar(prevmarks)] and getfont(prevmarks) == currentfont and getsubtype(prevmarks) < 256 do
+ prevmarks = getprev(prevmarks)
+ end
+ --
+ if prev and (pre or replace) and not (getid(prev) == glyph_code and getfont(prev) == currentfont and getsubtype(prev)<256) then
+ prev = false
+ end
+ if next and (post or replace) and not (getid(next) == glyph_code and getfont(next) == currentfont and getsubtype(next)<256) then
+ next = false
+ end
+ --
+ if not pre then
+ -- go on
+ elseif prev then
+ local nest = getprev(pre)
+ setfield(pre,"prev",prev)
+ setfield(prev,"next",pre)
+ run(prevmarks,"preinjections")
+ setfield(pre,"prev",nest)
+ setfield(prev,"next",disc)
+ else
+ run(pre,"preinjections")
+ end
+ --
+ if not post then
+ -- go on
+ elseif next then
+ local tail = find_node_tail(post)
+ setfield(tail,"next",next)
+ setfield(next,"prev",tail)
+ run(post,"postinjections",next)
+ setfield(tail,"next",nil)
+ setfield(next,"prev",disc)
+ else
+ run(post,"postinjections")
+ end
+ --
+ if not replace and prev and next then
+ -- this should be already done by discfound
+ setfield(prev,"next",next)
+ setfield(next,"prev",prev)
+ run(prevmarks,"injections",next)
+ setfield(prev,"next",disc)
+ setfield(next,"prev",disc)
+ elseif prev and next then
+ local tail = find_node_tail(replace)
+ local nest = getprev(replace)
+ setfield(replace,"prev",prev)
+ setfield(prev,"next",replace)
+ setfield(tail,"next",next)
+ setfield(next,"prev",tail)
+ run(prevmarks,"replaceinjections",next)
+ setfield(replace,"prev",nest)
+ setfield(prev,"next",disc)
+ setfield(tail,"next",nil)
+ setfield(next,"prev",disc)
+ elseif prev then
+ local nest = getprev(replace)
+ setfield(replace,"prev",prev)
+ setfield(prev,"next",replace)
+ run(prevmarks,"replaceinjections")
+ setfield(replace,"prev",nest)
+ setfield(prev,"next",disc)
+ elseif next then
+ local tail = find_node_tail(replace)
+ setfield(tail,"next",next)
+ setfield(next,"prev",tail)
+ run(replace,"replaceinjections",next)
+ setfield(tail,"next",nil)
+ setfield(next,"prev",disc)
+ else
+ run(replace,"replaceinjections")
+ end
+end
--- there will be a new direction parser (pre-parsed etc)
+-- the if new test might be dangerous as luatex will check / set some tail stuff
+-- in a temp node
--- less bytecode: 290 -> 254
---
--- attr = attr or false
---
--- local a = getattr(start,0)
--- if (a == attr and (not attribute or getprop(start,a_state) == attribute)) or (not attribute or getprop(start,a_state) == attribute) then
--- -- the action
--- end
+local function comprun(disc,run)
+ if trace_compruns then
+ report_run("comp: %s",languages.serializediscretionary(disc))
+ end
+ --
+ local pre = getfield(disc,"pre")
+ if pre then
+ sweepnode = disc
+ sweeptype = "pre" -- in alternative code preinjections is used (also used then for proeprties, saves a variable)
+ local new, done = run(pre)
+ if done then
+ setfield(disc,"pre",new)
+ end
+ end
+ --
+ local post = getfield(disc,"post")
+ if post then
+ sweepnode = disc
+ sweeptype = "post"
+ local new, done = run(post)
+ if done then
+ setfield(disc,"post",new)
+ end
+ end
+ --
+ local replace = getfield(disc,"replace")
+ if replace then
+ sweepnode = disc
+ sweeptype = "replace"
+ local new, done = run(replace)
+ if done then
+ setfield(disc,"replace",new)
+ end
+ end
+ sweepnode = nil
+ sweeptype = nil
+end
+
+local function testrun(disc,trun,crun) -- use helper
+ local next = getnext(disc)
+ if next then
+ local replace = getfield(disc,"replace")
+ if replace then
+ local prev = getprev(disc)
+ if prev then
+ -- only look ahead
+ local tail = find_node_tail(replace)
+ -- local nest = getprev(replace)
+ setfield(tail,"next",next)
+ setfield(next,"prev",tail)
+ if trun(replace,next) then
+ setfield(disc,"replace",nil) -- beware, side effects of nest so first
+ setfield(prev,"next",replace)
+ setfield(replace,"prev",prev)
+ setfield(next,"prev",tail)
+ setfield(tail,"next",next)
+ setfield(disc,"prev",nil)
+ setfield(disc,"next",nil)
+ flush_node_list(disc)
+ return replace -- restart
+ else
+ setfield(tail,"next",nil)
+ setfield(next,"prev",disc)
+ end
+ else
+ -- weird case
+ end
+ else
+ -- no need
+ end
+ else
+ -- weird case
+ end
+ comprun(disc,crun)
+ return next
+end
+
+local function discrun(disc,drun,krun)
+ local next = getnext(disc)
+ local prev = getprev(disc)
+ if trace_discruns then
+ report_run("disc") -- will be more detailed
+ end
+ if next and prev then
+ setfield(prev,"next",next)
+ -- setfield(next,"prev",prev)
+ drun(prev)
+ setfield(prev,"next",disc)
+ -- setfield(next,"prev",disc)
+ end
+ --
+ local pre = getfield(disc,"pre")
+ if not pre then
+ -- go on
+ elseif prev then
+ local nest = getprev(pre)
+ setfield(pre,"prev",prev)
+ setfield(prev,"next",pre)
+ krun(prev,"preinjections")
+ setfield(pre,"prev",nest)
+ setfield(prev,"next",disc)
+ else
+ krun(pre,"preinjections")
+ end
+ return next
+end
+
+-- todo: maybe run lr and rl stretches
local function featuresprocessor(head,font,attr)
@@ -2180,6 +3055,7 @@ local function featuresprocessor(head,font,attr)
currentfont = font
rlmode = 0
+ sweephead = { }
local sequences = resources.sequences
local done = false
@@ -2195,23 +3071,27 @@ local function featuresprocessor(head,font,attr)
-- Keeping track of the headnode is needed for devanagari (I generalized it a bit
-- so that multiple cases are also covered.)
- -- todo: retain prev
+ -- We don't goto the next node of a disc node is created so that we can then treat
+ -- the pre, post and replace. It's abit of a hack but works out ok for most cases.
+
+ -- there can be less subtype and attr checking in the comprun etc helpers
for s=1,#datasets do
- local dataset = datasets[s]
- featurevalue = dataset[1] -- todo: pass to function instead of using a global
-
- local sequence = dataset[5] -- sequences[s] -- also dataset[5]
- local rlparmode = 0
- local topstack = 0
- local success = false
- local attribute = dataset[2]
- local chain = dataset[3] -- sequence.chain or 0
- local typ = sequence.type
- local subtables = sequence.subtables
- if chain < 0 then
+ local dataset = datasets[s]
+ featurevalue = dataset[1] -- todo: pass to function instead of using a global
+ local attribute = dataset[2]
+ local sequence = dataset[3] -- sequences[s] -- also dataset[5]
+ local kind = dataset[4]
+ ----- chain = dataset[5] -- sequence.chain or 0
+ local rlparmode = 0
+ local topstack = 0
+ local success = false
+ local typ = sequence.type
+ local gpossing = typ == "gpos_single" or typ == "gpos_pair" -- maybe all of them
+ local subtables = sequence.subtables
+ local handler = handlers[typ]
+ if typ == "gsub_reversecontextchain" then -- chain < 0
-- this is a limited case, no special treatments like 'init' etc
- local handler = handlers[typ]
-- we need to get rid of this slide! probably no longer needed in latest luatex
local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo
while start do
@@ -2225,13 +3105,15 @@ local function featuresprocessor(head,font,attr)
a = true
end
if a then
+ local char = getchar(start)
for i=1,#subtables do
local lookupname = subtables[i]
local lookupcache = lookuphash[lookupname]
if lookupcache then
- local lookupmatch = lookupcache[getchar(start)]
+ local lookupmatch = lookupcache[char]
if lookupmatch then
- head, start, success = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+ -- todo: disc?
+ head, start, success = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
if success then
break
end
@@ -2252,24 +3134,30 @@ local function featuresprocessor(head,font,attr)
end
end
else
- local handler = handlers[typ]
local ns = #subtables
local start = head -- local ?
rlmode = 0 -- to be checked ?
if ns == 1 then -- happens often
- local lookupname = subtables[1]
+ local lookupname = subtables[1]
local lookupcache = lookuphash[lookupname]
if not lookupcache then -- also check for empty cache
report_missing_cache(typ,lookupname)
else
- local function subrun(start)
- -- mostly for gsub, gpos would demand a more clever approach
- local head = start
- local done = false
+ local function c_run(head) -- no need to check for 256 and attr probably also the same
+ local done = false
+ local start = sweephead[head]
+ if start then
+ sweephead[head] = nil
+ else
+ start = head
+ end
while start do
local id = getid(start)
- if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then
+ if id ~= glyph_code then
+ -- very unlikely
+ start = getnext(start)
+ elseif getfont(start) == font and getsubtype(start) < 256 then
local a = getattr(start,0)
if a then
a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)
@@ -2281,7 +3169,7 @@ local function featuresprocessor(head,font,attr)
if lookupmatch then
-- sequence kan weg
local ok
- head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
+ head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)
if ok then
done = true
end
@@ -2291,48 +3179,106 @@ local function featuresprocessor(head,font,attr)
start = getnext(start)
end
else
- start = getnext(start)
+ return head, false
end
end
if done then
- success = true
- return head
+ success = true -- needed in this subrun?
end
+ return head, done
end
- local function kerndisc(disc) -- we can assume that prev and next are glyphs
- local prev = getprev(disc)
- local next = getnext(disc)
- if prev and next then
- setfield(prev,"next",next)
- -- setfield(next,"prev",prev)
- local a = getattr(prev,0)
- if a then
- a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute)
+ local function t_run(start,stop)
+ while start ~= stop do
+ local id = getid(start)
+ if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then
+ local a = getattr(start,0)
+ if a then
+ a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)
+ else
+ a = not attribute or getprop(start,a_state) == attribute
+ end
+ if a then
+ local lookupmatch = lookupcache[getchar(start)]
+ if lookupmatch then -- hm, hyphens can match (tlig) so we need to really check
+ -- if we need more than ligatures we can outline the code and use functions
+ local s = getnext(start)
+ local l = nil
+ while s do
+ local lg = lookupmatch[getchar(s)]
+ if lg then
+ l = lg
+ s = getnext(s)
+ else
+ break
+ end
+ end
+ if l and l.ligature then
+ return true
+ end
+ end
+ end
+ start = getnext(start)
else
- a = not attribute or getprop(prev,a_state) == attribute
+ break
end
- if a then
- local lookupmatch = lookupcache[getchar(prev)]
- if lookupmatch then
- -- sequence kan weg
- local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
- if ok then
- done = true
- success = true
+ end
+ end
+
+ local function d_run(prev) -- we can assume that prev and next are glyphs
+ local a = getattr(prev,0)
+ if a then
+ a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute)
+ else
+ a = not attribute or getprop(prev,a_state) == attribute
+ end
+ if a then
+ local lookupmatch = lookupcache[getchar(prev)]
+ if lookupmatch then
+ -- sequence kan weg
+ local h, d, ok = handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,1)
+ if ok then
+ done = true
+ success = true
+ end
+ end
+ end
+ end
+
+ local function k_run(sub,injection,last)
+ local a = getattr(sub,0)
+ if a then
+ a = (a == attr) and (not attribute or getprop(sub,a_state) == attribute)
+ else
+ a = not attribute or getprop(sub,a_state) == attribute
+ end
+ if a then
+ -- sequence kan weg
+ for n in traverse_nodes(sub) do -- only gpos
+ if n == last then
+ break
+ end
+ local id = getid(n)
+ if id == glyph_code then
+ local lookupmatch = lookupcache[getchar(n)]
+ if lookupmatch then
+ local h, d, ok = handler(sub,n,kind,lookupname,lookupmatch,sequence,lookuphash,1,injection)
+ if ok then
+ done = true
+ success = true
+ end
end
+ else
+ -- message
end
end
- setfield(prev,"next",disc)
- -- setfield(next,"prev",disc)
end
- return next
end
while start do
local id = getid(start)
if id == glyph_code then
- if getfont(start) == font and getsubtype(start) < 256 then
+ if getfont(start) == font and getsubtype(start) < 256 then -- why a 256 test ...
local a = getattr(start,0)
if a then
a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)
@@ -2340,81 +3286,37 @@ local function featuresprocessor(head,font,attr)
a = not attribute or getprop(start,a_state) == attribute
end
if a then
- local lookupmatch = lookupcache[getchar(start)]
+ local char = getchar(start)
+ local lookupmatch = lookupcache[char]
if lookupmatch then
-- sequence kan weg
local ok
- head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
+ head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)
if ok then
success = true
+ elseif gpossing and zwnjruns and char == zwnj then
+ discrun(start,d_run)
end
+ elseif gpossing and zwnjruns and char == zwnj then
+ discrun(start,d_run)
end
if start then start = getnext(start) end
else
- start = getnext(start)
+ start = getnext(start)
end
else
start = getnext(start)
end
elseif id == disc_code then
- -- mostly for gsub
- if getsubtype(start) == discretionary_code then
- local pre = getfield(start,"pre")
- if pre then
- local new = subrun(pre)
- if new then setfield(start,"pre",new) end
- end
- local post = getfield(start,"post")
- if post then
- local new = subrun(post)
- if new then setfield(start,"post",new) end
- end
- local replace = getfield(start,"replace")
- if replace then
- local new = subrun(replace)
- if new then setfield(start,"replace",new) end
- end
-elseif typ == "gpos_single" or typ == "gpos_pair" then
- kerndisc(start)
- end
- start = getnext(start)
- elseif id == whatsit_code then -- will be function
- local subtype = getsubtype(start)
- if subtype == dir_code then
- local dir = getfield(start,"dir")
- if dir == "+TRT" or dir == "+TLT" then
- topstack = topstack + 1
- dirstack[topstack] = dir
- elseif dir == "-TRT" or dir == "-TLT" then
- topstack = topstack - 1
- end
- local newdir = dirstack[topstack]
- if newdir == "+TRT" then
- rlmode = -1
- elseif newdir == "+TLT" then
- rlmode = 1
- else
- rlmode = rlparmode
- end
- if trace_directions then
- report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
- end
- elseif subtype == localpar_code then
- local dir = getfield(start,"dir")
- if dir == "TRT" then
- rlparmode = -1
- elseif dir == "TLT" then
- rlparmode = 1
- else
- rlparmode = 0
- end
- -- one might wonder if the par dir should be looked at, so we might as well drop the next line
- rlmode = rlparmode
- if trace_directions then
- report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
- end
+ if gpossing then
+ kernrun(start,k_run)
+ start = getnext(start)
+ elseif typ == "gsub_ligature" then
+ start = testrun(start,t_run,c_run)
+ else
+ comprun(start,c_run)
+ start = getnext(start)
end
- start = getnext(start)
elseif id == math_code then
start = getnext(end_of_math(start))
else
@@ -2422,15 +3324,23 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then
end
end
end
+
else
- local function subrun(start)
- -- mostly for gsub, gpos would demand a more clever approach
- local head = start
- local done = false
+ local function c_run(head)
+ local done = false
+ local start = sweephead[head]
+ if start then
+ sweephead[head] = nil
+ else
+ start = head
+ end
while start do
local id = getid(start)
- if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then
+ if id ~= glyph_code then
+ -- very unlikely
+ start = getnext(start)
+ elseif getfont(start) == font and getsubtype(start) < 256 then
local a = getattr(start,0)
if a then
a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)
@@ -2438,15 +3348,16 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then
a = not attribute or getprop(start,a_state) == attribute
end
if a then
+ local char = getchar(start)
for i=1,ns do
local lookupname = subtables[i]
local lookupcache = lookuphash[lookupname]
if lookupcache then
- local lookupmatch = lookupcache[getchar(start)]
+ local lookupmatch = lookupcache[char]
if lookupmatch then
-- we could move all code inline but that makes things even more unreadable
local ok
- head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+ head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
if ok then
done = true
break
@@ -2464,50 +3375,127 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then
start = getnext(start)
end
else
- start = getnext(start)
+ return head, false
end
end
if done then
success = true
- return head
end
+ return head, done
end
- local function kerndisc(disc) -- we can assume that prev and next are glyphs
- local prev = getprev(disc)
- local next = getnext(disc)
- if prev and next then
- setfield(prev,"next",next)
- -- setfield(next,"prev",prev)
- local a = getattr(prev,0)
- if a then
- a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute)
- else
- a = not attribute or getprop(prev,a_state) == attribute
+ local function d_run(prev)
+ local a = getattr(prev,0)
+ if a then
+ a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute)
+ else
+ a = not attribute or getprop(prev,a_state) == attribute
+ end
+ if a then
+ -- brr prev can be disc
+ local char = getchar(prev)
+ for i=1,ns do
+ local lookupname = subtables[i]
+ local lookupcache = lookuphash[lookupname]
+ if lookupcache then
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ -- we could move all code inline but that makes things even more unreadable
+ local h, d, ok = handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,i)
+ if ok then
+ done = true
+ break
+ end
+ end
+ else
+ report_missing_cache(typ,lookupname)
+ end
end
- if a then
- for i=1,ns do
- local lookupname = subtables[i]
- local lookupcache = lookuphash[lookupname]
- if lookupcache then
- local lookupmatch = lookupcache[getchar(prev)]
- if lookupmatch then
- -- we could move all code inline but that makes things even more unreadable
- local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
- if ok then
- done = true
- break
+ end
+ end
+
+ local function k_run(sub,injection,last)
+ local a = getattr(sub,0)
+ if a then
+ a = (a == attr) and (not attribute or getprop(sub,a_state) == attribute)
+ else
+ a = not attribute or getprop(sub,a_state) == attribute
+ end
+ if a then
+ for n in traverse_nodes(sub) do -- only gpos
+ if n == last then
+ break
+ end
+ local id = getid(n)
+ if id == glyph_code then
+ local char = getchar(n)
+ for i=1,ns do
+ local lookupname = subtables[i]
+ local lookupcache = lookuphash[lookupname]
+ if lookupcache then
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ local h, d, ok = handler(head,n,kind,lookupname,lookupmatch,sequence,lookuphash,i,injection)
+ if ok then
+ done = true
+ break
+ end
end
+ else
+ report_missing_cache(typ,lookupname)
end
- else
- report_missing_cache(typ,lookupname)
end
+ else
+ -- message
end
end
- setfield(prev,"next",disc)
- -- setfield(next,"prev",disc)
end
- return next
+ end
+
+ local function t_run(start,stop)
+ while start ~= stop do
+ local id = getid(start)
+ if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then
+ local a = getattr(start,0)
+ if a then
+ a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)
+ else
+ a = not attribute or getprop(start,a_state) == attribute
+ end
+ if a then
+ local char = getchar(start)
+ for i=1,ns do
+ local lookupname = subtables[i]
+ local lookupcache = lookuphash[lookupname]
+ if lookupcache then
+ local lookupmatch = lookupcache[char]
+ if lookupmatch then
+ -- if we need more than ligatures we can outline the code and use functions
+ local s = getnext(start)
+ local l = nil
+ while s do
+ local lg = lookupmatch[getchar(s)]
+ if lg then
+ l = lg
+ s = getnext(s)
+ else
+ break
+ end
+ end
+ if l and l.ligature then
+ return true
+ end
+ end
+ else
+ report_missing_cache(typ,lookupname)
+ end
+ end
+ end
+ start = getnext(start)
+ else
+ break
+ end
+ end
end
while start do
@@ -2522,21 +3510,26 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then
end
if a then
for i=1,ns do
- local lookupname = subtables[i]
+ local lookupname = subtables[i]
local lookupcache = lookuphash[lookupname]
if lookupcache then
- local lookupmatch = lookupcache[getchar(start)]
+ local char = getchar(start)
+ local lookupmatch = lookupcache[char]
if lookupmatch then
-- we could move all code inline but that makes things even more unreadable
local ok
- head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+ head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
if ok then
success = true
break
elseif not start then
-- don't ask why ... shouldn't happen
break
+ elseif gpossing and zwnjruns and char == zwnj then
+ discrun(start,d_run)
end
+ elseif gpossing and zwnjruns and char == zwnj then
+ discrun(start,d_run)
end
else
report_missing_cache(typ,lookupname)
@@ -2550,63 +3543,15 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then
start = getnext(start)
end
elseif id == disc_code then
- -- mostly for gsub
- if getsubtype(start) == discretionary_code then
- local pre = getfield(start,"pre")
- if pre then
- local new = subrun(pre)
- if new then setfield(start,"pre",new) end
- end
- local post = getfield(start,"post")
- if post then
- local new = subrun(post)
- if new then setfield(start,"post",new) end
- end
- local replace = getfield(start,"replace")
- if replace then
- local new = subrun(replace)
- if new then setfield(start,"replace",new) end
- end
-elseif typ == "gpos_single" or typ == "gpos_pair" then
- kerndisc(start)
- end
- start = getnext(start)
- elseif id == whatsit_code then
- local subtype = getsubtype(start)
- if subtype == dir_code then
- local dir = getfield(start,"dir")
- if dir == "+TRT" or dir == "+TLT" then
- topstack = topstack + 1
- dirstack[topstack] = dir
- elseif dir == "-TRT" or dir == "-TLT" then
- topstack = topstack - 1
- end
- local newdir = dirstack[topstack]
- if newdir == "+TRT" then
- rlmode = -1
- elseif newdir == "+TLT" then
- rlmode = 1
- else
- rlmode = rlparmode
- end
- if trace_directions then
- report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
- end
- elseif subtype == localpar_code then
- local dir = getfield(start,"dir")
- if dir == "TRT" then
- rlparmode = -1
- elseif dir == "TLT" then
- rlparmode = 1
- else
- rlparmode = 0
- end
- rlmode = rlparmode
- if trace_directions then
- report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
- end
+ if gpossing then
+ kernrun(start,k_run)
+ start = getnext(start)
+ elseif typ == "gsub_ligature" then
+ start = testrun(start,t_run,c_run)
+ else
+ comprun(start,c_run)
+ start = getnext(start)
end
- start = getnext(start)
elseif id == math_code then
start = getnext(end_of_math(start))
else
@@ -2629,6 +3574,8 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then
return head, done
end
+-- this might move to the loader
+
local function generic(lookupdata,lookupname,unicode,lookuphash)
local target = lookuphash[lookupname]
if target then
@@ -2638,47 +3585,48 @@ local function generic(lookupdata,lookupname,unicode,lookuphash)
end
end
-local action = {
+local function ligature(lookupdata,lookupname,unicode,lookuphash)
+ local target = lookuphash[lookupname]
+ if not target then
+ target = { }
+ lookuphash[lookupname] = target
+ end
+ for i=1,#lookupdata do
+ local li = lookupdata[i]
+ local tu = target[li]
+ if not tu then
+ tu = { }
+ target[li] = tu
+ end
+ target = tu
+ end
+ target.ligature = unicode
+end
+
+local function pair(lookupdata,lookupname,unicode,lookuphash)
+ local target = lookuphash[lookupname]
+ if not target then
+ target = { }
+ lookuphash[lookupname] = target
+ end
+ local others = target[unicode]
+ local paired = lookupdata[1]
+ if others then
+ others[paired] = lookupdata
+ else
+ others = { [paired] = lookupdata }
+ target[unicode] = others
+ end
+end
+local action = {
substitution = generic,
multiple = generic,
alternate = generic,
position = generic,
-
- ligature = function(lookupdata,lookupname,unicode,lookuphash)
- local target = lookuphash[lookupname]
- if not target then
- target = { }
- lookuphash[lookupname] = target
- end
- for i=1,#lookupdata do
- local li = lookupdata[i]
- local tu = target[li]
- if not tu then
- tu = { }
- target[li] = tu
- end
- target = tu
- end
- target.ligature = unicode
- end,
-
- pair = function(lookupdata,lookupname,unicode,lookuphash)
- local target = lookuphash[lookupname]
- if not target then
- target = { }
- lookuphash[lookupname] = target
- end
- local others = target[unicode]
- local paired = lookupdata[1]
- if others then
- others[paired] = lookupdata
- else
- others = { [paired] = lookupdata }
- target[unicode] = others
- end
- end,
-
+ ligature = ligature,
+ pair = pair,
+ kern = pair,
}
local function prepare_lookups(tfmdata)
@@ -2691,12 +3639,17 @@ local function prepare_lookups(tfmdata)
local lookuptypes = resources.lookuptypes
local characters = tfmdata.characters
local descriptions = tfmdata.descriptions
+ local duplicates = resources.duplicates
-- we cannot free the entries in the descriptions as sometimes we access
-- then directly (for instance anchors) ... selectively freeing does save
-- much memory as it's only a reference to a table and the slot in the
-- description hash is not freed anyway
+ -- we can delay this using metatables so that we don't make the hashes for
+ -- features we don't use but then we need to loop over the characters
+ -- many times so we gain nothing
+
for unicode, character in next, characters do -- we cannot loop over descriptions !
local description = descriptions[unicode]
@@ -2706,7 +3659,7 @@ local function prepare_lookups(tfmdata)
local lookups = description.slookups
if lookups then
for lookupname, lookupdata in next, lookups do
- action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash)
+ action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash,duplicates)
end
end
@@ -2716,7 +3669,7 @@ local function prepare_lookups(tfmdata)
local lookuptype = lookuptypes[lookupname]
for l=1,#lookuplist do
local lookupdata = lookuplist[l]
- action[lookuptype](lookupdata,lookupname,unicode,lookuphash)
+ action[lookuptype](lookupdata,lookupname,unicode,lookuphash,duplicates)
end
end
end
@@ -2740,7 +3693,7 @@ local function prepare_lookups(tfmdata)
for name, anchor in next, anchors do
local lookups = anchor_to_lookup[name]
if lookups then
- for lookup, _ in next, lookups do
+ for lookup in next, lookups do
local target = lookuphash[lookup]
if target then
target[unicode] = anchors
@@ -2760,6 +3713,8 @@ local function prepare_lookups(tfmdata)
end
+-- so far
+
local function split(replacement,original)
local result = { }
for i=1,#replacement do
@@ -2835,7 +3790,7 @@ local function prepare_contextchains(tfmdata)
-- use sequence[start] instead but it's somewhat ugly.
nt = nt + 1
t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements }
- for unic, _ in next, sequence[start] do
+ for unic in next, sequence[start] do
local cu = contexts[unic]
if not cu then
contexts[unic] = t
diff --git a/src/fontloader/misc/fontloader-fonts.lua b/src/fontloader/misc/fontloader-fonts.lua
index c81e8cd..f18ba35 100644
--- a/src/fontloader/misc/fontloader-fonts.lua
+++ b/src/fontloader/misc/fontloader-fonts.lua
@@ -27,16 +27,10 @@ if not modules then modules = { } end modules ['luatex-fonts'] = {
-- also add more helper code here, but that depends to what extend metatex (sidetrack of context)
-- evolves into a low level layer (depends on time, as usual).
-texio.write_nl("")
-texio.write_nl("--------------------------------------------------------------------------------")
-texio.write_nl("The font code has been brought in sync with the context version of 2014.12.21 so")
-texio.write_nl("if things don't work out as expected the interfacing needs to be checked. When")
-texio.write_nl("this works as expected a second upgrade will happen that gives a more complete")
-texio.write_nl("support and another sync with the context code (that new code is currently being")
-texio.write_nl("tested. The base pass is now integrated in the main pass. The results can differ")
-texio.write_nl("from those in context because there we integrate some mechanisms differently.")
-texio.write_nl("--------------------------------------------------------------------------------")
-texio.write_nl("")
+-- The code here is the same as in context version 2015.09.11 but the rendering in context can be
+-- different from generic. This can be a side effect of additional callbacks, additional features
+-- and interferences between mechanisms between macro packages. We use the rendering in context
+-- and luatex-plain as reference for issues.
utf = utf or unicode.utf8
@@ -221,9 +215,9 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then
loadmodule('font-oti.lua')
loadmodule('font-otf.lua')
loadmodule('font-otb.lua')
- loadmodule('luatex-fonts-inj.lua')
+ loadmodule('luatex-fonts-inj.lua') -- normally the same as font-inj.lua
loadmodule('luatex-fonts-ota.lua')
- loadmodule('luatex-fonts-otn.lua')
+ loadmodule('luatex-fonts-otn.lua') -- normally the same as font-otn.lua
loadmodule('font-otp.lua')
loadmodule('luatex-fonts-lua.lua')
loadmodule('font-def.lua') -- this code (stripped) might end up in luatex-fonts-def.lua
diff --git a/src/fontloader/misc/fontloader-l-lpeg.lua b/src/fontloader/misc/fontloader-l-lpeg.lua
index 55a0d89..5be1246 100644
--- a/src/fontloader/misc/fontloader-l-lpeg.lua
+++ b/src/fontloader/misc/fontloader-l-lpeg.lua
@@ -82,7 +82,7 @@ local lpegtype, lpegmatch, lpegprint = lpeg.type, lpeg.match, lpeg.print
-- let's start with an inspector:
if setinspector then
- setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end)
+ setinspector("lpeg",function(v) if lpegtype(v) then lpegprint(v) return true end end)
end
-- Beware, we predefine a bunch of patterns here and one reason for doing so
diff --git a/src/fontloader/misc/fontloader-l-lua.lua b/src/fontloader/misc/fontloader-l-lua.lua
index 1a2a987..cb61829 100644
--- a/src/fontloader/misc/fontloader-l-lua.lua
+++ b/src/fontloader/misc/fontloader-l-lua.lua
@@ -129,22 +129,36 @@ local print, select, tostring = print, select, tostring
local inspectors = { }
-function setinspector(inspector) -- global function
- inspectors[#inspectors+1] = inspector
+function setinspector(kind,inspector) -- global function
+ inspectors[kind] = inspector
end
function inspect(...) -- global function
for s=1,select("#",...) do
local value = select(s,...)
- local done = false
- for i=1,#inspectors do
- done = inspectors[i](value)
- if done then
- break
+ if value == nil then
+ print("nil")
+ else
+ local done = false
+ -- type driven (table)
+ local kind = type(value)
+ local inspector = inspectors[kind]
+ if inspector then
+ done = inspector(value)
+ if done then
+ break
+ end
+ end
+ -- whatever driven (token, node, ...)
+ for kind, inspector in next, inspectors do
+ done = inspector(value)
+ if done then
+ break
+ end
+ end
+ if not done then
+ print(tostring(value))
end
- end
- if not done then
- print(tostring(value))
end
end
end
diff --git a/src/fontloader/misc/fontloader-l-string.lua b/src/fontloader/misc/fontloader-l-string.lua
index 70c66f6..e9dc2bb 100644
--- a/src/fontloader/misc/fontloader-l-string.lua
+++ b/src/fontloader/misc/fontloader-l-string.lua
@@ -192,10 +192,11 @@ string.itself = function(s) return s end
-- also handy (see utf variant)
-local pattern = Ct(C(1)^0) -- string and not utf !
+local pattern_c = Ct( C(1) ^0) -- string and not utf !
+local pattern_b = Ct((C(1)/byte)^0)
-function string.totable(str)
- return lpegmatch(pattern,str)
+function string.totable(str,bytes)
+ return lpegmatch(bytes and pattern_b or pattern_c,str)
end
-- handy from within tex:
diff --git a/src/fontloader/misc/fontloader-l-table.lua b/src/fontloader/misc/fontloader-l-table.lua
index b02f210..552097e 100644
--- a/src/fontloader/misc/fontloader-l-table.lua
+++ b/src/fontloader/misc/fontloader-l-table.lua
@@ -1144,7 +1144,7 @@ function table.print(t,...)
end
if setinspector then
- setinspector(function(v) if type(v) == "table" then serialize(print,v,"table") return true end end)
+ setinspector("table",function(v) if type(v) == "table" then serialize(print,v,"table") return true end end)
end
-- -- -- obsolete but we keep them for a while and might comment them later -- -- --
diff --git a/src/fontloader/misc/fontloader-mplib.lua b/src/fontloader/misc/fontloader-mplib.lua
index c6628ac..fd6eb97 100644
--- a/src/fontloader/misc/fontloader-mplib.lua
+++ b/src/fontloader/misc/fontloader-mplib.lua
@@ -22,7 +22,9 @@ if metapost and metapost.version then
else
- local format, concat, abs, match = string.format, table.concat, math.abs, string.match
+ local format, match, gsub = string.format, string.match, string.gsub
+ local concat = table.concat
+ local abs = math.abs
local mplib = require ('mplib')
local kpse = require ('kpse')
@@ -144,10 +146,101 @@ else
metapost.make = metapost.make or function()
end
+ local template = [[
+ \pdfoutput=1
+ \pdfpkresolution600
+ \pdfcompresslevel=9
+ %s\relax
+ \hsize=100in
+ \vsize=\hsize
+ \hoffset=-1in
+ \voffset=\hoffset
+ \topskip=0pt
+ \setbox0=\hbox{%s}\relax
+ \pageheight=\ht0
+ \pagewidth=\wd0
+ \box0
+ \bye
+ ]]
+
+ metapost.texrunner = "mtxrun --script plain"
+
+ local texruns = 0 -- per document
+ local texhash = { } -- per document
+
+ function metapost.maketext(mpd,str,what)
+ -- inefficient but one can always use metafun .. it's more a test
+ -- feature
+ local verbatimtex = mpd.verbatimtex
+ if not verbatimtex then
+ verbatimtex = { }
+ mpd.verbatimtex = verbatimtex
+ end
+ if what == 1 then
+ table.insert(verbatimtex,str)
+ else
+ local texcode = format(template,concat(verbatimtex,"\n"),str)
+ local texdone = texhash[texcode]
+ local jobname = tex.jobname
+ if not texdone then
+ texruns = texruns + 1
+ texdone = texruns
+ texhash[texcode] = texdone
+ local texname = format("%s-mplib-%s.tmp",jobname,texdone)
+ local logname = format("%s-mplib-%s.log",jobname,texdone)
+ local pdfname = format("%s-mplib-%s.pdf",jobname,texdone)
+ io.savedata(texname,texcode)
+ os.execute(format("%s %s",metapost.texrunner,texname))
+ os.remove(texname)
+ os.remove(logname)
+ end
+ return format('"image::%s-mplib-%s.pdf" infont defaultfont',jobname,texdone)
+ end
+ end
+
+ local function mpprint(buffer,...)
+ for i=1,select("#",...) do
+ local value = select(i,...)
+ if value ~= nil then
+ local t = type(value)
+ if t == "number" then
+ buffer[#buffer+1] = format("%.16f",value)
+ elseif t == "string" then
+ buffer[#buffer+1] = value
+ elseif t == "table" then
+ buffer[#buffer+1] = "(" .. concat(value,",") .. ")"
+ else -- boolean or whatever
+ buffer[#buffer+1] = tostring(value)
+ end
+ end
+ end
+ end
+
+ function metapost.runscript(mpd,code)
+ local code = loadstring(code)
+ if type(code) == "function" then
+ local buffer = { }
+ function metapost.print(...)
+ mpprint(buffer,...)
+ end
+ code()
+ -- mpd.buffer = buffer -- for tracing
+ return concat(buffer,"")
+ end
+ return ""
+ end
+
function metapost.load(name)
+ local mpd = {
+ buffer = { },
+ verbatim = { }
+ }
local mpx = mplib.new {
ini_version = true,
- find_file = metapost.finder,
+ find_file = metapost.finder,
+ make_text = function(...) return metapost.maketext (mpd,...) end,
+ run_script = function(...) return metapost.runscript(mpd,...) end,
+ extensions = 1,
}
local result
if not mpx then
@@ -217,8 +310,8 @@ else
return figure:objects()
end
- function metapost.convert(result, flusher)
- metapost.flush(result, flusher)
+ function metapost.convert(result,flusher)
+ metapost.flush(result,flusher)
return true -- done
end
@@ -239,8 +332,13 @@ else
end
function pdf_textfigure(font,size,text,width,height,depth)
- text = text:gsub(".","\\hbox{%1}") -- kerning happens in metapost
- tex.sprint(format("\\MPLIBtextext{%s}{%s}{%s}{%s}{%s}",font,size,text,0,-( 7200/ 7227)/65536*depth))
+ local how, what = match(text,"^(.-)::(.+)$")
+ if how == "image" then
+ tex.sprint(format("\\MPLIBpdftext{%s}{%s}",what,depth))
+ else
+ text = gsub(text,".","\\hbox{%1}") -- kerning happens in metapost
+ tex.sprint(format("\\MPLIBtextext{%s}{%s}{%s}{%s}",font,size,text,depth))
+ end
end
local bend_tolerance = 131/65536
@@ -375,8 +473,10 @@ else
pdf_literalcode("Q")
else
local cs = object.color
+ local cr = false
if cs and #cs > 0 then
- pdf_literalcode(metapost.colorconverter(cs))
+ cs, cr = metapost.colorconverter(cs)
+ pdf_literalcode(cs)
end
local ml = object.miterlimit
if ml and ml ~= miterlimit then
diff --git a/src/fontloader/misc/fontloader-mplib.tex b/src/fontloader/misc/fontloader-mplib.tex
index 09dd179..f9de4b2 100644
--- a/src/fontloader/misc/fontloader-mplib.tex
+++ b/src/fontloader/misc/fontloader-mplib.tex
@@ -106,15 +106,14 @@
%D Text items have a special handler:
-\def\MPLIBtextext#1#2#3#4#5%
+\def\MPLIBtextext#1#2#3#4%
{\begingroup
\setbox\mplibscratchbox\hbox
{\font\temp=#1 at #2bp%
\temp
#3}%
\setbox\mplibscratchbox\hbox
- {\hskip#4 bp%
- \raise#5 bp%
+ {\raise#4sp%
\box\mplibscratchbox}%
\wd\mplibscratchbox0pt%
\ht\mplibscratchbox0pt%
@@ -122,4 +121,20 @@
\box\mplibscratchbox
\endgroup}
+\def\MPLIBpdftext#1#2%
+ {\ifcsname mplib::#1\endcsname
+ % already done, forgotten outside convert group
+ \message{<reusing mplib: #1>}%
+ \else
+ \message{<embedding mplib: #1>}%
+ \immediate\pdfximage{#1}% we cannot remove the file as it is included last
+ \expandafter\edef\csname mplib::#1\endcsname{\the\pdflastximage}%
+ \fi
+ \setbox\mplibscratchbox\hbox
+ {\raise#2sp\hbox{\pdfrefximage\csname mplib::#1\endcsname}}%
+ \wd\mplibscratchbox0pt%
+ \ht\mplibscratchbox0pt%
+ \dp\mplibscratchbox0pt%
+ \box\mplibscratchbox}
+
\endinput
diff --git a/src/fontloader/misc/fontloader-plain.tex b/src/fontloader/misc/fontloader-plain.tex
index c9a9e36..99347ed 100644
--- a/src/fontloader/misc/fontloader-plain.tex
+++ b/src/fontloader/misc/fontloader-plain.tex
@@ -11,7 +11,30 @@
\directlua {tex.enableprimitives('', tex.extraprimitives())}
-\pdfoutput=1
+% We assume that pdf is used.
+
+\ifdefined\pdfextension
+ \input luatex-pdf \relax
+\fi
+
+\pdfoutput 1
+
+% We set the page dimensions because otherwise the backend does weird things
+% when we have for instance this on a line of its own:
+%
+% \hbox to 100cm {\hss wide indeed\hss}
+%
+% The page dimension calculation is a fuzzy one as there are some compensations
+% for the \hoffset and \voffset and such. I remember long discussions and much
+% trial and error in figuring this out during pdftex development times. Where
+% a dvi driver will project on a papersize (and thereby clip) the pdf backend
+% has to deal with the lack of a page concept on tex by some guessing. Normally
+% a macro package will set the dimensions to something reasonable anyway.
+
+\pagewidth 8.5in
+\pageheight 11.0in
+
+% We load some code at runtime:
\everyjob \expandafter {%
\the\everyjob
@@ -20,9 +43,11 @@
\input {luatex-math}%
\input {luatex-languages}%
\input {luatex-mplib}%
- % \input {luatex-gadgets}%
+ \input {luatex-gadgets}%
}
+% We also patch the version number:
+
\edef\fmtversion{\fmtversion+luatex}
\dump
diff --git a/src/fontloader/misc/fontloader-test.tex b/src/fontloader/misc/fontloader-test.tex
index 6f48e0c..f851aab 100644
--- a/src/fontloader/misc/fontloader-test.tex
+++ b/src/fontloader/misc/fontloader-test.tex
@@ -1,3 +1,5 @@
+% texformat=luatex-plain
+
%D \module
%D [ file=luatex-test,
%D version=2009.12.01,
@@ -33,12 +35,12 @@
\font\mathtest=cambria(math) {\mathtest 123}
-\font\gothic=msgothic(ms-gothic) {\gothic whatever}
+% \font\gothic=msgothic(ms-gothic) {\gothic whatever} % no longer in windows 10
\bgroup
- \pdfprotrudechars2
- \pdfadjustspacing2
+ \ifdefined\pdfprotrudechars \pdfprotrudechars \else \protrudechars \fi 2 \relax
+ \ifdefined\pdfadjustspacing \pdfadjustspacing \else \adjustspacing \fi 2 \relax
\font\testb=file:lmroman12-regular:+liga;extend=1.5 at 12pt \testb \input tufte \par
\font\testb=file:lmroman12-regular:+liga;slant=0.8 at 12pt \testb \input tufte \par
@@ -48,12 +50,30 @@
\setmplibformat{plain}
+\directlua {
+ function MpTest()
+ metapost.print("fullcircle scaled 3cm")
+ end
+}
+
\mplibcode
beginfig(1) ;
draw fullcircle
scaled 10cm
withcolor red
withpen pencircle xscaled 4mm yscaled 2mm rotated 30 ;
+ draw "test" infont defaultfont scaled 4 ;
+ verbatimtex \sl etex;
+ draw btex some more test etex scaled 2 ;
+ currentpicture := currentpicture shifted (0,1cm) ;
+ verbatimtex \bf etex;
+ draw btex another test etex scaled 2 ;
+ currentpicture := currentpicture shifted (0,1cm) ;
+ draw btex another test etex scaled 2 ;
+ draw
+ runscript("MpTest()")
+ withcolor green
+ withpen pencircle xscaled 2mm yscaled 1mm rotated 20 ;
endfig ;
\endmplibcode
diff --git a/src/fontloader/misc/fontloader-util-str.lua b/src/fontloader/misc/fontloader-util-str.lua
index c2139b1..95534c8 100644
--- a/src/fontloader/misc/fontloader-util-str.lua
+++ b/src/fontloader/misc/fontloader-util-str.lua
@@ -6,7 +6,7 @@ if not modules then modules = { } end modules ['util-str'] = {
license = "see context related readme files"
}
-utilities = utilities or {}
+utilities = utilities or { }
utilities.strings = utilities.strings or { }
local strings = utilities.strings
@@ -354,7 +354,16 @@ function string.autosingle(s,sep)
return ("'" .. tostring(s) .. "'")
end
-local tracedchars = { }
+local tracedchars = { [0] =
+ -- the regular bunch
+ "[null]", "[soh]", "[stx]", "[etx]", "[eot]", "[enq]", "[ack]", "[bel]",
+ "[bs]", "[ht]", "[lf]", "[vt]", "[ff]", "[cr]", "[so]", "[si]",
+ "[dle]", "[dc1]", "[dc2]", "[dc3]", "[dc4]", "[nak]", "[syn]", "[etb]",
+ "[can]", "[em]", "[sub]", "[esc]", "[fs]", "[gs]", "[rs]", "[us]",
+ -- plus space
+ "[space]", -- 0x20
+}
+
string.tracedchars = tracedchars
strings.tracers = tracedchars
diff --git a/src/fontloader/runtime/fontloader-reference.lua b/src/fontloader/runtime/fontloader-reference.lua
index d8095a2..ae36617 100644
--- a/src/fontloader/runtime/fontloader-reference.lua
+++ b/src/fontloader/runtime/fontloader-reference.lua
@@ -1,6 +1,6 @@
-- merged file : luatex-fonts-merged.lua
-- parent file : luatex-fonts.lua
--- merge date : 05/24/15 12:42:55
+-- merge date : 11/19/15 19:13:15
do -- begin closure to overcome local limits and interference
@@ -57,21 +57,33 @@ if not package.loaders then
end
local print,select,tostring=print,select,tostring
local inspectors={}
-function setinspector(inspector)
- inspectors[#inspectors+1]=inspector
+function setinspector(kind,inspector)
+ inspectors[kind]=inspector
end
function inspect(...)
for s=1,select("#",...) do
local value=select(s,...)
- local done=false
- for i=1,#inspectors do
- done=inspectors[i](value)
- if done then
- break
+ if value==nil then
+ print("nil")
+ else
+ local done=false
+ local kind=type(value)
+ local inspector=inspectors[kind]
+ if inspector then
+ done=inspector(value)
+ if done then
+ break
+ end
+ end
+ for kind,inspector in next,inspectors do
+ done=inspector(value)
+ if done then
+ break
+ end
+ end
+ if not done then
+ print(tostring(value))
end
- end
- if not done then
- print(tostring(value))
end
end
end
@@ -112,7 +124,7 @@ local floor=math.floor
local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt
local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print
if setinspector then
- setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end)
+ setinspector("lpeg",function(v) if lpegtype(v) then lpegprint(v) return true end end)
end
lpeg.patterns=lpeg.patterns or {}
local patterns=lpeg.patterns
@@ -995,9 +1007,10 @@ function string.valid(str,default)
return (type(str)=="string" and str~="" and str) or default or nil
end
string.itself=function(s) return s end
-local pattern=Ct(C(1)^0)
-function string.totable(str)
- return lpegmatch(pattern,str)
+local pattern_c=Ct(C(1)^0)
+local pattern_b=Ct((C(1)/byte)^0)
+function string.totable(str,bytes)
+ return lpegmatch(bytes and pattern_b or pattern_c,str)
end
local replacer=lpeg.replacer("@","%%")
function string.tformat(fmt,...)
@@ -1884,7 +1897,7 @@ function table.print(t,...)
end
end
if setinspector then
- setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end)
+ setinspector("table",function(v) if type(v)=="table" then serialize(print,v,"table") return true end end)
end
function table.sub(t,i,j)
return { unpack(t,i,j) }
@@ -2937,7 +2950,13 @@ function string.autosingle(s,sep)
end
return ("'"..tostring(s).."'")
end
-local tracedchars={}
+local tracedchars={ [0]=
+ "[null]","[soh]","[stx]","[etx]","[eot]","[enq]","[ack]","[bel]",
+ "[bs]","[ht]","[lf]","[vt]","[ff]","[cr]","[so]","[si]",
+ "[dle]","[dc1]","[dc2]","[dc3]","[dc4]","[nak]","[syn]","[etb]",
+ "[can]","[em]","[sub]","[esc]","[fs]","[gs]","[rs]","[us]",
+ "[space]",
+}
string.tracedchars=tracedchars
strings.tracers=tracedchars
function string.tracedchar(b)
@@ -3886,6 +3905,8 @@ local nodecodes={} for k,v in next,node.types () do nodecodes[string.gsub(v,"_"
local whatcodes={} for k,v in next,node.whatsits() do whatcodes[string.gsub(v,"_","")]=k end
local glyphcodes={ [0]="character","glyph","ligature","ghost","left","right" }
local disccodes={ [0]="discretionary","explicit","automatic","regular","first","second" }
+for i=0,#glyphcodes do glyphcodes[glyphcodes[i]]=i end
+for i=0,#disccodes do disccodes [disccodes [i]]=i end
nodes.nodecodes=nodecodes
nodes.whatcodes=whatcodes
nodes.whatsitcodes=whatcodes
@@ -4190,8 +4211,8 @@ end
constructors.setfactor()
function constructors.scaled(scaledpoints,designsize)
if scaledpoints<0 then
+ local factor=constructors.factor
if designsize then
- local factor=constructors.factor
if designsize>factor then
return (- scaledpoints/1000)*designsize
else
@@ -4361,6 +4382,7 @@ function constructors.scale(tfmdata,specification)
local hdelta=delta
local vdelta=delta
target.designsize=parameters.designsize
+ target.units=units
target.units_per_em=units
local direction=properties.direction or tfmdata.direction or 0
target.direction=direction
@@ -4472,21 +4494,28 @@ function constructors.scale(tfmdata,specification)
target.nomath=true
target.mathparameters=nil
end
- local italickey="italic"
- local useitalics=true
if hasmath then
- autoitalicamount=false
- elseif properties.textitalics then
- italickey="italic_correction"
- useitalics=false
- if properties.delaytextitalics then
+ local mathitalics=properties.mathitalics
+ if mathitalics==false then
+ if trace_defining then
+ report_defining("%s italics %s for font %a, fullname %a, filename %a","math",hasitalics and "ignored" or "disabled",name,fullname,filename)
+ end
+ hasitalics=false
+ autoitalicamount=false
+ end
+ else
+ local textitalics=properties.textitalics
+ if textitalics==false then
+ if trace_defining then
+ report_defining("%s italics %s for font %a, fullname %a, filename %a","text",hasitalics and "ignored" or "disabled",name,fullname,filename)
+ end
+ hasitalics=false
autoitalicamount=false
end
end
if trace_defining then
report_defining("defining tfm, name %a, fullname %a, filename %a, hscale %a, vscale %a, math %a, italics %a",
- name,fullname,filename,hdelta,vdelta,
- hasmath and "enabled" or "disabled",useitalics and "enabled" or "disabled")
+ name,fullname,filename,hdelta,vdelta,hasmath and "enabled" or "disabled",hasitalics and "enabled" or "disabled")
end
constructors.beforecopyingcharacters(target,tfmdata)
local sharedkerns={}
@@ -4584,22 +4613,6 @@ function constructors.scale(tfmdata,specification)
chr.right_protruding=protrusionfactor*width*vr
end
end
- if autoitalicamount then
- local vi=description.italic
- if not vi then
- local vi=description.boundingbox[3]-description.width+autoitalicamount
- if vi>0 then
- chr[italickey]=vi*hdelta
- end
- elseif vi~=0 then
- chr[italickey]=vi*hdelta
- end
- elseif hasitalics then
- local vi=description.italic
- if vi and vi~=0 then
- chr[italickey]=vi*hdelta
- end
- end
if hasmath then
local vn=character.next
if vn then
@@ -4637,7 +4650,11 @@ function constructors.scale(tfmdata,specification)
end
end
end
- local va=character.top_accent
+ local vi=character.vert_italic
+ if vi and vi~=0 then
+ chr.vert_italic=vi*hdelta
+ end
+ local va=character.accent
if va then
chr.top_accent=vdelta*va
end
@@ -4660,6 +4677,27 @@ function constructors.scale(tfmdata,specification)
chr.mathkern=kerns
end
end
+ if hasitalics then
+ local vi=character.italic
+ if vi and vi~=0 then
+ chr.italic=vi*hdelta
+ end
+ end
+ elseif autoitalicamount then
+ local vi=description.italic
+ if not vi then
+ local vi=description.boundingbox[3]-description.width+autoitalicamount
+ if vi>0 then
+ chr.italic=vi*hdelta
+ end
+ elseif vi~=0 then
+ chr.italic=vi*hdelta
+ end
+ elseif hasitalics then
+ local vi=character.italic
+ if vi and vi~=0 then
+ chr.italic=vi*hdelta
+ end
end
if haskerns then
local vk=character.kerns
@@ -4722,6 +4760,7 @@ function constructors.scale(tfmdata,specification)
end
targetcharacters[unicode]=chr
end
+ properties.setitalics=hasitalics
constructors.aftercopyingcharacters(target,tfmdata)
constructors.trytosharefont(target,tfmdata)
return target
@@ -4762,11 +4801,20 @@ function constructors.finalize(tfmdata)
if not parameters.slantfactor then
parameters.slantfactor=tfmdata.slant or 0
end
- if not parameters.designsize then
- parameters.designsize=tfmdata.designsize or (factors.pt*10)
+ local designsize=parameters.designsize
+ if designsize then
+ parameters.minsize=tfmdata.minsize or designsize
+ parameters.maxsize=tfmdata.maxsize or designsize
+ else
+ designsize=factors.pt*10
+ parameters.designsize=designsize
+ parameters.minsize=designsize
+ parameters.maxsize=designsize
end
+ parameters.minsize=tfmdata.minsize or parameters.designsize
+ parameters.maxsize=tfmdata.maxsize or parameters.designsize
if not parameters.units then
- parameters.units=tfmdata.units_per_em or 1000
+ parameters.units=tfmdata.units or tfmdata.units_per_em or 1000
end
if not tfmdata.descriptions then
local descriptions={}
@@ -4829,6 +4877,7 @@ function constructors.finalize(tfmdata)
tfmdata.auto_protrude=nil
tfmdata.extend=nil
tfmdata.slant=nil
+ tfmdata.units=nil
tfmdata.units_per_em=nil
tfmdata.cache=nil
properties.finalized=true
@@ -5393,24 +5442,13 @@ local fonts=fonts or {}
local mappings=fonts.mappings or {}
fonts.mappings=mappings
local allocate=utilities.storage.allocate
-local function loadlumtable(filename)
- local lumname=file.replacesuffix(file.basename(filename),"lum")
- local lumfile=resolvers.findfile(lumname,"map") or ""
- if lumfile~="" and lfs.isfile(lumfile) then
- if trace_loading or trace_mapping then
- report_fonts("loading map table %a",lumfile)
- end
- lumunic=dofile(lumfile)
- return lumunic,lumfile
- end
-end
local hex=R("AF","09")
-local hexfour=(hex*hex*hex*hex)/function(s) return tonumber(s,16) end
-local hexsix=(hex*hex*hex*hex*hex*hex)/function(s) return tonumber(s,16) end
+local hexfour=(hex*hex*hex^-2)/function(s) return tonumber(s,16) end
+local hexsix=(hex*hex*hex^-4)/function(s) return tonumber(s,16) end
local dec=(R("09")^1)/tonumber
local period=P(".")
-local unicode=P("uni")*(hexfour*(period+P(-1))*Cc(false)+Ct(hexfour^1)*Cc(true))
-local ucode=P("u")*(hexsix*(period+P(-1))*Cc(false)+Ct(hexsix^1)*Cc(true))
+local unicode=(P("uni")+P("UNI"))*(hexfour*(period+P(-1))*Cc(false)+Ct(hexfour^1)*Cc(true))
+local ucode=(P("u")+P("U") )*(hexsix*(period+P(-1))*Cc(false)+Ct(hexsix^1)*Cc(true))
local index=P("index")*dec*Cc(false)
local parser=unicode+ucode+index
local parsers={}
@@ -5485,7 +5523,6 @@ local function fromunicode16(str)
return (tonumber(l,16))*0x400+tonumber(r,16)-0xDC00
end
end
-mappings.loadlumtable=loadlumtable
mappings.makenameparser=makenameparser
mappings.tounicode=tounicode
mappings.tounicode16=tounicode16
@@ -5516,243 +5553,161 @@ for k,v in next,overloads do
end
end
mappings.overloads=overloads
-function mappings.addtounicode(data,filename)
+function mappings.addtounicode(data,filename,checklookups)
local resources=data.resources
- local properties=data.properties
- local descriptions=data.descriptions
local unicodes=resources.unicodes
- local lookuptypes=resources.lookuptypes
if not unicodes then
return
end
+ local properties=data.properties
+ local descriptions=data.descriptions
unicodes['space']=unicodes['space'] or 32
unicodes['hyphen']=unicodes['hyphen'] or 45
unicodes['zwj']=unicodes['zwj'] or 0x200D
unicodes['zwnj']=unicodes['zwnj'] or 0x200C
- local private=fonts.constructors.privateoffset
- local unicodevector=fonts.encodings.agl.unicodes
+ local private=fonts.constructors and fonts.constructors.privateoffset or 0xF0000
+ local unicodevector=fonts.encodings.agl.unicodes or {}
+ local contextvector=fonts.encodings.agl.ctxcodes or {}
local missing={}
- local lumunic,uparser,oparser
- local cidinfo,cidnames,cidcodes,usedmap
- cidinfo=properties.cidinfo
- usedmap=cidinfo and fonts.cid.getmap(cidinfo)
+ local nofmissing=0
+ local oparser=nil
+ local cidnames=nil
+ local cidcodes=nil
+ local cidinfo=properties.cidinfo
+ local usedmap=cidinfo and fonts.cid.getmap(cidinfo)
+ local uparser=makenameparser()
if usedmap then
- oparser=usedmap and makenameparser(cidinfo.ordering)
- cidnames=usedmap.names
- cidcodes=usedmap.unicodes
+ oparser=usedmap and makenameparser(cidinfo.ordering)
+ cidnames=usedmap.names
+ cidcodes=usedmap.unicodes
end
- uparser=makenameparser()
- local ns,nl=0,0
+ local ns=0
+ local nl=0
for unic,glyph in next,descriptions do
- local index=glyph.index
local name=glyph.name
- local r=overloads[name]
- if r then
- glyph.unicode=r.unicode
- elseif unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then
- local unicode=lumunic and lumunic[name] or unicodevector[name]
- if unicode then
- glyph.unicode=unicode
- ns=ns+1
- end
- if (not unicode) and usedmap then
- local foundindex=lpegmatch(oparser,name)
- if foundindex then
- unicode=cidcodes[foundindex]
- if unicode then
- glyph.unicode=unicode
- ns=ns+1
- else
- local reference=cidnames[foundindex]
- if reference then
- local foundindex=lpegmatch(oparser,reference)
- if foundindex then
- unicode=cidcodes[foundindex]
- if unicode then
- glyph.unicode=unicode
- ns=ns+1
- end
- end
- if not unicode or unicode=="" then
- local foundcodes,multiple=lpegmatch(uparser,reference)
- if foundcodes then
- glyph.unicode=foundcodes
- if multiple then
- nl=nl+1
- unicode=true
- else
+ if name then
+ local index=glyph.index
+ local r=overloads[name]
+ if r then
+ glyph.unicode=r.unicode
+ elseif not unic or unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then
+ local unicode=unicodevector[name] or contextvector[name]
+ if unicode then
+ glyph.unicode=unicode
+ ns=ns+1
+ end
+ if (not unicode) and usedmap then
+ local foundindex=lpegmatch(oparser,name)
+ if foundindex then
+ unicode=cidcodes[foundindex]
+ if unicode then
+ glyph.unicode=unicode
+ ns=ns+1
+ else
+ local reference=cidnames[foundindex]
+ if reference then
+ local foundindex=lpegmatch(oparser,reference)
+ if foundindex then
+ unicode=cidcodes[foundindex]
+ if unicode then
+ glyph.unicode=unicode
ns=ns+1
- unicode=foundcodes
+ end
+ end
+ if not unicode or unicode=="" then
+ local foundcodes,multiple=lpegmatch(uparser,reference)
+ if foundcodes then
+ glyph.unicode=foundcodes
+ if multiple then
+ nl=nl+1
+ unicode=true
+ else
+ ns=ns+1
+ unicode=foundcodes
+ end
end
end
end
end
end
end
- end
- if not unicode or unicode=="" then
- local split=lpegmatch(namesplitter,name)
- local nsplit=split and #split or 0
- local t,n={},0
- unicode=true
- for l=1,nsplit do
- local base=split[l]
- local u=unicodes[base] or unicodevector[base]
- if not u then
- break
- elseif type(u)=="table" then
- if u[1]>=private then
- unicode=false
- break
+ if not unicode or unicode=="" then
+ local split=lpegmatch(namesplitter,name)
+ local nsplit=split and #split or 0
+ if nsplit==0 then
+ elseif nsplit==1 then
+ local base=split[1]
+ local u=unicodes[base] or unicodevector[base] or contextvector[name]
+ if not u then
+ elseif type(u)=="table" then
+ if u[1]<private then
+ unicode=u
+ glyph.unicode=unicode
+ end
+ elseif u<private then
+ unicode=u
+ glyph.unicode=unicode
end
- n=n+1
- t[n]=u[1]
else
- if u>=private then
- unicode=false
- break
- end
- n=n+1
- t[n]=u
- end
- end
- if n==0 then
- elseif n==1 then
- glyph.unicode=t[1]
- else
- glyph.unicode=t
- end
- nl=nl+1
- end
- if not unicode or unicode=="" then
- local foundcodes,multiple=lpegmatch(uparser,name)
- if foundcodes then
- glyph.unicode=foundcodes
- if multiple then
- nl=nl+1
- unicode=true
- else
- ns=ns+1
- unicode=foundcodes
- end
- end
- end
- local r=overloads[unicode]
- if r then
- unicode=r.unicode
- glyph.unicode=unicode
- end
- if not unicode then
- missing[name]=true
- end
- end
- end
- if next(missing) then
- local guess={}
- local function check(gname,code,unicode)
- local description=descriptions[code]
- local variant=description.name
- if variant==gname then
- return
- end
- local unic=unicodes[variant]
- if unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then
- else
- return
- end
- if descriptions[code].unicode then
- return
- end
- local g=guess[variant]
- if g then
- g[gname]=unicode
- else
- guess[variant]={ [gname]=unicode }
- end
- end
- for unicode,description in next,descriptions do
- local slookups=description.slookups
- if slookups then
- local gname=description.name
- for tag,data in next,slookups do
- local lookuptype=lookuptypes[tag]
- if lookuptype=="alternate" then
- for i=1,#data do
- check(gname,data[i],unicode)
- end
- elseif lookuptype=="substitution" then
- check(gname,data,unicode)
- end
- end
- end
- local mlookups=description.mlookups
- if mlookups then
- local gname=description.name
- for tag,list in next,mlookups do
- local lookuptype=lookuptypes[tag]
- if lookuptype=="alternate" then
- for i=1,#list do
- local data=list[i]
- for i=1,#data do
- check(gname,data[i],unicode)
+ local t,n={},0
+ for l=1,nsplit do
+ local base=split[l]
+ local u=unicodes[base] or unicodevector[base] or contextvector[name]
+ if not u then
+ break
+ elseif type(u)=="table" then
+ if u[1]>=private then
+ break
+ end
+ n=n+1
+ t[n]=u[1]
+ else
+ if u>=private then
+ break
+ end
+ n=n+1
+ t[n]=u
end
end
- elseif lookuptype=="substitution" then
- for i=1,#list do
- check(gname,list[i],unicode)
+ if n>0 then
+ if n==1 then
+ unicode=t[1]
+ else
+ unicode=t
+ end
+ glyph.unicode=unicode
end
end
- end
- end
- end
- local done=true
- while done do
- done=false
- for k,v in next,guess do
- if type(v)~="number" then
- for kk,vv in next,v do
- if vv==-1 or vv>=private or (vv>=0xE000 and vv<=0xF8FF) or vv==0xFFFE or vv==0xFFFF then
- local uu=guess[kk]
- if type(uu)=="number" then
- guess[k]=uu
- done=true
- end
+ nl=nl+1
+ end
+ if not unicode or unicode=="" then
+ local foundcodes,multiple=lpegmatch(uparser,name)
+ if foundcodes then
+ glyph.unicode=foundcodes
+ if multiple then
+ nl=nl+1
+ unicode=true
else
- guess[k]=vv
- done=true
+ ns=ns+1
+ unicode=foundcodes
end
end
end
- end
- end
- local orphans=0
- local guessed=0
- for k,v in next,guess do
- if type(v)=="number" then
- descriptions[unicodes[k]].unicode=descriptions[v].unicode or v
- guessed=guessed+1
- else
- local t=nil
- local l=lower(k)
- local u=unicodes[l]
- if not u then
- orphans=orphans+1
- elseif u==-1 or u>=private or (u>=0xE000 and u<=0xF8FF) or u==0xFFFE or u==0xFFFF then
- local unicode=descriptions[u].unicode
- if unicode then
- descriptions[unicodes[k]].unicode=unicode
- guessed=guessed+1
- else
- orphans=orphans+1
- end
- else
- orphans=orphans+1
+ local r=overloads[unicode]
+ if r then
+ unicode=r.unicode
+ glyph.unicode=unicode
+ end
+ if not unicode then
+ missing[unic]=true
+ nofmissing=nofmissing+1
end
end
+ else
end
- if trace_loading and orphans>0 or guessed>0 then
- report_fonts("%s glyphs with no related unicode, %s guessed, %s orphans",guessed+orphans,guessed,orphans)
- end
+ end
+ if type(checklookups)=="function" then
+ checklookups(data,missing,nofmissing)
end
if trace_mapping then
for unic,glyph in table.sortedhash(descriptions) do
@@ -5881,6 +5836,9 @@ local readers=fonts.readers
local constructors=fonts.constructors
local encodings=fonts.encodings
local tfm=constructors.newhandler("tfm")
+tfm.version=1.000
+tfm.maxnestingdepth=5
+tfm.maxnestingsize=65536*1024
local tfmfeatures=constructors.newfeatures("tfm")
local registertfmfeature=tfmfeatures.register
constructors.resolvevirtualtoo=false
@@ -5893,9 +5851,11 @@ function tfm.setfeatures(tfmdata,features)
return {}
end
end
+local depth={}
local function read_from_tfm(specification)
local filename=specification.filename
local size=specification.size
+ depth[filename]=(depth[filename] or 0)+1
if trace_defining then
report_defining("loading tfm file %a at size %s",filename,size)
end
@@ -5939,6 +5899,25 @@ local function read_from_tfm(specification)
end
properties.virtualized=true
tfmdata.fonts=vfdata.fonts
+ tfmdata.type="virtual"
+ local fontlist=vfdata.fonts
+ local name=file.nameonly(filename)
+ for i=1,#fontlist do
+ local n=fontlist[i].name
+ local s=fontlist[i].size
+ local d=depth[filename]
+ s=constructors.scaled(s,vfdata.designsize)
+ if d>tfm.maxnestingdepth then
+ report_defining("too deeply nested virtual font %a with size %a, max nesting depth %s",n,s,tfm.maxnestingdepth)
+ fontlist[i]={ id=0 }
+ elseif (d>1) and (s>tfm.maxnestingsize) then
+ report_defining("virtual font %a exceeds size %s",n,s)
+ fontlist[i]={ id=0 }
+ else
+ local t,id=fonts.constructors.readanddefine(n,s)
+ fontlist[i]={ id=id }
+ end
+ end
end
end
end
@@ -5954,7 +5933,10 @@ local function read_from_tfm(specification)
properties.haslogatures=true
resources.unicodes={}
resources.lookuptags={}
+ depth[filename]=depth[filename]-1
return tfmdata
+ else
+ depth[filename]=depth[filename]-1
end
end
local function check_tfm(specification,fullname)
@@ -6067,7 +6049,7 @@ local keys={}
function keys.FontName (data,line) data.metadata.fontname=strip (line)
data.metadata.fullname=strip (line) end
function keys.ItalicAngle (data,line) data.metadata.italicangle=tonumber (line) end
-function keys.IsFixedPitch(data,line) data.metadata.isfixedpitch=toboolean(line,true) end
+function keys.IsFixedPitch(data,line) data.metadata.monospaced=toboolean(line,true) end
function keys.CharWidth (data,line) data.metadata.charwidth=tonumber (line) end
function keys.XHeight (data,line) data.metadata.xheight=tonumber (line) end
function keys.Descender (data,line) data.metadata.descender=tonumber (line) end
@@ -6489,7 +6471,7 @@ local function copytotfm(data)
local emdash=0x2014
local spacer="space"
local spaceunits=500
- local monospaced=metadata.isfixedpitch
+ local monospaced=metadata.monospaced
local charwidth=metadata.charwidth
local italicangle=metadata.italicangle
local charxheight=metadata.xheight and metadata.xheight>0 and metadata.xheight
@@ -7144,12 +7126,10 @@ if not modules then modules={} end modules ['font-otf']={
license="see context related readme files"
}
local utfbyte=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 gmatch,gsub,find,match,lower,strip=string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip
local type,next,tonumber,tostring=type,next,tonumber,tostring
local abs=math.abs
-local insert=table.insert
-local lpegmatch=lpeg.match
-local reversed,concat,remove,sortedkeys=table.reversed,table.concat,table.remove,table.sortedkeys
+local reversed,concat,insert,remove,sortedkeys=table.reversed,table.concat,table.insert,table.remove,table.sortedkeys
local ioflush=io.flush
local fastcopy,tohash,derivetable=table.fastcopy,table.tohash,table.derive
local formatters=string.formatters
@@ -7176,7 +7156,7 @@ local report_otf=logs.reporter("fonts","otf loading")
local fonts=fonts
local otf=fonts.handlers.otf
otf.glists={ "gsub","gpos" }
-otf.version=2.812
+otf.version=2.819
otf.cache=containers.define("fonts","otf",otf.version,true)
local hashes=fonts.hashes
local definers=fonts.definers
@@ -7353,10 +7333,10 @@ local ordered_enhancers={
"reorganize subtables",
"check glyphs",
"check metadata",
- "check extra features",
"prepare tounicode",
"check encoding",
"add duplicates",
+ "expand lookups",
"cleanup tables",
"compact lookups",
"purge names",
@@ -7493,6 +7473,7 @@ function otf.load(filename,sub,featurefile)
end
end
if reload then
+ starttiming("fontloader")
report_otf("loading %a, hash %a",filename,hash)
local fontdata,messages
if sub then
@@ -7526,6 +7507,7 @@ function otf.load(filename,sub,featurefile)
data={
size=size,
time=time,
+ subfont=sub,
format=otf_format(filename),
featuredata=featurefiles,
resources={
@@ -7553,7 +7535,6 @@ function otf.load(filename,sub,featurefile)
tounicodetable=Ct(splitter),
},
}
- starttiming(data)
report_otf("file size: %s",size)
enhancers.apply(data,filename,fontdata)
local packtime={}
@@ -7570,10 +7551,10 @@ function otf.load(filename,sub,featurefile)
if cleanup>1 then
collectgarbage("collect")
end
- stoptiming(data)
+ stoptiming("fontloader")
if elapsedtime then
- report_otf("preprocessing and caching time %s, packtime %s",
- elapsedtime(data),packdata and elapsedtime(packtime) or 0)
+ report_otf("loading, optimizing, packing and caching time %s, pack time %s",
+ elapsedtime("fontloader"),packdata and elapsedtime(packtime) or 0)
end
close_font(fontdata)
if cleanup>3 then
@@ -7584,6 +7565,7 @@ function otf.load(filename,sub,featurefile)
collectgarbage("collect")
end
else
+ stoptiming("fontloader")
data=nil
report_otf("loading failed due to read error")
end
@@ -7625,6 +7607,7 @@ function otf.load(filename,sub,featurefile)
applyruntimefixes(filename,data)
end
enhance("add dimensions",data,filename,nil,false)
+enhance("check extra features",data,filename)
if trace_sequences then
showfeatureorder(data,filename)
end
@@ -7785,7 +7768,7 @@ actions["prepare glyphs"]=function(data,filename,raw)
end
if not unicode or unicode==-1 then
if not name then
- name=format("u%06X.ctx",private)
+ name=formatters["u%06X.ctx"](private)
end
unicode=private
unicodes[name]=private
@@ -7796,7 +7779,7 @@ actions["prepare glyphs"]=function(data,filename,raw)
nofnames=nofnames+1
else
if not name then
- name=format("u%06X.ctx",unicode)
+ name=formatters["u%06X.ctx"](unicode)
end
unicodes[name]=unicode
nofunicodes=nofunicodes+1
@@ -7810,25 +7793,25 @@ actions["prepare glyphs"]=function(data,filename,raw)
glyph=glyph,
}
descriptions[unicode]=description
-local altuni=glyph.altuni
-if altuni then
- for i=1,#altuni do
- local a=altuni[i]
- local u=a.unicode
- if u~=unicode then
- local v=a.variant
- if v then
- local vv=variants[v]
- if vv then
- vv[u]=unicode
- else
- vv={ [u]=unicode }
- variants[v]=vv
- end
- end
- end
- end
-end
+ local altuni=glyph.altuni
+ if altuni then
+ for i=1,#altuni do
+ local a=altuni[i]
+ local u=a.unicode
+ if u~=unicode then
+ local v=a.variant
+ if v then
+ local vv=variants[v]
+ if vv then
+ vv[u]=unicode
+ else
+ vv={ [u]=unicode }
+ variants[v]=vv
+ end
+ end
+ end
+ end
+ end
end
end
else
@@ -8014,7 +7997,7 @@ actions["add duplicates"]=function(data,filename,raw)
end
if u>0 then
local duplicate=table.copy(description)
- duplicate.comment=format("copy of U+%05X",unicode)
+ duplicate.comment=formatters["copy of %U"](unicode)
descriptions[u]=duplicate
if trace_loading then
report_otf("duplicating %U to %U with index %H (%s kerns)",unicode,u,description.index,n)
@@ -8035,7 +8018,7 @@ actions["analyze glyphs"]=function(data,filename,raw)
local marks={}
for unicode,description in next,descriptions do
local glyph=description.glyph
- local italic=glyph.italic_correction
+ local italic=glyph.italic_correction
if not italic then
elseif italic==0 then
else
@@ -8096,7 +8079,8 @@ end
actions["reorganize features"]=function(data,filename,raw)
local features={}
data.resources.features=features
- for k,what in next,otf.glists do
+ for k=1,#otf.glists do
+ local what=otf.glists[k]
local dw=raw[what]
if dw then
local f={}
@@ -8178,8 +8162,9 @@ actions["reorganize subtables"]=function(data,filename,raw)
local lookups={}
local chainedfeatures={}
resources.sequences=sequences
- resources.lookups=lookups
- for _,what in next,otf.glists do
+ resources.lookups=lookups
+ for k=1,#otf.glists do
+ local what=otf.glists[k]
local dw=raw[what]
if dw then
for k=1,#dw do
@@ -8353,12 +8338,15 @@ local function r_uncover(splitter,cache,cover,replacements)
end
actions["reorganize lookups"]=function(data,filename,raw)
if data.lookups then
- local splitter=data.helpers.tounicodetable
+ local helpers=data.helpers
+ local duplicates=data.resources.duplicates
+ local splitter=helpers.tounicodetable
local t_u_cache={}
local s_u_cache=t_u_cache
local t_h_cache={}
local s_h_cache=t_h_cache
local r_u_cache={}
+ helpers.matchcache=t_h_cache
for _,lookup in next,data.lookups do
local rules=lookup.rules
if rules then
@@ -8504,6 +8492,44 @@ actions["reorganize lookups"]=function(data,filename,raw)
end
end
end
+actions["expand lookups"]=function(data,filename,raw)
+ if data.lookups then
+ local cache=data.helpers.matchcache
+ if cache then
+ local duplicates=data.resources.duplicates
+ for key,hash in next,cache do
+ local done=nil
+ for key in next,hash do
+ local unicode=duplicates[key]
+ if not unicode then
+ elseif type(unicode)=="table" then
+ for i=1,#unicode do
+ local u=unicode[i]
+ if hash[u] then
+ elseif done then
+ done[u]=key
+ else
+ done={ [u]=key }
+ end
+ end
+ else
+ if hash[unicode] then
+ elseif done then
+ done[unicode]=key
+ else
+ done={ [unicode]=key }
+ end
+ end
+ end
+ if done then
+ for u in next,done do
+ hash[u]=true
+ end
+ end
+ end
+ end
+ end
+end
local function check_variants(unicode,the_variants,splitter,unicodes)
local variants=the_variants.variants
if variants then
@@ -8544,11 +8570,11 @@ local function check_variants(unicode,the_variants,splitter,unicodes)
parts=nil
end
end
- local italic_correction=the_variants.italic_correction
- if italic_correction and italic_correction==0 then
- italic_correction=nil
+ local italic=the_variants.italic
+ if italic and italic==0 then
+ italic=nil
end
- return variants,parts,italic_correction
+ return variants,parts,italic
end
actions["analyze math"]=function(data,filename,raw)
if raw.math then
@@ -8558,13 +8584,14 @@ actions["analyze math"]=function(data,filename,raw)
for unicode,description in next,data.descriptions do
local glyph=description.glyph
local mathkerns=glyph.mathkern
- local horiz_variants=glyph.horiz_variants
- local vert_variants=glyph.vert_variants
- local top_accent=glyph.top_accent
- if mathkerns or horiz_variants or vert_variants or top_accent then
+ local hvariants=glyph.horiz_variants
+ local vvariants=glyph.vert_variants
+ local accent=glyph.top_accent
+ local italic=glyph.italic_correction
+ if mathkerns or hvariants or vvariants or accent or italic then
local math={}
- if top_accent then
- math.top_accent=top_accent
+ if accent then
+ math.accent=accent
end
if mathkerns then
for k,v in next,mathkerns do
@@ -8580,15 +8607,14 @@ actions["analyze math"]=function(data,filename,raw)
end
math.kerns=mathkerns
end
- if horiz_variants then
- math.horiz_variants,math.horiz_parts,math.horiz_italic_correction=check_variants(unicode,horiz_variants,splitter,unicodes)
+ if hvariants then
+ math.hvariants,math.hparts,math.hitalic=check_variants(unicode,hvariants,splitter,unicodes)
end
- if vert_variants then
- math.vert_variants,math.vert_parts,math.vert_italic_correction=check_variants(unicode,vert_variants,splitter,unicodes)
+ if vvariants then
+ math.vvariants,math.vparts,math.vitalic=check_variants(unicode,vvariants,splitter,unicodes)
end
- local italic_correction=description.italic
- if italic_correction and italic_correction~=0 then
- math.italic_correction=italic_correction
+ if italic and italic~=0 then
+ math.italic=italic
end
description.math=math
end
@@ -8656,7 +8682,7 @@ actions["merge kern classes"]=function(data,filename,raw)
local subtable=subtables[s]
local kernclass=subtable.kernclass
local lookup=subtable.lookup or subtable.name
- if kernclass then
+ if kernclass then
if #kernclass>0 then
kernclass=kernclass[1]
lookup=type(kernclass.lookup)=="string" and kernclass.lookup or lookup
@@ -8681,14 +8707,16 @@ actions["merge kern classes"]=function(data,filename,raw)
if splt then
local extrakerns={}
local baseoffset=(fk-1)*maxseconds
- for sk=2,maxseconds do
+ for sk=2,maxseconds do
local sv=seconds[sk]
- local splt=split[sv]
- if splt then
- local offset=offsets[baseoffset+sk]
- if offset then
- for i=1,#splt do
- extrakerns[splt[i]]=offset
+ if sv then
+ local splt=split[sv]
+ if splt then
+ local offset=offsets[baseoffset+sk]
+ if offset then
+ for i=1,#splt do
+ extrakerns[splt[i]]=offset
+ end
end
end
end
@@ -8745,7 +8773,7 @@ actions["merge kern classes"]=function(data,filename,raw)
report_otf("%s kern overloads ignored",ignored)
end
if blocked>0 then
- report_otf("%s succesive kerns blocked",blocked)
+ report_otf("%s successive kerns blocked",blocked)
end
end
end
@@ -8774,16 +8802,18 @@ actions["check metadata"]=function(data,filename,raw)
ttftables[i].data="deleted"
end
end
+ local names=raw.names
if metadata.validation_state and table.contains(metadata.validation_state,"bad_ps_fontname") then
local function valid(what)
- local names=raw.names
- for i=1,#names do
- local list=names[i]
- local names=list.names
- if names then
- local name=names[what]
- if name and valid_ps_name(name) then
- return name
+ if names then
+ for i=1,#names do
+ local list=names[i]
+ local names=list.names
+ if names then
+ local name=names[what]
+ if name and valid_ps_name(name) then
+ return name
+ end
end
end
end
@@ -8806,6 +8836,28 @@ actions["check metadata"]=function(data,filename,raw)
check("fontname")
check("fullname")
end
+ if names then
+ local psname=metadata.psname
+ if not psname or psname=="" then
+ for i=1,#names do
+ local name=names[i]
+ if lower(name.lang)=="english (us)" then
+ local specification=name.names
+ if specification then
+ local postscriptname=specification.postscriptname
+ if postscriptname then
+ psname=postscriptname
+ end
+ end
+ end
+ break
+ end
+ end
+ if psname~=metadata.fontname then
+ report_otf("fontname %a, fullname %a, psname %a",metadata.fontname,metadata.fullname,psname)
+ end
+ metadata.psname=psname
+ end
end
actions["cleanup tables"]=function(data,filename,raw)
local duplicates=data.resources.duplicates
@@ -8901,7 +8953,7 @@ actions["reorganize glyph lookups"]=function(data,filename,raw)
end
end
local zero={ 0,0 }
-actions["reorganize glyph anchors"]=function(data,filename,raw)
+actions["reorganize glyph anchors"]=function(data,filename,raw)
local descriptions=data.descriptions
for unicode,description in next,descriptions do
local anchors=description.glyph.anchors
@@ -9103,9 +9155,13 @@ local function copytotfm(data,cache_id)
local spaceunits=500
local spacer="space"
local designsize=metadata.designsize or metadata.design_size or 100
+ local minsize=metadata.minsize or metadata.design_range_bottom or designsize
+ local maxsize=metadata.maxsize or metadata.design_range_top or designsize
local mathspecs=metadata.math
if designsize==0 then
designsize=100
+ minsize=100
+ maxsize=100
end
if mathspecs then
for name,value in next,mathspecs do
@@ -9120,8 +9176,10 @@ local function copytotfm(data,cache_id)
local d=descriptions[unicode]
local m=d.math
if m then
- local variants=m.horiz_variants
- local parts=m.horiz_parts
+ local italic=m.italic
+ local vitalic=m.vitalic
+ local variants=m.hvariants
+ local parts=m.hparts
if variants then
local c=character
for i=1,#variants do
@@ -9132,9 +9190,10 @@ local function copytotfm(data,cache_id)
c.horiz_variants=parts
elseif parts then
character.horiz_variants=parts
+ italic=m.hitalic
end
- local variants=m.vert_variants
- local parts=m.vert_parts
+ local variants=m.vvariants
+ local parts=m.vparts
if variants then
local c=character
for i=1,#variants do
@@ -9146,13 +9205,15 @@ local function copytotfm(data,cache_id)
elseif parts then
character.vert_variants=parts
end
- local italic_correction=m.vert_italic_correction
- if italic_correction then
- character.vert_italic_correction=italic_correction
+ if italic and italic~=0 then
+ character.italic=italic
end
- local top_accent=m.top_accent
- if top_accent then
- character.top_accent=top_accent
+ if vitalic and vitalic~=0 then
+ character.vert_italic=vitalic
+ end
+ local accent=m.accent
+ if accent then
+ character.accent=accent
end
local kerns=m.kerns
if kerns then
@@ -9164,14 +9225,14 @@ local function copytotfm(data,cache_id)
local filename=constructors.checkedfilename(resources)
local fontname=metadata.fontname
local fullname=metadata.fullname or fontname
- local psname=fontname or fullname
- local units=metadata.units_per_em or 1000
+ local psname=metadata.psname or fontname or fullname
+ local units=metadata.units or metadata.units_per_em or 1000
if units==0 then
units=1000
- metadata.units_per_em=1000
+ metadata.units=1000
report_otf("changing %a units to %a",0,units)
end
- local monospaced=metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion=="Monospaced")
+ local monospaced=metadata.monospaced or metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion=="Monospaced")
local charwidth=pfminfo.avgwidth
local charxheight=pfminfo.os2_xheight and pfminfo.os2_xheight>0 and pfminfo.os2_xheight
local italicangle=metadata.italicangle
@@ -9236,8 +9297,10 @@ local function copytotfm(data,cache_id)
end
end
parameters.designsize=(designsize/10)*65536
- parameters.ascender=abs(metadata.ascent or 0)
- parameters.descender=abs(metadata.descent or 0)
+ parameters.minsize=(minsize/10)*65536
+ parameters.maxsize=(maxsize/10)*65536
+ parameters.ascender=abs(metadata.ascender or metadata.ascent or 0)
+ parameters.descender=abs(metadata.descender or metadata.descent or 0)
parameters.units=units
properties.space=spacer
properties.encodingbytes=2
@@ -9416,6 +9479,99 @@ function otf.scriptandlanguage(tfmdata,attr)
local properties=tfmdata.properties
return properties.script or "dflt",properties.language or "dflt"
end
+local function justset(coverage,unicode,replacement)
+ coverage[unicode]=replacement
+end
+otf.coverup={
+ stepkey="subtables",
+ actions={
+ substitution=justset,
+ alternate=justset,
+ multiple=justset,
+ ligature=justset,
+ kern=justset,
+ },
+ register=function(coverage,lookuptype,format,feature,n,descriptions,resources)
+ local name=formatters["ctx_%s_%s"](feature,n)
+ if lookuptype=="kern" then
+ resources.lookuptypes[name]="position"
+ else
+ resources.lookuptypes[name]=lookuptype
+ end
+ for u,c in next,coverage do
+ local description=descriptions[u]
+ local slookups=description.slookups
+ if slookups then
+ slookups[name]=c
+ else
+ description.slookups={ [name]=c }
+ end
+ end
+ return name
+ end
+}
+local function getgsub(tfmdata,k,kind)
+ local description=tfmdata.descriptions[k]
+ if description then
+ local slookups=description.slookups
+ if slookups then
+ local shared=tfmdata.shared
+ local rawdata=shared and shared.rawdata
+ if rawdata then
+ local lookuptypes=rawdata.resources.lookuptypes
+ if lookuptypes then
+ local properties=tfmdata.properties
+ local validlookups,lookuplist=otf.collectlookups(rawdata,kind,properties.script,properties.language)
+ if validlookups then
+ for l=1,#lookuplist do
+ local lookup=lookuplist[l]
+ local found=slookups[lookup]
+ if found then
+ return found,lookuptypes[lookup]
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+otf.getgsub=getgsub
+function otf.getsubstitution(tfmdata,k,kind,value)
+ local found,kind=getgsub(tfmdata,k,kind)
+ if not found then
+ elseif kind=="substitution" then
+ return found
+ elseif kind=="alternate" then
+ local choice=tonumber(value) or 1
+ return found[choice] or found[1] or k
+ end
+ return k
+end
+otf.getalternate=otf.getsubstitution
+function otf.getmultiple(tfmdata,k,kind)
+ local found,kind=getgsub(tfmdata,k,kind)
+ if found and kind=="multiple" then
+ return found
+ end
+ return { k }
+end
+function otf.getkern(tfmdata,left,right,kind)
+ local kerns=getgsub(tfmdata,left,kind or "kern",true)
+ if kerns then
+ local found=kerns[right]
+ local kind=type(found)
+ if kind=="table" then
+ found=found[1][3]
+ elseif kind~="number" then
+ found=false
+ end
+ if found then
+ return found*tfmdata.parameters.factor
+ end
+ end
+ return 0
+end
end -- closure
@@ -9946,8 +10102,8 @@ local function featuresinitializer(tfmdata,value)
local collectlookups=otf.collectlookups
local rawdata=tfmdata.shared.rawdata
local properties=tfmdata.properties
- local script=properties.script
- local language=properties.language
+ local script=properties.script
+ local language=properties.language
local basesubstitutions=rawdata.resources.features.gsub
local basepositionings=rawdata.resources.features.gpos
if basesubstitutions or basepositionings then
@@ -10125,7 +10281,8 @@ end
function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext)
local dx=factor*(exit[1]-entry[1])
local dy=-factor*(exit[2]-entry[2])
- local ws,wn=tfmstart.width,tfmnext.width
+ local ws=tfmstart.width
+ local wn=tfmnext.width
nofregisteredcursives=nofregisteredcursives+1
if rlmode<0 then
dx=-(dx+wn)
@@ -10172,7 +10329,10 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne
return dx,dy,nofregisteredcursives
end
function injections.setpair(current,factor,rlmode,r2lflag,spec,injection)
- local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4]
+ local x=factor*spec[1]
+ local y=factor*spec[2]
+ local w=factor*spec[3]
+ local h=factor*spec[4]
if x~=0 or w~=0 or y~=0 or h~=0 then
local yoffset=y-h
local leftkern=x
@@ -10182,9 +10342,12 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection)
if rlmode and rlmode<0 then
leftkern,rightkern=rightkern,leftkern
end
+ if not injection then
+ injection="injections"
+ end
local p=rawget(properties,current)
if p then
- local i=rawget(p,"injections")
+ local i=rawget(p,injection)
if i then
if leftkern~=0 then
i.leftkern=(i.leftkern or 0)+leftkern
@@ -10196,19 +10359,19 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection)
i.yoffset=(i.yoffset or 0)+yoffset
end
elseif leftkern~=0 or rightkern~=0 then
- p.injections={
+ p[injection]={
leftkern=leftkern,
rightkern=rightkern,
yoffset=yoffset,
}
else
- p.injections={
+ p[injection]={
yoffset=yoffset,
}
end
elseif leftkern~=0 or rightkern~=0 then
properties[current]={
- injections={
+ [injection]={
leftkern=leftkern,
rightkern=rightkern,
yoffset=yoffset,
@@ -10216,7 +10379,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection)
}
else
properties[current]={
- injections={
+ [injection]={
yoffset=yoffset,
},
}
@@ -10255,7 +10418,7 @@ function injections.setkern(current,factor,rlmode,x,injection)
return 0,0
end
end
-function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase)
+function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk)
local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2])
nofregisteredmarks=nofregisteredmarks+1
if rlmode>=0 then
@@ -10265,11 +10428,15 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase)
if p then
local i=rawget(p,"injections")
if i then
- i.markx=dx
- i.marky=dy
- i.markdir=rlmode or 0
- i.markbase=nofregisteredmarks
- i.markbasenode=base
+ if i.markmark then
+ else
+ i.markx=dx
+ i.marky=dy
+ i.markdir=rlmode or 0
+ i.markbase=nofregisteredmarks
+ i.markbasenode=base
+ i.markmark=mkmk
+ end
else
p.injections={
markx=dx,
@@ -10277,6 +10444,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase)
markdir=rlmode or 0,
markbase=nofregisteredmarks,
markbasenode=base,
+ markmark=mkmk,
}
end
else
@@ -10287,6 +10455,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase)
markdir=rlmode or 0,
markbase=nofregisteredmarks,
markbasenode=base,
+ markmark=mkmk,
},
}
end
@@ -10391,27 +10560,33 @@ local function show_result(head)
current=getnext(current)
end
end
-local function collect_glyphs_1(head)
- local glyphs,nofglyphs={},0
- local marks,nofmarks={},0
+local function collect_glyphs(head,offsets)
+ local glyphs,glyphi,nofglyphs={},{},0
+ local marks,marki,nofmarks={},{},0
local nf,tm=nil,nil
- for n in traverse_id(glyph_code,head) do
- if getsubtype(n)<256 then
- local f=getfont(n)
- if f~=nf then
- nf=f
- tm=fontdata[nf].resources.marks
- end
- if tm and tm[getchar(n)] then
- nofmarks=nofmarks+1
- marks[nofmarks]=n
- else
- nofglyphs=nofglyphs+1
- glyphs[nofglyphs]=n
- end
+ local n=head
+ local function identify(n,what)
+ local f=getfont(n)
+ if f~=nf then
+ nf=f
+ tm=fontdata[nf].resources
+ if tm then
+ tm=tm.marks
+ end
+ end
+ if tm and tm[getchar(n)] then
+ nofmarks=nofmarks+1
+ marks[nofmarks]=n
+ marki[nofmarks]="injections"
+ else
+ nofglyphs=nofglyphs+1
+ glyphs[nofglyphs]=n
+ glyphi[nofglyphs]=what
+ end
+ if offsets then
local p=rawget(properties,n)
if p then
- local i=rawget(p,"injections")
+ local i=rawget(p,what)
if i then
local yoffset=i.yoffset
if yoffset and yoffset~=0 then
@@ -10421,36 +10596,47 @@ local function collect_glyphs_1(head)
end
end
end
- return glyphs,nofglyphs,marks,nofmarks
-end
-local function collect_glyphs_2(head)
- local glyphs,nofglyphs={},0
- local marks,nofmarks={},0
- local nf,tm=nil,nil
- for n in traverse_id(glyph_code,head) do
- if getsubtype(n)<256 then
- local f=getfont(n)
- if f~=nf then
- nf=f
- tm=fontdata[nf].resources.marks
- end
- if tm and tm[getchar(n)] then
- nofmarks=nofmarks+1
- marks[nofmarks]=n
- else
- nofglyphs=nofglyphs+1
- glyphs[nofglyphs]=n
- end
+ while n do
+ local id=getid(n)
+ if id==glyph_code then
+ identify(n,"injections")
+ elseif id==disc_code then
+ local d=getfield(n,"pre")
+ if d then
+ for n in traverse_id(glyph_code,d) do
+ if getsubtype(n)<256 then
+ identify(n,"preinjections")
+ end
+ end
+ end
+ local d=getfield(n,"post")
+ if d then
+ for n in traverse_id(glyph_code,d) do
+ if getsubtype(n)<256 then
+ identify(n,"postinjections")
+ end
+ end
+ end
+ local d=getfield(n,"replace")
+ if d then
+ for n in traverse_id(glyph_code,d) do
+ if getsubtype(n)<256 then
+ identify(n,"replaceinjections")
+ end
+ end
+ end
end
+ n=getnext(n)
end
- return glyphs,nofglyphs,marks,nofmarks
+ return glyphs,glyphi,nofglyphs,marks,marki,nofmarks
end
-local function inject_marks(marks,nofmarks)
+local function inject_marks(marks,marki,nofmarks)
for i=1,nofmarks do
local n=marks[i]
local pn=rawget(properties,n)
if pn then
- pn=rawget(pn,"injections")
+ local ni=marki[i]
+ local pn=rawget(pn,ni)
if pn then
local p=pn.markbasenode
if p then
@@ -10459,7 +10645,7 @@ local function inject_marks(marks,nofmarks)
local rightkern=nil
local pp=rawget(properties,p)
if pp then
- pp=rawget(pp,"injections")
+ pp=rawget(pp,ni)
if pp then
rightkern=pp.rightkern
end
@@ -10468,11 +10654,17 @@ local function inject_marks(marks,nofmarks)
if pn.markdir<0 then
ox=px-pn.markx-rightkern
else
- local leftkern=pp.leftkern
- if leftkern then
- ox=px-pn.markx
+
+
+ if false then
+ local leftkern=pp.leftkern
+ if leftkern then
+ ox=px-pn.markx-leftkern
+ else
+ ox=px-pn.markx
+ end
else
- ox=px-pn.markx-leftkern
+ ox=px-pn.markx
end
end
else
@@ -10485,12 +10677,7 @@ local function inject_marks(marks,nofmarks)
end
setfield(n,"xoffset",ox)
local py=getfield(p,"yoffset")
- local oy=0
- if marks[p] then
- oy=py+pn.marky
- else
- oy=getfield(n,"yoffset")+py+pn.marky
- end
+ local oy=getfield(n,"yoffset")+py+pn.marky
setfield(n,"yoffset",oy)
else
end
@@ -10498,14 +10685,14 @@ local function inject_marks(marks,nofmarks)
end
end
end
-local function inject_cursives(glyphs,nofglyphs)
+local function inject_cursives(glyphs,glyphi,nofglyphs)
local cursiveanchor,lastanchor=nil,nil
local minc,maxc,last=0,0,nil
for i=1,nofglyphs do
local n=glyphs[i]
local pn=rawget(properties,n)
if pn then
- pn=rawget(pn,"injections")
+ pn=rawget(pn,glyphi[i])
end
if pn then
local cursivex=pn.cursivex
@@ -10571,22 +10758,59 @@ local function inject_cursives(glyphs,nofglyphs)
end
end
end
-local function inject_kerns(head,list,length)
+local function inject_kerns(head,glist,ilist,length)
for i=1,length do
- local n=list[i]
+ local n=glist[i]
local pn=rawget(properties,n)
if pn then
- local i=rawget(pn,"injections")
- if i then
- local leftkern=i.leftkern
- if leftkern and leftkern~=0 then
- insert_node_before(head,n,newkern(leftkern))
- end
- local rightkern=i.rightkern
- if rightkern and rightkern~=0 then
- insert_node_after(head,n,newkern(rightkern))
- end
- end
+ local dp=nil
+ local dr=nil
+ local ni=ilist[i]
+ local p=nil
+ if ni=="injections" then
+ p=getprev(n)
+ if p then
+ local id=getid(p)
+ if id==disc_code then
+ dp=getfield(p,"post")
+ dr=getfield(p,"replace")
+ end
+ end
+ end
+ if dp then
+ local i=rawget(pn,"postinjections")
+ if i then
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ local t=find_tail(dp)
+ insert_node_after(dp,t,newkern(leftkern))
+ setfield(p,"post",dp)
+ end
+ end
+ end
+ if dr then
+ local i=rawget(pn,"replaceinjections")
+ if i then
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ local t=find_tail(dr)
+ insert_node_after(dr,t,newkern(leftkern))
+ setfield(p,"replace",dr)
+ end
+ end
+ else
+ local i=rawget(pn,ni)
+ if i then
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ insert_node_before(head,n,newkern(leftkern))
+ end
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ insert_node_after(head,n,newkern(rightkern))
+ end
+ end
+ end
end
end
end
@@ -10595,23 +10819,18 @@ local function inject_everything(head,where)
if trace_injections then
trace(head,"everything")
end
- local glyphs,nofglyphs,marks,nofmarks
- if nofregisteredpairs>0 then
- glyphs,nofglyphs,marks,nofmarks=collect_glyphs_1(head)
- else
- glyphs,nofglyphs,marks,nofmarks=collect_glyphs_2(head)
- end
+ local glyphs,glyphi,nofglyphs,marks,marki,nofmarks=collect_glyphs(head,nofregisteredpairs>0)
if nofglyphs>0 then
if nofregisteredcursives>0 then
- inject_cursives(glyphs,nofglyphs)
+ inject_cursives(glyphs,glyphi,nofglyphs)
end
if nofregisteredmarks>0 then
- inject_marks(marks,nofmarks)
+ inject_marks(marks,marki,nofmarks)
end
- inject_kerns(head,glyphs,nofglyphs)
+ inject_kerns(head,glyphs,glyphi,nofglyphs)
end
if nofmarks>0 then
- inject_kerns(head,marks,nofmarks)
+ inject_kerns(head,marks,marki,nofmarks)
end
if keepregisteredcounts then
keepregisteredcounts=false
@@ -10629,7 +10848,7 @@ local function inject_kerns_only(head,where)
trace(head,"kerns")
end
local n=head
- local p=nil
+ local p=nil
while n do
local id=getid(n)
if id==glyph_code then
@@ -10645,6 +10864,7 @@ local function inject_kerns_only(head,where)
if leftkern and leftkern~=0 then
local t=find_tail(d)
insert_node_after(d,t,newkern(leftkern))
+ setfield(p,"post",d)
end
end
end
@@ -10656,6 +10876,7 @@ local function inject_kerns_only(head,where)
if leftkern and leftkern~=0 then
local t=find_tail(d)
insert_node_after(d,t,newkern(leftkern))
+ setfield(p,"replace",d)
end
end
else
@@ -10677,8 +10898,6 @@ local function inject_kerns_only(head,where)
end
end
end
- else
- break
end
p=nil
elseif id==disc_code then
@@ -10733,7 +10952,7 @@ local function inject_kerns_only(head,where)
local h=d
for n in traverse_id(glyph_code,d) do
if getsubtype(n)<256 then
- local pn=rawget(properties,n)
+ local pn=rawget(properties,n)
if pn then
local i=rawget(pn,"replaceinjections")
if i then
@@ -10770,7 +10989,7 @@ local function inject_pairs_only(head,where)
trace(head,"pairs")
end
local n=head
- local p=nil
+ local p=nil
while n do
local id=getid(n)
if id==glyph_code then
@@ -10786,6 +11005,7 @@ local function inject_pairs_only(head,where)
if leftkern and leftkern~=0 then
local t=find_tail(d)
insert_node_after(d,t,newkern(leftkern))
+ setfield(p,"post",d)
end
end
end
@@ -10797,6 +11017,7 @@ local function inject_pairs_only(head,where)
if leftkern and leftkern~=0 then
local t=find_tail(d)
insert_node_after(d,t,newkern(leftkern))
+ setfield(p,"replace",d)
end
end
else
@@ -10811,24 +11032,22 @@ local function inject_pairs_only(head,where)
else
local i=rawget(pn,"injections")
if i then
- local yoffset=i.yoffset
- if yoffset and yoffset~=0 then
- setfield(n,"yoffset",yoffset)
- end
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
- insert_node_before(head,n,newkern(leftkern))
+ head=insert_node_before(head,n,newkern(leftkern))
end
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
insert_node_after(head,n,newkern(rightkern))
n=getnext(n)
end
+ local yoffset=i.yoffset
+ if yoffset and yoffset~=0 then
+ setfield(n,"yoffset",yoffset)
+ end
end
end
end
- else
- break
end
p=nil
elseif id==disc_code then
@@ -10837,16 +11056,12 @@ local function inject_pairs_only(head,where)
local h=d
for n in traverse_id(glyph_code,d) do
if getsubtype(n)<256 then
- local p=rawget(properties,n)
- if p then
- local i=rawget(p,"preinjections")
+ local pn=rawget(properties,n)
+ if pn then
+ local i=rawget(pn,"preinjections")
if i then
- local yoffset=i.yoffset
- if yoffset and yoffset~=0 then
- setfield(n,"yoffset",yoffset)
- end
local leftkern=i.leftkern
- if leftkern~=0 then
+ if leftkern and leftkern~=0 then
h=insert_node_before(h,n,newkern(leftkern))
end
local rightkern=i.rightkern
@@ -10854,6 +11069,10 @@ local function inject_pairs_only(head,where)
insert_node_after(head,n,newkern(rightkern))
n=getnext(n)
end
+ local yoffset=i.yoffset
+ if yoffset and yoffset~=0 then
+ setfield(n,"yoffset",yoffset)
+ end
end
end
else
@@ -10869,14 +11088,10 @@ local function inject_pairs_only(head,where)
local h=d
for n in traverse_id(glyph_code,d) do
if getsubtype(n)<256 then
- local p=rawget(properties,n)
- if p then
- local i=rawget(p,"postinjections")
+ local pn=rawget(properties,n)
+ if pn then
+ local i=rawget(pn,"postinjections")
if i then
- local yoffset=i.yoffset
- if yoffset and yoffset~=0 then
- setfield(n,"yoffset",yoffset)
- end
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
h=insert_node_before(h,n,newkern(leftkern))
@@ -10886,6 +11101,10 @@ local function inject_pairs_only(head,where)
insert_node_after(head,n,newkern(rightkern))
n=getnext(n)
end
+ local yoffset=i.yoffset
+ if yoffset and yoffset~=0 then
+ setfield(n,"yoffset",yoffset)
+ end
end
end
else
@@ -10901,14 +11120,10 @@ local function inject_pairs_only(head,where)
local h=d
for n in traverse_id(glyph_code,d) do
if getsubtype(n)<256 then
- local p=rawget(properties,n)
- if p then
- local i=rawget(p,"replaceinjections")
+ local pn=rawget(properties,n)
+ if pn then
+ local i=rawget(pn,"replaceinjections")
if i then
- local yoffset=i.yoffset
- if yoffset and yoffset~=0 then
- setfield(n,"yoffset",yoffset)
- end
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
h=insert_node_before(h,n,newkern(leftkern))
@@ -10918,6 +11133,10 @@ local function inject_pairs_only(head,where)
insert_node_after(head,n,newkern(rightkern))
n=getnext(n)
end
+ local yoffset=i.yoffset
+ if yoffset and yoffset~=0 then
+ setfield(n,"yoffset",yoffset)
+ end
end
end
else
@@ -10942,7 +11161,7 @@ local function inject_pairs_only(head,where)
end
return tonode(head),true
end
-function injections.handler(head,where)
+function injections.handler(head,where)
if nofregisteredmarks>0 or nofregisteredcursives>0 then
return inject_everything(head,where)
elseif nofregisteredpairs>0 then
@@ -11342,14 +11561,12 @@ if not modules then modules={} end modules ['font-otn']={
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files",
}
-local concat,insert,remove=table.concat,table.insert,table.remove
-local gmatch,gsub,find,match,lower,strip=string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip
-local type,next,tonumber,tostring=type,next,tonumber,tostring
-local lpegmatch=lpeg.match
+local type,next,tonumber=type,next,tonumber
local random=math.random
local formatters=string.formatters
local logs,trackers,nodes,attributes=logs,trackers,nodes,attributes
local registertracker=trackers.register
+local registerdirective=directives.register
local fonts=fonts
local otf=fonts.handlers.otf
local trace_lookups=false registertracker("otf.lookups",function(v) trace_lookups=v end)
@@ -11368,6 +11585,13 @@ local trace_applied=false registertracker("otf.applied",function(v) trace_applie
local trace_steps=false registertracker("otf.steps",function(v) trace_steps=v end)
local trace_skips=false registertracker("otf.skips",function(v) trace_skips=v end)
local trace_directions=false registertracker("otf.directions",function(v) trace_directions=v end)
+local trace_kernruns=false registertracker("otf.kernruns",function(v) trace_kernruns=v end)
+local trace_discruns=false registertracker("otf.discruns",function(v) trace_discruns=v end)
+local trace_compruns=false registertracker("otf.compruns",function(v) trace_compruns=v end)
+local quit_on_no_replacement=true
+local zwnjruns=true
+registerdirective("otf.zwnjruns",function(v) zwnjruns=v end)
+registerdirective("otf.chain.quitonnoreplacement",function(value) quit_on_no_replacement=value end)
local report_direct=logs.reporter("fonts","otf direct")
local report_subchain=logs.reporter("fonts","otf subchain")
local report_chain=logs.reporter("fonts","otf chain")
@@ -11421,13 +11645,10 @@ local disccodes=nodes.disccodes
local glyph_code=nodecodes.glyph
local glue_code=nodecodes.glue
local disc_code=nodecodes.disc
-local whatsit_code=nodecodes.whatsit
local math_code=nodecodes.math
local dir_code=whatcodes.dir
local localpar_code=whatcodes.localpar
local discretionary_code=disccodes.discretionary
-local regular_code=disccodes.regular
-local automatic_code=disccodes.automatic
local ligature_code=glyphcodes.ligature
local privateattribute=attributes.private
local a_state=privateattribute('state')
@@ -11461,6 +11682,13 @@ local lookuptags=false
local handlers={}
local rlmode=0
local featurevalue=false
+local sweephead={}
+local sweepnode=nil
+local sweepprev=nil
+local sweepnext=nil
+local notmatchpre={}
+local notmatchpost={}
+local notmatchreplace={}
local checkstep=(nodes and nodes.tracers and nodes.tracers.steppers.check) or function() end
local registerstep=(nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end
local registermessage=(nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end
@@ -11530,6 +11758,65 @@ local function copy_glyph(g)
return n
end
end
+local function flattendisk(head,disc)
+ local replace=getfield(disc,"replace")
+ setfield(disc,"replace",nil)
+ free_node(disc)
+ if head==disc then
+ local next=getnext(disc)
+ if replace then
+ if next then
+ local tail=find_node_tail(replace)
+ setfield(tail,"next",next)
+ setfield(next,"prev",tail)
+ end
+ return replace,replace
+ elseif next then
+ return next,next
+ else
+ return
+ end
+ else
+ local next=getnext(disc)
+ local prev=getprev(disc)
+ if replace then
+ local tail=find_node_tail(replace)
+ if next then
+ setfield(tail,"next",next)
+ setfield(next,"prev",tail)
+ end
+ setfield(prev,"next",replace)
+ setfield(replace,"prev",prev)
+ return head,replace
+ else
+ if next then
+ setfield(next,"prev",prev)
+ end
+ setfield(prev,"next",next)
+ return head,next
+ end
+ end
+end
+local function appenddisc(disc,list)
+ local post=getfield(disc,"post")
+ local replace=getfield(disc,"replace")
+ local phead=list
+ local rhead=copy_node_list(list)
+ local ptail=find_node_tail(post)
+ local rtail=find_node_tail(replace)
+ if post then
+ setfield(ptail,"next",phead)
+ setfield(phead,"prev",ptail)
+ else
+ setfield(disc,"post",phead)
+ end
+ if replace then
+ setfield(rtail,"next",rhead)
+ setfield(rhead,"prev",rtail)
+ else
+ setfield(disc,"replace",rhead)
+ end
+end
local function markstoligature(kind,lookupname,head,start,stop,char)
if start==stop and getchar(start)==char then
return head,start
@@ -11557,8 +11844,8 @@ local function markstoligature(kind,lookupname,head,start,stop,char)
return head,base
end
end
-local function getcomponentindex(start)
- if getid(start)~=glyph_code then
+local function getcomponentindex(start)
+ if getid(start)~=glyph_code then
return 0
elseif getsubtype(start)==ligature_code then
local i=0
@@ -11574,14 +11861,22 @@ local function getcomponentindex(start)
return 0
end
end
+local a_noligature=attributes.private("noligature")
local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound)
+ if getattr(start,a_noligature)==1 then
+ return head,start
+ end
if start==stop and getchar(start)==char then
resetinjection(start)
setfield(start,"char",char)
return head,start
end
+ local components=getfield(start,"components")
+ if components then
+ end
local prev=getprev(start)
local next=getnext(stop)
+ local comp=start
setfield(start,"prev",nil)
setfield(stop,"next",nil)
local base=copy_glyph(start)
@@ -11591,15 +11886,15 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
resetinjection(base)
setfield(base,"char",char)
setfield(base,"subtype",ligature_code)
- setfield(base,"components",start)
+ setfield(base,"components",comp)
if prev then
setfield(prev,"next",base)
end
if next then
setfield(next,"prev",base)
end
- setfield(base,"next",next)
setfield(base,"prev",prev)
+ setfield(base,"next",next)
if not discfound then
local deletemarks=markflag~="mark"
local components=start
@@ -11617,7 +11912,9 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
if trace_marks then
logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getligaindex(start))
end
- head,current=insert_node_after(head,current,copy_node(start))
+ local n=copy_node(start)
+ copyinjection(n,start)
+ head,current=insert_node_after(head,current,n)
elseif trace_marks then
logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))
end
@@ -11636,16 +11933,75 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
end
start=getnext(start)
end
+ else
+ local discprev=getfield(discfound,"prev")
+ local discnext=getfield(discfound,"next")
+ if discprev and discnext then
+ local pre=getfield(discfound,"pre")
+ local post=getfield(discfound,"post")
+ local replace=getfield(discfound,"replace")
+ if not replace then
+ local prev=getfield(base,"prev")
+ local copied=copy_node_list(comp)
+ setfield(discnext,"prev",nil)
+ setfield(discprev,"next",nil)
+ if pre then
+ setfield(discprev,"next",pre)
+ setfield(pre,"prev",discprev)
+ end
+ pre=comp
+ if post then
+ local tail=find_node_tail(post)
+ setfield(tail,"next",discnext)
+ setfield(discnext,"prev",tail)
+ setfield(post,"prev",nil)
+ else
+ post=discnext
+ end
+ setfield(prev,"next",discfound)
+ setfield(discfound,"prev",prev)
+ setfield(discfound,"next",next)
+ setfield(next,"prev",discfound)
+ setfield(base,"next",nil)
+ setfield(base,"prev",nil)
+ setfield(base,"components",copied)
+ setfield(discfound,"pre",pre)
+ setfield(discfound,"post",post)
+ setfield(discfound,"replace",base)
+ setfield(discfound,"subtype",discretionary_code)
+ base=prev
+ end
+ end
end
return head,base
end
-function handlers.gsub_single(head,start,kind,lookupname,replacement)
- if trace_singles then
- logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement))
+local function multiple_glyphs(head,start,multiple,ignoremarks)
+ local nofmultiples=#multiple
+ if nofmultiples>0 then
+ resetinjection(start)
+ setfield(start,"char",multiple[1])
+ if nofmultiples>1 then
+ local sn=getnext(start)
+ for k=2,nofmultiples do
+ local n=copy_node(start)
+ resetinjection(n)
+ setfield(n,"char",multiple[k])
+ setfield(n,"prev",start)
+ setfield(n,"next",sn)
+ if sn then
+ setfield(sn,"prev",n)
+ end
+ setfield(start,"next",n)
+ start=n
+ end
+ end
+ return head,start,true
+ else
+ if trace_multiples then
+ logprocess("no multiple for %s",gref(getchar(start)))
+ end
+ return head,start,false
end
- resetinjection(start)
- setfield(start,"char",replacement)
- return head,start,true
end
local function get_alternative_glyph(start,alternatives,value,trace_alternatives)
local n=#alternatives
@@ -11678,33 +12034,13 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives
end
end
end
-local function multiple_glyphs(head,start,multiple,ignoremarks)
- local nofmultiples=#multiple
- if nofmultiples>0 then
- resetinjection(start)
- setfield(start,"char",multiple[1])
- if nofmultiples>1 then
- local sn=getnext(start)
- for k=2,nofmultiples do
- local n=copy_node(start)
- resetinjection(n)
- setfield(n,"char",multiple[k])
- setfield(n,"next",sn)
- setfield(n,"prev",start)
- if sn then
- setfield(sn,"prev",n)
- end
- setfield(start,"next",n)
- start=n
- end
- end
- return head,start,true
- else
- if trace_multiples then
- logprocess("no multiple for %s",gref(getchar(start)))
- end
- return head,start,false
+function handlers.gsub_single(head,start,kind,lookupname,replacement)
+ if trace_singles then
+ logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement))
end
+ resetinjection(start)
+ setfield(start,"char",replacement)
+ return head,start,true
end
function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence)
local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue
@@ -11729,7 +12065,7 @@ function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence)
return multiple_glyphs(head,start,multiple,sequence.flags[1])
end
function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
- local s,stop,discfound=getnext(start),nil,false
+ local s,stop=getnext(start),nil
local startchar=getchar(start)
if marks[startchar] then
while s do
@@ -11757,23 +12093,29 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
else
head,start=markstoligature(kind,lookupname,head,start,stop,lig)
end
- return head,start,true
+ return head,start,true,false
else
end
end
else
local skipmark=sequence.flags[1]
+ local discfound=false
+ local lastdisc=nil
while s do
local id=getid(s)
- if id==glyph_code and getsubtype(s)<256 then
- if getfont(s)==currentfont then
+ if id==glyph_code and getsubtype(s)<256 then
+ if getfont(s)==currentfont then
local char=getchar(s)
if skipmark and marks[char] then
s=getnext(s)
- else
- local lg=ligature[char]
+ else
+ local lg=ligature[char]
if lg then
- stop=s
+ if not discfound and lastdisc then
+ discfound=lastdisc
+ lastdisc=nil
+ end
+ stop=s
ligature=lg
s=getnext(s)
else
@@ -11784,13 +12126,13 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
break
end
elseif id==disc_code then
- discfound=true
+ lastdisc=s
s=getnext(s)
else
break
end
end
- local lig=ligature.ligature
+ local lig=ligature.ligature
if lig then
if stop then
if trace_ligatures then
@@ -11807,12 +12149,71 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig))
end
end
- return head,start,true
+ return head,start,true,discfound
else
end
end
+ return head,start,false,discfound
+end
+function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence,injection)
+ local startchar=getchar(start)
+ local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,injection)
+ if trace_kerns then
+ logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h)
+ end
return head,start,false
end
+function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence,lookuphash,i,injection)
+ local snext=getnext(start)
+ if not snext then
+ return head,start,false
+ else
+ local prev=start
+ local done=false
+ local factor=tfmdata.parameters.factor
+ local lookuptype=lookuptypes[lookupname]
+ while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do
+ local nextchar=getchar(snext)
+ local krn=kerns[nextchar]
+ if not krn and marks[nextchar] then
+ prev=snext
+ snext=getnext(snext)
+ else
+ if not krn then
+ elseif type(krn)=="table" then
+ if lookuptype=="pair" then
+ local a,b=krn[2],krn[3]
+ if a and #a>0 then
+ local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,injection)
+ if trace_kerns then
+ local startchar=getchar(start)
+ logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ if b and #b>0 then
+ local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,injection)
+ if trace_kerns then
+ local startchar=getchar(start)
+ logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ else
+ report_process("%s: check this out (old kern stuff)",pref(kind,lookupname))
+ end
+ done=true
+ elseif krn~=0 then
+ local k=setkern(snext,factor,rlmode,krn,injection)
+ if trace_kerns then
+ logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar))
+ end
+ done=true
+ end
+ break
+ end
+ end
+ return head,start,done
+ end
+end
function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence)
local markchar=getchar(start)
if marks[markchar] then
@@ -11965,7 +12366,7 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence
if al[anchor] then
local ma=markanchors[anchor]
if ma then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar])
+ local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)
if trace_marks then
logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -12043,65 +12444,6 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)
return head,start,false
end
end
-function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence)
- local startchar=getchar(start)
- local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h)
- end
- return head,start,false
-end
-function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)
- local snext=getnext(start)
- if not snext then
- return head,start,false
- else
- local prev,done=start,false
- local factor=tfmdata.parameters.factor
- local lookuptype=lookuptypes[lookupname]
- while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do
- local nextchar=getchar(snext)
- local krn=kerns[nextchar]
- if not krn and marks[nextchar] then
- prev=snext
- snext=getnext(snext)
- else
- if not krn then
- elseif type(krn)=="table" then
- if lookuptype=="pair" then
- local a,b=krn[2],krn[3]
- if a and #a>0 then
- local startchar=getchar(start)
- local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- if b and #b>0 then
- local startchar=getchar(start)
- local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
- if trace_kerns then
- logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- else
- report_process("%s: check this out (old kern stuff)",pref(kind,lookupname))
- end
- done=true
- elseif krn~=0 then
- local k=setkern(snext,factor,rlmode,krn)
- if trace_kerns then
- logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar))
- end
- done=true
- end
- break
- end
- end
- return head,start,done
- end
-end
-local chainmores={}
local chainprocs={}
local function logprocess(...)
if trace_steps then
@@ -12121,10 +12463,6 @@ function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,looku
logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
return head,start,false
end
-function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n)
- logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
- return head,start,false
-end
function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements)
local char=getchar(start)
local replacement=replacements[char]
@@ -12143,7 +12481,7 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
local current=start
local subtables=currentlookup.subtables
if #subtables>1 then
- logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," "))
+ logwarning("todo: check if we need to loop over the replacements: % t",subtables)
end
while current do
if getid(current)==glyph_code then
@@ -12177,7 +12515,6 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
end
return head,start,false
end
-chainmores.gsub_single=chainprocs.gsub_single
function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
local startchar=getchar(start)
local subtables=currentlookup.subtables
@@ -12202,7 +12539,6 @@ function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,
end
return head,start,false
end
-chainmores.gsub_multiple=chainprocs.gsub_multiple
function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
local current=start
local subtables=currentlookup.subtables
@@ -12244,7 +12580,6 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext
end
return head,start,false
end
-chainmores.gsub_alternate=chainprocs.gsub_alternate
function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex)
local startchar=getchar(start)
local subtables=currentlookup.subtables
@@ -12264,13 +12599,19 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
local s=getnext(start)
local discfound=false
local last=stop
- local nofreplacements=0
+ local nofreplacements=1
local skipmark=currentlookup.flags[1]
while s do
local id=getid(s)
if id==disc_code then
- s=getnext(s)
- discfound=true
+ if not discfound then
+ discfound=s
+ end
+ if s==stop then
+ break
+ else
+ s=getnext(s)
+ end
else
local schar=getchar(s)
if skipmark and marks[schar] then
@@ -12303,7 +12644,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
end
end
head,start=toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound)
- return head,start,true,nofreplacements
+ return head,start,true,nofreplacements,discfound
elseif trace_bugs then
if start==stop then
logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
@@ -12313,9 +12654,82 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
end
end
end
- return head,start,false,0
+ return head,start,false,0,false
+end
+function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
+ local startchar=getchar(start)
+ local subtables=currentlookup.subtables
+ local lookupname=subtables[1]
+ local kerns=lookuphash[lookupname]
+ if kerns then
+ kerns=kerns[startchar]
+ if kerns then
+ local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns)
+ if trace_kerns then
+ logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h)
+ end
+ end
+ end
+ return head,start,false
+end
+function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
+ local snext=getnext(start)
+ if snext then
+ local startchar=getchar(start)
+ local subtables=currentlookup.subtables
+ local lookupname=subtables[1]
+ local kerns=lookuphash[lookupname]
+ if kerns then
+ kerns=kerns[startchar]
+ if kerns then
+ local lookuptype=lookuptypes[lookupname]
+ local prev,done=start,false
+ local factor=tfmdata.parameters.factor
+ while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do
+ local nextchar=getchar(snext)
+ local krn=kerns[nextchar]
+ if not krn and marks[nextchar] then
+ prev=snext
+ snext=getnext(snext)
+ else
+ if not krn then
+ elseif type(krn)=="table" then
+ if lookuptype=="pair" then
+ local a,b=krn[2],krn[3]
+ if a and #a>0 then
+ local startchar=getchar(start)
+ local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a)
+ if trace_kerns then
+ logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ if b and #b>0 then
+ local startchar=getchar(start)
+ local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b)
+ if trace_kerns then
+ logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ else
+ report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname))
+ end
+ done=true
+ elseif krn~=0 then
+ local k=setkern(snext,factor,rlmode,krn)
+ if trace_kerns then
+ logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar))
+ end
+ done=true
+ end
+ break
+ end
+ end
+ return head,start,done
+ end
+ end
+ end
+ return head,start,false
end
-chainmores.gsub_ligature=chainprocs.gsub_ligature
function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
local markchar=getchar(start)
if marks[markchar] then
@@ -12479,7 +12893,7 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext
if al[anchor] then
local ma=markanchors[anchor]
if ma then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar])
+ local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)
if trace_marks then
logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -12566,113 +12980,286 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l
end
return head,start,false
end
-function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
- local startchar=getchar(start)
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local kerns=lookuphash[lookupname]
- if kerns then
- kerns=kerns[startchar]
- if kerns then
- local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h)
+local function show_skip(kind,chainname,char,ck,class)
+ if ck[9] then
+ logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
+ else
+ logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])
+ end
+end
+local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,chainindex,sequence,chainproc)
+ if not start then
+ return head,start,false
+ end
+ local startishead=start==head
+ local seq=ck[3]
+ local f=ck[4]
+ local l=ck[5]
+ local s=#seq
+ local done=false
+ local sweepnode=sweepnode
+ local sweeptype=sweeptype
+ local sweepoverflow=false
+ local checkdisc=getprev(head)
+ local keepdisc=not sweepnode
+ local lookaheaddisc=nil
+ local backtrackdisc=nil
+ local current=start
+ local last=start
+ local prev=getprev(start)
+ local i=f
+ while i<=l do
+ local id=getid(current)
+ if id==glyph_code then
+ i=i+1
+ last=current
+ current=getnext(current)
+ elseif id==disc_code then
+ if keepdisc then
+ keepdisc=false
+ if notmatchpre[current]~=notmatchreplace[current] then
+ lookaheaddisc=current
+ end
+ local replace=getfield(current,"replace")
+ while replace and i<=l do
+ if getid(replace)==glyph_code then
+ i=i+1
+ end
+ replace=getnext(replace)
+ end
+ last=current
+ current=getnext(c)
+ else
+ head,current=flattendisk(head,current)
+ end
+ else
+ last=current
+ current=getnext(current)
+ end
+ if current then
+ elseif sweepoverflow then
+ break
+ elseif sweeptype=="post" or sweeptype=="replace" then
+ current=getnext(sweepnode)
+ if current then
+ sweeptype=nil
+ sweepoverflow=true
+ else
+ break
end
end
end
- return head,start,false
-end
-chainmores.gpos_single=chainprocs.gpos_single
-function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
- local snext=getnext(start)
- if snext then
- local startchar=getchar(start)
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local kerns=lookuphash[lookupname]
- if kerns then
- kerns=kerns[startchar]
- if kerns then
- local lookuptype=lookuptypes[lookupname]
- local prev,done=start,false
- local factor=tfmdata.parameters.factor
- while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do
- local nextchar=getchar(snext)
- local krn=kerns[nextchar]
- if not krn and marks[nextchar] then
- prev=snext
- snext=getnext(snext)
- else
- if not krn then
- elseif type(krn)=="table" then
- if lookuptype=="pair" then
- local a,b=krn[2],krn[3]
- if a and #a>0 then
- local startchar=getchar(start)
- local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- if b and #b>0 then
- local startchar=getchar(start)
- local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
- if trace_kerns then
- logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- else
- report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname))
- local a,b=krn[2],krn[6]
- if a and a~=0 then
- local k=setkern(snext,factor,rlmode,a)
- if trace_kerns then
- logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),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=setkern(snext,factor,rlmode,krn)
- if trace_kerns then
- logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar))
- end
- done=true
+ if sweepoverflow then
+ local prev=current and getprev(current)
+ if not current or prev~=sweepnode then
+ local head=getnext(sweepnode)
+ local tail=nil
+ if prev then
+ tail=prev
+ setfield(current,"prev",sweepnode)
+ else
+ tail=find_node_tail(head)
+ end
+ setfield(sweepnode,"next",current)
+ setfield(head,"prev",nil)
+ setfield(tail,"next",nil)
+ appenddisc(sweepnode,head)
+ end
+ end
+ if l<s then
+ local i=l
+ local t=sweeptype=="post" or sweeptype=="replace"
+ while current and i<s do
+ local id=getid(current)
+ if id==glyph_code then
+ i=i+1
+ current=getnext(current)
+ elseif id==disc_code then
+ if keepdisc then
+ keepdisc=false
+ if notmatchpre[current]~=notmatchreplace[current] then
+ lookaheaddisc=current
+ end
+ local replace=getfield(c,"replace")
+ while replace and i<s do
+ if getid(replace)==glyph_code then
+ i=i+1
end
- break
+ replace=getnext(replace)
end
+ current=getnext(current)
+ elseif notmatchpre[current]~=notmatchreplace[current] then
+ head,current=flattendisk(head,current)
+ else
+ current=getnext(current)
+ end
+ else
+ current=getnext(current)
+ end
+ if not current and t then
+ current=getnext(sweepnode)
+ if current then
+ sweeptype=nil
end
- return head,start,done
end
end
end
- return head,start,false
-end
-chainmores.gpos_pair=chainprocs.gpos_pair
-local function show_skip(kind,chainname,char,ck,class)
- if ck[9] then
- logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
+ if f>1 then
+ local current=prev
+ local i=f
+ local t=sweeptype=="pre" or sweeptype=="replace"
+ if not current and t and current==checkdisk then
+ current=getprev(sweepnode)
+ end
+ while current and i>1 do
+ local id=getid(current)
+ if id==glyph_code then
+ i=i-1
+ elseif id==disc_code then
+ if keepdisc then
+ keepdisc=false
+ if notmatchpost[current]~=notmatchreplace[current] then
+ backtrackdisc=current
+ end
+ local replace=getfield(current,"replace")
+ while replace and i>1 do
+ if getid(replace)==glyph_code then
+ i=i-1
+ end
+ replace=getnext(replace)
+ end
+ elseif notmatchpost[current]~=notmatchreplace[current] then
+ head,current=flattendisk(head,current)
+ end
+ end
+ current=getprev(current)
+ if t and current==checkdisk then
+ current=getprev(sweepnode)
+ end
+ end
+ end
+ local ok=false
+ if lookaheaddisc then
+ local cf=start
+ local cl=getprev(lookaheaddisc)
+ local cprev=getprev(start)
+ local insertedmarks=0
+ while cprev and getid(cf)==glyph_code and getfont(cf)==currentfont and getsubtype(cf)<256 and marks[getchar(cf)] do
+ insertedmarks=insertedmarks+1
+ cf=cprev
+ startishead=cf==head
+ cprev=getprev(cprev)
+ end
+ setfield(lookaheaddisc,"prev",cprev)
+ if cprev then
+ setfield(cprev,"next",lookaheaddisc)
+ end
+ setfield(cf,"prev",nil)
+ setfield(cl,"next",nil)
+ if startishead then
+ head=lookaheaddisc
+ end
+ local replace=getfield(lookaheaddisc,"replace")
+ local pre=getfield(lookaheaddisc,"pre")
+ local new=copy_node_list(cf)
+ local cnew=new
+ for i=1,insertedmarks do
+ cnew=getnext(cnew)
+ end
+ local clast=cnew
+ for i=f,l do
+ clast=getnext(clast)
+ end
+ if not notmatchpre[lookaheaddisc] then
+ cf,start,ok=chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ end
+ if not notmatchreplace[lookaheaddisc] then
+ new,cnew,ok=chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ end
+ if pre then
+ setfield(cl,"next",pre)
+ setfield(pre,"prev",cl)
+ end
+ if replace then
+ local tail=find_node_tail(new)
+ setfield(tail,"next",replace)
+ setfield(replace,"prev",tail)
+ end
+ setfield(lookaheaddisc,"pre",cf)
+ setfield(lookaheaddisc,"replace",new)
+ start=getprev(lookaheaddisc)
+ sweephead[cf]=getnext(clast)
+ sweephead[new]=getnext(last)
+ elseif backtrackdisc then
+ local cf=getnext(backtrackdisc)
+ local cl=start
+ local cnext=getnext(start)
+ local insertedmarks=0
+ while cnext and getid(cnext)==glyph_code and getfont(cnext)==currentfont and getsubtype(cnext)<256 and marks[getchar(cnext)] do
+ insertedmarks=insertedmarks+1
+ cl=cnext
+ cnext=getnext(cnext)
+ end
+ if cnext then
+ setfield(cnext,"prev",backtrackdisc)
+ end
+ setfield(backtrackdisc,"next",cnext)
+ setfield(cf,"prev",nil)
+ setfield(cl,"next",nil)
+ local replace=getfield(backtrackdisc,"replace")
+ local post=getfield(backtrackdisc,"post")
+ local new=copy_node_list(cf)
+ local cnew=find_node_tail(new)
+ for i=1,insertedmarks do
+ cnew=getprev(cnew)
+ end
+ local clast=cnew
+ for i=f,l do
+ clast=getnext(clast)
+ end
+ if not notmatchpost[backtrackdisc] then
+ cf,start,ok=chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ end
+ if not notmatchreplace[backtrackdisc] then
+ new,cnew,ok=chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ end
+ if post then
+ local tail=find_node_tail(post)
+ setfield(tail,"next",cf)
+ setfield(cf,"prev",tail)
+ else
+ post=cf
+ end
+ if replace then
+ local tail=find_node_tail(replace)
+ setfield(tail,"next",new)
+ setfield(new,"prev",tail)
+ else
+ replace=new
+ end
+ setfield(backtrackdisc,"post",post)
+ setfield(backtrackdisc,"replace",replace)
+ start=getprev(backtrackdisc)
+ sweephead[post]=getnext(clast)
+ sweephead[replace]=getnext(last)
else
- logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])
+ head,start,ok=chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
end
+ return head,start,ok
end
-local quit_on_no_replacement=true
-directives.register("otf.chain.quitonnoreplacement",function(value)
- quit_on_no_replacement=value
-end)
local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash)
+ local sweepnode=sweepnode
+ local sweeptype=sweeptype
+ local diskseen=false
+ local checkdisc=getprev(head)
local flags=sequence.flags
local done=false
local skipmark=flags[1]
local skipligature=flags[2]
local skipbase=flags[3]
- local someskip=skipmark or skipligature or skipbase
- local markclass=sequence.markclass
+ local markclass=sequence.markclass
local skipped=false
- for k=1,#contexts do
+ for k=1,#contexts do
local match=true
local current=start
local last=start
@@ -12682,14 +13269,20 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
if s==1 then
match=getid(current)==glyph_code and getfont(current)==currentfont and getsubtype(current)<256 and seq[1][getchar(current)]
else
- local f,l=ck[4],ck[5]
+ local f=ck[4]
+ local l=ck[5]
if f==1 and f==l then
else
if f==l then
else
+ local discfound=nil
local n=f+1
last=getnext(last)
while n<=l do
+ if not last and (sweeptype=="post" or sweeptype=="replace") then
+ last=getnext(sweepnode)
+ sweeptype=nil
+ end
if last then
local id=getid(last)
if id==glyph_code then
@@ -12697,7 +13290,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
local char=getchar(last)
local ccd=descriptions[char]
if ccd then
- local class=ccd.class
+ local class=ccd.class or "base"
if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
skipped=true
if trace_skips then
@@ -12710,18 +13303,76 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
n=n+1
else
- match=false
+ if discfound then
+ notmatchreplace[discfound]=true
+ match=not notmatchpre[discfound]
+ else
+ match=false
+ end
break
end
else
- match=false
+ if discfound then
+ notmatchreplace[discfound]=true
+ match=not notmatchpre[discfound]
+ else
+ match=false
+ end
break
end
else
- match=false
+ if discfound then
+ notmatchreplace[discfound]=true
+ match=not notmatchpre[discfound]
+ else
+ match=false
+ end
break
end
elseif id==disc_code then
+ diskseen=true
+ discfound=last
+ notmatchpre[last]=nil
+ notmatchpost[last]=true
+ notmatchreplace[last]=nil
+ local pre=getfield(last,"pre")
+ local replace=getfield(last,"replace")
+ if pre then
+ local n=n
+ while pre do
+ if seq[n][getchar(pre)] then
+ n=n+1
+ pre=getnext(pre)
+ if n>l then
+ break
+ end
+ else
+ notmatchpre[last]=true
+ break
+ end
+ end
+ if n<=l then
+ notmatchpre[last]=true
+ end
+ else
+ notmatchpre[last]=true
+ end
+ if replace then
+ while replace do
+ if seq[n][getchar(replace)] then
+ n=n+1
+ replace=getnext(replace)
+ if n>l then
+ break
+ end
+ else
+ notmatchreplace[last]=true
+ match=not notmatchpre[last]
+ break
+ end
+ end
+ match=not notmatchpre[last]
+ end
last=getnext(last)
else
match=false
@@ -12737,49 +13388,132 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
if match and f>1 then
local prev=getprev(start)
if prev then
- local n=f-1
- while n>=1 do
- if prev then
- local id=getid(prev)
- if id==glyph_code then
- if getfont(prev)==currentfont and getsubtype(prev)<256 then
- local char=getchar(prev)
- local ccd=descriptions[char]
- if ccd then
- local class=ccd.class
- if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
- skipped=true
- if trace_skips then
- show_skip(kind,chainname,char,ck,class)
+ if prev==checkdisc and (sweeptype=="pre" or sweeptype=="replace") then
+ prev=getprev(sweepnode)
+ end
+ if prev then
+ local discfound=nil
+ local n=f-1
+ while n>=1 do
+ if prev then
+ local id=getid(prev)
+ if id==glyph_code then
+ if getfont(prev)==currentfont and getsubtype(prev)<256 then
+ local char=getchar(prev)
+ local ccd=descriptions[char]
+ if ccd then
+ local class=ccd.class
+ if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
+ skipped=true
+ if trace_skips then
+ show_skip(kind,chainname,char,ck,class)
+ end
+ elseif seq[n][char] then
+ n=n -1
+ else
+ if discfound then
+ notmatchreplace[discfound]=true
+ match=not notmatchpost[discfound]
+ else
+ match=false
+ end
+ break
end
- elseif seq[n][char] then
- n=n -1
else
- match=false
+ if discfound then
+ notmatchreplace[discfound]=true
+ match=not notmatchpost[discfound]
+ else
+ match=false
+ end
break
end
else
- match=false
+ if discfound then
+ notmatchreplace[discfound]=true
+ match=not notmatchpost[discfound]
+ else
+ match=false
+ end
break
end
+ elseif id==disc_code then
+ diskseen=true
+ discfound=prev
+ notmatchpre[prev]=true
+ notmatchpost[prev]=nil
+ notmatchreplace[prev]=nil
+ local pre=getfield(prev,"pre")
+ local post=getfield(prev,"post")
+ local replace=getfield(prev,"replace")
+ if pre~=start and post~=start and replace~=start then
+ if post then
+ local n=n
+ local posttail=find_node_tail(post)
+ while posttail do
+ if seq[n][getchar(posttail)] then
+ n=n-1
+ if posttail==post then
+ break
+ else
+ posttail=getprev(posttail)
+ if n<1 then
+ break
+ end
+ end
+ else
+ notmatchpost[prev]=true
+ break
+ end
+ end
+ if n>=1 then
+ notmatchpost[prev]=true
+ end
+ else
+ notmatchpost[prev]=true
+ end
+ if replace then
+ local replacetail=find_node_tail(replace)
+ while replacetail do
+ if seq[n][getchar(replacetail)] then
+ n=n-1
+ if replacetail==replace then
+ break
+ else
+ replacetail=getprev(replacetail)
+ if n<1 then
+ break
+ end
+ end
+ else
+ notmatchreplace[prev]=true
+ match=not notmatchpost[prev]
+ break
+ end
+ end
+ if not match then
+ break
+ end
+ else
+ end
+ else
+ end
+ elseif seq[n][32] then
+ n=n -1
else
match=false
break
end
- elseif id==disc_code then
- elseif seq[n][32] then
- n=n -1
+ prev=getprev(prev)
+ elseif seq[n][32] then
+ n=n-1
else
match=false
break
end
- prev=getprev(prev)
- elseif seq[n][32] then
- n=n -1
- else
- match=false
- break
end
+ else
+ match=false
end
else
match=false
@@ -12787,7 +13521,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
if match and s>l then
local current=last and getnext(last)
+ if not current then
+ if sweeptype=="post" or sweeptype=="replace" then
+ current=getnext(sweepnode)
+ end
+ end
if current then
+ local discfound=nil
local n=l+1
while n<=s do
if current then
@@ -12806,18 +13546,79 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
elseif seq[n][char] then
n=n+1
else
- match=false
+ if discfound then
+ notmatchreplace[discfound]=true
+ match=not notmatchpre[discfound]
+ else
+ match=false
+ end
break
end
else
- match=false
+ if discfound then
+ notmatchreplace[discfound]=true
+ match=not notmatchpre[discfound]
+ else
+ match=false
+ end
break
end
else
- match=false
+ if discfound then
+ notmatchreplace[discfound]=true
+ match=not notmatchpre[discfound]
+ else
+ match=false
+ end
break
end
elseif id==disc_code then
+ diskseen=true
+ discfound=current
+ notmatchpre[current]=nil
+ notmatchpost[current]=true
+ notmatchreplace[current]=nil
+ local pre=getfield(current,"pre")
+ local replace=getfield(current,"replace")
+ if pre then
+ local n=n
+ while pre do
+ if seq[n][getchar(pre)] then
+ n=n+1
+ pre=getnext(pre)
+ if n>s then
+ break
+ end
+ else
+ notmatchpre[current]=true
+ break
+ end
+ end
+ if n<=s then
+ notmatchpre[current]=true
+ end
+ else
+ notmatchpre[current]=true
+ end
+ if replace then
+ while replace do
+ if seq[n][getchar(replace)] then
+ n=n+1
+ replace=getnext(replace)
+ if n>s then
+ break
+ end
+ else
+ notmatchreplace[current]=true
+ match=notmatchpre[current]
+ break
+ end
+ end
+ if not match then
+ break
+ end
+ else
+ end
elseif seq[n][32] then
n=n+1
else
@@ -12838,6 +13639,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
end
if match then
+ local diskchain=diskseen or sweepnode
if trace_contexts then
local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5]
local char=getchar(start)
@@ -12856,10 +13658,14 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
local chainlookupname=chainlookups[1]
local chainlookup=lookuptable[chainlookupname]
if chainlookup then
- local cp=chainprocs[chainlookup.type]
- if cp then
+ local chainproc=chainprocs[chainlookup.type]
+ if chainproc then
local ok
- head,start,ok=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ if diskchain then
+ head,start,ok=chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc)
+ else
+ head,start,ok=chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ end
if ok then
done=true
end
@@ -12871,13 +13677,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
else
local i=1
- while true do
+ while start and true do
if skipped then
- while true do
+ while true do
local char=getchar(start)
local ccd=descriptions[char]
if ccd then
- local class=ccd.class
+ local class=ccd.class or "base"
if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
start=getnext(start)
else
@@ -12893,26 +13699,33 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
if not chainlookup then
i=i+1
else
- local cp=chainmores[chainlookup.type]
- if not cp then
+ local chainproc=chainprocs[chainlookup.type]
+ if not chainproc then
logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
i=i+1
else
local ok,n
- head,start,ok,n=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence)
+ if diskchain then
+ head,start,ok=chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc)
+ else
+ head,start,ok,n=chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence)
+ end
if ok then
done=true
- i=i+(n or 1)
- else
- i=i+1
+ if n and n>1 then
+ if i+n>nofchainlookups then
+ break
+ else
+ end
+ end
end
+ i=i+1
end
end
- if i>nofchainlookups then
+ if i>nofchainlookups or not start then
break
elseif start then
start=getnext(start)
- else
end
end
end
@@ -12927,8 +13740,16 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
end
end
+ if done then
+ break
+ end
end
end
+ if diskseen then
+ notmatchpre={}
+ notmatchpost={}
+ notmatchreplace={}
+ end
return head,start,done
end
local verbose_handle_contextchain=function(font,...)
@@ -12999,7 +13820,7 @@ local function initialize(sequence,script,language,enabled)
local scripts=features[kind]
local languages=scripts[script] or scripts[wildcard]
if languages and (languages[language] or languages[wildcard]) then
- return { valid,autofeatures[kind] or false,sequence.chain or 0,kind,sequence }
+ return { valid,autofeatures[kind] or false,sequence,kind }
end
end
end
@@ -13039,6 +13860,175 @@ function otf.dataset(tfmdata,font)
end
return rl
end
+local function kernrun(disc,run)
+ if trace_kernruns then
+ report_run("kern")
+ end
+ local prev=getprev(disc)
+ local next=getnext(disc)
+ local pre=getfield(disc,"pre")
+ local post=getfield(disc,"post")
+ local replace=getfield(disc,"replace")
+ local prevmarks=prev
+ while prevmarks and getid(prevmarks)==glyph_code and marks[getchar(prevmarks)] and getfont(prevmarks)==currentfont and getsubtype(prevmarks)<256 do
+ prevmarks=getprev(prevmarks)
+ end
+ if prev and (pre or replace) and not (getid(prev)==glyph_code and getfont(prev)==currentfont and getsubtype(prev)<256) then
+ prev=false
+ end
+ if next and (post or replace) and not (getid(next)==glyph_code and getfont(next)==currentfont and getsubtype(next)<256) then
+ next=false
+ end
+ if not pre then
+ elseif prev then
+ local nest=getprev(pre)
+ setfield(pre,"prev",prev)
+ setfield(prev,"next",pre)
+ run(prevmarks,"preinjections")
+ setfield(pre,"prev",nest)
+ setfield(prev,"next",disc)
+ else
+ run(pre,"preinjections")
+ end
+ if not post then
+ elseif next then
+ local tail=find_node_tail(post)
+ setfield(tail,"next",next)
+ setfield(next,"prev",tail)
+ run(post,"postinjections",next)
+ setfield(tail,"next",nil)
+ setfield(next,"prev",disc)
+ else
+ run(post,"postinjections")
+ end
+ if not replace and prev and next then
+ setfield(prev,"next",next)
+ setfield(next,"prev",prev)
+ run(prevmarks,"injections",next)
+ setfield(prev,"next",disc)
+ setfield(next,"prev",disc)
+ elseif prev and next then
+ local tail=find_node_tail(replace)
+ local nest=getprev(replace)
+ setfield(replace,"prev",prev)
+ setfield(prev,"next",replace)
+ setfield(tail,"next",next)
+ setfield(next,"prev",tail)
+ run(prevmarks,"replaceinjections",next)
+ setfield(replace,"prev",nest)
+ setfield(prev,"next",disc)
+ setfield(tail,"next",nil)
+ setfield(next,"prev",disc)
+ elseif prev then
+ local nest=getprev(replace)
+ setfield(replace,"prev",prev)
+ setfield(prev,"next",replace)
+ run(prevmarks,"replaceinjections")
+ setfield(replace,"prev",nest)
+ setfield(prev,"next",disc)
+ elseif next then
+ local tail=find_node_tail(replace)
+ setfield(tail,"next",next)
+ setfield(next,"prev",tail)
+ run(replace,"replaceinjections",next)
+ setfield(tail,"next",nil)
+ setfield(next,"prev",disc)
+ else
+ run(replace,"replaceinjections")
+ end
+end
+local function comprun(disc,run)
+ if trace_compruns then
+ report_run("comp: %s",languages.serializediscretionary(disc))
+ end
+ local pre=getfield(disc,"pre")
+ if pre then
+ sweepnode=disc
+ sweeptype="pre"
+ local new,done=run(pre)
+ if done then
+ setfield(disc,"pre",new)
+ end
+ end
+ local post=getfield(disc,"post")
+ if post then
+ sweepnode=disc
+ sweeptype="post"
+ local new,done=run(post)
+ if done then
+ setfield(disc,"post",new)
+ end
+ end
+ local replace=getfield(disc,"replace")
+ if replace then
+ sweepnode=disc
+ sweeptype="replace"
+ local new,done=run(replace)
+ if done then
+ setfield(disc,"replace",new)
+ end
+ end
+ sweepnode=nil
+ sweeptype=nil
+end
+local function testrun(disc,trun,crun)
+ local next=getnext(disc)
+ if next then
+ local replace=getfield(disc,"replace")
+ if replace then
+ local prev=getprev(disc)
+ if prev then
+ local tail=find_node_tail(replace)
+ setfield(tail,"next",next)
+ setfield(next,"prev",tail)
+ if trun(replace,next) then
+ setfield(disc,"replace",nil)
+ setfield(prev,"next",replace)
+ setfield(replace,"prev",prev)
+ setfield(next,"prev",tail)
+ setfield(tail,"next",next)
+ setfield(disc,"prev",nil)
+ setfield(disc,"next",nil)
+ flush_node_list(disc)
+ return replace
+ else
+ setfield(tail,"next",nil)
+ setfield(next,"prev",disc)
+ end
+ else
+ end
+ else
+ end
+ else
+ end
+ comprun(disc,crun)
+ return next
+end
+local function discrun(disc,drun,krun)
+ local next=getnext(disc)
+ local prev=getprev(disc)
+ if trace_discruns then
+ report_run("disc")
+ end
+ if next and prev then
+ setfield(prev,"next",next)
+ drun(prev)
+ setfield(prev,"next",disc)
+ end
+ local pre=getfield(disc,"pre")
+ if not pre then
+ elseif prev then
+ local nest=getprev(pre)
+ setfield(pre,"prev",prev)
+ setfield(prev,"next",pre)
+ krun(prev,"preinjections")
+ setfield(pre,"prev",nest)
+ setfield(prev,"next",disc)
+ else
+ krun(pre,"preinjections")
+ end
+ return next
+end
local function featuresprocessor(head,font,attr)
local lookuphash=lookuphashes[font]
if not lookuphash then
@@ -13059,23 +14049,25 @@ local function featuresprocessor(head,font,attr)
lookuptags=resources.lookuptags
currentfont=font
rlmode=0
+ sweephead={}
local sequences=resources.sequences
local done=false
local datasets=otf.dataset(tfmdata,font,attr)
local dirstack={}
for s=1,#datasets do
local dataset=datasets[s]
- featurevalue=dataset[1]
- local sequence=dataset[5]
+ featurevalue=dataset[1]
+ local attribute=dataset[2]
+ local sequence=dataset[3]
+ local kind=dataset[4]
local rlparmode=0
local topstack=0
local success=false
- local attribute=dataset[2]
- local chain=dataset[3]
local typ=sequence.type
+ local gpossing=typ=="gpos_single" or typ=="gpos_pair"
local subtables=sequence.subtables
- if chain<0 then
- local handler=handlers[typ]
+ local handler=handlers[typ]
+ if typ=="gsub_reversecontextchain" then
local start=find_node_tail(head)
while start do
local id=getid(start)
@@ -13088,13 +14080,14 @@ local function featuresprocessor(head,font,attr)
a=true
end
if a then
+ local char=getchar(start)
for i=1,#subtables do
local lookupname=subtables[i]
local lookupcache=lookuphash[lookupname]
if lookupcache then
- local lookupmatch=lookupcache[getchar(start)]
+ local lookupmatch=lookupcache[char]
if lookupmatch then
- head,start,success=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+ head,start,success=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
if success then
break
end
@@ -13115,7 +14108,6 @@ local function featuresprocessor(head,font,attr)
end
end
else
- local handler=handlers[typ]
local ns=#subtables
local start=head
rlmode=0
@@ -13125,12 +14117,19 @@ local function featuresprocessor(head,font,attr)
if not lookupcache then
report_missing_cache(typ,lookupname)
else
- local function subrun(start)
- local head=start
+ local function c_run(head)
local done=false
+ local start=sweephead[head]
+ if start then
+ sweephead[head]=nil
+ else
+ start=head
+ end
while start do
local id=getid(start)
- if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
+ if id~=glyph_code then
+ start=getnext(start)
+ elseif getfont(start)==font and getsubtype(start)<256 then
local a=getattr(start,0)
if a then
a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
@@ -13141,7 +14140,7 @@ local function featuresprocessor(head,font,attr)
local lookupmatch=lookupcache[getchar(start)]
if lookupmatch then
local ok
- head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
+ head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)
if ok then
done=true
end
@@ -13151,43 +14150,98 @@ local function featuresprocessor(head,font,attr)
start=getnext(start)
end
else
- start=getnext(start)
+ return head,false
end
end
if done then
- success=true
- return head
+ success=true
end
+ return head,done
end
- local function kerndisc(disc)
- local prev=getprev(disc)
- local next=getnext(disc)
- if prev and next then
- setfield(prev,"next",next)
- local a=getattr(prev,0)
- if a then
- a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute)
+ local function t_run(start,stop)
+ while start~=stop do
+ local id=getid(start)
+ if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
+ local a=getattr(start,0)
+ if a then
+ a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
+ else
+ a=not attribute or getprop(start,a_state)==attribute
+ end
+ if a then
+ local lookupmatch=lookupcache[getchar(start)]
+ if lookupmatch then
+ local s=getnext(start)
+ local l=nil
+ while s do
+ local lg=lookupmatch[getchar(s)]
+ if lg then
+ l=lg
+ s=getnext(s)
+ else
+ break
+ end
+ end
+ if l and l.ligature then
+ return true
+ end
+ end
+ end
+ start=getnext(start)
else
- a=not attribute or getprop(prev,a_state)==attribute
+ break
end
- if a then
- local lookupmatch=lookupcache[getchar(prev)]
- if lookupmatch then
- local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
- if ok then
- done=true
- success=true
+ end
+ end
+ local function d_run(prev)
+ local a=getattr(prev,0)
+ if a then
+ a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute)
+ else
+ a=not attribute or getprop(prev,a_state)==attribute
+ end
+ if a then
+ local lookupmatch=lookupcache[getchar(prev)]
+ if lookupmatch then
+ local h,d,ok=handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,1)
+ if ok then
+ done=true
+ success=true
+ end
+ end
+ end
+ end
+ local function k_run(sub,injection,last)
+ local a=getattr(sub,0)
+ if a then
+ a=(a==attr) and (not attribute or getprop(sub,a_state)==attribute)
+ else
+ a=not attribute or getprop(sub,a_state)==attribute
+ end
+ if a then
+ for n in traverse_nodes(sub) do
+ if n==last then
+ break
+ end
+ local id=getid(n)
+ if id==glyph_code then
+ local lookupmatch=lookupcache[getchar(n)]
+ if lookupmatch then
+ local h,d,ok=handler(sub,n,kind,lookupname,lookupmatch,sequence,lookuphash,1,injection)
+ if ok then
+ done=true
+ success=true
+ end
end
+ else
end
end
- setfield(prev,"next",disc)
end
- return next
end
while start do
local id=getid(start)
if id==glyph_code then
- if getfont(start)==font and getsubtype(start)<256 then
+ if getfont(start)==font and getsubtype(start)<256 then
local a=getattr(start,0)
if a then
a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
@@ -13195,13 +14249,18 @@ local function featuresprocessor(head,font,attr)
a=not attribute or getprop(start,a_state)==attribute
end
if a then
- local lookupmatch=lookupcache[getchar(start)]
+ local char=getchar(start)
+ local lookupmatch=lookupcache[char]
if lookupmatch then
local ok
- head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
+ head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)
if ok then
success=true
+ elseif gpossing and zwnjruns and char==zwnj then
+ discrun(start,d_run)
end
+ elseif gpossing and zwnjruns and char==zwnj then
+ discrun(start,d_run)
end
if start then start=getnext(start) end
else
@@ -13211,62 +14270,15 @@ local function featuresprocessor(head,font,attr)
start=getnext(start)
end
elseif id==disc_code then
- if getsubtype(start)==discretionary_code then
- local pre=getfield(start,"pre")
- if pre then
- local new=subrun(pre)
- if new then setfield(start,"pre",new) end
- end
- local post=getfield(start,"post")
- if post then
- local new=subrun(post)
- if new then setfield(start,"post",new) end
- end
- local replace=getfield(start,"replace")
- if replace then
- local new=subrun(replace)
- if new then setfield(start,"replace",new) end
- end
-elseif typ=="gpos_single" or typ=="gpos_pair" then
- kerndisc(start)
- end
- start=getnext(start)
- elseif id==whatsit_code then
- local subtype=getsubtype(start)
- if subtype==dir_code then
- local dir=getfield(start,"dir")
- if dir=="+TRT" or dir=="+TLT" then
- topstack=topstack+1
- dirstack[topstack]=dir
- elseif dir=="-TRT" or dir=="-TLT" then
- topstack=topstack-1
- end
- local newdir=dirstack[topstack]
- if newdir=="+TRT" then
- rlmode=-1
- elseif newdir=="+TLT" then
- rlmode=1
- else
- rlmode=rlparmode
- end
- if trace_directions then
- report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
- end
- elseif subtype==localpar_code then
- local dir=getfield(start,"dir")
- if dir=="TRT" then
- rlparmode=-1
- elseif dir=="TLT" then
- rlparmode=1
- else
- rlparmode=0
- end
- rlmode=rlparmode
- if trace_directions then
- report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
- end
+ if gpossing then
+ kernrun(start,k_run)
+ start=getnext(start)
+ elseif typ=="gsub_ligature" then
+ start=testrun(start,t_run,c_run)
+ else
+ comprun(start,c_run)
+ start=getnext(start)
end
- start=getnext(start)
elseif id==math_code then
start=getnext(end_of_math(start))
else
@@ -13275,12 +14287,19 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
end
end
else
- local function subrun(start)
- local head=start
+ local function c_run(head)
local done=false
+ local start=sweephead[head]
+ if start then
+ sweephead[head]=nil
+ else
+ start=head
+ end
while start do
local id=getid(start)
- if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
+ if id~=glyph_code then
+ start=getnext(start)
+ elseif getfont(start)==font and getsubtype(start)<256 then
local a=getattr(start,0)
if a then
a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
@@ -13288,14 +14307,15 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
a=not attribute or getprop(start,a_state)==attribute
end
if a then
+ local char=getchar(start)
for i=1,ns do
local lookupname=subtables[i]
local lookupcache=lookuphash[lookupname]
if lookupcache then
- local lookupmatch=lookupcache[getchar(start)]
+ local lookupmatch=lookupcache[char]
if lookupmatch then
local ok
- head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+ head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
if ok then
done=true
break
@@ -13312,46 +14332,120 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
start=getnext(start)
end
else
- start=getnext(start)
+ return head,false
end
end
if done then
success=true
- return head
end
+ return head,done
end
- local function kerndisc(disc)
- local prev=getprev(disc)
- local next=getnext(disc)
- if prev and next then
- setfield(prev,"next",next)
- local a=getattr(prev,0)
- if a then
- a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute)
- else
- a=not attribute or getprop(prev,a_state)==attribute
+ local function d_run(prev)
+ local a=getattr(prev,0)
+ if a then
+ a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute)
+ else
+ a=not attribute or getprop(prev,a_state)==attribute
+ end
+ if a then
+ local char=getchar(prev)
+ for i=1,ns do
+ local lookupname=subtables[i]
+ local lookupcache=lookuphash[lookupname]
+ if lookupcache then
+ local lookupmatch=lookupcache[char]
+ if lookupmatch then
+ local h,d,ok=handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,i)
+ if ok then
+ done=true
+ break
+ end
+ end
+ else
+ report_missing_cache(typ,lookupname)
+ end
end
- if a then
- for i=1,ns do
- local lookupname=subtables[i]
- local lookupcache=lookuphash[lookupname]
- if lookupcache then
- local lookupmatch=lookupcache[getchar(prev)]
- if lookupmatch then
- local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
- if ok then
- done=true
- break
+ end
+ end
+ local function k_run(sub,injection,last)
+ local a=getattr(sub,0)
+ if a then
+ a=(a==attr) and (not attribute or getprop(sub,a_state)==attribute)
+ else
+ a=not attribute or getprop(sub,a_state)==attribute
+ end
+ if a then
+ for n in traverse_nodes(sub) do
+ if n==last then
+ break
+ end
+ local id=getid(n)
+ if id==glyph_code then
+ local char=getchar(n)
+ for i=1,ns do
+ local lookupname=subtables[i]
+ local lookupcache=lookuphash[lookupname]
+ if lookupcache then
+ local lookupmatch=lookupcache[char]
+ if lookupmatch then
+ local h,d,ok=handler(head,n,kind,lookupname,lookupmatch,sequence,lookuphash,i,injection)
+ if ok then
+ done=true
+ break
+ end
end
+ else
+ report_missing_cache(typ,lookupname)
+ end
+ end
+ else
+ end
+ end
+ end
+ end
+ local function t_run(start,stop)
+ while start~=stop do
+ local id=getid(start)
+ if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
+ local a=getattr(start,0)
+ if a then
+ a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
+ else
+ a=not attribute or getprop(start,a_state)==attribute
+ end
+ if a then
+ local char=getchar(start)
+ for i=1,ns do
+ local lookupname=subtables[i]
+ local lookupcache=lookuphash[lookupname]
+ if lookupcache then
+ local lookupmatch=lookupcache[char]
+ if lookupmatch then
+ local s=getnext(start)
+ local l=nil
+ while s do
+ local lg=lookupmatch[getchar(s)]
+ if lg then
+ l=lg
+ s=getnext(s)
+ else
+ break
+ end
+ end
+ if l and l.ligature then
+ return true
+ end
+ end
+ else
+ report_missing_cache(typ,lookupname)
end
- else
- report_missing_cache(typ,lookupname)
end
end
+ start=getnext(start)
+ else
+ break
end
- setfield(prev,"next",disc)
end
- return next
end
while start do
local id=getid(start)
@@ -13368,16 +14462,21 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
local lookupname=subtables[i]
local lookupcache=lookuphash[lookupname]
if lookupcache then
- local lookupmatch=lookupcache[getchar(start)]
+ local char=getchar(start)
+ local lookupmatch=lookupcache[char]
if lookupmatch then
local ok
- head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+ head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
if ok then
success=true
break
elseif not start then
break
+ elseif gpossing and zwnjruns and char==zwnj then
+ discrun(start,d_run)
end
+ elseif gpossing and zwnjruns and char==zwnj then
+ discrun(start,d_run)
end
else
report_missing_cache(typ,lookupname)
@@ -13391,62 +14490,15 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
start=getnext(start)
end
elseif id==disc_code then
- if getsubtype(start)==discretionary_code then
- local pre=getfield(start,"pre")
- if pre then
- local new=subrun(pre)
- if new then setfield(start,"pre",new) end
- end
- local post=getfield(start,"post")
- if post then
- local new=subrun(post)
- if new then setfield(start,"post",new) end
- end
- local replace=getfield(start,"replace")
- if replace then
- local new=subrun(replace)
- if new then setfield(start,"replace",new) end
- end
-elseif typ=="gpos_single" or typ=="gpos_pair" then
- kerndisc(start)
- end
- start=getnext(start)
- elseif id==whatsit_code then
- local subtype=getsubtype(start)
- if subtype==dir_code then
- local dir=getfield(start,"dir")
- if dir=="+TRT" or dir=="+TLT" then
- topstack=topstack+1
- dirstack[topstack]=dir
- elseif dir=="-TRT" or dir=="-TLT" then
- topstack=topstack-1
- end
- local newdir=dirstack[topstack]
- if newdir=="+TRT" then
- rlmode=-1
- elseif newdir=="+TLT" then
- rlmode=1
- else
- rlmode=rlparmode
- end
- if trace_directions then
- report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
- end
- elseif subtype==localpar_code then
- local dir=getfield(start,"dir")
- if dir=="TRT" then
- rlparmode=-1
- elseif dir=="TLT" then
- rlparmode=1
- else
- rlparmode=0
- end
- rlmode=rlparmode
- if trace_directions then
- report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
- end
+ if gpossing then
+ kernrun(start,k_run)
+ start=getnext(start)
+ elseif typ=="gsub_ligature" then
+ start=testrun(start,t_run,c_run)
+ else
+ comprun(start,c_run)
+ start=getnext(start)
end
- start=getnext(start)
elseif id==math_code then
start=getnext(end_of_math(start))
else
@@ -13473,43 +14525,46 @@ local function generic(lookupdata,lookupname,unicode,lookuphash)
lookuphash[lookupname]={ [unicode]=lookupdata }
end
end
+local function ligature(lookupdata,lookupname,unicode,lookuphash)
+ local target=lookuphash[lookupname]
+ if not target then
+ target={}
+ lookuphash[lookupname]=target
+ end
+ for i=1,#lookupdata do
+ local li=lookupdata[i]
+ local tu=target[li]
+ if not tu then
+ tu={}
+ target[li]=tu
+ end
+ target=tu
+ end
+ target.ligature=unicode
+end
+local function pair(lookupdata,lookupname,unicode,lookuphash)
+ local target=lookuphash[lookupname]
+ if not target then
+ target={}
+ lookuphash[lookupname]=target
+ end
+ local others=target[unicode]
+ local paired=lookupdata[1]
+ if others then
+ others[paired]=lookupdata
+ else
+ others={ [paired]=lookupdata }
+ target[unicode]=others
+ end
+end
local action={
substitution=generic,
multiple=generic,
alternate=generic,
position=generic,
- ligature=function(lookupdata,lookupname,unicode,lookuphash)
- local target=lookuphash[lookupname]
- if not target then
- target={}
- lookuphash[lookupname]=target
- end
- for i=1,#lookupdata do
- local li=lookupdata[i]
- local tu=target[li]
- if not tu then
- tu={}
- target[li]=tu
- end
- target=tu
- end
- target.ligature=unicode
- end,
- pair=function(lookupdata,lookupname,unicode,lookuphash)
- local target=lookuphash[lookupname]
- if not target then
- target={}
- lookuphash[lookupname]=target
- end
- local others=target[unicode]
- local paired=lookupdata[1]
- if others then
- others[paired]=lookupdata
- else
- others={ [paired]=lookupdata }
- target[unicode]=others
- end
- end,
+ ligature=ligature,
+ pair=pair,
+ kern=pair,
}
local function prepare_lookups(tfmdata)
local rawdata=tfmdata.shared.rawdata
@@ -13520,13 +14575,14 @@ local function prepare_lookups(tfmdata)
local lookuptypes=resources.lookuptypes
local characters=tfmdata.characters
local descriptions=tfmdata.descriptions
+ local duplicates=resources.duplicates
for unicode,character in next,characters do
local description=descriptions[unicode]
if description then
local lookups=description.slookups
if lookups then
for lookupname,lookupdata in next,lookups do
- action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash)
+ action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash,duplicates)
end
end
local lookups=description.mlookups
@@ -13535,7 +14591,7 @@ local function prepare_lookups(tfmdata)
local lookuptype=lookuptypes[lookupname]
for l=1,#lookuplist do
local lookupdata=lookuplist[l]
- action[lookuptype](lookupdata,lookupname,unicode,lookuphash)
+ action[lookuptype](lookupdata,lookupname,unicode,lookuphash,duplicates)
end
end
end
@@ -13557,7 +14613,7 @@ local function prepare_lookups(tfmdata)
for name,anchor in next,anchors do
local lookups=anchor_to_lookup[name]
if lookups then
- for lookup,_ in next,lookups do
+ for lookup in next,lookups do
local target=lookuphash[lookup]
if target then
target[unicode]=anchors
@@ -13639,7 +14695,7 @@ local function prepare_contextchains(tfmdata)
if sequence[1] then
nt=nt+1
t[nt]={ nofrules,lookuptype,sequence,start,stop,rule.lookups,replacements }
- for unic,_ in next,sequence[start] do
+ for unic in next,sequence[start] do
local cu=contexts[unic]
if not cu then
contexts[unic]=t
@@ -13698,9 +14754,8 @@ if not modules then modules={} end modules ['font-otp']={
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
-local next,type=next,type
+local next,type,tostring=next,type,tostring
local sort,concat=table.sort,table.concat
-local sortedhash=table.sortedhash
local trace_packing=false trackers.register("otf.packing",function(v) trace_packing=v end)
local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
local report_otf=logs.reporter("fonts","otf loading")
@@ -14676,10 +15731,11 @@ end
function resolvers.name(specification)
local resolve=fonts.names.resolve
if resolve then
- local resolved,sub=resolve(specification.name,specification.sub,specification)
+ local resolved,sub,subindex=resolve(specification.name,specification.sub,specification)
if resolved then
specification.resolved=resolved
specification.sub=sub
+ specification.subindex=subindex
local suffix=lower(suffixonly(resolved))
if fonts.formats[suffix] then
specification.forced=suffix
@@ -14696,10 +15752,11 @@ end
function resolvers.spec(specification)
local resolvespec=fonts.names.resolvespec
if resolvespec then
- local resolved,sub=resolvespec(specification.name,specification.sub,specification)
+ local resolved,sub,subindex=resolvespec(specification.name,specification.sub,specification)
if resolved then
specification.resolved=resolved
specification.sub=sub
+ specification.subindex=subindex
specification.forced=lower(suffixonly(resolved))
specification.forcedname=resolved
specification.name=removesuffix(resolved)
@@ -15341,12 +16398,30 @@ function nodes.handlers.nodepass(head)
local range=basefonts[i]
local start=range[1]
local stop=range[2]
- if stop then
- start,stop=ligaturing(start,stop)
- start,stop=kerning(start,stop)
- elseif start then
- start=ligaturing(start)
- start=kerning(start)
+ if start or stop then
+ local prev=nil
+ local next=nil
+ local front=start==head
+ if stop then
+ next=stop.next
+ start,stop=ligaturing(start,stop)
+ start,stop=kerning(start,stop)
+ elseif start then
+ prev=start.prev
+ start=ligaturing(start)
+ start=kerning(start)
+ end
+ if prev then
+ start.prev=prev
+ prev.next=start
+ end
+ if next then
+ stop.next=next
+ next.prev=stop
+ end
+ if front then
+ head=start
+ end
end
end
end
@@ -15356,7 +16431,7 @@ function nodes.handlers.nodepass(head)
end
end
function nodes.handlers.basepass(head)
- if not basepass then
+ if basepass then
head=ligaturing(head)
head=kerning(head)
end
diff --git a/src/luaotfload-auxiliary.lua b/src/luaotfload-auxiliary.lua
index 1ef581e..15541af 100644
--- a/src/luaotfload-auxiliary.lua
+++ b/src/luaotfload-auxiliary.lua
@@ -4,8 +4,6 @@
-- DESCRIPTION: part of luaotfload
-- REQUIREMENTS: luaotfload 2.6
-- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang
--- VERSION: 2.6
--- MODIFIED: 2015-03-29 12:43:26+0200
-----------------------------------------------------------------------
--
@@ -19,6 +17,7 @@ local aux = luaotfload.aux
local log = luaotfload.log
local report = log.report
local fonthashes = fonts.hashes
+local encodings = fonts.encodings
local identifiers = fonthashes.identifiers
local fontnames = fonts.names
@@ -214,8 +213,6 @@ luatexbase.add_to_callback(
--- glyphs and characters
-----------------------------------------------------------------------
-local agl = fonts.encodings.agl
-
--- int -> int -> bool
local font_has_glyph = function (font_id, codepoint)
local fontdata = fonts.hashes.identifiers[font_id]
@@ -232,7 +229,7 @@ aux.font_has_glyph = font_has_glyph
local raw_slot_of_name = function (font_id, glyphname)
local fontdata = font.fonts[font_id]
if fontdata.type == "virtual" then --- get base font for glyph idx
- local codepoint = agl.unicodes[glyphname]
+ local codepoint = encodings.agl.unicodes[glyphname]
local glyph = fontdata.characters[codepoint]
if fontdata.characters[codepoint] then
return codepoint
@@ -293,7 +290,7 @@ local indices
--- int -> (string | false)
local name_of_slot = function (codepoint)
if not indices then --- this will load the glyph list
- local unicodes = agl.unicodes
+ local unicodes = encodings.agl.unicodes
indices = table.swapped(unicodes)
end
local glyphname = indices[codepoint]
diff --git a/src/luaotfload-colors.lua b/src/luaotfload-colors.lua
index 89884b6..98549c8 100644
--- a/src/luaotfload-colors.lua
+++ b/src/luaotfload-colors.lua
@@ -19,8 +19,7 @@ explanation: http://tug.org/pipermail/luatex/2013-May/004305.html
--doc]]--
-local log = luaotfload.log
-local logreport = log.report
+local logreport = luaotfload and luaotfload.log.report or print
local nodedirect = node.direct
local newnode = nodedirect.new
@@ -44,10 +43,7 @@ local texsettoks = tex.settoks
local texgettoks = tex.gettoks
local stringformat = string.format
-
-local otffeatures = fonts.constructors.newfeatures("otf")
local identifiers = fonts.hashes.identifiers
-local registerotffeature = otffeatures.register
local add_color_callback --[[ this used to be a global‽ ]]
@@ -101,36 +97,6 @@ local sanitize_color_expression = function (digits)
return sanitized
end
---[[doc--
-``setcolor`` modifies tfmdata.properties.color in place
---doc]]--
-
---- fontobj -> string -> unit
----
---- (where “string” is a rgb value as three octet
---- hexadecimal, with an optional fourth transparency
---- value)
----
-local setcolor = function (tfmdata, value)
- local sanitized = sanitize_color_expression(value)
- local properties = tfmdata.properties
-
- if sanitized then
- properties.color = sanitized
- add_color_callback()
- end
-end
-
-registerotffeature {
- name = "color",
- description = "color",
- initializers = {
- base = setcolor,
- node = setcolor,
- }
-}
-
-
--- something is carried around in ``res``
--- for later use by color_handler() --- but what?
@@ -192,6 +158,7 @@ local whatsit_t = nodetype("whatsit")
local disc_t = nodetype("disc")
local pdfliteral_t = node.subtype("pdf_literal")
local colorstack_t = node.subtype("pdf_colorstack")
+local mlist_to_hlist = node.mlist_to_hlist
local color_callback
local color_attr = luatexbase.new_attribute("luaotfload_color_attribute")
@@ -230,8 +197,6 @@ local get_font_color = function (font_id)
return font_color
end
-local cnt = 0
-
--[[doc--
While the second argument and second returned value are apparently
always nil when the function is called, they temporarily take string
@@ -240,26 +205,24 @@ values during the node list traversal.
--- (node * (string | nil)) -> (node * (string | nil))
local node_colorize
-node_colorize = function (head, current_color)
+node_colorize = function (head, toplevel, current_color)
local n = head
while n do
local n_id = getid(n)
if n_id == hlist_t or n_id == vlist_t then
- cnt = cnt + 1
local n_list = getlist(n)
if getattribute(n_list, color_attr) then
if current_color then
head, n, current_color = color_whatsit(head, n, current_color, false)
end
else
- n_list, current_color = node_colorize(n_list, current_color)
+ n_list, current_color = node_colorize(n_list, false, current_color)
if current_color and getsubtype(n) == 1 then -- created by linebreak
n_list, _, current_color = color_whatsit(n_list, nodetail(n_list), current_color, false, true)
end
setfield(n, "head", n_list)
end
- cnt = cnt - 1
elseif n_id == glyph_t then
--- colorization is restricted to those fonts
@@ -303,7 +266,7 @@ node_colorize = function (head, current_color)
n = getnext(n)
end
- if cnt == 0 and current_color then
+ if toplevel and current_color then
head, _, current_color = color_whatsit(head, nodetail(head), current_color, false, true)
end
@@ -314,7 +277,7 @@ end
--- node -> node
local color_handler = function (head)
head = todirect(head)
- head = node_colorize(head)
+ head = node_colorize(head, true)
head = tonode(head)
-- now append our page resources
@@ -351,6 +314,8 @@ local color_handler = function (head)
end
local color_callback_activated = 0
+local add_to_callback = luatexbase.add_to_callback
+local priority_in_callback = luatexbase.priority_in_callback
--- unit -> unit
add_color_callback = function ( )
@@ -360,22 +325,75 @@ add_color_callback = function ( )
end
if color_callback_activated == 0 then
- luatexbase.add_to_callback(color_callback,
- color_handler,
- "luaotfload.color_handler")
- luatexbase.add_to_callback("hpack_filter",
- function (head, groupcode)
- if groupcode == "hbox" or
- groupcode == "adjusted_hbox" or
- groupcode == "align_set" then
- head = color_handler(head)
- end
- return head
- end,
- "luaotfload.color_handler")
+ add_to_callback(color_callback,
+ color_handler,
+ "luaotfload.color_handler")
+ add_to_callback("hpack_filter",
+ function (head, groupcode)
+ if groupcode == "hbox" or
+ groupcode == "adjusted_hbox" or
+ groupcode == "align_set" then
+ head = color_handler(head)
+ end
+ return head
+ end,
+ "luaotfload.color_handler")
+ add_to_callback("mlist_to_hlist",
+ function (head, display_type, need_penalties)
+ if priority_in_callback("mlist_to_hlist","luaotfload.color_handler") == 1 then
+ head = mlist_to_hlist(head, display_type, need_penalties)
+ end
+ if display_type == "text" then
+ return head
+ end
+ return color_handler(head)
+ end,
+ "luaotfload.color_handler")
color_callback_activated = 1
end
end
+--[[doc--
+``setcolor`` modifies tfmdata.properties.color in place
+--doc]]--
+
+--- fontobj -> string -> unit
+---
+--- (where “string” is a rgb value as three octet
+--- hexadecimal, with an optional fourth transparency
+--- value)
+---
+local setcolor = function (tfmdata, value)
+ local sanitized = sanitize_color_expression(value)
+ local properties = tfmdata.properties
+
+ if sanitized then
+ properties.color = sanitized
+ add_color_callback()
+ end
+end
+
+return {
+ init = function ()
+ logreport = luaotfload.log.report
+ if not fonts then
+ logreport ("log", 0, "color",
+ "OTF mechanisms missing -- did you forget to \z
+ load a font loader?")
+ return false
+ end
+ fonts.handlers.otf.features.register {
+ name = "color",
+ description = "color",
+ initializers = {
+ base = setcolor,
+ node = setcolor,
+ }
+ }
+ return true
+ end
+}
+
+
-- vim:tw=71:sw=4:ts=4:expandtab
diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua
index e2cfbd8..57311dc 100644
--- a/src/luaotfload-configuration.lua
+++ b/src/luaotfload-configuration.lua
@@ -5,8 +5,6 @@
-- REQUIREMENTS: Luaotfload 2.6 or above
-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
-- AUTHOR: Dohyun Kim <nomosnomos@gmail.com>
--- VERSION: same as Luaotfload
--- MODIFIED: 2015-05-05
-------------------------------------------------------------------------------
--
@@ -18,17 +16,12 @@ if not modules then modules = { } end modules ["luaotfload-configuration"] = {
license = "GNU GPL v2.0"
}
-luaotfload = luaotfload or { }
-config = config or { }
-config.luaotfload = { }
-
local status_file = "luaotfload-status"
local luaotfloadstatus = require (status_file)
-local stringexplode = string.explode
+local string = string
local stringfind = string.find
local stringformat = string.format
-local string = string
local stringstrip = string.strip
local stringsub = string.sub
@@ -55,9 +48,7 @@ local lpegmatch = lpeg.match
local commasplitter = lpeg.splitat ","
local equalssplitter = lpeg.splitat "="
-local kpse = kpse
local kpseexpand_path = kpse.expand_path
-local kpselookup = kpse.lookup
local lfs = lfs
local lfsisfile = lfs.isfile
@@ -67,16 +58,12 @@ local file = file
local filejoin = file.join
local filereplacesuffix = file.replacesuffix
+local logreport = print -- overloaded later
+local getwritablepath = caches.getwritablepath
-local parsers = luaotfload.parsers
-
-local log = luaotfload.log
-local logreport = log.report
-
-local config_parser = parsers.config
-local stripslashes = parsers.stripslashes
-local getwritablepath = caches.getwritablepath
+local config_parser -- set later during init
+local stripslashes -- set later during init
-------------------------------------------------------------------------------
--- SETTINGS
@@ -140,11 +127,40 @@ local feature_presets = {
--doc]]--
local registered_loaders = {
- default = luaotfloadstatus and luaotfloadstatus.notes.loader or "reference",
- reference = "reference",
- tl2014 = "tl2014",
+ default = luaotfloadstatus and luaotfloadstatus.notes.loader or "reference",
+ reference = "reference",
+ unpackaged = "unpackaged",
+ context = "context",
+ tl2014 = "tl2014",
}
+local pick_fontloader = function (s)
+ local ldr = registered_loaders[s]
+ if ldr ~= nil and type (ldr) == "string" then
+ logreport ("log", 2, "conf", "Using predefined fontloader \"%s\".", ldr)
+ return ldr
+ end
+ local idx = stringfind (s, ":")
+ if idx and idx > 2 then
+ if stringsub (s, 1, idx - 1) == "context" then
+ local pth = stringsub (s, idx + 1)
+ pth = stringstrip (pth)
+ logreport ("log", 2, "conf", "Context base path specified at \"%s\".", pth)
+ if lfsisdir (pth) then
+ logreport ("log", 5, "conf", "Context base path exists at \"%s\".", pth)
+ return pth
+ end
+ pth = kpseexpand_path (pth)
+ if lfsisdir (pth) then
+ logreport ("log", 5, "conf", "Context base path exists at \"%s\".", pth)
+ return pth
+ end
+ logreport ("both", 0, "conf", "Context base path not found at \"%s\".", pth)
+ end
+ end
+ return nil
+end
+
--[[doc--
The ``post_linebreak_filter`` has been made the default callback for
@@ -182,7 +198,7 @@ local default_config = {
definer = "patch",
log_level = 0,
color_callback = "post_linebreak_filter",
- live = true,
+ fontloader = "default",
},
misc = {
bisect = false,
@@ -292,17 +308,20 @@ local set_name_resolver = function ()
--- replace the resolver from luatex-fonts
if config.luaotfload.db.resolver == "cached" then
logreport ("both", 2, "cache", "Caching of name: lookups active.")
- names.resolvespec = names.resolve_cached
+ names.resolvespec = fonts.names.lookup_font_name_cached
else
- names.resolvespec = names.resolve_name
+ names.resolvespec = fonts.names.lookup_font_name
end
end
return true
end
local set_loglevel = function ()
- log.set_loglevel (config.luaotfload.run.log_level)
- return true
+ if luaotfload then
+ luaotfload.log.set_loglevel (config.luaotfload.run.log_level)
+ return true
+ end
+ return false
end
local build_cache_paths = function ()
@@ -476,10 +495,8 @@ local option_spec = {
in_t = string_t,
out_t = string_t,
transform = function (id)
- local ldr = registered_loaders[id]
+ local ldr = pick_fontloader (id)
if ldr ~= nil then
- logreport ("log", 2, "conf",
- "Using predefined fontloader \"%s\".", ldr)
return ldr
end
logreport ("log", 0, "conf",
@@ -846,7 +863,7 @@ local read = function (extra)
return false
end
- local parsed = lpegmatch (parsers.config, raw)
+ local parsed = lpegmatch (config_parser, raw)
if not parsed then
logreport ("both", 2, "conf", "Error parsing configuration file %q.", readme)
return false
@@ -912,13 +929,27 @@ end
--- EXPORTS
-------------------------------------------------------------------------------
-luaotfload.default_config = default_config
-
-config.actions = {
- read = read,
- apply = apply,
- apply_defaults = apply_defaults,
- reconfigure = reconfigure,
- dump = dump,
+return {
+ init = function ()
+ config.luaotfload = { }
+ logreport = luaotfload.log.report
+ local parsers = luaotfload.parsers
+ config_parser = parsers.config
+ stripslashes = parsers.stripslashes
+
+ luaotfload.default_config = default_config
+ config.actions = {
+ read = read,
+ apply = apply,
+ apply_defaults = apply_defaults,
+ reconfigure = reconfigure,
+ dump = dump,
+ }
+ if not apply_defaults () then
+ logreport ("log", 0, "load",
+ "Configuration unsuccessful: error loading default settings.")
+ end
+ return true
+ end
}
diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua
index f4aab16..1bc2768 100644
--- a/src/luaotfload-database.lua
+++ b/src/luaotfload-database.lua
@@ -39,13 +39,8 @@ if not modules then modules = { } end modules ['luaotfload-database'] = {
local lpeg = require "lpeg"
local P, Cc, lpegmatch = lpeg.P, lpeg.Cc, lpeg.match
-local parsers = luaotfload.parsers
-local read_fonts_conf = parsers.read_fonts_conf
-local stripslashes = parsers.stripslashes
-local splitcomma = parsers.splitcomma
-
local log = luaotfload.log
-local report = log.report
+local logreport = log and log.report or print -- overriden later on
local report_status = log.names_status
local report_status_start = log.names_status_start
local report_status_stop = log.names_status_stop
@@ -119,19 +114,10 @@ local tablefastcopy = table.fastcopy
local tabletofile = table.tofile
local tabletohash = table.tohash
local tableserialize = table.serialize
---- the font loader namespace is “fonts”, same as in Context
---- we need to put some fallbacks into place for when running
---- as a script
-fonts = fonts or { }
-fonts.names = fonts.names or { }
-fonts.definers = fonts.definers or { }
-
-local names = fonts.names
+local names = fonts and fonts.names or { }
+
local name_index = nil --> upvalue for names.data
local lookup_cache = nil --> for names.lookups
-names.version = 2.51
-names.data = nil --- contains the loaded database
-names.lookups = nil --- contains the lookup cache
--- string -> (string * string)
local make_luanames = function (path)
@@ -363,7 +349,7 @@ local initialize_namedata = function (formats, created)
status = { }, -- was: status; map abspath -> mapping
mappings = { }, -- TODO: check if still necessary after rewrite
names = { },
--- files = { }, -- created later
+ files = { }, -- created later
meta = {
created = created or now,
formats = formats,
@@ -485,12 +471,12 @@ load_names = function (dry_run, no_rebuild)
local foundname, data = load_lua_file (config.luaotfload.paths.index_path_lua)
if data then
- report ("log", 0, "db",
- "Font names database loaded from %s", foundname)
- report ("term", 3, "db",
- "Font names database loaded from %s", foundname)
- report ("info", 3, "db", "Loading took %0.f ms.",
- 1000 * (osgettimeofday () - starttime))
+ logreport ("log", 0, "db",
+ "Font names database loaded from %s", foundname)
+ logreport ("term", 3, "db",
+ "Font names database loaded from %s", foundname)
+ logreport ("info", 3, "db", "Loading took %0.f ms.",
+ 1000 * (osgettimeofday () - starttime))
local db_version, names_version
if data.meta then
@@ -503,32 +489,32 @@ load_names = function (dry_run, no_rebuild)
end
names_version = names.version
if db_version ~= names_version then
- report ("both", 0, "db",
- [[Version mismatch; expected %4.3f, got %4.3f.]],
- names_version, db_version)
+ logreport ("both", 0, "db",
+ [[Version mismatch; expected %4.3f, got %4.3f.]],
+ names_version, db_version)
if not fonts_reloaded then
- report ("both", 0, "db", [[Force rebuild.]])
+ logreport ("both", 0, "db", [[Force rebuild.]])
data = update_names ({ }, true, false)
if not data then
- report ("both", 0, "db",
- "Database creation unsuccessful.")
+ logreport ("both", 0, "db",
+ "Database creation unsuccessful.")
end
end
end
else
if no_rebuild == true then
- report ("both", 2, "db",
- [[Database does not exist, skipping rebuild though.]])
+ logreport ("both", 2, "db",
+ [[Database does not exist, skipping rebuild though.]])
return false
end
- report ("both", 0, "db",
- [[Font names database not found, generating new one.]])
- report ("both", 0, "db",
- [[This can take several minutes; please be patient.]])
+ logreport ("both", 0, "db",
+ [[Font names database not found, generating new one.]])
+ logreport ("both", 0, "db",
+ [[This can take several minutes; please be patient.]])
data = update_names (initialize_namedata (get_font_filter ()),
nil, dry_run)
if not data then
- report ("both", 0, "db", "Database creation unsuccessful.")
+ logreport ("both", 0, "db", "Database creation unsuccessful.")
end
end
return data
@@ -559,12 +545,12 @@ local load_lookups
load_lookups = function ( )
local foundname, data = load_lua_file(config.luaotfload.paths.lookup_path_lua)
if data then
- report("log", 0, "cache", "Lookup cache loaded from %s.", foundname)
- report("term", 3, "cache",
- "Lookup cache loaded from %s.", foundname)
+ logreport ("log", 0, "cache", "Lookup cache loaded from %s.", foundname)
+ logreport ("term", 3, "cache",
+ "Lookup cache loaded from %s.", foundname)
else
- report("both", 1, "cache",
- "No lookup cache, creating empty.")
+ logreport ("both", 1, "cache",
+ "No lookup cache, creating empty.")
data = { }
end
lookup_cache = data
@@ -780,23 +766,24 @@ local lookup_font_name_cached
lookup_font_name_cached = function (specification)
if not lookup_cache then load_lookups () end
local request = hash_request(specification)
- report("both", 4, "cache", "Looking for %q in cache ...",
- request)
+ logreport ("both", 4, "cache", "Looking for %q in cache ...",
+ request)
local found = lookup_cache [request]
--- case 1) cache positive ----------------------------------------
if found then --- replay fields from cache hit
- report("info", 4, "cache", "Found!")
+ logreport ("info", 4, "cache", "Found!")
local basename = found[1]
--- check the presence of the file in case it’s been removed
local success = verify_font_file (basename)
if success == true then
return basename, found[2], true
end
- report("both", 4, "cache", "Cached file not found; resolving again.")
+ logreport ("both", 4, "cache",
+ "Cached file not found; resolving again.")
else
- report("both", 4, "cache", "Not cached; resolving.")
+ logreport ("both", 4, "cache", "Not cached; resolving.")
end
--- case 2) cache negative ----------------------------------------
@@ -807,16 +794,16 @@ lookup_font_name_cached = function (specification)
end
--- ... then we add the fields to the cache ... ...
local entry = { filename, subfont }
- report("both", 4, "cache", "New entry: %s.", request)
+ logreport ("both", 4, "cache", "New entry: %s.", request)
lookup_cache [request] = entry
--- obviously, the updated cache needs to be stored.
--- TODO this should trigger a save only once the
--- document is compiled (finish_pdffile callback?)
- report("both", 5, "cache", "Saving updated cache.")
+ logreport ("both", 5, "cache", "Saving updated cache.")
local success = save_lookups ()
if not success then --- sad, but not critical
- report("both", 0, "cache", "Error writing cache.")
+ logreport ("both", 0, "cache", "Error writing cache.")
end
return filename, subfont
end
@@ -973,8 +960,8 @@ local lookup_familyname = function (specification, name, style, askedsize)
if not success then
return nil, nil
end
- report ("info", 2, "db", "Match found: %s(%d).",
- resolved, subfont or 0)
+ logreport ("info", 2, "db", "Match found: %s(%d).",
+ resolved, subfont or 0)
return resolved, subfont
end
@@ -1140,9 +1127,9 @@ reload_db = function (why, caller, ...)
local namedata = name_index
local formats = tableconcat (namedata.meta.formats, ",")
- report ("both", 0, "db",
- "Reload initiated (formats: %s); reason: %q.",
- formats, why)
+ logreport ("both", 0, "db",
+ "Reload initiated (formats: %s); reason: %q.",
+ formats, why)
set_font_filter (formats)
namedata = update_names (namedata, false, false)
@@ -1153,7 +1140,7 @@ reload_db = function (why, caller, ...)
return caller (...)
end
- report ("both", 0, "db", "Database update unsuccessful.")
+ logreport ("both", 0, "db", "Database update unsuccessful.")
end
--- string -> string -> int
@@ -1184,6 +1171,25 @@ local iterative_levenshtein = function (s1, s2)
return costs[len2]--- lower right has the distance
end
+--- string list -> string list
+local delete_dupes = function (lst)
+ local n0 = #lst
+ if n0 == 0 then return lst end
+ tablesort (lst)
+ local ret = { }
+ local last
+ for i = 1, n0 do
+ local cur = lst[i]
+ if cur ~= last then
+ last = cur
+ ret[#ret + 1] = cur
+ end
+ end
+ logreport (false, 8, "query",
+ "Removed %d duplicate names.", n0 - #ret)
+ return ret
+end
+
--- string -> int -> bool
find_closest = function (name, limit)
local name = sanitize_fontname (name)
@@ -1229,6 +1235,7 @@ find_closest = function (name, limit)
else --- append
namelst[#namelst+1] = fullname
end
+
by_distance[dist] = namelst
end
@@ -1237,16 +1244,16 @@ find_closest = function (name, limit)
if n_distances > 0 then --- got some data
tablesort(distances)
limit = mathmin(n_distances, limit)
- report(false, 1, "query",
- "Displaying %d distance levels.", limit)
+ logreport (false, 1, "query",
+ "Displaying %d distance levels.", limit)
for i = 1, limit do
local dist = distances[i]
- local namelst = by_distance[dist]
- report(false, 0, "query",
- "Distance from \"%s\": %s\n "
- .. tableconcat (namelst, "\n "),
- name, dist)
+ local namelst = delete_dupes (by_distance[dist])
+ logreport (false, 0, "query",
+ "Distance from \"%s\": %s\n "
+ .. tableconcat (namelst, "\n "),
+ name, dist)
end
return true
@@ -1273,7 +1280,7 @@ local load_font_file = function (filename, subfont)
local rawfont, _msg = fontloaderopen (filename, subfont)
--local rawfont, _msg = fontloaderinfo (filename, subfont)
if not rawfont then
- report ("log", 1, "db", "ERROR: failed to open %s.", filename)
+ logreport ("log", 1, "db", "ERROR: failed to open %s.", filename)
return
end
return rawfont
@@ -1316,8 +1323,8 @@ local get_english_names = function (metadata)
end
-- no (English) names table, probably a broken font
- report("both", 3, "db",
- "%s: missing or broken English names table.", basename)
+ logreport ("both", 3, "db",
+ "%s: missing or broken English names table.", basename)
return { fontname = metadata.fontname,
fullname = metadata.fullname, }
end
@@ -1343,9 +1350,9 @@ local get_raw_info = function (metadata, basename)
--- Broken names table, e.g. avkv.ttf with UTF-16 strings;
--- we put some dummies in place like the fontloader
--- (font-otf.lua) does.
- report("both", 3, "db",
- "%s has invalid postscript font names, using dummies.",
- basename)
+ logreport ("both", 3, "db",
+ "%s has invalid postscript font names, using dummies.",
+ basename)
fontname = "bad-fontname-" .. basename
fullname = "bad-fullname-" .. basename
end
@@ -1619,7 +1626,7 @@ local compare_timestamps = function (fullname,
if targetentrystatus ~= nil
and targetentrystatus.timestamp == targettimestamp then
- report ("log", 3, "db", "Font %q already read.", fullname)
+ logreport ("log", 3, "db", "Font %q already read.", fullname)
return false
end
@@ -1641,7 +1648,7 @@ local compare_timestamps = function (fullname,
targetentrystatus.index [targetindex + 1] = location
end
- report ("log", 3, "db", "Font %q already indexed.", fullname)
+ logreport ("log", 3, "db", "Font %q already indexed.", fullname)
return false
end
@@ -1721,8 +1728,8 @@ local read_font_names = function (fullname,
--- 1) skip if blacklisted
if names.blacklist[fullname] or names.blacklist[basename] then
- report("log", 2, "db",
- "Ignoring blacklisted font %q.", fullname)
+ logreport ("log", 2, "db",
+ "Ignoring blacklisted font %q.", fullname)
return false
end
@@ -1745,8 +1752,8 @@ local read_font_names = function (fullname,
local loader = loaders [format] --- ot_fullinfo, t1_fullinfo
if not loader then
- report ("both", 0, "db",
- "Unknown format: %q, skipping.", format)
+ logreport ("both", 0, "db",
+ "Unknown format: %q, skipping.", format)
return false
end
@@ -1755,8 +1762,8 @@ local read_font_names = function (fullname,
local info = fontloaderinfo (fullname)
if not info then
- report ("log", 1, "db",
- "Failed to read basic information from %q", basename)
+ logreport ("log", 1, "db",
+ "Failed to read basic information from %q", basename)
return false
end
@@ -1828,11 +1835,7 @@ do
end
end
-fonts.path_normalize = path_normalize
-
-names.blacklist = { }
-
-local blacklist = names.blacklist
+local blacklist = { }
local p_blacklist --- prefixes of dirs
--- string list -> string list
@@ -1861,8 +1864,8 @@ local create_blacklist = function (blacklist, whitelist)
local result = { }
local dirs = { }
- report("info", 2, "db", "Blacklisting %d files and directories.",
- #blacklist)
+ logreport ("info", 2, "db", "Blacklisting %d files and directories.",
+ #blacklist)
for i=1, #blacklist do
local entry = blacklist[i]
if lfsisdir(entry) then
@@ -1872,7 +1875,7 @@ local create_blacklist = function (blacklist, whitelist)
end
end
- report("info", 2, "db", "Whitelisting %d files.", #whitelist)
+ logreport ("info", 2, "db", "Whitelisting %d files.", #whitelist)
for i=1, #whitelist do
result[whitelist[i]] = nil
end
@@ -1914,9 +1917,9 @@ read_blacklist = function ()
if first_chr == "%" or stringis_empty(line) then
-- comment or empty line
elseif first_chr == "-" then
- report ("both", 3, "db",
- "Whitelisted file %q via %q.",
- line, path)
+ logreport ("both", 3, "db",
+ "Whitelisted file %q via %q.",
+ line, path)
whitelist[#whitelist+1] = stringsub(line, 2, -1)
else
local cmt = stringfind(line, "%%")
@@ -1924,9 +1927,9 @@ read_blacklist = function ()
line = stringsub(line, 1, cmt - 1)
end
line = stringstrip(line)
- report ("both", 3, "db",
- "Blacklisted file %q via %q.",
- line, path)
+ logreport ("both", 3, "db",
+ "Blacklisted file %q via %q.",
+ line, path)
blacklist[#blacklist+1] = line
end
end
@@ -1938,9 +1941,8 @@ end
local p_font_filter
do
- local current_formats = { }
-
local extension_pattern = function (list)
+ if type (list) ~= "table" or #list == 0 then return P(-1) end
local pat
for i=#list, 1, -1 do
local e = list[i]
@@ -1957,12 +1959,17 @@ do
--- small helper to adjust the font filter pattern (--formats
--- option)
+ local current_formats = { }
+
set_font_filter = function (formats)
if not formats or type (formats) ~= "string" then
return
end
+ if splitcomma == nil then
+ splitcomma = luaotfload.parsers and luaotfload.parsers.splitcomma
+ end
if stringsub (formats, 1, 1) == "+" then -- add
formats = lpegmatch (splitcomma, stringsub (formats, 2))
if formats then
@@ -2123,24 +2130,24 @@ end
--- string -> string -> string * string list
local collect_font_filenames_dir = function (dirname, location)
if lpegmatch (p_blacklist, dirname) then
- report ("both", 4, "db",
- "Skipping blacklisted directory %s.", dirname)
+ logreport ("both", 4, "db",
+ "Skipping blacklisted directory %s.", dirname)
--- ignore
return { }
end
local found = find_font_files (dirname, location ~= "texmf" and location ~= "local")
if not found then
- report ("both", 4, "db",
- "No such directory: %q; skipping.", dirname)
+ logreport ("both", 4, "db",
+ "No such directory: %q; skipping.", dirname)
return { }
end
local nfound = #found
local files = { }
- report ("both", 4, "db",
- "%d font files detected in %s.",
- nfound, dirname)
+ logreport ("both", 4, "db",
+ "%d font files detected in %s.",
+ nfound, dirname)
for j = 1, nfound do
local fullname = found[j]
files[#files + 1] = { path_normalize (fullname), location }
@@ -2148,10 +2155,12 @@ local collect_font_filenames_dir = function (dirname, location)
return files
end
-
--- string list -> string list
local filter_out_pwd = function (dirs)
local result = { }
+ if stripslashes == nil then
+ stripslashes = luaotfload.parsers and luaotfload.parsers.stripslashes
+ end
local pwd = path_normalize (lpegmatch (stripslashes,
lfscurrentdir ()))
for i = 1, #dirs do
@@ -2184,14 +2193,14 @@ local collect_font_filenames_texmf = function ()
local osfontdir = kpseexpand_path "$OSFONTDIR"
if stringis_empty (osfontdir) then
- report ("info", 1, "db", "Scanning TEXMF for fonts...")
+ logreport ("info", 1, "db", "Scanning TEXMF for fonts...")
else
- report ("info", 1, "db", "Scanning TEXMF and $OSFONTDIR for fonts...")
+ logreport ("info", 1, "db", "Scanning TEXMF and $OSFONTDIR for fonts...")
if log.get_loglevel () > 3 then
local osdirs = filesplitpath (osfontdir)
- report ("info", 0, "db", "$OSFONTDIR has %d entries:", #osdirs)
+ logreport ("info", 0, "db", "$OSFONTDIR has %d entries:", #osdirs)
for i = 1, #osdirs do
- report ("info", 0, "db", "[%d] %s", i, osdirs[i])
+ logreport ("info", 0, "db", "[%d] %s", i, osdirs[i])
end
end
end
@@ -2205,14 +2214,14 @@ local collect_font_filenames_texmf = function ()
end
local tasks = filter_out_pwd (filesplitpath (fontdirs))
- report ("info", 3, "db",
- "Initiating scan of %d directories.", #tasks)
+ logreport ("info", 3, "db",
+ "Initiating scan of %d directories.", #tasks)
local files = { }
for _, dir in next, tasks do
files = tableappend (files, collect_font_filenames_dir (dir, "texmf"))
end
- report ("term", 3, "db", "Collected %d files.", #files)
+ logreport ("term", 3, "db", "Collected %d files.", #files)
return files
end
@@ -2233,7 +2242,10 @@ local function get_os_dirs ()
"/usr/local/etc/fonts/fonts.conf",
"/etc/fonts/fonts.conf",
}
- local os_dirs = read_fonts_conf(fonts_conves, find_files)
+ if not luaotfload.parsers then
+ logreport ("log", 0, "db", "Fatal: no fonts.conf parser.")
+ end
+ local os_dirs = luaotfload.parsers.read_fonts_conf(fonts_conves, find_files)
return os_dirs
end
return {}
@@ -2246,15 +2258,20 @@ end
--doc]]--
--- string list -> size_t
-local count_removed = function (old)
- report("log", 4, "db", "Checking removed files.")
+local count_removed = function (files)
+ if not files or not files.full then
+ logreport ("log", 4, "db", "Empty file store; no data to work with.")
+ return 0
+ end
+ local old = files.full
+ logreport ("log", 4, "db", "Checking removed files.")
local nrem = 0
local nold = #old
for i = 1, nold do
local f = old[i]
if not kpsereadable_file (f) then
- report("log", 2, "db",
- "File %s does not exist in file system.")
+ logreport ("log", 2, "db",
+ "File %s does not exist in file system.")
nrem = nrem + 1
end
end
@@ -2281,7 +2298,7 @@ local retrieve_namedata = function (files, currentnames, targetnames, dry_run)
local nfiles = #files
local nnew = 0
- report ("info", 1, "db", "Scanning %d collected font files ...", nfiles)
+ logreport ("info", 1, "db", "Scanning %d collected font files ...", nfiles)
local bylocation = { texmf = { 0, 0 }
, ["local"] = { 0, 0 }
@@ -2294,12 +2311,12 @@ local retrieve_namedata = function (files, currentnames, targetnames, dry_run)
count[1] = count[1] + 1
if dry_run == true then
local truncated = truncate_string (fullname, 43)
- report ("log", 2, "db", "Would have been loading %s.", fullname)
+ logreport ("log", 2, "db", "Would have been loading %s.", fullname)
report_status ("term", "db", "Would have been loading %s", truncated)
--- skip the read_font_names part
else
local truncated = truncate_string (fullname, 32)
- report ("log", 2, "db", "Loading font %s.", fullname)
+ logreport ("log", 2, "db", "Loading font %s.", fullname)
report_status ("term", "db", "Loading font %s", truncated)
local new = read_font_names (fullname, currentnames,
targetnames, location)
@@ -2311,8 +2328,8 @@ local retrieve_namedata = function (files, currentnames, targetnames, dry_run)
end
report_status_stop ("term", "db", "Scanned %d files, %d new.", nfiles, nnew)
for location, count in next, bylocation do
- report ("term", 4, "db", " * %s: %d files, %d new",
- location, count[1], count[2])
+ logreport ("term", 4, "db", " * %s: %d files, %d new",
+ location, count[1], count[2])
end
return nnew
end
@@ -2321,15 +2338,15 @@ end
local collect_font_filenames_system = function ()
local n_scanned, n_new = 0, 0
- report ("info", 1, "db", "Scanning system fonts...")
- report ("info", 2, "db",
- "Searching in static system directories...")
+ logreport ("info", 1, "db", "Scanning system fonts...")
+ logreport ("info", 2, "db",
+ "Searching in static system directories...")
local files = { }
for _, dir in next, get_os_dirs () do
tableappend (files, collect_font_filenames_dir (dir, "system"))
end
- report ("term", 3, "db", "Collected %d files.", #files)
+ logreport ("term", 3, "db", "Collected %d files.", #files)
return files
end
@@ -2355,25 +2372,25 @@ end
--- unit -> string * string list
local collect_font_filenames_local = function ()
local pwd = lfscurrentdir ()
- report ("both", 1, "db", "Scanning for fonts in $PWD (%q) ...", pwd)
+ logreport ("both", 1, "db", "Scanning for fonts in $PWD (%q) ...", pwd)
local files = collect_font_filenames_dir (pwd, "local")
local nfiles = #files
if nfiles > 0 then
targetnames.meta["local"] = true --- prevent saving to disk
- report ("term", 1, "db", "Found %d files.", pwd)
+ logreport ("term", 1, "db", "Found %d files.", pwd)
else
- report ("term", 1, "db",
- "Couldn’t find a thing here. What a waste.", pwd)
+ logreport ("term", 1, "db",
+ "Couldn’t find a thing here. What a waste.", pwd)
end
- report ("term", 3, "db", "Collected %d files.", #files)
+ logreport ("term", 3, "db", "Collected %d files.", #files)
return files
end
--- fontentry list -> filemap
generate_filedata = function (mappings)
- report ("both", 2, "db", "Creating filename map.")
+ logreport ("both", 2, "db", "Creating filename map.")
local nmappings = #mappings
@@ -2435,10 +2452,10 @@ generate_filedata = function (mappings)
if inbase then
local present = inbase [basename]
if present then
- report ("both", 4, "db",
- "Conflicting basename: %q already indexed \z
- in category %s, ignoring.",
- barename, location)
+ logreport ("both", 4, "db",
+ "Conflicting basename: %q already indexed \z
+ in category %s, ignoring.",
+ barename, location)
conflicts.basenames = conflicts.basenames + 1
--- track conflicts per font
@@ -2465,10 +2482,10 @@ generate_filedata = function (mappings)
if inbare then
local present = inbare [barename]
if present then
- report ("both", 4, "db",
- "Conflicting barename: %q already indexed \z
- in category %s/%s, ignoring.",
- barename, location, format)
+ logreport ("both", 4, "db",
+ "Conflicting barename: %q already indexed \z
+ in category %s/%s, ignoring.",
+ barename, location, format)
conflicts.barenames = conflicts.barenames + 1
--- track conflicts per font
@@ -2650,7 +2667,7 @@ end
collect_families = function (mappings)
- report ("info", 2, "db", "Analyzing families.")
+ logreport ("info", 2, "db", "Analyzing families.")
local families = {
["local"] = { },
@@ -2746,7 +2763,7 @@ local style_categories = { "r", "b", "i", "bi" }
local bold_categories = { "b", "bi" }
group_modifiers = function (mappings, families)
- report ("info", 2, "db", "Analyzing shapes, weights, and styles.")
+ logreport ("info", 2, "db", "Analyzing shapes, weights, and styles.")
for location, location_data in next, families do
for format, format_data in next, location_data do
for familyname, collected in next, format_data do
@@ -2845,7 +2862,7 @@ end
order_design_sizes = function (families)
- report ("info", 2, "db", "Ordering design sizes.")
+ logreport ("info", 2, "db", "Ordering design sizes.")
for location, data in next, families do
for format, data in next, data do
@@ -2870,7 +2887,7 @@ end
--- unit -> string * string list
local collect_font_filenames = function ()
- report ("info", 4, "db", "Scanning the filesystem for font files.")
+ logreport ("info", 4, "db", "Scanning the filesystem for font files.")
local filenames = { }
local bisect = config.luaotfload.misc.bisect
@@ -2900,7 +2917,7 @@ end
--- int -> string
local nth_font_filename = function (n)
- report ("info", 4, "db", "Picking font file no. %d.", n)
+ logreport ("info", 4, "db", "Picking font file no. %d.", n)
if not p_blacklist then
read_blacklist ()
end
@@ -2915,7 +2932,7 @@ end
--doc]]--
local font_slice = function (lo, hi)
- report ("info", 4, "db", "Retrieving font files nos. %d--%d.", lo, hi)
+ logreport ("info", 4, "db", "Retrieving font files nos. %d--%d.", lo, hi)
if not p_blacklist then
read_blacklist ()
end
@@ -2937,7 +2954,7 @@ end
--- unit -> int
local count_font_files = function ()
- report ("info", 4, "db", "Counting font files.")
+ logreport ("info", 4, "db", "Counting font files.")
if not p_blacklist then
read_blacklist ()
end
@@ -3063,31 +3080,31 @@ local collect_statistics = function (mappings)
itemlist = tableconcat (itemlist, ", ")
end
- report ("both", 0, "db",
- " · %4d × %s.",
- freq, itemlist)
+ logreport ("both", 0, "db",
+ " · %4d × %s.",
+ freq, itemlist)
end
end
- report ("both", 0, "", "~~~~ font index statistics ~~~~")
- report ("both", 0, "db",
- " · Collected %d fonts (%d names) in %d families.",
- #mappings, n_fullname, n_family)
+ logreport ("both", 0, "", "~~~~ font index statistics ~~~~")
+ logreport ("both", 0, "db",
+ " · Collected %d fonts (%d names) in %d families.",
+ #mappings, n_fullname, n_family)
pprint_top (families, 4, true)
- report ("both", 0, "db",
- " · %d different “subfamily” kinds.",
- setsize (subfamily))
+ logreport ("both", 0, "db",
+ " · %d different “subfamily” kinds.",
+ setsize (subfamily))
pprint_top (subfamily, 4)
- report ("both", 0, "db",
- " · %d different “prefmodifiers” kinds.",
- setsize (prefmodifiers))
+ logreport ("both", 0, "db",
+ " · %d different “prefmodifiers” kinds.",
+ setsize (prefmodifiers))
pprint_top (prefmodifiers, 4)
- report ("both", 0, "db",
- " · %d different “fontstyle_name” kinds.",
- setsize (fontstyle_name))
+ logreport ("both", 0, "db",
+ " · %d different “fontstyle_name” kinds.",
+ setsize (fontstyle_name))
pprint_top (fontstyle_name, 4)
end
@@ -3121,7 +3138,7 @@ update_names = function (currentnames, force, dry_run)
local conf = config.luaotfload
if conf.run.live ~= false and conf.db.update_live == false then
- report ("info", 2, "db", "Skipping database update.")
+ logreport ("info", 2, "db", "Skipping database update.")
--- skip all db updates
return currentnames or name_index
end
@@ -3133,15 +3150,16 @@ update_names = function (currentnames, force, dry_run)
- “targetnames” is the final table to return
- force is whether we rebuild it from scratch or not
]]
- report("both", 1, "db", "Updating the font names database"
- .. (force and " forcefully." or "."))
+ logreport ("both", 1, "db",
+ "Updating the font names database"
+ .. (force and " forcefully." or "."))
if config.luaotfload.db.skip_read == true then
--- the difference to a “dry run” is that we don’t search
--- for font files entirely. we also ignore the “force”
--- parameter since it concerns only the font files.
- report ("info", 2, "db",
- "Ignoring font files, reusing old data.")
+ logreport ("info", 2, "db",
+ "Ignoring font files, reusing old data.")
currentnames = load_names (false)
targetnames = currentnames
else
@@ -3152,8 +3170,9 @@ update_names = function (currentnames, force, dry_run)
currentnames = load_names (dry_run)
end
if currentnames.meta.version ~= names.version then
- report ("both", 1, "db", "No font names database or old "
- .. "one found; generating new one.")
+ logreport ("both", 1, "db",
+ "No font names database or old \z
+ one found; generating new one.")
currentnames = initialize_namedata (get_font_filter ())
end
end
@@ -3169,16 +3188,16 @@ update_names = function (currentnames, force, dry_run)
--- pass 2: read font files (normal case) or reuse information
--- present in index
- n_rem = count_removed (currentnames.files.full)
+ n_rem = count_removed (currentnames.files)
n_new = retrieve_namedata (font_filenames,
currentnames,
targetnames,
dry_run)
- report ("info", 3, "db",
- "Found %d font files; %d new, %d stale entries.",
- #font_filenames, n_new, n_rem)
+ logreport ("info", 3, "db",
+ "Found %d font files; %d new, %d stale entries.",
+ #font_filenames, n_new, n_rem)
end
--- pass 3 (optional): collect some stats about the raw font info
@@ -3204,27 +3223,27 @@ update_names = function (currentnames, force, dry_run)
--- pass 7: order design size tables
targetnames.families = order_design_sizes (targetnames.families)
- report ("info", 3, "db",
- "Rebuilt in %0.f ms.",
- 1000 * (osgettimeofday () - starttime))
+ logreport ("info", 3, "db",
+ "Rebuilt in %0.f ms.",
+ 1000 * (osgettimeofday () - starttime))
name_index = targetnames
if dry_run ~= true then
if n_new + n_rem == 0 then
- report ("info", 2, "db",
- "No new or removed fonts, skip saving to disk.")
+ logreport ("info", 2, "db",
+ "No new or removed fonts, skip saving to disk.")
else
local success, reason = save_names ()
if not success then
- report ("both", 0, "db",
- "Failed to save database to disk: %s",
- reason)
+ logreport ("both", 0, "db",
+ "Failed to save database to disk: %s",
+ reason)
end
end
if flush_lookup_cache () and save_lookups () then
- report ("both", 2, "cache", "Lookup cache emptied.")
+ logreport ("both", 2, "cache", "Lookup cache emptied.")
return targetnames
end
end
@@ -3241,18 +3260,18 @@ save_lookups = function ( )
caches.compile (lookup_cache, luaname, lucname)
--- double check ...
if lfsisfile (luaname) and lfsisfile (lucname) then
- report ("both", 3, "cache", "Lookup cache saved.")
+ logreport ("both", 3, "cache", "Lookup cache saved.")
return true
end
- report ("info", 0, "cache", "Could not compile lookup cache.")
+ logreport ("info", 0, "cache", "Could not compile lookup cache.")
return false
end
- report ("info", 0, "cache", "Lookup cache file not writable.")
+ logreport ("info", 0, "cache", "Lookup cache file not writable.")
if not fileiswritable (luaname) then
- report ("info", 0, "cache", "Failed to write %s.", luaname)
+ logreport ("info", 0, "cache", "Failed to write %s.", luaname)
end
if not fileiswritable (lucname) then
- report ("info", 0, "cache", "Failed to write %s.", lucname)
+ logreport ("info", 0, "cache", "Failed to write %s.", lucname)
end
return false
end
@@ -3281,33 +3300,33 @@ save_names = function (currentnames)
tabletofile (luaname, currentnames, true)
caches.compile (currentnames, luaname, lucname)
end
- report ("info", 2, "db", "Font index saved at ...")
+ logreport ("info", 2, "db", "Font index saved at ...")
local success = false
if lfsisfile (luaname) then
- report ("info", 2, "db", "Text: " .. luaname)
+ logreport ("info", 2, "db", "Text: " .. luaname)
success = true
end
if lfsisfile (gzname) then
- report ("info", 2, "db", "Gzip: " .. gzname)
+ logreport ("info", 2, "db", "Gzip: " .. gzname)
success = true
end
if lfsisfile (lucname) then
- report ("info", 2, "db", "Byte: " .. lucname)
+ logreport ("info", 2, "db", "Byte: " .. lucname)
success = true
end
if success then
return true
else
- report ("info", 0, "db", "Could not compile font index.")
+ logreport ("info", 0, "db", "Could not compile font index.")
return false
end
end
- report ("info", 0, "db", "Index file not writable")
+ logreport ("info", 0, "db", "Index file not writable")
if not fileiswritable (luaname) then
- report ("info", 0, "db", "Failed to write %s.", luaname)
+ logreport ("info", 0, "db", "Failed to write %s.", luaname)
end
if not fileiswritable (lucname) then
- report ("info", 0, "db", "Failed to write %s.", lucname)
+ logreport ("info", 0, "db", "Failed to write %s.", lucname)
end
return false
end
@@ -3321,7 +3340,7 @@ end
--- string -> string -> string list -> string list -> string list -> unit
local print_cache = function (category, path, luanames, lucnames, rest)
local report_indeed = function (...)
- report("info", 0, "cache", ...)
+ logreport ("info", 0, "cache", ...)
end
report_indeed("Luaotfload cache: %s", category)
report_indeed("location: %s", path)
@@ -3333,15 +3352,15 @@ end
--- string -> string -> string list -> bool -> bool
local purge_from_cache = function (category, path, list, all)
- report("info", 1, "cache", "Luaotfload cache: %s %s",
- (all and "erase" or "purge"), category)
- report("info", 1, "cache", "location: %s",path)
+ logreport ("info", 1, "cache", "Luaotfload cache: %s %s",
+ (all and "erase" or "purge"), category)
+ logreport ("info", 1, "cache", "location: %s", path)
local n = 0
for i=1,#list do
local filename = list[i]
if stringfind(filename,"luatex%-cache") then -- safeguard
if all then
- report("info", 5, "cache", "Removing %s.", filename)
+ logreport ("info", 5, "cache", "Removing %s.", filename)
osremove(filename)
n = n + 1
else
@@ -3350,7 +3369,7 @@ local purge_from_cache = function (category, path, list, all)
local checkname = file.replacesuffix(
filename, "lua", "luc")
if lfsisfile(checkname) then
- report("info", 5, "cache", "Removing %s.", filename)
+ logreport ("info", 5, "cache", "Removing %s.", filename)
osremove(filename)
n = n + 1
end
@@ -3358,7 +3377,7 @@ local purge_from_cache = function (category, path, list, all)
end
end
end
- report("info", 1, "cache", "Removed lua files : %i", n)
+ logreport ("info", 1, "cache", "Removed lua files : %i", n)
return true
end
@@ -3435,7 +3454,7 @@ local erase_cache = function ( )
end
local separator = function ( )
- report("info", 0, string.rep("-", 67))
+ logreport ("info", 0, string.rep("-", 67))
end
--- unit -> unit
@@ -3464,35 +3483,55 @@ end
--- export functionality to the namespace “fonts.names”
-----------------------------------------------------------------------
-names.set_font_filter = set_font_filter
-names.flush_lookup_cache = flush_lookup_cache
-names.save_lookups = save_lookups
-names.load = load_names
-names.access_font_index = access_font_index
-names.data = function () return name_index end
-names.save = save_names
-names.update = update_names
-names.lookup_font_file = lookup_font_file
-names.lookup_font_name = lookup_font_name
-names.lookup_font_name_cached = lookup_font_name_cached
-names.getfilename = lookup_fullpath
-names.lookup_fullpath = lookup_fullpath
-names.read_blacklist = read_blacklist
-names.sanitize_fontname = sanitize_fontname
-names.getmetadata = getmetadata
-names.set_location_precedence = set_location_precedence
-names.count_font_files = count_font_files
-names.nth_font_filename = nth_font_filename
-names.font_slice = font_slice
-
---- font cache
-names.purge_cache = purge_cache
-names.erase_cache = erase_cache
-names.show_cache = show_cache
-
-names.find_closest = find_closest
-
--- for testing purpose
-names.read_fonts_conf = read_fonts_conf
+local export = {
+ set_font_filter = set_font_filter,
+ flush_lookup_cache = flush_lookup_cache,
+ save_lookups = save_lookups,
+ load = load_names,
+ access_font_index = access_font_index,
+ data = function () return name_index end,
+ save = save_names,
+ update = update_names,
+ lookup_font_file = lookup_font_file,
+ lookup_font_name = lookup_font_name,
+ lookup_font_name_cached = lookup_font_name_cached,
+ getfilename = lookup_fullpath,
+ lookup_fullpath = lookup_fullpath,
+ read_blacklist = read_blacklist,
+ sanitize_fontname = sanitize_fontname,
+ getmetadata = getmetadata,
+ set_location_precedence = set_location_precedence,
+ count_font_files = count_font_files,
+ nth_font_filename = nth_font_filename,
+ font_slice = font_slice,
+ --- font cache
+ purge_cache = purge_cache,
+ erase_cache = erase_cache,
+ show_cache = show_cache,
+ find_closest = find_closest,
+ -- for testing purpose
+}
+
+return {
+ init = function ()
+ --- the font loader namespace is “fonts”, same as in Context
+ --- we need to put some fallbacks into place for when running
+ --- as a script
+ if not fonts then return false end
+ logreport = luaotfload.log.report
+ local fonts = fonts
+ fonts.names = fonts.names or names
+ fonts.formats = fonts.formats or { }
+ fonts.definers = fonts.definers or { resolvers = { } }
+
+ names.blacklist = blacklist
+ names.version = 2.51
+ names.data = nil --- contains the loaded database
+ names.lookups = nil --- contains the lookup cache
+
+ for sym, ref in next, export do names[sym] = ref end
+ return true
+ end
+}
-- vim:tw=71:sw=4:ts=4:expandtab
diff --git a/src/luaotfload-diagnostics.lua b/src/luaotfload-diagnostics.lua
index 80e461c..582105a 100644
--- a/src/luaotfload-diagnostics.lua
+++ b/src/luaotfload-diagnostics.lua
@@ -4,8 +4,6 @@
-- DESCRIPTION: functionality accessible by the --diagnose option
-- REQUIREMENTS: luaotfload-tool.lua
-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
--- VERSION: 2.5
--- MODIFIED: 2014-01-02 21:23:06+0100
-----------------------------------------------------------------------
--
local names = fonts.names
@@ -655,27 +653,27 @@ local diagnose = function (job)
you may sleep well.")
return true, false
end
- out ( [[===============================================
- WARNING
- ===============================================
+ out ( [[===============================================
+ WARNING
+ ===============================================
- The diagnostic detected %d errors.
+ The diagnostic detected %d errors.
- This version of luaotfload may have been
- tampered with. Modified versions of the
- luaotfload source are unsupported. Read the log
- carefully and get a clean version from CTAN or
- github:
+ This version of luaotfload may have been
+ tampered with. Modified versions of the
+ luaotfload source are unsupported. Read the log
+ carefully and get a clean version from CTAN or
+ github:
- × http://www.ctan.org/pkg/luaotfload
- × https://github.com/lualatex/luaotfload/releases
+ × http://www.ctan.org/pkg/luaotfload
+ × https://github.com/lualatex/luaotfload/releases
- If you are uncertain as to how to proceed, then
- ask on the lualatex mailing list:
+ If you are uncertain as to how to proceed, then
+ ask on the lualatex mailing list:
- http://www.tug.org/mailman/listinfo/lualatex-dev
+ http://www.tug.org/mailman/listinfo/lualatex-dev
- ===============================================
+ ===============================================
]], errcnt)
return true, false
end
diff --git a/src/luaotfload-features.lua b/src/luaotfload-features.lua
index 9b895ce..6fb2114 100644
--- a/src/luaotfload-features.lua
+++ b/src/luaotfload-features.lua
@@ -921,24 +921,13 @@ end
---[[ end included font-ltx.lua ]]
---[[doc--
-This uses the code from luatex-fonts-merged (<- font-otc.lua) instead
-of the removed luaotfload-font-otc.lua.
-
-TODO find out how far we get setting features without these lines,
-relying on luatex-fonts only (it *does* handle features somehow, after
-all).
---doc]]--
-
--- we assume that the other otf stuff is loaded already
+-- We assume that the other otf stuff is loaded already; though there’s
+-- another check below during the initialization phase.
---[[ begin snippet from font-otc.lua ]]
local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end)
local report_otf = logs.reporter("fonts","otf loading")
-local otf = fonts.handlers.otf
-local registerotffeature = otf.features.register
-
--[[HH--
In the userdata interface we can not longer tweak the loaded font as
@@ -960,7 +949,7 @@ setmetatableindex(types, function(t,k) t[k] = k return k end) -- "key"
local everywhere = { ["*"] = { ["*"] = true } } -- or: { ["*"] = { "*" } }
local noflags = { }
-local function addfeature(data,feature,specifications)
+local function addfeature (data, feature, specifications)
local descriptions = data.descriptions
local resources = data.resources
local lookups = resources.lookups
@@ -1100,26 +1089,9 @@ local function addfeature(data,feature,specifications)
end
end
-
-otf.enhancers.addfeature = addfeature
-
-local extrafeatures = { }
-
-function otf.addfeature(name,specification)
- extrafeatures[name] = specification
-end
-
-local function enhance(data,filename,raw)
- for feature, specification in next, extrafeatures do
- addfeature(data,feature,specification)
- end
-end
-
-otf.enhancers.register("check extra features",enhance)
-
---[[ end snippet from font-otc.lua ]]
-local tlig = {
+local tlig_specification = {
{
type = "substitution",
features = everywhere,
@@ -1167,9 +1139,6 @@ local tlig = {
},
}
-otf.addfeature ("tlig", tlig)
-otf.addfeature ("trep", { })
-
local anum_arabic = { --- these are the same as in font-otc
[0x0030] = 0x0660,
[0x0031] = 0x0661,
@@ -1228,11 +1197,45 @@ local anum_specification = {
},
}
-otf.addfeature ("anum", anum_specification)
+return {
+ init = function ()
+
+ if not fonts and fonts.handlers then
+ logreport ("log", 0, "color",
+ "OTF mechanisms missing -- did you forget to \z
+ load a font loader?")
+ return false
+ end
+
+ local otf = fonts.handlers.otf
-registerotffeature {
- name = "anum",
- description = "arabic digits",
+ local extrafeatures = {
+ tlig = tlig_specification,
+ trep = { },
+ anum = anum_specification,
+ }
+
+ otf.enhancers.register ("check extra features",
+ function (data,filename, raw)
+ for feature, specification in next, extrafeatures do
+ addfeature (data, feature, specification)
+ end
+ end)
+
+ logreport = luaotfload.log.report
+ if not fonts then
+ logreport ("log", 0, "color",
+ "OTF mechanisms missing -- did you forget to \z
+ load a font loader?")
+ return false
+ end
+
+ otf.features.register {
+ name = "anum",
+ description = "arabic digits",
+ }
+ return true
+ end
}
-- vim:tw=71:sw=4:ts=4:expandtab
diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index a493cc1..0f7464a 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -4,12 +4,11 @@
-- DESCRIPTION: Luaotfload font loader initialization
-- REQUIREMENTS: luatex v.0.80 or later; packages lualibs, luatexbase
-- AUTHOR: Philipp Gesang (Phg), <phg@phi-gamma.net>
--- VERSION: 1.0
--- CREATED: 2015-05-26 07:50:54+0200
-----------------------------------------------------------------------
--
local setmetatable = setmetatable
+local kpselookup = kpse.lookup
--[[doc--
@@ -38,8 +37,8 @@ local logreport --- filled in after loading the log module
--[[doc--
\subsection{Preparing the Font Loader}
- We treat the fontloader as a black box so behavior is consistent
- between formats.
+ We treat the fontloader as a semi-black box so behavior is
+ consistent between formats.
We load the fontloader code directly in the same fashion as the
Plain format \identifier{luatex-fonts} that is part of Context.
How this is executed depends on the presence on the
@@ -183,6 +182,78 @@ local pop_namespaces = function (normalglobal,
end
end
+--- below paths are relative to the texmf-context
+local ltx = "tex/generic/context/luatex"
+local ctx = "tex/context/base"
+
+local context_modules = {
+
+ --- Since 2.6 those are directly provided by the Lualibs package.
+ { false, "l-lua" },
+ { false, "l-lpeg" },
+ { false, "l-function" },
+ { false, "l-string" },
+ { false, "l-table" },
+ { false, "l-io" },
+ { false, "l-file" },
+ { false, "l-boolean" },
+ { false, "l-math" },
+ { false, "util-str" },
+
+ --- These constitute the fontloader proper.
+ { ltx, "luatex-basics-gen" },
+ { ctx, "data-con" },
+ { ltx, "luatex-basics-nod" },
+ { ctx, "font-ini" },
+ { ctx, "font-con" },
+ { ltx, "luatex-fonts-enc" },
+ { ctx, "font-cid" },
+ { ctx, "font-map" },
+ { ltx, "luatex-fonts-syn" },
+ { ltx, "luatex-fonts-tfm" },
+ { ctx, "font-oti" },
+ { ctx, "font-otf" },
+ { ctx, "font-otb" },
+ { ltx, "luatex-fonts-inj" }, --> since 2014-01-07, replaces node-inj.lua
+ { ltx, "luatex-fonts-ota" },
+ { ltx, "luatex-fonts-otn" }, --> since 2014-01-07, replaces font-otn.lua
+ { ctx, "font-otp" }, --> since 2013-04-23
+ { ltx, "luatex-fonts-lua" },
+ { ctx, "font-def" },
+ { ltx, "luatex-fonts-def" },
+ { ltx, "luatex-fonts-ext" },
+ { ltx, "luatex-fonts-cbk" },
+
+} --[[context_modules]]
+
+local load_context_modules = function (pth)
+
+ local load_module = luaotfload.loaders.context
+ local ignore_module = luaotfload.loaders.ignore
+
+ logreport ("both", 2, "init",
+ "Loading fontloader components from context.")
+ local n = #context_modules
+ for i = 1, n do
+ local sub, spec = unpack (context_modules [i])
+ if sub == false then
+ ignore_module (spec)
+ elseif type (sub) == "string" then
+ if pth then
+ load_module (spec, file.join (pth, sub))
+ else
+ load_module (spec)
+ end
+ else
+ logreport ("both", 0, "init",
+ "Internal error, please report. \z
+ This is not your fault.")
+ os.exit (-1)
+ end
+ end
+
+end
+
local init_adapt = function ()
local context_environment = { }
@@ -205,6 +276,7 @@ end --- [init_adapt]
local init_main = function ()
local load_fontloader_module = luaotfload.loaders.fontloader
+ local ignore_module = luaotfload.loaders.ignore
--[[doc--
@@ -216,49 +288,103 @@ local init_main = function ()
--doc]]--
- load_fontloader_module (luaotfload.fontloader_package)
+ local fontloader = config.luaotfload and config.luaotfload.run.fontloader
+ or "reference"
+ fontloader = tostring (fontloader)
- ---load_fontloader_module "font-odv.lua" --- <= Devanagari support from Context
+ if fontloader == "reference" or fontloader == "default" then
+ logreport ("log", 4, "init", "Using reference fontloader.")
+ load_fontloader_module (luaotfload.fontloader_package)
- if not fonts then
- --- the loading sequence is known to change, so this might have to
- --- be updated with future updates!
- --- do not modify it though unless there is a change to the merged
- --- package!
- load_fontloader_module "l-lua"
- load_fontloader_module "l-lpeg"
- load_fontloader_module "l-function"
- load_fontloader_module "l-string"
- load_fontloader_module "l-table"
- load_fontloader_module "l-io"
- load_fontloader_module "l-file"
- load_fontloader_module "l-boolean"
- load_fontloader_module "l-math"
- load_fontloader_module "util-str"
- load_fontloader_module "luatex-basics-gen"
+ elseif fontloader == "unpackaged" then
+ logreport ("both", 4, "init",
+ "Loading fontloader components individually.")
+ --- The loading sequence is known to change, so this might have to be
+ --- updated with future updates. Do not modify it though unless there is
+ --- a change to the upstream package!
+
+ --- Since 2.6 those are directly provided by the Lualibs package.
+ ignore_module "l-lua"
+ ignore_module "l-lpeg"
+ ignore_module "l-function"
+ ignore_module "l-string"
+ ignore_module "l-table"
+ ignore_module "l-io"
+ ignore_module "l-file"
+ ignore_module "l-boolean"
+ ignore_module "l-math"
+ ignore_module "util-str"
+ ignore_module "luatex-basics-gen"
+
+ --- These constitute the fontloader proper.
load_fontloader_module "data-con"
- load_fontloader_module "luatex-basics-nod"
+ load_fontloader_module "basics-nod"
load_fontloader_module "font-ini"
load_fontloader_module "font-con"
- load_fontloader_module "luatex-fonts-enc"
+ load_fontloader_module "fonts-enc"
load_fontloader_module "font-cid"
load_fontloader_module "font-map"
- load_fontloader_module "luatex-fonts-syn"
- load_fontloader_module "luatex-fonts-tfm"
+ load_fontloader_module "fonts-syn"
+ load_fontloader_module "fonts-tfm"
load_fontloader_module "font-oti"
load_fontloader_module "font-otf"
load_fontloader_module "font-otb"
- load_fontloader_module "luatex-fonts-inj" --> since 2014-01-07, replaces node-inj.lua
- load_fontloader_module "luatex-fonts-ota"
- load_fontloader_module "luatex-fonts-otn" --> since 2014-01-07, replaces font-otn.lua
- load_fontloader_module "font-otp" --> since 2013-04-23
- load_fontloader_module "luatex-fonts-lua"
+ load_fontloader_module "fonts-inj" --> since 2014-01-07, replaces node-inj.lua
+ load_fontloader_module "fonts-ota"
+ load_fontloader_module "fonts-otn" --> since 2014-01-07, replaces font-otn.lua
+ load_fontloader_module "font-otp" --> since 2013-04-23
+ load_fontloader_module "fonts-lua"
load_fontloader_module "font-def"
- load_fontloader_module "luatex-fonts-def"
- load_fontloader_module "luatex-fonts-ext"
- load_fontloader_module "luatex-fonts-cbk"
- end --- non-merge fallback scope
+ load_fontloader_module "fonts-def"
+ load_fontloader_module "fonts-ext"
+ load_fontloader_module "fonts-cbk"
+
+ elseif fontloader == "context" then
+ logreport ("both", 2, "init",
+ "Attempting to load Context modules in lookup path.")
+ load_context_modules ()
+
+ elseif lfs.isdir (fontloader) then
+ logreport ("both", 2, "init",
+ "Attempting to load Context files under prefix “%s”.",
+ fontloader)
+ load_context_modules (fontloader)
+
+ elseif lfs.isfile (fontloader) then
+ logreport ("both", 2, "init",
+ "Attempting to load fontloader from absolute path “%s”.",
+ fontloader)
+ local _void = require (fontloader)
+
+ elseif kpselookup (fontloader) then
+ local pth = kpselookup (fontloader)
+ logreport ("both", 2, "init",
+ "Attempting to load fontloader “%s” from kpse-resolved path “%s”.",
+ fontloader, path)
+ local _void = require (path)
+
+ elseif fontloader then
+ logreport ("log", 4, "init",
+ "Using predefined fontloader “%s”.",
+ fontloader)
+ load_fontloader_module (fontloader)
+
+ else
+ logreport ("log", 4, "init",
+ "No match for requested fontloader “%s”.",
+ fontloader)
+ fontloader = luaotfload.fontloader_package
+ logreport ("log", 4, "init",
+ "Defaulting to predefined fontloader “%s”.",
+ fontloader)
+ load_fontloader_module (fontloader)
+ end
+
+ ---load_fontloader_module "font-odv.lua" --- <= Devanagari support from Context
+ logreport ("both", 0, "init",
+ "Context OpenType loader version “%s”",
+ fonts.handlers.otf.version)
end --- [init_main]
local init_cleanup = function (store)
@@ -431,6 +557,8 @@ return {
os.gettimeofday() - starttime)
local n = init_post ()
logreport ("both", 5, "init", "post hook terminated, %d actions performed", n)
+ return true
end
}
+-- vim:tw=79:sw=2:ts=2:expandtab
diff --git a/src/luaotfload-letterspace.lua b/src/luaotfload-letterspace.lua
index 9a0646b..ab81881 100644
--- a/src/luaotfload-letterspace.lua
+++ b/src/luaotfload-letterspace.lua
@@ -27,6 +27,7 @@ local getnext = nodedirect.getnext
local getprev = nodedirect.getprev
local getfield = nodedirect.getfield
local setfield = nodedirect.setfield
+local getsubtype = nodedirect.getsubtype
local find_node_tail = nodedirect.tail
local todirect = nodedirect.tonut
local tonode = nodedirect.tonode
diff --git a/src/luaotfload-loaders.lua b/src/luaotfload-loaders.lua
index 901d4d8..38062f6 100644
--- a/src/luaotfload-loaders.lua
+++ b/src/luaotfload-loaders.lua
@@ -1,34 +1,141 @@
-if not modules then modules = { } end modules ["loaders"] = {
- version = "2.5",
- comment = "companion to luaotfload-main.lua",
- author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
+#!/usr/bin/env texlua
+-----------------------------------------------------------------------
+-- FILE: luaotfload-loaders.lua
+-- DESCRIPTION: Luaotfload callback handling
+-- REQUIREMENTS: luatex v.0.80 or later; packages lualibs, luatexbase
+-- AUTHOR: Philipp Gesang (Phg), <phg@phi-gamma.net>, Hans Hagen, Khaled Hosny, Elie Roux
+-----------------------------------------------------------------------
+--
+--- Contains parts of the earlier main script.
+
+if not lualibs then error "this module requires Luaotfload" end
+if not luaotfload then error "this module requires Luaotfload" end
+
+local logreport = luaotfload.log and luaotfload.log.report or print
+
+local install_formats = function ()
+ local fonts = fonts
+ if not fonts then return false end
+
+ local readers = fonts.readers
+ local handlers = fonts.handlers
+ local formats = fonts.formats
+ if not readers or not handlers or not formats then return false end
-local fonts = fonts
-local readers = fonts.readers
-local handlers = fonts.handlers
-local formats = fonts.formats
-
-local pfb_reader = function (specification)
- return readers.opentype (specification, "pfb", "type1")
-end
-
-local pfa_reader = function (specification)
- return readers.opentype (specification, "pfa", "type1")
+ local aux = function (which, reader)
+ if not which or type (which) ~= "string"
+ or not reader or type (reader) ~= "function" then
+ logreport ("both", 2, "loaders", "Error installing reader for “%s”.", which)
+ return false
+ end
+ formats [which] = "type1"
+ readers [which] = reader
+ handlers [which] = { }
+ return true
+ end
+
+ return aux ("pfa", function (spec) return readers.opentype (spec, "pfa", "type1") end)
+ and aux ("pfb", function (spec) return readers.opentype (spec, "pfb", "type1") end)
+ and aux ("ofm", readers.tfm)
end
-formats.pfa = "type1"
-readers.pfa = pfa_reader
-handlers.pfa = { }
+--[[doc--
+
+ \subsection{\CONTEXT override}
+ \label{define-font}
+ We provide a simplified version of the original font definition
+ callback.
+
+--doc]]--
+
+
+local definers --- (string, spec -> size -> id -> tmfdata) hash_t
+do
+ local read = fonts.definers.read
-formats.pfb = "type1"
-readers.pfb = pfb_reader
-handlers.pfb = { }
+ local patch = function (specification, size, id)
+ local fontdata = read (specification, size, id)
+ if type (fontdata) == "table" and fontdata.shared then
+ --- We need to test for the “shared” field here
+ --- or else the fontspec capheight callback will
+ --- operate on tfm fonts.
+ luatexbase.call_callback ("luaotfload.patch_font", fontdata, specification)
+ else
+ luatexbase.call_callback ("luaotfload.patch_font_unsafe", fontdata, specification)
+ end
+ return fontdata
+ end
-formats.ofm = "type1"
-readers.ofm = readers.tfm
-handlers.ofm = { }
+ local mk_info = function (name)
+ local definer = name == "patch" and patch or read
+ return function (specification, size, id)
+ logreport ("both", 0, "loaders", "defining font no. %d", id)
+ logreport ("both", 0, "loaders", " > active font definer: %q", name)
+ logreport ("both", 0, "loaders", " > spec %q", specification)
+ logreport ("both", 0, "loaders", " > at size %.2f pt", size / 2^16)
+ local result = definer (specification, size, id)
+ if not result then
+ logreport ("both", 0, "loaders", " > font definition failed")
+ return
+ elseif type (result) == "number" then
+ logreport ("both", 0, "loaders", " > font definition yielded id %d", result)
+ return result
+ end
+ logreport ("both", 0, "loaders", " > font definition successful")
+ logreport ("both", 0, "loaders", " > name %q", result.name or "<nil>")
+ logreport ("both", 0, "loaders", " > fontname %q", result.fontname or "<nil>")
+ logreport ("both", 0, "loaders", " > fullname %q", result.fullname or "<nil>")
+ return result
+ end
+ end
--- vim:tw=71:sw=2:ts=2:expandtab
+ definers = {
+ patch = patch,
+ generic = read,
+ info_patch = mk_info "patch",
+ info_generic = mk_info "generic",
+ }
+end
+
+--[[doc--
+
+ We create callbacks for patching fonts on the fly, to be used by
+ other packages. In addition to the regular \identifier{patch_font}
+ callback there is an unsafe variant \identifier{patch_font_unsafe}
+ that will be invoked even if the target font lacks certain essential
+ tfmdata tables.
+
+ The callbacks initially contain the empty function that we are going
+ to override below.
+
+--doc]]--
+
+local install_callbacks = function ()
+ local create_callback = luatexbase.create_callback
+ local dummy_function = function () end
+ create_callback ("luaotfload.patch_font", "simple", dummy_function)
+ create_callback ("luaotfload.patch_font_unsafe", "simple", dummy_function)
+ luatexbase.reset_callback "define_font"
+ local definer = config.luaotfload.run.definer
+ luatexbase.add_to_callback ("define_font",
+ definers[definer or "patch"],
+ "luaotfload.define_font",
+ 1)
+ return true
+end
+
+return {
+ init = function ()
+ local ret = true
+ if not install_formats () then
+ logreport ("log", 0, "loaders", "Error initializing OFM/PF{A,B} loaders.")
+ ret = false
+ end
+ if not install_callbacks () then
+ logreport ("log", 0, "loaders", "Error installing font loader callbacks.")
+ ret = false
+ end
+ return ret
+ end
+}
+-- vim:tw=79:sw=2:ts=2:expandtab
diff --git a/src/luaotfload-log.lua b/src/luaotfload-log.lua
index 7c012f4..e5db310 100644
--- a/src/luaotfload-log.lua
+++ b/src/luaotfload-log.lua
@@ -91,7 +91,7 @@ local set_logout = function (s, finalizers)
logout = "redirect"
local chan = choose_logfile ()
chan:write (stringformat ("logging initiated at %s",
- osdate ("%Y-%m-%d %h:%m:%s", --- i. e. osdate "%F %T"
+ osdate ("%Y-%m-%d %H:%M:%S", --- i. e. osdate "%F %T"
ostime ())))
local writefile = function (...)
if select ("#", ...) == 2 then
@@ -118,7 +118,7 @@ local set_logout = function (s, finalizers)
finalizers[#finalizers+1] = function ()
chan:write (stringformat ("\nlogging finished at %s\n",
- osdate ("%Y-%m-%d %h:%m:%s", --- i. e. osdate "%F %T"
+ osdate ("%Y-%m-%d %H:%M:%S", --- i. e. osdate "%F %T"
ostime ())))
chan:close ()
texiowrite = texio.write
diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 919e343..17d06d4 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -3,23 +3,17 @@
-- DESCRIPTION: Luaotfload entry point
-- REQUIREMENTS: luatex v.0.80 or later; packages lualibs, luatexbase
-- AUTHOR: Élie Roux, Khaled Hosny, Philipp Gesang
--- VERSION: same as Luaotfload
--- MODIFIED: 2015-06-09 23:08:18+0200
-----------------------------------------------------------------------
--
---- Note:
---- This file was part of the original luaotfload.dtx and has been
---- converted to a pure Lua file during the transition from Luaotfload
---- version 2.4 to 2.5. Thus, the comments are still in TeX (Latex)
---- markup.
-local initial_log_level = 0
+local osgettimeofday = os.gettimeofday
+config = config or { }
luaotfload = luaotfload or { }
local luaotfload = luaotfload
luaotfload.log = luaotfload.log or { }
luaotfload.version = "2.6"
luaotfload.loaders = { }
-luaotfload.min_luatex_version = 79 --- i. e. 0.79
+luaotfload.min_luatex_version = 80 --- i. e. 0.79
luaotfload.fontloader_package = "reference" --- default: from current Context
local authors = "\z
@@ -36,7 +30,7 @@ local authors = "\z
luaotfload.module = {
name = "luaotfload-main",
version = 2.60001,
- date = "2015/05/26",
+ date = "2015/11/05",
description = "OpenType layout system.",
author = authors,
copyright = authors,
@@ -67,23 +61,10 @@ luaotfload.module = {
local luatexbase = luatexbase
local require = require
local type = type
-local add_to_callback = luatexbase.add_to_callback
-local create_callback = luatexbase.create_callback
-local reset_callback = luatexbase.reset_callback
-local call_callback = luatexbase.call_callback
-local dummy_function = function () end --- XXX this will be moved to the luaotfload namespace when we have the init module
-
-local error, warning, info, log =
+local _error, _warning, _info, _log =
luatexbase.provides_module(luaotfload.module)
-luaotfload.log.tex = {
- error = error,
- warning = warning,
- info = info,
- log = log,
-}
-
--[[doc--
We set the minimum version requirement for \LUATEX to v0.76,
@@ -117,155 +98,165 @@ end
--doc]]--
local make_loader_name = function (prefix, name)
- local msg = luaotfload.log and luaotfload.log.report or print
- if prefix then
- msg ("log", 7, "load",
- "Composing fontloader name from constitutents %s, %s",
- prefix, name)
- return prefix .. "-" .. name .. ".lua"
+ local msg = luaotfload.log and luaotfload.log.report
+ or function (...) texio.write_nl ("log", ...) end
+ if not name then
+ msg ("both", 0, "load",
+ "Fatal error: make_loader_name (“%s”, “%s”).",
+ tostring (prefix), tostring (name))
+ return "dummy-name"
end
- msg ("log", 7, "load",
- "Loading fontloader file %s literally.",
- name)
- return name
+ name = tostring (name)
+ if prefix == false then
+ msg ("log", 9, "load",
+ "No prefix requested, passing module name “%s” unmodified.",
+ name)
+ return tostring (name) .. ".lua"
+ end
+ prefix = tostring (prefix)
+ msg ("log", 9, "load",
+ "Composing module name from constituents %s, %s.",
+ prefix, name)
+ return prefix .. "-" .. name .. ".lua"
end
+local timing_info = {
+ t_load = { },
+ t_init = { },
+}
+
local make_loader = function (prefix)
return function (name)
+ local t_0 = osgettimeofday ()
local modname = make_loader_name (prefix, name)
- return require (modname)
+ local data = require (modname)
+ local t_end = osgettimeofday ()
+ timing_info.t_load [name] = t_end - t_0
+ return data
end
end
-local load_luaotfload_module = make_loader "luaotfload"
------ load_luaotfload_module = make_loader "luatex" --=> for Luatex-Plain
-local load_fontloader_module = make_loader "fontloader"
-
-luaotfload.loaders.luaotfload = load_luaotfload_module
-luaotfload.loaders.fontloader = load_fontloader_module
-
-luaotfload.init = load_luaotfload_module "init" --- fontloader initialization
-
-local store = luaotfload.init.early ()
-local log = luaotfload.log
-local logreport = log.report
-
--[[doc--
-
- Now we load the modules written for \identifier{luaotfload}.
-
+ Certain files are kept around that aren’t loaded because they are part of
+ the imported fontloader. In order to keep the initialization structure
+ intact we also provide a no-op version of the module loader that can be
+ called in the expected places.
--doc]]--
-load_luaotfload_module "parsers" --- fonts.conf and syntax
-load_luaotfload_module "configuration" --- configuration options
-
-if not config.actions.apply_defaults () then
- logreport ("log", 0, "load", "Configuration unsuccessful.")
+local dummy_loader = function (name)
+ luaotfload.log.report ("log", 3, "load",
+ "Skipping module “%s” on purpose.",
+ name)
end
-luaotfload.init.main (store)
-
-load_luaotfload_module "loaders" --- Type1 font wrappers
-load_luaotfload_module "database" --- Font management.
-load_luaotfload_module "colors" --- Per-font colors.
-
-luaotfload.resolvers = load_luaotfload_module "resolvers" --- Font lookup
-
-luaotfload.resolvers.install ()
-
-if not config.actions.reconfigure () then
- logreport ("log", 0, "load", "Post-configuration hooks failed.")
-end
-
---[[doc--
-
- We create callbacks for patching fonts on the fly, to be used by
- other packages. In addition to the regular \identifier{patch_font}
- callback there is an unsafe variant \identifier{patch_font_unsafe}
- that will be invoked even if the target font lacks certain essential
- tfmdata tables.
-
- The callbacks initially contain the empty function that we are going to
- override below.
-
---doc]]--
-
-create_callback("luaotfload.patch_font", "simple", dummy_function)
-create_callback("luaotfload.patch_font_unsafe", "simple", dummy_function)
-
---[[doc--
-
- \subsection{\CONTEXT override}
- \label{define-font}
- We provide a simplified version of the original font definition
- callback.
-
---doc]]--
-
-
-local definers = { } --- (string, spec -> size -> id -> tmfdata) hash_t
-do
- local read = fonts.definers.read
-
- local patch = function (specification, size, id)
- local fontdata = read (specification, size, id)
- if type (fontdata) == "table" and fontdata.shared then
- --- We need to test for the “shared” field here
- --- or else the fontspec capheight callback will
- --- operate on tfm fonts.
- call_callback ("luaotfload.patch_font", fontdata, specification)
+local context_loader = function (name, path)
+ luaotfload.log.report ("log", 3, "load",
+ "Loading module “%s” from Context.",
+ name)
+ local t_0 = osgettimeofday ()
+ local modname = make_loader_name (false, name)
+ local modpath = modname
+ if path then
+ if lfs.isdir (path) then
+ luaotfload.log.report ("log", 3, "load",
+ "Prepending path “%s”.",
+ path)
+ modpath = file.join (path, modname)
else
- call_callback ("luaotfload.patch_font_unsafe", fontdata, specification)
+ luaotfload.log.report ("both", 0, "load",
+ "Non-existant path “%s” specified, ignoring.",
+ path)
end
- return fontdata
end
+ local ret = require (modpath)
+ local t_end = osgettimeofday ()
+ timing_info.t_load [name] = t_end - t_0
+
+ if ret ~= true then
+ --- require () returns “true” upon success unless the loaded file
+ --- yields a non-zero exit code. This isn’t per se indicating that
+ --- something isn’t right, but against HH’s coding practices. We’ll
+ --- silently ignore this ever happening on lower log levels.
+ luaotfload.log.report ("log", 4, "load",
+ "Module “%s” returned “%s”.", ret)
+ end
+ return ret
+end
- local mk_info = function (name)
- local definer = name == "patch" and patch or read
- return function (specification, size, id)
- logreport ("both", 0, "main", "defining font no. %d", id)
- logreport ("both", 0, "main", " > active font definer: %q", name)
- logreport ("both", 0, "main", " > spec %q", specification)
- logreport ("both", 0, "main", " > at size %.2f pt", size / 2^16)
- local result = definer (specification, size, id)
- if not result then
- logreport ("both", 0, "main", " > font definition failed")
- return
- elseif type (result) == "number" then
- logreport ("both", 0, "main", " > font definition yielded id %d", result)
- return result
+local install_loaders = function ()
+ local loaders = { }
+ local loadmodule = make_loader "luaotfload"
+ loaders.luaotfload = loadmodule
+ loaders.fontloader = make_loader "fontloader"
+ loaders.context = context_loader
+ loaders.ignore = dummy_loader
+----loaders.plaintex = make_loader "luatex" --=> for Luatex-Plain
+
+ loaders.initialize = function (name)
+ local tmp = loadmodule (name)
+ local logreport = luaotfload.log.report
+ if type (tmp) == "table" then
+ local init = tmp.init
+ if init and type (init) == "function" then
+ local t_0 = osgettimeofday ()
+ if not init () then
+ logreport ("log", 0, "load",
+ "Failed to load module “%s”.", name)
+ return
+ end
+ local t_end = osgettimeofday ()
+ local d_t = t_end - t_0
+ logreport ("log", 4, "load",
+ "Module “%s” loaded in %d ms.",
+ name, d_t)
+ timing_info.t_init [name] = d_t
end
- logreport ("both", 0, "main", " > font definition successful")
- logreport ("both", 0, "main", " > name %q", result.name or "<nil>")
- logreport ("both", 0, "main", " > fontname %q", result.fontname or "<nil>")
- logreport ("both", 0, "main", " > fullname %q", result.fullname or "<nil>")
- return result
end
end
- definers.patch = patch
- definers.generic = read
- definers.info_patch = mk_info "patch"
- definers.info_generic = mk_info "generic"
+ return loaders
end
-if not reset_callback == nil then
- reset_callback "define_font"
-end
+luaotfload.main = function ()
---[[doc--
+ luaotfload.loaders = install_loaders ()
+ local loaders = luaotfload.loaders
+ local loadmodule = loaders.luaotfload
+ local initialize = loaders.initialize
- Finally we register the callbacks.
+ local starttime = osgettimeofday ()
+ local init = loadmodule "init" --- fontloader initialization
+ local store = init.early () --- injects the log module too
+ local logreport = luaotfload.log.report
---doc]]--
+ initialize "parsers" --- fonts.conf and syntax
+ initialize "configuration" --- configuration options
+
+ if not init.main (store) then
+ logreport ("log", 0, "load", "Main fontloader initialization failed.")
+ end
-local definer = config.luaotfload.run.definer
-add_to_callback ("define_font", definers[definer], "luaotfload.define_font", 1)
+ initialize "loaders" --- Font loading; callbacks
+ initialize "database" --- Font management.
+ initialize "colors" --- Per-font colors.
-load_luaotfload_module "features" --- font request and feature handling
-load_luaotfload_module "letterspace" --- extra character kerning
-load_luaotfload_module "auxiliary" --- additional high-level functionality
+ luaotfload.resolvers = loadmodule "resolvers" --- Font lookup
+ luaotfload.resolvers.init ()
-luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec
+ if not config.actions.reconfigure () then
+ logreport ("log", 0, "load", "Post-configuration hooks failed.")
+ end
+
+ initialize "features" --- font request and feature handling
+ loadmodule "letterspace" --- extra character kerning
+ loadmodule "auxiliary" --- additional high-level functionality
+
+ luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec
+
+ logreport ("both", 0, "main",
+ "initialization completed in %0.3f seconds",
+ osgettimeofday() - starttime)
+----inspect (timing_info)
+end
-- vim:tw=79:sw=4:ts=4:et
diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua
index a52b5d4..0349cdc 100644
--- a/src/luaotfload-parsers.lua
+++ b/src/luaotfload-parsers.lua
@@ -2,26 +2,12 @@
-------------------------------------------------------------------------------
-- FILE: luaotfload-parsers.lua
-- DESCRIPTION: various lpeg-based parsers used in Luaotfload
--- REQUIREMENTS: Luaotfload > 2.4
--- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
--- VERSION: same as Luaotfload
--- CREATED: 2014-01-14 10:15:20+0100
+-- REQUIREMENTS: Luaotfload >= 2.6
+-- AUTHOR: Philipp Gesang (Phg), <phg@phi-gamma.net>
-------------------------------------------------------------------------------
--
-if not modules then modules = { } end modules ['luaotfload-parsers'] = {
- version = "2.5",
- comment = "companion to luaotfload-main.lua",
- author = "Philipp Gesang",
- copyright = "Luaotfload Development Team",
- license = "GNU GPL v2.0"
-}
-
-luaotfload = luaotfload or { }
-luaotfload.parsers = luaotfload.parsers or { }
-local parsers = luaotfload.parsers
-parsers.traversal_maxdepth = 42 --- prevent stack overflows
-local traversal_maxdepth = parsers.traversal_maxdepth --- TODO could be an option
+local traversal_maxdepth = 42 --- prevent stack overflows
local rawset = rawset
@@ -42,8 +28,7 @@ local filedirname = file.dirname
local io = io
local ioopen = io.open
-local log = luaotfload.log
-local logreport = log.report
+local logreport = print
local string = string
local stringsub = string.sub
@@ -399,10 +384,6 @@ local read_fonts_conf = function (path_list, find_files)
return acc
end
-luaotfload.parsers.read_fonts_conf = read_fonts_conf
-
-
-
-------------------------------------------------------------------------------
--- MISC PARSERS
-------------------------------------------------------------------------------
@@ -410,10 +391,8 @@ luaotfload.parsers.read_fonts_conf = read_fonts_conf
local trailingslashes = slash^1 * P(-1)
local stripslashes = C((1 - trailingslashes)^0)
-parsers.stripslashes = stripslashes
local splitcomma = Ct((C(noncomma^1) + comma)^1)
-parsers.splitcomma = splitcomma
@@ -653,8 +632,6 @@ local font_request = Ct(path_lookup * (colon^-1 * features)^-1
--- v2.5 parser: 1065 rules
--- v1.2 parser: 230 rules
-luaotfload.parsers.font_request = font_request
-
-------------------------------------------------------------------------------
--- INI FILES
-------------------------------------------------------------------------------
@@ -731,7 +708,7 @@ local ini_variables = Cg (Cf (Ct "" * ini_variable^0, rawset), "variables")
local ini_section = Ct (ini_heading * ini_variables)
local ini_sections = skip_line^0 * ini_section^0
-local config = Ct (ini_sections)
+local parse_config = Ct (ini_sections)
--[=[doc--
@@ -763,6 +740,22 @@ local config = Ct (ini_sections)
--doc]=]--
-luaotfload.parsers.config = config
+return {
+ init = function ()
+ logreport = luaotfload.log.report
+ luaotfload.parsers = {
+ --- parameters
+ traversal_maxdepth = traversal_maxdepth,
+ --- main parsers
+ read_fonts_conf = read_fonts_conf,
+ font_request = font_request,
+ config = parse_config,
+ --- common patterns
+ stripslashes = stripslashes,
+ splitcomma = splitcomma,
+ }
+ return true
+ end
+}
-- vim:ft=lua:tw=71:et:sw=2:sts=4:ts=8
diff --git a/src/luaotfload-resolvers.lua b/src/luaotfload-resolvers.lua
index 42ea2fd..3d7f6b0 100644
--- a/src/luaotfload-resolvers.lua
+++ b/src/luaotfload-resolvers.lua
@@ -5,8 +5,6 @@
-- DESCRIPTION: Resolvers for hooking into the fontloader
-- REQUIREMENTS: Luaotfload and a decent bit of courage
-- AUTHOR: Philipp Gesang (Phg), <phg@phi-gamma.net>
--- VERSION: 1.0
--- CREATED: 2015-07-23 07:31:50+0200
-----------------------------------------------------------------------
--
--- The bare fontloader uses a set of simplistic file name resolvers
@@ -37,15 +35,9 @@ local stringlower = string.lower
local stringformat = string.format
local filesuffix = file.suffix
local fileremovesuffix = file.removesuffix
-local formats = fonts.formats
-local names = fonts.names
-local encodings = fonts.encodings
local luatexbase = luatexbase
local logreport = luaotfload.log.report
-formats.ofm = "type1"
-encodings.known = encodings.known or { }
-
--[[doc--
\identifier{luaotfload} promises easy access to system fonts.
@@ -70,9 +62,9 @@ encodings.known = encodings.known or { }
local resolve_file
resolve_file = function (specification)
- local name = names.lookup_font_file (specification.name)
+ local name = fonts.names.lookup_font_file (specification.name)
local suffix = filesuffix (name)
- if formats[suffix] then
+ if fonts.formats[suffix] then
specification.forced = stringlower (suffix)
specification.forcedname = fileremovesuffix(name)
else
@@ -101,7 +93,7 @@ resolve_path = function (specification)
resolve_file (specification)
else
local suffix = filesuffix (name)
- if formats[suffix] then
+ if fonts.formats[suffix] then
specification.forced = stringlower (suffix)
specification.name = fileremovesuffix(name)
specification.forcedname = name
@@ -122,9 +114,9 @@ end
local resolve_name
resolve_name = function (specification)
- local resolver = names.lookup_font_name_cached
+ local resolver = fonts.names.lookup_font_name_cached
if config.luaotfload.run.resolver == "normal" then
- resolver = names.lookup_font_name
+ resolver = fonts.names.lookup_font_name
end
local resolved, subfont = resolver (specification)
if resolved then
@@ -210,7 +202,7 @@ local resolve_kpse
resolve_kpse = function (specification)
local name = specification.name
local suffix = filesuffix (name)
- if suffix and formats[suffix] then
+ if suffix and fonts.formats[suffix] then
name = fileremovesuffix (name)
if resolvers.findfile (name, suffix) then
specification.forced = stringlower (suffix)
@@ -218,7 +210,7 @@ resolve_kpse = function (specification)
return
end
end
- for t, format in next, formats do --- brute force
+ for t, format in next, fonts.formats do --- brute force
if kpsefind_file (name, format) then
specification.forced = t
specification.name = name
@@ -238,8 +230,11 @@ local resolve_my = function (specification)
end
return {
- install = function ( )
- luatexbase.create_callback ("luaotfload.resolve_font", "simple", function () end)
+ init = function ( )
+ if luatexbase and luatexbase.create_callback then
+ luatexbase.create_callback ("luaotfload.resolve_font",
+ "simple", function () end)
+ end
logreport ("log", 5, "resolvers", "installing font resolvers", name)
local request_resolvers = fonts.definers.resolvers
request_resolvers.file = resolve_file
@@ -248,8 +243,11 @@ return {
request_resolvers.path = resolve_path
request_resolvers.kpse = resolve_kpse
request_resolvers.my = resolve_my
+ fonts.formats.ofm = "type1"
+ fonts.encodings = fonts.encodings or { }
+ fonts.encodings.known = fonts.encodings.known or { }
return true
- end, --- [.install]
+ end, --- [.init]
}
--- vim:ft=lua:ts=8:sw=4:et
diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua
index 4376e90..e9a434a 100755
--- a/src/luaotfload-tool.lua
+++ b/src/luaotfload-tool.lua
@@ -4,9 +4,7 @@
-- DESCRIPTION: database functionality
-- REQUIREMENTS: luaotfload 2.6
-- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang
--- VERSION: 2.6
-- LICENSE: GPL v2.0
--- MODIFIED: 2015-03-29 12:43:00+0200
-----------------------------------------------------------------------
luaotfload = luaotfload or { }
@@ -76,7 +74,6 @@ else -- 5.2
runtime = { "stock", _VERSION }
end
-
local C, Ct, P, S = lpeg.C, lpeg.Ct, lpeg.P, lpeg.S
local lpegmatch = lpeg.match
@@ -102,6 +99,7 @@ config.lualibs.prefer_merged = true
config.lualibs.load_extended = true
require "lualibs"
+
local iosavedata = io.savedata
local lfsisdir = lfs.isdir
local lfsisfile = lfs.isfile
@@ -131,22 +129,69 @@ local backup = {
}
texio.write, texio.write_nl = dummy_function, dummy_function
-require"luaotfload-basics-gen.lua"
+require "luaotfload-basics-gen.lua"
texio.write, texio.write_nl = backup.write, backup.write_nl
utilities = backup.utilities
-require "luaotfload-log.lua" --- this populates the luaotfload.log.* namespace
-require "luaotfload-parsers" --- fonts.conf, configuration, and request syntax
-require "luaotfload-configuration" --- configuration file handling
-require "luaotfload-database"
+fonts = { names = { } } -- for db; normally provided by the fontloaders
+
+local require_init = { }
+
+local loadmodule = function (name)
+ local v = require ("luaotfload-" .. name)
+ if v then
+ local mod = { }
+ local tv = type (v)
+ if tv == "table" then
+ mod.name = name
+ mod.init = v.init
+ require_init [#require_init + 1] = mod
+ elseif tv == "function" then
+ mod.name = name
+ mod.init = v
+ require_init [#require_init + 1] = mod
+ end
+ end
+end
+
require "alt_getopt"
-local names = fonts.names
-local sanitize_fontname = names.sanitize_fontname
+loadmodule "log.lua" --- this populates the luaotfload.log.* namespace
+loadmodule "parsers" --- fonts.conf, configuration, and request syntax
+loadmodule "configuration" --- configuration file handling
+loadmodule "database"
+loadmodule "resolvers" --- Font lookup
+
+local logreport
+
+local init_modules = function ()
+ --- NB we don’t command the logger at this point.
+ local todo = #require_init
+ local ret = true
+ for i = 1, todo do
+ local mod = require_init[i]
+ local name = mod.name
+ local init = mod.init
+ if type (init) ~= "function" then
+ error ("luaotfload broken; module "
+ .. name .. " missing initializers!")
+ end
+ local v = mod.init ()
+ if v == true then
+ --- evaluated well
+ elseif type (v) == "table" then
+ luaotfload[name] = v
+ else
+ error ("luaotfload broken; initialization of module "
+ .. name .. " returned " .. tostring (v) .. ".")
+ return false
+ end
+ end
+ logreport = luaotfload.log.report
+ return ret
+end
-local log = luaotfload.log
-local report = log.report
local help_messages = {
["luaotfload-tool"] = [[
@@ -163,8 +208,7 @@ Usage: %s [OPTIONS...]
-q --quiet don't output anything
-v --verbose=LEVEL be more verbose (print the searched directories)
- -vv print the loaded fonts
- -vvv print all steps of directory searching
+ -v, -vv .. -vvvvvvvvv set loglevel in unary
--log=stdout redirect log output to stdout
-V --version print version and exit
@@ -184,7 +228,7 @@ Usage: %s [OPTIONS...]
-c --no-compress do not gzip index file (text version only)
-l --flush-lookups empty lookup cache of font requests
-D --dry-run skip loading of fonts, just scan
- --formats=[+|-]EXTENSIONS set, add, or subtract formats to index
+ --formats=[+|-]EXTENSIONS set, add, or subtract file formats
-p --prefer-texmf prefer fonts in the TEXMF over system fonts
--max-fonts=N process at most N font files
@@ -265,7 +309,7 @@ local about = [[
local version_msg = function ( )
local out = function (...) texiowrite_nl (stringformat (...)) end
local uname = os.uname ()
- local meta = names.getmetadata ()
+ local meta = fonts.names.getmetadata ()
out (about, luaotfload.self)
out ("%s version: %q", luaotfload.self, version)
out ("Revision: %q", config.luaotfload.status.notes.revision)
@@ -666,7 +710,7 @@ subfont_by_name = function (lst, askedname, n)
local font = lst[n]
if font then
- if sanitize_fontname (font.fullname) == askedname then
+ if fonts.names.sanitize_fontname (font.fullname) == askedname then
return font
end
return subfont_by_name (lst, askedname, n+1)
@@ -683,10 +727,10 @@ The font info knows two levels of detail:
--doc]]--
local show_font_info = function (basename, askedname, detail, warnings)
- local filenames = names.data().files
+ local filenames = fonts.names.data().files
local index = filenames.base[basename]
local fullname = filenames.full[index]
- askedname = sanitize_fontname (askedname)
+ askedname = fonts.names.sanitize_fontname (askedname)
if not fullname then -- texmf
fullname = resolvers.findfile(basename)
end
@@ -696,9 +740,9 @@ local show_font_info = function (basename, askedname, detail, warnings)
if nfonts > 0 then -- true type collection
local subfont
if askedname then
- report (true, 1, "resolve",
- [[%s is part of the font collection %s]],
- askedname, basename)
+ logreport (true, 1, "resolve",
+ [[%s is part of the font collection %s]],
+ askedname, basename)
subfont = subfont_by_name(shortinfo, askedname)
end
if subfont then
@@ -707,11 +751,11 @@ local show_font_info = function (basename, askedname, detail, warnings)
show_full_info(fullname, subfont, warnings)
end
else -- list all subfonts
- report (true, 1, "resolve",
- [[%s is a font collection]], basename)
+ logreport (true, 1, "resolve",
+ [[%s is a font collection]], basename)
for subfont = 1, nfonts do
- report (true, 1, "resolve",
- [[Showing info for font no. %d]], n)
+ logreport (true, 1, "resolve",
+ [[Showing info for font no. %d]], n)
show_info_items(shortinfo[subfont])
if detail == true then
show_full_info(fullname, subfont, warnings)
@@ -725,7 +769,7 @@ local show_font_info = function (basename, askedname, detail, warnings)
end
end
else
- report (true, 1, "resolve", "Font %s not found", filename)
+ logreport (true, 1, "resolve", "Font %s not found", filename)
end
end
@@ -753,9 +797,9 @@ local actions = { } --- (jobspec -> (bool * bool)) list
actions.loglevel = function (job)
local lvl = job.log_level
if lvl then
- log.set_loglevel(lvl)
- report ("info", 3, "util", "Setting the log level to %d.", lvl)
- report ("log", 2, "util", "Lua=%q", _VERSION)
+ luaotfload.log.set_loglevel(lvl)
+ logreport ("info", 3, "util", "Setting the log level to %d.", lvl)
+ logreport ("log", 2, "util", "Lua=%q", _VERSION)
end
return true, true
end
@@ -790,19 +834,21 @@ actions.help = function (job)
end
actions.blacklist = function (job)
- names.read_blacklist()
+ fonts.names.read_blacklist()
local n = 0
- for n, entry in next, tablesortedkeys(names.blacklist) do
+ for n, entry in next, tablesortedkeys(fonts.names.blacklist) do
iowrite (stringformat("(%d %s)\n", n, entry))
end
return true, false
end
actions.generate = function (job)
- local _ = names.update (fontnames, job.force_reload, job.dry_run)
- local namedata = names.data ()
+ local _ = fonts.names.update (fontnames, job.force_reload, job.dry_run)
+ local namedata = fonts.names.data ()
if namedata then
- report ("info", 2, "db", "Fonts in the database: %i", #namedata.mappings)
+ logreport ("info", 2, "db",
+ "Fonts in the database: %i",
+ #namedata.mappings)
return true, true
end
return false, false
@@ -838,12 +884,14 @@ local write_bisect_status = function (data)
osdate ("%Y-%m-d %H:%M:%S", os.time ()),
payload)
if status and iosavedata (bisect_status_file, status) then
- report ("info", 4, "bisect",
- "Bisection state written to %s.", bisect_status_file)
+ logreport ("info", 4, "bisect",
+ "Bisection state written to %s.",
+ bisect_status_file)
return true
end
- report ("info", 0, "bisect",
- "Failed to write bisection state to %s.", bisect_status_file)
+ logreport ("info", 0, "bisect",
+ "Failed to write bisection state to %s.",
+ bisect_status_file)
return false
end
@@ -855,16 +903,22 @@ end
--- unit -> state list
local read_bisect_status = function ()
- report ("info", 4, "bisect", "Testing for status file: %q.", bisect_status_file)
+ logreport ("info", 4, "bisect",
+ "Testing for status file: %q.",
+ bisect_status_file)
if not lfsisfile (bisect_status_file) then
- report ("info", 2, "bisect", "No such file: %q.", bisect_status_file)
- report ("info", 0, "bisect", "Not in bisect mode.")
+ logreport ("info", 2, "bisect",
+ "No such file: %q.", bisect_status_file)
+ logreport ("info", 0, "bisect",
+ "Not in bisect mode.")
return false
end
- report ("info", 4, "bisect", "Reading status file: %q.", bisect_status_file)
+ logreport ("info", 4, "bisect",
+ "Reading status file: %q.", bisect_status_file)
local success, status = pcall (dofile, bisect_status_file)
if not success then
- report ("info", 0, "bisect", "Could not read status file.")
+ logreport ("info", 0, "bisect",
+ "Could not read status file.")
return false
end
return status
@@ -879,19 +933,21 @@ end
local bisect_start = function ()
if lfsisfile (bisect_status_file) then
- report ("info", 0, "bisect",
- "Bisect session in progress.",
- bisect_status_file)
- report ("info", 0, "bisect",
- "Use --bisect=stop to erase it before starting over.")
+ logreport ("info", 0, "bisect",
+ "Bisect session in progress.",
+ bisect_status_file)
+ logreport ("info", 0, "bisect",
+ "Use --bisect=stop to erase it before starting over.")
return false, false
end
- report ("info", 2, "bisect",
- "Starting bisection of font database %q.", bisect_status_file)
- local n = names.count_font_files ()
+ logreport ("info", 2, "bisect",
+ "Starting bisection of font database %q.",
+ bisect_status_file)
+ local n = fonts.names.count_font_files ()
local pivot = mathfloor (n / 2)
local data = { { 1, n, pivot } }
- report ("info", 0, "bisect", "Initializing pivot to %d.", pivot)
+ logreport ("info", 0, "bisect",
+ "Initializing pivot to %d.", pivot)
if write_bisect_status (data) then
return true, false
end
@@ -905,21 +961,23 @@ end
--doc]]--
local bisect_stop = function ()
- report ("info", 3, "bisect", "Erasing bisection state at %s.", bisect_status_file)
+ logreport ("info", 3, "bisect",
+ "Erasing bisection state at %s.",
+ bisect_status_file)
if lfsisfile (bisect_status_file) then
local success, msg = os.remove (bisect_status_file)
if not success then
- report ("info", 2, "bisect",
- "Failed to erase file %s (%s).",
- bisect_status_file, msg)
+ logreport ("info", 2, "bisect",
+ "Failed to erase file %s (%s).",
+ bisect_status_file, msg)
end
end
if lfsisdir (bisect_status_path) then
local success, msg = os.remove (bisect_status_path)
if not success then
- report ("info", 2, "bisect",
- "Failed to erase directory %s (%s).",
- bisect_status_path, msg)
+ logreport ("info", 2, "bisect",
+ "Failed to erase directory %s (%s).",
+ bisect_status_path, msg)
end
end
if lfsisfile (bisect_status_file) then
@@ -936,12 +994,12 @@ end
--doc]]--
local bisect_terminate = function (nsteps, culprit)
- report ("info", 1, "bisect",
- "Bisection completed after %d steps.", nsteps)
- report ("info", 0, "bisect",
- "Bad file: %s.", names.nth_font_filename (culprit))
- report ("info", 0, "bisect",
- "Run with --bisect=stop to finish bisection.")
+ logreport ("info", 1, "bisect",
+ "Bisection completed after %d steps.", nsteps)
+ logreport ("info", 0, "bisect",
+ "Bad file: %s.", fonts.names.nth_font_filename (culprit))
+ logreport ("info", 0, "bisect",
+ "Run with --bisect=stop to finish bisection.")
return true, false
end
@@ -952,10 +1010,10 @@ end
--doc]]--
local list_remainder = function (lo, hi)
- local fonts = names.font_slice (lo, hi)
- report ("info", 0, "bisect", "%d fonts left.", hi - lo + 1)
+ local fonts = fonts.names.font_slice (lo, hi)
+ logreport ("info", 0, "bisect", "%d fonts left.", hi - lo + 1)
for i = 1, #fonts do
- report ("info", 1, "bisect", " · %2d: %s", lo, fonts[i])
+ logreport ("info", 1, "bisect", " · %2d: %s", lo, fonts[i])
lo = lo + 1
end
end
@@ -988,8 +1046,9 @@ local bisect_set = function (outcome)
local lo, hi, pivot = unpack (previous)
- report ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.",
- nsteps, lo, hi, pivot)
+ logreport ("info", 3, "bisect",
+ "Previous step %d: lo=%d, hi=%d, pivot=%d.",
+ nsteps, lo, hi, pivot)
if outcome == "bad" then
hi = pivot
@@ -1000,9 +1059,9 @@ local bisect_set = function (outcome)
return bisect_terminate (nsteps, lo)
end
pivot = mathfloor ((lo + hi) / 2)
- report ("info", 0, "bisect",
- "Continuing with the lower segment: lo=%d, hi=%d, pivot=%d.",
- lo, hi, pivot)
+ logreport ("info", 0, "bisect",
+ "Continuing with the lower segment: lo=%d, hi=%d, pivot=%d.",
+ lo, hi, pivot)
elseif outcome == "good" then
lo = pivot + 1
if lo >= hi then --- complete
@@ -1012,11 +1071,12 @@ local bisect_set = function (outcome)
return bisect_terminate (nsteps, lo)
end
pivot = mathfloor ((lo + hi) / 2)
- report ("info", 0, "bisect",
- "Continuing with the upper segment: lo=%d, hi=%d, pivot=%d.",
- lo, hi, pivot)
+ logreport ("info", 0, "bisect",
+ "Continuing with the upper segment: lo=%d, hi=%d, pivot=%d.",
+ lo, hi, pivot)
else -- can’t happen
- report ("info", 0, "bisect", "What the hell?", lo, hi, pivot)
+ logreport ("info", 0, "bisect",
+ "What the hell?", lo, hi, pivot)
return false, false
end
@@ -1043,13 +1103,13 @@ local bisect_status = function ()
if nsteps > 1 then
for i = nsteps - 1, 1, -1 do
local step = status[i]
- report ("info", 2, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.",
- i, unpack (step))
+ logreport ("info", 2, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.",
+ i, unpack (step))
end
end
local current = status[nsteps]
- report ("info", 0, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.",
- nsteps, unpack (current))
+ logreport ("info", 0, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.",
+ nsteps, unpack (current))
return true, false
end
@@ -1075,10 +1135,10 @@ local bisect_run = function ()
current = status[nsteps - 1]
end
local lo, hi, pivot = unpack (current)
- report ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.",
- nsteps, lo, hi, pivot)
- report ("info", 1, "bisect", "Step %d: Testing fonts from %d to %d.",
- currentstep, lo, pivot)
+ logreport ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.",
+ nsteps, lo, hi, pivot)
+ logreport ("info", 1, "bisect", "Step %d: Testing fonts from %d to %d.",
+ currentstep, lo, pivot)
config.luaotfload.misc.bisect = { lo, pivot }
return true, true
end
@@ -1096,18 +1156,18 @@ actions.bisect = function (job)
local mode = job.bisect
local runner = bisect_modes[mode]
if not runner then
- report ("info", 0, "bisect", "Unknown directive %q.", mode)
+ logreport ("info", 0, "bisect", "Unknown directive %q.", mode)
return false, false
end
return runner (job)
end
actions.flush = function (job)
- local success = names.flush_lookup_cache()
+ local success = fonts.names.flush_lookup_cache()
if success then
- local success = names.save_lookups()
+ local success = fonts.names.save_lookups()
if success then
- report ("info", 2, "cache", "Lookup cache emptied")
+ logreport ("info", 2, "cache", "Lookup cache emptied")
return true, true
end
end
@@ -1115,16 +1175,16 @@ actions.flush = function (job)
end
local cache_directives = {
- ["purge"] = names.purge_cache,
- ["erase"] = names.erase_cache,
- ["show"] = names.show_cache,
+ ["purge"] = fonts.names.purge_cache,
+ ["erase"] = fonts.names.erase_cache,
+ ["show"] = fonts.names.show_cache,
}
actions.cache = function (job)
local directive = cache_directives[job.cache]
if not directive or type(directive) ~= "function" then
- report ("info", 2, "cache",
- "Invalid font cache directive %s.", job.cache)
+ logreport ("info", 2, "cache",
+ "Invalid font cache directive %s.", job.cache)
return false, false
end
if directive() then
@@ -1147,7 +1207,7 @@ actions.query = function (job)
features = { },
}
- tmpspec = names.handle_request (tmpspec)
+ tmpspec = fonts.names.handle_request (tmpspec)
if not tmpspec.size then
tmpspec.size = 655360 --- assume 10pt
@@ -1158,38 +1218,38 @@ actions.query = function (job)
if tmpspec.lookup == "name"
or tmpspec.lookup == "anon" --- not *exactly* as resolvers.anon
then
- foundname, subfont = names.resolve_name (tmpspec)
+ foundname, _, success = fonts.names.lookup_font_name (tmpspec)
if foundname then
- foundname, _, success = names.font_file_lookup (foundname)
+ foundname, _, success = fonts.names.lookup_font_file (foundname)
end
elseif tmpspec.lookup == "file" then
- foundname, _, success =
- names.font_file_lookup (tmpspec.name)
+ foundname, _, success = fonts.names.lookup_font_file (tmpspec.name)
end
if success then
- report (false, 0, "resolve", "Font %q found!", query)
+ logreport (false, 0, "resolve", "Font %q found!", query)
if subfont then
- report (false, 0, "resolve",
- "Resolved file name %q, subfont nr. %q",
- foundname, subfont)
+ logreport (false, 0, "resolve",
+ "Resolved file name %q, subfont nr. %q",
+ foundname, subfont)
else
- report (false, 0, "resolve",
- "Resolved file name %q", foundname)
+ logreport (false, 0, "resolve",
+ "Resolved file name %q", foundname)
end
if job.show_info then
show_font_info (foundname, query, job.full_info, job.warnings)
iowrite "\n"
end
else
- report (false, 0, "resolve", "Cannot find %q in index.", query)
- report (false, 0, "resolve",
- "Hint: use the --fuzzy option to display suggestions.",
- query)
+ logreport (false, 0, "resolve", "Cannot find %q in index.", query)
if job.fuzzy == true then
- report (false, 0, "resolve",
- "Looking for close matches, this may take a while ...")
- local _success = names.find_closest(query, job.fuzzy_limit)
+ logreport (false, 0, "resolve",
+ "Looking for close matches, this may take a while ...")
+ local _success = fonts.names.find_closest(query, job.fuzzy_limit)
+ else
+ logreport (false, 0, "resolve",
+ "Hint: use the --fuzzy option to display suggestions.",
+ query)
end
end
return true, true
@@ -1262,14 +1322,13 @@ set_primary_field = function (fields, addme, acc, n)
return acc
end
-local splitcomma = luaotfload.parsers.splitcomma
-
actions.list = function (job)
local criterion = job.criterion
local asked_fields = job.asked_fields
- local name_index = names.data ()
+ local name_index = fonts.names.data ()
if asked_fields then
+ local splitcomma = luaotfload.parsers.splitcomma
asked_fields = lpegmatch(splitcomma, asked_fields)
end
@@ -1279,14 +1338,14 @@ actions.list = function (job)
end
if not name_index then
- name_index = names.load()
+ name_index = fonts.names.load()
end
local mappings = name_index.mappings
local nmappings = #mappings
if criterion == "*" then
- report (false, 1, "list", "All %d entries", nmappings)
+ logreport (false, 1, "list", "All %d entries", nmappings)
for i=1, nmappings do
local entry = mappings[i]
local fields = get_fields(entry, asked_fields)
@@ -1301,12 +1360,12 @@ actions.list = function (job)
criterion = criterion[1]
asked_fields = set_primary_field(asked_fields, criterion)
- report (false, 1, "list", "By %s", criterion)
+ logreport (false, 1, "list", "By %s", criterion)
--- firstly, build a list of fonts to operate on
local targets = { }
if asked_value then --- only those whose value matches
- report (false, 2, "list", "Restricting to value %s", asked_value)
+ logreport (false, 2, "list", "Restricting to value %s", asked_value)
for i=1, nmappings do
local entry = mappings[i]
if entry[criterion]
@@ -1351,7 +1410,7 @@ actions.list = function (job)
end
end
local ntargets = #targets
- report (false, 2, "list", "%d entries", ntargets)
+ logreport (false, 2, "list", "%d entries", ntargets)
--- now, output the collection
for i=1, ntargets do
@@ -1488,7 +1547,7 @@ local process_cmdline = function ( ) -- unit -> jobspec
elseif v == "log" then
local str = optarg[n]
if str then
- finalizers = log.set_logout(str, finalizers)
+ finalizers = luaotfload.log.set_logout(str, finalizers)
end
elseif v == "find" then
action_pending["query"] = true
@@ -1521,7 +1580,7 @@ local process_cmdline = function ( ) -- unit -> jobspec
elseif v == "D" then
result.dry_run = true
elseif v == "p" then
- names.set_location_precedence {
+ fonts.names.set_location_precedence {
"local", "texmf", "system"
}
elseif v == "b" then
@@ -1582,6 +1641,8 @@ local process_cmdline = function ( ) -- unit -> jobspec
end
local main = function ( ) -- unit -> int
+ if init_modules () == false then return -42 end
+
local retval = 0
local job = process_cmdline()
@@ -1592,23 +1653,23 @@ local main = function ( ) -- unit -> int
local actionname = action_sequence[i]
local exit = false
if action_pending[actionname] then
- report ("log", 3, "util", "Preparing for task", "%s", actionname)
+ logreport ("log", 3, "util", "Preparing for task", "%s", actionname)
local action = actions[actionname]
local success, continue = action(job)
if not success then
- report (false, 0, "util",
- "Failed to execute task.", "%s", actionname)
+ logreport (false, 0, "util",
+ "Failed to execute task.", "%s", actionname)
retval = -1
exit = true
elseif not continue then
- report (false, 3, "util",
- "Task completed, exiting.", "%s", actionname)
+ logreport (false, 3, "util",
+ "Task completed, exiting.", "%s", actionname)
exit = true
else
- report (false, 3, "util",
- "Task completed successfully.", "%s", actionname)
+ logreport (false, 3, "util",
+ "Task completed successfully.", "%s", actionname)
end
end
if exit then break end
diff --git a/src/luaotfload.sty b/src/luaotfload.sty
index ba61428..1b31b1d 100644
--- a/src/luaotfload.sty
+++ b/src/luaotfload.sty
@@ -46,3 +46,4 @@
require('luaotfload-main')
local _void = luaotfload.main ()
}
+