From 64ba2a9dbcc524178aec8cecbb6947b449724990 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Sun, 23 May 2010 23:49:58 +0000 Subject: luaotfload update git-svn-id: svn://tug.org/texlive/trunk@18437 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/doc/luatex/luaotfload/NEWS | 20 + Master/texmf-dist/doc/luatex/luaotfload/News | 15 - Master/texmf-dist/doc/luatex/luaotfload/README | 46 +- .../doc/luatex/luaotfload/luaotfload.pdf | Bin 178017 -> 271792 bytes .../scripts/luaotfload/mkluatexfontdb.lua | 173 +++ .../texmf-dist/source/luatex/luaotfload/Makefile | 19 +- .../source/luatex/luaotfload/luaotfload.dtx | 408 ++--- .../tex/luatex/luaotfload/luaotfload.lua | 170 +- .../tex/luatex/luaotfload/luaotfload.sty | 6 +- .../tex/luatex/luaotfload/otfl-compat-font-otf.lua | 1629 -------------------- .../tex/luatex/luaotfload/otfl-font-cid.lua | 3 +- .../tex/luatex/luaotfload/otfl-font-clr.lua | 155 ++ .../tex/luatex/luaotfload/otfl-font-def.lua | 18 +- .../tex/luatex/luaotfload/otfl-font-dum.lua | 245 ++- .../tex/luatex/luaotfload/otfl-font-ini.lua | 8 +- .../tex/luatex/luaotfload/otfl-font-map.lua | 377 +++-- .../tex/luatex/luaotfload/otfl-font-nms.lua | 495 ++++++ .../tex/luatex/luaotfload/otfl-font-ota.lua | 7 +- .../tex/luatex/luaotfload/otfl-font-otb.lua | 224 +-- .../tex/luatex/luaotfload/otfl-font-otc.lua | 2 + .../tex/luatex/luaotfload/otfl-font-otf.lua | 476 +++--- .../tex/luatex/luaotfload/otfl-font-otn.lua | 470 +++--- .../tex/luatex/luaotfload/otfl-font-ott.lua | 12 +- .../tex/luatex/luaotfload/otfl-font-tfm.lua | 411 ++--- .../tex/luatex/luaotfload/otfl-font-xtx.lua | 28 +- .../tex/luatex/luaotfload/otfl-luat-dum.lua | 68 +- .../tex/luatex/luaotfload/otfl-luat-ovr.lua | 35 + .../tex/luatex/luaotfload/otfl-node-fnt.lua | 6 +- .../tex/luatex/luaotfload/otfl-node-ini.lua | 48 +- .../tex/luatex/luaotfload/otfl-node-inj.lua | 3 +- .../tex/luatex/luaotfload/otfl-node-res.lua | 193 ++- 31 files changed, 2815 insertions(+), 2955 deletions(-) create mode 100644 Master/texmf-dist/doc/luatex/luaotfload/NEWS delete mode 100644 Master/texmf-dist/doc/luatex/luaotfload/News create mode 100755 Master/texmf-dist/scripts/luaotfload/mkluatexfontdb.lua delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/otfl-compat-font-otf.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/otfl-font-clr.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/otfl-font-nms.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/otfl-luat-ovr.lua (limited to 'Master') diff --git a/Master/texmf-dist/doc/luatex/luaotfload/NEWS b/Master/texmf-dist/doc/luatex/luaotfload/NEWS new file mode 100644 index 00000000000..129a2b0e5fd --- /dev/null +++ b/Master/texmf-dist/doc/luatex/luaotfload/NEWS @@ -0,0 +1,20 @@ + History of the luaotfload bundle + +Currrent git, luaotfload v1.07: + * using the new luatexbase package + * synchronizing with latest ConTeXt stable 2010.05.08 + * adding support for microtypography + * adding support for color and transparency + * adding a script to generate a font database with TeX and system fonts + * more verbose log file + +2010/02/07, luaotfload v1.06a: + * more complete tlig and trep + * defaults options changed to "+ccmp;+locl;+rlig;+liga;+clig;+kern; + +mark;+mkmk;" + * supporting windows absolute path + +2009/12/01, luaotfload v1.06: + * synchronizing with ConTeXt stable 2009.11.26 + * adding backward compatibility with LuaTeX 0.40.6 + diff --git a/Master/texmf-dist/doc/luatex/luaotfload/News b/Master/texmf-dist/doc/luatex/luaotfload/News deleted file mode 100644 index 6521e44bfd1..00000000000 --- a/Master/texmf-dist/doc/luatex/luaotfload/News +++ /dev/null @@ -1,15 +0,0 @@ - History of the luaotfload bundle - -2010/02/22, luaotfload v1.06b: - * fix loading OpenType feature files - -2010/02/07, luaotfload v1.06a: - * more complete tlig and trep - * defaults options changed to "+ccmp;+locl;+rlig;+liga;+clig;+kern; - +mark;+mkmk;" - * supporting windows absolute path - -2009/12/01, luaotfload v1.06: - * synchronizing with ConTeXt stable 2009.11.26 - * adding backward compatibility with LuaTeX 0.40.6 - diff --git a/Master/texmf-dist/doc/luatex/luaotfload/README b/Master/texmf-dist/doc/luatex/luaotfload/README index 5b1f7453334..94096480e7a 100644 --- a/Master/texmf-dist/doc/luatex/luaotfload/README +++ b/Master/texmf-dist/doc/luatex/luaotfload/README @@ -5,8 +5,8 @@ luaotfload is the adaptation of the ConTeXt OTF loading system for Plain and LaTeX. It allows to open OTF fonts with almost all features available through the XeTeX font syntax. -This package is developed on , see the 'News' -file for version history. +This package is developed on , see the +'NEWS' file for version history. Installation @@ -34,6 +34,7 @@ Manifest Source files: luaotfload.dtx single file source & doc for the package + mkluatexfontdb.lua script using otfl-font-nms.lua otfl-data-con.lua OTF support files from the ConTeXt distribution otfl-font-cid.lua . otfl-font-def.lua . @@ -56,6 +57,9 @@ Source files: otfl-node-ini.lua . otfl-node-inj.lua . otfl-node-res.lua OTF support files from the ConTeXt distribution + otfl-font-clr.lua luaotfload module for font color support + otfl-luat-ovr.lua luaotfload module for overriding some ConTeXt functions + otfl-luat-nms.lua luaotfload module for font name database generation Derived files: luaotfload.pdf documentation @@ -68,36 +72,48 @@ License This work is distributed under the GNU GPLv2 license. Please see COPYING file for more details about the license. -The files otfl-*.lua are under the GNU GPLv2 license. Their legal notice is - +The files otfl-*.lua are under the GNU GPLv2 license. Their legal notice +starts by: + " Copyright 2008 PRAGMA ADE / ConTeXt Development Team - +" + +for otf-*.lua files except otfl-font-clr.lua and otfl-luat-ovr.lua, and + +" +Copyright 2010 Luaotfload Development Team +" + +for otfl-font-clr.lua, otfl-font-nms and otfl-luat-ovr.lua, and continues by: + +" This program 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; either version 2 of the License, or (at your option) any later version. - + This program 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 +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 this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. " - + + The file luaotfload.dtx and the derived files luaotfload.pdf luaotfload.sty luaotfload.lua -are under the CC0 license. You can find the CC0 license full text at - +as well as the file mkluatexfontdb.lua are under the CC0 license. You can find +the CC0 license full text at + http://creativecommons.org/publicdomain/zero/1.0/legalcode - + and a FAQ at - -http://wiki.creativecommons.org/CC0 +http://wiki.creativecommons.org/CC0 diff --git a/Master/texmf-dist/doc/luatex/luaotfload/luaotfload.pdf b/Master/texmf-dist/doc/luatex/luaotfload/luaotfload.pdf index 8f4638a9a1a..a85639692e6 100644 Binary files a/Master/texmf-dist/doc/luatex/luaotfload/luaotfload.pdf and b/Master/texmf-dist/doc/luatex/luaotfload/luaotfload.pdf differ diff --git a/Master/texmf-dist/scripts/luaotfload/mkluatexfontdb.lua b/Master/texmf-dist/scripts/luaotfload/mkluatexfontdb.lua new file mode 100755 index 00000000000..161080d740d --- /dev/null +++ b/Master/texmf-dist/scripts/luaotfload/mkluatexfontdb.lua @@ -0,0 +1,173 @@ +#!/usr/bin/env texlua +--[[ +This file is copyright 2010 Elie Roux and Khaled Hosny and is under CC0 +license (see http://creativecommons.org/publicdomain/zero/1.0/legalcode). + +This file is a wrapper for the luaotfload's font names module. It is part of the +luaotfload bundle, please see the luaotfload documentation for more info. +--]] + +kpse.set_program_name("luatex") + +require("lualibs") +require("otfl-font-nms") +require("alt_getopt") + +local name = 'mkluatexfontdb' +local version = '1.07' -- same version number as luaotfload + +mkluatexfontdb = { } -- just for now, elie is rewriting it anyway +local names = fonts.names + +-- the directory in which the database will be saved, can be overwritten +mkluatexfontdb.directory = names.path.localdir + +local log = logs.report + +local function help_msg() + texio.write(string.format([[ +Usage: %s [OPTION]... + +Rebuild the LuaTeX font database. + +Valid options: + -d --database-dir=DIRECTORY install the database in the specified directory + -f --force force re-indexing all fonts + -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 + --fc-cache run fc-cache before updating database + --sys install the database system-wide + (default is only for the current user) + -V --version print version and exit + -h --help print this message + +The output database file is named otfl-fonts.lua. By default it is placed +in $TEXMFVAR/luatex/generic/luaotfload/names. +]], name)) +end + +local function version_msg() + texio.write(string.format( + "%s version %s, database version %s.\n", name, version, names.version)) +end + +--[[ +Command-line processing. +Here we fill cmdargs with the good values, and then analyze it. +--]] + +local long_opts = { + ['database-dir'] = "d", + force = "f", + quiet = "q", + help = "h", + ['fc-cache'] = 0 , + sys = 0 , + verbose = 1 , + version = "V", +} + +local short_opts = "d:fqpvVh" + +local function do_run_fc_cache(c) + --[[ + Function running fc-cache if needed. + The argument is nil for default, 0 for no fc-cache and 1 for fc-cache. + Default behaviour is to run fc-cache if available. + --]] + if c == 0 then return end + if not c then + -- TODO: detect if fc-cache is available + end + local toexec = 'fc-cache' + if os.type == 'windows' then + toexec = 'fc-cache.exe' -- TODO: to test on a non-cygwin Windows + end + log('executing %s...\n', toexec) + os.execute(toexec) +end + +-- a temporary variable, containing the command line option concerning fc-cache +local run_fc_cache = 0 +local force_reload = nil + +local function process_cmdline() + local opts, optind, optarg = alt_getopt.get_ordered_opts (arg, short_opts, long_opts) + local log_level = 1 + for i,v in ipairs(opts) do + if v == "q" then + log_level = 0 + elseif v == "v" then + if log_level > 0 then + log_level = log_level + 1 + else + log_level = 2 + end + elseif v == "V" then + version_msg() + os.exit(0) + elseif v == "h" then + help_msg() + os.exit(0) + elseif v == "d" then + mkluatexfontdb.directory = optarg [i] + elseif v == "f" then + force_reload = 1 + elseif v == "fc-cache" then + run_fc_cache = 1 + elseif v == "sys" then + mkluatexfontdb.directory = names.path.systemdir + end + end + if string.match(arg[0], '-sys') then + mkluatexfontdb.directory = names.path.systemdir + end + mkluatexfontdb.directory = fonts.path_normalize(mkluatexfontdb.directory) + names.set_log_level(log_level) +end + +process_cmdline() +do_run_fc_cache(run_fc_cache) + +-- the status table is containing correspondances between absolute path and last modification +-- timestamp, it's uses to save time during update, by not reparsing unchanged fonts. +local status = nil +local status_file = mkluatexfontdb.directory .. "/otfl-names-status.lua" +if not force_reload and file.isreadable(status_file) then + status = dofile(status_file) +end + +local function generate(force) + log("generating font names database.") + local savepath = mkluatexfontdb.directory + if not lfs.isdir(savepath) then + log("creating directory %s", savepath) + dir.mkdirs(savepath) + if not lfs.isdir(savepath) then + texio.write_nl(string.format("Error: cannot create directory '%s', exiting.\n", savepath)) + os.exit(1) + end + end + savepath = savepath .. '/' .. names.path.basename + local fh = io.open(savepath, 'a+') + if not fh then + texio.write_nl(string.format("Error: cannot write file '%s', exiting.\n", savepath)) + os.exit(1) + end + fh:close() + local fontnames + if not force_reload and file.isreadable(savepath) then + fontnames = dofile(savepath) + else + fontnames = nil + end + fontnames, status = names.update(fontnames, status, force) + log("%s fonts in the database", #fontnames.mappings) + io.savedata(savepath, table.serialize(fontnames, true)) + log("saved font names database in %s\n", savepath) + io.savedata(status_file, table.serialize(status, true)) +end + +generate(force_reload) diff --git a/Master/texmf-dist/source/luatex/luaotfload/Makefile b/Master/texmf-dist/source/luatex/luaotfload/Makefile index 63d389bb1b4..6a1363073dc 100644 --- a/Master/texmf-dist/source/luatex/luaotfload/Makefile +++ b/Master/texmf-dist/source/luatex/luaotfload/Makefile @@ -9,12 +9,13 @@ OTFL = $(wildcard otfl-*.lua) COMPILED = $(DOC) UNPACKED = luaotfload.sty luaotfload.lua GENERATED = $(COMPILED) $(UNPACKED) -SOURCE = $(DTX) $(OTFL) README Makefile News +SOURCE = $(DTX) $(OTFL) README Makefile NEWS mkluatexfontdb.lua # Files grouped by installation location -RUNFILES = $(UNPACKED) $(OTFL) -DOCFILES = $(DOC) README News -SRCFILES = $(DTX) Makefile +SCRIPTFILES = mkluatexfontdb.lua +RUNFILES = $(UNPACKED) $(OTFL) +DOCFILES = $(DOC) README NEWS +SRCFILES = $(DTX) Makefile # The following definitions should be equivalent # ALL_FILES = $(RUNFILES) $(DOCFILES) $(SRCFILES) @@ -22,9 +23,10 @@ ALL_FILES = $(GENERATED) $(SOURCE) # Installation locations FORMAT = luatex -RUNDIR = $(TEXMFROOT)/tex/$(FORMAT)/$(NAME) -DOCDIR = $(TEXMFROOT)/doc/$(FORMAT)/$(NAME) -SRCDIR = $(TEXMFROOT)/source/$(FORMAT)/$(NAME) +SCRIPTDIR = $(TEXMFROOT)/scripts/mkluatexfontdb +RUNDIR = $(TEXMFROOT)/tex/$(FORMAT)/$(NAME) +DOCDIR = $(TEXMFROOT)/doc/$(FORMAT)/$(NAME) +SRCDIR = $(TEXMFROOT)/source/$(FORMAT)/$(NAME) TEXMFROOT = ./texmf CTAN_ZIP = $(NAME).zip @@ -57,6 +59,7 @@ $(CTAN_ZIP): $(SOURCE) $(COMPILED) $(TDS_ZIP) @zip -9 $@ $^ >/dev/null define run-install +@mkdir -p $(SCRIPTDIR) && cp $(SCRIPTFILES) $(SCRIPTDIR) @mkdir -p $(RUNDIR) && cp $(RUNFILES) $(RUNDIR) @mkdir -p $(DOCDIR) && cp $(DOCFILES) $(DOCDIR) @mkdir -p $(SRCDIR) && cp $(SRCFILES) $(SRCDIR) @@ -84,7 +87,7 @@ manifest: @for f in $(GENERATED); do echo $$f; done clean: - @$(RM) -- *.log *.aux *.toc *.idx *.ind *.ilg + @$(RM) -- *.log *.aux *.toc *.idx *.ind *.ilg *.out mrproper: clean @$(RM) -- $(GENERATED) $(ZIPS) diff --git a/Master/texmf-dist/source/luatex/luaotfload/luaotfload.dtx b/Master/texmf-dist/source/luatex/luaotfload/luaotfload.dtx index 1160f80cc03..8c2de488978 100644 --- a/Master/texmf-dist/source/luatex/luaotfload/luaotfload.dtx +++ b/Master/texmf-dist/source/luatex/luaotfload/luaotfload.dtx @@ -34,7 +34,7 @@ \input docstrip.tex \Msg{************************************************************************} \Msg{* Installation} -\Msg{* Package: luaotfload 2010/02/22 v1.06b ConTeXt font loading system} +\Msg{* Package: luaotfload 2010/05/10 v1.07 ConTeXt font loading system} \Msg{************************************************************************} \keepsilent @@ -99,11 +99,18 @@ and the derived files % %<*driver> \NeedsTeXFormat{LaTeX2e} -\ProvidesFile{luaminimalotf.drv}% - [2010/02/22 v1.06b ConTeXt font loading system]% +\ProvidesFile{luaotfload.drv}% + [2010/05/10 v1.07 ConTeXt font loading system]% \documentclass{ltxdoc} +\usepackage{metalogo,multicol,mdwlist,fancyvrb} +\usepackage[colorlinks=true]{hyperref} + +\def\LuaTeX{Lua\TeX} +\def\ConTeXt{Con\TeX t} + \EnableCrossrefs \CodelineIndex +\VerbatimFootnotes \begin{document} \DocInput{luaotfload.dtx}% \end{document} @@ -131,55 +138,163 @@ and the derived files % \GetFileInfo{luaotfload.drv} % % \title{The \textsf{luaotfload} package} -% \date{2010/02/22 v1.06b} -% \author{Elie Roux \\ \texttt{elie.roux@telecom-bretagne.eu}} +% \date{2010/05/10 v1.07} +% \author{ Elie Roux \footnote{\texttt{elie.roux@telecom-bretagne.eu}} +% \and Khaled hosny \footnote{\texttt{khaledhosny@eglug.org}}} % % \maketitle % % \begin{abstract} -% Con\TeX t font loading system, providing the possibility to load OTF fonts -% with a lot of features, and the Xe\TeX\ font loading syntax. +% Adaptation of \ConTeXt\ font loading system, providing the ability to load +% \textsf{OpenType} fonts with a lot of features, and extended font loading +% syntax. % \end{abstract} % % \tableofcontents % % \section{Documentation} -% +% % \subsection{Introduction} % % Font management and installation has always been painful with \TeX\ (and -% even more with \LaTeX ). A lot of files are needed for one font (tfm, pfb, +% even more with \LaTeX). A lot of files are needed for one font (tfm, pfb, % map, fd, vf), and they are limited to 256 characters. But the font world has % evolved since, and new standard types of fonts have appeared, like -% \textsf{truetype} or \textsf{opentype} fonts. These fonts can contain a lot +% \textsf{TrueType} and \textsf{OpenType} fonts. These fonts can contain a lot % of characters, and have some functionalities (ligatures, old-style numbers, % small capitals, etc.). They are everywhere, as the system fonts and most % modern text softwares fonts are of this type. Until now the (almost) only -% way to use them with \TeX\ was to use them with Xe\TeX . +% way to use them with \TeX\ was to use them with \XeTeX . % -% Unlike Xe\TeX , Lua\TeX\ does not provide facilities for these fonts by +% Unlike \XeTeX , \LuaTeX\ does not provide facilities for these fonts by % default, but it provides a way to hook lua code in some points of the \TeX\ % algorithm, for instance we can improve the font loading system; this is what % we do in this package. % % This package is quite low-level, and should be loaded directly in the macro -% package, like it is in Con\TeX t. Sadly, Plain and \LaTeX\ are frozen and -% it's even impossible to adapt them to the new engines. +% package, like it is in \ConTeXt. Sadly, Plain and \LaTeX\ are frozen and +% it's even impossible to adapt them to the new engines. +% +% \subsection{Loading fonts} +% +% \textsf{luaotfload} supports an extended font loading syntax which looks like: +% +% \begin{center} +% |\font\foo={|\meta{prefix}|:|\meta{font name}|:|\meta{font features}|}| \meta{\TeX\ font features} +% \end{center} +% +% The curly brackets are optional and are used for escaping spaces in font names +% (\XeTeX-like double quotes can also used for the same purpose). +% +% \paragraph{Prefix} +% +% Can be either \texttt{file:} or \texttt{name:} and are used to select between +% filename based or font name based search mechanisms. +% Loading fonts based on filename is restricted to files found by +% \textsf{kpathsea} (typically in the \textsc{texmf} tree). Surrounding font +% name with square brackets is synonym to using \texttt{file:} prefix (for +% compatibility with \XeTeX). +% This is usually used for loading old \textsc{tfm} fonts. +% Accessing fonts by fontname allows loading system installed fonts as well as +% \textsc{texmf} ones, and requires a font names database that can be generated +% using the bundled |mkluatexfontdb.lua| script. +% \footnote{run |mkluatexfontdb.lua --help| for help and usage information} +% +% If no prefix is specified, then \texttt{file:} is assumed. +% +% \paragraph{Font name} +% +% Font name can be either a font filename or actual font name; based on the +% prefix specified. +% +% \paragraph{Font features} +% +% Font features are a semicolon separated list of items, which are either +% |key=value| font parameters, or switches to enable/disable certain font +% features, in the form of |+feat|/|-feat|. +% The supported keys are: +% \begin{description} +% \item [mode] \hfill \\ +% \textsf{luaotfload} has two OpenType processing modes; \texttt{base} mode +% which supports only a subset of OpenType features and works by mapping those +% features to traditional \TeX\ ligaturing and kerning mechanisms and is a bit +% faster, and \texttt{node} mode which, hopefully, supports OpenType fully and +% works by direct processing of node list at lua end and is a bit slower. +% Note that \texttt{node} mode doesn't work inside math. +% +% By default, \texttt{base} mode is used, however it is advisable to always +% enable \texttt{node} made, except for math fonts, otherwise many OpenType +% features will not function properly or even not work at all, especially for +% advanced scripts like Arabic. +% +% \item [script] \hfill \\ +% OpenType script string, default value is |dflt|. Some fonts don't assign +% features to |dflt| script, in this case script need to be set explicitly. +% +% \item [language] \hfill \\ OpenType language string, default value is |latn|. +% +% \item [featurefile] \hfill \\ +% feature files are textual representation of OpenType tables and can be used to +% extend OpenType features of the font on fly. The file name of the feature file +% is passed, then features defined in the file can be enabled/disabled like any +% other feature. The actual syntax is described at +% \url{http://fontforge.sourceforge.net/featurefile.html} and +% \url{http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}. +% +% For example, to set a |tkrn| feature from |mykern.fea| file: +% +% |\font\lmr=Latin Modern Roman:featurefile=mykern.fea;+tkrn| % -% \subsection{Con\TeX t files needed} +% \item [color] \hfill \\ +% font color, defined as a triplet of two-digit Hex RGB values, with optionally +% another value for the transparency (where |00| is completely transparent and +% |FF| is opaque.) % -% This package is a wrapper for several files taken from the Con\TeX t macro -% package. The philosophy is to let Con\TeX t do all the implementation and +% For example, to set text in semitransparent red: +% +% |\font\lmr=Latin Modern Roman:color=FF0000BB| +% +% \item [protrusion \& expansion] \hfill \\ +% Both keys control microtypographic features of the font, namely glyph +% protrusion and expansion. The value of the key is the name of predefined +% lua tables of protrusion and expansion values, see the bottom of +% \texttt{otfl-font-dum.lua} file for an example of such tables. The only +% predefined value is |default|. +% +% For example, to enable default protrusion\footnote{You also need to set +% |\pdfprotrudechars2 \pdfadjustspacing2| to activate protrusion and expansion, +% respectively. See PDF\TeX\ manual for details}: +% +% |\font\lmr=Latin Modern Roman:protrusion=default| +% +% \end{description} +% +% \subparagraph{Non-standard font features} +% \textsf{luaotfload} defines some additional font feature not defined in +% OpenType, currently three features are defined: +% +% \begin{itemize*} +% \item \texttt{anum}: replaces European numbers with eastern Arabic numbers or +% Persian numbers, depending on the value of |language|. +% \item \texttt{tlig}: applies legacy \TeX\ ligatures (|``''-- -- !` ?` <<>>|). +% \item \texttt{trep}: applies legacy \TeX\ replacements (|`'"|). +% \end{itemize*} +% +% \subsection{\ConTeXt\ files needed} +% +% This package is a wrapper for several files taken from the \ConTeXt\ macro +% package. The philosophy is to let \ConTeXt\ do all the implementation and % update these files from time to time. To do so we did not modify the files -% taken from Con\TeX t, we only changed their names to prevent name clashes. +% taken from \ConTeXt, we only changed their names to prevent name clashes. % You can thus update the font system of this package simply by updating the -% files taken from Con\TeX t, without (theorically) changing the \texttt{.sty} +% files taken from \ConTeXt, without (theoretically) changing the \texttt{.sty} % file nor the main \texttt{.lua} file. % -% The Con\TeX t files are renamed by adding the prefix \texttt{otfl-} to them -% (\texttt{otfl} as \texttt{OTF L}oad).The files are: +% The \ConTeXt\ files are renamed by adding the prefix \texttt{otfl-} to them +% (\texttt{otfl} as \texttt{OTF L}oad). The files are: % -% \begin{itemize} +% \begin{multicols}{3} +% \begin{itemize*} % \item \texttt{luat-dum.lua} % \item \texttt{data-con.lua} % \item \texttt{node-ini.lua} @@ -201,16 +316,26 @@ and the derived files % \item \texttt{font-xtx.lua} % \item \texttt{font-map.lua} % \item \texttt{font-dum.lua} -% \end{itemize} -% +% \end{itemize*} +% \end{multicols} +% % \subsection{Troubleshooting} % -% If you encounter problems with some fonts, please first update to the latest version of this package before reporting a bug, as this package is under active development. +% If you encounter problems with some fonts, please first update to the latest +% version of this package before reporting a bug, as this package is under +% active development. % -% A very common problem is the lack of features for some otf fonts even when specified. It can be related to the fact that some fonts do not provide features for the |dflt| script, which is the default one in this package, so you may have to specify the script in the command line, for example: +% A very common problem is the lack of features for some OpenType fonts +% even when specified. It can be related to the fact that some fonts do not +% provide features for the |dflt| script, which is the default one in this +% package, so you may have to specify the script in the command line, for +% example: % % |\font\myfont = MyFont.otf:script=latn;+liga;| % +% Also, some feature like contextual substitution, |calt|, will only work with +% |node| mode. +% % \section{\texttt{luaotfload.lua}} % % \iffalse @@ -220,138 +345,83 @@ and the derived files % First some usual initializations. % % \begin{macrocode} -luaotfload = { } +module('luaotfload', package.seeall) luaotfload.module = { name = "luaotfload", - version = 1.06, - date = "2010/02/22", + version = 1.07, + date = "2010/05/10", description = "ConTeXt font loading system.", author = "Elie Roux & Hans Hagen", copyright = "Elie Roux", license = "CC0" } -luatextra.provides_module(luaotfload.module) +luatexbase.provides_module(luaotfload.module) % \end{macrocode} % -% We load the Con\TeX t files with this function. It automatically adds the -% \texttt{otfl-} prefix to it, so that we call it with the actual Con\TeX t -% name. -% -% Some Con\TeX t files introduce incompatibilities with old Lua\TeX s, thus -% the old versions of these files are kept under the name |otfl-compat-...| -% and are called according to the version. The only file in this case -% is a |font-otf.lua| that, in version 2009.11.26 of Con\TeX t generates -% invalid PDFs with Lua\TeX{} older than version 0.45. +% Some helper functions. % % \begin{macrocode} +local format = string.format -function luaotfload.loadmodule(name, compat) - local tofind - if compat then - if tex.luatexversion < 45 then - tofind = 'otfl-compat-'..name - else - tofind = 'otfl-'..name - end - else - tofind = 'otfl-'..name - end - local found = kpse.find_file(tofind,"tex") - if not found then - luatextra.module_error('luaotfload', string.format('file %s not found.', tofind)) - return - end - luatextra.module_log('luaotfload', "loading file "..found) - dofile(found) +local function log(...) + luatexbase.module_log ('luaotfload', format(...)) +end + +local function error(...) + luatexbase.module_error ('luaotfload', format(...)) +end + +local function warning(...) + luatexbase.module_warning('luaotfload', format(...)) end % \end{macrocode} % -% The following functions are made to map Con\TeX t functions to -% \textsf{luaextra} functions. +% The minimal required \LuaTeX\ version. % % \begin{macrocode} -string.strip = string.stripspaces +local luatex_version = 60 -file = fpath -file.extname = fpath.suffix +if tex.luatexversion < luatex_version then + warning('LuaTeX v%.2f is old, v%.2f is recommended.', + tex.luatexversion/100, + luatex_version /100) +end % \end{macrocode} % -% These are small functions that are not already in \textsf{luatextra}. +% We load the \ConTeXt\ files with this function. It automatically adds the +% \texttt{otfl-} prefix to it, so that we call it with the actual \ConTeXt\ +% name. % % \begin{macrocode} -local splitters_s, splitters_m = { }, { } - -function lpeg.splitat(separator,single) - local splitter = (single and splitters_s[separator]) or splitters_m[separator] - if not splitter then - separator = lpeg.P(separator) - if single then - local other, any = lpeg.C((1 - separator)^0), lpeg.P(1) - splitter = other * (separator * lpeg.C(any^0) + "") - splitters_s[separator] = splitter - else - local other = lpeg.C((1 - separator)^0) - splitter = other * (separator * other)^0 - splitters_m[separator] = splitter - end - end - return splitter -end - -function table.compact(t) - if t then - for k,v in next, t do - if not next(v) then - t[k] = nil - end - end - end -end - -function table.sortedhashkeys(tab) -- fast one - local srt = { } - for key,_ in next, tab do - srt[#srt+1] = key - end - table.sort(srt) - return srt -end - -function table.reverse_hash(h) - local r = { } - for k,v in next, h do - r[v] = string.lower(string.gsub(k," ","")) - end - return r -end - -function table.reverse(t) - local tt = { } - if #t > 0 then - for i=#t,1,-1 do - tt[#tt+1] = t[i] - end +function luaotfload.loadmodule(name) + local tofind = 'otfl-'..name + local found = kpse.find_file(tofind,"tex") + if found then + log('loading file %s.', found) + dofile(found) + else + error('file %s not found.', tofind) end - return tt end % \end{macrocode} % % We start loading some lua files. These two are some code not used by -% Con\TeX t at all that allow other modules to be used, it provides some -% low-level Con\TeX t functions. +% \ConTeXt\ at all that allow other modules to be used, it provides some +% low-level \ConTeXt\ functions. % % \begin{macrocode} luaotfload.loadmodule('luat-dum.lua') -- not used in context at all +luaotfload.loadmodule('luat-ovr.lua') -- override some luat-dum functions luaotfload.loadmodule('data-con.lua') -- maybe some day we don't need this one % \end{macrocode} @@ -364,7 +434,7 @@ luaotfload.loadmodule('node-ini.lua') % \end{macrocode} % -% By default Con\TeX t takes some private attributes for internal use. With +% By default \ConTeXt\ takes some private attributes for internal use. With % Plain and \LaTeX\ we can't do so, we use |\newluaattribute|. This % functions overrides a function defined in the previous module that % returns the number of a private attribute. We allocate new attributes in @@ -375,9 +445,11 @@ luaotfload.loadmodule('node-ini.lua') % \begin{macrocode} function attributes.private(name) - local number = tex.attributenumber['otfl@'..name] - if not number then - luatextra.module_error('luaotfload', string.format('asking for attribute %s, but not declared. Please report to the maintainer of luaotfload.', name)) + local number = luatexbase.attributes['otfl@'..name] + if not number then + error('asking for attribute %s, but not declared. ' + ..'Please report to the maintainer of luaotfload.', + name) end return number end @@ -386,9 +458,9 @@ end % % Some more modules. We don't load neither \texttt{font-enc.lua} nor % \texttt{font-afm.lua} as it will never be used here. -% -% We also remove a warning from \texttt{font-fnt.lua} as it not relevant -% with Lua\TeX{}tra. +% +% We also remove a warning from \texttt{node-fnt.lua} as it is \ConTeXt\ +% specific. % % \begin{macrocode} @@ -402,78 +474,52 @@ luaotfload.loadmodule('node-dum.lua') luaotfload.loadmodule('font-ini.lua') luaotfload.loadmodule('font-tfm.lua') luaotfload.loadmodule('font-cid.lua') +luaotfload.loadmodule('font-map.lua') luaotfload.loadmodule('font-ott.lua') -luaotfload.loadmodule('font-otf.lua', 1) +luaotfload.loadmodule('font-otf.lua') luaotfload.loadmodule('font-otd.lua') luaotfload.loadmodule('font-oti.lua') luaotfload.loadmodule('font-otb.lua') luaotfload.loadmodule('font-otn.lua') luaotfload.loadmodule('font-ota.lua') luaotfload.loadmodule('font-otc.lua') - -% \end{macrocode} -% -% \texttt{font-def.lua} calls the function \texttt{callback.register} to -% register its callbacks. We override it with a dumb function so that it -% does not register any callback. We will register the callbacks later. -% -% \begin{macrocode} - -do - local temp = callback.register - callback.register = function (...) - return - end - luaotfload.loadmodule('font-def.lua') - callback.register = temp -end - +luaotfload.loadmodule('font-def.lua') luaotfload.loadmodule('font-xtx.lua') -luaotfload.loadmodule('font-map.lua') luaotfload.loadmodule('font-dum.lua') +luaotfload.loadmodule('font-nms.lua') +luaotfload.loadmodule('font-clr.lua') % \end{macrocode} % -% This is a small patch that prevents errors in some \LaTeX\ files. -% -% \begin{macrocode} - -fonts.enc.known = {} - -% \end{macrocode} -% -% We have to register a function in the |find_vf_file| callback in order to make everything work. -% -% \begin{macrocode} - -function luaotfload.find_vf_file(name) - name = file.removesuffix(file.basename(name)) - local result = kpse.find_file(name, "vf") or "" - if result == "" then - result = kpse.find_file(name, "ovf") or "" - end - return result -end - -% \end{macrocode} -% -% Finally two functions +% Finally two functions % % \begin{macrocode} function luaotfload.register_callbacks() - callback.add('pre_linebreak_filter', nodes.simple_font_handler, 'luaotfload.pre_linebreak_filter') - callback.add('hpack_filter', nodes.simple_font_handler, 'luaotfload.hpack_filter') - callback.reset('define_font') - callback.add('define_font', fonts.define.read, 'luaotfload.define_font', 1) - callback.add('find_vf_file', luaotfload.find_vf_file, 'luaotfload.find_vf_file') + luatexbase.add_to_callback('pre_linebreak_filter', + nodes.simple_font_handler, + 'luaotfload.pre_linebreak_filter') + luatexbase.add_to_callback('hpack_filter', + nodes.simple_font_handler, + 'luaotfload.hpack_filter') + luatexbase.reset_callback('define_font') + luatexbase.add_to_callback('define_font', + fonts.define.read, + 'luaotfload.define_font', 1) + luatexbase.add_to_callback('find_vf_file', + fonts.vf.find, + 'luaotfload.find_vf_file') end function luaotfload.unregister_callbacks() - callback.remove('pre_linebreak_filter', 'luaotfload.pre_linebreak_filter') - callback.remove('hpack_filter', 'luaotfload.hpack_filter') - callback.remove('define_font', 'luaotfload.define_font') - callback.remove('find_vf_file', 'luaotfload.find_vf_file') + luatexbase.remove_from_callback('pre_linebreak_filter', + 'luaotfload.pre_linebreak_filter') + luatexbase.remove_from_callback('hpack_filter', + 'luaotfload.hpack_filter') + luatexbase.remove_from_callback('define_font', + 'luaotfload.define_font') + luatexbase.remove_from_callback('find_vf_file', + 'luaotfload.find_vf_file') end % \end{macrocode} % \iffalse @@ -497,7 +543,7 @@ end \else \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{luaotfload}% - [2010/02/22 v1.06b ConTeXt font loading system] + [2010/05/10 v1.07 ConTeXt font loading system] \RequirePackage{luatextra} \fi @@ -532,11 +578,11 @@ end % \begin{macrocode} \def\otfl@off{ -\luadirect{luaotfload.unregister_callbacks()} +\directlua{luaotfload.unregister_callbacks()} } \def\otfl@on{ -\luadirect{luaotfload.register_callbacks()} +\directlua{luaotfload.register_callbacks()} } % \end{macrocode} diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua index 2c63bd9a22c..6e76fa57f8f 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua @@ -15,106 +15,59 @@ -- and the derived files -- luaotfload.sty, luaotfload.lua -- -luaotfload = { } +module('luaotfload', package.seeall) luaotfload.module = { name = "luaotfload", - version = 1.06, - date = "2010/02/22", + version = 1.07, + date = "2010/05/10", description = "ConTeXt font loading system.", author = "Elie Roux & Hans Hagen", copyright = "Elie Roux", license = "CC0" } -luatextra.provides_module(luaotfload.module) +luatexbase.provides_module(luaotfload.module) -function luaotfload.loadmodule(name, compat) - local tofind - if compat then - if tex.luatexversion < 45 then - tofind = 'otfl-compat-'..name - else - tofind = 'otfl-'..name - end - else - tofind = 'otfl-'..name - end - local found = kpse.find_file(tofind,"tex") - if not found then - luatextra.module_error('luaotfload', string.format('file %s not found.', tofind)) - return - end - luatextra.module_log('luaotfload', "loading file "..found) - dofile(found) -end - - -string.strip = string.stripspaces - -file = fpath -file.extname = fpath.suffix +local format = string.format - -local splitters_s, splitters_m = { }, { } - -function lpeg.splitat(separator,single) - local splitter = (single and splitters_s[separator]) or splitters_m[separator] - if not splitter then - separator = lpeg.P(separator) - if single then - local other, any = lpeg.C((1 - separator)^0), lpeg.P(1) - splitter = other * (separator * lpeg.C(any^0) + "") - splitters_s[separator] = splitter - else - local other = lpeg.C((1 - separator)^0) - splitter = other * (separator * other)^0 - splitters_m[separator] = splitter - end - end - return splitter +local function log(...) + luatexbase.module_log ('luaotfload', format(...)) end -function table.compact(t) - if t then - for k,v in next, t do - if not next(v) then - t[k] = nil - end - end - end +local function error(...) + luatexbase.module_error ('luaotfload', format(...)) end -function table.sortedhashkeys(tab) -- fast one - local srt = { } - for key,_ in next, tab do - srt[#srt+1] = key - end - table.sort(srt) - return srt +local function warning(...) + luatexbase.module_warning('luaotfload', format(...)) end -function table.reverse_hash(h) - local r = { } - for k,v in next, h do - r[v] = string.lower(string.gsub(k," ","")) - end - return r + +local luatex_version = 60 + +if tex.luatexversion < luatex_version then + warning('LuaTeX v%.2f is old, v%.2f is recommended.', + tex.luatexversion/100, + luatex_version /100) end -function table.reverse(t) - local tt = { } - if #t > 0 then - for i=#t,1,-1 do - tt[#tt+1] = t[i] - end + +function luaotfload.loadmodule(name) + local tofind = 'otfl-'..name + local found = kpse.find_file(tofind,"tex") + if found then + log('loading file %s.', found) + dofile(found) + else + error('file %s not found.', tofind) end - return tt end luaotfload.loadmodule('luat-dum.lua') -- not used in context at all +luaotfload.loadmodule('luat-ovr.lua') -- override some luat-dum functions luaotfload.loadmodule('data-con.lua') -- maybe some day we don't need this one @@ -122,9 +75,11 @@ luaotfload.loadmodule('node-ini.lua') function attributes.private(name) - local number = tex.attributenumber['otfl@'..name] + local number = luatexbase.attributes['otfl@'..name] if not number then - luatextra.module_error('luaotfload', string.format('asking for attribute %s, but not declared. Please report to the maintainer of luaotfload.', name)) + error('asking for attribute %s, but not declared. ' + ..'Please report to the maintainer of luaotfload.', + name) end return number end @@ -140,56 +95,47 @@ luaotfload.loadmodule('node-dum.lua') luaotfload.loadmodule('font-ini.lua') luaotfload.loadmodule('font-tfm.lua') luaotfload.loadmodule('font-cid.lua') +luaotfload.loadmodule('font-map.lua') luaotfload.loadmodule('font-ott.lua') -luaotfload.loadmodule('font-otf.lua', 1) +luaotfload.loadmodule('font-otf.lua') luaotfload.loadmodule('font-otd.lua') luaotfload.loadmodule('font-oti.lua') luaotfload.loadmodule('font-otb.lua') luaotfload.loadmodule('font-otn.lua') luaotfload.loadmodule('font-ota.lua') luaotfload.loadmodule('font-otc.lua') - - -do - local temp = callback.register - callback.register = function (...) - return - end - luaotfload.loadmodule('font-def.lua') - callback.register = temp -end - +luaotfload.loadmodule('font-def.lua') luaotfload.loadmodule('font-xtx.lua') -luaotfload.loadmodule('font-map.lua') luaotfload.loadmodule('font-dum.lua') - - -fonts.enc.known = {} - - -function luaotfload.find_vf_file(name) - name = file.removesuffix(file.basename(name)) - local result = kpse.find_file(name, "vf") or "" - if result == "" then - result = kpse.find_file(name, "ovf") or "" - end - return result -end +luaotfload.loadmodule('font-nms.lua') +luaotfload.loadmodule('font-clr.lua') function luaotfload.register_callbacks() - callback.add('pre_linebreak_filter', nodes.simple_font_handler, 'luaotfload.pre_linebreak_filter') - callback.add('hpack_filter', nodes.simple_font_handler, 'luaotfload.hpack_filter') - callback.reset('define_font') - callback.add('define_font', fonts.define.read, 'luaotfload.define_font', 1) - callback.add('find_vf_file', luaotfload.find_vf_file, 'luaotfload.find_vf_file') + luatexbase.add_to_callback('pre_linebreak_filter', + nodes.simple_font_handler, + 'luaotfload.pre_linebreak_filter') + luatexbase.add_to_callback('hpack_filter', + nodes.simple_font_handler, + 'luaotfload.hpack_filter') + luatexbase.reset_callback('define_font') + luatexbase.add_to_callback('define_font', + fonts.define.read, + 'luaotfload.define_font', 1) + luatexbase.add_to_callback('find_vf_file', + fonts.vf.find, + 'luaotfload.find_vf_file') end function luaotfload.unregister_callbacks() - callback.remove('pre_linebreak_filter', 'luaotfload.pre_linebreak_filter') - callback.remove('hpack_filter', 'luaotfload.hpack_filter') - callback.remove('define_font', 'luaotfload.define_font') - callback.remove('find_vf_file', 'luaotfload.find_vf_file') + luatexbase.remove_from_callback('pre_linebreak_filter', + 'luaotfload.pre_linebreak_filter') + luatexbase.remove_from_callback('hpack_filter', + 'luaotfload.hpack_filter') + luatexbase.remove_from_callback('define_font', + 'luaotfload.define_font') + luatexbase.remove_from_callback('find_vf_file', + 'luaotfload.find_vf_file') end -- -- End of File `luaotfload.lua'. diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty index 839b62e8bee..e2ec4bba711 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty @@ -23,7 +23,7 @@ \else \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{luaotfload}% - [2010/02/22 v1.06b ConTeXt font loading system] + [2010/05/10 v1.07 ConTeXt font loading system] \RequirePackage{luatextra} \fi @@ -46,11 +46,11 @@ \def\otfl@off{ -\luadirect{luaotfload.unregister_callbacks()} +\directlua{luaotfload.unregister_callbacks()} } \def\otfl@on{ -\luadirect{luaotfload.register_callbacks()} +\directlua{luaotfload.register_callbacks()} } diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-compat-font-otf.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-compat-font-otf.lua deleted file mode 100644 index be1873898a5..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-compat-font-otf.lua +++ /dev/null @@ -1,1629 +0,0 @@ -if not modules then modules = { } end modules ['font-otf'] = { - version = 1.001, - comment = "companion to font-ini.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -local utf = unicode.utf8 - -local concat, getn, utfbyte = table.concat, table.getn, utf.byte -local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip -local type, next, tonumber, tostring = type, next, tonumber, tostring - -local trace_private = false trackers.register("otf.private", function(v) trace_private = v end) -local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) -local trace_features = false trackers.register("otf.features", function(v) trace_features = v end) -local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) -local trace_sequences = false trackers.register("otf.sequences", function(v) trace_sequences = v end) -local trace_math = false trackers.register("otf.math", function(v) trace_math = v end) -local trace_unimapping = false trackers.register("otf.unimapping", function(v) trace_unimapping = v end) - ---~ trackers.enable("otf.loading") - -local zwnj = 0x200C -local zwj = 0x200D - ---[[ldx-- -

The fontforge table has organized lookups in a certain way. A first implementation -of this code was organized featurewise: information related to features was -collected and processing boiled down to a run over the features. The current -implementation honors the order in the main feature table. Since we can reorder this -table as we want, we can eventually support several models of processing. We kept -the static as well as dynamic feature processing, because it had proved to be -rather useful. The formerly three loop variants have beem discarded but will -reapear at some time.

- - -we loop over all lookups -for each lookup we do a run over the list of glyphs -but we only process them for features that are enabled -if we're dealing with a contextual lookup, we loop over all contexts -in that loop we quit at a match and then process the list of sublookups -we always continue after the match - - -

In we do this for each font that is used in a list, so in -practice we have quite some nested loops.

- -

We process the whole list and then consult the glyph nodes. An alternative approach -is to collect strings of characters using the same font including spaces (because some -lookups involve spaces). However, we then need to reconstruct the list which is no fun. -Also, we need to carry quite some information, like attributes, so eventually we don't -gain much (if we gain something at all).

- -

Another consideration has been to operate on sublists (subhead, subtail) but again -this would complicate matters as we then neext to keep track of a changing subhead -and subtail. On the other hand, this might save some runtime. The number of changes -involved is not that large. This only makes sense when we have many fonts in a list -and don't change to frequently.

---ldx]]-- - -fonts = fonts or { } -fonts.otf = fonts.otf or { } -fonts.tfm = fonts.tfm or { } - -local otf = fonts.otf -local tfm = fonts.tfm - -local fontdata = fonts.ids - -otf.tables = otf.tables or { } -- defined in font-ott.lua -otf.meanings = otf.meanings or { } -- defined in font-ott.lua -otf.tables.features = otf.tables.features or { } -- defined in font-ott.lua -otf.tables.languages = otf.tables.languages or { } -- defined in font-ott.lua -otf.tables.scripts = otf.tables.scripts or { } -- defined in font-ott.lua - -otf.features = otf.features or { } -otf.features.list = otf.features.list or { } -otf.features.default = otf.features.default or { } - -otf.enhancers = otf.enhancers or { } -otf.glists = { "gsub", "gpos" } - -otf.version = 2.635 -- beware: also sync font-mis.lua -otf.pack = true -- beware: also sync font-mis.lua -otf.syncspace = true -otf.notdef = false -otf.cache = containers.define("fonts", "otf", otf.version, true) -otf.cleanup_aat = false -- only context - ---[[ldx-- -

We start with a lot of tables and related functions.

---ldx]]-- - -otf.tables.global_fields = table.tohash { - "lookups", - "glyphs", - "subfonts", - "luatex", - "pfminfo", - "cidinfo", - "tables", - "names", - "unicodes", - "names", ---~ "math", - "anchor_classes", - "kern_classes", - "gpos", - "gsub" -} - -otf.tables.valid_fields = { - "anchor_classes", - "ascent", - "cache_version", - "cidinfo", - "copyright", - "creationtime", - "descent", - "design_range_bottom", - "design_range_top", - "design_size", - "encodingchanged", - "extrema_bound", - "familyname", - "fontname", - "fontstyle_id", - "fontstyle_name", - "fullname", - "glyphs", - "hasvmetrics", - "head_optimized_for_cleartype", - "horiz_base", - "issans", - "isserif", - "italicangle", - "kerns", - "lookups", - -- "luatex", - "macstyle", - "modificationtime", - "onlybitmaps", - "origname", - "os2_version", - "pfminfo", - "private", - "serifcheck", - "sfd_version", - -- "size", - "strokedfont", - "strokewidth", - "subfonts", - "table_version", - -- "tables", - -- "ttf_tab_saved", - "ttf_tables", - "uni_interp", - "uniqueid", - "units_per_em", - "upos", - "use_typo_metrics", - "uwidth", - "validation_state", - "verbose", - "version", - "vert_base", - "weight", - "weight_width_slope_only", - "xuid", -} - ---[[ldx-- -

Here we go.

---ldx]]-- - -local function load_featurefile(ff,featurefile) - if featurefile then - featurefile = resolvers.find_file(file.addsuffix(featurefile,'fea')) -- "FONTFEATURES" - if featurefile and featurefile ~= "" then - if trace_loading then - logs.report("load otf", "featurefile: %s", featurefile) - end - fontloader.apply_featurefile(ff, featurefile) - end - end -end - -function otf.enhance(name,data,filename,verbose) - local enhancer = otf.enhancers[name] - if enhancer then - if (verbose ~= nil and verbose) or trace_loading then - logs.report("load otf","enhance: %s",name) - end - enhancer(data,filename) - end -end - -local enhancers = { - -- pack and unpack are handled separately; they might even be moved - -- away from the enhancers namespace - "patch bugs", - "merge cid fonts", "prepare unicode", "cleanup ttf tables", "compact glyphs", "reverse coverage", - "cleanup aat", "enrich with features", "add some missing characters", - "reorganize mark classes", - "reorganize kerns", -- moved here - "flatten glyph lookups", "flatten anchor tables", "flatten feature tables", - "prepare luatex tables", - "analyse features", "rehash features", - "analyse anchors", "analyse marks", "analyse unicodes", "analyse subtables", - "check italic correction","check math", - "share widths", - "strip not needed data", - "migrate metadata", -} - -function otf.load(filename,format,sub,featurefile) - local name = file.basename(file.removesuffix(filename)) - if featurefile then - name = name .. "@" .. file.removesuffix(file.basename(featurefile)) - end - if sub == "" then sub = false end - local hash = name - if sub then - hash = hash .. "-" .. sub - end - hash = containers.cleanname(hash) - local data = containers.read(otf.cache(), hash) - local size = lfs.attributes(filename,"size") or 0 - if not data or data.verbose ~= fonts.verbose or data.size ~= size then - logs.report("load otf","loading: %s (hash: %s)",filename,hash) - local ff, messages - if sub then - ff, messages = fontloader.open(filename,sub) - else - ff, messages = fontloader.open(filename) - end - if trace_loading and messages and #messages > 0 then - for m=1,#messages do - logs.report("load otf","warning: %s",messages[m]) - end - end - if ff then - load_featurefile(ff,featurefile) - data = fontloader.to_table(ff) - fontloader.close(ff) - if data then - logs.report("load otf","file size: %s", size) - logs.report("load otf","enhancing ...") - for e=1,#enhancers do - otf.enhance(enhancers[e],data,filename) - end - if otf.pack and not fonts.verbose then - otf.enhance("pack",data,filename) - end - data.size = size - data.verbose = fonts.verbose - logs.report("load otf","saving in cache: %s",filename) - data = containers.write(otf.cache(), hash, data) - collectgarbage("collect") - data = containers.read(otf.cache(), hash) -- this frees the old table and load the sparse one - collectgarbage("collect") - else - logs.report("load otf","loading failed (table conversion error)") - end - else - logs.report("load otf","loading failed (file read error)") - end - end - if data then - otf.enhance("unpack",data,filename,false) -- no message here - otf.add_dimensions(data) - if trace_sequences then - otf.show_feature_order(data,filename) - end - end - return data -end - -function otf.add_dimensions(data) - -- todo: forget about the width if it's the defaultwidth (saves mem) - -- we could also build the marks hash here (instead of storing it) - if data then - local force = otf.notdef - local luatex = data.luatex - local defaultwidth = luatex.defaultwidth or 0 - local defaultheight = luatex.defaultheight or 0 - local defaultdepth = luatex.defaultdepth or 0 - for _, d in next, data.glyphs do - local bb, wd = d.boundingbox, d.width - if not wd then - d.width = defaultwidth - elseif wd ~= 0 and d.class == "mark" then - d.width = -wd - end - if force and not d.name then - d.name = ".notdef" - end - if bb then - local ht, dp = bb[4], -bb[2] - if ht == 0 or ht < 0 then - -- no need to set it and no negative heights, nil == 0 - else - d.height = ht - end - if dp == 0 or dp < 0 then - -- no negative depths and no negative depths, nil == 0 - else - d.depth = dp - end - end - end - end -end - -function otf.show_feature_order(otfdata,filename) - local sequences = otfdata.luatex.sequences - if sequences and #sequences > 0 then - if trace_loading then - logs.report("otf check","font %s has %s sequences",filename,#sequences) - logs.report("otf check"," ") - end - for nos=1,#sequences do - local sequence = sequences[nos] - local typ = sequence.type or "no-type" - local name = sequence.name or "no-name" - local subtables = sequence.subtables or { "no-subtables" } - local features = sequence.features - if trace_loading then - logs.report("otf check","%3i %-15s %-20s [%s]",nos,name,typ,concat(subtables,",")) - end - if features then - for feature, scripts in next, features do - local tt = { } - for script, languages in next, scripts do - local ttt = { } - for language, _ in next, languages do - ttt[#ttt+1] = language - end - tt[#tt+1] = format("[%s: %s]",script,concat(ttt," ")) - end - if trace_loading then - logs.report("otf check"," %s: %s",feature,concat(tt," ")) - end - end - end - end - if trace_loading then - logs.report("otf check","\n") - end - elseif trace_loading then - logs.report("otf check","font %s has no sequences",filename) - end -end - --- todo: normalize, design_size => designsize - -otf.enhancers["reorganize mark classes"] = function(data,filename) - if data.mark_classes then - local unicodes = data.luatex.unicodes - local reverse = { } - for name, class in next, data.mark_classes do - local t = { } - for s in gmatch(class,"[^ ]+") do - t[unicodes[s]] = true - end - reverse[name] = t - end - data.luatex.markclasses = reverse - data.mark_classes = nil - end -end - -otf.enhancers["prepare luatex tables"] = function(data,filename) - data.luatex = data.luatex or { } - local luatex = data.luatex - luatex.filename = filename - luatex.version = otf.version - luatex.creator = "context mkiv" -end - -otf.enhancers["cleanup aat"] = function(data,filename) - if otf.cleanup_aat then - end -end - -local function analyze_features(g, features) - if g then - local t, done = { }, { } - for k=1,#g do - local f = features or g[k].features - if f then - for k=1,#f do - -- scripts and tag - local tag = f[k].tag - if not done[tag] then - t[#t+1] = tag - done[tag] = true - end - end - end - end - if #t > 0 then - return t - end - end - return nil -end - -otf.enhancers["analyse features"] = function(data,filename) - -- local luatex = data.luatex - -- luatex.gposfeatures = analyze_features(data.gpos) - -- luatex.gsubfeatures = analyze_features(data.gsub) -end - -otf.enhancers["rehash features"] = function(data,filename) - local features = { } - data.luatex.features = features - for k, what in next, otf.glists do - local dw = data[what] - if dw then - local f = { } - features[what] = f - for i=1,#dw do - local d= dw[i] - local dfeatures = d.features - if dfeatures then - for i=1,#dfeatures do - local df = dfeatures[i] - local tag = strip(lower(df.tag)) - local ft = f[tag] if not ft then ft = {} f[tag] = ft end - local dscripts = df.scripts - for script, languages in next, dscripts do - script = strip(lower(script)) - local fts = ft[script] if not fts then fts = {} ft[script] = fts end - for i=1,#languages do - fts[strip(lower(languages[i]))] = true - end - end - end - end - end - end - end -end - -otf.enhancers["analyse anchors"] = function(data,filename) - local classes = data.anchor_classes - local luatex = data.luatex - local anchor_to_lookup, lookup_to_anchor = { }, { } - luatex.anchor_to_lookup, luatex.lookup_to_anchor = anchor_to_lookup, lookup_to_anchor - if classes then - for c=1,#classes do - local class = classes[c] - local anchor = class.name - local lookups = class.lookup - if type(lookups) ~= "table" then - lookups = { lookups } - end - local a = anchor_to_lookup[anchor] - if not a then a = { } anchor_to_lookup[anchor] = a end - for l=1,#lookups do - local lookup = lookups[l] - local l = lookup_to_anchor[lookup] - if not l then l = { } lookup_to_anchor[lookup] = l end - l[anchor] = true - a[lookup] = true - end - end - end -end - -otf.enhancers["analyse marks"] = function(data,filename) - local glyphs = data.glyphs - local marks = { } - data.luatex.marks = marks - for unicode, index in next, data.luatex.indices do - local glyph = glyphs[index] - if glyph.class == "mark" then - marks[unicode] = true - end - end -end - -local separator = lpeg.S("_.") -local other = lpeg.C((1 - separator)^1) -local ligsplitter = lpeg.Ct(other * (separator * other)^0) - ---~ print(table.serialize(ligsplitter:match("this"))) ---~ print(table.serialize(ligsplitter:match("this.that"))) ---~ print(table.serialize(ligsplitter:match("japan1.123"))) ---~ print(table.serialize(ligsplitter:match("such_so_more"))) ---~ print(table.serialize(ligsplitter:match("such_so_more.that"))) - -otf.enhancers["analyse unicodes"] = function(data,filename) - local tounicode16, tounicode16sequence = fonts.map.tounicode16, fonts.map.tounicode16sequence - local unicodes = data.luatex.unicodes - -- we need to move this code - unicodes['space'] = unicodes['space'] or 32 -- handly later on - unicodes['hyphen'] = unicodes['hyphen'] or 45 -- handly later on - unicodes['zwj'] = unicodes['zwj'] or zwj -- handly later on - unicodes['zwnj'] = unicodes['zwnj'] or zwnj -- handly later on - -- the tounicode mapping is sparse and only needed for alternatives - local tounicode, originals, ns, nl, private, unknown = { }, { }, 0, 0, fonts.private, format("%04X",utfbyte("?")) - data.luatex.tounicode, data.luatex.originals = tounicode, originals - local lumunic, uparser, oparser - if false then -- will become an option - lumunic = fonts.map.load_lum_table(filename) - lumunic = lumunic and lumunic.tounicode - end - local cidinfo, cidnames, cidcodes = data.cidinfo - local usedmap = cidinfo and cidinfo.usedname - usedmap = usedmap and fonts.cid.map[usedmap] - if usedmap then - oparser = usedmap and fonts.map.make_name_parser(cidinfo.ordering) - cidnames = usedmap.names - cidcodes = usedmap.unicodes - end - uparser = fonts.map.make_name_parser() - local aglmap = fonts.map and fonts.map.agl_to_unicode - for index, glyph in next, data.glyphs do - local name, unic = glyph.name, glyph.unicode or -1 -- play safe - if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then - local unicode = lumunic and lumunic[name] - if unicode then - originals[index], tounicode[index], ns = unicode, tounicode16(unicode), 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 = oparser:match(name) - if foundindex then - unicode = cidcodes[foundindex] -- name to number - if not unicode then - local reference = cidnames[foundindex] -- number to name - if reference then - local foundindex = oparser:match(reference) - if foundindex then - unicode = cidcodes[foundindex] - if unicode then - originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 - end - end - if not unicode then - local foundcodes, multiple = uparser:match(reference) - if foundcodes then - if multiple then - originals[index], tounicode[index], nl, unicode = foundcodes, tounicode16sequence(foundcodes), nl + 1, true - else - originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes - end - end - end - end - end - end - end - -- a.whatever or a_b_c.whatever or a_b_c (no numbers) - if not unicode then - local split = ligsplitter:match(name) - local nplit = (split and #split) or 0 - if nplit == 0 then - -- skip - elseif nplit == 1 then - local base = split[1] - unicode = unicodes[base] or (agl and agl[base]) - if unicode then - if type(unicode) == "table" then - unicode = unicode[1] - end - originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 - end - else - local t = { } - for l=1,nplit do - local base = split[l] - local u = unicodes[base] or (agl and agl[base]) - if not u then - break - elseif type(u) == "table" then - t[#t+1] = u[1] - else - t[#t+1] = u - end - end - if #t > 0 then -- done then - originals[index], tounicode[index], nl, unicode = t, tounicode16sequence(t), nl + 1, true - end - end - end - -- last resort - if not unicode then - local foundcodes, multiple = uparser:match(name) - if foundcodes then - if multiple then - originals[index], tounicode[index], nl, unicode = foundcodes, tounicode16sequence(foundcodes), nl + 1, true - else - originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes - end - end - end - if not unicode then - originals[index], tounicode[index] = 0xFFFD, "FFFD" - end - end - end - if trace_unimapping then - for index, glyph in table.sortedpairs(data.glyphs) do - local toun, name, unic = tounicode[index], glyph.name, glyph.unicode or -1 -- play safe - if toun then - logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun) - else - logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic) - end - end - end - if trace_loading and (ns > 0 or nl > 0) then - logs.report("load otf","enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns) - end -end - -otf.enhancers["analyse subtables"] = function(data,filename) - data.luatex = data.luatex or { } - local luatex = data.luatex - local sequences = { } - local lookups = { } - luatex.sequences = sequences - luatex.lookups = lookups - for _, g in next, { data.gsub, data.gpos } do - for k=1,#g do - local gk = g[k] - local typ = gk.type - if typ == "gsub_contextchain" or typ == "gpos_contextchain" then - gk.chain = 1 - elseif typ == "gsub_reversecontextchain" or typ == "gpos_reversecontextchain" then - gk.chain = -1 - else - gk.chain = 0 - end - local features = gk.features - if features then - sequences[#sequences+1] = gk - -- scripts, tag, ismac - local t = { } - for f=1,#features do - local feature = features[f] - local hash = { } - -- only script and langs matter - for s, languages in next, feature.scripts do - s = lower(s) - local h = hash[s] - if not h then h = { } hash[s] = h end - for l=1,#languages do - h[strip(lower(languages[l]))] = true - end - end - t[feature.tag] = hash - end - gk.features = t - else - lookups[gk.name] = gk - gk.name = nil - end - local subtables = gk.subtables - if subtables then - local t = { } - for s=1,#subtables do - local subtable = subtables[s] - local name = subtable.name - t[#t+1] = name - end - gk.subtables = t - end - local flags = gk.flags - if flags then - gk.flags = { -- forcing false packs nicer - (flags.ignorecombiningmarks and "mark") or false, - (flags.ignoreligatures and "ligature") or false, - (flags.ignorebaseglyphs and "base") or false, - flags.r2l or false, - } - if flags.mark_class then - gk.markclass = luatex.markclasses[flags.mark_class] - end - end - end - end -end - -otf.enhancers["merge cid fonts"] = function(data,filename) - -- we can also move the names to data.luatex.names which might - -- save us some more memory (at the cost of harder tracing) - if data.subfonts and table.is_empty(data.glyphs) then - local cidinfo = data.cidinfo - local verbose = fonts.verbose - if cidinfo.registry then - local cidmap, cidname = fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) - if cidmap then - cidinfo.usedname = cidmap.usedname - local glyphs, uni_to_int, int_to_uni, nofnames, nofunicodes = { }, { }, { }, 0, 0 - local unicodes, names = cidmap.unicodes, cidmap.names - for n, subfont in next, data.subfonts do - for index, g in next, subfont.glyphs do - if not next(g) then - -- dummy entry - else - local unicode, name = unicodes[index], names[index] - g.cidindex = n - g.boundingbox = g.boundingbox -- or zerobox - g.name = g.name or name or "unknown" - if unicode then - uni_to_int[unicode] = index - int_to_uni[index] = unicode - nofunicodes = nofunicodes + 1 - g.unicode = unicode - elseif name then - nofnames = nofnames + 1 - g.unicode = -1 - end - glyphs[index] = g - end - end - subfont.glyphs = nil - end - if trace_loading then - logs.report("load otf","cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames) - end - data.glyphs = glyphs - data.map = data.map or { } - data.map.map = uni_to_int - data.map.backmap = int_to_uni - elseif trace_loading then - logs.report("load otf","unable to remap cid font, missing cid file for %s",filename) - end - elseif trace_loading then - logs.report("load otf","font %s has no glyphs",filename) - end - end -end - -otf.enhancers["prepare unicode"] = function(data,filename) - local luatex = data.luatex - if not luatex then luatex = { } data.luatex = luatex end - local indices, unicodes, multiples, internals = { }, { }, { }, { } - local glyphs = data.glyphs - local mapmap = data.map - if not mapmap then - logs.report("load otf","no map in %s",filename) - mapmap = { } - data.map = { map = mapmap } - elseif not mapmap.map then - logs.report("load otf","no unicode map in %s",filename) - mapmap = { } - data.map.map = mapmap - else - mapmap = mapmap.map - end - local criterium = fonts.private - local private = fonts.private - for index, glyph in next, glyphs do - if index > 0 then - local name = glyph.name - if name then - local unicode = glyph.unicode - if unicode == -1 or unicode >= criterium then - glyph.unicode = private - indices[private] = index - unicodes[name] = private - internals[index] = true - if trace_private then - logs.report("load otf","enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",name,index,private) - end - private = private + 1 - else - indices[unicode] = index - unicodes[name] = unicode - end - end - end - end - -- beware: the indices table is used to initialize the tfm table - for unicode, index in next, mapmap do - if not internals[index] then - local name = glyphs[index].name - if name then - local un = unicodes[name] - if not un then - unicodes[name] = unicode -- or 0 - elseif type(un) == "number" then - if un ~= unicode then - multiples[#multiples+1] = name - unicodes[name] = { un, unicode } - indices[unicode] = index - end - else - local ok = false - for u=1,#un do - if un[u] == unicode then - ok = true - break - end - end - if not ok then - multiples[#multiples+1] = name - un[#un+1] = unicode - indices[unicode] = index - end - end - end - end - end - if trace_loading then - if #multiples > 0 then - logs.report("load otf","%s glyph are reused: %s",#multiples, concat(multiples," ")) - else - logs.report("load otf","no glyph are reused") - end - end - luatex.indices = indices - luatex.unicodes = unicodes - luatex.private = private -end - -otf.enhancers["cleanup ttf tables"] = function(data,filename) - local ttf_tables = data.ttf_tables - if ttf_tables then - for k=1,#ttf_tables do - if ttf_tables[k].data then ttf_tables[k].data = "deleted" end - end - end - data.ttf_tab_saved = nil -end - -otf.enhancers["compact glyphs"] = function(data,filename) - table.compact(data.glyphs) -- needed? - if data.subfonts then - for _, subfont in next, data.subfonts do - table.compact(subfont.glyphs) -- needed? - end - end -end - -otf.enhancers["reverse coverage"] = function(data,filename) - -- we prefer the before lookups in a normal order - if data.lookups then - for _, v in next, data.lookups do - if v.rules then - for _, vv in next, v.rules do - local c = vv.coverage - if c and c.before then - c.before = table.reverse(c.before) - end - end - end - end - end -end - -otf.enhancers["check italic correction"] = function(data,filename) - local glyphs = data.glyphs - local ok = false - for index, glyph in next, glyphs do - local ic = glyph.italic_correction - if ic then - if ic ~= 0 then - glyph.italic = ic - end - glyph.italic_correction = nil - ok = true - end - end - -- we can use this to avoid calculations - otf.tables.valid_fields[#otf.tables.valid_fields+1] = "has_italic" - data.has_italic = true -end - -otf.enhancers["check math"] = function(data,filename) - if data.math then - -- we move the math stuff into a math subtable because we then can - -- test faster in the tfm copy - local glyphs = data.glyphs - local unicodes = data.luatex.unicodes - for index, glyph in next, glyphs do - local mk = glyph.mathkern - local hv = glyph.horiz_variants - local vv = glyph.vert_variants - if mk or hv or vv then - local math = { } - glyph.math = math - if mk then - for k, v in next, mk do - if not next(v) then - mk[k] = nil - end - end - math.kerns = mk - glyph.mathkern = nil - end - if hv then - math.horiz_variants = hv.variants - local p = hv.parts - if p then - if #p>0 then - for i=1,#p do - local pi = p[i] - pi.glyph = unicodes[pi.component] or 0 - end - math.horiz_parts = p - end - end - local ic = hv.italic_correction - if ic and ic ~= 0 then - math.horiz_italic_correction = ic - end - glyph.horiz_variants = nil - end - if vv then - local uc = unicodes[index] - math.vert_variants = vv.variants - local p = vv.parts - if p then - if #p>0 then - for i=1,#p do - local pi = p[i] - pi.glyph = unicodes[pi.component] or 0 - end - math.vert_parts = p - end - end - local ic = vv.italic_correction - if ic and ic ~= 0 then - math.vert_italic_correction = ic - end - glyph.vert_variants = nil - end - local ic = glyph.italic_correction - if ic then - if ic ~= 0 then - math.italic_correction = ic - end - glyph.italic_correction = nil - end - end - end - end -end - -otf.enhancers["share widths"] = function(data,filename) - local glyphs = data.glyphs - local widths = { } - for index, glyph in next, glyphs do - local width = glyph.width - widths[width] = (widths[width] or 0) + 1 - end - -- share width for cjk fonts - local wd, most = 0, 1 - for k,v in next, widths do - if v > most then - wd, most = k, v - end - end - if most > 1000 then - if trace_loading then - logs.report("load otf", "most common width: %s (%s times), sharing (cjk font)",wd,most) - end - for k, v in next, glyphs do - if v.width == wd then - v.width = nil - end - end - data.luatex.defaultwidth = wd - end -end - --- kern: ttf has a table with kerns - -otf.enhancers["reorganize kerns"] = function(data,filename) - local glyphs, mapmap, unicodes = data.glyphs, data.luatex.indices, data.luatex.unicodes - local mkdone = false - for index, glyph in next, data.glyphs do - if glyph.kerns then - local mykerns = { } - for k,v in next, glyph.kerns do - local vc, vo, vl = v.char, v.off, v.lookup - if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones - local uvc = unicodes[vc] - if not uvc then - if trace_loading then - logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index) - end - else - if type(vl) ~= "table" then - vl = { vl } - end - for l=1,#vl do - local vll = vl[l] - local mkl = mykerns[vll] - if not mkl then - mkl = { } - mykerns[vll] = mkl - end - if type(uvc) == "table" then - for u=1,#uvc do - mkl[uvc[u]] = vo - end - else - mkl[uvc] = vo - end - end - end - end - end - glyph.mykerns = mykerns - glyph.kerns = nil -- saves space and time - mkdone = true - end - end - if trace_loading and mkdone then - logs.report("load otf", "replacing 'kerns' tables by 'mykerns' tables") - end - if data.kerns then - if trace_loading then - logs.report("load otf", "removing global 'kern' table") - end - data.kerns = nil - end - local dgpos = data.gpos - if dgpos then - for gp=1,#dgpos do - local gpos = dgpos[gp] - local subtables = gpos.subtables - if subtables then - for s=1,#subtables do - local subtable = subtables[s] - local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes - if kernclass then - for k=1,#kernclass do - local kcl = kernclass[k] - local firsts, seconds, offsets, lookups = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup -- singular - if type(lookups) ~= "table" then - lookups = { lookups } - end - for l=1,#lookups do - local lookup = lookups[l] - local maxfirsts, maxseconds = getn(firsts), getn(seconds) - if trace_loading then - logs.report("load otf", "adding kernclass %s with %s times %s pairs",lookup, maxfirsts, maxseconds) - end - for fk, fv in next, firsts do - for first in gmatch(fv,"[^ ]+") do - local first_unicode = unicodes[first] - if type(first_unicode) == "number" then - first_unicode = { first_unicode } - end - for f=1,#first_unicode do - local glyph = glyphs[mapmap[first_unicode[f]]] - if glyph then - local mykerns = glyph.mykerns - if not mykerns then - mykerns = { } -- unicode indexed ! - glyph.mykerns = mykerns - end - local lookupkerns = mykerns[lookup] - if not lookupkerns then - lookupkerns = { } - mykerns[lookup] = lookupkerns - end - for sk, sv in next, seconds do - local offset = offsets[(fk-1) * maxseconds + sk] - --~ local offset = offsets[sk] -- (fk-1) * maxseconds + sk] - for second in gmatch(sv,"[^ ]+") do - local second_unicode = unicodes[second] - if type(second_unicode) == "number" then - lookupkerns[second_unicode] = offset - else - for s=1,#second_unicode do - lookupkerns[second_unicode[s]] = offset - end - end - end - end - elseif trace_loading then - logs.report("load otf", "no glyph data for U+%04X", first_unicode[f]) - end - end - end - end - end - end - subtable.comment = "The kernclass table is merged into mykerns in the indexed glyph tables." - subtable.kernclass = { } - end - end - end - end - end -end - -otf.enhancers["strip not needed data"] = function(data,filename) - local verbose = fonts.verbose - local int_to_uni = data.luatex.unicodes - for k, v in next, data.glyphs do - local d = v.dependents - if d then v.dependents = nil end - local a = v.altuni - if a then v.altuni = nil end - if verbose then - local code = int_to_uni[k] - -- looks like this is done twice ... bug? - if code then - local vu = v.unicode - if not vu then - v.unicode = code - elseif type(vu) == "table" then - if vu[#vu] == code then - -- weird - else - vu[#vu+1] = code - end - elseif vu ~= code then - v.unicode = { vu, code } - end - end - else - v.unicode = nil - v.index = nil - end - end - data.luatex.comment = "Glyph tables have their original index. When present, mykern tables are indexed by unicode." - data.map = nil - data.names = nil -- funny names for editors - data.glyphcnt = nil - data.glyphmax = nil - if true then - data.gpos = nil - data.gsub = nil - data.anchor_classes = nil - end -end - -otf.enhancers["migrate metadata"] = function(data,filename) - local global_fields = otf.tables.global_fields - local metadata = { } - for k,v in next, data do - if not global_fields[k] then - metadata[k] = v - data[k] = nil - end - end - data.metadata = metadata - -- goodies - local pfminfo = data.pfminfo - metadata.isfixedpitch = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose["proportion"] == "Monospaced") - metadata.charwidth = pfminfo and pfminfo.avgwidth -end - -otf.enhancers["flatten glyph lookups"] = function(data,filename) - for k, v in next, data.glyphs do - if v.lookups then - for kk, vv in next, v.lookups do - for kkk=1,#vv do - local vvv = vv[kkk] - local s = vvv.specification - if s then - local t = vvv.type - if t == "ligature" then - vv[kkk] = { "ligature", s.components, s.char } - elseif t == "alternate" then - vv[kkk] = { "alternate", s.components } - elseif t == "substitution" then - vv[kkk] = { "substitution", s.variant } - elseif t == "multiple" then - vv[kkk] = { "multiple", s.components } - elseif t == "position" then - vv[kkk] = { "position", { s.x or 0, s.y or 0, s.h or 0, s.v or 0 } } - elseif t == "pair" then - local one, two, paired = s.offsets[1], s.offsets[2], s.paired or "" - if one then - if two then - vv[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0 } } - else - vv[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } } - end - else - if two then - vv[kkk] = { "pair", paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { } - else - vv[kkk] = { "pair", paired } - end - end - else - if trace_loading then - logs.report("load otf", "flattening needed, report to context list") - end - for a, b in next, s do - if trace_loading and vvv[a] then - logs.report("load otf", "flattening conflict, report to context list") - end - vvv[a] = b - end - vvv.specification = nil - end - end - end - end - end - end -end - -otf.enhancers["flatten anchor tables"] = function(data,filename) - for k, v in next, data.glyphs do - if v.anchors then - for kk, vv in next, v.anchors do - for kkk, vvv in next, vv do - if vvv.x or vvv.y then - vv[kkk] = { vvv.x or 0, vvv.y or 0 } - else - for kkkk=1,#vvv do - local vvvv = vvv[kkkk] - vvv[kkkk] = { vvvv.x or 0, vvvv.y or 0 } - end - end - end - end - end - end -end - -otf.enhancers["flatten feature tables"] = function(data,filename) - -- is this needed? do we still use them at all? - for _, tag in next, otf.glists do - if data[tag] then - if trace_loading then - logs.report("load otf", "flattening %s table", tag) - end - for k, v in next, data[tag] do - local features = v.features - if features then - for kk=1,#features do - local vv = features[kk] - local t = { } - local scripts = vv.scripts - for kkk=1,#scripts do - local vvv = scripts[kkk] - t[vvv.script] = vvv.langs - end - vv.scripts = t - end - end - end - end - end -end - -otf.enhancers.patches = otf.enhancers.patches or { } - -otf.enhancers["patch bugs"] = function(data,filename) - local basename = file.basename(lower(filename)) - for pattern, action in next, otf.enhancers.patches do - if find(basename,pattern) then - action(data,filename) - end - end -end - --- tex features - -fonts.otf.enhancers["enrich with features"] = function(data,filename) - -- later, ctx only -end - -function otf.features.register(name,default) - otf.features.list[#otf.features.list+1] = name - otf.features.default[name] = default -end - -function otf.set_features(tfmdata,features) - local processes = { } - if not table.is_empty(features) then - local lists = { - fonts.triggers, - fonts.processors, - fonts.manipulators, - } - local mode = tfmdata.mode or fonts.mode -- or features.mode - local initializers = fonts.initializers - local fi = initializers[mode] - if fi then - local fiotf = fi.otf - if fiotf then - local done = { } - for l=1,4 do - local list = lists[l] - if list then - for i=1,#list do - local f = list[i] - local value = features[f] - if value and fiotf[f] then -- brr - if not done[f] then -- so, we can move some to triggers - if trace_features then - logs.report("define otf","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown', tfmdata.fullname or 'unknown') - end - fiotf[f](tfmdata,value) -- can set mode (no need to pass otf) - mode = tfmdata.mode or fonts.mode -- keep this, mode can be set local ! - local im = initializers[mode] - if im then - fiotf = initializers[mode].otf - end - done[f] = true - end - end - end - end - end - end - end - local fm = fonts.methods[mode] - if fm then - local fmotf = fm.otf - if fmotf then - for l=1,4 do - local list = lists[l] - if list then - for i=1,#list do - local f = list[i] - if fmotf[f] then -- brr - if trace_features then - logs.report("define otf","installing feature handler %s for mode %s for font %s",f,mode or 'unknown', tfmdata.fullname or 'unknown') - end - processes[#processes+1] = fmotf[f] - end - end - end - end - end - else - -- message - end - end - return processes, features -end - -function otf.otf_to_tfm(specification) - local name = specification.name - local sub = specification.sub - local filename = specification.filename - local format = specification.format - local features = specification.features.normal - local cache_id = specification.hash - local tfmdata = containers.read(tfm.cache(),cache_id) ---~ print(cache_id) - if not tfmdata then - local otfdata = otf.load(filename,format,sub,features and features.featurefile) - if not table.is_empty(otfdata) then - otfdata.shared = otfdata.shared or { - featuredata = { }, - anchorhash = { }, - initialized = false, - } - tfmdata = otf.copy_to_tfm(otfdata,cache_id) - if not table.is_empty(tfmdata) then - tfmdata.unique = tfmdata.unique or { } - tfmdata.shared = tfmdata.shared or { } -- combine - local shared = tfmdata.shared - shared.otfdata = otfdata - shared.features = features -- default - shared.dynamics = { } - shared.processes = { } - shared.set_dynamics = otf.set_dynamics -- fast access and makes other modules independent - -- this will be done later anyway, but it's convenient to have - -- them already for fast access - tfmdata.luatex = otfdata.luatex - tfmdata.indices = otfdata.luatex.indices - tfmdata.unicodes = otfdata.luatex.unicodes - tfmdata.marks = otfdata.luatex.marks - tfmdata.originals = otfdata.luatex.originals - tfmdata.changed = { } - tfmdata.has_italic = otfdata.metadata.has_italic - if not tfmdata.language then tfmdata.language = 'dflt' end - if not tfmdata.script then tfmdata.script = 'dflt' end - shared.processes, shared.features = otf.set_features(tfmdata,fonts.define.check(features,otf.features.default)) - end - end - containers.write(tfm.cache(),cache_id,tfmdata) - end - return tfmdata -end - ---~ { ---~ ['boundingbox']={ 95, -458, 733, 1449 }, ---~ ['class']="base", ---~ ['name']="braceleft", ---~ ['unicode']=123, ---~ ['vert_variants']={ ---~ ['italic_correction']=0, ---~ ['parts']={ ---~ { ['component']="uni23A9", ['endConnectorLength']=1000, ['fullAdvance']=2546, ['is_extender']=0, ['startConnectorLength']=0, }, -- bot ---~ { ['component']="uni23AA", ['endConnectorLength']=2500, ['fullAdvance']=2501, ['is_extender']=1, ['startConnectorLength']=2500, }, -- rep ---~ { ['component']="uni23A8", ['endConnectorLength']=1000, ['fullAdvance']=4688, ['is_extender']=0, ['startConnectorLength']=1000, }, -- mid ---~ { ['component']="uni23AA", ['endConnectorLength']=2500, ['fullAdvance']=2501, ['is_extender']=1, ['startConnectorLength']=2500, }, -- rep ---~ { ['component']="uni23A7", ['endConnectorLength']=0, ['fullAdvance']=2546, ['is_extender']=0, ['startConnectorLength']=1000, }, -- top ---~ }, ---~ ['variants']="braceleft braceleft.vsize1 braceleft.vsize2 braceleft.vsize3 braceleft.vsize4 braceleft.vsize5 braceleft.vsize6 braceleft.vsize7", ---~ }, ---~ ['width']=793, ---~ }, - --- the first version made a top/mid/not extensible table, now we just pass on the variants data --- and deal with it in the tfm scaler (there is no longer an extensible table anyway) - --- we cannot share descriptions as virtual fonts might extend them (ok, we could --- use a cache with a hash - -function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder the tma to unicode (nasty due to one->many) - if data then - local glyphs, pfminfo, metadata = data.glyphs or { }, data.pfminfo or { }, data.metadata or { } - local luatex = data.luatex - local unicodes = luatex.unicodes -- names to unicodes - local indices = luatex.indices - local characters, parameters, math_parameters, descriptions = { }, { }, { }, { } - local tfm = { - characters = characters, - parameters = parameters, - math_parameters = math_parameters, - descriptions = descriptions, - indices = indices, - unicodes = unicodes, - } - -- indices maps from unicodes to indices - for u, i in next, indices do - characters[u] = { } -- we need this because for instance we add protruding info and loop over characters - descriptions[u] = glyphs[i] - end - -- math - if metadata.math then - -- parameters - for name, value in next, metadata.math do - math_parameters[name] = value - end - -- we could use a subset - for u, char in next, characters do - local d = descriptions[u] - local m = d.math - -- we have them shared because that packs nicer - -- we could prepare the variants and keep 'm in descriptions - if m then - local variants = m.horiz_variants - if variants then - local c = char - for n in variants:gmatch("[^ ]+") do - local un = unicodes[n] - if un and u ~= un then - c.next = un - c = characters[un] - end - end - c.horiz_variants = m.horiz_parts - else - local variants = m.vert_variants - if variants then - local c = char - for n in variants:gmatch("[^ ]+") do - local un = unicodes[n] - if un and u ~= un then - c.next = un - c = characters[un] - end - end - c.vert_variants = m.vert_parts - end - end - local kerns = m.kerns - if kerns then - char.mathkerns = kerns - end - end - end - end - -- end math - local designsize = metadata.designsize or metadata.design_size or 100 - if designsize == 0 then - designsize = 100 - end - local spaceunits = 500 - tfm.units = metadata.units_per_em or 1000 - -- we need a runtime lookup because of running from cdrom or zip, brrr - tfm.filename = resolvers.findbinfile(luatex.filename,"") or luatex.filename - tfm.fullname = metadata.fontname or metadata.fullname - tfm.psname = tfm.fullname - tfm.encodingbytes = 2 - tfm.cidinfo = data.cidinfo - tfm.cidinfo.registry = tfm.cidinfo.registry or "" - tfm.type = "real" - tfm.stretch = 0 -- stretch - tfm.slant = 0 -- slant - tfm.direction = 0 - tfm.boundarychar_label = 0 - tfm.boundarychar = 65536 - tfm.designsize = (designsize/10)*65536 - tfm.spacer = "500 units" - local endash, emdash = 0x20, 0x2014 -- unicodes['space'], unicodes['emdash'] - if metadata.isfixedpitch then - if descriptions[endash] then - spaceunits, tfm.spacer = descriptions[endash].width, "space" - end - if not spaceunits and descriptions[emdash] then - spaceunits, tfm.spacer = descriptions[emdash].width, "emdash" - end - if not spaceunits and metadata.charwidth then - spaceunits, tfm.spacer = metadata.charwidth, "charwidth" - end - else - if descriptions[endash] then - spaceunits, tfm.spacer = descriptions[endash].width, "space" - end - if not spaceunits and descriptions[emdash] then - spaceunits, tfm.spacer = descriptions[emdash].width/2, "emdash/2" - end - if not spaceunits and metadata.charwidth then - spaceunits, tfm.spacer = metadata.charwidth, "charwidth" - end - end - spaceunits = tonumber(spaceunits) or tfm.units/2 -- 500 -- brrr - parameters.slant = 0 - parameters.space = spaceunits -- 3.333 (cmr10) - parameters.space_stretch = tfm.units/2 -- 500 -- 1.666 (cmr10) - parameters.space_shrink = 1*tfm.units/3 -- 333 -- 1.111 (cmr10) - parameters.x_height = 2*tfm.units/5 -- 400 - parameters.quad = tfm.units -- 1000 - if spaceunits < 2*tfm.units/5 then - -- todo: warning - end - local italicangle = metadata.italicangle - tfm.ascender = math.abs(metadata.ascent or 0) - tfm.descender = math.abs(metadata.descent or 0) - if italicangle then -- maybe also in afm _ - tfm.italicangle = italicangle - parameters.slant = parameters.slant - math.round(math.tan(italicangle*math.pi/180)) - end - if metadata.isfixedpitch then - parameters.space_stretch = 0 - parameters.space_shrink = 0 - elseif otf.syncspace then -- - parameters.space_stretch = spaceunits/2 - parameters.space_shrink = spaceunits/3 - end - parameters.extra_space = parameters.space_shrink -- 1.111 (cmr10) - if pfminfo.os2_xheight and pfminfo.os2_xheight > 0 then - parameters.x_height = pfminfo.os2_xheight - else - local x = 0x78 -- unicodes['x'] - if x then - local x = descriptions[x] - if x then - parameters.x_height = x.height - end - end - end - -- [6] - return tfm - else - return nil - end -end - -otf.features.register('mathsize') - -function tfm.read_from_open_type(specification) - local tfmtable = otf.otf_to_tfm(specification) - if tfmtable then - local otfdata = tfmtable.shared.otfdata - tfmtable.name = specification.name - tfmtable.sub = specification.sub - local s = specification.size - local m = otfdata.metadata.math - if m then - local f = specification.features - if f then - local f = f.normal - if f and f.mathsize then - local mathsize = specification.mathsize or 0 - if mathsize == 2 then - local p = m.ScriptPercentScaleDown - if p then - local ps = p * specification.textsize / 100 - if trace_math then - logs.report("define font","asked script size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) - end - s = ps - end - elseif mathsize == 3 then - local p = m.ScriptScriptPercentScaleDown - if p then - local ps = p * specification.textsize / 100 - if trace_math then - logs.report("define font","asked scriptscript size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) - end - s = ps - end - end - end - end - end - tfmtable = tfm.scale(tfmtable,s) - -- here we resolve the name; file can be relocated, so this info is not in the cache - local filename = (otfdata and otfdata.luatex and otfdata.luatex.filename) or specification.filename - if not filename then - -- try to locate anyway and set otfdata.luatex.filename - end - if filename then - tfmtable.encodingbytes = 2 - tfmtable.filename = resolvers.findbinfile(filename,"") or filename - tfmtable.fullname = tfmtable.fullname or otfdata.metadata.fontname or otfdata.metadata.fullname - local order = otfdata and otfdata.metadata.order2 - if order == 0 then - tfmtable.format = 'opentype' - elseif order == 1 then - tfmtable.format = 'truetype' - else - tfmtable.format = specification.format - end - tfmtable.name = tfmtable.filename or tfmtable.fullname - end - fonts.logger.save(tfmtable,file.extname(specification.filename),specification) - end ---~ print(tfmtable.fullname) - return tfmtable -end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-cid.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-cid.lua index 57bfcd3787d..d1c727af2d9 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-cid.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-cid.lua @@ -8,6 +8,7 @@ if not modules then modules = { } end modules ['font-cid'] = { local format, match, lower = string.format, string.match, string.lower local tonumber = tonumber +local lpegmatch = lpeg.match local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) @@ -61,7 +62,7 @@ function fonts.cid.load(filename) local data = io.loaddata(filename) if data then unicodes, names = { }, { } - grammar:match(data) + lpegmatch(grammar,data) local supplement, registry, ordering = match(filename,"^(.-)%-(.-)%-()%.(.-)$") return { supplement = supplement, diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-clr.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-clr.lua new file mode 100644 index 00000000000..e1f0faa4184 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-clr.lua @@ -0,0 +1,155 @@ +if not modules then modules = { } end modules ['font-clr'] = { + version = 1.001, + comment = "companion to font-otf.lua (font color)", + author = "Khaled Hosny and Elie Roux", + copyright = "Luaotfload Development Team", + license = "GPL" +} + +fonts.triggers = fonts.triggers or { } +fonts.initializers = fonts.initializers or { } +fonts.initializers.common = fonts.initializers.common or { } + +local initializers, format = fonts.initializers, string.format + +table.insert(fonts.triggers,"color") + +function initializers.common.color(tfmdata,value) + if value then + tfmdata.color = value + add_color_callback() + end +end + +initializers.base.otf.color = initializers.common.color +initializers.node.otf.color = initializers.common.color + +local function hex2dec(hex,one) + if one then + return format("%.1g", tonumber(hex, 16)/255) + else + return format("%.3g", tonumber(hex, 16)/255) + end +end + +local res + +local function pageresources(a) + local res2 + if not res then + res = "/TransGs1<>" + end + res2 = format("/TransGs%s<>", a, a, a) + res = format("%s%s", res, res:find(res2) and "" or res2) +end + +local function hex_to_rgba(hex) + local r, g, b, a, push, pop, res3 + if hex then + if #hex == 6 then + _, _, r, g, b = hex:find('(..)(..)(..)') + elseif #hex == 8 then + _, _, r, g, b, a = hex:find('(..)(..)(..)(..)') + a = hex2dec(a,true) + pageresources(a) + end + else + return nil + end + r = hex2dec(r) + g = hex2dec(g) + b = hex2dec(b) + if a then + push = format('/TransGs%g gs %s %s %s rg', a, r, g, b) + pop = '0 g /TransGs1 gs' + else + push = format('%s %s %s rg', r, g, b) + pop = '0 g' + end + return push, pop +end + +local glyph = node.id('glyph') +local hlist = node.id('hlist') +local vlist = node.id('vlist') +local whatsit = node.id('whatsit') +local pgi = node.id('page_insert') +local sbox = node.id('sub_box') + +local function lookup_next_color(head) + for n in node.traverse(head) do + if n.id == glyph then + if fonts.ids[n.font] and fonts.ids[n.font].color then + return fonts.ids[n.font].color + else + return -1 + end + elseif n.id == vlist or n.id == hlist or n.id == sbox then + local r = lookup_next_color(n.list) + if r == -1 then + return -1 + elseif r then + return r + end + elseif n.id == whatsit or n.id == pgi then + return -1 + end + end + return nil +end + +local function node_colorize(head, current_color, next_color) + for n in node.traverse(head) do + if n.id == hlist or n.id == vlist or n.id == sbox then + local next_color_in = lookup_next_color(n.next) or next_color + n.list, current_color = node_colorize(n.list, current_color, next_color_in) + elseif n.id == glyph then + local tfmdata = fonts.ids[n.font] + if tfmdata and tfmdata.color then + if tfmdata.color ~= current_color then + local pushcolor = hex_to_rgba(tfmdata.color) + local push = node.new(whatsit, 8) + push.mode = 1 + push.data = pushcolor + head = node.insert_before(head, n, push) + current_color = tfmdata.color + end + local next_color_in = lookup_next_color (n.next) or next_color + if next_color_in ~= tfmdata.color then + local _, popcolor = hex_to_rgba(tfmdata.color) + local pop = node.new(whatsit, 8) + pop.mode = 1 + pop.data = popcolor + head = node.insert_after(head, n, pop) + current_color = nil + end + end + end + end + return head, current_color +end + +local function font_colorize(head) + -- check if our page resources existed in the previous run + -- and remove it to avoid duplicating it later + if res then + local r = "/ExtGState<<"..res..">>" + tex.pdfpageresources = tex.pdfpageresources:gsub(r, "") + end + local h = node_colorize(head, nil, nil) + -- now append our page resources + if res and res:find("%S") then -- test for non-empty string + local r = "/ExtGState<<"..res..">>" + tex.pdfpageresources = tex.pdfpageresources..r + end + return h +end + +local color_callback_activated = 0 + +function add_color_callback() + if color_callback_activated == 0 then + luatexbase.add_to_callback("pre_output_filter", font_colorize, "loaotfload.colorize") + color_callback_activated = 1 + end +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-def.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-def.lua index 65c74d48fd2..f64bae5636e 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-def.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-def.lua @@ -8,6 +8,7 @@ if not modules then modules = { } end modules ['font-def'] = { local format, concat, gmatch, match, find, lower = string.format, table.concat, string.gmatch, string.match, string.find, string.lower local tostring, next = tostring, next +local lpegmatch = lpeg.match local trace_defining = false trackers .register("fonts.defining", function(v) trace_defining = v end) local directive_embedall = false directives.register("fonts.embedall", function(v) directive_embedall = v end) @@ -108,7 +109,7 @@ define.add_lookup("name") define.add_lookup("spec") function define.get_specification(str) - return splitter:match(str) + return lpegmatch(splitter,str) end function define.register_split(symbol,action) @@ -240,7 +241,7 @@ end function define.resolvers.name(specification) local resolve = fonts.names.resolve if resolve then - specification.resolved, specification.sub = fonts.names.resolve(specification.name,specification.sub) + specification.resolved, specification.sub = fonts.names.resolve(specification) if specification.resolved then specification.forced = file.extname(specification.resolved) specification.name = file.removesuffix(specification.resolved) @@ -253,7 +254,7 @@ end function define.resolvers.spec(specification) local resolvespec = fonts.names.resolvespec if resolvespec then - specification.resolved, specification.sub = fonts.names.resolvespec(specification.name,specification.sub) + specification.resolved, specification.sub = fonts.names.resolvespec(specification) if specification.resolved then specification.forced = file.extname(specification.resolved) specification.name = file.removesuffix(specification.resolved) @@ -313,7 +314,7 @@ function tfm.read(specification) local reader = sequence[s] if readers[reader] then -- not really needed if trace_defining then - logs.report("define font","trying (sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") + logs.report("define font","trying (reader sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") end tfmtable = readers[reader](specification) if tfmtable then @@ -533,7 +534,10 @@ function define.register(fontdata,id) if trace_defining then logs.report("define font","loading at 2 id %s, hash: %s",id or "?",hash or "?") end - fonts.ids[id] = fontdata + fonts.identifiers[id] = fontdata + fonts.characters [id] = fontdata.characters + fonts.quads [id] = fontdata.parameters.quad + -- todo: extra functions, e.g. setdigitwidth etc in list tfm.internalized[hash] = id end end @@ -643,5 +647,5 @@ end

We overload both the and readers.

--ldx]]-- -callback.register('define_font' , define.read) -callback.register('find_vf_file', vf.find ) -- not that relevant any more +callbacks.register('define_font' , define.read, "definition of fonts (tfmtable preparation)") +callbacks.register('find_vf_file', vf.find , "locating virtual fonts, insofar needed") -- not that relevant any more diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-dum.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-dum.lua index e13f8a25536..5224ae187e7 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-dum.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-dum.lua @@ -10,8 +10,9 @@ fonts = fonts or { } -- general -fonts.otf.pack = false -fonts.tfm.resolve_vf = false -- no sure about this +fonts.otf.pack = false +fonts.tfm.resolve_vf = false -- no sure about this +fonts.tfm.fontname_mode = "specification" -- somehow latex needs this -- readers @@ -113,5 +114,245 @@ end fonts.initializers.base.otf.itlc = itlc fonts.initializers.node.otf.itlc = itlc +-- slant and extend + +function fonts.initializers.common.slant(tfmdata,value) + value = tonumber(value) + if not value then + value = 0 + elseif value > 1 then + value = 1 + elseif value < -1 then + value = -1 + end + tfmdata.slant_factor = value +end + +function fonts.initializers.common.extend(tfmdata,value) + value = tonumber(value) + if not value then + value = 0 + elseif value > 10 then + value = 10 + elseif value < -10 then + value = -10 + end + tfmdata.extend_factor = value +end + +table.insert(fonts.triggers,"slant") +table.insert(fonts.triggers,"extend") + +fonts.initializers.base.otf.slant = fonts.initializers.common.slant +fonts.initializers.node.otf.slant = fonts.initializers.common.slant +fonts.initializers.base.otf.extend = fonts.initializers.common.extend +fonts.initializers.node.otf.extend = fonts.initializers.common.extend + +-- expansion and protrusion + +fonts.protrusions = fonts.protrusions or { } +fonts.protrusions.setups = fonts.protrusions.setups or { } + +local setups = fonts.protrusions.setups + +-- As this is experimental code, users should not depend on it. The +-- implications are still discussed on the ConTeXt Dev List and we're +-- not sure yet what exactly the spec is (the next code is tested with +-- a gyre font patched by / fea file made by Khaled Hosny). The double +-- trick should not be needed it proper hanging punctuation is used in +-- which case values < 1 can be used. +-- +-- preferred (in context, usine vectors): +-- +-- \definefontfeature[whatever][default][mode=node,protrusion=quality] +-- +-- using lfbd and rtbd, with possibibility to enable only one side : +-- +-- \definefontfeature[whocares][default][mode=node,protrusion=yes, opbd=yes,script=latn] +-- \definefontfeature[whocares][default][mode=node,protrusion=right,opbd=yes,script=latn] +-- +-- idem, using multiplier +-- +-- \definefontfeature[whocares][default][mode=node,protrusion=2,opbd=yes,script=latn] +-- \definefontfeature[whocares][default][mode=node,protrusion=double,opbd=yes,script=latn] +-- +-- idem, using named feature file (less frozen): +-- +-- \definefontfeature[whocares][default][mode=node,protrusion=2,opbd=yes,script=latn,featurefile=texgyrepagella-regularxx.fea] + +local function map_opbd_onto_protrusion(tfmdata,value,opbd) + local characters, descriptions = tfmdata.characters, tfmdata.descriptions + local otfdata = tfmdata.shared.otfdata + local singles = otfdata.shared.featuredata.gpos_single + local script, language = tfmdata.script, tfmdata.language + local done, factor, left, right = false, 1, 1, 1 + local setup = setups[value] + if setup then + factor = setup.factor or 1 + left = setup.left or 1 + right = setup.right or 1 + else + factor = tonumber(value) or 1 + end + if opbd ~= "right" then + local validlookups, lookuplist = fonts.otf.collect_lookups(otfdata,"lfbd",script,language) + if validlookups then + for i=1,#lookuplist do + local lookup = lookuplist[i] + local data = singles[lookup] + if data then + if trace_protrusion then + logs.report("fonts","set left protrusion using lfbd lookup '%s'",lookup) + end + for k, v in next, data do + -- local p = - v[3] / descriptions[k].width-- or 1 ~= 0 too but the same + local p = - (v[1] / 1000) * factor * left + characters[k].left_protruding = p + if trace_protrusion then + logs.report("opbd","lfbd -> %s -> 0x%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," ")) + end + end + done = true + end + end + end + end + if opbd ~= "left" then + local validlookups, lookuplist = fonts.otf.collect_lookups(otfdata,"rtbd",script,language) + if validlookups then + for i=1,#lookuplist do + local lookup = lookuplist[i] + local data = singles[lookup] + if data then + if trace_protrusion then + logs.report("fonts","set right protrusion using rtbd lookup '%s'",lookup) + end + for k, v in next, data do + -- local p = v[3] / descriptions[k].width -- or 3 + local p = (v[1] / 1000) * factor * right + characters[k].right_protruding = p + if trace_protrusion then + logs.report("opbd","rtbd -> %s -> 0x%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," ")) + end + end + end + done = true + end + end + end + tfmdata.auto_protrude = done +end + +-- The opbd test is just there because it was discussed on the +-- context development list. However, the mentioned fxlbi.otf font +-- only has some kerns for digits. So, consider this feature not +-- supported till we have a proper test font. + +function fonts.initializers.common.protrusion(tfmdata,value) + if value then + local opbd = tfmdata.shared.features.opbd + if opbd then + -- possible values: left right both yes no (experimental) + map_opbd_onto_protrusion(tfmdata,value,opbd) + elseif value then + local setup = setups[value] + if setup then + local factor, left, right = setup.factor or 1, setup.left or 1, setup.right or 1 + local emwidth = tfmdata.parameters.quad + tfmdata.auto_protrude = true + for i, chr in next, tfmdata.characters do + local v, pl, pr = setup[i], nil, nil + if v then + pl, pr = v[1], v[2] + end + if pl and pl ~= 0 then chr.left_protruding = left *pl*factor end + if pr and pr ~= 0 then chr.right_protruding = right*pr*factor end + end + end + end + end +end + +fonts.expansions = fonts.expansions or { } +fonts.expansions.setups = fonts.expansions.setups or { } + +local setups = fonts.expansions.setups + +function fonts.initializers.common.expansion(tfmdata,value) + if value then + local setup = setups[value] + if setup then + local stretch, shrink, step, factor = setup.stretch or 0, setup.shrink or 0, setup.step or 0, setup.factor or 1 + tfmdata.stretch, tfmdata.shrink, tfmdata.step, tfmdata.auto_expand = stretch * 10, shrink * 10, step * 10, true + for i, chr in next, tfmdata.characters do + local v = setup[i] + if v and v ~= 0 then + chr.expansion_factor = v*factor + else -- can be option + chr.expansion_factor = factor + end + end + end + end +end + +table.insert(fonts.manipulators,"protrusion") +table.insert(fonts.manipulators,"expansion") + +fonts.initializers.base.otf.protrusion = fonts.initializers.common.protrusion +fonts.initializers.node.otf.protrusion = fonts.initializers.common.protrusion +fonts.initializers.base.otf.expansion = fonts.initializers.common.expansion +fonts.initializers.node.otf.expansion = fonts.initializers.common.expansion + +-- left over + function fonts.register_message() end + +-- example vectors + +local byte = string.byte + +fonts.expansions.setups['default'] = { + + stretch = 2, shrink = 2, step = .5, factor = 1, + + [byte('A')] = 0.5, [byte('B')] = 0.7, [byte('C')] = 0.7, [byte('D')] = 0.5, [byte('E')] = 0.7, + [byte('F')] = 0.7, [byte('G')] = 0.5, [byte('H')] = 0.7, [byte('K')] = 0.7, [byte('M')] = 0.7, + [byte('N')] = 0.7, [byte('O')] = 0.5, [byte('P')] = 0.7, [byte('Q')] = 0.5, [byte('R')] = 0.7, + [byte('S')] = 0.7, [byte('U')] = 0.7, [byte('W')] = 0.7, [byte('Z')] = 0.7, + [byte('a')] = 0.7, [byte('b')] = 0.7, [byte('c')] = 0.7, [byte('d')] = 0.7, [byte('e')] = 0.7, + [byte('g')] = 0.7, [byte('h')] = 0.7, [byte('k')] = 0.7, [byte('m')] = 0.7, [byte('n')] = 0.7, + [byte('o')] = 0.7, [byte('p')] = 0.7, [byte('q')] = 0.7, [byte('s')] = 0.7, [byte('u')] = 0.7, + [byte('w')] = 0.7, [byte('z')] = 0.7, + [byte('2')] = 0.7, [byte('3')] = 0.7, [byte('6')] = 0.7, [byte('8')] = 0.7, [byte('9')] = 0.7, +} + +fonts.protrusions.setups['default'] = { + + factor = 1, left = 1, right = 1, + + [0x002C] = { 0, 1 }, -- comma + [0x002E] = { 0, 1 }, -- period + [0x003A] = { 0, 1 }, -- colon + [0x003B] = { 0, 1 }, -- semicolon + [0x002D] = { 0, 1 }, -- hyphen + [0x2013] = { 0, 0.50 }, -- endash + [0x2014] = { 0, 0.33 }, -- emdash + [0x3001] = { 0, 1 }, -- ideographic comma 、 + [0x3002] = { 0, 1 }, -- ideographic full stop 。 + [0x060C] = { 0, 1 }, -- arabic comma ، + [0x061B] = { 0, 1 }, -- arabic semicolon ؛ + [0x06D4] = { 0, 1 }, -- arabic full stop ۔ + +} + +-- normalizer + +fonts.otf.meanings = fonts.otf.meanings or { } + +fonts.otf.meanings.normalize = fonts.otf.meanings.normalize or function(t) + if t.rand then + t.rand = "random" + end +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-ini.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-ini.lua index 5cff22760cc..b9988687d68 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-ini.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-ini.lua @@ -22,7 +22,11 @@ fontloader.totable = fontloader.to_table -- fix comes last fonts = fonts or { } -fonts.ids = fonts.ids or { } -- aka fontdata + +fonts.ids = fonts.ids or { } fonts.identifiers = fonts.ids -- aka fontdata +fonts.chr = fonts.chr or { } fonts.characters = fonts.chr -- aka chardata +fonts.qua = fonts.qua or { } fonts.quads = fonts.qua -- aka quaddata + fonts.tfm = fonts.tfm or { } fonts.mode = 'base' @@ -35,6 +39,8 @@ fonts.ids[0] = { -- nullfont name = "nullfont", } +fonts.chr[0] = { } + fonts.methods = fonts.methods or { base = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, node = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-map.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-map.lua index 0dc4eca5b86..208ad50a873 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-map.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-map.lua @@ -6,9 +6,13 @@ if not modules then modules = { } end modules ['font-map'] = { license = "see context related readme files" } -local match, format, find, concat = string.match, string.format, string.find, table.concat +local utf = unicode.utf8 +local match, format, find, concat, gsub, lower = string.match, string.format, string.find, table.concat, string.gsub, string.lower +local lpegmatch = lpeg.match +local utfbyte = utf.byte -local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) +local trace_unimapping = false trackers.register("otf.unimapping", function(v) trace_unimapping = v end) local ctxcatcodes = tex and tex.ctxcatcodes @@ -18,116 +22,10 @@ of obsolete. Some code may move to runtime or auxiliary modules.

The name to unciode related code will stay of course.

--ldx]]-- -fonts = fonts or { } -fonts.map = fonts.map or { } -fonts.map.data = fonts.map.data or { } -fonts.map.encodings = fonts.map.encodings or { } -fonts.map.done = fonts.map.done or { } -fonts.map.loaded = fonts.map.loaded or { } -fonts.map.direct = fonts.map.direct or { } -fonts.map.line = fonts.map.line or { } - -function fonts.map.line.pdfmapline(tag,str) - return "\\loadmapline[" .. tag .. "][" .. str .. "]" -end - -function fonts.map.line.pdftex(e) -- so far no combination of slant and stretch - if e.name and e.fontfile then - local fullname = e.fullname or "" - if e.slant and e.slant ~= 0 then - if e.encoding then - return fonts.map.line.pdfmapline("=",format('%s %s "%g SlantFont" <%s <%s',e.name,fullname,e.slant,e.encoding,e.fontfile)) - else - return fonts.map.line.pdfmapline("=",format('%s %s "%g SlantFont" <%s',e.name,fullname,e.slant,e.fontfile)) - end - elseif e.stretch and e.stretch ~= 1 and e.stretch ~= 0 then - if e.encoding then - return fonts.map.line.pdfmapline("=",format('%s %s "%g ExtendFont" <%s <%s',e.name,fullname,e.stretch,e.encoding,e.fontfile)) - else - return fonts.map.line.pdfmapline("=",format('%s %s "%g ExtendFont" <%s',e.name,fullname,e.stretch,e.fontfile)) - end - else - if e.encoding then - return fonts.map.line.pdfmapline("=",format('%s %s <%s <%s',e.name,fullname,e.encoding,e.fontfile)) - else - return fonts.map.line.pdfmapline("=",format('%s %s <%s',e.name,fullname,e.fontfile)) - end - end - else - return nil - end -end - -function fonts.map.flush(backend) -- will also erase the accumulated data - local flushline = fonts.map.line[backend or "pdftex"] or fonts.map.line.pdftex - for _, e in pairs(fonts.map.data) do - tex.sprint(ctxcatcodes,flushline(e)) - end - fonts.map.data = { } -end - -fonts.map.line.dvips = fonts.map.line.pdftex -fonts.map.line.dvipdfmx = function() end - -function fonts.map.convert_entries(filename) - if not fonts.map.loaded[filename] then - fonts.map.data, fonts.map.encodings = fonts.map.load_file(filename,fonts.map.data, fonts.map.encodings) - fonts.map.loaded[filename] = true - end -end - -function fonts.map.load_file(filename, entries, encodings) - entries = entries or { } - encodings = encodings or { } - local f = io.open(filename) - if f then - local data = f:read("*a") - if data then - for line in gmatch(data,"(.-)[\n\t]") do - if find(line,"^[%#%%%s]") then - -- print(line) - else - local stretch, slant, name, fullname, fontfile, encoding - line = line:gsub('"(.+)"', function(s) - stretch = find(s,'"([^"]+) ExtendFont"') - slant = find(s,'"([^"]+) SlantFont"') - return "" - end) - if not name then - -- name fullname encoding fontfile - name, fullname, encoding, fontfile = match(line,"^(%S+)%s+(%S*)[%s<]+(%S*)[%s<]+(%S*)%s*$") - end - if not name then - -- name fullname (flag) fontfile encoding - name, fullname, fontfile, encoding = match(line,"^(%S+)%s+(%S*)[%d%s<]+(%S*)[%s<]+(%S*)%s*$") - end - if not name then - -- name fontfile - name, fontfile = match(line,"^(%S+)%s+[%d%s<]+(%S*)%s*$") - end - if name then - if encoding == "" then encoding = nil end - entries[name] = { - name = name, -- handy - fullname = fullname, - encoding = encoding, - fontfile = fontfile, - slant = tonumber(slant), - stretch = tonumber(stretch) - } - encodings[name] = encoding - elseif line ~= "" then - -- print(line) - end - end - end - end - f:close() - end - return entries, encodings -end +fonts = fonts or { } +fonts.map = fonts.map or { } -function fonts.map.load_lum_table(filename) +local function load_lum_table(filename) -- will move to font goodies local lumname = file.replacesuffix(file.basename(filename),"lum") local lumfile = resolvers.find_file(lumname,"map") or "" if lumfile ~= "" and lfs.isfile(lumfile) then @@ -153,7 +51,7 @@ local parser = unicode + ucode + index local parsers = { } -function fonts.map.make_name_parser(str) +local function make_name_parser(str) if not str or str == "" then return parser else @@ -169,7 +67,7 @@ end --~ local parser = fonts.map.make_name_parser("Japan1") --~ local parser = fonts.map.make_name_parser() --~ local function test(str) ---~ local b, a = parser:match(str) +--~ local b, a = lpegmatch(parser,str) --~ print((a and table.serialize(b)) or b) --~ end --~ test("a.sc") @@ -180,7 +78,7 @@ end --~ test("index1234") --~ test("Japan1.123") -function fonts.map.tounicode16(unicode) +local function tounicode16(unicode) if unicode < 0x10000 then return format("%04X",unicode) else @@ -188,7 +86,7 @@ function fonts.map.tounicode16(unicode) end end -function fonts.map.tounicode16sequence(unicodes) +local function tounicode16sequence(unicodes) local t = { } for l=1,#unicodes do local unicode = unicodes[l] @@ -221,3 +119,252 @@ end --~ return s --~ end +fonts.map.load_lum_table = load_lum_table +fonts.map.make_name_parser = make_name_parser +fonts.map.tounicode16 = tounicode16 +fonts.map.tounicode16sequence = tounicode16sequence + +local separator = lpeg.S("_.") +local other = lpeg.C((1 - separator)^1) +local ligsplitter = lpeg.Ct(other * (separator * other)^0) + +--~ print(table.serialize(lpegmatch(ligsplitter,"this"))) +--~ print(table.serialize(lpegmatch(ligsplitter,"this.that"))) +--~ print(table.serialize(lpegmatch(ligsplitter,"japan1.123"))) +--~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more"))) +--~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more.that"))) + +fonts.map.add_to_unicode = function(data,filename) + local unicodes = data.luatex and data.luatex.unicodes + if not unicodes then + return + end + -- 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 + -- the tounicode mapping is sparse and only needed for alternatives + local tounicode, originals, ns, nl, private, unknown = { }, { }, 0, 0, fonts.private, format("%04X",utfbyte("?")) + data.luatex.tounicode, data.luatex.originals = tounicode, originals + local lumunic, uparser, oparser + if false then -- will become an option + lumunic = load_lum_table(filename) + lumunic = lumunic and lumunic.tounicode + end + local cidinfo, cidnames, cidcodes = data.cidinfo + local usedmap = cidinfo and cidinfo.usedname + usedmap = usedmap and lower(usedmap) + usedmap = usedmap and fonts.cid.map[usedmap] + if usedmap then + oparser = usedmap and make_name_parser(cidinfo.ordering) + cidnames = usedmap.names + cidcodes = usedmap.unicodes + end + uparser = make_name_parser() + local aglmap = fonts.map and fonts.map.agl_to_unicode + for index, glyph in next, data.glyphs do + local name, unic = glyph.name, glyph.unicode or -1 -- play safe + if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then + local unicode = (lumunic and lumunic[name]) or (aglmap and aglmap[name]) + if unicode then + originals[index], tounicode[index], ns = unicode, tounicode16(unicode), 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 + originals[index], tounicode[index], ns = unicode, tounicode16(unicode), 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 + originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 + end + end + if not unicode then + local foundcodes, multiple = lpegmatch(uparser,reference) + if foundcodes then + if multiple then + originals[index], tounicode[index], nl, unicode = foundcodes, tounicode16sequence(foundcodes), nl + 1, true + else + originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes + end + end + end + end + end + end + end + -- a.whatever or a_b_c.whatever or a_b_c (no numbers) + if not unicode then + local split = lpegmatch(ligsplitter,name) + local nplit = (split and #split) or 0 + if nplit == 0 then + -- skip + elseif nplit == 1 then + local base = split[1] + unicode = unicodes[base] or (aglmap and aglmap[base]) + if unicode then + if type(unicode) == "table" then + unicode = unicode[1] + end + originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 + end + else + local t = { } + for l=1,nplit do + local base = split[l] + local u = unicodes[base] or (aglmap and aglmap[base]) + if not u then + break + elseif type(u) == "table" then + t[#t+1] = u[1] + else + t[#t+1] = u + end + end + if #t > 0 then -- done then + originals[index], tounicode[index], nl, unicode = t, tounicode16sequence(t), nl + 1, true + end + end + end + -- last resort + if not unicode then + local foundcodes, multiple = lpegmatch(uparser,name) + if foundcodes then + if multiple then + originals[index], tounicode[index], nl, unicode = foundcodes, tounicode16sequence(foundcodes), nl + 1, true + else + originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes + end + end + end + if not unicode then + originals[index], tounicode[index] = 0xFFFD, "FFFD" + end + end + end + if trace_unimapping then + for index, glyph in table.sortedpairs(data.glyphs) do + local toun, name, unic = tounicode[index], glyph.name, glyph.unicode or -1 -- play safe + if toun then + logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun) + else + logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic) + end + end + end + if trace_loading and (ns > 0 or nl > 0) then + logs.report("load otf","enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns) + end +end + +-- the following is sort of obsolete +-- +-- fonts.map.data = fonts.map.data or { } +-- fonts.map.encodings = fonts.map.encodings or { } +-- fonts.map.loaded = fonts.map.loaded or { } +-- fonts.map.line = fonts.map.line or { } +-- +-- function fonts.map.line.pdftex(e) +-- if e.name and e.fontfile then +-- local fullname = e.fullname or "" +-- if e.slant and e.slant ~= 0 then +-- if e.encoding then +-- pdf.mapline(format('= %s %s "%g SlantFont" <%s <%s',e.name,fullname,e.slant,e.encoding,e.fontfile))) +-- else +-- pdf.mapline(format('= %s %s "%g SlantFont" <%s',e.name,fullname,e.slant,e.fontfile))) +-- end +-- elseif e.extend and e.extend ~= 1 and e.extend ~= 0 then +-- if e.encoding then +-- pdf.mapline(format('= %s %s "%g ExtendFont" <%s <%s',e.name,fullname,e.extend,e.encoding,e.fontfile))) +-- else +-- pdf.mapline(format('= %s %s "%g ExtendFont" <%s',e.name,fullname,e.extend,e.fontfile))) +-- end +-- else +-- if e.encoding then +-- pdf.mapline(format('= %s %s <%s <%s',e.name,fullname,e.encoding,e.fontfile))) +-- else +-- pdf.mapline(format('= %s %s <%s',e.name,fullname,e.fontfile))) +-- end +-- end +-- else +-- return nil +-- end +-- end +-- +-- function fonts.map.flush(backend) -- will also erase the accumulated data +-- local flushline = fonts.map.line[backend or "pdftex"] or fonts.map.line.pdftex +-- for _, e in pairs(fonts.map.data) do +-- flushline(e) +-- end +-- fonts.map.data = { } +-- end +-- +-- fonts.map.line.dvips = fonts.map.line.pdftex +-- fonts.map.line.dvipdfmx = function() end +-- +-- function fonts.map.convert_entries(filename) +-- if not fonts.map.loaded[filename] then +-- fonts.map.data, fonts.map.encodings = fonts.map.load_file(filename,fonts.map.data, fonts.map.encodings) +-- fonts.map.loaded[filename] = true +-- end +-- end +-- +-- function fonts.map.load_file(filename, entries, encodings) +-- entries = entries or { } +-- encodings = encodings or { } +-- local f = io.open(filename) +-- if f then +-- local data = f:read("*a") +-- if data then +-- for line in gmatch(data,"(.-)[\n\t]") do +-- if find(line,"^[%#%%%s]") then +-- -- print(line) +-- else +-- local extend, slant, name, fullname, fontfile, encoding +-- line = gsub(line,'"(.+)"', function(s) +-- extend = find(s,'"([^"]+) ExtendFont"') +-- slant = find(s,'"([^"]+) SlantFont"') +-- return "" +-- end) +-- if not name then +-- -- name fullname encoding fontfile +-- name, fullname, encoding, fontfile = match(line,"^(%S+)%s+(%S*)[%s<]+(%S*)[%s<]+(%S*)%s*$") +-- end +-- if not name then +-- -- name fullname (flag) fontfile encoding +-- name, fullname, fontfile, encoding = match(line,"^(%S+)%s+(%S*)[%d%s<]+(%S*)[%s<]+(%S*)%s*$") +-- end +-- if not name then +-- -- name fontfile +-- name, fontfile = match(line,"^(%S+)%s+[%d%s<]+(%S*)%s*$") +-- end +-- if name then +-- if encoding == "" then encoding = nil end +-- entries[name] = { +-- name = name, -- handy +-- fullname = fullname, +-- encoding = encoding, +-- fontfile = fontfile, +-- slant = tonumber(slant), +-- extend = tonumber(extend) +-- } +-- encodings[name] = encoding +-- elseif line ~= "" then +-- -- print(line) +-- end +-- end +-- end +-- end +-- f:close() +-- end +-- return entries, encodings +-- end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-nms.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-nms.lua new file mode 100644 index 00000000000..fd31bac0832 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-nms.lua @@ -0,0 +1,495 @@ +if not modules then modules = { } end modules ['font-nms'] = { + version = 1.002, + comment = "companion to luaotfload.lua", + author = "Khaled Hosny and Elie Roux", + copyright = "Luaotfload Development Team", + license = "GPL" +} + +fonts = fonts or { } +fonts.names = fonts.names or { } + +local names = fonts.names +local names_dir = "/luatex/generic/luaotfload/names/" +names.version = 2.007 -- not the same as in context +names.data = nil +names.path = { + basename = "otfl-names.lua", + localdir = kpse.expand_var("$TEXMFVAR") .. names_dir, + systemdir = kpse.expand_var("$TEXMFSYSVAR") .. names_dir, +} + + +local splitpath, expandpath, glob, basename = file.split_path, kpse.expand_path, dir.glob, file.basename +local upper, lower, format, gsub, match = string.upper, string.lower, string.format, string.gsub, string.match +local rpadd = string.rpadd +local utfgsub = unicode.utf8.gsub + +local trace_progress = true --trackers.register("names.progress", function(v) trace_progress = v end) +local trace_search = false --trackers.register("names.search", function(v) trace_search = v end) +local trace_loading = false --trackers.register("names.loading", function(v) trace_loading = v end) + +local function sanitize(str) + if str then + return utfgsub(lower(str), "[^%a%d]", "") + else + return str -- nil + end +end + +function names.load() + local localpath = names.path.localdir .. names.path.basename + local systempath = names.path.systemdir .. names.path.basename + local kpsefound = kpse.find_file(names.path.basename) + local data + if kpsefound and file.isreadable(kpsefound) then + data = dofile(kpsefound) + elseif file.isreadable(localpath) then + data = dofile(localpath) + elseif file.isreadable(systempath) then + data = dofile(systempath) + end + if data then + if trace_loading then + logs.report("load font", "loaded font names database: %s", foundname) + end + return data + end +end + +local loaded = false + +local synonyms = { + regular = {normal=true, roman=true, plain=true, book=true, medium=true}, + italic = {regularitalic=true, normalitalic=true, oblique=true, slant=true}, + bolditalic = {boldoblique=true, boldslant=true}, +} + +-- a small hack to get the log message "no font names database loaded" only +-- once in the log +local log_message_shown = false + +function names.resolve(specification) + local tfm = resolvers.find_file(specification.name, "ofm") + local name = sanitize(specification.name) + local style = sanitize(specification.style) or "regular" + local size = tonumber(specification.optsize) or specification.size and specification.size / 65536 + if tfm then + -- is a tfm font, skip names database + return specification.name, false + end + if not loaded then + names.data = names.load() + loaded = true + end + local data = names.data + if type(data) == "table" and data.version == names.version then + if data.mappings then + local found = { } + for _,face in ipairs(data.mappings) do + local family = sanitize(face.names.family) + local subfamily = sanitize(face.names.subfamily) + local fullname = sanitize(face.names.fullname) + local psname = sanitize(face.names.psname) + local fontname = sanitize(face.fontname) + local pfullname = sanitize(face.fullname) + local optsize, dsnsize, maxsize, minsize + if #face.size > 0 then + optsize = face.size + dsnsize = optsize[1] and optsize[1] / 10 + maxsize = optsize[2] and optsize[2] / 10 or dsnsize -- can be nil + minsize = optsize[3] and optsize[3] / 10 or dsnsize -- can be nil + end + if name == family then + if subfamily == style then + if optsize then + if dsnsize == size or (size > minsize and size <= maxsize) then + found[1] = face + break + else + found[#found+1] = face + end + else + found[1] = face + break + end + elseif synonyms[style] and synonyms[style][subfamily] then + if optsize then + if dsnsize == size or (size > minsize and size <= maxsize) then + found[1] = face + break + else + found[#found+1] = face + end + else + found[1] = face + break + end + else + found[1] = face + end + else + if name == fullname or name == pfullname or name == fontname or name == psname then + if optsize then + if dsnsize == size or (size > minsize and size <= maxsize) then + found[1] = face + break + else + found[#found+1] = face + end + else + found[1] = face + break + end + end + end + end + if #found == 1 then + if kpse.lookup(found[1].filename[1]) then + logs.report("load font", + "font family='%s', subfamily='%s' found: %s", + name, style, found[1].filename[1]) + return found[1].filename[1], found[1].filename[2] + end + elseif #found > 1 then + -- we found matching font(s) but not in the requested optical + -- sizes, so we loop through the matches to find the one with + -- least difference from the requested size. + local closest + local least = math.huge -- initial value is infinity + for i,face in ipairs(found) do + local dsnsize = face.size[1]/10 + local difference = math.abs(dsnsize-size) + if difference < least then + closest = face + least = difference + end + end + if kpse.lookup(closest.filename[1]) then + logs.report("load font", + "font family='%s', subfamily='%s' found: %s", + name, style, closest.filename[1]) + return closest.filename[1], closest.filename[2] + end + end + -- no font found so far, fallback to filename + return specification.name, false + end + elseif not log_message_shown then + logs.report("load font", "no font names database loaded") + log_message_shown = true + end +end + +names.resolvespec = names.resolve -- only supported in mkiv + +function names.set_log_level(level) + if level == 2 then + trace_progress = false + trace_loading = true + elseif level >= 3 then + trace_progress = false + trace_loading = true + trace_search = true + end +end + +local lastislog = 0 + +function log(fmt, ...) + lastislog = 1 + texio.write_nl(format("luaotfload | %s", format(fmt,...))) +end + +logs = logs or { } +logs.report = logs.report or log + +local log = names.log + +-- The progress bar +local function progress(current, total) + if trace_progress then +-- local width = os.getenv("COLUMNS") -2 --doesn't work + local width = 78 + local percent = current/total + local gauge = format("[%s]", rpadd(" ", width, " ")) + if percent > 0 then + local done = rpadd("=", (width * percent) - 1, "=") .. ">" + gauge = format("[%s]", rpadd(done, width, " ") ) + end + if lastislog == 1 then + texio.write_nl("") + lastislog = 0 + end + io.stderr:write("\r"..gauge) + io.stderr:flush() + end +end + +local function font_fullinfo(filename, subfont, texmf) + local t = { } + local f = fontloader.open(filename, subfont) + local m = f and fontloader.to_table(f) + fontloader.close(f) + collectgarbage('collect') + -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size + if m.fontstyle_name then + for _,v in pairs(m.fontstyle_name) do + if v.lang == 1033 then + t.fontstyle_name = v.name + end + end + end + if m.names then + for _,v in pairs(m.names) do + if v.lang == "English (US)" then + t.names = { + -- see http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html + fullname = v.names.compatfull or v.names.fullname, -- 18, 4 + family = v.names.preffamilyname or v.names.family, -- 17, 1 + subfamily = t.fontstyle_name or v.names.prefmodifiers or v.names.subfamily, -- opt. style, 16, 2 + psname = v.names.postscriptname --or t.fontname + } + end + end + end + t.fontname = m.fontname + t.fullname = m.fullname + t.familyname = m.familyname + t.filename = { texmf and basename(filename) or filename, subfont } + t.weight = m.pfminfo.weight + t.width = m.pfminfo.width + t.slant = m.italicangle + -- don't waste the space with zero values + t.size = { + m.design_size ~= 0 and m.design_size or nil, + m.design_range_top ~= 0 and m.design_range_top or nil, + m.design_range_bottom ~= 0 and m.design_range_bottom or nil, + } + + return t +end + +local function load_font(filename, fontnames, status, newfontnames, newstatus, texmf) + local mappings = newfontnames and newfontnames.mappings or { } + local oldmappings = fontnames.mappings or { } + if filename then + local timestamp, db_timestamp + db_timestamp = status[filename] and status[filename].timestamp + timestamp = lfs.attributes(filename, "modification") + newstatus[filename] = { } + newstatus[filename].timestamp = timestamp + newstatus[filename].index = {} + if db_timestamp == timestamp then + for _,v in ipairs(status[filename].index) do + mappings[#mappings+1] = oldmappings[v] + newstatus[filename].index[#newstatus[filename].index+1] = #mappings + end + if trace_loading then + logs.report("font already indexed: %s", filename) + end + return + end + if trace_loading then + logs.report("loading font: %s", filename) + end + local info = fontloader.info(filename) + if info then + if type(info) == "table" and #info > 1 then + for i in ipairs(info) do + local fullinfo = font_fullinfo(filename, i-1, texmf) + mappings[#mappings+1] = fullinfo + newstatus[filename].index[#newstatus[filename].index+1] = #mappings + end + else + local fullinfo = font_fullinfo(filename, false, texmf) + mappings[#mappings+1] = fullinfo + newstatus[filename].index[#newstatus[filename].index+1] = #mappings + end + else + if trace_loading then + logs.report("failed to load %s", filename) + end + end + end +end + +local function path_normalize(path) + --[[ + path normalization: + - a\b\c -> a/b/c + - a/../b -> b + - /cygdrive/a/b -> a:/b + --]] + if os.type == "windows" or os.type == "msdos" or os.name == "cygwin" then + path = path:gsub('\\', '/') + path = path:lower() + -- for cygwin cases... + path = path:gsub('^/cygdrive/(%a)/', '%1:/') + end + path = file.collapse_path(path) + return path +end + +fonts.path_normalize = path_normalize + +local function scan_dir(dirname, fontnames, status, newfontnames, newstatus, recursive, texmf) + --[[ + this function scans a directory and populates the list of fonts + with all the fonts it finds. + - dirname is the name of the directory to scan + - names is the font database to fill + - recursive is whether we scan all directories recursively (always false + in this script) + - texmf is a boolean saying if we are scanning a texmf directory (always + true in this script) + --]] + local list, found = { }, { } + local nbfound = 0 + for _,ext in ipairs { "otf", "ttf", "ttc", "dfont" } do + if recursive then pat = "/**." else pat = "/*." end + if trace_search then + logs.report("scanning '%s' for '%s' fonts", dirname, ext) + end + found = glob(dirname .. pat .. ext) + -- note that glob fails silently on broken symlinks, which happens + -- sometimes in TeX Live. + if trace_search then + logs.report("%s fonts found", #found) + end + nbfound = nbfound + #found + table.append(list, found) + if trace_search then + logs.report("scanning '%s' for '%s' fonts", dirname, upper(ext)) + end + found = glob(dirname .. pat .. upper(ext)) + table.append(list, found) + nbfound = nbfound + #found + end + if trace_search then + logs.report("%d fonts found in '%s'", nbfound, dirname) + end + for _,fnt in ipairs(list) do + fnt = path_normalize(fnt) + load_font(fnt, fontnames, status, newfontnames, newstatus, texmf) + end +end + +local function scan_texmf_tree(fontnames, status, newfontnames, newstatus) + --[[ + The function that scans all fonts in the texmf tree, through kpathsea + variables OPENTYPEFONTS and TTFONTS of texmf.cnf + --]] + if trace_progress then + if expandpath("$OSFONTDIR"):is_empty() then + logs.report("scanning TEXMF fonts:") + else + logs.report("scanning TEXMF and OS fonts:") + end + end + local fontdirs = expandpath("$OPENTYPEFONTS") + fontdirs = fontdirs .. gsub(expandpath("$TTFONTS"), "^\.", "") + if not fontdirs:is_empty() then + local explored_dirs = {} + fontdirs = splitpath(fontdirs) + -- hack, don't scan current dir + table.remove(fontdirs, 1) + count = 0 + for _,d in ipairs(fontdirs) do + if not explored_dirs[d] then + count = count + 1 + progress(count, #fontdirs) + scan_dir(d, fontnames, status, newfontnames, newstatus, false, true) + explored_dirs[d] = true + end + end + end +end + +local function read_fcdata(data) + --[[ + this function takes raw data returned by fc-list, parses it, normalizes the + paths and makes a list out of it. + --]] + local list = { } + for line in data:lines() do + line = line:gsub(": ", "") + local ext = lower(match(line,"^.+%.([^/\\]-)$")) + if ext == "otf" or ext == "ttf" or ext == "ttc" or ext == "dfont" then + list[#list+1] = path_normalize(line:gsub(": ", "")) + end + end + return list +end + +local function scan_os_fonts(fontnames, status, newfontnames, newstatus) + --[[ + This function scans the OS fonts through fontcache (fc-list), it executes + only if OSFONTDIR is empty (which is the case under most Unix by default). + If OSFONTDIR is non-empty, this means that the system fonts it contains have + already been scanned, and thus we don't scan them again. + --]] + if expandpath("$OSFONTDIR"):is_empty() then + if trace_progress then + logs.report("scanning OS fonts:") + end + if trace_search then + logs.report("executing 'fc-list : file' and parsing its result...") + end + local data = io.popen("fc-list : file", 'r') + local list = read_fcdata(data) + data:close() + if trace_search then + logs.report("%d fonts found", #list) + end + count = 0 + for _,fnt in ipairs(list) do + count = count + 1 + progress(count, #list) + load_font(fnt, fontnames, status, newfontnames, newstatus, false) + end + end +end + +local function fontnames_init() + return { + mappings = { }, + version = names.version, + } +end + +local function status_init() + return { + version = names.version, + } +end + +local function update(fontnames, status, force) + --[[ + The main function, scans everything + - fontnames is the final table to return + - force is whether we rebuild it from scratch or not + - status is a table containing the current status of the database + --]] + if force then + fontnames = fontnames_init() + status = status_init() + else + if not fontnames or not fontnames.version or fontnames.version ~= names.version + or not status or not status.version or status.version ~= names.version then + fontnames = fontnames_init() + status = status_init() + if trace_search then + logs.report("no font names database or old one found, generating new one") + end + end + end + local newfontnames = fontnames_init() + local newstatus = status_init() + scan_texmf_tree(fontnames, status, newfontnames, newstatus) + scan_os_fonts (fontnames, status, newfontnames, newstatus) + return newfontnames, newstatus +end + +names.scan = scan_dir +names.update = update diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-ota.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-ota.lua index e2fa3f290da..558e2fc8063 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-ota.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-ota.lua @@ -41,6 +41,8 @@ local insert_node_after = node.insert_after local insert_node_before = node.insert_before local traverse_node_list = node.traverse +local new_glue_node = nodes.glue + local fontdata = fonts.ids local state = attributes.private('state') @@ -302,11 +304,12 @@ function fonts.analyzers.methods.arab(head,font,attr) -- maybe make a special ve end first, last = finish(first,last) if removejoiners then + -- is never head for i=1,#joiners do - head = delete_node(head,joiners[i]) + delete_node(head,joiners[i]) end for i=1,#nonjoiners do - head = replace_node(head,nonjoiners[i],nodes.glue(0)) -- or maybe a kern + replace_node(head,nonjoiners[i],new_glue_node(0)) -- or maybe a kern end end return head, done diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otb.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otb.lua index 127d4c21896..a3d347737c8 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otb.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otb.lua @@ -9,6 +9,7 @@ if not modules then modules = { } end modules ['font-otb'] = { local concat = table.concat local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip local type, next, tonumber, tostring = type, next, tonumber, tostring +local lpegmatch = lpeg.match local otf = fonts.otf local tfm = fonts.tfm @@ -69,7 +70,7 @@ local function resolve_ligatures(tfmdata,ligatures,kind) for k,v in next, ligatures do local lig = v[1] if not done[lig] then - local ligs = split_at_space:match(lig) + local ligs = lpegmatch(split_at_space,lig) if #ligs == 2 then local uc = v[2] local c, f, s = characters[uc], ligs[1], ligs[2] @@ -133,43 +134,12 @@ local function resolve_ligatures(tfmdata,ligatures,kind) end end -local function collect_lookups(otfdata,kind,script,language) - -- maybe store this in the font - local sequences = otfdata.luatex.sequences - if sequences then - local featuremap, featurelist = { }, { } - for s=1,#sequences do - local sequence = sequences[s] - local features = sequence.features - features = features and features[kind] - features = features and (features[script] or features[default] or features[wildcard]) - features = features and (features[language] or features[default] or features[wildcard]) - if features then - local subtables = sequence.subtables - if subtables then - for s=1,#subtables do - local ss = subtables[s] - if not featuremap[s] then - featuremap[ss] = true - featurelist[#featurelist+1] = ss - end - end - end - end - end - if #featurelist > 0 then - return featuremap, featurelist - end - end - return nil, nil -end - local splitter = lpeg.splitat(" ") function prepare_base_substitutions(tfmdata,kind,value) -- we can share some code with the node features if value then local otfdata = tfmdata.shared.otfdata - local validlookups, lookuplist = collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) + local validlookups, lookuplist = otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) if validlookups then local ligatures = { } local unicodes = tfmdata.unicodes -- names to unicodes @@ -177,9 +147,84 @@ function prepare_base_substitutions(tfmdata,kind,value) -- we can share some cod local characters = tfmdata.characters local descriptions = tfmdata.descriptions local changed = tfmdata.changed + -- + local actions = { + substitution = function(p,lookup,k,glyph,unicode) + local pv = p[2] -- p.variant + if pv then + local upv = unicodes[pv] + if upv then + if type(upv) == "table" then + upv = upv[1] + end + if characters[upv] then + if trace_baseinit and trace_singles then + logs.report("define otf","%s: base substitution %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upv)) + end + changed[k] = upv + end + end + end + end, + alternate = function(p,lookup,k,glyph,unicode) + local pc = p[2] -- p.components + if pc then + -- a bit optimized ugliness + if value == 1 then + pc = lpegmatch(splitter,pc) + elseif value == 2 then + local a, b = lpegmatch(splitter,pc) + pc = b or a + else + pc = { lpegmatch(splitter,pc) } + pc = pc[value] or pc[#pc] + end + if pc then + local upc = unicodes[pc] + if upc then + if type(upc) == "table" then + upc = upc[1] + end + if characters[upc] then + if trace_baseinit and trace_alternatives then + logs.report("define otf","%s: base alternate %s %s => %s",cref(kind,lookup),tostring(value),gref(descriptions,k),gref(descriptions,upc)) + end + changed[k] = upc + end + end + end + end + end, + ligature = function(p,lookup,k,glyph,unicode) + local pc = p[2] + if pc then + if trace_baseinit and trace_ligatures then + local upc = { lpegmatch(splitter,pc) } + for i=1,#upc do upc[i] = unicodes[upc[i]] end + -- we assume that it's no table + logs.report("define otf","%s: base ligature %s => %s",cref(kind,lookup),gref(descriptions,upc),gref(descriptions,k)) + end + ligatures[#ligatures+1] = { pc, k } + end + end, + } + -- for k,c in next, characters do local glyph = descriptions[k] - local lookups = glyph.lookups + local lookups = glyph.slookups + if lookups then + for l=1,#lookuplist do + local lookup = lookuplist[l] + local p = lookups[lookup] + if p then + local a = actions[p[1]] + if a then + a(p,lookup,k,glyph,unicode) + end + end + end + end + local lookups = glyph.mlookups if lookups then for l=1,#lookuplist do local lookup = lookuplist[l] @@ -187,62 +232,9 @@ function prepare_base_substitutions(tfmdata,kind,value) -- we can share some cod if ps then for i=1,#ps do local p = ps[i] - local t = p[1] - if t == 'substitution' then - local pv = p[2] -- p.variant - if pv then - local upv = unicodes[pv] - if upv then - if type(upv) == "table" then - upv = upv[1] - end - if characters[upv] then - if trace_baseinit and trace_singles then - logs.report("define otf","%s: base substitution %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upv)) - end - changed[k] = upv - end - end - end - elseif t == 'alternate' then - local pc = p[2] -- p.components - if pc then - -- a bit optimized ugliness - if value == 1 then - pc = splitter:match(pc) - elseif value == 2 then - local a, b = splitter:match(pc) - pc = b or a - else - pc = { splitter:match(pc) } - pc = pc[value] or pc[#pc] - end - if pc then - local upc = unicodes[pc] - if upc then - if type(upc) == "table" then - upc = upc[1] - end - if characters[upc] then - if trace_baseinit and trace_alternatives then - logs.report("define otf","%s: base alternate %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upc)) - end - changed[k] = upc - end - end - end - end - elseif t == 'ligature' and not changed[k] then - local pc = p[2] - if pc then - if trace_baseinit and trace_ligatures then - local upc = { splitter:match(pc) } - for i=1,#upc do upc[i] = unicodes[upc[i]] end - -- we assume that it's no table - logs.report("define otf","%s: base ligature %s => %s",cref(kind,lookup),gref(descriptions,upc),gref(descriptions,k)) - end - ligatures[#ligatures+1] = { pc, k } - end + local a = actions[p[1]] + if a then + a(p,lookup,k,glyph,unicode) end end end @@ -259,37 +251,46 @@ end local function prepare_base_kerns(tfmdata,kind,value) -- todo what kind of kerns, currently all if value then local otfdata = tfmdata.shared.otfdata - local validlookups, lookuplist = collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) + local validlookups, lookuplist = otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) if validlookups then local unicodes = tfmdata.unicodes -- names to unicodes local indices = tfmdata.indices local characters = tfmdata.characters local descriptions = tfmdata.descriptions + local sharedkerns = { } for u, chr in next, characters do local d = descriptions[u] if d then - local dk = d.mykerns + local dk = d.mykerns -- shared if dk then - local t, done = chr.kerns or { }, false - for l=1,#lookuplist do - local lookup = lookuplist[l] - local kerns = dk[lookup] - if kerns then - for k, v in next, kerns do - if v ~= 0 and not t[k] then -- maybe no 0 test here - t[k], done = v, true - if trace_baseinit and trace_kerns then - logs.report("define otf","%s: base kern %s + %s => %s",cref(kind,lookup),gref(descriptions,u),gref(descriptions,k),v) + local s = sharedkerns[dk] + if s == false then + -- skip + elseif s then + chr.kerns = s + else + local t, done = chr.kerns or { }, false + for l=1,#lookuplist do + local lookup = lookuplist[l] + local kerns = dk[lookup] + if kerns then + for k, v in next, kerns do + if v ~= 0 and not t[k] then -- maybe no 0 test here + t[k], done = v, true + if trace_baseinit and trace_kerns then + logs.report("define otf","%s: base kern %s + %s => %s",cref(kind,lookup),gref(descriptions,u),gref(descriptions,k),v) + end end end end end + if done then + sharedkerns[dk] = t + chr.kerns = t -- no empty assignments + else + sharedkerns[dk] = false + end end - if done then - chr.kerns = t -- no empty assignments - end - -- elseif d.kerns then - -- logs.report("define otf","%s: invalid mykerns for %s",cref(kind),gref(descriptions,u)) end end end @@ -304,12 +305,13 @@ end -- to do complete mixed runs and not run featurewise (as we did before). local supported_gsub = { - 'liga','dlig','rlig','hlig', - 'pnum','onum','tnum','lnum', + 'liga', 'dlig', 'rlig', 'hlig', + 'pnum', 'onum', 'tnum', 'lnum', 'zero', - 'smcp','cpsp','c2sc','ornm','aalt', - 'hwid','fwid', + 'smcp', 'cpsp', 'c2sc', 'ornm', 'aalt', + 'hwid', 'fwid', 'ssty', 'rtlm', -- math +-- 'tlig', 'trep', } local supported_gpos = { diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otc.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otc.lua index 059748dc5ef..64e09f8b117 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otc.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otc.lua @@ -21,6 +21,8 @@ local tfm = fonts.tfm -- instead of "script = "DFLT", langs = { 'dflt' }" we now use wildcards (we used to -- have always); some day we can write a "force always when true" trick for other -- features as well +-- +-- we could have a tnum variant as well local extra_lists = { tlig = { diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otf.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otf.lua index 6900b844591..29c56cd0012 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otf.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otf.lua @@ -11,6 +11,7 @@ local utf = unicode.utf8 local concat, getn, utfbyte = table.concat, table.getn, utf.byte local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip local type, next, tonumber, tostring = type, next, tonumber, tostring +local lpegmatch = lpeg.match local trace_private = false trackers.register("otf.private", function(v) trace_private = v end) local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) @@ -18,14 +19,10 @@ local trace_features = false trackers.register("otf.features", function(v local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) local trace_sequences = false trackers.register("otf.sequences", function(v) trace_sequences = v end) local trace_math = false trackers.register("otf.math", function(v) trace_math = v end) -local trace_unimapping = false trackers.register("otf.unimapping", function(v) trace_unimapping = v end) local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) --~ trackers.enable("otf.loading") -local zwnj = 0x200C -local zwj = 0x200D - --[[ldx--

The fontforge table has organized lookups in a certain way. A first implementation of this code was organized featurewise: information related to features was @@ -83,13 +80,16 @@ otf.features.default = otf.features.default or { } otf.enhancers = otf.enhancers or { } otf.glists = { "gsub", "gpos" } -otf.version = 2.636 -- beware: also sync font-mis.lua +otf.version = 2.645 -- beware: also sync font-mis.lua otf.pack = true -- beware: also sync font-mis.lua otf.syncspace = true otf.notdef = false otf.cache = containers.define("fonts", "otf", otf.version, true) otf.cleanup_aat = false -- only context +local wildcard = "*" +local default = "dflt" + --[[ldx--

We start with a lot of tables and related functions.

--ldx]]-- @@ -178,7 +178,7 @@ otf.tables.valid_fields = { local function load_featurefile(ff,featurefile) if featurefile then - featurefile = resolvers.find_file(file.addsuffix(featurefile,'fea')) -- "FONTFEATURES" + featurefile = resolvers.find_file(file.addsuffix(featurefile,'fea'),'fea') if featurefile and featurefile ~= "" then if trace_loading then logs.report("load otf", "featurefile: %s", featurefile) @@ -192,7 +192,7 @@ function otf.enhance(name,data,filename,verbose) local enhancer = otf.enhancers[name] if enhancer then if (verbose ~= nil and verbose) or trace_loading then - logs.report("load otf","enhance: %s",name) + logs.report("load otf","enhance: %s (%s)",name,filename) end enhancer(data,filename) end @@ -207,6 +207,7 @@ local enhancers = { "reorganize mark classes", "reorganize kerns", -- moved here "flatten glyph lookups", "flatten anchor tables", "flatten feature tables", + "simplify glyph lookups", -- some saving "prepare luatex tables", "analyse features", "rehash features", "analyse anchors", "analyse marks", "analyse unicodes", "analyse subtables", @@ -214,6 +215,7 @@ local enhancers = { "share widths", "strip not needed data", "migrate metadata", + "check math parameters", } function otf.load(filename,format,sub,featurefile) @@ -238,9 +240,15 @@ function otf.load(filename,format,sub,featurefile) ff, messages = fontloader.open(filename) end if trace_loading and messages and #messages > 0 then - for m=1,#messages do - logs.report("load otf","warning: %s",messages[m]) + if type(messages) == "string" then + logs.report("load otf","warning: %s",messages) + else + for m=1,#messages do + logs.report("load otf","warning: %s",tostring(messages[m])) + end end + else + logs.report("load otf","font loaded okay") end if ff then load_featurefile(ff,featurefile) @@ -251,6 +259,7 @@ function otf.load(filename,format,sub,featurefile) logs.report("load otf","enhancing ...") for e=1,#enhancers do otf.enhance(enhancers[e],data,filename) + io.flush() -- we want instant messages end if otf.pack and not fonts.verbose then otf.enhance("pack",data,filename) @@ -494,142 +503,7 @@ otf.enhancers["analyse marks"] = function(data,filename) end end -local separator = lpeg.S("_.") -local other = lpeg.C((1 - separator)^1) -local ligsplitter = lpeg.Ct(other * (separator * other)^0) - ---~ print(table.serialize(ligsplitter:match("this"))) ---~ print(table.serialize(ligsplitter:match("this.that"))) ---~ print(table.serialize(ligsplitter:match("japan1.123"))) ---~ print(table.serialize(ligsplitter:match("such_so_more"))) ---~ print(table.serialize(ligsplitter:match("such_so_more.that"))) - -otf.enhancers["analyse unicodes"] = function(data,filename) - local tounicode16, tounicode16sequence = fonts.map.tounicode16, fonts.map.tounicode16sequence - local unicodes = data.luatex.unicodes - -- we need to move this code - unicodes['space'] = unicodes['space'] or 32 -- handly later on - unicodes['hyphen'] = unicodes['hyphen'] or 45 -- handly later on - unicodes['zwj'] = unicodes['zwj'] or zwj -- handly later on - unicodes['zwnj'] = unicodes['zwnj'] or zwnj -- handly later on - -- the tounicode mapping is sparse and only needed for alternatives - local tounicode, originals, ns, nl, private, unknown = { }, { }, 0, 0, fonts.private, format("%04X",utfbyte("?")) - data.luatex.tounicode, data.luatex.originals = tounicode, originals - local lumunic, uparser, oparser - if false then -- will become an option - lumunic = fonts.map.load_lum_table(filename) - lumunic = lumunic and lumunic.tounicode - end - local cidinfo, cidnames, cidcodes = data.cidinfo - local usedmap = cidinfo and cidinfo.usedname - usedmap = usedmap and fonts.cid.map[usedmap] - if usedmap then - oparser = usedmap and fonts.map.make_name_parser(cidinfo.ordering) - cidnames = usedmap.names - cidcodes = usedmap.unicodes - end - uparser = fonts.map.make_name_parser() - local aglmap = fonts.map and fonts.map.agl_to_unicode - for index, glyph in next, data.glyphs do - local name, unic = glyph.name, glyph.unicode or -1 -- play safe - if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then - local unicode = lumunic and lumunic[name] - if unicode then - originals[index], tounicode[index], ns = unicode, tounicode16(unicode), 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 = oparser:match(name) - if foundindex then - unicode = cidcodes[foundindex] -- name to number - if not unicode then - local reference = cidnames[foundindex] -- number to name - if reference then - local foundindex = oparser:match(reference) - if foundindex then - unicode = cidcodes[foundindex] - if unicode then - originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 - end - end - if not unicode then - local foundcodes, multiple = uparser:match(reference) - if foundcodes then - if multiple then - originals[index], tounicode[index], nl, unicode = foundcodes, tounicode16sequence(foundcodes), nl + 1, true - else - originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes - end - end - end - end - end - end - end - -- a.whatever or a_b_c.whatever or a_b_c (no numbers) - if not unicode then - local split = ligsplitter:match(name) - local nplit = (split and #split) or 0 - if nplit == 0 then - -- skip - elseif nplit == 1 then - local base = split[1] - unicode = unicodes[base] or (agl and agl[base]) - if unicode then - if type(unicode) == "table" then - unicode = unicode[1] - end - originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 - end - else - local t = { } - for l=1,nplit do - local base = split[l] - local u = unicodes[base] or (agl and agl[base]) - if not u then - break - elseif type(u) == "table" then - t[#t+1] = u[1] - else - t[#t+1] = u - end - end - if #t > 0 then -- done then - originals[index], tounicode[index], nl, unicode = t, tounicode16sequence(t), nl + 1, true - end - end - end - -- last resort - if not unicode then - local foundcodes, multiple = uparser:match(name) - if foundcodes then - if multiple then - originals[index], tounicode[index], nl, unicode = foundcodes, tounicode16sequence(foundcodes), nl + 1, true - else - originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes - end - end - end - if not unicode then - originals[index], tounicode[index] = 0xFFFD, "FFFD" - end - end - end - if trace_unimapping then - for index, glyph in table.sortedpairs(data.glyphs) do - local toun, name, unic = tounicode[index], glyph.name, glyph.unicode or -1 -- play safe - if toun then - logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun) - else - logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic) - end - end - end - if trace_loading and (ns > 0 or nl > 0) then - logs.report("load otf","enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns) - end -end +otf.enhancers["analyse unicodes"] = fonts.map.add_to_unicode otf.enhancers["analyse subtables"] = function(data,filename) data.luatex = data.luatex or { } @@ -986,6 +860,129 @@ end -- kern: ttf has a table with kerns +--~ otf.enhancers["reorganize kerns"] = function(data,filename) +--~ local glyphs, mapmap, unicodes = data.glyphs, data.luatex.indices, data.luatex.unicodes +--~ local mkdone = false +--~ for index, glyph in next, data.glyphs do +--~ if glyph.kerns then +--~ local mykerns = { } +--~ for k,v in next, glyph.kerns do +--~ local vc, vo, vl = v.char, v.off, v.lookup +--~ if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones +--~ local uvc = unicodes[vc] +--~ if not uvc then +--~ if trace_loading then +--~ logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index) +--~ end +--~ else +--~ if type(vl) ~= "table" then +--~ vl = { vl } +--~ end +--~ for l=1,#vl do +--~ local vll = vl[l] +--~ local mkl = mykerns[vll] +--~ if not mkl then +--~ mkl = { } +--~ mykerns[vll] = mkl +--~ end +--~ if type(uvc) == "table" then +--~ for u=1,#uvc do +--~ mkl[uvc[u]] = vo +--~ end +--~ else +--~ mkl[uvc] = vo +--~ end +--~ end +--~ end +--~ end +--~ end +--~ glyph.mykerns = mykerns +--~ glyph.kerns = nil -- saves space and time +--~ mkdone = true +--~ end +--~ end +--~ if trace_loading and mkdone then +--~ logs.report("load otf", "replacing 'kerns' tables by 'mykerns' tables") +--~ end +--~ if data.kerns then +--~ if trace_loading then +--~ logs.report("load otf", "removing global 'kern' table") +--~ end +--~ data.kerns = nil +--~ end +--~ local dgpos = data.gpos +--~ if dgpos then +--~ for gp=1,#dgpos do +--~ local gpos = dgpos[gp] +--~ local subtables = gpos.subtables +--~ if subtables then +--~ for s=1,#subtables do +--~ local subtable = subtables[s] +--~ local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes +--~ if kernclass then -- the next one is quite slow +--~ for k=1,#kernclass do +--~ local kcl = kernclass[k] +--~ local firsts, seconds, offsets, lookups = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup -- singular +--~ if type(lookups) ~= "table" then +--~ lookups = { lookups } +--~ end +--~ for l=1,#lookups do +--~ local lookup = lookups[l] +--~ -- weird, as maxfirst and maxseconds can have holes +--~ local maxfirsts, maxseconds = getn(firsts), getn(seconds) +--~ if trace_loading then +--~ logs.report("load otf", "adding kernclass %s with %s times %s pairs",lookup, maxfirsts, maxseconds) +--~ end +--~ for fk, fv in next, firsts do +--~ for first in gmatch(fv,"[^ ]+") do +--~ local first_unicode = unicodes[first] +--~ if type(first_unicode) == "number" then +--~ first_unicode = { first_unicode } +--~ end +--~ for f=1,#first_unicode do +--~ local glyph = glyphs[mapmap[first_unicode[f]]] +--~ if glyph then +--~ local mykerns = glyph.mykerns +--~ if not mykerns then +--~ mykerns = { } -- unicode indexed ! +--~ glyph.mykerns = mykerns +--~ end +--~ local lookupkerns = mykerns[lookup] +--~ if not lookupkerns then +--~ lookupkerns = { } +--~ mykerns[lookup] = lookupkerns +--~ end +--~ for sk, sv in next, seconds do +--~ local offset = offsets[(fk-1) * maxseconds + sk] +--~ --~ local offset = offsets[sk] -- (fk-1) * maxseconds + sk] +--~ for second in gmatch(sv,"[^ ]+") do +--~ local second_unicode = unicodes[second] +--~ if type(second_unicode) == "number" then +--~ lookupkerns[second_unicode] = offset +--~ else +--~ for s=1,#second_unicode do +--~ lookupkerns[second_unicode[s]] = offset +--~ end +--~ end +--~ end +--~ end +--~ elseif trace_loading then +--~ logs.report("load otf", "no glyph data for U+%04X", first_unicode[f]) +--~ end +--~ end +--~ end +--~ end +--~ end +--~ end +--~ subtable.comment = "The kernclass table is merged into mykerns in the indexed glyph tables." +--~ subtable.kernclass = { } +--~ end +--~ end +--~ end +--~ end +--~ end +--~ end + otf.enhancers["reorganize kerns"] = function(data,filename) local glyphs, mapmap, unicodes = data.glyphs, data.luatex.indices, data.luatex.unicodes local mkdone = false @@ -1038,6 +1035,9 @@ otf.enhancers["reorganize kerns"] = function(data,filename) end local dgpos = data.gpos if dgpos then + local separator = lpeg.P(" ") + local other = ((1 - separator)^0) / unicodes + local splitter = lpeg.Ct(other * (separator * other)^0) for gp=1,#dgpos do local gpos = dgpos[gp] local subtables = gpos.subtables @@ -1045,54 +1045,75 @@ otf.enhancers["reorganize kerns"] = function(data,filename) for s=1,#subtables do local subtable = subtables[s] local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes - if kernclass then + if kernclass then -- the next one is quite slow for k=1,#kernclass do local kcl = kernclass[k] local firsts, seconds, offsets, lookups = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup -- singular if type(lookups) ~= "table" then lookups = { lookups } end + local split = { } for l=1,#lookups do local lookup = lookups[l] + -- weird, as maxfirst and maxseconds can have holes, first seems to be indexed, seconds starts at 2 local maxfirsts, maxseconds = getn(firsts), getn(seconds) + for _, s in next, firsts do + split[s] = split[s] or lpegmatch(splitter,s) + end + for _, s in next, seconds do + split[s] = split[s] or lpegmatch(splitter,s) + end if trace_loading then logs.report("load otf", "adding kernclass %s with %s times %s pairs",lookup, maxfirsts, maxseconds) end - for fk, fv in next, firsts do - for first in gmatch(fv,"[^ ]+") do - local first_unicode = unicodes[first] - if type(first_unicode) == "number" then - first_unicode = { first_unicode } + local function do_it(fk,first_unicode) + local glyph = glyphs[mapmap[first_unicode]] + if glyph then + local mykerns = glyph.mykerns + if not mykerns then + mykerns = { } -- unicode indexed ! + glyph.mykerns = mykerns end - for f=1,#first_unicode do - local glyph = glyphs[mapmap[first_unicode[f]]] - if glyph then - local mykerns = glyph.mykerns - if not mykerns then - mykerns = { } -- unicode indexed ! - glyph.mykerns = mykerns - end - local lookupkerns = mykerns[lookup] - if not lookupkerns then - lookupkerns = { } - mykerns[lookup] = lookupkerns - end - for sk, sv in next, seconds do - local offset = offsets[(fk-1) * maxseconds + sk] - --~ local offset = offsets[sk] -- (fk-1) * maxseconds + sk] - for second in gmatch(sv,"[^ ]+") do - local second_unicode = unicodes[second] - if type(second_unicode) == "number" then - lookupkerns[second_unicode] = offset - else - for s=1,#second_unicode do - lookupkerns[second_unicode[s]] = offset - end + local lookupkerns = mykerns[lookup] + if not lookupkerns then + lookupkerns = { } + mykerns[lookup] = lookupkerns + end + local baseoffset = (fk-1) * maxseconds + for sk=2,maxseconds do + local sv = seconds[sk] + local offset = offsets[baseoffset + sk] + --~ local offset = offsets[sk] -- (fk-1) * maxseconds + sk] + local splt = split[sv] + if splt then + for i=1,#splt do + local second_unicode = splt[i] + if tonumber(second_unicode) then + lookupkerns[second_unicode] = offset + else + for s=1,#second_unicode do + lookupkerns[second_unicode[s]] = offset end end end - elseif trace_loading then - logs.report("load otf", "no glyph data for U+%04X", first_unicode[f]) + end + end + elseif trace_loading then + logs.report("load otf", "no glyph data for U+%04X", first_unicode) + end + end + for fk=1,#firsts do + local fv = firsts[fk] + local splt = split[fv] + if splt then + for i=1,#splt do + local first_unicode = splt[i] + if tonumber(first_unicode) then + do_it(fk,first_unicode) + else + for f=1,#first_unicode do + do_it(fk,first_unicode[f]) + end end end end @@ -1166,10 +1187,31 @@ otf.enhancers["migrate metadata"] = function(data,filename) metadata.charwidth = pfminfo and pfminfo.avgwidth end +local private_math_parameters = { + "FractionDelimiterSize", + "FractionDelimiterDisplayStyleSize", +} + +otf.enhancers["check math parameters"] = function(data,filename) + local mathdata = data.metadata.math + if mathdata then + for m=1,#private_math_parameters do + local pmp = private_math_parameters[m] + if not mathdata[pmp] then + if trace_loading then + logs.report("load otf", "setting math parameter '%s' to 0", pmp) + end + mathdata[pmp] = 0 + end + end + end +end + otf.enhancers["flatten glyph lookups"] = function(data,filename) for k, v in next, data.glyphs do - if v.lookups then - for kk, vv in next, v.lookups do + local lookups = v.lookups + if lookups then + for kk, vv in next, lookups do for kkk=1,#vv do local vvv = vv[kkk] local s = vvv.specification @@ -1219,6 +1261,31 @@ otf.enhancers["flatten glyph lookups"] = function(data,filename) end end +otf.enhancers["simplify glyph lookups"] = function(data,filename) + for k, v in next, data.glyphs do + local lookups = v.lookups + if lookups then + local slookups, mlookups + for kk, vv in next, lookups do + if #vv == 1 then + if not slookups then + slookups = { } + v.slookups = slookups + end + slookups[kk] = vv[1] + else + if not mlookups then + mlookups = { } + v.mlookups = mlookups + end + mlookups[kk] = vv + end + end + v.lookups = nil + end + end +end + otf.enhancers["flatten anchor tables"] = function(data,filename) for k, v in next, data.glyphs do if v.anchors then @@ -1459,7 +1526,7 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th local variants = m.horiz_variants if variants then local c = char - for n in variants:gmatch("[^ ]+") do + for n in gmatch(variants,"[^ ]+") do local un = unicodes[n] if un and u ~= un then c.next = un @@ -1471,7 +1538,7 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th local variants = m.vert_variants if variants then local c = char - for n in variants:gmatch("[^ ]+") do + for n in gmatch(variants,"[^ ]+") do local un = unicodes[n] if un and u ~= un then c.next = un @@ -1505,8 +1572,6 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th tfm.cidinfo = data.cidinfo tfm.cidinfo.registry = tfm.cidinfo.registry or "" tfm.type = "real" - tfm.stretch = 0 -- stretch - tfm.slant = 0 -- slant tfm.direction = 0 tfm.boundarychar_label = 0 tfm.boundarychar = 65536 @@ -1615,7 +1680,7 @@ function tfm.read_from_open_type(specification) end end end - tfmtable = tfm.scale(tfmtable,s) + tfmtable = tfm.scale(tfmtable,s,specification.relativeid) -- here we resolve the name; file can be relocated, so this info is not in the cache local filename = (otfdata and otfdata.luatex and otfdata.luatex.filename) or specification.filename if not filename then @@ -1635,9 +1700,52 @@ function tfm.read_from_open_type(specification) tfmtable.format = specification.format end tfmtable.name = tfmtable.filename or tfmtable.fullname or tfmtable.fontname + if tfm.fontname_mode == "specification" then + -- not to be used in context ! + local specname = specification.specification + if specname then + tfmtable.name = specname + if trace_defining then + logs.report("define font","overloaded fontname: '%s'",specname) + end + end + end end fonts.logger.save(tfmtable,file.extname(specification.filename),specification) end --~ print(tfmtable.fullname) return tfmtable end + +-- helpers + +function otf.collect_lookups(otfdata,kind,script,language) + -- maybe store this in the font + local sequences = otfdata.luatex.sequences + if sequences then + local featuremap, featurelist = { }, { } + for s=1,#sequences do + local sequence = sequences[s] + local features = sequence.features + features = features and features[kind] + features = features and (features[script] or features[default] or features[wildcard]) + features = features and (features[language] or features[default] or features[wildcard]) + if features then + local subtables = sequence.subtables + if subtables then + for s=1,#subtables do + local ss = subtables[s] + if not featuremap[s] then + featuremap[ss] = true + featurelist[#featurelist+1] = ss + end + end + end + end + end + if #featurelist > 0 then + return featuremap, featurelist + end + end + return nil, nil +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otn.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otn.lua index fb04b367ea0..3aa1927d35f 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otn.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-otn.lua @@ -10,6 +10,9 @@ if not modules then modules = { } end modules ['font-otn'] = { -- much functionality could only be implemented thanks to the husayni font -- of Idris Samawi Hamid to who we dedicate this module. +-- I'm in the process of cleaning up the code (which happens in another +-- file) so don't rely on things staying the same. + -- some day when we can jit this, we can use more functions -- we can use more lpegs when lpeg is extended with function args and so @@ -327,6 +330,7 @@ local function toligature(kind,lookupname,start,stop,char,markflag,discfound) -- next.prev = lignode end lignode.next, lignode.prev = next, prev + start = lignode -- print("start->end",nodes.tosequence(start)) else -- start is the ligature local deletemarks = markflag ~= "mark" @@ -456,7 +460,8 @@ end function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or maybe pass lookup ref local s, stop, discfound = start.next, nil, false - if marks[start.char] then + local startchar = start.char + if marks[startchar] then while s do local id = s.id if id == glyph and s.subtype<256 then @@ -479,7 +484,7 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or ma end if stop and ligature[2] then if trace_ligatures then - local startchar, stopchar = start.char, stop.char + local stopchar = stop.char start = markstoligature(kind,lookupname,start,stop,ligature[2]) logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) else @@ -518,7 +523,7 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or ma end if stop and ligature[2] then if trace_ligatures then - local startchar, stopchar = start.char, stop.char + local stopchar = stop.char start = toligature(kind,lookupname,start,stop,ligature[2],skipmark,discfound) logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) else @@ -1536,6 +1541,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local skipmark, skipligature, skipbase = flags[1], flags[2], flags[3] local someskip = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) local markclass = sequence.markclass -- todo, first we need a proper test + local skipped = false for k=1,#contexts do local match, current, last = true, start, start local ck = contexts[k] @@ -1570,6 +1576,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence 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 @@ -1614,6 +1621,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence 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 @@ -1668,6 +1676,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence 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 @@ -1733,8 +1742,47 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence end else -- actually this needs a more complex treatment for which we will use chainmores +--~ local i = 1 +--~ repeat +--~ local chainlookupname = chainlookups[i] +--~ local chainlookup = lookuptable[chainlookupname] +--~ local cp = chainmores[chainlookup.type] +--~ if cp then +--~ local ok, n +--~ start, ok, n = cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,i,sequence) +--~ -- messy since last can be changed ! +--~ if ok then +--~ done = true +--~ start = start.next +--~ if n then +--~ -- skip next one(s) if ligature +--~ i = i + n - 1 +--~ end +--~ end +--~ else +--~ logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) +--~ end +--~ i = i + 1 +--~ until i > nofchainlookups + local i = 1 repeat +if skipped then + while true do + local char = start.char + 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 + start = start.next + else + break + end + else + break + end + end +end local chainlookupname = chainlookups[i] local chainlookup = lookuptable[chainlookupname] local cp = chainmores[chainlookup.type] @@ -1744,17 +1792,18 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence -- messy since last can be changed ! if ok then done = true - start = start.next - if n then - -- skip next one(s) if ligature - i = i + n - 1 - end + -- skip next one(s) if ligature + i = i + (n or 1) + else + i = i + 1 end else logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) + i = i + 1 end - i = i + 1 + start = start.next until i > nofchainlookups + end else local replacements = ck[7] @@ -1833,9 +1882,6 @@ local resolved = { } -- we only resolve a font,script,language pair once -- todo: pass all these 'locals' in a table --- maybe some day i'll make an alternative that works on 'sub direction runs' which might be --- more efficient for arabic but it has quite some consequences - function fonts.methods.node.otf.features(head,font,attr) if trace_steps then checkstep(head) @@ -1879,7 +1925,7 @@ function fonts.methods.node.otf.features(head,font,attr) local ra = rl [attr] if ra == nil then ra = { } rl [attr] = ra end -- attr can be false -- sequences always > 1 so no need for optimization for s=1,#sequences do - local pardir, txtdir = 0, { } + local pardir, txtdir = 0, { } local success = false local sequence = sequences[s] local r = ra[s] -- cache @@ -1905,12 +1951,10 @@ function fonts.methods.node.otf.features(head,font,attr) -- only first attribute match check, so we assume simple fina's -- default can become a font feature itself if l[language] then ---~ valid, what = true, language valid, what = s_e or a_e, language -- elseif l[default] then -- valid, what = true, default elseif l[wildcard] then ---~ valid, what = true, wildcard valid, what = s_e or a_e, wildcard end if valid then @@ -1945,12 +1989,12 @@ function fonts.methods.node.otf.features(head,font,attr) local handler = handlers[typ] local thecache = featuredata[typ] or { } -- we need to get rid of this slide ! - start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo + local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo while start do local id = start.id if id == glyph then ---~ if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) then - if start.subtype<256 and start.font == font and has_attribute(start,0,attr) then + if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) then +--~ if start.subtype<256 and start.font == font and has_attribute(start,0,attr) then for i=1,#subtables do local lookupname = subtables[i] local lookupcache = thecache[lookupname] @@ -1978,7 +2022,7 @@ function fonts.methods.node.otf.features(head,font,attr) local handler = handlers[typ] local ns = #subtables local thecache = featuredata[typ] or { } - start = head -- local ? + local start = head -- local ? rlmode = 0 -- to be checked ? if ns == 1 then local lookupname = subtables[1] @@ -1989,14 +2033,13 @@ function fonts.methods.node.otf.features(head,font,attr) while start do local id = start.id if id == glyph then ---~ if start.font == font and start.subtype<256 and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then - if start.font == font and start.subtype<256 and has_attribute(start,0,attr) and (not attribute or has_attribute(start,state,attribute)) then +--~ if start.font == font and start.subtype<256 and has_attribute(start,0,attr) and (not attribute or has_attribute(start,state,attribute)) then + if start.font == font and start.subtype<256 and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then local lookupmatch = lookupcache[start.char] if lookupmatch then -- sequence kan weg local ok start, ok = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,1) ---~ texio.write_nl(tostring(lookupname),tostring(lookupmatch),tostring(ok)) if ok then success = true end @@ -2021,25 +2064,6 @@ function fonts.methods.node.otf.features(head,font,attr) -- start = start.next -- end elseif id == whatsit then ---~ if subtype == 7 then ---~ local dir = start.dir ---~ if dir == "+TRT" then ---~ rlmode = -1 ---~ elseif dir == "+TLT" then ---~ rlmode = 1 ---~ else ---~ rlmode = 0 ---~ end ---~ elseif subtype == 6 then ---~ local dir = start.dir ---~ if dir == "TRT" then ---~ rlmode = -1 ---~ elseif dir == "TLT" then ---~ rlmode = 1 ---~ else ---~ rlmode = 0 ---~ end ---~ end local subtype = start.subtype if subtype == 7 then local dir = start.dir @@ -2084,8 +2108,8 @@ function fonts.methods.node.otf.features(head,font,attr) while start do local id = start.id if id == glyph then ---~ if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then - if start.subtype<256 and start.font == font and has_attribute(start,0,attr) and (not attribute or has_attribute(start,state,attribute)) then + if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then +--~ if start.subtype<256 and start.font == font and has_attribute(start,0,attr) and (not attribute or has_attribute(start,state,attribute)) then for i=1,ns do local lookupname = subtables[i] local lookupcache = thecache[lookupname] @@ -2125,59 +2149,40 @@ function fonts.methods.node.otf.features(head,font,attr) -- end elseif id == whatsit then local subtype = start.subtype ---~ if subtype == 7 then ---~ local dir = start.dir ---~ if dir == "+TRT" then ---~ rlmode = -1 ---~ elseif dir == "+TLT" then ---~ rlmode = 1 ---~ else ---~ rlmode = 0 ---~ end ---~ elseif subtype == 6 then ---~ local dir = start.dir ---~ if dir == "TRT" then ---~ rlmode = -1 ---~ elseif dir == "TLT" then ---~ rlmode = 1 ---~ else ---~ rlmode = 0 ---~ end ---~ end - local subtype = start.subtype - if subtype == 7 then - local dir = start.dir - if dir == "+TRT" or dir == "+TLT" then - insert(txtdir,dir) - elseif dir == "-TRT" or dir == "-TLT" then - remove(txtdir) - end - local d = txtdir[#txtdir] - if d == "+TRT" then - rlmode = -1 - elseif d == "+TLT" then - rlmode = 1 - else - rlmode = pardir - end - if trace_directions then - logs.report("fonts","directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) - end - elseif subtype == 6 then - local dir = start.dir - if dir == "TRT" then - pardir = -1 - elseif dir == "TLT" then - pardir = 1 - else - pardir = 0 - end + local subtype = start.subtype + if subtype == 7 then + local dir = start.dir + if dir == "+TRT" or dir == "+TLT" then + insert(txtdir,dir) + elseif dir == "-TRT" or dir == "-TLT" then + remove(txtdir) + end + local d = txtdir[#txtdir] + if d == "+TRT" then + rlmode = -1 + elseif d == "+TLT" then + rlmode = 1 + else rlmode = pardir - --~ txtdir = { } + end if trace_directions then - logs.report("fonts","directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) + logs.report("fonts","directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) end + elseif subtype == 6 then + local dir = start.dir + if dir == "TRT" then + pardir = -1 + elseif dir == "TLT" then + pardir = 1 + else + pardir = 0 end + rlmode = pardir + --~ txtdir = { } + if trace_directions then + logs.report("fonts","directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) + end + end start = start.next else start = start.next @@ -2273,142 +2278,153 @@ local function prepare_lookups(tfmdata) -- we can change the otf table after loading but then we need to adapt base mode -- as well (no big deal) -- - for unicode, glyph in next, descriptions do - local lookups = glyph.lookups - if lookups then - for lookup, whatever in next, lookups do - for i=1,#whatever do -- normaly one - local p = whatever[i] - local what = p[1] - if what == 'substitution' then - local old, new = unicode, unicodes[p[2]] - if type(new) == "table" then - new = new[1] - end - local s = single[lookup] - if not s then s = { } single[lookup] = s end - s[old] = new ---~ if trace_lookups then ---~ logs.report("define otf","lookup %s: substitution %s => %s",lookup,old,new) ---~ end - break - elseif what == 'multiple' then - local old, new = unicode, { } - local m = multiple[lookup] - if not m then m = { } multiple[lookup] = m end - m[old] = new - for pc in gmatch(p[2],"[^ ]+") do - local upc = unicodes[pc] - if type(upc) == "number" then - new[#new+1] = upc - else - new[#new+1] = upc[1] - end - end ---~ if trace_lookups then ---~ logs.report("define otf","lookup %s: multiple %s => %s",lookup,old,concat(new," ")) ---~ end + local action = { + substitution = function(p,lookup,k,glyph,unicode) + local old, new = unicode, unicodes[p[2]] + if type(new) == "table" then + new = new[1] + end + local s = single[lookup] + if not s then s = { } single[lookup] = s end + s[old] = new + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: substitution %s => %s",lookup,old,new) + --~ end + end, + multiple = function (p,lookup,k,glyph,unicode) + local old, new = unicode, { } + local m = multiple[lookup] + if not m then m = { } multiple[lookup] = m end + m[old] = new + for pc in gmatch(p[2],"[^ ]+") do + local upc = unicodes[pc] + if type(upc) == "number" then + new[#new+1] = upc + else + new[#new+1] = upc[1] + end + end + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: multiple %s => %s",lookup,old,concat(new," ")) + --~ end + end, + alternate = function(p,lookup,k,glyph,unicode) + local old, new = unicode, { } + local a = alternate[lookup] + if not a then a = { } alternate[lookup] = a end + a[old] = new + for pc in gmatch(p[2],"[^ ]+") do + local upc = unicodes[pc] + if type(upc) == "number" then + new[#new+1] = upc + else + new[#new+1] = upc[1] + end + end + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: alternate %s => %s",lookup,old,concat(new,"|")) + --~ end + end, + ligature = function (p,lookup,k,glyph,unicode) + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: ligature %s => %s",lookup,p[2],glyph.name) + --~ end + local first = true + local t = ligature[lookup] + if not t then t = { } ligature[lookup] = t end + for s in gmatch(p[2],"[^ ]+") do + if first then + local u = unicodes[s] + if not u then + logs.report("define otf","lookup %s: ligature %s => %s ignored due to invalid unicode",lookup,p[2],glyph.name) break - elseif what == 'alternate' then - local old, new = unicode, { } - local a = alternate[lookup] - if not a then a = { } alternate[lookup] = a end - a[old] = new - for pc in gmatch(p[2],"[^ ]+") do - local upc = unicodes[pc] - if type(upc) == "number" then - new[#new+1] = upc - else - new[#new+1] = upc[1] - end + elseif type(u) == "number" then + if not t[u] then + t[u] = { { } } end ---~ if trace_lookups then ---~ logs.report("define otf","lookup %s: alternate %s => %s",lookup,old,concat(new,"|")) ---~ end - break - elseif what == "ligature" then ---~ if trace_lookups then ---~ logs.report("define otf","lookup %s: ligature %s => %s",lookup,p[2],glyph.name) ---~ end - local first = true - local t = ligature[lookup] - if not t then t = { } ligature[lookup] = t end - for s in gmatch(p[2],"[^ ]+") do - if first then - local u = unicodes[s] - if not u then - logs.report("define otf","lookup %s: ligature %s => %s ignored due to invalid unicode",lookup,p[2],glyph.name) - break - elseif type(u) == "number" then - if not t[u] then - t[u] = { { } } - end - t = t[u] - else - local tt = t - local tu - for i=1,#u do - local u = u[i] - if i==1 then - if not t[u] then - t[u] = { { } } - end - tu = t[u] - t = tu - else - if not t[u] then - tt[u] = tu - end - end - end + t = t[u] + else + local tt = t + local tu + for i=1,#u do + local u = u[i] + if i==1 then + if not t[u] then + t[u] = { { } } end - first = false + tu = t[u] + t = tu else - s = unicodes[s] - local t1 = t[1] - if not t1[s] then - t1[s] = { { } } + if not t[u] then + tt[u] = tu end - t = t1[s] end end - t[2] = unicode - elseif what == 'position' then - -- not used - local s = position[lookup] - if not s then s = { } position[lookup] = s end - s[unicode] = p[2] -- direct pointer to kern spec - elseif what == 'pair' then - local s = pair[lookup] - if not s then s = { } pair[lookup] = s end - local others = s[unicode] - if not others then others = { } s[unicode] = others end - -- todo: fast check for space - local two = p[2] - local upc = unicodes[two] - if not upc then - for pc in gmatch(two,"[^ ]+") do - local upc = unicodes[pc] - if type(upc) == "number" then - others[upc] = p -- direct pointer to main table - else - for i=1,#upc do - others[upc[i]] = p -- direct pointer to main table - end - end - end - elseif type(upc) == "number" then - others[upc] = p -- direct pointer to main table - else - for i=1,#upc do - others[upc[i]] = p -- direct pointer to main table - end + end + first = false + else + s = unicodes[s] + local t1 = t[1] + if not t1[s] then + t1[s] = { { } } + end + t = t1[s] + end + end + t[2] = unicode + end, + position = function(p,lookup,k,glyph,unicode) + -- not used + local s = position[lookup] + if not s then s = { } position[lookup] = s end + s[unicode] = p[2] -- direct pointer to kern spec + end, + pair = function(p,lookup,k,glyph,unicode) + local s = pair[lookup] + if not s then s = { } pair[lookup] = s end + local others = s[unicode] + if not others then others = { } s[unicode] = others end + -- todo: fast check for space + local two = p[2] + local upc = unicodes[two] + if not upc then + for pc in gmatch(two,"[^ ]+") do + local upc = unicodes[pc] + if type(upc) == "number" then + others[upc] = p -- direct pointer to main table + else + for i=1,#upc do + others[upc[i]] = p -- direct pointer to main table end ---~ if trace_lookups then ---~ logs.report("define otf","lookup %s: pair for U+%04X",lookup,unicode) ---~ end end end + elseif type(upc) == "number" then + others[upc] = p -- direct pointer to main table + else + for i=1,#upc do + others[upc[i]] = p -- direct pointer to main table + end + end + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: pair for U+%04X",lookup,unicode) + --~ end + end, + } + -- + for unicode, glyph in next, descriptions do + local lookups = glyph.slookups + if lookups then + for lookup, p in next, lookups do + action[p[1]](p,lookup,k,glyph,unicode) + end + end + local lookups = glyph.mlookups + if lookups then + for lookup, whatever in next, lookups do + for i=1,#whatever do -- normaly one + local p = whatever[i] + action[p[1]](p,lookup,k,glyph,unicode) + end end end local list = glyph.mykerns @@ -2417,9 +2433,9 @@ local function prepare_lookups(tfmdata) local k = kerns[lookup] if not k then k = { } kerns[lookup] = k end k[unicode] = krn -- ref to glyph, saves lookup ---~ if trace_lookups then ---~ logs.report("define otf","lookup %s: kern for U+%04X",lookup,unicode) ---~ end + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: kern for U+%04X",lookup,unicode) + --~ end end end local oanchor = glyph.anchors @@ -2433,9 +2449,9 @@ local function prepare_lookups(tfmdata) local f = mark[lookup] if not f then f = { } mark[lookup] = f end f[unicode] = anchors -- ref to glyph, saves lookup ---~ if trace_lookups then ---~ logs.report("define otf","lookup %s: mark anchor %s for U+%04X",lookup,name,unicode) ---~ end + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: mark anchor %s for U+%04X",lookup,name,unicode) + --~ end end end end @@ -2447,9 +2463,9 @@ local function prepare_lookups(tfmdata) local f = cursive[lookup] if not f then f = { } cursive[lookup] = f end f[unicode] = anchors -- ref to glyph, saves lookup ---~ if trace_lookups then ---~ logs.report("define otf","lookup %s: exit anchor %s for U+%04X",lookup,name,unicode) ---~ end + --~ if trace_lookups then + --~ logs.report("define otf","lookup %s: exit anchor %s for U+%04X",lookup,name,unicode) + --~ end end end end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-ott.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-ott.lua index 55cb734d675..d26c55f4b82 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-ott.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-ott.lua @@ -682,6 +682,14 @@ for k, v in pairs(to_features) do to_features[lower(k)] = v end +otf.meanings.checkers = { + rand = function(v) + return v and "random" + end +} + +local checkers = otf.meanings.checkers + function otf.meanings.normalize(features) local h = { } for k,v in next, features do @@ -710,7 +718,9 @@ function otf.meanings.normalize(features) v = b end end - h[to_features[k] or k] = v + k = to_features[k] or k + local c = checkers[k] + h[k] = c and c(v) or v end end return h diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-tfm.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-tfm.lua index efea9def3f9..4b217a3c407 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-tfm.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-tfm.lua @@ -36,6 +36,7 @@ fonts.initializers = fonts.initializers or { } fonts.initializers.common = fonts.initializers.common or { } local fontdata = fonts.ids +local disc = node.id('disc') local glyph = node.id('glyph') local set_attribute = node.set_attribute @@ -47,33 +48,9 @@ supplied by .

tfm.resolve_vf = true -- false tfm.share_base_kerns = false -- true (.5 sec slower on mk but brings down mem from 410M to 310M, beware: then script/lang share too) tfm.mathactions = { } +tfm.fontname_mode = "fullpath" -function tfm.enhance(tfmdata,specification) - local name, size = specification.name, specification.size - local encoding, filename = match(name,"^(.-)%-(.*)$") -- context: encoding-name.* - if filename and encoding and fonts.enc.known[encoding] then - local data = fonts.enc.load(encoding) - if data then - local characters = tfmdata.characters - tfmdata.encoding = encoding - local vector = data.vector - local original = { } - for k, v in next, characters do - v.name = vector[k] - v.index = k - original[k] = v - end - for k,v in next, data.unicodes do - if k ~= v then - if trace_defining then - logs.report("define font","mapping %s onto %s",k,v) - end - characters[k] = original[v] - end - end - end - end -end +tfm.enhance = tfm.enhance or function() end function tfm.read_from_tfm(specification) local fname, tfmdata = specification.filename or "", nil @@ -186,36 +163,36 @@ fonts.trace_scaling = false -- basekerns are scaled and will be hashed by table id -- sharedkerns are unscaled and are be hashed by concatenated indexes -function tfm.check_base_kerns(tfmdata) - if tfm.share_base_kerns then - local sharedkerns = tfmdata.sharedkerns - if sharedkerns then - local basekerns = { } - tfmdata.basekerns = basekerns - return sharedkerns, basekerns - end - end - return nil, nil -end +--~ function tfm.check_base_kerns(tfmdata) +--~ if tfm.share_base_kerns then +--~ local sharedkerns = tfmdata.sharedkerns +--~ if sharedkerns then +--~ local basekerns = { } +--~ tfmdata.basekerns = basekerns +--~ return sharedkerns, basekerns +--~ end +--~ end +--~ return nil, nil +--~ end -function tfm.prepare_base_kerns(tfmdata) - if tfm.share_base_kerns and not tfmdata.sharedkerns then - local sharedkerns = { } - tfmdata.sharedkerns = sharedkerns - for u, chr in next, tfmdata.characters do - local kerns = chr.kerns - if kerns then - local hash = concat(sortedkeys(kerns), " ") - local base = sharedkerns[hash] - if not base then - sharedkerns[hash] = kerns - else - chr.kerns = base - end - end - end - end -end +--~ function tfm.prepare_base_kerns(tfmdata) +--~ if tfm.share_base_kerns and not tfmdata.sharedkerns then +--~ local sharedkerns = { } +--~ tfmdata.sharedkerns = sharedkerns +--~ for u, chr in next, tfmdata.characters do +--~ local kerns = chr.kerns +--~ if kerns then +--~ local hash = concat(sortedkeys(kerns), " ") +--~ local base = sharedkerns[hash] +--~ if not base then +--~ sharedkerns[hash] = kerns +--~ else +--~ chr.kerns = base +--~ end +--~ end +--~ end +--~ end +--~ end -- we can have cache scaled characters when we are in node mode and don't have -- protruding and expansion: hash == fullname @ size @ protruding @ expansion @@ -228,13 +205,21 @@ local charactercache = { } -- a virtual font has italic correction make sure to set the -- has_italic flag. Some more flags will be added in the future. -function tfm.do_scale(tfmtable, scaledpoints) - tfm.prepare_base_kerns(tfmtable) -- optimalization +function tfm.calculate_scale(tfmtable, scaledpoints, relativeid) if scaledpoints < 0 then scaledpoints = (- scaledpoints/1000) * tfmtable.designsize -- already in sp end - local delta = scaledpoints/(tfmtable.units or 1000) -- brr, some open type fonts have 2048 - local t = { } + local units = tfmtable.units or 1000 + local delta = scaledpoints/units -- brr, some open type fonts have 2048 + return scaledpoints, delta, units +end + +function tfm.do_scale(tfmtable, scaledpoints, relativeid) + -- tfm.prepare_base_kerns(tfmtable) -- optimalization + local t = { } -- the new table + local scaledpoints, delta, units = tfm.calculate_scale(tfmtable, scaledpoints, relativeid) + t.units_per_em = units or 1000 + local hdelta, vdelta = delta, delta -- unicoded unique descriptions shared cidinfo characters changed parameters indices for k,v in next, tfmtable do if type(v) == "table" then @@ -243,6 +228,19 @@ function tfm.do_scale(tfmtable, scaledpoints) t[k] = v end end + local extend_factor = tfmtable.extend_factor or 0 + if extend_factor ~= 0 and extend_factor ~= 1 then + hdelta = hdelta * extend_factor + t.extend = extend_factor * 1000 + else + t.extend = 1000 + end + local slant_factor = tfmtable.slant_factor or 0 + if slant_factor ~= 0 then + t.slant = slant_factor * 1000 + else + t.slant = 0 + end -- status local isvirtual = tfmtable.type == "virtual" or tfmtable.virtualized local hasmath = (tfmtable.math_parameters ~= nil and next(tfmtable.math_parameters) ~= nil) or (tfmtable.MathConstants ~= nil and next(tfmtable.MathConstants) ~= nil) @@ -270,12 +268,12 @@ t.colorscheme = tfmtable.colorscheme local tfmp = tfmtable.parameters -- let's check for indexes -- tp.slant = (tfmp.slant or tfmp[1] or 0) - tp.space = (tfmp.space or tfmp[2] or 0)*delta - tp.space_stretch = (tfmp.space_stretch or tfmp[3] or 0)*delta - tp.space_shrink = (tfmp.space_shrink or tfmp[4] or 0)*delta - tp.x_height = (tfmp.x_height or tfmp[5] or 0)*delta - tp.quad = (tfmp.quad or tfmp[6] or 0)*delta - tp.extra_space = (tfmp.extra_space or tfmp[7] or 0)*delta + tp.space = (tfmp.space or tfmp[2] or 0)*hdelta + tp.space_stretch = (tfmp.space_stretch or tfmp[3] or 0)*hdelta + tp.space_shrink = (tfmp.space_shrink or tfmp[4] or 0)*hdelta + tp.x_height = (tfmp.x_height or tfmp[5] or 0)*vdelta + tp.quad = (tfmp.quad or tfmp[6] or 0)*hdelta + tp.extra_space = (tfmp.extra_space or tfmp[7] or 0)*hdelta local protrusionfactor = (tp.quad ~= 0 and 1000/tp.quad) or 0 local tc = t.characters local characters = tfmtable.characters @@ -289,14 +287,15 @@ t.colorscheme = tfmtable.colorscheme local defaultheight = luatex and luatex.defaultheight or 0 local defaultdepth = luatex and luatex.defaultdepth or 0 -- experimental, sharing kerns (unscaled and scaled) saves memory - local sharedkerns, basekerns = tfm.check_base_kerns(tfmtable) + -- local sharedkerns, basekerns = tfm.check_base_kerns(tfmtable) -- loop over descriptions (afm and otf have descriptions, tfm not) -- there is no need (yet) to assign a value to chr.tonunicode - local scaledwidth = defaultwidth * delta - local scaledheight = defaultheight * delta - local scaleddepth = defaultdepth * delta + local scaledwidth = defaultwidth * hdelta + local scaledheight = defaultheight * vdelta + local scaleddepth = defaultdepth * vdelta local stackmath = tfmtable.ignore_stack_math ~= true -local private = fonts.private + local private = fonts.private + local sharedkerns = { } for k,v in next, characters do local chr, description, index if ischanged then @@ -317,9 +316,9 @@ local private = fonts.private local width = description.width local height = description.height local depth = description.depth - if width then width = delta*width else width = scaledwidth end - if height then height = delta*height else height = scaledheight end - -- if depth then depth = delta*depth else depth = scaleddepth end + if width then width = hdelta*width else width = scaledwidth end + if height then height = vdelta*height else height = scaledheight end + -- if depth then depth = vdelta*depth else depth = scaleddepth end if depth and depth ~= 0 then depth = delta*depth if nameneeded then @@ -383,7 +382,7 @@ local private = fonts.private if hasitalic then local vi = description.italic or v.italic if vi and vi ~= 0 then - chr.italic = vi*delta + chr.italic = vi*hdelta end end -- to be tested @@ -399,19 +398,19 @@ local private = fonts.private for i=1,#vv do local vvi = vv[i] t[i] = { - ["start"] = (vvi["start"] or 0)*delta, - ["end"] = (vvi["end"] or 0)*delta, - ["advance"] = (vvi["advance"] or 0)*delta, + ["start"] = (vvi["start"] or 0)*vdelta, + ["end"] = (vvi["end"] or 0)*vdelta, + ["advance"] = (vvi["advance"] or 0)*vdelta, ["extender"] = vvi["extender"], ["glyph"] = vvi["glyph"], } end chr.vert_variants = t ---~ local ic = v.vert_italic_correction ---~ if ic then ---~ chr.italic = ic * delta ---~ print(format("0x%05X -> %s",k,chr.italic)) ---~ end + --~ local ic = v.vert_italic_correction + --~ if ic then + --~ chr.italic = ic * hdelta + --~ print(format("0x%05X -> %s",k,chr.italic)) + --~ end else local hv = v.horiz_variants if hv then @@ -419,9 +418,9 @@ local private = fonts.private for i=1,#hv do local hvi = hv[i] t[i] = { - ["start"] = (hvi["start"] or 0)*delta, - ["end"] = (hvi["end"] or 0)*delta, - ["advance"] = (hvi["advance"] or 0)*delta, + ["start"] = (hvi["start"] or 0)*hdelta, + ["end"] = (hvi["end"] or 0)*hdelta, + ["advance"] = (hvi["advance"] or 0)*hdelta, ["extender"] = hvi["extender"], ["glyph"] = hvi["glyph"], } @@ -432,31 +431,23 @@ local private = fonts.private end local vt = description.top_accent if vt then - chr.top_accent = delta*vt + chr.top_accent = vdelta*vt end if stackmath then local mk = v.mathkerns if mk then local kerns = { } - -- for k, v in next, mk do - -- local kk = { } - -- for i=1,#v do - -- local vi = v[i] - -- kk[i] = { height = delta*vi.height, kern = delta*vi.kern } - -- end - -- kerns[k] = kk - -- end local v = mk.top_right if v then local k = { } for i=1,#v do local vi = v[i] - k[i] = { height = delta*vi.height, kern = delta*vi.kern } + k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern } end kerns.top_right = k end local v = mk.top_left if v then local k = { } for i=1,#v do local vi = v[i] - k[i] = { height = delta*vi.height, kern = delta*vi.kern } + k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern } end kerns.top_left = k end local v = mk.bottom_left if v then local k = { } for i=1,#v do local vi = v[i] - k[i] = { height = delta*vi.height, kern = delta*vi.kern } + k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern } end kerns.bottom_left = k end local v = mk.bottom_right if v then local k = { } for i=1,#v do local vi = v[i] - k[i] = { height = delta*vi.height, kern = delta*vi.kern } + k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern } end kerns.bottom_right = k end chr.mathkern = kerns -- singular end @@ -465,19 +456,26 @@ local private = fonts.private if not nodemode then local vk = v.kerns if vk then - if sharedkerns then - local base = basekerns[vk] -- hashed by table id, not content - if not base then - base = {} - for k,v in next, vk do base[k] = v*delta end - basekerns[vk] = base - end - chr.kerns = base - else - local tt = {} - for k,v in next, vk do tt[k] = v*delta end - chr.kerns = tt + --~ if sharedkerns then + --~ local base = basekerns[vk] -- hashed by table id, not content + --~ if not base then + --~ base = {} + --~ for k,v in next, vk do base[k] = v*hdelta end + --~ basekerns[vk] = base + --~ end + --~ chr.kerns = base + --~ else + --~ local tt = {} + --~ for k,v in next, vk do tt[k] = v*hdelta end + --~ chr.kerns = tt + --~ end + local s = sharedkerns[vk] + if not s then + s = { } + for k,v in next, vk do s[k] = v*hdelta end + sharedkerns[vk] = s end + chr.kerns = s end local vl = v.ligatures if vl then @@ -496,6 +494,8 @@ local private = fonts.private local vc = v.commands if vc then -- we assume non scaled commands here + -- tricky .. we need to scale pseudo math glyphs too + -- which is why we deal with rules too local ok = false for i=1,#vc do local key = vc[i][1] @@ -509,8 +509,12 @@ local private = fonts.private for i=1,#vc do local ivc = vc[i] local key = ivc[1] - if key == "right" or key == "down" then - tt[#tt+1] = { key, ivc[2]*delta } + if key == "right" then + tt[#tt+1] = { key, ivc[2]*hdelta } + elseif key == "down" then + tt[#tt+1] = { key, ivc[2]*vdelta } + elseif key == "rule" then + tt[#tt+1] = { key, ivc[2]*vdelta, ivc[3]*hdelta } else -- not comment tt[#tt+1] = ivc -- shared since in cache and untouched end @@ -522,13 +526,12 @@ local private = fonts.private end end tc[k] = chr ---~ if k == 0x222B then ---~ print(t.fontname,table.serialize(chr)) ---~ end end -- t.encodingbytes, t.filename, t.fullname, t.name: elsewhere t.size = scaledpoints t.factor = delta + t.hfactor = hdelta + t.vfactor = vdelta if t.fonts then t.fonts = table.fastcopy(t.fonts) -- maybe we virtualize more afterwards end @@ -536,7 +539,7 @@ local private = fonts.private -- mathematics.extras.copy(t) -- can be done elsewhere if needed local ma = tfm.mathactions for i=1,#ma do - ma[i](t,tfmtable,delta) + ma[i](t,tfmtable,delta,hdelta,vdelta) -- what delta? end end -- needed for \high cum suis @@ -593,21 +596,19 @@ local lastfont = nil -- -- flushing the kern and ligature tables from memory saves a lot (only -- base mode) but it complicates vf building where the new characters --- demand this data - ---~ for id, f in pairs(fonts.ids) do -- or font.fonts ---~ local ffi = font.fonts[id] ---~ f.characters = ffi.characters ---~ f.kerns = ffi.kerns ---~ f.ligatures = ffi.ligatures ---~ end +-- demand this data .. solution: functions that access them function tfm.cleanup_table(tfmdata) -- we need a cleanup callback, now we miss the last one if tfm.auto_cleanup then -- ok, we can hook this into everyshipout or so ... todo if tfmdata.type == 'virtual' or tfmdata.virtualized then for k, v in next, tfmdata.characters do - if v.commands then v.commands = nil end + if v.commands then v.commands = nil end + -- if v.kerns then v.kerns = nil end end + else + -- for k, v in next, tfmdata.characters do + -- if v.kerns then v.kerns = nil end + -- end end end end @@ -615,8 +616,8 @@ end function tfm.cleanup(tfmdata) -- we need a cleanup callback, now we miss the last one end -function tfm.scale(tfmtable, scaledpoints) - local t, factor = tfm.do_scale(tfmtable, scaledpoints) +function tfm.scale(tfmtable, scaledpoints, relativeid) + local t, factor = tfm.do_scale(tfmtable, scaledpoints, relativeid) t.factor = factor t.ascender = factor*(tfmtable.ascender or 0) t.descender = factor*(tfmtable.descender or 0) @@ -650,7 +651,8 @@ function fonts.analyzers.aux.setstate(head,font) local descriptions = tfmdata.descriptions local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean while current do - if current.id == glyph and current.font == font then + local id = current.id + if id == glyph and current.font == font then local d = descriptions[current.char] if d then if d.class == "mark" then @@ -671,6 +673,10 @@ function fonts.analyzers.aux.setstate(head,font) end first, last, n = nil, nil, 0 end + elseif id == disc then + -- always in the middle + set_attribute(current,state,2) -- midi + last = current else -- finish if first and first == last then set_attribute(last,state,4) -- isol @@ -698,155 +704,8 @@ function tfm.replacements(tfm,value) -- tfm.characters[0x0060] = tfm.characters[0x2018] end --- auto complete font with missing composed characters - -table.insert(fonts.manipulators,"compose") - -function fonts.initializers.common.compose(tfmdata,value) - if value then - fonts.vf.aux.compose_characters(tfmdata) - end -end - --- tfm features, experimental - -tfm.features = tfm.features or { } -tfm.features.list = tfm.features.list or { } -tfm.features.default = tfm.features.default or { } - -function tfm.enhance(tfmdata,specification) - -- we don't really share tfm data because we always reload - -- but this is more in sycn with afm and such - local features = (specification.features and specification.features.normal ) or { } - tfmdata.shared = tfmdata.shared or { } - tfmdata.shared.features = features - -- tfmdata.shared.tfmdata = tfmdata -- circular - tfmdata.filename = specification.name - if not features.encoding then - local name, size = specification.name, specification.size - local encoding, filename = match(name,"^(.-)%-(.*)$") -- context: encoding-name.* - if filename and encoding and fonts.enc.known[encoding] then - features.encoding = encoding - end - end - tfm.set_features(tfmdata) -end - -function tfm.set_features(tfmdata) - -- todo: no local functions - local shared = tfmdata.shared --- local tfmdata = shared.tfmdata - local features = shared.features - if not table.is_empty(features) then - local mode = tfmdata.mode or fonts.mode - local fi = fonts.initializers[mode] - if fi and fi.tfm then - local function initialize(list) -- using tex lig and kerning - if list then - for i=1,#list do - local f = list[i] - local value = features[f] - if value and fi.tfm[f] then -- brr - if tfm.trace_features then - logs.report("define font","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.name or 'unknown') - end - fi.tfm[f](tfmdata,value) - mode = tfmdata.mode or fonts.mode - fi = fonts.initializers[mode] - end - end - end - end - initialize(fonts.triggers) - initialize(tfm.features.list) - initialize(fonts.manipulators) - end - local fm = fonts.methods[mode] - if fm and fm.tfm then - local function register(list) -- node manipulations - if list then - for i=1,#list do - local f = list[i] - if features[f] and fm.tfm[f] then -- brr - if not shared.processors then -- maybe also predefine - shared.processors = { fm.tfm[f] } - else - shared.processors[#shared.processors+1] = fm.tfm[f] - end - end - end - end - end - register(tfm.features.list) - end - end -end - -function tfm.features.register(name,default) - tfm.features.list[#tfm.features.list+1] = name - tfm.features.default[name] = default -end - -function tfm.reencode(tfmdata,encoding) - if encoding and fonts.enc.known[encoding] then - local data = fonts.enc.load(encoding) - if data then - local characters, original, vector = tfmdata.characters, { }, data.vector - tfmdata.encoding = encoding -- not needed - for k, v in next, characters do - v.name, v.index, original[k] = vector[k], k, v - end - for k,v in next, data.unicodes do - if k ~= v then - if trace_defining then - logs.report("define font","reencoding U+%04X to U+%04X",k,v) - end - characters[k] = original[v] - end - end - end - end -end - -tfm.features.register('reencode') - -fonts.initializers.base.tfm.reencode = tfm.reencode -fonts.initializers.node.tfm.reencode = tfm.reencode - -fonts.enc = fonts.enc or { } -fonts.enc.remappings = fonts.enc.remappings or { } - -function tfm.remap(tfmdata,remapping) - local vector = remapping and fonts.enc.remappings[remapping] - if vector then - local characters, original = tfmdata.characters, { } - for k, v in next, characters do - original[k], characters[k] = v, nil - end - for k,v in next, vector do - if k ~= v then - if trace_defining then - logs.report("define font","remapping U+%04X to U+%04X",k,v) - end - local c = original[k] - characters[v] = c - c.index = k - end - end - tfmdata.encodingbytes = 2 - tfmdata.format = 'type1' - end -end - -tfm.features.register('remap') - -fonts.initializers.base.tfm.remap = tfm.remap -fonts.initializers.node.tfm.remap = tfm.remap - -- status info statistics.register("fonts load time", function() - if statistics.elapsedindeed(fonts) then - return format("%s seconds",statistics.elapsedtime(fonts)) - end + return statistics.elapsedseconds(fonts) end) diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-xtx.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-xtx.lua index d5397da3aa3..0fa2d696ab0 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-xtx.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-font-xtx.lua @@ -74,7 +74,7 @@ local function isstyle(s) elseif v:find("^s=") then list.optsize = v:split("=")[2] elseif v == "aat" or v == "icu" or v == "gr" then - logs.report("define font", "unsupported font option: %s", v) + logs.report("load font", "unsupported font option: %s", v) elseif not v:is_empty() then list.style = v:gsub("[^%a%d]", "") end @@ -143,13 +143,17 @@ local function parse_script(script) if otf.tables.scripts[script] then local dflt if otf.tables.defaults[script] then + logs.report("load font", "auto-selecting default features for script: %s", script) dflt = otf.tables.defaults[script] else + logs.report("load font", "auto-selecting default features for script: dflt (was %s)", script) dflt = otf.tables.defaults["dflt"] end for _,v in next, dflt do list[v] = "yes" end + else + logs.report("load font", "unknown script: %s", script) end end @@ -158,9 +162,6 @@ local function isfile () list.lookup = 'file' end local function isname () list.lookup = 'name' end local function thename(s) list.name = s end local function issub (v) list.sub = v end -local function istrue (s) list[s] = 'yes' end ---KH local function isfalse(s) list[s] = 'no' end -local function isfalse(s) list[s] = nil end -- see mpg/luaotfload#4 local function iskey (k,v) if k == "script" then parse_script(v) @@ -168,6 +169,9 @@ local function iskey (k,v) list[k] = v end +local function istrue (s) list[s] = true end +local function isfalse(s) list[s] = false end + local spaces = lpeg.P(" ")^0 -- ER: now accepting names like C:/program files/texlive/2009/... local namespec = (lpeg.R("az", "AZ") * lpeg.P(":"))^-1 * (1-lpeg.S("/:("))^1 -- was: (1-lpeg.S("/: ("))^0 @@ -175,26 +179,21 @@ local crapspec = spaces * lpeg.P("/") * (((1-lpeg.P(":"))^0)/isstyle) * spaces -- ER: can't understand why the 'file:' thing doesn't work with fontnames starting by c:... local filename = (lpeg.P("file:")/isfile * (namespec/thename)) + (lpeg.P("[") * lpeg.P(true)/isname * (((1-lpeg.P("]"))^0)/thename) * lpeg.P("]")) local fontname = (lpeg.P("name:")/isname * (namespec/thename)) + lpeg.P(true)/issome * (namespec/thename) -local sometext = (lpeg.R("az") + lpeg.R("AZ") + lpeg.R("09"))^1 +local sometext = (lpeg.R("az","AZ","09") + lpeg.S("+-."))^1 local truevalue = lpeg.P("+") * spaces * (sometext/istrue) local falsevalue = lpeg.P("-") * spaces * (sometext/isfalse) -local someval = (lpeg.S("+-.") + sometext)^1 -local keyvalue = (lpeg.C(sometext) * spaces * lpeg.P("=") * spaces * lpeg.C(someval))/iskey +local keyvalue = (lpeg.C(sometext) * spaces * lpeg.P("=") * spaces * lpeg.C(sometext))/iskey local somevalue = sometext/istrue local subvalue = lpeg.P("(") * (lpeg.C(lpeg.P(1-lpeg.S("()"))^1)/issub) * lpeg.P(")") -- for Kim local option = spaces * (keyvalue + falsevalue + truevalue + somevalue) * spaces local options = lpeg.P(":") * spaces * (lpeg.P(";")^0 * option)^0 local pattern = (filename + fontname) * subvalue^0 * crapspec^0 * options^0 +local normalize_meanings = fonts.otf.meanings.normalize + function fonts.define.specify.colonized(specification) -- xetex mode list = { } lpegmatch(pattern,specification.specification) - for k, v in next, list do - list[k] = v:is_boolean() - if type(list[a]) == "nil" then - list[k] = v - end - end if list.style then specification.style = list.style list.style = nil @@ -215,7 +214,8 @@ function fonts.define.specify.colonized(specification) -- xetex mode specification.sub = list.sub list.sub = nil end - specification.features.normal = list +-- specification.features.normal = list + specification.features.normal = normalize_meanings(list) return specification end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-luat-dum.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-luat-dum.lua index ffabda8e114..3946b6f399e 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-luat-dum.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-luat-dum.lua @@ -23,6 +23,11 @@ trackers = { enable = dummyfunction, disable = dummyfunction, } +experiments = { + register = dummyfunction, + enable = dummyfunction, + disable = dummyfunction, +} storage = { register = dummyfunction, shared = { }, @@ -37,6 +42,9 @@ tasks = { appendaction = dummyfunction, prependaction = dummyfunction, } +callbacks = { + register = function(n,f) return callback.register(n,f) end, +} -- we need to cheat a bit here @@ -49,13 +57,14 @@ local remapper = { ttf = "truetype fonts", ttc = "truetype fonts", dfont = "truetype dictionary", - cid = "other text files", -- will become "cid files" + cid = "cid maps", + fea = "font feature files", } function resolvers.find_file(name,kind) name = string.gsub(name,"\\","\/") - kind = kind and string.lower(kind) - return kpse.find_file(name,(kind and kind ~= "" and (remapper[kind] or kind)) or "tex") + kind = string.lower(kind) + return kpse.find_file(name,(kind and kind ~= "" and (remapper[kind] or kind)) or file.extname(name,"tex")) end function resolvers.findbinfile(name,kind) @@ -64,3 +73,56 @@ function resolvers.findbinfile(name,kind) end return resolvers.find_file(name,(kind and remapper[kind]) or kind) end + +-- Caches ... I will make a real stupid version some day when I'm in the +-- mood. After all, the generic code does not need the more advanced +-- ConTeXt features. Cached data is not shared between ConTeXt and other +-- usage as I don't want any dependency at all. Also, ConTeXt might have +-- different needs and tricks added. + +caches = { } + +--~ containers.usecache = true + +function caches.setpath(category,subcategory) +-- local root = kpse.var_value("TEXMFCACHE") or "" +-- if root == "" then +-- root = kpse.var_value("VARTEXMF") or "" +-- end + local var = kpse.var_value("TEXMFVAR") + local root = var and (var .. "/luatex/generic/luaotfload/") or "" + if root ~= "" then + root = file.join(root,category) + lfs.mkdir(root) + root = file.join(root,subcategory) + lfs.mkdir(root) + return lfs.isdir(root) and root + end +end + +local function makefullname(path,name) + if path and path ~= "" then + name = "temp-" and name -- clash prevention + return file.addsuffix(file.join(path,name),"lua") + end +end + +function caches.iswritable(path,name) + local fullname = makefullname(path,name) + return fullname and file.iswritable(fullname) +end + +function caches.loaddata(path,name) + local fullname = makefullname(path,name) + if fullname then + local data = loadfile(fullname) + return data and data() + end +end + +function caches.savedata(path,name,data) + local fullname = makefullname(path,name) + if fullname then + table.tofile(fullname,data,'return',false,true,false) + end +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-luat-ovr.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-luat-ovr.lua new file mode 100644 index 00000000000..af5e05b364f --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-luat-ovr.lua @@ -0,0 +1,35 @@ +if not modules then modules = { } end modules ['luat-ovr'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Khaled Hosny and Elie Roux", + copyright = "Luaotfload Development Team", + license = "GPL" +} + + +local write_nl, format, name = texio.write_nl, string.format, "luaotfload" +local dummyfunction = function() end + +callbacks = { + register = dummyfunction, +} + +function logs.report(category,fmt,...) + if fmt then + write_nl('log', format("%s | %s: %s",name,category,format(fmt,...))) + elseif category then + write_nl('log', format("%s | %s",name,category)) + else + write_nl('log', format("%s |",name)) + end +end + +function logs.simple(fmt,...) + if fmt then + write_nl('log', format("%s | %s",name,format(fmt,...))) + else + write_nl('log', format("%s |",name)) + end +end + +tex.ctxcatcodes = luatexbase.catcodetables.latex diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-fnt.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-fnt.lua index 72bb140ca3e..f2d8e1d7707 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-fnt.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-fnt.lua @@ -51,7 +51,7 @@ function nodes.process_characters(head) local usedfonts, attrfonts, done = { }, { }, false local a, u, prevfont, prevattr = 0, 0, nil, 0 for n in traverse_id(glyph,head) do - local font, attr = n.font, has_attribute(n,0) -- zero attribute is reserved for fonts, preset to 0 is faster (first match) + local font, attr = n.font, has_attribute(n,0) -- zero attribute is reserved for fonts in context if attr and attr > 0 then if font ~= prevfont or attr ~= prevattr then local used = attrfonts[font] @@ -107,7 +107,7 @@ function nodes.process_characters(head) head, done = h or head, done or d if n > 1 then for i=2,n do - local h, d = processors[i](head,font,0) -- false) + local h, d = processors[i](head,font,false) head, done = h or head, done or d end end @@ -119,7 +119,7 @@ function nodes.process_characters(head) head, done = h or head, done or d if n > 1 then for i=2,n do - local h, d = processors[i](head,font,0) -- false) + local h, d = processors[i](head,font,false) head, done = h or head, done or d end end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-ini.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-ini.lua index 741e53d6bfd..36e240238e3 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-ini.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-ini.lua @@ -108,10 +108,12 @@ local penalty = node.id('penalty') local kern = node.id('kern') local whatsit = node.id('whatsit') -local traverse_id = node.traverse_id -local traverse = node.traverse -local free_node = node.free -local remove_node = node.remove +local traverse_id = node.traverse_id +local traverse = node.traverse +local free_node = node.free +local remove_node = node.remove +local insert_node_before = node.insert_before +local insert_node_after = node.insert_after function nodes.remove(head, current, free_too) local t = current @@ -131,8 +133,8 @@ function nodes.delete(head,current) return nodes.remove(head,current,true) end -nodes.before = node.insert_before -nodes.after = node.insert_after +nodes.before = insert_node_before +nodes.after = insert_node_after -- we need to test this, as it might be fixed now @@ -172,21 +174,31 @@ function nodes.after(h,c,n) return n, n end -function nodes.replace(head,current,new) - if current and next then - local p, n = current.prev, current.next - new.prev, new.next = p, n - if p then - p.next = new - else +-- local h, c = nodes.replace(head,current,new) +-- local c = nodes.replace(false,current,new) +-- local c = nodes.replace(current,new) + +function nodes.replace(head,current,new) -- no head returned if false + if not new then + head, current, new = false, head, current + end + local prev, next = current.prev, current.next + if next then + new.next, next.prev = next, new + end + if prev then + new.prev, prev.next = prev, new + end + if head then + if head == current then head = new end - if n then - n.prev = new - end free_node(current) + return head, new + else + free_node(current) + return new end - return head, current end -- will move @@ -212,7 +224,7 @@ end nodes.count = count --- new +-- new, will move function attributes.ofnode(n) local a = n.attr diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-inj.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-inj.lua index 4c5840978c1..9c4612a2299 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-inj.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-inj.lua @@ -70,7 +70,8 @@ function nodes.set_pair(current,factor,rlmode,r2lflag,spec,tfmchr) local bound = has_attribute(current,kernpair) if bound then local kb = kerns[bound] - kb[2], kb[3], kb[4], kb[5] = kb[2] + x, kb[3] + y, kb[4] + w, kb[5] + h + -- inefficient but singles have less, but weird anyway, needs checking + kb[2], kb[3], kb[4], kb[5] = (kb[2] or 0) + x, (kb[3] or 0) + y, (kb[4] or 0)+ w, (kb[5] or 0) + h else bound = #kerns + 1 set_attribute(current,kernpair,bound) diff --git a/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-res.lua b/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-res.lua index 49a1297eb9e..2657dfa1e71 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-res.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/otfl-node-res.lua @@ -7,7 +7,10 @@ if not modules then modules = { } end modules ['node-res'] = { } local gmatch, format = string.gmatch, string.format -local copy_node, free_node, free_list, new_node = node.copy, node.free, node.flush_list, node.new +local copy_node, free_node, free_list, new_node, node_type, node_id = node.copy, node.free, node.flush_list, node.new, node.type, node.id +local tonumber, round = tonumber, math.round + +local glyph_node = node_id("glyph") --[[ldx--

The next function is not that much needed but in we use @@ -16,18 +19,30 @@ for debugging node management.

nodes = nodes or { } +nodes.whatsits = { } -- table.swapped(node.whatsits()) + local reserved = { } +local whatsits = nodes.whatsits + +for k, v in pairs(node.whatsits()) do + whatsits[k], whatsits[v] = v, k -- two way +end -function nodes.register(n) +local function register_node(n) reserved[#reserved+1] = n return n end +nodes.register = register_node + function nodes.cleanup_reserved(nofboxes) -- todo nodes.tracers.steppers.reset() -- todo: make a registration subsystem local nr, nl = #reserved, 0 for i=1,nr do - free_node(reserved[i]) + local ri = reserved[i] + -- if not (ri.id == glue_spec and not ri.is_writable) then + free_node(reserved[i]) + -- end end if nofboxes then local tb = tex.box @@ -51,19 +66,37 @@ function nodes.usage() return t end -local disc = nodes.register(new_node("disc")) -local kern = nodes.register(new_node("kern",1)) -local penalty = nodes.register(new_node("penalty")) -local glue = nodes.register(new_node("glue")) -local glue_spec = nodes.register(new_node("glue_spec")) -local glyph = nodes.register(new_node("glyph",0)) -local textdir = nodes.register(new_node("whatsit",7)) -local rule = nodes.register(new_node("rule")) -local latelua = nodes.register(new_node("whatsit",35)) -local user_n = nodes.register(new_node("whatsit",44)) user_n.type = 100 -local user_l = nodes.register(new_node("whatsit",44)) user_l.type = 110 -local user_s = nodes.register(new_node("whatsit",44)) user_s.type = 115 -local user_t = nodes.register(new_node("whatsit",44)) user_t.type = 116 +local disc = register_node(new_node("disc")) +local kern = register_node(new_node("kern",1)) +local penalty = register_node(new_node("penalty")) +local glue = register_node(new_node("glue")) -- glue.spec = nil +local glue_spec = register_node(new_node("glue_spec")) +local glyph = register_node(new_node("glyph",0)) +local textdir = register_node(new_node("whatsit",whatsits.dir)) -- 7 (6 is local par node) +local rule = register_node(new_node("rule")) +local latelua = register_node(new_node("whatsit",whatsits.late_lua)) -- 35 +local user_n = register_node(new_node("whatsit",whatsits.user_defined)) user_n.type = 100 -- 44 +local user_l = register_node(new_node("whatsit",whatsits.user_defined)) user_l.type = 110 -- 44 +local user_s = register_node(new_node("whatsit",whatsits.user_defined)) user_s.type = 115 -- 44 +local user_t = register_node(new_node("whatsit",whatsits.user_defined)) user_t.type = 116 -- 44 +local left_margin_kern = register_node(new_node("margin_kern",0)) +local right_margin_kern = register_node(new_node("margin_kern",1)) +local lineskip = register_node(new_node("glue",1)) +local baselineskip = register_node(new_node("glue",2)) +local leftskip = register_node(new_node("glue",8)) +local rightskip = register_node(new_node("glue",9)) +local temp = register_node(new_node("temp",0)) + +function nodes.zeroglue(n) + local s = n.spec + return not writable or ( + s.width == 0 + and s.stretch == 0 + and s.shrink == 0 + and s.stretch_order == 0 + and s.shrink_order == 0 + ) +end function nodes.glyph(fnt,chr) local n = copy_node(glyph) @@ -71,48 +104,156 @@ function nodes.glyph(fnt,chr) if chr then n.char = chr end return n end + function nodes.penalty(p) local n = copy_node(penalty) n.penalty = p return n end + function nodes.kern(k) local n = copy_node(kern) n.kern = k return n end -function nodes.glue(width,stretch,shrink) - local n, s = copy_node(glue), copy_node(glue_spec) - s.width, s.stretch, s.shrink = width, stretch, shrink - n.spec = s - return n -end + function nodes.glue_spec(width,stretch,shrink) local s = copy_node(glue_spec) s.width, s.stretch, s.shrink = width, stretch, shrink return s end + +local function someskip(skip,width,stretch,shrink) + local n = copy_node(skip) + if not width then + -- no spec + elseif tonumber(width) then + local s = copy_node(glue_spec) + s.width, s.stretch, s.shrink = width, stretch, shrink + n.spec = s + else + -- shared + n.spec = copy_node(width) + end + return n +end + +function nodes.glue(width,stretch,shrink) + return someskip(glue,width,stretch,shrink) +end +function nodes.leftskip(width,stretch,shrink) + return someskip(leftskip,width,stretch,shrink) +end +function nodes.rightskip(width,stretch,shrink) + return someskip(rightskip,width,stretch,shrink) +end +function nodes.lineskip(width,stretch,shrink) + return someskip(lineskip,width,stretch,shrink) +end +function nodes.baselineskip(width,stretch,shrink) + return someskip(baselineskip,width,stretch,shrink) +end + function nodes.disc() return copy_node(disc) end + function nodes.textdir(dir) local t = copy_node(textdir) t.dir = dir return t end -function nodes.rule(w,h,d) + +function nodes.rule(width,height,depth,dir) local n = copy_node(rule) - if w then n.width = w end - if h then n.height = h end - if d then n.depth = d end + if width then n.width = width end + if height then n.height = height end + if depth then n.depth = depth end + if dir then n.dir = dir end return n end + function nodes.latelua(code) local n = copy_node(latelua) n.data = code return n end +function nodes.leftmarginkern(glyph,width) + local n = copy_node(left_margin_kern) + if not glyph then + logs.fatal("nodes","invalid pointer to left margin glyph node") + elseif glyph.id ~= glyph_node then + logs.fatal("nodes","invalid node type %s for left margin glyph node",node_type(glyph)) + else + n.glyph = glyph + end + if width then + n.width = width + end + return n +end + +function nodes.rightmarginkern(glyph,width) + local n = copy_node(right_margin_kern) + if not glyph then + logs.fatal("nodes","invalid pointer to right margin glyph node") + elseif glyph.id ~= glyph_node then + logs.fatal("nodes","invalid node type %s for right margin glyph node",node_type(p)) + else + n.glyph = glyph + end + if width then + n.width = width + end + return n +end + +function nodes.temp() + return copy_node(temp) +end +--[[ +

At some point we ran into a problem that the glue specification +of the zeropoint dimension was overwritten when adapting a glue spec +node. This is a side effect of glue specs being shared. After a +couple of hours tracing and debugging Taco and I came to the +conclusion that it made no sense to complicate the spec allocator +and settled on a writable flag. This all is a side effect of the +fact that some glues use reserved memory slots (with the zeropoint +glue being a noticeable one). So, next we wrap this into a function +and hide it for the user. And yes, LuaTeX now gives a warning as +well.

+]]-- + +if tex.luatexversion > 51 then + + function nodes.writable_spec(n) + local spec = n.spec + if not spec then + spec = copy_node(glue_spec) + n.spec = spec + elseif not spec.writable then + spec = copy_node(spec) + n.spec = spec + end + return spec + end + +else + + function nodes.writable_spec(n) + local spec = n.spec + if not spec then + spec = copy_node(glue_spec) + else + spec = copy_node(spec) + end + n.spec = spec + return spec + end + +end + local cache = { } function nodes.usernumber(num) -- cgit v1.2.3