From ff3d8cdee7284dec8be1092a253ba6bca6f2a93b Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Mon, 17 Jan 2022 20:58:06 +0000 Subject: kanbun (17jan22) git-svn-id: svn://tug.org/texlive/trunk@61636 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/doc/latex/kanbun/README.md | 24 ++ .../texmf-dist/doc/latex/kanbun/kanbun-example.pdf | Bin 0 -> 45616 bytes .../texmf-dist/doc/latex/kanbun/kanbun-example.tex | 44 +++ Master/texmf-dist/doc/latex/kanbun/kanbun.lua | 224 +++++++++++++++ Master/texmf-dist/doc/latex/kanbun/kanbun.pdf | Bin 0 -> 376249 bytes Master/texmf-dist/doc/latex/kanbun/kanbun.tex | 301 +++++++++++++++++++ Master/texmf-dist/tex/latex/kanbun/kanbun.sty | 317 +++++++++++++++++++++ Master/tlpkg/bin/tlpkg-ctan-check | 2 +- Master/tlpkg/tlpsrc/collection-langjapanese.tlpsrc | 1 + Master/tlpkg/tlpsrc/kanbun.tlpsrc | 0 10 files changed, 912 insertions(+), 1 deletion(-) create mode 100644 Master/texmf-dist/doc/latex/kanbun/README.md create mode 100644 Master/texmf-dist/doc/latex/kanbun/kanbun-example.pdf create mode 100644 Master/texmf-dist/doc/latex/kanbun/kanbun-example.tex create mode 100644 Master/texmf-dist/doc/latex/kanbun/kanbun.lua create mode 100644 Master/texmf-dist/doc/latex/kanbun/kanbun.pdf create mode 100644 Master/texmf-dist/doc/latex/kanbun/kanbun.tex create mode 100644 Master/texmf-dist/tex/latex/kanbun/kanbun.sty create mode 100644 Master/tlpkg/tlpsrc/kanbun.tlpsrc diff --git a/Master/texmf-dist/doc/latex/kanbun/README.md b/Master/texmf-dist/doc/latex/kanbun/README.md new file mode 100644 index 00000000000..3b6e755d2df --- /dev/null +++ b/Master/texmf-dist/doc/latex/kanbun/README.md @@ -0,0 +1,24 @@ +# The `kanbun` package + +The `kanbun` package, like other *kanbun-kundoku* (漢文訓読) LaTeX packages (such as `gckanbun`), allows users to manually input macros for elements in a *kanbun-kundoku* paragraph. + +More importantly, it accepts plain text input in the “*kanbun* annotation” form when used with LuaLaTeX, which allows typesetting *kanbun-kundoku* paragraphs efficiently. + +## Licence + +Copyright 2022 Yuanhao Chen + +This work may be distributed and/or modified under the +conditions of the LaTeX Project Public License, either version 1.3 +of this license or (at your option) any later version. +The latest version of this license is in + +and version 1.3 or later is part of all distributions of LaTeX +version 2005/12/01 or later. + +This work has the LPPL maintenance status `maintained'. + +The Current Maintainer of this work is Yuanhao Chen. + +This work consists of the files `kanbun.sty`, `kanbun.lua`, +`kanbun-example.tex` and `kanbun.tex`. diff --git a/Master/texmf-dist/doc/latex/kanbun/kanbun-example.pdf b/Master/texmf-dist/doc/latex/kanbun/kanbun-example.pdf new file mode 100644 index 00000000000..f437066ba93 Binary files /dev/null and b/Master/texmf-dist/doc/latex/kanbun/kanbun-example.pdf differ diff --git a/Master/texmf-dist/doc/latex/kanbun/kanbun-example.tex b/Master/texmf-dist/doc/latex/kanbun/kanbun-example.tex new file mode 100644 index 00000000000..0ae963112fc --- /dev/null +++ b/Master/texmf-dist/doc/latex/kanbun/kanbun-example.tex @@ -0,0 +1,44 @@ +%% Copyright 2022 Yuanhao Chen +% +% This work may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3 +% of this license or (at your option) any later version. +% The latest version of this license is in +% http://www.latex-project.org/lppl.txt +% and version 1.3 or later is part of all distributions of LaTeX +% version 2005/12/01 or later. +% +% This work has the LPPL maintenance status `maintained'. +% +% The Current Maintainer of this work is Yuanhao Chen. +% +% This work consists of the files kanbun.sty, kanbun.lua, +% kanbun-example.tex and kanbun.tex. +% +% example text from https://phesoca.com/kanbun-html/ +\documentclass{ltjtarticle} + +\usepackage[match]{luatexja-fontspec} +\setmainjfont{Kozuka Mincho Pr6N} + +\newlength\myzw +\setlength\myzw{2.25\zw} +\usepackage[kumi=beta, unit=\myzw, yokoaki=3.5]{kanbun} + +\usepackage[margin=0pt]{geometry} + +\usepackage{xcolor} + +\Kanbun +初メテ,帝ノ母劉貴人ハ賜(たま)ハリ[レ]死ヲ,太祖ハ告ゲ[レ]帝ニ曰ハク「昔漢武帝ハ將‹し›«テ»ニ[レ]立タセント[二]其ノ子ヲ[一]而殺シ[二]其ノ母ヲ[一],不[下]令(し)メ[三]婦人ヲ後ロニ與(あづか)ラ[二]國政ニ[一],使(し)メ[中]外家ヲ為(な)サ[上レ]亂ヲ。汝當ニ‹べ›«ク»[二]繼統ス[一],故ニ吾レ遠クシテ同ズルコト[二]漢武ト[一],為(た)リ[二]長久之計[一]。」帝素ヨリ純孝ナリ,哀シク泣キ不[レ]能ハ[二]自ラ勝ツコト[一],太祖怒ル[レ]之レヲ。帝ハ還リ[レ]宮ニ,哀シク不[二]自ラ止マ[一],日夜ニシテ號泣ス。太祖知リテ而又タ召ス[レ]之レヲ。帝欲シ[レ]入ラント,左右曰ハク「孝子事ヘバ[レ]父ニ,小杖則チ受ケ,大杖避ク[レ]之レヲ。今陛下ノ怒ルコト盛ンナリ,入レバ或イハ不測ナリ,陷ラス[二]帝ヲ於不義ニ[一]。不[レ]如カ[二]且(しばら)ク出ヅルニ[一],待チテ[二]怒リノ解クルヲ[一]而進ムコト,不ル[レ]晚カラ也。」帝ハ懼レ,從ヒ[レ]之レニ,乃チ游行シ逃グ[二]於外ニ[一]。 +\EndKanbun + +\begin{document} +\color[HTML]{e0e0e0} +\pagestyle{empty} +\vspace*{\fill} +\begin{minipage}{0.5\pageheight} + \printkanbun +\end{minipage} +\vspace*{\fill} +\end{document} \ No newline at end of file diff --git a/Master/texmf-dist/doc/latex/kanbun/kanbun.lua b/Master/texmf-dist/doc/latex/kanbun/kanbun.lua new file mode 100644 index 00000000000..9f8f01aab39 --- /dev/null +++ b/Master/texmf-dist/doc/latex/kanbun/kanbun.lua @@ -0,0 +1,224 @@ +function directtex(str) + coroutine.yield(str) +end + +function to_TeX_box(str) + directtex("\\ExplSyntaxOn\\newbox\\kanbun_lua_box\\sbox\\kanbun_lua_box{"..str.."}\\ExplSyntaxOff") + return tex.getbox('kanbun_lua_box') +end + +function has_value (tab, val) + for index, value in ipairs(tab) do + if value == val then + return true + end + end + return false +end + +function main_loop() + brackets = {} + -- + brackets["furigana"] = {"(", ")"} + brackets["okurigana"] = {"{","}"} -- allow user to omit + brackets["kaeriten"] = {"[","]"} + brackets["furigana4saidokumoji"] = {"‹","›"} + brackets["okurigana4saidokumoji"] = {"«","»"} + -- + -- other_brackets["punctuation"] = {"⦉","⦊"} + -- other_brackets["kanji"] = {"⌊","⌋"} + -- other_brackets["multikanji"] = {"‘","’"} + -- other_brackets["unit"] = {"“","”"} + -- + left_brackets = {} + right_brackets = {} + for k,v in pairs(brackets) do + table.insert(left_brackets, v[1]) + table.insert(right_brackets, v[2]) + end + + -- punctuation_str = "〻―・、,。…「」『』" + punctuation_str = "㆐〻―—・、,。…「」『』!?:" + left_punctuation_str = "「『" + + lines_chars_table = {} + tex_kana_bool = token.create("g_kana_bool") + tex_true_bool = token.create("c_true_bool") + for i,l in ipairs(verb_table) do + split_line = {} + for c in l:gmatch(utf8.charpattern) do + table.insert(split_line, c) + end + + -- some unknown bug (in \matchkana ?) solved by changing the last entry to an empty string if not ascii + for i,c in ipairs(split_line) do + last_index = i + end + if utf8.codepoint(split_line[last_index] or " ") < 128 then + split_line[last_index+1]="" + else + split_line[last_index]="" + end + + last_bracket_index = 0 + for j,c in ipairs(split_line) do + if has_value(left_brackets, c) then + last_bracket_index = j + end + if last_bracket_index == 0 then + directtex("\\matchkana{"..c.."}") + if tex_kana_bool.mode == tex_true_bool.mode then + split_line[j] = brackets["okurigana"][1]..c..brackets["okurigana"][2] + else + split_line[j] = "“⌊"..c.."⌋”" + end + end + if has_value(right_brackets, c) then + last_bracket_index = 0 + end + end + table.insert(lines_chars_table, split_line) + end + + annotated_lines_table = {} + for i,l in ipairs(lines_chars_table) do + line = table.concat(l, "") + + line = string.gsub(line, brackets["okurigana"][2]..brackets["okurigana"][1], "") + for k,v in pairs(brackets) do + line = string.gsub(line, "”(%"..v[1]..")", "%1") + line = string.gsub(line, "(%"..v[2]..")(“)", "%1”%2") + end + for c in line:gmatch(utf8.charpattern) do + last = c + end + if has_value(right_brackets, last) then + str = str .. '”' + end + line = string.gsub(line, "⌊‘⌋”", "‘") + line = string.gsub(line, "“⌊’⌋", "’") + for p in punctuation_str:gmatch(utf8.charpattern) do + line = string.gsub(line, "”“⌊("..p..")⌋", "⦉%1⦊") + end + line = string.gsub(line, "⦊⦉", "") + + -- reverse the makeshift bug fix to \matchkana + line = string.gsub(line, "“⌊⌋”", "") + + -- process annotated text + tmp_number_of_multikanji_braces = {utf8.char(61442), utf8.char(61443)} + -- process line into units + split_line = {} + for c in line:gmatch(utf8.charpattern) do + table.insert(split_line, c) + end + units = {} + unit_content = {} + last_bracket_index = 0 + for j,c in ipairs(split_line) do + if c == "”" then + last_bracket_index = last_bracket_index - 1 + end + if last_bracket_index < 1 then + table.insert(units, table.concat(unit_content, "")) + unit_content = {} + else + table.insert(unit_content, c) + end + if c == "“" then + last_bracket_index = last_bracket_index + 1 + end + end + -- account for multikanji + for j,u in ipairs(units) do + split_unit = {} + for c in u:gmatch(utf8.charpattern) do + table.insert(split_unit, c) + end + number_of_multikanji = 0 + local last_k + for k,c in ipairs(split_unit) do + if c == "“" then + number_of_multikanji = number_of_multikanji + 1 + last_k = k + end + end + for m,c in ipairs(split_unit) do + if m == last_k then + split_unit[m] = "“"..tmp_number_of_multikanji_braces[1]..number_of_multikanji..tmp_number_of_multikanji_braces[2] + end + end + units[j] = table.concat(split_unit, "") + if number_of_multikanji > 0 then + units[j] = string.gsub(units[j], "“(.-)”’(.*)", "“%1%2”") + units[j] = string.gsub(units[j], "‘", "") + tmp_unit = units[j] + table.remove(units, j) + for new_unit in tmp_unit:gmatch"“(.-)”" do + table.insert(units, j, new_unit) + j = j + 1 + end + end + end + for j,u in ipairs(units) do + if u == "" then + table.remove(units, j) + end + end + next_left_punct_ = "" + for j,u in ipairs(units) do + right_okuri_ = u:match("%{(.-)%}") or "" + kanji_ = u:match("⌊(.-)⌋") or "" + right_furi_ = u:match("%((.-)%)") or "" + left_furi_ = u:match("‹(.-)›") or "" + left_okuri_ = u:match("«(.-)»") or "" + punct_ = u:match("⦉(.-)⦊") or "" + -- + -- punct_ = punct_:gsub("―", "\\tateten") + -- punct_ = punct_:gsub("—", "\\tateten") + -- punct_ = punct_:gsub("〻", "\\ninojiten") + -- + kaeriten_ = u:match("%[(.-)%]") or "" + multikanji_ = u:match(tmp_number_of_multikanji_braces[1].."(.-)"..tmp_number_of_multikanji_braces[2]) or 0 + if punctuation_str:match(kanji_) then + punct_ = kanji_ + kanji_ = "" + end + left_punct_ = next_left_punct_ + next_left_punct_ = "" + for p_ in punct_:gmatch(utf8.charpattern) do + if left_punctuation_str:match(p_) then + next_left_punct_ = next_left_punct_ .. p_ + end + end + for p_ in left_punctuation_str:gmatch(utf8.charpattern) do + punct_ = string.gsub(punct_, p_, "") + end + if u:match(tmp_number_of_multikanji_braces[1]) then + multiruby_raise_by_ = 0 + for trace_back_index = 1, multikanji_-1 do + multiruby_raise_by_ = multiruby_raise_by_ + to_TeX_box(units[j - trace_back_index]).width/(kanbunzwtosp) + end + units[j - multikanji_ + 1] = units[j - multikanji_ + 1]:gsub("\\kanjiunit", "\\hbox{\\kanjiunit") + units[j] = "\\kanjiunit{\\multifuriokuri["..multiruby_raise_by_.."]{"..right_furi_.."}{"..right_okuri_.."}}{"..left_punct_.."}{"..kanji_.."}{"..punct_.."}{"..kaeriten_.."}{\\multifuriokuri["..multiruby_raise_by_.."]{"..left_furi_.."}{"..left_okuri_.."}}}" + else + if kanji_ == "" then + units[j] = "" + else + units[j] = "\\kanjiunit{\\furiokuri{"..right_furi_.."}{"..right_okuri_.."}}{"..left_punct_.."}{"..kanji_.."}{"..punct_.."}{"..kaeriten_.."}{\\furiokuri{"..left_furi_.."}{"..left_okuri_.."}}" + end + end + end + line = table.concat(units, "") + + table.insert(annotated_lines_table, line) + end + + -- ouput + output = "{\\kanbunfont"..table.concat(annotated_lines_table, "\\par").."\\par}" + directtex("\\def\\printkanbun{"..output.."}") + directtex("\\def\\printkanbuncode{\\directlua{print('')print(output)}}") + + -- end loop in TeX + directtex("\\continuefalse") +end diff --git a/Master/texmf-dist/doc/latex/kanbun/kanbun.pdf b/Master/texmf-dist/doc/latex/kanbun/kanbun.pdf new file mode 100644 index 00000000000..a3b0cbdd30f Binary files /dev/null and b/Master/texmf-dist/doc/latex/kanbun/kanbun.pdf differ diff --git a/Master/texmf-dist/doc/latex/kanbun/kanbun.tex b/Master/texmf-dist/doc/latex/kanbun/kanbun.tex new file mode 100644 index 00000000000..02e4c0e9187 --- /dev/null +++ b/Master/texmf-dist/doc/latex/kanbun/kanbun.tex @@ -0,0 +1,301 @@ +%% Copyright 2022 Yuanhao Chen +% +% This work may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3 +% of this license or (at your option) any later version. +% The latest version of this license is in +% http://www.latex-project.org/lppl.txt +% and version 1.3 or later is part of all distributions of LaTeX +% version 2005/12/01 or later. +% +% This work has the LPPL maintenance status `maintained'. +% +% The Current Maintainer of this work is Yuanhao Chen. +% +% This work consists of the files kanbun.sty, kanbun.lua, +% kanbun-example.tex and kanbun.tex. +% +\documentclass[12pt]{ltxdockit} + +\AfterTOCHead[toc]{\sffamily} +\makeatletter + \def\@seccntformat#1{\protect\makebox[0pt][r]{\csname the#1\endcsname\hspace{\marglistsep}}} +\makeatother + +\usepackage{shortvrb} +\MakeShortVerb{|} + +\lstnewenvironment{example}[1][] + {\lstset{ + basicstyle=\ttfamily, + frame=single, + columns=flexible, + language=[LaTeX]TeX, + breaklines=true, + postbreak=\mbox{\textcolor{spot}{$\hookrightarrow$}\space}, + % morekeywords={drawuntpoint,untpoint,linkuntpoints}, + % escapeinside={<@}{@>}, + }} + {} + +\usepackage{luatexja} + \ltjdefcharrange{10}{"2E3A} + \ltjdefcharrange{11}{"2039-"203A} + \ltjsetparameter{jacharrange={-1, +2, +3, -4, -5, +6, +7, -8, +9, +10, -11}} +\usepackage[match]{luatexja-fontspec} +\defaultfontfeatures{Numbers=OldStyle, Scale=MatchLowercase} +\defaultjfontfeatures{Scale=MatchLowercase} +\setmainjfont{Kozuka Mincho Pr6N} +\setsansjfont{Sarasa Mono J} +\setmonojfont[YokoFeatures={JFM=prop}]{Sarasa Mono J} +\setmainfont{Minion Pro} +\setsansfont{Myriad Pro} +\setmonofont{Sarasa Mono J} +\newfontfamily{\kanbunfamily}{Kozuka Mincho Pr6N} + +\usepackage{luatexja-ruby} + \ltjsetruby{fontcmd=\addjfontfeature{RawFeature={+ruby}}} + +\usepackage{kanbun} +\newcommand{\printkanbunblock}[2]{ + { + \parindent=0pt + \vspace*{1\zw} + \hfill + \vbox{ + \hsize=#1\zw + { + \tate + \addjfontfeature{Scale=1} + #2 + } + } + \hfill + \vspace*{1\zw} + } +} + +\usepackage{bxtexlogo} +\usepackage{realscripts} +\usepackage{microtype} + +\usepackage{wallpaper} + +\Kanbun +以テ[二]羅(ら)蝶(てふ)ヲ[一]作ルガ[二]漢文訓読ヲ[一]用(ため)ノ包(ぱつけーぢ) +\EndKanbun +\title{The \sty{kanbun} package} +\subtitle{\normalsize\normalfont\addjfontfeature{Scale=1}\printkanbun} +\author{Yuanhao Chen (\ruby[rubysmash=true]{陳|元|鎬}{ちん|げん|こう})} +\date{\scshape 15 january 2022, v1.0} + + +\begin{document} + +\ThisCenterWallPaper{1}{kanbun-example.pdf} +\ExplSyntaxOn +\tl_set:Nn \kanbun_kumi { beta } +\ExplSyntaxOff +\maketitle +\tableofcontents + + +\section{Introduction} +The \sty{kanbun} package, like other \emph{kanbun-kundoku} (漢文訓読) \LaTeX{} packages (such as \sty{gckanbun}), allows users to manually input macros for elements in a \emph{kanbun-kundoku} paragraph. + +More importantly, it accepts plain text input in the ``\emph{kanbun} annotation'' form when used with \LuaLaTeX, which allows typesetting \emph{kanbun-kundoku} paragraphs efficiently\footnote{The idea comes from \href{https://phesoca.com/kanbun-html/}{漢文\textsc{html}} by UntPhesoca, which is a JavaScript and \textsc{css} implementation.}. + + +\section{Basic example with \LuaLaTeX} +As seen in the following example, typesetting a \emph{kanbun-kundoku} paragraph with the \sty{kanbun} package requires only light annotations --- it automatically transforms the annotated plain text into \LaTeX{} macros through Lua, rather than having users type in macros themselves. + +\ExplSyntaxOn +\tl_set:Nn \kanbun_tateaki { 1 } +\tl_set:Nn \kanbun_kumi { aki } +\ExplSyntaxOff +\Kanbun +月落チ烏啼キテ霜満ツ[レ]天ニ, +江楓漁火対ス[二]愁眠ニ[一]。 +姑(こ)蘇(そ)城外ノ寒山寺, +夜半ノ鐘声到ル[二]客船ニ[一]。 +\EndKanbun + +\penalty0 +\printkanbunblock{13}{\printkanbun} +\vspace{-2\zw} + +\begin{example} +\documentclass{ltjtarticle} +\usepackage[kumi=aki, tateaki=1]{kanbun} +\begin{document} +\Kanbun +月落チ烏啼キテ霜満ツ[レ]天ニ, +江楓漁火対ス[二]愁眠ニ[一]。 +姑(こ)蘇(そ)城外ノ寒山寺, +夜半ノ鐘声到ル[二]客船ニ[一]。 +\EndKanbun +\printkanbun +\end{document} +\end{example} + +Note that if you want to use this functionality, you have to run this document with \LuaLaTeX. + + +\section{Usage} + +\subsection{Package options} +Load the package with +\begin{ltxsyntax} + \cmditem{usepackage}\oprm{\sty{kanbun} options}|{kanbun}| +\end{ltxsyntax} + +This package provides a variety of customisable features in \emph{kanbun-kundoku}. + +\begin{optionlist} + \optitem[]{scale}{\prm{ratio}} + Sets the ratio of the size of \emph{kanji} to that of ruby texts. Default: \verb|2|. + + \optitem[]{fontcmd}{\prm{font command}} + Sets the font command to use for \emph{kanji}. If \sty{luatexja-fontspec} is loaded, it is set default to \verb|\addjfontfeatures{RawFeature={+trad}}| to obtain traditional \emph{kanji} if applicable. + + \optitem[]{rubyfontcmd}{\prm{font command}} + Sets the font command to use for ruby texts. If \sty{luatexja-fontspec} is loaded, it is set default to \verb|\addjfontfeatures{RawFeature={+ruby}}| to obtain ruby glyphs when applicable. + + \optitem[]{unit}{\prm{length}} + Sets the base size (size of \emph{kanji}). Default: \verb|\kanbun_zw|, which is initialised as \verb|1em|. + + \optitem[]{yokoaki}{\prm{ratio}} + Sets the horizontal space between \emph{kanji} with respect to the size of ruby texts. Default: \verb|2|. + + \optitem[]{tateaki}{\prm{ratio}} + Sets the vertical space between \emph{kanji} with respect to the size of ruby texts. Default: \verb|2|. + + \optitem[]{okuriprotrusion}{\prm{ratio}} + Sets how much \emph{okurigana} should be vertically tucked into the space of \emph{kanji} with respect to the size of ruby texts, if that does not cause an overlap with \emph{furigana}. Default: \verb|1|. + + \optitem[]{kumi}{\opt{aki}, \opt{beta}} + Sets whether to use \emph{aki-gumi} (typeset with uniform inter-character spacing) or \emph{beta-gumi} (typeset with no inter-character space between adjacent character frames). Or simply call \opt{aki} or \opt{beta} without \opt{kumi=}. Default: \opt{aki}. +\end{optionlist} + +After initialising the options, you can still change the option values through \sty{exlp3} syntax, with a prefix \verb|\kanbun_| to option names. For example, to switch to \emph{beta-gumi}, you could use +\begin{example} +\ExplSyntaxOn +\tl_set:Nn \kanbun_kumi { beta } +\ExplSyntaxOff +\end{example} + +\subsection[Basic usage without \LuaLaTeX]{Basic usage without \LuaLaTeX{} (not recommended)} +When not using the advanced \emph{kanbun}-annotation functionality, it is possible to typeset \emph{kanbun} with any engine with \textsc{cjk} support, such as using \XeLaTeX{} with the \sty{xeCJK} package, or using \upLaTeX{} with \sty{utarticle} or other appropriate class. + +\begin{ltxsyntax} + \cmditem{kanjiunit}\verb|{ |\cmd{furiokuri}\mprm{right furigana}\mprm{right okurigana}\verb| }|\vspace{-5pt}\\ + \mprm{left punctuation (e.g.~`「')}\\ + \mprm{kanji}\\ + \mprm{other punctuation}\\ + \mprm{kaeriten}\\ + \verb|{ |\cmd{furiokuri}\mprm{left furigana}\mprm{left okurigana}\verb| }| + + \cmditem{kanbunfont} + + Sets the font size of \emph{kanji}. Use when the \opt{unit} option is set different to the document's default font size. + + Use \cmd{multifuriokuri} instead of \cmd{furiokuri} if you are putting \emph{furigana} to multiple kanji. + + \cmditem{multifuriokuri}\oprm{length by which {furigana} is raised}\mprm{furigana}\mprm{okurigana} +\end{ltxsyntax} + +For example, the code +\begin{example} +% example text from https://phesoca.com/kanbun-html/ +\kanbunfont +\kanjiunit{}{}{子}{}{}{} +\kanjiunit{\furiokuri{}{ク}}{}{曰}{,}{}{} +\kanjiunit{\furiokuri{}{ゾ}}{}{盍}{}{三}{\furiokuri{}{ル}} +\kanjiunit{}{}{各}{〻}{}{} +\kanjiunit{\furiokuri{}{ハ}}{}{言}{}{二}{} +\kanjiunit{\furiokuri{}{ノ}}{}{爾}{}{}{} +\kanjiunit{\furiokuri{}{ヲ}}{}{志}{。}{一}{} +\par +\end{example} +outputs + +\printkanbunblock{6}{ +\kanbunfont +\kanjiunit{}{}{子}{}{}{} +\kanjiunit{\furiokuri{}{ク}}{}{曰}{,}{}{} +\kanjiunit{\furiokuri{}{ゾ}}{}{盍}{}{三}{\furiokuri{}{ル}} +\kanjiunit{}{}{各}{〻}{}{} +\kanjiunit{\furiokuri{}{ハ}}{}{言}{}{二}{} +\kanjiunit{\furiokuri{}{ノ}}{}{爾}{}{}{} +\kanjiunit{\furiokuri{}{ヲ}}{}{志}{。}{一}{} +\par +} + +\noindent +with \opt{tateaki} set to \verb|1|. + +\subsection{Usage with \LuaLaTeX} +\emph{Kanbun} annotation uses the following brackets to mark different elements in \emph{kanbun-kundoku} (as described in \href{https://phesoca.com/kanbun-html/}{漢文\textsc{html}} by UntPhesoca). +\begin{itemize} + \item \verb|( )|: \emph{furigana} (振り仮名) + \item \verb|{ }|: \emph{okurigana} (送り仮名) (these brackets can be omitted) + \item \verb|‹ ›|: \emph{furigana} (振り仮名) of \emph{saidoku-moji} (再読文字) + \item \verb|« »|: \emph{okurigana} (送り仮名) of \emph{saidoku-moji} (再読文字) + \item \verb|[ ]|: \emph{kaeriten} (返り点) + \item \verb|‘ ’|: multiple \emph{kanji}, potentially with \emph{tateten} inserted, as a ruby base; group ruby (グループルビ) + \item no annotation: \emph{kanji} (漢字) and punctuation. +\end{itemize} +\emph{Tateten} (竪点) can be input with either \verb|―| (\verb|U+2015|), \verb|—| (\verb|U+2014|) or \verb|㆐| (\verb|U+3190|). + +\begin{ltxsyntax} + \cmditem{Kanbun}\cmditem{EndKanbun} + + Write the annotated \emph{kanbun} between the commands \cmd{Kanbun} and \cmd{EndKanbun}, and it will be processed and saved, ready to be used later. + + \cmditem{printkanbun} + + Where you would like to use the most recently saved \emph{kanbun-kundoku} paragraph, use \cmd{printkanbun}. + + \cmditem{printkanbuncode} + + If you wish to make modifications on the result or to use the result with a non-\LuaTeX{} engine, it is possible to obtain the macros using \cmd{printkanbuncode} (prints in the terminal), and continue to work from there. +\end{ltxsyntax} + +You can always save \cmd{printkanbun} to a macro and start a new annotated \emph{kanbun} block, as in the following example. +\begin{example} +% example text from https://phesoca.com/kanbun-html/ +\documentclass{ltjtarticle} +\usepackage[kumi=beta]{kanbun} + +\Kanbun +此レ乃チ信(しん)之‘所―[三]以’(ゆゑん)為ル[二]陛下ノ禽(とりこ)ト[一]也。 +\EndKanbun +\let\信\printkanbun + +\Kanbun +孤之有ルハ[二]孔明[一],猶ホ‹ごと›«キ»[二]魚之有ルガ[一レ]水也。 +\EndKanbun +\let\孔明\printkanbun + +\begin{document} +\孔明\par\bfseries\信 +\end{document} +\end{example} + +\Kanbun +此レ乃チ信(しん)之‘所―[三]以’(ゆゑん)為ル[二]陛下ノ禽(とりこ)ト[一]也。 +\EndKanbun +\let\信\printkanbun + +\Kanbun +孤之有ルハ[二]孔明[一],猶ホ‹ごと›«キ»[二]魚之有ルガ[一レ]水也。 +\EndKanbun +\let\孔明\printkanbun + +\ExplSyntaxOn +\tl_set:Nn \kanbun_kumi { beta } +\ExplSyntaxOff +\printkanbunblock{16}{\孔明\par\bfseries\信} + + +\end{document} diff --git a/Master/texmf-dist/tex/latex/kanbun/kanbun.sty b/Master/texmf-dist/tex/latex/kanbun/kanbun.sty new file mode 100644 index 00000000000..70994ab463f --- /dev/null +++ b/Master/texmf-dist/tex/latex/kanbun/kanbun.sty @@ -0,0 +1,317 @@ +%% Copyright 2022 Yuanhao Chen +% +% This work may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3 +% of this license or (at your option) any later version. +% The latest version of this license is in +% http://www.latex-project.org/lppl.txt +% and version 1.3 or later is part of all distributions of LaTeX +% version 2005/12/01 or later. +% +% This work has the LPPL maintenance status `maintained'. +% +% The Current Maintainer of this work is Yuanhao Chen. +% +% This work consists of the files kanbun.sty, kanbun.lua, +% kanbun-example.tex and kanbun.tex. +% +\NeedsTeXFormat{LaTeX2e}[2021/06/01] +\RequirePackage{expl3,xparse,l3keys2e,ifluatex} +\ProvidesExplClass{kanbun} + {2022/01/15} {1.0} {漢文の訓点文の組版} + +\ExplSyntaxOn +\cs_generate_variant:Nn \str_if_eq:nnTF { V } +\cs_generate_variant:Nn \str_case:nnTF { V } + +% vars used in options +\tl_set:Nn \kanbun_scale { 2 } +\newlength\kanbun_init_zw +\setlength\kanbun_init_zw{1em} +\@ifpackageloaded{luatexja-fontspec}{ + \tl_set:Nn \kanbun_rubyfontcmd_default { \addjfontfeatures{RawFeature={+ruby}} } + \tl_set:Nn \kanbun_fontcmd_default { \addjfontfeatures{RawFeature={+trad}} } +}{ + \tl_set:Nn \kanbun_rubyfontcmd_default {} + \tl_set:Nn \kanbun_fontcmd_default {} +} + +% options +\keys_define:nn { kanbun } + { + , scale .tl_set:N = \kanbun_scale + , scale .initial:n = { 2 } + , fontcmd .tl_set:N = \kanbun_fontcmd + , fontcmd .initial:n = \kanbun_fontcmd_default + , rubyfontcmd .tl_set:N = \kanbun_rubyfontcmd + , rubyfontcmd .initial:n = \kanbun_rubyfontcmd_default + , unit .tl_set:N = \kanbun_zw + , unit .initial:n = { \kanbun_init_zw } + , yokoaki .tl_set:N = \kanbun_yokoaki + , yokoaki .initial:n = { \kanbun_scale } + , tateaki .tl_set:N = \kanbun_tateaki + , tateaki .initial:n = { 2 } + , okuriprotrusion .tl_set:N = \kanbun_okuriprotrusion + , okuriprotrusion .initial:n = { 1 } + , kumi .tl_set:N = \kanbun_kumi + , kumi .initial:n = { aki } + , aki .code:n = \keys_set:nn { kanbun } { kumi = aki } + , beta .code:n = \keys_set:nn { kanbun } { kumi = beta } + } +\ProcessKeysOptions { kanbun } + +% other vars +\tl_set:Nn \kanbun_rubyfontsize_rubyfontcmd { \kanbun_rubyfontcmd\fontsize{\fp_eval:n {1/\kanbun_scale}\kanbun_zw}{0pt}\selectfont } +\newlength{\kanbun_furilen} +\tl_set:Nn \kanbun_tateten_width { 1/30 } +\tl_set:Nn \kanbunzwtosp { \fp_eval:n {\dim_ratio:nn { 1 \kanbun_zw } { 1 sp } } } +\tl_set:Nn \kanbun_unit_glue { + \str_if_eq:VnTF \kanbun_kumi { aki } { + \penalty0 + } + { + \hskip 0\kanbun_zw plus 0.25\kanbun_zw minus 0\kanbun_zw + } +} +\tl_set:Nn \kanbun_punct_glue { + \hskip 0\kanbun_zw plus 0.5\kanbun_zw minus 0\kanbun_zw +} + +% \kanjiunit and other macros to be used in a \kanjiunit +\NewDocumentCommand { \kanbun_ensure_height } { m m } + { + \vbox to #1{\vss{#2}\vss} + } +\newbox\kanbun_current_kanjiunit +\NewDocumentCommand { \kanjiunit } { m m m m m m } + { + \tl_set:Nn \ninojiten_punct {} + \tl_set:Nn \right_punct {} + \tl_set:Nn \central_punct {} + \process_punct{ #4 } + \tl_set:Nn \kanbun_central_punct_box { + \kanbun_ensure_height{1\kanbun_zw} { + \str_if_eq:VnTF \kanbun_kumi { aki } + { + \hbox{\kern 1\kanbun_zw{\hbox to \fp_eval:n { \kanbun_tateaki/\kanbun_scale }\kanbun_zw{\hss{\central_punct}\hss}}} + } + { + \hbox{\kern 1\kanbun_zw{\central_punct}} + } + } + } + \tl_set:Nn \kanbun_left_punct_phantom { + \kanbun_ensure_height{1\kanbun_zw} { + \str_if_eq:VnTF \kanbun_kumi { aki } { } + { + \str_if_eq:nnTF { #2 } { } { } { + \hbox{\phantom{\hbox to 0.5\kanbun_zw{\hss#2\hfil}}} + } + } + } + } + \tl_set:Nn \kanbun_right_punct_box { + \kanbun_ensure_height{1\kanbun_zw} { + \str_if_eq:VnTF \right_punct { } { } { + \hbox{\kern 1\kanbun_zw{\right_punct}} + } + } + } + % + \tl_set:Nn \unit_content { + \kanbun_left_punct_phantom % compensate for width of left punct + \vbox{ + \kanbun_ensure_height{\fp_eval:n { 1/\kanbun_scale }\kanbun_zw}{\hbox{\kanbun_rubyfontsize_rubyfontcmd{#1}}} % right kana + \nointerlineskip % + \kanbun_ensure_height{1\kanbun_zw}{\llap{#2}} % left punct + \nointerlineskip % + \vspace*{-1 \kanbun_zw} + \kanbun_ensure_height{1\kanbun_zw}{ + \hbox to 1\kanbun_zw{\hss{#3}\hss} + } % kanji + \nointerlineskip % + \vspace*{-1 \kanbun_zw} + \kanbun_ensure_height{\fp_eval:n { 1/\kanbun_scale }\kanbun_zw}{\hbox{\kern 1\kanbun_zw{\ninojiten_punct}}} % ninojiten + \nointerlineskip % + \vspace*{\fp_eval:n { -1/\kanbun_scale }\kanbun_zw} + \kanbun_right_punct_box % right punct + \nointerlineskip % + \vspace*{-1\kanbun_zw} + \kanbun_central_punct_box % central punct + \nointerlineskip % + \vspace*{ \fp_eval:n { -1/\kanbun_scale }\kanbun_zw } + \kanbun_ensure_height{\fp_eval:n { 1/\kanbun_scale }\kanbun_zw}{\hbox{\kanbun_rubyfontsize_rubyfontcmd\kern 1\kanbun_zw{\kaeriten{#5}}}} % kaeriten + \nointerlineskip % + \kanbun_ensure_height{\fp_eval:n { 1/\kanbun_scale }\kanbun_zw}{\hbox{\kanbun_rubyfontsize_rubyfontcmd{#6}}} % left kana + } + } + \str_if_eq:VnTF \kanbun_kumi { aki } { + \setbox\kanbun_current_kanjiunit \hbox to \fp_eval:n { 1+\kanbun_tateaki/\kanbun_scale }\kanbun_zw{ \unit_content } + }{ + \setbox\kanbun_current_kanjiunit \hbox{ \unit_content } + } + % + % output + \str_if_eq:VnTF \kanbun_kumi { aki } { } + { + \str_if_eq:nnTF { #2 } { } { } { + \kanbun_punct_glue + } + } + \nobreak + \str_if_eq:VnTF \kanbun_kumi { aki } + { + \usebox\kanbun_current_kanjiunit + } + { + \discretionary{ + \usebox\kanbun_current_kanjiunit\kern\dimexpr1\kanbun_zw-\wd\kanbun_current_kanjiunit\relax + }{ + % + }{ + \usebox\kanbun_current_kanjiunit + } + } + \nobreak + \str_if_eq:VnTF \kanbun_kumi { aki } { } + { + \str_if_eq:VnTF \right_punct { } { } { + \kanbun_punct_glue + } + } + \nobreak + \kanbun_unit_glue + } +\NewDocumentCommand { \furiokuri } { m m } + { + \settowidth{\kanbun_furilen}{#1} + \ifdim\kanbun_furilen < \fp_eval:n {1-\kanbun_okuriprotrusion/\kanbun_scale}\kanbun_zw + \makebox[\fp_eval:n {1-\kanbun_okuriprotrusion/\kanbun_scale}\kanbun_zw][l]{#1} + \else + #1 + \fi + #2 + } +\NewDocumentCommand { \kaeriten } { m } + { + \tl_set:Nn \kaeriten_output { #1 } + \sys_if_engine_xetex:TF {} { + \platex_if_direction_tate:TF { + \str_case:VnTF \kaeriten_output { + { 一レ } { \tl_set:Nn \kaeriten_output { \kern \fp_eval:n {-0.25/\kanbun_scale}\kanbun_zw 一\kern \fp_eval:n {-0.75/\kanbun_scale}\kanbun_zw レ } } + { 甲レ } { \tl_set:Nn \kaeriten_output { 甲\kern \fp_eval:n {-0.25/\kanbun_scale}\kanbun_zw レ } } + { 上レ } { \tl_set:Nn \kaeriten_output { 上\kern \fp_eval:n {-0.25/\kanbun_scale}\kanbun_zw レ } } + { 天レ } { \tl_set:Nn \kaeriten_output { 天\kern \fp_eval:n {-0.25/\kanbun_scale}\kanbun_zw レ } } + } {} {} + } {} + } + \tl_use:N \kaeriten_output + } +\NewDocumentCommand { \process_punct_map } { m } { + \str_case:nnTF { #1 } { + { — } { \tl_put_right:Nn \central_punct { \tateten } } + { ― } { \tl_put_right:Nn \central_punct { \tateten } } + { ㆐ } { \tl_put_right:Nn \central_punct { \tateten } } + % { : } { \tl_put_right:Nn \central_punct { \hbox to 1\kanbun_zw{\hss{:}\hss} } } + % { ・ } { \tl_put_right:Nn \central_punct { \hbox to 1\kanbun_zw{\hss{・}\hss} } } + { : } { \tl_put_right:Nn \right_punct { \hbox to 0.5\kanbun_zw{\hss{:}\hss} } } + { ・ } { \tl_put_right:Nn \right_punct { \hbox to 0.5\kanbun_zw{\hss{・}\hss} } } + { … } { \tl_put_right:Nn \central_punct { \hbox to 1\kanbun_zw{\hss{…}\hss} } } + { ! } { \tl_put_right:Nn \central_punct { \hbox to 1\kanbun_zw{\hss{!}\hss} } } + { ? } { \tl_put_right:Nn \central_punct { \hbox to 1\kanbun_zw{\hss{?}\hss} } } + { 〻 } { + \tl_put_right:Nn \ninojiten_punct { \ninojiten } + \tl_put_right:Nn \right_punct { \phantom{\ninojiten} } + } + } {} { + \tl_put_right:Nn \right_punct { \hbox to 0.5\kanbun_zw{\hfil#1\hss} } + } +} +\NewDocumentCommand { \process_punct } { m } + { + \str_map_function:nN { #1 } \process_punct_map + } +\NewDocumentCommand { \multifuriokuri } { O{2\kanbun_zw} m m } + { + \kanbun_rubyfontcmd + \kern \fp_eval:n { -(#1) }\kanbun_zw + \makebox[\fp_eval:n { (#1) + 1 - \kanbun_okuriprotrusion/\kanbun_scale }\kanbun_zw][s]{#2} + #3 + } + +% these commands are used as punct +\NewDocumentCommand { \tateten } {} + { + \str_if_eq:VnTF \kanbun_kumi { aki } { + \makebox[\fp_eval:n {\kanbun_tateaki/\kanbun_scale}\kanbun_zw][c]{ + \rule[\fp_eval:n {-\kanbun_tateten_width/2}\kanbun_zw]{\fp_eval:n {\kanbun_tateaki/\kanbun_scale}\kanbun_zw}{\fp_eval:n {\kanbun_tateten_width}\kanbun_zw} + } + }{ + \makebox[\fp_eval:n {1/\kanbun_scale}\kanbun_zw][c]{ + \rule[\fp_eval:n {-\kanbun_tateten_width/2}\kanbun_zw]{\fp_eval:n {1/\kanbun_scale}\kanbun_zw}{\fp_eval:n {\kanbun_tateten_width}\kanbun_zw} + } + } + } +\NewDocumentCommand { \ninojiten } {} + { \kanbun_rubyfontsize_rubyfontcmd 〻 } + +% corrects fontsize of ruby base +\NewDocumentCommand { \kanbunfont } {} { \parindent=0pt\kanbun_fontcmd\fontsize{\kanbun_zw}{\fp_eval:n {1+\kanbun_yokoaki/\kanbun_scale}\kanbun_zw}\selectfont} +\NewDocumentEnvironment { kanjipar } { +b } + { + { + \kanbunfont + #1 + \par + } + } {} + +% for LuaLaTeX to parse Kanbun annotation +\NewDocumentCommand{\matchkana}{ m }{ + \regex_set:Nn \c_kana_regex {[\x{3040}-\x{30FA}\x{30FC}-\x{30FF}\x{31F0}-\x{31FF}\x{FF66}-\x{FF9F}\x{1B100}-\x{1B122}\x{1AFF0}-\x{1AFFF}\x{1B000}-\x{1B0FF}\x{1B130}-\x{1B16F}]} + \regex_match:NnTF \c_kana_regex { #1 } { \bool_set_true:N \g_kana_bool } { \bool_set_false:N \g_kana_bool } +} +% +\ExplSyntaxOff +% +\ifluatex +% verbatim reader adapted from https://tex.stackexchange.com/a/361759 +\directlua{ +verb_table = {} +function store_lines (str) + if string.find (str , "\noexpand\\EndKanbun" ) then + luatexbase.remove_from_callback ( + "process_input_buffer" , "store_lines") + return "\\newif\\ifcontinue\\continuetrue\\directlua { co = coroutine.create(main_loop) }\\loop\\directlua{ ok,b=coroutine.resume(co) tex.sprint(b) }\\ifcontinue\\repeat" + else + table.insert(verb_table, str) + end + return "" +end +function register_verbatim () + verb_table = {} + luatexbase.add_to_callback( + "process_input_buffer" , store_lines , "store_lines") +end +% require main loop +kanbunzwtosp = \luaescapestring{\kanbunzwtosp} +require("kanbun.lua") +} +\def\Kanbun{\directlua{register_verbatim()}} +\def\createcatcodes{% + \bgroup% + \catcode`\\=12 \catcode`\{=12 \catcode`\}=12% + \catcode`\$=12 \catcode`\&=12 \catcode`\^^M=13% + \catcode`\#=12 \catcode`\^=12 \catcode`\_=12% + \catcode`\ =13 \catcode`\~=12 \catcode`\%=12% + \savecatcodetable 1% + \egroup% +} +\createcatcodes +\bgroup% + \catcode`\^^M=13\gdef^^M{\quitvmode\par}% + \catcode`\ = 13\gdef {\quitvmode\Space}% +\egroup% +\def\Space{ } +% +\fi diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check index 6266a05300c..f587188c9d0 100755 --- a/Master/tlpkg/bin/tlpkg-ctan-check +++ b/Master/tlpkg/bin/tlpkg-ctan-check @@ -425,7 +425,7 @@ my @TLP_working = qw( jmb jmlr jneurosci jnuexam josefin jpsj jsclasses jslectureplanner jumplines junicode jupynotex jura juraabbrev jurabib juramisc jurarsp js-misc jvlisting - kalendarium kanaparser kantlipsum karnaugh karnaugh-map karnaughmap + kalendarium kanaparser kanbun kantlipsum karnaugh karnaugh-map karnaughmap kastrup kaytannollista-latexia kblocks kdgdocs kdpcover kerkis kerntest ketcindy keycommand keyfloat keyindex keyparse keyreader keystroke diff --git a/Master/tlpkg/tlpsrc/collection-langjapanese.tlpsrc b/Master/tlpkg/tlpsrc/collection-langjapanese.tlpsrc index e5a5ed6b426..d2c43a365c5 100644 --- a/Master/tlpkg/tlpsrc/collection-langjapanese.tlpsrc +++ b/Master/tlpkg/tlpsrc/collection-langjapanese.tlpsrc @@ -30,6 +30,7 @@ depend japanese-otf-uptex depend jlreq depend jlreq-deluxe depend jsclasses +depend kanbun depend lshort-japanese depend luatexja depend mendex-doc diff --git a/Master/tlpkg/tlpsrc/kanbun.tlpsrc b/Master/tlpkg/tlpsrc/kanbun.tlpsrc new file mode 100644 index 00000000000..e69de29bb2d -- cgit v1.2.3