diff options
Diffstat (limited to 'macros/luatex/generic/luatexko')
-rw-r--r-- | macros/luatex/generic/luatexko/ChangeLog | 235 | ||||
-rw-r--r-- | macros/luatex/generic/luatexko/README | 151 | ||||
-rw-r--r-- | macros/luatex/generic/luatexko/luatexko-doc.pdf | bin | 0 -> 288860 bytes | |||
-rw-r--r-- | macros/luatex/generic/luatexko/luatexko-doc.tex | 664 | ||||
-rw-r--r-- | macros/luatex/generic/luatexko/luatexko-normalize.lua | 620 | ||||
-rw-r--r-- | macros/luatex/generic/luatexko/luatexko-uhc2utf8.lua | 128 | ||||
-rw-r--r-- | macros/luatex/generic/luatexko/luatexko.lua | 1969 | ||||
-rw-r--r-- | macros/luatex/generic/luatexko/luatexko.sty | 728 |
8 files changed, 4495 insertions, 0 deletions
diff --git a/macros/luatex/generic/luatexko/ChangeLog b/macros/luatex/generic/luatexko/ChangeLog new file mode 100644 index 0000000000..38e815ea96 --- /dev/null +++ b/macros/luatex/generic/luatexko/ChangeLog @@ -0,0 +1,235 @@ +2019-08-01 Dohyun Kim <nomos at ktug org> + + Version 2.4 + + * luatexko.lua: allow linebreak before/after a normal hbox + * luatexko.{sty,lua}: moveleft horizontal box inside vertical writing + +2019-06-21 Dohyun Kim <nomos at ktug org> + + Version 2.3 + + * luatexko.lua: prevent discretionary node after Kana; + provide lua functions deactivateall() and reactivateall(). + +2019-06-07 Dohyun Kim <nomos at ktug org> + + Version 2.2 + + * luatexko.sty, luatexko.lua: provide font option `InterCharStretch' + +2019-05-25 Dohyun Kim <nomos at ktug org> + + Version 2.1 + + * luatexko.sty: give smallest stretch to quite short ruby; + allow star option to markoverwith. + + +2019-05-01 Dohyun Kim <nomos at ktug org> + + Version 2.0 + + * luatexko-core.sty: removed. + * luatexko.lua: totally rewritten. + * luatexko.sty: Package option `nofontspec' removed. + Font options `CompressPunctuations' `RemoveClassicSpaces' + and `InterCharacter' added. + `\registerbreakablebefore' and `\registerbreakableafter' added. + +2019-04-06 Dohyun Kim <nomos at ktug org> + + Version 1.24 + + * luatexko.lua: fix a bug regarding vertical typesetting + +2019-03-24 Dohyun Kim <nomos at ktug org> + + Version 1.23 + + * luatexko.lua: change post-linebreak callback from + 'post_linebreak_filter' to 'hpack_filter' with groupcode restriction. + * luatex-core.sty: remove '\pdffontsize' and replace it with a + more suitable equivalent. + +2018-11-09 Dohyun Kim <nomos at ktug org> + + Version 1.22 + + * luatexko-core.sty, luatexko.lua: new command \hangulbyhangulfont. + This being declared, Hangul characters will be forcibly typeset + with Hangul font even when Latin font has Hangul glyphs. + +2018-08-02 Dohyun Kim <nomos at ktug org> + + Version 1.21 + + * luatexko.sty: adapt to the fontspec v2.6h + * luatexko.lua: font substitution for disc node as well + +2018-04-19 Dohyun Kim <nomos at ktug org> + + Version 1.20 + + * luatexko.lua: node.tail -> node.slide: see the bug reported at + http://www.ktug.org/xe/index.php?document_srl=229431 + +2018-04-06 Dohyun Kim <nomos at ktug org> + + Version 1.19 + + * luatexko.sty: use glueexpr + +2017-09-29 Dohyun Kim <nomos at ktug org> + + Version 1.18 + + * luatexko.sty: zf@enc -> f@encoding + +2017-05-01 Dohyun Kim <nomos at ktug org> + + Version 1.17 + + * luatexko.lua: rewrite reorderTM (reorder-tone-marks) function + + +2017-02-06 Dohyun Kim <nomos at ktug org> + + Version 1.16 + + * luatexko.sty: patch {rm|sf|tt}family of fontspec 2017 + +2016-05-14 Dohyun Kim <nomos at ktug org> + + Version 1.15 + + * luatexko-core.sty: allow nested underline emphasis + +2016-05-08 Dohyun Kim <nomos at ktug org> + + Version 1.14 + + * luatexko.lua, luatexko-core.sty: more updates for luatex 0.95, + user_defined whatsit intead of special whatsit + +2016-04-25 Dohyun Kim <nomos at ktug org> + + Version 1.13 + + * luatexko.lua: direct access to vmtx table; drop caching + +2016-04-22 Dohyun Kim <nomos at ktug org> + + Version 1.12 + + * *.sty, *.lua: drop loading luatexbase.sty; updates for luatex 0.95 + * luatexko.lua: avoid nested underline emphasis + +2015-12-10 Dohyun Kim <nomos at ktug org> + + Version 1.11 + + * luatexko-core.sty: adapt to luaotfload v2.6 + * luatexko-normalize.lua: a small fix for windows os + +2015-10-02 Dohyun Kim <nomos at ktug org> + + Version 1.10 + + * luatexko.lua: adapt to latex 2015/10/01 and luatexbase v1.0 + * luatexko.lua: assing pre/post penalty to U+FF62, U+FF63 + * luatexko.lua: fix an infinite loop in reorderTM + +2015-07-02 Dohyun Kim <nomos at ktug org> + + Version 1.9 + + * luatexko.lua: some more compatibility with synctex + * luatexko-core.sty: new environment `horizontal' + * luatexko.lua: remove support for ideographic variations (fontloader + supports them) + +2015-05-10 Dohyun Kim <nomos at ktug org> + + Version 1.8 + + * luatexko.lua: internal changes using `node.direct' + * luatexko-core.sty: \xxruby is a convenient variant of \ruby + +2015-01-16 Dohyun Kim <nomos at ktug org> + + Version 1.7 + + * luatexko.sty: package option [unfonts] is removed + * *.lua, *.sty: minor fixes and updated copyright year + +2014-06-17 Dohyun Kim <nomos at ktug org> + + Version 1.6 + + * luatexko.sty, luatexko.lua: new font option [NoEmbedding] prevents + inclusion of fonts into PDF file. + * luatexko-uhc2utf8.lua: fix a bug in utf-8 detection routine, + reported by Karnes Kim + * luatexko.lua: put underline leader node before text, so that text + should not be overprinted by a color box. Requested by Karnes Kim + * luatexko.sty: [Ligatures=TeX] is now default option for main/sans + hangul/hanja/fallback fonts. Requires fontspec v2.4 + +2014-05-11 Dohyun Kim <nomos at ktug org> + + Version 1.5 + + * luatexko.sty: new package option [nofontspec] which prevents loading + of fontspec.sty + * luatexko.lua, luatexko.sty: use Nanum Type1 fonts as final fallback + fonts + * luatexko.sty: font option YetHangul=Off has gone now, only YetHangul + is still there. + * luatexko.lua: implemented experimental support for vertical + typesetting. LuaTeX version 0.79 or later is required. + * luatexko.lua, luatexko-core.sty: new macro \actualtext inspired by + accsupp.sty authored by Heiko Oberdiek. + * luatexko.lua: italic correction for replacing cjk fonts as well + +2014-01-16 Dohyun Kim <nomos at ktug org> + + Version 1.4 + + * luatexko.lua: italic correction of fakeslant font + * luatexko.sty: use everysel.sty to redefine selectfont. This fixes + a bug reported by Hoze Yi & Juho Lee. + +2013-12-06 Dohyun Kim <nomos at ktug org> + + Version 1.3 + + * luatexko.lua: if \josaignoreparens=1, then characters inside + parentheses are ignored during automatic josa selection routine. + * luatexko.sty, luatexko-core.sty, luatexko.lua: new macros + \hangulpunctuations \registerpunctuations etc; removed [QuoteRaise] + font option. + * luatexko.sty, luatexko-core.sty, luatexko.lua: new macros + \setmainfallbackfont \setsansfallbackfont etc. + +2013-07-03 Dohyun Kim <nomos at ktug org> + + Version 1.2 + + * luatexko.sty: move Hangul caption setting to AtBeginDocument + * luatexko.lua: implemented reordering of hangul tone marks; + fix a bug wrt. \dotemph of old hangul. + * luatexko-core.sty: adapt to \fontname fix by luaotfload + +2013-05-19 Dohyun Kim <nomos at ktug org> + + Version 1.1 + + * luatexko.sty: fix a bug regarding hyperref pdf string + * luatexko.lua, luatexko-core.sty: remove redundant code wrt. + font switching + +2013-05-10 Dohyun Kim <nomos at ktug org> + + Initial release. Version 1.0 + diff --git a/macros/luatex/generic/luatexko/README b/macros/luatex/generic/luatexko/README new file mode 100644 index 0000000000..1284ed2271 --- /dev/null +++ b/macros/luatex/generic/luatexko/README @@ -0,0 +1,151 @@ +LuaTeX-ko Package version 2.4 (2019/08/01) +=========================================== + +This is a Lua(La)TeX macro package that supports typesetting Korean +documents including Old Hangul texts. As LuaTeX has opened up access to +almost all the hidden routines of TeX engine, users can obtain more +beautiful outcome using this package rather than other Hangul packages +operating on other engines. + +LuaTeX version 1.10+ and luaotfload package version 2.96+ are required for +this package to run. + +This package also requires cjk-ko package for its full functionality. + + +License +------- + +This package is licensed under [LPPL](http://latex-project.org/lppl/) +(LaTeX Project Public License) version 1.3c or later. + +See each file for details. + + +Author +------ + + Dohyun Kim <nomos at ktug org> + Soojin Nam <jsunam at gmail com> + +Please report any errors or suggestions to +or leave messages in the issue tracker at: + <http://github.com/dohyunkim/luatexko> + + +Files +----- + +TeXinputs + + luatexko.sty -> tex/luatex/luatexko/ + luatexko.lua -> tex/luatex/luatexko/ + luatexko-normalize.lua -> tex/luatex/luatexko/ + luatexko-uhc2utf8.lua -> tex/luatex/luatexko/ + +Documents + + luatexko-doc.pdf -> doc/luatex/luatexko/ + luatexko-doc.tex -> doc/luatex/luatexko/ + README (this file) -> doc/luatex/luatexko/ + ChangeLog -> doc/luatex/luatexko/ + + +Loading +------- + +For a LaTeX user, declaring + + \usepackage{luatexko} +or + + \usepackage{kotex} +is sufficient to load the package, which will load fontspec as well. +Notice that kotex.sty is a file provided by cjk-ko package. + +Under plain TeX: + + \input luatexko.sty + + +Package Options +--------------- + + [hangul] +Load Hangul captions. Besides, this option adjusts interword and +interline spacing. Declares \hangulpunctuations=1 as well. + + [hanja] +Load Hanja captions. Also apply other settings as [hangul] option does. + + +Hangul Font Commands +-------------------- + + \setmainhangulfont + \setsanshangulfont + \setmonohangulfont +Equivalent to \setmainfont et. al. of fontspec package. These fonts are +used when the font loaded by \setmainfont et. al. does not have Hangul +glyphs. + + \setmainhanjafont + \setsanshanjafont + \setmonohanjafont +These fonts are used when the font loaded by \setmainfont or +\setmainhangulfont et. al. does not have Hanja glyphs. + + \setmainfallbackfont + \setsansfallbackfont + \setmonofallbackfont +These fonts are used when the font loaded by \setmainfont, +\setmainhangulfont, or \setmainhanjafont et. al. does not have Hanja +glyphs. + +In like manner, these commands are available as well: + + \hangulfontspec \newhangulfontfamily \addhangulfontfeature + \hanjafontspec \newhanjafontfamily \addhanjafontfeature + \fallbackfontspec \newfallbackfontfamily \addfallbackfontfeature + +If any of these CJK fonts are not specified, UnBatang/UnDotum TrueType +fonts will be used for typesetting CJK characters. + + +Hangul Font Options +------------------- + + [InterHangul=<dimen>] +Set spacing between Hangul characters. + + [InterLatinCJK=<dimen>] +Set spacing between CJK and Latin characters. + + [CharRaise=<dimen>] +Raise CJK characters by <dimen>. + + +Other User Commands +------------------- + + \hangulpunctuations=<number> +When <number> is 1 or greater (being default value), latin punctuations +will be typeset with hangul fonts. + + \dotemph{...} +Emphasise Hangul or Hanja by putting dot above. + + \ruby{<base text>}{<ruby text>} +Typeset ruby annotations. + + \uline{...} + \sout{...} + \uuline{...} + \xout{...} + \uwave{...} + \dashuline{...} + \dotuline{...} +Same functionality as those ulem package provides. + + +*END of README* diff --git a/macros/luatex/generic/luatexko/luatexko-doc.pdf b/macros/luatex/generic/luatexko/luatexko-doc.pdf Binary files differnew file mode 100644 index 0000000000..0eb5a5078f --- /dev/null +++ b/macros/luatex/generic/luatexko/luatexko-doc.pdf diff --git a/macros/luatex/generic/luatexko/luatexko-doc.tex b/macros/luatex/generic/luatexko/luatexko-doc.tex new file mode 100644 index 0000000000..517fe4d2ed --- /dev/null +++ b/macros/luatex/generic/luatexko/luatexko-doc.tex @@ -0,0 +1,664 @@ +%% !TEX program = lualatex +%% !TEX encoding = UTF-8 +%% +%% luatexko-doc.tex +%% +%% written by Dohyun Kim <nomos at ktug org> +%% Soojin Nam <jsunam at gmail com> +%% +%% public domain + +\documentclass[a4paper]{article} +\usepackage[hmargin=3.8cm]{geometry} +\usepackage[svgnames]{xcolor} +\usepackage[hangul]{luatexko} +\directlua{ + fonts.protrusions.setups.notoserifcjk = { + [171] = { 0.5, 0 }, + [187] = { 0, 0.5 }, + [1002965] = { 0, 0.5 }, + [1002968] = { 0.5, 0 }, + [1002969] = { 0, 0.5 }, + [1002970] = { 0, 1 }, + [1002971] = { 0, 0.5 }, + [1002972] = { 0, 1 }, + [1002974] = { 0, 0.5 }, + [1002975] = { 0, 0.5 }, + [1002976] = { 0, 0.5 }, + [1002985] = { 1, 0 }, + [1002986] = { 0, 1 }, + [1002988] = { 0.5, 0 }, + [1002989] = { 0, 0.5 }, + } +} +\setmainhangulfont{Noto Serif CJK KR}[ + Scale=0.98, + AutoFakeSlant, + Script=Hangul, + Language=Korean, + LocalForms=On, + UprightFont=* Light, + BoldFont=* Bold, + InterLatinCJK=.125em, + Expansion, Protrusion=notoserifcjk, +] +\setsanshangulfont{Noto Sans CJK KR}[ + Scale=0.98, + Script=Hangul, + Language=Korean, + UprightFont=* DemiLight, + BoldFont=* Bold, + InterLatinCJK=.125em, +] +\setmonofont{Inconsolatazi4}[ + Scale=0.98, + CharRaise=0.125ex, + Color=MidnightBlue, +] +\setmonohangulfont{Noto Sans CJK KR}[ + Scale=0.98, + UprightFont=* DemiLight, + BoldFont=* Bold, + CharRaise=-.125ex, + Color=MidnightBlue, +] +\setmathhangulfont{Noto Sans CJK KR}[ + Script=Hangul, + Language=Korean, + SizeFeatures={ + {Size=-6, Font=* Medium}, + {Size=6-9, Font=* Regular}, + {Size=9-, Font=* DemiLight}, + }, +] +\newhangulfontface\rubyfont{Noto Serif CJK KR Medium}[ + Script=Hangul, + Language=Korean, +] +\usepackage{hologo} +\def\luatex{\hologo{LuaTeX}} +\def\XeTeX{\hologo{XeTeX}} +\usepackage[pdfencoding=auto,bookmarksnumbered,hidelinks]{hyperref} + +\edef\verbatim{\unexpanded\expandafter{\verbatim\linespread{1.1}\selectfont}} +\edef\itemize{\unexpanded\expandafter{\itemize\small}} +\catcode`|=13 \protected\def|{\verb|\let\\=\textbackslash} +\protected\def\cs#1{\texttt{\language=1 \textbackslash\detokenize{#1}}} +\def\logoko{\textsf{k}\kern-.0625em\textit{o}} +\def\luatexko{\luatex-\logoko} +\def\texlive{\TeX\ Live} +\def\kotex{\logoko\kern-.1ex .\kern-.1667em \TeX} +\def\hemph#1{\textsf{\bfseries #1}} +\def\ldots{$\dots$} + +\begin{document} +\title{\luatexko\ 간단 매뉴얼} +\author{Dohyun Kim \normalsize |<nomos at ktug org>| \and + Soojin Nam \normalsize |<jsunam at gmail com>| \and + \normalsize <\url{http://github.com/dohyunkim/luatexko}>} +\date{Version 2.4\quad 2019/08/01} +\maketitle + +\begin{quote}\small + For a summary introduction in English, please see |README| file. + + \begin{itemize}\linespread{1.1}\selectfont + \item[v2.2] ------ + \item \hyperref[sec:fontoption]{글꼴옵션 |InterCharStretch| 제공} + \item[v2.0] ------ + \item \hyperref[sec:fontoption]{글꼴옵션 |PunctRaise|, |NoEmbeding| 제거} + \item \hyperref[sec:mathhangul]{수식 한글 글꼴을 자동으로 잡아주지 않음} + \item \cs{actualtext} 명령 작동 않음. 대신, 옛한글 텍스트 추출 자동 지원% + \footnote{% + PDF 리더 어플리케이션이 ActualText 기능을 지원해야 가능하다. + 어도비 리더는 확실히 가능할 것이다. } + \item \hyperref[sec:fontoption]{글꼴옵션 |CompressPunctuations|, + |RemoveClassicSpaces|, |InterCharacter| 추가} + \item \hyperref[sec:break]{\cs{registerbreakableafter}, + \cs{registerbreakablebefore} 명령 추가} + \end{itemize} +\end{quote} + +\smallskip + +\section{부르기}\label{sec:loading} + +\hemph{\texlive\ 2019 이상}{ \small (\luatex\ v1.10 이상)}을 사용해야 한다. + +\luatexko\ 부르기: \cs{usepackage{luatexko}} 혹은 \cs{usepackage{kotex}}.% +\footnote{% + 플레인텍에서는 \cs{input luatexko.sty} 혹은 \cs{input kotex.sty}. } + +입력은 원칙적으로 UTF-8으로 한다. +BOM (Byte Order Mark)은 있어도 좋고 없어도 좋다. +CP949 인코딩에 관해서는 제\ref{sec:uhcencoding}절 참조. + +\section{패키지 옵션}\label{sec:packageopt} + +패키지 옵션으로 |[hangul]|과 |[hanja]|가 제공된다.% +\footnote{% + |[nofontspec]| 옵션은 v2.0부터 더는 제공되지 않는다. } +행간격 조정이 행해지며 +장 제목이나 표·그림 캡션 따위가 한글 또는 한자 방식으로 표시된다. +장(chapter) 제목과 편(part) 제목에만 ``제(第)''가 붙어 ``제~1 편'' +``제~3 장''과 같은 방식으로 표시되며 절 제목 이하에는 붙지 않는다. + +\section{글꼴명령}\label{sec:fontcmds} + +\luatexko를 로드하면 fontspec 패키지를 자동으로 불러온다. 글꼴 설정에 +대해서는 fontspec 문서를 참조. + +한국어 글꼴을 위해 새로 마련한 명령은 다음과 같다. +첫 두 줄, 즉 main/sans 글꼴들에는 |Ligatures=TeX| 옵션이 자동으로 주어진다.% +\footnote{% + \cs{adhochangulfont} \cs{adhochanjafont} \cs{addhocfallbackfont}는 + 마지막 줄 명령들의 다른 이름들이다. }\,% +\footnote{% + 플레인텍에서 한글 글꼴 설정은 다음과 같이 한다:\par + \leftskip=3em\noindent + \cs{hangulfont=UnDotum\relax}\\ + \cs{hanjafont=UnDotum at 14pt}\\ + \cs{fallbackfont=HanaMinB at 12pt} } +\begin{verbatim} + \setmainhangulfont \setmainhanjafont \setmainfallbackfont + \setsanshangulfont \setsanshanjafont \setsansfallbackfont + \setmonohangulfont \setmonohanjafont \setmonofallbackfont + \newhangulfontfamily \newhanjafontfamily \newfallbackfontfamily + \newhangulfontface \newhanjafontface \newfallbackfontface + \addhangulfontfeature \addhanjafontfeature \addfallbackfontfeature + \hangulfontspec \hanjafontspec \fallbackfontspec +\end{verbatim} + +\section{글꼴 대체}\label{sec:fontswitch} + +원칙적으로 \luatexko는 지시된 글자가 영문폰트에 없으면 한글폰트에서, +한글폰트에도 없으면 한자폰트에서, 한자폰트에도 없으면 fallback +폰트에서 글자를 찾아 찍는다. +한글폰트가 지정되지 않았고 기본폰트에 한글 글리프가 없다면 +\hemph{은바탕, 은돋움 트루타입}을 자동으로 불러온다.% +\footnote{% + 은글꼴 트루타입은 \texlive에 포함되어 있다. } + +하지만 \cs{hangulbyhangulfont=1}을 선언하면 한글은 우선 한글폰트로 +식자한다. 또한 \cs{hanjabyhanjafont=1}을 선언하면 한자는 우선 한자폰트로 +식자한다. 두 경우 모두 |0|을 선언하면 원래 방식으로 되돌아간다. +문단 중간에서 사용해도 동작한다. 그러나 루아코드가 실행되므로 +텍의 그룹에 의해 영향받지 않는 전역적 효과를 가진다. + +일정한 영문 문장부호들은 한글 폰트로 식자된다. +즉, \cs{hangulpunctuations=1}이 기본으로 작동하고, +|0|을 지시해야 이 기능이 비로소 꺼진다. +verbatim 코드를 식자할 때는 이 기능이 작동하지 않는다.% +\footnote{% + 좀 더 정확히 말하면 \cs{language=1} 상황에서는 작동하지 않는다. + 부연하건대, \cs{language=1}인 상황에서 작동하지 않는 그밖의 기능들은 + 다음과 같다: 줄바꿈 허용, InterCharacter, InterHangul, InterLatinCJK, + CompressPunctuations, RemoveClassicSpaces, + 한글^^b7한자 폰트 문자 다음에 오는 공백의 크기 조정 등. }\,% +\footnote{% + 또한 플레인텍에서는 영문글꼴을 트루타입/오픈타입으로 지정해야만 작동한다. } +영향 받는 문장부호들의 기본값은 다음과 같다: +\begin{verbatim} + [0x21] ! [0x27] ' [0x28] ( [0x29] ) + [0x2C] , [0x2E] . [0x3A] : [0x3B] ; + [0x3F] ? [0x60] ` [0xB7] · [0x2014] — + [0x2015] ― [0x2018] ‘ [0x2019] ’ [0x201C] “ + [0x201D] ” [0x2026] … [0x203B] ※ +\end{verbatim} + +다음과 같은 명령으로 이 목록에 문자를 추가하거나 제거할 수 있다. +인자는 콤마로 분리된 숫자 형식으로서 유니코드 코드포인트를 뜻한다. +이들 명령은 문단 중간에서도 쓸 수 있지만, 전역적 효과를 가진다. +\begin{verbatim} + \registerpunctuations{45, "2D, `-} + \unregisterpunctuations{"2D, "2015} +\end{verbatim} + +\section{줄바꿈 허용}\label{sec:break} + +어떤 글자 앞이나 뒤에서 줄바꿈을 허용하고 싶을 때 아래와 같이 명령을 준다. +\begin{verbatim} +\registerbreakableafter{"2460, "2461} +\registerbreakablebefore{"2460, "2461} +\end{verbatim} +\luatexko는 다분히 보수적으로 줄바꿈을 허용하고 있다. +예시한 유니코드는 `①'과 `②'인데 기본적으로 +이들 문자 앞뒤 모두에서 줄바꿈이 되지 않는다. + +\section{글꼴 옵션}\label{sec:fontoption} + +fontspec의 글꼴 옵션 외에 \luatexko가 따로 제공하는 것들이 있다.% +\footnote{% + 옛한글 식자를 위해서는 적어도 |Script=Hangul| + (플레인텍이라면 |script=hang|)을 주어야 한다. + 옛한글이 아니더라도 한글을 쓴다면 이 옵션은 주는 것이 좋다. + 또한 폰트가 지원한다면 |LocalForms=On| (플레인텍이라면 |+locl|)과 + 함께 쓰는 것이 권장된다. }\,% +\footnote{% + |PunctRaise|, |NoEmbedding| 옵션은 v2.0부터 더는 제공하지 않는다. } + +\def\hunmintxt{% + \noindent\textbf{% + 世솅〮宗조ᇰ御ᅌᅥᆼ〮製졩〮訓훈〮民민正져ᇰ〮音ᅙᅳᆷ + }\par + 製졩〮ᄂᆞᆫ〮글〮지ᅀᅳᆯ〮씨〮니〮御ᅌᅥᆼ〮製졩〮ᄂᆞᆫ〮님〯금〮지〯ᅀᅳ샨〮그〮리라〮訓훈〮은〮ᄀᆞᄅᆞ칠〮씨〮오〮民% + 민ᄋᆞᆫ〮百ᄇᆡᆨ〮姓셔ᇰ〮이〮오〮音ᅙᅳᆷ은〮소리〮니〮訓훈〮民% + 민正져ᇰ〮音ᅙᅳᆷ은〮百ᄇᆡᆨ〮姓셔ᇰ〮ᄀᆞᄅᆞ치〮시논〮正져ᇰ〮ᄒᆞᆫ소리〮라〮 + \par\medskip + \noindent\textbf{% + 國귁〮之징語ᅌᅥᆼ〯音ᅙᅳᆷ이〮 + }\par + 國귁〮ᄋᆞᆫ〮나라〮히라〮之징ᄂᆞᆫ〮입〮겨〮지라〮語ᅌᅥᆼ〯는말〯ᄊᆞ미라〮 + \par + 나랏〮말〯ᄊᆞ미〮 + \par\medskip + \noindent\textbf{% + 異잉〮乎ᅘᅩᆼ中듀ᇰ國귁〮ᄒᆞ〮야〮 + }\par + 異잉〮ᄂᆞᆫ〮다ᄅᆞᆯ씨라乎ᅘᅩᆼᄂᆞᆫ〮아〯모〮그ᅌᅦᄒᆞ〮논겨〮체ᄡᅳ〮는字ᄍᆞᆼ〮ㅣ라〮中듀ᇰ國귁〮ᄋᆞᆫ〮% + 皇ᅘᅪᇰ帝뎽〮겨〯신나라〮히니〮우〮리〮나랏〮常쌰ᇰ談땀애〮江가ᇰ南남이〮라〮ᄒᆞ〮ᄂᆞ〮니라〮 + \par + 中듀ᇰ國귁〮에〮달아〮 +} + +\begin{figure} + \fboxsep=1em + \fbox{\parbox{\dimexpr\linewidth-2.1em\relax}{\sffamily + \parindent1em + \hunmintxt +}} +\caption{옛한글 조판 보기}\label{fig:yethangul} +\end{figure} + +\begin{description} + \item[InterHangul] {\addhangulfontfeature{InterHangul=.5ex}한글 글자 + 사이의 자간}. 아래는 $-0.04$em 만큼 한글 자간을 지시한다.% + \footnote{% + 플레인텍에서는 |interhangul=<dimen>|. } +\begin{verbatim} + [InterHangul=-0.04em] +\end{verbatim} + +\item[InterLatinCJK] 한글 또는 한자와 Latin문자 사이의 자간을 설정한다.% + \footnote{% + 플레인텍에서는 |interlatincjk=<dimen>|. } + 한글^^b7한자 다음에 라틴 문자가 오는 경우 원래는 줄바꿈이 되지 않지만 + 이 옵션을 주면 줄바꿈이 가능해진다. +\begin{verbatim} + [InterLatinCJK=0.25em] +\end{verbatim} + +\item[InterCharacter] CJK 글자들 사이의 자간을 지시한다.% + \footnote{% + 플레인텍에서는 |intercharacter=<dimen>|. } + fontspec의 |LetterSpace| 옵션과 기능이 유사하지만, 옛한글을 깨뜨리는 등의 + 오작동을 하지 않는다. 전술한 자간 옵션들보다 후순위로 동작한다. +\begin{verbatim} + [InterCharacter=.125em] +\end{verbatim} + +\item[InterCharStretch] CJK 글자간 가변공백{\small(글루)}의 + stretch 값을 지시한다.% + \footnote{% + 플레인텍에서는 |intercharstretch=<dimen>|. } +\begin{verbatim} + [InterCharStretch=0.5pt] +\end{verbatim} + +\item[CharRaise] 글자의 세로 위치를 + {\addhangulfontfeature{CharRaise=.3em} 조절}할 수 있는 기능이다. + 이로써 주변에 식자되는 다른 글꼴과 조화를 이루게 한다.% + \footnote{% + 플레인텍에서는 |charraise=<dimen>|. } +\begin{verbatim} + [CharRaise=2pt] +\end{verbatim} + 세로쓰기에서는 모든 글자들을 지정한 값만큼 오른쪽으로 이동시킨다. + 세로쓰기에서 이 옵션 값을 고정길이{\small(가령 |pt| 단위)}로 주면 + 크기가 다른 폰트들간에 중앙정렬이 이루어진다. + 세로쓰기에서 이 옵션을 주지 않으면 |0.5ex|가 기본값으로 동작한다. + +\item[RemoveClassicSpaces] 고문헌 조판시에 CJK 글자들 사이의 공백을 없애준다.% + \footnote{% + 플레인텍에서는 |+removeclassicspaces|. } + +\item[CompressPunctuations] CJK 구두점 {\small (낫표 따위)}의 글자폭을 + 반각으로 만든다. v2.0부터는 사용자가 지시하지 않는 한 자동으로 글자폭을 + 줄여주지 않는다.% + \footnote{% + 플레인텍에서는 |+compresspunctuations|. } + 이 옵션은 |CharacterWidth=AlternateHalf| 내지 {\small (세로쓰기의 경우)} + |Vertical=HalfMetrics|와 거의 같은 기능을 한다.% + \footnote{% + 플레인텍에서는 각각 |+halt| 및 |+vhal|이다. 이들과 완전 동일하진 + 않다. 가령 U+3002 (\char"3002) 처리의 경우.} + +\item[Protrusion] 특정 글자가 행 끌에 왔을 때 판면 바깥으로 끌어내는 + 기능이다. Lua\TeX은 기본값으로 온점 반점 등을 완전 글자내밀기 한다. + |Protrusion|은 |Protrusion=default|와 같은 뜻이다.% + \footnote{% + 플레인텍에서는 |protrusion=default|. } + 자신만의 설정을 만들어 지정할 수 있다.% + \footnote{% + default 설정을 고치는 방법은 이를테면 다음과 같다:\par + \leftskip=3em\noindent + \cs{directlua{ fonts.protrusions.setups.default[0x201C] = { 1, 0 } }} } + +\item[Expansion] 판면의 균일한 조판을 위해 글자들을 미세하게 늘이거나 + 줄이는 기능이다. |Expansion|은 |Expansion=default|와 마찬가지 뜻이다.% + \footnote{% + 플레인텍에서는 |expansion=default|. } + +\end{description} + +\section{고문헌}\label{sec:classic} + +\def\analectstext{% +子曰:「學而時習之,不亦說乎?有朋自遠方來,不亦樂乎?人不知而不慍, +不亦君子乎?」\par +有子曰:「其爲人也孝弟,而好犯上者,鮮矣!不好犯上,而好作亂者,未之有也! +君子務本,本立而道生;孝弟也者,其爲仁之本與?」\par +子曰:「巧言令色,鮮矣仁!」\par +曾子曰:「吾日三省吾身:爲人謀,而不忠乎?與朋友交,而不信乎?傳, +不習乎?」\par +子曰:「道千乘之國,敬事而信,節用而愛人,使民以時。」\par +子曰:「弟子入則孝,出則弟;謹而信,汎愛衆;而親仁,行有餘力,則以學文。」 +} +\begin{figure} + \centering + \fbox{\parbox{37em}{\typesetclassic\sffamily + \addhangulfontfeature{CompressPunctuations} + \analectstext + }} +\caption{고문헌 조판 보기. typesetclassic 환경을 이용했다.}\label{fig:gomun} +\end{figure} + +고문헌 조판을 위해 |typesetclassic| 환경을 제공한다.% +\footnote{% + 플레인텍에서는 (문서 전체에 적용하지 않는다면 그룹 안에서) + \cs{typesetclassic}을 사용한다. + |japanese|, |chinese|, |Schinese|, |Tchinese|, |korean| 따위의 환경은 + 지난 버전과의 호환성을 위해 당분간 제공되고 있을 뿐이다. } +그림~\ref{fig:gomun} 참조. + +v2.0부터는 고문헌 조판에서 한글^^b7한자와 영문자가 붙어 나오더라도 자동으로 +간격을 잡아주지 않으므로 이런 경우 사용자가 |InterLatinCJK| 옵션을 +지정해야 한다. +또한 불필요한 공백이 오더라도 자동으로 제거해주지 않으므로 이런 경우 +사용자가 |RemoveClassicSpaces| 옵션을 주어야 한다. + +|typesetmodern|은 고문헌 조판 중에 잠깐 현대 한국어를 조판하는 데 사용한다.% +\footnote{% + 플레인텍에서는 (문서 전체에 적용하지 않는다면 그룹 안에서) + \cs{typesetmodern}을 사용한다. } + +고문헌 조판 중 \luatexko가 글자 사이에 삽입하는 미세간격을 +사용자가 영{\small(zero)}으로 강제하기 위해선 \cs{inhibitglue} 명령을 이용한다. + +\section{세로쓰기}\label{sec:verttype} + +세로쓰기는 폰트의 고급 오픈타입 속성을 이용하므로 폰트가 이를 지원해야 +가능한 일이다. 폰트에 |Vertical=Alternates|와 |RawFeature=vertical| 옵션을 +준다.% +\footnote{% + 이는 플레인텍에서 |vertical;+vert| 옵션을 주는 것과 같다. + 사실 |vertical|을 선언하면 |vert|는 자동으로 켜지도록 해 두었다. }\,% +\footnote{% + |vmtx| 정보를 가지고 있지 않은 글꼴은 세로쓰기에 적합치 않은 글꼴이다. + |otfinfo -t <파일>| 명령으로 글꼴에 들어있는 테이블 정보를 알 수 있다. } +세로쓰기에서 |CharRaise| 옵션의 효과에 대해서는 제\ref{sec:fontoption}절을 +참조할 것. + +세로쓰기에서는 \cs{typesetclassic}의 아종인 \cs{typesetvertical}이 +기본으로 셋팅되어 있다. 일종의 고문헌 조판으로 보는 것이다. + +\begin{figure} +\framebox[\linewidth]{\begin{vertical}{19.6em}\sffamily + \addhangulfontfeature{Vertical=Alternates, RawFeature=vertical} + \linespread{1.5}\selectfont + \hunmintxt +\end{vertical}} +\caption{세로쓰기의 예. 박스 높이 |20em|을 지시했다.}\label{fig:vertical} +\end{figure} + +\begin{figure} +\framebox[\linewidth]{\begin{vertical}{18.62em}\sffamily + \addhangulfontfeature{Vertical=Alternates, RemoveClassicSpaces, + InterCharStretch=1pt, CompressPunctuations, RawFeature=vertical} + \linespread{1.5}\selectfont + \analectstext +\end{vertical}} +\caption{세로쓰기의 다른 예. 박스 높이 |19em|을 지시했다. + 글꼴옵션으로 |RemoveClassicSpaces|, |CompressPunctuations|와 더불어 + |InterCharStretch=1pt|를 추가했다. 공백이 없는 문서는 자간늘이기 값을 + 넉넉하게 주는 것이 좋다.}\label{fig:vertical3} +\end{figure} + +문서의 일부를 세로쓰기하려면 \cs{begin{vertical}{<dimen>}} \ldots\ \cs{end{vertical}} +환경을 이용한다. |<dimen>|으로 세로쓰기 박스의 높이를 지시한다.% +\footnote{% + 플레인텍에서는 \cs{vertical{<dimen>}} \ldots\ \cs{endvertical}. } +그림~\ref{fig:vertical},~\ref{fig:vertical3} 및 \ref{fig:vertical2} 참조. + +문서 전체를 세로쓰기한다면 이 환경을 쓰는 대신 +\cs{verticaltypesetting} 명령을 전처리부에 선언한다. +이때 면주는 가로로 식자되며 면주 폰트의 설정은 사용자의 몫이다. + +세로쓰기 도중에 문서의 일부를 가로쓰기하려면 +\cs{begin{horizontal}{<dimen>}} \ldots\ \cs{end{horizontal}} +환경을 이용한다. |<dimen>|은 가로쓰기 박스의 너비를 지시한다.% +\footnote{% + 플레인텍에서는 \cs{horizontal{<dimen>}} \ldots\ \cs{endhorizontal}. } +가로쓰기 영역의 폰트 설정은 사용자의 몫이다.% +\footnote{폰트 지시는 |horizontal| 환경 안에서 행하는 것을 권장한다.} + +\begin{figure} +\framebox[\linewidth]{\begin{vertical}{16.66em}\sffamily + \addhangulfontfeature{Vertical=Alternates, CharRaise=3.2pt, + CompressPunctuations, CharacterWidth=Full, RawFeature=vertical} + \parindent-1em\leftskip1em \linespread{1.5}\selectfont + \spaceskip=.5em plus.25em minus.125em + \noindent 님의 침묵(The Silent Beloved) + \smallbreak + \hfil\hfil 한 용 운\hfil + \bigbreak + 님은 갓슴니다 아아 사랑하는나의님은 갓슴니다\par + 푸른산빗을깨치고 단풍나무숩을향하야난 적은길을 거러서 참어떨치고 + 갓슴니다\par + 黃金의꽃가티 굿고빗나든 옛盟誓는 차듸찬띠끌이되야서 한숨의 微風에 + 나러갓슴니다\par + 날카로은 첫〈키쓰〉의追憶은 나의運命의指針을 돌너노코 뒷거름처서 사러젓슴니다\par + \hellipsis\par + 아아 님은갓지마는 나는 님을보내지 아니하얏슴니다\par + 제곡조를못이기는 사랑의노래는 님의沈默을 휩싸고돔니다\par +\end{vertical}} +\caption{세로쓰기의 또 다른 예. 박스 높이 |17em|을 지시했다. + Noto Sans CJK 글꼴의 세로쓰기 공백은 원래 |1em|, + 즉 전각크기로 매우 크기 때문에, 여기서는 \cs{spaceskip} 명령을 이용하여 + 반각 크기로 강제했다.}\label{fig:vertical2} +\end{figure} + +\section{드러냄표}\label{sec:dotemph} + +\cs{dotemph} 명령으로 \dotemph{드러냄표}% +를 이용한 강조를 할 수 있다. 기본은 글자 위에 점을 찍는 형태이나 +다음과 같이 명령을 주어 개인적으로 선호하는 기호를 드러냄표로 쓸 수 +있다. + +\medskip +①~|\def\dotemphraise{0.4em }|: 드러냄표를 피강조 글자 위로 끌어올리는 길이 + +②~|\def\dotemphchar{\bfseries ^^^^02d9}|: 드러냄표 기호 자체를 정의. + +\section{루비}\label{sec:ruby} + +루비를 달 수 있다. ruby 패키지가 이미 존재하지만 \luatexko와 궁합이 잘 +맞지 않아 새로 매크로를 제공한다. +\begin{quote} + \cs{ruby{漢字}{한자}}\quad$\Rightarrow$\quad\ruby{漢字}{한자} +\end{quote} +이처럼 글자별로 따로 루비를 달 필요가 없다. 관련 설정은 다음처럼 +한다. + +\medskip +①~\cs{rubyfont}: 루비를 식자할 폰트를 지시해 둔다. 기본값은 현재 폰트% +\footnote{% + 루비는 글자 크기가 작기 때문에 본문 폰트보다 약간 굵은 폰트로 + 지정하는 것이 좋다. } + +②~|\def\rubysize{0.6}|: 루비 글자 크기를 본문 글자 크기에 대한 비율로 +지정% +\footnote{% + 플레인텍에서는 루비의 글꼴크기를 따로 조정해주지 않는다. + 사용자가 \cs{rubyfont}를 설정할 때 크기도 함께 지시하도록 한다. + 가령:\par + \leftskip=3em\noindent + |\\font\\rubyfont=UnBatang at \\rubysize em| } + +③~|\def\rubysep{0.1ex}|: 루비와 본문 글자 사이의 간격을 지정 + +④~\cs{rubynooverlap}: 루비의 폭이 본문 글자의 폭보다 클 때 루비가 이웃 +글자들 위로 삐져나가지 못하게 한다. 본문 글자의 흐름을 중시하여 +\cs{rubyoverlap}을 기본값으로 하였으므로 이는 따로 선언할 필요가 없다. + +\medskip +한편, 연속된 본문 글자들에 각각 한 글자씩 루비를 달고자 한다면 +\cs{xxruby{...}{...}} 명령을 사용한다. 글자들 사이에 줄바꿈이 허용된다. +두 인자의 글자 수가 동일해야 한다. + +\section{밑줄긋기}\label{sec:uline} + +ulem 패키지가 \luatexko와 궁합이 잘 맞지 않아{\small (줄바꿈에 문제가 있음)} +명령을 따로 제공한다. + +\def\RA{\>$\Rightarrow$\>} +\begin{tabbing} + \cs{dotuline{밑줄을 점선으로 긋는다}}\quad\=$\Rightarrow$\quad\= \kill + \cs{uline{밑줄을 그을 수 있다}}\RA\uline{밑줄을 그을 수 있다}\\ + \cs{sout{취소선을 그을 수 있다}}\RA\sout{취소선을 그을 수 있다}\\ + \cs{uuline{밑줄을 두 줄 긋는다}}\RA\uuline{밑줄을 두 줄 긋는다}\\ + \cs{xout{빗금으로 취소할 수 있다}}\RA\xout{빗금으로 취소할 수 있다}\\ + \cs{uwave{물결표로 밑줄을 삼는다}}\RA\uwave{물결표로 밑줄을 삼는다}\\ + \cs{dashuline{대시로 밑줄을 삼는다}}\RA\dashuline{대시로 밑줄을 삼는다}\\ + \cs{dotuline{밑줄을 점선으로 긋는다}}\RA\dotuline{밑줄을 점선으로 긋는다} +\end{tabbing} + +관련하여 다음 설정을 할 수 있다. + +\medskip +①~|\def\ulinedown{0.5ex}|: 밑줄을 베이스라인 아래로 끌어내리는 정도 + +②~|\def\ulinewidth{0.04em}|: 밑줄의 굵기 + +\medskip +사실 위 밑줄 양식들을 만드는 데는 예외없이 \cs{markoverwith} 명령이 사용되었다. +따라서 사용자는 이를 이용해 원하는 양식을 만들 수 있다. +가령 노란색 마커펜을 흉내내려면,% +\footnote{% + 이 명령의 인자로 들어갈 수 있는 것은 box와 rule에 국한된다. + 예시에서는 박스를 넣었다.}\,% +\footnote{% + 별표 붙은 명령 \cs{markoverwith*}는 \cs{xleaders}를 실행하고, + 예시처럼 별표가 없으면 \cs{cleaders}를 실행한다. } +\begin{verbatim} + \def\ymarker{% + \markoverwith{% + \hbox{{\color{yellow!50}\vrule width 1pt height 9pt depth 2pt}}% + }} +\end{verbatim} + +\def\ymarker{\markoverwith{% + \hbox{{\color{yellow!50}\vrule width 1pt height 9pt depth 2pt}}}} + +\cs{ymarker{노란색을 칠해서 강조한다}}\quad$\Rightarrow$\quad +\ymarker{노란색을 칠해서 강조한다}. + +\section{자동조사}\label{sec:autojosa} + +자동조사는 \kotex 과 동일하게 \cs{은} \cs{는} \cs{이} \cs{가} \cs{을} \cs{를} +\cs{와} \cs{과} \cs{로} \cs{으로} \cs{라} \cs{이라} 따위를 사용한다. +버전 1.3부터는 \cs{josaignoreparens=1}이 선언되어 있으면 자동조사는 +\hemph{괄호 부분을 건너뛰고} 그 앞 글자에 매칭한다. +|0|이 선언되면 원래 방식으로 돌아간다. +\begin{quote} + \cs{josaignoreparens=1} \josaignoreparens=1 \\ + |홍길동(1992)\는|\quad$\Rightarrow$\quad 홍길동(1992)\는\\ + |홍길동(2001)\로|\quad$\Rightarrow$\quad 홍길동(2001)\로\par + \cs{josaignoreparens=0} \josaignoreparens=0 \\ + |홍길동(1992)\는|\quad$\Rightarrow$\quad 홍길동(1992)\는\\ + |홍길동(2001)\로|\quad$\Rightarrow$\quad 홍길동(2001)\로 +\end{quote} + +\section{루아 모듈의 제거 및 복원}\label{sec:luamodule} + +루아 코드 |luatexko.deactivateall()|은 이상 언급한 \luatexko 의 거의 모든 +기능을 무력화한다. |luatexko.reactivateall()|은 이를 복원한다.% +\footnote{수직 모드 (문단과 문단 사이)에서 사용할 것. + 수평 모드에서는 정상작동을 보장하지 못한다.} +다른 패키지와 충돌할 때 시도해 볼 수 있다. +전자의 함수에는 문자열을 인자로 줄 수도 있는데, 해당하는 다른 패키지의 +모듈들을 잠시 제거할 수 있다. + +\section{수식 한글}\label{sec:mathhangul} + +\begin{quote} + |$가^{나^다}$|\quad$\Rightarrow\quad가^{나^다}$ +\end{quote} +수식 한글 폰트를 설정하면 수식 모드에서도 한글을 {\small(hbox로 감싸지 않고)} +직접 입력할 수 있다.% +\footnote{% + 플레인텍에서는 \cs{mathhangulfont=UnBatang}과 같이 설정한다. + 또는 좀 더 그럴듯하게 하려면:\par + \leftskip=3em\noindent + |\\font\\texthangul="Noto Sans CJK KR DemiLight" at 10pt|\\ + |\\font\\scripthangul="Noto Sans CJK KR Regular" at 7pt|\\ + |\\font\\scriptscripthangul="Noto Sans CJK KR Medium" at 5pt|\\ + |\\setmathhangulfonts\\texthangul\\scripthangul\\scriptscripthangul| } +v2.0부터는 자동으로 수식 한글을 잡아주지 않는다. +\begin{verbatim} + \setmathhangulfont{Noto Sans CJK KR}[ + SizeFeatures={ + {Size=-6, Font=* Medium}, + {Size=6-9, Font=* Regular}, + {Size=9-, Font=* DemiLight}, + } ] +\end{verbatim} +현재 한글만 쓸 수 있게 설정되어 있다. +한자도 수식에 직접 입력하려면 사용자는 +다음 명령으로 유니코드 블럭을 추가 지정해야 한다. +\begin{verbatim} + \setmathhangulblock{4E00}{9FC3} +\end{verbatim} + +\section{한국어 항목 번호}\label{sec:hangulnums} + +\kotex과 동일하게 항목 번호를 한국어 기호로 붙일 수 있다.% +\footnote{% + \cs{jaso} \cs{gana} \cs{ojaso} \cs{ogana} \cs{pjaso} \cs{pgana} + \cs{onum} \cs{pnum} \cs{oeng} \cs{peng} \cs{hnum} \cs{Hnum} + \cs{hroman} \cs{hRoman} \cs{hNum} \cs{hanjanum} 따위를 사용한다. } + +\section{입력 변환}\label{sec:noramlize} + +\cs{luatexhangulnormalize=1}이라 지시하면 첫가끝 자모를 완성형 음절로, +|2|라면 완성형 음절을 첫가끝 자모로 인코딩 변환한다. |0|이 할당되면 +인코딩 변환 기능이 꺼진다. \XeTeX의 \cs{XeTeXinputnormalization} 명령과 +유사하나, 오직 한글과 일부 한자에 대해서만 정규화가 작동할 뿐이다. + +\section{UHC 입력 인코딩}\label{sec:uhcencoding} + +권장하지 않지만 불가피하게 입력 인코딩이 UHC (Unified Hangul Code)% +\footnote{% + CP949라고도 하며 EUC-KR을 포함한다. }% +로 되어 있는 파일을 처리할 때는 +\cs{luatexuhcinputencoding=1}을 선언한다. +|0|을 할당하면 다시 UTF-8 입력으로 간주한다. +\XeTeX의 \cs{XeTeXinputencoding} 명령과 유사하나, 오직 한국어 문자만 처리할 수 +있다.% +\footnote{% + 윈도 운영체제에서의 한글 파일이름 불러오기 기능은 v2.0부터는 제공하지 않는다. + 대신 |luatex -cmdx ...|, |lualatex -cmdx ...|와 같이 컴파일시 명령행에 + |-cmdx| 옵션을 주면 된다고 하니 테스트해 보시기 바란다. } +\hfill \fboxsep=-\fboxrule \fbox{\vbox to 1em{\hbox to 1em{\hss}\vss}} + +\end{document} diff --git a/macros/luatex/generic/luatexko/luatexko-normalize.lua b/macros/luatex/generic/luatexko/luatexko-normalize.lua new file mode 100644 index 0000000000..97c2b0ccf8 --- /dev/null +++ b/macros/luatex/generic/luatexko/luatexko-normalize.lua @@ -0,0 +1,620 @@ +-- luatexko-normalize.lua +-- +-- Copyright (c) 2013-2019 Dohyun Kim <nomos at ktug org> +-- Soojin Nam <jsunam at gmail com> +-- +-- This work may be distributed and/or modified under the +-- conditions of the LaTeX Project Public License, either version 1.3c +-- 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.3c or later is part of all distributions of LaTeX +-- version 2006/05/20 or later. + +luatexbase.provides_module({ + name = "luatexko-normalize", + version = "2.4", + date = "2019/08/01", + author = "Dohyun Kim, Soojin Nam", + description = "Hangul normalization", + license = "LPPL v1.3+", +}) + +luatexko = luatexko or {} +luatexko.normalize = luatexko.normalize or {} +local luatexkonormalize = luatexko.normalize + +local utf8codes = utf8.codes +local utf8char = utf8.char +local tableinsert = table.insert +local tableappend = table.append +local tableunpack = table.unpack + +local chanjatohanja = { + [0xF900] = {0x8C48, 0xFE00}, + [0xF901] = {0x66F4, 0xFE00}, + [0xF902] = {0x8ECA, 0xFE00}, + [0xF903] = {0x8CC8, 0xFE00}, + [0xF904] = {0x6ED1, 0xFE00}, + [0xF905] = {0x4E32, 0xFE00}, + [0xF906] = {0x53E5, 0xFE00}, + [0xF907] = {0x9F9C, 0xFE00}, + [0xF908] = {0x9F9C, 0xFE01}, + [0xF909] = {0x5951, 0xFE00}, + [0xF90A] = {0x91D1, 0xFE00}, + [0xF90B] = {0x5587, 0xFE00}, + [0xF90C] = {0x5948, 0xFE00}, + [0xF90D] = {0x61F6, 0xFE00}, + [0xF90E] = {0x7669, 0xFE00}, + [0xF90F] = {0x7F85, 0xFE00}, + [0xF910] = {0x863F, 0xFE00}, + [0xF911] = {0x87BA, 0xFE00}, + [0xF912] = {0x88F8, 0xFE00}, + [0xF913] = {0x908F, 0xFE00}, + [0xF914] = {0x6A02, 0xFE00}, + [0xF915] = {0x6D1B, 0xFE00}, + [0xF916] = {0x70D9, 0xFE00}, + [0xF917] = {0x73DE, 0xFE00}, + [0xF918] = {0x843D, 0xFE00}, + [0xF919] = {0x916A, 0xFE00}, + [0xF91A] = {0x99F1, 0xFE00}, + [0xF91B] = {0x4E82, 0xFE00}, + [0xF91C] = {0x5375, 0xFE00}, + [0xF91D] = {0x6B04, 0xFE00}, + [0xF91E] = {0x721B, 0xFE00}, + [0xF91F] = {0x862D, 0xFE00}, + [0xF920] = {0x9E1E, 0xFE00}, + [0xF921] = {0x5D50, 0xFE00}, + [0xF922] = {0x6FEB, 0xFE00}, + [0xF923] = {0x85CD, 0xFE00}, + [0xF924] = {0x8964, 0xFE00}, + [0xF925] = {0x62C9, 0xFE00}, + [0xF926] = {0x81D8, 0xFE00}, + [0xF927] = {0x881F, 0xFE00}, + [0xF928] = {0x5ECA, 0xFE00}, + [0xF929] = {0x6717, 0xFE00}, + [0xF92A] = {0x6D6A, 0xFE00}, + [0xF92B] = {0x72FC, 0xFE00}, + [0xF92C] = {0x90CE, 0xFE00}, + [0xF92D] = {0x4F86, 0xFE00}, + [0xF92E] = {0x51B7, 0xFE00}, + [0xF92F] = {0x52DE, 0xFE00}, + [0xF930] = {0x64C4, 0xFE00}, + [0xF931] = {0x6AD3, 0xFE00}, + [0xF932] = {0x7210, 0xFE00}, + [0xF933] = {0x76E7, 0xFE00}, + [0xF934] = {0x8001, 0xFE00}, + [0xF935] = {0x8606, 0xFE00}, + [0xF936] = {0x865C, 0xFE00}, + [0xF937] = {0x8DEF, 0xFE00}, + [0xF938] = {0x9732, 0xFE00}, + [0xF939] = {0x9B6F, 0xFE00}, + [0xF93A] = {0x9DFA, 0xFE00}, + [0xF93B] = {0x788C, 0xFE00}, + [0xF93C] = {0x797F, 0xFE00}, + [0xF93D] = {0x7DA0, 0xFE00}, + [0xF93E] = {0x83C9, 0xFE00}, + [0xF93F] = {0x9304, 0xFE00}, + [0xF940] = {0x9E7F, 0xFE00}, + [0xF941] = {0x8AD6, 0xFE00}, + [0xF942] = {0x58DF, 0xFE00}, + [0xF943] = {0x5F04, 0xFE00}, + [0xF944] = {0x7C60, 0xFE00}, + [0xF945] = {0x807E, 0xFE00}, + [0xF946] = {0x7262, 0xFE00}, + [0xF947] = {0x78CA, 0xFE00}, + [0xF948] = {0x8CC2, 0xFE00}, + [0xF949] = {0x96F7, 0xFE00}, + [0xF94A] = {0x58D8, 0xFE00}, + [0xF94B] = {0x5C62, 0xFE00}, + [0xF94C] = {0x6A13, 0xFE00}, + [0xF94D] = {0x6DDA, 0xFE00}, + [0xF94E] = {0x6F0F, 0xFE00}, + [0xF94F] = {0x7D2F, 0xFE00}, + [0xF950] = {0x7E37, 0xFE00}, + [0xF951] = {0x964B, 0xFE00}, + [0xF952] = {0x52D2, 0xFE00}, + [0xF953] = {0x808B, 0xFE00}, + [0xF954] = {0x51DC, 0xFE00}, + [0xF955] = {0x51CC, 0xFE00}, + [0xF956] = {0x7A1C, 0xFE00}, + [0xF957] = {0x7DBE, 0xFE00}, + [0xF958] = {0x83F1, 0xFE00}, + [0xF959] = {0x9675, 0xFE00}, + [0xF95A] = {0x8B80, 0xFE00}, + [0xF95B] = {0x62CF, 0xFE00}, + [0xF95C] = {0x6A02, 0xFE01}, + [0xF95D] = {0x8AFE, 0xFE00}, + [0xF95E] = {0x4E39, 0xFE00}, + [0xF95F] = {0x5BE7, 0xFE00}, + [0xF960] = {0x6012, 0xFE00}, + [0xF961] = {0x7387, 0xFE00}, + [0xF962] = {0x7570, 0xFE00}, + [0xF963] = {0x5317, 0xFE00}, + [0xF964] = {0x78FB, 0xFE00}, + [0xF965] = {0x4FBF, 0xFE00}, + [0xF966] = {0x5FA9, 0xFE00}, + [0xF967] = {0x4E0D, 0xFE00}, + [0xF968] = {0x6CCC, 0xFE00}, + [0xF969] = {0x6578, 0xFE00}, + [0xF96A] = {0x7D22, 0xFE00}, + [0xF96B] = {0x53C3, 0xFE00}, + [0xF96C] = {0x585E, 0xFE00}, + [0xF96D] = {0x7701, 0xFE00}, + [0xF96E] = {0x8449, 0xFE00}, + [0xF96F] = {0x8AAA, 0xFE00}, + [0xF970] = {0x6BBA, 0xFE00}, + [0xF971] = {0x8FB0, 0xFE00}, + [0xF972] = {0x6C88, 0xFE00}, + [0xF973] = {0x62FE, 0xFE00}, + [0xF974] = {0x82E5, 0xFE00}, + [0xF975] = {0x63A0, 0xFE00}, + [0xF976] = {0x7565, 0xFE00}, + [0xF977] = {0x4EAE, 0xFE00}, + [0xF978] = {0x5169, 0xFE00}, + [0xF979] = {0x51C9, 0xFE00}, + [0xF97A] = {0x6881, 0xFE00}, + [0xF97B] = {0x7CE7, 0xFE00}, + [0xF97C] = {0x826F, 0xFE00}, + [0xF97D] = {0x8AD2, 0xFE00}, + [0xF97E] = {0x91CF, 0xFE00}, + [0xF97F] = {0x52F5, 0xFE00}, + [0xF980] = {0x5442, 0xFE00}, + [0xF981] = {0x5973, 0xFE00}, + [0xF982] = {0x5EEC, 0xFE00}, + [0xF983] = {0x65C5, 0xFE00}, + [0xF984] = {0x6FFE, 0xFE00}, + [0xF985] = {0x792A, 0xFE00}, + [0xF986] = {0x95AD, 0xFE00}, + [0xF987] = {0x9A6A, 0xFE00}, + [0xF988] = {0x9E97, 0xFE00}, + [0xF989] = {0x9ECE, 0xFE00}, + [0xF98A] = {0x529B, 0xFE00}, + [0xF98B] = {0x66C6, 0xFE00}, + [0xF98C] = {0x6B77, 0xFE00}, + [0xF98D] = {0x8F62, 0xFE00}, + [0xF98E] = {0x5E74, 0xFE00}, + [0xF98F] = {0x6190, 0xFE00}, + [0xF990] = {0x6200, 0xFE00}, + [0xF991] = {0x649A, 0xFE00}, + [0xF992] = {0x6F23, 0xFE00}, + [0xF993] = {0x7149, 0xFE00}, + [0xF994] = {0x7489, 0xFE00}, + [0xF995] = {0x79CA, 0xFE00}, + [0xF996] = {0x7DF4, 0xFE00}, + [0xF997] = {0x806F, 0xFE00}, + [0xF998] = {0x8F26, 0xFE00}, + [0xF999] = {0x84EE, 0xFE00}, + [0xF99A] = {0x9023, 0xFE00}, + [0xF99B] = {0x934A, 0xFE00}, + [0xF99C] = {0x5217, 0xFE00}, + [0xF99D] = {0x52A3, 0xFE00}, + [0xF99E] = {0x54BD, 0xFE00}, + [0xF99F] = {0x70C8, 0xFE00}, + [0xF9A0] = {0x88C2, 0xFE00}, + [0xF9A1] = {0x8AAA, 0xFE01}, + [0xF9A2] = {0x5EC9, 0xFE00}, + [0xF9A3] = {0x5FF5, 0xFE00}, + [0xF9A4] = {0x637B, 0xFE00}, + [0xF9A5] = {0x6BAE, 0xFE00}, + [0xF9A6] = {0x7C3E, 0xFE00}, + [0xF9A7] = {0x7375, 0xFE00}, + [0xF9A8] = {0x4EE4, 0xFE00}, + [0xF9A9] = {0x56F9, 0xFE00}, + [0xF9AA] = {0x5BE7, 0xFE01}, + [0xF9AB] = {0x5DBA, 0xFE00}, + [0xF9AC] = {0x601C, 0xFE00}, + [0xF9AD] = {0x73B2, 0xFE00}, + [0xF9AE] = {0x7469, 0xFE00}, + [0xF9AF] = {0x7F9A, 0xFE00}, + [0xF9B0] = {0x8046, 0xFE00}, + [0xF9B1] = {0x9234, 0xFE00}, + [0xF9B2] = {0x96F6, 0xFE00}, + [0xF9B3] = {0x9748, 0xFE00}, + [0xF9B4] = {0x9818, 0xFE00}, + [0xF9B5] = {0x4F8B, 0xFE00}, + [0xF9B6] = {0x79AE, 0xFE00}, + [0xF9B7] = {0x91B4, 0xFE00}, + [0xF9B8] = {0x96B8, 0xFE00}, + [0xF9B9] = {0x60E1, 0xFE00}, + [0xF9BA] = {0x4E86, 0xFE00}, + [0xF9BB] = {0x50DA, 0xFE00}, + [0xF9BC] = {0x5BEE, 0xFE00}, + [0xF9BD] = {0x5C3F, 0xFE00}, + [0xF9BE] = {0x6599, 0xFE00}, + [0xF9BF] = {0x6A02, 0xFE02}, + [0xF9C0] = {0x71CE, 0xFE00}, + [0xF9C1] = {0x7642, 0xFE00}, + [0xF9C2] = {0x84FC, 0xFE00}, + [0xF9C3] = {0x907C, 0xFE00}, + [0xF9C4] = {0x9F8D, 0xFE00}, + [0xF9C5] = {0x6688, 0xFE00}, + [0xF9C6] = {0x962E, 0xFE00}, + [0xF9C7] = {0x5289, 0xFE00}, + [0xF9C8] = {0x677B, 0xFE00}, + [0xF9C9] = {0x67F3, 0xFE00}, + [0xF9CA] = {0x6D41, 0xFE00}, + [0xF9CB] = {0x6E9C, 0xFE00}, + [0xF9CC] = {0x7409, 0xFE00}, + [0xF9CD] = {0x7559, 0xFE00}, + [0xF9CE] = {0x786B, 0xFE00}, + [0xF9CF] = {0x7D10, 0xFE00}, + [0xF9D0] = {0x985E, 0xFE00}, + [0xF9D1] = {0x516D, 0xFE00}, + [0xF9D2] = {0x622E, 0xFE00}, + [0xF9D3] = {0x9678, 0xFE00}, + [0xF9D4] = {0x502B, 0xFE00}, + [0xF9D5] = {0x5D19, 0xFE00}, + [0xF9D6] = {0x6DEA, 0xFE00}, + [0xF9D7] = {0x8F2A, 0xFE00}, + [0xF9D8] = {0x5F8B, 0xFE00}, + [0xF9D9] = {0x6144, 0xFE00}, + [0xF9DA] = {0x6817, 0xFE00}, + [0xF9DB] = {0x7387, 0xFE01}, + [0xF9DC] = {0x9686, 0xFE00}, + [0xF9DD] = {0x5229, 0xFE00}, + [0xF9DE] = {0x540F, 0xFE00}, + [0xF9DF] = {0x5C65, 0xFE00}, + [0xF9E0] = {0x6613, 0xFE00}, + [0xF9E1] = {0x674E, 0xFE00}, + [0xF9E2] = {0x68A8, 0xFE00}, + [0xF9E3] = {0x6CE5, 0xFE00}, + [0xF9E4] = {0x7406, 0xFE00}, + [0xF9E5] = {0x75E2, 0xFE00}, + [0xF9E6] = {0x7F79, 0xFE00}, + [0xF9E7] = {0x88CF, 0xFE00}, + [0xF9E8] = {0x88E1, 0xFE00}, + [0xF9E9] = {0x91CC, 0xFE00}, + [0xF9EA] = {0x96E2, 0xFE00}, + [0xF9EB] = {0x533F, 0xFE00}, + [0xF9EC] = {0x6EBA, 0xFE00}, + [0xF9ED] = {0x541D, 0xFE00}, + [0xF9EE] = {0x71D0, 0xFE00}, + [0xF9EF] = {0x7498, 0xFE00}, + [0xF9F0] = {0x85FA, 0xFE00}, + [0xF9F1] = {0x96A3, 0xFE00}, + [0xF9F2] = {0x9C57, 0xFE00}, + [0xF9F3] = {0x9E9F, 0xFE00}, + [0xF9F4] = {0x6797, 0xFE00}, + [0xF9F5] = {0x6DCB, 0xFE00}, + [0xF9F6] = {0x81E8, 0xFE00}, + [0xF9F7] = {0x7ACB, 0xFE00}, + [0xF9F8] = {0x7B20, 0xFE00}, + [0xF9F9] = {0x7C92, 0xFE00}, + [0xF9FA] = {0x72C0, 0xFE00}, + [0xF9FB] = {0x7099, 0xFE00}, + [0xF9FC] = {0x8B58, 0xFE00}, + [0xF9FD] = {0x4EC0, 0xFE00}, + [0xF9FE] = {0x8336, 0xFE00}, + [0xF9FF] = {0x523A, 0xFE00}, + [0xFA00] = {0x5207, 0xFE00}, + [0xFA01] = {0x5EA6, 0xFE00}, + [0xFA02] = {0x62D3, 0xFE00}, + [0xFA03] = {0x7CD6, 0xFE00}, + [0xFA04] = {0x5B85, 0xFE00}, + [0xFA05] = {0x6D1E, 0xFE00}, + [0xFA06] = {0x66B4, 0xFE00}, + [0xFA07] = {0x8F3B, 0xFE00}, + [0xFA08] = {0x884C, 0xFE00}, + [0xFA09] = {0x964D, 0xFE00}, + [0xFA0A] = {0x898B, 0xFE00}, + [0xFA0B] = {0x5ED3, 0xFE00}, + [0xFA2E] = {0x90DE, 0xFE00}, + [0xFA2F] = {0x96B7, 0xFE00}, +} +local jamotocjamo = { + ccho = { + [0x1100] = 0x3131, + [0x1101] = 0x3132, + -- [0x11AA] = 0x3133, + [0x1102] = 0x3134, + -- [0x11AC] = 0x3135, + -- [0x11AD] = 0x3136, + [0x1103] = 0x3137, + [0x1104] = 0x3138, + [0x1105] = 0x3139, + -- [0x11B0] = 0x313A, + -- [0x11B1] = 0x313B, + -- [0x11B2] = 0x313C, + -- [0x11B3] = 0x313D, + -- [0x11B4] = 0x313E, + -- [0x11B5] = 0x313F, + [0x111A] = 0x3140, + [0x1106] = 0x3141, + [0x1107] = 0x3142, + [0x1108] = 0x3143, + [0x1121] = 0x3144, + [0x1109] = 0x3145, + [0x110A] = 0x3146, + [0x110B] = 0x3147, + [0x110C] = 0x3148, + [0x110D] = 0x3149, + [0x110E] = 0x314A, + [0x110F] = 0x314B, + [0x1110] = 0x314C, + [0x1111] = 0x314D, + [0x1112] = 0x314E, + [0x1114] = 0x3165, + [0x1115] = 0x3166, + -- [0x11C7] = 0x3167, + -- [0x11C8] = 0x3168, + -- [0x11CC] = 0x3169, + -- [0x11CE] = 0x316A, + -- [0x11D3] = 0x316B, + -- [0x11D7] = 0x316C, + -- [0x11D9] = 0x316D, + [0x111C] = 0x316E, + -- [0x11DD] = 0x316F, + -- [0x11DF] = 0x3170, + [0x111D] = 0x3171, + [0x111E] = 0x3172, + [0x1120] = 0x3173, + [0x1122] = 0x3174, + [0x1123] = 0x3175, + [0x1127] = 0x3176, + [0x1129] = 0x3177, + [0x112B] = 0x3178, + [0x112C] = 0x3179, + [0x112D] = 0x317A, + [0x112E] = 0x317B, + [0x112F] = 0x317C, + [0x1132] = 0x317D, + [0x1136] = 0x317E, + [0x1140] = 0x317F, + [0x1147] = 0x3180, + [0x114C] = 0x3181, + -- [0x11F1] = 0x3182, + -- [0x11F2] = 0x3183, + [0x1157] = 0x3184, + [0x1158] = 0x3185, + [0x1159] = 0x3186, + }, + cjung = { + [0x1161] = 0x314F, + [0x1162] = 0x3150, + [0x1163] = 0x3151, + [0x1164] = 0x3152, + [0x1165] = 0x3153, + [0x1166] = 0x3154, + [0x1167] = 0x3155, + [0x1168] = 0x3156, + [0x1169] = 0x3157, + [0x116A] = 0x3158, + [0x116B] = 0x3159, + [0x116C] = 0x315A, + [0x116D] = 0x315B, + [0x116E] = 0x315C, + [0x116F] = 0x315D, + [0x1170] = 0x315E, + [0x1171] = 0x315F, + [0x1172] = 0x3160, + [0x1173] = 0x3161, + [0x1174] = 0x3162, + [0x1175] = 0x3163, + -- [0x1160] = 0x3164, + [0x1184] = 0x3187, + [0x1185] = 0x3188, + [0x1188] = 0x3189, + [0x1191] = 0x318A, + [0x1192] = 0x318B, + [0x1194] = 0x318C, + [0x119E] = 0x318D, + [0x11A1] = 0x318E, + } +} + +local function is_hangul (c) + return c >= 0xAC00 and c <= 0xD7A3 +end + +local function is_modern_cho (c) + return c >= 0x1100 and c <= 0x1112 +end + +local function is_modern_jung (c) + return c >= 0x1161 and c <= 0x1175 +end + +local function is_modern_jong (c) + return c >= 0x11A8 and c <= 0x11C2 +end + +local function is_old_jong (c) + return c >= 0x11C3 and c <= 0x11FF + or c >= 0xD7CB and c <= 0xD7FB +end + +local function is_jongsong (c) + return is_modern_jong(c) or is_old_jong(c) +end + +local function jamo2syllable (t) -- table -> integer + local cho, jung, jong = tableunpack(t) + local s = (cho - 0x1100) * 21 + s = (s + jung - 0x1161) * 28 + if jong then + s = s + jong - 0x11A7 + end + return s + 0xAC00 +end + +local function syllable2jamo (s) -- integer -> table + local t = {} + s = s - 0xAC00 + t[1] = s // 588 + 0x1100 + t[2] = s % 588 // 28 + 0x1161 + local jong = s % 28 + if jong ~= 0 then + t[3] = jong + 0x11A7 + end + return t +end + +local function hanguldecompose (buffer) + local t = {} + for _, c in utf8codes(buffer) do + if is_hangul(c) then + tableappend(t, syllable2jamo(c)) + else + tableinsert(t, c) + end + end + return utf8char(tableunpack(t)) +end + +-- LV | LVT, T -> L, V, T+ +local function flush_syllable_jong (t, s) + if #s == 2 then + tableappend(t, syllable2jamo( s[1] )) + tableinsert(t, s[2]) + else + tableappend(t, s) + end + return t, {} +end + +local function compose_hanguldecompose (buffer) -- string -> table + local t, s = {}, {} + for _, c in utf8codes(buffer) do + if #s == 1 and is_jongsong(c) then + tableinsert(s, c) + else + t, s = flush_syllable_jong(t, s) + tableinsert(is_hangul(c) and s or t, c) + end + end + t = flush_syllable_jong(t, s) + return t +end + +-- L, V, [T & ^OT]? -> LV | LVT +local function flush_syllable (t, s) + if #s == 2 or #s == 3 and is_modern_jong( s[3] ) then + tableinsert(t, jamo2syllable(s)) + else + tableappend(t, s) + end + return t, {} +end + +local function compose_modern_hangul (ot) + local t, s = {}, {} + for _, c in ipairs(ot) do + if #s == 1 and is_modern_jung(c) or #s >= 2 and is_jongsong(c) then + tableinsert(s, c) + else + t, s = flush_syllable(t, s) + tableinsert(is_modern_cho(c) and s or t, c) + end + end + t = flush_syllable(t, s) + return t +end + +-- L, VF -> CL +local function flush_cjamocho (t, s) + if #s == 2 then + tableinsert(t, jamotocjamo.ccho[ s[1] ]) + else + tableappend(t, s) + end + return t, {} +end + +local function compose_jamo_chosong (ot) + local t, s = {}, {} + for _, c in ipairs(ot) do + if #s == 1 and c == 0x1160 then + tableinsert(s, c) + else + t, s = flush_cjamocho(t, s) + tableinsert(jamotocjamo.ccho[c] and s or t, c) + end + end + t = flush_cjamocho(t, s) + return t +end + +-- LF, V, ^T -> CV, ^T +local function flush_cjamojung (t, s) + if #s == 2 then + tableinsert(t, jamotocjamo.cjung[ s[2] ]) + else + tableappend(t, s) + end + return t, {} +end + +local function compose_jamo_jungsong (ot) + local t, s = {}, {} + for _, c in ipairs(ot) do + if #s == 1 and jamotocjamo.cjung[c] or #s == 2 and is_jongsong(c) then + tableinsert(s, c) + else + t, s = flush_cjamojung(t, s) + tableinsert(c == 0x115F and s or t, c) + end + end + t = flush_cjamojung(t, s) + return t +end + +-- CHJ -> HJ, VS +local function flush_compat_hanja (t, s) + if #s == 1 then + tableappend(t, chanjatohanja[ s[1] ]) + else + tableappend(t, s) + end + return t, {} +end + +local function compose_compat_hanja (ot) + local t, s = {}, {} + for _, c in ipairs(ot) do + if #s == 1 and c >= 0xFE00 and c <= 0xFE02 then + tableinsert(s, c) + else + t, s = flush_compat_hanja(t, s) + tableinsert(chanjatohanja[c] and s or t, c) + end + end + t = flush_compat_hanja(t, s) + return t +end + +local function hangulcompose (buffer) + local t = compose_hanguldecompose(buffer) + t = compose_modern_hangul(t) + t = compose_jamo_chosong (t) + t = compose_jamo_jungsong(t) + t = compose_compat_hanja (t) + + return utf8char(tableunpack(t)) +end + +local loaded = false +local add_to_callback = luatexbase.add_to_callback +local remove_from_callback = luatexbase.remove_from_callback + +local function unload() + if loaded then + remove_from_callback('process_input_buffer', 'luatexko.hangul_normalize') + loaded = false + end +end +luatexkonormalize.unload = unload + +local function compose() + unload() + add_to_callback('process_input_buffer', hangulcompose, 'luatexko.hangul_normalize') + loaded = true +end +luatexkonormalize.compose = compose + +local function decompose() + unload() + add_to_callback('process_input_buffer', hanguldecompose, 'luatexko.hangul_normalize') + loaded = true +end +luatexkonormalize.decompose = decompose diff --git a/macros/luatex/generic/luatexko/luatexko-uhc2utf8.lua b/macros/luatex/generic/luatexko/luatexko-uhc2utf8.lua new file mode 100644 index 0000000000..82d11379cb --- /dev/null +++ b/macros/luatex/generic/luatexko/luatexko-uhc2utf8.lua @@ -0,0 +1,128 @@ +-- luatexko-uhc2utf8.lua +-- +-- Copyright (c) 2013-2019 Dohyun Kim <nomos at ktug org> +-- Soojin Nam <jsunam at gmail com> +-- +-- This work may be distributed and/or modified under the +-- conditions of the LaTeX Project Public License, either version 1.3c +-- 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.3c or later is part of all distributions of LaTeX +-- version 2006/05/20 or later. + +luatexbase.provides_module({ + name = "luatexko-uhc2utf8", + version = "2.4", + date = "2019/08/01", + author = "Dohyun Kim, Soojin Nam", + description = "UHC (CP949) input encoding", + license = "LPPL v1.3+", +}) + +luatexko = luatexko or {} +luatexko.uhc2utf8 = luatexko.uhc2utf8 or {} +local luatexkouhc2utf8 = luatexko.uhc2utf8 + +local kpse_find_file = kpse.find_file +local add_to_callback = luatexbase.add_to_callback +local remove_from_callback = luatexbase.remove_from_callback + +local function get_uhc_uni_table() + local t_uhc2ucs = {} + local file = kpse_find_file("KSCms-UHC-UCS2","cmap files") + if file then + file = io.open(file, "r") + while true do + local line = file:read"*line" + if not line then break end + local ea,eb,uni = line:match"<(%x+)>%s+<(%x+)>%s+<(%x+)>" + if ea and eb and uni then + ea, eb, uni = tonumber(ea,16),tonumber(eb,16),tonumber(uni,16) + for i=ea,eb do + t_uhc2ucs[i] = uni + uni = uni + 1 + end + end + end + file:close() + end + return t_uhc2ucs +end + +local t_uhc2ucs = t_uhc2ucs or get_uhc_uni_table() + +local function uhc_to_utf8 (buffer) + if not buffer then return end + if utf8.len(buffer) then return end -- check if buffer is already utf-8 + buffer = buffer:gsub("([\129-\253])([\65-\254])", function(a, b) + local u = t_uhc2ucs[a:byte() * 256 + b:byte()] + if u then + return utf8.char(u) + end + end) + return buffer +end + +local function startconvert () + add_to_callback('process_input_buffer', uhc_to_utf8, 'luatexko.uhctoutf8', 1) +end +luatexkouhc2utf8.startconvert = startconvert + +local function stopconvert () + remove_from_callback('process_input_buffer', 'luatexko.uhctoutf8') +end +luatexkouhc2utf8.stopconvert = stopconvert + +----------------------------------------- +-- Hangul Windows OS uses CP949 filenames +----------------------------------------- +local function get_uni_uhc_table() + local t_ucs2uhc = {} + for i,v in pairs(t_uhc2ucs) do + t_ucs2uhc[v] = i + end + return t_ucs2uhc +end + +local t_ucs2uhc = t_ucs2uhc or get_uni_uhc_table() + +local function utf8_to_uhc (name) + if not name then return end + local t = {} + for _, u in utf8.codes(name) do + if u >= 0xA1 and u <= 0xFFE6 then + local c = t_ucs2uhc[u] + if c then + t[#t + 1] = c // 256 + t[#t + 1] = c % 256 + else + t[#t + 1] = u + end + else + t[#t + 1] = u + end + end + return string.char(table.unpack(t)) +end + +local function uhc_find_file (file, ...) + local f = kpse_find_file(file, ...) + if f then return f end + f = utf8_to_uhc(file) + return f and kpse_find_file(f, ...) +end + +local function start_uhc_filename () + add_to_callback('find_read_file', function(id, name) return uhc_find_file(name) end, 'luatexko.touhc_findreadfile') + add_to_callback('find_image_file', uhc_find_file, 'luatexko.touhc_findimagefile') + kpse.find_file = uhc_find_file +end +luatexkouhc2utf8.start_uhc_filename = start_uhc_filename + +local function stop_uhc_filename () + remove_from_callback('find_read_file', 'luatexko.touhc_findreadfile') + remove_from_callback('find_image_file', 'luatexko.touhc_findimagefile') + kpse.find_file = kpse_find_file +end +luatexkouhc2utf8.stop_uhc_filename = stop_uhc_filename diff --git a/macros/luatex/generic/luatexko/luatexko.lua b/macros/luatex/generic/luatexko/luatexko.lua new file mode 100644 index 0000000000..3ab4f18961 --- /dev/null +++ b/macros/luatex/generic/luatexko/luatexko.lua @@ -0,0 +1,1969 @@ +-- luatexko.lua +-- +-- Copyright (c) 2013-2019 Dohyun Kim <nomos at ktug org> +-- Soojin Nam <jsunam at gmail com> +-- +-- This work may be distributed and/or modified under the +-- conditions of the LaTeX Project Public License, either version 1.3c +-- 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.3c or later is part of all distributions of LaTeX +-- version 2006/05/20 or later. + +luatexbase.provides_module { + name = 'luatexko', + date = '2019/08/01', + version = '2.4', + description = 'typesetting Korean with LuaTeX', + author = 'Dohyun Kim, Soojin Nam', + license = 'LPPL v1.3+', +} + +luatexko = luatexko or {} +local luatexko = luatexko + +local dimensions = node.dimensions +local end_of_math = node.end_of_math +local getglue = node.getglue +local getnext = node.getnext +local getprev = node.getprev +local getproperty = node.getproperty +local has_attribute = node.has_attribute +local has_glyph = node.has_glyph +local insert_after = node.insert_after +local insert_before = node.insert_before +local nodecopy = node.copy +local nodecount = node.count +local nodefree = node.free +local nodeid = node.id +local nodenew = node.new +local noderemove = node.remove +local nodeslide = node.slide +local nodesubtype = node.subtype +local nodewrite = node.write +local rangedimensions = node.rangedimensions +local setglue = node.setglue +local setproperty = node.setproperty +local unset_attribute = node.unset_attribute + +local fontcurrent = font.current +local fontfonts = font.fonts +local fontgetfont = font.getfont +local getparameters = font.getparameters + +local texattribute = tex.attribute +local texcount = tex.count +local texset = tex.set +local texsp = tex.sp + +local set_macro = token.set_macro + +local stringformat = string.format + +local tableconcat = table.concat +local tableunpack = table.unpack + +local add_to_callback = luatexbase.add_to_callback +local attributes = luatexbase.attributes +local call_callback = luatexbase.call_callback +local callback_descriptions = luatexbase.callback_descriptions +local create_callback = luatexbase.create_callback +local module_warning = luatexbase.module_warning +local new_user_whatsit = luatexbase.new_user_whatsit +local new_user_whatsit_id = luatexbase.new_user_whatsit_id +local registernumber = luatexbase.registernumber +local remove_from_callback = luatexbase.remove_from_callback + +local function warning (...) + return module_warning("luatexko", stringformat(...)) +end + +local dirid = nodeid"dir" +local discid = nodeid"disc" +local glueid = nodeid"glue" +local glyphid = nodeid"glyph" +local hlistid = nodeid"hlist" +local kernid = nodeid"kern" +local mathid = nodeid"math" +local penaltyid = nodeid"penalty" +local ruleid = nodeid"rule" +local vlistid = nodeid"vlist" +local whatsitid = nodeid"whatsit" +local literal_whatsit = nodesubtype"pdf_literal" +local user_whatsit = nodesubtype"user_defined" +local directmode = 2 +local fontkern = 0 +local userkern = 1 +local italcorr = 3 +local lua_number = 100 +local lua_value = 108 +local spaceskip = 13 +local nohyphen = registernumber"l@nohyphenation" or -1 -- verbatim +local langkor = registernumber"koreanlanguage" or 16383 + +local hangulfontattr = attributes.luatexkohangulfontattr +local hanjafontattr = attributes.luatexkohanjafontattr +local fallbackfontattr = attributes.luatexkofallbackfontattr +local autojosaattr = attributes.luatexkoautojosaattr +local classicattr = attributes.luatexkoclassicattr +local dotemphattr = attributes.luatexkodotemphattr +local rubyattr = attributes.luatexkorubyattr + +local vert_classic = 1 +local SC_classic = 2 + +local function is_hanja (c) + return c >= 0x3400 and c <= 0xA4C6 + or c >= 0xF900 and c <= 0xFAD9 + or c >= 0x20000 and c <= 0x2FFFD + or c >= 0x2E81 and c <= 0x2FD5 +end + +local function is_hangul (c) + return c >= 0xAC00 and c <= 0xD7A3 +end + +local function is_chosong (c) + return c >= 0x1100 and c <= 0x115F + or c >= 0xA960 and c <= 0xA97C +end + +local function is_jungsong (c) + return c >= 0x1160 and c <= 0x11A7 + or c >= 0xD7B0 and c <= 0xD7C6 +end + +local function is_jongsong (c) + return c >= 0x11A8 and c <= 0x11FF + or c >= 0xD7CB and c <= 0xD7FB +end + +local hangul_tonemark = { + [0x302E] = true, [0x302F] = true, +} + +local function is_compat_jamo (c) + return c >= 0x3131 and c <= 0x318E +end + +local function is_unicode_var_sel (c) + return c >= 0xFE00 and c <= 0xFE0F + or c >= 0xE0100 and c <= 0xE01EF +end + +local function is_cjk_combining (c) + return c >= 0x302A and c <= 0x302F + or c >= 0x3099 and c <= 0x309C + or c >= 0xFF9E and c <= 0xFF9F + or is_unicode_var_sel(c) +end + +local function is_noncjk_char (c) + return c >= 0x30 and c <= 0x39 + or c >= 0x41 and c <= 0x5A + or c >= 0x61 and c <= 0x7A + or c >= 0xC0 and c <= 0xD6 + or c >= 0xD8 and c <= 0xF6 + or c >= 0xF8 and c <= 0x10FC + or c >= 0x1200 and c <= 0x1FFE + or c >= 0xA4D0 and c <= 0xA877 + or c >= 0xAB01 and c <= 0xABBF + or c >= 0xFB00 and c <= 0xFDFD + or c >= 0xFE70 and c <= 0xFEFC +end + +local function is_kana (c) + return c >= 0x3041 and c <= 0x3096 + or c >= 0x30A1 and c <= 0x30FA + or c >= 0x31F0 and c <= 0x31FF + or c >= 0xFF66 and c <= 0xFF6F + or c >= 0xFF71 and c <= 0xFF9D + or c == 0x309F or c == 0x30FF + or c >= 0x1B000 and c <= 0x1B16F +end + +local function is_hangul_jamo (c) + return is_hangul(c) + or is_compat_jamo(c) + or is_chosong(c) + or is_jungsong(c) + or is_jongsong(c) +end + +local stretch_f = 5/100 -- should be consistent for ruby + +local function get_font_data (fontid) + return fontgetfont(fontid) or fontfonts[fontid] or {} +end + +local function get_font_param (f, key) + local t + if type(f) == "number" then + t = getparameters(f) + if t and t[key] then + return t[key] + end + f = get_font_data(f) + end + if type(f) == "table" then + t = f.parameters + return t and t[key] + end +end + +local function get_en_size (f) + local quad = get_font_param(f, "quad") + return quad and quad/2 or 327680 +end + +local function char_in_font(fontdata, char) + if type(fontdata) == "number" then + fontdata = get_font_data(fontdata) + end + if fontdata.characters then + return fontdata.characters[char] + end +end + +local function option_in_font (fontdata, optionname) + if type(fontdata) == "number" then + fontdata = get_font_data(fontdata) + end + if fontdata.shared then + return fontdata.shared.features[optionname] + end +end + +local function my_node_props (n) + local t = getproperty(n) + if not t then + t = {} + setproperty(n, t) + end + t.luatexko = t.luatexko or {} + return t.luatexko +end + +-- font fallback + +local force_hangul = { + [0x21] = true, -- ! + [0x27] = true, -- ' + [0x28] = true, -- ( + [0x29] = true, -- ) + [0x2C] = true, -- , + [0x2E] = true, -- . + [0x3A] = true, -- : + [0x3B] = true, -- ; + [0x3F] = true, -- ? + [0x60] = true, -- ` + [0xB7] = true, -- · + [0x2014] = true, -- — + [0x2015] = true, -- ― + [0x2018] = true, -- ‘ + [0x2019] = true, -- ’ + [0x201C] = true, -- “ + [0x201D] = true, -- ” + [0x2026] = true, -- … + [0x203B] = true, -- ※ +} +luatexko.forcehangulchars = force_hangul + +local forcehf_f, forcehf_id = new_user_whatsit("forcehf","luatexko") + +local function update_force_hangul (value) + local what = forcehf_f() + what.type = lua_value -- function + what.value = value + nodewrite(what) +end +luatexko.updateforcehangul = update_force_hangul + +local active_processes = {} + +local char_font_options = { + hangulspaceskip = {}, + intercharacter = {}, + intercharstretch = {}, + interhangul = {}, + interlatincjk = {}, + tonemarkwidth = {}, +} + +local function hangul_space_skip (curr, newfont) + if curr.lang ~= nohyphen then + local n = getnext(curr) + if n and n.id == glueid and n.subtype == spaceskip then + local params = getparameters(curr.font) + local oldwd, oldst, oldsh, oldsto, oldsho = getglue(n) + if params + and oldwd == params.space + and oldst == params.space_stretch + and oldsh == params.space_shrink + and oldsto == 0 + and oldsho == 0 then -- not user's spaceskip + + local newwd = char_font_options.hangulspaceskip[newfont] + if newwd == nil then + local newsp = nodecopy(curr) + newsp.char, newsp.font = 32, newfont + newsp = nodes.simple_font_handler(newsp) + newwd = newsp and newsp.width or false + if newwd then + newwd = { texsp(newwd), texsp(newwd/2), texsp(newwd/3) } + end + char_font_options.hangulspaceskip[newfont] = newwd + if newsp then + nodefree(newsp) + end + end + if newwd then + setglue(n, newwd[1], newwd[2], newwd[3]) + end + end + end + end +end + +local function process_fonts (head) + local curr = head + while curr do + local id = curr.id + if id == glyphid then + local props = my_node_props(curr) + if not props.unicode then + + local c = curr.char + + if is_cjk_combining(c) then + local p = getprev(curr) + if p.id == glyphid and curr.font ~= p.font then + hangul_space_skip(curr, p.font) + curr.font = p.font + end + + if not active_processes.reorderTM and + hangul_tonemark[c] and + option_in_font(curr.font, "script") == "hang" then + luatexko.activate("reorderTM") -- activate reorderTM here + active_processes.reorderTM = true + end + + else + local hf = has_attribute(curr, hangulfontattr) or false + local hjf = has_attribute(curr, hanjafontattr) or false + local fontdata = get_font_data(curr.font) + local format = fontdata.format + local encode = fontdata.encodingbytes + local widefont = encode == 2 or format == "opentype" or format == "truetype" + if hf and widefont and force_hangul[c] and curr.lang ~= nohyphen then + curr.font = hf + elseif hf and luatexko.hangulbyhangulfont and is_hangul_jamo(c) then + hangul_space_skip(curr, hf) + curr.font = hf + elseif hjf and luatexko.hanjabyhanjafont and is_hanja(c) then + hangul_space_skip(curr, hjf) + curr.font = hjf + elseif not char_in_font(fontdata, c) then + local fbf = has_attribute(curr, fallbackfontattr) or false + for _,f in ipairs{ hf, hjf, fbf } do + if f and char_in_font(f, c) then + hangul_space_skip(curr, f) + curr.font = f + break + end + end + end + end + + props.unicode = c + end + elseif id == discid then + curr.pre = process_fonts(curr.pre) + curr.post = process_fonts(curr.post) + curr.replace = process_fonts(curr.replace) + elseif id == mathid then + curr = end_of_math(curr) + elseif id == whatsitid + and curr.subtype == user_whatsit + and curr.user_id == forcehf_id + and curr.type == lua_value then + + local value = curr.value + if type(value) == "function" then + value() + end + end + curr = getnext(curr) + end + return head +end + +-- linebreak + +local intercharclass = { [0] = + { [0] = nil, {1,1}, nil, {.5,.5} }, + { [0] = nil, nil, nil, {.5,.5} }, + { [0] = {1,1}, {1,1}, nil, {.5,.5}, nil, {1,1}, {1,1} }, + { [0] = {.5,.5},{.5,.5},{.5,.5},{1,.5}, {.5,.5},{.5,.5},{.5,.5} }, + { [0] = {1,0}, {1,0}, nil, {1.5,.5},nil, {1,0}, {1,0} }, + { [0] = nil, {1,1}, nil, {.5,.5} }, + { [0] = {1,1}, {1,1}, nil, {.5,.5} }, +} + +local charclass = setmetatable({ + [0x2018] = 1, [0x201C] = 1, [0x2329] = 1, [0x3008] = 1, + [0x300A] = 1, [0x300C] = 1, [0x300E] = 1, [0x3010] = 1, + [0x3014] = 1, [0x3016] = 1, [0x3018] = 1, [0x301A] = 1, + [0x301D] = 1, [0xFE17] = 1, [0xFE35] = 1, [0xFE37] = 1, + [0xFE39] = 1, [0xFE3B] = 1, [0xFE3D] = 1, [0xFE3F] = 1, + [0xFE41] = 1, [0xFE43] = 1, [0xFE47] = 1, [0xFF08] = 1, + [0xFF3B] = 1, [0xFF5B] = 1, [0xFF5F] = 1, [0xFF62] = 1, + -- + [0x2019] = 2, [0x201D] = 2, [0x232A] = 2, [0x3001] = 2, + [0x3009] = 2, [0x300B] = 2, [0x300D] = 2, [0x300F] = 2, + [0x3011] = 2, [0x3015] = 2, [0x3017] = 2, [0x3019] = 2, + [0x301B] = 2, [0x301E] = 2, [0x301F] = 2, [0xFE10] = 2, + [0xFE11] = 2, [0xFE18] = 2, [0xFE36] = 2, [0xFE38] = 2, + [0xFE3A] = 2, [0xFE3C] = 2, [0xFE3E] = 2, [0xFE40] = 2, + [0xFE42] = 2, [0xFE44] = 2, [0xFE48] = 2, [0xFF09] = 2, + [0xFF0C] = 2, [0xFF3D] = 2, [0xFF5D] = 2, [0xFF60] = 2, + [0xFF63] = 2, [0xFF64] = 2, + -- + [0x00B7] = 3, [0x30FB] = 3, [0xFF1A] = 3, [0xFF1B] = 3, + [0xFF65] = 3, + -- + [0x3002] = 4, [0xFE12] = 4, [0xFF0E] = 4, [0xFF61] = 4, + -- + [0x2015] = 5, [0x2025] = 5, [0x2026] = 5, [0xFE19] = 5, + [0xFE30] = 5, [0xFE31] = 5, + -- + [0xFE15] = 6, [0xFE16] = 6, [0xFF01] = 6, [0xFF1F] = 6, +}, { __index = function() return 0 end }) + +local SC_charclass = setmetatable({ + [0xFF01] = 4, [0xFF1A] = 4, [0xFF1B] = 4, [0xFF1F] = 4, +}, { __index = charclass }) + +local vert_charclass = setmetatable({ + [0xFF1A] = 5, -- 0xFE13 + [0xFF1B] = 5, -- 0xFE14 +}, { __index = charclass }) + +local function get_char_class (c, classic) + if classic == vert_classic then + return vert_charclass[c] + elseif classic == SC_classic then + return SC_charclass[c] + end + return charclass[c] +end + +local breakable_after = setmetatable({ + [0x21] = true, [0x22] = true, [0x25] = true, [0x27] = true, + [0x29] = true, [0x2C] = true, [0x2D] = true, [0x2E] = true, + [0x3A] = true, [0x3B] = true, [0x3E] = true, [0x3F] = true, + [0x5D] = true, [0x7D] = true, [0x7E] = true, [0xBB] = true, + [0x2013] = true, [0x2014] = true, [0x25A1] = true, [0x25CB] = true, + [0x2E80] = true, [0x3003] = true, [0x3005] = true, [0x3007] = true, + [0x301C] = true, [0x3035] = true, [0x303B] = true, [0x303C] = true, + [0x309D] = true, [0x309E] = true, [0x30A0] = true, [0x30FC] = true, + [0x30FD] = true, [0x30FE] = true, [0xFE13] = true, [0xFE14] = true, + [0xFE32] = true, [0xFE50] = true, [0xFE51] = true, [0xFE52] = true, + [0xFE54] = true, [0xFE55] = true, [0xFE57] = true, [0xFE57] = true, + [0xFE58] = true, [0xFE5A] = true, [0xFE5C] = true, [0xFE5E] = true, + [0xFF1E] = true, [0xFF5E] = true, [0xFF70] = true, +},{ __index = function (_,c) + return is_hangul_jamo(c) and not is_chosong(c) + or is_noncjk_char(c) + or is_hanja(c) + or is_cjk_combining(c) + or is_kana(c) + or charclass[c] >= 2 +end }) +luatexko.breakableafter = breakable_after + +local breakable_before = setmetatable({ + [0x28] = true, [0x3C] = true, [0x5B] = true, [0x60] = true, + [0x7B] = true, [0xAB] = true, [0x25A1] = true, [0x25CB] = true, + [0x3007] = true, [0xFE59] = true, [0xFE5B] = true, [0xFE5D] = true, + [0xFF1C] = true, + -- small kana + [0x3041] = 1000, [0x3043] = 1000, [0x3045] = 1000, [0x3047] = 1000, + [0x3049] = 1000, [0x3063] = 1000, [0x3083] = 1000, [0x3085] = 1000, + [0x3087] = 1000, [0x308E] = 1000, [0x3095] = 1000, [0x3096] = 1000, + [0x30A1] = 1000, [0x30A3] = 1000, [0x30A5] = 1000, [0x30A7] = 1000, + [0x30A9] = 1000, [0x30C3] = 1000, [0x30E3] = 1000, [0x30E5] = 1000, + [0x30E7] = 1000, [0x30EE] = 1000, [0x30F5] = 1000, [0x30F6] = 1000, + [0x31F0] = 1000, [0x31F1] = 1000, [0x31F2] = 1000, [0x31F3] = 1000, + [0x31F4] = 1000, [0x31F5] = 1000, [0x31F6] = 1000, [0x31F7] = 1000, + [0x31F8] = 1000, [0x31F9] = 1000, [0x31FA] = 1000, [0x31FB] = 1000, + [0x31FC] = 1000, [0x31FD] = 1000, [0x31FE] = 1000, [0x31FF] = 1000, + [0xFF67] = 1000, [0xFF68] = 1000, [0xFF69] = 1000, [0xFF6A] = 1000, + [0xFF6B] = 1000, [0xFF6C] = 1000, [0xFF6D] = 1000, [0xFF6E] = 1000, + [0xFF6F] = 1000, [0x1B150] = 1000, [0x1B151] = 1000, [0x1B152] = 1000, + [0x1B164] = 1000, [0x1B165] = 1000, [0x1B166] = 1000, [0x1B167] = 1000, +},{ __index = function(_,c) + return is_hangul(c) + or is_compat_jamo(c) + or is_chosong(c) + or is_hanja(c) + or is_kana(c) + or charclass[c] == 1 +end +}) +luatexko.breakablebefore = breakable_before + +local allowbreak_false_nodes = { + [hlistid] = true, + [vlistid] = true, + [ruleid] = true, + [discid] = true, + [glueid] = true, + [penaltyid] = true, +} + +local function is_blocking_node (curr) + local id, subtype = curr.id, curr.subtype + return allowbreak_false_nodes[id] or id == kernid and subtype == userkern +end + +local function ruby_char_font (rb) + local n = has_glyph(rb.list) + if n then + local c, f = my_node_props(n).unicode or n.char, n.font + if is_chosong(c) or hangul_tonemark[c] then + c = 0xAC00 + end + return c, f + end +end + +local function hbox_ini_char_font (box) + local curr = box.list + while curr do + local id = curr.id + if id == glyphid then + local c = my_node_props(curr).unicode or curr.char + if c and not is_cjk_combining(c) then + return c, curr.font + end + elseif id == hlistid and curr.list then + return hbox_ini_char_font(curr) + elseif is_blocking_node(curr) then + return + end + curr = getnext(curr) + end +end + +local function hbox_fin_char_font (box) + local curr = nodeslide(box.list) + while curr do + local id = curr.id + if id == glyphid then + local c = my_node_props(curr).unicode or curr.char + if c and not is_cjk_combining(c) then + return c, curr.font + end + elseif id == hlistid and curr.list then + return hbox_fin_char_font(curr) + elseif is_blocking_node(curr) then + return + end + curr = getprev(curr) + end +end + +local function get_actualtext (curr) + local actual = my_node_props(curr).startactualtext + if type(actual) == "table" then + return actual[0], actual[1], actual[#actual] + end +end + +local function goto_end_actualtext (curr) + local n = getnext(curr) + while n do + if n.id == whatsitid + and n.mode == directmode + and my_node_props(n).endactualtext then + curr = n + break + end + n = getnext(n) + end + return curr +end + +local function fontdata_opt_dim (fd, optname) + local dim = option_in_font(fd, optname) + if dim then + local m, u = dim:match"^(.+)(e[mx])%s*$" + if m and u then + if u == "em" then + dim = m * fd.parameters.quad + else + dim = m * fd.parameters.x_height + end + else + dim = texsp(dim) + end + return dim + end +end + +local function get_font_opt_dimen (fontid, optionname) + local t = char_font_options[optionname] + local dim = t and t[fontid] + if dim == nil then + local fd = get_font_data(fontid) + dim = fontdata_opt_dim(fd, optionname) + t[fontid] = dim or false -- cache + end + return dim +end + +local function insert_glue_before (head, curr, par, br, brb, classic, ict, dim, fid) + local pn = nodenew(penaltyid) + if not br then + pn.penalty = 10000 + elseif type(brb) == "number" then + pn.penalty = brb + elseif par and nodecount(glyphid, curr) <= 2 then + pn.penalty = 1000 -- supress orphan + else + pn.penalty = 50 + end + + dim = dim or 0 + local gl = nodenew(glueid) + local en = get_en_size(fid) + if ict then + en = classic and en or en/4 + setglue(gl, en * ict[1] + dim, nil, en * ict[2]) + else + local str = get_font_opt_dimen(fid, "intercharstretch") or stretch_f*en + setglue(gl, dim, str, str*0.6) + end + + head = insert_before(head, curr, pn) + return insert_before(head, curr, gl) +end + +local function maybe_linebreak (head, curr, pc, pcl, cc, old, fid, par) + local ccl = get_char_class(cc, old) + if pc and cc and curr.lang ~= nohyphen then + local ict = intercharclass[pcl][ccl] + local brb = breakable_before[cc] + local br = brb and breakable_after[pc] + local dim = get_font_opt_dimen(fid, "intercharacter") + if ict or br or dim and (pcl >= 1 or ccl >= 1) then + head = insert_glue_before(head, curr, par, br, brb, old, ict, dim, fid) + end + end + return head, cc, ccl +end + +local function process_linebreak (head, par) + local curr, pc, pcl = head, false, 0 + while curr do + local id = curr.id + if id == glyphid then + local c = my_node_props(curr).unicode or curr.char + if c and not is_unicode_var_sel(c) then + local old = has_attribute(curr, classicattr) + head, pc, pcl = maybe_linebreak(head, curr, pc, pcl, c, old, curr.font, par) + end + + elseif id == hlistid and curr.list then + local old = has_attribute(curr, classicattr) + if has_attribute(curr, rubyattr) then + local c, f = ruby_char_font(curr) -- rubybase + if c and f then + head, pc, pcl = maybe_linebreak(head, curr, pc, pcl, c, old, f, par) + else + pc, pcl = false, 0 + end + else + local c, f = hbox_ini_char_font(curr) + if c and f then + head = maybe_linebreak(head, curr, pc, pcl, c, old, f, par) + end + pc = hbox_fin_char_font(curr) + pcl = pc and get_char_class(pc, old) or 0 + end + + elseif id == whatsitid and curr.mode == directmode then + local glyf, c, fin = get_actualtext(curr) + if c and fin and glyf then + local old = has_attribute(glyf, classicattr) + head = maybe_linebreak(head, curr, pc, pcl, c, old, glyf.font, par) + pc, pcl, curr = fin, 0, goto_end_actualtext(curr) + end + + elseif id == mathid then + pc, pcl, curr = 0x30, 0, end_of_math(curr) + elseif id == dirid then + pc, pcl = curr.dir:sub(1,1) == "-" and 0x30, 0 -- pop dir + elseif is_blocking_node(curr) then + pc, pcl = false, 0 + end + curr = getnext(curr) + end + return head +end + +-- compress punctuations + +local function process_glyph_width (head) + local curr = head + while curr do + local id = curr.id + if id == glyphid then + if curr.lang ~= nohyphen + and option_in_font(curr.font, "compresspunctuations") then + + local cc = my_node_props(curr).unicode or curr.char + local old = has_attribute(curr, classicattr) + local class = get_char_class(cc, old) + if class >= 1 and class <= 4 and + (old or cc < 0x2000 or cc > 0x202F) then -- exclude general puncts + + local gpos = class == 1 and getprev(curr) or getnext(curr) + gpos = gpos and gpos.id == kernid and gpos.subtype == fontkern + + if not gpos then + local wd = get_en_size(curr.font) - curr.width + if wd ~= 0 then + local k = nodenew(kernid) -- fontkern (subtype 0) is default + k.kern = class == 3 and wd/2 or wd + if class == 1 then + head = insert_before(head, curr, k) + elseif class == 2 or class == 4 then + head, curr = insert_after(head, curr, k) + else + local k2 = nodecopy(k) + head = insert_before(head, curr, k) + head, curr = insert_after(head, curr, k2) + end + end + end + end + end + elseif id == mathid then + curr = end_of_math(curr) + end + curr = getnext(curr) + end + return head +end + +-- interhangul & interlatincjk + +local function is_cjk_char (c) + return is_hangul_jamo(c) + or is_hanja(c) + or is_cjk_combining(c) + or is_kana(c) + or charclass[c] >= 1 + or rawget(breakable_before, c) and c >= 0x2000 + or rawget(breakable_after, c) and c >= 0x2000 +end + +local function do_interhangul_option (head, curr, pc, c, fontid, par) + local cc = (is_hangul(c) or is_compat_jamo(c) or is_chosong(c)) and 1 or 0 + + if cc*pc == 1 and curr.lang ~= nohyphen then + local dim = get_font_opt_dimen(fontid, "interhangul") + if dim then + head = insert_glue_before(head, curr, par, true, true, false, false, dim, fontid) + end + end + + return head, cc +end + +local function process_interhangul (head, par) + local curr, pc = head, 0 + while curr do + local id = curr.id + if id == glyphid then + local c = my_node_props(curr).unicode or curr.char + if c and not is_unicode_var_sel(c) then + head, pc = do_interhangul_option(head, curr, pc, c, curr.font, par) + + if is_chosong(c) then + pc = 0 + elseif is_jungsong(c) or is_jongsong(c) or hangul_tonemark[c] then + pc = 1 + end + end + + elseif id == hlistid and curr.list then + if has_attribute(curr, rubyattr) then + local c, f = ruby_char_font(curr) + if c and f then + head, pc = do_interhangul_option(head, curr, pc, c, f, par) + else + pc = 0 + end + else + local c, f = hbox_ini_char_font(curr) + if c and f then + head = do_interhangul_option(head, curr, pc, c, f, par) + end + c = hbox_fin_char_font(curr) + pc = c and is_hangul_jamo(c) and 1 or 0 + end + + elseif id == whatsitid and curr.mode == directmode then + local glyf, c = get_actualtext(curr) + if c and glyf then + head = do_interhangul_option(head, curr, pc, c, glyf.font, par) + pc, curr = 1, goto_end_actualtext(curr) + end + + elseif id == mathid then + pc, curr = 0, end_of_math(curr) + elseif is_blocking_node(curr) or id == dirid then + pc = 0 + end + curr = getnext(curr) + end + return head +end + +local function do_interlatincjk_option (head, curr, pc, pf, pcl, c, cf, par) + local cc = is_cjk_char(c) and 1 or is_noncjk_char(c) and 2 or 0 + local old = has_attribute(curr, classicattr) + local ccl = get_char_class(c, old) + + if cc*pc == 2 and curr.lang ~= nohyphen then + local brb = cc == 2 or breakable_before[c] -- numletter != br_before + if brb then + local f = cc == 1 and cf or pf + local dim = get_font_opt_dimen(f, "interlatincjk") + if dim then + local ict = old and intercharclass[pcl][ccl] -- under classic env. only + if ict then + dim = get_font_opt_dimen(f, "intercharacter") or 0 + end + head = insert_glue_before(head, curr, par, true, brb, old, ict, dim, f) + end + end + end + + return head, cc, cf, ccl +end + +local function process_interlatincjk (head, par) + local curr, pc, pf, pcl = head, 0, 0, 0 + while curr do + local id = curr.id + if id == glyphid then + local c = my_node_props(curr).unicode or curr.char + if c and not is_unicode_var_sel(c) then + head, pc, pf, pcl = do_interlatincjk_option(head, curr, pc, pf, pcl, c, curr.font, par) + pc = breakable_after[c] and pc or 0 + end + + elseif id == hlistid and curr.list then + if has_attribute(curr, rubyattr) then + local c, f = ruby_char_font(curr) + if c and f then + head, pc, pf, pcl = do_interlatincjk_option(head, curr, pc, pf, pcl, c, f, par) + else + pc, pf, pcl = 0, 0, 0 + end + else + local c, f = hbox_ini_char_font(curr) + if c and f then + head = do_interlatincjk_option(head, curr, pc, pf, pcl, c, f, par) + end + c, f = hbox_fin_char_font(curr) + if c and breakable_after[c] then + pc = is_cjk_char(c) and 1 or is_noncjk_char(c) and 2 or 0 + else + pc = 0 + end + pcl = c and get_char_class(c, has_attribute(curr, classicattr)) or 0 + pf = f or 0 + end + + elseif id == whatsitid and curr.mode == directmode then + local glyf, c = get_actualtext(curr) + if c and glyf then + head, pc, pf, pcl = do_interlatincjk_option(head, curr, pc, pf, pcl, c, glyf.font, par) + curr = goto_end_actualtext(curr) + end + + elseif id == mathid then + if pc == 1 then + head = do_interlatincjk_option(head, curr, pc, pf, pcl, 0x30, pf, par) + end + pc, pf, pcl = 2, 0, 0 + curr = end_of_math(curr) + + elseif id == dirid then + if pc == 1 and curr.dir:sub(1,1) == "+" then + head = do_interlatincjk_option(head, curr, pc, pf, pcl, 0x30, pf, par) + pc, pf, pcl = 0, 0, 0 + end + + elseif is_blocking_node(curr) then + pc, pf, pcl = 0, 0, 0 + end + curr = getnext(curr) + end + return head +end + +-- remove classic spaces + +local function process_remove_spaces (head) + local curr, opt_name, to_free = head, "removeclassicspaces", {} + while curr do + local id = curr.id + if id == glueid then + if curr.subtype == spaceskip and has_attribute(curr, classicattr) then + + for k, v in pairs{ p = getprev(curr), n = getnext(curr) } do + local ok + while v do + local id = v.id + if id ~= whatsitid -- skip whatsit or kern except userkern + and ( id ~= kernid or v.subtype == userkern ) then + + local vchar, vfont + if id == glyphid and v.lang ~= nohyphen then + local c = my_node_props(v).unicode or v.char or 0 + if is_unicode_var_sel(c) then + v = getprev(v) or v + end + vchar, vfont = my_node_props(v).unicode or v.char, v.font + elseif id == hlistid and v.list then + if has_attribute(v, rubyattr) then + vchar, vfont = ruby_char_font(v) + elseif k == "p" then + vchar, vfont = hbox_fin_char_font(v) + else + vchar, vfont = hbox_ini_char_font(v) + end + end + if vchar and vfont and option_in_font(vfont, opt_name) then + ok = is_cjk_char(vchar) + end + + break + end + v = k == "p" and getprev(v) or getnext(v) + end + if ok then + head = noderemove(head, curr) + to_free[#to_free + 1] = curr + break + end + end + end + elseif id == mathid then + curr = end_of_math(curr) + end + curr = getnext(curr) + end + for _,v in ipairs(to_free) do nodefree(v) end + return head +end + +-- josa + +local josa_table = { + -- 리을, 중성, 종성 + [0xAC00] = {0xC774, 0xAC00, 0xC774}, -- 가 = 이, 가, 이 + [0xC740] = {0xC740, 0xB294, 0xC740}, -- 은 = 은, 는, 은 + [0xC744] = {0xC744, 0xB97C, 0xC744}, -- 을 = 을, 를, 을 + [0xC640] = {0xACFC, 0xC640, 0xACFC}, -- 와 = 과, 와, 과 + [0xC73C] = {nil, nil, 0xC73C}, -- 으(로) = , , 으 + [0xC774] = {0xC774, nil, 0xC774}, -- 이(라) = 이, , 이 +} + +local josa_code = setmetatable({ + [0x30] = 3, [0x31] = 1, [0x33] = 3, [0x36] = 3, + [0x37] = 1, [0x38] = 1, [0x4C] = 1, [0x4D] = 3, + [0x4E] = 3, [0x6C] = 1, [0x6D] = 3, [0x6E] = 3, + [0x2160] = 1, [0x2162] = 3, [0x2165] = 3, [0x2166] = 1, + [0x2167] = 1, [0x2169] = 3, [0x216A] = 1, [0x216C] = 3, + [0x216D] = 3, [0x216E] = 3, [0x216F] = 3, [0x2170] = 1, + [0x2172] = 3, [0x2175] = 3, [0x2176] = 1, [0x2177] = 1, + [0x2179] = 3, [0x217A] = 1, [0x217C] = 3, [0x217D] = 3, + [0x217E] = 3, [0x217F] = 3, [0x2460] = 1, [0x2462] = 3, + [0x2465] = 3, [0x2466] = 1, [0x2467] = 1, [0x2469] = 3, + [0x246A] = 1, [0x246C] = 3, [0x246F] = 3, [0x2470] = 1, + [0x2471] = 1, [0x2473] = 3, [0x2474] = 1, [0x2476] = 3, + [0x2479] = 3, [0x247A] = 1, [0x247B] = 1, [0x247D] = 3, + [0x247E] = 1, [0x2480] = 3, [0x2483] = 3, [0x2484] = 1, + [0x2485] = 1, [0x2487] = 3, [0x2488] = 1, [0x248A] = 3, + [0x248D] = 3, [0x248E] = 1, [0x248F] = 1, [0x2491] = 3, + [0x2492] = 1, [0x2494] = 3, [0x2497] = 3, [0x2498] = 1, + [0x2499] = 1, [0x249B] = 3, [0x24A7] = 1, [0x24A8] = 3, + [0x24A9] = 3, [0x24C1] = 1, [0x24C2] = 3, [0x24C3] = 3, + [0x24DB] = 1, [0x24DC] = 3, [0x24DD] = 3, [0x3139] = 1, + [0x3203] = 1, [0x3263] = 1, [0xFF10] = 3, [0xFF11] = 1, + [0xFF13] = 3, [0xFF16] = 3, [0xFF17] = 1, [0xFF18] = 1, + [0xFF2C] = 1, [0xFF2D] = 3, [0xFF2E] = 3, [0xFF4C] = 1, + [0xFF4D] = 3, [0xFF4E] = 3, +},{ __index = function(_,c) + if is_hangul(c) then + c = (c - 0xAC00) % 28 + 0x11A7 + end + if is_chosong(c) then + return c == 0x1105 and 1 or 3 + elseif is_jungsong(c) then + return c ~= 0x1160 and 2 + elseif is_jongsong(c) then + return c == 0x11AF and 1 or 3 + elseif is_noncjk_char(c) and c <= 0x7A + or c >= 0x2160 and c <= 0x217F -- roman + or c >= 0x2460 and c <= 0x24E9 -- ① + or c >= 0x314F and c <= 0x3163 or c >= 0x3187 and c <= 0x318E -- ㅏ + or c >= 0x320E and c <= 0x321E -- ㈎ + or c >= 0x326E and c <= 0x327F -- ㉮ + or c >= 0xFF10 and c <= 0xFF19 -- 0 + or c >= 0xFF21 and c <= 0xFF3A -- A + or c >= 0xFF41 and c <= 0xFF5A -- a + then return 2 + elseif c >= 0x3131 and c <= 0x314E or c >= 0x3165 and c <= 0x3186 -- ㄱ + or c >= 0x3200 and c <= 0x320D -- ㈀ + or c >= 0x3260 and c <= 0x326D -- ㉠ + then return 3 + end +end }) + +local ignore_parens = false + +local function prevjosacode (n, parenlevel) + local parenlevel, josacode = parenlevel or 0 + while n do + local id = n.id + if id == glyphid then + local c = my_node_props(n).unicode or n.char -- beware hlist/vlist + if ignore_parens and c == 0x29 then + parenlevel = parenlevel + 1 + elseif ignore_parens and c == 0x28 then + parenlevel = parenlevel - 1 + elseif parenlevel <= 0 then + josacode = josa_code[c] + if josacode then break end + end + elseif id == hlistid or id == vlistid then + local list = n.list + if list then + josacode, parenlevel = prevjosacode(nodeslide(list), parenlevel) + if josacode then break end + end + end + n = getprev(n) + end + return josacode, parenlevel +end + +local function process_josa (head) + local curr, tofree = head, {} + while curr do + local id = curr.id + if id == glyphid then + local autojosaattr = has_attribute(curr, autojosaattr) + if autojosaattr then + local cc = curr.char + if cc == 0xC774 then + local n = getnext(curr) + if n and n.char and is_hangul(n.char) then + else + cc = 0xAC00 + end + end + local t = josa_table[cc] + if t then + ignore_parens = autojosaattr > 0 + cc = t[prevjosacode(getprev(curr)) or 3] + if cc then + curr.char = cc + else + head = noderemove(head, curr) + tofree[#tofree + 1] = curr + end + end + unset_attribute(curr, autojosaattr) + end + elseif id == mathid then + curr = end_of_math(curr) + end + curr = getnext(curr) + end + for _,v in ipairs(tofree) do nodefree(v) end + return head +end + +-- dotemph + +local dotemphbox = {} +luatexko.dotemphbox = dotemphbox + +local function process_dotemph (head, tofree) + local curr, outer, tofree = head, not tofree, tofree or {} + while curr do + local id = curr.id + if id == glyphid then + local dotattr = has_attribute(curr, dotemphattr) + if dotattr then + local c = my_node_props(curr).unicode or curr.char + if is_hangul(c) or + is_compat_jamo(c) or + is_chosong(c) or + is_hanja(c) or + is_kana(c) then + local currwd = curr.width + if currwd >= get_en_size(curr.font) then + local box = nodecopy(dotemphbox[dotattr]) + local shift = (currwd - box.width)/2 + if shift ~= 0 then + local list = box.list + local k = nodenew(kernid) + k.kern = shift + box.list = insert_before(list, list, k) + end + box.width = 0 + head = insert_before(head, curr, box) + tofree[dotattr] = true + end + end + unset_attribute(curr, dotemphattr) + end + elseif id == hlistid then + local list = curr.list + if list then + curr.list, tofree = process_dotemph(list, tofree) + end + end + curr = getnext(curr) + end + if outer then + for k in pairs(tofree) do nodefree(dotemphbox[k]) end + end + return head, tofree +end + +-- uline + +local uline_f, uline_id = new_user_whatsit("uline","luatexko") +local no_uline_id = new_user_whatsit_id("no_uline","luatexko") + +local function ulboundary (i, n, subtype) + local what = uline_f() + if n then + what.type = lua_value -- table + what.value = { i, nodecopy(n), subtype } + else + what.type = lua_number + what.value = i + end + nodewrite(what) +end +luatexko.ulboundary = ulboundary + +local white_nodes = { + [glueid] = true, + [penaltyid] = true, + [kernid] = true, + [whatsitid] = true, +} + +function skip_white_nodes (n, ltr) + local nextnode = ltr and getnext or getprev + while n do + if not white_nodes[n.id] then break end + n = nextnode(n) + end + return n +end + +local function draw_uline (head, curr, parent, t, final) + local start, list, subtype = t.start or head, t.list, t.subtype + start = skip_white_nodes(start, true) + if final then + nodeslide(start) -- to get correct getprev. + end + curr = skip_white_nodes(curr) + curr = getnext(curr) or curr + local len = parent and rangedimensions(parent, start, curr) + or dimensions(start, curr) -- it works?! + if len and len ~= 0 then + local g = nodenew(glueid) + setglue(g, len) + g.subtype = subtype + g.leader = final and list or nodecopy(list) + local k = nodenew(kernid) + k.kern = -len + head = insert_before(head, start, g) + head = insert_before(head, start, k) + end + return head +end + +local function process_uline (head, parent, items, level) + local curr, items, level = head, items or {}, level or 0 + while curr do + local id = curr.id + if id == whatsitid + and curr.subtype == user_whatsit + and curr.user_id == uline_id then + + local value = curr.value + if curr.type == lua_value then + local count, list, subtype = tableunpack(value) + items[count] = { + start = curr, + list = list, + subtype = subtype, + level = level, + } + elseif items[value] then + head = draw_uline(head, curr, parent, items[value], true) + items[value] = nil + end + + curr.user_id = no_uline_id -- avoid multiple run + elseif id == hlistid then + local list = curr.list + if list then + curr.list, items = process_uline(list, curr, items, level+1) + end + end + curr = getnext(curr) + end + + curr = nodeslide(head) + for i, t in pairs(items) do + if level == t.level then + head = draw_uline(head, curr, parent, t) + t.start = nil + end + end + return head, items +end + +-- ruby + +local rubybox = {} +luatexko.rubybox = rubybox + +local function getrubystretchfactor (box) + local _, fid = ruby_char_font(box) + local str = get_font_opt_dimen(fid, "intercharstretch") + if str then + local em = get_en_size(fid) * 2 + set_macro("luatexkostretchfactor", stringformat("%.4f", str/em/2)) + end +end +luatexko.getrubystretchfactor = getrubystretchfactor + +local function process_ruby_pre_linebreak (head) + local curr = head + while curr do + local id = curr.id + if id == hlistid then + local rubyid = has_attribute(curr, rubyattr) + if rubyid then + local ruby_t = rubybox[rubyid] + if ruby_t[3] then -- rubyoverlap + local side = (ruby_t[1].width - curr.width)/2 + if side > 0 then -- ruby is wide + local k, r = nodenew(kernid), nodenew(ruleid) + k.subtype, k.kern = userkern, -side + r.width, r.height, r.depth = side, 0, 0 + local k2, r2 = nodecopy(k), nodecopy(r) + head = insert_before(head, curr, k) + head = insert_before(head, curr, r) + head, curr = insert_after(head, curr, r2) + head, curr = insert_after(head, curr, k2) + end + ruby_t[3] = false + end + end + elseif id == mathid then + curr = end_of_math(curr) + end + curr = getnext(curr) + end + return head +end + +local function process_ruby_post_linebreak (head) + local curr = head + while curr do + if curr.id == hlistid then + local rubyid = has_attribute(curr, rubyattr) + if rubyid then + local ruby_t = rubybox[rubyid] + local ruby = ruby_t and ruby_t[1] + if ruby then + local side = (curr.width - ruby.width)/2 + if side ~= 0 then + local list = ruby.list + local k = nodenew(kernid) + k.kern = side + ruby.list = insert_before(list, list, k) + end + ruby.width = 0 + local _, f = ruby_char_font(curr) + local ascender = get_font_param(f, "ascender") or curr.height + ruby.shift = -ascender - ruby.depth - ruby_t[2] -- rubysep + head = insert_before(head, curr, ruby) + end + ruby_t = nil + unset_attribute(curr, rubyattr) + else + local list = curr.list + if list then + curr.list = process_ruby_post_linebreak(list) + end + end + end + curr = getnext(curr) + end + return head +end + +-- reorder tone marks + +local function conv_tounicode (uni) + if uni < 0x10000 then + return stringformat("%04X", uni) + else -- surrogate + uni = uni - 0x10000 + local high = uni // 0x400 + 0xD800 + local low = uni % 0x400 + 0xDC00 + return stringformat("%04X%04X", high, low) + end +end + +local function pdfliteral_direct_actual (syllable) + local data + if syllable then + local t = {} + for _,v in ipairs(syllable) do + t[#t + 1] = conv_tounicode(v) + end + data = stringformat("/Span<</ActualText<FEFF%s>>>BDC", tableconcat(t)) + else + data = "EMC" + end + local what = nodenew(whatsitid, literal_whatsit) + what.mode = directmode + what.data = data + if syllable then + my_node_props(what).startactualtext = syllable + else + my_node_props(what).endactualtext = true + end + return what +end + +local function get_tonemark_width (curr, uni) + local fontid = curr.font + local hwidth = char_font_options.tonemarkwidth[fontid] + if not hwidth then + -- check horizontal width; vertical width is mostly non-zero + local fontdata = get_font_data(fontid) + local shared = fontdata.shared or {} + local rawdata = shared.rawdata or {} + local descriptions = rawdata.descriptions or {} + local description = descriptions[uni] or {} + hwidth = description.width or 0 + char_font_options.tonemarkwidth[fontid] = hwidth + end + return hwidth +end + +local function process_reorder_tonemarks (head) + local curr = head + while curr do + local id = curr.id + if id == glyphid and option_in_font(curr.font, "script") == "hang" then + local fontdata = get_font_data(curr.font) + local uni = my_node_props(curr).unicode or curr.char + if is_hangul(uni) or is_chosong(uni) or uni == 0x25CC then + + local syllable = { [0]=curr, uni } + + local n = getnext(curr) + while n do + local nid = n.id + if nid == glyphid then + local u = my_node_props(n).unicode or n.char + if is_jungsong(u) + or is_jongsong(u) + or hangul_tonemark[u] then + syllable[#syllable + 1] = u + curr, uni = n, u + else + break + end + elseif nid ~= kernid or n.subtype == userkern then + break + end + n = getnext(n) + end + + if #syllable > 1 + and hangul_tonemark[uni] + and get_tonemark_width(curr, uni) ~= 0 then + + local ini, fin = syllable[0], curr + local actual = pdfliteral_direct_actual(syllable) + local endactual = pdfliteral_direct_actual() + head = insert_before(head, ini, actual) + head, curr = insert_after(head, curr, endactual) + + head = noderemove(head, fin) + head = insert_before(head, ini, fin) + end + + elseif hangul_tonemark[uni] -- isolated tone mark + and char_in_font(fontdata, 0x25CC) then + + local dotcircle = nodecopy(curr) + dotcircle.char = 0x25CC + if get_tonemark_width(curr, uni) ~= 0 then + local actual = pdfliteral_direct_actual{ [0]=curr, uni } + local endactual = pdfliteral_direct_actual() + head = insert_before(head, curr, actual) + head, curr = insert_after(head, curr, dotcircle) + head, curr = insert_after(head, curr, endactual) + else + head = insert_before(head, curr, dotcircle) + end + end + elseif id == whatsitid + and curr.mode == directmode + and my_node_props(curr).startactualtext then + + curr = goto_end_actualtext(curr) + + elseif id == mathid then + curr = end_of_math(curr) + end + curr = getnext(curr) + end + return head +end + +-- vertical font + +local streamreader = utilities.files +local openfile = streamreader.open +local closefile = streamreader.close +local readstring = streamreader.readstring +local readulong = streamreader.readcardinal4 +local readushort = streamreader.readcardinal2 +local readfixed = streamreader.readfixed4 +local readshort = streamreader.readinteger2 +local setpos = streamreader.setposition + +local function get_otf_tables (f, subfont) + if f then + local sfntversion = readstring(f,4) + if sfntversion == "ttcf" then + local ttcversion = readfixed(f) + local numfonts = readulong(f) + if subfont >= 1 and subfont <= numfonts then + local offsets = {} + for i = 1, numfonts do + offsets[i] = readulong(f) + end + setpos(f, offsets[subfont]) + sfntversion = readstring(f,4) + end + end + if sfntversion == "OTTO" or sfntversion == "true" or sfntversion == "\0\1\0\0" then + local numtables = readushort(f) + local searchrange = readushort(f) + local entryselector = readushort(f) + local rangeshift = readushort(f) + local tables = {} + for i= 1, numtables do + local tag = readstring(f,4) + tables[tag] = { + checksum = readulong(f), + offset = readulong(f), + length = readulong(f), + } + end + return tables + end + end +end + +local function read_maxp (f, t) + if f and t then + setpos(f, t.offset) + return { + version = readfixed(f), + numglyphs = readushort(f), + } + end +end + +local function read_vhea (f, t) + if f and t then + setpos(f, t.offset) + return { + version = readfixed(f), + ascent = readshort(f), + descent = readshort(f), + lineGap = readshort(f), + advanceheightmax = readshort(f), + mintopsidebearing = readshort(f), + minbottomsidebrearing = readshort(f), + ymaxextent = readshort(f), + caretsloperise = readshort(f), + caretsloperun = readshort(f), + caretoffset = readshort(f), + reserved1 = readshort(f), + reserved2 = readshort(f), + reserved3 = readshort(f), + reserved4 = readshort(f), + metricdataformat = readshort(f), + numheights = readushort(f), + } + end +end + +local function read_vmtx (f, t, numofheights, numofglyphs) + if f and t and numofheights and numofglyphs then + setpos(f, t.offset) + local vmtx = {} + local height = 0 + for i = 0, numofheights-1 do + height = readushort(f) + vmtx[i] = { + ht = height, + tsb = readshort(f), + } + end + for i = numofheights, numofglyphs-1 do + vmtx[i] = { + ht = height, + tsb = readshort(f), + } + end + return vmtx + end +end + +local tsb_font_data = {} + +local function get_tsb_table (filename, subfont) + subfont = tonumber(subfont) or 1 + local key = stringformat("%s::%s", filename, subfont) + if tsb_font_data[key] then + return tsb_font_data[key] + end + local f = openfile(filename, true) -- true: zero-based + if f then + local vmtx + local tables = get_otf_tables(f, subfont) + if tables then + local vhea = read_vhea(f, tables.vhea) + local numofheights = vhea and vhea.numheights + local maxp = read_maxp(f, tables.maxp) + local numofglyphs = maxp and maxp.numglyphs + vmtx = read_vmtx(f, tables.vmtx, numofheights, numofglyphs) + end + closefile(f) + tsb_font_data[key] = vmtx + return vmtx + end +end + +local function process_vertical_font (fontdata) + local subfont = fontdata.specification and fontdata.specification.sub + local tsb_tab = get_tsb_table(fontdata.filename, subfont) + if not tsb_tab then + warning("Vertical metrics table (vmtx) not found in the font\n".. + "`%s'", fontdata.fullname) + return + end + + local shared = fontdata.shared or {} + local descriptions = shared.rawdata and shared.rawdata.descriptions or {} + local parameters = fontdata.parameters or {} + local scale = parameters.factor or 655.36 + local quad = parameters.quad or 655360 + local ascender = parameters.ascender or quad*0.8 + + local goffset = fontdata_opt_dim(fontdata, "charraise") or + (parameters.x_height or quad/2)/2 + -- declare shift amount of horizontal box inside vertical env. + fontdata.horizboxmoveleftamount = quad/2-goffset + + for i,v in pairs(fontdata.characters) do + local voff = goffset - (v.width or 0)/2 + local bbox = descriptions[i] and descriptions[i].boundingbox or {0,0,0,0} + local gid = v.index + local tsb = tsb_tab[gid].tsb + local hoff = tsb and (bbox[4] + tsb) * scale or ascender + v.commands = { + { "down", -voff }, + { "right", hoff }, + { "pdf", "q 0 1 -1 0 0 0 cm" }, + { "push" }, + { "char", i }, + { "pop" }, + { "pdf", "Q" }, + } + local vw = tsb_tab[gid].ht + v.width = vw and vw * scale or quad + local ht = bbox[3] * scale + voff + local dp = bbox[1] * scale + voff + v.height = ht > 0 and ht or nil + v.depth = dp < 0 and -dp or nil + end + local spacechar = char_in_font(fontdata, 32) + if spacechar then + parameters.space = spacechar.width + parameters.space_stretch = spacechar.width/2 + parameters.space_shrink = spacechar.width/2 + end + + local res = fontdata.resources or {} + local fea = shared.features or {} + fea.kern = nil -- only for horizontal writing + fea.vert = true -- should be activated by default + local seq = res.sequences or {} + for _,v in ipairs(seq) do + local fea = v.features or {} + if fea.vhal or fea.vkrn or fea.valt or fea.vpal or fea.vert then + if v.type == "gpos_single" then + for _,vv in pairs(v.steps or {}) do + for _,vvv in pairs(vv.coverage or {}) do + if type(vvv) == "table" and #vvv == 4 then + vvv[1], vvv[2], vvv[3], vvv[4], vvv[5] = + -vvv[2], vvv[1], vvv[4], vvv[3], 0 -- last 0 to avoid multiple run + end + end + end + elseif v.type == "gpos_pair" then + for _,vv in pairs(v.steps or {}) do + for _,vvv in pairs(vv.coverage or {}) do + for _,vvvv in pairs(vvv) do + for _,vvvvv in pairs(vvvv) do + if type(vvvvv) == "table" and #vvvvv == 4 then + vvvvv[1], vvvvv[2], vvvvv[3], vvvvv[4], vvvvv[5] = + -vvvvv[2], vvvvv[1], vvvvv[4], vvvvv[3], 0 + end + end + end + end + end + end + end + end +end + +local function get_horizbox_moveleft () + for _, v in ipairs{ fontcurrent(), + texattribute.luatexkohangulfontattr, + texattribute.luatexkohanjafontattr, + texattribute.luatexkofallbackfontattr } do + if v and v > 0 then + local amount = get_font_data(v).horizboxmoveleftamount + if amount then + set_macro("luatexkohorizboxmoveleft", texsp(amount).."sp") + break + end + end + end +end +luatexko.gethorizboxmoveleft = get_horizbox_moveleft + +-- charraise + +local function process_charriase_font (fontdata) + local raise = fontdata_opt_dim(fontdata, "charraise") + if raise then + local shared = fontdata.shared or {} + local descriptions = shared.rawdata and shared.rawdata.descriptions or {} + local scale = fontdata.parameters.factor or 655.36 + for i, v in pairs( fontdata.characters ) do + v.commands = { + {"down", -raise }, + {"char", i}, + } + local bbox = descriptions[i] and descriptions[i].boundingbox or {0,0,0,0} + local ht = bbox[4] * scale + raise + local dp = bbox[2] * scale + raise + v.height = ht > 0 and ht or nil + v.depth = dp < 0 and -dp or nil + end + end +end + +-- fake italic correctioin + +local function process_fake_slant_corr (head) -- for font fallback + local curr = head + while curr do + local id = curr.id + if id == kernid then + if curr.subtype == italcorr and curr.kern == 0 then + local p = getprev(curr) + while p do -- skip jungsong/jongsong + if p.id == glyphid and p.width < get_en_size(p.font) then + local c = my_node_props(p).unicode or p.char + if not is_jungsong(c) and not is_jongsong(c) then + break + end + elseif p.id == whatsitid + and p.mode == directmode + and my_node_props(p).endactualtext then -- skip + else + break + end + p = getprev(p) + end + if p.id == glyphid then + local fontdata = get_font_data(p.font) + if fontdata.slant and fontdata.slant > 0 then + local italic = char_in_font(fontdata, p.char).italic + if italic then + curr.kern = italic + end + end + end + end + elseif id == mathid then + curr = end_of_math(curr) + end + curr = getnext(curr) + end + return head +end + +local function process_fake_slant_font (fontdata) + local fsl = fontdata.slant + if fsl and fsl > 0 then + fsl = fsl/1000 + local params = fontdata.parameters or {} + params.slant = (params.slant or 0) + fsl*65536 -- slant per point + local scale = params.factor or 655.36 + local shared = fontdata.shared or {} + local descriptions = shared.rawdata and shared.rawdata.descriptions or {} + for i, v in pairs(fontdata.characters) do + local bbox = descriptions[i] and descriptions[i].boundingbox or {0,0,0,0} + local italic = (v.height or 0) * fsl - (v.width or 0) + bbox[3]*scale + if italic > 0 then + v.italic = italic + end + end + end +end + +-- wrap up + +local pass_fun = function(...) return ... end +create_callback("luatexko_pre_hpack", "data", pass_fun) +create_callback("luatexko_pre_prelinebreak", "data", pass_fun) +create_callback("luatexko_post_hpack", "data", pass_fun) +create_callback("luatexko_post_prelinebreak", "data", pass_fun) + +add_to_callback("hpack_filter", function(h) + h = process_fonts(h) + h = call_callback("luatexko_pre_hpack", h) + h = call_callback("luatexko_post_hpack", h) + return process_linebreak(h) +end, "luatexko.hpack_filter.pre_rendering", 1) + +add_to_callback("pre_linebreak_filter", function(h) + h = process_fonts(h) + h = call_callback("luatexko_pre_prelinebreak", h, true) + h = call_callback("luatexko_post_prelinebreak", h, true) + return process_linebreak(h, true) +end, "luatexko.pre_linebreak_filter.pre_rendering", 1) + +local font_opt_procs = { + removeclassicspaces = { + luatexko_pre_hpack = process_remove_spaces, + luatexko_pre_prelinebreak = process_remove_spaces, + }, + interhangul = { + luatexko_post_hpack = process_interhangul, + luatexko_post_prelinebreak = process_interhangul, + }, + interlatincjk = { + luatexko_post_hpack = process_interlatincjk, + luatexko_post_prelinebreak = process_interlatincjk, + }, + compresspunctuations = { + hpack_filter = process_glyph_width, + pre_linebreak_filter = process_glyph_width, + }, + slant = { + hpack_filter = process_fake_slant_corr, + pre_linebreak_filter = process_fake_slant_corr, + }, +} + +local function process_patch_font (fontdata) + for name, procs in pairs( font_opt_procs ) do + if not active_processes[name] and option_in_font(fontdata, name) then + for cbnam, cbfun in pairs( procs ) do + add_to_callback(cbnam, cbfun, "luatexko."..cbnam.."."..name) + end + active_processes[name] = true + end + end + + if not active_processes.expansion + and option_in_font(fontdata, "expansion") then + texset("global", "adjustspacing", 2) + active_processes.expansion = true + end + + if option_in_font(fontdata, "protrusion") then + if not active_processes.protrusion then + texset("global", "protrudechars", 2) + active_processes.protrusion = true + end + if not active_processes[fontdata.fullname] and + option_in_font(fontdata, "compresspunctuations") then + warning("Both `compresspunctuations' and `protrusion' are\n".. + "enabled for the font `%s'.\n".. + "Beware that this could result in bad justifications.\n", + fontdata.fullname) + active_processes[fontdata.fullname] = true + end + end + + if option_in_font(fontdata, "vertical") then + process_vertical_font(fontdata) + elseif option_in_font(fontdata, "charraise") then + process_charriase_font(fontdata) + end + + if option_in_font(fontdata, "slant") then + process_fake_slant_font(fontdata) + end +end + +add_to_callback("luaotfload.patch_font", process_patch_font, +"luatexko.patch_font") + +local auxiliary_procs = { + dotemph = { + hpack_filter = process_dotemph, + vpack_filter = process_dotemph, + }, + uline = { + hpack_filter = process_uline, + vpack_filter = process_uline, + }, + ruby = { + pre_linebreak_filter = process_ruby_pre_linebreak, + hpack_filter = process_ruby_post_linebreak, + vpack_filter = process_ruby_post_linebreak, + }, + autojosa = { + luatexko_pre_hpack = process_josa, + luatexko_pre_prelinebreak = process_josa, + }, + reorderTM = { + luatexko_pre_hpack = process_reorder_tonemarks, + luatexko_pre_prelinebreak = process_reorder_tonemarks, + }, +} + +local function activate (name) + for cbnam, cbfun in pairs( auxiliary_procs[name] ) do + local fun + if cbnam == "hpack_filter" then + fun = function(h, gc) + if gc == "align_set" then + h = cbfun(h) + end + return h + end + else + fun = function(h) + h = cbfun(h) + return h + end + end + add_to_callback(cbnam, fun, "luatexko."..cbnam.."."..name) + end +end +luatexko.activate = activate + +add_to_callback ("hyphenate", +function(head) + local curr = head + while curr do + local id = curr.id + if id == glyphid and curr.subtype == 1 and curr.lang ~= nohyphen then + local c = curr.char + if c then + if is_unicode_var_sel(c) then + local p = getprev(curr) + if p.id == glyphid then + curr.lang = p.lang + end + elseif is_cjk_char(c) then + curr.lang = langkor + end + end + elseif id == mathid then + curr = end_of_math(curr) + end + curr = getnext(curr) + end + lang.hyphenate(head) +end, +"luatexko.hyphenate.prevent_disc_nodes") + +-- aux functions + +local function deactivate_all (str) + luatexko.deactivated = {} + for _, name in ipairs{ "hpack_filter", + "pre_linebreak_filter", + "vpack_filter", + "hyphenate", + "luaotfload.patch_font" } do + local t = {} + for i, v in ipairs( callback_descriptions(name) ) do + if v:find(str or "^luatexko%.") then + local ff, dd = remove_from_callback(name, v) + t[#t + 1] = { ff, dd, i } + end + end + luatexko.deactivated[name] = t + end +end +luatexko.deactivateall = deactivate_all + +local function reactivate_all () + for name, v in pairs(luatexko.deactivated or {}) do + for _, vv in ipairs(v) do + add_to_callback(name, tableunpack(vv)) + end + end + luatexko.deactivated = nil +end +luatexko.reactivateall = reactivate_all + +local function current_has_hangul_chars (cnt) + texcount[cnt] = char_in_font(fontcurrent(), 0xAC00) and 1 or 0 +end +luatexko.currenthashangulchars = current_has_hangul_chars + diff --git a/macros/luatex/generic/luatexko/luatexko.sty b/macros/luatex/generic/luatexko/luatexko.sty new file mode 100644 index 0000000000..7599a0977b --- /dev/null +++ b/macros/luatex/generic/luatexko/luatexko.sty @@ -0,0 +1,728 @@ +% luatexko.sty +% +% Copyright (c) 2013-2019 Dohyun Kim <nomos at ktug org> +% Soojin Nam <jsunam at gmail com> +% +% This work may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3c +% 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.3c or later is part of all distributions of LaTeX +% version 2006/05/20 or later. + +\ifdefined\luatexkohangulfontattr \endinput\fi +\ifdefined\selectfont + \ProvidesPackage{luatexko}[2019/08/01 v2.4 typesetting Korean with LuaTeX] + \RequirePackage{luatexbase} + \RequirePackage{fontspec} +\else + \input luatexbase.sty + \input luaotfload.sty + \chardef\luatexkoatcatcode=\catcode`\@ + \catcode`\@=11 +\fi +\newlanguage\koreanlanguage +\protected\def\pdfliteral{\pdfextension literal} +\newattribute\luatexkohangulfontattr +\newattribute\luatexkohanjafontattr +\newattribute\luatexkofallbackfontattr +\newattribute\luatexkoautojosaattr +\newattribute\luatexkoclassicattr +\newattribute\luatexkodotemphattr +\newattribute\luatexkorubyattr \chardef\luatexkorubyalloc\allocationnumber +\directlua{ require"luatexko" } +% classic +\protected\def\typesetclassic{\luatexkoclassicattr\z@\parindent1em } +\protected\def\typesetvertical{\luatexkoclassicattr\@ne\parindent1em } +\protected\def\typesetmodern{\unsetattribute\luatexkoclassicattr} +\protected\def\inhibitglue{\hskip\z@skip} +\protected\def\Schinese{\luatexkoclassicattr\tw@\parindent2em } +\protected\def\Tchinese{\luatexkoclassicattr\thr@@\parindent2em } % +halt +\let\korean\typesetmodern \let\japanese\typesetclassic \let\chinese\Schinese +% josa +\count@"AC00 +\loop + \catcode\count@ 12 + \ifnum\count@<"D7A3 \advance\count@\@ne \repeat +\newcount\josaignoreparens +\def\luatexkojosaactivate{% + \ifx\luatexkojosaactive\relax + \else + \directlua{ luatexko.activate("autojosa") }% + \global\let\luatexkojosaactive\relax + \fi + \luatexkoautojosaattr\josaignoreparens } +\protected\def\은{\begingroup\luatexkojosaactivate 은\endgroup} +\let\는\은 +\protected\def\을{\begingroup\luatexkojosaactivate 을\endgroup} +\let\를\을 +\protected\def\와{\begingroup\luatexkojosaactivate 와\endgroup} +\let\과\와 +\protected\def\가{\begingroup\luatexkojosaactivate 가\endgroup} +\protected\def\이{\begingroup\luatexkojosaactivate 이\endgroup} +\protected\def\라{\이라} +\protected\def\으{\begingroup\luatexkojosaactivate 으\endgroup} +\protected\def\로{\으로} +% force hangul font +\def\luatexkotoks@appendchars#1,{% + \ifx\empty#1\empty + \expandafter\luatexkotoks@appendchars + \else + \ifx*#1\else + \expandafter\ifx\expandafter\empty\the\toks@\empty + \etoksapp0{\number#1}% + \else + \etoksapp0{,\number#1}% + \fi + \expandafter\expandafter\expandafter\luatexkotoks@appendchars + \fi + \fi } +\def\luatexkodoluacode#1{\directlua{ + \ifhmode luatexko.updateforcehangul(function() \fi + #1 + \ifhmode end) \fi }} +\protected\def\registerpunctuations#1{% + \toks@{}\luatexkotoks@appendchars#1,*,\luatexkodoluacode{ + local t = luatexko.forcehangulchars + for _,v in ipairs{\the\toks@} do t[v]=true end }} +\let\registerhangulpunctuations\registerpunctuations +\protected\def\unregisterpunctuations#1{% + \toks@{}\luatexkotoks@appendchars#1,*,\luatexkodoluacode{ + local t = luatexko.forcehangulchars + for _,v in ipairs{\the\toks@} do t[v]=nil end }} +\let\unregisterhangulpunctuations\unregisterpunctuations +\protected\def\hangulpunctuations{% + \afterassignment\luatexkohangulpunctuations\count@ } +\def\luatexkohangulpunctuations{\luatexkodoluacode{ + local t = luatexko.forcehangulchars + for i in pairs(t) do t[i]=\ifcase\count@ false \else true \fi end }} +\protected\def\registerbreakableafter#1{% + \toks@{}\luatexkotoks@appendchars#1,*,\luatexkodoluacode{ + local t = luatexko.breakableafter + for _,v in ipairs{\the\toks@} do t[v]=true end }} +\protected\def\registerbreakablebefore#1{% + \toks@{}\luatexkotoks@appendchars#1,*,\luatexkodoluacode{ + local t = luatexko.breakablebefore + for _,v in ipairs{\the\toks@} do t[v]=true end }} +\protected\def\hangulbyhangulfont{% + \afterassignment\luatexkohangulbyhangulfont\count@ } +\def\luatexkohangulbyhangulfont{\luatexkodoluacode{ + luatexko.hangulbyhangulfont=\ifcase\count@ false \else true \fi }} +\protected\def\hanjabyhanjafont{% + \afterassignment\luatexkohanjabyhanjafont\count@ } +\def\luatexkohanjabyhanjafont{\luatexkodoluacode{ + luatexko.hanjabyhanjafont=\ifcase\count@ false \else true \fi }} +% dotemph +\newcount\luatexkodotemphcount +\def\dotemphraise{1ex } +\unless\ifdefined\bfseries\let\bfseries\bf\fi +\def\dotemphchar{\bfseries\char"02D9 } +\protected\def\dotemph#1{% + \global\advance\luatexkodotemphcount\@ne + \begingroup + \setbox\z@\hbox{\raise\dotemphraise\hbox{\dotemphchar}}% + \directlua{ + if \the\luatexkodotemphcount == 1 then luatexko.activate("dotemph") end + luatexko.dotemphbox[\the\luatexkodotemphcount] = node.copy(tex.box[0].list) + }\luatexkodotemphattr\luatexkodotemphcount #1% + \endgroup } +% uline +\newcount\luatexkoulinecount +\def\ulinedown{0.5ex }\def\ulinewidth{0.04em } +\protected\def\markoverwith#1#{% + \ifx\empty#1\empty + \def\luatexkoleaderstype{101}% cleaders + \else + \def\luatexkoleaderstype{102}% xleaders + \fi + \luatexkomarkoverwith } +\def\luatexkomarkoverwith#1#2{% + \global\advance\luatexkoulinecount\@ne + \begingroup + \count@\luatexkoulinecount + \ifx\luatexkoulinerunning\relax + \edef\ulinedown{\the\dimexpr\ulinedown+\ulinewidth+.15ex\relax}\fi + \let\luatexkoulinerunning\relax + \leavevmode + \setbox\z@\hbox{#1}% + \directlua{ + if \the\luatexkoulinecount == 1 then luatexko.activate("uline") end + luatexko.ulboundary(\the\count@, tex.box[0].list, \luatexkoleaderstype) + }#2\directlua{ + luatexko.ulboundary(\the\count@) + }\endgroup } +\protected\def\uline{\markoverwith{% + \vrule width\z@ height-\ulinedown depth\dimexpr\ulinedown+\ulinewidth\relax }} +\protected\def\dashuline{\markoverwith*{% + \hbox{\kern.125em + \vrule width.3em height-\ulinedown depth\dimexpr\ulinedown+\ulinewidth\relax + \kern.125em }}} +\protected\def\dotuline{\markoverwith*{% + \lower\dimexpr\ulinedown+.1ex\relax\hbox{\kern.07em .\kern.07em }}} +\protected\def\uuline#1{\uline{\uline{#1}}} +\protected\def\sout#1{\begingroup + \def\ulinedown{-.5\dimexpr\ulinewidth+1ex\relax}% + \uline{#1}\endgroup} +\protected\def\xout{\markoverwith{\hbox to.4em{\hss/\hss}}} +\protected\def\uwave{\font\luatexkofontsixly=lasy6\relax + \markoverwith{\lower4\p@\hbox{\luatexkofontsixly\char58}}} +% ruby +\def\rubysize{0.6} % size of ruby compared to base chars +\def\rubysep{0.1ex} % distance between base chars and ruby +\def\luatexkostretchfactor{.0125} % .5em/20/2 is hard coded. +\newif\ifruby@overlap +\let\rubyoverlap\ruby@overlaptrue +\let\rubynooverlap\ruby@overlapfalse +\rubyoverlap % ruby may hangover neighboring chars +\newcount\luatexkorubycount +\def\luatexkorubybasehbox{\hbox attr\luatexkorubyalloc=\luatexkorubycount} +\protected\def\ruby#1#2{% + \global\advance\luatexkorubycount\@ne + \begingroup + \leavevmode + \setbox\z@\luatexkorubybasehbox{#1}% base + \setbox\tw@\hbox{% ruby + \ifdefined\rubyfont \rubyfont \fi + \ifdefined\selectfont % <- latex + \dimen@\dimexpr\rubysize em\relax + \fontsize\dimen@\dimen@\selectfont + \fi + #2}% + \hbadness\@M % supress underfull warning + \dimen@ii\dimexpr\rubysize em\relax % ruby font size + \directlua{ luatexko.getrubystretchfactor(tex.box[2]) }% for intercharstretch + \ifdim\wd\z@ < \wd\tw@ % ruby is wider + \ifruby@overlap + \dimen@\dimexpr\wd\tw@-\wd\z@-\dimen@ii\relax % overhang .5 rubysize + \ifdim\dimen@ > \z@ + \setbox\z@\luatexkorubybasehbox spread\dimen@{% + \hskip\z@ plus \luatexkostretchfactor em\relax + \unhbox\z@ + \hskip\z@ plus \luatexkostretchfactor em\relax }% + \fi + \else + \setbox\z@\luatexkorubybasehbox to\wd\tw@{% + \hskip\z@ plus \luatexkostretchfactor em\relax + \unhbox\z@ + \hskip\z@ plus \luatexkostretchfactor em\relax }% + \fi + \else + \ifdim\rubysize\wd\z@ > 2\wd\tw@ % ruby is far shorter + \setbox\tw@\hbox to\wd\z@{% + \hskip\dimen@ii plus 1sp\relax + \unhbox\tw@ + \hskip\dimen@ii plus 1sp\relax }% + \else + \setbox\tw@\hbox to\wd\z@{% + \hskip\z@ plus \luatexkostretchfactor \dimen@ii\relax + \unhbox\tw@ + \hskip\z@ plus \luatexkostretchfactor \dimen@ii\relax }% + \fi + \fi + \directlua{ + if \the\luatexkorubycount == 1 then luatexko.activate("ruby") end + luatexko.rubybox[\the\luatexkorubycount] = { + node.copy(tex.box[2]), + \number\numexpr\dimexpr\rubysep\relax\relax, + \ifruby@overlap true\else false\fi, }}% + \box\z@ + \endgroup } +\protected\def\xxruby#1#2{% + \begingroup\def\basestr{#1}\def\rubystr{#2}\luatexkoxxruby\endgroup } +\def\luatexkoxxruby{% + \ifx\empty\basestr\else + \expandafter\luatexkogetrubybasechar\basestr\end + \expandafter\luatexkogetrubyrubychar\rubystr\end + \ruby\basechar\rubychar + \expandafter\luatexkoxxruby + \fi } +\def\luatexkogetrubybasechar#1#2\end{\def\basechar{#1}\def\basestr{#2}} +\def\luatexkogetrubyrubychar#1#2\end{\def\rubychar{#1}\def\rubystr{#2}} +% vertical writing +\protected\def\vertical#1{% + \leavevmode + \setbox\z@\vbox\bgroup + \hsize#1\relax + \typesetvertical } +\protected\def\endvertical{% + \egroup + \luatexkorotatebox\z@ + \box\z@ } +\protected\def\luatexkorotatebox#1{% + \setbox#1\hbox to\dimexpr\ht#1+\dp#1{% + \hfil + \vbox to\wd#1{% + \wd#1\z@ \ht#1\z@ \dp#1\z@ + \pdfliteral{q 0 -1 1 0 0 0 cm}\box#1\pdfliteral{Q}% + \vfil }}} +% horizontal box inside vertical writing +\def\luatexkohorizboxmoveleft{0.5ex} +\protected\def\horizontal#1{% + \directlua{ luatexko.gethorizboxmoveleft() }% + \leavevmode + \setbox\z@\vbox\bgroup + \hsize#1\relax + \typesetmodern } +\protected\def\endhorizontal{% + \egroup + \luatexkounrotatebox\z@ + \lower\luatexkohorizboxmoveleft\box\z@ } +\protected\def\luatexkounrotatebox#1{% + \setbox#1\hbox to\dimexpr\ht#1+\dp#1{% + \vbox to\wd#1{% + \vfil + \wd#1\z@ \ht#1\z@ \dp#1\z@ + \pdfliteral{q 0 1 -1 0 0 0 cm}\box#1\pdfliteral{Q}}% + \hfil }} +% do not veticalize headline/footline +\ifdefined\selectfont + \def\verticaltypesetting{% + \maxdepth\z@ \typesetvertical + \dimen@\textwidth \textwidth\textheight \textheight\dimen@ + \edef\@outputpage{\unexpanded{\luatexkorotatebox\@outputbox + \textwidth\textheight}\unexpanded\expandafter{\@outputpage}}} +\else + \def\verticaltypesetting{% + \dimen@\hsize \hsize\vsize \vsize\dimen@ + \edef\plainoutput{\unexpanded{\dimen@\hsize \hsize\vsize \vsize\dimen@}% + \unexpanded\expandafter{\plainoutput}}% + \def\pagebody{\setbox\z@\vbox to\hsize{\boxmaxdepth=\maxdepth + \pagecontents}\luatexkorotatebox\z@\box\z@}% + \maxdepth\z@ \typesetvertical } +\fi +% hangul normalize +\def\luatexhangulnormalize{% + \directlua{require"luatexko-normalize"}% + \afterassignment\luatexkohangulnormalize\count@} +\def\luatexkohangulnormalize{% + \ifcase\count@ \directlua{ luatexko.normalize.unload() }% 0: none + \or \directlua{ luatexko.normalize.compose() }% 1: nfc + \else \directlua{ luatexko.normalize.decompose() }% 2: nfd + \fi } +% convert uhc to utf8 +\def\luatexuhcinputencoding{% + \directlua{require"luatexko-uhc2utf8"}% + \afterassignment\luatexkouhcinputencoding\count@} +\def\luatexkouhcinputencoding{% + \ifcase\count@ \directlua{ luatexko.uhc2utf8.stopconvert() }% + \else \directlua{ luatexko.uhc2utf8.startconvert() }% + \fi} +% actualtext not provided +\protected\def\actualtext#1#{\luatexkoactualtext} +\def\luatexkoactualtext#1{#1} +% math hangul +\def\setmathhangulblock#1#2{% + \count@="#1 + \loop + \Umathcode\count@ = 7 \symmathhangul\count@ + \ifnum\count@<"#2 \advance\count@\@ne \repeat} +% font fallback +\newif\ifluatexkoselectfontrunning +\def\luatexkohangulselectfont{% + \ifluatexkoselectfontrunning \else + \ifdefined\luatexkohangulfont + \begingroup + \luatexkoselectfontrunningtrue + \luatexkohangulfont + \expandafter\expandafter\expandafter\endgroup + \expandafter\luatexkohangulfontattr\fontid\font\relax + \fi + \fi } +\def\luatexkohanjaselectfont{% + \ifluatexkoselectfontrunning \else + \ifdefined\luatexkohanjafont + \begingroup + \luatexkoselectfontrunningtrue + \luatexkohanjafont + \expandafter\expandafter\expandafter\endgroup + \expandafter\luatexkohanjafontattr\fontid\font\relax + \fi + \fi } +\def\luatexkofallbackselectfont{% + \ifluatexkoselectfontrunning \else + \ifdefined\luatexkofallbackfont + \begingroup + \luatexkoselectfontrunningtrue + \luatexkofallbackfont + \expandafter\expandafter\expandafter\endgroup + \expandafter\luatexkofallbackfontattr\fontid\font\relax + \fi + \fi } +% plain +\ifdefined\selectfont\else + \protected\def\hangulfont{% + \afterassignment\luatexkohangulselectfont\font\luatexkohangulfont} + \protected\def\hanjafont{% + \afterassignment\luatexkohanjaselectfont\font\luatexkohanjafont} + \protected\def\fallbackfont{% + \afterassignment\luatexkofallbackselectfont\font\luatexkofallbackfont} + \newfam\symmathhangul + \protected\def\setmathhangulfonts#1#2#3{% font identifiers + \ifnum\Umathcharfam"AC00=\symmathhangul\else + \setmathhangulblock{AC00}{D7A3}% + \fi + \textfont\symmathhangul=#1\relax + \scriptfont\symmathhangul=#2\relax + \scriptscriptfont\symmathhangul=#3\relax } + \protected\def\mathhangulfont{% + \afterassignment\luatexkosetmathhangulfonts\font\textmathhangul} + \def\luatexkosetmathhangulfonts{% + \toks@\expandafter{\directlua{ + local n = string.gsub("\fontname\textmathhangul", " at .+", "") + tex.sprint(n) }}% + \dimen@\fontdimen 6 \textmathhangul + \font\scriptmathhangul= {\the\toks@} at .7\dimen@ + \font\scriptscriptmathhangul = {\the\toks@} at .5\dimen@ + \setmathhangulfonts\textmathhangul\scriptmathhangul\scriptscriptmathhangul} + \catcode`\@=\luatexkoatcatcode +\endinput\fi +% latex +\RequirePackage{everysel} +\EverySelectfont{% + \ifx\f@encoding\UTFencname + \luatexkohangulselectfont + \luatexkohanjaselectfont + \luatexkofallbackselectfont + \fi } +\protected\def\fontfamily#1{\edef\f@family{#1}% redefine \fontfamily + \ifx\f@encoding\UTFencname + \ifluatexkoselectfontrunning \else + \ifx\f@family\rmdefault + \let\luatexkohangulfont\luatexkomainhangulfont + \let\luatexkohanjafont\luatexkomainhanjafont + \let\luatexkofallbackfont\luatexkomainfallbackfont + \else\ifx\f@family\sfdefault + \let\luatexkohangulfont\luatexkosanshangulfont + \let\luatexkohanjafont\luatexkosanshanjafont + \let\luatexkofallbackfont\luatexkosansfallbackfont + \else\ifx\f@family\ttdefault + \let\luatexkohangulfont\luatexkomonohangulfont + \let\luatexkohanjafont\luatexkomonohanjafont + \let\luatexkofallbackfont\luatexkomonofallbackfont + \fi\fi\fi + \fi\fi } +% fontspec-like +\ExplSyntaxOn +\DeclareDocumentCommand \setmainhangulfont { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkomainhangulfamily { Ligatures=TeX, #1, #3 } { #2 } + \DeclareRobustCommand \luatexkomainhangulfont + { + \fontfamily \luatexkomainhangulfamily \selectfont + } + \str_if_eq_x:nnT \familydefault \rmdefault + { + \cs_set_eq:NN \luatexkohangulfont \luatexkomainhangulfont + \luatexkohangulselectfont + } + \ignorespaces +} +\DeclareDocumentCommand \setsanshangulfont { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkosanshangulfamily { Ligatures=TeX, #1, #3 } { #2 } + \DeclareRobustCommand \luatexkosanshangulfont + { + \fontfamily \luatexkosanshangulfamily \selectfont + } + \str_if_eq_x:nnT \familydefault \sfdefault + { + \cs_set_eq:NN \luatexkohangulfont \luatexkosanshangulfont + \luatexkohangulselectfont + } + \ignorespaces +} +\DeclareDocumentCommand \setmonohangulfont { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkomonohangulfamily { #1, #3 } { #2 } + \DeclareRobustCommand \luatexkomonohangulfont + { + \fontfamily \luatexkomonohangulfamily \selectfont + } + \str_if_eq_x:nnT \familydefault \ttdefault + { + \cs_set_eq:NN \luatexkohangulfont \luatexkomonohangulfont + \luatexkohangulselectfont + } + \ignorespaces +} +\DeclareDocumentCommand \newhangulfontfamily { m O{} m O{} } +{ + \fontspec_set_family:cnn { luatexko_user_family_ \cs_to_str:N #1 } { #2, #4 } { #3 } + \DeclareRobustCommand #1 + { + \tl_set:Nn \luatexkohangulfont + { + \exp_args:Nc \fontfamily { luatexko_user_family_ \cs_to_str:N #1 } \selectfont + } + \luatexkohangulselectfont + } + \ignorespaces +} +\DeclareDocumentCommand \newhangulfontface { m O{} m O{} } +{ + \newhangulfontfamily #1 { #3 } [ BoldFont={}, ItalicFont={}, SmallCapsFont={}, #2, #4 ] +} +\DeclareDocumentCommand \hangulfontspec { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkohangulfontfamily { #1, #3 } { #2 } + \tl_set:Nn \luatexkohangulfont + { + \fontfamily \luatexkohangulfontfamily \selectfont + } + \luatexkohangulselectfont + \ignorespaces +} +\DeclareDocumentCommand \setmainhanjafont { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkomainhanjafamily { Ligatures=TeX, #1, #3 } { #2 } + \DeclareRobustCommand \luatexkomainhanjafont + { + \fontfamily \luatexkomainhanjafamily \selectfont + } + \str_if_eq_x:nnT \familydefault \rmdefault + { + \cs_set_eq:NN \luatexkohanjafont \luatexkomainhanjafont + \luatexkohanjaselectfont + } + \ignorespaces +} +\DeclareDocumentCommand \setsanshanjafont { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkosanshanjafamily { Ligatures=TeX, #1, #3 } { #2 } + \DeclareRobustCommand \luatexkosanshanjafont + { + \fontfamily \luatexkosanshanjafamily \selectfont + } + \str_if_eq_x:nnT \familydefault \sfdefault + { + \cs_set_eq:NN \luatexkohanjafont \luatexkosanshanjafont + \luatexkohanjaselectfont + } + \ignorespaces +} +\DeclareDocumentCommand \setmonohanjafont { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkomonohanjafamily { #1, #3 } { #2 } + \DeclareRobustCommand \luatexkomonohanjafont + { + \fontfamily \luatexkomonohanjafamily \selectfont + } + \str_if_eq_x:nnT \familydefault \ttdefault + { + \cs_set_eq:NN \luatexkohanjafont \luatexkomonohanjafont + \luatexkohanjaselectfont + } + \ignorespaces +} +\DeclareDocumentCommand \newhanjafontfamily { m O{} m O{} } +{ + \fontspec_set_family:cnn { luatexko_user_family_ \cs_to_str:N #1 } { #2, #4 } { #3 } + \DeclareRobustCommand #1 + { + \tl_set:Nn \luatexkohanjafont + { + \exp_args:Nc \fontfamily { luatexko_user_family_ \cs_to_str:N #1 } \selectfont + } + \luatexkohanjaselectfont + } + \ignorespaces +} +\DeclareDocumentCommand \newhanjafontface { m O{} m O{} } +{ + \newhanjafontfamily #1 { #3 } [ BoldFont={}, ItalicFont={}, SmallCapsFont={}, #2, #4 ] +} +\DeclareDocumentCommand \hanjafontspec { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkohanjafontfamily { #1, #3 } { #2 } + \tl_set:Nn \luatexkohanjafont + { + \fontfamily \luatexkohanjafontfamily \selectfont + } + \luatexkohanjaselectfont + \ignorespaces +} + +\DeclareDocumentCommand \setmainfallbackfont { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkomainfallbackfamily { Ligatures=TeX, #1, #3 } { #2 } + \DeclareRobustCommand \luatexkomainfallbackfont + { + \fontfamily \luatexkomainfallbackfamily \selectfont + } + \str_if_eq_x:nnT \familydefault \rmdefault + { + \cs_set_eq:NN \luatexkofallbackfont \luatexkomainfallbackfont + \luatexkofallbackselectfont + } + \ignorespaces +} +\DeclareDocumentCommand \setsansfallbackfont { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkosansfallbackfamily { Ligatures=TeX, #1, #3 } { #2 } + \DeclareRobustCommand \luatexkosansfallbackfont + { + \fontfamily \luatexkosansfallbackfamily \selectfont + } + \str_if_eq_x:nnT \familydefault \sfdefault + { + \cs_set_eq:NN \luatexkofallbackfont \luatexkosansfallbackfont + \luatexkofallbackselectfont + } + \ignorespaces +} +\DeclareDocumentCommand \setmonofallbackfont { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkomonofallbackfamily { #1, #3 } { #2 } + \DeclareRobustCommand \luatexkomonofallbackfont + { + \fontfamily \luatexkomonofallbackfamily \selectfont + } + \str_if_eq_x:nnT \familydefault \ttdefault + { + \cs_set_eq:NN \luatexkofallbackfont \luatexkomonofallbackfont + \luatexkofallbackselectfont + } + \ignorespaces +} +\DeclareDocumentCommand \newfallbackfontfamily { m O{} m O{} } +{ + \fontspec_set_family:cnn { luatexko_user_family_ \cs_to_str:N #1 } { #2, #4 } { #3 } + \DeclareRobustCommand #1 + { + \tl_set:Nn \luatexkofallbackfont + { + \exp_args:Nc \fontfamily { luatexko_user_family_ \cs_to_str:N #1 } \selectfont + } + \luatexkofallbackselectfont + } + \ignorespaces +} +\DeclareDocumentCommand \newfallbackfontface { m O{} m O{} } +{ + \newfallbackfontfamily #1 { #3 } [ BoldFont={}, ItalicFont={}, SmallCapsFont={}, #2, #4 ] +} +\DeclareDocumentCommand \fallbackfontspec { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkofallbackfontfamily { #1, #3 } { #2 } + \tl_set:Nn \luatexkofallbackfont + { + \fontfamily \luatexkofallbackfontfamily \selectfont + } + \luatexkofallbackselectfont + \ignorespaces +} +\DeclareDocumentCommand \setmathhangulfont { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkomathhangulfamily { #1, #3 } { #2 } + \DeclareSymbolFont { mathhangul } + \g_fontspec_encoding_tl \luatexkomathhangulfamily \mddefault \updefault + \int_compare:nNnF { \Umathcharfam"AC00 } = { \symmathhangul } + { + \setmathhangulblock{AC00}{D7A3} + } + \ignorespaces +} +\ExplSyntaxOff +\let\adhochangulfont\hangulfontspec +\let\adhochanjafont\hanjafontspec +\let\adhocfallbackfont\fallbackfontspec +% interhangul, interlatincjk, charraise +\protected\def\addhangulfontfeature#1{% + \begingroup + \ifdefined\luatexkohangulfont \luatexkohangulfont \fi + \addfontfeature{#1}% + \edef\x{\endgroup + \def\noexpand\luatexkohangulfont{% + \noexpand\fontfamily{\f@family}\noexpand\selectfont}}\x + \luatexkohangulselectfont + \ignorespaces} +\let\addhangulfontfeatures\addhangulfontfeature +\protected\def\addhanjafontfeature#1{% + \begingroup + \ifdefined\luatexkohanjafont \luatexkohanjafont \fi + \addfontfeature{#1}% + \edef\x{\endgroup + \def\noexpand\luatexkohanjafont{% + \noexpand\fontfamily{\f@family}\noexpand\selectfont}}\x + \luatexkohanjaselectfont + \ignorespaces} +\let\addhanjafontfeatures\addhanjafontfeature +\protected\def\addfallbackfontfeature#1{% + \begingroup + \ifdefined\luatexkofallbackfont \luatexkofallbackfont \fi + \addfontfeature{#1}% + \edef\x{\endgroup + \def\noexpand\luatexkofallbackfont{% + \noexpand\fontfamily{\f@family}\noexpand\selectfont}}\x + \luatexkofallbackselectfont + \ignorespaces} +\let\addfallbackfontfeatures\addfallbackfontfeature +\newfontfeature{InterHangul}{interhangul=#1} +\newfontfeature{InterLatinCJK}{interlatincjk=#1} +\newfontfeature{CharRaise}{charraise=#1} +\newfontfeature{RemoveClassicSpaces}{+removeclassicspaces} +\newfontfeature{CompressPunctuations}{+compresspunctuations} +\newfontfeature{Expansion}{expansion=\ifx\empty#1\empty default\else #1\fi } +\newfontfeature{Protrusion}{protrusion=\ifx\empty#1\empty default\else #1\fi } +\newfontfeature{InterCharacter}{intercharacter=#1} +\newfontfeature{InterCharStretch}{intercharstretch=#1} +% italic correction +\def\nocorrlist{,.^^^^3001^^^^3002^^^^ff0c^^^^ff0e^^^^ff61^^^^ff64} +% package options +\newif\if@hangul +\newif\if@hanja +\DeclareOption{hangul}{\@hangultrue} +\DeclareOption{hanja}{\@hangultrue\@hanjatrue} +\ProcessOptions\relax +% default hangul font +\AtBeginDocument{ + \ifdefined\luatexkomainhangulfont\else + \begingroup\rmfamily + \directlua{ luatexko.currenthashangulchars(255) }\expandafter\endgroup + \ifnum\count@=\z@ + \setmainhangulfont{UnBatang}[Script=Hangul,Language=Korean]\fi\fi + \ifdefined\luatexkosanshangulfont\else + \begingroup\sffamily + \directlua{ luatexko.currenthashangulchars(255) }\expandafter\endgroup + \ifnum\count@=\z@ + \setsanshangulfont{UnDotum}\fi\fi + \ifdefined\luatexkomonohangulfont\else + \begingroup\ttfamily + \directlua{ luatexko.currenthashangulchars(255) }\expandafter\endgroup + \ifnum\count@=\z@ + \let\luatexkomonohangulfont\luatexkosanshangulfont\fi\fi + \@ifpackageloaded{hyperref}{\pdfstringdefDisableCommands{% + \let\ruby\@firstoftwo \let\xxruby\@firstoftwo + \let\dotemph\@firstofone + \let\markoverwith\@secondoftwo + \let\uline\@firstofone \let\sout\@firstofone + \let\uuline\@firstofone \let\xout\@firstofone + \let\uwave\@firstofone \let\dashuline\@firstofone + \let\dotuline\@firstofone + \let\typesetclassic\empty \let\typesetmodern\empty + \let\inhibitglue\empty + \let\hangulpunctuations\count@ + \let\registerpunctuations\@gobble \let\unregisterpunctuations\@gobble + \let\registerhangulpunctuations\@gobble + \let\unregisterhangulpunctuations\@gobble + \let\registerbreakableafter\@gobble \let\registerbreakablebefore\@gobble + \let\hangulbyhangulfont\count@ \let\hanjabyhanjafont\count@ + \let\addhangulfontfeature\@gobble \let\addhangulfontfeatures\@gobble + \let\addhanjafontfeature\@gobble \let\addhanjafontfeatures\@gobble + \let\addfallbackfontfeature\@gobble \let\addfallbackfontfeatures\@gobble + \def\는{는}\def\은{은}\def\을{을}\def\를{를}\def\와{와}\def\과{과}% + \def\가{가}\def\이{이}\def\라{라}\def\으{으}\def\로{로}% + \def\hellipsis{...}}}{} + \if@hangul \RequirePackage{konames-utf}\fi +} +% misc +\RequirePackage{kolabels-utf} +\protected\def\hellipsis{\char"2026\char"2026 } +\if@hangul + \linespread{1.3888} + \footnotesep=1.3888\footnotesep + \skip\footins=\glueexpr\skip\footins/72*100\relax + \frenchspacing +\fi +\endinput |