From 3447815896954281938500d49bedef715078a140 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Fri, 19 Feb 2016 22:41:23 +0100
Subject: [features,parsers] implement font fallbacks
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Building on the combination mechanism, this allows defining fallback
fonts of which all glyphs are pulled that aren’t currently part of the
base font. Example:

    \input luaotfload.sty
    \font \lm  = file:lmroman10-regular.otf:mode=base
    \font \cmu = file:cmunrm.otf:mode=base
    \font \lmu = "combo: 1->\fontid\lm; 2->\fontid\cmu,fallback"
    \lmu Eh bien, mon prince. Gênes et Lueques ne sont plus que des
         apanages, des поместья, de la famille Buonaparte.
    \bye

This allows setting Latin Modern text that contains Cyrillic letters.

Note that -- as with the other combinations -- only glyphs are
considered, no other properties of the fallback font. So besides the
occasional letter in a different script this functionality is probably
useless.
---
 src/luaotfload-features.lua | 56 ++++++++++++++++++++++++++++-----------------
 src/luaotfload-loaders.lua  |  1 +
 src/luaotfload-parsers.lua  |  9 ++++----
 3 files changed, 41 insertions(+), 25 deletions(-)

diff --git a/src/luaotfload-features.lua b/src/luaotfload-features.lua
index aeeaea3..4bcfbc3 100644
--- a/src/luaotfload-features.lua
+++ b/src/luaotfload-features.lua
@@ -100,9 +100,15 @@ local handle_combination = function (combo, spec)
         local fnt = fontidentifiers [id]
         if fnt then
             local chars = cur.chars
-            report ("both", 0, "load",
-                    " *> %.2d: include font %d at rank %d (%d items).",
-                    i, id, idx, (chars and #chars or 0))
+            if chars == true then
+                report ("both", 0, "load",
+                        " *> %.2d: fallback font %d at rank %d.",
+                        i, id, idx)
+            else
+                report ("both", 0, "load",
+                        " *> %.2d: include font %d at rank %d (%d items).",
+                        i, id, idx, (chars and #chars or 0))
+            end
             chain   [#chain + 1]   = { fnt, chars, idx = idx }
             fontids [#fontids + 1] = { id = id }
         else
@@ -143,8 +149,12 @@ local handle_combination = function (combo, spec)
         local src = fnt.characters
         local cnt = 0
 
-        local pickchr = function (uc)
+        local pickchr = function (uc, unavailable)
             local chr = src [uc]
+            if unavailable == true and basechar [uc] then
+                --- fallback mode: already known
+                return
+            end
             if chr then
                 chr.commands = { { "slot", i, uc } }
                 basechar [uc] = chr
@@ -152,23 +162,27 @@ local handle_combination = function (combo, spec)
             end
         end
 
-        for j = 1, #def do
-            local this = def [j]
-            if type (this) == "number" then
-                report ("both", 0, "load",
-                        " *> [%d][%d]: import codepoint U+%.4X",
-                        i, j, this)
-                pickchr (this)
-            elseif type (this) == "table" then
-                local lo, hi = unpack (this)
-                report ("both", 0, "load",
-                        " *> [%d][%d]: import codepoint range U+%.4X--U+%.4X",
-                        i, j, lo, hi)
-                for uc = lo, hi do pickchr (uc) end
-            else
-                report ("both", 0, "load",
-                        " *> item no. %d of combination definition \z
-                         %d not processable.", j, i)
+        if def == true then --> fallback; grab all currently unavailable
+            for uc, _chr in next, src do pickchr (uc, true) end
+        else --> grab only defined range
+            for j = 1, #def do
+                local this = def [j]
+                if type (this) == "number" then
+                    report ("both", 0, "load",
+                            " *> [%d][%d]: import codepoint U+%.4X",
+                            i, j, this)
+                    pickchr (this)
+                elseif type (this) == "table" then
+                    local lo, hi = unpack (this)
+                    report ("both", 0, "load",
+                            " *> [%d][%d]: import codepoint range U+%.4X--U+%.4X",
+                            i, j, lo, hi)
+                    for uc = lo, hi do pickchr (uc) end
+                else
+                    report ("both", 0, "load",
+                            " *> item no. %d of combination definition \z
+                             %d not processable.", j, i)
+                end
             end
         end
         report ("both", 0, "load",
diff --git a/src/luaotfload-loaders.lua b/src/luaotfload-loaders.lua
index 80ce41a..fb01821 100644
--- a/src/luaotfload-loaders.lua
+++ b/src/luaotfload-loaders.lua
@@ -123,6 +123,7 @@ do
       logreport ("both", 0, "loaders", "   > name %q",     result.name     or "<nil>")
       logreport ("both", 0, "loaders", "   > fontname %q", result.fontname or "<nil>")
       logreport ("both", 0, "loaders", "   > fullname %q", result.fullname or "<nil>")
+      logreport ("both", 0, "loaders", "   > type %s",     result.type     or "<nil>")
       return result
     end
   end
diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua
index 71e539d..b190c2c 100644
--- a/src/luaotfload-parsers.lua
+++ b/src/luaotfload-parsers.lua
@@ -591,11 +591,12 @@ local parenthesized     = function (p) return P"(" * ws * p * ws * P")" end
 
 local comboidxpat       = Cg(combouint, "idx")
 local comboidpat        = Cg(combouint, "id" )
-local combocharspat     = comboidpat * combodefsep * Cg(Ct(combochars^1), "chars")
+local combocharspat     = Cg(P"fallback" * Cc(true) + Ct(combochars^1), "chars")
+local comboidcharspat   = comboidpat * combodefsep * combocharspat
 
-local comboidx          = parenthesized (comboidxpat  ) + comboidxpat
-local comboid           = parenthesized (comboidpat   ) + comboidpat
-local comboidchars      = parenthesized (combocharspat) + combocharspat
+local comboidx          = parenthesized (comboidxpat    ) + comboidxpat
+local comboid           = parenthesized (comboidpat     ) + comboidpat
+local comboidchars      = parenthesized (comboidcharspat) + comboidcharspat
 
 local combodef1         = Ct(comboidx * combomapsep * comboid) --> no chars
 local combodef          = Ct(comboidx * combomapsep * comboidchars)
-- 
cgit v1.2.3