LuaLaTeX 使用什麼演算法作為後備字體?

LuaLaTeX 使用什麼演算法作為後備字體?

背景 幾年來,我一直斷斷續續地忙著寫一本關於世界文字和語言的書。它涵蓋全部列出的 unicode 腳本,以及十幾個左右,其中還沒有 unicode 標準,我使用圖像而不是字體。

隨著 Noto 字體、unicode LuaLaTeX 和 l3 的成熟,我已經能夠根據寫作中的需要為所有腳本列印合理的範圍。除了東亞腳本之外,每個腳本我只有幾頁。我使用 Brill 作為主要字體,並添加了後備字體來覆蓋腳本的其餘部分。到目前為止,本書的頁數徘徊在 350 頁左右,我預期最終的頁數將達到 600 頁。為了覆蓋 unicode 點,字體需要提供 +-150,000 個字形。正如我之前提到的,並不是所有的程式碼點都在書中使用,我估計我只需要大約一半的程式碼點。顯然,可以理解的是,編譯速度是一個問題,所以我希望了解 luaotfload-fallback.lua 使用的演算法,試著看看是否可以縮短處理時間。我正在尋找優化編譯時間的策略,不僅針對我的文檔,而且針對一般情況。

我主要發現了三個方面的瓶頸:a) 字體 b) 圖像 c) 日誌記錄(通常是磁碟寫入)。影像 我將使用預處理器並優化所有影像並產生 pdf。我將使用 Golang 作為預處理器,如果需要的話它也可以進行標記。字體和日誌記錄的想法請見下文。

  1. 我有一個(瘋狂的)想法,即處理過程中節點所需的字形資訊可以透過本地伺服器獲取,因此某些任務可以外部化並並發運行。我正在考慮某種形式的優先權佇列,以便可以快速提供經常使用的程式碼點的數據,並且在第二次運行時將任何未使用的程式碼點從快取中取出。在這裡我將再次使用 Golang 和 sqlite3,因為一切都是本地的。我現在有一個 Lua 表,它根據配置文件將 unicode 點映射到字體。

  2. 所有日誌記錄也將發送到伺服器而不是寫入磁碟。也可以對 aux 檔案執行此操作。

  3. pdf的生成也需要時間,但我目前還不確定是否可以優化。目前的編譯速度約為每頁 1.3 秒 + 初速為 30-40 秒。

問題 有人可以跟我解釋一下 luaotfload-fallback.lua 中的演算法步驟嗎? LuaTeX 在建立文件時何時以及如何使用它?什麼時候需要字形資訊?歡迎任何想法。感謝您閱讀本文。

答案1

這根本沒有回答問題標題中的問題,但我認為這解決了問題正文中提出的問題(希望如此)。

間接回答

這是一個使用 LuaLaTeX 加載 231 種獨特字體並在 7.505 秒(平均)內列印 83 020 個獨特字元(103 頁)的解決方案。

首先,運行此腳本來下載所有字體:

#!/bin/sh
set -eu

mkdir fonts
cd fonts

git clone --depth 1 --no-checkout --filter=blob:none \
    https://github.com/notofonts/notofonts.github.io.git
cd notofonts.github.io
git sparse-checkout set --no-cone '!/*' '/fonts/**/hinted/ttf/*-Regular.ttf'
git checkout main
cd ..

git clone --depth 1 --no-checkout --filter=blob:none \
    https://github.com/notofonts/noto-cjk.git
cd noto-cjk
git sparse-checkout set --no-cone '!/*' '/Serif/SubsetOTF/**/*-Regular.otf'
git checkout main
cd ..

wget -O unifont-Regular.otf \
    https://unifoundry.com/pub/unifont/unifont-15.1.04/font-builds/unifont-15.1.04.otf
wget -O unifont_upper-Regular.otf \
    https://unifoundry.com/pub/unifont/unifont-15.1.04/font-builds/unifont_upper-15.1.04.otf

wget -O NotoEmoji-Regular.ttf \
    "$(curl 'https://fonts.googleapis.com/css2?family=Noto+Emoji' | grep -o 'https.*ttf')"

cd ..

然後,將以下內容放入all-characters.lua

-- Save some globals for speed
local ipairs = ipairs
local max = math.max
local new_node = node.new
local node_write = node.write
local pairs = pairs

-- Define some constants
local GLUE_ID = node.id("glue")
local GLYPH_ID = node.id("glyph")
local SIZE = tex.sp("10pt")

-- Get all the fonts
local fontpaths = dir.glob("**-Regular.*", "./fonts")

-- Sort the fonts such that the "preferred" fonts are last
table.sort(fontpaths, function(a, b)
    local a = file.nameonly(a):match("(.+)-Regular")
    local b = file.nameonly(b):match("(.+)-Regular")

    if a:match("Serif") and not b:match("Serif") then
        return false
    end
    if b:match("Serif") and not a:match("Serif") then
        return true
    end
    if a:match("unifont") and not b:match("unifont") then
        return true
    end
    if b:match("unifont") and not a:match("unifont") then
        return false
    end
    if #a == #b then
        return a > b
    end
    return #a > #b
end)


-- Create a mapping from codepoint to font id
local by_character = {}
local virtual_fonts = {}

for _, filename in ipairs(fontpaths) do
    local fontdata = fonts.definers.read {
        lookup = "file",
        name = filename,
        size = SIZE,
        features = {},
    }
    local id = font.define(fontdata)
    fonts.definers.register(fontdata, id)

    virtual_fonts[#virtual_fonts + 1] = { id = id }

    for codepoint, char in pairs(fontdata.characters) do
        if char.unicode == codepoint then
            by_character[codepoint] = {
                width = char.width,
                height = char.height,
                depth = char.depth,
                font = id,
                commands = {
                    { "slot", #virtual_fonts, codepoint }
                },
            }
        end
    end
end

local function print_all_chars()
    local count = 0

    tex.forcehmode()
    for codepoint, data in table.sortedpairs(by_character) do
        local glyph = new_node(GLYPH_ID)
        glyph.font = data.font
        glyph.char = codepoint

        local space = new_node(GLUE_ID)
        space.width = max(2 * SIZE - glyph.width, 0)
        glyph.next = space

        node_write(glyph)
        count = count + 1
    end
    tex.sprint("\\par Characters: " .. count)
    tex.sprint("\\par Fonts: " .. #virtual_fonts)
end


-- Make the virtual font
local id = font.define {
    name = "all-characters",
    parameters = {},
    characters = by_character,
    properties = {},
    type = "virtual",
    fonts = virtual_fonts,
}

local new_command
if ltx then
    new_command = function(name, func)
        local index = luatexbase.new_luafunction(name)
        lua.get_functions_table()[index] = func
        token.set_lua(name, index, "protected")
    end
elseif context then
    new_command = function(name, func)
        interfaces.implement {
            name = name,
            actions = func,
            public = true,
        }
    end
end

new_command("printallchars", print_all_chars)
new_command("allcharactersfont", function() font.current(id) end)

然後,您可以使用以下文件列印所有字元:

\documentclass{article}

\ExplSyntaxOn
\lua_load_module:n { all-characters }
\ExplSyntaxOn

\begin{document}
    \printallchars
\end{document}

ConTeXt 速度提高了 50%,平均為 4.849 秒:

\ctxloadluafile{all-characters}

\starttext
    \printallchars
\stoptext

更有用的是,這也定義了一個虛擬字體\allcharactersfont ,其中包含所有載入字體中的字元:

\documentclass{article}
\pagestyle{empty}

\ExplSyntaxOn
\lua_load_module:n { all-characters }
\ExplSyntaxOn

\begin{document}
    {\allcharactersfont
        A Ξ Ж س
        क ௵ ෴ ფ
        ጄ ᑠ ᘟ Ⅶ
        ∰ ⡿ だ 㬯

相關內容