summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore21
-rw-r--r--COPYING350
-rw-r--r--Makefile244
-rw-r--r--NEWS29
-rw-r--r--README1
-rw-r--r--doc/Makefile58
-rw-r--r--doc/filegraph.dot (renamed from filegraph.dot)12
-rw-r--r--doc/luaotfload-context.tex485
-rw-r--r--doc/luaotfload-latex.tex448
-rw-r--r--doc/luaotfload-main.tex1591
-rw-r--r--doc/luaotfload-tool.rst (renamed from luaotfload-tool.rst)105
-rw-r--r--doc/luaotfload.conf.example30
-rw-r--r--doc/luaotfload.conf.rst347
-rw-r--r--luaotfload-blacklist.cnf12
-rw-r--r--luaotfload-legacy-attributes.lua27
-rw-r--r--luaotfload-legacy-database.lua724
-rw-r--r--luaotfload-legacy-merged.lua8157
-rwxr-xr-xluaotfload-legacy-tool.lua105
-rw-r--r--luaotfload-legacy.lua402
-rw-r--r--luaotfload.dtx2685
-rw-r--r--misc/luaotfload-blacklist.cnf4
-rw-r--r--misc/valgrind-kpse-suppression.sup140
-rwxr-xr-xscripts/mkcharacters (renamed from mkcharacters)6
-rwxr-xr-xscripts/mkglyphlist (renamed from mkglyphlist)13
-rwxr-xr-xscripts/mkstatus (renamed from mkstatus)84
-rwxr-xr-xscripts/mktests (renamed from mktests)88
-rw-r--r--src/luaotfload-auxiliary.lua (renamed from luaotfload-auxiliary.lua)231
-rw-r--r--src/luaotfload-basics-gen.lua (renamed from luaotfload-basics-gen.lua)25
-rw-r--r--src/luaotfload-basics-nod.lua (renamed from luaotfload-basics-nod.lua)15
-rw-r--r--src/luaotfload-colors.lua (renamed from luaotfload-colors.lua)39
-rw-r--r--src/luaotfload-configuration.lua704
-rw-r--r--src/luaotfload-database.lua (renamed from luaotfload-database.lua)1412
-rw-r--r--src/luaotfload-diagnostics.lua (renamed from luaotfload-diagnostics.lua)49
-rw-r--r--src/luaotfload-features.lua (renamed from luaotfload-features.lua)389
-rw-r--r--src/luaotfload-fontloader.lua (renamed from luaotfload-fontloader.lua)1522
-rw-r--r--src/luaotfload-fonts-cbk.lua (renamed from luaotfload-fonts-cbk.lua)0
-rw-r--r--src/luaotfload-fonts-def.lua (renamed from luaotfload-fonts-def.lua)0
-rw-r--r--src/luaotfload-fonts-enc.lua (renamed from luaotfload-fonts-enc.lua)0
-rw-r--r--src/luaotfload-fonts-ext.lua (renamed from luaotfload-fonts-ext.lua)0
-rw-r--r--src/luaotfload-fonts-inj.lua526
-rw-r--r--src/luaotfload-fonts-lua.lua (renamed from luaotfload-fonts-lua.lua)0
-rw-r--r--src/luaotfload-fonts-otn.lua2848
-rw-r--r--src/luaotfload-fonts-tfm.lua (renamed from luaotfload-fonts-tfm.lua)0
-rw-r--r--src/luaotfload-letterspace.lua (renamed from luaotfload-letterspace.lua)15
-rw-r--r--src/luaotfload-loaders.lua (renamed from luaotfload-loaders.lua)4
-rw-r--r--src/luaotfload-log.lua (renamed from luaotfload-override.lua)65
-rw-r--r--src/luaotfload-main.lua708
-rw-r--r--src/luaotfload-override.lua52
-rw-r--r--src/luaotfload-parsers.lua701
-rwxr-xr-xsrc/luaotfload-tool.lua (renamed from luaotfload-tool.lua)699
-rw-r--r--src/luaotfload.sty45
-rw-r--r--tests/alternate_sub.tex15
-rw-r--r--tests/anum.tex17
-rw-r--r--tests/caseinsensitive.tex6
-rw-r--r--tests/color.tex10
-rw-r--r--tests/fallback.tex6
-rw-r--r--tests/featurefiles.tex5
-rw-r--r--tests/font_patch.tex28
-rw-r--r--tests/fontconfig_conf_reading.tex8
-rw-r--r--tests/fontencoding.tex4
-rw-r--r--tests/fonts.conf.test25
-rw-r--r--tests/fontspec_lookup.ltx41
-rw-r--r--tests/frac.tex4
-rw-r--r--tests/fullname.tex15
-rw-r--r--tests/itlc.tex5
-rw-r--r--tests/lookups.tex20
-rw-r--r--tests/marks.tex10
-rw-r--r--tests/math.tex56
-rw-r--r--tests/microtypography.tex36
-rw-r--r--tests/opbd.fea187
-rw-r--r--tests/opbd.tex35
-rw-r--r--tests/opticalsize.tex13
-rw-r--r--tests/pln-aux-1.tex55
-rw-r--r--tests/pln-aux-2.tex102
-rw-r--r--tests/pln-aux-3.tex39
-rw-r--r--tests/pln-aux-4.tex41
-rw-r--r--tests/pln-request-4-slashed.tex12
-rw-r--r--tests/pln-request-5-cached.tex18
-rw-r--r--tests/pln-subfont-1.tex12
-rw-r--r--tests/pln-tfm.tex10
-rw-r--r--tests/sanitize_color.tex8
-rw-r--r--tests/systemfonts.tex50
-rw-r--r--tests/texligatures.tex7
-rw-r--r--tests/tfmofm.ltx6
-rw-r--r--tests/tkrn.fea8
-rw-r--r--tests/weirdfonts.tex15
-rw-r--r--tests/zero_width_marks_lig.tex16
87 files changed, 11814 insertions, 15348 deletions
diff --git a/.gitignore b/.gitignore
index f231acf..d9cba93 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,9 +18,7 @@ luaotfload-tool.1
luaotfload/*
# Files generated by 'make world' and removed by 'make mrproper'
-luaotfload.lua
luaotfload.pdf
-luaotfload.sty
luaotfload.tds.zip
luaotfload.zip
@@ -41,3 +39,22 @@ tests/*.ovf
tests/*.sty
tests/luaotfload*
+# temporary directories
+build/*
+testing/*
+tests/*
+tmp/*
+
+# tex side-effects
+doc/*.aux
+doc/*.log
+doc/*.out
+doc/*.pdf
+doc/*.tmp
+doc/*.toc
+doc/*.tuc
+doc/*.vimout
+
+# valgrind log
+src/*.log
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d769b3f
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,350 @@
+Context, where the fontloader Luaotfload is built around originates, is
+licensed under the GPL version 2.0 (exactly). As a derived work, anything
+Luaotflaod adds to that is also subject to the same license at the same
+version. The “any later version” clause as used by the FSF in the license text
+*does not apply* to either Context or Luaotfload, despite being kept around in
+the license text given below.
+
+-------------------------------------------------------------------------------
+ LICENSE TEXT BELOW
+-------------------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+ 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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
index 2d618e7..fd3eefb 100644
--- a/Makefile
+++ b/Makefile
@@ -1,89 +1,94 @@
# Makefile for luaotfload
-NAME = luaotfload
-DOC = $(NAME).pdf
-DTX = $(NAME).dtx
-OTFL = $(wildcard luaotfload-*.lua) luaotfload-blacklist.cnf
+NAME = luaotfload
-GLYPHSCRIPT = mkglyphlist
-GLYPHSOURCE = glyphlist.txt
-CHARSCRIPT = mkcharacters
-STATUSSCRIPT = mkstatus
+DOCSRCDIR = ./doc
+SCRIPTSRCDIR = ./scripts
+SRCSRCDIR = ./src
+BUILDDIR = ./build
+MISCDIR = ./misc
+
+SRC = $(wildcard $(SRCSRCDIR)/luaotfload-*.lua)
+SRC += $(SRCSRCDIR)/luaotfload.sty
+SRC += $(MISCDIR)/luaotfload-blacklist.cnf
+
+VGND = $(MISCDIR)/valgrind-kpse-suppression.sup
+
+GLYPHSCRIPT = $(SCRIPTSRCDIR)/mkglyphlist
+CHARSCRIPT = $(SCRIPTSRCDIR)/mkcharacters
+STATUSSCRIPT = $(SCRIPTSRCDIR)/mkstatus
+
+GLYPHSOURCE = $(BUILDDIR)/glyphlist.txt
RESOURCESCRIPTS = $(GLYPHSCRIPT) $(CHARSCRIPT) $(STATUSSCRIPT)
-SCRIPTNAME = luaotfload-tool
-SCRIPT = $(SCRIPTNAME).lua
-MANSOURCE = $(SCRIPTNAME).rst
-MANPAGE = $(SCRIPTNAME).1
-OLDSCRIPT = luaotfload-legacy-tool.lua
+TOOLNAME = luaotfload-tool
+TOOL = $(SRCSRCDIR)/$(TOOLNAME).lua
+
+CONFNAME = luaotfload.conf
-GRAPH = filegraph
-DOTPDF = $(GRAPH).pdf
-DOT = $(GRAPH).dot
+GRAPH = filegraph
+DOCSRC = $(addprefix $(DOCSRCDIR)/$(NAME), -main.tex -latex.tex)
+GRAPHSRC = $(DOCSRCDIR)/$(GRAPH).dot
+MANSRC = $(DOCSRCDIR)/$(TOOLNAME).rst $(DOCSRCDIR)/$(CONFNAME).rst
+
+DOCPDF = $(DOCSRCDIR)/$(NAME).pdf
+DOTPDF = $(DOCSRCDIR)/$(GRAPH).pdf
+TOOLMAN = $(DOCSRCDIR)/$(TOOLNAME).1
+CONFMAN = $(DOCSRCDIR)/$(CONFNAME).5
+MANPAGES = $(TOOLMAN) $(CONFMAN)
+
+DOCS = $(DOCPDF) $(DOTPDF) $(MANPAGES)
# Files grouped by generation mode
-GLYPHS = luaotfload-glyphlist.lua
-CHARS = luaotfload-characters.lua
-STATUS = luaotfload-status.lua
+GLYPHS = $(BUILDDIR)/$(NAME)-glyphlist.lua
+CHARS = $(BUILDDIR)/$(NAME)-characters.lua
+STATUS = $(BUILDDIR)/$(NAME)-status.lua
RESOURCES = $(GLYPHS) $(CHARS) $(STATUS)
-GRAPHED = $(DOTPDF)
-MAN = $(MANPAGE)
-COMPILED = $(DOC)
-UNPACKED = luaotfload.sty luaotfload.lua
-GENERATED = $(GRAPHED) $(UNPACKED) $(COMPILED) $(RESOURCES) $(MAN)
-SOURCE = $(DTX) $(MANSOURCE) $(OTFL) README Makefile NEWS $(RESOURCESCRIPTS)
-
-# test files
-TESTDIR = tests
-TESTSTATUS = $(wildcard $(TESTDIR)/*.tex $(TESTDIR)/*.ltx)
-TESTSTATUS_SYS = $(TESTDIR)/systemfonts.tex $(TESTDIR)/fontconfig_conf_reading.tex
-TESTSTATUS_TL = $(filter-out $(TESTSTATUS_SYS), $(TESTSTATUS))
+SOURCE = $(DOCSRC) $(MANSRC) $(SRC) README COPYING Makefile NEWS $(RESOURCESCRIPTS)
# Files grouped by installation location
-SCRIPTSTATUS = $(SCRIPT) $(OLDSCRIPT) $(RESOURCESCRIPTS)
-RUNSTATUS = $(UNPACKED) $(filter-out $(SCRIPTSTATUS),$(OTFL))
-DOCSTATUS = $(DOC) $(DOTPDF) README NEWS
-MANSTATUS = $(MANPAGE)
-SRCSTATUS = $(DTX) Makefile
+SCRIPTSTATUS = $(TOOL) $(RESOURCESCRIPTS)
+RUNSTATUS = $(filter-out $(SCRIPTSTATUS),$(SRC))
+DOCSTATUS = $(DOCPDF) $(DOTPDF) README NEWS COPYING
+SRCSTATUS = $(DOCSRC) $(MANSRC) $(GRAPHSRC) Makefile
# The following definitions should be equivalent
# ALL_STATUS = $(RUNSTATUS) $(DOCSTATUS) $(SRCSTATUS)
-ALL_STATUS = $(GENERATED) $(SOURCE)
+ALL_STATUS = $(RESOURCES) $(SOURCE)
# Installation locations
-FORMAT = luatex
-SCRIPTDIR = $(TEXMFROOT)/scripts/$(NAME)
-RUNDIR = $(TEXMFROOT)/tex/$(FORMAT)/$(NAME)
-DOCDIR = $(TEXMFROOT)/doc/$(FORMAT)/$(NAME)
-MANDIR = $(TEXMFROOT)/doc/man/man1/
-SRCDIR = $(TEXMFROOT)/source/$(FORMAT)/$(NAME)
-TEXMFROOT = $(shell kpsewhich --var-value TEXMFHOME)
+FORMAT = luatex
+SCRIPTDIR = $(TEXMFROOT)/scripts/$(NAME)
+RUNDIR = $(TEXMFROOT)/tex/$(FORMAT)/$(NAME)
+DOCDIR = $(TEXMFROOT)/doc/$(FORMAT)/$(NAME)
+MAN1DIR = $(TEXMFROOT)/doc/man/man1/
+MAN5DIR = $(TEXMFROOT)/doc/man/man5/
+SRCDIR = $(TEXMFROOT)/source/$(FORMAT)/$(NAME)
+TEXMFROOT = $(shell kpsewhich --var-value TEXMFHOME)
# CTAN-friendly subdirectory for packaging
-DISTDIR = ./luaotfload
+DISTDIR = $(BUILDDIR)/$(NAME)
+
+CTAN_ZIPFILE = $(NAME).zip
+TDS_ZIPFILE = $(NAME).tds.zip
+CTAN_ZIP = $(BUILDDIR)/$(CTAN_ZIPFILE)
+TDS_ZIP = $(BUILDDIR)/$(TDS_ZIPFILE)
+ZIPS = $(CTAN_ZIP) $(TDS_ZIP)
-CTAN_ZIP = $(NAME).zip
-TDS_ZIP = $(NAME).tds.zip
-ZIPS = $(CTAN_ZIP) $(TDS_ZIP)
+LUA = texlua
-LUA = texlua
+## For now the $(BUILDDIR) is hardcoded in the scripts
+## but we might just as well pass it to them by as environment
+## variables.
+DO_GLYPHS = $(LUA) $(GLYPHSCRIPT) > /dev/null
+DO_CHARS = $(LUA) $(CHARSCRIPT) > /dev/null
+DO_STATUS = $(LUA) $(STATUSSCRIPT) > /dev/null
-DO_TEX = luatex --interaction=batchmode $< >/dev/null
-DO_LATEXMK = latexmk -e '$$max_repeat = 5' -pdf -lualatex -silent $< >/dev/null
-# latexmk does only one run on my machine, so we’re not going to rely on it
-DO_LATEX = lualatex -interaction=batchmode $< >/dev/null
-DO_GRAPHVIZ = dot -Tpdf -o $@ $< > /dev/null
-DO_GLYPHS = $(LUA) $(GLYPHSCRIPT) > /dev/null
-DO_CHARS = $(LUA) $(CHARSCRIPT) > /dev/null
-DO_STATUS = $(LUA) $(STATUSSCRIPT) > /dev/null
-DO_DOCUTILS = rst2man $< >$@ 2>/dev/null
+show: showtargets
all: $(GENERATED)
-graph: $(GRAPHED)
-doc: $(GRAPHED) $(COMPILED) $(MAN)
-manual: $(MAN)
-unpack: $(UNPACKED)
+builddir: $(BUILDDIR)
resources: $(RESOURCES)
chars: $(CHARS)
status: $(STATUS)
@@ -91,87 +96,110 @@ ctan: $(CTAN_ZIP)
tds: $(TDS_ZIP)
world: all ctan
-$(GLYPHS): /dev/null
- $(DO_GLYPHS)
+graph: $(DOTPDF)
+doc: $(DOCS)
+pdf: $(DOCPDF)
+manual: $(MANPAGES)
-$(CHARS): /dev/null
- $(DO_CHARS)
+$(DOTPDF):
+ @$(MAKE) -C $(DOCSRCDIR) graph
-$(STATUS): /dev/null
- $(DO_STATUS)
+$(DOCPDF):
+ @$(MAKE) -C $(DOCSRCDIR) doc
-$(GRAPHED): $(DOT)
- $(DO_GRAPHVIZ)
+$(MANPAGES):
+ @$(MAKE) -C $(DOCSRCDIR) manuals
-$(COMPILED): $(DTX)
- $(DO_LATEX)
- $(DO_LATEX)
+$(GLYPHS): builddir
+ $(DO_GLYPHS)
-$(UNPACKED): $(DTX)
- $(DO_TEX)
+$(CHARS): builddir
+ $(DO_CHARS)
+
+$(STATUS): builddir
+ $(DO_STATUS)
-$(MAN): $(MANSOURCE)
- $(DO_DOCUTILS)
+$(BUILDDIR): /dev/null
+ mkdir -p $(BUILDDIR)
define make-ctandir
@$(RM) -rf $(DISTDIR)
-@mkdir -p $(DISTDIR) && cp $(SOURCE) $(COMPILED) $(DISTDIR)
+@mkdir -p $(DISTDIR) && cp $(VGND) $(SOURCE) $(COMPILED) $(DISTDIR)
endef
-$(CTAN_ZIP): $(SOURCE) $(COMPILED) $(TDS_ZIP)
+$(CTAN_ZIP): $(DOCS) $(SOURCE) $(COMPILED) $(TDS_ZIP)
@echo "Making $@ for CTAN upload."
@$(RM) -- $@
$(make-ctandir)
- @zip -r -9 $@ $(TDS_ZIP) $(DISTDIR) >/dev/null
+ cd $(BUILDDIR) && zip -r -9 $(CTAN_ZIPFILE) $(TDS_ZIPFILE) $(NAME) >/dev/null
+
+define run-install-doc
+@mkdir -p $(DOCDIR) && cp -- $(DOCSTATUS) $(VGND) $(DOCDIR)
+@mkdir -p $(SRCDIR) && cp -- $(SRCSTATUS) $(SRCDIR)
+@mkdir -p $(MAN1DIR) && cp -- $(TOOLMAN) $(MAN1DIR)
+@mkdir -p $(MAN5DIR) && cp -- $(CONFMAN) $(MAN5DIR)
+endef
define run-install
-@mkdir -p $(SCRIPTDIR) && cp $(SCRIPTSTATUS) $(SCRIPTDIR)
-@mkdir -p $(RUNDIR) && cp $(RUNSTATUS) $(RUNDIR)
-@mkdir -p $(DOCDIR) && cp $(DOCSTATUS) $(DOCDIR)
-@mkdir -p $(SRCDIR) && cp $(SRCSTATUS) $(SRCDIR)
-@mkdir -p $(MANDIR) && cp $(MANSTATUS) $(MANDIR)
+@mkdir -p $(SCRIPTDIR) && cp -- $(SCRIPTSTATUS) $(SCRIPTDIR)
+@mkdir -p $(RUNDIR) && cp -- $(RESOURCES) $(RUNSTATUS) $(RUNDIR)
endef
$(TDS_ZIP): TEXMFROOT=./tmp-texmf
-$(TDS_ZIP): $(ALL_STATUS)
+$(TDS_ZIP): $(DOCS) $(ALL_STATUS)
@echo "Making TDS-ready archive $@."
@$(RM) -- $@
+ $(run-install-doc)
$(run-install)
@cd $(TEXMFROOT) && zip -9 ../$@ -r . >/dev/null
@$(RM) -r -- $(TEXMFROOT)
-.PHONY: install manifest clean mrproper
+.PHONY: install manifest clean mrproper show showtargets
install: $(ALL_STATUS)
@echo "Installing in '$(TEXMFROOT)'."
+ $(run-install-docs)
$(run-install)
-check: $(RUNSTATUS) $(TESTSTATUS_TL)
- @rm -rf var
- @for f in $(TESTSTATUS_TL); do \
- echo "check: luatex $$f"; \
- luatex --interaction=batchmode $$f \
- > /dev/null || exit $$?; \
- done
-
-check-all: $(TESTSTATUS_SYS) check
- @cd $(TESTDIR); for f in $(TESTSTATUS_SYS); do \
- echo "check: luatex $$f"; \
- $(TESTENV) luatex --interaction=batchmode ../$$f \
- > /dev/null || exit $$?; \
- done
-
-manifest:
+manifest:
@echo "Source files:"
@for f in $(SOURCE); do echo $$f; done
@echo ""
@echo "Derived files:"
@for f in $(GENERATED); do echo $$f; done
-clean:
- @$(RM) -- *.log *.aux *.toc *.idx *.ind *.ilg *.out $(TESTDIR)/*.log
+CLEANEXTS = log aux toc idx ind ilg out
+CLEANME = $(foreach ext,$(CLEANEXTS),$(wildcard *.$(ext)))
+CLEANME += $(foreach ext,$(CLEANEXTS),$(wildcard $(BUILDDIR)/*$(ext)))
-mrproper: clean
- @$(RM) -- $(GENERATED) $(ZIPS) $(GLYPHSOURCE) $(TESTDIR)/*.pdf
- @$(RM) -r -- $(DISTDIR)
+clean:
+ $(MAKE) -C $(DOCSRCDIR) $@
+ @$(RM) -- $(CLEANME)
+mrproper: clean
+ $(MAKE) -C $(DOCSRCDIR) $@
+ @$(RM) -- $(GENERATED) $(ZIPS) $(GLYPHSOURCE)
+ @$(RM) -r -- $(BUILDDIR)
+
+###############################################################################
+showtargets:
+ @echo "Available targets:"
+ @echo
+ @echo " all build everything: documentation, resources,"
+ @echo " world build everything and package zipballs"
+ @echo " doc compile PDF documentation"
+ @echo " resources generate resource files (chars, glyphs)"
+ @echo
+ @echo " pdf build luaotfload.pdf"
+ @echo " manual crate manpages for luaotfload-tool(1) and"
+ @echo " luaotfload.conf(5) (requires Docutils)"
+ @echo " graph generate file graph (requires GraphViz)"
+ @echo
+ @echo " chars import char-def.lua as luaotfload-characters.lua"
+ @echo " status create repository info (luaotfload-status.lua)"
+ @echo
+ @echo " tds package a zipball according to the TDS"
+ @echo " ctan package a zipball for uploading to CTAN"
+ @echo
+
+# vim:noexpandtab:tabstop=8:shiftwidth=2
diff --git a/NEWS b/NEWS
index a87417f..7fd4268 100644
--- a/NEWS
+++ b/NEWS
@@ -1,14 +1,27 @@
Change History
--------------
-2014/05/18, luaotfload v2.4-4
- * Fix incorrect handling of font file formats (backport of commit
- 828a69ef... to the 2.5 branch).
-
-2014/02/05, luaotfload v2.4-3
- * Add contact info to --version output of luaotfload-tool (backport from
- 2.5).
- * Fix bug with broken font names (backport from 2.5).
+2014/**/**, luaotfload v2.5
+ * Remove legacy code.
+ * Remove compatibility with the old mkluatexfontdb script.
+ * Remove test directory. Use https://bitbucket.org/phg/lua-la-tex-tests
+ instead.
+ * Remove luaotfload.lua from luaotfload.dtx; it is now a separate file
+ luaotfload-main.lua.
+ * Standard source tree structure: the code is now located in the ./doc,
+ ./scripts, ./src, ./build, and ./misc directories.
+ * Move the heavier LPEG parsers from luaotfload-features (syntax) and
+ luaotfload-database (fontconfig) into the new file
+ luaotfload-parsers.lua.
+ * Move logging routines from luaotfload-override in to luaotfload-log.
+ * Scan local font files (``--local`` flag to luaotfload-tool, flag
+ ``scan_local`` during TeX run).
+ * Add bisection mode (``--bisect``) to luaotfload-tool.
+ * Add functions for accessing the database: ``aux.font_index()`` and
+ ``aux.read_font_index()``.
+ * Distinguish XDG configuration paths (Reuben Thomas)
+ * Optional configuration via rc files.
+ * Configure default features via configuration file.
2013/12/31, luaotfload v2.4
* Additional self-tests, now in separate file (luaotfload-diagnostics.lua)
diff --git a/README b/README
index 903551f..75575d2 100644
--- a/README
+++ b/README
@@ -32,6 +32,7 @@ Elie Roux <elie.roux@telecom-bretagne.eu>
Will Robertson <will.robertson@latex-project.org>
Philipp Gesang <philipp.gesang@alumni.uni-heidelberg.de>
Dohyun Kim <nomosnomos@gmail.com>
+Reuben Thomas <https://github.com/rrthomas>
Installation
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..ed340a4
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,58 @@
+NAME = luaotfload
+DOCPDF = $(NAME).pdf
+DOCSRC = $(NAME)-latex.tex
+
+SCRIPTNAME = luaotfload-tool
+TOOLMANSRC = $(SCRIPTNAME).rst
+TOOLMAN = $(SCRIPTNAME).1
+
+CONFNAME = luaotfload.conf
+CONFMANSRC = $(CONFNAME).rst
+CONFMAN = $(CONFNAME).5
+
+MANPAGES = $(TOOLMAN) $(CONFMAN)
+
+GRAPH = filegraph
+DOTPDF = $(GRAPH).pdf
+DOT = $(GRAPH).dot
+
+DOCS = $(DOTPDF) $(DOCPDF) $(MANPAGES)
+
+DO_LATEXMK = @latexmk -e '$$max_repeat = 5' -pdf -lualatex -silent $< >/dev/null
+# latexmk does only one run on my machine, so we’re not going to rely on it
+DO_LATEX = @lualatex -interaction=batchmode $< >/dev/null
+DO_GRAPHVIZ = @dot -Tpdf -o $@ $< > /dev/null
+DO_DOCUTILS = @rst2man $< >$@ 2>/dev/null
+
+doc: graph $(DOCPDF)
+all: manuals doc
+graph: $(DOTPDF)
+manuals: $(TOOLMAN) $(CONFMAN)
+
+$(DOCPDF): $(DOCSRC)
+ @echo "creating PDF documentation ($@)"
+ $(DO_LATEX)
+ $(DO_LATEX)
+ mv -f -- $(<:tex=pdf) $@
+
+$(TOOLMAN): $(TOOLMANSRC)
+ @echo "creating man page ($(TOOLMAN))"
+ $(DO_DOCUTILS)
+
+$(CONFMAN): $(CONFMANSRC)
+ @echo "creating man page ($(CONFMAN))"
+ $(DO_DOCUTILS)
+
+$(DOTPDF): $(DOT)
+ @echo "creating file graph ($(DOTPDF))"
+ $(DO_GRAPHVIZ)
+
+.PHONY: clean mrproper
+
+clean:
+ @$(RM) -- *.log *.aux *.toc *.idx *.ind *.ilg *.out
+
+mrproper: clean
+ @$(RM) -- $(DOCS)
+
+# vim:noexpandtab:tabstop=8:shiftwidth=2
diff --git a/filegraph.dot b/doc/filegraph.dot
index 9eccd80..47db9ea 100644
--- a/filegraph.dot
+++ b/doc/filegraph.dot
@@ -122,7 +122,7 @@ strict digraph luaotfload_files { //looks weird with circo ...
style = "filled,rounded",
penwidth=2]
- luaotfload [label = "luaotfload.lua",
+ luaotfload [label = "luaotfload-main.lua",
shape = rect,
width = "3.2cm",
height = "1.2cm",
@@ -200,8 +200,9 @@ strict digraph luaotfload_files { //looks weird with circo ...
<th> <td colspan="2"> <font point-size="12" face="Iwona Italic">Luaotfload Libraries</font> </td> </th>
<tr> <td>luaotfload-auxiliary.lua</td> <td>luaotfload-features.lua</td> </tr>
<tr> <td>luaotfload-override.lua</td> <td>luaotfload-loaders.lua</td> </tr>
- <tr> <td>luaotfload-database.lua</td> <td>luaotfload-color.lua</td> </tr>
- <tr> <td>luaotfload-letterspace.lua</td> </tr>
+ <tr> <td>luaotfload-log.lua</td> <td>luaotfload-letterspace.lua</td> </tr>
+ <tr> <td>luaotfload-parsers.lua</td> <td>luaotfload-database.lua</td> </tr>
+ <tr> <td>luaotfload-color.lua</td> </tr>
</table>
>,
]
@@ -256,6 +257,7 @@ strict digraph luaotfload_files { //looks weird with circo ...
<tr> <td>luatex-font-tfm.lua</td> <td>luatex-font-afm.lua</td> </tr>
<tr> <td>luatex-font-afk.lua</td> <td>luatex-fonts-tfm.lua</td> </tr>
<tr> <td>luatex-fonts-chr.lua</td> <td>luatex-fonts-lua.lua</td> </tr>
+ <tr> <td>luatex-fonts-inj.lua</td> <td>luatex-fonts-otn.lua</td> </tr>
<tr> <td>luatex-fonts-def.lua</td> <td>luatex-fonts-ext.lua</td> </tr>
<tr> <td>luatex-fonts-cbk.lua</td> </tr>
@@ -275,8 +277,8 @@ strict digraph luaotfload_files { //looks weird with circo ...
<th> <td colspan="3"> <font point-size="12" face="Iwona Italic"> Font and Node Libraries from Context </font> </td> </th>
<tr> <td>data-con.lua</td> <td>font-ini.lua</td> <td>font-con.lua</td> </tr>
<tr> <td>font-cid.lua</td> <td>font-map.lua</td> <td>font-oti.lua</td> </tr>
- <tr> <td>font-otf.lua</td> <td>font-otb.lua</td> <td>node-inj.lua</td> </tr>
- <tr> <td>font-ota.lua</td> <td>font-otn.lua</td> <td>font-def.lua</td> </tr>
+ <tr> <td>font-otf.lua</td> <td>font-otb.lua</td> <td>font-ota.lua</td> </tr>
+ <tr> <td>font-def.lua</td> </tr>
</table>
>,
]
diff --git a/doc/luaotfload-context.tex b/doc/luaotfload-context.tex
new file mode 100644
index 0000000..6c8d4b2
--- /dev/null
+++ b/doc/luaotfload-context.tex
@@ -0,0 +1,485 @@
+% macros=mkvi
+%% Copyright (C) 2009-2014
+%%
+%% by Elie Roux <elie.roux@telecom-bretagne.eu>
+%% and Khaled Hosny <khaledhosny@eglug.org>
+%% and Philipp Gesang <philipp.gesang@alumni.uni-heidelberg.de>
+%%
+%% This file is part of Luaotfload.
+%%
+%% Home: https://github.com/lualatex/luaotfload
+%% Support: <lualatex-dev@tug.org>.
+%%
+%% Luaotfload is under the GPL v2.0 (exactly) license.
+%%
+%% ----------------------------------------------------------------------------
+%%
+%% Luaotfload is free software; you can redistribute it and/or
+%% modify it under the terms of the GNU General Public License
+%% as published by the Free Software Foundation; version 2
+%% of the License.
+%%
+%% Luaotfload is distributed in the hope that it will be useful,
+%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%% GNU General Public License for more details.
+%%
+%% You should have received a copy of the GNU General Public License
+%% along with Luaotfload; if not, see <http://www.gnu.org/licenses/>.
+%%
+%% ----------------------------------------------------------------------------
+%%
+
+\unprotect
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% layout and paper
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\setuppapersize [A5] [A5] %% 148×210
+
+\definelayout [mainlayout] [
+ backspace=15mm, %% 133
+ textwidth=103mm,
+ topspace=15mm,
+]
+
+\setuplayout [mainlayout]
+
+\setuppagenumbering [location=,alternative=doublesided]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% colors
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\usecolors [x11]
+\definecolor [primarycolor] [dodgerblue4]
+\definecolor [secondarycolor] [goldenrod4]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% interaction
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\setupinteraction [
+ state=start,
+ page=no,
+ click=yes,
+ style=italic,
+ color=primarycolor,
+ contrastcolor=secondarycolor,
+ title={The Luaotfload package},
+ subtitle={OpenType layout system for Plain TeX and LaTeX},
+ author={Elie Roux & Khaled Hosny & Philipp Gesang},
+ keywords={luatex, lualatex, unicode, opentype},
+]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% fonts
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\usemodule [simplefonts]
+
+\definefontfeature [default] [default] [mode=base,liga=yes,dlig=yes,tlig=yes,onum=yes]
+\definefontfeature [monospace] [liga=no,tlig=no,onum=no]
+
+\definefontfamily [mainface] [serif] [Linux Libertine O] [features=default]
+%definefontfamily [mainface] [serif] [Liberation Serif] [feature=default]
+%definefontfamily [mainface] [sans] [Iwona] [feature=default]
+\definefontfamily [mainface] [sans] [Iwona Medium] [
+ feature=default,
+ it=file:IwonaMedium-Italic.otf,
+ tf=file:IwonaMedium-Regular.otf,
+ bf=file:Iwona-Bold.otf,
+ bi=file:Iwona-BoldItalic.otf,
+]
+%definefontfamily [mainface] [sans] [DejaVu Sans] [feature=default]
+\definefontfamily [mainface] [mono] [Liberation Mono] [scale=0.85,features=monospace]
+
+\setupbodyfont [mainface,10pt]
+
+\def \LUA {Lua}
+\def \LUALATEX {Lua\LATEX}
+\def \OpenType {\identifier{Open\kern-.25ex Type}}
+
+\definealternativestyle [emphasis:texmacro] [\ss \it \letterbackslash] [\ss \it \letterbackslash]
+\definealternativestyle [emphasis:identifier] [\ss] [\ss]
+\definealternativestyle [emphasis:normal] [\sl] [\sl]
+\definealternativestyle [emphasis:abbrev] [{\feature [+][smallcaps]}] [{\feature [+][smallcaps]}]
+\definealternativestyle [emphasis:Largefont] [{\switchtobodyfont[14pt]}] [{\switchtobodyfont[14pt]}]
+\definealternativestyle [emphasis:smallcaps] [{\feature [+][smallcaps]}] [{\feature [+][smallcaps]}]
+%definealternativestyle [emphasis:nonproportional] [\mono] [\mono]
+\definealternativestyle [emphasis:nonproportional] [\tt] [\tt]
+\definealternativestyle [head:section] [{\roman\feature[+][smallcaps]}] [{\roman\feature[+][smallcaps]}]
+\definealternativestyle [head:subsection] [{\roman\feature[+][smallcaps]}] [{\roman\feature[+][smallcaps]}]
+\definealternativestyle [head:subsubsection] [{\roman\feature[+][smallcaps]}] [{\roman\feature[+][smallcaps]}]
+\definealternativestyle [typing:luafunction] [\italic] [\italic]
+\definealternativestyle [typing:fileent] [\tt] [\tt]
+
+\definehighlight [texmacro] [style=emphasis:texmacro] %% cs
+\definehighlight [identifier] [style=emphasis:identifier] %% names
+\definehighlight [abbrev] [style=emphasis:abbrev] %% acronyms
+\definehighlight [emphasis] [style=emphasis:normal] %% level 1 emph
+
+\definehighlight [Largefont] [style=emphasis:Largefont] %% font size
+\definehighlight [smallcaps] [style=emphasis:smallcaps] %% font feature
+\definehighlight [nonproportional] [style=emphasis:nonproportional] %% font switch
+
+\definetype [fileent] [style=typing:fileent]
+\definetype [luafunction] [style=typing:luafunction]
+\setuptyping [style=ttx]
+
+\definebodyfontenvironment [8pt]
+\definebodyfontenvironment [10pt]
+\definebodyfontenvironment [12pt]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% headings
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\setuphead [section] [style=head:section, alternative=inmargin]
+\setuphead [subsection] [style=head:subsection, alternative=inmargin]
+\setuphead [subsubsection] [style=head:subsubsection,alternative=inmargin]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% running headers
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\setupheadertexts
+ [{\tfx \getmarking[section]}] [pagenumber]
+ [pagenumber] [{\tfx \fileent{Luaotfload} Manual}]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% structurals
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% section
+\def \beginsection {\dosingleempty \section_begin_indeed}
+
+\def \section_begin_indeed [#ref]#title{%
+ \iffirstargument
+ \startsection [reference=#ref,title=#title]%
+ \else
+ \startsection [title=#title]%
+ \fi
+}
+
+\let \endsection \stopsection
+
+%% subsection
+\def \beginsubsection {\dosingleempty \section_begin_indeed}
+
+\def \subsection_begin_indeed [#ref]#title{%
+ \iffirstargument
+ \startsubsection [reference=#ref,title=#title]%
+ \else
+ \startsubsection [title=#title]%
+ \fi
+}
+
+\let \endsubsection \stopsection
+
+%% subsubsection
+\def \beginsubsubsection {\dosingleempty \section_begin_indeed}
+
+\def \subsubsection_begin_indeed [#ref]#title{%
+ \iffirstargument
+ \startsubsubsection [reference=#ref,title=#title]%
+ \else
+ \startsubsubsection [title=#title]%
+ \fi
+}
+
+\let \endsubsubsection \stopsection
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% inline verbatim
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Context offers both \type{…} and \type<<…>>, but not an unbalanced
+%% one that we could map directly onto Latex’s \verb|…|.
+
+\definetype [inlinecode_indeed] [style=emphasis:nonproportional]
+
+%% The listings macros don’t seem to handle backslashes and braces
+%% well. We emulate this behavior by handling the escaping in Lua.
+
+\startluacode
+ local lpeg = require "lpeg"
+ local Cs, P, S = lpeg.Cs, lpeg.P, lpeg.S
+ local lpegmatch = lpeg.match
+ local unescape_char = S[[\letterbackslash\letterleftbrace\letterrightbrace]]
+ local backslash = P[[\letterbackslash]]
+ local unescape = Cs (((backslash / "" * unescape_char) + 1)^0)
+ commands.unescape_things = function (str)
+ context.type (lpegmatch (unescape, str))
+ end
+\stopluacode
+
+\unexpanded \def \inlinecode #content{%
+ \ctxcommand {unescape_things \!!bs \detokenize {#content}\!!es}%
+}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% codelistings
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Now *that’s* what I call easy.
+
+\unexpanded \def \beginlisting {%
+ \grabbufferdatadirect{listing}{beginlisting}{endlisting}%
+}
+
+\unexpanded \def \endlisting {\typebuffer [listing]}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% enumerations and lists
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\definedescription [descriptionitem] [
+ align=right,
+ alternative=hanging,
+ width=2em,
+]
+
+\def \begindescriptions {%
+ \begingroup
+ \def \beginnormalitem ##1\endnormalitem{%
+ \startitem##1\stopitem
+ }
+ \let \endnormalitem \relax
+ \let \beginaltitem \startdescriptionitem
+ \let \endaltitem \stopdescriptionitem
+}
+
+\let \enddescriptions \endgroup
+
+
+\definedescription [definitionitem] [
+ align=right,
+ alternative=hanging,
+]
+
+\def \begindefinitions {%
+ \begingroup
+ \def \beginnormalitem ##1\endnormalitem{%
+ \startitem##1\stopitem
+ }
+ \let \endnormalitem \relax
+ \let \beginaltitem \startdefinitionitem
+ \let \endaltitem \stopdefinitionitem
+}
+
+\let \enddefinitions \endgroup
+
+
+\definedescription [filelistitem] [
+ align=normal,
+ alternative=hanging,
+ headstyle=typing:fileent,
+ width=4cm,
+]
+
+\def \beginfilelist {%
+ \begingroup
+ \def \beginnormalitem ##1\endnormalitem{%
+ \startitem##1\stopitem
+ }
+ \let \endnormalitem \relax
+ \let \beginaltitem \startfilelistitem
+ \let \endaltitem \stopfilelistitem
+}
+
+\let \endfilelist \endgroup
+
+\definedescription [functionlistitem] [
+ align=normal,
+ alternative=hanging,
+ headstyle=typing:luafunction,
+ width=4cm,
+]
+
+\def \beginfunctionlist {%
+ \begingroup
+ \def \beginnormalitem ##1\endnormalitem{%
+ \startitem##1\stopitem
+ }
+ \let \endnormalitem \relax
+ \let \beginaltitem \startfunctionlistitem
+ \let \endaltitem \stopfunctionlistitem
+}
+
+\let \endfunctionlist \endgroup
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% columns
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\def \begindoublecolumns {\startcolumns [2]}
+\let \enddoublecolumns \stopcolumns
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% alignment
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\setupnarrower [before={\blank[line]},after={\blank[line]}]
+\let \beginnarrower \startnarrower
+\let \endnarrower \stopnarrower
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% special elements
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\definefont [lmromantenregular] [file:lmroman10-regular.otf*default]
+
+\def \meta #1{%
+ {\lmromantenregular<}%
+ {\italic #1}%
+ {\lmromantenregular>}%
+}
+
+\def \beginabstractcontent {%
+ \grabbufferdatadirect{abstractcontent}{beginabstractcontent}{endabstractcontent}%
+}
+
+\let \endabstractcontent \relax
+
+\def \setdocumenttitle #1{\setvalue {document_title}{#1}}
+\def \setdocumentdate #1{\setvalue {document_date}{#1}}
+\def \setdocumentauthor #1{\setvalue {document_author}{#1}}
+
+\let \typesetdocumenttitle \relax
+\let \beginfrontmatter \relax
+
+\def \endfrontmatter {
+ \startstandardmakeup
+ \vfill
+ \strut \hfill
+ \startframed [frame=off,align=middle,width=.5\textwidth]
+ \Largefont{\getvalue {document_title}}
+ \stopframed
+ \hfill \strut \par
+
+ \blank [2*big]
+
+ \strut \hfill
+ \startframed [frame=off,align=middle,width=.65\textwidth]
+ \setuplocalinterlinespace [18pt]
+ \getvalue {document_author}
+ \stopframed
+ \hfill \strut \par
+
+ \vfill
+ \strut \hfill \getvalue {document_date} \hfill \strut
+ \blank [2*big]
+
+ \strut \hfill
+ \startframed [width=.7\textwidth,align=normal,style=tfx,frame=off]%
+ \getbuffer [abstractcontent]
+ \stopframed
+ \hfill \strut
+ \stopstandardmakeup
+}
+
+\let \typesetcontent \completecontent
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% floats
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% XXX we can improve on this part later
+
+\usemodule [vim]
+\definevimtyping [bnf] [syntax=bnf]
+\definefloat [syntax] [figure]
+
+\def \beginsyntaxfloat #reference#caption{%
+ \begingroup
+ \edef \currentreference {#reference}%
+ \edef \currentcaption {#caption}%
+ \grabbufferdatadirect{rawsyntaxdata}{beginsyntaxfloat}{endsyntaxfloat}%
+}
+
+\def \endsyntaxfloat {%
+ \savebuffer [rawsyntaxdata] [rawsyntaxdata]
+ \startplacesyntax [
+ reference=\currentreference,
+ title={\currentcaption},
+ ]
+ %% there’s no \typebnfbuffer in t-vim :(
+ \typebnffile {\jobname-rawsyntaxdata.tmp}
+ \stopplacesyntax
+ \endgroup%
+}
+
+\def \figurefloat #reference#caption#file{%
+ \startplacefigure [
+ reference=#reference,
+ title={#caption},
+ ]
+ \externalfigure [#file] [width=\textwidth]
+ \stopplacefigure
+}
+
+
+\def \tablefloat #reference#caption#content{%
+ \startplacetable [
+ reference=#reference,
+ title={#caption},
+ ]
+ #content
+ \stopplacetable
+}
+
+
+%% tables
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% tables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\setupxtable [frame=off,option=stretch,textwidth=\dimexpr(\textwidth/2)]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% hyperlinks and references
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\unexpanded \def \hyperlink{%
+ \dosingleempty \hyperlink_indeed%
+}
+
+\def \hyperlink_indeed [#text]#url{%
+ \iffirstargument
+ \useURL [temporary_url] [#url] [] [#text]%
+ \else
+ \useURL [temporary_url] [#url]%
+ \fi%
+ \from [temporary_url]%
+}
+
+
+\def \email #1{\goto{#1}[url(mailto:#1)]}
+
+\def \label #tag{\reference [#tag]\empty}
+\def \pageref #tag{\at{page}{#tag}}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% escaped characters
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\let \charpercent \letterpercent
+\let \charbackslash \letterbackslash
+\let \chartilde \lettertilde
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% main
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\protect
+
+\newif \ifcontextmkiv \contextmkivtrue
+
+\starttext
+ \input luaotfload-main.tex
+\stoptext
+
diff --git a/doc/luaotfload-latex.tex b/doc/luaotfload-latex.tex
new file mode 100644
index 0000000..34c494d
--- /dev/null
+++ b/doc/luaotfload-latex.tex
@@ -0,0 +1,448 @@
+\luatexsuppresslongerror1%% sigh ...
+%% Copyright (C) 2009-2014
+%%
+%% by Elie Roux <elie.roux@telecom-bretagne.eu>
+%% and Khaled Hosny <khaledhosny@eglug.org>
+%% and Philipp Gesang <philipp.gesang@alumni.uni-heidelberg.de>
+%%
+%% This file is part of Luaotfload.
+%%
+%% Home: https://github.com/lualatex/luaotfload
+%% Support: <lualatex-dev@tug.org>.
+%%
+%% Luaotfload is under the GPL v2.0 (exactly) license.
+%%
+%% ----------------------------------------------------------------------------
+%%
+%% Luaotfload is free software; you can redistribute it and/or
+%% modify it under the terms of the GNU General Public License
+%% as published by the Free Software Foundation; version 2
+%% of the License.
+%%
+%% Luaotfload is distributed in the hope that it will be useful,
+%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%% GNU General Public License for more details.
+%%
+%% You should have received a copy of the GNU General Public License
+%% along with Luaotfload; if not, see <http://www.gnu.org/licenses/>.
+%%
+%% ----------------------------------------------------------------------------
+%%
+
+\documentclass{ltxdoc}
+
+\makeatletter
+
+\usepackage {metalogo,multicol,fancyvrb,xspace}
+\usepackage [x11names] {xcolor}
+
+\def \primarycolor {DodgerBlue4} %%-> rgb 16 78 139 | #104e8b
+\def \secondarycolor {Goldenrod4} %%-> rgb 139 105 200 | #8b6914
+
+\usepackage[
+ bookmarks=true,
+ colorlinks=true,
+ linkcolor=\primarycolor,
+ urlcolor=\secondarycolor,
+ citecolor=\primarycolor,
+ pdftitle={The Luaotfload package},
+ pdfsubject={OpenType layout system for Plain TeX and LaTeX},
+ pdfauthor={Elie Roux & Khaled Hosny & Philipp Gesang},
+ pdfkeywords={luatex, lualatex, unicode, opentype}
+]{hyperref}
+
+\usepackage {fontspec}
+\usepackage {unicode-math}
+
+\setmainfont[
+% Numbers = OldStyle, %% buggy with font cache
+ Ligatures = TeX,
+ BoldFont = {Linux Libertine O Bold},
+ ItalicFont = {Linux Libertine O Italic},
+ SlantedFont = {Linux Libertine O Italic},
+]{Linux Libertine O}
+\setmonofont[Ligatures=TeX,Scale=MatchLowercase]{Liberation Mono}
+%setsansfont[Ligatures=TeX]{Linux Biolinum O}
+\setsansfont[Ligatures=TeX,Scale=MatchLowercase]{Iwona Medium}
+%setmathfont{XITS Math}
+
+\usepackage{hologo}
+
+\newcommand\TEX {\TeX\xspace}
+\newcommand\LUA {Lua\xspace}
+\newcommand\PDFTEX {pdf\TeX\xspace}
+\newcommand\LUATEX {Lua\TeX\xspace}
+\newcommand\XETEX {\XeTeX\xspace}
+\newcommand\LATEX {\LaTeX\xspace}
+\newcommand\LUALATEX {Lua\LaTeX\xspace}
+\newcommand\CONTEXT {Con\TeX t\xspace}
+\newcommand\OpenType {\identifier{Open\kern-.25ex Type}\xspace}
+
+%% \groupedcommand, with some omissions taken from syst-aux.mkiv
+\let \handlegroupnormalbefore \relax
+\let \handlegroupnormalafter \relax
+
+\protected \def \handlegroupnormal #1#2{%
+ \bgroup % 1
+ \def \handlegroupbefore {#1}%
+ \def \handlegroupafter {#2}%
+ \afterassignment \handlegroupnormalbefore
+ \let \next =
+}
+
+\def \handlegroupnormalbefore {%
+ \bgroup % 2
+ \handlegroupbefore
+ \bgroup % 3
+ \aftergroup \handlegroupnormalafter%
+}
+
+\def \handlegroupnormalafter {%
+ \handlegroupafter
+ \egroup % 3
+ \egroup % 2
+}
+
+\let \groupedcommand \handlegroupnormal %% only the two arg version
+
+\def \definehighlight [#1][#2]{%
+ \ifcsname #1\endcsname\else
+ \expandafter\def\csname #1\endcsname{%
+ \leavevmode
+ \groupedcommand {#2}\empty%
+ }
+ \fi%
+}
+
+%% old, simplistic definition: obsolete now that we have
+%% \groupedcommand
+%\def\definehighlight[#1][#2]%
+ %{\ifcsname #1\endcsname\else
+ %\expandafter\def\csname #1\endcsname%
+ %{\bgroup#2\csname #1_indeed\endcsname}
+ %\expandafter\def\csname #1_indeed\endcsname##1%
+ %{##1\egroup}%
+ %\fi}
+
+\def\restoreunderscore{\catcode`\_=12\relax}
+
+\definehighlight [fileent][\ttfamily\restoreunderscore] %% files, dirs
+\definehighlight [texmacro][\sffamily\itshape\textbackslash] %% cs
+\definehighlight [luafunction][\sffamily\itshape\restoreunderscore] %% lua identifiers
+\definehighlight [identifier][\sffamily] %% names
+\definehighlight [abbrev][\rmfamily\scshape] %% acronyms
+\definehighlight [emphasis][\rmfamily\slshape] %% level 1 emph
+
+\definehighlight [Largefont][\Large] %% font size
+\definehighlight [smallcaps][\sc] %% font feature
+\definehighlight [nonproportional][\tt] %% font switch
+
+\newcommand*\email[1]{\href{mailto:#1}{#1}}
+
+\renewcommand\partname{Part}%% gets rid of the stupid “file” heading
+
+\usepackage{syntax}%% bnf for font request syntax
+
+\usepackage{titlesec}
+
+\def\movecountertomargin#1{\llap{\rmfamily\upshape#1\hskip2em}}
+\def\zeropoint{0pt}
+\titleformat \part
+ {\normalsize\rmfamily\bfseries}
+ {\movecountertomargin\thepart} \zeropoint {}
+\titleformat \section
+ {\normalsize\rmfamily\scshape}
+ {\movecountertomargin\thesection} \zeropoint {}
+\titleformat \subsection
+ {\small\rmfamily\itshape}
+ {\movecountertomargin\thesubsection} \zeropoint {}
+\titleformat \subsubsection
+ {\normalsize\rmfamily\upshape}
+ {\movecountertomargin\thesubsubsection} \zeropoint {}
+
+\usepackage{tocloft}
+\renewcommand \cftpartfont {\rmfamily\upshape}
+\renewcommand \cftsecfont {\rmfamily\upshape}
+\renewcommand \cftsubsecfont {\rmfamily\upshape}
+\setlength \cftbeforepartskip {1ex}
+\setlength \cftbeforesecskip {1ex}
+
+\VerbatimFootnotes
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% structurals
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\def \definestructural #1{%
+ \expandafter \let \csname end#1\endcsname \relax
+
+ \expandafter \def \csname begin#1\endcsname {%
+ \@ifnextchar[{\csname begin#1indeed\endcsname}
+ {\csname begin#1indeed\endcsname[]}%
+ }
+
+ \expandafter \def \csname begin#1indeed\endcsname [##1]##2{%
+ \edef \first {##1}%
+ \ifx \first \empty
+ \csname #1\endcsname [##2]{##2}%
+ \else
+ \csname #1\endcsname [\first]{##2}%
+ \fi
+ }
+}
+
+\definestructural {section}
+\definestructural {subsection}
+\definestructural {subsubsection}
+
+\def \fakesection #1{\section*{#1}}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% inline verbatim
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Context offers both \type{…} and \type<<…>>, but not an unbalanced
+%% one that we could map directly onto Latex’s \verb|…|.
+
+\usepackage {listings}
+\lstset {
+ basicstyle=\ttfamily,
+}
+
+%\let \inlinecode \lstinline
+\protected \def \inlinecode {\lstinline}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% codelistings; this sucks hard since we lack access to buffers
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\newcount \othercatcode \othercatcode 12
+\newcount \activecatcode \othercatcode 13
+
+\newluatexcatcodetable \vrbcatcodes
+\setluatexcatcodetable \vrbcatcodes {%
+ \luatexcatcodetable \CatcodeTableIniTeX
+ \catcode 9 \othercatcode %% \tabasciicode
+ \catcode 13 \othercatcode %% \endoflineasciicode
+ \catcode 12 \othercatcode %% \formfeedasciicode
+ \catcode 26 \othercatcode %% \endoffileasciicode
+ \catcode 32 \othercatcode %% \spaceasciicode
+}
+
+\newluatexcatcodetable \literalcatcodes
+\setluatexcatcodetable \literalcatcodes {%
+ \luatexcatcodetable \CatcodeTableString
+ \catcode 32 \activecatcode %% \spaceasciicode
+}
+
+\def \beginlisting {%
+ \begingroup
+ \luatexcatcodetable \vrbcatcodes
+ \beginlistingindeed%
+}
+
+\directlua {
+ local texprint = tex.print
+ local stringsub = string.sub
+ local backslash = string.byte (0x5c)
+ document = document or { }
+ document.printlines = function (buffer)
+ for _, line in next, string.explode (buffer, "\noexpand\n") do
+ if stringsub (line, 1, 1) == " " then
+ line = backslash .. line
+ end
+ texprint (-1, line)
+ texprint (-1, "")
+ end
+ end
+}
+
+\def \beginlistingindeed#1\endlisting{%
+ \endgroup
+ \begingroup
+ \ttfamily
+ \small
+ \begin {quote}
+ \bgroup
+ \addfontfeature {RawFeature=-tlig;-liga}%% So one can’t just turn them all off at once using the ``Ligatures`` key?
+ \luatexcatcodetable \literalcatcodes
+ \obeyspaces
+ \obeylines
+ \directlua{document.printlines ([==[\detokenize {#1}]==])}
+ \egroup
+ \end {quote}
+ \endgroup
+}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% enumerations and lists
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\def \definelist [#1]#2{% name, itemcode
+ \expandafter \def \csname begin#1\endcsname {%
+ \begin {itemize}
+ \let \normalitem = \item
+ \def \altitem ####1{%
+ \def \first {####1}%
+ #2
+ }
+ \let \beginnormalitem \item
+ \let \endnormalitem \relax
+ \let \beginaltitem \altitem
+ \let \endaltitem \relax
+ }
+
+ \expandafter \def \csname end#1\endcsname {%
+ \end {itemize}
+ }
+}
+
+\definelist [descriptions]{\normalitem {\textbf \first}\hfill\break}
+\definelist [definitions]{\normalitem {\fileent {\first}}}
+\definelist [filelist]{\normalitem {\fileent {\first}}\space--\hskip 1em}
+\definelist [functionlist]{\normalitem {\luafunction {\first}}\hfill\break}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% columns
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\def \begindoublecolumns {\begin {multicols} {2}}
+\def \enddoublecolumns {\end {multicols}}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% alignment
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\def \begincentered {\begin {center}}
+\def \endcentered {\end {center}}
+
+\def \beginnarrower {\begin {quote}}
+\def \endnarrower {\end {quote}}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% special elements
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\let \beginfrontmatter \relax
+\let \endfrontmatter \relax
+
+\def \beginabstractcontent {\begin {abstract}}
+\def \endabstractcontent {\end {abstract}}
+
+\let \setdocumenttitle \title
+\let \setdocumentdate \date
+\let \setdocumentauthor \author
+\let \typesetdocumenttitle \maketitle
+
+\AtBeginDocument {%% seriously?
+ \let \typesetcontent \tableofcontents%
+}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% floats
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% syntax definition
+\def \beginsyntaxfloat #1#2{%% #1:label #2:caption
+ \begin {figure} [b]
+ \edef \syntaxlabel {#1}%
+ \def \syntaxcaption {#2}%
+ \setlength\grammarparsep{12pt plus 2pt minus 2pt}%
+ \setlength\grammarindent{5cm}%
+ \begingroup
+ \small
+ \begin {grammar}
+}
+
+\def \endsyntaxfloat {%
+ \end {grammar}
+ \endgroup
+ \caption \syntaxcaption
+ \label \syntaxlabel
+ \end {figure}
+}
+
+%% figures, e.g. the file graph
+\def \figurefloat #1#2#3{%% #1:label #2:caption #3:file
+ \begin {figure} [b]
+ \caption {#2}%
+ \includegraphics[width=\textwidth]{#3}%
+ \label {#1}
+ \end {figure}
+}
+
+%% tables
+\def \tablefloat #1#2{%% #1:label #2:caption
+ \begin {table} [t]
+ \hrule
+ \caption {#2}%
+ \label {#1}
+ \hrule
+ \end {table}
+}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% hyperlinks
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\protected \def \hyperlink{%
+ \@ifnextchar[{\hyperlinkindeed}%
+ {\hyperlinkindeed[]}%
+}
+
+\def \hyperlinkindeed [#1]#2{%
+ \def \first {#1}%
+ \ifx \first \empty
+ \url {#2}%
+ \else
+ \href {#2}{#1}%
+ \fi%
+}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% tables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Our tables aren’t anything special so we stick with “tabular” on the
+%% Latex end.
+%%
+%% This is going to be largely incompatible with Context since format
+%% specifications work quite differently (even between different
+%% Context table variants).
+
+\def \begintabulate [#1]#2\endtabulate{%
+ \begingroup
+ \let \beginrow = \relax %% -> \NC in Context
+ \let \newcell = & %% -> \NC
+ \let \endrow = \cr %% -> \NC \NR
+ \begin {tabular}{#1}%
+ #2
+ \end {tabular}
+ \endgroup
+}
+
+\let \endtabulate \relax
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% escaped characters
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\let \charpercent \textpercent
+\let \charbackslash \textbackslash
+\let \chartilde \textasciitilde
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% main
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\makeatother
+
+\newif \ifcontextmkiv \contextmkivfalse
+
+\begin {document}
+ \input {luaotfload-main.tex}
+\end {document}
+
+
diff --git a/doc/luaotfload-main.tex b/doc/luaotfload-main.tex
new file mode 100644
index 0000000..034b704
--- /dev/null
+++ b/doc/luaotfload-main.tex
@@ -0,0 +1,1591 @@
+%% Copyright (C) 2009-2014
+%%
+%% by Elie Roux <elie.roux@telecom-bretagne.eu>
+%% and Khaled Hosny <khaledhosny@eglug.org>
+%% and Philipp Gesang <philipp.gesang@alumni.uni-heidelberg.de>
+%%
+%% This file is part of Luaotfload.
+%%
+%% Home: https://github.com/lualatex/luaotfload
+%% Support: <lualatex-dev@tug.org>.
+%%
+%% Luaotfload is under the GPL v2.0 (exactly) license.
+%%
+%% ----------------------------------------------------------------------------
+%%
+%% Luaotfload is free software; you can redistribute it and/or
+%% modify it under the terms of the GNU General Public License
+%% as published by the Free Software Foundation; version 2
+%% of the License.
+%%
+%% Luaotfload is distributed in the hope that it will be useful,
+%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%% GNU General Public License for more details.
+%%
+%% You should have received a copy of the GNU General Public License
+%% along with Luaotfload; if not, see <http://www.gnu.org/licenses/>.
+%%
+%% ----------------------------------------------------------------------------
+%%
+
+\beginfrontmatter
+
+ \setdocumenttitle {The \identifier{luaotfload} package}
+ \setdocumentdate {2014/**/** v2.5}
+ \setdocumentauthor {Elie Roux · Khaled Hosny · Philipp Gesang\\
+ Home: \hyperlink {https://github.com/lualatex/luaotfload}\\
+ Support: \email {lualatex-dev@tug.org}}
+
+ \typesetdocumenttitle
+
+ \beginabstractcontent
+ This package is an adaptation of the \CONTEXT font loading system.
+ It allows for loading \OpenType fonts with an extended syntax and adds
+ support for a variety of font features.
+ \endabstractcontent
+
+\endfrontmatter
+
+\typesetcontent
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\beginsection {Introduction}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+Font management and installation has always been painful with \TEX. A
+lot of files are needed for one font (\abbrev{tfm}, \abbrev{pfb},
+\abbrev{map}, \abbrev{fd}, \abbrev{vf}), and due to the 8-Bit encoding
+each font is limited to 256 characters.
+
+But the font world has evolved since the original \TEX, and new
+typographic systems have appeared, most notably the so called
+\emphasis{smart font} technologies like \OpenType fonts (\abbrev{otf}).
+
+These fonts can contain many more characters than \TEX fonts, as well
+as additional functionality like ligatures, old-style numbers, small
+capitals, etc., and support more complex writing systems like Arabic
+and Indic\footnote{%
+ Unfortunately, \identifier{luaotfload} doesn‘t support many Indic
+ scripts right now.
+ Assistance in implementing the prerequisites is greatly
+ appreciated.
+}
+scripts.
+
+\OpenType fonts are widely deployed and available for all modern
+operating systems.
+
+As of 2013 they have become the de facto standard for advanced text
+layout.
+
+However, until recently the only way to use them directly in the \TEX
+world was with the \XETEX engine.
+
+Unlike \XETEX, \LUATEX has no built-in support for \OpenType or
+technologies other than the original \TEX fonts.
+
+Instead, it provides hooks for executing \LUA code during the \TEX run
+that allow implementing extensions for loading fonts and manipulating
+how input text is processed without modifying the underlying engine.
+
+This is where \identifier{luaotfload} comes into play:
+Based on code from \CONTEXT, it extends \LUATEX with functionality necessary
+for handling \OpenType fonts.
+
+Additionally, it provides means for accessing fonts known to the operating
+system conveniently by indexing the metadata.
+
+\endsection
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\beginsection {Thanks}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\identifier{Luaotfload} is part of \LUALATEX, the community-driven
+project to provide a foundation for using the \LATEX format with the
+full capabilites of the \LUATEX engine.
+%
+As such, the distinction between end users, contributors, and project
+maintainers is intentionally kept less strict, lest we unduly
+personalize the common effort.
+
+Nevertheless, the current maintainers would like to express their
+gratitude to Khaled Hosny, Akira Kakuto, Hironori Kitagawa and Dohyun
+Kim.
+%
+Their contributions -- be it patches, advice, or systematic
+testing -- made the switch from version 1.x to 2.2 possible.
+%
+Also, Hans Hagen, the author of the font loader, made porting the
+code to \LATEX a breeze due to the extra effort he invested into
+isolating it from the rest of \CONTEXT, not to mention his assistance
+in the task and willingness to respond to our suggestions.
+
+\endsection
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\beginsection {Loading Fonts}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\identifier{luaotfload} supports an extended font request syntax:
+
+\beginnarrower
+ \nonproportional{\string\font\string\foo\space= \string{}%
+ \meta{prefix}\nonproportional{:}%
+ \meta{font name}\nonproportional{:}%
+ \meta{font features}\nonproportional{\string}}%
+ \meta{\TEX font features}
+\endnarrower
+
+\noindent
+The curly brackets are optional and escape the spaces in the enclosed
+font name.
+%
+Alternatively, double quotes serve the same purpose.
+%
+A selection of individual parts of the syntax are discussed below;
+for a more formal description see figure \ref{font-syntax}.
+
+\beginsyntaxfloat
+ {font-syntax}
+ {Font request syntax.
+ Braces or double quotes around the
+ \emphasis{specification} rule will
+ preserve whitespace in file names.
+ In addition to the font style modifiers
+ (\emphasis{slash-notation}) given above, there
+ are others that are recognized but will be silently
+ ignored: \nonproportional{aat},
+ \nonproportional{icu}, and
+ \nonproportional{gr}.
+ The special terminals are:
+ \smallcaps {feature\textunderscore id} for a valid font
+ feature name and
+ \smallcaps {feature\textunderscore value} for the corresponding
+ value.
+ \smallcaps {tfmname} is the name of a \abbrev{tfm} file.
+ \smallcaps {digit} again refers to bytes 48--57, and
+ \smallcaps {all\textunderscore characters} to all byte values.
+ \smallcaps {csname} and \smallcaps {dimension} are the \TEX concepts.}
+%
+ <definition> ::= `\\font', {\sc csname}, `=', <font request>, [ <size> ] ;
+
+ <size> ::= `at', {\sc dimension} ;
+
+ <font request> ::= `"', <unquoted font request> `"'
+ \alt `{', <unquoted font request> `}'
+ \alt <unquoted font request> ;
+
+ <unquoted font request> ::= <specification>, [`:', <feature list> ]
+ \alt `[', <path lookup> `]', [ [`:'], <feature list> ] ;
+
+ <specification> ::= <prefixed spec>, [ <subfont no> ], \{ <modifier> \}
+ \alt <anon lookup>, \{ <modifier> \} ;
+
+ <prefixed spec> ::= `file:', <file lookup>
+ \alt `name:', <name lookup> ;
+
+ <file lookup> ::= \{ <name character> \} ;
+
+ <name lookup> ::= \{ <name character> \} ;
+
+ <anon lookup> ::= {\sc tfmname} | <name lookup> ;
+
+ <path lookup> ::= \{ {\sc all_characters} - `]' \} ;
+
+ <modifier> ::= `/', (`I' | `B' | `BI' | `IB' | `S=', \{ {\sc digit} \} ) ;
+
+ <subfont no> ::= `(', \{ {\sc digit} \}, `)' ;
+
+ <feature list> ::= <feature expr>, \{ `;', <feature expr> \} ;
+
+ <feature expr> ::= {\sc feature_id}, `=', {\sc feature_value}
+ \alt <feature switch>, {\sc feature_id} ;
+
+ <feature switch> ::= `+' | `-' ;
+
+ <name character> ::= {\sc all_characters} - ( `(' | `/' | `:' ) ;
+\endsyntaxfloat
+
+\beginsubsection{Prefix -- the \identifier{luaotfload}{ }Way}
+
+In \identifier{luaotfload}, the canonical syntax for font requests
+requires a \emphasis{prefix}:
+%
+\beginnarrower
+ \nonproportional{\string\font\string\fontname\space= }%
+ \meta{prefix}%
+ \nonproportional{:}%
+ \meta{fontname}%
+ \dots
+\endnarrower
+%
+where \meta{prefix} is either \inlinecode{file:} or \inlinecode {name:}.\footnote{%
+ The development version also knows two further prefixes,
+ \inlinecode {kpse:} and \inlinecode {my:}.
+ %
+ A \inlinecode {kpse} lookup is restricted to files that can be found by
+ \identifier{kpathsea} and
+ will not attempt to locate system fonts.
+ %
+ This behavior can be of value when an extra degree of encapsulation is
+ needed, for instance when supplying a customized tex distribution.
+
+ The \inlinecode {my} lookup takes this a step further: it lets you define
+ a custom resolver function and hook it into the \luafunction{resolve_font}
+ callback.
+ %
+ This ensures full control over how a file is located.
+ %
+ For a working example see the
+ \hyperlink [test repo]{https://bitbucket.org/phg/lua-la-tex-tests/src/5f6a535d/pln-lookup-callback-1.tex}.
+}
+%
+It determines whether the font loader should interpret the request as
+a \emphasis{file name} or
+ \emphasis{font name}, respectively,
+which again influences how it will attempt to locate the font.
+%
+Examples for font names are
+ “Latin Modern Italic”,
+ “GFS Bodoni Rg”, and
+ “PT Serif Caption”
+-- they are the human readable identifiers
+usually listed in drop-down menus and the like.\footnote{%
+ Font names may appear like a great choice at first because they
+ offer seemingly more intuitive identifiers in comparison to arguably
+ cryptic file names:
+ %
+ “PT Sans Bold” is a lot more descriptive than \fileent{PTS75F.ttf}.
+ On the other hand, font names are quite arbitrary and there is no
+ universal method to determine their meaning.
+ %
+ While \identifier{luaotfload} provides fairly sophisticated heuristic
+ to figure out a matching font style, weight, and optical size, it
+ cannot be relied upon to work satisfactorily for all font files.
+ %
+ For an in-depth analysis of the situation and how broken font names
+ are, please refer to
+ \hyperlink [this post]{http://www.ntg.nl/pipermail/ntg-context/2013/073889.html}
+ by Hans Hagen, the author of the font loader.
+ %
+ If in doubt, use filenames.
+ %
+ \fileent{luaotfload-tool} can perform the matching for you with the
+ option \inlinecode {--find=<name>}, and you can use the file name it returns
+ in your font definition.
+}
+%
+In order for fonts installed both in system locations and in your
+\fileent{texmf} to be accessible by font name, \identifier{luaotfload} must
+first collect the metadata included in the files.
+%
+Please refer to section~\ref{sec:fontdb} below for instructions on how to
+create the database.
+
+File names are whatever your file system allows them to be, except
+that that they may not contain the characters
+ \inlinecode {(},
+ \inlinecode {:}, and
+ \inlinecode {/}.
+%
+As is obvious from the last exception, the \inlinecode {file:} lookup will
+not process paths to the font location -- only those
+files found when generating the database are addressable this way.
+%
+Continue below in the \XETEX section if you need to load your fonts
+by path.
+%
+The file names corresponding to the example font names above are
+ \fileent{lmroman12-italic.otf},
+ \fileent{GFSBodoni.otf}, and
+ \fileent{PTZ56F.ttf}.
+
+\endsubsection
+
+\beginsubsection {Compatibility Layer}
+
+In addition to the regular prefixed requests, \identifier{luaotfload}
+accepts loading fonts the \XETEX way.
+%
+There are again two modes: bracketed and unbracketed.
+A bracketed request looks as follows.
+
+\beginnarrower
+ \nonproportional{\string\font\string\fontname\space = [}%
+ \meta{/path/to/file}%
+ \nonproportional{]}
+\endnarrower
+
+\noindent
+Inside the square brackets, every character except for a closing
+bracket is permitted, allowing for specifying paths to a font file.
+%
+Naturally, path-less file names are equally valid and processed the
+same way as an ordinary \inlinecode {file:} lookup.
+
+\beginnarrower
+ \nonproportional{\string\font\string\fontname\space= }%
+ \meta{font name}
+ \dots
+\endnarrower
+
+Unbracketed (or, for lack of a better word: \emphasis{anonymous})
+font requests resemble the conventional \TEX syntax.
+%
+However, they have a broader spectrum of possible interpretations:
+before anything else, \identifier{luaotfload} attempts to load a
+traditional \TEX Font Metric (\abbrev{tfm} or \abbrev{ofm}).
+%
+If this fails, it performs a \inlinecode {name:} lookup, which itself will
+fall back to a \inlinecode {file:} lookup if no database entry matches
+\meta{font name}.
+
+Furthermore, \identifier{luaotfload} supports the slashed (shorthand)
+font style notation from \XETEX.
+
+\beginnarrower
+ \nonproportional{\string\font\string\fontname\space= }%
+ \meta{font name}%
+ \nonproportional{/}%
+ \meta{modifier}
+ \dots
+\endnarrower
+
+\noindent
+Currently, four style modifiers are supported:
+ \inlinecode {I} for italic shape,
+ \inlinecode {B} for bold weight,
+ \inlinecode {BI} or \inlinecode {IB} for the combination of both.
+%
+Other “slashed” modifiers are too specific to the \XETEX engine and
+have no meaning in \LUATEX.
+
+\endsubsection
+
+\beginsubsection{Examples}
+
+\beginsubsubsection{Loading by File Name}
+
+For example, conventional \abbrev{type1} font can be loaded with a
+\inlinecode {file:} request like so:
+
+\beginlisting
+ \font \lmromanten = {file:ec-lmr10} at 10pt
+\endlisting
+
+The \OpenType version of Janusz Nowacki’s font \emphasis{Antykwa
+Półtawskiego}\footnote{%
+ \hyperlink {http://jmn.pl/antykwa-poltawskiego/}, also available in
+ in \TEX Live.
+}
+in its condensed variant can be loaded as follows:
+
+\beginlisting
+ \font \apcregular = file:antpoltltcond-regular.otf at 42pt
+\endlisting
+
+The next example shows how to load the \emphasis{Porson} font digitized by
+the Greek Font Society using \XETEX-style syntax and an absolute path from a
+non-standard directory:
+
+\beginlisting
+ \font \gfsporson = "[/tmp/GFSPorson.otf]" at 12pt
+\endlisting
+
+\endsubsubsection
+
+\beginsubsubsection{Loading by Font Name}
+
+The \inlinecode {name:} lookup does not depend on cryptic filenames:
+
+\beginlisting
+ \font \pagellaregular = {name:TeX Gyre Pagella} at 9pt
+\endlisting
+
+A bit more specific but essentially the same lookup would be:
+
+\beginlisting
+ \font \pagellaregular = {name:TeX Gyre Pagella Regular} at 9pt
+\endlisting
+
+\noindent
+Which fits nicely with the whole set:
+
+\beginlisting
+ \font\pagellaregular = {name:TeX Gyre Pagella Regular} at 9pt
+ \font\pagellaitalic = {name:TeX Gyre Pagella Italic} at 9pt
+ \font\pagellabold = {name:TeX Gyre Pagella Bold} at 9pt
+ \font\pagellabolditalic = {name:TeX Gyre Pagella Bolditalic} at 9pt
+
+ {\pagellaregular foo bar baz\endgraf}
+ {\pagellaitalic foo bar baz\endgraf}
+ {\pagellabold foo bar baz\endgraf}
+ {\pagellabolditalic foo bar baz\endgraf}
+
+ ...
+\endlisting
+
+\endsubsubsection
+
+\beginsubsubsection{Modifiers}
+
+If the entire \emphasis{Iwona} family\footnote{%
+ \hyperlink {http://jmn.pl/kurier-i-iwona/},
+ also in \TEX Live.
+}
+is installed in some location accessible by \identifier{luaotfload},
+the regular shape can be loaded as follows:
+
+\beginlisting
+ \font \iwona = Iwona at 20pt
+\endlisting
+
+\noindent
+To load the most common of the other styles, the slash notation can
+be employed as shorthand:
+
+\beginlisting
+ \font \iwonaitalic = Iwona/I at 20pt
+ \font \iwonabold = Iwona/B at 20pt
+ \font \iwonabolditalic = Iwona/BI at 20pt
+\endlisting
+
+\noindent
+which is equivalent to these full names:
+
+\beginlisting
+ \font \iwonaitalic = "Iwona Italic" at 20pt
+ \font \iwonabold = "Iwona Bold" at 20pt
+ \font \iwonabolditalic = "Iwona BoldItalic" at 20pt
+\endlisting
+
+\endsubsubsection
+\endsubsection
+\endsection
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\beginsection {Font features}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\emphasis{Font features} are the second to last component in the
+general scheme for font requests:
+
+\beginnarrower
+ \nonproportional{\string\font\string\foo\space= "}%
+ \meta{prefix}%
+ \nonproportional{:}%
+ \meta{font name}%
+ \nonproportional{:}%
+ \meta{font features}%
+ \meta{\TEX font features}%
+ \nonproportional{"}
+\endnarrower
+
+\noindent
+If style modifiers are present (\XETEX style), they must precede
+\meta{font features}.
+
+The element \meta{font features} is a semicolon-separated list of feature
+tags\footnote{%
+ Cf. \hyperlink {http://www.microsoft.com/typography/otspec/featurelist.htm}.
+}
+and font options.
+%
+Prepending a font feature with a \inlinecode{+} (plus sign) enables it,
+whereas a \inlinecode{-} (minus) disables it. For instance, the request
+
+\beginlisting
+ \font \test = LatinModernRoman:+clig;-kern
+\endlisting
+
+\noindent activates contextual ligatures (\inlinecode{clig}) and
+disables kerning (\inlinecode{kern}).
+%
+Alternatively the options \inlinecode{true} or \inlinecode{false} can
+be passed to the feature in a key/value expression.
+%
+The following request has the same meaning as the last one:
+
+\beginlisting
+ \font \test = LatinModernRoman:clig=true;kern=false
+\endlisting
+
+\noindent
+Furthermore, this second syntax is required should a font feature
+accept other options besides a true/false switch.
+%
+For example, \emphasis{stylistic alternates} (\inlinecode{salt}) are
+variants of given glyphs.
+%
+They can be selected either explicitly by supplying the variant
+index (starting from one), or randomly by setting the value to,
+obviously, \inlinecode{random}.
+
+%% TODO verify that this actually works with a font that supports
+%% the salt/random feature!\fi
+\beginlisting
+ \font \librmsaltfirst = LatinModernRoman:salt=1
+\endlisting
+
+\beginsubsection {Basic font features}
+
+\begindescriptions
+
+ \beginaltitem {mode}
+ \identifier{luaotfload} has two \OpenType processing
+ \emphasis{modes}:
+ \identifier{base} and \identifier{node}.
+
+ \identifier{base} mode works by mapping \OpenType
+ features to traditional \TEX ligature and kerning mechanisms.
+ %
+ Supporting only non-contextual substitutions and kerning
+ pairs, it is the slightly faster, albeit somewhat limited, variant.
+ %
+ \identifier{node} mode works by processing \TeX’s internal
+ node list directly at the \LUA end and supports
+ a wider range of \OpenType features.
+ %
+ The downside is that the intricate operations required for
+ \identifier{node} mode may slow down typesetting especially
+ with complex fonts and it does not work in math mode.
+
+ By default \identifier{luaotfload} is in \identifier{node}
+ mode, and \identifier{base} mode has to be requested where needed,
+ e.~g. for math fonts.
+ \endaltitem
+
+ \beginaltitem {script} \label{script-tag}
+ An \OpenType script tag;\footnote{%
+ See \hyperlink {http://www.microsoft.com/typography/otspec/scripttags.htm}
+ for a list of valid values.
+ %
+ For scripts derived from the Latin alphabet the value
+ \inlinecode{latn} is good choice.
+ }
+ the default value is \inlinecode{dlft}.
+ %
+ Some fonts, including very popular ones by foundries like Adobe,
+ do not assign features to the \inlinecode{dflt} script, in
+ which case the script needs to be set explicitly.
+ \endaltitem
+
+ \beginaltitem {language}
+ An \OpenType language system identifier,\footnote{%
+ Cf. \hyperlink {http://www.microsoft.com/typography/otspec/languagetags.htm}.
+ }
+ defaulting to \inlinecode{dflt}.
+ \endaltitem
+
+ \beginaltitem {featurefile}
+ A comma-separated list of feature files to be applied to the
+ font.
+ %
+ Feature files contain a textual representation of
+ \OpenType tables and extend the features of a font
+ on fly.
+ %
+ After they are applied to a font, features defined in a
+ feature file can be enabled or disabled just like any
+ other font feature.
+ %
+ The syntax is documented in \identifier{Adobe}’s
+ \OpenType Feature File Specification.\footnote{%
+ Cf. \hyperlink {http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}.
+ Feature file support is part of the engine which at the
+ time of this writing (2014) implements the spec only
+ partially.
+ See the
+ \hyperlink [\LUATEX tracker]{http://tracker.luatex.org/view.php?id=231}
+ for details.
+ }
+
+ For a demonstration of how to set a \inlinecode{tkrn} feature consult
+ the file \inlinecode{tkrn.fea} that is part of \identifier{luaotfload}.
+ It can be read and applied as follows:
+
+ \inlinecode{\\font \\test = Latin Modern Roman:featurefile=tkrn.fea;+tkrn}
+ \endaltitem
+
+ \beginaltitem {color}
+ A font color, defined as a triplet of two-digit hexadecimal
+ \abbrev{rgb} values, with an optional fourth value for
+ transparency
+ (where \inlinecode{00} is completely transparent and
+ \inlinecode{FF} is opaque).
+
+ For example, in order to set text in semitransparent red:
+
+ \beginlisting
+\font \test = "Latin Modern Roman:color=FF0000BB"
+ \endlisting
+ \endaltitem
+
+ \beginaltitem {kernfactor \& letterspace}
+ Define a font with letterspacing (tracking) enabled.
+ %
+ In \identifier{luaotfload}, letterspacing is implemented by
+ inserting additional kerning between glyphs.
+
+ This approach is derived from and still quite similar to the
+ \emphasis{character kerning} (\texmacro{setcharacterkerning} /
+ \texmacro{definecharacterkerning} \& al.) functionality of
+ Context, see the file \fileent{typo-krn.lua} there.
+ %
+ The main difference is that \identifier{luaotfload} does not
+ use \LUATEX attributes to assign letterspacing to regions,
+ but defines virtual letterspaced versions of a font.
+
+ The option \identifier{kernfactor} accepts a numeric value that
+ determines the letterspacing factor to be applied to the font
+ size.
+ %
+ E.~g. a kern factor of $0.42$ applied to a $10$ pt font
+ results in $4.2$ pt of additional kerning applied to each
+ pair of glyphs.
+ %
+ Ligatures are split into their component glyphs unless
+ explicitly ignored (see below).
+
+ For compatibility with \XETEX an alternative
+ \identifier{letterspace} option is supplied that interprets the
+ supplied value as a \emphasis{percentage} of the font size but
+ is otherwise identical to \identifier{kernfactor}.
+ %
+ Consequently, both definitions in below snippet yield the same
+ letterspacing width:
+
+ \beginlisting
+\font \iwonakernedA = "file:Iwona-Regular.otf:kernfactor=0.125"
+\font \iwonakernedB = "file:Iwona-Regular.otf:letterspace=12.5"
+ \endlisting
+
+ Specific pairs of letters and ligatures may be exempt from
+ letterspacing by defining the \LUA functions
+ \luafunction{keeptogether} and \luafunction{keepligature},
+ respectively, inside the namespace \inlinecode {luaotfload.letterspace}.
+ %
+ Both functions are called whenever the letterspacing callback
+ encounters an appropriate node or set of nodes.
+ %
+ If they return a true-ish value, no extra kern is inserted at
+ the current position.
+ %
+ \luafunction{keeptogether} receives a pair of consecutive
+ glyph nodes in order of their appearance in the node list.
+ %
+ \luafunction{keepligature} receives a single node which can be
+ analyzed into components.
+ %
+ (For details refer to the \emphasis{glyph nodes} section in the
+ \LUATEX reference manual.)
+ %
+ The implementation of both functions is left entirely to the
+ user.
+ \endaltitem
+
+\ifcontextmkiv
+ \startbuffer [printvectors]
+ \directlua{inspect(fonts.protrusions.setups.default)
+ inspect(fonts.expansions.setups.default)}
+ \stopbuffer
+\fi
+
+ \beginaltitem {protrusion \& expansion}
+ These keys control microtypographic features of the font,
+ namely \emphasis{character protrusion} and \emphasis{font
+ expansion}.
+ %
+ Their arguments are names of \LUA tables that contain
+ values for the respective features.\footnote{%
+ For examples of the table layout please refer to the
+ section of the file \fileent{luaotfload-fonts-ext.lua} where the
+ default values are defined.
+ %
+ Alternatively and with loss of information, you can dump
+ those tables into your terminal by issuing
+ \unless \ifcontextmkiv
+ \beginlisting
+ \directlua{inspect(fonts.protrusions.setups.default)
+ inspect(fonts.expansions.setups.default)}
+ \endlisting
+ \else
+ \typebuffer [printvectors]
+ \fi
+ at some point after loading \fileent{luaotfload.sty}.
+ }
+ %
+ For both, only the set \identifier{default} is predefined.
+
+ For example, to define a font with the default
+ protrusion vector applied\footnote{%
+ You also need to set
+ \inlinecode {pdfprotrudechars=2} and
+ \inlinecode {pdfadjustspacing=2}
+ to activate protrusion and expansion, respectively.
+ See the
+ \hyperlink [\PDFTEX manual]{http://mirrors.ctan.org/systems/pdftex/manual/pdftex-a.pdf}%
+ for details.
+ }:
+
+ \beginlisting
+\font \test = LatinModernRoman:protrusion=default
+ \endlisting
+ \endaltitem
+\enddescriptions
+
+\endsubsection
+
+\beginsubsection {Non-standard font features}
+\identifier{luaotfload} adds a number of features that are not defined
+in the original \OpenType specification, most of them
+aiming at emulating the behavior familiar from other \TEX engines.
+%
+Currently (2014) there are three of them:
+
+\begindescriptions
+
+ \beginaltitem {anum}
+ Substitutes the glyphs in the \abbrev{ascii} number range
+ with their counterparts from eastern Arabic or Persian,
+ depending on the value of \identifier{language}.
+ \endaltitem
+
+ \beginaltitem {tlig}
+ Applies legacy \TEX ligatures\footnote{%
+ These contain the feature set \inlinecode {trep} of earlier
+ versions of \identifier{luaotfload}.
+
+ Note to \XETEX users: this is the equivalent of the
+ assignment \inlinecode {mapping=text-tex} using \XETEX's input
+ remapping feature.
+ }:
+
+ \unless \ifcontextmkiv
+ %% Using braced arg syntax with inline code appears to be
+ %% impossible within Latex tables -- just ignore the weird
+ %% exclamation points below.
+ \begintabulate [rlrl]
+ \beginrow `` \newcell {\inlinecode !``! } \newcell '' \newcell {\inlinecode !''!} \endrow
+ \beginrow ` \newcell {\inlinecode !`! } \newcell ' \newcell {\inlinecode !'! } \endrow
+ \beginrow " \newcell {\inlinecode !"! } \newcell -- \newcell {\inlinecode !--!} \endrow
+ \beginrow --- \newcell {\inlinecode !---!} \newcell !` \newcell {\inlinecode ?!`?} \endrow
+ \beginrow ?` \newcell {\inlinecode !?`! } \newcell \newcell \endrow
+ \endtabulate
+ \else
+ %% XXX find a way to wrap these in the tabulate environment
+ \startframed [frame=off,width=broad,align=middle]
+ \startframed [frame=off,width=\dimexpr(\textwidth/2)]
+ \startxtable [align=middle]
+ \startxrow \startxcell `` \stopxcell \startxcell \inlinecode {``} \stopxcell \startxcell '' \stopxcell \startxcell \inlinecode {''} \stopxcell \stopxrow
+ \startxrow \startxcell ` \stopxcell \startxcell \inlinecode {`} \stopxcell \startxcell ' \stopxcell \startxcell \inlinecode {'} \stopxcell \stopxrow
+ \startxrow \startxcell " \stopxcell \startxcell \inlinecode {"} \stopxcell \startxcell -- \stopxcell \startxcell \inlinecode {--} \stopxcell \stopxrow
+ \startxrow \startxcell --- \stopxcell \startxcell \inlinecode {---} \stopxcell \startxcell !` \stopxcell \startxcell \inlinecode {!`} \stopxcell \stopxrow
+ \startxrow \startxcell ?` \stopxcell \startxcell \inlinecode {?`} \stopxcell \startxcell \stopxcell \startxcell \stopxcell \stopxrow
+ \stopxtable
+ \stopframed
+ \stopframed
+ \fi
+ \endaltitem
+
+ \beginaltitem {itlc}
+ Computes italic correction values (active by default).
+ \endaltitem
+
+\enddescriptions
+
+\endsubsection
+\endsection
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\beginsection {Font names database}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\label{sec:fontdb}
+
+As mentioned above, \identifier{luaotfload} keeps track of which
+fonts are available to \LUATEX by means of a \emphasis{database}.
+%
+This allows referring to fonts not only by explicit filenames but
+also by the proper names contained in the metadata which is often
+more accessible to humans.\footnote{%
+ The tool \hyperlink[\fileent{otfinfo}]{http://www.lcdf.org/type/}
+ (comes with \TEX Live), when invoked on a font file with the
+ \inlinecode {-i} option, lists the variety of name fields defined for
+ it.
+}
+
+When \identifier{luaotfload} is asked to load a font by a font name,
+it will check if the database exists and load it, or else generate a
+fresh one.
+%
+Should it then fail to locate the font, an update to the database is
+performed in case the font has been added to the system only
+recently.
+%
+As soon as the database is updated, the resolver will try
+and look up the font again, all without user intervention.
+%
+The goal is for \identifier{luaotfload} to act in the background and
+behave as unobtrusively as possible, while providing a convenient
+interface to the fonts installed on the system.
+
+Generating the database for the first time may take a while since it
+inspects every font file on your computer.
+%
+This is particularly noticeable if it occurs during a typesetting run.
+In any case, subsequent updates to the database will be quite fast.
+
+\beginsubsection[luaotfload-tool]
+ {\fileent{luaotfload-tool}}
+
+It can still be desirable at times to do some of these steps
+manually, and without having to compile a document.
+%
+To this end, \identifier{luaotfload} comes with the utility
+\fileent{luaotfload-tool} that offers an interface to the database
+functionality.
+%
+Being a \LUA script, there are two ways to run it:
+either make it executable (\inlinecode {chmod +x} on unixoid systems) or
+pass it as an argument to \fileent{texlua}.\footnote{%
+ Tests by the maintainer show only marginal performance gain by
+ running with Luigi Scarso’s
+ \hyperlink [\identifier{Luajit\kern-.25ex\TEX}]{https://foundry.supelec.fr/projects/luajittex/},
+ which is probably due to the fact that most of the time is spent
+ on file system operations.
+
+ \emphasis{Note}:
+ On \abbrev{MS} \identifier{Windows} systems, the script can be run
+ either by calling the wrapper application
+ \fileent{luaotfload-tool.exe} or as
+ \inlinecode {texlua.exe luaotfload-tool.lua}.
+}
+%
+Invoked with the argument \inlinecode {--update} it will perform a database
+update, scanning for fonts not indexed.
+
+\beginlisting
+ luaotfload-tool --update
+\endlisting
+
+Adding the \inlinecode {--force} switch will initiate a complete
+rebuild of the database.
+
+\beginlisting
+ luaotfload-tool --update --force
+\endlisting
+
+\endsubsection
+
+\beginsubsection{Search Paths}
+
+\identifier{luaotfload} scans those directories where fonts are
+expected to be located on a given system.
+%
+On a Linux machine it follows the paths listed in the
+\identifier{Fontconfig} configuration files;
+consult \inlinecode {man 5 fonts.conf} for further information.
+%
+On \identifier{Windows} systems, the standard location is
+\inlinecode {Windows\\Fonts},
+%
+while \identifier{Mac OS~X} requires a multitude of paths to
+be examined.
+%
+The complete list is is given in table \ref{table-searchpaths}.
+Other paths can be specified by setting the environment variable
+\inlinecode {OSFONTDIR}.
+%
+If it is non-empty, then search will be extended to the included
+directories.
+
+\tablefloat {table-searchpaths}
+ {List of paths searched for each supported operating system.}
+ {%
+ \unless \ifcontextmkiv
+ \begincentered
+ \begintabulate [lp{.5\textwidth}]
+ \beginrow
+ Windows \newcell \inlinecode !\% WINDIR\%\\ Fonts!
+ \endrow
+ \beginrow
+ Linux \newcell \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break
+ \fileent{/etc/fonts/fonts.conf}
+ \endrow
+ \beginrow
+ Mac \newcell \fileent{\textasciitilde/Library/Fonts},\break
+ \fileent{/Library/Fonts},\break
+ \fileent{/System/Library/Fonts}, and\hfill\break
+ \fileent{/Network/Library/Fonts}
+ \endrow
+ \endtabulate
+ \endcentered
+ \else
+ \setuplocalinterlinespace [14pt]
+ \starttabulate [|l|p(.5\textwidth)|]
+ \NC Windows \NC \inlinecode {\% WINDIR\%\\ Fonts} \NC \NR
+ \NC Linux \NC \fileent{/usr/local/etc/fonts/fonts.conf} and\crlf
+ \fileent{/etc/fonts/fonts.conf} \NC \NR
+ \NC
+ Mac \NC \fileent{\textasciitilde/Library/Fonts},\crlf
+ \fileent{/Library/Fonts},\break
+ \fileent{/System/Library/Fonts}, and\crlf
+ \fileent{/Network/Library/Fonts} \NC \NR
+ \stoptabulate
+ \fi%
+ }
+
+\endsubsection
+
+\beginsubsection{Querying from Outside}
+
+\fileent{luaotfload-tool} also provides rudimentary means of
+accessing the information collected in the font database.
+%
+If the option \inlinecode {--find=}\emphasis{name} is given, the script will
+try and search the fonts indexed by \identifier{luaotfload} for a
+matching name.
+%
+For instance, the invocation
+
+\beginlisting
+ luaotfload-tool --find="Iwona Regular"
+\endlisting
+
+\noindent
+will verify if “Iwona Regular” is found in the database and can be
+readily requested in a document.
+
+If you are unsure about the actual font name, then add the
+\inlinecode {-F} (or \inlinecode {--fuzzy}) switch to the command line to enable
+approximate matching.
+%
+Suppose you cannot precisely remember if the variant of
+\identifier{Iwona} you are looking for was “Bright” or “Light”.
+The query
+
+\beginlisting
+ luaotfload-tool -F --find="Iwona Bright"
+\endlisting
+
+\noindent
+will tell you that indeed the latter name is correct.
+
+Basic information about fonts in the database can be displayed
+using the \inlinecode {-i} option (\inlinecode {--info}).
+%
+\beginlisting
+ luaotfload-tool -i --find="Iwona Light Italic"
+\endlisting
+%
+\noindent
+The meaning of the printed values is described in section 4.4 of the
+\LUATEX reference manual.\footnote{%
+ In \TEX Live: \fileent{texmf-dist/doc/luatex/base/luatexref-t.pdf}.
+}
+
+For a much more detailed report about a given font try the
+\inlinecode {-I} option instead (\inlinecode {--inspect}).
+\beginlisting
+ luaotfload-tool -I --find="Iwona Light Italic"
+\endlisting
+
+\inlinecode {luaotfload-tool --help} will list the available command line
+switches, including some not discussed in detail here.
+%
+For a full documentation of \identifier{luaotfload-tool} and its
+capabilities refer to the manpage
+(\inlinecode {man 1 luaotfload-tool}).\footnote{%
+ Or see \inlinecode {luaotfload-tool.rst} in the source directory.
+}
+
+\endsubsection
+
+\beginsubsection {Blacklisting Fonts}
+\label{font-blacklist}
+
+Some fonts are problematic in general, or just in \LUATEX.
+%
+If you find that compiling your document takes far too long or eats
+away all your system’s memory, you can track down the culprit by
+running \inlinecode {luaotfload-tool -v} to increase verbosity.
+%
+Take a note of the \emphasis{filename} of the font that database
+creation fails with and append it to the file
+\fileent{luaotfload-blacklist.cnf}.
+
+A blacklist file is a list of font filenames, one per line.
+Specifying the full path to where the file is located is optional, the
+plain filename should suffice.
+%
+File extensions (\fileent{.otf}, \fileent{.ttf}, etc.) may be omitted.
+%
+Anything after a percent (\inlinecode {\%}) character until the end of the line
+is ignored, so use this to add comments.
+%
+Place this file to some location where the \identifier{kpse}
+library can find it, e.~g.
+\fileent{texmf-local/tex/luatex/luaotfload} if you are running
+\identifier{\TEX Live},\footnote{%
+ You may have to run \inlinecode {mktexlsr} if you created a new file in
+ your \fileent{texmf} tree.
+}
+or just leave it in the working directory of your document.
+%
+\identifier{luaotfload} reads all files named
+\fileent{luaotfload-blacklist.cnf} it finds, so the fonts in
+\fileent{./luaotfload-blacklist.cnf} extend the global blacklist.
+
+Furthermore, a filename prepended with a dash character (\inlinecode{-}) is
+removed from the blacklist, causing it to be temporarily whitelisted
+without modifying the global file.
+%
+An example with explicit paths:
+
+\beginlisting
+% example otf-blacklist.cnf
+/Library/Fonts/GillSans.ttc % Luaotfload ignores this font.
+-/Library/Fonts/Optima.ttc % This one is usable again, even if
+ % blacklisted somewhere else.
+\endlisting
+
+\endsubsection
+\endsection
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\beginsection {Files from \CONTEXT and \LUATEX-Fonts}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\identifier{luaotfload} relies on code originally written by Hans
+Hagen for the \hyperlink[\identifier{\CONTEXT}]{http://wiki.contextgarden.net}
+format.
+%
+It integrates the font loader as distributed in
+the \identifier{\LUATEX-Fonts} package.
+%
+The original \LUA source files have been combined using the
+\fileent{mtx-package} script into a single, self-contained blob.
+In this form the font loader has no further dependencies\footnote{%
+ It covers, however, to some extent the functionality of the
+ \identifier{lualibs} package.
+}
+and requires only minor adaptions to integrate into
+\identifier{luaotfload}.
+%
+The guiding principle is to let \CONTEXT/\LUATEX-Fonts take care of
+the implementation, and update the imported code from time to time.
+%
+As maintainers, we aim at importing files from upstream essentially
+\emphasis{unmodified}, except for renaming them to prevent name
+clashes.
+%
+This job has been greatly alleviated since the advent of
+\LUATEX-Fonts, prior to which the individual dependencies had to be
+manually spotted and extracted from the \CONTEXT source code in a
+complicated and error-prone fashion.
+
+Below is a commented list of the files distributed with
+\identifier{luaotfload} in one way or the other.
+%
+See figure \ref{file-graph} on page \pageref{file-graph} for a
+graphical representation of the dependencies.
+%
+From \LUATEX-Fonts, only the file \fileent{luatex-fonts-merged.lua}
+has been imported as \fileent{luaotfload-fontloader.lua}.
+%
+It is generated by \fileent{mtx-package}, a \LUA source code merging
+too developed by Hans Hagen.\footnote{%
+ \fileent{mtx-package} is
+ \hyperlink [part of \CONTEXT]{http://repo.or.cz/w/context.git/blob_plain/refs/heads/origin:/scripts/context/lua/mtx-package.lua}
+ and requires \fileent{mtxrun}.
+ Run
+ \inlinecode {mtxrun --script package --help}
+ to display further information.
+ For the actual merging code see the file
+ \fileent{util-mrg.lua} that is part of \CONTEXT.
+}
+It houses several \LUA files that can be classed in three
+categories.
+
+\begindefinitions
+ \beginnormalitem
+ \emphasis{\LUA utility libraries}, a subset
+ of what is provided by the \identifier{lualibs}
+ package.
+
+ \begindoublecolumns
+ \begindefinitions
+ \beginaltitem {l-lua.lua} \endaltitem
+ \beginaltitem {l-lpeg.lua} \endaltitem
+ \beginaltitem {l-function.lua} \endaltitem
+ \beginaltitem {l-string.lua} \endaltitem
+ \beginaltitem {l-table.lua} \endaltitem
+ \beginaltitem {l-io.lua} \endaltitem
+ \beginaltitem {l-file.lua} \endaltitem
+ \beginaltitem {l-boolean.lua} \endaltitem
+ \beginaltitem {l-math.lua} \endaltitem
+ \beginaltitem {util-str.lua} \endaltitem
+ \enddefinitions
+ \enddoublecolumns
+ \endnormalitem
+
+ \beginnormalitem
+ The \emphasis{font loader} itself.
+ These files have been written for
+ \LUATEX-Fonts and they are distributed along
+ with \identifier{luaotfload}.
+ \begindoublecolumns
+ \begindefinitions
+ \beginaltitem{luatex-basics-gen.lua} \endaltitem
+ \beginaltitem{luatex-basics-nod.lua} \endaltitem
+ \beginaltitem{luatex-fonts-enc.lua} \endaltitem
+ \beginaltitem{luatex-fonts-syn.lua} \endaltitem
+ \beginaltitem{luatex-fonts-tfm.lua} \endaltitem
+ \beginaltitem{luatex-fonts-chr.lua} \endaltitem
+ \beginaltitem{luatex-fonts-lua.lua} \endaltitem
+ \beginaltitem{luatex-fonts-inj.lua} \endaltitem
+ \beginaltitem{luatex-fonts-otn.lua} \endaltitem
+ \beginaltitem{luatex-fonts-def.lua} \endaltitem
+ \beginaltitem{luatex-fonts-ext.lua} \endaltitem
+ \beginaltitem{luatex-fonts-cbk.lua} \endaltitem
+ \enddefinitions
+ \enddoublecolumns
+ \endnormalitem
+
+ \beginnormalitem
+ Code related to \emphasis{font handling and
+ node processing}, taken directly from
+ \CONTEXT.
+ \begindoublecolumns
+ \begindefinitions
+ \beginaltitem{data-con.lua} \endaltitem
+ \beginaltitem{font-ini.lua} \endaltitem
+ \beginaltitem{font-con.lua} \endaltitem
+ \beginaltitem{font-cid.lua} \endaltitem
+ \beginaltitem{font-map.lua} \endaltitem
+ \beginaltitem{font-oti.lua} \endaltitem
+ \beginaltitem{font-otf.lua} \endaltitem
+ \beginaltitem{font-otb.lua} \endaltitem
+ \beginaltitem{font-ota.lua} \endaltitem
+ \beginaltitem{font-def.lua} \endaltitem
+ \beginaltitem{font-otp.lua} \endaltitem
+ \enddefinitions
+ \enddoublecolumns
+ \endnormalitem
+\enddefinitions
+
+Note that if \identifier{luaotfload} cannot locate the
+merged file, it will load the individual \LUA libraries
+instead.
+%
+Their names remain the same as in \CONTEXT (without the
+\inlinecode {otfl}-prefix) since we imported the relevant section of
+\fileent{luatex-fonts.lua} unmodified into \fileent{luaotfload-main.lua}.
+Thus if you prefer running bleeding edge code from the
+\CONTEXT beta, all you have to do is remove
+\fileent{luaotfload-merged.lua} from the search path.
+
+Also, the merged file at some point loads the Adobe Glyph List from a
+\LUA table that is contained in \fileent{luaotfload-glyphlist.lua},
+which is automatically generated by the script
+\fileent{mkglyphlist}.\footnote{%
+ See \fileent{luaotfload-font-enc.lua}.
+ The hard-coded file name is why we have to replace the procedure
+ that loads the file in \fileent{luaotfload-override.lua}.
+}
+There is a make target \identifier{glyphs} that will create a fresh
+glyph list so we don’t need to import it from \CONTEXT any longer.
+
+In addition to these, \identifier{luaotfload} requires a number of
+files not contained in the merge. Some of these have no equivalent in
+\LUATEX-Fonts or \CONTEXT, some were taken unmodified from the latter.
+
+
+\beginfilelist
+ \beginaltitem {luaotfload-features.lua}
+ font feature handling; incorporates some of the code from
+ \fileent{font-otc} from \CONTEXT;
+ \endaltitem
+ \beginaltitem {luaotfload-override.lua}
+ overrides the \CONTEXT logging functionality.
+ \endaltitem
+ \beginaltitem {luaotfload-loaders.lua}
+ registers the \OpenType font reader as handler for Postscript
+ fonts (\abbrev{pfa}, \abbrev{pfb}).
+ \endaltitem
+ \beginaltitem {luaotfload-parsers.lua}
+ various \abbrev{lpeg}-based parsers.
+ \endaltitem
+ \beginaltitem {luaotfload-database.lua}
+ font names database.
+ \endaltitem
+ \beginaltitem {luaotfload-colors.lua}
+ color handling.
+ \endaltitem
+ \beginaltitem {luaotfload-auxiliary.lua}
+ access to internal functionality for package authors (proposals
+ for additions welcome).
+ \endaltitem
+ \beginaltitem {luaotfload-letterspace.lua}
+ font-based letterspacing.
+ \endaltitem
+\endfilelist
+
+\figurefloat
+ {file-graph}
+ {Schematic of the files in \identifier{Luaotfload}}
+ {filegraph.pdf}
+
+\endsection
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\beginsection {Auxiliary Functions}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+With release version 2.2, \identifier{luaotfload} received
+additional functions for package authors to call from outside
+(see the file \fileent{luaotfload-auxiliary.lua} for details).
+%
+The purpose of this addition twofold.
+%
+Firstly, \identifier{luaotfload} failed to provide a stable interface
+to internals in the past which resulted in an unmanageable situation
+of different packages abusing the raw access to font objects by means
+of the \luafunction{patch_font} callback.
+%
+When the structure of the font object changed due to an update, all
+of these imploded and several packages had to be fixed while
+simultaneously providing fallbacks for earlier versions.
+%
+Now the patching is done on the \identifier{luaotfload} side and can
+be adapted with future modifications to font objects without touching
+the packages that depend on it.
+%
+Second, some the capabilities of the font loader and the names
+database are not immediately relevant in \identifier{luaotfload}
+itself but might nevertheless be of great value to package authors or
+end users.
+
+Note that the current interface is not yet set in stone and the
+development team is open to suggestions for improvements or
+additions.
+
+\beginsubsection {Callback Functions}
+
+The \luafunction{patch_font} callback is inserted in the wrapper
+\identifier{luaotfload} provides for the font definition callback
+(see below, page \pageref{define-font}).
+%
+At this place it allows manipulating the font object immediately after
+the font loader is done creating it.
+%
+For a short demonstration of its usefulness, here is a snippet that
+writes an entire font object to the file \fileent{fontdump.lua}:
+
+\beginlisting
+ \input luaotfload.sty
+ \directlua{
+ local dumpfile = "fontdump.lua"
+ local dump_font = function (tfmdata)
+ local data = table.serialize(tfmdata)
+ io.savedata(dumpfile, data)
+ end
+
+ luatexbase.add_to_callback(
+ "luaotfload.patch_font",
+ dump_font,
+ "my_private_callbacks.dump_font"
+ )
+ }
+ \font \dumpme = name:Iwona
+ \bye
+\endlisting
+
+\emphasis{Beware}: this creates a Lua file of around 150,000 lines of
+code, taking up 3~\abbrev{mb} of disk space.
+%
+By inspecting the output you can get a first impression of how a font
+is structured in \LUATEX’s memory, what elements it is composed of,
+and in what ways it can be rearranged.
+
+\beginsubsubsection {Compatibility with Earlier Versions}
+
+As has been touched on in the preface to this section, the structure
+of the object as returned by the fontloader underwent rather drastic
+changes during different stages of its development, and not all
+packages that made use of font patching have kept up with every one
+of it.
+%
+To ensure compatibility with these as well as older versions of
+some packages, \identifier{luaotfload} sets up copies of or references
+to data in the font table where it used to be located.
+%
+For instance, important parameters like the requested point size, the
+units factor, and the font name have again been made accessible from
+the toplevel of the table even though they were migrated to different
+subtables in the meantime.
+
+\endsubsubsection
+
+\beginsubsubsection{Patches}
+
+These are mostly concerned with establishing compatibility with \XETEX.
+
+\beginfunctionlist
+
+ \beginaltitem {set_sscale_dimens}
+ Calculate \texmacro{fontdimen}s 10 and 11 to emulate \XETEX.
+ \endaltitem
+
+ \beginaltitem {set_capheight}
+ Calculates \texmacro{fontdimen} 8 like \XETEX.
+ \endaltitem
+
+ \beginaltitem {patch_cambria_domh}
+ Correct some values of the font \emphasis{Cambria Math}.
+ \endaltitem
+
+\endfunctionlist
+
+\endsubsection
+
+\beginsubsection {Package Author’s Interface}
+
+As \LUATEX release 1.0 is nearing, the demand for a reliable interface
+for package authors increases.
+
+\endsubsubsection
+
+\beginsubsubsection{Font Properties}
+
+Below functions mostly concern querying the different components of a
+font like for instance the glyphs it contains, or what font features
+are defined for which scripts.
+
+\beginfunctionlist
+
+ \beginaltitem {aux.font_has_glyph (id : int, index : int)}
+ Predicate that returns true if the font \luafunction{id}
+ has glyph \luafunction{index}.
+ \endaltitem
+
+ \beginaltitem {aux.slot_of_name(name : string)}
+ Translates an Adobe Glyph name to the corresponding glyph
+ slot.
+ \endaltitem
+
+ \beginaltitem {aux.name_of_slot(slot : int)}
+ The inverse of \luafunction{slot_of_name}; note that this
+ might be incomplete as multiple glyph names may map to the
+ same codepoint, only one of which is returned by
+ \luafunction{name_of_slot}.
+ \endaltitem
+
+ \beginaltitem {aux.provides_script(id : int, script : string)}
+ Test if a font supports \luafunction{script}.
+ \endaltitem
+
+ \beginaltitem {aux.provides_language(id : int, script : string, language : string)}
+ Test if a font defines \luafunction{language} for a given
+ \luafunction{script}.
+ \endaltitem
+
+ \beginaltitem {aux.provides_feature(id : int, script : string,
+ language : string, feature : string)}
+ Test if a font defines \luafunction{feature} for
+ \luafunction{language} for a given \luafunction{script}.
+ \endaltitem
+
+ \beginaltitem {aux.get_math_dimension(id : int, dimension : string)}
+ Get the dimension \luafunction{dimension} of font \luafunction{id}.
+ \endaltitem
+
+ \beginaltitem {aux.sprint_math_dimension(id : int, dimension : string)}
+ Same as \luafunction{get_math_dimension()}, but output the value
+ in scaled points at the \TEX end.
+ \endaltitem
+
+\endfunctionlist
+
+\endsubsubsection
+
+\beginsubsubsection{Database}
+
+%% not implemented, may come back later
+\beginfunctionlist
+% \beginaltitem {aux.scan_external_dir(dir : string)}
+% Include fonts in directory \luafunction{dir} in font lookups without
+% adding them to the database.
+%
+ \beginaltitem {aux.read_font_index (void)}
+ Read the index file from the appropriate location (usually
+ the bytecode file \fileent{luaotfload-names.luc} somewhere
+ in the \fileent{texmf-var} tree) and return the result as a
+ table. The file is processed with each call so it is up to
+ the user to store the result for later access.
+ \endaltitem
+
+ \beginaltitem {aux.font_index (void)}
+ Return a reference of the font names table used internally
+ by \identifier{luaotfload}. The index will be read if it
+ has not been loaded up to this point. Also a font scan that
+ overwrites the current index file might be triggered. Since
+ the return value points to the actual index, any
+ modifications to the table might influence runtime behavior
+ of \identifier{luaotfload}.
+ \endaltitem
+
+\endfunctionlist
+
+\endsubsubsection
+
+\endsubsection
+\endsection
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\beginsection {Troubleshooting}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\beginsubsection {Database Generation}
+
+If you encounter problems with some fonts, please first update to the
+latest version of this package before reporting a bug, as
+\identifier{luaotfload} is under active development and still a moving
+target.
+%
+The development takes place on \identifier{github} at
+\hyperlink {https://github.com/lualatex/luaotfload} where there is an issue
+tracker for submitting bug reports, feature requests and the likes.
+
+Bug reports are more likely to be addressed if they contain the output
+of
+
+\beginlisting
+ luaotfload-tool --diagnose=environment,files,permissions
+\endlisting
+
+\noindent Consult the man page for a description of these options.
+
+Errors during database generation can be traced by increasing the
+verbosity level and redirecting log output to \fileent{stdout}:
+
+\beginlisting
+ luaotfload-tool -fuvvv --log=stdout
+\endlisting
+
+\noindent or to a file in \fileent{/tmp}:
+
+\beginlisting
+ luaotfload-tool -fuvvv --log=file
+\endlisting
+
+\noindent In the latter case, invoke the \inlinecode {tail(1)} utility on the
+file for live monitoring of the progress.
+
+If database generation fails, the font last printed to the terminal or
+log file is likely to be the culprit.
+%
+Please specify it when reporting a bug, and blacklist it for the time
+being (see above, page \pageref{font-blacklist}).
+
+\endsubsection
+
+\beginsubsection {Font Features}
+
+A common problem is the lack of features for some
+\OpenType fonts even when specified.
+%
+This can be related to the fact that some fonts do not provide features
+for the \inlinecode {dflt} script (see above on page \pageref{script-tag}),
+which is the default one in this package.
+%
+If this happens, assigning a noth script when the font is defined should
+fix it.
+%
+For example with \inlinecode {latn}:
+
+\beginlisting
+ \font \test = file:MyFont.otf:script=latn;+liga;
+\endlisting
+
+You can get a list of features that a font defines for scripts and
+languages by querying it in \fileent{luaotfload-tool}:
+
+\beginlisting
+ luaotfload-tool --find="Iwona" --inspect
+\endlisting
+
+\endsubsection
+
+\beginsubsection {\LUATEX Programming}
+
+Another strategy that helps avoiding problems is to not access raw
+\LUATEX internals directly.
+%
+Some of them, even though they are dangerous to access, have not been
+overridden or disabled.
+%
+Thus, whenever possible prefer the functions in the \luafunction{aux}
+namespace over direct manipulation of font objects. For example, raw
+access to the \luafunction{font.fonts} table like:
+
+\beginlisting
+ local somefont = font.fonts[2]
+\endlisting
+
+\noindent can render already defined fonts unusable.
+%
+Instead, the function \luafunction{font.getfont()} should be used
+because it has been replaced by a safe variant.
+
+However, \luafunction{font.getfont()} only covers fonts handled by the
+font loader, e.~g. \identifier{OpenType} and \identifier{TrueType}
+fonts, but not \abbrev{tfm} or \abbrev{ofm}.
+%
+Should you absolutely require access to all fonts known to \LUATEX,
+including the virtual and autogenerated ones, then you need to query
+both \luafunction{font.getfont()} and \luafunction{font.fonts}.
+%
+In this case, best define you own accessor:
+
+\beginlisting
+ local unsafe_getfont = function (id)
+ local tfmdata = font.getfont (id)
+ if not tfmdata then
+ tfmdata = font.fonts[id]
+ end
+ return tfmdata
+ end
+
+ --- use like getfont()
+ local somefont = unsafe_getfont (2)
+\endlisting
+
+\endsubsection
+\endsection
+
+\beginsection {License}
+
+\identifier {luaotfload} is licensed under the terms of the
+\hyperlink [GNU General Public License version 2.0]%
+ {https://www.gnu.org/licenses/old-licenses/gpl-2.0.html}.
+Following the underlying fontloader code \identifier {luaotfload}
+recognizes only that exact version as its license.
+The „any later version” clause of the original license text as
+copyrighted by the \hyperlink [Free Software Foundation]{http://www.fsf.org/}
+\emphasis {does not apply} to either \identifier {luaotfload} or the
+code imported from \CONTEXT.
+
+The complete text of the license is given as a separate file \fileent
+{COPYING} in the toplevel directory of the
+\hyperlink [\fileent {Luaotfload} Git repository]{https://github.com/lualatex/luaotfload/blob/master/COPYING}.
+Distributions probably package it as \fileent
+{doc/luatex/luaotfload/COPYING} in the relevant \fileent {texmf} tree.
+
+\endsection
+
+\endinput
+
diff --git a/luaotfload-tool.rst b/doc/luaotfload-tool.rst
index 08acb6c..4b1a934 100644
--- a/luaotfload-tool.rst
+++ b/doc/luaotfload-tool.rst
@@ -6,21 +6,22 @@
generate and query the Luaotfload font names database
-----------------------------------------------------------------------
-:Date: 2014-05-18
-:Copyright: GPL v2.0
-:Version: 2.4-4
-:Manual section: 1
-:Manual group: text processing
+:Date: 2014-03-30
+:Copyright: GPL v2.0
+:Version: 2.5
+:Manual section: 1
+:Manual group: text processing
SYNOPSIS
=======================================================================
-**luaotfload-tool** [ -bDfFiIlnpquvVhw ]
+**luaotfload-tool** [ -bcDfFiIlLnpqRSuvVhw ]
**luaotfload-tool** --update [ --force ] [ --quiet ] [ --verbose ]
[ --prefer-texmf ] [ --dry-run ]
[ --formats=[+|-]EXTENSIONS ]
[ --no-compress ] [ --no-strip ]
+ [ --local ] [ --max-fonts=N ]
**luaotfload-tool** --find=FONTNAME [ --fuzzy ] [ --info ] [ --inspect ]
[ --no-reload ]
@@ -31,6 +32,8 @@ SYNOPSIS
**luaotfload-tool** --list=CRITERION[:VALUE] [ --fields=F1,F2,...,Fn ]
+**luaotfload-tool** --bisect=DIRECTIVE
+
**luaotfload-tool** --help
**luaotfload-tool** --version
@@ -49,10 +52,6 @@ the *Luaotfload* package. There are two general modes: **update** and
+ **update**: update the database or rebuild it entirely;
+ **query**: resolve a font name or display close matches.
-Note that if the script is named ``mkluatexfontdb`` it will behave like
-earlier versions (<=1.3) and always update the database first. Also,
-the verbosity level will be set to 2.
-
OPTIONS
=======================================================================
@@ -61,12 +60,15 @@ update mode
--update, -u Update the database; indexes new fonts.
--force, -f Force rebuilding of the database; re-indexes
all fonts.
+--local, -L Include font files in ``$PWD``. This option
+ will cause large parts of the database to be
+ rebuilt. Thus it is quite inefficient.
+ Additionally, if local font files are found,
+ the database is prevented from being saved
+ to disk, so the local fonts need to be parsed
+ with every invocation of ``luaotfload-tool``.
--no-reload, -n Suppress auto-updates to the database (e.g.
when ``--find`` is passed an unknown name).
---no-strip Do not strip redundant information after
- building the database. Warning: this will
- inflate the index to about two to three times
- the normal size.
--no-compress, -c Do not filter the plain text version of the
font index through gzip. Useful for debugging
if your editor is built without zlib.
@@ -74,8 +76,6 @@ update mode
--prefer-texmf, -p Organize the file name database in a way so
that it prefer fonts in the *TEXMF* tree over
system fonts if they are installed in both.
---max-fonts=N Process at most *N* font files, including fonts
- already indexed in the count.
--formats=EXTENSIONS Extensions of the font files to index.
Where *EXTENSIONS* is a comma-separated list of
supported file extensions (otf, ttf, ttc,
@@ -95,9 +95,6 @@ update mode
grow the database considerably and slow down
font indexing.
---dry-run, -D Don’t load fonts, scan directories only.
- (For debugging file system related issues.)
-
query mode
-----------------------------------------------------------------------
--find=NAME Resolve a font name; this looks up <name> in
@@ -121,7 +118,6 @@ query mode
library (assumes ``-I``). Automatically enabled
if the verbosity level exceeds 2.
---show-blacklist, -b Show blacklisted files (not directories).
--list=CRITERION Show entries, where *CRITERION* is one of the
following:
@@ -183,6 +179,68 @@ font and lookup caches
cache;
3) ``show`` -> print stats.
+debugging methods
+-----------------------------------------------------------------------
+--show-blacklist, -b Show blacklisted files (not directories).
+--dry-run, -D Don’t load fonts when updating the database;
+ scan directories only.
+ (For debugging file system related issues.)
+--no-strip Do not strip redundant information after
+ building the database. Warning: this will
+ inflate the index to about two to three times
+ the normal size.
+--max-fonts=N Process at most *N* font files, including fonts
+ already indexed in the count.
+--bisect=DIRECTIVE Bisection of the font database.
+ This mode is intended as assistance in
+ debugging the Luatex engine, especially when
+ tracking memleaks or buggy fonts.
+
+ *DIRECTIVE* can be one of the following:
+
+ 1) ``run`` -> Make ``luaotfload-tool`` respect
+ the bisection progress when running.
+ Combined with ``--update`` and possibly
+ ``--force`` this will only process the files
+ from the start up until the pivot and ignore
+ the rest.
+ 2) ``start`` -> Start bisection: create a
+ bisection state file and initialize the low,
+ high, and pivot indices.
+ 3) ``stop`` -> Terminate the current bisection
+ session by deleting the state file.
+ 4) ``good`` | ``bad`` -> Mark the section
+ processed last as “good” or “bad”,
+ respectively. The next bisection step will
+ continue with the bad section.
+ 5) ``status`` -> Print status information about
+ the current bisection session. Hint: Use
+ with higher verbosity settings for more
+ output.
+
+ A bisection session is initiated by issuing the
+ ``start`` directive. This sets the pivot to the
+ middle of the list of available font files.
+ Now run *luaotfload-tool* with the ``--update``
+ flag set as well as ``--bisect=run``: only the
+ fonts up to the pivot will be considered. If
+ that task exhibited the issue you are tracking,
+ then tell Luaotfload using ``--bisect=bad``.
+ The next step of ``--bisect=run`` will continue
+ bisection with the part of the files below the
+ pivot.
+ Likewise, issue ``--bisect=good`` in order to
+ continue with the fonts above the pivot,
+ assuming the tested part of the list did not
+ trigger the bug.
+
+ Once the culprit font is tracked down, ``good``
+ or ``bad`` will have no effect anymore. ``run``
+ will always end up processing the single font
+ file that was left.
+ Use ``--bisect=stop`` to clear the bisection
+ state.
+
miscellaneous
-----------------------------------------------------------------------
--verbose=N, -v Set verbosity level to *n* or the number of
@@ -192,12 +250,13 @@ miscellaneous
troubleshooting), where *CHANNEL* can be
1) ``stdout`` -> all output will be
- dumped to the terminal; or
+ dumped to the terminal (default); or
2) ``file`` -> write to a file to the temporary
directory (the name will be chosen
- automatically (**experimental!**).
+ automatically.
---version, -V Show version info of components and exit.
+--version, -V Show version numbers of components as well as
+ some basic information and exit.
--help, -h Show help message and exit.
--diagnose=CHECK Run the diagnostic procedure *CHECK*. Available
diff --git a/doc/luaotfload.conf.example b/doc/luaotfload.conf.example
new file mode 100644
index 0000000..2756d62
--- /dev/null
+++ b/doc/luaotfload.conf.example
@@ -0,0 +1,30 @@
+;; Example configuration file for Luaotfload. This file contains the
+;; defaults only, see luaotfload.conf(5) for more information.
+
+[db]
+ compress = true
+ formats = otf, ttf, ttc, dfont
+ max-fonts = 2.2517998136852e15
+ scan-local = false
+ skip-read = false
+ strip = true
+ update-live = true
+
+[misc]
+ statistics = false
+ termwidth = nil
+
+[paths]
+ cache-dir = fonts
+ names-dir = names
+ index-file = luaotfload-names.lua
+ lookups-file = luaotfload-lookup-cache.lua
+
+[run]
+ color-callback = pre_linebreak_filter
+ definer = patch
+ log-level = 0
+ resolver = cached
+
+;; vim:ft=dosini:et:sw=4:ts=8
+
diff --git a/doc/luaotfload.conf.rst b/doc/luaotfload.conf.rst
new file mode 100644
index 0000000..774095b
--- /dev/null
+++ b/doc/luaotfload.conf.rst
@@ -0,0 +1,347 @@
+=======================================================================
+ luaotfload.conf
+=======================================================================
+
+-----------------------------------------------------------------------
+ Luaotfload configuration file
+-----------------------------------------------------------------------
+
+:Date: 2014-06-09
+:Copyright: GPL v2.0
+:Version: 2.5
+:Manual section: 5
+:Manual group: text processing
+
+SYNOPSIS
+=======================================================================
+
+- **./luaotfload{.conf,rc}**
+- **XDG_CONFIG_HOME/luaotfload/luaotfload{.conf,rc}**
+- **~/.luaotfloadrc**
+
+DESCRIPTION
+=======================================================================
+
+The file ``luaotfload.conf`` contains configuration options for
+*Luaotfload*, a font loading and font management component for LuaTeX.
+
+
+EXAMPLE
+=======================================================================
+
+A small Luaotfload configuration file with few customizations could
+look as follows: ::
+
+ [db]
+ formats = afm, pfa, pfb
+ compress = false
+
+ [misc]
+ termwidth = 60
+
+ [run]
+ log-level = 6
+
+This will make Luaotfload ignore all font files except for PostScript
+formats. NB: With a default Tex Live install the PS fonts will take
+much longer to index than OpenType or TrueType ones. Also, an
+uncompressed index file will be dumped which is going to be much larger
+due to the huge amount of PostScript fonts indexed. The terminal width
+is truncated to 60 characters which influences the verbose output
+during indexing. Finally, the verbosity is increased greatly: each font
+file being processed will be printed to the stdout on a separate line,
+along with lots of other information.
+
+To observe the difference in behavior, save above snippet to
+``./luaotfload.conf`` and update the font index: ::
+
+ luaotfload --update --force
+
+
+SYNTAX
+=======================================================================
+
+The configuration file syntax follows the common INI format. For a more
+detailed description please refer to the section “CONFIGURATION FILE”
+of **git-config**\(1). A brief list of rules is given below:
+
+ * Blank lines and lines starting with a semicolon (``;``) are ignored.
+
+ * A configuration file is partitioned into sections that are declared
+ by specifying the section title in brackets on a separate line: ::
+
+ [some-section]
+ ... section content ...
+
+ * Sections consist of one or more variable assignments of the form
+ ``variable-name = value`` E. g.::
+
+ [foo]
+ bar = baz
+ quux = xyzzy
+ ...
+
+ * Section and variable names may contain only uppercase and lowercase
+ letters as well as dashes (``-``).
+
+
+VARIABLES
+=======================================================================
+
+Variables in belong into a configuration section and their values must
+be of a certain type. Some of them have further constraints. For
+example, the “color callback” must be a string of one of the values
+``pre_linebreak_filter`` or ``pre_output_filter``, defined in the
+section *run*.
+
+Currently, the configuration is organized into four sections:
+
+db
+ Options relating to the font index.
+
+misc
+ Options without a clearly defined category.
+
+paths
+ Path and file name settings.
+
+run
+ Options controlling runtime behavior of Luaotfload.
+
+The list of valid variables, the sections they are part of and their
+type is given below. Types represent Lua types that the values must be
+convertible to; they are abbreviated as follows: ``s`` for the *string*
+type, ``n`` for *number*, ``b`` for *boolean*. A value of ``nil`` means
+the variable is unset.
+
+
+Section ``db``
+-----------------------------------------------------------------------
+
++---------------+--------+---------------------------+
+| variable | type | default |
++---------------+--------+---------------------------+
+| compress | b | ``true`` |
++---------------+--------+---------------------------+
+| formats | s | ``"otf,ttf,ttc,dfont"`` |
++---------------+--------+---------------------------+
+| max-fonts | n | ``2^51`` |
++---------------+--------+---------------------------+
+| scan-local | b | ``false`` |
++---------------+--------+---------------------------+
+| skip-read | b | ``false`` |
++---------------+--------+---------------------------+
+| strip | b | ``true`` |
++---------------+--------+---------------------------+
+| update-live | b | ``true`` |
++---------------+--------+---------------------------+
+
+The flag ``compress`` determines whether the font index (usually
+``luaotfload-names.lua[.gz]`` will be stored in compressed forms.
+If unset it is equivalent of passing ``--no-compress`` to
+**luaotfload-tool**. Since the file is only created for convenience
+and has no effect on the runtime behavior of Luaotfload, the flag
+should remain set. Most editors come with zlib support anyways.
+
+The list of ``formats`` must be a comma separated sequence of strings
+containing one or more of these elements:
+
+* ``otf`` (OpenType format),
+* ``ttf`` and ``ttc`` (TrueType format),
+* ``dfont`` (Macintosh TrueType format),
+* ``afm`` (Adobe Font Metrics),
+* ``pfb`` and ``pfa`` (PostScript format).
+
+It corresponds loosely to the ``--formats`` option to
+**luaotfload-tool**. Invalid or duplicate members are ignored; if the
+list does not contain any useful identifiers, the default list
+``"otf,ttf,ttc,dfont"`` will be used.
+
+The variable ``max-fonts`` determines after processing how many font
+files the font scanner will terminate the search. This is useful for
+debugging issues with the font index and has the same effect as the
+option ``--max-fonts`` to **luaotfload-tools**.
+
+The ``scan-local`` flag, if set, will incorporate the current working
+directory as a font search location. NB: This will potentially slow
+down document processing because a font index with local fonts will not
+be saved to disk, so these fonts will have to be re-indexed whenever
+the document is built.
+
+The ``skip-read`` flag is only useful for debugging: It makes
+Luaotfload skip reading fonts. The font information for rebuilding the
+index is taken from the presently existing one.
+
+Unsetting the ``strip`` flag prevents Luaotfload from removing data
+from the index that is only useful when processing font files. NB: this
+can increase the size of the index files significantly and has no
+effect on the runtime behavior.
+
+If ``update-live`` is set, Luaotfload will reload the database if it
+cannot find a requested font. Those who prefer to update manually using
+**luaotfload-tool** should unset this flag.
+
+
+Section ``default-features``
+-----------------------------------------------------------------------
+
+By default Luaotfload enables ``node`` mode and picks the default font
+features that are prescribed in the OpenType standard. This behavior
+may be overridden in the ``default-features`` section. Global defaults
+that will be applied for all scripts can be set via the ``global``
+option, others by the script they are to be applied to. For example,
+a setting of ::
+
+ [default-features]
+ global = mode=base,color=0000FF
+ dflt = smcp,onum
+
+would force *base* mode, tint all fonts blue and activate small
+capitals and text figures globally. Features are specified as a comma
+separated list of variables or variable-value pairs. Variables without
+an explicit value are set to ``true``.
+
+
+Section ``misc``
+-----------------------------------------------------------------------
+
++---------------+--------+-------------------------+
+| variable | type | default |
++---------------+--------+-------------------------+
+| statistics | b | ``false`` |
++---------------+--------+-------------------------+
+| termwidth | n | ``nil`` |
++---------------+--------+-------------------------+
+| version | s | <Luaotfload version> |
++---------------+--------+-------------------------+
+
+With ``statistics`` enabled, extra statistics will be collected during
+index creation and appended to the index file. It may then be queried
+at the Lua end or inspected by reading the file itself.
+
+The value of ``termwidth``, if set, overrides the value retrieved by
+querying the properties of the terminal in which Luatex runs. This is
+useful if the engine runs with ``shell_escape`` disabled and the actual
+terminal dimensions cannot be retrieved.
+
+The value of ``version`` is derived from the version string hard-coded
+in the Luaotfload source. Override at your own risk.
+
+
+Section ``paths``
+-----------------------------------------------------------------------
+
++------------------+--------+------------------------------------+
+| variable | type | default |
++------------------+--------+------------------------------------+
+| cache-dir | s | ``"fonts"`` |
++------------------+--------+------------------------------------+
+| names-dir | s | ``"names"`` |
++------------------+--------+------------------------------------+
+| index-file | s | ``"luaotfload-names.lua"`` |
++------------------+--------+------------------------------------+
+| lookups-file | s | ``"luaotfload-lookup-cache.lua"`` |
++------------------+--------+------------------------------------+
+
+The paths ``cache-dir`` and ``names-dir`` determine the subdirectory
+inside the Luaotfload subtree of the ``luatex-cache`` directory where
+the font cache and the font index will be stored, respectively.
+
+Inside the index directory, the names of the index file and the font
+lookup cache will be derived from the respective values of
+``index-file`` and ``lookups-file``. This is the filename base for the
+bytecode compiled version as well as -- for the index -- the gzipped
+version.
+
+
+Section ``run``
+-----------------------------------------------------------------------
+
++------------------+--------+------------------------------+
+| variable | type | default |
++------------------+--------+------------------------------+
+| color-callback | s | ``"pre_linebreak_filter"`` |
++------------------+--------+------------------------------+
+| definer | s | ``"patch"`` |
++------------------+--------+------------------------------+
+| log-level | n | ``0`` |
++------------------+--------+------------------------------+
+| resolver | s | ``"cached"`` |
++------------------+--------+------------------------------+
+
+The ``color-callback`` option determines the stage at which fonts that
+defined with a ``color=xxyyzz`` feature will be colorized. By default
+this happens in a ``pre_linebreak_filter`` but alternatively the
+``pre_output_filter`` may be chosen, which is faster but might produce
+inconsistent output. The latter also was the default in the 1.x series
+of Luaotfload.
+
+The ``definer`` allows for switching the ``define_font`` callback.
+Apart from the default ``patch`` one may also choose the ``generic``
+one that comes with the vanilla fontloader. Beware that this might
+break tools like Fontspect that rely on the ``patch_font`` callback
+provided by Luaotfload to perform important corrections on font data.
+
+The value of ``log-level`` sets the default verbosity of messages
+printed by Luaotfload. Only messages defined with a verbosity of less
+than or equal to the supplied value will be output on the terminal.
+At a log level of five Luaotfload can be very noisy. Also, printing too
+many messages will slow down the interpreter due to line buffering
+being disabled (see **setbuf**\(3)).
+
+The ``resolver`` setting allows choosing the font name resolution
+function: With the default value ``cached`` Luaotfload saves the result
+of a successful font name request to a cache file to speed up
+subsequent lookups. The alternative, ``normal`` circumvents the cache
+and resolves every request individually. (Since to the restructuring of
+the font name index in Luaotfload 2.4 the performance difference
+between the cached and uncached lookups should be marginal.)
+
+
+FILES
+=======================================================================
+
+Luaotfload only processes the first configuration file it encounters at
+one of the search locations. The file name may be either
+``luaotfload.conf`` or ``luaotfloadrc``, except for the dotfile in the
+user’s home directory which is expected at ``~/.luaotfloadrc``.
+
+Configuration files are located following a series of steps. The search
+terminates as soon as a suitable file is encountered. The sequence of
+locations that Luaotfload looks at is
+
+i. The current working directory of the LuaTeX process.
+ii. The subdirectory ``luaotfload/`` inside the XDG configuration
+ tree, e. g. ``/home/oenothea/config/luaotfload/``.
+iii. The dotfile.
+iv. The *TEXMF* (using kpathsea).
+
+
+SEE ALSO
+=======================================================================
+
+**luaotfload-tool**\(1), **luatex**\(1), **lua**\(1)
+
+* ``texdoc luaotfload`` to display the PDF manual for the *Luaotfload*
+ package
+* Luaotfload development `<https://github.com/lualatex/luaotfload>`_
+* LuaLaTeX mailing list `<http://tug.org/pipermail/lualatex-dev/>`_
+* LuaTeX `<http://luatex.org/>`_
+* Luaotfload on CTAN `<http://ctan.org/pkg/luaotfload>`_
+
+
+REFERENCES
+=======================================================================
+
+* The XDG base specification
+ `<http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`_.
+
+AUTHORS
+=======================================================================
+
+*Luaotfload* is maintained by the LuaLaTeX dev team
+(`<https://github.com/lualatex/>`_).
+
+This manual page was written by Philipp Gesang
+<philipp.gesang@alumni.uni-heidelberg.de>.
+
diff --git a/luaotfload-blacklist.cnf b/luaotfload-blacklist.cnf
deleted file mode 100644
index 5c03dc2..0000000
--- a/luaotfload-blacklist.cnf
+++ /dev/null
@@ -1,12 +0,0 @@
-% Takes ages to load
-LastResort.ttf % a MacOSX font, but also available for free from unicode.org
-% Segfaults under LuaTeX 0.76
-lingoes.ttf
-% http://tug.org/pipermail/luatex/2013-May/004239.html
-Diablindall.ttf
-spltfgbd.ttf
-spltfgbi.ttf
-spltfgit.ttf
-spltfgrg.ttf
-% Buggy Max OS font, see https://github.com/lualatex/luaotfload/issues/139
-Skia.ttf
diff --git a/luaotfload-legacy-attributes.lua b/luaotfload-legacy-attributes.lua
deleted file mode 100644
index c6130b4..0000000
--- a/luaotfload-legacy-attributes.lua
+++ /dev/null
@@ -1,27 +0,0 @@
------------------------------------------------------------------------
--- FILE: otfl-luat-att.lua
--- USAGE: with old luaotfload
--- DESCRIPTION: setting attributes abide luatexbase rules
--- REQUIREMENTS: some old luatex
--- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
--- CREATED: 2013-05-10 20:37:19+0200
------------------------------------------------------------------------
---
-
-if not modules then modules = { } end modules ['otfl-luat-att'] = {
- version = math.pi/42,
- comment = "companion to luaotfload.lua",
- author = "Philipp Gesang",
- copyright = "Luaotfload Development Team",
- license = "GNU GPL v2"
-}
-
-function attributes.private(name)
- local attr = "otfl@" .. name
- local number = luatexbase.attributes[attr]
- if not number then
- number = luatexbase.new_attribute(attr)
- end
- return number
-end
-
diff --git a/luaotfload-legacy-database.lua b/luaotfload-legacy-database.lua
deleted file mode 100644
index b31fe88..0000000
--- a/luaotfload-legacy-database.lua
+++ /dev/null
@@ -1,724 +0,0 @@
-if not modules then modules = { } end modules ['font-nms'] = {
- version = "old",
- comment = "companion to luaotfload.lua",
- author = "Khaled Hosny and Elie Roux",
- copyright = "Luaotfload Development Team",
- license = "GNU GPL v2"
-}
-
-fonts = fonts or { }
-fonts.names = fonts.names or { }
-
-local names = fonts.names
-local names_dir = "luatex-cache/generic/names"
-names.version = "old" -- not the same as in context
-names.data = nil
-names.path = {
- basename = "otfl-names.lua", --- different from current
- localdir = file.join(kpse.expand_var("$TEXMFVAR"), names_dir),
- systemdir = file.join(kpse.expand_var("$TEXMFSYSVAR"), names_dir),
-}
-
-
-local splitpath, expandpath = file.split_path, kpse.expand_path
-local glob, basename = dir.glob, file.basename
-local upper, lower, format = string.upper, string.lower, string.format
-local gsub, match, rpadd = string.gsub, string.match, string.rpadd
-local gmatch, sub, find = string.gmatch, string.sub, string.find
-local utfgsub = unicode.utf8.gsub
-
-local trace_short = false --tracing adapted to rebuilding of the database inside a document
-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
-
-local function fontnames_init()
- return {
- mappings = { },
- status = { },
- version = names.version,
- }
-end
-
-local function load_names()
- local localpath = file.join(names.path.localdir, names.path.basename)
- local systempath = file.join(names.path.systemdir, names.path.basename)
- local kpsefound = kpse.find_file(names.path.basename)
- local foundname
- local data
- if kpsefound and file.isreadable(kpsefound) then
- data = dofile(kpsefound)
- foundname = kpsefound
- elseif file.isreadable(localpath) then
- data = dofile(localpath)
- foundname = localpath
- elseif file.isreadable(systempath) then
- data = dofile(systempath)
- foundname = systempath
- end
- if data then
- logs.info("Font names database loaded: " .. foundname)
- else
- logs.info([[Font names database not found, generating new one.
- This can take several minutes; please be patient.]])
- data = names.update(fontnames_init())
- names.save(data)
- end
- return data
-end
-
-local synonyms = {
- regular = { "normal", "roman", "plain", "book", "medium" },
- -- boldregular was for old versions of Linux Libertine, is it still useful?
- -- semibold is in new versions of Linux Libertine, but there is also a bold,
- -- not sure it's useful here...
- bold = { "demi", "demibold", "semibold", "boldregular" },
- italic = { "regularitalic", "normalitalic", "oblique", "slanted" },
- bolditalic = { "boldoblique", "boldslanted", "demiitalic", "demioblique", "demislanted", "demibolditalic", "semibolditalic" },
-}
-
-local loaded = false
-local reloaded = false
-
-function names.resolve(specification)
- local name = sanitize(specification.name)
- local style = sanitize(specification.style) or "regular"
-
- local size
- if specification.optsize then
- size = tonumber(specification.optsize)
- elseif specification.size then
- size = specification.size / 65536
- 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 next, data.mappings do
- local family, subfamily, fullname, psname
- local optsize, dsnsize, maxsize, minsize
-
- if face.names then
- family = sanitize(face.names.family)
- subfamily = sanitize(face.names.subfamily)
- fullname = sanitize(face.names.fullname)
- psname = sanitize(face.names.psname)
- end
- local fontname = sanitize(face.fontname)
- local pfullname = sanitize(face.fullname)
- if #face.size > 0 then
- optsize = face.size
- dsnsize = optsize[1] and optsize[1] / 10
- -- can be nil
- maxsize = optsize[2] and optsize[2] / 10 or dsnsize
- minsize = optsize[3] and optsize[3] / 10 or dsnsize
- 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
- table.contains(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
- elseif subfamily == "regular" or
- table.contains(synonyms.regular, subfamily) then
- found.fallback = face
- elseif 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
- 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]
- elseif lfs.isfile(found[1].found_at) then
- logs.report("load font",
- "font family='%s', subfamily='%s' found: %s",
- name, style, found[1].found_at)
- return found[1].found_at, 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 next, 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]
- elseif lfs.isfile(closest.found_at) then
- logs.report("load font",
- "font family='%s', subfamily='%s' found: %s",
- name, style, closest.found_at)
- return closest.found_at, closest.filename[2]
- end
- elseif found.fallback then
- return found.fallback.filename[1], found.fallback.filename[2]
- end
- -- no font found so far
- if not reloaded then
- -- try reloading the database
- names.data = names.update(names.data)
- names.save(names.data)
- reloaded = true
- return names.resolve(specification)
- else
- -- else, fallback to filename
- return specification.name, false
- end
- end
- else
- if not reloaded then
- names.data = names.update()
- names.save(names.data)
- reloaded = true
- return names.resolve(specification)
- else
- return specification.name, false
- end
- end
-end
-
-names.resolvespec = names.resolve
-
-function names.set_log_level(level)
- if level == 2 then
- trace_loading = true
- elseif level >= 3 then
- trace_loading = true
- trace_search = true
- end
-end
-
-local lastislog = 0
-
-local function log(fmt, ...)
- lastislog = 1
- texio.write_nl(format("luaotfload | %s", format(fmt,...)))
- io.flush()
-end
-
-logs = logs or { }
-logs.report = logs.report or log
-logs.info = logs.info or log
-
-local function font_fullinfo(filename, subfont, texmf)
- local found_at = filename
- local t = { }
- local f = fontloader.open(filename, subfont)
- if not f then
- if trace_loading then
- logs.report("error: failed to open %s", filename)
- end
- return
- end
- local m = 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 next, m.fontstyle_name do
- if v.lang == 1033 then
- t.fontstyle_name = v.name
- end
- end
- end
- if m.names then
- for _,v in next, 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,
- family = v.names.preffamilyname or v.names.family,
- subfamily= t.fontstyle_name or v.names.prefmodifiers or v.names.subfamily,
- psname = v.names.postscriptname
- }
- end
- end
- else
- -- no names table, propably a broken font
- if trace_loading then
- logs.report("broken font rejected: %s", basefile)
- end
- return
- 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,
- }
- -- rather, waste space on paths
- t.found_at = found_at
- return t
-end
-
-local function load_font(filename, fontnames, newfontnames, texmf)
- local newmappings = newfontnames.mappings
- local newstatus = newfontnames.status
- local mappings = fontnames.mappings
- local status = fontnames.status
- local basefile = texmf and basename(filename) or filename
- if filename then
- if table.contains(names.blacklist, filename) or
- table.contains(names.blacklist, basename(filename)) then
- if trace_search then
- logs.report("ignoring font '%s'", filename)
- end
- return
- end
- local timestamp, db_timestamp
- db_timestamp = status[basefile] and status[basefile].timestamp
- timestamp = lfs.attributes(filename, "modification")
-
- local index_status = newstatus[basefile] or (not texmf and newstatus[basename(filename)])
- if index_status and index_status.timestamp == timestamp then
- -- already indexed this run
- return
- end
-
- newstatus[basefile] = newstatus[basefile] or { }
- newstatus[basefile].timestamp = timestamp
- newstatus[basefile].index = newstatus[basefile].index or { }
-
- if db_timestamp == timestamp and not newstatus[basefile].index[1] then
- for _,v in next, status[basefile].index do
- local index = #newstatus[basefile].index
- newmappings[#newmappings+1] = mappings[v]
- newstatus[basefile].index[index+1] = #newmappings
- end
- if trace_loading then
- logs.report("font already indexed: %s", basefile)
- end
- return
- end
- local info = fontloader.info(filename)
- if info then
- if type(info) == "table" and #info > 1 then
- for i in next, info do
- local fullinfo = font_fullinfo(filename, i-1, texmf)
- if not fullinfo then
- return
- end
- local index = newstatus[basefile].index[i]
- if not index then
- index = #newmappings+1
- end
- newmappings[index] = fullinfo
- newstatus[basefile].index[i] = index
- end
- else
- local fullinfo = font_fullinfo(filename, false, texmf)
- if not fullinfo then
- return
- end
- local index = newstatus[basefile].index[1]
- if not index then
- index = #newmappings+1
- end
- newmappings[index] = fullinfo
- newstatus[basefile].index[1] = index
- end
- else
- if trace_loading then
- logs.report("failed to load %s", basefile)
- 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
- - reading symlinks under non-Win32
- - using kpse.readable_file on Win32
- ]]
- if os.type == "windows" or os.type == "msdos" then
- path = path:gsub('\\', '/')
- path = path:lower()
- path = path:gsub('^/cygdrive/(%a)/', '%1:/')
- end
- if os.type ~= "windows" and os.type ~= "msdos" then
- local dest = lfs.readlink(path)
- if dest then
- if kpse.readable_file(dest) then
- path = dest
- elseif kpse.readable_file(file.join(file.dirname(path), dest)) then
- path = file.join(file.dirname(path), dest)
- else
- -- broken symlink?
- end
- end
- end
- path = file.collapse_path(path)
- return path
-end
-
-fonts.path_normalize = path_normalize
-
-names.blacklist = { }
-
-local function read_blacklist()
- local files = {
- kpse.lookup("otfl-blacklist.cnf", {all=true, format="tex"})
- }
- local blacklist = names.blacklist
-
- if files and type(files) == "table" then
- for _,v in next, files do
- for line in io.lines(v) do
- line = line:strip() -- to get rid of lines like " % foo"
- if line:find("^%%") or line:is_empty() then
- -- comment or empty line
- else
- line = line:split("%")[1]
- line = line:strip()
- if trace_search then
- logs.report("blacklisted file: %s", line)
- end
- blacklist[#blacklist+1] = line
- end
- end
- end
- end
-end
-
-local font_extensions = { "otf", "ttf", "ttc", "dfont" }
-
-local function scan_dir(dirname, fontnames, newfontnames, 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
- - texmf is a boolean saying if we are scanning a texmf directory
- ]]
- local list, found = { }, { }
- local nbfound = 0
- if trace_search then
- logs.report("scanning '%s'", dirname)
- end
- for _,i in next, font_extensions do
- for _,ext in next, { i, upper(i) } do
- found = glob(format("%s/**.%s$", dirname, ext))
- -- note that glob fails silently on broken symlinks, which happens
- -- sometimes in TeX Live.
- if trace_search then
- logs.report("%s '%s' fonts found", #found, ext)
- end
- nbfound = nbfound + #found
- table.append(list, found)
- end
- end
- if trace_search then
- logs.report("%d fonts found in '%s'", nbfound, dirname)
- end
-
- for _,file in next, list do
- file = path_normalize(file)
- if trace_loading then
- logs.report("loading font: %s", file)
- end
- load_font(file, fontnames, newfontnames, texmf)
- end
-end
-
-local function scan_texmf_fonts(fontnames, newfontnames)
- --[[
- This function scans all fonts in the texmf tree, through kpathsea
- variables OPENTYPEFONTS and TTFONTS of texmf.cnf
- ]]
- if expandpath("$OSFONTDIR"):is_empty() then
- logs.info("Scanning TEXMF fonts...")
- else
- logs.info("Scanning TEXMF and OS fonts...")
- end
- local fontdirs = expandpath("$OPENTYPEFONTS"):gsub("^%.", "")
- fontdirs = fontdirs .. expandpath("$TTFONTS"):gsub("^%.", "")
- if not fontdirs:is_empty() then
- for _,d in next, splitpath(fontdirs) do
- scan_dir(d, fontnames, newfontnames, true)
- end
- end
-end
-
---[[
- For the OS fonts, there are several options:
- - if OSFONTDIR is set (which is the case under windows by default but
- not on the other OSs), it scans it at the same time as the texmf tree,
- in the scan_texmf_fonts.
- - in addition:
- - under Windows and Mac OSX, we take a look at some hardcoded directories
- - under Unix, we read /etc/fonts/fonts.conf and read the directories in it
-
- This means that if you have fonts in fancy directories, you need to set them
- in OSFONTDIR if they cannot be found by fontconfig.
-]]
-
-local function read_fonts_conf(path, results, passed_paths)
- --[[
- This function parses /etc/fonts/fonts.conf and returns all the dir it finds.
- The code is minimal, please report any error it may generate.
- ]]
- local f = io.open(path)
- table.insert(passed_paths, path)
- if not f then
- logs.info("Warning: unable to read "..path.. ", skipping...")
- return results
- end
- local incomments = false
- for line in f:lines() do
- while line and line ~= "" do
- -- spaghetti code... hmmm...
- if incomments then
- local tmp = find(line, '-->')
- if tmp then
- incomments = false
- line = sub(line, tmp+3)
- else
- line = nil
- end
- else
- local tmp = find(line, '<!--')
- local newline = line
- if tmp then
- -- for the analysis, we take everything that is before the
- -- comment sign
- newline = sub(line, 1, tmp-1)
- -- and we loop again with the comment
- incomments = true
- line = sub(line, tmp+4)
- else
- -- if there is no comment start, the block after that will
- -- end the analysis, we exit the while loop
- line = nil
- end
- for dir in gmatch(newline, '<dir>([^<]+)</dir>') do
- -- now we need to replace ~ by kpse.expand_path('~')
- if sub(dir, 1, 1) == '~' then
- dir = file.join(kpse.expand_path('~'), sub(dir, 2))
- end
- -- we exclude paths with texmf in them, as they should be
- -- found anyway
- if not find(dir, 'texmf') then
- results[#results+1] = dir
- end
- end
- for include in gmatch(newline, '<include[^<]*>([^<]+)</include>') do
- -- include here can be four things: a directory or a file,
- -- in absolute or relative path.
- if sub(include, 1, 1) == '~' then
- include = file.join(kpse.expand_path('~'),sub(include, 2))
- -- First if the path is relative, we make it absolute:
- elseif not lfs.isfile(include) and not lfs.isdir(include) then
- include = file.join(file.dirname(path), include)
- end
- if lfs.isfile(include) and kpse.readable_file(include) and not table.contains(passed_paths, include) then
- -- we exclude path with texmf in them, as they should
- -- be found otherwise
- read_fonts_conf(include, results, passed_paths)
- elseif lfs.isdir(include) then
- for _,f in next, glob(file.join(include, "*.conf")) do
- if not table.contains(passed_paths, f) then
- read_fonts_conf(f, results, passed_paths)
- end
- end
- end
- end
- end
- end
- end
- f:close()
- return results
-end
-
--- for testing purpose
-names.read_fonts_conf = read_fonts_conf
-
-local function get_os_dirs()
- if os.name == 'macosx' then
- return {
- file.join(kpse.expand_path('~'), "Library/Fonts"),
- "/Library/Fonts",
- "/System/Library/Fonts",
- "/Network/Library/Fonts",
- }
- elseif os.type == "windows" or os.type == "msdos" then
- local windir = os.getenv("WINDIR")
- return { file.join(windir, 'Fonts') }
- else
- return read_fonts_conf("/etc/fonts/fonts.conf", {}, {})
- end
-end
-
-local function scan_os_fonts(fontnames, newfontnames)
- --[[
- This function scans the OS fonts through
- - fontcache for Unix (reads the fonts.conf file and scans the directories)
- - a static set of directories for Windows and MacOSX
- ]]
- logs.info("Scanning OS fonts...")
- if trace_search then
- logs.info("Searching in static system directories...")
- end
- for _,d in next, get_os_dirs() do
- scan_dir(d, fontnames, newfontnames, false)
- end
-end
-
-local function update_names(fontnames, force)
- --[[
- The main function, scans everything
- - fontnames is the final table to return
- - force is whether we rebuild it from scratch or not
- ]]
- logs.info("Updating the font names database:")
-
- if force then
- fontnames = fontnames_init()
- else
- if not fontnames then
- fontnames = names.load()
- end
- if fontnames.version ~= names.version then
- fontnames = fontnames_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()
- read_blacklist()
- scan_texmf_fonts(fontnames, newfontnames)
- scan_os_fonts(fontnames, newfontnames)
- return newfontnames
-end
-
-local function save_names(fontnames)
- local savepath = names.path.localdir
- if not lfs.isdir(savepath) then
- dir.mkdirs(savepath)
- end
- savepath = file.join(savepath, names.path.basename)
- if file.iswritable(savepath) then
- table.tofile(savepath, fontnames, true)
- logs.info("Font names database saved: %s \n", savepath)
- return savepath
- else
- logs.info("Failed to save names database\n")
- return nil
- end
-end
-
-local function scan_external_dir(dir)
- local old_names, new_names
- if loaded then
- old_names = names.data
- else
- old_names = names.load()
- loaded = true
- end
- new_names = table.copy(old_names)
- scan_dir(dir, old_names, new_names)
- names.data = new_names
-end
-
-names.scan = scan_external_dir
-names.load = load_names
-names.update = update_names
-names.save = save_names
-
--- vim:ft=lua:sw=4:ts=4:expandtab
diff --git a/luaotfload-legacy-merged.lua b/luaotfload-legacy-merged.lua
deleted file mode 100644
index 9bec298..0000000
--- a/luaotfload-legacy-merged.lua
+++ /dev/null
@@ -1,8157 +0,0 @@
--- merged file : luaotfload-legacy-merged.lua
--- parent file : luaotfload-legacy.lua
--- merge date : Fri May 10 20:57:35 2013
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['luat-dum']={
- version=1.100,
- comment="companion to luatex-*.tex",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local dummyfunction=function() end
-statistics={
- register=dummyfunction,
- starttiming=dummyfunction,
- stoptiming=dummyfunction,
-}
-directives={
- register=dummyfunction,
- enable=dummyfunction,
- disable=dummyfunction,
-}
-trackers={
- register=dummyfunction,
- enable=dummyfunction,
- disable=dummyfunction,
-}
-experiments={
- register=dummyfunction,
- enable=dummyfunction,
- disable=dummyfunction,
-}
-storage={
- register=dummyfunction,
- shared={},
-}
-logs={
- report=dummyfunction,
- simple=dummyfunction,
-}
-tasks={
- new=dummyfunction,
- actions=dummyfunction,
- appendaction=dummyfunction,
- prependaction=dummyfunction,
-}
-callbacks={
- register=function(n,f) return callback.register(n,f) end,
-}
-texconfig.kpse_init=true
-resolvers=resolvers or {}
-local remapper={
- otf="opentype fonts",
- ttf="truetype fonts",
- ttc="truetype fonts",
- dfont="truetype fonts",
- cid="cid maps",
- fea="font feature files",
-}
-function resolvers.find_file(name,kind)
- name=string.gsub(name,"\\","/")
- 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)
- if not kind or kind=="" then
- kind=file.extname(name)
- end
- return resolvers.find_file(name,(kind and remapper[kind]) or kind)
-end
-caches={}
-local writable,readables=nil,{}
-if not caches.namespace or caches.namespace=="" or caches.namespace=="context" then
- caches.namespace='generic'
-end
-do
- local cachepaths
- if kpse.expand_var('$TEXMFCACHE')~='$TEXMFCACHE' then
- cachepaths=kpse.expand_var('$TEXMFCACHE')
- elseif kpse.expand_var('$TEXMFVAR')~='$TEXMFVAR' then
- cachepaths=kpse.expand_var('$TEXMFVAR')
- end
- if not cachepaths then
- cachepaths="."
- end
- cachepaths=string.split(cachepaths,os.type=="windows" and ";" or ":")
- for i=1,#cachepaths do
- local done
- writable=file.join(cachepaths[i],"luatex-cache")
- writable=file.join(writable,caches.namespace)
- writable,done=dir.mkdirs(writable)
- if done then
- break
- end
- end
- for i=1,#cachepaths do
- if file.isreadable(cachepaths[i]) then
- readables[#readables+1]=file.join(cachepaths[i],"luatex-cache",caches.namespace)
- end
- end
- if not writable then
- texio.write_nl("quiting: fix your writable cache path\n")
- os.exit()
- elseif #readables==0 then
- texio.write_nl("quiting: fix your readable cache path\n")
- os.exit()
- elseif #readables==1 and readables[1]==writable then
- texio.write(string.format("(using cache: %s)",writable))
- else
- texio.write(string.format("(using write cache: %s)",writable))
- texio.write(string.format("(using read cache: %s)",table.concat(readables," ")))
- end
-end
-function caches.getwritablepath(category,subcategory)
- local path=file.join(writable,category)
- lfs.mkdir(path)
- path=file.join(path,subcategory)
- lfs.mkdir(path)
- return path
-end
-function caches.getreadablepaths(category,subcategory)
- local t={}
- for i=1,#readables do
- t[i]=file.join(readables[i],category,subcategory)
- end
- return t
-end
-local function makefullname(path,name)
- if path and path~="" then
- name="temp-"..name
- 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(paths,name)
- for i=1,#paths do
- local fullname=makefullname(paths[i],name)
- if fullname then
- texio.write(string.format("(load: %s)",fullname))
- local data=loadfile(fullname)
- return data and data()
- end
- end
-end
-function caches.savedata(path,name,data)
- local fullname=makefullname(path,name)
- if fullname then
- texio.write(string.format("(save: %s)",fullname))
- table.tofile(fullname,data,'return',false,true,false)
- end
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-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="GNU GPL v2"
-}
-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.info(category,fmt,...)
- if fmt then
- write_nl(format("%s | %s: %s",name,category,format(fmt,...)))
- elseif category then
- write_nl(format("%s | %s",name,category))
- else
- write_nl(format("%s |",name))
- end
- io.flush()
-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.attribute[0]=0
-tex.ctxcatcodes=luatexbase.catcodetables.latex
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['data-con']={
- version=1.100,
- comment="companion to luat-lib.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local format,lower,gsub=string.format,string.lower,string.gsub
-local trace_cache=false trackers.register("resolvers.cache",function(v) trace_cache=v end)
-local trace_containers=false trackers.register("resolvers.containers",function(v) trace_containers=v end)
-local trace_storage=false trackers.register("resolvers.storage",function(v) trace_storage=v end)
-containers=containers or {}
-containers.usecache=true
-local function report(container,tag,name)
- if trace_cache or trace_containers then
- logs.report(format("%s cache",container.subcategory),"%s: %s",tag,name or 'invalid')
- end
-end
-local allocated={}
-local mt={
- __index=function(t,k)
- if k=="writable" then
- local writable=caches.getwritablepath(t.category,t.subcategory) or { "." }
- t.writable=writable
- return writable
- elseif k=="readables" then
- local readables=caches.getreadablepaths(t.category,t.subcategory) or { "." }
- t.readables=readables
- return readables
- end
- end
-}
-function containers.define(category,subcategory,version,enabled)
- if category and subcategory then
- local c=allocated[category]
- if not c then
- c={}
- allocated[category]=c
- end
- local s=c[subcategory]
- if not s then
- s={
- category=category,
- subcategory=subcategory,
- storage={},
- enabled=enabled,
- version=version or math.pi,
- trace=false,
- }
- setmetatable(s,mt)
- c[subcategory]=s
- end
- return s
- end
-end
-function containers.is_usable(container,name)
- return container.enabled and caches and caches.iswritable(container.writable,name)
-end
-function containers.is_valid(container,name)
- if name and name~="" then
- local storage=container.storage[name]
- return storage and storage.cache_version==container.version
- else
- return false
- end
-end
-function containers.read(container,name)
- local storage=container.storage
- local stored=storage[name]
- if not stored and container.enabled and caches and containers.usecache then
- stored=caches.loaddata(container.readables,name)
- if stored and stored.cache_version==container.version then
- report(container,"loaded",name)
- else
- stored=nil
- end
- storage[name]=stored
- elseif stored then
- report(container,"reusing",name)
- end
- return stored
-end
-function containers.write(container,name,data)
- if data then
- data.cache_version=container.version
- if container.enabled and caches then
- local unique,shared=data.unique,data.shared
- data.unique,data.shared=nil,nil
- caches.savedata(container.writable,name,data)
- report(container,"saved",name)
- data.unique,data.shared=unique,shared
- end
- report(container,"stored",name)
- container.storage[name]=data
- end
- return data
-end
-function containers.content(container,name)
- return container.storage[name]
-end
-function containers.cleanname(name)
- return (gsub(lower(name),"[^%w%d]+","-"))
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-ini']={
- 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 format,serialize=string.format,table.serialize
-local write_nl=texio.write_nl
-local lower=string.lower
-if not fontloader then fontloader=fontforge end
-fontloader.totable=fontloader.to_table
-fonts=fonts or {}
-fonts.ids=fonts.ids or {} fonts.identifiers=fonts.ids
-fonts.chr=fonts.chr or {} fonts.characters=fonts.chr
-fonts.qua=fonts.qua or {} fonts.quads=fonts.qua
-fonts.tfm=fonts.tfm or {}
-fonts.mode='base'
-fonts.private=0xF0000
-fonts.verbose=false
-fonts.ids[0]={
- characters={},
- descriptions={},
- name="nullfont",
-}
-fonts.chr[0]={}
-fonts.methods=fonts.methods or {
- base={ tfm={},afm={},otf={},vtf={},fix={} },
- node={ tfm={},afm={},otf={},vtf={},fix={} },
-}
-fonts.initializers=fonts.initializers or {
- base={ tfm={},afm={},otf={},vtf={},fix={} },
- node={ tfm={},afm={},otf={},vtf={},fix={} }
-}
-fonts.triggers=fonts.triggers or {
- 'mode',
- 'language',
- 'script',
- 'strategy',
-}
-fonts.processors=fonts.processors or {}
-fonts.manipulators=fonts.manipulators or {}
-fonts.define=fonts.define or {}
-fonts.define.specify=fonts.define.specify or {}
-fonts.define.specify.synonyms=fonts.define.specify.synonyms or {}
-if not fonts.color then
- fonts.color={
- set=function() end,
- reset=function() end,
- }
-end
-fonts.formats={}
-function fonts.fontformat(filename,default)
- local extname=lower(file.extname(filename))
- local format=fonts.formats[extname]
- if format then
- return format
- else
- logs.report("fonts define","unable to determine font format for '%s'",filename)
- return default
- end
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['node-dum']={
- version=1.001,
- comment="companion to luatex-*.tex",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-nodes=nodes or {}
-fonts=fonts or {}
-attributes=attributes or {}
-local traverse_id=node.traverse_id
-local free_node=node.free
-local remove_node=node.remove
-local new_node=node.new
-local glyph=node.id('glyph')
-local fontdata=fonts.ids or {}
-function nodes.simple_font_handler(head)
- head=nodes.process_characters(head)
- nodes.inject_kerns(head)
- nodes.protect_glyphs(head)
- head=node.ligaturing(head)
- head=node.kerning(head)
- return head
-end
-if tex.attribute[0]~=0 then
- texio.write_nl("log","!")
- texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be")
- texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special")
- texio.write_nl("log","! purposed so setting them at the TeX end might break the font handler.")
- texio.write_nl("log","!")
- tex.attribute[0]=0
-end
-nodes.protect_glyphs=node.protect_glyphs
-nodes.unprotect_glyphs=node.unprotect_glyphs
-function nodes.process_characters(head)
- local usedfonts,done,prevfont={},false,nil
- for n in traverse_id(glyph,head) do
- local font=n.font
- if font~=prevfont then
- prevfont=font
- local used=usedfonts[font]
- if not used then
- local tfmdata=fontdata[font]
- if tfmdata then
- local shared=tfmdata.shared
- if shared then
- local processors=shared.processes
- if processors and #processors>0 then
- usedfonts[font]=processors
- done=true
- end
- end
- end
- end
- end
- end
- if done then
- for font,processors in next,usedfonts do
- for i=1,#processors do
- local h,d=processors[i](head,font,0)
- head,done=h or head,done or d
- end
- end
- end
- return head,true
-end
-function nodes.kern(k)
- local n=new_node("kern",1)
- n.kern=k
- return n
-end
-function nodes.remove(head,current,free_too)
- local t=current
- head,current=remove_node(head,current)
- if t then
- if free_too then
- free_node(t)
- t=nil
- else
- t.next,t.prev=nil,nil
- end
- end
- return head,current,t
-end
-function nodes.delete(head,current)
- return nodes.remove(head,current,true)
-end
-nodes.before=node.insert_before
-nodes.after=node.insert_after
-attributes.unsetvalue=-0x7FFFFFFF
-local numbers,last={},127
-function attributes.private(name)
- local number=numbers[name]
- if not number then
- if last<255 then
- last=last+1
- end
- number=last
- numbers[name]=number
- end
- return number
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['node-inj']={
- version=1.001,
- comment="companion to node-ini.mkiv",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local next=next
-local trace_injections=false trackers.register("nodes.injections",function(v) trace_injections=v end)
-fonts=fonts or {}
-fonts.tfm=fonts.tfm or {}
-fonts.ids=fonts.ids or {}
-local fontdata=fonts.ids
-local glyph=node.id('glyph')
-local kern=node.id('kern')
-local traverse_id=node.traverse_id
-local unset_attribute=node.unset_attribute
-local has_attribute=node.has_attribute
-local set_attribute=node.set_attribute
-local insert_node_before=node.insert_before
-local insert_node_after=node.insert_after
-local newkern=nodes.kern
-local markbase=attributes.private('markbase')
-local markmark=attributes.private('markmark')
-local markdone=attributes.private('markdone')
-local cursbase=attributes.private('cursbase')
-local curscurs=attributes.private('curscurs')
-local cursdone=attributes.private('cursdone')
-local kernpair=attributes.private('kernpair')
-local cursives={}
-local marks={}
-local kerns={}
-function nodes.set_cursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext)
- local dx,dy=factor*(exit[1]-entry[1]),factor*(exit[2]-entry[2])
- local ws,wn=tfmstart.width,tfmnext.width
- local bound=#cursives+1
- set_attribute(start,cursbase,bound)
- set_attribute(nxt,curscurs,bound)
- cursives[bound]={ rlmode,dx,dy,ws,wn }
- return dx,dy,bound
-end
-function nodes.set_pair(current,factor,rlmode,r2lflag,spec,tfmchr)
- local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4]
- if x~=0 or w~=0 or y~=0 or h~=0 then
- local bound=has_attribute(current,kernpair)
- if bound then
- local kb=kerns[bound]
- kb[2],kb[3],kb[4],kb[5]=(kb[2] 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)
- kerns[bound]={ rlmode,x,y,w,h,r2lflag,tfmchr.width }
- end
- return x,y,w,h,bound
- end
- return x,y,w,h
-end
-function nodes.set_kern(current,factor,rlmode,x,tfmchr)
- local dx=factor*x
- if dx~=0 then
- local bound=#kerns+1
- set_attribute(current,kernpair,bound)
- kerns[bound]={ rlmode,dx }
- return dx,bound
- else
- return 0,0
- end
-end
-function nodes.set_mark(start,base,factor,rlmode,ba,ma,index)
- local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2])
- local bound=has_attribute(base,markbase)
- if bound then
- local mb=marks[bound]
- if mb then
- if not index then index=#mb+1 end
- mb[index]={ dx,dy }
- set_attribute(start,markmark,bound)
- set_attribute(start,markdone,index)
- return dx,dy,bound
- else
- logs.report("nodes mark","possible problem, U+%04X is base without data (id: %s)",base.char,bound)
- end
- end
- index=index or 1
- bound=#marks+1
- set_attribute(base,markbase,bound)
- set_attribute(start,markmark,bound)
- set_attribute(start,markdone,index)
- marks[bound]={ [index]={ dx,dy,rlmode } }
- return dx,dy,bound
-end
-function nodes.trace_injection(head)
- local function dir(n)
- return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or ("unset")
- end
- local function report(...)
- logs.report("nodes finisher",...)
- end
- report("begin run")
- for n in traverse_id(glyph,head) do
- if n.subtype<256 then
- local kp=has_attribute(n,kernpair)
- local mb=has_attribute(n,markbase)
- local mm=has_attribute(n,markmark)
- local md=has_attribute(n,markdone)
- local cb=has_attribute(n,cursbase)
- local cc=has_attribute(n,curscurs)
- report("char U+%05X, font=%s",n.char,n.font)
- if kp then
- local k=kerns[kp]
- if k[3] then
- report(" pairkern: dir=%s, x=%s, y=%s, w=%s, h=%s",dir(k[1]),k[2] or "?",k[3] or "?",k[4] or "?",k[5] or "?")
- else
- report(" kern: dir=%s, dx=%s",dir(k[1]),k[2] or "?")
- end
- end
- if mb then
- report(" markbase: bound=%s",mb)
- end
- if mm then
- local m=marks[mm]
- if mb then
- local m=m[mb]
- if m then
- report(" markmark: bound=%s, index=%s, dx=%s, dy=%s",mm,md or "?",m[1] or "?",m[2] or "?")
- else
- report(" markmark: bound=%s, missing index",mm)
- end
- else
- m=m[1]
- report(" markmark: bound=%s, dx=%s, dy=%s",mm,m[1] or "?",m[2] or "?")
- end
- end
- if cb then
- report(" cursbase: bound=%s",cb)
- end
- if cc then
- local c=cursives[cc]
- report(" curscurs: bound=%s, dir=%s, dx=%s, dy=%s",cc,dir(c[1]),c[2] or "?",c[3] or "?")
- end
- end
- end
- report("end run")
-end
-function nodes.inject_kerns(head,where,keep)
- local has_marks,has_cursives,has_kerns=next(marks),next(cursives),next(kerns)
- if has_marks or has_cursives then
- if trace_injections then
- nodes.trace_injection(head)
- end
- local done,ky,rl,valid,cx,wx,mk=false,{},{},{},{},{},{}
- if has_kerns then
- local nf,tm=nil,nil
- for n in traverse_id(glyph,head) do
- if n.subtype<256 then
- valid[#valid+1]=n
- if n.font~=nf then
- nf=n.font
- tm=fontdata[nf].marks
- end
- mk[n]=tm[n.char]
- local k=has_attribute(n,kernpair)
- if k then
- local kk=kerns[k]
- if kk then
- local x,y,w,h=kk[2] or 0,kk[3] or 0,kk[4] or 0,kk[5] or 0
- local dy=y-h
- if dy~=0 then
- ky[n]=dy
- end
- if w~=0 or x~=0 then
- wx[n]=kk
- end
- rl[n]=kk[1]
- end
- end
- end
- end
- else
- local nf,tm=nil,nil
- for n in traverse_id(glyph,head) do
- if n.subtype<256 then
- valid[#valid+1]=n
- if n.font~=nf then
- nf=n.font
- tm=fontdata[nf].marks
- end
- mk[n]=tm[n.char]
- end
- end
- end
- if #valid>0 then
- local cx={}
- if has_kerns and next(ky) then
- for n,k in next,ky do
- n.yoffset=k
- end
- end
- if has_cursives then
- local p_cursbase,p=nil,nil
- local t,d,maxt={},{},0
- for i=1,#valid do
- local n=valid[i]
- if not mk[n] then
- local n_cursbase=has_attribute(n,cursbase)
- if p_cursbase then
- local n_curscurs=has_attribute(n,curscurs)
- if p_cursbase==n_curscurs then
- local c=cursives[n_curscurs]
- if c then
- local rlmode,dx,dy,ws,wn=c[1],c[2],c[3],c[4],c[5]
- if rlmode>=0 then
- dx=dx-ws
- else
- dx=dx+wn
- end
- if dx~=0 then
- cx[n]=dx
- rl[n]=rlmode
- end
- dy=-dy
- maxt=maxt+1
- t[maxt]=p
- d[maxt]=dy
- else
- maxt=0
- end
- end
- elseif maxt>0 then
- local ny=n.yoffset
- for i=maxt,1,-1 do
- ny=ny+d[i]
- local ti=t[i]
- ti.yoffset=ti.yoffset+ny
- end
- maxt=0
- end
- if not n_cursbase and maxt>0 then
- local ny=n.yoffset
- for i=maxt,1,-1 do
- ny=ny+d[i]
- local ti=t[i]
- ti.yoffset=ny
- end
- maxt=0
- end
- p_cursbase,p=n_cursbase,n
- end
- end
- if maxt>0 then
- local ny=n.yoffset
- for i=maxt,1,-1 do
- ny=ny+d[i]
- local ti=t[i]
- ti.yoffset=ny
- end
- maxt=0
- end
- if not keep then
- cursives={}
- end
- end
- if has_marks then
- for i=1,#valid do
- local p=valid[i]
- local p_markbase=has_attribute(p,markbase)
- if p_markbase then
- local mrks=marks[p_markbase]
- for n in traverse_id(glyph,p.next) do
- local n_markmark=has_attribute(n,markmark)
- if p_markbase==n_markmark then
- local index=has_attribute(n,markdone) or 1
- local d=mrks[index]
- if d then
- local rlmode=d[3]
- if rlmode and rlmode>0 then
- local k=wx[p]
- if k then
- n.xoffset=p.xoffset-(p.width-d[1])-k[2]
- else
- n.xoffset=p.xoffset-(p.width-d[1])
- end
- else
- local k=wx[p]
- if k then
- n.xoffset=p.xoffset-d[1]-k[2]
- else
- n.xoffset=p.xoffset-d[1]
- end
- end
- if mk[p] then
- n.yoffset=p.yoffset+d[2]
- else
- n.yoffset=n.yoffset+p.yoffset+d[2]
- end
- end
- else
- break
- end
- end
- end
- end
- if not keep then
- marks={}
- end
- end
- if next(wx) then
- for n,k in next,wx do
- local rl,x,w,r2l=k[1],k[2] or 0,k[4] or 0,k[6]
- local wx=w-x
- if r2l then
- if wx~=0 then
- insert_node_before(head,n,newkern(wx))
- end
- if x~=0 then
- insert_node_after (head,n,newkern(x))
- end
- else
- if x~=0 then
- insert_node_before(head,n,newkern(x))
- end
- if wx~=0 then
- insert_node_after(head,n,newkern(wx))
- end
- end
- end
- end
- if next(cx) then
- for n,k in next,cx do
- if k~=0 then
- local rln=rl[n]
- if rln and rln<0 then
- insert_node_before(head,n,newkern(-k))
- else
- insert_node_before(head,n,newkern(k))
- end
- end
- end
- end
- if not keep then
- kerns={}
- end
- return head,true
- elseif not keep then
- kerns,cursives,marks={},{},{}
- end
- elseif has_kerns then
- if trace_injections then
- nodes.trace_injection(head)
- end
- for n in traverse_id(glyph,head) do
- if n.subtype<256 then
- local k=has_attribute(n,kernpair)
- if k then
- local kk=kerns[k]
- if kk then
- local rl,x,y,w=kk[1],kk[2] or 0,kk[3],kk[4]
- if y and y~=0 then
- n.yoffset=y
- end
- if w then
- local r2l=kk[6]
- local wx=w-x
- if r2l then
- if wx~=0 then
- insert_node_before(head,n,newkern(wx))
- end
- if x~=0 then
- insert_node_after (head,n,newkern(x))
- end
- else
- if x~=0 then
- insert_node_before(head,n,newkern(x))
- end
- if wx~=0 then
- insert_node_after(head,n,newkern(wx))
- end
- end
- else
- if x~=0 then
- insert_node_before(head,n,newkern(x))
- end
- end
- end
- end
- end
- end
- if not keep then
- kerns={}
- end
- return head,true
- else
- end
- return head,false
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-
-if not modules then modules={} end modules ['otfl-luat-att']={
- version=math.pi/42,
- comment="companion to luaotfload.lua",
- author="Philipp Gesang",
- copyright="Luaotfload Development Team",
- license="GNU GPL v2"
-}
-function attributes.private(name)
- local attr="otfl@"..name
- local number=luatexbase.attributes[attr]
- if not number then
- number=luatexbase.new_attribute(attr)
- end
- return number
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-tfm']={
- 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 next,format,match,lower,gsub=next,string.format,string.match,string.lower,string.gsub
-local concat,sortedkeys,utfbyte,serialize=table.concat,table.sortedkeys,utf.byte,table.serialize
-local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
-local trace_scaling=false trackers.register("fonts.scaling",function(v) trace_scaling=v end)
-fonts=fonts or {}
-fonts.tfm=fonts.tfm or {}
-fonts.ids=fonts.ids or {}
-local tfm=fonts.tfm
-fonts.loaded=fonts.loaded or {}
-fonts.dontembed=fonts.dontembed or {}
-fonts.triggers=fonts.triggers or {}
-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
-tfm.resolve_vf=true
-tfm.share_base_kerns=false
-tfm.mathactions={}
-tfm.fontname_mode="fullpath"
-tfm.enhance=tfm.enhance or function() end
-fonts.formats.tfm="type1"
-function tfm.read_from_tfm(specification)
- local fname,tfmdata=specification.filename or "",nil
- if fname~="" then
- if trace_defining then
- logs.report("define font","loading tfm file %s at size %s",fname,specification.size)
- end
- tfmdata=font.read_tfm(fname,specification.size)
- if tfmdata then
- tfmdata.descriptions=tfmdata.descriptions or {}
- if tfm.resolve_vf then
- fonts.logger.save(tfmdata,file.extname(fname),specification)
- fname=resolvers.findbinfile(specification.name,'ovf')
- if fname and fname~="" then
- local vfdata=font.read_vf(fname,specification.size)
- if vfdata then
- local chars=tfmdata.characters
- for k,v in next,vfdata.characters do
- chars[k].commands=v.commands
- end
- tfmdata.type='virtual'
- tfmdata.fonts=vfdata.fonts
- end
- end
- end
- tfm.enhance(tfmdata,specification)
- end
- elseif trace_defining then
- logs.report("define font","loading tfm with name %s fails",specification.name)
- end
- return tfmdata
-end
-local factors={
- pt=65536.0,
- bp=65781.8,
-}
-function tfm.setfactor(f)
- tfm.factor=factors[f or 'pt'] or factors.pt
-end
-tfm.setfactor()
-function tfm.scaled(scaledpoints,designsize)
- if scaledpoints<0 then
- if designsize then
- if designsize>tfm.factor then
- return (- scaledpoints/1000)*designsize
- else
- return (- scaledpoints/1000)*designsize*tfm.factor
- end
- else
- return (- scaledpoints/1000)*10*tfm.factor
- end
- else
- return scaledpoints
- end
-end
-function tfm.get_virtual_id(tfmdata)
- if not tfmdata.fonts then
- tfmdata.type="virtual"
- tfmdata.fonts={ { id=0 } }
- return 1
- else
- tfmdata.fonts[#tfmdata.fonts+1]={ id=0 }
- return #tfmdata.fonts
- end
-end
-function tfm.check_virtual_id(tfmdata,id)
- if tfmdata and tfmdata.type=="virtual" then
- if not tfmdata.fonts or #tfmdata.fonts==0 then
- tfmdata.type,tfmdata.fonts="real",nil
- else
- local vfonts=tfmdata.fonts
- for f=1,#vfonts do
- local fnt=vfonts[f]
- if fnt.id and fnt.id==0 then
- fnt.id=id
- end
- end
- end
- end
-end
-fonts.trace_scaling=false
-local charactercache={}
-function tfm.calculate_scale(tfmtable,scaledpoints,relativeid)
- if scaledpoints<0 then
- scaledpoints=(- scaledpoints/1000)*tfmtable.designsize
- end
- local units=tfmtable.units or 1000
- local delta=scaledpoints/units
- return scaledpoints,delta,units
-end
-function tfm.do_scale(tfmtable,scaledpoints,relativeid)
- local t={}
- local scaledpoints,delta,units=tfm.calculate_scale(tfmtable,scaledpoints,relativeid)
- t.units_per_em=units or 1000
- local hdelta,vdelta=delta,delta
- for k,v in next,tfmtable do
- if type(v)=="table" then
- else
- 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
- 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)
- local nodemode=tfmtable.mode=="node"
- local hasquality=tfmtable.auto_expand or tfmtable.auto_protrude
- local hasitalic=tfmtable.has_italic
- t.parameters={}
- t.characters={}
- t.MathConstants={}
- local descriptions=tfmtable.descriptions or {}
- t.unicodes=tfmtable.unicodes
- t.indices=tfmtable.indices
- t.marks=tfmtable.marks
-t.goodies=tfmtable.goodies
-t.colorscheme=tfmtable.colorscheme
- t.descriptions=descriptions
- if tfmtable.fonts then
- t.fonts=table.fastcopy(tfmtable.fonts)
- end
- local tp=t.parameters
- local mp=t.math_parameters
- local tfmp=tfmtable.parameters
- tp.slant=(tfmp.slant or tfmp[1] or 0)
- 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
- local nameneeded=not tfmtable.shared.otfdata
- local changed=tfmtable.changed or {}
- local ischanged=changed and next(changed)
- local indices=tfmtable.indices
- local luatex=tfmtable.luatex
- local tounicode=luatex and luatex.tounicode
- local defaultwidth=luatex and luatex.defaultwidth or 0
- local defaultheight=luatex and luatex.defaultheight or 0
- local defaultdepth=luatex and luatex.defaultdepth or 0
- local scaledwidth=defaultwidth*hdelta
- local scaledheight=defaultheight*vdelta
- local scaleddepth=defaultdepth*vdelta
- local stackmath=tfmtable.ignore_stack_math~=true
- local private=fonts.private
- local sharedkerns={}
- for k,v in next,characters do
- local chr,description,index
- if ischanged then
- local c=changed[k]
- if c then
- description=descriptions[c] or v
- v=characters[c] or v
- index=(indices and indices[c]) or c
- else
- description=descriptions[k] or v
- index=(indices and indices[k]) or k
- end
- else
- description=descriptions[k] or v
- index=(indices and indices[k]) or k
- end
- local width=description.width
- local height=description.height
- local depth=description.depth
- if width then width=hdelta*width else width=scaledwidth end
- if height then height=vdelta*height else height=scaledheight end
- if depth and depth~=0 then
- depth=delta*depth
- if nameneeded then
- chr={
- name=description.name,
- index=index,
- height=height,
- depth=depth,
- width=width,
- }
- else
- chr={
- index=index,
- height=height,
- depth=depth,
- width=width,
- }
- end
- else
- if nameneeded then
- chr={
- name=description.name,
- index=index,
- height=height,
- width=width,
- }
- else
- chr={
- index=index,
- height=height,
- width=width,
- }
- end
- end
- if tounicode then
- local tu=tounicode[index]
- if tu then
- chr.tounicode=tu
- end
- end
- if hasquality then
- local ve=v.expansion_factor
- if ve then
- chr.expansion_factor=ve*1000
- end
- local vl=v.left_protruding
- if vl then
- chr.left_protruding=protrusionfactor*width*vl
- end
- local vr=v.right_protruding
- if vr then
- chr.right_protruding=protrusionfactor*width*vr
- end
- end
- if hasitalic then
- local vi=description.italic or v.italic
- if vi and vi~=0 then
- chr.italic=vi*hdelta
- end
- end
- if hasmath then
- local vn=v.next
- if vn then
- chr.next=vn
- else
- local vv=v.vert_variants
- if vv then
- local t={}
- for i=1,#vv do
- local vvi=vv[i]
- t[i]={
- ["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
- else
- local hv=v.horiz_variants
- if hv then
- local t={}
- for i=1,#hv do
- local hvi=hv[i]
- t[i]={
- ["start"]=(hvi["start"] or 0)*hdelta,
- ["end"]=(hvi["end"] or 0)*hdelta,
- ["advance"]=(hvi["advance"] or 0)*hdelta,
- ["extender"]=hvi["extender"],
- ["glyph"]=hvi["glyph"],
- }
- end
- chr.horiz_variants=t
- end
- end
- end
- local vt=description.top_accent
- if vt then
- chr.top_accent=vdelta*vt
- end
- if stackmath then
- local mk=v.mathkerns
- if mk then
- local kerns={}
- local v=mk.top_right if v then local k={} for i=1,#v do local vi=v[i]
- 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=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=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=vdelta*vi.height,kern=vdelta*vi.kern }
- end kerns.bottom_right=k end
- chr.mathkern=kerns
- end
- end
- end
- if not nodemode then
- local vk=v.kerns
- if vk then
- 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
- if true then
- chr.ligatures=vl
- else
- local tt={}
- for i,l in next,vl do
- tt[i]=l
- end
- chr.ligatures=tt
- end
- end
- end
- if isvirtual then
- local vc=v.commands
- if vc then
- local ok=false
- for i=1,#vc do
- local key=vc[i][1]
- if key=="right" or key=="down" then
- ok=true
- break
- end
- end
- if ok then
- local tt={}
- for i=1,#vc do
- local ivc=vc[i]
- local key=ivc[1]
- 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
- tt[#tt+1]=ivc
- end
- end
- chr.commands=tt
- else
- chr.commands=vc
- end
- end
- end
- tc[k]=chr
- end
- t.size=scaledpoints
- t.factor=delta
- t.hfactor=hdelta
- t.vfactor=vdelta
- if t.fonts then
- t.fonts=table.fastcopy(t.fonts)
- end
- if hasmath then
- local ma=tfm.mathactions
- for i=1,#ma do
- ma[i](t,tfmtable,delta,hdelta,vdelta)
- end
- end
- local tpx=tp.x_height
- if hasmath then
- if not tp[13] then tp[13]=.86*tpx end
- if not tp[14] then tp[14]=.86*tpx end
- if not tp[15] then tp[15]=.86*tpx end
- if not tp[16] then tp[16]=.48*tpx end
- if not tp[17] then tp[17]=.48*tpx end
- if not tp[22] then tp[22]=0 end
- if t.MathConstants then t.MathConstants.AccentBaseHeight=nil end
- end
- t.tounicode=1
- t.cidinfo=tfmtable.cidinfo
- if hasmath then
- if trace_defining then
- logs.report("define font","math enabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename")
- end
- else
- if trace_defining then
- logs.report("define font","math disabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename")
- end
- t.nomath,t.MathConstants=true,nil
- end
- if not t.psname then
- t.psname=t.fontname or (t.fullname and fonts.names.cleanname(t.fullname))
- end
- if trace_defining then
- logs.report("define font","used for accesing subfont: '%s'",t.psname or "nopsname")
- logs.report("define font","used for subsetting: '%s'",t.fontname or "nofontname")
- end
- return t,delta
-end
-tfm.auto_cleanup=true
-local lastfont=nil
-function tfm.cleanup_table(tfmdata)
- if tfm.auto_cleanup then
- if tfmdata.type=='virtual' or tfmdata.virtualized then
- for k,v in next,tfmdata.characters do
- if v.commands then v.commands=nil end
- end
- else
- end
- end
-end
-function tfm.cleanup(tfmdata)
-end
-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)
- t.shared=tfmtable.shared or {}
- t.unique=table.fastcopy(tfmtable.unique or {})
- tfm.cleanup(t)
- return t
-end
-fonts.analyzers=fonts.analyzers or {}
-fonts.analyzers.aux=fonts.analyzers.aux or {}
-fonts.analyzers.methods=fonts.analyzers.methods or {}
-fonts.analyzers.initializers=fonts.analyzers.initializers or {}
-local state=attributes.private('state')
-function fonts.analyzers.aux.setstate(head,font)
- local tfmdata=fontdata[font]
- local characters=tfmdata.characters
- local descriptions=tfmdata.descriptions
- local first,last,current,n,done=nil,nil,head,0,false
- while current do
- 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
- done=true
- set_attribute(current,state,5)
- elseif n==0 then
- first,last,n=current,current,1
- set_attribute(current,state,1)
- else
- last,n=current,n+1
- set_attribute(current,state,2)
- end
- else
- if first and first==last then
- set_attribute(last,state,4)
- elseif last then
- set_attribute(last,state,3)
- end
- first,last,n=nil,nil,0
- end
- elseif id==disc then
- set_attribute(current,state,2)
- last=current
- else
- if first and first==last then
- set_attribute(last,state,4)
- elseif last then
- set_attribute(last,state,3)
- end
- first,last,n=nil,nil,0
- end
- current=current.next
- end
- if first and first==last then
- set_attribute(last,state,4)
- elseif last then
- set_attribute(last,state,3)
- end
- return head,done
-end
-function tfm.replacements(tfm,value)
- tfm.characters[0x0027]=tfm.characters[0x2019]
-end
-function tfm.checked_filename(metadata,whatever)
- local foundfilename=metadata.foundfilename
- if not foundfilename then
- local askedfilename=metadata.filename or ""
- if askedfilename~="" then
- foundfilename=resolvers.findbinfile(askedfilename,"") or ""
- if foundfilename=="" then
- logs.report("fonts","source file '%s' is not found",askedfilename)
- foundfilename=resolvers.findbinfile(file.basename(askedfilename),"") or ""
- if foundfilename~="" then
- logs.report("fonts","using source file '%s' (cache mismatch)",foundfilename)
- end
- end
- elseif whatever then
- logs.report("fonts","no source file for '%s'",whatever)
- foundfilename=""
- end
- metadata.foundfilename=foundfilename
- end
- return foundfilename
-end
-statistics.register("fonts load time",function()
- return statistics.elapsedseconds(fonts)
-end)
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-cid']={
- version=1.001,
- comment="companion to font-otf.lua (cidmaps)",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-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)
-fonts=fonts or {}
-fonts.cid=fonts.cid or {}
-fonts.cid.map=fonts.cid.map or {}
-fonts.cid.max=fonts.cid.max or 10
-local number=lpeg.C(lpeg.R("09","af","AF")^1)
-local space=lpeg.S(" \n\r\t")
-local spaces=space^0
-local period=lpeg.P(".")
-local periods=period*period
-local name=lpeg.P("/")*lpeg.C((1-space)^1)
-local unicodes,names={},{}
-local function do_one(a,b)
- unicodes[tonumber(a)]=tonumber(b,16)
-end
-local function do_range(a,b,c)
- c=tonumber(c,16)
- for i=tonumber(a),tonumber(b) do
- unicodes[i]=c
- c=c+1
- end
-end
-local function do_name(a,b)
- names[tonumber(a)]=b
-end
-local grammar=lpeg.P { "start",
- start=number*spaces*number*lpeg.V("series"),
- series=(spaces*(lpeg.V("one")+lpeg.V("range")+lpeg.V("named")) )^1,
- one=(number*spaces*number)/do_one,
- range=(number*periods*number*spaces*number)/do_range,
- named=(number*spaces*name)/do_name
-}
-function fonts.cid.load(filename)
- local data=io.loaddata(filename)
- if data then
- unicodes,names={},{}
- lpegmatch(grammar,data)
- local supplement,registry,ordering=match(filename,"^(.-)%-(.-)%-()%.(.-)$")
- return {
- supplement=supplement,
- registry=registry,
- ordering=ordering,
- filename=filename,
- unicodes=unicodes,
- names=names
- }
- else
- return nil
- end
-end
-local template="%s-%s-%s.cidmap"
-local function locate(registry,ordering,supplement)
- local filename=format(template,registry,ordering,supplement)
- local hashname=lower(filename)
- local cidmap=fonts.cid.map[hashname]
- if not cidmap then
- if trace_loading then
- logs.report("load otf","checking cidmap, registry: %s, ordering: %s, supplement: %s, filename: %s",registry,ordering,supplement,filename)
- end
- local fullname=resolvers.find_file(filename,'cid') or ""
- if fullname~="" then
- cidmap=fonts.cid.load(fullname)
- if cidmap then
- if trace_loading then
- logs.report("load otf","using cidmap file %s",filename)
- end
- fonts.cid.map[hashname]=cidmap
- cidmap.usedname=file.basename(filename)
- return cidmap
- end
- end
- end
- return cidmap
-end
-function fonts.cid.getmap(registry,ordering,supplement)
- local supplement=tonumber(supplement)
- if trace_loading then
- logs.report("load otf","needed cidmap, registry: %s, ordering: %s, supplement: %s",registry,ordering,supplement)
- end
- local cidmap=locate(registry,ordering,supplement)
- if not cidmap then
- local cidnum=nil
- if supplement<fonts.cid.max then
- for supplement=supplement+1,fonts.cid.max do
- local c=locate(registry,ordering,supplement)
- if c then
- cidmap,cidnum=c,supplement
- break
- end
- end
- end
- if not cidmap and supplement>0 then
- for supplement=supplement-1,0,-1 do
- local c=locate(registry,ordering,supplement)
- if c then
- cidmap,cidnum=c,supplement
- break
- end
- end
- end
- if cidmap and cidnum>0 then
- for s=0,cidnum-1 do
- filename=format(template,registry,ordering,s)
- if not fonts.cid.map[filename] then
- fonts.cid.map[filename]=cidmap
- end
- end
- end
- end
- return cidmap
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-otf']={
- version=1.001,
- comment="companion to font-otf.lua (tables)",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local type,next,tonumber,tostring=type,next,tonumber,tostring
-local gsub,lower=string.gsub,string.lower
-fonts=fonts or {}
-fonts.otf=fonts.otf or {}
-local otf=fonts.otf
-otf.tables=otf.tables or {}
-otf.meanings=otf.meanings or {}
-otf.tables.scripts={
- ['dflt']='Default',
- ['arab']='Arabic',
- ['armn']='Armenian',
- ['bali']='Balinese',
- ['beng']='Bengali',
- ['bopo']='Bopomofo',
- ['brai']='Braille',
- ['bugi']='Buginese',
- ['buhd']='Buhid',
- ['byzm']='Byzantine Music',
- ['cans']='Canadian Syllabics',
- ['cher']='Cherokee',
- ['copt']='Coptic',
- ['cprt']='Cypriot Syllabary',
- ['cyrl']='Cyrillic',
- ['deva']='Devanagari',
- ['dsrt']='Deseret',
- ['ethi']='Ethiopic',
- ['geor']='Georgian',
- ['glag']='Glagolitic',
- ['goth']='Gothic',
- ['grek']='Greek',
- ['gujr']='Gujarati',
- ['guru']='Gurmukhi',
- ['hang']='Hangul',
- ['hani']='CJK Ideographic',
- ['hano']='Hanunoo',
- ['hebr']='Hebrew',
- ['ital']='Old Italic',
- ['jamo']='Hangul Jamo',
- ['java']='Javanese',
- ['kana']='Hiragana and Katakana',
- ['khar']='Kharosthi',
- ['khmr']='Khmer',
- ['knda']='Kannada',
- ['lao' ]='Lao',
- ['latn']='Latin',
- ['limb']='Limbu',
- ['linb']='Linear B',
- ['math']='Mathematical Alphanumeric Symbols',
- ['mlym']='Malayalam',
- ['mong']='Mongolian',
- ['musc']='Musical Symbols',
- ['mymr']='Myanmar',
- ['nko' ]="N'ko",
- ['ogam']='Ogham',
- ['orya']='Oriya',
- ['osma']='Osmanya',
- ['phag']='Phags-pa',
- ['phnx']='Phoenician',
- ['runr']='Runic',
- ['shaw']='Shavian',
- ['sinh']='Sinhala',
- ['sylo']='Syloti Nagri',
- ['syrc']='Syriac',
- ['tagb']='Tagbanwa',
- ['tale']='Tai Le',
- ['talu']='Tai Lu',
- ['taml']='Tamil',
- ['telu']='Telugu',
- ['tfng']='Tifinagh',
- ['tglg']='Tagalog',
- ['thaa']='Thaana',
- ['thai']='Thai',
- ['tibt']='Tibetan',
- ['ugar']='Ugaritic Cuneiform',
- ['xpeo']='Old Persian Cuneiform',
- ['xsux']='Sumero-Akkadian Cuneiform',
- ['yi' ]='Yi',
-}
-otf.tables.languages={
- ['dflt']='Default',
- ['aba']='Abaza',
- ['abk']='Abkhazian',
- ['ady']='Adyghe',
- ['afk']='Afrikaans',
- ['afr']='Afar',
- ['agw']='Agaw',
- ['als']='Alsatian',
- ['alt']='Altai',
- ['amh']='Amharic',
- ['ara']='Arabic',
- ['ari']='Aari',
- ['ark']='Arakanese',
- ['asm']='Assamese',
- ['ath']='Athapaskan',
- ['avr']='Avar',
- ['awa']='Awadhi',
- ['aym']='Aymara',
- ['aze']='Azeri',
- ['bad']='Badaga',
- ['bag']='Baghelkhandi',
- ['bal']='Balkar',
- ['bau']='Baule',
- ['bbr']='Berber',
- ['bch']='Bench',
- ['bcr']='Bible Cree',
- ['bel']='Belarussian',
- ['bem']='Bemba',
- ['ben']='Bengali',
- ['bgr']='Bulgarian',
- ['bhi']='Bhili',
- ['bho']='Bhojpuri',
- ['bik']='Bikol',
- ['bil']='Bilen',
- ['bkf']='Blackfoot',
- ['bli']='Balochi',
- ['bln']='Balante',
- ['blt']='Balti',
- ['bmb']='Bambara',
- ['bml']='Bamileke',
- ['bos']='Bosnian',
- ['bre']='Breton',
- ['brh']='Brahui',
- ['bri']='Braj Bhasha',
- ['brm']='Burmese',
- ['bsh']='Bashkir',
- ['bti']='Beti',
- ['cat']='Catalan',
- ['ceb']='Cebuano',
- ['che']='Chechen',
- ['chg']='Chaha Gurage',
- ['chh']='Chattisgarhi',
- ['chi']='Chichewa',
- ['chk']='Chukchi',
- ['chp']='Chipewyan',
- ['chr']='Cherokee',
- ['chu']='Chuvash',
- ['cmr']='Comorian',
- ['cop']='Coptic',
- ['cos']='Corsican',
- ['cre']='Cree',
- ['crr']='Carrier',
- ['crt']='Crimean Tatar',
- ['csl']='Church Slavonic',
- ['csy']='Czech',
- ['dan']='Danish',
- ['dar']='Dargwa',
- ['dcr']='Woods Cree',
- ['deu']='German',
- ['dgr']='Dogri',
- ['div']='Divehi',
- ['djr']='Djerma',
- ['dng']='Dangme',
- ['dnk']='Dinka',
- ['dri']='Dari',
- ['dun']='Dungan',
- ['dzn']='Dzongkha',
- ['ebi']='Ebira',
- ['ecr']='Eastern Cree',
- ['edo']='Edo',
- ['efi']='Efik',
- ['ell']='Greek',
- ['eng']='English',
- ['erz']='Erzya',
- ['esp']='Spanish',
- ['eti']='Estonian',
- ['euq']='Basque',
- ['evk']='Evenki',
- ['evn']='Even',
- ['ewe']='Ewe',
- ['fan']='French Antillean',
- ['far']='Farsi',
- ['fin']='Finnish',
- ['fji']='Fijian',
- ['fle']='Flemish',
- ['fne']='Forest Nenets',
- ['fon']='Fon',
- ['fos']='Faroese',
- ['fra']='French',
- ['fri']='Frisian',
- ['frl']='Friulian',
- ['fta']='Futa',
- ['ful']='Fulani',
- ['gad']='Ga',
- ['gae']='Gaelic',
- ['gag']='Gagauz',
- ['gal']='Galician',
- ['gar']='Garshuni',
- ['gaw']='Garhwali',
- ['gez']="Ge'ez",
- ['gil']='Gilyak',
- ['gmz']='Gumuz',
- ['gon']='Gondi',
- ['grn']='Greenlandic',
- ['gro']='Garo',
- ['gua']='Guarani',
- ['guj']='Gujarati',
- ['hai']='Haitian',
- ['hal']='Halam',
- ['har']='Harauti',
- ['hau']='Hausa',
- ['haw']='Hawaiin',
- ['hbn']='Hammer-Banna',
- ['hil']='Hiligaynon',
- ['hin']='Hindi',
- ['hma']='High Mari',
- ['hnd']='Hindko',
- ['ho']='Ho',
- ['hri']='Harari',
- ['hrv']='Croatian',
- ['hun']='Hungarian',
- ['hye']='Armenian',
- ['ibo']='Igbo',
- ['ijo']='Ijo',
- ['ilo']='Ilokano',
- ['ind']='Indonesian',
- ['ing']='Ingush',
- ['inu']='Inuktitut',
- ['iri']='Irish',
- ['irt']='Irish Traditional',
- ['isl']='Icelandic',
- ['ism']='Inari Sami',
- ['ita']='Italian',
- ['iwr']='Hebrew',
- ['jan']='Japanese',
- ['jav']='Javanese',
- ['jii']='Yiddish',
- ['jud']='Judezmo',
- ['jul']='Jula',
- ['kab']='Kabardian',
- ['kac']='Kachchi',
- ['kal']='Kalenjin',
- ['kan']='Kannada',
- ['kar']='Karachay',
- ['kat']='Georgian',
- ['kaz']='Kazakh',
- ['keb']='Kebena',
- ['kge']='Khutsuri Georgian',
- ['kha']='Khakass',
- ['khk']='Khanty-Kazim',
- ['khm']='Khmer',
- ['khs']='Khanty-Shurishkar',
- ['khv']='Khanty-Vakhi',
- ['khw']='Khowar',
- ['kik']='Kikuyu',
- ['kir']='Kirghiz',
- ['kis']='Kisii',
- ['kkn']='Kokni',
- ['klm']='Kalmyk',
- ['kmb']='Kamba',
- ['kmn']='Kumaoni',
- ['kmo']='Komo',
- ['kms']='Komso',
- ['knr']='Kanuri',
- ['kod']='Kodagu',
- ['koh']='Korean Old Hangul',
- ['kok']='Konkani',
- ['kon']='Kikongo',
- ['kop']='Komi-Permyak',
- ['kor']='Korean',
- ['koz']='Komi-Zyrian',
- ['kpl']='Kpelle',
- ['kri']='Krio',
- ['krk']='Karakalpak',
- ['krl']='Karelian',
- ['krm']='Karaim',
- ['krn']='Karen',
- ['krt']='Koorete',
- ['ksh']='Kashmiri',
- ['ksi']='Khasi',
- ['ksm']='Kildin Sami',
- ['kui']='Kui',
- ['kul']='Kulvi',
- ['kum']='Kumyk',
- ['kur']='Kurdish',
- ['kuu']='Kurukh',
- ['kuy']='Kuy',
- ['kyk']='Koryak',
- ['lad']='Ladin',
- ['lah']='Lahuli',
- ['lak']='Lak',
- ['lam']='Lambani',
- ['lao']='Lao',
- ['lat']='Latin',
- ['laz']='Laz',
- ['lcr']='L-Cree',
- ['ldk']='Ladakhi',
- ['lez']='Lezgi',
- ['lin']='Lingala',
- ['lma']='Low Mari',
- ['lmb']='Limbu',
- ['lmw']='Lomwe',
- ['lsb']='Lower Sorbian',
- ['lsm']='Lule Sami',
- ['lth']='Lithuanian',
- ['ltz']='Luxembourgish',
- ['lub']='Luba',
- ['lug']='Luganda',
- ['luh']='Luhya',
- ['luo']='Luo',
- ['lvi']='Latvian',
- ['maj']='Majang',
- ['mak']='Makua',
- ['mal']='Malayalam Traditional',
- ['man']='Mansi',
- ['map']='Mapudungun',
- ['mar']='Marathi',
- ['maw']='Marwari',
- ['mbn']='Mbundu',
- ['mch']='Manchu',
- ['mcr']='Moose Cree',
- ['mde']='Mende',
- ['men']="Me'en",
- ['miz']='Mizo',
- ['mkd']='Macedonian',
- ['mle']='Male',
- ['mlg']='Malagasy',
- ['mln']='Malinke',
- ['mlr']='Malayalam Reformed',
- ['mly']='Malay',
- ['mnd']='Mandinka',
- ['mng']='Mongolian',
- ['mni']='Manipuri',
- ['mnk']='Maninka',
- ['mnx']='Manx Gaelic',
- ['moh']='Mohawk',
- ['mok']='Moksha',
- ['mol']='Moldavian',
- ['mon']='Mon',
- ['mor']='Moroccan',
- ['mri']='Maori',
- ['mth']='Maithili',
- ['mts']='Maltese',
- ['mun']='Mundari',
- ['nag']='Naga-Assamese',
- ['nan']='Nanai',
- ['nas']='Naskapi',
- ['ncr']='N-Cree',
- ['ndb']='Ndebele',
- ['ndg']='Ndonga',
- ['nep']='Nepali',
- ['new']='Newari',
- ['ngr']='Nagari',
- ['nhc']='Norway House Cree',
- ['nis']='Nisi',
- ['niu']='Niuean',
- ['nkl']='Nkole',
- ['nko']="N'ko",
- ['nld']='Dutch',
- ['nog']='Nogai',
- ['nor']='Norwegian',
- ['nsm']='Northern Sami',
- ['nta']='Northern Tai',
- ['nto']='Esperanto',
- ['nyn']='Nynorsk',
- ['oci']='Occitan',
- ['ocr']='Oji-Cree',
- ['ojb']='Ojibway',
- ['ori']='Oriya',
- ['oro']='Oromo',
- ['oss']='Ossetian',
- ['paa']='Palestinian Aramaic',
- ['pal']='Pali',
- ['pan']='Punjabi',
- ['pap']='Palpa',
- ['pas']='Pashto',
- ['pgr']='Polytonic Greek',
- ['pil']='Pilipino',
- ['plg']='Palaung',
- ['plk']='Polish',
- ['pro']='Provencal',
- ['ptg']='Portuguese',
- ['qin']='Chin',
- ['raj']='Rajasthani',
- ['rbu']='Russian Buriat',
- ['rcr']='R-Cree',
- ['ria']='Riang',
- ['rms']='Rhaeto-Romanic',
- ['rom']='Romanian',
- ['roy']='Romany',
- ['rsy']='Rusyn',
- ['rua']='Ruanda',
- ['rus']='Russian',
- ['sad']='Sadri',
- ['san']='Sanskrit',
- ['sat']='Santali',
- ['say']='Sayisi',
- ['sek']='Sekota',
- ['sel']='Selkup',
- ['sgo']='Sango',
- ['shn']='Shan',
- ['sib']='Sibe',
- ['sid']='Sidamo',
- ['sig']='Silte Gurage',
- ['sks']='Skolt Sami',
- ['sky']='Slovak',
- ['sla']='Slavey',
- ['slv']='Slovenian',
- ['sml']='Somali',
- ['smo']='Samoan',
- ['sna']='Sena',
- ['snd']='Sindhi',
- ['snh']='Sinhalese',
- ['snk']='Soninke',
- ['sog']='Sodo Gurage',
- ['sot']='Sotho',
- ['sqi']='Albanian',
- ['srb']='Serbian',
- ['srk']='Saraiki',
- ['srr']='Serer',
- ['ssl']='South Slavey',
- ['ssm']='Southern Sami',
- ['sur']='Suri',
- ['sva']='Svan',
- ['sve']='Swedish',
- ['swa']='Swadaya Aramaic',
- ['swk']='Swahili',
- ['swz']='Swazi',
- ['sxt']='Sutu',
- ['syr']='Syriac',
- ['tab']='Tabasaran',
- ['taj']='Tajiki',
- ['tam']='Tamil',
- ['tat']='Tatar',
- ['tcr']='TH-Cree',
- ['tel']='Telugu',
- ['tgn']='Tongan',
- ['tgr']='Tigre',
- ['tgy']='Tigrinya',
- ['tha']='Thai',
- ['tht']='Tahitian',
- ['tib']='Tibetan',
- ['tkm']='Turkmen',
- ['tmn']='Temne',
- ['tna']='Tswana',
- ['tne']='Tundra Nenets',
- ['tng']='Tonga',
- ['tod']='Todo',
- ['trk']='Turkish',
- ['tsg']='Tsonga',
- ['tua']='Turoyo Aramaic',
- ['tul']='Tulu',
- ['tuv']='Tuvin',
- ['twi']='Twi',
- ['udm']='Udmurt',
- ['ukr']='Ukrainian',
- ['urd']='Urdu',
- ['usb']='Upper Sorbian',
- ['uyg']='Uyghur',
- ['uzb']='Uzbek',
- ['ven']='Venda',
- ['vit']='Vietnamese',
- ['wa' ]='Wa',
- ['wag']='Wagdi',
- ['wcr']='West-Cree',
- ['wel']='Welsh',
- ['wlf']='Wolof',
- ['xbd']='Tai Lue',
- ['xhs']='Xhosa',
- ['yak']='Yakut',
- ['yba']='Yoruba',
- ['ycr']='Y-Cree',
- ['yic']='Yi Classic',
- ['yim']='Yi Modern',
- ['zhh']='Chinese Hong Kong',
- ['zhp']='Chinese Phonetic',
- ['zhs']='Chinese Simplified',
- ['zht']='Chinese Traditional',
- ['znd']='Zande',
- ['zul']='Zulu'
-}
-otf.tables.features={
- ['aalt']='Access All Alternates',
- ['abvf']='Above-Base Forms',
- ['abvm']='Above-Base Mark Positioning',
- ['abvs']='Above-Base Substitutions',
- ['afrc']='Alternative Fractions',
- ['akhn']='Akhands',
- ['blwf']='Below-Base Forms',
- ['blwm']='Below-Base Mark Positioning',
- ['blws']='Below-Base Substitutions',
- ['c2pc']='Petite Capitals From Capitals',
- ['c2sc']='Small Capitals From Capitals',
- ['calt']='Contextual Alternates',
- ['case']='Case-Sensitive Forms',
- ['ccmp']='Glyph Composition/Decomposition',
- ['cjct']='Conjunct Forms',
- ['clig']='Contextual Ligatures',
- ['cpsp']='Capital Spacing',
- ['cswh']='Contextual Swash',
- ['curs']='Cursive Positioning',
- ['dflt']='Default Processing',
- ['dist']='Distances',
- ['dlig']='Discretionary Ligatures',
- ['dnom']='Denominators',
- ['dtls']='Dotless Forms',
- ['expt']='Expert Forms',
- ['falt']='Final glyph Alternates',
- ['fin2']='Terminal Forms #2',
- ['fin3']='Terminal Forms #3',
- ['fina']='Terminal Forms',
- ['flac']='Flattened Accents Over Capitals',
- ['frac']='Fractions',
- ['fwid']='Full Width',
- ['half']='Half Forms',
- ['haln']='Halant Forms',
- ['halt']='Alternate Half Width',
- ['hist']='Historical Forms',
- ['hkna']='Horizontal Kana Alternates',
- ['hlig']='Historical Ligatures',
- ['hngl']='Hangul',
- ['hojo']='Hojo Kanji Forms',
- ['hwid']='Half Width',
- ['init']='Initial Forms',
- ['isol']='Isolated Forms',
- ['ital']='Italics',
- ['jalt']='Justification Alternatives',
- ['jp04']='JIS2004 Forms',
- ['jp78']='JIS78 Forms',
- ['jp83']='JIS83 Forms',
- ['jp90']='JIS90 Forms',
- ['kern']='Kerning',
- ['lfbd']='Left Bounds',
- ['liga']='Standard Ligatures',
- ['ljmo']='Leading Jamo Forms',
- ['lnum']='Lining Figures',
- ['locl']='Localized Forms',
- ['mark']='Mark Positioning',
- ['med2']='Medial Forms #2',
- ['medi']='Medial Forms',
- ['mgrk']='Mathematical Greek',
- ['mkmk']='Mark to Mark Positioning',
- ['mset']='Mark Positioning via Substitution',
- ['nalt']='Alternate Annotation Forms',
- ['nlck']='NLC Kanji Forms',
- ['nukt']='Nukta Forms',
- ['numr']='Numerators',
- ['onum']='Old Style Figures',
- ['opbd']='Optical Bounds',
- ['ordn']='Ordinals',
- ['ornm']='Ornaments',
- ['palt']='Proportional Alternate Width',
- ['pcap']='Petite Capitals',
- ['pnum']='Proportional Figures',
- ['pref']='Pre-base Forms',
- ['pres']='Pre-base Substitutions',
- ['pstf']='Post-base Forms',
- ['psts']='Post-base Substitutions',
- ['pwid']='Proportional Widths',
- ['qwid']='Quarter Widths',
- ['rand']='Randomize',
- ['rkrf']='Rakar Forms',
- ['rlig']='Required Ligatures',
- ['rphf']='Reph Form',
- ['rtbd']='Right Bounds',
- ['rtla']='Right-To-Left Alternates',
- ['rtlm']='Right To Left Math',
- ['ruby']='Ruby Notation Forms',
- ['salt']='Stylistic Alternates',
- ['sinf']='Scientific Inferiors',
- ['size']='Optical Size',
- ['smcp']='Small Capitals',
- ['smpl']='Simplified Forms',
- ['ss01']='Stylistic Set 1',
- ['ss02']='Stylistic Set 2',
- ['ss03']='Stylistic Set 3',
- ['ss04']='Stylistic Set 4',
- ['ss05']='Stylistic Set 5',
- ['ss06']='Stylistic Set 6',
- ['ss07']='Stylistic Set 7',
- ['ss08']='Stylistic Set 8',
- ['ss09']='Stylistic Set 9',
- ['ss10']='Stylistic Set 10',
- ['ss11']='Stylistic Set 11',
- ['ss12']='Stylistic Set 12',
- ['ss13']='Stylistic Set 13',
- ['ss14']='Stylistic Set 14',
- ['ss15']='Stylistic Set 15',
- ['ss16']='Stylistic Set 16',
- ['ss17']='Stylistic Set 17',
- ['ss18']='Stylistic Set 18',
- ['ss19']='Stylistic Set 19',
- ['ss20']='Stylistic Set 20',
- ['ssty']='Script Style',
- ['subs']='Subscript',
- ['sups']='Superscript',
- ['swsh']='Swash',
- ['titl']='Titling',
- ['tjmo']='Trailing Jamo Forms',
- ['tnam']='Traditional Name Forms',
- ['tnum']='Tabular Figures',
- ['trad']='Traditional Forms',
- ['twid']='Third Widths',
- ['unic']='Unicase',
- ['valt']='Alternate Vertical Metrics',
- ['vatu']='Vattu Variants',
- ['vert']='Vertical Writing',
- ['vhal']='Alternate Vertical Half Metrics',
- ['vjmo']='Vowel Jamo Forms',
- ['vkna']='Vertical Kana Alternates',
- ['vkrn']='Vertical Kerning',
- ['vpal']='Proportional Alternate Vertical Metrics',
- ['vrt2']='Vertical Rotation',
- ['zero']='Slashed Zero',
- ['trep']='Traditional TeX Replacements',
- ['tlig']='Traditional TeX Ligatures',
-}
-otf.tables.baselines={
- ['hang']='Hanging baseline',
- ['icfb']='Ideographic character face bottom edge baseline',
- ['icft']='Ideographic character face tope edige baseline',
- ['ideo']='Ideographic em-box bottom edge baseline',
- ['idtp']='Ideographic em-box top edge baseline',
- ['math']='Mathmatical centered baseline',
- ['romn']='Roman baseline'
-}
-function otf.tables.to_tag(id)
- return stringformat("%4s",lower(id))
-end
-local function resolve(tab,id)
- if tab and id then
- id=lower(id)
- return tab[id] or tab[gsub(id," ","")] or tab['dflt'] or ''
- else
- return "unknown"
- end
-end
-function otf.meanings.script(id)
- return resolve(otf.tables.scripts,id)
-end
-function otf.meanings.language(id)
- return resolve(otf.tables.languages,id)
-end
-function otf.meanings.feature(id)
- return resolve(otf.tables.features,id)
-end
-function otf.meanings.baseline(id)
- return resolve(otf.tables.baselines,id)
-end
-otf.tables.to_scripts=table.reverse_hash(otf.tables.scripts )
-otf.tables.to_languages=table.reverse_hash(otf.tables.languages)
-otf.tables.to_features=table.reverse_hash(otf.tables.features )
-local scripts=otf.tables.scripts
-local languages=otf.tables.languages
-local features=otf.tables.features
-local to_scripts=otf.tables.to_scripts
-local to_languages=otf.tables.to_languages
-local to_features=otf.tables.to_features
-for k,v in next,to_features do
- local stripped=gsub(k,"%-"," ")
- to_features[stripped]=v
- local stripped=gsub(k,"[^a-zA-Z0-9]","")
- to_features[stripped]=v
-end
-for k,v in next,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
- k=lower(k)
- if k=="language" or k=="lang" then
- v=gsub(lower(v),"[^a-z0-9%-]","")
- if not languages[v] then
- h.language=to_languages[v] or "dflt"
- else
- h.language=v
- end
- elseif k=="script" then
- v=gsub(lower(v),"[^a-z0-9%-]","")
- if not scripts[v] then
- h.script=to_scripts[v] or "dflt"
- else
- h.script=v
- end
- else
- if type(v)=="string" then
- local b=v:is_boolean()
- if type(b)=="nil" then
- v=tonumber(v) or lower(v)
- else
- v=b
- end
- end
- k=to_features[k] or k
- local c=checkers[k]
- h[k]=c and c(v) or v
- end
- end
- return h
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-map']={
- 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 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_unimapping=false trackers.register("otf.unimapping",function(v) trace_unimapping=v end)
-local ctxcatcodes=tex and tex.ctxcatcodes
-fonts=fonts or {}
-fonts.map=fonts.map or {}
-local function load_lum_table(filename)
- local lumname=file.replacesuffix(file.basename(filename),"lum")
- local lumfile=resolvers.find_file(lumname,"map") or ""
- if lumfile~="" and lfs.isfile(lumfile) then
- if trace_loading or trace_unimapping then
- logs.report("load otf","enhance: loading %s ",lumfile)
- end
- lumunic=dofile(lumfile)
- return lumunic,lumfile
- end
-end
-local hex=lpeg.R("AF","09")
-local hexfour=(hex*hex*hex*hex)/function(s) return tonumber(s,16) end
-local hexsix=(hex^1)/function(s) return tonumber(s,16) end
-local dec=(lpeg.R("09")^1)/tonumber
-local period=lpeg.P(".")
-local unicode=lpeg.P("uni")*(hexfour*(period+lpeg.P(-1))*lpeg.Cc(false)+lpeg.Ct(hexfour^1)*lpeg.Cc(true))
-local ucode=lpeg.P("u")*(hexsix*(period+lpeg.P(-1))*lpeg.Cc(false)+lpeg.Ct(hexsix^1)*lpeg.Cc(true))
-local index=lpeg.P("index")*dec*lpeg.Cc(false)
-local parser=unicode+ucode+index
-local parsers={}
-local function make_name_parser(str)
- if not str or str=="" then
- return parser
- else
- local p=parsers[str]
- if not p then
- p=lpeg.P(str)*period*dec*lpeg.Cc(false)
- parsers[str]=p
- end
- return p
- end
-end
-local function tounicode16(unicode)
- if unicode<0x10000 then
- return format("%04X",unicode)
- else
- return format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00)
- end
-end
-local function tounicode16sequence(unicodes)
- local t={}
- for l=1,#unicodes do
- local unicode=unicodes[l]
- if unicode<0x10000 then
- t[l]=format("%04X",unicode)
- else
- t[l]=format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00)
- end
- end
- return concat(t)
-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)
-fonts.map.add_to_unicode=function(data,filename)
- local unicodes=data.luatex and data.luatex.unicodes
- if not unicodes then
- return
- end
- unicodes['space']=unicodes['space'] or 32
- unicodes['hyphen']=unicodes['hyphen'] or 45
- unicodes['zwj']=unicodes['zwj'] or 0x200D
- unicodes['zwnj']=unicodes['zwnj'] or 0x200C
- local 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
- 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
- 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
- if (not unicode) and usedmap then
- local foundindex=lpegmatch(oparser,name)
- if foundindex then
- unicode=cidcodes[foundindex]
- if unicode then
- originals[index],tounicode[index],ns=unicode,tounicode16(unicode),ns+1
- else
- local reference=cidnames[foundindex]
- 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
- if not unicode then
- local split=lpegmatch(ligsplitter,name)
- local nplit=(split and #split) or 0
- if nplit==0 then
- 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
- originals[index],tounicode[index],nl,unicode=t,tounicode16sequence(t),nl+1,true
- end
- end
- end
- 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.sortedhash(data.glyphs) do
- local toun,name,unic=tounicode[index],glyph.name,glyph.unicode or -1
- 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
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-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,utfbyte=table.concat,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 abs=math.abs
-local getn=table.getn
-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)
-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_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
-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 {}
-otf.meanings=otf.meanings or {}
-otf.tables.features=otf.tables.features or {}
-otf.tables.languages=otf.tables.languages or {}
-otf.tables.scripts=otf.tables.scripts or {}
-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.653
-otf.pack=true
-otf.syncspace=true
-otf.notdef=false
-otf.cache=containers.define("fonts","otf",otf.version,true)
-otf.cleanup_aat=false
-local wildcard="*"
-local default="dflt"
-otf.tables.global_fields=table.tohash {
- "lookups",
- "glyphs",
- "subfonts",
- "luatex",
- "pfminfo",
- "cidinfo",
- "tables",
- "names",
- "unicodes",
- "names",
- "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",
- "macstyle",
- "modificationtime",
- "onlybitmaps",
- "origname",
- "os2_version",
- "pfminfo",
- "private",
- "serifcheck",
- "sfd_version",
- "strokedfont",
- "strokewidth",
- "subfonts",
- "table_version",
- "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",
-}
-local function load_featurefile(ff,featurefile)
- if featurefile then
- 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)
- 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 (%s)",name,filename)
- end
- enhancer(data,filename)
- end
-end
-local enhancers={
- "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",
- "flatten glyph lookups","flatten anchor tables","flatten feature tables",
- "simplify glyph lookups",
- "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",
- "check math parameters",
-}
-function otf.load(filename,format,sub,featurefile)
- local name=file.basename(file.removesuffix(filename))
- local attr=lfs.attributes(filename)
- local size,time=attr.size or 0,attr.modification or 0
- if featurefile then
- local fattr=lfs.attributes(featurefile)
- local fsize,ftime=fattr and fattr.size or 0,fattr and fattr.modification or 0
- name=name.."@"..file.removesuffix(file.basename(featurefile))..ftime..fsize
- 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)
- if not data or data.verbose~=fonts.verbose or data.size~=size or data.time~=time 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
- 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)
- 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)
- io.flush()
- end
- if otf.pack and not fonts.verbose then
- otf.enhance("pack",data,filename)
- end
- data.size=size
- data.time=time
- 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)
- 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
- if trace_defining then
- logs.report("define font","loading from cache: %s",hash)
- end
- otf.enhance("unpack",data,filename,false)
- otf.add_dimensions(data)
- if trace_sequences then
- otf.show_feature_order(data,filename)
- end
- end
- return data
-end
-function otf.add_dimensions(data)
- 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
- else
- d.height=ht
- end
- if dp==0 or dp<0 then
- 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
-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
- local us=unicodes[s]
- if type(us)=="table" then
- for u=1,#us do
- t[us[u]]=true
- end
- else
- t[us]=true
- end
- 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
- 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)
-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
-otf.enhancers["analyse unicodes"]=fonts.map.add_to_unicode
-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
- local t={}
- for f=1,#features do
- local feature=features[f]
- local hash={}
- 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={
- (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)
- if data.subfonts then
- if data.glyphs and next(data.glyphs) then
- logs.report("load otf","replacing existing glyph table due to subfonts")
- end
- 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
- else
- local unicode,name=unicodes[index],names[index]
- g.cidindex=n
- g.boundingbox=g.boundingbox
- 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
- 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
- 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)
- if data.subfonts then
- for _,subfont in next,data.subfonts do
- table.compact(subfont.glyphs)
- end
- end
-end
-otf.enhancers["reverse coverage"]=function(data,filename)
- 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
- 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
- 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 and #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
- 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 and #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
- 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
- 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
-otf.enhancers["reorganize kerns"]=function(data,filename)
- local glyphs,mapmap,unicodes=data.glyphs,data.luatex.indices,data.luatex.unicodes
- local mkdone=false
- local function do_it(lookup,first_unicode,kerns)
- local glyph=glyphs[mapmap[first_unicode]]
- if glyph then
- local mykerns=glyph.mykerns
- if not mykerns then
- mykerns={}
- glyph.mykerns=mykerns
- end
- local lookupkerns=mykerns[lookup]
- if not lookupkerns then
- lookupkerns={}
- mykerns[lookup]=lookupkerns
- end
- for second_unicode,kern in next,kerns do
- lookupkerns[second_unicode]=kern
- end
- elseif trace_loading then
- logs.report("load otf","no glyph data for U+%04X",first_unicode)
- end
- end
- for index,glyph in next,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
- 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
- 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
- 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
- if subtables then
- for s=1,#subtables do
- local subtable=subtables[s]
- local kernclass=subtable.kernclass
- if kernclass then
- local split={}
- for k=1,#kernclass do
- local kcl=kernclass[k]
- local firsts,seconds,offsets,lookups=kcl.firsts,kcl.seconds,kcl.offsets,kcl.lookup
- if type(lookups)~="table" then
- lookups={ lookups }
- end
- local maxfirsts,maxseconds=#firsts,#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
- for l=1,#lookups do
- local lookup=lookups[l]
- for fk=1,#firsts do
- local fv=firsts[fk]
- local splt=split[fv]
- if splt then
- local kerns,baseoffset={},(fk-1)*maxseconds
- for sk=2,maxseconds do
- local sv=seconds[sk]
- local splt=split[sv]
- if splt then
- local offset=offsets[baseoffset+sk]
- if offset then
- for i=1,#splt do
- local second_unicode=splt[i]
- if tonumber(second_unicode) then
- kerns[second_unicode]=offset
- else for s=1,#second_unicode do
- kerns[second_unicode[s]]=offset
- end end
- end
- end
- end
- end
- for i=1,#splt do
- local first_unicode=splt[i]
- if tonumber(first_unicode) then
- do_it(lookup,first_unicode,kerns)
- else for f=1,#first_unicode do
- do_it(lookup,first_unicode[f],kerns)
- end 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]
- if code then
- local vu=v.unicode
- if not vu then
- v.unicode=code
- elseif type(vu)=="table" then
- if vu[#vu]==code then
- 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
- 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
- local pfminfo=data.pfminfo
- metadata.isfixedpitch=metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose["proportion"]=="Monospaced")
- 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
- 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
- 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} }
- 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["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
- 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)
- 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
-fonts.otf.enhancers["enrich with features"]=function(data,filename)
-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 features and next(features) then
- local lists={
- fonts.triggers,
- fonts.processors,
- fonts.manipulators,
- }
- local mode=tfmdata.mode or fonts.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
- if not done[f] then
- if trace_features then
- logs.report("define otf","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.fullname or 'unknown')
- end
- fiotf[f](tfmdata,value)
- mode=tfmdata.mode or fonts.mode
- 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
- 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
- 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)
- if not tfmdata then
- local otfdata=otf.load(filename,format,sub,features and features.featurefile)
- if otfdata and next(otfdata) then
- otfdata.shared=otfdata.shared or {
- featuredata={},
- anchorhash={},
- initialized=false,
- }
- tfmdata=otf.copy_to_tfm(otfdata,cache_id)
- if tfmdata and next(tfmdata) then
- tfmdata.unique=tfmdata.unique or {}
- tfmdata.shared=tfmdata.shared or {}
- local shared=tfmdata.shared
- shared.otfdata=otfdata
- shared.features=features
- shared.dynamics={}
- shared.processes={}
- shared.set_dynamics=otf.set_dynamics
- 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
-fonts.formats.dfont="truetype"
-fonts.formats.ttc="truetype"
-fonts.formats.ttf="truetype"
-fonts.formats.otf="opentype"
-function otf.copy_to_tfm(data,cache_id)
- if data then
- local glyphs,pfminfo,metadata=data.glyphs or {},data.pfminfo or {},data.metadata or {}
- local luatex=data.luatex
- local unicodes=luatex.unicodes
- local indices=luatex.indices
- local characters,parameters,math_parameters,descriptions={},{},{},{}
- local designsize=metadata.designsize or metadata.design_size or 100
- if designsize==0 then
- designsize=100
- end
- local spaceunits,spacer=500,"space"
- for u,i in next,indices do
- characters[u]={}
- descriptions[u]=glyphs[i]
- end
- if metadata.math then
- for name,value in next,metadata.math do
- math_parameters[name]=value
- end
- for u,char in next,characters do
- local d=descriptions[u]
- local m=d.math
- if m then
- local variants,parts,c,uc=m.horiz_variants,m.horiz_parts,char,u
- if variants then
- for n in gmatch(variants,"[^ ]+") do
- local un=unicodes[n]
- if un and uc~=un then
- c.next=un
- c=characters[un]
- uc=un
- end
- end
- c.horiz_variants=parts
- elseif parts then
- c.horiz_variants=parts
- end
- local variants,parts,c,uc=m.vert_variants,m.vert_parts,char,u
- if variants then
- for n in gmatch(variants,"[^ ]+") do
- local un=unicodes[n]
- if un and uc~=un then
- c.next=un
- c=characters[un]
- uc=un
- end
- end
- c.vert_variants=parts
- elseif parts then
- c.vert_variants=parts
- end
- local italic_correction=m.vert_italic_correction
- if italic_correction then
- c.vert_italic_correction=italic_correction
- end
- local kerns=m.kerns
- if kerns then
- char.mathkerns=kerns
- end
- end
- end
- end
- local endash,emdash,space=0x20,0x2014,"space"
- if metadata.isfixedpitch then
- if descriptions[endash] then
- spaceunits,spacer=descriptions[endash].width,"space"
- end
- if not spaceunits and descriptions[emdash] then
- spaceunits,spacer=descriptions[emdash].width,"emdash"
- end
- if not spaceunits and metadata.charwidth then
- spaceunits,spacer=metadata.charwidth,"charwidth"
- end
- else
- if descriptions[endash] then
- spaceunits,spacer=descriptions[endash].width,"space"
- end
- if not spaceunits and descriptions[emdash] then
- spaceunits,spacer=descriptions[emdash].width/2,"emdash/2"
- end
- if not spaceunits and metadata.charwidth then
- spaceunits,spacer=metadata.charwidth,"charwidth"
- end
- end
- spaceunits=tonumber(spaceunits) or tfm.units/2
- local filename=fonts.tfm.checked_filename(luatex)
- local fontname=metadata.fontname
- local fullname=metadata.fullname or fontname
- local cidinfo=data.cidinfo
- local units=metadata.units_per_em or 1000
- cidinfo.registry=cidinfo and cidinfo.registry or ""
- parameters.slant=0
- parameters.space=spaceunits
- parameters.space_stretch=units/2
- parameters.space_shrink=1*units/3
- parameters.x_height=2*units/5
- parameters.quad=units
- if spaceunits<2*units/5 then
- end
- local italicangle=metadata.italicangle
- if italicangle then
- 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
- if pfminfo.os2_xheight and pfminfo.os2_xheight>0 then
- parameters.x_height=pfminfo.os2_xheight
- else
- local x=0x78
- if x then
- local x=descriptions[x]
- if x then
- parameters.x_height=x.height
- end
- end
- end
- return {
- characters=characters,
- parameters=parameters,
- math_parameters=math_parameters,
- descriptions=descriptions,
- indices=indices,
- unicodes=unicodes,
- type="real",
- direction=0,
- boundarychar_label=0,
- boundarychar=65536,
- designsize=(designsize/10)*65536,
- spacer="500 units",
- encodingbytes=2,
- filename=filename,
- fontname=fontname,
- fullname=fullname,
- psname=fontname or fullname,
- name=filename or fullname,
- units=units,
- format=fonts.fontformat(filename,"opentype"),
- cidinfo=cidinfo,
- ascender=abs(metadata.ascent or 0),
- descender=abs(metadata.descent or 0),
- spacer=spacer,
- italicangle=italicangle,
- }
- 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,specification.relativeid)
- if tfm.fontname_mode=="specification" then
- 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
- fonts.logger.save(tfmtable,file.extname(specification.filename),specification)
- end
- return tfmtable
-end
-function otf.collect_lookups(otfdata,kind,script,language)
- 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
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-otd']={
- 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 trace_dynamics=false trackers.register("otf.dynamics",function(v) trace_dynamics=v end)
-fonts=fonts or {}
-fonts.otf=fonts.otf or {}
-local otf=fonts.otf
-local fontdata=fonts.ids
-otf.features=otf.features or {}
-otf.features.default=otf.features.default or {}
-local context_setups=fonts.define.specify.context_setups
-local context_numbers=fonts.define.specify.context_numbers
-local a_to_script={} otf.a_to_script=a_to_script
-local a_to_language={} otf.a_to_language=a_to_language
-function otf.set_dynamics(font,dynamics,attribute)
- local features=context_setups[context_numbers[attribute]]
- if features then
- local script=features.script or 'dflt'
- local language=features.language or 'dflt'
- local ds=dynamics[script]
- if not ds then
- ds={}
- dynamics[script]=ds
- end
- local dsl=ds[language]
- if not dsl then
- dsl={}
- ds[language]=dsl
- end
- local dsla=dsl[attribute]
- if dsla then
- return dsla
- else
- local tfmdata=fontdata[font]
- a_to_script [attribute]=script
- a_to_language[attribute]=language
- local saved={
- script=tfmdata.script,
- language=tfmdata.language,
- mode=tfmdata.mode,
- features=tfmdata.shared.features
- }
- tfmdata.mode="node"
- tfmdata.language=language
- tfmdata.script=script
- tfmdata.shared.features={}
- local set=fonts.define.check(features,otf.features.default)
- dsla=otf.set_features(tfmdata,set)
- if trace_dynamics then
- logs.report("otf define","setting dynamics %s: attribute %s, script %s, language %s, set: %s",context_numbers[attribute],attribute,script,language,table.sequenced(set))
- end
- tfmdata.script=saved.script
- tfmdata.language=saved.language
- tfmdata.mode=saved.mode
- tfmdata.shared.features=saved.features
- dynamics[script][language][attribute]=dsla
- return dsla
- end
- end
- return nil
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-oti']={
- 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 lower=string.lower
-local otf=fonts.otf
-otf.default_language='latn'
-otf.default_script='dflt'
-local languages=otf.tables.languages
-local scripts=otf.tables.scripts
-function otf.features.language(tfmdata,value)
- if value then
- value=lower(value)
- if languages[value] then
- tfmdata.language=value
- end
- end
-end
-function otf.features.script(tfmdata,value)
- if value then
- value=lower(value)
- if scripts[value] then
- tfmdata.script=value
- end
- end
-end
-function otf.features.mode(tfmdata,value)
- if value then
- tfmdata.mode=lower(value)
- end
-end
-fonts.initializers.base.otf.language=otf.features.language
-fonts.initializers.base.otf.script=otf.features.script
-fonts.initializers.base.otf.mode=otf.features.mode
-fonts.initializers.base.otf.method=otf.features.mode
-fonts.initializers.node.otf.language=otf.features.language
-fonts.initializers.node.otf.script=otf.features.script
-fonts.initializers.node.otf.mode=otf.features.mode
-fonts.initializers.node.otf.method=otf.features.mode
-otf.features.register("features",true)
-table.insert(fonts.processors,"features")
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-otb']={
- 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 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
-local trace_baseinit=false trackers.register("otf.baseinit",function(v) trace_baseinit=v end)
-local trace_singles=false trackers.register("otf.singles",function(v) trace_singles=v end)
-local trace_multiples=false trackers.register("otf.multiples",function(v) trace_multiples=v end)
-local trace_alternatives=false trackers.register("otf.alternatives",function(v) trace_alternatives=v end)
-local trace_ligatures=false trackers.register("otf.ligatures",function(v) trace_ligatures=v end)
-local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end)
-local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end)
-local wildcard="*"
-local default="dflt"
-local split_at_space=lpeg.Ct(lpeg.splitat(" "))
-local pcache,fcache={},{}
-local function gref(descriptions,n)
- if type(n)=="number" then
- local name=descriptions[n].name
- if name then
- return format("U+%04X (%s)",n,name)
- else
- return format("U+%04X")
- end
- elseif n then
- local num,nam={},{}
- for i=1,#n do
- local ni=n[i]
- num[i]=format("U+%04X",ni)
- nam[i]=descriptions[ni].name or "?"
- end
- return format("%s (%s)",concat(num," "),concat(nam," "))
- else
- return "?"
- end
-end
-local function cref(kind,lookupname)
- if lookupname then
- return format("feature %s, lookup %s",kind,lookupname)
- else
- return format("feature %s",kind)
- end
-end
-local function resolve_ligatures(tfmdata,ligatures,kind)
- kind=kind or "unknown"
- local unicodes=tfmdata.unicodes
- local characters=tfmdata.characters
- local descriptions=tfmdata.descriptions
- local changed=tfmdata.changed
- local done={}
- while true do
- local ok=false
- for k,v in next,ligatures do
- local lig=v[1]
- if not done[lig] then
- local ligs=lpegmatch(split_at_space,lig)
- if #ligs==2 then
- local uc=v[2]
- local c,f,s=characters[uc],ligs[1],ligs[2]
- local uft,ust=unicodes[f] or 0,unicodes[s] or 0
- if not uft or not ust then
- logs.report("define otf","%s: unicode problem with base ligature %s = %s + %s",cref(kind),gref(descriptions,uc),gref(descriptions,uft),gref(descriptions,ust))
- else
- if type(uft)=="number" then uft={ uft } end
- if type(ust)=="number" then ust={ ust } end
- for ufi=1,#uft do
- local uf=uft[ufi]
- for usi=1,#ust do
- local us=ust[usi]
- if changed[uf] or changed[us] then
- if trace_baseinit and trace_ligatures then
- logs.report("define otf","%s: base ligature %s + %s ignored",cref(kind),gref(descriptions,uf),gref(descriptions,us))
- end
- else
- local first,second=characters[uf],us
- if first and second then
- local t=first.ligatures
- if not t then
- t={}
- first.ligatures=t
- end
- if type(uc)=="number" then
- t[second]={ type=0,char=uc }
- else
- t[second]={ type=0,char=uc[1] }
- end
- if trace_baseinit and trace_ligatures then
- logs.report("define otf","%s: base ligature %s + %s => %s",cref(kind),gref(descriptions,uf),gref(descriptions,us),gref(descriptions,uc))
- end
- end
- end
- end
- end
- end
- ok,done[lig]=true,descriptions[uc].name
- end
- end
- end
- if ok then
- for d,n in next,done do
- local pattern=pcache[d] if not pattern then pattern="^("..d..") " pcache[d]=pattern end
- local fnc=fcache[n] if not fnc then fnc=function() return n.." " end fcache[n]=fnc end
- for k,v in next,ligatures do
- v[1]=gsub(v[1],pattern,fnc)
- end
- end
- else
- break
- end
- end
-end
-local splitter=lpeg.splitat(" ")
-local function prepare_base_substitutions(tfmdata,kind,value)
- if value then
- local otfdata=tfmdata.shared.otfdata
- local validlookups,lookuplist=otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language)
- if validlookups then
- local ligatures={}
- local unicodes=tfmdata.unicodes
- local indices=tfmdata.indices
- 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]
- 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]
- if pc then
- 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
- 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.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]
- local ps=lookups[lookup]
- if ps then
- for i=1,#ps do
- local p=ps[i]
- local a=actions[p[1]]
- if a then
- a(p,lookup,k,glyph,unicode)
- end
- end
- end
- end
- end
- end
- resolve_ligatures(tfmdata,ligatures,kind)
- end
- else
- tfmdata.ligatures=tfmdata.ligatures or {}
- end
-end
-local function prepare_base_kerns(tfmdata,kind,value)
- if value then
- local otfdata=tfmdata.shared.otfdata
- local validlookups,lookuplist=otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language)
- if validlookups then
- local unicodes=tfmdata.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
- if dk then
- local s=sharedkerns[dk]
- if s==false then
- 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
- 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
- else
- sharedkerns[dk]=false
- end
- end
- end
- end
- end
- end
- end
-end
-local supported_gsub={
- 'liga','dlig','rlig','hlig',
- 'pnum','onum','tnum','lnum',
- 'zero',
- 'smcp','cpsp','c2sc','ornm','aalt',
- 'hwid','fwid',
- 'ssty','rtlm',
-}
-local supported_gpos={
- 'kern'
-}
-function otf.features.register_base_substitution(tag)
- supported_gsub[#supported_gsub+1]=tag
-end
-function otf.features.register_base_kern(tag)
- supported_gsub[#supported_gpos+1]=tag
-end
-local basehash,basehashes={},1
-function fonts.initializers.base.otf.features(tfmdata,value)
- if true then
- local t=trace_preparing and os.clock()
- local features=tfmdata.shared.features
- if features then
- local h={}
- for f=1,#supported_gsub do
- local feature=supported_gsub[f]
- local value=features[feature]
- prepare_base_substitutions(tfmdata,feature,value)
- if value then
- h[#h+1]=feature.."="..tostring(value)
- end
- end
- for f=1,#supported_gpos do
- local feature=supported_gpos[f]
- local value=features[feature]
- prepare_base_kerns(tfmdata,feature,features[feature])
- if value then
- h[#h+1]=feature.."="..tostring(value)
- end
- end
- local hash=concat(h," ")
- local base=basehash[hash]
- if not base then
- basehashes=basehashes+1
- base=basehashes
- basehash[hash]=base
- end
- tfmdata.fullname=tfmdata.fullname.."-"..base
- end
- if trace_preparing then
- logs.report("otf define","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?")
- end
- end
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-otn']={
- 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 concat,insert,remove=table.concat,table.insert,table.remove
-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
-local trace_lookups=false trackers.register("otf.lookups",function(v) trace_lookups=v end)
-local trace_singles=false trackers.register("otf.singles",function(v) trace_singles=v end)
-local trace_multiples=false trackers.register("otf.multiples",function(v) trace_multiples=v end)
-local trace_alternatives=false trackers.register("otf.alternatives",function(v) trace_alternatives=v end)
-local trace_ligatures=false trackers.register("otf.ligatures",function(v) trace_ligatures=v end)
-local trace_contexts=false trackers.register("otf.contexts",function(v) trace_contexts=v end)
-local trace_marks=false trackers.register("otf.marks",function(v) trace_marks=v end)
-local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end)
-local trace_cursive=false trackers.register("otf.cursive",function(v) trace_cursive=v end)
-local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end)
-local trace_bugs=false trackers.register("otf.bugs",function(v) trace_bugs=v end)
-local trace_details=false trackers.register("otf.details",function(v) trace_details=v end)
-local trace_applied=false trackers.register("otf.applied",function(v) trace_applied=v end)
-local trace_steps=false trackers.register("otf.steps",function(v) trace_steps=v end)
-local trace_skips=false trackers.register("otf.skips",function(v) trace_skips=v end)
-local trace_directions=false trackers.register("otf.directions",function(v) trace_directions=v end)
-trackers.register("otf.verbose_chain",function(v) otf.setcontextchain(v and "verbose") end)
-trackers.register("otf.normal_chain",function(v) otf.setcontextchain(v and "normal") end)
-trackers.register("otf.replacements","otf.singles,otf.multiples,otf.alternatives,otf.ligatures")
-trackers.register("otf.positions","otf.marks,otf.kerns,otf.cursive")
-trackers.register("otf.actions","otf.replacements,otf.positions")
-trackers.register("otf.injections","nodes.injections")
-trackers.register("*otf.sample","otf.steps,otf.actions,otf.analyzing")
-local insert_node_after=node.insert_after
-local delete_node=nodes.delete
-local copy_node=node.copy
-local find_node_tail=node.tail or node.slide
-local set_attribute=node.set_attribute
-local has_attribute=node.has_attribute
-local zwnj=0x200C
-local zwj=0x200D
-local wildcard="*"
-local default="dflt"
-local split_at_space=lpeg.splitters[" "] or lpeg.Ct(lpeg.splitat(" "))
-local glyph=node.id('glyph')
-local glue=node.id('glue')
-local kern=node.id('kern')
-local disc=node.id('disc')
-local whatsit=node.id('whatsit')
-local state=attributes.private('state')
-local markbase=attributes.private('markbase')
-local markmark=attributes.private('markmark')
-local markdone=attributes.private('markdone')
-local cursbase=attributes.private('cursbase')
-local curscurs=attributes.private('curscurs')
-local cursdone=attributes.private('cursdone')
-local kernpair=attributes.private('kernpair')
-local set_mark=nodes.set_mark
-local set_cursive=nodes.set_cursive
-local set_kern=nodes.set_kern
-local set_pair=nodes.set_pair
-local markonce=true
-local cursonce=true
-local kernonce=true
-local fontdata=fonts.ids
-otf.features.process={}
-local tfmdata=false
-local otfdata=false
-local characters=false
-local descriptions=false
-local marks=false
-local indices=false
-local unicodes=false
-local currentfont=false
-local lookuptable=false
-local anchorlookups=false
-local handlers={}
-local rlmode=0
-local featurevalue=false
-local context_setups=fonts.define.specify.context_setups
-local context_numbers=fonts.define.specify.context_numbers
-local context_merged=fonts.define.specify.context_merged
-local special_attributes={
- init=1,
- medi=2,
- fina=3,
- isol=4
-}
-local checkstep=(nodes and nodes.tracers and nodes.tracers.steppers.check) or function() end
-local registerstep=(nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end
-local registermessage=(nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end
-local function logprocess(...)
- if trace_steps then
- registermessage(...)
- end
- logs.report("otf direct",...)
-end
-local function logwarning(...)
- logs.report("otf direct",...)
-end
-local function gref(n)
- if type(n)=="number" then
- local description=descriptions[n]
- local name=description and description.name
- if name then
- return format("U+%04X (%s)",n,name)
- else
- return format("U+%04X",n)
- end
- elseif not n then
- return "<error in tracing>"
- else
- local num,nam={},{}
- for i=1,#n do
- local ni=n[i]
- num[#num+1]=format("U+%04X",ni)
- local dni=descriptions[ni]
- nam[#num]=(dni and dni.name) or "?"
- end
- return format("%s (%s)",concat(num," "),concat(nam," "))
- end
-end
-local function cref(kind,chainname,chainlookupname,lookupname,index)
- if index then
- return format("feature %s, chain %s, sub %s, lookup %s, index %s",kind,chainname,chainlookupname,lookupname,index)
- elseif lookupname then
- return format("feature %s, chain %s, sub %s, lookup %s",kind,chainname or "?",chainlookupname or "?",lookupname)
- elseif chainlookupname then
- return format("feature %s, chain %s, sub %s",kind,chainname or "?",chainlookupname)
- elseif chainname then
- return format("feature %s, chain %s",kind,chainname)
- else
- return format("feature %s",kind)
- end
-end
-local function pref(kind,lookupname)
- return format("feature %s, lookup %s",kind,lookupname)
-end
-local function markstoligature(kind,lookupname,start,stop,char)
- local n=copy_node(start)
- local keep=start
- local current
- current,start=insert_node_after(start,start,n)
- local snext=stop.next
- current.next=snext
- if snext then
- snext.prev=current
- end
- start.prev,stop.next=nil,nil
- current.char,current.subtype,current.components=char,2,start
- return keep
-end
-local function toligature(kind,lookupname,start,stop,char,markflag,discfound)
- if start~=stop then
- if discfound then
- local lignode=copy_node(start)
- lignode.font,lignode.char,lignode.subtype=start.font,char,2
- local next,prev=stop.next,start.prev
- stop.next=nil
- lignode=node.do_ligature_n(start,stop,lignode)
- prev.next=lignode
- if next then
- next.prev=lignode
- end
- lignode.next,lignode.prev=next,prev
- start=lignode
- else
- local deletemarks=markflag~="mark"
- local n=copy_node(start)
- local current
- current,start=insert_node_after(start,start,n)
- local snext=stop.next
- current.next=snext
- if snext then
- snext.prev=current
- end
- start.prev,stop.next=nil,nil
- current.char,current.subtype,current.components=char,2,start
- local head=current
- if deletemarks then
- if trace_marks then
- while start do
- if marks[start.char] then
- logwarning("%s: remove mark %s",pref(kind,lookupname),gref(start.char))
- end
- start=start.next
- end
- end
- else
- local i=0
- while start do
- if marks[start.char] then
- set_attribute(start,markdone,i)
- if trace_marks then
- logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i)
- end
- head,current=insert_node_after(head,current,copy_node(start))
- else
- i=i+1
- end
- start=start.next
- end
- start=current.next
- while start and start.id==glyph do
- if marks[start.char] then
- set_attribute(start,markdone,i)
- if trace_marks then
- logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i)
- end
- else
- break
- end
- start=start.next
- end
- end
- return head
- end
- else
- start.char=char
- end
- return start
-end
-function handlers.gsub_single(start,kind,lookupname,replacement)
- if trace_singles then
- logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement))
- end
- start.char=replacement
- return start,true
-end
-local function alternative_glyph(start,alternatives,kind,chainname,chainlookupname,lookupname)
- local value,choice,n=featurevalue or tfmdata.shared.features[kind],nil,#alternatives
- if value=="random" then
- local r=math.random(1,n)
- value,choice=format("random, choice %s",r),alternatives[r]
- elseif value=="first" then
- value,choice=format("first, choice %s",1),alternatives[1]
- elseif value=="last" then
- value,choice=format("last, choice %s",n),alternatives[n]
- else
- value=tonumber(value)
- if type(value)~="number" then
- value,choice="default, choice 1",alternatives[1]
- elseif value>n then
- value,choice=format("no %s variants, taking %s",value,n),alternatives[n]
- elseif value==0 then
- value,choice=format("choice %s (no change)",value),start.char
- elseif value<1 then
- value,choice=format("no %s variants, taking %s",value,1),alternatives[1]
- else
- value,choice=format("choice %s",value),alternatives[value]
- end
- end
- if not choice then
- logwarning("%s: no variant %s for %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(start.char))
- choice,value=start.char,format("no replacement instead of %s",value)
- end
- return choice,value
-end
-function handlers.gsub_alternate(start,kind,lookupname,alternative,sequence)
- local choice,index=alternative_glyph(start,alternative,kind,lookupname)
- if trace_alternatives then
- logprocess("%s: replacing %s by alternative %s (%s)",pref(kind,lookupname),gref(start.char),gref(choice),index)
- end
- start.char=choice
- return start,true
-end
-function handlers.gsub_multiple(start,kind,lookupname,multiple)
- if trace_multiples then
- logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple))
- end
- start.char=multiple[1]
- if #multiple>1 then
- for k=2,#multiple do
- local n=copy_node(start)
- n.char=multiple[k]
- local sn=start.next
- n.next=sn
- n.prev=start
- if sn then
- sn.prev=n
- end
- start.next=n
- start=n
- end
- end
- return start,true
-end
-function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence)
- local s,stop,discfound=start.next,nil,false
- local startchar=start.char
- if marks[startchar] then
- while s do
- local id=s.id
- if id==glyph and s.subtype<256 then
- if s.font==currentfont then
- local char=s.char
- local lg=ligature[1][char]
- if not lg then
- break
- else
- stop=s
- ligature=lg
- s=s.next
- end
- else
- break
- end
- else
- break
- end
- end
- if stop and ligature[2] then
- if trace_ligatures then
- 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
- start=markstoligature(kind,lookupname,start,stop,ligature[2])
- end
- return start,true
- end
- else
- local skipmark=sequence.flags[1]
- while s do
- local id=s.id
- if id==glyph and s.subtype<256 then
- if s.font==currentfont then
- local char=s.char
- if skipmark and marks[char] then
- s=s.next
- else
- local lg=ligature[1][char]
- if not lg then
- break
- else
- stop=s
- ligature=lg
- s=s.next
- end
- end
- else
- break
- end
- elseif id==disc then
- discfound=true
- s=s.next
- else
- break
- end
- end
- if stop and ligature[2] then
- if trace_ligatures then
- 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
- start=toligature(kind,lookupname,start,stop,ligature[2],skipmark,discfound)
- end
- return start,true
- end
- end
- return start,false
-end
-function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence)
- local markchar=start.char
- if marks[markchar] then
- local base=start.prev
- if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
- local basechar=base.char
- if marks[basechar] then
- while true do
- base=base.prev
- if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
- basechar=base.char
- if not marks[basechar] then
- break
- end
- else
- if trace_bugs then
- logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
- end
- return start,false
- end
- end
- end
- local baseanchors=descriptions[basechar]
- if baseanchors then
- baseanchors=baseanchors.anchors
- end
- if baseanchors then
- local baseanchors=baseanchors['basechar']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma)
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)",
- pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
- end
- return start,true
- end
- end
- end
- if trace_bugs then
- logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar))
- end
- end
- else
- fonts.register_message(currentfont,basechar,"no base anchors")
- end
- elseif trace_bugs then
- logwarning("%s: prev node is no char",pref(kind,lookupname))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
- end
- return start,false
-end
-function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence)
- local markchar=start.char
- if marks[markchar] then
- local base=start.prev
- local index=1
- if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
- local basechar=base.char
- if marks[basechar] then
- index=index+1
- while true do
- base=base.prev
- if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
- basechar=base.char
- if marks[basechar] then
- index=index+1
- else
- break
- end
- else
- if trace_bugs then
- logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
- end
- return start,false
- end
- end
- end
- local i=has_attribute(start,markdone)
- if i then index=i end
- local baseanchors=descriptions[basechar]
- if baseanchors then
- baseanchors=baseanchors.anchors
- if baseanchors then
- local baseanchors=baseanchors['baselig']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- ba=ba[index]
- if ba then
- local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index)
- if trace_marks then
- logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)",
- pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy)
- end
- return start,true
- end
- end
- end
- end
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar))
- end
- end
- end
- else
- fonts.register_message(currentfont,basechar,"no base anchors")
- end
- elseif trace_bugs then
- logwarning("%s: prev node is no char",pref(kind,lookupname))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
- end
- return start,false
-end
-function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence)
- local markchar=start.char
- if marks[markchar] then
- local base=start.prev
- if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
- local basechar=base.char
- local baseanchors=descriptions[basechar]
- if baseanchors then
- baseanchors=baseanchors.anchors
- if baseanchors then
- baseanchors=baseanchors['basemark']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma)
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)",
- pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
- end
- return start,true
- end
- end
- end
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar))
- end
- end
- end
- else
- fonts.register_message(currentfont,basechar,"no base anchors")
- end
- elseif trace_bugs then
- logwarning("%s: prev node is no mark",pref(kind,lookupname))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
- end
- return start,false
-end
-function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence)
- local alreadydone=cursonce and has_attribute(start,cursbase)
- if not alreadydone then
- local done=false
- local startchar=start.char
- if marks[startchar] then
- if trace_cursive then
- logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
- end
- else
- local nxt=start.next
- while not done and nxt and nxt.id==glyph and nxt.subtype<256 and nxt.font==currentfont do
- local nextchar=nxt.char
- if marks[nextchar] then
- nxt=nxt.next
- else
- local entryanchors=descriptions[nextchar]
- if entryanchors then
- entryanchors=entryanchors.anchors
- if entryanchors then
- entryanchors=entryanchors['centry']
- if entryanchors then
- local al=anchorlookups[lookupname]
- for anchor,entry in next,entryanchors do
- if al[anchor] then
- local exit=exitanchors[anchor]
- if exit then
- local dx,dy,bound=set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
- if trace_cursive then
- logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode)
- end
- done=true
- break
- end
- end
- end
- end
- end
- else
- fonts.register_message(currentfont,startchar,"no entry anchors")
- end
- break
- end
- end
- end
- return start,done
- else
- if trace_cursive and trace_details then
- logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)
- end
- return start,false
- end
-end
-function handlers.gpos_single(start,kind,lookupname,kerns,sequence)
- local startchar=start.char
- local dx,dy,w,h=set_pair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),dx,dy,w,h)
- end
- return start,false
-end
-function handlers.gpos_pair(start,kind,lookupname,kerns,sequence)
- local snext=start.next
- if not snext then
- return start,false
- else
- local prev,done=start,false
- local factor=tfmdata.factor
- while snext and snext.id==glyph and snext.subtype<256 and snext.font==currentfont do
- local nextchar=snext.char
-local krn=kerns[nextchar]
- if not krn and marks[nextchar] then
- prev=snext
- snext=snext.next
- else
- local krn=kerns[nextchar]
- if not krn then
- elseif type(krn)=="table" then
- if krn[1]=="pair" then
- local a,b=krn[3],krn[4]
- if a and #a>0 then
- local startchar=start.char
- local x,y,w,h=set_pair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- if b and #b>0 then
- local startchar=start.char
- local x,y,w,h=set_pair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
- if trace_kerns then
- logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- else
- logs.report("%s: check this out (old kern stuff)",pref(kind,lookupname))
- local a,b=krn[3],krn[7]
- if a and a~=0 then
- local k=set_kern(snext,factor,rlmode,a)
- if trace_kerns then
- logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar))
- end
- end
- if b and b~=0 then
- logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor)
- end
- end
- done=true
- elseif krn~=0 then
- local k=set_kern(snext,factor,rlmode,krn)
- if trace_kerns then
- logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar))
- end
- done=true
- end
- break
- end
- end
- return start,done
- end
-end
-local chainmores={}
-local chainprocs={}
-local function logprocess(...)
- if trace_steps then
- registermessage(...)
- end
- logs.report("otf subchain",...)
-end
-local function logwarning(...)
- logs.report("otf subchain",...)
-end
-function chainmores.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname,n)
- logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
- return start,false
-end
-function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n)
- logprocess("%s: gsub_multiple not yet supported",cref(kind,chainname,chainlookupname))
- return start,false
-end
-function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n)
- logprocess("%s: gsub_alternate not yet supported",cref(kind,chainname,chainlookupname))
- return start,false
-end
-local function logprocess(...)
- if trace_steps then
- registermessage(...)
- end
- logs.report("otf chain",...)
-end
-local function logwarning(...)
- logs.report("otf chain",...)
-end
-function chainprocs.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname)
- logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
- return start,false
-end
-function chainprocs.reversesub(start,stop,kind,chainname,currentcontext,cache,replacements)
- local char=start.char
- local replacement=replacements[char]
- if replacement then
- if trace_singles then
- logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement))
- end
- start.char=replacement
- return start,true
- else
- return start,false
- end
-end
-local function delete_till_stop(start,stop,ignoremarks)
- if start~=stop then
- local done=false
- while not done do
- done=start==stop
- delete_node(start,start.next)
- end
- end
-end
-function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex)
- if not chainindex then
- delete_till_stop(start,stop)
- end
- local current=start
- local subtables=currentlookup.subtables
- while current do
- if current.id==glyph then
- local currentchar=current.char
- local lookupname=subtables[1]
- local replacement=cache.gsub_single[lookupname]
- if not replacement then
- if trace_bugs then
- logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex))
- end
- else
- replacement=replacement[currentchar]
- if not replacement then
- if trace_bugs then
- logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar))
- end
- else
- if trace_singles then
- logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement))
- end
- current.char=replacement
- end
- end
- return start,true
- elseif current==stop then
- break
- else
- current=current.next
- end
- end
- return start,false
-end
-chainmores.gsub_single=chainprocs.gsub_single
-function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname)
- delete_till_stop(start,stop)
- local startchar=start.char
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local replacements=cache.gsub_multiple[lookupname]
- if not replacements then
- if trace_bugs then
- logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname))
- end
- else
- replacements=replacements[startchar]
- if not replacements then
- if trace_bugs then
- logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar))
- end
- else
- if trace_multiples then
- logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements))
- end
- local sn=start.next
- for k=1,#replacements do
- if k==1 then
- start.char=replacements[k]
- else
- local n=copy_node(start)
- n.char=replacements[k]
- n.next,n.prev=sn,start
- if sn then
- sn.prev=n
- end
- start.next,start=n,n
- end
- end
- return start,true
- end
- end
- return start,false
-end
-function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname)
- delete_till_stop(start,stop)
- local current=start
- local subtables=currentlookup.subtables
- while current do
- if current.id==glyph then
- local currentchar=current.char
- local lookupname=subtables[1]
- local alternatives=cache.gsub_alternate[lookupname]
- if not alternatives then
- if trace_bugs then
- logwarning("%s: no alternative hits",cref(kind,chainname,chainlookupname,lookupname))
- end
- else
- alternatives=alternatives[currentchar]
- if not alternatives then
- if trace_bugs then
- logwarning("%s: no alternative for %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar))
- end
- else
- local choice,index=alternative_glyph(current,alternatives,kind,chainname,chainlookupname,lookupname)
- current.char=choice
- if trace_alternatives then
- logprocess("%s: replacing single %s by alternative %s (%s)",cref(kind,chainname,chainlookupname,lookupname),index,gref(currentchar),gref(choice),index)
- end
- end
- end
- return start,true
- elseif current==stop then
- break
- else
- current=current.next
- end
- end
- return start,false
-end
-function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex)
- local startchar=start.char
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local ligatures=cache.gsub_ligature[lookupname]
- if not ligatures then
- if trace_bugs then
- logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex))
- end
- else
- ligatures=ligatures[startchar]
- if not ligatures then
- if trace_bugs then
- logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
- end
- else
- local s,discfound,last,nofreplacements=start.next,false,stop,0
- while s do
- local id=s.id
- if id==disc then
- s=s.next
- discfound=true
- else
- local schar=s.char
- if marks[schar] then
- s=s.next
- else
- local lg=ligatures[1][schar]
- if not lg then
- break
- else
- ligatures,last,nofreplacements=lg,s,nofreplacements+1
- if s==stop then
- break
- else
- s=s.next
- end
- end
- end
- end
- end
- local l2=ligatures[2]
- if l2 then
- if chainindex then
- stop=last
- end
- if trace_ligatures then
- if start==stop then
- logprocess("%s: replacing character %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2))
- else
- logprocess("%s: replacing character %s upto %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2))
- end
- end
- start=toligature(kind,lookupname,start,stop,l2,currentlookup.flags[1],discfound)
- return start,true,nofreplacements
- elseif trace_bugs then
- if start==stop then
- logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
- else
- logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char))
- end
- end
- end
- end
- return start,false,0
-end
-chainmores.gsub_ligature=chainprocs.gsub_ligature
-function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname)
- local markchar=start.char
- if marks[markchar] then
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local markanchors=cache.gpos_mark2base[lookupname]
- if markanchors then
- markanchors=markanchors[markchar]
- end
- if markanchors then
- local base=start.prev
- if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
- local basechar=base.char
- if marks[basechar] then
- while true do
- base=base.prev
- if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
- basechar=base.char
- if not marks[basechar] then
- break
- end
- else
- if trace_bugs then
- logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
- end
- return start,false
- end
- end
- end
- local baseanchors=descriptions[basechar].anchors
- if baseanchors then
- local baseanchors=baseanchors['basechar']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma)
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)",
- cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
- end
- return start,true
- end
- end
- end
- if trace_bugs then
- logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
- end
- end
- end
- elseif trace_bugs then
- logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
- end
- return start,false
-end
-function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname)
- local markchar=start.char
- if marks[markchar] then
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local markanchors=cache.gpos_mark2ligature[lookupname]
- if markanchors then
- markanchors=markanchors[markchar]
- end
- if markanchors then
- local base=start.prev
- local index=1
- if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
- local basechar=base.char
- if marks[basechar] then
- index=index+1
- while true do
- base=base.prev
- if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
- basechar=base.char
- if marks[basechar] then
- index=index+1
- else
- break
- end
- else
- if trace_bugs then
- logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar)
- end
- return start,false
- end
- end
- end
- local i=has_attribute(start,markdone)
- if i then index=i end
- local baseanchors=descriptions[basechar].anchors
- if baseanchors then
- local baseanchors=baseanchors['baselig']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- ba=ba[index]
- if ba then
- local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index)
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)",
- cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy)
- end
- return start,true
- end
- end
- end
- end
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
- end
- end
- end
- elseif trace_bugs then
- logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname)
- end
- elseif trace_bugs then
- logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
- end
- return start,false
-end
-function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname)
- local markchar=start.char
- if marks[markchar] then
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local markanchors=cache.gpos_mark2mark[lookupname]
- if markanchors then
- markanchors=markanchors[markchar]
- end
- if markanchors then
- local base=start.prev
- if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
- local basechar=base.char
- local baseanchors=descriptions[basechar].anchors
- if baseanchors then
- baseanchors=baseanchors['basemark']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma)
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)",
- cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
- end
- return start,true
- end
- end
- end
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
- end
- end
- end
- elseif trace_bugs then
- logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
- end
- elseif trace_bugs then
- logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
- end
- return start,false
-end
-function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname)
- local alreadydone=cursonce and has_attribute(start,cursbase)
- if not alreadydone then
- local startchar=start.char
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local exitanchors=cache.gpos_cursive[lookupname]
- if exitanchors then
- exitanchors=exitanchors[startchar]
- end
- if exitanchors then
- local done=false
- if marks[startchar] then
- if trace_cursive then
- logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
- end
- else
- local nxt=start.next
- while not done and nxt and nxt.id==glyph and nxt.subtype<256 and nxt.font==currentfont do
- local nextchar=nxt.char
- if marks[nextchar] then
- nxt=nxt.next
- else
- local entryanchors=descriptions[nextchar]
- if entryanchors then
- entryanchors=entryanchors.anchors
- if entryanchors then
- entryanchors=entryanchors['centry']
- if entryanchors then
- local al=anchorlookups[lookupname]
- for anchor,entry in next,entryanchors do
- if al[anchor] then
- local exit=exitanchors[anchor]
- if exit then
- local dx,dy,bound=set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
- if trace_cursive then
- logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode)
- end
- done=true
- break
- end
- end
- end
- end
- end
- else
- fonts.register_message(currentfont,startchar,"no entry anchors")
- end
- break
- end
- end
- end
- return start,done
- else
- if trace_cursive and trace_details then
- logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)
- end
- return start,false
- end
- end
- return start,false
-end
-function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex,sequence)
- local startchar=start.char
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local kerns=cache.gpos_single[lookupname]
- if kerns then
- kerns=kerns[startchar]
- if kerns then
- local dx,dy,w,h=set_pair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h)
- end
- end
- end
- return start,false
-end
-function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex,sequence)
- local snext=start.next
- if snext then
- local startchar=start.char
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local kerns=cache.gpos_pair[lookupname]
- if kerns then
- kerns=kerns[startchar]
- if kerns then
- local prev,done=start,false
- local factor=tfmdata.factor
- while snext and snext.id==glyph and snext.subtype<256 and snext.font==currentfont do
- local nextchar=snext.char
- local krn=kerns[nextchar]
- if not krn and marks[nextchar] then
- prev=snext
- snext=snext.next
- else
- if not krn then
- elseif type(krn)=="table" then
- if krn[1]=="pair" then
- local a,b=krn[3],krn[4]
- if a and #a>0 then
- local startchar=start.char
- local x,y,w,h=set_pair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
- if trace_kerns then
- logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- if b and #b>0 then
- local startchar=start.char
- local x,y,w,h=set_pair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
- if trace_kerns then
- logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- else
- logs.report("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname))
- local a,b=krn[3],krn[7]
- if a and a~=0 then
- local k=set_kern(snext,factor,rlmode,a)
- if trace_kerns then
- logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))
- end
- end
- if b and b~=0 then
- logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor)
- end
- end
- done=true
- elseif krn~=0 then
- local k=set_kern(snext,factor,rlmode,krn)
- if trace_kerns then
- logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))
- end
- done=true
- end
- break
- end
- end
- return start,done
- end
- end
- end
- return start,false
-end
-local function show_skip(kind,chainname,char,ck,class)
- if ck[9] then
- logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s (%s=>%s)",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
- else
- logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s",cref(kind,chainname),gref(char),class,ck[1],ck[2])
- end
-end
-local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,cache)
- local flags,done=sequence.flags,false
- local skipmark,skipligature,skipbase=flags[1],flags[2],flags[3]
- local someskip=skipmark or skipligature or skipbase
- local markclass=sequence.markclass
- local skipped=false
- for k=1,#contexts do
- local match,current,last=true,start,start
- local ck=contexts[k]
- local seq=ck[3]
- local s=#seq
- if s==1 then
- match=current.id==glyph and current.subtype<256 and current.font==currentfont and seq[1][current.char]
- else
- local f,l=ck[4],ck[5]
- if f==l then
- match=true
- else
- local n=f+1
- last=last.next
- while n<=l do
- if last then
- local id=last.id
- if id==glyph then
- if last.subtype<256 and last.font==currentfont then
- local char=last.char
- local ccd=descriptions[char]
- if ccd then
- local class=ccd.class
- if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
- skipped=true
- if trace_skips then
- show_skip(kind,chainname,char,ck,class)
- end
- last=last.next
- elseif seq[n][char] then
- if n<l then
- last=last.next
- end
- n=n+1
- else
- match=false break
- end
- else
- match=false break
- end
- else
- match=false break
- end
- elseif id==disc then
- last=last.next
- else
- match=false break
- end
- else
- match=false break
- end
- end
- end
- if match and f>1 then
- local prev=start.prev
- if prev then
- local n=f-1
- while n>=1 do
- if prev then
- local id=prev.id
- if id==glyph then
- if prev.subtype<256 and prev.font==currentfont then
- local char=prev.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
- skipped=true
- if trace_skips then
- show_skip(kind,chainname,char,ck,class)
- end
- elseif seq[n][char] then
- n=n -1
- else
- match=false break
- end
- else
- match=false break
- end
- else
- match=false break
- end
- elseif id==disc then
- elseif seq[n][32] then
- n=n -1
- else
- match=false break
- end
- prev=prev.prev
- elseif seq[n][32] then
- n=n -1
- else
- match=false break
- end
- end
- elseif f==2 then
- match=seq[1][32]
- else
- for n=f-1,1 do
- if not seq[n][32] then
- match=false break
- end
- end
- end
- end
- if match and s>l then
- local current=last.next
- if current then
- local n=l+1
- while n<=s do
- if current then
- local id=current.id
- if id==glyph then
- if current.subtype<256 and current.font==currentfont then
- local char=current.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
- skipped=true
- if trace_skips then
- show_skip(kind,chainname,char,ck,class)
- end
- elseif seq[n][char] then
- n=n+1
- else
- match=false break
- end
- else
- match=false break
- end
- else
- match=false break
- end
- elseif id==disc then
- elseif seq[n][32] then
- n=n+1
- else
- match=false break
- end
- current=current.next
- elseif seq[n][32] then
- n=n+1
- else
- match=false break
- end
- end
- elseif s-l==1 then
- match=seq[s][32]
- else
- for n=l+1,s do
- if not seq[n][32] then
- match=false break
- end
- end
- end
- end
- end
- if match then
- if trace_contexts then
- local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5]
- local char=start.char
- if ck[9] then
- logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s (%s=>%s)",cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10])
- else
- logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s",cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype)
- end
- end
- local chainlookups=ck[6]
- if chainlookups then
- local nofchainlookups=#chainlookups
- if nofchainlookups==1 then
- local chainlookupname=chainlookups[1]
- local chainlookup=lookuptable[chainlookupname]
- local cp=chainprocs[chainlookup.type]
- if cp then
- start,done=cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,nil,sequence)
- else
- logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
- end
- else
- 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]
- if cp then
- local ok,n
- start,ok,n=cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,i,sequence)
- if ok then
- done=true
- 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
- start=start.next
- until i>nofchainlookups
- end
- else
- local replacements=ck[7]
- if replacements then
- start,done=chainprocs.reversesub(start,last,kind,chainname,ck,cache,replacements)
- else
- done=true
- if trace_contexts then
- logprocess("%s: skipping match",cref(kind,chainname))
- end
- end
- end
- end
- end
- return start,done
-end
-local verbose_handle_contextchain=function(font,...)
- logwarning("no verbose handler installed, reverting to 'normal'")
- otf.setcontextchain()
- return normal_handle_contextchain(...)
-end
-otf.chainhandlers={
- normal=normal_handle_contextchain,
- verbose=verbose_handle_contextchain,
-}
-function otf.setcontextchain(method)
- if not method or method=="normal" or not otf.chainhandlers[method] then
- if handlers.contextchain then
- logwarning("installing normal contextchain handler")
- end
- handlers.contextchain=normal_handle_contextchain
- else
- logwarning("installing contextchain handler '%s'",method)
- local handler=otf.chainhandlers[method]
- handlers.contextchain=function(...)
- return handler(currentfont,...)
- end
- end
- handlers.gsub_context=handlers.contextchain
- handlers.gsub_contextchain=handlers.contextchain
- handlers.gsub_reversecontextchain=handlers.contextchain
- handlers.gpos_contextchain=handlers.contextchain
- handlers.gpos_context=handlers.contextchain
-end
-otf.setcontextchain()
-local missing={}
-local function logprocess(...)
- if trace_steps then
- registermessage(...)
- end
- logs.report("otf process",...)
-end
-local function logwarning(...)
- logs.report("otf process",...)
-end
-local function report_missing_cache(typ,lookup)
- local f=missing[currentfont] if not f then f={} missing[currentfont]=f end
- local t=f[typ] if not t then t={} f[typ]=t end
- if not t[lookup] then
- t[lookup]=true
- logwarning("missing cache for lookup %s of type %s in font %s (%s)",lookup,typ,currentfont,tfmdata.fullname)
- end
-end
-local resolved={}
-function fonts.methods.node.otf.features(head,font,attr)
- if trace_steps then
- checkstep(head)
- end
- tfmdata=fontdata[font]
- local shared=tfmdata.shared
- otfdata=shared.otfdata
- local luatex=otfdata.luatex
- descriptions=tfmdata.descriptions
- characters=tfmdata.characters
- indices=tfmdata.indices
- unicodes=tfmdata.unicodes
- marks=tfmdata.marks
- anchorlookups=luatex.lookup_to_anchor
- currentfont=font
- rlmode=0
- local featuredata=otfdata.shared.featuredata
- local sequences=luatex.sequences
- lookuptable=luatex.lookups
- local done=false
- local script,language,s_enabled,a_enabled,dyn
- local attribute_driven=attr and attr~=0
- if attribute_driven then
- local features=context_setups[context_numbers[attr]]
- dyn=context_merged[attr] or 0
- language,script=features.language or "dflt",features.script or "dflt"
- a_enabled=features
- if dyn==2 or dyn==-2 then
- s_enabled=shared.features
- end
- else
- language,script=tfmdata.language or "dflt",tfmdata.script or "dflt"
- s_enabled=shared.features
- dyn=0
- end
- local res=resolved[font] if not res then res={} resolved[font]=res end
- local rs=res [script] if not rs then rs={} res [script]=rs end
- local rl=rs [language] if not rl then rl={} rs [language]=rl end
- local ra=rl [attr] if ra==nil then ra={} rl [attr]=ra end
- for s=1,#sequences do
- local pardir,txtdir,success=0,{},false
- local sequence=sequences[s]
- local r=ra[s]
- if r==nil then
- local chain=sequence.chain or 0
- local features=sequence.features
- if not features then
- r=false
- else
- local valid,attribute,kind,what=false,false
- for k,v in next,features do
- local s_e=s_enabled and s_enabled[k]
- local a_e=a_enabled and a_enabled[k]
- if s_e or a_e then
- local l=v[script] or v[wildcard]
- if l then
- if l[language] then
- valid,what=s_e or a_e,language
- elseif l[wildcard] then
- valid,what=s_e or a_e,wildcard
- end
- if valid then
- kind,attribute=k,special_attributes[k] or false
- if a_e and dyn<0 then
- valid=false
- end
- if trace_applied then
- local typ,action=match(sequence.type,"(.*)_(.*)")
- logs.report("otf node mode",
- "%s font: %03i, dynamic: %03i, kind: %s, lookup: %3i, script: %-4s, language: %-4s (%-4s), type: %s, action: %s, name: %s",
- (valid and "+") or "-",font,attr or 0,kind,s,script,language,what,typ,action,sequence.name)
- end
- break
- end
- end
- end
- end
- if valid then
- r={ valid,attribute,chain,kind }
- else
- r=false
- end
- end
- ra[s]=r
- end
- featurevalue=r and r[1]
- if featurevalue then
- local attribute,chain,typ,subtables=r[2],r[3],sequence.type,sequence.subtables
- if chain<0 then
- local handler=handlers[typ]
- local thecache=featuredata[typ] or {}
- local start=find_node_tail(head)
- while start do
- local id=start.id
- if id==glyph then
- if start.subtype<256 and start.font==font then
- local a=has_attribute(start,0)
- if a then
- a=a==attr
- else
- a=true
- end
- if a then
- for i=1,#subtables do
- local lookupname=subtables[i]
- local lookupcache=thecache[lookupname]
- if lookupcache then
- local lookupmatch=lookupcache[start.char]
- if lookupmatch then
- start,success=handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i)
- if success then
- break
- end
- end
- else
- report_missing_cache(typ,lookupname)
- end
- end
- if start then start=start.prev end
- else
- start=start.prev
- end
- else
- start=start.prev
- end
- else
- start=start.prev
- end
- end
- else
- local handler=handlers[typ]
- local ns=#subtables
- local thecache=featuredata[typ] or {}
- local start=head
- rlmode=0
- if ns==1 then
- local lookupname=subtables[1]
- local lookupcache=thecache[lookupname]
- if not lookupcache then
- report_missing_cache(typ,lookupname)
- else
- while start do
- local id=start.id
- if id==glyph then
- if start.subtype<256 and start.font==font then
- local a=has_attribute(start,0)
- if a then
- a=(a==attr) and (not attribute or has_attribute(start,state,attribute))
- else
- a=not attribute or has_attribute(start,state,attribute)
- end
- if a then
- local lookupmatch=lookupcache[start.char]
- if lookupmatch then
- local ok
- start,ok=handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,1)
- if ok then
- success=true
- end
- end
- if start then start=start.next end
- else
- start=start.next
- end
- else
- start=start.next
- end
- elseif id==whatsit then
- 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
- rlmode=pardir
- 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
- end
- end
- end
- else
- while start do
- local id=start.id
- if id==glyph then
- if start.subtype<256 and start.font==font then
- local a=has_attribute(start,0)
- if a then
- a=(a==attr) and (not attribute or has_attribute(start,state,attribute))
- else
- a=not attribute or has_attribute(start,state,attribute)
- end
- if a then
- for i=1,ns do
- local lookupname=subtables[i]
- local lookupcache=thecache[lookupname]
- if lookupcache then
- local lookupmatch=lookupcache[start.char]
- if lookupmatch then
- local ok
- start,ok=handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i)
- if ok then
- success=true
- break
- end
- end
- else
- report_missing_cache(typ,lookupname)
- end
- end
- if start then start=start.next end
- else
- start=start.next
- end
- else
- start=start.next
- end
- elseif id==whatsit then
- 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
- rlmode=pardir
- 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
- end
- end
- end
- end
- if success then
- done=true
- end
- if trace_steps then
- registerstep(head)
- end
- end
- end
- return head,done
-end
-otf.features.prepare={}
-local function split(replacement,original,cache,unicodes)
- local o,t,n={},{},0
- for s in gmatch(original,"[^ ]+") do
- local us=unicodes[s]
- if type(us)=="number" then
- o[#o+1]=us
- else
- o[#o+1]=us[1]
- end
- end
- for s in gmatch(replacement,"[^ ]+") do
- n=n+1
- local us=unicodes[s]
- if type(us)=="number" then
- t[o[n]]=us
- else
- t[o[n]]=us[1]
- end
- end
- return t
-end
-local function uncover(covers,result,cache,unicodes)
- for n=1,#covers do
- local c=covers[n]
- local cc=cache[c]
- if not cc then
- local t={}
- for s in gmatch(c,"[^ ]+") do
- local us=unicodes[s]
- if type(us)=="number" then
- t[us]=true
- else
- for i=1,#us do
- t[us[i]]=true
- end
- end
- end
- cache[c]=t
- result[#result+1]=t
- else
- result[#result+1]=cc
- end
- end
-end
-local function prepare_lookups(tfmdata)
- local otfdata=tfmdata.shared.otfdata
- local featuredata=otfdata.shared.featuredata
- local anchor_to_lookup=otfdata.luatex.anchor_to_lookup
- local lookup_to_anchor=otfdata.luatex.lookup_to_anchor
- local multiple=featuredata.gsub_multiple
- local alternate=featuredata.gsub_alternate
- local single=featuredata.gsub_single
- local ligature=featuredata.gsub_ligature
- local pair=featuredata.gpos_pair
- local position=featuredata.gpos_single
- local kerns=featuredata.gpos_pair
- local mark=featuredata.gpos_mark2mark
- local cursive=featuredata.gpos_cursive
- local unicodes=tfmdata.unicodes
- local indices=tfmdata.indices
- local descriptions=tfmdata.descriptions
- local action={
- substitution=function(p,lookup,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
- end,
- multiple=function (p,lookup,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
- end,
- alternate=function(p,lookup,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
- end,
- ligature=function (p,lookup,glyph,unicode)
- local first=true
- local t=ligature[lookup]
- if not t then t={} ligature[lookup]=t end
- for s in gmatch(p[2],"[^ ]+") do
- if first then
- local u=unicodes[s]
- if not u then
- logs.report("define otf","lookup %s: ligature %s => %s ignored due to invalid unicode",lookup,p[2],glyph.name)
- break
- elseif type(u)=="number" then
- if not t[u] then
- t[u]={ {} }
- end
- t=t[u]
- else
- local tt=t
- local tu
- for i=1,#u do
- local u=u[i]
- if i==1 then
- if not t[u] then
- t[u]={ {} }
- end
- tu=t[u]
- t=tu
- else
- if not t[u] then
- tt[u]=tu
- end
- end
- end
- end
- first=false
- else
- s=unicodes[s]
- local t1=t[1]
- if not t1[s] then
- t1[s]={ {} }
- end
- t=t1[s]
- end
- end
- t[2]=unicode
- end,
- position=function(p,lookup,glyph,unicode)
- local s=position[lookup]
- if not s then s={} position[lookup]=s end
- s[unicode]=p[2]
- end,
- pair=function(p,lookup,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
- 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
- else
- for i=1,#upc do
- others[upc[i]]=p
- end
- end
- end
- elseif type(upc)=="number" then
- others[upc]=p
- else
- for i=1,#upc do
- others[upc[i]]=p
- end
- 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,glyph,unicode)
- end
- end
- local lookups=glyph.mlookups
- if lookups then
- for lookup,whatever in next,lookups do
- for i=1,#whatever do
- local p=whatever[i]
- action[p[1]](p,lookup,glyph,unicode)
- end
- end
- end
- local list=glyph.mykerns
- if list then
- for lookup,krn in next,list do
- local k=kerns[lookup]
- if not k then k={} kerns[lookup]=k end
- k[unicode]=krn
- end
- end
- local oanchor=glyph.anchors
- if oanchor then
- for typ,anchors in next,oanchor do
- if typ=="mark" then
- for name,anchor in next,anchors do
- local lookups=anchor_to_lookup[name]
- if lookups then
- for lookup,_ in next,lookups do
- local f=mark[lookup]
- if not f then f={} mark[lookup]=f end
- f[unicode]=anchors
- end
- end
- end
- elseif typ=="cexit" then
- for name,anchor in next,anchors do
- local lookups=anchor_to_lookup[name]
- if lookups then
- for lookup,_ in next,lookups do
- local f=cursive[lookup]
- if not f then f={} cursive[lookup]=f end
- f[unicode]=anchors
- end
- end
- end
- end
- end
- end
- end
-end
-luatex=luatex or {}
-local function prepare_contextchains(tfmdata)
- local otfdata=tfmdata.shared.otfdata
- local lookups=otfdata.lookups
- if lookups then
- local featuredata=otfdata.shared.featuredata
- local contextchain=featuredata.gsub_contextchain
- local reversecontextchain=featuredata.gsub_reversecontextchain
- local characters=tfmdata.characters
- local unicodes=tfmdata.unicodes
- local indices=tfmdata.indices
- local cache=luatex.covers
- if not cache then
- cache={}
- luatex.covers=cache
- end
- for lookupname,lookupdata in next,otfdata.lookups do
- local lookuptype=lookupdata.type
- if not lookuptype then
- logs.report("otf process","missing lookuptype for %s",lookupname)
- else
- local rules=lookupdata.rules
- if rules then
- local fmt=lookupdata.format
- if fmt=="coverage" then
- if lookuptype~="chainsub" and lookuptype~="chainpos" then
- logs.report("otf process","unsupported coverage %s for %s",lookuptype,lookupname)
- else
- local contexts=contextchain[lookupname]
- if not contexts then
- contexts={}
- contextchain[lookupname]=contexts
- end
- local t={}
- for nofrules=1,#rules do
- local rule=rules[nofrules]
- local coverage=rule.coverage
- if coverage and coverage.current then
- local current,before,after,sequence=coverage.current,coverage.before,coverage.after,{}
- if before then
- uncover(before,sequence,cache,unicodes)
- end
- local start=#sequence+1
- uncover(current,sequence,cache,unicodes)
- local stop=#sequence
- if after then
- uncover(after,sequence,cache,unicodes)
- end
- if sequence[1] then
- t[#t+1]={ nofrules,lookuptype,sequence,start,stop,rule.lookups }
- for unic,_ in next,sequence[start] do
- local cu=contexts[unic]
- if not cu then
- contexts[unic]=t
- end
- end
- end
- end
- end
- end
- elseif fmt=="reversecoverage" then
- if lookuptype~="reversesub" then
- logs.report("otf process","unsupported reverse coverage %s for %s",lookuptype,lookupname)
- else
- local contexts=reversecontextchain[lookupname]
- if not contexts then
- contexts={}
- reversecontextchain[lookupname]=contexts
- end
- local t={}
- for nofrules=1,#rules do
- local rule=rules[nofrules]
- local reversecoverage=rule.reversecoverage
- if reversecoverage and reversecoverage.current then
- local current,before,after,replacements,sequence=reversecoverage.current,reversecoverage.before,reversecoverage.after,reversecoverage.replacements,{}
- if before then
- uncover(before,sequence,cache,unicodes)
- end
- local start=#sequence+1
- uncover(current,sequence,cache,unicodes)
- local stop=#sequence
- if after then
- uncover(after,sequence,cache,unicodes)
- end
- if replacements then
- replacements=split(replacements,current[1],cache,unicodes)
- end
- if sequence[1] then
- t[#t+1]={ nofrules,lookuptype,sequence,start,stop,rule.lookups,replacements }
- for unic,_ in next,sequence[start] do
- local cu=contexts[unic]
- if not cu then
- contexts[unic]=t
- end
- end
- end
- end
- end
- end
- elseif fmt=="glyphs" then
- if lookuptype~="chainsub" and lookuptype~="chainpos" then
- logs.report("otf process","unsupported coverage %s for %s",lookuptype,lookupname)
- else
- local contexts=contextchain[lookupname]
- if not contexts then
- contexts={}
- contextchain[lookupname]=contexts
- end
- local t={}
- for nofrules=1,#rules do
- local rule=rules[nofrules]
- local glyphs=rule.glyphs
- if glyphs and glyphs.names then
- local fore,back,names,sequence=glyphs.fore,glyphs.back,glyphs.names,{}
- if fore and fore~="" then
- fore=lpegmatch(split_at_space,fore)
- uncover(fore,sequence,cache,unicodes)
- end
- local start=#sequence+1
- names=lpegmatch(split_at_space,names)
- uncover(names,sequence,cache,unicodes)
- local stop=#sequence
- if back and back~="" then
- back=lpegmatch(split_at_space,back)
- uncover(back,sequence,cache,unicodes)
- end
- if sequence[1] then
- t[#t+1]={ nofrules,lookuptype,sequence,start,stop,rule.lookups }
- for unic,_ in next,sequence[start] do
- local cu=contexts[unic]
- if not cu then
- contexts[unic]=t
- end
- end
- end
- end
- end
- end
- end
- end
- end
- end
- end
-end
-function fonts.initializers.node.otf.features(tfmdata,value)
- if true then
- if not tfmdata.shared.otfdata.shared.initialized then
- local t=trace_preparing and os.clock()
- local otfdata=tfmdata.shared.otfdata
- local featuredata=otfdata.shared.featuredata
- featuredata.gsub_multiple={}
- featuredata.gsub_alternate={}
- featuredata.gsub_single={}
- featuredata.gsub_ligature={}
- featuredata.gsub_contextchain={}
- featuredata.gsub_reversecontextchain={}
- featuredata.gpos_pair={}
- featuredata.gpos_single={}
- featuredata.gpos_mark2base={}
- featuredata.gpos_mark2ligature=featuredata.gpos_mark2base
- featuredata.gpos_mark2mark=featuredata.gpos_mark2base
- featuredata.gpos_cursive={}
- featuredata.gpos_contextchain=featuredata.gsub_contextchain
- featuredata.gpos_reversecontextchain=featuredata.gsub_reversecontextchain
- prepare_contextchains(tfmdata)
- prepare_lookups(tfmdata)
- otfdata.shared.initialized=true
- if trace_preparing then
- logs.report("otf process","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?")
- end
- end
- end
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-ota']={
- version=1.001,
- comment="companion to font-otf.lua (analysing)",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local type,tostring,match,format,concat=type,tostring,string.match,string.format,table.concat
-if not trackers then trackers={ register=function() end } end
-local trace_analyzing=false trackers.register("otf.analyzing",function(v) trace_analyzing=v end)
-local trace_cjk=false trackers.register("cjk.injections",function(v) trace_cjk=v end)
-trackers.register("cjk.analyzing","otf.analyzing")
-fonts=fonts or {}
-fonts.analyzers=fonts.analyzers or {}
-fonts.analyzers.initializers=fonts.analyzers.initializers or { node={ otf={} } }
-fonts.analyzers.methods=fonts.analyzers.methods or { node={ otf={} } }
-local otf=fonts.otf
-local tfm=fonts.tfm
-local initializers=fonts.analyzers.initializers
-local methods=fonts.analyzers.methods
-local glyph=node.id('glyph')
-local glue=node.id('glue')
-local penalty=node.id('penalty')
-local set_attribute=node.set_attribute
-local has_attribute=node.has_attribute
-local traverse_id=node.traverse_id
-local traverse_node_list=node.traverse
-local fontdata=fonts.ids
-local state=attributes.private('state')
-local fcs=(fonts.color and fonts.color.set) or function() end
-local fcr=(fonts.color and fonts.color.reset) or function() end
-local a_to_script=otf.a_to_script
-local a_to_language=otf.a_to_language
-function fonts.initializers.node.otf.analyze(tfmdata,value,attr)
- local script,language
- if attr and attr>0 then
- script,language=a_to_script[attr],a_to_language[attr]
- else
- script,language=tfmdata.script,tfmdata.language
- end
- local action=initializers[script]
- if action then
- if type(action)=="function" then
- return action(tfmdata,value)
- else
- local action=action[language]
- if action then
- return action(tfmdata,value)
- end
- end
- end
- return nil
-end
-function fonts.methods.node.otf.analyze(head,font,attr)
- local tfmdata=fontdata[font]
- local script,language
- if attr and attr>0 then
- script,language=a_to_script[attr],a_to_language[attr]
- else
- script,language=tfmdata.script,tfmdata.language
- end
- local action=methods[script]
- if action then
- if type(action)=="function" then
- return action(head,font,attr)
- else
- action=action[language]
- if action then
- return action(head,font,attr)
- end
- end
- end
- return head,false
-end
-otf.features.register("analyze",true)
-table.insert(fonts.triggers,"analyze")
-fonts.analyzers.methods.latn=fonts.analyzers.aux.setstate
-local zwnj=0x200C
-local zwj=0x200D
-local isol={
- [0x0600]=true,[0x0601]=true,[0x0602]=true,[0x0603]=true,
- [0x0608]=true,[0x060B]=true,[0x0621]=true,[0x0674]=true,
- [0x06DD]=true,[zwnj]=true,
-}
-local isol_fina={
- [0x0622]=true,[0x0623]=true,[0x0624]=true,[0x0625]=true,
- [0x0627]=true,[0x0629]=true,[0x062F]=true,[0x0630]=true,
- [0x0631]=true,[0x0632]=true,[0x0648]=true,[0x0671]=true,
- [0x0672]=true,[0x0673]=true,[0x0675]=true,[0x0676]=true,
- [0x0677]=true,[0x0688]=true,[0x0689]=true,[0x068A]=true,
- [0x068B]=true,[0x068C]=true,[0x068D]=true,[0x068E]=true,
- [0x068F]=true,[0x0690]=true,[0x0691]=true,[0x0692]=true,
- [0x0693]=true,[0x0694]=true,[0x0695]=true,[0x0696]=true,
- [0x0697]=true,[0x0698]=true,[0x0699]=true,[0x06C0]=true,
- [0x06C3]=true,[0x06C4]=true,[0x06C5]=true,[0x06C6]=true,
- [0x06C7]=true,[0x06C8]=true,[0x06C9]=true,[0x06CA]=true,
- [0x06CB]=true,[0x06CD]=true,[0x06CF]=true,[0x06D2]=true,
- [0x06D3]=true,[0x06D5]=true,[0x06EE]=true,[0x06EF]=true,
- [0x0759]=true,[0x075A]=true,[0x075B]=true,[0x076B]=true,
- [0x076C]=true,[0x0771]=true,[0x0773]=true,[0x0774]=true,
- [0x0778]=true,[0x0779]=true,[0xFEF5]=true,[0xFEF7]=true,
- [0xFEF9]=true,[0xFEFB]=true,
-}
-local isol_fina_medi_init={
- [0x0626]=true,[0x0628]=true,[0x062A]=true,[0x062B]=true,
- [0x062C]=true,[0x062D]=true,[0x062E]=true,[0x0633]=true,
- [0x0634]=true,[0x0635]=true,[0x0636]=true,[0x0637]=true,
- [0x0638]=true,[0x0639]=true,[0x063A]=true,[0x063B]=true,
- [0x063C]=true,[0x063D]=true,[0x063E]=true,[0x063F]=true,
- [0x0640]=true,[0x0641]=true,[0x0642]=true,[0x0643]=true,
- [0x0644]=true,[0x0645]=true,[0x0646]=true,[0x0647]=true,
- [0x0649]=true,[0x064A]=true,[0x066E]=true,[0x066F]=true,
- [0x0678]=true,[0x0679]=true,[0x067A]=true,[0x067B]=true,
- [0x067C]=true,[0x067D]=true,[0x067E]=true,[0x067F]=true,
- [0x0680]=true,[0x0681]=true,[0x0682]=true,[0x0683]=true,
- [0x0684]=true,[0x0685]=true,[0x0686]=true,[0x0687]=true,
- [0x069A]=true,[0x069B]=true,[0x069C]=true,[0x069D]=true,
- [0x069E]=true,[0x069F]=true,[0x06A0]=true,[0x06A1]=true,
- [0x06A2]=true,[0x06A3]=true,[0x06A4]=true,[0x06A5]=true,
- [0x06A6]=true,[0x06A7]=true,[0x06A8]=true,[0x06A9]=true,
- [0x06AA]=true,[0x06AB]=true,[0x06AC]=true,[0x06AD]=true,
- [0x06AE]=true,[0x06AF]=true,[0x06B0]=true,[0x06B1]=true,
- [0x06B2]=true,[0x06B3]=true,[0x06B4]=true,[0x06B5]=true,
- [0x06B6]=true,[0x06B7]=true,[0x06B8]=true,[0x06B9]=true,
- [0x06BA]=true,[0x06BB]=true,[0x06BC]=true,[0x06BD]=true,
- [0x06BE]=true,[0x06BF]=true,[0x06C1]=true,[0x06C2]=true,
- [0x06CC]=true,[0x06CE]=true,[0x06D0]=true,[0x06D1]=true,
- [0x06FA]=true,[0x06FB]=true,[0x06FC]=true,[0x06FF]=true,
- [0x0750]=true,[0x0751]=true,[0x0752]=true,[0x0753]=true,
- [0x0754]=true,[0x0755]=true,[0x0756]=true,[0x0757]=true,
- [0x0758]=true,[0x075C]=true,[0x075D]=true,[0x075E]=true,
- [0x075F]=true,[0x0760]=true,[0x0761]=true,[0x0762]=true,
- [0x0763]=true,[0x0764]=true,[0x0765]=true,[0x0766]=true,
- [0x0767]=true,[0x0768]=true,[0x0769]=true,[0x076A]=true,
- [0x076D]=true,[0x076E]=true,[0x076F]=true,[0x0770]=true,
- [0x0772]=true,[0x0775]=true,[0x0776]=true,[0x0777]=true,
- [0x077A]=true,[0x077B]=true,[0x077C]=true,[0x077D]=true,
- [0x077E]=true,[0x077F]=true,[zwj]=true,
-}
-local arab_warned={}
-local function warning(current,what)
- local char=current.char
- if not arab_warned[char] then
- log.report("analyze","arab: character %s (U+%04X) has no %s class",char,char,what)
- arab_warned[char]=true
- end
-end
-function fonts.analyzers.methods.nocolor(head,font,attr)
- for n in traverse_node_list(head,glyph) do
- if not font or n.font==font then
- fcr(n)
- end
- end
- return head,true
-end
-local function finish(first,last)
- if last then
- if first==last then
- local fc=first.char
- if isol_fina_medi_init[fc] or isol_fina[fc] then
- set_attribute(first,state,4)
- if trace_analyzing then fcs(first,"font:isol") end
- else
- warning(first,"isol")
- set_attribute(first,state,0)
- if trace_analyzing then fcr(first) end
- end
- else
- local lc=last.char
- if isol_fina_medi_init[lc] or isol_fina[lc] then
- set_attribute(last,state,3)
- if trace_analyzing then fcs(last,"font:fina") end
- else
- warning(last,"fina")
- set_attribute(last,state,0)
- if trace_analyzing then fcr(last) end
- end
- end
- first,last=nil,nil
- elseif first then
- local fc=first.char
- if isol_fina_medi_init[fc] or isol_fina[fc] then
- set_attribute(first,state,4)
- if trace_analyzing then fcs(first,"font:isol") end
- else
- warning(first,"isol")
- set_attribute(first,state,0)
- if trace_analyzing then fcr(first) end
- end
- first=nil
- end
- return first,last
-end
-function fonts.analyzers.methods.arab(head,font,attr)
- local tfmdata=fontdata[font]
- local marks=tfmdata.marks
- local first,last,current,done=nil,nil,head,false
- while current do
- if current.id==glyph and current.subtype<256 and current.font==font and not has_attribute(current,state) then
- done=true
- local char=current.char
- if marks[char] then
- set_attribute(current,state,5)
- if trace_analyzing then fcs(current,"font:mark") end
- elseif isol[char] then
- first,last=finish(first,last)
- set_attribute(current,state,4)
- if trace_analyzing then fcs(current,"font:isol") end
- first,last=nil,nil
- elseif not first then
- if isol_fina_medi_init[char] then
- set_attribute(current,state,1)
- if trace_analyzing then fcs(current,"font:init") end
- first,last=first or current,current
- elseif isol_fina[char] then
- set_attribute(current,state,4)
- if trace_analyzing then fcs(current,"font:isol") end
- first,last=nil,nil
- else
- first,last=finish(first,last)
- end
- elseif isol_fina_medi_init[char] then
- first,last=first or current,current
- set_attribute(current,state,2)
- if trace_analyzing then fcs(current,"font:medi") end
- elseif isol_fina[char] then
- if not has_attribute(last,state,1) then
- set_attribute(last,state,2)
- if trace_analyzing then fcs(last,"font:medi") end
- end
- set_attribute(current,state,3)
- if trace_analyzing then fcs(current,"font:fina") end
- first,last=nil,nil
- elseif char>=0x0600 and char<=0x06FF then
- if trace_analyzing then fcs(current,"font:rest") end
- first,last=finish(first,last)
- else
- first,last=finish(first,last)
- end
- else
- first,last=finish(first,last)
- end
- current=current.next
- end
- first,last=finish(first,last)
- return head,done
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-otc']={
- version=1.001,
- comment="companion to font-otf.lua (context)",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-local format,insert=string.format,table.insert
-local type,next=type,next
-local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
-local otf=fonts.otf
-local tfm=fonts.tfm
-local extra_lists={
- tlig={
- {
- endash="hyphen hyphen",
- emdash="hyphen hyphen hyphen",
- quotedblleft="quoteleft quoteleft",
- quotedblright="quoteright quoteright",
- quotedblleft="grave grave",
- quotedblright="quotesingle quotesingle",
- quotedblbase="comma comma",
- exclamdown="exclam grave",
- questiondown="question grave",
- guillemotleft="less less",
- guillemotright="greater greater",
- },
- },
- trep={
- {
- [0x0022]=0x201D,
- [0x0027]=0x2019,
- [0x0060]=0x2018,
- },
- },
- anum={
- {
- [0x0030]=0x0660,
- [0x0031]=0x0661,
- [0x0032]=0x0662,
- [0x0033]=0x0663,
- [0x0034]=0x0664,
- [0x0035]=0x0665,
- [0x0036]=0x0666,
- [0x0037]=0x0667,
- [0x0038]=0x0668,
- [0x0039]=0x0669,
- },
- {
- [0x0030]=0x06F0,
- [0x0031]=0x06F1,
- [0x0032]=0x06F2,
- [0x0033]=0x06F3,
- [0x0034]=0x06F4,
- [0x0035]=0x06F5,
- [0x0036]=0x06F6,
- [0x0037]=0x06F7,
- [0x0038]=0x06F8,
- [0x0039]=0x06F9,
- },
- },
-}
-local extra_features={
- tlig={
- {
- features={ { scripts={ { script="*",langs={ "*" },} },tag="tlig",comment="added bij mkiv" },},
- name="ctx_tlig_1",
- subtables={ { name="ctx_tlig_1_s" } },
- type="gsub_ligature",
- flags={},
- },
- },
- trep={
- {
- features={ { scripts={ { script="*",langs={ "*" },} },tag="trep",comment="added bij mkiv" },},
- name="ctx_trep_1",
- subtables={ { name="ctx_trep_1_s" } },
- type="gsub_single",
- flags={},
- },
- },
- anum={
- {
- features={ { scripts={ { script="arab",langs={ "dflt","ARA" },} },tag="anum",comment="added bij mkiv" },},
- name="ctx_anum_1",
- subtables={ { name="ctx_anum_1_s" } },
- type="gsub_single",
- flags={},
- },
- {
- features={ { scripts={ { script="arab",langs={ "FAR" },} },tag="anum",comment="added bij mkiv" },},
- name="ctx_anum_2",
- subtables={ { name="ctx_anum_2_s" } },
- type="gsub_single",
- flags={},
- },
- },
-}
-fonts.otf.enhancers["add some missing characters"]=function(data,filename)
-end
-fonts.otf.enhancers["enrich with features"]=function(data,filename)
- local used={}
- for i=1,#otf.glists do
- local g=data[otf.glists[i]]
- if g then
- for i=1,#g do
- local f=g[i].features
- if f then
- for i=1,#f do
- local t=f[i].tag
- if t then used[t]=true end
- end
- end
- end
- end
- end
- local glyphs=data.glyphs
- local indices=data.map.map
- data.gsub=data.gsub or {}
- for kind,specifications in next,extra_features do
- if not used[kind] then
- local done=0
- for s=1,#specifications do
- local added=false
- local specification=specifications[s]
- local list=extra_lists[kind][s]
- local name=specification.name.."_s"
- if specification.type=="gsub_ligature" then
- for unicode,index in next,indices do
- local glyph=glyphs[index]
- local ligature=list[glyph.name]
- if ligature then
- local o=glyph.lookups or {}
- o[name]={
- {
- ["type"]="ligature",
- ["specification"]={
- char=glyph.name,
- components=ligature,
- }
- }
- }
- glyph.lookups,done,added=o,done+1,true
- end
- end
- elseif specification.type=="gsub_single" then
- for unicode,index in next,indices do
- local glyph=glyphs[index]
- local r=list[unicode]
- if r then
- local replacement=indices[r]
- if replacement and glyphs[replacement] then
- local o=glyph.lookups or {}
- o[name]={
- {
- ["type"]="substitution",
- ["specification"]={
- variant=glyphs[replacement].name,
- }
- }
- }
- glyph.lookups,done,added=o,done+1,true
- end
- end
- end
- end
- if added then
- insert(data.gsub,s,table.fastcopy(specification))
- end
- end
- if done>0 then
- if trace_loading then
- logs.report("load otf","enhance: registering %s feature (%s glyphs affected)",kind,done)
- end
- end
- end
- end
-end
-otf.tables.features['tlig']='TeX Ligatures'
-otf.tables.features['trep']='TeX Replacements'
-otf.tables.features['anum']='Arabic Digits'
-otf.features.register_base_substitution('tlig')
-otf.features.register_base_substitution('trep')
-otf.features.register_base_substitution('anum')
-fonts.initializers.base.otf.equaldigits=fonts.initializers.common.equaldigits
-fonts.initializers.node.otf.equaldigits=fonts.initializers.common.equaldigits
-fonts.initializers.base.otf.lineheight=fonts.initializers.common.lineheight
-fonts.initializers.node.otf.lineheight=fonts.initializers.common.lineheight
-fonts.initializers.base.otf.compose=fonts.initializers.common.compose
-fonts.initializers.node.otf.compose=fonts.initializers.common.compose
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-def']={
- 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 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)
-trackers.register("fonts.loading","fonts.defining","otf.loading","afm.loading","tfm.loading")
-trackers.register("fonts.all","fonts.*","otf.*","afm.*","tfm.*")
-fonts=fonts or {}
-fonts.define=fonts.define or {}
-fonts.tfm=fonts.tfm or {}
-fonts.ids=fonts.ids or {}
-fonts.vf=fonts.vf or {}
-fonts.used=fonts.used or {}
-local tfm=fonts.tfm
-local vf=fonts.vf
-local define=fonts.define
-tfm.version=1.01
-tfm.cache=containers.define("fonts","tfm",tfm.version,false)
-define.method="afm or tfm"
-define.specify=fonts.define.specify or {}
-define.methods=fonts.define.methods or {}
-tfm.fonts=tfm.fonts or {}
-tfm.readers=tfm.readers or {}
-tfm.internalized=tfm.internalized or {}
-tfm.readers.sequence={ 'otf','ttf','afm','tfm' }
-tfm.auto_afm=true
-local readers=tfm.readers
-local sequence=readers.sequence
-fonts.version=1.05
-fonts.cache=containers.define("fonts","def",fonts.version,false)
-local splitter,specifiers=nil,""
-local P,C,S,Cc=lpeg.P,lpeg.C,lpeg.S,lpeg.Cc
-local left=P("(")
-local right=P(")")
-local colon=P(":")
-local space=P(" ")
-define.defaultlookup="file"
-local prefixpattern=P(false)
-function define.add_specifier(symbol)
- specifiers=specifiers..symbol
- local method=S(specifiers)
- local lookup=C(prefixpattern)*colon
- local sub=left*C(P(1-left-right-method)^1)*right
- local specification=C(method)*C(P(1)^1)
- local name=C((1-sub-specification)^1)
- splitter=P((lookup+Cc(""))*name*(sub+Cc(""))*(specification+Cc("")))
-end
-function define.add_lookup(str,default)
- prefixpattern=prefixpattern+P(str)
-end
-define.add_lookup("file")
-define.add_lookup("name")
-define.add_lookup("spec")
-function define.get_specification(str)
- return lpegmatch(splitter,str)
-end
-function define.register_split(symbol,action)
- define.add_specifier(symbol)
- define.specify[symbol]=action
-end
-function define.makespecification(specification,lookup,name,sub,method,detail,size)
- size=size or 655360
- if trace_defining then
- logs.report("define font","%s -> lookup: %s, name: %s, sub: %s, method: %s, detail: %s",
- specification,(lookup~="" and lookup) or "[file]",(name~="" and name) or "-",
- (sub~="" and sub) or "-",(method~="" and method) or "-",(detail~="" and detail) or "-")
- end
- if not lookup or lookup=="" then
- lookup=define.defaultlookup
- end
- local t={
- lookup=lookup,
- specification=specification,
- size=size,
- name=name,
- sub=sub,
- method=method,
- detail=detail,
- resolved="",
- forced="",
- features={},
- }
- return t
-end
-function define.analyze(specification,size)
- local lookup,name,sub,method,detail=define.get_specification(specification or "")
- return define.makespecification(specification,lookup,name,sub,method,detail,size)
-end
-local sortedhashkeys=table.sortedhashkeys
-function tfm.hash_features(specification)
- local features=specification.features
- if features then
- local t={}
- local normal=features.normal
- if normal and next(normal) then
- local f=sortedhashkeys(normal)
- for i=1,#f do
- local v=f[i]
- if v~="number" and v~="features" then
- t[#t+1]=v..'='..tostring(normal[v])
- end
- end
- end
- local vtf=features.vtf
- if vtf and next(vtf) then
- local f=sortedhashkeys(vtf)
- for i=1,#f do
- local v=f[i]
- t[#t+1]=v..'='..tostring(vtf[v])
- end
- end
- if #t>0 then
- return concat(t,"+")
- end
- end
- return "unknown"
-end
-fonts.designsizes={}
-function tfm.hash_instance(specification,force)
- local hash,size,fallbacks=specification.hash,specification.size,specification.fallbacks
- if force or not hash then
- hash=tfm.hash_features(specification)
- specification.hash=hash
- end
- if size<1000 and fonts.designsizes[hash] then
- size=math.round(tfm.scaled(size,fonts.designsizes[hash]))
- specification.size=size
- end
- if fallbacks then
- return hash..' @ '..tostring(size)..' @ '..fallbacks
- else
- return hash..' @ '..tostring(size)
- end
-end
-define.resolvers=resolvers
-function define.resolvers.file(specification)
- local suffix=file.suffix(specification.name)
- if fonts.formats[suffix] then
- specification.forced=suffix
- specification.name=file.removesuffix(specification.name)
- end
-end
-function define.resolvers.name(specification)
- local resolve=fonts.names.resolve
- if resolve then
- local resolved,sub=fonts.names.resolve(specification)
- specification.resolved,specification.sub=resolved,sub
- if resolved then
- local suffix=file.suffix(resolved)
- if fonts.formats[suffix] then
- specification.forced=suffix
- specification.name=file.removesuffix(resolved)
- else
- specification.name=resolved
- end
- end
- else
- define.resolvers.file(specification)
- end
-end
-function define.resolvers.spec(specification)
- local resolvespec=fonts.names.resolvespec
- if resolvespec then
- specification.resolved,specification.sub=fonts.names.resolvespec(specification)
- if specification.resolved then
- specification.forced=file.extname(specification.resolved)
- specification.name=file.removesuffix(specification.resolved)
- end
- else
- define.resolvers.name(specification)
- end
-end
-function define.resolve(specification)
- if not specification.resolved or specification.resolved=="" then
- local r=define.resolvers[specification.lookup]
- if r then
- r(specification)
- end
- end
- if specification.forced=="" then
- specification.forced=nil
- else
- specification.forced=specification.forced
- end
- specification.hash=lower(specification.name..' @ '..tfm.hash_features(specification))
- if specification.sub and specification.sub~="" then
- specification.hash=specification.sub..' @ '..specification.hash
- end
- return specification
-end
-function tfm.read(specification)
- local hash=tfm.hash_instance(specification)
- local tfmtable=tfm.fonts[hash]
- if not tfmtable then
- local forced=specification.forced or ""
- if forced~="" then
- tfmtable=readers[lower(forced)](specification)
- if not tfmtable then
- logs.report("define font","forced type %s of %s not found",forced,specification.name)
- end
- else
- for s=1,#sequence do
- local reader=sequence[s]
- if readers[reader] then
- if trace_defining then
- 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
- break
- else
- specification.filename=nil
- end
- end
- end
- end
- if tfmtable then
- if directive_embedall then
- tfmtable.embedding="full"
- elseif tfmtable.filename and fonts.dontembed[tfmtable.filename] then
- tfmtable.embedding="no"
- else
- tfmtable.embedding="subset"
- end
- tfm.fonts[hash]=tfmtable
- fonts.designsizes[specification.hash]=tfmtable.designsize
- end
- end
- if not tfmtable then
- logs.report("define font","font with name %s is not found",specification.name)
- end
- return tfmtable
-end
-function tfm.read_and_define(name,size)
- local specification=define.analyze(name,size)
- local method=specification.method
- if method and define.specify[method] then
- specification=define.specify[method](specification)
- end
- specification=define.resolve(specification)
- local hash=tfm.hash_instance(specification)
- local id=define.registered(hash)
- if not id then
- local fontdata=tfm.read(specification)
- if fontdata then
- fontdata.hash=hash
- id=font.define(fontdata)
- define.register(fontdata,id)
- tfm.cleanup_table(fontdata)
- else
- id=0
- end
- end
- return fonts.ids[id],id
-end
-local function check_tfm(specification,fullname)
- local foundname=resolvers.findbinfile(fullname,'tfm') or ""
- if foundname=="" then
- foundname=resolvers.findbinfile(fullname,'ofm') or ""
- end
- if foundname~="" then
- specification.filename,specification.format=foundname,"ofm"
- return tfm.read_from_tfm(specification)
- end
-end
-local function check_afm(specification,fullname)
- local foundname=resolvers.findbinfile(fullname,'afm') or ""
- if foundname=="" and tfm.auto_afm then
- local encoding,shortname=match(fullname,"^(.-)%-(.*)$")
- if encoding and shortname and fonts.enc.known[encoding] then
- shortname=resolvers.findbinfile(shortname,'afm') or ""
- if shortname~="" then
- foundname=shortname
- if trace_loading then
- logs.report("load afm","stripping encoding prefix from filename %s",afmname)
- end
- end
- end
- end
- if foundname~="" then
- specification.filename,specification.format=foundname,"afm"
- return tfm.read_from_afm(specification)
- end
-end
-function readers.tfm(specification)
- local fullname,tfmtable=specification.filename or "",nil
- if fullname=="" then
- local forced=specification.forced or ""
- if forced~="" then
- tfmtable=check_tfm(specification,specification.name.."."..forced)
- end
- if not tfmtable then
- tfmtable=check_tfm(specification,specification.name)
- end
- else
- tfmtable=check_tfm(specification,fullname)
- end
- return tfmtable
-end
-function readers.afm(specification,method)
- local fullname,tfmtable=specification.filename or "",nil
- if fullname=="" then
- local forced=specification.forced or ""
- if forced~="" then
- tfmtable=check_afm(specification,specification.name.."."..forced)
- end
- if not tfmtable then
- method=method or define.method or "afm or tfm"
- if method=="tfm" then
- tfmtable=check_tfm(specification,specification.name)
- elseif method=="afm" then
- tfmtable=check_afm(specification,specification.name)
- elseif method=="tfm or afm" then
- tfmtable=check_tfm(specification,specification.name) or check_afm(specification,specification.name)
- else
- tfmtable=check_afm(specification,specification.name) or check_tfm(specification,specification.name)
- end
- end
- else
- tfmtable=check_afm(specification,fullname)
- end
- return tfmtable
-end
-local function check_otf(forced,specification,suffix,what)
- local name=specification.name
- if forced then
- name=file.addsuffix(name,suffix,true)
- end
- local fullname,tfmtable=resolvers.findbinfile(name,suffix) or "",nil
- if fullname=="" then
- local fb=fonts.names.old_to_new[name]
- if fb then
- fullname=resolvers.findbinfile(fb,suffix) or ""
- end
- end
- if fullname=="" then
- local fb=fonts.names.new_to_old[name]
- if fb then
- fullname=resolvers.findbinfile(fb,suffix) or ""
- end
- end
- if fullname~="" then
- specification.filename,specification.format=fullname,what
- tfmtable=tfm.read_from_open_type(specification)
- end
- return tfmtable
-end
-function readers.opentype(specification,suffix,what)
- local forced=specification.forced or ""
- if forced=="otf" then
- return check_otf(true,specification,forced,"opentype")
- elseif forced=="ttf" or forced=="ttc" or forced=="dfont" then
- return check_otf(true,specification,forced,"truetype")
- else
- return check_otf(false,specification,suffix,what)
- end
-end
-function readers.otf (specification) return readers.opentype(specification,"otf","opentype") end
-function readers.ttf (specification) return readers.opentype(specification,"ttf","truetype") end
-function readers.ttc (specification) return readers.opentype(specification,"ttf","truetype") end
-function readers.dfont(specification) return readers.opentype(specification,"ttf","truetype") end
-function define.check(features,defaults)
- local done=false
- if features and next(features) then
- for k,v in next,defaults do
- if features[k]==nil then
- features[k],done=v,true
- end
- end
- else
- features,done=table.fastcopy(defaults),true
- end
- return features,done
-end
-define.last=nil
-function define.register(fontdata,id)
- if fontdata and id then
- local hash=fontdata.hash
- if not tfm.internalized[hash] then
- if trace_defining then
- logs.report("define font","loading at 2 id %s, hash: %s",id or "?",hash or "?")
- end
- fonts.identifiers[id]=fontdata
- fonts.characters [id]=fontdata.characters
- fonts.quads [id]=fontdata.parameters.quad
- tfm.internalized[hash]=id
- end
- end
-end
-function define.registered(hash)
- local id=tfm.internalized[hash]
- return id,id and fonts.ids[id]
-end
-local cache_them=false
-function tfm.make(specification)
- local fvm=define.methods[specification.features.vtf.preset]
- if fvm then
- return fvm(specification)
- else
- return nil
- end
-end
-function define.read(specification,size,id)
- statistics.starttiming(fonts)
- if type(specification)=="string" then
- specification=define.analyze(specification,size)
- end
- local method=specification.method
- if method and define.specify[method] then
- specification=define.specify[method](specification)
- end
- specification=define.resolve(specification)
- local hash=tfm.hash_instance(specification)
- if cache_them then
- local fontdata=containers.read(fonts.cache,hash)
- end
- local fontdata=define.registered(hash)
- if not fontdata then
- if specification.features.vtf and specification.features.vtf.preset then
- fontdata=tfm.make(specification)
- else
- fontdata=tfm.read(specification)
- if fontdata then
- tfm.check_virtual_id(fontdata)
- end
- end
- if cache_them then
- fontdata=containers.write(fonts.cache,hash,fontdata)
- end
- if fontdata then
- fontdata.hash=hash
- fontdata.cache="no"
- if id then
- define.register(fontdata,id)
- end
- end
- end
- define.last=fontdata or id
- if not fontdata then
- logs.report("define font","unknown font %s, loading aborted",specification.name)
- elseif trace_defining and type(fontdata)=="table" then
- logs.report("define font","using %s font with id %s, name:%s size:%s bytes:%s encoding:%s fullname:%s filename:%s",
- fontdata.type or "unknown",
- id or "?",
- fontdata.name or "?",
- fontdata.size or "default",
- fontdata.encodingbytes or "?",
- fontdata.encodingname or "unicode",
- fontdata.fullname or "?",
- file.basename(fontdata.filename or "?"))
- end
- statistics.stoptiming(fonts)
- return fontdata
-end
-function vf.find(name)
- name=file.removesuffix(file.basename(name))
- if tfm.resolve_vf then
- local format=fonts.logger.format(name)
- if format=='tfm' or format=='ofm' then
- if trace_defining then
- logs.report("define font","locating vf for %s",name)
- end
- return resolvers.findbinfile(name,"ovf")
- else
- if trace_defining then
- logs.report("define font","vf for %s is already taken care of",name)
- end
- return nil
- end
- else
- if trace_defining then
- logs.report("define font","locating vf for %s",name)
- end
- return resolvers.findbinfile(name,"ovf")
- end
-end
-callbacks.register('define_font',define.read,"definition of fonts (tfmtable preparation)")
-callbacks.register('find_vf_file',vf.find,"locating virtual fonts, insofar needed")
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-xtx']={
- 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 texsprint,count=tex.sprint,tex.count
-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 list={}
-fonts.define.specify.colonized_default_lookup="file"
-local function isstyle(s)
- local style=string.lower(s):split("/")
- for _,v in ipairs(style) do
- if v=="b" then
- list.style="bold"
- elseif v=="i" then
- list.style="italic"
- elseif v=="bi" or v=="ib" then
- list.style="bolditalic"
- elseif v:find("^s=") then
- list.optsize=v:split("=")[2]
- elseif v=="aat" or v=="icu" or v=="gr" then
- logs.report("load font","unsupported font option: %s",v)
- elseif not v:is_empty() then
- list.style=v:gsub("[^%a%d]","")
- end
- end
-end
-fonts=fonts or {}
-fonts.otf=fonts.otf or {}
-local otf=fonts.otf
-otf.tables=otf.tables or {}
-otf.tables.defaults={
- dflt={
- "ccmp","locl","rlig","liga","clig",
- "kern","mark","mkmk","itlc",
- },
- arab={
- "ccmp","locl","isol","fina","fin2",
- "fin3","medi","med2","init","rlig",
- "calt","liga","cswh","mset","curs",
- "kern","mark","mkmk",
- },
- deva={
- "ccmp","locl","init","nukt","akhn",
- "rphf","blwf","half","pstf","vatu",
- "pres","blws","abvs","psts","haln",
- "calt","blwm","abvm","dist","kern",
- "mark","mkmk",
- },
- khmr={
- "ccmp","locl","pref","blwf","abvf",
- "pstf","pres","blws","abvs","psts",
- "clig","calt","blwm","abvm","dist",
- "kern","mark","mkmk",
- },
- thai={
- "ccmp","locl","liga","kern","mark",
- "mkmk",
- },
- hang={
- "ccmp","ljmo","vjmo","tjmo",
- },
-}
-otf.tables.defaults.beng=otf.tables.defaults.deva
-otf.tables.defaults.guru=otf.tables.defaults.deva
-otf.tables.defaults.gujr=otf.tables.defaults.deva
-otf.tables.defaults.orya=otf.tables.defaults.deva
-otf.tables.defaults.taml=otf.tables.defaults.deva
-otf.tables.defaults.telu=otf.tables.defaults.deva
-otf.tables.defaults.knda=otf.tables.defaults.deva
-otf.tables.defaults.mlym=otf.tables.defaults.deva
-otf.tables.defaults.sinh=otf.tables.defaults.deva
-otf.tables.defaults.syrc=otf.tables.defaults.arab
-otf.tables.defaults.mong=otf.tables.defaults.arab
-otf.tables.defaults.nko=otf.tables.defaults.arab
-otf.tables.defaults.tibt=otf.tables.defaults.khmr
-otf.tables.defaults.lao=otf.tables.defaults.thai
-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
-local function issome () list.lookup=fonts.define.specify.colonized_default_lookup end
-local function isfile () list.lookup='file' end
-local function isname () list.lookup='name' end
-local function thename(s) list.name=s end
-local function issub (v) list.sub=v end
-local function iskey (k,v)
- if k=="script" then
- parse_script(v)
- end
- 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
-local namespec=(1-lpeg.S("/:("))^0
-local filespec=(lpeg.R("az","AZ")*lpeg.P(":"))^-1*(1-lpeg.S(":("))^1
-local crapspec=spaces*lpeg.P("/")*(((1-lpeg.P(":"))^0)/isstyle)*spaces
-local filename=(lpeg.P("file:")/isfile*(filespec/thename))+(lpeg.P("[")*lpeg.P(true)/isfile*(((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","AZ","09")+lpeg.S("+-."))^1
-local truevalue=lpeg.P("+")*spaces*(sometext/istrue)
-local falsevalue=lpeg.P("-")*spaces*(sometext/isfalse)
-local keyvalue=lpeg.P("+")+(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(")")
-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)
- list={}
- lpegmatch(pattern,specification.specification)
- if list.style then
- specification.style=list.style
- list.style=nil
- end
- if list.optsize then
- specification.optsize=list.optsize
- list.optsize=nil
- end
- if list.name then
- if resolvers.find_file(list.name,"tfm") then
- list.lookup="file"
- list.name=file.addsuffix(list.name,"tfm")
- elseif resolvers.find_file(list.name,"ofm") then
- list.lookup="file"
- list.name=file.addsuffix(list.name,"ofm")
- end
- specification.name=list.name
- list.name=nil
- end
- if list.lookup then
- specification.lookup=list.lookup
- list.lookup=nil
- end
- if list.sub then
- specification.sub=list.sub
- list.sub=nil
- end
- specification.features.normal=normalize_meanings(list)
- return specification
-end
-fonts.define.register_split(":",fonts.define.specify.colonized)
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-if not modules then modules={} end modules ['font-dum']={
- version=1.001,
- comment="companion to luatex-*.tex",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-fonts=fonts or {}
-fonts.otf.pack=false
-fonts.tfm.resolve_vf=false
-fonts.tfm.fontname_mode="specification"
-fonts.tfm.readers=fonts.tfm.readers or {}
-fonts.tfm.readers.sequence={ 'otf','ttf','tfm' }
-fonts.tfm.readers.afm=nil
-fonts.define=fonts.define or {}
-fonts.define.specify.colonized_default_lookup="name"
-function fonts.define.get_specification(str)
- return "",str,"",":",str
-end
-fonts.logger=fonts.logger or {}
-function fonts.logger.save()
-end
-fonts.names=fonts.names or {}
-fonts.names.version=1.001
-fonts.names.basename="luatex-fonts-names.lua"
-fonts.names.new_to_old={}
-fonts.names.old_to_new={}
-local data,loaded=nil,false
-local fileformats={ "lua","tex","other text files" }
-function fonts.names.resolve(name,sub)
- if not loaded then
- local basename=fonts.names.basename
- if basename and basename~="" then
- for i=1,#fileformats do
- local format=fileformats[i]
- local foundname=resolvers.find_file(basename,format) or ""
- if foundname~="" then
- data=dofile(foundname)
- break
- end
- end
- end
- loaded=true
- end
- if type(data)=="table" and data.version==fonts.names.version then
- local condensed=string.gsub(string.lower(name),"[^%a%d]","")
- local found=data.mappings and data.mappings[condensed]
- if found then
- local fontname,filename,subfont=found[1],found[2],found[3]
- if subfont then
- return filename,fontname
- else
- return filename,false
- end
- else
- return name,false
- end
- end
-end
-fonts.names.resolvespec=fonts.names.resolve
-table.insert(fonts.triggers,"itlc")
-local function itlc(tfmdata,value)
- if value then
- local metadata=tfmdata.shared.otfdata.metadata
- if metadata then
- local italicangle=metadata.italicangle
- if italicangle and italicangle~=0 then
- local uwidth=(metadata.uwidth or 40)/2
- for unicode,d in next,tfmdata.descriptions do
- local it=d.boundingbox[3]-d.width+uwidth
- if it~=0 then
- d.italic=it
- end
- end
- tfmdata.has_italic=true
- end
- end
- end
-end
-fonts.initializers.base.otf.itlc=itlc
-fonts.initializers.node.otf.itlc=itlc
-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
-fonts.protrusions=fonts.protrusions or {}
-fonts.protrusions.setups=fonts.protrusions.setups or {}
-local setups=fonts.protrusions.setups
-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[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[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
-function fonts.initializers.common.protrusion(tfmdata,value)
- if value then
- local opbd=tfmdata.shared.features.opbd
- if opbd then
- 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
- 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
-function fonts.register_message()
-end
-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 },
- [0x002E]={ 0,1 },
- [0x003A]={ 0,1 },
- [0x003B]={ 0,1 },
- [0x002D]={ 0,1 },
- [0x2013]={ 0,0.50 },
- [0x2014]={ 0,0.33 },
- [0x3001]={ 0,1 },
- [0x3002]={ 0,1 },
- [0x060C]={ 0,1 },
- [0x061B]={ 0,1 },
- [0x06D4]={ 0,1 },
-}
-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
-function fonts.otf.name_to_slot(name)
- local tfmdata=fonts.ids[font.current()]
- if tfmdata and tfmdata.shared then
- local otfdata=tfmdata.shared.otfdata
- local unicode=otfdata.luatex.unicodes[name]
- return unicode and (type(unicode)=="number" and unicode or unicode[1])
- end
-end
-function fonts.otf.char(n)
- if type(n)=="string" then
- n=fonts.otf.name_to_slot(n)
- end
- if type(n)=="number" then
- tex.sprint("\\char"..n)
- end
-end
-fonts.strippables=table.tohash {
- 0x000AD,0x017B4,0x017B5,0x0200B,0x0200C,0x0200D,0x0200E,0x0200F,0x0202A,0x0202B,
- 0x0202C,0x0202D,0x0202E,0x02060,0x02061,0x02062,0x02063,0x0206A,0x0206B,0x0206C,
- 0x0206D,0x0206E,0x0206F,0x0FEFF,0x1D173,0x1D174,0x1D175,0x1D176,0x1D177,0x1D178,
- 0x1D179,0x1D17A,0xE0001,0xE0020,0xE0021,0xE0022,0xE0023,0xE0024,0xE0025,0xE0026,
- 0xE0027,0xE0028,0xE0029,0xE002A,0xE002B,0xE002C,0xE002D,0xE002E,0xE002F,0xE0030,
- 0xE0031,0xE0032,0xE0033,0xE0034,0xE0035,0xE0036,0xE0037,0xE0038,0xE0039,0xE003A,
- 0xE003B,0xE003C,0xE003D,0xE003E,0xE003F,0xE0040,0xE0041,0xE0042,0xE0043,0xE0044,
- 0xE0045,0xE0046,0xE0047,0xE0048,0xE0049,0xE004A,0xE004B,0xE004C,0xE004D,0xE004E,
- 0xE004F,0xE0050,0xE0051,0xE0052,0xE0053,0xE0054,0xE0055,0xE0056,0xE0057,0xE0058,
- 0xE0059,0xE005A,0xE005B,0xE005C,0xE005D,0xE005E,0xE005F,0xE0060,0xE0061,0xE0062,
- 0xE0063,0xE0064,0xE0065,0xE0066,0xE0067,0xE0068,0xE0069,0xE006A,0xE006B,0xE006C,
- 0xE006D,0xE006E,0xE006F,0xE0070,0xE0071,0xE0072,0xE0073,0xE0074,0xE0075,0xE0076,
- 0xE0077,0xE0078,0xE0079,0xE007A,0xE007B,0xE007C,0xE007D,0xE007E,0xE007F,
-}
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
-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)
- local sanitized
- if value then
- value=tostring(value)
- if #value==6 or #value==8 then
- sanitized=value
- elseif #value==7 then
- _,_,sanitized=value:find("(......)")
- elseif #value>8 then
- _,_,sanitized=value:find("(........)")
- else
- end
- end
- if sanitized then
- tfmdata.color=sanitized
- 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<</ca 1/CA 1>>"
- end
- res2=format("/TransGs%s<</ca %s/CA %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)
- if res then
- local r="/ExtGState<<"..res..">>"
- tex.pdfpageresources=tex.pdfpageresources:gsub(r,"")
- end
- local h=node_colorize(head,nil,nil)
- if res and res:find("%S") then
- 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
-
-end -- closure
diff --git a/luaotfload-legacy-tool.lua b/luaotfload-legacy-tool.lua
deleted file mode 100755
index 30ab13b..0000000
--- a/luaotfload-legacy-tool.lua
+++ /dev/null
@@ -1,105 +0,0 @@
-#!/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") ---> current
-require("luaotfload-legacy-database") ---> old
-require("alt_getopt") ---> ?
-
-local name = "mkluatexfontdb"
-local version = "1.31 (legacy)"
-
-local names = fonts.names
-
-local function help_msg()
- texio.write(string.format([[
-Usage: %s [OPTION]...
-
-================================================================================
- please update your luatex binary
- this version is unsupported and likely to break things
-================================================================================
-
-Rebuild the LuaTeX font database.
-
-Valid options:
- -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
- -V --version print version and exit
- -h --help print this message
-
-The output database file is named otfl-names.lua and is placed under:
-
- %s
-
-contact: https://github.com/lualatex/luaotfload
-
-]], name, names.path.localdir))
-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 = {
- force = "f",
- quiet = "q",
- help = "h",
- verbose = 1 ,
- version = "V",
-}
-
-local short_opts = "fqpvVh"
-
-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 == "f" then
- force_reload = 1
- end
- end
- names.set_log_level(log_level)
-end
-
-local function generate(force)
- local fontnames, saved
- fontnames = names.update(fontnames, force)
- logs.report("%s fonts in the database", #fontnames.mappings)
- saved = names.save(fontnames)
-end
-
-process_cmdline()
-generate(force_reload)
diff --git a/luaotfload-legacy.lua b/luaotfload-legacy.lua
deleted file mode 100644
index 8bb1790..0000000
--- a/luaotfload-legacy.lua
+++ /dev/null
@@ -1,402 +0,0 @@
-module("luaotfload", package.seeall)
-
-luaotfload.module = {
- name = "luaotfload-legacy",
- version = 1.31,
- date = "2013/04/25",
- description = "Unsupported Luaotfload",
- author = "Elie Roux & Hans Hagen",
- copyright = "Elie Roux",
- license = "GPL v2"
-}
-
-local error, warning, info, log = luatexbase.provides_module(luaotfload.module)
-
---[[doc--
-
- This used to be a necessary initalization in order not to rebuild an
- existing font. Maybe 600 should be replaced by |\pdfpkresolution|
- or |texconfig.pk_dpi| (and it should be replaced dynamically), but
- we don't have access (yet) to the |texconfig| table, so we let it be
- 600. Anyway, it does still work fine even if |\pdfpkresolution| is
- changed.
-
---doc]]--
-
-kpse.init_prog("", 600, "/")
-
---[[doc--
-
- The minimal required \luatex version.
- We are tolerant folks.
-
---doc]]--
-
-local luatex_version = 60
-if tex.luatexversion < luatex_version then
- warning("LuaTeX v%.2f is old, v%.2f is required, v0.76 recommended.",
- tex.luatexversion/100,
- luatex_version /100)
-end
-
---[[doc--
-
- \subsection{Module loading}
- We load the outdated \context files with this function. It
- automatically adds the |otfl-| prefix to it, so that we call it with
- the actual \context name.
-
---doc]]--
-
-function luaotfload.loadmodule(tofind)
- 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
-end
-local loadmodule = luaotfload.loadmodule
-
---[[doc--
-
- Keep away from these lines!
-
---doc]]--
-loadmodule"luaotfload-legacy-merged.lua"
-
-if not fonts then
- loadmodule("otfl-luat-dum.lua") -- not used in context at all
- loadmodule("otfl-luat-ovr.lua") -- override some luat-dum functions
- loadmodule("otfl-data-con.lua") -- maybe some day we don't need this one
- loadmodule("otfl-font-ini.lua")
- loadmodule("otfl-node-dum.lua")
- loadmodule("otfl-node-inj.lua")
---[[doc--
- By default \context takes some private attributes for internal use. To
- avoide attribute clashes with other packages, we override the function
- that allocates new attributes, making it a wraper around
- |luatexbase.new_attribute()|. We also prefix attributes with |otfl@| to
- avoid possiple name clashes.
---doc]]--
- loadmodule("luaotfload-legacy-attributes.lua") -- patch attributes
---[[doc--
- Font handling modules.
---doc]]--
- loadmodule("otfl-font-tfm.lua")
- loadmodule("otfl-font-cid.lua")
- loadmodule("otfl-font-ott.lua")
- loadmodule("otfl-font-map.lua")
- loadmodule("otfl-font-otf.lua")
- loadmodule("otfl-font-otd.lua")
- loadmodule("otfl-font-oti.lua")
- loadmodule("otfl-font-otb.lua")
- loadmodule("otfl-font-otn.lua")
- loadmodule("otfl-font-ota.lua")
- loadmodule("otfl-font-otc.lua")
- loadmodule("otfl-font-def.lua")
---[[doc--
- \textsf{old luaotfload} specific modules.
---doc]]--
- loadmodule("otfl-font-xtx.lua")
- loadmodule("otfl-font-dum.lua")
- loadmodule("otfl-font-clr.lua")
-end
-loadmodule"luaotfload-legacy-database.lua" --- unmerged coz needed in db script
-
---[[doc--
-
- This is a patch for |otfl-font-def.lua|, that defines a reader for ofm
- fonts, this is necessary if we set the forced field of the specification
- to |ofm|.
-
---doc]]--
-
-if fonts and fonts.tfm and fonts.tfm.readers then
- fonts.tfm.readers.ofm = fonts.tfm.readers.tfm
-end
-
---[[doc--
-
- \subsection{Post-processing TFM table}
- Here we do some final touches to the loaded TFM table before passing it
- to the \tex end.
- First we create a callback for patching fonts on the fly, to be used by
- other packages.
-
---doc]]--
-
-luatexbase.create_callback("luaotfload.patch_font", "simple", function() end)
-
---[[doc--
-
- then define a function where font manipulation will take place.
-
---doc]]--
-
-local function def_font(...)
- local fontdata = fonts.define.read(...)
- if type(fontdata) == "table" and fontdata.shared then
---[[doc--
-
- Then we populate |MathConstants| table, which is required for
- OpenType math.
-
- Note: actually it isn’t, but you’re asking for it by using outdated
- code.
-
---doc]]--
- local otfdata = fontdata.shared.otfdata
- if otfdata.metadata.math then
- local mc = { }
- for k,v in next, otfdata.metadata.math do
- if k:find("Percent") then
- -- keep percent values as is
- mc[k] = v
- else
- mc[k] = v / fontdata.units * fontdata.size
- end
- end
- -- for \overwithdelims
- mc.FractionDelimiterSize = 1.01 * fontdata.size
- mc.FractionDelimiterDisplayStyleSize = 2.39 * fontdata.size
-
- fontdata.MathConstants = mc
- end
---[[doc--
-
- Execute any registered font patching callbacks.
-
---doc]]--
- luatexbase.call_callback("luaotfload.patch_font", fontdata)
- end
- return fontdata
-end
-
---[[doc--
-\subsection{\context override}
-
- We have a unified function for both file and name resolver. This
- line is commented as it makes database reload too often. This means
- that in some cases, a font in the database will not be found if
- it's not in the texmf tree. A similar thing will reappear in next
- version.
-
---doc]]--
-
---fonts.define.resolvers.file = fonts.define.resolvers.name
-
---[[doc--
-
- Overriding some defaults set in \context code.
-
---doc]]--
-
-fonts.mode = "node"
-
---[[doc--
-
- The following features are useful in math (e.g. in XITS Math font),
- but \textsf{luaotfload} does not recognize them in |base| mode.
-
---doc]]--
-
-local register_base_sub = fonts.otf.features.register_base_substitution
-local gsubs = {
- "ss01", "ss02", "ss03", "ss04", "ss05",
- "ss06", "ss07", "ss08", "ss09", "ss10",
- "ss11", "ss12", "ss13", "ss14", "ss15",
- "ss16", "ss17", "ss18", "ss19", "ss20",
-}
-
-for _,v in next, gsubs do
- register_base_sub(v)
-end
-
---[[doc--
-
- Finally we register the callbacks
-
---doc]]--
-
-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",
- def_font,
- "luaotfload.define_font", 1)
-luatexbase.add_to_callback("find_vf_file",
- fonts.vf.find,
- "luaotfload.find_vf_file")
---[[doc--
-
- XXX: see https://github.com/wspr/unicode-math/issues/185
- \luatex does not provide interface to accessing
- |(Script)ScriptPercentScaleDown| math constants, so we
- emulate \xetex behaviour by setting |\fontdimen10| and
- |\fontdimen11|.
-
- Note: actually, it does now, but not unless you update.
-
---doc]]--
-
-local function set_sscale_diments(fontdata)
- local mc = fontdata.MathConstants
- if mc then
- if mc["ScriptPercentScaleDown"] then
- fontdata.parameters[10] = mc.ScriptPercentScaleDown
- else -- resort to plain TeX default
- fontdata.parameters[10] = 70
- end
- if mc["ScriptScriptPercentScaleDown"] then
- fontdata.parameters[11] = mc.ScriptScriptPercentScaleDown
- else -- resort to plain TeX default
- fontdata.parameters[11] = 50
- end
- end
-end
-
-luatexbase.add_to_callback("luaotfload.patch_font", set_sscale_diments, "unicodemath.set_sscale_diments")
-
---[[doc--
- Version 2.3c of fontspec dropped a couple features that are now
- provided in the luaotfload auxiliary libraries. To avoid breaking
- Mik\TEX (again), which is sorta the entire point of distributing the
- legacy codebase, we temporarily restore those functions here.
-
- Note that apart from cosmetic changes these are still the same as in
- pre-TL2013 fontspec, relying on pairs() and other inefficient methods.
---doc]]--
-
-luaotfload.aux = luaotfload.aux or { }
-local aux = luaotfload.aux
-
-local stringlower = string.lower
-local fontid = font.id
-
-local identifiers = fonts.identifiers
-
-local check_script = function (id, script)
- local s = stringlower(script)
- if id and id > 0 then
- local tfmdata = identifiers[id]
- local otfdata = tfmdata.shared and tfmdata.shared.otfdata
- if otfdata then
- local features = otfdata.luatex.features
- for i, _ in pairs(features) do
- for j, _ in pairs(features[i]) do
- if features[i][j][s] then
- fontspec.log("script '%s' exists in font '%s'",
- script, tfmdata.fullname)
- return true
- end
- end
- end
- end
- end
-end
-
-local check_language = function (id, script, language)
- local s = stringlower(script)
- local l = stringlower(language)
- if id and id > 0 then
- local tfmdata = identifiers[id]
- local otfdata = tfmdata.shared and tfmdata.shared.otfdata
- if otfdata then
- local features = otfdata.luatex.features
- for i, _ in pairs(features) do
- for j, _ in pairs(features[i]) do
- if features[i][j][s] and features[i][j][s][l] then
- fontspec.log("language '%s' for script '%s' exists in font '%s'",
- language, script, tfmdata.fullname)
- return true
- end
- end
- end
- end
- end
-end
-
-local check_feature = function (id, script, language, feature)
- local s = stringlower(script)
- local l = stringlower(language)
- local f = stringlower(feature:gsub("^[+-]", ""):gsub("=.*$", ""))
- if id and id > 0 then
- local tfmdata = identifiers[id]
- local otfdata = tfmdata.shared and tfmdata.shared.otfdata
- if otfdata then
- local features = otfdata.luatex.features
- for i, _ in pairs(features) do
- if features[i][f] and features[i][f][s] then
- if features[i][f][s][l] == true then
- fontspec.log("feature '%s' for language '%s' and script '%s' exists in font '%s'",
- feature, language, script, tfmdata.fullname)
- return true
- end
- end
- end
- end
- end
-end
-
-local get_math_dimension = function(fnt, str)
- if type(fnt) == "string" then
- fnt = fontid(fnt)
- end
- local tfmdata = identifiers[fnt]
- if tfmdata then
- local mathdata = tfmdata.MathConstants
- if mathdata then
- return mathdata[str]
- end
- end
-end
-
-aux.provides_script = check_script
-aux.provides_language = check_language
-aux.provides_feature = check_feature
-aux.get_math_dimension = get_math_dimension
-
-local set_capheight = function (tfmdata)
- local capheight
- local shared = tfmdata.shared
- if shared then
- local metadata = shared.otfdata.metadata
- local units_per_em = metadata.units_per_em or tfmdata.units
- local os2_capheight = shared.otfdata.pfminfo.os2_capheight
- local size = tfmdata.size
-
- if os2_capheight > 0 then
- capheight = os2_capheight / units_per_em * size
- else
- local X8 = string.byte"X"
- if tfmdata.characters[X8] then
- capheight = tfmdata.characters[X8].height
- else
- capheight = metadata.ascent / units_per_em * size
- end
- end
- else
- local X8 = string.byte"X"
- if tfmdata.characters[X8] then
- capheight = tfmdata.characters[X8].height
- end
- end
- if capheight then
- tfmdata.parameters[8] = capheight
- end
-end
-luatexbase.add_to_callback("luaotfload.patch_font",
- set_capheight,
- "luaotfload.set_capheight")
-
---[[doc--
-End of auxiliary functionality that was moved from fontspec.lua.
---doc]]--
-
--- vim:ts=2:sw=2:expandtab:ft=lua
diff --git a/luaotfload.dtx b/luaotfload.dtx
deleted file mode 100644
index 3615fa8..0000000
--- a/luaotfload.dtx
+++ /dev/null
@@ -1,2685 +0,0 @@
-% \iffalse meta-comment
-%
-% Copyright (C) 2009-2014
-% by Elie Roux <elie.roux@telecom-bretagne.eu>
-% and Khaled Hosny <khaledhosny@eglug.org>
-% and Philipp Gesang <philipp.gesang@alumni.uni-heidelberg.de>
-%
-% Home: https://github.com/lualatex/luaotfload
-% Support: <lualatex-dev@tug.org>.
-%
-% This work is under the GPL v2.0 license.
-%
-% This work consists of the main source file luaotfload.dtx
-% and the derived files
-% luaotfload.sty, luaotfload.lua
-%
-% Unpacking:
-% tex luaotfload.dtx
-%
-% Documentation:
-% lualatex luaotfload.dtx
-%
-% The class ltxdoc loads the configuration file ltxdoc.cfg
-% if available. Here you can specify further options, e.g.
-% use A4 as paper format:
-% \PassOptionsToClass{a4paper}{article}
-%
-%
-%
-%<*ignore>
-\begingroup
- \def\x{LaTeX2e}%
-\expandafter\endgroup
-\ifcase 0\ifx\install y1\fi\expandafter
- \ifx\csname processbatchFile\endcsname\relax\else1\fi
- \ifx\fmtname\x\else 1\fi\relax
-\else\csname fi\endcsname
-%</ignore>
-%<*install>
-\input docstrip.tex
-\Msg{************************************************************************}
-\Msg{* Installation}
-\Msg{* Package: luaotfload v2.4-4 OpenType layout system}
-\Msg{************************************************************************}
-
-\keepsilent
-\askforoverwritefalse
-
-\let\MetaPrefix\relax
-
-\preamble
-This is a generated file.
-
-Copyright (C) 2009-2014
- by Elie Roux <elie.roux@telecom-bretagne.eu>
- and Khaled Hosny <khaledhosny@eglug.org>
- and Philipp Gesang <philipp.gesang@alumni.uni-heidelberg.de>
-
- Home: https://github.com/lualatex/luaotfload
- Support: <lualatex-dev@tug.org>.
-
-This work is under the GPL v2.0 license.
-
-This work consists of the main source file luaotfload.dtx
-and the derived files
- luaotfload.sty, luaotfload.lua
-
-\endpreamble
-
-\let\MetaPrefix\DoubleperCent
-
-\generate{%
- \usedir{tex/luatex/luaotfload}%
- \file{luaotfload.sty}{\from{luaotfload.dtx}{package}}%
-}
-
-% The following hacks are to generate a lua file with lua comments starting with
-% -- instead of %%
-
-\def\MetaPrefix{-- }
-
-\def\luapostamble{%
- \MetaPrefix^^J%
- \MetaPrefix\space End of File `\outFileName'.%
-}
-
-\def\currentpostamble{\luapostamble}%
-
-\generate{%
- \usedir{tex/luatex/luaotfload}%
- \file{luaotfload.lua}{\from{luaotfload.dtx}{lua}}%%
-}
-
-\obeyspaces
-\Msg{************************************************************************}
-\Msg{*}
-\Msg{* To finish the installation you have to move the following}
-\Msg{* files into a directory searched by TeX:}
-\Msg{*}
-\Msg{* luaotfload.sty, luaotfload.lua}
-\Msg{*}
-\Msg{* Happy TeXing!}
-\Msg{*}
-\Msg{************************************************************************}
-
-\endbatchfile
-%</install>
-%<*ignore>
-\fi
-%</ignore>
-%<*driver>
-\NeedsTeXFormat{LaTeX2e}
-\ProvidesFile{luaotfload.drv}%
- [2014/05/18 v2.4-4 OpenType layout system]%
-\documentclass{ltxdoc}
-\usepackage{metalogo,multicol,mdwlist,fancyvrb,xspace}
-\usepackage[x11names]{xcolor}
-%
-\def\primarycolor{DodgerBlue4} %%-> rgb 16 78 139 | #104e8b
-\def\secondarycolor{Goldenrod4} %%-> rgb 139 105 200 | #8b6914
-%
-\usepackage[
- bookmarks=true,
- colorlinks=true,
- linkcolor=\primarycolor,
- urlcolor=\secondarycolor,
- citecolor=\primarycolor,
- pdftitle={The luaotfload package},
- pdfsubject={OpenType layout system for Plain TeX and LaTeX},
- pdfauthor={Elie Roux & Khaled Hosny & Philipp Gesang},
- pdfkeywords={luatex, lualatex, unicode, opentype}
-]{hyperref}
-\usepackage{fontspec}
-\usepackage{unicode-math}
-\setmainfont[
-% Numbers = OldStyle, %% buggy with font cache
- Ligatures = TeX,
- BoldFont = {Linux Libertine O Bold},
- ItalicFont = {Linux Libertine O Italic},
- SlantedFont = {Linux Libertine O Italic},
-]{Linux Libertine O}
-\setmonofont[Ligatures=TeX,Scale=MatchLowercase]{Liberation Mono}
-%setsansfont[Ligatures=TeX]{Linux Biolinum O}
-\setsansfont[Ligatures=TeX,Scale=MatchLowercase]{Iwona Medium}
-%setmathfont{XITS Math}
-
-\usepackage{hologo}
-
-\newcommand\TEX {\TeX\xspace}
-\newcommand\LUA {Lua\xspace}
-\newcommand\PDFTEX {pdf\TeX\xspace}
-\newcommand\LUATEX {Lua\TeX\xspace}
-\newcommand\XETEX {\XeTeX\xspace}
-\newcommand\LATEX {\LaTeX\xspace}
-\newcommand\LUALATEX {Lua\LaTeX\xspace}
-\newcommand\CONTEXT {Con\TeX t\xspace}
-\newcommand\OpenType {\identifier{Open\kern-.25ex Type}\xspace}
-
-\def\definehighlight[#1][#2]%
- {\ifcsname #1\endcsname\else
- \expandafter\def\csname #1\endcsname%
- {\bgroup#2\csname #1_indeed\endcsname}
- \expandafter\def\csname #1_indeed\endcsname##1%
- {##1\egroup}%
- \fi}
-
-\def\restoreunderscore{\catcode`\_=12\relax}
-
-\definehighlight [fileent][\ttfamily\restoreunderscore] %% files, dirs
-\definehighlight [texmacro][\sffamily\itshape\textbackslash] %% cs
-\definehighlight[luafunction][\sffamily\itshape\restoreunderscore] %% lua identifiers
-\definehighlight [identifier][\sffamily] %% names
-\definehighlight [abbrev][\rmfamily\scshape] %% acronyms
-\definehighlight [emphasis][\rmfamily\slshape] %% level 1 emph
-
-\newcommand*\email[1]{\href{mailto:#1}{#1}}
-
-\renewcommand\partname{Part}%% gets rid of the stupid “file” heading
-
-\usepackage{syntax}%% bnf for font request syntax
-
-\usepackage{titlesec}
-
-\def\movecountertomargin#1{\llap{\rmfamily\upshape#1\hskip2em}}
-\def\zeropoint{0pt}
-\titleformat \part
- {\normalsize\rmfamily\bfseries}
- {\movecountertomargin\thepart} \zeropoint {}
-\titleformat \section
- {\normalsize\rmfamily\scshape}
- {\movecountertomargin\thesection} \zeropoint {}
-\titleformat \subsection
- {\small\rmfamily\itshape}
- {\movecountertomargin\thesubsection} \zeropoint {}
-\titleformat \subsubsection
- {\normalsize\rmfamily\upshape}
- {\movecountertomargin\thesubsubsection} \zeropoint {}
-
-\usepackage{tocloft}
-\renewcommand \cftpartfont {\rmfamily\upshape}
-\renewcommand \cftsecfont {\rmfamily\upshape}
-\renewcommand \cftsubsecfont {\rmfamily\upshape}
-\setlength \cftbeforepartskip {1ex}
-\setlength \cftbeforesecskip {1ex}
-
-\VerbatimFootnotes
-\begin{document}
- \DocInput{luaotfload.dtx}%
-\end{document}
-%</driver>
-% \fi
-%
-% \CheckSum{0}
-%
-% \CharacterTable
-% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
-% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
-% Digits \0\1\2\3\4\5\6\7\8\9
-% Exclamation \! Double quote \" Hash (number) \#
-% Dollar \$ Percent \% Ampersand \&
-% Acute accent \' Left paren \( Right paren \)
-% Asterisk \* Plus \+ Comma \,
-% Minus \- Point \. Solidus \/
-% Colon \: Semicolon \; Less than \<
-% Equals \= Greater than \> Question mark \?
-% Commercial at \@ Left bracket \[ Backslash \\
-% Right bracket \] Circumflex \^ Underscore \_
-% Grave accent \` Left brace \{ Vertical bar \|
-% Right brace \} Tilde \~}
-%
-% \GetFileInfo{luaotfload.drv}
-%
-% \title{The \identifier{luaotfload} package}
-% \date{2014/05/18 v2.4-4}
-% \author{Elie Roux · Khaled Hosny · Philipp Gesang\\
-% Home: \url{https://github.com/lualatex/luaotfload}\\
-% Support: \email{lualatex-dev@tug.org}}
-%
-% \maketitle
-%
-% \begin{abstract}
-% This package is an adaptation of the \CONTEXT font loading system.
-% It allows for loading \OpenType fonts with an extended syntax and adds
-% support for a variety of font features.
-% \end{abstract}
-%
-% \tableofcontents
-%
-% \part{Package Description}
-%
-% \section{Introduction}
-%
-% Font management and installation has always been painful with \TEX. A lot of
-% files are needed for one font (\abbrev{tfm}, \abbrev{pfb}, \abbrev{map},
-% \abbrev{fd}, \abbrev{vf}), and due to the 8-Bit encoding each font is limited
-% to 256 characters.
-% But the font world has evolved since the original
-% \TEX, and new typographic systems have appeared, most notably the so
-% called \emphasis{smart font} technologies like \OpenType
-% fonts (\abbrev{otf}).
-% These fonts can contain many more characters than \TEX fonts, as well as additional
-% functionality like ligatures, old-style numbers, small capitals,
-% etc., and support more complex writing systems like Arabic and
-% Indic\footnote{%
-% Unfortunately, \identifier{luaotfload} doesn‘t support many Indic
-% scripts right now.
-% Assistance in implementing the prerequisites is greatly
-% appreciated.
-% }
-% scripts.
-% \OpenType fonts are widely deployed and available for all
-% modern operating systems.
-% As of 2013 they have become the de facto standard for advanced text
-% layout.
-% However, until recently the only way to use them directly in the \TEX
-% world was with the \XETEX engine.
-%
-% Unlike \XETEX, \LUATEX has no built-in support for
-% \OpenType or technologies other than the original \TEX fonts.
-% Instead, it provides hooks for executing \LUA code during the \TEX run
-% that allow implementing extensions for loading fonts and manipulating
-% how input text is processed without modifying the underlying engine.
-% This is where \identifier{luaotfload} comes into play:
-% Based on code from \CONTEXT, it extends \LUATEX with functionality necessary
-% for handling \OpenType fonts.
-% Additionally, it provides means for accessing fonts known to the operating
-% system conveniently by indexing the metadata.
-%
-%
-% \section{Thanks}
-%
-% \identifier{Luaotfload} is part of \LUALATEX, the community-driven
-% project to provide a foundation for using the \LATEX format with the
-% full capabilites of the \LUATEX engine.
-% As such, the distinction between end users, contributors, and project
-% maintainers is intentionally kept less strict, lest we unduly
-% personalize the common effort.
-%
-% Nevertheless, the current maintainers would like to express their
-% gratitude to Khaled Hosny, Akira Kakuto, Hironori Kitagawa and Dohyun
-% Kim.
-% Their contributions -- be it patches, advice, or systematic
-% testing -- made the switch from version 1.x to 2.2 possible.
-% Also, Hans Hagen, the author of the font loader, made porting the
-% code to \LATEX a breeze due to the extra effort he invested into
-% isolating it from the rest of \CONTEXT, not to mention his assistance
-% in the task and willingness to respond to our suggestions.
-%
-%
-% \section{Loading Fonts}
-%
-% \identifier{luaotfload} supports an extended font request syntax:
-%
-% \begin{quote}
-% |\font\foo={|%
-% \meta{prefix}|:|%
-% \meta{font name}|:|%
-% \meta{font features}|}|%
-% \meta{\TEX font features}
-% \end{quote}
-%
-% \noindent
-% The curly brackets are optional and escape the spaces in the enclosed
-% font name.
-% Alternatively, double quotes serve the same purpose.
-% A selection of individual parts of the syntax are discussed below;
-% for a more formal description see figure \ref{font-syntax}.
-%
-% \begin{figure}[b]
-% \setlength\grammarparsep{12pt plus 2pt minus 2pt}
-% \setlength\grammarindent{5cm}
-% \begingroup
-% \small
-% \begin{grammar}
-% <definition> ::= `\\font', {\sc csname}, `=', <font request>, [ <size> ] ;
-%
-% <size> ::= `at', {\sc dimension} ;
-%
-% <font request> ::= `"', <unquoted font request> `"'
-% \alt `{', <unquoted font request> `}'
-% \alt <unquoted font request> ;
-%
-% <unquoted font request> ::= <specification>, [`:', <feature list> ]
-% \alt `[', <path lookup> `]', [ [`:'], <feature list> ] ;
-%
-% <specification> ::= <prefixed spec>, [ <subfont no> ], \{ <modifier> \}
-% \alt <anon lookup>, \{ <modifier> \} ;
-%
-% <prefixed spec> ::= `file:', <file lookup>
-% \alt `name:', <name lookup> ;
-%
-% <file lookup> ::= \{ <name character> \} ;
-%
-% <name lookup> ::= \{ <name character> \} ;
-%
-% <anon lookup> ::= {\sc tfmname} | <name lookup> ;
-%
-% <path lookup> ::= \{ {\sc all_characters} - `]' \} ;
-%
-% <modifier> ::= `/', (`I' | `B' | `BI' | `IB' | `S=', \{ {\sc digit} \} ) ;
-%
-% <subfont no> ::= `(', \{ {\sc digit} \}, `)' ;
-%
-% <feature list> ::= <feature expr>, \{ `;', <feature expr> \} ;
-%
-% <feature expr> ::= {\sc feature_id}, `=', {\sc feature_value}
-% \alt <feature switch>, {\sc feature_id} ;
-%
-% <feature switch> ::= `+' | `-' ;
-%
-% <name character> ::= {\sc all_characters} - ( `(' | `/' | `:' ) ;
-% \end{grammar}
-% \endgroup
-% \caption{Font request syntax.
-% Braces or double quotes around the
-% \emphasis{specification} rule will
-% preserve whitespace in file names.
-% In addition to the font style modifiers
-% (\emphasis{slash-notation}) given above, there
-% are others that are recognized but will be silently
-% ignored: {\ttfamily aat},
-% {\ttfamily icu}, and
-% {\ttfamily gr}.
-% The special terminals are:
-% {\sc feature\textunderscore id} for a valid font
-% feature name and
-% {\sc feature\textunderscore value} for the corresponding
-% value.
-% {\sc tfmname} is the name of a \abbrev{tfm} file.
-% {\sc digit} again refers to bytes 48--57, and
-% {\sc all\textunderscore characters} to all byte values.
-% {\sc csname} and {\sc dimension} are the \TEX concepts.}
-% \label{font-syntax}
-% \end{figure}
-%
-% \subsection{Prefix -- the \identifier{luaotfload}{ }Way}
-%
-% In \identifier{luaotfload}, the canonical syntax for font requests
-% requires a \emphasis{prefix}:
-% \begin{quote}
-% |\font\fontname=|\meta{prefix}|:|\meta{fontname}\dots
-% \end{quote}
-% where \meta{prefix} is either \verb|file:| or \verb|name:|.\footnote{%
-% The development version also knows two further prefixes,
-% \verb|kpse:| and \verb|my:|.
-% A \verb|kpse| lookup is restricted to files that can be found by
-% \identifier{kpathsea} and
-% will not attempt to locate system fonts.
-% This behavior can be of value when an extra degree of encapsulation is
-% needed, for instance when supplying a customized tex distribution.
-%
-% The \verb|my| lookup takes this a step further: it lets you define
-% a custom resolver function and hook it into the \luafunction{resolve_font}
-% callback.
-% This ensures full control over how a file is located.
-% For a working example see the
-% \href{https://bitbucket.org/phg/lua-la-tex-tests/src/5f6a535d/pln-lookup-callback-1.tex}
-% {test repo}.
-% }
-% It determines whether the font loader should interpret the request as
-% a \emphasis{file name} or
-% \emphasis{font name}, respectively,
-% which again influences how it will attempt to locate the font.
-% Examples for font names are
-% “Latin Modern Italic”,
-% “GFS Bodoni Rg”, and
-% “PT Serif Caption”
-% -- they are the human readable identifiers
-% usually listed in drop-down menus and the like.\footnote{%
-% Font names may appear like a great choice at first because they
-% offer seemingly more intuitive identifiers in comparison to arguably
-% cryptic file names:
-% “PT Sans Bold” is a lot more descriptive than \fileent{PTS75F.ttf}.
-% On the other hand, font names are quite arbitrary and there is no
-% universal method to determine their meaning.
-% While \identifier{luaotfload} provides fairly sophisticated heuristic
-% to figure out a matching font style, weight, and optical size, it
-% cannot be relied upon to work satisfactorily for all font files.
-% For an in-depth analysis of the situation and how broken font names
-% are, please refer to
-% \href{http://www.ntg.nl/pipermail/ntg-context/2013/073889.html}
-% {this post}
-% by Hans Hagen, the author of the font loader.
-% If in doubt, use filenames.
-% \fileent{luaotfload-tool} can perform the matching for you with the
-% option \verb|--find=<name>|, and you can use the file name it returns
-% in your font definition.
-% }
-% In order for fonts installed both in system locations and in your
-% \fileent{texmf} to be accessible by font name, \identifier{luaotfload} must
-% first collect the metadata included in the files.
-% Please refer to section~\ref{sec:fontdb} below for instructions on how to
-% create the database.
-%
-% File names are whatever your file system allows them to be, except
-% that that they may not contain the characters
-% \verb|(|,
-% \verb|:|, and
-% \verb|/|.
-% As is obvious from the last exception, the \verb|file:| lookup will
-% not process paths to the font location -- only those
-% files found when generating the database are addressable this way.
-% Continue below in the \XETEX section if you need to load your fonts
-% by path.
-% The file names corresponding to the example font names above are
-% \fileent{lmroman12-italic.otf},
-% \fileent{GFSBodoni.otf}, and
-% \fileent{PTZ56F.ttf}.
-%
-% \subsection{Compatibility Layer}
-%
-% In addition to the regular prefixed requests, \identifier{luaotfload}
-% accepts loading fonts the \XETEX way.
-% There are again two modes: bracketed and unbracketed.
-% A bracketed request looks as follows.
-%
-% \begin{quote}
-% |\font\fontname=[|\meta{path to file}|]|
-% \end{quote}
-%
-% \noindent
-% Inside the square brackets, every character except for a closing
-% bracket is permitted, allowing for specifying paths to a font file.
-% Naturally, path-less file names are equally valid and processed the
-% same way as an ordinary \verb|file:| lookup.
-%
-% \begin{quote}
-% |\font\fontname=|\meta{font name} \dots
-% \end{quote}
-%
-% Unbracketed (or, for lack of a better word: \emphasis{anonymous})
-% font requests resemble the conventional \TEX syntax.
-% However, they have a broader spectrum of possible interpretations:
-% before anything else, \identifier{luaotfload} attempts to load a
-% traditional \TEX Font Metric (\abbrev{tfm} or \abbrev{ofm}).
-% If this fails, it performs a \verb|name:| lookup, which itself will
-% fall back to a \verb|file:| lookup if no database entry matches
-% \meta{font name}.
-%
-% Furthermore, \identifier{luaotfload} supports the slashed (shorthand)
-% font style notation from \XETEX.
-%
-% \begin{quote}
-% |\font\fontname=|\meta{font name}|/|\meta{modifier}\dots
-% \end{quote}
-%
-% \noindent
-% Currently, four style modifiers are supported:
-% \verb|I| for italic shape,
-% \verb|B| for bold weight,
-% \verb|BI| or \verb|IB| for the combination of both.
-% Other “slashed” modifiers are too specific to the \XETEX engine and
-% have no meaning in \LUATEX.
-%
-% \subsection{Examples}
-%
-% \subsubsection{Loading by File Name}
-%
-% For example, conventional \abbrev{type1} font can be loaded with a \verb|file:|
-% request like so:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\lmromanten={file:ec-lmr10} at 10pt
-% \end{verbatim}
-% \end{quote}
-%
-% The \OpenType version of Janusz Nowacki’s font \emphasis{Antykwa
-% Półtawskiego}\footnote{%
-% \url{http://jmn.pl/antykwa-poltawskiego/}, also available in
-% in \TEX Live.
-% }
-% in its condensed variant can be loaded as follows:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\apcregular=file:antpoltltcond-regular.otf at 42pt
-% \end{verbatim}
-% \end{quote}
-%
-% The next example shows how to load the \emphasis{Porson} font digitized by
-% the Greek Font Society using \XETEX-style syntax and an absolute path from a
-% non-standard directory:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\gfsporson="[/tmp/GFSPorson.otf]" at 12pt
-% \end{verbatim}
-% \end{quote}
-%
-% \subsubsection{Loading by Font Name}
-%
-% The \verb|name:| lookup does not depend on cryptic filenames:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\pagellaregular={name:TeX Gyre Pagella} at 9pt
-% \end{verbatim}
-% \end{quote}
-%
-% A bit more specific but essentially the same lookup would be:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\pagellaregular={name:TeX Gyre Pagella Regular} at 9pt
-% \end{verbatim}
-% \end{quote}
-%
-% \noindent
-% Which fits nicely with the whole set:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\pagellaregular ={name:TeX Gyre Pagella Regular} at 9pt
-% \font\pagellaitalic ={name:TeX Gyre Pagella Italic} at 9pt
-% \font\pagellabold ={name:TeX Gyre Pagella Bold} at 9pt
-% \font\pagellabolditalic={name:TeX Gyre Pagella Bolditalic} at 9pt
-%
-% {\pagellaregular foo bar baz\endgraf}
-% {\pagellaitalic foo bar baz\endgraf}
-% {\pagellabold foo bar baz\endgraf}
-% {\pagellabolditalic foo bar baz\endgraf}
-%
-% ...
-% \end{verbatim}
-% \end{quote}
-%
-% \subsubsection{Modifiers}
-%
-% If the entire \emphasis{Iwona} family\footnote{%
-% \url{http://jmn.pl/kurier-i-iwona/},
-% also in \TEX Live.
-% }
-% is installed in some location accessible by \identifier{luaotfload},
-% the regular shape can be loaded as follows:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\iwona=Iwona at 20pt
-% \end{verbatim}
-% \end{quote}
-%
-% \noindent
-% To load the most common of the other styles, the slash notation can
-% be employed as shorthand:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\iwonaitalic =Iwona/I at 20pt
-% \font\iwonabold =Iwona/B at 20pt
-% \font\iwonabolditalic=Iwona/BI at 20pt
-% \end{verbatim}
-% \end{quote}
-%
-% \noindent
-% which is equivalent to these full names:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\iwonaitalic ="Iwona Italic" at 20pt
-% \font\iwonabold ="Iwona Bold" at 20pt
-% \font\iwonabolditalic="Iwona BoldItalic" at 20pt
-% \end{verbatim}
-% \end{quote}
-%
-% \section{Font features}
-%
-% \emphasis{Font features} are the second to last component in the
-% general scheme for font requests:
-%
-% \begin{quote}
-% |\font\foo={|%
-% \meta{prefix}|:|%
-% \meta{font name}|:|%
-% \meta{font features}|}|%
-% \meta{\TEX font features}
-% \end{quote}
-%
-% \noindent
-% If style modifiers are present (\XETEX style), they must precede
-% \meta{font features}.
-%
-% The element \meta{font features} is a semicolon-separated list of feature
-% tags\footnote{%
-% Cf. \url{http://www.microsoft.com/typography/otspec/featurelist.htm}.
-% }
-% and font options.
-% Prepending a font feature with a |+| (plus sign) enables it, whereas
-% a |-| (minus) disables it. For instance, the request
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\test=LatinModernRoman:+clig;-kern
-% \end{verbatim}
-% \end{quote}
-%
-% \noindent activates contextual ligatures (|clig|) and disables
-% kerning (|kern|).
-% Alternatively the options |true| or |false| can be passed to
-% the feature in a key/value expression.
-% The following request has the same meaning as the last one:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\test=LatinModernRoman:clig=true;kern=false
-% \end{verbatim}
-% \end{quote}
-%
-% \noindent
-% Furthermore, this second syntax is required should a font feature
-% accept other options besides a true/false switch.
-% For example, \emphasis{stylistic alternates} (|salt|) are variants of given
-% glyphs.
-% They can be selected either explicitly by supplying the variant
-% index (starting from one), or randomly by setting the value to,
-% obviously, |random|.
-%
-% \iffalse TODO verify that this actually works with a font that supports
-% the salt/random feature!\fi
-% \begin{quote}
-% \begin{verbatim}
-% \font\librmsaltfirst=LatinModernRoman:salt=1
-% \end{verbatim}
-% \end{quote}
-%
-% \noindent Other font options include:
-%
-% \begin{description}
-%
-% \item [mode] \hfill \\
-% \identifier{luaotfload} has two \OpenType processing
-% \emphasis{modes}:
-% \identifier{base} and \identifier{node}.
-%
-% \identifier{base} mode works by mapping \OpenType
-% features to traditional \TEX ligature and kerning mechanisms.
-% Supporting only non-contextual substitutions and kerning
-% pairs, it is the slightly faster, albeit somewhat limited, variant.
-% \identifier{node} mode works by processing \TeX’s internal
-% node list directly at the \LUA end and supports
-% a wider range of \OpenType features.
-% The downside is that the intricate operations required for
-% \identifier{node} mode may slow down typesetting especially
-% with complex fonts and it does not work in math mode.
-%
-% By default \identifier{luaotfload} is in \identifier{node}
-% mode, and \identifier{base} mode has to be requested where needed,
-% e.~g. for math fonts.
-%
-% \item [script] \label{script-tag} \hfill \\
-% An \OpenType script tag;\footnote{%
-% See \url{http://www.microsoft.com/typography/otspec/scripttags.htm}
-% for a list of valid values.
-% For scripts derived from the Latin alphabet the value
-% |latn| is good choice.
-% }
-% the default value is |dlft|.
-% Some fonts, including very popular ones by foundries like Adobe,
-% do not assign features to the |dflt| script, in
-% which case the script needs to be set explicitly.
-%
-% \item [language] \hfill \\
-% An \OpenType language system identifier,\footnote{%
-% Cf. \url{http://www.microsoft.com/typography/otspec/languagetags.htm}.
-% }
-% defaulting to |dflt|.
-%
-% \item [featurefile] \hfill \\
-% A comma-separated list of feature files to be applied to the
-% font.
-% Feature files contain a textual representation of
-% \OpenType tables and extend the features of a font
-% on fly.
-% After they are applied to a font, features defined in a
-% feature file can be enabled or disabled just like any
-% other font feature.
-% The syntax is documented in \identifier{Adobe}’s
-% \OpenType Feature File Specification.\footnote{%
-% Cf. \url{http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html}.
-% }
-%
-% For a demonstration of how to set a |tkrn| feature consult
-% the file |tkrn.fea| that is part of \identifier{luaotfload}.
-% It can be read and applied as follows:
-%
-% |\font\test=Latin Modern Roman:featurefile=tkrn.fea;+tkrn|
-%
-% \item [color] \hfill \\
-% A font color, defined as a triplet of two-digit hexadecimal
-% \abbrev{rgb} values, with an optional fourth value for
-% transparency
-% (where |00| is completely transparent and |FF| is opaque).
-%
-% For example, in order to set text in semitransparent red:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\test={Latin Modern Roman}:color=FF0000BB
-% \end{verbatim}
-% \end{quote}
-%
-% \item [kernfactor \& letterspace] \hfill \\
-% Define a font with letterspacing (tracking) enabled.
-% In \identifier{luaotfload}, letterspacing is implemented by
-% inserting additional kerning between glyphs.
-%
-% This approach is derived from and still quite similar to the
-% \emphasis{character kerning} (\texmacro{setcharacterkerning} /
-% \texmacro{definecharacterkerning} \& al.) functionality of
-% Context, see the file \fileent{typo-krn.lua} there.
-% The main difference is that \identifier{luaotfload} does not
-% use \LUATEX attributes to assign letterspacing to regions,
-% but defines virtual letterspaced versions of a font.
-%
-% The option \identifier{kernfactor} accepts a numeric value that
-% determines the letterspacing factor to be applied to the font
-% size.
-% E.~g. a kern factor of $0.42$ applied to a $10$ pt font
-% results in $4.2$ pt of additional kerning applied to each
-% pair of glyphs.
-% Ligatures are split into their component glyphs unless
-% explicitly ignored (see below).
-%
-% For compatibility with \XETEX an alternative
-% \identifier{letterspace} option is supplied that interprets the
-% supplied value as a \emphasis{percentage} of the font size but
-% is otherwise identical to \identifier{kernfactor}.
-% Consequently, both definitions in below snippet yield the same
-% letterspacing width:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\iwonakernedA="file:Iwona-Regular.otf:kernfactor=0.125"
-% \font\iwonakernedB="file:Iwona-Regular.otf:letterspace=12.5"
-% \end{verbatim}
-% \end{quote}
-%
-% Specific pairs of letters and ligatures may be exempt from
-% letterspacing by defining the \LUA functions
-% \luafunction{keeptogether} and \luafunction{keepligature},
-% respectively, inside the namespace \verb|luaotfload.letterspace|.
-% Both functions are called whenever the letterspacing callback
-% encounters an appropriate node or set of nodes.
-% If they return a true-ish value, no extra kern is inserted at
-% the current position.
-% \luafunction{keeptogether} receives a pair of consecutive
-% glyph nodes in order of their appearance in the node list.
-% \luafunction{keepligature} receives a single node which can be
-% analyzed into components.
-% (For details refer to the \emphasis{glyph nodes} section in the
-% \LUATEX reference manual.)
-% The implementation of both functions is left entirely to the
-% user.
-%
-%
-% \item [protrusion \& expansion] \hfill \\
-% These keys control microtypographic features of the font,
-% namely \emphasis{character protrusion} and \emphasis{font
-% expansion}.
-% Their arguments are names of \LUA tables that contain
-% values for the respective features.\footnote{%
-% For examples of the table layout please refer to the
-% section of the file \fileent{luaotfload-fonts-ext.lua} where the
-% default values are defined.
-% Alternatively and with loss of information, you can dump
-% those tables into your terminal by issuing
-% \begin{verbatim}
-% \directlua{inspect(fonts.protrusions.setups.default)
-% inspect(fonts.expansions.setups.default)}
-% \end{verbatim}
-% at some point after loading \fileent{luaotfload.sty}.
-% }
-% For both, only the set \identifier{default} is predefined.
-%
-% For example, to define a font with the default
-% protrusion vector applied\footnote{%
-% You also need to set
-% \verb|pdfprotrudechars=2| and
-% \verb|pdfadjustspacing=2|
-% to activate protrusion and expansion, respectively.
-% See the
-% \href{http://mirrors.ctan.org/systems/pdftex/manual/pdftex-a.pdf}%
-% {\PDFTEX manual}
-% for details.
-% }:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\test=LatinModernRoman:protrusion=default
-% \end{verbatim}
-% \end{quote}
-% \end{description}
-%
-% \paragraph{Non-standard font features}
-% \identifier{luaotfload} adds a number of features that are not defined
-% in the original \OpenType specification, most of them
-% aiming at emulating the behavior familiar from other \TEX engines.
-% Currently (2013) there are three of them:
-%
-% \begin{description}
-%
-% \item [anum]
-% Substitutes the glyphs in the \abbrev{ascii} number range
-% with their counterparts from eastern Arabic or Persian,
-% depending on the value of \identifier{language}.
-%
-% \item [tlig]
-% Applies legacy \TEX ligatures:
-%
-% \begin{tabular}{rlrl}
-% `` & \verb|``| & '' & \verb|''| \\
-% ` & \verb|`| & ' & \verb|'| \\
-% " & \verb|"| & -- & \verb|--| \\
-% --- & \verb|---| & !` & \verb|!`| \\
-% ?` & \verb|?`| & & \\
-% \end{tabular}
-%
-% \footnote{%
-% These contain the feature set \verb|trep| of earlier
-% versions of \identifier{luaotfload}.
-%
-% Note to \XETEX users: this is the equivalent of the
-% assignment \verb|mapping=text-tex| using \XETEX's input
-% remapping feature.
-% }
-%
-% \item [itlc]
-% Computes italic correction values (active by default).
-%
-% \end{description}
-%
-%
-%
-% \section{Font names database}
-% \label{sec:fontdb}
-%
-% As mentioned above, \identifier{luaotfload} keeps track of which
-% fonts are available to \LUATEX by means of a \emphasis{database}.
-% This allows referring to fonts not only by explicit filenames but
-% also by the proper names contained in the metadata which is often
-% more accessible to humans.\footnote{%
-% The tool \href{http://www.lcdf.org/type/}{\fileent{otfinfo}} (comes
-% with \TEX Live), when invoked on a font file with the \verb|-i|
-% option, lists the variety of name fields defined for it.
-% }
-%
-% When \identifier{luaotfload} is asked to load a font by a font name,
-% it will check if the database exists and load it, or else generate a
-% fresh one.
-% Should it then fail to locate the font, an update to the database is
-% performed in case the font has been added to the system only
-% recently. As soon as the database is updated, the resolver will try
-% and look up the font again, all without user intervention.
-% The goal is for \identifier{luaotfload} to act in the background and
-% behave as unobtrusively as possible, while providing a convenient
-% interface to the fonts installed on the system.
-%
-% Generating the database for the first time may take a while since it
-% inspects every font file on your computer.
-% This is particularly noticeable if it occurs during a typesetting run.
-% In any case, subsequent updates to the database will be quite fast.
-%
-% \subsection[luaotfload-tool / mkluatexfontdb.lua]%
-% {\fileent{luaotfload-tool} /
-% \fileent{mkluatexfontdb.lua}\footnote{%
-% The script may be named just \fileent{mkluatexfontdb} in your
-% distribution.
-% }}
-%
-% It can still be desirable at times to do some of these steps
-% manually, and without having to compile a document.
-% To this end, \identifier{luaotfload} comes with the utility
-% \fileent{luaotfload-tool} that offers an interface to the database
-% functionality.
-% Being a \LUA script, there are two ways to run it:
-% either make it executable (\verb|chmod +x| on unixoid systems) or
-% pass it as an argument to \fileent{texlua}.\footnote{%
-% Tests by the maintainer show only marginal performance gain by
-% running with Luigi Scarso’s
-% \href{https://foundry.supelec.fr/projects/luajittex/}%
-% {\identifier{Luajit\kern-.25ex\TEX}},
-% which is probably due to the fact that most of the time is spent
-% on file system operations.
-%
-% \emphasis{Note}:
-% On \abbrev{MS} \identifier{Windows} systems, the script can be run
-% either by calling the wrapper application
-% \fileent{luaotfload-tool.exe} or as
-% \verb|texlua.exe luaotfload-tool.lua|.
-% }
-% Invoked with the argument \verb|--update| it will perform a database
-% update, scanning for fonts not indexed.
-%
-% \begin{quote}
-% \begin{verbatim}
-% luaotfload-tool --update
-% \end{verbatim}
-% \end{quote}
-%
-% Adding the \verb|--force| switch will initiate a complete
-% rebuild of the database.
-%
-% \begin{quote}
-% \begin{verbatim}
-% luaotfload-tool --update --force
-% \end{verbatim}
-% \end{quote}
-%
-% For sake of backwards compatibility, \fileent{luaotfload-tool} may be
-% renamed or symlinked to \fileent{mkluatexfontdb}.
-% Whenever it is run under this name, it will update the database
-% first, mimicking the behavior of earlier versions of
-% \identifier{luaotfload}.
-%
-% \subsection{Search Paths}
-%
-% \identifier{luaotfload} scans those directories where fonts are
-% expected to be located on a given system.
-% On a Linux machine it follows the paths listed in the
-% \identifier{Fontconfig} configuration files;
-% consult \verb|man 5 fonts.conf| for further information.
-% On \identifier{Windows} systems, the standard location is
-% \verb|Windows\Fonts|,
-% while \identifier{Mac OS~X} requires a multitude of paths to
-% be examined.
-% The complete list is is given in table \ref{table-searchpaths}.
-% Other paths can be specified by setting the environment variable
-% \verb+OSFONTDIR+.
-% If it is non-empty, then search will be extended to the included
-% directories.
-%
-% \begin{table}[t]
-% \hrule
-% \caption{List of paths searched for each supported operating
-% system.}
-% \renewcommand{\arraystretch}{1.2}
-% \begin{center}
-% \begin{tabular}{lp{.5\textwidth}}
-% Windows & \verb|%WINDIR%\Fonts|
-% \\
-% Linux & \fileent{/usr/local/etc/fonts/fonts.conf} and\hfill\break
-% \fileent{/etc/fonts/fonts.conf}
-% \\
-% Mac & \fileent{\textasciitilde/Library/Fonts},\break
-% \fileent{/Library/Fonts},\break
-% \fileent{/System/Library/Fonts}, and\hfill\break
-% \fileent{/Network/Library/Fonts}
-% \\
-% \end{tabular}
-% \end{center}
-% \label{table-searchpaths}
-% \hrule
-% \end{table}
-%
-% \subsection{Querying from Outside}
-%
-% \fileent{luaotfload-tool} also provides rudimentary means of
-% accessing the information collected in the font database.
-% If the option \verb|--find=|\emphasis{name} is given, the script will
-% try and search the fonts indexed by \identifier{luaotfload} for a
-% matching name.
-% For instance, the invocation
-%
-% \begin{quote}
-% \begin{verbatim}
-% luaotfload-tool --find="Iwona Regular"
-% \end{verbatim}
-% \end{quote}
-%
-% \noindent
-% will verify if “Iwona Regular” is found in the database and can be
-% readily requested in a document.
-%
-% If you are unsure about the actual font name, then add the
-% \verb|-F| (or \verb|--fuzzy|) switch to the command line to enable
-% approximate matching.
-% Suppose you cannot precisely remember if the variant of
-% \identifier{Iwona} you are looking for was “Bright” or “Light”.
-% The query
-%
-% \begin{quote}
-% \begin{verbatim}
-% luaotfload-tool -F --find="Iwona Bright"
-% \end{verbatim}
-% \end{quote}
-%
-% \noindent
-% will tell you that indeed the latter name is correct.
-%
-% Basic information about fonts in the database can be displayed
-% using the \verb|-i| option (\verb|--info|).
-% \begin{quote}
-% \begin{verbatim}
-% luaotfload-tool -i --find="Iwona Light Italic"
-% \end{verbatim}
-% \end{quote}
-% \noindent
-% The meaning of the printed values is described in section 4.4 of the
-% \LUATEX reference manual.\footnote{%
-% In \TEX Live: \fileent{texmf-dist/doc/luatex/base/luatexref-t.pdf}.
-% }
-%
-% For a much more detailed report about a given font try the \verb|-I| option
-% instead (\verb|--inspect|).
-% \begin{quote}
-% \begin{verbatim}
-% luaotfload-tool -I --find="Iwona Light Italic"
-% \end{verbatim}
-% \end{quote}
-%
-% \verb|luaotfload-tool --help| will list the available command line
-% switches, including some not discussed in detail here.
-% For a full documentation of \identifier{luaotfload-tool} and its
-% capabilities refer to the manpage
-% (\verb|man 1 luaotfload-tool|).\footnote{%
-% Or see \verb|luaotfload-tool.rst| in the source directory.
-% }
-%
-% \subsection{Blacklisting Fonts}
-% \label{font-blacklist}
-%
-% Some fonts are problematic in general, or just in \LUATEX.
-% If you find that compiling your document takes far too long or eats
-% away all your system’s memory, you can track down the culprit by
-% running \verb|luaotfload-tool -v| to increase verbosity.
-% Take a note of the \emphasis{filename} of the font that database
-% creation fails with and append it to the file
-% \fileent{luaotfload-blacklist.cnf}.
-%
-% A blacklist file is a list of font filenames, one per line.
-% Specifying the full path to where the file is located is optional, the
-% plain filename should suffice.
-% File extensions (\fileent{.otf}, \fileent{.ttf}, etc.) may be omitted.
-% Anything after a percent (|%|) character until the end of the line
-% is ignored, so use this to add comments.
-% Place this file to some location where the \identifier{kpse}
-% library can find it, e.~g.
-% \fileent{texmf-local/tex/luatex/luaotfload} if you are running
-% \identifier{\TEX Live},\footnote{%
-% You may have to run \verb|mktexlsr| if you created a new file in
-% your \fileent{texmf} tree.
-% }
-% or just leave it in the working directory of your document.
-% \identifier{luaotfload} reads all files named
-% \fileent{luaotfload-blacklist.cnf} it finds, so the fonts in
-% \fileent{./luaotfload-blacklist.cnf} extend the global blacklist.
-%
-% Furthermore, a filename prepended with a dash character (|-|) is
-% removed from the blacklist, causing it to be temporarily whitelisted
-% without modifying the global file.
-% An example with explicit paths:
-%
-% \begin{verbatim}
-% % example otf-blacklist.cnf
-% /Library/Fonts/GillSans.ttc % Luaotfload ignores this font.
-% -/Library/Fonts/Optima.ttc % This one is usable again, even if
-% % blacklisted somewhere else.
-% \end{verbatim}
-%
-% \section{Files from \CONTEXT and \LUATEX-Fonts}
-%
-% \identifier{luaotfload} relies on code originally written by Hans
-% Hagen\footnote{%
-% The creator of the \href{http://wiki.contextgarden.net}{\CONTEXT}
-% format.
-% }
-% for and tested with \CONTEXT.
-% It integrates the font loader as distributed in
-% the \identifier{\LUATEX-Fonts} package.
-% The original \LUA source files have been combined using the
-% \fileent{mtx-package} script into a single, self-contained blob.
-% In this form the font loader has no further dependencies\footnote{%
-% It covers, however, to some extent the functionality of the
-% \identifier{lualibs} package.
-% }
-% and requires only minor adaptions to integrate into
-% \identifier{luaotfload}.
-% The guiding principle is to let \CONTEXT/\LUATEX-Fonts take care of
-% the implementation, and update the imported code from time to time.
-% As maintainers, we aim at importing files from upstream essentially
-% \emphasis{unmodified}, except for renaming them to prevent name
-% clashes.
-% This job has been greatly alleviated since the advent of
-% \LUATEX-Fonts, prior to which the individual dependencies had to be
-% manually spotted and extracted from the \CONTEXT source code in a
-% complicated and error-prone fashion.
-%
-% Below is a commented list of the files distributed with
-% \identifier{luaotfload} in one way or the other.
-% See figure \ref{file-graph} on page \pageref{file-graph} for a
-% graphical representation of the dependencies.
-% From \LUATEX-Fonts, only the file \fileent{luatex-fonts-merged.lua}
-% has been imported as \fileent{luaotfload-fontloader.lua}.
-% It is generated by \fileent{mtx-package}, a \LUA source code merging
-% too developed by Hans Hagen.\footnote{%
-% \fileent{mtx-package} is
-% \href
-% {http://repo.or.cz/w/context.git/blob_plain/refs/heads/origin:/scripts/context/lua/mtx-package.lua}
-% {part of \CONTEXT}
-% and requires \fileent{mtxrun}.
-% Run
-% \verb|mtxrun --script package --help|
-% to display further information.
-% For the actual merging code see the file
-% \fileent{util-mrg.lua} that is part of \CONTEXT.
-% }
-% It houses several \LUA files that can be classed in three
-% categories.
-%
-% \begin{itemize}
-% \let\normalitem=\item
-% \def\incitem#1{%
-% \normalitem{\fileent{#1}}
-% }
-% \normalitem \emphasis{\LUA utility libraries}, a subset
-% of what is provided by the \identifier{lualibs}
-% package.
-%
-% \begin{multicols}{2}
-% \begin{itemize}
-% \incitem{l-lua.lua} \incitem{l-lpeg.lua}
-% \incitem{l-function.lua} \incitem{l-string.lua}
-% \incitem{l-table.lua} \incitem{l-io.lua}
-% \incitem{l-file.lua} \incitem{l-boolean.lua}
-% \incitem{l-math.lua} \incitem{util-str.lua}
-% \end{itemize}
-% \end{multicols}
-%
-% \normalitem The \emphasis{font loader} itself.
-% These files have been written for
-% \LUATEX-Fonts and they are distributed along
-% with \identifier{luaotfload}.
-% \begin{multicols}{2}
-% \begin{itemize}
-% \incitem{luatex-basics-gen.lua}
-% \incitem{luatex-basics-nod.lua}
-% \incitem{luatex-fonts-enc.lua}
-% \incitem{luatex-fonts-syn.lua}
-% \incitem{luatex-fonts-tfm.lua}
-% \incitem{luatex-fonts-chr.lua}
-% \incitem{luatex-fonts-lua.lua}
-% \incitem{luatex-fonts-def.lua}
-% \incitem{luatex-fonts-ext.lua}
-% \incitem{luatex-fonts-cbk.lua}
-% \end{itemize}
-% \end{multicols}
-%
-% \normalitem Code related to \emphasis{font handling and
-% node processing}, taken directly from
-% \CONTEXT.
-% \begin{multicols}{2}
-% \begin{itemize}
-% \incitem{data-con.lua} \incitem{font-ini.lua}
-% \incitem{font-con.lua} \incitem{font-cid.lua}
-% \incitem{font-map.lua} \incitem{font-oti.lua}
-% \incitem{font-otf.lua} \incitem{font-otb.lua}
-% \incitem{node-inj.lua} \incitem{font-ota.lua}
-% \incitem{font-otn.lua} \incitem{font-def.lua}
-% \incitem{font-otp.lua}
-% \end{itemize}
-% \end{multicols}
-% \end{itemize}
-%
-% Note that if \identifier{luaotfload} cannot locate the
-% merged file, it will load the individual \LUA libraries
-% instead.
-% Their names remain the same as in \CONTEXT (without the
-% \verb|otfl|-prefix) since we imported the relevant section of
-% \fileent{luatex-fonts.lua} unmodified into \fileent{luaotfload.lua}.
-% Thus if you prefer running bleeding edge code from the
-% \CONTEXT beta, all you have to do is remove
-% \fileent{luaotfload-merged.lua} from the search path.
-%
-% Also, the merged file at some point
-% loads the Adobe Glyph List from a \LUA table that is contained in
-% \fileent{luaotfload-glyphlist.lua}, which is automatically generated by the
-% script \fileent{mkglyphlist}.\footnote{%
-% See \fileent{luaotfload-font-enc.lua}.
-% The hard-coded file name is why we have to replace the procedure
-% that loads the file in \fileent{luaotfload-override.lua}.
-% }
-% There is a make target \identifier{glyphs} that will create a fresh
-% glyph list so we don’t need to import it from \CONTEXT
-% any longer.
-%
-% In addition to these, \identifier{luaotfload} requires a number of
-% files not contained in the merge. Some of these have no equivalent in
-% \LUATEX-Fonts or \CONTEXT, some were taken unmodified from the
-% latter.
-%
-% \begin{itemize}
-% \let\normalitem=\item
-% \def\ouritem#1{%
-% \normalitem{\fileent{#1}}%
-% \space--\hskip1em
-% }
-% \ouritem {luaotfload-features.lua} font feature handling;
-% incorporates some of the code from
-% \fileent{font-otc} from \CONTEXT;
-% \ouritem {luaotfload-override.lua} overrides the \CONTEXT logging
-% functionality.
-% \ouritem {luaotfload-loaders.lua} registers the \OpenType
-% font reader as handler for
-% Postscript fonts
-% (\abbrev{pfa}, \abbrev{pfb}).
-% \ouritem {luaotfload-database.lua} font names database.
-% \ouritem {luaotfload-colors.lua} color handling.
-% \ouritem {luaotfload-auxiliary.lua} access to internal functionality
-% for package authors
-% (proposals for additions welcome).
-% \ouritem {luaotfload-letterspace.lua} font-based letterspacing.
-% \end{itemize}
-%
-% \begin{figure}[b]
-% \caption{Schematic of the files in \identifier{Luaotfload}}
-% \includegraphics[width=\textwidth]{filegraph.pdf}
-% \label{file-graph}
-% \end{figure}
-%
-% \section{Auxiliary Functions}
-%
-% With release version 2.2, \identifier{luaotfload} received
-% additional functions for package authors to call from outside
-% (see the file \fileent{luaotfload-auxiliary.lua} for details).
-% The purpose of this addition twofold.
-% Firstly, \identifier{luaotfload} failed to provide a stable interface
-% to internals in the past which resulted in an unmanageable situation
-% of different packages abusing the raw access to font objects by means
-% of the \luafunction{patch_font} callback.
-% When the structure of the font object changed due to an update, all
-% of these imploded and several packages had to be fixed while
-% simultaneously providing fallbacks for earlier versions.
-% Now the patching is done on the \identifier{luaotfload} side and can
-% be adapted with future modifications to font objects without touching
-% the packages that depend on it.
-% Second, some the capabilities of the font loader and the names
-% database are not immediately relevant in \identifier{luaotfload}
-% itself but might nevertheless be of great value to package authors or
-% end users.
-%
-% Note that the current interface is not yet set in stone and the
-% development team is open to suggestions for improvements or
-% additions.
-%
-% \subsection{Callback Functions}
-%
-% The \luafunction{patch_font} callback is inserted in the wrapper
-% \identifier{luaotfload} provides for the font definition callback
-% (see below, page \pageref{define-font}).
-% At this place it allows manipulating the font object immediately after
-% the font loader is done creating it.
-% For a short demonstration of its usefulness, here is a snippet that
-% writes an entire font object to the file \fileent{fontdump.lua}:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \input luaotfload.sty
-% \directlua{
-% local dumpfile = "fontdump.lua"
-% local dump_font = function (tfmdata)
-% local data = table.serialize(tfmdata)
-% io.savedata(dumpfile, data)
-% end
-%
-% luatexbase.add_to_callback(
-% "luaotfload.patch_font",
-% dump_font,
-% "my_private_callbacks.dump_font"
-% )
-% }
-% \font\dumpme=name:Iwona
-% \bye
-% \end{verbatim}
-% \end{quote}
-%
-% \emphasis{Beware}: this creates a Lua file of around 150,000 lines of
-% code, taking up 3~\abbrev{mb} of disk space.
-% By inspecting the output you can get a first impression of how a font
-% is structured in \LUATEX’s memory, what elements it is composed of,
-% and in what ways it can be rearranged.
-%
-% \subsubsection{Compatibility with Earlier Versions}
-%
-% As has been touched on in the preface to this section, the structure
-% of the object as returned by the fontloader underwent rather drastic
-% changes during different stages of its development, and not all
-% packages that made use of font patching have kept up with every one
-% of it.
-% To ensure compatibility with these as well as older versions of
-% some packages, \identifier{luaotfload} sets up copies of or references
-% to data in the font table where it used to be located.
-% For instance, important parameters like the requested point size, the
-% units factor, and the font name have again been made accessible from
-% the toplevel of the table even though they were migrated to different
-% subtables in the meantime.
-%
-% \subsubsection{Patches}
-%
-% These are mostly concerned with establishing compatibility with
-% \XETEX.
-%
-% \begin{itemize}
-% \let\normalitem=\item
-% \def\ouritem#1{%
-% \normalitem{\luafunction{#1}}%
-% \hfill\break
-% }
-%
-% \ouritem {set_sscale_dimens}
-% Calculate \texmacro{fontdimen}s 10 and 11 to emulate \XETEX.
-%
-% \ouritem {set_capheight}
-% Calculates \texmacro{fontdimen} 8 like \XETEX.
-%
-% \ouritem {patch_cambria_domh}
-% Correct some values of the font \emphasis{Cambria Math}.
-%
-% \end{itemize}
-%
-% \subsection{Package Author’s Interface}
-%
-% As \LUATEX release 1.0 is nearing, the demand for a reliable interface
-% for package authors increases.
-%
-% \subsubsection{Font Properties}
-%
-% Below functions mostly concern querying the different components of a
-% font like for instance the glyphs it contains, or what font features
-% are defined for which scripts.
-%
-% \begin{itemize}
-% \let\normalitem=\item
-% \def\ouritem#1{%
-% \normalitem{\luafunction{#1}}%
-% \hfill\break
-% }
-%
-% \ouritem {aux.font_has_glyph (id : int, index : int)}
-% Predicate that returns true if the font \luafunction{id}
-% has glyph \luafunction{index}.
-%
-% \ouritem {aux.slot_of_name(name : string)}
-% Translates an Adobe Glyph name to the corresponding glyph
-% slot.
-%
-% \ouritem {aux.name_of_slot(slot : int)}
-% The inverse of \luafunction{slot_of_name}; note that this
-% might be incomplete as multiple glyph names may map to the
-% same codepoint, only one of which is returned by
-% \luafunction{name_of_slot}.
-%
-% \ouritem {aux.provides_script(id : int, script : string)}
-% Test if a font supports \luafunction{script}.
-%
-% \ouritem {aux.provides_language(id : int, script : string, language : string)}
-% Test if a font defines \luafunction{language} for a given
-% \luafunction{script}.
-%
-% \ouritem {aux.provides_feature(id : int, script : string,
-% language : string, feature : string)}
-% Test if a font defines \luafunction{feature} for
-% \luafunction{language} for a given \luafunction{script}.
-%
-% \ouritem {aux.get_math_dimension(id : int, dimension : string)}
-% Get the dimension \luafunction{dimension} of font \luafunction{id}.
-%
-% \ouritem {aux.sprint_math_dimension(id : int, dimension : string)}
-% Same as \luafunction{get_math_dimension()}, but output the value
-% in scaled points at the \TEX end.
-%
-% \end{itemize}
-%
-% \subsubsection{Database}
-%
-% \begin{itemize}
-% \let\normalitem=\item
-% \def\ouritem#1{%
-% \normalitem{\luafunction{#1}}%
-% \hfill\break
-% }
-%
-% \ouritem {aux.scan_external_dir(dir : string)}
-% Include fonts in directory \luafunction{dir} in font lookups without
-% adding them to the database.
-%
-% \end{itemize}
-%
-% \section{Troubleshooting}
-%
-% \subsection {Database Generation}
-% If you encounter problems with some fonts, please first update to the latest
-% version of this package before reporting a bug, as
-% \identifier{luaotfload} is under active development and still a
-% moving target.
-% The development takes place on \identifier{github} at
-% \url{https://github.com/lualatex/luaotfload} where there is an issue
-% tracker for submitting bug reports, feature requests and the likes
-% requests and the likes.
-%
-% Bug reports are more likely to be addressed if they contain the output of
-%
-% \begin{quote}
-% \begin{verbatim}
-% luaotfload-tool --diagnose=environment,files,permissions
-% \end{verbatim}
-% \end{quote}
-%
-% \noindent Consult the man page for a description of these options.
-%
-% Errors during database generation can be traced by increasing the
-% verbosity level and redirecting log output to \fileent{stdout}:
-%
-% \begin{quote}
-% \begin{verbatim}
-% luaotfload-tool -fuvvv --log=stdout
-% \end{verbatim}
-% \end{quote}
-%
-% \noindent or to a file in \fileent{/tmp}:
-%
-% \begin{quote}
-% \begin{verbatim}
-% luaotfload-tool -fuvvv --log=file
-% \end{verbatim}
-% \end{quote}
-%
-% \noindent In the latter case, invoke the \verb|tail(1)| utility on the file
-% for live monitoring of the progress.
-%
-% If database generation fails, the font last printed to the terminal or log
-% file is likely to be the culprit.
-% Please specify it when reporting a bug, and blacklist it for the time
-% being (see above, page \pageref{font-blacklist}).
-%
-% \subsection {Font Features}
-% A common problem is the lack of features for some
-% \OpenType fonts even when specified.
-% This can be related to the fact that some fonts do not provide
-% features for the \verb|dflt| script (see above on page
-% \pageref{script-tag}),
-% which is the default one in this package.
-% If this happens, assigning a noth script when the font is defined should
-% fix it.
-% For example with \verb|latn|:
-%
-% \begin{quote}
-% \begin{verbatim}
-% \font\test=file:MyFont.otf:script=latn;+liga;
-% \end{verbatim}
-% \end{quote}
-%
-% You can get a list of features that a font defines for scripts and languages
-% by querying it in \fileent{luaotfload-tool}:
-%
-% \begin{quote}
-% \begin{verbatim}
-% luaotfload-tool --find="Iwona" --inspect
-% \end{verbatim}
-% \end{quote}
-%
-% \subsection {\LUATEX Programming}
-% Another strategy that helps avoiding problems is to not access raw \LUATEX
-% internals directly.
-% Some of them, even though they are dangerous to access, have not been
-% overridden or disabled.
-% Thus, whenever possible prefer the functions in the
-% \luafunction{aux} namespace over direct manipulation of font objects.
-% For example, raw access to the \luafunction{font.fonts} table like:
-%
-% \begin{quote}
-% \begin{verbatim}
-% local somefont = font.fonts[2]
-% \end{verbatim}
-% \end{quote}
-%
-% \noindent can render already defined fonts unusable.
-% Instead, the function \luafunction{font.getfont()} should be used because
-% it has been replaced by a safe variant.
-%
-% However, \luafunction{font.getfont()} only covers fonts handled by the font
-% loader, e.~g. \identifier{OpenType} and \identifier{TrueType} fonts, but
-% not \abbrev{tfm} or \abbrev{ofm}.
-% Should you absolutely require access to all fonts known to \LUATEX, including
-% the virtual and autogenerated ones, then you need to query both
-% \luafunction{font.getfont()} and \luafunction{font.fonts}.
-% In this case, best define you own accessor:
-%
-% \begin{quote}
-% \begin{verbatim}
-% local unsafe_getfont = function (id)
-% local tfmdata = font.getfont (id)
-% if not tfmdata then
-% tfmdata = font.fonts[id]
-% end
-% return tfmdata
-% end
-%
-% --- use like getfont()
-% local somefont = unsafe_getfont (2)
-% \end{verbatim}
-% \end{quote}
-%
-% \part{Implementation}
-%
-% \section{\fileent{luaotfload.lua}}
-%
-% This file initializes the system and loads the font loader.
-% To minimize potential conflicts between other packages and the
-% code imported from \CONTEXT, several precautions are in order.
-% Some of the functionality that the font loader expects to be present,
-% like raw access to callbacks, are assumed to have been disabled by
-% \identifier{luatexbase} when this file is processed.
-% In some cases it is possible to trick it by putting dummies into
-% place and restoring the behavior from \identifier{luatexbase} after
-% initilization.
-% Other cases such as attribute allocation require that we hook the
-% functionality from \identifier{luatexbase} into locations where they
-% normally wouldn’t be.
-%
-% Anyways we can import the code base without modifications, which is
-% due mostly to the extra effort by
-% Hans Hagen to make \LUATEX-Fonts self-contained and encapsulate it,
-% and especially due to his willingness to incorporate our suggestions.
-%
-% \iffalse
-%<*lua>
-% \fi
-% \begin{macrocode}
-luaotfload = luaotfload or {}
-local luaotfload = luaotfload
-
-config = config or { }
-config.luaotfload = config.luaotfload or { }
-------.luaotfload.resolver = config.luaotfload.resolver or "normal"
-config.luaotfload.resolver = config.luaotfload.resolver or "cached"
-config.luaotfload.definer = config.luaotfload.definer or "patch"
-config.luaotfload.compatibility = config.luaotfload.compatibility or false
-config.luaotfload.loglevel = config.luaotfload.loglevel or 2
-config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter"
-config.luaotfload.prioritize = config.luaotfload.prioritize or "sys"
-config.luaotfload.names_dir = config.luaotfload.names_dir or "names"
-config.luaotfload.cache_dir = config.luaotfload.cache_dir or "fonts"
-config.luaotfload.index_file = config.luaotfload.index_file or "luaotfload-names.lua"
-config.luaotfload.formats = config.luaotfload.formats or "otf,ttf,ttc,dfont"
-if not config.luaotfload.strip then
- config.luaotfload.strip = true
-end
-
-luaotfload.module = {
- name = "luaotfload",
- version = 2.40005, --- 2.4-4
- date = "2014/05/18",
- description = "OpenType layout system.",
- author = "Elie Roux & Hans Hagen",
- copyright = "Elie Roux",
- license = "GPL v2.0"
-}
-
-local luatexbase = luatexbase
-
-local setmetatable = setmetatable
-local type, next = type, next
-
-local kpsefind_file = kpse.find_file
-local lfsisfile = lfs.isfile
-
-local add_to_callback, create_callback =
- luatexbase.add_to_callback, luatexbase.create_callback
-local reset_callback, call_callback =
- luatexbase.reset_callback, luatexbase.call_callback
-
-local dummy_function = function () end
-
-local error, warning, info, log =
- luatexbase.provides_module(luaotfload.module)
-
-luaotfload.error = error
-luaotfload.warning = warning
-luaotfload.info = info
-luaotfload.log = log
-
-% \end{macrocode}
-% We set the minimum version requirement for \LUATEX to v0.76,
-% because the font loader requires recent features like direct
-% attribute indexing and \luafunction{node.end_of_math()} that aren’t
-% available in earlier versions.\footnote{%
-% See Taco’s announcement of v0.76:
-% \url{http://comments.gmane.org/gmane.comp.tex.luatex.user/4042}
-% and this commit by Hans that introduced those features.
-% \url{http://repo.or.cz/w/context.git/commitdiff/a51f6cf6ee087046a2ae5927ed4edff0a1acec1b}.
-% }
-%
-% \begin{macrocode}
-
-local luatex_version = 76
-
-if tex.luatexversion < luatex_version then
- warning("LuaTeX v%.2f is old, v%.2f is recommended.",
- tex.luatexversion/100,
- luatex_version /100)
- --- we install a fallback for older versions as a safety
- if not node.end_of_math then
- local math_t = node.id"math"
- local traverse_nodes = node.traverse_id
- node.end_of_math = function (n)
- for n in traverse_nodes(math_t, n.next) do
- return n
- end
- end
- end
-end
-
-% \end{macrocode}
-% \subsection{Module loading}
-% We load the files imported from \CONTEXT with this function.
-% It automatically prepends the prefix \fileent{luaotfload-} to its
-% argument, so we can refer to the files with their actual \CONTEXT name.
-%
-% \begin{macrocode}
-
-local fl_prefix = "luaotfload" -- “luatex” for luatex-plain
-local loadmodule = function (name)
- require(fl_prefix .."-"..name)
-end
-
-% \end{macrocode}
-% Before \TeX Live 2013 version, \LUATEX had a bug that made ofm fonts fail
-% when called with their extension. There was a side-effect making ofm
-% totally unloadable when luaotfload was present. The following lines are
-% a patch for this bug. The utility of these lines is questionable as they
-% are not necessary since \TeX Live 2013. They should be removed in the next
-% version.
-%
-% \begin{macrocode}
-local Cs, P, lpegmatch = lpeg.Cs, lpeg.P, lpeg.match
-
-local p_dot, p_slash = P".", P"/"
-local p_suffix = (p_dot * (1 - p_dot - p_slash)^1 * P(-1)) / ""
-local p_removesuffix = Cs((p_suffix + 1)^1)
-
-local find_vf_file = function (name)
- local fullname = kpsefind_file(name, "ovf")
- if not fullname then
- --fullname = kpsefind_file(file.removesuffix(name), "ovf")
- fullname = kpsefind_file(lpegmatch(p_removesuffix, name), "ovf")
- end
- if fullname then
- log("loading virtual font file %s.", fullname)
- end
- return fullname
-end
-
-% \end{macrocode}
-% \subsection{Preparing the Font Loader}
-% We treat the fontloader as a black box so behavior is consistent
-% between formats.
-% We do no longer run the intermediate wrapper file
-% \fileent{luaotfload-fonts.lua} which we used to import from
-% \href{http://standalone.contextgarden.net/current/context/experimental/tex/generic/context/luatex/}{\LUATEX-Plain}.
-% Rather, we load the fontloader code directly in the same fashion as
-% \identifier{luatex-fonts}.
-% How this is executed depends on the presence on the \emphasis{merged
-% font loader code}.
-% In \identifier{luaotfload} this is contained in the file
-% \fileent{luaotfload-merged.lua}.
-% If this file cannot be found, the original libraries from \CONTEXT of
-% which the merged code was composed are loaded instead.
-% The imported font loader will call \luafunction{callback.register} once
-% while reading \fileent{font-def.lua}.
-% This is unavoidable unless we modify the imported files, but harmless
-% if we make it call a dummy instead.
-% However, this problem might vanish if we decide to do the merging
-% ourselves, like the \identifier{lualibs} package does.
-% With this step we would obtain the freedom to load our own overrides in
-% the process right where they are needed, at the cost of losing
-% encapsulation.
-% The decision on how to progress is currently on indefinite hold.
-%
-% \begin{macrocode}
-
-local starttime = os.gettimeofday()
-
-local trapped_register = callback.register
-callback.register = dummy_function
-
-% \end{macrocode}
-% By default, the fontloader requires a number of \emphasis{private
-% attributes} for internal use.
-% These must be kept consistent with the attribute handling methods as
-% provided by \identifier{luatexbase}.
-% Our strategy is to override the function that allocates new attributes
-% before we initialize the font loader, making it a wrapper around
-% \luafunction{luatexbase.new_attribute}.\footnote{%
-% Many thanks, again, to Hans Hagen for making this part
-% configurable!
-% }
-% The attribute identifiers are prefixed “\fileent{luaotfload@}” to
-% avoid name clashes.
-%
-% \begin{macrocode}
-
-do
- local new_attribute = luatexbase.new_attribute
- local the_attributes = luatexbase.attributes
-
- attributes = attributes or { }
-
- attributes.private = function (name)
- local attr = "luaotfload@" .. name --- used to be: “otfl@”
- local number = the_attributes[attr]
- if not number then
- number = new_attribute(attr)
- end
- return number
- end
-end
-
-% \end{macrocode}
-% These next lines replicate the behavior of \fileent{luatex-fonts.lua}.
-%
-% \begin{macrocode}
-
-local context_environment = { }
-
-local push_namespaces = function ()
- log("push namespace for font loader")
- local normalglobal = { }
- for k, v in next, _G do
- normalglobal[k] = v
- end
- return normalglobal
-end
-
-local pop_namespaces = function (normalglobal, isolate)
- if normalglobal then
- local _G = _G
- local mode = "non-destructive"
- if isolate then mode = "destructive" end
- log("pop namespace from font loader -- " .. mode)
- for k, v in next, _G do
- if not normalglobal[k] then
- context_environment[k] = v
- if isolate then
- _G[k] = nil
- end
- end
- end
- for k, v in next, normalglobal do
- _G[k] = v
- end
- -- just to be sure:
- setmetatable(context_environment,_G)
- else
- log("irrecoverable error during pop_namespace: no globals to restore")
- os.exit()
- end
-end
-
-luaotfload.context_environment = context_environment
-luaotfload.push_namespaces = push_namespaces
-luaotfload.pop_namespaces = pop_namespaces
-
-local our_environment = push_namespaces()
-
-% \end{macrocode}
-% The font loader requires that the attribute with index zero be zero.
-% We happily oblige.
-% (Cf. \fileent{luatex-fonts-nod.lua}.)
-%
-% \begin{macrocode}
-
-tex.attribute[0] = 0
-
-% \end{macrocode}
-% Now that things are sorted out we can finally load the fontloader.
-%
-% \begin{macrocode}
-
-loadmodule"fontloader.lua"
----loadmodule"font-odv.lua" --- <= Devanagari support from Context
-
-if fonts then
-
- if not fonts._merge_loaded_message_done_ then
- log [["I am using the merged version of 'luaotfload.lua' here.]]
- log [[ If you run into problems or experience unexpected]]
- log [[ behaviour, and if you have ConTeXt installed you can try]]
- log [[ to delete the file 'luaotfload-merged.lua' as I might]]
- log [[ then use the possibly updated libraries. The merged]]
- log [[ version is not supported as it is a frozen instance.]]
- log [[ Problems can be reported to the ConTeXt mailing list."]]
- end
- fonts._merge_loaded_message_done_ = true
-
-else--- the loading sequence is known to change, so this might have to
- --- be updated with future updates!
- --- do not modify it though unless there is a change to the merged
- --- package!
- loadmodule("l-lua.lua")
- loadmodule("l-lpeg.lua")
- loadmodule("l-function.lua")
- loadmodule("l-string.lua")
- loadmodule("l-table.lua")
- loadmodule("l-io.lua")
- loadmodule("l-file.lua")
- loadmodule("l-boolean.lua")
- loadmodule("l-math.lua")
- loadmodule("util-str.lua")
- loadmodule('luatex-basics-gen.lua')
- loadmodule('data-con.lua')
- loadmodule('luatex-basics-nod.lua')
- loadmodule('font-ini.lua')
- loadmodule('font-con.lua')
- loadmodule('luatex-fonts-enc.lua')
- loadmodule('font-cid.lua')
- loadmodule('font-map.lua')
- loadmodule('luatex-fonts-syn.lua')
- loadmodule('luatex-fonts-tfm.lua')
- loadmodule('font-oti.lua')
- loadmodule('font-otf.lua')
- loadmodule('font-otb.lua')
- loadmodule('node-inj.lua')
- loadmodule('font-ota.lua')
- loadmodule('font-otn.lua')
- loadmodule('font-otp.lua')--- since 2013-04-23
- loadmodule('luatex-fonts-lua.lua')
- loadmodule('font-def.lua')
- loadmodule('luatex-fonts-def.lua')
- loadmodule('luatex-fonts-ext.lua')
- loadmodule('luatex-fonts-cbk.lua')
-end --- non-merge fallback scope
-
-% \end{macrocode}
-% Here we adjust the globals created during font loader initialization.
-% If the second argument to \luafunction{pop_namespaces()} is \verb|true|
-% this will restore the state of \luafunction{_G}, eliminating every
-% global generated since the last call to \luafunction{push_namespaces()}.
-% At the moment we see no reason to do this, and since the font loader is
-% considered an essential part of \identifier{luatex} as well as a very
-% well organized piece of code, we happily concede it the right to add to
-% \luafunction{_G} if needed.
-%
-% \begin{macrocode}
-
-pop_namespaces(our_environment, false)-- true)
-
-log("fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime)
-
-% \end{macrocode}
-% \subsection{Callbacks}
-% After the fontloader is ready we can restore the callback trap from
-% \identifier{luatexbase}.
-%
-% \begin{macrocode}
-
-callback.register = trapped_register
-
-% \end{macrocode}
-% We do our own callback handling with the means provided by luatexbase.
-% Note: \luafunction{pre_linebreak_filter} and \luafunction{hpack_filter}
-% are coupled in \CONTEXT in the concept of \emphasis{node processor}.
-%
-% \begin{macrocode}
-
-add_to_callback("pre_linebreak_filter",
- nodes.simple_font_handler,
- "luaotfload.node_processor",
- 1)
-add_to_callback("hpack_filter",
- nodes.simple_font_handler,
- "luaotfload.node_processor",
- 1)
-add_to_callback("find_vf_file",
- find_vf_file, "luaotfload.find_vf_file")
-
-loadmodule"override.lua" --- “luat-ovr”
-
-logs.set_loglevel(config.luaotfload.loglevel)
-
-% \end{macrocode}
-% Now we load the modules written for \identifier{luaotfload}.
-%
-% \begin{macrocode}
-loadmodule"loaders.lua" --- “font-pfb” new in 2.0, added 2011
-loadmodule"database.lua" --- “font-nms”
-loadmodule"colors.lua" --- “font-clr”
-
-% \end{macrocode}
-% Relying on the \verb|name:| resolver for everything has been the source
-% of permanent trouble with the database.
-% With the introduction of the new syntax parser we now have enough
-% granularity to distinguish between the \XETEX emulation layer and the
-% genuine \verb|name:| and \verb|file:| lookups of \LUATEX-Fonts.
-% Another benefit is that we can now easily plug in or replace new lookup
-% behaviors if necessary.
-% The name resolver remains untouched, but it calls
-% \luafunction{fonts.names.resolve()} internally anyways (see
-% \fileent{luaotfload-database.lua}).
-%
-% \begin{macrocode}
-
-local filesuffix = file.suffix
-local fileremovesuffix = file.removesuffix
-local request_resolvers = fonts.definers.resolvers
-local formats = fonts.formats
-local names = fonts.names
-formats.ofm = "type1"
-
-fonts.encodings.known = fonts.encodings.known or { }
-
-% \end{macrocode}
-% \identifier{luaotfload} promises easy access to system fonts.
-% Without additional precautions, this cannot be achieved by
-% \identifier{kpathsea} alone, because it searches only the
-% \fileent{texmf} directories by default.
-% Although it is possible for \identifier{kpathsea} to include extra
-% paths by adding them to the \verb|OSFONTDIR| environment variable,
-% this is still short of the goal »\emphasis{it just works!}«.
-% When building the font database \identifier{luaotfload} scans
-% system font directories anyways, so we already have all the
-% information for looking sytem fonts.
-% With the release version 2.2 the file names are indexed in the database
-% as well and we are ready to resolve \verb|file:| lookups this way.
-% Thus we no longer need to call the \identifier{kpathsea} library in
-% most cases when looking up font files, only when generating the database,
-% and when verifying the existence of a file in the \fileent{texmf} tree.
-%
-% \begin{macrocode}
-
-local resolve_file = names.crude_file_lookup
---local resolve_file = names.crude_file_lookup_verbose
-local resolve_name = names.resolve_name
-
-local file_resolver = function (specification)
- local name = resolve_file (specification.name)
- local suffix = filesuffix(name)
- if formats[suffix] then
- specification.forced = string.lower (suffix)
- specification.forcedname = file.removesuffix(name)
- else
- specification.name = name
- end
-end
-
-request_resolvers.file = file_resolver
-
-% \end{macrocode}
-% We classify as \verb|anon:| those requests that have neither a
-% prefix nor brackets. According to Khaled\footnote{%
-% \url{https://github.com/phi-gamma/luaotfload/issues/4#issuecomment-17090553}.
-% }
-% they are the \XETEX equivalent of a \verb|name:| request, so we will be
-% treating them as such.
-%
-% \begin{macrocode}
-
---request_resolvers.anon = request_resolvers.name
-
-% \end{macrocode}
-% There is one drawback, though.
-% This syntax is also used for requesting fonts in \identifier{Type1}
-% (\abbrev{tfm}, \abbrev{ofm}) format.
-% These are essentially \verb|file:| lookups and must be caught before
-% the \verb|name:| resolver kicks in, lest they cause the database to
-% update.
-% Even if we were to require the \verb|file:| prefix for all
-% \identifier{Type1} requests, tests have shown that certain fonts still
-% include further fonts (e.~g. \fileent{omlgcb.ofm} will ask for
-% \fileent{omsecob.tfm}) \emphasis{using the old syntax}.
-% For this reason, we introduce an extra check with an early return.
-%
-% \begin{macrocode}
-
-local type1_formats = { "tfm", "ofm", }
-
-request_resolvers.anon = function (specification)
- local name = specification.name
- for i=1, #type1_formats do
- local format = type1_formats[i]
- if resolvers.findfile(name, format) then
- specification.forcedname = file.addsuffix(name, format)
- specification.forced = format
- return
- end
- end
- --- under some weird circumstances absolute paths get
- --- passed to the definer; we have to catch them
- --- before the name: resolver misinterprets them.
- name = specification.specification
- local exists, _ = lfsisfile(name)
- if exists then --- garbage; we do this because we are nice,
- --- not because it is correct
- logs.names_report("log", 1, "load", "file %q exists", name)
- logs.names_report("log", 1, "load",
- "... overriding borked anon: lookup with path: lookup")
- specification.name = name
- request_resolvers.path(specification)
- return
- end
- request_resolvers.name(specification)
-end
-
-% \end{macrocode}
-% Prior to version 2.2, \identifier{luaotfload} did not distinguish
-% \verb|file:| and \verb|path:| lookups, causing complications with the
-% resolver.
-% Now we test if the requested name is an absolute path in the file
-% system, otherwise we fall back to the \verb|file:| lookup.
-%
-% \begin{macrocode}
-
-request_resolvers.path = function (specification)
- local name = specification.name
- local exists, _ = lfsisfile(name)
- if not exists then -- resort to file: lookup
- logs.names_report("log", 1, "load",
- "path lookup of %q unsuccessful, falling back to file:",
- name)
- file_resolver (specification)
- else
- local suffix = filesuffix (name)
- if formats[suffix] then
- specification.forced = string.lower (suffix)
- specification.name = file.removesuffix(name)
- specification.forcedname = name
- else
- specification.name = name
- end
- end
-end
-
-% \end{macrocode}
-% {\bfseries EXPERIMENTAL}:
-% \identifier{kpse}-only resolver, for those who can do without system
-% fonts.
-%
-% \begin{macrocode}
-
-request_resolvers.kpse = function (specification)
- local name = specification.name
- local suffix = filesuffix(name)
- if suffix and formats[suffix] then
- name = file.removesuffix(name)
- if resolvers.findfile(name, suffix) then
- specification.forced = string.lower (suffix)
- specification.forcedname = name
- return
- end
- end
- for t, format in next, formats do --- brute force
- if kpse.find_file (name, format) then
- specification.forced = t
- specification.name = name
- return
- end
- end
-end
-
-% \end{macrocode}
-% The \verb|name:| resolver wraps the database function
-% \luafunction{resolve_name}.
-%
-% \begin{macrocode}
-
---- fonts.names.resolvers.name -- Customized version of the
---- generic name resolver.
-
-request_resolvers.name = function (specification)
- local resolved, subfont = resolve_name (specification)
- if resolved then
- specification.resolved = resolved
- specification.sub = subfont
- specification.forced = string.lower (filesuffix (resolved) or "")
- specification.forcedname = resolved
- specification.name = fileremovesuffix (resolved)
- else
- file_resolver (specification)
- end
-end
-
-% \end{macrocode}
-% Also {\bfseries EXPERIMENTAL}:
-% custom file resolvers via callback.
-%
-% \begin{macrocode}
-create_callback("luaotfload.resolve_font", "simple", dummy_function)
-
-request_resolvers.my = function (specification)
- call_callback("luaotfload.resolve_font", specification)
-end
-
-% \end{macrocode}
-% We create a callback for patching fonts on the fly, to be used by other
-% packages.
-% It initially contains the empty function that we are going to override
-% below.
-%
-% \begin{macrocode}
-
-create_callback("luaotfload.patch_font", "simple", dummy_function)
-
-% \end{macrocode}
-% \subsection{\CONTEXT override}
-% \label{define-font}
-% We provide a simplified version of the original font definition
-% callback.
-%
-% \begin{macrocode}
-
-local read_font_file = fonts.definers.read
-
---- spec -> size -> id -> tmfdata
-local patch_defined_font = function (specification, size, id)
- local tfmdata = read_font_file(specification, size, id)
- if type(tfmdata) == "table" and tfmdata.shared then
- --- We need to test for the “shared” field here
- --- or else the fontspec capheight callback will
- --- operate on tfm fonts.
- call_callback("luaotfload.patch_font", tfmdata, specification)
- end
- return tfmdata
-end
-
-reset_callback "define_font"
-
-% \end{macrocode}
-% Finally we register the callbacks.
-%
-% \begin{macrocode}
-
-local font_definer = config.luaotfload.definer
-
-if font_definer == "generic" then
- add_to_callback("define_font",
- fonts.definers.read,
- "luaotfload.define_font",
- 1)
-elseif font_definer == "patch" then
- add_to_callback("define_font",
- patch_defined_font,
- "luaotfload.define_font",
- 1)
-end
-
-loadmodule"features.lua" --- contains what was “font-ltx” and “font-otc”
-loadmodule"letterspace.lua" --- extra character kerning
-loadmodule"auxiliary.lua" --- additionaly high-level functionality (new)
-
-luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec
-
--- vim:tw=71:sw=4:ts=4:expandtab
-
-% \end{macrocode}
-%
-% \iffalse
-%</lua>
-% \fi
-%
-% \section{\fileent{luaotfload.sty}}
-%
-% \iffalse
-%<*package>
-% \fi
-%
-% Classical Plain+\LATEX package initialization.
-%
-% \begin{macrocode}
-\csname ifluaotfloadloaded\endcsname
-\let\ifluaotfloadloaded\endinput
-\bgroup\expandafter\expandafter\expandafter\egroup
-\expandafter\ifx\csname ProvidesPackage\endcsname\relax
- \input luatexbase.sty
-\else
- \NeedsTeXFormat{LaTeX2e}
- \ProvidesPackage{luaotfload}%
- [2014/05/18 v2.4-4 OpenType layout system]
- \RequirePackage{luatexbase}
-\fi
-\ifnum\luatexversion<76
- %% here some deprecation warning would be in order
- \RequireLuaModule{lualibs}
- \RequireLuaModule{luaotfload-legacy}
-\else
- \RequireLuaModule{luaotfload}
-\fi
-\endinput
-% \end{macrocode}
-% \iffalse
-%</package>
-% \fi
-%
-% \clearpage
-% \section{The GNU GPL License v2}
-%
-% The GPL requires the complete license text to be distributed along
-% with the code. I recommend the canonical source, instead:
-% \url{http://www.gnu.org/licenses/old-licenses/gpl-2.0.html}.
-% But if you insist on an included copy, here it is.
-% You might want to zoom in.
-%
-% \newsavebox{\gpl}
-% \begin{lrbox}{\gpl}
-% \begin{minipage}{3\textwidth}
-% \columnsep=3\columnsep
-% \begin{multicols}{3}
-% \begin{center}
-% {\Large GNU GENERAL PUBLIC LICENSE\par}
-% \bigskip
-% {Version 2, June 1991}
-% \end{center}
-%
-% \begin{center}
-% {\parindent 0in
-%
-% Copyright \textcopyright\ 1989, 1991 Free Software Foundation, Inc.
-%
-% \bigskip
-%
-% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-%
-% \bigskip
-%
-% Everyone is permitted to copy and distribute verbatim copies
-% of this license document, but changing it is not allowed.
-% }
-% \end{center}
-%
-% \begin{center}
-% {\bf\large Preamble}
-% \end{center}
-%
-%
-% The licenses for most software are designed to take away your freedom to
-% share and change it. By contrast, the GNU General Public License is
-% intended to guarantee your freedom to share and change free software---to
-% make sure the software is free for all its users. This General Public
-% License applies to most of the Free Software Foundation's software and to
-% any other program whose authors commit to using it. (Some other Free
-% Software Foundation software is covered by the GNU Library General Public
-% License instead.) You can apply it to your programs, too.
-%
-% When we speak of free software, we are referring to freedom, not price.
-% Our General Public Licenses are designed to make sure that you have the
-% freedom to distribute copies of free software (and charge for this service
-% if you wish), that you receive source code or can get it if you want it,
-% that you can change the software or use pieces of it in new free programs;
-% and that you know you can do these things.
-%
-% To protect your rights, we need to make restrictions that forbid anyone to
-% deny you these rights or to ask you to surrender the rights. These
-% restrictions translate to certain responsibilities for you if you
-% distribute copies of the software, or if you modify it.
-%
-% For example, if you distribute copies of such a program, whether gratis or
-% for a fee, you must give the recipients all the rights that you have. You
-% must make sure that they, too, receive or can get the source code. And
-% you must show them these terms so they know their rights.
-%
-% We protect your rights with two steps: (1) copyright the software, and (2)
-% offer you this license which gives you legal permission to copy,
-% distribute and/or modify the software.
-%
-% Also, for each author's protection and ours, we want to make certain that
-% everyone understands that there is no warranty for this free software. If
-% the software is modified by someone else and passed on, we want its
-% recipients to know that what they have is not the original, so that any
-% problems introduced by others will not reflect on the original authors'
-% reputations.
-%
-% Finally, any free program is threatened constantly by software patents.
-% We wish to avoid the danger that redistributors of a free program will
-% individually obtain patent licenses, in effect making the program
-% proprietary. To prevent this, we have made it clear that any patent must
-% be licensed for everyone's free use or not licensed at all.
-%
-% The precise terms and conditions for copying, distribution and
-% modification follow.
-%
-% \begin{center}
-% {\Large \sc Terms and Conditions For Copying, Distribution and
-% Modification}
-% \end{center}
-%
-% \begin{enumerate}
-% \item
-% This License applies to any program or other work which contains a notice
-% placed by the copyright holder saying it may be distributed under the
-% terms of this General Public License. The ``Program'', below, refers to
-% any such program or work, and a ``work based on the Program'' means either
-% the Program or any derivative work under copyright law: that is to say, a
-% work containing the Program or a portion of it, either verbatim or with
-% modifications and/or translated into another language. (Hereinafter,
-% translation is included without limitation in the term ``modification''.)
-% Each licensee is addressed as ``you''.
-%
-% Activities other than copying, distribution and modification are not
-% covered by this License; they are outside its scope. The act of
-% running the Program is not restricted, and the output from the Program
-% is covered only if its contents constitute a work based on the
-% Program (independent of having been made by running the Program).
-% Whether that is true depends on what the Program does.
-%
-% \item You may copy and distribute verbatim copies of the Program's source
-% code as you receive it, in any medium, provided that you conspicuously
-% and appropriately publish on each copy an appropriate copyright notice
-% and disclaimer of warranty; keep intact all the notices that refer to
-% this License and to the absence of any warranty; and give any other
-% recipients of the Program a copy of this License along with the Program.
-%
-% You may charge a fee for the physical act of transferring a copy, and you
-% may at your option offer warranty protection in exchange for a fee.
-%
-% \item
-% You may modify your copy or copies of the Program or any portion
-% of it, thus forming a work based on the Program, and copy and
-% distribute such modifications or work under the terms of Section 1
-% above, provided that you also meet all of these conditions:
-%
-% \begin{enumerate}
-%
-% \item
-% You must cause the modified files to carry prominent notices stating that
-% you changed the files and the date of any change.
-%
-% \item
-% You must cause any work that you distribute or publish, that in
-% whole or in part contains or is derived from the Program or any
-% part thereof, to be licensed as a whole at no charge to all third
-% parties under the terms of this License.
-%
-% \item
-% If the modified program normally reads commands interactively
-% when run, you must cause it, when started running for such
-% interactive use in the most ordinary way, to print or display an
-% announcement including an appropriate copyright notice and a
-% notice that there is no warranty (or else, saying that you provide
-% a warranty) and that users may redistribute the program under
-% these conditions, and telling the user how to view a copy of this
-% License. (Exception: if the Program itself is interactive but
-% does not normally print such an announcement, your work based on
-% the Program is not required to print an announcement.)
-%
-% \end{enumerate}
-%
-%
-% These requirements apply to the modified work as a whole. If
-% identifiable sections of that work are not derived from the Program,
-% and can be reasonably considered independent and separate works in
-% themselves, then this License, and its terms, do not apply to those
-% sections when you distribute them as separate works. But when you
-% distribute the same sections as part of a whole which is a work based
-% on the Program, the distribution of the whole must be on the terms of
-% this License, whose permissions for other licensees extend to the
-% entire whole, and thus to each and every part regardless of who wrote it.
-%
-% Thus, it is not the intent of this section to claim rights or contest
-% your rights to work written entirely by you; rather, the intent is to
-% exercise the right to control the distribution of derivative or
-% collective works based on the Program.
-%
-% In addition, mere aggregation of another work not based on the Program
-% with the Program (or with a work based on the Program) on a volume of
-% a storage or distribution medium does not bring the other work under
-% the scope of this License.
-%
-% \item
-% You may copy and distribute the Program (or a work based on it,
-% under Section 2) in object code or executable form under the terms of
-% Sections 1 and 2 above provided that you also do one of the following:
-%
-% \begin{enumerate}
-%
-% \item
-%
-% Accompany it with the complete corresponding machine-readable
-% source code, which must be distributed under the terms of Sections
-% 1 and 2 above on a medium customarily used for software interchange; or,
-%
-% \item
-%
-% Accompany it with a written offer, valid for at least three
-% years, to give any third party, for a charge no more than your
-% cost of physically performing source distribution, a complete
-% machine-readable copy of the corresponding source code, to be
-% distributed under the terms of Sections 1 and 2 above on a medium
-% customarily used for software interchange; or,
-%
-% \item
-%
-% Accompany it with the information you received as to the offer
-% to distribute corresponding source code. (This alternative is
-% allowed only for noncommercial distribution and only if you
-% received the program in object code or executable form with such
-% an offer, in accord with Subsection b above.)
-%
-% \end{enumerate}
-%
-%
-% The source code for a work means the preferred form of the work for
-% making modifications to it. For an executable work, complete source
-% code means all the source code for all modules it contains, plus any
-% associated interface definition files, plus the scripts used to
-% control compilation and installation of the executable. However, as a
-% special exception, the source code distributed need not include
-% anything that is normally distributed (in either source or binary
-% form) with the major components (compiler, kernel, and so on) of the
-% operating system on which the executable runs, unless that component
-% itself accompanies the executable.
-%
-% If distribution of executable or object code is made by offering
-% access to copy from a designated place, then offering equivalent
-% access to copy the source code from the same place counts as
-% distribution of the source code, even though third parties are not
-% compelled to copy the source along with the object code.
-%
-% \item
-% You may not copy, modify, sublicense, or distribute the Program
-% except as expressly provided under this License. Any attempt
-% otherwise to copy, modify, sublicense or distribute the Program is
-% void, and will automatically terminate your rights under this License.
-% However, parties who have received copies, or rights, from you under
-% this License will not have their licenses terminated so long as such
-% parties remain in full compliance.
-%
-% \item
-% You are not required to accept this License, since you have not
-% signed it. However, nothing else grants you permission to modify or
-% distribute the Program or its derivative works. These actions are
-% prohibited by law if you do not accept this License. Therefore, by
-% modifying or distributing the Program (or any work based on the
-% Program), you indicate your acceptance of this License to do so, and
-% all its terms and conditions for copying, distributing or modifying
-% the Program or works based on it.
-%
-% \item
-% Each time you redistribute the Program (or any work based on the
-% Program), the recipient automatically receives a license from the
-% original licensor to copy, distribute or modify the Program subject to
-% these terms and conditions. You may not impose any further
-% restrictions on the recipients' exercise of the rights granted herein.
-% You are not responsible for enforcing compliance by third parties to
-% this License.
-%
-% \item
-% If, as a consequence of a court judgment or allegation of patent
-% infringement or for any other reason (not limited to patent issues),
-% conditions are imposed on you (whether by court order, agreement or
-% otherwise) that contradict the conditions of this License, they do not
-% excuse you from the conditions of this License. If you cannot
-% distribute so as to satisfy simultaneously your obligations under this
-% License and any other pertinent obligations, then as a consequence you
-% may not distribute the Program at all. For example, if a patent
-% license would not permit royalty-free redistribution of the Program by
-% all those who receive copies directly or indirectly through you, then
-% the only way you could satisfy both it and this License would be to
-% refrain entirely from distribution of the Program.
-%
-% If any portion of this section is held invalid or unenforceable under
-% any particular circumstance, the balance of the section is intended to
-% apply and the section as a whole is intended to apply in other
-% circumstances.
-%
-% It is not the purpose of this section to induce you to infringe any
-% patents or other property right claims or to contest validity of any
-% such claims; this section has the sole purpose of protecting the
-% integrity of the free software distribution system, which is
-% implemented by public license practices. Many people have made
-% generous contributions to the wide range of software distributed
-% through that system in reliance on consistent application of that
-% system; it is up to the author/donor to decide if he or she is willing
-% to distribute software through any other system and a licensee cannot
-% impose that choice.
-%
-% This section is intended to make thoroughly clear what is believed to
-% be a consequence of the rest of this License.
-%
-% \item
-% If the distribution and/or use of the Program is restricted in
-% certain countries either by patents or by copyrighted interfaces, the
-% original copyright holder who places the Program under this License
-% may add an explicit geographical distribution limitation excluding
-% those countries, so that distribution is permitted only in or among
-% countries not thus excluded. In such case, this License incorporates
-% the limitation as if written in the body of this License.
-%
-% \item
-% The Free Software Foundation may publish revised and/or new versions
-% of the General Public License from time to time. Such new versions will
-% be similar in spirit to the present version, but may differ in detail to
-% address new problems or concerns.
-%
-% Each version is given a distinguishing version number. If the Program
-% specifies a version number of this License which applies to it and ``any
-% later version'', you have the option of following the terms and conditions
-% either of that version or of any later version published by the Free
-% Software Foundation. If the Program does not specify a version number of
-% this License, you may choose any version ever published by the Free Software
-% Foundation.
-%
-% \item
-% If you wish to incorporate parts of the Program into other free
-% programs whose distribution conditions are different, write to the author
-% to ask for permission. For software which is copyrighted by the Free
-% Software Foundation, write to the Free Software Foundation; we sometimes
-% make exceptions for this. Our decision will be guided by the two goals
-% of preserving the free status of all derivatives of our free software and
-% of promoting the sharing and reuse of software generally.
-%
-% \begin{center}
-% {\Large\sc
-% No Warranty
-% }
-% \end{center}
-%
-% \item
-% {\sc Because the program is licensed free of charge, there is no warranty
-% for the program, to the extent permitted by applicable law. Except when
-% otherwise stated in writing the copyright holders and/or other parties
-% provide the program ``as is'' without warranty of any kind, either expressed
-% or implied, including, but not limited to, the implied warranties of
-% merchantability and fitness for a particular purpose. The entire risk as
-% to the quality and performance of the program is with you. Should the
-% program prove defective, you assume the cost of all necessary servicing,
-% repair or correction.}
-%
-% \item
-% {\sc In no event unless required by applicable law or agreed to in writing
-% will any copyright holder, or any other party who may modify and/or
-% redistribute the program as permitted above, be liable to you for damages,
-% including any general, special, incidental or consequential damages arising
-% out of the use or inability to use the program (including but not limited
-% to loss of data or data being rendered inaccurate or losses sustained by
-% you or third parties or a failure of the program to operate with any other
-% programs), even if such holder or other party has been advised of the
-% possibility of such damages.}
-%
-% \end{enumerate}
-%
-%
-% \begin{center}
-% {\Large\sc End of Terms and Conditions}
-% \end{center}
-%
-%
-% \pagebreak[2]
-%
-% \section*{Appendix: How to Apply These Terms to Your New Programs}
-%
-% If you develop a new program, and you want it to be of the greatest
-% possible use to the public, the best way to achieve this is to make it
-% free software which everyone can redistribute and change under these
-% terms.
-%
-% To do so, attach the following notices to the program. It is safest to
-% attach them to the start of each source file to most effectively convey
-% the exclusion of warranty; and each file should have at least the
-% ``copyright'' line and a pointer to where the full notice is found.
-%
-% \begin{quote}
-% one line to give the program's name and a brief idea of what it does. \\
-% Copyright (C) yyyy name of author \\
-%
-% 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
-% 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.
-% \end{quote}
-%
-% Also add information on how to contact you by electronic and paper mail.
-%
-% If the program is interactive, make it output a short notice like this
-% when it starts in an interactive mode:
-%
-% \begin{quote}
-% Gnomovision version 69, Copyright (C) yyyy name of author \\
-% Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\
-% This is free software, and you are welcome to redistribute it
-% under certain conditions; type `show c' for details.
-% \end{quote}
-%
-%
-% The hypothetical commands {\tt show w} and {\tt show c} should show the
-% appropriate parts of the General Public License. Of course, the commands
-% you use may be called something other than {\tt show w} and {\tt show c};
-% they could even be mouse-clicks or menu items---whatever suits your
-% program.
-%
-% You should also get your employer (if you work as a programmer) or your
-% school, if any, to sign a ``copyright disclaimer'' for the program, if
-% necessary. Here is a sample; alter the names:
-%
-% \begin{quote}
-% Yoyodyne, Inc., hereby disclaims all copyright interest in the program \\
-% `Gnomovision' (which makes passes at compilers) written by James Hacker. \\
-%
-% signature of Ty Coon, 1 April 1989 \\
-% Ty Coon, President of Vice
-% \end{quote}
-%
-%
-% This General Public License does not permit incorporating your program
-% into proprietary programs. If your program is a subroutine library, you
-% may consider it more useful to permit linking proprietary applications
-% with the library. If this is what you want to do, use the GNU Library
-% General Public License instead of this License.
-%
-% \end{multicols}
-% \end{minipage}
-% \end{lrbox}
-%
-% \begin{center}
-% \scalebox{0.33}{\usebox{\gpl}}
-% \end{center}
-%
-% \Finale
-\endinput
diff --git a/misc/luaotfload-blacklist.cnf b/misc/luaotfload-blacklist.cnf
new file mode 100644
index 0000000..e82669b
--- /dev/null
+++ b/misc/luaotfload-blacklist.cnf
@@ -0,0 +1,4 @@
+spltfgbd.ttf
+spltfgbi.ttf
+spltfgit.ttf
+spltfgrg.ttf
diff --git a/misc/valgrind-kpse-suppression.sup b/misc/valgrind-kpse-suppression.sup
new file mode 100644
index 0000000..dc32586
--- /dev/null
+++ b/misc/valgrind-kpse-suppression.sup
@@ -0,0 +1,140 @@
+{
+ kpathsea-garbage-1
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:kpathsea_cnf_get
+}
+
+{
+ kpathsea-garbage-2
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:kpse_program_basename
+}
+
+
+{
+ kpathsea-garbage-3
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:kpse_find_file
+}
+
+
+{
+ kpathsea-garbage-4
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:find_file
+}
+
+{
+ kpathsea-garbage-5
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:lua_kpse_lookup
+}
+
+{
+ kpathsea-garbage-6
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:find_file
+}
+
+
+{
+ kpathsea-garbage-7
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:expand_path
+}
+
+{
+ kpathsea-garbage-8
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:do_lua_kpathsea_lookup
+}
+
+
+{
+ kpathsea-garbage-9
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:kpathsea_find_file
+}
+
+
+{
+ kpathsea-garbage-10
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:kpathsea_init_db
+}
+
+
+{
+ kpathsea-garbage-11
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:kpathsea_find_file_generic
+}
+
+
+{
+ kpathsea-garbage-12
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:expand_var
+}
+
+
+{
+ kpathsea-garbage-13
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:init_path
+}
+
+
+{
+ kpathsea-garbage-14
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:kpse_in_name_ok
+}
+
+
+{
+ kpathsea-garbage-15
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:kpathsea_var_value
+}
+
+
+{
+ kpathsea-garbage-16
+ Memcheck:Leak
+ match-leak-kinds: all
+ ...
+ fun:kpse_set_program_name
+}
+
+
diff --git a/mkcharacters b/scripts/mkcharacters
index 5d4a2f4..abed2c9 100755
--- a/mkcharacters
+++ b/scripts/mkcharacters
@@ -5,8 +5,8 @@
-- DESCRIPTION: import parts of char-def.lua
-- REQUIREMENTS: lua, ConTeXt, the lualibs package
-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
--- VERSION: 2.4
--- CREATED: 2013-05-17 12:41:39+0200
+-- VERSION: 2.5
+-- MODIFIED: 2014-02-11 07:24:25+0100
-----------------------------------------------------------------------
-- we create a stripped-down version of char-def.lua
-----------------------------------------------------------------------
@@ -14,7 +14,7 @@
-----------------------------------------------------------------------
-- config
-----------------------------------------------------------------------
-local charfile = "./luaotfload-characters.lua"
+local charfile = "./build/luaotfload-characters.lua"
local chardef = "/home/phg/base/char-def.lua"
--- for every code point char-def.lua provides a set of fields. they
diff --git a/mkglyphlist b/scripts/mkglyphlist
index f7a1cb9..8fde098 100755
--- a/mkglyphlist
+++ b/scripts/mkglyphlist
@@ -5,19 +5,21 @@
-- DESCRIPTION: part of the luaotfload package
-- REQUIREMENTS: lua, lpeg, luasocket, the lualibs package
-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
--- VERSION: 2.4
--- CREATED: 04/23/2013 12:42:17 PM CEST
+-- VERSION: 2.5
+-- MODIFIED: 2014-02-11 06:44:50+0100
-----------------------------------------------------------------------
-- interesting thread on the Context list:
-- http://www.ntg.nl/pipermail/ntg-context/2008/029057.html
+--
+-- N.B. this script assumes network connectivity!
-----------------------------------------------------------------------
-----------------------------------------------------------------------
-- config
-----------------------------------------------------------------------
-local glyphfile = "./glyphlist.txt"
-local font_age = "./luaotfload-glyphlist.lua"
+local glyphfile = "./build/glyphlist.txt"
+local font_age = "./build/luaotfload-glyphlist.lua"
local glyph_source = "http://partners.adobe.com/public/developer/en/opentype/glyphlist.txt"
-----------------------------------------------------------------------
@@ -133,6 +135,7 @@ end
local get_raw get_raw = function (retry)
local fh = io.open(glyphfile, "rb")
if fh then
+ print ("info: reading glyph list from", glyphfile)
local data = fh:read"*all"
fh:close()
if data then return data end
@@ -143,7 +146,7 @@ local get_raw get_raw = function (retry)
if glyphdata then
local fh = io.open(glyphfile, "wb")
if not fh then
- print"error: glyph file not writable"
+ print (string.format ("error: glyph file (%s) not writable", glyphfile))
os.exit(-1)
end
fh:write(glyphdata)
diff --git a/mkstatus b/scripts/mkstatus
index 9940970..15cb97c 100755
--- a/mkstatus
+++ b/scripts/mkstatus
@@ -24,39 +24,44 @@ local md5sumhexa = md5.sumhexa
local ioloaddata = io.loaddata
local iosavedata = io.savedata
local iopopen = io.popen
+local iowrite = io.write
+local lfsisdir = lfs.isdir
-----------------------------------------------------------------------
-- settings
-----------------------------------------------------------------------
-local filelist = "luaotfload-status.lua" --- result
+local verbose = false
+local filelist = "./build/luaotfload-status.lua" --- result
local names = {
--- only the runtime files and scripts
- "luaotfload-auxiliary.lua",
- "luaotfload-basics-gen.lua",
- "luaotfload-basics-nod.lua",
- "luaotfload-characters.lua",
- "luaotfload-colors.lua",
- "luaotfload-database.lua",
- "luaotfload-diagnostics.lua",
- "luaotfload-features.lua",
- "luaotfload-fonts-cbk.lua",
- "luaotfload-fonts-def.lua",
- "luaotfload-fonts-enc.lua",
- "luaotfload-fonts-ext.lua",
- "luaotfload-fonts-lua.lua",
- "luaotfload-fonts-tfm.lua",
- "luaotfload-glyphlist.lua",
- "luaotfload-letterspace.lua",
- "luaotfload-loaders.lua",
- "luaotfload.lua",
- "luaotfload-fontloader.lua",
- "luaotfload-override.lua",
- "luaotfload-tool.lua",
- "mkcharacters",
- "mkglyphlist",
- "mkstatus",
+ { "src", "luaotfload-auxiliary.lua", },
+ { "src", "luaotfload-basics-gen.lua", },
+ { "src", "luaotfload-basics-nod.lua", },
+ { "build", "luaotfload-characters.lua", },
+ { "src", "luaotfload-colors.lua", },
+ { "src", "luaotfload-database.lua", },
+ { "src", "luaotfload-diagnostics.lua", },
+ { "src", "luaotfload-features.lua", },
+ { "src", "luaotfload-fonts-cbk.lua", },
+ { "src", "luaotfload-fonts-def.lua", },
+ { "src", "luaotfload-fonts-enc.lua", },
+ { "src", "luaotfload-fonts-ext.lua", },
+ { "src", "luaotfload-fonts-lua.lua", },
+ { "src", "luaotfload-fonts-tfm.lua", },
+ { "build", "luaotfload-glyphlist.lua", },
+ { "src", "luaotfload-letterspace.lua", },
+ { "src", "luaotfload-loaders.lua", },
+ { "src", "luaotfload-log.lua", },
+ { "src", "luaotfload-main.lua", },
+ { "src", "luaotfload-fontloader.lua", },
+ { "src", "luaotfload-override.lua", },
+ { "src", "luaotfload-parsers.lua", },
+ { "src", "luaotfload-tool.lua", },
+ { "scripts", "mkcharacters", },
+ { "scripts", "mkglyphlist", },
+ { "scripts", "mkstatus", },
}
-----------------------------------------------------------------------
@@ -120,17 +125,38 @@ hash_all = function (list, acc)
return hash_all (table.fastcopy (names), { })
end
- local fname = list[#list]
+ local finfo = list[#list]
list[#list] = nil
- if fname then
- local sum = hash_file (fname)
- acc[#acc+1] = { fname, sum }
+ if finfo then
+ local fpath
+ if type (finfo) == "table" then
+ local d, f = finfo [1], finfo [2]
+ if lfs.isdir (d) then
+ fpath = file.join (d, f)
+ else
+ fpath = f
+ end
+ else
+ fpath = finfo
+ end
+ if verbose then
+ iowrite "· md5("
+ iowrite (fpath)
+ end
+ local sum = hash_file (fpath)
+ if verbose then
+ iowrite ") = \""
+ iowrite (sum)
+ iowrite "\"\n"
+ end
+ acc[#acc+1] = { fpath, sum }
return hash_all (list, acc)
end
return acc
end
local main = function ()
+ if arg [1] == "-v" then verbose = true end
local hashes = hash_all ()
local notes = git_info ()
local serialized = table.serialize ({ notes = notes,
diff --git a/mktests b/scripts/mktests
index baa710c..b36b6dd 100755
--- a/mktests
+++ b/scripts/mktests
@@ -6,7 +6,7 @@
-- REQUIREMENTS: Luatex > 0.76, Luaotfload
-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
-- VERSION: 2.4
--- MODIFIED: 2013-08-26 09:31:22+0200
+-- MODIFIED: 2014-05-15 22:16:47+0200
-----------------------------------------------------------------------
--
--===================================================================--
@@ -16,21 +16,17 @@
--===================================================================--
-local tests = { }
-
-config = { luaotfload = {
- names_dir = "names",
- cache_dir = "fonts",
- index_file = "luaotfload-names.lua",
- resolver = "normal",
- update_live = true, --- suppress db updates
-}}
+local tests = { }
+local lpeg = require "lpeg"
+local lpegmatch = lpeg.match
kpse.set_program_name "luatex"
require "lualibs"
require "luaotfload-basics-gen.lua"
-require "luaotfload-override.lua"
+require "luaotfload-log.lua"
+require "luaotfload-parsers"
+require "luaotfload-configuration"
require "luaotfload-database"
local names = fonts.names
@@ -65,10 +61,64 @@ local pprint_spec = function (spec)
end
-----------------------------------------------------------------------
---- tool tests
+--- parser tests
-----------------------------------------------------------------------
+local test_config_input = [==[
+
+
+[foo]
+bar = baz
+xyzzy = no
+buzz
+
+[lavernica "brutalitops"]
+# It’s a locomotive that runs on us.
+ laan-ev = zip zop zooey ; jib-jab
+Crouton = "Fibrosis \"\\ # "
+]==]
+
+local test_config_output = {
+ { section = { title = "foo" },
+ variables = { bar = "baz",
+ xyzzy = false,
+ buzz = true } },
+ { section = { title = "lavernica",
+ subtitle = "brutalitops" },
+ variables = { ["laan-ev"] = "zip zop zooey",
+ crouton = "Fibrosis \"\\ # " } }
+}
+
+local parse_config = function ()
+ local parser = luaotfload.parsers.config
+ local result = lpegmatch (parser, test_config_input)
+ --- compare values recursively
+ local aux aux = function (t1, t2)
+ --- cheaply non-tail recursive
+ local k1 = table.keys (t1)
+ local k2 = table.keys (t2)
+ if #k1 ~= #k2 then
+ return false
+ end
+ for i = 1, #k1 do
+ local k = k1[i]
+ local v1 = t1[k]
+ local v2 = t2[k]
+ if type (v1) == "table" then
+ if type (v2) ~= "table" or not aux (v1, v2) then
+ return false
+ end
+ elseif v1 ~= v2 then
+ return false
+ end
+ end
+ return true
+ end
+ return aux (result, test_config_output) and 0 or 1, 1
+end
+
+tests["parse_config"] = parse_config
-----------------------------------------------------------------------
--- font tests
@@ -86,11 +136,11 @@ local infer_regular_style = {
{ "Garamond Premier Pro", "GaramondPremrPro.otf" },
{ "CMU Serif", "cmunrm.otf" },
{ "CMU Sans Serif", "cmunss.otf" },
- { "Minion Pro", "MinionPro_Regular.otf" },
+ { "Minion Pro", "MinionPro-Regular.otf" },
--- Below test will succeed only if we match for the
--- splainname (= sanitized tfmdata.fullname) field
--- explicitly.
- { "Minion Pro Italic", "MinionPro_It.otf" },
+ { "Minion Pro Italic", "MinionPro-It.otf" },
}
local choose_optical_size = {
@@ -140,10 +190,10 @@ local choose_style = {
Also, the full Minion Pro set comes with different optical sizes which
for monetary reasons cannot considered here.
--]]--
- { { name = "Minion Pro", style = "regular" }, "MinionPro_Regular.otf" },
- { { name = "Minion Pro", style = "italic" }, "MinionPro_It.otf" },
- { { name = "Minion Pro", style = "bold" }, "MinionPro_Bold.otf" },
- { { name = "Minion Pro", style = "bolditalic" }, "MinionPro_BoldIt.otf" },
+ { { name = "Minion Pro", style = "regular" }, "MinionPro-Regular.otf" },
+ { { name = "Minion Pro", style = "italic" }, "MinionPro-It.otf" },
+ { { name = "Minion Pro", style = "bold" }, "MinionPro-Bold.otf" },
+ { { name = "Minion Pro", style = "bolditalic" }, "MinionPro-BoldIt.otf" },
}
--- this needs a database built with --formats=+pfa,pfb,afm
@@ -227,12 +277,14 @@ end
tests ["resolve_font_name"] = resolve_font_name
+
-----------------------------------------------------------------------
--- runner
-----------------------------------------------------------------------
local main = function ()
local failed, total = 0, 0
+ config.actions.apply_defaults ()
for name, test in next, tests do
texio.write_nl ("[" .. name .. "]")
local newfailed, newtotal = test ()
diff --git a/luaotfload-auxiliary.lua b/src/luaotfload-auxiliary.lua
index d3de731..89bf51b 100644
--- a/luaotfload-auxiliary.lua
+++ b/src/luaotfload-auxiliary.lua
@@ -2,10 +2,10 @@
-----------------------------------------------------------------------
-- FILE: luaotfload-auxiliary.lua
-- DESCRIPTION: part of luaotfload
--- REQUIREMENTS: luaotfload 2.4
+-- REQUIREMENTS: luaotfload 2.5
-- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang
--- VERSION: 2.4
--- CREATED: 2013-05-01 14:40:50+0200
+-- VERSION: 2.5
+-- MODIFIED: 2014-01-02 21:24:25+0100
-----------------------------------------------------------------------
--
@@ -15,28 +15,26 @@
luaotfload = luaotfload or {}
luaotfload.aux = luaotfload.aux or { }
-config = config or { }
-config.luaotfload = config.luaotfload or { }
-
-local aux = luaotfload.aux
-local log = luaotfload.log
-local warning = luaotfload.log
-local fonthashes = fonts.hashes
-local identifiers = fonthashes.identifiers
-
-local fontid = font.id
-local texsprint = tex.sprint
-
-local dofile = dofile
-local getmetatable = getmetatable
-local setmetatable = setmetatable
-local utf8 = unicode.utf8
-local stringlower = string.lower
-local stringformat = string.format
-local stringgsub = string.gsub
-local stringbyte = string.byte
-local stringfind = string.find
-local tablecopy = table.copy
+local aux = luaotfload.aux
+local log = luaotfload.log
+local report = log.report
+local fonthashes = fonts.hashes
+local identifiers = fonthashes.identifiers
+local fontnames = fonts.names
+
+local fontid = font.id
+local texsprint = tex.sprint
+
+local dofile = dofile
+local getmetatable = getmetatable
+local setmetatable = setmetatable
+local utf8 = unicode.utf8
+local stringlower = string.lower
+local stringformat = string.format
+local stringgsub = string.gsub
+local stringbyte = string.byte
+local stringfind = string.find
+local tablecopy = table.copy
-----------------------------------------------------------------------
--- font patches
@@ -57,8 +55,8 @@ local start_rewrite_fontname = function ()
rewrite_fontname,
"luaotfload.rewrite_fontname")
rewriting = true
- logs.names_report ("log", 0, "aux",
- "start rewriting tfmdata.name field")
+ report ("log", 1, "aux",
+ "start rewriting tfmdata.name field")
end
end
@@ -69,97 +67,13 @@ local stop_rewrite_fontname = function ()
luatexbase.remove_fromt_callback
("luaotfload.patch_font", "luaotfload.rewrite_fontname")
rewriting = false
- logs.names_report ("log", 0, "aux",
- "stop rewriting tfmdata.name field")
+ report ("log", 1, "aux",
+ "stop rewriting tfmdata.name field")
end
end
aux.stop_rewrite_fontname = stop_rewrite_fontname
---- as of 2.3 the compatibility hacks for TL 2013 are made optional
-
-if config.luaotfload.compatibility == true then
-
---[[doc--
-
-The font object (tfmdata) structure has changed since version 1.x, so
-in case other packages haven’t been updated we put fallbacks in place
-where they’d expect them. Specifically we have in mind:
-
- · fontspec
- · unicode-math
- · microtype (most likely fixed till TL2013)
-
---doc]]--
-
---- fontobj -> fontobj
-local add_fontdata_fallbacks = function (fontdata)
- if type(fontdata) == "table" then
- local fontparameters = fontdata.parameters
- local metadata
- if not fontdata.shared then --- that would be a tfm
- --- we can’t really catch everything that
- --- goes wrong; for some reason, fontspec.lua
- --- just assumes it always gets an otf object,
- --- so its capheight callback, which does not
- --- bother to do any checks, will access
- --- fontdata.shared no matter what ...
- fontdata.units = fontdata.units_per_em
-
- else --- otf
- metadata = fontdata.shared.rawdata.metadata
- fontdata.name = metadata.origname or fontdata.name
- fontdata.units = fontdata.units_per_em
- fontdata.size = fontdata.size or fontparameters.size
- local resources = fontdata.resources
- --- for legacy fontspec.lua and unicode-math.lua
- fontdata.shared.otfdata = {
- pfminfo = { os2_capheight = metadata.pfminfo.os2_capheight },
- metadata = { ascent = metadata.ascent },
- }
- --- for microtype and fontspec
- --local fake_features = { }
- local fake_features = table.copy(resources.features)
- setmetatable(fake_features, { __index = function (tab, idx)
- warning("some package (probably fontspec) is outdated")
- warning(
- "attempt to index " ..
- "tfmdata.shared.otfdata.luatex.features (%s)",
- idx)
- --os.exit(1)
- return tab[idx]
- end,
- })
- fontdata.shared.otfdata.luatex = {
- unicodes = resources.unicodes,
- features = fake_features,
- }
- end
- end
- return fontdata
-end
-
-luatexbase.add_to_callback(
- "luaotfload.patch_font",
- add_fontdata_fallbacks,
- "luaotfload.fontdata_fallbacks")
-
---[[doc--
-
-Additionally, the font registry is expected at fonts.identifiers
-(fontspec) or fonts.ids (microtype), but in the meantime it has been
-migrated to fonts.hashes.identifiers. We’ll make luaotfload satisfy
-those assumptions. (Maybe it’d be more appropriate to use
-font.getfont() since Hans made it a harmless wrapper [1].)
-
-[1] http://www.ntg.nl/pipermail/ntg-context/2013/072166.html
-
---doc]]--
-
-fonts.identifiers = fonts.hashes.identifiers
-fonts.ids = fonts.hashes.identifiers
-
-end
--[[doc--
This sets two dimensions apparently relied upon by the unicode-math
@@ -453,7 +367,7 @@ do
local load_chardef = function ()
- log ("Loading character metadata from %s.", chardef)
+ report ("both", 1, "aux", "Loading character metadata from %s.", chardef)
chardata = dofile (kpse.find_file (chardef, "lua"))
if chardata == nil then
@@ -511,19 +425,19 @@ local provides_script = function (font_id, asked_script)
--- where method: "gpos" | "gsub"
for feature, data in next, featuredata do
if data[asked_script] then
- log(stringformat(
- "font no %d (%s) defines feature %s for script %s",
- font_id, fontname, feature, asked_script))
+ report ("log", 1, "aux",
+ "font no %d (%s) defines feature %s for script %s",
+ font_id, fontname, feature, asked_script)
return true
end
end
end
- log(stringformat(
- "font no %d (%s) defines no feature for script %s",
- font_id, fontname, asked_script))
+ report ("log", 0, "aux",
+ "font no %d (%s) defines no feature for script %s",
+ font_id, fontname, asked_script)
end
end
- log(stringformat("no font with id %d", font_id))
+ report ("log", 0, "aux", "no font with id %d", font_id)
return false
end
@@ -550,20 +464,22 @@ local provides_language = function (font_id, asked_script, asked_language)
for feature, data in next, featuredata do
local scriptdata = data[asked_script]
if scriptdata and scriptdata[asked_language] then
- log(stringformat("font no %d (%s) defines feature %s "
- .. "for script %s with language %s",
- font_id, fontname, feature,
- asked_script, asked_language))
+ report ("log", 1, "aux",
+ "font no %d (%s) defines feature %s "
+ .. "for script %s with language %s",
+ font_id, fontname, feature,
+ asked_script, asked_language)
return true
end
end
end
- log(stringformat(
- "font no %d (%s) defines no feature for script %s with language %s",
- font_id, fontname, asked_script, asked_language))
+ report ("log", 0, "aux",
+ "font no %d (%s) defines no feature "
+ .. "for script %s with language %s",
+ font_id, fontname, asked_script, asked_language)
end
end
- log(stringformat("no font with id %d", font_id))
+ report ("log", 0, "aux", "no font with id %d", font_id)
return false
end
@@ -621,20 +537,21 @@ local provides_feature = function (font_id, asked_script,
if feature then
local scriptdata = feature[asked_script]
if scriptdata and scriptdata[asked_language] then
- log(stringformat("font no %d (%s) defines feature %s "
- .. "for script %s with language %s",
- font_id, fontname, asked_feature,
- asked_script, asked_language))
+ report ("log", 1, "aux",
+ "font no %d (%s) defines feature %s "
+ .. "for script %s with language %s",
+ font_id, fontname, asked_feature,
+ asked_script, asked_language)
return true
end
end
end
- log(stringformat(
- "font no %d (%s) does not define feature %s for script %s with language %s",
- font_id, fontname, asked_feature, asked_script, asked_language))
+ report ("log", 0, "aux",
+ "font no %d (%s) does not define feature %s for script %s with language %s",
+ font_id, fontname, asked_feature, asked_script, asked_language)
end
end
- log(stringformat("no font with id %d", font_id))
+ report ("log", 0, "aux", "no font with id %d", font_id)
return false
end
@@ -674,8 +591,8 @@ aux.sprint_math_dimension = sprint_math_dimension
--- extra database functions
-----------------------------------------------------------------------
-local namesresolve = fonts.names.resolve
-local namesscan_dir = fonts.names.scan_dir
+local namesresolve = fontnames.resolve
+local namesscan_dir = fontnames.scan_dir
--[====[-- TODO -> port this to new db model
@@ -747,6 +664,42 @@ end
aux.resolve_fontlist = resolve_fontlist
+--- index access ------------------------------------------------------
+
+--- Based on a discussion on the Luatex mailing list:
+--- http://tug.org/pipermail/luatex/2014-June/004881.html
+
+--[[doc--
+
+ aux.read_font_index -- Read the names index from the canonical
+ location and return its contents. This does not affect the behavior
+ of Luaotfload: The returned table is independent of what the font
+ resolvers use internally. Access is raw: each call to the function
+ will result in the entire table being re-read from disk.
+
+--doc]]--
+
+local load_names = fontnames.load
+local access_font_index = fontnames.access_font_index
+
+local read_font_index = function ()
+ return load_names (true) or { }
+end
+
+--[[doc--
+
+ aux.font_index -- Access Luaotfload’s internal database. If the
+ database hasn’t been loaded yet this will cause it to be loaded, with
+ all the possible side-effects like for instance creating the index
+ file if it doesn’t exist, reading all font files, &c.
+
+--doc]]--
+
+local font_index = function () return access_font_index () end
+
+aux.read_font_index = read_font_index
+aux.font_index = font_index
+
--- loaded fonts ------------------------------------------------------
--- just a proof of concept
diff --git a/luaotfload-basics-gen.lua b/src/luaotfload-basics-gen.lua
index 9cf5b93..c19a49a 100644
--- a/luaotfload-basics-gen.lua
+++ b/src/luaotfload-basics-gen.lua
@@ -254,6 +254,18 @@ function caches.loaddata(paths,name)
for i=1,#paths do
local data = false
local luaname, lucname = makefullname(paths[i],name)
+ if lucname and not lfs.isfile(lucname) and type(caches.compile) == "function" then
+ -- in case we used luatex and luajittex mixed ... lub or luc file
+ texio.write(string.format("(compiling luc: %s)",lucname))
+ data = loadfile(luaname)
+ if data then
+ data = data()
+ end
+ if data then
+ caches.compile(data,luaname,lucname)
+ return data
+ end
+ end
if lucname and lfs.isfile(lucname) then -- maybe also check for size
texio.write(string.format("(load luc: %s)",lucname))
data = loadfile(lucname)
@@ -341,3 +353,16 @@ end
function table.setmetatableindex(t,f)
setmetatable(t,{ __index = f })
end
+
+-- helper for plain:
+
+arguments = { }
+
+if arg then
+ for i=1,#arg do
+ local k, v = string.match(arg[i],"^%-%-([^=]+)=?(.-)$")
+ if k and v then
+ arguments[k] = v
+ end
+ end
+end
diff --git a/luaotfload-basics-nod.lua b/src/luaotfload-basics-nod.lua
index 50a1e86..373dab5 100644
--- a/luaotfload-basics-nod.lua
+++ b/src/luaotfload-basics-nod.lua
@@ -54,22 +54,33 @@ nodes.handlers = { }
local nodecodes = { } for k,v in next, node.types () do nodecodes[string.gsub(v,"_","")] = k end
local whatcodes = { } for k,v in next, node.whatsits() do whatcodes[string.gsub(v,"_","")] = k end
local glyphcodes = { [0] = "character", "glyph", "ligature", "ghost", "left", "right" }
+local disccodes = { [0] = "discretionary", "explicit", "automatic", "regular", "first", "second" }
nodes.nodecodes = nodecodes
nodes.whatcodes = whatcodes
nodes.whatsitcodes = whatcodes
nodes.glyphcodes = glyphcodes
+nodes.disccodes = disccodes
local free_node = node.free
local remove_node = node.remove
local new_node = node.new
local traverse_id = node.traverse_id
-local math_code = nodecodes.math
-
nodes.handlers.protectglyphs = node.protect_glyphs
nodes.handlers.unprotectglyphs = node.unprotect_glyphs
+local math_code = nodecodes.math
+local end_of_math = node.end_of_math
+
+function node.end_of_math(n)
+ if n.id == math_code and n.subtype == 1 then
+ return n
+ else
+ return end_of_math(n)
+ end
+end
+
function nodes.remove(head, current, free_too)
local t = current
head, current = remove_node(head,current)
diff --git a/luaotfload-colors.lua b/src/luaotfload-colors.lua
index b8ecb87..9be2974 100644
--- a/luaotfload-colors.lua
+++ b/src/luaotfload-colors.lua
@@ -1,9 +1,9 @@
if not modules then modules = { } end modules ['luaotfload-colors'] = {
- version = "2.4",
- comment = "companion to luaotfload.lua (font color)",
+ version = "2.5",
+ comment = "companion to luaotfload-main.lua (font color)",
author = "Khaled Hosny, Elie Roux, Philipp Gesang",
copyright = "Luaotfload Development Team",
- license = "GNU GPL v2"
+ license = "GNU GPL v2.0"
}
--[[doc--
@@ -19,14 +19,8 @@ explanation: http://tug.org/pipermail/luatex/2013-May/004305.html
--doc]]--
-
-local color_callback = config.luaotfload.color_callback
-if not color_callback then
- --- maybe this would be better as a method: "early" | "late"
- color_callback = "pre_linebreak_filter"
--- color_callback = "pre_output_filter" --- old behavior, breaks expansion
-end
-
+local log = luaotfload.log
+local logreport = log.report
local newnode = node.new
local nodetype = node.id
@@ -34,6 +28,9 @@ local traverse_nodes = node.traverse
local insert_node_before = node.insert_before
local insert_node_after = node.insert_after
+local texset = tex.set
+local texget = tex.get
+
local stringformat = string.format
local stringgsub = string.gsub
local stringfind = string.find
@@ -86,8 +83,9 @@ local sanitize_color_expression = function (digits)
digits = tostring(digits)
local sanitized = lpegmatch(valid_digits, digits)
if not sanitized then
- luaotfload.warning(
- "%q is not a valid rgb[a] color expression", digits)
+ logreport("both", 0, "color",
+ "%q is not a valid rgb[a] color expression",
+ digits)
return nil
end
return sanitized
@@ -276,20 +274,24 @@ local color_handler = function (head)
local new_head = node_colorize(head, nil, nil)
-- now append our page resources
if res then
- res["1"] = true
- local tpr, t = tex.pdfpageresources, ""
+ res["1"] = true
+ local tpr = texget("pdfpageresources")
+ local t = ""
for k in pairs(res) do
local str = stringformat("/TransGs%s<</ca %s/CA %s>>", k, k, k)
if not stringfind(tpr,str) then
t = t .. str
end
end
+ print""
if t ~= "" then
+ print(">>", tpr, "<<")
if not stringfind(tpr,"/ExtGState<<.*>>") then
tpr = tpr.."/ExtGState<<>>"
end
tpr = stringgsub(tpr,"/ExtGState<<","%1"..t)
- tex.pdfpageresources = tpr
+ texset("global", "pdfpageresources", tpr)
+ print(">>", tpr, "<<")
end
res = nil -- reset res
end
@@ -300,6 +302,11 @@ local color_callback_activated = 0
--- unit -> unit
add_color_callback = function ( )
+ local color_callback = config.luaotfload.run.color_callback
+ if not color_callback then
+ color_callback = "pre_linebreak_filter"
+ end
+
if color_callback_activated == 0 then
luatexbase.add_to_callback(color_callback,
color_handler,
diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua
new file mode 100644
index 0000000..dfa222c
--- /dev/null
+++ b/src/luaotfload-configuration.lua
@@ -0,0 +1,704 @@
+#!/usr/bin/env texlua
+-------------------------------------------------------------------------------
+-- FILE: luaotfload-configuration.lua
+-- DESCRIPTION: config file reader
+-- REQUIREMENTS: Luaotfload 2.5 or above
+-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
+-- VERSION: same as Luaotfload
+-- MODIFIED: 2014-07-13 14:19:32+0200
+-------------------------------------------------------------------------------
+--
+
+if not modules then modules = { } end modules ["luaotfload-configuration"] = {
+ version = "2.5",
+ comment = "part of Luaotfload",
+ author = "Philipp Gesang",
+ copyright = "Luaotfload Development Team",
+ license = "GNU GPL v2.0"
+}
+
+luaotfload = luaotfload or { }
+config = config or { }
+config.luaotfload = { }
+
+local status_file = "luaotfload-status"
+local luaotfloadstatus = require (status_file)
+
+local string = string
+local stringsub = string.sub
+local stringexplode = string.explode
+local stringstrip = string.strip
+local stringfind = string.find
+
+local table = table
+local tableappend = table.append
+local tablecopy = table.copy
+local tableconcat = table.concat
+local tabletohash = table.tohash
+
+local math = math
+local mathfloor = math.floor
+
+local io = io
+local ioloaddata = io.loaddata
+local iopopen = io.popen
+
+local os = os
+local osgetenv = os.getenv
+
+local lpeg = require "lpeg"
+local lpegmatch = lpeg.match
+local commasplitter = lpeg.splitat ","
+local equalssplitter = lpeg.splitat "="
+
+local kpse = kpse
+local kpseexpand_path = kpse.expand_path
+local kpselookup = kpse.lookup
+
+local lfs = lfs
+local lfsisfile = lfs.isfile
+local lfsisdir = lfs.isdir
+
+local file = file
+local filejoin = file.join
+local filereplacesuffix = file.replacesuffix
+
+
+local parsers = luaotfload.parsers
+
+local log = luaotfload.log
+local logreport = log.report
+
+local config_parser = parsers.config
+local stripslashes = parsers.stripslashes
+
+local getwritablepath = caches.getwritablepath
+
+-------------------------------------------------------------------------------
+--- SETTINGS
+-------------------------------------------------------------------------------
+
+local path_t = 0
+local kpse_t = 1
+
+local val_home = kpseexpand_path "~"
+local val_xdg_config_home = kpseexpand_path "$XDG_CONFIG_HOME"
+
+if val_xdg_config_home == "" then val_xdg_config_home = "~/.config" end
+
+local config_paths = {
+ --- needs adapting for those other OS
+ { path_t, "./luaotfload.conf" },
+ { path_t, "./luaotfloadrc" },
+ { path_t, filejoin (val_xdg_config_home, "luaotfload/luaotfload.conf") },
+ { path_t, filejoin (val_xdg_config_home, "luaotfload/luaotfloadrc") },
+ { path_t, filejoin (val_home, ".luaotfloadrc") },
+ { kpse_t, "luaotfloadrc" },
+ { kpse_t, "luaotfload.conf" },
+}
+
+local valid_formats = tabletohash {
+ "otf", "ttc", "ttf", "dfont", "afm", "pfb", "pfa",
+}
+
+local feature_presets = {
+ arab = tabletohash {
+ "ccmp", "locl", "isol", "fina", "fin2",
+ "fin3", "medi", "med2", "init", "rlig",
+ "calt", "liga", "cswh", "mset", "curs",
+ "kern", "mark", "mkmk",
+ },
+ deva = tabletohash {
+ "ccmp", "locl", "init", "nukt", "akhn",
+ "rphf", "blwf", "half", "pstf", "vatu",
+ "pres", "blws", "abvs", "psts", "haln",
+ "calt", "blwm", "abvm", "dist", "kern",
+ "mark", "mkmk",
+ },
+ khmr = tabletohash {
+ "ccmp", "locl", "pref", "blwf", "abvf",
+ "pstf", "pres", "blws", "abvs", "psts",
+ "clig", "calt", "blwm", "abvm", "dist",
+ "kern", "mark", "mkmk",
+ },
+ thai = tabletohash {
+ "ccmp", "locl", "liga", "kern", "mark",
+ "mkmk",
+ },
+}
+
+
+
+-------------------------------------------------------------------------------
+--- DEFAULTS
+-------------------------------------------------------------------------------
+
+local default_config = {
+ db = {
+ formats = "otf,ttf,ttc,dfont",
+ scan_local = false,
+ skip_read = false,
+ strip = true,
+ update_live = true,
+ compress = true,
+ max_fonts = 2^51,
+ },
+ run = {
+ resolver = "cached",
+ definer = "patch",
+ log_level = 0,
+ color_callback = "pre_linebreak_filter",
+ },
+ misc = {
+ bisect = false,
+ version = luaotfload.version,
+ statistics = false,
+ termwidth = nil,
+ },
+ paths = {
+ names_dir = "names",
+ cache_dir = "fonts",
+ index_file = "luaotfload-names.lua",
+ lookups_file = "luaotfload-lookup-cache.lua",
+ lookup_path_lua = nil,
+ lookup_path_luc = nil,
+ index_path_lua = nil,
+ index_path_luc = nil,
+ },
+ default_features = {
+ global = { mode = "node" },
+ dflt = tabletohash {
+ "ccmp", "locl", "rlig", "liga", "clig",
+ "kern", "mark", "mkmk", 'itlc',
+ },
+
+ arab = feature_presets.arab,
+ syrc = feature_presets.arab,
+ mong = feature_presets.arab,
+ nko = feature_presets.arab,
+
+ deva = feature_presets.deva,
+ beng = feature_presets.deva,
+ guru = feature_presets.deva,
+ gujr = feature_presets.deva,
+ orya = feature_presets.deva,
+ taml = feature_presets.deva,
+ telu = feature_presets.deva,
+ knda = feature_presets.deva,
+ mlym = feature_presets.deva,
+ sinh = feature_presets.deva,
+
+ khmr = feature_presets.khmr,
+ tibt = feature_presets.khmr,
+ thai = feature_presets.thai,
+ lao = feature_presets.thai,
+
+ hang = tabletohash { "ccmp", "ljmo", "vjmo", "tjmo", },
+ },
+}
+
+-------------------------------------------------------------------------------
+--- RECONFIGURATION TASKS
+-------------------------------------------------------------------------------
+
+--[[doc--
+
+ Procedures to be executed in order to put the new configuration into effect.
+
+--doc]]--
+
+local reconf_tasks = nil
+
+local min_terminal_width = 40
+
+--- The “termwidth” value is only considered when printing
+--- short status messages, e.g. when building the database
+--- online.
+local check_termwidth = function ()
+ if config.luaotfload.misc.termwidth == nil then
+ local tw = 79
+ if not ( os.type == "windows" --- Assume broken terminal.
+ or osgetenv "TERM" == "dumb")
+ then
+ local p = iopopen "tput cols"
+ if p then
+ result = tonumber (p:read "*all")
+ p:close ()
+ if result then
+ tw = result
+ else
+ logreport ("log", 2, "db", "tput returned non-number.")
+ end
+ else
+ logreport ("log", 2, "db", "Shell escape disabled or tput executable missing.")
+ logreport ("log", 2, "db", "Assuming 79 cols terminal width.")
+ end
+ end
+ config.luaotfload.misc.termwidth = tw
+ end
+ return true
+end
+
+local set_font_filter = function ()
+ local names = fonts.names
+ if names and names.set_font_filter then
+ local formats = config.luaotfload.db.formats
+ if not formats or formats == "" then
+ formats = default_config.db.formats
+ end
+ names.set_font_filter (formats)
+ end
+ return true
+end
+
+local set_name_resolver = function ()
+ local names = fonts.names
+ if names and names.resolve_cached then
+ --- replace the resolver from luatex-fonts
+ if config.luaotfload.db.resolver == "cached" then
+ logreport ("both", 2, "cache", "Caching of name: lookups active.")
+ names.resolvespec = names.resolve_cached
+ else
+ names.resolvespec = names.resolve_name
+ end
+ end
+ return true
+end
+
+local set_loglevel = function ()
+ log.set_loglevel (config.luaotfload.run.log_level)
+ return true
+end
+
+local build_cache_paths = function ()
+ local paths = config.luaotfload.paths
+ local prefix = getwritablepath (paths.names_dir, "")
+
+ if not prefix then
+ luaotfload.error ("Impossible to find a suitable writeable cache...")
+ return false
+ end
+
+ prefix = lpegmatch (stripslashes, prefix)
+ logreport ("log", 0, "conf", "Root cache directory is %s.", prefix)
+
+ local index_file = filejoin (prefix, paths.index_file)
+ local lookups_file = filejoin (prefix, paths.lookups_file)
+
+ paths.prefix = prefix
+ paths.index_path_lua = filereplacesuffix (index_file, "lua")
+ paths.index_path_luc = filereplacesuffix (index_file, "luc")
+ paths.lookup_path_lua = filereplacesuffix (lookups_file, "lua")
+ paths.lookup_path_luc = filereplacesuffix (lookups_file, "luc")
+ return true
+end
+
+
+local set_default_features = function ()
+ local default_features = config.luaotfload.default_features
+ luaotfload.features = luaotfload.features or {
+ global = { },
+ defaults = { },
+ }
+ current_features = luaotfload.features
+ for var, val in next, default_features do
+ if var == "global" then
+ current_features.global = val
+ else
+ current_features.defaults[var] = val
+ end
+ end
+ return true
+end
+
+
+reconf_tasks = {
+ { "Set the log level" , set_loglevel },
+ { "Build cache paths" , build_cache_paths },
+ { "Check terminal dimensions" , check_termwidth },
+ { "Set the font filter" , set_font_filter },
+ { "Install font name resolver", set_name_resolver },
+ { "Set default features" , set_default_features },
+}
+
+-------------------------------------------------------------------------------
+--- OPTION SPECIFICATION
+-------------------------------------------------------------------------------
+
+local string_t = "string"
+local table_t = "table"
+local number_t = "number"
+local boolean_t = "boolean"
+local function_t = "function"
+
+local tointeger = function (n)
+ n = tonumber (n)
+ if n then
+ return mathfloor (n + 0.5)
+ end
+end
+
+local toarray = function (s)
+ local fields = { lpegmatch (commasplitter, s) }
+ local ret = { }
+ for i = 1, #fields do
+ local field = stringstrip (fields[i])
+ if field and field ~= "" then
+ ret[#ret + 1] = field
+ end
+ end
+ return ret
+end
+
+local tohash = function (s)
+ local result = { }
+ local fields = toarray (s)
+ for _, field in next, fields do
+ local var, val
+ if stringfind (field, "=") then
+ local tmp
+ var, tmp = lpegmatch (equalssplitter, field)
+ if tmp == "true" or tmp == "yes" then val = true else val = tmp end
+ else
+ var, val = field, true
+ end
+ result[var] = val
+ end
+ return result
+end
+
+local option_spec = {
+ db = {
+ formats = {
+ in_t = string_t,
+ out_t = string_t,
+ transform = function (f)
+ local fields = toarray (f)
+
+ --- check validity
+ if not fields then
+ logreport ("both", 0, "conf",
+ "Expected list of identifiers, got %q.", f)
+ return nil
+ end
+
+ --- strip dupes
+ local known = { }
+ local result = { }
+ for i = 1, #fields do
+ local field = fields[i]
+ if known[field] ~= true then
+ --- yet unknown, tag as seen
+ known[field] = true
+ --- include in output if valid
+ if valid_formats[field] == true then
+ result[#result + 1] = field
+ else
+ logreport ("both", 4, "conf",
+ "Invalid font format identifier %q, ignoring.",
+ field)
+ end
+ end
+ end
+ if #result == 0 then
+ --- force defaults
+ return nil
+ end
+ return tableconcat (result, ",")
+ end
+ },
+ scan_local = { in_t = boolean_t, },
+ skip_read = { in_t = boolean_t, },
+ strip = { in_t = boolean_t, },
+ update_live = { in_t = boolean_t, },
+ compress = { in_t = boolean_t, },
+ max_fonts = {
+ in_t = number_t,
+ out_t = number_t, --- TODO int_t from 5.3.x on
+ transform = tointeger,
+ },
+ },
+ run = {
+ resolver = {
+ in_t = string_t,
+ out_t = string_t,
+ transform = function (r) return r == "normal" and r or "cached" end,
+ },
+ definer = {
+ in_t = string_t,
+ out_t = string_t,
+ transform = function (d) return d == "generic" and d or "patch" end,
+ },
+ log_level = {
+ in_t = number_t,
+ out_t = number_t, --- TODO int_t from 5.3.x on
+ transform = tointeger,
+ },
+ color_callback = {
+ in_t = string_t,
+ out_t = string_t,
+ transform = function (cb)
+ --- These are the two that make sense.
+ return cb == "pre_output_filter" and cb or "pre_linebreak_filter"
+ end,
+ },
+ },
+ misc = {
+ bisect = { in_t = boolean_t, }, --- doesn’t make sense in a config file
+ version = { in_t = string_t, },
+ statistics = { in_t = boolean_t, },
+ termwidth = {
+ in_t = number_t,
+ out_t = number_t,
+ transform = function (w)
+ w = tointeger (w)
+ if w < min_terminal_width then
+ return min_terminal_width
+ end
+ return w
+ end,
+ },
+ },
+ paths = {
+ names_dir = { in_t = string_t, },
+ cache_dir = { in_t = string_t, },
+ index_file = { in_t = string_t, },
+ lookups_file = { in_t = string_t, },
+ lookup_path_lua = { in_t = string_t, },
+ lookup_path_luc = { in_t = string_t, },
+ index_path_lua = { in_t = string_t, },
+ index_path_luc = { in_t = string_t, },
+ },
+ default_features = {
+ __default = { in_t = string_t, out_t = table_t, transform = tohash, },
+ },
+}
+
+-------------------------------------------------------------------------------
+--- MAIN FUNCTIONALITY
+-------------------------------------------------------------------------------
+
+--[[doc--
+
+ tilde_expand -- Rudimentary tilde expansion; covers just the “substitute ‘~’
+ by the current users’s $HOME” part.
+
+--doc]]--
+
+local tilde_expand = function (p)
+ if #p > 2 then
+ if stringsub (p, 1, 2) == "~/" then
+ local homedir = osgetenv "HOME"
+ if homedir and lfsisdir (homedir) then
+ p = filejoin (homedir, stringsub (p, 3))
+ end
+ end
+ end
+ return p
+end
+
+local resolve_config_path = function ()
+ for i = 1, #config_paths do
+ local t, p = unpack (config_paths[i])
+ local fullname
+ if t == kpse_t then
+ fullname = kpse.lookup (p)
+ logreport ("both", 6, "conf", "kpse lookup: %s -> %s.", p, fullname)
+ elseif t == path_t then
+ local expanded = tilde_expand (p)
+ if lfsisfile (expanded) then
+ fullname = expanded
+ end
+ logreport ("both", 6, "conf", "path lookup: %s -> %s.", p, fullname)
+ end
+ if fullname then
+ logreport ("both", 3, "conf", "Reading configuration file at %q.", fullname)
+ return fullname
+ end
+ end
+ logreport ("both", 2, "conf", "No configuration file found.")
+ return false
+end
+
+local add_config_paths = function (t)
+ if not next (t) then
+ return
+ end
+ local result = { }
+ for i = 1, #t do
+ local path = t[i]
+ result[#result + 1] = { path_t, path }
+ end
+ config_paths = tableappend (result, config_paths)
+end
+
+local process_options = function (opts)
+ local new = { }
+ for i = 1, #opts do
+ local section = opts[i]
+ local title = section.section.title
+ local vars = section.variables
+
+ if not title then --- trigger warning: arrow code ahead
+ logreport ("both", 2, "conf", "Section %d lacks a title; skipping.", i)
+ elseif not vars then
+ logreport ("both", 2, "conf", "Section %d (%s) lacks a variable section; skipping.", i, title)
+ else
+ local spec = option_spec[title]
+ if not spec then
+ logreport ("both", 2, "conf", "Section %d (%s) unknown; skipping.", i, title)
+ else
+ local newsection = new[title]
+ if not newsection then
+ newsection = { }
+ new[title] = newsection
+ end
+
+ for var, val in next, vars do
+ local vspec = spec[var] or spec.__default
+ local t_val = type (val)
+ if not vspec then
+ logreport ("both", 2, "conf",
+ "Section %d (%s): invalid configuration variable %q (%q); ignoring.",
+ i, title,
+ var, tostring (val))
+ elseif t_val ~= vspec.in_t then
+ logreport ("both", 2, "conf",
+ "Section %d (%s): type mismatch of input value %q (%q, %s != %s); ignoring.",
+ i, title,
+ var, tostring (val), t_val, vspec.in_t)
+ else --- type matches
+ local transform = vspec.transform
+ if transform then
+ local dval
+ local t_transform = type (transform)
+ if t_transform == function_t then
+ dval = transform (val)
+ elseif t_transform == table_t then
+ dval = transform[val]
+ end
+ if dval then
+ local out_t = vspec.out_t
+ if out_t then
+ local t_dval = type (dval)
+ if t_dval == out_t then
+ newsection[var] = dval
+ else
+ logreport ("both", 2, "conf",
+ "Section %d (%s): type mismatch of derived value of %q (%q, %s != %s); ignoring.",
+ i, title,
+ var, tostring (dval), t_dval, out_t)
+ end
+ else
+ newsection[var] = dval
+ end
+ else
+ logreport ("both", 2, "conf",
+ "Section %d (%s): value of %q could not be derived via %s from input %q; ignoring.",
+ i, title, var, t_transform, tostring (val))
+ end
+ else --- insert as is
+ newsection[var] = val
+ end
+ end
+ end
+ end
+ end
+ end
+ return new
+end
+
+local apply
+apply = function (old, new)
+ if not new then
+ if not old then
+ return false
+ end
+ return tablecopy (old)
+ elseif not old then
+ return tablecopy (new)
+ end
+ local result = tablecopy (old)
+ for name, section in next, new do
+ local t_section = type (section)
+ if t_section ~= table_t then
+ logreport ("both", 1, "conf",
+ "Error applying configuration: entry %s is %s, expected table.",
+ section, t_section)
+ --- ignore
+ else
+ local currentsection = result[name]
+ for var, val in next, section do
+ currentsection[var] = val
+ end
+ end
+ end
+ result.status = luaotfloadstatus
+ return result
+end
+
+local reconfigure = function ()
+ for i = 1, #reconf_tasks do
+ local name, task = unpack (reconf_tasks[i])
+ logreport ("both", 3, "conf", "Launch post-configuration task %q.", name)
+ if not task () then
+ logreport ("both", 0, "conf", "Post-configuration task %q failed.", name)
+ return false
+ end
+ end
+ return true
+end
+
+local read = function (extra)
+ if extra then
+ add_config_paths (extra)
+ end
+
+ local readme = resolve_config_path ()
+ if readme == false then
+ logreport ("both", 2, "conf", "No configuration file.")
+ return false
+ end
+
+ local raw = ioloaddata (readme)
+ if not raw then
+ logreport ("both", 2, "conf", "Error reading the configuration file %q.", readme)
+ return false
+ end
+
+ local parsed = lpegmatch (parsers.config, raw)
+ if not parsed then
+ logreport ("both", 2, "conf", "Error parsing configuration file %q.", readme)
+ return false
+ end
+
+ local ret, msg = process_options (parsed)
+ if not ret then
+ logreport ("both", 2, "conf", "File %q is not a valid configuration file.", readme)
+ logreport ("both", 2, "conf", "Error: %s", msg)
+ return false
+ end
+ return ret
+end
+
+local apply_defaults = function ()
+ local defaults = default_config
+ local vars = read ()
+ --- Side-effects galore ...
+ config.luaotfload = apply (defaults, vars)
+ return reconfigure ()
+end
+
+-------------------------------------------------------------------------------
+--- EXPORTS
+-------------------------------------------------------------------------------
+
+luaotfload.default_config = default_config
+
+config.actions = {
+ read = read,
+ apply = apply,
+ apply_defaults = apply_defaults,
+ reconfigure = reconfigure,
+}
+
diff --git a/luaotfload-database.lua b/src/luaotfload-database.lua
index cb3ec33..7a01ca6 100644
--- a/luaotfload-database.lua
+++ b/src/luaotfload-database.lua
@@ -1,9 +1,9 @@
if not modules then modules = { } end modules ['luaotfload-database'] = {
- version = "2.4",
- comment = "companion to luaotfload.lua",
+ version = "2.5",
+ comment = "companion to luaotfload-main.lua",
author = "Khaled Hosny, Elie Roux, Philipp Gesang",
copyright = "Luaotfload Development Team",
- license = "GNU GPL v2"
+ license = "GNU GPL v2.0"
}
--[[doc--
@@ -36,13 +36,20 @@ if not modules then modules = { } end modules ['luaotfload-database'] = {
--doc]]--
-local lpeg = require "lpeg"
+local lpeg = require "lpeg"
+local P, Cc, lpegmatch = lpeg.P, lpeg.Cc, lpeg.match
-local P, R, S, lpegmatch
- = lpeg.P, lpeg.R, lpeg.S, lpeg.match
+local parsers = luaotfload.parsers
+local read_fonts_conf = parsers.read_fonts_conf
+local stripslashes = parsers.stripslashes
+local splitcomma = parsers.splitcomma
+
+local log = luaotfload.log
+local report = log.report
+local report_status = log.names_status
+local report_status_start = log.names_status_start
+local report_status_stop = log.names_status_stop
-local C, Cc, Cf, Cg, Cs, Ct
- = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cs, lpeg.Ct
--- Luatex builtins
local load = load
@@ -60,7 +67,6 @@ local iolines = io.lines
local ioopen = io.open
local iopopen = io.popen
local kpseexpand_path = kpse.expand_path
-local kpseexpand_var = kpse.expand_var
local kpsefind_file = kpse.find_file
local kpselookup = kpse.lookup
local kpsereadable_file = kpse.readable_file
@@ -70,6 +76,7 @@ local lfscurrentdir = lfs.currentdir
local lfsdir = lfs.dir
local mathabs = math.abs
local mathmin = math.min
+local osgetenv = os.getenv
local osgettimeofday = os.gettimeofday
local osremove = os.remove
local stringfind = string.find
@@ -106,12 +113,12 @@ local stringis_empty = string.is_empty
local stringsplit = string.split
local stringstrip = string.strip
local tableappend = table.append
+local tablecontains = table.contains
local tablecopy = table.copy
local tablefastcopy = table.fastcopy
local tabletofile = table.tofile
local tabletohash = table.tohash
local tableserialize = table.serialize
-local runasscript = caches == nil
--- the font loader namespace is “fonts”, same as in Context
--- we need to put some fallbacks into place for when running
--- as a script
@@ -119,80 +126,19 @@ fonts = fonts or { }
fonts.names = fonts.names or { }
fonts.definers = fonts.definers or { }
-local luaotfloadconfig = config.luaotfload --- always present
-luaotfloadconfig.resolver = luaotfloadconfig.resolver or "normal"
-luaotfloadconfig.formats = luaotfloadconfig.formats or "otf,ttf,ttc,dfont"
-luaotfloadconfig.strip = luaotfloadconfig.strip == true
-
---- this option allows for disabling updates
---- during a TeX run
-luaotfloadconfig.update_live = luaotfloadconfig.update_live ~= false
-luaotfloadconfig.compress = luaotfloadconfig.compress ~= false
-
local names = fonts.names
local name_index = nil --> upvalue for names.data
local lookup_cache = nil --> for names.lookups
-names.version = 2.41
+names.version = 2.51
names.data = nil --- contains the loaded database
names.lookups = nil --- contains the lookup cache
-names.path = { index = { }, lookups = { } }
-names.path.globals = {
- prefix = "", --- writable_path/names_dir
- names_dir = luaotfloadconfig.names_dir or "names",
- index_file = luaotfloadconfig.index_file
- or "luaotfload-names.lua",
- lookups_file = "luaotfload-lookup-cache.lua",
-}
-
--- string -> (string * string)
local make_luanames = function (path)
return filereplacesuffix(path, "lua"),
filereplacesuffix(path, "luc")
end
-local report = logs.names_report
-local report_status = logs.names_status
-local report_status_start = logs.names_status_start
-local report_status_stop = logs.names_status_stop
-
---- The “termwidth” value is only considered when printing
---- short status messages, e.g. when building the database
---- online.
-if not luaotfloadconfig.termwidth then
- local tw = 79
- if not ( os.type == "windows" --- Assume broken terminal.
- or os.getenv "TERM" == "dumb")
- then
- local p = iopopen "tput cols"
- if p then
- result = tonumber (p:read "*all")
- p:close ()
- if result then
- tw = result
- else
- report ("log", 2, "db", "tput returned non-number.")
- end
- else
- report ("log", 2, "db", "Shell escape disabled or tput executable missing.")
- report ("log", 2, "db", "Assuming 79 cols terminal width.")
- end
- end
- luaotfloadconfig.termwidth = tw
-end
-
-names.patterns = { }
-local patterns = names.patterns
-
-local trailingslashes = P"/"^1 * P(-1)
-local stripslashes = C((1 - trailingslashes)^0)
-patterns.stripslashes = stripslashes
-
-local comma = P","
-local noncomma = 1-comma
-local splitcomma = Ct((C(noncomma^1) + comma)^1)
-patterns.splitcomma = splitcomma
-
local format_precedence = {
"otf", "ttc", "ttf",
"dfont", "afm", "pfb",
@@ -208,47 +154,9 @@ local set_location_precedence = function (precedence)
end
--[[doc--
- We use the functions in the cache.* namespace that come with the
- fontloader (see luat-basics-gen). it’s safe to use for the most part
- since most checks and directory creations are already done. It
- uses TEXMFCACHE or TEXMFVAR as starting points.
-
- There is one quirk, though: ``getwritablepath()`` will always
- assume that files in subdirectories of the cache tree are writable.
- It gives no feedback at all if it fails to open a file in write
- mode. This may cause trouble when the index or lookup cache were
- created by different user.
---doc]]--
-
-if not runasscript then
- local globals = names.path.globals
- local names_dir = globals.names_dir
- prefix = getwritablepath (names_dir, "")
- if not prefix then
- luaotfload.error
- ("Impossible to find a suitable writeable cache...")
- else
- prefix = lpegmatch (stripslashes, prefix)
- report ("log", 0, "db",
- "Root cache directory is %s.", prefix)
- end
-
- globals.prefix = prefix
- local lookup_path = names.path.lookups
- local index = names.path.index
- local lookups_file = filejoin (prefix, globals.lookups_file)
- local index_file = filejoin (prefix, globals.index_file)
- lookup_path.lua, lookup_path.luc = make_luanames (lookups_file)
- index.lua, index.luc = make_luanames (index_file)
-else --- running as script, inject some dummies
- caches = { }
- logs = { report = function () end }
-end
+ Auxiliary functions
-
---[[doc--
-Auxiliary functions
--doc]]--
--- fontnames contain all kinds of garbage; as a precaution we
@@ -343,7 +251,6 @@ This is a sketch of the luaotfload db:
status : filestatus;
mappings : fontentry list;
meta : metadata;
- names : namedata; // TODO: check for relevance after db is finalized
}
and familytable = {
local : (format, familyentry) hash; // specified with include dir
@@ -351,21 +258,24 @@ This is a sketch of the luaotfload db:
system : (format, familyentry) hash;
}
and familyentry = {
- regular : sizes;
- italic : sizes;
- bold : sizes;
- bolditalic : sizes;
+ r : sizes; // regular
+ i : sizes; // italic
+ b : sizes; // bold
+ bi : sizes; // bold italic
}
and sizes = {
default : int; // points into mappings or names
optical : (int, int) list; // design size -> index entry
}
and metadata = {
+ created : string // creation time
formats : string list; // { "otf", "ttf", "ttc", "dfont" }
- statistics : TODO;
- version : float;
+ local : bool; (* set if local fonts were added to the db *)
+ modified : string // modification time
+ statistics : TODO; // created when built with "--stats"
+ version : float; // index version
}
- and filemap = {
+ and filemap = { // created by generate_filedata()
base : {
local : (string, int) hash; // basename -> idx
system : (string, int) hash;
@@ -378,36 +288,36 @@ This is a sketch of the luaotfload db:
};
full : (int, string) hash; // idx -> full path
}
- and fontentry = {
- barename : string;
- familyname : string;
- filename : string;
- fontname : string; // <- metadata
- fullname : string; // <- metadata
- sanitized : {
- family : string;
- fontstyle_name : string; // <- new in 2.4
- fontname : string; // <- metadata
- fullname : string; // <- namedata.names
- metafamily : string;
- pfullname : string;
- prefmodifiers : string;
- psname : string;
- subfamily : string;
- };
- size : int list;
- slant : int;
- subfont : int;
- location : local | system | texmf;
- weight : int;
- width : int;
- units_per_em : int; // mainly 1000, but also 2048 or 256
+ and fontentry = { // finalized by collect_families()
+ basename : string; // file name without path "foo.otf"
+ conflicts : { barename : int; basename : int }; // filename conflict with font at index; happens with subfonts
+ familyname : string; // sanitized name of the font family the font belongs to, usually from the names table
+ fontname : string; // sanitized name of the font
+ fontstyle_name : string; // the fontstyle_name field returned by fontloader.info()
+ format : string; // "otf" | "ttf" | "dfont" | "pfa" | "pfb" | "afm"
+ fullname : string; // sanitized full name of the font including style modifiers
+ fullpath : string; // path to font in filesystem
+ index : int; // index in the mappings table
+ italicangle : float; // italic angle; non-zero with oblique faces
+ location : string; // "texmf" | "system" | "local"
+ metafamily : string; // alternative family identifier if appropriate, sanitized
+ plainname : string; // unsanitized font name
+ prefmodifiers : string; // sanitized preferred subfamily (names table 14)
+ psname : string; // PostScript name
+ size : (false | float * float * float); // if available, size info from the size table converted from decipoints
+ splainname : string; // sanitized version of the “plainname” field
+ splitstyle : string; // style information obtained by splitting the full name at the last dash
+ subfamily : string; // sanitized subfamily (names table 2)
+ subfont : (int | bool); // integer if font is part of a TrueType collection ("ttc")
+ version : string; // font version string
+ weight : int; // usWeightClass
}
and filestatus = (string, // fullname
{ index : int list; // pointer into mappings
timestamp : int; }) dict
-beware that this is a reconstruction and may be incomplete.
+beware that this is a reconstruction and may be incomplete or out of
+date. Last update: 2014-04-06, describing version 2.51.
mtx-fonts has in names.tma:
@@ -444,7 +354,10 @@ mtx-fonts has in names.tma:
--doc]]--
-local initialize_namedata = function (formats) --- returns dbobj
+--- string list -> string option -> dbobj
+
+local initialize_namedata = function (formats, created)
+ local now = os.date "%F %T"
return {
--families = { },
status = { }, -- was: status; map abspath -> mapping
@@ -452,7 +365,10 @@ local initialize_namedata = function (formats) --- returns dbobj
names = { },
-- files = { }, -- created later
meta = {
+ created = created or now,
formats = formats,
+ ["local"] = false,
+ modified = now,
statistics = { },
version = names.version,
},
@@ -536,29 +452,32 @@ local load_lua_file = function (path)
end
--- define locals in scope
-local crude_file_lookup
-local crude_file_lookup_verbose
+local access_font_index
+local collect_families
+local font_file_lookup
local find_closest
local flush_lookup_cache
-local ot_fullinfo
-local t1_fullinfo
-local load_names
+local generate_filedata
+local get_font_filter
+local group_modifiers
local load_lookups
+local load_names
+local getmetadata
+local order_design_sizes
+local ot_fullinfo
local read_blacklist
-local read_fonts_conf
local reload_db
-local resolve_name
local resolve_cached
local resolve_fullpath
-local save_names
+local resolve_name
local save_lookups
-local update_names
-local get_font_filter
+local save_names
local set_font_filter
+local t1_fullinfo
+local update_names
--- state of the database
local fonts_reloaded = false
-local fonts_read = 0
--- limit output when approximate font matching (luaotfload-tool -F)
local fuzzy_limit = 1 --- display closest only
@@ -566,15 +485,17 @@ local fuzzy_limit = 1 --- display closest only
--- bool? -> dbobj
load_names = function (dry_run)
local starttime = osgettimeofday ()
- local foundname, data = load_lua_file (names.path.index.lua)
+ local foundname, data = load_lua_file (config.luaotfload.paths.index_path_lua)
if data then
- report ("both", 2, "db",
- "Font names database loaded", "%s", foundname)
+ report ("log", 0, "db",
+ "Font names database loaded from %s", foundname)
+ report ("term", 3, "db",
+ "Font names database loaded from %s", foundname)
report ("info", 3, "db", "Loading took %0.f ms.",
1000 * (osgettimeofday () - starttime))
- local db_version, nms_version
+ local db_version, names_version
if data.meta then
db_version = data.meta.version
else
@@ -583,11 +504,11 @@ load_names = function (dry_run)
--- an earlier index version broke.
db_version = data.version or -42 --- invalid
end
- nms_version = names.version
- if db_version ~= nms_version then
+ names_version = names.version
+ if db_version ~= names_version then
report ("both", 0, "db",
[[Version mismatch; expected %4.3f, got %4.3f.]],
- nms_version, db_version)
+ names_version, db_version)
if not fonts_reloaded then
report ("both", 0, "db", [[Force rebuild.]])
data = update_names ({ }, true, false)
@@ -611,11 +532,29 @@ load_names = function (dry_run)
return data
end
+--[[doc--
+
+ access_font_index -- Provide a reference of the index table. Will
+ cause the index to be loaded if not present.
+
+--doc]]--
+
+access_font_index = function ()
+ if not name_index then name_index = load_names () end
+ return name_index
+end
+
+getmetadata = function ()
+ if not name_index then name_index = load_names() end
+ return tablefastcopy (name_index.meta)
+end
+
--- unit -> unit
load_lookups = function ( )
- local foundname, data = load_lua_file(names.path.lookups.lua)
+ local foundname, data = load_lua_file(config.luaotfload.paths.lookup_path_lua)
if data then
- report("both", 3, "cache",
+ report("log", 0, "cache", "Lookup cache loaded from %s.", foundname)
+ report("term", 3, "cache",
"Lookup cache loaded from %s.", foundname)
else
report("both", 1, "cache",
@@ -650,57 +589,10 @@ local style_category = {
i = "italic",
}
-local type1_formats = { "tfm", "ofm", }
+local type1_metrics = { "tfm", "ofm", }
local dummy_findfile = resolvers.findfile -- from basics-gen
---- filemap -> string -> string -> (string | bool)
-local verbose_lookup = function (data, kind, filename)
- local found = data[kind][filename]
- if found ~= nil then
- found = data.full[found]
- if found == nil then --> texmf
- report("info", 0, "db",
- "Crude file lookup: req=%s; hit=%s => kpse.",
- filename, kind)
- found = dummy_findfile(filename)
- else
- report("info", 0, "db",
- "Crude file lookup: req=%s; hit=%s; ret=%s.",
- filename, kind, found)
- end
- return found
- end
- return false
-end
-
---- string -> (string * string * bool)
-crude_file_lookup_verbose = function (filename)
- if not name_index then name_index = load_names() end
- local mappings = name_index.mappings
- local files = name_index.files
- local found
-
- --- look up in db first ...
- found = verbose_lookup(files, "bare", filename)
- if found then
- return found, nil, true
- end
- found = verbose_lookup(files, "base", filename)
- if found then
- return found, nil, true
- end
-
- --- ofm and tfm, returns pair
- for i=1, #type1_formats do
- local format = type1_formats[i]
- if resolvers.findfile(filename, format) then
- return file.addsuffix(filename, format), format, true
- end
- end
- return filename, nil, false
-end
-
local lookup_filename = function (filename)
if not name_index then name_index = load_names () end
local files = name_index.files
@@ -736,8 +628,18 @@ local lookup_filename = function (filename)
end
end
---- string -> (string * string * bool)
-crude_file_lookup = function (filename)
+--[[doc--
+
+ font_file_lookup -- The ``file:`` are ultimately delegated here.
+ The lookups are kind of a blunt instrument since they try locating
+ the file using every conceivable method, which is quite
+ inefficient. Nevertheless, resolving files that way is rarely the
+ bottleneck.
+
+--doc]]--
+
+--- string -> string * string * bool
+font_file_lookup = function (filename)
local found = lookup_filename (filename)
if not found then
@@ -748,21 +650,37 @@ crude_file_lookup = function (filename)
return found, nil, true
end
- for i=1, #type1_formats do
- local format = type1_formats[i]
+ for i=1, #type1_metrics do
+ local format = type1_metrics[i]
if resolvers.findfile(filename, format) then
return file.addsuffix(filename, format), format, true
end
end
+ if not fonts_reloaded and config.luaotfload.db.update_live == true then
+ return reload_db (stringformat ("File not found: %s.", filename),
+ font_file_lookup,
+ filename)
+ end
return filename, nil, false
end
--[[doc--
-Existence of the resolved file name is verified differently depending
-on whether the index entry has a texmf flag set.
+
+ get_font_file -- Look up the file of an entry in the mappings
+ table. If the index is valid, pass on the name and subfont index
+ after verifing the existence of the resolved file. This
+ verification differs depending the index entry’s ``location``
+ field:
+
+ * ``texmf`` fonts are verified using the (slow) function
+ ``kpse.lookup()``;
+ * other locations are tested by resolving the full path and
+ checking for the presence of a file there.
+
--doc]]--
+--- int -> bool * (string * int) option
local get_font_file = function (index)
local entry = name_index.mappings [index]
if not entry then
@@ -809,29 +727,20 @@ We’ll just store successful name: lookups in a separate cache file.
type lookup_cache = (string, (string * num)) dict
-Complete, needs testing:
- × 1) add cache to dbobj
- × 2) wrap lookups in cached versions
- × 3) make caching optional (via the config table) for debugging
- × 4) make names_update() cache aware (nil if “force”)
- × 5) add logging
- × 6) add cache control to luaotfload-tool
- × 7) incr db version (now 2.203)
- × 8) save cache only at the end of a run
-
-The spec is modified in place (ugh), so we’ll have to catalogue what
-fields actually influence its behavior.
+The spec is expected to be modified in place (ugh), so we’ll have to
+catalogue what fields actually influence its behavior.
Idk what the “spec” resolver is for.
lookup inspects modifies
+ ---------- ----------------- ---------------------------
file: name forced, name
- name:* name, style, sub, resolved, sub, name, forced
+ name:[*] name, style, sub, resolved, sub, name, forced
optsize, size
spec: name, sub resolved, sub, name, forced
-* name: contains both the name resolver from luatex-fonts and
- resolve_name() below
+[*] name: contains both the name resolver from luatex-fonts and
+ resolve_name() below
From my reading of font-def.lua, what a resolver does is
basically rewrite the “name” field of the specification record
@@ -1178,13 +1087,11 @@ resolve_name = function (specification)
name,
style)
end
- if not resolved then
- resolved = specification.name, false
- end
if not resolved then
- if not fonts_reloaded then
- return reload_db ("Font not found.",
+ if not fonts_reloaded and config.luaotfload.db.update_live == true then
+ return reload_db (stringformat ("Font %s not found.",
+ specification.name or "<?>"),
resolve_name,
specification)
end
@@ -1225,7 +1132,7 @@ reload_db = function (why, caller, ...)
local namedata = name_index
local formats = tableconcat (namedata.meta.formats, ",")
- report ("both", 1, "db",
+ report ("both", 0, "db",
"Reload initiated (formats: %s); reason: %q.",
formats, why)
@@ -1277,7 +1184,7 @@ find_closest = function (name, limit)
if not name_index then name_index = load_names () end
if not name_index or type (name_index) ~= "table" then
if not fonts_reloaded then
- return reload_db("no database", find_closest, name)
+ return reload_db("Font index missing.", find_closest, name)
end
return false
end
@@ -1345,10 +1252,18 @@ end --- find_closest()
<http://www.ntg.nl/pipermail/ntg-context/2013/075885.html>
regarding the omission of ``fontloader.close()``.
+ TODO -- check if fontloader.info() is ready for prime in 0.78+
+ -- fields /tables needed:
+ -- names
+ -- postscriptname
+ -- validation_state
+ -- ..
+
--doc]]--
local load_font_file = function (filename, subfont)
local rawfont, _msg = fontloaderopen (filename, subfont)
+ --local rawfont, _msg = fontloaderinfo (filename, subfont)
if not rawfont then
report ("log", 1, "db", "ERROR: failed to open %s.", filename)
return
@@ -1379,42 +1294,87 @@ local get_size_info = function (metadata)
return false
end
-local get_english_names = function (names, basename)
-
+local get_english_names = function (metadata)
+ local names = metadata.names
local english_names
if names then
--inspect(names)
for _, raw_namedata in next, names do
if raw_namedata.lang == "English (US)" then
- english_names = raw_namedata.names
+ return raw_namedata.names
end
end
- else
- -- no names table, probably a broken font
- report("log", 1, "db",
- "Broken font %s rejected due to missing names table.",
+ end
+
+ -- no (English) names table, probably a broken font
+ report("both", 3, "db",
+ "%s: missing or broken English names table.", basename)
+ return { fontname = metadata.fontname,
+ fullname = metadata.fullname, }
+end
+
+--[[--
+ In case of broken PS names we set some dummies. However, we cannot
+ directly modify the font data as returned by fontloader.open() because
+ it is a userdata object.
+
+ For this reason we copy what is necessary whilst keeping the table
+ structure the same as in the tfmdata.
+--]]--
+local get_raw_info = function (metadata, basename)
+ local fullname
+ local fontname = metadata.fontname
+ local fullname = metadata.fullname
+ local psname
+
+ local validation_state = metadata.validation_state
+ if (validation_state and tablecontains (validation_state, "bad_ps_fontname"))
+ or not fontname
+ then
+ --- Broken names table, e.g. avkv.ttf with UTF-16 strings;
+ --- we put some dummies in place like the fontloader
+ --- (font-otf.lua) does.
+ report("both", 3, "db",
+ "%s has invalid postscript font names, using dummies.",
basename)
+ fontname = "bad-fontname-" .. basename
+ fullname = "bad-fullname-" .. basename
end
- return english_names or { }
+ return {
+ familyname = metadata.familyname,
+ fontname = fontname,
+ fontstyle_name = metadata.fontstyle_name,
+ fullname = fullname,
+ italicangle = metadata.italicangle,
+ names = metadata.names,
+ pfminfo = metadata.pfminfo,
+ units_per_em = metadata.units_per_em,
+ version = metadata.version,
+ design_size = metadata.design_size,
+ design_range_top = metadata.design_range_top,
+ design_range_bottom = metadata.design_range_bottom,
+ }
end
-local organize_namedata = function (metadata,
+local organize_namedata = function (rawinfo,
english_names,
basename,
info)
local default_name = english_names.compatfull
or english_names.fullname
or english_names.postscriptname
- or metadata.fullname
- or metadata.fontname
- or info.fullname --- TODO check if fontloader.info() is ready for prime
+ or rawinfo.fullname
+ or rawinfo.fontname
+ or info.fullname
or info.fontname
local default_family = english_names.preffamily
or english_names.family
- or metadata.familyname
+ or rawinfo.familyname
or info.familyname
+-- local default_modifier = english_names.prefmodifiers
+-- or english_names.subfamily
local fontnames = {
--- see
--- https://developer.apple.com/fonts/TTRefMan/RM06/Chap6name.html
@@ -1445,9 +1405,9 @@ local organize_namedata = function (metadata,
},
metadata = {
- fullname = metadata.fullname,
- fontname = metadata.fontname,
- familyname = metadata.familyname,
+ fullname = rawinfo.fullname,
+ fontname = rawinfo.fontname,
+ familyname = rawinfo.familyname,
},
info = {
@@ -1458,14 +1418,14 @@ local organize_namedata = function (metadata,
}
-- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size
- if metadata.fontstyle_name then
+ if rawinfo.fontstyle_name then
--- not present in all fonts, often differs from the preferred
--- subfamily as well as subfamily fields, e.g. with
--- LMSans10-BoldOblique:
--- subfamily: “Bold Italic”
--- prefmodifiers: “10 Bold Oblique”
--- fontstyle_name: “Bold Oblique”
- for _, name in next, metadata.fontstyle_name do
+ for _, name in next, rawinfo.fontstyle_name do
if name.lang == 1033 then --- I hate magic numbers
fontnames.fontstyle_name = name.name
end
@@ -1474,11 +1434,10 @@ local organize_namedata = function (metadata,
return {
sanitized = sanitize_fontnames (fontnames),
- fontname = metadata.fontname,
- fullname = metadata.fullname,
- familyname = metadata.familyname,
+ fontname = rawinfo.fontname,
+ fullname = rawinfo.fullname,
+ familyname = rawinfo.familyname,
}
-
end
@@ -1498,23 +1457,16 @@ local organize_styledata = function (fontname,
metadata,
english_names,
info)
- local pfminfo = metadata.pfminfo
+ local pfminfo = metadata.pfminfo or { }
local names = metadata.names
return {
- -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size
+ --- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size
size = get_size_info (metadata),
- weight = {
- pfminfo.weight, -- integer (multiple of 100?)
- sanitize_fontname (info.weight), -- style name
- },
+ weight = pfminfo.weight or 400,
split = split_fontname (fontname),
width = pfminfo.width,
italicangle = metadata.italicangle,
--- italicangle = {
--- metadata.italicangle, -- float
--- info.italicangle, -- truncated to integer point size?
--- },
--- this is for querying, see www.ntg.nl/maps/40/07.pdf for details
units_per_em = metadata.units_per_em,
version = metadata.version,
@@ -1541,13 +1493,18 @@ ot_fullinfo = function (filename,
return nil
end
- local english_names = get_english_names (metadata.names, basename)
- local namedata = organize_namedata (metadata,
+ local rawinfo = get_raw_info (metadata, basename)
+ --- Closing the file manually is a tad faster and more memory
+ --- efficient than having it closed by the gc
+ fontloaderclose (metadata)
+
+ local english_names = get_english_names (rawinfo)
+ local namedata = organize_namedata (rawinfo,
english_names,
basename,
info)
local style = organize_styledata (namedata.fontname,
- metadata,
+ rawinfo,
english_names,
info)
@@ -1559,11 +1516,8 @@ ot_fullinfo = function (filename,
format = format,
names = namedata,
style = style,
- version = metadata.version,
+ version = rawinfo.version,
}
- --- Closing the file manually is a tad faster and more memory
- --- efficient than having it closed by the gc
- fontloaderclose (metadata)
return res
end
@@ -1626,8 +1580,7 @@ t1_fullinfo = function (filename, _subfont, location, basename, format)
size = false,
splitstyle = splitstyle,
fontstyle_name = style ~= "" and style or weight,
- weight = { metadata.pfminfo.weight,
- weight },
+ weight = metadata.pfminfo.weight or 400,
italicangle = italicangle,
}
end
@@ -1946,13 +1899,16 @@ read_blacklist = function ()
local whitelist = { }
if files and type(files) == "table" then
- for _,v in next, files do
- for line in iolines(v) do
+ for _, path in next, files do
+ for line in iolines (path) do
line = stringstrip(line) -- to get rid of lines like " % foo"
- local first_chr = stringsub(line, 1, 1) --- faster than find
+ local first_chr = stringsub(line, 1, 1)
if first_chr == "%" or stringis_empty(line) then
-- comment or empty line
elseif first_chr == "-" then
+ report ("both", 3, "db",
+ "Whitelisted file %q via %q.",
+ line, path)
whitelist[#whitelist+1] = stringsub(line, 2, -1)
else
local cmt = stringfind(line, "%%")
@@ -1960,7 +1916,9 @@ read_blacklist = function ()
line = stringsub(line, 1, cmt - 1)
end
line = stringstrip(line)
- report("log", 2, "db", "Blacklisted file %q.", line)
+ report ("both", 3, "db",
+ "Blacklisted file %q via %q.",
+ line, path)
blacklist[#blacklist+1] = line
end
end
@@ -2033,9 +1991,6 @@ do
get_font_filter = function (formats)
return tablefastcopy (current_formats)
end
-
- --- initialize
- set_font_filter (luaotfloadconfig.formats)
end
local process_dir_tree
@@ -2138,7 +2093,7 @@ end
--- indicates the number of characters already consumed on the
--- line.
local truncate_string = function (str, restrict)
- local tw = luaotfloadconfig.termwidth
+ local tw = config.luaotfload.misc.termwidth
local wd = tw - restrict
local len = utf8len (str)
if wd - len < 0 then
@@ -2148,73 +2103,44 @@ local truncate_string = function (str, restrict)
return str
end
---[[doc--
- scan_dir() scans a directory and populates the list of fonts
- with all the fonts it finds.
+--[[doc--
- · dirname : name of the directory to scan
- · currentnames : current font db object
- · targetnames : font db object to fill
- · dry_run : don’t touch anything
+ collect_font_filenames_dir -- Traverse the directory root at
+ ``dirname`` looking for font files. Returns a list of {*filename*;
+ *location*} pairs.
--doc]]--
---- string -> dbobj -> dbobj -> bool -> bool -> (int * int)
-
-local scan_dir = function (dirname, currentnames, targetnames,
- dry_run, location)
+--- string -> string -> string * string list
+local collect_font_filenames_dir = function (dirname, location)
if lpegmatch (p_blacklist, dirname) then
report ("both", 4, "db",
"Skipping blacklisted directory %s.", dirname)
--- ignore
- return 0, 0
+ return { }
end
- local found = find_font_files (dirname, location ~= "texmf")
+ local found = find_font_files (dirname, location ~= "texmf" and location ~= "local")
if not found then
report ("both", 4, "db",
"No such directory: %q; skipping.", dirname)
- return 0, 0
+ return { }
end
- report ("both", 4, "db", "Scanning directory %s.", dirname)
- local n_new = 0 --- total of fonts collected
- local n_found = #found
- local max_fonts = luaotfloadconfig.max_fonts
-
- report ("both", 4, "db", "%d font files detected.", n_found)
- for j=1, n_found do
- if max_fonts and fonts_read >= max_fonts then
- break
- end
+ local nfound = #found
+ local files = { }
+ report ("both", 4, "db",
+ "%d font files detected in %s.",
+ nfound, dirname)
+ for j = 1, nfound do
local fullname = found[j]
- fullname = path_normalize(fullname)
- local new
-
- if dry_run == true then
- local truncated = truncate_string (fullname, 43)
- report ("log", 2, "db",
- "Would have been loading %s.", fullname)
- report_status ("term", "db",
- "Would have been loading %s", truncated)
- else
- local truncated = truncate_string (fullname, 32)
- report ("log", 2, "db", "Loading font %s.", fullname)
- report_status ("term", "db", "Loading font %s", truncated)
- local new = read_font_names (fullname, currentnames,
- targetnames, texmf)
- if new == true then
- fonts_read = fonts_read + 1
- n_new = n_new + 1
- end
- end
+ files[#files + 1] = { path_normalize (fullname), location }
end
- report ("both", 4, "db", "Done. %d fonts indexed in %q.",
- n_found, dirname)
- return n_found, n_new
+ return files
end
+
--- string list -> string list
local filter_out_pwd = function (dirs)
local result = { }
@@ -2233,27 +2159,29 @@ end
local path_separator = ostype == "windows" and ";" or ":"
--[[doc--
- scan_texmf_fonts() scans all fonts in the texmf tree through the
- kpathsea variables OPENTYPEFONTS and TTFONTS of texmf.cnf.
+
+ collect_font_filenames_texmf -- Scan texmf tree for font files
+ relying on the kpathsea variables $OPENTYPEFONTS and $TTFONTS of
+ texmf.cnf.
The current working directory comes as “.” (texlive) or absolute
path (miktex) and will always be filtered out.
---doc]]--
---- dbobj -> dbobj -> bool? -> (int * int)
+ Returns a list of { *filename*; *location* } pairs.
+
+--doc]]--
-local scan_texmf_fonts = function (currentnames, targetnames, dry_run)
+--- unit -> string * string list
+local collect_font_filenames_texmf = function ()
- local n_scanned, n_new, fontdirs = 0, 0
local osfontdir = kpseexpand_path "$OSFONTDIR"
if stringis_empty (osfontdir) then
- report ("info", 1, "db", "Scanning TEXMF fonts...")
+ report ("info", 1, "db", "Scanning TEXMF for fonts...")
else
- report ("info", 1, "db", "Scanning TEXMF and OS fonts...")
- if logs.get_loglevel () > 3 then
+ report ("info", 1, "db", "Scanning TEXMF and $OSFONTDIR for fonts...")
+ if log.get_loglevel () > 3 then
local osdirs = filesplitpath (osfontdir)
- report ("info", 0, "db",
- "$OSFONTDIR has %d entries:", #osdirs)
+ report ("info", 0, "db", "$OSFONTDIR has %d entries:", #osdirs)
for i = 1, #osdirs do
report ("info", 0, "db", "[%d] %s", i, osdirs[i])
end
@@ -2264,255 +2192,22 @@ local scan_texmf_fonts = function (currentnames, targetnames, dry_run)
fontdirs = fontdirs .. path_separator .. kpseexpand_path "$TTFONTS"
fontdirs = fontdirs .. path_separator .. kpseexpand_path "$T1FONTS"
- if not stringis_empty (fontdirs) then
- local tasks = filter_out_pwd (filesplitpath (fontdirs))
- report ("info", 3, "db",
- "Initiating scan of %d directories.", #tasks)
- report_status_start (2, 4)
- for _, d in next, tasks do
- local found, new = scan_dir (d, currentnames, targetnames,
- dry_run, "texmf")
- n_scanned = n_scanned + found
- n_new = n_new + new
- end
- report_status_stop ("term", "db", "Scanned %d files, %d new.", n_scanned, n_new)
- end
-
- return n_scanned, n_new
-end
-
---[[
- For the OS fonts, there are several options:
- - if OSFONTDIR is set (which is the case under windows by default but
- not on the other OSs), it scans it at the same time as the texmf tree,
- in the scan_texmf_fonts.
- - if not:
- - under Windows and Mac OSX, we take a look at some hardcoded directories
- - under Unix, we read /etc/fonts/fonts.conf and read the directories in it
-
- This means that if you have fonts in fancy directories, you need to set them
- in OSFONTDIR.
-]]
-
-local read_fonts_conf
-do --- closure for read_fonts_conf()
-
- local alpha = R("az", "AZ")
- local digit = R"09"
- local tag_name = C(alpha^1)
- local whitespace = S" \n\r\t\v"
- local ws = whitespace^1
- local comment = P"<!--" * (1 - P"--")^0 * P"-->"
-
- ---> header specifica
- local xml_declaration = P"<?xml" * (1 - P"?>")^0 * P"?>"
- local xml_doctype = P"<!DOCTYPE" * ws
- * "fontconfig" * (1 - P">")^0 * P">"
- local header = xml_declaration^-1
- * (xml_doctype + comment + ws)^0
-
- ---> enforce root node
- local root_start = P"<" * ws^-1 * P"fontconfig" * ws^-1 * P">"
- local root_stop = P"</" * ws^-1 * P"fontconfig" * ws^-1 * P">"
-
- local dquote, squote = P[["]], P"'"
- local xml_namestartchar = S":_" + alpha --- ascii only, funk the rest
- local xml_namechar = S":._" + alpha + digit
- local xml_name = ws^-1
- * C(xml_namestartchar * xml_namechar^0)
- local xml_attvalue = dquote * C((1 - S[[%&"]])^1) * dquote * ws^-1
- + squote * C((1 - S[[%&']])^1) * squote * ws^-1
- local xml_attr = Cg(xml_name * P"=" * xml_attvalue)
- local xml_attr_list = Cf(Ct"" * xml_attr^1, rawset)
-
- --[[doc--
- scan_node creates a parser for a given xml tag.
- --doc]]--
- --- string -> bool -> lpeg_t
- local scan_node = function (tag)
- --- Node attributes go into a table with the index “attributes”
- --- (relevant for “prefix="xdg"” and the likes).
- local p_tag = P(tag)
- local with_attributes = P"<" * p_tag
- * Cg(xml_attr_list, "attributes")^-1
- * ws^-1
- * P">"
- local plain = P"<" * p_tag * ws^-1 * P">"
- local node_start = plain + with_attributes
- local node_stop = P"</" * p_tag * ws^-1 * P">"
- --- there is no nesting, the earth is flat ...
- local node = node_start
- * Cc(tag) * C(comment + (1 - node_stop)^1)
- * node_stop
- return Ct(node) -- returns {string, string [, attributes = { key = val }] }
- end
-
- --[[doc--
- At the moment, the interesting tags are “dir” for
- directory declarations, and “include” for including
- further configuration files.
-
- spec: http://freedesktop.org/software/fontconfig/fontconfig-user.html
- --doc]]--
- local include_node = scan_node"include"
- local dir_node = scan_node"dir"
-
- local element = dir_node
- + include_node
- + comment --> ignore
- + P(1-root_stop) --> skip byte
-
- local root = root_start * Ct(element^0) * root_stop
- local p_cheapxml = header * root
-
- --lpeg.print(p_cheapxml) ---> 757 rules with v0.10
-
- --[[doc--
- fonts_conf_scanner() handles configuration files.
- It is called on an abolute path to a config file (e.g.
- /home/luser/.config/fontconfig/fonts.conf) and returns a list
- of the nodes it managed to extract from the file.
- --doc]]--
- --- string -> path list
- local fonts_conf_scanner = function (path)
- local fh = ioopen(path, "r")
- if not fh then
- report("both", 3, "db", "Cannot open fontconfig file %s.", path)
- return
- end
- local raw = fh:read"*all"
- fh:close()
-
- local confdata = lpegmatch(p_cheapxml, raw)
- if not confdata then
- report("both", 3, "db", "Cannot scan fontconfig file %s.", path)
- return
- end
- return confdata
- end
-
- local p_conf = P".conf" * P(-1)
- local p_filter = (1 - p_conf)^1 * p_conf
-
- local conf_filter = function (path)
- if lpegmatch (p_filter, path) then
- return true
- end
- return false
+ if stringis_empty (fontdirs) then
+ return { }
end
- --[[doc--
- read_fonts_conf_indeed() is called with six arguments; the
- latter three are tables that represent the state and are
- always returned.
- The first three are
- · the path to the file
- · the expanded $HOME
- · the expanded $XDG_CONFIG_DIR
- --doc]]--
- --- string -> string -> string -> tab -> tab -> (tab * tab * tab)
- local read_fonts_conf_indeed
- read_fonts_conf_indeed = function (start, home, xdg_home,
- acc, done, dirs_done)
-
- local paths = fonts_conf_scanner(start)
- if not paths then --- nothing to do
- return acc, done, dirs_done
- end
-
- for i=1, #paths do
- local pathobj = paths[i]
- local kind, path = pathobj[1], pathobj[2]
- local attributes = pathobj.attributes
- if attributes and attributes.prefix == "xdg" then
- --- this prepends the xdg root (usually ~/.config)
- path = filejoin(xdg_home, path)
- end
-
- if kind == "dir" then
- if stringsub(path, 1, 1) == "~" then
- path = filejoin(home, stringsub(path, 2))
- end
- --- We exclude paths with texmf in them, as they should be
- --- found anyway; also duplicates are ignored by checking
- --- if they are elements of dirs_done.
- ---
- --- FIXME does this mean we cannot access paths from
- --- distributions (e.g. Context minimals) installed
- --- separately?
- if not (stringfind(path, "texmf") or dirs_done[path]) then
- acc[#acc+1] = path
- dirs_done[path] = true
- end
+ local tasks = filter_out_pwd (filesplitpath (fontdirs))
+ report ("info", 3, "db",
+ "Initiating scan of %d directories.", #tasks)
- elseif kind == "include" then
- --- here the path can be four things: a directory or a file,
- --- in absolute or relative path.
- if stringsub(path, 1, 1) == "~" then
- path = filejoin(home, stringsub(path, 2))
- elseif --- if the path is relative, we make it absolute
- not ( lfsisfile(path) or lfsisdir(path) )
- then
- path = filejoin(filedirname(start), path)
- end
- if lfsisfile(path)
- and kpsereadable_file(path)
- and not done[path]
- then
- --- we exclude path with texmf in them, as they should
- --- be found otherwise
- acc = read_fonts_conf_indeed(
- path, home, xdg_home,
- acc, done, dirs_done)
- elseif lfsisdir(path) then --- arrow code ahead
- local config_files = find_files (path, conf_filter)
- for _, filename in next, config_files do
- if not done[filename] then
- acc = read_fonts_conf_indeed(
- filename, home, xdg_home,
- acc, done, dirs_done)
- end
- end
- end --- match “kind”
- end --- iterate paths
- end
-
- --inspect(acc)
- --inspect(done)
- return acc, done, dirs_done
- end --- read_fonts_conf_indeed()
-
- --[[doc--
- read_fonts_conf() sets up an accumulator and two sets
- for tracking what’s been done.
-
- Also, the environment variables HOME and XDG_CONFIG_HOME --
- which are constants anyways -- are expanded so don’t have to
- repeat that over and over again as with the old parser.
- Now they’re just passed on to every call of
- read_fonts_conf_indeed().
-
- read_fonts_conf() is also the only reference visible outside
- the closure.
- --doc]]--
- --- list -> list
- read_fonts_conf = function (path_list)
- local home = kpseexpand_path"~" --- could be os.getenv"HOME"
- local xdg_home = kpseexpand_path"$XDG_CONFIG_HOME"
- if xdg_home == "" then xdg_home = filejoin(home, ".config") end
- local acc = { } ---> list: paths collected
- local done = { } ---> set: files inspected
- local dirs_done = { } ---> set: dirs in list
- for i=1, #path_list do --- we keep the state between files
- acc, done, dirs_done = read_fonts_conf_indeed(
- path_list[i], home, xdg_home,
- acc, done, dirs_done)
- end
- return acc
+ local files = { }
+ for _, dir in next, tasks do
+ files = tableappend (files, collect_font_filenames_dir (dir, "texmf"))
end
-end --- read_fonts_conf closure
+ report ("term", 3, "db", "Collected %d files.", #files)
+ return files
+end
---- TODO stuff those paths into some writable table
--- unit -> string list
local function get_os_dirs ()
if os.name == 'macosx' then
@@ -2523,14 +2218,14 @@ local function get_os_dirs ()
"/Network/Library/Fonts",
}
elseif os.type == "windows" or os.type == "msdos" then
- local windir = os.getenv("WINDIR")
+ local windir = osgetenv("WINDIR")
return { filejoin(windir, 'Fonts') }
else
local fonts_conves = { --- plural, much?
"/usr/local/etc/fonts/fonts.conf",
"/etc/fonts/fonts.conf",
}
- local os_dirs = read_fonts_conf(fonts_conves)
+ local os_dirs = read_fonts_conf(fonts_conves, find_files)
return os_dirs
end
return {}
@@ -2538,51 +2233,118 @@ end
--[[doc--
- scan_os_fonts() scans the OS fonts through
- - fontconfig for Unix (reads the fonts.conf file[s] and scans the
- directories)
- - a static set of directories for Windows and MacOSX
+ retrieve_namedata -- Scan the list of collected fonts and populate
+ the list of namedata.
+
+ · dirname : name of the directory to scan
+ · currentnames : current font db object
+ · targetnames : font db object to fill
+ · dry_run : don’t touch anything
- **NB**: If $OSFONTDIR is nonempty, as it appears to be by default
- on Windows setups, the system fonts will have already been
- processed while scanning the TEXMF. Thus, this function is
- never called.
+ Returns the number of fonts that were actually added to the index.
--doc]]--
---- dbobj -> dbobj -> bool? -> (int * int)
-local scan_os_fonts = function (currentnames,
- targetnames,
- dry_run)
+--- string * string list -> dbobj -> dbobj -> bool? -> int
+local retrieve_namedata = function (files, currentnames, targetnames, dry_run)
+
+ local nfiles = #files
+ local nnew = 0
+
+ report ("info", 1, "db", "Scanning %d collected font files ...", nfiles)
+
+ local bylocation = { texmf = { 0, 0 }
+ , ["local"] = { 0, 0 }
+ , system = { 0, 0 }
+ }
+ report_status_start (2, 4)
+ for i = 1, nfiles do
+ local fullname, location = unpack (files[i])
+ local count = bylocation[location]
+ count[1] = count[1] + 1
+ if dry_run == true then
+ local truncated = truncate_string (fullname, 43)
+ report ("log", 2, "db", "Would have been loading %s.", fullname)
+ report_status ("term", "db", "Would have been loading %s", truncated)
+ --- skip the read_font_names part
+ else
+ local truncated = truncate_string (fullname, 32)
+ report ("log", 2, "db", "Loading font %s.", fullname)
+ report_status ("term", "db", "Loading font %s", truncated)
+ local new = read_font_names (fullname, currentnames,
+ targetnames, location)
+ if new == true then
+ nnew = nnew + 1
+ count[2] = count[2] + 1
+ end
+ end
+ end
+ report_status_stop ("term", "db", "Scanned %d files, %d new.", nfiles, nnew)
+ for location, count in next, bylocation do
+ report ("term", 4, "db", " * %s: %d files, %d new",
+ location, count[1], count[2])
+ end
+ return nnew
+end
+
+--- unit -> string * string list
+local collect_font_filenames_system = function ()
local n_scanned, n_new = 0, 0
- report ("info", 1, "db", "Scanning OS fonts...")
+ report ("info", 1, "db", "Scanning system fonts...")
report ("info", 2, "db",
"Searching in static system directories...")
- report_status_start (2, 4)
- for _, d in next, get_os_dirs () do
- local found, new = scan_dir (d, currentnames,
- targetnames, dry_run)
- n_scanned = n_scanned + found
- n_new = n_new + new
+ local files = { }
+ for _, dir in next, get_os_dirs () do
+ tableappend (files, collect_font_filenames_dir (dir, "system"))
end
- report_status_stop ("term", "db", "Scanned %d files, %d new.", n_scanned, n_new)
-
- return n_scanned, n_new
+ report ("term", 3, "db", "Collected %d files.", #files)
+ return files
end
---- unit -> (bool, lookup_cache)
+--- unit -> bool
flush_lookup_cache = function ()
lookup_cache = { }
collectgarbage "collect"
- return true, lookup_cache
+ return true
+end
+
+--[[doc--
+
+ collect_font_filenames_local -- Scan $PWD (during a TeX run)
+ for font files.
+
+ Side effect: This sets the “local” flag in the subtable “meta” to
+ prevent the merged table from being saved to disk.
+
+ TODO the local tree could be cached in $PWD.
+
+--doc]]--
+
+--- unit -> string * string list
+local collect_font_filenames_local = function ()
+ local pwd = lfscurrentdir ()
+ report ("both", 1, "db", "Scanning for fonts in $PWD (%q) ...", pwd)
+
+ local files = collect_font_filenames_dir (pwd, "local")
+ local nfiles = #files
+ if nfiles > 0 then
+ targetnames.meta["local"] = true --- prevent saving to disk
+ report ("term", 1, "db", "Found %d files.", pwd)
+ else
+ report ("term", 1, "db",
+ "Couldn’t find a thing here. What a waste.", pwd)
+ end
+ report ("term", 3, "db", "Collected %d files.", #files)
+ return files
end
+--- dbobj -> dbobj -> int * int
--- fontentry list -> filemap
-local generate_filedata = function (mappings)
+generate_filedata = function (mappings)
report ("both", 2, "db", "Creating filename map.")
@@ -2715,11 +2477,16 @@ do
local splitfontname = lpeg.splitat "-"
local choose_exact = function (field)
+ --- only clean matches, without guessing
if italic_synonym [field] then
return "i"
- elseif field == "bold" then
+ end
+
+ if field == "bold" then
return "b"
- elseif field == "bolditalic" or field == "boldoblique" then
+ end
+
+ if field == "bolditalic" or field == "boldoblique" then
return "bi"
end
@@ -2741,19 +2508,30 @@ do
style = choose_exact (subfamily)
end
end
--- if not style and splitstyle then
--- style = choose_exact (splitstyle)
--- end
return style
end
+ pick_fallback_style = function (italicangle, weight)
+ --- more aggressive, but only to determine bold faces
+ if weight > 500 then --- bold spectrum matches
+ if italicangle == 0 then
+ return tostring (weight)
+ else
+ return tostring (weight) .. "i"
+ end
+ end
+ return false
+ end
+
--- we use only exact matches here since there are constructs
--- like “regularitalic” (Cabin, Bodoni Old Fashion)
check_regular = function (fontstyle_name,
prefmodifiers,
subfamily,
- splitstyle)
+ splitstyle,
+ italicangle,
+ weight)
if fontstyle_name then
return regular_synonym [fontstyle_name]
@@ -2763,6 +2541,8 @@ do
return regular_synonym [subfamily]
elseif splitstyle then
return regular_synonym [splitstyle]
+ elseif italicangle == 0 and weight == 400 then
+ return true
end
return nil
@@ -2786,7 +2566,7 @@ local pull_values = function (entry)
--- pull name info ...
entry.psname = english.psname
- entry.fontname = info.fontname
+ entry.fontname = info.fontname or metadata.fontname
entry.fullname = english.fullname or info.fullname
entry.splainname = metadata.fullname
entry.prefmodifiers = english.prefmodifiers
@@ -2806,7 +2586,7 @@ local pull_values = function (entry)
entry.splitstyle = style.split
entry.weight = style.weight
- if luaotfloadconfig.strip == true then
+ if config.luaotfload.db.strip == true then
entry.file = nil
entry.names = nil
entry.style = nil
@@ -2814,7 +2594,7 @@ local pull_values = function (entry)
end
local add_family = function (name, subtable, modifier, entry)
- if not name then
+ if not name then --- probably borked font
return
end
local familytable = subtable [name]
@@ -2823,33 +2603,28 @@ local add_family = function (name, subtable, modifier, entry)
subtable [name] = familytable
end
- --- the style table is treated as an unordered list
- local styletable = familytable [modifier]
- if not styletable then
- styletable = { }
- familytable [modifier] = styletable
- end
+ local size = entry.size
- if not entry.prefmodifiers then --- default size for this style/family combo
- styletable.default = entry.index
- end
+ familytable [#familytable + 1] = {
+ index = entry.index,
+ modifier = modifier,
+ }
+end
- local size = entry.size --- dsnsize * high * low
- if size then
- styletable [#styletable + 1] = {
- size [1],
- size [2],
- size [3],
- entry.index,
- }
- else
- styletable.default = entry.index
+local get_subtable = function (families, entry)
+ local location = entry.location
+ local format = entry.format
+ local subtable = families [location] [format]
+ if not subtable then
+ subtable = { }
+ families [location] [format] = subtable
end
+ return subtable
end
-local collect_families = function (mappings)
+collect_families = function (mappings)
- report ("info", 2, "db", "Analyzing families, sizes, and styles.")
+ report ("info", 2, "db", "Analyzing families.")
local families = {
["local"] = { },
@@ -2865,14 +2640,7 @@ local collect_families = function (mappings)
pull_values (entry)
end
- local location = entry.location
- local format = entry.format
-
- local subtable = families [location] [format]
- if not subtable then
- subtable = { }
- families [location] [format] = subtable
- end
+ local subtable = get_subtable (families, entry)
local familyname = entry.familyname
local metafamily = entry.metafamily
@@ -2893,7 +2661,9 @@ local collect_families = function (mappings)
modifier = check_regular (fontstyle_name,
prefmodifiers,
subfamily,
- splitstyle)
+ splitstyle,
+ italicangle,
+ weight)
end
if modifier then
@@ -2921,6 +2691,11 @@ local collect_families = function (mappings)
-- if metafamily and metafamily ~= familyname then
-- add_family (metafamily, subtable, modifier, entry)
-- end
+ elseif weight > 500 then -- in bold spectrum
+ modifier = pick_fallback_style (italicangle, weight)
+ if modifier then
+ add_family (familyname, subtable, modifier, entry)
+ end
end
end
@@ -2928,11 +2703,121 @@ local collect_families = function (mappings)
return families
end
+--[[doc--
+
+ group_modifiers -- For not-quite-bold faces, determine whether
+ they can fill in for a missing bold face slot in a matching family.
+
+ Some families like Lucida do not contain real bold / bold italic
+ members. Instead, they have semibold variants at weight 600 which
+ we must add in a separate pass.
+
+--doc]]--
+
+local bold_spectrum_low = 501 --- 500 is medium, 900 heavy/black
+local bold_weight = 700
+local style_categories = { "r", "b", "i", "bi" }
+local bold_categories = { "b", "bi" }
+
+group_modifiers = function (mappings, families)
+ report ("info", 2, "db", "Analyzing shapes, weights, and styles.")
+ for location, location_data in next, families do
+ for format, format_data in next, location_data do
+ for familyname, collected in next, format_data do
+ local styledata = { } --- will replace the “collected” table
+ --- First, fill in the ordinary style data that
+ --- fits neatly into the four relevant modifier
+ --- categories.
+ for _, modifier in next, style_categories do
+ local entries
+ for key, info in next, collected do
+ if info.modifier == modifier then
+ if not entries then
+ entries = { }
+ end
+ local index = info.index
+ local entry = mappings [index]
+ local size = entry.size
+ if size then
+ entries [#entries + 1] = {
+ size [1],
+ size [2],
+ size [3],
+ index,
+ }
+ else
+ entries.default = index
+ end
+ collected [key] = nil
+ end
+ styledata [modifier] = entries
+ end
+ end
+
+ --- At this point the family set may still lack
+ --- entries for bold or bold italic. We will fill
+ --- those in using the modifier with the numeric
+ --- weight that is closest to bold (700).
+ if next (collected) then --- there are uncategorized entries
+ for _, modifier in next, bold_categories do
+ if not styledata [modifier] then
+ local closest
+ local minimum = 2^51
+ for key, info in next, collected do
+ local info_modifier = tonumber (info.modifier) and "b" or "bi"
+ if modifier == info_modifier then
+ local index = info.index
+ local entry = mappings [index]
+ local weight = entry.weight
+ local diff = weight < 700 and 700 - weight or weight - 700
+ if diff < minimum then
+ minimum = diff
+ closest = weight
+ end
+ end
+ end
+ if closest then
+ --- We know there is a substitute face for the modifier.
+ --- Now we scan the list again to extract the size data
+ --- in case the shape is available at multiple sizes.
+ local entries = { }
+ for key, info in next, collected do
+ local info_modifier = tonumber (info.modifier) and "b" or "bi"
+ if modifier == info_modifier then
+ local index = info.index
+ local entry = mappings [index]
+ local size = entry.size
+ if entry.weight == closest then
+ if size then
+ entries [#entries + 1] = {
+ size [1],
+ size [2],
+ size [3],
+ index,
+ }
+ else
+ entries.default = index
+ end
+ end
+ end
+ end
+ styledata [modifier] = entries
+ end
+ end
+ end
+ end
+ format_data [familyname] = styledata
+ end
+ end
+ end
+ return families
+end
+
local cmp_sizes = function (a, b)
return a [1] < b [1]
end
-local order_design_sizes = function (families)
+order_design_sizes = function (families)
report ("info", 2, "db", "Ordering design sizes.")
@@ -2949,27 +2834,89 @@ local order_design_sizes = function (families)
return families
end
-local retrieve_namedata = function (currentnames,
- targetnames,
- dry_run,
- n_rawnames,
- n_newnames)
+--[[doc--
- local rawnames, new = scan_texmf_fonts (currentnames,
- targetnames,
- dry_run)
+ collect_font_filenames -- Scan the three search path categories for
+ font files. This constitutes the first pass of the update mode.
+
+--doc]]--
- n_rawnames = n_rawnames + rawnames
- n_newnames = n_newnames + new
+--- unit -> string * bool list
+local collect_font_filenames = function ()
- rawnames, new = scan_os_fonts (currentnames, targetnames, dry_run)
+ report ("info", 4, "db", "Scanning the filesystem for font files.")
- n_rawnames = n_rawnames + rawnames
- n_newnames = n_newnames + new
+ local filenames = { }
+ local bisect = config.luaotfload.misc.bisect
+ local max_fonts = config.luaotfload.db.max_fonts --- XXX revisit for lua 5.3 wrt integers
- return n_rawnames, n_newnames
+ tableappend (filenames, collect_font_filenames_texmf ())
+ tableappend (filenames, collect_font_filenames_system ())
+ if config.luaotfload.db.scan_local == true then
+ tableappend (filenames, collect_font_filenames_local ())
+ end
+ --- Now drop everything above max_fonts.
+ if max_fonts < #filenames then
+ filenames = { unpack (filenames, 1, max_fonts) }
+ end
+ --- And choose the requested slice if in bisect mode.
+ if bisect then
+ return { unpack (filenames, bisect[1], bisect[2]) }
+ end
+ return filenames
end
+--[[doc--
+
+ nth_font_file -- Return the filename of the nth font.
+
+--doc]]--
+
+--- int -> string
+local nth_font_filename = function (n)
+ report ("info", 4, "db", "Picking font file no. %d.", n)
+ if not p_blacklist then
+ read_blacklist ()
+ end
+ local filenames = collect_font_filenames ()
+ return filenames[n] and filenames[n][1] or "<error>"
+end
+
+--[[doc--
+
+ font_slice -- Return the fonts in the range from lo to hi.
+
+--doc]]--
+
+local font_slice = function (lo, hi)
+ report ("info", 4, "db", "Retrieving font files nos. %d--%d.", lo, hi)
+ if not p_blacklist then
+ read_blacklist ()
+ end
+ local filenames = collect_font_filenames ()
+ local result = { }
+ for i = lo, hi do
+ result[#result + 1] = filenames[i][1]
+ end
+ return result
+end
+
+--[[doc
+
+ count_font_files -- Return the number of files found by
+ collect_font_filenames. This function is exported primarily
+ for use with luaotfload-tool.lua in bisect mode.
+
+--doc]]--
+
+--- unit -> int
+local count_font_files = function ()
+ report ("info", 4, "db", "Counting font files.")
+ if not p_blacklist then
+ read_blacklist ()
+ end
+ return #collect_font_filenames ()
+end
--- dbobj -> stats
@@ -3053,7 +3000,7 @@ local collect_statistics = function (mappings)
local n_fullname = setsize (fullname)
local n_family = setsize (family)
- if logs.get_loglevel () > 1 then
+ if log.get_loglevel () > 1 then
local pprint_top = function (hash, n, set)
local freqs = { }
@@ -3142,18 +3089,16 @@ end
--- dbobj? -> bool? -> bool? -> dbobj
update_names = function (currentnames, force, dry_run)
-
local targetnames
- if luaotfloadconfig.update_live == false then
+ if config.luaotfload.db.update_live == false then
report ("info", 2, "db",
"Skipping database update.")
--- skip all db updates
return currentnames or name_index
end
- local starttime = osgettimeofday ()
- local n_rawnames, n_newnames = 0, 0
+ local starttime = osgettimeofday ()
--[[
The main function, scans everything
@@ -3163,10 +3108,7 @@ update_names = function (currentnames, force, dry_run)
report("both", 1, "db", "Updating the font names database"
.. (force and " forcefully." or "."))
- --- pass 1 get raw data: read font files (normal case) or reuse
- --- information present in index
-
- if luaotfloadconfig.skip_read == true then
+ if config.luaotfload.db.skip_read == true then
--- the difference to a “dry run” is that we don’t search
--- for font files entirely. we also ignore the “force”
--- parameter since it concerns only the font files.
@@ -3188,22 +3130,28 @@ update_names = function (currentnames, force, dry_run)
end
end
- targetnames = initialize_namedata (get_font_filter ())
+ targetnames = initialize_namedata (get_font_filter (),
+ currentnames.meta and currentnames.meta.created)
read_blacklist ()
- local n_raw, n_new= retrieve_namedata (currentnames,
- targetnames,
- dry_run,
- n_rawnames,
- n_newnames)
+ --- pass 1: Collect the names of all fonts we are going to process.
+ local font_filenames = collect_font_filenames ()
+
+ --- pass 2: read font files (normal case) or reuse information
+ --- present in index
+
+ n_new = retrieve_namedata (font_filenames,
+ currentnames,
+ targetnames,
+ dry_run)
report ("info", 3, "db",
- "Scanned %d font files; %d new entries.",
- n_rawnames, n_newnames)
+ "Found %d font files; %d new entries.",
+ #font_filenames, n_new)
end
- --- pass 2 (optional): collect some stats about the raw font info
- if luaotfloadconfig.statistics == true then
+ --- pass 3 (optional): collect some stats about the raw font info
+ if config.luaotfload.misc.statistics == true then
targetnames.meta.statistics = collect_statistics
(targetnames.mappings)
end
@@ -3212,13 +3160,17 @@ update_names = function (currentnames, force, dry_run)
--- non-texmf entries are redirected there and the mapping
--- needs to be 100% consistent
- --- pass 3: build filename table
+ --- pass 4: build filename table
targetnames.files = generate_filedata (targetnames.mappings)
- --- pass 4: build family lookup table
- targetnames.families = collect_families (targetnames.mappings)
+ --- pass 5: build family lookup table
+ targetnames.families = collect_families (targetnames.mappings)
+
+ --- pass 6: arrange style and size info
+ targetnames.families = group_modifiers (targetnames.mappings,
+ targetnames.families)
- --- pass 5: order design size tables
+ --- pass 7: order design size tables
targetnames.families = order_design_sizes (targetnames.families)
@@ -3229,25 +3181,29 @@ update_names = function (currentnames, force, dry_run)
if dry_run ~= true then
- save_names ()
-
- local success, _lookups = flush_lookup_cache ()
- if success then
- local success = save_lookups ()
- if success then
- logs.names_report ("info", 2, "cache",
- "Lookup cache emptied.")
- return targetnames
+ if n_new == 0 then
+ report ("info", 2, "db", "No new fonts found, skip saving to disk.")
+ else
+ local success, reason = save_names ()
+ if not success then
+ report ("both", 0, "db",
+ "Failed to save database to disk: %s",
+ reason)
end
end
+
+ if flush_lookup_cache () and save_lookups () then
+ report ("both", 2, "cache", "Lookup cache emptied.")
+ return targetnames
+ end
end
return targetnames
end
--- unit -> bool
save_lookups = function ( )
- local path = names.path.lookups
- local luaname, lucname = path.lua, path.luc
+ local paths = config.luaotfload.paths
+ local luaname, lucname = paths.lookup_path_lua, paths.lookup_path_luc
if fileiswritable (luaname) and fileiswritable (lucname) then
tabletofile (luaname, lookup_cache, true)
osremove (lucname)
@@ -3271,17 +3227,22 @@ save_lookups = function ( )
end
--- save_names() is usually called without the argument
---- dbobj? -> bool
+--- dbobj? -> bool * string option
save_names = function (currentnames)
if not currentnames then
currentnames = name_index
end
- local path = names.path.index
- local luaname, lucname = path.lua, path.luc
+ if not currentnames or type (currentnames) ~= "table" then
+ return false, "invalid names table"
+ elseif currentnames.meta and currentnames.meta["local"] then
+ return false, "table contains local entries"
+ end
+ local paths = config.luaotfload.paths
+ local luaname, lucname = paths.index_path_lua, paths.index_path_luc
if fileiswritable (luaname) and fileiswritable (lucname) then
osremove (lucname)
local gzname = luaname .. ".gz"
- if luaotfloadconfig.compress then
+ if config.luaotfload.db.compress then
local serialized = tableserialize (currentnames, true)
save_gzipped (gzname, serialized)
caches.compile (currentnames, "", lucname)
@@ -3399,7 +3360,7 @@ end
local getwritablecachepath = function ( )
--- fonts.handlers.otf doesn’t exist outside a Luatex run,
--- so we have to improvise
- local writable = getwritablepath (luaotfloadconfig.cache_dir)
+ local writable = getwritablepath (config.luaotfload.paths.cache_dir)
if writable then
return writable
end
@@ -3407,7 +3368,7 @@ end
local getreadablecachepaths = function ( )
local readables = caches.getreadablepaths
- (luaotfloadconfig.cache_dir)
+ (config.luaotfload.paths.cache_dir)
local result = { }
if readables then
for i=1, #readables do
@@ -3424,7 +3385,7 @@ end
local purge_cache = function ( )
local writable_path = getwritablecachepath ()
local luanames, lucnames, rest = collect_cache(writable_path)
- if logs.get_loglevel() > 1 then
+ if log.get_loglevel() > 1 then
print_cache("writable path", writable_path, luanames, lucnames, rest)
end
local success = purge_from_cache("writable path", writable_path, luanames, false)
@@ -3435,7 +3396,7 @@ end
local erase_cache = function ( )
local writable_path = getwritablecachepath ()
local luanames, lucnames, rest, all = collect_cache(writable_path)
- if logs.get_loglevel() > 1 then
+ if log.get_loglevel() > 1 then
print_cache("writable path", writable_path, luanames, lucnames, rest)
end
local success = purge_from_cache("writable path", writable_path, all, true)
@@ -3472,36 +3433,31 @@ end
--- export functionality to the namespace “fonts.names”
-----------------------------------------------------------------------
-names.scan_dir = scan_dir
names.set_font_filter = set_font_filter
names.flush_lookup_cache = flush_lookup_cache
names.save_lookups = save_lookups
names.load = load_names
+names.access_font_index = access_font_index
names.data = function () return name_index end
names.save = save_names
names.update = update_names
-names.crude_file_lookup = crude_file_lookup
-names.crude_file_lookup_verbose = crude_file_lookup_verbose
+names.font_file_lookup = font_file_lookup
names.read_blacklist = read_blacklist
names.sanitize_fontname = sanitize_fontname
names.getfilename = resolve_fullpath
+names.getmetadata = getmetadata
names.set_location_precedence = set_location_precedence
+names.count_font_files = count_font_files
+names.nth_font_filename = nth_font_filename
+names.font_slice = font_slice
+names.resolve_cached = resolve_cached
+names.resolve_name = resolve_name
--- font cache
names.purge_cache = purge_cache
names.erase_cache = erase_cache
names.show_cache = show_cache
---- replace the resolver from luatex-fonts
-if luaotfloadconfig.resolver == "cached" then
- report("both", 2, "cache", "Caching of name: lookups active.")
- names.resolvespec = resolve_cached
- names.resolve_name = resolve_cached
-else
- names.resolvespec = resolve_name
- names.resolve_name = resolve_name
-end
-
names.find_closest = find_closest
-- for testing purpose
diff --git a/luaotfload-diagnostics.lua b/src/luaotfload-diagnostics.lua
index 68ed18c..d9b13f5 100644
--- a/luaotfload-diagnostics.lua
+++ b/src/luaotfload-diagnostics.lua
@@ -4,13 +4,11 @@
-- DESCRIPTION: functionality accessible by the --diagnose option
-- REQUIREMENTS: luaotfload-tool.lua
-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
--- VERSION: 2.4
--- CREATED: 2013-07-28 10:01:18+0200
+-- VERSION: 2.5
+-- MODIFIED: 2014-01-02 21:23:06+0100
-----------------------------------------------------------------------
--
local names = fonts.names
-local luatexstatus = status
-local status = config.luaotfload.status
local kpse = require "kpse"
local kpseexpand_path = kpse.expand_path
@@ -49,10 +47,15 @@ local lpeg = require "lpeg"
local C, Cg, Ct = lpeg.C, lpeg.Cg, lpeg.Ct
local lpegmatch = lpeg.match
+local report = luaotfload.log.report
local out = function (...)
- logs.names_report (false, 0, "diagnose", ...)
+ report (false, 0, "diagnose", ...)
end
+local parsers = luaotfload.parsers
+local stripslashes = parsers.stripslashes
+local splitcomma = parsers.splitcomma
+
local check_index = function (errcnt)
out "================= font names =================="
@@ -94,8 +97,9 @@ local check_index = function (errcnt)
return errcnt
end
-local verify_files = function (errcnt, status)
+local verify_files = function (errcnt)
out "================ verify files ================="
+ local status = config.luaotfload.status
local hashes = status.hashes
local notes = status.notes
if not hashes or #hashes == 0 then
@@ -163,8 +167,6 @@ local analyze_permissions = function (raw)
return lpegmatch (p_permissions, raw)
end
-local stripslashes = names.patterns.stripslashes
-
local get_permissions = function (t, location)
if stringsub (location, #location) == "/" then
--- strip trailing slashes (lfs idiosyncrasy on Win)
@@ -238,19 +240,23 @@ local check_conformance = function (spec, permissions, errcnt)
return errcnt
end
-local path = names.path
-
-local desired_permissions = {
- { "d", {"r","w"}, function () return caches.getwritablepath () end },
- { "d", {"r","w"}, path.globals.prefix },
- { "f", {"r","w"}, path.index.lua .. ".gz" },
- { "f", {"r","w"}, path.index.luc },
- { "f", {"r","w"}, path.lookups.lua },
- { "f", {"r","w"}, path.lookups.luc },
-}
+local desired_permissions
+local init_desired_permissions = function ()
+ inspect(config.luaotfload.paths)
+ local paths = config.luaotfload.paths
+ desired_permissions = {
+ { "d", {"r","w"}, function () return caches.getwritablepath () end },
+ { "d", {"r","w"}, paths.prefix },
+ { "f", {"r","w"}, paths.index_path_lua .. ".gz" },
+ { "f", {"r","w"}, paths.index_path_luc },
+ { "f", {"r","w"}, paths.lookup_path_lua },
+ { "f", {"r","w"}, paths.lookup_path_luc },
+ }
+end
local check_permissions = function (errcnt)
out [[=============== file permissions ==============]]
+ if not desired_permissions then init_desired_permissions () end
for i = 1, #desired_permissions do
local t, spec, path = unpack (desired_permissions[i])
if type (path) == "function" then
@@ -334,8 +340,7 @@ else
end
out ("Requesting <%s>.", request)
- local response, code, headers, status
- = https.request (request)
+ local response, code, headers, status = https.request (request)
if status ~= alright then
out "Request failed!"
return false
@@ -605,8 +610,6 @@ local anamneses = {
"permissions"
}
-local splitcomma = names.patterns.splitcomma
-
local diagnose = function (job)
local errcnt = 0
local asked = job.asked_diagnostics
@@ -628,7 +631,7 @@ local diagnose = function (job)
end
if asked.files == true then
- errcnt = verify_files (errcnt, status)
+ errcnt = verify_files (errcnt)
asked.files = nil
end
diff --git a/luaotfload-features.lua b/src/luaotfload-features.lua
index 4b2f206..1fb6d7c 100644
--- a/luaotfload-features.lua
+++ b/src/luaotfload-features.lua
@@ -1,15 +1,26 @@
if not modules then modules = { } end modules ["features"] = {
- version = "2.4",
- comment = "companion to luaotfload.lua",
+ version = "2.5",
+ comment = "companion to luaotfload-main.lua",
author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
-local type, next = type, next
+local type = type
+local next = next
local tonumber = tonumber
local tostring = tostring
+
+local lpeg = require "lpeg"
local lpegmatch = lpeg.match
+local P = lpeg.P
+local R = lpeg.R
+local C = lpeg.C
+
+local table = table
+local tabletohash = table.tohash
+local setmetatableindex = table.setmetatableindex
+local insert = table.insert
---[[ begin included font-ltx.lua ]]
--- this appears to be based in part on luatex-fonts-def.lua
@@ -43,7 +54,8 @@ function fonts.definers.getspecification(str)
return "", str, "", ":", str
end
-local report = logs.names_report
+local log = luaotfload.log
+local report = log.report
local stringfind = string.find
local stringlower = string.lower
@@ -53,58 +65,6 @@ local stringformat = string.format
local stringis_empty = string.is_empty
local mathceil = math.ceil
-local defaults = {
- dflt = {
- "ccmp", "locl", "rlig", "liga", "clig",
- "kern", "mark", "mkmk", 'itlc',
- },
- arab = {
- "ccmp", "locl", "isol", "fina", "fin2",
- "fin3", "medi", "med2", "init", "rlig",
- "calt", "liga", "cswh", "mset", "curs",
- "kern", "mark", "mkmk",
- },
- deva = {
- "ccmp", "locl", "init", "nukt", "akhn",
- "rphf", "blwf", "half", "pstf", "vatu",
- "pres", "blws", "abvs", "psts", "haln",
- "calt", "blwm", "abvm", "dist", "kern",
- "mark", "mkmk",
- },
- khmr = {
- "ccmp", "locl", "pref", "blwf", "abvf",
- "pstf", "pres", "blws", "abvs", "psts",
- "clig", "calt", "blwm", "abvm", "dist",
- "kern", "mark", "mkmk",
- },
- thai = {
- "ccmp", "locl", "liga", "kern", "mark",
- "mkmk",
- },
- hang = {
- "ccmp", "ljmo", "vjmo", "tjmo",
- },
-}
-
-local global_defaults = { mode = "node" }
-
-defaults.beng = defaults.deva
-defaults.guru = defaults.deva
-defaults.gujr = defaults.deva
-defaults.orya = defaults.deva
-defaults.taml = defaults.deva
-defaults.telu = defaults.deva
-defaults.knda = defaults.deva
-defaults.mlym = defaults.deva
-defaults.sinh = defaults.deva
-
-defaults.syrc = defaults.arab
-defaults.mong = defaults.arab
-defaults.nko = defaults.arab
-
-defaults.tibt = defaults.khmr
-
-defaults.lao = defaults.thai
---[[ begin excerpt from font-ott.lua ]]
@@ -749,7 +709,7 @@ local verbosebaselines = swapped(baselines)
--doc]]--
-local support_incomplete = table.tohash({
+local support_incomplete = tabletohash({
"deva", "beng", "guru", "gujr",
"orya", "taml", "telu", "knda",
"mlym", "sinh",
@@ -764,6 +724,8 @@ local support_incomplete = table.tohash({
--- (string, string) dict -> (string, string) dict
local set_default_features = function (speclist)
+ local default_features = luaotfload.features
+
speclist = speclist or { }
speclist[""] = nil --- invalid options stub
@@ -788,32 +750,31 @@ local set_default_features = function (speclist)
or "dflt"
if support_incomplete[script] then
report("log", 0, "load",
- "support for the requested script: "
- .. "%q may be incomplete", script)
+ "Support for the requested script: "
+ .. "%q may be incomplete.", script)
end
else
script = "dflt"
end
speclist.script = script
- report("log", 0, "load",
- "auto-selecting default features for script: %s",
+ report("log", 1, "load",
+ "Auto-selecting default features for script: %s.",
script)
- local requested = defaults[script]
+ local requested = default_features.defaults[script]
if not requested then
- report("log", 0, "load",
- "no defaults for script %q, falling back to \"dflt\"",
+ report("log", 1, "load",
+ "No default features for script %q, falling back to \"dflt\".",
script)
- requested = defaults.dflt
+ requested = default_features.defaults.dflt
end
- for i=1, #requested do
- local feat = requested[i]
- if speclist[feat] ~= false then speclist[feat] = true end
+ for feat, state in next, requested do
+ if not speclist[feat] then speclist[feat] = state end
end
- for feat, state in next, global_defaults do
+ for feat, state in next, default_features.global do
--- This is primarily intended for setting node
--- mode unless “base” is requested, as stated
--- in the manual.
@@ -822,259 +783,6 @@ local set_default_features = function (speclist)
return speclist
end
------------------------------------------------------------------------
---- request syntax parser 2.2
------------------------------------------------------------------------
---- the luaotfload font request syntax (see manual)
---- has a canonical form:
----
---- \font<csname>=<prefix>:<identifier>:<features>
----
---- where
---- <csname> is the control sequence that activates the font
---- <prefix> is either “file” or “name”, determining the lookup
---- <identifer> is either a file name (no path) or a font
---- name, depending on the lookup
---- <features> is a list of switches or options, separated by
---- semicolons or commas; a switch is of the form “+” foo
---- or “-” foo, options are of the form lhs “=” rhs
----
---- however, to ensure backward compatibility we also have
---- support for Xetex-style requests.
----
---- for the Xetex emulation see:
---- · The XeTeX Reference Guide by Will Robertson, 2011
---- · The XeTeX Companion by Michel Goosens, 2010
---- · About XeTeX by Jonathan Kew, 2005
----
----
---- caueat emptor.
---- the request is parsed into one of **four** different
---- lookup categories: the regular ones, file and name,
---- as well as the Xetex compatibility ones, path and anon.
---- (maybe a better choice of identifier would be “ambig”.)
----
---- according to my reconstruction, the correct chaining
---- of the lookups for each category is as follows:
----
---- | File -> ( db/filename lookup )
----
---- | Name -> ( db/name lookup,
---- db/filename lookup )
----
---- | Path -> ( db/filename lookup,
---- fullpath lookup )
----
---- | Anon -> ( kpse.find_file(), // <- for tfm, ofm
---- db/name lookup,
---- db/filename lookup,
---- fullpath lookup )
----
---- caching of successful lookups is essential. we now
---- as of v2.2 have an experimental lookup cache that is
---- stored in a separate file. it pertains only to name:
---- lookups, and is described in more detail in
---- luaotfload-database.lua.
----
------------------------------------------------------------------------
-
---[[doc--
-
- One further incompatibility between Xetex and Luatex-Fonts consists
- in their option list syntax: apparently, Xetex requires key-value
- options to be prefixed by a "+" (ascii “plus”) character. We
- silently accept this as well, dropping the first byte if it is a
- plus or minus character.
-
- Reference: https://github.com/lualatex/luaotfload/issues/79#issuecomment-18104483
-
---doc]]--
-
-local handle_normal_option = function (key, val)
- val = stringlower(val)
- --- the former “toboolean()” handler
- if val == "true" then
- val = true
- elseif val == "false" then
- val = false
- end
- return key, val
-end
-
---[[doc--
-
- Xetex style indexing begins at zero which we just increment before
- passing it along to the font loader. Ymmv.
-
---doc]]--
-
-local handle_xetex_option = function (key, val)
- val = stringlower(val)
- local numeric = tonumber(val) --- decimal only; keeps colors intact
- if numeric then --- ugh
- if mathceil(numeric) == numeric then -- integer, possible index
- val = tostring(numeric + 1)
- end
- elseif val == "true" then
- val = true
- elseif val == "false" then
- val = false
- end
- return key, val
-end
-
---[[doc--
-
- Instead of silently ignoring invalid options we emit a warning to
- the log.
-
- Note that we have to return a pair to please rawset(). This creates
- an entry on the resulting features hash which will later be removed
- during set_default_features().
-
---doc]]--
-
-local handle_invalid_option = function (opt)
- report("log", 0, "load", "font option %q unknown.", opt)
- return "", false
-end
-
---[[doc--
-
- Dirty test if a file: request is actually a path: lookup; don’t
- ask! Note this fails on Windows-style absolute paths. These will
- *really* have to use the correct request.
-
---doc]]--
-
-local check_garbage = function (_,i, garbage)
- if stringfind(garbage, "/") then
- report("log", 0, "load", --- ffs use path!
- "warning: path in file: lookups is deprecated; ")
- report("log", 0, "load", "use bracket syntax instead!")
- report("log", 0, "load",
- "position: %d; full match: %q",
- i, garbage)
- return true
- end
- return false
-end
-
-local lpegmatch = lpeg.match
-local P, S, R = lpeg.P, lpeg.S, lpeg.R
-local C, Cc, Cf, Cg, Cmt, Cs, Ct
- = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cmt, lpeg.Cs, lpeg.Ct
-
---- terminals and low-level classes -----------------------------------
---- note we could use the predefined ones from lpeg.patterns
-local dot = P"."
-local colon = P":"
-local featuresep = S",;"
-local slash = P"/"
-local equals = P"="
-local lbrk, rbrk = P"[", P"]"
-
-local spacing = S" \t\v"
-local ws = spacing^0
-
-local digit = R"09"
-local alpha = R("az", "AZ")
-local anum = alpha + digit
-local decimal = digit^1 * (dot * digit^0)^-1
-
---- modifiers ---------------------------------------------------------
---[[doc--
- The slash notation: called “modifiers” (Kew) or “font options”
- (Robertson, Goosens)
- we only support the shorthands for italic / bold / bold italic
- shapes, as well as setting optical size, the rest is ignored.
---doc]]--
-local style_modifier = (P"BI" + P"IB" + P"bi" + P"ib" + S"biBI")
- / stringlower
-local size_modifier = S"Ss" * P"=" --- optical size
- * Cc"optsize" * C(decimal)
-local other_modifier = P"AAT" + P"aat" --- apple stuff; unsupported
- + P"ICU" + P"icu" --- not applicable
- + P"GR" + P"gr" --- sil stuff; unsupported
-local garbage_modifier = ((1 - colon - slash)^0 * Cc(false))
-local modifier = slash * (other_modifier --> ignore
- + Cs(style_modifier) --> collect
- + Ct(size_modifier) --> collect
- + garbage_modifier) --> warn
-local modifier_list = Cg(Ct(modifier^0), "modifiers")
-
---- lookups -----------------------------------------------------------
-local fontname = C((1-S":(/")^1) --- like luatex-fonts
-local unsupported = Cmt((1-S":(")^1, check_garbage)
-local prefixed = P"name:" * ws * Cg(fontname, "name")
---- initially we intended file: to emulate the behavior of
---- luatex-fonts, i.e. no paths allowed. after all, we do have XeTeX
---- emulation with the path lookup and it interferes with db lookups.
---- turns out fontspec and other widely used packages rely on file:
---- with paths already, so we’ll add a less strict rule here. anyways,
---- we’ll emit a warning.
- + P"file:" * ws * Cg(unsupported, "path")
- + P"file:" * ws * Cg(fontname, "file")
---- EXPERIMENTAL: kpse lookup
- + P"kpse:" * ws * Cg(fontname, "kpse")
---- EXPERIMENTAL: custom lookup
- + P"my:" * ws * Cg(fontname, "my")
-local unprefixed = Cg(fontname, "anon")
-local path_lookup = lbrk * Cg(C((1-rbrk)^1), "path") * rbrk
-
---- features ----------------------------------------------------------
-local field_char = anum + S"+-." --- sic!
-local field = field_char^1
---- assignments are “lhs=rhs”
---- or “+lhs=rhs” (Xetex-style)
---- switches are “+key” | “-key”
-local normal_option = C(field) * ws * equals * ws * C(field) * ws
-local xetex_option = P"+" * ws * normal_option
-local ignore_option = (1 - equals - featuresep)^1
- * equals
- * (1 - featuresep)^1
-local assignment = xetex_option / handle_xetex_option
- + normal_option / handle_normal_option
- + ignore_option / handle_invalid_option
-local switch = P"+" * ws * C(field) * Cc(true)
- + P"-" * ws * C(field) * Cc(false)
- + C(field) * Cc(true) --- default
-local feature_expr = ws * Cg(assignment + switch) * ws
-local option = feature_expr
-local feature_list = Cf(Ct""
- * option
- * (featuresep * option^-1)^0
- , rawset)
- * featuresep^-1
-
---- other -------------------------------------------------------------
---- This rule is present in the original parser. It sets the “sub”
---- field of the specification which allows addressing a specific
---- font inside a TTC container. Neither in Luatex-Fonts nor in
---- Luaotfload is this documented, so we might as well silently drop
---- it. However, as backward compatibility is one of our prime goals we
---- just insert it here and leave it undocumented until someone cares
---- to ask. (Note: afair subfonts are numbered, but this rule matches a
---- string; I won’t mess with it though until someone reports a
---- problem.)
---- local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim
---- Note to self: subfonts apparently start at index 0. Tested with
---- Cambria.ttc that includes “Cambria Math” at 0 and “Cambria” at 1.
---- Other values cause luatex to segfault.
-local subfont = P"(" * Cg((1 - S"()")^1, "sub") * P")"
---- top-level rules ---------------------------------------------------
---- \font\foo=<specification>:<features>
-local features = Cg(feature_list, "features")
-local specification = (prefixed + unprefixed)
- * subfont^-1
- * modifier_list^-1
-local font_request = Ct(path_lookup * (colon^-1 * features)^-1
- + specification * (colon * features)^-1)
-
--- lpeg.print(font_request)
---- new parser: 657 rules
---- old parser: 230 rules
-
local import_values = {
--- That’s what the 1.x parser did, not quite as graciously,
--- with an array of branch expressions.
@@ -1138,7 +846,7 @@ end
--- spec -> spec
local handle_request = function (specification)
- local request = lpegmatch(font_request,
+ local request = lpegmatch(luaotfload.parsers.font_request,
specification.specification)
if not request then
--- happens when called with an absolute path
@@ -1230,7 +938,6 @@ local report_otf = logs.reporter("fonts","otf loading")
local otf = fonts.handlers.otf
local registerotffeature = otf.features.register
-local setmetatableindex = table.setmetatableindex
--[[HH--
@@ -1286,6 +993,7 @@ local function addfeature(data,feature,specifications)
local subtables = specification.subtables or { specification.data } or { }
local featuretype = types[specification.type or "substitution"]
local featureflags = specification.flags or noflags
+ local featureorder = specification.order or { feature }
local added = false
local featurename = stringformat("ctx_%s_%s",feature,s)
local st = { }
@@ -1346,17 +1054,23 @@ local function addfeature(data,feature,specifications)
-- script = { lang1, lang2, lang3 } or script = { lang1 = true, ... }
for k, v in next, askedfeatures do
if v[1] then
- askedfeatures[k] = table.tohash(v)
+ askedfeatures[k] = tabletohash(v)
end
end
- sequences[#sequences+1] = {
+ local sequence = {
chain = 0,
features = { [feature] = askedfeatures },
flags = featureflags,
name = featurename,
+ order = featureorder,
subtables = st,
type = featuretype,
}
+ if specification.prepend then
+ insert(sequences,1,sequence)
+ else
+ insert(sequences,sequence)
+ end
-- register in metadata (merge as there can be a few)
if not gsubfeatures then
gsubfeatures = { }
@@ -1386,6 +1100,7 @@ local function addfeature(data,feature,specifications)
end
end
+
otf.enhancers.addfeature = addfeature
local extrafeatures = { }
@@ -1414,6 +1129,8 @@ local tlig = {
[0x0060] = 0x2018, -- quoteright
},
flags = { },
+ order = { "tlig" },
+ prepend = true,
},
{
type = "ligature",
@@ -1432,6 +1149,8 @@ local tlig = {
[0x00BB] = {0x003E, 0x003E}, -- RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
},
flags = { },
+ order = { "tlig" },
+ prepend = true,
},
{
type = "ligature",
@@ -1443,6 +1162,8 @@ local tlig = {
[0x00BF] = {0x003F, 0x0060}, -- questiondown
},
flags = { },
+ order = { "tlig" },
+ prepend = true,
},
}
@@ -1494,6 +1215,7 @@ local anum_specification = {
features = { arab = { far = true, urd = true, snd = true } },
data = anum_persian,
flags = { },
+ order = { "anum" },
valid = valid,
},
{
@@ -1501,23 +1223,16 @@ local anum_specification = {
features = { arab = { ["*"] = true } },
data = anum_arabic,
flags = { },
+ order = { "anum" },
valid = valid,
},
}
---[[doc--
-
- Below the specifications as given in the removed font-otc.lua.
- The rest was identical to what this file had from the beginning.
- Both make the “anum.tex” test pass anyways.
-
---doc]]--
-
-otf.addfeature("anum",anum_specification)
+otf.addfeature ("anum", anum_specification)
registerotffeature {
- name = 'anum',
- description = 'arabic digits',
+ name = "anum",
+ description = "arabic digits",
}
-- vim:tw=71:sw=4:ts=4:expandtab
diff --git a/luaotfload-fontloader.lua b/src/luaotfload-fontloader.lua
index 1773241..2f26be7 100644
--- a/luaotfload-fontloader.lua
+++ b/src/luaotfload-fontloader.lua
@@ -1,6 +1,6 @@
-- merged file : luatex-fonts-merged.lua
-- parent file : luatex-fonts.lua
--- merge date : 12/24/13 17:52:44
+-- merge date : 07/06/14 22:50:12
do -- begin closure to overcome local limits and interference
@@ -82,6 +82,9 @@ function optionalrequire(...)
return result
end
end
+if lua then
+ lua.mask=load([[τεχ = 1]]) and "utf" or "ascii"
+end
end -- closure
@@ -101,7 +104,9 @@ local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.forma
local floor=math.floor
local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt
local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print
-setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end)
+if setinspector then
+ setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end)
+end
lpeg.patterns=lpeg.patterns or {}
local patterns=lpeg.patterns
local anything=P(1)
@@ -120,7 +125,7 @@ local uppercase=R("AZ")
local underscore=P("_")
local hexdigit=digit+lowercase+uppercase
local cr,lf,crlf=P("\r"),P("\n"),P("\r\n")
-local newline=crlf+S("\r\n")
+local newline=P("\r")*(P("\n")+P(true))+P("\n")
local escaped=P("\\")*anything
local squote=P("'")
local dquote=P('"')
@@ -142,8 +147,8 @@ patterns.utfbom_32_le=utfbom_32_le
patterns.utfbom_16_be=utfbom_16_be
patterns.utfbom_16_le=utfbom_16_le
patterns.utfbom_8=utfbom_8
-patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n")
-patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000")
+patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n")
+patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000")
patterns.utf8one=R("\000\127")
patterns.utf8two=R("\194\223")*utf8next
patterns.utf8three=R("\224\239")*utf8next*utf8next
@@ -170,9 +175,11 @@ patterns.spacer=spacer
patterns.whitespace=whitespace
patterns.nonspacer=nonspacer
patterns.nonwhitespace=nonwhitespace
-local stripper=spacer^0*C((spacer^0*nonspacer^1)^0)
+local stripper=spacer^0*C((spacer^0*nonspacer^1)^0)
+local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0)
local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0))
patterns.stripper=stripper
+patterns.fullstripper=fullstripper
patterns.collapser=collapser
patterns.lowercase=lowercase
patterns.uppercase=uppercase
@@ -210,9 +217,12 @@ patterns.integer=sign^-1*digit^1
patterns.unsigned=digit^0*period*digit^1
patterns.float=sign^-1*patterns.unsigned
patterns.cunsigned=digit^0*comma*digit^1
+patterns.cpunsigned=digit^0*(period+comma)*digit^1
patterns.cfloat=sign^-1*patterns.cunsigned
+patterns.cpfloat=sign^-1*patterns.cpunsigned
patterns.number=patterns.float+patterns.integer
patterns.cnumber=patterns.cfloat+patterns.integer
+patterns.cpnumber=patterns.cpfloat+patterns.integer
patterns.oct=zero*octdigit^1
patterns.octal=patterns.oct
patterns.HEX=zero*P("X")*(digit+uppercase)^1
@@ -395,7 +405,7 @@ function lpeg.replacer(one,two,makefunction,isutf)
return pattern
end
end
-function lpeg.finder(lst,makefunction)
+function lpeg.finder(lst,makefunction,isutf)
local pattern
if type(lst)=="table" then
pattern=P(false)
@@ -411,7 +421,11 @@ function lpeg.finder(lst,makefunction)
else
pattern=P(lst)
end
- pattern=(1-pattern)^0*pattern
+ if isutf then
+ pattern=((utf8char or 1)-pattern)^0*pattern
+ else
+ pattern=(1-pattern)^0*pattern
+ end
if makefunction then
return function(str)
return lpegmatch(pattern,str)
@@ -625,21 +639,22 @@ function lpeg.append(list,pp,delayed,checked)
end
return p
end
-local function make(t)
- local p
+local function make(t,hash)
+ local p=P(false)
local keys=sortedkeys(t)
for i=1,#keys do
local k=keys[i]
local v=t[k]
- if not p then
+ local h=hash[v]
+ if h then
if next(v) then
- p=P(k)*make(v)
+ p=p+P(k)*(make(v,hash)+P(true))
else
- p=P(k)
+ p=p+P(k)*P(true)
end
else
if next(v) then
- p=p+P(k)*make(v)
+ p=p+P(k)*make(v,hash)
else
p=p+P(k)
end
@@ -649,16 +664,37 @@ local function make(t)
end
function lpeg.utfchartabletopattern(list)
local tree={}
- for i=1,#list do
- local t=tree
- for c in gmatch(list[i],".") do
- if not t[c] then
- t[c]={}
+ local hash={}
+ local n=#list
+ if n==0 then
+ for s in next,list do
+ local t=tree
+ for c in gmatch(s,".") do
+ local tc=t[c]
+ if not tc then
+ tc={}
+ t[c]=tc
+ end
+ t=tc
end
- t=t[c]
+ hash[t]=s
+ end
+ else
+ for i=1,n do
+ local t=tree
+ local s=list[i]
+ for c in gmatch(s,".") do
+ local tc=t[c]
+ if not tc then
+ tc={}
+ t[c]=tc
+ end
+ t=tc
+ end
+ hash[t]=s
end
end
- return make(tree)
+ return make(tree,hash)
end
patterns.containseol=lpeg.finder(eol)
local function nextstep(n,step,result)
@@ -748,11 +784,15 @@ function string.limit(str,n,sentinel)
end
end
local stripper=patterns.stripper
+local fullstripper=patterns.fullstripper
local collapser=patterns.collapser
local longtostring=patterns.longtostring
function string.strip(str)
return lpegmatch(stripper,str) or ""
end
+function string.fullstrip(str)
+ return lpegmatch(fullstripper,str) or ""
+end
function string.collapsespaces(str)
return lpegmatch(collapser,str) or ""
end
@@ -886,6 +926,36 @@ local function sortedkeys(tab)
return {}
end
end
+local function sortedhashonly(tab)
+ if tab then
+ local srt,s={},0
+ for key,_ in next,tab do
+ if type(key)=="string" then
+ s=s+1
+ srt[s]=key
+ end
+ end
+ sort(srt)
+ return srt
+ else
+ return {}
+ end
+end
+local function sortedindexonly(tab)
+ if tab then
+ local srt,s={},0
+ for key,_ in next,tab do
+ if type(key)=="number" then
+ s=s+1
+ srt[s]=key
+ end
+ end
+ sort(srt)
+ return srt
+ else
+ return {}
+ end
+end
local function sortedhashkeys(tab,cmp)
if tab then
local srt,s={},0
@@ -911,6 +981,8 @@ function table.allkeys(t)
return sortedkeys(keys)
end
table.sortedkeys=sortedkeys
+table.sortedhashonly=sortedhashonly
+table.sortedindexonly=sortedindexonly
table.sortedhashkeys=sortedhashkeys
local function nothing() end
local function sortedhash(t,cmp)
@@ -923,14 +995,14 @@ local function sortedhash(t,cmp)
end
local n=0
local m=#s
- local function kv(s)
+ local function kv()
if n<m then
n=n+1
local k=s[n]
return k,t[k]
end
end
- return kv,s
+ return kv
else
return nothing
end
@@ -1095,7 +1167,7 @@ local function simple_table(t)
if tv=="number" then
nt=nt+1
if hexify then
- tt[nt]=format("0x%04X",v)
+ tt[nt]=format("0x%X",v)
else
tt[nt]=tostring(v)
end
@@ -1126,7 +1198,7 @@ local function do_serialize(root,name,depth,level,indexed)
local tn=type(name)
if tn=="number" then
if hexify then
- handle(format("%s[0x%04X]={",depth,name))
+ handle(format("%s[0x%X]={",depth,name))
else
handle(format("%s[%s]={",depth,name))
end
@@ -1165,7 +1237,7 @@ local function do_serialize(root,name,depth,level,indexed)
if compact and first and tk=="number" and k>=first and k<=last then
if tv=="number" then
if hexify then
- handle(format("%s 0x%04X,",depth,v))
+ handle(format("%s 0x%X,",depth,v))
else
handle(format("%s %s,",depth,v))
end
@@ -1206,25 +1278,25 @@ local function do_serialize(root,name,depth,level,indexed)
elseif tv=="number" then
if tk=="number" then
if hexify then
- handle(format("%s [0x%04X]=0x%04X,",depth,k,v))
+ handle(format("%s [0x%X]=0x%X,",depth,k,v))
else
handle(format("%s [%s]=%s,",depth,k,v))
end
elseif tk=="boolean" then
if hexify then
- handle(format("%s [%s]=0x%04X,",depth,k and "true" or "false",v))
+ handle(format("%s [%s]=0x%X,",depth,k and "true" or "false",v))
else
handle(format("%s [%s]=%s,",depth,k and "true" or "false",v))
end
elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
if hexify then
- handle(format("%s %s=0x%04X,",depth,k,v))
+ handle(format("%s %s=0x%X,",depth,k,v))
else
handle(format("%s %s=%s,",depth,k,v))
end
else
if hexify then
- handle(format("%s [%q]=0x%04X,",depth,k,v))
+ handle(format("%s [%q]=0x%X,",depth,k,v))
else
handle(format("%s [%q]=%s,",depth,k,v))
end
@@ -1233,7 +1305,7 @@ local function do_serialize(root,name,depth,level,indexed)
if reduce and tonumber(v) then
if tk=="number" then
if hexify then
- handle(format("%s [0x%04X]=%s,",depth,k,v))
+ handle(format("%s [0x%X]=%s,",depth,k,v))
else
handle(format("%s [%s]=%s,",depth,k,v))
end
@@ -1247,7 +1319,7 @@ local function do_serialize(root,name,depth,level,indexed)
else
if tk=="number" then
if hexify then
- handle(format("%s [0x%04X]=%q,",depth,k,v))
+ handle(format("%s [0x%X]=%q,",depth,k,v))
else
handle(format("%s [%s]=%q,",depth,k,v))
end
@@ -1263,7 +1335,7 @@ local function do_serialize(root,name,depth,level,indexed)
if not next(v) then
if tk=="number" then
if hexify then
- handle(format("%s [0x%04X]={},",depth,k))
+ handle(format("%s [0x%X]={},",depth,k))
else
handle(format("%s [%s]={},",depth,k))
end
@@ -1279,7 +1351,7 @@ local function do_serialize(root,name,depth,level,indexed)
if st then
if tk=="number" then
if hexify then
- handle(format("%s [0x%04X]={ %s },",depth,k,concat(st,", ")))
+ handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", ")))
else
handle(format("%s [%s]={ %s },",depth,k,concat(st,", ")))
end
@@ -1299,7 +1371,7 @@ local function do_serialize(root,name,depth,level,indexed)
elseif tv=="boolean" then
if tk=="number" then
if hexify then
- handle(format("%s [0x%04X]=%s,",depth,k,v and "true" or "false"))
+ handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false"))
else
handle(format("%s [%s]=%s,",depth,k,v and "true" or "false"))
end
@@ -1315,7 +1387,7 @@ local function do_serialize(root,name,depth,level,indexed)
local f=getinfo(v).what=="C" and dump(dummy) or dump(v)
if tk=="number" then
if hexify then
- handle(format("%s [0x%04X]=load(%q),",depth,k,f))
+ handle(format("%s [0x%X]=load(%q),",depth,k,f))
else
handle(format("%s [%s]=load(%q),",depth,k,f))
end
@@ -1330,7 +1402,7 @@ local function do_serialize(root,name,depth,level,indexed)
else
if tk=="number" then
if hexify then
- handle(format("%s [0x%04X]=%q,",depth,k,tostring(v)))
+ handle(format("%s [0x%X]=%q,",depth,k,tostring(v)))
else
handle(format("%s [%s]=%q,",depth,k,tostring(v)))
end
@@ -1384,7 +1456,7 @@ local function serialize(_handle,root,name,specification)
end
elseif tname=="number" then
if hexify then
- handle(format("[0x%04X]={",name))
+ handle(format("[0x%X]={",name))
else
handle("["..name.."]={")
end
@@ -1636,7 +1708,9 @@ function table.print(t,...)
serialize(print,t,...)
end
end
-setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end)
+if setinspector then
+ setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end)
+end
function table.sub(t,i,j)
return { unpack(t,i,j) }
end
@@ -1689,6 +1763,44 @@ function table.values(t,s)
return {}
end
end
+function table.filtered(t,pattern,sort,cmp)
+ if t and type(pattern)=="string" then
+ if sort then
+ local s
+ if cmp then
+ s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
+ else
+ s=sortedkeys(t)
+ end
+ local n=0
+ local m=#s
+ local function kv(s)
+ while n<m do
+ n=n+1
+ local k=s[n]
+ if find(k,pattern) then
+ return k,t[k]
+ end
+ end
+ end
+ return kv,s
+ else
+ local n=next(t)
+ local function iterator()
+ while n do
+ local k=n
+ n=next(t,k)
+ if find(k,pattern) then
+ return k,t[k]
+ end
+ end
+ end
+ return iterator,t
+ end
+ else
+ return nothing
+ end
+end
end -- closure
@@ -1706,7 +1818,7 @@ local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format
local concat=table.concat
local floor=math.floor
local type=type
-if string.find(os.getenv("PATH"),";") then
+if string.find(os.getenv("PATH"),";",1,true) then
io.fileseparator,io.pathseparator="\\",";"
else
io.fileseparator,io.pathseparator="/",":"
@@ -2508,15 +2620,28 @@ local unpack,concat=table.unpack,table.concat
local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc
local patterns,lpegmatch=lpeg.patterns,lpeg.match
local utfchar,utfbyte=utf.char,utf.byte
-local loadstripped=_LUAVERSION<5.2 and load or function(str)
- return load(dump(load(str),true))
+local loadstripped=nil
+if _LUAVERSION<5.2 then
+ loadstripped=function(str,shortcuts)
+ return load(str)
+ end
+else
+ loadstripped=function(str,shortcuts)
+ if shortcuts then
+ return load(dump(load(str),true),nil,nil,shortcuts)
+ else
+ return load(dump(load(str),true))
+ end
+ end
end
if not number then number={} end
local stripper=patterns.stripzeros
local function points(n)
+ n=tonumber(n)
return (not n or n==0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536))
end
local function basepoints(n)
+ n=tonumber(n)
return (not n or n==0) and "0bp" or lpegmatch(stripper,format("%.5fbp",n*(7200/7227)/65536))
end
number.points=points
@@ -2579,11 +2704,40 @@ local pattern=Carg(1)/function(t)
function strings.tabtospace(str,tab)
return lpegmatch(pattern,str,1,tab or 7)
end
-function strings.striplong(str)
- str=gsub(str,"^%s*","")
- str=gsub(str,"[\n\r]+ *","\n")
- return str
+local newline=patterns.newline
+local endofstring=patterns.endofstring
+local whitespace=patterns.whitespace
+local spacer=patterns.spacer
+local space=spacer^0
+local nospace=space/""
+local endofline=nospace*newline
+local stripend=(whitespace^1*endofstring)/""
+local normalline=(nospace*((1-space*(newline+endofstring))^1)*nospace)
+local stripempty=endofline^1/""
+local normalempty=endofline^1
+local singleempty=endofline*(endofline^0/"")
+local doubleempty=endofline*endofline^-1*(endofline^0/"")
+local stripstart=stripempty^0
+local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 )
+local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 )
+local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 )
+local p_retain_normal=Cs ((normalline+normalempty )^0 )
+local p_retain_collapse=Cs ((normalline+doubleempty )^0 )
+local p_retain_noempty=Cs ((normalline+singleempty )^0 )
+local striplinepatterns={
+ ["prune"]=p_prune_normal,
+ ["prune and collapse"]=p_prune_collapse,
+ ["prune and no empty"]=p_prune_noempty,
+ ["retain"]=p_retain_normal,
+ ["retain and collapse"]=p_retain_collapse,
+ ["retain and no empty"]=p_retain_noempty,
+ ["collapse"]=patterns.collapser,
+}
+strings.striplinepatterns=striplinepatterns
+function strings.striplines(str,how)
+ return str and lpegmatch(how and striplinepatterns[how] or p_prune_collapse,str) or str
end
+strings.striplong=strings.striplines
function strings.nice(str)
str=gsub(str,"[:%-+_]+"," ")
return str
@@ -2659,31 +2813,58 @@ function number.sparseexponent(f,n)
end
return tostring(n)
end
-local preamble=[[
-local type = type
-local tostring = tostring
-local tonumber = tonumber
-local format = string.format
-local concat = table.concat
-local signed = number.signed
-local points = number.points
-local basepoints = number.basepoints
-local utfchar = utf.char
-local utfbyte = utf.byte
-local lpegmatch = lpeg.match
-local nspaces = string.nspaces
-local tracedchar = string.tracedchar
-local autosingle = string.autosingle
-local autodouble = string.autodouble
-local sequenced = table.sequenced
-local formattednumber = number.formatted
-local sparseexponent = number.sparseexponent
-]]
local template=[[
%s
%s
return function(%s) return %s end
]]
+local preamble,environment="",{}
+if _LUAVERSION<5.2 then
+ preamble=[[
+local lpeg=lpeg
+local type=type
+local tostring=tostring
+local tonumber=tonumber
+local format=string.format
+local concat=table.concat
+local signed=number.signed
+local points=number.points
+local basepoints= number.basepoints
+local utfchar=utf.char
+local utfbyte=utf.byte
+local lpegmatch=lpeg.match
+local nspaces=string.nspaces
+local tracedchar=string.tracedchar
+local autosingle=string.autosingle
+local autodouble=string.autodouble
+local sequenced=table.sequenced
+local formattednumber=number.formatted
+local sparseexponent=number.sparseexponent
+ ]]
+else
+ environment={
+ global=global or _G,
+ lpeg=lpeg,
+ type=type,
+ tostring=tostring,
+ tonumber=tonumber,
+ format=string.format,
+ concat=table.concat,
+ signed=number.signed,
+ points=number.points,
+ basepoints=number.basepoints,
+ utfchar=utf.char,
+ utfbyte=utf.byte,
+ lpegmatch=lpeg.match,
+ nspaces=string.nspaces,
+ tracedchar=string.tracedchar,
+ autosingle=string.autosingle,
+ autodouble=string.autodouble,
+ sequenced=table.sequenced,
+ formattednumber=number.formatted,
+ sparseexponent=number.sparseexponent,
+ }
+end
local arguments={ "a1" }
setmetatable(arguments,{ __index=function(t,k)
local v=t[k-1]..",a"..k
@@ -2722,7 +2903,7 @@ local format_i=function(f)
if f and f~="" then
return format("format('%%%si',a%s)",f,n)
else
- return format("format('%%i',a%s)",n)
+ return format("format('%%i',a%s)",n)
end
end
local format_d=format_i
@@ -2734,6 +2915,14 @@ local format_f=function(f)
n=n+1
return format("format('%%%sf',a%s)",f,n)
end
+local format_F=function()
+ n=n+1
+ if not f or f=="" then
+ return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or format((a%s %% 1 == 0) and '%%i' or '%%.9f',a%s))",n,n,n,n)
+ else
+ return format("format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s)",n,f,n)
+ end
+end
local format_g=function(f)
n=n+1
return format("format('%%%sg',a%s)",f,n)
@@ -2948,7 +3137,7 @@ local builder=Cs { "start",
(
P("%")/""*(
V("!")
-+V("s")+V("q")+V("i")+V("d")+V("f")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o")
++V("s")+V("q")+V("i")+V("d")+V("f")+V("F")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o")
+V("c")+V("C")+V("S")
+V("Q")
+V("N")
@@ -2968,6 +3157,7 @@ local builder=Cs { "start",
["i"]=(prefix_any*P("i"))/format_i,
["d"]=(prefix_any*P("d"))/format_d,
["f"]=(prefix_any*P("f"))/format_f,
+ ["F"]=(prefix_any*P("F"))/format_F,
["g"]=(prefix_any*P("g"))/format_g,
["G"]=(prefix_any*P("G"))/format_G,
["e"]=(prefix_any*P("e"))/format_e,
@@ -3005,8 +3195,8 @@ local builder=Cs { "start",
["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension,
}
local direct=Cs (
- P("%")/""*Cc([[local format = string.format return function(str) return format("%]])*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*Cc([[",str) end]])*P(-1)
- )
+ P("%")*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*P(-1)/[[local format = string.format return function(str) return format("%0",str) end]]
+)
local function make(t,str)
local f
local p
@@ -3015,10 +3205,10 @@ local function make(t,str)
f=loadstripped(p)()
else
n=0
- p=lpegmatch(builder,str,1,"..",t._extensions_)
+ p=lpegmatch(builder,str,1,t._connector_,t._extensions_)
if n>0 then
p=format(template,preamble,t._preamble_,arguments[n],p)
- f=loadstripped(p)()
+ f=loadstripped(p,t._environment_)()
else
f=function() return str end
end
@@ -3030,10 +3220,22 @@ local function use(t,fmt,...)
return t[fmt](...)
end
strings.formatters={}
-function strings.formatters.new()
- local t={ _extensions_={},_preamble_="",_type_="formatter" }
- setmetatable(t,{ __index=make,__call=use })
- return t
+if _LUAVERSION<5.2 then
+ function strings.formatters.new(noconcat)
+ local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_=preamble,_environment_={} }
+ setmetatable(t,{ __index=make,__call=use })
+ return t
+ end
+else
+ function strings.formatters.new(noconcat)
+ local e={}
+ for k,v in next,environment do
+ e[k]=v
+ end
+ local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_="",_environment_=e }
+ setmetatable(t,{ __index=make,__call=use })
+ return t
+ end
end
local formatters=strings.formatters.new()
string.formatters=formatters
@@ -3041,8 +3243,12 @@ string.formatter=function(str,...) return formatters[str](...) end
local function add(t,name,template,preamble)
if type(t)=="table" and t._type_=="formatter" then
t._extensions_[name]=template or "%s"
- if preamble then
+ if type(preamble)=="string" then
t._preamble_=preamble.."\n"..t._preamble_
+ elseif type(preamble)=="table" then
+ for k,v in next,preamble do
+ t._environment_[k]=v
+ end
end
end
end
@@ -3051,9 +3257,24 @@ patterns.xmlescape=Cs((P("<")/"&lt;"+P(">")/"&gt;"+P("&")/"&amp;"+P('"')/"&quot;
patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0)
patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0)
patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"'))
-add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],[[local xmlescape = lpeg.patterns.xmlescape]])
-add(formatters,"tex",[[lpegmatch(texescape,%s)]],[[local texescape = lpeg.patterns.texescape]])
-add(formatters,"lua",[[lpegmatch(luaescape,%s)]],[[local luaescape = lpeg.patterns.luaescape]])
+if _LUAVERSION<5.2 then
+ add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],"local xmlescape = lpeg.patterns.xmlescape")
+ add(formatters,"tex",[[lpegmatch(texescape,%s)]],"local texescape = lpeg.patterns.texescape")
+ add(formatters,"lua",[[lpegmatch(luaescape,%s)]],"local luaescape = lpeg.patterns.luaescape")
+else
+ add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape })
+ add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape })
+ add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape })
+end
+local dquote=patterns.dquote
+local equote=patterns.escaped+dquote/'\\"'+1
+local space=patterns.space
+local cquote=Cc('"')
+local pattern=Cs(dquote*(equote-P(-2))^0*dquote)
++Cs(cquote*(equote-space)^0*space*equote^0*cquote)
+function string.optionalquoted(str)
+ return lpegmatch(pattern,str) or str
+end
end -- closure
@@ -3250,6 +3471,17 @@ function caches.loaddata(paths,name)
for i=1,#paths do
local data=false
local luaname,lucname=makefullname(paths[i],name)
+ if lucname and not lfs.isfile(lucname) and type(caches.compile)=="function" then
+ texio.write(string.format("(compiling luc: %s)",lucname))
+ data=loadfile(luaname)
+ if data then
+ data=data()
+ end
+ if data then
+ caches.compile(data,luaname,lucname)
+ return data
+ end
+ end
if lucname and lfs.isfile(lucname) then
texio.write(string.format("(load luc: %s)",lucname))
data=loadfile(lucname)
@@ -3305,6 +3537,15 @@ end
function table.setmetatableindex(t,f)
setmetatable(t,{ __index=f })
end
+arguments={}
+if arg then
+ for i=1,#arg do
+ local k,v=string.match(arg[i],"^%-%-([^=]+)=?(.-)$")
+ if k and v then
+ arguments[k]=v
+ end
+ end
+end
end -- closure
@@ -3473,9 +3714,17 @@ local free_node=node.free
local remove_node=node.remove
local new_node=node.new
local traverse_id=node.traverse_id
-local math_code=nodecodes.math
nodes.handlers.protectglyphs=node.protect_glyphs
nodes.handlers.unprotectglyphs=node.unprotect_glyphs
+local math_code=nodecodes.math
+local end_of_math=node.end_of_math
+function node.end_of_math(n)
+ if n.id==math_code and n.subtype==1 then
+ return n
+ else
+ return end_of_math(n)
+ end
+end
function nodes.remove(head,current,free_too)
local t=current
head,current=remove_node(head,current)
@@ -3769,14 +4018,15 @@ constructors.sharefonts=false
constructors.nofsharedfonts=0
local sharednames={}
function constructors.trytosharefont(target,tfmdata)
- if constructors.sharefonts then
+ if constructors.sharefonts then
local characters=target.characters
local n=1
local t={ target.psname }
local u=sortedkeys(characters)
for i=1,#u do
+ local k=u[i]
n=n+1;t[n]=k
- n=n+1;t[n]=characters[u[i]].index or k
+ n=n+1;t[n]=characters[k].index or k
end
local h=md5.HEX(concat(t," "))
local s=sharednames[h]
@@ -4006,6 +4256,7 @@ function constructors.scale(tfmdata,specification)
if changed then
local c=changed[unicode]
if c then
+ local ligatures=character.ligatures
description=descriptions[c] or descriptions[unicode] or character
character=characters[c] or character
index=description.index or c
@@ -4017,6 +4268,9 @@ function constructors.scale(tfmdata,specification)
touni=tounicode[i]
end
end
+ if ligatures and not character.ligatures then
+ character.ligatures=ligatures
+ end
else
description=descriptions[unicode] or character
index=description.index or unicode
@@ -4694,6 +4948,7 @@ end
local fonts=fonts
fonts.encodings={}
fonts.encodings.agl={}
+fonts.encodings.known={}
setmetatable(fonts.encodings.agl,{ __index=function(t,k)
if k=="unicodes" then
texio.write(" <loading (extended) adobe glyph list>")
@@ -5620,7 +5875,6 @@ unify=function(data,filename)
if unicode then
krn[unicode]=kern
else
- print(unicode,name)
end
end
description.kerns=krn
@@ -6405,7 +6659,7 @@ local type,next,tonumber,tostring=type,next,tonumber,tostring
local abs=math.abs
local insert=table.insert
local lpegmatch=lpeg.match
-local reversed,concat,remove=table.reversed,table.concat,table.remove
+local reversed,concat,remove,sortedkeys=table.reversed,table.concat,table.remove,table.sortedkeys
local ioflush=io.flush
local fastcopy,tohash,derivetable=table.fastcopy,table.tohash,table.derive
local formatters=string.formatters
@@ -6427,7 +6681,7 @@ local report_otf=logs.reporter("fonts","otf loading")
local fonts=fonts
local otf=fonts.handlers.otf
otf.glists={ "gsub","gpos" }
-otf.version=2.749
+otf.version=2.756
otf.cache=containers.define("fonts","otf",otf.version,true)
local fontdata=fonts.hashes.identifiers
local chardata=characters and characters.data
@@ -6579,6 +6833,7 @@ local valid_fields=table.tohash {
"upos",
"use_typo_metrics",
"uwidth",
+ "validation_state",
"version",
"vert_base",
"weight",
@@ -6969,15 +7224,22 @@ actions["prepare glyphs"]=function(data,filename,raw)
local glyph=cidglyphs[index]
if glyph then
local unicode=glyph.unicode
+if unicode>=0x00E000 and unicode<=0x00F8FF then
+ unicode=-1
+elseif unicode>=0x0F0000 and unicode<=0x0FFFFD then
+ unicode=-1
+elseif unicode>=0x100000 and unicode<=0x10FFFD then
+ unicode=-1
+end
local name=glyph.name or cidnames[index]
- if not unicode or unicode==-1 or unicode>=criterium then
+ if not unicode or unicode==-1 then
unicode=cidunicodes[index]
end
if unicode and descriptions[unicode] then
report_otf("preventing glyph %a at index %H to overload unicode %U",name or "noname",index,unicode)
unicode=-1
end
- if not unicode or unicode==-1 or unicode>=criterium then
+ if not unicode or unicode==-1 then
if not name then
name=format("u%06X",private)
end
@@ -7023,7 +7285,7 @@ actions["prepare glyphs"]=function(data,filename,raw)
if glyph then
local unicode=glyph.unicode
local name=glyph.name
- if not unicode or unicode==-1 or unicode>=criterium then
+ if not unicode or unicode==-1 then
unicode=private
unicodes[name]=private
if trace_private then
@@ -7045,7 +7307,6 @@ actions["prepare glyphs"]=function(data,filename,raw)
}
local altuni=glyph.altuni
if altuni then
- local d
for i=1,#altuni do
local a=altuni[i]
local u=a.unicode
@@ -7058,15 +7319,8 @@ actions["prepare glyphs"]=function(data,filename,raw)
vv={ [u]=unicode }
variants[v]=vv
end
- elseif d then
- d[#d+1]=u
- else
- d={ u }
end
end
- if d then
- duplicates[unicode]=d
- end
end
else
report_otf("potential problem: glyph %U is used but empty",index)
@@ -7084,47 +7338,45 @@ actions["check encoding"]=function(data,filename,raw)
local duplicates=resources.duplicates
local mapdata=raw.map or {}
local unicodetoindex=mapdata and mapdata.map or {}
+ local indextounicode=mapdata and mapdata.backmap or {}
local encname=lower(data.enc_name or mapdata.enc_name or "")
- local criterium=0xFFFF
+ local criterium=0xFFFF
+ local privateoffset=constructors.privateoffset
if find(encname,"unicode") then
if trace_loading then
report_otf("checking embedded unicode map %a",encname)
end
- for unicode,index in next,unicodetoindex do
- if unicode<=criterium and not descriptions[unicode] then
- local parent=indices[index]
- if not parent then
- report_otf("weird, unicode %U points to nowhere with index %H",unicode,index)
+ local reported={}
+ for maybeunicode,index in next,unicodetoindex do
+ if descriptions[maybeunicode] then
+ else
+ local unicode=indices[index]
+ if not unicode then
+ elseif maybeunicode==unicode then
+ elseif unicode>privateoffset then
else
- local parentdescription=descriptions[parent]
- if parentdescription then
- local altuni=parentdescription.altuni
- if not altuni then
- altuni={ { unicode=unicode } }
- parentdescription.altuni=altuni
- duplicates[parent]={ unicode }
+ local d=descriptions[unicode]
+ if d then
+ local c=d.copies
+ if c then
+ c[maybeunicode]=true
else
- local done=false
- for i=1,#altuni do
- if altuni[i].unicode==unicode then
- done=true
- break
- end
- end
- if not done then
- insert(altuni,{ unicode=unicode })
- insert(duplicates[parent],unicode)
- end
- end
- if trace_loading then
- report_otf("weird, unicode %U points to nowhere with index %H",unicode,index)
+ d.copies={ [maybeunicode]=true }
end
- else
- report_otf("weird, unicode %U points to %U with index %H",unicode,index)
+ elseif index and not reported[index] then
+ report_otf("missing index %i",index)
+ reported[index]=true
end
end
end
end
+ for unicode,data in next,descriptions do
+ local d=data.copies
+ if d then
+ duplicates[unicode]=sortedkeys(d)
+ data.copies=nil
+ end
+ end
elseif properties.cidinfo then
report_otf("warning: no unicode map, used cidmap %a",properties.cidinfo.usedname)
else
@@ -7132,6 +7384,7 @@ actions["check encoding"]=function(data,filename,raw)
end
if mapdata then
mapdata.map={}
+ mapdata.backmap={}
end
end
actions["add duplicates"]=function(data,filename,raw)
@@ -7142,28 +7395,37 @@ actions["add duplicates"]=function(data,filename,raw)
local indices=resources.indices
local duplicates=resources.duplicates
for unicode,d in next,duplicates do
- for i=1,#d do
- local u=d[i]
- if not descriptions[u] then
- local description=descriptions[unicode]
- local duplicate=table.copy(description)
- duplicate.comment=format("copy of U+%05X",unicode)
- descriptions[u]=duplicate
- local n=0
- for _,description in next,descriptions do
- if kerns then
- local kerns=description.kerns
- for _,k in next,kerns do
- local ku=k[unicode]
- if ku then
- k[u]=ku
- n=n+1
+ local nofduplicates=#d
+ if nofduplicates>4 then
+ if trace_loading then
+ report_otf("ignoring excessive duplicates of %U (n=%s)",unicode,nofduplicates)
+ end
+ else
+ for i=1,nofduplicates do
+ local u=d[i]
+ if not descriptions[u] then
+ local description=descriptions[unicode]
+ local n=0
+ for _,description in next,descriptions do
+ if kerns then
+ local kerns=description.kerns
+ for _,k in next,kerns do
+ local ku=k[unicode]
+ if ku then
+ k[u]=ku
+ n=n+1
+ end
end
end
end
- end
- if trace_loading then
- report_otf("duplicating %U to %U with index %H (%s kerns)",unicode,u,description.index,n)
+ if u>0 then
+ local duplicate=table.copy(description)
+ duplicate.comment=format("copy of U+%05X",unicode)
+ descriptions[u]=duplicate
+ if trace_loading then
+ report_otf("duplicating %U to %U with index %H (%s kerns)",unicode,u,description.index,n)
+ end
+ end
end
end
end
@@ -7358,10 +7620,16 @@ actions["reorganize subtables"]=function(data,filename,raw)
report_otf("skipping weird lookup number %s",k)
elseif features then
local f={}
+ local o={}
for i=1,#features do
local df=features[i]
local tag=strip(lower(df.tag))
- local ft=f[tag] if not ft then ft={} f[tag]=ft end
+ local ft=f[tag]
+ if not ft then
+ ft={}
+ f[tag]=ft
+ o[#o+1]=tag
+ end
local dscripts=df.scripts
for i=1,#dscripts do
local d=dscripts[i]
@@ -7381,6 +7649,7 @@ actions["reorganize subtables"]=function(data,filename,raw)
subtables=subtables,
markclass=markclass,
features=f,
+ order=o,
}
else
lookups[name]={
@@ -7616,6 +7885,14 @@ actions["reorganize lookups"]=function(data,filename,raw)
rule.current=s_hashed(names,s_h_cache)
end
rule.glyphs=nil
+ local lookups=rule.lookups
+ if lookups then
+ for i=1,#names do
+ if not lookups[i] then
+ lookups[i]=""
+ end
+ end
+ end
end
end
end
@@ -7887,6 +8164,11 @@ actions["check metadata"]=function(data,filename,raw)
ttftables[i].data="deleted"
end
end
+ if metadata.validation_state and table.contains(metadata.validation_state,"bad_ps_fontname") then
+ local name=file.nameonly(filename)
+ metadata.fontname="bad-fontname-"..name
+ metadata.fullname="bad-fullname-"..name
+ end
end
actions["cleanup tables"]=function(data,filename,raw)
data.resources.indices=nil
@@ -8184,6 +8466,24 @@ local function otftotfm(specification)
local features=specification.features.normal
local rawdata=otf.load(filename,sub,features and features.featurefile)
if rawdata and next(rawdata) then
+ local descriptions=rawdata.descriptions
+ local duplicates=rawdata.resources.duplicates
+ if duplicates then
+ local nofduplicates,nofduplicated=0,0
+ for parent,list in next,duplicates do
+ for i=1,#list do
+ local unicode=list[i]
+ if not descriptions[unicode] then
+ descriptions[unicode]=descriptions[parent]
+ nofduplicated=nofduplicated+1
+ end
+ end
+ nofduplicates=nofduplicates+#list
+ end
+ if trace_otf and nofduplicated~=nofduplicates then
+ report_otf("%i extra duplicates copied out of %i",nofduplicated,nofduplicates)
+ end
+ end
rawdata.lookuphash={}
tfmdata=copytotfm(rawdata,cache_id)
if tfmdata and next(tfmdata) then
@@ -8341,13 +8641,14 @@ local function gref(descriptions,n)
return f_unicode(n)
end
elseif n then
- local num,nam={},{}
- for i=2,#n do
+ local num,nam,j={},{},0
+ for i=1,#n do
local ni=n[i]
if tonumber(ni) then
+ j=j+1
local di=descriptions[ni]
- num[i]=f_unicode(ni)
- nam[i]=di and di.name or "-"
+ num[j]=f_unicode(ni)
+ nam[j]=di and di.name or "-"
end
end
return f_unilist(num,nam)
@@ -8430,8 +8731,8 @@ local function finalize_ligatures(tfmdata,ligatures)
local ligature=ligatures[i]
if ligature then
local unicode,lookupdata=ligature[1],ligature[2]
- if trace then
- trace_ligatures_detail("building % a into %a",lookupdata,unicode)
+ if trace_ligatures_detail then
+ report_prepare("building % a into %a",lookupdata,unicode)
end
local size=#lookupdata
local firstcode=lookupdata[1]
@@ -8443,8 +8744,8 @@ local function finalize_ligatures(tfmdata,ligatures)
local firstdata=characters[firstcode]
if not firstdata then
firstcode=private
- if trace then
- trace_ligatures_detail("defining %a as %a",firstname,firstcode)
+ if trace_ligatures_detail then
+ report_prepare("defining %a as %a",firstname,firstcode)
end
unicodes[firstname]=firstcode
firstdata={ intermediate=true,ligatures={} }
@@ -8467,8 +8768,8 @@ local function finalize_ligatures(tfmdata,ligatures)
break
end
end
- if trace then
- trace_ligatures_detail("codes (%a,%a) + (%a,%a) -> %a",firstname,firstcode,secondname,secondcode,target)
+ if trace_ligatures_detail then
+ report_prepare("codes (%a,%a) + (%a,%a) -> %a",firstname,firstcode,secondname,secondcode,target)
end
local firstligs=firstdata.ligatures
if firstligs then
@@ -8479,6 +8780,8 @@ local function finalize_ligatures(tfmdata,ligatures)
firstcode=target
firstname=secondname
end
+ elseif trace_ligatures_detail then
+ report_prepare("no glyph (%a,%a) for building %a",firstname,firstcode,target)
end
if okay then
ligatures[i]=false
@@ -8488,12 +8791,14 @@ local function finalize_ligatures(tfmdata,ligatures)
end
alldone=done==0
end
- if trace then
- for k,v in next,characters do
- if v.ligatures then table.print(v,k) end
+ if trace_ligatures_detail then
+ for k,v in table.sortedhash(characters) do
+ if v.ligatures then
+ table.print(v,k)
+ end
end
end
- tfmdata.resources.private=private
+ resources.private=private
end
end
local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)
@@ -8712,7 +9017,7 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis
end
changed[unicode]=data
elseif lookuptype=="alternate" then
- local replacement=data[alternate]
+ local replacement=data[alternate]
if replacement then
changed[unicode]=replacement
if trace_alternatives then
@@ -8803,8 +9108,9 @@ basemethods.shared={
basemethod="independent"
local function featuresinitializer(tfmdata,value)
if true then
- local t=trace_preparing and os.clock()
+ local starttime=trace_preparing and os.clock()
local features=tfmdata.shared.features
+ local fullname=trace_preparing and tfmdata.properties.fullname
if features then
applybasemethod("initializehashes",tfmdata)
local collectlookups=otf.collectlookups
@@ -8814,26 +9120,34 @@ local function featuresinitializer(tfmdata,value)
local language=properties.language
local basesubstitutions=rawdata.resources.features.gsub
local basepositionings=rawdata.resources.features.gpos
- if basesubstitutions then
- for feature,data in next,basesubstitutions do
- local value=features[feature]
- if value then
- local validlookups,lookuplist=collectlookups(rawdata,feature,script,language)
- if validlookups then
- applybasemethod("preparesubstitutions",tfmdata,feature,value,validlookups,lookuplist)
- registerbasefeature(feature,value)
- end
- end
- end
- end
- if basepositionings then
- for feature,data in next,basepositionings do
- local value=features[feature]
- if value then
- local validlookups,lookuplist=collectlookups(rawdata,feature,script,language)
- if validlookups then
- applybasemethod("preparepositionings",tfmdata,feature,features[feature],validlookups,lookuplist)
- registerbasefeature(feature,value)
+ if basesubstitutions or basepositionings then
+ local sequences=tfmdata.resources.sequences
+ for s=1,#sequences do
+ local sequence=sequences[s]
+ local sfeatures=sequence.features
+ if sfeatures then
+ local order=sequence.order
+ if order then
+ for i=1,#order do
+ local feature=order[i]
+ if features[feature] then
+ local validlookups,lookuplist=collectlookups(rawdata,feature,script,language)
+ if not validlookups then
+ elseif basesubstitutions and basesubstitutions[feature] then
+ if trace_preparing then
+ report_prepare("filtering base feature %a for %a",feature,fullname)
+ end
+ applybasemethod("preparesubstitutions",tfmdata,feature,value,validlookups,lookuplist)
+ registerbasefeature(feature,value)
+ elseif basepositionings and basepositionings[feature] then
+ if trace_preparing then
+ report_prepare("filtering base feature %a for %a",feature,fullname)
+ end
+ applybasemethod("preparepositionings",tfmdata,feature,features[feature],validlookups,lookuplist)
+ registerbasefeature(feature,value)
+ end
+ end
+ end
end
end
end
@@ -8841,7 +9155,7 @@ local function featuresinitializer(tfmdata,value)
registerbasehash(tfmdata)
end
if trace_preparing then
- report_prepare("preparation time is %0.3f seconds for %a",os.clock()-t,tfmdata.properties.fullname)
+ report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,fullname)
end
end
end
@@ -8881,26 +9195,12 @@ nodes.injections=nodes.injections or {}
local injections=nodes.injections
local nodecodes=nodes.nodecodes
local glyph_code=nodecodes.glyph
-local disc_code=nodecodes.disc
local kern_code=nodecodes.kern
-local nuts=nodes.nuts
-local nodepool=nuts.pool
+local nodepool=nodes.pool
local newkern=nodepool.kern
-local tonode=nuts.tonode
-local tonut=nuts.tonut
-local getfield=nuts.getfield
-local getnext=nuts.getnext
-local getprev=nuts.getprev
-local getid=nuts.getid
-local getattr=nuts.getattr
-local getfont=nuts.getfont
-local getsubtype=nuts.getsubtype
-local getchar=nuts.getchar
-local setfield=nuts.setfield
-local setattr=nuts.setattr
-local traverse_id=nuts.traverse_id
-local insert_node_before=nuts.insert_before
-local insert_node_after=nuts.insert_after
+local traverse_id=node.traverse_id
+local insert_node_before=node.insert_before
+local insert_node_after=node.insert_after
local a_kernpair=attributes.private('kernpair')
local a_ligacomp=attributes.private('ligacomp')
local a_markbase=attributes.private('markbase')
@@ -8919,21 +9219,21 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne
local dx,dy=factor*(exit[1]-entry[1]),factor*(exit[2]-entry[2])
local ws,wn=tfmstart.width,tfmnext.width
local bound=#cursives+1
- setattr(start,a_cursbase,bound)
- setattr(nxt,a_curscurs,bound)
+ start[a_cursbase]=bound
+ nxt[a_curscurs]=bound
cursives[bound]={ rlmode,dx,dy,ws,wn }
return dx,dy,bound
end
function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr)
local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4]
if x~=0 or w~=0 or y~=0 or h~=0 then
- local bound=getattr(current,a_kernpair)
+ local bound=current[a_kernpair]
if bound then
local kb=kerns[bound]
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
- setattr(current,a_kernpair,bound)
+ current[a_kernpair]=bound
kerns[bound]={ rlmode,x,y,w,h,r2lflag,tfmchr.width }
end
return x,y,w,h,bound
@@ -8944,35 +9244,35 @@ function injections.setkern(current,factor,rlmode,x,tfmchr)
local dx=factor*x
if dx~=0 then
local bound=#kerns+1
- setattr(current,a_kernpair,bound)
+ current[a_kernpair]=bound
kerns[bound]={ rlmode,dx }
return dx,bound
else
return 0,0
end
end
-function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark)
- local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2])
- local bound=getattr(base,a_markbase)
+function injections.setmark(start,base,factor,rlmode,ba,ma)
+ local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2])
+ local bound=base[a_markbase]
local index=1
if bound then
local mb=marks[bound]
if mb then
index=#mb+1
mb[index]={ dx,dy,rlmode }
- setattr(start,a_markmark,bound)
- setattr(start,a_markdone,index)
+ start[a_markmark]=bound
+ start[a_markdone]=index
return dx,dy,bound
else
- report_injections("possible problem, %U is base mark without data (id %a)",getchar(base),bound)
+ report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound)
end
end
index=index or 1
bound=#marks+1
- setattr(base,a_markbase,bound)
- setattr(start,a_markmark,bound)
- setattr(start,a_markdone,index)
- marks[bound]={ [index]={ dx,dy,rlmode,baseismark } }
+ base[a_markbase]=bound
+ start[a_markmark]=bound
+ start[a_markdone]=index
+ marks[bound]={ [index]={ dx,dy,rlmode } }
return dx,dy,bound
end
local function dir(n)
@@ -8981,15 +9281,15 @@ end
local function trace(head)
report_injections("begin run")
for n in traverse_id(glyph_code,head) do
- if getsubtype(n)<256 then
- local kp=getattr(n,a_kernpair)
- local mb=getattr(n,a_markbase)
- local mm=getattr(n,a_markmark)
- local md=getattr(n,a_markdone)
- local cb=getattr(n,a_cursbase)
- local cc=getattr(n,a_curscurs)
- local char=getchar(n)
- report_injections("font %s, char %U, glyph %c",getfont(n),char,char)
+ if n.subtype<256 then
+ local kp=n[a_kernpair]
+ local mb=n[a_markbase]
+ local mm=n[a_markmark]
+ local md=n[a_markdone]
+ local cb=n[a_cursbase]
+ local cc=n[a_curscurs]
+ local char=n.char
+ report_injections("font %s, char %U, glyph %c",n.font,char,char)
if kp then
local k=kerns[kp]
if k[3] then
@@ -9030,23 +9330,21 @@ local function show_result(head)
local current=head
local skipping=false
while current do
- local id=getid(current)
+ local id=current.id
if id==glyph_code then
- report_injections("char: %C, width %p, xoffset %p, yoffset %p",
- getchar(current),getfield(current,"width"),getfield(current,"xoffset"),getfield(current,"yoffset"))
+ report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset)
skipping=false
elseif id==kern_code then
- report_injections("kern: %p",getfield(current,"kern"))
+ report_injections("kern: %p",current.kern)
skipping=false
elseif not skipping then
report_injections()
skipping=true
end
- current=getnext(current)
+ current=current.next
end
end
function injections.handler(head,where,keep)
- head=tonut(head)
local has_marks,has_cursives,has_kerns=next(marks),next(cursives),next(kerns)
if has_marks or has_cursives then
if trace_injections then
@@ -9056,18 +9354,17 @@ function injections.handler(head,where,keep)
if has_kerns then
local nf,tm=nil,nil
for n in traverse_id(glyph_code,head) do
- if getsubtype(n)<256 then
+ if n.subtype<256 then
nofvalid=nofvalid+1
valid[nofvalid]=n
- local f=getfont(n)
- if f~=nf then
- nf=f
- tm=fontdata[nf].resources.marks
+ if n.font~=nf then
+ nf=n.font
+ tm=fontdata[nf].resources.marks
end
if tm then
- mk[n]=tm[getchar(n)]
+ mk[n]=tm[n.char]
end
- local k=getattr(n,a_kernpair)
+ local k=n[a_kernpair]
if k then
local kk=kerns[k]
if kk then
@@ -9087,16 +9384,15 @@ function injections.handler(head,where,keep)
else
local nf,tm=nil,nil
for n in traverse_id(glyph_code,head) do
- if getsubtype(n)<256 then
+ if n.subtype<256 then
nofvalid=nofvalid+1
valid[nofvalid]=n
- local f=getfont(n)
- if f~=nf then
- nf=f
- tm=fontdata[nf].resources.marks
+ if n.font~=nf then
+ nf=n.font
+ tm=fontdata[nf].resources.marks
end
if tm then
- mk[n]=tm[getchar(n)]
+ mk[n]=tm[n.char]
end
end
end
@@ -9105,7 +9401,7 @@ function injections.handler(head,where,keep)
local cx={}
if has_kerns and next(ky) then
for n,k in next,ky do
- setfield(n,"yoffset",k)
+ n.yoffset=k
end
end
if has_cursives then
@@ -9114,9 +9410,9 @@ function injections.handler(head,where,keep)
for i=1,nofvalid do
local n=valid[i]
if not mk[n] then
- local n_cursbase=getattr(n,a_cursbase)
+ local n_cursbase=n[a_cursbase]
if p_cursbase then
- local n_curscurs=getattr(n,a_curscurs)
+ local n_curscurs=n[a_curscurs]
if p_cursbase==n_curscurs then
local c=cursives[n_curscurs]
if c then
@@ -9139,20 +9435,20 @@ function injections.handler(head,where,keep)
end
end
elseif maxt>0 then
- local ny=getfield(n,"yoffset")
+ local ny=n.yoffset
for i=maxt,1,-1 do
ny=ny+d[i]
local ti=t[i]
- setfield(ti,"yoffset",getfield(ti,"yoffset")+ny)
+ ti.yoffset=ti.yoffset+ny
end
maxt=0
end
if not n_cursbase and maxt>0 then
- local ny=getfield(n,"yoffset")
+ local ny=n.yoffset
for i=maxt,1,-1 do
ny=ny+d[i]
local ti=t[i]
- setfield(ti,"yoffset",ny)
+ ti.yoffset=ny
end
maxt=0
end
@@ -9160,11 +9456,11 @@ function injections.handler(head,where,keep)
end
end
if maxt>0 then
- local ny=getfield(n,"yoffset")
+ local ny=n.yoffset
for i=maxt,1,-1 do
ny=ny+d[i]
local ti=t[i]
- setfield(ti,"yoffset",ny)
+ ti.yoffset=ny
end
maxt=0
end
@@ -9175,66 +9471,57 @@ function injections.handler(head,where,keep)
if has_marks then
for i=1,nofvalid do
local p=valid[i]
- local p_markbase=getattr(p,a_markbase)
+ local p_markbase=p[a_markbase]
if p_markbase then
local mrks=marks[p_markbase]
local nofmarks=#mrks
- for n in traverse_id(glyph_code,getnext(p)) do
- local n_markmark=getattr(n,a_markmark)
+ for n in traverse_id(glyph_code,p.next) do
+ local n_markmark=n[a_markmark]
if p_markbase==n_markmark then
- local index=getattr(n,a_markdone) or 1
+ local index=n[a_markdone] or 1
local d=mrks[index]
if d then
local rlmode=d[3]
local k=wx[p]
- local px=getfield(p,"xoffset")
- local ox=0
if k then
local x=k[2]
local w=k[4]
if w then
if rlmode and rlmode>=0 then
- ox=px-getfield(p,"width")+d[1]-(w-x)
+ n.xoffset=p.xoffset-p.width+d[1]-(w-x)
else
- ox=px-d[1]-x
+ n.xoffset=p.xoffset-d[1]-x
end
else
if rlmode and rlmode>=0 then
- ox=px-getfield(p,"width")+d[1]
+ n.xoffset=p.xoffset-p.width+d[1]
else
- ox=px-d[1]-x
+ n.xoffset=p.xoffset-d[1]-x
end
end
else
- local wp=getfield(p,"width")
- local wn=getfield(n,"width")
if rlmode and rlmode>=0 then
- ox=px-wp+d[1]
+ n.xoffset=p.xoffset-p.width+d[1]
else
- ox=px-d[1]
+ n.xoffset=p.xoffset-d[1]
end
- if wn~=0 then
- insert_node_before(head,n,newkern(-wn/2))
- insert_node_after(head,n,newkern(-wn/2))
+ local w=n.width
+ if w~=0 then
+ insert_node_before(head,n,newkern(-w/2))
+ insert_node_after(head,n,newkern(-w/2))
end
end
- setfield(n,"xoffset",ox)
- local py=getfield(p,"yoffset")
- local oy=0
if mk[p] then
- oy=py+d[2]
+ n.yoffset=p.yoffset+d[2]
else
- oy=getfield(n,"yoffset")+py+d[2]
+ n.yoffset=n.yoffset+p.yoffset+d[2]
end
- setfield(n,"yoffset",oy)
if nofmarks==1 then
break
else
nofmarks=nofmarks-1
end
end
- elseif not n_markmark then
- break
else
end
end
@@ -9286,7 +9573,6 @@ function injections.handler(head,where,keep)
if not keep then
kerns={}
end
-head=tonode(head)
return head,true
elseif not keep then
kerns,cursives,marks={},{},{}
@@ -9296,14 +9582,14 @@ head=tonode(head)
trace(head)
end
for n in traverse_id(glyph_code,head) do
- if getsubtype(n)<256 then
- local k=getattr(n,a_kernpair)
+ if n.subtype<256 then
+ local k=n[a_kernpair]
if k then
local kk=kerns[k]
if kk then
local rl,x,y,w=kk[1],kk[2] or 0,kk[3],kk[4]
if y and y~=0 then
- setfield(n,"yoffset",y)
+ n.yoffset=y
end
if w then
local wx=w-x
@@ -9334,10 +9620,10 @@ head=tonode(head)
if not keep then
kerns={}
end
- return tonode(head),true
+ return head,true
else
end
- return tonode(head),false
+ return head,false
end
end -- closure
@@ -9511,6 +9797,7 @@ local isolated={
[0x0856]=true,[0x0858]=true,[0x0857]=true,
[0x07FA]=true,
[zwnj]=true,
+ [0x08AD]=true,
}
local final={
[0x0622]=true,[0x0623]=true,[0x0624]=true,[0x0625]=true,
@@ -9528,15 +9815,16 @@ local final={
[0x06D3]=true,[0x06D5]=true,[0x06EE]=true,[0x06EF]=true,
[0x0759]=true,[0x075A]=true,[0x075B]=true,[0x076B]=true,
[0x076C]=true,[0x0771]=true,[0x0773]=true,[0x0774]=true,
- [0x0778]=true,[0x0779]=true,
+ [0x0778]=true,[0x0779]=true,
[0x08AA]=true,[0x08AB]=true,[0x08AC]=true,
[0xFEF5]=true,[0xFEF7]=true,[0xFEF9]=true,[0xFEFB]=true,
- [0x0710]=true,[0x0715]=true,[0x0716]=true,[0x0717]=true,
- [0x0718]=true,[0x0719]=true,[0x0728]=true,[0x072A]=true,
- [0x072C]=true,[0x071E]=true,
+ [0x0710]=true,[0x0715]=true,[0x0716]=true,[0x0717]=true,
+ [0x0718]=true,[0x0719]=true,[0x0728]=true,[0x072A]=true,
+ [0x072C]=true,[0x071E]=true,
[0x072F]=true,[0x074D]=true,
[0x0840]=true,[0x0849]=true,[0x0854]=true,[0x0846]=true,
- [0x084F]=true
+ [0x084F]=true,
+ [0x08AE]=true,[0x08B1]=true,[0x08B2]=true,
}
local medial={
[0x0626]=true,[0x0628]=true,[0x062A]=true,[0x062B]=true,
@@ -9596,8 +9884,8 @@ local medial={
[0x07D2]=true,[0x07D0]=true,[0x07CF]=true,[0x07CD]=true,
[0x07CB]=true,[0x07D3]=true,[0x07E4]=true,[0x07D5]=true,
[0x07E6]=true,
- [tatweel]=true,
- [zwj]=true,
+ [tatweel]=true,[zwj]=true,
+ [0x08A1]=true,[0x08AF]=true,[0x08B0]=true,
}
local arab_warned={}
local function warning(current,what)
@@ -9752,25 +10040,12 @@ registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive")
registertracker("otf.actions","otf.replacements,otf.positions")
registertracker("otf.injections","nodes.injections")
registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing")
-local nuts=nodes.nuts
-local tonode=nuts.tonode
-local tonut=nuts.tonut
-local getfield=nuts.getfield
-local getnext=nuts.getnext
-local getprev=nuts.getprev
-local getid=nuts.getid
-local getattr=nuts.getattr
-local getfont=nuts.getfont
-local getsubtype=nuts.getsubtype
-local getchar=nuts.getchar
-local setfield=nuts.setfield
-local setattr=nuts.setattr
-local insert_node_after=nuts.insert_after
-local delete_node=nuts.delete
-local copy_node=nuts.copy
-local find_node_tail=nuts.tail
-local flush_node_list=nuts.flush_list
-local end_of_math=nuts.end_of_math
+local insert_node_after=node.insert_after
+local delete_node=nodes.delete
+local copy_node=node.copy
+local find_node_tail=node.tail or node.slide
+local flush_node_list=node.flush_list
+local end_of_math=node.end_of_math
local setmetatableindex=table.setmetatableindex
local zwnj=0x200C
local zwj=0x200D
@@ -9881,83 +10156,83 @@ local function pref(kind,lookupname)
return formatters["feature %a, lookup %a"](kind,lookupname)
end
local function copy_glyph(g)
- local components=getfield(g,"components")
+ local components=g.components
if components then
- setfield(g,"components",nil)
+ g.components=nil
local n=copy_node(g)
- setfield(g,"components",components)
+ g.components=components
return n
else
return copy_node(g)
end
end
local function markstoligature(kind,lookupname,head,start,stop,char)
- if start==stop and getchar(start)==char then
+ if start==stop and start.char==char then
return head,start
else
- local prev=getprev(start)
- local next=getnext(stop)
- setfield(start,"prev",nil)
- setfield(stop,"next",nil)
+ local prev=start.prev
+ local next=stop.next
+ start.prev=nil
+ stop.next=nil
local base=copy_glyph(start)
if head==start then
head=base
end
- setfield(base,"char",char)
- setfield(base,"subtype",ligature_code)
- setfield(base,"components",start)
+ base.char=char
+ base.subtype=ligature_code
+ base.components=start
if prev then
- setfield(prev,"next",base)
+ prev.next=base
end
if next then
- setfield(next,"prev",base)
+ next.prev=base
end
- setfield(base,"next",next)
- setfield(base,"prev",prev)
+ base.next=next
+ base.prev=prev
return head,base
end
end
local function getcomponentindex(start)
- if getid(start)~=glyph_code then
+ if start.id~=glyph_code then
return 0
- elseif getsubtype(start)==ligature_code then
+ elseif start.subtype==ligature_code then
local i=0
- local components=getfield(start,"components")
+ local components=start.components
while components do
i=i+getcomponentindex(components)
- components=getnext(components)
+ components=components.next
end
return i
- elseif not marks[getchar(start)] then
+ elseif not marks[start.char] then
return 1
else
return 0
end
end
local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound)
- if start==stop and getchar(start)==char then
- setfield(start,"char",char)
+ if start==stop and start.char==char then
+ start.char=char
return head,start
end
- local prev=getprev(start)
- local next=getnext(stop)
- setfield(start,"prev",nil)
- setfield(stop,"next",nil)
+ local prev=start.prev
+ local next=stop.next
+ start.prev=nil
+ stop.next=nil
local base=copy_glyph(start)
if start==head then
head=base
end
- setfield(base,"char",char)
- setfield(base,"subtype",ligature_code)
- setfield(base,"components",start)
+ base.char=char
+ base.subtype=ligature_code
+ base.components=start
if prev then
- setfield(prev,"next",base)
+ prev.next=base
end
if next then
- setfield(next,"prev",base)
+ next.prev=base
end
- setfield(base,"next",next)
- setfield(base,"prev",prev)
+ base.next=next
+ base.prev=prev
if not discfound then
local deletemarks=markflag~="mark"
local components=start
@@ -9966,42 +10241,42 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
local head=base
local current=base
while start do
- local char=getchar(start)
+ local char=start.char
if not marks[char] then
baseindex=baseindex+componentindex
componentindex=getcomponentindex(start)
elseif not deletemarks then
- setattr(start,a_ligacomp,baseindex+(getattr(start,a_ligacomp) or componentindex))
+ start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex)
if trace_marks then
- logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getattr(start,a_ligacomp))
+ logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp])
end
head,current=insert_node_after(head,current,copy_node(start))
elseif trace_marks then
logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))
end
- start=getnext(start)
+ start=start.next
end
- local start=getnext(current)
- while start and getid(start)==glyph_code do
- local char=getchar(start)
+ local start=current.next
+ while start and start.id==glyph_code do
+ local char=start.char
if marks[char] then
- setattr(start,a_ligacomp,baseindex+(getattr(start,a_ligacomp) or componentindex))
+ start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex)
if trace_marks then
- logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),getattr(start,a_ligacomp))
+ logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp])
end
else
break
end
- start=getnext(start)
+ start=start.next
end
end
return head,base
end
function handlers.gsub_single(head,start,kind,lookupname,replacement)
if trace_singles then
- logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement))
+ logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement))
end
- setfield(start,"char",replacement)
+ start.char=replacement
return head,start,true
end
local function get_alternative_glyph(start,alternatives,value,trace_alternatives)
@@ -10027,7 +10302,7 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives
return false,trace_alternatives and formatters["invalid value %a, %s"](value,"out of range")
end
elseif value==0 then
- return getchar(start),trace_alternatives and formatters["invalid value %a, %s"](value,"no change")
+ return start.char,trace_alternatives and formatters["invalid value %a, %s"](value,"no change")
elseif value<1 then
return alternatives[1],trace_alternatives and formatters["invalid value %a, taking %a"](value,1)
else
@@ -10038,25 +10313,25 @@ end
local function multiple_glyphs(head,start,multiple,ignoremarks)
local nofmultiples=#multiple
if nofmultiples>0 then
- setfield(start,"char",multiple[1])
+ start.char=multiple[1]
if nofmultiples>1 then
- local sn=getnext(start)
+ local sn=start.next
for k=2,nofmultiples do
local n=copy_node(start)
- setfield(n,"char",multiple[k])
- setfield(n,"next",sn)
- setfield(n,"prev",start)
+ n.char=multiple[k]
+ n.next=sn
+ n.prev=start
if sn then
- setfield(sn,"prev",n)
+ sn.prev=n
end
- setfield(start,"next",n)
+ start.next=n
start=n
end
end
return head,start,true
else
if trace_multiples then
- logprocess("no multiple for %s",gref(getchar(start)))
+ logprocess("no multiple for %s",gref(start.char))
end
return head,start,false
end
@@ -10066,34 +10341,34 @@ function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence
local choice,comment=get_alternative_glyph(start,alternative,value,trace_alternatives)
if choice then
if trace_alternatives then
- logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(getchar(start)),choice,gref(choice),comment)
+ logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment)
end
- setfield(start,"char",choice)
+ start.char=choice
else
if trace_alternatives then
- logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(getchar(start)),comment)
+ logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment)
end
end
return head,start,true
end
function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence)
if trace_multiples then
- logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(getchar(start)),gref(multiple))
+ logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple))
end
return multiple_glyphs(head,start,multiple,sequence.flags[1])
end
function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
- local s,stop,discfound=getnext(start),nil,false
- local startchar=getchar(start)
+ local s,stop,discfound=start.next,nil,false
+ local startchar=start.char
if marks[startchar] then
while s do
- local id=getid(s)
- if id==glyph_code and getfont(s)==currentfont and getsubtype(s)<256 then
- local lg=ligature[getchar(s)]
+ local id=s.id
+ if id==glyph_code and s.font==currentfont and s.subtype<256 then
+ local lg=ligature[s.char]
if lg then
stop=s
ligature=lg
- s=getnext(s)
+ s=s.next
else
break
end
@@ -10105,9 +10380,9 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
local lig=ligature.ligature
if lig then
if trace_ligatures then
- local stopchar=getchar(stop)
+ local stopchar=stop.char
head,start=markstoligature(kind,lookupname,head,start,stop,lig)
- logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(getchar(start)))
+ logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char))
else
head,start=markstoligature(kind,lookupname,head,start,stop,lig)
end
@@ -10118,18 +10393,18 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
else
local skipmark=sequence.flags[1]
while s do
- local id=getid(s)
- if id==glyph_code and getsubtype(s)<256 then
- if getfont(s)==currentfont then
- local char=getchar(s)
+ local id=s.id
+ if id==glyph_code and s.subtype<256 then
+ if s.font==currentfont then
+ local char=s.char
if skipmark and marks[char] then
- s=getnext(s)
+ s=s.next
else
local lg=ligature[char]
if lg then
stop=s
ligature=lg
- s=getnext(s)
+ s=s.next
else
break
end
@@ -10139,7 +10414,7 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
end
elseif id==disc_code then
discfound=true
- s=getnext(s)
+ s=s.next
else
break
end
@@ -10148,35 +10423,36 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
if lig then
if stop then
if trace_ligatures then
- local stopchar=getchar(stop)
+ local stopchar=stop.char
head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)
- logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(getchar(start)))
+ logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char))
else
head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)
end
+ return head,start,true
else
- setfield(start,"char",lig)
+ start.char=lig
if trace_ligatures then
logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig))
end
+ return head,start,true
end
- return head,start,true
else
end
end
return head,start,false
end
function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence)
- local markchar=getchar(start)
+ local markchar=start.char
if marks[markchar] then
- local base=getprev(start)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- local basechar=getchar(base)
+ local base=start.prev
+ if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
+ local basechar=base.char
if marks[basechar] then
while true do
- base=getprev(base)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- basechar=getchar(base)
+ base=base.prev
+ if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
+ basechar=base.char
if not marks[basechar] then
break
end
@@ -10225,16 +10501,16 @@ function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence
return head,start,false
end
function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence)
- local markchar=getchar(start)
+ local markchar=start.char
if marks[markchar] then
- local base=getprev(start)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- local basechar=getchar(base)
+ local base=start.prev
+ if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
+ local basechar=base.char
if marks[basechar] then
while true do
- base=getprev(base)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- basechar=getchar(base)
+ base=base.prev
+ if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
+ basechar=base.char
if not marks[basechar] then
break
end
@@ -10246,7 +10522,7 @@ function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequ
end
end
end
- local index=getattr(start,a_ligacomp)
+ local index=start[a_ligacomp]
local baseanchors=descriptions[basechar]
if baseanchors then
baseanchors=baseanchors.anchors
@@ -10291,22 +10567,22 @@ function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequ
return head,start,false
end
function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence)
- local markchar=getchar(start)
+ local markchar=start.char
if marks[markchar] then
- local base=getprev(start)
- local slc=getattr(start,a_ligacomp)
+ local base=start.prev
+ local slc=start[a_ligacomp]
if slc then
while base do
- local blc=getattr(base,a_ligacomp)
+ local blc=base[a_ligacomp]
if blc and blc~=slc then
- base=getprev(base)
+ base=base.prev
else
break
end
end
end
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- local basechar=getchar(base)
+ if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
+ local basechar=base.char
local baseanchors=descriptions[basechar]
if baseanchors then
baseanchors=baseanchors.anchors
@@ -10344,20 +10620,20 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence
return head,start,false
end
function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)
- local alreadydone=cursonce and getattr(start,a_cursbase)
+ local alreadydone=cursonce and start[a_cursbase]
if not alreadydone then
local done=false
- local startchar=getchar(start)
+ local startchar=start.char
if marks[startchar] then
if trace_cursive then
logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
end
else
- local nxt=getnext(start)
- while not done and nxt and getid(nxt)==glyph_code and getfont(nxt)==currentfont and getsubtype(nxt)<256 do
- local nextchar=getchar(nxt)
+ local nxt=start.next
+ while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do
+ local nextchar=nxt.char
if marks[nextchar] then
- nxt=getnext(nxt)
+ nxt=nxt.next
else
local entryanchors=descriptions[nextchar]
if entryanchors then
@@ -10391,13 +10667,13 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)
return head,start,done
else
if trace_cursive and trace_details then
- logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(getchar(start)),alreadydone)
+ logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)
end
return head,start,false
end
end
function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence)
- local startchar=getchar(start)
+ local startchar=start.char
local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
if trace_kerns then
logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h)
@@ -10405,33 +10681,33 @@ function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence)
return head,start,false
end
function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)
- local snext=getnext(start)
+ local snext=start.next
if not snext then
return head,start,false
else
local prev,done=start,false
local factor=tfmdata.parameters.factor
local lookuptype=lookuptypes[lookupname]
- while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do
- local nextchar=getchar(snext)
+ while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do
+ local nextchar=snext.char
local krn=kerns[nextchar]
if not krn and marks[nextchar] then
prev=snext
- snext=getnext(snext)
+ snext=snext.next
else
if not krn then
elseif type(krn)=="table" then
if lookuptype=="pair" then
local a,b=krn[2],krn[3]
if a and #a>0 then
- local startchar=getchar(start)
+ local startchar=start.char
local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
if trace_kerns then
logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
end
end
if b and #b>0 then
- local startchar=getchar(start)
+ local startchar=start.char
local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
if trace_kerns then
logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
@@ -10444,7 +10720,7 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)
elseif krn~=0 then
local k=setkern(snext,factor,rlmode,krn)
if trace_kerns then
- logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar))
+ logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar))
end
done=true
end
@@ -10479,13 +10755,13 @@ function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,looku
return head,start,false
end
function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements)
- local char=getchar(start)
+ local char=start.char
local replacement=replacements[char]
if replacement then
if trace_singles then
logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement))
end
- setfield(start,"char",replacement)
+ start.char=replacement
return head,start,true
else
return head,start,false
@@ -10498,8 +10774,8 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," "))
end
while current do
- if getid(current)==glyph_code then
- local currentchar=getchar(current)
+ if current.id==glyph_code then
+ local currentchar=current.char
local lookupname=subtables[1]
local replacement=lookuphash[lookupname]
if not replacement then
@@ -10516,21 +10792,21 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
if trace_singles then
logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement))
end
- setfield(current,"char",replacement)
+ current.char=replacement
end
end
return head,start,true
elseif current==stop then
break
else
- current=getnext(current)
+ current=current.next
end
end
return head,start,false
end
chainmores.gsub_single=chainprocs.gsub_single
function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
- local startchar=getchar(start)
+ local startchar=start.char
local subtables=currentlookup.subtables
local lookupname=subtables[1]
local replacements=lookuphash[lookupname]
@@ -10559,8 +10835,8 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext
local subtables=currentlookup.subtables
local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue
while current do
- if getid(current)==glyph_code then
- local currentchar=getchar(current)
+ if current.id==glyph_code then
+ local currentchar=current.char
local lookupname=subtables[1]
local alternatives=lookuphash[lookupname]
if not alternatives then
@@ -10575,7 +10851,7 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext
if trace_alternatives then
logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment)
end
- setfield(start,"char",choice)
+ start.char=choice
else
if trace_alternatives then
logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment)
@@ -10589,14 +10865,14 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext
elseif current==stop then
break
else
- current=getnext(current)
+ current=current.next
end
end
return head,start,false
end
chainmores.gsub_alternate=chainprocs.gsub_alternate
function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex)
- local startchar=getchar(start)
+ local startchar=start.char
local subtables=currentlookup.subtables
local lookupname=subtables[1]
local ligatures=lookuphash[lookupname]
@@ -10611,20 +10887,20 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
end
else
- local s=getnext(start)
+ local s=start.next
local discfound=false
local last=stop
local nofreplacements=0
local skipmark=currentlookup.flags[1]
while s do
- local id=getid(s)
+ local id=s.id
if id==disc_code then
- s=getnext(s)
+ s=s.next
discfound=true
else
- local schar=getchar(s)
+ local schar=s.char
if skipmark and marks[schar] then
- s=getnext(s)
+ s=s.next
else
local lg=ligatures[schar]
if lg then
@@ -10632,7 +10908,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
if s==stop then
break
else
- s=getnext(s)
+ s=s.next
end
else
break
@@ -10649,7 +10925,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
if start==stop then
logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2))
else
- logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(getchar(stop)),gref(l2))
+ logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2))
end
end
head,start=toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound)
@@ -10658,7 +10934,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
if start==stop then
logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
else
- logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(getchar(stop)))
+ logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char))
end
end
end
@@ -10667,7 +10943,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
end
chainmores.gsub_ligature=chainprocs.gsub_ligature
function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
- local markchar=getchar(start)
+ local markchar=start.char
if marks[markchar] then
local subtables=currentlookup.subtables
local lookupname=subtables[1]
@@ -10676,14 +10952,14 @@ function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext
markanchors=markanchors[markchar]
end
if markanchors then
- local base=getprev(start)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- local basechar=getchar(base)
+ local base=start.prev
+ if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
+ local basechar=base.char
if marks[basechar] then
while true do
- base=getprev(base)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- basechar=getchar(base)
+ base=base.prev
+ if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
+ basechar=base.char
if not marks[basechar] then
break
end
@@ -10730,7 +11006,7 @@ function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext
return head,start,false
end
function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
- local markchar=getchar(start)
+ local markchar=start.char
if marks[markchar] then
local subtables=currentlookup.subtables
local lookupname=subtables[1]
@@ -10739,14 +11015,14 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon
markanchors=markanchors[markchar]
end
if markanchors then
- local base=getprev(start)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- local basechar=getchar(base)
+ local base=start.prev
+ if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
+ local basechar=base.char
if marks[basechar] then
while true do
- base=getprev(base)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- basechar=getchar(base)
+ base=base.prev
+ if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
+ basechar=base.char
if not marks[basechar] then
break
end
@@ -10758,7 +11034,7 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon
end
end
end
- local index=getattr(start,a_ligacomp)
+ local index=start[a_ligacomp]
local baseanchors=descriptions[basechar].anchors
if baseanchors then
local baseanchors=baseanchors['baselig']
@@ -10797,7 +11073,7 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon
return head,start,false
end
function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
- local markchar=getchar(start)
+ local markchar=start.char
if marks[markchar] then
local subtables=currentlookup.subtables
local lookupname=subtables[1]
@@ -10806,20 +11082,20 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext
markanchors=markanchors[markchar]
end
if markanchors then
- local base=getprev(start)
- local slc=getattr(start,a_ligacomp)
+ local base=start.prev
+ local slc=start[a_ligacomp]
if slc then
while base do
- local blc=getattr(base,a_ligacomp)
+ local blc=base[a_ligacomp]
if blc and blc~=slc then
- base=getprev(base)
+ base=base.prev
else
break
end
end
end
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- local basechar=getchar(base)
+ if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
+ local basechar=base.char
local baseanchors=descriptions[basechar].anchors
if baseanchors then
baseanchors=baseanchors['basemark']
@@ -10855,9 +11131,9 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext
return head,start,false
end
function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
- local alreadydone=cursonce and getattr(start,a_cursbase)
+ local alreadydone=cursonce and start[a_cursbase]
if not alreadydone then
- local startchar=getchar(start)
+ local startchar=start.char
local subtables=currentlookup.subtables
local lookupname=subtables[1]
local exitanchors=lookuphash[lookupname]
@@ -10871,11 +11147,11 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l
logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
end
else
- local nxt=getnext(start)
- while not done and nxt and getid(nxt)==glyph_code and getfont(nxt)==currentfont and getsubtype(nxt)<256 do
- local nextchar=getchar(nxt)
+ local nxt=start.next
+ while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do
+ local nextchar=nxt.char
if marks[nextchar] then
- nxt=getnext(nxt)
+ nxt=nxt.next
else
local entryanchors=descriptions[nextchar]
if entryanchors then
@@ -10909,7 +11185,7 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l
return head,start,done
else
if trace_cursive and trace_details then
- logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(getchar(start)),alreadydone)
+ logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)
end
return head,start,false
end
@@ -10917,7 +11193,7 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l
return head,start,false
end
function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
- local startchar=getchar(start)
+ local startchar=start.char
local subtables=currentlookup.subtables
local lookupname=subtables[1]
local kerns=lookuphash[lookupname]
@@ -10934,9 +11210,9 @@ function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lo
end
chainmores.gpos_single=chainprocs.gpos_single
function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
- local snext=getnext(start)
+ local snext=start.next
if snext then
- local startchar=getchar(start)
+ local startchar=start.char
local subtables=currentlookup.subtables
local lookupname=subtables[1]
local kerns=lookuphash[lookupname]
@@ -10946,26 +11222,26 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look
local lookuptype=lookuptypes[lookupname]
local prev,done=start,false
local factor=tfmdata.parameters.factor
- while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do
- local nextchar=getchar(snext)
+ while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do
+ local nextchar=snext.char
local krn=kerns[nextchar]
if not krn and marks[nextchar] then
prev=snext
- snext=getnext(snext)
+ snext=snext.next
else
if not krn then
elseif type(krn)=="table" then
if lookuptype=="pair" then
local a,b=krn[2],krn[3]
if a and #a>0 then
- local startchar=getchar(start)
+ local startchar=start.char
local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
if trace_kerns then
logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
end
end
if b and #b>0 then
- local startchar=getchar(start)
+ local startchar=start.char
local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
if trace_kerns then
logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
@@ -10977,7 +11253,7 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look
if a and a~=0 then
local k=setkern(snext,factor,rlmode,a)
if trace_kerns then
- logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar))
+ logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))
end
end
if b and b~=0 then
@@ -10988,7 +11264,7 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look
elseif krn~=0 then
local k=setkern(snext,factor,rlmode,krn)
if trace_kerns then
- logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar))
+ logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))
end
done=true
end
@@ -11009,10 +11285,6 @@ local function show_skip(kind,chainname,char,ck,class)
logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])
end
end
-local quit_on_no_replacement=true
-directives.register("otf.chain.quitonnoreplacement",function(value)
- quit_on_no_replacement=value
-end)
local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash)
local flags=sequence.flags
local done=false
@@ -11030,7 +11302,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
local seq=ck[3]
local s=#seq
if s==1 then
- match=getid(current)==glyph_code and getfont(current)==currentfont and getsubtype(current)<256 and seq[1][getchar(current)]
+ match=current.id==glyph_code and current.font==currentfont and current.subtype<256 and seq[1][current.char]
else
local f,l=ck[4],ck[5]
if f==1 and f==l then
@@ -11038,13 +11310,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
if f==l then
else
local n=f+1
- last=getnext(last)
+ last=last.next
while n<=l do
if last then
- local id=getid(last)
+ local id=last.id
if id==glyph_code then
- if getfont(last)==currentfont and getsubtype(last)<256 then
- local char=getchar(last)
+ if last.font==currentfont and last.subtype<256 then
+ local char=last.char
local ccd=descriptions[char]
if ccd then
local class=ccd.class
@@ -11053,10 +11325,10 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
if trace_skips then
show_skip(kind,chainname,char,ck,class)
end
- last=getnext(last)
+ last=last.next
elseif seq[n][char] then
if n<l then
- last=getnext(last)
+ last=last.next
end
n=n+1
else
@@ -11072,7 +11344,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
break
end
elseif id==disc_code then
- last=getnext(last)
+ last=last.next
else
match=false
break
@@ -11085,15 +11357,15 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
end
if match and f>1 then
- local prev=getprev(start)
+ local prev=start.prev
if prev then
local n=f-1
while n>=1 do
if prev then
- local id=getid(prev)
+ local id=prev.id
if id==glyph_code then
- if getfont(prev)==currentfont and getsubtype(prev)<256 then
- local char=getchar(prev)
+ if prev.font==currentfont and prev.subtype<256 then
+ local char=prev.char
local ccd=descriptions[char]
if ccd then
local class=ccd.class
@@ -11123,7 +11395,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
match=false
break
end
- prev=getprev(prev)
+ prev=prev.prev
elseif seq[n][32] then
n=n -1
else
@@ -11143,15 +11415,15 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
end
if match and s>l then
- local current=last and getnext(last)
+ local current=last and last.next
if current then
local n=l+1
while n<=s do
if current then
- local id=getid(current)
+ local id=current.id
if id==glyph_code then
- if getfont(current)==currentfont and getsubtype(current)<256 then
- local char=getchar(current)
+ if current.font==currentfont and current.subtype<256 then
+ local char=current.char
local ccd=descriptions[char]
if ccd then
local class=ccd.class
@@ -11181,7 +11453,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
match=false
break
end
- current=getnext(current)
+ current=current.next
elseif seq[n][32] then
n=n+1
else
@@ -11204,7 +11476,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
if match then
if trace_contexts then
local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5]
- local char=getchar(start)
+ local char=start.char
if ck[9] then
logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a",
cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10])
@@ -11238,12 +11510,12 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
repeat
if skipped then
while true do
- local char=getchar(start)
+ 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=getnext(start)
+ start=start.next
else
break
end
@@ -11273,7 +11545,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
end
if start then
- start=getnext(start)
+ start=start.next
else
end
until i>nofchainlookups
@@ -11283,7 +11555,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
if replacements then
head,start,done=chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements)
else
- done=quit_on_no_replacement
+ done=true
if trace_contexts then
logprocess("%s: skipping match",cref(kind,chainname))
end
@@ -11352,14 +11624,20 @@ local autofeatures=fonts.analyzers.features
local function initialize(sequence,script,language,enabled)
local features=sequence.features
if features then
- for kind,scripts in next,features do
- local valid=enabled[kind]
- if valid then
- local languages=scripts[script] or scripts[wildcard]
- if languages and (languages[language] or languages[wildcard]) then
- return { valid,autofeatures[kind] or false,sequence.chain or 0,kind,sequence }
+ local order=sequence.order
+ if order then
+ for i=1,#order do
+ local kind=order[i]
+ local valid=enabled[kind]
+ if valid then
+ local scripts=features[kind]
+ local languages=scripts[script] or scripts[wildcard]
+ if languages and (languages[language] or languages[wildcard]) then
+ return { valid,autofeatures[kind] or false,sequence.chain or 0,kind,sequence }
+ end
end
end
+ else
end
end
return false
@@ -11386,12 +11664,12 @@ function otf.dataset(tfmdata,font)
}
rs[language]=rl
local sequences=tfmdata.resources.sequences
-for s=1,#sequences do
- local v=enabled and initialize(sequences[s],script,language,enabled)
- if v then
- rl[#rl+1]=v
- end
-end
+ for s=1,#sequences do
+ local v=enabled and initialize(sequences[s],script,language,enabled)
+ if v then
+ rl[#rl+1]=v
+ end
+ end
end
return rl
end
@@ -11400,7 +11678,6 @@ local function featuresprocessor(head,font,attr)
if not lookuphash then
return head,false
end
- head=tonut(head)
if trace_steps then
checkstep(head)
end
@@ -11433,10 +11710,10 @@ local function featuresprocessor(head,font,attr)
local handler=handlers[typ]
local start=find_node_tail(head)
while start do
- local id=getid(start)
+ local id=start.id
if id==glyph_code then
- if getfont(start)==font and getsubtype(start)<256 then
- local a=getattr(start,0)
+ if start.font==font and start.subtype<256 then
+ local a=start[0]
if a then
a=a==attr
else
@@ -11447,7 +11724,7 @@ local function featuresprocessor(head,font,attr)
local lookupname=subtables[i]
local lookupcache=lookuphash[lookupname]
if lookupcache then
- local lookupmatch=lookupcache[getchar(start)]
+ local lookupmatch=lookupcache[start.char]
if lookupmatch then
head,start,success=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
if success then
@@ -11458,15 +11735,15 @@ local function featuresprocessor(head,font,attr)
report_missing_cache(typ,lookupname)
end
end
- if start then start=getprev(start) end
+ if start then start=start.prev end
else
- start=getprev(start)
+ start=start.prev
end
else
- start=getprev(start)
+ start=start.prev
end
else
- start=getprev(start)
+ start=start.prev
end
end
else
@@ -11484,16 +11761,16 @@ local function featuresprocessor(head,font,attr)
local head=start
local done=false
while start do
- local id=getid(start)
- if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
- local a=getattr(start,0)
+ local id=start.id
+ if id==glyph_code and start.font==font and start.subtype<256 then
+ local a=start[0]
if a then
- a=(a==attr) and (not attribute or getattr(start,a_state)==attribute)
+ a=(a==attr) and (not attribute or start[a_state]==attribute)
else
- a=not attribute or getattr(start,a_state)==attribute
+ a=not attribute or start[a_state]==attribute
end
if a then
- local lookupmatch=lookupcache[getchar(start)]
+ local lookupmatch=lookupcache[start.char]
if lookupmatch then
local ok
head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
@@ -11501,12 +11778,12 @@ local function featuresprocessor(head,font,attr)
done=true
end
end
- if start then start=getnext(start) end
+ if start then start=start.next end
else
- start=getnext(start)
+ start=start.next
end
else
- start=getnext(start)
+ start=start.next
end
end
if done then
@@ -11515,18 +11792,18 @@ local function featuresprocessor(head,font,attr)
end
end
local function kerndisc(disc)
- local prev=getprev(disc)
- local next=getnext(disc)
+ local prev=disc.prev
+ local next=disc.next
if prev and next then
- setfield(prev,"next",next)
- local a=getattr(prev,0)
+ prev.next=next
+ local a=prev[0]
if a then
- a=(a==attr) and (not attribute or getattr(prev,a_state)==attribute)
+ a=(a==attr) and (not attribute or prev[a_state]==attribute)
else
- a=not attribute or getattr(prev,a_state)==attribute
+ a=not attribute or prev[a_state]==attribute
end
if a then
- local lookupmatch=lookupcache[getchar(prev)]
+ local lookupmatch=lookupcache[prev.char]
if lookupmatch then
local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
if ok then
@@ -11535,22 +11812,22 @@ local function featuresprocessor(head,font,attr)
end
end
end
- setfield(prev,"next",disc)
+ prev.next=disc
end
return next
end
while start do
- local id=getid(start)
+ local id=start.id
if id==glyph_code then
- if getfont(start)==font and getsubtype(start)<256 then
- local a=getattr(start,0)
+ if start.font==font and start.subtype<256 then
+ local a=start[0]
if a then
- a=(a==attr) and (not attribute or getattr(start,a_state)==attribute)
+ a=(a==attr) and (not attribute or start[a_state]==attribute)
else
- a=not attribute or getattr(start,a_state)==attribute
+ a=not attribute or start[a_state]==attribute
end
if a then
- local lookupmatch=lookupcache[getchar(start)]
+ local lookupmatch=lookupcache[start.char]
if lookupmatch then
local ok
head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
@@ -11558,38 +11835,38 @@ local function featuresprocessor(head,font,attr)
success=true
end
end
- if start then start=getnext(start) end
+ if start then start=start.next end
else
- start=getnext(start)
+ start=start.next
end
else
- start=getnext(start)
+ start=start.next
end
elseif id==disc_code then
- if getsubtype(start)==discretionary_code then
- local pre=getfield(start,"pre")
+ if start.subtype==discretionary_code then
+ local pre=start.pre
if pre then
local new=subrun(pre)
- if new then setfield(start,"pre",new) end
+ if new then start.pre=new end
end
- local post=getfield(start,"post")
+ local post=start.post
if post then
local new=subrun(post)
- if new then setfield(start,"post",new) end
+ if new then start.post=new end
end
- local replace=getfield(start,"replace")
+ local replace=start.replace
if replace then
local new=subrun(replace)
- if new then setfield(start,"replace",new) end
+ if new then start.replace=new end
end
elseif typ=="gpos_single" or typ=="gpos_pair" then
kerndisc(start)
end
- start=getnext(start)
+ start=start.next
elseif id==whatsit_code then
- local subtype=getsubtype(start)
+ local subtype=start.subtype
if subtype==dir_code then
- local dir=getfield(start,"dir")
+ local dir=start.dir
if dir=="+TRT" or dir=="+TLT" then
topstack=topstack+1
dirstack[topstack]=dir
@@ -11608,7 +11885,7 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
end
elseif subtype==localpar_code then
- local dir=getfield(start,"dir")
+ local dir=start.dir
if dir=="TRT" then
rlparmode=-1
elseif dir=="TLT" then
@@ -11621,11 +11898,11 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
end
end
- start=getnext(start)
+ start=start.next
elseif id==math_code then
- start=getnext(end_of_math(start))
+ start=end_of_math(start).next
else
- start=getnext(start)
+ start=start.next
end
end
end
@@ -11634,20 +11911,20 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
local head=start
local done=false
while start do
- local id=getid(start)
- if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
- local a=getattr(start,0)
+ local id=start.id
+ if id==glyph_code and start.id==font and start.subtype<256 then
+ local a=start[0]
if a then
- a=(a==attr) and (not attribute or getattr(start,a_state)==attribute)
+ a=(a==attr) and (not attribute or start[a_state]==attribute)
else
- a=not attribute or getattr(start,a_state)==attribute
+ a=not attribute or start[a_state]==attribute
end
if a then
for i=1,ns do
local lookupname=subtables[i]
local lookupcache=lookuphash[lookupname]
if lookupcache then
- local lookupmatch=lookupcache[getchar(start)]
+ local lookupmatch=lookupcache[start.char]
if lookupmatch then
local ok
head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
@@ -11662,12 +11939,12 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
report_missing_cache(typ,lookupname)
end
end
- if start then start=getnext(start) end
+ if start then start=start.next end
else
- start=getnext(start)
+ start=start.next
end
else
- start=getnext(start)
+ start=start.next
end
end
if done then
@@ -11676,22 +11953,22 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
end
end
local function kerndisc(disc)
- local prev=getprev(disc)
- local next=getnext(disc)
+ local prev=disc.prev
+ local next=disc.next
if prev and next then
- setfield(prev,"next",next)
- local a=getattr(prev,0)
+ prev.next=next
+ local a=prev[0]
if a then
- a=(a==attr) and (not attribute or getattr(prev,a_state)==attribute)
+ a=(a==attr) and (not attribute or prev[a_state]==attribute)
else
- a=not attribute or getattr(prev,a_state)==attribute
+ a=not attribute or prev[a_state]==attribute
end
if a then
for i=1,ns do
local lookupname=subtables[i]
local lookupcache=lookuphash[lookupname]
if lookupcache then
- local lookupmatch=lookupcache[getchar(prev)]
+ local lookupmatch=lookupcache[prev.char]
if lookupmatch then
local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
if ok then
@@ -11704,26 +11981,26 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
end
end
end
- setfield(prev,"next",disc)
+ prev.next=disc
end
return next
end
while start do
- local id=getid(start)
+ local id=start.id
if id==glyph_code then
- if getfont(start)==font and getsubtype(start)<256 then
- local a=getattr(start,0)
+ if start.font==font and start.subtype<256 then
+ local a=start[0]
if a then
- a=(a==attr) and (not attribute or getattr(start,a_state)==attribute)
+ a=(a==attr) and (not attribute or start[a_state]==attribute)
else
- a=not attribute or getattr(start,a_state)==attribute
+ a=not attribute or start[a_state]==attribute
end
if a then
for i=1,ns do
local lookupname=subtables[i]
local lookupcache=lookuphash[lookupname]
if lookupcache then
- local lookupmatch=lookupcache[getchar(start)]
+ local lookupmatch=lookupcache[start.char]
if lookupmatch then
local ok
head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
@@ -11738,38 +12015,38 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
report_missing_cache(typ,lookupname)
end
end
- if start then start=getnext(start) end
+ if start then start=start.next end
else
- start=getnext(start)
+ start=start.next
end
else
- start=getnext(start)
+ start=start.next
end
elseif id==disc_code then
- if getsubtype(start)==discretionary_code then
- local pre=getfield(start,"pre")
+ if start.subtype==discretionary_code then
+ local pre=start.pre
if pre then
local new=subrun(pre)
- if new then setfield(start,"pre",new) end
+ if new then start.pre=new end
end
- local post=getfield(start,"post")
+ local post=start.post
if post then
local new=subrun(post)
- if new then setfield(start,"post",new) end
+ if new then start.post=new end
end
- local replace=getfield(start,"replace")
+ local replace=start.replace
if replace then
local new=subrun(replace)
- if new then setfield(start,"replace",new) end
+ if new then start.replace=new end
end
elseif typ=="gpos_single" or typ=="gpos_pair" then
kerndisc(start)
end
- start=getnext(start)
+ start=start.next
elseif id==whatsit_code then
- local subtype=getsubtype(start)
+ local subtype=start.subtype
if subtype==dir_code then
- local dir=getfield(start,"dir")
+ local dir=start.dir
if dir=="+TRT" or dir=="+TLT" then
topstack=topstack+1
dirstack[topstack]=dir
@@ -11788,7 +12065,7 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
end
elseif subtype==localpar_code then
- local dir=getfield(start,"dir")
+ local dir=start.dir
if dir=="TRT" then
rlparmode=-1
elseif dir=="TLT" then
@@ -11801,11 +12078,11 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
end
end
- start=getnext(start)
+ start=start.next
elseif id==math_code then
- start=getnext(end_of_math(start))
+ start=end_of_math(start).next
else
- start=getnext(start)
+ start=start.next
end
end
end
@@ -11817,7 +12094,6 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
registerstep(head)
end
end
- head=tonode(head)
return head,done
end
local function generic(lookupdata,lookupname,unicode,lookuphash)
@@ -12420,6 +12696,14 @@ local function packdata(data)
features[script]=pack_normal(feature)
end
end
+ local order=sequence.order
+ if order then
+ sequence.order=pack_indexed(order)
+ end
+ local markclass=sequence.markclass
+ if markclass then
+ sequence.markclass=pack_boolean(markclass)
+ end
end
end
local lookups=resources.lookups
@@ -12832,6 +13116,20 @@ local function unpackdata(data)
end
end
end
+ local order=feature.order
+ if order then
+ local tv=tables[order]
+ if tv then
+ feature.order=tv
+ end
+ end
+ local markclass=feature.markclass
+ if markclass then
+ local tv=tables[markclass]
+ if tv then
+ feature.markclass=tv
+ end
+ end
end
end
local lookups=resources.lookups
diff --git a/luaotfload-fonts-cbk.lua b/src/luaotfload-fonts-cbk.lua
index 9db94f6..9db94f6 100644
--- a/luaotfload-fonts-cbk.lua
+++ b/src/luaotfload-fonts-cbk.lua
diff --git a/luaotfload-fonts-def.lua b/src/luaotfload-fonts-def.lua
index 0c2f0db..0c2f0db 100644
--- a/luaotfload-fonts-def.lua
+++ b/src/luaotfload-fonts-def.lua
diff --git a/luaotfload-fonts-enc.lua b/src/luaotfload-fonts-enc.lua
index e20c3a0..e20c3a0 100644
--- a/luaotfload-fonts-enc.lua
+++ b/src/luaotfload-fonts-enc.lua
diff --git a/luaotfload-fonts-ext.lua b/src/luaotfload-fonts-ext.lua
index b60d045..b60d045 100644
--- a/luaotfload-fonts-ext.lua
+++ b/src/luaotfload-fonts-ext.lua
diff --git a/src/luaotfload-fonts-inj.lua b/src/luaotfload-fonts-inj.lua
new file mode 100644
index 0000000..ae48150
--- /dev/null
+++ b/src/luaotfload-fonts-inj.lua
@@ -0,0 +1,526 @@
+if not modules then modules = { } end modules ['node-inj'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- This is very experimental (this will change when we have luatex > .50 and
+-- a few pending thingies are available. Also, Idris needs to make a few more
+-- test fonts. Btw, future versions of luatex will have extended glyph properties
+-- that can be of help. Some optimizations can go away when we have faster machines.
+
+-- todo: make a special one for context
+
+local next = next
+local utfchar = utf.char
+
+local trace_injections = false trackers.register("nodes.injections", function(v) trace_injections = v end)
+
+local report_injections = logs.reporter("nodes","injections")
+
+local attributes, nodes, node = attributes, nodes, node
+
+fonts = fonts
+local fontdata = fonts.hashes.identifiers
+
+nodes.injections = nodes.injections or { }
+local injections = nodes.injections
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+local kern_code = nodecodes.kern
+local nodepool = nodes.pool
+local newkern = nodepool.kern
+
+local traverse_id = node.traverse_id
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+
+local a_kernpair = attributes.private('kernpair')
+local a_ligacomp = attributes.private('ligacomp')
+local a_markbase = attributes.private('markbase')
+local a_markmark = attributes.private('markmark')
+local a_markdone = attributes.private('markdone')
+local a_cursbase = attributes.private('cursbase')
+local a_curscurs = attributes.private('curscurs')
+local a_cursdone = attributes.private('cursdone')
+
+-- This injector has been tested by Idris Samawi Hamid (several arabic fonts as well as
+-- the rather demanding Husayni font), Khaled Hosny (latin and arabic) and Kaj Eigner
+-- (arabic, hebrew and thai) and myself (whatever font I come across). I'm pretty sure
+-- that this code is not 100% okay but examples are needed to figure things out.
+
+function injections.installnewkern(nk)
+ newkern = nk or newkern
+end
+
+local cursives = { }
+local marks = { }
+local kerns = { }
+
+-- Currently we do gpos/kern in a bit inofficial way but when we have the extra fields in
+-- glyphnodes to manipulate ht/dp/wd explicitly I will provide an alternative; also, we
+-- can share tables.
+
+-- For the moment we pass the r2l key ... volt/arabtype tests .. idris: this needs
+-- checking with husayni (volt and fontforge).
+
+function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext)
+ local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2])
+ local ws, wn = tfmstart.width, tfmnext.width
+ local bound = #cursives + 1
+ start[a_cursbase] = bound
+ nxt[a_curscurs] = bound
+ cursives[bound] = { rlmode, dx, dy, ws, wn }
+ return dx, dy, bound
+end
+
+function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr)
+ local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4]
+ -- dy = y - h
+ if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then
+ local bound = current[a_kernpair]
+ if bound then
+ local kb = kerns[bound]
+ -- 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
+ current[a_kernpair] = bound
+ kerns[bound] = { rlmode, x, y, w, h, r2lflag, tfmchr.width }
+ end
+ return x, y, w, h, bound
+ end
+ return x, y, w, h -- no bound
+end
+
+function injections.setkern(current,factor,rlmode,x,tfmchr)
+ local dx = factor*x
+ if dx ~= 0 then
+ local bound = #kerns + 1
+ current[a_kernpair] = bound
+ kerns[bound] = { rlmode, dx }
+ return dx, bound
+ else
+ return 0, 0
+ end
+end
+
+function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark) -- ba=baseanchor, ma=markanchor
+ local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) -- the index argument is no longer used but when this
+ local bound = base[a_markbase] -- fails again we should pass it
+ local index = 1
+ if bound then
+ local mb = marks[bound]
+ if mb then
+ -- if not index then index = #mb + 1 end
+ index = #mb + 1
+ mb[index] = { dx, dy, rlmode }
+ start[a_markmark] = bound
+ start[a_markdone] = index
+ return dx, dy, bound
+ else
+ report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound)
+ end
+ end
+-- index = index or 1
+ index = index or 1
+ bound = #marks + 1
+ base[a_markbase] = bound
+ start[a_markmark] = bound
+ start[a_markdone] = index
+ marks[bound] = { [index] = { dx, dy, rlmode, baseismark } }
+ return dx, dy, bound
+end
+
+local function dir(n)
+ return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset"
+end
+
+local function trace(head)
+ report_injections("begin run")
+ for n in traverse_id(glyph_code,head) do
+ if n.subtype < 256 then
+ local kp = n[a_kernpair]
+ local mb = n[a_markbase]
+ local mm = n[a_markmark]
+ local md = n[a_markdone]
+ local cb = n[a_cursbase]
+ local cc = n[a_curscurs]
+ local char = n.char
+ report_injections("font %s, char %U, glyph %c",n.font,char,char)
+ if kp then
+ local k = kerns[kp]
+ if k[3] then
+ report_injections(" pairkern: dir %a, x %p, y %p, w %p, h %p",dir(k[1]),k[2],k[3],k[4],k[5])
+ else
+ report_injections(" kern: dir %a, dx %p",dir(k[1]),k[2])
+ end
+ end
+ if mb then
+ report_injections(" markbase: bound %a",mb)
+ end
+ if mm then
+ local m = marks[mm]
+ if mb then
+ local m = m[mb]
+ if m then
+ report_injections(" markmark: bound %a, index %a, dx %p, dy %p",mm,md,m[1],m[2])
+ else
+ report_injections(" markmark: bound %a, missing index",mm)
+ end
+ else
+ m = m[1]
+ report_injections(" markmark: bound %a, dx %p, dy %p",mm,m and m[1],m and m[2])
+ end
+ end
+ if cb then
+ report_injections(" cursbase: bound %a",cb)
+ end
+ if cc then
+ local c = cursives[cc]
+ report_injections(" curscurs: bound %a, dir %a, dx %p, dy %p",cc,dir(c[1]),c[2],c[3])
+ end
+ end
+ end
+ report_injections("end run")
+end
+
+-- todo: reuse tables (i.e. no collection), but will be extra fields anyway
+-- todo: check for attribute
+
+-- We can have a fast test on a font being processed, so we can check faster for marks etc
+-- but I'll make a context variant anyway.
+
+local function show_result(head)
+ local current = head
+ local skipping = false
+ while current do
+ local id = current.id
+ if id == glyph_code then
+ report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset)
+ skipping = false
+ elseif id == kern_code then
+ report_injections("kern: %p",current.kern)
+ skipping = false
+ elseif not skipping then
+ report_injections()
+ skipping = true
+ end
+ current = current.next
+ end
+end
+
+function injections.handler(head,where,keep)
+ local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns)
+ if has_marks or has_cursives then
+ if trace_injections then
+ trace(head)
+ end
+ -- in the future variant we will not copy items but refs to tables
+ local done, ky, rl, valid, cx, wx, mk, nofvalid = false, { }, { }, { }, { }, { }, { }, 0
+ if has_kerns then -- move outside loop
+ local nf, tm = nil, nil
+ for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts
+ if n.subtype < 256 then
+ nofvalid = nofvalid + 1
+ valid[nofvalid] = n
+ if n.font ~= nf then
+ nf = n.font
+ tm = fontdata[nf].resources.marks
+ end
+ if tm then
+ mk[n] = tm[n.char]
+ end
+ local k = n[a_kernpair]
+ if k then
+ local kk = kerns[k]
+ if kk then
+ local x, y, w, h = kk[2] or 0, kk[3] or 0, kk[4] or 0, kk[5] or 0
+ local dy = y - h
+ if dy ~= 0 then
+ ky[n] = dy
+ end
+ if w ~= 0 or x ~= 0 then
+ wx[n] = kk
+ end
+ rl[n] = kk[1] -- could move in test
+ end
+ end
+ end
+ end
+ else
+ local nf, tm = nil, nil
+ for n in traverse_id(glyph_code,head) do
+ if n.subtype < 256 then
+ nofvalid = nofvalid + 1
+ valid[nofvalid] = n
+ if n.font ~= nf then
+ nf = n.font
+ tm = fontdata[nf].resources.marks
+ end
+ if tm then
+ mk[n] = tm[n.char]
+ end
+ end
+ end
+ end
+ if nofvalid > 0 then
+ -- we can assume done == true because we have cursives and marks
+ local cx = { }
+ if has_kerns and next(ky) then
+ for n, k in next, ky do
+ n.yoffset = k
+ end
+ end
+ -- todo: reuse t and use maxt
+ if has_cursives then
+ local p_cursbase, p = nil, nil
+ -- since we need valid[n+1] we can also use a "while true do"
+ local t, d, maxt = { }, { }, 0
+ for i=1,nofvalid do -- valid == glyphs
+ local n = valid[i]
+ if not mk[n] then
+ local n_cursbase = n[a_cursbase]
+ if p_cursbase then
+ local n_curscurs = n[a_curscurs]
+ if p_cursbase == n_curscurs then
+ local c = cursives[n_curscurs]
+ if c then
+ local rlmode, dx, dy, ws, wn = c[1], c[2], c[3], c[4], c[5]
+ if rlmode >= 0 then
+ dx = dx - ws
+ else
+ dx = dx + wn
+ end
+ if dx ~= 0 then
+ cx[n] = dx
+ rl[n] = rlmode
+ end
+ -- if rlmode and rlmode < 0 then
+ dy = -dy
+ -- end
+ maxt = maxt + 1
+ t[maxt] = p
+ d[maxt] = dy
+ else
+ maxt = 0
+ end
+ end
+ elseif maxt > 0 then
+ local ny = n.yoffset
+ for i=maxt,1,-1 do
+ ny = ny + d[i]
+ local ti = t[i]
+ ti.yoffset = ti.yoffset + ny
+ end
+ maxt = 0
+ end
+ if not n_cursbase and maxt > 0 then
+ local ny = n.yoffset
+ for i=maxt,1,-1 do
+ ny = ny + d[i]
+ local ti = t[i]
+ ti.yoffset = ny
+ end
+ maxt = 0
+ end
+ p_cursbase, p = n_cursbase, n
+ end
+ end
+ if maxt > 0 then
+ local ny = n.yoffset
+ for i=maxt,1,-1 do
+ ny = ny + d[i]
+ local ti = t[i]
+ ti.yoffset = ny
+ end
+ maxt = 0
+ end
+ if not keep then
+ cursives = { }
+ end
+ end
+ if has_marks then
+ for i=1,nofvalid do
+ local p = valid[i]
+ local p_markbase = p[a_markbase]
+ if p_markbase then
+ local mrks = marks[p_markbase]
+ local nofmarks = #mrks
+ for n in traverse_id(glyph_code,p.next) do
+ local n_markmark = n[a_markmark]
+ if p_markbase == n_markmark then
+ local index = n[a_markdone] or 1
+ local d = mrks[index]
+ if d then
+ local rlmode = d[3]
+ --
+ local k = wx[p]
+ if k then
+ local x = k[2]
+ local w = k[4]
+ if w then
+ if rlmode and rlmode >= 0 then
+ -- kern(x) glyph(p) kern(w-x) mark(n)
+ n.xoffset = p.xoffset - p.width + d[1] - (w-x)
+ else
+ -- kern(w-x) glyph(p) kern(x) mark(n)
+ n.xoffset = p.xoffset - d[1] - x
+ end
+ else
+ if rlmode and rlmode >= 0 then
+ -- okay for husayni
+ n.xoffset = p.xoffset - p.width + d[1]
+ else
+ -- needs checking: is x ok here?
+ n.xoffset = p.xoffset - d[1] - x
+ end
+ end
+ else
+ if rlmode and rlmode >= 0 then
+ n.xoffset = p.xoffset - p.width + d[1]
+ else
+ n.xoffset = p.xoffset - d[1]
+ end
+ local w = n.width
+ if w ~= 0 then
+ insert_node_before(head,n,newkern(-w/2))
+ insert_node_after(head,n,newkern(-w/2))
+ end
+ end
+ -- --
+ if mk[p] then
+ n.yoffset = p.yoffset + d[2]
+ else
+ n.yoffset = n.yoffset + p.yoffset + d[2]
+ end
+ --
+ if nofmarks == 1 then
+ break
+ else
+ nofmarks = nofmarks - 1
+ end
+ end
+ else
+ -- KE: there can be <mark> <mkmk> <mark> sequences in ligatures
+ end
+ end
+ end
+ end
+ if not keep then
+ marks = { }
+ end
+ end
+ -- todo : combine
+ if next(wx) then
+ for n, k in next, wx do
+ -- only w can be nil (kernclasses), can be sped up when w == nil
+ local x = k[2]
+ local w = k[4]
+ if w then
+ local rl = k[1] -- r2l = k[6]
+ local wx = w - x
+ if rl < 0 then -- KE: don't use r2l here
+ if wx ~= 0 then
+ insert_node_before(head,n,newkern(wx)) -- type 0/2
+ end
+ if x ~= 0 then
+ insert_node_after (head,n,newkern(x)) -- type 0/2
+ end
+ else
+ if x ~= 0 then
+ insert_node_before(head,n,newkern(x)) -- type 0/2
+ end
+ if wx ~= 0 then
+ insert_node_after (head,n,newkern(wx)) -- type 0/2
+ end
+ end
+ elseif x ~= 0 then
+ -- this needs checking for rl < 0 but it is unlikely that a r2l script
+ -- uses kernclasses between glyphs so we're probably safe (KE has a
+ -- problematic font where marks interfere with rl < 0 in the previous
+ -- case)
+ insert_node_before(head,n,newkern(x)) -- a real font kern, type 0
+ end
+ end
+ end
+ if next(cx) then
+ for n, k in next, cx do
+ if k ~= 0 then
+ local rln = rl[n]
+ if rln and rln < 0 then
+ insert_node_before(head,n,newkern(-k)) -- type 0/2
+ else
+ insert_node_before(head,n,newkern(k)) -- type 0/2
+ end
+ end
+ end
+ end
+ if not keep then
+ kerns = { }
+ end
+ -- if trace_injections then
+ -- show_result(head)
+ -- end
+ return head, true
+ elseif not keep then
+ kerns, cursives, marks = { }, { }, { }
+ end
+ elseif has_kerns then
+ if trace_injections then
+ trace(head)
+ end
+ for n in traverse_id(glyph_code,head) do
+ if n.subtype < 256 then
+ local k = n[a_kernpair]
+ if k then
+ local kk = kerns[k]
+ if kk then
+ local rl, x, y, w = kk[1], kk[2] or 0, kk[3], kk[4]
+ if y and y ~= 0 then
+ n.yoffset = y -- todo: h ?
+ end
+ if w then
+ -- copied from above
+ -- local r2l = kk[6]
+ local wx = w - x
+ if rl < 0 then -- KE: don't use r2l here
+ if wx ~= 0 then
+ insert_node_before(head,n,newkern(wx))
+ end
+ if x ~= 0 then
+ insert_node_after (head,n,newkern(x))
+ end
+ else
+ if x ~= 0 then
+ insert_node_before(head,n,newkern(x))
+ end
+ if wx ~= 0 then
+ insert_node_after(head,n,newkern(wx))
+ end
+ end
+ else
+ -- simple (e.g. kernclass kerns)
+ if x ~= 0 then
+ insert_node_before(head,n,newkern(x))
+ end
+ end
+ end
+ end
+ end
+ end
+ if not keep then
+ kerns = { }
+ end
+ -- if trace_injections then
+ -- show_result(head)
+ -- end
+ return head, true
+ else
+ -- no tracing needed
+ end
+ return head, false
+end
diff --git a/luaotfload-fonts-lua.lua b/src/luaotfload-fonts-lua.lua
index ec3fe38..ec3fe38 100644
--- a/luaotfload-fonts-lua.lua
+++ b/src/luaotfload-fonts-lua.lua
diff --git a/src/luaotfload-fonts-otn.lua b/src/luaotfload-fonts-otn.lua
new file mode 100644
index 0000000..c57be5f
--- /dev/null
+++ b/src/luaotfload-fonts-otn.lua
@@ -0,0 +1,2848 @@
+if not modules then modules = { } end modules ['font-otn'] = {
+ 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",
+}
+
+-- preprocessors = { "nodes" }
+
+-- this is still somewhat preliminary and it will get better in due time;
+-- much functionality could only be implemented thanks to the husayni font
+-- of Idris Samawi Hamid to who we dedicate this module.
+
+-- in retrospect it always looks easy but believe it or not, it took a lot
+-- of work to get proper open type support done: buggy fonts, fuzzy specs,
+-- special made testfonts, many skype sessions between taco, idris and me,
+-- torture tests etc etc ... unfortunately the code does not show how much
+-- time it took ...
+
+-- todo:
+--
+-- kerning is probably not yet ok for latin around dics nodes (interesting challenge)
+-- extension infrastructure (for usage out of context)
+-- sorting features according to vendors/renderers
+-- alternative loop quitters
+-- check cursive and r2l
+-- find out where ignore-mark-classes went
+-- default features (per language, script)
+-- handle positions (we need example fonts)
+-- handle gpos_single (we might want an extra width field in glyph nodes because adding kerns might interfere)
+-- mark (to mark) code is still not what it should be (too messy but we need some more extreem husayni tests)
+-- remove some optimizations (when I have a faster machine)
+--
+-- maybe redo the lot some way (more context specific)
+
+--[[ldx--
+<p>This module is a bit more split up that I'd like but since we also want to test
+with plain <l n='tex'/> it has to be so. This module is part of <l n='context'/>
+and discussion about improvements and functionality mostly happens on the
+<l n='context'/> mailing list.</p>
+
+<p>The specification of OpenType is kind of vague. Apart from a lack of a proper
+free specifications there's also the problem that Microsoft and Adobe
+may have their own interpretation of how and in what order to apply features.
+In general the Microsoft website has more detailed specifications and is a
+better reference. There is also some information in the FontForge help files.</p>
+
+<p>Because there is so much possible, fonts might contain bugs and/or be made to
+work with certain rederers. These may evolve over time which may have the side
+effect that suddenly fonts behave differently.</p>
+
+<p>After a lot of experiments (mostly by Taco, me and Idris) we're now at yet another
+implementation. Of course all errors are mine and of course the code can be
+improved. There are quite some optimizations going on here and processing speed
+is currently acceptable. Not all functions are implemented yet, often because I
+lack the fonts for testing. Many scripts are not yet supported either, but I will
+look into them as soon as <l n='context'/> users ask for it.</p>
+
+<p>Because there are different interpretations possible, I will extend the code
+with more (configureable) variants. I can also add hooks for users so that they can
+write their own extensions.</p>
+
+<p>Glyphs are indexed not by unicode but in their own way. This is because there is no
+relationship with unicode at all, apart from the fact that a font might cover certain
+ranges of characters. One character can have multiple shapes. However, at the
+<l n='tex'/> end we use unicode so and all extra glyphs are mapped into a private
+space. This is needed because we need to access them and <l n='tex'/> has to include
+then in the output eventually.</p>
+
+<p>The raw table as it coms from <l n='fontforge'/> gets reorganized in to fit out needs.
+In <l n='context'/> that table is packed (similar tables are shared) and cached on disk
+so that successive runs can use the optimized table (after loading the table is
+unpacked). The flattening code used later is a prelude to an even more compact table
+format (and as such it keeps evolving).</p>
+
+<p>This module is sparsely documented because it is a moving target. The table format
+of the reader changes and we experiment a lot with different methods for supporting
+features.</p>
+
+<p>As with the <l n='afm'/> code, we may decide to store more information in the
+<l n='otf'/> table.</p>
+
+<p>Incrementing the version number will force a re-cache. We jump the number by one
+when there's a fix in the <l n='fontforge'/> library or <l n='lua'/> code that
+results in different tables.</p>
+--ldx]]--
+
+-- action handler chainproc chainmore comment
+--
+-- gsub_single ok ok ok
+-- gsub_multiple ok ok not implemented yet
+-- gsub_alternate ok ok not implemented yet
+-- gsub_ligature ok ok ok
+-- gsub_context ok --
+-- gsub_contextchain ok --
+-- gsub_reversecontextchain ok --
+-- chainsub -- ok
+-- reversesub -- ok
+-- gpos_mark2base ok ok
+-- gpos_mark2ligature ok ok
+-- gpos_mark2mark ok ok
+-- gpos_cursive ok untested
+-- gpos_single ok ok
+-- gpos_pair ok ok
+-- gpos_context ok --
+-- gpos_contextchain ok --
+--
+-- todo: contextpos and contextsub and class stuff
+--
+-- actions:
+--
+-- handler : actions triggered by lookup
+-- chainproc : actions triggered by contextual lookup
+-- chainmore : multiple substitutions triggered by contextual lookup (e.g. fij -> f + ij)
+--
+-- remark: the 'not implemented yet' variants will be done when we have fonts that use them
+-- remark: we need to check what to do with discretionaries
+
+-- We used to have independent hashes for lookups but as the tags are unique
+-- we now use only one hash. If needed we can have multiple again but in that
+-- case I will probably prefix (i.e. rename) the lookups in the cached font file.
+
+-- Todo: make plugin feature that operates on char/glyphnode arrays
+
+local concat, insert, remove = table.concat, table.insert, table.remove
+local gmatch, gsub, find, match, lower, strip = string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip
+local type, next, tonumber, tostring = type, next, tonumber, tostring
+local lpegmatch = lpeg.match
+local random = math.random
+local formatters = string.formatters
+
+local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes
+
+local registertracker = trackers.register
+
+local fonts = fonts
+local otf = fonts.handlers.otf
+
+local trace_lookups = false registertracker("otf.lookups", function(v) trace_lookups = v end)
+local trace_singles = false registertracker("otf.singles", function(v) trace_singles = v end)
+local trace_multiples = false registertracker("otf.multiples", function(v) trace_multiples = v end)
+local trace_alternatives = false registertracker("otf.alternatives", function(v) trace_alternatives = v end)
+local trace_ligatures = false registertracker("otf.ligatures", function(v) trace_ligatures = v end)
+local trace_contexts = false registertracker("otf.contexts", function(v) trace_contexts = v end)
+local trace_marks = false registertracker("otf.marks", function(v) trace_marks = v end)
+local trace_kerns = false registertracker("otf.kerns", function(v) trace_kerns = v end)
+local trace_cursive = false registertracker("otf.cursive", function(v) trace_cursive = v end)
+local trace_preparing = false registertracker("otf.preparing", function(v) trace_preparing = v end)
+local trace_bugs = false registertracker("otf.bugs", function(v) trace_bugs = v end)
+local trace_details = false registertracker("otf.details", function(v) trace_details = v end)
+local trace_applied = false registertracker("otf.applied", function(v) trace_applied = v end)
+local trace_steps = false registertracker("otf.steps", function(v) trace_steps = v end)
+local trace_skips = false registertracker("otf.skips", function(v) trace_skips = v end)
+local trace_directions = false registertracker("otf.directions", function(v) trace_directions = v end)
+
+local report_direct = logs.reporter("fonts","otf direct")
+local report_subchain = logs.reporter("fonts","otf subchain")
+local report_chain = logs.reporter("fonts","otf chain")
+local report_process = logs.reporter("fonts","otf process")
+local report_prepare = logs.reporter("fonts","otf prepare")
+local report_warning = logs.reporter("fonts","otf warning")
+
+registertracker("otf.verbose_chain", function(v) otf.setcontextchain(v and "verbose") end)
+registertracker("otf.normal_chain", function(v) otf.setcontextchain(v and "normal") end)
+
+registertracker("otf.replacements", "otf.singles,otf.multiples,otf.alternatives,otf.ligatures")
+registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive")
+registertracker("otf.actions","otf.replacements,otf.positions")
+registertracker("otf.injections","nodes.injections")
+
+registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing")
+
+local insert_node_after = node.insert_after
+local delete_node = nodes.delete
+local copy_node = node.copy
+local find_node_tail = node.tail or node.slide
+local flush_node_list = node.flush_list
+local end_of_math = node.end_of_math
+
+local setmetatableindex = table.setmetatableindex
+
+local zwnj = 0x200C
+local zwj = 0x200D
+local wildcard = "*"
+local default = "dflt"
+
+local nodecodes = nodes.nodecodes
+local whatcodes = nodes.whatcodes
+local glyphcodes = nodes.glyphcodes
+local disccodes = nodes.disccodes
+
+local glyph_code = nodecodes.glyph
+local glue_code = nodecodes.glue
+local disc_code = nodecodes.disc
+local whatsit_code = nodecodes.whatsit
+local math_code = nodecodes.math
+
+local dir_code = whatcodes.dir
+local localpar_code = whatcodes.localpar
+
+local discretionary_code = disccodes.discretionary
+
+local ligature_code = glyphcodes.ligature
+
+local privateattribute = attributes.private
+
+-- Something is messed up: we have two mark / ligature indices, one at the injection
+-- end and one here ... this is bases in KE's patches but there is something fishy
+-- there as I'm pretty sure that for husayni we need some connection (as it's much
+-- more complex than an average font) but I need proper examples of all cases, not
+-- of only some.
+
+local a_state = privateattribute('state')
+local a_markbase = privateattribute('markbase')
+local a_markmark = privateattribute('markmark')
+local a_markdone = privateattribute('markdone') -- assigned at the injection end
+local a_cursbase = privateattribute('cursbase')
+local a_curscurs = privateattribute('curscurs')
+local a_cursdone = privateattribute('cursdone')
+local a_kernpair = privateattribute('kernpair')
+local a_ligacomp = privateattribute('ligacomp') -- assigned here (ideally it should be combined)
+
+local injections = nodes.injections
+local setmark = injections.setmark
+local setcursive = injections.setcursive
+local setkern = injections.setkern
+local setpair = injections.setpair
+
+local markonce = true
+local cursonce = true
+local kernonce = true
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+
+local otffeatures = fonts.constructors.newfeatures("otf")
+local registerotffeature = otffeatures.register
+
+local onetimemessage = fonts.loggers.onetimemessage or function() end
+
+otf.defaultnodealternate = "none" -- first last
+
+-- we share some vars here, after all, we have no nested lookups and less code
+
+local tfmdata = false
+local characters = false
+local descriptions = false
+local resources = false
+local marks = false
+local currentfont = false
+local lookuptable = false
+local anchorlookups = false
+local lookuptypes = false
+local handlers = { }
+local rlmode = 0
+local featurevalue = false
+
+-- head is always a whatsit so we can safely assume that head is not changed
+
+-- we use this for special testing and documentation
+
+local checkstep = (nodes and nodes.tracers and nodes.tracers.steppers.check) or function() end
+local registerstep = (nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end
+local registermessage = (nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end
+
+local function logprocess(...)
+ if trace_steps then
+ registermessage(...)
+ end
+ report_direct(...)
+end
+
+local function logwarning(...)
+ report_direct(...)
+end
+
+local f_unicode = formatters["%U"]
+local f_uniname = formatters["%U (%s)"]
+local f_unilist = formatters["% t (% t)"]
+
+local function gref(n) -- currently the same as in font-otb
+ if type(n) == "number" then
+ local description = descriptions[n]
+ local name = description and description.name
+ if name then
+ return f_uniname(n,name)
+ else
+ return f_unicode(n)
+ end
+ elseif n then
+ local num, nam = { }, { }
+ for i=1,#n do
+ local ni = n[i]
+ if tonumber(ni) then -- later we will start at 2
+ local di = descriptions[ni]
+ num[i] = f_unicode(ni)
+ nam[i] = di and di.name or "-"
+ end
+ end
+ return f_unilist(num,nam)
+ else
+ return "<error in node mode tracing>"
+ end
+end
+
+local function cref(kind,chainname,chainlookupname,lookupname,index) -- not in the mood to alias f_
+ if index then
+ return formatters["feature %a, chain %a, sub %a, lookup %a, index %a"](kind,chainname,chainlookupname,lookupname,index)
+ elseif lookupname then
+ return formatters["feature %a, chain %a, sub %a, lookup %a"](kind,chainname,chainlookupname,lookupname)
+ elseif chainlookupname then
+ return formatters["feature %a, chain %a, sub %a"](kind,chainname,chainlookupname)
+ elseif chainname then
+ return formatters["feature %a, chain %a"](kind,chainname)
+ else
+ return formatters["feature %a"](kind)
+ end
+end
+
+local function pref(kind,lookupname)
+ return formatters["feature %a, lookup %a"](kind,lookupname)
+end
+
+-- We can assume that languages that use marks are not hyphenated. We can also assume
+-- that at most one discretionary is present.
+
+-- We do need components in funny kerning mode but maybe I can better reconstruct then
+-- as we do have the font components info available; removing components makes the
+-- previous code much simpler. Also, later on copying and freeing becomes easier.
+-- However, for arabic we need to keep them around for the sake of mark placement
+-- and indices.
+
+local function copy_glyph(g) -- next and prev are untouched !
+ local components = g.components
+ if components then
+ g.components = nil
+ local n = copy_node(g)
+ g.components = components
+ return n
+ else
+ return copy_node(g)
+ end
+end
+
+-- start is a mark and we need to keep that one
+
+local function markstoligature(kind,lookupname,head,start,stop,char)
+ if start == stop and start.char == char then
+ return head, start
+ else
+ local prev = start.prev
+ local next = stop.next
+ start.prev = nil
+ stop.next = nil
+ local base = copy_glyph(start)
+ if head == start then
+ head = base
+ end
+ base.char = char
+ base.subtype = ligature_code
+ base.components = start
+ if prev then
+ prev.next = base
+ end
+ if next then
+ next.prev = base
+ end
+ base.next = next
+ base.prev = prev
+ return head, base
+ end
+end
+
+-- The next code is somewhat complicated by the fact that some fonts can have ligatures made
+-- from ligatures that themselves have marks. This was identified by Kai in for instance
+-- arabtype: KAF LAM SHADDA ALEF FATHA (0x0643 0x0644 0x0651 0x0627 0x064E). This becomes
+-- KAF LAM-ALEF with a SHADDA on the first and a FATHA op de second component. In a next
+-- iteration this becomes a KAF-LAM-ALEF with a SHADDA on the second and a FATHA on the
+-- third component.
+
+local function getcomponentindex(start)
+ if start.id ~= glyph_code then
+ return 0
+ elseif start.subtype == ligature_code then
+ local i = 0
+ local components = start.components
+ while components do
+ i = i + getcomponentindex(components)
+ components = components.next
+ end
+ return i
+ elseif not marks[start.char] then
+ return 1
+ else
+ return 0
+ end
+end
+
+-- eventually we will do positioning in an other way (needs addional w/h/d fields)
+
+local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) -- brr head
+ if start == stop and start.char == char then
+ start.char = char
+ return head, start
+ end
+ local prev = start.prev
+ local next = stop.next
+ start.prev = nil
+ stop.next = nil
+ local base = copy_glyph(start)
+ if start == head then
+ head = base
+ end
+ base.char = char
+ base.subtype = ligature_code
+ base.components = start -- start can have components
+ if prev then
+ prev.next = base
+ end
+ if next then
+ next.prev = base
+ end
+ base.next = next
+ base.prev = prev
+ if not discfound then
+ local deletemarks = markflag ~= "mark"
+ local components = start
+ local baseindex = 0
+ local componentindex = 0
+ local head = base
+ local current = base
+ -- first we loop over the glyphs in start .. stop
+ while start do
+ local char = start.char
+ if not marks[char] then
+ baseindex = baseindex + componentindex
+ componentindex = getcomponentindex(start)
+ elseif not deletemarks then -- quite fishy
+ start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex)
+ if trace_marks then
+ logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp])
+ end
+ head, current = insert_node_after(head,current,copy_node(start)) -- unlikely that mark has components
+ elseif trace_marks then
+ logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))
+ end
+ start = start.next
+ end
+ -- we can have one accent as part of a lookup and another following
+ -- local start = components -- was wrong (component scanning was introduced when more complex ligs in devanagari was added)
+ local start = current.next
+ while start and start.id == glyph_code do
+ local char = start.char
+ if marks[char] then
+ start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex)
+ if trace_marks then
+ logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp])
+ end
+ else
+ break
+ end
+ start = start.next
+ end
+ end
+ return head, base
+end
+
+function handlers.gsub_single(head,start,kind,lookupname,replacement)
+ if trace_singles then
+ logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement))
+ end
+ start.char = replacement
+ return head, start, true
+end
+
+local function get_alternative_glyph(start,alternatives,value,trace_alternatives)
+ local n = #alternatives
+ if value == "random" then
+ local r = random(1,n)
+ return alternatives[r], trace_alternatives and formatters["value %a, taking %a"](value,r)
+ elseif value == "first" then
+ return alternatives[1], trace_alternatives and formatters["value %a, taking %a"](value,1)
+ elseif value == "last" then
+ return alternatives[n], trace_alternatives and formatters["value %a, taking %a"](value,n)
+ else
+ value = tonumber(value)
+ if type(value) ~= "number" then
+ return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
+ elseif value > n then
+ local defaultalt = otf.defaultnodealternate
+ if defaultalt == "first" then
+ return alternatives[n], trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
+ elseif defaultalt == "last" then
+ return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,n)
+ else
+ return false, trace_alternatives and formatters["invalid value %a, %s"](value,"out of range")
+ end
+ elseif value == 0 then
+ return start.char, trace_alternatives and formatters["invalid value %a, %s"](value,"no change")
+ elseif value < 1 then
+ return alternatives[1], trace_alternatives and formatters["invalid value %a, taking %a"](value,1)
+ else
+ return alternatives[value], trace_alternatives and formatters["value %a, taking %a"](value,value)
+ end
+ end
+end
+
+local function multiple_glyphs(head,start,multiple,ignoremarks)
+ local nofmultiples = #multiple
+ if nofmultiples > 0 then
+ start.char = multiple[1]
+ if nofmultiples > 1 then
+ local sn = start.next
+ for k=2,nofmultiples do -- todo: use insert_node
+-- untested:
+--
+-- while ignoremarks and marks[sn.char] then
+-- local sn = sn.next
+-- end
+ local n = copy_node(start) -- ignore components
+ n.char = multiple[k]
+ n.next = sn
+ n.prev = start
+ if sn then
+ sn.prev = n
+ end
+ start.next = n
+ start = n
+ end
+ end
+ return head, start, true
+ else
+ if trace_multiples then
+ logprocess("no multiple for %s",gref(start.char))
+ end
+ return head, start, false
+ end
+end
+
+function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence)
+ local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue
+ local choice, comment = get_alternative_glyph(start,alternative,value,trace_alternatives)
+ if choice then
+ if trace_alternatives then
+ logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment)
+ end
+ start.char = choice
+ else
+ if trace_alternatives then
+ logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment)
+ end
+ end
+ return head, start, true
+end
+
+function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence)
+ if trace_multiples then
+ logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple))
+ end
+ return multiple_glyphs(head,start,multiple,sequence.flags[1])
+end
+
+function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
+ local s, stop, discfound = start.next, nil, false
+ local startchar = start.char
+ if marks[startchar] then
+ while s do
+ local id = s.id
+ if id == glyph_code and s.font == currentfont and s.subtype<256 then
+ local lg = ligature[s.char]
+ if lg then
+ stop = s
+ ligature = lg
+ s = s.next
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ if stop then
+ local lig = ligature.ligature
+ if lig then
+ if trace_ligatures then
+ local stopchar = stop.char
+ head, start = markstoligature(kind,lookupname,head,start,stop,lig)
+ logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char))
+ else
+ head, start = markstoligature(kind,lookupname,head,start,stop,lig)
+ end
+ return head, start, true
+ else
+ -- ok, goto next lookup
+ end
+ end
+ else
+ local skipmark = sequence.flags[1]
+ while s do
+ local id = s.id
+ if id == glyph_code and s.subtype<256 then
+ if s.font == currentfont then
+ local char = s.char
+ if skipmark and marks[char] then
+ s = s.next
+ else
+ local lg = ligature[char]
+ if lg then
+ stop = s
+ ligature = lg
+ s = s.next
+ else
+ break
+ end
+ end
+ else
+ break
+ end
+ elseif id == disc_code then
+ discfound = true
+ s = s.next
+ else
+ break
+ end
+ end
+ local lig = ligature.ligature
+ if lig then
+ if stop then
+ if trace_ligatures then
+ local stopchar = stop.char
+ head, start = toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)
+ logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char))
+ else
+ head, start = toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)
+ end
+ return head, start, true
+ else
+ -- weird but happens (in some arabic font)
+ start.char = lig
+ if trace_ligatures then
+ logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig))
+ end
+ return head, start, true
+ end
+ else
+ -- weird but happens
+ end
+ end
+ return head, start, false
+end
+
+--[[ldx--
+<p>We get hits on a mark, but we're not sure if the it has to be applied so
+we need to explicitly test for basechar, baselig and basemark entries.</p>
+--ldx]]--
+
+function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence)
+ local markchar = start.char
+ if marks[markchar] then
+ local base = start.prev -- [glyph] [start=mark]
+ if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then
+ local basechar = base.char
+ if marks[basechar] then
+ while true do
+ base = base.prev
+ if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then
+ basechar = base.char
+ if not marks[basechar] then
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
+ end
+ return head, start, false
+ end
+ end
+ end
+ local baseanchors = descriptions[basechar]
+ if baseanchors then
+ baseanchors = baseanchors.anchors
+ end
+ if baseanchors then
+ local baseanchors = baseanchors['basechar']
+ if baseanchors then
+ local al = anchorlookups[lookupname]
+ for anchor,ba in next, baseanchors do
+ if al[anchor] then
+ local ma = markanchors[anchor]
+ if ma then
+ local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma)
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
+ pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
+ end
+ return head, start, true
+ end
+ end
+ end
+ if trace_bugs then
+ logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar))
+ end
+ end
+ elseif trace_bugs then
+ -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar))
+ onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no char",pref(kind,lookupname))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
+ end
+ return head, start, false
+end
+
+function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence)
+ -- check chainpos variant
+ local markchar = start.char
+ if marks[markchar] then
+ local base = start.prev -- [glyph] [optional marks] [start=mark]
+ if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then
+ local basechar = base.char
+ if marks[basechar] then
+ while true do
+ base = base.prev
+ if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then
+ basechar = base.char
+ if not marks[basechar] then
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
+ end
+ return head, start, false
+ end
+ end
+ end
+ local index = start[a_ligacomp]
+ local baseanchors = descriptions[basechar]
+ if baseanchors then
+ baseanchors = baseanchors.anchors
+ if baseanchors then
+ local baseanchors = baseanchors['baselig']
+ if baseanchors then
+ local al = anchorlookups[lookupname]
+ for anchor, ba in next, baseanchors do
+ if al[anchor] then
+ local ma = markanchors[anchor]
+ if ma then
+ ba = ba[index]
+ if ba then
+ local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index
+ if trace_marks then
+ logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
+ pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy)
+ end
+ return head, start, true
+ else
+ if trace_bugs then
+ logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(kind,lookupname),gref(markchar),gref(basechar),index)
+ end
+ end
+ end
+ end
+ end
+ if trace_bugs then
+ logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar))
+ end
+ end
+ end
+ elseif trace_bugs then
+ -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar))
+ onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no char",pref(kind,lookupname))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
+ end
+ return head, start, false
+end
+
+function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence)
+ local markchar = start.char
+ if marks[markchar] then
+ local base = start.prev -- [glyph] [basemark] [start=mark]
+ local slc = start[a_ligacomp]
+ if slc then -- a rather messy loop ... needs checking with husayni
+ while base do
+ local blc = base[a_ligacomp]
+ if blc and blc ~= slc then
+ base = base.prev
+ else
+ break
+ end
+ end
+ end
+ if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go
+ local basechar = base.char
+ local baseanchors = descriptions[basechar]
+ if baseanchors then
+ baseanchors = baseanchors.anchors
+ if baseanchors then
+ baseanchors = baseanchors['basemark']
+ if baseanchors then
+ local al = anchorlookups[lookupname]
+ for anchor,ba in next, baseanchors do
+ if al[anchor] then
+ local ma = markanchors[anchor]
+ if ma then
+ local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true)
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
+ pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
+ end
+ return head, start, true
+ end
+ end
+ end
+ if trace_bugs then
+ logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar))
+ end
+ end
+ end
+ elseif trace_bugs then
+ -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar))
+ onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no mark",pref(kind,lookupname))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
+ end
+ return head, start, false
+end
+
+function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) -- to be checked
+ local alreadydone = cursonce and start[a_cursbase]
+ if not alreadydone then
+ local done = false
+ local startchar = start.char
+ if marks[startchar] then
+ if trace_cursive then
+ logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
+ end
+ else
+ local nxt = start.next
+ while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do
+ local nextchar = nxt.char
+ if marks[nextchar] then
+ -- should not happen (maybe warning)
+ nxt = nxt.next
+ else
+ local entryanchors = descriptions[nextchar]
+ if entryanchors then
+ entryanchors = entryanchors.anchors
+ if entryanchors then
+ entryanchors = entryanchors['centry']
+ if entryanchors then
+ local al = anchorlookups[lookupname]
+ for anchor, entry in next, entryanchors do
+ if al[anchor] then
+ local exit = exitanchors[anchor]
+ if exit then
+ local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
+ if trace_cursive then
+ logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode)
+ end
+ done = true
+ break
+ end
+ end
+ end
+ end
+ end
+ elseif trace_bugs then
+ -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar))
+ onetimemessage(currentfont,startchar,"no entry anchors",report_fonts)
+ end
+ break
+ end
+ end
+ end
+ return head, start, done
+ else
+ if trace_cursive and trace_details then
+ logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)
+ end
+ return head, start, false
+ end
+end
+
+function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence)
+ local startchar = start.char
+ local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
+ if trace_kerns then
+ logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h)
+ end
+ return head, start, false
+end
+
+function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)
+ -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too
+ -- todo: kerns in components of ligatures
+ local snext = start.next
+ if not snext then
+ return head, start, false
+ else
+ local prev, done = start, false
+ local factor = tfmdata.parameters.factor
+ local lookuptype = lookuptypes[lookupname]
+ while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do
+ local nextchar = snext.char
+ local krn = kerns[nextchar]
+ if not krn and marks[nextchar] then
+ prev = snext
+ snext = snext.next
+ else
+ if not krn then
+ -- skip
+ elseif type(krn) == "table" then
+ if lookuptype == "pair" then -- probably not needed
+ local a, b = krn[2], krn[3]
+ if a and #a > 0 then
+ local startchar = start.char
+ local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
+ if trace_kerns then
+ logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ if b and #b > 0 then
+ local startchar = start.char
+ local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
+ if trace_kerns then
+ logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ else -- wrong ... position has different entries
+ report_process("%s: check this out (old kern stuff)",pref(kind,lookupname))
+ -- local a, b = krn[2], krn[6]
+ -- if a and a ~= 0 then
+ -- local k = setkern(snext,factor,rlmode,a)
+ -- if trace_kerns then
+ -- logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar))
+ -- end
+ -- end
+ -- if b and b ~= 0 then
+ -- logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor)
+ -- end
+ end
+ done = true
+ elseif krn ~= 0 then
+ local k = setkern(snext,factor,rlmode,krn)
+ if trace_kerns then
+ logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar))
+ end
+ done = true
+ end
+ break
+ end
+ end
+ return head, start, done
+ end
+end
+
+--[[ldx--
+<p>I will implement multiple chain replacements once I run into a font that uses
+it. It's not that complex to handle.</p>
+--ldx]]--
+
+local chainmores = { }
+local chainprocs = { }
+
+local function logprocess(...)
+ if trace_steps then
+ registermessage(...)
+ end
+ report_subchain(...)
+end
+
+local logwarning = report_subchain
+
+local function logprocess(...)
+ if trace_steps then
+ registermessage(...)
+ end
+ report_chain(...)
+end
+
+local logwarning = report_chain
+
+-- We could share functions but that would lead to extra function calls with many
+-- arguments, redundant tests and confusing messages.
+
+function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname)
+ logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
+ return head, start, false
+end
+
+function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n)
+ logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
+ return head, start, false
+end
+
+-- The reversesub is a special case, which is why we need to store the replacements
+-- in a bit weird way. There is no lookup and the replacement comes from the lookup
+-- itself. It is meant mostly for dealing with Urdu.
+
+function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements)
+ local char = start.char
+ local replacement = replacements[char]
+ if replacement then
+ if trace_singles then
+ logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement))
+ end
+ start.char = replacement
+ return head, start, true
+ else
+ return head, start, false
+ end
+end
+
+--[[ldx--
+<p>This chain stuff is somewhat tricky since we can have a sequence of actions to be
+applied: single, alternate, multiple or ligature where ligature can be an invalid
+one in the sense that it will replace multiple by one but not neccessary one that
+looks like the combination (i.e. it is the counterpart of multiple then). For
+example, the following is valid:</p>
+
+<typing>
+<line>xxxabcdexxx [single a->A][multiple b->BCD][ligature cde->E] xxxABCDExxx</line>
+</typing>
+
+<p>Therefore we we don't really do the replacement here already unless we have the
+single lookup case. The efficiency of the replacements can be improved by deleting
+as less as needed but that would also make the code even more messy.</p>
+--ldx]]--
+
+-- local function delete_till_stop(head,start,stop,ignoremarks) -- keeps start
+-- local n = 1
+-- if start == stop then
+-- -- done
+-- elseif ignoremarks then
+-- repeat -- start x x m x x stop => start m
+-- local next = start.next
+-- if not marks[next.char] then
+-- local components = next.components
+-- if components then -- probably not needed
+-- flush_node_list(components)
+-- end
+-- head = delete_node(head,next)
+-- end
+-- n = n + 1
+-- until next == stop
+-- else -- start x x x stop => start
+-- repeat
+-- local next = start.next
+-- local components = next.components
+-- if components then -- probably not needed
+-- flush_node_list(components)
+-- end
+-- head = delete_node(head,next)
+-- n = n + 1
+-- until next == stop
+-- end
+-- return head, n
+-- end
+
+--[[ldx--
+<p>Here we replace start by a single variant, First we delete the rest of the
+match.</p>
+--ldx]]--
+
+function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex)
+ -- todo: marks ?
+ local current = start
+ local subtables = currentlookup.subtables
+ if #subtables > 1 then
+ logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," "))
+ end
+ while current do
+ if current.id == glyph_code then
+ local currentchar = current.char
+ local lookupname = subtables[1] -- only 1
+ local replacement = lookuphash[lookupname]
+ if not replacement then
+ if trace_bugs then
+ logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex))
+ end
+ else
+ replacement = replacement[currentchar]
+ if not replacement or replacement == "" then
+ if trace_bugs then
+ logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar))
+ end
+ else
+ if trace_singles then
+ logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement))
+ end
+ current.char = replacement
+ end
+ end
+ return head, start, true
+ elseif current == stop then
+ break
+ else
+ current = current.next
+ end
+ end
+ return head, start, false
+end
+
+chainmores.gsub_single = chainprocs.gsub_single
+
+--[[ldx--
+<p>Here we replace start by a sequence of new glyphs. First we delete the rest of
+the match.</p>
+--ldx]]--
+
+function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
+ -- local head, n = delete_till_stop(head,start,stop)
+ local startchar = start.char
+ local subtables = currentlookup.subtables
+ local lookupname = subtables[1]
+ local replacements = lookuphash[lookupname]
+ if not replacements then
+ if trace_bugs then
+ logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname))
+ end
+ else
+ replacements = replacements[startchar]
+ if not replacements or replacement == "" then
+ if trace_bugs then
+ logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar))
+ end
+ else
+ if trace_multiples then
+ logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements))
+ end
+ return multiple_glyphs(head,start,replacements,currentlookup.flags[1])
+ end
+ end
+ return head, start, false
+end
+
+chainmores.gsub_multiple = chainprocs.gsub_multiple
+
+--[[ldx--
+<p>Here we replace start by new glyph. First we delete the rest of the match.</p>
+--ldx]]--
+
+-- char_1 mark_1 -> char_x mark_1 (ignore marks)
+-- char_1 mark_1 -> char_x
+
+-- to be checked: do we always have just one glyph?
+-- we can also have alternates for marks
+-- marks come last anyway
+-- are there cases where we need to delete the mark
+
+function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
+ local current = start
+ local subtables = currentlookup.subtables
+ local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue
+ while current do
+ if current.id == glyph_code then -- is this check needed?
+ local currentchar = current.char
+ local lookupname = subtables[1]
+ local alternatives = lookuphash[lookupname]
+ if not alternatives then
+ if trace_bugs then
+ logwarning("%s: no alternative hit",cref(kind,chainname,chainlookupname,lookupname))
+ end
+ else
+ alternatives = alternatives[currentchar]
+ if alternatives then
+ local choice, comment = get_alternative_glyph(current,alternatives,value,trace_alternatives)
+ if choice then
+ if trace_alternatives then
+ logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment)
+ end
+ start.char = choice
+ else
+ if trace_alternatives then
+ logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment)
+ end
+ end
+ elseif trace_bugs then
+ logwarning("%s: no alternative for %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar),comment)
+ end
+ end
+ return head, start, true
+ elseif current == stop then
+ break
+ else
+ current = current.next
+ end
+ end
+ return head, start, false
+end
+
+chainmores.gsub_alternate = chainprocs.gsub_alternate
+
+--[[ldx--
+<p>When we replace ligatures we use a helper that handles the marks. I might change
+this function (move code inline and handle the marks by a separate function). We
+assume rather stupid ligatures (no complex disc nodes).</p>
+--ldx]]--
+
+function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex)
+ local startchar = start.char
+ local subtables = currentlookup.subtables
+ local lookupname = subtables[1]
+ local ligatures = lookuphash[lookupname]
+ if not ligatures then
+ if trace_bugs then
+ logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex))
+ end
+ else
+ ligatures = ligatures[startchar]
+ if not ligatures then
+ if trace_bugs then
+ logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
+ end
+ else
+ local s = start.next
+ local discfound = false
+ local last = stop
+ local nofreplacements = 0
+ local skipmark = currentlookup.flags[1]
+ while s do
+ local id = s.id
+ if id == disc_code then
+ s = s.next
+ discfound = true
+ else
+ local schar = s.char
+ if skipmark and marks[schar] then -- marks
+ s = s.next
+ else
+ local lg = ligatures[schar]
+ if lg then
+ ligatures, last, nofreplacements = lg, s, nofreplacements + 1
+ if s == stop then
+ break
+ else
+ s = s.next
+ end
+ else
+ break
+ end
+ end
+ end
+ end
+ local l2 = ligatures.ligature
+ if l2 then
+ if chainindex then
+ stop = last
+ end
+ if trace_ligatures then
+ if start == stop then
+ logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2))
+ else
+ logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2))
+ end
+ end
+ head, start = toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound)
+ return head, start, true, nofreplacements
+ elseif trace_bugs then
+ if start == stop then
+ logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
+ else
+ logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char))
+ end
+ end
+ end
+ end
+ return head, start, false, 0
+end
+
+chainmores.gsub_ligature = chainprocs.gsub_ligature
+
+function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
+ local markchar = start.char
+ if marks[markchar] then
+ local subtables = currentlookup.subtables
+ local lookupname = subtables[1]
+ local markanchors = lookuphash[lookupname]
+ if markanchors then
+ markanchors = markanchors[markchar]
+ end
+ if markanchors then
+ local base = start.prev -- [glyph] [start=mark]
+ if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then
+ local basechar = base.char
+ if marks[basechar] then
+ while true do
+ base = base.prev
+ if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then
+ basechar = base.char
+ if not marks[basechar] then
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
+ end
+ return head, start, false
+ end
+ end
+ end
+ local baseanchors = descriptions[basechar].anchors
+ if baseanchors then
+ local baseanchors = baseanchors['basechar']
+ if baseanchors then
+ local al = anchorlookups[lookupname]
+ for anchor,ba in next, baseanchors do
+ if al[anchor] then
+ local ma = markanchors[anchor]
+ if ma then
+ local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma)
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
+ cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
+ end
+ return head, start, true
+ end
+ end
+ end
+ if trace_bugs then
+ logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
+ end
+ end
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
+ end
+ return head, start, false
+end
+
+function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
+ local markchar = start.char
+ if marks[markchar] then
+ local subtables = currentlookup.subtables
+ local lookupname = subtables[1]
+ local markanchors = lookuphash[lookupname]
+ if markanchors then
+ markanchors = markanchors[markchar]
+ end
+ if markanchors then
+ local base = start.prev -- [glyph] [optional marks] [start=mark]
+ if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then
+ local basechar = base.char
+ if marks[basechar] then
+ while true do
+ base = base.prev
+ if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then
+ basechar = base.char
+ if not marks[basechar] then
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar)
+ end
+ return head, start, false
+ end
+ end
+ end
+ -- todo: like marks a ligatures hash
+ local index = start[a_ligacomp]
+ local baseanchors = descriptions[basechar].anchors
+ if baseanchors then
+ local baseanchors = baseanchors['baselig']
+ if baseanchors then
+ local al = anchorlookups[lookupname]
+ for anchor,ba in next, baseanchors do
+ if al[anchor] then
+ local ma = markanchors[anchor]
+ if ma then
+ ba = ba[index]
+ if ba then
+ local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
+ cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy)
+ end
+ return head, start, true
+ end
+ end
+ end
+ end
+ if trace_bugs then
+ logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
+ end
+ end
+ end
+ elseif trace_bugs then
+ logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname)
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
+ end
+ return head, start, false
+end
+
+function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
+ local markchar = start.char
+ if marks[markchar] then
+ -- local alreadydone = markonce and start[a_markmark]
+ -- if not alreadydone then
+ -- local markanchors = descriptions[markchar].anchors markanchors = markanchors and markanchors.mark
+ local subtables = currentlookup.subtables
+ local lookupname = subtables[1]
+ local markanchors = lookuphash[lookupname]
+ if markanchors then
+ markanchors = markanchors[markchar]
+ end
+ if markanchors then
+ local base = start.prev -- [glyph] [basemark] [start=mark]
+ local slc = start[a_ligacomp]
+ if slc then -- a rather messy loop ... needs checking with husayni
+ while base do
+ local blc = base[a_ligacomp]
+ if blc and blc ~= slc then
+ base = base.prev
+ else
+ break
+ end
+ end
+ end
+ if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go
+ local basechar = base.char
+ local baseanchors = descriptions[basechar].anchors
+ if baseanchors then
+ baseanchors = baseanchors['basemark']
+ if baseanchors then
+ local al = anchorlookups[lookupname]
+ for anchor,ba in next, baseanchors do
+ if al[anchor] then
+ local ma = markanchors[anchor]
+ if ma then
+ local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true)
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
+ cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
+ end
+ return head, start, true
+ end
+ end
+ end
+ if trace_bugs then
+ logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
+ end
+ end
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
+ end
+ -- elseif trace_marks and trace_details then
+ -- logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone)
+ -- end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
+ end
+ return head, start, false
+end
+
+function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
+ local alreadydone = cursonce and start[a_cursbase]
+ if not alreadydone then
+ local startchar = start.char
+ local subtables = currentlookup.subtables
+ local lookupname = subtables[1]
+ local exitanchors = lookuphash[lookupname]
+ if exitanchors then
+ exitanchors = exitanchors[startchar]
+ end
+ if exitanchors then
+ local done = false
+ if marks[startchar] then
+ if trace_cursive then
+ logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
+ end
+ else
+ local nxt = start.next
+ while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do
+ local nextchar = nxt.char
+ if marks[nextchar] then
+ -- should not happen (maybe warning)
+ nxt = nxt.next
+ else
+ local entryanchors = descriptions[nextchar]
+ if entryanchors then
+ entryanchors = entryanchors.anchors
+ if entryanchors then
+ entryanchors = entryanchors['centry']
+ if entryanchors then
+ local al = anchorlookups[lookupname]
+ for anchor, entry in next, entryanchors do
+ if al[anchor] then
+ local exit = exitanchors[anchor]
+ if exit then
+ local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
+ if trace_cursive then
+ logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode)
+ end
+ done = true
+ break
+ end
+ end
+ end
+ end
+ end
+ elseif trace_bugs then
+ -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar))
+ onetimemessage(currentfont,startchar,"no entry anchors",report_fonts)
+ end
+ break
+ end
+ end
+ end
+ return head, start, done
+ else
+ if trace_cursive and trace_details then
+ logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)
+ end
+ return head, start, false
+ end
+ end
+ return head, start, false
+end
+
+function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
+ -- untested .. needs checking for the new model
+ local startchar = start.char
+ local subtables = currentlookup.subtables
+ local lookupname = subtables[1]
+ local kerns = lookuphash[lookupname]
+ if kerns then
+ kerns = kerns[startchar] -- needed ?
+ if kerns then
+ local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
+ if trace_kerns then
+ logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h)
+ end
+ end
+ end
+ return head, start, false
+end
+
+chainmores.gpos_single = chainprocs.gpos_single -- okay?
+
+-- when machines become faster i will make a shared function
+
+function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
+ local snext = start.next
+ if snext then
+ local startchar = start.char
+ local subtables = currentlookup.subtables
+ local lookupname = subtables[1]
+ local kerns = lookuphash[lookupname]
+ if kerns then
+ kerns = kerns[startchar]
+ if kerns then
+ local lookuptype = lookuptypes[lookupname]
+ local prev, done = start, false
+ local factor = tfmdata.parameters.factor
+ while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do
+ local nextchar = snext.char
+ local krn = kerns[nextchar]
+ if not krn and marks[nextchar] then
+ prev = snext
+ snext = snext.next
+ else
+ if not krn then
+ -- skip
+ elseif type(krn) == "table" then
+ if lookuptype == "pair" then
+ local a, b = krn[2], krn[3]
+ if a and #a > 0 then
+ local startchar = start.char
+ local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
+ if trace_kerns then
+ logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ if b and #b > 0 then
+ local startchar = start.char
+ local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
+ if trace_kerns then
+ logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ else
+ report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname))
+ local a, b = krn[2], krn[6]
+ if a and a ~= 0 then
+ local k = setkern(snext,factor,rlmode,a)
+ if trace_kerns then
+ logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))
+ end
+ end
+ if b and b ~= 0 then
+ logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor)
+ end
+ end
+ done = true
+ elseif krn ~= 0 then
+ local k = setkern(snext,factor,rlmode,krn)
+ if trace_kerns then
+ logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))
+ end
+ done = true
+ end
+ break
+ end
+ end
+ return head, start, done
+ end
+ end
+ end
+ return head, start, false
+end
+
+chainmores.gpos_pair = chainprocs.gpos_pair -- okay?
+
+-- what pointer to return, spec says stop
+-- to be discussed ... is bidi changer a space?
+-- elseif char == zwnj and sequence[n][32] then -- brrr
+
+-- somehow l or f is global
+-- we don't need to pass the currentcontext, saves a bit
+-- make a slow variant then can be activated but with more tracing
+
+local function show_skip(kind,chainname,char,ck,class)
+ if ck[9] then
+ logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
+ else
+ logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])
+ end
+end
+
+local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash)
+ -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6]
+ local flags = sequence.flags
+ local done = false
+ local skipmark = flags[1]
+ local skipligature = flags[2]
+ local skipbase = flags[3]
+ local someskip = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !)
+ local markclass = sequence.markclass -- todo, first we need a proper test
+ local skipped = false
+ for k=1,#contexts do
+ local match = true
+ local current = start
+ local last = start
+ local ck = contexts[k]
+ local seq = ck[3]
+ local s = #seq
+ -- f..l = mid string
+ if s == 1 then
+ -- never happens
+ match = current.id == glyph_code and current.font == currentfont and current.subtype<256 and seq[1][current.char]
+ else
+ -- maybe we need a better space check (maybe check for glue or category or combination)
+ -- we cannot optimize for n=2 because there can be disc nodes
+ local f, l = ck[4], ck[5]
+ -- current match
+ if f == 1 and f == l then -- current only
+ -- already a hit
+ -- match = true
+ else -- before/current/after | before/current | current/after
+ -- no need to test first hit (to be optimized)
+ if f == l then -- new, else last out of sync (f is > 1)
+ -- match = true
+ else
+ local n = f + 1
+ last = last.next
+ while n <= l do
+ if last then
+ local id = last.id
+ if id == glyph_code then
+ if last.font == currentfont and last.subtype<256 then
+ local char = last.char
+ local ccd = descriptions[char]
+ if ccd then
+ local class = ccd.class
+ if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then
+ skipped = true
+ if trace_skips then
+ show_skip(kind,chainname,char,ck,class)
+ end
+ last = last.next
+ elseif seq[n][char] then
+ if n < l then
+ last = last.next
+ end
+ n = n + 1
+ else
+ match = false
+ break
+ end
+ else
+ match = false
+ break
+ end
+ else
+ match = false
+ break
+ end
+ elseif id == disc_code then
+ last = last.next
+ else
+ match = false
+ break
+ end
+ else
+ match = false
+ break
+ end
+ end
+ end
+ end
+ -- before
+ if match and f > 1 then
+ local prev = start.prev
+ if prev then
+ local n = f-1
+ while n >= 1 do
+ if prev then
+ local id = prev.id
+ if id == glyph_code then
+ if prev.font == currentfont and prev.subtype<256 then -- normal char
+ local char = prev.char
+ local ccd = descriptions[char]
+ if ccd then
+ local class = ccd.class
+ if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then
+ skipped = true
+ if trace_skips then
+ show_skip(kind,chainname,char,ck,class)
+ end
+ elseif seq[n][char] then
+ n = n -1
+ else
+ match = false
+ break
+ end
+ else
+ match = false
+ break
+ end
+ else
+ match = false
+ break
+ end
+ elseif id == disc_code then
+ -- skip 'm
+ elseif seq[n][32] then
+ n = n -1
+ else
+ match = false
+ break
+ end
+ prev = prev.prev
+ elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces
+ n = n -1
+ else
+ match = false
+ break
+ end
+ end
+ elseif f == 2 then
+ match = seq[1][32]
+ else
+ for n=f-1,1 do
+ if not seq[n][32] then
+ match = false
+ break
+ end
+ end
+ end
+ end
+ -- after
+ if match and s > l then
+ local current = last and last.next
+ if current then
+ -- removed optimization for s-l == 1, we have to deal with marks anyway
+ local n = l + 1
+ while n <= s do
+ if current then
+ local id = current.id
+ if id == glyph_code then
+ if current.font == currentfont and current.subtype<256 then -- normal char
+ local char = current.char
+ local ccd = descriptions[char]
+ if ccd then
+ local class = ccd.class
+ if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then
+ skipped = true
+ if trace_skips then
+ show_skip(kind,chainname,char,ck,class)
+ end
+ elseif seq[n][char] then
+ n = n + 1
+ else
+ match = false
+ break
+ end
+ else
+ match = false
+ break
+ end
+ else
+ match = false
+ break
+ end
+ elseif id == disc_code then
+ -- skip 'm
+ elseif seq[n][32] then -- brrr
+ n = n + 1
+ else
+ match = false
+ break
+ end
+ current = current.next
+ elseif seq[n][32] then
+ n = n + 1
+ else
+ match = false
+ break
+ end
+ end
+ elseif s-l == 1 then
+ match = seq[s][32]
+ else
+ for n=l+1,s do
+ if not seq[n][32] then
+ match = false
+ break
+ end
+ end
+ end
+ end
+ end
+ if match then
+ -- ck == currentcontext
+ if trace_contexts then
+ local rule, lookuptype, f, l = ck[1], ck[2], ck[4], ck[5]
+ local char = start.char
+ if ck[9] then
+ logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a",
+ cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10])
+ else
+ logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a",
+ cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype)
+ end
+ end
+ local chainlookups = ck[6]
+ if chainlookups then
+ local nofchainlookups = #chainlookups
+ -- we can speed this up if needed
+ if nofchainlookups == 1 then
+ local chainlookupname = chainlookups[1]
+ local chainlookup = lookuptable[chainlookupname]
+ if chainlookup then
+ local cp = chainprocs[chainlookup.type]
+ if cp then
+ local ok
+ head, start, ok = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ if ok then
+ done = true
+ end
+ else
+ logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
+ end
+ else -- shouldn't happen
+ logprocess("%s is not yet supported",cref(kind,chainname,chainlookupname))
+ end
+ else
+ 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]
+ if not chainlookup then
+ -- okay, n matches, < n replacements
+ i = i + 1
+ else
+ local cp = chainmores[chainlookup.type]
+ if not cp then
+ -- actually an error
+ logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
+ i = i + 1
+ else
+ local ok, n
+ head, start, ok, n = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence)
+ -- messy since last can be changed !
+ if ok then
+ done = true
+ -- skip next one(s) if ligature
+ i = i + (n or 1)
+ else
+ i = i + 1
+ end
+ end
+ end
+ if start then
+ start = start.next
+ else
+ -- weird
+ end
+ until i > nofchainlookups
+ end
+ else
+ local replacements = ck[7]
+ if replacements then
+ head, start, done = chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements) -- sequence
+ else
+ done = true -- can be meant to be skipped
+ if trace_contexts then
+ logprocess("%s: skipping match",cref(kind,chainname))
+ end
+ end
+ end
+ end
+ end
+ return head, start, done
+end
+
+-- Because we want to keep this elsewhere (an because speed is less an issue) we
+-- pass the font id so that the verbose variant can access the relevant helper tables.
+
+local verbose_handle_contextchain = function(font,...)
+ logwarning("no verbose handler installed, reverting to 'normal'")
+ otf.setcontextchain()
+ return normal_handle_contextchain(...)
+end
+
+otf.chainhandlers = {
+ normal = normal_handle_contextchain,
+ verbose = verbose_handle_contextchain,
+}
+
+function otf.setcontextchain(method)
+ if not method or method == "normal" or not otf.chainhandlers[method] then
+ if handlers.contextchain then -- no need for a message while making the format
+ logwarning("installing normal contextchain handler")
+ end
+ handlers.contextchain = normal_handle_contextchain
+ else
+ logwarning("installing contextchain handler %a",method)
+ local handler = otf.chainhandlers[method]
+ handlers.contextchain = function(...)
+ return handler(currentfont,...) -- hm, get rid of ...
+ end
+ end
+ handlers.gsub_context = handlers.contextchain
+ handlers.gsub_contextchain = handlers.contextchain
+ handlers.gsub_reversecontextchain = handlers.contextchain
+ handlers.gpos_contextchain = handlers.contextchain
+ handlers.gpos_context = handlers.contextchain
+end
+
+otf.setcontextchain()
+
+local missing = { } -- we only report once
+
+local function logprocess(...)
+ if trace_steps then
+ registermessage(...)
+ end
+ report_process(...)
+end
+
+local logwarning = report_process
+
+local function report_missing_cache(typ,lookup)
+ local f = missing[currentfont] if not f then f = { } missing[currentfont] = f end
+ local t = f[typ] if not t then t = { } f[typ] = t end
+ if not t[lookup] then
+ t[lookup] = true
+ logwarning("missing cache for lookup %a, type %a, font %a, name %a",lookup,typ,currentfont,tfmdata.properties.fullname)
+ end
+end
+
+local resolved = { } -- we only resolve a font,script,language pair once
+
+-- todo: pass all these 'locals' in a table
+
+local lookuphashes = { }
+
+setmetatableindex(lookuphashes, function(t,font)
+ local lookuphash = fontdata[font].resources.lookuphash
+ if not lookuphash or not next(lookuphash) then
+ lookuphash = false
+ end
+ t[font] = lookuphash
+ return lookuphash
+end)
+
+-- fonts.hashes.lookups = lookuphashes
+
+local autofeatures = fonts.analyzers.features -- was: constants
+
+local function initialize(sequence,script,language,enabled)
+ local features = sequence.features
+ if features then
+ for kind, scripts in next, features do
+ local valid = enabled[kind]
+ if valid then
+ local languages = scripts[script] or scripts[wildcard]
+ if languages and (languages[language] or languages[wildcard]) then
+ return { valid, autofeatures[kind] or false, sequence.chain or 0, kind, sequence }
+ end
+ end
+ end
+ end
+ return false
+end
+
+function otf.dataset(tfmdata,font) -- generic variant, overloaded in context
+ local shared = tfmdata.shared
+ local properties = tfmdata.properties
+ local language = properties.language or "dflt"
+ local script = properties.script or "dflt"
+ local enabled = shared.features
+ local res = resolved[font]
+ if not res then
+ res = { }
+ resolved[font] = res
+ end
+ local rs = res[script]
+ if not rs then
+ rs = { }
+ res[script] = rs
+ end
+ local rl = rs[language]
+ if not rl then
+ rl = {
+ -- indexed but we can also add specific data by key
+ }
+ rs[language] = rl
+ local sequences = tfmdata.resources.sequences
+-- setmetatableindex(rl, function(t,k)
+-- if type(k) == "number" then
+-- local v = enabled and initialize(sequences[k],script,language,enabled)
+-- t[k] = v
+-- return v
+-- end
+-- end)
+for s=1,#sequences do
+ local v = enabled and initialize(sequences[s],script,language,enabled)
+ if v then
+ rl[#rl+1] = v
+ end
+end
+ end
+ return rl
+end
+
+-- elseif id == glue_code then
+-- if p[5] then -- chain
+-- local pc = pp[32]
+-- if pc then
+-- start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4])
+-- if ok then
+-- done = true
+-- end
+-- if start then start = start.next end
+-- else
+-- start = start.next
+-- end
+-- else
+-- start = start.next
+-- end
+
+-- there will be a new direction parser (pre-parsed etc)
+
+-- less bytecode: 290 -> 254
+--
+-- attr = attr or false
+--
+-- local a = getattr(start,0)
+-- if (a == attr and (not attribute or getattr(start,a_state) == attribute)) or (not attribute or getattr(start,a_state) == attribute) then
+-- -- the action
+-- end
+
+local function featuresprocessor(head,font,attr)
+
+ local lookuphash = lookuphashes[font] -- we can also check sequences here
+
+ if not lookuphash then
+ return head, false
+ end
+
+ if trace_steps then
+ checkstep(head)
+ end
+
+ tfmdata = fontdata[font]
+ descriptions = tfmdata.descriptions
+ characters = tfmdata.characters
+ resources = tfmdata.resources
+
+ marks = resources.marks
+ anchorlookups = resources.lookup_to_anchor
+ lookuptable = resources.lookups
+ lookuptypes = resources.lookuptypes
+
+ currentfont = font
+ rlmode = 0
+
+ local sequences = resources.sequences
+ local done = false
+ local datasets = otf.dataset(tfmdata,font,attr)
+
+ local dirstack = { } -- could move outside function
+
+ -- We could work on sub start-stop ranges instead but I wonder if there is that
+ -- much speed gain (experiments showed that it made not much sense) and we need
+ -- to keep track of directions anyway. Also at some point I want to play with
+ -- font interactions and then we do need the full sweeps.
+
+ -- Keeping track of the headnode is needed for devanagari (I generalized it a bit
+ -- so that multiple cases are also covered.)
+
+ for s=1,#datasets do
+ local dataset = datasets[s]
+ featurevalue = dataset[1] -- todo: pass to function instead of using a global
+
+ local sequence = dataset[5] -- sequences[s] -- also dataset[5]
+ local rlparmode = 0
+ local topstack = 0
+ local success = false
+ local attribute = dataset[2]
+ local chain = dataset[3] -- sequence.chain or 0
+ local typ = sequence.type
+ local subtables = sequence.subtables
+ if chain < 0 then
+ -- this is a limited case, no special treatments like 'init' etc
+ local handler = handlers[typ]
+ -- we need to get rid of this slide! probably no longer needed in latest luatex
+ local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo
+ while start do
+ local id = start.id
+ if id == glyph_code then
+ if start.font == font and start.subtype<256 then
+ local a = start[0]
+ if a then
+ a = a == attr
+ else
+ a = true
+ end
+ if a then
+ for i=1,#subtables do
+ local lookupname = subtables[i]
+ local lookupcache = lookuphash[lookupname]
+ if lookupcache then
+ local lookupmatch = lookupcache[start.char]
+ if lookupmatch then
+ head, start, success = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+ if success then
+ break
+ end
+ end
+ else
+ report_missing_cache(typ,lookupname)
+ end
+ end
+ if start then start = start.prev end
+ else
+ start = start.prev
+ end
+ else
+ start = start.prev
+ end
+ else
+ start = start.prev
+ end
+ end
+ else
+ local handler = handlers[typ]
+ local ns = #subtables
+ local start = head -- local ?
+ rlmode = 0 -- to be checked ?
+ if ns == 1 then -- happens often
+ local lookupname = subtables[1]
+ local lookupcache = lookuphash[lookupname]
+ if not lookupcache then -- also check for empty cache
+ report_missing_cache(typ,lookupname)
+ else
+
+ local function subrun(start)
+ -- mostly for gsub, gpos would demand a more clever approach
+ local head = start
+ local done = false
+ while start do
+ local id = start.id
+ if id == glyph_code and start.font == font and start.subtype <256 then
+ local a = start[0]
+ if a then
+ a = (a == attr) and (not attribute or start[a_state] == attribute)
+ else
+ a = not attribute or start[a_state] == attribute
+ end
+ if a then
+ local lookupmatch = lookupcache[start.char]
+ if lookupmatch then
+ -- sequence kan weg
+ local ok
+ head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
+ if ok then
+ done = true
+ end
+ end
+ if start then start = start.next end
+ else
+ start = start.next
+ end
+ else
+ start = start.next
+ end
+ end
+ if done then
+ success = true
+ return head
+ end
+ end
+
+ local function kerndisc(disc) -- we can assume that prev and next are glyphs
+ local prev = disc.prev
+ local next = disc.next
+ if prev and next then
+ prev.next = next
+ -- next.prev = prev
+ local a = prev[0]
+ if a then
+ a = (a == attr) and (not attribute or prev[a_state] == attribute)
+ else
+ a = not attribute or prev[a_state] == attribute
+ end
+ if a then
+ local lookupmatch = lookupcache[prev.char]
+ if lookupmatch then
+ -- sequence kan weg
+ local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
+ if ok then
+ done = true
+ success = true
+ end
+ end
+ end
+ prev.next = disc
+ -- next.prev = disc
+ end
+ return next
+ end
+
+ while start do
+ local id = start.id
+ if id == glyph_code then
+ if start.font == font and start.subtype<256 then
+ local a = start[0]
+ if a then
+ a = (a == attr) and (not attribute or start[a_state] == attribute)
+ else
+ a = not attribute or start[a_state] == attribute
+ end
+ if a then
+ local lookupmatch = lookupcache[start.char]
+ if lookupmatch then
+ -- sequence kan weg
+ local ok
+ head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
+ if ok then
+ success = true
+ end
+ end
+ if start then start = start.next end
+ else
+ start = start.next
+ end
+ else
+ start = start.next
+ end
+ elseif id == disc_code then
+ -- mostly for gsub
+ if start.subtype == discretionary_code then
+ local pre = start.pre
+ if pre then
+ local new = subrun(pre)
+ if new then start.pre = new end
+ end
+ local post = start.post
+ if post then
+ local new = subrun(post)
+ if new then start.post = new end
+ end
+ local replace = start.replace
+ if replace then
+ local new = subrun(replace)
+ if new then start.replace = new end
+ end
+elseif typ == "gpos_single" or typ == "gpos_pair" then
+ kerndisc(start)
+ end
+ start = start.next
+ elseif id == whatsit_code then -- will be function
+ local subtype = start.subtype
+ if subtype == dir_code then
+ local dir = start.dir
+ if dir == "+TRT" or dir == "+TLT" then
+ topstack = topstack + 1
+ dirstack[topstack] = dir
+ elseif dir == "-TRT" or dir == "-TLT" then
+ topstack = topstack - 1
+ end
+ local newdir = dirstack[topstack]
+ if newdir == "+TRT" then
+ rlmode = -1
+ elseif newdir == "+TLT" then
+ rlmode = 1
+ else
+ rlmode = rlparmode
+ end
+ if trace_directions then
+ report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
+ end
+ elseif subtype == localpar_code then
+ local dir = start.dir
+ if dir == "TRT" then
+ rlparmode = -1
+ elseif dir == "TLT" then
+ rlparmode = 1
+ else
+ rlparmode = 0
+ end
+ -- one might wonder if the par dir should be looked at, so we might as well drop the next line
+ rlmode = rlparmode
+ if trace_directions then
+ report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
+ end
+ end
+ start = start.next
+ elseif id == math_code then
+ start = end_of_math(start).next
+ else
+ start = start.next
+ end
+ end
+ end
+ else
+
+ local function subrun(start)
+ -- mostly for gsub, gpos would demand a more clever approach
+ local head = start
+ local done = false
+ while start do
+ local id = start.id
+ if id == glyph_code and start.id == font and start.subtype <256 then
+ local a = start[0]
+ if a then
+ a = (a == attr) and (not attribute or start[a_state] == attribute)
+ else
+ a = not attribute or start[a_state] == attribute
+ end
+ if a then
+ for i=1,ns do
+ local lookupname = subtables[i]
+ local lookupcache = lookuphash[lookupname]
+ if lookupcache then
+ local lookupmatch = lookupcache[start.char]
+ if lookupmatch then
+ -- we could move all code inline but that makes things even more unreadable
+ local ok
+ head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+ if ok then
+ done = true
+ break
+ elseif not start then
+ -- don't ask why ... shouldn't happen
+ break
+ end
+ end
+ else
+ report_missing_cache(typ,lookupname)
+ end
+ end
+ if start then start = start.next end
+ else
+ start = start.next
+ end
+ else
+ start = start.next
+ end
+ end
+ if done then
+ success = true
+ return head
+ end
+ end
+
+ local function kerndisc(disc) -- we can assume that prev and next are glyphs
+ local prev = disc.prev
+ local next = disc.next
+ if prev and next then
+ prev.next = next
+ -- next.prev = prev
+ local a = prev[0]
+ if a then
+ a = (a == attr) and (not attribute or prev[a_state] == attribute)
+ else
+ a = not attribute or prev[a_state] == attribute
+ end
+ if a then
+ for i=1,ns do
+ local lookupname = subtables[i]
+ local lookupcache = lookuphash[lookupname]
+ if lookupcache then
+ local lookupmatch = lookupcache[prev.char]
+ if lookupmatch then
+ -- we could move all code inline but that makes things even more unreadable
+ local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+ if ok then
+ done = true
+ break
+ end
+ end
+ else
+ report_missing_cache(typ,lookupname)
+ end
+ end
+ end
+ prev.next = disc
+ -- next.prev = disc
+ end
+ return next
+ end
+
+ while start do
+ local id = start.id
+ if id == glyph_code then
+ if start.font == font and start.subtype<256 then
+ local a = start[0]
+ if a then
+ a = (a == attr) and (not attribute or start[a_state] == attribute)
+ else
+ a = not attribute or start[a_state] == attribute
+ end
+ if a then
+ for i=1,ns do
+ local lookupname = subtables[i]
+ local lookupcache = lookuphash[lookupname]
+ if lookupcache then
+ local lookupmatch = lookupcache[start.char]
+ if lookupmatch then
+ -- we could move all code inline but that makes things even more unreadable
+ local ok
+ head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+ if ok then
+ success = true
+ break
+ elseif not start then
+ -- don't ask why ... shouldn't happen
+ break
+ end
+ end
+ else
+ report_missing_cache(typ,lookupname)
+ end
+ end
+ if start then start = start.next end
+ else
+ start = start.next
+ end
+ else
+ start = start.next
+ end
+ elseif id == disc_code then
+ -- mostly for gsub
+ if start.subtype == discretionary_code then
+ local pre = start.pre
+ if pre then
+ local new = subrun(pre)
+ if new then start.pre = new end
+ end
+ local post = start.post
+ if post then
+ local new = subrun(post)
+ if new then start.post = new end
+ end
+ local replace = start.replace
+ if replace then
+ local new = subrun(replace)
+ if new then start.replace = new end
+ end
+elseif typ == "gpos_single" or typ == "gpos_pair" then
+ kerndisc(start)
+ end
+ start = start.next
+ elseif id == whatsit_code then
+ local subtype = start.subtype
+ if subtype == dir_code then
+ local dir = start.dir
+ if dir == "+TRT" or dir == "+TLT" then
+ topstack = topstack + 1
+ dirstack[topstack] = dir
+ elseif dir == "-TRT" or dir == "-TLT" then
+ topstack = topstack - 1
+ end
+ local newdir = dirstack[topstack]
+ if newdir == "+TRT" then
+ rlmode = -1
+ elseif newdir == "+TLT" then
+ rlmode = 1
+ else
+ rlmode = rlparmode
+ end
+ if trace_directions then
+ report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
+ end
+ elseif subtype == localpar_code then
+ local dir = start.dir
+ if dir == "TRT" then
+ rlparmode = -1
+ elseif dir == "TLT" then
+ rlparmode = 1
+ else
+ rlparmode = 0
+ end
+ rlmode = rlparmode
+ if trace_directions then
+ report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
+ end
+ end
+ start = start.next
+ elseif id == math_code then
+ start = end_of_math(start).next
+ else
+ start = start.next
+ end
+ end
+ end
+ end
+ if success then
+ done = true
+ end
+ if trace_steps then -- ?
+ registerstep(head)
+ end
+ end
+ return head, done
+end
+
+local function generic(lookupdata,lookupname,unicode,lookuphash)
+ local target = lookuphash[lookupname]
+ if target then
+ target[unicode] = lookupdata
+ else
+ lookuphash[lookupname] = { [unicode] = lookupdata }
+ end
+end
+
+local action = {
+
+ substitution = generic,
+ multiple = generic,
+ alternate = generic,
+ position = generic,
+
+ ligature = function(lookupdata,lookupname,unicode,lookuphash)
+ local target = lookuphash[lookupname]
+ if not target then
+ target = { }
+ lookuphash[lookupname] = target
+ end
+ for i=1,#lookupdata do
+ local li = lookupdata[i]
+ local tu = target[li]
+ if not tu then
+ tu = { }
+ target[li] = tu
+ end
+ target = tu
+ end
+ target.ligature = unicode
+ end,
+
+ pair = function(lookupdata,lookupname,unicode,lookuphash)
+ local target = lookuphash[lookupname]
+ if not target then
+ target = { }
+ lookuphash[lookupname] = target
+ end
+ local others = target[unicode]
+ local paired = lookupdata[1]
+ if others then
+ others[paired] = lookupdata
+ else
+ others = { [paired] = lookupdata }
+ target[unicode] = others
+ end
+ end,
+
+}
+
+local function prepare_lookups(tfmdata)
+
+ local rawdata = tfmdata.shared.rawdata
+ local resources = rawdata.resources
+ local lookuphash = resources.lookuphash
+ local anchor_to_lookup = resources.anchor_to_lookup
+ local lookup_to_anchor = resources.lookup_to_anchor
+ local lookuptypes = resources.lookuptypes
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+
+ -- we cannot free the entries in the descriptions as sometimes we access
+ -- then directly (for instance anchors) ... selectively freeing does save
+ -- much memory as it's only a reference to a table and the slot in the
+ -- description hash is not freed anyway
+
+ for unicode, character in next, characters do -- we cannot loop over descriptions !
+
+ local description = descriptions[unicode]
+
+ if description then
+
+ local lookups = description.slookups
+ if lookups then
+ for lookupname, lookupdata in next, lookups do
+ action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash)
+ end
+ end
+
+ local lookups = description.mlookups
+ if lookups then
+ for lookupname, lookuplist in next, lookups do
+ local lookuptype = lookuptypes[lookupname]
+ for l=1,#lookuplist do
+ local lookupdata = lookuplist[l]
+ action[lookuptype](lookupdata,lookupname,unicode,lookuphash)
+ end
+ end
+ end
+
+ local list = description.kerns
+ if list then
+ for lookup, krn in next, list do -- ref to glyph, saves lookup
+ local target = lookuphash[lookup]
+ if target then
+ target[unicode] = krn
+ else
+ lookuphash[lookup] = { [unicode] = krn }
+ end
+ end
+ end
+
+ local list = description.anchors
+ if list then
+ for typ, anchors in next, list do -- types
+ if typ == "mark" or typ == "cexit" then -- or entry?
+ for name, anchor in next, anchors do
+ local lookups = anchor_to_lookup[name]
+ if lookups then
+ for lookup, _ in next, lookups do
+ local target = lookuphash[lookup]
+ if target then
+ target[unicode] = anchors
+ else
+ lookuphash[lookup] = { [unicode] = anchors }
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ end
+
+ end
+
+end
+
+local function split(replacement,original)
+ local result = { }
+ for i=1,#replacement do
+ result[original[i]] = replacement[i]
+ end
+ return result
+end
+
+local valid = {
+ coverage = { chainsub = true, chainpos = true, contextsub = true },
+ reversecoverage = { reversesub = true },
+ glyphs = { chainsub = true, chainpos = true },
+}
+
+local function prepare_contextchains(tfmdata)
+ local rawdata = tfmdata.shared.rawdata
+ local resources = rawdata.resources
+ local lookuphash = resources.lookuphash
+ local lookups = rawdata.lookups
+ if lookups then
+ for lookupname, lookupdata in next, rawdata.lookups do
+ local lookuptype = lookupdata.type
+ if lookuptype then
+ local rules = lookupdata.rules
+ if rules then
+ local format = lookupdata.format
+ local validformat = valid[format]
+ if not validformat then
+ report_prepare("unsupported format %a",format)
+ elseif not validformat[lookuptype] then
+ -- todo: dejavu-serif has one (but i need to see what use it has)
+ report_prepare("unsupported format %a, lookuptype %a, lookupname %a",format,lookuptype,lookupname)
+ else
+ local contexts = lookuphash[lookupname]
+ if not contexts then
+ contexts = { }
+ lookuphash[lookupname] = contexts
+ end
+ local t, nt = { }, 0
+ for nofrules=1,#rules do
+ local rule = rules[nofrules]
+ local current = rule.current
+ local before = rule.before
+ local after = rule.after
+ local replacements = rule.replacements
+ local sequence = { }
+ local nofsequences = 0
+ -- Eventually we can store start, stop and sequence in the cached file
+ -- but then less sharing takes place so best not do that without a lot
+ -- of profiling so let's forget about it.
+ if before then
+ for n=1,#before do
+ nofsequences = nofsequences + 1
+ sequence[nofsequences] = before[n]
+ end
+ end
+ local start = nofsequences + 1
+ for n=1,#current do
+ nofsequences = nofsequences + 1
+ sequence[nofsequences] = current[n]
+ end
+ local stop = nofsequences
+ if after then
+ for n=1,#after do
+ nofsequences = nofsequences + 1
+ sequence[nofsequences] = after[n]
+ end
+ end
+ if sequence[1] then
+ -- Replacements only happen with reverse lookups as they are single only. We
+ -- could pack them into current (replacement value instead of true) and then
+ -- use sequence[start] instead but it's somewhat ugly.
+ nt = nt + 1
+ t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements }
+ for unic, _ in next, sequence[start] do
+ local cu = contexts[unic]
+ if not cu then
+ contexts[unic] = t
+ end
+ end
+ end
+ end
+ end
+ else
+ -- no rules
+ end
+ else
+ report_prepare("missing lookuptype for lookupname %a",lookupname)
+ end
+ end
+ end
+end
+
+-- we can consider lookuphash == false (initialized but empty) vs lookuphash == table
+
+local function featuresinitializer(tfmdata,value)
+ if true then -- value then
+ -- beware we need to use the topmost properties table
+ local rawdata = tfmdata.shared.rawdata
+ local properties = rawdata.properties
+ if not properties.initialized then
+ local starttime = trace_preparing and os.clock()
+ local resources = rawdata.resources
+ resources.lookuphash = resources.lookuphash or { }
+ prepare_contextchains(tfmdata)
+ prepare_lookups(tfmdata)
+ properties.initialized = true
+ if trace_preparing then
+ report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,tfmdata.properties.fullname)
+ end
+ end
+ end
+end
+
+registerotffeature {
+ name = "features",
+ description = "features",
+ default = true,
+ initializers = {
+ position = 1,
+ node = featuresinitializer,
+ },
+ processors = {
+ node = featuresprocessor,
+ }
+}
+
+-- This can be used for extra handlers, but should be used with care!
+
+otf.handlers = handlers
diff --git a/luaotfload-fonts-tfm.lua b/src/luaotfload-fonts-tfm.lua
index b9bb1bd..b9bb1bd 100644
--- a/luaotfload-fonts-tfm.lua
+++ b/src/luaotfload-fonts-tfm.lua
diff --git a/luaotfload-letterspace.lua b/src/luaotfload-letterspace.lua
index 7c5a967..20f29f5 100644
--- a/luaotfload-letterspace.lua
+++ b/src/luaotfload-letterspace.lua
@@ -1,11 +1,14 @@
if not modules then modules = { } end modules ['letterspace'] = {
- version = "2.4",
- comment = "companion to luaotfload.lua",
+ version = "2.5",
+ comment = "companion to luaotfload-main.lua",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL; adapted by Philipp Gesang",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
+local log = luaotfload.log
+local report = log.report
+
local getmetatable = getmetatable
local require = require
local setmetatable = setmetatable
@@ -512,10 +515,10 @@ otffeatures.register {
local initializecompatfontkerning = function (tfmdata, percentage)
local factor = tonumber (percentage)
if not factor then
- logs.names_report ("both", 0, "letterspace",
- "Invalid argument to letterspace: %s (type %q), " ..
- "was expecting percentage as Lua number instead.",
- percentage, type (percentage))
+ report ("both", 0, "letterspace",
+ "Invalid argument to letterspace: %s (type %q), " ..
+ "was expecting percentage as Lua number instead.",
+ percentage, type (percentage))
return
end
return initializefontkerning (tfmdata, factor * 0.01)
diff --git a/luaotfload-loaders.lua b/src/luaotfload-loaders.lua
index 523dc24..2aa8c7c 100644
--- a/luaotfload-loaders.lua
+++ b/src/luaotfload-loaders.lua
@@ -1,6 +1,6 @@
if not modules then modules = { } end modules ["loaders"] = {
- version = "2.4",
- comment = "companion to luaotfload.lua",
+ version = "2.5",
+ comment = "companion to luaotfload-main.lua",
author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
diff --git a/luaotfload-override.lua b/src/luaotfload-log.lua
index fe70dcb..5698c84 100644
--- a/luaotfload-override.lua
+++ b/src/luaotfload-log.lua
@@ -1,9 +1,9 @@
-if not modules then modules = { } end modules ['luat-ovr'] = {
- version = "2.4",
- comment = "companion to luatex-*.tex",
+if not modules then modules = { } end modules ["luaotfload-log"] = {
+ version = "2.5",
+ comment = "companion to Luaotfload",
author = "Khaled Hosny, Elie Roux, Philipp Gesang",
copyright = "Luaotfload Development Team",
- license = "GNU GPL v2"
+ license = "GNU GPL v2.0"
}
--[[doc--
@@ -13,22 +13,24 @@ hand, the more efficient approach followed by Context isn’t an option
because we lack a user interface to toggle per-subsystem tracing.
--doc]]--
-local module_name = "luaotfload"
+local module_name = "luaotfload" --- prefix for messages
+
+luaotfload = luaotfload or { }
+luaotfload.log = luaotfload.log or { }
+local log = luaotfload.log
local ioopen = io.open
local iowrite = io.write
local lfsisdir = lfs.isdir
local lfsisfile = lfs.isfile
-local md5sumhexa = md5.sumhexa
local osdate = os.date
local ostime = os.time
+local osuuid = os.uuid
local select = select
local stringformat = string.format
local stringsub = string.sub
local tableconcat = table.concat
-local texio_write_nl = texio.write_nl
local texiowrite_nl = texio.write_nl
-local texio_write = texio.write
local texiowrite = texio.write
local type = type
@@ -50,17 +52,13 @@ local set_loglevel = function (n)
end
return true
end
-logs.setloglevel = set_loglevel
-logs.set_loglevel = set_loglevel
-logs.set_log_level = set_loglevel --- accomodating lazy typists
+log.set_loglevel = set_loglevel
--- unit -> int
local get_loglevel = function ( )
return loglevel
end
-logs.getloglevel = get_loglevel
-logs.get_loglevel = get_loglevel
-logs.get_log_level = get_loglevel
+log.get_loglevel = get_loglevel
local writeln --- pointer to terminal/log writer
local statusln --- terminal writer that reuses the current line
@@ -77,8 +75,7 @@ local choose_logfile = function ( )
if lfsisdir (tmppath) then
local fname
repeat --- ensure that file of that name doesn’t exist
- fname = tmppath .. "/luaotfload-log-"
- .. stringsub (md5sumhexa (ostime ()), 1, 8)
+ fname = tmppath .. "/luaotfload-log-" .. osuuid()
until not lfsisfile (fname)
iowrite (stringformat (log_msg, fname, fname))
return ioopen (fname, "w")
@@ -131,9 +128,9 @@ local set_logout = function (s, finalizers)
return finalizers
end
-logs.set_logout = set_logout
+log.set_logout = set_logout
-local log = function (category, fmt, ...)
+local basic_logger = function (category, fmt, ...)
local res = { module_name, "|", category, ":" }
if fmt then
res [#res + 1] = stringformat (fmt, ...)
@@ -203,7 +200,7 @@ local level_ids = { common = 1, loading = 2, search = 3 }
--[[doc--
- The names_report logger is used more or less all over luaotfload.
+ The report() logger is used more or less all over luaotfload.
Its requirements are twofold:
1) Provide two logging channels, the terminal and the log file;
@@ -222,12 +219,12 @@ local level_ids = { common = 1, loading = 2, search = 3 }
over. (This is a little sub-optimal performance-wise since the
function calls to the logger are executed regardless.) The log
level during a Luatex run can be adjusted by setting the “loglevel”
- field in config.luaotfload, or by calling logs.set_loglevel() as
+ field in config.luaotfload, or by calling log.set_loglevel() as
defined above.
--doc]]--
-local names_report = function (mode, lvl, ...)
+local report = function (mode, lvl, ...)
if type(lvl) == "string" then
lvl = level_ids[lvl]
end
@@ -235,9 +232,9 @@ local names_report = function (mode, lvl, ...)
if loglevel >= lvl then
if mode == "log" then
- log (...)
+ basic_logger (...)
elseif mode == "both" and logout ~= "redirect" then
- log (...)
+ basic_logger (...)
stdout (writeln, ...)
else
stdout (writeln, ...)
@@ -245,7 +242,7 @@ local names_report = function (mode, lvl, ...)
end
end
-logs.names_report = names_report
+log.report = report
--[[doc--
@@ -269,10 +266,10 @@ logs.names_report = names_report
local status_logger = function (mode, ...)
if mode == "log" then
- log (...)
+ basic_logger (...)
else
if mode == "both" and logout ~= "redirect" then
- log (...)
+ basic_logger (...)
stdout (statusln, ...)
else
stdout (statusln, ...)
@@ -304,7 +301,7 @@ local status_start = function (low, high)
or os.getenv "TERM" == "dumb"
then
status_writer = function (mode, ...)
- names_report (mode, high, ...)
+ report (mode, high, ...)
end
return
end
@@ -313,7 +310,7 @@ local status_start = function (low, high)
status_writer = status_logger
else
status_writer = function (mode, ...)
- names_report (mode, high, ...)
+ report (mode, high, ...)
end
end
end
@@ -334,9 +331,9 @@ local status_stop = function (...)
end
end
-logs.names_status = function (...) status_writer (...) end
-logs.names_status_start = status_start
-logs.names_status_stop = status_stop
+log.names_status = function (...) status_writer (...) end
+log.names_status_start = status_start
+log.names_status_stop = status_stop
--[[doc--
@@ -351,7 +348,7 @@ logs.names_status_stop = status_stop
--doc]]--
local texioreporter = function (message)
- names_report("log", 2, message)
+ report ("log", 2, message)
end
texio.reporter = texioreporter
@@ -389,10 +386,10 @@ if fonts then --- need to be running TeX
if k == "unicodes" then
local glyphlist = resolvers.findfile"luaotfload-glyphlist.lua"
if glyphlist then
- names_report("log", 1, "load", "loading the Adobe glyph list")
+ report ("log", 1, "load", "loading the Adobe glyph list")
else
glyphlist = resolvers.findfile"font-age.lua"
- names_report("both", 0, "load",
+ report ("both", 0, "load",
"loading the extended glyph list from ConTeXt")
end
local unicodes = dofile(glyphlist)
diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
new file mode 100644
index 0000000..6616468
--- /dev/null
+++ b/src/luaotfload-main.lua
@@ -0,0 +1,708 @@
+-----------------------------------------------------------------------
+-- FILE: luaotfload-main.lua
+-- DESCRIPTION: Luatex fontloader initialization
+-- REQUIREMENTS: luatex v.0.78 or later; the lualibs package
+-- AUTHOR: Élie Roux, Khaled Hosny, Philipp Gesang
+-- VERSION: same as Luaotfload
+-- MODIFIED: 2014-02-14 22:51:09+0100
+-----------------------------------------------------------------------
+--
+--- Note:
+--- This file was part of the original luaotfload.dtx and has been
+--- converted to a pure Lua file during the transition from Luaotfload
+--- version 2.4 to 2.5. Thus, the comments are still in TeX (Latex)
+--- markup.
+
+if not modules then modules = { } end modules ["luaotfload-main"] = {
+ version = "2.5",
+ comment = "fontloader initialization",
+ author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "GNU General Public License v. 2.0"
+}
+
+
+--[[doc--
+
+ This file initializes the system and loads the font loader. To
+ minimize potential conflicts between other packages and the code
+ imported from \CONTEXT, several precautions are in order. Some of
+ the functionality that the font loader expects to be present, like
+ raw access to callbacks, are assumed to have been disabled by
+ \identifier{luatexbase} when this file is processed. In some cases
+ it is possible to trick it by putting dummies into place and
+ restoring the behavior from \identifier{luatexbase} after
+ initilization. Other cases such as attribute allocation require
+ that we hook the functionality from \identifier{luatexbase} into
+ locations where they normally wouldn’t be.
+
+ Anyways we can import the code base without modifications, which is
+ due mostly to the extra effort by Hans Hagen to make \LUATEX-Fonts
+ self-contained and encapsulate it, and especially due to his
+ willingness to incorporate our suggestions.
+
+--doc]]--
+
+local initial_log_level = 0
+
+luaotfload = luaotfload or { }
+local luaotfload = luaotfload
+luaotfload.log = luaotfload.log or { }
+
+luaotfload.module = {
+ name = "luaotfload-main",
+ version = 2.50000,
+ date = "2014/**/**",
+ description = "OpenType layout system.",
+ author = "Elie Roux & Hans Hagen",
+ copyright = "Elie Roux",
+ license = "GPL v2.0"
+}
+
+local luatexbase = luatexbase
+
+local setmetatable = setmetatable
+local type, next = type, next
+local stringlower = string.lower
+local stringformat = string.format
+
+local kpsefind_file = kpse.find_file
+local lfsisfile = lfs.isfile
+
+local add_to_callback = luatexbase.add_to_callback
+local create_callback = luatexbase.create_callback
+local reset_callback = luatexbase.reset_callback
+local call_callback = luatexbase.call_callback
+
+local dummy_function = function () end --- XXX this will be moved to the luaotfload namespace when we have the init module
+
+local error, warning, info, log =
+ luatexbase.provides_module(luaotfload.module)
+
+luaotfload.log.tex = {
+ error = error,
+ warning = warning,
+ info = info,
+ log = log,
+}
+
+--[[doc--
+
+ We set the minimum version requirement for \LUATEX to v0.76,
+ because the font loader requires recent features like direct
+ attribute indexing and \luafunction{node.end_of_math()} that aren’t
+ available in earlier versions.\footnote{%
+ See Taco’s announcement of v0.76:
+ \url{http://comments.gmane.org/gmane.comp.tex.luatex.user/4042}
+ and this commit by Hans that introduced those features.
+ \url{http://repo.or.cz/w/context.git/commitdiff/a51f6cf6ee087046a2ae5927ed4edff0a1acec1b}.
+ }
+
+--doc]]--
+
+local min_luatex_version = 76
+
+if tex.luatexversion < min_luatex_version then
+ warning ("LuaTeX v%.2f is old, v%.2f or later is recommended.",
+ tex.luatexversion / 100,
+ min_luatex_version / 100)
+ --- we install a fallback for older versions as a safety
+ if not node.end_of_math then
+ local math_t = node.id "math"
+ local traverse_nodes = node.traverse_id
+ node.end_of_math = function (n)
+ for n in traverse_nodes (math_t, n.next) do
+ return n
+ end
+ end
+ end
+end
+
+--[[doc--
+
+ \subsection{Module loading}
+ We load the files imported from \CONTEXT with this function. It
+ automatically prepends the prefix \fileent{luaotfload-} to its
+ argument, so we can refer to the files with their actual \CONTEXT
+ name.
+
+--doc]]--
+
+local fl_prefix = "luaotfload" -- “luatex” for luatex-plain
+local loadmodule = function (name)
+ require (fl_prefix .."-"..name)
+end
+
+loadmodule "log.lua" --- log messages
+--loadmodule "parsers.lua" --- new in 2.5; fonts.conf and syntax
+--loadmodule "configuration.lua" --- configuration options
+
+local log = luaotfload.log
+local logreport = log.report
+
+log.set_loglevel (default_log_level)
+
+--[[doc--
+
+ Before \TeX Live 2013 version, \LUATEX had a bug that made ofm fonts
+ fail when called with their extension. There was a side-effect making
+ ofm totally unloadable when luaotfload was present. The following
+ lines are a patch for this bug. The utility of these lines is
+ questionable as they are not necessary since \TeX Live 2013. They
+ should be removed in the next version.
+
+--doc]]--
+
+local Cs, P, lpegmatch = lpeg.Cs, lpeg.P, lpeg.match
+
+local p_dot, p_slash = P".", P"/"
+local p_suffix = (p_dot * (1 - p_dot - p_slash)^1 * P(-1)) / ""
+local p_removesuffix = Cs((p_suffix + 1)^1)
+
+local find_vf_file = function (name)
+ local fullname = kpsefind_file(name, "ovf")
+ if not fullname then
+ --fullname = kpsefind_file(file.removesuffix(name), "ovf")
+ fullname = kpsefind_file(lpegmatch(p_removesuffix, name), "ovf")
+ end
+ if fullname then
+ logreport ("log", 0, "main",
+ "loading virtual font file %s.", fullname)
+ end
+ return fullname
+end
+
+--[[doc--
+
+ \subsection{Preparing the Font Loader}
+ We treat the fontloader as a black box so behavior is consistent
+ between formats.
+ We load the fontloader code directly in the same fashion as the
+ Plain format \identifier{luatex-fonts} that is part of Context.
+ How this is executed depends on the presence on the
+ \emphasis{merged font loader code}.
+ In \identifier{luaotfload} this is contained in the file
+ \fileent{luaotfload-merged.lua}.
+ If this file cannot be found, the original libraries from \CONTEXT
+ of which the merged code was composed are loaded instead.
+ Since these files are not shipped with Luaotfload, an installation
+ of Context is required.
+ (Since we pull the fontloader directly from the Context minimals,
+ the necessary Context version is likely to be more recent than that
+ of other TeX distributions like Texlive.)
+ The imported font loader will call \luafunction{callback.register}
+ once while reading \fileent{font-def.lua}.
+ This is unavoidable unless we modify the imported files, but
+ harmless if we make it call a dummy instead.
+ However, this problem might vanish if we decide to do the merging
+ ourselves, like the \identifier{lualibs} package does.
+ With this step we would obtain the freedom to load our own
+ overrides in the process right where they are needed, at the cost
+ of losing encapsulation.
+ The decision on how to progress is currently on indefinite hold.
+
+--doc]]--
+
+local starttime = os.gettimeofday ()
+local trapped_register = callback.register
+callback.register = dummy_function
+
+--[[doc--
+
+ By default, the fontloader requires a number of \emphasis{private
+ attributes} for internal use.
+ These must be kept consistent with the attribute handling methods
+ as provided by \identifier{luatexbase}.
+ Our strategy is to override the function that allocates new
+ attributes before we initialize the font loader, making it a
+ wrapper around \luafunction{luatexbase.new_attribute}.\footnote{%
+ Many thanks, again, to Hans Hagen for making this part
+ configurable!
+ }
+ The attribute identifiers are prefixed “\fileent{luaotfload@}” to
+ avoid name clashes.
+
+--doc]]--
+
+do
+ local new_attribute = luatexbase.new_attribute
+ local the_attributes = luatexbase.attributes
+
+ attributes = attributes or { }
+
+ attributes.private = function (name)
+ local attr = "luaotfload@" .. name --- used to be: “otfl@”
+ local number = the_attributes[attr]
+ if not number then
+ number = new_attribute(attr)
+ end
+ return number
+ end
+end
+
+--[[doc--
+
+ These next lines replicate the behavior of
+ \fileent{luatex-fonts.lua}.
+
+--doc]]--
+
+local context_environment = { }
+
+local push_namespaces = function ()
+ logreport ("log", 1, "main", "push namespace for font loader")
+ local normalglobal = { }
+ for k, v in next, _G do
+ normalglobal[k] = v
+ end
+ return normalglobal
+end
+
+local pop_namespaces = function (normalglobal, isolate)
+ if normalglobal then
+ local _G = _G
+ local mode = "non-destructive"
+ if isolate then mode = "destructive" end
+ logreport ("log", 1, "main", "pop namespace from font loader -- " .. mode)
+ for k, v in next, _G do
+ if not normalglobal[k] then
+ context_environment[k] = v
+ if isolate then
+ _G[k] = nil
+ end
+ end
+ end
+ for k, v in next, normalglobal do
+ _G[k] = v
+ end
+ -- just to be sure:
+ setmetatable(context_environment,_G)
+ else
+ logreport ("both", 0, "main",
+ "irrecoverable error during pop_namespace: no globals to restore")
+ os.exit()
+ end
+end
+
+luaotfload.context_environment = context_environment
+luaotfload.push_namespaces = push_namespaces
+luaotfload.pop_namespaces = pop_namespaces
+
+local our_environment = push_namespaces()
+
+--[[doc--
+
+ The font loader requires that the attribute with index zero be
+ zero. We happily oblige.
+ (Cf. \fileent{luatex-fonts-nod.lua}.)
+
+--doc]]--
+
+tex.attribute[0] = 0
+
+--[[doc--
+
+ Now that things are sorted out we can finally load the fontloader.
+
+--doc]]--
+
+loadmodule "fontloader.lua"
+---loadmodule"font-odv.lua" --- <= Devanagari support from Context
+
+if fonts then
+
+ if not fonts._merge_loaded_message_done_ then
+ logreport ("log", 5, "main", [["I am using the merged fontloader here.]])
+ logreport ("log", 5, "main", [[ If you run into problems or experience unexpected]])
+ logreport ("log", 5, "main", [[ behaviour, and if you have ConTeXt installed you can try]])
+ logreport ("log", 5, "main", [[ to delete the file 'luaotfload-fontloader.lua' as I might]])
+ logreport ("log", 5, "main", [[ then use the possibly updated libraries. The merged]])
+ logreport ("log", 5, "main", [[ version is not supported as it is a frozen instance.]])
+ logreport ("log", 5, "main", [[ Problems can be reported to the ConTeXt mailing list."]])
+ end
+ fonts._merge_loaded_message_done_ = true
+
+else--- the loading sequence is known to change, so this might have to
+ --- be updated with future updates!
+ --- do not modify it though unless there is a change to the merged
+ --- package!
+ loadmodule("l-lua.lua")
+ loadmodule("l-lpeg.lua")
+ loadmodule("l-function.lua")
+ loadmodule("l-string.lua")
+ loadmodule("l-table.lua")
+ loadmodule("l-io.lua")
+ loadmodule("l-file.lua")
+ loadmodule("l-boolean.lua")
+ loadmodule("l-math.lua")
+ loadmodule("util-str.lua")
+ loadmodule('luatex-basics-gen.lua')
+ loadmodule('data-con.lua')
+ loadmodule('luatex-basics-nod.lua')
+ loadmodule('font-ini.lua')
+ loadmodule('font-con.lua')
+ loadmodule('luatex-fonts-enc.lua')
+ loadmodule('font-cid.lua')
+ loadmodule('font-map.lua')
+ loadmodule('luatex-fonts-syn.lua')
+ loadmodule('luatex-fonts-tfm.lua')
+ loadmodule('font-oti.lua')
+ loadmodule('font-otf.lua')
+ loadmodule('font-otb.lua')
+ loadmodule('luatex-fonts-inj.lua') --> since 2014-01-07, replaces node-inj.lua
+ loadmodule('font-ota.lua')
+ loadmodule('luatex-fonts-otn.lua') --> since 2014-01-07, replaces font-otn.lua
+ loadmodule('font-otp.lua') --> since 2013-04-23
+ loadmodule('luatex-fonts-lua.lua')
+ loadmodule('font-def.lua')
+ loadmodule('luatex-fonts-def.lua')
+ loadmodule('luatex-fonts-ext.lua')
+ loadmodule('luatex-fonts-cbk.lua')
+end --- non-merge fallback scope
+
+--[[doc--
+
+ Here we adjust the globals created during font loader
+ initialization. If the second argument to
+ \luafunction{pop_namespaces()} is \verb|true| this will restore the
+ state of \luafunction{_G}, eliminating every global generated since
+ the last call to \luafunction{push_namespaces()}. At the moment we
+ see no reason to do this, and since the font loader is considered
+ an essential part of \identifier{luatex} as well as a very well
+ organized piece of code, we happily concede it the right to add to
+ \luafunction{_G} if needed.
+
+--doc]]--
+
+pop_namespaces(our_environment, false)-- true)
+
+logreport ("both", 0, "main",
+ "fontloader loaded in %0.3f seconds", os.gettimeofday()-starttime)
+
+--[[doc--
+
+ \subsection{Callbacks}
+ After the fontloader is ready we can restore the callback trap from
+ \identifier{luatexbase}.
+
+--doc]]--
+
+callback.register = trapped_register
+
+--[[doc--
+
+ We do our own callback handling with the means provided by
+ luatexbase.
+ Note: \luafunction{pre_linebreak_filter} and
+ \luafunction{hpack_filter} are coupled in \CONTEXT in the concept
+ of \emphasis{node processor}.
+
+--doc]]--
+
+add_to_callback("pre_linebreak_filter",
+ nodes.simple_font_handler,
+ "luaotfload.node_processor",
+ 1)
+add_to_callback("hpack_filter",
+ nodes.simple_font_handler,
+ "luaotfload.node_processor",
+ 1)
+add_to_callback("find_vf_file",
+ find_vf_file, "luaotfload.find_vf_file")
+
+loadmodule "override.lua" --- load glyphlist on demand
+
+--[[doc--
+
+ Now we load the modules written for \identifier{luaotfload}.
+
+--doc]]--
+
+loadmodule "parsers.lua" --- fonts.conf and syntax
+loadmodule "configuration.lua" --- configuration options
+
+if not config.actions.apply_defaults () then
+ logreport ("log", 0, "load", "Configuration unsuccessful.")
+end
+
+loadmodule "loaders.lua" --- Type1 font wrappers
+loadmodule "database.lua" --- Font management.
+loadmodule "colors.lua" --- Per-font colors.
+
+if not config.actions.reconfigure () then
+ logreport ("log", 0, "load", "Post-configuration hooks failed.")
+end
+
+--[[doc--
+
+ Relying on the \verb|name:| resolver for everything has been the
+ source of permanent trouble with the database.
+ With the introduction of the new syntax parser we now have enough
+ granularity to distinguish between the \XETEX emulation layer and
+ the genuine \verb|name:| and \verb|file:| lookups of \LUATEX-Fonts.
+ Another benefit is that we can now easily plug in or replace new
+ lookup behaviors if necessary.
+ The name resolver remains untouched, but it calls
+ \luafunction{fonts.names.resolve()} internally anyways (see
+ \fileent{luaotfload-database.lua}).
+
+--doc]]--
+
+local filesuffix = file.suffix
+local fileremovesuffix = file.removesuffix
+local request_resolvers = fonts.definers.resolvers
+local formats = fonts.formats
+local names = fonts.names
+formats.ofm = "type1"
+
+fonts.encodings.known = fonts.encodings.known or { }
+
+--[[doc--
+
+ \identifier{luaotfload} promises easy access to system fonts.
+ Without additional precautions, this cannot be achieved by
+ \identifier{kpathsea} alone, because it searches only the
+ \fileent{texmf} directories by default.
+ Although it is possible for \identifier{kpathsea} to include extra
+ paths by adding them to the \verb|OSFONTDIR| environment variable,
+ this is still short of the goal »\emphasis{it just works!}«.
+ When building the font database \identifier{luaotfload} scans
+ system font directories anyways, so we already have all the
+ information for looking sytem fonts.
+ With the release version 2.2 the file names are indexed in the
+ database as well and we are ready to resolve \verb|file:| lookups
+ this way.
+ Thus we no longer need to call the \identifier{kpathsea} library in
+ most cases when looking up font files, only when generating the
+ database, and when verifying the existence of a file in the
+ \fileent{texmf} tree.
+
+--doc]]--
+
+local resolve_file = names.font_file_lookup
+
+local file_resolver = function (specification)
+ local name = resolve_file (specification.name)
+ local suffix = filesuffix(name)
+ if formats[suffix] then
+ specification.forced = stringlower (suffix)
+ specification.forcedname = file.removesuffix(name)
+ else
+ specification.name = name
+ end
+end
+
+request_resolvers.file = file_resolver
+
+--[[doc--
+
+ We classify as \verb|anon:| those requests that have neither a
+ prefix nor brackets. According to Khaled\footnote{%
+ \url{https://github.com/phi-gamma/luaotfload/issues/4#issuecomment-17090553}.
+ }
+ they are the \XETEX equivalent of a \verb|name:| request, so we
+ will be treating them as such.
+
+--doc]]--
+
+--request_resolvers.anon = request_resolvers.name
+
+--[[doc--
+
+ There is one drawback, though.
+ This syntax is also used for requesting fonts in \identifier{Type1}
+ (\abbrev{tfm}, \abbrev{ofm}) format.
+ These are essentially \verb|file:| lookups and must be caught
+ before the \verb|name:| resolver kicks in, lest they cause the
+ database to update.
+ Even if we were to require the \verb|file:| prefix for all
+ \identifier{Type1} requests, tests have shown that certain fonts
+ still include further fonts (e.~g. \fileent{omlgcb.ofm} will ask
+ for \fileent{omsecob.tfm}) \emphasis{using the old syntax}.
+ For this reason, we introduce an extra check with an early return.
+
+--doc]]--
+
+local type1_formats = { "tfm", "ofm", "TFM", "OFM", }
+
+request_resolvers.anon = function (specification)
+ local name = specification.name
+ for i=1, #type1_formats do
+ local format = type1_formats[i]
+ if resolvers.findfile(name, format) then
+ specification.forcedname = file.addsuffix(name, format)
+ specification.forced = format
+ return
+ end
+ end
+ --- under some weird circumstances absolute paths get
+ --- passed to the definer; we have to catch them
+ --- before the name: resolver misinterprets them.
+ name = specification.specification
+ local exists, _ = lfsisfile(name)
+ if exists then --- garbage; we do this because we are nice,
+ --- not because it is correct
+ logreport ("log", 1, "load", "file %q exists", name)
+ logreport ("log", 1, "load",
+ "... overriding borked anon: lookup with path: lookup")
+ specification.name = name
+ request_resolvers.path(specification)
+ return
+ end
+ request_resolvers.name(specification)
+end
+
+--[[doc--
+
+ Prior to version 2.2, \identifier{luaotfload} did not distinguish
+ \verb|file:| and \verb|path:| lookups, causing complications with
+ the resolver.
+ Now we test if the requested name is an absolute path in the file
+ system, otherwise we fall back to the \verb|file:| lookup.
+
+--doc]]--
+
+request_resolvers.path = function (specification)
+ local name = specification.name
+ local exists, _ = lfsisfile(name)
+ if not exists then -- resort to file: lookup
+ logreport ("log", 0, "load",
+ "path lookup of %q unsuccessful, falling back to file:",
+ name)
+ file_resolver (specification)
+ else
+ local suffix = filesuffix (name)
+ if formats[suffix] then
+ specification.forced = stringlower (suffix)
+ specification.name = file.removesuffix(name)
+ specification.forcedname = name
+ else
+ specification.name = name
+ end
+ end
+end
+
+--[[doc--
+
+ {\bfseries EXPERIMENTAL}:
+ \identifier{kpse}-only resolver, for those who can do without
+ system fonts.
+
+--doc]]--
+
+request_resolvers.kpse = function (specification)
+ local name = specification.name
+ local suffix = filesuffix(name)
+ if suffix and formats[suffix] then
+ name = file.removesuffix(name)
+ if resolvers.findfile(name, suffix) then
+ specification.forced = stringlower (suffix)
+ specification.forcedname = name
+ return
+ end
+ end
+ for t, format in next, formats do --- brute force
+ if kpse.find_file (name, format) then
+ specification.forced = t
+ specification.name = name
+ return
+ end
+ end
+end
+
+--[[doc--
+
+ The \verb|name:| resolver.
+
+--doc]]--
+
+--- fonts.names.resolvers.name -- Customized version of the
+--- generic name resolver.
+
+request_resolvers.name = function (specification)
+ local resolver = names.resolve_cached
+ if config.luaotfload.run.resolver == "normal" then
+ resolver = names.resolve_name
+ end
+ local resolved, subfont = resolver (specification)
+ if resolved then
+ logreport ("log", 0, "load", "Lookup/name: %q -> \"%s%s\"",
+ specification.name,
+ resolved,
+ subfont and stringformat ("(%d)", subfont) or "")
+ specification.resolved = resolved
+ specification.sub = subfont
+ specification.forced = stringlower (filesuffix (resolved) or "")
+ specification.forcedname = resolved
+ specification.name = fileremovesuffix (resolved)
+ else
+ file_resolver (specification)
+ end
+end
+
+--[[doc--
+
+ Also {\bfseries EXPERIMENTAL}: custom file resolvers via callback.
+
+--doc]]--
+create_callback("luaotfload.resolve_font", "simple", dummy_function)
+
+request_resolvers.my = function (specification)
+ call_callback("luaotfload.resolve_font", specification)
+end
+
+--[[doc--
+
+ We create a callback for patching fonts on the fly, to be used by
+ other packages.
+ It initially contains the empty function that we are going to
+ override below.
+
+--doc]]--
+
+create_callback("luaotfload.patch_font", "simple", dummy_function)
+
+--[[doc--
+
+ \subsection{\CONTEXT override}
+ \label{define-font}
+ We provide a simplified version of the original font definition
+ callback.
+
+--doc]]--
+
+local read_font_file = fonts.definers.read
+
+local definers = {
+ generic = read_font_file,
+ --- spec -> size -> id -> tmfdata
+ patch = function (specification, size, id)
+ local tfmdata = read_font_file (specification, size, id)
+ if type (tfmdata) == "table" and tfmdata.shared then
+ --- We need to test for the “shared” field here
+ --- or else the fontspec capheight callback will
+ --- operate on tfm fonts.
+ call_callback ("luaotfload.patch_font", tfmdata, specification)
+ end
+ return tfmdata
+ end,
+}
+
+reset_callback "define_font"
+
+--[[doc--
+
+ Finally we register the callbacks.
+
+--doc]]--
+
+local definer = config.luaotfload.run.definer
+add_to_callback ("define_font", definers[definer], "luaotfload.define_font", 1)
+
+loadmodule "features.lua" --- font request and feature handling
+loadmodule "letterspace.lua" --- extra character kerning
+loadmodule "auxiliary.lua" --- additional high-level functionality
+
+luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec
+
+-- vim:tw=79:sw=4:ts=4:et
diff --git a/src/luaotfload-override.lua b/src/luaotfload-override.lua
new file mode 100644
index 0000000..b75530b
--- /dev/null
+++ b/src/luaotfload-override.lua
@@ -0,0 +1,52 @@
+if not modules then modules = { } end modules ["luaotfload-override"] = {
+ version = "2.5",
+ comment = "companion to Luaotfload",
+ author = "Khaled Hosny, Elie Roux, Philipp Gesang",
+ copyright = "Luaotfload Development Team",
+ license = "GNU GPL v2.0"
+}
+
+local findfile = resolvers.findfile
+local encodings = fonts.encodings
+
+local log = luaotfload.log
+local report = log.report
+
+--[[doc--
+
+ Adobe Glyph List.
+ -------------------------------------------------------------------
+
+ Context provides a somewhat different font-age.lua from an unclear
+ origin. Unfortunately, the file name it reads from is hard-coded
+ in font-enc.lua, so we have to replace the entire table.
+
+ This shouldn’t cause any complications. Due to its implementation
+ the glyph list will be loaded upon loading a OTF or TTF for the
+ first time during a TeX run. (If one sticks to TFM/OFM then it is
+ never read at all.) For this reason we can install a metatable that
+ looks up the file of our choosing and only falls back to the
+ Context one in case it cannot be found.
+
+--doc]]--
+
+encodings.agl = { }
+
+setmetatable(fonts.encodings.agl, { __index = function (t, k)
+ if k ~= "unicodes" then
+ return nil
+ end
+ local glyphlist = findfile "luaotfload-glyphlist.lua"
+ if glyphlist then
+ report ("log", 1, "load", "loading the Adobe glyph list")
+ else
+ glyphlist = findfile "font-age.lua"
+ report ("both", 0, "load",
+ "loading the extended glyph list from ConTeXt")
+ end
+ local unicodes = dofile(glyphlist)
+ encodings.agl = { unicodes = unicodes }
+ return unicodes
+end })
+
+-- vim:tw=71:sw=4:ts=4:expandtab
diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua
new file mode 100644
index 0000000..3eeb614
--- /dev/null
+++ b/src/luaotfload-parsers.lua
@@ -0,0 +1,701 @@
+#!/usr/bin/env texlua
+-------------------------------------------------------------------------------
+-- FILE: luaotfload-parsers.lua
+-- DESCRIPTION: various lpeg-based parsers used in Luaotfload
+-- REQUIREMENTS: Luaotfload > 2.4
+-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
+-- VERSION: same as Luaotfload
+-- CREATED: 2014-01-14 10:15:20+0100
+-------------------------------------------------------------------------------
+--
+
+if not modules then modules = { } end modules ['luaotfload-parsers'] = {
+ version = "2.5",
+ comment = "companion to luaotfload-main.lua",
+ author = "Philipp Gesang",
+ copyright = "Luaotfload Development Team",
+ license = "GNU GPL v2.0"
+}
+
+luaotfload = luaotfload or { }
+luaotfload.parsers = luaotfload.parsers or { }
+local parsers = luaotfload.parsers
+
+local rawset = rawset
+
+local lpeg = require "lpeg"
+local P, R, S = lpeg.P, lpeg.R, lpeg.S
+local lpegmatch = lpeg.match
+local C, Cc, Cf = lpeg.C, lpeg.Cc, lpeg.Cf
+local Cg, Cmt, Cs, Ct = lpeg.Cg, lpeg.Cmt, lpeg.Cs, lpeg.Ct
+
+local kpse = kpse
+local kpseexpand_path = kpse.expand_path
+local kpsereadable_file = kpse.readable_file
+
+local file = file
+local filejoin = file.join
+local filedirname = file.dirname
+
+local io = io
+local ioopen = io.open
+
+local log = luaotfload.log
+local logreport = log.report
+
+local string = string
+local stringsub = string.sub
+local stringfind = string.find
+local stringlower = string.lower
+
+local mathceil = math.ceil
+
+local lfs = lfs
+local lfsisfile = lfs.isfile
+local lfsisdir = lfs.isdir
+
+-------------------------------------------------------------------------------
+--- COMMON PATTERNS
+-------------------------------------------------------------------------------
+
+local dot = P"."
+local colon = P":"
+local semicolon = P";"
+local comma = P","
+local noncomma = 1 - comma
+local slash = P"/"
+local backslash = P"\\"
+local equals = P"="
+local dash = P"-"
+local gartenzaun = P"#"
+local lbrk, rbrk = P"[", P"]"
+local squote = P"'"
+local dquote = P"\""
+
+local newline = P"\n"
+local returnchar = P"\r"
+local spacing = S" \t\v"
+local linebreak = S"\n\r"
+local whitespace = spacing + linebreak
+local ws = spacing^0
+local xmlws = whitespace^1
+local eol = P"\n\r" + P"\r\n" + linebreak
+
+local digit = R"09"
+local alpha = R("az", "AZ")
+local anum = alpha + digit
+local decimal = digit^1 * (dot * digit^0)^-1
+
+-------------------------------------------------------------------------------
+--- FONTCONFIG
+-------------------------------------------------------------------------------
+
+--[[doc--
+
+ For fonts installed on the operating system, there are several
+ options to make Luaotfload index them:
+
+ - If OSFONTDIR is set (which is the case under windows by default
+ but not on the other OSs), it scans it at the same time as the
+ texmf tree, in the function scan_texmf_fonts().
+
+ - Otherwise
+ - under Windows and Mac OSX, we take a look at some hardcoded
+ directories,
+ - under Unix, it reads /etc/fonts/fonts.conf and processes the
+ directories specified there.
+
+ This means that if you have fonts in fancy directories, you need to
+ set them in OSFONTDIR.
+
+ Beware: OSFONTDIR is a kpathsea variable, so fonts found in these
+ paths, though technically system fonts, are registered in the
+ category “texmf”, not “system”. This may have consequences for the
+ lookup order when a font file (or a font with the same name
+ information) is located in both the system and the texmf tree.
+
+--doc]]--
+
+local tag_name = C(alpha^1)
+local comment = P"<!--" * (1 - P"--")^0 * P"-->"
+
+---> header specifica
+local xml_declaration = P"<?xml" * (1 - P"?>")^0 * P"?>"
+local xml_doctype = P"<!DOCTYPE" * xmlws
+ * "fontconfig" * (1 - P">")^0 * P">"
+local header = xml_declaration^-1
+ * (xml_doctype + comment + xmlws)^0
+
+---> enforce root node
+local root_start = P"<" * xmlws^-1 * P"fontconfig" * xmlws^-1 * P">"
+local root_stop = P"</" * xmlws^-1 * P"fontconfig" * xmlws^-1 * P">"
+
+local dquote, squote = P[["]], P"'"
+local xml_namestartchar = S":_" + alpha --- ascii only, funk the rest
+local xml_namechar = S":._" + alpha + digit
+local xml_name = xmlws^-1
+ * C(xml_namestartchar * xml_namechar^0)
+local xml_attvalue = dquote * C((1 - S[[%&"]])^1) * dquote * xmlws^-1
+ + squote * C((1 - S[[%&']])^1) * squote * xmlws^-1
+local xml_attr = Cg(xml_name * P"=" * xml_attvalue)
+local xml_attr_list = Cf(Ct"" * xml_attr^1, rawset)
+
+--[[doc--
+ scan_node creates a parser for a given xml tag.
+--doc]]--
+--- string -> bool -> lpeg_t
+local scan_node = function (tag)
+ --- Node attributes go into a table with the index “attributes”
+ --- (relevant for “prefix="xdg"” and the likes).
+ local p_tag = P(tag)
+ local with_attributes = P"<" * p_tag
+ * Cg(xml_attr_list, "attributes")^-1
+ * xmlws^-1
+ * P">"
+ local plain = P"<" * p_tag * xmlws^-1 * P">"
+ local node_start = plain + with_attributes
+ local node_stop = P"</" * p_tag * xmlws^-1 * P">"
+ --- there is no nesting, the earth is flat ...
+ local node = node_start
+ * Cc(tag) * C(comment + (1 - node_stop)^1)
+ * node_stop
+ return Ct(node) -- returns {string, string [, attributes = { key = val }] }
+end
+
+--[[doc--
+ At the moment, the interesting tags are “dir” for
+ directory declarations, and “include” for including
+ further configuration files.
+
+ spec: http://freedesktop.org/software/fontconfig/fontconfig-user.html
+--doc]]--
+local include_node = scan_node"include"
+local dir_node = scan_node"dir"
+
+local element = dir_node
+ + include_node
+ + comment --> ignore
+ + P(1-root_stop) --> skip byte
+
+local root = root_start * Ct(element^0) * root_stop
+local p_cheapxml = header * root
+
+--lpeg.print(p_cheapxml) ---> 757 rules with v0.10
+
+--[[doc--
+ fonts_conf_scanner() handles configuration files.
+ It is called on an abolute path to a config file (e.g.
+ /home/luser/.config/fontconfig/fonts.conf) and returns a list
+ of the nodes it managed to extract from the file.
+--doc]]--
+--- string -> path list
+local fonts_conf_scanner = function (path)
+ local fh = ioopen(path, "r")
+ if not fh then
+ logreport("both", 3, "db", "Cannot open fontconfig file %s.", path)
+ return
+ end
+ local raw = fh:read"*all"
+ fh:close()
+
+ local confdata = lpegmatch(p_cheapxml, raw)
+ if not confdata then
+ logreport("both", 3, "db", "Cannot scan fontconfig file %s.", path)
+ return
+ end
+ return confdata
+end
+
+local p_conf = P".conf" * P(-1)
+local p_filter = (1 - p_conf)^1 * p_conf
+
+local conf_filter = function (path)
+ if lpegmatch (p_filter, path) then
+ return true
+ end
+ return false
+end
+
+--[[doc--
+ read_fonts_conf_indeed() is called with seven arguments; the
+ latter three are tables that represent the state and are
+ always returned.
+ The first four are
+ · the path to the file
+ · the expanded $HOME
+ · the expanded $XDG_CONFIG_HOME
+ · the expanded $XDG_DATA_HOME
+--doc]]--
+--- string -> string -> string -> tab -> tab -> (tab * tab * tab)
+local read_fonts_conf_indeed
+read_fonts_conf_indeed = function (start, home, xdg_config_home,
+ xdg_data_home,
+ acc, done, dirs_done,
+ find_files)
+
+ local paths = fonts_conf_scanner(start)
+ if not paths then --- nothing to do
+ return acc, done, dirs_done
+ end
+
+ for i=1, #paths do
+ local pathobj = paths[i]
+ local kind, path = pathobj[1], pathobj[2]
+ local attributes = pathobj.attributes
+
+ if kind == "dir" then
+ if attributes and attributes.prefix == "xdg" then
+ path = filejoin(xdg_data_home, path)
+ end
+ if stringsub(path, 1, 1) == "~" then
+ path = filejoin(home, stringsub(path, 2))
+ end
+ --- We exclude paths with texmf in them, as they should be
+ --- found anyway; also duplicates are ignored by checking
+ --- if they are elements of dirs_done.
+ ---
+ --- FIXME does this mean we cannot access paths from
+ --- distributions (e.g. Context minimals) installed
+ --- separately?
+ if not (stringfind(path, "texmf") or dirs_done[path]) then
+ acc[#acc+1] = path
+ dirs_done[path] = true
+ end
+
+ elseif kind == "include" then
+ if attributes and attributes.prefix == "xdg" then
+ path = filejoin(xdg_config_home, path)
+ end
+ --- here the path can be four things: a directory or a file,
+ --- in absolute or relative path.
+ if stringsub(path, 1, 1) == "~" then
+ path = filejoin(home, stringsub(path, 2))
+ elseif --- if the path is relative, we make it absolute
+ not ( lfsisfile(path) or lfsisdir(path) )
+ then
+ path = filejoin(filedirname(start), path)
+ end
+ if lfsisfile(path)
+ and kpsereadable_file(path)
+ and not done[path]
+ then
+ --- we exclude path with texmf in them, as they should
+ --- be found otherwise
+ acc = read_fonts_conf_indeed(
+ path, home, xdg_config_home, xdg_data_home,
+ acc, done, dirs_done)
+ elseif lfsisdir(path) then --- arrow code ahead
+ local config_files = find_files (path, conf_filter)
+ for _, filename in next, config_files do
+ if not done[filename] then
+ acc = read_fonts_conf_indeed(
+ filename, home, xdg_config_home, xdg_data_home,
+ acc, done, dirs_done)
+ end
+ end
+ end --- match “kind”
+ end --- iterate paths
+ end
+
+ --inspect(acc)
+ --inspect(done)
+ return acc, done, dirs_done
+ end --- read_fonts_conf_indeed()
+
+--[[doc--
+ read_fonts_conf() sets up an accumulator and two sets
+ for tracking what’s been done.
+
+ Also, the environment variables HOME, XDG_DATA_HOME and
+ XDG_CONFIG_HOME -- which are constants anyways -- are expanded
+ so we don’t have to repeat that over and over again as with the
+ old parser. Now they’re just passed on to every call of
+ read_fonts_conf_indeed().
+--doc]]--
+
+--- list -> (string -> function option -> string list) -> list
+
+local read_fonts_conf = function (path_list, find_files)
+ local home = kpseexpand_path"~" --- could be os.getenv"HOME"
+ local xdg_config_home = kpseexpand_path"$XDG_CONFIG_HOME"
+ if xdg_config_home == "" then xdg_config_home = filejoin(home, ".config") end
+ local xdg_data_home = kpseexpand_path"$XDG_DATA_HOME"
+ if xdg_data_home == "" then xdg_data_home = filejoin(home, ".local/share") end
+ local acc = { } ---> list: paths collected
+ local done = { } ---> set: files inspected
+ local dirs_done = { } ---> set: dirs in list
+ for i=1, #path_list do --- we keep the state between files
+ acc, done, dirs_done = read_fonts_conf_indeed(
+ path_list[i], home, xdg_config_home,
+ xdg_data_home,
+ acc, done, dirs_done,
+ find_files)
+ end
+ return acc
+end
+
+luaotfload.parsers.read_fonts_conf = read_fonts_conf
+
+
+
+-------------------------------------------------------------------------------
+--- MISC PARSERS
+-------------------------------------------------------------------------------
+
+
+local trailingslashes = slash^1 * P(-1)
+local stripslashes = C((1 - trailingslashes)^0)
+parsers.stripslashes = stripslashes
+
+local splitcomma = Ct((C(noncomma^1) + comma)^1)
+parsers.splitcomma = splitcomma
+
+
+
+-------------------------------------------------------------------------------
+--- FONT REQUEST
+-------------------------------------------------------------------------------
+
+
+--[[doc------------------------------------------------------------------------
+
+ The luaotfload font request syntax (see manual)
+ has a canonical form:
+
+ \font<csname>=<prefix>:<identifier>:<features>
+
+ where
+ <csname> is the control sequence that activates the font
+ <prefix> is either “file” or “name”, determining the lookup
+ <identifer> is either a file name (no path) or a font
+ name, depending on the lookup
+ <features> is a list of switches or options, separated by
+ semicolons or commas; a switch is of the form “+” foo
+ or “-” foo, options are of the form lhs “=” rhs
+
+ however, to ensure backward compatibility we also have
+ support for Xetex-style requests.
+
+ for the Xetex emulation see:
+ · The XeTeX Reference Guide by Will Robertson, 2011
+ · The XeTeX Companion by Michel Goosens, 2010
+ · About XeTeX by Jonathan Kew, 2005
+
+
+ caueat emptor.
+
+ the request is parsed into one of **four** different lookup
+ categories: the regular ones, file and name, as well as the
+ Xetex compatibility ones, path and anon. (maybe a better choice
+ of identifier would be “ambig”.)
+
+ according to my reconstruction, the correct chaining of the
+ lookups for each category is as follows:
+
+ | File -> ( db/filename lookup )
+
+ | Name -> ( db/name lookup,
+ db/filename lookup )
+
+ | Path -> ( db/filename lookup,
+ fullpath lookup )
+
+ | Anon -> ( kpse.find_file(), // <- for tfm, ofm
+ db/name lookup,
+ db/filename lookup,
+ fullpath lookup )
+
+ caching of successful lookups is essential. we now as of v2.2
+ have a lookup cache that is stored in a separate file. it
+ pertains only to name: lookups, and is described in more detail
+ in luaotfload-database.lua.
+
+-------------------------------------------------------------------------------
+
+ One further incompatibility between Xetex and Luatex-Fonts consists
+ in their option list syntax: apparently, Xetex requires key-value
+ options to be prefixed by a "+" (ascii “plus”) character. We
+ silently accept this as well, dropping the first byte if it is a
+ plus or minus character.
+
+ Reference: https://github.com/lualatex/luaotfload/issues/79#issuecomment-18104483
+
+--doc]]------------------------------------------------------------------------
+
+
+local handle_normal_option = function (key, val)
+ val = stringlower(val)
+ --- the former “toboolean()” handler
+ if val == "true" then
+ val = true
+ elseif val == "false" then
+ val = false
+ end
+ return key, val
+end
+
+--[[doc--
+
+ Xetex style indexing begins at zero which we just increment before
+ passing it along to the font loader. Ymmv.
+
+--doc]]--
+
+local handle_xetex_option = function (key, val)
+ val = stringlower(val)
+ local numeric = tonumber(val) --- decimal only; keeps colors intact
+ if numeric then --- ugh
+ if mathceil(numeric) == numeric then -- integer, possible index
+ val = tostring(numeric + 1)
+ end
+ elseif val == "true" then
+ val = true
+ elseif val == "false" then
+ val = false
+ end
+ return key, val
+end
+
+--[[doc--
+
+ Instead of silently ignoring invalid options we emit a warning to
+ the log.
+
+ Note that we have to return a pair to please rawset(). This creates
+ an entry on the resulting features hash which will later be removed
+ during set_default_features().
+
+--doc]]--
+
+local handle_invalid_option = function (opt)
+ logreport("log", 0, "load", "font option %q unknown.", opt)
+ return "", false
+end
+
+--[[doc--
+
+ Dirty test if a file: request is actually a path: lookup; don’t
+ ask! Note this fails on Windows-style absolute paths. These will
+ *really* have to use the correct request.
+
+--doc]]--
+
+local check_garbage = function (_,i, garbage)
+ if stringfind(garbage, "/") then
+ logreport("log", 0, "load", --- ffs use path!
+ "warning: path in file: lookups is deprecated; ")
+ logreport("log", 0, "load", "use bracket syntax instead!")
+ logreport("log", 0, "load",
+ "position: %d; full match: %q",
+ i, garbage)
+ return true
+ end
+ return false
+end
+
+local featuresep = comma + semicolon
+
+--- modifiers ---------------------------------------------------------
+--[[doc--
+ The slash notation: called “modifiers” (Kew) or “font options”
+ (Robertson, Goosens)
+ we only support the shorthands for italic / bold / bold italic
+ shapes, as well as setting optical size, the rest is ignored.
+--doc]]--
+local style_modifier = (P"BI" + P"IB" + P"bi" + P"ib" + S"biBI")
+ / stringlower
+local size_modifier = S"Ss" * P"=" --- optical size
+ * Cc"optsize" * C(decimal)
+local other_modifier = P"AAT" + P"aat" --- apple stuff; unsupported
+ + P"ICU" + P"icu" --- not applicable
+ + P"GR" + P"gr" --- sil stuff; unsupported
+local garbage_modifier = ((1 - colon - slash)^0 * Cc(false))
+local modifier = slash * (other_modifier --> ignore
+ + Cs(style_modifier) --> collect
+ + Ct(size_modifier) --> collect
+ + garbage_modifier) --> warn
+local modifier_list = Cg(Ct(modifier^0), "modifiers")
+
+--- lookups -----------------------------------------------------------
+local fontname = C((1-S":(/")^1) --- like luatex-fonts
+local unsupported = Cmt((1-S":(")^1, check_garbage)
+local prefixed = P"name:" * ws * Cg(fontname, "name")
+--- initially we intended file: to emulate the behavior of
+--- luatex-fonts, i.e. no paths allowed. after all, we do have XeTeX
+--- emulation with the path lookup and it interferes with db lookups.
+--- turns out fontspec and other widely used packages rely on file:
+--- with paths already, so we’ll add a less strict rule here. anyways,
+--- we’ll emit a warning.
+ + P"file:" * ws * Cg(unsupported, "path")
+ + P"file:" * ws * Cg(fontname, "file")
+--- EXPERIMENTAL: kpse lookup
+ + P"kpse:" * ws * Cg(fontname, "kpse")
+--- EXPERIMENTAL: custom lookup
+ + P"my:" * ws * Cg(fontname, "my")
+local unprefixed = Cg(fontname, "anon")
+local path_lookup = lbrk * Cg(C((1-rbrk)^1), "path") * rbrk
+
+--- features ----------------------------------------------------------
+local field_char = anum + S"+-." --- sic!
+local field = field_char^1
+--- assignments are “lhs=rhs”
+--- or “+lhs=rhs” (Xetex-style)
+--- switches are “+key” | “-key”
+local normal_option = C(field) * ws * equals * ws * C(field) * ws
+local xetex_option = P"+" * ws * normal_option
+local ignore_option = (1 - equals - featuresep)^1
+ * equals
+ * (1 - featuresep)^1
+local assignment = xetex_option / handle_xetex_option
+ + normal_option / handle_normal_option
+ + ignore_option / handle_invalid_option
+local switch = P"+" * ws * C(field) * Cc(true)
+ + P"-" * ws * C(field) * Cc(false)
+ + C(field) * Cc(true) --- default
+local feature_expr = ws * Cg(assignment + switch) * ws
+local option = feature_expr
+local feature_list = Cf(Ct""
+ * option
+ * (featuresep * option^-1)^0
+ , rawset)
+ * featuresep^-1
+
+--- other -------------------------------------------------------------
+--- This rule is present in the original parser. It sets the “sub”
+--- field of the specification which allows addressing a specific
+--- font inside a TTC container. Neither in Luatex-Fonts nor in
+--- Luaotfload is this documented, so we might as well silently drop
+--- it. However, as backward compatibility is one of our prime goals we
+--- just insert it here and leave it undocumented until someone cares
+--- to ask. (Note: afair subfonts are numbered, but this rule matches a
+--- string; I won’t mess with it though until someone reports a
+--- problem.)
+--- local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim
+--- Note to self: subfonts apparently start at index 0. Tested with
+--- Cambria.ttc that includes “Cambria Math” at 0 and “Cambria” at 1.
+--- Other values cause luatex to segfault.
+local subfont = P"(" * Cg((1 - S"()")^1, "sub") * P")"
+--- top-level rules ---------------------------------------------------
+--- \font\foo=<specification>:<features>
+local features = Cg(feature_list, "features")
+local specification = (prefixed + unprefixed)
+ * subfont^-1
+ * modifier_list^-1
+local font_request = Ct(path_lookup * (colon^-1 * features)^-1
+ + specification * (colon * features)^-1)
+
+-- lpeg.print(font_request)
+--- v2.5 parser: 1065 rules
+--- v1.2 parser: 230 rules
+
+luaotfload.parsers.font_request = font_request
+
+-------------------------------------------------------------------------------
+--- INI FILES
+-------------------------------------------------------------------------------
+
+--[[doc--
+
+ Luaotfload uses the pervasive flavor of the INI files that allows '#' in
+ addition to ';' to indicate comment lines (see git-config(1) for a
+ description of the syntax we’re targeting).
+
+--doc]]--
+
+local truth_ids = {
+ ["true"] = true,
+ ["1"] = true,
+ yes = true,
+ on = true,
+ ["false"] = false,
+ ["2"] = false,
+ no = false,
+ off = false,
+}
+
+local maybe_cast = function (var)
+ local bool = truth_ids[var]
+ if bool ~= nil then
+ return bool
+ end
+ return tonumber (var) or var
+end
+local escape = function (chr, repl)
+ return (backslash * P(chr) / (repl or chr))
+end
+local valid_escapes = escape "\""
+ + escape "\\"
+ + escape ("n", "\n")
+ + escape ("t", "\t")
+ + escape ("b", "\b")
+local comment_char = semicolon + gartenzaun
+local comment_line = ws * comment_char * (1 - eol)^0 * eol
+local blank_line = ws * eol
+local skip_line = comment_line + blank_line
+local ini_id_char = alpha + (dash / "_")
+local ini_id = Cs(alpha * ini_id_char^0) / stringlower
+local ini_value_char = (valid_escapes + (1 - newline - backslash - comment_char))
+local ini_value = (Cs (ini_value_char^0) / string.strip)
+ * (comment_char * (1 - eol)^0)^-1
+local ini_string_char = (valid_escapes + (1 - newline - dquote - backslash))
+local ini_string = dquote
+ * Cs (ini_string_char^0)
+ * dquote
+
+local ini_heading_title = Ct (Cg (ini_id, "title")
+ * (ws * Cg (ini_string / stringlower, "subtitle"))^-1)
+local ini_heading = lbrk * ws
+ * Cg (ini_heading_title, "section")
+ * ws * rbrk * ws * eol
+
+local ini_variable_full = Cg (ws
+ * ini_id
+ * ws
+ * equals
+ * ws
+ * (ini_string + (ini_value / maybe_cast))
+ * ws
+ * eol)
+local ini_variable_true = Cg (ws * ini_id * ws * eol * Cc (true))
+local ini_variable = ini_variable_full
+ + ini_variable_true
+ + skip_line
+local ini_variables = Cg (Cf (Ct "" * ini_variable^0, rawset), "variables")
+
+local ini_section = Ct (ini_heading * ini_variables)
+local ini_sections = skip_line^0 * ini_section^0
+local config = Ct (ini_sections)
+
+--[=[doc--
+
+ The INI parser converts an input of the form
+
+ [==[
+ [foo]
+ bar = baz
+ xyzzy = no
+ buzz
+
+ [lavernica "brutalitops"]
+ # It’s a locomotive that runs on us.
+ laan-ev = zip zop zooey ; jib-jab
+ Crouton = "Fibrosis \"\\ # "
+
+ ]==]
+
+ to a Lua table of the form
+
+ { { section = { title = "foo" },
+ variables = { bar = "baz",
+ xyzzy = false,
+ buzz = true } },
+ { section = { title = "boing",
+ subtitle = "brutalitops" },
+ variables = { ["laan-ev"] = "zip zop zooey",
+ crouton = "Fibrosis \"\\ # " } } }
+
+--doc]=]--
+
+luaotfload.parsers.config = config
+
+-- vim:ft=lua:tw=71:et:sts=4:ts=8
diff --git a/luaotfload-tool.lua b/src/luaotfload-tool.lua
index 5607c04..8cfcac0 100755
--- a/luaotfload-tool.lua
+++ b/src/luaotfload-tool.lua
@@ -2,17 +2,22 @@
-----------------------------------------------------------------------
-- FILE: luaotfload-tool.lua
-- DESCRIPTION: database functionality
--- REQUIREMENTS: luaotfload 2.4
+-- REQUIREMENTS: luaotfload 2.5
-- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang
--- VERSION: 2.4-4
--- LICENSE: GPL v2
--- MODIFIED: 2013-07-28 13:12:04+0200
+-- VERSION: 2.5
+-- LICENSE: GPL v2.0
+-- MODIFIED: 2014-05-15 21:47:39+0200
-----------------------------------------------------------------------
-local version = "2.4-4" --- <int: major>.<int: minor><alpha: fixes>
+luaotfload = luaotfload or { }
+local version = "2.5" --- <int: major>.<int: minor>-<int: fixes>
+luaotfload.version = version
+luaotfload.self = "luaotfload-tool"
--[[doc--
+luaotfload-tool(1)
+
This file was originally written (as \fileent{mkluatexfontdb.lua}) by
Elie Roux and Khaled Hosny and, as a derived work of ConTeXt, is
provided under the terms of the GPL v2.0 license as printed in full
@@ -33,17 +38,16 @@ kpse.set_program_name "luatex"
--[[doc--
We test for Lua 5.1 by means of capability detection to see if
- we’re running an outdated Luatex. If so, we hand over control to
- the legacy db runner.
+ we’re running an outdated Luatex. If so, we bail.
\url{http://lua-users.org/wiki/LuaVersionCompatibility}
--doc]]--
-local ioopen = io.open
local iowrite = io.write
local kpsefind_file = kpse.find_file
+local mathfloor = math.floor
local next = next
local osdate = os.date
local ostype = os.type
@@ -63,8 +67,10 @@ if _G.getfenv ~= nil then -- 5.1 or LJ
runtime = { "jit", jit.version }
else
runtime = { "stock", _VERSION }
- local oldscript = kpsefind_file "luaotfload-legacy-tool.lua"
- return require (oldscript)
+ print "FATAL ERROR"
+ print "Luaotfload requires a Luatex version >=0.76."
+ print "Please update your TeX distribution!"
+ os.exit (-1)
end
else -- 5.2
runtime = { "stock", _VERSION }
@@ -83,56 +89,19 @@ string.quoted = string.quoted or function (str)
return string.format("%q",str)
end
-require(loader_path)
+require (loader_path)
--[[doc--
-Depending on how the script is called we change its behavior.
-For backwards compatibility, moving or symlinking the script to a
-file name starting with \fileent{mkluatexfontdb} will cause it to
-trigger a database update on every run.
-Running as \fileent{luaotfload-tool} -- the new name -- will do this upon
-request only.
-
-There are two naming conventions followed here: firstly that of
-utilities such as \fileent{mktexpk}, \fileent{mktexlsr} and the likes,
-and secondly that of \fileent{fmtutil}.
-After support for querying the database was added, the latter appeared
-to be the more appropriate.
---doc]]--
-config = config or { }
-local config = config
-local luaotfloadconfig = config.luaotfload or { }
-config.luaotfload = luaotfloadconfig
-luaotfloadconfig.version = luaotfloadconfig.version or version
-luaotfloadconfig.names_dir = luaotfloadconfig.names_dir or "names"
-luaotfloadconfig.cache_dir = luaotfloadconfig.cache_dir or "fonts"
-luaotfloadconfig.index_file = luaotfloadconfig.index_file
- or "luaotfload-names.lua"
-luaotfloadconfig.formats = luaotfloadconfig.formats
- or "otf,ttf,ttc,dfont"
-luaotfloadconfig.reload = false
-if not luaotfloadconfig.strip then
- luaotfloadconfig.strip = true
-end
+ XXX:
+ Creating the config table will be moved to the common
+ initialization when the times comes.
-do -- we don’t have file.basename and the likes yet, so inline parser ftw
- local slash = P"/"
- local dot = P"."
- local noslash = 1 - slash
- local slashes = slash^1
- local path = slashes^-1 * (noslash^1 * slashes)^1
- local thename = (1 - slash - dot)^1
- local extension = dot * (1 - slash - dot)^1
- local p_basename = path^-1 * C(thename) * extension^-1 * P(-1)
-
- local self = lpegmatch(p_basename, stringlower(arg[0]))
- if self == "luaotfload-tool" then
- luaotfloadconfig.self = "luaotfload-tool"
- else
- luaotfloadconfig.self = "mkluatexfontdb"
- end
-end
+--doc]]--
+
+config = config or { }
+local config = config
+config.luaotfload = config.luaotfload or { }
config.lualibs = config.lualibs or { }
config.lualibs.verbose = false
@@ -140,8 +109,13 @@ config.lualibs.prefer_merged = true
config.lualibs.load_extended = true
require "lualibs"
-local tabletohash = table.tohash
+local iosavedata = io.savedata
+local lfsisdir = lfs.isdir
+local lfsisfile = lfs.isfile
local stringsplit = string.split
+local tableserialize = table.serialize
+local tablesortedkeys = table.sortedkeys
+local tabletohash = table.tohash
--[[doc--
\fileent{luatex-basics-gen.lua} calls functions from the
@@ -169,22 +143,17 @@ require"luaotfload-basics-gen.lua"
texio.write, texio.write_nl = backup.write, backup.write_nl
utilities = backup.utilities
-require"luaotfload-override.lua" --- this populates the logs.* namespace
-require"luaotfload-database"
-require"alt_getopt"
-
-local names = fonts.names
-
-local status_file = "luaotfload-status"
-local luaotfloadstatus = require (status_file)
-luaotfloadconfig.status = luaotfloadstatus
+require "luaotfload-log.lua" --- this populates the luaotfload.log.* namespace
+require "luaotfload-parsers" --- fonts.conf, configuration, and request syntax
+require "luaotfload-configuration" --- configuration file handling
+require "luaotfload-database"
+require "alt_getopt"
+local names = fonts.names
local sanitize_fontname = names.sanitize_fontname
-local pathdata = names.path
-local names_plain = pathdata.index.lua
-local names_gzip = names_plain .. ".gz"
-local names_bin = pathdata.index.luc
+local log = luaotfload.log
+local report = log.report
local help_messages = {
["luaotfload-tool"] = [[
@@ -211,9 +180,6 @@ Usage: %s [OPTIONS...]
"environment", "index", "permissions", or
"repository"
- --alias=<name> force behavior of "luaotfload-tool" or legacy
- "mkluatexfontdb"
-
-------------------------------------------------------------------------------
DATABASE
@@ -221,6 +187,7 @@ Usage: %s [OPTIONS...]
-n --no-reload suppress db update
--no-strip keep redundant information in db
-f --force force re-indexing all fonts
+ -L --local scan font files in $PWD
-c --no-compress do not gzip index file (text version only)
-l --flush-lookups empty lookup cache of font requests
-D --dry-run skip loading of fonts, just scan
@@ -243,6 +210,10 @@ Usage: %s [OPTIONS...]
--fields=<f1>,<f2>,…,<fn> which fields <f> to print with --list
-b --show-blacklist show blacklisted files
+ --bisect=<directive> control database bisection: valid
+ directives are "start", "stop", "run", "status",
+ "good", "bad"
+
The font database will be saved to
%s
%s
@@ -258,49 +229,37 @@ The font cache will be written to
]],
mkluatexfontdb = [[
-
-Usage: %s [OPTION]...
-
-Rebuild or update the Luaotfload font names database.
-
-Valid options:
- -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
- -V --version print version and exit
- -h --help print this message
- --alias=<name> force behavior of "luaotfload-tool" or legacy
- "mkluatexfontdb"
-
-The font database will be saved to
- %s
- %s
-
+FATAL ERROR
+As of Luaotfload v2.5, legacy behavior is not supported anymore. Please
+update your scripts and/or habits! Kthxbye.
]],
short = [[
Usage: luaotfload-tool [--help] [--version] [--verbose=<lvl>]
- [--update] [--force] [--prefer-texmf]
+ [--update] [--force] [--prefer-texmf] [--local]
[--dry-run] [--formats=<extension list>]
[--find=<font name>] [--fuzzy] [--info] [--inspect]
[--list=<criterion>] [--fields=<field list>]
[--cache=<directive>] [--flush-lookups]
[--show-blacklist] [--diagnose=<procedure>]
+ [--no-compress] [--no-strip] [--local]
+ [--max-fonts=<n>] [--bisect=<directive>]
Enter 'luaotfload-tool --help' for a larger list of options.
]]
}
local help_msg = function (version)
- local template = help_messages[version]
+ local template = help_messages[version]
+ local paths = config.luaotfload.paths
+ local names_plain = paths.index_path_lua
+ local names_gzip = names_plain .. ".gz"
+ local names_bin = paths.index_path_luc
+
iowrite(stringformat(template,
- luaotfloadconfig.self,
--- names_plain,
+ luaotfload.self,
names_gzip,
names_bin,
- caches.getwritablepath (
- luaotfloadconfig.cache_dir)))
+ caches.getwritablepath (config.luaotfload.cache_dir)))
end
local about = [[
@@ -311,16 +270,28 @@ local about = [[
]]
local version_msg = function ( )
- local out = function (...) texiowrite_nl (stringformat (...)) end
- out (about, luaotfloadconfig.self)
- out ("%s version %q", luaotfloadconfig.self, version)
- out ("revision %q", luaotfloadstatus.notes.revision)
- out ("database version %q", names.version)
+ local out = function (...) texiowrite_nl (stringformat (...)) end
+ local uname = os.uname ()
+ local meta = names.getmetadata ()
+ out (about, luaotfload.self)
+ out ("%s version: %q", luaotfload.self, version)
+ out ("Revision: %q", config.luaotfload.status.notes.revision)
out ("Lua interpreter: %s; version %q", runtime[1], runtime[2])
- out ("Luatex SVN revision %d", status.luatex_svn)
- out ("Luatex version %.2f.%d",
+ out ("Luatex SVN revision: %d", status.luatex_svn)
+ out ("Luatex version: %.2f.%d",
status.luatex_version / 100,
status.luatex_revision)
+ out ("Platform: type=%s name=%s", os.type, os.name)
+
+ local uname_vars = tablesortedkeys (uname)
+ for i = 1, #uname_vars do
+ local var = uname_vars[i]
+ out (" + %8s: %s", var, uname[var])
+ end
+ out ("Index: version=%q created=%q modified=%q",
+ config.luaotfload.status.notes.revision,
+ meta.created or "ages ago",
+ meta.modified or "ages ago")
out ""
end
@@ -332,7 +303,7 @@ local head_adornchars = {
}
local textwidth = 80
-local wd_leftcolumn = math.floor(textwidth * .25)
+local wd_leftcolumn = mathfloor(textwidth * .25)
local key_fmt = stringformat([[%%%ds]], wd_leftcolumn)
local val_fmt = [[%s]]
local fieldseparator = ":"
@@ -380,15 +351,36 @@ local print_heading = function (title, level)
texiowrite_nl (s .. stringrep(adornchar, textwidth-utf.len(s)))
end
+local baseindent = " "
+
+--[[doc--
+
+ show_info_items -- Together with show_info_table prints the table returned by
+ fontloader.info(), recursing into nested tables if appropriate (as necessitated
+ by Luatex versions 0.78+ which include the pfminfo table in the result.
+
+--doc]]--
+
+local show_info_table show_info_table = function (t, depth)
+ depth = depth or 0
+ local indent = stringrep (baseindent, depth)
+ local keys = tablesortedkeys (t)
+ for n = 1, #keys do
+ local key = keys [n]
+ local val = t [key]
+ if type (val) == "table" then
+ texiowrite_nl (indent .. stringformat (info_fmt, key, "<table>"))
+ show_info_table (val, depth + 1)
+ else
+ texiowrite_nl (indent .. stringformat (info_fmt, key, val))
+ end
+ end
+end
+
local show_info_items = function (fontinfo)
- local items = table.sortedkeys(fontinfo)
- print_heading(fontinfo.fullname, 1)
+ print_heading (fontinfo.fullname, 1)
texiowrite_nl ""
- for n = 1, #items do
- local item = items[n]
- texiowrite_nl(stringformat(
- info_fmt, item, fontinfo[item]))
- end
+ show_info_table (fontinfo)
texiowrite_nl ""
end
@@ -707,9 +699,9 @@ local show_font_info = function (basename, askedname, detail, warnings)
if nfonts > 0 then -- true type collection
local subfont
if askedname then
- logs.names_report(true, 1, "resolve",
- [[%s is part of the font collection %s]],
- askedname, basename)
+ report (true, 1, "resolve",
+ [[%s is part of the font collection %s]],
+ askedname, basename)
subfont = subfont_by_name(shortinfo, askedname)
end
if subfont then
@@ -718,11 +710,11 @@ local show_font_info = function (basename, askedname, detail, warnings)
show_full_info(fullname, subfont, warnings)
end
else -- list all subfonts
- logs.names_report(true, 1, "resolve",
- [[%s is a font collection]], basename)
+ report (true, 1, "resolve",
+ [[%s is a font collection]], basename)
for subfont = 1, nfonts do
- logs.names_report(true, 1, "resolve",
- [[Showing info for font no. %d]], n)
+ report (true, 1, "resolve",
+ [[Showing info for font no. %d]], n)
show_info_items(shortinfo[subfont])
if detail == true then
show_full_info(fullname, subfont, warnings)
@@ -736,8 +728,7 @@ local show_font_info = function (basename, askedname, detail, warnings)
end
end
else
- logs.names_report(true, 1, "resolve",
- "Font %s not found", filename)
+ report (true, 1, "resolve", "Font %s not found", filename)
end
end
@@ -748,23 +739,40 @@ set.
--]]--
local action_sequence = {
- "loglevel", "help", "version", "diagnose",
- "blacklist", "cache", "flush", "generate",
- "list", "query",
+ "config", "loglevel", "help", "version",
+ "diagnose", "blacklist", "cache", "flush",
+ "bisect", "generate", "list", "query",
}
local action_pending = tabletohash(action_sequence, false)
+action_pending.config = true --- always read the configuration
action_pending.loglevel = true --- always set the loglevel
action_pending.generate = false --- this is the default action
local actions = { } --- (jobspec -> (bool * bool)) list
actions.loglevel = function (job)
- logs.set_loglevel(job.log_level)
- logs.names_report("info", 3, "util",
- "Setting log level", "%d", job.log_level)
- logs.names_report("log", 2, "util", "Lua=%s", _VERSION)
+ local lvl = job.log_level
+ if lvl then
+ log.set_loglevel(lvl)
+ report ("info", 3, "util", "Setting the log level to %d.", lvl)
+ report ("log", 2, "util", "Lua=%q", _VERSION)
+ end
+ return true, true
+end
+
+actions.config = function (job)
+ local defaults = luaotfload.default_config
+ local vars = config.actions.read (job.extra_config)
+ config.luaotfload = config.actions.apply (defaults, vars)
+ config.luaotfload = config.actions.apply (config.luaotfload, job.config)
+
+ --inspect(config.luaotfload)
+ --os.exit()
+ if not config.actions.reconfigure () then
+ return false, false
+ end
return true, true
end
@@ -781,29 +789,321 @@ end
actions.blacklist = function (job)
names.read_blacklist()
local n = 0
- for n, entry in next, table.sortedkeys(names.blacklist) do
+ for n, entry in next, tablesortedkeys(names.blacklist) do
iowrite (stringformat("(%d %s)\n", n, entry))
end
return true, false
end
actions.generate = function (job)
- local fontnames, savedname
- fontnames = names.update(fontnames, job.force_reload, job.dry_run)
- logs.names_report("info", 2, "db",
- "Fonts in the database: %i", #fontnames.mappings)
+ local fontnames = names.update(fontnames, job.force_reload, job.dry_run)
+ report ("info", 2, "db", "Fonts in the database: %i", #fontnames.mappings)
if names.data() then
return true, true
end
return false, false
end
+-------------------------------------------------------------------------------
+--- bisect mode
+-------------------------------------------------------------------------------
+
+local bisect_status_path = caches.getwritablepath "bisect"
+local bisect_status_file = bisect_status_path .."/" .. "luaotfload-bisect-status.lua"
+local bisect_status_fmt = [[
+--[==[-------------------------------------------------------------------------
+ This file is generated by Luaotfload. It can be safely deleted.
+ Creation date: %s.
+-------------------------------------------------------------------------]==]--
+
+%s
+
+--- vim:ft=lua:ts=8:et:sw=2
+]]
+
+--[[doc--
+
+ write_bisect_status -- Write the history of the current bisection to disk.
+
+--doc]]--
+
+--- state list -> bool
+local write_bisect_status = function (data)
+ local payload = tableserialize (data, true)
+ local status = stringformat (bisect_status_fmt,
+ osdate ("%Y-%m-d %H:%M:%S", os.time ()),
+ payload)
+ if status and iosavedata (bisect_status_file, status) then
+ report ("info", 4, "bisect",
+ "Bisection state written to %s.", bisect_status_file)
+ return true
+ end
+ report ("info", 0, "bisect",
+ "Failed to write bisection state to %s.", bisect_status_file)
+ return false
+end
+
+--[[doc--
+
+ read_bisect_status -- Read the bisect log from disk.
+
+--doc]]--
+
+--- unit -> state list
+local read_bisect_status = function ()
+ report ("info", 4, "bisect", "Testing for status file: %q.", bisect_status_file)
+ if not lfsisfile (bisect_status_file) then
+ report ("info", 2, "bisect", "No such file: %q.", bisect_status_file)
+ report ("info", 0, "bisect", "Not in bisect mode.")
+ return false
+ end
+ report ("info", 4, "bisect", "Reading status file: %q.", bisect_status_file)
+ local success, status = pcall (dofile, bisect_status_file)
+ if not success then
+ report ("info", 0, "bisect", "Could not read status file.")
+ return false
+ end
+ return status
+end
+
+--[[doc--
+
+ bisect_start -- Begin a bisect session. Determines the number of
+ fonts and sets the initial high, low, and pivot values.
+
+--doc]]--
+
+local bisect_start = function ()
+ if lfsisfile (bisect_status_file) then
+ report ("info", 0, "bisect",
+ "Bisect session in progress.",
+ bisect_status_file)
+ report ("info", 0, "bisect",
+ "Use --bisect=stop to erase it before starting over.")
+ return false, false
+ end
+ report ("info", 2, "bisect",
+ "Starting bisection of font database %q.", bisect_status_file)
+ local n = names.count_font_files ()
+ local pivot = mathfloor (n / 2)
+ local data = { { 1, n, pivot } }
+ report ("info", 0, "bisect", "Initializing pivot to %d.", pivot)
+ if write_bisect_status (data) then
+ return true, false
+ end
+ return false, false
+end
+
+--[[doc--
+
+ bisect_stop -- Terminate bisection session by removing all state info.
+
+--doc]]--
+
+local bisect_stop = function ()
+ report ("info", 3, "bisect", "Erasing bisection state at %s.", bisect_status_file)
+ if lfsisfile (bisect_status_file) then
+ local success, msg = os.remove (bisect_status_file)
+ if not success then
+ report ("info", 2, "bisect",
+ "Failed to erase file %s (%s).",
+ bisect_status_file, msg)
+ end
+ end
+ if lfsisdir (bisect_status_path) then
+ local success, msg = os.remove (bisect_status_path)
+ if not success then
+ report ("info", 2, "bisect",
+ "Failed to erase directory %s (%s).",
+ bisect_status_path, msg)
+ end
+ end
+ if lfsisfile (bisect_status_file) then
+ return false, false
+ end
+ return true, false
+end
+
+--[[doc--
+
+ bisect_terminate -- Wrap up a bisect session by printing the
+ offending font and removing the state file.
+
+--doc]]--
+
+local bisect_terminate = function (nsteps, culprit)
+ report ("info", 1, "bisect",
+ "Bisection completed after %d steps.", nsteps)
+ report ("info", 0, "bisect",
+ "Bad file: %s.", names.nth_font_filename (culprit))
+ report ("info", 0, "bisect",
+ "Run with --bisect=stop to finish bisection.")
+ return true, false
+end
+
+--[[doc--
+
+ list_remainder -- Show remaining fonts in bisect slice.
+
+--doc]]--
+
+local list_remainder = function (lo, hi)
+ local fonts = names.font_slice (lo, hi)
+ report ("info", 0, "bisect", "%d fonts left.", hi - lo + 1)
+ for i = 1, #fonts do
+ report ("info", 1, "bisect", " · %2d: %s", lo, fonts[i])
+ lo = lo + 1
+ end
+end
+
+--[[doc--
+
+ bisect_set -- Prepare the next bisection step by setting high, low,
+ and pivot to new values.
+
+ The “run” directive always picks the segment below the pivot so we
+ can rely on the “outcome parameter” to be referring to that.
+
+--doc]]--
+
+local bisect_set = function (outcome)
+ local status = read_bisect_status ()
+ if not status then
+ return false, false
+ end
+
+ local nsteps = #status
+ local previous = status[nsteps]
+ if previous == true then
+ --- Bisection already completed; we exit early through
+ --- bisect_terminate() to avoid further writes to the
+ --- state files that mess up step counting.
+ nsteps = nsteps - 1
+ return bisect_terminate (nsteps, status[nsteps][1])
+ end
+
+ local lo, hi, pivot = unpack (previous)
+
+ report ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.",
+ nsteps, lo, hi, pivot)
+
+ if outcome == "bad" then
+ hi = pivot
+ if lo >= hi then --- complete
+ status[nsteps + 1] = { lo, lo, lo }
+ status[nsteps + 2] = true
+ write_bisect_status (status)
+ return bisect_terminate (nsteps, lo)
+ end
+ pivot = mathfloor ((lo + hi) / 2)
+ report ("info", 0, "bisect",
+ "Continuing with the lower segment: lo=%d, hi=%d, pivot=%d.",
+ lo, hi, pivot)
+ elseif outcome == "good" then
+ lo = pivot + 1
+ if lo >= hi then --- complete
+ status[nsteps + 1] = { lo, lo, lo }
+ write_bisect_status (status)
+ status[nsteps + 2] = true
+ return bisect_terminate (nsteps, lo)
+ end
+ pivot = mathfloor ((lo + hi) / 2)
+ report ("info", 0, "bisect",
+ "Continuing with the upper segment: lo=%d, hi=%d, pivot=%d.",
+ lo, hi, pivot)
+ else -- can’t happen
+ report ("info", 0, "bisect", "What the hell?", lo, hi, pivot)
+ return false, false
+ end
+
+ status[nsteps + 1] = { lo, hi, pivot }
+ write_bisect_status (status)
+ if hi - lo <= 10 then
+ list_remainder (lo, hi)
+ end
+ return true, false
+end
+
+--[[doc--
+
+ bisect_status -- Output information about the current bisect session.
+
+--doc]]--
+
+local bisect_status = function ()
+ local status = read_bisect_status ()
+ if not status then
+ return false, false
+ end
+ local nsteps = #status
+ if nsteps > 1 then
+ for i = nsteps - 1, 1, -1 do
+ local step = status[i]
+ report ("info", 2, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.",
+ i, unpack (step))
+ end
+ end
+ local current = status[nsteps]
+ report ("info", 0, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.",
+ nsteps, unpack (current))
+ return true, false
+end
+
+--[[doc--
+
+ bisect_run -- Run Luaotfload utilizing the current bisection state.
+ This should be combined with the --update mode, possibly with the
+ --force option.
+
+ Luaotfload always tests the segment below the pivot first.
+
+--doc]]--
+
+local bisect_run = function ()
+ local status = read_bisect_status ()
+ if not status then
+ return false, false
+ end
+ local nsteps = #status
+ local currentstep = nsteps + 1
+ local current = status[nsteps]
+ if current == true then -- final step
+ current = status[nsteps - 1]
+ end
+ local lo, hi, pivot = unpack (current)
+ report ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.",
+ nsteps, lo, hi, pivot)
+ report ("info", 1, "bisect", "Step %d: Testing fonts from %d to %d.",
+ currentstep, lo, pivot)
+ config.luaotfload.misc.bisect = { lo, pivot }
+ return true, true
+end
+
+local bisect_modes = {
+ start = bisect_start,
+ good = function () return bisect_set "good" end,
+ bad = function () return bisect_set "bad" end,
+ stop = bisect_stop,
+ status = bisect_status,
+ run = bisect_run,
+}
+
+actions.bisect = function (job)
+ local mode = job.bisect
+ local runner = bisect_modes[mode]
+ if not runner then
+ report ("info", 0, "bisect", "Unknown directive %q.", mode)
+ return false, false
+ end
+ return runner (job)
+end
+
actions.flush = function (job)
- local success, lookups = names.flush_lookup_cache()
+ local success = names.flush_lookup_cache()
if success then
local success = names.save_lookups()
if success then
- logs.names_report("info", 2, "cache", "Lookup cache emptied")
+ report ("info", 2, "cache", "Lookup cache emptied")
return true, true
end
end
@@ -819,8 +1119,8 @@ local cache_directives = {
actions.cache = function (job)
local directive = cache_directives[job.cache]
if not directive or type(directive) ~= "function" then
- logs.names_report("info", 2, "cache",
- "Invalid font cache directive %s.", job.cache)
+ report ("info", 2, "cache",
+ "Invalid font cache directive %s.", job.cache)
return false, false
end
if directive() then
@@ -856,36 +1156,35 @@ actions.query = function (job)
then
foundname, subfont = names.resolve_name (tmpspec)
if foundname then
- foundname, _, success = names.crude_file_lookup (foundname)
+ foundname, _, success = names.font_file_lookup (foundname)
end
elseif tmpspec.lookup == "file" then
foundname, _, success =
- names.crude_file_lookup (tmpspec.name)
+ names.font_file_lookup (tmpspec.name)
end
if success then
- logs.names_report(false, 0,
- "resolve", "Font %q found!", query)
+ report (false, 0, "resolve", "Font %q found!", query)
if subfont then
- logs.names_report(false, 0, "resolve",
- "Resolved file name %q, subfont nr. %q",
+ report (false, 0, "resolve",
+ "Resolved file name %q, subfont nr. %q",
foundname, subfont)
else
- logs.names_report(false, 0, "resolve",
- "Resolved file name %q", foundname)
+ report (false, 0, "resolve",
+ "Resolved file name %q", foundname)
end
if job.show_info then
show_font_info (foundname, query, job.full_info, job.warnings)
iowrite "\n"
end
else
- logs.names_report(false, 0,
- "resolve", "Cannot find %q in index.", query)
- logs.names_report(false, 0,
- "resolve", "Hint: use the --fuzzy option to display suggestions.", query)
+ report (false, 0, "resolve", "Cannot find %q in index.", query)
+ report (false, 0, "resolve",
+ "Hint: use the --fuzzy option to display suggestions.",
+ query)
if job.fuzzy == true then
- logs.names_report(false, 0,
- "resolve", "Looking for close matches, this may take a while ...")
+ report (false, 0, "resolve",
+ "Looking for close matches, this may take a while ...")
local _success = names.find_closest(query, job.fuzzy_limit)
end
end
@@ -959,7 +1258,7 @@ set_primary_field = function (fields, addme, acc, n)
return acc
end
-local splitcomma = names.patterns.splitcomma
+local splitcomma = luaotfload.parsers.splitcomma
actions.list = function (job)
local criterion = job.criterion
@@ -983,7 +1282,7 @@ actions.list = function (job)
local nmappings = #mappings
if criterion == "*" then
- logs.names_report(false, 1, "list", "All %d entries", nmappings)
+ report (false, 1, "list", "All %d entries", nmappings)
for i=1, nmappings do
local entry = mappings[i]
local fields = get_fields(entry, asked_fields)
@@ -998,12 +1297,12 @@ actions.list = function (job)
criterion = criterion[1]
asked_fields = set_primary_field(asked_fields, criterion)
- logs.names_report(false, 1, "list", "By %s", criterion)
+ report (false, 1, "list", "By %s", criterion)
--- firstly, build a list of fonts to operate on
local targets = { }
if asked_value then --- only those whose value matches
- logs.names_report(false, 2, "list", "Restricting to value %s", asked_value)
+ report (false, 2, "list", "Restricting to value %s", asked_value)
for i=1, nmappings do
local entry = mappings[i]
if entry[criterion]
@@ -1048,7 +1347,7 @@ actions.list = function (job)
end
end
local ntargets = #targets
- logs.names_report(false, 2, "list", "%d entries", ntargets)
+ report (false, 2, "list", "%d entries", ntargets)
--- now, output the collection
for i=1, ntargets do
@@ -1088,7 +1387,7 @@ end
--[[--
Command-line processing.
-mkluatexfontdb.lua relies on the script alt_getopt to process argv and
+luaotfload-tool relies on the script alt_getopt to process argv and
analyzes its output.
TODO with extended lualibs we have the functionality from the
@@ -1103,13 +1402,15 @@ local process_cmdline = function ( ) -- unit -> jobspec
warnings = false,
criterion = "",
query = "",
- log_level = 0, --- 2 is approx. the old behavior
+ log_level = nil,
+ bisect = nil,
+ config = { db = { }, misc = { }, run = { }, paths = { } },
}
local long_options = {
- alias = 1,
+ ["bisect"] = 1,
cache = 1,
- ["no-compress"] = "c",
+ conf = 1,
diagnose = 1,
["dry-run"] = "D",
["flush-lookups"] = "l",
@@ -1123,12 +1424,15 @@ local process_cmdline = function ( ) -- unit -> jobspec
inspect = "I",
limit = 1,
list = 1,
+ ["local"] = "L",
log = 1,
["max-fonts"] = 1,
+ ["no-compress"] = "c",
["no-reload"] = "n",
["no-strip"] = 0,
["skip-read"] = "R",
["prefer-texmf"] = "p",
+ ["print-conf"] = 0,
quiet = "q",
["show-blacklist"] = "b",
stats = "S",
@@ -1138,7 +1442,7 @@ local process_cmdline = function ( ) -- unit -> jobspec
warnings = "w",
}
- local short_options = "bcDfFiIlnpqRSuvVhw"
+ local short_options = "bcDfFiIlLnpqRSuvVhw"
local options, _, optarg =
alt_getopt.get_ordered_opts (arg, short_options, long_options)
@@ -1151,11 +1455,13 @@ local process_cmdline = function ( ) -- unit -> jobspec
elseif v == "u" then
action_pending["generate"] = true
elseif v == "v" then
- if result.log_level > 0 then
- result.log_level = result.log_level + 1
+ local lvl = result.log_level
+ if not lvl or lvl < 1 then
+ lvl = 1
else
- result.log_level = 1
+ lvl = lvl + 1
end
+ result.log_level = lvl
elseif v == "V" then
action_pending["version"] = true
elseif v == "h" then
@@ -1177,7 +1483,7 @@ local process_cmdline = function ( ) -- unit -> jobspec
elseif v == "log" then
local str = optarg[n]
if str then
- finalizers = logs.set_logout(str, finalizers)
+ finalizers = log.set_logout(str, finalizers)
end
elseif v == "find" then
action_pending["query"] = true
@@ -1194,10 +1500,11 @@ local process_cmdline = function ( ) -- unit -> jobspec
elseif v == "I" then
result.show_info = true
result.full_info = true
- elseif v == "alias" then
- luaotfloadconfig.self = optarg[n]
elseif v == "l" then
action_pending["flush"] = true
+ elseif v == "L" then
+ action_pending["generate"] = true
+ config.luaotfload.db.scan_local = true
elseif v == "list" then
action_pending["list"] = true
result.criterion = optarg[n]
@@ -1218,35 +1525,46 @@ local process_cmdline = function ( ) -- unit -> jobspec
action_pending["diagnose"] = true
result.asked_diagnostics = optarg[n]
elseif v == "formats" then
- names.set_font_filter (optarg[n])
+ result.config.db.formats = optarg[n]
+ --names.set_font_filter (optarg[n])
elseif v == "n" then
- luaotfloadconfig.update_live = false
+ config.luaotfload.db.update_live = false
elseif v == "S" then
- luaotfloadconfig.statistics = true
+ config.luaotfload.misc.statistics = true
elseif v == "R" then
--- dev only, undocumented
- luaotfloadconfig.skip_read = true
+ config.luaotfload.db.skip_read = true
elseif v == "c" then
- luaotfloadconfig.compress = false
+ config.luaotfload.db.compress = false
elseif v == "no-strip" then
- luaotfloadconfig.strip = false
+ config.luaotfload.db.strip = false
elseif v == "max-fonts" then
local n = optarg[n]
if n then
n = tonumber(n)
if n and n > 0 then
- luaotfloadconfig.max_fonts = n
+ config.luaotfload.db.max_fonts = n
end
end
+ elseif v == "bisect" then
+ result.bisect = optarg[n]
+ action_pending.bisect = true
+ elseif v == "conf" then
+ local extra = stringexplode (optarg[n], ",+")
+ if extra then
+ local extra_config = result.extra_config
+ if extra_config then
+ table.append (extra_config, extra)
+ else
+ result.extra_config = extra
+ end
+ end
+ elseif v == "print-conf" then
+ result.print_config = true
end
end
- if luaotfloadconfig.self == "mkluatexfontdb" then --- TODO drop legacy ballast after 2.4
- result.help_version = "mkluatexfontdb"
- action_pending["generate"] = true
- result.log_level = math.max(1, result.log_level)
- logs.set_logout("stdout", finalizers)
- elseif nopts == 0 then
+ if nopts == 0 then
action_pending["help"] = true
result.help_version = "short"
end
@@ -1264,24 +1582,23 @@ local main = function ( ) -- unit -> int
local actionname = action_sequence[i]
local exit = false
if action_pending[actionname] then
- logs.names_report("log", 3, "util", "Preparing for task",
- "%s", actionname)
+ report ("log", 3, "util", "Preparing for task", "%s", actionname)
local action = actions[actionname]
local success, continue = action(job)
if not success then
- logs.names_report(false, 0, "util",
- "Could not finish task", "%s", actionname)
+ report (false, 0, "util",
+ "Failed to execute task.", "%s", actionname)
retval = -1
exit = true
elseif not continue then
- logs.names_report(false, 3, "util",
- "Task completed, exiting", "%s", actionname)
- exit = true
+ report (false, 3, "util",
+ "Task completed, exiting.", "%s", actionname)
+ exit = true
else
- logs.names_report(false, 3, "util",
- "Task completed successfully", "%s", actionname)
+ report (false, 3, "util",
+ "Task completed successfully.", "%s", actionname)
end
end
if exit then break end
diff --git a/src/luaotfload.sty b/src/luaotfload.sty
new file mode 100644
index 0000000..a235d6b
--- /dev/null
+++ b/src/luaotfload.sty
@@ -0,0 +1,45 @@
+%% Copyright (C) 2009-2014
+%%
+%% by Elie Roux <elie.roux@telecom-bretagne.eu>
+%% and Khaled Hosny <khaledhosny@eglug.org>
+%% and Philipp Gesang <philipp.gesang@alumni.uni-heidelberg.de>
+%%
+%% This file is part of Luaotfload.
+%%
+%% Home: https://github.com/lualatex/luaotfload
+%% Support: <lualatex-dev@tug.org>.
+%%
+%% Luaotfload is under the GPL v2.0 (exactly) license.
+%%
+%% ----------------------------------------------------------------------------
+%%
+%% Luaotfload is free software; you can redistribute it and/or
+%% modify it under the terms of the GNU General Public License
+%% as published by the Free Software Foundation; version 2
+%% of the License.
+%%
+%% Luaotfload is distributed in the hope that it will be useful,
+%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%% GNU General Public License for more details.
+%%
+%% You should have received a copy of the GNU General Public License
+%% along with Luaotfload; if not, see <http://www.gnu.org/licenses/>.
+%%
+%% ----------------------------------------------------------------------------
+%%
+%% Classical Plain+\LATEX package initialization.
+%%
+\csname ifluaotfloadloaded\endcsname
+\let\ifluaotfloadloaded\endinput
+\bgroup\expandafter\expandafter\expandafter\egroup
+\expandafter\ifx\csname ProvidesPackage\endcsname\relax
+ \input luatexbase.sty
+\else
+ \NeedsTeXFormat{LaTeX2e}
+ \ProvidesPackage{luaotfload}%
+ [2014/42/42 v2.5 OpenType layout system]
+ \RequirePackage{luatexbase}
+\fi
+\RequireLuaModule{luaotfload-main}
+
diff --git a/tests/alternate_sub.tex b/tests/alternate_sub.tex
deleted file mode 100644
index 862c665..0000000
--- a/tests/alternate_sub.tex
+++ /dev/null
@@ -1,15 +0,0 @@
-\input luaotfload.sty
-\font\0=name:Scheherazade:mode=node;script=arab;salt=0 at 10pt
-\font\1=name:Scheherazade:mode=node;script=arab;salt=1 at 10pt
-\font\2=name:Scheherazade:mode=node;script=arab;salt=2 at 10pt
-\0 \char"06DD
-\1 \char"06DD
-\2 \char"06DD
-
-\font\0=name:Scheherazade:mode=base;script=arab;salt=0 at 10pt
-\font\1=name:Scheherazade:mode=base;script=arab;salt=1 at 10pt
-\font\2=name:Scheherazade:mode=base;script=arab;salt=2 at 10pt
-\0 \char"06DD
-\1 \char"06DD
-\2 \char"06DD
-\bye
diff --git a/tests/anum.tex b/tests/anum.tex
deleted file mode 100644
index 773955f..0000000
--- a/tests/anum.tex
+++ /dev/null
@@ -1,17 +0,0 @@
-\input luaotfload.sty
-
-\font\testd={name:amiri:script=arab;language=dflt;+anum}
-\font\testa={name:amiri:script=arab;language=ara;+anum}
-\font\testp={name:amiri:script=arab;language=far;+anum}
-\font\tests={name:amiri:script=arab;language=snd;+anum}
-\font\testu={name:amiri:script=arab;language=urd;+anum}
-\font\testx={name:amiri:script=arab;language=xxx;+anum}
-
-\def\test{\luatextextdir TRT ضرب 0123456789}
-\testd \test\par
-\testa \test\par
-\testp \test\par
-\tests \test\par
-\testu \test\par
-\testx \test\par
-\bye
diff --git a/tests/caseinsensitive.tex b/tests/caseinsensitive.tex
deleted file mode 100644
index 0e9fb5f..0000000
--- a/tests/caseinsensitive.tex
+++ /dev/null
@@ -1,6 +0,0 @@
-\input luaotfload.sty
-
-\font\termesr ={tex gyre termes:+liga} at 10pt
-
-\termesr fi fl ffi ffl ff\par
-\bye
diff --git a/tests/color.tex b/tests/color.tex
deleted file mode 100644
index 188889c..0000000
--- a/tests/color.tex
+++ /dev/null
@@ -1,10 +0,0 @@
-\input luaotfload.sty
-
-\font\testa=file:lmroman10-regular.otf:color=FF0000BB;+trep at 10pt
-\font\testb=file:lmroman10-regular.otf:color=FFFF0099;+trep at 10pt
-\font\testc=file:lmroman10-regular.otf:color=559922;+trep at 12pt
-
-\testa FF0000BB \par
-\testb FFFF0099 \par
-\testc 559922 \par
-\bye
diff --git a/tests/fallback.tex b/tests/fallback.tex
deleted file mode 100644
index 71baea9..0000000
--- a/tests/fallback.tex
+++ /dev/null
@@ -1,6 +0,0 @@
-\input luaotfload.sty
-\font\testa={XITS Math}
-\font\testb={XITS Math/B}
-\testa text\par
-\testb text\par
-\bye
diff --git a/tests/featurefiles.tex b/tests/featurefiles.tex
deleted file mode 100644
index c1a3044..0000000
--- a/tests/featurefiles.tex
+++ /dev/null
@@ -1,5 +0,0 @@
-\input luaotfload.sty
-\font\pagella=texgyrepagella-regular:mode=node;featurefile=tkrn.fea;+tkrn
-
-\pagella TEX
-\bye
diff --git a/tests/font_patch.tex b/tests/font_patch.tex
deleted file mode 100644
index d3bba07..0000000
--- a/tests/font_patch.tex
+++ /dev/null
@@ -1,28 +0,0 @@
-\input{luaotfload.sty}
-\directlua {
- local function patch(fontdata)
- local mc = fontdata.MathConstants
- local em = fontdata.parameters.units
- local sz = fontdata.parameters.size
- if fontdata.psname == "CambriaMath" and mc then
- mc.DisplayOperatorMinHeight = 2800 / em * sz
- end
- end
- %% part of luaotfload-auxiliary
- %luatexbase.add_to_callback(
- %"luaotfload.patch_font",
- %patch,
- %"luaotfload.aux.patch_cambria_domh")
-}
-
-\font\4={name:Cambria Math:mode=base;script=math} at 10pt
-\font\5={name:Cambria Math:mode=base;script=math;ssty=1} at 7pt
-\font\6={name:Cambria Math:mode=node;script=math;ssty=2} at 5pt
-\textfont4=\4 \scriptfont4=\5 \scriptscriptfont4=\6
-
-$$
-\Umathchar"1"4`∫
-\Umathchar"1"4`∑
-$$
-\bye
-
diff --git a/tests/fontconfig_conf_reading.tex b/tests/fontconfig_conf_reading.tex
deleted file mode 100644
index 66ab377..0000000
--- a/tests/fontconfig_conf_reading.tex
+++ /dev/null
@@ -1,8 +0,0 @@
-\directlua{
- config = { lualibs = { load_extended = false } }
- require"lualibs"
- require"luaotfload-database"
- local results = fonts.names.read_fonts_conf{"fonts.conf.test"}
- inspect(results)
-}
-\bye
diff --git a/tests/fontencoding.tex b/tests/fontencoding.tex
deleted file mode 100644
index bbbbac6..0000000
--- a/tests/fontencoding.tex
+++ /dev/null
@@ -1,4 +0,0 @@
-\input luaotfload.sty
-\font\tenrm=ec-lmr10
-\tenrm
-\bye
diff --git a/tests/fonts.conf.test b/tests/fonts.conf.test
deleted file mode 100644
index 3c3e132..0000000
--- a/tests/fonts.conf.test
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
-<!-- /etc/fonts/fonts.conf file to configure system font access -->
-<fontconfig>
- <!-- this is a comment -->
- <!-- this is a comment with the <dir>test 0 failed!</dir> -->
- <!---->
- <!--
- this is a comment -->
- <!--
- this is a comment
- -->
- <!--
- this is a comment
- <dir>test -1 failed!</dir>
- -->
- <include prefix="xdg">fontconfig/fonts.conf</include><!-- this is a dir -->
- <dir>test 1 ok</dir>
- <dir>test 2 ok</dir><dir>test 3 ok</dir>
- <dir>test 4 ok</dir><!-- comment--><dir>test 5 ok</dir>
- <!-- this starts to be more debian-specific... change it to fit your system -->
- <include>/etc/fonts/conf.d</include><!-- this is a dir -->
- <include>/etc/fonts/fonts.conf</include>
- <include ignore_missing="no">/etc/fonts/conf.d/69-unifont.conf</include>
-</fontconfig>
diff --git a/tests/fontspec_lookup.ltx b/tests/fontspec_lookup.ltx
deleted file mode 100644
index 6645427..0000000
--- a/tests/fontspec_lookup.ltx
+++ /dev/null
@@ -1,41 +0,0 @@
-\documentclass[a5paper,12pt]{scrartcl}
-\usepackage{fontspec}
-%% --------------------------------------------------------------------
-%% weirdness ahead
-%% --------------------------------------------------------------------
-\setmainfont
- [Numbers=Lining,
- BoldFont={TeX Gyre Pagella Bold},
- BoldItalicFont={TeX Gyre Termes BoldItalic}]
- {EB Garamond}
-%% --------------------------------------------------------------------
-
-%% --------------------------------------------------------------------
-%% excerpt from samples/knuth.tex
-%% --------------------------------------------------------------------
-\def\knuth{%
- Thus, I came to the conclusion that the designer of a new
- system must not only be the implementer and first
- large--scale user; the designer should also write the first
- user manual.
-
- The separation of any of these four components would have
- hurt \TeX\ significantly. If I had not participated fully in
- all these activities, literally hundreds of improvements
- would never have been made, because I would never have
- thought of them or perceived why they were important.
-
-}
-
-%% --------------------------------------------------------------------
-%% main
-%% --------------------------------------------------------------------
-\begin{document}
-
- \section{regular} {\rmfamily\upshape\knuth}
- \section{bold face} {\rmfamily\bfseries\knuth}
- \section{italic} {\rmfamily\itshape\knuth}
- \section{slanted} {\rmfamily\slshape\knuth}
- \section{bold italic} {\rmfamily\bfseries\itshape\knuth}
-
-\end{document}
diff --git a/tests/frac.tex b/tests/frac.tex
deleted file mode 100644
index c6a4868..0000000
--- a/tests/frac.tex
+++ /dev/null
@@ -1,4 +0,0 @@
-\input luaotfload.sty
-\font\testa={name:Linux Libertine O:script=latn;+frac} at 10pt
-\testa 1/8
-\bye
diff --git a/tests/fullname.tex b/tests/fullname.tex
deleted file mode 100644
index 78cf4d0..0000000
--- a/tests/fullname.tex
+++ /dev/null
@@ -1,15 +0,0 @@
-\input luaotfload.sty
-
-\font\testa={LM Roman Slanted 10 Regular} at 10pt
-\font\testb={LM Roman 9 Italic} at 10pt
-\font\testc={TeX Gyre Termes Bold} at 25pt
-% Also testing with absolute filename, please change the path according to your
-% system
-\font\testd=file:/usr/share/texmf/fonts/opentype/public/lm/lmmono10-italic.otf
-
-\testa abcd ABCD\par
-\testb abcd ABCD\par
-\testc abcd ABCD\par
-\testd abcd ABCD\par
-
-\bye
diff --git a/tests/itlc.tex b/tests/itlc.tex
deleted file mode 100644
index bff0c39..0000000
--- a/tests/itlc.tex
+++ /dev/null
@@ -1,5 +0,0 @@
-\input luaotfload.sty
-\font\testa={Latin Modern Roman:script=latn} at 10pt
-\font\testb={Latin Modern Roman/I:script=latn} at 10pt
-\testa ({\testb f\/})
-\bye
diff --git a/tests/lookups.tex b/tests/lookups.tex
deleted file mode 100644
index 8b03d8e..0000000
--- a/tests/lookups.tex
+++ /dev/null
@@ -1,20 +0,0 @@
-\input luaotfload.sty
-%% lookup font by name (involving database)
-\font\first=name:iwonaregular at 42pt
-%% lookup font by file name (kpse)
-\font\second=file:antpoltltsemiexpd-bolditalic.otf at 42pt
-%% lookup font by name, with style in slash notation
-\font\third={name:Antykwa torunska/I} at 42pt
-%% unspecified lookup; in definers.read:
-%% - first it falls back to “file”
-%% - empty “method” field triggers fallback to “name”
-%% - names.resolve -> kpse.lookup -> hit!
-\font\fourth=iwona at 42pt
-
-{\first foo \endgraf}
-{\second bar \endgraf}
-{\third baz \endgraf}
-{\fourth xyzzy \endgraf}
-
-\bye
-
diff --git a/tests/marks.tex b/tests/marks.tex
deleted file mode 100644
index 9dcf460..0000000
--- a/tests/marks.tex
+++ /dev/null
@@ -1,10 +0,0 @@
-\input luaotfload.sty
-\font\test={file:GenBasR.ttf:script=latn}
-%font\test={name:AntykwaTorunska:script=latn}
-\test ä\quad Ä
-
-\test a\char"0308 %% -> combining dihaeresis
-\quad A\char"0308 %% -> combining dihaeresis
-\quad j\char"0323 %% -> combining dot below
-
-\bye
diff --git a/tests/math.tex b/tests/math.tex
deleted file mode 100644
index a2615f1..0000000
--- a/tests/math.tex
+++ /dev/null
@@ -1,56 +0,0 @@
-% start out with plain.tex,
-% having TFM-based CM fonts preloaded in \fam 0...3
-
-% load OT math font in \fam 4
-\input luaotfload.sty
-\font\4={name:XITS Math:mode=base;script=math} at 10pt
-\font\5={name:XITS Math:mode=base;script=math;ssty=1} at 7pt
-\font\6={name:XITS Math:mode=node;script=math;ssty=2} at 5pt
-\textfont4=\4 \scriptfont4=\5 \scriptscriptfont4=\6
-
-\Umathcode`a="7"4"1D44E
-\Umathcode`b="7"4"1D44F
-\Umathcode`x="7"4"1D465
-\Umathcode`y="7"4"1D466
-\Umathcode`A="7"4"1D434
-\Umathcode`B="7"4"1D435
-\Umathcode`C="7"4"1D436
-\Umathcode`P="7"4"1D443
-\Umathcode`3="7"4`3
-\Umathcode`+="2"4`+
-\Umathcode`=="3"4`=
-\Umathcode`(="4"4`(
-\Umathcode`)="5"4`)
-
-\Udelcode`(="4`(
-\Udelcode`)="4`)
-
-$$\Uradical "4 "221A {x}$$
-$$\Uroot "4 "221A {3}{x^3+y^3}$$
-
-$$
- \Udelimiterover "4 "23DE {a+b}
-+ \Udelimiterunder "4 "23DF {a+b} = C
-$$
-
-$$
- \Umathaccent "0 "4 "23DE {a+b}
-$$
-
-$$
-A \mathrel{\Uoverdelimiter "4 "2192 {a+b}}
-B \mathrel{\Uunderdelimiter "4 "2192 {a+b}}
-$$
-
-$$
-{a \overwithdelims() b}
-$$
-
-
-\centerline{$ a \overwithdelims() b $}
-
-$$
-P(a)
-$$
-
-\bye
diff --git a/tests/microtypography.tex b/tests/microtypography.tex
deleted file mode 100644
index 99deb5f..0000000
--- a/tests/microtypography.tex
+++ /dev/null
@@ -1,36 +0,0 @@
-\input luaotfload.sty
-
-\pdfprotrudechars2 \pdfadjustspacing2
-
-\font\testa=file:texgyretermes-regular:script=latn at 12pt
-\font\testb=file:texgyretermes-regular:script=latn;protrusion=default at 12pt
-
-\testa We thrive in information thick worlds because of our
-marvelous and everyday capacity to select, edit,
-single out, structure, highlight, group, pair, merge,
-harmonize, synthesize, focus, organize, condense,
-reduce, boil down, choose, categorize, catalog, classify,
-list, abstract, scan, look into, idealize, isolate,
-discriminate, distinguish, screen, pigeonhole, pick over,
-sort, integrate, blend, inspect, filter, lump, skip,
-smooth, chunk, average, approximate, cluster, aggregate,
-outline, summarize, itemize, review, dip into,
-flip through, browse, glance into, leaf through, skim,
-refine, enumerate, glean, synopsize, winnow the wheat
-from the chaff and separate the sheep from the goats.\par
-
-\testb We thrive in information thick worlds because of our
-marvelous and everyday capacity to select, edit,
-single out, structure, highlight, group, pair, merge,
-harmonize, synthesize, focus, organize, condense,
-reduce, boil down, choose, categorize, catalog, classify,
-list, abstract, scan, look into, idealize, isolate,
-discriminate, distinguish, screen, pigeonhole, pick over,
-sort, integrate, blend, inspect, filter, lump, skip,
-smooth, chunk, average, approximate, cluster, aggregate,
-outline, summarize, itemize, review, dip into,
-flip through, browse, glance into, leaf through, skim,
-refine, enumerate, glean, synopsize, winnow the wheat
-from the chaff and separate the sheep from the goats.\par
-
-\bye
diff --git a/tests/opbd.fea b/tests/opbd.fea
deleted file mode 100644
index 54f687a..0000000
--- a/tests/opbd.fea
+++ /dev/null
@@ -1,187 +0,0 @@
-languagesystem DFLT dlft;
-languagesystem grek dflt;
-languagesystem latn dflt;
-languagesystem latn AZE;
-languagesystem latn CRT;
-languagesystem latn MOL;
-languagesystem latn NLD;
-languagesystem latn PLK;
-languagesystem latn ROM;
-languagesystem latn TRK;
-
-feature rtbd {
- lookupflag 0;
- pos \exclam <100 0 0 0>;
- pos \percent <100 0 0 0>;
- pos \ampersand <100 0 0 0>;
- pos \parenright <300 0 0 0>;
- pos \asterisk <200 0 0 0>;
- pos \plus <250 0 0 0>;
- pos \comma <500 0 0 0>;
- pos \hyphen <500 0 0 0>;
- pos \period <700 0 0 0>;
- pos \slash <300 0 0 0>;
- pos \one <100 0 0 0>;
- pos \seven <50 0 0 0>;
- pos \colon <500 0 0 0>;
- pos \semicolon <500 0 0 0>;
- pos \question <200 0 0 0>;
- pos \at <50 0 0 0>;
- pos \A <50 0 0 0>;
- pos \K <50 0 0 0>;
- pos \L <50 0 0 0>;
- pos \T <50 0 0 0>;
- pos \V <50 0 0 0>;
- pos \W <50 0 0 0>;
- pos \X <50 0 0 0>;
- pos \Y <50 0 0 0>;
- pos \k <50 0 0 0>;
- pos \p <50 0 0 0>;
- pos \r <50 0 0 0>;
- pos \v <50 0 0 0>;
- pos \w <50 0 0 0>;
- pos \x <50 0 0 0>;
- pos \y <70 0 0 0>;
- pos \asciitilde <250 0 0 0>;
- pos \Agrave <50 0 0 0>;
- pos \Aacute <50 0 0 0>;
- pos \Acircumflex <50 0 0 0>;
- pos \Atilde <50 0 0 0>;
- pos \Adieresis <50 0 0 0>;
- pos \Aring <50 0 0 0>;
- pos \Amacron <50 0 0 0>;
- pos \Abreve <50 0 0 0>;
- pos \Aogonek <50 0 0 0>;
- pos \Kcommaaccent <50 0 0 0>;
- pos \kcommaaccent <50 0 0 0>;
- pos \Lacute <50 0 0 0>;
- pos \Lcommaaccent <50 0 0 0>;
- pos \Lcaron <50 0 0 0>;
- pos \Ldot <50 0 0 0>;
- pos \Lslash <50 0 0 0>;
- pos \racute <50 0 0 0>;
- pos \rcommaaccent <50 0 0 0>;
- pos \rcaron <50 0 0 0>;
- pos \wcircumflex <50 0 0 0>;
- pos \ycircumflex <70 0 0 0>;
- pos \Acaron <50 0 0 0>;
- pos \Aringacute <50 0 0 0>;
- pos \Adblgrave <50 0 0 0>;
- pos \rdblgrave <50 0 0 0>;
- pos \uni021A <50 0 0 0>;
- pos \Alpha <50 0 0 0>;
- pos \Kappa <50 0 0 0>;
- pos \Lambda <50 0 0 0>;
- pos \Tau <50 0 0 0>;
- pos \Ldotbelow <50 0 0 0>;
- pos \Ldotbelowmacron <50 0 0 0>;
- pos \rdotaccent <50 0 0 0>;
- pos \rdotbelow <50 0 0 0>;
- pos \rdotbelowmacron <50 0 0 0>;
- pos \Tdotbelow <50 0 0 0>;
- pos \Tlinebelow <50 0 0 0>;
- pos \wgrave <50 0 0 0>;
- pos \wacute <50 0 0 0>;
- pos \wdieresis <50 0 0 0>;
- pos \Ahookabove <50 0 0 0>;
- pos \Acircumflexacute <50 0 0 0>;
- pos \Acircumflexgrave <50 0 0 0>;
- pos \Acircumflexhookabove <50 0 0 0>;
- pos \Acircumflextilde <50 0 0 0>;
- pos \Acircumflexdotbelow <50 0 0 0>;
- pos \Abreveacute <50 0 0 0>;
- pos \Abrevegrave <50 0 0 0>;
- pos \Abrevehookabove <50 0 0 0>;
- pos \Abrevetilde <50 0 0 0>;
- pos \Abrevedotbelow <50 0 0 0>;
- pos \ygrave <70 0 0 0>;
- pos \ydotbelow <70 0 0 0>;
- pos \yhookabove <70 0 0 0>;
- pos \ytilde <70 0 0 0>;
- pos \endash <300 0 0 0>;
- pos \emdash <200 0 0 0>;
- pos \quoteleft <700 0 0 0>;
- pos \quoteright <700 0 0 0>;
- pos \quotedblleft <400 0 0 0>;
- pos \quotedblright <400 0 0 0>;
- pos \Aogonekacute <50 0 0 0>;
- pos \L_uni0303 <50 0 0 0>;
- pos \T_uni0303 <50 0 0 0>;
- pos \T_uni0308 <50 0 0 0>;
-} rtbd;
-
-feature lfbd {
- lookupflag 0;
- pos \percent <-100 0 -100 0>;
- pos \ampersand <-50 0 -50 0>;
- pos \parenleft <-100 0 -100 0>;
- pos \asterisk <-200 0 -200 0>;
- pos \plus <-250 0 -250 0>;
- pos \hyphen <-400 0 -400 0>;
- pos \slash <-200 0 -200 0>;
- pos \one <-100 0 -100 0>;
- pos \at <-50 0 -50 0>;
- pos \A <-50 0 -50 0>;
- pos \J <-50 0 -50 0>;
- pos \T <-50 0 -50 0>;
- pos \V <-50 0 -50 0>;
- pos \W <-50 0 -50 0>;
- pos \X <-50 0 -50 0>;
- pos \Y <-50 0 -50 0>;
- pos \p <-50 0 -50 0>;
- pos \q <-50 0 -50 0>;
- pos \v <-50 0 -50 0>;
- pos \w <-50 0 -50 0>;
- pos \x <-50 0 -50 0>;
- pos \y <-50 0 -50 0>;
- pos \asciitilde <-200 0 -200 0>;
- pos \Agrave <-50 0 -50 0>;
- pos \Aacute <-50 0 -50 0>;
- pos \Acircumflex <-50 0 -50 0>;
- pos \Atilde <-50 0 -50 0>;
- pos \Adieresis <-50 0 -50 0>;
- pos \Aring <-50 0 -50 0>;
- pos \Amacron <-50 0 -50 0>;
- pos \Abreve <-50 0 -50 0>;
- pos \Aogonek <-50 0 -50 0>;
- pos \Jcircumflex <-50 0 -50 0>;
- pos \wcircumflex <-50 0 -50 0>;
- pos \ycircumflex <-50 0 -50 0>;
- pos \Acaron <-50 0 -50 0>;
- pos \Aringacute <-50 0 -50 0>;
- pos \Adblgrave <-50 0 -50 0>;
- pos \uni021A <-50 0 -50 0>;
- pos \Alpha <-50 0 -50 0>;
- pos \Lambda <-50 0 -50 0>;
- pos \Tau <-50 0 -50 0>;
- pos \Tdotbelow <-50 0 -50 0>;
- pos \Tlinebelow <-50 0 -50 0>;
- pos \wgrave <-50 0 -50 0>;
- pos \wacute <-50 0 -50 0>;
- pos \wdieresis <-50 0 -50 0>;
- pos \Ahookabove <-50 0 -50 0>;
- pos \Acircumflexacute <-50 0 -50 0>;
- pos \Acircumflexgrave <-50 0 -50 0>;
- pos \Acircumflexhookabove <-50 0 -50 0>;
- pos \Acircumflextilde <-50 0 -50 0>;
- pos \Acircumflexdotbelow <-50 0 -50 0>;
- pos \Abreveacute <-50 0 -50 0>;
- pos \Abrevegrave <-50 0 -50 0>;
- pos \Abrevehookabove <-50 0 -50 0>;
- pos \Abrevetilde <-50 0 -50 0>;
- pos \Abrevedotbelow <-50 0 -50 0>;
- pos \ygrave <-50 0 -50 0>;
- pos \ydotbelow <-50 0 -50 0>;
- pos \yhookabove <-50 0 -50 0>;
- pos \ytilde <-50 0 -50 0>;
- pos \endash <-300 0 -300 0>;
- pos \emdash <-200 0 -200 0>;
- pos \quoteleft <-500 0 -500 0>;
- pos \quoteright <-500 0 -500 0>;
- pos \quotedblleft <-300 0 -300 0>;
- pos \quotedblright <-300 0 -300 0>;
- pos \Aogonekacute <-50 0 -50 0>;
- pos \J_uni030C.cap <-50 0 -50 0>;
- pos \T_uni0303 <-50 0 -50 0>;
- pos \T_uni0308 <-50 0 -50 0>;
-} lfbd;
diff --git a/tests/opbd.tex b/tests/opbd.tex
deleted file mode 100644
index 1a838cd..0000000
--- a/tests/opbd.tex
+++ /dev/null
@@ -1,35 +0,0 @@
-\input luaotfload.sty
-
-\pdfprotrudechars2 \pdfadjustspacing2
-
-\font\testa=file:texgyrepagella-regular:script=latn at 12pt
-\font\testb=file:texgyrepagella-regular:mode=node;script=latn;protrusion=yes;featurefile=opbd.fea;+opbd at 12pt
-
-\testa We thrive in information thick worlds because of our
-marvelous and everyday capacity to select, edit,
-single out, structure, highlight, group, pair, merge,
-harmonize, synthesize, focus, organize, condense,
-reduce, boil down, choose, categorize, catalog, classify,
-list, abstract, scan, look into, idealize, isolate,
-discriminate, distinguish, screen, pigeonhole, pick over,
-sort, integrate, blend, inspect, filter, lump, skip,
-smooth, chunk, average, approximate, cluster, aggregate,
-outline, summarize, itemize, review, dip into,
-flip through, browse, glance into, leaf through, skim,
-refine, enumerate, glean, synopsize, winnow the wheat
-from the chaff and separate the sheep from the goats.\par
-
-\testb We thrive in information thick worlds because of our
-marvelous and everyday capacity to select, edit,
-single out, structure, highlight, group, pair, merge,
-harmonize, synthesize, focus, organize, condense,
-reduce, boil down, choose, categorize, catalog, classify,
-list, abstract, scan, look into, idealize, isolate,
-discriminate, distinguish, screen, pigeonhole, pick over,
-sort, integrate, blend, inspect, filter, lump, skip,
-smooth, chunk, average, approximate, cluster, aggregate,
-outline, summarize, itemize, review, dip into,
-flip through, browse, glance into, leaf through, skim,
-refine, enumerate, glean, synopsize, winnow the wheat
-from the chaff and separate the sheep from the goats.\par
-\bye
diff --git a/tests/opticalsize.tex b/tests/opticalsize.tex
deleted file mode 100644
index 53b1bb8..0000000
--- a/tests/opticalsize.tex
+++ /dev/null
@@ -1,13 +0,0 @@
-\input luaotfload.sty
-
-\font\testa={Latin Modern Roman/S=12} at 10pt
-\font\testb={Latin Modern Roman} at 5pt
-\font\testc={Latin Modern Roman} at 10pt
-\font\testd={Latin Modern Roman/S=15} at 10pt
-\font\teste={Latin Modern Roman/S=3} at 10pt
-\testa abcd ABCD \par
-\testb abcd ABCD \par
-\testc abcd ABCD \par
-\testd abcd ABCD \par
-\teste abcd ABCD \par
-\bye
diff --git a/tests/pln-aux-1.tex b/tests/pln-aux-1.tex
deleted file mode 100644
index a228be0..0000000
--- a/tests/pln-aux-1.tex
+++ /dev/null
@@ -1,55 +0,0 @@
-\input luaotfload.sty
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% usage for glyph tests
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\baselineskip=17.28pt
-
-\font\iwonaregular=name:iwona at 14.4pt
-\font\lmromanten=file:lmroman10-regular.otf at 14.4pt
-\font\cmuregular=file:cmunrm.otf at 14.4pt
-
-%% wrap tests in macros (could move to style file)
-\def\doifglyphelse#1#2#3{%
- \directlua{
- local codepoint = tonumber('\string#1')
- if not codepoint then codepoint = unicode.utf8.byte('\string#1') end
- if luaotfload.aux.font_has_glyph(font.current(), codepoint) then
- tex.sprint('\string#2')
- else
- tex.sprint('\string#3')
- end
- }%
-}
-
-\def\doifglyph#1#2{\doifglyphelse{#1}{#2}{}}
-
-%% no otf font loaded yet, so both fail:
-first:
-\doifglyphelse{a}{true}{false}
-\doifglyph {a}{yep}
-
-%% load lm and try repeat:
-\lmromanten
-second:
-\doifglyphelse{a}{true}{false}
-\doifglyph {a}{yep}
-
-%% let’s test some more free fonts
-\def\checkglyphset{%
- \doifglyphelse ö{ö}{nope}
- \doifglyphelse п{п}{nope}
- \doifglyphelse α{α}{nope}
- \doifglyphelse Æ{Æ}{nope}
- \doifglyphelse ą{ą}{nope}
- \doifglyphelse ř{ř}{nope}
- \doifglyphelse ˝{˝}{nope}
- \doifglyphelse ѩ{ѩ}{nope}
- \endgraf
-}
-
-\iwonaregular \checkglyphset
-\lmromanten \checkglyphset
-\cmuregular \checkglyphset
-
-\bye
diff --git a/tests/pln-aux-2.tex b/tests/pln-aux-2.tex
deleted file mode 100644
index 62192a5..0000000
--- a/tests/pln-aux-2.tex
+++ /dev/null
@@ -1,102 +0,0 @@
-\input luaotfload.sty
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% script, features, and language
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\font\minionregular=file:MinionPro_Regular.otf at 9pt
-\font\biolinum=file:LinBiolinum_R.otf at 9pt
-\font\cmuregular=file:cmunrm.otf at 9pt
-
-%% (1) luaotfload.aux.provides_script(font_id, script)
-%% #1 defined font; #2 OT script tag
-\def\providesscript[#1][#2]{%
- \bgroup#1%
- let’s see whether \detokenize{#1} has script #2:
- \directlua{
- local aux = luaotfload.aux
- local succ = aux.provides_script(font.current(), [[#2]])
- if succ then tex.sprint"true" else tex.sprint"false" end
- }%
- \egroup
- \endgraf%
-}
-
-\providesscript [\minionregular][latn]%% Latin
-\providesscript [\biolinum][latn]
-\providesscript [\cmuregular][latn]
-\providesscript [\minionregular][cyrl]%% Cyrillic
-\providesscript [\biolinum][cyrl]
-\providesscript [\cmuregular][cyrl]
-\providesscript [\minionregular][tibt]%% Tibetan
-\providesscript [\biolinum][tibt]
-\providesscript [\cmuregular][tibt]
-
-\hrule % --------------------------------------------------------------
-
-%% (2) luaotfload.aux.provides_language(font_id, script, language)
-%% #1 defined font; #2 OT script tag; #3 OT language tag
-\def\provideslanguage[#1][#2][#3]{%
- \bgroup#1%
- let’s see whether \detokenize{#1} supports language #3 for script #2:
- \directlua{
- local aux = luaotfload.aux
- local succ = aux.provides_language(font.current(), [[#2]], [[#3]])
- if succ then tex.sprint"true" else tex.sprint"false" end
- }%
- \egroup
- \endgraf%
-}
-
-\provideslanguage [\minionregular][latn][nld]%% Latin/Dutch
-\provideslanguage [\biolinum][latn][nld]
-\provideslanguage [\cmuregular][latn][nld]
-\provideslanguage [\minionregular][latn][deu]%% Latin/German
-\provideslanguage [\biolinum][latn][deu]
-\provideslanguage [\cmuregular][latn][deu]
-\provideslanguage [\minionregular][cyrl][rus]%% Cyrillic/Russian
-\provideslanguage [\biolinum][cyrl][rus]
-\provideslanguage [\cmuregular][cyrl][rus]
-\provideslanguage [\minionregular][cyrl][klm]%% Cyrillic/Kalmyk
-\provideslanguage [\biolinum][cyrl][klm]
-\provideslanguage [\cmuregular][cyrl][klm]
-\provideslanguage [\minionregular][cyrl][srb]%% Cyrillic/Serbian
-\provideslanguage [\biolinum][cyrl][srb]
-\provideslanguage [\cmuregular][cyrl][srb]
-\provideslanguage [\minionregular][tibt][tib]%% Tibetan
-\provideslanguage [\biolinum][tibt][tib]
-\provideslanguage [\cmuregular][tibt][tib]
-
-\hrule % --------------------------------------------------------------
-
-%% (3) luaotfload.aux.provides_feature(
-%% font_id, script, language, feature)
-%% #1 defined font; #2 OT script tag;
-%% #3 OT language tag; #4 OT feature
-\def\providesfeature[#1][#2][#3][#4]{%this is getting ridiculous
- \bgroup#1%
- let’s see whether \detokenize{#1} supports feature #4 for the
- combination of script #2 with language #3:
- \directlua{
- local aux = luaotfload.aux
- local succ = aux.provides_feature(
- font.current(), [[#2]], [[#3]], [[#4]])
- if succ then tex.sprint"true" else tex.sprint"false" end
- }%
- \egroup
- \endgraf%
-}
-
-\providesfeature [\minionregular][latn][nld][liga]%% Latin/Dutch
-\providesfeature [\biolinum][latn][nld][liga]
-\providesfeature [\cmuregular][latn][nld][liga]
-\providesfeature [\minionregular][latn][deu][liga]%% Latin/German
-\providesfeature [\biolinum][latn][deu][liga]
-\providesfeature [\cmuregular][latn][deu][liga]
-\providesfeature [\minionregular][cyrl][srb][liga]%% Cyrillic/Serbian
-\providesfeature [\biolinum][cyrl][srb][liga]
-\providesfeature [\cmuregular][cyrl][srb][liga]
-\providesfeature [\minionregular][tibt][tib][liga]%% Tibetan
-\providesfeature [\biolinum][tibt][tib][liga]
-\providesfeature [\cmuregular][tibt][tib][liga]
-
-\bye
diff --git a/tests/pln-aux-3.tex b/tests/pln-aux-3.tex
deleted file mode 100644
index 12a80cf..0000000
--- a/tests/pln-aux-3.tex
+++ /dev/null
@@ -1,39 +0,0 @@
-\input luaotfload.sty
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% math dimension getter
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\font\xitsmath=file:xits-math.otf
-\font\cambriamath=file:cambria.ttc(1)
-
-\font\main=file:Iwona-Regular.otf at 12pt\main
-
-\directlua{
- local aux = luaotfload.aux
- local test_a = function (fontname, dimension)
- tex.sprint(
- "(", fontname, " (", dimension, " ",
- aux.get_math_dimension(fontname, dimension),
- [[))\endgraf ]])
- end
-
- local test_b = function (fontname, dimension)
- aux.sprint_math_dimension(fontname, dimension)
- tex.print[[\endgraf ]]
- end
-
- test_a("xitsmath", "AxisHeight")
- test_a("xitsmath", "RadicalVerticalGap")
- test_a("cambriamath", "StackTopShiftUp")
- test_a("cambriamath", "FractionNumeratorGapMin")
-
- test_b("xitsmath", "AxisHeight")
- test_b("xitsmath", "RadicalVerticalGap")
- test_b("cambriamath", "StackTopShiftUp")
- test_b("cambriamath", "FractionNumeratorGapMin")
-}
-
-foo bar baz
-
-\bye
diff --git a/tests/pln-aux-4.tex b/tests/pln-aux-4.tex
deleted file mode 100644
index 80ffc0b..0000000
--- a/tests/pln-aux-4.tex
+++ /dev/null
@@ -1,41 +0,0 @@
-\input luaotfload.sty
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% unicode character mappings
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-\font\ptserifregular = file:PTF55F.ttf \ptserifregular
-
-%% here we map the function luaotfload.aux.name_of_slot
-%% on a short text, printing a list of letters, their
-%% code points and names (as specified in the Adobe
-%% Glyph List).
-
-\directlua{
- local aux = luaotfload.aux
- local cbk = function (str)
- if string.match(str, "^EOF") then
- luatexbase.remove_from_callback("process_input_buffer", "weird")
- return [[the end!]]
- end
- local res = { }
- for chr in string.utfcharacters(str) do
- local val = unicode.utf8.byte(chr)
- local line = chr .. " <> " .. tostring(val)
- line = line .. " <> " .. (aux.name_of_slot(val) or "")
- res[\string#res+1] = line
- end
- return table.concat(res, [[\endgraf]])
- end
-
- luatexbase.add_to_callback("process_input_buffer", cbk, "weird")
-}
-
-Я узнал что у меня
-Есть огромная семья
-И тропинка и лесок
-В поле каждый колосок
-
-EOF
-
-\bye
diff --git a/tests/pln-request-4-slashed.tex b/tests/pln-request-4-slashed.tex
deleted file mode 100644
index 5e7d99e..0000000
--- a/tests/pln-request-4-slashed.tex
+++ /dev/null
@@ -1,12 +0,0 @@
-\ifdefined\directlua\input luaotfload.sty\fi
-\font\iwona =iwona at 20pt
-\font\iwonabold =iwona/b at 20pt
-\font\iwonaitalic =iwona/i at 20pt
-\font\iwonabolditalic =iwona/bi at 20pt
-
-\def\test{foo bar baz \endgraf}
-{\iwona \test}
-{\iwonabold \test}
-{\iwonaitalic \test}
-{\iwonabolditalic \test}
-\bye
diff --git a/tests/pln-request-5-cached.tex b/tests/pln-request-5-cached.tex
deleted file mode 100644
index 8ba4a5e..0000000
--- a/tests/pln-request-5-cached.tex
+++ /dev/null
@@ -1,18 +0,0 @@
-\ifdefined\directlua
- \directlua{config = config or { luaotfload = { } }
- config.luaotfload.resolver = "cached"
- config.luaotfload.loglevel = 5 }
- \input luaotfload.sty
-\fi
-
-\font\iwona =name:iwona at 20pt
-\font\iwonabold =name:iwona/b at 20pt
-\font\iwonaitalic =name:iwona/i at 20pt
-\font\iwonabolditalic =name:iwona/bi at 20pt
-
-\def\test{foo bar baz \endgraf}
-{\iwona \test}
-{\iwonabold \test}
-{\iwonaitalic \test}
-{\iwonabolditalic \test}
-\bye
diff --git a/tests/pln-subfont-1.tex b/tests/pln-subfont-1.tex
deleted file mode 100644
index fb8e1e7..0000000
--- a/tests/pln-subfont-1.tex
+++ /dev/null
@@ -1,12 +0,0 @@
-\ifdefined\directlua\input luaotfload.sty\fi
-%% This requires the Cambria fonts from MS.
-\directlua{
- inspect(fontloader.info"cambria.ttc")
-}
-%% Here we load both subfonts in the collection
-%% with the not-quite documented subfont syntax.
-\font\subfontone="file:cambria.ttc(0)" at 42pt
-\font\subfonttwo="file:cambria.ttc(1)" at 42pt
-\subfontone foo bar baz \endgraf
-\subfonttwo foo bar baz \endgraf
-\bye
diff --git a/tests/pln-tfm.tex b/tests/pln-tfm.tex
deleted file mode 100644
index 16ae41a..0000000
--- a/tests/pln-tfm.tex
+++ /dev/null
@@ -1,10 +0,0 @@
-\ifdefined\directlua\input luaotfload.sty \fi
-%% TFM’s can be loaded with a file: request ...
-\font\antykwatorunska="file:rm-anttr"
-%% or with an anonymous request, like in þe olde TeX:
-\font\antykwatorunskabcap=ec-anttbcap
-\font\lmromanten={file:ec-lmr10} at 10pt
-\antykwatorunska foo bar
-\antykwatorunskabcap baz xyzzy
-\lmromanten Donde, está, la biblioteca. Me llamo T-Bone La araña discoteca.
-\bye
diff --git a/tests/sanitize_color.tex b/tests/sanitize_color.tex
deleted file mode 100644
index fe95d37..0000000
--- a/tests/sanitize_color.tex
+++ /dev/null
@@ -1,8 +0,0 @@
-\input luaotfload.sty
-\font\testa={name:Latin Modern Roman:color=0000ff99f} at 10pt
-\font\testb={name:Latin Modern Roman:color=0000ff9} at 10pt
-\font\testc={name:Latin Modern Roman:color=0000f} at 10pt
-\testa test\par
-\testb test\par
-\testc test\par
-\bye
diff --git a/tests/systemfonts.tex b/tests/systemfonts.tex
deleted file mode 100644
index af08509..0000000
--- a/tests/systemfonts.tex
+++ /dev/null
@@ -1,50 +0,0 @@
-\input luaotfload.sty
-
-\font\termesr ={TeX Gyre Termes:+liga} at 10pt
-\font\termesb ={TeX Gyre Termes/B:+liga} at 10pt
-\font\termesi ={TeX Gyre Termes/I:+liga} at 10pt
-\font\termesbi ={TeX Gyre Termes/BI:+liga} at 10pt
-\font\termesib ={TeX Gyre Termes/IB:+liga} at 10pt
-\font\termesicu={TeX Gyre Termes/ICU/B:+liga} at 10pt
-\font\termesaat={TeX Gyre Termes/AAT/IB:+liga} at 10pt
-\font\termess ={TeX Gyre Termes/ICU/S=10:+liga} at 10pt
-\font\dsans ={DejaVu Sans:+liga} at 10pt
-\font\dsansi ={DejaVu Sans/I:+liga} at 10pt
-\font\dsansb ={DejaVu Sans/B:+liga} at 10pt
-\font\dsansib ={DejaVu Sans/IB:+liga} at 10pt
-\font\dsansextr={DejaVu Sans/Extra Light:+liga} at 10pt
-\font\dsansecnd={DejaVu Sans/Condensed:+liga} at 10pt
-\font\lmr ={Latin Modern Roman:+liga} at 10pt
-\font\lmf ={Latin Modern Roman/S=5:+liga} at 10pt
-\font\lmb ={Latin Modern Roman/B:+liga} at 10pt
-\font\lmi ={Latin Modern Roman/I:+liga} at 10pt
-\font\lmbi ={Latin Modern Roman/BI:+liga} at 10pt
-\font\lms ={Latin Modern Roman Slanted:+liga} at 10pt
-\font\lmltn ={Latin Modern Roman:script=latn} at 10pt
-%% get this font here:
-%% http://sourceforge.net/projects/arabeyes/files/kacst_fonts/kacst_one_5.0.tar.bz2/download
-\font\arab ={KacstOne:mode=node;script=arab} at 10pt
-
-\termesr fi fl ffi ffl ff\par
-\termesb fi fl ffi ffl ff\par
-\termesi fi fl ffi ffl ff\par
-\termesbi fi fl ffi ffl ff\par
-\termesib fi fl ffi ffl ff\par
-\termesicu fi fl ffi ffl ff\par
-\termesaat fi fl ffi ffl ff\par
-\termess fi fl ffi ffl ff\par
-\dsans fi fl ffi ffl ff\par
-\dsansi fi fl ffi ffl ff\par
-\dsansb fi fl ffi ffl ff\par
-\dsansib fi fl ffi ffl ff\par
-\dsansextr fi fl ffi ffl ff\par
-\dsansecnd fi fl ffi ffl ff\par
-\lmr fi fl ffi ffl ff\par
-\lmf fi fl ffi ffl ff\par
-\lmb fi fl ffi ffl ff\par
-\lmi fi fl ffi ffl ff\par
-\lmbi fi fl ffi ffl ff\par
-\lms fi fl ffi ffl ff\par
-\lmltn fi fl ffi ffl ff\par
-\leavevmode\arab\luatextextdir TRT بِسْمِ الله الرَّحْمنِ الرحيم\par
-\bye
diff --git a/tests/texligatures.tex b/tests/texligatures.tex
deleted file mode 100644
index 8317ee1..0000000
--- a/tests/texligatures.tex
+++ /dev/null
@@ -1,7 +0,0 @@
-\input luaotfload.sty
-
-\font\testa={file:lmroman10-regular:mode=base;script=latn;+tlig} at 10pt
-\font\testb={file:lmroman10-regular:mode=node;script=latn;+tlig} at 10pt
-\testa ``fi `fl' ffi--ffl---ff'' !` ?` "\par
-\testb ``fi `fl' ffi--ffl---ff'' !` ?` "\par
-\bye
diff --git a/tests/tfmofm.ltx b/tests/tfmofm.ltx
deleted file mode 100644
index 0f9f904..0000000
--- a/tests/tfmofm.ltx
+++ /dev/null
@@ -1,6 +0,0 @@
-\documentclass{article}
-\usepackage{luaotfload}
-
-\begin{document}
-Hello \(1+1=\sqrt{4}\)
-\end{document}
diff --git a/tests/tkrn.fea b/tests/tkrn.fea
deleted file mode 100644
index c83927d..0000000
--- a/tests/tkrn.fea
+++ /dev/null
@@ -1,8 +0,0 @@
-languagesystem DFLT dflt;
-languagesystem latn dflt;
-
-feature tkrn {
- lookupflag 0;
- pos E X -125;
- pos T <0 0 -166 0> E <0 -235 0 0>;
-} tkrn;
diff --git a/tests/weirdfonts.tex b/tests/weirdfonts.tex
deleted file mode 100644
index 9cbf8ac..0000000
--- a/tests/weirdfonts.tex
+++ /dev/null
@@ -1,15 +0,0 @@
-%% non-standard fonts deserve an extra test file
-\documentclass{scrartcl}
-\usepackage{fontspec}
-%% ····································································
-%% libertine monospace
-%% -------------------
-%% real-world example from: http://tex.stackexchange.com/q/110566
-%% causing database lookups to fail; addressed in luaotfload since
-%% https://github.com/phi-gamma/luaotfload/commit/4d0d2c19ab36d4918a72041a087fbcb451ac8c52
-\setmonofont{Linux Libertine Mono O}
-%% ····································································
-
-\begin{document}
- foo {\ttfamily bar} baz
-\end{document}
diff --git a/tests/zero_width_marks_lig.tex b/tests/zero_width_marks_lig.tex
deleted file mode 100644
index 2c6dba9..0000000
--- a/tests/zero_width_marks_lig.tex
+++ /dev/null
@@ -1,16 +0,0 @@
-\input luaotfload.sty
-
-% https://bugs.freedesktop.org/attachment.cgi?id=72363
-\font\testa=file:TestLig.ttf:script=tibt;+ccmp+abvs+blws+kern at 10pt
-
-\testa གཚོའི་ཁིའུ་ཨཱཿཀ
-
-% good result for the first part:
-% https://bugs.freedesktop.org/attachment.cgi?id=72365
-% for the second part, the two circles shoud appear clearlybefore the last
-% letter, not mixed with it
-
-% see http://lists.freedesktop.org/archives/harfbuzz/2013-April/003101.html
-% for some technical details.
-
-\bye