背景 私はここ数年、世界の文字と言語に関する本を執筆しています。全てリストされているスクリプトは Unicode で、さらに Unicode 標準がまだ存在せず、フォントの代わりに画像を使用しているスクリプトも 12 個ほどあります。
Noto フォント、Unicode LuaLaTeX、および l3 の成熟により、執筆に必要なすべてのスクリプトについて妥当な範囲を印刷できるようになりました。東アジアのスクリプトは例外で、スクリプトごとに数ページしかありません。メイン フォントとして Brill を使用しており、残りのスクリプトをカバーするためにフォールバック フォントを追加しました。これまでのところ、この本は約 350 ページですが、最終的には 600 ページになる予定です。Unicode ポイントをカバーするには、フォントで +-150,000 グリフを提供する必要があります。前述のように、すべてのコード ポイントが本で使用されているわけではありませんが、私の見積もりではその半分程度で十分です。当然のことながら、コンパイル速度が問題となるため、luaotfload-fallback.lua で使用されるアルゴリズムを理解し、処理時間を改善できるかどうか試してみたいと思っています。自分のドキュメントだけでなく、一般的にコンパイル時間を最適化する戦略を検討しています。
ボトルネックは主に 3 つの領域 (a) フォント、b) 画像、c) ログ (一般的なディスク書き込み) で特定しました。画像については、プリプロセッサを使用してすべてを最適化し、PDF を生成します。プリプロセッサには Golang を使用します。これは、必要に応じてマーキングも実行できます。フォントとログのアイデアについては、以下を参照してください。
処理中にノードで必要なグリフ情報をローカル サーバー経由で取得し、一部のタスクを外部化して並行して実行できるようにするという (クレイジーな) アイデアがあります。頻繁に使用されるコードポイントのデータを高速に提供し、2 回目の実行で使用されないコードポイントをキャッシュから取り出すために、何らかの形式の優先キューを考えています。ここでも、すべてがローカルであるため、Golang と sqlite3 を使用します。現時点では、構成ファイルに基づいて Unicode ポイントをフォントにマップする Lua テーブルがあります。
すべてのログはディスクに書き込まれるのではなく、サーバーに送信されます。aux ファイルに対しても実行できます。
PDF の生成にも時間がかかりますが、現時点では最適化できるかどうかは未定です。現在のコンパイル速度は 1 ページあたり約 1.3 秒 + 初期 30 ~ 40 秒です。
質問 luaotfload-fallback.lua のアルゴリズムの手順を説明していただけますか? ドキュメントを作成するときに、LuaTeX はいつ、どのようにこれを使用しますか? どの時点でグリフ情報が必要になりますか? アイデアがあれば歓迎します。ここまで読んでいただき、ありがとうございます。
答え1
これは質問タイトルの質問にはまったく答えていませんが、質問本文で提示された問題には対処していると思います (うまくいけば)。
間接的な答え
これは、LuaLaTeX を使用して 231 個の固有フォントを読み込み、83,020 個の固有文字 (103 ページ) を 7.505 秒 (平均) で印刷するソリューションです。
まず、このスクリプトを実行してすべてのフォントをダウンロードします。
#!/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 は平均 4.849 秒で 50% 高速化しました。
\ctxloadluafile{all-characters}
\starttext
\printallchars
\stoptext
\allcharactersfont
さらに便利なことに、これは、読み込まれたすべてのフォントの文字を含む仮想フォントも定義します。
\documentclass{article}
\pagestyle{empty}
\ExplSyntaxOn
\lua_load_module:n { all-characters }
\ExplSyntaxOn
\begin{document}
{\allcharactersfont
A Ξ Ж س
क ௵ ෴ ფ
ጄ ᑠ ᘟ Ⅶ
∰ ⡿ だ 㬯
䷥