summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/font-otf.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/font-otf.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/font-otf.lua5599
1 files changed, 1197 insertions, 4402 deletions
diff --git a/Master/texmf-dist/tex/context/base/font-otf.lua b/Master/texmf-dist/tex/context/base/font-otf.lua
index 5c0db392dba..e8867c53d37 100644
--- a/Master/texmf-dist/tex/context/base/font-otf.lua
+++ b/Master/texmf-dist/tex/context/base/font-otf.lua
@@ -1,4 +1,4 @@
- if not modules then modules = { } end modules ['font-otf'] = {
+if not modules then modules = { } end modules ['font-otf'] = {
version = 1.001,
comment = "companion to font-ini.tex",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
@@ -6,1170 +6,679 @@
license = "see context related readme files"
}
--- once we have all features working, i will redo this module .. caching lookups and such
+local utf = unicode.utf8
--- the flattening code is a prelude to a more compact table format (so, we're now
--- at the fourth version); maybe we will go unicode, although that will mean that we
--- miss some glyphs (unicode -1)
+local concat, getn, utfbyte = table.concat, table.getn, utf.byte
+local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip
+local type, next, tonumber, tostring = type, next, tonumber, tostring
--- todo: featuredata is now indexed by kind,lookup but probably lookup is okay too
+local trace_private = false trackers.register("otf.private", function(v) trace_private = v end)
+local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end)
+local trace_features = false trackers.register("otf.features", function(v) trace_features = v end)
+local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end)
+local trace_sequences = false trackers.register("otf.sequences", function(v) trace_sequences = v end)
+local trace_math = false trackers.register("otf.math", function(v) trace_math = v end)
+local trace_unimapping = false trackers.register("otf.unimapping", function(v) trace_unimapping = v end)
--- todo: now that we pack ... resolve strings to unicode points
--- todo: unpack already in tmc file, i.e. save tables and return ref''d version
--- todo: dependents etc resolve too, maybe even reorder glyphs to unicode
--- todo: pack ignoreflags
+--~ trackers.enable("otf.loading")
--- abvf abvs blwf blws dist falt half halt jalt lfbd ljmo
--- mset opbd palt pwid qwid rand rtbd rtla ruby size tjmo twid valt vatu vert
--- vhal vjmo vkna vkrn vpal vrt2
-
--- The specification of OpenType is vague, very vague. Apart from a lack of proper
--- specifications (a free one) there's also the problem that Microsoft and Adobe
--- may have their own rules. Anyhow, the following is from Adobe's feature file
--- specification:
---
--- http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html#6.h
---
--- The following is a reference summary of the algorithm used by an OpenType layout
--- (OTL) engine to perform substitutions and positionings. The important aspect of
--- this for a feature file editor is that each lookup corresponds to one "pass" over
--- the glyph run (see step 4 below). Thus, each lookup has as input the accumulated
--- result of all previous lookups in the LookupList (whether in the same feature or
--- in other features).
--- 1. All glyphs in the client's glyph run must belong to the same language
--- system. Glyph sequence matching may not occur across language
--- systems. Do the following first for the GSUB and then for the GPOS:
--- 2. Assemble all features (including any required feature) for the glyph
--- run's language system.
--- 3. Assemble all lookups in these features, in LookupList order, removing
--- any duplicates. All features and thus all lookups needn't be applied to
--- every glyph in the run.
--- 4. For each lookup:
--- 5. For each glyph in the glyph run:
--- 6. If the lookup is applied to that glyph and the lookupflag doesn't
--- indicate that that glyph is to be ignored:
--- 7. For each subtable in the lookup:
--- 8. If the subtable's target context is matched:
--- 9. Do the glyph substitution or positioning,
--- --- OR: ---
--- If this is a (chain) contextual lookup do the following
--- [(10)-(11)] in the subtable's Subst/PosLookupRecord order:
--- 10. For each (sequenceIndex, lookupListIndex) pair:
--- 11. Apply lookup[lookupListIndex] at input sequence[sequenceIndex]
--- [steps (7)-(11)]
--- 12. Goto the glyph after the input sequence matched in (8)
--- (i.e. skip any remaining subtables in the lookup).
--- The "target context" in step 8 above comprises the input sequence and any
--- backtrack and lookahead sequences.
--- The input sequence must be matched entirely within the lookup's "application
--- range" at that glyph (that contiguous subrun of glyphs including and around
--- the current glyph on which the lookup is applied). There is no such restriction
--- on the backtrack and lookahead sequences.
--- "Matching" includes matching any glyphs designated to be skipped in the
--- lookup's LookupFlag.
+local zwnj = 0x200C
+local zwj = 0x200D
--[[ldx--
-<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>
+<p>The fontforge table has organized lookups in a certain way. A first implementation
+of this code was organized featurewise: information related to features was
+collected and processing boiled down to a run over the features. The current
+implementation honors the order in the main feature table. Since we can reorder this
+table as we want, we can eventually support several models of processing. We kept
+the static as well as dynamic feature processing, because it had proved to be
+rather useful. The formerly three loop variants have beem discarded but will
+reapear at some time.</p>
+
+<itemize>
+<item>we loop over all lookups</item>
+<item>for each lookup we do a run over the list of glyphs</item>
+<item>but we only process them for features that are enabled</item>
+<item>if we're dealing with a contextual lookup, we loop over all contexts</item>
+<item>in that loop we quit at a match and then process the list of sublookups</item>
+<item>we always continue after the match</item>
+</itemize>
+
+<p>In <l n='context'/> we do this for each font that is used in a list, so in
+practice we have quite some nested loops.</p>
+
+<p>We process the whole list and then consult the glyph nodes. An alternative approach
+is to collect strings of characters using the same font including spaces (because some
+lookups involve spaces). However, we then need to reconstruct the list which is no fun.
+Also, we need to carry quite some information, like attributes, so eventually we don't
+gain much (if we gain something at all).</p>
+
+<p>Another consideration has been to operate on sublists (subhead, subtail) but again
+this would complicate matters as we then neext to keep track of a changing subhead
+and subtail. On the other hand, this might save some runtime. The number of changes
+involved is not that large. This only makes sense when we have many fonts in a list
+and don't change to frequently.</p>
--ldx]]--
---~ The node based processing functions look quite complex which is mainly due to
---~ the fact that we need to share data and cache resolved issues (saves much memory and
---~ is also faster). A further complication is that we support static as well as dynamic
---~ features.
+fonts = fonts or { }
+fonts.otf = fonts.otf or { }
+fonts.tfm = fonts.tfm or { }
-fonts = fonts or { }
-fonts.otf = fonts.otf or { }
-fonts.otf.version = 2.09
-fonts.otf.pack = true
-fonts.otf.tables = fonts.otf.tables or { }
-fonts.otf.meanings = fonts.otf.meanings or { }
-fonts.otf.enhance_data = false
-fonts.otf.syncspace = true
-fonts.otf.features = { }
-fonts.otf.features.aux = { }
-fonts.otf.features.data = { }
-fonts.otf.features.list = { } -- not (yet) used, oft fonts have gpos/gsub lists
-fonts.otf.features.default = { }
-fonts.otf.trace_features = false
-fonts.otf.trace_set_features = false
-fonts.otf.trace_replacements = false
-fonts.otf.trace_contexts = false
-fonts.otf.trace_anchors = false
-fonts.otf.trace_ligatures = false
-fonts.otf.trace_kerns = false
-fonts.otf.trace_cursive = false
-fonts.otf.notdef = false
-fonts.otf.cache = containers.define("fonts", "otf", fonts.otf.version, true)
+local otf = fonts.otf
+local tfm = fonts.tfm
-function fonts.otf.trace_process()
- fonts.otf.trace_replacements = true
- fonts.otf.trace_contexts = true
- fonts.otf.trace_anchors = true
- fonts.otf.trace_ligatures = true
- fonts.otf.trace_kerns = true
- fonts.otf.trace_cursive = true
-end
+local fontdata = fonts.ids
---[[ldx--
-<p>We start with a lot of tables and related functions.</p>
---ldx]]--
+otf.tables = otf.tables or { } -- defined in font-ott.lua
+otf.meanings = otf.meanings or { } -- defined in font-ott.lua
+otf.tables.features = otf.tables.features or { } -- defined in font-ott.lua
+otf.tables.languages = otf.tables.languages or { } -- defined in font-ott.lua
+otf.tables.scripts = otf.tables.scripts or { } -- defined in font-ott.lua
-fonts.otf.tables.scripts = {
- ['dflt'] = 'Default',
+otf.features = otf.features or { }
+otf.features.list = otf.features.list or { }
+otf.features.default = otf.features.default or { }
- ['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.enhancers = otf.enhancers or { }
+otf.glists = { "gsub", "gpos" }
-fonts.otf.tables.languages = {
- ['dflt'] = 'Default',
+otf.version = 2.628 -- beware: also sync font-mis.lua
+otf.pack = true -- beware: also sync font-mis.lua
+otf.syncspace = true
+otf.notdef = false
+otf.cache = containers.define("fonts", "otf", otf.version, true)
+otf.cleanup_aat = false -- only context
- ['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'
-}
+--[[ldx--
+<p>We start with a lot of tables and related functions.</p>
+--ldx]]--
-fonts.otf.tables.features = {
- ['aalt'] = 'Access All Alternates',
- ['abvf'] = 'Above-Base Forms',
- ['abvm'] = 'Above-Base Mark Positioning',
- ['abvs'] = 'Above-Base Substitutions',
- ['afrc'] = 'Alternative Fractions',
- ['akhn'] = 'Akhands',
- ['blwf'] = 'Below-Base Forms',
- ['blwm'] = 'Below-Base Mark Positioning',
- ['blws'] = 'Below-Base Substitutions',
- ['c2pc'] = 'Petite Capitals From Capitals',
- ['c2sc'] = 'Small Capitals From Capitals',
- ['calt'] = 'Contextual Alternates',
- ['case'] = 'Case-Sensitive Forms',
- ['ccmp'] = 'Glyph Composition/Decomposition',
- ['cjct'] = 'Conjunct Forms',
- ['clig'] = 'Contextual Ligatures',
- ['cpsp'] = 'Capital Spacing',
- ['cswh'] = 'Contextual Swash',
- ['curs'] = 'Cursive Positioning',
- ['dflt'] = 'Default Processing',
- ['dist'] = 'Distances',
- ['dlig'] = 'Discretionary Ligatures',
- ['dnom'] = 'Denominators',
- ['expt'] = 'Expert Forms',
- ['falt'] = 'Final glyph Alternates',
- ['fin2'] = 'Terminal Forms #2',
- ['fin3'] = 'Terminal Forms #3',
- ['fina'] = 'Terminal Forms',
- ['frac'] = 'Fractions',
- ['fwid'] = 'Full Width',
- ['half'] = 'Half Forms',
- ['haln'] = 'Halant Forms',
- ['halt'] = 'Alternate Half Width',
- ['hist'] = 'Historical Forms',
- ['hkna'] = 'Horizontal Kana Alternates',
- ['hlig'] = 'Historical Ligatures',
- ['hngl'] = 'Hangul',
- ['hojo'] = 'Hojo Kanji Forms',
- ['hwid'] = 'Half Width',
- ['init'] = 'Initial Forms',
- ['isol'] = 'Isolated Forms',
- ['ital'] = 'Italics',
- ['jalt'] = 'Justification Alternatives',
- ['jp04'] = 'JIS2004 Forms',
- ['jp78'] = 'JIS78 Forms',
- ['jp83'] = 'JIS83 Forms',
- ['jp90'] = 'JIS90 Forms',
- ['kern'] = 'Kerning',
- ['lfbd'] = 'Left Bounds',
- ['liga'] = 'Standard Ligatures',
- ['ljmo'] = 'Leading Jamo Forms',
- ['lnum'] = 'Lining Figures',
- ['locl'] = 'Localized Forms',
- ['mark'] = 'Mark Positioning',
- ['med2'] = 'Medial Forms #2',
- ['medi'] = 'Medial Forms',
- ['mgrk'] = 'Mathematical Greek',
- ['mkmk'] = 'Mark to Mark Positioning',
- ['mset'] = 'Mark Positioning via Substitution',
- ['nalt'] = 'Alternate Annotation Forms',
- ['nlck'] = 'NLC Kanji Forms',
- ['nukt'] = 'Nukta Forms',
- ['numr'] = 'Numerators',
- ['onum'] = 'Old Style Figures',
- ['opbd'] = 'Optical Bounds',
- ['ordn'] = 'Ordinals',
- ['ornm'] = 'Ornaments',
- ['palt'] = 'Proportional Alternate Width',
- ['pcap'] = 'Petite Capitals',
- ['pnum'] = 'Proportional Figures',
- ['pref'] = 'Pre-base Forms',
- ['pres'] = 'Pre-base Substitutions',
- ['pstf'] = 'Post-base Forms',
- ['psts'] = 'Post-base Substitutions',
- ['pwid'] = 'Proportional Widths',
- ['qwid'] = 'Quarter Widths',
- ['rand'] = 'Randomize',
- ['rkrf'] = 'Rakar Forms',
- ['rlig'] = 'Required Ligatures',
- ['rphf'] = 'Reph Form',
- ['rtbd'] = 'Right Bounds',
- ['rtla'] = 'Right-To-Left Alternates',
- ['ruby'] = 'Ruby Notation Forms',
- ['salt'] = 'Stylistic Alternates',
- ['sinf'] = 'Scientific Inferiors',
- ['size'] = 'Optical Size',
- ['smcp'] = 'Small Capitals',
- ['smpl'] = 'Simplified Forms',
- ['ss01'] = 'Stylistic Set 1',
- ['ss02'] = 'Stylistic Set 2',
- ['ss03'] = 'Stylistic Set 3',
- ['ss04'] = 'Stylistic Set 4',
- ['ss05'] = 'Stylistic Set 5',
- ['ss06'] = 'Stylistic Set 6',
- ['ss07'] = 'Stylistic Set 7',
- ['ss08'] = 'Stylistic Set 8',
- ['ss09'] = 'Stylistic Set 9',
- ['ss10'] = 'Stylistic Set 10',
- ['ss11'] = 'Stylistic Set 11',
- ['ss12'] = 'Stylistic Set 12',
- ['ss13'] = 'Stylistic Set 13',
- ['ss14'] = 'Stylistic Set 14',
- ['ss15'] = 'Stylistic Set 15',
- ['ss16'] = 'Stylistic Set 16',
- ['ss17'] = 'Stylistic Set 17',
- ['ss18'] = 'Stylistic Set 18',
- ['ss19'] = 'Stylistic Set 19',
- ['ss20'] = 'Stylistic Set 20',
- ['subs'] = 'Subscript',
- ['sups'] = 'Superscript',
- ['swsh'] = 'Swash',
- ['titl'] = 'Titling',
- ['tjmo'] = 'Trailing Jamo Forms',
- ['tnam'] = 'Traditional Name Forms',
- ['tnum'] = 'Tabular Figures',
- ['trad'] = 'Traditional Forms',
- ['twid'] = 'Third Widths',
- ['unic'] = 'Unicase',
- ['valt'] = 'Alternate Vertical Metrics',
- ['vatu'] = 'Vattu Variants',
- ['vert'] = 'Vertical Writing',
- ['vhal'] = 'Alternate Vertical Half Metrics',
- ['vjmo'] = 'Vowel Jamo Forms',
- ['vkna'] = 'Vertical Kana Alternates',
- ['vkrn'] = 'Vertical Kerning',
- ['vpal'] = 'Proportional Alternate Vertical Metrics',
- ['vrt2'] = 'Vertical Rotation',
- ['zero'] = 'Slashed Zero'
+otf.tables.global_fields = table.tohash {
+ "lookups",
+ "glyphs",
+ "subfonts",
+ "luatex",
+ "pfminfo",
+ "cidinfo",
+ "tables",
+ "names",
+ "unicodes",
+ "names",
+ "anchor_classes",
+ "kern_classes",
+ "gpos",
+ "gsub"
}
-fonts.otf.tables.baselines = {
- ['hang'] = 'Hanging baseline',
- ['icfb'] = 'Ideographic character face bottom edge baseline',
- ['icft'] = 'Ideographic character face tope edige baseline',
- ['ideo'] = 'Ideographic em-box bottom edge baseline',
- ['idtp'] = 'Ideographic em-box top edge baseline',
- ['math'] = 'Mathmatical centered baseline',
- ['romn'] = 'Roman baseline'
+otf.tables.valid_fields = {
+ "anchor_classes",
+ "ascent",
+ "cache_version",
+ "cidinfo",
+ "copyright",
+ "creationtime",
+ "descent",
+ "design_range_bottom",
+ "design_range_top",
+ "design_size",
+ "encodingchanged",
+ "extrema_bound",
+ "familyname",
+ "fontname",
+ "fontstyle_id",
+ "fontstyle_name",
+ "fullname",
+ "glyphs",
+ "hasvmetrics",
+ "head_optimized_for_cleartype",
+ "horiz_base",
+ "issans",
+ "isserif",
+ "italicangle",
+ "kerns",
+ "lookups",
+ -- "luatex",
+ "macstyle",
+ "modificationtime",
+ "onlybitmaps",
+ "origname",
+ "os2_version",
+ "pfminfo",
+ "private",
+ "serifcheck",
+ "sfd_version",
+ -- "size",
+ "strokedfont",
+ "strokewidth",
+ "subfonts",
+ "table_version",
+ -- "tables",
+ -- "ttf_tab_saved",
+ "ttf_tables",
+ "uni_interp",
+ "uniqueid",
+ "units_per_em",
+ "upos",
+ "use_typo_metrics",
+ "uwidth",
+ "validation_state",
+ "verbose",
+ "version",
+ "vert_base",
+ "weight",
+ "weight_width_slope_only",
+ "xuid",
}
-function fonts.otf.tables.to_tag(id)
- return stringformat("%4s",id:lower())
-end
+--[[ldx--
+<p>Here we go.</p>
+--ldx]]--
-function fonts.otf.meanings.resolve(tab,id)
- if tab and id then
- id = id:lower()
- return tab[id] or tab[id:gsub(" ","")] or tab['dflt'] or ''
- else
- return "unknown"
+local function load_featurefile(ff,featurefile)
+ if featurefile then
+ featurefile = resolvers.find_file(file.addsuffix(featurefile,'fea')) -- "FONTFEATURES"
+ if featurefile and featurefile ~= "" then
+ if trace_loading then
+ logs.report("load otf", "featurefile: %s", featurefile)
+ end
+ fontloader.apply_featurefile(ff, featurefile)
+ end
end
end
-function fonts.otf.meanings.script(id)
- return fonts.otf.meanings.resolve(fonts.otf.tables.scripts,id)
-end
-function fonts.otf.meanings.language(id)
- return fonts.otf.meanings.resolve(fonts.otf.tables.languages,id)
-end
-function fonts.otf.meanings.feature(id)
- return fonts.otf.meanings.resolve(fonts.otf.tables.features,id)
-end
-function fonts.otf.meanings.baseline(id)
- return fonts.otf.meanings.resolve(fonts.otf.tables.baselines,id)
-end
-
-fonts.otf.tables.to_scripts = table.reverse_hash(fonts.otf.tables.scripts )
-fonts.otf.tables.to_languages = table.reverse_hash(fonts.otf.tables.languages)
-fonts.otf.tables.to_features = table.reverse_hash(fonts.otf.tables.features )
-
-do
-
- local scripts = fonts.otf.tables.scripts
- local languages = fonts.otf.tables.languages
- local features = fonts.otf.tables.features
-
- local to_scripts = fonts.otf.tables.to_scripts
- local to_languages = fonts.otf.tables.to_languages
- local to_features = fonts.otf.tables.to_features
-
- function fonts.otf.meanings.normalize(features)
- local h = { }
- for k,v in pairs(features) do
- k = k:lower() -- :gsub("[^a-z0-9%-%.]" -- not needed
- if k == "language" or k == "lang" then
- v = (v:lower()):gsub("[^a-z0-9%-]","")
- k = language
- if not languages[v] then
- h.language = to_languages[v] or "dflt"
- else
- h.language = v
- end
- elseif k == "script" then
- v = (v:lower()):gsub("[^a-z0-9%-]","")
- if not scripts[v] then
- h.script = to_scripts[v] or "dflt"
- else
- h.script = v
- end
- else
- if type(v) == "string" then
- local b = v:is_boolean()
- if type(b) == "nil" then
- v = tonumber(v) or v:lower() -- gsub("[^a-z0-9%-]") -- too dangerous, e.g. featurefiles
- else
- v = b
- end
- end
- h[to_features[k] or k] = v
- end
+function otf.enhance(name,data,filename,verbose)
+ local enhancer = otf.enhancers[name]
+ if enhancer then
+ if (verbose ~= nil and verbose) or trace_loading then
+ logs.report("load otf","enhance: %s",name)
end
- return h
+ enhancer(data,filename)
end
-
end
---[[ldx--
-<p>Here we go.</p>
---ldx]]--
-
-fonts.otf.enhance = fonts.otf.enhance or { }
-fonts.otf.enhance.add_kerns = true
-
-fonts.otf.featurefiles = {
---~ "texhistoric.fea"
+local enhancers = {
+ -- pack and unpack are handled separately; they might even be moved
+ -- away from the enhancers namespace
+ "patch bugs",
+ "merge cid fonts", "prepare unicode", "cleanup ttf tables", "compact glyphs", "reverse coverage",
+ "cleanup aat", "enrich with features", "add some missing characters",
+ "reorganize kerns", -- moved here
+ "flatten glyph lookups", "flatten anchor tables", "flatten feature tables",
+ "prepare luatex tables",
+ "analyse features", "rehash features",
+ "analyse anchors", "analyse marks", "analyse unicodes", "analyse subtables",
+ "check italic correction","check math",
+ "share widths",
+ "strip not needed data",
+ "migrate metadata",
}
-function fonts.otf.load(filename,format,sub,featurefile)
+function otf.load(filename,format,sub,featurefile)
local name = file.basename(file.removesuffix(filename))
if featurefile then
name = name .. "@" .. file.removesuffix(file.basename(featurefile))
end
if sub == "" then sub = false end
local hash = name
- if sub then -- name cleanup will move to cache code
+ if sub then
hash = hash .. "-" .. sub
- hash = hash:lower()
- hash = hash:gsub("[^%w%d]+","-")
end
- local data = containers.read(fonts.otf.cache(), hash)
+ hash = containers.cleanname(hash)
+ local data = containers.read(otf.cache(), hash)
local size = lfs.attributes(filename,"size") or 0
- if data and data.size ~= size then
- data = nil
- end
- if not data then
- logs.report("load otf","loading: " .. filename)
+ if not data or data.verbose ~= fonts.verbose or data.size ~= size then
+ logs.report("load otf","loading: %s",filename)
local ff, messages
if sub then
- ff, messages = fontforge.open(filename,sub)
+ ff, messages = fontloader.open(filename,sub)
else
- ff, messages = fontforge.open(filename)
+ ff, messages = fontloader.open(filename)
end
- if messages and #messages > 0 then
- for _, m in ipairs(messages) do
- logs.report("load otf","warning: " .. m)
+ if trace_loading and messages and #messages > 0 then
+ for m=1,#messages do
+ logs.report("load otf","warning: %s",messages[m])
end
end
if ff then
- local function load_featurefile(featurefile)
- if featurefile then
- featurefile = input.find_file(texmf.instance,file.addsuffix(featurefile,'fea')) -- "FONTFEATURES"
- if featurefile and featurefile ~= "" then
- logs.report("load otf", "featurefile: " .. featurefile)
- fontforge.apply_featurefile(ff, featurefile)
- end
- end
- end
- -- for _, featurefile in pairs(fonts.otf.featurefiles) do
- -- load_featurefile(featurefile)
- -- end
- load_featurefile(featurefile)
- data = fontforge.to_table(ff)
- fontforge.close(ff)
+ load_featurefile(ff,featurefile)
+ data = fontloader.to_table(ff)
+ fontloader.close(ff)
if data then
- logs.report("load otf","enhance: before")
- fonts.otf.enhance.before(data,filename)
- logs.report("load otf","enhance: enrich")
- fonts.otf.enhance.enrich(data,filename)
- logs.report("load otf","enhance: flatten")
- fonts.otf.enhance.flatten(data,filename)
- logs.report("load otf","enhance: analyze")
- fonts.otf.enhance.analyze(data,filename)
- logs.report("load otf","enhance: after")
- fonts.otf.enhance.after(data,filename)
- logs.report("load otf","enhance: patch")
- fonts.otf.enhance.patch(data,filename)
- logs.report("load otf","enhance: strip")
- fonts.otf.enhance.strip(data,filename)
- if fonts.otf.pack then
- logs.report("load otf","enhance: pack")
- fonts.otf.enhance.pack(data)
+ logs.report("load otf","file size: %s", size)
+ logs.report("load otf","enhancing ...")
+ for e=1,#enhancers do
+ otf.enhance(enhancers[e],data,filename)
+ end
+ if otf.pack and not fonts.verbose then
+ otf.enhance("pack",data,filename)
end
- logs.report("load otf","file size: " .. size)
data.size = size
- logs.report("load otf","saving: in cache")
- data = containers.write(fonts.otf.cache(), hash, data)
+ data.verbose = fonts.verbose
+ logs.report("load otf","saving in cache: %s",filename)
+ data = containers.write(otf.cache(), hash, data)
+ collectgarbage("collect")
+ data = containers.read(otf.cache(), hash) -- this frees the old table and load the sparse one
+ collectgarbage("collect")
else
- logs.error("load otf","loading failed (table conversion error)")
+ logs.report("load otf","loading failed (table conversion error)")
end
else
- logs.error("load otf","loading failed (file read error)")
+ logs.report("load otf","loading failed (file read error)")
+ end
+ end
+ if data then
+ otf.enhance("unpack",data,filename,false) -- no message here
+ otf.add_dimensions(data)
+ if trace_sequences then
+ otf.show_feature_order(data,filename)
end
end
- fonts.otf.enhance.unpack(data)
return data
end
--- memory saver ..
-
-function fonts.otf.enhance.pack(data)
+function otf.add_dimensions(data)
+ -- todo: forget about the width if it's the defaultwidth (saves mem)
+ -- we could also build the marks hash here (instead of storing it)
if data then
- local h, t = { }, { }
- local concat = table.concat
- local function tabstr(t)
- for i=1,#t do
- if type(t[i]) == "boolean" then
- local s = tostring(t[1])
- for i=2,#t do
- s = s .. ",".. tostring(t[i])
- end
- return s
- end
- end
- return concat(t,",")
- end
- local function pack(v)
- local tag = tabstr(v,",")
- if not h[tag] then
- t[#t+1] = v
- h[tag] = #t
+ 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
- return h[tag]
- end
- for k, v in pairs(data.glyphs) do
- v.boundingbox = pack(v.boundingbox)
- if v.lookups then
- for k,v in pairs(v.lookups) do
- for kk, vv in ipairs(v) do
- v[kk] = pack(vv)
- end
- end
+ if force and not d.name then
+ d.name = ".notdef"
end
- end
- if data.lookups then
- for k, v in pairs(data.lookups) do
- if v.rules then
- for kk, vv in pairs(v.rules) do
- local l = vv.lookups
- if l then
- vv.lookups = pack(l)
- end
- local c = vv.coverage
- if c then
- c.before = c.before and pack(c.before )
- c.after = c.after and pack(c.after )
- c.current = c.current and pack(c.current)
- end
- end
+ if bb then
+ local ht, dp = bb[4], -bb[2]
+ if ht == 0 or ht < 0 then
+ -- no need to set it and no negative heights, nil == 0
+ else
+ d.height = ht
end
- end
- end
- if data.luatex then
- local li = data.luatex.ignore_flags
- if li then
- for k, v in pairs(li) do
- li[k] = pack(v)
+ if dp == 0 or dp < 0 then
+ -- no negative depths and no negative depths, nil == 0
+ else
+ d.depth = dp
end
end
end
- if #t > 0 then
- data.tables = t
- end
end
end
-function fonts.otf.enhance.unpack(data)
- if data then
- local t = data.tables
- if t then
- for k, v in pairs(data.glyphs) do
- v.boundingbox = t[v.boundingbox]
- local l = v.lookups
- if l then
- for k,v in pairs(l) do
- for i=1,#v do
- v[i] = t[v[i]]
- end
- end
- end
+function otf.show_feature_order(otfdata,filename)
+ local sequences = otfdata.luatex.sequences
+ if sequences and #sequences > 0 then
+ if trace_loading then
+ logs.report("otf check","font %s has %s sequences",filename,#sequences)
+ logs.report("otf check"," ")
+ end
+ for nos=1,#sequences do
+ local sequence = sequences[nos]
+ local typ = sequence.type or "no-type"
+ local name = sequence.name or "no-name"
+ local subtables = sequence.subtables or { "no-subtables" }
+ local features = sequence.features
+ if trace_loading then
+ logs.report("otf check","%3i %-15s %-20s [%s]",nos,name,typ,concat(subtables,","))
end
- if data.lookups then
- for k, v in pairs(data.lookups) do
- local r = v.rules
- if r then
- for kk, vv in pairs(r) do
- local l = vv.lookups
- if l then
- vv.lookups = t[l]
- end
- local c = vv.coverage
- if c then
- local cc = c.before if cc then c.before = t[cc] end
- cc = c.after if cc then c.after = t[cc] end
- cc = c.current if cc then c.current = t[cc] end
- end
+ if features then
+ for feature, scripts in next, features do
+ local tt = { }
+ for script, languages in next, scripts do
+ local ttt = { }
+ for language, _ in next, languages do
+ ttt[#ttt+1] = language
end
+ tt[#tt+1] = format("[%s: %s]",script,concat(ttt," "))
end
- end
- end
- if data.luatex then
- local li = data.luatex.ignore_flags
- if li then
- for k, v in pairs(li) do
- li[k] = t[v]
+ if trace_loading then
+ logs.report("otf check"," %s: %s",feature,concat(tt," "))
end
end
end
- data.tables = nil
end
+ if trace_loading then
+ logs.report("otf check","\n")
+ end
+ elseif trace_loading then
+ logs.report("otf check","font %s has no sequences",filename)
end
end
-- todo: normalize, design_size => designsize
-function fonts.otf.enhance.analyze(data,filename)
- local t = {
- filename = file.basename(filename),
- version = fonts.otf.version,
- creator = "context mkiv",
- unicodes = fonts.otf.analyze_unicodes(data),
- gposfeatures = fonts.otf.analyze_features(data.gpos),
- gsubfeatures = fonts.otf.analyze_features(data.gsub),
- marks = fonts.otf.analyze_class(data,'mark'),
- }
- t.subtables, t.name_to_type, t.internals, t.always_valid, t.ignore_flags, t.ctx_always = fonts.otf.analyze_subtables(data)
- data.luatex = t
+otf.enhancers["prepare luatex tables"] = function(data,filename)
+ data.luatex = data.luatex or { }
+ local luatex = data.luatex
+ luatex.filename = filename
+ luatex.version = otf.version
+ luatex.creator = "context mkiv"
end
-do
- -- original string parser: 0.109, lpeg parser: 0.036 seconds for Adobe-CNS1-4.cidmap
- --
- -- 18964 18964 (leader)
- -- 0 /.notdef
- -- 1..95 0020
- -- 99 3000
-
- local number = lpeg.C(lpeg.R("09","af","AF")^1)
- local space = lpeg.S(" \n\r\t")
- local spaces = space^0
- local period = lpeg.P(".")
- local periods = period * period
- local name = lpeg.P("/") * lpeg.C((1-space)^1)
-
- local unicodes, names = { }, {}
-
- local tonumber = tonumber
-
- local function do_one(a,b)
- unicodes[tonumber(a)] = tonumber(b,16)
+otf.enhancers["cleanup aat"] = function(data,filename)
+ if otf.cleanup_aat then
end
- local function do_range(a,b,c)
- c = tonumber(c,16)
- for i=tonumber(a),tonumber(b) do
- unicodes[i] = c
- c = c + 1
+end
+
+local function analyze_features(g, features)
+ if g then
+ local t, done = { }, { }
+ for k=1,#g do
+ local f = features or g[k].features
+ if f then
+ for k=1,#f do
+ -- scripts and tag
+ local tag = f[k].tag
+ if not done[tag] then
+ t[#t+1] = tag
+ done[tag] = true
+ end
+ end
+ end
+ end
+ if #t > 0 then
+ return t
end
end
- local function do_name(a,b)
- names[tonumber(a)] = b
- end
+ return nil
+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
- }
+otf.enhancers["analyse features"] = function(data,filename)
+ -- local luatex = data.luatex
+ -- luatex.gposfeatures = analyze_features(data.gpos)
+ -- luatex.gsubfeatures = analyze_features(data.gsub)
+end
- function fonts.otf.load_cidmap(filename) -- lpeg
- local data = io.loaddata(filename)
- if data then
- unicodes, names = { }, { }
- grammar:match(data)
- local supplement, registry, ordering = filename:match("^(.-)%-(.-)%-()%.(.-)$")
- return {
- supplement = supplement,
- registry = registry,
- ordering = ordering,
- filename = filename,
- unicodes = unicodes,
- names = names
- }
- else
- return nil
+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
-fonts.otf.cidmaps = { }
-fonts.otf.cidmax = 10
+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
-function fonts.otf.cidmap(registry,ordering,supplement)
- -- cf Arthur R. we can safely scan upwards since cids are downward compatible
- local template = "%s-%s-%s.cidmap"
- local supplement = tonumber(supplement)
- logs.report("load otf",string.format("needed cidmap, registry: %s, ordering: %s, supplement: %s",registry,ordering,supplement))
- local function locate(registry,ordering,supplement)
- local filename = string.format(template,registry,ordering,supplement)
- local cidmap = fonts.otf.cidmaps[filename]
- if not cidmap then
- logs.report("load otf",string.format("checking cidmap, registry: %s, ordering: %s, supplement: %s, filename: %s",registry,ordering,supplement,filename))
- local fullname = input.find_file(texmf.instance,filename,'cid') or ""
- if fullname ~= "" then
- cidmap = fonts.otf.load_cidmap(fullname)
- if cidmap then
- logs.report("load otf",string.format("using cidmap file %s",filename))
- fonts.otf.cidmaps[filename] = cidmap
- return cidmap
+local separator = lpeg.S("_.")
+local other = lpeg.C((1 - separator)^1)
+local ligsplitter = lpeg.Ct(other * (separator * other)^0)
+
+--~ print(table.serialize(ligsplitter:match("this")))
+--~ print(table.serialize(ligsplitter:match("this.that")))
+--~ print(table.serialize(ligsplitter:match("japan1.123")))
+--~ print(table.serialize(ligsplitter:match("such_so_more")))
+--~ print(table.serialize(ligsplitter:match("such_so_more.that")))
+
+otf.enhancers["analyse unicodes"] = function(data,filename)
+ local tounicode16, tounicode16sequence = fonts.map.tounicode16, fonts.map.tounicode16sequence
+ local unicodes = data.luatex.unicodes
+ -- we need to move this code
+ unicodes['space'] = unicodes['space'] or 32 -- handly later on
+ unicodes['hyphen'] = unicodes['hyphen'] or 45 -- handly later on
+ unicodes['zwj'] = unicodes['zwj'] or zwj -- handly later on
+ unicodes['zwnj'] = unicodes['zwnj'] or zwnj -- handly later on
+ -- the tounicode mapping is sparse and only needed for alternatives
+ local tounicode, originals, ns, nl, private, unknown = { }, { }, 0, 0, fonts.private, format("%04X",utfbyte("?"))
+ data.luatex.tounicode, data.luatex.originals = tounicode, originals
+ local lumunic, uparser, oparser
+ if false then -- will become an option
+ lumunic = fonts.map.load_lum_table(filename)
+ lumunic = lumunic and lumunic.tounicode
+ end
+ local cidinfo, cidnames, cidcodes = data.cidinfo
+ local usedmap = cidinfo and cidinfo.usedname
+ usedmap = usedmap and fonts.cid.map[usedmap]
+ if usedmap then
+ oparser = usedmap and fonts.map.make_name_parser(cidinfo.ordering)
+ cidnames = usedmap.names
+ cidcodes = usedmap.unicodes
+ end
+ uparser = fonts.map.make_name_parser()
+ for index, glyph in next, data.glyphs do
+ local name, unic = glyph.name, glyph.unicode or -1 -- play safe
+ if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then
+ local unicode = lumunic and lumunic[name]
+ if unicode then
+ originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1
+ end
+ -- cidmap heuristics, beware, there is no guarantee for a match unless
+ -- the chain resolves
+ if not unicode and usedmap then
+ local foundindex = oparser:match(name)
+ if foundindex then
+ unicode = cidcodes[foundindex] -- name to number
+ if not unicode then
+ local reference = cidnames[foundindex] -- number to name
+ if reference then
+ local foundindex = oparser:match(reference)
+ if foundindex then
+ unicode = cidcodes[foundindex]
+ if unicode then
+ originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1
+ end
+ end
+ if not unicode then
+ local foundcodes, multiple = uparser:match(reference)
+ if foundcodes then
+ if multiple then
+ originals[index], tounicode[index], nl, unicode = foundcodes, tounicode16sequence(foundcodes), nl + 1, true
+ else
+ originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes
+ end
+ end
+ end
+ end
+ end
end
end
- end
- return cidmap
- end
- local cidmap = locate(registry,ordering,supplement)
- if not cidmap then
- local cidnum = nil
- -- next highest (alternatively we could start high)
- if supplement < fonts.otf.cidmax then
- for supplement=supplement+1,fonts.otf.cidmax do
- local c = locate(registry,ordering,supplement)
- if c then
- cidmap, cidnum = c, supplement
- break
+ -- a.whatever or a_b_c.whatever or a_b_c (no numbers)
+ if not unicode then
+ local split = ligsplitter:match(name)
+ local nplit = (split and #split) or 0
+ if nplit == 0 then
+ -- skip
+ elseif nplit == 1 then
+ unicode = unicodes[split[1]]
+ 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 done = true
+ for l=1,nplit do
+ local u = unicodes[split[l]]
+ if not u then
+ done = false
+ break
+ elseif type(u) == "table" then
+ split[l] = u[1]
+ else
+ split[l] = u
+ end
+ end
+ if done then
+ originals[index], tounicode[index], nl, unicode = split, tounicode16sequence(split), nl + 1, true
+ end
end
end
- end
- -- next lowest (least worse fit)
- if not cidmap and supplement > 0 then
- for supplement=supplement-1,0,-1 do
- local c = locate(registry,ordering,supplement)
- if c then
- cidmap, cidnum = c, supplement
- break
+ -- last resort
+ if not unicode then
+ local foundcodes, multiple = uparser:match(name)
+ if foundcodes then
+ if multiple then
+ originals[index], tounicode[index], nl, unicode = foundcodes, tounicode16sequence(foundcodes), nl + 1, true
+ else
+ originals[index], tounicode[index], ns, unicode = foundcodes, tounicode16(foundcodes), ns + 1, foundcodes
+ end
end
end
+ if not unicode then
+ originals[index], tounicode[index] = 0xFFFD, "FFFD"
+ end
end
- -- prevent further lookups
- if cidmap and cidnum > 0 then
- for s=0,cidnum-1 do
- filename = string.format(template,registry,ordering,s)
- if not fonts.otf.cidmaps[filename] then
- fonts.otf.cidmaps[filename] = cidmap -- copy of ref
- end
+ end
+ if trace_unimapping then
+ for index, glyph in table.sortedpairs(data.glyphs) do
+ local toun, name, unic = tounicode[index], glyph.name, glyph.unicode or -1 -- play safe
+ if toun then
+ logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun)
+ else
+ logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic)
end
end
end
- return cidmap
+ if trace_loading and (ns > 0 or nl > 0) then
+ logs.report("load otf","enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns)
+ end
end
---~ ["cidinfo"]={
---~ ["ordering"]="Japan1",
---~ ["registry"]="Adobe",
---~ ["supplement"]=6,
---~ ["version"]=6,
---~ },
+otf.enhancers["analyse subtables"] = function(data,filename)
+ data.luatex = data.luatex or { }
+ local luatex = data.luatex
+ local sequences = { }
+ local lookups = { }
+ luatex.sequences = sequences
+ luatex.lookups = lookups
+ for _, g in next, { data.gsub, data.gpos } do
+ for k=1,#g do
+ local gk = g[k]
+ local typ = gk.type
+ if typ == "gsub_contextchain" or typ == "gpos_contextchain" then
+ gk.chain = 1
+ elseif typ == "gsub_reversecontextchain" or typ == "gpos_reversecontextchain" then
+ gk.chain = -1
+ else
+ gk.chain = 0
+ end
+ local features = gk.features
+ if features then
+ sequences[#sequences+1] = gk
+ -- scripts, tag, ismac
+ local t = { }
+ for f=1,#features do
+ local feature = features[f]
+ local hash = { }
+ -- only script and langs matter
+ for s, languages in next, feature.scripts do
+ s = lower(s)
+ local h = hash[s]
+ if not h then h = { } hash[s] = h end
+ for l=1,#languages do
+ h[strip(lower(languages[l]))] = true
+ end
+ end
+ t[feature.tag] = hash
+ end
+ gk.features = t
+ else
+ lookups[gk.name] = gk
+ gk.name = nil
+ end
+ local subtables = gk.subtables
+ if subtables then
+ local t = { }
+ for s=1,#subtables do
+ local subtable = subtables[s]
+ local name = subtable.name
+ t[#t+1] = name
+ end
+ gk.subtables = t
+ end
+ local flags = gk.flags
+ if flags then
+ gk.flags = { -- forcing false packs nicer
+ (flags.ignorecombiningmarks and "mark") or false,
+ (flags.ignoreligatures and "ligature") or false,
+ (flags.ignorebaseglyphs and "base") or false,
+ flags.r2l or false
+ }
+ end
+ end
+ end
+end
-function fonts.otf.enhance.before(data,filename)
- local private = fonts.private
+otf.enhancers["merge cid fonts"] = function(data,filename)
+ -- we can also move the names to data.luatex.names which might
+ -- save us some more memory (at the cost of harder tracing)
if data.subfonts and table.is_empty(data.glyphs) then
local cidinfo = data.cidinfo
+ local verbose = fonts.verbose
if cidinfo.registry then
- local cidmap = fonts.otf.cidmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement)
+ 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 pairs(data.subfonts) do
- for index, g in pairs(subfont.glyphs) do
+ for n, subfont in next, data.subfonts do
+ for index, g in next, subfont.glyphs do
if not next(g) then
-- dummy entry
else
@@ -1178,91 +687,143 @@ function fonts.otf.enhance.before(data,filename)
g.boundingbox = g.boundingbox -- or zerobox
g.name = g.name or name or "unknown"
if unicode then
- g.unicode = unicode
uni_to_int[unicode] = index
int_to_uni[index] = unicode
nofunicodes = nofunicodes + 1
+ g.unicode = unicode
elseif name then
- g.unicode = -1
nofnames = nofnames + 1
+ g.unicode = -1
end
glyphs[index] = g
end
end
subfont.glyphs = nil
end
- logs.report("load otf",string.format("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames))
+ if trace_loading then
+ logs.report("load otf","cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames)
+ end
data.glyphs = glyphs
data.map = data.map or { }
data.map.map = uni_to_int
data.map.backmap = int_to_uni
- else
- logs.report("load otf",string.format("unable to remap cid font, missing cid file for %s",filename))
+ elseif trace_loading then
+ logs.report("load otf","unable to remap cid font, missing cid file for %s",filename)
end
- else
- logs.report("load otf",string.format("font %s has no glyphs",filename))
+ elseif trace_loading then
+ logs.report("load otf","font %s has no glyphs",filename)
end
end
- if data.map then
- local uni_to_int = data.map.map -- [unic] = slot
- local int_to_uni = data.map.backmap -- { [0|1] = unic, ... }
- for index, glyph in pairs(data.glyphs) do
- if glyph.name then
- local unic = glyph.unicode or glyph.unicodeenc or -1
- glyph.unicodeenc = nil -- older luatex version
- if index > 0 and (unic == -1 or unic >= 0x110000) then
- while uni_to_int[private] do
- private = private + 1
- end
- uni_to_int[private] = index
- int_to_uni[index] = private
+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
- if fonts.trace then
- logs.report("load otf",string.format("enhance: glyph %s at index %s is moved to private unicode slot %s",glyph.name,index,private))
+ indices[private] = index
+ unicodes[name] = private
+ internals[index] = true
+ if trace_private then
+ logs.report("load otf","enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",name,index,private)
end
+ private = private + 1
else
- glyph.unicode = unic -- safeguard for older version
+ indices[unicode] = index
+ unicodes[name] = unicode
end
end
end
- local n = 0
- for k,v in pairs(int_to_uni) do
- if v == -1 or v >= 0x110000 then
- int_to_uni[k], n = nil, n+1
- end
- end
- if fonts.trace then
- logs.report("load otf",string.format("enhance: %s entries removed from map.backmap",n))
- end
- local n = 0
- for k,v in pairs(uni_to_int) do
- if k == -1 or k >= 0x110000 then
- uni_to_int[k], n = nil, n+1
+ end
+ -- beware: the indeces table is used to initialize the tfm table
+ for unicode, index in next, mapmap do
+ if not internals[index] then
+ local name = glyphs[index].name
+ if name then
+ local un = unicodes[name]
+ if not un then
+ unicodes[name] = unicode -- or 0
+ elseif type(un) == "number" then
+ if un ~= unicode then
+ multiples[#multiples+1] = name
+ unicodes[name] = { un, unicode }
+ indices[unicode] = index
+ end
+ else
+ local ok = false
+ for u=1,#un do
+ if un[u] == unicode then
+ ok = true
+ break
+ end
+ end
+ if not ok then
+ multiples[#multiples+1] = name
+ un[#un+1] = unicode
+ indices[unicode] = index
+ end
+ end
end
end
- if fonts.trace then
- logs.report("load otf",string.format("enhance: %s entries removed from map.mapmap",n))
+ end
+ if trace_loading then
+ if #multiples > 0 then
+ logs.report("load otf","%s glyph are reused: %s",#multiples, concat(multiples," "))
+ else
+ logs.report("load otf","no glyph are reused")
end
- else
- data.map = { map = {}, backmap = {} }
end
- if data.ttf_tables then
- for _, v in ipairs(data.ttf_tables) do
- if v.data then v.data = "deleted" end
- --~ if v.data then v.data = v.data:gsub("\026","\\026") end -- does not work out well
+ luatex.indices = indices
+ luatex.unicodes = unicodes
+ luatex.private = private
+end
+
+otf.enhancers["cleanup ttf tables"] = function(data,filename)
+ local ttf_tables = data.ttf_tables
+ if ttf_tables then
+ for k=1,#ttf_tables do
+ if ttf_tables[k].data then ttf_tables[k].data = "deleted" end
end
end
- table.compact(data.glyphs)
+ data.ttf_tab_saved = nil
+end
+
+otf.enhancers["compact glyphs"] = function(data,filename)
+ table.compact(data.glyphs) -- needed?
if data.subfonts then
- for _, subfont in pairs(data.subfonts) do
- table.compact(subfont.glyphs)
+ for _, subfont in next, data.subfonts do
+ table.compact(subfont.glyphs) -- needed?
end
end
+end
+
+otf.enhancers["reverse coverage"] = function(data,filename)
-- we prefer the before lookups in a normal order
if data.lookups then
- for _, v in pairs(data.lookups) do
+ for _, v in next, data.lookups do
if v.rules then
- for _, vv in pairs(v.rules) do
+ for _, vv in next, v.rules do
local c = vv.coverage
if c and c.before then
c.before = table.reverse(c.before)
@@ -1271,79 +832,241 @@ function fonts.otf.enhance.before(data,filename)
end
end
end
---~ for index, glyph in pairs(data.glyphs) do
---~ for k,v in pairs(glyph) do
---~ if v == 0 then glyph[k] = nil end
---~ end
---~ end
end
-function fonts.otf.enhance.after(data,filename) -- to be split
- if fonts.otf.enhance.add_kerns then
- local glyphs, mapmap, unicodes = data.glyphs, data.map.map, data.luatex.unicodes
- local mkdone = false
- for index, glyph in pairs(data.glyphs) do
- if glyph.kerns then
- local mykerns = { } -- unicode indexed !
- for k,v in pairs(glyph.kerns) do
- local vc, vo, vl = v.char, v.off, v.lookup
- if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones
- local uvc = unicodes[vc]
- if uvc then
- local mkl = mykerns[vl]
+otf.enhancers["check italic correction"] = function(data,filename)
+ local glyphs = data.glyphs
+ local ok = false
+ for index, glyph in next, glyphs do
+ local ic = glyph.italic_correction
+ if ic then
+ if ic ~= 0 then
+ glyph.italic = ic
+ end
+ glyph.italic_correction = nil
+ ok = true
+ end
+ end
+ -- we can use this to avoid calculations
+ otf.tables.valid_fields[#otf.tables.valid_fields+1] = "has_italic"
+ data.has_italic = true
+end
+
+otf.enhancers["check math"] = function(data,filename)
+ if data.math then
+ -- we move the math stuff into a math subtable because we then can
+ -- test faster in the tfm copy
+ local glyphs = data.glyphs
+ local unicodes = data.luatex.unicodes
+ for index, glyph in next, glyphs do
+ local mk = glyph.mathkern
+ local hv = glyph.horiz_variants
+ local vv = glyph.vert_variants
+ if mk or hv or vv then
+ local math = { }
+ glyph.math = math
+ if mk then
+ for k, v in next, mk do
+ if not next(v) then
+ mk[k] = nil
+ end
+ end
+ math.kerns = mk
+ glyph.mathkern = nil
+ end
+ if hv then
+ math.horiz_variants = hv.variants
+ local p = hv.parts
+ if p then
+ if #p>0 then
+ for i=1,#p do
+ local pi = p[i]
+ pi.glyph = unicodes[pi.component] or 0
+ end
+ math.horiz_parts = p
+ end
+ end
+ local ic = hv.italic_correction
+ if ic and ic ~= 0 then
+ math.horiz_italic_correction = ic
+ end
+ glyph.horiz_variants = nil
+ end
+ if vv then
+ local uc = unicodes[index]
+ math.vert_variants = vv.variants
+ local p = vv.parts
+ if p then
+ if #p>0 then
+ for i=1,#p do
+ local pi = p[i]
+ pi.glyph = unicodes[pi.component] or 0
+ end
+ math.vert_parts = p
+ end
+ end
+ local ic = vv.italic_correction
+ if ic and ic ~= 0 then
+ math.vert_italic_correction = ic
+ end
+ glyph.vert_variants = nil
+ end
+ local ic = glyph.italic_correction
+ if ic then
+ if ic ~= 0 then
+ math.italic_correction = ic
+ end
+ glyph.italic_correction = nil
+ end
+ end
+ end
+ end
+end
+
+otf.enhancers["share widths"] = function(data,filename)
+ local glyphs = data.glyphs
+ local widths = { }
+ for index, glyph in next, glyphs do
+ local width = glyph.width
+ widths[width] = (widths[width] or 0) + 1
+ end
+ -- share width for cjk fonts
+ local wd, most = 0, 1
+ for k,v in next, widths do
+ if v > most then
+ wd, most = k, v
+ end
+ end
+ if most > 1000 then
+ if trace_loading then
+ logs.report("load otf", "most common width: %s (%s times), sharing (cjk font)",wd,most)
+ end
+ for k, v in next, glyphs do
+ if v.width == wd then
+ v.width = nil
+ end
+ end
+ data.luatex.defaultwidth = wd
+ end
+end
+
+-- kern: ttf has a table with kerns
+
+otf.enhancers["reorganize kerns"] = function(data,filename)
+ local glyphs, mapmap, unicodes = data.glyphs, data.luatex.indices, data.luatex.unicodes
+ local mkdone = false
+ for index, glyph in next, data.glyphs do
+ if glyph.kerns then
+ local mykerns = { }
+ for k,v in next, glyph.kerns do
+ local vc, vo, vl = v.char, v.off, v.lookup
+ if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones
+ local uvc = unicodes[vc]
+ if not uvc then
+ if trace_loading then
+ logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index)
+ end
+ else
+ if type(vl) ~= "table" then
+ vl = { vl }
+ end
+ for l=1,#vl do
+ local vll = vl[l]
+ local mkl = mykerns[vll]
if not mkl then
- mkl = { [unicodes[vc]] = vo }
- mykerns[v.lookup] = mkl
+ mkl = { }
+ mykerns[vll] = mkl
+ end
+ if type(uvc) == "table" then
+ for u=1,#uvc do
+ mkl[uvc[u]] = vo
+ end
else
- mkl[unicodes[vc]] = vo
+ mkl[uvc] = vo
end
- else
- logs.report("load otf", string.format("problems with unicode %s of kern %s at glyph %s",vc,k,index))
end
end
end
- glyph.mykerns = mykerns
- glyph.kerns = nil -- saves space and time
- mkdone = true
end
+ glyph.mykerns = mykerns
+ glyph.kerns = nil -- saves space and time
+ mkdone = true
end
- if mkdone then
- logs.report("load otf", "replacing 'kerns' tables by 'mykerns' tables")
+ 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
- if data.gpos then
- for _, gpos in ipairs(data.gpos) do
- if gpos.subtables then
- for _, subtable in ipairs(gpos.subtables) do
- local kernclass = subtable.kernclass
- if kernclass then
- for _, kcl in ipairs(kernclass) do
- local firsts, seconds, offsets, lookup = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup
- local maxfirsts, maxseconds = table.getn(firsts), table.getn(seconds)
- logs.report("load otf", string.format("adding kernclass %s with %s times %s pairs)",lookup, maxfirsts, maxseconds))
- for fk, fv in pairs(firsts) do
- for first in fv:gmatch("[^ ]+") do
- local glyph = glyphs[mapmap[unicodes[first]]]
- local mykerns = glyph.mykerns
- if not mykerns then
- mykerns = { } -- unicode indexed !
- glyph.mykerns = mykerns
- end
- local lookupkerns = mykerns[lookup]
- if not lookupkerns then
- lookupkerns = { }
- mykerns[lookup] = lookupkerns
+ data.kerns = nil
+ end
+ local dgpos = data.gpos
+ if dgpos then
+ for gp=1,#dgpos do
+ local gpos = dgpos[gp]
+ local subtables = gpos.subtables
+ if subtables then
+ for s=1,#subtables do
+ local subtable = subtables[s]
+ local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes
+ if kernclass then
+ for k=1,#kernclass do
+ local kcl = kernclass[k]
+ local firsts, seconds, offsets, lookups = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup -- singular
+ if type(lookups) ~= "table" then
+ lookups = { lookups }
+ end
+ for l=1,#lookups do
+ local lookup = lookups[l]
+ local maxfirsts, maxseconds = getn(firsts), getn(seconds)
+ if trace_loading then
+ logs.report("load otf", "adding kernclass %s with %s times %s pairs",lookup, maxfirsts, maxseconds)
+ end
+ for fk, fv in next, firsts do
+ for first in gmatch(fv,"[^ ]+") do
+ local first_unicode = unicodes[first]
+ if type(first_unicode) == "number" then
+ first_unicode = { first_unicode }
end
- for sk, sv in pairs(seconds) do
- for second in sv:gmatch("[^ ]+") do
- lookupkerns[unicodes[second]] = offsets[(fk-1) * maxseconds + sk]
+ for f=1,#first_unicode do
+ local glyph = glyphs[mapmap[first_unicode[f]]]
+ if glyph then
+ local mykerns = glyph.mykerns
+ if not mykerns then
+ mykerns = { } -- unicode indexed !
+ glyph.mykerns = mykerns
+ end
+ local lookupkerns = mykerns[lookup]
+ if not lookupkerns then
+ lookupkerns = { }
+ mykerns[lookup] = lookupkerns
+ end
+ for sk, sv in next, seconds do
+ local offset = offsets[(fk-1) * maxseconds + sk]
+ --~ local offset = offsets[sk] -- (fk-1) * maxseconds + sk]
+ for second in gmatch(sv,"[^ ]+") do
+ local second_unicode = unicodes[second]
+ if type(second_unicode) == "number" then
+ lookupkerns[second_unicode] = offset
+ else
+ for s=1,#second_unicode do
+ lookupkerns[second_unicode[s]] = offset
+ end
+ end
+ end
+ end
+ elseif trace_loading then
+ logs.report("load otf", "no glyph data for U+%04X", first_unicode[f])
end
end
end
end
end
- subtable.comment = "The kernclass table is merged into mykerns in the indexed glyph tables."
- subtable.kernclass = { }
end
+ subtable.comment = "The kernclass table is merged into mykerns in the indexed glyph tables."
+ subtable.kernclass = { }
end
end
end
@@ -1351,22 +1074,70 @@ function fonts.otf.enhance.after(data,filename) -- to be split
end
end
-function fonts.otf.enhance.strip(data)
- for k, v in pairs(data.glyphs) do
+otf.enhancers["strip not needed data"] = function(data,filename)
+ local verbose = fonts.verbose
+ local int_to_uni = data.luatex.unicodes
+ for k, v in next, data.glyphs do
local d = v.dependents
if d then v.dependents = nil end
+ local a = v.altuni
+ if a then v.altuni = nil end
+ if verbose then
+ local code = int_to_uni[k]
+ -- looks like this is done twice ... bug?
+ if code then
+ local vu = v.unicode
+ if not vu then
+ v.unicode = code
+ elseif type(vu) == "table" then
+ if vu[#vu] == code then
+ -- weird
+ else
+ vu[#vu+1] = code
+ end
+ elseif vu ~= code then
+ v.unicode = { vu, code }
+ end
+ end
+ else
+ v.unicode = nil
+ v.index = nil
+ end
end
- data.map = nil
- data.names = nil
data.luatex.comment = "Glyph tables have their original index. When present, mykern tables are indexed by unicode."
+ data.map = nil
+ data.names = nil -- funny names for editors
+ data.glyphcnt = nil
+ data.glyphmax = nil
+ if true then
+ data.gpos = nil
+ data.gsub = nil
+ data.anchor_classes = nil
+ end
end
-function fonts.otf.enhance.flatten(data,filename) -- to be split
- logs.report("load otf", "flattening 'specifications' tables")
- for k, v in pairs(data.glyphs) do
+otf.enhancers["migrate metadata"] = function(data,filename)
+ local global_fields = otf.tables.global_fields
+ local metadata = { }
+ for k,v in next, data do
+ if not global_fields[k] then
+ metadata[k] = v
+ data[k] = nil
+ end
+ end
+ data.metadata = metadata
+ -- goodies
+ local pfminfo = data.pfminfo
+ metadata.isfixedpitch = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose["proportion"] == "Monospaced")
+ metadata.charwidth = pfminfo and pfminfo.avgwidth
+end
+
+otf.enhancers["flatten glyph lookups"] = function(data,filename)
+ for k, v in next, data.glyphs do
if v.lookups then
- for kk, vv in pairs(v.lookups) do
- for kkk, vvv in ipairs(vv) do
+ for kk, vv in next, v.lookups do
+ for kkk=1,#vv do
+ local vvv = vv[kkk]
local s = vvv.specification
if s then
local t = vvv.type
@@ -1379,27 +1150,29 @@ function fonts.otf.enhance.flatten(data,filename) -- to be split
elseif t == "multiple" then
vv[kkk] = { "multiple", s.components }
elseif t == "position" then
- vv[kkk] = { "position", s.x or 0, s.y or 0, s.h or 0, s.v or 0 }
+ vv[kkk] = { "position", { s.x or 0, s.y or 0, s.h or 0, s.v or 0 } }
elseif t == "pair" then
local one, two, paired = s.offsets[1], s.offsets[2], s.paired or ""
if one then
if two then
- vv[kkk] = { "pair", paired, one.x or 0, one.y or 0, one.h or 0, one.v or 0, two.x or 0, two.y or 0, two.h or 0, two.v or 0 }
+ vv[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0 } }
else
- vv[kkk] = { "pair", paired, one.x or 0, one.y or 0, one.h or 0 }
+ vv[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } }
end
else
if two then
- vv[kkk] = { "pair", paired, 0, 0, 0, 0, two.x or 0, two.y or 0, two.h or 0, two.v or 0 }
+ vv[kkk] = { "pair", paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { }
else
vv[kkk] = { "pair", paired }
end
end
else
- logs.report("load otf", "flattening needed, warn Hans and/or Taco")
- for a, b in pairs(s) do
- if vvv[a] then
- logs.report("load otf", "flattening conflict, warn Hans and/or Taco")
+ if trace_loading then
+ logs.report("load otf", "flattening needed, report to context list")
+ end
+ for a, b in next, s do
+ if trace_loading and vvv[a] then
+ logs.report("load otf", "flattening conflict, report to context list")
end
vvv[a] = b
end
@@ -1410,15 +1183,18 @@ function fonts.otf.enhance.flatten(data,filename) -- to be split
end
end
end
- logs.report("load otf", "flattening 'anchor' tables")
- for k, v in pairs(data.glyphs) do
+end
+
+otf.enhancers["flatten anchor tables"] = function(data,filename)
+ for k, v in next, data.glyphs do
if v.anchors then
- for kk, vv in pairs(v.anchors) do
- for kkk, vvv in pairs(vv) do
- if vvv.x or vvv.y then -- kkk == "centry"
+ for kk, vv in next, v.anchors do
+ for kkk, vvv in next, vv do
+ if vvv.x or vvv.y then
vv[kkk] = { vvv.x or 0, vvv.y or 0 }
else
- for kkkk, vvvv in ipairs(vvv) do
+ for kkkk=1,#vvv do
+ local vvvv = vvv[kkkk]
vvv[kkkk] = { vvvv.x or 0, vvvv.y or 0 }
end
end
@@ -1426,14 +1202,24 @@ function fonts.otf.enhance.flatten(data,filename) -- to be split
end
end
end
- for _, tag in pairs({"gpos","gsub"}) do
+end
+
+otf.enhancers["flatten feature tables"] = function(data,filename)
+ -- is this needed? do we still use them at all?
+ for _, tag in next, otf.glists do
if data[tag] then
- logs.report("load otf", "flattening '" .. tag.. "' tables")
- for k, v in pairs(data[tag]) do
- if v.features then
- for kk, vv in ipairs(v.features) do
+ if trace_loading then
+ logs.report("load otf", "flattening %s table", tag)
+ end
+ for k, v in next, data[tag] do
+ local features = v.features
+ if features then
+ for kk=1,#features do
+ local vv = features[kk]
local t = { }
- for kkk, vvv in ipairs(vv.scripts) do
+ local scripts = vv.scripts
+ for kkk=1,#scripts do
+ local vvv = scripts[kkk]
t[vvv.script] = vvv.langs
end
vv.scripts = t
@@ -1444,12 +1230,12 @@ function fonts.otf.enhance.flatten(data,filename) -- to be split
end
end
-fonts.otf.enhance.patches = { }
+otf.enhancers.patches = otf.enhancers.patches or { }
-function fonts.otf.enhance.patch(data,filename)
- local basename = file.basename(filename)
- for pattern, action in pairs(fonts.otf.enhance.patches) do
- if basename:find(pattern) then
+otf.enhancers["patch bugs"] = function(data,filename)
+ local basename = file.basename(lower(filename))
+ for pattern, action in next, otf.enhancers.patches do
+ if find(basename,pattern) then
action(data,filename)
end
end
@@ -1457,374 +1243,228 @@ end
-- tex features
-function fonts.otf.enhance.enrich(data,filename)
- -- later
-end
-
--- patching
-
-do -- will move to a typescript
-
- local function patch(data,filename)
- if data.design_size == 0 then
- local ds = (file.basename(filename)):match("(%d+)")
- if ds then
- logs.report("load otf",string.format("patching design size (%s)",ds))
- data.design_size = tonumber(ds) * 10
- end
- end
- end
-
- fonts.otf.enhance.patches["^lmroman"] = patch
- fonts.otf.enhance.patches["^lmsans"] = patch
- fonts.otf.enhance.patches["^lmtypewriter"] = patch
-
+fonts.otf.enhancers["enrich with features"] = function(data,filename)
+ -- later, ctx only
end
-function fonts.otf.analyze_class(data,class)
- local classes = { }
- for index, glyph in pairs(data.glyphs) do
- if glyph.class == class then
- classes[glyph.unicode] = true
- end
- end
- return classes
+function otf.features.register(name,default)
+ otf.features.list[#otf.features.list+1] = name
+ otf.features.default[name] = default
end
-function fonts.otf.analyze_subtables(data)
- local subtables, name_to_type, internals, always_valid, ignore_flags, ctx_always = { }, { }, { }, { }, { }, { }
- local function collect(g)
- if g then
- for k,v in ipairs(g) do
- if v.features then
- local ignored = { false, false, false }
- if v.flags.ignorecombiningmarks then ignored[1] = 'mark' end
- if v.flags.ignorebasechars then ignored[2] = 'base' end
- if v.flags.ignoreligatures then ignored[3] = 'ligature' end
- if v.subtables then
- local type = v.type
- for _, feature in ipairs(v.features) do
- local ft = feature.tag:lower()
- subtables[ft] = subtables[ft] or { }
- ctx_always[ft] = v.always
- for script, languages in pairs(feature.scripts) do
- script = script:lower()
- script = script:strip()
- sft = subtables[ft]
- local sfts = sft[script]
- if not sfts then
- sfts = { }
- sft[script] = sfts
- end
- for _, language in ipairs(languages) do
- language = language:lower()
- language = language:strip()
- local sftsl = sfts[language]
- if not sftsl then
- sftsl = sfts[language] or { }
- sfts[language] = sftsl
- end
- local lookups, valid = sftsl.lookups or { }, sftsl.valid or { }
- for n, subtable in ipairs(v.subtables) do
- local stl = subtable.name
- if stl then
- lookups[#lookups+1] = stl
- valid[stl] = true
- name_to_type[stl] = type
- ignore_flags[stl] = ignored
- end
- end
- sftsl.lookups, sftsl.valid = lookups, valid
- end
- end
- end
- end
- else
- -- we have an internal feature, say ss_l_83 that resolves to
- -- subfeatures like ss_l_83_s which we find in the glyphs
- name_to_type[v.name] = v.type
- local lookups, valid = { }, { }
- for n, subtable in ipairs(v.subtables) do
- local stl = subtable.name
- if stl then
- lookups[#lookups+1] = stl
- valid[stl] = true
- always_valid[stl] = true
- end
- end
- internals[v.name] = {
- lookups = lookups,
- valid = valid
- }
- always_valid[v.name] = true -- bonus
- end
- end
- end
- end
- collect(data.gsub)
- collect(data.gpos)
- return subtables, name_to_type, internals, always_valid, ignore_flags, ctx_always
-end
-
-function fonts.otf.analyze_unicodes(data)
- local unicodes = { }
- for _, blob in pairs(data.glyphs) do
- if blob.name then
- unicodes[blob.name] = blob.unicode or 0
- end
- end
- unicodes['space'] = unicodes['space'] or 32 -- handly later on
- return unicodes
-end
-
-function fonts.otf.analyze_features(g, features)
- if g then
- local t, done = { }, { }
- for k=1,#g do
- local f = features or g[k].features
- if f then
- for k=1,#f do
- -- scripts and tag
- local tag = f[k].tag
- if not done[tag] then
- t[#t+1] = tag
- done[tag] = true
- end
- end
- end
- end
- if #t > 0 then
- return t
- end
- end
- return nil
-end
-
-function fonts.otf.valid_subtable(otfdata,kind,script,language)
- local tk = otfdata.luatex.subtables[kind]
- if tk then
- local tks = tk[script] or tk.dflt
- if tks then
- local tksl = tks[language] or tks.dflt
- if tksl then
- return tksl.lookups
- end
- end
- end
- return false
-end
-
-function fonts.otf.features.register(name,default)
- fonts.otf.features.list[#fonts.otf.features.list+1] = name
- fonts.otf.features.default[name] = default
-end
-
-function fonts.otf.set_features(tfmdata) -- node and base, simple mapping
- local shared = tfmdata.shared
- local otfdata = shared.otfdata
- shared.features = fonts.define.check(shared.features,fonts.otf.features.default)
- local features = shared.features
- local trace = fonts.otf.trace_features or fonts.otf.trace_set_features
- if not tfmdata.language then tfmdata.language = 'dflt' end
- if not tfmdata.script then tfmdata.script = 'dflt' end
+function otf.set_features(tfmdata,features)
+ local processes = { }
if not table.is_empty(features) then
- local gposlist = otfdata.luatex.gposfeatures
- local gsublist = otfdata.luatex.gsubfeatures
- local mode = tfmdata.mode or fonts.mode
+ local lists = {
+ fonts.triggers,
+ fonts.processors,
+ fonts.manipulators,
+ }
+ local mode = tfmdata.mode or fonts.mode -- or features.mode
local initializers = fonts.initializers
local fi = initializers[mode]
- if fi then -- todo: delay initilization for mode 'node'
+ if fi then
local fiotf = fi.otf
if fiotf then
local done = { }
- local function initialize(list) -- using tex lig and kerning
+ for l=1,4 do
+ local list = lists[l]
if list then
for i=1,#list do
local f = list[i]
local value = features[f]
if value and fiotf[f] then -- brr
if not done[f] then -- so, we can move some to triggers
- if trace then
- logs.report("define otf",string.format("initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown', tfmdata.fullname or 'unknown'))
+ if trace_features then
+ logs.report("define otf","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown', tfmdata.fullname or 'unknown')
end
fiotf[f](tfmdata,value) -- can set mode (no need to pass otf)
mode = tfmdata.mode or fonts.mode -- keep this, mode can be set local !
- local fi = initializers[mode]
- fiotf = fi.otf
+ local im = initializers[mode]
+ if im then
+ fiotf = initializers[mode].otf
+ end
done[f] = true
end
end
end
end
end
- initialize(fonts.triggers)
- initialize(gsublist)
- initialize(gposlist)
- initialize(fonts.manipulators)
end
end
local fm = fonts.methods[mode]
if fm then
local fmotf = fm.otf
- local sp = shared.processors
if fmotf then
- local function register(list) -- node manipulations
+ for l=1,4 do
+ local list = lists[l]
if list then
for i=1,#list do
local f = list[i]
- if features[f] and fmotf[f] then -- brr
- if trace then
- logs.report("define otf",string.format("installing feature handler %s for mode %s for font %s",f,mode or 'unknown', tfmdata.fullname or 'unknown'))
+ if fmotf[f] then -- brr
+ if trace_features then
+ logs.report("define otf","installing feature handler %s for mode %s for font %s",f,mode or 'unknown', tfmdata.fullname or 'unknown')
end
- sp[#sp+1] = fmotf[f]
+ processes[#processes+1] = fmotf[f]
end
end
end
end
- register(fonts.triggers)
- register(gsublist)
- register(gposlist)
- register(fonts.manipulators)
end
+ else
+ -- message
end
end
+ return processes, features
end
-function fonts.otf.otf_to_tfm(specification)
+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(fonts.tfm.cache(),cache_id)
+ local tfmdata = containers.read(tfm.cache(),cache_id)
+--~ print(cache_id)
if not tfmdata then
- local otfdata = fonts.otf.load(filename,format,sub,features and features.featurefile)
+ local otfdata = otf.load(filename,format,sub,features and features.featurefile)
if not table.is_empty(otfdata) then
- fonts.otf.add_dimensions(otfdata)
- if true then
- otfdata._shared_ = otfdata._shared_ or { -- aggressive sharing
- processes = { },
- lookuptable = { },
- featuredata = { },
- featurecache = { },
- }
- end
- tfmdata = fonts.otf.copy_to_tfm(otfdata)
+ otfdata.shared = otfdata.shared or {
+ featuredata = { },
+ anchorhash = { },
+ initialized = false,
+ }
+ tfmdata = otf.copy_to_tfm(otfdata,cache_id)
if not table.is_empty(tfmdata) then
tfmdata.unique = tfmdata.unique or { }
tfmdata.shared = tfmdata.shared or { } -- combine
local shared = tfmdata.shared
shared.otfdata = otfdata
- shared.features = features
- shared.processors = { }
+ shared.features = features -- default
shared.dynamics = { }
shared.processes = { }
- shared.lookuptable = { }
- shared.featuredata = { }
- shared.featurecache = { }
- if otfdata._shared_ then
- shared.processes = otfdata._shared_.processes
- shared.lookuptable = otfdata._shared_.lookuptable
- shared.featuredata = otfdata._shared_.featuredata
- shared.featurecache = otfdata._shared_.featurecache
- end
- fonts.otf.set_features(tfmdata)
- end
- end
- containers.write(fonts.tfm.cache(),cache_id,tfmdata)
+ shared.set_dynamics = otf.set_dynamics -- fast access and makes other modules independent
+ -- this will be done later anyway, but it's convenient to have
+ -- them already for fast access
+ tfmdata.luatex = otfdata.luatex
+ tfmdata.indices = otfdata.luatex.indices
+ tfmdata.unicodes = otfdata.luatex.unicodes
+ tfmdata.marks = otfdata.luatex.marks
+ tfmdata.originals = otfdata.luatex.originals
+ tfmdata.changed = { }
+ tfmdata.has_italic = otfdata.metadata.has_italic
+ if not tfmdata.language then tfmdata.language = 'dflt' end
+ if not tfmdata.script then tfmdata.script = 'dflt' end
+ shared.processes, shared.features = otf.set_features(tfmdata,fonts.define.check(features,otf.features.default))
+ end
+ end
+ containers.write(tfm.cache(),cache_id,tfmdata)
end
return tfmdata
end
-function fonts.otf.features.prepare_base_kerns(tfmdata,kind,value) -- todo what kind of kerns, currently all
- if value then
- local otfdata = tfmdata.shared.otfdata
- local charlist = otfdata.glyphs
- local unicodes = otfdata.luatex.unicodes
- local somevalid = fonts.otf.some_valid_feature(otfdata,kind,tfmdata.script,tfmdata.language)
- for _, chr in pairs(tfmdata.characters) do
- local d = charlist[chr.description.index]
- if d then
- local dk = d.mykerns
- if dk then
- local t, done = chr.kerns or { }, false
- for lookup,kerns in pairs(dk) do
- if somevalid[lookup] then
- for k, v in pairs(kerns) do
- if v ~= 0 then
- t[k], done = v, true
- end
+--~ {
+--~ ['boundingbox']={ 95, -458, 733, 1449 },
+--~ ['class']="base",
+--~ ['name']="braceleft",
+--~ ['unicode']=123,
+--~ ['vert_variants']={
+--~ ['italic_correction']=0,
+--~ ['parts']={
+--~ { ['component']="uni23A9", ['endConnectorLength']=1000, ['fullAdvance']=2546, ['is_extender']=0, ['startConnectorLength']=0, }, -- bot
+--~ { ['component']="uni23AA", ['endConnectorLength']=2500, ['fullAdvance']=2501, ['is_extender']=1, ['startConnectorLength']=2500, }, -- rep
+--~ { ['component']="uni23A8", ['endConnectorLength']=1000, ['fullAdvance']=4688, ['is_extender']=0, ['startConnectorLength']=1000, }, -- mid
+--~ { ['component']="uni23AA", ['endConnectorLength']=2500, ['fullAdvance']=2501, ['is_extender']=1, ['startConnectorLength']=2500, }, -- rep
+--~ { ['component']="uni23A7", ['endConnectorLength']=0, ['fullAdvance']=2546, ['is_extender']=0, ['startConnectorLength']=1000, }, -- top
+--~ },
+--~ ['variants']="braceleft braceleft.vsize1 braceleft.vsize2 braceleft.vsize3 braceleft.vsize4 braceleft.vsize5 braceleft.vsize6 braceleft.vsize7",
+--~ },
+--~ ['width']=793,
+--~ },
+
+-- the first version made a top/mid/not extensible table, now we just pass on the variants data
+-- and deal with it in the tfm scaler (there is no longer an extensible table anyway)
+
+-- we cannot share descriptions as virtual fonts might extend them (ok, we could
+-- use a cache with a hash
+
+function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder the tma to unicode (nasty due to one->many)
+ if data then
+ local glyphs, pfminfo, metadata = data.glyphs or { }, data.pfminfo or { }, data.metadata or { }
+ local luatex = data.luatex
+ local unicodes = luatex.unicodes -- names to unicodes
+ local indices = luatex.indices
+ local characters, parameters, math_parameters, descriptions = { }, { }, { }, { }
+ local tfm = {
+ characters = characters,
+ parameters = parameters,
+ math_parameters = math_parameters,
+ descriptions = descriptions,
+ indices = indices,
+ unicodes = unicodes,
+ }
+ -- indices maps from unicodes to indices
+ for u, i in next, indices do
+ characters[u] = { } -- we need this because for instance we add protruding info and loop over characters
+ descriptions[u] = glyphs[i]
+ end
+ -- math
+ if metadata.math then
+ -- parameters
+ for name, value in next, metadata.math do
+ math_parameters[name] = value
+ end
+ -- we could use a subset
+ for u, char in next, characters do
+ local d = descriptions[u]
+ local m = d.math
+ -- we have them shared because that packs nicer
+ -- we could prepare the variants and keep 'm in descriptions
+ if m then
+ local variants = m.horiz_variants
+ if variants then
+ local c = char
+ for n in variants:gmatch("[^ ]+") do
+ local un = unicodes[n]
+ if un and u ~= un then
+ c.next = un
+ c = characters[un]
end
end
- end
- if done then
- chr.kerns = t -- no empty assignments
- end
- else
- dk = d.kerns
- if dk then
- local t, done = chr.kerns or { }, false
- for _, v in pairs(dk) do
- if somevalid[v.lookup] then
- local k = unicodes[v.char]
- if k > 0 then
- t[k], done = v.off, true
+ c.horiz_variants = m.horiz_parts
+ else
+ local variants = m.vert_variants
+ if variants then
+ local c = char
+ for n in variants:gmatch("[^ ]+") do
+ local un = unicodes[n]
+ if un and u ~= un then
+ c.next = un
+ c = characters[un]
end
end
+ c.vert_variants = m.vert_parts
end
- if done then
- chr.kerns = t -- no empty assignments
- end
+ end
+ local kerns = m.kerns
+ if kerns then
+ char.mathkerns = kerns
end
end
end
end
- end
-end
-
-function fonts.otf.add_dimensions(data)
- if data then
- local force = fonts.otf.notdef
- for k, d in pairs(data.glyphs) do
- local bb, wd = d.boundingbox, d.width or 0
- if force and not d.name then
- d.name = ".notdef"
- end
- if wd ~= 0 and d.class == "mark" then
- d.width = -wd
- end
- if bb then
- local ht, dp = bb[4], -bb[2]
- if ht ~= 0 then d.height = ht end
- if dp ~= 0 then d.depth = dp end
- end
- d.index = k
- end
- end
-end
-
-function fonts.otf.copy_to_tfm(data) -- we can save a copy when we reorder the tma to unicode
- if data then
- local tfm = { characters = { }, parameters = { } }
- local unicodes = data.luatex.unicodes
- local characters = tfm.characters
- local parameters = tfm.parameters
- local glyphs = data.glyphs
- for k, d in pairs(glyphs) do
- if d.name then
- characters[d.unicode] = { description = d }
- end
- end
- local designsize = data.designsize or data.design_size or 100
+ -- end math
+ local designsize = metadata.designsize or metadata.design_size or 100
if designsize == 0 then
designsize = 100
end
local spaceunits = 500
- tfm.units = data.units_per_em or 1000
+ tfm.units = metadata.units_per_em or 1000
-- we need a runtime lookup because of running from cdrom or zip, brrr
- tfm.filename = input.findbinfile(texmf.instance,data.luatex.filename,"") or data.luatex.filename
- tfm.fullname = data.fontname or data.fullname
+ tfm.filename = resolvers.findbinfile(luatex.filename,"") or luatex.filename
+ tfm.fullname = metadata.fontname or metadata.fullname
+ tfm.psname = tfm.fullname
tfm.encodingbytes = 2
tfm.cidinfo = data.cidinfo
tfm.cidinfo.registry = tfm.cidinfo.registry or ""
@@ -1836,63 +1476,62 @@ function fonts.otf.copy_to_tfm(data) -- we can save a copy when we reorder the t
tfm.boundarychar = 65536
tfm.designsize = (designsize/10)*65536
tfm.spacer = "500 units"
- data.isfixedpitch = data.pfminfo and data.pfminfo.panose and data.pfminfo.panose["proportion"] == "Monospaced"
- data.charwidth = nil
- if data.pfminfo then
- data.charwidth = data.pfminfo.avgwidth
- end
- local endash, emdash = unicodes['space'], unicodes['emdash']
- if data.isfixedpitch then
- if characters[endash] then
- spaceunits, tfm.spacer = characters[endash].description.width, "space"
+ local endash, emdash = 0x20, 0x2014 -- unicodes['space'], unicodes['emdash']
+ if metadata.isfixedpitch then
+ if descriptions[endash] then
+ spaceunits, tfm.spacer = descriptions[endash].width, "space"
end
- if not spaceunits and characters[emdash] then
- spaceunits, tfm.spacer = characters[emdash].description.width, "emdash"
+ if not spaceunits and descriptions[emdash] then
+ spaceunits, tfm.spacer = descriptions[emdash].width, "emdash"
end
- if not spaceunits and data.charwidth then
- spaceunits, tfm.spacer = data.charwidth, "charwidth"
+ if not spaceunits and metadata.charwidth then
+ spaceunits, tfm.spacer = metadata.charwidth, "charwidth"
end
else
- if characters[endash] then
- spaceunits, tfm.spacer = characters[endash].description.width, "space"
+ if descriptions[endash] then
+ spaceunits, tfm.spacer = descriptions[endash].width, "space"
end
- if not spaceunits and characters[emdash] then
- spaceunits, tfm.spacer = characters[emdash].description.width/2, "emdash/2"
+ if not spaceunits and descriptions[emdash] then
+ spaceunits, tfm.spacer = descriptions[emdash].width/2, "emdash/2"
end
- if not spaceunits and data.charwidth then
- spaceunits, tfm.spacer = data.charwidth, "charwidth"
+ if not spaceunits and metadata.charwidth then
+ spaceunits, tfm.spacer = metadata.charwidth, "charwidth"
end
end
spaceunits = tonumber(spaceunits) or tfm.units/2 -- 500 -- brrr
parameters.slant = 0
- parameters.space = spaceunits
- parameters.space_stretch = tfm.units/2 -- 500
- parameters.space_shrink = 2*tfm.units/3 -- 333
- parameters.x_height = 4*tfm.units/5 -- 400
+ parameters.space = spaceunits -- 3.333 (cmr10)
+ parameters.space_stretch = tfm.units/2 -- 500 -- 1.666 (cmr10)
+ parameters.space_shrink = 1*tfm.units/3 -- 333 -- 1.111 (cmr10)
+ parameters.x_height = 2*tfm.units/5 -- 400
parameters.quad = tfm.units -- 1000
- parameters.extra_space = 0
if spaceunits < 2*tfm.units/5 then
-- todo: warning
end
- tfm.italicangle = data.italicangle
- tfm.ascender = math.abs(data.ascent or 0)
- tfm.descender = math.abs(data.descent or 0)
- if data.italicangle then -- maybe also in afm _
- parameters.slant = parameters.slant - math.round(math.tan(data.italicangle*math.pi/180))
+ local italicangle = metadata.italicangle
+ tfm.ascender = math.abs(metadata.ascent or 0)
+ tfm.descender = math.abs(metadata.descent or 0)
+ if italicangle then -- maybe also in afm _
+ tfm.italicangle = italicangle
+ parameters.slant = parameters.slant - math.round(math.tan(italicangle*math.pi/180))
end
- if data.isfixedpitch then
+ if metadata.isfixedpitch then
parameters.space_stretch = 0
parameters.space_shrink = 0
- elseif fonts.otf.syncspace then --
+ elseif otf.syncspace then --
parameters.space_stretch = spaceunits/2
parameters.space_shrink = spaceunits/3
end
- if data.pfminfo and data.pfminfo.os2_xheight and data.pfminfo.os2_xheight > 0 then
- parameters.x_height = data.pfminfo.os2_xheight
+ parameters.extra_space = parameters.space_shrink -- 1.111 (cmr10)
+ if pfminfo.os2_xheight and pfminfo.os2_xheight > 0 then
+ parameters.x_height = pfminfo.os2_xheight
else
- local x = characters[unicodes['x']]
+ local x = 0x78 -- unicodes['x']
if x then
- parameters.x_height = x.description.height
+ local x = descriptions[x]
+ if x then
+ parameters.x_height = x.height
+ end
end
end
-- [6]
@@ -1902,23 +1541,55 @@ function fonts.otf.copy_to_tfm(data) -- we can save a copy when we reorder the t
end
end
-function fonts.tfm.read_from_open_type(specification)
- local tfmtable = fonts.otf.otf_to_tfm(specification)
+otf.features.register('mathsize')
+
+function tfm.read_from_open_type(specification)
+ local tfmtable = otf.otf_to_tfm(specification)
if tfmtable then
+ local otfdata = tfmtable.shared.otfdata
tfmtable.name = specification.name
tfmtable.sub = specification.sub
- tfmtable = fonts.tfm.scale(tfmtable, specification.size)
+ local s = specification.size
+ local m = otfdata.metadata.math
+ if m then
+ local f = specification.features
+ if f then
+ local f = f.normal
+ if f and f.mathsize then
+ local mathsize = specification.mathsize or 0
+ if mathsize == 2 then
+ local p = m.ScriptPercentScaleDown
+ if p then
+ local ps = p * specification.textsize / 100
+ if trace_math then
+ logs.report("define font","asked script size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100)
+ end
+ s = ps
+ end
+ elseif mathsize == 3 then
+ local p = m.ScriptScriptPercentScaleDown
+ if p then
+ local ps = p * specification.textsize / 100
+ if trace_math then
+ logs.report("define font","asked scriptscript size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100)
+ end
+ s = ps
+ end
+ end
+ end
+ end
+ end
+ tfmtable = tfm.scale(tfmtable,s)
-- here we resolve the name; file can be relocated, so this info is not in the cache
- local otfdata = tfmtable.shared.otfdata
local filename = (otfdata and otfdata.luatex and otfdata.luatex.filename) or specification.filename
if not filename then
-- try to locate anyway and set otfdata.luatex.filename
end
if filename then
tfmtable.encodingbytes = 2
- tfmtable.filename = input.findbinfile(texmf.instance,filename,"") or filename
- tfmtable.fullname = otfdata.fontname or otfdata.fullname
- local order = otfdata and otfdata.order2
+ tfmtable.filename = resolvers.findbinfile(filename,"") or filename
+ tfmtable.fullname = tfmtable.fullname or otfdata.metadata.fontname or otfdata.metadata.fullname
+ local order = otfdata and otfdata.metadata.order2
if order == 0 then
tfmtable.format = 'opentype'
elseif order == 1 then
@@ -1930,2882 +1601,6 @@ function fonts.tfm.read_from_open_type(specification)
end
fonts.logger.save(tfmtable,file.extname(specification.filename),specification)
end
+--~ print(tfmtable.fullname)
return tfmtable
end
-
-function fonts.otf.analyze_only(otfdata)
- local analyze = fonts.otf.analyze_features
- return analyze(otfdata.gpos), analyze(otfdata.gsub)
-end
-
-local a_to_script = { }
-local a_to_language = { }
-
-do
-
- local context_setups = fonts.define.specify.context_setups
- local context_numbers = fonts.define.specify.context_numbers
-
- function fonts.otf.set_dynamics(tfmdata,attribute,features) --currently experimental and slow / hackery
- local shared = tfmdata.shared
- if shared then
- local dynamics = shared.dynamics
- if dynamics then
- features = features or context_setups[context_numbers[attribute]]
- if features then
- local script = features.script or 'dflt'
- local language = features.language or 'dflt'
- local ds = dynamics[script]
- if not ds then
- ds = { }
- dynamics[script] = ds
- end
- local dsl = ds[language]
- if not dsl then
- dsl = { }
- ds[language] = dsl
- end
- local dsla = dsl[attribute]
- if dsla then
- return dsla
- else
- a_to_script [attribute] = script
- a_to_language[attribute] = language
- dsla = { }
- local otfdata = shared.otfdata
- local methods = fonts.methods.node.otf
- local initializers = fonts.initializers.node.otf
- local gposfeatures, gsubfeatures = fonts.otf.analyze_only(otfdata,features)
- local default = fonts.otf.features.default
- local function register(list)
- if list then
- for i=1,#list do
- local f = list[i]
- local value = features[f] or default[f]
- if value then
- local i, m = initializers[f], methods[f]
- if i then
- i(tfmdata,value)
- end
- if m then
- dsla[#dsla+1] = m
- end
- end
- end
- end
- end
- register(fonts.triggers)
- register(gsubfeatures)
- register(gposfeatures)
- dynamics[script][language][attribute] = dsla
- return dsla
- end
- end
- end
- end
- return { } -- todo: false
- end
-
-end
-
--- scripts
-
-fonts.otf.default_language = 'latn'
-fonts.otf.default_script = 'dflt'
-
-function fonts.otf.valid_feature(otfdata,kind,script,language) -- return hash is faster
- if otfdata.luatex.ctx_always[kind] then
- script, language = 'dflt', 'dflt'
- else
- script = script or fonts.otf.default_script
- language = language or fonts.otf.default_language
- end
- script, language = script:lower(), language:lower() -- will go away, we will lowercase values
- local ft = otfdata.luatex.subtables[kind]
- local st = ft[script] or ft.dflt
- local lt = st and (st[language] or st.dflt)
- return false, otfdata.luatex.always_valid, lt.valid
-end
-
-function fonts.otf.some_valid_feature(otfdata,kind,script,language)
- if otfdata.luatex.ctx_always[kind] then
- script, language = 'dflt', 'dflt'
- else
- script = script or fonts.otf.default_script
- language = language or fonts.otf.default_language
- script, language = script:lower(), language:lower() -- will go away, we will lowercase values
- end
- local t = otfdata.luatex.subtables[kind]
- if t then
- local ts = t[script] or t.dflt
- if ts then
- local tsl = ts[language] or ts.dflt
- return (tsl and tsl.valid) or { }
- end
- end
- return { }
-end
-
-function fonts.otf.features.aux.resolve_ligatures(tfmdata,ligatures,kind)
- local otfdata = tfmdata.shared.otfdata
- local unicodes = otfdata.luatex.unicodes
- local chars = tfmdata.characters
- local changed = tfmdata.changed or { }
- local done = { }
- kind = kind or "unknown"
- local trace = fonts.otf.trace_features
- while true do
- local ok = false
- for k,v in pairs(ligatures) do
- local lig = v[1]
- if not done[lig] then
- local ligs = { }
- for s in lig:gmatch("[^ ]+") do
- ligs[#ligs+1] = s
- end
- if #ligs == 2 then
- local c, f, s = chars[v[2]], ligs[1], ligs[2]
- local uf, us = unicodes[f], unicodes[s]
- if changed[uf] or changed[us] then
- if trace then
- logs.report("define otf",string.format("%s: %s (%s) + %s (%s) ignored",kind,f,uf,s,us))
- end
- else
- local first, second = chars[uf], us
- if first and second then
- local t = first.ligatures
- if not t then
- t = { }
- first.ligatures = t
- end
- t[second] = {
- char = unicodes[c.description.name],
- type = 0
- }
- if trace then
- logs.report("define otf",string.format("%s: %s (%s) + %s (%s) = %s (%s)",kind,f,uf,s,us,c.description.name,unicodes[c.description.name]))
- end
- end
- end
- ok, done[lig] = true, c.description.name
- end
- end
- end
- if ok then
- for d,n in pairs(done) do
- local pattern = "^(" .. d .. ") "
- for k,v in pairs(ligatures) do
- v[1] = v[1]:gsub(pattern, function(str)
- return n .. " "
- end)
- end
- end
- else
- break
- end
- end
-end
-
-function fonts.otf.features.prepare_base_substitutions(tfmdata,kind,value) -- we can share some code with the node features
- if value then
- local ligatures = { }
- local otfdata = tfmdata.shared.otfdata
- local unicodes = otfdata.luatex.unicodes
- local trace = fonts.otf.trace_features
- local chars = tfmdata.characters
- local somevalid = fonts.otf.some_valid_feature(otfdata,kind,tfmdata.script,tfmdata.language)
- if not table.is_empty(somevalid) then
- tfmdata.changed = tfmdata.changed or { }
- local changed = tfmdata.changed
- local glyphs = otfdata.glyphs
- for k,c in pairs(chars) do
- local o = glyphs[c.description.index]
- if o and o.lookups then
- for lookup,ps in pairs(o.lookups) do
- if somevalid[lookup] then
- for i=1,#ps do
- local p = ps[i]
- local t = p[1]
- if t == 'substitution' then
- local pv = p[2] -- p.variant
- if pv then
- local upv = unicodes[pv]
- if upv and chars[upv] then
- if trace then
- logs.report("define otf",string.format("%s: %s (%s) => %s (%s)",kind,chars[k].description.name,k,chars[upv].description.name,upv))
- end
- chars[k] = chars[upv]
- changed[k] = true
- end
- end
- elseif t == 'alternate' then
- local pc = p[2] -- p.components
- if pc then
- pc = pa.components:match("([^ ]+)")
- if pc then
- local upc = unicodes[pc]
- if upc and chars[upc] then
- if trace then
- logs.report("define otf",string.format("%s: %s (%s) => %s (%s)",kind,chars[k].description.name,k,chars[upc].description.name,upc))
- end
- chars[k] = chars[upc]
- changed[k] = true
- end
- end
- end
- elseif t == 'ligature' and not changed[k] then
- local pc = p[2]
- if pc then
- if trace then
- logs.report("define otf",string.format("%s: %s => %s (%s)",kind,pc,chars[k].description.name,k))
- end
- ligatures[#ligatures+1] = { pc, k }
- end
- end
- end
- end
- end
- end
- end
- fonts.otf.features.aux.resolve_ligatures(tfmdata,ligatures,kind)
- end
- else
- tfmdata.ligatures = tfmdata.ligatures or { }
- end
-end
-
-function fonts.initializers.base.otf.liga(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'liga',value) end
-function fonts.initializers.base.otf.dlig(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'dlig',value) end
-function fonts.initializers.base.otf.rlig(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'rlig',value) end
-function fonts.initializers.base.otf.hlig(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'hlig',value) end
-function fonts.initializers.base.otf.pnum(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'pnum',value) end
-function fonts.initializers.base.otf.onum(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'onum',value) end
-function fonts.initializers.base.otf.tnum(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'tnum',value) end
-function fonts.initializers.base.otf.lnum(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'lnum',value) end
-function fonts.initializers.base.otf.zero(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'zero',value) end
-function fonts.initializers.base.otf.smcp(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'smcp',value) end
-function fonts.initializers.base.otf.cpsp(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'cpsp',value) end
-function fonts.initializers.base.otf.c2sc(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'c2sc',value) end
-function fonts.initializers.base.otf.ornm(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'ornm',value) end
-function fonts.initializers.base.otf.aalt(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'aalt',value) end
-
-function fonts.initializers.base.otf.hwid(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'hwid',value) end
-function fonts.initializers.base.otf.fwid(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'fwid',value) end
-
--- Here comes the real thing ... node processing! The next session prepares
--- things. The main features (unchained by rules) have their own caches,
--- while the private ones cache locally.
-
-do
-
- fonts.otf.features.prepare = { }
-
- local falsetable = { false, false, false }
-
- function fonts.otf.features.prepare.feature(tfmdata,kind,value)
- if value then
- local language, script = tfmdata.language or "dflt", tfmdata.script or "dflt"
- local shared = tfmdata.shared
- local otfdata = shared.otfdata
- local lookuptable = fonts.otf.valid_subtable(otfdata,kind,script,language)
- if lookuptable then
- local fullkind = kind .. script .. language
- if not shared.lookuptable [fullkind] then
- --~ print(tfmdata,file.basename(tfmdata.fullname or ""),kind,script,language,lookuptable,fullkind)
- local processes = { }
- -- featuredata and featurecache are indexed by lookup so we can share them
- shared.featuredata [kind] = shared.featuredata [kind] or { }
- shared.featurecache[kind] = shared.featurecache[kind] or false -- signal
- shared.lookuptable [fullkind] = lookuptable
- shared.processes [fullkind] = processes
- local types = otfdata.luatex.name_to_type
- local flags = otfdata.luatex.ignore_flags
- local preparers = fonts.otf.features.prepare
- local process = fonts.otf.features.process
- for i=1,#lookuptable do
- local lookupname = lookuptable[i]
- local lookuptype = types[lookupname]
- local prepare = preparers[lookuptype]
- if prepare then
- local processdata = prepare(tfmdata,kind,lookupname)
- if processdata then
- local processflags = flags[lookupname] or falsetable --- share false table
- -- local chain = (lookuptype == "gsub_contextchain") or (lookuptype == "gpos_contextchain")
- local chain = lookuptype:find("context") ~= nil
- processes[#processes+1] = { process[lookuptype], lookupname, processdata, processflags, chain }
- end
- end
- end
- end
- end
- end
- end
-
- -- helper: todo, we don't need to store non local ones for chains so we can pass the
- -- validator as parameter
-
- local pairs = pairs
-
- function fonts.otf.features.collect_ligatures(tfmdata,kind) -- ligs are spread all over the place
- local otfdata = tfmdata.shared.otfdata
- local unicodes = tfmdata.shared.otfdata.luatex.unicodes -- actually the char index is ok too
- local trace = fonts.otf.trace_features
- local ligatures = { }
- local function collect(lookup,o,ps)
- for i=1,#ps do
- local p = ps[i]
- if p[1] == 'ligature' then
- if trace then
- logs.report("define otf",string.format("feature %s lookup %s ligature %s => %s",kind,lookup,p[2],o.name))
- end
- local t = ligatures[lookup]
- if not t then
- t = { }
- ligatures[lookup] = t
- end
- local first = true
- for s in p[2]:gmatch("[^ ]+") do
- local u = unicodes[s]
- if first then
- if not t[u] then
- t[u] = { { } }
- end
- t = t[u]
- first = false
- else
- local t1 = t[1]
- if not t1[u] then
- t1[u] = { { } }
- end
- t = t1[u]
- end
- end
- t[2] = o.unicode
- end
- end
- end
- local forced, always, okay = fonts.otf.valid_feature(otfdata,kind,tfmdata.script,tfmdata.language)
- for _,o in pairs(otfdata.glyphs) do
- local lookups = o.lookups
- if lookups then
- if forced then
- for lookup, ps in pairs(lookups) do collect(lookup,o,ps) end
- elseif okay then
- for lookup, ps in pairs(lookups) do if always[lookup] or okay[lookup] then collect(lookup,o,ps) end end
- else
- for lookup, ps in pairs(lookups) do if always[lookup] then collect(lookup,o,ps) end end
- end
- end
- end
- return ligatures
- end
-
- -- gsub_single -> done
- -- gsub_multiple -> done
- -- gsub_alternate -> done
- -- gsub_ligature -> done
- -- gsub_context -> todo
- -- gsub_contextchain -> done
- -- gsub_reversecontextchain -> todo
-
- -- we used to share code in the following functions but that was relatively
- -- due to extensive calls to functions (easily hundreds of thousands per
- -- document)
-
- function fonts.otf.features.prepare.gsub_single(tfmdata,kind,lookupname)
- local featuredata = tfmdata.shared.featuredata[kind]
- local substitutions = featuredata[lookupname]
- if not substitutions then
- substitutions = { }
- featuredata[lookupname] = substitutions
- local otfdata = tfmdata.shared.otfdata
- local unicodes = otfdata.luatex.unicodes
- local trace = fonts.otf.trace_features
- for _, o in pairs(otfdata.glyphs) do
- local lookups = o.lookups
- if lookups then
- for lookup,ps in pairs(lookups) do
- if lookup == lookupname then
- for i=1,#ps do
- local p = ps[i]
- if p[1] == 'substitution' then
- local old, new = o.unicode, unicodes[p[2]]
- substitutions[old] = new
- if trace then
- logs.report("define otf",string.format("%s:%s substitution %s => %s",kind,lookupname,old,new))
- end
- end
- end
- end
- end
- end
- end
- end
- return substitutions
- end
-
- function fonts.otf.features.prepare.gsub_multiple(tfmdata,kind,lookupname)
- local featuredata = tfmdata.shared.featuredata[kind]
- local substitutions = featuredata[lookupname]
- if not substitutions then
- substitutions = { }
- featuredata[lookupname] = substitutions
- local otfdata = tfmdata.shared.otfdata
- local unicodes = otfdata.luatex.unicodes
- local trace = fonts.otf.trace_features
- for _,o in pairs(otfdata.glyphs) do
- local lookups = o.lookups
- if lookups then
- for lookup,ps in pairs(lookups) do
- if lookup == lookupname then
- for i=1,#ps do
- local p = ps[i]
- if p[1] == 'multiple' then
- local old, new = o.unicode, { }
- substitutions[old] = new
- for pc in p[2]:gmatch("[^ ]+") do
- new[#new+1] = unicodes[pc]
- end
- if trace then
- logs.report("define otf",string.format("%s:%s multiple %s => %s",kind,lookupname,old,table.concat(new," ")))
- end
- end
- end
- end
- end
- end
- end
- end
- return substitutions
- end
-
- function fonts.otf.features.prepare.gsub_alternate(tfmdata,kind,lookupname)
- -- todo: configurable preference list
- local featuredata = tfmdata.shared.featuredata[kind]
- local substitutions = featuredata[lookupname]
- if not substitutions then
- featuredata[lookupname] = { }
- substitutions = featuredata[lookupname]
- local otfdata = tfmdata.shared.otfdata
- local unicodes = otfdata.luatex.unicodes
- local trace = fonts.otf.trace_features
- for _,o in pairs(otfdata.glyphs) do
- local lookups = o.lookups
- if lookups then
- for lookup,ps in pairs(lookups) do
- if lookup == lookupname then
- for i=1,#ps do
- local p = ps[i]
- if p[1] == 'alternate' then
- local old = o.unicode
- local t = { }
- for pc in p[2]:gmatch("[^ ]+") do
- t[#t+1] = unicodes[pc]
- end
- substitutions[old] = t
- if trace then
- logs.report("define otf",string.format("%s:%s alternate %s => %s",kind,lookupname,old,table.concat(substitutions,"|")))
- end
- end
- end
- end
- end
- end
- end
- end
- return substitutions
- end
-
- function fonts.otf.features.prepare.gsub_ligature(tfmdata,kind,lookupname)
- -- we collect them for all lookups, this saves loops, we only use the
- -- lookupname for testing, we need to check if this leads to redundant
- -- collections
- local ligatures = tfmdata.shared.featuredata[kind]
- if not ligatures[lookupname] then
- ligatures = fonts.otf.features.collect_ligatures(tfmdata,kind)
- tfmdata.shared.featuredata[kind] = ligatures
- end
- return ligatures[lookupname]
- end
-
- function fonts.otf.features.prepare.contextchain(tfmdata,kind,lookupname)
- local featuredata = tfmdata.shared.featuredata[kind]
- local contexts = featuredata[lookupname]
- if not contexts then
- featuredata[lookupname] = { }
- contexts = featuredata[lookupname]
- local otfdata = tfmdata.shared.otfdata
- local unicodes = otfdata.luatex.unicodes
- local internals = otfdata.luatex.internals
- local flags = otfdata.luatex.ignore_flags
- local types = otfdata.luatex.name_to_type
- otfdata.luatex.covers = otfdata.luatex.covers or { }
- local characters = tfmdata.characters
- local cache = otfdata.luatex.covers
- local function uncover(covers,result)
- -- lpeg hardly faster (.005 sec on mk)
- for n=1,#covers do
- local c = covers[n]
- local cc = cache[c]
- if not cc then
- local t = { }
- for s in c:gmatch("[^ ]+") do
- t[unicodes[s]] = true
- end
- cache[c] = t
- result[#result+1] = t
- else
- result[#result+1] = cc
- end
- end
- end
- local lookupdata = otfdata.lookups[lookupname]
- if not lookupdata then
- logs.error("otf process", string.format("missing lookupdata table %s",lookupname))
- elseif lookupdata.rules then
- local rules = lookupdata.rules
- local center_match = fonts.otf.center_match
- for nofrules=1,#rules do
- local rule = rules[nofrules]
- local coverage = rule.coverage
- if coverage and coverage.current then
- local current, before, after, sequence = coverage.current, coverage.before, coverage.after, { }
- if before then
- uncover(before,sequence)
- end
- local start = #sequence + 1
- uncover(current,sequence)
- local stop = #sequence
- if after then
- uncover(after,sequence)
- end
- if sequence[1] then
- local lookups, lookuptype = rule.lookups, 'self'
- -- for the moment only lookup index 1
- if lookups then
- if #lookups > 1 then
- logs.report("otf process","WARNING: more than one lookup in rule")
- end
- lookuptype = types[lookups[1]]
- end
- -- this may be wrong; we cannot copy inside the for loop (out of memory with hz);
- -- so we may end up with a different usage of sequence in the chainproc handlers
- sequence = table.copy(sequence)
- -- we trigger on the first character in current
- for unic, _ in pairs(sequence[start]) do
- local t = contexts[unic]
- if not t then
- contexts[unic] = { lookups={}, flags=flags[lookupname] }
- t = contexts[unic].lookups
- end
- t[#t+1] = { nofrules, lookuptype, sequence, start, stop, lookups }
- end
- end
- end
- end
- end
- end
- return contexts
- end
-
- fonts.otf.features.prepare.gsub_context = fonts.otf.features.prepare.contextchain
- fonts.otf.features.prepare.gsub_contextchain = fonts.otf.features.prepare.contextchain
- fonts.otf.features.prepare.gsub_reversecontextchain = fonts.otf.features.prepare.contextchain
-
- -- ruled->lookup=ks_latn_l_27_c_4 => internal[ls_l_84] => valid[ls_l_84_s]
-
- -- gpos_mark2base -> done
- -- gpos_mark2ligature -> done
- -- gpos_mark2mark -> done
- -- gpos_single -> not done
- -- gpos_pair -> not done
- -- gpos_cursive -> not done
- -- gpos_context -> not done
- -- gpos_reversecontextchain -> not done
-
- function fonts.otf.features.prepare.anchors(tfmdata,kind,lookupname) -- tracing
- local featuredata = tfmdata.shared.featuredata[kind]
- local anchors = featuredata[lookupname]
- if not anchors then
- anchors = { }
- featuredata[lookupname] = anchors
- local otfdata = tfmdata.shared.otfdata
- local unicodes = otfdata.luatex.unicodes
- local validanchors = { }
- local glyphs = otfdata.glyphs
- local trace = fonts.otf.trace_features
- if otfdata.anchor_classes then
- local classes = otfdata.anchor_classes
- for k=1,#classes do
- local class = classes[k]
- if class.lookup == lookupname then
- if trace then
- logs.report("define otf",string.format("%s:%s anchor -> %s",kind,lookupname,class.name))
- end
- validanchors[class.name] = true
- end
- end
- end
- for _,o in pairs(glyphs) do
- local oanchor = o.anchors
- if oanchor then
- local t, ok = { }, false
- for type, anchors in pairs(oanchor) do -- types
- local tt = false
- for name, anchor in pairs(anchors) do
- if validanchors[name] then
- if not tt then
- tt = { [name] = anchor }
- t[type] = tt
- ok = true
- else
- tt[name] = anchor
- end
- end
- end
- end
- if ok then
- anchors[o.unicode] = t
- end
- end
- end
- end
---~ if kind == "mkmk" then print(lookupname,table.serialize(anchors)) end
- return anchors
- end
-
- fonts.otf.features.prepare.gpos_mark2base = fonts.otf.features.prepare.anchors
- fonts.otf.features.prepare.gpos_mark2ligature = fonts.otf.features.prepare.anchors
- fonts.otf.features.prepare.gpos_mark2mark = fonts.otf.features.prepare.anchors
- fonts.otf.features.prepare.gpos_cursive = fonts.otf.features.prepare.anchors
- fonts.otf.features.prepare.gpos_context = fonts.otf.features.prepare.contextchain
- fonts.otf.features.prepare.gpos_contextchain = fonts.otf.features.prepare.contextchain
-
- function fonts.otf.features.prepare.gpos_single(tfmdata,kind,lookupname)
- logs.report("otf define","gpos_single not yet supported")
- end
-
- -- ["kerns"]={ { ["char"]="ytilde", ["lookup"]="pp_l_1_s", ["off"]=-83, ...
- -- ["mykerns"] = { ["pp_l_1_s"] = { [67] = -28, ...
-
- function fonts.otf.features.prepare.gpos_pair(tfmdata,kind,lookupname)
- local featuredata = tfmdata.shared.featuredata[kind]
- local kerns = featuredata[lookupname]
- if not kerns then
- local trace = fonts.otf.trace_features
- featuredata[lookupname] = { }
- kerns = featuredata[lookupname]
- local otfdata = tfmdata.shared.otfdata
- local unicodes = otfdata.luatex.unicodes
- local glyphs = otfdata.glyphs
- -- ff has isolated kerns in a separate table
- for k,o in pairs(glyphs) do
- local list = o.mykerns
- if list then
- local omk = list[lookupname]
- if omk then
- local one = o.unicode
- for char, off in pairs(omk) do
- local two = char
- local krn = kerns[one]
- if krn then
- krn[two] = off
- else
- kerns[one] = { two = off }
- end
- if trace then
- logs.report("define otf",string.format("feature %s kern pair %s - %s",kind,one,two))
- end
- end
- end
- elseif o.kerns then
- local one = o.unicode
- local okerns = o.kerns
- for ok=1,#okerns do
- local k = okerns[ok]
- if k.lookup == lookupname then
- local char = k.char
- if char then
- local two = unicodes[char]
- local krn = kerns[one]
- if krn then
- krn[two] = k.off
- else
- kerns[one] = { two = k.off }
- end
- if trace then
- logs.report("define otf",string.format("feature %s kern pair %s - %s",kind,one,two))
- end
- end
- end
- end
- end
- list = o.lookups
- if list then
- local one = o.unicode
- for lookup,ps in pairs(list) do
- if lookup == lookupname then
- for i=1,#ps do
- local p = ps[i]
- if p[1] == 'pair' then
- local two = unicodes[p[2]]
- local krn = kerns[one]
- if krn then
- krn[two] = p
- else
- kerns[one] = { two = p }
- end
- if trace then
- logs.report("define otf",string.format("feature %s kern pair %s - %s",kind,one,two))
- end
- end
- end
- end
- end
- end
- end
- end
- return kerns
- end
-
- fonts.otf.features.prepare.gpos_contextchain = fonts.otf.features.prepare.contextchain
-
-end
-
--- can be generalized: one loop in main
-
-do
-
- local prepare = fonts.otf.features.prepare.feature
-
- function fonts.initializers.node.otf.aalt(tfm,value) return prepare(tfm,'aalt',value) end
- function fonts.initializers.node.otf.abvm(tfm,value) return prepare(tfm,'abvm',value) end
- function fonts.initializers.node.otf.afrc(tfm,value) return prepare(tfm,'afrc',value) end
- function fonts.initializers.node.otf.akhn(tfm,value) return prepare(tfm,'akhn',value) end
- function fonts.initializers.node.otf.blwm(tfm,value) return prepare(tfm,'blwm',value) end
- function fonts.initializers.node.otf.c2pc(tfm,value) return prepare(tfm,'c2pc',value) end
- function fonts.initializers.node.otf.c2sc(tfm,value) return prepare(tfm,'c2sc',value) end
- function fonts.initializers.node.otf.calt(tfm,value) return prepare(tfm,'calt',value) end
- function fonts.initializers.node.otf.case(tfm,value) return prepare(tfm,'case',value) end
- function fonts.initializers.node.otf.ccmp(tfm,value) return prepare(tfm,'ccmp',value) end
- function fonts.initializers.node.otf.clig(tfm,value) return prepare(tfm,'clig',value) end
- function fonts.initializers.node.otf.cpsp(tfm,value) return prepare(tfm,'cpsp',value) end
- function fonts.initializers.node.otf.cswh(tfm,value) return prepare(tfm,'cswh',value) end
- function fonts.initializers.node.otf.curs(tfm,value) return prepare(tfm,'curs',value) end
- function fonts.initializers.node.otf.dlig(tfm,value) return prepare(tfm,'dlig',value) end
- function fonts.initializers.node.otf.dnom(tfm,value) return prepare(tfm,'dnom',value) end
- function fonts.initializers.node.otf.expt(tfm,value) return prepare(tfm,'expt',value) end
- function fonts.initializers.node.otf.fin2(tfm,value) return prepare(tfm,'fin2',value) end
- function fonts.initializers.node.otf.fin3(tfm,value) return prepare(tfm,'fin3',value) end
- function fonts.initializers.node.otf.fina(tfm,value) return prepare(tfm,'fina',value) end
- function fonts.initializers.node.otf.frac(tfm,value) return prepare(tfm,'frac',value) end
- function fonts.initializers.node.otf.fwid(tfm,value) return prepare(tfm,'fwid',value) end
- function fonts.initializers.node.otf.haln(tfm,value) return prepare(tfm,'haln',value) end
- function fonts.initializers.node.otf.hist(tfm,value) return prepare(tfm,'hist',value) end
- function fonts.initializers.node.otf.hkna(tfm,value) return prepare(tfm,'hkna',value) end
- function fonts.initializers.node.otf.hlig(tfm,value) return prepare(tfm,'hlig',value) end
- function fonts.initializers.node.otf.hngl(tfm,value) return prepare(tfm,'hngl',value) end
- function fonts.initializers.node.otf.hwid(tfm,value) return prepare(tfm,'hwid',value) end
- function fonts.initializers.node.otf.init(tfm,value) return prepare(tfm,'init',value) end
- function fonts.initializers.node.otf.isol(tfm,value) return prepare(tfm,'isol',value) end
- function fonts.initializers.node.otf.ital(tfm,value) return prepare(tfm,'ital',value) end
- function fonts.initializers.node.otf.jp78(tfm,value) return prepare(tfm,'jp78',value) end
- function fonts.initializers.node.otf.jp83(tfm,value) return prepare(tfm,'jp83',value) end
- function fonts.initializers.node.otf.jp90(tfm,value) return prepare(tfm,'jp90',value) end
- function fonts.initializers.node.otf.kern(tfm,value) return prepare(tfm,'kern',value) end
- function fonts.initializers.node.otf.liga(tfm,value) return prepare(tfm,'liga',value) end
- function fonts.initializers.node.otf.lnum(tfm,value) return prepare(tfm,'lnum',value) end
- function fonts.initializers.node.otf.locl(tfm,value) return prepare(tfm,'locl',value) end
- function fonts.initializers.node.otf.mark(tfm,value) return prepare(tfm,'mark',value) end
- function fonts.initializers.node.otf.med2(tfm,value) return prepare(tfm,'med2',value) end
- function fonts.initializers.node.otf.medi(tfm,value) return prepare(tfm,'medi',value) end
- function fonts.initializers.node.otf.mgrk(tfm,value) return prepare(tfm,'mgrk',value) end
- function fonts.initializers.node.otf.mkmk(tfm,value) return prepare(tfm,'mkmk',value) end
- function fonts.initializers.node.otf.nalt(tfm,value) return prepare(tfm,'nalt',value) end
- function fonts.initializers.node.otf.nlck(tfm,value) return prepare(tfm,'nlck',value) end
- function fonts.initializers.node.otf.nukt(tfm,value) return prepare(tfm,'nukt',value) end
- function fonts.initializers.node.otf.numr(tfm,value) return prepare(tfm,'numr',value) end
- function fonts.initializers.node.otf.onum(tfm,value) return prepare(tfm,'onum',value) end
- function fonts.initializers.node.otf.ordn(tfm,value) return prepare(tfm,'ordn',value) end
- function fonts.initializers.node.otf.ornm(tfm,value) return prepare(tfm,'ornm',value) end
- function fonts.initializers.node.otf.pnum(tfm,value) return prepare(tfm,'pnum',value) end
- function fonts.initializers.node.otf.pref(tfm,value) return prepare(tfm,'pref',value) end
- function fonts.initializers.node.otf.pres(tfm,value) return prepare(tfm,'pres',value) end
- function fonts.initializers.node.otf.pstf(tfm,value) return prepare(tfm,'pstf',value) end
- function fonts.initializers.node.otf.rlig(tfm,value) return prepare(tfm,'rlig',value) end
- function fonts.initializers.node.otf.rphf(tfm,value) return prepare(tfm,'rphf',value) end
- function fonts.initializers.node.otf.salt(tfm,value) return prepare(tfm,'salt',value) end
- function fonts.initializers.node.otf.sinf(tfm,value) return prepare(tfm,'sinf',value) end
- function fonts.initializers.node.otf.smcp(tfm,value) return prepare(tfm,'smcp',value) end
- function fonts.initializers.node.otf.smpl(tfm,value) return prepare(tfm,'smpl',value) end
- function fonts.initializers.node.otf.ss01(tfm,value) return prepare(tfm,'ss01',value) end
- function fonts.initializers.node.otf.ss02(tfm,value) return prepare(tfm,'ss02',value) end
- function fonts.initializers.node.otf.ss03(tfm,value) return prepare(tfm,'ss03',value) end
- function fonts.initializers.node.otf.ss04(tfm,value) return prepare(tfm,'ss04',value) end
- function fonts.initializers.node.otf.ss05(tfm,value) return prepare(tfm,'ss05',value) end
- function fonts.initializers.node.otf.ss06(tfm,value) return prepare(tfm,'ss06',value) end
- function fonts.initializers.node.otf.ss07(tfm,value) return prepare(tfm,'ss07',value) end
- function fonts.initializers.node.otf.ss08(tfm,value) return prepare(tfm,'ss08',value) end
- function fonts.initializers.node.otf.ss09(tfm,value) return prepare(tfm,'ss09',value) end
- function fonts.initializers.node.otf.subs(tfm,value) return prepare(tfm,'subs',value) end
- function fonts.initializers.node.otf.sups(tfm,value) return prepare(tfm,'sups',value) end
- function fonts.initializers.node.otf.swsh(tfm,value) return prepare(tfm,'swsh',value) end
- function fonts.initializers.node.otf.titl(tfm,value) return prepare(tfm,'titl',value) end
- function fonts.initializers.node.otf.tnam(tfm,value) return prepare(tfm,'tnam',value) end
- function fonts.initializers.node.otf.tnum(tfm,value) return prepare(tfm,'tnum',value) end
- function fonts.initializers.node.otf.trad(tfm,value) return prepare(tfm,'trad',value) end
- function fonts.initializers.node.otf.unic(tfm,value) return prepare(tfm,'unic',value) end
- function fonts.initializers.node.otf.zero(tfm,value) return prepare(tfm,'zero',value) end
-
-end
-
-do
-
- -- todo: use nodes helpers
-
- local glyph = node.id('glyph')
- local glue = node.id('glue')
- local kern = node.id('kern')
- local disc = node.id('disc')
-
- local fontdata = fonts.tfm.id
- local has_attribute = node.has_attribute
- local set_attribute = node.set_attribute
- local state = attributes.numbers['state'] or 100
- local marknumber = attributes.numbers['mark'] or 200
- local format = string.format
- local report = logs.report
- local scale = tex.scale
-
- fonts.otf.features.process = { }
-
- -- we share some vars here, after all, we have no nested lookups and
- -- less code
-
- local tfmdata = false
- local otfdata = false
- local characters = false
- local marks = false
- local glyphs = false
- local currentfont = false
-
- -- we cheat a bit and assume that a font,attr combination are kind of ranged
-
- local context_setups = fonts.define.specify.context_setups
- local context_numbers = fonts.define.specify.context_numbers
-
- -- 1 loop over glyphs loop over lookups, quit at match
- -- 2 loop over glyphs loop over lookups, continue at match
- -- 3 loop over lookups loop over glyphs
-
- fonts.otf.strategy = 2
-
- function fonts.otf.features.process.feature(head,font,attr,kind,attribute)
- tfmdata = fontdata[font]
- local shared = tfmdata.shared
- otfdata = shared.otfdata
- characters = tfmdata.characters
- marks = otfdata.luatex.marks
- glyphs = otfdata.glyphs
- currentfont = font
- local script, language, strategy
- if attr and attr > 0 then
- local features = context_setups[context_numbers[attr]]
- language, script, strategy = features.language or "dflt", features.script or "dflt", features.strategy or fonts.otf.strategy
- else
- language, script, strategy = tfmdata.language or "dflt", tfmdata.script or "dflt", tfmdata.strategy or fonts.otf.strategy
- end
- local fullkind = kind .. script .. language
- local lookuptable = shared.lookuptable[fullkind]
- if lookuptable then
- -- local strategy = fonts.otf.strategy
- local types = otfdata.luatex.name_to_type
- local start, done, ok = head, false, false
- local processes = shared.processes[fullkind]
- if #processes == 1 then
- local p = processes[1]
- while start do -- evt splitsen
- if start.id == glyph then
- if start.subtype<256 and start.font == font and
- (not attr or has_attribute(start,0,attr)) and -- dynamic feature
- (not attribute or has_attribute(start,state,attribute)) then
- -- we can make the p vars also global to this closure
- local pp = p[3] -- all lookups
- local pc = pp[start.char]
- if pc then
- start, ok = p[1](start,kind,p[2],pc,pp,p[4])
- done = done or ok
- if start then start = start.next end
- else
- start = start.next
- end
- else
- start = start.next
- end
- elseif start.id == glue and p[5] then
- local pp = p[3] -- all lookups
- local pc = pp[32] -- space
- if pc then
- start, ok = p[1](start,kind,p[2],pc,pp,p[4])
- done = done or ok
- if start then start = start.next end
- else
- start = start.next
- end
- else
- start = start.next
- end
- end
- elseif strategy == 3 then
- for i=1,#processes do local p = processes[i]
- local pp = p[3]
- start = head
- while start do
- if start.id == glyph then
- if start.subtype<256 and start.font == font and
- (not attr or has_attribute(start,0,attr)) and -- dynamic feature
- (not attribute or has_attribute(start,state,attribute)) then
- local pc = pp[start.char]
- if pc then
- start, ok = p[1](start,kind,p[2],pc,pp,p[4])
- if ok then
- done = true
- end -- else
- if start then start = start.next end
- else
- start = start.next
- end
- else
- start = start.next
- end
- elseif start.id == glue then
- if p[5] then -- chain
- local pc = pp[32]
- if pc then
- start, ok = p[1](start,kind,p[2],pc,p[3],p[4])
- if ok then
- done = true
- end
- if start then start = start.next end
- else
- start = start.next
- end
- else
- start = start.next
- end
- else
- start = start.next
- end
- end
- end
- else
- while start do
- if start.id == glyph then
- if start.subtype<256 and start.font == font and
- (not attr or has_attribute(start,0,attr)) and -- dynamic feature
- (not attribute or has_attribute(start,state,attribute)) then
- local chr = start.char -- used ?
- for i=1,#processes do local p = processes[i]
- local pp = p[3]
---~ local pc = pp[chr]
- local pc = pp[start.char]
- if pc then
- start, ok = p[1](start,kind,p[2],pc,pp,p[4])
- if ok then
- done = true
- if strategy == 1 then
- break
- end
- end -- else
- if not start then
- break
- end
- end
- end
- if start then start = start.next end
- elseif start.id == glue then
- for i=1,#processes do local p = processes[i]
- if p[5] then -- chain
- local pp = p[3]
- local pc = pp[32]
- if pc then
- start, ok = p[1](start,kind,p[2],pc,pp,p[4])
- if ok then
- done = true
- if strategy == 1 then
- break
- end
- end
- if not start then
- break
- end
- end
- end
- end
- if start then start = start.next end
- else
- start = start.next
- end
- else
- start = start.next
- end
- end
- end
- return head, done
- else
- return head, false
- end
- end
-
- -- we can assume that languages that use marks are not hyphenated
- -- we can also assume that at most one discretionary is present
-
- local function toligature(start,stop,char,markflag,discfound) -- brr head
- if start ~= stop then
- if discfound then
- local lignode = node.copy(start)
- lignode.font = start.font
- lignode.char = char
- lignode.subtype = 2
- start = node.do_ligature_n(start, stop, lignode)
- if start.id == disc then
- local prev = start.prev
- start = start.next
- end
- else
- local deletemarks = markflag ~= "mark"
- start.components = node.copy_list(start,stop)
- node.slide(start.components)
- -- todo: components
- start.subtype = 2
- start.char = char
- local marknum = 1
- local next = start.next
- while true do
- if marks[next.char] then
- if not deletemarks then
- set_attribute(next,marknumber,marknum)
- end
- else
- marknum = marknum + 1
- end
- if next == stop then
- break
- else
- next = next.next
- end
- end
- next = stop.next
- while next do
- if next.id == glyph and next.font == currentfont and marks[next.char] then
- set_attribute(next,marknumber,marknum)
- next = next.next
- else
- break
- end
- end
- local next = start.next
- while next do
- if next == stop or deletemarks or marks[next.char] then
- local crap = next
- local np, nn = next.prev, next.next
- np.next = nn
- if nn then
- nn.prev = np
- end
- if next == stop then
- stop = crap.prev
- node.free(crap)
- break
- else
- next = nn
- node.free(crap)
- end
- else
- next = nn
- end
- end
- end
- end
- return start
- end
-
- function fonts.otf.features.process.gsub_single(start,kind,lookupname,replacements)
- if replacements then
- if fonts.otf.trace_replacements then
- report("otf process",format("%s:%s replacing 0x%04X by 0x%04X",kind,lookupname,start.char,replacements))
- end
- start.char = replacements
- return start, true
- else
- return start, false
- end
- end
-
- function fonts.otf.features.process.gsub_alternate(start,kind,lookupname,alternatives)
- if alternatives then
- if fonts.otf.trace_replacements then
- report("otf process",format("%s:%s alternative 0x%04X => %s",kind,lookupname,start.char,table.hexed(alternatives)))
- end
- start.char = alternatives[1] -- will be preference
- return start, true
- else
- return start, false
- end
- end
-
- function fonts.otf.features.process.gsub_multiple(start,kind,lookupname,multiples)
- if multiples then
- if fonts.otf.trace_replacements then
- report("otf process",format("%s:%s multiple 0x%04X => %s",kind,lookupname,start.char,table.hexed(multiples)))
- end
- start.char = multiples[1]
- if #multiples > 1 then
- for k=2,#multiples do
- local n = node.copy(start)
- local sn = start.next
- n.char = multiples[k]
- n.next = sn
- n.prev = start
- if sn then
- sn.prev = n
- end
- start.next = n
- start = n
- end
- end
- return start, true
- else
- return start, false
- end
- end
-
- function fonts.otf.features.process.gsub_ligature(start,kind,lookupname,ligatures,alldata,flags)
- local s, stop, discfound = start.next, nil, false
- while s do
- local id = s.id
- if id == glyph and s.subtype<256 then
- if s.font == currentfont then
- if marks[s.char] then
- s = s.next
- else
- local lg = ligatures[1][s.char]
- if not lg then
- break
- else
- stop = s
- ligatures = lg
- s = s.next
- end
- end
- else
- break
- end
- elseif id == disc then
- discfound = true
- s = s.next
- else
- break
- end
- end
- if stop and ligatures[2] then
- start = toligature(start,stop,ligatures[2],flags[1],discfound)
- if fonts.otf.trace_ligatures then
- report("otf process",format("%s: inserting ligature 0x%04X (%s)",kind,start.char,utf.char(start.char)))
- end
- return start, true
- end
- return start, false
- end
-
- function fonts.otf.features.process.gpos_mark2base(start,kind,lookupname,m_anchors,b_anchors)
- local markchar = start.char
- if marks[markchar] then
- local markanchors = m_anchors['mark']
- if markanchors then
- local component = start.prev
- while component and component.id == glyph and component.subtype<256 and component.font == currentfont do
- local basechar = component.char
- if marks[basechar] then
- component = component.prev
- else
- local baseanchors = b_anchors[basechar]
- if baseanchors then
- baseanchors = baseanchors['basechar']
- if baseanchors then
- for anchor, ma in pairs(markanchors) do
- local ba = baseanchors[anchor]
- if ba then
- local factor = tfmdata.factor
- local dx, dy = scale(ba[1]-ma[1],factor), scale(ba[2]-ma[2],factor)
- start.xoffset, start.yoffset = component.xoffset - dx, component.yoffset + dy
- if fonts.otf.trace_anchors then
- report("otf process",format("%s: anchoring mark 0x%04X to basechar 0x%04X => (%s,%s) => (%s,%s)",
- kind,markchar,basechar,dx,dy,start.xoffset,start.yoffset))
- end
- return start, true
- end
- end
- end
- end
- break
- end
- end
- end
- end
- return start, false
- end
-
- function fonts.otf.features.process.gpos_mark2ligature(start,kind,lookupname,m_anchors,b_anchors) -- maybe use copies
- local markchar = start.char
- if marks[markchar] then
- local markanchors = m_anchors['mark']
- if markanchors then
- local component = start.prev
- while component and component.id == glyph and component.subtype<256 and component.font == currentfont do
- local basechar = component.char
- if marks[basechar] then
- component = component.prev
- else
- local baseanchors = b_anchors[basechar]
- if baseanchors then
- baseanchors = baseanchors['baselig']
- if baseanchors then
- for anchor, ma in pairs(markanchors) do
- local ba = baseanchors[anchor]
- if ba then
- local n = has_attribute(start,marknumber)
- ba = ba[n]
- if ba then
- local factor = tfmdata.factor
- local dx, dy = scale(ba[1]-ma[1],factor), scale(ba[2]-ma[2],factor)
- start.xoffset, start.yoffset = component.xoffset - dx, component.yoffset + dy
- if fonts.otf.trace_anchors then
- report("otf process",format("%s: anchoring mark 0x%04X to baseligature 0x%04X => (%s,%s) => (%s,%s)",
- kind,markchar,basechar,dx,dy,component.xoffset,component.yoffset))
- end
- return start, true
- end
- end
- end
- end
- end
- break
- end
- end
- return start, done
- end
- end
- return start, false
- end
-
- function fonts.otf.features.process.gpos_mark2mark(start,kind,lookupname,b_anchors,m_anchors)
- local basemarkchar = start.char
---~ print(lookupname)
- if marks[basemarkchar] then
---~ print('')
---~ print('basemarkchar',basemarkchar)
---~ print('basemarkanchors', table.serialize(b_anchors))
- local baseanchors = b_anchors['basemark']
- if baseanchors then
- local component = start.next
- while component and component.id == glyph and component.subtype<256 and component.font == currentfont do
- local markchar = component.char
- if not marks[markchar] then
- break
- else
---~ print('markchar',markchar)
- local basemarkattr = has_attribute(start, marknumber) or 1
- local markattr = has_attribute(component,marknumber) or 1
---~ print(basemarkattr,markattr)
- if basemarkattr == markattr then -- still needed?
---~ print('markanchors *', table.serialize(m_anchors))
-
- local markanchors = m_anchors[markchar]
- if markanchors then
---~ print('markanchors')
- local markanchor = markanchors['mark']
- if markanchor then
- for anchor,ma in pairs(markanchor) do
- local ba = baseanchors[anchor]
- if ba then
- local factor = tfmdata.factor
- local dx, dy = scale(ba[1]-ma[1],factor), scale(ba[2]-ma[2],factor)
- component.xoffset, component.yoffset = start.xoffset - dx, start.yoffset + dy
- if fonts.otf.trace_anchors then
- report("otf process",format("%s:%s:%s anchoring mark 0x%04X to basemark 0x%04X => (%s,%s) => (%s,%s)",
- kind,anchor,markattr,markchar,basemarkchar,dx,dy,component.xoffset,component.yoffset))
- end
- return start, true
- end
- end
- end
- end
- component = component.next
- end
- end
- end
- end
- end
- return start, false
- end
-
- -- the following can be optimized, also, we can share the table (no need to collect)
-
- function fonts.otf.features.process.gpos_cursive(start,kind,lookupname,exitanchors,anchors)
- local trace = fonts.otf.trace_anchors
- local next, done, x, y, total_x, total_y, tx, ty, first = start.next, false, 0, 0, 0, 0, { }, { }, nil
- local factor = tfmdata.factor
- local function finish()
- local i = 0
- while first and first.id == glyph do
- if marks[first.char] then
- first = first.next
- else
- i = i + 1
- first.yoffset = scale(total_y, factor)
- if fonts.otf.trace_cursive then
- report("otf process",format("%s:%s move 0x%04X cursive (%s,%s)",kind,lookupname,first.char,"?",total_y))
- end
- if first == next then
- break
- else
- total_y = total_y - (ty[i] or 0)
- first = first.next
- end
- end
- end
- x, y, total_x, total_y, tx, ty, first = 0, 0, 0, 0, { }, { }, nil
- end
- while next do
- if next.id == glyph and next.subtype<256 and next.font == currentfont then
- local nextchar = next.char
- if marks[nextchar] then
- next = next.next
- else
- local entryanchors, exitanchors = anchors[nextchar], anchors[start.char]
- if entryanchors and exitanchors then
- local centry, cexit = entryanchors['centry'], exitanchors['cexit']
- if centry and cexit then
- for anchor, entry in pairs(centry) do
- local exit = cexit[anchor]
- if exit then
- if not first then first = start end
- local dx, dy = exit[1] + entry[1], -exit[2] + entry[2]
- tx[#tx+1], ty[#ty+1] = dx, dy
- total_x, total_y = total_x + dx, total_y + dy
- done = true
- break
- end
- end
- else
- finish()
- end
- else
- finish()
- end
- start = next
- next = start.next
- end
- else
- finish()
- break
- end
- end
- return start, done
- end
-
- function fonts.otf.features.process.gpos_single(start,kind,lookupname,basekerns,kerns)
- report("otf process","gpos_single not yet supported")
- return start, false
- end
-
- function fonts.otf.features.process.gpos_pair(start,kind,lookupname,basekerns,kerns)
- local next, prev, done = start.next, start, false
- -- to be optimized
- local trace = fonts.otf.trace_kerns
- local factor = tfmdata.factor
- while next and next.id == glyph and next.subtype<256 and next.font == currentfont do
- local cn = characters[next.char]
- if not cn or cn.description.class == 'mark' then
- prev = next
- next = next.next
- else
- local krn = basekerns[next.char]
- if not krn then
- -- skip
- elseif type(krn) == "table" then
- local a, b = krn[3], krn[7]
- if a and a ~= 0 then
- local k = nodes.kern(scale(a,factor))
- k.next = next
- k.prev = prev
- prev.next = k
- next.prev = k
- if trace then
- -- todo
- end
- end
- if b and b ~= 0 then
- report("otf process","we need to do something with the second kern xoff " .. b)
- end
- else
- -- todo, just start, next = node.insert_before(head,next,nodes.kern(scale(kern,factor)))
- if fonts.otf.trace_kerns then
- report("otf process",format("%s: inserting kern %s between 0x%04X and 0x%04X",kind,krn,prev.char,next.char))
- end
- local k = nodes.kern(scale(krn,factor))
- k.next = next
- k.prev = prev
- prev.next = k
- next.prev = k
- end
- break
- end
- end
- return start, done
- end
-
- local chainprocs = { } -- we can probably optimize this because they're all internal lookups
-
- -- For the moment we save each looked up glyph in the sequence, which is ok because
- -- each lookup in the chain has its own sequence. This saves memory. Only ligatures
- -- are stored in the featurecache, because we don't want to loop over all characters
- -- in order to locate them.
-
- function chainprocs.gsub_single(start,stop,kind,lookupname,sequence,f,l,lookups)
- local trace = fonts.otf.trace_replacements
- local c, r = trace and { }, trace and { }
- local lookup, index, current = 1, f, start
- while current ~= nil do
- if current.id == glyph then -- test for more ?
- local char = current.char
- local cacheslot = sequence[index]
- local replacement = cacheslot[char]
- if replacement == true then
- if lookups then
- local looks = glyphs[tfmdata.characters[char].description.index].lookups -- SLOW, USE OTFDATA
- if looks then
- local glyphlookups = otfdata.luatex.internals[lookups[lookup]].lookups
- local unicodes = otfdata.luatex.unicodes
- for gl=1,#glyphlookups do
- local lv = looks[glyphlookups[gl]]
- if lv then
- replacement = unicodes[lv[1][2]] or char
- cacheslot[char] = replacement
- break
- end
- end
- else
- replacement, cacheslot[char] = char, char
- end
- else
- replacement, cacheslot[char] = char, char
- end
- end
- if trace then
- c[#c+1], r[#r+1] = char, replacement
- end
- current.char = replacement
- if current == stop then
- break
- else
- current, lookup, index = current.next, lookup + 1, index + 1
- end
- elseif current == stop then
- break
- else
- current = current.next
- end
- end
- if trace then
- report("otf chain",format("%s: single replacement %s by %s",kind,table.hexed(c),table.hexed(r)))
- end
- return start
- end
-
- function chainprocs.gsub_multiple(start,stop,kind,lookupname,sequence,f,l,lookups)
- local char = start.char
- local cacheslot = sequence[f] -- [1]
- local replacement = cacheslot[char]
- if replacement == true then
- if lookups then
- local looks = glyphs[tfmdata.characters[char].description.index].lookups
- if looks then
- local lookups = otfdata.luatex.internals[lookups[1]].lookups
- local unicodes = otfdata.luatex.unicodes
- for l=1,#lookups do
- local lv = looks[lookups[l]]
- if lv then
- replacement = { }
- for c in lv[1][2]:gmatch("[^ ]+") do
- replacement[#replacement+1] = unicodes[c]
- end
- cacheslot[char] = replacement
- break
- end
- end
- else
- replacement = { char }
- cacheslot[char] = replacement
- end
- else
- replacement = { char }
- cacheslot[char] = replacement
- end
- end
- if fonts.otf.trace_replacements then
- report("otf chain",format("%s: replacing character 0x%04X by multiple 0x%04X",kind,char,table.hexed(replacement)))
- end
- start.char = replacement[1]
- if #replacement > 1 then
- for k=2,#replacement do
- local n = node.copy(start)
- local sn = start.next
- n.char = replacement[k]
- n.next = sn
- n.prev = start
- if sn then
- sn.prev = n
- end
- start.next = n
- start = n
- end
- end
- return start
- end
-
- function chainprocs.gsub_alternate(start,stop,kind,lookupname,sequence,f,l,lookups)
- local char = start.char
- local cacheslot = sequence[f] -- [1]
- local replacement = cacheslot[char]
- if replacement == true then
- if lookups then
- local looks = glyphs[tfmdata.characters[char].description.index].lookups
- if looks then
- local lookups = otfdata.luatex.internals[lookups[1]].lookups
- local unicodes = otfdata.luatex.unicodes
- for l=1,#lookups do
- local lv = looks[lookups[l]]
- if lv then
- replacement = { }
- for c in lv[1][2]:gmatch("[^ ]+") do
- replacement[#replacement+1] = unicodes[c]
- end
- cacheslot[char] = replacement
- break
- end
- end
- else
- replacement = { char }
- cacheslot[char] = replacement
- end
- else
- replacement = { char }
- cacheslot[char] = replacement
- end
- end
- if fonts.otf.trace_replacements then
- report("otf chain",format("%s: replacing character 0x%04X by alternate",kind,char))
- end
- start.char = replacement[1]
- return start
- end
-
- function chainprocs.gsub_ligature(start,stop,kind,lookupname,sequence,f,l,lookups,flags)
- if lookups then
- local featurecache = fontdata[currentfont].shared.featurecache
- if not featurecache[kind] then
- featurecache[kind] = fonts.otf.features.collect_ligatures(tfmdata,kind) -- double cached ?
- end
- local lookups = otfdata.luatex.internals[lookups[1]].lookups
- local ligaturecache = featurecache[kind]
- local trace = fonts.otf.trace_ligatures
- for i=1,#lookups do
- local ligatures = ligaturecache[lookups[i]]
- if ligatures and ligatures[start.char] then
- ligatures = ligatures[start.char]
- local s, discfound = start.next, false
- while s do
- local id = s.id
- if id == disc then
- s = s.next
- discfound = true
- elseif characters[s.char].description.class == 'mark' then -- marks
- s = s.next
- else
- local lg = ligatures[1][s.char]
- if not lg then
- break
- else
- ligatures = lg
- if s == stop then
- break
- else
- s = s.next
- end
- end
- end
- end
- if ligatures[2] then
- if trace then
- if start == stop then
- report("otf chain",format("%s: replacing character 0x%04X by ligature 0x%04X",kind,start.char,ligatures[2]))
- else
- report("otf chain",format("%s: replacing character 0x%04X upto 0x%04X by ligature 0x%04X",kind,start.char,stop.char,ligatures[2]))
- end
- end
- return toligature(start,stop,ligatures[2],flags[1],discfound)
- end
- break
- end
- end
- end
- return stop
- end
-
- -- weird, mkmk can have a mark2base, in idris font
-
- function chainprocs.gpos_mark2base(start,stop,kind,lookupname,sequence,f,l,lookups,flags)
- -- dynamic resolver
- local markchar = start.char
- if marks[markchar] then
- local anchortag = sequence[f][markchar]
- if anchortag == true then
- local ok = false
- local classes = otfdata.anchor_classes
- local lookups = otfdata.luatex.internals[lookups[1]].lookups
- for k=1,#classes do
- local v = classes[k]
- if v.lookup == lookups[1] then -- let's gamble for uniqueness: and v.type == kind then
- anchortag = v.name
- sequence[f][markchar] = anchortag
- ok = true
- break
- end
- end
- if not ok and fonts.otf.trace_anchors then
- report("otf chain",format("%s: no matching mark2base anchor class for 0x%04X, lookup %s",kind,markchar,lookups[1]))
- end
- end
- if anchortag ~= true then
- local component = start.prev
- while component and component.id == glyph and component.subtype<256 and component.font == currentfont do
- local basechar = component.char
- if marks[basechar] then
- component = component.prev
- else
- local bglyph = glyphs[characters[basechar].description.index] -- startchar
- local baseanchors = bglyph.anchors['basechar']
- if baseanchors then
- local ba = baseanchors[anchortag]
- if ba then
- local mglyph = glyphs[characters[markchar].description.index]
- local markanchors = mglyph.anchors['mark']
- if markanchors then
- local ma = markanchors[anchortag]
- if ma then
- local factor = tfmdata.factor
- local dx, dy = scale(ba[1]-ma[1],factor), scale(ba[2]-ma[2],factor)
- start.xoffset, start.yoffset = component.xoffset - dx, component.yoffset + dy
- if fonts.otf.trace_anchors then
- report("otf chain",format("%s: anchoring mark 0x%04X to basechar 0x%04X => (%s,%s) => (%s,%s)",
- kind,markchar,basechar,dx,dy,start.xoffset,start.yoffset))
- end
- return start, true
- end
- end
- end
- end
- break
- end
- end
- end
- end
- return start, false
- end
-
- function chainprocs.gpos_mark2ligature(start,stop,kind,lookupname,sequence,f,l,lookups,flags)
- -- dynamic resolver
- local markchar = start.char
- if marks[markchar] then
- local anchortag = sequence[f][markchar]
- if anchortag == true then
- local classes = otfdata.anchor_classes
- local lookups = otfdata.luatex.internals[lookups[1]].lookups
- local ok = false
- for k=1,#classes do
- local v = classes[k]
- if v.lookup == lookups[1] then -- and v.type == kind then
- anchortag = v.name
- sequence[f][markchar] = anchortag
- ok = true
- break
- end
- end
- if not ok and fonts.otf.trace_anchors then
- report("otf chain",format("%s: no matching mark2ligature anchor class for 0x%04X, lookup %s",kind,markchar,lookups[1]))
- end
- end
- if anchortag ~= true then
- local component = start.prev
- while component and component.id == glyph and component.subtype<256 and component.font == currentfont do
- local basechar = component.char
- if marks[basechar] then
- component = component.prev
- else
- local bglyph = glyphs[characters[basechar].description.index] -- startchar
- local baseanchors = bglyph.anchors['baselig']
- if baseanchors then
- local ba = baseanchors[anchortag]
- if ba then
- local n = has_attribute(start,marknumber)
- ba = ba[n] -- ok ?
- if ba then
- local mglyph = glyphs[characters[markchar].description.index]
- local markanchors = mglyph.anchors['mark']
- if markanchors then
- local ma = markanchors[anchortag]
- if ma then
- local factor = tfmdata.factor
- local dx, dy = scale(ba[1]-ma[1],factor), scale(ba[2]-ma[2],factor)
- start.xoffset, start.yoffset = component.xoffset - dx, component.yoffset + dy
- if fonts.otf.trace_anchors then
- report("otf chain",format("%s: anchoring mark 0x%04X to baseligature 0x%04X => (%s,%s) => (%s,%s)",
- kind,basechar,markchar,dx,dy,start.xoffset,start.yoffset))
- end
- return start, true
- end
- end
- end
- end
- end
- break
- end
- end
- end
- end
- return start, false
- end
-
- -- to be checked
-
- function chainprocs.gpos_mark2mark(start,stop,kind,lookupname,sequence,f,l,lookups)
- local component = start.next
- if component and component.id == glyph and component.subtype<256 and component.font == currentfont and marks[component.char] then
- local markchar = start.char
- local anchortag = sequence[f][markchar] -- [1][char]
- if anchortag == true then
- local classes = otfdata.anchor_classes
- local ok = false
- for k=1,#classes do
- local v = classes[k]
- if v.lookup == lookupname then -- and v.type == kind then
- anchortag = v.name
- sequence[f][markchar] = anchortag
- ok = true
- break
- end
- end
- if not ok and fonts.otf.trace_anchors then
- report("otf chain",format("%s: no matching mark2mark anchor class for 0x%04X, lookup %s",kind,markchar,lookups[1]))
- end
- end
- if anchortag ~= true then
- -- the following may have been be spoiled while idrising the other ones
- local markattr = has_attribute(start, marknumber) or 1 -- i need to check this ! 1 is new !
- local baseattr = has_attribute(component,marknumber) or 1 -- i need to check this ! 1 is new !
- if baseattr == markattr then
- local glyph = glyphs[characters[markchar].description.index]
- if glyph.anchors and glyph.anchors[anchortag] then
- local trace = fonts.otf.trace_anchors
- local done = false
- local baseanchors = glyph.anchors['basemark'][anchortag]
- while true do
- local basechar = component.char
- local charnext = characters[basechar]
- local markanchors = glyphs[charnext.description.index].anchors['mark'][anchortag]
- if markanchors then
- for anchor,data in pairs(markanchors) do
- local ba = baseanchors[anchor]
- if ba then
- local factor = tfmdata.factor
- local dx, dy = scale(ba[1]-ma[1],factor), scale(ba[2]-ma[2],factor)
- start.xoffset, start.yoffset = component.xoffset - dx, component.yoffset + dy
- if fonts.otf.trace_anchors then
- report("otf chain",format("%s: anchoring mark 0x%04X to basemark 0x%04X => (%s,%s) => (%s,%s)",
- kind,markchar,basechar,dx,dy,component.xoffset,component.yoffset))
- end
- done = true
- break
- end
- end
- end
- component = component.next
- if component and component.id == glyph and component.subtype<256 and component.font == currentfont and marks[component.char] then
- markattr = has_attribute(component,marknumber)
- if baseattr ~= markattr then
- break
- end
- else
- break
- end
- end
- return start, done
- end
- end
- end
- end
- return start, false
- end
-
- function chainprocs.gpos_cursive(start,stop,kind,lookupname,sequence,f,l,lookups)
- report("otf chain","chainproc gpos_cursive not yet supported")
- return start
- end
- function chainprocs.gpos_single(start,stop,kind,lookupname,sequence,f,l,lookups)
- report("otf process","chainproc gpos_single not yet supported")
- return start
- end
- function chainprocs.gpos_pair(start,stop,kind,lookupname,sequence,f,l,lookups)
- report("otf process","chainproc gpos_pair not yet supported")
- return start
- end
-
- function chainprocs.self(start,stop,kind,lookupname,sequence,f,l,lookups)
- report("otf process","self refering lookup cannot happen")
- return stop
- end
-
- local zwnj = 0x200C
- local zwj = 0x200D
-
- -- what pointer to return, spec says stop
-
- function fonts.otf.features.process.contextchain(start,kind,lookupname,contextdata)
- local contexts, flags, done = contextdata.lookups, contextdata.flags, false
- local skipmark, skipligature, skipbase = unpack(flags) -- unpack slower than assignment
- for k=1,#contexts do
- local match, next, last = true, start, start
- local rule, lookuptype, sequence, f, l, lookups = unpack(contexts[k]) -- unpack is slow
- local s = #sequence
- if s == 1 then
- match = next.id == glyph and next.subtype<256 and next.font == currentfont and sequence[1][next.char]
- else
- -- todo: better space check (maybe check for glue)
- local n = f
- while n <= l do
- if last then
- local id = last.id
- if id == glyph and last.subtype<256 and last.font == currentfont then
- local char = last.char
- local class = characters[char].description.class
- if class == skipmark or class == skipligature or class == skipbase then
- -- skip 'm
- last = last.next
- elseif sequence[n][char] then
- if n < l then
- last = last.next
- end
- n = n + 1
- else
- match = false break
- end
- elseif id == disc then -- what to do with kerns?
- last = last.next
- else
- match = false break
- end
- else
- match = false break
- end
- end
- if match and f > 1 then
- local prev = start.prev
- if prev then
- if f == 2 then
- match = prev.id == glyph and prev.subtype<256 and prev.font == currentfont and sequence[1][prev.char]
- else
- local n = f-1
- while n >= 1 do
- if prev then
- local id = prev.id
- if id == glyph and prev.subtype<256 and prev.font == currentfont then -- normal char
- local char = prev.char
- local class = characters[char].description.class
- if class == skipmark or class == skipligature or class == skipbase then
- -- skip 'm
- elseif sequence[n][char] then
- n = n -1
- else
- match = false break
- end
- elseif id == disc then
- -- skip 'm
- elseif sequence[n][32] then
- n = n -1
- else
- match = false break
- end
- prev = prev.prev
- elseif sequence[n][32] then
- n = n -1
- else
- match = false break
- end
- end
- end
- elseif f == 2 then
- match = sequence[1][32]
- else
- for n=f-1,1 do
- if not sequence[n][32] then
- match = false break
- end
- end
- end
- end
- if match and s > l then
- local next = last.next
- if next then
- if s-l == 1 then
- match = next.id == glyph and next.subtype<256 and next.font == currentfont and sequence[s][next.char]
- else
- local n = l+ 1
- while n <= s do
- if next then
- local id = next.id
- if id == glyph and next.subtype<256 and next.font == currentfont then -- normal char
- local char = next.char
- local class = characters[char].description.class
- if class == skipmark or class == skipligature or class == skipbase then
- -- skip 'm
- elseif sequence[n][char] then
- n = n + 1
- else
- match = false break
- end
- elseif id == disc then
- -- skip 'm
- elseif sequence[n][32] then -- brrr
- n = n + 1
- else
- match = false break
- end
- next = next.next
- elseif sequence[n][32] then
- n = n + 1
- else
- match = false break
- end
- end
- end
- elseif s-l == 1 then
- match = sequence[s][32]
- else
- for n=l+1,s do
- if not sequence[n][32] then
- match = false break
- end
- end
- end
- end
- end
- if match then
- local trace = fonts.otf.trace_contexts
- if trace then
- local char = start.char
- report("otf chain",format("%s: rule %s of %s matches at char 0x%04X (%s) for (%s,%s,%s) chars, lookuptype %s",kind,rule,lookupname,char,utf.char(char),f-1,l-f+1,s-l,lookuptype))
- end
- if lookups then
- local cp = chainprocs[lookuptype]
- if cp then
- start = cp(start,last,kind,lookupname,sequence,f,l,lookups,flags)
- else
- report("otf chain",format("%s: lookuptype %s not supported yet for %s",kind,lookuptype,lookupname))
- end
- elseif trace then
- report("otf chain",format("%s: skipping match for %s",kind,lookupname))
- end
- done = true
- break
- end
- end
- return start, done
- end
-
---~ if true then
---~ if n < f then
---~ texio.write_nl(string.format("%s before %s %04x %s %s %s",lookupname,n,char,class,skipmark or "?",tostring(sequence[n][char])))
---~ elseif n > l then
---~ texio.write_nl(string.format("%s after %s %04x %s %s %s",lookupname,n,char,class,skipmark or "?",tostring(sequence[n][char])))
---~ else
---~ texio.write_nl(string.format("%s current %s %04x %s %s %s",lookupname,n,char,class,skipmark or "?",tostring(sequence[n][char])))
---~ end
---~ end
-
---~ elseif char == zwnj and sequence[n][32] then -- brrr
-
- -- this needs to be fixed ! ! ! ! ! ! ! !
-
- function fonts.otf.features.process.reversecontextchain(start,kind,lookupname,contextdata)
- -- PROBABLY WRONG, WE NEED TO WALK BACK OVER THE LIST
- local done = false
- local contexts = contextdata.lookups
- local flags = contextdata.flags
- local skipmark, skipligature, skipbase = unpack(flags)
- for k=1,#contexts do
- local match, next, first, last = true, start, start, start
- local rule, lookuptype, sequence, f, l, lookups = unpack(contexts[k]) -- unpack is slow
- if #sequence == 1 then
- match = next.id == glyph and next.subtype<256 and next.font == currentfont and sequence[1][next.char]
- else
- local n, s = #sequence, 1
- while n > 0 do
- if next then
- local id = next.id
- if id == glyph and next.subtype<256 and next.font == currentfont then -- normal char
- local char = next.char
- local class = characters[char].description.class
- if class == skipmark or class == skipligature or class == skipbase then
- -- skip
- elseif sequence[n][char] then
- if n == f then
- first = next -- ok ?
- end
- if n == l then
- last = next -- ok ?
- end
- n = n - 1
- else
- match = false break
- end
- elseif id == disc then
- -- skip
- elseif not sequence[n][32] then -- brrr
- match = false break
- end
- next = next.next
- elseif sequence[n][32] then
- n = n - 1
- else
- match = false break
- end
- end
- end
- if match then
- local trace = fonts.otf.trace_contexts
- if trace then
- local char = first.char
- report("otf reverse chain",format("%s: rule %s of %s matches, replacing starts at char 0x%04X (%s) lookuptype %s",kind,rule,lookupname,char,utf.char(char),lookuptype))
- end
- if lookups then
- local cp = chainprocs[lookuptype]
- if cp then
- if start == first then
- start = cp(first,last,kind,lookupname,sequence,f,l,lookups,flags)
- else
- first = cp(first,last,kind,lookupname,sequence,f,l,lookups,flags)
- end
- else
- report("otf reverse chain",format("%s: lookuptype %s not supported yet for %s",kind,lookuptype,lookupname))
- end
- elseif trace then
- report("otf reverse chain",format("%s: skipping match for %s",kind,lookupname))
- end
- done = true
- break
- end
- end
- return start, done
- end
-
- fonts.otf.features.process.gsub_context = fonts.otf.features.process.contextchain
- fonts.otf.features.process.gsub_contextchain = fonts.otf.features.process.contextchain
- fonts.otf.features.process.gsub_reversecontextchain = fonts.otf.features.process.reversecontextchain
-
- fonts.otf.features.process.gpos_contextchain = fonts.otf.features.process.contextchain
- fonts.otf.features.process.gpos_context = fonts.otf.features.process.contextchain
-
-end
-
-do
-
- local process = fonts.otf.features.process.feature
-
- function fonts.methods.node.otf.aalt(head,font,attr) return process(head,font,attr,'aalt') end
- function fonts.methods.node.otf.abvm(head,font,attr) return process(head,font,attr,'abvm') end
- function fonts.methods.node.otf.afrc(head,font,attr) return process(head,font,attr,'afrc') end
- function fonts.methods.node.otf.akhn(head,font,attr) return process(head,font,attr,'akhn') end
- function fonts.methods.node.otf.blwm(head,font,attr) return process(head,font,attr,'blwm') end
- function fonts.methods.node.otf.c2pc(head,font,attr) return process(head,font,attr,'c2pc') end
- function fonts.methods.node.otf.c2sc(head,font,attr) return process(head,font,attr,'c2sc') end
- function fonts.methods.node.otf.calt(head,font,attr) return process(head,font,attr,'calt') end
- function fonts.methods.node.otf.case(head,font,attr) return process(head,font,attr,'case') end
- function fonts.methods.node.otf.ccmp(head,font,attr) return process(head,font,attr,'ccmp') end
- function fonts.methods.node.otf.clig(head,font,attr) return process(head,font,attr,'clig') end
- function fonts.methods.node.otf.cpsp(head,font,attr) return process(head,font,attr,'cpsp') end
- function fonts.methods.node.otf.cswh(head,font,attr) return process(head,font,attr,'cswh') end
- function fonts.methods.node.otf.curs(head,font,attr) return process(head,font,attr,'curs') end
- function fonts.methods.node.otf.dlig(head,font,attr) return process(head,font,attr,'dlig') end
- function fonts.methods.node.otf.dnom(head,font,attr) return process(head,font,attr,'dnom') end
- function fonts.methods.node.otf.expt(head,font,attr) return process(head,font,attr,'expt') end
- function fonts.methods.node.otf.fin2(head,font,attr) return process(head,font,attr,'fin2') end
- function fonts.methods.node.otf.fin3(head,font,attr) return process(head,font,attr,'fin3') end
- function fonts.methods.node.otf.fina(head,font,attr) return process(head,font,attr,'fina',3) end
- function fonts.methods.node.otf.frac(head,font,attr) return process(head,font,attr,'frac') end
- function fonts.methods.node.otf.fwid(head,font,attr) return process(head,font,attr,'fwid') end
- function fonts.methods.node.otf.haln(head,font,attr) return process(head,font,attr,'haln') end
- function fonts.methods.node.otf.hist(head,font,attr) return process(head,font,attr,'hist') end
- function fonts.methods.node.otf.hkna(head,font,attr) return process(head,font,attr,'hkna') end
- function fonts.methods.node.otf.hlig(head,font,attr) return process(head,font,attr,'hlig') end
- function fonts.methods.node.otf.hngl(head,font,attr) return process(head,font,attr,'hngl') end
- function fonts.methods.node.otf.hwid(head,font,attr) return process(head,font,attr,'hwid') end
- function fonts.methods.node.otf.init(head,font,attr) return process(head,font,attr,'init',1) end
- function fonts.methods.node.otf.isol(head,font,attr) return process(head,font,attr,'isol',4) end
- function fonts.methods.node.otf.ital(head,font,attr) return process(head,font,attr,'ital') end
- function fonts.methods.node.otf.jp78(head,font,attr) return process(head,font,attr,'jp78') end
- function fonts.methods.node.otf.jp83(head,font,attr) return process(head,font,attr,'jp83') end
- function fonts.methods.node.otf.jp90(head,font,attr) return process(head,font,attr,'jp90') end
- function fonts.methods.node.otf.kern(head,font,attr) return process(head,font,attr,'kern') end
- function fonts.methods.node.otf.liga(head,font,attr) return process(head,font,attr,'liga') end
- function fonts.methods.node.otf.lnum(head,font,attr) return process(head,font,attr,'lnum') end
- function fonts.methods.node.otf.locl(head,font,attr) return process(head,font,attr,'locl') end
- function fonts.methods.node.otf.mark(head,font,attr) return process(head,font,attr,'mark') end
- function fonts.methods.node.otf.med2(head,font,attr) return process(head,font,attr,'med2') end
- function fonts.methods.node.otf.medi(head,font,attr) return process(head,font,attr,'medi',2) end
- function fonts.methods.node.otf.mgrk(head,font,attr) return process(head,font,attr,'mgrk') end
- function fonts.methods.node.otf.mkmk(head,font,attr) return process(head,font,attr,'mkmk') end
- function fonts.methods.node.otf.nalt(head,font,attr) return process(head,font,attr,'nalt') end
- function fonts.methods.node.otf.nlck(head,font,attr) return process(head,font,attr,'nlck') end
- function fonts.methods.node.otf.nukt(head,font,attr) return process(head,font,attr,'nukt') end
- function fonts.methods.node.otf.numr(head,font,attr) return process(head,font,attr,'numr') end
- function fonts.methods.node.otf.onum(head,font,attr) return process(head,font,attr,'onum') end
- function fonts.methods.node.otf.ordn(head,font,attr) return process(head,font,attr,'ordn') end
- function fonts.methods.node.otf.ornm(head,font,attr) return process(head,font,attr,'ornm') end
- function fonts.methods.node.otf.pnum(head,font,attr) return process(head,font,attr,'pnum') end
- function fonts.methods.node.otf.pref(head,font,attr) return process(head,font,attr,'pref') end
- function fonts.methods.node.otf.pres(head,font,attr) return process(head,font,attr,'pres') end
- function fonts.methods.node.otf.pstf(head,font,attr) return process(head,font,attr,'pstf') end
- function fonts.methods.node.otf.rlig(head,font,attr) return process(head,font,attr,'rlig') end
- function fonts.methods.node.otf.rphf(head,font,attr) return process(head,font,attr,'rphf') end
- function fonts.methods.node.otf.salt(head,font,attr) return process(head,font,attr,'calt') end
- function fonts.methods.node.otf.sinf(head,font,attr) return process(head,font,attr,'sinf') end
- function fonts.methods.node.otf.smcp(head,font,attr) return process(head,font,attr,'smcp') end
- function fonts.methods.node.otf.smpl(head,font,attr) return process(head,font,attr,'smpl') end
- function fonts.methods.node.otf.ss01(head,font,attr) return process(head,font,attr,'ss01') end
- function fonts.methods.node.otf.ss02(head,font,attr) return process(head,font,attr,'ss02') end
- function fonts.methods.node.otf.ss03(head,font,attr) return process(head,font,attr,'ss03') end
- function fonts.methods.node.otf.ss04(head,font,attr) return process(head,font,attr,'ss04') end
- function fonts.methods.node.otf.ss05(head,font,attr) return process(head,font,attr,'ss05') end
- function fonts.methods.node.otf.ss06(head,font,attr) return process(head,font,attr,'ss06') end
- function fonts.methods.node.otf.ss07(head,font,attr) return process(head,font,attr,'ss07') end
- function fonts.methods.node.otf.ss08(head,font,attr) return process(head,font,attr,'ss08') end
- function fonts.methods.node.otf.ss09(head,font,attr) return process(head,font,attr,'ss09') end
- function fonts.methods.node.otf.subs(head,font,attr) return process(head,font,attr,'subs') end
- function fonts.methods.node.otf.sups(head,font,attr) return process(head,font,attr,'sups') end
- function fonts.methods.node.otf.swsh(head,font,attr) return process(head,font,attr,'swsh') end
- function fonts.methods.node.otf.titl(head,font,attr) return process(head,font,attr,'titl') end
- function fonts.methods.node.otf.tnam(head,font,attr) return process(head,font,attr,'tnam') end
- function fonts.methods.node.otf.tnum(head,font,attr) return process(head,font,attr,'tnum') end
- function fonts.methods.node.otf.trad(head,font,attr) return process(head,font,attr,'trad') end
- function fonts.methods.node.otf.unic(head,font,attr) return process(head,font,attr,'unic') end
- function fonts.methods.node.otf.zero(head,font,attr) return process(head,font,attr,'zero') end
-
-end
-
--- common stuff
-
-function fonts.otf.features.language(tfmdata,value)
- if value then
- value = value:lower()
- if fonts.otf.tables.languages[value] then
- tfmdata.language = value
- end
- end
-end
-
-function fonts.otf.features.script(tfmdata,value)
- if value then
- value = value:lower()
- if fonts.otf.tables.scripts[value] then
- tfmdata.script = value
- end
- end
-end
-
-function fonts.otf.features.mode(tfmdata,value)
- if value then
- tfmdata.mode = value:lower()
- end
-end
-
-function fonts.otf.features.strategy(tfmdata,value)
- if value then
- tfmdata.strategy = tonumber(value) or fonts.otf.strategy
- end
-end
-
-fonts.initializers.base.otf.language = fonts.otf.features.language
-fonts.initializers.base.otf.script = fonts.otf.features.script
-fonts.initializers.base.otf.mode = fonts.otf.features.mode
-fonts.initializers.base.otf.method = fonts.otf.features.mode
-fonts.initializers.base.otf.strategy = fonts.otf.features.strategy -- not needed
-
-fonts.initializers.node.otf.language = fonts.otf.features.language
-fonts.initializers.node.otf.script = fonts.otf.features.script
-fonts.initializers.node.otf.mode = fonts.otf.features.mode
-fonts.initializers.node.otf.method = fonts.otf.features.mode
-fonts.initializers.node.otf.strategy = fonts.otf.features.strategy
-
-do
-
- local tlig_list = {
- endash = "hyphen hyphen",
- emdash = "hyphen hyphen hyphen",
---~ quotedblleft = "quoteleft quoteleft",
---~ quotedblright = "quoteright quoteright",
---~ quotedblleft = "grave grave",
---~ quotedblright = "quotesingle quotesingle",
---~ quotedblbase = "comma comma",
- }
- local trep_list = {
---~ [0x0022] = 0x201D,
- [0x0027] = 0x2019,
---~ [0x0060] = 0x2018,
- }
-
- local tlig_feature = {
- features = { { scripts = { { script = "DFLT", langs = { "dflt" }, } }, tag = "tlig", comment = "added bij mkiv" }, },
- name = "ctx_tlig",
- subtables = { { name = "ctx_tlig_1" } },
- type = "gsub_ligature",
- flags = { },
- always = true
- }
- local trep_feature = {
- features = { { scripts = { { script = "DFLT", langs = { "dflt" }, } }, tag = "trep", comment = "added bij mkiv" }, },
- name = "ctx_trep",
- subtables = { { name = "ctx_trep_1" } },
- type = "gsub_single",
- flags = { },
- always = true
- }
-
- function fonts.otf.enhance.enrich(data,filename)
- for index, glyph in pairs(data.glyphs) do
- local l = tlig_list[glyph.name]
- if l then
- local o = glyph.lookups or { }
- o["ctx_tlig_1"] = { { "ligature", l, glyph.name } }
- glyph.lookups = o
- end
- local r = trep_list[glyph.unicode]
- if r then
- local replacement = data.map.map[r]
- if replacement then
- local o = glyph.lookups or { }
- o["ctx_trep_1"] = { { "substitution", data.glyphs[replacement].name } } ---
- glyph.lookups = o
- end
- end
- end
- data.gsub = data.gsub or { }
- logs.report("load otf","enhance: registering tlig feature")
- table.insert(data.gsub,1,table.fastcopy(tlig_feature))
- logs.report("load otf","enhance: registering trep feature")
- table.insert(data.gsub,1,table.fastcopy(trep_feature))
- end
-
- local prepare = fonts.otf.features.prepare.feature
- local process = fonts.otf.features.process.feature
-
- fonts.otf.tables.features['tlig'] = 'TeX Ligatures'
- fonts.otf.tables.features['trep'] = 'TeX Replacements'
-
- function fonts.initializers.node.otf.tlig(tfm,value) return prepare(tfm,'tlig',value) end
- function fonts.initializers.node.otf.trep(tfm,value) return prepare(tfm,'trep',value) end
-
- function fonts.methods.node.otf.tlig(head,font,attr) return process(head,font,attr,'tlig') end
- function fonts.methods.node.otf.trep(head,font,attr) return process(head,font,attr,'trep') end
-
- function fonts.initializers.base.otf.tlig(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'tlig',value) end
- function fonts.initializers.base.otf.trep(tfm,value) fonts.otf.features.prepare_base_substitutions(tfm,'trep',value) end
-
-end
-
--- we need this because fonts can be bugged
-
--- \definefontfeature[calt][language=nld,script=latn,mode=node,calt=yes,clig=yes,rlig=yes]
--- \definefontfeature[dflt][language=nld,script=latn,mode=node,calt=no, clig=yes,rlig=yes]
--- \definefontfeature[fixd][language=nld,script=latn,mode=node,calt=no, clig=yes,rlig=yes,ignoredrules={44,45,47}]
-
--- \starttext
-
--- {\type{dflt:}\font\test=ZapfinoExtraLTPro*dflt at 24pt \test \char57777\char57812 c/o} \endgraf
--- {\type{calt:}\font\test=ZapfinoExtraLTPro*calt at 24pt \test \char57777\char57812 c/o} \endgraf
--- {\type{fixd:}\font\test=ZapfinoExtraLTPro*fixd at 24pt \test \char57777\char57812 c/o} \endgraf
-
--- \stoptext
-
---~ table.insert(fonts.triggers,"ignoredrules")
-
---~ function fonts.initializers.node.otf.ignoredrules(tfmdata,value)
---~ if value then
---~ -- these tests must move !
---~ tfmdata.unique = tfmdata.unique or { }
---~ tfmdata.unique.ignoredrules = tfmdata.unique.ignoredrules or { }
---~ local ignored = tfmdata.unique.ignoredrules
---~ -- value is already ok now
---~ for s in string.gmatch(value:gsub("[{}]","")..",", "%s*(.-),") do
---~ ignored[tonumber(s)] = true
---~ end
---~ end
---~ end
-
-fonts.initializers.base.otf.equaldigits = fonts.initializers.common.equaldigits
-fonts.initializers.node.otf.equaldigits = fonts.initializers.common.equaldigits
-
-fonts.initializers.base.otf.lineheight = fonts.initializers.common.lineheight
-fonts.initializers.node.otf.lineheight = fonts.initializers.common.lineheight
-
-fonts.initializers.base.otf.compose = fonts.initializers.common.compose
-fonts.initializers.node.otf.compose = fonts.initializers.common.compose
-
--- temp hack, may change
-
-function fonts.initializers.base.otf.kern(tfmdata,value)
- fonts.otf.features.prepare_base_kerns(tfmdata,'kern',value)
-end
-
--- bonus function
-
-function fonts.otf.name_to_slot(name) -- todo: afm en tfm
- local tfmdata = fonts.tfm.id[font.current()]
- if tfmdata and tfmdata.shared then
- local otfdata = tfmdata.shared.otfdata
- if otfdata and otfdata.luatex then
- return otfdata.luatex.unicodes[name]
- end
- end
- return nil
-end
-
-function fonts.otf.char(n) -- todo: afm en tfm
- if type(n) == "string" then
- n = fonts.otf.name_to_slot(n)
- end
- if n then
- tex.sprint(tex.ctxcatcodes,string.format("\\char%s ",n))
- end
-end
-
---~ function fonts.otf.name_to_table(name)
---~ lcoal temp, result = { }
---~ local tfmdata = fonts.tfm.id[font.current()]
---~ if tfmdata and tfmdata.shared then
---~ local otfdata = tfmdata.shared.otfdata
---~ if otfdata and otfdata.luatex then
---~ for k,v in pairs(otfdata.glyphs) do
---~ if v.name:find(name) then
---~ temp[v.name] = v.unicode
---~ end
---~ end
---~ end
---~ end
---~ for k,v in pairs(table.sortedkeys(temp)) do
---~ result[#result+1] = { v, temp[v] }
---~ end
---~ return result
---~ end
-
--- Here we plug in some analyzing code
-
--- will move to font-tfm
-
-do
-
- local glyph = node.id('glyph')
- local glue = node.id('glue')
- local penalty = node.id('penalty')
-
- local fontdata = fonts.tfm.id
- local set_attribute = node.set_attribute
- local has_attribute = node.has_attribute
- local state = attributes.numbers['state'] or 100
-
- local fcs = fonts.color.set
- local fcr = fonts.color.reset
-
- -- in the future we will use language/script attributes instead of the
- -- font related value, but then we also need dynamic features which is
- -- somewhat slower; and .. we need a chain of them
-
- local type = type
-
- local initializers, methods = fonts.analyzers.initializers, fonts.analyzers.methods
-
- function fonts.initializers.node.otf.analyze(tfmdata,value,attr)
- if attr and attr > 0 then
- script, language = a_to_script[attr], a_to_language[attr]
- else
- script, language = tfmdata.script, tfmdata.language
- end
- local action = initializers[script]
- if action then
- if type(action) == "function" then
- return action(tfmdata,value)
- else
- local action = action[language]
- if action then
- return action(tfmdata,value)
- end
- end
- end
- return nil
- end
-
- function fonts.methods.node.otf.analyze(head,font,attr)
- local tfmdata = fontdata[font]
- local script, language
- if attr and attr > 0 then
- script, language = a_to_script[attr], a_to_language[attr]
- else
- script, language = tfmdata.script, tfmdata.language
- end
- local action = methods[script]
- if action then
- if type(action) == "function" then
- return action(head,font,attr)
- else
- action = action[language]
- if action then
- return action(head,font,attr)
- end
- end
- end
- return head, false
- end
-
- fonts.otf.features.register("analyze",true) -- we always analyze
- table.insert(fonts.triggers,"analyze") -- we need a proper function for doing this
-
- -- latin
-
- fonts.analyzers.methods.latn = fonts.analyzers.aux.setstate
-
- -- this info eventually will go into char-def
-
- local zwnj = 0x200C
- local zwj = 0x200D
-
- local isol = {
- [0x0621] = true, [zwnj] = true,
- }
-
- local isol_fina = {
- [0x0622] = true, [0x0623] = true, [0x0624] = true, [0x0625] = true, [0x0627] = true, [0x062F] = true,
- [0x0630] = true, [0x0631] = true, [0x0632] = true,
- [0x0648] = true,
- [0xFEF5] = true, [0xFEF7] = true, [0xFEF9] = true, [0xFEFB] = true,
- }
-
- local isol_fina_medi_init = {
- [0x0626] = true, [0x0628] = true, [0x0629] = true, [0x062A] = true, [0x062B] = true, [0x062C] = true, [0x062D] = true, [0x062E] = true,
- [0x0633] = true, [0x0634] = true, [0x0635] = true, [0x0636] = true, [0x0637] = true, [0x0638] = true, [0x0639] = true, [0x063A] = true,
- [0x0640] = true, -- tadwil
- [0x0641] = true, [0x0642] = true, [0x0643] = true, [0x0644] = true, [0x0645] = true, [0x0646] = true, [0x0647] = true, [0x0649] = true, [0x064A] = true,
- [0x067E] = true,
- [0x0686] = true, [zwj] = true,
- }
-
- local arab_warned = { }
-
- local function warning(current,what)
- local char = current.char
- if not arab_warned[char] then
- log.report("analyze",string.format("arab: character %s (0x%04X) has no %s class", char, char, what))
- arab_warned[char] = true
- end
- end
-
- function fonts.analyzers.methods.nocolor(head,font,attr)
- for n in node.traverse(head,glyph) do
- if not font or n.font == font then
- fcr(n)
- end
- end
- return head, true
- end
-
- function fonts.analyzers.methods.arab(head,font,attr) -- maybe make a special version with no trace
- local characters = fontdata[font].characters
- local first, last, current, done = nil, nil, head, false
- local trace = fonts.color.trace
- --~ local laststate = 0
- local function finish()
- if last then
- if first == last then
- if isol_fina_medi_init[first.char] or isol_fina[first.char] then
- set_attribute(first,state,4) -- isol
- if trace then fcs(first,"font:isol") end
- else
- warning(first,"isol")
- set_attribute(first,state,0) -- error
- if trace then fcr(first) end
- end
- else
- if isol_fina_medi_init[last.char] or isol_fina[last.char] then -- why isol here ?
- -- if laststate == 1 or laststate == 2 or laststate == 4 then
- set_attribute(last,state,3) -- fina
- if trace then fcs(last,"font:fina") end
- else
- warning(last,"fina")
- set_attribute(last,state,0) -- error
- if trace then fcr(last) end
- end
- end
- first, last = nil, nil
- elseif first then
- -- first and last are either both set so we never com here
- if isol_fina_medi_init[first.char] or isol_fina[first.char] then
- set_attribute(first,state,4) -- isol
- if trace then fcs(first,"font:isol") end
- else
- warning(first,"isol")
- set_attribute(first,state,0) -- error
- if trace then fcr(first) end
- end
- first = nil
- end
- --~ laststate = 0
- end
- while current do
- if current.id == glyph and current.subtype<256 and current.font == font then
- done = true
- local char = current.char
- local chardata = characters[char] -- some day we will make a characters.marks hash
- if not chardata then -- this is also more efficient since it's shared
- -- troubles
- -- elseif char == zwj then
- -- -- can probably be ignored, we could turn it into a kern or penalty
- -- elseif char == zwnj then
- -- -- acts like a space, we could turn it into a kern or penalty
- -- finish()
- elseif chardata.description.class == "mark" then
- set_attribute(current,state,5) -- mark
- if trace then fcs(current,"font:mark") end
- elseif isol[char] then
- finish()
- set_attribute(current,state,4) -- isol
- if trace then fcs(current,"font:isol") end
- first, last = nil, nil
- --~ laststate = 0
- elseif not first then
- if isol_fina_medi_init[char] then
- set_attribute(current,state,1) -- init
- if trace then fcs(current,"font:init") end
- first, last = first or current, current
- --~ laststate = 1
- elseif isol_fina[char] then
- set_attribute(current,state,4) -- isol
- if trace then fcs(current,"font:isol") end
- first, last = nil, nil
- --~ laststate = 0
- else -- no arab
- finish()
- end
- elseif isol_fina_medi_init[char] then
- first, last = first or current, current
- set_attribute(current,state,2) -- medi
- if trace then fcs(current,"font:medi") end
- --~ laststate = 2
- elseif isol_fina[char] then
- -- if not laststate == 1 then
- if not has_attribute(last,state,1) then
- -- tricky, we need to check what last may be !
- set_attribute(last,state,2) -- medi
- if trace then fcs(last,"font:medi") end
- end
- set_attribute(current,state,3) -- fina
- if trace then fcs(current,"font:fina") end
- first, last = nil, nil
- --~ laststate = 0
- elseif char >= 0x0600 and char <= 0x06FF then
- if trace then fcs(current,"font:rest") end
- finish()
- else --no
- finish()
- end
- else
- finish()
- end
- current = current.next
- end
- finish()
- return head, done
- end
-
- -- han (chinese) (unfinished)
-
- -- this info eventually will go into char-def
-
- -- in the future we will use language/script attributes instead of the
- -- font related value, but then we also need dynamic features which is
- -- somewhat slower; and .. we need a chain of them
-
- local type = type
-
- local opening_parenthesis_hw = table.tohash { -- half width
- 0x0028,
- 0x005B,
- 0x007B,
- 0x2018, -- ‘
- 0x201C, -- “
- }
-
- local opening_parenthesis_fw = table.tohash { -- full width
- 0x3008, -- 〈 Left book quote
- 0x300A, -- 《 Left double book quote
- 0x300C, -- 「 left quote
- 0x300E, -- 『 left double quote
- 0x3010, -- 【 left double book quote
- 0x3014, -- 〔 left book quote
- 0x3016, --〖 left double book quote
- 0x3018, -- left tortoise bracket
- 0x301A, -- left square bracket
- 0x301D, -- reverse double prime qm
- 0xFF08, -- ( left parenthesis
- 0xFF3B, -- [ left square brackets
- 0xFF5B, -- { left curve bracket
- 0xFF62, -- left corner bracket
- }
-
- local closing_parenthesis_hw = table.tohash { -- half width
- 0x0029,
- 0x005D,
- 0x007D,
- 0x2019, -- ’ right quote, right
- 0x201D, -- ” right double quote
- }
-
- local closing_parenthesis_fw = table.tohash { -- full width
- 0x3009, -- 〉 book quote
- 0x300B, -- 》 double book quote
- 0x300D, -- 」 right quote, right
- 0x300F, -- 』 right double quote
- 0x3011, -- 】 right double book quote
- 0x3015, -- 〕 right book quote
- 0x3017, -- 〗 right double book quote
- 0x3019, -- right tortoise bracket
- 0x301B, -- right square bracket
- 0x301E, -- double prime qm
- 0x301F, -- low double prime qm
- 0xFF09, -- ) right parenthesis
- 0xFF3D, -- ] right square brackets
- 0xFF5D, -- } right curve brackets
- 0xFF63, -- right corner bracket
- }
-
- local opening_vertical = table.tohash {
- 0xFE35, 0xFE37, 0xFE39, 0xFE3B, 0xFE3D, 0xFE3F, 0xFE41, 0xFE43, 0xFE47,
- }
-
- local closing_vertical = table.tohash {
- 0xFE36, 0xFE38, 0xFE3A, 0xFE3C, 0xFE3E, 0xFE40, 0xFE42, 0xFE44, 0xFE48,
- }
-
- local opening_punctuation_hw = table.tohash { -- half width
- }
-
- local opening_punctuation_fw = table.tohash {
- -- 0x2236, -- ∶
- -- 0xFF0C, -- ,
- }
-
- local closing_punctuation_hw = table.tohash { -- half width
- 0x0021, -- !
- 0x002C, -- ,
- 0x002E, -- .
- 0x003A, -- :
- 0x003B, -- ;
- 0x003F, -- ?
- 0xFF61, -- hw full stop
- }
-
- local closing_punctuation_fw = table.tohash { -- full width
- 0x3001, -- 、
- 0x3002, -- 。
- 0xFF01, -- !
- 0xFF0C, -- ,
- 0xFF0E, -- .
- 0xFF1A, -- :
- 0xFF1B, -- ;
- 0xFF1F, -- ?
- }
-
- local non_starter = table.tohash { -- japanese
- 0x3005, 0x3041, 0x3043, 0x3045, 0x3047,
- 0x3049, 0x3063, 0x3083, 0x3085, 0x3087,
- 0x308E, 0x3095, 0x3096, 0x309B, 0x309C,
- 0x309D, 0x309E, 0x30A0, 0x30A1, 0x30A3,
- 0x30A5, 0x30A7, 0x30A9, 0x30C3, 0x30E3,
- 0x30E5, 0x30E7, 0x30EE, 0x30F5, 0x30F6,
- 0x30FC, 0x30FD, 0x30FE, 0x31F0, 0x31F1,
- 0x30F2, 0x30F3, 0x30F4, 0x31F5, 0x31F6,
- 0x30F7, 0x30F8, 0x30F9, 0x31FA, 0x31FB,
- 0x30FC, 0x30FD, 0x30FE, 0x31FF,
- }
-
- -- the characters below are always appear in a double form, so there
- -- will be two Chinese ellipsis characters together that denote
- -- ellipsis marks and it is not allowed to break between them
-
- local hyphenation = table.tohash {
- 0x2026, -- … ellipsis
- 0x2014, -- — hyphen
- }
-
- local function is_han_character(char)
- return
- (char>=0x04E00 and char<=0x09FFF) or
- (char>=0x03400 and char<=0x04DFF) or
- (char>=0x20000 and char<=0x2A6DF) or
- (char>=0x0F900 and char<=0x0FAFF) or
- (char>=0x2F800 and char<=0x2FA1F)
- end
-
- --~ opening_parenthesis_hw / closing_parenthesis_hw
- --~ opening_parenthesis_fw / closing_parenthesis_fw
- --~ opening_punctuation_hw / closing_punctuation_hw
- --~ opening_punctuation_fw / closing_punctuation_fw
-
- --~ non_starter
- --~ hyphenation
-
- --~ opening_vertical / closing_vertical
-
- fonts.analyzers.methods.stretch_hang = true
-
- fonts.analyzers.methods.hang_data = {
- inter_char_stretch_factor = 2.00, -- we started with 0.5, then 1.0
- inter_char_half_factor = 0.50, -- normally there is no reason to change this
- inter_char_half_shrink_factor = 0.25, -- normally there is no reason to change this
- }
-
- local hang_data = fonts.analyzers.methods.hang_data
-
- local insert_after, insert_before, delete = node.insert_after, node.insert_before, nodes.delete
-
- local function nobreak_before(head,current)
- local p = current.prev
- if p then
- p = p.prev
- if p and p.id == penalty then
- p.penalty = 10000
- return head, current
- end
- end
- return insert_before(head,current,nodes.penalty(10000))
- end
-
- function fonts.analyzers.methods.hani(head,font,attr)
- -- maybe make a special version with no trace
- local characters = fontdata[font].characters
- local current, done, stretch, prevclass = head, false, 0, 0
- if fonts.analyzers.methods.stretch_hang then
- stretch = fontdata[font].parameters.quad
- end
- -- penalty before break
- local interspecialskip = - stretch * hang_data.inter_char_half_factor
- local interspecialshrink = stretch * hang_data.inter_char_half_shrink_factor
- local internormalstretch = stretch * hang_data.inter_char_stretch_factor
- local trace = fonts.color.trace
--- todo: check for first and last
- while current do
- if current.id == glyph and current.subtype<256 then
- if current.font == font then
- local char = current.char
- if false then
- -- don't ask -)
- elseif opening_punctuation_fw[char] or opening_parenthesis_fw[char] then
- if trace then fcs(current,"font:init") end
-if head ~= current then
- head, _ = insert_before(head,current,nodes.glue(interspecialskip,0,interspecialshrink))
-end
- head, current = insert_after(head,current,nodes.penalty(10000))
- head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0))
- prevclass, done = 1, true
- elseif closing_punctuation_fw[char] or closing_parenthesis_fw[char] then
- if trace then fcs(current,"font:fina") end
- if prevclass > 0 then
- head, current = nobreak_before(head,current)
- head, current = insert_after(head,current,nodes.penalty(10000))
- head, current = insert_after(head,current,nodes.glue(interspecialskip,0,interspecialshrink))
- head, current = insert_after(head,current,nodes.penalty(0))
- head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0))
- end
- prevclass, done = 2, true
- elseif opening_punctuation_hw[char] or opening_parenthesis_hw[char] then
- if trace then fcs(current,"font:init") end
- head, current = insert_after(head,current,nodes.penalty(10000))
- head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0))
- prevclass, done = 3, true
- elseif closing_punctuation_hw[char] or closing_parenthesis_hw[char] then
- if trace then fcs(current,"font:fina") end
- if prevclass > 0 then
- head, current = nobreak_before(head,current)
- head, current = insert_after(head,current,nodes.penalty(0))
- head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0))
- end
- prevclass, done = 4, true
- elseif hyphenation[char] then
- if trace then fcs(current,"font:medi") end
- if prevclass > 0 then
- head, current = nobreak_before(head,current)
- head, current = insert_after(head,current,nodes.penalty(0))
- head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0))
- end
- prevclass, done = 5, true
- elseif non_starter[char] then
- if trace then fcs(current,"font:isol") end
- head, current = insert_after(head,current,nodes.penalty(10000))
- head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0))
- prevclass, done = 6, true
- elseif is_han_character(char) then
- prevclass, done = 7, true
- head, current = insert_after(head,current,nodes.penalty(0))
- head, current = insert_after(head,current,nodes.glue(0,internormalstretch,0))
- end
- else
- prevclass = 0
- end
- elseif prevclass > 0 and current.id == glue and current.spec and current.spec.width > 0 then
- head, current = delete(head,current)
- end
- if current then
- current = current.next
- end
- end
- return head, done
- end
-
- fonts.analyzers.methods.hang = fonts.analyzers.methods.hani
-
-end
-
--- experimental and will probably change
-
-do
- local process = fonts.otf.features.process.feature
- local prepare = fonts.otf.features.prepare.feature
- function fonts.install_feature(type,...)
- if fonts[type] and fonts[type].install_feature then
- fonts[type].install_feature(...)
- end
- end
- function fonts.otf.install_feature(tag)
- fonts.methods.node.otf [tag] = function(head,font,attr) return process(head,font,attr,tag) end
- fonts.initializers.node.otf[tag] = function(tfm,value) return prepare(tfm,tag,value) end
- end
-end