モジュール:Kanbun
表示
漢文訓読文を表示するためのモジュールです。
使用法
[編集]例:
{{#invoke:Kanbun|kanbun|
桃之夭夭タル\O灼灼タリ其ノ華\\
之ノ子于キ帰グ\O宜シカラン[二]其{ノ}室家ニ[一]\\\\
桃之ノ夭夭タル\O有リ[レ]蕡タル其ノ実\\
之ノ子于キ帰グ\O宜シカラン[二]其ノ家室ニ[一]\\\\
桃之夭夭タル\O其ノ葉蓁蓁タリ\\
之ノ子于キ帰グ\O宜シカラン[二]其ノ家人ニ[一]
}}出力:
桃之夭夭タル灼灼タリ其ノ華
之ノ子于キ帰グ宜㆓シカラン其ノ室家㆒ニ
桃之ノ夭夭タル有㆑リ蕡タル其ノ実
之ノ子于キ帰グ宜㆓シカラン其ノ家室㆒ニ
桃之夭夭タル其ノ葉蓁蓁タリ
之ノ子于キ帰グ宜㆓シカラン其ノ家人㆒ニ
之ノ子于キ帰グ宜㆓シカラン其ノ室家㆒ニ
桃之ノ夭夭タル有㆑リ蕡タル其ノ実
之ノ子于キ帰グ宜㆓シカラン其ノ家室㆒ニ
桃之夭夭タル其ノ葉蓁蓁タリ
之ノ子于キ帰グ宜㆓シカラン其ノ家人㆒ニ
送り仮名と読み仮名は{}で囲むか、漢字の直後に書きます。[]で囲んだ部分は返り点を表します。 入力中の改行は漢字の区切り部分であれば出力に影響しません。出力で改行を挿入する場合は、\\を使用します。
特別な記号
- \\:改行を挿入する。
- \O:空白を挿入する。
local p = {}
local kunten = {
["レ"] = "㆑", -- 返り点には U+3191からU+319Fを使う。
["一"] = "㆒",
["二"] = "㆓",
["三"] = "㆔",
["四"] = "㆕",
["上"] = "㆖",
["中"] = "㆗",
["下"] = "㆘",
["甲"] = "㆙",
["乙"] = "㆚",
["丙"] = "㆛",
["丁"] = "㆜",
["天"] = "㆝",
["地"] = "㆞",
["人"] = "㆟",
["一レ"] = "㆒㆑",
["上レ"] = "㆖㆑",
["甲レ"] = "㆙㆑",
["天レ"] = "㆝㆑"
}
local function normalize(input_text)
input_text = input_text:gsub("{", "{")
input_text = input_text:gsub("}", "}")
input_text = input_text:gsub("[", "[")
input_text = input_text:gsub("]", "]")
input_text = input_text:gsub("。", "。")
input_text = input_text:gsub("、", "、")
input_text = input_text:gsub("(", "(")
input_text = input_text:gsub(")", ")")
input_text = input_text:gsub("「", "「")
input_text = input_text:gsub("」", "」")
input_text = input_text:gsub("[\r\n]", "")
return input_text
end
local function parse(input)
local i = 1
local j = 1
local res
return function()
while j <= #input do
i = j
local byte1 = input:byte(j)
if byte1 == 0x20 or byte1 == 0xa then
j = j + 1
elseif byte1 == 0x5c then -- バックスラッシュ
local next_char = input:sub(j + 1, j + 1)
if next_char == "\\" then
res = input:sub(i, j + 1)
j = j + 2
return res
elseif next_char == "O" then
res = input:sub(i, j + 1)
j = j + 2
return res
elseif next_char == "R" then
local s, e = input:find("\\R%b{}%b{}", i)
res = input:sub(i,e+1)
j = e + 1
return res
elseif next_char == "B" then
local s, e = input:find("\\B%b{}%b{}", i)
res = input:sub(i,e)
j = e + 1
return res
else
local brace, pos = input:find("{.-}", j + 1)
if not pos then
res = input:sub(i, j + 1)
j = j + 2
return res
end
res = input:sub(i, pos)
j = pos + 1
return res
end
else
if input:sub(i, i + 2) == "「" then
j = i + 3
end
if input:sub(i, i + 2) == "『" then
j = i + 3
end
if byte1 >= 0xF0 then
j = j + 3
elseif byte1 >= 0xE0 then
j = j + 2
elseif byte1 >= 0xC0 then
j = j + 1
end
-- IVS
local byteo = input:byte(j + 1)
if byteo and byteo >= 0xF0 then
local byte2 = input:byte(j + 2) or 0
local byte3 = input:byte(j + 3) or 0
local byte4 = input:byte(j + 4) or 0
local codepoint = (byteo - 0xF0) * 0x40000 + (byte2 - 0x80) * 0x1000 + (byte3 - 0x80) * 0x40 + (byte4 - 0x80)
if codepoint >= 0xE0100 and codepoint <= 0xE01EF then
j = j + 4
end
end
local bytek = input:byte(j+1)
local nami = 0
while bytek and bytek >= 0xE0 and bytek <= 0xEF do
local second = input:byte(j + 2) or 0
local third = input:byte(j + 3) or 0
local codepoint = ((bytek % 16) * 4096) + ((second % 64) * 64) + (third % 64)
if codepoint >= 0x3041 and codepoint <= 0x30FF then
if nami == 0 then
input = input:sub(1,j) .. "{" .. input:sub(j+1)
j = j + 1
nami = 1
end
j = j + 3
bytek = input:byte(j+1)
if not (bytek and bytek >= 0xE0 and bytek <= 0xEF) then
if nami == 1 then
input = input:sub(1,j) .. "}" .. input:sub(j+1)
j = j + 1
nami = 0
end
end
else
if nami == 1 then
input = input:sub(1,j) .. "}" .. input:sub(j+1)
j = j + 1
nami = 0
end
break
end
end
local next_char = input:sub(j + 1, j + 1)
while next_char == "[" or next_char == "{" or next_char == "(" do
if next_char == "[" then
j = input:find("]", j, true)
elseif next_char == "{" then
j = input:find("}", j, true)
elseif next_char == "(" then
j = input:find(")", j, true)
end
next_char = input:sub(j + 1, j + 1)
end
if input:sub(j + 1, j + 6) == "。」" then
j = j + 6
elseif input:sub(j + 1, j + 6) == "。』" then
j = j + 6
elseif input:sub(j + 1, j + 3) == "。" then
j = j + 3
elseif input:sub(j + 1, j + 3) == "、" then
j = j + 3
elseif input:sub(j + 1, j + 3) == "」" then
j = j + 3
elseif input:sub(j + 1, j + 3) == "』" then
j = j + 3
elseif input:sub(j + 1, j + 3) == "㆐" then
j = j + 3
else
end
res = input:sub(i, j)
j = j + 1
return res
end
end
end
end
local col = -1
local shift = -1
local style = nil
function kanbun(input_text, colmax)
local mozi = {}
for char in parse(input_text) do
local kanzi = char:match("^(.-)[\\%[%{]") or char
kanzi = kanzi:gsub("。", "")
kanzi = kanzi:gsub("、", "")
kanzi = kanzi:gsub("「", "")
kanzi = kanzi:gsub("」", "")
kanzi = kanzi:gsub("『", "")
kanzi = kanzi:gsub("』", "")
kanzi = kanzi:gsub("㆐", "")
local okurigana = char:match("{(.-)}")
local kaeriten = char:match("%[(.-)%]")
local saidoku = char:match("%((.-)%)")
local symbol = char:match("\\(B)") or char:match("\\(R)") or char:match("\\(begin)") or char:match("\\(end)") or char:match("\\(.)")
if symbol == "O" then
if col == 0 then
mozi[#mozi+1] = '<span style="display: inline-block; position: relative; margin-left: 0.5em; margin-right: 0.5em; margin-top: 0em; margin-bottom: 1.9em;"></span>'
else
mozi[#mozi+1] = '<span style="display: inline-block; position: relative; margin-left: 0.5em; margin-right: 0.5em; margin-top: 0em; margin-bottom: 0.9em;"></span>'
end
col = col + 1
elseif symbol == "\\" then
mozi[#mozi+1] = "<br>"
shift = 0
col = 0
elseif symbol == "begin" then
local style = char:match("\\begin{(.-)}")
style = style:gsub("~", " ")
mozi[#mozi+1] = string.format('<span style="padding-right:0.5em; %s">', style)
elseif symbol == "end" then
mozi[#mozi+1] = "</span>"
elseif symbol == "R" then
local sen, ruby = char:match("\\R%{([^}]*)%}%{([^}]*)%}")
mozi[#mozi+1] = [[<span style="position:relative;display:inline-block;">]]
mozi[#mozi+1] = kanbun(sen, colmax)
local rubi = {}
for i = 1, #ruby do
if (i - 1) % 3 == 0 then
local hira = string.sub(ruby, i, i + 2)
table.insert(rubi, "<span>" .. hira .. "</span>")
end
end
mozi[#mozi+1] = [[<span style="position:absolute;right:0.5em;top:0;bottom:0;display:flex;flex-direction:column;justify-content:space-between;font-size:0.5em;writing-mode:horizontal-tb;margin-block:0.6em;margin-block-end:1em;">]] .. table.concat(rubi) .. [[</span>]]
mozi[#mozi+1] = [[</span>]]
elseif symbol == "B" then
local a1s, a1e = char:find("%b{}", 3)
local a2s, a2e = char:find("%b{}", a1e + 1)
local sen = char:sub(a1s + 1, a1e - 1)
local ban = char:sub(a2s + 1, a2e - 1)
mozi[#mozi+1] = [[<span style="padding-right: 0.5em; background: linear-gradient(to right, transparent 95%, black 95%, black 98%, transparent 98% ); box-decoration-break: clone; -webkit-box-decoration-break: clone;">]]
mozi[#mozi+1] = string.format('<span style="position:absolute;writing-mode: horizontal-tb;text-orientation: upright;font-size: 50%%;transform: translate(1.2em, -0.2em);">%s</span>', ban)
mozi[#mozi+1] = kanbun(sen, colmax)
mozi[#mozi+1] = "</span>"
else
-- 漢字
if not style then
mozi[#mozi+1] = string.format(
'<span style="display: inline-block; position: relative; margin-left: 0.5em; margin-right: 0.5em; bottom: -%sem; %s">%s',
shift, style, kanzi)
col = col + 1
else
mozi[#mozi+1] = string.format(
'<span style="display: inline-block; position: relative; margin-left: 0.5em; margin-right: 0.5em; bottom: -%sem;">%s',
shift, kanzi)
col = col + 1
end
-- 返り点
if kaeriten then
kaeriten = kunten[kaeriten] or kaeriten
if kaeriten == "㆒㆑" then
mozi[#mozi+1] = [[<span style="position: absolute; font-size: 50%; bottom: -0.6em; left: 0.25em;">㆒</span><span style="position: absolute; font-size: 50%; bottom: -0.95em; left: 0.25em;">㆑</span>]]
elseif kaeriten == "㆖㆑" then
mozi[#mozi+1] = [[<span style="position: absolute; font-size: 50%; bottom: -0.9em; left: 0.25em; transform: scaleY(0.6);">㆖</span><span style="position: absolute; font-size: 50%; bottom: -1.3em; left: 0.25em; transform: scaleY(0.6);">㆑</span>]]
elseif kaeriten == "㆙㆑" then
mozi[#mozi+1] = [[<span style="position: absolute; font-size: 50%; bottom: -0.9em; left: 0.25em; transform: scaleY(0.6);">㆙</span><span style="position: absolute; font-size: 50%; bottom: -1.3em; left: 0.25em; transform: scaleY(0.6);">㆑</span>]]
elseif kaeriten == "㆝㆑" then
mozi[#mozi+1] = [[<span style="position: absolute; font-size: 50%; bottom: -0.9em; left: 0.25em; transform: scaleY(0.6);">㆝</span><span style="position: absolute; font-size: 50%; bottom: -1.3em; left: 0.25em; transform: scaleY(0.6);">㆑</span>]]
elseif kaeriten == "ニ" then
mozi[#mozi+1] = [[<span style="position: absolute; font-size: 50%; bottom: -1em; left: 0.25em;color:red">ニ</span>]]
else
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: -1em; left: 0.25em;">%s</span>',
kaeriten)
end
end
-- 送り仮名
if okurigana then
-- ひらがなとカタカナの個数を計算
local hiraganumber = 0
local katakanumber = 0
local i = 1
while i <= #okurigana do
local byte = string.byte(okurigana, i)
local second = string.byte(okurigana, i + 1) or 0
local third = string.byte(okurigana, i + 2) or 0
local codepoint = ((byte % 16) * 4096) + ((second % 64) * 64) + (third % 64)
if codepoint >= 0x3041 and codepoint <= 0x309F then
hiraganumber = hiraganumber + 1
elseif codepoint >= 0x30A0 and codepoint <= 0x30FF then
katakanumber = katakanumber + 1
end
i = i + 3
end
-- 位置の調整
if hiraganumber == 0 and katakanumber == 5 then
bottom = "-1"
local first_line = okurigana:sub(1, 9)
local second_line = okurigana:sub(10)
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: %sem; right: -1.5em; white-space: nowrap;">%s</span>',
bottom, first_line)
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: %sem; right: -0.5em; white-space: nowrap;">%s</span>',
bottom, second_line)
shift = shift + 0.1
elseif hiraganumber == 0 and katakanumber == 1 then
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: -0.5em; right: -0.5em; white-space: nowrap;">%s</span>',
okurigana)
elseif hiraganumber == 0 and katakanumber == 2 then
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: -1em; right: -0.5em; white-space: nowrap;">%s</span>',
okurigana)
elseif hiraganumber == 0 and katakanumber == 3 then
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: -2em; right: -0.5em; white-space: nowrap;">%s</span>',
okurigana)
shift = shift + 0.1
elseif hiraganumber == 0 and katakanumber == 4 then
local first_line = okurigana:sub(1, 6)
local second_line = okurigana:sub(7)
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: -0.5em; right: -1.5em; white-space: nowrap;">%s</span>',
first_line)
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: -1.5em; right: -0.5em; white-space: nowrap;">%s</span>',
second_line)
elseif hiraganumber == 1 and katakanumber == 0 then
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: 0.5em; right: -0.5em; white-space: nowrap;">%s</span>',
okurigana)
elseif hiraganumber == 2 and katakanumber == 0 then
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: 0em; right: -0.5em; white-space: nowrap;">%s</span>',
okurigana)
elseif hiraganumber == 3 and katakanumber == 0 then
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: -0.5em; right: -0.5em; white-space: nowrap;">%s</span>',
okurigana)
elseif hiraganumber == 4 and katakanumber == 0 then
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: -1em; right: -0.5em; white-space: nowrap;">%s</span>',
okurigana)
elseif hiraganumber == 1 and katakanumber == 1 then
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: -0.5em; right: -0.5em; white-space: nowrap;">%s</span>',
okurigana)
elseif hiraganumber + katakanumber == 4 then
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; top: -0.5em; right: -0.5em; white-space: nowrap; transform: scaleY(0.85);">%s</span>',
okurigana)
elseif hiraganumber + katakanumber >= 5 then
shift = shift + 0.5 * 0.8 * (hiraganumber + katakanumber - 4.5)
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; top: -0.5em; right: -0.5em; white-space: nowrap; transform: scaleY(0.8);">%s</span>',
okurigana)
else
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: -1em; right: -0.5em; white-space: nowrap;">%s</span>',
okurigana)
end
end
if char:find("。」") then
mozi[#mozi+1] = [[<span style="position: absolute; bottom: -1em;">。</span><span style="position: absolute; bottom: -1.25em;">」</span>]]
elseif char:find("」") then
mozi[#mozi+1] = [[<span style="position: absolute; bottom: -1em;">」</span>]]
end
if char:find("。』") then
mozi[#mozi+1] = [[<span style="position: absolute; bottom: -1em;">。</span><span style="position: absolute; bottom: -1.25em;">』</span>]]
elseif char:find("』") then
mozi[#mozi+1] = [[<span style="position: absolute; bottom: -1em;">』</span>]]
end
if char:find("、") then
mozi[#mozi+1] = [[<span style="position: absolute; bottom: -1em;">、</span>]]
end
if char:find("。") then
mozi[#mozi+1] = [[<span style="position: absolute; bottom: -1em;">。</span>]]
end
if char:find("「") then
mozi[#mozi+1] = [[<span style="position: absolute; top: -1em;">「</span>]]
end
if char:find("『") then
mozi[#mozi+1] = [[<span style="position: absolute; top: -1em;">『</span>]]
end
if char:find("㆐") then
mozi[#mozi+1] = [[<span style="position: absolute; bottom: -0.8em; transform: scale(0.5, 0.7);">㆐</span>]]
end
if saidoku then
local hiraganumber = 0
local katakanumber = 0
local i = 1
while i <= #saidoku do
local byte = string.byte(saidoku, i)
local second = string.byte(saidoku, i + 1) or 0
local third = string.byte(saidoku, i + 2) or 0
local codepoint = ((byte % 16) * 4096) + ((second % 64) * 64) + (third % 64)
if codepoint >= 0x3041 and codepoint <= 0x309F then
hiraganumber = hiraganumber + 1
elseif codepoint >= 0x30A0 and codepoint <= 0x30FF then
katakanumber = katakanumber + 1
end
i = i + 3
end
local bottom = nil
local top = nil
if hiraganumber == 0 and katakanumber == 1 then
bottom = "-0.5"
elseif hiraganumber == 0 and katakanumber == 2 then
bottom = "-1"
elseif hiraganumber == 0 and katakanumber == 3 then
bottom = "-2"
elseif hiraganumber == 0 and katakanumber == 4 then
bottom = "-2.5"
elseif hiraganumber == 1 and katakanumber == 0 then
bottom = "0.5"
elseif hiraganumber == 2 and katakanumber == 0 then
bottom = "0"
elseif hiraganumber == 3 and katakanumber == 0 then
bottom = "-0.5"
elseif hiraganumber == 4 and katakanumber == 0 then
bottom = "-1"
elseif hiraganumber == 1 and katakanumber == 1 then
bottom = "-0.5"
elseif hiraganumber + katakanumber >= 5 then
top = "0"
shift = shift + 0.5 * (hiraganumber + katakanumber - 4)
else
bottom = "-1"
end
if bottom then
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; bottom: %sem; left: -0.5em; white-space: nowrap;">%s</span>',
bottom, saidoku)
else
mozi[#mozi+1] = string.format(
'<span style="position: absolute; font-size: 50%%; top: %sem; left: -0.5em; white-space: nowrap;">%s</span>',
top, saidoku)
end
end
mozi[#mozi+1] = "</span>"
if col < colmax then
mozi[#mozi+1] = '<span style="display: inline-block; position: relative; margin-left: 0.5em; margin-right: 0.5em; margin-top: 0em; margin-bottom: 0.9em;"></span>'
end
end
if col >= colmax then
mozi[#mozi+1] = "<br>"
shift = 0
col = 0
end
end
return table.concat(mozi)
end
function p.kanbun(frame)
local input_text = frame.args[1]
local colmax = tonumber(frame.args.col) or 99
input_text = normalize(input_text)
local syuturyoku = [[<div style="writing-mode: vertical-rl; font-size: 150%; margin-top:0.6em; margin-bottom:0.6em; white-space: nowrap; font-family: 'Noto Serif CJK JP', serif">]]
if col == -1 then
col = 0
end
if shift == -1 then
shift = 0
end
if style == nil then
style = ""
end
syuturyoku = syuturyoku .. kanbun(input_text, colmax)
syuturyoku = syuturyoku .. "</div>"
return syuturyoku
end
return p