背景 幾年來,我一直斷斷續續地忙著寫一本關於世界文字和語言的書。它涵蓋全部列出的 unicode 腳本,以及十幾個左右,其中還沒有 unicode 標準,我使用圖像而不是字體。
隨著 Noto 字體、unicode LuaLaTeX 和 l3 的成熟,我已經能夠根據寫作中的需要為所有腳本列印合理的範圍。除了東亞腳本之外,每個腳本我只有幾頁。我使用 Brill 作為主要字體,並添加了後備字體來覆蓋腳本的其餘部分。到目前為止,本書的頁數徘徊在 350 頁左右,我預期最終的頁數將達到 600 頁。為了覆蓋 unicode 點,字體需要提供 +-150,000 個字形。正如我之前提到的,並不是所有的程式碼點都在書中使用,我估計我只需要大約一半的程式碼點。顯然,可以理解的是,編譯速度是一個問題,所以我希望了解 luaotfload-fallback.lua 使用的演算法,試著看看是否可以縮短處理時間。我正在尋找優化編譯時間的策略,不僅針對我的文檔,而且針對一般情況。
我主要發現了三個方面的瓶頸:a) 字體 b) 圖像 c) 日誌記錄(通常是磁碟寫入)。影像 我將使用預處理器並優化所有影像並產生 pdf。我將使用 Golang 作為預處理器,如果需要的話它也可以進行標記。字體和日誌記錄的想法請見下文。
我有一個(瘋狂的)想法,即處理過程中節點所需的字形資訊可以透過本地伺服器獲取,因此某些任務可以外部化並並發運行。我正在考慮某種形式的優先權佇列,以便可以快速提供經常使用的程式碼點的數據,並且在第二次運行時將任何未使用的程式碼點從快取中取出。在這裡我將再次使用 Golang 和 sqlite3,因為一切都是本地的。我現在有一個 Lua 表,它根據配置文件將 unicode 點映射到字體。
所有日誌記錄也將發送到伺服器而不是寫入磁碟。也可以對 aux 檔案執行此操作。
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 Ξ Ж س
क ௵ ෴ ფ
ጄ ᑠ ᘟ Ⅶ
∰ ⡿ だ 㬯
䷥