diff options
-rw-r--r-- | .gitignore | 21 | ||||
-rw-r--r-- | COPYING | 350 | ||||
-rw-r--r-- | Makefile | 244 | ||||
-rw-r--r-- | NEWS | 29 | ||||
-rw-r--r-- | README | 1 | ||||
-rw-r--r-- | doc/Makefile | 58 | ||||
-rw-r--r-- | doc/filegraph.dot (renamed from filegraph.dot) | 12 | ||||
-rw-r--r-- | doc/luaotfload-context.tex | 485 | ||||
-rw-r--r-- | doc/luaotfload-latex.tex | 448 | ||||
-rw-r--r-- | doc/luaotfload-main.tex | 1591 | ||||
-rw-r--r-- | doc/luaotfload-tool.rst (renamed from luaotfload-tool.rst) | 105 | ||||
-rw-r--r-- | doc/luaotfload.conf.example | 30 | ||||
-rw-r--r-- | doc/luaotfload.conf.rst | 347 | ||||
-rw-r--r-- | luaotfload-blacklist.cnf | 12 | ||||
-rw-r--r-- | luaotfload-legacy-attributes.lua | 27 | ||||
-rw-r--r-- | luaotfload-legacy-database.lua | 724 | ||||
-rw-r--r-- | luaotfload-legacy-merged.lua | 8157 | ||||
-rwxr-xr-x | luaotfload-legacy-tool.lua | 105 | ||||
-rw-r--r-- | luaotfload-legacy.lua | 402 | ||||
-rw-r--r-- | luaotfload.dtx | 2685 | ||||
-rw-r--r-- | misc/luaotfload-blacklist.cnf | 4 | ||||
-rw-r--r-- | misc/valgrind-kpse-suppression.sup | 140 | ||||
-rwxr-xr-x | scripts/mkcharacters (renamed from mkcharacters) | 6 | ||||
-rwxr-xr-x | scripts/mkglyphlist (renamed from mkglyphlist) | 13 | ||||
-rwxr-xr-x | scripts/mkstatus (renamed from mkstatus) | 84 | ||||
-rwxr-xr-x | scripts/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.lua | 704 | ||||
-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.lua | 526 | ||||
-rw-r--r-- | src/luaotfload-fonts-lua.lua (renamed from luaotfload-fonts-lua.lua) | 0 | ||||
-rw-r--r-- | src/luaotfload-fonts-otn.lua | 2848 | ||||
-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.lua | 708 | ||||
-rw-r--r-- | src/luaotfload-override.lua | 52 | ||||
-rw-r--r-- | src/luaotfload-parsers.lua | 701 | ||||
-rwxr-xr-x | src/luaotfload-tool.lua (renamed from luaotfload-tool.lua) | 699 | ||||
-rw-r--r-- | src/luaotfload.sty | 45 | ||||
-rw-r--r-- | tests/alternate_sub.tex | 15 | ||||
-rw-r--r-- | tests/anum.tex | 17 | ||||
-rw-r--r-- | tests/caseinsensitive.tex | 6 | ||||
-rw-r--r-- | tests/color.tex | 10 | ||||
-rw-r--r-- | tests/fallback.tex | 6 | ||||
-rw-r--r-- | tests/featurefiles.tex | 5 | ||||
-rw-r--r-- | tests/font_patch.tex | 28 | ||||
-rw-r--r-- | tests/fontconfig_conf_reading.tex | 8 | ||||
-rw-r--r-- | tests/fontencoding.tex | 4 | ||||
-rw-r--r-- | tests/fonts.conf.test | 25 | ||||
-rw-r--r-- | tests/fontspec_lookup.ltx | 41 | ||||
-rw-r--r-- | tests/frac.tex | 4 | ||||
-rw-r--r-- | tests/fullname.tex | 15 | ||||
-rw-r--r-- | tests/itlc.tex | 5 | ||||
-rw-r--r-- | tests/lookups.tex | 20 | ||||
-rw-r--r-- | tests/marks.tex | 10 | ||||
-rw-r--r-- | tests/math.tex | 56 | ||||
-rw-r--r-- | tests/microtypography.tex | 36 | ||||
-rw-r--r-- | tests/opbd.fea | 187 | ||||
-rw-r--r-- | tests/opbd.tex | 35 | ||||
-rw-r--r-- | tests/opticalsize.tex | 13 | ||||
-rw-r--r-- | tests/pln-aux-1.tex | 55 | ||||
-rw-r--r-- | tests/pln-aux-2.tex | 102 | ||||
-rw-r--r-- | tests/pln-aux-3.tex | 39 | ||||
-rw-r--r-- | tests/pln-aux-4.tex | 41 | ||||
-rw-r--r-- | tests/pln-request-4-slashed.tex | 12 | ||||
-rw-r--r-- | tests/pln-request-5-cached.tex | 18 | ||||
-rw-r--r-- | tests/pln-subfont-1.tex | 12 | ||||
-rw-r--r-- | tests/pln-tfm.tex | 10 | ||||
-rw-r--r-- | tests/sanitize_color.tex | 8 | ||||
-rw-r--r-- | tests/systemfonts.tex | 50 | ||||
-rw-r--r-- | tests/texligatures.tex | 7 | ||||
-rw-r--r-- | tests/tfmofm.ltx | 6 | ||||
-rw-r--r-- | tests/tkrn.fea | 8 | ||||
-rw-r--r-- | tests/weirdfonts.tex | 15 | ||||
-rw-r--r-- | tests/zero_width_marks_lig.tex | 16 |
87 files changed, 11814 insertions, 15348 deletions
@@ -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 + @@ -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. @@ -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 @@ -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) @@ -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("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"" 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 |